Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SMTP Protocol Support #1201

Merged
merged 46 commits into from
Jan 31, 2024
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
43908c1
init
egecetin Sep 12, 2023
ea47872
wip
egecetin Sep 14, 2023
1dcb111
add functions
egecetin Sep 17, 2023
4145366
structure test file
egecetin Sep 18, 2023
982a656
Add test files (WIP)
egecetin Sep 21, 2023
1e608fd
Update code and command list
egecetin Sep 22, 2023
83e3702
parsing tests
egecetin Sep 22, 2023
46d9b2c
Merge remote-tracking branch 'upstream/dev' into smtp
egecetin Sep 23, 2023
f081e25
creation tests
egecetin Sep 23, 2023
a1dcf1d
fix typos
egecetin Sep 23, 2023
226dddc
Fix doxy
egecetin Sep 23, 2023
5987a29
Add editing tests
egecetin Oct 10, 2023
2915fa8
Remove <domain> to fix doxy
egecetin Oct 10, 2023
1cf2e68
Update README
egecetin Oct 10, 2023
455fa70
Remove extra space
egecetin Oct 10, 2023
3323d5e
Revert "Remove <domain> to fix doxy"
egecetin Oct 10, 2023
7f43e6c
escape html tags
egecetin Oct 10, 2023
c7e27df
Merge branch 'dev' into smtp
egecetin Oct 10, 2023
7a853d2
Merge branch 'dev' into smtp
egecetin Nov 3, 2023
da6dd81
Merge branch 'smtp' of https://github.com/egecetin/PcapPlusPlus into …
egecetin Nov 6, 2023
b3a029a
fix for starttls and multiline editing
egecetin Nov 6, 2023
47937a0
Merge remote-tracking branch 'upstream/dev' into smtp
egecetin Nov 6, 2023
61aabdc
fix minor comments
egecetin Nov 28, 2023
48f8b28
align one more public
egecetin Nov 28, 2023
bebf89e
simplify removing escape
egecetin Dec 2, 2023
d55996b
remove return types
egecetin Dec 2, 2023
768e439
search both hyphen and <SP>
egecetin Dec 3, 2023
7ac2df2
fix print when no command
egecetin Dec 3, 2023
5b9d76d
[BUGFIX] wrong use of find_first_of/find_last_of
egecetin Dec 3, 2023
aeb057d
Merge branch 'dev' into smtp
egecetin Dec 3, 2023
e1ed4b4
Merge remote-tracking branch 'upstream/dev' into smtp
egecetin Dec 7, 2023
3688efe
Merge remote-tracking branch 'upstream/dev' into smtp
egecetin Dec 9, 2023
c5f66e0
Merge branch 'dev' into smtp
egecetin Dec 14, 2023
4719e94
minor fixes
egecetin Jan 15, 2024
c529150
read comments carefully
egecetin Jan 15, 2024
9a76d3e
add user/pass packets
egecetin Jan 18, 2024
75ac849
Merge remote-tracking branch 'upstream/master' into smtp
egecetin Jan 18, 2024
f6d66ee
update type for new ProtocolType
egecetin Jan 18, 2024
91bb212
remove whitespace
egecetin Jan 18, 2024
9240362
Merge branch 'dev' into smtp
egecetin Jan 19, 2024
f2634f4
Remove protocol delimiter from multiline packets
egecetin Jan 27, 2024
5e2675d
char to string?
egecetin Jan 27, 2024
2758392
centos7 still fails?
egecetin Jan 27, 2024
ea1b5ca
regex reqs gcc 4.9 centos 7 not supports
egecetin Jan 28, 2024
ecf9599
Merge branch 'dev' into smtp
egecetin Jan 29, 2024
b3dc78a
fix comment
egecetin Jan 31, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Common++/header/Logger.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ namespace pcpp
PacketLogModuleSomeIpLayer, ///< SomeIpLayer module (Packet++)
PacketLogModuleSomeIpSdLayer, ///< SomeIpSdLayer module (Packet++)
PacketLogModuleWakeOnLanLayer, ///< WakeOnLanLayer module (Packet++)
PacketLogModuleSmtpLayer, ///< SmtpLayer module (Packet++)
PcapLogModuleWinPcapLiveDevice, ///< WinPcapLiveDevice module (Pcap++)
PcapLogModuleRemoteDevice, ///< WinPcapRemoteDevice module (Pcap++)
PcapLogModuleLiveDevice, ///< PcapLiveDevice module (Pcap++)
Expand Down
1 change: 1 addition & 0 deletions Packet++/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ add_library(
src/SipLayer.cpp
src/Sll2Layer.cpp
src/SllLayer.cpp
src/SmtpLayer.cpp
egecetin marked this conversation as resolved.
Show resolved Hide resolved
src/SomeIpLayer.cpp
src/SomeIpSdLayer.cpp
src/SSHLayer.cpp
Expand Down
5 changes: 5 additions & 0 deletions Packet++/header/ProtocolType.h
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,11 @@ namespace pcpp
*/
const ProtocolType SLL2 = 0x8000000000000;

/**
* SMTP protocol
*/
const ProtocolType SMTP = 0x10000000000000;

/**
* An enum representing OSI model layers
*/
Expand Down
340 changes: 340 additions & 0 deletions Packet++/header/SmtpLayer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,340 @@
#ifndef PACKETPP_SMTP_LAYER
#define PACKETPP_SMTP_LAYER

#include "PayloadLayer.h"
#include "SingleCommandTextProtocol.h"

/// @file

/**
* \namespace pcpp
* \brief The main namespace for the PcapPlusPlus lib
*/
namespace pcpp
{
/**
* Class for general SMTP message
*/
class SmtpLayer : public SingleCommandTextProtocol
{
protected:
egecetin marked this conversation as resolved.
Show resolved Hide resolved
SmtpLayer(uint8_t *data, size_t dataLen, Layer *prevLayer, Packet *packet)
: SingleCommandTextProtocol(data, dataLen, prevLayer, packet)
{
m_Protocol = SMTP;
};

SmtpLayer(const std::string &command, const std::string &option) : SingleCommandTextProtocol(command, option)
{
m_Protocol = SMTP;
};

public:
/**
* A static method that checks whether the port is considered as SMTP control
* @param[in] port The port number to be checked
*/
egecetin marked this conversation as resolved.
Show resolved Hide resolved
static bool isSmtpPort(uint16_t port) { return port == 25 || port == 587; }

// overridden methods

/// SMTP is the always last so does nothing for this layer
void parseNextLayer() {}

/**
* @return Get the size of the layer
*/
size_t getHeaderLen() const { return m_DataLen; }

/// Does nothing for this layer
void computeCalculateFields() {}

/**
* @return The OSI layer level of SMTP (Application Layer).
*/
OsiModelLayer getOsiModelLayer() const { return OsiModelApplicationLayer; }
};

/**
* Class for representing the request messages of SMTP Layer
*/
class SmtpRequestLayer : public SmtpLayer
{
public:
/**
* Enum for SMTP command codes
*/
enum class SmtpCommand : int
{
/// Unknown command
UNK,
/// Starting mail body
DATA = ('D') | ('A' << 8) | ('T' << 16) | ('A' << 24),
/// Initiate conversation
EHLO = ('E') | ('H' << 8) | ('L' << 16) | ('O' << 24),
/// Expand the mailing list
EXPN = ('E') | ('X' << 8) | ('P' << 16) | ('N' << 24),
/// Initiate conversation
HELO = ('H') | ('E' << 8) | ('L' << 16) | ('O' << 24),
/// Ask information
HELP = ('H') | ('E' << 8) | ('L' << 16) | ('P' << 24),
/// Sender indication
MAIL = ('M') | ('A' << 8) | ('I' << 16) | ('L' << 24),
/// No operation
NOOP = ('N') | ('O' << 8) | ('O' << 16) | ('P' << 24),
/// Close conversation
QUIT = ('Q') | ('U' << 8) | ('I' << 16) | ('T' << 24),
/// Receiver indication
RCPT = ('R') | ('C' << 8) | ('P' << 16) | ('T' << 24),
/// Abort transaction
RSET = ('R') | ('S' << 8) | ('E' << 16) | ('T' << 24),
/// Identify user
VRFY = ('V') | ('R' << 8) | ('F' << 16) | ('Y' << 24),
/// Start TLS handshake
STARTTLS = ('S') | ('T' << 8) | ('L' << 16) | ('S' << 24),
egecetin marked this conversation as resolved.
Show resolved Hide resolved
/// Reverse the role of sender and receiver
TURN = ('T') | ('U' << 8) | ('R' << 16) | ('N' << 24),
/// Send mail to terminal
SEND = ('S') | ('E' << 8) | ('N' << 16) | ('D' << 24),
/// Send mail to terminal or to mailbox
SOML = ('S') | ('O' << 8) | ('M' << 16) | ('L' << 24),
/// Send mail to terminal and mailbox
SAML = ('S') | ('A' << 8) | ('M' << 16) | ('L' << 24),
/// Authenticate client and server
AUTH = ('A') | ('U' << 8) | ('T' << 16) | ('H' << 24),
/// Reverse the role of sender and receiver
ATRN = ('A') | ('T' << 8) | ('R' << 16) | ('N' << 24),
/// Submit mail contents
BDAT = ('B') | ('D' << 8) | ('A' << 16) | ('T' << 24),
/// Request to start SMTP queue processing
ETRN = ('E') | ('T' << 8) | ('R' << 16) | ('N' << 24),
/// Release status of the channel
XADR = ('X') | ('A' << 8) | ('D' << 16) | ('R' << 24),
/// Release status of the circuit checking facility
XCIR = ('X') | ('C' << 8) | ('I' << 16) | ('R' << 24),
/// Release status of the number of messages in channel queues
XSTA = ('X') | ('S' << 8) | ('T' << 16) | ('A' << 24),
/// Release status of whether a compiled configuration and character set are in use
XGEN = ('X') | ('G' << 8) | ('E' << 16) | ('N' << 24)
};

/** A constructor that creates the layer from an existing packet raw data
* @param[in] data A pointer to the raw data
* @param[in] dataLen Size of the data in bytes
* @param[in] prevLayer A pointer to the previous layer
* @param[in] packet A pointer to the Packet instance where layer will be stored in
*/
SmtpRequestLayer(uint8_t *data, size_t dataLen, Layer *prevLayer, Packet *packet)
: SmtpLayer(data, dataLen, prevLayer, packet){};

/**
* A constructor that creates layer with provided input values
* @param[in] command SMTP command
* @param[in] option Argument of the command
*/
explicit SmtpRequestLayer(const SmtpCommand &command, const std::string &option = "")
: SmtpLayer(getCommandAsString(command), option){};

/**
* Set the command of request message
* @param[in] code Value to set command
* @return True if the operation is successful, false otherwise
*/
bool setCommand(SmtpCommand code);

/**
* Get the command of request message
* @return SmtpCommand Value of the command
egecetin marked this conversation as resolved.
Show resolved Hide resolved
*/
SmtpCommand getCommand() const;
egecetin marked this conversation as resolved.
Show resolved Hide resolved

/**
* Get the command of request message as string
* @return std::string Value of the command as string
egecetin marked this conversation as resolved.
Show resolved Hide resolved
*/
std::string getCommandString() const;

/**
* Set the command argument of request message
* @param[in] value Value to set command argument
* @return True if the operation is successful, false otherwise
*/
bool setCommandOption(const std::string &value);

/**
* Get the command argument of request message
* @param[in] removeEscapeCharacters Whether non-alphanumerical characters should be removed or not
* @return std::string Value of command argument
*/
std::string getCommandOption(bool removeEscapeCharacters = true) const;

/**
* Convert the command info to readable string
* @param[in] code Command code to convert
* @return std::string Returns the command info as readable string
*/
static std::string getCommandInfo(SmtpCommand code);

/**
* Convert the command to readable string
* @param[in] code Command code to convert
* @return std::string Returns the command as readable string
*/
static std::string getCommandAsString(SmtpCommand code);

// overridden methods

/**
* @return Returns the protocol info as readable string
*/
std::string toString() const;
};

/**
* Class for representing the response messages of SMTP Layer
*/
class SmtpResponseLayer : public SmtpLayer
{
public:
/**
* Enum for SMTP response codes
*/
enum class SmtpStatusCode : int
{
/// System status, or system help reply
SYSTEM_STATUS = 211,
/// Help message
HELP_MESSAGE = 214,
/// \<domain\> Service ready
SERVICE_READY = 220,
/// \<domain\> Service closing transmission channel
SERVICE_CLOSE = 221,
/// Authentication successful
AUTH_SUCCESS = 235,
/// Requested mail action okay, completed
COMPLETED = 250,
/// User not local; will forward to <forward-path>
WILL_FORWARD = 251,
/// Cannot VRFY user, but will accept message and attempt delivery
CANNOT_VERIFY = 252,
/// AUTH input
AUTH_INPUT = 334,
/// Start mail input; end with \<CRLF\>.\<CRLF\>
MAIL_INPUT = 354,
/// \<domain\> Service not available, closing transmission channel
SERVICE_UNAVAILABLE = 421,
/// A password transition is needed
PASS_NEEDED = 432,
/// Requested mail action not taken: mailbox unavailable (mail busy or temporarily blocked)
MAILBOX_UNAVAILABLE_TEMP = 450,
/// Requested action aborted: local error in processing
ABORT_LOCAL_ERROR = 451,
/// Requested action not taken: insufficient system storage
INSUFFICIENT_STORAGE = 452,
/// Temporary authentication failed
TEMP_AUTH_FAILED = 454,
/// Server unable to accommodate parameters
PARAM_NOT_ACCOMMODATED = 455,
/// Syntax error, command unrecognized
CMD_NOT_RECOGNIZED = 500,
/// Syntax error in parameters or arguments
SYNTAX_ERROR_PARAM = 501,
/// Command not implemented
CMD_NOT_IMPLEMENTED = 502,
/// Bad sequence of commands
CMD_BAD_SEQUENCE = 503,
/// Command parameter not implemented
PARAM_NOT_IMPLEMENTED = 504,
/// Server does not accept mail
MAIL_NOT_ACCEPTED = 521,
/// Encryption needed
ENCRYPT_NEED = 523,
/// Authentication required
AUTH_REQUIRED = 530,
/// Authentication mechanism is too weak
AUTH_TOO_WEAK = 534,
/// Authentication credentials invalid
AUTH_CRED_INVALID = 535,
/// Encryption required for requested authentication mechanism
ENCRYPT_REQUIRED = 538,
/// Requested action not taken: mailbox unavailable
MAILBOX_UNAVAILABLE = 550,
/// User not local; please try <forward-path>
USER_NOT_LOCAL = 551,
/// Requested mail action aborted: exceeded storage allocation
EXCEED_STORAGE = 552,
/// Requested action not taken: mailbox name not allowed
NAME_NOT_ALLOWED = 553,
/// Transaction failed
TRANSACTION_FAIL = 554,
/// Domain does not accept mail
DOMAIN_NOT_ACCEPT = 556
};

/** A constructor that creates the layer from an existing packet raw data
* @param[in] data A pointer to the raw data
* @param[in] dataLen Size of the data in bytes
* @param[in] prevLayer A pointer to the previous layer
* @param[in] packet A pointer to the Packet instance where layer will be stored in
*/
SmtpResponseLayer(uint8_t *data, size_t dataLen, Layer *prevLayer, Packet *packet)
: SmtpLayer(data, dataLen, prevLayer, packet){};

/**
* A constructor that creates layer with provided input values
* @param[in] code Status code
* @param[in] option Argument of the status code
*/
explicit SmtpResponseLayer(const SmtpStatusCode &code, const std::string &option = "")
: SmtpLayer(std::to_string(int(code)), option){};

/**
* Set the status code of response message
* @param[in] code Value to set status code
* @return True if the operation is successful, false otherwise
*/
bool setStatusCode(SmtpStatusCode code);

/**
* Get the status code of response message
* @return SmtpStatusCode Value of the status code
egecetin marked this conversation as resolved.
Show resolved Hide resolved
*/
SmtpStatusCode getStatusCode() const;

/**
* Get the status code of response message as string
* @return std::string Value of the status code as string
*/
std::string getStatusCodeString() const;

/**
* Set the argument of response message
* @param[in] value Value to set argument
* @return True if the operation is successful, false otherwise
*/
bool setStatusOption(const std::string &value);

/**
* Get the argument of response message
* @param[in] removeEscapeCharacters Whether non-alphanumerical characters should be removed or not
* @return std::string Value of argument
*/
std::string getStatusOption(bool removeEscapeCharacters = true) const;

/**
* Convert the status code to readable string
* @param[in] code Status code to convert
* @return std::string Returns the status info as readable string
*/
static std::string getStatusCodeAsString(SmtpStatusCode code);

// overridden methods

/**
* @return Returns the protocol info as readable string
*/
std::string toString() const;
};
} // namespace pcpp

#endif /* PACKETPP_SMTP_LAYER */
Loading