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