Browse Source

DNS resolver rewritten, moved to proto/dns.c

tags/v0.9.3
Michael Brown 19 years ago
parent
commit
78cdb1da8b
4 changed files with 414 additions and 477 deletions
  1. 0
    419
      src/core/dns_resolver.c
  2. 81
    0
      src/include/dns.h
  3. 0
    58
      src/include/dns_resolver.h
  4. 333
    0
      src/proto/dns.c

+ 0
- 419
src/core/dns_resolver.c View File

@@ -1,419 +0,0 @@
1
-/**************************************************************************
2
-*
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>
7
-*
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.
12
-*
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.
17
-*
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.
21
-*
22
-*    This code is using nuts and bolts from throughout etherboot.
23
-*    It is a fresh implementation according to the DNS RFC, #1035
24
-*    
25
-*    $Revision$
26
-*    $Author$
27
-*    $Date$
28
-*
29
-*    REVISION HISTORY:
30
-*    ================
31
-*    2004-05-10 File created
32
-*    2004-05-19 First release to CVS
33
-*    2004-05-22 CNAME support first stage finished
34
-*    2004-05-24 First "stable" release to CVS
35
-*    2004-08-28 Improve readability, set recursion flag
36
-***************************************************************************/
37
-
38
-#ifdef DNS_RESOLVER
39
-#include "etherboot.h"
40
-#include "nic.h"
41
-#include "dns_resolver.h"
42
-
43
-#define	MAX_DNS_RETRIES	3
44
-#define	MAX_CNAME_RECURSION 0x30
45
-#undef DNSDEBUG
46
-
47
-int	donameresolution ( char * hostname, int hnlength, char * deststring );
48
-
49
-/*
50
- *	dns_resolver
51
- *	Function: Main function for name resolution - will be called by other
52
- *		parts of etherboot
53
- *	Param:	string filename (not containing proto prefix like "tftp://")
54
- *	Return:	string filename, with hostname replaced by IP in dotted quad
55
- *		or NULL for resolver error
56
- *		In case no substitution is necessary, return will be = param
57
- *		The returned string, if not == input, will be temporary and
58
- *		probably be overwritten during the next call to dns_resolver
59
- *		The returned string may (or may not) only contain an IP
60
- *		address, or an IP followed by a ":" or "/", or be NULL(=error)
61
- */
62
-char *	dns_resolver ( char * filename ) {
63
-	int	i = 0, j, k;
64
-	static char ipaddr[16] = { 0 };
65
-	// Search for "end of hostname" (which might be either ":" or "/")
66
-	for ( j = i; (filename[j] != ':') && (filename[j] != '/'); ++j ) {
67
-		// If no hostname delimiter was found, assume no name present
68
-		if ( filename[j] == 0 )	return	filename;
69
-	}
70
-	// Check if the filename is an IP, in which case, leave unchanged
71
-	k = j - i - 1;
72
-	while ( ( '.' == filename[i+k] ) ||
73
-		( ( '0' <= filename[i+k] ) && ( '9' >= filename[i+k] ) ) ) {
74
-		--k;
75
-		if ( k < 0 ) return filename; // Only had nums and dots->IP
76
-	}
77
-	// Now that we know it's a full hostname, attempt to resolve
78
-	if ( donameresolution ( filename + i, j - i, ipaddr ) ) {
79
-		return	NULL;	// Error in resolving - Fatal.
80
-	}
81
-	// Return the dotted-quad IP which resulted
82
-	return	ipaddr;
83
-}
84
-
85
-/*
86
- *	await_dns
87
- *	Shall be called on any incoming packet during the resolution process
88
- *	(as is the case with all the other await_ functions in etherboot)
89
- *	Param:	as any await functions
90
- *	Return:	see dns_resolver.h for constant return values + descriptions
91
- */
92
-static int await_dns (int ival, void *ptr,
93
-	unsigned short ptype __unused, struct iphdr *ip __unused,
94
-	struct udphdr *udp, struct tcphdr *tcp __unused) {
95
-	int	i, j, k;
96
-	unsigned char *p = (unsigned char *)udp + sizeof(struct udphdr);
97
-	// p is set to the beginning of the payload
98
-	unsigned char *q;
99
-	unsigned int querytype = QUERYTYPE_A;
100
-	if (  0 == udp  )	// Parser couldn't find UDP header
101
-		return RET_PACK_GARBAG;	// Not a UDP packet
102
-	if (( UDP_PORT_DNS != ntohs (udp->src )) ||
103
-	    ( UDP_PORT_DNS != ntohs (udp->dest)) )
104
-		// Neither source nor destination port is "53"
105
-		return RET_PACK_GARBAG;	// UDP port wrong
106
-	if (( p[QINDEX_ID  ] != 0) ||
107
-	    ( p[QINDEX_ID+1] != (ival & 0xff)))
108
-		// Checking if this packet has set (inside payload)
109
-		// the sequence identifier that we expect
110
-		return RET_PACK_GARBAG;	// Not matching our request ID
111
-	if (( p[QINDEX_FLAGS  ] & QUERYFLAGS_MASK ) != QUERYFLAGS_WANT )
112
-		// We only accept responses to the query(ies) we sent
113
-		return	RET_PACK_GARBAG;	// Is not response=opcode <0>
114
-	querytype = (ival & 0xff00) >> 8;
115
-	if (((p[QINDEX_NUMANSW+1] + (p[QINDEX_NUMANSW+1]<<8)) == 0 ) ||
116
-	     ( ERR_NOSUCHNAME == (p[QINDEX_FLAGS+1] & 0x0f) ) ) {
117
-		// Answer section has 0 entries, or "no such name" returned
118
-		if ( QUERYTYPE_A == querytype) {
119
-			// It was an A type query, so we should try if there's
120
-			// an alternative "CNAME" record available
121
-			return	RET_RUN_CNAME_Q; // So try CNAME query next
122
-		} else if ( QUERYTYPE_CNAME == querytype) {
123
-			// There's no CNAME either, so give up
124
-			return	RET_NOSUCHNAME;
125
-		} else {
126
-			// Anything else? No idea what. Should not happen.
127
-			return	RET_NOSUCHNAME; // Bail out with error
128
-		}
129
-	}
130
-	if ( 0 != ( p[QINDEX_FLAGS+1] & 0x0f ) )
131
-		// The response packet's flag section tells us:
132
-		return	RET_NOSUCHNAME;	// Another (unspecific) error occured
133
-	// Now we have an answer packet in response to our query. Next thing
134
-	// to do is to search the payload for the "answer section", as there is
135
-	// a question section first usually that needs to be skipped
136
-	// If question section was not repeated, that saves a lot of work :
137
-	if ( 0 >= (i = ((p[QINDEX_NUMQUEST] << 8) + p[QINDEX_NUMQUEST+1]))) {
138
-		q = p+ QINDEX_QUESTION;	// No question section, just continue;
139
-	} else if ( i >= 2 ) {		// More than one query section? Error!
140
-		return	RET_NOSUCHNAME;	// That's invalid for us anyway - 
141
-					// We only place one query at a time
142
-	} else {
143
-		// We have to skip through the question section first to
144
-		// find the beginning of the answer section
145
-		q = p + QINDEX_QUESTION;
146
-		while ( 0 != q[0] )
147
-			q += q[0] + 1; // Skip through
148
-		q += 5; // Skip over end-\0 and query type section
149
-	}
150
-	// Now our pointer shows the beginning of the answer section
151
-	// So now move it past the (repeated) query string, we only
152
-	// want the answer
153
-	while ( 0 != q[0] ) {
154
-		if ( 0xc0 == ( q[0] & 0xc0 ) ) { // Pointer
155
-			++q;
156
-			break;
157
-		}
158
-		q += q[0] + 1;
159
-	}
160
-	++q;
161
-	// Now check wether it's an INET host address (resp. CNAME)?
162
-	// There seem to be nameservers out there (Bind 9, for example),
163
-	// that return CNAMEs when no As are there, e.g. in
164
-	//   testname.test.   IN CNAME othername.test.
165
-	// case, bind 9 would return the CNAME record on an A query.
166
-	// Accept this case as it saves a lot of work (an extra query)
167
-	if (( QUERYTYPE_CNAME == q[1] ) &&
168
-	    ( QUERYTYPE_A     == querytype )) {
169
-		// A query, but CNAME response.
170
-		// Do simulate having done a CNAME query now and just assume
171
-		// the answer we are working on here is that of a CNAME query
172
-		// This works for single-depth CNAMEs fine.
173
-		// For deeper recursion, we won't parse the answer
174
-		// packet deeper but rely on a separate CNAME query
175
-		querytype = QUERYTYPE_CNAME;
176
-	}
177
-	// Now check wether the answer packet is of the expected type
178
-	// (remember, we just tweaked CNAME answers to A queries already)
179
-	if ( (q[0] != 0) ||
180
-	     (q[1] !=   querytype) ||   // query type matches?
181
-	     (q[2] != ((QUERYCLASS_INET & 0xff00) >> 8)) ||
182
-	     (q[3] !=  (QUERYCLASS_INET & 0x00ff))) { // class IN response?
183
-		return	RET_DNSERROR;	// Should not happen. DNS server bad?
184
-	}
185
-	q += 8;	// skip querytype/-class/ttl which are uninteresting now
186
-	if ( querytype == QUERYTYPE_A ) {
187
-		// So what we sent was an A query - expect an IPv4 address
188
-		// Check if datalength looks satisfactory
189
-		if ( ( 0 != q[0] ) || ( 4 != q[1] ) ) {
190
-			// Data length is not 4 bytes
191
-			return	RET_DNSERROR;
192
-		}
193
-		// Go to the IP address and copy it to the response buffer
194
-		p = ptr + QINDEX_STORE_A;
195
-		for ( i = 0; i < 4; ++i ) {
196
-			p[i] = q[i+2];
197
-		}
198
-		return	RET_GOT_ADDR; // OK! Address RETURNED! VERY FINE!
199
-	} else if ( querytype == QUERYTYPE_CNAME ) { // CNAME QUERY
200
-		// The pointer "q" now stands on the "payload length" of the
201
-		// CNAME data [2 byte field]. We will have to check wether this
202
-		// name is referenced in a following response, which would save
203
-		// us further queries, or if it is standalone, which calls for
204
-		// making a separate query
205
-		// This statement above probably needs clarification. To help
206
-		// the reader understand what's going on, imagine the DNS
207
-		// answer to be 4-section: query(repeating what we sent to the
208
-		// DNS server), answer(like the CNAME record we wanted),
209
-		// additional (which might hold the A for the CNAME - esp.
210
-		// Bind9 seems to provide us with this info when we didn't
211
-		// query for it yet) and authoritative (the DNS server's info
212
-		// on who is responsible for that data). For compression's
213
-		// sake, instead of specifying the full hostname string all
214
-		// the time, one can instead specify something like "same as on
215
-		// payload offset 0x123" - if this happens here, we can check
216
-		// if that payload offset given here by coincidence is that of
217
-		// an additional "A" record which saves us sending a separate
218
-		// query.
219
-		// We will look if there's  a/ two or more answer sections
220
-		// AND  b/ the next is a reference to us.
221
-		p = (unsigned char *)udp + sizeof(struct udphdr);
222
-		i = q + 2 - p;
223
-		if ( p[7] > 1 ) { // More than one answer section
224
-			// Check the next if it's a ref
225
-			// For that, we have to locate the next. Do this with "q".
226
-			p = q + (q[0] * 0x100) + q[1] + 2;
227
-			if ( (p[0] == (0xc0 + ((i & 0xf00)/256))) &&
228
-			     (p[1] == (i & 0xff) ) &&
229
-			     (p[2] == ((QUERYTYPE_A     & 0xff00) >> 8)) &&
230
-			     (p[3] ==  (QUERYTYPE_A     & 0x00ff)) &&
231
-			     (p[4] == ((QUERYCLASS_INET & 0xff00) >> 8 )) &&
232
-			     (p[5] ==  (QUERYCLASS_INET & 0x00ff)) &&
233
-			     (p[10]== 0) &&		// Data length expected
234
-			     (p[11]== 4)) {		// to be <4> (IP-addr)
235
-				// Behind that sections: TTL, data length(4)
236
-				for ( i = 0; i < 4; ++i ) {
237
-				    ((unsigned char*)ptr)[QINDEX_STORE_A+i] =
238
-					    p[12+i];
239
-				}
240
-				return	RET_GOT_ADDR;
241
-			}
242
-		}
243
-		// Reference not found, next query (A) needed (because CNAME
244
-		// queries usually return another hostname, not an IP address
245
-		// as we need it - that's the point in CNAME, anyway :-)
246
-		p = (unsigned char *)udp + sizeof(struct udphdr);
247
-#ifdef DNSDEBUG
248
-		printf ( " ->[");
249
-#endif
250
-		k = QINDEX_QUESTION;
251
-		i = (q-p) + 2;
252
-		// Compose the hostname that needs to be queried for
253
-		// This looks complicated (and is a little bit) because
254
-		// we might not be able to copy verbatim, but need to
255
-		// check wether the CNAME looks like
256
-		// "servername" "plus what offset 0x123 of the payload says"
257
-		// (this saves transfer bandwidth, which is why DNS allows
258
-		// this, but it makes programmers' lives more difficult)
259
-		while (p[i] != 0) {
260
-			if ( (((unsigned char *)p)[i] & 0xc0) != 0 ) {
261
-				i = ((p[i] & 0x3f) * 0x100) + p[i+1];
262
-				continue;
263
-			}
264
-			((unsigned char *)ptr)[k] = p[i];
265
-			for ( j = i + 1; j <= i + p[i]; ++j ) {
266
-				((unsigned char *)ptr)[k+j-i] = p[j];
267
-#ifdef DNSDEBUG
268
-				printf ( "%c", p[j] );
269
-#endif
270
-			}
271
-			k += ((unsigned char *)ptr)[k] + 1;
272
-			i += p[i] + 1;
273
-#ifdef DNSDEBUG
274
-			printf ( (p[i] ? ".": "") );
275
-#endif
276
-		}
277
-		((unsigned char *)ptr)[k] = 0;
278
-#ifdef DNSDEBUG
279
-		printf ( "].." );
280
-#endif
281
-		// So we need to run another query, this time try to
282
-		// get an "A" record for the hostname that the last CNAME
283
-		// query pointed to
284
-		return	RET_RUN_NEXT_A;
285
-	}
286
-	// We only accept CNAME or A replies from the nameserver, every-
287
-	// thing else is of no use for us.
288
-	// Must be an invalid packet that the nameserver sent.
289
-	return	RET_DNSERROR;
290
-}
291
-
292
-int	chars_to_next_dot ( char * countfrom, int maxnum ) {
293
-	// Count the number of characters of this part of a hostname
294
-	int i;
295
-	for ( i = 1; i < maxnum; ++i ) {
296
-		if ( countfrom[i] == '.' ) return i;
297
-	}
298
-	return	maxnum;
299
-}
300
-
301
-/*
302
- *	donameresolution
303
- *	Function: Compose the initial query packet, handle answers until
304
- *		a/ an IP address is retrieved
305
- *		b/ too many CNAME references occured (max. MAX_DNS_RETRIES)
306
- *		c/ No matching record for A or CNAME can be found
307
- *	Param:	string hostname, length (hostname needs no \0-end-marker),
308
- *		string to which dotted-quad-IP shall be written
309
- *	Return:	0 for success, >0 for failure
310
- */
311
-int	donameresolution ( char * hostname, int hnlength, char * deststring ) {
312
-	unsigned char	querybuf[260+sizeof(struct iphdr)+sizeof(struct udphdr)];
313
-		// 256 for the DNS query payload, +4 for the result temporary
314
-	unsigned char	*query = &querybuf[sizeof(struct iphdr)+sizeof(struct udphdr)];
315
-		// Pointer to the payload
316
-	int	i, h = hnlength;
317
-	long	timeout;
318
-	int	retry, recursion;
319
-	// Setup the query data
320
-	query[QINDEX_ID  ]	= (QUERYIDENTIFIER & 0xff00) >> 8;
321
-	query[QINDEX_ID+1]	=  QUERYIDENTIFIER & 0xff;
322
-	query[QINDEX_FLAGS  ]	= (QUERYFLAGS & 0xff00) >> 8;
323
-	query[QINDEX_FLAGS+1]	=  QUERYFLAGS & 0xff;
324
-	query[QINDEX_NUMQUEST  ]= 0;
325
-	query[QINDEX_NUMQUEST+1]= 1; // 1 standard query to be sent
326
-	query[QINDEX_NUMANSW  ]	= 0;
327
-	query[QINDEX_NUMANSW+1]	= 0;
328
-	query[QINDEX_NUMAUTH  ]	= 0;
329
-	query[QINDEX_NUMAUTH+1]	= 0;
330
-	query[QINDEX_NUMADDIT  ]= 0;
331
-	query[QINDEX_NUMADDIT+1]= 0;
332
-	query[QINDEX_QUESTION]	= chars_to_next_dot(hostname,h);
333
-	if ( h > 236 ) return 1;	// Hostnames longer than 236 chars are refused.
334
-					// This is an arbitrary decision, SHOULD check
335
-					// what the RFCs say about that
336
-	for ( i = 0; i < h; ++i ) {
337
-		// Compose the query section's hostname - replacing dots (and
338
-		// preceding the string) with one-byte substring-length values
339
-		// (for the immediately following substring) - \0 terminated
340
-		query[QINDEX_QUESTION+i+1] = hostname[i];
341
-		if ( hostname[i] == '.' )
342
-			query[QINDEX_QUESTION+i+1] = chars_to_next_dot(hostname + i + 1, h - i - 1);
343
-	}
344
-	query[QINDEX_QTYPE+h  ]	= (QUERYTYPE_A & 0xff00) >> 8;
345
-	query[QINDEX_QTYPE+h+1]	=  QUERYTYPE_A & 0xff;
346
-					// First try an A query, if that
347
-					// won't work, try CNAME
348
-	printf ( "Resolving hostname [" );
349
-	for ( i = 0; i < hnlength; ++i ) { printf ( "%c", hostname[i] ); }
350
-	printf ("]" );
351
-	for ( recursion = MAX_CNAME_RECURSION; recursion > 0; --recursion ) {
352
-		printf ( ".." );
353
-		// Try MAX_CNAME_RECURSION CNAME queries maximally, if then no
354
-		// A record is found, assume the hostname to not be resolvable
355
-		query[QINDEX_QUESTION+h+1] = 0;	// Marks the end of
356
-						// the query string
357
-		query[QINDEX_QCLASS+h  ]= (QUERYCLASS_INET & 0xff00) >> 8;
358
-		query[QINDEX_QCLASS+h+1]=  QUERYCLASS_INET & 0xff;
359
-		rx_qdrain();	// Clear NIC packet buffer -
360
-				// there won't be anything of interest *now*.
361
-		udp_transmit ( arptable[ARP_NAMESERVER].ipaddr.s_addr,
362
-				UDP_PORT_DNS, UDP_PORT_DNS,
363
-				hnlength + 18 + sizeof(struct iphdr) +
364
-				sizeof(struct udphdr), querybuf );
365
-		// If no answer comes in in a certain period of time, retry
366
-		for (retry = 1; retry <= MAX_DNS_RETRIES; retry++) {
367
-			timeout = rfc2131_sleep_interval(TIMEOUT, retry);
368
-			i = await_reply ( await_dns,
369
-				(query[QINDEX_QTYPE+h+1] << 8) +
370
-				((unsigned char *)query)[QINDEX_ID+1],
371
-				query, timeout);
372
-			// The parameters given are
373
-			//	bits 15...8 of integer = Query type  (low bits)
374
-			//	bits  7...0 of integer = Query index (low bits)
375
-			//	query + QINDEX_STORE_A points to the place
376
-			//	where A records	shall be stored (4 bytes);
377
-			//	query + 12(QINDEX_QUESTION) points to where
378
-			//	a CNAME should go in case one is received
379
-			if (i) break;
380
-		}
381
-		switch ( i ) {
382
-		  case	RET_GOT_ADDR:	// Address successfully retrieved
383
-			sprintf( deststring, "%@",
384
-				  (long)query[QINDEX_STORE_A  ]           +
385
-				( (long)query[QINDEX_STORE_A+1] * 256 ) +
386
-				( (long)query[QINDEX_STORE_A+2] * 65536 )+
387
-				( (long)query[QINDEX_STORE_A+3] * 16777216 ));
388
-			printf ( " -> IP [%s]\n", deststring );
389
-			return	RET_DNS_OK;
390
-		  case	RET_RUN_CNAME_Q: // No A record found, try CNAME
391
-			query[QINDEX_QTYPE+h  ]=(QUERYTYPE_CNAME & 0xff00)>> 8;
392
-			query[QINDEX_QTYPE+h+1]= QUERYTYPE_CNAME & 0xff;
393
-			break;
394
-		  case	RET_RUN_NEXT_A:
395
-			// Found a CNAME, now try A for the name it pointed to
396
-			for ( i = 0; query[QINDEX_QUESTION+i] != 0;
397
-					i += query[QINDEX_QUESTION+i] + 1 ) {;}
398
-			h = i - 1;
399
-			query[QINDEX_QTYPE+h  ]=(QUERYTYPE_A & 0xff00)>> 8;
400
-			query[QINDEX_QTYPE+h+1]= QUERYTYPE_A & 0xff;
401
-			break;
402
-		  case	RET_CNAME_FAIL:
403
-			// Neither A nor CNAME gave a usable result
404
-			printf ("Host name cannot be resolved\n");
405
-			return	RET_DNS_FAIL;
406
-		  case	RET_NOSUCHNAME:
407
-		  default:
408
-			printf ( "Name resolution failed\n" );
409
-			return	RET_DNS_FAIL;
410
-		}
411
-		query[QINDEX_ID  ] = 0;	// We will probably never have more
412
-					// than 256 queries in one run
413
-		query[QINDEX_ID+1]++;
414
-	}
415
-	// To deep recursion
416
-	printf ( "CNAME recursion to deep - abort name resolver\n" );
417
-	return	RET_DNS_FAIL;
418
-}
419
-#endif				/* DNS_RESOLVER */

