Coverage Report

Created: 2025-07-11 07:47

/src/PcapPlusPlus/Packet++/header/DnsLayer.h
Line
Count
Source (jump to first uncovered line)
1
#pragma once
2
3
#include "DnsLayerEnums.h"
4
#include "DnsResource.h"
5
#include "DnsResourceData.h"
6
#include "Layer.h"
7
8
/// @file
9
10
/// @namespace pcpp
11
/// @brief The main namespace for the PcapPlusPlus lib
12
namespace pcpp
13
{
14
  /// @struct dnshdr
15
  /// Represents the fixed part of the DNS header, meaning the part that doesn't include the DNS data (queries,
16
  /// answers, authorities and additional records)
17
#pragma pack(push, 1)
18
  struct dnshdr
19
  {
20
    /// DNS query identification
21
    uint16_t transactionID;
22
#if (BYTE_ORDER == LITTLE_ENDIAN)
23
    uint16_t
24
        /// Recursion desired flag
25
        recursionDesired : 1,
26
        /// Truncated flag
27
        truncation : 1,
28
        /// Authoritative answer flag
29
        authoritativeAnswer : 1,
30
        /// Operation Code
31
        opcode : 4,
32
        /// Query/Response flag
33
        queryOrResponse : 1,
34
        /// Return Code
35
        responseCode : 4,
36
        /// Checking disabled flag
37
        checkingDisabled : 1,
38
        /// Authenticated data flag
39
        authenticData : 1,
40
        /// Zero flag (Reserved)
41
        zero : 1,
42
        /// Recursion available flag
43
        recursionAvailable : 1;
44
#elif (BYTE_ORDER == BIG_ENDIAN)
45
    uint16_t
46
        /// Query/Response flag
47
        queryOrResponse : 1,
48
        /// Operation Code
49
        opcode : 4,
50
        /// Authoritative answer flag
51
        authoritativeAnswer : 1,
52
        /// Truncated flag
53
        truncation : 1,
54
        /// Recursion desired flag
55
        recursionDesired : 1,
56
        /// Recursion available flag
57
        recursionAvailable : 1,
58
        /// Zero flag (Reserved)
59
        zero : 1,
60
        /// Authenticated data flag
61
        authenticData : 1,
62
        /// Checking disabled flag
63
        checkingDisabled : 1,
64
        /// Return Code
65
        responseCode : 4;
66
#endif
67
    /// Number of DNS query records in packet
68
    uint16_t numberOfQuestions;
69
    /// Number of DNS answer records in packet
70
    uint16_t numberOfAnswers;
71
    /// Number of authority records in packet
72
    uint16_t numberOfAuthority;
73
    /// Number of additional records in packet
74
    uint16_t numberOfAdditional;
75
  };
76
#pragma pack(pop)
77
  static_assert(sizeof(dnshdr) == 12, "dnshdr size is not 12 bytes");
78
79
  // forward declarations
80
  class DnsQuery;
81
  class IDnsResource;
82
  class DnsResource;
83
  class IDnsResourceData;
84
85
  /// @class DnsLayer
86
  /// Represents the DNS protocol layer
87
  class DnsLayer : public Layer
88
  {
89
    friend class IDnsResource;
90
    friend class DnsQuery;
91
    friend class DnsResource;
92
93
  public:
94
    /// A constructor that creates the layer from an existing packet raw data
95
    /// @param[in] data A pointer to the raw data
96
    /// @param[in] dataLen Size of the data in bytes
97
    /// @param[in] prevLayer A pointer to the previous layer
98
    /// @param[in] packet A pointer to the Packet instance where layer will be stored in
99
    DnsLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet);
100
101
    /// A constructor that creates an empty DNS layer: all members of dnshdr are set to 0 and layer will contain no
102
    /// records
103
    DnsLayer();
104
105
    /// A copy constructor for this layer
106
    /// @param[in] other The DNS layer to copy from
107
    DnsLayer(const DnsLayer& other);
108
109
    /// An assignment operator for this layer
110
    /// @param[in] other The DNS layer to assign
111
    /// @return A reference to the assignee
112
    DnsLayer& operator=(const DnsLayer& other);
113
114
    ~DnsLayer() override;
115
116
    /// Get a pointer to the DNS header (as opposed to the DNS data which is the queries, answers, etc. Data can be
117
    /// retrieved through the other methods of this layer. Notice the return value points directly to the data, so
118
    /// every change will change the actual packet data
119
    /// @return A pointer to the @ref dnshdr
120
    dnshdr* getDnsHeader() const;
121
122
    /// Searches for a DNS query by its name field. Notice this method returns only a query which its name equals to
123
    /// the requested name. If several queries match the requested name, the first one will be returned. If no
124
    /// queries match the requested name, nullptr will be returned
125
    /// @param[in] name The name of the query to search
126
    /// @param[in] exactMatch Indicate whether to match the whole name or just a part of it
127
    /// @return The first matching DNS query or nullptr if no queries were found
128
    DnsQuery* getQuery(const std::string& name, bool exactMatch) const;
129
130
    /// @return The first DNS query in the packet or nullptr if packet doesn't contain any queries
131
    DnsQuery* getFirstQuery() const;
132
133
    /// Get the DNS query following a certain query
134
    /// @param[in] query A pointer to a DNS query that exist in the packet
135
    /// @return The DNS query following 'query'. If 'query' is nullptr or 'query' is the last query in the packet
136
    /// nullptr will be returned
137
    DnsQuery* getNextQuery(DnsQuery* query) const;
138
139
    /// @return The number of DNS queries in the packet
140
    size_t getQueryCount() const;
141
142
    /// Add a new DNS query to the layer
143
    /// @param[in] name The value that shall be set in the name field of the query
144
    /// @param[in] dnsType The value that shall be set in the DNS type field of the query
145
    /// @param[in] dnsClass The value that shall be set in the DNS class field of the query
146
    /// @return A pointer to the newly created DNS query or nullptr if query could not be created (an appropriate
147
    /// error log message will be printed in this case)
148
    DnsQuery* addQuery(const std::string& name, DnsType dnsType, DnsClass dnsClass);
149
150
    /// Add a new DNS query similar to an already existing DNS query. All query fields will be copied from the
151
    /// existing query
152
    /// @param[in] copyQuery The record to create the new record from. copyQuery won't be changed in any way
153
    /// @return A pointer to the newly created DNS query or nullptr if query could not be created (an appropriate
154
    /// error log message will be printed in this case)
155
    DnsQuery* addQuery(DnsQuery* const copyQuery);
156
157
    /// Remove an existing query by name. If several queries matches the name, the first match will be removed
158
    /// @param[in] queryNameToRemove The name of the query to remove
159
    /// @param[in] exactMatch Indicate whether to match the whole name or just a part of it
160
    /// @return True if query was found and successfully removed or false if query was not found or couldn't be
161
    /// removed
162
    bool removeQuery(const std::string& queryNameToRemove, bool exactMatch);
163
164
    /// Remove an existing query
165
    /// @param[in] queryToRemove A pointer to the query to remove
166
    /// @return True if query was found and successfully removed or false if query was not found or couldn't be
167
    /// removed
168
    bool removeQuery(DnsQuery* queryToRemove);
169
170
    /// Searches for a DNS answer by its name field. Notice this method returns only an answer which its name equals
171
    /// to the requested name. If several answers match the requested name, the first one will be returned. If no
172
    /// answers match the requested name, nullptr will be returned
173
    /// @param[in] name The name of the answer to search
174
    /// @param[in] exactMatch Indicate whether to match the whole name or just a part of it
175
    /// @return The first matching DNS answer or nullptr if no answers were found
176
    DnsResource* getAnswer(const std::string& name, bool exactMatch) const;
177
178
    /// @return The first DNS answer in the packet or nullptr if packet doesn't contain any answers
179
    DnsResource* getFirstAnswer() const;
180
181
    /// Get the DNS answer following a certain answer
182
    /// @param[in] answer A pointer to a DNS answer that exist in the packet
183
    /// @return The DNS answer following 'answer'. If 'answer' is nullptr or 'answer' is the last answer in the
184
    /// packet nullptr will be returned
185
    DnsResource* getNextAnswer(DnsResource* answer) const;
186
187
    /// @return The number of DNS answers in the packet
188
    size_t getAnswerCount() const;
189
190
    /// Add a new DNS answer to the layer
191
    /// @param[in] name The value that shall be set in the name field of the answer
192
    /// @param[in] dnsType The value that shall be set in the DNS type field of the answer
193
    /// @param[in] dnsClass The value that shall be set in the DNS class field of the answer
194
    /// @param[in] ttl The value that shall be set in the 'time-to-leave' field of the answer
195
    /// @param[in] data The answer data to be set. The type of the data should match the type of the DNS record
196
    /// (for example: DNS record of type A should have data of type IPv4DnsResourceData. Please see
197
    /// DnsResource#setData() for more info on this
198
    /// @return A pointer to the newly created DNS answer or nullptr if answer could not be created (an appropriate
199
    /// error log message will be printed in this case)
200
    DnsResource* addAnswer(const std::string& name, DnsType dnsType, DnsClass dnsClass, uint32_t ttl,
201
                           IDnsResourceData* data);
202
203
    /// Add a new DNS answer similar to an already existing DNS answer. All answer fields will be copied from the
204
    /// existing answer
205
    /// @param[in] copyAnswer The record to create the new record from. copyAnswer won't be changed in any way
206
    /// @return A pointer to the newly created DNS answer or nullptr if query could not be created (an appropriate
207
    /// error log message will be printed in this case)
208
    DnsResource* addAnswer(DnsResource* const copyAnswer);
209
210
    /// Remove an existing answer by name. If several answers matches the name, the first match will be removed
211
    /// @param[in] answerNameToRemove The name of the answer to remove
212
    /// @param[in] exactMatch Indicate whether to match the whole name or just a part of it
213
    /// @return True if answer was found and successfully removed or false if answer was not found or couldn't be
214
    /// removed
215
    bool removeAnswer(const std::string& answerNameToRemove, bool exactMatch);
216
217
    /// Remove an existing answer
218
    /// @param[in] answerToRemove A pointer to the answer to remove
219
    /// @return True if answer was found and successfully removed or false if answer was not found or couldn't be
220
    /// removed
221
    bool removeAnswer(DnsResource* answerToRemove);
222
223
    /// Searches for a DNS authority by its name field. Notice this method returns only an authority which its name
224
    /// equals to the requested name. If several authorities match the requested name, the first one will be
225
    /// returned. If no authorities match the requested name, nullptr will be returned
226
    /// @param[in] name The name of the authority to search
227
    /// @param[in] exactMatch Indicate whether to match the whole name or just a part of it
228
    /// @return The first matching DNS authority or nullptr if no authorities were found
229
    DnsResource* getAuthority(const std::string& name, bool exactMatch) const;
230
231
    /// @return The first DNS authority in the packet or nullptr if packet doesn't contain any authorities
232
    DnsResource* getFirstAuthority() const;
233
234
    /// Get the DNS authority following a certain authority
235
    /// @param[in] authority A pointer to a DNS authority that exist in the packet
236
    /// @return The DNS authority following 'authority'. If 'authority' is nullptr or 'authority' is the last
237
    /// authority in the packet nullptr will be returned
238
    DnsResource* getNextAuthority(DnsResource* authority) const;
239
240
    /// @return The number of DNS authorities in the packet
241
    size_t getAuthorityCount() const;
242
243
    /// Add a new DNS authority to the layer
244
    /// @param[in] name The value that shall be set in the name field of the authority
245
    /// @param[in] dnsType The value that shall be set in the DNS type field of the authority
246
    /// @param[in] dnsClass The value that shall be set in the DNS class field of the authority
247
    /// @param[in] ttl The value that shall be set in the 'time-to-leave' field of the authority
248
    /// @param[in] data The authority data to be set. The type of the data should match the type of the DNS record
249
    /// (for example: DNS record of type A should have data of type IPv4DnsResourceData. Please see
250
    /// DnsResource#setData() for more info on this
251
    /// @return A pointer to the newly created DNS authority or nullptr if authority could not be created (an
252
    /// appropriate error log message will be printed in this case)
253
    DnsResource* addAuthority(const std::string& name, DnsType dnsType, DnsClass dnsClass, uint32_t ttl,
254
                              IDnsResourceData* data);
255
256
    /// Add a new DNS authority similar to an already existing DNS authority. All authority fields will be copied
257
    /// from the existing authority
258
    /// @param[in] copyAuthority The record to create the new record from. copyAuthority won't be changed in any way
259
    /// @return A pointer to the newly created DNS authority or nullptr if query could not be created (an
260
    /// appropriate error log message will be printed in this case)
261
    DnsResource* addAuthority(DnsResource* const copyAuthority);
262
263
    /// Remove an existing authority by name. If several authorities matches the name, the first match will be
264
    /// removed
265
    /// @param[in] authorityNameToRemove The name of the authority to remove
266
    /// @param[in] exactMatch Indicate whether to match the whole name or just a part of it
267
    /// @return True if authority was found and successfully removed or false if authority was not found or couldn't
268
    /// be removed
269
    bool removeAuthority(const std::string& authorityNameToRemove, bool exactMatch);
270
271
    /// Remove an existing authority
272
    /// @param[in] authorityToRemove A pointer to the authority to remove
273
    /// @return True if authority was found and successfully removed or false if authority was not found or couldn't
274
    /// be removed
275
    bool removeAuthority(DnsResource* authorityToRemove);
276
277
    /// Searches for a DNS additional record by its name field. Notice this method returns only an additional record
278
    /// which its name equals to the requested name. If several additional records match the requested name, the
279
    /// first one will be returned. If no additional records match the requested name, nullptr will be returned
280
    /// @param[in] name The name of the additional record to search
281
    /// @param[in] exactMatch Indicate whether to match the whole name or just a part of it
282
    /// @return The first matching DNS additional record or nullptr if no additional records were found
283
    DnsResource* getAdditionalRecord(const std::string& name, bool exactMatch) const;
284
285
    /// @return The first DNS additional record in the packet or nullptr if packet doesn't contain any additional
286
    /// records
287
    DnsResource* getFirstAdditionalRecord() const;
288
289
    /// Get the DNS additional record following a certain additional record
290
    /// @param[in] additionalRecord A pointer to a DNS additional record that exist in the packet
291
    /// @return The DNS additional record following 'additionalRecord'. If 'additionalRecord' is nullptr or
292
    /// 'additionalRecord' is the last additional record in the packet nullptr will be returned
293
    DnsResource* getNextAdditionalRecord(DnsResource* additionalRecord) const;
294
295
    /// @return The number of DNS additional records in the packet
296
    size_t getAdditionalRecordCount() const;
297
298
    /// Add a new DNS additional record to the layer
299
    /// @param[in] name The value that shall be set in the name field of the additional record
300
    /// @param[in] dnsType The value that shall be set in the DNS type field of the additional record
301
    /// @param[in] dnsClass The value that shall be set in the DNS class field of the additional record
302
    /// @param[in] ttl The value that shall be set in the 'time-to-leave' field of the additional record
303
    /// @param[in] data The additional record data to be set. The type of the data should match the type of the DNS
304
    /// record (for example: DNS record of type A should have data of type IPv4DnsResourceData. Please see
305
    /// DnsResource#setData() for more info on this
306
    /// @return A pointer to the newly created DNS additional record or nullptr if additional record could not be
307
    /// created (an appropriate error log message will be printed in this case)
308
    DnsResource* addAdditionalRecord(const std::string& name, DnsType dnsType, DnsClass dnsClass, uint32_t ttl,
309
                                     IDnsResourceData* data);
310
311
    /// Add a new DNS additional record to the layer that doesn't have DNS class and TTL. Instead these bytes may
312
    /// contains some arbitrary data. In the future I may add support for these kinds of additional data records.
313
    /// For now, these bytes are set as raw
314
    /// @param[in] name The value that shall be set in the name field of the additional record
315
    /// @param[in] dnsType The value that shall be set in the DNS type field of the additional record
316
    /// @param[in] customData1 Two bytes of the arbitrary data that will be set in the offset usually used for the
317
    /// DNS class
318
    /// @param[in] customData2 Four bytes of the arbitrary data that will be set in the offset usually used for the
319
    /// TTL
320
    /// @param[in] data The additional record data to be set. The type of the data should match the type of the DNS
321
    /// record. (for example: DNS record of type A should have data of type IPv4DnsResourceData. Please see
322
    /// DnsResource#setData() for more info on this
323
    /// @return A pointer to the newly created DNS additional record or nullptr if additional record could not be
324
    /// created (an appropriate error log message will be printed in this case)
325
    DnsResource* addAdditionalRecord(const std::string& name, DnsType dnsType, uint16_t customData1,
326
                                     uint32_t customData2, IDnsResourceData* data);
327
328
    /// Add a new DNS additional record similar to an already existing DNS additional record. All additional record
329
    /// fields will be copied from the existing additional record
330
    /// @param[in] copyAdditionalRecord The record to create the new record from. copyAdditionalRecord won't be
331
    /// changed in any way
332
    /// @return A pointer to the newly created DNS additional record or nullptr if query could not be created (an
333
    /// appropriate error log message will be printed in this case)
334
    DnsResource* addAdditionalRecord(DnsResource* const copyAdditionalRecord);
335
336
    /// Remove an existing additional record by name. If several additional records matches the name, the first
337
    /// match will be removed
338
    /// @param[in] additionalRecordNameToRemove The name of the additional record to remove
339
    /// @param[in] exactMatch Indicate whether to match the whole name or just a part of it
340
    /// @return True if additional record was found and successfully removed or false if additional record was not
341
    /// found or couldn't be removed
342
    bool removeAdditionalRecord(const std::string& additionalRecordNameToRemove, bool exactMatch);
343
344
    /// Remove an existing additional record
345
    /// @param[in] additionalRecordToRemove A pointer to the additional record to remove
346
    /// @return True if additional record was found and successfully removed or false if additional record was not
347
    /// found or couldn't be removed
348
    bool removeAdditionalRecord(DnsResource* additionalRecordToRemove);
349
350
    // implement abstract methods
351
352
    /// Does nothing for this layer (DnsLayer is always last)
353
    void parseNextLayer() override
354
33.2k
    {}
355
356
    /// @return The size of the DNS data in the packet including he DNS header and size of all queries, answers,
357
    /// authorities and additional records
358
    size_t getHeaderLen() const override
359
77.9k
    {
360
77.9k
      return m_DataLen;
361
77.9k
    }  // No layer above DNS
362
363
    /// Does nothing for this layer
364
    void computeCalculateFields() override
365
4.86k
    {}
366
367
    std::string toString() const override;
368
369
    OsiModelLayer getOsiModelLayer() const override
370
6.52k
    {
371
6.52k
      return OsiModelApplicationLayer;
372
6.52k
    }
373
374
    /// A static method that checks whether the port is considered as DNS
375
    /// @param[in] port The port number to be checked
376
    /// @return True if the port is associated with the DNS protocol
377
    static inline bool isDnsPort(uint16_t port);
378
379
    /// A static method that validates the input data
380
    /// @param[in] data The pointer to the beginning of a byte stream of a DNS packet
381
    /// @param[in] dataLen The length of the byte stream
382
    /// @param[in] dnsOverTcp Should be set to "true" if this is DNS is over TCP, otherwise set to "false"
383
    /// (which is also the default value)
384
    /// @return True if the data is valid and can represent a DNS packet
385
    static inline bool isDataValid(const uint8_t* data, size_t dataLen, bool dnsOverTcp = false);
386
387
  protected:
388
    DnsLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet, size_t offsetAdjustment);
