2 * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
4 * Portions copyright (C) 2004 Anselm M. Hoffmeister
5 * <stockholm@users.sourceforge.net>.
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.
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.
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.
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/features.h>
43 FEATURE ( FEATURE_PROTOCOL, "DNS", DHCP_EB_FEATURE_DNS, 1 );
46 static struct sockaddr_tcpip nameserver = {
47 .st_port = htons ( DNS_PORT ),
52 /** Reference counter */
54 /** Name resolution interface */
55 struct resolv_interface resolv;
56 /** Data transfer interface */
57 struct xfer_interface socket;
59 struct retry_timer timer;
61 /** Socket address to fill in with resolved address */
63 /** Current query packet */
64 struct dns_query query;
65 /** Location of query info structure within current packet
67 * The query info structure is located immediately after the
70 struct dns_query_info *qinfo;
71 /** Recursion counter */
72 unsigned int recursion;
76 * Mark DNS request as complete
79 * @v rc Return status code
81 static void dns_done ( struct dns_request *dns, int rc ) {
83 /* Stop the retry timer */
84 stop_timer ( &dns->timer );
86 /* Close data transfer interface */
87 xfer_nullify ( &dns->socket );
88 xfer_close ( &dns->socket, rc );
90 /* Mark name resolution as complete */
91 resolv_done ( &dns->resolv, &dns->sa, rc );
95 * Compare DNS reply name against the query name from the original request
100 * @ret zero Names match
101 * @ret non-zero Names do not match
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;
110 /* Obtain next section of rname */
111 while ( ( *rname ) & 0xc0 ) {
112 rname = ( ( ( char * ) reply ) +
113 ( ntohs( *((uint16_t *)rname) ) & ~0xc000 ));
115 /* Check that lengths match */
116 if ( *rname != *qname )
118 /* If length is zero, we have reached the end */
121 /* Check that data matches */
122 for ( i = *qname + 1; i > 0 ; i-- ) {
123 if ( *(rname++) != *(qname++) )
130 * Skip over a (possibly compressed) DNS name
133 * @ret name Next DNS name
135 static const char * dns_skip_name ( const char *name ) {
141 if ( *name & 0xc0 ) {
142 /* Start of a compressed name */
145 /* Uncompressed name portion */
151 * Find an RR in a reply packet corresponding to our query
155 * @ret rr DNS RR, or NULL if not found
157 static union dns_rr_info * dns_find_rr ( struct dns_request *dns,
158 const struct dns_header *reply ) {
160 const char *p = ( ( char * ) reply ) + sizeof ( struct dns_header );
161 union dns_rr_info *rr_info;
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 );
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 );
175 p += ( sizeof ( rr_info->common ) +
176 ntohs ( rr_info->common.rdlength ) );
183 * Convert a standard NUL-terminated string to a DNS name
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
189 * DNS names consist of "<length>element" pairs.
191 static char * dns_make_name ( const char *string, char *buf ) {
192 char *length_byte = buf++;
195 while ( ( c = *(string++) ) ) {
197 *length_byte = buf - length_byte - 1;
202 *length_byte = buf - length_byte - 1;
208 * Convert an uncompressed DNS name to a NUL-terminated string
211 * @ret string NUL-terminated string
213 * Produce a printable version of a DNS name. Used only for debugging.
215 static inline char * dns_unmake_name ( char *name ) {
220 while ( ( len = *p ) ) {
229 * Decompress a DNS name
231 * @v reply DNS replay
233 * @v buf Buffer into which to decompress DNS name
234 * @ret next Byte following decompressed DNS name
236 static char * dns_decompress_name ( const struct dns_header *reply,
237 const char *name, char *buf ) {
241 /* Obtain next section of name */
242 while ( ( *name ) & 0xc0 ) {
243 name = ( ( char * ) reply +
244 ( ntohs ( *((uint16_t *)name) ) & ~0xc000 ) );
248 for ( i = len + 1 ; i > 0 ; i-- ) {
249 *(buf++) = *(name++);
256 * Send next packet in DNS request
260 static int dns_send_packet ( struct dns_request *dns ) {
261 static unsigned int qid = 0;
264 /* Increment query ID */
265 dns->query.dns.id = htons ( ++qid );
267 DBGC ( dns, "DNS %p sending query ID %d\n", dns, qid );
269 /* Start retransmission timer */
270 start_timer ( &dns->timer );
273 qlen = ( ( ( void * ) dns->qinfo ) - ( ( void * ) &dns->query )
274 + sizeof ( dns->qinfo ) );
275 return xfer_deliver_raw ( &dns->socket, &dns->query, qlen );
279 * Handle DNS retransmission timer expiry
281 * @v timer Retry timer
282 * @v fail Failure indicator
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 );
289 dns_done ( dns, -ETIMEDOUT );
291 dns_send_packet ( dns );
298 * @v socket UDP socket
300 * @v len Length of DNS reply
301 * @ret rc Return status code
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;
313 if ( len < sizeof ( *reply ) ) {
314 DBGC ( dns, "DNS %p received underlength packet length %zd\n",
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 ) );
327 DBGC ( dns, "DNS %p received reply ID %d\n", dns, ntohs ( reply->id ));
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
334 stop_timer ( &dns->timer );
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
341 while ( ( rr_info = dns_find_rr ( dns, reply ) ) ) {
342 switch ( rr_info->common.type ) {
344 case htons ( DNS_TYPE_A ):
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;
353 /* Mark operation as complete */
357 case htons ( DNS_TYPE_CNAME ):
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 );
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",
371 dns_done ( dns, -ELOOP );
377 DBGC ( dns, "DNS %p got unknown record type %d\n",
378 dns, ntohs ( rr_info->common.type ) );
383 /* Determine what to do next based on the type of query we
384 * issued and the reponse we received
388 case htons ( DNS_TYPE_A ):
389 /* We asked for an A record and got nothing;
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 );
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.
402 if ( dns->qinfo->qtype == htons ( DNS_TYPE_A ) ) {
403 dns_send_packet ( dns );
406 DBGC ( dns, "DNS %p found no CNAME record\n", dns );
407 dns_done ( dns, -ENXIO );
413 dns_done ( dns, -EINVAL );
421 * @v socket UDP socket
422 * @v rc Reason for close
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 );
431 dns_done ( dns, rc );
434 /** DNS socket operations */
435 static struct xfer_interface_operations dns_socket_operations = {
436 .close = dns_xfer_close,
437 .vredirect = xfer_vopen,
438 .seek = ignore_xfer_seek,
439 .window = unlimited_xfer_window,
440 .alloc_iob = default_xfer_alloc_iob,
441 .deliver_iob = xfer_deliver_as_raw,
442 .deliver_raw = dns_xfer_deliver_raw,
446 * Resolve name using DNS
448 * @v resolv Name resolution interface
449 * @v name Name to resolve
450 * @v sa Socket address to fill in
451 * @ret rc Return status code
453 static int dns_resolv ( struct resolv_interface *resolv,
454 const char *name, struct sockaddr *sa ) {
455 struct dns_request *dns;
458 /* Fail immediately if no DNS servers */
459 if ( ! nameserver.st_family ) {
460 DBG ( "DNS not attempting to resolve \"%s\": "
461 "no DNS servers\n", name );
465 /* Allocate DNS structure */
466 dns = zalloc ( sizeof ( *dns ) );
469 resolv_init ( &dns->resolv, &null_resolv_ops, &dns->refcnt );
470 xfer_init ( &dns->socket, &dns_socket_operations, &dns->refcnt );
471 dns->timer.expired = dns_timer_expired;
472 memcpy ( &dns->sa, sa, sizeof ( dns->sa ) );
475 dns->query.dns.flags = htons ( DNS_FLAG_QUERY | DNS_FLAG_OPCODE_QUERY |
477 dns->query.dns.qdcount = htons ( 1 );
478 dns->qinfo = ( void * ) dns_make_name ( name, dns->query.payload );
479 dns->qinfo->qtype = htons ( DNS_TYPE_A );
480 dns->qinfo->qclass = htons ( DNS_CLASS_IN );
482 /* Open UDP connection */
483 if ( ( rc = xfer_open_socket ( &dns->socket, SOCK_DGRAM,
484 ( struct sockaddr * ) &nameserver,
486 DBGC ( dns, "DNS %p could not open socket: %s\n",
487 dns, strerror ( rc ) );
491 /* Send first DNS packet */
492 dns_send_packet ( dns );
494 /* Attach parent interface, mortalise self, and return */
495 resolv_plug_plug ( &dns->resolv, resolv );
496 ref_put ( &dns->refcnt );
500 ref_put ( &dns->refcnt );
504 /** DNS name resolver */
505 struct resolver dns_resolver __resolver ( RESOLV_NORMAL ) = {
507 .resolv = dns_resolv,
511 * Apply DHCP nameserver option
513 * @v tag DHCP option tag
514 * @v option DHCP option
516 static int apply_dhcp_nameserver ( unsigned int tag __unused,
517 struct dhcp_option *option ) {
518 struct sockaddr_in *sin_nameserver;
520 sin_nameserver = ( struct sockaddr_in * ) &nameserver;
521 sin_nameserver->sin_family = AF_INET;
522 dhcp_ipv4_option ( option, &sin_nameserver->sin_addr );
524 DBG ( "DNS using nameserver %s\n",
525 inet_ntoa ( sin_nameserver->sin_addr ) );
530 /** DHCP nameserver applicator */
531 struct dhcp_option_applicator dhcp_nameserver_applicator __dhcp_applicator = {
532 .tag = DHCP_DNS_SERVERS,
533 .apply = apply_dhcp_nameserver,