[dns] Use all configured DNS servers

When no response is obtained from the first configured DNS server,
fall back to attempting the other configured servers.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
pull/121/head
Michael Brown 2020-07-15 19:10:30 +01:00
parent a95a2eafc5
commit 366206517e
1 changed files with 130 additions and 49 deletions

View File

@ -63,18 +63,33 @@ FEATURE ( FEATURE_PROTOCOL, "DNS", DHCP_EB_FEATURE_DNS, 1 );
#define EINFO_ENXIO_NO_NAMESERVER \ #define EINFO_ENXIO_NO_NAMESERVER \
__einfo_uniqify ( EINFO_ENXIO, 0x02, "No DNS servers available" ) __einfo_uniqify ( EINFO_ENXIO, 0x02, "No DNS servers available" )
/** The DNS server */ /** A DNS server list */
static union { struct dns_server {
struct sockaddr sa; /** Server list */
struct sockaddr_tcpip st; union {
struct sockaddr_in sin; /** IPv4 addresses */
struct sockaddr_in6 sin6; struct in_addr *in;
} nameserver = { /** IPv6 addresses */
.st = { struct in6_addr *in6;
.st_port = htons ( DNS_PORT ), /** Raw data */
}, void *data;
};
/** Number of servers */
unsigned int count;
}; };
/** IPv4 DNS server list */
static struct dns_server dns4;
/** IPv6 DNS server list */
static struct dns_server dns6;
/** Total number of DNS servers */
static unsigned int dns_count;
/** Current DNS server index */
static unsigned int dns_index;
/** The DNS search list */ /** The DNS search list */
static struct dns_name dns_search; static struct dns_name dns_search;
@ -555,6 +570,9 @@ static int dns_question ( struct dns_request *dns ) {
/* Restore name */ /* Restore name */
dns->name.offset = offsetof ( typeof ( dns->buf ), name ); dns->name.offset = offsetof ( typeof ( dns->buf ), name );
/* Reset query ID */
dns->buf.query.id = 0;
DBGC2 ( dns, "DNS %p question is %s type %s\n", dns, DBGC2 ( dns, "DNS %p question is %s type %s\n", dns,
dns_name ( &dns->name ), dns_type ( dns->question->qtype ) ); dns_name ( &dns->name ), dns_type ( dns->question->qtype ) );
@ -569,24 +587,54 @@ static int dns_question ( struct dns_request *dns ) {
*/ */
static int dns_send_packet ( struct dns_request *dns ) { static int dns_send_packet ( struct dns_request *dns ) {
struct dns_header *query = &dns->buf.query; struct dns_header *query = &dns->buf.query;
union {
struct sockaddr sa;
struct sockaddr_tcpip st;
struct sockaddr_in sin;
struct sockaddr_in6 sin6;
} nameserver;
struct xfer_metadata meta;
unsigned int index;
/* Start retransmission timer */ /* Start retransmission timer */
start_timer ( &dns->timer ); start_timer ( &dns->timer );
/* Generate query identifier */ /* Construct DNS server address */
query->id = random(); memset ( &nameserver, 0, sizeof ( nameserver ) );
nameserver.st.st_port = htons ( DNS_PORT );
if ( ! dns_count ) {
DBGC ( dns, "DNS %p lost DNS servers mid query\n", dns );
return -EINVAL;
}
index = ( dns_index % dns_count );
if ( index < dns6.count ) {
nameserver.sin6.sin6_family = AF_INET6;
memcpy ( &nameserver.sin6.sin6_addr, &dns6.in6[index],
sizeof ( nameserver.sin6.sin6_addr ) );
} else {
nameserver.sin.sin_family = AF_INET;
nameserver.sin.sin_addr = dns4.in[index - dns6.count];
}
/* Construct metadata */
memset ( &meta, 0, sizeof ( meta ) );
meta.dest = &nameserver.sa;
/* Generate query identifier if applicable */
if ( ! query->id )
query->id = random();
/* Send query */ /* Send query */
DBGC ( dns, "DNS %p sending query ID %#04x for %s type %s\n", dns, DBGC ( dns, "DNS %p sending %s query ID %#04x for %s type %s\n", dns,
ntohs ( query->id ), dns_name ( &dns->name ), sock_ntoa ( &nameserver.sa ), ntohs ( query->id ),
dns_type ( dns->question->qtype ) ); dns_name ( &dns->name ), dns_type ( dns->question->qtype ) );
/* Send the data */ /* Send the data */
return xfer_deliver_raw ( &dns->socket, query, dns->len ); return xfer_deliver_raw_meta ( &dns->socket, query, dns->len, &meta );
} }
/** /**
* Handle DNS retransmission timer expiry * Handle DNS (re)transmission timer expiry
* *
* @v timer Retry timer * @v timer Retry timer
* @v fail Failure indicator * @v fail Failure indicator
@ -595,11 +643,18 @@ static void dns_timer_expired ( struct retry_timer *timer, int fail ) {
struct dns_request *dns = struct dns_request *dns =
container_of ( timer, struct dns_request, timer ); container_of ( timer, struct dns_request, timer );
/* Terminate DNS request on failure */
if ( fail ) { if ( fail ) {
dns_done ( dns, -ETIMEDOUT ); dns_done ( dns, -ETIMEDOUT );
} else { return;
dns_send_packet ( dns );
} }
/* Move to next DNS server if this is a retransmission */
if ( dns->buf.query.id )
dns_index++;
/* Send DNS query */
dns_send_packet ( dns );
} }
/** /**
@ -927,7 +982,7 @@ static int dns_resolv ( struct interface *resolv,
int rc; int rc;
/* Fail immediately if no DNS servers */ /* Fail immediately if no DNS servers */
if ( ! nameserver.sa.sa_family ) { if ( dns_count == 0 ) {
DBG ( "DNS not attempting to resolve \"%s\": " DBG ( "DNS not attempting to resolve \"%s\": "
"no DNS servers\n", name ); "no DNS servers\n", name );
rc = -ENXIO_NO_NAMESERVER; rc = -ENXIO_NO_NAMESERVER;
@ -953,17 +1008,8 @@ static int dns_resolv ( struct interface *resolv,
memcpy ( dns->search.data, dns_search.data, search_len ); memcpy ( dns->search.data, dns_search.data, search_len );
/* Determine initial query type */ /* Determine initial query type */
switch ( nameserver.sa.sa_family ) { dns->qtype = ( ( dns6.count != 0 ) ?
case AF_INET: htons ( DNS_TYPE_AAAA ) : htons ( DNS_TYPE_A ) );
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 */ /* Construct query */
query = &dns->buf.query; query = &dns->buf.query;
@ -984,7 +1030,7 @@ static int dns_resolv ( struct interface *resolv,
/* Open UDP connection */ /* Open UDP connection */
if ( ( rc = xfer_open_socket ( &dns->socket, SOCK_DGRAM, if ( ( rc = xfer_open_socket ( &dns->socket, SOCK_DGRAM,
&nameserver.sa, NULL ) ) != 0 ) { NULL, NULL ) ) != 0 ) {
DBGC ( dns, "DNS %p could not open socket: %s\n", DBGC ( dns, "DNS %p could not open socket: %s\n",
dns, strerror ( rc ) ); dns, strerror ( rc ) );
goto err_open_socket; goto err_open_socket;
@ -1001,7 +1047,6 @@ static int dns_resolv ( struct interface *resolv,
err_open_socket: err_open_socket:
err_question: err_question:
err_encode: err_encode:
err_type:
ref_put ( &dns->refcnt ); ref_put ( &dns->refcnt );
err_alloc_dns: err_alloc_dns:
err_no_nameserver: err_no_nameserver:
@ -1096,6 +1141,31 @@ const struct setting dnssl_setting __setting ( SETTING_IP_EXTRA, dnssl ) = {
.type = &setting_type_dnssl, .type = &setting_type_dnssl,
}; };
/**
* Apply DNS server addresses
*
*/
static void apply_dns_servers ( void ) {
int len;
/* Free existing server addresses */
free ( dns4.data );
free ( dns6.data );
dns4.data = NULL;
dns6.data = NULL;
dns4.count = 0;
dns6.count = 0;
/* Fetch DNS server addresses */
len = fetch_raw_setting_copy ( NULL, &dns_setting, &dns4.data );
if ( len >= 0 )
dns4.count = ( len / sizeof ( dns4.in[0] ) );
len = fetch_raw_setting_copy ( NULL, &dns6_setting, &dns6.data );
if ( len >= 0 )
dns6.count = ( len / sizeof ( dns6.in6[0] ) );
dns_count = ( dns4.count + dns6.count );
}
/** /**
* Apply DNS search list * Apply DNS search list
* *
@ -1109,8 +1179,7 @@ static void apply_dns_search ( void ) {
memset ( &dns_search, 0, sizeof ( dns_search ) ); memset ( &dns_search, 0, sizeof ( dns_search ) );
/* Fetch DNS search list */ /* Fetch DNS search list */
len = fetch_setting_copy ( NULL, &dnssl_setting, NULL, NULL, len = fetch_raw_setting_copy ( NULL, &dnssl_setting, &dns_search.data );
&dns_search.data );
if ( len >= 0 ) { if ( len >= 0 ) {
dns_search.len = len; dns_search.len = len;
return; return;
@ -1138,19 +1207,31 @@ static void apply_dns_search ( void ) {
* @ret rc Return status code * @ret rc Return status code
*/ */
static int apply_dns_settings ( void ) { static int apply_dns_settings ( void ) {
void *dbgcol = &dns_count;
/* Fetch DNS server address */ /* Fetch DNS server address */
nameserver.sa.sa_family = 0; apply_dns_servers();
if ( fetch_ipv6_setting ( NULL, &dns6_setting, if ( DBG_LOG && ( dns_count != 0 ) ) {
&nameserver.sin6.sin6_addr ) >= 0 ) { union {
nameserver.sin6.sin6_family = AF_INET6; struct sockaddr sa;
} else if ( fetch_ipv4_setting ( NULL, &dns_setting, struct sockaddr_in sin;
&nameserver.sin.sin_addr ) >= 0 ) { struct sockaddr_in6 sin6;
nameserver.sin.sin_family = AF_INET; } u;
} unsigned int i;
if ( nameserver.sa.sa_family ) {
DBG ( "DNS using nameserver %s\n", DBGC ( dbgcol, "DNS servers:" );
sock_ntoa ( &nameserver.sa ) ); for ( i = 0 ; i < dns6.count ; i++ ) {
u.sin6.sin6_family = AF_INET6;
memcpy ( &u.sin6.sin6_addr, &dns6.in6[i],
sizeof ( u.sin6.sin6_addr ) );
DBGC ( dbgcol, " %s", sock_ntoa ( &u.sa ) );
}
for ( i = 0 ; i < dns4.count ; i++ ) {
u.sin.sin_family = AF_INET;
u.sin.sin_addr = dns4.in[i];
DBGC ( dbgcol, " %s", sock_ntoa ( &u.sa ) );
}
DBGC ( dbgcol, "\n" );
} }
/* Fetch DNS search list */ /* Fetch DNS search list */
@ -1159,16 +1240,16 @@ static int apply_dns_settings ( void ) {
struct dns_name name; struct dns_name name;
int offset; int offset;
DBG ( "DNS search list:" ); DBGC ( dbgcol, "DNS search list:" );
memcpy ( &name, &dns_search, sizeof ( name ) ); memcpy ( &name, &dns_search, sizeof ( name ) );
while ( name.offset != name.len ) { while ( name.offset != name.len ) {
DBG ( " %s", dns_name ( &name ) ); DBGC ( dbgcol, " %s", dns_name ( &name ) );
offset = dns_skip_search ( &name ); offset = dns_skip_search ( &name );
if ( offset < 0 ) if ( offset < 0 )
break; break;
name.offset = offset; name.offset = offset;
} }
DBG ( "\n" ); DBGC ( dbgcol, "\n" );
} }
return 0; return 0;