Coverage Report

Created: 2025-11-16 07:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/PcapPlusPlus/Packet++/src/DnsResource.cpp
Line
Count
Source
1
36.3k
#define LOG_MODULE PacketLogModuleDnsLayer
2
3
#include "DnsResource.h"
4
#include "Logger.h"
5
#include <sstream>
6
#include "EndianPortable.h"
7
8
namespace pcpp
9
{
10
11
  IDnsResource::IDnsResource(DnsLayer* dnsLayer, size_t offsetInLayer)
12
198k
      : m_DnsLayer(dnsLayer), m_OffsetInLayer(offsetInLayer), m_NextResource(nullptr), m_ExternalRawData(nullptr)
13
198k
  {
14
198k
    char decodedName[4096];
15
198k
    m_NameLength = decodeName((const char*)getRawData(), decodedName);
16
198k
    if (m_NameLength > 0)
17
172k
      m_DecodedName = decodedName;
18
198k
  }
19
20
  IDnsResource::IDnsResource(uint8_t* emptyRawData)
21
59.1k
      : m_DnsLayer(nullptr), m_OffsetInLayer(0), m_NextResource(nullptr), m_DecodedName(""), m_NameLength(0),
22
59.1k
        m_ExternalRawData(emptyRawData)
23
59.1k
  {}
24
25
  uint8_t* IDnsResource::getRawData() const
26
823k
  {
27
823k
    if (m_DnsLayer == nullptr)
28
481k
      return m_ExternalRawData;
29
30
341k
    return m_DnsLayer->m_Data + m_OffsetInLayer;
31
823k
  }
32
33
  static size_t cleanup(char* resultPtr, char* result, size_t encodedNameLength)
34
129k
  {
35
    // remove the last "."
36
129k
    if (resultPtr > result)
37
100k
    {
38
100k
      result[resultPtr - result - 1] = 0;
39
100k
    }
40
41
129k
    if (resultPtr - result < 256)
42
129k
    {
43
      // add the last '\0' to encodedNameLength
44
129k
      resultPtr[0] = 0;
45
129k
      encodedNameLength++;
46
129k
    }
47
48
129k
    return encodedNameLength;
49
129k
  }
50
51
  size_t IDnsResource::decodeName(const char* encodedName, char* result, int iteration)
52
400k
  {
53
400k
    size_t encodedNameLength = 0;
54
400k
    size_t decodedNameLength = 0;
55
400k
    char* resultPtr = result;
56
400k
    resultPtr[0] = 0;
57
58
400k
    size_t curOffsetInLayer = (uint8_t*)encodedName - m_DnsLayer->m_Data;
59
400k
    if (curOffsetInLayer + 1 > m_DnsLayer->m_DataLen)
60
8.40k
      return encodedNameLength;
61
62
392k
    if (iteration > 20)
63
7.92k
    {
64
7.92k
      return encodedNameLength;
65
7.92k
    }
66
67
384k
    uint8_t wordLength = encodedName[0];
68
69
    // A string to parse
70
1.05M
    while (wordLength != 0)
71
924k
    {
72
      // A pointer to another place in the packet
73
924k
      if ((wordLength & 0xc0) == 0xc0)
74
220k
      {
75
220k
        if (curOffsetInLayer + 2 > m_DnsLayer->m_DataLen || encodedNameLength > 255)
76
224
          return cleanup(resultPtr, result, encodedNameLength);
77
78
220k
        uint16_t offsetInLayer =
79
220k
            (wordLength & 0x3f) * 256 + (0xFF & encodedName[1]) + m_DnsLayer->m_OffsetAdjustment;
80
220k
        if (offsetInLayer < sizeof(dnshdr) || offsetInLayer >= m_DnsLayer->m_DataLen)
81
18.1k
        {
82
18.1k
          PCPP_LOG_ERROR("DNS parsing error: name pointer is illegal");
83
18.1k
          return 0;
84
18.1k
        }
85
86
202k
        char tempResult[4096];
87
202k
        memset(tempResult, 0, sizeof(tempResult));
88
202k
        int i = 0;
89
202k
        decodeName((const char*)(m_DnsLayer->m_Data + offsetInLayer), tempResult, iteration + 1);
90
19.0M
        while (tempResult[i] != 0 && decodedNameLength < 255)
91
18.8M
        {
92
18.8M
          resultPtr[0] = tempResult[i++];
93
18.8M
          resultPtr++;
94
18.8M
          decodedNameLength++;
95
18.8M
        }
96
97
202k
        resultPtr[0] = 0;
98
99
        // in this case the length of the pointer is: 1 byte for 0xc0 + 1 byte for the offset itself
100
202k
        return encodedNameLength + sizeof(uint16_t);
101
220k
      }
102
704k
      else
103
704k
      {
104
        // return if next word would be outside of the DNS layer or overflow the buffer behind resultPtr
105
704k
        if (curOffsetInLayer + wordLength + 1 > m_DnsLayer->m_DataLen || encodedNameLength + wordLength > 255)
106
32.6k
        {
107
          // add the last '\0' to the decoded string
108
32.6k
          if (encodedNameLength == 256)
109
0
          {
110
0
            resultPtr--;
111
            // cppcheck-suppress unreadVariable
112
0
            decodedNameLength--;
113
0
          }
114
32.6k
          else
115
32.6k
          {
116
32.6k
            encodedNameLength++;
117
32.6k
          }
118
119
32.6k
          resultPtr[0] = 0;
120
32.6k
          return encodedNameLength;
121
32.6k
        }
122
123
671k
        memcpy(resultPtr, encodedName + 1, wordLength);
124
671k
        resultPtr += wordLength;
125
671k
        resultPtr[0] = '.';
126
671k
        resultPtr++;
127
671k
        decodedNameLength += wordLength + 1;
128
671k
        encodedName += wordLength + 1;
129
671k
        encodedNameLength += wordLength + 1;
130
131
671k
        curOffsetInLayer = (uint8_t*)encodedName - m_DnsLayer->m_Data;
132
671k
        if (curOffsetInLayer + 1 > m_DnsLayer->m_DataLen)
133
1.79k
        {
134
          // add the last '\0' to the decoded string
135
1.79k
          if (encodedNameLength == 256)
136
0
          {
137
            // cppcheck-suppress unreadVariable
138
0
            decodedNameLength--;
139
0
            resultPtr--;
140
0
          }
141
1.79k
          else
142
1.79k
          {
143
1.79k
            encodedNameLength++;
144
1.79k
          }
145
146
1.79k
          resultPtr[0] = 0;
147
1.79k
          return encodedNameLength;
148
1.79k
        }
149
150
669k
        wordLength = encodedName[0];
151
669k
      }
152
924k
    }
153
154
129k
    return cleanup(resultPtr, result, encodedNameLength);
155
384k
  }
156
157
  void IDnsResource::encodeName(const std::string& decodedName, char* result, size_t& resultLen)
158
59.1k
  {
159
59.1k
    resultLen = 0;
160
59.1k
    std::stringstream strstream(decodedName);
161
59.1k
    std::string word;
162
221k
    while (getline(strstream, word, '.'))
163
162k
    {
164
      // pointer to a different hostname in the packet
165
162k
      if (word[0] == '#')
166
0
      {
167
        // convert the number from string to int
168
0
        std::stringstream stream(word.substr(1));
169
0
        int pointerInPacket = 0;
170
0
        stream >> pointerInPacket;
171
172
        // verify it's indeed a number and that is in the range of [0-255]
173
0
        if (stream.fail() || pointerInPacket < 0 || pointerInPacket > 0xff)
174
0
        {
175
0
          PCPP_LOG_ERROR("Error encoding the string '" << decodedName << "'");
176
0
          return;
177
0
        }
178
179
        // set the pointer to the encoded string result
180
0
        result[0] = (uint8_t)0xc0;
181
0
        result[1] = (uint8_t)pointerInPacket;
182
0
        resultLen += 2;
183
0
        return;  // pointer always comes last
184
0
      }
185
186
162k
      result[0] = word.length();
187
162k
      result++;
188
162k
      memcpy(result, word.c_str(), word.length());
189
162k
      result += word.length();
190
162k
      resultLen += word.length() + 1;
191
162k
    }
192
193
59.1k
    result[0] = 0;
194
59.1k
    resultLen++;
195
59.1k
  }
196
197
  DnsType IDnsResource::getDnsType() const
198
44.3k
  {
199
44.3k
    uint16_t dnsType = *reinterpret_cast<uint16_t*>(getRawData() + m_NameLength);
200
44.3k
    return static_cast<DnsType>(be16toh(dnsType));
201
44.3k
  }
202
203
  void IDnsResource::setDnsType(DnsType newType)
204
59.1k
  {
205
59.1k
    uint16_t newTypeAsInt = htobe16((uint16_t)newType);
206
59.1k
    memcpy(getRawData() + m_NameLength, &newTypeAsInt, sizeof(uint16_t));
207
59.1k
  }
208
209
  DnsClass IDnsResource::getDnsClass() const
210
0
  {
211
0
    uint16_t dnsClass = *reinterpret_cast<uint16_t*>(getRawData() + m_NameLength + sizeof(uint16_t));
212
0
    return static_cast<DnsClass>(be16toh(dnsClass));
213
0
  }
214
215
  void IDnsResource::setDnsClass(DnsClass newClass)
216
59.1k
  {
217
59.1k
    uint16_t newClassAsInt = htobe16((uint16_t)newClass);
218
59.1k
    memcpy(getRawData() + m_NameLength + sizeof(uint16_t), &newClassAsInt, sizeof(uint16_t));
219
59.1k
  }
220
221
  bool IDnsResource::setName(const std::string& newName)
222
59.1k
  {
223
59.1k
    char encodedName[4096];
224
59.1k
    size_t encodedNameLen = 0;
225
59.1k
    encodeName(newName, encodedName, encodedNameLen);
226
59.1k
    if (m_DnsLayer != nullptr)
227
0
    {
228
0
      if (encodedNameLen > m_NameLength)
229
0
      {
230
0
        if (!m_DnsLayer->extendLayer(m_OffsetInLayer, encodedNameLen - m_NameLength, this))
231
0
        {
232
0
          PCPP_LOG_ERROR("Couldn't set name for DNS query, unable to extend layer");
233
0
          return false;
234
0
        }
235
0
      }
236
0
      else if (encodedNameLen < m_NameLength)
237
0
      {
238
0
        if (!m_DnsLayer->shortenLayer(m_OffsetInLayer, m_NameLength - encodedNameLen, this))
239
0
        {
240
0
          PCPP_LOG_ERROR("Couldn't set name for DNS query, unable to shorten layer");
241
0
          return false;
242
0
        }
243
0
      }
244
0
    }
245
59.1k
    else
246
59.1k
    {
247
59.1k
      size_t size = getSize();
248
59.1k
      char* tempData = new char[size];
249
59.1k
      memcpy(tempData, m_ExternalRawData, size);
250
59.1k
      memcpy(m_ExternalRawData + encodedNameLen, tempData, size);
251
59.1k
      delete[] tempData;
252
59.1k
    }
253
254
59.1k
    memcpy(getRawData(), encodedName, encodedNameLen);
255
59.1k
    m_NameLength = encodedNameLen;
256
59.1k
    m_DecodedName = newName;
257
258
59.1k
    return true;
259
59.1k
  }
260
261
  void IDnsResource::setDnsLayer(DnsLayer* dnsLayer, size_t offsetInLayer)
262
50.1k
  {
263
50.1k
    memcpy(dnsLayer->m_Data + offsetInLayer, m_ExternalRawData, getSize());
264
50.1k
    m_DnsLayer = dnsLayer;
265
50.1k
    m_OffsetInLayer = offsetInLayer;
266
50.1k
    m_ExternalRawData = nullptr;
267
50.1k
  }
268
269
  uint32_t DnsResource::getTTL() const
270
0
  {
271
0
    uint32_t ttl = *reinterpret_cast<uint32_t*>(getRawData() + m_NameLength + 2 * sizeof(uint16_t));
272
0
    return be32toh(ttl);
273
0
  }
274
275
  void DnsResource::setTTL(uint32_t newTTL)
276
44.3k
  {
277
44.3k
    newTTL = htobe32(newTTL);
278
44.3k
    memcpy(getRawData() + m_NameLength + 2 * sizeof(uint16_t), &newTTL, sizeof(uint32_t));
279
44.3k
  }
280
281
  size_t DnsResource::getDataLength() const
282
276k
  {
283
284
276k
    size_t sizeToRead = m_NameLength + 2 * sizeof(uint16_t) + sizeof(uint32_t);
285
286
    // Heap buffer overflow may occur here, check boundary of m_DnsLayer->m_Data first
287
    // Due to dataLength which is uint16_t, here m_DnsLayer->m_Data must have at least 2 bytes to read
288
276k
    if (m_DnsLayer && m_OffsetInLayer + sizeToRead >= m_DnsLayer->m_DataLen - 1)
289
19.1k
    {
290
19.1k
      return 0;
291
19.1k
    }
292
293
257k
    uint16_t dataLength = *reinterpret_cast<uint16_t*>(getRawData() + sizeToRead);
294
257k
    return be16toh(dataLength);
295
276k
  }
296
297
  DnsResourceDataPtr DnsResource::getData() const
298
0
  {
299
0
    uint8_t* resourceRawData = getRawData() + m_NameLength + 3 * sizeof(uint16_t) + sizeof(uint32_t);
300
0
    size_t dataLength = getDataLength();
301
302
0
    switch (getDnsType())
303
0
    {
304
0
    case DNS_TYPE_A:
305
0
    {
306
0
      return DnsResourceDataPtr(new IPv4DnsResourceData(resourceRawData, dataLength));
307
0
    }
308
309
0
    case DNS_TYPE_AAAA:
310
0
    {
311
0
      return DnsResourceDataPtr(new IPv6DnsResourceData(resourceRawData, dataLength));
312
0
    }
313
314
0
    case DNS_TYPE_NS:
315
0
    case DNS_TYPE_CNAME:
316
0
    case DNS_TYPE_DNAM:
317
0
    case DNS_TYPE_PTR:
318
0
    {
319
0
      return DnsResourceDataPtr(new StringDnsResourceData(
320
0
          resourceRawData, dataLength, const_cast<IDnsResource*>(static_cast<const IDnsResource*>(this))));
321
0
    }
322
323
0
    case DNS_TYPE_MX:
324
0
    {
325
0
      return DnsResourceDataPtr(new MxDnsResourceData(
326
0
          resourceRawData, dataLength, const_cast<IDnsResource*>(static_cast<const IDnsResource*>(this))));
327
0
    }
328
329
0
    default:
330
0
    {
331
0
      return DnsResourceDataPtr(new GenericDnsResourceData(resourceRawData, dataLength));
332
0
    }
333
0
    }
334
0
  }
335
336
  size_t DnsResource::getDataOffset() const
337
0
  {
338
0
    return (size_t)(m_OffsetInLayer + m_NameLength + 3 * sizeof(uint16_t) + sizeof(uint32_t));
339
0
  }
340
341
  bool DnsResource::setData(IDnsResourceData* data)
342
44.3k
  {
343
    // convert data to byte array according to the DNS type
344
44.3k
    size_t dataLength = 0;
345
44.3k
    uint8_t dataAsByteArr[4096];
346
347
44.3k
    if (data == nullptr)
348
0
    {
349
0
      PCPP_LOG_ERROR("Given data is nullptr");
350
0
      return false;
351
0
    }
352
353
44.3k
    switch (getDnsType())
354
44.3k
    {
355
29.5k
    case DNS_TYPE_A:
356
29.5k
    {
357
29.5k
      if (!data->isTypeOf<IPv4DnsResourceData>())
358
0
      {
359
0
        PCPP_LOG_ERROR("DNS record is of type A but given data isn't of type IPv4DnsResourceData");
360
0
        return false;
361
0
      }
362
29.5k
      break;
363
29.5k
    }
364
365
29.5k
    case DNS_TYPE_AAAA:
366
0
    {
367
0
      if (!data->isTypeOf<IPv6DnsResourceData>())
368
0
      {
369
0
        PCPP_LOG_ERROR("DNS record is of type AAAA but given data isn't of type IPv6DnsResourceData");
370
0
        return false;
371
0
      }
372
0
      break;
373
0
    }
374
375
0
    case DNS_TYPE_NS:
376
0
    case DNS_TYPE_CNAME:
377
0
    case DNS_TYPE_DNAM:
378
0
    case DNS_TYPE_PTR:
379
0
    {
380
0
      if (!data->isTypeOf<StringDnsResourceData>())
381
0
      {
382
0
        PCPP_LOG_ERROR(
383
0
            "DNS record is of type NS, CNAME, DNAM or PTR but given data isn't of type StringDnsResourceData");
384
0
        return false;
385
0
      }
386
0
      break;
387
0
    }
388
389
0
    case DNS_TYPE_MX:
390
0
    {
391
0
      if (!data->isTypeOf<MxDnsResourceData>())
392
0
      {
393
0
        PCPP_LOG_ERROR("DNS record is of type MX but given data isn't of type MxDnsResourceData");
394
0
        return false;
395
0
      }
396
0
      break;
397
0
    }
398
399
14.7k
    default:
400
14.7k
    {
401
      // do nothing
402
14.7k
    }
403
44.3k
    }
404
405
    // convert the IDnsResourceData to byte array
406
44.3k
    if (!data->toByteArr(dataAsByteArr, dataLength, this))
407
0
    {
408
0
      PCPP_LOG_ERROR("Cannot convert DNS resource data to byte array, data is probably invalid");
409
0
      return false;
410
0
    }
411
412
44.3k
    size_t dataLengthOffset = m_NameLength + (2 * sizeof(uint16_t)) + sizeof(uint32_t);
413
44.3k
    size_t dataOffset = dataLengthOffset + sizeof(uint16_t);
414
415
44.3k
    if (m_DnsLayer != nullptr)
416
0
    {
417
0
      size_t curLength = getDataLength();
418
0
      if (dataLength > curLength)
419
0
      {
420
0
        if (!m_DnsLayer->extendLayer(m_OffsetInLayer + dataOffset, dataLength - curLength, this))
421
0
        {
422
0
          PCPP_LOG_ERROR("Couldn't set data for DNS query, unable to extend layer");
423
0
          return false;
424
0
        }
425
0
      }
426
0
      else if (dataLength < curLength)
427
0
      {
428
0
        if (!m_DnsLayer->shortenLayer(m_OffsetInLayer + dataOffset, curLength - dataLength, this))
429
0
        {
430
0
          PCPP_LOG_ERROR("Couldn't set data for DNS query, unable to shorten layer");
431
0
          return false;
432
0
        }
433
0
      }
434
0
    }
435
436
    // write data to resource
437
44.3k
    memcpy(getRawData() + dataOffset, dataAsByteArr, dataLength);
438
    // update data length in resource
439
44.3k
    dataLength = htobe16((uint16_t)dataLength);
440
44.3k
    memcpy(getRawData() + dataLengthOffset, &dataLength, sizeof(uint16_t));
441
442
44.3k
    return true;
443
44.3k
  }
444
445
  uint16_t DnsResource::getCustomDnsClass() const
446
0
  {
447
0
    uint16_t value = *reinterpret_cast<uint16_t*>(getRawData() + m_NameLength + sizeof(uint16_t));
448
0
    return be16toh(value);
449
0
  }
450
451
  void DnsResource::setCustomDnsClass(uint16_t customValue)
452
12.5k
  {
453
12.5k
    memcpy(getRawData() + m_NameLength + sizeof(uint16_t), &customValue, sizeof(uint16_t));
454
12.5k
  }
455
456
}  // namespace pcpp