Coverage Report

Created: 2023-01-17 06:15

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