dns.c 10.2 KB
Newer Older
Ted Lemon's avatar
Ted Lemon committed
1
2
/* dns.c

3
   Domain Name Service subroutines. */
Ted Lemon's avatar
Ted Lemon committed
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

/*
 * Copyright (C) 1992 by Ted Lemon.
 * Copyright (c) 1997 The Internet Software Consortium.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of The Internet Software Consortium nor the names
 *    of its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
 * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * This file is based on software written in 1992 by Ted Lemon for
 * a portable network boot loader.   That original code base has been
 * substantially modified for use in the Internet Software Consortium
 * DHCP suite.
 *
42
43
44
45
46
 * These later modifications were done on behalf of the Internet
 * Software Consortium by Ted Lemon <mellon@fugue.com> in cooperation
 * with Vixie Enterprises.  To learn more about the Internet Software
 * Consortium, see ``http://www.vix.com/isc''.  To learn more about
 * Vixie Enterprises, see ``http://www.vix.com''.
Ted Lemon's avatar
Ted Lemon committed
47
48
49
50
 */

#ifndef lint
static char copyright[] =
51
"$Id: dns.c,v 1.6 1998/01/12 01:00:09 mellon Exp $ Copyright (c) 1997 The Internet Software Consortium.  All rights reserved.\n";
Ted Lemon's avatar
Ted Lemon committed
52
53
54
55
56
#endif /* not lint */

#include "dhcpd.h"
#include "arpa/nameser.h"

57
58
59
60
61
62
int dns_protocol_initialized;
int dns_protocol_fd;

static int addlabel PROTO ((u_int8_t *, char *));
static int skipname PROTO ((u_int8_t *));
static int copy_out_name PROTO ((u_int8_t *, u_int8_t *, char *));
63
64
65
static int nslookup PROTO ((u_int8_t, char *, int, u_int16_t, u_int16_t));
static int zonelookup PROTO ((u_int8_t, char *, int, u_int16_t));
u_int16_t dns_port;
66

67
68
struct dns_query *queries [65536];

Ted Lemon's avatar
Ted Lemon committed
69
70
/* Initialize the DNS protocol. */

71
void dns_startup ()
Ted Lemon's avatar
Ted Lemon committed
72
{
73
	struct servent *srv;
Ted Lemon's avatar
Ted Lemon committed
74
75
76
	struct sockaddr_in from;

	/* Only initialize icmp once. */
77
78
79
	if (dns_protocol_initialized)
		error ("attempted to reinitialize dns protocol");
	dns_protocol_initialized = 1;
Ted Lemon's avatar
Ted Lemon committed
80
81

	/* Get the protocol number (should be 1). */
82
	srv = getservbyname ("domain", "tcp");
83
	if (srv)
84
85
86
		dns_port = srv -> s_port;
	else
		dns_port = htons (53);
Ted Lemon's avatar
Ted Lemon committed
87

88
	/* Get a socket for the DNS protocol. */
89
	dns_protocol_fd = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP);
90
	if (dns_protocol_fd < 0)
91
		error ("unable to create dns socket: %m");
Ted Lemon's avatar
Ted Lemon committed
92

93
94
	pick_name_server ();

95
	add_protocol ("dns", dns_protocol_fd, dns_packet, 0);
Ted Lemon's avatar
Ted Lemon committed
96
97
98
99
100
101
102
103
104
}

/* Label manipulation stuff; see RFC1035, page 28 section 4.1.2 and
   page 30, section 4.1.4. */

/* addlabel copies a label into the specified buffer, putting the length of
   the label in the first character, the contents of the label in subsequent
   characters, and returning the length of the conglomeration. */

105
106
107
static int addlabel (buf, label)
	u_int8_t *buf;
	char *label;
Ted Lemon's avatar
Ted Lemon committed
108
{
109
110
111
	*buf = strlen (label);
	memcpy (buf + 1, label, *buf);
	return *buf + 1;
Ted Lemon's avatar
Ted Lemon committed
112
113
114
115
116
}

/* skipname skips over all of the labels in a single domain name,
   returning the length of the domain name. */

117
118
static int skipname (label)
     u_int8_t *label;
Ted Lemon's avatar
Ted Lemon committed
119
{
120
121
122
123
124
	if (*label & INDIR_MASK)
		return 2;
	if (*label == 0)
		return 1;
	return *label + 1 + skipname (label + *label + 1);
Ted Lemon's avatar
Ted Lemon committed
125
126
127
128
129
130
}

