解析 CNAME,DNS 中的 NS 答案

Parsing CNAME, NS in DNS answer

我有以下代码检查 DNS 响应中的查询类型,然后相应地进一步打印它。我需要一种使用给定参数解析 CNAME 和 NS 的方法,但我无法这样做。这里 tmp 是定义为 tmp = (u_char *)(dpkt->payload + 12); 的变量,dns_label_to_str 是将 DNS 名称转换为字符串格式的函数,定义为:*dns_label_to_str(u_char **label, u_char *dest,size_t dest_size,const u_char *payload,const u_char *end)

代码:

   switch (qtype) {
            case 1: /* A */
                    data = inet_ntop(AF_INET, tmp, dbuf, BUFSIZ);
            break;
            case 2:  /* NS */
            case 5:  /* CNAME */
            case 12: /* PTR */
                    data = (char *)dns_label_to_str(
                            &tmp, (u_char *)dbuf, BUFSIZ,
                            dpkt->payload, tmp + len
                    );
            break;
            case 10: /* NULL */
                    data = "NULL";
            break;
            case 15: /* MX (16-bit priority / label) */
                    i = snprintf(dbuf, 7, "%u ", ntohs(*(uint16_t *)tmp));
                    tmp += 2;
                    data = (char *)dns_label_to_str(
                            &tmp, (u_char *)(dbuf + i), BUFSIZ - i,
                            dpkt->payload, tmp + len - 2
                    );
                    data = dbuf;
            break;
            case 16: /* TXT (1 byte text length / text) */
                    if (*tmp <= len && tmp + len < end) {
                            memcpy(dbuf, tmp+1, *tmp);
                            dbuf[*tmp+1] = '[=10=]';
                    } else *dbuf = '[=10=]';
                    data = dbuf;
            break;
            case 17: /* AAAA */
                    data = inet_ntop(AF_INET6, tmp, dbuf, BUFSIZ);
            break;
            default:
                    /* Ignore unhandled RR types */
                    *dbuf = '[=10=]';
                    data = dbuf;
    }

    /* Print the output. */
    printf("%ld %-5s %-30s %s\n", hdr->ts.tv_sec,
           dns_types[qtype], label, data);

ret:
    return 0;
}

如果有人可以帮助我如何获得 CNAME,qtype == 5 会有所帮助。提前致谢。

我不完全清楚 dns_label_to_str 函数的作用,但参数的最可能含义是:

  • label:指向指针的指针。 *label 最初指向数据包中名称的开头,并更新为指向数据包中名称之后的下一个字节。
  • dest:目标缓冲区的地址。 (这可以是字符串(文本格式)或未压缩的名称(有线格式)。
  • dest:目标缓冲区的长度。根据函数的输出格式,这应该至少为 256(有线格式)或至少 1024(文本格式,由于转义)。当然,目标缓冲区也必须足够大。
  • payload:指向整个 DNS数据包开始的指针(不是当前资源记录的数据!)。
  • end:指向整个 DNS 数据包中最后一个字节的指针。

dns_label_to_str 等函数需要引用整个数据包的原因是 DNS 标签压缩:名称可以包含指向另一个数据包中较早位置的压缩引用,重用另一个名称的尾部在数据包中,偏移量较小。这些引用永远不会指向资源记录数据,因为对于 CNAME 记录,它只包含一个名称:如果使用压缩,名称 tail 必须来自其他地方。

EDIT 我们在关于前向压缩指针的评论中有一些争论。 BIND 9 拒绝它们,但许多其他实现接受它们。

dpkt->payload + 12 处的数据是 问题部分 ,即原始请求中内容的副本,并将包含一个(未压缩的)标签,称为 QNAME 然后是 16 位 QTYPEQCLASS 字段。

只有在那之后你才会开始查找响应数据,其中的每条记录都遵循 RFC 1035 第 4 节中的这种结构,我强烈建议你在继续之前阅读:

                                1  1  1  1  1  1
  0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|                                               |
/                                               /
/                      NAME                     /
|                                               |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|                      TYPE                     |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|                     CLASS                     |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|                      TTL                      |
|                                               |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|                   RDLENGTH                    |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
/                     RDATA                     /
/                                               /
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

请注意,问题部分的数据结构与上面的前三个字段相同。