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