+ 81
- 0
src/include/dns.h View File

@@ -0,0 +1,81 @@
1
+#ifndef DNS_RESOLVER_H
2
+#define DNS_RESOLVER_H
3
+
4
+#include "stdint.h"
5
+#include "nic.h"
6
+#include "in.h"
7
+
8
+/*
9
+ * Constants
10
+ *
11
+ */
12
+
13
+#define DNS_TYPE_A		1
14
+#define DNS_TYPE_CNAME		5
15
+#define DNS_TYPE_ANY		255
16
+
17
+#define DNS_CLASS_IN		1
18
+#define DNS_CLASS_CS		2
19
+#define DNS_CLASS_CH		3
20
+#define DNS_CLASS_HS		4
21
+
22
+#define DNS_FLAG_QUERY		( 0x00 << 15 )
23
+#define DNS_FLAG_RESPONSE	( 0x01 << 15 )
24
+#define DNS_FLAG_QR(flags)	( (flags) & ( 0x01 << 15 ) )
25
+#define DNS_FLAG_OPCODE_QUERY	( 0x00 << 11 )
26
+#define DNS_FLAG_OPCODE_IQUERY	( 0x01 << 11 )
27
+#define DNS_FLAG_OPCODE_STATUS	( 0x02 << 11 )
28
+#define DNS_FLAG_OPCODE(flags)	( (flags) & ( 0x0f << 11 ) )
29
+#define DNS_FLAG_RD		( 0x01 << 8 )
30
+#define DNS_FLAG_RA		( 0x01 << 7 )
31
+#define DNS_FLAG_RCODE_OK	( 0x00 << 0 )
32
+#define DNS_FLAG_RCODE_NX	( 0x03 << 0 )
33
+#define DNS_FLAG_RCODE(flags)	( (flags) & ( 0x0f << 0 ) )
34
+
35
+#define	DNS_UDP_PORT		53
36
+#define	DNS_MAX_RETRIES		3
37
+#define	DNS_MAX_CNAME_RECURSION	0x30
38
+
39
+/*
40
+ * DNS protocol structures
41
+ *
42
+ */
43
+struct dns_header {
44
+	uint16_t	id;
45
+	uint16_t	flags;
46
+	uint16_t	qdcount;
47
+	uint16_t	ancount;
48
+	uint16_t	nscount;
49
+	uint16_t	arcount;
50
+} __attribute__ (( packed ));
51
+
52
+struct dns_query_info {
53
+	uint16_t	qtype;
54
+	uint16_t	qclass;
55
+} __attribute__ (( packed ));
56
+
57
+struct dns_query {
58
+	struct iphdr	ip;
59
+	struct udphdr	udp;
60
+	struct dns_header dns;
61
+	char		payload[ 256 + sizeof ( struct dns_query_info ) ];
62
+} __attribute__ (( packed ));
63
+
64
+struct dns_rr_info {
65
+	uint16_t	type;
66
+	uint16_t	class;
67
+	uint16_t	ttl;
68
+	uint16_t	rdlength;
69
+} __attribute__ (( packed ));
70
+
71
+struct dns_rr_info_a {
72
+	struct dns_rr_info;
73
+	struct in_addr in_addr;
74
+} __attribute__ (( packed ));
75
+
76
+struct dns_rr_info_cname {
77
+	struct dns_rr_info;
78
+	char		cname[0];
79
+};
80
+
81
+#endif /* DNS_RESOLVER_H */

