dns.c File Reference

DNS protocol. More...

#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <byteswap.h>
#include <gpxe/refcnt.h>
#include <gpxe/xfer.h>
#include <gpxe/open.h>
#include <gpxe/resolv.h>
#include <gpxe/retry.h>
#include <gpxe/tcpip.h>
#include <gpxe/settings.h>
#include <gpxe/features.h>
#include <gpxe/dns.h>

Go to the source code of this file.

Data Structures

struct  dns_request
 A DNS request. More...

Functions

 FILE_LICENCE (GPL2_OR_LATER)
 FEATURE (FEATURE_PROTOCOL,"DNS", DHCP_EB_FEATURE_DNS, 1)
static void dns_done (struct dns_request *dns, int rc)
 Mark DNS request as complete.
static int dns_name_cmp (struct dns_request *dns, const struct dns_header *reply, const char *rname)
 Compare DNS reply name against the query name from the original request.
static const char * dns_skip_name (const char *name)
 Skip over a (possibly compressed) DNS name.
static union dns_rr_infodns_find_rr (struct dns_request *dns, const struct dns_header *reply)
 Find an RR in a reply packet corresponding to our query.
static char * dns_qualify_name (const char *string)
 Append DHCP domain name if available and name is not fully qualified.
static char * dns_make_name (const char *string, char *buf)
 Convert a standard NUL-terminated string to a DNS name.
static char * dns_unmake_name (char *name)
 Convert an uncompressed DNS name to a NUL-terminated string.
static char * dns_decompress_name (const struct dns_header *reply, const char *name, char *buf)
 Decompress a DNS name.
static int dns_send_packet (struct dns_request *dns)
 Send next packet in DNS request.
static void dns_timer_expired (struct retry_timer *timer, int fail)
 Handle DNS retransmission timer expiry.
static int dns_xfer_deliver_raw (struct xfer_interface *socket, const void *data, size_t len)
 Receive new data.
static void dns_xfer_close (struct xfer_interface *socket, int rc)
 Receive new data.
static int dns_resolv (struct resolv_interface *resolv, const char *name, struct sockaddr *sa)
 Resolve name using DNS.
struct resolver dns_resolver __resolver (RESOLV_NORMAL)
 DNS name resolver.
static int apply_dns_settings (void)
 Apply DNS settings.

Variables

static struct sockaddr_tcpip nameserver
 The DNS server.
static char * localdomain
 The local domain.
static struct
xfer_interface_operations 
dns_socket_operations
 DNS socket operations.
struct setting dns_setting __setting
 DNS server setting.
struct settings_applicator
dns_applicator 
__settings_applicator
 DNS settings applicator.


Detailed Description

DNS protocol.

Definition in file dns.c.


Function Documentation

FILE_LICENCE ( GPL2_OR_LATER   ) 

FEATURE ( FEATURE_PROTOCOL  ,
"DNS"  ,
DHCP_EB_FEATURE_DNS  ,
 
)

static void dns_done ( struct dns_request dns,
int  rc 
) [static]

Mark DNS request as complete.

Parameters:
dns DNS request
rc Return status code

Definition at line 87 of file dns.c.

References dns_request::resolv, resolv_done(), dns_request::sa, dns_request::socket, stop_timer(), dns_request::timer, and xfer_close().

Referenced by dns_timer_expired(), dns_xfer_close(), and dns_xfer_deliver_raw().

00087                                                          {
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 }

static int dns_name_cmp ( struct dns_request dns,
const struct dns_header reply,
const char *  rname 
) [static]

Compare DNS reply name against the query name from the original request.

Parameters:
dns DNS request
reply DNS reply
rname Reply name
Return values:
zero Names match
non-zero Names do not match

Definition at line 109 of file dns.c.

References ntohs, dns_query::payload, and dns_request::query.

Referenced by dns_find_rr().

00111                                               {
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 }

static const char* dns_skip_name ( const char *  name  )  [static]

Skip over a (possibly compressed) DNS name.

Parameters:
name DNS name
Return values:
name Next DNS name

Definition at line 141 of file dns.c.

Referenced by dns_find_rr().

00141                                                        {
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 }

static union dns_rr_info* dns_find_rr ( struct dns_request dns,
const struct dns_header reply 
) [static, write]

Find an RR in a reply packet corresponding to our query.

Parameters:
dns DNS request
reply DNS reply
Return values:
rr DNS RR, or NULL if not found

Definition at line 163 of file dns.c.

References dns_header::ancount, dns_rr_info::common, dns_name_cmp(), dns_skip_name(), ntohs, NULL, dns_header::qdcount, and dns_rr_info_common::rdlength.

Referenced by dns_xfer_deliver_raw().

00164                                                                           {
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 }

static char* dns_qualify_name ( const char *  string  )  [static]

Append DHCP domain name if available and name is not fully qualified.

Parameters:
string Name as a NUL-terminated string
Return values:
fqdn Fully-qualified domain name, malloc'd copy
The caller must free fqdn which is allocated even if the name is already fully qualified.

Definition at line 197 of file dns.c.

References asprintf(), localdomain, strchr(), and strdup().

Referenced by dns_resolv().

00197                                                       {
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 }

static char* dns_make_name ( const char *  string,
char *  buf 
) [static]

Convert a standard NUL-terminated string to a DNS name.

Parameters:
string Name as a NUL-terminated string
buf Buffer in which to place DNS name
Return values:
next Byte following constructed DNS name
DNS names consist of "<length>element" pairs.

Definition at line 218 of file dns.c.

Referenced by dns_resolv().

00218                                                               {
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 }

static char* dns_unmake_name ( char *  name  )  [inline, static]

