You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C语言构建DNS查询QNAME字段时遇到的问题求助

问题描述

我尝试用C语言创建DNS查询,已在buf起始位置构建好DNS_QUERY_HEADER,但将argv[1]传入的主机名添加到数组后续位置时出现问题:数据包头部、类型和类别都正常,但查询的主机名显示为watson.events.data.microsoft.com或root这类非预期值,推测是QNAME标签构建有误。

我的实现代码片段如下:

分割主机名

unsigned char *qname;
qname = &buf[sizeof(struct DNS_QUERY_HEADER)];
char *host = argv[1]; //my hostname
strcat((char*)host,".");
unsigned char *htrim = strtok(host, ".");

构建QNAME标签

if(htrim != NULL){
    while(htrim != NULL){
        *qname++ = htons(strlen((char*)htrim));
        for(int i = 0; i< strlen((char*)htrim); i++)
        {
            *qname++ = (htrim[i]);
        }
        htrim = strtok(NULL, ".");       
    }
}
*qname++='\0';

问题分析与修复方案

核心错误点

  • 直接修改只读内存:argv[1]指向程序启动时传入的字符串字面量,属于只读内存区域,调用strcat修改它会触发未定义行为,导致内存数据错乱,出现随机域名。
  • 错误的字节序转换:DNS的QNAME标签长度是单字节无符号整数,不需要用htons(该函数用于16位数据的网络字节序转换)。用htons会把1字节的长度转成2字节,破坏后续数据包结构。
  • strtok的误用:strtok会直接修改传入的字符串,但原字符串是只读的,同样会导致未定义行为。

修复后的代码实现

// 1. 先将argv[1]复制到可写缓冲区,避免修改只读内存
char host_buf[256]; // 足够容纳绝大多数域名
strncpy(host_buf, argv[1], sizeof(host_buf)-1);
host_buf[sizeof(host_buf)-1] = '\0'; // 确保字符串终止

// 2. 拼接末尾的点,处理根域名或普通域名
size_t host_len = strlen(host_buf);
if (host_len > 0 && host_buf[host_len-1] != '.') {
    strncat(host_buf, ".", sizeof(host_buf)-host_len-1);
}

unsigned char *qname = &buf[sizeof(struct DNS_QUERY_HEADER)];
char *htrim = strtok(host_buf, ".");

while (htrim != NULL) {
    size_t label_len = strlen(htrim);
    if (label_len > 63) {
        // DNS标签长度不能超过63字节,这里可以加错误处理
        break;
    }
    // 写入标签长度(单字节,无需字节序转换)
    *qname++ = (unsigned char)label_len;
    // 写入标签内容
    memcpy(qname, htrim, label_len);
    qname += label_len;
    htrim = strtok(NULL, ".");
}
// 写入QNAME结束符(0x00)
*qname++ = 0x00;

// 后续写入查询类型和类别(例如A记录,IN类)
uint16_t qtype = htons(1); // A记录
uint16_t qclass = htons(1); // IN类
memcpy(qname, &qtype, sizeof(qtype));
qname += sizeof(qtype);
memcpy(qname, &qclass, sizeof(qclass));
qname += sizeof(qclass);

额外注意事项

  • 域名长度限制:整个QNAME(包括所有标签和分隔的长度字节)不能超过255字节,实际代码中可以添加长度检查,避免缓冲区溢出。
  • 缓冲区安全:使用strncpy/strncat代替strcpy/strcat,防止缓冲区溢出。
  • 字节序:查询类型和类别是16位字段,必须用htons转换为网络字节序,这部分你之前的代码是正确的。

内容的提问来源于stack exchange,提问作者laramruma

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 21:15:04