从名称服务器回复 c++ 中提取 DNS 记录 (MX) 详细信息
Extract DNS record (MX) details from name server reply c++
我正在尝试使用以下代码获取低优先级 MX 记录的主机名,但我无法从给定的名称服务器回复中解析所需的详细信息(主机名、ttl、优先级)。
u_char nsbuf[4096], dispbuf[4096];
ns_msg msg;
ns_rr rr;
int i, j, l;
std::string domain("gmail.com");
l = res_query(domain.c_str(), ns_c_any, ns_t_mx, nsbuf, sizeof (nsbuf));
ns_initparse(nsbuf, l, &msg);
printf("%s :\n", domain.c_str());
l = ns_msg_count(msg, ns_s_an);
for (j = 0; j < l; j++)
{
int prr = ns_parserr(&msg, ns_s_an, j, &rr);
ns_sprintrr(&msg, &rr, NULL, NULL, reinterpret_cast<char*> (dispbuf), sizeof (dispbuf));
printf("%s\n", dispbuf);
}
以上代码给出的结果为
gmail.com. 15M IN MX 30
alt3.gmail-smtp-in.l.google.com.
是否有任何可用的函数来获取主机名、优先级、ttl 等在单独的缓冲区中,如下所示?
host -> alt3.gmail-smtp-in.l.google.com
priority -> 30
ttl -> 15M
而且,我们是否应该手动检查优先级更高的记录,
或者是否有任何实用函数或代码可以满足要求?
编辑:
我尝试了以下代码来提取数据
#include <cstdlib>
#include <stdio.h>
#include <iostream>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/in.h>
#include <resolv.h>
#include <cstring>
#include <string>
#include <string.h>
using namespace std;
int main(int argc, char** argv) {
u_char nsbuf[4096];
u_char dispbuf[4096];
ns_msg msg;
ns_rr rr;
int i, j, l;
std::string domain("gmail.com");
l = res_query(domain.c_str(), ns_c_any, ns_t_mx, nsbuf, sizeof (nsbuf));
if (l < 0) {
perror(domain.c_str());
} else {
#ifdef USE_PQUERY
res_pquery(&_res, nsbuf, l, stdout);
#else
ns_initparse(nsbuf, l, &msg);
l = ns_msg_count(msg, ns_s_an);
for (j = 0; j < l; j++) {
int prr = ns_parserr(&msg, ns_s_an, j, &rr);
//BLOCK 1
char *cp;
cp = (char *) ns_rr_name(rr);
printf("CP->%s\n", (char *) cp);
int i1 = ns_rr_type(rr);
printf("Type->%d\n", i1);
int i2 = ns_rr_class(rr);
printf("Class->%d\n", i2);
int i3 = ns_rr_ttl(rr);
printf("TTL->%d\n", i3);
int i4 = ns_rr_rdlen(rr);
printf("DataLength->%d\n", i4);
//BLOCK 2
const u_char *rdata = ns_rr_rdata(rr) +1;
printf("DataU_char-> %s\n", reinterpret_cast<const char*> (rdata));
int len = strlen(reinterpret_cast<const char*> (rdata));
printf("len->%d\n", len);
char rdatatemp[1024];
strncpy(rdatatemp, reinterpret_cast<const char*> (rdata), sizeof (rdatatemp));
printf("DataChar->%s\n", rdatatemp);
ns_sprintrr(&msg, &rr, NULL, NULL, reinterpret_cast<char*> (dispbuf), sizeof (dispbuf));
printf("FullRecord->%s\n", dispbuf);
printf("\n");
}
#endif
}
return 0;
}
以上代码对于txt记录效果很好,但是对于mx记录,解析不正确,结果如下
输出:
CP->gmail.com
输入->15
Class->1
TTL->130
DataLength->32
DataU_char-> gmail-smtp-inlgoogle...
len->33
DataChar->gmail-smtp-inlgoogle�
FullRecord->gmail.com。 2m10s IN MX 30 alt3.gmail-smtp-in.l.google.com.
CP->gmail.com
输入->15
Class->1
TTL->130
DataLength->9
DataU_char-> alt2�.�
长度->10
DataChar->alt2�.�
FullRecord->gmail.com。 2m10s IN MX 20 alt2.gmail-smtp-in.l.google.com.
因此在 DataChar & DataU_char 中打印了特殊符号。
打印 'alt2�.�' 而不是 'alt2.gmail-smtp-in.l.google.com.'
DataLength 值也是错误的。
我也无法获得记录的优先级。
我是不是遗漏了什么,还是 c++ 库本身的错误?
libresolv
没有 public 函数来解包特定的资源记录类型,但是里面有 函数可以帮助你自己做。
特别是,查看可以读取(压缩的)域名的 dn_expand
和将从记录中读取大端双八位字节字段的 ns_get16
,因此在您的情况下:
char exchange[NS_MAXDNAME];
const u_char *rdata = ns_rr_rdata(rr);
const uint16_t pri = ns_get16(rdata);
int len = dn_expand(nsbuf, nsbuf + msg_len, rdata + 2, exchange, sizeof(exchange));
printf("Pri->%d\n", pri);
printf("Exchange->%s\n", exchange);
其中 msg_len
替换了您覆盖的 l
变量,其中包含接收到的数据包的长度。
调用 dn_expand()
中的 rdata + 2
跳过 16 位优先级字段。
我正在尝试使用以下代码获取低优先级 MX 记录的主机名,但我无法从给定的名称服务器回复中解析所需的详细信息(主机名、ttl、优先级)。
u_char nsbuf[4096], dispbuf[4096];
ns_msg msg;
ns_rr rr;
int i, j, l;
std::string domain("gmail.com");
l = res_query(domain.c_str(), ns_c_any, ns_t_mx, nsbuf, sizeof (nsbuf));
ns_initparse(nsbuf, l, &msg);
printf("%s :\n", domain.c_str());
l = ns_msg_count(msg, ns_s_an);
for (j = 0; j < l; j++)
{
int prr = ns_parserr(&msg, ns_s_an, j, &rr);
ns_sprintrr(&msg, &rr, NULL, NULL, reinterpret_cast<char*> (dispbuf), sizeof (dispbuf));
printf("%s\n", dispbuf);
}
以上代码给出的结果为
gmail.com. 15M IN MX 30 alt3.gmail-smtp-in.l.google.com.
是否有任何可用的函数来获取主机名、优先级、ttl 等在单独的缓冲区中,如下所示?
host -> alt3.gmail-smtp-in.l.google.com
priority -> 30
ttl -> 15M
而且,我们是否应该手动检查优先级更高的记录, 或者是否有任何实用函数或代码可以满足要求?
编辑:
我尝试了以下代码来提取数据
#include <cstdlib>
#include <stdio.h>
#include <iostream>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/in.h>
#include <resolv.h>
#include <cstring>
#include <string>
#include <string.h>
using namespace std;
int main(int argc, char** argv) {
u_char nsbuf[4096];
u_char dispbuf[4096];
ns_msg msg;
ns_rr rr;
int i, j, l;
std::string domain("gmail.com");
l = res_query(domain.c_str(), ns_c_any, ns_t_mx, nsbuf, sizeof (nsbuf));
if (l < 0) {
perror(domain.c_str());
} else {
#ifdef USE_PQUERY
res_pquery(&_res, nsbuf, l, stdout);
#else
ns_initparse(nsbuf, l, &msg);
l = ns_msg_count(msg, ns_s_an);
for (j = 0; j < l; j++) {
int prr = ns_parserr(&msg, ns_s_an, j, &rr);
//BLOCK 1
char *cp;
cp = (char *) ns_rr_name(rr);
printf("CP->%s\n", (char *) cp);
int i1 = ns_rr_type(rr);
printf("Type->%d\n", i1);
int i2 = ns_rr_class(rr);
printf("Class->%d\n", i2);
int i3 = ns_rr_ttl(rr);
printf("TTL->%d\n", i3);
int i4 = ns_rr_rdlen(rr);
printf("DataLength->%d\n", i4);
//BLOCK 2
const u_char *rdata = ns_rr_rdata(rr) +1;
printf("DataU_char-> %s\n", reinterpret_cast<const char*> (rdata));
int len = strlen(reinterpret_cast<const char*> (rdata));
printf("len->%d\n", len);
char rdatatemp[1024];
strncpy(rdatatemp, reinterpret_cast<const char*> (rdata), sizeof (rdatatemp));
printf("DataChar->%s\n", rdatatemp);
ns_sprintrr(&msg, &rr, NULL, NULL, reinterpret_cast<char*> (dispbuf), sizeof (dispbuf));
printf("FullRecord->%s\n", dispbuf);
printf("\n");
}
#endif
}
return 0;
}
以上代码对于txt记录效果很好,但是对于mx记录,解析不正确,结果如下
输出:
CP->gmail.com
输入->15
Class->1
TTL->130
DataLength->32
DataU_char-> gmail-smtp-inlgoogle...
len->33
DataChar->gmail-smtp-inlgoogle�
FullRecord->gmail.com。 2m10s IN MX 30 alt3.gmail-smtp-in.l.google.com.
CP->gmail.com
输入->15
Class->1
TTL->130
DataLength->9
DataU_char-> alt2�.�
长度->10
DataChar->alt2�.�
FullRecord->gmail.com。 2m10s IN MX 20 alt2.gmail-smtp-in.l.google.com.
因此在 DataChar & DataU_char 中打印了特殊符号。
打印 'alt2�.�' 而不是 'alt2.gmail-smtp-in.l.google.com.'
DataLength 值也是错误的。
我也无法获得记录的优先级。
我是不是遗漏了什么,还是 c++ 库本身的错误?
libresolv
没有 public 函数来解包特定的资源记录类型,但是里面有 函数可以帮助你自己做。
特别是,查看可以读取(压缩的)域名的 dn_expand
和将从记录中读取大端双八位字节字段的 ns_get16
,因此在您的情况下:
char exchange[NS_MAXDNAME];
const u_char *rdata = ns_rr_rdata(rr);
const uint16_t pri = ns_get16(rdata);
int len = dn_expand(nsbuf, nsbuf + msg_len, rdata + 2, exchange, sizeof(exchange));
printf("Pri->%d\n", pri);
printf("Exchange->%s\n", exchange);
其中 msg_len
替换了您覆盖的 l
变量,其中包含接收到的数据包的长度。
调用 dn_expand()
中的 rdata + 2
跳过 16 位优先级字段。