[dns] Handle AAAA records
[people/meteger/gpxe.git] / src / net / udp / dns.c
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  */
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  */
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  */
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  */
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  */
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  */
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  */
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  */
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  */
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  */
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  */
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         struct sockaddr_in6 *sin6;
338         unsigned int qtype = dns->qinfo->qtype;
339
340         /* Sanity check */
341         if ( len < sizeof ( *reply ) ) {
342                 DBGC ( dns, "DNS %p received underlength packet length %zd\n",
343                        dns, len );
344                 return -EINVAL;
345         }
346
347         /* Check reply ID matches query ID */
348         if ( reply->id != dns->query.dns.id ) {
349                 DBGC ( dns, "DNS %p received unexpected reply ID %d "
350                        "(wanted %d)\n", dns, ntohs ( reply->id ),
351                        ntohs ( dns->query.dns.id ) );
352                 return -EINVAL;
353         }
354
355         DBGC ( dns, "DNS %p received reply ID %d\n", dns, ntohs ( reply->id ));
356
357         /* Stop the retry timer.  After this point, each code path
358          * must either restart the timer by calling dns_send_packet(),
359          * or mark the DNS operation as complete by calling
360          * dns_done()
361          */
362         stop_timer ( &dns->timer );
363
364         /* Search through response for useful answers.  Do this
365          * multiple times, to take advantage of useful nameservers
366          * which send us e.g. the CNAME *and* the A record for the
367          * pointed-to name.
368          */
369         while ( ( rr_info = dns_find_rr ( dns, reply ) ) ) {
370                 switch ( rr_info->common.type ) {
371
372                 case htons ( DNS_TYPE_A ):
373
374                         /* Found the target A record */
375                         DBGC ( dns, "DNS %p found address %s\n",
376                                dns, inet_ntoa ( rr_info->a.in_addr ) );
377                         sin = ( struct sockaddr_in * ) &dns->sa;
378                         sin->sin_family = AF_INET;
379                         sin->sin_addr = rr_info->a.in_addr;
380
381                         /* Mark operation as complete */
382                         dns_done ( dns, 0 );
383                         return 0;
384
385                 case htons ( DNS_TYPE_CNAME ):
386
387                         /* Found a CNAME record; update query and recurse */
388                         DBGC ( dns, "DNS %p found CNAME\n", dns );
389                         dns->qinfo = ( void * ) dns_decompress_name ( reply,
390                                                          rr_info->cname.cname,
391                                                          dns->query.payload );
392                         dns->qinfo->qtype = htons ( DNS_TYPE_A );
393                         dns->qinfo->qclass = htons ( DNS_CLASS_IN );
394                         
395                         /* Terminate the operation if we recurse too far */
396                         if ( ++dns->recursion > DNS_MAX_CNAME_RECURSION ) {
397                                 DBGC ( dns, "DNS %p recursion exceeded\n",
398                                        dns );
399                                 dns_done ( dns, -ELOOP );
400                                 return 0;
401                         }
402                         break;
403                 
404                 case htons ( DNS_TYPE_AAAA ):
405
406                         /* Found the target AAAA record */
407                         DBGC ( dns, "DNS %p found address %s\n",
408                                dns, inet6_ntoa ( rr_info->aaaa.in6_addr ) );
409                         sin6 = ( struct sockaddr_in6 * ) &dns->sa;
410                         sin6->sin_family = AF_INET6;
411                         sin6->sin6_addr = rr_info->aaaa.in6_addr;
412
413                         /* Mark operation as complete */
414                         dns_done ( dns, 0 );
415                         return 0;
416
417                 default:
418                         DBGC ( dns, "DNS %p got unknown record type %d\n",
419                                dns, ntohs ( rr_info->common.type ) );
420                         break;
421                 }
422         }
423         
424         /* Determine what to do next based on the type of query we
425          * issued and the reponse we received
426          */
427         switch ( qtype ) {
428
429         case htons ( DNS_TYPE_A ):
430                 /* We asked for an A record and got nothing;
431                  * try the AAAA record.
432                  */
433                 DBGC ( dns, "DNS %p found no A record; trying AAAA\n", dns );
434                 dns->qinfo->qtype = htons ( DNS_TYPE_AAAA );
435                 dns_send_packet ( dns );
436                 return 0;
437
438         case htons ( DNS_TYPE_AAAA ):
439                 /* We asked for an AAAA record and got nothing;
440                  * try the CNAME.
441                  */
442                 DBGC ( dns, "DNS %p found no AAAA record; trying CNAME\n", dns );
443                 dns->qinfo->qtype = htons ( DNS_TYPE_CNAME );
444                 dns_send_packet ( dns );
445                 return 0;
446
447         case htons ( DNS_TYPE_CNAME ):
448                 /* We asked for a CNAME record.  If we got a response
449                  * (i.e. if the next AAAA query is already set up), then
450                  * issue it, otherwise abort.
451                  */
452                 if ( dns->qinfo->qtype == htons ( DNS_TYPE_AAAA ) ) {
453                         dns_send_packet ( dns );
454                         return 0;
455                 } else {
456                         DBGC ( dns, "DNS %p found no CNAME record\n", dns );
457                         dns_done ( dns, -ENXIO );
458                         return 0;
459                 }
460
461         default:
462                 assert ( 0 );
463                 dns_done ( dns, -EINVAL );
464                 return 0;
465         }
466 }
467
468 /**
469  * Receive new data
470  *
471  * @v socket            UDP socket
472  * @v rc                Reason for close
473  */
474 static void dns_xfer_close ( struct xfer_interface *socket, int rc ) {
475         struct dns_request *dns =
476                 container_of ( socket, struct dns_request, socket );
477
478         if ( ! rc )
479                 rc = -ECONNABORTED;
480
481         dns_done ( dns, rc );
482 }
483
484 /** DNS socket operations */
485 static struct xfer_interface_operations dns_socket_operations = {
486         .close          = dns_xfer_close,
487         .vredirect      = xfer_vreopen,
488         .window         = unlimited_xfer_window,
489         .alloc_iob      = default_xfer_alloc_iob,
490         .deliver_iob    = xfer_deliver_as_raw,
491         .deliver_raw    = dns_xfer_deliver_raw,
492 };
493
494 /**
495  * Resolve name using DNS
496  *
497  * @v resolv            Name resolution interface
498  * @v name              Name to resolve
499  * @v sa                Socket address to fill in
500  * @ret rc              Return status code
501  */
502 static int dns_resolv ( struct resolv_interface *resolv,
503                         const char *name, struct sockaddr *sa ) {
504         struct dns_request *dns;
505         char *fqdn;
506         int rc;
507
508         /* Fail immediately if no DNS servers */
509         if ( ! nameserver.st_family ) {
510                 DBG ( "DNS not attempting to resolve \"%s\": "
511                       "no DNS servers\n", name );
512                 rc = -ENXIO;
513                 goto err_no_nameserver;
514         }
515
516         /* Ensure fully-qualified domain name if DHCP option was given */
517         fqdn = dns_qualify_name ( name );
518         if ( ! fqdn ) {
519                 rc = -ENOMEM;
520                 goto err_qualify_name;
521         }
522
523         /* Allocate DNS structure */
524         dns = zalloc ( sizeof ( *dns ) );
525         if ( ! dns ) {
526                 rc = -ENOMEM;
527                 goto err_alloc_dns;
528         }
529         ref_init ( &dns->refcnt, NULL );
530         resolv_init ( &dns->resolv, &null_resolv_ops, &dns->refcnt );
531         xfer_init ( &dns->socket, &dns_socket_operations, &dns->refcnt );
532         timer_init ( &dns->timer, dns_timer_expired );
533         memcpy ( &dns->sa, sa, sizeof ( dns->sa ) );
534
535         /* Create query */
536         dns->query.dns.flags = htons ( DNS_FLAG_QUERY | DNS_FLAG_OPCODE_QUERY |
537                                        DNS_FLAG_RD );
538         dns->query.dns.qdcount = htons ( 1 );
539         dns->qinfo = ( void * ) dns_make_name ( fqdn, dns->query.payload );
540         dns->qinfo->qtype = htons ( DNS_TYPE_A );
541         dns->qinfo->qclass = htons ( DNS_CLASS_IN );
542
543         /* Open UDP connection */
544         if ( ( rc = xfer_open_socket ( &dns->socket, SOCK_DGRAM,
545                                        ( struct sockaddr * ) &nameserver,
546                                        NULL ) ) != 0 ) {
547                 DBGC ( dns, "DNS %p could not open socket: %s\n",
548                        dns, strerror ( rc ) );
549                 goto err_open_socket;
550         }
551
552         /* Send first DNS packet */
553         dns_send_packet ( dns );
554
555         /* Attach parent interface, mortalise self, and return */
556         resolv_plug_plug ( &dns->resolv, resolv );
557         ref_put ( &dns->refcnt );
558         free ( fqdn );
559         return 0;       
560
561  err_open_socket:
562  err_alloc_dns:
563         ref_put ( &dns->refcnt );
564  err_qualify_name:
565         free ( fqdn );
566  err_no_nameserver:
567         return rc;
568 }
569
570 /** DNS name resolver */
571 struct resolver dns_resolver __resolver ( RESOLV_NORMAL ) = {
572         .name = "DNS",
573         .resolv = dns_resolv,
574 };
575
576 /******************************************************************************
577  *
578  * Settings
579  *
580  ******************************************************************************
581  */
582
583 /** DNS server setting */
584 struct setting dns_setting __setting = {
585         .name = "dns",
586         .description = "DNS server",
587         .tag = DHCP_DNS_SERVERS,
588         .type = &setting_type_ipv4,
589 };
590
591 /** Domain name setting */
592 struct setting domain_setting __setting = {
593         .name = "domain",
594         .description = "Local domain",
595         .tag = DHCP_DOMAIN_NAME,
596         .type = &setting_type_string,
597 };
598
599 /**
600  * Apply DNS settings
601  *
602  * @ret rc              Return status code
603  */
604 static int apply_dns_settings ( void ) {
605         struct sockaddr_in *sin_nameserver =
606                 ( struct sockaddr_in * ) &nameserver;
607         int len;
608
609         if ( ( len = fetch_ipv4_setting ( NULL, &dns_setting,
610                                           &sin_nameserver->sin_addr ) ) >= 0 ){
611                 sin_nameserver->sin_family = AF_INET;
612                 DBG ( "DNS using nameserver %s\n",
613                       inet_ntoa ( sin_nameserver->sin_addr ) );
614         }
615
616         /* Get local domain DHCP option */
617         if ( ( len = fetch_string_setting_copy ( NULL, &domain_setting,
618                                                  &localdomain ) ) >= 0 )
619                 DBG ( "DNS local domain %s\n", localdomain );
620
621         return 0;
622 }
623
624 /** DNS settings applicator */
625 struct settings_applicator dns_applicator __settings_applicator = {
626         .apply = apply_dns_settings,
627 };