iPXE
Data Structures | Defines | Functions | Variables
dns.c File Reference

DNS protocol. More...

#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <errno.h>
#include <byteswap.h>
#include <ipxe/refcnt.h>
#include <ipxe/iobuf.h>
#include <ipxe/xfer.h>
#include <ipxe/open.h>
#include <ipxe/resolv.h>
#include <ipxe/retry.h>
#include <ipxe/tcpip.h>
#include <ipxe/settings.h>
#include <ipxe/features.h>
#include <ipxe/job.h>
#include <ipxe/dhcp.h>
#include <ipxe/dhcpv6.h>
#include <ipxe/dns.h>

Go to the source code of this file.

Data Structures

struct  dns_request
 A DNS request. More...

Defines

#define ENXIO_NO_RECORD   __einfo_error ( EINFO_ENXIO_NO_RECORD )
#define EINFO_ENXIO_NO_RECORD   __einfo_uniqify ( EINFO_ENXIO, 0x01, "DNS name does not exist" )
#define ENXIO_NO_NAMESERVER   __einfo_error ( EINFO_ENXIO_NO_NAMESERVER )
#define EINFO_ENXIO_NO_NAMESERVER   __einfo_uniqify ( EINFO_ENXIO, 0x02, "No DNS servers available" )

Functions

 FILE_LICENCE (GPL2_OR_LATER_OR_UBDL)
 FEATURE (FEATURE_PROTOCOL,"DNS", DHCP_EB_FEATURE_DNS, 1)
int dns_encode (const char *string, struct dns_name *name)
 Encode a DNS name using RFC1035 encoding.
static int dns_label (struct dns_name *name, size_t offset)
 Find start of valid label within an RFC1035-encoded DNS name.
int dns_decode (struct dns_name *name, char *data, size_t len)
 Decode RFC1035-encoded DNS name.
int dns_compare (struct dns_name *first, struct dns_name *second)
 Compare DNS names for equality.
int dns_copy (struct dns_name *src, struct dns_name *dst)
 Copy a DNS name.
int dns_skip (struct dns_name *name)
 Skip RFC1035-encoded DNS name.
static int dns_skip_search (struct dns_name *name)
 Skip RFC1035-encoded DNS name in search list.
static const char * dns_name (struct dns_name *name)
 Transcribe DNS name (for debugging)
static const char * dns_type (uint16_t type)
 Name a DNS query type (for debugging)
static void dns_done (struct dns_request *dns, int rc)
 Mark DNS request as complete.
static void dns_resolved (struct dns_request *dns)
 Mark DNS request as resolved and complete.
static int dns_question (struct dns_request *dns)
 Construct DNS question.
static int dns_send_packet (struct dns_request *dns)
 Send DNS query.
static void dns_timer_expired (struct retry_timer *timer, int fail)
 Handle DNS retransmission timer expiry.
static int dns_xfer_deliver (struct dns_request *dns, struct io_buffer *iobuf, struct xfer_metadata *meta __unused)
 Receive new data.
static void dns_xfer_close (struct dns_request *dns, int rc)
 Receive new data.
static int dns_progress (struct dns_request *dns, struct job_progress *progress)
 Report job progress.
static int dns_resolv (struct interface *resolv, const char *name, struct sockaddr *sa)
 Resolve name using DNS.
struct resolver dns_resolver __resolver (RESOLV_NORMAL)
 DNS name resolver.
static int format_dnssl_setting (const struct setting_type *type __unused, const void *raw, size_t raw_len, char *buf, size_t len)
 Format DNS search list setting.
struct setting dns_setting __setting (SETTING_IP4_EXTRA, dns)
 IPv4 DNS server setting.
struct setting dns6_setting __setting (SETTING_IP6_EXTRA, dns6)
 IPv6 DNS server setting.
struct setting dnssl_setting __setting (SETTING_IP_EXTRA, dnssl)
 DNS search list.
static void apply_dns_search (void)
 Apply DNS search list.
static int apply_dns_settings (void)
 Apply DNS settings.

Variables

union {
   struct sockaddr   sa
   struct sockaddr_tcpip   st
   struct sockaddr_in   sin
   struct sockaddr_in6   sin6
nameserver
 The DNS server.
static struct dns_name dns_search
 The DNS search list.
static struct interface_operation dns_socket_operations []
 DNS socket interface operations.
static struct interface_descriptor dns_socket_desc
 DNS socket interface descriptor.
static struct interface_operation dns_resolv_op []
 DNS resolver interface operations.
static struct interface_descriptor dns_resolv_desc
 DNS resolver interface descriptor.
struct setting_type
setting_type_dnssl 
__setting_type
 A DNS search list setting type.
struct settings_applicator
dns_applicator 
__settings_applicator
 DNS settings applicator.

Detailed Description

DNS protocol.

Definition in file dns.c.


Define Documentation

Definition at line 59 of file dns.c.

Referenced by dns_xfer_deliver().

#define EINFO_ENXIO_NO_RECORD   __einfo_uniqify ( EINFO_ENXIO, 0x01, "DNS name does not exist" )

Definition at line 60 of file dns.c.

Definition at line 62 of file dns.c.

Referenced by dns_resolv().

#define EINFO_ENXIO_NO_NAMESERVER   __einfo_uniqify ( EINFO_ENXIO, 0x02, "No DNS servers available" )

Definition at line 63 of file dns.c.


Function Documentation

FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL  )
FEATURE ( FEATURE_PROTOCOL  ,
"DNS"  ,
DHCP_EB_FEATURE_DNS  ,
 
)
int dns_encode ( const char *  string,
struct dns_name name 
)

