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