1 /*
2 * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
3 *
4 * Portions copyright (C) 2004 Anselm M. Hoffmeister
5 * <stockholm@users.sourceforge.net>.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 of the
10 * License, or any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 FILE_LICENCE ( GPL2_OR_LATER );
23
24 #include <stdint.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <stdio.h>
28 #include <errno.h>
29 #include <byteswap.h>
30 #include <gpxe/refcnt.h>
31 #include <gpxe/xfer.h>
32 #include <gpxe/open.h>
33 #include <gpxe/resolv.h>
34 #include <gpxe/retry.h>
35 #include <gpxe/tcpip.h>
36 #include <gpxe/settings.h>
37 #include <gpxe/features.h>
38 #include <gpxe/dns.h>
39
40 /** @file
41 *
42 * DNS protocol
43 *
44 */
45
46 FEATURE ( FEATURE_PROTOCOL, "DNS", DHCP_EB_FEATURE_DNS, 1 );
47
48 /** The DNS server */
49 static struct sockaddr_tcpip nameserver = {
50 .st_port = htons ( DNS_PORT ),
51 };
52
53 /** The local domain */
54 static char *localdomain;
55
56 /** A DNS request */
57 struct dns_request {
58 /** Reference counter */
59 struct refcnt refcnt;
60 /** Name resolution interface */
61 struct resolv_interface resolv;
62 /** Data transfer interface */
63 struct xfer_interface socket;
64 /** Retry timer */
65 struct retry_timer timer;
66
67 /** Socket address to fill in with resolved address */
68 struct sockaddr sa;
69 /** Current query packet */
70 struct dns_query query;
71 /** Location of query info structure within current packet
72 *
73 * The query info structure is located immediately after the
74 * compressed name.
75 */
76 struct dns_query_info *qinfo;
77 /** Recursion counter */
78 unsigned int recursion;
79 };
80
81 /**
82 * Mark DNS request as complete
83 *
84 * @v dns DNS request
85 * @v rc Return status code
86 */
dns_done(struct dns_request * dns,int rc)87 static void dns_done ( struct dns_request *dns, int rc ) {
88
89 /* Stop the retry timer */
90 stop_timer ( &dns->timer );
91
92 /* Close data transfer interface */
93 xfer_nullify ( &dns->socket );
94 xfer_close ( &dns->socket, rc );
95
96 /* Mark name resolution as complete */
97 resolv_done ( &dns->resolv, &dns->sa, rc );
98 }
99
100 /**
101 * Compare DNS reply name against the query name from the original request
102 *
103 * @v dns DNS request
104 * @v reply DNS reply
105 * @v rname Reply name
106 * @ret zero Names match
107 * @ret non-zero Names do not match
108 */
dns_name_cmp(struct dns_request * dns,const struct dns_header * reply,const char * rname)109 static int dns_name_cmp ( struct dns_request *dns,
110 const struct dns_header *reply,
111 const char *rname ) {
112 const char *qname = dns->query.payload;
113 int i;
114
115 while ( 1 ) {
116 /* Obtain next section of rname */
117 while ( ( *rname ) & 0xc0 ) {
118 rname = ( ( ( char * ) reply ) +
119 ( ntohs( *((uint16_t *)rname) ) & ~0xc000 ));
120 }
121 /* Check that lengths match */
122 if ( *rname != *qname )
123 return -1;
124 /* If length is zero, we have reached the end */
125 if ( ! *qname )
126 return 0;
127 /* Check that data matches */
128 for ( i = *qname + 1; i > 0 ; i-- ) {
129 if ( *(rname++) != *(qname++) )
130 return -1;
131 }
132 }
133 }
134
135 /**
136 * Skip over a (possibly compressed) DNS name
137 *
138 * @v name DNS name
139 * @ret name Next DNS name
140 */
dns_skip_name(const char * name)141 static const char * dns_skip_name ( const char *name ) {
142 while ( 1 ) {
143 if ( ! *name ) {
144 /* End of name */
145 return ( name + 1);
146 }
147 if ( *name & 0xc0 ) {
148 /* Start of a compressed name */
149 return ( name + 2 );
150 }
151 /* Uncompressed name portion */
152 name += *name + 1;
153 }
154 }
155
156 /**
157 * Find an RR in a reply packet corresponding to our query
158 *
159 * @v dns DNS request
160 * @v reply DNS reply
161 * @ret rr DNS RR, or NULL if not found
162 */
dns_find_rr(struct dns_request * dns,const struct dns_header * reply)163 static union dns_rr_info * dns_find_rr ( struct dns_request *dns,
164 const struct dns_header *reply ) {
165 int i, cmp;
166 const char *p = ( ( char * ) reply ) + sizeof ( struct dns_header );
167 union dns_rr_info *rr_info;
168
169 /* Skip over the questions section */
170 for ( i = ntohs ( reply->qdcount ) ; i > 0 ; i-- ) {
171 p = dns_skip_name ( p ) + sizeof ( struct dns_query_info );
172 }
173
174 /* Process the answers section */
175 for ( i = ntohs ( reply->ancount ) ; i > 0 ; i-- ) {
176 cmp = dns_name_cmp ( dns, reply, p );
177 p = dns_skip_name ( p );
178 rr_info = ( ( union dns_rr_info * ) p );
179 if ( cmp == 0 )
180 return rr_info;
181 p += ( sizeof ( rr_info->common ) +
182 ntohs ( rr_info->common.rdlength ) );
183 }
184
185 return NULL;
186 }
187
188 /**
189 * Append DHCP domain name if available and name is not fully qualified
190 *
191 * @v string Name as a NUL-terminated string
192 * @ret fqdn Fully-qualified domain name, malloc'd copy
193 *
194 * The caller must free fqdn which is allocated even if the name is already
195 * fully qualified.
196 */
dns_qualify_name(const char * string)197 static char * dns_qualify_name ( const char *string ) {
198 char *fqdn;
199
200 /* Leave unchanged if already fully-qualified or no local domain */
201 if ( ( ! localdomain ) || ( strchr ( string, '.' ) != 0 ) )
202 return strdup ( string );
203
204 /* Append local domain to name */
205 asprintf ( &fqdn, "%s.%s", string, localdomain );
206 return fqdn;
207 }
208
209 /**
210 * Convert a standard NUL-terminated string to a DNS name
211 *
212 * @v string Name as a NUL-terminated string
213 * @v buf Buffer in which to place DNS name
214 * @ret next Byte following constructed DNS name
215 *
216 * DNS names consist of "<length>element" pairs.
217 */
dns_make_name(const char * string,char * buf)218 static char * dns_make_name ( const char *string, char *buf ) {
219 char *length_byte = buf++;
220 char c;
221
222 while ( ( c = *(string++) ) ) {
223 if ( c == '.' ) {
224 *length_byte = buf - length_byte - 1;
225 length_byte = buf;
226 }
227 *(buf++) = c;
228 }
229 *length_byte = buf - length_byte - 1;
230 *(buf++) = '\0';
231 return buf;
232 }
233
234 /**
235 * Convert an uncompressed DNS name to a NUL-terminated string
236 *
237 * @v name DNS name
238 * @ret string NUL-terminated string
239 *
240 * Produce a printable version of a DNS name. Used only for debugging.
241 */
dns_unmake_name(char * name)242 static inline char * dns_unmake_name ( char *name ) {
243 char *p;
244 unsigned int len;
245
246 p = name;
247 while ( ( len = *p ) ) {
248 *(p++) = '.';
249 p += len;
250 }
251
252 return name + 1;
253 }
254
255 /**
256 * Decompress a DNS name
257 *
258 * @v reply DNS replay
259 * @v name DNS name
260 * @v buf Buffer into which to decompress DNS name
261 * @ret next Byte following decompressed DNS name
262 */
dns_decompress_name(const struct dns_header * reply,const char * name,char * buf)263 static char * dns_decompress_name ( const struct dns_header *reply,
264 const char *name, char *buf ) {
265 int i, len;
266
267 do {
268 /* Obtain next section of name */
269 while ( ( *name ) & 0xc0 ) {
270 name = ( ( char * ) reply +
271 ( ntohs ( *((uint16_t *)name) ) & ~0xc000 ) );
272 }
273 /* Copy data */
274 len = *name;
275 for ( i = len + 1 ; i > 0 ; i-- ) {
276 *(buf++) = *(name++);
277 }
278 } while ( len );
279 return buf;
280 }
281
282 /**
283 * Send next packet in DNS request
284 *
285 * @v dns DNS request
286 */
dns_send_packet(struct dns_request * dns)287 static int dns_send_packet ( struct dns_request *dns ) {
288 static unsigned int qid = 0;
289 size_t qlen;
290
291 /* Increment query ID */
292 dns->query.dns.id = htons ( ++qid );
293
294 DBGC ( dns, "DNS %p sending query ID %d\n", dns, qid );
295
296 /* Start retransmission timer */
297 start_timer ( &dns->timer );
298
299 /* Send the data */
300 qlen = ( ( ( void * ) dns->qinfo ) - ( ( void * ) &dns->query )
301 + sizeof ( dns->qinfo ) );
302 return xfer_deliver_raw ( &dns->socket, &dns->query, qlen );
303 }
304
305 /**
306 * Handle DNS retransmission timer expiry
307 *
308 * @v timer Retry timer
309 * @v fail Failure indicator
310 */
dns_timer_expired(struct retry_timer * timer,int fail)311 static void dns_timer_expired ( struct retry_timer *timer, int fail ) {
312 struct dns_request *dns =
313 container_of ( timer, struct dns_request, timer );
314
315 if ( fail ) {
316 dns_done ( dns, -ETIMEDOUT );
317 } else {
318 dns_send_packet ( dns );
319 }
320 }
321
322 /**
323 * Receive new data
324 *
325 * @v socket UDP socket
326 * @v data DNS reply
327 * @v len Length of DNS reply
328 * @ret rc Return status code
329 */
dns_xfer_deliver_raw(struct xfer_interface * socket,const void * data,size_t len)330 static int dns_xfer_deliver_raw ( struct xfer_interface *socket,
331 const void *data, size_t len ) {
332 struct dns_request *dns =
333 container_of ( socket, struct dns_request, socket );
334 const struct dns_header *reply = data;
335 union dns_rr_info *rr_info;
336 struct sockaddr_in *sin;
337 unsigned int qtype = dns->qinfo->qtype;
338
339 /* Sanity check */
340 if ( len < sizeof ( *reply ) ) {
341 DBGC ( dns, "DNS %p received underlength packet length %zd\n",
342 dns, len );
343 return -EINVAL;
344 }
345
346 /* Check reply ID matches query ID */
347 if ( reply->id != dns->query.dns.id ) {
348 DBGC ( dns, "DNS %p received unexpected reply ID %d "
349 "(wanted %d)\n", dns, ntohs ( reply->id ),
350 ntohs ( dns->query.dns.id ) );
351 return -EINVAL;
352 }
353
354 DBGC ( dns, "DNS %p received reply ID %d\n", dns, ntohs ( reply->id ));
355
356 /* Stop the retry timer. After this point, each code path
357 * must either restart the timer by calling dns_send_packet(),
358 * or mark the DNS operation as complete by calling
359 * dns_done()
360 */
361 stop_timer ( &dns->timer );
362
363 /* Search through response for useful answers. Do this
364 * multiple times, to take advantage of useful nameservers
365 * which send us e.g. the CNAME *and* the A record for the
366 * pointed-to name.
367 */
368 while ( ( rr_info = dns_find_rr ( dns, reply ) ) ) {
369 switch ( rr_info->common.type ) {
370
371 case htons ( DNS_TYPE_A ):
372
373 /* Found the target A record */
374 DBGC ( dns, "DNS %p found address %s\n",
375 dns, inet_ntoa ( rr_info->a.in_addr ) );
376 sin = ( struct sockaddr_in * ) &dns->sa;
377 sin->sin_family = AF_INET;
378 sin->sin_addr = rr_info->a.in_addr;
379
380 /* Mark operation as complete */
381 dns_done ( dns, 0 );
382 return 0;
383
384 case htons ( DNS_TYPE_CNAME ):
385
386 /* Found a CNAME record; update query and recurse */
387 DBGC ( dns, "DNS %p found CNAME\n", dns );
388 dns->qinfo = ( void * ) dns_decompress_name ( reply,
389 rr_info->cname.cname,
390 dns->query.payload );
391 dns->qinfo->qtype = htons ( DNS_TYPE_A );
392 dns->qinfo->qclass = htons ( DNS_CLASS_IN );
393
394 /* Terminate the operation if we recurse too far */
395 if ( ++dns->recursion > DNS_MAX_CNAME_RECURSION ) {
396 DBGC ( dns, "DNS %p recursion exceeded\n",
397 dns );
398 dns_done ( dns, -ELOOP );
399 return 0;
400 }
401 break;
402
403 default:
404 DBGC ( dns, "DNS %p got unknown record type %d\n",
405 dns, ntohs ( rr_info->common.type ) );
406 break;
407 }
408 }
409
410 /* Determine what to do next based on the type of query we
411 * issued and the reponse we received
412 */
413 switch ( qtype ) {
414
415 case htons ( DNS_TYPE_A ):
416 /* We asked for an A record and got nothing;
417 * try the CNAME.
418 */
419 DBGC ( dns, "DNS %p found no A record; trying CNAME\n", dns );
420 dns->qinfo->qtype = htons ( DNS_TYPE_CNAME );
421 dns_send_packet ( dns );
422 return 0;
423
424 case htons ( DNS_TYPE_CNAME ):
425 /* We asked for a CNAME record. If we got a response
426 * (i.e. if the next A query is already set up), then
427 * issue it, otherwise abort.
428 */
429 if ( dns->qinfo->qtype == htons ( DNS_TYPE_A ) ) {
430 dns_send_packet ( dns );
431 return 0;
432 } else {
433 DBGC ( dns, "DNS %p found no CNAME record\n", dns );
434 dns_done ( dns, -ENXIO );
435 return 0;
436 }
437
438 default:
439 assert ( 0 );
440 dns_done ( dns, -EINVAL );
441 return 0;
442 }
443 }
444
445 /**
446 * Receive new data
447 *
448 * @v socket UDP socket
449 * @v rc Reason for close
450 */
dns_xfer_close(struct xfer_interface * socket,int rc)451 static void dns_xfer_close ( struct xfer_interface *socket, int rc ) {
452 struct dns_request *dns =
453 container_of ( socket, struct dns_request, socket );
454
455 if ( ! rc )
456 rc = -ECONNABORTED;
457
458 dns_done ( dns, rc );
459 }
460
461 /** DNS socket operations */
462 static struct xfer_interface_operations dns_socket_operations = {
463 .close = dns_xfer_close,
464 .vredirect = xfer_vreopen,
465 .window = unlimited_xfer_window,
466 .alloc_iob = default_xfer_alloc_iob,
467 .deliver_iob = xfer_deliver_as_raw,
468 .deliver_raw = dns_xfer_deliver_raw,
469 };
470
471 /**
472 * Resolve name using DNS
473 *
474 * @v resolv Name resolution interface
475 * @v name Name to resolve
476 * @v sa Socket address to fill in
477 * @ret rc Return status code
478 */
dns_resolv(struct resolv_interface * resolv,const char * name,struct sockaddr * sa)479 static int dns_resolv ( struct resolv_interface *resolv,
480 const char *name, struct sockaddr *sa ) {
481 struct dns_request *dns;
482 char *fqdn;
483 int rc;
484
485 /* Fail immediately if no DNS servers */
486 if ( ! nameserver.st_family ) {
487 DBG ( "DNS not attempting to resolve \"%s\": "
488 "no DNS servers\n", name );
489 rc = -ENXIO;
490 goto err_no_nameserver;
491 }
492
493 /* Ensure fully-qualified domain name if DHCP option was given */
494 fqdn = dns_qualify_name ( name );
495 if ( ! fqdn ) {
496 rc = -ENOMEM;
497 goto err_qualify_name;
498 }
499
500 /* Allocate DNS structure */
501 dns = zalloc ( sizeof ( *dns ) );
502 if ( ! dns ) {
503 rc = -ENOMEM;
504 goto err_alloc_dns;
505 }
506 resolv_init ( &dns->resolv, &null_resolv_ops, &dns->refcnt );
507 xfer_init ( &dns->socket, &dns_socket_operations, &dns->refcnt );
508 dns->timer.expired = dns_timer_expired;
509 memcpy ( &dns->sa, sa, sizeof ( dns->sa ) );
510
511 /* Create query */
512 dns->query.dns.flags = htons ( DNS_FLAG_QUERY | DNS_FLAG_OPCODE_QUERY |
513 DNS_FLAG_RD );
514 dns->query.dns.qdcount = htons ( 1 );
515 dns->qinfo = ( void * ) dns_make_name ( fqdn, dns->query.payload );
516 dns->qinfo->qtype = htons ( DNS_TYPE_A );
517 dns->qinfo->qclass = htons ( DNS_CLASS_IN );
518
519 /* Open UDP connection */
520 if ( ( rc = xfer_open_socket ( &dns->socket, SOCK_DGRAM,
521 ( struct sockaddr * ) &nameserver,
522 NULL ) ) != 0 ) {
523 DBGC ( dns, "DNS %p could not open socket: %s\n",
524 dns, strerror ( rc ) );
525 goto err_open_socket;
526 }
527
528 /* Send first DNS packet */
529 dns_send_packet ( dns );
530
531 /* Attach parent interface, mortalise self, and return */
532 resolv_plug_plug ( &dns->resolv, resolv );
533 ref_put ( &dns->refcnt );
534 free ( fqdn );
535 return 0;
536
537 err_open_socket:
538 err_alloc_dns:
539 ref_put ( &dns->refcnt );
540 err_qualify_name:
541 free ( fqdn );
542 err_no_nameserver:
543 return rc;
544 }
545
546 /** DNS name resolver */
547 struct resolver dns_resolver __resolver ( RESOLV_NORMAL ) = {
548 .name = "DNS",
549 .resolv = dns_resolv,
550 };
551
552 /******************************************************************************
553 *
554 * Settings
555 *
556 ******************************************************************************
557 */
558
559 /** DNS server setting */
560 struct setting dns_setting __setting = {
561 .name = "dns",
562 .description = "DNS server",
563 .tag = DHCP_DNS_SERVERS,
564 .type = &setting_type_ipv4,
565 };
566
567 /** Domain name setting */
568 struct setting domain_setting __setting = {
569 .name = "domain",
570 .description = "Local domain",
571 .tag = DHCP_DOMAIN_NAME,
572 .type = &setting_type_string,
573 };
574
575 /**
576 * Apply DNS settings
577 *
578 * @ret rc Return status code
579 */
apply_dns_settings(void)580 static int apply_dns_settings ( void ) {
581 struct sockaddr_in *sin_nameserver =
582 ( struct sockaddr_in * ) &nameserver;
583 int len;
584
585 if ( ( len = fetch_ipv4_setting ( NULL, &dns_setting,
586 &sin_nameserver->sin_addr ) ) >= 0 ){
587 sin_nameserver->sin_family = AF_INET;
588 DBG ( "DNS using nameserver %s\n",
589 inet_ntoa ( sin_nameserver->sin_addr ) );
590 }
591
592 /* Get local domain DHCP option */
593 if ( ( len = fetch_string_setting_copy ( NULL, &domain_setting,
594 &localdomain ) ) >= 0 )
595 DBG ( "DNS local domain %s\n", localdomain );
596
597 return 0;
598 }
599
600 /** DNS settings applicator */
601 struct settings_applicator dns_applicator __settings_applicator = {
602 .apply = apply_dns_settings,
603 };
604