Encode a DNS name using RFC1035 encoding.

Parameters:
stringDNS name as a string
nameDNS name to fill in
Return values:
lenLength of DNS name, or negative error

Definition at line 88 of file dns.c.

References dns_name::data, DNS_MAX_LABEL_LEN, EINVAL, end, dns_name::len, len, dns_name::offset, and start.

Referenced by apply_dns_search(), dns_encode_fail_okx(), dns_encode_okx(), and dns_resolv().

                                                             {
        uint8_t *start = ( name->data + name->offset );
        uint8_t *end = ( name->data + name->len );
        uint8_t *dst = start;
        size_t len = 0;
        char c;

        /* Encode name */
        while ( ( c = *(string++) ) ) {

                /* Handle '.' separators */
                if ( c == '.' ) {

                        /* Reject consecutive '.' */
                        if ( ( len == 0 ) && ( dst != start ) )
                                return -EINVAL;

                        /* Terminate if this is the trailing '.' */
                        if ( *string == '\0' )
                                break;

                        /* Reject initial non-terminating '.' */
                        if ( len == 0 )
                                return -EINVAL;

                        /* Reset length */
                        len = 0;

                } else {

                        /* Increment length */
                        len++;

                        /* Check for overflow */
                        if ( len > DNS_MAX_LABEL_LEN )
                                return -EINVAL;
                }

                /* Copy byte, update length */
                if ( ++dst < end ) {
                        *dst = c;
                        dst[-len] = len;
                }
        }

        /* Add terminating root marker */
        if ( len )
                dst++;
        if ( dst < end )
                *dst = '\0';
        dst++;

        return ( dst - start );
}
static int dns_label ( struct dns_name name,
size_t  offset 
) [static]

Find start of valid label within an RFC1035-encoded DNS name.

Parameters:
nameDNS name
offsetCurrent offset
Return values:
offsetOffset of label, or negative error

Definition at line 150 of file dns.c.

References byte, dns_name::data, DNS_COMPRESSED_OFFSET, DNS_IS_COMPRESSED, EINVAL, dns_name::len, len, ntohs, and offset.

Referenced by dns_compare(), dns_copy(), dns_decode(), and dns_skip().

                                                              {
        const uint8_t *byte;
        const uint16_t *word;
        size_t len;
        size_t ptr;

        while ( 1 ) {

                /* Fail if we have overrun the DNS name */
                if ( ( offset + sizeof ( *byte) ) > name->len )
                        return -EINVAL;
                byte = ( name->data + offset );

                /* Follow compression pointer, if applicable */
                if ( DNS_IS_COMPRESSED ( *byte ) ) {

                        /* Fail if we have overrun the DNS name */
                        if ( ( offset + sizeof ( *word ) ) > name->len )
                                return -EINVAL;
                        word = ( name->data + offset );

                        /* Extract pointer to new offset */
                        ptr = DNS_COMPRESSED_OFFSET ( ntohs ( *word ) );

                        /* Fail if pointer does not point backwards.
                         * (This guarantees termination of the
                         * function.)
                         */
                        if ( ptr >= offset )
                                return -EINVAL;

                        /* Continue from new offset */
                        offset = ptr;
                        continue;
                }

                /* Fail if we have overrun the DNS name */
                len = *byte;
                if ( ( offset + sizeof ( *byte ) + len ) > name->len )
                        return -EINVAL;

                /* We have a valid label */
                return offset;
        }
}
int dns_decode ( struct dns_name name,
char *  data,
size_t  len 
)

Decode RFC1035-encoded DNS name.

Parameters:
nameDNS name
dataOutput buffer
lenLength of output buffer
Return values:
lenLength of decoded DNS name, or negative error

Definition at line 204 of file dns.c.

References dns_name::data, dns_label(), EINVAL, dns_name::len, len, memcpy(), dns_name::offset, and offset.

Referenced by dns_decode_fail_okx(), dns_decode_okx(), dns_list_okx(), dns_name(), dns_progress(), and format_dnssl_setting().

                                                                 {
        unsigned int recursion_limit = name->len; /* Generous upper bound */
        int offset = name->offset;
        const uint8_t *label;
        size_t decoded_len = 0;
        size_t label_len;
        size_t copy_len;

        while ( recursion_limit-- ) {

                /* Find valid DNS label */
                offset = dns_label ( name, offset );
                if ( offset < 0 )
                        return offset;

                /* Terminate if we have reached the root */
                label = ( name->data + offset );
                label_len = *(label++);
                if ( label_len == 0 ) {
                        if ( decoded_len < len )
                                *data = '\0';
                        return decoded_len;
                }

                /* Prepend '.' if applicable */
                if ( decoded_len && ( decoded_len++ < len ) )
                        *(data++) = '.';

                /* Copy label to output buffer */
                copy_len = ( ( decoded_len < len ) ? ( len - decoded_len ) : 0);
                if ( copy_len > label_len )
                        copy_len = label_len;
                memcpy ( data, label, copy_len );
                data += copy_len;
                decoded_len += label_len;

                /* Move to next label */
                offset += ( sizeof ( *label ) + label_len );
        }

        /* Recursion limit exceeded */
        return -EINVAL;
}
int dns_compare ( struct dns_name first,
struct dns_name second 
)