389
    explicit DnsLayer(size_t offsetAdjustment);
390
391
  private:
392
    IDnsResource* m_ResourceList;
393
    DnsQuery* m_FirstQuery;
394
    DnsResource* m_FirstAnswer;
395
    DnsResource* m_FirstAuthority;
396
    DnsResource* m_FirstAdditional;
397
    uint16_t m_OffsetAdjustment;
398
399
    size_t getBasicHeaderSize();
400
    void init(size_t offsetAdjustment, bool callParseResource);
401
    void initNewLayer(size_t offsetAdjustment);
402
403
    IDnsResource* getFirstResource(DnsResourceType resType) const;
404
    void setFirstResource(DnsResourceType resType, IDnsResource* resource);
405
406
    using Layer::extendLayer;
407
    bool extendLayer(int offsetInLayer, size_t numOfBytesToExtend, IDnsResource* resource);
408
409
    using Layer::shortenLayer;
410
    bool shortenLayer(int offsetInLayer, size_t numOfBytesToShorten, IDnsResource* resource);
411
412
    IDnsResource* getResourceByName(IDnsResource* startFrom, size_t resourceCount, const std::string& name,
413
                                    bool exactMatch) const;
414
415
    void parseResources();
416
417
    DnsResource* addResource(DnsResourceType resType, const std::string& name, DnsType dnsType, DnsClass dnsClass,
418
                             uint32_t ttl, IDnsResourceData* data);
