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