1/* 2 * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>. 3 * 4 * Portions copyright (C) 2004 Anselm M. Hoffmeister 5 * <stockholm@users.sourceforge.net>. 6 * 7 * This program is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU General Public License as 9 * published by the Free Software Foundation; either version 2 of the 10 * License, or any later version. 11 * 12 * This program is distributed in the hope that it will be useful, but 13 * WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program; if not, write to the Free Software 19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 20 */ 21 22FILE_LICENCE ( GPL2_OR_LATER ); 23 24#include <stdint.h> 25#include <stdlib.h> 26#include <string.h> 27#include <stdio.h> 28#include <errno.h> 29#include <byteswap.h> 30#include <gpxe/refcnt.h> 31#include <gpxe/xfer.h> 32#include <gpxe/open.h> 33#include <gpxe/resolv.h> 34#include <gpxe/retry.h> 35#include <gpxe/tcpip.h> 36#include <gpxe/settings.h> 37#include <gpxe/features.h> 38#include <gpxe/dns.h> 39 40/** @file 41 * 42 * DNS protocol 43 * 44 */ 45 46FEATURE ( FEATURE_PROTOCOL, "DNS", DHCP_EB_FEATURE_DNS, 1 ); 47 48/** The DNS server */ 49static struct sockaddr_tcpip nameserver = { 50 .st_port = htons ( DNS_PORT ), 51}; 52 53/** The local domain */ 54static char *localdomain; 55 56/** A DNS request */ 57struct dns_request { 58 /** Reference counter */ 59 struct refcnt refcnt; 60 /** Name resolution interface */ 61 struct resolv_interface resolv; 62 /** Data transfer interface */ 63 struct xfer_interface socket; 64 /** Retry timer */ 65 struct retry_timer timer; 66 67 /** Socket address to fill in with resolved address */ 68 struct sockaddr sa; 69 /** Current query packet */ 70 struct dns_query query; 71 /** Location of query info structure within current packet 72 * 73 * The query info structure is located immediately after the 74 * compressed name. 75 */ 76 struct dns_query_info *qinfo; 77 /** Recursion counter */ 78 unsigned int recursion; 79}; 80 81/** 82 * Mark DNS request as complete 83 * 84 * @v dns DNS request 85 * @v rc Return status code 86 */ 87static void dns_done ( struct dns_request *dns, int rc ) { 88 89 /* Stop the retry timer */ 90 stop_timer ( &dns->timer ); 91 92 /* Close data transfer interface */ 93 xfer_nullify ( &dns->socket ); 94 xfer_close ( &dns->socket, rc ); 95 96 /* Mark name resolution as complete */ 97 resolv_done ( &dns->resolv, &dns->sa, rc ); 98} 99 100/** 101 * Compare DNS reply name against the query name from the original request 102 * 103 * @v dns DNS request 104 * @v reply DNS reply 105 * @v rname Reply name 106 * @ret zero Names match 107 * @ret non-zero Names do not match 108 */ 109static int dns_name_cmp ( struct dns_request *dns, 110 const struct dns_header *reply, 111 const char *rname ) { 112 const char *qname = dns->query.payload; 113 int i; 114 115 while ( 1 ) { 116 /* Obtain next section of rname */ 117 while ( ( *rname ) & 0xc0 ) { 118 rname = ( ( ( char * ) reply ) + 119 ( ntohs( *((uint16_t *)rname) ) & ~0xc000 )); 120 } 121 /* Check that lengths match */ 122 if ( *rname != *qname ) 123 return -1; 124 /* If length is zero, we have reached the end */ 125 if ( ! *qname ) 126 return 0; 127 /* Check that data matches */ 128 for ( i = *qname + 1; i > 0 ; i-- ) { 129 if ( *(rname++) != *(qname++) ) 130 return -1; 131 } 132 } 133} 134 135/** 136 * Skip over a (possibly compressed) DNS name 137 * 138 * @v name DNS name 139 * @ret name Next DNS name 140 */ 141static const char * dns_skip_name ( const char *name ) { 142 while ( 1 ) { 143 if ( ! *name ) { 144 /* End of name */ 145 return ( name + 1); 146 } 147 if ( *name & 0xc0 ) { 148 /* Start of a compressed name */ 149 return ( name + 2 ); 150 } 151 /* Uncompressed name portion */ 152 name += *name + 1; 153 } 154} 155 156/** 157 * Find an RR in a reply packet corresponding to our query 158 * 159 * @v dns DNS request 160 * @v reply DNS reply 161 * @ret rr DNS RR, or NULL if not found 162 */ 163static union dns_rr_info * dns_find_rr ( struct dns_request *dns, 164 const struct dns_header *reply ) { 165 int i, cmp; 166 const char *p = ( ( char * ) reply ) + sizeof ( struct dns_header ); 167 union dns_rr_info *rr_info; 168 169 /* Skip over the questions section */ 170 for ( i = ntohs ( reply->qdcount ) ; i > 0 ; i-- ) { 171 p = dns_skip_name ( p ) + sizeof ( struct dns_query_info ); 172 } 173 174 /* Process the answers section */ 175 for ( i = ntohs ( reply->ancount ) ; i > 0 ; i-- ) { 176 cmp = dns_name_cmp ( dns, reply, p ); 177 p = dns_skip_name ( p ); 178 rr_info = ( ( union dns_rr_info * ) p ); 179 if ( cmp == 0 ) 180 return rr_info; 181 p += ( sizeof ( rr_info->common ) + 182 ntohs ( rr_info->common.rdlength ) ); 183 } 184 185 return NULL; 186} 187 188/** 189 * Append DHCP domain name if available and name is not fully qualified 190 * 191 * @v string Name as a NUL-terminated string 192 * @ret fqdn Fully-qualified domain name, malloc'd copy 193 * 194 * The caller must free fqdn which is allocated even if the name is already 195 * fully qualified. 196 */ 197static char * dns_qualify_name ( const char *string ) { 198 char *fqdn; 199 200 /* Leave unchanged if already fully-qualified or no local domain */ 201 if ( ( ! localdomain ) || ( strchr ( string, '.' ) != 0 ) ) 202 return strdup ( string ); 203 204 /* Append local domain to name */ 205 asprintf ( &fqdn, "%s.%s", string, localdomain ); 206 return fqdn; 207} 208 209/** 210 * Convert a standard NUL-terminated string to a DNS name 211 * 212 * @v string Name as a NUL-terminated string 213 * @v buf Buffer in which to place DNS name 214 * @ret next Byte following constructed DNS name 215 * 216 * DNS names consist of "<length>element" pairs. 217 */ 218static char * dns_make_name ( const char *string, char *buf ) { 219 char *length_byte = buf++; 220 char c; 221 222 while ( ( c = *(string++) ) ) { 223 if ( c == '.' ) { 224 *length_byte = buf - length_byte - 1; 225 length_byte = buf; 226 } 227 *(buf++) = c; 228 } 229 *length_byte = buf - length_byte - 1; 230 *(buf++) = '\0'; 231 return buf; 232} 233 234/** 235 * Convert an uncompressed DNS name to a NUL-terminated string 236 * 237 * @v name DNS name 238 * @ret string NUL-terminated string 239 * 240 * Produce a printable version of a DNS name. Used only for debugging. 241 */ 242static inline char * dns_unmake_name ( char *name ) { 243 char *p; 244 unsigned int len; 245 246 p = name; 247 while ( ( len = *p ) ) { 248 *(p++) = '.'; 249 p += len; 250 } 251 252 return name + 1; 253} 254 255/** 256 * Decompress a DNS name 257 * 258 * @v reply DNS replay 259 * @v name DNS name 260 * @v buf Buffer into which to decompress DNS name 261 * @ret next Byte following decompressed DNS name 262 */ 263static char * dns_decompress_name ( const struct dns_header *reply, 264 const char *name, char *buf ) { 265 int i, len; 266 267 do { 268 /* Obtain next section of name */ 269 while ( ( *name ) & 0xc0 ) { 270 name = ( ( char * ) reply + 271 ( ntohs ( *((uint16_t *)name) ) & ~0xc000 ) ); 272 } 273 /* Copy data */ 274 len = *name; 275 for ( i = len + 1 ; i > 0 ; i-- ) { 276 *(buf++) = *(name++); 277 } 278 } while ( len ); 279 return buf; 280} 281 282/** 283 * Send next packet in DNS request 284 * 285 * @v dns DNS request 286 */ 287static int dns_send_packet ( struct dns_request *dns ) { 288 static unsigned int qid = 0; 289 size_t qlen; 290 291 /* Increment query ID */ 292 dns->query.dns.id = htons ( ++qid ); 293 294 DBGC ( dns, "DNS %p sending query ID %d\n", dns, qid ); 295 296 /* Start retransmission timer */ 297 start_timer ( &dns->timer ); 298 299 /* Send the data */ 300 qlen = ( ( ( void * ) dns->qinfo ) - ( ( void * ) &dns->query ) 301 + sizeof ( dns->qinfo ) ); 302 return xfer_deliver_raw ( &dns->socket, &dns->query, qlen ); 303} 304 305/** 306 * Handle DNS retransmission timer expiry 307 * 308 * @v timer Retry timer 309 * @v fail Failure indicator 310 */ 311static void dns_timer_expired ( struct retry_timer *timer, int fail ) { 312 struct dns_request *dns = 313 container_of ( timer, struct dns_request, timer ); 314 315 if ( fail ) { 316 dns_done ( dns, -ETIMEDOUT ); 317 } else { 318 dns_send_packet ( dns ); 319 } 320} 321 322/** 323 * Receive new data 324 * 325 * @v socket UDP socket 326 * @v data DNS reply 327 * @v len Length of DNS reply 328 * @ret rc Return status code 329 */ 330static int dns_xfer_deliver_raw ( struct xfer_interface *socket, 331 const void *data, size_t len ) { 332 struct dns_request *dns = 333 container_of ( socket, struct dns_request, socket ); 334 const struct dns_header *reply = data; 335 union dns_rr_info *rr_info; 336 struct sockaddr_in *sin; 337 unsigned int qtype = dns->qinfo->qtype; 338 339 /* Sanity check */ 340 if ( len < sizeof ( *reply ) ) { 341 DBGC ( dns, "DNS %p received underlength packet length %zd\n", 342 dns, len ); 343 return -EINVAL; 344 } 345 346 /* Check reply ID matches query ID */ 347 if ( reply->id != dns->query.dns.id ) { 348 DBGC ( dns, "DNS %p received unexpected reply ID %d " 349 "(wanted %d)\n", dns, ntohs ( reply->id ), 350 ntohs ( dns->query.dns.id ) ); 351 return -EINVAL; 352 } 353 354 DBGC ( dns, "DNS %p received reply ID %d\n", dns, ntohs ( reply->id )); 355 356 /* Stop the retry timer. After this point, each code path 357 * must either restart the timer by calling dns_send_packet(), 358 * or mark the DNS operation as complete by calling 359 * dns_done() 360 */ 361 stop_timer ( &dns->timer ); 362 363 /* Search through response for useful answers. Do this 364 * multiple times, to take advantage of useful nameservers 365 * which send us e.g. the CNAME *and* the A record for the 366 * pointed-to name. 367 */ 368 while ( ( rr_info = dns_find_rr ( dns, reply ) ) ) { 369 switch ( rr_info->common.type ) { 370 371 case htons ( DNS_TYPE_A ): 372 373 /* Found the target A record */ 374 DBGC ( dns, "DNS %p found address %s\n", 375 dns, inet_ntoa ( rr_info->a.in_addr ) ); 376 sin = ( struct sockaddr_in * ) &dns->sa; 377 sin->sin_family = AF_INET; 378 sin->sin_addr = rr_info->a.in_addr; 379 380 /* Mark operation as complete */ 381 dns_done ( dns, 0 ); 382 return 0; 383 384 case htons ( DNS_TYPE_CNAME ): 385 386 /* Found a CNAME record; update query and recurse */ 387 DBGC ( dns, "DNS %p found CNAME\n", dns ); 388 dns->qinfo = ( void * ) dns_decompress_name ( reply, 389 rr_info->cname.cname, 390 dns->query.payload ); 391 dns->qinfo->qtype = htons ( DNS_TYPE_A ); 392 dns->qinfo->qclass = htons ( DNS_CLASS_IN ); 393 394 /* Terminate the operation if we recurse too far */ 395 if ( ++dns->recursion > DNS_MAX_CNAME_RECURSION ) { 396 DBGC ( dns, "DNS %p recursion exceeded\n", 397 dns ); 398 dns_done ( dns, -ELOOP ); 399 return 0; 400 } 401 break; 402 403 default: 404 DBGC ( dns, "DNS %p got unknown record type %d\n", 405 dns, ntohs ( rr_info->common.type ) ); 406 break; 407 } 408 } 409 410 /* Determine what to do next based on the type of query we 411 * issued and the reponse we received 412 */ 413 switch ( qtype ) { 414 415 case htons ( DNS_TYPE_A ): 416 /* We asked for an A record and got nothing; 417 * try the CNAME. 418 */ 419 DBGC ( dns, "DNS %p found no A record; trying CNAME\n", dns ); 420 dns->qinfo->qtype = htons ( DNS_TYPE_CNAME ); 421 dns_send_packet ( dns ); 422 return 0; 423 424 case htons ( DNS_TYPE_CNAME ): 425 /* We asked for a CNAME record. If we got a response 426 * (i.e. if the next A query is already set up), then 427 * issue it, otherwise abort. 428 */ 429 if ( dns->qinfo->qtype == htons ( DNS_TYPE_A ) ) { 430 dns_send_packet ( dns ); 431 return 0; 432 } else { 433 DBGC ( dns, "DNS %p found no CNAME record\n", dns ); 434 dns_done ( dns, -ENXIO ); 435 return 0; 436 } 437 438 default: 439 assert ( 0 ); 440 dns_done ( dns, -EINVAL ); 441 return 0; 442 } 443} 444 445/** 446 * Receive new data 447 * 448 * @v socket UDP socket 449 * @v rc Reason for close 450 */ 451static void dns_xfer_close ( struct xfer_interface *socket, int rc ) { 452 struct dns_request *dns = 453 container_of ( socket, struct dns_request, socket ); 454 455 if ( ! rc ) 456 rc = -ECONNABORTED; 457 458 dns_done ( dns, rc ); 459} 460 461/** DNS socket operations */ 462static struct xfer_interface_operations dns_socket_operations = { 463 .close = dns_xfer_close, 464 .vredirect = xfer_vreopen, 465 .window = unlimited_xfer_window, 466 .alloc_iob = default_xfer_alloc_iob, 467 .deliver_iob = xfer_deliver_as_raw, 468 .deliver_raw = dns_xfer_deliver_raw, 469}; 470 471/** 472 * Resolve name using DNS 473 * 474 * @v resolv Name resolution interface 475 * @v name Name to resolve 476 * @v sa Socket address to fill in 477 * @ret rc Return status code 478 */ 479static int dns_resolv ( struct resolv_interface *resolv, 480 const char *name, struct sockaddr *sa ) { 481 struct dns_request *dns; 482 char *fqdn; 483 int rc; 484 485 /* Fail immediately if no DNS servers */ 486 if ( ! nameserver.st_family ) { 487 DBG ( "DNS not attempting to resolve \"%s\": " 488 "no DNS servers\n", name ); 489 rc = -ENXIO; 490 goto err_no_nameserver; 491 } 492 493 /* Ensure fully-qualified domain name if DHCP option was given */ 494 fqdn = dns_qualify_name ( name ); 495 if ( ! fqdn ) { 496 rc = -ENOMEM; 497 goto err_qualify_name; 498 } 499 500 /* Allocate DNS structure */ 501 dns = zalloc ( sizeof ( *dns ) ); 502 if ( ! dns ) { 503 rc = -ENOMEM; 504 goto err_alloc_dns; 505 } 506 resolv_init ( &dns->resolv, &null_resolv_ops, &dns->refcnt ); 507 xfer_init ( &dns->socket, &dns_socket_operations, &dns->refcnt ); 508 dns->timer.expired = dns_timer_expired; 509 memcpy ( &dns->sa, sa, sizeof ( dns->sa ) ); 510 511 /* Create query */ 512 dns->query.dns.flags = htons ( DNS_FLAG_QUERY | DNS_FLAG_OPCODE_QUERY | 513 DNS_FLAG_RD ); 514 dns->query.dns.qdcount = htons ( 1 ); 515 dns->qinfo = ( void * ) dns_make_name ( fqdn, dns->query.payload ); 516 dns->qinfo->qtype = htons ( DNS_TYPE_A ); 517 dns->qinfo->qclass = htons ( DNS_CLASS_IN ); 518 519 /* Open UDP connection */ 520 if ( ( rc = xfer_open_socket ( &dns->socket, SOCK_DGRAM, 521 ( struct sockaddr * ) &nameserver, 522 NULL ) ) != 0 ) { 523 DBGC ( dns, "DNS %p could not open socket: %s\n", 524 dns, strerror ( rc ) ); 525 goto err_open_socket; 526 } 527 528 /* Send first DNS packet */ 529 dns_send_packet ( dns ); 530 531 /* Attach parent interface, mortalise self, and return */ 532 resolv_plug_plug ( &dns->resolv, resolv ); 533 ref_put ( &dns->refcnt ); 534 free ( fqdn ); 535 return 0; 536 537 err_open_socket: 538 err_alloc_dns: 539 ref_put ( &dns->refcnt ); 540 err_qualify_name: 541 free ( fqdn ); 542 err_no_nameserver: 543 return rc; 544} 545 546/** DNS name resolver */ 547struct resolver dns_resolver __resolver ( RESOLV_NORMAL ) = { 548 .name = "DNS", 549 .resolv = dns_resolv, 550}; 551 552/****************************************************************************** 553 * 554 * Settings 555 * 556 ****************************************************************************** 557 */ 558 559/** DNS server setting */ 560struct setting dns_setting __setting = { 561 .name = "dns", 562 .description = "DNS server", 563 .tag = DHCP_DNS_SERVERS, 564 .type = &setting_type_ipv4, 565}; 566 567/** Domain name setting */ 568struct setting domain_setting __setting = { 569 .name = "domain", 570 .description = "Local domain", 571 .tag = DHCP_DOMAIN_NAME, 572 .type = &setting_type_string, 573}; 574 575/** 576 * Apply DNS settings 577 * 578 * @ret rc Return status code 579 */ 580static int apply_dns_settings ( void ) { 581 struct sockaddr_in *sin_nameserver = 582 ( struct sockaddr_in * ) &nameserver; 583 int len; 584 585 if ( ( len = fetch_ipv4_setting ( NULL, &dns_setting, 586 &sin_nameserver->sin_addr ) ) >= 0 ){ 587 sin_nameserver->sin_family = AF_INET; 588 DBG ( "DNS using nameserver %s\n", 589 inet_ntoa ( sin_nameserver->sin_addr ) ); 590 } 591 592 /* Get local domain DHCP option */ 593 if ( ( len = fetch_string_setting_copy ( NULL, &domain_setting, 594 &localdomain ) ) >= 0 ) 595 DBG ( "DNS local domain %s\n", localdomain ); 596 597 return 0; 598} 599 600/** DNS settings applicator */ 601struct settings_applicator dns_applicator __settings_applicator = { 602 .apply = apply_dns_settings, 603}; 604