419
420
    bool removeResource(IDnsResource* resourceToRemove);
421
  };
422
423
  /// @class DnsOverTcpLayer
424
  /// Represents the DNS over TCP layer.
425
  /// DNS over TCP is described here: https://tools.ietf.org/html/rfc7766 .
426
  /// It is very similar to DNS over UDP, except for one field: TCP message length which is added in the beginning of
427
  /// the message before the other DNS data properties. The rest of the data is similar.
428
429
  /// Note: DNS over TCP can spread over more than one packet, but this implementation doesn't support this use-case
430
  /// and assumes the whole message fits in a single packet.
431
  class DnsOverTcpLayer : public DnsLayer
432
  {
433
  public:
434
    /// A constructor that creates the layer from an existing packet raw data
435
    /// @param[in] data A pointer to the raw data
436
    /// @param[in] dataLen Size of the data in bytes
437
    /// @param[in] prevLayer A pointer to the previous layer
438
    /// @param[in] packet A pointer to the Packet instance where layer will be stored in
439
    DnsOverTcpLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet)
440
8.44k
        : DnsLayer(data, dataLen, prevLayer, packet, sizeof(uint16_t))
441
8.44k
    {}
442
443
    /// A constructor that creates an empty DNS layer: all members of dnshdr are set to 0 and layer will contain no
