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