]>
Commit | Line | Data |
---|---|---|
7f3647d6 | 1 | /* |
706d89bc | 2 | * $Id: rfc1035.c,v 1.4 1999/04/14 06:38:03 wessels Exp $ |
7f3647d6 | 3 | * |
4 | * Low level DNS protocol routines | |
5 | * AUTHOR: Duane Wessels | |
6 | * | |
7 | * SQUID Internet Object Cache http://squid.nlanr.net/Squid/ | |
8 | * ---------------------------------------------------------- | |
9 | * | |
10 | * Squid is the result of efforts by numerous individuals from the | |
11 | * Internet community. Development is led by Duane Wessels of the | |
12 | * National Laboratory for Applied Network Research and funded by the | |
13 | * National Science Foundation. Squid is Copyrighted (C) 1998 by | |
14 | * Duane Wessels and the University of California San Diego. Please | |
15 | * see the COPYRIGHT file for full details. Squid incorporates | |
16 | * software developed and/or copyrighted by other sources. Please see | |
17 | * the CREDITS file for full details. | |
18 | * | |
19 | * This program is free software; you can redistribute it and/or modify | |
20 | * it under the terms of the GNU General Public License as published by | |
21 | * the Free Software Foundation; either version 2 of the License, or | |
22 | * (at your option) any later version. | |
23 | * | |
24 | * This program is distributed in the hope that it will be useful, | |
25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
27 | * GNU General Public License for more details. | |
28 | * | |
29 | * You should have received a copy of the GNU General Public License | |
30 | * along with this program; if not, write to the Free Software | |
31 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. | |
32 | * | |
33 | */ | |
34 | ||
fb29421b | 35 | #include "config.h" |
36 | ||
37 | #if HAVE_STDIO_H | |
38 | #include <stdio.h> | |
39 | #endif | |
40 | #if HAVE_UNISTD_H | |
41 | #include <unistd.h> | |
42 | #endif | |
43 | #if HAVE_STDLIB_H | |
44 | #include <stdlib.h> | |
45 | #endif | |
46 | #if HAVE_MEMORY_H | |
47 | #include <memory.h> | |
48 | #endif | |
49 | #if HAVE_SYS_TYPES_H | |
50 | #include <sys/types.h> | |
51 | #endif | |
52 | #if HAVE_ASSERT_H | |
53 | #include <assert.h> | |
54 | #endif | |
55 | #if HAVE_NETINET_IN_H | |
56 | #include <netinet/in.h> | |
57 | #endif | |
58 | #if HAVE_ARPA_INET_H | |
59 | #include <arpa/inet.h> | |
60 | #endif | |
61 | #if HAVE_STRINGS_H | |
62 | #include <strings.h> | |
63 | #endif | |
64 | ||
7b724b86 | 65 | #include "rfc1035.h" |
fb29421b | 66 | |
67 | #define RFC1035_MAXLABELSZ 63 | |
fb29421b | 68 | |
7b724b86 | 69 | typedef struct _rfc1035_header rfc1035_header; |
fb29421b | 70 | |
71 | int rfc1035_errno; | |
7b724b86 | 72 | const char *rfc1035_error_message; |
73 | struct _rfc1035_header { | |
fb29421b | 74 | unsigned short id; |
75 | unsigned int qr:1; | |
76 | unsigned int opcode:4; | |
77 | unsigned int aa:1; | |
78 | unsigned int tc:1; | |
79 | unsigned int rd:1; | |
80 | unsigned int ra:1; | |
81 | unsigned int rcode:4; | |
82 | unsigned short qdcount; | |
83 | unsigned short ancount; | |
84 | unsigned short nscount; | |
85 | unsigned short arcount; | |
86 | }; | |
87 | ||
fb29421b | 88 | /* |
89 | * rfc1035HeaderPack() | |
90 | * | |
7b724b86 | 91 | * Packs a rfc1035_header structure into a buffer. |
fb29421b | 92 | * Returns number of octets packed (should always be 12) |
93 | */ | |
94 | static off_t | |
7b724b86 | 95 | rfc1035HeaderPack(char *buf, size_t sz, rfc1035_header * hdr) |
fb29421b | 96 | { |
97 | off_t off = 0; | |
98 | unsigned short s; | |
99 | unsigned short t; | |
100 | assert(sz >= 12); | |
101 | s = htons(hdr->id); | |
102 | memcpy(buf + off, &s, sizeof(s)); | |
103 | off += sizeof(s); | |
104 | t = 0; | |
105 | t |= hdr->qr << 15; | |
106 | t |= (hdr->opcode << 11); | |
107 | t |= (hdr->aa << 10); | |
108 | t |= (hdr->tc << 9); | |
109 | t |= (hdr->rd << 8); | |
110 | t |= (hdr->ra << 7); | |
111 | t |= hdr->rcode; | |
112 | s = htons(t); | |
113 | memcpy(buf + off, &s, sizeof(s)); | |
114 | off += sizeof(s); | |
115 | s = htons(hdr->qdcount); | |
116 | memcpy(buf + off, &s, sizeof(s)); | |
117 | off += sizeof(s); | |
118 | s = htons(hdr->ancount); | |
119 | memcpy(buf + off, &s, sizeof(s)); | |
120 | off += sizeof(s); | |
121 | s = htons(hdr->nscount); | |
122 | memcpy(buf + off, &s, sizeof(s)); | |
123 | off += sizeof(s); | |
124 | s = htons(hdr->arcount); | |
125 | memcpy(buf + off, &s, sizeof(s)); | |
126 | off += sizeof(s); | |
127 | assert(off == 12); | |
128 | return off; | |
129 | } | |
130 | ||
131 | /* | |
132 | * rfc1035LabelPack() | |
133 | * | |
134 | * Packs a label into a buffer. The format of | |
135 | * a label is one octet specifying the number of character | |
136 | * bytes to follow. Labels must be smaller than 64 octets. | |
137 | * Returns number of octets packed. | |
138 | */ | |
139 | static off_t | |
140 | rfc1035LabelPack(char *buf, size_t sz, const char *label) | |
141 | { | |
142 | off_t off = 0; | |
143 | size_t len = label ? strlen(label) : 0; | |
144 | if (label) | |
145 | assert(!strchr(label, '.')); | |
146 | if (len > RFC1035_MAXLABELSZ) | |
147 | len = RFC1035_MAXLABELSZ; | |
148 | assert(sz >= len + 1); | |
149 | *(buf + off) = (char) len; | |
150 | off++; | |
151 | memcpy(buf + off, label, len); | |
152 | off += len; | |
153 | return off; | |
154 | } | |
155 | ||
156 | /* | |
157 | * rfc1035NamePack() | |
158 | * | |
159 | * Packs a name into a buffer. Names are packed as a | |
160 | * sequence of labels, terminated with NULL label. | |
161 | * Note message compression is not supported here. | |
162 | * Returns number of octets packed. | |
163 | */ | |
164 | off_t | |
165 | rfc1035NamePack(char *buf, size_t sz, const char *name) | |
166 | { | |
167 | off_t off = 0; | |
168 | char *copy = strdup(name); | |
169 | char *t; | |
170 | for (t = strtok(copy, "."); t; t = strtok(NULL, ".")) | |
171 | off += rfc1035LabelPack(buf + off, sz - off, t); | |
172 | free(copy); | |
173 | off += rfc1035LabelPack(buf + off, sz - off, NULL); | |
174 | assert(off <= sz); | |
175 | return off; | |
176 | } | |
177 | ||
178 | /* | |
179 | * rfc1035QuestionPack() | |
180 | * | |
181 | * Packs a QUESTION section of a message. | |
182 | * Returns number of octets packed. | |
183 | */ | |
184 | static off_t | |
185 | rfc1035QuestionPack(char *buf, | |
186 | size_t sz, | |
187 | const char *name, | |
188 | unsigned short type, | |
189 | unsigned short class) | |
190 | { | |
191 | off_t off = 0; | |
192 | unsigned short s; | |
193 | off += rfc1035NamePack(buf + off, sz - off, name); | |
194 | s = htons(type); | |
195 | memcpy(buf + off, &s, sizeof(s)); | |
196 | off += sizeof(s); | |
197 | s = htons(class); | |
198 | memcpy(buf + off, &s, sizeof(s)); | |
199 | off += sizeof(s); | |
200 | assert(off <= sz); | |
201 | return off; | |
202 | } | |
203 | ||
204 | /* | |
205 | * rfc1035HeaderUnpack() | |
206 | * | |
7b724b86 | 207 | * Unpacks a RFC1035 message header buffer into a rfc1035_header |
fb29421b | 208 | * structure. |
209 | * Returns the new buffer offset, which is the same as number of | |
210 | * octects unpacked since the header starts at offset 0. | |
211 | */ | |
212 | static off_t | |
7b724b86 | 213 | rfc1035HeaderUnpack(const char *buf, size_t sz, rfc1035_header * h) |
fb29421b | 214 | { |
215 | unsigned short s; | |
216 | unsigned short t; | |
217 | off_t off = 0; | |
218 | assert(sz >= 12); | |
219 | memcpy(&s, buf + off, sizeof(s)); | |
220 | off += sizeof(s); | |
221 | h->id = ntohs(s); | |
222 | memcpy(&s, buf + off, sizeof(s)); | |
223 | off += sizeof(s); | |
224 | t = ntohs(s); | |
225 | h->qr = (t >> 15) & 0x01; | |
226 | h->opcode = (t >> 11) & 0x0F; | |
227 | h->aa = (t >> 10) & 0x01; | |
228 | h->tc = (t >> 8) & 0x01; | |
229 | h->rd = (t >> 8) & 0x01; | |
230 | h->ra = (t >> 7) & 0x01; | |
231 | h->rcode = t & 0x0F; | |
232 | memcpy(&s, buf + off, sizeof(s)); | |
233 | off += sizeof(s); | |
234 | h->qdcount = ntohs(s); | |
235 | memcpy(&s, buf + off, sizeof(s)); | |
236 | off += sizeof(s); | |
237 | h->ancount = ntohs(s); | |
238 | memcpy(&s, buf + off, sizeof(s)); | |
239 | off += sizeof(s); | |
240 | h->nscount = ntohs(s); | |
241 | memcpy(&s, buf + off, sizeof(s)); | |
242 | off += sizeof(s); | |
243 | h->arcount = ntohs(s); | |
244 | assert(off == 12); | |
245 | return off; | |
246 | } | |
247 | ||
248 | /* | |
249 | * rfc1035NameUnpack() | |
250 | * | |
251 | * Unpacks a Name in a message buffer into a char*. | |
252 | * Note 'buf' points to the beginning of the whole message, | |
253 | * 'off' points to the spot where the Name begins, and 'sz' | |
254 | * is the size of the whole message. 'name' must be allocated | |
255 | * by the caller. | |
256 | * | |
257 | * Supports the RFC1035 message compression through recursion. | |
258 | * | |
259 | * Returns the new buffer offset. | |
260 | */ | |
261 | static off_t | |
262 | rfc1035NameUnpack(const char *buf, size_t sz, off_t off, char *name, size_t ns) | |
263 | { | |
264 | off_t no = 0; | |
265 | unsigned char c; | |
266 | size_t len; | |
267 | assert(ns > 0); | |
268 | do { | |
269 | c = *(buf + off); | |
270 | if (c > RFC1035_MAXLABELSZ) { | |
271 | /* fucking compression */ | |
272 | unsigned short s; | |
273 | off_t ptr; | |
274 | memcpy(&s, buf + off, sizeof(s)); | |
275 | s = ntohs(s); | |
276 | off += sizeof(s); | |
277 | ptr = s & 0x3FFF; | |
278 | (void) rfc1035NameUnpack(buf, sz, ptr, name + no, ns - no); | |
279 | return off; | |
280 | } else { | |
281 | off++; | |
282 | len = (size_t) c; | |
283 | if (len == 0) | |
284 | break; | |
7b724b86 | 285 | if (len > (ns - 1)) |
286 | len = ns - 1; | |
fb29421b | 287 | memcpy(name + no, buf + off, len); |
288 | off += len; | |
289 | no += len; | |
290 | *(name + (no++)) = '.'; | |
291 | } | |
292 | } while (c > 0); | |
293 | *(name + no - 1) = '\0'; | |
294 | assert(no <= ns); | |
295 | return off; | |
296 | } | |
297 | ||
298 | /* | |
299 | * rfc1035RRUnpack() | |
300 | * | |
301 | * Unpacks a RFC1035 Resource Record into 'RR' from a message buffer. | |
302 | * The caller must free RR->rdata! | |
303 | * Returns the new message buffer offset. | |
304 | */ | |
305 | static off_t | |
7b724b86 | 306 | rfc1035RRUnpack(const char *buf, size_t sz, off_t off, rfc1035_rr * RR) |
fb29421b | 307 | { |
308 | unsigned short s; | |
309 | unsigned int i; | |
310 | off = rfc1035NameUnpack(buf, sz, off, RR->name, RFC1035_MAXHOSTNAMESZ); | |
311 | memcpy(&s, buf + off, sizeof(s)); | |
312 | off += sizeof(s); | |
313 | RR->type = ntohs(s); | |
314 | memcpy(&s, buf + off, sizeof(s)); | |
315 | off += sizeof(s); | |
316 | RR->class = ntohs(s); | |
317 | memcpy(&i, buf + off, sizeof(i)); | |
318 | off += sizeof(i); | |
319 | RR->ttl = ntohl(i); | |
320 | memcpy(&s, buf + off, sizeof(s)); | |
321 | off += sizeof(s); | |
322 | RR->rdlength = ntohs(s); | |
323 | RR->rdata = malloc(RR->rdlength); | |
324 | memcpy(RR->rdata, buf + off, RR->rdlength); | |
325 | off += RR->rdlength; | |
326 | assert(off <= sz); | |
327 | return off; | |
328 | } | |
329 | ||
7b724b86 | 330 | static unsigned short |
331 | rfc1035Qid(void) | |
332 | { | |
333 | static unsigned short qid = 0x0001; | |
334 | if (++qid == 0xFFFF) | |
335 | qid = 0x0001; | |
336 | return qid; | |
337 | } | |
338 | ||
339 | void | |
340 | rfc1035RRDestroy(rfc1035_rr * rr, int n) | |
341 | { | |
342 | if (rr == NULL) | |
343 | return; | |
344 | assert(n > 0); | |
345 | while (n--) { | |
346 | if (rr[n].rdata) | |
347 | free(rr[n].rdata); | |
348 | } | |
349 | free(rr); | |
350 | } | |
351 | ||
fb29421b | 352 | int |
7b724b86 | 353 | rfc1035AnswersUnpack(const char *buf, |
354 | size_t sz, | |
355 | rfc1035_rr ** records, | |
356 | unsigned short *id) | |
fb29421b | 357 | { |
358 | off_t off = 0; | |
359 | int l; | |
360 | int i; | |
7b724b86 | 361 | int nr = 0; |
362 | rfc1035_header hdr; | |
363 | rfc1035_rr *recs; | |
fb29421b | 364 | memset(&hdr, '\0', sizeof(hdr)); |
365 | off = rfc1035HeaderUnpack(buf + off, sz - off, &hdr); | |
366 | *id = hdr.id; | |
7b724b86 | 367 | rfc1035_errno = 0; |
368 | rfc1035_error_message = NULL; | |
fb29421b | 369 | if (hdr.rcode) { |
370 | rfc1035_errno = (int) hdr.rcode; | |
7b724b86 | 371 | switch (rfc1035_errno) { |
372 | case 0: | |
373 | rfc1035_error_message = "No error condition"; | |
374 | break; | |
375 | case 1: | |
376 | rfc1035_error_message = "Format Error: The name server was " | |
377 | "unable to interpret the query."; | |
378 | break; | |
379 | case 2: | |
380 | rfc1035_error_message = "Server Failure: The name server was " | |
381 | "unable to process this query."; | |
382 | break; | |
383 | case 3: | |
384 | rfc1035_error_message = "Name Error: The domain name does " | |
385 | "not exist."; | |
386 | break; | |
387 | case 4: | |
388 | rfc1035_error_message = "Not Implemented: The name server does " | |
389 | "not support the requested kind of query."; | |
390 | break; | |
391 | case 5: | |
392 | rfc1035_error_message = "Refused: The name server refuses to " | |
393 | "perform the specified operation."; | |
394 | break; | |
395 | default: | |
396 | rfc1035_error_message = "Unknown Error"; | |
397 | break; | |
398 | } | |
fb29421b | 399 | return -rfc1035_errno; |
400 | } | |
401 | i = (int) hdr.qdcount; | |
402 | /* skip question */ | |
403 | while (i--) { | |
404 | do { | |
405 | l = (int) *(buf + off); | |
406 | off++; | |
407 | if (l > RFC1035_MAXLABELSZ) { /* compression */ | |
408 | off++; | |
409 | break; | |
410 | } else { | |
411 | off += l; | |
412 | } | |
413 | } while (l > 0); | |
414 | off += 4; /* qtype, qclass */ | |
415 | assert(off <= sz); | |
416 | } | |
417 | i = (int) hdr.ancount; | |
706d89bc | 418 | if (i == 0) |
419 | return 0; | |
7b724b86 | 420 | recs = calloc(i, sizeof(*recs)); |
fb29421b | 421 | while (i--) { |
7b724b86 | 422 | off = rfc1035RRUnpack(buf, sz, off, &recs[i]); |
fb29421b | 423 | assert(off <= sz); |
7b724b86 | 424 | nr++; |
fb29421b | 425 | } |
7b724b86 | 426 | *records = recs; |
427 | return nr; | |
fb29421b | 428 | } |
429 | ||
430 | /* | |
431 | * rfc1035BuildQuery() | |
432 | * | |
433 | * Builds a message buffer with a QUESTION to lookup A records | |
434 | * for a hostname. Caller must allocate 'buf' which should | |
435 | * probably be at least 512 octets. The 'szp' initially | |
436 | * specifies the size of the buffer, on return it contains | |
437 | * the size of the message (i.e. how much to write). | |
438 | * Return value is the query ID. | |
439 | */ | |
440 | unsigned short | |
7b724b86 | 441 | rfc1035BuildAQuery(const char *hostname, char *buf, size_t * szp) |
fb29421b | 442 | { |
7b724b86 | 443 | static rfc1035_header h; |
fb29421b | 444 | off_t offset = 0; |
445 | size_t sz = *szp; | |
446 | memset(&h, '\0', sizeof(h)); | |
7b724b86 | 447 | h.id = rfc1035Qid(); |
fb29421b | 448 | h.qr = 0; |
449 | h.rd = 1; | |
450 | h.opcode = 0; /* QUERY */ | |
451 | h.qdcount = (unsigned int) 1; | |
452 | offset += rfc1035HeaderPack(buf + offset, sz - offset, &h); | |
453 | offset += rfc1035QuestionPack(buf + offset, | |
454 | sz - offset, | |
455 | hostname, | |
456 | RFC1035_TYPE_A, | |
457 | RFC1035_CLASS_IN); | |
458 | assert(offset <= sz); | |
459 | *szp = (size_t) offset; | |
7b724b86 | 460 | return h.id; |
fb29421b | 461 | } |
462 | ||
463 | #if DRIVER | |
464 | int | |
465 | main(int argc, char *argv[]) | |
466 | { | |
7b724b86 | 467 | rfc1035_header h; |
fb29421b | 468 | char input[512]; |
469 | char buf[512]; | |
470 | char rbuf[512]; | |
471 | size_t sz = 512; | |
472 | unsigned short sid; | |
473 | off_t offset = 0; | |
474 | int s; | |
475 | int rl; | |
476 | struct sockaddr_in S; | |
477 | setbuf(stdout, NULL); | |
478 | setbuf(stderr, NULL); | |
479 | s = socket(PF_INET, SOCK_DGRAM, 0); | |
480 | if (s < 0) { | |
481 | perror("socket"); | |
482 | return 1; | |
483 | } | |
484 | while (fgets(input, 512, stdin)) { | |
485 | strtok(input, "\r\n"); | |
486 | memset(buf, '\0', 512); | |
487 | memset(&h, '\0', sizeof(h)); | |
488 | offset = 0; | |
489 | h.id = sid = (unsigned short) 0x1234; | |
490 | h.qr = 0; | |
491 | h.rd = 1; | |
492 | h.opcode = 0; | |
493 | h.qdcount = (unsigned int) 1; | |
494 | offset += rfc1035HeaderPack(buf + offset, sz - offset, &h); | |
495 | offset += rfc1035QuestionPack(buf + offset, | |
496 | sz - offset, | |
497 | input, | |
498 | RFC1035_TYPE_A, | |
499 | RFC1035_CLASS_IN); | |
500 | memset(&S, '\0', sizeof(S)); | |
501 | S.sin_family = AF_INET; | |
502 | S.sin_port = htons(53); | |
503 | S.sin_addr.s_addr = inet_addr("128.117.28.219"); | |
504 | sendto(s, buf, (size_t) offset, 0, (struct sockaddr *) &S, sizeof(S)); | |
505 | do { | |
7b724b86 | 506 | fd_set R; |
507 | struct timeval to; | |
fb29421b | 508 | FD_ZERO(&R); |
509 | FD_SET(s, &R); | |
510 | to.tv_sec = 10; | |
511 | to.tv_usec = 0; | |
7b724b86 | 512 | rl = select(s + 1, &R, NULL, NULL, &to); |
513 | } while (0); | |
514 | if (rl < 1) { | |
515 | printf("TIMEOUT\n"); | |
516 | continue; | |
517 | } | |
fb29421b | 518 | memset(rbuf, '\0', 512); |
519 | rl = recv(s, rbuf, 512, 0); | |
520 | { | |
521 | unsigned short rid; | |
522 | int i; | |
523 | int n; | |
524 | struct in_addr addrs[10]; | |
525 | time_t ttl = 0; | |
526 | char rname[RFC1035_MAXHOSTNAMESZ]; | |
527 | n = rfc1035ARecordsUnpack(rbuf, | |
528 | rl, | |
529 | addrs, 10, | |
530 | rname, RFC1035_MAXHOSTNAMESZ, | |
531 | &rid, | |
532 | &ttl); | |
533 | if (rid != sid) { | |
534 | printf("ERROR, ID mismatch (%#hx, %#hx)\n", sid, rid); | |
535 | } else if (n < 0) { | |
536 | printf("ERROR %d\n", rfc1035_errno); | |
537 | } else { | |
538 | printf("name\t%s, %d A records\n", rname, n); | |
539 | printf("ttl\t%d\n", (int) ttl); | |
540 | for (i = 0; i < n; i++) | |
541 | printf("addr %d\t%s\n", i, inet_ntoa(addrs[i])); | |
542 | } | |
543 | } | |
544 | } | |
545 | return 0; | |
546 | } | |
547 | #endif |