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>
43 struct sockaddr_tcpip nameserver = {
44 .st_port = htons ( DNS_PORT ),
49 /** Reference counter */
51 /** Name resolution interface */
52 struct resolv_interface resolv;
53 /** Data transfer interface */
54 struct xfer_interface socket;
56 struct retry_timer timer;
58 /** Socket address to fill in with resolved address */
60 /** Current query packet */
61 struct dns_query query;
62 /** Location of query info structure within current packet
64 * The query info structure is located immediately after the
67 struct dns_query_info *qinfo;
68 /** Recursion counter */
69 unsigned int recursion;
73 * Mark DNS request as complete
76 * @v rc Return status code
78 static void dns_done ( struct dns_request *dns, int rc ) {
80 /* Stop the retry timer */
81 stop_timer ( &dns->timer );
83 /* Close data transfer interface */
84 xfer_nullify ( &dns->socket );
85 xfer_close ( &dns->socket, rc );
87 /* Mark name resolution as complete */
88 resolv_done ( &dns->resolv, &dns->sa, rc );
92 * Compare DNS reply name against the query name from the original request
97 * @ret zero Names match
98 * @ret non-zero Names do not match
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;
107 /* Obtain next section of rname */
108 while ( ( *rname ) & 0xc0 ) {
109 rname = ( ( ( char * ) reply ) +
110 ( ntohs( *((uint16_t *)rname) ) & ~0xc000 ));
112 /* Check that lengths match */
113 if ( *rname != *qname )
115 /* If length is zero, we have reached the end */
118 /* Check that data matches */
119 for ( i = *qname + 1; i > 0 ; i-- ) {
120 if ( *(rname++) != *(qname++) )
127 * Skip over a (possibly compressed) DNS name
130 * @ret name Next DNS name
132 static const char * dns_skip_name ( const char *name ) {
138 if ( *name & 0xc0 ) {
139 /* Start of a compressed name */
142 /* Uncompressed name portion */
148 * Find an RR in a reply packet corresponding to our query
152 * @ret rr DNS RR, or NULL if not found
154 static union dns_rr_info * dns_find_rr ( struct dns_request *dns,
155 const struct dns_header *reply ) {
157 const char *p = ( ( char * ) reply ) + sizeof ( struct dns_header );
158 union dns_rr_info *rr_info;
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 );
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 );
172 p += ( sizeof ( rr_info->common ) +
173 ntohs ( rr_info->common.rdlength ) );
180 * Convert a standard NUL-terminated string to a DNS name
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
186 * DNS names consist of "<length>element" pairs.
188 static char * dns_make_name ( const char *string, char *buf ) {
189 char *length_byte = buf++;
192 while ( ( c = *(string++) ) ) {
194 *length_byte = buf - length_byte - 1;
199 *length_byte = buf - length_byte - 1;
205 * Convert an uncompressed DNS name to a NUL-terminated string
208 * @ret string NUL-terminated string
210 * Produce a printable version of a DNS name. Used only for debugging.
212 static inline char * dns_unmake_name ( char *name ) {
217 while ( ( len = *p ) ) {
226 * Decompress a DNS name
228 * @v reply DNS replay
230 * @v buf Buffer into which to decompress DNS name
231 * @ret next Byte following decompressed DNS name
233 static char * dns_decompress_name ( const struct dns_header *reply,
234 const char *name, char *buf ) {
238 /* Obtain next section of name */
239 while ( ( *name ) & 0xc0 ) {
240 name = ( ( char * ) reply +
241 ( ntohs ( *((uint16_t *)name) ) & ~0xc000 ) );
245 for ( i = len + 1 ; i > 0 ; i-- ) {
246 *(buf++) = *(name++);
253 * Send next packet in DNS request
257 static int dns_send_packet ( struct dns_request *dns ) {
258 static unsigned int qid = 0;
261 /* Increment query ID */
262 dns->query.dns.id = htons ( ++qid );
264 DBGC ( dns, "DNS %p sending query ID %d\n", dns, qid );
266 /* Start retransmission timer */
267 start_timer ( &dns->timer );
270 qlen = ( ( ( void * ) dns->qinfo ) - ( ( void * ) &dns->query )
271 + sizeof ( dns->qinfo ) );
272 return xfer_deliver_raw ( &dns->socket, &dns->query, qlen );
276 * Handle DNS retransmission timer expiry
278 * @v timer Retry timer
279 * @v fail Failure indicator
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 );
286 dns_done ( dns, -ETIMEDOUT );
288 dns_send_packet ( dns );
295 * @v socket UDP socket
297 * @v len Length of DNS reply
298 * @ret rc Return status code
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;
310 if ( len < sizeof ( *reply ) ) {
311 DBGC ( dns, "DNS %p received underlength packet length %zd\n",
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 ) );
324 DBGC ( dns, "DNS %p received reply ID %d\n", dns, ntohs ( reply->id ));
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
331 stop_timer ( &dns->timer );
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
338 while ( ( rr_info = dns_find_rr ( dns, reply ) ) ) {
339 switch ( rr_info->common.type ) {
341 case htons ( DNS_TYPE_A ):
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;
350 /* Mark operation as complete */
354 case htons ( DNS_TYPE_CNAME ):
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 );
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",
368 dns_done ( dns, -ELOOP );
374 DBGC ( dns, "DNS %p got unknown record type %d\n",
375 dns, ntohs ( rr_info->common.type ) );
380 /* Determine what to do next based on the type of query we
381 * issued and the reponse we received
385 case htons ( DNS_TYPE_A ):
386 /* We asked for an A record and got nothing;
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 );
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.
399 if ( dns->qinfo->qtype == htons ( DNS_TYPE_A ) ) {
400 dns_send_packet ( dns );
403 DBGC ( dns, "DNS %p found no CNAME record\n", dns );
404 dns_done ( dns, -ENXIO );
410 dns_done ( dns, -EINVAL );
418 * @v socket UDP socket
419 * @v rc Reason for close
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 );
428 dns_done ( dns, rc );
431 /** DNS socket operations */
432 static struct xfer_interface_operations dns_socket_operations = {
433 .close = dns_xfer_close,
434 .vredirect = xfer_vopen,
435 .request = ignore_xfer_request,
436 .seek = ignore_xfer_seek,
437 .alloc_iob = default_xfer_alloc_iob,
438 .deliver_iob = xfer_deliver_as_raw,
439 .deliver_raw = dns_xfer_deliver_raw,
443 * Resolve name using DNS
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
450 static int dns_resolv ( struct resolv_interface *resolv,
451 const char *name, struct sockaddr *sa ) {
452 struct dns_request *dns;
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 );
462 /* Allocate DNS structure */
463 dns = malloc ( sizeof ( *dns ) );
466 memset ( dns, 0, sizeof ( *dns ) );
467 resolv_init ( &dns->resolv, &null_resolv_ops, &dns->refcnt );
468 xfer_init ( &dns->socket, &dns_socket_operations, &dns->refcnt );
469 dns->timer.expired = dns_timer_expired;
470 memcpy ( &dns->sa, sa, sizeof ( dns->sa ) );
473 dns->query.dns.flags = htons ( DNS_FLAG_QUERY | DNS_FLAG_OPCODE_QUERY |
475 dns->query.dns.qdcount = htons ( 1 );
476 dns->qinfo = ( void * ) dns_make_name ( name, dns->query.payload );
477 dns->qinfo->qtype = htons ( DNS_TYPE_A );
478 dns->qinfo->qclass = htons ( DNS_CLASS_IN );
480 /* Open UDP connection */
481 if ( ( rc = xfer_open_socket ( &dns->socket, SOCK_DGRAM,
482 ( struct sockaddr * ) &nameserver,
484 DBGC ( dns, "DNS %p could not open socket: %s\n",
485 dns, strerror ( rc ) );
489 /* Send first DNS packet */
490 dns_send_packet ( dns );
492 /* Attach parent interface, mortalise self, and return */
493 resolv_plug_plug ( &dns->resolv, resolv );
494 ref_put ( &dns->refcnt );
498 ref_put ( &dns->refcnt );
502 /** DNS name resolver */
503 struct resolver dns_resolver __resolver ( RESOLV_NORMAL ) = {
505 .resolv = dns_resolv,
509 * Apply DHCP nameserver option
511 * @v tag DHCP option tag
512 * @v option DHCP option
514 static int apply_dhcp_nameserver ( unsigned int tag __unused,
515 struct dhcp_option *option ) {
516 struct sockaddr_in *sin_nameserver;
518 sin_nameserver = ( struct sockaddr_in * ) &nameserver;
519 sin_nameserver->sin_family = AF_INET;
520 dhcp_ipv4_option ( option, &sin_nameserver->sin_addr );
524 /** DHCP nameserver applicator */
525 struct dhcp_option_applicator dhcp_nameserver_applicator __dhcp_applicator = {
526 .tag = DHCP_DNS_SERVERS,
527 .apply = apply_dhcp_nameserver,