Coverage Report

Created: 2026-01-17 08:02

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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