|
|
1.1 root 1: /*
2: * Copyright (C) 2006 Michael Brown <[email protected]>.
3: *
4: * Portions copyright (C) 2004 Anselm M. Hoffmeister
5: * <[email protected]>.
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 <ipxe/refcnt.h>
31: #include <ipxe/iobuf.h>
32: #include <ipxe/xfer.h>
33: #include <ipxe/open.h>
34: #include <ipxe/resolv.h>
35: #include <ipxe/retry.h>
36: #include <ipxe/tcpip.h>
37: #include <ipxe/settings.h>
38: #include <ipxe/features.h>
39: #include <ipxe/dns.h>
40:
41: /** @file
42: *
43: * DNS protocol
44: *
45: */
46:
47: FEATURE ( FEATURE_PROTOCOL, "DNS", DHCP_EB_FEATURE_DNS, 1 );
48:
49: /* Disambiguate the various error causes */
50: #define ENXIO_NO_RECORD __einfo_error ( EINFO_ENXIO_NO_RECORD )
51: #define EINFO_ENXIO_NO_RECORD \
52: __einfo_uniqify ( EINFO_ENXIO, 0x01, "DNS name does not exist" )
53: #define ENXIO_NO_NAMESERVER __einfo_error ( EINFO_ENXIO_NO_NAMESERVER )
54: #define EINFO_ENXIO_NO_NAMESERVER \
55: __einfo_uniqify ( EINFO_ENXIO, 0x02, "No DNS servers available" )
56:
57: /** The DNS server */
58: static struct sockaddr_tcpip nameserver = {
59: .st_port = htons ( DNS_PORT ),
60: };
61:
62: /** The local domain */
63: static char *localdomain;
64:
65: /** A DNS request */
66: struct dns_request {
67: /** Reference counter */
68: struct refcnt refcnt;
69: /** Name resolution interface */
70: struct interface resolv;
71: /** Data transfer interface */
72: struct interface socket;
73: /** Retry timer */
74: struct retry_timer timer;
75:
76: /** Socket address to fill in with resolved address */
77: struct sockaddr sa;
78: /** Current query packet */
79: struct dns_query query;
80: /** Location of query info structure within current packet
81: *
82: * The query info structure is located immediately after the
83: * compressed name.
84: */
85: struct dns_query_info *qinfo;
86: /** Recursion counter */
87: unsigned int recursion;
88: };
89:
90: /**
91: * Mark DNS request as complete
92: *
93: * @v dns DNS request
94: * @v rc Return status code
95: */
96: static void dns_done ( struct dns_request *dns, int rc ) {
97:
98: /* Stop the retry timer */
99: stop_timer ( &dns->timer );
100:
101: /* Shut down interfaces */
102: intf_shutdown ( &dns->socket, rc );
103: intf_shutdown ( &dns->resolv, rc );
104: }
105:
106: /**
107: * Compare DNS reply name against the query name from the original request
108: *
109: * @v dns DNS request
110: * @v reply DNS reply
111: * @v rname Reply name
112: * @ret zero Names match
113: * @ret non-zero Names do not match
114: */
115: static int dns_name_cmp ( struct dns_request *dns,
116: const struct dns_header *reply,
117: const char *rname ) {
118: const char *qname = dns->query.payload;
119: int i;
120:
121: while ( 1 ) {
122: /* Obtain next section of rname */
123: while ( ( *rname ) & 0xc0 ) {
124: rname = ( ( ( char * ) reply ) +
125: ( ntohs( *((uint16_t *)rname) ) & ~0xc000 ));
126: }
127: /* Check that lengths match */
128: if ( *rname != *qname )
129: return -1;
130: /* If length is zero, we have reached the end */
131: if ( ! *qname )
132: return 0;
133: /* Check that data matches */
134: for ( i = *qname + 1; i > 0 ; i-- ) {
135: if ( *(rname++) != *(qname++) )
136: return -1;
137: }
138: }
139: }
140:
141: /**
142: * Skip over a (possibly compressed) DNS name
143: *
144: * @v name DNS name
145: * @ret name Next DNS name
146: */
147: static const char * dns_skip_name ( const char *name ) {
148: while ( 1 ) {
149: if ( ! *name ) {
150: /* End of name */
151: return ( name + 1);
152: }
153: if ( *name & 0xc0 ) {
154: /* Start of a compressed name */
155: return ( name + 2 );
156: }
157: /* Uncompressed name portion */
158: name += *name + 1;
159: }
160: }
161:
162: /**
163: * Find an RR in a reply packet corresponding to our query
164: *
165: * @v dns DNS request
166: * @v reply DNS reply
167: * @ret rr DNS RR, or NULL if not found
168: */
169: static union dns_rr_info * dns_find_rr ( struct dns_request *dns,
170: const struct dns_header *reply ) {
171: int i, cmp;
172: const char *p = ( ( char * ) reply ) + sizeof ( struct dns_header );
173: union dns_rr_info *rr_info;
174:
175: /* Skip over the questions section */
176: for ( i = ntohs ( reply->qdcount ) ; i > 0 ; i-- ) {
177: p = dns_skip_name ( p ) + sizeof ( struct dns_query_info );
178: }
179:
180: /* Process the answers section */
181: for ( i = ntohs ( reply->ancount ) ; i > 0 ; i-- ) {
182: cmp = dns_name_cmp ( dns, reply, p );
183: p = dns_skip_name ( p );
184: rr_info = ( ( union dns_rr_info * ) p );
185: if ( cmp == 0 )
186: return rr_info;
187: p += ( sizeof ( rr_info->common ) +
188: ntohs ( rr_info->common.rdlength ) );
189: }
190:
191: return NULL;
192: }
193:
194: /**
195: * Append DHCP domain name if available and name is not fully qualified
196: *
197: * @v string Name as a NUL-terminated string
198: * @ret fqdn Fully-qualified domain name, malloc'd copy
199: *
200: * The caller must free fqdn which is allocated even if the name is already
201: * fully qualified.
202: */
203: static char * dns_qualify_name ( const char *string ) {
204: char *fqdn;
205:
206: /* Leave unchanged if already fully-qualified or no local domain */
207: if ( ( ! localdomain ) || ( strchr ( string, '.' ) != 0 ) )
208: return strdup ( string );
209:
210: /* Append local domain to name */
211: asprintf ( &fqdn, "%s.%s", string, localdomain );
212: return fqdn;
213: }
214:
215: /**
216: * Convert a standard NUL-terminated string to a DNS name
217: *
218: * @v string Name as a NUL-terminated string
219: * @v buf Buffer in which to place DNS name
220: * @ret next Byte following constructed DNS name
221: *
222: * DNS names consist of "<length>element" pairs.
223: */
224: static char * dns_make_name ( const char *string, char *buf ) {
225: char *length_byte = buf++;
226: char c;
227:
228: while ( ( c = *(string++) ) ) {
229: if ( c == '.' ) {
230: *length_byte = buf - length_byte - 1;
231: length_byte = buf;
232: }
233: *(buf++) = c;
234: }
235: *length_byte = buf - length_byte - 1;
236: *(buf++) = '\0';
237: return buf;
238: }
239:
240: /**
241: * Convert an uncompressed DNS name to a NUL-terminated string
242: *
243: * @v name DNS name
244: * @ret string NUL-terminated string
245: *
246: * Produce a printable version of a DNS name. Used only for debugging.
247: */
248: static inline char * dns_unmake_name ( char *name ) {
249: char *p;
250: unsigned int len;
251:
252: p = name;
253: while ( ( len = *p ) ) {
254: *(p++) = '.';
255: p += len;
256: }
257:
258: return name + 1;
259: }
260:
261: /**
262: * Decompress a DNS name
263: *
264: * @v reply DNS replay
265: * @v name DNS name
266: * @v buf Buffer into which to decompress DNS name
267: * @ret next Byte following decompressed DNS name
268: */
269: static char * dns_decompress_name ( const struct dns_header *reply,
270: const char *name, char *buf ) {
271: int i, len;
272:
273: do {
274: /* Obtain next section of name */
275: while ( ( *name ) & 0xc0 ) {
276: name = ( ( char * ) reply +
277: ( ntohs ( *((uint16_t *)name) ) & ~0xc000 ) );
278: }
279: /* Copy data */
280: len = *name;
281: for ( i = len + 1 ; i > 0 ; i-- ) {
282: *(buf++) = *(name++);
283: }
284: } while ( len );
285: return buf;
286: }
287:
288: /**
289: * Send next packet in DNS request
290: *
291: * @v dns DNS request
292: */
293: static int dns_send_packet ( struct dns_request *dns ) {
294: static unsigned int qid = 0;
295: size_t qlen;
296:
297: /* Increment query ID */
298: dns->query.dns.id = htons ( ++qid );
299:
300: DBGC ( dns, "DNS %p sending query ID %d\n", dns, qid );
301:
302: /* Start retransmission timer */
303: start_timer ( &dns->timer );
304:
305: /* Send the data */
306: qlen = ( ( ( void * ) dns->qinfo ) - ( ( void * ) &dns->query )
307: + sizeof ( dns->qinfo ) );
308: return xfer_deliver_raw ( &dns->socket, &dns->query, qlen );
309: }
310:
311: /**
312: * Handle DNS retransmission timer expiry
313: *
314: * @v timer Retry timer
315: * @v fail Failure indicator
316: */
317: static void dns_timer_expired ( struct retry_timer *timer, int fail ) {
318: struct dns_request *dns =
319: container_of ( timer, struct dns_request, timer );
320:
321: if ( fail ) {
322: dns_done ( dns, -ETIMEDOUT );
323: } else {
324: dns_send_packet ( dns );
325: }
326: }
327:
328: /**
329: * Receive new data
330: *
331: * @v dns DNS request
332: * @v iobuf I/O buffer
333: * @v meta Data transfer metadata
334: * @ret rc Return status code
335: */
336: static int dns_xfer_deliver ( struct dns_request *dns,
337: struct io_buffer *iobuf,
338: struct xfer_metadata *meta __unused ) {
339: const struct dns_header *reply = iobuf->data;
340: union dns_rr_info *rr_info;
341: struct sockaddr_in *sin;
342: unsigned int qtype = dns->qinfo->qtype;
343: int rc;
344:
345: /* Sanity check */
346: if ( iob_len ( iobuf ) < sizeof ( *reply ) ) {
347: DBGC ( dns, "DNS %p received underlength packet length %zd\n",
348: dns, iob_len ( iobuf ) );
349: rc = -EINVAL;
350: goto done;
351: }
352:
353: /* Check reply ID matches query ID */
354: if ( reply->id != dns->query.dns.id ) {
355: DBGC ( dns, "DNS %p received unexpected reply ID %d "
356: "(wanted %d)\n", dns, ntohs ( reply->id ),
357: ntohs ( dns->query.dns.id ) );
358: rc = -EINVAL;
359: goto done;
360: }
361:
362: DBGC ( dns, "DNS %p received reply ID %d\n", dns, ntohs ( reply->id ));
363:
364: /* Stop the retry timer. After this point, each code path
365: * must either restart the timer by calling dns_send_packet(),
366: * or mark the DNS operation as complete by calling
367: * dns_done()
368: */
369: stop_timer ( &dns->timer );
370:
371: /* Search through response for useful answers. Do this
372: * multiple times, to take advantage of useful nameservers
373: * which send us e.g. the CNAME *and* the A record for the
374: * pointed-to name.
375: */
376: while ( ( rr_info = dns_find_rr ( dns, reply ) ) ) {
377: switch ( rr_info->common.type ) {
378:
379: case htons ( DNS_TYPE_A ):
380:
381: /* Found the target A record */
382: DBGC ( dns, "DNS %p found address %s\n",
383: dns, inet_ntoa ( rr_info->a.in_addr ) );
384: sin = ( struct sockaddr_in * ) &dns->sa;
385: sin->sin_family = AF_INET;
386: sin->sin_addr = rr_info->a.in_addr;
387:
388: /* Return resolved address */
389: resolv_done ( &dns->resolv, &dns->sa );
390:
391: /* Mark operation as complete */
392: dns_done ( dns, 0 );
393: rc = 0;
394: goto done;
395:
396: case htons ( DNS_TYPE_CNAME ):
397:
398: /* Found a CNAME record; update query and recurse */
399: DBGC ( dns, "DNS %p found CNAME\n", dns );
400: dns->qinfo = ( void * ) dns_decompress_name ( reply,
401: rr_info->cname.cname,
402: dns->query.payload );
403: dns->qinfo->qtype = htons ( DNS_TYPE_A );
404: dns->qinfo->qclass = htons ( DNS_CLASS_IN );
405:
406: /* Terminate the operation if we recurse too far */
407: if ( ++dns->recursion > DNS_MAX_CNAME_RECURSION ) {
408: DBGC ( dns, "DNS %p recursion exceeded\n",
409: dns );
410: dns_done ( dns, -ELOOP );
411: rc = 0;
412: goto done;
413: }
414: break;
415:
416: default:
417: DBGC ( dns, "DNS %p got unknown record type %d\n",
418: dns, ntohs ( rr_info->common.type ) );
419: break;
420: }
421: }
422:
423: /* Determine what to do next based on the type of query we
424: * issued and the reponse we received
425: */
426: switch ( qtype ) {
427:
428: case htons ( DNS_TYPE_A ):
429: /* We asked for an A record and got nothing;
430: * try the CNAME.
431: */
432: DBGC ( dns, "DNS %p found no A record; trying CNAME\n", dns );
433: dns->qinfo->qtype = htons ( DNS_TYPE_CNAME );
434: dns_send_packet ( dns );
435: rc = 0;
436: goto done;
437:
438: case htons ( DNS_TYPE_CNAME ):
439: /* We asked for a CNAME record. If we got a response
440: * (i.e. if the next A query is already set up), then
441: * issue it, otherwise abort.
442: */
443: if ( dns->qinfo->qtype == htons ( DNS_TYPE_A ) ) {
444: dns_send_packet ( dns );
445: rc = 0;
446: goto done;
447: } else {
448: DBGC ( dns, "DNS %p found no CNAME record\n", dns );
449: dns_done ( dns, -ENXIO_NO_RECORD );
450: rc = 0;
451: goto done;
452: }
453:
454: default:
455: assert ( 0 );
456: dns_done ( dns, -EINVAL );
457: rc = -EINVAL;
458: goto done;
459: }
460:
461: done:
462: /* Free I/O buffer */
463: free_iob ( iobuf );
464: return rc;
465: }
466:
467: /**
468: * Receive new data
469: *
470: * @v dns DNS request
471: * @v rc Reason for close
472: */
473: static void dns_xfer_close ( struct dns_request *dns, int rc ) {
474:
475: if ( ! rc )
476: rc = -ECONNABORTED;
477:
478: dns_done ( dns, rc );
479: }
480:
481: /** DNS socket interface operations */
482: static struct interface_operation dns_socket_operations[] = {
483: INTF_OP ( xfer_deliver, struct dns_request *, dns_xfer_deliver ),
484: INTF_OP ( intf_close, struct dns_request *, dns_xfer_close ),
485: };
486:
487: /** DNS socket interface descriptor */
488: static struct interface_descriptor dns_socket_desc =
489: INTF_DESC ( struct dns_request, socket, dns_socket_operations );
490:
491: /** DNS resolver interface operations */
492: static struct interface_operation dns_resolv_op[] = {
493: INTF_OP ( intf_close, struct dns_request *, dns_done ),
494: };
495:
496: /** DNS resolver interface descriptor */
497: static struct interface_descriptor dns_resolv_desc =
498: INTF_DESC ( struct dns_request, resolv, dns_resolv_op );
499:
500: /**
501: * Resolve name using DNS
502: *
503: * @v resolv Name resolution interface
504: * @v name Name to resolve
505: * @v sa Socket address to fill in
506: * @ret rc Return status code
507: */
508: static int dns_resolv ( struct interface *resolv,
509: const char *name, struct sockaddr *sa ) {
510: struct dns_request *dns;
511: char *fqdn;
512: int rc;
513:
514: /* Fail immediately if no DNS servers */
515: if ( ! nameserver.st_family ) {
516: DBG ( "DNS not attempting to resolve \"%s\": "
517: "no DNS servers\n", name );
518: rc = -ENXIO_NO_NAMESERVER;
519: goto err_no_nameserver;
520: }
521:
522: /* Ensure fully-qualified domain name if DHCP option was given */
523: fqdn = dns_qualify_name ( name );
524: if ( ! fqdn ) {
525: rc = -ENOMEM;
526: goto err_qualify_name;
527: }
528:
529: /* Allocate DNS structure */
530: dns = zalloc ( sizeof ( *dns ) );
531: if ( ! dns ) {
532: rc = -ENOMEM;
533: goto err_alloc_dns;
534: }
535: ref_init ( &dns->refcnt, NULL );
536: intf_init ( &dns->resolv, &dns_resolv_desc, &dns->refcnt );
537: intf_init ( &dns->socket, &dns_socket_desc, &dns->refcnt );
538: timer_init ( &dns->timer, dns_timer_expired, &dns->refcnt );
539: memcpy ( &dns->sa, sa, sizeof ( dns->sa ) );
540:
541: /* Create query */
542: dns->query.dns.flags = htons ( DNS_FLAG_QUERY | DNS_FLAG_OPCODE_QUERY |
543: DNS_FLAG_RD );
544: dns->query.dns.qdcount = htons ( 1 );
545: dns->qinfo = ( void * ) dns_make_name ( fqdn, dns->query.payload );
546: dns->qinfo->qtype = htons ( DNS_TYPE_A );
547: dns->qinfo->qclass = htons ( DNS_CLASS_IN );
548:
549: /* Open UDP connection */
550: if ( ( rc = xfer_open_socket ( &dns->socket, SOCK_DGRAM,
551: ( struct sockaddr * ) &nameserver,
552: NULL ) ) != 0 ) {
553: DBGC ( dns, "DNS %p could not open socket: %s\n",
554: dns, strerror ( rc ) );
555: goto err_open_socket;
556: }
557:
558: /* Send first DNS packet */
559: dns_send_packet ( dns );
560:
561: /* Attach parent interface, mortalise self, and return */
562: intf_plug_plug ( &dns->resolv, resolv );
563: ref_put ( &dns->refcnt );
564: free ( fqdn );
565: return 0;
566:
567: err_open_socket:
568: err_alloc_dns:
569: ref_put ( &dns->refcnt );
570: err_qualify_name:
571: free ( fqdn );
572: err_no_nameserver:
573: return rc;
574: }
575:
576: /** DNS name resolver */
577: struct resolver dns_resolver __resolver ( RESOLV_NORMAL ) = {
578: .name = "DNS",
579: .resolv = dns_resolv,
580: };
581:
582: /******************************************************************************
583: *
584: * Settings
585: *
586: ******************************************************************************
587: */
588:
589: /** DNS server setting */
590: struct setting dns_setting __setting ( SETTING_IPv4_EXTRA ) = {
591: .name = "dns",
592: .description = "DNS server",
593: .tag = DHCP_DNS_SERVERS,
594: .type = &setting_type_ipv4,
595: };
596:
597: /** Domain name setting */
598: struct setting domain_setting __setting ( SETTING_IPv4_EXTRA ) = {
599: .name = "domain",
600: .description = "DNS domain",
601: .tag = DHCP_DOMAIN_NAME,
602: .type = &setting_type_string,
603: };
604:
605: /**
606: * Apply DNS settings
607: *
608: * @ret rc Return status code
609: */
610: static int apply_dns_settings ( void ) {
611: struct sockaddr_in *sin_nameserver =
612: ( struct sockaddr_in * ) &nameserver;
613: int len;
614:
615: /* Fetch DNS server address */
616: nameserver.st_family = 0;
617: if ( ( len = fetch_ipv4_setting ( NULL, &dns_setting,
618: &sin_nameserver->sin_addr ) ) >= 0 ){
619: nameserver.st_family = AF_INET;
620: DBG ( "DNS using nameserver %s\n",
621: inet_ntoa ( sin_nameserver->sin_addr ) );
622: }
623:
624: /* Get local domain DHCP option */
625: free ( localdomain );
626: if ( ( len = fetch_string_setting_copy ( NULL, &domain_setting,
627: &localdomain ) ) < 0 ) {
628: DBG ( "DNS could not fetch local domain: %s\n",
629: strerror ( len ) );
630: }
631: if ( localdomain )
632: DBG ( "DNS local domain %s\n", localdomain );
633:
634: return 0;
635: }
636:
637: /** DNS settings applicator */
638: struct settings_applicator dns_applicator __settings_applicator = {
639: .apply = apply_dns_settings,
640: };
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.