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