Coverage Report

Created: 2026-03-31 07:53

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/PcapPlusPlus/Packet++/header/DhcpLayer.h
Line
Count
Source
1
#pragma once
2
3
#include "Layer.h"
4
#include "TLVData.h"
5
#include "IpAddress.h"
6
#include "MacAddress.h"
7
#include <string.h>
8
9
/// @file
10
11
/// @namespace pcpp
12
/// @brief The main namespace for the PcapPlusPlus lib
13
namespace pcpp
14
{
15
  /// @struct dhcp_header
16
  /// Represents a DHCP protocol header
17
#pragma pack(push, 1)
18
  struct dhcp_header
19
  {
20
    /// BootP opcode
21
    uint8_t opCode;
22
    /// Hardware type, set to 1 (Ethernet) by default
23
    uint8_t hardwareType;
24
    /// Hardware address length, set to 6 (MAC address length) by default
25
    uint8_t hardwareAddressLength;
26
    /// Hop count
27
    uint8_t hops;
28
    /// DHCP/BootP transaction ID
29
    uint32_t transactionID;
30
    /// The elapsed time, in seconds since the client sent its first BOOTREQUEST message
31
    uint16_t secondsElapsed;
32
    /// BootP flags
33
    uint16_t flags;
34
    /// Client IPv4 address
35
    uint32_t clientIpAddress;
36
    /// Your IPv4 address
37
    uint32_t yourIpAddress;
38
    /// Server IPv4 address
39
    uint32_t serverIpAddress;
40
    /// Gateway IPv4 address
41
    uint32_t gatewayIpAddress;
42
    /// Client hardware address, by default contains the MAC address (only 6 first bytes are used)
43
    uint8_t clientHardwareAddress[16];
44
    /// BootP server name
45
    uint8_t serverName[64];
46
    /// BootP boot file name
47
    uint8_t bootFilename[128];
48
    /// DHCP magic number (set to the default value of 0x63538263)
49
    uint32_t magicNumber;
50
  };
51
#pragma pack(pop)
52
  static_assert(sizeof(dhcp_header) == 240, "dhcp_header size is not 240 bytes");
53
54
  /// BootP opcodes
55
  enum BootpOpCodes
56
  {
57
    /// BootP request
58
    DHCP_BOOTREQUEST = 1,
59
    /// BootP reply
60
    DHCP_BOOTREPLY = 2
61
  };
62
63
  /// DHCP message types
64
  enum DhcpMessageType
65
  {
66
    /// Unknown message type
67
    DHCP_UNKNOWN_MSG_TYPE = 0,
68
    /// Discover message type
69
    DHCP_DISCOVER = 1,
70
    /// Offer message type
71
    DHCP_OFFER = 2,
72
    /// Request message type
73
    DHCP_REQUEST = 3,
74
    /// Decline message type
75
    DHCP_DECLINE = 4,
76
    /// Acknowledge message type
77
    DHCP_ACK = 5,
78
    /// Non-acknowledge message type
79
    DHCP_NAK = 6,
80
    /// Release message type
81
    DHCP_RELEASE = 7,
82
    /// Inform message type
83
    DHCP_INFORM = 8
84
  };
85
86
  /// DHCP option types.
87
  enum DhcpOptionTypes
88
  {
89
    /// Unknown option type
90
    DHCPOPT_UNKNOWN = -1,
91
    /// Pad
92
    DHCPOPT_PAD = 0,
93
    /// Subnet Mask Value
94
    DHCPOPT_SUBNET_MASK = 1,
95
    /// Time Offset in Seconds from UTC
96
    DHCPOPT_TIME_OFFSET = 2,
97
    /// N/4 Router addresses
98
    DHCPOPT_ROUTERS = 3,
99
    /// N/4 Timeserver addresses
100
    DHCPOPT_TIME_SERVERS = 4,
101
    /// N/4 IEN-116 Server addresses
102
    DHCPOPT_NAME_SERVERS = 5,
103
    /// N/4 DNS Server addresses
104
    DHCPOPT_DOMAIN_NAME_SERVERS = 6,
105
    /// N/4 Logging Server addresses
106
    DHCPOPT_LOG_SERVERS = 7,
107
    /// N/4 Quotes Server addresses
108
    DHCPOPT_QUOTES_SERVERS = 8,
109
    /// N/4 Quotes Server addresses
110
    DHCPOPT_LPR_SERVERS = 9,
111
    /// N/4 Quotes Server addresses
112
    DHCPOPT_IMPRESS_SERVERS = 10,
113
    /// N/4 RLP Server addresses
114
    DHCPOPT_RESOURCE_LOCATION_SERVERS = 11,
115
    /// Hostname string
116
    DHCPOPT_HOST_NAME = 12,
117
    /// Size of boot file in 512 byte chunks
118
    DHCPOPT_BOOT_SIZE = 13,
119
    /// Client to dump and name the file to dump it to
120
    DHCPOPT_MERIT_DUMP = 14,
121
    /// The DNS domain name of the client
122
    DHCPOPT_DOMAIN_NAME = 15,
123
    /// Swap Server address
124
    DHCPOPT_SWAP_SERVER = 16,
125
    /// Path name for root disk
126
    DHCPOPT_ROOT_PATH = 17,
127
    /// Path name for more BOOTP info
128
    DHCPOPT_EXTENSIONS_PATH = 18,
129
    /// Enable/Disable IP Forwarding
130
    DHCPOPT_IP_FORWARDING = 19,
131
    /// Enable/Disable Source Routing
132
    DHCPOPT_NON_LOCAL_SOURCE_ROUTING = 20,
133
    /// Routing Policy Filters
134
    DHCPOPT_POLICY_FILTER = 21,
135
    /// Max Datagram Reassembly Size
136
    DHCPOPT_MAX_DGRAM_REASSEMBLY = 22,
137
    /// Default IP Time to Live
138
    DEFAULT_IP_TTL = 23,
139
    /// Path MTU Aging Timeout
140
    DHCPOPT_PATH_MTU_AGING_TIMEOUT = 24,
141
    /// Path MTU Plateau Table
142
    PATH_MTU_PLATEAU_TABLE = 25,
143
    /// Interface MTU Size
144
    DHCPOPT_INTERFACE_MTU = 26,
145
    /// All Subnets are Local
146
    DHCPOPT_ALL_SUBNETS_LOCAL = 27,
147
    /// Broadcast Address
148
    DHCPOPT_BROADCAST_ADDRESS = 28,
149
    /// Perform Mask Discovery
150
    DHCPOPT_PERFORM_MASK_DISCOVERY = 29,
151
    /// Provide Mask to Others
152
    DHCPOPT_MASK_SUPPLIER = 30,
153
    /// Perform Router Discovery
154
    DHCPOPT_ROUTER_DISCOVERY = 31,
155
    /// Router Solicitation Address
156
    DHCPOPT_ROUTER_SOLICITATION_ADDRESS = 32,
157
    /// Static Routing Table
158
    DHCPOPT_STATIC_ROUTES = 33,
159
    /// Trailer Encapsulation
160
    DHCPOPT_TRAILER_ENCAPSULATION = 34,
161
    /// ARP Cache Timeout
162
    DHCPOPT_ARP_CACHE_TIMEOUT = 35,
163
    /// IEEE802.3 Encapsulation
164
    DHCPOPT_IEEE802_3_ENCAPSULATION = 36,
165
    /// Default TCP Time to Live
166
    DHCPOPT_DEFAULT_TCP_TTL = 37,
167
    /// TCP Keepalive Interval
168
    DHCPOPT_TCP_KEEPALIVE_INTERVAL = 38,
169
    /// TCP Keepalive Garbage
170
    DHCPOPT_TCP_KEEPALIVE_GARBAGE = 39,
171
    /// NIS Domain Name
172
    DHCPOPT_NIS_DOMAIN = 40,
173
    /// NIS Server Addresses
174
    DHCPOPT_NIS_SERVERS = 41,
175
    /// NTP Server Addresses
176
    DHCPOPT_NTP_SERVERS = 42,
177
    /// Vendor Specific Information
178
    DHCPOPT_VENDOR_ENCAPSULATED_OPTIONS = 43,
179
    /// NETBIOS Name Servers
180
    DHCPOPT_NETBIOS_NAME_SERVERS = 44,
181
    /// NETBIOS Datagram Distribution
182
    DHCPOPT_NETBIOS_DD_SERVER = 45,
183
    /// NETBIOS Node Type
184
    DHCPOPT_NETBIOS_NODE_TYPE = 46,
185
    /// NETBIOS Scope
186
    DHCPOPT_NETBIOS_SCOPE = 47,
187
    /// X Window Font Server
188
    DHCPOPT_FONT_SERVERS = 48,
189
    /// X Window Display Manager
190
    DHCPOPT_X_DISPLAY_MANAGER = 49,
191
    /// Requested IP Address
192
    DHCPOPT_DHCP_REQUESTED_ADDRESS = 50,
193
    /// IP Address Lease Time
194
    DHCPOPT_DHCP_LEASE_TIME = 51,
195
    /// Overload "sname" or "file"
196
    DHCPOPT_DHCP_OPTION_OVERLOAD = 52,
197
    /// DHCP Message Type
198
    DHCPOPT_DHCP_MESSAGE_TYPE = 53,
199
    /// DHCP Server Identification
200
    DHCPOPT_DHCP_SERVER_IDENTIFIER = 54,
201
    /// Parameter Request List
202
    DHCPOPT_DHCP_PARAMETER_REQUEST_LIST = 55,
203
    /// DHCP Error Message
204
    DHCPOPT_DHCP_MESSAGE = 56,
205
    /// DHCP Maximum Message Size
206
    DHCPOPT_DHCP_MAX_MESSAGE_SIZE = 57,
207
    /// DHCP Renewal (T1) Time
208
    DHCPOPT_DHCP_RENEWAL_TIME = 58,
209
    /// DHCP Rebinding (T2) Time
210
    DHCPOPT_DHCP_REBINDING_TIME = 59,
211
    /// Class Identifier
212
    DHCPOPT_VENDOR_CLASS_IDENTIFIER = 60,
213
    /// Class Identifier
214
    DHCPOPT_DHCP_CLIENT_IDENTIFIER = 61,
215
    /// NetWare/IP Domain Name
216
    DHCPOPT_NWIP_DOMAIN_NAME = 62,
217
    /// NetWare/IP sub Options
218
    DHCPOPT_NWIP_SUBOPTIONS = 63,
219
    /// NIS+ v3 Client Domain Name
220
    DHCPOPT_NIS_DOMAIN_NAME = 64,
221
    /// NIS+ v3 Server Addresses
222
    DHCPOPT_NIS_SERVER_ADDRESS = 65,
223
    /// TFTP Server Name
224
    DHCPOPT_TFTP_SERVER_NAME = 66,
225
    /// Boot File Name
226
    DHCPOPT_BOOTFILE_NAME = 67,
227
    /// Home Agent Addresses
228
    DHCPOPT_HOME_AGENT_ADDRESS = 68,
229
    /// Simple Mail Server (SMTP) Addresses
230
    DHCPOPT_SMTP_SERVER = 69,
231
    /// Post Office (POP3) Server Addresses
232
    DHCPOPT_POP3_SERVER = 70,
233
    /// Network News (NNTP) Server Addresses
234
    DHCPOPT_NNTP_SERVER = 71,
235
    /// WWW Server Addresses
236
    DHCPOPT_WWW_SERVER = 72,
237
    /// Finger Server Addresses
238
    DHCPOPT_FINGER_SERVER = 73,
239
    /// Chat (IRC) Server Addresses
240
    DHCPOPT_IRC_SERVER = 74,
241
    /// StreetTalk Server Addresses
242
    DHCPOPT_STREETTALK_SERVER = 75,
243
    /// ST Directory Assist. Addresses
244
    DHCPOPT_STDA_SERVER = 76,
245
    /// User Class Information
246
    DHCPOPT_USER_CLASS = 77,
247
    /// Directory Agent Information
248
    DHCPOPT_DIRECTORY_AGENT = 78,
249
    /// Service Location Agent Scope
250
    DHCPOPT_SERVICE_SCOPE = 79,
251
    /// Rapid Commit
252
    DHCPOPT_RAPID_COMMIT = 80,
253
    /// Fully Qualified Domain Name
254
    DHCPOPT_FQDN = 81,
255
    /// Relay Agent Information
256
    DHCPOPT_DHCP_AGENT_OPTIONS = 82,
257
    /// Internet Storage Name Service
258
    DHCPOPT_ISNS = 83,
259
    /// Novell Directory Services
260
    DHCPOPT_NDS_SERVERS = 85,
261
    /// Novell Directory Services
262
    DHCPOPT_NDS_TREE_NAME = 86,
263
    /// Novell Directory Services
264
    DHCPOPT_NDS_CONTEXT = 87,
265
    /// BCMCS Controller Domain Name list
266
    DHCPOPT_BCMCS_CONTROLLER_DOMAIN_NAME_LIST = 88,
267
    /// BCMCS Controller IPv4 address option
268
    DHCPOPT_BCMCS_CONTROLLER_IPV4_ADDRESS = 89,
269
    /// Authentication
270
    DHCPOPT_AUTHENTICATION = 90,
271
    /// Client Last Transaction Time
272
    DHCPOPT_CLIENT_LAST_TXN_TIME = 91,
273
    /// Associated IP
274
    DHCPOPT_ASSOCIATED_IP = 92,
275
    /// Client System Architecture
276
    DHCPOPT_CLIENT_SYSTEM = 93,
277
    /// Client Network Device Interface
278
    DHCPOPT_CLIENT_NDI = 94,
279
    /// Lightweight Directory Access Protocol [
280
    DHCPOPT_LDAP = 95,
281
    /// UUID/GUID-based Client Identifier
282
    DHCPOPT_UUID_GUID = 97,
283
    /// Open Group's User Authentication
284
    DHCPOPT_USER_AUTH = 98,
285
    /// GEOCONF_CIVIC
286
    DHCPOPT_GEOCONF_CIVIC = 99,
287
    /// IEEE 1003.1 TZ String
288
    DHCPOPT_PCODE = 100,
289
    /// Reference to the TZ Database
290
    DHCPOPT_TCODE = 101,
291
    /// NetInfo Parent Server Address
292
    DHCPOPT_NETINFO_ADDRESS = 112,
293
    /// NetInfo Parent Server Tag
294
    DHCPOPT_NETINFO_TAG = 113,
295
    /// URL
296
    DHCPOPT_URL = 114,
297
    /// DHCP Auto-Configuration
298
    DHCPOPT_AUTO_CONFIG = 116,
299
    /// Name Service Search
300
    DHCPOPT_NAME_SERVICE_SEARCH = 117,
301
    /// Subnet Selection Option
302
    DHCPOPT_SUBNET_SELECTION = 118,
303
    /// DNS Domain Search List
304
    DHCPOPT_DOMAIN_SEARCH = 119,
305
    /// SIP Servers DHCP Option
306
    DHCPOPT_SIP_SERVERS = 120,
307
    /// Classless Static Route Option
308
    DHCPOPT_CLASSLESS_STATIC_ROUTE = 121,
309
    /// CableLabs Client Configuration
310
    DHCPOPT_CCC = 122,
311
    /// GeoConf Option
312
    DHCPOPT_GEOCONF = 123,
313
    /// Vendor-Identifying Vendor Class
314
    DHCPOPT_V_I_VENDOR_CLASS = 124,
315
    /// Vendor-Identifying Vendor-Specific Information
316
    DHCPOPT_V_I_VENDOR_OPTS = 125,
317
    /// OPTION_PANA_AGENT
318
    DHCPOPT_OPTION_PANA_AGENT = 136,
319
    /// OPTION_V4_LOST
320
    DHCPOPT_OPTION_V4_LOST = 137,
321
    /// CAPWAP Access Controller addresses
322
    DHCPOPT_OPTION_CAPWAP_AC_V4 = 138,
323
    /// A Series Of Suboptions
324
    DHCPOPT_OPTION_IPV4_ADDRESS_MOS = 139,
325
    /// A Series Of Suboptions
326
    DHCPOPT_OPTION_IPV4_FQDN_MOS = 140,
327
    /// List of domain names to search for SIP User Agent Configuration
328
    DHCPOPT_SIP_UA_CONFIG = 141,
329
    /// ANDSF IPv4 Address Option for DHCPv4
330
    DHCPOPT_OPTION_IPV4_ADDRESS_ANDSF = 142,
331
    /// Geospatial Location with Uncertainty [RF
332
    DHCPOPT_GEOLOC = 144,
333
    /// Forcerenew Nonce Capable
334
    DHCPOPT_FORCERENEW_NONCE_CAPABLE = 145,
335
    /// Information for selecting RDNSS
336
    DHCPOPT_RDNSS_SELECTION = 146,
337
    /// Status code and optional N byte text message describing status
338
    DHCPOPT_STATUS_CODE = 151,
339
    /// Absolute time (seconds since Jan 1, 1970) message was sent
340
    DHCPOPT_BASE_TIME = 152,
341
    /// Number of seconds in the past when client entered current state
342
    DHCPOPT_START_TIME_OF_STATE = 153,
343
    /// Absolute time (seconds since Jan 1, 1970) for beginning of query
344
    DHCPOPT_QUERY_START_TIME = 154,
345
    /// Absolute time (seconds since Jan 1, 1970) for end of query
346
    DHCPOPT_QUERY_END_TIME = 155,
347
    /// State of IP address
348
    DHCPOPT_DHCP_STATE = 156,
349
    /// Indicates information came from local or remote server
350
    DHCPOPT_DATA_SOURCE = 157,
351
    /// Includes one or multiple lists of PCP server IP addresses; each list is treated as a separate PCP server
352
    DHCPOPT_OPTION_V4_PCP_SERVER = 158,
353
    /// This option is used to configure a set of ports bound to a shared IPv4 address
354
    DHCPOPT_OPTION_V4_PORTPARAMS = 159,
355
    /// DHCP Captive-Portal
356
    DHCPOPT_CAPTIVE_PORTAL = 160,
357
    /// Manufacturer Usage Descriptions
358
    DHCPOPT_OPTION_MUD_URL_V4 = 161,
359
    /// Etherboot
360
    DHCPOPT_ETHERBOOT = 175,
361
    /// IP Telephone
362
    DHCPOPT_IP_TELEPHONE = 176,
363
    /// Magic string = F1:00:74:7E
364
    DHCPOPT_PXELINUX_MAGIC = 208,
365
    /// Configuration file
366
    DHCPOPT_CONFIGURATION_FILE = 209,
367
    /// Path Prefix Option
368
    DHCPOPT_PATH_PREFIX = 210,
369
    /// Reboot Time
370
    DHCPOPT_REBOOT_TIME = 211,
371
    /// OPTION_6RD with N/4 6rd BR addresses
372
    DHCPOPT_OPTION_6RD = 212,
373
    /// Access Network Domain Name
374
    DHCPOPT_OPTION_V4_ACCESS_DOMAIN = 213,
375
    /// Subnet Allocation Option
376
    DHCPOPT_SUBNET_ALLOCATION = 220,
377
    /// Virtual Subnet Selection (VSS) Option
378
    DHCPOPT_VIRTUAL_SUBNET_SELECTION = 221,
379
    /// End (last option)
380
    DHCPOPT_END = 255
381
  };
382
383
  /// @class DhcpOption
384
  /// A wrapper class for DHCP options. This class does not create or modify DHCP option records, but rather
385
  /// serves as a wrapper and provides useful methods for setting and retrieving data to/from them
386
  class DhcpOption : public TLVRecord<uint8_t, uint8_t>
387
  {
388
  public:
389
    /// A c'tor for this class that gets a pointer to the option raw data (byte array)
390
    /// @param[in] optionRawData A pointer to the option raw data
391
275k
    explicit DhcpOption(uint8_t* optionRawData) : TLVRecord(optionRawData)
392
275k
    {}
393
394
    /// A d'tor for this class, currently does nothing
395
    ~DhcpOption() override = default;
396
397
    /// Retrieve DHCP option data as IPv4 address. Relevant only if option value is indeed an IPv4 address
398
    /// @return DHCP option data as IPv4 address
399
    IPv4Address getValueAsIpAddr() const
400
2.95k
    {
401
2.95k
      return getValueAs<uint32_t>();
402
2.95k
    }
403
404
    /// Set DHCP option data as IPv4 address. This method copies the 4 bytes of the IP address to the option value
405
    /// @param[in] addr The IPv4 address to set
406
    /// @param[in] valueOffset An optional parameter that specifies where to start set the option data (default set
407
    /// to 0). For example: if option data is 20 bytes long and you want to set the IP address in the 4 last bytes
408
    /// then use this method like this: setValueIpAddr(your_addr, 16)
409
    void setValueIpAddr(const IPv4Address& addr, int valueOffset = 0)
410
0
    {
411
0
      setValue<uint32_t>(addr.toInt(), valueOffset);
412
0
    }
413
414
    /// Retrieve DHCP option data as string. Relevant only if option value is indeed a string
415
    /// @param[in] valueOffset An optional parameter that specifies where to start copy the DHCP option data. For
416
    /// example: when retrieving Client FQDN option, you may ignore the flags and RCODE fields using this method
417
    /// like this: getValueAsString(3). The default is 0 - start copying from the beginning of option data
418
    /// @return DHCP option data as string
419
    std::string getValueAsString(int valueOffset = 0) const
420
2.95k
    {
421
      // TODO: This will burn if valueOffset is negative.
422
      // Should negative offsets even be allowed? Potentially change it to size_t?
423
2.95k
      if (m_Data == nullptr || getDataSize() < static_cast<size_t>(valueOffset) + 1)
424
228
        return "";
425
426
2.72k
      return std::string(reinterpret_cast<const char*>(m_Data->recordValue) + valueOffset,
427
2.72k
                         static_cast<int>(m_Data->recordLen) - valueOffset);
428
2.95k
    }
429
430
    /// Set DHCP option data as string. This method copies the string to the option value. If the string is longer
431
    /// than option length the string is trimmed so it will fit the option length
432
    /// @param[in] stringValue The string to set
433
    /// @param[in] valueOffset An optional parameter that specifies where to start set the option data (default set
434
    /// to 0). For example: if option data is 20 bytes long and you want to set a 6 char-long string in the 6 last
435
    /// bytes then use this method like this: setValueString("string", 14)
436
    void setValueString(const std::string& stringValue, int valueOffset = 0)
437
0
    {
438
0
      // calculate the maximum length of the destination buffer
439
0
      size_t len = static_cast<size_t>(m_Data->recordLen) - static_cast<size_t>(valueOffset);
440
0
441
0
      // use the length of input string if a buffer is large enough for whole string
442
0
      if (stringValue.length() < len)
443
0
        len = stringValue.length();
444
0
445
0
      memcpy(m_Data->recordValue + valueOffset, stringValue.data(), len);
446
0
    }
447
448
    /// Check if a pointer can be assigned to the TLV record data
449
    /// @param[in] recordRawData A pointer to the TLV record raw data
450
    /// @param[in] tlvDataLen The size of the TLV record raw data
451
    /// @return True if data is valid and can be assigned
452
    static bool canAssign(const uint8_t* recordRawData, size_t tlvDataLen)
453
275k
    {
454
275k
      auto data = reinterpret_cast<TLVRawData const*>(recordRawData);
455
275k
      if (data == nullptr)
456
0
        return false;
457
458
275k
      if (tlvDataLen < sizeof(TLVRawData::recordType))
459
9.81k
        return false;
460
461
265k
      if (data->recordType == static_cast<uint8_t>(DHCPOPT_END) ||
462
263k
          data->recordType == static_cast<uint8_t>(DHCPOPT_PAD))
463
199k
        return true;
464
465
66.3k
      return TLVRecord<uint8_t, uint8_t>::canAssign(recordRawData, tlvDataLen);
466
265k
    }
467
468
    // implement abstract methods
469
470
    size_t getTotalSize() const override
471
0
    {
472
0
      if (m_Data == nullptr)
473
0
        return 0;
474
0
475
0
      if (m_Data->recordType == static_cast<uint8_t>(DHCPOPT_END) ||
476
0
          m_Data->recordType == static_cast<uint8_t>(DHCPOPT_PAD))
477
0
        return sizeof(uint8_t);
478
0
479
0
      return sizeof(uint8_t) * 2 + static_cast<size_t>(m_Data->recordLen);
480
0
    }
481
482
    size_t getDataSize() const override
483
0
    {
484
0
      if (m_Data == nullptr)
485
0
        return 0;
486
0
487
0
      if (m_Data->recordType == static_cast<uint8_t>(DHCPOPT_END) ||
488
0
          m_Data->recordType == static_cast<uint8_t>(DHCPOPT_PAD))
489
0
        return 0;
490
0
491
0
      return m_Data->recordLen;
492
0
    }
493
  };
494
495
  /// @class DhcpOptionBuilder
496
  /// A class for building DHCP options. This builder receives the option parameters in its c'tor,
497
  /// builds the DHCP option raw buffer and provides a build() method to get a DhcpOption object out of it
498
  class DhcpOptionBuilder : public TLVRecordBuilder
499
  {
500
  public:
501
    /// A c'tor for building DHCP options which their value is a byte array. The DhcpOption object can later
502
    /// be retrieved by calling build()
503
    /// @param[in] optionType DHCP option type
504
    /// @param[in] optionValue A buffer containing the option value. This buffer is read-only and isn't modified in
505
    /// any way
506
    /// @param[in] optionValueLen DHCP option value length in bytes
507
    DhcpOptionBuilder(DhcpOptionTypes optionType, const uint8_t* optionValue, uint8_t optionValueLen)
508
        : TLVRecordBuilder(static_cast<uint8_t>(optionType), optionValue, optionValueLen)
509
0
    {}
510
511
    /// A c'tor for building DHCP options which have a 1-byte value. The DhcpOption object can later be retrieved
512
    /// by calling build()
513
    /// @param[in] optionType DHCP option type
514
    /// @param[in] optionValue A 1-byte option value
515
    DhcpOptionBuilder(DhcpOptionTypes optionType, uint8_t optionValue)
516
        : TLVRecordBuilder(static_cast<uint8_t>(optionType), optionValue)
517
0
    {}
518
519
    /// A c'tor for building DHCP options which have a 2-byte value. The DhcpOption object can later be retrieved
520
    /// by calling build()
521
    /// @param[in] optionType DHCP option type
522
    /// @param[in] optionValue A 2-byte option value
523
    DhcpOptionBuilder(DhcpOptionTypes optionType, uint16_t optionValue)
524
        : TLVRecordBuilder(static_cast<uint8_t>(optionType), optionValue)
525
0
    {}
526
527
    /// A c'tor for building DHCP options which have a 4-byte value. The DhcpOption object can later be retrieved
528
    /// by calling build()
529
    /// @param[in] optionType DHCP option type
530
    /// @param[in] optionValue A 4-byte option value
531
    DhcpOptionBuilder(DhcpOptionTypes optionType, uint32_t optionValue)
532
        : TLVRecordBuilder(static_cast<uint8_t>(optionType), optionValue)
533
0
    {}
534
535
    /// A c'tor for building DHCP options which have an IPv4Address value. The DhcpOption object can later be
536
    /// retrieved by calling build()
537
    /// @param[in] optionType DHCP option type
538
    /// @param[in] optionValue The IPv4 address option value
539
    DhcpOptionBuilder(DhcpOptionTypes optionType, const IPv4Address& optionValue)
540
        : TLVRecordBuilder(static_cast<uint8_t>(optionType), optionValue)
541
0
    {}
542
543
    /// A c'tor for building DHCP options which have a string value. The DhcpOption object can later be retrieved
544
    /// by calling build()
545
    /// @param[in] optionType DHCP option type
546
    /// @param[in] optionValue The string option value
547
    DhcpOptionBuilder(DhcpOptionTypes optionType, const std::string& optionValue)
548
        : TLVRecordBuilder(static_cast<uint8_t>(optionType), optionValue)
549
0
    {}
550
551
    /// A copy c'tor which copies all the data from another instance of DhcpOptionBuilder
552
    /// @param[in] other The instance to copy from
553
    DhcpOptionBuilder(const DhcpOptionBuilder& other) : TLVRecordBuilder(other)
554
0
    {}
555
556
    /// Assignment operator that copies all data from another instance of DhcpOptionBuilder
557
    /// @param[in] other The instance to assign from
558
    /// @return A reference to the assignee
559
    DhcpOptionBuilder& operator=(const DhcpOptionBuilder& other)
560
0
    {
561
0
      TLVRecordBuilder::operator=(other);
562
0
      return *this;
563
0
    }
564
565
    /// Build the DhcpOption object out of the parameters defined in the c'tor
566
    /// @return The DhcpOption object
567
    DhcpOption build() const;
568
  };
569
570
  /// @class DhcpLayer
571
  /// Represents a DHCP (Dynamic Host Configuration Protocol) protocol layer
572
  class DhcpLayer : public Layer
573
  {
574
  public:
575
    /// A constructor that creates the layer from an existing packet raw data
576
    /// @param[in] data A pointer to the raw data
577
    /// @param[in] dataLen Size of the data in bytes
578
    /// @param[in] prevLayer A pointer to the previous layer
579
    /// @param[in] packet A pointer to the Packet instance where layer will be stored in
580
    DhcpLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet);
581
582
    /// A constructor that creates the layer from scratch. Adds a ::DHCPOPT_DHCP_MESSAGE_TYPE and a ::DHCPOPT_END
583
    /// options
584
    /// @param[in] msgType A DHCP message type to be set
585
    /// @param[in] clientMacAddr A client MAC address to set in dhcp_header#clientHardwareAddress field
586
    DhcpLayer(DhcpMessageType msgType, const MacAddress& clientMacAddr);
587
588
    /// A constructor that creates the layer from scratch with clean data
589
    DhcpLayer();
590
591
    /// A destructor for this layer
592
15.1k
    ~DhcpLayer() override = default;
593
594
    static bool isDataValid(uint8_t const* data, size_t dataLen)
595
15.1k
    {
596
15.1k
      return canReinterpretAs<dhcp_header>(data, dataLen);
597
15.1k
    }
598
599
    /// Get a pointer to the DHCP header. Notice this points directly to the data, so every change will change the
600
    /// actual packet data
601
    /// @return A pointer to the @ref dhcp_header
602
    dhcp_header* getDhcpHeader() const
603
24.0k
    {
604
24.0k
      return reinterpret_cast<dhcp_header*>(m_Data);
605
24.0k
    }
606
607
    /// @return The BootP opcode of this message
608
    BootpOpCodes getOpCode() const
609
3.00k
    {
610
3.00k
      return static_cast<BootpOpCodes>(getDhcpHeader()->opCode);
611
3.00k
    }
612
613
    /// @return The client IPv4 address (as extracted from dhcp_header#clientIpAddress converted to IPv4Address
614
    /// object)
615
    IPv4Address getClientIpAddress() const
616
3.00k
    {
617
3.00k
      return getDhcpHeader()->clientIpAddress;
618
3.00k
    }
619
620
    /// Set the client IPv4 address in dhcp_header#clientIpAddress
621
    /// @param[in] addr The IPv4 address to set
622
    void setClientIpAddress(const IPv4Address& addr)
623
0
    {
624
0
      getDhcpHeader()->clientIpAddress = addr.toInt();
625
0
    }
626
627
    /// @return The server IPv4 address (as extracted from dhcp_header#serverIpAddress converted to IPv4Address
628
    /// object)
629
    IPv4Address getServerIpAddress() const
630
3.00k
    {
631
3.00k
      return getDhcpHeader()->serverIpAddress;
632
3.00k
    }
633
634
    /// Set the server IPv4 address in dhcp_header#serverIpAddress
635
    /// @param[in] addr The IPv4 address to set
636
    void setServerIpAddress(const IPv4Address& addr)
637
0
    {
638
0
      getDhcpHeader()->serverIpAddress = addr.toInt();
639
0
    }
640
641
    /// @return Your IPv4 address (as extracted from dhcp_header#yourIpAddress converted to IPv4Address object)
642
    IPv4Address getYourIpAddress() const
643
3.00k
    {
644
3.00k
      return getDhcpHeader()->yourIpAddress;
645
3.00k
    }
646
647
    /// Set your IPv4 address in dhcp_header#yourIpAddress
648
    /// @param[in] addr The IPv4 address to set
649
    void setYourIpAddress(const IPv4Address& addr)
650
0
    {
651
0
      getDhcpHeader()->yourIpAddress = addr.toInt();
652
0
    }
653
654
    /// @return Gateway IPv4 address (as extracted from dhcp_header#gatewayIpAddress converted to IPv4Address
655
    /// object)
656
    IPv4Address getGatewayIpAddress() const
657
3.00k
    {
658
3.00k
      return getDhcpHeader()->gatewayIpAddress;
659
3.00k
    }
660
661
    /// Set the gateway IPv4 address in dhcp_header#gatewayIpAddress
662
    /// @param[in] addr The IPv4 address to set
663
    void setGatewayIpAddress(const IPv4Address& addr)
664
0
    {
665
0
      getDhcpHeader()->gatewayIpAddress = addr.toInt();
666
0
    }
667
668
    /// @return The client MAC address as extracted from dhcp_header#clientHardwareAddress, assuming
669
    /// dhcp_header#hardwareType is 1 (Ethernet) and dhcp_header#hardwareAddressLength is 6 (MAC address length).
670
    /// Otherwise returns MacAddress#Zero
671
    MacAddress getClientHardwareAddress() const;
672
673
    /// Set a MAC address into the first 6 bytes of dhcp_header#clientHardwareAddress. This method also sets
674
    /// dhcp_header#hardwareType to 1 (Ethernet) and dhcp_header#hardwareAddressLength to 6 (MAC address length)
675
    /// @param[in] addr The MAC address to set
676
    void setClientHardwareAddress(const MacAddress& addr);
677
678
    /// @return DHCP message type as extracted from ::DHCPOPT_DHCP_MESSAGE_TYPE option. If this option doesn't exist
679
    /// the value of
680
    /// ::DHCP_UNKNOWN_MSG_TYPE is returned
681
    DhcpMessageType getMessageType() const;
682
683
    /// Set DHCP message type. This method searches for existing ::DHCPOPT_DHCP_MESSAGE_TYPE option. If found, it
684
    /// sets the requested message type as its value. If not, it creates a ::DHCPOPT_DHCP_MESSAGE_TYPE option and
685
    /// sets the requested message type as its value
686
    /// @param[in] msgType Message type to set
687
    /// @return True if message type was set successfully or false if msgType is ::DHCP_UNKNOWN_MSG_TYPE or if
688
    /// failed to add
689
    /// ::DHCPOPT_DHCP_MESSAGE_TYPE option
690
    bool setMessageType(DhcpMessageType msgType);
691
692
    /// @return The first DHCP option in the packet. If there are no DHCP options the returned value will contain
693
    /// a logical null (DhcpOption#isNull() == true)
694
    DhcpOption getFirstOptionData() const;
695
696
    /// Get the DHCP option that comes after a given option. If the given option was the last one, the
697
    /// returned value will contain a logical null (DhcpOption#isNull() == true)
698
    /// @param[in] dhcpOption A given DHCP option
699
    /// @return A DhcpOption object containing the option data that comes next, or logical null if the given DHCP
700
    /// option: (1) was the last one; (2) contains a logical null or (3) doesn't belong to this packet
701
    DhcpOption getNextOptionData(DhcpOption dhcpOption) const;
702
703
    /// Get a DHCP option by type
704
    /// @param[in] option DHCP option type
705
    /// @return A DhcpOption object containing the first DHCP option data that matches this type, or logical null
706
    /// (DhcpOption#isNull() == true) if no such option found
707
    DhcpOption getOptionData(DhcpOptionTypes option) const;
708
709
    /// @return The number of DHCP options in this layer
710
    size_t getOptionsCount() const;
711
712
    /// Add a new DHCP option at the end of the layer
713
    /// @param[in] optionBuilder A DhcpOptionBuilder object that contains the requested DHCP option data to add
714
    /// @return A DhcpOption object containing the newly added DHCP option data or logical null
715
    /// (DhcpOption#isNull() == true) if addition failed
716
    DhcpOption addOption(const DhcpOptionBuilder& optionBuilder);
717
718
    /// Add a new DHCP option after an existing one
719
    /// @param[in] optionBuilder A DhcpOptionBuilder object that contains the requested DHCP option data to add
720
    /// @param[in] prevOption The DHCP option type which the newly added option will come after
721
    /// @return A DhcpOption object containing the newly added DHCP option data or logical null
722
    /// (DhcpOption#isNull() == true) if addition failed
723
    DhcpOption addOptionAfter(const DhcpOptionBuilder& optionBuilder, DhcpOptionTypes prevOption);
724
725
    /// Remove an existing DHCP option from the layer
726
    /// @param[in] optionType The DHCP option type to remove
727
    /// @return True if DHCP option was successfully removed or false if type wasn't found or if removal failed
728
    bool removeOption(DhcpOptionTypes optionType);
729
730
    /// Remove all DHCP options in this layer
731
    /// @return True if all DHCP options were successfully removed or false if removal failed for some reason
732
    bool removeAllOptions();
733
734
    /// A static method that checks whether a pair of ports are considered DHCP ports
735
    /// @param[in] portSrc The source port number to check
736
    /// @param[in] portDst The destination port number to check
737
    /// @return True if these are DHCP port numbers, false otherwise
738
    static inline bool isDhcpPorts(uint16_t portSrc, uint16_t portDst);
739
740
    // implement abstract methods
741
742
    /// Does nothing for this layer (DhcpLayer is always last)
743
    void parseNextLayer() override
744
15.1k
    {}
745
746
    /// @return The size of @ref dhcp_header + size of options
747
    size_t getHeaderLen() const override
748
137k
    {
749
137k
      return m_DataLen;
750
137k
    }
751
752
    /// Calculate the following fields:
753
    /// - @ref dhcp_header#magicNumber = DHCP magic number (0x63538263)
754
    /// - @ref dhcp_header#opCode = ::DHCP_BOOTREQUEST for message types: ::DHCP_DISCOVER, ::DHCP_REQUEST,
755
    /// ::DHCP_DECLINE, ::DHCP_RELEASE,
756
    ///                            ::DHCP_INFORM, ::DHCP_UNKNOWN_MSG_TYPE
757
    ///                            ::DHCP_BOOTREPLY for message types: ::DHCP_OFFER, ::DHCP_ACK, ::DHCP_NAK
758
    /// - @ref dhcp_header#hardwareType = 1 (Ethernet)
759
    /// - @ref dhcp_header#hardwareAddressLength = 6 (MAC address length)
760
    void computeCalculateFields() override;
761
762
    std::string toString() const override;
763
764
    OsiModelLayer getOsiModelLayer() const override
765
3.00k
    {
766
3.00k
      return OsiModelApplicationLayer;
767
3.00k
    }
768
769
  private:
770
    uint8_t* getOptionsBasePtr() const
771
134k
    {
772
134k
      return m_Data + sizeof(dhcp_header);
773
134k
    }
774
775
    TLVRecordReader<DhcpOption> m_OptionReader;
776
777
    void initDhcpLayer(size_t numOfBytesToAllocate);
778
779
    DhcpOption addOptionAt(const DhcpOptionBuilder& optionBuilder, int offset);
780
  };
781
782
  // implementation of inline methods
783
784
  bool DhcpLayer::isDhcpPorts(uint16_t portSrc, uint16_t portDst)
785
0
  {
786
0
    return ((portSrc == 68 && portDst == 67) || (portSrc == 67 && portDst == 68) ||
787
0
            (portSrc == 67 && portDst == 67));
788
0
  }
789
790
}  // namespace pcpp