1 /* ==========================================================================
2 * dns.c - Recursive, Reentrant DNS Resolver.
3 * --------------------------------------------------------------------------
4 * Copyright (c) 2008, 2009, 2010 William Ahern
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sublicense, and/or sell copies of the Software, and to permit
11 * persons to whom the Software is furnished to do so, subject to the
12 * following conditions:
14 * The above copyright notice and this permission notice shall be included
15 * in all copies or substantial portions of the Software.
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
20 * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
21 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
22 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
23 * USE OR OTHER DEALINGS IN THE SOFTWARE.
24 * ==========================================================================
30 #if !defined(__FreeBSD__)
32 #define _XOPEN_SOURCE 600
38 #undef _DARWIN_C_SOURCE
39 #define _DARWIN_C_SOURCE
42 #define _NETBSD_SOURCE
45 #include <stddef.h> /* offsetof() */
46 #include <stdint.h> /* uint32_t */
47 #include <stdlib.h> /* malloc(3) realloc(3) free(3) rand(3) random(3) arc4random(3) */
48 #include <stdio.h> /* FILE fopen(3) fclose(3) getc(3) rewind(3) */
50 #include <string.h> /* memcpy(3) strlen(3) memmove(3) memchr(3) memcmp(3) strchr(3) strsep(3) strcspn(3) */
51 #include <strings.h> /* strcasecmp(3) strncasecmp(3) */
53 #include <ctype.h> /* isspace(3) isdigit(3) */
55 #include <time.h> /* time_t time(2) */
57 #include <signal.h> /* sig_atomic_t */
59 #include <errno.h> /* errno EINVAL ENOENT */
62 #include <assert.h> /* assert(3) */
68 #include <sys/types.h> /* FD_SETSIZE socklen_t */
69 #include <sys/select.h> /* FD_ZERO FD_SET fd_set select(2) */
70 #include <sys/socket.h> /* AF_INET AF_INET6 AF_UNIX struct sockaddr struct sockaddr_in struct sockaddr_in6 socket(2) */
73 #include <sys/un.h> /* struct sockaddr_un */
76 #include <fcntl.h> /* F_SETFD F_GETFL F_SETFL O_NONBLOCK fcntl(2) */
78 #include <unistd.h> /* gethostname(3) close(2) */
80 #include <poll.h> /* POLLIN POLLOUT */
82 #include <netinet/in.h> /* struct sockaddr_in struct sockaddr_in6 */
84 #include <arpa/inet.h> /* inet_pton(3) inet_ntop(3) htons(3) ntohs(3) */
86 #include <netdb.h> /* struct addrinfo */
93 * S T A N D A R D M A C R O S
95 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
98 #define MIN(a, b) (((a) < (b))? (a) : (b))
103 #define MAX(a, b) (((a) > (b))? (a) : (b))
108 #define lengthof(a) (sizeof (a) / sizeof (a)[0])
112 #define endof(a) (&(a)[lengthof((a))])
117 * D E B U G M A C R O S
119 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
126 #define DNS_DEBUG dns_debug
128 #define DNS_SAY_(fmt, ...) \
129 do { if (DNS_DEBUG > 0) fprintf(stderr, fmt "%.1s", __func__, __LINE__, __VA_ARGS__); } while (0)
130 #define DNS_SAY(...) DNS_SAY_("@@ (%s:%d) " __VA_ARGS__, "\n")
131 #define DNS_HAI DNS_SAY("HAI")
133 #define DNS_SHOW_(P, fmt, ...) do { \
134 if (DNS_DEBUG > 1) { \
135 fprintf(stderr, "@@ BEGIN * * * * * * * * * * * *\n"); \
136 fprintf(stderr, "@@ " fmt "%.0s\n", __VA_ARGS__); \
137 dns_p_dump((P), stderr); \
138 fprintf(stderr, "@@ END * * * * * * * * * * * * *\n\n"); \
142 #define DNS_SHOW(...) DNS_SHOW_(__VA_ARGS__, "")
144 #else /* !DNS_DEBUG */
151 #define DNS_SHOW(...)
153 #endif /* DNS_DEBUG */
157 * V E R S I O N R O U T I N E S
159 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
161 const char *dns_vendor(void) {
166 int dns_v_rel(void) {
171 int dns_v_abi(void) {
176 int dns_v_api(void) {
182 * E R R O R R O U T I N E S
184 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
188 #define DNS_EINTR WSAEINTR
189 #define DNS_EINPROGRESS WSAEINPROGRESS
190 #define DNS_EISCONN WSAEISCONN
191 #define DNS_EWOULDBLOCK WSAEWOULDBLOCK
192 #define DNS_EALREADY WSAEALREADY
193 #define DNS_EAGAIN EAGAIN
194 #define DNS_ETIMEDOUT WSAETIMEDOUT
196 #define dns_syerr() ((int)GetLastError())
197 #define dns_soerr() ((int)WSAGetLastError())
201 #define DNS_EINTR EINTR
202 #define DNS_EINPROGRESS EINPROGRESS
203 #define DNS_EISCONN EISCONN
204 #define DNS_EWOULDBLOCK EWOULDBLOCK
205 #define DNS_EALREADY EALREADY
206 #define DNS_EAGAIN EAGAIN
207 #define DNS_ETIMEDOUT ETIMEDOUT
209 #define dns_syerr() errno
210 #define dns_soerr() errno
215 const char *dns_strerror(int error) {
218 return "DNS packet buffer too small";
220 return "Illegal DNS RR name or data";
222 return "Attempt to push RR out of section order";
224 return "Invalid section specified";
226 return "Unknown DNS error";
228 return strerror(error);
230 } /* dns_strerror() */
234 * A T O M I C R O U T I N E S
236 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
238 static unsigned dns_atomic_inc(dns_atomic_t *i) {
240 } /* dns_atomic_inc() */
243 static unsigned dns_atomic_dec(dns_atomic_t *i) {
245 } /* dns_atomic_dec() */
249 * C R Y P T O R O U T I N E S
251 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
258 #if defined(HAVE_ARC4RANDOM) \
259 || defined(__OpenBSD__) \
260 || defined(__FreeBSD__) \
261 || defined(__NetBSD__) \
262 || defined(__APPLE__)
263 #define DNS_RANDOM arc4random
265 #define DNS_RANDOM random
267 #define DNS_RANDOM rand
271 #define DNS_RANDOM_arc4random 1
272 #define DNS_RANDOM_random 2
273 #define DNS_RANDOM_rand 3
274 #define DNS_RANDOM_RAND_bytes 4
276 #define DNS_RANDOM_OPENSSL (DNS_RANDOM_RAND_bytes == DNS_PP_XPASTE(DNS_RANDOM_, DNS_RANDOM))
278 #if DNS_RANDOM_OPENSSL
279 #include <openssl/rand.h>
282 static unsigned dns_random_(void) {
283 #if DNS_RANDOM_OPENSSL
286 assert(1 == RAND_bytes((unsigned char *)&r, sizeof r));
292 } /* dns_random_() */
294 unsigned (*dns_random)(void) __attribute__((weak)) = &dns_random_;
298 * P E R M U T A T I O N G E N E R A T O R
301 #define DNS_K_TEA_KEY_SIZE 16
302 #define DNS_K_TEA_BLOCK_SIZE 8
303 #define DNS_K_TEA_CYCLES 32
304 #define DNS_K_TEA_MAGIC 0x9E3779B9U
307 uint32_t key[DNS_K_TEA_KEY_SIZE / sizeof (uint32_t)];
309 }; /* struct dns_k_tea */
312 static void dns_k_tea_init(struct dns_k_tea *tea, uint32_t key[], unsigned cycles) {
313 memcpy(tea->key, key, sizeof tea->key);
315 tea->cycles = (cycles)? cycles : DNS_K_TEA_CYCLES;
316 } /* dns_k_tea_init() */
319 static void dns_k_tea_encrypt(struct dns_k_tea *tea, uint32_t v[], uint32_t *w) {
320 uint32_t y, z, sum, n;
326 for (n = 0; n < tea->cycles; n++) {
327 sum += DNS_K_TEA_MAGIC;
328 y += ((z << 4) + tea->key[0]) ^ (z + sum) ^ ((z >> 5) + tea->key[1]);
329 z += ((y << 4) + tea->key[2]) ^ (y + sum) ^ ((y >> 5) + tea->key[3]);
336 } /* dns_k_tea_encrypt() */
340 * Permutation generator, based on a Luby-Rackoff Feistel construction.
342 * Specifically, this is a generic balanced Feistel block cipher using TEA
343 * (another block cipher) as the pseudo-random function, F. At best it's as
344 * strong as F (TEA), notwithstanding the seeding. F could be AES, SHA-1, or
345 * perhaps Bernstein's Salsa20 core; I am naively trying to keep things
348 * The generator can create a permutation of any set of numbers, as long as
349 * the size of the set is an even power of 2. This limitation arises either
350 * out of an inherent property of balanced Feistel constructions, or by my
351 * own ignorance. I'll tackle an unbalanced construction after I wrap my
352 * head around Schneier and Kelsey's paper.
354 * CAVEAT EMPTOR. IANAC.
356 #define DNS_K_PERMUTOR_ROUNDS 8
358 struct dns_k_permutor {
359 unsigned stepi, length, limit;
360 unsigned shift, mask, rounds;
362 struct dns_k_tea tea;
363 }; /* struct dns_k_permutor */
366 static inline unsigned dns_k_permutor_powof(unsigned n) {
369 for (m = 1; m < n; m <<= 1, i++)
373 } /* dns_k_permutor_powof() */
375 static void dns_k_permutor_init(struct dns_k_permutor *p, unsigned low, unsigned high) {
376 uint32_t key[DNS_K_TEA_KEY_SIZE / sizeof (uint32_t)];
381 p->length = (high - low) + 1;
384 width = dns_k_permutor_powof(p->length);
387 p->shift = width / 2;
388 p->mask = (1U << p->shift) - 1;
389 p->rounds = DNS_K_PERMUTOR_ROUNDS;
391 for (i = 0; i < lengthof(key); i++)
392 key[i] = dns_random();
394 dns_k_tea_init(&p->tea, key, 0);
397 } /* dns_k_permutor_init() */
400 static unsigned dns_k_permutor_F(struct dns_k_permutor *p, unsigned k, unsigned x) {
401 uint32_t in[DNS_K_TEA_BLOCK_SIZE / sizeof (uint32_t)], out[DNS_K_TEA_BLOCK_SIZE / sizeof (uint32_t)];
403 memset(in, '\0', sizeof in);
408 dns_k_tea_encrypt(&p->tea, in, out);
410 return p->mask & out[0];
411 } /* dns_k_permutor_F() */
414 static unsigned dns_k_permutor_E(struct dns_k_permutor *p, unsigned n) {
419 l[i] = p->mask & (n >> p->shift);
420 r[i] = p->mask & (n >> 0);
423 l[(i + 1) % 2] = r[i % 2];
424 r[(i + 1) % 2] = l[i % 2] ^ dns_k_permutor_F(p, i, r[i % 2]);
427 } while (i < p->rounds - 1);
429 return ((l[i % 2] & p->mask) << p->shift) | ((r[i % 2] & p->mask) << 0);
430 } /* dns_k_permutor_E() */
432 static unsigned dns_k_permutor_step(struct dns_k_permutor *p) {
436 n = dns_k_permutor_E(p, p->stepi++);
437 } while (n >= p->length);
439 return n + (p->limit + 1 - p->length);
440 } /* dns_k_permutor_step() */
444 * Simple permutation box. Useful for shuffling rrsets from an iterator.
445 * Uses AES s-box to provide good diffusion.
447 * Seems to pass muster under runs test.
449 * $ for i in 0 1 2 3 4 5 6 7 8 9; do ./dns shuffle-16 > /tmp/out; done
450 * $ R -q -f /dev/stdin 2>/dev/null <<-EOF | awk '/p-value/{ print $8 }'
452 * runs.test(scan(file="/tmp/out"))
455 static unsigned short dns_k_shuffle16(unsigned short n, unsigned s) {
456 static const unsigned char sbox[256] =
457 { 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5,
458 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
459 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0,
460 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
461 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc,
462 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
463 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a,
464 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
465 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0,
466 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
467 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b,
468 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
469 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85,
470 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
471 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5,
472 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
473 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17,
474 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
475 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88,
476 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
477 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c,
478 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
479 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9,
480 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
481 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6,
482 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
483 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e,
484 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
485 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94,
486 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
487 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68,
488 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 };
495 for (i = 0; i < 4; i++) {
502 return ((0xff00 & (a << 8)) | (0x00ff & (b << 0)));
503 } /* dns_k_shuffle16() */
507 * U T I L I T Y R O U T I N E S
509 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
515 static time_t dns_now(void) {
516 /* XXX: Assumes sizeof (time_t) <= sizeof (sig_atomic_t) */
517 static volatile sig_atomic_t last, tick;
518 volatile sig_atomic_t tmp_last, tmp_tick;
525 if (now > tmp_last) {
527 tmp_tick += now - tmp_last;
536 static time_t dns_elapsed(time_t from) {
537 time_t now = dns_now();
539 return (now > from)? now - from : 0;
540 } /* dns_elpased() */
543 static size_t dns_af_len(int af) {
544 static const size_t table[AF_MAX] = {
545 [AF_INET6] = sizeof (struct sockaddr_in6),
546 [AF_INET] = sizeof (struct sockaddr_in),
547 #if defined(AF_UNIX) && !defined(_WIN32)
548 [AF_UNIX] = sizeof (struct sockaddr_un),
555 #define dns_sa_len(sa) dns_af_len(dns_sa_family(sa))
558 #define DNS_SA_NOPORT &dns_sa_noport
559 static unsigned short dns_sa_noport;
561 unsigned short *dns_sa_port(int af, void *sa) {
564 return &((struct sockaddr_in6 *)sa)->sin6_port;
566 return &((struct sockaddr_in *)sa)->sin_port;
568 return DNS_SA_NOPORT;
570 } /* dns_sa_port() */
573 void *dns_sa_addr(int af, void *sa) {
576 return &((struct sockaddr_in6 *)sa)->sin6_addr;
578 return &((struct sockaddr_in *)sa)->sin_addr;
582 } /* dns_sa_addr() */
586 static int dns_inet_pton(int af, const void *src, void *dst) {
587 union { struct sockaddr_in sin; struct sockaddr_in6 sin6; } u;
589 u.sin.sin_family = af;
591 if (0 != WSAStringToAddressA((void *)src, af, (void *)0, (struct sockaddr *)&u, &(int){ sizeof u }))
596 *(struct in6_addr *)dst = u.sin6.sin6_addr;
600 *(struct in_addr *)dst = u.sin.sin_addr;
606 } /* dns_inet_pton() */
608 const char *dns_inet_ntop(int af, const void *src, void *dst, unsigned long lim) {
609 union { struct sockaddr_in sin; struct sockaddr_in6 sin6; } u;
611 /* NOTE: WSAAddressToString will print .sin_port unless zeroed. */
612 memset(&u, 0, sizeof u);
614 u.sin.sin_family = af;
618 u.sin6.sin6_addr = *(struct in6_addr *)src;
621 u.sin.sin_addr = *(struct in_addr *)src;
628 if (0 != WSAAddressToStringA((struct sockaddr *)&u, dns_sa_len(&u), (void *)0, dst, &lim))
632 } /* dns_inet_ntop() */
636 size_t dns_strlcpy(char *dst, const char *src, size_t lim) {
643 if ('\0' == (*d++ = *s++))
654 } /* dns_strlcpy() */
657 size_t dns_strlcat(char *dst, const char *src, size_t lim) {
658 char *d = memchr(dst, '\0', lim);
665 if ('\0' == (*d++ = *s++))
677 return lim + (s - p - 1);
678 } /* dns_strlcat() */
683 static char *dns_strsep(char **sp, const char *delim) {
689 *sp += strcspn(p, delim);
701 #define dns_strsep(...) strsep(__VA_ARGS__)
706 #define strcasecmp(...) _stricmp(__VA_ARGS__)
707 #define strncasecmp(...) _strnicmp(__VA_ARGS__)
711 static int dns_poll(int fd, short events, int timeout) {
717 assert(fd >= 0 && fd < FD_SETSIZE);
722 if (events & DNS_POLLIN)
725 if (events & DNS_POLLOUT)
728 select(fd + 1, &rset, &wset, 0, (timeout >= 0)? &(struct timeval){ timeout, 0 } : NULL);
735 * P A C K E T R O U T I N E S
737 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
739 unsigned dns_p_count(struct dns_packet *P, enum dns_section section) {
744 return ntohs(dns_header(P)->qdcount);
746 return ntohs(dns_header(P)->ancount);
748 return ntohs(dns_header(P)->nscount);
750 return ntohs(dns_header(P)->arcount);
754 if (section & DNS_S_QD)
755 count += ntohs(dns_header(P)->qdcount);
756 if (section & DNS_S_AN)
757 count += ntohs(dns_header(P)->ancount);
758 if (section & DNS_S_NS)
759 count += ntohs(dns_header(P)->nscount);
760 if (section & DNS_S_AR)
761 count += ntohs(dns_header(P)->arcount);
765 } /* dns_p_count() */
768 struct dns_packet *dns_p_init(struct dns_packet *P, size_t size) {
772 assert(size >= offsetof(struct dns_packet, data) + 12);
774 memset(P, 0, sizeof *P);
775 P->size = size - offsetof(struct dns_packet, data);
778 memset(P->data, '\0', 12);
784 static unsigned short dns_p_qend(struct dns_packet *P) {
785 unsigned short qend = 12;
786 unsigned i, count = dns_p_count(P, DNS_S_QD);
788 for (i = 0; i < count && qend < P->end; i++) {
789 if (P->end == (qend = dns_d_skip(qend, P)))
792 if (P->end - qend < 4)
798 return MIN(qend, P->end);
804 struct dns_packet *dns_p_make(size_t len, int *error) {
805 struct dns_packet *P;
806 size_t size = dns_p_calcsize(len);
808 if (!(P = dns_p_init(malloc(size), size)))
809 *error = dns_syerr();
815 int dns_p_grow(struct dns_packet **P) {
816 struct dns_packet *tmp;
821 if (!(*P = dns_p_make(DNS_P_QBUFSIZ, &error)))
827 size = dns_p_sizeof(*P);
837 if (!(tmp = realloc(*P, dns_p_calcsize(size))))
847 struct dns_packet *dns_p_copy(struct dns_packet *P, const struct dns_packet *P0) {
851 P->end = MIN(P->size, P0->end);
853 memcpy(P->data, P0->data, P->end);
859 struct dns_packet *dns_p_merge(struct dns_packet *A, enum dns_section Amask, struct dns_packet *B, enum dns_section Bmask, int *error_) {
860 size_t bufsiz = MIN(65535, ((A)? A->end : 0) + ((B)? B->end : 0));
861 struct dns_packet *M;
862 enum dns_section section;
863 struct dns_rr rr, mr;
873 if (!(M = dns_p_make(bufsiz, &error)))
876 for (section = DNS_S_QD; (DNS_S_ALL & section); section <<= 1) {
877 if (A && (section & Amask)) {
878 dns_rr_foreach(&rr, A, .section = section) {
879 if ((error = dns_rr_copy(M, &rr, A)))
884 if (B && (section & Bmask)) {
885 dns_rr_foreach(&rr, B, .section = section) {
888 dns_rr_foreach(&mr, M, .type = rr.type, .section = DNS_S_ALL) {
889 if (!(copy = dns_rr_cmp(&rr, B, &mr, M)))
893 if (copy && (error = dns_rr_copy(M, &rr, B)))
903 if (error == DNS_ENOBUFS && bufsiz < 65535) {
904 bufsiz = MIN(65535, bufsiz * 2);
912 } /* dns_p_merge() */
915 static unsigned short dns_l_skip(unsigned short, const unsigned char *, size_t);
917 void dns_p_dictadd(struct dns_packet *P, unsigned short dn) {
918 unsigned short lp, lptr, i;
922 while (lp < P->end) {
923 if (0xc0 == (0xc0 & P->data[lp]) && P->end - lp >= 2 && lp != dn) {
924 lptr = ((0x3f & P->data[lp + 0]) << 8)
925 | ((0xff & P->data[lp + 1]) << 0);
927 for (i = 0; i < lengthof(P->dict) && P->dict[i]; i++) {
928 if (P->dict[i] == lptr) {
936 lp = dns_l_skip(lp, P->data, P->end);
939 for (i = 0; i < lengthof(P->dict); i++) {
946 } /* dns_p_dictadd() */
949 int dns_p_push(struct dns_packet *P, enum dns_section section, const void *dn, size_t dnlen, enum dns_type type, enum dns_class class, unsigned ttl, const void *any) {
953 if ((error = dns_d_push(P, dn, dnlen)))
956 if (P->size - P->end < 4)
959 P->data[P->end++] = 0xff & (type >> 8);
960 P->data[P->end++] = 0xff & (type >> 0);
962 P->data[P->end++] = 0xff & (class >> 8);
963 P->data[P->end++] = 0xff & (class >> 0);
965 if (section == DNS_S_QD)
968 if (P->size - P->end < 6)
971 P->data[P->end++] = 0x7f & (ttl >> 24);
972 P->data[P->end++] = 0xff & (ttl >> 16);
973 P->data[P->end++] = 0xff & (ttl >> 8);
974 P->data[P->end++] = 0xff & (ttl >> 0);
976 if ((error = dns_any_push(P, (union dns_any *)any, type)))
982 if (dns_p_count(P, DNS_S_AN|DNS_S_NS|DNS_S_AR))
985 if (!P->qd.base && (error = dns_p_study(P)))
988 dns_header(P)->qdcount = htons(ntohs(dns_header(P)->qdcount) + 1);
1000 if (dns_p_count(P, DNS_S_NS|DNS_S_AR))
1003 if (!P->an.base && (error = dns_p_study(P)))
1006 dns_header(P)->ancount = htons(ntohs(dns_header(P)->ancount) + 1);
1009 P->ns.base = P->end;
1011 P->ar.base = P->end;
1016 if (dns_p_count(P, DNS_S_AR))
1019 if (!P->ns.base && (error = dns_p_study(P)))
1022 dns_header(P)->nscount = htons(ntohs(dns_header(P)->nscount) + 1);
1025 P->ar.base = P->end;
1030 if (!P->ar.base && (error = dns_p_study(P)))
1033 dns_header(P)->arcount = htons(ntohs(dns_header(P)->arcount) + 1);
1039 error = DNS_ESECTION;
1046 error = DNS_ENOBUFS;
1057 } /* dns_p_push() */
1060 static void dns_p_dump3(struct dns_packet *P, struct dns_rr_i *I, FILE *fp) {
1061 enum dns_section section;
1065 char pretty[sizeof any * 2];
1068 fputs(";; [HEADER]\n", fp);
1069 fprintf(fp, ";; qr : %s(%d)\n", (dns_header(P)->qr)? "RESPONSE" : "QUERY", dns_header(P)->qr);
1070 fprintf(fp, ";; opcode : %s(%d)\n", dns_stropcode(dns_header(P)->opcode), dns_header(P)->opcode);
1071 fprintf(fp, ";; aa : %s(%d)\n", (dns_header(P)->aa)? "AUTHORITATIVE" : "NON-AUTHORITATIVE", dns_header(P)->aa);
1072 fprintf(fp, ";; tc : %s(%d)\n", (dns_header(P)->tc)? "TRUNCATED" : "NOT-TRUNCATED", dns_header(P)->tc);
1073 fprintf(fp, ";; rd : %s(%d)\n", (dns_header(P)->rd)? "RECURSION-DESIRED" : "RECURSION-NOT-DESIRED", dns_header(P)->rd);
1074 fprintf(fp, ";; ra : %s(%d)\n", (dns_header(P)->ra)? "RECURSION-ALLOWED" : "RECURSION-NOT-ALLOWED", dns_header(P)->ra);
1075 fprintf(fp, ";; rcode : %s(%d)\n", dns_strrcode(dns_header(P)->rcode), dns_header(P)->rcode);
1079 while (dns_rr_grep(&rr, 1, I, P, &error)) {
1080 if (section != rr.section)
1081 fprintf(fp, "\n;; [%s:%d]\n", dns_strsection(rr.section), dns_p_count(P, rr.section));
1083 if ((len = dns_rr_print(pretty, sizeof pretty, &rr, P, &error)))
1084 fprintf(fp, "%s\n", pretty);
1086 section = rr.section;
1088 } /* dns_p_dump3() */
1091 void dns_p_dump(struct dns_packet *P, FILE *fp) {
1092 dns_p_dump3(P, dns_rr_i_new(P, .section = 0), fp);
1093 } /* dns_p_dump() */
1096 static void dns_s_unstudy(struct dns_s_memo *m)
1097 { m->base = 0; m->end = 0; }
1099 static void dns_p_unstudy(struct dns_packet *P) {
1100 dns_s_unstudy(&P->qd);
1101 dns_s_unstudy(&P->an);
1102 dns_s_unstudy(&P->ns);
1103 dns_s_unstudy(&P->ar);
1104 } /* dns_p_unstudy() */
1106 static int dns_s_study(struct dns_s_memo *m, enum dns_section section, unsigned base, struct dns_packet *P) {
1107 unsigned short count, rp;
1109 count = dns_p_count(P, section);
1111 for (rp = base; count && rp < P->end; count--)
1112 rp = dns_rr_skip(rp, P);
1118 } /* dns_s_study() */
1120 int dns_p_study(struct dns_packet *P) {
1123 if ((error = dns_s_study(&P->qd, DNS_S_QD, 12, P)))
1126 if ((error = dns_s_study(&P->an, DNS_S_AN, P->qd.end, P)))
1129 if ((error = dns_s_study(&P->ns, DNS_S_NS, P->an.end, P)))
1132 if ((error = dns_s_study(&P->ar, DNS_S_AR, P->ns.end, P)))
1140 } /* dns_p_study() */
1144 * D O M A I N N A M E R O U T I N E S
1146 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1148 #ifndef DNS_D_MAXPTRS
1149 #define DNS_D_MAXPTRS 127 /* Arbitrary; possible, valid depth is something like packet size / 2 + fudge. */
1152 static size_t dns_l_expand(unsigned char *dst, size_t lim, unsigned short src, unsigned short *nxt, const unsigned char *data, size_t end) {
1160 switch (0x03 & (data[src] >> 6)) {
1162 len = (0x3f & (data[src++]));
1164 if (end - src < len)
1168 memcpy(dst, &data[src], MIN(lim, len));
1170 dst[MIN(lim - 1, len)] = '\0';
1181 if (++nptrs > DNS_D_MAXPTRS)
1187 src = ((0x3f & data[src + 0]) << 8)
1188 | ((0xff & data[src + 1]) << 0);
1198 } /* dns_l_expand() */
1201 static unsigned short dns_l_skip(unsigned short src, const unsigned char *data, size_t end) {
1207 switch (0x03 & (data[src] >> 6)) {
1209 len = (0x3f & (data[src++]));
1211 if (end - src < len)
1214 return (len)? src + len : end;
1226 } /* dns_l_skip() */
1229 size_t dns_d_trim(void *dst_, size_t lim, const void *src_, size_t len, int flags) {
1230 unsigned char *dst = dst_;
1231 const unsigned char *src = src_;
1232 size_t dp = 0, sp = 0;
1235 /* trim any leading dot(s) */
1236 while (sp < len && src[sp] == '.')
1239 for (lc = 0; sp < len; lc = src[sp]) {
1246 /* trim extra dot(s) */
1247 while (sp < len && src[sp] == '.')
1251 if ((flags & DNS_D_ANCHOR) && lc != '.') {
1259 dst[MIN(dp, lim - 1)] = '\0';
1262 } /* dns_d_trim() */
1265 char *dns_d_init(void *dst, size_t lim, const void *src, size_t len, int flags) {
1266 if (flags & DNS_D_TRIM) {
1267 dns_d_trim(dst, lim, src, len, flags);
1268 } if (flags & DNS_D_ANCHOR) {
1269 dns_d_anchor(dst, lim, src, len);
1271 memmove(dst, src, MIN(lim, len));
1274 ((char *)dst)[MIN(len, lim - 1)] = '\0';
1278 } /* dns_d_init() */
1281 size_t dns_d_anchor(void *dst, size_t lim, const void *src, size_t len) {
1285 memmove(dst, src, MIN(lim, len));
1287 if (((const char *)src)[len - 1] != '.') {
1289 ((char *)dst)[len] = '.';
1294 ((char *)dst)[MIN(lim - 1, len)] = '\0';
1297 } /* dns_d_anchor() */
1300 size_t dns_d_cleave(void *dst, size_t lim, const void *src, size_t len) {
1303 /* XXX: Skip any leading dot. Handles cleaving root ".". */
1304 if (len == 0 || !(dot = memchr((const char *)src + 1, '.', len - 1)))
1307 len -= dot - (const char *)src;
1309 /* XXX: Unless root, skip the label's trailing dot. */
1316 memmove(dst, src, MIN(lim, len));
1319 ((char *)dst)[MIN(lim - 1, len)] = '\0';
1322 } /* dns_d_cleave() */
1325 size_t dns_d_comp(void *dst_, size_t lim, const void *src_, size_t len, struct dns_packet *P, int *error __UNUSED__) {
1326 struct { unsigned char *b; size_t p, x; } dst, src;
1327 unsigned char ch = '.';
1333 src.b = (unsigned char *)src_;
1337 while (src.x < len) {
1342 dst.b[dst.p] = (0x3f & (src.x - src.p));
1355 if (src.x > src.p) {
1357 dst.b[dst.p] = (0x3f & (src.x - src.p));
1364 dst.b[dst.p] = 0x00;
1371 struct { unsigned char label[DNS_D_MAXLABEL + 1]; size_t len; unsigned short p, x, y; } a, b;
1376 while ((a.len = dns_l_expand(a.label, sizeof a.label, a.p, &a.x, dst.b, lim))) {
1377 for (i = 0; i < lengthof(P->dict) && P->dict[i]; i++) {
1380 while ((b.len = dns_l_expand(b.label, sizeof b.label, b.p, &b.x, P->data, P->end))) {
1384 while (a.len && b.len && 0 == strcasecmp((char *)a.label, (char *)b.label)) {
1385 a.len = dns_l_expand(a.label, sizeof a.label, a.y, &a.y, dst.b, lim);
1386 b.len = dns_l_expand(b.label, sizeof b.label, b.y, &b.y, P->data, P->end);
1389 if (a.len == 0 && b.len == 0 && b.p <= 0x3fff) {
1391 | (0x3f & (b.p >> 8));
1392 dst.b[a.p++] = (0xff & (b.p >> 0));
1407 } /* dns_d_comp() */
1410 unsigned short dns_d_skip(unsigned short src, struct dns_packet *P) {
1413 while (src < P->end) {
1414 switch (0x03 & (P->data[src] >> 6)) {
1415 case 0x00: /* FOLLOWS */
1416 len = (0x3f & P->data[src++]);
1419 /* success ==> */ return src;
1420 } else if (P->end - src > len) {
1428 case 0x01: /* RESERVED */
1430 case 0x02: /* RESERVED */
1432 case 0x03: /* POINTER */
1433 if (P->end - src < 2)
1438 /* success ==> */ return src;
1445 } /* dns_d_skip() */
1450 size_t dns_d_expand(void *dst, size_t lim, unsigned short src, struct dns_packet *P, int *error) {
1455 while (src < P->end) {
1456 switch ((0x03 & (P->data[src] >> 6))) {
1457 case 0x00: /* FOLLOWS */
1458 len = (0x3f & P->data[src]);
1463 ((unsigned char *)dst)[dstp] = '.';
1470 ((unsigned char *)dst)[MIN(dstp, lim - 1)] = '\0';
1472 /* success ==> */ return dstp;
1477 if (P->end - src < len)
1481 memcpy(&((unsigned char *)dst)[dstp], &P->data[src], MIN(len, lim - dstp));
1487 ((unsigned char *)dst)[dstp] = '.';
1494 case 0x01: /* RESERVED */
1496 case 0x02: /* RESERVED */
1498 case 0x03: /* POINTER */
1499 if (++nptrs > DNS_D_MAXPTRS)
1502 if (P->end - src < 2)
1505 src = ((0x3f & P->data[src + 0]) << 8)
1506 | ((0xff & P->data[src + 1]) << 0);
1513 *error = DNS_EILLEGAL;
1516 ((unsigned char *)dst)[MIN(dstp, lim - 1)] = '\0';
1520 *error = DNS_EILLEGAL;
1523 ((unsigned char *)dst)[MIN(dstp, lim - 1)] = '\0';
1526 } /* dns_d_expand() */
1529 int dns_d_push(struct dns_packet *P, const void *dn, size_t len) {
1530 size_t lim = P->size - P->end;
1531 unsigned dp = P->end;
1534 len = dns_d_comp(&P->data[dp], lim, dn, len, P, &error);
1543 dns_p_dictadd(P, dp);
1546 } /* dns_d_push() */
1549 size_t dns_d_cname(void *dst, size_t lim, const void *dn, size_t len, struct dns_packet *P, int *error_) {
1550 char host[DNS_D_MAXNAME + 1];
1556 if (sizeof host <= dns_d_anchor(host, sizeof host, dn, len))
1557 { error = ENAMETOOLONG; goto error; }
1559 for (depth = 0; depth < 7; depth++) {
1560 dns_rr_i_init(memset(&i, 0, sizeof i), P);
1562 i.section = DNS_S_ALL & ~DNS_S_QD;
1564 i.type = DNS_T_CNAME;
1566 if (!dns_rr_grep(&rr, 1, &i, P, &error))
1569 if ((error = dns_cname_parse((struct dns_cname *)host, &rr, P)))
1573 return dns_strlcpy(dst, host, lim);
1578 } /* dns_d_cname() */
1582 * R E S O U R C E R E C O R D R O U T I N E S
1584 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1586 int dns_rr_copy(struct dns_packet *P, struct dns_rr *rr, struct dns_packet *Q) {
1587 unsigned char dn[DNS_D_MAXNAME + 1];
1592 if (!(len = dns_d_expand(dn, sizeof dn, rr->dn.p, Q, &error)))
1594 else if (len >= sizeof dn)
1595 return DNS_EILLEGAL;
1597 if (rr->section != DNS_S_QD && (error = dns_any_parse(dns_any_init(&any, sizeof any), rr, Q)))
1600 return dns_p_push(P, rr->section, dn, len, rr->type, rr->class, rr->ttl, &any);
1601 } /* dns_rr_copy() */
1604 int dns_rr_parse(struct dns_rr *rr, unsigned short src, struct dns_packet *P) {
1605 unsigned short p = src;
1611 rr->dn.len = (p = dns_d_skip(p, P)) - rr->dn.p;
1616 rr->type = ((0xff & P->data[p + 0]) << 8)
1617 | ((0xff & P->data[p + 1]) << 0);
1619 rr->class = ((0xff & P->data[p + 2]) << 8)
1620 | ((0xff & P->data[p + 3]) << 0);
1624 if (src < dns_p_qend(P)) {
1625 rr->section = DNS_S_QUESTION;
1637 rr->ttl = ((0x7f & P->data[p + 0]) << 24)
1638 | ((0xff & P->data[p + 1]) << 16)
1639 | ((0xff & P->data[p + 2]) << 8)
1640 | ((0xff & P->data[p + 3]) << 0);
1647 rr->rd.len = ((0xff & P->data[p + 0]) << 8)
1648 | ((0xff & P->data[p + 1]) << 0);
1653 if (P->end - p < rr->rd.len)
1659 return DNS_EILLEGAL;
1660 } /* dns_rr_parse() */
1663 static unsigned short dns_rr_len(const unsigned short src, struct dns_packet *P) {
1664 unsigned short rp, rdlen;
1666 rp = dns_d_skip(src, P);
1668 if (P->end - rp < 4)
1669 return P->end - src;
1671 rp += 4; /* TYPE, CLASS */
1673 if (rp <= dns_p_qend(P))
1676 if (P->end - rp < 6)
1677 return P->end - src;
1679 rp += 6; /* TTL, RDLEN */
1681 rdlen = ((0xff & P->data[rp - 2]) << 8)
1682 | ((0xff & P->data[rp - 1]) << 0);
1684 if (P->end - rp < rdlen)
1685 return P->end - src;
1690 } /* dns_rr_len() */
1693 unsigned short dns_rr_skip(unsigned short src, struct dns_packet *P) {
1694 return src + dns_rr_len(src, P);
1695 } /* dns_rr_skip() */
1698 static enum dns_section dns_rr_section(unsigned short src, struct dns_packet *P) {
1699 enum dns_section section;
1700 unsigned count, ind;
1703 if (src >= P->qd.base && src < P->qd.end)
1705 if (src >= P->an.base && src < P->an.end)
1707 if (src >= P->ns.base && src < P->ns.end)
1709 if (src >= P->ar.base && src < P->ar.end)
1712 /* NOTE: Possibly bad memoization. Try it the hard-way. */
1714 for (rp = 12, ind = 0; rp < src && rp < P->end; ind++)
1715 rp = dns_rr_skip(rp, P);
1718 count = dns_p_count(P, section);
1720 while (ind >= count && section <= DNS_S_AR) {
1722 count += dns_p_count(P, section);
1725 return DNS_S_ALL & section;
1726 } /* dns_rr_section() */
1729 static enum dns_type dns_rr_type(unsigned short src, struct dns_packet *P) {
1733 if ((error = dns_rr_parse(&rr, src, P)))
1737 } /* dns_rr_type() */
1740 int dns_rr_cmp(struct dns_rr *r0, struct dns_packet *P0, struct dns_rr *r1, struct dns_packet *P1) {
1741 char host0[DNS_D_MAXNAME + 1], host1[DNS_D_MAXNAME + 1];
1742 union dns_any any0, any1;
1746 if ((cmp = r0->type - r1->type))
1749 if ((cmp = r0->class - r1->class))
1753 * FIXME: Do label-by-label comparison to handle illegally long names?
1756 if (!(len = dns_d_expand(host0, sizeof host0, r0->dn.p, P0, &error))
1757 || len >= sizeof host0)
1760 if (!(len = dns_d_expand(host1, sizeof host1, r1->dn.p, P1, &error))
1761 || len >= sizeof host1)
1764 if ((cmp = strcasecmp(host0, host1)))
1767 if (DNS_S_QD & (r0->section | r1->section)) {
1768 if (r0->section == r1->section)
1771 return (r0->section == DNS_S_QD)? -1 : 1;
1774 if ((error = dns_any_parse(&any0, r0, P0)))
1777 if ((error = dns_any_parse(&any1, r1, P1)))
1780 return dns_any_cmp(&any0, r0->type, &any1, r1->type);
1781 } /* dns_rr_cmp() */
1784 static _Bool dns_rr_exists(struct dns_rr *rr0, struct dns_packet *P0, struct dns_packet *P1) {
1787 dns_rr_foreach(&rr1, P1, .section = rr0->section, .type = rr0->type) {
1788 if (0 == dns_rr_cmp(rr0, P0, &rr1, P1))
1793 } /* dns_rr_exists() */
1796 static unsigned short dns_rr_offset(struct dns_rr *rr) {
1798 } /* dns_rr_offset() */
1801 static _Bool dns_rr_i_match(struct dns_rr *rr, struct dns_rr_i *i, struct dns_packet *P) {
1802 if (i->section && !(rr->section & i->section))
1805 if (i->type && rr->type != i->type && i->type != DNS_T_ALL)
1808 if (i->class && rr->class != i->class && i->class != DNS_C_ANY)
1812 char dn[DNS_D_MAXNAME + 1];
1816 if (!(len = dns_d_expand(dn, sizeof dn, rr->dn.p, P, &error))
1817 || len >= sizeof dn)
1820 if (0 != strcasecmp(dn, i->name))
1824 if (i->data && i->type && rr->section > DNS_S_QD) {
1828 if ((error = dns_any_parse(&rd, rr, P)))
1831 if (0 != dns_any_cmp(&rd, rr->type, i->data, i->type))
1836 } /* dns_rr_i_match() */
1839 static unsigned short dns_rr_i_start(struct dns_rr_i *i, struct dns_packet *P) {
1841 struct dns_rr r0, rr;
1844 if ((i->section & DNS_S_QD) && P->qd.base)
1846 else if ((i->section & DNS_S_AN) && P->an.base)
1848 else if ((i->section & DNS_S_NS) && P->ns.base)
1850 else if ((i->section & DNS_S_AR) && P->ar.base)
1855 for (rp = 12; rp < P->end; rp = dns_rr_skip(rp, P)) {
1856 if ((error = dns_rr_parse(&rr, rp, P)))
1859 rr.section = dns_rr_section(rp, P);
1861 if (!dns_rr_i_match(&rr, i, P))
1871 if (i->sort == &dns_rr_i_packet)
1872 return dns_rr_offset(&r0);
1874 while ((rp = dns_rr_skip(rp, P)) < P->end) {
1875 if ((error = dns_rr_parse(&rr, rp, P)))
1878 rr.section = dns_rr_section(rp, P);
1880 if (!dns_rr_i_match(&rr, i, P))
1883 if (i->sort(&rr, &r0, i, P) < 0)
1887 return dns_rr_offset(&r0);
1888 } /* dns_rr_i_start() */
1891 static unsigned short dns_rr_i_skip(unsigned short rp, struct dns_rr_i *i, struct dns_packet *P) {
1892 struct dns_rr r0, r1, rr;
1895 if ((error = dns_rr_parse(&r0, rp, P)))
1898 r0.section = dns_rr_section(rp, P);
1900 rp = (i->sort == &dns_rr_i_packet)? dns_rr_skip(rp, P) : 12;
1902 for (; rp < P->end; rp = dns_rr_skip(rp, P)) {
1903 if ((error = dns_rr_parse(&rr, rp, P)))
1906 rr.section = dns_rr_section(rp, P);
1908 if (!dns_rr_i_match(&rr, i, P))
1911 if (i->sort(&rr, &r0, i, P) <= 0)
1921 if (i->sort == &dns_rr_i_packet)
1922 return dns_rr_offset(&r1);
1924 while ((rp = dns_rr_skip(rp, P)) < P->end) {
1925 if ((error = dns_rr_parse(&rr, rp, P)))
1928 rr.section = dns_rr_section(rp, P);
1930 if (!dns_rr_i_match(&rr, i, P))
1933 if (i->sort(&rr, &r0, i, P) <= 0)
1936 if (i->sort(&rr, &r1, i, P) >= 0)
1942 return dns_rr_offset(&r1);
1943 } /* dns_rr_i_skip() */
1946 int dns_rr_i_packet(struct dns_rr *a, struct dns_rr *b, struct dns_rr_i *i __UNUSED__, struct dns_packet *P __UNUSED__) {
1947 return (int)a->dn.p - (int)b->dn.p;
1948 } /* dns_rr_i_packet() */
1951 int dns_rr_i_order(struct dns_rr *a, struct dns_rr *b, struct dns_rr_i *i __UNUSED__, struct dns_packet *P) {
1954 if ((cmp = a->section - b->section))
1957 if (a->type != b->type)
1958 return (int)a->dn.p - (int)b->dn.p;
1960 return dns_rr_cmp(a, P, b, P);
1961 } /* dns_rr_i_order() */
1964 int dns_rr_i_shuffle(struct dns_rr *a, struct dns_rr *b, struct dns_rr_i *i, struct dns_packet *P __UNUSED__) {
1967 while (!i->state.regs[0])
1968 i->state.regs[0] = dns_random();
1970 if ((cmp = a->section - b->section))
1973 return dns_k_shuffle16(a->dn.p, i->state.regs[0]) - dns_k_shuffle16(b->dn.p, i->state.regs[0]);
1974 } /* dns_rr_i_shuffle() */
1977 struct dns_rr_i *dns_rr_i_init(struct dns_rr_i *i, struct dns_packet *P __UNUSED__) {
1978 static const struct dns_rr_i i_initializer;
1980 i->state = i_initializer.state;
1981 i->saved = i->state;
1984 } /* dns_rr_i_init() */
1987 unsigned dns_rr_grep(struct dns_rr *rr, unsigned lim, struct dns_rr_i *i, struct dns_packet *P, int *error_) {
1991 switch (i->state.exec) {
1994 i->sort = &dns_rr_i_packet;
1996 i->state.next = dns_rr_i_start(i, P);
2001 while (count < lim && i->state.next < P->end) {
2002 if ((error = dns_rr_parse(rr, i->state.next, P)))
2005 rr->section = dns_rr_section(i->state.next, P);
2011 i->state.next = dns_rr_i_skip(i->state.next, i, P);
2022 } /* dns_rr_grep() */
2025 static size_t dns__printchar(void *dst, size_t lim, size_t cp, unsigned char ch) {
2027 ((unsigned char *)dst)[cp] = ch;
2030 } /* dns__printchar() */
2033 static size_t dns__printstring(void *dst, size_t lim, size_t cp, const void *src, size_t len) {
2035 memcpy(&((unsigned char *)dst)[cp], src, MIN(len, lim - cp));
2038 } /* dns__printstring() */
2040 #define dns__printstring5(a, b, c, d, e) dns__printstring((a), (b), (c), (d), (e))
2041 #define dns__printstring4(a, b, c, d) dns__printstring((a), (b), (c), (d), strlen((d)))
2042 #define dns__printstring(...) DNS_PP_CALL(DNS_PP_XPASTE(dns__printstring, DNS_PP_NARG(__VA_ARGS__)), __VA_ARGS__)
2045 static void dns__printnul(void *dst, size_t lim, size_t off) {
2047 ((unsigned char *)dst)[MIN(off, lim - 1)] = '\0';
2048 } /* dns__printnul() */
2051 static size_t dns__print10(void *dst, size_t lim, size_t off, unsigned n, unsigned pad) {
2052 unsigned char tmp[32];
2055 unsigned d = 1000000000;
2061 if ((ch = n / d) || cp > 0) {
2073 dp += dns__printchar(dst, lim, dp, '0');
2077 dp += dns__printstring(dst, lim, dp, tmp, cp);
2080 } /* dns__print10() */
2083 size_t dns_rr_print(void *dst, size_t lim, struct dns_rr *rr, struct dns_packet *P, int *error_) {
2085 size_t cp, n, rdlen;
2091 if (rr->section == DNS_S_QD)
2092 cp += dns__printchar(dst, lim, cp, ';');
2094 if (!(n = dns_d_expand(&((unsigned char *)dst)[cp], (cp < lim)? lim - cp : 0, rr->dn.p, P, &error)))
2099 if (rr->section != DNS_S_QD) {
2100 cp += dns__printchar(dst, lim, cp, ' ');
2101 cp += dns__print10(dst, lim, cp, rr->ttl, 0);
2104 cp += dns__printchar(dst, lim, cp, ' ');
2105 cp += dns__printstring(dst, lim, cp, dns_strclass(rr->class), strlen(dns_strclass(rr->class)));
2106 cp += dns__printchar(dst, lim, cp, ' ');
2107 cp += dns__printstring(dst, lim, cp, dns_strtype(rr->type), strlen(dns_strtype(rr->type)));
2109 if (rr->section == DNS_S_QD)
2112 cp += dns__printchar(dst, lim, cp, ' ');
2114 if ((error = dns_any_parse(dns_any_init(&any, sizeof any), rr, P)))
2118 rd = &((unsigned char *)dst)[cp];
2125 cp += dns_any_print(rd, rdlen, &any, rr->type);
2128 dns__printnul(dst, lim, cp);
2135 } /* dns_rr_print() */
2138 int dns_a_parse(struct dns_a *a, struct dns_rr *rr, struct dns_packet *P) {
2141 if (rr->rd.len != 4)
2142 return DNS_EILLEGAL;
2144 addr = ((0xff & P->data[rr->rd.p + 0]) << 24)
2145 | ((0xff & P->data[rr->rd.p + 1]) << 16)
2146 | ((0xff & P->data[rr->rd.p + 2]) << 8)
2147 | ((0xff & P->data[rr->rd.p + 3]) << 0);
2149 a->addr.s_addr = htonl(addr);
2152 } /* dns_a_parse() */
2155 int dns_a_push(struct dns_packet *P, struct dns_a *a) {
2158 if (P->size - P->end < 6)
2161 P->data[P->end++] = 0x00;
2162 P->data[P->end++] = 0x04;
2164 addr = ntohl(a->addr.s_addr);
2166 P->data[P->end++] = 0xff & (addr >> 24);
2167 P->data[P->end++] = 0xff & (addr >> 16);
2168 P->data[P->end++] = 0xff & (addr >> 8);
2169 P->data[P->end++] = 0xff & (addr >> 0);
2172 } /* dns_a_push() */
2175 size_t dns_a_arpa(void *dst, size_t lim, const struct dns_a *a) {
2176 unsigned long a4 = ntohl(a->addr.s_addr);
2180 for (i = 4; i > 0; i--) {
2181 cp += dns__print10(dst, lim, cp, (0xff & a4), 0);
2182 cp += dns__printchar(dst, lim, cp, '.');
2186 cp += dns__printstring(dst, lim, cp, "in-addr.arpa.");
2188 dns__printnul(dst, lim, cp);
2191 } /* dns_a_arpa() */
2194 int dns_a_cmp(const struct dns_a *a, const struct dns_a *b) {
2195 if (ntohl(a->addr.s_addr) < ntohl(b->addr.s_addr))
2197 if (ntohl(a->addr.s_addr) > ntohl(b->addr.s_addr))
2204 size_t dns_a_print(void *dst, size_t lim, struct dns_a *a) {
2205 char addr[INET_ADDRSTRLEN + 1] = "0.0.0.0";
2208 dns_inet_ntop(AF_INET, &a->addr, addr, sizeof addr);
2210 dns__printnul(dst, lim, (len = dns__printstring(dst, lim, 0, addr)));
2213 } /* dns_a_print() */
2216 int dns_aaaa_parse(struct dns_aaaa *aaaa, struct dns_rr *rr, struct dns_packet *P) {
2217 if (rr->rd.len != sizeof aaaa->addr.s6_addr)
2218 return DNS_EILLEGAL;
2220 memcpy(aaaa->addr.s6_addr, &P->data[rr->rd.p], sizeof aaaa->addr.s6_addr);
2223 } /* dns_aaaa_parse() */
2226 int dns_aaaa_push(struct dns_packet *P, struct dns_aaaa *aaaa) {
2227 if (P->size - P->end < 2 + sizeof aaaa->addr.s6_addr)
2230 P->data[P->end++] = 0x00;
2231 P->data[P->end++] = 0x10;
2233 memcpy(&P->data[P->end], aaaa->addr.s6_addr, sizeof aaaa->addr.s6_addr);
2235 P->end += sizeof aaaa->addr.s6_addr;
2238 } /* dns_aaaa_push() */
2241 int dns_aaaa_cmp(const struct dns_aaaa *a, const struct dns_aaaa *b) {
2245 for (i = 0; i < lengthof(a->addr.s6_addr); i++) {
2246 if ((cmp = (a->addr.s6_addr[i] - b->addr.s6_addr[i])))
2251 } /* dns_aaaa_cmp() */
2254 size_t dns_aaaa_arpa(void *dst, size_t lim, const struct dns_aaaa *aaaa) {
2255 static const unsigned char hex[16] = "0123456789abcdef";
2260 for (i = sizeof aaaa->addr.s6_addr - 1; i >= 0; i--) {
2261 nyble = aaaa->addr.s6_addr[i];
2263 for (j = 0; j < 2; j++) {
2264 cp += dns__printchar(dst, lim, cp, hex[0x0f & nyble]);
2265 cp += dns__printchar(dst, lim, cp, '.');
2270 cp += dns__printstring(dst, lim, cp, "ip6.arpa.");
2272 dns__printnul(dst, lim, cp);
2275 } /* dns_aaaa_arpa() */
2278 size_t dns_aaaa_print(void *dst, size_t lim, struct dns_aaaa *aaaa) {
2279 char addr[INET6_ADDRSTRLEN + 1] = "::";
2282 dns_inet_ntop(AF_INET6, &aaaa->addr, addr, sizeof addr);
2284 dns__printnul(dst, lim, (len = dns__printstring(dst, lim, 0, addr)));
2287 } /* dns_aaaa_print() */
2290 int dns_mx_parse(struct dns_mx *mx, struct dns_rr *rr, struct dns_packet *P) {
2295 return DNS_EILLEGAL;
2297 mx->preference = (0xff00 & (P->data[rr->rd.p + 0] << 8))
2298 | (0x00ff & (P->data[rr->rd.p + 1] << 0));
2300 if (!(len = dns_d_expand(mx->host, sizeof mx->host, rr->rd.p + 2, P, &error)))
2302 else if (len >= sizeof mx->host)
2303 return DNS_EILLEGAL;
2306 } /* dns_mx_parse() */
2309 int dns_mx_push(struct dns_packet *P, struct dns_mx *mx) {
2313 if (P->size - P->end < 5)
2319 P->data[P->end++] = 0xff & (mx->preference >> 8);
2320 P->data[P->end++] = 0xff & (mx->preference >> 0);
2322 if ((error = dns_d_push(P, mx->host, strlen(mx->host))))
2325 len = P->end - end - 2;
2327 P->data[end + 0] = 0xff & (len >> 8);
2328 P->data[end + 1] = 0xff & (len >> 0);
2335 } /* dns_mx_push() */
2338 int dns_mx_cmp(const struct dns_mx *a, const struct dns_mx *b) {
2341 if ((cmp = a->preference - b->preference))
2344 return strcasecmp(a->host, b->host);
2345 } /* dns_mx_cmp() */
2348 size_t dns_mx_print(void *dst, size_t lim, struct dns_mx *mx) {
2351 cp += dns__print10(dst, lim, cp, mx->preference, 0);
2352 cp += dns__printchar(dst, lim, cp, ' ');
2353 cp += dns__printstring(dst, lim, cp, mx->host, strlen(mx->host));
2355 dns__printnul(dst, lim, cp);
2358 } /* dns_mx_print() */
2361 size_t dns_mx_cname(void *dst, size_t lim, struct dns_mx *mx) {
2362 return dns_strlcpy(dst, mx->host, lim);
2363 } /* dns_mx_cname() */
2366 int dns_ns_parse(struct dns_ns *ns, struct dns_rr *rr, struct dns_packet *P) {
2370 if (!(len = dns_d_expand(ns->host, sizeof ns->host, rr->rd.p, P, &error)))
2372 else if (len >= sizeof ns->host)
2373 return DNS_EILLEGAL;
2376 } /* dns_ns_parse() */
2379 int dns_ns_push(struct dns_packet *P, struct dns_ns *ns) {
2383 if (P->size - P->end < 3)
2389 if ((error = dns_d_push(P, ns->host, strlen(ns->host))))
2392 len = P->end - end - 2;
2394 P->data[end + 0] = 0xff & (len >> 8);
2395 P->data[end + 1] = 0xff & (len >> 0);
2402 } /* dns_ns_push() */
2405 int dns_ns_cmp(const struct dns_ns *a, const struct dns_ns *b) {
2406 return strcasecmp(a->host, b->host);
2407 } /* dns_ns_cmp() */
2410 size_t dns_ns_print(void *dst, size_t lim, struct dns_ns *ns) {
2413 cp = dns__printstring(dst, lim, 0, ns->host, strlen(ns->host));
2415 dns__printnul(dst, lim, cp);
2418 } /* dns_ns_print() */
2421 size_t dns_ns_cname(void *dst, size_t lim, struct dns_ns *ns) {
2422 return dns_strlcpy(dst, ns->host, lim);
2423 } /* dns_ns_cname() */
2426 int dns_cname_parse(struct dns_cname *cname, struct dns_rr *rr, struct dns_packet *P) {
2427 return dns_ns_parse((struct dns_ns *)cname, rr, P);
2428 } /* dns_cname_parse() */
2431 int dns_cname_push(struct dns_packet *P, struct dns_cname *cname) {
2432 return dns_ns_push(P, (struct dns_ns *)cname);
2433 } /* dns_cname_push() */
2436 int dns_cname_cmp(const struct dns_cname *a, const struct dns_cname *b) {
2437 return strcasecmp(a->host, b->host);
2438 } /* dns_cname_cmp() */
2441 size_t dns_cname_print(void *dst, size_t lim, struct dns_cname *cname) {
2442 return dns_ns_print(dst, lim, (struct dns_ns *)cname);
2443 } /* dns_cname_print() */
2446 size_t dns_cname_cname(void *dst, size_t lim, struct dns_cname *cname) {
2447 return dns_strlcpy(dst, cname->host, lim);
2448 } /* dns_cname_cname() */
2451 int dns_soa_parse(struct dns_soa *soa, struct dns_rr *rr, struct dns_packet *P) {
2452 struct { void *dst; size_t lim; } dn[] =
2453 { { soa->mname, sizeof soa->mname },
2454 { soa->rname, sizeof soa->rname } };
2456 { &soa->serial, &soa->refresh, &soa->retry, &soa->expire, &soa->minimum };
2462 if ((rp = rr->rd.p) >= P->end)
2463 return DNS_EILLEGAL;
2465 for (i = 0; i < lengthof(dn); i++) {
2466 if (!(n = dns_d_expand(dn[i].dst, dn[i].lim, rp, P, &error)))
2468 else if (n >= dn[i].lim)
2469 return DNS_EILLEGAL;
2471 if ((rp = dns_d_skip(rp, P)) >= P->end)
2472 return DNS_EILLEGAL;
2475 /* SERIAL / REFRESH / RETRY / EXPIRE / MINIMUM */
2476 for (i = 0; i < lengthof(ts); i++) {
2477 for (j = 0; j < 4; j++, rp++) {
2479 return DNS_EILLEGAL;
2482 *ts[i] |= (0xff & P->data[rp]);
2487 } /* dns_soa_parse() */
2490 int dns_soa_push(struct dns_packet *P, struct dns_soa *soa) {
2491 void *dn[] = { soa->mname, soa->rname };
2492 unsigned ts[] = { (0xffffffff & soa->serial),
2493 (0x7fffffff & soa->refresh),
2494 (0x7fffffff & soa->retry),
2495 (0x7fffffff & soa->expire),
2496 (0xffffffff & soa->minimum) };
2503 if ((P->end += 2) >= P->size)
2507 for (i = 0; i < lengthof(dn); i++) {
2508 if ((error = dns_d_push(P, dn[i], strlen(dn[i]))))
2512 /* SERIAL / REFRESH / RETRY / EXPIRE / MINIMUM */
2513 for (i = 0; i < lengthof(ts); i++) {
2514 if ((P->end += 4) >= P->size)
2517 for (j = 1; j <= 4; j++) {
2518 P->data[P->end - j] = (0xff & ts[i]);
2523 len = P->end - end - 2;
2524 P->data[end + 0] = (0xff & (len >> 8));
2525 P->data[end + 1] = (0xff & (len >> 0));
2529 error = DNS_ENOBUFS;
2536 } /* dns_soa_push() */
2539 int dns_soa_cmp(const struct dns_soa *a, const struct dns_soa *b) {
2542 if ((cmp = strcasecmp(a->mname, b->mname)))
2545 if ((cmp = strcasecmp(a->rname, b->rname)))
2548 if (a->serial > b->serial)
2550 else if (a->serial < b->serial)
2553 if (a->refresh > b->refresh)
2555 else if (a->refresh < b->refresh)
2558 if (a->retry > b->retry)
2560 else if (a->retry < b->retry)
2563 if (a->expire > b->expire)
2565 else if (a->expire < b->expire)
2568 if (a->minimum > b->minimum)
2570 else if (a->minimum < b->minimum)
2574 } /* dns_soa_cmp() */
2577 size_t dns_soa_print(void *dst, size_t lim, struct dns_soa *soa) {
2580 cp += dns__printstring(dst, lim, cp, soa->mname, strlen(soa->mname));
2581 cp += dns__printchar(dst, lim, cp, ' ');
2582 cp += dns__printstring(dst, lim, cp, soa->rname, strlen(soa->rname));
2583 cp += dns__printchar(dst, lim, cp, ' ');
2584 cp += dns__print10(dst, lim, cp, soa->serial, 0);
2585 cp += dns__printchar(dst, lim, cp, ' ');
2586 cp += dns__print10(dst, lim, cp, soa->refresh, 0);
2587 cp += dns__printchar(dst, lim, cp, ' ');
2588 cp += dns__print10(dst, lim, cp, soa->retry, 0);
2589 cp += dns__printchar(dst, lim, cp, ' ');
2590 cp += dns__print10(dst, lim, cp, soa->expire, 0);
2591 cp += dns__printchar(dst, lim, cp, ' ');
2592 cp += dns__print10(dst, lim, cp, soa->minimum, 0);
2594 dns__printnul(dst, lim, cp);
2597 } /* dns_soa_print() */
2600 int dns_srv_parse(struct dns_srv *srv, struct dns_rr *rr, struct dns_packet *P) {
2606 memset(srv, '\0', sizeof *srv);
2610 if (P->size - P->end < 6)
2611 return DNS_EILLEGAL;
2613 for (i = 0; i < 2; i++, rp++) {
2614 srv->priority <<= 8;
2615 srv->priority |= (0xff & P->data[rp]);
2618 for (i = 0; i < 2; i++, rp++) {
2620 srv->weight |= (0xff & P->data[rp]);
2623 for (i = 0; i < 2; i++, rp++) {
2625 srv->port |= (0xff & P->data[rp]);
2628 if (!(n = dns_d_expand(srv->target, sizeof srv->target, rp, P, &error)))
2630 else if (n >= sizeof srv->target)
2631 return DNS_EILLEGAL;
2634 } /* dns_srv_parse() */
2637 int dns_srv_push(struct dns_packet *P, struct dns_srv *srv) {
2643 if (P->size - P->end < 2)
2648 if (P->size - P->end < 6)
2651 P->data[P->end++] = 0xff & (srv->priority >> 8);
2652 P->data[P->end++] = 0xff & (srv->priority >> 0);
2654 P->data[P->end++] = 0xff & (srv->weight >> 8);
2655 P->data[P->end++] = 0xff & (srv->weight >> 0);
2657 P->data[P->end++] = 0xff & (srv->port >> 8);
2658 P->data[P->end++] = 0xff & (srv->port >> 0);
2660 if (0 == (len = dns_d_comp(&P->data[P->end], P->size - P->end, srv->target, strlen(srv->target), P, &error)))
2662 else if (P->size - P->end < len)
2670 len = P->end - end - 2;
2672 P->data[end + 0] = 0xff & (len >> 8);
2673 P->data[end + 1] = 0xff & (len >> 0);
2677 error = DNS_ENOBUFS;
2684 } /* dns_srv_push() */
2687 int dns_srv_cmp(const struct dns_srv *a, const struct dns_srv *b) {
2690 if ((cmp = a->priority - b->priority))
2694 * FIXME: We need some sort of random seed to implement the dynamic
2695 * weighting required by RFC 2782.
2697 if ((cmp = a->weight - b->weight))
2700 if ((cmp = a->port - b->port))
2703 return strcasecmp(a->target, b->target);
2704 } /* dns_srv_cmp() */
2707 size_t dns_srv_print(void *dst, size_t lim, struct dns_srv *srv) {
2710 cp += dns__print10(dst, lim, cp, srv->priority, 0);
2711 cp += dns__printchar(dst, lim, cp, ' ');
2712 cp += dns__print10(dst, lim, cp, srv->weight, 0);
2713 cp += dns__printchar(dst, lim, cp, ' ');
2714 cp += dns__print10(dst, lim, cp, srv->port, 0);
2715 cp += dns__printchar(dst, lim, cp, ' ');
2716 cp += dns__printstring(dst, lim, cp, srv->target, strlen(srv->target));
2718 dns__printnul(dst, lim, cp);
2721 } /* dns_srv_print() */
2724 size_t dns_srv_cname(void *dst, size_t lim, struct dns_srv *srv) {
2725 return dns_strlcpy(dst, srv->target, lim);
2726 } /* dns_srv_cname() */
2729 int dns_ptr_parse(struct dns_ptr *ptr, struct dns_rr *rr, struct dns_packet *P) {
2730 return dns_ns_parse((struct dns_ns *)ptr, rr, P);
2731 } /* dns_ptr_parse() */
2734 int dns_ptr_push(struct dns_packet *P, struct dns_ptr *ptr) {
2735 return dns_ns_push(P, (struct dns_ns *)ptr);
2736 } /* dns_ptr_push() */
2739 size_t dns_ptr_qname(void *dst, size_t lim, int af, void *addr) {
2740 unsigned len = (af == AF_INET6)
2741 ? dns_aaaa_arpa(dst, lim, addr)
2742 : dns_a_arpa(dst, lim, addr);
2744 dns__printnul(dst, lim, len);
2747 } /* dns_ptr_qname() */
2750 int dns_ptr_cmp(const struct dns_ptr *a, const struct dns_ptr *b) {
2751 return strcasecmp(a->host, b->host);
2752 } /* dns_ptr_cmp() */
2755 size_t dns_ptr_print(void *dst, size_t lim, struct dns_ptr *ptr) {
2756 return dns_ns_print(dst, lim, (struct dns_ns *)ptr);
2757 } /* dns_ptr_print() */
2760 size_t dns_ptr_cname(void *dst, size_t lim, struct dns_ptr *ptr) {
2761 return dns_strlcpy(dst, ptr->host, lim);
2762 } /* dns_ptr_cname() */
2765 int dns_sshfp_parse(struct dns_sshfp *fp, struct dns_rr *rr, struct dns_packet *P) {
2766 unsigned p = rr->rd.p, pe = rr->rd.p + rr->rd.len;
2769 return DNS_EILLEGAL;
2771 fp->algo = P->data[p++];
2772 fp->type = P->data[p++];
2775 case DNS_SSHFP_SHA1:
2776 if (pe - p < sizeof fp->digest.sha1)
2777 return DNS_EILLEGAL;
2779 memcpy(fp->digest.sha1, &P->data[p], sizeof fp->digest.sha1);
2787 } /* dns_sshfp_parse() */
2790 int dns_sshfp_push(struct dns_packet *P, struct dns_sshfp *fp) {
2791 unsigned p = P->end, pe = P->size, n;
2797 P->data[p++] = 0xff & fp->algo;
2798 P->data[p++] = 0xff & fp->type;
2801 case DNS_SSHFP_SHA1:
2802 if (pe - p < sizeof fp->digest.sha1)
2805 memcpy(&P->data[p], fp->digest.sha1, sizeof fp->digest.sha1);
2806 p += sizeof fp->digest.sha1;
2810 return DNS_EILLEGAL;
2814 P->data[P->end++] = 0xff & (n >> 8);
2815 P->data[P->end++] = 0xff & (n >> 0);
2819 } /* dns_sshfp_push() */
2822 int dns_sshfp_cmp(const struct dns_sshfp *a, const struct dns_sshfp *b) {
2825 if ((cmp = a->algo - b->algo) || (cmp - a->type - b->type))
2829 case DNS_SSHFP_SHA1:
2830 return memcmp(a->digest.sha1, b->digest.sha1, sizeof a->digest.sha1);
2836 } /* dns_sshfp_cmp() */
2839 size_t dns_sshfp_print(void *dst, size_t lim, struct dns_sshfp *fp) {
2840 static const unsigned char hex[16] = "0123456789abcdef";
2843 p += dns__print10(dst, lim, p, fp->algo, 0);
2844 p += dns__printchar(dst, lim, p, ' ');
2845 p += dns__print10(dst, lim, p, fp->type, 0);
2846 p += dns__printchar(dst, lim, p, ' ');
2849 case DNS_SSHFP_SHA1:
2850 for (i = 0; i < sizeof fp->digest.sha1; i++) {
2851 p += dns__printchar(dst, lim, p, hex[0x0f & (fp->digest.sha1[i] >> 4)]);
2852 p += dns__printchar(dst, lim, p, hex[0x0f & (fp->digest.sha1[i] >> 0)]);
2857 p += dns__printchar(dst, lim, p, '0');
2862 dns__printnul(dst, lim, p);
2865 } /* dns_sshfp_print() */
2868 struct dns_txt *dns_txt_init(struct dns_txt *txt, size_t size) {
2869 assert(size > offsetof(struct dns_txt, data));
2871 txt->size = size - offsetof(struct dns_txt, data);
2875 } /* dns_txt_init() */
2878 int dns_txt_parse(struct dns_txt *txt, struct dns_rr *rr, struct dns_packet *P) {
2879 struct { unsigned char *b; size_t p, end; } dst, src;
2884 dst.end = txt->size;
2888 src.end = src.p + rr->rd.len;
2890 while (src.p < src.end) {
2891 n = 0xff & P->data[src.p++];
2893 if (src.end - src.p < n || dst.end - dst.p < n)
2894 return DNS_EILLEGAL;
2896 memcpy(&dst.b[dst.p], &src.b[src.p], n);
2905 } /* dns_txt_parse() */
2908 int dns_txt_push(struct dns_packet *P, struct dns_txt *txt) {
2909 struct { unsigned char *b; size_t p, end; } dst, src;
2920 if (dst.end - dst.p < 2)
2923 n = txt->len + ((txt->len + 254) / 255);
2925 dst.b[dst.p++] = 0xff & (n >> 8);
2926 dst.b[dst.p++] = 0xff & (n >> 0);
2928 while (src.p < src.end) {
2929 n = MIN(255, src.end - src.p);
2931 if (dst.p >= dst.end)
2936 if (dst.end - dst.p < n)
2939 memcpy(&dst.b[dst.p], &src.b[src.p], n);
2948 } /* dns_txt_push() */
2951 int dns_txt_cmp(const struct dns_txt *a __UNUSED__, const struct dns_txt *b __UNUSED__) {
2953 } /* dns_txt_cmp() */
2956 size_t dns_txt_print(void *dst_, size_t lim, struct dns_txt *txt) {
2957 struct { unsigned char *b; size_t p, end; } dst, src;
2968 dst.p += dns__printchar(dst.b, dst.end, dst.p, '"');
2970 while (src.p < src.end) {
2973 if (0 == (src.p++ % 255) && src.p != 1) {
2974 dst.p += dns__printchar(dst.b, dst.end, dst.p, '"');
2975 dst.p += dns__printchar(dst.b, dst.end, dst.p, ' ');
2976 dst.p += dns__printchar(dst.b, dst.end, dst.p, '"');
2979 if (ch < 32 || ch > 126 || ch == '"' || ch == '\\') {
2980 dst.p += dns__printchar(dst.b, dst.end, dst.p, '\\');
2981 dst.p += dns__print10(dst.b, dst.end, dst.p, ch, 3);
2983 dst.p += dns__printchar(dst.b, dst.end, dst.p, ch);
2987 dst.p += dns__printchar(dst.b, dst.end, dst.p, '"');
2989 dns__printnul(dst.b, dst.end, dst.p);
2992 } /* dns_txt_print() */
2995 static const struct {
3004 { DNS_T_A, "A", &dns_a_parse, &dns_a_push, &dns_a_cmp, &dns_a_print, 0 },
3005 { DNS_T_AAAA, "AAAA", &dns_aaaa_parse, &dns_aaaa_push, &dns_aaaa_cmp, &dns_aaaa_print, 0 },
3006 { DNS_T_MX, "MX", &dns_mx_parse, &dns_mx_push, &dns_mx_cmp, &dns_mx_print, &dns_mx_cname },
3007 { DNS_T_NS, "NS", &dns_ns_parse, &dns_ns_push, &dns_ns_cmp, &dns_ns_print, &dns_ns_cname },
3008 { DNS_T_CNAME, "CNAME", &dns_cname_parse, &dns_cname_push, &dns_cname_cmp, &dns_cname_print, &dns_cname_cname },
3009 { DNS_T_SOA, "SOA", &dns_soa_parse, &dns_soa_push, &dns_soa_cmp, &dns_soa_print, 0 },
3010 { DNS_T_SRV, "SRV", &dns_srv_parse, &dns_srv_push, &dns_srv_cmp, &dns_srv_print, &dns_srv_cname },
3011 { DNS_T_PTR, "PTR", &dns_ptr_parse, &dns_ptr_push, &dns_ptr_cmp, &dns_ptr_print, &dns_ptr_cname },
3012 { DNS_T_TXT, "TXT", &dns_txt_parse, &dns_txt_push, &dns_txt_cmp, &dns_txt_print, 0 },
3013 { DNS_T_SPF, "SPF", &dns_txt_parse, &dns_txt_push, &dns_txt_cmp, &dns_txt_print, 0 },
3014 { DNS_T_SSHFP, "SSHFP", &dns_sshfp_parse, &dns_sshfp_push, &dns_sshfp_cmp, &dns_sshfp_print, 0 },
3015 }; /* dns_rrtypes[] */
3018 union dns_any *dns_any_init(union dns_any *any, size_t size) {
3019 return (union dns_any *)dns_txt_init(&any->rdata, size);
3020 } /* dns_any_init() */
3023 int dns_any_parse(union dns_any *any, struct dns_rr *rr, struct dns_packet *P) {
3026 for (i = 0; i < lengthof(dns_rrtypes); i++) {
3027 if (dns_rrtypes[i].type == rr->type)
3028 return dns_rrtypes[i].parse(any, rr, P);
3031 if (rr->rd.len > any->rdata.size)
3032 return DNS_EILLEGAL;
3034 memcpy(any->rdata.data, &P->data[rr->rd.p], rr->rd.len);
3035 any->rdata.len = rr->rd.len;
3038 } /* dns_any_parse() */
3041 int dns_any_push(struct dns_packet *P, union dns_any *any, enum dns_type type) {
3044 for (i = 0; i < lengthof(dns_rrtypes); i++) {
3045 if (dns_rrtypes[i].type == type)
3046 return dns_rrtypes[i].push(P, any);
3049 if (P->size - P->end < any->rdata.len + 2)
3052 P->data[P->end++] = 0xff & (any->rdata.len >> 8);
3053 P->data[P->end++] = 0xff & (any->rdata.len >> 0);
3055 memcpy(&P->data[P->end], any->rdata.data, any->rdata.len);
3056 P->end += any->rdata.len;
3059 } /* dns_any_push() */
3062 int dns_any_cmp(const union dns_any *a, enum dns_type x, const union dns_any *b, enum dns_type y) {
3069 for (i = 0; i < lengthof(dns_rrtypes); i++) {
3070 if (dns_rrtypes[i].type == x)
3071 return dns_rrtypes[i].cmp(a, b);
3075 } /* dns_any_cmp() */
3078 size_t dns_any_print(void *dst_, size_t lim, union dns_any *any, enum dns_type type) {
3079 struct { unsigned char *b; size_t p, end; } dst, src;
3082 for (i = 0; i < lengthof(dns_rrtypes); i++) {
3083 if (dns_rrtypes[i].type == type)
3084 return dns_rrtypes[i].print(dst_, lim, any);
3091 src.b = any->rdata.data;
3092 src.end = any->rdata.len;
3095 dst.p += dns__printchar(dst.b, dst.end, dst.p, '"');
3097 while (src.p < src.end) {
3098 ch = src.b[src.p++];
3100 dst.p += dns__printchar(dst.b, dst.end, dst.p, '\\');
3101 dst.p += dns__print10(dst.b, dst.end, dst.p, ch, 3);
3104 dst.p += dns__printchar(dst.b, dst.end, dst.p, '"');
3106 dns__printnul(dst.b, dst.end, dst.p);
3109 } /* dns_any_print() */
3112 size_t dns_any_cname(void *dst, size_t lim, union dns_any *any, enum dns_type type) {
3115 for (i = 0; i < lengthof(dns_rrtypes); i++) {
3116 if (dns_rrtypes[i].type == type)
3117 return (dns_rrtypes[i].cname)? dns_rrtypes[i].cname(dst, lim, any) : 0;
3121 } /* dns_any_cname() */
3125 * H O S T S R O U T I N E S
3127 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
3130 struct dns_hosts_entry {
3131 char host[DNS_D_MAXNAME + 1];
3143 struct dns_hosts_entry *next;
3146 dns_atomic_t refcount;
3147 }; /* struct dns_hosts */
3150 struct dns_hosts *dns_hosts_open(int *error) {
3151 static const struct dns_hosts hosts_initializer = { .refcount = 1 };
3152 struct dns_hosts *hosts;
3154 if (!(hosts = malloc(sizeof *hosts)))
3157 *hosts = hosts_initializer;
3159 hosts->tail = &hosts->head;
3163 *error = dns_syerr();
3168 } /* dns_hosts_open() */
3171 void dns_hosts_close(struct dns_hosts *hosts) {
3172 struct dns_hosts_entry *ent, *xnt;
3174 if (!hosts || 1 != dns_hosts_release(hosts))
3177 for (ent = hosts->head; ent; ent = xnt) {
3186 } /* dns_hosts_close() */
3189 unsigned dns_hosts_acquire(struct dns_hosts *hosts) {
3190 return dns_atomic_inc(&hosts->refcount);
3191 } /* dns_hosts_acquire() */
3194 unsigned dns_hosts_release(struct dns_hosts *hosts) {
3195 return dns_atomic_dec(&hosts->refcount);
3196 } /* dns_hosts_release() */
3199 struct dns_hosts *dns_hosts_mortal(struct dns_hosts *hosts) {
3201 dns_hosts_release(hosts);
3204 } /* dns_hosts_mortal() */
3207 struct dns_hosts *dns_hosts_local(int *error_) {
3208 struct dns_hosts *hosts;
3211 if (!(hosts = dns_hosts_open(&error)))
3214 if ((error = dns_hosts_loadpath(hosts, "/etc/hosts")))
3221 dns_hosts_close(hosts);
3224 } /* dns_hosts_local() */
3227 #define dns_hosts_issep(ch) (isspace(ch))
3228 #define dns_hosts_iscom(ch) ((ch) == '#' || (ch) == ';')
3230 int dns_hosts_loadfile(struct dns_hosts *hosts, FILE *fp) {
3231 struct dns_hosts_entry ent;
3232 char word[MAX(INET6_ADDRSTRLEN, DNS_D_MAXNAME) + 1];
3233 unsigned wp, wc, skip;
3239 memset(&ent, '\0', sizeof ent);
3244 memset(word, '\0', sizeof word);
3247 while (EOF != (ch = fgetc(fp)) && ch != '\n') {
3248 skip |= !!dns_hosts_iscom(ch);
3253 if (dns_hosts_issep(ch))
3256 if (wp < sizeof word - 1)
3270 ent.af = (strchr(word, ':'))? AF_INET6 : AF_INET;
3271 skip = (1 != dns_inet_pton(ent.af, word, &ent.addr));
3278 dns_d_anchor(ent.host, sizeof ent.host, word, wp);
3280 if ((error = dns_hosts_insert(hosts, ent.af, &ent.addr, ent.host, (wc > 2))))
3285 } while (ch != EOF && ch != '\n');
3286 } while (ch != EOF);
3289 } /* dns_hosts_loadfile() */
3292 int dns_hosts_loadpath(struct dns_hosts *hosts, const char *path) {
3296 if (!(fp = fopen(path, "r")))
3299 error = dns_hosts_loadfile(hosts, fp);
3304 } /* dns_hosts_loadpath() */
3307 int dns_hosts_dump(struct dns_hosts *hosts, FILE *fp) {
3308 struct dns_hosts_entry *ent, *xnt;
3309 char addr[INET6_ADDRSTRLEN + 1];
3312 for (ent = hosts->head; ent; ent = xnt) {
3315 dns_inet_ntop(ent->af, &ent->addr, addr, sizeof addr);
3319 for (i = strlen(addr); i < INET_ADDRSTRLEN; i++)
3324 fputs(ent->host, fp);
3329 } /* dns_hosts_dump() */
3332 int dns_hosts_insert(struct dns_hosts *hosts, int af, const void *addr, const void *host, _Bool alias) {
3333 struct dns_hosts_entry *ent;
3336 if (!(ent = malloc(sizeof *ent)))
3339 dns_d_anchor(ent->host, sizeof ent->host, host, strlen(host));
3341 switch ((ent->af = af)) {
3343 memcpy(&ent->addr.a6, addr, sizeof ent->addr.a6);
3345 dns_aaaa_arpa(ent->arpa, sizeof ent->arpa, addr);
3349 memcpy(&ent->addr.a4, addr, sizeof ent->addr.a4);
3351 dns_a_arpa(ent->arpa, sizeof ent->arpa, addr);
3364 hosts->tail = &ent->next;
3368 error = dns_syerr();
3373 } /* dns_hosts_insert() */
3376 struct dns_packet *dns_hosts_query(struct dns_hosts *hosts, struct dns_packet *Q, int *error_) {
3377 struct dns_packet *P = dns_p_new(512);
3378 struct dns_packet *A = 0;
3380 struct dns_hosts_entry *ent;
3382 char qname[DNS_D_MAXNAME + 1];
3385 if ((error = dns_rr_parse(&rr, 12, Q)))
3388 if (!(qlen = dns_d_expand(qname, sizeof qname, rr.dn.p, Q, &error)))
3390 else if (qlen >= sizeof qname)
3393 if ((error = dns_p_push(P, DNS_S_QD, qname, qlen, rr.type, rr.class, 0, 0)))
3398 for (ent = hosts->head; ent; ent = ent->next) {
3399 if (ent->alias || 0 != strcasecmp(qname, ent->arpa))
3402 if ((error = dns_p_push(P, DNS_S_AN, qname, qlen, rr.type, rr.class, 0, ent->host)))
3414 loop: for (ent = hosts->head; ent; ent = ent->next) {
3415 if (ent->af != af || 0 != strcasecmp(qname, ent->host))
3418 if ((error = dns_p_push(P, DNS_S_AN, qname, qlen, rr.type, rr.class, 0, &ent->addr)))
3428 if (!(A = dns_p_copy(dns_p_make(P->end, &error), P)))
3433 error = DNS_EILLEGAL;
3440 } /* dns_hosts_query() */
3444 * R E S O L V . C O N F R O U T I N E S
3446 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
3448 struct dns_resolv_conf *dns_resconf_open(int *error) {
3449 static const struct dns_resolv_conf resconf_initializer
3450 = { .lookup = "bf", .options = { .ndots = 1, .timeout = 5, .attempts = 2, .tcp = DNS_RESCONF_TCP_ENABLE, },
3451 .iface = { .ss_family = AF_INET }, };
3452 struct dns_resolv_conf *resconf;
3453 struct sockaddr_in *sin;
3455 if (!(resconf = malloc(sizeof *resconf)))
3458 *resconf = resconf_initializer;
3460 sin = (struct sockaddr_in *)&resconf->nameserver[0];
3461 sin->sin_family = AF_INET;
3462 sin->sin_addr.s_addr = INADDR_ANY;
3463 sin->sin_port = htons(53);
3465 sin->sin_len = sizeof *sin;
3468 if (0 != gethostname(resconf->search[0], sizeof resconf->search[0]))
3471 dns_d_anchor(resconf->search[0], sizeof resconf->search[0], resconf->search[0], strlen(resconf->search[0]));
3472 dns_d_cleave(resconf->search[0], sizeof resconf->search[0], resconf->search[0], strlen(resconf->search[0]));
3475 * XXX: If gethostname() returned a string without any label
3476 * separator, then search[0][0] should be NUL.
3479 dns_resconf_acquire(resconf);
3483 *error = dns_syerr();
3488 } /* dns_resconf_open() */
3491 void dns_resconf_close(struct dns_resolv_conf *resconf) {
3492 if (!resconf || 1 != dns_resconf_release(resconf))
3496 } /* dns_resconf_close() */
3499 unsigned dns_resconf_acquire(struct dns_resolv_conf *resconf) {
3500 return dns_atomic_inc(&resconf->_.refcount);
3501 } /* dns_resconf_acquire() */
3504 unsigned dns_resconf_release(struct dns_resolv_conf *resconf) {
3505 return dns_atomic_dec(&resconf->_.refcount);
3506 } /* dns_resconf_release() */
3509 struct dns_resolv_conf *dns_resconf_mortal(struct dns_resolv_conf *resconf) {
3511 dns_resconf_release(resconf);
3514 } /* dns_resconf_mortal() */
3517 struct dns_resolv_conf *dns_resconf_local(int *error_) {
3518 struct dns_resolv_conf *resconf;
3521 if (!(resconf = dns_resconf_open(&error)))
3524 if ((error = dns_resconf_loadpath(resconf, "/etc/resolv.conf")))
3531 dns_resconf_close(resconf);
3534 } /* dns_resconf_local() */
3537 struct dns_resolv_conf *dns_resconf_root(int *error_) {
3538 struct dns_resolv_conf *resconf;
3541 if (!(resconf = dns_resconf_open(&error)))
3544 if ((error = dns_resconf_loadpath(resconf, "/etc/resolv.conf")))
3547 resconf->options.recurse = 1;
3553 dns_resconf_close(resconf);
3556 } /* dns_resconf_root() */
3559 enum dns_resconf_keyword {
3560 DNS_RESCONF_NAMESERVER,
3567 DNS_RESCONF_OPTIONS,
3570 DNS_RESCONF_TIMEOUT,
3571 DNS_RESCONF_ATTEMPTS,
3573 DNS_RESCONF_RECURSE,
3577 DNS_RESCONF_INTERFACE,
3582 DNS_RESCONF_DISABLE,
3583 }; /* enum dns_resconf_keyword */
3585 static enum dns_resconf_keyword dns_resconf_keyword(const char *word) {
3586 static const char *words[] = {
3587 [DNS_RESCONF_NAMESERVER] = "nameserver",
3588 [DNS_RESCONF_DOMAIN] = "domain",
3589 [DNS_RESCONF_SEARCH] = "search",
3590 [DNS_RESCONF_LOOKUP] = "lookup",
3591 [DNS_RESCONF_FILE] = "file",
3592 [DNS_RESCONF_BIND] = "bind",
3593 [DNS_RESCONF_CACHE] = "cache",
3594 [DNS_RESCONF_OPTIONS] = "options",
3595 [DNS_RESCONF_EDNS0] = "edns0",
3596 [DNS_RESCONF_ROTATE] = "rotate",
3597 [DNS_RESCONF_RECURSE] = "recurse",
3598 [DNS_RESCONF_SMART] = "smart",
3599 [DNS_RESCONF_TCP] = "tcp",
3600 [DNS_RESCONF_INTERFACE] = "interface",
3601 [DNS_RESCONF_ZERO] = "0",
3602 [DNS_RESCONF_ONE] = "1",
3603 [DNS_RESCONF_ENABLE] = "enable",
3604 [DNS_RESCONF_ONLY] = "only",
3605 [DNS_RESCONF_DISABLE] = "disable",
3609 for (i = 0; i < lengthof(words); i++) {
3610 if (words[i] && 0 == strcasecmp(words[i], word))
3614 if (0 == strncasecmp(word, "ndots:", sizeof "ndots:" - 1))
3615 return DNS_RESCONF_NDOTS;
3617 if (0 == strncasecmp(word, "timeout:", sizeof "timeout:" - 1))
3618 return DNS_RESCONF_TIMEOUT;
3620 if (0 == strncasecmp(word, "attempts:", sizeof "attempts:" - 1))
3621 return DNS_RESCONF_ATTEMPTS;
3623 if (0 == strncasecmp(word, "tcp:", sizeof "tcp:" - 1))
3624 return DNS_RESCONF_TCPx;
3627 } /* dns_resconf_keyword() */
3630 /** OpenBSD-style "[1.2.3.4]:53" nameserver syntax */
3631 static int dns_resconf_pton(struct sockaddr_storage *ss, const char *src) {
3632 struct { char buf[128], *p; } addr = { "", addr.buf };
3633 unsigned short port = 0;
3634 int ch, af = AF_INET;
3636 while ((ch = *src++)) {
3645 while ((ch = *src++)) {
3646 if (isdigit((unsigned char)ch)) {
3658 if (addr.p < endof(addr.buf) - 1)
3666 switch (dns_inet_pton(af, addr.buf, dns_sa_addr(af, ss))) {
3673 port = (!port)? 53 : port;
3674 *dns_sa_port(af, ss) = htons(port);
3675 dns_sa_family(ss) = af;
3678 } /* dns_resconf_pton() */
3680 #define dns_resconf_issep(ch) (isspace(ch) || (ch) == ',')
3681 #define dns_resconf_iscom(ch) ((ch) == '#' || (ch) == ';')
3683 int dns_resconf_loadfile(struct dns_resolv_conf *resconf, FILE *fp) {
3684 unsigned sa_count = 0;
3685 char words[6][DNS_D_MAXNAME + 1];
3686 unsigned wp, wc, i, j, n;
3692 memset(words, '\0', sizeof words);
3696 while (EOF != (ch = getc(fp)) && ch != '\n') {
3697 if (dns_resconf_issep(ch)) {
3701 if (++wc >= lengthof(words))
3704 } else if (dns_resconf_iscom(ch)) {
3708 } while (ch != EOF && ch != '\n');
3712 dns__printchar(words[wc], sizeof words[wc], wp, ch);
3723 switch (dns_resconf_keyword(words[0])) {
3724 case DNS_RESCONF_NAMESERVER:
3725 if (sa_count >= lengthof(resconf->nameserver))
3728 if ((error = dns_resconf_pton(&resconf->nameserver[sa_count], words[1])))
3734 case DNS_RESCONF_DOMAIN:
3735 case DNS_RESCONF_SEARCH:
3736 memset(resconf->search, '\0', sizeof resconf->search);
3738 for (i = 1, j = 0; i < wc && j < lengthof(resconf->search); i++, j++)
3739 dns_d_anchor(resconf->search[j], sizeof resconf->search[j], words[i], strlen(words[i]));
3742 case DNS_RESCONF_LOOKUP:
3743 for (i = 1, j = 0; i < wc && j < lengthof(resconf->lookup); i++) {
3744 switch (dns_resconf_keyword(words[i])) {
3745 case DNS_RESCONF_FILE:
3746 resconf->lookup[j++] = 'f';
3749 case DNS_RESCONF_BIND:
3750 resconf->lookup[j++] = 'b';
3753 case DNS_RESCONF_CACHE:
3754 resconf->lookup[j++] = 'c';
3763 case DNS_RESCONF_OPTIONS:
3764 for (i = 1; i < wc; i++) {
3765 switch (dns_resconf_keyword(words[i])) {
3766 case DNS_RESCONF_EDNS0:
3767 resconf->options.edns0 = 1;
3770 case DNS_RESCONF_NDOTS:
3771 for (j = sizeof "ndots:" - 1, n = 0; isdigit((int)words[i][j]); j++) {
3773 n += words[i][j] - '0';
3776 resconf->options.ndots = n;
3779 case DNS_RESCONF_TIMEOUT:
3780 for (j = sizeof "timeout:" - 1, n = 0; isdigit((int)words[i][j]); j++) {
3782 n += words[i][j] - '0';
3785 resconf->options.timeout = n;
3788 case DNS_RESCONF_ATTEMPTS:
3789 for (j = sizeof "attempts:" - 1, n = 0; isdigit((int)words[i][j]); j++) {
3791 n += words[i][j] - '0';
3794 resconf->options.attempts = n;
3797 case DNS_RESCONF_ROTATE:
3798 resconf->options.rotate = 1;
3801 case DNS_RESCONF_RECURSE:
3802 resconf->options.recurse = 1;
3805 case DNS_RESCONF_SMART:
3806 resconf->options.smart = 1;
3809 case DNS_RESCONF_TCP:
3810 resconf->options.tcp = DNS_RESCONF_TCP_ONLY;
3813 case DNS_RESCONF_TCPx:
3814 switch (dns_resconf_keyword(&words[i][sizeof "tcp:" - 1])) {
3815 case DNS_RESCONF_ENABLE:
3816 resconf->options.tcp = DNS_RESCONF_TCP_ENABLE;
3819 case DNS_RESCONF_ONE:
3820 case DNS_RESCONF_ONLY:
3821 resconf->options.tcp = DNS_RESCONF_TCP_ONLY;
3824 case DNS_RESCONF_ZERO:
3825 case DNS_RESCONF_DISABLE:
3826 resconf->options.tcp = DNS_RESCONF_TCP_DISABLE;
3840 case DNS_RESCONF_INTERFACE:
3841 for (i = 0, n = 0; isdigit((int)words[2][i]); i++) {
3843 n += words[2][i] - '0';
3846 dns_resconf_setiface(resconf, words[1], n);
3852 } while (ch != EOF);
3855 } /* dns_resconf_loadfile() */
3858 int dns_resconf_loadpath(struct dns_resolv_conf *resconf, const char *path) {
3862 if (!(fp = fopen(path, "r")))
3865 error = dns_resconf_loadfile(resconf, fp);
3870 } /* dns_resconf_loadpath() */
3873 int dns_resconf_setiface(struct dns_resolv_conf *resconf, const char *addr, unsigned short port) {
3874 int af = (strchr(addr, ':'))? AF_INET6 : AF_INET;
3876 if (1 != dns_inet_pton(af, addr, dns_sa_addr(af, &resconf->iface)))
3879 *dns_sa_port(af, &resconf->iface) = htons(port);
3880 resconf->iface.ss_family = af;
3883 } /* dns_resconf_setiface() */
3886 size_t dns_resconf_search(void *dst, size_t lim, const void *qname, size_t qlen, struct dns_resolv_conf *resconf, dns_resconf_i_t *state) {
3887 unsigned srchi = 0xff & (*state >> 8);
3888 unsigned ndots = 0xff & (*state >> 16);
3889 unsigned slen, len = 0;
3890 const char *qp, *qe;
3892 // assert(0xff > lengthof(resconf->search));
3894 switch (0xff & *state) {
3899 while ((qp = memchr(qp, '.', qe - qp)))
3904 if (ndots >= resconf->options.ndots) {
3905 len = dns_d_anchor(dst, lim, qname, qlen);
3912 if (srchi < lengthof(resconf->search) && (slen = strlen(resconf->search[srchi]))) {
3913 len = dns__printstring(dst, lim, 0, qname, qlen);
3914 len = dns_d_anchor(dst, lim, dst, len);
3915 len += dns__printstring(dst, lim, len, resconf->search[srchi], slen);
3928 if (ndots < resconf->options.ndots) {
3929 len = dns_d_anchor(dst, lim, qname, qlen);
3939 dns__printnul(dst, lim, len);
3941 *state = ((0xff & *state) << 0)
3942 | ((0xff & srchi) << 8)
3943 | ((0xff & ndots) << 16);
3946 } /* dns_resconf_search() */
3949 int dns_resconf_dump(struct dns_resolv_conf *resconf, FILE *fp) {
3953 for (i = 0; i < lengthof(resconf->nameserver) && (af = resconf->nameserver[i].ss_family) != AF_UNSPEC; i++) {
3954 char addr[INET6_ADDRSTRLEN + 1] = "[INVALID]";
3955 unsigned short port;
3957 dns_inet_ntop(af, dns_sa_addr(af, &resconf->nameserver[i]), addr, sizeof addr);
3958 port = ntohs(*dns_sa_port(af, &resconf->nameserver[i]));
3961 fprintf(fp, "nameserver %s\n", addr);
3963 fprintf(fp, "nameserver [%s]:%hu\n", addr, port);
3967 fprintf(fp, "search");
3969 for (i = 0; i < lengthof(resconf->search) && resconf->search[i][0]; i++)
3970 fprintf(fp, " %s", resconf->search[i]);
3975 fprintf(fp, "lookup");
3977 for (i = 0; i < lengthof(resconf->lookup) && resconf->lookup[i]; i++) {
3978 switch (resconf->lookup[i]) {
3980 fprintf(fp, " bind"); break;
3982 fprintf(fp, " file"); break;
3984 fprintf(fp, " cache"); break;
3991 fprintf(fp, "options ndots:%u timeout:%u attempts:%u", resconf->options.ndots, resconf->options.timeout, resconf->options.attempts);
3993 if (resconf->options.edns0)
3994 fprintf(fp, " edns0");
3995 if (resconf->options.rotate)
3996 fprintf(fp, " rotate");
3997 if (resconf->options.recurse)
3998 fprintf(fp, " recurse");
3999 if (resconf->options.smart)
4000 fprintf(fp, " smart");
4005 if ((af = resconf->iface.ss_family) != AF_UNSPEC) {
4006 char addr[INET6_ADDRSTRLEN + 1] = "[INVALID]";
4008 dns_inet_ntop(af, dns_sa_addr(af, &resconf->iface), addr, sizeof addr);
4010 fprintf(fp, "interface %s %hu\n", addr, ntohs(*dns_sa_port(af, &resconf->iface)));
4014 } /* dns_resconf_dump() */
4018 * H I N T S E R V E R R O U T I N E S
4020 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
4022 struct dns_hints_soa {
4023 unsigned char zone[DNS_D_MAXNAME + 1];
4026 struct sockaddr_storage ss;
4032 struct dns_hints_soa *next;
4033 }; /* struct dns_hints_soa */
4037 dns_atomic_t refcount;
4039 struct dns_hints_soa *head;
4040 }; /* struct dns_hints */
4043 struct dns_hints *dns_hints_open(struct dns_resolv_conf *resconf __UNUSED__, int *error) {
4044 static const struct dns_hints H_initializer;
4045 struct dns_hints *H;
4047 if (!(H = malloc(sizeof *H)))
4052 dns_hints_acquire(H);
4056 *error = dns_syerr();
4061 } /* dns_hints_open() */
4064 void dns_hints_close(struct dns_hints *H) {
4065 struct dns_hints_soa *soa, *nxt;
4067 if (!H || 1 != dns_hints_release(H))
4070 for (soa = H->head; soa; soa = nxt) {
4079 } /* dns_hints_close() */
4082 unsigned dns_hints_acquire(struct dns_hints *H) {
4083 return dns_atomic_inc(&H->refcount);
4084 } /* dns_hints_acquire() */
4087 unsigned dns_hints_release(struct dns_hints *H) {
4088 return dns_atomic_dec(&H->refcount);
4089 } /* dns_hints_release() */
4092 struct dns_hints *dns_hints_mortal(struct dns_hints *hints) {
4094 dns_hints_release(hints);
4097 } /* dns_hints_mortal() */
4100 struct dns_hints *dns_hints_local(struct dns_resolv_conf *resconf, int *error_) {
4101 struct dns_hints *hints = 0;
4105 dns_resconf_acquire(resconf);
4106 else if (!(resconf = dns_resconf_local(&error)))
4109 if (!(hints = dns_hints_open(resconf, &error)))
4114 if (0 == dns_hints_insert_resconf(hints, ".", resconf, &error) && error)
4117 dns_resconf_close(resconf);
4123 dns_resconf_close(resconf);
4124 dns_hints_close(hints);
4127 } /* dns_hints_local() */
4130 struct dns_hints *dns_hints_root(struct dns_resolv_conf *resconf, int *error_) {
4131 static const struct {
4133 char addr[INET6_ADDRSTRLEN];
4135 { AF_INET, "198.41.0.4" }, /* A.ROOT-SERVERS.NET. */
4136 { AF_INET6, "2001:503:ba3e::2:30" }, /* A.ROOT-SERVERS.NET. */
4137 { AF_INET, "192.228.79.201" }, /* B.ROOT-SERVERS.NET. */
4138 { AF_INET, "192.33.4.12" }, /* C.ROOT-SERVERS.NET. */
4139 { AF_INET, "128.8.10.90" }, /* D.ROOT-SERVERS.NET. */
4140 { AF_INET, "192.203.230.10" }, /* E.ROOT-SERVERS.NET. */
4141 { AF_INET, "192.5.5.241" }, /* F.ROOT-SERVERS.NET. */
4142 { AF_INET6, "2001:500:2f::f" }, /* F.ROOT-SERVERS.NET. */
4143 { AF_INET, "192.112.36.4" }, /* G.ROOT-SERVERS.NET. */
4144 { AF_INET, "128.63.2.53" }, /* H.ROOT-SERVERS.NET. */
4145 { AF_INET6, "2001:500:1::803f:235" }, /* H.ROOT-SERVERS.NET. */
4146 { AF_INET, "192.36.148.17" }, /* I.ROOT-SERVERS.NET. */
4147 { AF_INET, "192.58.128.30" }, /* J.ROOT-SERVERS.NET. */
4148 { AF_INET6, "2001:503:c27::2:30" }, /* J.ROOT-SERVERS.NET. */
4150 struct dns_hints *hints = 0;
4151 struct sockaddr_storage ss;
4155 if (!(hints = dns_hints_open(resconf, &error)))
4158 for (i = 0; i < lengthof(root_hints); i++) {
4159 af = root_hints[i].af;
4161 if (1 != dns_inet_pton(af, root_hints[i].addr, dns_sa_addr(af, &ss)))
4164 *dns_sa_port(af, &ss) = htons(53);
4167 if ((error = dns_hints_insert(hints, ".", (struct sockaddr *)&ss, 1)))
4173 error = dns_soerr();
4179 dns_hints_close(hints);
4182 } /* dns_hints_root() */
4185 static struct dns_hints_soa *dns_hints_fetch(struct dns_hints *H, const char *zone) {
4186 struct dns_hints_soa *soa;
4188 for (soa = H->head; soa; soa = soa->next) {
4189 if (0 == strcasecmp(zone, (char *)soa->zone))
4194 } /* dns_hints_fetch() */
4197 int dns_hints_insert(struct dns_hints *H, const char *zone, const struct sockaddr *sa, unsigned priority) {
4198 static const struct dns_hints_soa soa_initializer;
4199 struct dns_hints_soa *soa;
4202 if (!(soa = dns_hints_fetch(H, zone))) {
4203 if (!(soa = malloc(sizeof *soa)))
4206 *soa = soa_initializer;
4208 dns__printstring(soa->zone, sizeof soa->zone, 0, zone);
4210 soa->next = H->head;
4214 i = soa->count % lengthof(soa->addrs);
4216 memcpy(&soa->addrs[i].ss, sa, dns_sa_len(sa));
4218 soa->addrs[i].priority = MAX(1, priority);
4220 if (soa->count < lengthof(soa->addrs))
4224 } /* dns_hints_insert() */
4227 unsigned dns_hints_insert_resconf(struct dns_hints *H, const char *zone, const struct dns_resolv_conf *resconf, int *error_) {
4231 for (i = 0, n = 0, p = 1; i < lengthof(resconf->nameserver) && resconf->nameserver[i].ss_family != AF_UNSPEC; i++, n++) {
4232 if ((error = dns_hints_insert(H, zone, (struct sockaddr *)&resconf->nameserver[i], p)))
4235 p += !resconf->options.rotate;
4243 } /* dns_hints_insert_resconf() */
4246 static int dns_hints_i_cmp(unsigned a, unsigned b, struct dns_hints_i *i, struct dns_hints_soa *soa) {
4249 if ((cmp = soa->addrs[a].priority - soa->addrs[b].priority))
4252 return dns_k_shuffle16(a, i->state.seed) - dns_k_shuffle16(b, i->state.seed);
4253 } /* dns_hints_i_cmp() */
4256 static unsigned dns_hints_i_start(struct dns_hints_i *i, struct dns_hints_soa *soa) {
4261 for (p = 1; p < soa->count; p++) {
4262 if (dns_hints_i_cmp(p, p0, i, soa) < 0)
4267 } /* dns_hints_i_start() */
4270 static unsigned dns_hints_i_skip(unsigned p0, struct dns_hints_i *i, struct dns_hints_soa *soa) {
4273 for (pZ = 0; pZ < soa->count; pZ++) {
4274 if (dns_hints_i_cmp(pZ, p0, i, soa) > 0)
4280 for (p = pZ + 1; p < soa->count; p++) {
4281 if (dns_hints_i_cmp(p, p0, i, soa) <= 0)
4284 if (dns_hints_i_cmp(p, pZ, i, soa) >= 0)
4292 } /* dns_hints_i_skip() */
4295 struct dns_hints_i *dns_hints_i_init(struct dns_hints_i *i, struct dns_hints *hints) {
4296 static const struct dns_hints_i i_initializer;
4297 struct dns_hints_soa *soa;
4299 i->state = i_initializer.state;
4302 i->state.seed = dns_random();
4303 } while (0 == i->state.seed);
4305 if ((soa = dns_hints_fetch(hints, i->zone))) {
4306 i->state.next = dns_hints_i_start(i, soa);
4310 } /* dns_hints_i_init() */
4313 unsigned dns_hints_grep(struct sockaddr **sa, socklen_t *sa_len, unsigned lim, struct dns_hints_i *i, struct dns_hints *H) {
4314 struct dns_hints_soa *soa;
4317 if (!(soa = dns_hints_fetch(H, i->zone)))
4322 while (i->state.next < soa->count && n < lim) {
4323 *sa = (struct sockaddr *)&soa->addrs[i->state.next].ss;
4324 *sa_len = dns_sa_len(*sa);
4330 i->state.next = dns_hints_i_skip(i->state.next, i, soa);
4334 } /* dns_hints_grep() */
4337 struct dns_packet *dns_hints_query(struct dns_hints *hints, struct dns_packet *Q, int *error_) {
4338 struct dns_packet *A, *P;
4340 char zone[DNS_D_MAXNAME + 1];
4342 struct dns_hints_i i;
4343 struct sockaddr *sa;
4347 if (!dns_rr_grep(&rr, 1, dns_rr_i_new(Q, .section = DNS_S_QUESTION), Q, &error))
4350 if (!(zlen = dns_d_expand(zone, sizeof zone, rr.dn.p, Q, &error)))
4352 else if (zlen >= sizeof zone)
4356 dns_header(P)->qr = 1;
4358 if ((error = dns_rr_copy(P, &rr, Q)))
4361 if ((error = dns_p_push(P, DNS_S_AUTHORITY, ".", strlen("."), DNS_T_NS, DNS_C_IN, 0, "hints.local.")))
4367 dns_hints_i_init(&i, hints);
4369 while (dns_hints_grep(&sa, &slen, 1, &i, hints)) {
4370 int af = sa->sa_family;
4371 int rtype = (af == AF_INET6)? DNS_T_AAAA : DNS_T_A;
4373 if ((error = dns_p_push(P, DNS_S_ADDITIONAL, "hints.local.", strlen("hints.local."), rtype, DNS_C_IN, 0, dns_sa_addr(af, sa))))
4376 } while ((zlen = dns_d_cleave(zone, sizeof zone, zone, zlen)));
4378 if (!(A = dns_p_copy(dns_p_make(P->end, &error), P)))
4383 error = DNS_EILLEGAL;
4388 } /* dns_hints_query() */
4391 /** ugly hack to support specifying ports other than 53 in resolv.conf. */
4392 static unsigned short dns_hints_port(struct dns_hints *hints, int af, void *addr) {
4393 struct dns_hints_soa *soa;
4394 unsigned short port;
4397 for (soa = hints->head; soa; soa = soa->next) {
4398 for (i = 0; i < soa->count; i++) {
4399 if (af != soa->addrs[i].ss.ss_family)
4402 if (memcmp(addr, dns_sa_addr(af, &soa->addrs[i].ss), (af == AF_INET6)? sizeof (struct in6_addr) : sizeof (struct in_addr)))
4405 port = *dns_sa_port(af, &soa->addrs[i].ss);
4407 return (port)? port : htons(53);
4412 } /* dns_hints_port() */
4415 int dns_hints_dump(struct dns_hints *hints, FILE *fp) {
4416 struct dns_hints_soa *soa;
4417 char addr[INET6_ADDRSTRLEN];
4421 for (soa = hints->head; soa; soa = soa->next) {
4422 fprintf(fp, "ZONE \"%s\"\n", soa->zone);
4424 for (i = 0; i < soa->count; i++) {
4425 af = soa->addrs[i].ss.ss_family;
4426 if (!dns_inet_ntop(af, dns_sa_addr(af, &soa->addrs[i].ss), addr, sizeof addr))
4429 fprintf(fp, "\t(%d) [%s]:%hu\n", (int)soa->addrs[i].priority, addr, ntohs(*dns_sa_port(af, &soa->addrs[i].ss)));
4434 } /* dns_hints_dump() */
4438 * C A C H E R O U T I N E S
4440 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
4442 static dns_atomic_t dns_cache_acquire(struct dns_cache *cache __UNUSED__) {
4444 } /* dns_cache_acquire() */
4447 static dns_atomic_t dns_cache_release(struct dns_cache *cache __UNUSED__) {
4449 } /* dns_cache_release() */
4452 static struct dns_packet *dns_cache_query(struct dns_packet *query __UNUSED__, struct dns_cache *cache __UNUSED__, int *error __UNUSED__) {
4454 } /* dns_cache_submit() */
4457 static int dns_cache_submit(struct dns_packet *query __UNUSED__, struct dns_cache *cache __UNUSED__) {
4459 } /* dns_cache_submit() */
4462 static int dns_cache_check(struct dns_cache *cache __UNUSED__) {
4464 } /* dns_cache_check() */
4467 static struct dns_packet *dns_cache_fetch(struct dns_cache *cache __UNUSED__, int *error __UNUSED__) {
4469 } /* dns_cache_fetch() */
4472 static int dns_cache_pollfd(struct dns_cache *cache __UNUSED__) {
4474 } /* dns_cache_pollfd() */
4477 static short dns_cache_events(struct dns_cache *cache __UNUSED__) {
4479 } /* dns_cache_events() */
4482 static void dns_cache_clear(struct dns_cache *cache __UNUSED__) {
4484 } /* dns_cache_clear() */
4487 struct dns_cache *dns_cache_init(struct dns_cache *cache) {
4488 static const struct dns_cache c_init = {
4489 .acquire = &dns_cache_acquire,
4490 .release = &dns_cache_release,
4491 .query = &dns_cache_query,
4492 .submit = &dns_cache_submit,
4493 .check = &dns_cache_check,
4494 .fetch = &dns_cache_fetch,
4495 .pollfd = &dns_cache_pollfd,
4496 .events = &dns_cache_events,
4497 .clear = &dns_cache_clear,
4503 } /* dns_cache_init() */
4506 void dns_cache_close(struct dns_cache *cache) {
4508 cache->release(cache);
4509 } /* dns_cache_close() */
4513 * S O C K E T R O U T I N E S
4515 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
4517 static void dns_socketclose(int *fd) {
4526 } /* dns_socketclose() */
4529 #define DNS_SO_MAXTRY 7
4531 static int dns_socket(struct sockaddr *local, int type, int *error_) {
4533 #if defined(O_NONBLOCK)
4535 #elif defined(FIONBIO)
4539 if (-1 == (fd = socket(local->sa_family, type, 0)))
4542 #if defined(F_SETFD)
4543 if (-1 == fcntl(fd, F_SETFD, 1))
4547 #if defined(O_NONBLOCK)
4548 if (-1 == (flags = fcntl(fd, F_GETFL)))
4551 if (-1 == fcntl(fd, F_SETFL, flags | O_NONBLOCK))
4553 #elif defined(FIONBIO)
4556 if (0 != ioctlsocket(fd, FIONBIO, &opt))
4560 if (local->sa_family != AF_INET && local->sa_family != AF_INET6)
4563 if (type != SOCK_DGRAM)
4566 if (*dns_sa_port(local->sa_family, local) == 0) {
4567 struct sockaddr_storage tmp;
4570 memcpy(&tmp, local, dns_sa_len(local));
4572 for (i = 0; i < DNS_SO_MAXTRY; i++) {
4573 port = 1025 + (dns_random() % 64510);
4575 *dns_sa_port(tmp.ss_family, &tmp) = htons(port);
4577 if (0 == bind(fd, (struct sockaddr *)&tmp, dns_sa_len(&tmp)))
4582 if (0 == bind(fd, local, dns_sa_len(local)))
4587 error = dns_soerr();
4591 error = dns_syerr();
4597 dns_socketclose(&fd);
4600 } /* dns_socket() */
4604 DNS_SO_UDP_INIT = 1,
4618 struct dns_options opts;
4624 unsigned onum, olim;
4628 struct sockaddr_storage local, remote;
4630 struct dns_k_permutor qids;
4632 struct dns_stat stat;
4635 * NOTE: dns_so_reset() zeroes everything from here down.
4640 char qname[DNS_D_MAXNAME + 1];
4642 enum dns_type qtype;
4643 enum dns_class qclass;
4645 struct dns_packet *query;
4650 struct dns_packet *answer;
4652 }; /* struct dns_socket() */
4656 * NOTE: Actual closure delayed so that kqueue(2) and epoll(2) callers have
4657 * a chance to recognize a state change after installing a persistent event
4658 * and where sequential descriptors with the same integer value returned
4659 * from _pollfd() would be ambiguous. See dns_so_closefds().
4661 static int dns_so_closefd(struct dns_socket *so, int *fd) {
4667 if (so->opts.closefd.cb) {
4668 if ((error = so->opts.closefd.cb(fd, so->opts.closefd.arg))) {
4670 } else if (*fd == -1)
4674 if (!(so->onum < so->olim)) {
4675 unsigned olim = MAX(4, so->olim * 2);
4678 if (!(old = realloc(so->old, sizeof so->old[0] * olim)))
4685 so->old[so->onum++] = *fd;
4689 } /* dns_so_closefd() */
4692 #define DNS_SO_CLOSE_UDP 0x01
4693 #define DNS_SO_CLOSE_TCP 0x02
4694 #define DNS_SO_CLOSE_OLD 0x04
4695 #define DNS_SO_CLOSE_ALL (DNS_SO_CLOSE_UDP|DNS_SO_CLOSE_TCP|DNS_SO_CLOSE_OLD)
4697 static void dns_so_closefds(struct dns_socket *so, int which) {
4698 if (DNS_SO_CLOSE_UDP & which)
4699 dns_socketclose(&so->udp);
4700 if (DNS_SO_CLOSE_TCP & which)
4701 dns_socketclose(&so->tcp);
4702 if (DNS_SO_CLOSE_OLD & which) {
4704 for (i = 0; i < so->onum; i++)
4705 dns_socketclose(&so->old[i]);
4711 } /* dns_so_closefds() */
4714 static void dns_so_destroy(struct dns_socket *);
4716 static struct dns_socket *dns_so_init(struct dns_socket *so, const struct sockaddr *local, int type, const struct dns_options *opts, int *error) {
4717 static const struct dns_socket so_initializer = { .opts = DNS_OPTS_INITIALIZER, .udp = -1, .tcp = -1, };
4719 *so = so_initializer;
4726 memcpy(&so->local, local, dns_sa_len(local));
4728 if (-1 == (so->udp = dns_socket((struct sockaddr *)&so->local, SOCK_DGRAM, error)))
4731 dns_k_permutor_init(&so->qids, 1, 65535);
4738 } /* dns_so_init() */
4741 struct dns_socket *dns_so_open(const struct sockaddr *local, int type, const struct dns_options *opts, int *error) {
4742 struct dns_socket *so;
4744 if (!(so = malloc(sizeof *so)))
4747 if (!dns_so_init(so, local, type, opts, error))
4752 *error = dns_syerr();
4757 } /* dns_so_open() */
4760 static void dns_so_destroy(struct dns_socket *so) {
4762 dns_so_closefds(so, DNS_SO_CLOSE_ALL);
4763 } /* dns_so_destroy() */
4766 void dns_so_close(struct dns_socket *so) {
4773 } /* dns_so_close() */
4776 void dns_so_reset(struct dns_socket *so) {
4779 memset(&so->state, '\0', sizeof *so - offsetof(struct dns_socket, state));
4780 } /* dns_so_reset() */
4783 unsigned short dns_so_mkqid(struct dns_socket *so) {
4784 return dns_k_permutor_step(&so->qids);
4785 } /* dns_so_mkqid() */
4788 #define DNS_SO_MINBUF 768
4790 static int dns_so_newanswer(struct dns_socket *so, size_t len) {
4791 size_t size = offsetof(struct dns_packet, data) + MAX(len, DNS_SO_MINBUF);
4794 if (!(p = realloc(so->answer, size)))
4797 so->answer = dns_p_init(p, size);
4800 } /* dns_so_newanswer() */
4803 int dns_so_submit(struct dns_socket *so, struct dns_packet *Q, struct sockaddr *host) {
4809 if ((error = dns_rr_parse(&rr, 12, Q)))
4812 if (!(so->qlen = dns_d_expand(so->qname, sizeof so->qname, rr.dn.p, Q, &error)))
4815 * NOTE: don't bail if expansion is too long; caller may be
4816 * intentionally sending long names. However, we won't be able to
4817 * verify it on return.
4820 so->qtype = rr.type;
4821 so->qclass = rr.class;
4823 if ((error = dns_so_newanswer(so, DNS_SO_MINBUF)))
4826 memcpy(&so->remote, host, dns_sa_len(host));
4830 so->began = dns_now();
4832 if (dns_header(so->query)->qid == 0)
4833 dns_header(so->query)->qid = dns_so_mkqid(so);
4835 so->qid = dns_header(so->query)->qid;
4836 so->state = (so->type == SOCK_STREAM)? DNS_SO_TCP_INIT : DNS_SO_UDP_INIT;
4842 error = dns_syerr();
4847 } /* dns_so_submit() */
4850 static int dns_so_verify(struct dns_socket *so, struct dns_packet *P) {
4851 char qname[DNS_D_MAXNAME + 1];
4856 if (so->qid != dns_header(so->answer)->qid)
4857 return DNS_EUNKNOWN;
4859 if (!dns_p_count(so->answer, DNS_S_QD))
4860 return DNS_EUNKNOWN;
4862 if (0 != dns_rr_parse(&rr, 12, so->answer))
4863 return DNS_EUNKNOWN;
4865 if (rr.type != so->qtype || rr.class != so->qclass)
4866 return DNS_EUNKNOWN;
4868 if (!(qlen = dns_d_expand(qname, sizeof qname, rr.dn.p, P, &error)))
4870 else if (qlen >= sizeof qname || qlen != so->qlen)
4871 return DNS_EUNKNOWN;
4873 if (0 != strcasecmp(so->qname, qname))
4874 return DNS_EUNKNOWN;
4877 } /* dns_so_verify() */
4880 static int dns_so_tcp_send(struct dns_socket *so) {
4881 unsigned char *qsrc;
4885 so->query->data[-2] = 0xff & (so->query->end >> 8);
4886 so->query->data[-1] = 0xff & (so->query->end >> 0);
4888 qsrc = &so->query->data[-2] + so->qout;
4889 qend = so->query->end + 2;
4891 while (so->qout < qend) {
4892 if (0 > (n = send(so->tcp, (void *)&qsrc[so->qout], qend - so->qout, 0)))
4896 so->stat.tcp.sent.bytes += n;
4899 so->stat.tcp.sent.count++;
4902 } /* dns_so_tcp_send() */
4905 static int dns_so_tcp_recv(struct dns_socket *so) {
4906 unsigned char *asrc;
4911 aend = so->alen + 2;
4913 while (so->apos < aend) {
4914 asrc = &so->answer->data[-2];
4916 if (0 > (n = recv(so->tcp, (void *)&asrc[so->apos], aend - so->apos, 0)))
4919 return DNS_EUNKNOWN; /* FIXME */
4922 so->stat.tcp.rcvd.bytes += n;
4924 if (so->alen == 0 && so->apos >= 2) {
4925 alen = ((0xff & so->answer->data[-2]) << 8)
4926 | ((0xff & so->answer->data[-1]) << 0);
4928 if ((error = dns_so_newanswer(so, alen)))
4936 so->answer->end = so->alen;
4937 so->stat.tcp.rcvd.count++;
4940 } /* dns_so_tcp_recv() */
4943 int dns_so_check(struct dns_socket *so) {
4948 switch (so->state) {
4949 case DNS_SO_UDP_INIT:
4951 case DNS_SO_UDP_CONN:
4952 if (0 != connect(so->udp, (struct sockaddr *)&so->remote, dns_sa_len(&so->remote)))
4956 case DNS_SO_UDP_SEND:
4957 if (0 > (n = send(so->udp, (void *)so->query->data, so->query->end, 0)))
4960 so->stat.udp.sent.bytes += n;
4961 so->stat.udp.sent.count++;
4964 case DNS_SO_UDP_RECV:
4965 if (0 > (n = recv(so->udp, (void *)so->answer->data, so->answer->size, 0)))
4968 so->stat.udp.rcvd.bytes += n;
4969 so->stat.udp.rcvd.count++;
4971 if ((so->answer->end = n) < 12)
4974 if ((error = dns_so_verify(so, so->answer)))
4978 case DNS_SO_UDP_DONE:
4979 if (!dns_header(so->answer)->tc || so->type == SOCK_DGRAM)
4983 case DNS_SO_TCP_INIT:
4984 if ((error = dns_so_closefd(so, &so->tcp)))
4987 if (-1 == (so->tcp = dns_socket((struct sockaddr *)&so->local, SOCK_STREAM, &error)))
4991 case DNS_SO_TCP_CONN:
4992 if (0 != connect(so->tcp, (struct sockaddr *)&so->remote, dns_sa_len(&so->remote))) {
4993 if (dns_soerr() != DNS_EISCONN)
4998 case DNS_SO_TCP_SEND:
4999 if ((error = dns_so_tcp_send(so)))
5003 case DNS_SO_TCP_RECV:
5004 if ((error = dns_so_tcp_recv(so)))
5008 case DNS_SO_TCP_DONE:
5009 if ((error = dns_so_closefd(so, &so->tcp)))
5012 if (so->answer->end < 12)
5013 return DNS_EILLEGAL;
5015 if ((error = dns_so_verify(so, so->answer)))
5020 error = DNS_EUNKNOWN;
5028 error = dns_soerr();
5035 case DNS_EINPROGRESS:
5039 #if DNS_EWOULDBLOCK != DNS_EAGAIN
5040 case DNS_EWOULDBLOCK:
5049 } /* dns_so_check() */
5052 struct dns_packet *dns_so_fetch(struct dns_socket *so, int *error) {
5053 struct dns_packet *answer;
5055 switch (so->state) {
5056 case DNS_SO_UDP_DONE:
5057 case DNS_SO_TCP_DONE:
5058 answer = so->answer;
5063 *error = DNS_EUNKNOWN;
5067 } /* dns_so_fetch() */
5070 struct dns_packet *dns_so_query(struct dns_socket *so, struct dns_packet *Q, struct sockaddr *host, int *error_) {
5071 struct dns_packet *A;
5075 if ((error = dns_so_submit(so, Q, host)))
5079 if ((error = dns_so_check(so)))
5082 if (!(A = dns_so_fetch(so, &error)))
5092 } /* dns_so_query() */
5095 time_t dns_so_elapsed(struct dns_socket *so) {
5096 return dns_elapsed(so->began);
5097 } /* dns_so_elapsed() */
5100 void dns_so_clear(struct dns_socket *so) {
5101 dns_so_closefds(so, DNS_SO_CLOSE_OLD);
5102 } /* dns_so_clear() */
5105 static int dns_so_events2(struct dns_socket *so, enum dns_events type) {
5108 switch (so->state) {
5109 case DNS_SO_UDP_CONN:
5110 case DNS_SO_UDP_SEND:
5111 events |= DNS_POLLOUT;
5114 case DNS_SO_UDP_RECV:
5115 events |= DNS_POLLIN;
5118 case DNS_SO_TCP_CONN:
5119 case DNS_SO_TCP_SEND:
5120 events |= DNS_POLLOUT;
5123 case DNS_SO_TCP_RECV:
5124 events |= DNS_POLLIN;
5131 return DNS_POLL2EV(events);
5135 } /* dns_so_events2() */
5138 int dns_so_events(struct dns_socket *so) {
5139 return dns_so_events2(so, so->opts.events);
5140 } /* dns_so_events() */
5143 int dns_so_pollfd(struct dns_socket *so) {
5144 switch (so->state) {
5145 case DNS_SO_UDP_CONN:
5146 case DNS_SO_UDP_SEND:
5147 case DNS_SO_UDP_RECV:
5149 case DNS_SO_TCP_CONN:
5150 case DNS_SO_TCP_SEND:
5151 case DNS_SO_TCP_RECV:
5156 } /* dns_so_pollfd() */
5159 int dns_so_poll(struct dns_socket *so, int timeout) {
5160 return dns_poll(dns_so_pollfd(so), dns_so_events2(so, DNS_SYSPOLL), timeout);
5161 } /* dns_so_poll() */
5164 const struct dns_stat *dns_so_stat(struct dns_socket *so) {
5166 } /* dns_so_stat() */
5170 * R E S O L V E R R O U T I N E S
5172 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
5174 enum dns_res_state {
5177 DNS_R_SWITCH, /* (B)IND, (F)ILE, (C)ACHE */
5179 DNS_R_FILE, /* Lookup in local hosts database */
5181 DNS_R_CACHE, /* Lookup in application cache */
5186 DNS_R_BIND, /* Lookup in the network */
5191 DNS_R_RESOLV0_NS, /* Prologue: Setup next frame and recurse */
5192 DNS_R_RESOLV1_NS, /* Epilog: Inspect answer */
5203 }; /* enum dns_res_state */
5206 #define DNS_R_MAXDEPTH 8
5207 #define DNS_R_ENDFRAME (DNS_R_MAXDEPTH - 1)
5209 struct dns_resolver {
5210 struct dns_socket so;
5212 struct dns_resolv_conf *resconf;
5213 struct dns_hosts *hosts;
5214 struct dns_hints *hints;
5215 struct dns_cache *cache;
5217 dns_atomic_t refcount;
5219 /* Reset zeroes everything below here. */
5221 char qname[DNS_D_MAXNAME + 1];
5224 enum dns_type qtype;
5225 enum dns_class qclass;
5229 dns_resconf_i_t search;
5231 struct dns_rr_i smart;
5233 struct dns_res_frame {
5234 enum dns_res_state state;
5237 int which; /* (B)IND, (F)ILE; index into resconf->lookup */
5241 struct dns_packet *query, *answer, *hints;
5243 struct dns_rr_i hints_i, hints_j;
5244 struct dns_rr hints_ns, ans_cname;
5245 } stack[DNS_R_MAXDEPTH];
5248 }; /* struct dns_resolver */
5251 static int dns_res_tcp2type(int tcp) {
5253 case DNS_RESCONF_TCP_ONLY:
5255 case DNS_RESCONF_TCP_DISABLE:
5260 } /* dns_res_tcp2type() */
5262 struct dns_resolver *dns_res_open(struct dns_resolv_conf *resconf, struct dns_hosts *hosts, struct dns_hints *hints, struct dns_cache *cache, const struct dns_options *opts, int *error_) {
5263 static const struct dns_resolver R_initializer
5264 = { .refcount = 1, };
5265 struct dns_resolver *R = 0;
5269 * Grab ref count early because the caller may have passed us a mortal
5270 * reference, and we want to do the right thing if we return early
5274 dns_resconf_acquire(resconf);
5276 dns_hosts_acquire(hosts);
5278 dns_hints_acquire(hints);
5280 dns_cache_acquire(cache);
5283 * Don't try to load it ourselves because a NULL object might be an
5284 * error from, say, dns_resconf_root(), and loading
5285 * dns_resconf_local() by default would create undesirable surpises.
5287 if (!resconf || !hosts || !hints)
5290 if (!(R = malloc(sizeof *R)))
5294 type = dns_res_tcp2type(resconf->options.tcp);
5296 if (!dns_so_init(&R->so, (struct sockaddr *)&resconf->iface, type, opts, &error))
5299 R->resconf = resconf;
5306 error = dns_syerr();
5312 dns_resconf_close(resconf);
5313 dns_hosts_close(hosts);
5314 dns_hints_close(hints);
5315 dns_cache_close(cache);
5318 } /* dns_res_open() */
5321 struct dns_resolver *dns_res_stub(const struct dns_options *opts, int *error) {
5322 struct dns_resolv_conf *resconf = 0;
5323 struct dns_hosts *hosts = 0;
5324 struct dns_hints *hints = 0;
5325 struct dns_resolver *res = 0;
5327 if (!(resconf = dns_resconf_local(error)))
5330 if (!(hosts = dns_hosts_local(error)))
5333 if (!(hints = dns_hints_local(resconf, error)))
5336 if (!(res = dns_res_open(resconf, hosts, hints, NULL, opts, error)))
5340 dns_resconf_close(resconf);
5341 dns_hosts_close(hosts);
5342 dns_hints_close(hints);
5345 } /* dns_res_stub() */
5348 static void dns_res_reset_frame(struct dns_resolver *R __UNUSED__, struct dns_res_frame *frame) {
5350 free(frame->answer);
5353 memset(frame, '\0', sizeof *frame);
5354 } /* dns_res_reset_frame() */
5357 void dns_res_reset(struct dns_resolver *R) {
5360 dns_so_reset(&R->so);
5362 for (i = 0; i < lengthof(R->stack); i++)
5363 dns_res_reset_frame(R, &R->stack[i]);
5365 memset(&R->qname, '\0', sizeof *R - offsetof(struct dns_resolver, qname));
5366 } /* dns_res_reset() */
5369 void dns_res_close(struct dns_resolver *R) {
5370 if (!R || 1 < dns_res_release(R))
5375 dns_so_destroy(&R->so);
5377 dns_hints_close(R->hints);
5378 dns_hosts_close(R->hosts);
5379 dns_resconf_close(R->resconf);
5380 dns_cache_close(R->cache);
5383 } /* dns_res_close() */
5386 unsigned dns_res_acquire(struct dns_resolver *R) {
5387 return dns_atomic_inc(&R->refcount);
5388 } /* dns_res_acquire() */
5391 unsigned dns_res_release(struct dns_resolver *R) {
5392 return dns_atomic_dec(&R->refcount);
5393 } /* dns_res_release() */
5396 struct dns_resolver *dns_res_mortal(struct dns_resolver *res) {
5398 dns_res_release(res);
5400 } /* dns_res_mortal() */
5403 static struct dns_packet *dns_res_merge(struct dns_packet *P0, struct dns_packet *P1, int *error_) {
5404 size_t bufsiz = P0->end + P1->end;
5405 struct dns_packet *P[3] = { P0, P1, 0 };
5406 struct dns_rr rr[3];
5408 enum dns_section section;
5411 if (!(P[2] = dns_p_make(bufsiz, &error)))
5414 dns_rr_foreach(&rr[0], P[0], .section = DNS_S_QD) {
5415 if ((error = dns_rr_copy(P[2], &rr[0], P[0])))
5419 for (section = DNS_S_AN; (DNS_S_ALL & section); section <<= 1) {
5420 for (i = 0; i < 2; i++) {
5421 dns_rr_foreach(&rr[i], P[i], .section = section) {
5424 dns_rr_foreach(&rr[2], P[2], .type = rr[i].type, .section = (DNS_S_ALL & ~DNS_S_QD)) {
5425 if (0 == dns_rr_cmp(&rr[i], P[i], &rr[2], P[2])) {
5432 if (copy && (error = dns_rr_copy(P[2], &rr[i], P[i]))) {
5433 if (error == DNS_ENOBUFS && bufsiz < 65535) {
5434 free(P[2]); P[2] = 0;
5436 bufsiz = MAX(65535, bufsiz * 2);
5444 } /* foreach(packet) */
5445 } /* foreach(section) */
5454 } /* dns_res_merge() */
5457 static struct dns_packet *dns_res_glue(struct dns_resolver *R, struct dns_packet *Q) {
5458 struct dns_packet *P = dns_p_new(512);
5459 char qname[DNS_D_MAXNAME + 1];
5461 enum dns_type qtype;
5466 if (!(qlen = dns_d_expand(qname, sizeof qname, 12, Q, &error))
5467 || qlen >= sizeof qname)
5470 if (!(qtype = dns_rr_type(12, Q)))
5473 if ((error = dns_p_push(P, DNS_S_QD, qname, strlen(qname), qtype, DNS_C_IN, 0, 0)))
5476 for (sp = 0; sp <= R->sp; sp++) {
5477 if (!R->stack[sp].answer)
5480 dns_rr_foreach(&rr, R->stack[sp].answer, .name = qname, .type = qtype, .section = (DNS_S_ALL & ~DNS_S_QD)) {
5481 rr.section = DNS_S_AN;
5483 if ((error = dns_rr_copy(P, &rr, R->stack[sp].answer)))
5488 if (dns_p_count(P, DNS_S_AN) > 0)
5491 /* Otherwise, look for a CNAME */
5492 for (sp = 0; sp <= R->sp; sp++) {
5493 if (!R->stack[sp].answer)
5496 dns_rr_foreach(&rr, R->stack[sp].answer, .name = qname, .type = DNS_T_CNAME, .section = (DNS_S_ALL & ~DNS_S_QD)) {
5497 rr.section = DNS_S_AN;
5499 if ((error = dns_rr_copy(P, &rr, R->stack[sp].answer)))
5504 if (!dns_p_count(P, DNS_S_AN))
5508 return dns_p_copy(dns_p_make(P->end, &error), P);
5509 } /* dns_res_glue() */
5512 static struct dns_packet *dns_res_mkquery(struct dns_resolver *R, const char *qname, enum dns_type qtype, enum dns_class qclass, int *error_) {
5513 struct dns_packet *Q = 0;
5516 if (!(Q = dns_p_init(malloc(DNS_P_QBUFSIZ), DNS_P_QBUFSIZ)))
5519 if ((error = dns_p_push(Q, DNS_S_QD, qname, strlen(qname), qtype, qclass, 0, 0)))
5522 dns_header(Q)->rd = !R->resconf->options.recurse;
5526 error = dns_syerr();
5533 } /* dns_res_mkquery() */
5537 * Sort NS records by three criteria:
5539 * 1) Whether glue is present.
5540 * 2) Whether glue record is original or of recursive lookup.
5541 * 3) Randomly shuffle records which share the above criteria.
5543 * NOTE: Assumes only NS records passed, AND ASSUMES no new NS records will
5544 * be added during an iteration.
5546 * FIXME: Only groks A glue, not AAAA glue.
5548 static int dns_res_nameserv_cmp(struct dns_rr *a, struct dns_rr *b, struct dns_rr_i *i, struct dns_packet *P) {
5549 _Bool glued[2] = { 0 };
5554 if (!(error = dns_ns_parse(&ns, a, P)))
5555 if (!(glued[0] = !!dns_rr_grep(&x, 1, dns_rr_i_new(P, .section = (DNS_S_ALL & ~DNS_S_QD), .name = ns.host, .type = DNS_T_A), P, &error)))
5558 if (!(error = dns_ns_parse(&ns, b, P)))
5559 if (!(glued[1] = !!dns_rr_grep(&y, 1, dns_rr_i_new(P, .section = (DNS_S_ALL & ~DNS_S_QD), .name = ns.host, .type = DNS_T_A), P, &error)))
5562 if ((cmp = glued[1] - glued[0]))
5564 else if ((cmp = (dns_rr_offset(&y) < i->args[0]) - (dns_rr_offset(&x) < i->args[0])))
5567 return dns_rr_i_shuffle(a, b, i, P);
5568 } /* dns_res_nameserv_cmp() */
5571 #define goto(sp, i) \
5572 do { R->stack[(sp)].state = (i); goto exec; } while (0)
5574 static int dns_res_exec(struct dns_resolver *R) {
5575 struct dns_res_frame *F;
5576 struct dns_packet *P;
5577 char host[DNS_D_MAXNAME + 1];
5580 struct sockaddr_in sin;
5585 F = &R->stack[R->sp];
5592 goto(R->sp, DNS_R_SWITCH);
5596 if (!(F->answer = dns_res_glue(R, F->query)))
5597 goto(R->sp, DNS_R_SWITCH);
5599 if (!(len = dns_d_expand(host, sizeof host, 12, F->query, &error)))
5601 else if (len >= sizeof host)
5604 dns_rr_foreach(&rr, F->answer, .name = host, .type = dns_rr_type(12, F->query), .section = DNS_S_AN) {
5605 goto(R->sp, DNS_R_FINISH);
5608 dns_rr_foreach(&rr, F->answer, .name = host, .type = DNS_T_CNAME, .section = DNS_S_AN) {
5611 goto(R->sp, DNS_R_CNAME0_A);
5616 while (F->which < (int)sizeof R->resconf->lookup) {
5617 switch (R->resconf->lookup[F->which++]) {
5619 goto(R->sp, DNS_R_BIND);
5621 goto(R->sp, DNS_R_FILE);
5624 goto(R->sp, DNS_R_CACHE);
5632 goto(R->sp, DNS_R_SERVFAIL); /* FIXME: Right behavior? */
5637 if (!(F->answer = dns_hosts_query(R->hosts, F->query, &error)))
5640 if (dns_p_count(F->answer, DNS_S_AN) > 0)
5641 goto(R->sp, DNS_R_FINISH);
5643 free(F->answer); F->answer = 0;
5647 while ((len = dns_resconf_search(host, sizeof host, R->qname, R->qlen, R->resconf, &R->search))) {
5649 * FIXME: Some sort of bug, either with this code or with GCC 3.3.5 on
5650 * OpenBSD 4.4, overwites the stack guard. If the bug is in this file, it
5651 * appears to be localized somewhere around here. It can also be mitigated
5652 * in dns_hosts_query(). In any event, the bug manifests only when using
5653 * compound literals. alloca(), malloc(), calloc(), etc, all work fine.
5654 * Valgrind (tested on Linux) cannot detect any issues, but stack issues are
5655 * not Valgrind's forte. Neither can I spot anything in the assembly, but
5656 * that's not my forte.
5658 #if __OpenBSD__ && __GNUC__
5659 struct dns_packet *query = __builtin_alloca(DNS_P_QBUFSIZ);
5661 dns_p_init(query, DNS_P_QBUFSIZ);
5663 struct dns_packet *query = dns_p_new(DNS_P_QBUFSIZ);
5666 if ((error = dns_p_push(query, DNS_S_QD, host, len, R->qtype, R->qclass, 0, 0)))
5671 if (!(F->answer = dns_hosts_query(R->hosts, query, &error)))
5674 if (dns_p_count(F->answer, DNS_S_AN) > 0)
5675 goto(R->sp, DNS_R_FINISH);
5677 free(F->answer); F->answer = 0;
5681 goto(R->sp, DNS_R_SWITCH);
5685 if (!F->query && !(F->query = dns_res_mkquery(R, R->qname, R->qtype, R->qclass, &error)))
5690 if ((F->answer = R->cache->query(F->query, R->cache, &error))) {
5691 if (dns_p_count(F->answer, DNS_S_AN) > 0)
5692 goto(R->sp, DNS_R_FINISH);
5694 free(F->answer); F->answer = 0;
5696 goto(R->sp, DNS_R_SWITCH);
5702 if ((error = R->cache->submit(F->query, R->cache)))
5707 if ((error = R->cache->check(R->cache)))
5716 if ((F->answer = R->cache->fetch(R->cache, &error))) {
5717 if (dns_p_count(F->answer, DNS_S_AN) > 0)
5718 goto(R->sp, DNS_R_FINISH);
5720 free(F->answer); F->answer = 0;
5722 goto(R->sp, DNS_R_SWITCH);
5726 goto(R->sp, DNS_R_SWITCH);
5731 goto(R->sp, DNS_R_HINTS);
5738 if (!(len = dns_resconf_search(host, sizeof host, R->qname, R->qlen, R->resconf, &R->search)))
5739 goto(R->sp, DNS_R_SWITCH);
5741 if (!(P = dns_p_make(DNS_P_QBUFSIZ, &error)))
5744 dns_header(P)->rd = !R->resconf->options.recurse;
5746 free(F->query); F->query = P;
5748 if ((error = dns_p_push(F->query, DNS_S_QD, host, len, R->qtype, R->qclass, 0, 0)))
5753 if (!(F->hints = dns_hints_query(R->hints, F->query, &error)))
5758 dns_rr_i_init(&F->hints_i, F->hints);
5760 F->hints_i.section = DNS_S_AUTHORITY;
5761 F->hints_i.type = DNS_T_NS;
5762 F->hints_i.sort = &dns_res_nameserv_cmp;
5763 F->hints_i.args[0] = F->hints->end;
5766 case DNS_R_FOREACH_NS:
5767 dns_rr_i_save(&F->hints_i);
5769 /* Load our next nameserver host. */
5770 if (!dns_rr_grep(&F->hints_ns, 1, &F->hints_i, F->hints, &error)) {
5771 if (++F->attempts < R->resconf->options.attempts)
5772 goto(R->sp, DNS_R_ITERATE);
5774 goto(R->sp, DNS_R_SWITCH);
5777 dns_rr_i_init(&F->hints_j, F->hints);
5779 /* Assume there are glue records */
5780 goto(R->sp, DNS_R_FOREACH_A);
5781 case DNS_R_RESOLV0_NS:
5782 /* Have we reached our max depth? */
5783 if (&F[1] >= endof(R->stack))
5784 goto(R->sp, DNS_R_FOREACH_NS);
5786 dns_res_reset_frame(R, &F[1]);
5788 if (!(F[1].query = dns_p_make(DNS_P_QBUFSIZ, &error)))
5791 if ((error = dns_ns_parse((struct dns_ns *)host, &F->hints_ns, F->hints)))
5794 if ((error = dns_p_push(F[1].query, DNS_S_QD, host, strlen(host), DNS_T_A, DNS_C_IN, 0, 0)))
5799 goto(++R->sp, DNS_R_INIT);
5800 case DNS_R_RESOLV1_NS:
5801 if (!(len = dns_d_expand(host, sizeof host, 12, F[1].query, &error)))
5803 else if (len >= sizeof host)
5806 dns_rr_foreach(&rr, F[1].answer, .name = host, .type = DNS_T_A, .section = (DNS_S_ALL & ~DNS_S_QD)) {
5807 rr.section = DNS_S_AR;
5809 if ((error = dns_rr_copy(F->hints, &rr, F[1].answer)))
5812 dns_rr_i_rewind(&F->hints_i); /* Now there's glue. */
5815 goto(R->sp, DNS_R_FOREACH_NS);
5816 case DNS_R_FOREACH_A:
5818 * NOTE: Iterator initialized in DNS_R_FOREACH_NS because
5819 * this state is re-entrant, but we need to reset
5820 * .name to a valid pointer each time.
5822 if ((error = dns_ns_parse((struct dns_ns *)host, &F->hints_ns, F->hints)))
5825 F->hints_j.name = host;
5826 F->hints_j.type = DNS_T_A;
5827 F->hints_j.section = DNS_S_ALL & ~DNS_S_QD;
5829 if (!dns_rr_grep(&rr, 1, &F->hints_j, F->hints, &error)) {
5830 if (!dns_rr_i_count(&F->hints_j))
5831 goto(R->sp, DNS_R_RESOLV0_NS);
5833 goto(R->sp, DNS_R_FOREACH_NS);
5836 sin.sin_family = AF_INET;
5838 if ((error = dns_a_parse((struct dns_a *)&sin.sin_addr, &rr, F->hints)))
5842 sin.sin_port = dns_hints_port(R->hints, AF_INET, (struct sockaddr *)&sin.sin_addr);
5844 sin.sin_port = htons(53);
5847 char addr[INET_ADDRSTRLEN + 1];
5848 dns_a_print(addr, sizeof addr, (struct dns_a *)&sin.sin_addr);
5849 DNS_SHOW(F->query, "ASKING: %s/%s @ DEPTH: %u)", host, addr, R->sp);
5852 if ((error = dns_so_submit(&R->so, F->query, (struct sockaddr *)&sin)))
5857 if (dns_so_elapsed(&R->so) >= (time_t)R->resconf->options.timeout)
5858 goto(R->sp, DNS_R_FOREACH_A);
5860 if ((error = dns_so_check(&R->so)))
5865 if (!(F->answer = dns_so_fetch(&R->so, &error)))
5869 DNS_SHOW(F->answer, "ANSWER @ DEPTH: %u)", R->sp);
5872 if ((error = dns_rr_parse(&rr, 12, F->query)))
5875 if (!(len = dns_d_expand(host, sizeof host, rr.dn.p, F->query, &error)))
5877 else if (len >= sizeof host)
5880 dns_rr_foreach(&rr, F->answer, .section = DNS_S_AN, .name = host, .type = rr.type) {
5881 goto(R->sp, DNS_R_FINISH); /* Found */
5884 dns_rr_foreach(&rr, F->answer, .section = DNS_S_AN, .name = host, .type = DNS_T_CNAME) {
5887 goto(R->sp, DNS_R_CNAME0_A);
5890 if (!R->resconf->options.recurse)
5891 goto(R->sp, DNS_R_SWITCH);
5893 dns_rr_foreach(&rr, F->answer, .section = DNS_S_NS, .type = DNS_T_NS) {
5896 F->hints = F->answer;
5899 goto(R->sp, DNS_R_ITERATE);
5902 /* XXX: Should this go further up? */
5903 if (dns_header(F->answer)->aa)
5904 goto(R->sp, DNS_R_FINISH);
5906 goto(R->sp, DNS_R_FOREACH_A);
5907 case DNS_R_CNAME0_A:
5908 if (&F[1] >= endof(R->stack))
5909 goto(R->sp, DNS_R_FINISH);
5911 if ((error = dns_cname_parse((struct dns_cname *)host, &F->ans_cname, F->answer)))
5914 dns_res_reset_frame(R, &F[1]);
5916 if (!(F[1].query = dns_p_make(DNS_P_QBUFSIZ, &error)))
5919 if ((error = dns_p_push(F[1].query, DNS_S_QD, host, strlen(host), dns_rr_type(12, F->query), DNS_C_IN, 0, 0)))
5924 goto(++R->sp, DNS_R_INIT);
5925 case DNS_R_CNAME1_A:
5926 if (!(P = dns_res_merge(F->answer, F[1].answer, &error)))
5929 free(F->answer); F->answer = P;
5931 goto(R->sp, DNS_R_FINISH);
5935 if (!R->resconf->options.smart || R->sp > 0)
5936 goto(R->sp, DNS_R_DONE);
5938 R->smart.section = DNS_S_AN;
5939 R->smart.type = R->qtype;
5941 dns_rr_i_init(&R->smart, F->answer);
5944 case DNS_R_SMART0_A:
5945 if (&F[1] >= endof(R->stack))
5946 goto(R->sp, DNS_R_DONE);
5948 while (dns_rr_grep(&rr, 1, &R->smart, F->answer, &error)) {
5955 enum dns_type qtype;
5956 enum dns_class qclass;
5960 if ((error = dns_ns_parse(&rd.ns, &rr, F->answer)))
5969 if ((error = dns_mx_parse(&rd.mx, &rr, F->answer)))
5978 if ((error = dns_srv_parse(&rd.srv, &rr, F->answer)))
5981 qname = rd.srv.target;
5990 dns_res_reset_frame(R, &F[1]);
5992 if (!(F[1].query = dns_res_mkquery(R, qname, qtype, qclass, &error)))
5997 goto(++R->sp, DNS_R_INIT);
6001 * NOTE: SMTP specification says to fallback to A record.
6003 * XXX: Should we add a mock MX answer?
6005 if (R->qtype == DNS_T_MX && R->smart.state.count == 0) {
6006 dns_res_reset_frame(R, &F[1]);
6008 if (!(F[1].query = dns_res_mkquery(R, R->qname, DNS_T_A, DNS_C_IN, &error)))
6011 R->smart.state.count++;
6014 goto(++R->sp, DNS_R_INIT);
6017 goto(R->sp, DNS_R_DONE);
6018 case DNS_R_SMART1_A:
6019 assert(F[1].answer);
6022 * FIXME: For CNAME chains (which are typically illegal in
6023 * this context), we should rewrite the record host name
6024 * to the original smart qname. All the user cares about
6025 * is locating that A/AAAA record.
6027 dns_rr_foreach(&rr, F[1].answer, .section = DNS_S_AN, .type = DNS_T_A) {
6028 rr.section = DNS_S_AR;
6030 if (dns_rr_exists(&rr, F[1].answer, F->answer))
6033 while ((error = dns_rr_copy(F->answer, &rr, F[1].answer))) {
6034 if (error != DNS_ENOBUFS)
6036 if ((error = dns_p_grow(&F->answer)))
6041 goto(R->sp, DNS_R_SMART0_A);
6046 goto(--R->sp, F[-1].state);
6049 case DNS_R_SERVFAIL:
6052 if (!(F->answer = dns_p_make(DNS_P_QBUFSIZ, &error)))
6055 dns_header(F->answer)->qr = 1;
6056 dns_header(F->answer)->rcode = DNS_RC_SERVFAIL;
6058 if ((error = dns_p_push(F->answer, DNS_S_QD, R->qname, strlen(R->qname), R->qtype, R->qclass, 0, 0)))
6061 goto(R->sp, DNS_R_DONE);
6070 error = DNS_EILLEGAL;
6073 } /* dns_res_exec() */
6078 void dns_res_clear(struct dns_resolver *R) {
6079 switch (R->stack[R->sp].state) {
6081 return R->cache->clear(R->cache);
6083 return dns_so_clear(&R->so);
6085 } /* dns_res_clear() */
6088 static int dns_res_events2(struct dns_resolver *R, enum dns_events type) {
6091 switch (R->stack[R->sp].state) {
6093 events = R->cache->events(R->cache);
6095 return (type == DNS_LIBEVENT)? DNS_POLL2EV(events) : events;
6097 return dns_so_events2(&R->so, type);
6099 } /* dns_res_events2() */
6102 int dns_res_events(struct dns_resolver *R) {
6103 return dns_res_events2(R, R->so.opts.events);
6104 } /* dns_res_events() */
6107 int dns_res_pollfd(struct dns_resolver *R) {
6108 switch (R->stack[R->sp].state) {
6110 return R->cache->pollfd(R->cache);
6112 return dns_so_pollfd(&R->so);
6114 } /* dns_res_pollfd() */
6117 time_t dns_res_elapsed(struct dns_resolver *R) {
6118 return dns_elapsed(R->began);
6119 } /* dns_res_elapsed() */
6122 int dns_res_poll(struct dns_resolver *R, int timeout) {
6123 return dns_poll(dns_res_pollfd(R), dns_res_events2(R, DNS_SYSPOLL), timeout);
6124 } /* dns_res_poll() */
6127 int dns_res_submit(struct dns_resolver *R, const char *qname, enum dns_type qtype, enum dns_class qclass) {
6130 /* Don't anchor; that can conflict with searchlist generation. */
6131 dns_d_init(R->qname, sizeof R->qname, qname, (R->qlen = strlen(qname)), 0);
6136 R->began = dns_now();
6139 } /* dns_res_submit() */
6142 int dns_res_check(struct dns_resolver *R) {
6145 if ((error = dns_res_exec(R)))
6149 } /* dns_res_check() */
6152 struct dns_packet *dns_res_fetch(struct dns_resolver *R, int *error) {
6153 struct dns_packet *answer;
6155 if (R->stack[0].state != DNS_R_DONE) {
6156 *error = DNS_EUNKNOWN;
6161 answer = R->stack[0].answer;
6162 R->stack[0].answer = 0;
6165 } /* dns_res_fetch() */
6168 struct dns_packet *dns_res_query(struct dns_resolver *res, const char *qname, enum dns_type qtype, enum dns_class qclass, int timeout, int *error_) {
6171 if ((error = dns_res_submit(res, qname, qtype, qclass)))
6174 while ((error = dns_res_check(res))) {
6175 if (dns_res_elapsed(res) > timeout)
6176 error = DNS_ETIMEDOUT;
6178 if (error != DNS_EAGAIN)
6181 if ((error = dns_res_poll(res, 1)))
6185 return dns_res_fetch(res, error_);
6190 } /* dns_res_query() */
6193 const struct dns_stat *dns_res_stat(struct dns_resolver *res) {
6194 return dns_so_stat(&res->so);
6195 } /* dns_res_stat() */
6199 * A D D R I N F O R O U T I N E S
6201 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
6203 struct dns_addrinfo {
6204 struct addrinfo hints;
6205 struct dns_resolver *res;
6207 char qname[DNS_D_MAXNAME + 1];
6208 enum dns_type qtype;
6209 unsigned short qport, port;
6211 struct dns_packet *answer;
6212 struct dns_packet *glue;
6214 struct dns_rr_i i, g;
6217 char cname[DNS_D_MAXNAME + 1];
6220 }; /* struct dns_addrinfo */
6223 struct dns_addrinfo *dns_ai_open(const char *host, const char *serv, enum dns_type qtype, const struct addrinfo *hints, struct dns_resolver *res, int *error_) {
6224 static const struct dns_addrinfo ai_initializer;
6225 struct dns_addrinfo *ai;
6231 dns_res_acquire(res);
6233 if (!(ai = malloc(sizeof *ai)))
6236 *ai = ai_initializer;
6242 if (sizeof ai->qname <= dns_strlcpy(ai->qname, host, sizeof ai->qname))
6243 { error = ENAMETOOLONG; goto error; }
6249 while (isdigit((unsigned char)*serv)) {
6251 ai->qport += *serv++ - '0';
6255 ai->port = ai->qport;
6259 error = dns_syerr();
6267 } /* dns_ai_open() */
6270 void dns_ai_close(struct dns_addrinfo *ai) {
6274 dns_res_close(ai->res);
6276 if (ai->answer != ai->glue)
6281 } /* dns_ai_close() */
6284 static int dns_ai_setent(struct addrinfo **ent, union dns_any *any, enum dns_type type, struct dns_addrinfo *ai) {
6285 struct sockaddr *saddr;
6286 struct sockaddr_in sin;
6287 struct sockaddr_in6 sin6;
6293 saddr = memset(&sin, '\0', sizeof sin);
6295 sin.sin_family = AF_INET;
6296 sin.sin_port = htons(ai->port);
6298 memcpy(&sin.sin_addr, any, sizeof sin.sin_addr);
6302 saddr = memset(&sin6, '\0', sizeof sin6);
6304 sin6.sin6_family = AF_INET6;
6305 sin6.sin6_port = htons(ai->port);
6307 memcpy(&sin6.sin6_addr, any, sizeof sin6.sin6_addr);
6314 if (ai->hints.ai_flags & AI_CANONNAME) {
6315 cname = (*ai->cname)? ai->cname : ai->qname;
6316 clen = strlen(cname);
6322 if (!(*ent = malloc(sizeof **ent + dns_sa_len(saddr) + ((ai->hints.ai_flags & AI_CANONNAME)? clen + 1 : 0))))
6325 memset(*ent, '\0', sizeof **ent);
6327 (*ent)->ai_family = saddr->sa_family;
6328 (*ent)->ai_socktype = ai->hints.ai_socktype;
6329 (*ent)->ai_protocol = ai->hints.ai_protocol;
6331 (*ent)->ai_addr = memcpy((unsigned char *)*ent + sizeof **ent, saddr, dns_sa_len(saddr));
6332 (*ent)->ai_addrlen = dns_sa_len(saddr);
6334 if (ai->hints.ai_flags & AI_CANONNAME)
6335 (*ent)->ai_canonname = memcpy((unsigned char *)*ent + sizeof **ent + dns_sa_len(saddr), cname, clen + 1);
6338 } /* dns_ai_setent() */
6353 }; /* enum dns_ai_state */
6355 #define dns_ai_goto(which) do { ai->state = (which); goto exec; } while (0)
6357 int dns_ai_nextent(struct addrinfo **ent, struct dns_addrinfo *ai) {
6358 struct dns_packet *ans, *glue;
6360 char qname[DNS_D_MAXNAME + 1];
6369 switch (ai->state) {
6372 case DNS_AI_S_NUMERIC:
6373 if (1 == dns_inet_pton(AF_INET, ai->qname, &any.a)) {
6374 ai->state = DNS_AI_S_DONE;
6376 return dns_ai_setent(ent, &any, DNS_T_A, ai);
6379 if (1 == dns_inet_pton(AF_INET6, ai->qname, &any.aaaa)) {
6380 ai->state = DNS_AI_S_DONE;
6382 return dns_ai_setent(ent, &any, DNS_T_AAAA, ai);
6385 if (ai->hints.ai_flags & AI_NUMERICHOST)
6386 dns_ai_goto(DNS_AI_S_DONE);
6389 case DNS_AI_S_SUBMIT:
6390 if ((error = dns_res_submit(ai->res, ai->qname, ai->qtype, DNS_C_IN)))
6394 case DNS_AI_S_CHECK:
6395 if ((error = dns_res_check(ai->res)))
6399 case DNS_AI_S_FETCH:
6400 if (!(ai->answer = dns_res_fetch(ai->res, &error)))
6403 if ((error = dns_p_study(ai->answer)))
6406 ai->glue = ai->answer;
6408 dns_rr_i_init(&ai->i, ai->answer);
6410 ai->i.section = DNS_S_AN;
6411 ai->i.type = ai->qtype;
6412 ai->i.sort = &dns_rr_i_order;
6415 case DNS_AI_S_FOREACH_I:
6416 /* Search generator may have changed our qname. */
6417 if (!(len = dns_d_expand(qname, sizeof qname, 12, ai->answer, &error)))
6419 else if (len >= sizeof qname)
6420 return DNS_EILLEGAL;
6422 if (!dns_d_cname(ai->cname, sizeof ai->cname, qname, strlen(qname), ai->answer, &error))
6425 ai->i.name = ai->cname;
6427 if (!dns_rr_grep(&rr, 1, &ai->i, ai->answer, &error))
6428 dns_ai_goto(DNS_AI_S_DONE);
6430 if ((error = dns_any_parse(&any, &rr, ai->answer)))
6433 ai->port = ai->qport;
6438 return dns_ai_setent(ent, &any, rr.type, ai);
6440 if (!dns_any_cname(ai->cname, sizeof ai->cname, &any, rr.type))
6441 dns_ai_goto(DNS_AI_S_FOREACH_I);
6444 * Find the "real" canonical name. Some authorities
6445 * publish aliases where an RFC defines a canonical
6446 * name. We trust that the resolver followed any
6447 * CNAME chains on it's own, regardless of whether
6448 * the "smart" option is enabled.
6450 if (!dns_d_cname(ai->cname, sizeof ai->cname, ai->cname, strlen(ai->cname), ai->answer, &error))
6453 if (rr.type == DNS_T_SRV)
6454 ai->port = any.srv.port;
6459 dns_rr_i_init(&ai->g, ai->glue);
6461 ai->g.section = DNS_S_ALL & ~DNS_S_QD;
6462 ai->g.name = ai->cname;
6463 ai->g.type = (ai->hints.ai_family == AF_INET6)? DNS_T_AAAA : DNS_T_A;
6466 case DNS_AI_S_FOREACH_G:
6467 if (!dns_rr_grep(&rr, 1, &ai->g, ai->glue, &error)) {
6468 if (dns_rr_i_count(&ai->g) > 0)
6469 dns_ai_goto(DNS_AI_S_FOREACH_I);
6471 dns_ai_goto(DNS_AI_S_SUBMIT_G);
6474 if ((error = dns_any_parse(&any, &rr, ai->glue)))
6477 return dns_ai_setent(ent, &any, rr.type, ai);
6478 case DNS_AI_S_SUBMIT_G:
6479 if (dns_rr_grep(&rr, 1, dns_rr_i_new(ai->glue, .section = DNS_S_QD, .name = ai->g.name, .type = ai->g.type), ai->glue, &error))
6480 dns_ai_goto(DNS_AI_S_FOREACH_I);
6482 if ((error = dns_res_submit(ai->res, ai->g.name, ai->g.type, DNS_C_IN)))
6486 case DNS_AI_S_CHECK_G:
6487 if ((error = dns_res_check(ai->res)))
6491 case DNS_AI_S_FETCH_G:
6492 if (!(ans = dns_res_fetch(ai->res, &error)))
6497 glue = dns_p_merge(ai->glue, DNS_S_ALL, ans, DNS_S_ALL, &error);
6504 if (ai->glue != ai->answer)
6509 dns_rr_i_init(&ai->g, ai->glue);
6511 /* ai->g.name should already point to ai->cname */
6512 if (!dns_d_cname(ai->cname, sizeof ai->cname, ai->cname, strlen(ai->cname), ai->glue, &error))
6513 dns_ai_goto(DNS_AI_S_FOREACH_I);
6515 /* NOTE: Keep all the other iterator filters */
6517 dns_ai_goto(DNS_AI_S_FOREACH_G);
6523 } /* dns_ai_nextent() */
6526 time_t dns_ai_elapsed(struct dns_addrinfo *ai) {
6527 return dns_res_elapsed(ai->res);
6528 } /* dns_ai_elapsed() */
6531 void dns_ai_clear(struct dns_addrinfo *ai) {
6532 return dns_res_clear(ai->res);
6533 } /* dns_ai_clear() */
6536 int dns_ai_events(struct dns_addrinfo *ai) {
6537 return dns_res_events(ai->res);
6538 } /* dns_ai_events() */
6541 int dns_ai_pollfd(struct dns_addrinfo *ai) {
6542 return dns_res_pollfd(ai->res);
6543 } /* dns_ai_pollfd() */
6546 int dns_ai_poll(struct dns_addrinfo *ai, int timeout) {
6547 return dns_res_poll(ai->res, timeout);
6548 } /* dns_ai_poll() */
6551 size_t dns_ai_print(void *dst, size_t lim, struct addrinfo *ent, struct dns_addrinfo *ai) {
6552 char addr[MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN) + 1];
6555 cp += dns__printstring(dst, lim, cp, "[ ");
6556 cp += dns__printstring(dst, lim, cp, ai->qname);
6557 cp += dns__printstring(dst, lim, cp, " IN ");
6558 cp += dns__printstring(dst, lim, cp, dns_strtype(ai->qtype));
6559 cp += dns__printstring(dst, lim, cp, " ]\n");
6561 cp += dns__printstring(dst, lim, cp, ".ai_family = ");
6563 switch (ent->ai_family) {
6565 cp += dns__printstring(dst, lim, cp, "AF_INET");
6568 cp += dns__printstring(dst, lim, cp, "AF_INET6");
6571 cp += dns__print10(dst, lim, cp, ent->ai_family, 0);
6575 cp += dns__printchar(dst, lim, cp, '\n');
6577 cp += dns__printstring(dst, lim, cp, ".ai_socktype = ");
6579 switch (ent->ai_socktype) {
6581 cp += dns__printstring(dst, lim, cp, "SOCK_STREAM");
6584 cp += dns__printstring(dst, lim, cp, "SOCK_DGRAM");
6587 cp += dns__print10(dst, lim, cp, ent->ai_socktype, 0);
6591 cp += dns__printchar(dst, lim, cp, '\n');
6593 cp += dns__printstring(dst, lim, cp, ".ai_addr = [");
6595 dns_inet_ntop(dns_sa_family(ent->ai_addr), dns_sa_addr(dns_sa_family(ent->ai_addr), ent->ai_addr), addr, sizeof addr);
6597 cp += dns__printstring(dst, lim, cp, addr);
6598 cp += dns__printstring(dst, lim, cp, "]:");
6600 cp += dns__print10(dst, lim, cp, ntohs(*dns_sa_port(dns_sa_family(ent->ai_addr), ent->ai_addr)), 0);
6601 cp += dns__printchar(dst, lim, cp, '\n');
6603 cp += dns__printstring(dst, lim, cp, ".ai_canonname = ");
6604 cp += dns__printstring(dst, lim, cp, (ent->ai_canonname)? ent->ai_canonname : "[NULL]");
6605 cp += dns__printchar(dst, lim, cp, '\n');
6607 dns__printnul(dst, lim, cp);
6610 } /* dns_ai_print() */
6613 const struct dns_stat *dns_ai_stat(struct dns_addrinfo *ai) {
6614 return dns_res_stat(ai->res);
6615 } /* dns_ai_stat() */
6619 * M I S C E L L A N E O U S R O U T I N E S
6621 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
6623 static const struct {
6625 enum dns_section type;
6626 } dns_sections[] = {
6627 { "QUESTION", DNS_S_QUESTION },
6628 { "QD", DNS_S_QUESTION },
6629 { "ANSWER", DNS_S_ANSWER },
6630 { "AN", DNS_S_ANSWER },
6631 { "AUTHORITY", DNS_S_AUTHORITY },
6632 { "NS", DNS_S_AUTHORITY },
6633 { "ADDITIONAL", DNS_S_ADDITIONAL },
6634 { "AR", DNS_S_ADDITIONAL },
6637 const char *(dns_strsection)(enum dns_section section, void *dst, size_t lim) {
6640 for (i = 0; i < lengthof(dns_sections); i++) {
6641 if (dns_sections[i].type & section) {
6643 p += dns__printchar(dst, lim, p, '|');
6645 p += dns__printstring(dst, lim, p, dns_sections[i].name);
6647 section &= ~dns_sections[i].type;
6652 p += dns__print10(dst, lim, 0, (0xffff & section), 0);
6654 dns__printnul(dst, lim, p);
6657 } /* dns_strsection() */
6660 enum dns_section dns_isection(const char *src) {
6661 enum dns_section section = 0;
6666 dns_strlcpy(sbuf, src, sizeof sbuf);
6669 while ((name = dns_strsep(&next, "|+, \t"))) {
6670 for (i = 0; i < lengthof(dns_sections); i++) {
6671 if (!strcasecmp(dns_sections[i].name, name)) {
6672 section |= dns_sections[i].type;
6679 } /* dns_isection() */
6682 static const struct {
6684 enum dns_class type;
6689 const char *(dns_strclass)(enum dns_class type, void *dst, size_t lim) {
6692 for (i = 0; i < lengthof(dns_classes); i++) {
6693 if (dns_classes[i].type == type) {
6694 dns__printnul(dst, lim, dns__printstring(dst, lim, 0, dns_classes[i].name));
6700 dns__printnul(dst, lim, dns__print10(dst, lim, 0, (0xffff & type), 0));
6703 } /* dns_strclass() */
6706 enum dns_class dns_iclass(const char *name) {
6709 for (i = 0; i < lengthof(dns_classes); i++) {
6710 if (!strcasecmp(dns_classes[i].name, name))
6711 return dns_classes[i].type;
6715 } /* dns_iclass() */
6718 const char *(dns_strtype)(enum dns_type type, void *dst, size_t lim) {
6721 for (i = 0; i < lengthof(dns_rrtypes); i++) {
6722 if (dns_rrtypes[i].type == type) {
6723 dns__printnul(dst, lim, dns__printstring(dst, lim, 0, dns_rrtypes[i].name));
6729 dns__printnul(dst, lim, dns__print10(dst, lim, 0, (0xffff & type), 0));
6732 } /* dns_strtype() */
6735 enum dns_type dns_itype(const char *type) {
6738 for (i = 0; i < lengthof(dns_rrtypes); i++) {
6739 if (!strcasecmp(dns_rrtypes[i].name, type))
6740 return dns_rrtypes[i].type;
6747 static char dns_opcodes[16][16] = {
6748 [DNS_OP_QUERY] = "QUERY",
6749 [DNS_OP_IQUERY] = "IQUERY",
6750 [DNS_OP_STATUS] = "STATUS",
6751 [DNS_OP_NOTIFY] = "NOTIFY",
6752 [DNS_OP_UPDATE] = "UPDATE",
6755 const char *dns_stropcode(enum dns_opcode opcode) {
6758 if ('\0' == dns_opcodes[opcode][0])
6759 dns__printnul(dns_opcodes[opcode], sizeof dns_opcodes[opcode], dns__print10(dns_opcodes[opcode], sizeof dns_opcodes[opcode], 0, opcode, 0));
6761 return dns_opcodes[opcode];
6762 } /* dns_stropcode() */
6765 enum dns_opcode dns_iopcode(const char *name) {
6768 for (opcode = 0; opcode < lengthof(dns_opcodes); opcode++) {
6769 if (!strcasecmp(name, dns_opcodes[opcode]))
6773 return lengthof(dns_opcodes) - 1;
6774 } /* dns_iopcode() */
6777 static char dns_rcodes[16][16] = {
6778 [DNS_RC_NOERROR] = "NOERROR",
6779 [DNS_RC_FORMERR] = "FORMERR",
6780 [DNS_RC_SERVFAIL] = "SERVFAIL",
6781 [DNS_RC_NXDOMAIN] = "NXDOMAIN",
6782 [DNS_RC_NOTIMP] = "NOTIMP",
6783 [DNS_RC_REFUSED] = "REFUSED",
6784 [DNS_RC_YXDOMAIN] = "YXDOMAIN",
6785 [DNS_RC_YXRRSET] = "YXRRSET",
6786 [DNS_RC_NXRRSET] = "NXRRSET",
6787 [DNS_RC_NOTAUTH] = "NOTAUTH",
6788 [DNS_RC_NOTZONE] = "NOTZONE",
6791 const char *dns_strrcode(enum dns_rcode rcode) {
6794 if ('\0' == dns_rcodes[rcode][0])
6795 dns__printnul(dns_rcodes[rcode], sizeof dns_rcodes[rcode], dns__print10(dns_rcodes[rcode], sizeof dns_rcodes[rcode], 0, rcode, 0));
6797 return dns_rcodes[rcode];
6798 } /* dns_strrcode() */
6801 enum dns_rcode dns_ircode(const char *name) {
6804 for (rcode = 0; rcode < lengthof(dns_rcodes); rcode++) {
6805 if (!strcasecmp(name, dns_rcodes[rcode]))
6809 return lengthof(dns_rcodes) - 1;
6810 } /* dns_ircode() */
6814 * C O M M A N D - L I N E / R E G R E S S I O N R O U T I N E S
6816 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
6836 const char *path[8];
6841 const char *path[8];
6846 const char *path[8];
6851 enum dns_type qtype;
6857 .sort = &dns_rr_i_packet,
6861 void hexdump(const unsigned char *src, size_t len, FILE *fp) {
6862 static const unsigned char hex[] = "0123456789abcdef";
6863 static const unsigned char tmpl[] = " | |\n";
6864 unsigned char ln[sizeof tmpl];
6865 const unsigned char *sp, *se;
6866 unsigned char *h, *g;
6873 memcpy(ln, tmpl, sizeof ln);
6878 for (n = 0; n < 2; n++) {
6879 for (i = 0; i < 8 && se - sp > 0; i++, sp++) {
6880 h[0] = hex[0x0f & (*sp >> 4)];
6881 h[1] = hex[0x0f & (*sp >> 0)];
6884 *g++ = (isgraph(*sp))? *sp : '.';
6890 fputs((char *)ln, fp);
6897 static void panic(const char *fmt, ...) {
6903 vfprintf(stderr, fmt, ap);
6907 verrx(EXIT_FAILURE, fmt, ap);
6911 #define panic_(fn, ln, fmt, ...) \
6912 panic(fmt "%0s", (fn), (ln), __VA_ARGS__)
6913 #define panic(...) \
6914 panic_(__func__, __LINE__, "(%s:%d) " __VA_ARGS__, "")
6917 static void *grow(unsigned char *p, size_t size) {
6920 if (!(tmp = realloc(p, size)))
6921 panic("realloc(%zu): %s", size, dns_strerror(errno));
6927 static size_t add(size_t a, size_t b) {
6929 panic("%zu + %zu: integer overflow", a, b);
6935 static size_t append(unsigned char **dst, size_t osize, const void *src, size_t len) {
6936 size_t size = add(osize, len);
6938 *dst = grow(*dst, size);
6939 memcpy(*dst + osize, src, len);
6945 static size_t slurp(unsigned char **dst, size_t osize, FILE *fp, const char *path) {
6946 size_t size = osize;
6947 unsigned char buf[1024];
6950 while ((count = fread(buf, 1, sizeof buf, fp)))
6951 size = append(dst, size, buf, count);
6954 panic("%s: %s", path, dns_strerror(errno));
6960 static struct dns_resolv_conf *resconf(void) {
6961 static struct dns_resolv_conf *resconf;
6969 if (!(resconf = dns_resconf_open(&error)))
6970 panic("dns_resconf_open: %s", dns_strerror(error));
6972 if (!MAIN.resconf.count)
6973 MAIN.resconf.path[MAIN.resconf.count++] = "/etc/resolv.conf";
6975 for (i = 0; i < MAIN.resconf.count; i++) {
6976 path = MAIN.resconf.path[i];
6978 if (0 == strcmp(path, "-"))
6979 error = dns_resconf_loadfile(resconf, stdin);
6981 error = dns_resconf_loadpath(resconf, path);
6984 panic("%s: %s", path, dns_strerror(error));
6991 static struct dns_hosts *hosts(void) {
6992 static struct dns_hosts *hosts;
7000 if (!MAIN.hosts.count) {
7001 MAIN.hosts.path[MAIN.hosts.count++] = "/etc/hosts";
7003 /* Explicitly test dns_hosts_local() */
7004 if (!(hosts = dns_hosts_local(&error)))
7005 panic("%s: %s", "/etc/hosts", dns_strerror(error));
7010 if (!(hosts = dns_hosts_open(&error)))
7011 panic("dns_hosts_open: %s", dns_strerror(error));
7013 for (i = 0; i < MAIN.hosts.count; i++) {
7014 path = MAIN.hosts.path[i];
7016 if (0 == strcmp(path, "-"))
7017 error = dns_hosts_loadfile(hosts, stdin);
7019 error = dns_hosts_loadpath(hosts, path);
7022 panic("%s: %s", path, dns_strerror(error));
7032 struct dns_cache *cache(void) {
7033 static struct cache *cache;
7039 return cache_resi(cache);
7040 if (!MAIN.cache.count)
7043 if (!(cache = cache_open(&error)))
7044 panic("%s: %s", MAIN.cache.path[0], dns_strerror(error));
7046 for (i = 0; i < MAIN.cache.count; i++) {
7047 path = MAIN.cache.path[i];
7049 if (!strcmp(path, "-")) {
7050 if ((error = cache_loadfile(cache, stdin, NULL, 0)))
7051 panic("%s: %s", path, dns_strerror(error));
7052 } else if ((error = cache_loadpath(cache, path, NULL, 0)))
7053 panic("%s: %s", path, dns_strerror(error));
7056 return cache_resi(cache);
7059 struct dns_cache *cache(void) { return NULL; }
7063 static void print_packet(struct dns_packet *P, FILE *fp) {
7064 dns_p_dump3(P, dns_rr_i_new(P, .sort = MAIN.sort), fp);
7066 if (MAIN.verbose > 2)
7067 hexdump(P->data, P->end, fp);
7068 } /* print_packet() */
7071 static int parse_packet(int argc, char *argv[]) {
7072 struct dns_packet *P = dns_p_new(512);
7073 struct dns_packet *Q = dns_p_new(512);
7074 enum dns_section section;
7078 char pretty[sizeof any * 2];
7081 P->end = fread(P->data, 1, P->size, stdin);
7083 fputs(";; [HEADER]\n", stdout);
7084 fprintf(stdout, ";; qr : %s(%d)\n", (dns_header(P)->qr)? "QUERY" : "RESPONSE", dns_header(P)->qr);
7085 fprintf(stdout, ";; opcode : %s(%d)\n", dns_stropcode(dns_header(P)->opcode), dns_header(P)->opcode);
7086 fprintf(stdout, ";; aa : %s(%d)\n", (dns_header(P)->aa)? "AUTHORITATIVE" : "NON-AUTHORITATIVE", dns_header(P)->aa);
7087 fprintf(stdout, ";; tc : %s(%d)\n", (dns_header(P)->tc)? "TRUNCATED" : "NOT-TRUNCATED", dns_header(P)->tc);
7088 fprintf(stdout, ";; rd : %s(%d)\n", (dns_header(P)->rd)? "RECURSION-DESIRED" : "RECURSION-NOT-DESIRED", dns_header(P)->rd);
7089 fprintf(stdout, ";; ra : %s(%d)\n", (dns_header(P)->ra)? "RECURSION-ALLOWED" : "RECURSION-NOT-ALLOWED", dns_header(P)->ra);
7090 fprintf(stdout, ";; rcode : %s(%d)\n", dns_strrcode(dns_header(P)->rcode), dns_header(P)->rcode);
7094 dns_rr_foreach(&rr, P, .sort = MAIN.sort) {
7095 if (section != rr.section)
7096 fprintf(stdout, "\n;; [%s:%d]\n", dns_strsection(rr.section), dns_p_count(P, rr.section));
7098 if ((len = dns_rr_print(pretty, sizeof pretty, &rr, P, &error)))
7099 fprintf(stdout, "%s\n", pretty);
7101 dns_rr_copy(Q, &rr, P);
7103 section = rr.section;
7106 fputs("; ; ; ; ; ; ; ;\n\n", stdout);
7111 dns_rr_foreach(&rr, Q, .name = "ns8.yahoo.com.") {
7113 struct dns_rr rrset[32];
7114 struct dns_rr_i *rri = dns_rr_i_new(Q, .name = dns_d_new("ns8.yahoo.com", DNS_D_ANCHOR), .sort = MAIN.sort);
7115 unsigned rrcount = dns_rr_grep(rrset, lengthof(rrset), rri, Q, &error);
7118 for (i = 0; i < rrcount; i++) {
7121 if (section != rr.section)
7122 fprintf(stdout, "\n;; [%s:%d]\n", dns_strsection(rr.section), dns_p_count(Q, rr.section));
7124 if ((len = dns_rr_print(pretty, sizeof pretty, &rr, Q, &error)))
7125 fprintf(stdout, "%s\n", pretty);
7127 section = rr.section;
7130 if (MAIN.verbose > 1) {
7131 fprintf(stderr, "orig:%zu\n", P->end);
7132 hexdump(P->data, P->end, stdout);
7134 fprintf(stderr, "copy:%zu\n", Q->end);
7135 hexdump(Q->data, Q->end, stdout);
7139 } /* parse_packet() */
7142 static int parse_domain(int argc, char *argv[]) {
7145 dn = (argc > 1)? argv[1] : "f.l.google.com";
7147 printf("[%s]\n", dn);
7153 } while (dns_d_cleave(dn, strlen(dn) + 1, dn, strlen(dn)));
7156 } /* parse_domain() */
7159 static int expand_domain(int argc, char *argv[]) {
7160 unsigned short rp = 0;
7161 unsigned char *src = NULL;
7163 struct dns_packet *pkt;
7164 size_t lim = 0, len;
7170 len = slurp(&src, 0, stdin, "-");
7172 if (!(pkt = dns_p_make(len, &error)))
7173 panic("malloc(%zu): %s", len, dns_strerror(error));
7175 memcpy(pkt->data, src, len);
7179 dst = grow(NULL, lim);
7181 while (lim <= (len = dns_d_expand(dst, lim, rp, pkt, &error))) {
7183 dst = grow(dst, lim);
7187 panic("expand: %s", dns_strerror(error));
7189 fwrite(dst, 1, len, stdout);
7197 } /* expand_domain() */
7200 static int show_resconf(int argc, char *argv[]) {
7203 resconf(); /* load it */
7205 fputs("; SOURCES\n", stdout);
7207 for (i = 0; i < MAIN.resconf.count; i++)
7208 fprintf(stdout, "; %s\n", MAIN.resconf.path[i]);
7210 fputs(";\n", stdout);
7212 dns_resconf_dump(resconf(), stdout);
7215 } /* show_resconf() */
7218 static int show_hosts(int argc, char *argv[]) {
7223 fputs("# SOURCES\n", stdout);
7225 for (i = 0; i < MAIN.hosts.count; i++)
7226 fprintf(stdout, "# %s\n", MAIN.hosts.path[i]);
7228 fputs("#\n", stdout);
7230 dns_hosts_dump(hosts(), stdout);
7233 } /* show_hosts() */
7236 static int query_hosts(int argc, char *argv[]) {
7237 struct dns_packet *Q = dns_p_new(512);
7238 struct dns_packet *A;
7239 char qname[DNS_D_MAXNAME + 1];
7244 MAIN.qname = (argc > 1)? argv[1] : "localhost";
7246 MAIN.qtype = DNS_T_A;
7250 if (MAIN.qtype == DNS_T_PTR && !strstr(MAIN.qname, "arpa")) {
7251 union { struct in_addr a; struct in6_addr a6; } addr;
7252 int af = (strchr(MAIN.qname, ':'))? AF_INET6 : AF_INET;
7254 if (1 != dns_inet_pton(af, MAIN.qname, &addr))
7255 panic("%s: %s", MAIN.qname, dns_strerror(error));
7257 qlen = dns_ptr_qname(qname, sizeof qname, af, &addr);
7259 qlen = dns__printstring(qname, sizeof qname, 0, MAIN.qname);
7261 if ((error = dns_p_push(Q, DNS_S_QD, qname, qlen, MAIN.qtype, DNS_C_IN, 0, 0)))
7262 panic("%s: %s", qname, dns_strerror(error));
7264 if (!(A = dns_hosts_query(hosts(), Q, &error)))
7265 panic("%s: %s", qname, dns_strerror(error));
7267 print_packet(A, stdout);
7272 } /* query_hosts() */
7275 static int search_list(int argc, char *argv[]) {
7276 const char *qname = (argc > 1)? argv[1] : "f.l.google.com";
7277 unsigned long i = 0;
7278 char name[DNS_D_MAXNAME + 1];
7280 printf("[%s]\n", qname);
7282 while (dns_resconf_search(name, sizeof name, qname, strlen(qname), resconf(), &i))
7286 } /* search_list() */
7289 int permute_set(int argc, char *argv[]) {
7291 struct dns_k_permutor p;
7293 hi = (--argc > 0)? atoi(argv[argc]) : 8;
7294 lo = (--argc > 0)? atoi(argv[argc]) : 0;
7296 fprintf(stderr, "[%u .. %u]\n", lo, hi);
7298 dns_k_permutor_init(&p, lo, hi);
7300 for (i = lo; i <= hi; i++)
7301 fprintf(stdout, "%u\n", dns_k_permutor_step(&p));
7302 // printf("%u -> %u -> %u\n", i, dns_k_permutor_E(&p, i), dns_k_permutor_D(&p, dns_k_permutor_E(&p, i)));
7305 } /* permute_set() */
7308 int shuffle_16(int argc, char *argv[]) {
7312 n = 0xffff & atoi(argv[argc]);
7313 r = (--argc > 0)? (unsigned)atoi(argv[argc]) : dns_random();
7315 fprintf(stdout, "%hu\n", dns_k_shuffle16(n, r));
7319 for (n = 0; n < 65536; n++)
7320 fprintf(stdout, "%hu\n", dns_k_shuffle16(n, r));
7324 } /* shuffle_16() */
7327 int dump_random(int argc, char *argv[]) {
7328 unsigned char b[32];
7329 unsigned i, j, n, r;
7331 n = (argc > 1)? atoi(argv[1]) : 32;
7339 for (j = 0; j < sizeof r && i < n && i < sizeof b; i++, j++) {
7343 } while (i < n && i < sizeof b);
7345 hexdump(b, i, stdout);
7351 } /* dump_random() */
7354 static int send_query(int argc, char *argv[]) {
7355 struct dns_packet *A, *Q = dns_p_new(512);
7356 char host[INET6_ADDRSTRLEN + 1];
7357 struct sockaddr_storage ss;
7358 struct dns_socket *so;
7362 ss.ss_family = (strchr(argv[1], ':'))? AF_INET6 : AF_INET;
7364 if (1 != dns_inet_pton(ss.ss_family, argv[1], dns_sa_addr(ss.ss_family, &ss)))
7365 panic("%s: invalid host address", argv[1]);
7367 *dns_sa_port(ss.ss_family, &ss) = htons(53);
7369 memcpy(&ss, &resconf()->nameserver[0], dns_sa_len(&resconf()->nameserver[0]));
7371 if (!dns_inet_ntop(ss.ss_family, dns_sa_addr(ss.ss_family, &ss), host, sizeof host))
7372 panic("bad host address, or none provided");
7375 MAIN.qname = "ipv6.google.com";
7377 MAIN.qtype = DNS_T_AAAA;
7379 if ((error = dns_p_push(Q, DNS_S_QD, MAIN.qname, strlen(MAIN.qname), MAIN.qtype, DNS_C_IN, 0, 0)))
7380 panic("dns_p_push: %s", dns_strerror(error));
7382 dns_header(Q)->rd = 1;
7384 if (strstr(argv[0], "udp"))
7386 else if (strstr(argv[0], "tcp"))
7389 type = dns_res_tcp2type(resconf()->options.tcp);
7391 fprintf(stderr, "querying %s for %s IN %s\n", host, MAIN.qname, dns_strtype(MAIN.qtype));
7393 if (!(so = dns_so_open((struct sockaddr *)&resconf()->iface, type, dns_opts(), &error)))
7394 panic("dns_so_open: %s", dns_strerror(error));
7396 while (!(A = dns_so_query(so, Q, (struct sockaddr *)&ss, &error))) {
7397 if (error != EAGAIN)
7398 panic("dns_so_query: %s (%d)", dns_strerror(error), error);
7399 if (dns_so_elapsed(so) > 10)
7400 panic("query timed-out");
7405 print_packet(A, stdout);
7410 } /* send_query() */
7413 static int print_arpa(int argc, char *argv[]) {
7414 const char *ip = (argc > 1)? argv[1] : "::1";
7415 int af = (strchr(ip, ':'))? AF_INET6 : AF_INET;
7416 union { struct in_addr a4; struct in6_addr a6; } addr;
7417 char host[DNS_D_MAXNAME + 1];
7419 if (1 != dns_inet_pton(af, ip, &addr) || 0 == dns_ptr_qname(host, sizeof host, af, &addr))
7420 panic("%s: invalid address", ip);
7422 fprintf(stdout, "%s\n", host);
7425 } /* print_arpa() */
7428 static int show_hints(int argc, char *argv[]) {
7429 struct dns_hints *(*load)(struct dns_resolv_conf *, int *);
7430 const char *which, *how, *who;
7431 struct dns_hints *hints;
7434 which = (argc > 1)? argv[1] : "local";
7435 how = (argc > 2)? argv[2] : "plain";
7436 who = (argc > 3)? argv[3] : "google.com";
7438 load = (0 == strcmp(which, "local"))
7442 if (!(hints = load(resconf(), &error)))
7443 panic("%s: %s", argv[0], dns_strerror(error));
7445 if (0 == strcmp(how, "plain")) {
7446 dns_hints_dump(hints, stdout);
7448 struct dns_packet *query, *answer;
7450 query = dns_p_new(512);
7452 if ((error = dns_p_push(query, DNS_S_QUESTION, who, strlen(who), DNS_T_A, DNS_C_IN, 0, 0)))
7453 panic("%s: %s", who, dns_strerror(error));
7455 if (!(answer = dns_hints_query(hints, query, &error)))
7456 panic("%s: %s", who, dns_strerror(error));
7458 print_packet(answer, stdout);
7463 dns_hints_close(hints);
7466 } /* show_hints() */
7469 static int resolve_query(int argc, char *argv[]) {
7470 struct dns_hints *(*hints)() = (strstr(argv[0], "recurse"))? &dns_hints_root : &dns_hints_local;
7471 struct dns_resolver *R;
7472 struct dns_packet *ans;
7473 const struct dns_stat *st;
7477 MAIN.qname = "www.google.com";
7479 MAIN.qtype = DNS_T_A;
7481 resconf()->options.recurse = (0 != strstr(argv[0], "recurse"));
7483 if (!(R = dns_res_open(resconf(), hosts(), dns_hints_mortal(hints(resconf(), &error)), cache(), dns_opts(), &error)))
7484 panic("%s: %s", MAIN.qname, dns_strerror(error));
7486 if ((error = dns_res_submit(R, MAIN.qname, MAIN.qtype, DNS_C_IN)))
7487 panic("%s: %s", MAIN.qname, dns_strerror(error));
7489 while ((error = dns_res_check(R))) {
7490 if (error != EAGAIN)
7491 panic("dns_res_check: %s (%d)", dns_strerror(error), error);
7492 if (dns_res_elapsed(R) > 30)
7493 panic("query timed-out");
7498 ans = dns_res_fetch(R, &error);
7499 print_packet(ans, stdout);
7502 st = dns_res_stat(R);
7504 printf(";; queries: %zu\n", st->queries);
7505 printf(";; udp sent: %zu in %zu bytes\n", st->udp.sent.count, st->udp.sent.bytes);
7506 printf(";; udp rcvd: %zu in %zu bytes\n", st->udp.rcvd.count, st->udp.rcvd.bytes);
7507 printf(";; tcp sent: %zu in %zu bytes\n", st->tcp.sent.count, st->tcp.sent.bytes);
7508 printf(";; tcp rcvd: %zu in %zu bytes\n", st->tcp.rcvd.count, st->tcp.rcvd.bytes);
7513 } /* resolve_query() */
7516 static int resolve_addrinfo(int argc, char *argv[]) {
7517 struct dns_hints *(*hints)() = (strstr(argv[0], "recurse"))? &dns_hints_root : &dns_hints_local;
7518 struct dns_resolver *res = 0;
7519 struct dns_addrinfo *ai = 0;
7520 struct addrinfo ai_hints = { .ai_family = PF_UNSPEC, .ai_socktype = SOCK_STREAM, .ai_flags = AI_CANONNAME };
7521 struct addrinfo *ent;
7526 MAIN.qname = "www.google.com";
7528 MAIN.qtype = DNS_T_A;
7530 resconf()->options.recurse = (0 != strstr(argv[0], "recurse"));
7532 if (!(res = dns_res_open(resconf(), hosts(), dns_hints_mortal(hints(resconf(), &error)), cache(), dns_opts(), &error)))
7533 panic("%s: %s", MAIN.qname, dns_strerror(error));
7535 if (!(ai = dns_ai_open(MAIN.qname, "80", MAIN.qtype, &ai_hints, res, &error)))
7536 panic("%s: %s", MAIN.qname, dns_strerror(error));
7539 switch (error = dns_ai_nextent(&ent, ai)) {
7541 dns_ai_print(pretty, sizeof pretty, ent, ai);
7543 fputs(pretty, stdout);
7551 if (dns_ai_elapsed(ai) > 30)
7552 panic("query timed-out");
7558 panic("dns_ai_nextent: %s (%d)", dns_strerror(error), error);
7560 } while (error != ENOENT);
7566 } /* resolve_addrinfo() */
7569 static int echo_port(int argc, char *argv[]) {
7572 struct sockaddr_in sin;
7576 memset(&port, 0, sizeof port);
7577 port.sin.sin_family = AF_INET;
7578 port.sin.sin_port = htons(5354);
7579 port.sin.sin_addr.s_addr = inet_addr("127.0.0.1");
7581 if (-1 == (fd = socket(PF_INET, SOCK_DGRAM, 0)))
7582 panic("socket: %s", strerror(errno));
7584 if (0 != bind(fd, &port.sa, sizeof port.sa))
7585 panic("127.0.0.1:5353: %s", dns_strerror(errno));
7588 struct dns_packet *pkt = dns_p_new(512);
7589 struct sockaddr_storage ss;
7590 socklen_t slen = sizeof ss;
7592 #if defined(MSG_WAITALL) /* MinGW issue */
7593 int rflags = MSG_WAITALL;
7598 count = recvfrom(fd, (char *)pkt->data, pkt->size, rflags, (struct sockaddr *)&ss, &slen);
7601 if (!count || count < 0)
7602 panic("recvfrom: %s", strerror(errno));
7606 dns_p_dump(pkt, stdout);
7608 (void)sendto(fd, (char *)pkt->data, pkt->end, 0, (struct sockaddr *)&ss, slen);
7615 static int isection(int argc, char *argv[]) {
7616 const char *name = (argv[1])? argv[1] : "";
7619 type = dns_isection(name);
7620 name = dns_strsection(type);
7622 printf("%s (%d)\n", name, type);
7628 static int iclass(int argc, char *argv[]) {
7629 const char *name = (argv[1])? argv[1] : "";
7632 type = dns_iclass(name);
7633 name = dns_strclass(type);
7635 printf("%s (%d)\n", name, type);
7641 static int itype(int argc, char *argv[]) {
7642 const char *name = (argv[1])? argv[1] : "";
7645 type = dns_itype(name);
7646 name = dns_strtype(type);
7648 printf("%s (%d)\n", name, type);
7654 static int iopcode(int argc, char *argv[]) {
7655 const char *name = (argv[1])? argv[1] : "";
7658 type = dns_iopcode(name);
7659 name = dns_stropcode(type);
7661 printf("%s (%d)\n", name, type);
7667 static int ircode(int argc, char *argv[]) {
7668 const char *name = (argv[1])? argv[1] : "";
7671 type = dns_ircode(name);
7672 name = dns_strrcode(type);
7674 printf("%s (%d)\n", name, type);
7680 #define SIZE1(x) { DNS_PP_STRINGIFY(x), sizeof (x) }
7681 #define SIZE2(x, ...) SIZE1(x), SIZE1(__VA_ARGS__)
7682 #define SIZE3(x, ...) SIZE1(x), SIZE2(__VA_ARGS__)
7683 #define SIZE4(x, ...) SIZE1(x), SIZE3(__VA_ARGS__)
7684 #define SIZE(...) DNS_PP_CALL(DNS_PP_XPASTE(SIZE, DNS_PP_NARG(__VA_ARGS__)), __VA_ARGS__)
7686 static int sizes(int argc, char *argv[]) {
7687 static const struct { const char *name; size_t size; } type[] = {
7688 SIZE(struct dns_header, struct dns_packet, struct dns_rr, struct dns_rr_i),
7689 SIZE(struct dns_a, struct dns_aaaa, struct dns_mx, struct dns_ns),
7690 SIZE(struct dns_cname, struct dns_soa, struct dns_ptr, struct dns_srv),
7691 SIZE(struct dns_sshfp, struct dns_txt, union dns_any),
7692 SIZE(struct dns_resolv_conf, struct dns_hosts, struct dns_hints, struct dns_hints_i),
7693 SIZE(struct dns_options, struct dns_socket, struct dns_resolver, struct dns_addrinfo),
7694 SIZE(struct dns_cache),
7698 for (i = 0, max = 0; i < lengthof(type); i++)
7699 max = MAX(max, strlen(type[i].name));
7701 for (i = 0; i < lengthof(type); i++)
7702 printf("%*s : %zu\n", max, type[i].name, type[i].size);
7708 static const struct { const char *cmd; int (*run)(); const char *help; } cmds[] = {
7709 { "parse-packet", &parse_packet, "parse binary packet from stdin" },
7710 { "parse-domain", &parse_domain, "anchor and iteratively cleave domain" },
7711 { "expand-domain", &expand_domain, "expand domain at offset NN in packet from stdin" },
7712 { "show-resconf", &show_resconf, "show resolv.conf data" },
7713 { "show-hosts", &show_hosts, "show hosts data" },
7714 { "query-hosts", &query_hosts, "query A, AAAA or PTR in hosts data" },
7715 { "search-list", &search_list, "generate query search list from domain" },
7716 { "permute-set", &permute_set, "generate random permutation -> (0 .. N or N .. M)" },
7717 { "shuffle-16", &shuffle_16, "simple 16-bit permutation" },
7718 { "dump-random", &dump_random, "generate random bytes" },
7719 { "send-query", &send_query, "send query to host" },
7720 { "send-query-udp", &send_query, "send udp query to host" },
7721 { "send-query-tcp", &send_query, "send tcp query to host" },
7722 { "print-arpa", &print_arpa, "print arpa. zone name of address" },
7723 { "show-hints", &show_hints, "print hints: show-hints [local|root] [plain|packet]" },
7724 { "resolve-stub", &resolve_query, "resolve as stub resolver" },
7725 { "resolve-recurse", &resolve_query, "resolve as recursive resolver" },
7726 { "addrinfo-stub", &resolve_addrinfo, "resolve through getaddrinfo clone" },
7727 { "addrinfo-recurse", &resolve_addrinfo, "resolve through getaddrinfo clone" },
7728 /* { "resolve-nameinfo", &resolve_query, "resolve as recursive resolver" }, */
7729 { "echo", &echo_port, "server echo mode, for nmap fuzzing" },
7730 { "isection", &isection, "parse section string" },
7731 { "iclass", &iclass, "parse class string" },
7732 { "itype", &itype, "parse type string" },
7733 { "iopcode", &iopcode, "parse opcode string" },
7734 { "ircode", &ircode, "parse rcode string" },
7735 { "sizes", &sizes, "print data structure sizes" },
7739 static void print_usage(const char *progname, FILE *fp) {
7740 static const char *usage =
7741 " [OPTIONS] COMMAND [ARGS]\n"
7742 " -c PATH Path to resolv.conf\n"
7743 " -l PATH Path to local hosts\n"
7744 " -z PATH Path to zone cache\n"
7745 " -q QNAME Query name\n"
7746 " -t QTYPE Query type\n"
7747 " -s HOW Sort records\n"
7748 " -v Be more verbose (-vv show packets; -vvv hexdump packets)\n"
7749 " -V Print version info\n"
7750 " -h Print this usage message\n"
7754 fputs(progname, fp);
7757 for (i = 0, m = 0; i < lengthof(cmds); i++) {
7758 if (strlen(cmds[i].cmd) > m)
7759 m = strlen(cmds[i].cmd);
7762 for (i = 0; i < lengthof(cmds); i++) {
7763 fprintf(fp, " %s ", cmds[i].cmd);
7765 for (n = strlen(cmds[i].cmd); n < m; n++)
7768 fputs(cmds[i].help, fp);
7772 fputs("\nReport bugs to William Ahern <william@25thandClement.com>\n", fp);
7773 } /* print_usage() */
7776 static void print_version(const char *progname, FILE *fp) {
7777 fprintf(fp, "%s (dns.c) %.8X\n", progname, dns_v_rel());
7778 fprintf(fp, "vendor %s\n", dns_vendor());
7779 fprintf(fp, "release %.8X\n", dns_v_rel());
7780 fprintf(fp, "abi %.8X\n", dns_v_abi());
7781 fprintf(fp, "api %.8X\n", dns_v_api());
7782 } /* print_version() */
7785 int main(int argc, char **argv) {
7787 extern char *optarg;
7788 const char *progname = argv[0];
7792 while (-1 != (ch = getopt(argc, argv, "q:t:c:l:z:s:vVh"))) {
7795 assert(MAIN.resconf.count < lengthof(MAIN.resconf.path));
7797 MAIN.resconf.path[MAIN.resconf.count++] = optarg;
7801 assert(MAIN.hosts.count < lengthof(MAIN.hosts.path));
7803 MAIN.hosts.path[MAIN.hosts.count++] = optarg;
7807 assert(MAIN.cache.count < lengthof(MAIN.cache.path));
7809 MAIN.cache.path[MAIN.cache.count++] = optarg;
7813 MAIN.qname = optarg;
7817 for (i = 0; i < lengthof(dns_rrtypes); i++) {
7818 if (0 == strcasecmp(dns_rrtypes[i].name, optarg))
7819 { MAIN.qtype = dns_rrtypes[i].type; break; }
7825 for (i = 0; isdigit((int)optarg[i]); i++) {
7827 MAIN.qtype += optarg[i] - '0';
7831 panic("%s: invalid query type", optarg);
7835 if (0 == strcasecmp(optarg, "packet"))
7836 MAIN.sort = &dns_rr_i_packet;
7837 else if (0 == strcasecmp(optarg, "shuffle"))
7838 MAIN.sort = &dns_rr_i_shuffle;
7839 else if (0 == strcasecmp(optarg, "order"))
7840 MAIN.sort = &dns_rr_i_order;
7842 panic("%s: invalid sort method", optarg);
7846 dns_debug = ++MAIN.verbose;
7850 print_version(progname, stdout);
7854 print_usage(progname, stdout);
7858 print_usage(progname, stderr);
7860 return EXIT_FAILURE;
7867 for (i = 0; i < lengthof(cmds) && argv[0]; i++) {
7868 if (0 == strcmp(cmds[i].cmd, argv[0]))
7869 return cmds[i].run(argc, argv);
7872 print_usage(progname, stderr);
7874 return EXIT_FAILURE;
7878 #endif /* DNS_MAIN */