Merging upstream version 5.01+dfsg.
[syslinux-debian/hramrach.git] / core / fs / pxe / dnsresolv.c
blob184bacbdb69b2fcf56166312afd06258ddde89d5
1 #include <stdio.h>
2 #include <string.h>
3 #include <core.h>
4 #include "pxe.h"
6 /* DNS CLASS values we care about */
7 #define CLASS_IN 1
9 /* DNS TYPE values we care about */
10 #define TYPE_A 1
11 #define TYPE_CNAME 5
14 * The DNS header structure
16 struct dnshdr {
17 uint16_t id;
18 uint16_t flags;
19 /* number of entries in the question section */
20 uint16_t qdcount;
21 /* number of resource records in the answer section */
22 uint16_t ancount;
23 /* number of name server resource records in the authority records section*/
24 uint16_t nscount;
25 /* number of resource records in the additional records section */
26 uint16_t arcount;
27 } __attribute__ ((packed));
30 * The DNS query structure
32 struct dnsquery {
33 uint16_t qtype;
34 uint16_t qclass;
35 } __attribute__ ((packed));
38 * The DNS Resource recodes structure
40 struct dnsrr {
41 uint16_t type;
42 uint16_t class;
43 uint32_t ttl;
44 uint16_t rdlength; /* The lenght of this rr data */
45 char rdata[];
46 } __attribute__ ((packed));
49 uint32_t dns_server[DNS_MAX_SERVERS] = {0, };
53 * Turn a string in _src_ into a DNS "label set" in _dst_; returns the
54 * number of dots encountered. On return, *dst is updated.
56 int dns_mangle(char **dst, const char *p)
58 char *q = *dst;
59 char *count_ptr;
60 char c;
61 int dots = 0;
63 count_ptr = q;
64 *q++ = 0;
66 while (1) {
67 c = *p++;
68 if (c == 0 || c == ':' || c == '/')
69 break;
70 if (c == '.') {
71 dots++;
72 count_ptr = q;
73 *q++ = 0;
74 continue;
77 *count_ptr += 1;
78 *q++ = c;
81 if (*count_ptr)
82 *q++ = 0;
84 /* update the strings */
85 *dst = q;
86 return dots;
91 * Compare two sets of DNS labels, in _s1_ and _s2_; the one in _s2_
92 * is allowed pointers relative to a packet in buf.
95 static bool dns_compare(const void *s1, const void *s2, const void *buf)
97 const uint8_t *q = s1;
98 const uint8_t *p = s2;
99 unsigned int c0, c1;
101 while (1) {
102 c0 = p[0];
103 if (c0 >= 0xc0) {
104 /* Follow pointer */
105 c1 = p[1];
106 p = (const uint8_t *)buf + ((c0 - 0xc0) << 8) + c1;
107 } else if (c0) {
108 c0++; /* Include the length byte */
109 if (memcmp(q, p, c0))
110 return false;
111 q += c0;
112 p += c0;
113 } else {
114 return *q == 0;
120 * Copy a DNS label into a buffer, considering the possibility that we might
121 * have to follow pointers relative to "buf".
122 * Returns a pointer to the first free byte *after* the terminal null.
124 static void *dns_copylabel(void *dst, const void *src, const void *buf)
126 uint8_t *q = dst;
127 const uint8_t *p = src;
128 unsigned int c0, c1;
130 while (1) {
131 c0 = p[0];
132 if (c0 >= 0xc0) {
133 /* Follow pointer */
134 c1 = p[1];
135 p = (const uint8_t *)buf + ((c0 - 0xc0) << 8) + c1;
136 } else if (c0) {
137 c0++; /* Include the length byte */
138 memcpy(q, p, c0);
139 p += c0;
140 q += c0;
141 } else {
142 *q++ = 0;
143 return q;
149 * Skip past a DNS label set in DS:SI
151 static char *dns_skiplabel(char *label)
153 uint8_t c;
155 while (1) {
156 c = *label++;
157 if (c >= 0xc0)
158 return ++label; /* pointer is two bytes */
159 if (c == 0)
160 return label;
161 label += c;
166 * Actual resolver function
167 * Points to a null-terminated or :-terminated string in _name_
168 * and returns the ip addr in _ip_ if it exists and can be found.
169 * If _ip_ = 0 on exit, the lookup failed. _name_ will be updated
171 * XXX: probably need some caching here.
173 __export uint32_t dns_resolv(const char *name)
175 static char __lowmem DNSSendBuf[PKTBUF_SIZE];
176 static char __lowmem DNSRecvBuf[PKTBUF_SIZE];
177 char *p;
178 int err;
179 int dots;
180 int same;
181 int rd_len;
182 int ques, reps; /* number of questions and replies */
183 uint8_t timeout;
184 const uint8_t *timeout_ptr = TimeoutTable;
185 uint32_t oldtime;
186 uint32_t srv;
187 uint32_t *srv_ptr;
188 struct dnshdr *hd1 = (struct dnshdr *)DNSSendBuf;
189 struct dnshdr *hd2 = (struct dnshdr *)DNSRecvBuf;
190 struct dnsquery *query;
191 struct dnsrr *rr;
192 static __lowmem struct s_PXENV_UDP_WRITE udp_write;
193 static __lowmem struct s_PXENV_UDP_READ udp_read;
194 uint16_t local_port;
195 uint32_t result = 0;
197 /* Make sure we have at least one valid DNS server */
198 if (!dns_server[0])
199 return 0;
201 /* Get a local port number */
202 local_port = get_port();
204 /* First, fill the DNS header struct */
205 hd1->id++; /* New query ID */
206 hd1->flags = htons(0x0100); /* Recursion requested */
207 hd1->qdcount = htons(1); /* One question */
208 hd1->ancount = 0; /* No answers */
209 hd1->nscount = 0; /* No NS */
210 hd1->arcount = 0; /* No AR */
212 p = DNSSendBuf + sizeof(struct dnshdr);
213 dots = dns_mangle(&p, name); /* store the CNAME */
215 if (!dots) {
216 p--; /* Remove final null */
217 /* Uncompressed DNS label set so it ends in null */
218 p = stpcpy(p, LocalDomain);
221 /* Fill the DNS query packet */
222 query = (struct dnsquery *)p;
223 query->qtype = htons(TYPE_A);
224 query->qclass = htons(CLASS_IN);
225 p += sizeof(struct dnsquery);
227 /* Now send it to name server */
228 timeout_ptr = TimeoutTable;
229 timeout = *timeout_ptr++;
230 srv_ptr = dns_server;
231 while (timeout) {
232 srv = *srv_ptr++;
233 if (!srv) {
234 srv_ptr = dns_server;
235 srv = *srv_ptr++;
238 udp_write.status = 0;
239 udp_write.ip = srv;
240 udp_write.gw = gateway(srv);
241 udp_write.src_port = local_port;
242 udp_write.dst_port = DNS_PORT;
243 udp_write.buffer_size = p - DNSSendBuf;
244 udp_write.buffer = FAR_PTR(DNSSendBuf);
245 err = pxe_call(PXENV_UDP_WRITE, &udp_write);
246 if (err || udp_write.status)
247 continue;
249 oldtime = jiffies();
250 do {
251 if (jiffies() - oldtime >= timeout)
252 goto again;
254 udp_read.status = 0;
255 udp_read.src_ip = srv;
256 udp_read.dest_ip = IPInfo.myip;
257 udp_read.s_port = DNS_PORT;
258 udp_read.d_port = local_port;
259 udp_read.buffer_size = PKTBUF_SIZE;
260 udp_read.buffer = FAR_PTR(DNSRecvBuf);
261 err = pxe_call(PXENV_UDP_READ, &udp_read);
262 } while (err || udp_read.status || hd2->id != hd1->id);
264 if ((hd2->flags ^ 0x80) & htons(0xf80f))
265 goto badness;
267 ques = htons(hd2->qdcount); /* Questions */
268 reps = htons(hd2->ancount); /* Replies */
269 p = DNSRecvBuf + sizeof(struct dnshdr);
270 while (ques--) {
271 p = dns_skiplabel(p); /* Skip name */
272 p += 4; /* Skip question trailer */
275 /* Parse the replies */
276 while (reps--) {
277 same = dns_compare(DNSSendBuf + sizeof(struct dnshdr),
278 p, DNSRecvBuf);
279 p = dns_skiplabel(p);
280 rr = (struct dnsrr *)p;
281 rd_len = ntohs(rr->rdlength);
282 if (same && ntohs(rr->class) == CLASS_IN) {
283 switch (ntohs(rr->type)) {
284 case TYPE_A:
285 if (rd_len == 4) {
286 result = *(uint32_t *)rr->rdata;
287 goto done;
289 break;
290 case TYPE_CNAME:
291 dns_copylabel(DNSSendBuf + sizeof(struct dnshdr),
292 rr->rdata, DNSRecvBuf);
294 * We should probably rescan the packet from the top
295 * here, and technically we might have to send a whole
296 * new request here...
298 break;
299 default:
300 break;
304 /* not the one we want, try next */
305 p += sizeof(struct dnsrr) + rd_len;
308 badness:
311 ; We got back no data from this server.
312 ; Unfortunately, for a recursive, non-authoritative
313 ; query there is no such thing as an NXDOMAIN reply,
314 ; which technically means we can't draw any
315 ; conclusions. However, in practice that means the
316 ; domain doesn't exist. If this turns out to be a
317 ; problem, we may want to add code to go through all
318 ; the servers before giving up.
320 ; If the DNS server wasn't capable of recursion, and
321 ; isn't capable of giving us an authoritative reply
322 ; (i.e. neither AA or RA set), then at least try a
323 ; different setver...
325 if (hd2->flags == htons(0x480))
326 continue;
328 break; /* failed */
330 again:
331 continue;
334 done:
335 free_port(local_port); /* Return port number to the free pool */
337 return result;
340 void pm_pxe_dns_resolv(com32sys_t *regs)
342 const char *name = MK_PTR(regs->ds, regs->esi.w[0]);
344 regs->eax.l = dns_resolv(name);