#define LOG_MODULE PacketLogModuleDnsLayer
#include "DnsLayer.h"
#include "Logger.h"
#include "IpAddress.h"
#include <sstream>
#include <string.h>
#include <iomanip>
#include <stdlib.h>
#include "EndianPortable.h"
namespace pcpp
{
DnsLayer::DnsLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet)
: Layer(data, dataLen, prevLayer, packet)
{
init(0, true);
}
DnsLayer::DnsLayer()
{
initNewLayer(0);
}
DnsLayer::DnsLayer(const DnsLayer& other) : Layer(other)
{
init(other.m_OffsetAdjustment, true);
}
DnsLayer::DnsLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet, size_t offsetAdjustment)
: Layer(data, dataLen, prevLayer, packet)
{
init(offsetAdjustment, true);
}
DnsLayer::DnsLayer(size_t offsetAdjustment)
{
initNewLayer(offsetAdjustment);
}
DnsLayer& DnsLayer::operator=(const DnsLayer& other)
{
Layer::operator=(other);
IDnsResource* curResource = m_ResourceList;
while (curResource != nullptr)
{
IDnsResource* temp = curResource->getNextResource();
delete curResource;
curResource = temp;
}
init(other.m_OffsetAdjustment, true);
return (*this);
}
DnsLayer::~DnsLayer()
{
IDnsResource* curResource = m_ResourceList;
while (curResource != nullptr)
{
IDnsResource* nextResource = curResource->getNextResource();
delete curResource;
curResource = nextResource;
}
}
void DnsLayer::init(size_t offsetAdjustment, bool callParseResource)
{
m_OffsetAdjustment = offsetAdjustment;
m_Protocol = DNS;
m_ResourceList = nullptr;
m_FirstQuery = nullptr;
m_FirstAnswer = nullptr;
m_FirstAuthority = nullptr;
m_FirstAdditional = nullptr;
if (callParseResource)
parseResources();
}
void DnsLayer::initNewLayer(size_t offsetAdjustment)
{
m_OffsetAdjustment = offsetAdjustment;
const size_t headerLen = getBasicHeaderSize();
m_DataLen = headerLen;
m_Data = new uint8_t[headerLen];
memset(m_Data, 0, headerLen);
init(m_OffsetAdjustment, false);
}
size_t DnsLayer::getBasicHeaderSize()
{
return sizeof(dnshdr) + m_OffsetAdjustment;
}
dnshdr* DnsLayer::getDnsHeader() const
{
uint8_t* ptr = m_Data + m_OffsetAdjustment;
return (dnshdr*)ptr;
}
bool DnsLayer::extendLayer(int offsetInLayer, size_t numOfBytesToExtend, IDnsResource* resource)
{
if (!Layer::extendLayer(offsetInLayer, numOfBytesToExtend))
return false;
IDnsResource* curResource = resource->getNextResource();
while (curResource != nullptr)
{
curResource->m_OffsetInLayer += numOfBytesToExtend;
curResource = curResource->getNextResource();
}
return true;
}
bool DnsLayer::shortenLayer(int offsetInLayer, size_t numOfBytesToShorten, IDnsResource* resource)
{
if (!Layer::shortenLayer(offsetInLayer, numOfBytesToShorten))
return false;
IDnsResource* curResource = resource->getNextResource();
while (curResource != nullptr)
{
curResource->m_OffsetInLayer -= numOfBytesToShorten;
curResource = curResource->getNextResource();
}
return true;
}
void DnsLayer::parseResources()
{
size_t offsetInPacket = getBasicHeaderSize();
IDnsResource* curResource = m_ResourceList;
uint16_t numOfQuestions = be16toh(getDnsHeader()->numberOfQuestions);
uint16_t numOfAnswers = be16toh(getDnsHeader()->numberOfAnswers);
uint16_t numOfAuthority = be16toh(getDnsHeader()->numberOfAuthority);
uint16_t numOfAdditional = be16toh(getDnsHeader()->numberOfAdditional);
uint32_t numOfOtherResources = numOfQuestions + numOfAnswers + numOfAuthority + numOfAdditional;
if (numOfOtherResources > 300)
{
PCPP_LOG_ERROR("DNS layer contains more than 300 resources, probably a bad packet. "
"Skipping parsing DNS resources");
return;
}
for (uint32_t i = 0; i < numOfOtherResources; i++)
{
DnsResourceType resType;
if (numOfQuestions > 0)
{
resType = DnsQueryType;
numOfQuestions--;
}
else if (numOfAnswers > 0)
{
resType = DnsAnswerType;
numOfAnswers--;
}
else if (numOfAuthority > 0)
{
resType = DnsAuthorityType;
numOfAuthority--;
}
else
{
resType = DnsAdditionalType;
numOfAdditional--;
}
DnsResource* newResource = nullptr;
DnsQuery* newQuery = nullptr;
IDnsResource* newGenResource = nullptr;
if (resType == DnsQueryType)
{
newQuery = new DnsQuery(this, offsetInPacket);
newGenResource = newQuery;
offsetInPacket += newQuery->getSize();
}
else
{
newResource = new DnsResource(this, offsetInPacket, resType);
newGenResource = newResource;
offsetInPacket += newResource->getSize();
}
if (offsetInPacket > m_DataLen)
{
delete newGenResource;
return;
}
if (m_ResourceList == nullptr)
{
m_ResourceList = newGenResource;
curResource = m_ResourceList;
}
else
{
curResource->setNexResource(newGenResource);
curResource = curResource->getNextResource();
}
if (resType == DnsQueryType && m_FirstQuery == nullptr)
m_FirstQuery = newQuery;
else if (resType == DnsAnswerType && m_FirstAnswer == nullptr)
m_FirstAnswer = newResource;
else if (resType == DnsAuthorityType && m_FirstAuthority == nullptr)
m_FirstAuthority = newResource;
else if (resType == DnsAdditionalType && m_FirstAdditional == nullptr)
m_FirstAdditional = newResource;
}
}
IDnsResource* DnsLayer::getResourceByName(IDnsResource* startFrom, size_t resourceCount, const std::string& name, bool exactMatch) const
{
size_t index = 0;
while (index < resourceCount)
{
if (startFrom == nullptr)
return nullptr;
std::string resourceName = startFrom->getName();
if (exactMatch && resourceName == name)
return startFrom;
else if (!exactMatch && resourceName.find(name) != std::string::npos)
return startFrom;
startFrom = startFrom->getNextResource();
index++;
}
return nullptr;
}
DnsQuery* DnsLayer::getQuery(const std::string& name, bool exactMatch) const
{
uint16_t numOfQueries = be16toh(getDnsHeader()->numberOfQuestions);
IDnsResource* res = getResourceByName(m_FirstQuery, numOfQueries, name, exactMatch);
if (res != nullptr)
return dynamic_cast<DnsQuery*>(res);
return nullptr;
}
DnsQuery* DnsLayer::getFirstQuery() const
{
return m_FirstQuery;
}
DnsQuery* DnsLayer::getNextQuery(DnsQuery* query) const
{
if (query == nullptr
|| query->getNextResource() == nullptr
|| query->getType() != DnsQueryType
|| query->getNextResource()->getType() != DnsQueryType)
return nullptr;
return (DnsQuery*)(query->getNextResource());
}
size_t DnsLayer::getQueryCount() const
{
return be16toh(getDnsHeader()->numberOfQuestions);
}
DnsResource* DnsLayer::getAnswer(const std::string& name, bool exactMatch) const
{
uint16_t numOfAnswers = be16toh(getDnsHeader()->numberOfAnswers);
IDnsResource* res = getResourceByName(m_FirstAnswer, numOfAnswers, name, exactMatch);
if (res != nullptr)
return dynamic_cast<DnsResource*>(res);
return nullptr;
}
DnsResource* DnsLayer::getFirstAnswer() const
{
return m_FirstAnswer;
}
DnsResource* DnsLayer::getNextAnswer(DnsResource* answer) const
{
if (answer == nullptr
|| answer->getNextResource() == nullptr
|| answer->getType() != DnsAnswerType
|| answer->getNextResource()->getType() != DnsAnswerType)
return nullptr;
return (DnsResource*)(answer->getNextResource());
}
size_t DnsLayer::getAnswerCount() const
{
return be16toh(getDnsHeader()->numberOfAnswers);
}
DnsResource* DnsLayer::getAuthority(const std::string& name, bool exactMatch) const
{
uint16_t numOfAuthorities = be16toh(getDnsHeader()->numberOfAuthority);
IDnsResource* res = getResourceByName(m_FirstAuthority, numOfAuthorities, name, exactMatch);
if (res != nullptr)
return dynamic_cast<DnsResource*>(res);
return nullptr;
}
DnsResource* DnsLayer::getFirstAuthority() const
{
return m_FirstAuthority;
}
DnsResource* DnsLayer::getNextAuthority(DnsResource* authority) const
{
if (authority == nullptr
|| authority->getNextResource() == nullptr
|| authority->getType() != DnsAuthorityType
|| authority->getNextResource()->getType() != DnsAuthorityType)
return nullptr;
return (DnsResource*)(authority->getNextResource());
}
size_t DnsLayer::getAuthorityCount() const
{
return be16toh(getDnsHeader()->numberOfAuthority);
}
DnsResource* DnsLayer::getAdditionalRecord(const std::string& name, bool exactMatch) const
{
uint16_t numOfAdditionalRecords = be16toh(getDnsHeader()->numberOfAdditional);
IDnsResource* res = getResourceByName(m_FirstAdditional, numOfAdditionalRecords, name, exactMatch);
if (res != nullptr)
return dynamic_cast<DnsResource*>(res);
return nullptr;
}
DnsResource* DnsLayer::getFirstAdditionalRecord() const
{
return m_FirstAdditional;
}
DnsResource* DnsLayer::getNextAdditionalRecord(DnsResource* additionalRecord) const
{
if (additionalRecord == nullptr
|| additionalRecord->getNextResource() == nullptr
|| additionalRecord->getType() != DnsAdditionalType
|| additionalRecord->getNextResource()->getType() != DnsAdditionalType)
return nullptr;
return (DnsResource*)(additionalRecord->getNextResource());
}
size_t DnsLayer::getAdditionalRecordCount() const
{
return be16toh(getDnsHeader()->numberOfAdditional);
}
std::string DnsLayer::toString() const
{
std::ostringstream tidAsString;
tidAsString << be16toh(getDnsHeader()->transactionID);
std::ostringstream queryCount;
queryCount << getQueryCount();
std::ostringstream answerCount;
answerCount << getAnswerCount();
std::ostringstream authorityCount;
authorityCount << getAuthorityCount();
std::ostringstream additionalCount;
additionalCount << getAdditionalRecordCount();
if (getDnsHeader()->queryOrResponse == 1)
{
return "DNS query response, ID: " + tidAsString.str() + ";" +
" queries: " + queryCount.str() +
", answers: " + answerCount.str() +
", authorities: " + authorityCount.str() +
", additional record: " + additionalCount.str();
}
else if (getDnsHeader()->queryOrResponse == 0)
{
return "DNS query, ID: " + tidAsString.str() + ";" +
" queries: " + queryCount.str() +
", answers: " + answerCount.str() +
", authorities: " + authorityCount.str() +
", additional record: " + additionalCount.str();
}
else {
return "DNS record without queries and answers, ID: " + tidAsString.str() + ";" +
" queries: " + queryCount.str() +
", answers: " + answerCount.str() +
", authorities: " + authorityCount.str() +
", additional record: " + additionalCount.str();
}
}
IDnsResource* DnsLayer::getFirstResource(DnsResourceType resType) const
{
switch (resType)
{
case DnsQueryType:
{
return m_FirstQuery;
}
case DnsAnswerType:
{
return m_FirstAnswer;
}
case DnsAuthorityType:
{
return m_FirstAuthority;
}
case DnsAdditionalType:
{
return m_FirstAdditional;
}
default:
return nullptr;
}
}
void DnsLayer::setFirstResource(DnsResourceType resType, IDnsResource* resource)
{
switch (resType)
{
case DnsQueryType:
{
m_FirstQuery = dynamic_cast<DnsQuery*>(resource);
break;
}
case DnsAnswerType:
{
m_FirstAnswer = dynamic_cast<DnsResource*>(resource);
break;
}
case DnsAuthorityType:
{
m_FirstAuthority = dynamic_cast<DnsResource*>(resource);
break;
}
case DnsAdditionalType:
{
m_FirstAdditional = dynamic_cast<DnsResource*>(resource);
break;
}
default:
return;
}
}
DnsResource* DnsLayer::addResource(DnsResourceType resType, const std::string& name, DnsType dnsType, DnsClass dnsClass,
uint32_t ttl, IDnsResourceData* data)
{
uint8_t newResourceRawData[4096];
memset(newResourceRawData, 0, sizeof(newResourceRawData));
DnsResource* newResource = new DnsResource(newResourceRawData, resType);
newResource->setDnsClass(dnsClass);
newResource->setDnsType(dnsType);
newResource->setName(name);
newResource->setTTL(ttl);
if (!newResource->setData(data))
{
delete newResource;
PCPP_LOG_ERROR("Couldn't set new resource data");
return nullptr;
}
size_t newResourceOffsetInLayer = getBasicHeaderSize();
IDnsResource* curResource = m_ResourceList;
while (curResource != nullptr && curResource->getType() <= resType)
{
newResourceOffsetInLayer += curResource->getSize();
IDnsResource* nextResource = curResource->getNextResource();
if (nextResource == nullptr || nextResource->getType() > resType)
break;
curResource = nextResource;
}
if (curResource != nullptr)
{
if (curResource->getType() > newResource->getType())
newResource->setNexResource(m_ResourceList);
else
newResource->setNexResource(curResource->getNextResource());
}
else newResource->setNexResource(m_ResourceList);
if (!extendLayer(newResourceOffsetInLayer, newResource->getSize(), newResource))
{
PCPP_LOG_ERROR("Couldn't extend DNS layer, addResource failed");
delete newResource;
return nullptr;
}
newResource->setDnsLayer(this, newResourceOffsetInLayer);
if (curResource != nullptr)
{
curResource->setNexResource(newResource);
if (curResource->getType() < newResource->getType())
{
setFirstResource(resType, newResource);
}
else if (curResource->getType() > newResource->getType())
{
m_ResourceList = newResource;
setFirstResource(resType, newResource);
}
}
else {
m_ResourceList = newResource;
setFirstResource(resType, newResource);
}
return newResource;
}
DnsQuery* DnsLayer::addQuery(const std::string& name, DnsType dnsType, DnsClass dnsClass)
{
uint8_t newQueryRawData[256];
DnsQuery* newQuery = new DnsQuery(newQueryRawData);
newQuery->setDnsClass(dnsClass);
newQuery->setDnsType(dnsType);
newQuery->setName(name);
size_t newQueryOffsetInLayer = getBasicHeaderSize();
DnsQuery* curQuery = getFirstQuery();
while (curQuery != nullptr)
{
newQueryOffsetInLayer += curQuery->getSize();
DnsQuery* nextQuery = getNextQuery(curQuery);
if (nextQuery == nullptr)
break;
curQuery = nextQuery;
}
if (curQuery != nullptr)
newQuery->setNexResource(curQuery->getNextResource());
else
newQuery->setNexResource(m_ResourceList);
if (!extendLayer(newQueryOffsetInLayer, newQuery->getSize(), newQuery))
{
PCPP_LOG_ERROR("Couldn't extend DNS layer, addQuery failed");
delete newQuery;
return nullptr;
}
newQuery->setDnsLayer(this, newQueryOffsetInLayer);
if (curQuery != nullptr)
curQuery->setNexResource(newQuery);
else {
m_ResourceList = newQuery;
m_FirstQuery = newQuery;
}
getDnsHeader()->numberOfQuestions = htobe16(getQueryCount() + 1);
return newQuery;
}
DnsQuery* DnsLayer::addQuery(DnsQuery* const copyQuery)
{
if (copyQuery == nullptr)
return nullptr;
return addQuery(copyQuery->getName(), copyQuery->getDnsType(), copyQuery->getDnsClass());
}
bool DnsLayer::removeQuery(const std::string& queryNameToRemove, bool exactMatch)
{
DnsQuery* queryToRemove = getQuery(queryNameToRemove, exactMatch);
if (queryToRemove == nullptr)
{
PCPP_LOG_DEBUG("Query not found");
return false;
}
return removeQuery(queryToRemove);
}
bool DnsLayer::removeQuery(DnsQuery* queryToRemove)
{
bool res = removeResource(queryToRemove);
if (res)
{
getDnsHeader()->numberOfQuestions = htobe16(getQueryCount() - 1);
}
return res;
}
DnsResource* DnsLayer::addAnswer(const std::string& name, DnsType dnsType, DnsClass dnsClass, uint32_t ttl, IDnsResourceData* data)
{
DnsResource* res = addResource(DnsAnswerType, name, dnsType, dnsClass, ttl, data);
if (res != nullptr)
{
getDnsHeader()->numberOfAnswers = htobe16(getAnswerCount() + 1);
}
return res;
}
DnsResource* DnsLayer::addAnswer(DnsResource* const copyAnswer)
{
if (copyAnswer == nullptr)
return nullptr;
return addAnswer(copyAnswer->getName(), copyAnswer->getDnsType(), copyAnswer->getDnsClass(), copyAnswer->getTTL(), copyAnswer->getData().get());
}
bool DnsLayer::removeAnswer(const std::string& answerNameToRemove, bool exactMatch)
{
DnsResource* answerToRemove = getAnswer(answerNameToRemove, exactMatch);
if (answerToRemove == nullptr)
{
PCPP_LOG_DEBUG("Answer record not found");
return false;
}
return removeAnswer(answerToRemove);
}
bool DnsLayer::removeAnswer(DnsResource* answerToRemove)
{
bool res = removeResource(answerToRemove);
if (res)
{
getDnsHeader()->numberOfAnswers = htobe16(getAnswerCount() - 1);
}
return res;
}
DnsResource* DnsLayer::addAuthority(const std::string& name, DnsType dnsType, DnsClass dnsClass, uint32_t ttl, IDnsResourceData* data)
{
DnsResource* res = addResource(DnsAuthorityType, name, dnsType, dnsClass, ttl, data);
if (res != nullptr)
{
getDnsHeader()->numberOfAuthority = htobe16(getAuthorityCount() + 1);
}
return res;
}
DnsResource* DnsLayer::addAuthority(DnsResource* const copyAuthority)
{
if (copyAuthority == nullptr)
return nullptr;
return addAuthority(copyAuthority->getName(), copyAuthority->getDnsType(), copyAuthority->getDnsClass(), copyAuthority->getTTL(), copyAuthority->getData().get());
}
bool DnsLayer::removeAuthority(const std::string& authorityNameToRemove, bool exactMatch)
{
DnsResource* authorityToRemove = getAuthority(authorityNameToRemove, exactMatch);
if (authorityToRemove == nullptr)
{
PCPP_LOG_DEBUG("Authority not found");
return false;
}
return removeAuthority(authorityToRemove);
}
bool DnsLayer::removeAuthority(DnsResource* authorityToRemove)
{
bool res = removeResource(authorityToRemove);
if (res)
{
getDnsHeader()->numberOfAuthority = htobe16(getAuthorityCount() - 1);
}
return res;
}
DnsResource* DnsLayer::addAdditionalRecord(const std::string& name, DnsType dnsType, DnsClass dnsClass, uint32_t ttl, IDnsResourceData* data)
{
DnsResource* res = addResource(DnsAdditionalType, name, dnsType, dnsClass, ttl, data);
if (res != nullptr)
{
getDnsHeader()->numberOfAdditional = htobe16(getAdditionalRecordCount() + 1);
}
return res;
}
DnsResource* DnsLayer::addAdditionalRecord(const std::string& name, DnsType dnsType, uint16_t customData1, uint32_t customData2, IDnsResourceData* data)
{
DnsResource* res = addAdditionalRecord(name, dnsType, DNS_CLASS_ANY, customData2, data);
if (res != nullptr)
{
res->setCustomDnsClass(customData1);
}
return res;
}
DnsResource* DnsLayer::addAdditionalRecord(DnsResource* const copyAdditionalRecord)
{
if (copyAdditionalRecord == nullptr)
return nullptr;
return addAdditionalRecord(copyAdditionalRecord->getName(), copyAdditionalRecord->getDnsType(), copyAdditionalRecord->getCustomDnsClass(), copyAdditionalRecord->getTTL(), copyAdditionalRecord->getData().get());
}
bool DnsLayer::removeAdditionalRecord(const std::string& additionalRecordNameToRemove, bool exactMatch)
{
DnsResource* additionalRecordToRemove = getAdditionalRecord(additionalRecordNameToRemove, exactMatch);
if (additionalRecordToRemove == nullptr)
{
PCPP_LOG_DEBUG("Additional record not found");
return false;
}
return removeAdditionalRecord(additionalRecordToRemove);
}
bool DnsLayer::removeAdditionalRecord(DnsResource* additionalRecordToRemove)
{
bool res = removeResource(additionalRecordToRemove);
if (res)
{
getDnsHeader()->numberOfAdditional = htobe16(getAdditionalRecordCount() - 1);
}
return res;
}
bool DnsLayer::removeResource(IDnsResource* resourceToRemove)
{
if (resourceToRemove == nullptr)
{
PCPP_LOG_DEBUG("resourceToRemove cannot be NULL");
return false;
}
IDnsResource* prevResource = m_ResourceList;
if (m_ResourceList != resourceToRemove)
{
while (prevResource != nullptr)
{
IDnsResource* temp = prevResource->getNextResource();
if (temp == resourceToRemove)
break;
prevResource = temp;
}
}
if (prevResource == nullptr)
{
PCPP_LOG_DEBUG("Resource not found");
return false;
}
if (!shortenLayer(resourceToRemove->m_OffsetInLayer, resourceToRemove->getSize(), resourceToRemove))
{
PCPP_LOG_ERROR("Couldn't shorten the DNS layer, resource cannot be removed");
return false;
}
if (m_ResourceList != resourceToRemove)
{
prevResource->setNexResource(resourceToRemove->getNextResource());
}
else
{
m_ResourceList = resourceToRemove->getNextResource();
}
if (getFirstResource(resourceToRemove->getType()) == resourceToRemove)
{
IDnsResource* nextResource = resourceToRemove->getNextResource();
if (nextResource != nullptr && nextResource->getType() == resourceToRemove->getType())
setFirstResource(resourceToRemove->getType(), nextResource);
else
setFirstResource(resourceToRemove->getType(), nullptr);
}
delete resourceToRemove;
return true;
}
uint16_t DnsOverTcpLayer::getTcpMessageLength()
{
return be16toh(*(uint16_t*)m_Data);
}
void DnsOverTcpLayer::setTcpMessageLength(uint16_t value)
{
((uint16_t*)m_Data)[0] = htobe16(value);
}
void DnsOverTcpLayer::computeCalculateFields()
{
setTcpMessageLength(m_DataLen - sizeof(uint16_t));
}
}