/* copy_out_name copies out the name appearing at the specified location
   into a string, stored as fields seperated by dots rather than lengths
   and labels.   The length of the label-formatted name is returned. */

131
132
133
134
static int copy_out_name (base, name, buf)
     u_int8_t *base;
     u_int8_t *name;
     char *buf;
Ted Lemon's avatar
Ted Lemon committed
135
{
136
137
138
139
140
141
142
143
144
145
146
147
	if (*name & INDIR_MASK) {
		int offset = (*name & ~INDIR_MASK) + (*name + 1);
		return copy_out_name (base, base + offset, buf);
	}
	if (!*name) {
		*buf = 0;
		return 1;
	}
	memcpy (buf, name + 1, *name);
	*(buf + *name) = '.';
	return (*name + 1
		+ copy_out_name (base, name + *name + 1, buf + *name + 1));
Ted Lemon's avatar
Ted Lemon committed
148
149
150
151
152
153
154
155
}

/* ns_inaddr_lookup constructs a PTR lookup query for an internet address -
   e.g., 1.200.9.192.in-addr.arpa.   If the specified timeout period passes
   before the query is satisfied, or if the query fails, the callback is
   called with a null pointer.   Otherwise, the callback is called with the
   address of the string returned by the name server. */

156
struct dns_query *ns_inaddr_lookup (inaddr, wakeup)
157
	struct iaddr inaddr;
158
	struct dns_wakeup *wakeup;
Ted Lemon's avatar
Ted Lemon committed
159
{
160
161
	unsigned char question [512];
	unsigned char *s;
162
163
164
165
	unsigned char *label;
	int i;
	unsigned char c;

166
167
168
	s = question;

	/* Copy out the digits. */
169
170
171
	for (i = 3; i >= 0; --i) {
		label = s++;
		*label = 1;
172
		c = inaddr.iabuf [i];
173
174
175
176
177
178
179
180
181
		if (c > 100) {
			++*label;
			*s++ = '0' + c / 100;
		}
		if (c > 10) {
			++*label;
			*s++ = '0' + ((c / 10) % 10);
		}
		*s++ = '0' + (c % 10);
Ted Lemon's avatar
Ted Lemon committed
182
	}
183
184
185
	s += addlabel (s, "in-addr");
	s += addlabel (s, "arpa");
	*s++ = 0;
Ted Lemon's avatar
Ted Lemon committed
186

187
188
189
190
191
192
193
194
195
196
	/* Set the query type. */
	putUShort (s, T_PTR);
	s += sizeof (u_int16_t);

	/* Set the query class. */
	putUShort (s, C_IN);
	s += sizeof (u_int16_t);

	return ns_query (question, s - question, wakeup);
}
Ted Lemon's avatar
Ted Lemon committed
197

198
199
200
201
struct dns_query *ns_query (question, len, wakeup)
	unsigned char *question;
	int len;
	struct dns_wakeup *wakeup;
Ted Lemon's avatar
Ted Lemon committed
202
{
203
	HEADER *hdr;
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
	struct dns_query *query;
	unsigned char *s;

	/* See if there's already a query for this name, and allocate a
	   query if none exists. */
	query = find_dns_query (question, len, 1);

	/* If we can't allocate a query, report that the query failed. */
	if (!query) {
		return (struct dns_query *)-1;
	}

	/* If the query has already been answered, return it. */
	if (query -> expiry > cur_time)
		return query;

	/* The query hasn't yet been answered, so we have to wait, one
	   way or another.   Put the wakeup on the list. */
	if (wakeup) {
		wakeup -> next = query -> wakeups;
		query -> wakeups = wakeup;
	}

	/* If the query has already been sent, but we don't yet have
	   an answer, we're done. */
	if (query -> sent)
		return (struct dns_query *)0;
231

232
	/* Construct a header... */
233
	hdr = (HEADER *)query -> buf;
234
	memset (hdr, 0, sizeof *hdr);
235
	hdr -> id = query -> id;
236
237
238
239
	hdr -> rd = 1;
	hdr -> opcode = QUERY;
	hdr -> qdcount = htons (1);

240
241
242
243
244
	/* Copy the name into the buffer. */
	s = (unsigned char *)hdr + 1;
	memcpy (s, question, len);
	query -> question = s;
	query -> question_len = len;
245

246
247
248
249
250
251
	/* Figure out how long the whole message is */
	s += len;
	query -> len = s - query -> buf;

	/* Flag the query as having been sent. */
	query -> sent = 1;
252
253

	/* Send the query. */
254
	dns_timeout (query);
255

256
257
	/* No answer yet, obviously. */
	return (struct dns_query *)0;
Ted Lemon's avatar
Ted Lemon committed
258
259
}

