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., 675 Mass Ave, Cambridge, MA 02139, USA.
00020  */
00021 
00022 FILE_LICENCE ( GPL2_OR_LATER );
00023 
00024 #include <stdint.h>
00025 #include <stdlib.h>
00026 #include <string.h>
00027 #include <stdio.h>
00028 #include <errno.h>
00029 #include <byteswap.h>
00030 #include <gpxe/refcnt.h>
00031 #include <gpxe/xfer.h>
00032 #include <gpxe/open.h>
00033 #include <gpxe/resolv.h>
00034 #include <gpxe/retry.h>
00035 #include <gpxe/tcpip.h>
00036 #include <gpxe/settings.h>
00037 #include <gpxe/features.h>
00038 #include <gpxe/dns.h>
00039 
00040 /** @file
00041  *
00042  * DNS protocol
00043  *
00044  */
00045 
00046 FEATURE ( FEATURE_PROTOCOL, "DNS", DHCP_EB_FEATURE_DNS, 1 );
00047 
00048 /** The DNS server */
00049 static struct sockaddr_tcpip nameserver = {
00050         .st_port = htons ( DNS_PORT ),
00051 };
00052 
00053 /** The local domain */
00054 static char *localdomain;
00055 
00056 /** A DNS request */
00057 struct dns_request {
00058         /** Reference counter */
00059         struct refcnt refcnt;
00060         /** Name resolution interface */
00061         struct resolv_interface resolv;
00062         /** Data transfer interface */
00063         struct xfer_interface socket;
00064         /** Retry timer */
00065         struct retry_timer timer;
00066 
00067         /** Socket address to fill in with resolved address */
00068         struct sockaddr sa;
00069         /** Current query packet */
00070         struct dns_query query;
00071         /** Location of query info structure within current packet
00072          *
00073          * The query info structure is located immediately after the
00074          * compressed name.
00075          */
00076         struct dns_query_info *qinfo;
00077         /** Recursion counter */
00078         unsigned int recursion;
00079 };
00080 
00081 /**
00082  * Mark DNS request as complete
00083  *
00084  * @v dns               DNS request
00085  * @v rc                Return status code
00086  */
00087 static void dns_done ( struct dns_request *dns, int rc ) {
00088 
00089         /* Stop the retry timer */
00090         stop_timer ( &dns->timer );
00091 
00092         /* Close data transfer interface */
00093         xfer_nullify ( &dns->socket );
00094         xfer_close ( &dns->socket, rc );
00095 
00096         /* Mark name resolution as complete */
00097         resolv_done ( &dns->resolv, &dns->sa, rc );
00098 }
00099 
00100 /**
00101  * Compare DNS reply name against the query name from the original request
00102  *
00103  * @v dns               DNS request
00104  * @v reply             DNS reply
00105  * @v rname             Reply name
00106  * @ret zero            Names match
00107  * @ret non-zero        Names do not match
00108  */
00109 static int dns_name_cmp ( struct dns_request *dns,
00110                           const struct dns_header *reply, 
00111                           const char *rname ) {
00112         const char *qname = dns->query.payload;
00113         int i;
00114 
00115         while ( 1 ) {
00116                 /* Obtain next section of rname */
00117                 while ( ( *rname ) & 0xc0 ) {
00118                         rname = ( ( ( char * ) reply ) +
00119                                   ( ntohs( *((uint16_t *)rname) ) & ~0xc000 ));
00120                 }
00121                 /* Check that lengths match */
00122                 if ( *rname != *qname )
00123                         return -1;
00124                 /* If length is zero, we have reached the end */
00125                 if ( ! *qname )
00126                         return 0;
00127                 /* Check that data matches */
00128                 for ( i = *qname + 1; i > 0 ; i-- ) {
00129                         if ( *(rname++) != *(qname++) )
00130                                 return -1;
00131                 }
00132         }
00133 }
00134 
00135 /**
00136  * Skip over a (possibly compressed) DNS name
00137  *
00138  * @v name              DNS name
00139  * @ret name            Next DNS name
00140  */
00141 static const char * dns_skip_name ( const char *name ) {
00142         while ( 1 ) {
00143                 if ( ! *name ) {
00144                         /* End of name */
00145                         return ( name + 1);
00146                 }
00147                 if ( *name & 0xc0 ) {
00148                         /* Start of a compressed name */
00149                         return ( name + 2 );
00150                 }
00151                 /* Uncompressed name portion */
00152                 name += *name + 1;
00153         }
00154 }
00155 
00156 /**
00157  * Find an RR in a reply packet corresponding to our query
00158  *
00159  * @v dns               DNS request
00160  * @v reply             DNS reply
00161  * @ret rr              DNS RR, or NULL if not found
00162  */
00163 static union dns_rr_info * dns_find_rr ( struct dns_request *dns,
00164                                          const struct dns_header *reply ) {
00165         int i, cmp;
00166         const char *p = ( ( char * ) reply ) + sizeof ( struct dns_header );
00167         union dns_rr_info *rr_info;
00168 
00169         /* Skip over the questions section */
00170         for ( i = ntohs ( reply->qdcount ) ; i > 0 ; i-- ) {
00171                 p = dns_skip_name ( p ) + sizeof ( struct dns_query_info );
00172         }
00173 
00174         /* Process the answers section */
00175         for ( i = ntohs ( reply->ancount ) ; i > 0 ; i-- ) {
00176                 cmp = dns_name_cmp ( dns, reply, p );
00177                 p = dns_skip_name ( p );
00178                 rr_info = ( ( union dns_rr_info * ) p );
00179                 if ( cmp == 0 )
00180                         return rr_info;
00181                 p += ( sizeof ( rr_info->common ) +
00182                        ntohs ( rr_info->common.rdlength ) );
00183         }
00184 
00185         return NULL;
00186 }
00187 
00188 /**
00189  * Append DHCP domain name if available and name is not fully qualified
00190  *
00191  * @v string            Name as a NUL-terminated string
00192  * @ret fqdn            Fully-qualified domain name, malloc'd copy
00193  *
00194  * The caller must free fqdn which is allocated even if the name is already
00195  * fully qualified.
00196  */
00197 static char * dns_qualify_name ( const char *string ) {
00198         char *fqdn;
00199 
00200         /* Leave unchanged if already fully-qualified or no local domain */
00201         if ( ( ! localdomain ) || ( strchr ( string, '.' ) != 0 ) )
00202                 return strdup ( string );
00203 
00204         /* Append local domain to name */
00205         asprintf ( &fqdn, "%s.%s", string, localdomain );
00206         return fqdn;
00207 }
00208 
00209 /**
00210  * Convert a standard NUL-terminated string to a DNS name
00211  *
00212  * @v string            Name as a NUL-terminated string
00213  * @v buf               Buffer in which to place DNS name
00214  * @ret next            Byte following constructed DNS name
00215  *
00216  * DNS names consist of "<length>element" pairs.
00217  */
00218 static char * dns_make_name ( const char *string, char *buf ) {
00219         char *length_byte = buf++;
00220         char c;
00221 
00222         while ( ( c = *(string++) ) ) {
00223                 if ( c == '.' ) {
00224                         *length_byte = buf - length_byte - 1;
00225                         length_byte = buf;
00226                 }
00227                 *(buf++) = c;
00228         }
00229         *length_byte = buf - length_byte - 1;
00230         *(buf++) = '\0';
00231         return buf;
00232 }
00233 
00234 /**
00235  * Convert an uncompressed DNS name to a NUL-terminated string
00236  *
00237  * @v name              DNS name
00238  * @ret string          NUL-terminated string
00239  *
00240  * Produce a printable version of a DNS name.  Used only for debugging.
00241  */
00242 static inline char * dns_unmake_name ( char *name ) {
00243         char *p;
00244         unsigned int len;
00245 
00246         p = name;
00247         while ( ( len = *p ) ) {
00248                 *(p++) = '.';
00249                 p += len;
00250         }
00251 
00252         return name + 1;
00253 }
00254 
00255 /**
00256  * Decompress a DNS name
00257  *
00258  * @v reply             DNS replay
00259  * @v name              DNS name
00260  * @v buf               Buffer into which to decompress DNS name
00261  * @ret next            Byte following decompressed DNS name
00262  */
00263 static char * dns_decompress_name ( const struct dns_header *reply,
00264                                     const char *name, char *buf ) {
00265         int i, len;
00266 
00267         do {
00268                 /* Obtain next section of name */
00269                 while ( ( *name ) & 0xc0 ) {
00270                         name = ( ( char * ) reply +
00271                                  ( ntohs ( *((uint16_t *)name) ) & ~0xc000 ) );
00272                 }
00273                 /* Copy data */
00274                 len = *name;
00275                 for ( i = len + 1 ; i > 0 ; i-- ) {
00276                         *(buf++) = *(name++);
00277                 }
00278         } while ( len );
00279         return buf;
00280 }
00281 
00282 /**
00283  * Send next packet in DNS request
00284  *
00285  * @v dns               DNS request
00286  */
00287 static int dns_send_packet ( struct dns_request *dns ) {
00288         static unsigned int qid = 0;
00289         size_t qlen;
00290 
00291         /* Increment query ID */
00292         dns->query.dns.id = htons ( ++qid );
00293 
00294         DBGC ( dns, "DNS %p sending query ID %d\n", dns, qid );
00295 
00296         /* Start retransmission timer */
00297         start_timer ( &dns->timer );
00298 
00299         /* Send the data */
00300         qlen = ( ( ( void * ) dns->qinfo ) - ( ( void * ) &dns->query )
00301                  + sizeof ( dns->qinfo ) );
00302         return xfer_deliver_raw ( &dns->socket, &dns->query, qlen );
00303 }
00304 
00305 /**
00306  * Handle DNS retransmission timer expiry
00307  *
00308  * @v timer             Retry timer
00309  * @v fail              Failure indicator
00310  */
00311 static void dns_timer_expired ( struct retry_timer *timer, int fail ) {
00312         struct dns_request *dns =
00313                 container_of ( timer, struct dns_request, timer );
00314 
00315         if ( fail ) {
00316                 dns_done ( dns, -ETIMEDOUT );
00317         } else {
00318                 dns_send_packet ( dns );
00319         }
00320 }
00321 
00322 /**
00323  * Receive new data
00324  *
00325  * @v socket            UDP socket
00326  * @v data              DNS reply
00327  * @v len               Length of DNS reply
00328  * @ret rc              Return status code
00329  */
00330 static int dns_xfer_deliver_raw ( struct xfer_interface *socket,
00331                                   const void *data, size_t len ) {
00332         struct dns_request *dns =
00333                 container_of ( socket, struct dns_request, socket );
00334         const struct dns_header *reply = data;
00335         union dns_rr_info *rr_info;
00336         struct sockaddr_in *sin;
00337         unsigned int qtype = dns->qinfo->qtype;
00338 
00339         /* Sanity check */
00340         if ( len < sizeof ( *reply ) ) {
00341                 DBGC ( dns, "DNS %p received underlength packet length %zd\n",
00342                        dns, len );
00343                 return -EINVAL;
00344         }
00345 
00346         /* Check reply ID matches query ID */
00347         if ( reply->id != dns->query.dns.id ) {
00348                 DBGC ( dns, "DNS %p received unexpected reply ID %d "
00349                        "(wanted %d)\n", dns, ntohs ( reply->id ),
00350                        ntohs ( dns->query.dns.id ) );
00351                 return -EINVAL;
00352         }
00353 
00354         DBGC ( dns, "DNS %p received reply ID %d\n", dns, ntohs ( reply->id ));
00355 
00356         /* Stop the retry timer.  After this point, each code path
00357          * must either restart the timer by calling dns_send_packet(),
00358          * or mark the DNS operation as complete by calling
00359          * dns_done()
00360          */
00361         stop_timer ( &dns->timer );
00362 
00363         /* Search through response for useful answers.  Do this
00364          * multiple times, to take advantage of useful nameservers
00365          * which send us e.g. the CNAME *and* the A record for the
00366          * pointed-to name.
00367          */
00368         while ( ( rr_info = dns_find_rr ( dns, reply ) ) ) {
00369                 switch ( rr_info->common.type ) {
00370 
00371                 case htons ( DNS_TYPE_A ):
00372 
00373                         /* Found the target A record */
00374                         DBGC ( dns, "DNS %p found address %s\n",
00375                                dns, inet_ntoa ( rr_info->a.in_addr ) );
00376                         sin = ( struct sockaddr_in * ) &dns->sa;
00377                         sin->sin_family = AF_INET;
00378                         sin->sin_addr = rr_info->a.in_addr;
00379 
00380                         /* Mark operation as complete */
00381                         dns_done ( dns, 0 );
00382                         return 0;
00383 
00384                 case htons ( DNS_TYPE_CNAME ):
00385 
00386                         /* Found a CNAME record; update query and recurse */
00387                         DBGC ( dns, "DNS %p found CNAME\n", dns );
00388                         dns->qinfo = ( void * ) dns_decompress_name ( reply,
00389                                                          rr_info->cname.cname,
00390                                                          dns->query.payload );
00391                         dns->qinfo->qtype = htons ( DNS_TYPE_A );
00392                         dns->qinfo->qclass = htons ( DNS_CLASS_IN );
00393                         
00394                         /* Terminate the operation if we recurse too far */
00395                         if ( ++dns->recursion > DNS_MAX_CNAME_RECURSION ) {
00396                                 DBGC ( dns, "DNS %p recursion exceeded\n",
00397                                        dns );
00398                                 dns_done ( dns, -ELOOP );
00399                                 return 0;
00400                         }
00401                         break;
00402 
00403                 default:
00404                         DBGC ( dns, "DNS %p got unknown record type %d\n",
00405                                dns, ntohs ( rr_info->common.type ) );
00406                         break;
00407                 }
00408         }
00409         
00410         /* Determine what to do next based on the type of query we
00411          * issued and the reponse we received
00412          */
00413         switch ( qtype ) {
00414 
00415         case htons ( DNS_TYPE_A ):
00416                 /* We asked for an A record and got nothing;
00417                  * try the CNAME.
00418                  */
00419                 DBGC ( dns, "DNS %p found no A record; trying CNAME\n", dns );
00420                 dns->qinfo->qtype = htons ( DNS_TYPE_CNAME );
00421                 dns_send_packet ( dns );
00422                 return 0;
00423 
00424         case htons ( DNS_TYPE_CNAME ):
00425                 /* We asked for a CNAME record.  If we got a response
00426                  * (i.e. if the next A query is already set up), then
00427                  * issue it, otherwise abort.
00428                  */
00429                 if ( dns->qinfo->qtype == htons ( DNS_TYPE_A ) ) {
00430                         dns_send_packet ( dns );
00431                         return 0;
00432                 } else {
00433                         DBGC ( dns, "DNS %p found no CNAME record\n", dns );
00434                         dns_done ( dns, -ENXIO );
00435                         return 0;
00436                 }
00437 
00438         default:
00439                 assert ( 0 );
00440                 dns_done ( dns, -EINVAL );
00441                 return 0;
00442         }
00443 }
00444 
00445 /**
00446  * Receive new data
00447  *
00448  * @v socket            UDP socket
00449  * @v rc                Reason for close
00450  */
00451 static void dns_xfer_close ( struct xfer_interface *socket, int rc ) {
00452         struct dns_request *dns =
00453                 container_of ( socket, struct dns_request, socket );
00454 
00455         if ( ! rc )
00456                 rc = -ECONNABORTED;
00457 
00458         dns_done ( dns, rc );
00459 }
00460 
00461 /** DNS socket operations */
00462 static struct xfer_interface_operations dns_socket_operations = {
00463         .close          = dns_xfer_close,
00464         .vredirect      = xfer_vreopen,
00465         .window         = unlimited_xfer_window,
00466         .alloc_iob      = default_xfer_alloc_iob,
00467         .deliver_iob    = xfer_deliver_as_raw,
00468         .deliver_raw    = dns_xfer_deliver_raw,
00469 };
00470 
00471 /**
00472  * Resolve name using DNS
00473  *
00474  * @v resolv            Name resolution interface
00475  * @v name              Name to resolve
00476  * @v sa                Socket address to fill in
00477  * @ret rc              Return status code
00478  */
00479 static int dns_resolv ( struct resolv_interface *resolv,
00480                         const char *name, struct sockaddr *sa ) {
00481         struct dns_request *dns;
00482         char *fqdn;
00483         int rc;
00484 
00485         /* Fail immediately if no DNS servers */
00486         if ( ! nameserver.st_family ) {
00487                 DBG ( "DNS not attempting to resolve \"%s\": "
00488                       "no DNS servers\n", name );
00489                 rc = -ENXIO;
00490                 goto err_no_nameserver;
00491         }
00492 
00493         /* Ensure fully-qualified domain name if DHCP option was given */
00494         fqdn = dns_qualify_name ( name );
00495         if ( ! fqdn ) {
00496                 rc = -ENOMEM;
00497                 goto err_qualify_name;
00498         }
00499 
00500         /* Allocate DNS structure */
00501         dns = zalloc ( sizeof ( *dns ) );
00502         if ( ! dns ) {
00503                 rc = -ENOMEM;
00504                 goto err_alloc_dns;
00505         }
00506         resolv_init ( &dns->resolv, &null_resolv_ops, &dns->refcnt );
00507         xfer_init ( &dns->socket, &dns_socket_operations, &dns->refcnt );
00508         dns->timer.expired = dns_timer_expired;
00509         memcpy ( &dns->sa, sa, sizeof ( dns->sa ) );
00510 
00511         /* Create query */
00512         dns->query.dns.flags = htons ( DNS_FLAG_QUERY | DNS_FLAG_OPCODE_QUERY |
00513                                        DNS_FLAG_RD );
00514         dns->query.dns.qdcount = htons ( 1 );
00515         dns->qinfo = ( void * ) dns_make_name ( fqdn, dns->query.payload );
00516         dns->qinfo->qtype = htons ( DNS_TYPE_A );
00517         dns->qinfo->qclass = htons ( DNS_CLASS_IN );
00518 
00519         /* Open UDP connection */
00520         if ( ( rc = xfer_open_socket ( &dns->socket, SOCK_DGRAM,
00521                                        ( struct sockaddr * ) &nameserver,
00522                                        NULL ) ) != 0 ) {
00523                 DBGC ( dns, "DNS %p could not open socket: %s\n",
00524                        dns, strerror ( rc ) );
00525                 goto err_open_socket;
00526         }
00527 
00528         /* Send first DNS packet */
00529         dns_send_packet ( dns );
00530 
00531         /* Attach parent interface, mortalise self, and return */
00532         resolv_plug_plug ( &dns->resolv, resolv );
00533         ref_put ( &dns->refcnt );
00534         free ( fqdn );
00535         return 0;       
00536 
00537  err_open_socket:
00538  err_alloc_dns:
00539         ref_put ( &dns->refcnt );
00540  err_qualify_name:
00541         free ( fqdn );
00542  err_no_nameserver:
00543         return rc;
00544 }
00545 
00546 /** DNS name resolver */
00547 struct resolver dns_resolver __resolver ( RESOLV_NORMAL ) = {
00548         .name = "DNS",
00549         .resolv = dns_resolv,
00550 };
00551 
00552 /******************************************************************************
00553  *
00554  * Settings
00555  *
00556  ******************************************************************************
00557  */
00558 
00559 /** DNS server setting */
00560 struct setting dns_setting __setting = {
00561         .name = "dns",
00562         .description = "DNS server",
00563         .tag = DHCP_DNS_SERVERS,
00564         .type = &setting_type_ipv4,
00565 };
00566 
00567 /** Domain name setting */
00568 struct setting domain_setting __setting = {
00569         .name = "domain",
00570         .description = "Local domain",
00571         .tag = DHCP_DOMAIN_NAME,
00572         .type = &setting_type_string,
00573 };
00574 
00575 /**
00576  * Apply DNS settings
00577  *
00578  * @ret rc              Return status code
00579  */
00580 static int apply_dns_settings ( void ) {
00581         struct sockaddr_in *sin_nameserver =
00582                 ( struct sockaddr_in * ) &nameserver;
00583         int len;
00584 
00585         if ( ( len = fetch_ipv4_setting ( NULL, &dns_setting,
00586                                           &sin_nameserver->sin_addr ) ) >= 0 ){
00587                 sin_nameserver->sin_family = AF_INET;
00588                 DBG ( "DNS using nameserver %s\n",
00589                       inet_ntoa ( sin_nameserver->sin_addr ) );
00590         }
00591 
00592         /* Get local domain DHCP option */
00593         if ( ( len = fetch_string_setting_copy ( NULL, &domain_setting,
00594                                                  &localdomain ) ) >= 0 )
00595                 DBG ( "DNS local domain %s\n", localdomain );
00596 
00597         return 0;
00598 }
00599 
00600 /** DNS settings applicator */
00601 struct settings_applicator dns_applicator __settings_applicator = {
00602         .apply = apply_dns_settings,
00603 };

Generated on Tue Apr 6 20:01:11 2010 for gPXE by  doxygen 1.5.7.1