444
    /// records
445
    DnsOverTcpLayer() : DnsLayer(sizeof(uint16_t))
446
0
    {}
447
448
    /// A copy constructor for this layer
449
    /// @param[in] other The DNS over TCP layer to copy from
450
    DnsOverTcpLayer(const DnsOverTcpLayer& other) : DnsLayer(other)
451
0
    {}
452
453
    /// @return The value of the TCP message length as described in https://tools.ietf.org/html/rfc7766#section-8
454
    uint16_t getTcpMessageLength();
455
456
    /// Set the TCP message length value as described in https://tools.ietf.org/html/rfc7766#section-8
457
    /// @param[in] value The value to set
458
    void setTcpMessageLength(uint16_t value);
459
460
    // overridden methods
461
462
    /// Calculate the TCP message length field
463
    void computeCalculateFields() override;
464
  };
465
466
  // implementation of inline methods
467
468
  bool DnsLayer::isDnsPort(uint16_t port)
469
311k
  {
470
311k
    switch (port)
471
311k
    {
472
23.0k
    case 53:
473
23.5k
    case 5353:
474
33.2k
    case 5355:
475
33.2k
      return true;
476
278k
    default:
477
278k
      return false;
478
311k
    }
479
311k
  }
480
481
  bool DnsLayer::isDataValid(const uint8_t* data, size_t dataLen, bool dnsOverTcp)
482
171k
  {
483
171k
    size_t minSize = sizeof(dnshdr) + (dnsOverTcp ? sizeof(uint16_t) : 0);
484
171k
    return data && dataLen >= minSize;
485
171k
  }
486
487
}  // namespace pcpp