176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/* 276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>. 376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Portions copyright (C) 2004 Anselm M. Hoffmeister 576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * <stockholm@users.sourceforge.net>. 676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * This program is free software; you can redistribute it and/or 876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * modify it under the terms of the GNU General Public License as 976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * published by the Free Software Foundation; either version 2 of the 1076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * License, or any later version. 1176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 1276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * This program is distributed in the hope that it will be useful, but 1376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * WITHOUT ANY WARRANTY; without even the implied warranty of 1476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 1576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * General Public License for more details. 1676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 1776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * You should have received a copy of the GNU General Public License 1876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * along with this program; if not, write to the Free Software 1976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 2076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 2176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 2276d05dc695b06c4e987bb8078f78032441e1430cGreg HartmanFILE_LICENCE ( GPL2_OR_LATER ); 2376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 2476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <stdint.h> 2576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <stdlib.h> 2676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <string.h> 2776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <stdio.h> 2876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <errno.h> 2976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <byteswap.h> 3076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <gpxe/refcnt.h> 3176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <gpxe/xfer.h> 3276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <gpxe/open.h> 3376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <gpxe/resolv.h> 3476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <gpxe/retry.h> 3576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <gpxe/tcpip.h> 3676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <gpxe/settings.h> 3776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <gpxe/features.h> 3876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <gpxe/dns.h> 3976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 4076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** @file 4176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 4276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * DNS protocol 4376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 4476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 4576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 4676d05dc695b06c4e987bb8078f78032441e1430cGreg HartmanFEATURE ( FEATURE_PROTOCOL, "DNS", DHCP_EB_FEATURE_DNS, 1 ); 4776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 4876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** The DNS server */ 4976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic struct sockaddr_tcpip nameserver = { 5076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman .st_port = htons ( DNS_PORT ), 5176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}; 5276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 5376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** The local domain */ 5476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic char *localdomain; 5576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 5676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** A DNS request */ 5776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstruct dns_request { 5876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /** Reference counter */ 5976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct refcnt refcnt; 6076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /** Name resolution interface */ 6176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct resolv_interface resolv; 6276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /** Data transfer interface */ 6376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct xfer_interface socket; 6476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /** Retry timer */ 6576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct retry_timer timer; 6676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 6776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /** Socket address to fill in with resolved address */ 6876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct sockaddr sa; 6976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /** Current query packet */ 7076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct dns_query query; 7176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /** Location of query info structure within current packet 7276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 7376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * The query info structure is located immediately after the 7476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * compressed name. 7576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 7676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct dns_query_info *qinfo; 7776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /** Recursion counter */ 7876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman unsigned int recursion; 7976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}; 8076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 8176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** 8276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Mark DNS request as complete 8376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 8476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v dns DNS request 8576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v rc Return status code 8676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 8776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic void dns_done ( struct dns_request *dns, int rc ) { 8876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 8976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Stop the retry timer */ 9076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman stop_timer ( &dns->timer ); 9176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 9276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Close data transfer interface */ 9376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman xfer_nullify ( &dns->socket ); 9476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman xfer_close ( &dns->socket, rc ); 9576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 9676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Mark name resolution as complete */ 9776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman resolv_done ( &dns->resolv, &dns->sa, rc ); 9876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman} 9976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 10076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** 10176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Compare DNS reply name against the query name from the original request 10276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 10376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v dns DNS request 10476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v reply DNS reply 10576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v rname Reply name 10676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret zero Names match 10776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret non-zero Names do not match 10876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 10976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic int dns_name_cmp ( struct dns_request *dns, 11076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman const struct dns_header *reply, 11176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman const char *rname ) { 11276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman const char *qname = dns->query.payload; 11376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman int i; 11476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 11576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman while ( 1 ) { 11676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Obtain next section of rname */ 11776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman while ( ( *rname ) & 0xc0 ) { 11876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman rname = ( ( ( char * ) reply ) + 11976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman ( ntohs( *((uint16_t *)rname) ) & ~0xc000 )); 12076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 12176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Check that lengths match */ 12276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( *rname != *qname ) 12376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return -1; 12476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* If length is zero, we have reached the end */ 12576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( ! *qname ) 12676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return 0; 12776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Check that data matches */ 12876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman for ( i = *qname + 1; i > 0 ; i-- ) { 12976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( *(rname++) != *(qname++) ) 13076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return -1; 13176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 13276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 13376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman} 13476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 13576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** 13676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Skip over a (possibly compressed) DNS name 13776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 13876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v name DNS name 13976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret name Next DNS name 14076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 14176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic const char * dns_skip_name ( const char *name ) { 14276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman while ( 1 ) { 14376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( ! *name ) { 14476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* End of name */ 14576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return ( name + 1); 14676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 14776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( *name & 0xc0 ) { 14876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Start of a compressed name */ 14976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return ( name + 2 ); 15076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 15176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Uncompressed name portion */ 15276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman name += *name + 1; 15376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 15476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman} 15576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 15676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** 15776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Find an RR in a reply packet corresponding to our query 15876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 15976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v dns DNS request 16076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v reply DNS reply 16176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret rr DNS RR, or NULL if not found 16276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 16376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic union dns_rr_info * dns_find_rr ( struct dns_request *dns, 16476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman const struct dns_header *reply ) { 16576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman int i, cmp; 16676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman const char *p = ( ( char * ) reply ) + sizeof ( struct dns_header ); 16776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman union dns_rr_info *rr_info; 16876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 16976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Skip over the questions section */ 17076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman for ( i = ntohs ( reply->qdcount ) ; i > 0 ; i-- ) { 17176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman p = dns_skip_name ( p ) + sizeof ( struct dns_query_info ); 17276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 17376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 17476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Process the answers section */ 17576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman for ( i = ntohs ( reply->ancount ) ; i > 0 ; i-- ) { 17676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman cmp = dns_name_cmp ( dns, reply, p ); 17776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman p = dns_skip_name ( p ); 17876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman rr_info = ( ( union dns_rr_info * ) p ); 17976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( cmp == 0 ) 18076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return rr_info; 18176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman p += ( sizeof ( rr_info->common ) + 18276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman ntohs ( rr_info->common.rdlength ) ); 18376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 18476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 18576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return NULL; 18676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman} 18776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 18876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** 18976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Append DHCP domain name if available and name is not fully qualified 19076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 19176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v string Name as a NUL-terminated string 19276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret fqdn Fully-qualified domain name, malloc'd copy 19376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 19476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * The caller must free fqdn which is allocated even if the name is already 19576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * fully qualified. 19676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 19776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic char * dns_qualify_name ( const char *string ) { 19876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman char *fqdn; 19976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 20076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Leave unchanged if already fully-qualified or no local domain */ 20176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( ( ! localdomain ) || ( strchr ( string, '.' ) != 0 ) ) 20276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return strdup ( string ); 20376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 20476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Append local domain to name */ 20576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman asprintf ( &fqdn, "%s.%s", string, localdomain ); 20676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return fqdn; 20776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman} 20876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 20976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** 21076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Convert a standard NUL-terminated string to a DNS name 21176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 21276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v string Name as a NUL-terminated string 21376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v buf Buffer in which to place DNS name 21476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret next Byte following constructed DNS name 21576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 21676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * DNS names consist of "<length>element" pairs. 21776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 21876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic char * dns_make_name ( const char *string, char *buf ) { 21976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman char *length_byte = buf++; 22076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman char c; 22176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 22276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman while ( ( c = *(string++) ) ) { 22376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( c == '.' ) { 22476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *length_byte = buf - length_byte - 1; 22576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman length_byte = buf; 22676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 22776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *(buf++) = c; 22876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 22976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *length_byte = buf - length_byte - 1; 23076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *(buf++) = '\0'; 23176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return buf; 23276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman} 23376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 23476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** 23576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Convert an uncompressed DNS name to a NUL-terminated string 23676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 23776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v name DNS name 23876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret string NUL-terminated string 23976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 24076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Produce a printable version of a DNS name. Used only for debugging. 24176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 24276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic inline char * dns_unmake_name ( char *name ) { 24376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman char *p; 24476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman unsigned int len; 24576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 24676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman p = name; 24776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman while ( ( len = *p ) ) { 24876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *(p++) = '.'; 24976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman p += len; 25076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 25176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 25276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return name + 1; 25376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman} 25476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 25576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** 25676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Decompress a DNS name 25776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 25876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v reply DNS replay 25976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v name DNS name 26076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v buf Buffer into which to decompress DNS name 26176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret next Byte following decompressed DNS name 26276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 26376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic char * dns_decompress_name ( const struct dns_header *reply, 26476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman const char *name, char *buf ) { 26576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman int i, len; 26676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 26776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman do { 26876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Obtain next section of name */ 26976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman while ( ( *name ) & 0xc0 ) { 27076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman name = ( ( char * ) reply + 27176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman ( ntohs ( *((uint16_t *)name) ) & ~0xc000 ) ); 27276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 27376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Copy data */ 27476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman len = *name; 27576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman for ( i = len + 1 ; i > 0 ; i-- ) { 27676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *(buf++) = *(name++); 27776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 27876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } while ( len ); 27976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return buf; 28076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman} 28176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 28276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** 28376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Send next packet in DNS request 28476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 28576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v dns DNS request 28676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 28776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic int dns_send_packet ( struct dns_request *dns ) { 28876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman static unsigned int qid = 0; 28976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman size_t qlen; 29076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 29176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Increment query ID */ 29276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman dns->query.dns.id = htons ( ++qid ); 29376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 29476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBGC ( dns, "DNS %p sending query ID %d\n", dns, qid ); 29576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 29676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Start retransmission timer */ 29776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman start_timer ( &dns->timer ); 29876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 29976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Send the data */ 30076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman qlen = ( ( ( void * ) dns->qinfo ) - ( ( void * ) &dns->query ) 30176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman + sizeof ( dns->qinfo ) ); 30276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return xfer_deliver_raw ( &dns->socket, &dns->query, qlen ); 30376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman} 30476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 30576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** 30676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Handle DNS retransmission timer expiry 30776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 30876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v timer Retry timer 30976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v fail Failure indicator 31076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 31176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic void dns_timer_expired ( struct retry_timer *timer, int fail ) { 31276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct dns_request *dns = 31376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman container_of ( timer, struct dns_request, timer ); 31476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 31576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( fail ) { 31676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman dns_done ( dns, -ETIMEDOUT ); 31776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } else { 31876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman dns_send_packet ( dns ); 31976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 32076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman} 32176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 32276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** 32376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Receive new data 32476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 32576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v socket UDP socket 32676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v data DNS reply 32776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v len Length of DNS reply 32876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret rc Return status code 32976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 33076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic int dns_xfer_deliver_raw ( struct xfer_interface *socket, 33176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman const void *data, size_t len ) { 33276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct dns_request *dns = 33376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman container_of ( socket, struct dns_request, socket ); 33476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman const struct dns_header *reply = data; 33576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman union dns_rr_info *rr_info; 33676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct sockaddr_in *sin; 33776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman unsigned int qtype = dns->qinfo->qtype; 33876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 33976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Sanity check */ 34076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( len < sizeof ( *reply ) ) { 34176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBGC ( dns, "DNS %p received underlength packet length %zd\n", 34276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman dns, len ); 34376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return -EINVAL; 34476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 34576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 34676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Check reply ID matches query ID */ 34776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( reply->id != dns->query.dns.id ) { 34876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBGC ( dns, "DNS %p received unexpected reply ID %d " 34976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman "(wanted %d)\n", dns, ntohs ( reply->id ), 35076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman ntohs ( dns->query.dns.id ) ); 35176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return -EINVAL; 35276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 35376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 35476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBGC ( dns, "DNS %p received reply ID %d\n", dns, ntohs ( reply->id )); 35576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 35676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Stop the retry timer. After this point, each code path 35776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * must either restart the timer by calling dns_send_packet(), 35876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * or mark the DNS operation as complete by calling 35976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * dns_done() 36076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 36176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman stop_timer ( &dns->timer ); 36276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 36376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Search through response for useful answers. Do this 36476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * multiple times, to take advantage of useful nameservers 36576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * which send us e.g. the CNAME *and* the A record for the 36676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * pointed-to name. 36776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 36876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman while ( ( rr_info = dns_find_rr ( dns, reply ) ) ) { 36976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman switch ( rr_info->common.type ) { 37076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 37176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman case htons ( DNS_TYPE_A ): 37276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 37376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Found the target A record */ 37476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBGC ( dns, "DNS %p found address %s\n", 37576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman dns, inet_ntoa ( rr_info->a.in_addr ) ); 37676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman sin = ( struct sockaddr_in * ) &dns->sa; 37776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman sin->sin_family = AF_INET; 37876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman sin->sin_addr = rr_info->a.in_addr; 37976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 38076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Mark operation as complete */ 38176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman dns_done ( dns, 0 ); 38276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return 0; 38376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 38476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman case htons ( DNS_TYPE_CNAME ): 38576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 38676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Found a CNAME record; update query and recurse */ 38776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBGC ( dns, "DNS %p found CNAME\n", dns ); 38876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman dns->qinfo = ( void * ) dns_decompress_name ( reply, 38976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman rr_info->cname.cname, 39076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman dns->query.payload ); 39176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman dns->qinfo->qtype = htons ( DNS_TYPE_A ); 39276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman dns->qinfo->qclass = htons ( DNS_CLASS_IN ); 39376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 39476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Terminate the operation if we recurse too far */ 39576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( ++dns->recursion > DNS_MAX_CNAME_RECURSION ) { 39676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBGC ( dns, "DNS %p recursion exceeded\n", 39776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman dns ); 39876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman dns_done ( dns, -ELOOP ); 39976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return 0; 40076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 40176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman break; 40276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 40376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman default: 40476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBGC ( dns, "DNS %p got unknown record type %d\n", 40576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman dns, ntohs ( rr_info->common.type ) ); 40676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman break; 40776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 40876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 40976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 41076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Determine what to do next based on the type of query we 41176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * issued and the reponse we received 41276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 41376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman switch ( qtype ) { 41476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 41576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman case htons ( DNS_TYPE_A ): 41676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* We asked for an A record and got nothing; 41776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * try the CNAME. 41876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 41976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBGC ( dns, "DNS %p found no A record; trying CNAME\n", dns ); 42076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman dns->qinfo->qtype = htons ( DNS_TYPE_CNAME ); 42176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman dns_send_packet ( dns ); 42276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return 0; 42376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 42476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman case htons ( DNS_TYPE_CNAME ): 42576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* We asked for a CNAME record. If we got a response 42676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * (i.e. if the next A query is already set up), then 42776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * issue it, otherwise abort. 42876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 42976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( dns->qinfo->qtype == htons ( DNS_TYPE_A ) ) { 43076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman dns_send_packet ( dns ); 43176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return 0; 43276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } else { 43376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBGC ( dns, "DNS %p found no CNAME record\n", dns ); 43476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman dns_done ( dns, -ENXIO ); 43576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return 0; 43676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 43776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 43876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman default: 43976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman assert ( 0 ); 44076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman dns_done ( dns, -EINVAL ); 44176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return 0; 44276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 44376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman} 44476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 44576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** 44676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Receive new data 44776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 44876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v socket UDP socket 44976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v rc Reason for close 45076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 45176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic void dns_xfer_close ( struct xfer_interface *socket, int rc ) { 45276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct dns_request *dns = 45376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman container_of ( socket, struct dns_request, socket ); 45476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 45576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( ! rc ) 45676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman rc = -ECONNABORTED; 45776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 45876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman dns_done ( dns, rc ); 45976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman} 46076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 46176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** DNS socket operations */ 46276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic struct xfer_interface_operations dns_socket_operations = { 46376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman .close = dns_xfer_close, 46476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman .vredirect = xfer_vreopen, 46576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman .window = unlimited_xfer_window, 46676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman .alloc_iob = default_xfer_alloc_iob, 46776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman .deliver_iob = xfer_deliver_as_raw, 46876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman .deliver_raw = dns_xfer_deliver_raw, 46976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}; 47076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 47176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** 47276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Resolve name using DNS 47376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 47476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v resolv Name resolution interface 47576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v name Name to resolve 47676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v sa Socket address to fill in 47776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret rc Return status code 47876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 47976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic int dns_resolv ( struct resolv_interface *resolv, 48076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman const char *name, struct sockaddr *sa ) { 48176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct dns_request *dns; 48276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman char *fqdn; 48376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman int rc; 48476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 48576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Fail immediately if no DNS servers */ 48676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( ! nameserver.st_family ) { 48776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBG ( "DNS not attempting to resolve \"%s\": " 48876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman "no DNS servers\n", name ); 48976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman rc = -ENXIO; 49076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman goto err_no_nameserver; 49176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 49276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 49376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Ensure fully-qualified domain name if DHCP option was given */ 49476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman fqdn = dns_qualify_name ( name ); 49576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( ! fqdn ) { 49676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman rc = -ENOMEM; 49776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman goto err_qualify_name; 49876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 49976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 50076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Allocate DNS structure */ 50176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman dns = zalloc ( sizeof ( *dns ) ); 50276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( ! dns ) { 50376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman rc = -ENOMEM; 50476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman goto err_alloc_dns; 50576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 50676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman resolv_init ( &dns->resolv, &null_resolv_ops, &dns->refcnt ); 50776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman xfer_init ( &dns->socket, &dns_socket_operations, &dns->refcnt ); 50876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman dns->timer.expired = dns_timer_expired; 50976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman memcpy ( &dns->sa, sa, sizeof ( dns->sa ) ); 51076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 51176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Create query */ 51276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman dns->query.dns.flags = htons ( DNS_FLAG_QUERY | DNS_FLAG_OPCODE_QUERY | 51376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DNS_FLAG_RD ); 51476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman dns->query.dns.qdcount = htons ( 1 ); 51576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman dns->qinfo = ( void * ) dns_make_name ( fqdn, dns->query.payload ); 51676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman dns->qinfo->qtype = htons ( DNS_TYPE_A ); 51776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman dns->qinfo->qclass = htons ( DNS_CLASS_IN ); 51876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 51976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Open UDP connection */ 52076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( ( rc = xfer_open_socket ( &dns->socket, SOCK_DGRAM, 52176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman ( struct sockaddr * ) &nameserver, 52276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman NULL ) ) != 0 ) { 52376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBGC ( dns, "DNS %p could not open socket: %s\n", 52476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman dns, strerror ( rc ) ); 52576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman goto err_open_socket; 52676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 52776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 52876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Send first DNS packet */ 52976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman dns_send_packet ( dns ); 53076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 53176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Attach parent interface, mortalise self, and return */ 53276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman resolv_plug_plug ( &dns->resolv, resolv ); 53376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman ref_put ( &dns->refcnt ); 53476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman free ( fqdn ); 53576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return 0; 53676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 53776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman err_open_socket: 53876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman err_alloc_dns: 53976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman ref_put ( &dns->refcnt ); 54076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman err_qualify_name: 54176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman free ( fqdn ); 54276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman err_no_nameserver: 54376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return rc; 54476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman} 54576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 54676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** DNS name resolver */ 54776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstruct resolver dns_resolver __resolver ( RESOLV_NORMAL ) = { 54876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman .name = "DNS", 54976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman .resolv = dns_resolv, 55076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}; 55176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 55276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/****************************************************************************** 55376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 55476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Settings 55576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 55676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman ****************************************************************************** 55776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 55876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 55976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** DNS server setting */ 56076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstruct setting dns_setting __setting = { 56176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman .name = "dns", 56276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman .description = "DNS server", 56376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman .tag = DHCP_DNS_SERVERS, 56476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman .type = &setting_type_ipv4, 56576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}; 56676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 56776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** Domain name setting */ 56876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstruct setting domain_setting __setting = { 56976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman .name = "domain", 57076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman .description = "Local domain", 57176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman .tag = DHCP_DOMAIN_NAME, 57276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman .type = &setting_type_string, 57376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}; 57476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 57576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** 57676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Apply DNS settings 57776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * 57876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret rc Return status code 57976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */ 58076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic int apply_dns_settings ( void ) { 58176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman struct sockaddr_in *sin_nameserver = 58276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman ( struct sockaddr_in * ) &nameserver; 58376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman int len; 58476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 58576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( ( len = fetch_ipv4_setting ( NULL, &dns_setting, 58676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman &sin_nameserver->sin_addr ) ) >= 0 ){ 58776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman sin_nameserver->sin_family = AF_INET; 58876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBG ( "DNS using nameserver %s\n", 58976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman inet_ntoa ( sin_nameserver->sin_addr ) ); 59076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman } 59176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 59276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman /* Get local domain DHCP option */ 59376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman if ( ( len = fetch_string_setting_copy ( NULL, &domain_setting, 59476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman &localdomain ) ) >= 0 ) 59576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman DBG ( "DNS local domain %s\n", localdomain ); 59676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 59776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman return 0; 59876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman} 59976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman 60076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** DNS settings applicator */ 60176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstruct settings_applicator dns_applicator __settings_applicator = { 60276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman .apply = apply_dns_settings, 60376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}; 604