xref: /openbmc/u-boot/net/dns.c (revision 5794619e)
1 /*
2  * DNS support driver
3  *
4  * Copyright (c) 2008 Pieter Voorthuijsen <pieter.voorthuijsen@prodrive.nl>
5  * Copyright (c) 2009 Robin Getz <rgetz@blackfin.uclinux.org>
6  *
7  * This is a simple DNS implementation for U-Boot. It will use the first IP
8  * in the DNS response as NetServerIP. This can then be used for any other
9  * network related activities.
10  *
11  * The packet handling is partly based on TADNS, original copyrights
12  * follow below.
13  *
14  */
15 
16 /*
17  * Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
18  *
19  * "THE BEER-WARE LICENSE" (Revision 42):
20  * Sergey Lyubka wrote this file.  As long as you retain this notice you
21  * can do whatever you want with this stuff. If we meet some day, and you think
22  * this stuff is worth it, you can buy me a beer in return.
23  */
24 
25 #include <common.h>
26 #include <command.h>
27 #include <net.h>
28 
29 #include "dns.h"
30 
31 char *NetDNSResolve;	/* The host to resolve  */
32 char *NetDNSenvvar;	/* The envvar to store the answer in */
33 
34 static int DnsOurPort;
35 
36 static void
37 DnsSend(void)
38 {
39 	struct header *header;
40 	int n, name_len;
41 	uchar *p, *pkt;
42 	const char *s;
43 	const char *name;
44 	enum dns_query_type qtype = DNS_A_RECORD;
45 
46 	name = NetDNSResolve;
47 	pkt = p = (uchar *)(NetTxPacket + NetEthHdrSize() + IP_HDR_SIZE);
48 
49 	/* Prepare DNS packet header */
50 	header           = (struct header *) pkt;
51 	header->tid      = 1;
52 	header->flags    = htons(0x100);	/* standard query */
53 	header->nqueries = htons(1);		/* Just one query */
54 	header->nanswers = 0;
55 	header->nauth    = 0;
56 	header->nother   = 0;
57 
58 	/* Encode DNS name */
59 	name_len = strlen(name);
60 	p = (uchar *) &header->data;	/* For encoding host name into packet */
61 
62 	do {
63 		s = strchr(name, '.');
64 		if (!s)
65 			s = name + name_len;
66 
67 		n = s - name;			/* Chunk length */
68 		*p++ = n;			/* Copy length  */
69 		memcpy(p, name, n);		/* Copy chunk   */
70 		p += n;
71 
72 		if (*s == '.')
73 			n++;
74 
75 		name += n;
76 		name_len -= n;
77 	} while (*s != '\0');
78 
79 	*p++ = 0;			/* Mark end of host name */
80 	*p++ = 0;			/* Some servers require double null */
81 	*p++ = (unsigned char) qtype;	/* Query Type */
82 
83 	*p++ = 0;
84 	*p++ = 1;				/* Class: inet, 0x0001 */
85 
86 	n = p - pkt;				/* Total packet length */
87 	debug("Packet size %d\n", n);
88 
89 	DnsOurPort = random_port();
90 
91 	NetSendUDPPacket(NetServerEther, NetOurDNSIP, DNS_SERVICE_PORT,
92 		DnsOurPort, n);
93 	debug("DNS packet sent\n");
94 }
95 
96 static void
97 DnsTimeout(void)
98 {
99 	puts("Timeout\n");
100 	NetState = NETLOOP_FAIL;
101 }
102 
103 static void
104 DnsHandler(uchar *pkt, unsigned dest, unsigned src, unsigned len)
105 {
106 	struct header *header;
107 	const unsigned char *p, *e, *s;
108 	u16 type, i;
109 	int found, stop, dlen;
110 	char IPStr[22];
111 	IPaddr_t IPAddress;
112 	short tmp;
113 
114 
115 	debug("%s\n", __func__);
116 	if (dest != DnsOurPort)
117 		return;
118 
119 	for (i = 0; i < len; i += 4)
120 		debug("0x%p - 0x%.2x  0x%.2x  0x%.2x  0x%.2x\n",
121 			pkt+i, pkt[i], pkt[i+1], pkt[i+2], pkt[i+3]);
122 
123 	/* We sent 1 query. We want to see more that 1 answer. */
124 	header = (struct header *) pkt;
125 	if (ntohs(header->nqueries) != 1)
126 		return;
127 
128 	/* Received 0 answers */
129 	if (header->nanswers == 0) {
130 		puts("DNS server returned no answers\n");
131 		NetState = NETLOOP_SUCCESS;
132 		return;
133 	}
134 
135 	/* Skip host name */
136 	s = &header->data[0];
137 	e = pkt + len;
138 	for (p = s; p < e && *p != '\0'; p++)
139 		continue;
140 
141 	/* We sent query class 1, query type 1 */
142 	tmp = p[1] | (p[2] << 8);
143 	if (&p[5] > e || ntohs(tmp) != DNS_A_RECORD) {
144 		puts("DNS response was not A record\n");
145 		NetState = NETLOOP_SUCCESS;
146 		return;
147 	}
148 
149 	/* Go to the first answer section */
150 	p += 5;
151 
152 	/* Loop through the answers, we want A type answer */
153 	for (found = stop = 0; !stop && &p[12] < e; ) {
154 
155 		/* Skip possible name in CNAME answer */
156 		if (*p != 0xc0) {
157 			while (*p && &p[12] < e)
158 				p++;
159 			p--;
160 		}
161 		debug("Name (Offset in header): %d\n", p[1]);
162 
163 		tmp = p[2] | (p[3] << 8);
164 		type = ntohs(tmp);
165 		debug("type = %d\n", type);
166 		if (type == DNS_CNAME_RECORD) {
167 			/* CNAME answer. shift to the next section */
168 			debug("Found canonical name\n");
169 			tmp = p[10] | (p[11] << 8);
170 			dlen = ntohs(tmp);
171 			debug("dlen = %d\n", dlen);
172 			p += 12 + dlen;
173 		} else if (type == DNS_A_RECORD) {
174 			debug("Found A-record\n");
175 			found = stop = 1;
176 		} else {
177 			debug("Unknown type\n");
178 			stop = 1;
179 		}
180 	}
181 
182 	if (found && &p[12] < e) {
183 
184 		tmp = p[10] | (p[11] << 8);
185 		dlen = ntohs(tmp);
186 		p += 12;
187 		memcpy(&IPAddress, p, 4);
188 
189 		if (p + dlen <= e) {
190 			ip_to_string(IPAddress, IPStr);
191 			printf("%s\n", IPStr);
192 			if (NetDNSenvvar)
193 				setenv(NetDNSenvvar, IPStr);
194 		} else
195 			puts("server responded with invalid IP number\n");
196 	}
197 
198 	NetState = NETLOOP_SUCCESS;
199 }
200 
201 void
202 DnsStart(void)
203 {
204 	debug("%s\n", __func__);
205 
206 	NetSetTimeout(DNS_TIMEOUT, DnsTimeout);
207 	NetSetHandler(DnsHandler);
208 
209 	DnsSend();
210 }
211