Coverage Report

Created: 2026-10-17 07:09

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