Coverage Report

Created: 2026-11-12 06:49

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/PcapPlusPlus/Packet++/header/SmtpLayer.h
Line
Count
Source
1
#ifndef PACKETPP_SMTP_LAYER
2
#define PACKETPP_SMTP_LAYER
3
4
#include "PayloadLayer.h"
5
#include "SingleCommandTextProtocol.h"
6
7
/// @file
8
9
/// @namespace pcpp
10
/// @brief The main namespace for the PcapPlusPlus lib
11
namespace pcpp
12
{
13
  /// Class for general SMTP message
14
  class SmtpLayer : public SingleCommandTextProtocol
15
  {
16
  protected:
17
    SmtpLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet)
18
0
        : SingleCommandTextProtocol(data, dataLen, prevLayer, packet, SMTP) {};
19
20
    SmtpLayer(const std::string& command, const std::string& option)
21
0
        : SingleCommandTextProtocol(command, option, SMTP) {};
22
23
  public:
24
    /// A static method that checks whether the port is considered as SMTP control
25
    /// @param[in] port The port number to be checked
26
    /// @return True if this an SMTP port (25 or 587)
27
    static bool isSmtpPort(uint16_t port)
28
0
    {
29
0
      return port == 25 || port == 587;
30
0
    }
31
32
    // overridden methods
33
34
    /// SMTP is the always last so does nothing for this layer
35
    void parseNextLayer() override
36
0
    {}
37
38
    /// @return Get the size of the layer
39
    size_t getHeaderLen() const override
40
0
    {
41
0
      return m_DataLen;
42
0
    }
43
44
    /// Does nothing for this layer
45
    void computeCalculateFields() override
46
0
    {}
47
48
    /// @return The OSI layer level of SMTP (Application Layer).
49
    OsiModelLayer getOsiModelLayer() const override
50
0
    {
51
0
      return OsiModelApplicationLayer;
52
0
    }
53
  };
54
55
  /// Class for representing the request messages of SMTP Layer
56
  class SmtpRequestLayer : public SmtpLayer
57
  {
58
  public:
59
    /// Enum for SMTP command codes
60
    enum class SmtpCommand : uint64_t
61
    {
62
      /// Unknown command
63
      UNK,
64
      /// Starting mail body
65
      DATA = ('D') | ('A' << 8) | ('T' << 16) | ('A' << 24),
66
      /// Initiate conversation
67
      EHLO = ('E') | ('H' << 8) | ('L' << 16) | ('O' << 24),
68
      /// Expand the mailing list
69
      EXPN = ('E') | ('X' << 8) | ('P' << 16) | ('N' << 24),
70
      /// Initiate conversation
71
      HELO = ('H') | ('E' << 8) | ('L' << 16) | ('O' << 24),
72
      /// Ask information
73
      HELP = ('H') | ('E' << 8) | ('L' << 16) | ('P' << 24),
74
      /// Sender indication
75
      MAIL = ('M') | ('A' << 8) | ('I' << 16) | ('L' << 24),
76
      /// No operation
77
      NOOP = ('N') | ('O' << 8) | ('O' << 16) | ('P' << 24),
78
      /// Close conversation
79
      QUIT = ('Q') | ('U' << 8) | ('I' << 16) | ('T' << 24),
80
      /// Receiver indication
81
      RCPT = ('R') | ('C' << 8) | ('P' << 16) | ('T' << 24),
82
      /// Abort transaction
83
      RSET = ('R') | ('S' << 8) | ('E' << 16) | ('T' << 24),
84
      /// Identify user
85
      VRFY = ('V') | ('R' << 8) | ('F' << 16) | ('Y' << 24),
86
      /// Start TLS handshake
87
      STARTTLS = (('S') | ('T' << 8) | ('A' << 16) | ('R' << 24) |
88
                  static_cast<uint64_t>(('T') | ('T' << 8) | ('L' << 16) | ('S' << 24)) << 32),
89
      /// Reverse the role of sender and receiver
90
      TURN = ('T') | ('U' << 8) | ('R' << 16) | ('N' << 24),
91
      /// Send mail to terminal
92
      SEND = ('S') | ('E' << 8) | ('N' << 16) | ('D' << 24),
93
      /// Send mail to terminal or to mailbox
94
      SOML = ('S') | ('O' << 8) | ('M' << 16) | ('L' << 24),
95
      /// Send mail to terminal and mailbox
96
      SAML = ('S') | ('A' << 8) | ('M' << 16) | ('L' << 24),
97
      /// Authenticate client and server
98
      AUTH = ('A') | ('U' << 8) | ('T' << 16) | ('H' << 24),
99
      /// Reverse the role of sender and receiver
100
      ATRN = ('A') | ('T' << 8) | ('R' << 16) | ('N' << 24),
101
      /// Submit mail contents
102
      BDAT = ('B') | ('D' << 8) | ('A' << 16) | ('T' << 24),
103
      /// Request to start SMTP queue processing
104
      ETRN = ('E') | ('T' << 8) | ('R' << 16) | ('N' << 24),
105
      /// Release status of the channel
106
      XADR = ('X') | ('A' << 8) | ('D' << 16) | ('R' << 24),
107
      /// Release status of the circuit checking facility
108
      XCIR = ('X') | ('C' << 8) | ('I' << 16) | ('R' << 24),
109
      /// Release status of the number of messages in channel queues
110
      XSTA = ('X') | ('S' << 8) | ('T' << 16) | ('A' << 24),
111
      /// Release status of whether a compiled configuration and character set are in use
112
      XGEN = ('X') | ('G' << 8) | ('E' << 16) | ('N' << 24)
113
    };
114
115
    /// A constructor that creates the layer from an existing packet raw data
116
    /// @param[in] data A pointer to the raw data
117
    /// @param[in] dataLen Size of the data in bytes
118
    /// @param[in] prevLayer A pointer to the previous layer
119
    /// @param[in] packet A pointer to the Packet instance where layer will be stored in
120
    SmtpRequestLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet)
