wm: dnsparser

ref: eea3e7f97185f0c6ac097bad8009f23eee9b7b20
dir: /dns.c/

View raw version
/*
 * notes:
 * we dont parse compressed names very well.
 */

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

/* 
 * pos must be after data length, before size of first string
 * len must be dns.len
 */
int
parseDnsCname(const u_char *pkt, u_char cname[255], uint pos, uint len)
{
	int section_header, cname_head = 0;
	const u_char *pkt_head;
	
	pkt_head = pkt + pos;
	for(int i = 0 ; i < len ; i++)
	{
		section_header = pkt_head[i];
		
		/* compressed, we'r too lazy to handle it */
		if(section_header == 0xc0 || section_header == 0x0)
			break;
		
		/* not compressed, regular size */
		strncpy(cname + cname_head, pkt_head + i + 1, section_header);
		
		i += section_header;
		cname_head += section_header;
		cname[cname_head++] = '.';
	}
	return 1;
}

/*
 * dns have some variable length fields,
 * we need to keep our positon somewhere
 */
int
parseDns(const u_char *pkt, Dns *dns)
{
	int section_header;
	int pos = 0;
	int name_head = 0;
	
	dns->id = get2(pkt + pos);
	pos += DNS_ID;
	
	dns->flags = get2(pkt + pos);
	pos += DNS_FLAGS;
	
	dns->nQueries = get2(pkt + pos);
	pos += DNS_COUNT_QUERIES;
	
	dns->nAnswers = get2(pkt + pos);
	pos += DNS_COUNT_ANSWERS;
	
	dns->nAuthRR = get2(pkt + pos);
	pos += DNS_COUNT_AUTH_RR;
	
	dns->nAddRR = get2(pkt + pos);
	pos += DNS_COUNT_ADD_RR;
	
	memset(dns->domain, '\0', sizeof(dns->domain));
	for(int i = 0 ; i < sizeof(dns->domain) ; i++)
	{
		section_header = pkt[pos];
		pos++;
		
		if(section_header == 0x0)
			break;
		
		strncpy(dns->domain + name_head, pkt + pos, section_header);
		name_head += section_header;
		dns->domain[name_head++] = '.';
		pos += section_header;
	}

	dns->type = get2(pkt + pos);
	pos += DNS_TYPE;
	dns->class = get2(pkt + pos);
	pos += DNS_CLASS;
		
	if(dns->flags & DNS_ISRESP)
	{
		/* a resposnse, should have answers */
		pos += DNS_BACKREF; /* skip domain name */
		pos += DNS_TYPE; /* skip type */
		pos += DNS_CLASS; /* skip class */
		
		/* we are in next packet */
		dns->ttl = get4(pkt + pos);
		pos += DNS_TTL;
		
		dns->len = get2(pkt + pos);
		pos += 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:
			dns->ip6[0] = get2(pkt + pos);
			pos += 2;		
			dns->ip6[1] = get2(pkt + pos);
			pos += 2;
			dns->ip6[2] = get2(pkt + pos);
			pos += 2;
			dns->ip6[3] = get2(pkt + pos);
			pos += 2;
			dns->ip6[4] = get2(pkt + pos);
			pos += 2;		
			dns->ip6[5] = get2(pkt + pos);
			pos += 2;
			dns->ip6[6] = get2(pkt + pos);
			pos += 2;
			dns->ip6[7] = get2(pkt + pos);
			pos += 2;			
			break;
		case DNS_TYPE_CNAME:
			memset(dns->cname, '\0', sizeof(dns->domain));
			parseDnsCname(pkt, dns->cname, pos, dns->len);
			break;
		case DNS_TYPE_MX:
			break;
		default:
			err(1, "parseDnsQuery(%d) unknown type", 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"
	"\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_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_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 = {
	.parse = parseDns,
	.print = printDns,
};