把nDpi的DNS协议解析部分拿出来看看,写了一些注释。使用的是nDpi的1.6版本,该版本下的文件结构比较简单,协议解析的代码都在 src/lib/protocols目录下。dns的解析代码自然就是dns.c了。nDpi的协议解析代码基本有着相同的结构,协议解析的入口函数一般定义为 ndpi_search_###。比如DNS的入口函数就是ndpi_search_dns()。
这个文件下还有几个简单的函数:
// 一个递归的函数来计算dns中域名字段占据报文的长度
static u_int getNameLength
//用来计算ip地址的,
static char* ndpi_intoa_v4
// 用报文中得到一个16字节的整数
static u_int16_t get16
// dns 的入口函数
void ndpi_search_dns(struct ndpi_detection_module_struct *ndpi_struct, struct ndpi_flow_struct *flow)
{
struct ndpi_packet_struct *packet = &flow->packet; // 定义包接口
u_int16_t dport = 0, sport = 0;
#define NDPI_MAX_DNS_REQUESTS 16
NDPI_LOG(NDPI_PROTOCOL_DNS, ndpi_struct, NDPI_LOG_DEBUG, "search DNS.\n"); // 打日志
// 根据使用的是tcp还是udp,获取端口号
if (packet->udp != NULL) {
sport = ntohs(packet->udp->source), dport = ntohs(packet->udp->dest);
NDPI_LOG(NDPI_PROTOCOL_DNS, ndpi_struct, NDPI_LOG_DEBUG, "calculated dport over UDP.\n");
} else if(packet->tcp != NULL) {
sport = ntohs(packet->tcp->source), dport = ntohs(packet->tcp->dest);
NDPI_LOG(NDPI_PROTOCOL_DNS, ndpi_struct, NDPI_LOG_DEBUG, "calculated dport over tcp.\n");
}
// 如果端口包含53 可以确定为dns协议。否则退出整个函数。并且有数据内容。这个if一直到函数的结尾了
if(((dport == 53) || (sport == 53) || (dport == 5355))
&& (packet->payload_packet_len > sizeof(struct dns_packet_header))) {
int i = packet->tcp ? 2 : 0; // 一般dns协议使用的是udp协议。DNS服务器之间才会使用tcp。如果是tcp协议的话,需要偏移两个字节,??为什么
struct dns_packet_header header, *dns = (struct dns_packet_header*)&packet->payload[i];
u_int8_t is_query, ret_code, is_dns = 0;
u_int32_t a_record[NDPI_MAX_DNS_REQUESTS] = { 0 }, query_offset, num_a_records = 0;
// 获取dns协议的头部,将网络字节序转换为本地字节序
header.flags = ntohs(dns->flags);
header.transaction_id = ntohs(dns->transaction_id);
header.num_queries = ntohs(dns->num_queries);
header.answer_rrs = ntohs(dns->answer_rrs);
header.authority_rrs = ntohs(dns->authority_rrs);
header.additional_rrs = ntohs(dns->additional_rrs);
is_query = (header.flags & 0x8000) ? 0 : 1; // 从flags的第一位获取dns是查询报文还是响应报文
ret_code = is_query ? 0 : (header.flags & 0x0F); // 获取dns的flags字段的rcode,0表示没有差错,3表示名字差错(域名不存在)
i += sizeof(struct dns_packet_header); // 偏移值移动dns头部的长度
query_offset = i; // 设置偏移值
// 如果是一个查询报文
if(is_query) {
/* DNS Request */
if((header.num_queries > 0) && (header.num_queries <= NDPI_MAX_DNS_REQUESTS)
&& (((header.flags & 0x2800) == 0x2800 /* Dynamic DNS Update */)
|| ((header.answer_rrs == 0) && (header.authority_rrs == 0)))) {
/* This is a good query */ // dns 动态更新?开来查询报文分两种,一种是一般的dns请求,一种是dns服务器的动态更新。0x2800,opcode是5,不知道是什么
is_dns = 1;
if(header.num_queries > 0) { // 循环处理多个查询报文,并没有处理具体的查询内容,只是走了一个统计吗?
while(i < packet->payload_packet_len) {
if(packet->payload[i] == '\0') {
i++;
flow->protos.dns.query_type = get16(&i, packet->payload); // 获取了查询类型, flow的结构体是在是太强大了,好大。在ndpi_typedefs.h里定义的。
break;
} else
i++;
}
}
}
}// 查询包处理结束
else { // 如果包是一个响应包
/* DNS Reply */
flow->server_id = flow->dst;
if((header.num_queries <= NDPI_MAX_DNS_REQUESTS) /* Don't assume that num_queries must be zero */
&& (((header.answer_rrs > 0) && (header.answer_rrs <= NDPI_MAX_DNS_REQUESTS))
|| ((header.authority_rrs > 0) && (header.authority_rrs <= NDPI_MAX_DNS_REQUESTS))
|| ((header.additional_rrs > 0) && (header.additional_rrs <= NDPI_MAX_DNS_REQUESTS)))
) {
/* This is a good reply */
is_dns = 1;
i++;
// 越过查询问题字段的查询名称
if(packet->payload[i] != '\0') {
while((i < packet->payload_packet_len)
&& (packet->payload[i] != '\0')) {
i++;
}
i++;
}
i += 4; // 越过查询问题字段的查询类型和查询类
// 如果有回答报文,处理回答报文字段
if(header.answer_rrs > 0) {
u_int16_t rsp_type /*, rsp_class */;
u_int16_t num;
for(num = 0; num < header.answer_rrs; num++) { // 处理每一个回答报文
u_int16_t data_len;
if((i+6) >= packet->payload_packet_len) { // 包长度的合理性判断
break;
}
if((data_len = getNameLength(i, packet->payload, packet->payload_packet_len)) == 0) { // 包合法性检查,看看包的回答字段的域名是否合理
break;
} else
i += data_len;
rsp_type = get16(&i, packet->payload); // 得到第num个回答的查询类型
// rsp_class = get16(&i, packet->payload);
i += 4; // 这里应该是+8吧,要不下面的就不正确了,没有考虑生存时间字段的4个字节
data_len = get16(&i, packet->payload);
if((data_len <= 1) || (data_len > (packet->payload_packet_len-i))) {
break;
}
flow->protos.dns.rsp_type = rsp_type;
if(rsp_type == 1 /* A */) { // 如果是A类,那么就是一个4字节的ip地址
if(data_len == 4) {
u_int32_t v = ntohl(*((u_int32_t*)&packet->payload[i]));
if(num_a_records < (NDPI_MAX_DNS_REQUESTS-1)) // 做记录
a_record[num_a_records++] = v;
else
break; /* One record is enough */ // 明明是16个记录
}
}
if(data_len == 0) {
break;
}
i += data_len;
} /* for */
}
} // 正确的响应包处理结束
if((header.num_queries <= NDPI_MAX_DNS_REQUESTS) // 响应包的另外一种正确形式
&& ((header.answer_rrs == 0)
|| (header.authority_rrs == 0)
|| (header.additional_rrs == 0))
&& (ret_code != 0 /* 0 == OK */)
) {
/* This is a good reply */
is_dns = 1;
}
} //响应包处理结束
if(is_dns) {
int j = 0;
flow->protos.dns.num_queries = (u_int8_t)header.num_queries,
flow->protos.dns.num_answers = (u_int8_t)(header.answer_rrs+header.authority_rrs+header.additional_rrs),
flow->protos.dns.ret_code = ret_code;
i = query_offset+1;
// 下面是一些数据的统计功能了,全部放到flow里面。
while((i < packet->payload_packet_len)
&& (j < (sizeof(flow->host_server_name)-1))
&& (packet->payload[i] != '\0')) {
flow->host_server_name[j] = tolower(packet->payload[i]);
if(flow->host_server_name[j] < ' ')
flow->host_server_name[j] = '.';
j++, i++;
}
if(a_record[0] != 0) {
char a_buf[32];
int i;
for(i=0; i*)&flow->host_server_name[j], sizeof(flow->host_server_name)-1-j, "%s%s",
(i == 0) ? "@" : ";",
ndpi_intoa_v4(a_record[i], a_buf, sizeof(a_buf)));
}
}
flow->host_server_name[j] = '\0';
if(j > 0) {
#ifdef DEBUG
printf("==> %s\n", flow->host_server_name);
#endif
if(ndpi_struct->match_dns_host_names)
ndpi_match_string_subprotocol(ndpi_struct, flow,
(char *)flow->host_server_name,
strlen((const char*)flow->host_server_name));
}
i++;
memcpy(&flow->protos.dns.query_type, &packet->payload[i], 2);
flow->protos.dns.query_type = ntohs(flow->protos.dns.query_type), i += 2;
memcpy(&flow->protos.dns.query_class, &packet->payload[i], 2);
flow->protos.dns.query_class = ntohs(flow->protos.dns.query_class), i += 2;
#ifdef DEBUG
printf("%s [type=%04X][class=%04X]\n", flow->host_server_name, flow->protos.dns.query_type, flow->protos.dns.query_class);
#endif
if(packet->detected_protocol_stack[0] == NDPI_PROTOCOL_UNKNOWN) {
/*
Do not set the protocol with DNS if ndpi_match_string_subprotocol() has
matched a subprotocol
*/
NDPI_LOG(NDPI_PROTOCOL_DNS, ndpi_struct, NDPI_LOG_DEBUG, "found DNS.\n");
ndpi_int_add_connection(ndpi_struct, flow, (dport == 5355) ? NDPI_PROTOCOL_LLMNR : NDPI_PROTOCOL_DNS, NDPI_REAL_PROTOCOL);
}
} else {
flow->protos.dns.bad_packet = 1;
NDPI_LOG(NDPI_PROTOCOL_DNS, ndpi_struct, NDPI_LOG_DEBUG, "exclude DNS.\n");
NDPI_ADD_PROTOCOL_TO_BITMASK(flow->excluded_protocol_bitmask, NDPI_PROTOCOL_DNS);
}
}
}