iPXE
dns.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
00003  *
00004  * Portions copyright (C) 2004 Anselm M. Hoffmeister
00005  * <stockholm@users.sourceforge.net>.
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License as
00009  * published by the Free Software Foundation; either version 2 of the
00010  * License, or any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful, but
00013  * WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015  * General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00020  * 02110-1301, USA.
00021  *
00022  * You can also choose to distribute this program under the terms of
00023  * the Unmodified Binary Distribution Licence (as given in the file
00024  * COPYING.UBDL), provided that you have satisfied its requirements.
00025  */
00026 
00027 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
00028 
00029 #include <stdint.h>
00030 #include <stdlib.h>
00031 #include <string.h>
00032 #include <stdio.h>
00033 #include <ctype.h>
00034 #include <errno.h>
00035 #include <byteswap.h>
00036 #include <ipxe/refcnt.h>
00037 #include <ipxe/iobuf.h>
00038 #include <ipxe/xfer.h>
00039 #include <ipxe/open.h>
00040 #include <ipxe/resolv.h>
00041 #include <ipxe/retry.h>
00042 #include <ipxe/tcpip.h>
00043 #include <ipxe/settings.h>
00044 #include <ipxe/features.h>
00045 #include <ipxe/job.h>
00046 #include <ipxe/dhcp.h>
00047 #include <ipxe/dhcpv6.h>
00048 #include <ipxe/dns.h>
00049 
00050 /** @file
00051  *
00052  * DNS protocol
00053  *
00054  */
00055 
00056 FEATURE ( FEATURE_PROTOCOL, "DNS", DHCP_EB_FEATURE_DNS, 1 );
00057 
00058 /* Disambiguate the various error causes */
00059 #define ENXIO_NO_RECORD __einfo_error ( EINFO_ENXIO_NO_RECORD )
00060 #define EINFO_ENXIO_NO_RECORD \
00061         __einfo_uniqify ( EINFO_ENXIO, 0x01, "DNS name does not exist" )
00062 #define ENXIO_NO_NAMESERVER __einfo_error ( EINFO_ENXIO_NO_NAMESERVER )
00063 #define EINFO_ENXIO_NO_NAMESERVER \
00064         __einfo_uniqify ( EINFO_ENXIO, 0x02, "No DNS servers available" )
00065 
00066 /** The DNS server */
00067 static union {
00068         struct sockaddr sa;
00069         struct sockaddr_tcpip st;
00070         struct sockaddr_in sin;
00071         struct sockaddr_in6 sin6;
00072 } nameserver = {
00073         .st = {
00074                 .st_port = htons ( DNS_PORT ),
00075         },
00076 };
00077 
00078 /** The DNS search list */
00079 static struct dns_name dns_search;
00080 
00081 /**
00082  * Encode a DNS name using RFC1035 encoding
00083  *
00084  * @v string            DNS name as a string
00085  * @v name              DNS name to fill in
00086  * @ret len             Length of DNS name, or negative error
00087  */
00088 int dns_encode ( const char *string, struct dns_name *name ) {
00089         uint8_t *start = ( name->data + name->offset );
00090         uint8_t *end = ( name->data + name->len );
00091         uint8_t *dst = start;
00092         size_t len = 0;
00093         char c;
00094 
00095         /* Encode name */
00096         while ( ( c = *(string++) ) ) {
00097 
00098                 /* Handle '.' separators */
00099                 if ( c == '.' ) {
00100 
00101                         /* Reject consecutive '.' */
00102                         if ( ( len == 0 ) && ( dst != start ) )
00103                                 return -EINVAL;
00104 
00105                         /* Terminate if this is the trailing '.' */
00106                         if ( *string == '\0' )
00107                                 break;
00108 
00109                         /* Reject initial non-terminating '.' */
00110                         if ( len == 0 )
00111                                 return -EINVAL;
00112 
00113                         /* Reset length */
00114                         len = 0;
00115 
00116                 } else {
00117 
00118                         /* Increment length */
00119                         len++;
00120 
00121                         /* Check for overflow */
00122                         if ( len > DNS_MAX_LABEL_LEN )
00123                                 return -EINVAL;
00124                 }
00125 
00126                 /* Copy byte, update length */
00127                 if ( ++dst < end ) {
00128                         *dst = c;
00129                         dst[-len] = len;
00130                 }
00131         }
00132 
00133         /* Add terminating root marker */
00134         if ( len )
00135                 dst++;
00136         if ( dst < end )
00137                 *dst = '\0';
00138         dst++;
00139 
00140         return ( dst - start );
00141 }
00142 
00143 /**
00144  * Find start of valid label within an RFC1035-encoded DNS name
00145  *
00146  * @v name              DNS name
00147  * @v offset            Current offset
00148  * @ret offset          Offset of label, or negative error
00149  */
00150 static int dns_label ( struct dns_name *name, size_t offset ) {
00151         const uint8_t *byte;
00152         const uint16_t *word;
00153         size_t len;
00154         size_t ptr;
00155 
00156         while ( 1 ) {
00157 
00158                 /* Fail if we have overrun the DNS name */
00159                 if ( ( offset + sizeof ( *byte) ) > name->len )
00160                         return -EINVAL;
00161                 byte = ( name->data + offset );
00162 
00163                 /* Follow compression pointer, if applicable */
00164                 if ( DNS_IS_COMPRESSED ( *byte ) ) {
00165 
00166                         /* Fail if we have overrun the DNS name */
00167                         if ( ( offset + sizeof ( *word ) ) > name->len )
00168                                 return -EINVAL;
00169                         word = ( name->data + offset );
00170 
00171                         /* Extract pointer to new offset */
00172                         ptr = DNS_COMPRESSED_OFFSET ( ntohs ( *word ) );
00173 
00174                         /* Fail if pointer does not point backwards.
00175                          * (This guarantees termination of the
00176                          * function.)
00177                          */
00178                         if ( ptr >= offset )
00179                                 return -EINVAL;
00180 
00181                         /* Continue from new offset */
00182                         offset = ptr;
00183                         continue;
00184                 }
00185 
00186                 /* Fail if we have overrun the DNS name */
00187                 len = *byte;
00188                 if ( ( offset + sizeof ( *byte ) + len ) > name->len )
00189                         return -EINVAL;
00190 
00191                 /* We have a valid label */
00192                 return offset;
00193         }
00194 }
00195 
00196 /**
00197  * Decode RFC1035-encoded DNS name
00198  *
00199  * @v name              DNS name
00200  * @v data              Output buffer
00201  * @v len               Length of output buffer
00202  * @ret len             Length of decoded DNS name, or negative error
00203  */
00204 int dns_decode ( struct dns_name *name, char *data, size_t len ) {
00205         unsigned int recursion_limit = name->len; /* Generous upper bound */
00206         int offset = name->offset;
00207         const uint8_t *label;
00208         size_t decoded_len = 0;
00209         size_t label_len;
00210         size_t copy_len;
00211 
00212         while ( recursion_limit-- ) {
00213 
00214                 /* Find valid DNS label */
00215                 offset = dns_label ( name, offset );
00216                 if ( offset < 0 )
00217                         return offset;
00218 
00219                 /* Terminate if we have reached the root */
00220                 label = ( name->data + offset );
00221                 label_len = *(label++);
00222                 if ( label_len == 0 ) {
00223                         if ( decoded_len < len )
00224                                 *data = '\0';
00225                         return decoded_len;
00226                 }
00227 
00228                 /* Prepend '.' if applicable */
00229                 if ( decoded_len && ( decoded_len++ < len ) )
00230                         *(data++) = '.';
00231 
00232                 /* Copy label to output buffer */
00233                 copy_len = ( ( decoded_len < len ) ? ( len - decoded_len ) : 0);
00234                 if ( copy_len > label_len )
00235                         copy_len = label_len;
00236                 memcpy ( data, label, copy_len );
00237                 data += copy_len;
00238                 decoded_len += label_len;
00239 
00240                 /* Move to next label */
00241                 offset += ( sizeof ( *label ) + label_len );
00242         }
00243 
00244         /* Recursion limit exceeded */
00245         return -EINVAL;
00246 }
00247 
00248 /**
00249  * Compare DNS names for equality
00250  *
00251  * @v first             First DNS name
00252  * @v second            Second DNS name
00253  * @ret rc              Return status code
00254  */
00255 int dns_compare ( struct dns_name *first, struct dns_name *second ) {
00256         unsigned int recursion_limit = first->len; /* Generous upper bound */
00257         int first_offset = first->offset;
00258         int second_offset = second->offset;
00259         const uint8_t *first_label;
00260         const uint8_t *second_label;
00261         size_t label_len;
00262         size_t len;
00263 
00264         while ( recursion_limit-- ) {
00265 
00266                 /* Find valid DNS labels */
00267                 first_offset = dns_label ( first, first_offset );
00268                 if ( first_offset < 0 )
00269                         return first_offset;
00270                 second_offset = dns_label ( second, second_offset );
00271                 if ( second_offset < 0 )
00272                         return second_offset;
00273 
00274                 /* Compare label lengths */
00275                 first_label = ( first->data + first_offset );
00276                 second_label = ( second->data + second_offset );
00277                 label_len = *(first_label++);
00278                 if ( label_len != *(second_label++) )
00279                         return -ENOENT;
00280                 len = ( sizeof ( *first_label ) + label_len );
00281 
00282                 /* Terminate if we have reached the root */
00283                 if ( label_len == 0 )
00284                         return 0;
00285 
00286                 /* Compare label contents (case-insensitively) */
00287                 while ( label_len-- ) {
00288                         if ( tolower ( *(first_label++) ) !=
00289                              tolower ( *(second_label++) ) )
00290                                 return -ENOENT;
00291                 }
00292 
00293                 /* Move to next labels */
00294                 first_offset += len;
00295                 second_offset += len;
00296         }
00297 
00298         /* Recursion limit exceeded */
00299         return -EINVAL;
00300 }
00301 
00302 /**
00303  * Copy a DNS name
00304  *
00305  * @v src               Source DNS name
00306  * @v dst               Destination DNS name
00307  * @ret len             Length of copied DNS name, or negative error
00308  */
00309 int dns_copy ( struct dns_name *src, struct dns_name *dst ) {
00310         unsigned int recursion_limit = src->len; /* Generous upper bound */
00311         int src_offset = src->offset;
00312         size_t dst_offset = dst->offset;
00313         const uint8_t *label;
00314         size_t label_len;
00315         size_t copy_len;
00316         size_t len;
00317 
00318         while ( recursion_limit-- ) {
00319 
00320                 /* Find valid DNS label */
00321                 src_offset = dns_label ( src, src_offset );
00322                 if ( src_offset < 0 )
00323                         return src_offset;
00324 
00325                 /* Copy as an uncompressed label */
00326                 label = ( src->data + src_offset );
00327                 label_len = *label;
00328                 len = ( sizeof ( *label ) + label_len );
00329                 copy_len = ( ( dst_offset < dst->len ) ?
00330                              ( dst->len - dst_offset ) : 0 );
00331                 if ( copy_len > len )
00332                         copy_len = len;
00333                 memcpy ( ( dst->data + dst_offset ), label, copy_len );
00334                 src_offset += len;
00335                 dst_offset += len;
00336 
00337                 /* Terminate if we have reached the root */
00338                 if ( label_len == 0 )
00339                         return ( dst_offset - dst->offset );
00340         }
00341 
00342         /* Recursion limit exceeded */
00343         return -EINVAL;
00344 }
00345 
00346 /**
00347  * Skip RFC1035-encoded DNS name
00348  *
00349  * @v name              DNS name
00350  * @ret offset          Offset to next name, or negative error
00351  */
00352 int dns_skip ( struct dns_name *name ) {
00353         unsigned int recursion_limit = name->len; /* Generous upper bound */
00354         int offset = name->offset;
00355         int prev_offset;
00356         const uint8_t *label;
00357         size_t label_len;
00358 
00359         while ( recursion_limit-- ) {
00360 
00361                 /* Find valid DNS label */
00362                 prev_offset = offset;
00363                 offset = dns_label ( name, prev_offset );
00364                 if ( offset < 0 )
00365                         return offset;
00366 
00367                 /* Terminate if we have reached a compression pointer */
00368                 if ( offset != prev_offset )
00369                         return ( prev_offset + sizeof ( uint16_t ) );
00370 
00371                 /* Skip this label */
00372                 label = ( name->data + offset );
00373                 label_len = *label;
00374                 offset += ( sizeof ( *label ) + label_len );
00375 
00376                 /* Terminate if we have reached the root */
00377                 if ( label_len == 0 )
00378                         return offset;
00379         }
00380 
00381         /* Recursion limit exceeded */
00382         return -EINVAL;
00383 }
00384 
00385 /**
00386  * Skip RFC1035-encoded DNS name in search list
00387  *
00388  * @v name              DNS name
00389  * @ret offset          Offset to next non-empty name, or negative error
00390  */
00391 static int dns_skip_search ( struct dns_name *name ) {
00392         int offset;
00393 
00394         /* Find next name */
00395         offset = dns_skip ( name );
00396         if ( offset < 0 )
00397                 return offset;
00398 
00399         /* Skip over any subsequent empty names (e.g. due to padding
00400          * bytes used in the NDP DNSSL option).
00401          */
00402         while ( ( offset < ( ( int ) name->len ) ) &&
00403                 ( *( ( uint8_t * ) ( name->data + offset ) ) == 0 ) ) {
00404                 offset++;
00405         }
00406 
00407         return offset;
00408 }
00409 
00410 /**
00411  * Transcribe DNS name (for debugging)
00412  *
00413  * @v name              DNS name
00414  * @ret string          Transcribed DNS name
00415  */
00416 static const char * dns_name ( struct dns_name *name ) {
00417         static char buf[256];
00418         int len;
00419 
00420         len = dns_decode ( name, buf, ( sizeof ( buf ) - 1 /* NUL */ ) );
00421         return ( ( len < 0 ) ? "<INVALID>" : buf );
00422 }
00423 
00424 /**
00425  * Name a DNS query type (for debugging)
00426  *
00427  * @v type              Query type (in network byte order)
00428  * @ret name            Type name
00429  */
00430 static const char * dns_type ( uint16_t type ) {
00431         switch ( type ) {
00432         case htons ( DNS_TYPE_A ):      return "A";
00433         case htons ( DNS_TYPE_AAAA ):   return "AAAA";
00434         case htons ( DNS_TYPE_CNAME ):  return "CNAME";
00435         default:                        return "<UNKNOWN>";
00436         }
00437 }
00438 
00439 /** A DNS request */
00440 struct dns_request {
00441         /** Reference counter */
00442         struct refcnt refcnt;
00443         /** Name resolution interface */
00444         struct interface resolv;
00445         /** Data transfer interface */
00446         struct interface socket;
00447         /** Retry timer */
00448         struct retry_timer timer;
00449 
00450         /** Socket address to fill in with resolved address */
00451         union {
00452                 struct sockaddr sa;
00453                 struct sockaddr_in sin;
00454                 struct sockaddr_in6 sin6;
00455         } address;
00456         /** Initial query type */
00457         uint16_t qtype;
00458         /** Buffer for current query */
00459         struct {
00460                 /** Query header */
00461                 struct dns_header query;
00462                 /** Name buffer */
00463                 char name[DNS_MAX_NAME_LEN];
00464                 /** Space for question */
00465                 struct dns_question padding;
00466         } __attribute__ (( packed )) buf;
00467         /** Current query name */
00468         struct dns_name name;
00469         /** Question within current query */
00470         struct dns_question *question;
00471         /** Length of current query */
00472         size_t len;
00473         /** Offset of search suffix within current query */
00474         size_t offset;
00475         /** Search list */
00476         struct dns_name search;
00477         /** Recursion counter */
00478         unsigned int recursion;
00479 };
00480 
00481 /**
00482  * Mark DNS request as complete
00483  *
00484  * @v dns               DNS request
00485  * @v rc                Return status code
00486  */
00487 static void dns_done ( struct dns_request *dns, int rc ) {
00488 
00489         /* Stop the retry timer */
00490         stop_timer ( &dns->timer );
00491 
00492         /* Shut down interfaces */
00493         intf_shutdown ( &dns->socket, rc );
00494         intf_shutdown ( &dns->resolv, rc );
00495 }
00496 
00497 /**
00498  * Mark DNS request as resolved and complete
00499  *
00500  * @v dns               DNS request
00501  * @v rc                Return status code
00502  */
00503 static void dns_resolved ( struct dns_request *dns ) {
00504 
00505         DBGC ( dns, "DNS %p found address %s\n",
00506                dns, sock_ntoa ( &dns->address.sa ) );
00507 
00508         /* Return resolved address */
00509         resolv_done ( &dns->resolv, &dns->address.sa );
00510 
00511         /* Mark operation as complete */
00512         dns_done ( dns, 0 );
00513 }
00514 
00515 /**
00516  * Construct DNS question
00517  *
00518  * @v dns               DNS request
00519  * @ret rc              Return status code
00520  */
00521 static int dns_question ( struct dns_request *dns ) {
00522         static struct dns_name search_root = {
00523                 .data = "",
00524                 .len = 1,
00525         };
00526         struct dns_name *search = &dns->search;
00527         int len;
00528         size_t offset;
00529 
00530         /* Use root suffix if search list is empty */
00531         if ( search->offset == search->len )
00532                 search = &search_root;
00533 
00534         /* Overwrite current suffix */
00535         dns->name.offset = dns->offset;
00536         len = dns_copy ( search, &dns->name );
00537         if ( len < 0 )
00538                 return len;
00539 
00540         /* Sanity check */
00541         offset = ( dns->name.offset + len );
00542         if ( offset > dns->name.len ) {
00543                 DBGC ( dns, "DNS %p name is too long\n", dns );
00544                 return -EINVAL;
00545         }
00546 
00547         /* Construct question */
00548         dns->question = ( ( ( void * ) &dns->buf ) + offset );
00549         dns->question->qtype = dns->qtype;
00550         dns->question->qclass = htons ( DNS_CLASS_IN );
00551 
00552         /* Store length */
00553         dns->len = ( offset + sizeof ( *(dns->question) ) );
00554 
00555         /* Restore name */
00556         dns->name.offset = offsetof ( typeof ( dns->buf ), name );
00557 
00558         DBGC2 ( dns, "DNS %p question is %s type %s\n", dns,
00559                 dns_name ( &dns->name ), dns_type ( dns->question->qtype ) );
00560 
00561         return 0;
00562 }
00563 
00564 /**
00565  * Send DNS query
00566  *
00567  * @v dns               DNS request
00568  * @ret rc              Return status code
00569  */
00570 static int dns_send_packet ( struct dns_request *dns ) {
00571         struct dns_header *query = &dns->buf.query;
00572 
00573         /* Start retransmission timer */
00574         start_timer ( &dns->timer );
00575 
00576         /* Generate query identifier */
00577         query->id = random();
00578 
00579         /* Send query */
00580         DBGC ( dns, "DNS %p sending query ID %#04x for %s type %s\n", dns,
00581                ntohs ( query->id ), dns_name ( &dns->name ),
00582                dns_type ( dns->question->qtype ) );
00583 
00584         /* Send the data */
00585         return xfer_deliver_raw ( &dns->socket, query, dns->len );
00586 }
00587 
00588 /**
00589  * Handle DNS retransmission timer expiry
00590  *
00591  * @v timer             Retry timer
00592  * @v fail              Failure indicator
00593  */
00594 static void dns_timer_expired ( struct retry_timer *timer, int fail ) {
00595         struct dns_request *dns =
00596                 container_of ( timer, struct dns_request, timer );
00597 
00598         if ( fail ) {
00599                 dns_done ( dns, -ETIMEDOUT );
00600         } else {
00601                 dns_send_packet ( dns );
00602         }
00603 }
00604 
00605 /**
00606  * Receive new data
00607  *
00608  * @v dns               DNS request
00609  * @v iobuf             I/O buffer
00610  * @v meta              Data transfer metadata
00611  * @ret rc              Return status code
00612  */
00613 static int dns_xfer_deliver ( struct dns_request *dns,
00614                               struct io_buffer *iobuf,
00615                               struct xfer_metadata *meta __unused ) {
00616         struct dns_header *response = iobuf->data;
00617         struct dns_header *query = &dns->buf.query;
00618         unsigned int qtype = dns->question->qtype;
00619         struct dns_name buf;
00620         union dns_rr *rr;
00621         int offset;
00622         size_t answer_offset;
00623         size_t next_offset;
00624         size_t rdlength;
00625         size_t name_len;
00626         int rc;
00627 
00628         /* Sanity check */
00629         if ( iob_len ( iobuf ) < sizeof ( *response ) ) {
00630                 DBGC ( dns, "DNS %p received underlength packet length %zd\n",
00631                        dns, iob_len ( iobuf ) );
00632                 rc = -EINVAL;
00633                 goto done;
00634         }
00635 
00636         /* Check response ID matches query ID */
00637         if ( response->id != query->id ) {
00638                 DBGC ( dns, "DNS %p received unexpected response ID %#04x "
00639                        "(wanted %d)\n", dns, ntohs ( response->id ),
00640                        ntohs ( query->id ) );
00641                 rc = -EINVAL;
00642                 goto done;
00643         }
00644         DBGC ( dns, "DNS %p received response ID %#04x\n",
00645                dns, ntohs ( response->id ) );
00646 
00647         /* Check that we have exactly one question */
00648         if ( response->qdcount != htons ( 1 ) ) {
00649                 DBGC ( dns, "DNS %p received response with %d questions\n",
00650                        dns, ntohs ( response->qdcount ) );
00651                 rc = -EINVAL;
00652                 goto done;
00653         }
00654 
00655         /* Skip question section */
00656         buf.data = iobuf->data;
00657         buf.offset = sizeof ( *response );
00658         buf.len = iob_len ( iobuf );
00659         offset = dns_skip ( &buf );
00660         if ( offset < 0 ) {
00661                 rc = offset;
00662                 DBGC ( dns, "DNS %p received response with malformed "
00663                        "question: %s\n", dns, strerror ( rc ) );
00664                 goto done;
00665         }
00666         answer_offset = ( offset + sizeof ( struct dns_question ) );
00667 
00668         /* Search through response for useful answers.  Do this
00669          * multiple times, to take advantage of useful nameservers
00670          * which send us e.g. the CNAME *and* the A record for the
00671          * pointed-to name.
00672          */
00673         for ( buf.offset = answer_offset ; buf.offset != buf.len ;
00674               buf.offset = next_offset ) {
00675 
00676                 /* Check for valid name */
00677                 offset = dns_skip ( &buf );
00678                 if ( offset < 0 ) {
00679                         rc = offset;
00680                         DBGC ( dns, "DNS %p received response with malformed "
00681                                "answer: %s\n", dns, strerror ( rc ) );
00682                         goto done;
00683                 }
00684 
00685                 /* Check for sufficient space for resource record */
00686                 rr = ( buf.data + offset );
00687                 if ( ( offset + sizeof ( rr->common ) ) > buf.len ) {
00688                         DBGC ( dns, "DNS %p received response with underlength "
00689                                "RR\n", dns );
00690                         rc = -EINVAL;
00691                         goto done;
00692                 }
00693                 rdlength = ntohs ( rr->common.rdlength );
00694                 next_offset = ( offset + sizeof ( rr->common ) + rdlength );
00695                 if ( next_offset > buf.len ) {
00696                         DBGC ( dns, "DNS %p received response with underlength "
00697                                "RR\n", dns );
00698                         rc = -EINVAL;
00699                         goto done;
00700                 }
00701 
00702                 /* Skip non-matching names */
00703                 if ( dns_compare ( &buf, &dns->name ) != 0 ) {
00704                         DBGC2 ( dns, "DNS %p ignoring response for %s type "
00705                                 "%s\n", dns, dns_name ( &buf ),
00706                                 dns_type ( rr->common.type ) );
00707                         continue;
00708                 }
00709 
00710                 /* Handle answer */
00711                 switch ( rr->common.type ) {
00712 
00713                 case htons ( DNS_TYPE_AAAA ):
00714 
00715                         /* Found the target AAAA record */
00716                         if ( rdlength < sizeof ( dns->address.sin6.sin6_addr )){
00717                                 DBGC ( dns, "DNS %p received response with "
00718                                        "underlength AAAA\n", dns );
00719                                 rc = -EINVAL;
00720                                 goto done;
00721                         }
00722                         dns->address.sin6.sin6_family = AF_INET6;
00723                         memcpy ( &dns->address.sin6.sin6_addr,
00724                                  &rr->aaaa.in6_addr,
00725                                  sizeof ( dns->address.sin6.sin6_addr ) );
00726                         dns_resolved ( dns );
00727                         rc = 0;
00728                         goto done;
00729 
00730                 case htons ( DNS_TYPE_A ):
00731 
00732                         /* Found the target A record */
00733                         if ( rdlength < sizeof ( dns->address.sin.sin_addr ) ) {
00734                                 DBGC ( dns, "DNS %p received response with "
00735                                        "underlength A\n", dns );
00736                                 rc = -EINVAL;
00737                                 goto done;
00738                         }
00739                         dns->address.sin.sin_family = AF_INET;
00740                         dns->address.sin.sin_addr = rr->a.in_addr;
00741                         dns_resolved ( dns );
00742                         rc = 0;
00743                         goto done;
00744 
00745                 case htons ( DNS_TYPE_CNAME ):
00746 
00747                         /* Terminate the operation if we recurse too far */
00748                         if ( ++dns->recursion > DNS_MAX_CNAME_RECURSION ) {
00749                                 DBGC ( dns, "DNS %p recursion exceeded\n",
00750                                        dns );
00751                                 rc = -ELOOP;
00752                                 dns_done ( dns, rc );
00753                                 goto done;
00754                         }
00755 
00756                         /* Found a CNAME record; update query and recurse */
00757                         buf.offset = ( offset + sizeof ( rr->cname ) );
00758                         DBGC ( dns, "DNS %p found CNAME %s\n",
00759                                dns, dns_name ( &buf ) );
00760                         dns->search.offset = dns->search.len;
00761                         name_len = dns_copy ( &buf, &dns->name );
00762                         dns->offset = ( offsetof ( typeof ( dns->buf ), name ) +
00763                                         name_len - 1 /* Strip root label */ );
00764                         if ( ( rc = dns_question ( dns ) ) != 0 ) {
00765                                 dns_done ( dns, rc );
00766                                 goto done;
00767                         }
00768                         next_offset = answer_offset;
00769                         break;
00770 
00771                 default:
00772                         DBGC ( dns, "DNS %p got unknown record type %d\n",
00773                                dns, ntohs ( rr->common.type ) );
00774                         break;
00775                 }
00776         }
00777 
00778         /* Stop the retry timer.  After this point, each code path
00779          * must either restart the timer by calling dns_send_packet(),
00780          * or mark the DNS operation as complete by calling
00781          * dns_done()
00782          */
00783         stop_timer ( &dns->timer );
00784 
00785         /* Determine what to do next based on the type of query we
00786          * issued and the response we received
00787          */
00788         switch ( qtype ) {
00789 
00790         case htons ( DNS_TYPE_AAAA ):
00791                 /* We asked for an AAAA record and got nothing; try
00792                  * the A.
00793                  */
00794                 DBGC ( dns, "DNS %p found no AAAA record; trying A\n", dns );
00795                 dns->question->qtype = htons ( DNS_TYPE_A );
00796                 dns_send_packet ( dns );
00797                 rc = 0;
00798                 goto done;
00799 
00800         case htons ( DNS_TYPE_A ):
00801                 /* We asked for an A record and got nothing;
00802                  * try the CNAME.
00803                  */
00804                 DBGC ( dns, "DNS %p found no A record; trying CNAME\n", dns );
00805                 dns->question->qtype = htons ( DNS_TYPE_CNAME );
00806                 dns_send_packet ( dns );
00807                 rc = 0;
00808                 goto done;
00809 
00810         case htons ( DNS_TYPE_CNAME ):
00811                 /* We asked for a CNAME record.  If we got a response
00812                  * (i.e. if the next AAAA/A query is already set up),
00813                  * then issue it.
00814                  */
00815                 if ( qtype == dns->qtype ) {
00816                         dns_send_packet ( dns );
00817                         rc = 0;
00818                         goto done;
00819                 }
00820 
00821                 /* If we have already reached the end of the search list,
00822                  * then terminate lookup.
00823                  */
00824                 if ( dns->search.offset == dns->search.len ) {
00825                         DBGC ( dns, "DNS %p found no CNAME record\n", dns );
00826                         rc = -ENXIO_NO_RECORD;
00827                         dns_done ( dns, rc );
00828                         goto done;
00829                 }
00830 
00831                 /* Move to next entry in search list.  This can never fail,
00832                  * since we have already used this entry.
00833                  */
00834                 DBGC ( dns, "DNS %p found no CNAME record; trying next "
00835                        "suffix\n", dns );
00836                 dns->search.offset = dns_skip_search ( &dns->search );
00837                 if ( ( rc = dns_question ( dns ) ) != 0 ) {
00838                         dns_done ( dns, rc );
00839                         goto done;
00840                 }
00841                 dns_send_packet ( dns );
00842                 goto done;
00843 
00844         default:
00845                 assert ( 0 );
00846                 rc = -EINVAL;
00847                 dns_done ( dns, rc );
00848                 goto done;
00849         }
00850 
00851  done:
00852         /* Free I/O buffer */
00853         free_iob ( iobuf );
00854         return rc;
00855 }
00856 
00857 /**
00858  * Receive new data
00859  *
00860  * @v dns               DNS request
00861  * @v rc                Reason for close
00862  */
00863 static void dns_xfer_close ( struct dns_request *dns, int rc ) {
00864 
00865         if ( ! rc )
00866                 rc = -ECONNABORTED;
00867 
00868         dns_done ( dns, rc );
00869 }
00870 
00871 /**
00872  * Report job progress
00873  *
00874  * @v dns               DNS request
00875  * @v progress          Progress report to fill in
00876  * @ret ongoing_rc      Ongoing job status code (if known)
00877  */
00878 static int dns_progress ( struct dns_request *dns,
00879                           struct job_progress *progress ) {
00880         int len;
00881 
00882         /* Show current question as progress message */
00883         len = dns_decode ( &dns->name, progress->message,
00884                            ( sizeof ( progress->message ) - 1 /* NUL */ ) );
00885         if ( len < 0 ) {
00886                 /* Ignore undecodable names */
00887                 progress->message[0] = '\0';
00888         }
00889 
00890         return 0;
00891 }
00892 
00893 /** DNS socket interface operations */
00894 static struct interface_operation dns_socket_operations[] = {
00895         INTF_OP ( xfer_deliver, struct dns_request *, dns_xfer_deliver ),
00896         INTF_OP ( intf_close, struct dns_request *, dns_xfer_close ),
00897 };
00898 
00899 /** DNS socket interface descriptor */
00900 static struct interface_descriptor dns_socket_desc =
00901         INTF_DESC ( struct dns_request, socket, dns_socket_operations );
00902 
00903 /** DNS resolver interface operations */
00904 static struct interface_operation dns_resolv_op[] = {
00905         INTF_OP ( job_progress, struct dns_request *, dns_progress ),
00906         INTF_OP ( intf_close, struct dns_request *, dns_done ),
00907 };
00908 
00909 /** DNS resolver interface descriptor */
00910 static struct interface_descriptor dns_resolv_desc =
00911         INTF_DESC ( struct dns_request, resolv, dns_resolv_op );
00912 
00913 /**
00914  * Resolve name using DNS
00915  *
00916  * @v resolv            Name resolution interface
00917  * @v name              Name to resolve
00918  * @v sa                Socket address to fill in
00919  * @ret rc              Return status code
00920  */
00921 static int dns_resolv ( struct interface *resolv,
00922                         const char *name, struct sockaddr *sa ) {
00923         struct dns_request *dns;
00924         struct dns_header *query;
00925         size_t search_len;
00926         int name_len;
00927         int rc;
00928 
00929         /* Fail immediately if no DNS servers */
00930         if ( ! nameserver.sa.sa_family ) {
00931                 DBG ( "DNS not attempting to resolve \"%s\": "
00932                       "no DNS servers\n", name );
00933                 rc = -ENXIO_NO_NAMESERVER;
00934                 goto err_no_nameserver;
00935         }
00936 
00937         /* Determine whether or not to use search list */
00938         search_len = ( strchr ( name, '.' ) ? 0 : dns_search.len );
00939 
00940         /* Allocate DNS structure */
00941         dns = zalloc ( sizeof ( *dns ) + search_len );
00942         if ( ! dns ) {
00943                 rc = -ENOMEM;
00944                 goto err_alloc_dns;
00945         }
00946         ref_init ( &dns->refcnt, NULL );
00947         intf_init ( &dns->resolv, &dns_resolv_desc, &dns->refcnt );
00948         intf_init ( &dns->socket, &dns_socket_desc, &dns->refcnt );
00949         timer_init ( &dns->timer, dns_timer_expired, &dns->refcnt );
00950         memcpy ( &dns->address.sa, sa, sizeof ( dns->address.sa ) );
00951         dns->search.data = ( ( ( void * ) dns ) + sizeof ( *dns ) );
00952         dns->search.len = search_len;
00953         memcpy ( dns->search.data, dns_search.data, search_len );
00954 
00955         /* Determine initial query type */
00956         switch ( nameserver.sa.sa_family ) {
00957         case AF_INET:
00958                 dns->qtype = htons ( DNS_TYPE_A );
00959                 break;
00960         case AF_INET6:
00961                 dns->qtype = htons ( DNS_TYPE_AAAA );
00962                 break;
00963         default:
00964                 rc = -ENOTSUP;
00965                 goto err_type;
00966         }
00967 
00968         /* Construct query */
00969         query = &dns->buf.query;
00970         query->flags = htons ( DNS_FLAG_RD );
00971         query->qdcount = htons ( 1 );
00972         dns->name.data = &dns->buf;
00973         dns->name.offset = offsetof ( typeof ( dns->buf ), name );
00974         dns->name.len = offsetof ( typeof ( dns->buf ), padding );
00975         name_len = dns_encode ( name, &dns->name );
00976         if ( name_len < 0 ) {
00977                 rc = name_len;
00978                 goto err_encode;
00979         }
00980         dns->offset = ( offsetof ( typeof ( dns->buf ), name ) +
00981                         name_len - 1 /* Strip root label */ );
00982         if ( ( rc = dns_question ( dns ) ) != 0 )
00983                 goto err_question;
00984 
00985         /* Open UDP connection */
00986         if ( ( rc = xfer_open_socket ( &dns->socket, SOCK_DGRAM,
00987                                        &nameserver.sa, NULL ) ) != 0 ) {
00988                 DBGC ( dns, "DNS %p could not open socket: %s\n",
00989                        dns, strerror ( rc ) );
00990                 goto err_open_socket;
00991         }
00992 
00993         /* Start timer to trigger first packet */
00994         start_timer_nodelay ( &dns->timer );
00995 
00996         /* Attach parent interface, mortalise self, and return */
00997         intf_plug_plug ( &dns->resolv, resolv );
00998         ref_put ( &dns->refcnt );
00999         return 0;       
01000 
01001  err_open_socket:
01002  err_question:
01003  err_encode:
01004  err_type:
01005         ref_put ( &dns->refcnt );
01006  err_alloc_dns:
01007  err_no_nameserver:
01008         return rc;
01009 }
01010 
01011 /** DNS name resolver */
01012 struct resolver dns_resolver __resolver ( RESOLV_NORMAL ) = {
01013         .name = "DNS",
01014         .resolv = dns_resolv,
01015 };
01016 
01017 /******************************************************************************
01018  *
01019  * Settings
01020  *
01021  ******************************************************************************
01022  */
01023 
01024 /**
01025  * Format DNS search list setting
01026  *
01027  * @v type              Setting type
01028  * @v raw               Raw setting value
01029  * @v raw_len           Length of raw setting value
01030  * @v buf               Buffer to contain formatted value
01031  * @v len               Length of buffer
01032  * @ret len             Length of formatted value, or negative error
01033  */
01034 static int format_dnssl_setting ( const struct setting_type *type __unused,
01035                                   const void *raw, size_t raw_len,
01036                                   char *buf, size_t len ) {
01037         struct dns_name name = {
01038                 .data = ( ( void * ) raw ),
01039                 .len = raw_len,
01040         };
01041         size_t remaining = len;
01042         size_t total = 0;
01043         int name_len;
01044 
01045         while ( name.offset < raw_len ) {
01046 
01047                 /* Decode name */
01048                 remaining = ( ( total < len ) ? ( len - total ) : 0 );
01049                 name_len = dns_decode ( &name, ( buf + total ), remaining );
01050                 if ( name_len < 0 )
01051                         return name_len;
01052                 total += name_len;
01053 
01054                 /* Move to next name */
01055                 name.offset = dns_skip_search ( &name );
01056 
01057                 /* Add separator if applicable */
01058                 if ( name.offset != raw_len ) {
01059                         if ( total < len )
01060                                 buf[total] = ' ';
01061                         total++;
01062                 }
01063         }
01064 
01065         return total;
01066 }
01067 
01068 /** A DNS search list setting type */
01069 const struct setting_type setting_type_dnssl __setting_type = {
01070         .name = "dnssl",
01071         .format = format_dnssl_setting,
01072 };
01073 
01074 /** IPv4 DNS server setting */
01075 const struct setting dns_setting __setting ( SETTING_IP4_EXTRA, dns ) = {
01076         .name = "dns",
01077         .description = "DNS server",
01078         .tag = DHCP_DNS_SERVERS,
01079         .type = &setting_type_ipv4,
01080 };
01081 
01082 /** IPv6 DNS server setting */
01083 const struct setting dns6_setting __setting ( SETTING_IP6_EXTRA, dns6 ) = {
01084         .name = "dns6",
01085         .description = "DNS server",
01086         .tag = DHCPV6_DNS_SERVERS,
01087         .type = &setting_type_ipv6,
01088         .scope = &dhcpv6_scope,
01089 };
01090 
01091 /** DNS search list */
01092 const struct setting dnssl_setting __setting ( SETTING_IP_EXTRA, dnssl ) = {
01093         .name = "dnssl",
01094         .description = "DNS search list",
01095         .tag = DHCP_DOMAIN_SEARCH,
01096         .type = &setting_type_dnssl,
01097 };
01098 
01099 /**
01100  * Apply DNS search list
01101  *
01102  */
01103 static void apply_dns_search ( void ) {
01104         char *localdomain;
01105         int len;
01106 
01107         /* Free existing search list */
01108         free ( dns_search.data );
01109         memset ( &dns_search, 0, sizeof ( dns_search ) );
01110 
01111         /* Fetch DNS search list */
01112         len = fetch_setting_copy ( NULL, &dnssl_setting, NULL, NULL,
01113                                    &dns_search.data );
01114         if ( len >= 0 ) {
01115                 dns_search.len = len;
01116                 return;
01117         }
01118 
01119         /* If no DNS search list exists, try to fetch the local domain */
01120         fetch_string_setting_copy ( NULL, &domain_setting, &localdomain );
01121         if ( localdomain ) {
01122                 len = dns_encode ( localdomain, &dns_search );
01123                 if ( len >= 0 ) {
01124                         dns_search.data = malloc ( len );
01125                         if ( dns_search.data ) {
01126                                 dns_search.len = len;
01127                                 dns_encode ( localdomain, &dns_search );
01128                         }
01129                 }
01130                 free ( localdomain );
01131                 return;
01132         }
01133 }
01134 
01135 /**
01136  * Apply DNS settings
01137  *
01138  * @ret rc              Return status code
01139  */
01140 static int apply_dns_settings ( void ) {
01141 
01142         /* Fetch DNS server address */
01143         nameserver.sa.sa_family = 0;
01144         if ( fetch_ipv6_setting ( NULL, &dns6_setting,
01145                                   &nameserver.sin6.sin6_addr ) >= 0 ) {
01146                 nameserver.sin6.sin6_family = AF_INET6;
01147         } else if ( fetch_ipv4_setting ( NULL, &dns_setting,
01148                                          &nameserver.sin.sin_addr ) >= 0 ) {
01149                 nameserver.sin.sin_family = AF_INET;
01150         }
01151         if ( nameserver.sa.sa_family ) {
01152                 DBG ( "DNS using nameserver %s\n",
01153                       sock_ntoa ( &nameserver.sa ) );
01154         }
01155 
01156         /* Fetch DNS search list */
01157         apply_dns_search();
01158         if ( DBG_LOG && ( dns_search.len != 0 ) ) {
01159                 struct dns_name name;
01160                 int offset;
01161 
01162                 DBG ( "DNS search list:" );
01163                 memcpy ( &name, &dns_search, sizeof ( name ) );
01164                 while ( name.offset != name.len ) {
01165                         DBG ( " %s", dns_name ( &name ) );
01166                         offset = dns_skip_search ( &name );
01167                         if ( offset < 0 )
01168                                 break;
01169                         name.offset = offset;
01170                 }
01171                 DBG ( "\n" );
01172         }
01173 
01174         return 0;
01175 }
01176 
01177 /** DNS settings applicator */
01178 struct settings_applicator dns_applicator __settings_applicator = {
01179         .apply = apply_dns_settings,
01180 };