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