+ 0
- 58
src/include/dns_resolver.h View File

@@ -1,58 +0,0 @@
1
-// dns_resolver.h - #define statements for the DNS resolver
2
- 
3
-// We only need A and CNAME queries (later possibly AAAA/A6?)
4
-#define	QUERYTYPE_A	1
5
-#define	QUERYTYPE_CNAME	5
6
-
7
-// We only query with INTERNET class (not CHAOS or whatever)
8
-#define	QUERYCLASS_INET	1
9
-
10
-// Our first query will have the identifier <1> (arbitrary -
11
-// remember however that (256 - QUERYIDENTIFIER)/2 > MAX_CNAME_RECURSION !!!
12
-#define	QUERYIDENTIFIER	1
13
-
14
-// Query flags are standard values here
15
-#define	QUERYFLAGS	0x0100
16
-#define	QUERYFLAGS_MASK	0xf8
17
-#define	QUERYFLAGS_WANT	0x80
18
-
19
-// Indices inside the byte array that holds DNS queries/answers
20
-#define	QINDEX_ID	0
21
-#define	QINDEX_FLAGS	2
22
-#define	QINDEX_NUMQUEST	4
23
-#define	QINDEX_NUMANSW	6
24
-#define	QINDEX_NUMAUTH	8
25
-#define	QINDEX_NUMADDIT	10
26
-#define	QINDEX_QUESTION	12
27
-#define	QINDEX_QTYPE	14
28
-#define	QINDEX_QCLASS	16
29
-#define	QINDEX_STORE_A	256
30
-
31
-// Constant UDP port number for DNS traffic
32
-#define	UDP_PORT_DNS	53
33
-
34
-// Return values that the package parser may give
35
-//	This packet was not for us (broadcast or whatever)
36
-#define	RET_PACK_GARBAG	0
37
-//	Retrieved an address - query finishes
38
-#define	RET_GOT_ADDR	1
39
-//	No A record for that hostname - try running a CNAME query
40
-#define	RET_RUN_CNAME_Q	2
41
-//	The CNAME query returned a valid hostname - run A query on that
42
-#define	RET_RUN_NEXT_A	3
43
-//	The CNAME query failed - stop resolving
44
-#define	RET_CNAME_FAIL	4
45
-//	We have a reliable input that claims that the hostname does not exist
46
-#define	RET_NOSUCHNAME	5
47
-//	The name server response is somehow bogus/can not be parsed -> Abort
48
-#define	RET_DNSERROR	6
49
-
50
-// Return values that the query engine may give
51
-//	DNS query succeeded, IP address delivered
52
-#define	RET_DNS_OK	0
53
-//	DNS query failed
54
-#define	RET_DNS_FAIL	1
55
-
56
-// Error codes the DNS server can send to us
57
-#define	ERR_NOSUCHNAME	3
58
-

