Coverage Report

Created: 2025-11-11 07:46

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
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