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