121
0
        : SmtpLayer(data, dataLen, prevLayer, packet) {};
122
123
    /// A constructor that creates layer with provided input values
124
    /// @param[in] command SMTP command
125
    /// @param[in] option Argument of the command
126
    explicit SmtpRequestLayer(const SmtpCommand& command, const std::string& option = "")
127
0
        : SmtpLayer(getCommandAsString(command), option) {};
128
129
    /// Set the command of request message
130
    /// @param[in] code Value to set command
131
    /// @return True if the operation is successful, false otherwise
132
    bool setCommand(SmtpCommand code);
133
134
    /// Get the command of request message
135
    /// @return Value of the command
136
    SmtpCommand getCommand() const;
137
138
    /// Get the command of request message as string
139
    /// @return Value of the command as string
140
    std::string getCommandString() const;
141
142
    /// Set the command argument of request message
143
    /// @param[in] value Value to set command argument
144
    /// @return True if the operation is successful, false otherwise
145
    bool setCommandOption(const std::string& value);
146
147
    /// Get the command argument of request message
148
    /// @param[in] removeEscapeCharacters Whether non-alphanumerical characters should be removed or not
149
    /// @return Value of command argument
150
    std::string getCommandOption(bool removeEscapeCharacters = true) const;
151
152
    /// Convert the command info to readable string
153
    /// @param[in] code Command code to convert
154
    /// @return Returns the command info as readable string
155
    static std::string getCommandInfo(SmtpCommand code);
156
157
    /// Convert the command to readable string
158
    /// @param[in] code Command code to convert
159
    /// @return Returns the command as readable string
160
    static std::string getCommandAsString(SmtpCommand code);
161
162
    // overridden methods
163
164
    /// @return Returns the protocol info as readable string
165
    std::string toString() const override;
166
167
    /// @struct SerializedFields
168
    /// Fields written by SmtpRequestLayer's serializeLayer(), in addition to
169
    /// Layer::SerializedFields.
170
    struct SerializedFields : Layer::SerializedFields
