Coverage Report

Created: 2024-02-25 06:29

/src/PcapPlusPlus/Packet++/header/TcpLayer.h
Line
Count
Source (jump to first uncovered line)
1
#pragma once
2
3
#include "Layer.h"
4
#include "TLVData.h"
5
#include <string.h>
6
7
/// @file
8
9
/**
10
 * \namespace pcpp
11
 * \brief The main namespace for the PcapPlusPlus lib
12
 */
13
namespace pcpp
14
{
15
16
  /**
17
   * @struct tcphdr
18
   * Represents an TCP protocol header
19
   */
20
#pragma pack(push,1)
21
  struct tcphdr
22
  {
23
    /** Source TCP port */
24
    uint16_t portSrc;
25
    /** Destination TCP port */
26
    uint16_t portDst;
27
    /** Sequence number */
28
    uint32_t sequenceNumber;
29
    /** Acknowledgment number */
30
    uint32_t ackNumber;
31
#if (BYTE_ORDER == LITTLE_ENDIAN)
32
    uint16_t reserved:4,
33
    /** Specifies the size of the TCP header in 32-bit words */
34
    dataOffset:4,
35
    /** FIN flag */
36
    finFlag:1,
37
    /** SYN flag */
38
    synFlag:1,
39
    /** RST flag */
40
    rstFlag:1,
41
    /** PSH flag */
42
    pshFlag:1,
43
    /** ACK flag */
44
    ackFlag:1,
45
    /** URG flag */
46
    urgFlag:1,
47
    /** ECE flag */
48
    eceFlag:1,
49
    /** CWR flag */
50
    cwrFlag:1;
51
#elif (BYTE_ORDER == BIG_ENDIAN)
52
    /** Specifies the size of the TCP header in 32-bit words */
53
    uint16_t dataOffset:4,
54
    reserved:4,
55
    /** CWR flag */
56
    cwrFlag:1,
57
    /** ECE flag */
58
    eceFlag:1,
59
    /** URG flag */
60
    urgFlag:1,
61
    /** ACK flag */
62
    ackFlag:1,
63
    /** PSH flag */
64
    pshFlag:1,
65
    /** RST flag */
66
    rstFlag:1,
67
    /** SYN flag */
68
    synFlag:1,
69
    /** FIN flag */
70
    finFlag:1;
71
#else
72
#error  "Endian is not LE nor BE..."
73
#endif
74
    /** The size of the receive window, which specifies the number of window size units (by default, bytes) */
75
    uint16_t  windowSize;
76
    /** The 16-bit checksum field is used for error-checking of the header and data */
77
    uint16_t  headerChecksum;
78
    /** If the URG flag (@ref tcphdr#urgFlag) is set, then this 16-bit field is an offset from the sequence number indicating the last urgent data byte */
79
    uint16_t  urgentPointer;
80
  };
81
#pragma pack(pop)
82
83
84
  /**
85
   * TCP options types
86
   */
87
  enum TcpOptionType : uint8_t
88
  {
89
    /** Padding */
90
    PCPP_TCPOPT_NOP =       1,
91
    /** End of options */
92
    PCPP_TCPOPT_EOL =       0,
93
    /** Segment size negotiating */
94
    TCPOPT_MSS =            2,
95
    /** Window scaling */
96
    PCPP_TCPOPT_WINDOW =    3,
97
    /** SACK Permitted */
98
    TCPOPT_SACK_PERM =      4,
99
    /** SACK Block */
100
    PCPP_TCPOPT_SACK =      5,
101
    /** Echo (obsoleted by option ::PCPP_TCPOPT_TIMESTAMP) */
102
    TCPOPT_ECHO =           6,
103
    /** Echo Reply (obsoleted by option ::PCPP_TCPOPT_TIMESTAMP) */
104
    TCPOPT_ECHOREPLY =      7,
105
    /** TCP Timestamps */
106
    PCPP_TCPOPT_TIMESTAMP = 8,
107
    /** CC (obsolete) */
108
    TCPOPT_CC =             11,
109
    /** CC.NEW (obsolete) */
110
    TCPOPT_CCNEW =          12,
111
    /** CC.ECHO(obsolete) */
112
    TCPOPT_CCECHO =         13,
113
    /** MD5 Signature Option */
114
    TCPOPT_MD5 =            19,
115
    /** Multipath TCP */
116
    TCPOPT_MPTCP =          0x1e,
117
    /** SCPS Capabilities */
118
    TCPOPT_SCPS =           20,
119
    /** SCPS SNACK */
120
    TCPOPT_SNACK =          21,
121
    /** SCPS Record Boundary */
122
    TCPOPT_RECBOUND =       22,
123
    /** SCPS Corruption Experienced */
124
    TCPOPT_CORREXP =        23,
125
    /** Quick-Start Response */
126
    TCPOPT_QS =             27,
127
    /** User Timeout Option (also, other known unauthorized use) */
128
    TCPOPT_USER_TO =        28,
129
    /** RFC3692-style Experiment 1 (also improperly used for shipping products) */
130
    TCPOPT_EXP_FD =         0xfd,
131
    /** RFC3692-style Experiment 2 (also improperly used for shipping products) */
132
    TCPOPT_EXP_FE =         0xfe,
133
    /** Riverbed probe option, non IANA registered option number */
134
    TCPOPT_RVBD_PROBE =     76,
135
    /** Riverbed transparency option, non IANA registered option number */
136
    TCPOPT_RVBD_TRPY =      78,
137
    /** Unknown option */
138
    TCPOPT_Unknown =        255
139
  };
140
141
142
  // TCP option lengths
143
144
  /** pcpp::PCPP_TCPOPT_NOP length */
145
#define PCPP_TCPOLEN_NOP            1
146
  /** pcpp::PCPP_TCPOPT_EOL length */
147
#define PCPP_TCPOLEN_EOL            1
148
  /** pcpp::TCPOPT_MSS length */
149
#define PCPP_TCPOLEN_MSS            4
150
  /** pcpp::PCPP_TCPOPT_WINDOW length */
151
#define PCPP_TCPOLEN_WINDOW         3
152
  /** pcpp::TCPOPT_SACK_PERM length */
153
#define PCPP_TCPOLEN_SACK_PERM      2
154
  /** pcpp::PCPP_TCPOPT_SACK length */
155
#define PCPP_TCPOLEN_SACK_MIN       2
156
  /** pcpp::TCPOPT_ECHO length */
157
#define PCPP_TCPOLEN_ECHO           6
158
  /** pcpp::TCPOPT_ECHOREPLY length */
159
#define PCPP_TCPOLEN_ECHOREPLY      6
160
  /** pcpp::PCPP_TCPOPT_TIMESTAMP length */
161
#define PCPP_TCPOLEN_TIMESTAMP     10
162
  /** pcpp::TCPOPT_CC length */
163
#define PCPP_TCPOLEN_CC             6
164
  /** pcpp::TCPOPT_CCNEW length */
165
#define PCPP_TCPOLEN_CCNEW          6
166
  /** pcpp::TCPOPT_CCECHO length */
167
#define PCPP_TCPOLEN_CCECHO         6
168
  /** pcpp::TCPOPT_MD5 length */
169
#define PCPP_TCPOLEN_MD5           18
170
  /** pcpp::TCPOPT_MPTCP length */
171
#define PCPP_TCPOLEN_MPTCP_MIN      8
172
  /** pcpp::TCPOPT_SCPS length */
173
#define PCPP_TCPOLEN_SCPS           4
174
  /** pcpp::TCPOPT_SNACK length */
175
#define PCPP_TCPOLEN_SNACK          6
176
  /** pcpp::TCPOPT_RECBOUND length */
177
#define PCPP_TCPOLEN_RECBOUND       2
178
  /** pcpp::TCPOPT_CORREXP length */
179
#define PCPP_TCPOLEN_CORREXP        2
180
  /** pcpp::TCPOPT_QS length */
181
#define PCPP_TCPOLEN_QS             8
182
  /** pcpp::TCPOPT_USER_TO length */
183
#define PCPP_TCPOLEN_USER_TO        4
184
  /** pcpp::TCPOPT_RVBD_PROBE length */
185
#define PCPP_TCPOLEN_RVBD_PROBE_MIN 3
186
  /** pcpp::TCPOPT_RVBD_TRPY length */
187
#define PCPP_TCPOLEN_RVBD_TRPY_MIN 16
188
  /** pcpp::TCPOPT_EXP_FD and pcpp::TCPOPT_EXP_FE length */
189
#define PCPP_TCPOLEN_EXP_MIN        2
190
191
192
193
  /**
194
   * @class TcpOption
195
   * A wrapper class for TCP options. This class does not create or modify TCP option records, but rather
196
   * serves as a wrapper and provides useful methods for retrieving data from them
197
   */
198
  class TcpOption : public TLVRecord<uint8_t, uint8_t>
199
  {
200
  public:
201
202
    /**
203
     * A c'tor for this class that gets a pointer to the option raw data (byte array)
204
     * @param[in] optionRawData A pointer to the TCP option raw data
205
     */
206
494k
    explicit TcpOption(uint8_t* optionRawData) : TLVRecord(optionRawData) { }
207
208
    /**
209
     * A d'tor for this class, currently does nothing
210
     */
211
0
    ~TcpOption() { }
212
213
    /**
214
     * @return TCP option type casted as pcpp::TcpOptionType enum. If the data is null a value
215
     * of ::TCPOPT_Unknown is returned
216
     */
217
    TcpOptionType getTcpOptionType() const
218
0
    {
219
0
      return getTcpOptionType(m_Data);
220
0
    }
221
222
    /**
223
     * Check if a pointer can be assigned to the TLV record data
224
     * @param[in] recordRawData A pointer to the TLV record raw data
225
     * @param[in] tlvDataLen The size of the TLV record raw data
226
     * @return True if data is valid and can be assigned
227
     */
228
    static bool canAssign(const uint8_t* recordRawData, size_t tlvDataLen)
229
327k
    {
230
327k
      const auto* data = reinterpret_cast<const TLVRawData*>(recordRawData);
231
327k
      if (data == nullptr)
232
0
        return false;
233
234
327k
      if (tlvDataLen < sizeof(TLVRawData::recordType))
235
110k
        return false;
236
237
217k
      const auto recordType = getTcpOptionType(data);
238
217k
      if (recordType == TcpOptionType::PCPP_TCPOPT_NOP || recordType == TcpOptionType::PCPP_TCPOPT_EOL)
239
136k
        return true;
240
241
81.4k
      return TLVRecord<uint8_t, uint8_t>::canAssign(recordRawData, tlvDataLen);
242
217k
    }
243
244
    // implement abstract methods
245
246
    size_t getTotalSize() const
247
1.18M
    {
248
1.18M
      if (m_Data == nullptr)
249
0
        return 0;
250
251
1.18M
      const auto recordType = getTcpOptionType(m_Data);
252
1.18M
      if (recordType == TcpOptionType::PCPP_TCPOPT_NOP || recordType == TcpOptionType::PCPP_TCPOPT_EOL)
253
811k
        return sizeof(uint8_t);
254
255
375k
      return static_cast<size_t>(m_Data->recordLen);
256
1.18M
    }
257
258
    size_t getDataSize() const
259
0
    {
260
0
      if (m_Data == nullptr)
261
0
        return 0;
262
263
0
      const auto recordType = getTcpOptionType(m_Data);
264
0
      if (recordType == TcpOptionType::PCPP_TCPOPT_NOP || recordType == TcpOptionType::PCPP_TCPOPT_EOL)
265
0
        return 0;
266
267
0
      return static_cast<size_t>(m_Data->recordLen) - (2*sizeof(uint8_t));
268
0
    }
269
270
  private:
271
    static TcpOptionType getTcpOptionType(const TLVRawData* optionRawData)
272
1.40M
    {
273
1.40M
      if (optionRawData == nullptr)
274
0
        return TcpOptionType::TCPOPT_Unknown;
275
276
1.40M
      return static_cast<TcpOptionType>(optionRawData->recordType);
277
1.40M
    }
278
  };
279
280
281
  /**
282
   * @class TcpOptionBuilder
283
   * A class for building TCP option records. This builder receives the TCP option parameters in its c'tor,
284
   * builds the TCP option raw buffer and provides a build() method to get a TcpOption object out of it
285
   */
286
  class TcpOptionBuilder : public TLVRecordBuilder
287
  {
288
289
  public:
290
291
    /**
292
     * An enum to describe NOP and EOL TCP options. Used in one of this class's c'tors
293
     */
294
    enum NopEolOptionTypes : uint8_t
295
    {
296
      /** NOP TCP option */
297
      NOP,
298
      /** EOL TCP option */
299
      EOL
300
    };
301
302
    /**
303
     * A c'tor for building TCP options which their value is a byte array. The TcpOption object can be later
304
     * retrieved by calling build()
305
     * @param[in] optionType TCP option type
306
     * @param[in] optionValue A buffer containing the option value. This buffer is read-only and isn't modified in any way.
307
     * @param[in] optionValueLen Option value length in bytes
308
     */
309
    TcpOptionBuilder(TcpOptionType optionType, const uint8_t* optionValue, uint8_t optionValueLen) :
310
0
      TLVRecordBuilder((uint8_t)optionType, optionValue, optionValueLen) {}
311
312
    /**
313
     * A c'tor for building TCP options which have a 1-byte value. The TcpOption object can be later retrieved
314
     * by calling build()
315
     * @param[in] optionType TCP option type
316
     * @param[in] optionValue A 1-byte option value
317
     */
318
    TcpOptionBuilder(TcpOptionType optionType, uint8_t optionValue) :
319
0
      TLVRecordBuilder((uint8_t)optionType, optionValue) {}
320
321
    /**
322
     * A c'tor for building TCP options which have a 2-byte value. The TcpOption object can be later retrieved
323
     * by calling build()
324
     * @param[in] optionType TCP option type
325
     * @param[in] optionValue A 2-byte option value
326
     */
327
    TcpOptionBuilder(TcpOptionType optionType, uint16_t optionValue) :
328
0
      TLVRecordBuilder((uint8_t)optionType, optionValue) {}
329
330
    /**
331
     * A c'tor for building TCP options which have a 4-byte value. The TcpOption object can be later retrieved
332
     * by calling build()
333
     * @param[in] optionType TCP option type
334
     * @param[in] optionValue A 4-byte option value
335
     */
336
    TcpOptionBuilder(TcpOptionType optionType, uint32_t optionValue) :
337
0
      TLVRecordBuilder((uint8_t)optionType, optionValue) {}
338
339
    /**
340
     * A c'tor for building TCP NOP and EOL options. These option types are special in that they contain only 1 byte
341
     * which is the TCP option type (NOP or EOL). The TcpOption object can be later retrieved
342
     * by calling build()
343
     * @param[in] optionType An enum value indicating which option type to build (NOP or EOL)
344
     */
345
    explicit TcpOptionBuilder(NopEolOptionTypes optionType);
346
347
    /**
348
     * Build the TcpOption object out of the parameters defined in the c'tor
349
     * @return The TcpOption object
350
     */
351
    TcpOption build() const;
352
  };
353
354
355
  /**
356
   * @class TcpLayer
357
   * Represents a TCP (Transmission Control Protocol) protocol layer
358
   */
359
  class TcpLayer : public Layer
360
  {
361
  public:
362
    /**
363
     * A constructor that creates the layer from an existing packet raw data
364
     * @param[in] data A pointer to the raw data (will be casted to @ref tcphdr)
365
     * @param[in] dataLen Size of the data in bytes
366
     * @param[in] prevLayer A pointer to the previous layer
367
     * @param[in] packet A pointer to the Packet instance where layer will be stored in
368
     */
369
    TcpLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet);
370
371
    /**
372
     * A constructor that allocates a new TCP header with zero TCP options
373
     */
374
    TcpLayer();
375
376
    /**
377
     * A constructor that allocates a new TCP header with source port and destination port and zero TCP options
378
     * @param[in] portSrc Source port
379
     * @param[in] portDst Destination port
380
     */
381
    TcpLayer(uint16_t portSrc, uint16_t portDst);
382
383
728k
    ~TcpLayer() {}
384
385
    /**
386
     * A copy constructor that copy the entire header from the other TcpLayer (including TCP options)
387
     */
388
    TcpLayer(const TcpLayer& other);
389
390
    /**
391
     * An assignment operator that first delete all data from current layer and then copy the entire header from the other TcpLayer (including TCP options)
392
     */
393
    TcpLayer& operator=(const TcpLayer& other);
394
395
    /**
396
     * Get a pointer to the TCP header. Notice this points directly to the data, so every change will change the actual packet data
397
     * @return A pointer to the @ref tcphdr
398
     */
399
3.46M
    tcphdr* getTcpHeader() const { return (tcphdr*)m_Data; }
400
401
    /**
402
     * @return TCP source port
403
     */
404
    uint16_t getSrcPort() const;
405
406
    /**
407
     * @return TCP destination port
408
     */
409
    uint16_t getDstPort() const;
410
411
    /**
412
     * Get a TCP option by type
413
     * @param[in] option TCP option type to retrieve
414
     * @return An TcpOption object that contains the first option that matches this type, or logical NULL
415
     * (TcpOption#isNull() == true) if no such option found
416
     */
417
    TcpOption getTcpOption(TcpOptionType option) const;
418
419
    /**
420
     * @return The first TCP option in the packet. If the current layer contains no options the returned value will contain
421
     * a logical NULL (TcpOption#isNull() == true)
422
     */
423
    TcpOption getFirstTcpOption() const;
424
425
    /**
426
     * Get the TCP option that comes after a given option. If the given option was the last one, the
427
     * returned value will contain a logical NULL (TcpOption#isNull() == true)
428
     * @param[in] tcpOption A TCP option object that exists in the current layer
429
     * @return A TcpOption object that contains the TCP option data that comes next, or logical NULL if the given
430
     * TCP option: (1) was the last one; or (2) contains a logical NULL; or (3) doesn't belong to this packet
431
     */
432
    TcpOption getNextTcpOption(TcpOption& tcpOption) const;
433
434
    /**
435
     * @return The number of TCP options in this layer
436
     */
437
    size_t getTcpOptionCount() const;
438
439
    /**
440
     * Add a new TCP option at the end of the layer (after the last TCP option)
441
     * @param[in] optionBuilder A TcpOptionBuilder object that contains the TCP option data to be added
442
     * @return A TcpOption object that contains the newly added TCP option data or logical NULL
443
     * (TcpOption#isNull() == true) if addition failed. In case of a failure a corresponding error message will be
444
     * printed to log
445
     */
446
    TcpOption addTcpOption(const TcpOptionBuilder& optionBuilder);
447
448
    /**
449
     * Add a new TCP option after an existing one
450
     * @param[in] optionBuilder A TcpOptionBuilder object that contains the requested TCP option data to be added
451
     * @param[in] prevOptionType The TCP option which the newly added option should come after. This is an optional parameter which
452
     * gets a default value of ::TCPOPT_Unknown if omitted, which means the new option will be added as the first option in the layer
453
     * @return A TcpOption object containing the newly added TCP option data or logical NULL
454
     * (TcpOption#isNull() == true) if addition failed. In case of a failure a corresponding error message will be
455
     * printed to log
456
     */
457
    TcpOption addTcpOptionAfter(const TcpOptionBuilder& optionBuilder, TcpOptionType prevOptionType = TCPOPT_Unknown);
458
459
    /**
460
     * Remove an existing TCP option from the layer. TCP option is found by type
461
     * @param[in] optionType The TCP option type to remove
462
     * @return True if TCP option was removed or false if type wasn't found or if removal failed (in each case a proper error
463
     * will be written to log)
464
     */
465
    bool removeTcpOption(TcpOptionType optionType);
466
467
    /**
468
     * Remove all TCP options in this layer
469
     * @return True if all TCP options were successfully removed or false if removal failed for some reason
470
     * (a proper error will be written to log)
471
     */
472
    bool removeAllTcpOptions();
473
474
475
    /**
476
     * Calculate the checksum from header and data and possibly write the result to @ref tcphdr#headerChecksum
477
     * @param[in] writeResultToPacket If set to true then checksum result will be written to @ref tcphdr#headerChecksum
478
     * @return The checksum result
479
     */
480
    uint16_t calculateChecksum(bool writeResultToPacket);
481
482
    /**
483
     * The static method makes validation of input data
484
     * @param[in] data The pointer to the beginning of byte stream of TCP packet
485
     * @param[in] dataLen The length of byte stream
486
     * @return True if the data is valid and can represent a TCP packet
487
     */
488
    static inline bool isDataValid(const uint8_t* data, size_t dataLen);
489
490
    // implement abstract methods
491
492
    /**
493
     * Currently identifies the following next layers: HttpRequestLayer, HttpResponseLayer. Otherwise sets PayloadLayer
494
     */
495
    void parseNextLayer();
496
497
    /**
498
     * @return Size of @ref tcphdr + all TCP options
499
     */
500
1.44M
    size_t getHeaderLen() const { return getTcpHeader()->dataOffset*4 ;}
501
502
    /**
503
     * Calculate @ref tcphdr#headerChecksum field
504
     */
505
    void computeCalculateFields();
506
507
    std::string toString() const;
508
509
124k
    OsiModelLayer getOsiModelLayer() const { return OsiModelTransportLayer; }
510
511
  private:
512
513
    TLVRecordReader<TcpOption> m_OptionReader;
514
    int m_NumOfTrailingBytes;
515
516
    void initLayer();
517
309k
    uint8_t* getOptionsBasePtr() const { return m_Data + sizeof(tcphdr); }
518
    TcpOption addTcpOptionAt(const TcpOptionBuilder& optionBuilder, int offset);
519
    void adjustTcpOptionTrailer(size_t totalOptSize);
520
    void copyLayerData(const TcpLayer& other);
521
  };
522
523
524
  // implementation of inline methods
525
526
  bool TcpLayer::isDataValid(const uint8_t* data, size_t dataLen)
527
614k
  {
528
614k
    const tcphdr* hdr = reinterpret_cast<const tcphdr*>(data);
529
614k
    return dataLen >= sizeof(tcphdr)
530
614k
      && hdr->dataOffset >= 5 /* the minimum TCP header size */
531
614k
      && dataLen >= hdr->dataOffset * sizeof(uint32_t);
532
614k
  }
533
} // namespace pcpp