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