Compare DNS names for equality.

Parameters:
firstFirst DNS name
secondSecond DNS name
Return values:
rcReturn status code

Definition at line 255 of file dns.c.

References dns_name::data, dns_label(), EINVAL, ENOENT, dns_name::len, len, dns_name::offset, and tolower().

Referenced by dns_compare_fail_okx(), dns_compare_okx(), and dns_xfer_deliver().

                                                                    {
        unsigned int recursion_limit = first->len; /* Generous upper bound */
        int first_offset = first->offset;
        int second_offset = second->offset;
        const uint8_t *first_label;
        const uint8_t *second_label;
        size_t label_len;
        size_t len;

        while ( recursion_limit-- ) {

                /* Find valid DNS labels */
                first_offset = dns_label ( first, first_offset );
                if ( first_offset < 0 )
                        return first_offset;
                second_offset = dns_label ( second, second_offset );
                if ( second_offset < 0 )
                        return second_offset;

                /* Compare label lengths */
                first_label = ( first->data + first_offset );
                second_label = ( second->data + second_offset );
                label_len = *(first_label++);
                if ( label_len != *(second_label++) )
                        return -ENOENT;
                len = ( sizeof ( *first_label ) + label_len );

                /* Terminate if we have reached the root */
                if ( label_len == 0 )
                        return 0;

                /* Compare label contents (case-insensitively) */
                while ( label_len-- ) {
                        if ( tolower ( *(first_label++) ) !=
                             tolower ( *(second_label++) ) )
                                return -ENOENT;
                }

                /* Move to next labels */
                first_offset += len;
                second_offset += len;
        }

        /* Recursion limit exceeded */
        return -EINVAL;
}
int dns_copy ( struct dns_name src,
struct dns_name dst 
)

Copy a DNS name.

Parameters:
srcSource DNS name
dstDestination DNS name
Return values:
lenLength of copied DNS name, or negative error

Definition at line 309 of file dns.c.

References dns_name::data, dns_label(), EINVAL, dns_name::len, len, memcpy(), and dns_name::offset.

Referenced by dns_copy_fail_okx(), dns_copy_okx(), dns_question(), and dns_xfer_deliver().

                                                            {
        unsigned int recursion_limit = src->len; /* Generous upper bound */
        int src_offset = src->offset;
        size_t dst_offset = dst->offset;
        const uint8_t *label;
        size_t label_len;
        size_t copy_len;
        size_t len;

        while ( recursion_limit-- ) {

                /* Find valid DNS label */
                src_offset = dns_label ( src, src_offset );
                if ( src_offset < 0 )
                        return src_offset;

                /* Copy as an uncompressed label */
                label = ( src->data + src_offset );
                label_len = *label;
                len = ( sizeof ( *label ) + label_len );
                copy_len = ( ( dst_offset < dst->len ) ?
                             ( dst->len - dst_offset ) : 0 );
                if ( copy_len > len )
                        copy_len = len;
                memcpy ( ( dst->data + dst_offset ), label, copy_len );
                src_offset += len;
                dst_offset += len;

                /* Terminate if we have reached the root */
                if ( label_len == 0 )
                        return ( dst_offset - dst->offset );
        }

        /* Recursion limit exceeded */
        return -EINVAL;
}
int dns_skip ( struct dns_name name)

Skip RFC1035-encoded DNS name.

Parameters:
nameDNS name
Return values:
offsetOffset to next name, or negative error

Definition at line 352 of file dns.c.

References dns_name::data, dns_label(), EINVAL, dns_name::len, dns_name::offset, and offset.

Referenced by dns_list_okx(), dns_skip_search(), and dns_xfer_deliver().

                                       {
        unsigned int recursion_limit = name->len; /* Generous upper bound */
        int offset = name->offset;
        int prev_offset;
        const uint8_t *label;
        size_t label_len;

        while ( recursion_limit-- ) {

                /* Find valid DNS label */
                prev_offset = offset;
                offset = dns_label ( name, prev_offset );
                if ( offset < 0 )
                        return offset;

                /* Terminate if we have reached a compression pointer */
                if ( offset != prev_offset )
                        return ( prev_offset + sizeof ( uint16_t ) );

                /* Skip this label */
                label = ( name->data + offset );
                label_len = *label;
                offset += ( sizeof ( *label ) + label_len );

                /* Terminate if we have reached the root */
                if ( label_len == 0 )
                        return offset;
        }

        /* Recursion limit exceeded */
        return -EINVAL;
}
static int dns_skip_search ( struct dns_name name) [static]

Skip RFC1035-encoded DNS name in search list.

Parameters:
nameDNS name
Return values:
offsetOffset to next non-empty name, or negative error

Definition at line 391 of file dns.c.

References dns_name::data, dns_skip(), dns_name::len, and offset.

Referenced by apply_dns_settings(), dns_xfer_deliver(), and format_dnssl_setting().

                                                     {
        int offset;

        /* Find next name */
        offset = dns_skip ( name );
        if ( offset < 0 )
                return offset;

        /* Skip over any subsequent empty names (e.g. due to padding
         * bytes used in the NDP DNSSL option).
         */
        while ( ( offset < ( ( int ) name->len ) ) &&
                ( *( ( uint8_t * ) ( name->data + offset ) ) == 0 ) ) {
                offset++;
        }

        return offset;
}
static const char* dns_name ( struct dns_name name) [static]

Transcribe DNS name (for debugging)