171
    {
172
      /// @return All field descriptors for SmtpRequestLayer
173
      static std::vector<FieldDescriptor> all()
174
0
      {
175
0
        auto result = Layer::SerializedFields::all();
176
0
        std::initializer_list<FieldDescriptor> extra{ Command, CommandOption };
177
0
        std::copy(extra.begin(), extra.end(), std::back_inserter(result));
178
0
        return result;
179
0
      }
180
181
      /// @brief Field descriptor for the command, written as a string as it appears in the message (for
182
      /// example "EHLO").
183
      static const FieldDescriptor Command;
184
185
      /// @brief Field descriptor for the command argument, written as a string with non-printable characters
186
      /// removed. An empty string is written if the command has no argument.
187
      static const FieldDescriptor CommandOption;
188
    };
189
190
  protected:
191
    void serializeLayer(ObjectScope& serializer) const override;
192
  };
193
194
  /// Class for representing the response messages of SMTP Layer
195
  class SmtpResponseLayer : public SmtpLayer
196
  {
197
  public:
198
    /// Enum for SMTP response codes
199
    enum class SmtpStatusCode : int
200
    {
201
      /// System status, or system help reply
202
      SYSTEM_STATUS = 211,
203
      /// Help message
204
      HELP_MESSAGE = 214,
205
      /// \<domain\> Service ready
206
      SERVICE_READY = 220,
207
      /// \<domain\> Service closing transmission channel
208
      SERVICE_CLOSE = 221,
209
      /// Authentication successful
210
      AUTH_SUCCESS = 235,
211
      /// Requested mail action okay, completed
212
      COMPLETED = 250,
213
      /// User not local; will forward to <forward-path>
214
      WILL_FORWARD = 251,
215
      /// Cannot VRFY user, but will accept message and attempt delivery
216
      CANNOT_VERIFY = 252,
217
      /// AUTH input
218
      AUTH_INPUT = 334,
219
      /// Start mail input; end with \<CRLF\>.\<CRLF\>
220
      MAIL_INPUT = 354,
221
      /// \<domain\> Service not available, closing transmission channel
222
      SERVICE_UNAVAILABLE = 421,
223
      /// A password transition is needed
224
      PASS_NEEDED = 432,
225
      /// Requested mail action not taken: mailbox unavailable (mail busy or temporarily blocked)
226
      MAILBOX_UNAVAILABLE_TEMP = 450,
227
      /// Requested action aborted: local error in processing
228
      ABORT_LOCAL_ERROR = 451,
229
      /// Requested action not taken: insufficient system storage
230
      INSUFFICIENT_STORAGE = 452,
231
      /// Temporary authentication failed
232
      TEMP_AUTH_FAILED = 454,
233
      /// Server unable to accommodate parameters
234
      PARAM_NOT_ACCOMMODATED = 455,
235
      /// Syntax error, command unrecognized
236
      CMD_NOT_RECOGNIZED = 500,
237
      /// Syntax error in parameters or arguments
238
      SYNTAX_ERROR_PARAM = 501,
239
      /// Command not implemented
240
      CMD_NOT_IMPLEMENTED = 502,
241
      /// Bad sequence of commands
242
      CMD_BAD_SEQUENCE = 503,
243
      /// Command parameter not implemented
244
      PARAM_NOT_IMPLEMENTED = 504,
245
      /// Server does not accept mail
246
      MAIL_NOT_ACCEPTED = 521,
247
      /// Encryption needed
248
      ENCRYPT_NEED = 523,
249
      /// Authentication required
250
      AUTH_REQUIRED = 530,
251
      /// Authentication mechanism is too weak
252
      AUTH_TOO_WEAK = 534,
253
      /// Authentication credentials invalid
254
      AUTH_CRED_INVALID = 535,
255
      /// Encryption required for requested authentication mechanism
256
      ENCRYPT_REQUIRED = 538,
257
      /// Requested action not taken: mailbox unavailable
258
      MAILBOX_UNAVAILABLE = 550,
259
      /// User not local; please try <forward-path>
260
      USER_NOT_LOCAL = 551,
261
      /// Requested mail action aborted: exceeded storage allocation
262
      EXCEED_STORAGE = 552,
263
      /// Requested action not taken: mailbox name not allowed
264
      NAME_NOT_ALLOWED = 553,
265
      /// Transaction failed
266
      TRANSACTION_FAIL = 554,
267
      /// Domain does not accept mail
268
      DOMAIN_NOT_ACCEPT = 556
269
    };
270
271
    /// A constructor that creates the layer from an existing packet raw data
272
    /// @param[in] data A pointer to the raw data
273
    /// @param[in] dataLen Size of the data in bytes
274
    /// @param[in] prevLayer A pointer to the previous layer
275
    /// @param[in] packet A pointer to the Packet instance where layer will be stored in
276
    SmtpResponseLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet)
