]>
Commit | Line | Data |
---|---|---|
7f3647d6 | 1 | /* |
262a0e14 | 2 | * $Id$ |
7f3647d6 | 3 | * |
4 | * Low level DNS protocol routines | |
5 | * AUTHOR: Duane Wessels | |
6 | * | |
2b6662ba | 7 | * SQUID Web Proxy Cache http://www.squid-cache.org/ |
7f3647d6 | 8 | * ---------------------------------------------------------- |
9 | * | |
2b6662ba | 10 | * Squid is the result of efforts by numerous individuals from |
11 | * the Internet community; see the CONTRIBUTORS file for full | |
12 | * details. Many organizations have provided support for Squid's | |
13 | * development; see the SPONSORS file for full details. Squid is | |
14 | * Copyrighted (C) 2001 by the Regents of the University of | |
15 | * California; see the COPYRIGHT file for full details. Squid | |
16 | * incorporates software developed and/or copyrighted by other | |
17 | * sources; see the CREDITS file for full details. | |
7f3647d6 | 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. | |
26ac0430 | 23 | * |
7f3647d6 | 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. | |
26ac0430 | 28 | * |
7f3647d6 | 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 | ||
c415c128 | 35 | /* |
36 | * KNOWN BUGS: | |
26ac0430 | 37 | * |
c415c128 | 38 | * UDP replies with TC set should be retried via TCP |
39 | */ | |
40 | ||
fb29421b | 41 | #include "config.h" |
ec7bade0 | 42 | #include "util.h" |
fb29421b | 43 | |
44 | #if HAVE_STDIO_H | |
45 | #include <stdio.h> | |
46 | #endif | |
47 | #if HAVE_UNISTD_H | |
48 | #include <unistd.h> | |
49 | #endif | |
fb29421b | 50 | #if HAVE_MEMORY_H |
51 | #include <memory.h> | |
52 | #endif | |
fb29421b | 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" |
fb29421b | 67 | |
68 | #define RFC1035_MAXLABELSZ 63 | |
0768dfce | 69 | #define rfc1035_unpack_error 15 |
70 | ||
71 | #if 0 | |
72 | #define RFC1035_UNPACK_DEBUG fprintf(stderr, "unpack error at %s:%d\n", __FILE__,__LINE__) | |
73 | #else | |
74 | #define RFC1035_UNPACK_DEBUG (void)0 | |
75 | #endif | |
76 | ||
fb29421b | 77 | |
fb29421b | 78 | |
fb29421b | 79 | /* |
80 | * rfc1035HeaderPack() | |
26ac0430 | 81 | * |
7b724b86 | 82 | * Packs a rfc1035_header structure into a buffer. |
fb29421b | 83 | * Returns number of octets packed (should always be 12) |
84 | */ | |
64b636b9 | 85 | int |
ec7bade0 | 86 | rfc1035HeaderPack(char *buf, size_t sz, rfc1035_message * hdr) |
fb29421b | 87 | { |
fc0f8140 | 88 | int off = 0; |
fb29421b | 89 | unsigned short s; |
90 | unsigned short t; | |
91 | assert(sz >= 12); | |
92 | s = htons(hdr->id); | |
93 | memcpy(buf + off, &s, sizeof(s)); | |
94 | off += sizeof(s); | |
95 | t = 0; | |
96 | t |= hdr->qr << 15; | |
97 | t |= (hdr->opcode << 11); | |
98 | t |= (hdr->aa << 10); | |
99 | t |= (hdr->tc << 9); | |
100 | t |= (hdr->rd << 8); | |
101 | t |= (hdr->ra << 7); | |
102 | t |= hdr->rcode; | |
103 | s = htons(t); | |
104 | memcpy(buf + off, &s, sizeof(s)); | |
105 | off += sizeof(s); | |
106 | s = htons(hdr->qdcount); | |
107 | memcpy(buf + off, &s, sizeof(s)); | |
108 | off += sizeof(s); | |
109 | s = htons(hdr->ancount); | |
110 | memcpy(buf + off, &s, sizeof(s)); | |
111 | off += sizeof(s); | |
112 | s = htons(hdr->nscount); | |
113 | memcpy(buf + off, &s, sizeof(s)); | |
114 | off += sizeof(s); | |
115 | s = htons(hdr->arcount); | |
116 | memcpy(buf + off, &s, sizeof(s)); | |
117 | off += sizeof(s); | |
118 | assert(off == 12); | |
119 | return off; | |
120 | } | |
121 | ||
122 | /* | |
123 | * rfc1035LabelPack() | |
26ac0430 | 124 | * |
fb29421b | 125 | * Packs a label into a buffer. The format of |
126 | * a label is one octet specifying the number of character | |
127 | * bytes to follow. Labels must be smaller than 64 octets. | |
128 | * Returns number of octets packed. | |
129 | */ | |
fc0f8140 | 130 | static int |
fb29421b | 131 | rfc1035LabelPack(char *buf, size_t sz, const char *label) |
132 | { | |
fc0f8140 | 133 | int off = 0; |
fb29421b | 134 | size_t len = label ? strlen(label) : 0; |
135 | if (label) | |
26ac0430 | 136 | assert(!strchr(label, '.')); |
fb29421b | 137 | if (len > RFC1035_MAXLABELSZ) |
26ac0430 | 138 | len = RFC1035_MAXLABELSZ; |
fb29421b | 139 | assert(sz >= len + 1); |
140 | *(buf + off) = (char) len; | |
141 | off++; | |
142 | memcpy(buf + off, label, len); | |
143 | off += len; | |
144 | return off; | |
145 | } | |
146 | ||
147 | /* | |
148 | * rfc1035NamePack() | |
26ac0430 | 149 | * |
fb29421b | 150 | * Packs a name into a buffer. Names are packed as a |
151 | * sequence of labels, terminated with NULL label. | |
152 | * Note message compression is not supported here. | |
153 | * Returns number of octets packed. | |
154 | */ | |
fc0f8140 | 155 | static int |
fb29421b | 156 | rfc1035NamePack(char *buf, size_t sz, const char *name) |
157 | { | |
64b636b9 | 158 | unsigned int off = 0; |
ec7bade0 | 159 | char *copy = xstrdup(name); |
fb29421b | 160 | char *t; |
0a8e6ec2 | 161 | /* |
162 | * NOTE: use of strtok here makes names like foo....com valid. | |
163 | */ | |
fb29421b | 164 | for (t = strtok(copy, "."); t; t = strtok(NULL, ".")) |
26ac0430 | 165 | off += rfc1035LabelPack(buf + off, sz - off, t); |
ec7bade0 | 166 | xfree(copy); |
fb29421b | 167 | off += rfc1035LabelPack(buf + off, sz - off, NULL); |
168 | assert(off <= sz); | |
169 | return off; | |
170 | } | |
171 | ||
172 | /* | |
173 | * rfc1035QuestionPack() | |
26ac0430 | 174 | * |
fb29421b | 175 | * Packs a QUESTION section of a message. |
176 | * Returns number of octets packed. | |
177 | */ | |
64b636b9 | 178 | int |
fb29421b | 179 | rfc1035QuestionPack(char *buf, |
26ac0430 AJ |
180 | const size_t sz, |
181 | const char *name, | |
182 | const unsigned short type, | |
183 | const unsigned short _class) | |
fb29421b | 184 | { |
64b636b9 | 185 | unsigned int off = 0; |
fb29421b | 186 | unsigned short s; |
187 | off += rfc1035NamePack(buf + off, sz - off, name); | |
188 | s = htons(type); | |
189 | memcpy(buf + off, &s, sizeof(s)); | |
190 | off += sizeof(s); | |
29b8d8d6 | 191 | s = htons(_class); |
fb29421b | 192 | memcpy(buf + off, &s, sizeof(s)); |
193 | off += sizeof(s); | |
194 | assert(off <= sz); | |
195 | return off; | |
196 | } | |
197 | ||
198 | /* | |
199 | * rfc1035HeaderUnpack() | |
26ac0430 | 200 | * |
ec7bade0 | 201 | * Unpacks a RFC1035 message header buffer into the header fields |
202 | * of the rfc1035_message structure. | |
0768dfce | 203 | * |
204 | * Updates the buffer offset, which is the same as number of | |
fb29421b | 205 | * octects unpacked since the header starts at offset 0. |
0768dfce | 206 | * |
207 | * Returns 0 (success) or 1 (error) | |
fb29421b | 208 | */ |
64b636b9 | 209 | int |
210 | rfc1035HeaderUnpack(const char *buf, size_t sz, unsigned int *off, rfc1035_message * h) | |
fb29421b | 211 | { |
212 | unsigned short s; | |
213 | unsigned short t; | |
0768dfce | 214 | assert(*off == 0); |
215 | /* | |
216 | * The header is 12 octets. This is a bogus message if the size | |
217 | * is less than that. | |
218 | */ | |
219 | if (sz < 12) | |
26ac0430 | 220 | return 1; |
0768dfce | 221 | memcpy(&s, buf + (*off), sizeof(s)); |
222 | (*off) += sizeof(s); | |
fb29421b | 223 | h->id = ntohs(s); |
0768dfce | 224 | memcpy(&s, buf + (*off), sizeof(s)); |
225 | (*off) += sizeof(s); | |
fb29421b | 226 | t = ntohs(s); |
227 | h->qr = (t >> 15) & 0x01; | |
228 | h->opcode = (t >> 11) & 0x0F; | |
229 | h->aa = (t >> 10) & 0x01; | |
c415c128 | 230 | h->tc = (t >> 9) & 0x01; |
fb29421b | 231 | h->rd = (t >> 8) & 0x01; |
232 | h->ra = (t >> 7) & 0x01; | |
0768dfce | 233 | /* |
234 | * We might want to check that the reserved 'Z' bits (6-4) are | |
235 | * all zero as per RFC 1035. If not the message should be | |
236 | * rejected. | |
64b636b9 | 237 | * NO! RFCs say ignore inbound reserved, they may be used in future. |
238 | * NEW messages need to be set 0, thats all. | |
0768dfce | 239 | */ |
fb29421b | 240 | h->rcode = t & 0x0F; |
0768dfce | 241 | memcpy(&s, buf + (*off), sizeof(s)); |
242 | (*off) += sizeof(s); | |
fb29421b | 243 | h->qdcount = ntohs(s); |
0768dfce | 244 | memcpy(&s, buf + (*off), sizeof(s)); |
245 | (*off) += sizeof(s); | |
fb29421b | 246 | h->ancount = ntohs(s); |
0768dfce | 247 | memcpy(&s, buf + (*off), sizeof(s)); |
248 | (*off) += sizeof(s); | |
fb29421b | 249 | h->nscount = ntohs(s); |
0768dfce | 250 | memcpy(&s, buf + (*off), sizeof(s)); |
251 | (*off) += sizeof(s); | |
fb29421b | 252 | h->arcount = ntohs(s); |
0768dfce | 253 | assert((*off) == 12); |
254 | return 0; | |
fb29421b | 255 | } |
256 | ||
257 | /* | |
258 | * rfc1035NameUnpack() | |
26ac0430 | 259 | * |
fb29421b | 260 | * Unpacks a Name in a message buffer into a char*. |
261 | * Note 'buf' points to the beginning of the whole message, | |
262 | * 'off' points to the spot where the Name begins, and 'sz' | |
263 | * is the size of the whole message. 'name' must be allocated | |
264 | * by the caller. | |
265 | * | |
266 | * Supports the RFC1035 message compression through recursion. | |
267 | * | |
0768dfce | 268 | * Updates the new buffer offset. |
269 | * | |
270 | * Returns 0 (success) or 1 (error) | |
fb29421b | 271 | */ |
0768dfce | 272 | static int |
64b636b9 | 273 | rfc1035NameUnpack(const char *buf, size_t sz, unsigned int *off, unsigned short *rdlength, char *name, size_t ns, int rdepth) |
fb29421b | 274 | { |
64b636b9 | 275 | unsigned int no = 0; |
fb29421b | 276 | unsigned char c; |
277 | size_t len; | |
278 | assert(ns > 0); | |
279 | do { | |
fd7b53a4 AJ |
280 | if ((*off) >= sz) { |
281 | RFC1035_UNPACK_DEBUG; | |
282 | return 1; | |
283 | } | |
26ac0430 AJ |
284 | c = *(buf + (*off)); |
285 | if (c > 191) { | |
64b636b9 | 286 | /* blasted compression */ |
287 | unsigned short s; | |
288 | unsigned int ptr; | |
289 | if (rdepth > 64) { /* infinite pointer loop */ | |
290 | RFC1035_UNPACK_DEBUG; | |
291 | return 1; | |
292 | } | |
26ac0430 AJ |
293 | memcpy(&s, buf + (*off), sizeof(s)); |
294 | s = ntohs(s); | |
295 | (*off) += sizeof(s); | |
296 | /* Sanity check */ | |
1285d970 | 297 | if ((*off) > sz) { |
298 | RFC1035_UNPACK_DEBUG; | |
26ac0430 | 299 | return 1; |
1285d970 | 300 | } |
26ac0430 AJ |
301 | ptr = s & 0x3FFF; |
302 | /* Make sure the pointer is inside this message */ | |
1285d970 | 303 | if (ptr >= sz) { |
304 | RFC1035_UNPACK_DEBUG; | |
26ac0430 | 305 | return 1; |
1285d970 | 306 | } |
26ac0430 AJ |
307 | return rfc1035NameUnpack(buf, sz, &ptr, rdlength, name + no, ns - no, rdepth + 1); |
308 | } else if (c > RFC1035_MAXLABELSZ) { | |
309 | /* | |
310 | * "(The 10 and 01 combinations are reserved for future use.)" | |
311 | */ | |
1285d970 | 312 | RFC1035_UNPACK_DEBUG; |
26ac0430 AJ |
313 | return 1; |
314 | } else { | |
315 | (*off)++; | |
316 | len = (size_t) c; | |
317 | if (len == 0) | |
318 | break; | |
319 | if (len > (ns - no - 1)) { /* label won't fit */ | |
64b636b9 | 320 | RFC1035_UNPACK_DEBUG; |
321 | return 1; | |
322 | } | |
26ac0430 | 323 | if ((*off) + len >= sz) { /* message is too short */ |
64b636b9 | 324 | RFC1035_UNPACK_DEBUG; |
325 | return 1; | |
326 | } | |
26ac0430 AJ |
327 | memcpy(name + no, buf + (*off), len); |
328 | (*off) += len; | |
329 | no += len; | |
330 | *(name + (no++)) = '.'; | |
331 | if (rdlength) | |
332 | *rdlength += len + 1; | |
333 | } | |
13c900c4 | 334 | } while (c > 0 && no < ns); |
4775d711 | 335 | if (no) |
26ac0430 | 336 | *(name + no - 1) = '\0'; |
4775d711 | 337 | else |
26ac0430 | 338 | *name = '\0'; |
0768dfce | 339 | /* make sure we didn't allow someone to overflow the name buffer */ |
fb29421b | 340 | assert(no <= ns); |
0768dfce | 341 | return 0; |
fb29421b | 342 | } |
343 | ||
344 | /* | |
345 | * rfc1035RRUnpack() | |
26ac0430 | 346 | * |
fb29421b | 347 | * Unpacks a RFC1035 Resource Record into 'RR' from a message buffer. |
348 | * The caller must free RR->rdata! | |
0768dfce | 349 | * |
350 | * Updates the new message buffer offset. | |
351 | * | |
352 | * Returns 0 (success) or 1 (error) | |
fb29421b | 353 | */ |
0768dfce | 354 | static int |
64b636b9 | 355 | rfc1035RRUnpack(const char *buf, size_t sz, unsigned int *off, rfc1035_rr * RR) |
fb29421b | 356 | { |
357 | unsigned short s; | |
358 | unsigned int i; | |
c837c655 | 359 | unsigned short rdlength; |
64b636b9 | 360 | unsigned int rdata_off; |
c837c655 | 361 | if (rfc1035NameUnpack(buf, sz, off, NULL, RR->name, RFC1035_MAXHOSTNAMESZ, 0)) { |
26ac0430 AJ |
362 | RFC1035_UNPACK_DEBUG; |
363 | memset(RR, '\0', sizeof(*RR)); | |
364 | return 1; | |
0768dfce | 365 | } |
366 | /* | |
367 | * Make sure the remaining message has enough octets for the | |
368 | * rest of the RR fields. | |
369 | */ | |
370 | if ((*off) + 10 > sz) { | |
26ac0430 AJ |
371 | RFC1035_UNPACK_DEBUG; |
372 | memset(RR, '\0', sizeof(*RR)); | |
373 | return 1; | |
0768dfce | 374 | } |
375 | memcpy(&s, buf + (*off), sizeof(s)); | |
376 | (*off) += sizeof(s); | |
fb29421b | 377 | RR->type = ntohs(s); |
0768dfce | 378 | memcpy(&s, buf + (*off), sizeof(s)); |
379 | (*off) += sizeof(s); | |
29b8d8d6 | 380 | RR->_class = ntohs(s); |
0768dfce | 381 | memcpy(&i, buf + (*off), sizeof(i)); |
382 | (*off) += sizeof(i); | |
fb29421b | 383 | RR->ttl = ntohl(i); |
0768dfce | 384 | memcpy(&s, buf + (*off), sizeof(s)); |
385 | (*off) += sizeof(s); | |
c837c655 | 386 | rdlength = ntohs(s); |
387 | if ((*off) + rdlength > sz) { | |
26ac0430 AJ |
388 | /* |
389 | * We got a truncated packet. 'dnscache' truncates UDP | |
390 | * replies at 512 octets, as per RFC 1035. | |
391 | */ | |
392 | RFC1035_UNPACK_DEBUG; | |
393 | memset(RR, '\0', sizeof(*RR)); | |
394 | return 1; | |
c415c128 | 395 | } |
c837c655 | 396 | RR->rdlength = rdlength; |
b8cbc836 | 397 | switch (RR->type) { |
3be4d5d1 | 398 | #if DNS_CNAME |
64b636b9 | 399 | case RFC1035_TYPE_CNAME: |
3be4d5d1 | 400 | #endif |
b8cbc836 | 401 | case RFC1035_TYPE_PTR: |
26ac0430 AJ |
402 | RR->rdata = (char*)xmalloc(RFC1035_MAXHOSTNAMESZ); |
403 | rdata_off = *off; | |
404 | RR->rdlength = 0; /* Filled in by rfc1035NameUnpack */ | |
405 | if (rfc1035NameUnpack(buf, sz, &rdata_off, &RR->rdlength, RR->rdata, RFC1035_MAXHOSTNAMESZ, 0)) { | |
64b636b9 | 406 | RFC1035_UNPACK_DEBUG; |
407 | return 1; | |
408 | } | |
26ac0430 AJ |
409 | if (rdata_off > ((*off) + rdlength)) { |
410 | /* | |
411 | * This probably doesn't happen for valid packets, but | |
412 | * I want to make sure that NameUnpack doesn't go beyond | |
413 | * the RDATA area. | |
414 | */ | |
415 | RFC1035_UNPACK_DEBUG; | |
416 | xfree(RR->rdata); | |
417 | memset(RR, '\0', sizeof(*RR)); | |
418 | return 1; | |
419 | } | |
420 | break; | |
b8cbc836 | 421 | case RFC1035_TYPE_A: |
422 | default: | |
26ac0430 AJ |
423 | RR->rdata = (char*)xmalloc(rdlength); |
424 | memcpy(RR->rdata, buf + (*off), rdlength); | |
425 | break; | |
b8cbc836 | 426 | } |
c837c655 | 427 | (*off) += rdlength; |
0768dfce | 428 | assert((*off) <= sz); |
429 | return 0; | |
fb29421b | 430 | } |
431 | ||
42687bb2 HN |
432 | const char * |
433 | rfc1035ErrorMessage(int n) | |
76cb2b26 | 434 | { |
42687bb2 | 435 | if (n < 0) |
d45671b8 | 436 | n = -n; |
42687bb2 | 437 | switch (n) { |
76cb2b26 | 438 | case 0: |
42687bb2 | 439 | return "No error condition"; |
26ac0430 | 440 | break; |
76cb2b26 | 441 | case 1: |
42687bb2 | 442 | return "Format Error: The name server was " |
d45671b8 | 443 | "unable to interpret the query."; |
26ac0430 | 444 | break; |
76cb2b26 | 445 | case 2: |
42687bb2 | 446 | return "Server Failure: The name server was " |
d45671b8 | 447 | "unable to process this query."; |
26ac0430 | 448 | break; |
76cb2b26 | 449 | case 3: |
42687bb2 | 450 | return "Name Error: The domain name does " |
d45671b8 | 451 | "not exist."; |
26ac0430 | 452 | break; |
76cb2b26 | 453 | case 4: |
42687bb2 | 454 | return "Not Implemented: The name server does " |
d45671b8 | 455 | "not support the requested kind of query."; |
26ac0430 | 456 | break; |
76cb2b26 | 457 | case 5: |
42687bb2 | 458 | return "Refused: The name server refuses to " |
d45671b8 | 459 | "perform the specified operation."; |
26ac0430 | 460 | break; |
0768dfce | 461 | case rfc1035_unpack_error: |
42687bb2 | 462 | return "The DNS reply message is corrupt or could " |
d45671b8 | 463 | "not be safely parsed."; |
26ac0430 | 464 | break; |
76cb2b26 | 465 | default: |
42687bb2 | 466 | return "Unknown Error"; |
26ac0430 | 467 | break; |
76cb2b26 | 468 | } |
469 | } | |
470 | ||
bae9832d | 471 | void |
cc192b50 | 472 | rfc1035RRDestroy(rfc1035_rr ** rr, int n) |
7b724b86 | 473 | { |
bae9832d | 474 | if (*rr == NULL || n < 1) { |
26ac0430 | 475 | return; |
bae9832d | 476 | } |
477 | ||
7b724b86 | 478 | while (n--) { |
26ac0430 AJ |
479 | if ((*rr)[n].rdata) |
480 | xfree((*rr)[n].rdata); | |
7b724b86 | 481 | } |
cc192b50 | 482 | xfree(*rr); |
483 | *rr = NULL; | |
7b724b86 | 484 | } |
485 | ||
0768dfce | 486 | /* |
ec7bade0 | 487 | * rfc1035QueryUnpack() |
26ac0430 | 488 | * |
ec7bade0 | 489 | * Unpacks a RFC1035 Query Record into 'query' from a message buffer. |
490 | * | |
491 | * Updates the new message buffer offset. | |
492 | * | |
493 | * Returns 0 (success) or 1 (error) | |
494 | */ | |
495 | static int | |
64b636b9 | 496 | rfc1035QueryUnpack(const char *buf, size_t sz, unsigned int *off, rfc1035_query * query) |
ec7bade0 | 497 | { |
498 | unsigned short s; | |
499 | if (rfc1035NameUnpack(buf, sz, off, NULL, query->name, RFC1035_MAXHOSTNAMESZ, 0)) { | |
26ac0430 AJ |
500 | RFC1035_UNPACK_DEBUG; |
501 | memset(query, '\0', sizeof(*query)); | |
502 | return 1; | |
ec7bade0 | 503 | } |
504 | if (*off + 4 > sz) { | |
26ac0430 AJ |
505 | RFC1035_UNPACK_DEBUG; |
506 | memset(query, '\0', sizeof(*query)); | |
507 | return 1; | |
ec7bade0 | 508 | } |
509 | memcpy(&s, buf + *off, 2); | |
510 | *off += 2; | |
ec7bade0 | 511 | query->qtype = ntohs(s); |
512 | memcpy(&s, buf + *off, 2); | |
513 | *off += 2; | |
9e1f210d | 514 | query->qclass = ntohs(s); |
ec7bade0 | 515 | return 0; |
516 | } | |
517 | ||
fc0f8140 | 518 | void |
cc192b50 | 519 | rfc1035MessageDestroy(rfc1035_message ** msg) |
ec7bade0 | 520 | { |
cc192b50 | 521 | if (!*msg) |
26ac0430 | 522 | return; |
cc192b50 | 523 | if ((*msg)->query) |
26ac0430 | 524 | xfree((*msg)->query); |
cc192b50 | 525 | if ((*msg)->answer) |
26ac0430 | 526 | rfc1035RRDestroy(&(*msg)->answer, (*msg)->ancount); |
cc192b50 | 527 | xfree(*msg); |
528 | *msg = NULL; | |
ec7bade0 | 529 | } |
530 | ||
9e1f210d | 531 | /* |
532 | * rfc1035QueryCompare() | |
26ac0430 | 533 | * |
9e1f210d | 534 | * Compares two rfc1035_query entries |
535 | * | |
536 | * Returns 0 (equal) or !=0 (different) | |
537 | */ | |
538 | int | |
539 | rfc1035QueryCompare(const rfc1035_query * a, const rfc1035_query * b) | |
540 | { | |
577090e4 | 541 | size_t la, lb; |
9e1f210d | 542 | if (a->qtype != b->qtype) |
26ac0430 | 543 | return 1; |
9e1f210d | 544 | if (a->qclass != b->qclass) |
26ac0430 | 545 | return 1; |
577090e4 | 546 | la = strlen(a->name); |
547 | lb = strlen(b->name); | |
548 | if (la != lb) { | |
26ac0430 AJ |
549 | /* Trim root label(s) */ |
550 | while (la > 0 && a->name[la - 1] == '.') | |
551 | la--; | |
552 | while (lb > 0 && b->name[lb - 1] == '.') | |
553 | lb--; | |
577090e4 | 554 | } |
555 | if (la != lb) | |
26ac0430 | 556 | return 1; |
577090e4 | 557 | |
558 | return strncasecmp(a->name, b->name, la); | |
9e1f210d | 559 | } |
560 | ||
ec7bade0 | 561 | /* |
562 | * rfc1035MessageUnpack() | |
0768dfce | 563 | * |
564 | * Takes the contents of a DNS reply and fills in an array | |
565 | * of resource record structures. The records array is allocated | |
566 | * here, and should be freed by calling rfc1035RRDestroy(). | |
567 | * | |
568 | * Returns number of records unpacked, zero if DNS reply indicates | |
569 | * zero answers, or an error number < 0. | |
570 | */ | |
571 | ||
fb29421b | 572 | int |
ec7bade0 | 573 | rfc1035MessageUnpack(const char *buf, |
26ac0430 AJ |
574 | size_t sz, |
575 | rfc1035_message ** answer) | |
fb29421b | 576 | { |
64b636b9 | 577 | unsigned int off = 0; |
a99dbd09 | 578 | unsigned int i, j; |
64b636b9 | 579 | unsigned int nr = 0; |
5457e603 | 580 | rfc1035_message *msg = NULL; |
581 | rfc1035_rr *recs = NULL; | |
582 | rfc1035_query *querys = NULL; | |
64b636b9 | 583 | msg = (rfc1035_message*)xcalloc(1, sizeof(*msg)); |
ec7bade0 | 584 | if (rfc1035HeaderUnpack(buf + off, sz - off, &off, msg)) { |
26ac0430 | 585 | RFC1035_UNPACK_DEBUG; |
26ac0430 AJ |
586 | xfree(msg); |
587 | return -rfc1035_unpack_error; | |
0768dfce | 588 | } |
64b636b9 | 589 | i = (unsigned int) msg->qdcount; |
ec7bade0 | 590 | if (i != 1) { |
26ac0430 AJ |
591 | /* This can not be an answer to our queries.. */ |
592 | RFC1035_UNPACK_DEBUG; | |
26ac0430 AJ |
593 | xfree(msg); |
594 | return -rfc1035_unpack_error; | |
ec7bade0 | 595 | } |
64b636b9 | 596 | querys = msg->query = (rfc1035_query*)xcalloc(i, sizeof(*querys)); |
a99dbd09 | 597 | for (j = 0; j < i; j++) { |
26ac0430 AJ |
598 | if (rfc1035QueryUnpack(buf, sz, &off, &querys[j])) { |
599 | RFC1035_UNPACK_DEBUG; | |
26ac0430 AJ |
600 | rfc1035MessageDestroy(&msg); |
601 | return -rfc1035_unpack_error; | |
602 | } | |
fb29421b | 603 | } |
ec7bade0 | 604 | *answer = msg; |
577090e4 | 605 | if (msg->rcode) { |
26ac0430 | 606 | RFC1035_UNPACK_DEBUG; |
42687bb2 | 607 | return -msg->rcode; |
577090e4 | 608 | } |
ec7bade0 | 609 | if (msg->ancount == 0) |
26ac0430 | 610 | return 0; |
64b636b9 | 611 | i = (unsigned int) msg->ancount; |
cc192b50 | 612 | recs = msg->answer = (rfc1035_rr*)xcalloc(i, sizeof(*recs)); |
a99dbd09 | 613 | for (j = 0; j < i; j++) { |
26ac0430 AJ |
614 | if (off >= sz) { /* corrupt packet */ |
615 | RFC1035_UNPACK_DEBUG; | |
616 | break; | |
617 | } | |
618 | if (rfc1035RRUnpack(buf, sz, &off, &recs[j])) { /* corrupt RR */ | |
619 | RFC1035_UNPACK_DEBUG; | |
620 | break; | |
621 | } | |
622 | nr++; | |
fb29421b | 623 | } |
0768dfce | 624 | if (nr == 0) { |
26ac0430 AJ |
625 | /* |
626 | * we expected to unpack some answers (ancount != 0), but | |
627 | * didn't actually get any. | |
628 | */ | |
629 | rfc1035MessageDestroy(&msg); | |
630 | *answer = NULL; | |
26ac0430 | 631 | return -rfc1035_unpack_error; |
0768dfce | 632 | } |
7b724b86 | 633 | return nr; |
fb29421b | 634 | } |
635 | ||
636 | /* | |
b8cbc836 | 637 | * rfc1035BuildAQuery() |
26ac0430 | 638 | * |
fb29421b | 639 | * Builds a message buffer with a QUESTION to lookup A records |
640 | * for a hostname. Caller must allocate 'buf' which should | |
641 | * probably be at least 512 octets. The 'szp' initially | |
642 | * specifies the size of the buffer, on return it contains | |
643 | * the size of the message (i.e. how much to write). | |
108d67a0 | 644 | * Returns the size of the query |
fb29421b | 645 | */ |
108d67a0 | 646 | ssize_t |
9e1f210d | 647 | rfc1035BuildAQuery(const char *hostname, char *buf, size_t sz, unsigned short qid, rfc1035_query * query) |
fb29421b | 648 | { |
ec7bade0 | 649 | static rfc1035_message h; |
2d72d4fd | 650 | size_t offset = 0; |
fb29421b | 651 | memset(&h, '\0', sizeof(h)); |
108d67a0 | 652 | h.id = qid; |
fb29421b | 653 | h.qr = 0; |
654 | h.rd = 1; | |
655 | h.opcode = 0; /* QUERY */ | |
656 | h.qdcount = (unsigned int) 1; | |
657 | offset += rfc1035HeaderPack(buf + offset, sz - offset, &h); | |
658 | offset += rfc1035QuestionPack(buf + offset, | |
26ac0430 AJ |
659 | sz - offset, |
660 | hostname, | |
661 | RFC1035_TYPE_A, | |
662 | RFC1035_CLASS_IN); | |
9e1f210d | 663 | if (query) { |
26ac0430 AJ |
664 | query->qtype = RFC1035_TYPE_A; |
665 | query->qclass = RFC1035_CLASS_IN; | |
666 | xstrncpy(query->name, hostname, sizeof(query->name)); | |
9e1f210d | 667 | } |
fb29421b | 668 | assert(offset <= sz); |
108d67a0 | 669 | return offset; |
fb29421b | 670 | } |
671 | ||
b8cbc836 | 672 | /* |
673 | * rfc1035BuildPTRQuery() | |
26ac0430 | 674 | * |
b8cbc836 | 675 | * Builds a message buffer with a QUESTION to lookup PTR records |
676 | * for an address. Caller must allocate 'buf' which should | |
677 | * probably be at least 512 octets. The 'szp' initially | |
678 | * specifies the size of the buffer, on return it contains | |
679 | * the size of the message (i.e. how much to write). | |
fc0f8140 | 680 | * Returns the size of the query |
b8cbc836 | 681 | */ |
108d67a0 | 682 | ssize_t |
64b636b9 | 683 | rfc1035BuildPTRQuery(const struct in_addr addr, char *buf, size_t sz, unsigned short qid, rfc1035_query * query) |
b8cbc836 | 684 | { |
ec7bade0 | 685 | static rfc1035_message h; |
2d72d4fd | 686 | size_t offset = 0; |
b8cbc836 | 687 | static char rev[32]; |
688 | unsigned int i; | |
689 | memset(&h, '\0', sizeof(h)); | |
d0017a72 | 690 | i = (unsigned int) ntohl(addr.s_addr); |
691 | snprintf(rev, 32, "%u.%u.%u.%u.in-addr.arpa.", | |
26ac0430 AJ |
692 | i & 255, |
693 | (i >> 8) & 255, | |
694 | (i >> 16) & 255, | |
695 | (i >> 24) & 255); | |
108d67a0 | 696 | h.id = qid; |
b8cbc836 | 697 | h.qr = 0; |
698 | h.rd = 1; | |
699 | h.opcode = 0; /* QUERY */ | |
700 | h.qdcount = (unsigned int) 1; | |
701 | offset += rfc1035HeaderPack(buf + offset, sz - offset, &h); | |
702 | offset += rfc1035QuestionPack(buf + offset, | |
26ac0430 AJ |
703 | sz - offset, |
704 | rev, | |
705 | RFC1035_TYPE_PTR, | |
706 | RFC1035_CLASS_IN); | |
9e1f210d | 707 | if (query) { |
26ac0430 AJ |
708 | query->qtype = RFC1035_TYPE_PTR; |
709 | query->qclass = RFC1035_CLASS_IN; | |
710 | xstrncpy(query->name, rev, sizeof(query->name)); | |
9e1f210d | 711 | } |
b8cbc836 | 712 | assert(offset <= sz); |
108d67a0 | 713 | return offset; |
b8cbc836 | 714 | } |
715 | ||
558be27a | 716 | /* |
717 | * We're going to retry a former query, but we | |
718 | * just need a new ID for it. Lucky for us ID | |
719 | * is the first field in the message buffer. | |
720 | */ | |
108d67a0 | 721 | void |
722 | rfc1035SetQueryID(char *buf, unsigned short qid) | |
558be27a | 723 | { |
558be27a | 724 | unsigned short s = htons(qid); |
725 | memcpy(buf, &s, sizeof(s)); | |
558be27a | 726 | } |
727 | ||
fb29421b | 728 | #if DRIVER |
b8cbc836 | 729 | #include <sys/socket.h> |
fb29421b | 730 | int |
731 | main(int argc, char *argv[]) | |
732 | { | |
fb29421b | 733 | char input[512]; |
734 | char buf[512]; | |
735 | char rbuf[512]; | |
736 | size_t sz = 512; | |
737 | unsigned short sid; | |
fb29421b | 738 | int s; |
739 | int rl; | |
740 | struct sockaddr_in S; | |
0768dfce | 741 | if (3 != argc) { |
26ac0430 AJ |
742 | fprintf(stderr, "usage: %s ip port\n", argv[0]); |
743 | return 1; | |
0768dfce | 744 | } |
fb29421b | 745 | setbuf(stdout, NULL); |
746 | setbuf(stderr, NULL); | |
747 | s = socket(PF_INET, SOCK_DGRAM, 0); | |
748 | if (s < 0) { | |
26ac0430 AJ |
749 | perror("socket"); |
750 | return 1; | |
fb29421b | 751 | } |
0768dfce | 752 | memset(&S, '\0', sizeof(S)); |
753 | S.sin_family = AF_INET; | |
754 | S.sin_port = htons(atoi(argv[2])); | |
755 | S.sin_addr.s_addr = inet_addr(argv[1]); | |
fb29421b | 756 | while (fgets(input, 512, stdin)) { |
26ac0430 AJ |
757 | struct in_addr junk; |
758 | strtok(input, "\r\n"); | |
759 | memset(buf, '\0', 512); | |
760 | sz = 512; | |
761 | if (inet_pton(AF_INET, input, &junk)) { | |
762 | sid = rfc1035BuildPTRQuery(junk, buf, &sz); | |
763 | } else { | |
764 | sid = rfc1035BuildAQuery(input, buf, &sz); | |
765 | } | |
766 | sendto(s, buf, sz, 0, (struct sockaddr *) &S, sizeof(S)); | |
767 | do { | |
768 | fd_set R; | |
769 | struct timeval to; | |
770 | FD_ZERO(&R); | |
771 | FD_SET(s, &R); | |
772 | to.tv_sec = 10; | |
773 | to.tv_usec = 0; | |
774 | rl = select(s + 1, &R, NULL, NULL, &to); | |
775 | } while (0); | |
776 | if (rl < 1) { | |
777 | printf("TIMEOUT\n"); | |
778 | continue; | |
779 | } | |
780 | memset(rbuf, '\0', 512); | |
781 | rl = recv(s, rbuf, 512, 0); | |
782 | { | |
783 | unsigned short rid = 0; | |
784 | int i; | |
785 | int n; | |
786 | rfc1035_rr *answers = NULL; | |
787 | n = rfc1035AnswersUnpack(rbuf, | |
788 | rl, | |
789 | &answers, | |
790 | &rid); | |
791 | if (n < 0) { | |
42687bb2 | 792 | printf("ERROR %d\n", -n); |
26ac0430 AJ |
793 | } else if (rid != sid) { |
794 | printf("ERROR, ID mismatch (%#hx, %#hx)\n", sid, rid); | |
795 | } else { | |
796 | printf("%d answers\n", n); | |
797 | for (i = 0; i < n; i++) { | |
798 | if (answers[i].type == RFC1035_TYPE_A) { | |
799 | struct in_addr a; | |
2f0b84f7 | 800 | char ipa_str[sizeof(a)]; |
26ac0430 | 801 | memcpy(&a, answers[i].rdata, 4); |
2f0b84f7 | 802 | printf("A\t%d\t%s\n", answers[i].ttl, inet_ntop(AF_INET,&a,tmp,sizeof(a))); |
26ac0430 AJ |
803 | } else if (answers[i].type == RFC1035_TYPE_PTR) { |
804 | char ptr[128]; | |
805 | strncpy(ptr, answers[i].rdata, answers[i].rdlength); | |
806 | printf("PTR\t%d\t%s\n", answers[i].ttl, ptr); | |
807 | } else { | |
808 | fprintf(stderr, "can't print answer type %d\n", | |
809 | (int) answers[i].type); | |
810 | } | |
811 | } | |
812 | } | |
813 | } | |
fb29421b | 814 | } |
815 | return 0; | |
816 | } | |
817 | #endif |