3
3
#include " TelnetLayer.h"
4
4
#include " Logger.h"
5
5
#include " GeneralUtils.h"
6
+ #include " AssertionUtils.h"
6
7
#include < cstring>
8
+ #include < iterator>
9
+ #include < algorithm>
7
10
8
11
namespace pcpp
9
12
{
10
-
11
- bool TelnetLayer::isDataField (uint8_t * pos) const
13
+ namespace
12
14
{
13
- // "FF FF" means data
14
- return pos[0 ] != static_cast <int >(TelnetCommand::InterpretAsCommand) ||
15
- pos[1 ] == static_cast <int >(TelnetCommand::InterpretAsCommand);
16
- }
15
+ // / @brief An enum representing the type of a Telnet sequence
16
+ enum class TelnetSequenceType
17
+ {
18
+ // / @brief An unknown sequence type. Usually means parsing error.
19
+ // / Commonly happens when an IAC symbol is found at the end of the buffer without a following byte.
20
+ Unknown,
21
+ // / @brief A telnet command sequence. Starts with IAC followed by a command code.
22
+ Command,
23
+ // / @brief A telnet data sequence. Either does not start with IAC or starts with IAC IAC.
24
+ UserData,
25
+ };
26
+
27
+ // / @brief Checks if a given sequence matches Telnet command or data pattern.
28
+ // / @param first Start of the sequence to check
29
+ // / @param maxCount Maximum number of bytes to check
30
+ // / @return The type of the Telnet sequence. Unknown if the sequence does not match either command or data
31
+ // / pattern or if parsing error occurs.
32
+ TelnetSequenceType getTelnetSequenceType (uint8_t const * first, size_t maxCount)
33
+ {
34
+ if (first == nullptr || maxCount == 0 )
35
+ {
36
+ PCPP_LOG_DEBUG (" Checking empty or null buffer for telnet sequence type" );
37
+ return TelnetSequenceType::Unknown;
38
+ }
17
39
18
- bool TelnetLayer::isCommandField (uint8_t * pos) const
19
- {
20
- return !isDataField (pos);
21
- }
40
+ // If first byte is not "FF" it's data
41
+ if (*first != static_cast <int >(TelnetLayer::TelnetCommand::InterpretAsCommand))
42
+ {
43
+ return TelnetSequenceType::UserData;
44
+ }
45
+
46
+ // IAC must be followed by another octet
47
+ if (maxCount <= 1 )
48
+ {
49
+ PCPP_LOG_DEBUG (" Telnet Parse Error: IAC (FF) must always be followed by another octet" );
50
+ return TelnetSequenceType::Unknown;
51
+ }
52
+
53
+ if (first[1 ] == static_cast <int >(TelnetLayer::TelnetCommand::InterpretAsCommand))
54
+ {
55
+ // "FF FF" means data continue
56
+ return TelnetSequenceType::UserData;
57
+ }
58
+
59
+ // "FF X" where X != "FF" means command
60
+ return TelnetSequenceType::Command;
61
+ }
62
+
63
+ // / @brief Checks if a given sequence matches Telnet command pattern.
64
+ // / @param first Start of the sequence to check
65
+ // / @param maxCount Maximum number of bytes to check
66
+ // / @return True if the buffer matches Telnet command pattern, false otherwise
67
+ bool isTelnetCommand (uint8_t const * first, size_t maxCount)
68
+ {
69
+ return getTelnetSequenceType (first, maxCount) == TelnetSequenceType::Command;
70
+ }
71
+ } // namespace
22
72
23
73
size_t TelnetLayer::distanceToNextIAC (uint8_t * startPos, size_t maxLength)
24
74
{
@@ -64,36 +114,65 @@ namespace pcpp
64
114
65
115
uint8_t * TelnetLayer::getNextDataField (uint8_t * pos, size_t len)
66
116
{
67
- size_t offset = 0 ;
68
- while (offset < len)
69
- {
70
- // Move to next field
71
- size_t length = getFieldLen (pos, len - offset);
72
- pos += length;
73
- offset += length;
117
+ // This assumes `pos` points to the start of a valid field.
118
+ auto const endIt = pos + len;
74
119
75
- if (isDataField (pos))
120
+ // Advance to the next field, as we are skipping the current one from the search.
121
+ pos += getFieldLen (pos, len);
122
+
123
+ while (pos < endIt)
124
+ {
125
+ // Check if the current field is data
126
+ switch (getTelnetSequenceType (pos, std::distance (pos, endIt)))
127
+ {
128
+ case TelnetSequenceType::Unknown:
129
+ {
130
+ PCPP_LOG_DEBUG (" Telnet Parse Error: Unknown sequence found during data field search." );
131
+ return nullptr ;
132
+ }
133
+ case TelnetSequenceType::UserData:
76
134
return pos;
135
+ default :
136
+ break ; // continue searching
137
+ }
138
+
139
+ // If not data, move to next field
140
+ pos += getFieldLen (pos, std::distance (pos, endIt));
77
141
}
78
142
143
+ // If we got here, no data field has been found before the end of the buffer
79
144
return nullptr ;
80
145
}
81
146
82
147
uint8_t * TelnetLayer::getNextCommandField (uint8_t * pos, size_t len)
83
148
{
84
- size_t offset = 0 ;
85
- while (offset < len)
86
- {
87
- // Move to next field
88
- size_t length = getFieldLen (pos, len - offset);
89
- pos += length;
90
- offset += length;
149
+ // This assumes `pos` points to the start of a valid field.
150
+ auto const endIt = pos + len;
151
+
152
+ // Advance to the next field, as we are skipping the current one from the search.
153
+ pos += getFieldLen (pos, len);
91
154
92
- if ((static_cast <size_t >(pos - m_Data) <= (m_DataLen - 2 )) &&
93
- isCommandField (pos)) // Need at least 2 bytes for command
155
+ while (pos < endIt)
156
+ {
157
+ // Check if the current field is command
158
+ switch (getTelnetSequenceType (pos, std::distance (pos, endIt)))
159
+ {
160
+ case TelnetSequenceType::Unknown:
161
+ {
162
+ PCPP_LOG_DEBUG (" Telnet Parse Error: Unknown sequence found during command field search." );
163
+ return nullptr ;
164
+ }
165
+ case TelnetSequenceType::Command:
94
166
return pos;
167
+ default :
168
+ break ; // continue searching
169
+ }
170
+
171
+ // If not command, move to next field
172
+ pos += getFieldLen (pos, std::distance (pos, endIt));
95
173
}
96
174
175
+ // If we got here, no command field has been found before the end of the buffer
97
176
return nullptr ;
98
177
}
99
178
@@ -117,36 +196,59 @@ namespace pcpp
117
196
118
197
std::string TelnetLayer::getDataAsString (bool removeEscapeCharacters)
119
198
{
120
- uint8_t * dataPos = nullptr ;
121
- if (isDataField (m_Data))
122
- dataPos = m_Data;
123
- else
124
- dataPos = getNextDataField (m_Data, m_DataLen);
125
-
126
- if (!dataPos)
199
+ if (m_Data == nullptr )
127
200
{
128
- PCPP_LOG_DEBUG (" Packet does not have a data field " );
129
- return std::string () ;
201
+ PCPP_LOG_DEBUG (" Layer does not have data" );
202
+ return {} ;
130
203
}
131
204
132
205
// Convert to string
133
206
if (removeEscapeCharacters)
134
207
{
135
- std::stringstream ss;
136
- for (size_t idx = 0 ; idx < m_DataLen - (dataPos - m_Data) + 1 ; ++idx)
208
+ uint8_t * dataPos = nullptr ;
209
+ switch (getTelnetSequenceType (m_Data, m_DataLen))
210
+ {
211
+ case TelnetSequenceType::Unknown:
212
+ {
213
+ PCPP_LOG_DEBUG (" Telnet Parse Error: Unknown sequence found during data string extraction." );
214
+ return {};
215
+ }
216
+ case TelnetSequenceType::UserData:
217
+ dataPos = m_Data;
218
+ break ;
219
+ case TelnetSequenceType::Command:
220
+ dataPos = getNextDataField (m_Data, m_DataLen);
221
+ break ;
222
+ default :
223
+ throw std::logic_error (" Unsupported sequence type" );
224
+ }
225
+
226
+ if (!dataPos)
137
227
{
138
- if ( int (dataPos[idx]) < 127 && int (dataPos[idx]) > 31 ) // From SPACE to ~
139
- ss << dataPos[idx] ;
228
+ PCPP_LOG_DEBUG ( " Packet does not have a data field " );
229
+ return std::string () ;
140
230
}
141
- return ss.str ();
231
+
232
+ PCPP_ASSERT (dataPos >= m_Data && dataPos < (m_Data + m_DataLen),
233
+ " Data position is out of bounds, this should never happen!" );
234
+
235
+ // End of range is corrected by the advance offset.
236
+ auto const * beginIt = dataPos;
237
+ auto const * endIt = dataPos + m_DataLen - std::distance (m_Data, dataPos);
238
+
239
+ std::string result;
240
+ std::copy_if (beginIt, endIt, std::back_inserter (result), [](char ch) -> bool {
241
+ return ch > 31 && ch < 127 ; // From SPACE to ~
242
+ });
243
+ return result;
142
244
}
143
245
return std::string ((char *)m_Data, m_DataLen);
144
246
}
145
247
146
248
size_t TelnetLayer::getTotalNumberOfCommands ()
147
249
{
148
250
size_t ctr = 0 ;
149
- if (isCommandField (m_Data))
251
+ if (isTelnetCommand (m_Data, m_DataLen ))
150
252
++ctr;
151
253
152
254
uint8_t * pos = m_Data;
@@ -167,7 +269,7 @@ namespace pcpp
167
269
return 0 ;
168
270
169
271
size_t ctr = 0 ;
170
- if (isCommandField (m_Data) && m_Data[1 ] == static_cast <int >(command))
272
+ if (isTelnetCommand (m_Data, m_DataLen ) && m_Data[1 ] == static_cast <int >(command))
171
273
++ctr;
172
274
173
275
uint8_t * pos = m_Data;
@@ -185,7 +287,7 @@ namespace pcpp
185
287
TelnetLayer::TelnetCommand TelnetLayer::getFirstCommand ()
186
288
{
187
289
// If starts with command
188
- if (isCommandField (m_Data))
290
+ if (isTelnetCommand (m_Data, m_DataLen ))
189
291
return static_cast <TelnetCommand>(m_Data[1 ]);
190
292
191
293
// Check is there any command
@@ -200,7 +302,7 @@ namespace pcpp
200
302
if (lastPositionOffset == SIZE_MAX)
201
303
{
202
304
lastPositionOffset = 0 ;
203
- if (isCommandField (m_Data))
305
+ if (isTelnetCommand (m_Data, m_DataLen ))
204
306
return static_cast <TelnetLayer::TelnetCommand>(m_Data[1 ]);
205
307
}
206
308
@@ -231,7 +333,7 @@ namespace pcpp
231
333
return TelnetOption::TelnetOptionNoOption;
232
334
}
233
335
234
- if (isCommandField (m_Data) && m_Data[1 ] == static_cast <int >(command))
336
+ if (isTelnetCommand (m_Data, m_DataLen ) && m_Data[1 ] == static_cast <int >(command))
235
337
return static_cast <TelnetOption>(getSubCommand (m_Data, getFieldLen (m_Data, m_DataLen)));
236
338
237
339
uint8_t * pos = m_Data;
@@ -271,7 +373,7 @@ namespace pcpp
271
373
return nullptr ;
272
374
}
273
375
274
- if (isCommandField (m_Data) && m_Data[1 ] == static_cast <int >(command))
376
+ if (isTelnetCommand (m_Data, m_DataLen ) && m_Data[1 ] == static_cast <int >(command))
275
377
{
276
378
size_t lenBuffer = getFieldLen (m_Data, m_DataLen);
277
379
uint8_t * posBuffer = getCommandData (m_Data, lenBuffer);
@@ -473,9 +575,18 @@ namespace pcpp
473
575
474
576
std::string TelnetLayer::toString () const
475
577
{
476
- if (isDataField (m_Data))
578
+ // TODO: Perhaps print the entire sequence of commands and data?
579
+ switch (getTelnetSequenceType (m_Data, m_DataLen))
580
+ {
581
+ case TelnetSequenceType::Unknown:
582
+ return " Telnet Unknown" ;
583
+ case TelnetSequenceType::Command:
584
+ return " Telnet Control" ;
585
+ case TelnetSequenceType::UserData:
477
586
return " Telnet Data" ;
478
- return " Telnet Control" ;
587
+ default :
588
+ throw std::logic_error (" Unsupported sequence type" );
589
+ }
479
590
}
480
591
481
592
} // namespace pcpp
0 commit comments