Coverage Report

Created: 2026-08-31 07:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/PcapPlusPlus/Packet++/src/DnsResource.cpp
Line
Count
Source
1
31.7k
#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
169k
      : m_DnsLayer(dnsLayer), m_OffsetInLayer(offsetInLayer), m_NextResource(nullptr), m_ExternalRawData(nullptr)
13
169k
  {
14
169k
    char decodedName[4096];
15
169k
    m_NameLength = decodeName((const char*)getRawData(), decodedName);
16
169k
    if (m_NameLength > 0)
17
146k
      m_DecodedName = decodedName;
18
169k
  }
19
20
  IDnsResource::IDnsResource(uint8_t* emptyRawData)
21
53.1k
      : m_DnsLayer(nullptr), m_OffsetInLayer(0), m_NextResource(nullptr), m_DecodedName(""), m_NameLength(0),
22
53.1k
        m_ExternalRawData(emptyRawData)
23
53.1k
  {}
24
25
  uint8_t* IDnsResource::getRawData() const
26
697k
  {
27
697k
    if (m_DnsLayer == nullptr)
28
428k
      return m_ExternalRawData;
29
30
269k
    return m_DnsLayer->m_Data + m_OffsetInLayer;
31
697k
  }
32
33
  static size_t cleanup(char* resultPtr, char* result, size_t encodedNameLength)
34
106k
  {
35
    // remove the last "."
36
106k
    if (resultPtr > result)
37
77.8k
    {
38
77.8k
      result[resultPtr - result - 1] = 0;
39
77.8k
    }
40
41
106k
    if (resultPtr - result < 256)
42
106k
    {
43
      // add the last '\0' to encodedNameLength
44
106k
      resultPtr[0] = 0;
45
106k
      encodedNameLength++;
46
106k
    }
47
48
106k
    return encodedNameLength;
49
106k
  }
50
51
  size_t IDnsResource::decodeName(const char* encodedName, char* result, int iteration)