Parameters:
nameDNS name
Return values:
stringTranscribed DNS name

Definition at line 416 of file dns.c.

References dns_decode(), and len.

                                                       {
        static char buf[256];
        int len;

        len = dns_decode ( name, buf, ( sizeof ( buf ) - 1 /* NUL */ ) );
        return ( ( len < 0 ) ? "<INVALID>" : buf );
}
static const char* dns_type ( uint16_t  type) [static]

Name a DNS query type (for debugging)

Parameters:
typeQuery type (in network byte order)
Return values:
nameType name

Definition at line 430 of file dns.c.

References DNS_TYPE_A, DNS_TYPE_AAAA, DNS_TYPE_CNAME, and htons.

Referenced by dns_question(), dns_send_packet(), and dns_xfer_deliver().

                                               {
        switch ( type ) {
        case htons ( DNS_TYPE_A ):      return "A";
        case htons ( DNS_TYPE_AAAA ):   return "AAAA";
        case htons ( DNS_TYPE_CNAME ):  return "CNAME";
        default:                        return "<UNKNOWN>";
        }
}
static void dns_done ( struct dns_request dns,
int  rc 
) [static]

Mark DNS request as complete.

Parameters:
dnsDNS request
rcReturn status code

Definition at line 487 of file dns.c.

References intf_shutdown(), and stop_timer().

Referenced by dns_resolved(), dns_timer_expired(), dns_xfer_close(), and dns_xfer_deliver().

                                                         {

        /* Stop the retry timer */
        stop_timer ( &dns->timer );

        /* Shut down interfaces */
        intf_shutdown ( &dns->socket, rc );
        intf_shutdown ( &dns->resolv, rc );
}
static void dns_resolved ( struct dns_request dns) [static]

Mark DNS request as resolved and complete.

Parameters:
dnsDNS request
rcReturn status code

Definition at line 503 of file dns.c.

References dns_request::address, DBGC, dns_done(), dns_request::resolv, resolv_done(), dns_request::sa, and sock_ntoa().

Referenced by dns_xfer_deliver().

                                                     {

        DBGC ( dns, "DNS %p found address %s\n",
               dns, sock_ntoa ( &dns->address.sa ) );

        /* Return resolved address */
        resolv_done ( &dns->resolv, &dns->address.sa );

        /* Mark operation as complete */
        dns_done ( dns, 0 );
}
static int dns_question ( struct dns_request dns) [static]

Construct DNS question.

Parameters:
dnsDNS request
Return values:
rcReturn status code

Definition at line 521 of file dns.c.

References dns_request::buf, dns_name::data, DBGC, DBGC2, DNS_CLASS_IN, dns_copy(), dns_type(), EINVAL, htons, dns_name::len, dns_request::len, dns_request::name, dns_name::offset, dns_request::offset, offsetof, dns_question::qclass, dns_question::qtype, dns_request::qtype, dns_request::question, and dns_request::search.

                                                    {
        static struct dns_name search_root = {
                .data = "",
                .len = 1,
        };
        struct dns_name *search = &dns->search;
        int len;
        size_t offset;

        /* Use root suffix if search list is empty */
        if ( search->offset == search->len )
                search = &search_root;

        /* Overwrite current suffix */
        dns->name.offset = dns->offset;
        len = dns_copy ( search, &dns->name );
        if ( len < 0 )
                return len;

        /* Sanity check */
        offset = ( dns->name.offset + len );
        if ( offset > dns->name.len ) {
                DBGC ( dns, "DNS %p name is too long\n", dns );
                return -EINVAL;
        }

        /* Construct question */
        dns->question = ( ( ( void * ) &dns->buf ) + offset );
        dns->question->qtype = dns->qtype;
        dns->question->qclass = htons ( DNS_CLASS_IN );

        /* Store length */
        dns->len = ( offset + sizeof ( *(dns->question) ) );

        /* Restore name */
        dns->name.offset = offsetof ( typeof ( dns->buf ), name );

        DBGC2 ( dns, "DNS %p question is %s type %s\n", dns,
                dns_name ( &dns->name ), dns_type ( dns->question->qtype ) );

        return 0;
}
static int dns_send_packet ( struct dns_request dns) [static]

Send DNS query.

Parameters:
dnsDNS request
Return values:
rcReturn status code

Definition at line 570 of file dns.c.

References dns_request::buf, DBGC, dns_type(), dns_header::id, dns_request::len, dns_request::name, ntohs, dns_question::qtype, dns_request::query, dns_request::question, random(), dns_request::socket, start_timer(), dns_request::timer, and xfer_deliver_raw().

Referenced by dns_timer_expired(), and dns_xfer_deliver().

                                                       {
        struct dns_header *query = &dns->buf.query;

        /* Start retransmission timer */
        start_timer ( &dns->timer );

        /* Generate query identifier */
        query->id = random();

        /* Send query */
        DBGC ( dns, "DNS %p sending query ID %#04x for %s type %s\n", dns,
               ntohs ( query->id ), dns_name ( &dns->name ),
               dns_type ( dns->question->qtype ) );

        /* Send the data */
        return xfer_deliver_raw ( &dns->socket, query, dns->len );
}
static void dns_timer_expired ( struct retry_timer timer,
int  fail 
) [static]

Handle DNS retransmission timer expiry.

Parameters:
timerRetry timer
failFailure indicator

Definition at line 594 of file dns.c.

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