+ 333
- 0
src/proto/dns.c View File

@@ -0,0 +1,333 @@
1
+/**************************************************************************
2
+*
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>
7
+*
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.
12
+*
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.
17
+*
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.
21
+*
22
+*    This code is using nuts and bolts from throughout etherboot.
23
+*    It is a fresh implementation according to the DNS RFC, #1035
24
+*    
25
+*    REVISION HISTORY:
26
+*    ================
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
+***************************************************************************/
34
+
35
+#include "etherboot.h"
36
+#include "nic.h"
37
+#include "resolv.h"
38
+#include "dns.h"
39
+
40
+/*
41
+ *	await_dns
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
45
+ *
46
+ */
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;
52
+
53
+	if ( ! udp )
54
+		return 0;
55
+	if ( ntohs ( udp->dest ) != ival )
56
+		return 0;
57
+	*header = ( struct dns_header * ) ( udp + 1 );
58
+	return 1;
59
+}
60
+
61
+/*
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.
65
+ *
66
+ */
67
+struct dns_header * dns_query ( struct dns_query *query,
68
+				unsigned int query_len, 
69
+				struct sockaddr_in *nameserver ) {
70
+	long timeout;
71
+	int retry;
72
+	struct dns_header *reply;
73
+
74
+	for ( retry = 0 ; retry < DNS_MAX_RETRIES ; retry++ ) {
75
+		udp_transmit ( nameserver->sin_addr.s_addr,
76
+			       nameserver->sin_port, nameserver->sin_port,
77
+			       query_len, query );
78
+		timeout = rfc2131_sleep_interval ( TIMEOUT, retry );
79
+		if ( ! await_reply ( await_dns, nameserver->sin_port,
80
+				     &reply, timeout ) )
81
+			continue;
82
+		if ( reply->id != query->dns.id ) {
83
+			DBG ( "DNS received unexpected reply ID %d "
84
+			      "(wanted %d)\n",
85
+			      ntohs ( reply->id ), ntohs ( query->dns.id ) );
86
+			continue;
87
+		}
88
+		/* We have a valid reply! */
89
+		return reply;
90
+	}
91
+	return NULL;
92
+}
93
+
94
+/*
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
98
+ * strcmp et al).
99
+ *
100
+ */
101
+static inline int dns_name_cmp ( const char *qname, const char *rname,
102
+				 struct dns_header *reply ) {
103
+	int i;
104
+	while ( 1 ) {
105
+		/* Obtain next section of rname */
106
+		while ( ( *rname ) & 0xc0 ) {
107
+			rname = ( ( char * ) reply +
108
+				  ( ntohs( *((uint16_t *)rname) ) & ~0xc000 ));
109
+		}
110
+		/* Check that lengths match */
111
+		if ( *rname != *qname )
112
+			return 1;
113
+		/* If length is zero, we have reached the end */
114
+		if ( ! *qname )
115
+			return 0;
116
+		/* Check that data matches */
117
+		for ( i = *qname + 1; i > 0 ; i-- ) {
118
+			if ( *(rname++) != *(qname++) )
119
+				return 1;
120
+		}
121
+	}
122
+}
123
+
124
+/*
125
+ * Skip over a DNS name, which may be compressed
126
+ *
127
+ */
128
+static inline const char * dns_skip_name ( const char *name ) {
129
+	while ( 1 ) {
130
+		if ( ! *name ) {
131
+			/* End of name */
132
+			return ( name + 1);
133
+		}
134
+		if ( *name & 0xc0 ) {
135
+			/* Start of a compressed name */
136
+			return ( name + 2 );
137
+		}
138
+		/* Uncompressed name portion */
139
+		name += *name + 1;
140
+	}
141
+}
142
+
143
+/*
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.
146
+ *
147
+ */
148
+static struct dns_rr_info * dns_find_rr ( struct dns_query *query,
149
+					  struct dns_header *reply ) {
150
+	int i;
151
+	const char *p = ( ( char * ) reply ) + sizeof ( struct dns_header );
152
+
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 );
156
+	}
157
+
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 );
162
+		}
163
+		p = dns_skip_name ( p );
164
+		p += ( sizeof ( struct dns_rr_info ) +
165
+		       ntohs ( ( ( struct dns_rr_info * ) p )->rdlength ) );
166
+	}
167
+
168
+	return NULL;
169
+}
170
+
171
+/*
172
+ * Convert a standard NUL-terminated string to a DNS query name,
173
+ * consisting of "<length>element" pairs.
174
+ *
175
+ * Returns a pointer to the character following the constructed DNS
176
+ * query name.
177
+ *
178
+ */
179
+static inline char * dns_make_name ( char *dest, const char *name ) {
180
+	char *length_byte = dest++;
181
+	char c;
182
+
183
+	while ( ( c = *(name++) ) ) {
184
+		if ( c == '.' ) {
185
+			*length_byte = dest - length_byte - 1;
186
+			length_byte = dest;
187
+		}
188
+		*(dest++) = c;
189
+	}
190
+	*length_byte = dest - length_byte - 1;
191
+	*(dest++) = '\0';
192
+	return dest;
193
+}
194
+
195
+/*
196
+ * Decompress a DNS name.
197
+ *
198
+ * Returns a pointer to the character following the decompressed DNS
199
+ * name.
200
+ *
201
+ */
202
+static inline char * dns_decompress_name ( char *dest, const char *name,
203
+					   struct dns_header *header ) {
204
+	int i, len;
205
+
206
+	do {
207
+		/* Obtain next section of name */
208
+		while ( ( *name ) & 0xc0 ) {
209
+			name = ( ( char * ) header +
210
+				 ( ntohs ( *((uint16_t *)name) ) & ~0xc000 ) );
211
+		}
212
+		/* Copy data */
213
+		len = *name;
214
+		for ( i = len + 1 ; i > 0 ; i-- ) {
215
+			*(dest++) = *(name++);
216
+		}
217
+	} while ( len );
218
+	return dest;
219
+}
220
+
221
+/*
222
+ * Resolve a name using DNS
223
+ *
224
+ */
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;
231
+	uint16_t qtype;
232
+	unsigned int recursion = 0;
233
+	unsigned int id = 1;
234
+
235
+	DBG ( "DNS resolving %s\n", name );
236
+
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 |
242
+				  DNS_FLAG_RD );
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 );
247
+
248
+	while ( 1 ) {
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 ) ),
256
+				    &nameserver );
257
+		if ( ! reply ) {
258
+			DBG ( "DNS got no response from server %@ (port %d)\n",
259
+			      nameserver.sin_addr.s_addr,
260
+			      nameserver.sin_port );
261
+			return 0;
262
+		}
263
+
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.
268
+		 */
269
+		while ( ( rr_info = dns_find_rr ( &query, reply ) ) ) {
270
+			switch ( ntohs ( rr_info->type ) ) {
271
+			case DNS_TYPE_A: {
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 );
277
+				return 1; }
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;
283
+
284
+				DBG ( "DNS found CNAME\n" );
285
+				query_info = ( void * )
286
+					dns_decompress_name ( query.payload,
287
+							      cname, reply );
288
+				query_info->qtype = htons ( DNS_TYPE_A );
289
+				query_info->qclass = htons ( DNS_CLASS_IN );
290
+
291
+				if ( ++recursion > DNS_MAX_CNAME_RECURSION ) {
292
+					DBG ( "DNS recursion exceeded\n" );
293
+					return 0;
294
+				}
295
+				break; }
296
+			default:
297
+				DBG ( "DNS got unknown record type %d\n",
298
+				      ntohs ( rr_info->type ) );
299
+				return 0;
300
+			}
301
+		}
302
+		
303
+		/* Determine what to do next based on the type of
304
+		 * query we issued and the reponse we received
305
+		 */
306
+		switch ( qtype ) {
307
+		case DNS_TYPE_A :
308
+			/* We asked for an A record and got nothing;
309
+			 * try the CNAME.
310
+			 */
311
+			DBG ( "DNS found no A record; trying CNAME\n" );
312
+			query_info->qtype = htons ( DNS_TYPE_CNAME );
313
+			break;
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.
318
+			 */
319
+			if ( query_info->qtype != htons ( DNS_TYPE_A ) ) {
320
+				DBG ( "DNS found no CNAME record\n" );
321
+				return 0;
322
+			}
323
+			break;
324
+		default:
325
+			DBG ( "DNS internal error - inconsistent state\n" );
326
+		}
327
+	}
328
+}
329
+
330
+static struct resolver dns_resolver __resolver = {
331
+	.name = "DNS",
332
+	.resolv = dns_resolv,
333
+};

Loading…
Cancel
Save