/src/PcapPlusPlus/Packet++/src/TelnetLayer.cpp
Line | Count | Source |
1 | 54.2k | #define LOG_MODULE PacketLogModuleTelnetLayer |
2 | | |
3 | | #include "TelnetLayer.h" |
4 | | #include "Logger.h" |
5 | | #include "GeneralUtils.h" |
6 | | #include "AssertionUtils.h" |
7 | | #include <cstring> |
8 | | #include <iterator> |
9 | | #include <algorithm> |
10 | | |
11 | | namespace pcpp |
12 | | { |
13 | | namespace |
14 | | { |
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 | 587k | { |
34 | 587k | if (first == nullptr || maxCount == 0) |
35 | 0 | { |
36 | 0 | PCPP_LOG_DEBUG("Checking empty or null buffer for telnet sequence type"); |
37 | 0 | return TelnetSequenceType::Unknown; |
38 | 0 | } |
39 | | |
40 | | // If first byte is not "FF" it's data |
41 | 587k | if (*first != static_cast<int>(TelnetLayer::TelnetCommand::InterpretAsCommand)) |
42 | 152k | { |
43 | 152k | return TelnetSequenceType::UserData; |
44 | 152k | } |
45 | | |
46 | | // IAC must be followed by another octet |
47 | 434k | if (maxCount <= 1) |
48 | 7.63k | { |
49 | 7.63k | PCPP_LOG_DEBUG("Telnet Parse Error: IAC (FF) must always be followed by another octet"); |
50 | 7.63k | return TelnetSequenceType::Unknown; |
51 | 7.63k | } |
52 | | |
53 | 426k | if (first[1] == static_cast<int>(TelnetLayer::TelnetCommand::InterpretAsCommand)) |
54 | 88.2k | { |
55 | | // "FF FF" means data continue |
56 | 88.2k | return TelnetSequenceType::UserData; |
57 | 88.2k | } |
58 | | |
59 | | // "FF X" where X != "FF" means command |
60 | 338k | return TelnetSequenceType::Command; |
61 | 426k | } |
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 | 108k | { |
69 | 108k | return getTelnetSequenceType(first, maxCount) == TelnetSequenceType::Command; |
70 | 108k | } |
71 | | |
72 | | /// @brief Finds the next IAC symbol in a data stream ignoring the first byte. |
73 | | /// @param[in] first Iterator to the start of the data stream to check. |
74 | | /// @param[in] last Iterator one past the end of the data stream to check. |
75 | | /// @return The next encountered IAC symbol after the first. |
76 | | uint8_t* findNextIAC(uint8_t* first, uint8_t* last) |
77 | 217k | { |
78 | | // FF FF pattern is FF literal |
79 | | // FF non-FF pattern is IAC op code |
80 | | // Sample seq: FF AB CD 0F FF FF A4 B5 [FF] 2D DA |
81 | | |
82 | | // Requires at least 2 elements. |
83 | 217k | if (first + 1 >= last) |
84 | 4.80k | return last; |
85 | | |
86 | 212k | constexpr int IAC = static_cast<int>(TelnetLayer::TelnetCommand::InterpretAsCommand); |
87 | | |
88 | | // Start the search from the second byte. |
89 | 212k | auto it = first + 1; |
90 | 369k | while (it != last) |
91 | 362k | { |
92 | | // Find the next IAC symbol. |
93 | 362k | it = std::find(it, last, IAC); |
94 | | |
95 | | // Reached the end of the sequence. |
96 | 362k | if (it == last) |
97 | 15.1k | return last; |
98 | | |
99 | 347k | auto itNext = std::next(it); |
100 | | // Reached the end of the sequence. |
101 | | // IAC at the end of the sequence is invalid. |
102 | 347k | if (itNext == last) |
103 | 8.74k | return last; |
104 | | |
105 | | // If the next symbol is not IAC, this isn't an escaped sequence. |
106 | 338k | if (*itNext != IAC) |
107 | 181k | return it; |
108 | | |
109 | | // Escaped sequence "FF FF", move it to 1 past the second FF, to skip the view "FF [[FF XX]]". |
110 | 157k | it = std::next(itNext); |
111 | 157k | } |
112 | | |
113 | 7.16k | return last; |
114 | 212k | } |
115 | | } // namespace |
116 | | |
117 | | size_t TelnetLayer::distanceToNextIAC(uint8_t* startPos, size_t maxLength) |
118 | 217k | { |
119 | 217k | auto beginIt = startPos; |
120 | 217k | auto endIt = startPos + maxLength; |
121 | 217k | auto nextIacIt = findNextIAC(beginIt, endIt); |
122 | 217k | return std::distance(beginIt, nextIacIt); |
123 | 217k | } |
124 | | |
125 | | size_t TelnetLayer::getFieldLen(uint8_t* startPos, size_t maxLength) |
126 | 579k | { |
127 | | // Check first byte is IAC |
128 | 579k | if (startPos && (startPos[0] == static_cast<int>(TelnetCommand::InterpretAsCommand)) && (maxLength >= 2)) |
129 | 458k | { |
130 | | // If subnegotiation parse until next IAC |
131 | 458k | if (startPos[1] == static_cast<int>(TelnetCommand::Subnegotiation)) |
132 | 97.3k | return distanceToNextIAC(startPos, maxLength); |
133 | | // Only WILL, WONT, DO, DONT have option. Ref http://pcmicro.com/netfoss/telnet.html |
134 | 361k | else if (startPos[1] >= static_cast<int>(TelnetCommand::WillPerform) && |
135 | 135k | startPos[1] <= static_cast<int>(TelnetCommand::DontPerform)) |
136 | 53.6k | return std::min<size_t>(3, maxLength); |
137 | 307k | return 2; |
138 | 458k | } |
139 | 120k | return distanceToNextIAC(startPos, maxLength); |
140 | 579k | } |
141 | | |
142 | | uint8_t* TelnetLayer::getNextDataField(uint8_t* pos, size_t len) |
143 | 18.8k | { |
144 | | // This assumes `pos` points to the start of a valid field. |
145 | 18.8k | auto const endIt = pos + len; |
146 | | |
147 | | // Advance to the next field, as we are skipping the current one from the search. |
148 | 18.8k | pos += getFieldLen(pos, len); |
149 | | |
150 | 32.4k | while (pos < endIt) |
151 | 28.1k | { |
152 | | // Check if the current field is data |
153 | 28.1k | switch (getTelnetSequenceType(pos, std::distance(pos, endIt))) |
154 | 28.1k | { |
155 | 936 | case TelnetSequenceType::Unknown: |
156 | 936 | { |
157 | 936 | PCPP_LOG_DEBUG("Telnet Parse Error: Unknown sequence found during data field search."); |
158 | 936 | return nullptr; |
159 | 0 | } |
160 | 13.6k | case TelnetSequenceType::UserData: |
161 | 13.6k | return pos; |
162 | 13.6k | default: |
163 | 13.6k | break; // continue searching |
164 | 28.1k | } |
165 | | |
166 | | // If not data, move to next field |
167 | 13.6k | pos += getFieldLen(pos, std::distance(pos, endIt)); |
168 | 13.6k | } |
169 | | |
170 | | // If we got here, no data field has been found before the end of the buffer |
171 | 4.28k | return nullptr; |
172 | 18.8k | } |
173 | | |
174 | | uint8_t* TelnetLayer::getNextCommandField(uint8_t* pos, size_t len) |
175 | 285k | { |
176 | | // This assumes `pos` points to the start of a valid field. |
177 | 285k | auto const endIt = pos + len; |
178 | | |
179 | | // Advance to the next field, as we are skipping the current one from the search. |
180 | 285k | pos += getFieldLen(pos, len); |
181 | | |
182 | 436k | while (pos < endIt) |
183 | 396k | { |
184 | | // Check if the current field is command |
185 | 396k | switch (getTelnetSequenceType(pos, std::distance(pos, endIt))) |
186 | 396k | { |
187 | 5.76k | case TelnetSequenceType::Unknown: |
188 | 5.76k | { |
189 | 5.76k | PCPP_LOG_DEBUG("Telnet Parse Error: Unknown sequence found during command field search."); |
190 | 5.76k | return nullptr; |
191 | 0 | } |
192 | 240k | case TelnetSequenceType::Command: |
193 | 240k | return pos; |
194 | 150k | default: |
195 | 150k | break; // continue searching |
196 | 396k | } |
197 | | |
198 | | // If not command, move to next field |
199 | 150k | pos += getFieldLen(pos, std::distance(pos, endIt)); |
200 | 150k | } |
201 | | |
202 | | // If we got here, no command field has been found before the end of the buffer |
203 | 39.5k | return nullptr; |
204 | 285k | } |
205 | | |
206 | | int16_t TelnetLayer::getSubCommand(uint8_t* pos, size_t len) |
207 | 55.1k | { |
208 | 55.1k | if (len < 3 || pos[1] < static_cast<int>(TelnetCommand::Subnegotiation)) |
209 | 34.5k | return static_cast<int>(TelnetOption::TelnetOptionNoOption); |
210 | 20.6k | return pos[2]; |
211 | 55.1k | } |
212 | | |
213 | | uint8_t* TelnetLayer::getCommandData(uint8_t* pos, size_t& len) |
214 | 55.1k | { |
215 | 55.1k | if (pos[1] == static_cast<int>(TelnetCommand::Subnegotiation) && len > 3) |
216 | 12.1k | { |
217 | 12.1k | len -= 3; |
218 | 12.1k | return &pos[3]; |
219 | 12.1k | } |
220 | 43.0k | len = 0; |
221 | 43.0k | return nullptr; |
222 | 55.1k | } |
223 | | |
224 | | std::string TelnetLayer::getDataAsString(bool removeEscapeCharacters) |
225 | 36.2k | { |
226 | 36.2k | if (m_Data == nullptr) |
227 | 0 | { |
228 | 0 | PCPP_LOG_DEBUG("Layer does not have data"); |
229 | 0 | return {}; |
230 | 0 | } |
231 | | |
232 | | // Convert to string |
233 | 36.2k | if (removeEscapeCharacters) |
234 | 36.2k | { |
235 | 36.2k | uint8_t* dataPos = nullptr; |
236 | 36.2k | switch (getTelnetSequenceType(m_Data, m_DataLen)) |
237 | 36.2k | { |
238 | 155 | case TelnetSequenceType::Unknown: |
239 | 155 | { |
240 | 155 | PCPP_LOG_DEBUG("Telnet Parse Error: Unknown sequence found during data string extraction."); |
241 | 155 | return {}; |
242 | 0 | } |
243 | 17.2k | case TelnetSequenceType::UserData: |
244 | 17.2k | dataPos = m_Data; |
245 | 17.2k | break; |
246 | 18.8k | case TelnetSequenceType::Command: |
247 | 18.8k | dataPos = getNextDataField(m_Data, m_DataLen); |
248 | 18.8k | break; |
249 | 0 | default: |
250 | 0 | throw std::logic_error("Unsupported sequence type"); |
251 | 36.2k | } |
252 | | |
253 | 36.0k | if (!dataPos) |
254 | 5.21k | { |
255 | 5.21k | PCPP_LOG_DEBUG("Packet does not have a data field"); |
256 | 5.21k | return std::string(); |
257 | 5.21k | } |
258 | | |
259 | 30.8k | PCPP_ASSERT(dataPos >= m_Data && dataPos < (m_Data + m_DataLen), |
260 | 30.8k | "Data position is out of bounds, this should never happen!"); |
261 | | |
262 | | // End of range is corrected by the advance offset. |
263 | 30.8k | auto const* beginIt = dataPos; |
264 | 30.8k | auto const* endIt = dataPos + m_DataLen - std::distance(m_Data, dataPos); |
265 | | |
266 | 30.8k | std::string result; |
267 | 602k | std::copy_if(beginIt, endIt, std::back_inserter(result), [](char ch) -> bool { |
268 | 602k | return ch > 31 && ch < 127; // From SPACE to ~ |
269 | 602k | }); |
270 | 30.8k | return result; |
271 | 36.0k | } |
272 | 0 | return std::string(reinterpret_cast<char*>(m_Data), m_DataLen); |
273 | 36.2k | } |
274 | | |
275 | | size_t TelnetLayer::getTotalNumberOfCommands() |
276 | 8.63k | { |
277 | 8.63k | size_t ctr = 0; |
278 | 8.63k | if (isTelnetCommand(m_Data, m_DataLen)) |
279 | 4.55k | ++ctr; |
280 | | |
281 | 8.63k | uint8_t* pos = m_Data; |
282 | 40.2k | while (pos != nullptr) |
283 | 31.6k | { |
284 | 31.6k | size_t offset = pos - m_Data; |
285 | 31.6k | pos = getNextCommandField(pos, m_DataLen - offset); |
286 | 31.6k | if (pos) |
287 | 23.0k | ++ctr; |
288 | 31.6k | } |
289 | | |
290 | 8.63k | return ctr; |
291 | 8.63k | } |
292 | | |
293 | | size_t TelnetLayer::getNumberOfCommands(TelnetCommand command) |
294 | 36.2k | { |
295 | 36.2k | if (static_cast<int>(command) < 0) |
296 | 8.63k | return 0; |
297 | | |
298 | 27.5k | size_t ctr = 0; |
299 | 27.5k | if (isTelnetCommand(m_Data, m_DataLen) && m_Data[1] == static_cast<int>(command)) |
300 | 6.29k | ++ctr; |
301 | | |
302 | 27.5k | uint8_t* pos = m_Data; |
303 | 147k | while (pos != nullptr) |
304 | 120k | { |
305 | 120k | size_t offset = pos - m_Data; |
306 | 120k | pos = getNextCommandField(pos, m_DataLen - offset); |
307 | 120k | if (pos && pos[1] == static_cast<int>(command)) |
308 | 28.0k | ++ctr; |
309 | 120k | } |
310 | | |
311 | 27.5k | return ctr; |
312 | 36.2k | } |
313 | | |
314 | | TelnetLayer::TelnetCommand TelnetLayer::getFirstCommand() |
315 | 8.63k | { |
316 | | // If starts with command |
317 | 8.63k | if (isTelnetCommand(m_Data, m_DataLen)) |
318 | 4.55k | return static_cast<TelnetCommand>(m_Data[1]); |
319 | | |
320 | | // Check is there any command |
321 | 4.07k | uint8_t* pos = getNextCommandField(m_Data, m_DataLen); |
322 | 4.07k | if (pos) |
323 | 3.56k | return static_cast<TelnetCommand>(pos[1]); |
324 | 508 | return TelnetCommand::TelnetCommandEndOfPacket; |
325 | 4.07k | } |
326 | | |
327 | | TelnetLayer::TelnetCommand TelnetLayer::getNextCommand() |
328 | 36.2k | { |
329 | 36.2k | if (lastPositionOffset == SIZE_MAX) |
330 | 8.63k | { |
331 | 8.63k | lastPositionOffset = 0; |
332 | 8.63k | if (isTelnetCommand(m_Data, m_DataLen)) |
333 | 4.55k | return static_cast<TelnetLayer::TelnetCommand>(m_Data[1]); |
334 | 8.63k | } |
335 | | |
336 | 31.6k | uint8_t* pos = getNextCommandField(&m_Data[lastPositionOffset], m_DataLen - lastPositionOffset); |
337 | 31.6k | if (pos) |
338 | 23.0k | { |
339 | 23.0k | lastPositionOffset = pos - m_Data; |
340 | 23.0k | return static_cast<TelnetLayer::TelnetCommand>(pos[1]); |
341 | 23.0k | } |
342 | 8.63k | lastPositionOffset = SIZE_MAX; |
343 | 8.63k | return TelnetCommand::TelnetCommandEndOfPacket; |
344 | 31.6k | } |
345 | | |
346 | | TelnetLayer::TelnetOption TelnetLayer::getOption() |
347 | 36.2k | { |
348 | 36.2k | if (lastPositionOffset < m_DataLen) |
349 | 27.5k | return static_cast<TelnetOption>(getSubCommand( |
350 | 27.5k | &m_Data[lastPositionOffset], getFieldLen(&m_Data[lastPositionOffset], m_DataLen - lastPositionOffset))); |
351 | 8.63k | return TelnetOption::TelnetOptionNoOption; |
352 | 36.2k | } |
353 | | |
354 | | TelnetLayer::TelnetOption TelnetLayer::getOption(TelnetCommand command) |
355 | 36.2k | { |
356 | | // Check input |
357 | 36.2k | if (static_cast<int>(command) < 0) |
358 | 8.63k | { |
359 | 8.63k | PCPP_LOG_ERROR("Command type can't be negative"); |
360 | 8.63k | return TelnetOption::TelnetOptionNoOption; |
361 | 8.63k | } |
362 | | |
363 | 27.5k | if (isTelnetCommand(m_Data, m_DataLen) && m_Data[1] == static_cast<int>(command)) |
364 | 6.29k | return static_cast<TelnetOption>(getSubCommand(m_Data, getFieldLen(m_Data, m_DataLen))); |
365 | | |
366 | 21.2k | uint8_t* pos = m_Data; |
367 | 49.1k | while (pos != nullptr) |
368 | 49.1k | { |
369 | 49.1k | size_t offset = pos - m_Data; |
370 | 49.1k | pos = getNextCommandField(pos, m_DataLen - offset); |
371 | | |
372 | 49.1k | if (pos && pos[1] == static_cast<int>(command)) |
373 | 21.2k | { |
374 | 21.2k | offset = pos - m_Data; |
375 | 21.2k | return static_cast<TelnetOption>(getSubCommand(pos, getFieldLen(pos, m_DataLen - offset))); |
376 | 21.2k | } |
377 | 49.1k | } |
378 | | |
379 | 0 | PCPP_LOG_DEBUG("Can't find requested command"); |
380 | 0 | return TelnetOption::TelnetOptionNoOption; |
381 | 21.2k | } |
382 | | |
383 | | uint8_t* TelnetLayer::getOptionData(size_t& length) |
384 | 36.2k | { |
385 | 36.2k | if (lastPositionOffset < m_DataLen) |
386 | 27.5k | { |
387 | 27.5k | size_t lenBuffer = getFieldLen(&m_Data[lastPositionOffset], m_DataLen - lastPositionOffset); |
388 | 27.5k | uint8_t* posBuffer = getCommandData(&m_Data[lastPositionOffset], lenBuffer); |
389 | | |
390 | 27.5k | length = lenBuffer; |
391 | 27.5k | return posBuffer; |
392 | 27.5k | } |
393 | 8.63k | return nullptr; |
394 | 36.2k | } |
395 | | |
396 | | uint8_t* TelnetLayer::getOptionData(TelnetCommand command, size_t& length) |
397 | 36.2k | { |
398 | | // Check input |
399 | 36.2k | if (static_cast<int>(command) < 0) |
400 | 8.63k | { |
401 | 8.63k | PCPP_LOG_ERROR("Command type can't be negative"); |
402 | 8.63k | length = 0; |
403 | 8.63k | return nullptr; |
404 | 8.63k | } |
405 | | |
406 | 27.5k | if (isTelnetCommand(m_Data, m_DataLen) && m_Data[1] == static_cast<int>(command)) |
407 | 6.29k | { |
408 | 6.29k | size_t lenBuffer = getFieldLen(m_Data, m_DataLen); |
409 | 6.29k | uint8_t* posBuffer = getCommandData(m_Data, lenBuffer); |
410 | | |
411 | 6.29k | length = lenBuffer; |
412 | 6.29k | return posBuffer; |
413 | 6.29k | } |
414 | | |
415 | 21.2k | uint8_t* pos = m_Data; |
416 | 49.1k | while (pos != nullptr) |
417 | 49.1k | { |
418 | 49.1k | size_t offset = pos - m_Data; |
419 | 49.1k | pos = getNextCommandField(pos, m_DataLen - offset); |
420 | | |
421 | 49.1k | if (pos && pos[1] == static_cast<int>(command)) |
422 | 21.2k | { |
423 | 21.2k | offset = pos - m_Data; |
424 | 21.2k | size_t lenBuffer = getFieldLen(pos, m_DataLen - offset); |
425 | 21.2k | uint8_t* posBuffer = getCommandData(pos, lenBuffer); |
426 | | |
427 | 21.2k | length = lenBuffer; |
428 | 21.2k | return posBuffer; |
429 | 21.2k | } |
430 | 49.1k | } |
431 | | |
432 | 0 | PCPP_LOG_DEBUG("Can't find requested command"); |
433 | 0 | length = 0; |
434 | 0 | return nullptr; |
435 | 21.2k | } |
436 | | |
437 | | std::string TelnetLayer::getTelnetCommandAsString(TelnetCommand val) |
438 | 36.2k | { |
439 | 36.2k | switch (val) |
440 | 36.2k | { |
441 | 8.63k | case TelnetCommand::TelnetCommandEndOfPacket: |
442 | 8.63k | return "Reached end of packet while parsing"; |
443 | 50 | case TelnetCommand::EndOfFile: |
444 | 50 | return "End of File"; |
445 | 32 | case TelnetCommand::Suspend: |
446 | 32 | return "Suspend current process"; |
447 | 132 | case TelnetCommand::Abort: |
448 | 132 | return "Abort Process"; |
449 | 1.33k | case TelnetCommand::EndOfRecordCommand: |
450 | 1.33k | return "End of Record"; |
451 | 1.38k | case TelnetCommand::SubnegotiationEnd: |
452 | 1.38k | return "Subnegotiation End"; |
453 | 65 | case TelnetCommand::NoOperation: |
454 | 65 | return "No Operation"; |
455 | 80 | case TelnetCommand::DataMark: |
456 | 80 | return "Data Mark"; |
457 | 1 | case TelnetCommand::Break: |
458 | 1 | return "Break"; |
459 | 622 | case TelnetCommand::InterruptProcess: |
460 | 622 | return "Interrupt Process"; |
461 | 182 | case TelnetCommand::AbortOutput: |
462 | 182 | return "Abort Output"; |
463 | 110 | case TelnetCommand::AreYouThere: |
464 | 110 | return "Are You There"; |
465 | 215 | case TelnetCommand::EraseCharacter: |
466 | 215 | return "Erase Character"; |
467 | 185 | case TelnetCommand::EraseLine: |
468 | 185 | return "Erase Line"; |
469 | 381 | case TelnetCommand::GoAhead: |
470 | 381 | return "Go Ahead"; |
471 | 7.15k | case TelnetCommand::Subnegotiation: |
472 | 7.15k | return "Subnegotiation"; |
473 | 632 | case TelnetCommand::WillPerform: |
474 | 632 | return "Will Perform"; |
475 | 225 | case TelnetCommand::WontPerform: |
476 | 225 | return "Wont Perform"; |
477 | 584 | case TelnetCommand::DoPerform: |
478 | 584 | return "Do Perform"; |
479 | 2.69k | case TelnetCommand::DontPerform: |
480 | 2.69k | return "Dont Perform"; |
481 | 0 | case TelnetCommand::InterpretAsCommand: |
482 | 0 | return "Interpret As Command"; |
483 | 11.5k | default: |
484 | 11.5k | return "Unknown Command"; |
485 | 36.2k | } |
486 | 36.2k | } |
487 | | |
488 | | std::string TelnetLayer::getTelnetOptionAsString(TelnetOption val) |
489 | 36.2k | { |
490 | 36.2k | switch (val) |
491 | 36.2k | { |
492 | 25.9k | case TelnetOption::TelnetOptionNoOption: |
493 | 25.9k | return "No option for this command"; |
494 | 17 | case TelnetOption::TransmitBinary: |
495 | 17 | return "Binary Transmission"; |
496 | 155 | case TelnetOption::Echo: |
497 | 155 | return "Echo"; |
498 | 18 | case TelnetOption::Reconnection: |
499 | 18 | return "Reconnection"; |
500 | 0 | case TelnetOption::SuppressGoAhead: |
501 | 0 | return "Suppress Go Ahead"; |
502 | 0 | case TelnetOption::ApproxMsgSizeNegotiation: |
503 | 0 | return "Negotiate approximate message size"; |
504 | 43 | case TelnetOption::Status: |
505 | 43 | return "Status"; |
506 | 0 | case TelnetOption::TimingMark: |
507 | 0 | return "Timing Mark"; |
508 | 68 | case TelnetOption::RemoteControlledTransAndEcho: |
509 | 68 | return "Remote Controlled Transmission and Echo"; |
510 | 451 | case TelnetOption::OutputLineWidth: |
511 | 451 | return "Output Line Width"; |
512 | 48 | case TelnetOption::OutputPageSize: |
513 | 48 | return "Output Page Size"; |
514 | 0 | case TelnetOption::OutputCarriageReturnDisposition: |
515 | 0 | return "Negotiate About Output Carriage-Return Disposition"; |
516 | 48 | case TelnetOption::OutputHorizontalTabStops: |
517 | 48 | return "Negotiate About Output Horizontal Tabstops"; |
518 | 0 | case TelnetOption::OutputHorizontalTabDisposition: |
519 | 0 | return "Negotiate About Output Horizontal Tab Disposition"; |
520 | 48 | case TelnetOption::OutputFormfeedDisposition: |
521 | 48 | return "Negotiate About Output Formfeed Disposition"; |
522 | 16 | case TelnetOption::OutputVerticalTabStops: |
523 | 16 | return "Negotiate About Vertical Tabstops"; |
524 | 3.79k | case TelnetOption::OutputVerticalTabDisposition: |
525 | 3.79k | return "Negotiate About Output Vertcial Tab Disposition"; |
526 | 17 | case TelnetOption::OutputLinefeedDisposition: |
527 | 17 | return "Negotiate About Output Linefeed Disposition"; |
528 | 352 | case TelnetOption::ExtendedASCII: |
529 | 352 | return "Extended ASCII"; |
530 | 56 | case TelnetOption::Logout: |
531 | 56 | return "Logout"; |
532 | 272 | case TelnetOption::ByteMacro: |
533 | 272 | return "Byte Macro"; |
534 | 48 | case TelnetOption::DataEntryTerminal: |
535 | 48 | return "Data Entry Terminal"; |
536 | 16 | case TelnetOption::SUPDUP: |
537 | 16 | return "SUPDUP"; |
538 | 23 | case TelnetOption::SUPDUPOutput: |
539 | 23 | return "SUPDUP Output"; |
540 | 0 | case TelnetOption::SendLocation: |
541 | 0 | return "Send Location"; |
542 | 0 | case TelnetOption::TerminalType: |
543 | 0 | return "Terminal Type"; |
544 | 400 | case TelnetOption::EndOfRecordOption: |
545 | 400 | return "End Of Record"; |
546 | 0 | case TelnetOption::TACACSUserIdentification: |
547 | 0 | return "TACACS User Identification"; |
548 | 16 | case TelnetOption::OutputMarking: |
549 | 16 | return "Output Marking"; |
550 | 0 | case TelnetOption::TerminalLocationNumber: |
551 | 0 | return "Terminal Location Number"; |
552 | 57 | case TelnetOption::Telnet3270Regime: |
553 | 57 | return "Telnet 3270 Regime"; |
554 | 143 | case TelnetOption::X3Pad: |
555 | 143 | return "X3 Pad"; |
556 | 0 | case TelnetOption::NegotiateAboutWindowSize: |
557 | 0 | return "Negotiate About Window Size"; |
558 | 112 | case TelnetOption::TerminalSpeed: |
559 | 112 | return "Terminal Speed"; |
560 | 112 | case TelnetOption::RemoteFlowControl: |
561 | 112 | return "Remote Flow Control"; |
562 | 0 | case TelnetOption::Linemode: |
563 | 0 | return "Line mode"; |
564 | 16 | case TelnetOption::XDisplayLocation: |
565 | 16 | return "X Display Location"; |
566 | 50 | case TelnetOption::EnvironmentOption: |
567 | 50 | return "Environment Option"; |
568 | 129 | case TelnetOption::AuthenticationOption: |
569 | 129 | return "Authentication Option"; |
570 | 283 | case TelnetOption::EncryptionOption: |
571 | 283 | return "Encryption Option"; |
572 | 24 | case TelnetOption::NewEnvironmentOption: |
573 | 24 | return "New Environment Option"; |
574 | 0 | case TelnetOption::TN3270E: |
575 | 0 | return "TN3270E"; |
576 | 0 | case TelnetOption::XAuth: |
577 | 0 | return "X Server Authentication"; |
578 | 0 | case TelnetOption::Charset: |
579 | 0 | return "Charset"; |
580 | 0 | case TelnetOption::TelnetRemoteSerialPort: |
581 | 0 | return "Telnet Remote Serial Port"; |
582 | 133 | case TelnetOption::ComPortControlOption: |
583 | 133 | return "Com Port Control Option"; |
584 | 0 | case TelnetOption::TelnetSuppressLocalEcho: |
585 | 0 | return "Telnet Suppress Local Echo"; |
586 | 0 | case TelnetOption::TelnetStartTLS: |
587 | 0 | return "Telnet Start TLS"; |
588 | 32 | case TelnetOption::Kermit: |
589 | 32 | return "Kermit"; |
590 | 0 | case TelnetOption::SendURL: |
591 | 0 | return "Send URL"; |
592 | 0 | case TelnetOption::ForwardX: |
593 | 0 | return "Forward X Server"; |
594 | 48 | case TelnetOption::TelOptPragmaLogon: |
595 | 48 | return "Telnet Option Pragma Logon"; |
596 | 292 | case TelnetOption::TelOptSSPILogon: |
597 | 292 | return "Telnet Option SSPI Logon"; |
598 | 48 | case TelnetOption::TelOptPragmaHeartbeat: |
599 | 48 | return "Telnet Option Pragma Heartbeat"; |
600 | 2.57k | case TelnetOption::ExtendedOptions: |
601 | 2.57k | return "Extended option list"; |
602 | 256 | default: |
603 | 256 | return "Unknown Option"; |
604 | 36.2k | } |
605 | 36.2k | } |
606 | | |
607 | | std::string TelnetLayer::toString() const |
608 | 17.2k | { |
609 | | // TODO: Perhaps print the entire sequence of commands and data? |
610 | 17.2k | switch (getTelnetSequenceType(m_Data, m_DataLen)) |
611 | 17.2k | { |
612 | 310 | case TelnetSequenceType::Unknown: |
613 | 310 | return "Telnet Unknown"; |
614 | 9.11k | case TelnetSequenceType::Command: |
615 | 9.11k | return "Telnet Control"; |
616 | 7.83k | case TelnetSequenceType::UserData: |
617 | 7.83k | return "Telnet Data"; |
618 | 0 | default: |
619 | 0 | throw std::logic_error("Unsupported sequence type"); |
620 | 17.2k | } |
621 | 17.2k | } |
622 | | |
623 | | } // namespace pcpp |