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