#define LOG_MODULE PacketLogModuleDnsLayer
#include "DnsResource.h"
#include "Logger.h"
#include <sstream>
#include <string.h>
#include "EndianPortable.h"
namespace pcpp
{
IDnsResource::IDnsResource(DnsLayer* dnsLayer, size_t offsetInLayer)
: m_DnsLayer(dnsLayer), m_OffsetInLayer(offsetInLayer), m_NextResource(nullptr)
{
char decodedName[4096];
m_NameLength = decodeName((const char*)getRawData(), decodedName);
if (m_NameLength > 0)
m_DecodedName = decodedName;
}
IDnsResource::IDnsResource(uint8_t* emptyRawData)
: m_DnsLayer(nullptr), m_OffsetInLayer(0), m_NextResource(nullptr), m_DecodedName(""), m_NameLength(0), m_ExternalRawData(emptyRawData)
{
}
uint8_t* IDnsResource::getRawData() const
{
if (m_DnsLayer == nullptr)
return m_ExternalRawData;
return m_DnsLayer->m_Data + m_OffsetInLayer;
}
static size_t cleanup(char* resultPtr, char* result, size_t encodedNameLength)
{
if (resultPtr > result)
{
result[resultPtr - result - 1] = 0;
}
if (resultPtr - result < 256)
{
resultPtr[0] = 0;
encodedNameLength++;
}
return encodedNameLength;
}
size_t IDnsResource::decodeName(const char* encodedName, char* result, int iteration)
{
size_t encodedNameLength = 0;
size_t decodedNameLength = 0;
char* resultPtr = result;
resultPtr[0] = 0;
size_t curOffsetInLayer = (uint8_t*)encodedName - m_DnsLayer->m_Data;
if (curOffsetInLayer + 1 > m_DnsLayer->m_DataLen)
return encodedNameLength;
if (iteration > 20)
{
return encodedNameLength;
}
uint8_t wordLength = encodedName[0];
while (wordLength != 0)
{
if ((wordLength & 0xc0) == 0xc0)
{
if (curOffsetInLayer + 2 > m_DnsLayer->m_DataLen || encodedNameLength > 255)
return cleanup(resultPtr, result, encodedNameLength);
uint16_t offsetInLayer = (wordLength & 0x3f)*256 + (0xFF & encodedName[1]) + m_DnsLayer->m_OffsetAdjustment;
if (offsetInLayer < sizeof(dnshdr) || offsetInLayer >= m_DnsLayer->m_DataLen)
{
PCPP_LOG_ERROR("DNS parsing error: name pointer is illegal");
return 0;
}
char tempResult[4096];
memset(tempResult, 0, sizeof(tempResult));
int i = 0;
decodeName((const char*)(m_DnsLayer->m_Data + offsetInLayer), tempResult, iteration+1);
while (tempResult[i] != 0 && decodedNameLength < 255)
{
resultPtr[0] = tempResult[i++];
resultPtr++;
decodedNameLength++;
}
resultPtr[0] = 0;
return encodedNameLength + sizeof(uint16_t);
}
else
{
if (curOffsetInLayer + wordLength + 1 > m_DnsLayer->m_DataLen || encodedNameLength + wordLength > 255)
{
if (encodedNameLength == 256)
{
resultPtr--;
decodedNameLength--;
}
else
{
encodedNameLength++;
}
resultPtr[0] = 0;
return encodedNameLength;
}
memcpy(resultPtr, encodedName+1, wordLength);
resultPtr += wordLength;
resultPtr[0] = '.';
resultPtr++;
decodedNameLength += wordLength + 1;
encodedName += wordLength + 1;
encodedNameLength += wordLength + 1;
curOffsetInLayer = (uint8_t*)encodedName - m_DnsLayer->m_Data;
if (curOffsetInLayer + 1 > m_DnsLayer->m_DataLen)
{
if (encodedNameLength == 256)
{
decodedNameLength--;
resultPtr--;
}
else
{
encodedNameLength++;
}
resultPtr[0] = 0;
return encodedNameLength;
}
wordLength = encodedName[0];
}
}
return cleanup(resultPtr, result, encodedNameLength);
}
void IDnsResource::encodeName(const std::string& decodedName, char* result, size_t& resultLen)
{
resultLen = 0;
std::stringstream strstream(decodedName);
std::string word;
while (getline(strstream, word, '.'))
{
if (word[0] == '#')
{
std::stringstream stream(word.substr(1));
int pointerInPacket = 0;
stream >> pointerInPacket;
if (stream.fail() || pointerInPacket < 0 || pointerInPacket > 0xff)
{
PCPP_LOG_ERROR("Error encoding the string '" << decodedName << "'");
return;
}
result[0] = (uint8_t)0xc0;
result[1] = (uint8_t)pointerInPacket;
result += 2;
resultLen += 2;
return; }
result[0] = word.length();
result++;
memcpy(result, word.c_str(), word.length());
result += word.length();
resultLen += word.length() + 1;
}
result[0] = 0;
resultLen++;
}
DnsType IDnsResource::getDnsType() const
{
uint16_t dnsType = *(uint16_t*)(getRawData() + m_NameLength);
return (DnsType)be16toh(dnsType);
}
void IDnsResource::setDnsType(DnsType newType)
{
uint16_t newTypeAsInt = htobe16((uint16_t)newType);
memcpy(getRawData() + m_NameLength, &newTypeAsInt, sizeof(uint16_t));
}
DnsClass IDnsResource::getDnsClass() const
{
uint16_t dnsClass = *(uint16_t*)(getRawData() + m_NameLength + sizeof(uint16_t));
return (DnsClass)be16toh(dnsClass);
}
void IDnsResource::setDnsClass(DnsClass newClass)
{
uint16_t newClassAsInt = htobe16((uint16_t)newClass);
memcpy(getRawData() + m_NameLength + sizeof(uint16_t), &newClassAsInt, sizeof(uint16_t));
}
bool IDnsResource::setName(const std::string& newName)
{
char encodedName[4096];
size_t encodedNameLen = 0;
encodeName(newName, encodedName, encodedNameLen);
if (m_DnsLayer != nullptr)
{
if (encodedNameLen > m_NameLength)
{
if (!m_DnsLayer->extendLayer(m_OffsetInLayer, encodedNameLen-m_NameLength, this))
{
PCPP_LOG_ERROR("Couldn't set name for DNS query, unable to extend layer");
return false;
}
}
else if (encodedNameLen < m_NameLength)
{
if (!m_DnsLayer->shortenLayer(m_OffsetInLayer, m_NameLength-encodedNameLen, this))
{
PCPP_LOG_ERROR("Couldn't set name for DNS query, unable to shorten layer");
return false;
}
}
}
else
{
size_t size = getSize();
char* tempData = new char[size];
memcpy(tempData, m_ExternalRawData, size);
memcpy(m_ExternalRawData + encodedNameLen, tempData, size);
delete[] tempData;
}
memcpy(getRawData(), encodedName, encodedNameLen);
m_NameLength = encodedNameLen;
m_DecodedName = newName;
return true;
}
void IDnsResource::setDnsLayer(DnsLayer* dnsLayer, size_t offsetInLayer)
{
memcpy(dnsLayer->m_Data + offsetInLayer, m_ExternalRawData, getSize());
m_DnsLayer = dnsLayer;
m_OffsetInLayer = offsetInLayer;
m_ExternalRawData = nullptr;
}
uint32_t DnsResource::getTTL() const
{
uint32_t ttl = *(uint32_t*)(getRawData() + m_NameLength + 2*sizeof(uint16_t));
return be32toh(ttl);
}
void DnsResource::setTTL(uint32_t newTTL)
{
newTTL = htobe32(newTTL);
memcpy(getRawData() + m_NameLength + 2*sizeof(uint16_t), &newTTL, sizeof(uint32_t));
}
size_t DnsResource::getDataLength() const
{
size_t sizeToRead = m_NameLength + 2*sizeof(uint16_t) + sizeof(uint32_t);
if (m_DnsLayer && m_OffsetInLayer + sizeToRead >= m_DnsLayer->m_DataLen - 1)
{
return 0;
}
uint16_t dataLength = *(uint16_t*)(getRawData() + sizeToRead);
return be16toh(dataLength);
}
DnsResourceDataPtr DnsResource::getData() const
{
uint8_t* resourceRawData = getRawData() + m_NameLength + 3*sizeof(uint16_t) + sizeof(uint32_t);
size_t dataLength = getDataLength();
switch (getDnsType())
{
case DNS_TYPE_A:
{
return DnsResourceDataPtr(new IPv4DnsResourceData(resourceRawData, dataLength));
}
case DNS_TYPE_AAAA:
{
return DnsResourceDataPtr(new IPv6DnsResourceData(resourceRawData, dataLength));
}
case DNS_TYPE_NS:
case DNS_TYPE_CNAME:
case DNS_TYPE_DNAM:
case DNS_TYPE_PTR:
{
return DnsResourceDataPtr(new StringDnsResourceData(resourceRawData, dataLength, const_cast<IDnsResource*>(static_cast<const IDnsResource*>(this))));
}
case DNS_TYPE_MX:
{
return DnsResourceDataPtr(new MxDnsResourceData(resourceRawData, dataLength, const_cast<IDnsResource*>(static_cast<const IDnsResource*>(this))));
}
default:
{
return DnsResourceDataPtr(new GenericDnsResourceData(resourceRawData, dataLength));
}
}
}
size_t DnsResource::getDataOffset() const
{
return (size_t)(m_OffsetInLayer + m_NameLength + 3*sizeof(uint16_t) + sizeof(uint32_t));
}
bool DnsResource::setData(IDnsResourceData* data)
{
size_t dataLength = 0;
uint8_t dataAsByteArr[4096];
if (data == nullptr)
{
PCPP_LOG_ERROR("Given data is NULL");
return false;
}
switch (getDnsType())
{
case DNS_TYPE_A:
{
if (!data->isTypeOf<IPv4DnsResourceData>())
{
PCPP_LOG_ERROR("DNS record is of type A but given data isn't of type IPv4DnsResourceData");
return false;
}
break;
}
case DNS_TYPE_AAAA:
{
if (!data->isTypeOf<IPv6DnsResourceData>())
{
PCPP_LOG_ERROR("DNS record is of type AAAA but given data isn't of type IPv6DnsResourceData");
return false;
}
break;
}
case DNS_TYPE_NS:
case DNS_TYPE_CNAME:
case DNS_TYPE_DNAM:
case DNS_TYPE_PTR:
{
if (!data->isTypeOf<StringDnsResourceData>())
{
PCPP_LOG_ERROR("DNS record is of type NS, CNAME, DNAM or PTR but given data isn't of type StringDnsResourceData");
return false;
}
break;
}
case DNS_TYPE_MX:
{
if (!data->isTypeOf<MxDnsResourceData>())
{
PCPP_LOG_ERROR("DNS record is of type MX but given data isn't of type MxDnsResourceData");
return false;
}
break;
}
default:
{
}
}
if (!data->toByteArr(dataAsByteArr, dataLength, this))
{
PCPP_LOG_ERROR("Cannot convert DNS resource data to byte array, data is probably invalid");
return false;
}
size_t dataLengthOffset = m_NameLength + (2*sizeof(uint16_t)) + sizeof(uint32_t);
size_t dataOffset = dataLengthOffset + sizeof(uint16_t);
if (m_DnsLayer != nullptr)
{
size_t curLength = getDataLength();
if (dataLength > curLength)
{
if (!m_DnsLayer->extendLayer(m_OffsetInLayer + dataOffset, dataLength-curLength, this))
{
PCPP_LOG_ERROR("Couldn't set data for DNS query, unable to extend layer");
return false;
}
}
else if (dataLength < curLength)
{
if (!m_DnsLayer->shortenLayer(m_OffsetInLayer + dataOffset, curLength-dataLength, this))
{
PCPP_LOG_ERROR("Couldn't set data for DNS query, unable to shorten layer");
return false;
}
}
}
memcpy(getRawData() + dataOffset, dataAsByteArr, dataLength);
dataLength = htobe16((uint16_t)dataLength);
memcpy(getRawData() + dataLengthOffset, &dataLength, sizeof(uint16_t));
return true;
}
uint16_t DnsResource::getCustomDnsClass() const
{
uint16_t value = *(uint16_t*)(getRawData() + m_NameLength + sizeof(uint16_t));
return be16toh(value);
}
void DnsResource::setCustomDnsClass(uint16_t customValue)
{
memcpy(getRawData() + m_NameLength + sizeof(uint16_t), &customValue, sizeof(uint16_t));
}
}