/src/PcapPlusPlus/Packet++/src/SingleCommandTextProtocol.cpp
Line | Count | Source |
1 | | #include "SingleCommandTextProtocol.h" |
2 | | #include <cstring> |
3 | | #include <algorithm> |
4 | | #include <vector> |
5 | | |
6 | 8.78k | #define ASCII_HYPHEN 0x2d |
7 | 8.78k | #define ASCII_SPACE 0x20 |
8 | 17.4k | #define MAX_COMMAND_LENGTH 9 // From SMTP command "STARTTLS" + 1 byte hyphen or space |
9 | 10.2k | #define MIN_PACKET_LENGTH 2 // CRLF |
10 | | |
11 | | namespace pcpp |
12 | | { |
13 | | |
14 | | size_t SingleCommandTextProtocol::getArgumentFieldOffset() const |
15 | 8.78k | { |
16 | 8.78k | size_t maxLen; |
17 | 8.78k | if (m_DataLen < MAX_COMMAND_LENGTH) |
18 | 170 | maxLen = m_DataLen; |
19 | 8.61k | else |
20 | 8.61k | maxLen = MAX_COMMAND_LENGTH; |
21 | | |
22 | | // To correctly detect multi-line packets with the option containing a space in |
23 | | // the first MAX_CONTENT_LENGTH bytes, search the both of hyphen and space to take |
24 | | // correct command delimiter |
25 | | |
26 | 8.78k | std::string field(reinterpret_cast<char*>(m_Data), maxLen); |
27 | | |
28 | 8.78k | size_t posHyphen = field.find_first_of(ASCII_HYPHEN); |
29 | 8.78k | size_t posSpace = field.find_first_of(ASCII_SPACE); |
30 | 8.78k | size_t posCRLF = field.rfind("\r\n"); |
31 | | |
32 | | // No delimiter or packet end |
33 | 8.78k | if (posHyphen == std::string::npos && posSpace == std::string::npos && posCRLF == std::string::npos) |
34 | 5.25k | return 0; |
35 | | // Both hyphen and space found |
36 | 3.52k | else if (posHyphen != std::string::npos || posSpace != std::string::npos) |
37 | 2.47k | return std::min(posSpace, posHyphen); |
38 | | // If nothing found but there is a CRLF it is a only command packet |
39 | 1.05k | else if (posCRLF != std::string::npos) |
40 | 1.05k | return posCRLF; |
41 | | |
42 | 0 | return 0; |
43 | 8.78k | } |
44 | | |
45 | | void SingleCommandTextProtocol::setDelimiter(bool hyphen) |
46 | 0 | { |
47 | 0 | if (hyphen) |
48 | 0 | memset(&m_Data[getArgumentFieldOffset()], ASCII_HYPHEN, 1); |
49 | 0 | else |
50 | 0 | memset(&m_Data[getArgumentFieldOffset()], ASCII_SPACE, 1); |
51 | 0 | } |
52 | | |
53 | | bool SingleCommandTextProtocol::hyphenRequired(const std::string& value) |
54 | 0 | { |
55 | 0 | size_t firstPos = value.find("\r\n"); |
56 | 0 | size_t lastPos = value.rfind("\r\n"); |
57 | 0 | return (firstPos != std::string::npos) && (lastPos != std::string::npos) && (firstPos != lastPos); |
58 | 0 | } |
59 | | |
60 | | SingleCommandTextProtocol::SingleCommandTextProtocol(const std::string& command, const std::string& option, |
61 | | ProtocolType protocol) |
62 | 0 | { |
63 | 0 | m_Protocol = protocol; |
64 | 0 | m_Data = new uint8_t[MIN_PACKET_LENGTH]; |
65 | 0 | m_DataLen = MIN_PACKET_LENGTH; |
66 | 0 | if (!command.empty()) |
67 | 0 | setCommandInternal(command); |
68 | 0 | if (!option.empty()) |
69 | 0 | setCommandOptionInternal(option); |
70 | 0 | } |
71 | | |
72 | | bool SingleCommandTextProtocol::setCommandInternal(std::string value) |
73 | 0 | { |
74 | 0 | size_t currentOffset = getArgumentFieldOffset(); |
75 | 0 | if (currentOffset == m_DataLen - 1) |
76 | 0 | currentOffset = 0; |
77 | 0 | if (!currentOffset) |
78 | 0 | value += " \r\n"; |
79 | |
|
80 | 0 | if (value.size() < currentOffset) |
81 | 0 | { |
82 | 0 | if (!shortenLayer(0, currentOffset - value.size())) |
83 | 0 | return false; |
84 | 0 | } |
85 | 0 | else if (m_Data && value.size() > currentOffset) |
86 | 0 | { |
87 | 0 | if (!extendLayer(0, value.size() - currentOffset)) |
88 | 0 | return false; |
89 | 0 | } |
90 | | |
91 | 0 | memcpy(m_Data, value.c_str(), value.size()); |
92 | 0 | return true; |
93 | 0 | } |
94 | | |
95 | | bool SingleCommandTextProtocol::setCommandOptionInternal(std::string value) |
96 | 0 | { |
97 | 0 | size_t lastPos = value.rfind("\r\n"); |
98 | 0 | if (lastPos == std::string::npos || lastPos != value.size() - 2) |
99 | 0 | value += "\r\n"; |
100 | |
|
101 | 0 | size_t currentOffset = getArgumentFieldOffset() + 1; |
102 | |
|
103 | 0 | if (value.size() < (m_DataLen - currentOffset)) |
104 | 0 | { |
105 | 0 | if (!shortenLayer(currentOffset, (m_DataLen - currentOffset) - value.size())) |
106 | 0 | return false; |
107 | 0 | } |
108 | 0 | else if (m_Data && value.size() > (m_DataLen - currentOffset)) |
109 | 0 | { |
110 | 0 | if (!extendLayer(currentOffset, value.size() - (m_DataLen - currentOffset))) |
111 | 0 | return false; |
112 | 0 | } |
113 | | |
114 | 0 | memcpy(&m_Data[currentOffset], value.c_str(), value.size()); |
115 | |
|
116 | 0 | if (hyphenRequired(value)) |
117 | 0 | setDelimiter(true); |
118 | 0 | else |
119 | 0 | setDelimiter(false); |
120 | 0 | return true; |
121 | 0 | } |
122 | | |
123 | | std::string SingleCommandTextProtocol::getCommandInternal() const |
124 | 6.15k | { |
125 | 6.15k | size_t offset = getArgumentFieldOffset(); |
126 | | |
127 | | // If there is no option remove trailing newline characters |
128 | 6.15k | if (offset == (m_DataLen - 1) && offset > 1) |
129 | 0 | return std::string(reinterpret_cast<char*>(m_Data), offset - 1); |
130 | 6.15k | return std::string(reinterpret_cast<char*>(m_Data), offset); |
131 | 6.15k | } |
132 | | |
133 | | std::string SingleCommandTextProtocol::getCommandOptionInternal() const |
134 | 2.63k | { |
135 | 2.63k | size_t offset = getArgumentFieldOffset(); |
136 | | |
137 | | // We don't want to get delimiter so add 1 for start unless there is no command, |
138 | 2.63k | int addition = offset ? 1 : 0; |
139 | | |
140 | | // Check if command-only packet (-2 to account for len/position comparison and size of CRLF) |
141 | 2.63k | if (offset != (m_DataLen - 2)) |
142 | 2.21k | { |
143 | | // We don't want to trailing newline characters so remove 2 and remove addition from start point |
144 | 2.21k | auto option = |
145 | 2.21k | std::string(reinterpret_cast<char*>(&m_Data[offset + addition]), m_DataLen - (offset + 2 + addition)); |
146 | | |
147 | | // Remove XXX- and XXX<SP> since they are delimiters of the protocol where XXX is the usually status code |
148 | | // Check RFC821 (SMTP) Section 3.3 and RFC959 (FTP) Section 4.2 |
149 | 2.21k | auto code = getCommandInternal(); |
150 | 2.21k | auto vDelim = std::vector<std::string>{ code + " ", code + "-" }; |
151 | | |
152 | 2.21k | for (const auto& delim : vDelim) |
153 | 4.42k | { |
154 | 4.42k | size_t pos = 0; |
155 | 4.94k | while ((pos = option.find(delim, pos)) != std::string::npos) |
156 | 526 | { |
157 | 526 | option.replace(pos, delim.length(), ""); |
158 | 526 | } |
159 | 4.42k | } |
160 | 2.21k | return option; |
161 | 2.21k | } |
162 | 420 | return ""; |
163 | 2.63k | } |
164 | | |
165 | | bool SingleCommandTextProtocol::isMultiLine() const |
166 | 0 | { |
167 | 0 | return m_Data[getArgumentFieldOffset()] == ASCII_HYPHEN; |
168 | 0 | } |
169 | | |
170 | | bool SingleCommandTextProtocol::isDataValid(const uint8_t* data, size_t dataSize) |
171 | 10.2k | { |
172 | 10.2k | if (data == nullptr || dataSize < MIN_PACKET_LENGTH) |
173 | 70 | return false; |
174 | | |
175 | 10.1k | std::string payload = std::string(reinterpret_cast<const char*>(data), dataSize); |
176 | 10.1k | return payload.rfind("\r\n") == dataSize - 2; |
177 | 10.2k | } |
178 | | |
179 | | } // namespace pcpp |