• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 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  */
dns_done(struct dns_request * dns,int rc)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  */
dns_name_cmp(struct dns_request * dns,const struct dns_header * reply,const char * rname)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  */
dns_skip_name(const char * name)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  */
dns_find_rr(struct dns_request * dns,const struct dns_header * reply)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  */
dns_qualify_name(const char * string)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  */
dns_make_name(const char * string,char * buf)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  */
dns_unmake_name(char * name)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  */
dns_decompress_name(const struct dns_header * reply,const char * name,char * buf)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  */
dns_send_packet(struct dns_request * dns)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  */
dns_timer_expired(struct retry_timer * timer,int fail)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  */
dns_xfer_deliver_raw(struct xfer_interface * socket,const void * data,size_t len)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  */
dns_xfer_close(struct xfer_interface * socket,int rc)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  */
dns_resolv(struct resolv_interface * resolv,const char * name,struct sockaddr * sa)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  */
apply_dns_settings(void)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