260
/* Retransmit a DNS query. */
261

262
263
264
265
266
void dns_timeout (qv)
	void *qv;
{
	struct dns_query *query = qv;
	int status;
267

268
269
270
	/* Choose the server to send to. */
	if (!query -> next_server)
		query -> next_server = first_name_server ();
271

272
273
274
275
276
277
278
279
280
	/* Send the query. */
	if (query -> next_server)
		status = sendto (dns_protocol_fd,
				 query -> buf, query -> len, 0,
				 ((struct sockaddr *)&query ->
				  next_server -> addr),
				 sizeof query -> next_server -> addr);
	else
		status = -1;
281

282
283
	/* Look for the next server... */
	query -> next_server = query -> next_server -> next;
284

285
286
287
	/* If this is our first time, backoff one second. */
	if (!query -> backoff)
		query -> backoff = 1;
288

289
290
291
	/* If the send failed, don't advance the backoff. */
	else if (status < 0)
		;
292

293
294
295
296
297
298
299
	/* If we haven't run out of servers to try, don't backoff. */
	else if (query -> next_server)
		;

	/* If we haven't backed off enough yet, back off some more. */
	else if (query -> backoff < 30)
		query -> backoff += random() % query -> backoff;
300

301
302
	/* Set up the timeout. */
	add_timeout (cur_time + query -> backoff, dns_timeout, query);
303
304
}

305
306
/* Process a reply from a name server. */

307
void dns_packet (protocol)
308
	struct protocol *protocol;
Ted Lemon's avatar
Ted Lemon committed
309
{
310
311
	HEADER *ns_header;
	struct sockaddr_in from;
312
313
	struct dns_wakeup *wakeup;
	unsigned char buf [512];
314
	unsigned char *base;
315
	unsigned char *dptr, *name;
316
317
318
319
320
321
	u_int16_t type;
	u_int16_t class;
	TIME ttl;
	u_int16_t rdlength;
	int len, status;
	int i;
322
	struct dns_query *query;
323
324

	len = sizeof from;
325
	status = recvfrom (protocol -> fd, buf, sizeof buf, 0,
326
327
			  (struct sockaddr *)&from, &len);
	if (status < 0) {
328
329
330
331
332
333
334
		warn ("dns_packet: %m");
		return;
	}

	/* Response is too long? */
	if (len > 512) {
		warn ("dns_packet: dns message too long (%d)", len);
335
336
		return;
	}
Ted Lemon's avatar
Ted Lemon committed
337

338
339
	ns_header = (HEADER *)buf;
	base = (unsigned char *)(ns_header + 1);
Ted Lemon's avatar
Ted Lemon committed
340

341
342
343
344
345
346
347
	/* Parse the response... */
	dptr = base;

	/* If this is a response to a query from us, there should have
           been only one query. */
	if (ntohs (ns_header -> qdcount) != 1) {
		dns_bogus (buf, len);
348
349
		return;
	}
Ted Lemon's avatar
Ted Lemon committed
350

351
352
	/* Find the start of the name in the query. */
	name = dptr;
Ted Lemon's avatar
Ted Lemon committed
353

354
355
356
357
358
359
360
361
362
363
364
	/* Skip over the name. */
	dptr += skipname (dptr);

	/* Skip over the query type and query class. */
	dptr += 2 * sizeof (u_int16_t);

	/* See if we asked this question. */
	query = find_dns_query (name, dptr - name, 0);
	if (!query) {
		dns_bogus (buf, len);
		return;
365
	}
366

367
368
369
370
371
372
373
374
375
376
377
378
379
380
	/* Save the response. */
	memcpy (buf, query -> buf, len);

	/* Remember where the question is and how long it is. */
	query -> question = name;
	query -> question_len = dptr - name;

	/* Remember where the answer is and how long it is. */
	query -> answer = dptr;
	query -> answer_len = len - (dptr - buf);

	/* Wake up everybody who's waiting. */
	for (wakeup = query -> wakeups; wakeup; wakeup = wakeup -> next) {
		(*wakeup -> func) (query);
381
	}
Ted Lemon's avatar
Ted Lemon committed
382
}