Referenced by dns_resolv().

                                                                      {
        struct dns_request *dns =
                container_of ( timer, struct dns_request, timer );

        if ( fail ) {
                dns_done ( dns, -ETIMEDOUT );
        } else {
                dns_send_packet ( dns );
        }
}
static int dns_xfer_deliver ( struct dns_request dns,
struct io_buffer iobuf,
struct xfer_metadata *meta  __unused 
) [static]

Receive new data.

Parameters:
dnsDNS request
iobufI/O buffer
metaData transfer metadata
Return values:
rcReturn status code

Definition at line 613 of file dns.c.

References dns_rr::a, dns_rr::aaaa, dns_request::address, AF_INET, AF_INET6, assert, dns_request::buf, dns_rr::cname, dns_rr::common, dns_name::data, io_buffer::data, DBGC, DBGC2, dns_compare(), dns_copy(), dns_done(), DNS_MAX_CNAME_RECURSION, dns_resolved(), dns_send_packet(), dns_skip(), dns_skip_search(), dns_type(), DNS_TYPE_A, DNS_TYPE_AAAA, DNS_TYPE_CNAME, done, EINVAL, ELOOP, ENXIO_NO_RECORD, free_iob(), htons, dns_header::id, dns_rr_aaaa::in6_addr, dns_rr_a::in_addr, iob_len(), dns_name::len, memcpy(), dns_request::name, ntohs, dns_name::offset, dns_request::offset, offsetof, dns_header::qdcount, dns_question::qtype, dns_request::qtype, dns_request::query, dns_request::question, rc, dns_rr_common::rdlength, dns_request::recursion, dns_request::search, dns_request::sin, dns_request::sin6, sockaddr_in6::sin6_addr, sockaddr_in6::sin6_family, sockaddr_in::sin_addr, sockaddr_in::sin_family, stop_timer(), strerror(), dns_request::timer, and dns_rr_common::type.

                                                                    {
        struct dns_header *response = iobuf->data;
        struct dns_header *query = &dns->buf.query;
        unsigned int qtype = dns->question->qtype;
        struct dns_name buf;
        union dns_rr *rr;
        int offset;
        size_t answer_offset;
        size_t next_offset;
        size_t rdlength;
        size_t name_len;
        int rc;

        /* Sanity check */
        if ( iob_len ( iobuf ) < sizeof ( *response ) ) {
                DBGC ( dns, "DNS %p received underlength packet length %zd\n",
                       dns, iob_len ( iobuf ) );
                rc = -EINVAL;
                goto done;
        }

        /* Check response ID matches query ID */
        if ( response->id != query->id ) {
                DBGC ( dns, "DNS %p received unexpected response ID %#04x "
                       "(wanted %d)\n", dns, ntohs ( response->id ),
                       ntohs ( query->id ) );
                rc = -EINVAL;
                goto done;
        }
        DBGC ( dns, "DNS %p received response ID %#04x\n",
               dns, ntohs ( response->id ) );

        /* Check that we have exactly one question */
        if ( response->qdcount != htons ( 1 ) ) {
                DBGC ( dns, "DNS %p received response with %d questions\n",
                       dns, ntohs ( response->qdcount ) );
                rc = -EINVAL;
                goto done;
        }

        /* Skip question section */
        buf.data = iobuf->data;
        buf.offset = sizeof ( *response );
        buf.len = iob_len ( iobuf );
        offset = dns_skip ( &buf );
        if ( offset < 0 ) {
                rc = offset;
                DBGC ( dns, "DNS %p received response with malformed "
                       "question: %s\n", dns, strerror ( rc ) );
                goto done;
        }
        answer_offset = ( offset + sizeof ( struct dns_question ) );

        /* Search through response for useful answers.  Do this
         * multiple times, to take advantage of useful nameservers
         * which send us e.g. the CNAME *and* the A record for the
         * pointed-to name.
         */
        for ( buf.offset = answer_offset ; buf.offset != buf.len ;
              buf.offset = next_offset ) {

                /* Check for valid name */
                offset = dns_skip ( &buf );
                if ( offset < 0 ) {
                        rc = offset;
                        DBGC ( dns, "DNS %p received response with malformed "
                               "answer: %s\n", dns, strerror ( rc ) );
                        goto done;
                }

                /* Check for sufficient space for resource record */
                rr = ( buf.data + offset );
                if ( ( offset + sizeof ( rr->common ) ) > buf.len ) {
                        DBGC ( dns, "DNS %p received response with underlength "
                               "RR\n", dns );
                        rc = -EINVAL;
                        goto done;
                }
                rdlength = ntohs ( rr->common.rdlength );
                next_offset = ( offset + sizeof ( rr->common ) + rdlength );
                if ( next_offset > buf.len ) {
                        DBGC ( dns, "DNS %p received response with underlength "
                               "RR\n", dns );
                        rc = -EINVAL;
                        goto done;
                }

                /* Skip non-matching names */
                if ( dns_compare ( &buf, &dns->name ) != 0 ) {
                        DBGC2 ( dns, "DNS %p ignoring response for %s type "
                                "%s\n", dns, dns_name ( &buf ),
                                dns_type ( rr->common.type ) );
                        continue;
                }

                /* Handle answer */
                switch ( rr->common.type ) {

                case htons ( DNS_TYPE_AAAA ):

                        /* Found the target AAAA record */
                        if ( rdlength < sizeof ( dns->address.sin6.sin6_addr )){
                                DBGC ( dns, "DNS %p received response with "
                                       "underlength AAAA\n", dns );
                                rc = -EINVAL;
                                goto done;
                        }
                        dns->address.sin6.sin6_family = AF_INET6;
                        memcpy ( &dns->address.sin6.sin6_addr,
                                 &rr->aaaa.in6_addr,
                                 sizeof ( dns->address.sin6.sin6_addr ) );
                        dns_resolved ( dns );
                        rc = 0;
                        goto done;

                case htons ( DNS_TYPE_A ):

                        /* Found the target A record */
                        if ( rdlength < sizeof ( dns->address.sin.sin_addr ) ) {
                                DBGC ( dns, "DNS %p received response with "
                                       "underlength A\n", dns );
                                rc = -EINVAL;
                                goto done;
                        }
                        dns->address.sin.sin_family = AF_INET;
                        dns->address.sin.sin_addr = rr->a.in_addr;
                        dns_resolved ( dns );
                        rc = 0;
                        goto done;

                case htons ( DNS_TYPE_CNAME ):

                        /* Terminate the operation if we recurse too far */
                        if ( ++dns->recursion > DNS_MAX_CNAME_RECURSION ) {
                                DBGC ( dns, "DNS %p recursion exceeded\n",
                                       dns );
                                rc = -ELOOP;
                                dns_done ( dns, rc );
                                goto done;
                        }

                        /* Found a CNAME record; update query and recurse */
                        buf.offset = ( offset + sizeof ( rr->cname ) );
                        DBGC ( dns, "DNS %p found CNAME %s\n",
                               dns, dns_name ( &buf ) );
                        dns->search.offset = dns->search.len;
                        name_len = dns_copy ( &buf, &dns->name );
                        dns->offset = ( offsetof ( typeof ( dns->buf ), name ) +
                                        name_len - 1 /* Strip root label */ );
                        if ( ( rc = dns_question ( dns ) ) != 0 ) {
                                dns_done ( dns, rc );
                                goto done;
                        }
                        next_offset = answer_offset;
                        break;

                default:
                        DBGC ( dns, "DNS %p got unknown record type %d\n",
                               dns, ntohs ( rr->common.type ) );
                        break;
                }
        }

        /* Stop the retry timer.  After this point, each code path
         * must either restart the timer by calling dns_send_packet(),
         * or mark the DNS operation as complete by calling
         * dns_done()
         */
        stop_timer ( &dns->timer );

        /* Determine what to do next based on the type of query we
         * issued and the response we received
         */
        switch ( qtype ) {

        case htons ( DNS_TYPE_AAAA ):
                /* We asked for an AAAA record and got nothing; try
                 * the A.
                 */
                DBGC ( dns, "DNS %p found no AAAA record; trying A\n", dns );
                dns->question->qtype = htons ( DNS_TYPE_A );
                dns_send_packet ( dns );
                rc = 0;
                goto done;

        case htons ( DNS_TYPE_A ):
                /* We asked for an A record and got nothing;
                 * try the CNAME.
                 */
                DBGC ( dns, "DNS %p found no A record; trying CNAME\n", dns );
                dns->question->qtype = htons ( DNS_TYPE_CNAME );
                dns_send_packet ( dns );
                rc = 0;
                goto done;

        case htons ( DNS_TYPE_CNAME ):
                /* We asked for a CNAME record.  If we got a response
                 * (i.e. if the next AAAA/A query is already set up),
                 * then issue it.
                 */
                if ( qtype == dns->qtype ) {
                        dns_send_packet ( dns );
                        rc = 0;
                        goto done;
                }

                /* If we have already reached the end of the search list,
                 * then terminate lookup.
                 */
                if ( dns->search.offset == dns->search.len ) {
                        DBGC ( dns, "DNS %p found no CNAME record\n", dns );
                        rc = -ENXIO_NO_RECORD;
                        dns_done ( dns, rc );
                        goto done;
                }

                /* Move to next entry in search list.  This can never fail,
                 * since we have already used this entry.
                 */
                DBGC ( dns, "DNS %p found no CNAME record; trying next "
                       "suffix\n", dns );
                dns->search.offset = dns_skip_search ( &dns->search );
                if ( ( rc = dns_question ( dns ) ) != 0 ) {
                        dns_done ( dns, rc );
                        goto done;
                }
                dns_send_packet ( dns );
                goto done;

        default:
                assert ( 0 );
                rc = -EINVAL;
                dns_done ( dns, rc );
                goto done;
        }

 done:
        /* Free I/O buffer */
        free_iob ( iobuf );
        return rc;
}
static void dns_xfer_close ( struct dns_request dns,
int  rc 
) [static]

