From 9af12d5fba1483ec5e95ac302b3f5c5aeb3662c1 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 15 Jan 2007 17:31:35 +0000 Subject: [PATCH] A working DNS resolver (not yet tied in to anything) --- src/include/errno.h | 1 + src/include/{ => gpxe}/dns.h | 61 +++-- src/include/nmb.h | 2 +- src/net/udp/dns.c | 463 +++++++++++++++++++++++++++++++++++ src/proto/dns.c | 361 --------------------------- src/proto/nmb.c | 6 +- 6 files changed, 509 insertions(+), 385 deletions(-) rename src/include/{ => gpxe}/dns.h (67%) create mode 100644 src/net/udp/dns.c delete mode 100644 src/proto/dns.c diff --git a/src/include/errno.h b/src/include/errno.h index 3025fa96..541d9cbd 100644 --- a/src/include/errno.h +++ b/src/include/errno.h @@ -137,6 +137,7 @@ #define EINVAL 0xdd /**< Invalid argument */ #define EIO 0xde /**< Input/output error */ #define EISCONN 0xdf /**< Transport endpoint is already connected */ +#define ELOOP 0xf8 /**< Too many symbolic links */ #define EMFILE 0xe0 /**< Too many open files */ #define EMSGSIZE 0xe1 /**< Message too long */ #define ENAMETOOLONG 0xe2 /**< File name too long */ diff --git a/src/include/dns.h b/src/include/gpxe/dns.h similarity index 67% rename from src/include/dns.h rename to src/include/gpxe/dns.h index c72c6909..49292d58 100644 --- a/src/include/dns.h +++ b/src/include/gpxe/dns.h @@ -1,10 +1,16 @@ -#ifndef DNS_RESOLVER_H -#define DNS_RESOLVER_H +#ifndef _GPXE_DNS_H +#define _GPXE_DNS_H -#include "stdint.h" +/** @file + * + * DNS protocol + * + */ + +#include #include -#include "ip.h" -#include "udp.h" +#include +#include /* * Constants @@ -33,7 +39,7 @@ #define DNS_FLAG_RCODE_NX ( 0x03 << 0 ) #define DNS_FLAG_RCODE(flags) ( (flags) & ( 0x0f << 0 ) ) -#define DNS_UDP_PORT 53 +#define DNS_PORT 53 #define DNS_MAX_RETRIES 3 #define DNS_MAX_CNAME_RECURSION 0x30 @@ -56,13 +62,11 @@ struct dns_query_info { } __attribute__ (( packed )); struct dns_query { - struct iphdr ip; - struct udphdr udp; struct dns_header dns; char payload[ 256 + sizeof ( struct dns_query_info ) ]; } __attribute__ (( packed )); -struct dns_rr_info { +struct dns_rr_info_common { uint16_t type; uint16_t class; uint32_t ttl; @@ -70,23 +74,36 @@ struct dns_rr_info { } __attribute__ (( packed )); struct dns_rr_info_a { - struct dns_rr_info info; + struct dns_rr_info_common common; struct in_addr in_addr; } __attribute__ (( packed )); struct dns_rr_info_cname { - struct dns_rr_info info; - char cname[0]; + struct dns_rr_info_common common; + char cname[0]; } __attribute__ (( packed )); -/* - * Functions in dns.c (used by nmb.c) - * - */ -extern struct dns_header * dns_query ( struct dns_query *query, - unsigned int query_len, - struct sockaddr_in *nameserver ); -extern struct dns_rr_info * dns_find_rr ( struct dns_query *query, - struct dns_header *reply ); +union dns_rr_info { + struct dns_rr_info_common common; + struct dns_rr_info_a a; + struct dns_rr_info_cname cname; +}; -#endif /* DNS_RESOLVER_H */ +struct dns_request { + + struct sockaddr_tcpip *st; + + struct async async; + struct dns_query query; + struct dns_query_info *qinfo; + + unsigned int recursion; + + struct udp_connection udp; + struct retry_timer timer; +}; + +extern int dns_resolv ( const char *name, struct sockaddr_tcpip *st, + struct async *parent ); + +#endif /* _GPXE_DNS_H */ diff --git a/src/include/nmb.h b/src/include/nmb.h index 7948d9e4..3e551ffd 100644 --- a/src/include/nmb.h +++ b/src/include/nmb.h @@ -1,7 +1,7 @@ #ifndef NMB_H #define NMB_H -#include "dns.h" +#include /* * NetBIOS name query packets are basically the same as DNS packets, diff --git a/src/net/udp/dns.c b/src/net/udp/dns.c new file mode 100644 index 00000000..17075d68 --- /dev/null +++ b/src/net/udp/dns.c @@ -0,0 +1,463 @@ +/* + * Copyright (C) 2006 Michael Brown . + * + * Portions copyright (C) 2004 Anselm M. Hoffmeister + * . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * DNS protocol + * + */ + +/** + * Compare DNS reply name against the query name from the original request + * + * @v dns DNS request + * @v reply DNS reply + * @v rname Reply name + * @ret zero Names match + * @ret non-zero Names do not match + */ +static int dns_name_cmp ( struct dns_request *dns, struct dns_header *reply, + const char *rname ) { + const char *qname = dns->query.payload; + int i; + + while ( 1 ) { + /* Obtain next section of rname */ + while ( ( *rname ) & 0xc0 ) { + rname = ( ( ( char * ) reply ) + + ( ntohs( *((uint16_t *)rname) ) & ~0xc000 )); + } + /* Check that lengths match */ + if ( *rname != *qname ) + return -1; + /* If length is zero, we have reached the end */ + if ( ! *qname ) + return 0; + /* Check that data matches */ + for ( i = *qname + 1; i > 0 ; i-- ) { + if ( *(rname++) != *(qname++) ) + return -1; + } + } +} + +/** + * Skip over a (possibly compressed) DNS name + * + * @v name DNS name + * @ret name Next DNS name + */ +static const char * dns_skip_name ( const char *name ) { + while ( 1 ) { + if ( ! *name ) { + /* End of name */ + return ( name + 1); + } + if ( *name & 0xc0 ) { + /* Start of a compressed name */ + return ( name + 2 ); + } + /* Uncompressed name portion */ + name += *name + 1; + } +} + +/** + * Find an RR in a reply packet corresponding to our query + * + * @v dns DNS request + * @v reply DNS reply + * @ret rr DNS RR, or NULL if not found + */ +static union dns_rr_info * dns_find_rr ( struct dns_request *dns, + struct dns_header *reply ) { + int i, cmp; + const char *p = ( ( char * ) reply ) + sizeof ( struct dns_header ); + union dns_rr_info *rr_info; + + /* Skip over the questions section */ + for ( i = ntohs ( reply->qdcount ) ; i > 0 ; i-- ) { + p = dns_skip_name ( p ) + sizeof ( struct dns_query_info ); + } + + /* Process the answers section */ + for ( i = ntohs ( reply->ancount ) ; i > 0 ; i-- ) { + cmp = dns_name_cmp ( dns, reply, p ); + p = dns_skip_name ( p ); + rr_info = ( ( union dns_rr_info * ) p ); + if ( cmp == 0 ) + return rr_info; + p += ( sizeof ( rr_info->common ) + + ntohs ( rr_info->common.rdlength ) ); + } + + return NULL; +} + +/** + * Convert a standard NUL-terminated string to a DNS name + * + * @v string Name as a NUL-terminated string + * @v buf Buffer in which to place DNS name + * @ret next Byte following constructed DNS name + * + * DNS names consist of "element" pairs. + */ +static char * dns_make_name ( const char *string, char *buf ) { + char *length_byte = buf++; + char c; + + while ( ( c = *(string++) ) ) { + if ( c == '.' ) { + *length_byte = buf - length_byte - 1; + length_byte = buf; + } + *(buf++) = c; + } + *length_byte = buf - length_byte - 1; + *(buf++) = '\0'; + return buf; +} + +/** + * Convert an uncompressed DNS name to a NUL-terminated string + * + * @v name DNS name + * @ret string NUL-terminated string + * + * Produce a printable version of a DNS name. Used only for debugging. + */ +static inline char * dns_unmake_name ( char *name ) { + char *p; + unsigned int len; + + p = name; + while ( ( len = *p ) ) { + *(p++) = '.'; + p += len; + } + + return name + 1; +} + +/** + * Decompress a DNS name + * + * @v reply DNS replay + * @v name DNS name + * @v buf Buffer into which to decompress DNS name + * @ret next Byte following decompressed DNS name + */ +static char * dns_decompress_name ( struct dns_header *reply, + const char *name, char *buf ) { + int i, len; + + do { + /* Obtain next section of name */ + while ( ( *name ) & 0xc0 ) { + name = ( ( char * ) reply + + ( ntohs ( *((uint16_t *)name) ) & ~0xc000 ) ); + } + /* Copy data */ + len = *name; + for ( i = len + 1 ; i > 0 ; i-- ) { + *(buf++) = *(name++); + } + } while ( len ); + return buf; +} + +/** + * Mark DNS request as complete + * + * @v dns DNS request + * @v rc Return status code + */ +static void dns_done ( struct dns_request *dns, int rc ) { + + /* Stop the retry timer */ + stop_timer ( &dns->timer ); + + /* Close UDP connection */ + udp_close ( &dns->udp ); + + /* Mark async operation as complete */ + async_done ( &dns->async, rc ); +} + +/** + * Send next packet in DNS request + * + * @v dns DNS request + */ +static void dns_send_packet ( struct dns_request *dns ) { + static unsigned int qid = 0; + + /* Increment query ID */ + dns->query.dns.id = htons ( ++qid ); + + DBGC ( dns, "DNS %p sending query ID %d\n", dns, qid ); + + /* Start retransmission timer */ + start_timer ( &dns->timer ); + + /* Send the data */ + udp_send ( &dns->udp, &dns->query, + ( ( ( void * ) dns->qinfo ) - ( ( void * ) &dns->query ) + + sizeof ( dns->qinfo ) ) ); +} + +/** + * Handle DNS retransmission timer expiry + * + * @v timer Retry timer + * @v fail Failure indicator + */ +static void dns_timer_expired ( struct retry_timer *timer, int fail ) { + struct dns_request *dns = + container_of ( timer, struct dns_request, timer ); + + if ( fail ) { + dns_done ( dns, -ETIMEDOUT ); + } else { + dns_send_packet ( dns ); + } +} + +/** + * Receive new data + * + * @v udp UDP connection + * @v data Received data + * @v len Length of received data + * @v st_src Partially-filled source address + * @v st_dest Partially-filled destination address + */ +static int dns_newdata ( struct udp_connection *conn, void *data, size_t len, + struct sockaddr_tcpip *st_src __unused, + struct sockaddr_tcpip *st_dest __unused ) { + struct dns_request *dns = + container_of ( conn, struct dns_request, udp ); + struct dns_header *reply = data; + union dns_rr_info *rr_info; + struct sockaddr_in *sin; + unsigned int qtype = dns->qinfo->qtype; + + /* Sanity check */ + if ( len < sizeof ( *reply ) ) { + DBGC ( dns, "DNS %p received underlength packet length %zd\n", + dns, len ); + return -EINVAL; + } + + /* Check reply ID matches query ID */ + if ( reply->id != dns->query.dns.id ) { + DBGC ( dns, "DNS %p received unexpected reply ID %d " + "(wanted %d)\n", dns, ntohs ( reply->id ), + ntohs ( dns->query.dns.id ) ); + return -EINVAL; + } + + DBGC ( dns, "DNS %p received reply ID %d\n", dns, ntohs ( reply->id )); + + /* Stop the retry timer. After this point, each code path + * must either restart the timer by calling dns_send_packet(), + * or mark the DNS operation as complete by calling + * dns_done() + */ + stop_timer ( &dns->timer ); + + /* Search through response for useful answers. Do this + * multiple times, to take advantage of useful nameservers + * which send us e.g. the CNAME *and* the A record for the + * pointed-to name. + */ + while ( ( rr_info = dns_find_rr ( dns, reply ) ) ) { + switch ( rr_info->common.type ) { + + case htons ( DNS_TYPE_A ): + + /* Found the target A record */ + DBGC ( dns, "DNS %p found address %s\n", + dns, inet_ntoa ( rr_info->a.in_addr ) ); + sin = ( struct sockaddr_in * ) dns->st; + sin->sin_family = AF_INET; + sin->sin_addr = rr_info->a.in_addr; + + /* Mark operation as complete */ + dns_done ( dns, 0 ); + return 0; + + case htons ( DNS_TYPE_CNAME ): + + /* Found a CNAME record; update query and recurse */ + DBGC ( dns, "DNS %p found CNAME\n", dns ); + dns->qinfo = ( void * ) dns_decompress_name ( reply, + rr_info->cname.cname, + dns->query.payload ); + dns->qinfo->qtype = htons ( DNS_TYPE_A ); + dns->qinfo->qclass = htons ( DNS_CLASS_IN ); + + /* Terminate the operation if we recurse too far */ + if ( ++dns->recursion > DNS_MAX_CNAME_RECURSION ) { + DBGC ( dns, "DNS %p recursion exceeded\n", + dns ); + dns_done ( dns, -ELOOP ); + return 0; + } + break; + + default: + DBGC ( dns, "DNS %p got unknown record type %d\n", + dns, ntohs ( rr_info->common.type ) ); + break; + } + } + + /* Determine what to do next based on the type of query we + * issued and the reponse we received + */ + switch ( qtype ) { + + case htons ( DNS_TYPE_A ): + /* We asked for an A record and got nothing; + * try the CNAME. + */ + DBGC ( dns, "DNS %p found no A record; trying CNAME\n", dns ); + dns->qinfo->qtype = htons ( DNS_TYPE_CNAME ); + dns_send_packet ( dns ); + return 0; + + case htons ( DNS_TYPE_CNAME ): + /* We asked for a CNAME record. If we got a response + * (i.e. if the next A query is already set up), then + * issue it, otherwise abort. + */ + if ( dns->qinfo->qtype == htons ( DNS_TYPE_A ) ) { + dns_send_packet ( dns ); + return 0; + } else { + DBGC ( dns, "DNS %p found no CNAME record\n", dns ); + dns_done ( dns, -ENXIO ); + return 0; + } + + default: + assert ( 0 ); + dns_done ( dns, -EINVAL ); + return 0; + } +} + +/** DNS UDP operations */ +struct udp_operations dns_udp_operations = { + .newdata = dns_newdata, +}; + +/** + * Reap asynchronous operation + * + * @v async Asynchronous operation + */ +static void dns_reap ( struct async *async ) { + struct dns_request *dns = + container_of ( async, struct dns_request, async ); + + free ( dns ); +} + +/** DNS asynchronous operations */ +static struct async_operations dns_async_operations = { + .reap = dns_reap, +}; + +/** + * Resolve name using DNS + * + */ +int dns_resolv ( const char *name, struct sockaddr_tcpip *st, + struct async *parent ) { + struct dns_request *dns; + union { + struct sockaddr_tcpip st; + struct sockaddr_in sin; + } nameserver; + int rc; + + /* Allocate DNS structure */ + dns = malloc ( sizeof ( *dns ) ); + if ( ! dns ) { + rc = -ENOMEM; + goto err; + } + memset ( dns, 0, sizeof ( *dns ) ); + dns->st = st; + dns->timer.expired = dns_timer_expired; + dns->udp.udp_op = &dns_udp_operations; + + /* Create query */ + dns->query.dns.flags = htons ( DNS_FLAG_QUERY | DNS_FLAG_OPCODE_QUERY | + DNS_FLAG_RD ); + dns->query.dns.qdcount = htons ( 1 ); + dns->qinfo = ( void * ) dns_make_name ( name, dns->query.payload ); + dns->qinfo->qtype = htons ( DNS_TYPE_A ); + dns->qinfo->qclass = htons ( DNS_CLASS_IN ); + + /* Open UDP connection */ + memset ( &nameserver, 0, sizeof ( nameserver ) ); + nameserver.sin.sin_family = AF_INET; + nameserver.sin.sin_port = htons ( DNS_PORT ); +#warning "DHCP-DNS hack" + struct dhcp_option *option = + find_global_dhcp_option ( DHCP_DNS_SERVERS ); + if ( ! option ) { + DBGC ( dns, "DNS %p no name servers\n", dns ); + rc = -ENXIO; + goto err; + } + dhcp_ipv4_option ( option, &nameserver.sin.sin_addr ); + DBGC ( dns, "DNS %p using nameserver %s\n", dns, + inet_ntoa ( nameserver.sin.sin_addr ) ); + udp_connect ( &dns->udp, &nameserver.st ); + if ( ( rc = udp_open ( &dns->udp, 0 ) ) != 0 ) + goto err; + + dns_send_packet ( dns ); + + async_init ( &dns->async, &dns_async_operations, parent ); + return 0; + + err: + DBGC ( dns, "DNS %p could not create request: %s\n", + dns, strerror ( rc ) ); + free ( dns ); + return rc; +} diff --git a/src/proto/dns.c b/src/proto/dns.c deleted file mode 100644 index 60ea04a3..00000000 --- a/src/proto/dns.c +++ /dev/null @@ -1,361 +0,0 @@ -/************************************************************************** -* -* dns_resolver.c: Etherboot support for resolution of host/domain -* names in filename parameters -* Written 2004 by Anselm M. Hoffmeister -* -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 2 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the Free Software -* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -* -* This code is using nuts and bolts from throughout etherboot. -* It is a fresh implementation according to the DNS RFC, #1035 -* -* REVISION HISTORY: -* ================ -* 2004-05-10 File created -* 2004-05-19 First release to CVS -* 2004-05-22 CNAME support first stage finished -* 2004-05-24 First "stable" release to CVS -* 2004-08-28 Improve readability, set recursion flag -* 2005-04-30 Tidied up to the point of being a complete rewrite (mcb30) -***************************************************************************/ - -#include "etherboot.h" -#include "nic.h" -#include "resolv.h" -#include "dns.h" - -/* - * await_dns - * Shall be called on any incoming packet during the resolution process - * (as is the case with all the other await_ functions in etherboot) - * Param: as any await functions - * - */ -static int await_dns ( int ival, void *ptr, - unsigned short ptype __unused, - struct iphdr *ip __unused, - struct udphdr *udp, struct tcphdr *tcp __unused ) { - struct dns_header **header = ptr; - - if ( ! udp ) - return 0; - if ( ntohs ( udp->dest ) != ival ) - return 0; - *header = ( struct dns_header * ) ( udp + 1 ); - return 1; -} - -/* - * Send a name server query and wait for a response. Query is retried - * up to DNS_MAX_RETRIES times. Returns a pointer to the answer - * packet, or NULL if no answer was received. - * - */ -struct dns_header * dns_query ( struct dns_query *query, - unsigned int query_len, - struct sockaddr_in *nameserver ) { - long timeout; - int retry; - struct dns_header *reply; - - for ( retry = 0 ; retry < DNS_MAX_RETRIES ; retry++ ) { - udp_transmit ( nameserver->sin_addr.s_addr, - nameserver->sin_port, nameserver->sin_port, - query_len, query ); - timeout = rfc2131_sleep_interval ( TIMEOUT, retry ); - if ( ! await_reply ( await_dns, nameserver->sin_port, - &reply, timeout ) ) - continue; - if ( reply->id != query->dns.id ) { - DBG ( "DNS received unexpected reply ID %d " - "(wanted %d)\n", - ntohs ( reply->id ), ntohs ( query->dns.id ) ); - continue; - } - /* We have a valid reply! */ - return reply; - } - return NULL; -} - -/* - * Compare two DNS names to see if they are the same. Takes - * compressed names in the reply into account (though the query name - * must be uncompressed). Returns 0 for a match (for consistency with - * strcmp et al). - * - */ -static inline int dns_name_cmp ( const char *qname, const char *rname, - struct dns_header *reply ) { - int i; - while ( 1 ) { - /* Obtain next section of rname */ - while ( ( *rname ) & 0xc0 ) { - rname = ( ( char * ) reply + - ( ntohs( *((uint16_t *)rname) ) & ~0xc000 )); - } - /* Check that lengths match */ - if ( *rname != *qname ) - return 1; - /* If length is zero, we have reached the end */ - if ( ! *qname ) - return 0; - /* Check that data matches */ - for ( i = *qname + 1; i > 0 ; i-- ) { - if ( *(rname++) != *(qname++) ) - return 1; - } - } -} - -/* - * Skip over a DNS name, which may be compressed - * - */ -static inline const char * dns_skip_name ( const char *name ) { - while ( 1 ) { - if ( ! *name ) { - /* End of name */ - return ( name + 1); - } - if ( *name & 0xc0 ) { - /* Start of a compressed name */ - return ( name + 2 ); - } - /* Uncompressed name portion */ - name += *name + 1; - } -} - -/* - * Find a Resource Record in a reply packet corresponding to our - * query. Returns a pointer to the RR, or NULL if no answer found. - * - */ -struct dns_rr_info * dns_find_rr ( struct dns_query *query, - struct dns_header *reply ) { - int i, cmp; - const char *p = ( ( char * ) reply ) + sizeof ( struct dns_header ); - - /* Skip over the questions section */ - for ( i = ntohs ( reply->qdcount ) ; i > 0 ; i-- ) { - p = dns_skip_name ( p ) + sizeof ( struct dns_query_info ); - } - - /* Process the answers section */ - for ( i = ntohs ( reply->ancount ) ; i > 0 ; i-- ) { - cmp = dns_name_cmp ( query->payload, p, reply ); - p = dns_skip_name ( p ); - if ( cmp == 0 ) - return ( ( struct dns_rr_info * ) p ); - p += ( sizeof ( struct dns_rr_info ) + - ntohs ( ( ( struct dns_rr_info * ) p )->rdlength ) ); - } - - return NULL; -} - -/* - * Convert a standard NUL-terminated string to a DNS query name, - * consisting of "element" pairs. - * - * Returns a pointer to the character following the constructed DNS - * query name. - * - */ -static inline char * dns_make_name ( char *dest, const char *name ) { - char *length_byte = dest++; - char c; - - while ( ( c = *(name++) ) ) { - if ( c == '.' ) { - *length_byte = dest - length_byte - 1; - length_byte = dest; - } - *(dest++) = c; - } - *length_byte = dest - length_byte - 1; - *(dest++) = '\0'; - return dest; -} - -/* - * Produce a printable version of a DNS name. Used only for debugging. - * - */ -static inline char * dns_unmake_name ( char *name ) { - char *p; - unsigned int len; - - p = name; - while ( ( len = *p ) ) { - *(p++) = '.'; - p += len; - } - - return name + 1; -} - -/* - * Decompress a DNS name. - * - * Returns a pointer to the character following the decompressed DNS - * name. - * - */ -static inline char * dns_decompress_name ( char *dest, const char *name, - struct dns_header *header ) { - int i, len; - - do { - /* Obtain next section of name */ - while ( ( *name ) & 0xc0 ) { - name = ( ( char * ) header + - ( ntohs ( *((uint16_t *)name) ) & ~0xc000 ) ); - } - /* Copy data */ - len = *name; - for ( i = len + 1 ; i > 0 ; i-- ) { - *(dest++) = *(name++); - } - } while ( len ); - return dest; -} - -/* - * Resolve a name using DNS - * - */ -static int dns_resolv ( struct in_addr *addr, const char *name ) { - struct dns_query query; - struct dns_query_info *query_info; - struct dns_header *reply; - struct dns_rr_info *rr_info; - struct sockaddr_in nameserver; - uint16_t qtype; - unsigned int recursion = 0; - unsigned int id = 1; - - /* Fail immediately if we have no name server */ - if ( ! arptable[ARP_NAMESERVER].ipaddr.s_addr ) { - DBG ( "DNS has no nameserver\n" ); - return 0; - } - - DBG ( "DNS resolving %s\n", name ); - - /* Set up the query data */ - nameserver.sin_addr = arptable[ARP_NAMESERVER].ipaddr; - nameserver.sin_port = DNS_UDP_PORT; - memset ( &query, 0, sizeof ( query ) ); - query.dns.flags = htons ( DNS_FLAG_QUERY | DNS_FLAG_OPCODE_QUERY | - DNS_FLAG_RD ); - query.dns.qdcount = htons ( 1 ); - query_info = ( void * ) dns_make_name ( query.payload, name ); - query_info->qtype = htons ( DNS_TYPE_A ); - query_info->qclass = htons ( DNS_CLASS_IN ); - - while ( 1 ) { - /* Transmit current query, wait for reply */ - query.dns.id = htons ( id++ ); - qtype = ntohs ( query_info->qtype ); - reply = dns_query ( &query, - ( ( ( char * ) query_info ) - + sizeof ( *query_info ) - - ( ( char * ) &query ) ), - &nameserver ); - if ( ! reply ) { - DBG ( "DNS got no response from server %@ (port %d)\n", - nameserver.sin_addr.s_addr, - nameserver.sin_port ); - return 0; - } - - /* Search through response for useful answers. Do - * this multiple times, to take advantage of useful - * nameservers which send us e.g. the CNAME *and* the - * A record for the pointed-to name. - */ - while ( ( rr_info = dns_find_rr ( &query, reply ) ) ) { - switch ( ntohs ( rr_info->type ) ) { - case DNS_TYPE_A: { - /* Found the target A record */ - struct dns_rr_info_a *rr_info_a = - ( struct dns_rr_info_a * ) rr_info; - *addr = rr_info_a->in_addr; - DBG ( "DNS found address %@\n", addr->s_addr ); - return 1; } - case DNS_TYPE_CNAME: { - /* Found a CNAME record - update the query */ - struct dns_rr_info_cname *rr_info_cname = - ( struct dns_rr_info_cname * ) rr_info; - char *cname = rr_info_cname->cname; - - query_info = ( void * ) - dns_decompress_name ( query.payload, - cname, reply ); - DBG ( "DNS found CNAME %s\n", - dns_unmake_name ( query.payload ) ); - DBG ( "", /* Reconstruct name */ - dns_make_name ( query.payload, - query.payload + 1 ) ); - - query_info->qtype = htons ( DNS_TYPE_A ); - query_info->qclass = htons ( DNS_CLASS_IN ); - - if ( ++recursion > DNS_MAX_CNAME_RECURSION ) { - DBG ( "DNS recursion exceeded\n" ); - return 0; - } - break; } - default: - DBG ( "DNS got unknown record type %d\n", - ntohs ( rr_info->type ) ); - return 0; - } - } - - /* Determine what to do next based on the type of - * query we issued and the reponse we received - */ - switch ( qtype ) { - case DNS_TYPE_A : - /* We asked for an A record and got nothing; - * try the CNAME. - */ - DBG ( "DNS found no A record; trying CNAME\n" ); - query_info->qtype = htons ( DNS_TYPE_CNAME ); - break; - case DNS_TYPE_CNAME : - /* We asked for a CNAME record. If we didn't - * get any response (i.e. the next A query - * isn't already set up), then abort. - */ - if ( query_info->qtype != htons ( DNS_TYPE_A ) ) { - DBG ( "DNS found no CNAME record\n" ); - return 0; - } - break; - default: - DBG ( "DNS internal error - inconsistent state\n" ); - } - } -} - -struct resolver dns_resolver __resolver = { - .name = "DNS", - .resolv = dns_resolv, -}; diff --git a/src/proto/nmb.c b/src/proto/nmb.c index d2944031..e1fc911e 100644 --- a/src/proto/nmb.c +++ b/src/proto/nmb.c @@ -1,6 +1,8 @@ +#if 0 + #include "resolv.h" #include "string.h" -#include "dns.h" +#include #include "nic.h" #include "nmb.h" @@ -104,3 +106,5 @@ struct resolver nmb_resolver __resolver = { .name = "NMB", .resolv = nmb_resolv, }; + +#endif