它主要是提取DNS数据包之中查询问题的信息,如:问题类型、问题类别、问题内容(域/IP),我们如果想要对于某个DNS数据包需要进行遥测的时,或者进行NS缓存生命周期管理,那么就需要类似这样的函数实现了。
例子:
uint16_t queries_type = 0;
uint16_t queries_clazz = 0;
ppp::string domain = ppp::net::native::dns::ExtractHostY((Byte*)packet, packet_length,
[&queries_type, &queries_clazz](ppp::net::native::dns::dns_hdr* h, ppp::string& domain, uint16_t type, uint16_t clazz) noexcept -> bool {
queries_type = type;
queries_clazz = clazz;
return true;
});
源声明:
#pragma pack(push, 1)
struct dns_hdr {
uint16_t usTransID; // 标识符
uint16_t usFlags; // 各种标志位
uint16_t usQuestionCount; // Question字段个数
uint16_t usAnswerCount; // Answer字段个数
uint16_t usAuthorityCount; // Authority字段个数
uint16_t usAdditionalCount; // Additional字段个数
};
#pragma pack(pop)
static constexpr int MAX_DOMAINNAME_LEN = 255; /* MAX: 253 +. ≈ 254 BYTE or 254 CHAR+. ≈ 255 BYTE */
static constexpr int DNS_PORT = PPP_DNS_SYS_PORT;
static constexpr int DNS_TYPE_SIZE = 2;
static constexpr int DNS_CLASS_SIZE = 2;
static constexpr int DNS_TTL_SIZE = 4;
static constexpr int DNS_DATALEN_SIZE = 2;
static constexpr int DNS_TYPE_A = 0x0001; //1 a host address
static constexpr int DNS_TYPE_AAAA = 0x001c; //1 a host address
static constexpr int DNS_TYPE_CNAME = 0x0005; //5 the canonical name for an alias
static constexpr int DNS_CLASS_IN = 0x0001;
static constexpr int DNS_PACKET_MAX_SIZE = (sizeof(struct dns_hdr) + MAX_DOMAINNAME_LEN + DNS_TYPE_SIZE + DNS_CLASS_SIZE);
ppp::string ExtractHost(
const Byte* szPacketStartPos,
int nPacketLength) noexcept;
ppp::string ExtractHostX(
const Byte* szPacketStartPos,
int nPacketLength,
const ppp::function<bool(dns_hdr*)>& fPredicateB) noexcept;
ppp::string ExtractHostY(
const Byte* szPacketStartPos,
int nPacketLength,
const ppp::function<bool(dns_hdr*, ppp::string&, uint16_t, uint16_t)>& fPredicateE) noexcept;
ppp::string ExtractHostZ(
const Byte* szPacketStartPos,
int nPacketLength,
const ppp::function<bool(dns_hdr*)>& fPredicateB,
const ppp::function<bool(dns_hdr*, ppp::string&, uint16_t, uint16_t)>& fPredicateE) noexcept;
源实现:
static bool ExtractName(char* szEncodedStr, uint16_t* pusEncodedStrLen, char* szDotStr, uint16_t nDotStrSize, char* szPacketStartPos, char* szPacketEndPos, char** ppDecodePos) noexcept
{
if (NULL == szEncodedStr || NULL == pusEncodedStrLen || NULL == szDotStr || szEncodedStr >= szPacketEndPos)
{
return false;
}
char*& pDecodePos = *ppDecodePos;
pDecodePos = szEncodedStr;
uint16_t usPlainStrLen = 0;
uint8_t nLabelDataLen = 0;
*pusEncodedStrLen = 0;
while ((nLabelDataLen = *pDecodePos) != 0x00)
{
// Normal Format,LabelDataLen + Label
if ((nLabelDataLen & 0xc0) == 0)
{
if ((usPlainStrLen + nLabelDataLen + 1) > nDotStrSize || (pDecodePos + nLabelDataLen + 1) >= szPacketEndPos)
{
return false;
}
memcpy(szDotStr + usPlainStrLen, pDecodePos + 1, nLabelDataLen);
memcpy(szDotStr + usPlainStrLen + nLabelDataLen, ".", 1);
pDecodePos += (nLabelDataLen + 1);
usPlainStrLen += (nLabelDataLen + 1);
*pusEncodedStrLen += (nLabelDataLen + 1);
}
else
{
// Message compression format is 11000000 00000000, consisting of two bytes.
// The first two bits are the jump flag, and the last 14 bits are the offset of the jump。
if (NULL == szPacketStartPos)
{
return false;
}
uint16_t usJumpPos = ntohs(*(uint16_t*)(pDecodePos)) & 0x3fff;
uint16_t nEncodeStrLen = 0;
if (!ExtractName(szPacketStartPos + usJumpPos, &nEncodeStrLen, szDotStr + usPlainStrLen, nDotStrSize - usPlainStrLen, szPacketStartPos, szPacketEndPos, ppDecodePos))
{
return false;
}
else
{
*pusEncodedStrLen += 2;
return true;
}
}
}
++pDecodePos;
szDotStr[usPlainStrLen - 1] = '\0';
*pusEncodedStrLen += 1;
return true;
}
static bool ExtractHost_DefaultPredicateB(dns_hdr* h) noexcept
{
uint16_t usFlags = htons(h->usFlags) & htons(DNS_TYPE_A);
return usFlags != 0;
}
ppp::string ExtractHost(const Byte* szPacketStartPos, int nPacketLength) noexcept
{
ppp::function<bool(dns_hdr*)> predicate = ExtractHost_DefaultPredicateB;
return ExtractHostX(szPacketStartPos, nPacketLength, predicate);
}
ppp::string ExtractHostX(const Byte* szPacketStartPos, int nPacketLength, const ppp::function<bool(dns_hdr*)>& fPredicateB) noexcept
{
ppp::function<bool(dns_hdr*, ppp::string&, uint16_t, uint16_t)> fPredicateE =
[](dns_hdr* h, ppp::string& domain, uint16_t type, uint16_t clazz) noexcept -> bool
{
return true;
};
return ExtractHostZ(szPacketStartPos, nPacketLength, fPredicateB, fPredicateE);
}
ppp::string ExtractHostY(const Byte* szPacketStartPos, int nPacketLength, const ppp::function<bool(dns_hdr*, ppp::string&, uint16_t, uint16_t)>& fPredicateE) noexcept
{
ppp::function<bool(dns_hdr*)> fPredicateB = ExtractHost_DefaultPredicateB;
return ExtractHostZ(szPacketStartPos, nPacketLength, fPredicateB, fPredicateE);
}
ppp::string ExtractHostZ(const Byte* szPacketStartPos,
int nPacketLength,
const ppp::function<bool(dns_hdr*)>& fPredicateB,
const ppp::function<bool(dns_hdr*, ppp::string&, uint16_t, uint16_t)>& fPredicateE) noexcept
{
static constexpr int MAX_DOMAINNAME_LEN_STR = MAX_DOMAINNAME_LEN + 1;
if (NULL == fPredicateB || NULL == fPredicateE)
{
return ppp::string();
}
struct dns_hdr* pDNSHeader = (struct dns_hdr*)szPacketStartPos;
if (NULL == pDNSHeader || nPacketLength < sizeof(pDNSHeader))
{
return ppp::string();
}
if (!fPredicateB(pDNSHeader))
{
return ppp::string();
}
int nQuestionCount = htons(pDNSHeader->usQuestionCount);
if (nQuestionCount < 1)
{
return ppp::string();
}
std::shared_ptr<Byte> pioBuffers = make_shared_alloc<Byte>(MAX_DOMAINNAME_LEN_STR);
if (NULL == pioBuffers)
{
return ppp::string();
}
uint16_t pusEncodedStrLen = 0;
char* pDecodePos = NULL;
char* szDomainDotStr = (char*)pioBuffers.get();
if (!ExtractName((char*)(pDNSHeader + 1), &pusEncodedStrLen, szDomainDotStr,
(uint16_t)MAX_DOMAINNAME_LEN_STR, (char*)szPacketStartPos, (char*)szPacketStartPos + nPacketLength, &pDecodePos))
{
return ppp::string();
}
while (pusEncodedStrLen > 0 && szDomainDotStr[pusEncodedStrLen - 1] == '\x0')
{
pusEncodedStrLen--;
}
if (pusEncodedStrLen == 0)
{
return ppp::string();
}
uint16_t* pusDecodePos = (uint16_t*)pDecodePos;
uint16_t usQueriesType = ntohs(pusDecodePos[0]);
uint16_t usQueriesClass = ntohs(pusDecodePos[1]);
ppp::string strDomianStr(szDomainDotStr, pusEncodedStrLen);
if (!fPredicateE(pDNSHeader, strDomianStr, usQueriesType, usQueriesClass))
{
return ppp::string();
}
return strDomianStr.data();
}