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
相关产品推荐
相关产品推荐