Receive new data.

Parameters:
dnsDNS request
rcReason for close

Definition at line 863 of file dns.c.

References dns_done(), and ECONNABORTED.

                                                               {

        if ( ! rc )
                rc = -ECONNABORTED;

        dns_done ( dns, rc );
}
static int dns_progress ( struct dns_request dns,
struct job_progress progress 
) [static]

Report job progress.

Parameters:
dnsDNS request
progressProgress report to fill in
Return values:
ongoing_rcOngoing job status code (if known)

Definition at line 878 of file dns.c.

References dns_decode(), dns_request::len, job_progress::message, and dns_request::name.

                                                          {
        int len;

        /* Show current question as progress message */
        len = dns_decode ( &dns->name, progress->message,
                           ( sizeof ( progress->message ) - 1 /* NUL */ ) );
        if ( len < 0 ) {
                /* Ignore undecodable names */
                progress->message[0] = '\0';
        }

        return 0;
}
static int dns_resolv ( struct interface resolv,
const char *  name,
struct sockaddr sa 
) [static]

Resolve name using DNS.

Parameters:
resolvName resolution interface
nameName to resolve
saSocket address to fill in
Return values:
rcReturn status code

Definition at line 921 of file dns.c.

References dns_request::address, AF_INET, AF_INET6, dns_request::buf, dns_name::data, DBG, DBGC, dns_encode(), DNS_FLAG_RD, dns_resolv_desc, dns_search, dns_socket_desc, dns_timer_expired(), DNS_TYPE_A, DNS_TYPE_AAAA, ENOMEM, ENOTSUP, ENXIO_NO_NAMESERVER, dns_header::flags, htons, intf_init(), intf_plug_plug(), dns_name::len, memcpy(), dns_request::name, nameserver, NULL, dns_request::offset, offsetof, dns_request::padding, dns_header::qdcount, dns_request::qtype, dns_request::query, rc, ref_init, ref_put, dns_request::refcnt, dns_request::resolv, dns_request::sa, dns_request::search, SOCK_DGRAM, dns_request::socket, start_timer_nodelay(), strchr(), strerror(), dns_request::timer, xfer_open_socket(), and zalloc().

                                                                {
        struct dns_request *dns;
        struct dns_header *query;
        size_t search_len;
        int name_len;
        int rc;

        /* Fail immediately if no DNS servers */
        if ( ! nameserver.sa.sa_family ) {
                DBG ( "DNS not attempting to resolve \"%s\": "
                      "no DNS servers\n", name );
                rc = -ENXIO_NO_NAMESERVER;
                goto err_no_nameserver;
        }

        /* Determine whether or not to use search list */
        search_len = ( strchr ( name, '.' ) ? 0 : dns_search.len );

        /* Allocate DNS structure */
        dns = zalloc ( sizeof ( *dns ) + search_len );
        if ( ! dns ) {
                rc = -ENOMEM;
                goto err_alloc_dns;
        }
        ref_init ( &dns->refcnt, NULL );
        intf_init ( &dns->resolv, &dns_resolv_desc, &dns->refcnt );
        intf_init ( &dns->socket, &dns_socket_desc, &dns->refcnt );
        timer_init ( &dns->timer, dns_timer_expired, &dns->refcnt );
        memcpy ( &dns->address.sa, sa, sizeof ( dns->address.sa ) );
        dns->search.data = ( ( ( void * ) dns ) + sizeof ( *dns ) );
        dns->search.len = search_len;
        memcpy ( dns->search.data, dns_search.data, search_len );

        /* Determine initial query type */
        switch ( nameserver.sa.sa_family ) {
        case AF_INET:
                dns->qtype = htons ( DNS_TYPE_A );
                break;
        case AF_INET6:
                dns->qtype = htons ( DNS_TYPE_AAAA );
                break;
        default:
                rc = -ENOTSUP;
                goto err_type;
        }

        /* Construct query */
        query = &dns->buf.query;
        query->flags = htons ( DNS_FLAG_RD );
        query->qdcount = htons ( 1 );
        dns->name.data = &dns->buf;
        dns->name.offset = offsetof ( typeof ( dns->buf ), name );
        dns->name.len = offsetof ( typeof ( dns->buf ), padding );
        name_len = dns_encode ( name, &dns->name );
        if ( name_len < 0 ) {
                rc = name_len;
                goto err_encode;
        }
        dns->offset = ( offsetof ( typeof ( dns->buf ), name ) +
                        name_len - 1 /* Strip root label */ );
        if ( ( rc = dns_question ( dns ) ) != 0 )
                goto err_question;

        /* Open UDP connection */
        if ( ( rc = xfer_open_socket ( &dns->socket, SOCK_DGRAM,
                                       &nameserver.sa, NULL ) ) != 0 ) {
                DBGC ( dns, "DNS %p could not open socket: %s\n",
                       dns, strerror ( rc ) );
                goto err_open_socket;
        }

        /* Start timer to trigger first packet */
        start_timer_nodelay ( &dns->timer );

        /* Attach parent interface, mortalise self, and return */
        intf_plug_plug ( &dns->resolv, resolv );
        ref_put ( &dns->refcnt );
        return 0;       

 err_open_socket:
 err_question:
 err_encode:
 err_type:
        ref_put ( &dns->refcnt );
 err_alloc_dns:
 err_no_nameserver:
        return rc;
}
struct resolver dns_resolver __resolver ( RESOLV_NORMAL  ) [read]

