diff --git a/src/include/gpxe/settings.h b/src/include/gpxe/settings.h index e9f0bce5..60244d3a 100644 --- a/src/include/gpxe/settings.h +++ b/src/include/gpxe/settings.h @@ -218,6 +218,7 @@ extern struct setting ip_setting __setting; extern struct setting netmask_setting __setting; extern struct setting gateway_setting __setting; extern struct setting dns_setting __setting; +extern struct setting domain_setting __setting; extern struct setting hostname_setting __setting; extern struct setting filename_setting __setting; extern struct setting root_path_setting __setting; diff --git a/src/net/udp/dns.c b/src/net/udp/dns.c index 1bcdbc7e..a498aefc 100644 --- a/src/net/udp/dns.c +++ b/src/net/udp/dns.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -47,6 +48,9 @@ static struct sockaddr_tcpip nameserver = { .st_port = htons ( DNS_PORT ), }; +/** The local domain */ +static char *localdomain; + /** A DNS request */ struct dns_request { /** Reference counter */ @@ -179,6 +183,27 @@ static union dns_rr_info * dns_find_rr ( struct dns_request *dns, return NULL; } +/** + * Append DHCP domain name if available and name is not fully qualified + * + * @v string Name as a NUL-terminated string + * @ret fqdn Fully-qualified domain name, malloc'd copy + * + * The caller must free fqdn which is allocated even if the name is already + * fully qualified. + */ +static char * dns_qualify_name ( const char *string ) { + char *fqdn; + + /* Leave unchanged if already fully-qualified or no local domain */ + if ( ( ! localdomain ) || ( strchr ( string, '.' ) != 0 ) ) + return strdup ( string ); + + /* Append local domain to name */ + asprintf ( &fqdn, "%s.%s", string, localdomain ); + return fqdn; +} + /** * Convert a standard NUL-terminated string to a DNS name * @@ -452,19 +477,30 @@ static struct xfer_interface_operations dns_socket_operations = { static int dns_resolv ( struct resolv_interface *resolv, const char *name, struct sockaddr *sa ) { struct dns_request *dns; + char *fqdn; int rc; /* Fail immediately if no DNS servers */ if ( ! nameserver.st_family ) { DBG ( "DNS not attempting to resolve \"%s\": " "no DNS servers\n", name ); - return -ENXIO; + rc = -ENXIO; + goto err_no_nameserver; + } + + /* Ensure fully-qualified domain name if DHCP option was given */ + fqdn = dns_qualify_name ( name ); + if ( ! fqdn ) { + rc = -ENOMEM; + goto err_qualify_name; } /* Allocate DNS structure */ dns = zalloc ( sizeof ( *dns ) ); - if ( ! dns ) - return -ENOMEM; + if ( ! dns ) { + rc = -ENOMEM; + goto err_alloc_dns; + } resolv_init ( &dns->resolv, &null_resolv_ops, &dns->refcnt ); xfer_init ( &dns->socket, &dns_socket_operations, &dns->refcnt ); dns->timer.expired = dns_timer_expired; @@ -474,7 +510,7 @@ static int dns_resolv ( struct resolv_interface *resolv, dns->query.dns.flags = htons ( DNS_FLAG_QUERY | DNS_FLAG_OPCODE_QUERY | DNS_FLAG_RD ); dns->query.dns.qdcount = htons ( 1 ); - dns->qinfo = ( void * ) dns_make_name ( name, dns->query.payload ); + dns->qinfo = ( void * ) dns_make_name ( fqdn, dns->query.payload ); dns->qinfo->qtype = htons ( DNS_TYPE_A ); dns->qinfo->qclass = htons ( DNS_CLASS_IN ); @@ -484,7 +520,7 @@ static int dns_resolv ( struct resolv_interface *resolv, NULL ) ) != 0 ) { DBGC ( dns, "DNS %p could not open socket: %s\n", dns, strerror ( rc ) ); - goto err; + goto err_open_socket; } /* Send first DNS packet */ @@ -493,10 +529,15 @@ static int dns_resolv ( struct resolv_interface *resolv, /* Attach parent interface, mortalise self, and return */ resolv_plug_plug ( &dns->resolv, resolv ); ref_put ( &dns->refcnt ); + free ( fqdn ); return 0; - err: + err_open_socket: + err_alloc_dns: ref_put ( &dns->refcnt ); + err_qualify_name: + free ( fqdn ); + err_no_nameserver: return rc; } @@ -521,12 +562,20 @@ struct setting dns_setting __setting = { .type = &setting_type_ipv4, }; +/** Domain name setting */ +struct setting domain_setting __setting = { + .name = "domain", + .description = "Local domain", + .tag = DHCP_DOMAIN_NAME, + .type = &setting_type_string, +}; + /** - * Apply nameserver setting + * Apply DNS settings * * @ret rc Return status code */ -static int apply_nameserver_setting ( void ) { +static int apply_dns_settings ( void ) { struct sockaddr_in *sin_nameserver = ( struct sockaddr_in * ) &nameserver; int len; @@ -538,10 +587,15 @@ static int apply_nameserver_setting ( void ) { inet_ntoa ( sin_nameserver->sin_addr ) ); } + /* Get local domain DHCP option */ + if ( ( len = fetch_string_setting_copy ( NULL, &domain_setting, + &localdomain ) ) >= 0 ) + DBG ( "DNS local domain %s\n", localdomain ); + return 0; } -/** Nameserver setting applicator */ -struct settings_applicator nameserver_applicator __settings_applicator = { - .apply = apply_nameserver_setting, +/** DNS settings applicator */ +struct settings_applicator dns_applicator __settings_applicator = { + .apply = apply_dns_settings, };