diff options
author | RajithaY <rajithax.yerrumsetty@intel.com> | 2017-04-25 03:31:15 -0700 |
---|---|---|
committer | Rajitha Yerrumchetty <rajithax.yerrumsetty@intel.com> | 2017-05-22 06:48:08 +0000 |
commit | bb756eebdac6fd24e8919e2c43f7d2c8c4091f59 (patch) | |
tree | ca11e03542edf2d8f631efeca5e1626d211107e3 /qemu/roms/ipxe/src/net/udp/dns.c | |
parent | a14b48d18a9ed03ec191cf16b162206998a895ce (diff) |
Adding qemu as a submodule of KVMFORNFV
This Patch includes the changes to add qemu as a submodule to
kvmfornfv repo and make use of the updated latest qemu for the
execution of all testcase
Change-Id: I1280af507a857675c7f81d30c95255635667bdd7
Signed-off-by:RajithaY<rajithax.yerrumsetty@intel.com>
Diffstat (limited to 'qemu/roms/ipxe/src/net/udp/dns.c')
-rw-r--r-- | qemu/roms/ipxe/src/net/udp/dns.c | 1156 |
1 files changed, 0 insertions, 1156 deletions
diff --git a/qemu/roms/ipxe/src/net/udp/dns.c b/qemu/roms/ipxe/src/net/udp/dns.c deleted file mode 100644 index 2d77477f6..000000000 --- a/qemu/roms/ipxe/src/net/udp/dns.c +++ /dev/null @@ -1,1156 +0,0 @@ -/* - * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>. - * - * Portions copyright (C) 2004 Anselm M. Hoffmeister - * <stockholm@users.sourceforge.net>. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - * - * You can also choose to distribute this program under the terms of - * the Unmodified Binary Distribution Licence (as given in the file - * COPYING.UBDL), provided that you have satisfied its requirements. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#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/dhcp.h> -#include <ipxe/dhcpv6.h> -#include <ipxe/dns.h> - -/** @file - * - * DNS protocol - * - */ - -FEATURE ( FEATURE_PROTOCOL, "DNS", DHCP_EB_FEATURE_DNS, 1 ); - -/* Disambiguate the various error causes */ -#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" ) - -/** The DNS server */ -static union { - struct sockaddr sa; - struct sockaddr_tcpip st; - struct sockaddr_in sin; - struct sockaddr_in6 sin6; -} nameserver = { - .st = { - .st_port = htons ( DNS_PORT ), - }, -}; - -/** The DNS search list */ -static struct dns_name dns_search; - -/** - * Encode a DNS name using RFC1035 encoding - * - * @v string DNS name as a string - * @v name DNS name to fill in - * @ret len Length of DNS name, or negative error - */ -int dns_encode ( const char *string, struct dns_name *name ) { - 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 ); -} - -/** - * Find start of valid label within an RFC1035-encoded DNS name - * - * @v name DNS name - * @v offset Current offset - * @ret offset Offset of label, or negative error - */ -static int dns_label ( struct dns_name *name, size_t offset ) { - 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; - } -} - -/** - * Decode RFC1035-encoded DNS name - * - * @v name DNS name - * @v data Output buffer - * @v len Length of output buffer - * @ret len Length of decoded DNS name, or negative error - */ -int dns_decode ( struct dns_name *name, char *data, size_t len ) { - 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; -} - -/** - * Compare DNS names for equality - * - * @v first First DNS name - * @v second Second DNS name - * @ret rc Return status code - */ -int dns_compare ( struct dns_name *first, struct dns_name *second ) { - 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; -} - -/** - * Copy a DNS name - * - * @v src Source DNS name - * @v dst Destination DNS name - * @ret len Length of copied DNS name, or negative error - */ -int dns_copy ( struct dns_name *src, struct dns_name *dst ) { - 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; -} - -/** - * Skip RFC1035-encoded DNS name - * - * @v name DNS name - * @ret offset Offset to next name, or negative error - */ -int dns_skip ( struct dns_name *name ) { - 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; -} - -/** - * Skip RFC1035-encoded DNS name in search list - * - * @v name DNS name - * @ret offset Offset to next non-empty name, or negative error - */ -static int dns_skip_search ( struct dns_name *name ) { - 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; -} - -/** - * Transcribe DNS name (for debugging) - * - * @v name DNS name - * @ret string Transcribed DNS name - */ -static const char * dns_name ( struct dns_name *name ) { - static char buf[256]; - int len; - - len = dns_decode ( name, buf, sizeof ( buf ) ); - return ( ( len < 0 ) ? "<INVALID>" : buf ); -} - -/** - * Name a DNS query type (for debugging) - * - * @v type Query type (in network byte order) - * @ret name Type name - */ -static const char * dns_type ( uint16_t type ) { - 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>"; - } -} - -/** A DNS request */ -struct dns_request { - /** Reference counter */ - struct refcnt refcnt; - /** Name resolution interface */ - struct interface resolv; - /** Data transfer interface */ - struct interface socket; - /** Retry timer */ - struct retry_timer timer; - - /** Socket address to fill in with resolved address */ - union { - struct sockaddr sa; - struct sockaddr_in sin; - struct sockaddr_in6 sin6; - } address; - /** Initial query type */ - uint16_t qtype; - /** Buffer for current query */ - struct { - /** Query header */ - struct dns_header query; - /** Name buffer */ - char name[DNS_MAX_NAME_LEN]; - /** Space for question */ - struct dns_question padding; - } __attribute__ (( packed )) buf; - /** Current query name */ - struct dns_name name; - /** Question within current query */ - struct dns_question *question; - /** Length of current query */ - size_t len; - /** Offset of search suffix within current query */ - size_t offset; - /** Search list */ - struct dns_name search; - /** Recursion counter */ - unsigned int recursion; -}; - -/** - * Mark DNS request as complete - * - * @v dns DNS request - * @v rc Return status code - */ -static void dns_done ( struct dns_request *dns, int rc ) { - - /* Stop the retry timer */ - stop_timer ( &dns->timer ); - - /* Shut down interfaces */ - intf_shutdown ( &dns->socket, rc ); - intf_shutdown ( &dns->resolv, rc ); -} - -/** - * Mark DNS request as resolved and complete - * - * @v dns DNS request - * @v rc Return status code - */ -static void dns_resolved ( struct dns_request *dns ) { - - 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 ); -} - -/** - * Construct DNS question - * - * @v dns DNS request - * @ret rc Return status code - */ -static int dns_question ( struct dns_request *dns ) { - 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; -} - -/** - * Send DNS query - * - * @v dns DNS request - * @ret rc Return status code - */ -static int dns_send_packet ( struct dns_request *dns ) { - 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 ); -} - -/** - * Handle DNS retransmission timer expiry - * - * @v timer Retry timer - * @v fail Failure indicator - */ -static void dns_timer_expired ( struct retry_timer *timer, int fail ) { - struct dns_request *dns = - container_of ( timer, struct dns_request, timer ); - - if ( fail ) { - dns_done ( dns, -ETIMEDOUT ); - } else { - dns_send_packet ( dns ); - } -} - -/** - * Receive new data - * - * @v dns DNS request - * @v iobuf I/O buffer - * @v meta Data transfer metadata - * @ret rc Return status code - */ -static int dns_xfer_deliver ( struct dns_request *dns, - struct io_buffer *iobuf, - struct xfer_metadata *meta __unused ) { - 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; -} - -/** - * Receive new data - * - * @v dns DNS request - * @v rc Reason for close - */ -static void dns_xfer_close ( struct dns_request *dns, int rc ) { - - if ( ! rc ) - rc = -ECONNABORTED; - - dns_done ( dns, rc ); -} - -/** DNS socket interface operations */ -static struct interface_operation dns_socket_operations[] = { - INTF_OP ( xfer_deliver, struct dns_request *, dns_xfer_deliver ), - INTF_OP ( intf_close, struct dns_request *, dns_xfer_close ), -}; - -/** DNS socket interface descriptor */ -static struct interface_descriptor dns_socket_desc = - INTF_DESC ( struct dns_request, socket, dns_socket_operations ); - -/** DNS resolver interface operations */ -static struct interface_operation dns_resolv_op[] = { - INTF_OP ( intf_close, struct dns_request *, dns_done ), -}; - -/** DNS resolver interface descriptor */ -static struct interface_descriptor dns_resolv_desc = - INTF_DESC ( struct dns_request, resolv, dns_resolv_op ); - -/** - * Resolve name using DNS - * - * @v resolv Name resolution interface - * @v name Name to resolve - * @v sa Socket address to fill in - * @ret rc Return status code - */ -static int dns_resolv ( struct interface *resolv, - const char *name, struct sockaddr *sa ) { - 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; -} - -/** DNS name resolver */ -struct resolver dns_resolver __resolver ( RESOLV_NORMAL ) = { - .name = "DNS", - .resolv = dns_resolv, -}; - -/****************************************************************************** - * - * Settings - * - ****************************************************************************** - */ - -/** - * Format DNS search list setting - * - * @v type Setting type - * @v raw Raw setting value - * @v raw_len Length of raw setting value - * @v buf Buffer to contain formatted value - * @v len Length of buffer - * @ret len Length of formatted value, or negative error - */ -static int format_dnssl_setting ( const struct setting_type *type __unused, - const void *raw, size_t raw_len, - char *buf, size_t len ) { - 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; -} - -/** A DNS search list setting type */ -const struct setting_type setting_type_dnssl __setting_type = { - .name = "dnssl", - .format = format_dnssl_setting, -}; - -/** IPv4 DNS server setting */ -const struct setting dns_setting __setting ( SETTING_IP_EXTRA, dns ) = { - .name = "dns", - .description = "DNS server", - .tag = DHCP_DNS_SERVERS, - .type = &setting_type_ipv4, -}; - -/** IPv6 DNS server setting */ -const struct setting dns6_setting __setting ( SETTING_IP_EXTRA, dns6 ) = { - .name = "dns6", - .description = "DNS server", - .tag = DHCPV6_DNS_SERVERS, - .type = &setting_type_ipv6, - .scope = &ipv6_scope, -}; - -/** DNS search list */ -const struct setting dnssl_setting __setting ( SETTING_IP_EXTRA, dnssl ) = { - .name = "dnssl", - .description = "DNS search list", - .tag = DHCP_DOMAIN_SEARCH, - .type = &setting_type_dnssl, -}; - -/** - * Apply DNS search list - * - */ -static void apply_dns_search ( void ) { - 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; - } -} - -/** - * Apply DNS settings - * - * @ret rc Return status code - */ -static int apply_dns_settings ( void ) { - - /* 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; -} - -/** DNS settings applicator */ -struct settings_applicator dns_applicator __settings_applicator = { - .apply = apply_dns_settings, -}; |