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