Coverage Report

Created: 2023-01-25 06:41

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