DNS name resolver.

static int format_dnssl_setting ( const struct setting_type *type  __unused,
const void *  raw,
size_t  raw_len,
char *  buf,
size_t  len 
) [static]

Format DNS search list setting.

Parameters:
typeSetting type
rawRaw setting value
raw_lenLength of raw setting value
bufBuffer to contain formatted value
lenLength of buffer
Return values:
lenLength of formatted value, or negative error

Definition at line 1034 of file dns.c.

References dns_name::data, dns_decode(), dns_skip_search(), dns_request::len, dns_name::offset, raw_len, and total.

                                                          {
        struct dns_name name = {
                .data = ( ( void * ) raw ),
                .len = raw_len,
        };
        size_t remaining = len;
        size_t total = 0;
        int name_len;

        while ( name.offset < raw_len ) {

                /* Decode name */
                remaining = ( ( total < len ) ? ( len - total ) : 0 );
                name_len = dns_decode ( &name, ( buf + total ), remaining );
                if ( name_len < 0 )
                        return name_len;
                total += name_len;

                /* Move to next name */
                name.offset = dns_skip_search ( &name );

                /* Add separator if applicable */
                if ( name.offset != raw_len ) {
                        if ( total < len )
                                buf[total] = ' ';
                        total++;
                }
        }

        return total;
}
struct setting dns_setting __setting ( SETTING_IP4_EXTRA  ,
dns   
) [read]

