dns.h
#ifndef _DNS_H_
#define _DNS_H_
#define DBG_PRINT 0
#define DNS_PORT 53
#define MAX_DOMAIN 256
#define DNS_TYPE_A 1 //ipv4
#define DNS_TYPE_CNAME 5 //cname
#define DNS_TYPE_AAAA 28 //ipv6
#define DNS_MAX_IPNUM 10
typedef struct{
unsigned short id;
unsigned short flags;
unsigned short qry_cnt;
unsigned short ans_cnt;
unsigned short auth_cnt;
unsigned short add_cnt;
}dns_hdr;
int dns_parse(Packet *p);
int dns_parse_query(Flow* f, unsigned char* pdata, unsigned short int pktlen, dns_hdr dnsh);
int dns_parse_response(Flow* f, unsigned char* pdata, unsigned short int pktlen, dns_hdr dnsh);
dns.c
#include "dns.h"
#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>
int dns_parse(Packet *p)
{
unsigned char* pdata = p->payload;
unsigned short int pktlen = p->payload_len;
Flow* f = p->flow;
int ret;
dns_hdr dnsh;
unsigned char qrtype;
unsigned char opcode;
unsigned char zflag;
unsigned char rcode;
if (f == NULL)
{
if (DBG_PRINT) printf("The Dns Packet has no flow!\n");
return -1;
}
if (pktlen < 20)
{
if (DBG_PRINT) printf("The Dns Packet is too small!\n");
return -1;
}
dnsh.id = ntohs(*((unsigned short *)(pdata)));
dnsh.flags = ntohs(*((unsigned short *)(pdata + 2)));
dnsh.qry_cnt = ntohs(*((unsigned short *)(pdata + 4)));
dnsh.ans_cnt = ntohs(*((unsigned short *)(pdata + 6)));
qrtype = dnsh.flags & 0x8000;
opcode = dnsh.flags & 0x7800;
zflag = dnsh.flags & 0x0040;
rcode = dnsh.flags & 0x000f;
if (opcode != 0)
{
if (DBG_PRINT) printf("The Dns Packet is not a standard query!\n");
return -1;
}
if (zflag ! = 0)
{
if (DBG_PRINT) printf("The Dns Packet Z flag is not zero!\n");
return -1;
}
if (qrtype == 0) //dns request
{
ret = dns_parse_query(f, pdata, pktlen, dnsh);
}
if (qrtype == 1 && rcode == 0) //dns response
{
ret = dns_parse_response(f, pdata, pktlen, dnsh);
}
return ret;
}
int dns_parse_query(Flow* f, unsigned char* pdata, unsigned short int pktlen, dns_hdr dnsh)
{
unsigned char* pquery;
char domain[MAX_DOMAIN];
unsigned short int qrylen = 0;
unsigned short int offset = 0;
unsigned char dmlen = 0;
unsigned short int qry_type = 0;
unsigned short int qry_class = 0;
memset(domain, 0 , MAX_DOMAIN);
pquery = pdata + 12; // 12 is dns_hdr length
qrylen = qrylen + 12;
if (dnsh.qry_cnt != 1)
{
if (DBG_PRINT) printf("The Dns Packet has too many querys!\n");
return -1;
}
/* parse dns request domain name */
while ((*pquery != 0) && (qrylen < pktlen))
{
if (*pquery > 63)
{
if (DBG_PRINT) printf("The Dns Packet domain length must lowwer than 63!\n");
return -1;
}
dmlen = *pquery;
if (qrylen + dmlen + 1 > pktlen)
{
if (DBG_PRINT) printf("The Dns Packet domain length too long!\n");
return -1;
}
pquery++;
qrylen++;
if ((offset + dmlen + 1) < MAX_DOMAIN)
{
memcpy(domain + offset, pquery, dmlen);
offset += dmlen;
domain[offset++] = '.';
}
else
{
if (DBG_PRINT) printf("The Dns Packet domain length must lowwer than 255!\n");
return -1;
}
pquery += dmlen;
qrylen += dmlen;
}
domain[offset - 1] = '\0';
pquery++;
qrylen++;
if (qrylen + 4 > pktlen)
{
if (DBG_PRINT) printf("The Dns Packet domain has no type and class!\n");
return -1;
}
/* parse dns request type and class */
qry_type = ntohs(*((unsigned short *)(pquery)));
qry_class = ntohs(*((unsigned short *)(pquery + 2)));
if (qry_type != 1 || qry_class != 1)
{
if (DBG_PRINT) printf("The Dns Packet type or class is wrong!\n");
return -1;
}
/* store dns request state in Flow node, we can check the state with response packet */
f->dns_id = dnsh.id;
f->dns_domain = (unsigned char *)malloc(MAX_DOMAIN);
memcpy(f->dns_domain, domain, offset);
f->dns_dmlen = offset;
return 0;
}
int dns_parse_response(Flow* f, unsigned char* pdata, unsigned short int pktlen, dns_hdr dnsh)
{
unsigned char* pquery;
char domain[MAX_DOMAIN];
unsigned short int qrylen = 0;
unsigned short int offset = 0;
unsigned char dmlen = 0;
unsigned short int qry_type = 0;
unsigned short int qry_class = 0;
unsigned short int q = 0;
unsigned short int ans_type = 0;
unsigned short int ans_class = 0;
unsigned int ans_ttl = 0;
unsigned short int ans_len = 0;
if (dnsh.id != f->dns_id)
{
if (DBG_PRINT) printf("The Dns request txid is different with response txid!\n");
return -1;
}
memset(domain, 0 , MAX_DOMAIN);
pquery = pdata + 12; // 12 is dns_hdr length
qrylen = qrylen + 12;
if (dnsh.qry_cnt != 1)
{
if (DBG_PRINT) printf("The Dns Packet has too many querys!\n");
return -1;
}
/* parse dns request domain name */
while ((*pquery != 0) && (qrylen < pktlen))
{
if (*pquery > 63)
{
if (DBG_PRINT) printf("The Dns Packet domain length must lowwer than 63!\n");
return -1;
}
dmlen = *pquery;
if (qrylen + dmlen + 1 > pktlen)
{
if (DBG_PRINT) printf("The Dns Packet domain length too long!\n");
return -1;
}
pquery++;
qrylen++;
if ((offset + dmlen + 1) < MAX_DOMAIN)
{
memcpy(domain + offset, pquery, dmlen);
offset += dmlen;
domain[offset++] = '.';
}
else
{
if (DBG_PRINT) printf("The Dns Packet domain length must lowwer than 255!\n");
return -1;
}
pquery += dmlen;
qrylen += dmlen;
}
domain[offset - 1] = '\0';
pquery++;
qrylen++;
if (qrylen + 4 > pktlen)
{
if (DBG_PRINT) printf("The Dns Packet domain has no type and class!\n");
return -1;
}
/* parse dns request type and class */
qry_type = ntohs(*((unsigned short *)(pquery)));
qry_class = ntohs(*((unsigned short *)(pquery + 2)));
if (qry_type != 1 || qry_class != 1)
{
if (DBG_PRINT) printf("The Dns Packet type or class is wrong!\n");
return -1;
}
pquery += 4;
qrylen += 4;
/* parse dns response domain IP address */
for (q = 0; q < dnsh.ans_cnt && q < DNS_MAX_IPNUM; q++)
{
if (qrylen + 2 > pktlen)
{
if (DBG_PRINT) printf("The Dns Packet response answer domain name is too small!\n");
return -1;
}
if (*pquery != 0xc0))
{
if (DBG_PRINT) printf("The Dns Packet response answer domain name is wrong!\n");
return -1;
}
pquery += 2;
qrylen += 2;
if (qrylen + 10 > pktlen)
{
if (DBG_PRINT) printf("The Dns Packet response answer has no RR header!\n");
return -1;
}
ans_type = ntohs(*((unsigned short *)(pquery)));
ans_class = ntohs(*((unsigned short *)(pquery + 2)));
ans_ttl = ntohl(*((unsigned int *)(pquery + 4)));
ans_len = ntohs(*((unsigned short *)(pquery + 8)));
if (ans_class != 1)
{
if (DBG_PRINT) printf("The Dns Packet response class is wrong!\n");
return -1;
}
pquery += 10;
qrylen += 10;
if (qrylen + ans_len > pktlen)
{
if (DBG_PRINT) printf("The Dns Packet response answer RR length is too small!\n");
return -1;
}
if (ans_type == 1 && ans_len == 4)
{
f->dns_ip[q] = ntohl(*((unsigned int *)(pquery)));
}
pquery += ans_len;
qrylen += ans_len;
}
return 0;
}