Convert an uncompressed DNS name to a NUL-terminated string.

Parameters:
name DNS name
Return values:
string NUL-terminated string
Produce a printable version of a DNS name. Used only for debugging.

Definition at line 242 of file dns.c.

00242                                                     {
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 }

static char* dns_decompress_name ( const struct dns_header reply,
const char *  name,
char *  buf 
) [static]

Decompress a DNS name.

Parameters:
reply DNS replay
name DNS name
buf Buffer into which to decompress DNS name
Return values:
next Byte following decompressed DNS name

Definition at line 263 of file dns.c.

References ntohs.

Referenced by dns_xfer_deliver_raw().

00264                                                                   {
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 }

static int dns_send_packet ( struct dns_request dns  )  [static]

Send next packet in DNS request.

Parameters:
dns DNS request

Definition at line 287 of file dns.c.

References DBGC, dns_query::dns, htons, dns_header::id, dns_request::qinfo, dns_request::query, dns_request::socket, start_timer(), dns_request::timer, and xfer_deliver_raw().

Referenced by dns_resolv(), dns_timer_expired(), and dns_xfer_deliver_raw().

00287                                                        {
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 }

static void dns_timer_expired ( struct retry_timer timer,
int  fail 
) [static]

Handle DNS retransmission timer expiry.

Parameters:
timer Retry timer
fail Failure indicator

Definition at line 311 of file dns.c.

References container_of, dns_done(), dns_send_packet(), and ETIMEDOUT.

Referenced by dns_resolv().

00311                                                                       {
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 }

static int dns_xfer_deliver_raw ( struct xfer_interface socket,
const void *  data,
size_t  len 
) [static]

Receive new data.

Parameters:
socket UDP socket
data DNS reply
len Length of DNS reply
Return values:
rc Return status code

Definition at line 330 of file dns.c.

References dns_rr_info::a, AF_INET, assert, dns_rr_info_cname::cname, dns_rr_info::cname, dns_rr_info::common, container_of, DBGC, dns_query::dns, DNS_CLASS_IN, dns_decompress_name(), dns_done(), dns_find_rr(), DNS_MAX_CNAME_RECURSION, dns_send_packet(), DNS_TYPE_A, DNS_TYPE_CNAME, EINVAL, ELOOP, ENXIO, htons, dns_header::id, dns_rr_info_a::in_addr, inet_ntoa(), ntohs, dns_query::payload, dns_query_info::qclass, dns_request::qinfo, dns_query_info::qtype, dns_request::query, dns_request::recursion, dns_request::sa, sockaddr_in::sin_addr, sockaddr_in::sin_family, stop_timer(), dns_request::timer, and dns_rr_info_common::type.

00331                                                                  {
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 }

static void dns_xfer_close ( struct xfer_interface socket,
int  rc 
) [static]

Receive new data.

Parameters:
socket UDP socket
rc Reason for close

Definition at line 451 of file dns.c.

References container_of, dns_done(), and ECONNABORTED.

00451                                                                      {
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 }

static int dns_resolv ( struct resolv_interface resolv,
const char *  name,
struct sockaddr sa 
) [static]

Resolve name using DNS.

Parameters:
resolv Name resolution interface
name Name to resolve
sa Socket address to fill in
Return values:
rc Return status code

Definition at line 479 of file dns.c.

References DBG, DBGC, dns_query::dns, DNS_CLASS_IN, DNS_FLAG_OPCODE_QUERY, DNS_FLAG_QUERY, DNS_FLAG_RD, dns_make_name(), dns_qualify_name(), dns_send_packet(), dns_timer_expired(), DNS_TYPE_A, ENOMEM, ENXIO, retry_timer::expired, dns_header::flags, free(), htons, memcpy, NULL, null_resolv_ops, dns_query::payload, dns_query_info::qclass, dns_header::qdcount, dns_request::qinfo, dns_query_info::qtype, dns_request::query, ref_put(), dns_request::refcnt, dns_request::resolv, resolv_init(), resolv_plug_plug(), dns_request::sa, SOCK_DGRAM, dns_request::socket, sockaddr_tcpip::st_family, strerror(), dns_request::timer, xfer_init(), xfer_open_socket(), and zalloc().

00480                                                                 {
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 }

struct resolver dns_resolver __resolver ( RESOLV_NORMAL   )  [read]

DNS name resolver.

static int apply_dns_settings ( void   )  [static]

Apply DNS settings.

Return values:
rc Return status code

Definition at line 580 of file dns.c.

References AF_INET, DBG, fetch_ipv4_setting(), fetch_string_setting_copy(), inet_ntoa(), localdomain, NULL, sockaddr_in::sin_addr, and sockaddr_in::sin_family.

00580                                        {
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 }


Variable Documentation

struct sockaddr_tcpip nameserver [static]

Initial value:

 {
        .st_port = htons ( DNS_PORT ),
}
The DNS server.

Definition at line 49 of file dns.c.

char* localdomain [static]

The local domain.

Definition at line 54 of file dns.c.

Referenced by apply_dns_settings(), and dns_qualify_name().

Initial value:

 {
        .close          = dns_xfer_close,
        .vredirect      = xfer_vreopen,
        .window         = unlimited_xfer_window,
        .alloc_iob      = default_xfer_alloc_iob,
        .deliver_iob    = xfer_deliver_as_raw,
        .deliver_raw    = dns_xfer_deliver_raw,
}
DNS socket operations.

Definition at line 462 of file dns.c.

Initial value:

 {
        .apply = apply_dns_settings,
}
DNS settings applicator.

Definition at line 601 of file dns.c.


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