diff options
Diffstat (limited to 'net/dns.c')
| -rw-r--r-- | net/dns.c | 211 | 
1 files changed, 211 insertions, 0 deletions
| diff --git a/net/dns.c b/net/dns.c new file mode 100644 index 000000000..f25c3f8c2 --- /dev/null +++ b/net/dns.c @@ -0,0 +1,211 @@ +/* + * DNS support driver + * + * Copyright (c) 2008 Pieter Voorthuijsen <pieter.voorthuijsen@prodrive.nl> + * Copyright (c) 2009 Robin Getz <rgetz@blackfin.uclinux.org> + * + * This is a simple DNS implementation for U-Boot. It will use the first IP + * in the DNS response as NetServerIP. This can then be used for any other + * network related activities. + * + * The packet handling is partly based on TADNS, original copyrights + * follow below. + * + */ + +/* + * Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com> + * + * "THE BEER-WARE LICENSE" (Revision 42): + * Sergey Lyubka wrote this file.  As long as you retain this notice you + * can do whatever you want with this stuff. If we meet some day, and you think + * this stuff is worth it, you can buy me a beer in return. + */ + +#include <common.h> +#include <command.h> +#include <net.h> + +#include "dns.h" + +char *NetDNSResolve;	/* The host to resolve  */ +char *NetDNSenvvar;	/* The envvar to store the answer in */ + +static int DnsOurPort; + +static void +DnsSend(void) +{ +	struct header *header; +	int n, name_len; +	uchar *p, *pkt; +	const char *s; +	const char *name; +	enum dns_query_type qtype = DNS_A_RECORD; + +	name = NetDNSResolve; +	pkt = p = (uchar *)(NetTxPacket + NetEthHdrSize() + IP_HDR_SIZE); + +	/* Prepare DNS packet header */ +	header           = (struct header *) pkt; +	header->tid      = 1; +	header->flags    = htons(0x100);	/* standard query */ +	header->nqueries = htons(1);		/* Just one query */ +	header->nanswers = 0; +	header->nauth    = 0; +	header->nother   = 0; + +	/* Encode DNS name */ +	name_len = strlen(name); +	p = (uchar *) &header->data;	/* For encoding host name into packet */ + +	do { +		s = strchr(name, '.'); +		if (!s) +			s = name + name_len; + +		n = s - name;			/* Chunk length */ +		*p++ = n;			/* Copy length  */ +		memcpy(p, name, n);		/* Copy chunk   */ +		p += n; + +		if (*s == '.') +			n++; + +		name += n; +		name_len -= n; +	} while (*s != '\0'); + +	*p++ = 0;			/* Mark end of host name */ +	*p++ = 0;			/* Some servers require double null */ +	*p++ = (unsigned char) qtype;	/* Query Type */ + +	*p++ = 0; +	*p++ = 1;				/* Class: inet, 0x0001 */ + +	n = p - pkt;				/* Total packet length */ +	debug("Packet size %d\n", n); + +	DnsOurPort = random_port(); + +	NetSendUDPPacket(NetServerEther, NetOurDNSIP, DNS_SERVICE_PORT, +		DnsOurPort, n); +	debug("DNS packet sent\n"); +} + +static void +DnsTimeout(void) +{ +	puts("Timeout\n"); +	NetState = NETLOOP_FAIL; +} + +static void +DnsHandler(uchar *pkt, unsigned dest, unsigned src, unsigned len) +{ +	struct header *header; +	const unsigned char *p, *e, *s; +	u16 type, i; +	int found, stop, dlen; +	char IPStr[22]; +	IPaddr_t IPAddress; +	short tmp; + + +	debug("%s\n", __func__); +	if (dest != DnsOurPort) +		return; + +	for (i = 0; i < len; i += 4) +		debug("0x%p - 0x%.2x  0x%.2x  0x%.2x  0x%.2x\n", +			pkt+i, pkt[i], pkt[i+1], pkt[i+2], pkt[i+3]); + +	/* We sent 1 query. We want to see more that 1 answer. */ +	header = (struct header *) pkt; +	if (ntohs(header->nqueries) != 1) +		return; + +	/* Received 0 answers */ +	if (header->nanswers == 0) { +		puts("DNS server returned no answers\n"); +		NetState = NETLOOP_SUCCESS; +		return; +	} + +	/* Skip host name */ +	s = &header->data[0]; +	e = pkt + len; +	for (p = s; p < e && *p != '\0'; p++) +		continue; + +	/* We sent query class 1, query type 1 */ +	tmp = p[1] | (p[2] << 8); +	if (&p[5] > e || ntohs(tmp) != DNS_A_RECORD) { +		puts("DNS response was not A record\n"); +		NetState = NETLOOP_SUCCESS; +		return; +	} + +	/* Go to the first answer section */ +	p += 5; + +	/* Loop through the answers, we want A type answer */ +	for (found = stop = 0; !stop && &p[12] < e; ) { + +		/* Skip possible name in CNAME answer */ +		if (*p != 0xc0) { +			while (*p && &p[12] < e) +				p++; +			p--; +		} +		debug("Name (Offset in header): %d\n", p[1]); + +		tmp = p[2] | (p[3] << 8); +		type = ntohs(tmp); +		debug("type = %d\n", type); +		if (type == DNS_CNAME_RECORD) { +			/* CNAME answer. shift to the next section */ +			debug("Found canonical name\n"); +			tmp = p[10] | (p[11] << 8); +			dlen = ntohs(tmp); +			debug("dlen = %d\n", dlen); +			p += 12 + dlen; +		} else if (type == DNS_A_RECORD) { +			debug("Found A-record\n"); +			found = stop = 1; +		} else { +			debug("Unknown type\n"); +			stop = 1; +		} +	} + +	if (found && &p[12] < e) { + +		tmp = p[10] | (p[11] << 8); +		dlen = ntohs(tmp); +		p += 12; +		memcpy(&IPAddress, p, 4); + +		if (p + dlen <= e) { +			ip_to_string(IPAddress, IPStr); +			printf("%s\n", IPStr); +			if (NetDNSenvvar) +				setenv(NetDNSenvvar, IPStr); +		} else +			puts("server responded with invalid IP number\n"); +	} + +	NetState = NETLOOP_SUCCESS; +} + +void +DnsStart(void) +{ +	debug("%s\n", __func__); + +	NetSetTimeout(DNS_TIMEOUT, DnsTimeout); +	NetSetHandler(DnsHandler); + +	DnsSend(); +} + |