277
0
        : SmtpLayer(data, dataLen, prevLayer, packet) {};
278
279
    /// A constructor that creates layer with provided input values
280
    /// @param[in] code Status code
281
    /// @param[in] option Argument of the status code
282
    explicit SmtpResponseLayer(const SmtpStatusCode& code, const std::string& option = "")
283
0
        : SmtpLayer(std::to_string(int(code)), option) {};
284
285
    /// Set the status code of response message
286
    /// @param[in] code Value to set status code
287
    /// @return True if the operation is successful, false otherwise
288
    bool setStatusCode(SmtpStatusCode code);
289
290
    /// Get the status code of response message
291
    /// @return Value of the status code
292
    SmtpStatusCode getStatusCode() const;
293
294
    /// Get the status code of response message as string
295
    /// @return Value of the status code as string
296
    std::string getStatusCodeString() const;
297
298
    /// Set the argument of response message
299
    /// @param[in] value Value to set argument
300
    /// @return True if the operation is successful, false otherwise
301
    bool setStatusOption(const std::string& value);
302
303
    /// Get the argument of response message
304
    /// @param[in] removeEscapeCharacters Whether non-alphanumerical characters should be removed or not
305
    /// @return Value of argument
306
    std::string getStatusOption(bool removeEscapeCharacters = true) const;
307
308
    /// Convert the status code to readable string
309
    /// @param[in] code Status code to convert
310
    /// @return Returns the status info as readable string
311
    static std::string getStatusCodeAsString(SmtpStatusCode code);
312
313
    // overridden methods
314
315
    /// @return Returns the protocol info as readable string
316
    std::string toString() const override;
317
318
    /// @struct SerializedFields
319
    /// Fields written by SmtpResponseLayer's serializeLayer(), in addition to
320
    /// Layer::SerializedFields.
321
    struct SerializedFields : Layer::SerializedFields
322
    {
323
      /// @return All field descriptors for SmtpResponseLayer
324
      static std::vector<FieldDescriptor> all()
325
0
      {
326
0
        auto result = Layer::SerializedFields::all();
327
0
        std::initializer_list<FieldDescriptor> extra{ StatusCode, StatusCodeDescription, StatusOption,
328
0
                                                    MultiLine };
329
0
        std::copy(extra.begin(), extra.end(), std::back_inserter(result));
330
0
        return result;
331
0
      }
332
333
      /// @brief Field descriptor for the status code, written as a number (for example 220).
334
      static const FieldDescriptor StatusCode;
335
336
      /// @brief Field descriptor for a short description of the status code, written as a string (see
337
      /// getStatusCodeAsString()), or "Unknown status code" if the status code isn't recognized.
338
      static const FieldDescriptor StatusCodeDescription;
339
340
      /// @brief Field descriptor for the status argument, written as a string with non-printable characters
341
      /// removed. For multi-line replies the lines are concatenated with the status code prefixes removed.
342
      static const FieldDescriptor StatusOption;
343
344
      /// @brief Field descriptor for whether this is a multi-line reply, written as a boolean.
345
      static const FieldDescriptor MultiLine;
346
    };
347
348
  protected:
349
    void serializeLayer(ObjectScope& serializer) const override;
350
  };
351
}  // namespace pcpp
352
353
#endif  // PACKETPP_SMTP_LAYER