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