wm: dnsparser

ref: 16d016b87e7783cecedb1078c1327fa1244da4d7
dir: /dns.c/

View raw version
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <pcap.h>
#include "common.h"

int
parseDnsName(const u_char *pkt, char *buf, u_char len)
{
	int section_header, i;
	
	memset(buf, '\0', len);
	
	/* skip first len byte, we can't replace it with '.' */
	strncpy(buf, pkt+1, len);
	for(i = 0 ; i < len ; i++)
	{
		section_header = pkt[i];
		
		if(section_header == 0x0 || section_header == 0xc0)
			break;
		else
		{
				i += section_header;
				buf[i] = '.';
		}
	}
	
	buf[len] = '\0';
	return 1;
}

/* len must be dns.len */
int
parseDnsCname(const u_char *pkt, Dns *d, uint len)
{
	int section_header;
	const u_char *pkt_head;
	u_char next_header, *cname_head, namebuf[DNS_MAX_LEN];
	
	pkt_head = pkt + pos;
	cname_head = d->cname;
	for(int i = 0 ; i < len ; i++)
	{
		section_header = pkt_head[i];
				
		/* compressed */
		if(section_header == 0xc0)
		{
			next_header = pkt_head[i + 1];
			parseDnsName(pkt + d->start + next_header, namebuf, DNS_MAX_LEN);
			cname_head = strchr(cname_head, '\0');
			*cname_head++ = '.';
			strncpy(cname_head, namebuf, DNS_MAX_LEN - (cname_head - d->cname));
			break;
		}
		else if(section_header == 0x0)
			break;
		else
		{
			parseDnsName(pkt_head + i, namebuf, section_header);
			cname_head = strchr(cname_head, '\0');
			*cname_head++ = '.';
			strncpy(cname_head, namebuf, section_header);
		}
		i += section_header;
	}
	
	return 1;
}

/*
 * dns have some variable length fields,
 * we need to keep our positon somewhere
 */
int
parseDns(const u_char *pkt, Dns *dns)
{
	dns->start = pos;
	get(pkt, DNS_ID, &dns->id);
	get(pkt, DNS_FLAGS, &dns->flags);
	
	get(pkt, DNS_COUNT_QUERIES, &dns->nQueries);
	get(pkt, DNS_COUNT_ANSWERS, &dns->nAnswers);
	get(pkt, DNS_COUNT_AUTH_RR, &dns->nAuthRR);
	get(pkt, DNS_COUNT_ADD_RR, &dns->nAddRR);
	
	parseDnsName(PKTHEAD, dns->domain, DNS_MAX_LEN);
	/* first byte isn't counted. */
	pos += strnlen(dns->domain, DNS_MAX_LEN) + 1;
	get(pkt, DNS_TYPE, &dns->type);
	get(pkt, DNS_CLASS, &dns->class);
		
	if(dns->flags & DNS_ISRESP)
	{
		/* a resposnse, should have answers */
		pos += DNS_BACKREF; /* skip domain name */
		get(pkt, DNS_TYPE, &dns->type);
		
		pos += DNS_CLASS; /* skip class */
		
		/* we are in next packet */
		get(pkt, DNS_TTL, &dns->ttl);
		get(pkt, DNS_LEN, &dns->len);
	}
	else
		return 1;
	
	switch(dns->type)
	{
		case DNS_TYPE_A:
			dns->ip[0] = pkt[pos++];
			dns->ip[1] = pkt[pos++];
			dns->ip[2] = pkt[pos++];
			dns->ip[3] = pkt[pos++];
			break; 
		case DNS_TYPE_AAAA:
			get(pkt, 2, dns->ip6+0);
			get(pkt, 2, dns->ip6+1);
			get(pkt, 2, dns->ip6+2);
			get(pkt, 2, dns->ip6+3);
			get(pkt, 2, dns->ip6+4);
			get(pkt, 2, dns->ip6+5);
			get(pkt, 2, dns->ip6+6);
			get(pkt, 2, dns->ip6+7);
			break;
		case DNS_TYPE_SOA:	
		case DNS_TYPE_CNAME:
			memset(dns->cname, '\0', DNS_MAX_LEN);
			parseDnsCname(pkt, dns, dns->len);
			break;
		case DNS_TYPE_MX:
			break;
		default:
			err(1, "parseDns(): unknown type: %d", dns->type);
	}
	return 1;
}

void
printDns(Dns dns)
{
	printf("dns query:\n"
	"\tid: %d\tflags: %b (%x)\n"
	"\tnQueries: %d\tnAnswers: %d\tnAuthRR: %d\tnAddRR: %d\n"
	"\tdomain: %s (%p)\n\n"
	"\ttype: %d (%s)\tclass: %s\n"
	"\tttl: %x (%d)\tlen: %x (%d)\n",
	
	dns.id, dns.flags, dns.flags,
	dns.nQueries, dns.nAnswers, dns.nAuthRR, dns.nAddRR,
	dns.domain, dns.domain,
	dns.type, dnsTypeToStr(dns.type), dnsClassToStr(dns.class),
	dns.ttl, dns.ttl, dns.len, dns.len);
	
	if(dns.flags & DNS_ISRESP)
	{
		switch(dns.type)
		{
		case DNS_TYPE_A:
			printf("\tip: %d.%d.%d.%d\n",
			dns.ip[0], dns.ip[1], dns.ip[2], dns.ip[3]);
			break;
		case DNS_TYPE_AAAA:
			printf("\tip6: %x:%x:%x:%x:%x:%x:%x:%x\n",
			dns.ip6[0], dns.ip6[1], dns.ip6[2], dns.ip6[3],
			dns.ip6[4], dns.ip6[5], dns.ip6[6], dns.ip6[7]);
			break;
		case DNS_TYPE_CNAME:
			printf("\tcname: %s\n", dns.cname);
			break;
		case DNS_TYPE_SOA:	
			printf("\tsoa: %s\n", dns.cname);
			break;	
		case DNS_TYPE_MX:
			break;
		default:
			break;
		}
	}
}

char*
dnsClassToStr(uint16_t class)
{
	switch(class)
	{
		case DNS_CLASS_IN:
			return "IN";
		default:
			err(1, "dnsClassToStr(%d): unkown class", class);
			return NULL;
	}
}

char*
dnsTypeToStr(uint16_t type)
{
	switch(type)
	{
		case DNS_TYPE_A:
			return "A";
		case DNS_TYPE_CNAME:
			return "CNAME";
		case DNS_TYPE_SOA:
			return "SOA";
		case DNS_TYPE_TXT:
			return "TXT";
		case DNS_TYPE_AAAA:
			return "AAAA";
		case DNS_TYPE_MX:
			return "MX";
		default:
			err(1, "dnsTypeToStr(%d): unkown type", type);
			return NULL;
	}
}

Parser dnsParser = {
	.name = "dns",
	.parse = parseDns,
	.print = printDns,
};