52
285k
  {
53
285k
    size_t encodedNameLength = 0;
54
285k
    size_t decodedNameLength = 0;
55
285k
    char* resultPtr = result;
56
285k
    resultPtr[0] = 0;
57
58
285k
    size_t curOffsetInLayer = (uint8_t*)encodedName - m_DnsLayer->m_Data;
59
285k
    if (curOffsetInLayer + 1 > m_DnsLayer->m_DataLen)
60
7.09k
      return encodedNameLength;
61
62
278k
    if (iteration > 20)
63
4.44k
    {
64
4.44k
      return encodedNameLength;
65
4.44k
    }
66
67
274k
    uint8_t wordLength = encodedName[0];
68
69
    // A string to parse
70
646k
    while (wordLength != 0)
71
540k
    {
72
      // A pointer to another place in the packet
73
540k
      if ((wordLength & 0xc0) == 0xc0)
74
132k
      {
75
132k
        if (curOffsetInLayer + 2 > m_DnsLayer->m_DataLen || encodedNameLength > 255)
76
304
          return cleanup(resultPtr, result, encodedNameLength);
77
78
132k
        uint16_t offsetInLayer =
79
132k
            (wordLength & 0x3f) * 256 + (0xFF & encodedName[1]) + m_DnsLayer->m_OffsetAdjustment;
80
132k
        if (offsetInLayer < sizeof(dnshdr) || offsetInLayer >= m_DnsLayer->m_DataLen)
81
15.8k
        {
82
15.8k
          PCPP_LOG_ERROR("DNS parsing error: name pointer is illegal");
83
15.8k
          return 0;
84
15.8k
        }
85
86
116k
        char tempResult[4096];
87
116k
        memset(tempResult, 0, sizeof(tempResult));
88
116k
        int i = 0;
89
116k
        decodeName((const char*)(m_DnsLayer->m_Data + offsetInLayer), tempResult, iteration + 1);
90
9.80M
        while (tempResult[i] != 0 && decodedNameLength < 255)
91
9.68M
        {
92
9.68M
          resultPtr[0] = tempResult[i++];
93
9.68M
          resultPtr++;
94
9.68M
          decodedNameLength++;
95
9.68M
        }
96
97
116k
        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
116k
        return encodedNameLength + sizeof(uint16_t);
101
132k
      }
102
407k
      else
103
407k
      {
104
        // return if next word would be outside of the DNS layer or overflow the buffer behind resultPtr
105
407k
        if (curOffsetInLayer + wordLength + 1 > m_DnsLayer->m_DataLen || encodedNameLength + wordLength > 255)
106
33.4k
        {
107
          // add the last '\0' to the decoded string
108
33.4k
          if (encodedNameLength == 256)
109
33
          {
110
33
            resultPtr--;
111
            // cppcheck-suppress unreadVariable
112
33
            decodedNameLength--;
113
33
          }
114
33.3k
          else
115
33.3k
          {
116
33.3k
            encodedNameLength++;
117
33.3k
          }
118
119
33.4k
          resultPtr[0] = 0;
120
33.4k
          return encodedNameLength;
121
33.4k
        }
122
123
374k
        memcpy(resultPtr, encodedName + 1, wordLength);
124
374k
        resultPtr += wordLength;
125
374k
        resultPtr[0] = '.';
126
374k
        resultPtr++;
127
374k
        decodedNameLength += wordLength + 1;
128
374k
        encodedName += wordLength + 1;
129
374k
        encodedNameLength += wordLength + 1;
130
131
374k
        curOffsetInLayer = (uint8_t*)encodedName - m_DnsLayer->m_Data;
132
374k
        if (curOffsetInLayer + 1 > m_DnsLayer->m_DataLen)
133
1.93k
        {
134
          // add the last '\0' to the decoded string
135
1.93k
          if (encodedNameLength == 256)
136
0
          {
137
            // cppcheck-suppress unreadVariable
138
0
            decodedNameLength--;
139
0
            resultPtr--;
140
0
          }
141
1.93k
          else
142
1.93k
          {
143
1.93k
            encodedNameLength++;
144
1.93k
          }
145
146
1.93k
          resultPtr[0] = 0;
147
1.93k
          return encodedNameLength;
148
1.93k
        }
149
150
372k
        wordLength = encodedName[0];
151
372k
      }
152
540k
    }
153
154
106k
    return cleanup(resultPtr, result, encodedNameLength);
155
274k
  }
156
157
  void IDnsResource::encodeName(const std::string& decodedName, char* result, size_t& resultLen)
158
53.1k
  {
159
53.1k
    resultLen = 0;
160
53.1k
    std::stringstream strstream(decodedName);
161
53.1k
    std::string word;
162
199k
    while (getline(strstream, word, '.'))
163
146k
    {
164
      // pointer to a different hostname in the packet
165
146k
      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
146k
      result[0] = word.length();
187
146k
      result++;
188
146k
      memcpy(result, word.c_str(), word.length());
189
146k
      result += word.length();
190
146k
      resultLen += word.length() + 1;
191
146k
    }
192
193
53.1k
    result[0] = 0;
194
53.1k
    resultLen++;
195
53.1k
  }
196
197
  DnsType IDnsResource::getDnsType() const
198
39.8k
  {
199
39.8k
    uint16_t dnsType = *reinterpret_cast<uint16_t*>(getRawData() + m_NameLength);
200
39.8k
    return static_cast<DnsType>(be16toh(dnsType));
201
39.8k
  }
202
203
  void IDnsResource::setDnsType(DnsType newType)
