Coverage Report

Created: 2026-08-14 06:52

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