1 /**************************************************************************
3 * dns_resolver.c: Etherboot support for resolution of host/domain
4 * names in filename parameters
5 * Written 2004 by Anselm M. Hoffmeister
6 * <stockholm@users.sourceforge.net>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 * This code is using nuts and bolts from throughout etherboot.
23 * It is a fresh implementation according to the DNS RFC, #1035
27 * 2004-05-10 File created
28 * 2004-05-19 First release to CVS
29 * 2004-05-22 CNAME support first stage finished
30 * 2004-05-24 First "stable" release to CVS
31 * 2004-08-28 Improve readability, set recursion flag
32 * 2005-04-30 Tidied up to the point of being a complete rewrite (mcb30)
33 ***************************************************************************/
35 #include "etherboot.h"
42 * Shall be called on any incoming packet during the resolution process
43 * (as is the case with all the other await_ functions in etherboot)
44 * Param: as any await functions
47 static int await_dns ( int ival, void *ptr,
48 unsigned short ptype __unused,
49 struct iphdr *ip __unused,
50 struct udphdr *udp, struct tcphdr *tcp __unused ) {
51 struct dns_header **header = ptr;
55 if ( ntohs ( udp->dest ) != ival )
57 *header = ( struct dns_header * ) ( udp + 1 );
62 * Send a name server query and wait for a response. Query is retried
63 * up to DNS_MAX_RETRIES times. Returns a pointer to the answer
64 * packet, or NULL if no answer was received.
67 struct dns_header * dns_query ( struct dns_query *query,
68 unsigned int query_len,
69 struct sockaddr_in *nameserver ) {
72 struct dns_header *reply;
74 for ( retry = 0 ; retry < DNS_MAX_RETRIES ; retry++ ) {
75 udp_transmit ( nameserver->sin_addr.s_addr,
76 nameserver->sin_port, nameserver->sin_port,
78 timeout = rfc2131_sleep_interval ( TIMEOUT, retry );
79 if ( ! await_reply ( await_dns, nameserver->sin_port,
82 if ( reply->id != query->dns.id ) {
83 DBG ( "DNS received unexpected reply ID %d "
85 ntohs ( reply->id ), ntohs ( query->dns.id ) );
88 /* We have a valid reply! */
95 * Compare two DNS names to see if they are the same. Takes
96 * compressed names in the reply into account (though the query name
97 * must be uncompressed). Returns 0 for a match (for consistency with
101 static inline int dns_name_cmp ( const char *qname, const char *rname,
102 struct dns_header *reply ) {
105 /* Obtain next section of rname */
106 while ( ( *rname ) & 0xc0 ) {
107 rname = ( ( char * ) reply +
108 ( ntohs( *((uint16_t *)rname) ) & ~0xc000 ));
110 /* Check that lengths match */
111 if ( *rname != *qname )
113 /* If length is zero, we have reached the end */
116 /* Check that data matches */
117 for ( i = *qname + 1; i > 0 ; i-- ) {
118 if ( *(rname++) != *(qname++) )
125 * Skip over a DNS name, which may be compressed
128 static inline const char * dns_skip_name ( const char *name ) {
134 if ( *name & 0xc0 ) {
135 /* Start of a compressed name */
138 /* Uncompressed name portion */
144 * Find a Resource Record in a reply packet corresponding to our
145 * query. Returns a pointer to the RR, or NULL if no answer found.
148 static struct dns_rr_info * dns_find_rr ( struct dns_query *query,
149 struct dns_header *reply ) {
151 const char *p = ( ( char * ) reply ) + sizeof ( struct dns_header );
153 /* Skip over the questions section */
154 for ( i = ntohs ( reply->qdcount ) ; i > 0 ; i-- ) {
155 p = dns_skip_name ( p ) + sizeof ( struct dns_query_info );
158 /* Process the answers section */
159 for ( i = ntohs ( reply->ancount ) ; i > 0 ; i-- ) {
160 if ( dns_name_cmp ( query->payload, p, reply ) == 0 ) {
161 return ( ( struct dns_rr_info * ) p );
163 p = dns_skip_name ( p );
164 p += ( sizeof ( struct dns_rr_info ) +
165 ntohs ( ( ( struct dns_rr_info * ) p )->rdlength ) );
172 * Convert a standard NUL-terminated string to a DNS query name,
173 * consisting of "<length>element" pairs.
175 * Returns a pointer to the character following the constructed DNS
179 static inline char * dns_make_name ( char *dest, const char *name ) {
180 char *length_byte = dest++;
183 while ( ( c = *(name++) ) ) {
185 *length_byte = dest - length_byte - 1;
190 *length_byte = dest - length_byte - 1;
196 * Decompress a DNS name.
198 * Returns a pointer to the character following the decompressed DNS
202 static inline char * dns_decompress_name ( char *dest, const char *name,
203 struct dns_header *header ) {
207 /* Obtain next section of name */
208 while ( ( *name ) & 0xc0 ) {
209 name = ( ( char * ) header +
210 ( ntohs ( *((uint16_t *)name) ) & ~0xc000 ) );
214 for ( i = len + 1 ; i > 0 ; i-- ) {
215 *(dest++) = *(name++);
222 * Resolve a name using DNS
225 static int dns_resolv ( struct in_addr *addr, const char *name ) {
226 struct dns_query query;
227 struct dns_query_info *query_info;
228 struct dns_header *reply;
229 struct dns_rr_info *rr_info;
230 struct sockaddr_in nameserver;
232 unsigned int recursion = 0;
235 DBG ( "DNS resolving %s\n", name );
237 /* Set up the query data */
238 nameserver.sin_addr = arptable[ARP_NAMESERVER].ipaddr;
239 nameserver.sin_port = DNS_UDP_PORT;
240 memset ( &query, 0, sizeof ( query ) );
241 query.dns.flags = htons ( DNS_FLAG_QUERY | DNS_FLAG_OPCODE_QUERY |
243 query.dns.qdcount = htons ( 1 );
244 query_info = ( void * ) dns_make_name ( query.payload, name );
245 query_info->qtype = htons ( DNS_TYPE_A );
246 query_info->qclass = htons ( DNS_CLASS_IN );
249 /* Transmit current query, wait for reply */
250 query.dns.id = htons ( id++ );
251 qtype = ntohs ( query_info->qtype );
252 reply = dns_query ( &query,
253 ( ( ( char * ) query_info )
254 + sizeof ( *query_info )
255 - ( ( char * ) &query ) ),
258 DBG ( "DNS got no response from server %@ (port %d)\n",
259 nameserver.sin_addr.s_addr,
260 nameserver.sin_port );
264 /* Search through response for useful answers. Do
265 * this multiple times, to take advantage of useful
266 * nameservers which send us e.g. the CNAME *and* the
267 * A record for the pointed-to name.
269 while ( ( rr_info = dns_find_rr ( &query, reply ) ) ) {
270 switch ( ntohs ( rr_info->type ) ) {
272 /* Found the target A record */
273 struct dns_rr_info_a *rr_info_a =
274 ( struct dns_rr_info_a * ) rr_info;
275 *addr = rr_info_a->in_addr;
276 DBG ( "DNS found address %@\n", addr->s_addr );
278 case DNS_TYPE_CNAME: {
279 /* Found a CNAME record - update the query */
280 struct dns_rr_info_cname *rr_info_cname =
281 ( struct dns_rr_info_cname * ) rr_info;
282 char *cname = rr_info_cname->cname;
284 DBG ( "DNS found CNAME\n" );
285 query_info = ( void * )
286 dns_decompress_name ( query.payload,
288 query_info->qtype = htons ( DNS_TYPE_A );
289 query_info->qclass = htons ( DNS_CLASS_IN );
291 if ( ++recursion > DNS_MAX_CNAME_RECURSION ) {
292 DBG ( "DNS recursion exceeded\n" );
297 DBG ( "DNS got unknown record type %d\n",
298 ntohs ( rr_info->type ) );
303 /* Determine what to do next based on the type of
304 * query we issued and the reponse we received
308 /* We asked for an A record and got nothing;
311 DBG ( "DNS found no A record; trying CNAME\n" );
312 query_info->qtype = htons ( DNS_TYPE_CNAME );
314 case DNS_TYPE_CNAME :
315 /* We asked for a CNAME record. If we didn't
316 * get any response (i.e. the next A query
317 * isn't already set up), then abort.
319 if ( query_info->qtype != htons ( DNS_TYPE_A ) ) {
320 DBG ( "DNS found no CNAME record\n" );
325 DBG ( "DNS internal error - inconsistent state\n" );
330 static struct resolver dns_resolver __resolver = {
332 .resolv = dns_resolv,