204
53.1k
  {
205
53.1k
    uint16_t newTypeAsInt = htobe16((uint16_t)newType);
206
53.1k
    memcpy(getRawData() + m_NameLength, &newTypeAsInt, sizeof(uint16_t));
207
53.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
53.1k
  {
217
53.1k
    uint16_t newClassAsInt = htobe16((uint16_t)newClass);
218
53.1k
    memcpy(getRawData() + m_NameLength + sizeof(uint16_t), &newClassAsInt, sizeof(uint16_t));
219
53.1k
  }
220
221
  bool IDnsResource::setName(const std::string& newName)
222
53.1k
  {
223
53.1k
    char encodedName[4096];
224
53.1k
    size_t encodedNameLen = 0;
225
53.1k
    encodeName(newName, encodedName, encodedNameLen);
226
53.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
53.1k
    else
246
53.1k
    {
247
53.1k
      size_t size = getSize();
248
53.1k
      char* tempData = new char[size];
249
53.1k
      memcpy(tempData, m_ExternalRawData, size);
250
53.1k
      memcpy(m_ExternalRawData + encodedNameLen, tempData, size);
251
53.1k
      delete[] tempData;
252
53.1k
    }
253
254
53.1k
    memcpy(getRawData(), encodedName, encodedNameLen);
255
53.1k
    m_NameLength = encodedNameLen;
256
53.1k
    m_DecodedName = newName;
257
258
53.1k
    return true;
259
53.1k
  }
260
261
  void IDnsResource::setDnsLayer(DnsLayer* dnsLayer, size_t offsetInLayer)
262
39.8k
  {
263
39.8k
    memcpy(dnsLayer->m_Data + offsetInLayer, m_ExternalRawData, getSize());
264
39.8k
    m_DnsLayer = dnsLayer;
265
39.8k
    m_OffsetInLayer = offsetInLayer;
266
39.8k
    m_ExternalRawData = nullptr;
267
39.8k
  }
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
39.8k
  {
277
39.8k
    newTTL = htobe32(newTTL);
278
39.8k
    memcpy(getRawData() + m_NameLength + 2 * sizeof(uint16_t), &newTTL, sizeof(uint32_t));
279
39.8k
  }
280
281
  size_t DnsResource::getDataLength() const
282
214k
  {
283
284
214k
    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
214k
    if (m_DnsLayer && m_OffsetInLayer + sizeToRead >= m_DnsLayer->m_DataLen - 1)
289
15.6k
    {
290
15.6k
      return 0;
291
15.6k
    }
292
293
199k
    uint16_t dataLength = *reinterpret_cast<uint16_t*>(getRawData() + sizeToRead);
294
199k
    return be16toh(dataLength);
295
214k
  }
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
39.8k
  {
343
    // convert data to byte array according to the DNS type
344
39.8k
    size_t dataLength = 0;
345
39.8k
    uint8_t dataAsByteArr[4096];
346
347
39.8k
    if (data == nullptr)
348
0
    {
349
0
      PCPP_LOG_ERROR("Given data is nullptr");
350
0
      return false;
351
0
    }
352
353
39.8k
    switch (getDnsType())
354
39.8k
    {
355
26.5k
    case DNS_TYPE_A:
356
26.5k
    {
357
26.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
26.5k
      break;
363
26.5k
    }
364
365
26.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
13.2k
    default:
400
13.2k
    {
401
      // do nothing
402
13.2k
    }
403
39.8k
    }
404
405
    // convert the IDnsResourceData to byte array
406
39.8k
    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
39.8k
    size_t dataLengthOffset = m_NameLength + (2 * sizeof(uint16_t)) + sizeof(uint32_t);
413
39.8k
    size_t dataOffset = dataLengthOffset + sizeof(uint16_t);
414
415
39.8k
    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
39.8k
    memcpy(getRawData() + dataOffset, dataAsByteArr, dataLength);
438
    // update data length in resource
439
39.8k
    dataLength = htobe16((uint16_t)dataLength);
440
39.8k
    memcpy(getRawData() + dataLengthOffset, &dataLength, sizeof(uint16_t));
441
442
39.8k
    return true;
443
39.8k
  }
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
9.96k
  {
453
9.96k
    memcpy(getRawData() + m_NameLength + sizeof(uint16_t), &customValue, sizeof(uint16_t));
454
9.96k
  }
455
456
}  // namespace pcpp