Coverage Report

Created: 2026-08-13 07:36

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