IPv4 DNS server setting.

struct setting dns6_setting __setting ( SETTING_IP6_EXTRA  ,
dns6   
) [read]

IPv6 DNS server setting.

struct setting dnssl_setting __setting ( SETTING_IP_EXTRA  ,
dnssl   
) [read]

DNS search list.

static void apply_dns_search ( void  ) [static]

Apply DNS search list.

Definition at line 1103 of file dns.c.

References dns_name::data, dns_encode(), dns_search, fetch_setting_copy(), fetch_string_setting_copy(), free, dns_name::len, dns_request::len, malloc(), memset(), and NULL.

Referenced by apply_dns_settings().

                                      {
        char *localdomain;
        int len;

        /* Free existing search list */
        free ( dns_search.data );
        memset ( &dns_search, 0, sizeof ( dns_search ) );

        /* Fetch DNS search list */
        len = fetch_setting_copy ( NULL, &dnssl_setting, NULL, NULL,
                                   &dns_search.data );
        if ( len >= 0 ) {
                dns_search.len = len;
                return;
        }

        /* If no DNS search list exists, try to fetch the local domain */
        fetch_string_setting_copy ( NULL, &domain_setting, &localdomain );
        if ( localdomain ) {
                len = dns_encode ( localdomain, &dns_search );
                if ( len >= 0 ) {
                        dns_search.data = malloc ( len );
                        if ( dns_search.data ) {
                                dns_search.len = len;
                                dns_encode ( localdomain, &dns_search );
                        }
                }
                free ( localdomain );
                return;
        }
}
static int apply_dns_settings ( void  ) [static]

Apply DNS settings.

Return values:
rcReturn status code

Definition at line 1140 of file dns.c.

References AF_INET, AF_INET6, apply_dns_search(), DBG, DBG_LOG, dns_search, dns_skip_search(), fetch_ipv4_setting(), fetch_ipv6_setting(), dns_name::len, memcpy(), nameserver, NULL, dns_name::offset, dns_request::offset, and sock_ntoa().

                                       {

        /* Fetch DNS server address */
        nameserver.sa.sa_family = 0;
        if ( fetch_ipv6_setting ( NULL, &dns6_setting,
                                  &nameserver.sin6.sin6_addr ) >= 0 ) {
                nameserver.sin6.sin6_family = AF_INET6;
        } else if ( fetch_ipv4_setting ( NULL, &dns_setting,
                                         &nameserver.sin.sin_addr ) >= 0 ) {
                nameserver.sin.sin_family = AF_INET;
        }
        if ( nameserver.sa.sa_family ) {
                DBG ( "DNS using nameserver %s\n",
                      sock_ntoa ( &nameserver.sa ) );
        }

        /* Fetch DNS search list */
        apply_dns_search();
        if ( DBG_LOG && ( dns_search.len != 0 ) ) {
                struct dns_name name;
                int offset;

                DBG ( "DNS search list:" );
                memcpy ( &name, &dns_search, sizeof ( name ) );
                while ( name.offset != name.len ) {
                        DBG ( " %s", dns_name ( &name ) );
                        offset = dns_skip_search ( &name );
                        if ( offset < 0 )
                                break;
                        name.offset = offset;
                }
                DBG ( "\n" );
        }

        return 0;
}

Variable Documentation

struct sockaddr sa

Definition at line 68 of file dns.c.

Referenced by ftp_reply(), ib_create_path(), ib_create_service_madx(), and ib_mcast_mad().

u8 st

Definition at line 69 of file dns.c.

Referenced by arbel_create_cq(), arbel_create_eq(), hermon_create_cq(), and hermon_create_eq().

struct sockaddr_in sin

Definition at line 71 of file dns.c.

Referenced by ipv6_sock_aton(), ipv6_sock_ntoa(), nslookup_resolv_done(), and x509_check_ipaddress().

union { ... } nameserver [static]

The DNS server.

Referenced by apply_dns_settings(), and dns_resolv().

struct dns_name dns_search [static]

The DNS search list.

Definition at line 79 of file dns.c.

Referenced by apply_dns_search(), apply_dns_settings(), and dns_resolv().

Initial value:

DNS socket interface operations.

Definition at line 894 of file dns.c.

Initial value:

DNS socket interface descriptor.

Definition at line 900 of file dns.c.

Referenced by dns_resolv().

Initial value:

DNS resolver interface operations.

Definition at line 904 of file dns.c.

Initial value:

DNS resolver interface descriptor.

Definition at line 910 of file dns.c.

Referenced by dns_resolv().

struct setting_type setting_type_dnssl __setting_type
Initial value:
 {
        .name = "dnssl",
        .format = format_dnssl_setting,
}

A DNS search list setting type.

A string setting type.

Definition at line 1069 of file dns.c.

Initial value:
 {
        .apply = apply_dns_settings,
}

DNS settings applicator.

Definition at line 1178 of file dns.c.