#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_info * | dns_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. | |
Definition in file dns.c.
| FILE_LICENCE | ( | GPL2_OR_LATER | ) |
| FEATURE | ( | FEATURE_PROTOCOL | , | |
| "DNS" | , | |||
| DHCP_EB_FEATURE_DNS | , | |||
| 1 | ||||
| ) |
| static void dns_done | ( | struct dns_request * | dns, | |
| int | rc | |||
| ) | [static] |
Mark DNS request as complete.
| 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.
| dns | DNS request | |
| reply | DNS reply | |
| rname | Reply name |
| 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.
| name | DNS name |
| 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.
| dns | DNS request | |
| reply | DNS reply |
| 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.
| string | Name as a NUL-terminated string |
| fqdn | Fully-qualified domain name, malloc'd copy |
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.
| string | Name as a NUL-terminated string | |
| buf | Buffer in which to place DNS name |
| next | Byte following constructed DNS name |
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.
| name | DNS name |
| string | NUL-terminated string |
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.
| reply | DNS replay | |
| name | DNS name | |
| buf | Buffer into which to decompress DNS name |
| 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.
| 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.
| 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.
| socket | UDP socket | |
| data | DNS reply | |
| len | Length of DNS reply |
| 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.
| 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.
| resolv | Name resolution interface | |
| name | Name to resolve | |
| sa | Socket address to fill in |
| 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 }
| static int apply_dns_settings | ( | void | ) | [static] |
Apply DNS settings.
| 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 }
struct sockaddr_tcpip nameserver [static] |
char* localdomain [static] |
The local domain.
Definition at line 54 of file dns.c.
Referenced by apply_dns_settings(), and dns_qualify_name().
struct xfer_interface_operations dns_socket_operations [static] |
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,
}
| struct settings_applicator dns_applicator __settings_applicator |
1.5.7.1