Blob


1 /*
2 * Copyright (c) 2010-2014 Peter J. Philipp
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. The name of the author may not be used to endorse or promote products
14 * derived from this software without specific prior written permission
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 *
27 */
28 #include "include.h"
29 #include "dns.h"
30 #include "db.h"
32 extern struct question *build_fake_question(char *, int, u_int16_t);
33 extern struct question *build_question(char *, int);
34 extern void build_reply(struct sreply *, int, char *, int, struct question *, struct sockaddr *, socklen_t, struct domain *, struct domain *, u_int8_t, int, int, struct recurses *);
35 extern void dolog(int, char *, ...);
36 extern int free_question(struct question *);
37 extern int get_soa(DB *, struct question *, struct domain *, int);
38 extern in_addr_t getmask(int);
39 extern int getmask6(int, struct sockaddr_in6 *);
40 extern int lookup_zone(DB *, struct question *, struct domain *, int *, char *, int);
41 extern int memcasecmp(u_char *, u_char *, int);
42 extern void reply_a(struct sreply *, DB *);
43 extern void reply_aaaa(struct sreply *, DB *);
44 extern void reply_cname(struct sreply *);
45 extern void reply_mx(struct sreply *, DB *);
46 extern void reply_ns(struct sreply *, DB *);
47 extern void reply_noerror(struct sreply *);
48 extern void reply_nxdomain(struct sreply *);
49 extern void reply_ptr(struct sreply *);
50 extern void reply_soa(struct sreply *);
51 extern void reply_txt(struct sreply *sreply);
52 extern void slave_shutdown(void);
53 extern void update_db(DB *, struct domain *);
55 int contains(u_char *, u_char *);
56 void init_recurse(void);
57 int insert_recurse(char *, char *);
58 int fakerecurse(DB *, struct recurses *, struct ns *, int);
59 int find_recurse(struct sockaddr_storage *, int);
60 int level(u_char *);
61 int lookup_a(DB *, struct recurses *, struct ns *);
62 int lookup_aaaa(DB *, struct recurses *, struct ns *);
63 int lookup_ns(DB *, struct recurses *);
64 int negative_cache(DB *, struct recurses *);
65 int netlookup(DB *, struct recurses *);
66 int netlookup6(DB *, struct recurses *);
67 void recurseloop(int, int *, DB *);
68 int recurse_parse(DB *, struct recurses *, u_char *, u_int16_t);
69 void reply_raw(DB *, struct recurses *, struct domain *, int *);
70 void reply_raw_cname(DB *, struct recurses *, struct domain *, int *);
71 void reply_raw_noerror(DB *, struct recurses *, struct domain *, int *);
72 void reply_raw_nxdomain(DB *, struct recurses *, struct domain *, int *);
73 void remove_zone(DB *, struct domain *);
75 extern int debug, verbose;
77 #ifndef MIN
78 #define MIN(a,b) ((a < b) ? a : b)
79 #endif
81 SLIST_HEAD(listhead, recurseentry) recursehead;
83 static struct recurseentry {
84 char name[INET6_ADDRSTRLEN];
85 int family;
86 struct sockaddr_storage hostmask;
87 struct sockaddr_storage netmask;
88 u_int8_t prefixlen;
89 SLIST_ENTRY(recurseentry) recurse_entry;
90 } *rn2, *rnp;
93 static const char rcsid[] = "$Id: recurse.c,v 1.45 2014/09/27 17:38:28 pjp Exp $";
95 /*
96 * INIT_RECURSE - initialize the recurse singly linked list
97 */
99 void
100 init_recurse(void)
102 SLIST_INIT(&recursehead);
103 return;
106 /*
107 * INSERT_RECURSE - insert an address and prefixlen into the recurse slist
108 */
110 int
111 insert_recurse(char *address, char *prefixlen)
113 struct sockaddr_in *sin;
114 struct sockaddr_in6 *sin6;
115 int pnum;
116 int ret;
118 pnum = atoi(prefixlen);
119 rn2 = malloc(sizeof(struct recurseentry)); /* Insert after. */
121 if (strchr(address, ':') != NULL) {
122 rn2->family = AF_INET6;
123 sin6 = (struct sockaddr_in6 *)&rn2->hostmask;
124 if ((ret = inet_pton(AF_INET6, address, &sin6->sin6_addr.s6_addr)) != 1)
125 return (-1);
126 sin6->sin6_family = AF_INET6;
127 sin6 = (struct sockaddr_in6 *)&rn2->netmask;
128 sin6->sin6_family = AF_INET6;
129 if (getmask6(pnum, sin6) < 0)
130 return(-1);
131 rn2->prefixlen = pnum;
132 } else {
134 rn2->family = AF_INET;
135 sin = (struct sockaddr_in *)&rn2->hostmask;
136 sin->sin_family = AF_INET;
137 sin->sin_addr.s_addr = inet_addr(address);
138 sin = (struct sockaddr_in *)&rn2->netmask;
139 sin->sin_family = AF_INET;
140 sin->sin_addr.s_addr = getmask(pnum);
141 rn2->prefixlen = pnum;
145 SLIST_INSERT_HEAD(&recursehead, rn2, recurse_entry);
147 return (0);
150 /*
151 * FIND_RECURSE - walk the recurse list and find the correponding network
152 * if a network matches return 1, if no match is found return
153 * 0.
154 */
156 int
157 find_recurse(struct sockaddr_storage *sst, int family)
159 struct sockaddr_in *sin, *sin0;
160 struct sockaddr_in6 *sin6, *sin60, *sin61;
161 u_int32_t hostmask, netmask;
162 u_int32_t a;
163 #ifdef __amd64
164 u_int64_t *hm[2], *nm[2], *a6[2];
165 #else
166 u_int32_t *hm[4], *nm[4], *a6[4];
167 #endif
169 SLIST_FOREACH(rnp, &recursehead, recurse_entry) {
170 if (rnp->family == AF_INET) {
171 if (family != AF_INET)
172 continue;
173 sin = (struct sockaddr_in *)sst;
174 a = sin->sin_addr.s_addr;
175 sin = (struct sockaddr_in *)&rnp->hostmask;
176 sin0 = (struct sockaddr_in *)&rnp->netmask;
177 hostmask = sin->sin_addr.s_addr;
178 netmask = sin0->sin_addr.s_addr;
179 if ((hostmask & netmask) == (a & netmask)) {
180 return (1);
181 } /* if hostmask */
182 } else if (rnp->family == AF_INET6) {
183 if (family != AF_INET6)
184 continue;
185 sin6 = (struct sockaddr_in6 *)sst;
186 sin60 = (struct sockaddr_in6 *)&rnp->hostmask;
187 sin61 = (struct sockaddr_in6 *)&rnp->netmask;
188 #ifdef __amd64
189 /*
190 * If this is on a 64 bit machine, we'll benefit
191 * by using 64 bit registers, this should make it
192 * a tad faster...
193 */
194 hm[0] = (u_int64_t *)&sin60->sin6_addr.s6_addr;
195 hm[1] = (hm[0] + 1);
196 nm[0] = (u_int64_t *)&sin61->sin6_addr.s6_addr;
197 nm[1] = (nm[0] + 1);
198 a6[0] = (u_int64_t *)&sin6->sin6_addr.s6_addr;
199 a6[1] = (a6[0] + 1);
200 if ( ((*hm[0] & *nm[0]) == (*a6[0] & *nm[0]))&&
201 ((*hm[1] & *nm[1]) == (*a6[1] & *nm[1]))) {
202 #else
203 hm[0] = (u_int32_t *)&sin60->sin6_addr.s6_addr;
204 hm[1] = (hm[0] + 1); hm[2] = (hm[1] + 1);
205 hm[3] = (hm[2] + 1);
206 nm[0] = (u_int32_t *)&sin61->sin6_addr.s6_addr;
207 nm[1] = (nm[0] + 1); nm[2] = (nm[1] + 1);
208 nm[3] = (nm[2] + 1);
209 a6[0] = (u_int32_t *)&sin6->sin6_addr.s6_addr;
210 a6[1] = (a6[0] + 1); a6[2] = (a6[1] + 1);
211 a6[3] = (a6[2] + 1);
213 if ( ((*hm[0] & *nm[0]) == (*a6[0] & *nm[0]))&&
214 ((*hm[1] & *nm[1]) == (*a6[1] & *nm[1]))&&
215 ((*hm[2] & *nm[2]) == (*a6[2] & *nm[2]))&&
216 ((*hm[3] & *nm[3]) == (*a6[3] & *nm[3]))) {
217 #endif
219 return (1);
220 } /* if ip6 address */
222 } /* if AF_INET6 */
223 } /* SLIST */
225 return (0);
228 void
229 recurseloop(int sp, int *raw, DB *db)
231 int sel, ret;
232 int maxso, len;
233 socklen_t slen = sizeof(struct sockaddr_storage);
234 fd_set rset;
235 struct timeval tv;
236 struct srecurseheader rh;
237 struct domain sd;
238 struct dns_header *dh;
239 struct sockaddr_storage ssin;
240 struct sockaddr_in *sin;
241 struct sockaddr_in6 *sin6;
243 int type, lzerrno, wildcard = 0;
245 char fakereplystring[DNS_MAXNAME + 1];
246 char buf[2048];
247 char address[INET6_ADDRSTRLEN];
249 SLIST_INIT(&recurseshead);
251 for (;;) {
252 /*
253 * launch all fakesr requests
254 */
255 SLIST_FOREACH(sr1, &recurseshead, recurses_entry) {
256 if (sr1->isfake && !sr1->launched) {
257 dolog(LOG_DEBUG, "launching question (fakesr) for %s", sr1->question->hdr->name);
258 sr1->launched = 1;
259 type = lookup_zone(db, sr1->question, &sd, &lzerrno, (char *)fakereplystring, wildcard);
260 if (type < 0) {
261 netlookup(db, sr1);
262 } else {
263 SLIST_REMOVE(&recurseshead, sr1, recurses, recurses_entry);
264 sr1->callback->hascallback--;
265 free_question(sr1->question);
266 free(sr1);
270 /*
271 * while we're going through the list to look for
272 * fakesr launches we may as well expire recurses
273 * that have timed out (> 10 seconds)
274 */
275 if (difftime(time(NULL), sr1->received) >= 30) {
276 /* only remove if we don't have any callbacks
277 * outstanding...
278 */
279 if (! sr1->hascallback) {
280 dolog(LOG_DEBUG, "removing recurses struct");
281 SLIST_REMOVE(&recurseshead, sr1, recurses, recurses_entry);
282 if (sr1->so != -1) {
283 if (close(sr1->so) < 0)
284 dolog(LOG_ERR, "close: %m");
285 sr1->so = -1;
288 if (sr1->callback)
289 sr1->callback->hascallback--;
291 free_question(sr1->question);
292 free(sr1);
296 FD_ZERO(&rset);
298 maxso = sp;
299 FD_SET(sp, &rset);
301 /* XXX remember recurseshead is for struct recurses */
302 SLIST_FOREACH(sr1, &recurseshead, recurses_entry) {
303 if (sr1->so != -1) {
304 if (maxso < sr1->so)
305 maxso = sr1->so;
307 FD_SET(sr1->so, &rset);
311 tv.tv_sec = 1;
312 tv.tv_usec = 0;
313 sel = select(maxso + 1, &rset, NULL, NULL, &tv);
314 if (sel < 0) {
315 dolog(LOG_INFO, "select: %m");
316 continue;
317 } else if (sel == 0) {
318 /* timeout */
319 continue;
322 if (FD_ISSET(sp, &rset)) {
323 ret = recv(sp, (char *)&rh, sizeof(rh), 0);
324 if (ret < 0) {
325 dolog(LOG_INFO, "recv: %m");
326 continue;
329 /* place request on struct recurses linked list */
331 sr = calloc(sizeof(struct recurses), 1);
332 if (sr == NULL) {
333 dolog(LOG_ERR, "calloc: %m");
334 continue;
337 memcpy(&sr->query, &rh.buf, 512);
338 sr->len = rh.len;
339 sr->af = rh.af;
340 sr->proto = rh.proto;
341 sr->so = -1;
342 sr->callback = NULL;
343 sr->hascallback = 0;
344 sr->isfake = 0;
345 sr->packetcount = 0;
346 sr->lookrecord = NULL;
347 memcpy(&sr->source, &rh.source, sizeof(struct sockaddr_storage));
348 memcpy(&sr->dest, &rh.dest, sizeof(struct sockaddr_storage));
349 sr->received = time(NULL);
351 sr->question = build_question(sr->query, sr->len);
352 if (sr->question == NULL) {
353 dolog(LOG_ERR, "malformed question in recurse.c");
354 free(sr);
355 continue;
358 type = lookup_zone(db, sr->question, &sd, &lzerrno, (char *)fakereplystring, wildcard);
359 if (type < 0) {
360 if (lzerrno == ERR_NOERROR &&
361 (sd.flags & DOMAIN_NEGATIVE_CACHE) ==
362 DOMAIN_NEGATIVE_CACHE) {
364 reply_raw_nxdomain(db, sr, &sd, raw);
365 free_question(sr->question);
366 free(sr);
367 continue;
370 if (netlookup(db, sr) < 0)
371 continue;
373 SLIST_INSERT_HEAD(&recurseshead, sr, recurses_entry);
374 } else {
375 dolog(LOG_DEBUG, "we had the record in our cache, reply action");
376 /* check if zone is expired */
378 if ((! (sd.flags & DOMAIN_STATIC_ZONE)) &&
379 (sd.created + sd.ttl < time(NULL))) {
380 remove_zone(db, &sd);
382 /* continue with netlookup */
384 if (netlookup(db, sr) < 0)
385 continue;
387 SLIST_INSERT_HEAD(&recurseshead, sr, recurses_entry);
388 continue;
391 if (type == DNS_TYPE_CNAME)
392 reply_raw_cname(db, sr, &sd, raw);
393 else
394 reply_raw(db, sr, &sd, raw);
396 free_question(sr->question);
397 free(sr);
398 continue;
401 } /* FD_ISSET(sp) */
403 SLIST_FOREACH(sr1, &recurseshead, recurses_entry) {
404 if (sr1->so != -1 && FD_ISSET(sr1->so, &rset)) {
405 /*
406 * we got a reply from the nameserver we
407 * queried, now we must parse the input
408 */
410 slen = sizeof(struct sockaddr_storage);
411 if ((len = recvfrom(sr1->so, buf, sizeof(buf), 0, (struct sockaddr *)&ssin, &slen)) < 0) {
412 if (errno != EWOULDBLOCK)
413 dolog(LOG_ERR, "recvfrom: %m");
414 continue;
417 #if 1
418 /* XXX do some checking of expected IP address */
420 switch (ssin.ss_family) {
421 case AF_INET:
422 sin = (struct sockaddr_in *)&ssin;
423 if (sin->sin_addr.s_addr != sr1->a[0]) {
424 dolog(LOG_ERR, "return address is not from right nameserver");
425 continue;
427 break;
428 case AF_INET6:
429 sin6 = (struct sockaddr_in6*)&ssin;
430 if (memcmp((char *)&sin6->sin6_addr, (char *)&sr1->aaaa[0], sizeof(struct in6_addr)) != 0) {
431 inet_ntop(AF_INET6, &sin6->sin6_addr, address, sizeof(address));
433 dolog(LOG_ERR, "return IPv6 address (%s) is not from right nameserver", address);
434 continue;
436 break;
438 #endif
440 if (len < sizeof(struct dns_header)) {
441 dolog(LOG_ERR, "size malformed on reply len=%d", len);
442 /* on error, we just go out and wait for the real ID, this sucks! XXX */
443 continue;
446 dh = (struct dns_header*)&buf[0];
448 if (ntohs(dh->id) != sr1->id) {
449 dolog(LOG_ERR, "unexpected dns ID (%u != %u)", ntohs(dh->id), sr1->id);
450 /* on error, we just go out and wait for the real ID, this sucks! XXX */
451 continue;
454 if (! (ntohs(dh->query) & DNS_REPLY)) {
455 dolog(LOG_ERR, "reply is not a DNS reply");
456 continue;
459 /* XXX */
461 if (close(sr1->so) < 0)
462 dolog(LOG_ERR, "close: %m");
464 sr1->so = -1;
466 if (ntohs(dh->query) & DNS_NAMEERR) {
467 negative_cache(db, sr1);
468 dolog(LOG_DEBUG, "added negative cache for domain \"%s\"", sr1->question->converted_name);
469 /* reply negatively */
470 reply_raw_nxdomain(db, sr1, &sd, raw);
471 goto remove;
474 sr1->authoritative = 0;
475 recurse_parse(db, sr1, (u_char*)&buf, len);
477 /* check if we're flooding anything */
478 if (sr1->packetcount > 50) {
479 dolog(LOG_ERR, "packetcount is over 50, I think I'm flooding something, abort()");
480 slave_shutdown();
481 abort();
484 type = lookup_zone(db, sr1->question, &sd, &lzerrno, (char *)fakereplystring, wildcard);
485 if (type < 0) {
486 dolog(LOG_DEBUG, "lookup_zone failed, doing netlookup");
488 if (sr1->authoritative == DNS_TYPE_NS &&
489 netlookup(db, sr1) < 0) {
490 dolog(LOG_DEBUG, "subsequent netlookup failed");
494 if (sr1->authoritative == DNS_TYPE_SOA) {
495 dolog(LOG_DEBUG, "got an authoritative SOA answer, we'd reply an SOA here");
496 memset(&sd, 0, sizeof(struct domain));
497 get_soa(db, sr1->question, &sd, wildcard);
499 reply_raw_noerror(db, sr, &sd, raw);
500 if (sr1->callback)
501 sr1->callback->hascallback--;
502 goto remove;
505 continue;
506 } else {
507 /* we've found the record we're looking
508 * for do something with it..
509 */
511 if (sr1->isfake) {
512 /* do another netlookup with the callback */
513 dolog(LOG_DEBUG, "sr is fake, doing netlookup on the callback");
515 if (netlookup(db, sr1->callback) < 0) {
516 dolog(LOG_DEBUG, "callback netlookup failed");
519 sr1->callback->hascallback--;
520 /* XXX continue; */
523 } else {
524 if (type == DNS_TYPE_CNAME)
525 reply_raw_cname(db, sr, &sd, raw);
526 else
527 reply_raw(db, sr1, &sd, raw);
530 remove:
531 /* only remove if we don't have any callbacks
532 * outstanding...
533 */
534 if (! sr1->hascallback) {
535 SLIST_REMOVE(&recurseshead, sr1, recurses, recurses_entry);
536 free_question(sr1->question);
537 free(sr1);
540 } /* FD_ISSET(sr1->so */
541 } /* SLIST_FOREACH(sr... */
543 #if 0
544 /*
545 I drew this on a notepad one night, I think that's supposed to how
546 it shoudl go...
548 +----------------+ +--------------------------+
549 | | | |
550 | v v |
551 | -------------------- select ------------------- |
552 | || || |
553 | || || |
554 | +----+ +----+ |
555 | | | take request | | parse reply |
556 | +----+ from authoritative +----+ and insert |
557 | || side / new record |
558 | || / |
559 | || / |
560 | || +---------------+ |
561 | || / |
562 | || / bad or expired |
563 | +----+=====================>+----+ lookup record |
564 | | | lookup name in db | | on the net |
565 | +----+ +----+-------------------+
566 | ||
567 | || good
568 | ||
569 | +----+
570 | | | reply
571 | +----+
572 | ||
573 | ||
574 | ||
575 | +----+
576 +--------| | cleanup
577 +----+
579 */
581 #endif
584 } /* for(;;) */
586 /* NOTREACHED */
589 /*
590 * LOOKUP_NS - given an address try to look up the nameservers anywhere along
591 * its path. return number of servers reachable or -1 on error.
592 */
594 int
595 lookup_ns(DB *db, struct recurses *sr)
597 int ret, plen, i;
598 int onemore = 0;
599 char *p;
601 DBT key, data;
603 struct domain *sd, mydomain;
605 p = sr->question->hdr->name;
606 plen = sr->question->hdr->namelen;
608 do {
609 again:
610 memset(&key, 0, sizeof(key));
611 memset(&data, 0, sizeof(data));
613 key.data = (char *)p;
614 key.size = plen;
616 data.data = NULL;
617 data.size = 0;
619 ret = db->get(db, NULL, &key, &data, 0);
620 if (ret != 0) {
621 if (*p != 0) {
622 plen -= (*p + 1);
623 p = (p + (*p + 1));
624 sr->indicator++;
627 /* XXX this is different from lookup_zone(), not
628 * sure how it even works there...
629 */
630 if (*p == 0 && ! onemore) {
631 plen = 1;
632 onemore = 1;
633 sr->indicator++;
634 goto again; /* XXX */
636 } else {
637 /* we have a lookup */
639 if (data.size != sizeof(struct domain)) {
640 dolog(LOG_ERR, "btree db is damaged");
641 return (-1);
644 #if 0
645 dolog(LOG_DEBUG, "we gots a lookup, yay!\n");
646 #endif
648 /*
649 * record which record we used
650 */
652 sr->lookrecord = (u_char *)p;
654 memcpy((char *)&mydomain, (char *)data.data, sizeof(struct domain));
655 sd = (struct domain *)&mydomain;
657 /*
658 * If we're not a static zone (like hints) and we're
659 * expired then we go on to the next indicator..
660 * .. but first we must remove this zone...
661 */
662 if ((! (sd->flags & DOMAIN_STATIC_ZONE)) &&
663 (time(NULL) > (sd->created + sd->ttl))) {
665 remove_zone(db, sd);
667 if (*p != 0) {
668 plen -= (*p + 1);
669 p = (p + (*p + 1));
670 sr->indicator++;
671 continue;
672 } else {
673 return (-1);
676 /*
677 * If we have a negative cache, then just return with
678 * error.
679 */
680 if ((sd->flags & DOMAIN_NEGATIVE_CACHE) &&
681 (time(NULL) <= (sd->created + sd->ttl))) {
682 return (-1);
685 sr->aaaa_count = 0;
686 sr->a_count = 0;
687 sr->a_ptr = 0;
689 for (i = 0; i < sd->ns_count; i++) {
690 if (sr->af == AF_INET6) {
691 if (lookup_aaaa(db, sr, &sd->ns[(sd->ns_ptr + i) % sd->ns_count] ) < 0)
692 continue;
693 sr->aaaa_count++;
694 } else {
695 if (lookup_a(db, sr, &sd->ns[(sd->ns_ptr + i) % sd->ns_count] ) < 0)
696 continue;
697 sr->a_count++;
701 if (sd->ns_count)
702 sd->ns_ptr = (sd->ns_ptr + 1) % sd->ns_count;
703 else
704 sd->ns_ptr = 0;
706 update_db(db, sd);
708 break;
711 } while (*p != 0 && ret != 0);
713 #if 1
714 dolog(LOG_DEBUG, "got %d addresses for %s, indicator %d\n", sr->a_count, sr->question->hdr->name, sr->indicator);
716 #endif
718 return ((sr->af == AF_INET6) ? sr->aaaa_count : sr->a_count);
722 /*
723 * LOOKUP_A - given a path, lookup the A record in that record
725 */
727 int
728 lookup_a(DB *db, struct recurses *sr, struct ns *ns)
730 int ret, plen;
731 char *p;
733 DBT key, data;
735 struct domain *sd, sdomain;
736 int found = 0;
738 p = ns->nsserver;
739 plen = ns->nslen;
741 memset(&key, 0, sizeof(key));
742 memset(&data, 0, sizeof(data));
744 key.data = (char *)p;
745 key.size = plen;
747 data.data = NULL;
748 data.size = 0;
750 found = 0;
752 ret = db->get(db, NULL, &key, &data, 0);
753 if (ret == 0) {
754 if (data.size != sizeof(struct domain)) {
755 dolog(LOG_ERR, "btree db is damaged");
756 return (-1);
759 memcpy((char*)&sdomain, data.data, sizeof(struct domain));
760 sd = &sdomain;
762 if ((sd->flags & DOMAIN_HAVE_A) == DOMAIN_HAVE_A) {
763 memcpy((char *)&sr->a[sr->a_count], (char *)&sd->a[0], sizeof(in_addr_t));
764 sd->a_count++;
765 found = 1;
770 if (! found) {
771 dolog(LOG_DEBUG, "calling fakerecurse");
772 fakerecurse(db, sr, ns, DNS_TYPE_A);
773 return (-1);
776 return (0);
779 /*
780 * NEGATIVE_CACHE - cache a lookup as negative (NXDOMAIN)
782 */
784 int
785 negative_cache(DB *db, struct recurses *sr)
787 struct domain sd;
789 memset(&sd, 0, sizeof(sd));
791 sd.zonelen = sr->question->hdr->namelen;
793 memcpy((char *)&sd.zone, (char *)sr->question->hdr->name, sd.zonelen);
795 #if __linux__
796 strncpy((char *)&sd.zonename, (char *)sr->question->converted_name, DNS_MAXNAME);
797 sd.zonename[DNS_MAXNAME] = 0;
798 #else
799 strlcpy((char *)&sd.zonename, (char *)sr->question->converted_name, DNS_MAXNAME + 1);
800 #endif
802 sd.created = time(NULL);
803 sd.ttl = NEGATIVE_CACHE_TIME; /* 10 minutes */
805 sd.flags |= DOMAIN_NEGATIVE_CACHE;
807 update_db(db, &sd);
809 return (0);
812 /*
813 * RECURSE_PARSE - based on compress_label.
815 */
817 int
818 recurse_parse(DB *db, struct recurses *sr, u_char *buf, u_int16_t offset)
820 u_char *label[256]; /* should be enough */
821 static u_char converted_name[256][256];
822 u_int8_t cn_len[256];
823 u_char *end = &buf[offset];
824 int update;
825 int rrcount[3]; /* RR count answer, authoritative, additional */
826 int pointer = 0; /* default answer */
827 int txtlen;
829 char abuf[INET6_ADDRSTRLEN];
831 DBT key, data;
833 struct domain sdomain;
834 struct dns_header *dh;
835 struct question {
836 u_int16_t type;
837 u_int16_t class;
838 } __attribute__((packed));
839 struct answer {
840 u_int16_t type;
841 u_int16_t class;
842 u_int32_t ttl;
843 u_int16_t rdlength;
844 } __attribute__((packed));
845 struct mysoa {
846 u_int32_t serial;
847 u_int32_t refresh;
848 u_int32_t retry;
849 u_int32_t expire;
850 u_int32_t minttl;
851 } __attribute__((packed));
853 struct answer *a;
854 struct mysoa *mysoa;
855 struct soa *soa;
856 struct ns ns;
858 u_int i, j, k;
859 u_int16_t *compressor;
860 u_int16_t c;
861 u_int16_t *preference;
863 int found = 0;
865 u_char *p, *q, *r;
867 dh = (struct dns_header*)&buf[0];
868 rrcount[pointer++] = ntohs(dh->answer);
869 rrcount[pointer++] = ntohs(dh->nsrr);
870 rrcount[pointer++] = ntohs(dh->additional);
872 pointer = 0;
873 while (rrcount[pointer] == 0) {
874 pointer++;
875 if (pointer > 2)
876 return (-1);
879 p = &buf[sizeof(struct dns_header)];
880 label[0] = p;
882 while (p <= end && *p) {
883 p += *p;
884 p++;
887 /*
888 * the question label was bogus, we'll just get out of there, return 0
889 */
891 if (p > end)
892 return (-1);
894 p += sizeof(struct question);
895 p++; /* one more */
896 /* start of answer/additional/authoritative */
898 for (i = 1; i < 100; i++) {
899 label[i] = p;
901 while (p <= end && *p) {
902 if ((*p & 0xc0) == 0xc0) {
903 p++;
904 break;
906 p += *p;
907 p++;
909 if (p > end)
910 goto end;
913 p++; /* one more */
916 a = (struct answer *)p;
917 p += sizeof(struct answer);
919 if (p > end)
920 goto end;
922 switch (ntohs(a->type)) {
923 case DNS_TYPE_A:
924 p += sizeof(in_addr_t);
925 break;
926 case DNS_TYPE_AAAA:
927 p += 16; /* sizeof 4 * 32 bit */
928 break;
929 case DNS_TYPE_TXT:
930 p += *p;
931 p++;
932 break;
933 case DNS_TYPE_MX:
934 p += sizeof(u_int16_t); /* mx_priority */
935 /* FALLTHROUGH */
936 case DNS_TYPE_NS:
937 case DNS_TYPE_PTR:
938 case DNS_TYPE_CNAME:
939 label[++i] = p;
940 while (p <= end && *p) {
941 if ((*p & 0xc0) == 0xc0) {
942 p++;
943 break;
945 p += *p;
946 p++;
948 if (p > end)
949 goto end;
952 p++; /* one more */
953 break;
954 case DNS_TYPE_SOA:
955 /* nsserver */
956 label[++i] = p;
957 while (p <= end && *p) {
958 if ((*p & 0xc0) == 0xc0) {
959 p++;
960 break;
962 p += *p;
963 p++;
964 if (p > end)
965 goto end;
968 p++; /* one more */
970 if (p > end)
971 goto end;
973 /* responsible person */
974 label[++i] = p;
975 while (p <= end && *p) {
976 if ((*p & 0xc0) == 0xc0) {
977 p++;
978 break;
980 p += *p;
981 p++;
984 p++; /* one more */
986 if (p > end)
987 goto end;
989 p += sizeof(struct mysoa); /* advance struct soa */
991 break;
992 default:
993 break;
994 /* XXX */
995 } /* switch */
997 if (p >= end)
998 break;
999 } /* for (i *) */
1002 * go through our list of labels and expand them from possible
1003 * compression, then we make our next pass..
1006 for (j = 0; j <= i; j++) {
1007 q = converted_name[j];
1008 p = label[j];
1009 again:
1010 for (; *p ; p += *p, p++) {
1011 if ((*p & 0xc0) == 0xc0)
1012 break;
1014 r = (p + 1);
1016 *q++ = *p;
1018 for (k = 0; k < *p; k++)
1019 *q++ = tolower(r[k]);
1022 if ((*p & 0xc0) == 0xc0) {
1023 compressor = (u_int16_t *)p;
1024 c = (ntohs(*compressor) & ~(0xc000));
1025 for (k = 0; k <= i; k++) {
1026 found = 0;
1027 for (r = label[k]; *r; r += *r, r++) {
1028 if (r - buf == c) {
1029 p = r;
1030 found = 1;
1035 * we're searching for an offset in
1036 * non-compressed form, but we
1037 * encountered compression, so we
1038 * break and go to the next label
1041 if ((*r & 0xc0) == 0xc0) {
1042 break;
1046 if (found) {
1048 * pretend we found a match but we
1049 * have compression inside pointing
1050 * down, then break this, it's corrupt
1051 * it's a possible loop attempt
1053 if ((*r & 0xc0) == 0xc0) {
1054 compressor = (u_int16_t *)r;
1055 if ((ntohs(*compressor) & ~0xc000) >= c)
1056 break;
1059 goto again;
1064 * if we fall through the for loop, we didn't find the
1065 * recursive label... corrupt.
1068 dolog(LOG_ERR, "corrupt compression");
1069 return (-1);
1072 *q++ = '\0'; /* don't forget this */
1073 cn_len[j] = (q - converted_name[j]);
1075 } /* for (j .. */
1077 #if 0
1078 for (j = 0; j <= i; j++) {
1079 dolog(LOG_DEBUG, "%s with length %u", converted_name[j], cn_len[j]);
1081 #endif
1083 p = &buf[sizeof(struct dns_header)];
1084 label[0] = p;
1086 while (p <= end && *p) {
1087 p += *p;
1088 p++;
1092 * the question label was bogus, we'll just get out of there, return 0
1095 if (p > end)
1096 return (-1);
1098 p += sizeof(struct question);
1099 p++; /* one more */
1100 /* start of answer/additional/authoritative */
1102 for (i = 1; i < 100; i++) {
1103 label[i] = p;
1105 while (p <= end && *p) {
1106 if ((*p & 0xc0) == 0xc0) {
1107 p++;
1108 break;
1110 p += *p;
1111 p++;
1113 if (p > end)
1114 goto end;
1117 p++; /* one more */
1120 a = (struct answer *)p;
1121 p += sizeof(struct answer);
1123 /* load our record */
1124 memset(&key, 0, sizeof(key));
1125 memset(&data, 0, sizeof(data));
1127 key.data = (char *)converted_name[i];
1128 key.size = cn_len[i];
1130 data.data = NULL;
1131 data.size = 0;
1133 memset((char *)&sdomain, 0, sizeof(struct domain));
1134 if (db->get(db, NULL, &key, &data, 0) == 0) {
1135 if (data.size != sizeof(struct domain)) {
1136 dolog(LOG_INFO, "damaged btree database");
1137 return -1;
1140 memcpy((char *)&sdomain, (char *)data.data, data.size);
1145 if (sdomain.zone == NULL) {
1146 memcpy(&sdomain.zone, converted_name[i], cn_len[i]);
1147 sdomain.zonelen = cn_len[i];
1151 switch (ntohs(a->type)) {
1152 case DNS_TYPE_A:
1154 * scan addresses in this struct domain and check if
1155 * this one exists already...
1158 update = 1;
1159 for (j = 0; j < sdomain.a_count; j++) {
1160 if (memcmp(&sdomain.a[j], p, sizeof(in_addr_t)) == 0) {
1161 #if 0
1162 dolog(LOG_INFO, "record exists already");
1163 #endif
1164 update = 0;
1168 if (j >= RECORD_COUNT) {
1169 dolog(LOG_INFO, "db can't hold any more records\n");
1170 update = 0;
1174 * check if we're a 2nd level domain or higher and
1175 * if we were directly querying the zone...
1178 if (update) {
1179 if (level(sr->lookrecord) > 1) {
1180 if (!contains(sr->lookrecord, converted_name[i])) {
1181 memcpy(ns.nsserver, converted_name[i], cn_len[i]);
1182 ns.nslen = cn_len[i];
1184 fakerecurse(db, sr, &ns, DNS_TYPE_A);
1185 update = 0;
1191 if (update) {
1192 memcpy(&sdomain.a[j], p, sizeof(in_addr_t));
1193 sdomain.a_count++;
1194 sdomain.region[j] = 0xff;
1195 sdomain.a_ptr = 0;
1196 sdomain.flags |= DOMAIN_HAVE_A;
1197 sdomain.flags &= ~(DOMAIN_NEGATIVE_CACHE);
1198 sdomain.created = time(NULL);
1199 sdomain.ttl = ntohl(a->ttl);
1201 if (! (sdomain.flags & DOMAIN_STATIC_ZONE)) {
1202 update_db(db, &sdomain);
1203 inet_ntop(AF_INET, p, abuf, sizeof(abuf));
1204 dolog(LOG_DEBUG, "updateing zone %s with address %s ttl= %u, lookrecord = %s", converted_name[i], abuf, sdomain.ttl, sr->lookrecord);
1208 p += sizeof(in_addr_t);
1209 if (pointer > 2) {
1210 dolog(LOG_ERR, "there is more records than indicated in the header!!!");
1211 return (-1);
1213 rrcount[pointer]--;
1214 if (rrcount[pointer] == 0)
1215 pointer++;
1217 break;
1218 case DNS_TYPE_AAAA:
1220 * scan addresses in this struct domain and check if
1221 * this one exists already...
1224 update = 1;
1225 for (j = 0; j < sdomain.aaaa_count; j++) {
1226 if (memcmp(&sdomain.aaaa[j], p, sizeof(struct in6_addr)) == 0) {
1227 #if 0
1228 dolog(LOG_INFO, "record exists already");
1229 #endif
1230 update = 0;
1234 if (j >= RECORD_COUNT) {
1235 dolog(LOG_INFO, "db can't hold any more records\n");
1236 update = 0;
1240 * check if we're a 2nd level domain or higher and
1241 * if we were directly querying the zone...
1244 if (update) {
1245 if (level(sr->lookrecord) > 1) {
1246 if (!contains(sr->lookrecord, converted_name[i])) {
1247 memcpy(ns.nsserver, converted_name[i], cn_len[i]);
1248 ns.nslen = cn_len[i];
1250 fakerecurse(db, sr, &ns, DNS_TYPE_AAAA);
1251 update = 0;
1256 if (update) {
1257 memcpy(&sdomain.aaaa[j], p, sizeof(struct in6_addr));
1258 sdomain.aaaa_count++;
1259 sdomain.aaaa_ptr = 0;
1260 sdomain.flags |= DOMAIN_HAVE_AAAA;
1261 sdomain.flags &= ~(DOMAIN_NEGATIVE_CACHE);
1262 sdomain.created = time(NULL);
1263 sdomain.ttl = ntohl(a->ttl);
1265 if (! (sdomain.flags & DOMAIN_STATIC_ZONE)) {
1266 update_db(db, &sdomain);
1267 inet_ntop(AF_INET6, p, abuf, sizeof(abuf));
1268 dolog(LOG_DEBUG, "updateing zone %s with address %s ttl= %u\n", converted_name[i], abuf, sdomain.ttl);
1272 if (pointer > 2) {
1273 dolog(LOG_ERR, "there is more records than indicated in the header!!!");
1274 return (-1);
1276 rrcount[pointer]--;
1277 if (rrcount[pointer] == 0)
1278 pointer++;
1280 p += 16; /* sizeof 4 * 32 bit */
1281 break;
1282 case DNS_TYPE_TXT:
1283 txtlen = (*p);
1285 memcpy(&sdomain.txt, (p + 1), txtlen);
1286 sdomain.txtlen = txtlen;
1288 sdomain.flags |= DOMAIN_HAVE_TXT;
1289 sdomain.flags &= ~(DOMAIN_NEGATIVE_CACHE);
1290 sdomain.created = time(NULL);
1291 sdomain.ttl = ntohl(a->ttl);
1293 if (! (sdomain.flags & DOMAIN_STATIC_ZONE)) {
1294 update_db(db, &sdomain);
1297 if (pointer > 2) {
1298 dolog(LOG_ERR, "there is more records than indicated in the header!!!");
1299 return (-1);
1302 rrcount[pointer]--;
1303 if (rrcount[pointer] == 0)
1304 pointer++;
1307 p += *p;
1308 p++;
1309 break;
1310 case DNS_TYPE_MX:
1311 preference = (u_int16_t *)p;
1312 p += sizeof(u_int16_t); /* mx_priority */
1313 /* FALLTHROUGH */
1314 case DNS_TYPE_NS:
1315 case DNS_TYPE_PTR:
1316 case DNS_TYPE_CNAME:
1317 label[++i] = p;
1318 while (p <= end && *p) {
1319 if ((*p & 0xc0) == 0xc0) {
1320 p++;
1321 break;
1323 p += *p;
1324 p++;
1326 if (p > end)
1327 goto end;
1330 if (ntohs(a->type) == DNS_TYPE_CNAME) {
1332 * we check this as well as the A and AAAA
1333 * types since it may be possible to glue
1334 * a CNAME instead of an A and thus poison
1335 * our cache...
1337 if (level(sr->lookrecord) > 1) {
1338 if (!contains(sr->lookrecord, converted_name[i - 1])) {
1340 memcpy(ns.nsserver, converted_name[i - 1], cn_len[i - 1]);
1341 ns.nslen = cn_len[i];
1343 fakerecurse(db, sr, &ns, DNS_TYPE_A);
1344 rrcount[pointer]--;
1345 if (rrcount[pointer] == 0)
1346 pointer++;
1347 p++;
1348 break;
1352 memcpy(&sdomain.cname, converted_name[i], cn_len[i]);
1353 sdomain.cnamelen = cn_len[i];
1355 sdomain.flags |= DOMAIN_HAVE_CNAME;
1356 sdomain.flags &= ~(DOMAIN_NEGATIVE_CACHE);
1357 sdomain.created = time(NULL);
1358 sdomain.ttl = ntohl(a->ttl);
1360 if (! (sdomain.flags & DOMAIN_STATIC_ZONE)) {
1361 update_db(db, &sdomain);
1362 #if 1
1363 dolog(LOG_DEBUG, "updateing zone %s with PTR name %s ttl= %u\n", converted_name[i - 1], converted_name[i], sdomain.ttl);
1364 #endif
1367 if (pointer > 2) {
1368 dolog(LOG_ERR, "there is more records than indicated in the header!!!");
1369 return (-1);
1372 rrcount[pointer]--;
1373 if (rrcount[pointer] == 0)
1374 pointer++;
1375 } else if (ntohs(a->type) == DNS_TYPE_PTR) {
1376 memcpy(&sdomain.ptr, converted_name[i], cn_len[i]);
1377 sdomain.ptrlen = cn_len[i];
1379 sdomain.flags |= DOMAIN_HAVE_PTR;
1380 sdomain.flags &= ~(DOMAIN_NEGATIVE_CACHE);
1381 sdomain.created = time(NULL);
1382 sdomain.ttl = ntohl(a->ttl);
1384 if (! (sdomain.flags & DOMAIN_STATIC_ZONE)) {
1385 update_db(db, &sdomain);
1386 #if 1
1387 dolog(LOG_DEBUG, "updateing zone %s with PTR name %s ttl= %u\n", converted_name[i - 1], converted_name[i], sdomain.ttl);
1388 #endif
1391 if (pointer > 2) {
1392 dolog(LOG_ERR, "there is more records than indicated in the header!!!");
1393 return (-1);
1396 rrcount[pointer]--;
1397 if (rrcount[pointer] == 0)
1398 pointer++;
1399 } else if (ntohs(a->type) == DNS_TYPE_NS) {
1400 update = 1;
1401 for (j = 0; j < sdomain.ns_count; j++) {
1402 if (memcasecmp((u_char *)sdomain.ns[j].nsserver, (u_char *)converted_name[i], MIN(cn_len[i], sdomain.ns[j].nslen)) == 0) {
1403 #if 0
1404 dolog(LOG_INFO, "record exists already");
1405 #endif
1406 update = 0;
1410 if (j >= RECORD_COUNT) {
1411 dolog(LOG_INFO, "db can't hold any more records\n");
1412 update = 0;
1415 if (update) {
1416 memcpy(sdomain.ns[j].nsserver, converted_name[i], cn_len[i]);
1417 sdomain.ns[j].nslen = cn_len[i];
1419 sdomain.ns_count++;
1420 sdomain.ns_ptr = 0;
1421 sdomain.flags |= DOMAIN_HAVE_NS;
1422 sdomain.flags &= ~(DOMAIN_NEGATIVE_CACHE);
1423 sdomain.created = time(NULL);
1424 sdomain.ttl = ntohl(a->ttl);
1426 if (! (sdomain.flags & DOMAIN_STATIC_ZONE)) {
1427 update_db(db, &sdomain);
1428 #if 0
1429 dolog(LOG_DEBUG, "updateing zone %s with NS name %s ttl= %u\n", converted_name[i - 1], converted_name[i], sdomain.ttl);
1430 #endif
1432 } /* if update */
1433 if (pointer > 2) {
1434 dolog(LOG_ERR, "there is more records than indicated in the header!!!");
1435 return (-1);
1438 rrcount[pointer]--;
1439 if (pointer == 1) /* authoritative */
1440 sr->authoritative = DNS_TYPE_NS;
1441 if (rrcount[pointer] == 0)
1442 pointer++;
1444 } else if (ntohs(a->type) == DNS_TYPE_MX) {
1445 update = 1;
1446 for (j = 0; j < sdomain.mx_count; j++) {
1447 if (memcasecmp((u_char *)sdomain.mx[j].exchange, (u_char *)converted_name[i], MIN(cn_len[i], sdomain.mx[j].exchangelen)) == 0) {
1448 update = 0;
1452 if (j >= RECORD_COUNT) {
1453 dolog(LOG_INFO, "db can't hold any more records\n");
1454 update = 0;
1457 if (update) {
1458 memcpy(&sdomain.mx[j].exchange, converted_name[i], cn_len[i]);
1459 sdomain.mx[j].exchangelen = cn_len[i];
1460 sdomain.mx[j].preference = ntohs(*preference);
1462 sdomain.mx_count++;
1463 sdomain.mx_ptr = 0;
1464 sdomain.flags |= DOMAIN_HAVE_MX;
1465 sdomain.flags &= ~(DOMAIN_NEGATIVE_CACHE);
1466 sdomain.created = time(NULL);
1467 sdomain.ttl = ntohl(a->ttl);
1469 if (! (sdomain.flags & DOMAIN_STATIC_ZONE)) {
1470 update_db(db, &sdomain);
1471 #if 0
1472 dolog(LOG_DEBUG, "updateing zone %s with MX name %s ttl= %u\n", converted_name[i - 1], converted_name[i], sdomain.ttl);
1473 #endif
1475 } /* if update */
1476 if (pointer > 2) {
1477 dolog(LOG_ERR, "there is more records than indicated in the header!!!");
1478 return (-1);
1481 rrcount[pointer]--;
1482 #if 0
1483 if (pointer == 1) /* authoritative */
1484 sr->authoritative = DNS_TYPE_MX;
1485 #endif
1486 if (rrcount[pointer] == 0)
1487 pointer++;
1489 } /* if type ns */
1491 p++; /* one more */
1492 break;
1493 case DNS_TYPE_SOA:
1494 /* nsserver */
1495 label[++i] = p;
1496 while (p <= end && *p) {
1497 if ((*p & 0xc0) == 0xc0) {
1498 p++;
1499 break;
1501 p += *p;
1502 p++;
1503 if (p > end)
1504 goto end;
1507 p++; /* one more */
1509 if (p > end)
1510 goto end;
1512 /* responsible person */
1513 label[++i] = p;
1514 while (p <= end && *p) {
1515 if ((*p & 0xc0) == 0xc0) {
1516 p++;
1517 break;
1519 p += *p;
1520 p++;
1523 p++; /* one more */
1525 if (p > end)
1526 goto end;
1528 mysoa = (struct mysoa *)p;
1529 p += sizeof(struct mysoa); /* advance struct soa */
1531 /* malloc struct soa */
1533 soa = malloc(sizeof(struct soa));
1534 if (soa == NULL) {
1535 dolog(LOG_ERR, "malloc: %m");
1536 return (-1);
1539 memcpy(soa->nsserver, converted_name[i - 1], cn_len[i - 1]);
1540 soa->nsserver_len = cn_len[i - 1];
1541 memcpy(soa->responsible_person, converted_name[i], cn_len[i]);
1542 soa->rp_len = cn_len[i];
1543 soa->serial = ntohl(mysoa->serial);
1544 soa->refresh = ntohl(mysoa->refresh);
1545 soa->retry = ntohl(mysoa->retry);
1546 soa->expire = ntohl(mysoa->expire);
1547 soa->minttl = ntohl(mysoa->minttl);
1549 memcpy(&sdomain.soa, soa, sizeof(sdomain.soa));
1550 free(soa);
1552 sdomain.flags |= DOMAIN_HAVE_SOA;
1553 sdomain.created = time(NULL);
1554 sdomain.ttl = htonl(a->ttl);
1556 if (! (sdomain.flags & DOMAIN_STATIC_ZONE)) {
1557 update_db(db, &sdomain);
1560 if (pointer > 2) {
1561 dolog(LOG_ERR, "there is more records than indicated in the header!!!");
1562 return (-1);
1565 rrcount[pointer]--;
1566 if (pointer == 1) /* authoritative */
1567 sr->authoritative = DNS_TYPE_SOA;
1568 if (rrcount[pointer] == 0)
1569 pointer++;
1572 break;
1573 default:
1574 break;
1575 /* XXX */
1576 } /* switch */
1578 if (p >= end)
1579 break;
1580 } /* for (i *) */
1583 return (0);
1585 end:
1586 dolog(LOG_DEBUG, "mangled input packet");
1587 return (-1);
1593 * NETLOOKUP - do a internet lookup of the requested internet record
1597 int
1598 netlookup(DB *db, struct recurses *sr)
1600 struct sockaddr_in sin;
1601 struct dns_header *dh;
1603 char buf[2048];
1604 int flag;
1607 /* do the network stuff then */
1608 /* XXX should be IPv6 ready */
1610 if (sr->af == AF_INET6)
1611 return (netlookup6(db, sr));
1613 if (sr->so != -1) {
1614 if (close(sr->so) < 0)
1615 dolog(LOG_ERR, "close: %m");
1618 sr->so = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1619 if (sr->so < 0) {
1620 dolog(LOG_ERR, "socket: %m");
1621 sr->so = -1;
1622 return (-1);
1625 sr->port = arc4random() & 0xffff;
1627 * we have to avoid picking servers already
1628 * running ..
1630 if (sr->port < 1024)
1631 sr->port += 1024;
1633 sr->id = arc4random() & 0xffff;
1635 memset(&sin, 0, sizeof(sin));
1636 sin.sin_family = AF_INET;
1637 sin.sin_port = htons(sr->port);
1638 sin.sin_addr.s_addr = INADDR_ANY;
1640 if (bind(sr->so, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
1641 dolog(LOG_ERR, "bind: %m");
1642 if (close(sr->so) < 0) {
1643 dolog(LOG_ERR, "close: %m");
1645 sr->so = -1;
1646 return (-1);
1650 * make this socket nonblocking
1653 if ((flag = fcntl(sr->so, F_GETFL)) < 0) {
1654 dolog(LOG_INFO, "fcntl 3: %m");
1656 flag |= O_NONBLOCK;
1657 if (fcntl(sr->so, F_SETFL, flag) < 0) {
1658 dolog(LOG_INFO, "fcntl 4: %m");
1661 if (lookup_ns(db, sr) <= 0) {
1662 dolog(LOG_ERR, "can't establish any servers to reach for zone \"%s\"", sr->question->converted_name);
1663 if (close(sr->so) < 0) {
1664 dolog(LOG_ERR, "close: %m");
1666 sr->so = -1;
1667 return (-1);
1670 memset(&sin, 0, sizeof(sin));
1671 sin.sin_family = AF_INET;
1672 sin.sin_port = htons(53);
1674 sin.sin_addr.s_addr = sr->a[0];
1676 /* XXX we use buf here in order to preserve
1677 * the state of query...
1679 memcpy(buf, sr->query, sr->len);
1680 dh = (struct dns_header *)&buf[0];
1681 NTOHS(dh->query);
1682 UNSET_DNS_RECURSION(dh);
1683 HTONS(dh->query);
1684 dh->id = htons(sr->id);
1686 #if 1
1687 dolog(LOG_INFO, "sending request with id %u\n", sr->id);
1689 #endif
1691 if (sendto(sr->so, buf, sr->len, 0, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
1692 dolog(LOG_ERR, "sendto: %m");
1693 if (close(sr->so) < 0) {
1694 dolog(LOG_ERR, "close: %m");
1696 sr->so = -1;
1697 return (-1);
1700 sr->sent_last_query = time(NULL);
1701 sr->packetcount++;
1704 return (0);
1708 * FAKERECURSE - create a fake query of type A, for a nameserver that has
1709 * no glued A record, attach a callback to the struct recurses
1710 * that did the initiation for this...
1713 int
1714 fakerecurse(DB *db, struct recurses *sr, struct ns *ns, int type)
1716 struct recurses *fakesr;
1717 struct dns_header *dh;
1719 char *p;
1721 int len;
1722 u_int16_t *qtype, *qclass;
1724 /* check if we have already started a fakerecurse on the same name */
1726 SLIST_FOREACH(sr2, &recurseshead, recurses_entry) {
1727 if (memcasecmp((u_char *)ns->nsserver, (u_char *)sr2->question->hdr->name, MIN(ns->nslen, sr2->question->hdr->namelen)) == 0) {
1728 dolog(LOG_INFO, "already have a fakerecurse structure with name %s, drop\n", ns->nsserver);
1729 return (-1);
1734 /* place request on struct recurses linked list */
1736 fakesr = calloc(sizeof(struct recurses), 1);
1737 if (fakesr == NULL) {
1738 dolog(LOG_ERR, "calloc: %m");
1739 return (-1);
1743 fakesr->af = sr->af;
1744 fakesr->proto = sr->proto;
1745 fakesr->so = -1;
1746 fakesr->callback = sr;
1747 sr->hascallback++;
1748 fakesr->hascallback = 0;
1749 fakesr->isfake = 1;
1750 fakesr->launched = 0;
1751 fakesr->received = time(NULL);
1752 fakesr->packetcount = 0;
1753 fakesr->lookrecord = NULL;
1755 fakesr->question = build_fake_question(ns->nsserver, ns->nslen, htons(type));
1756 if (fakesr->question == NULL) {
1757 dolog(LOG_ERR, "malformed question in recurse.c");
1758 free(fakesr);
1759 return (-1);
1762 /* construct the question packet */
1764 len = sizeof(struct dns_header);
1765 dh = (struct dns_header *)fakesr->query;
1766 dh->id = htons(1);
1767 SET_DNS_QUERY(dh);
1768 HTONS(dh->query);
1769 dh->question = htons(1);
1770 dh->answer = 0;
1771 dh->nsrr = 0;
1772 dh->additional = 0;
1774 p = (char *)&fakesr->query[len];
1775 memcpy(p, ns->nsserver, ns->nslen);
1776 len += ns->nslen;
1777 qtype = (u_int16_t *)&fakesr->query[len];
1778 *qtype = fakesr->question->hdr->qtype;
1779 len += sizeof(u_int16_t);
1780 qclass = (u_int16_t *)&fakesr->query[len];
1781 *qclass = fakesr->question->hdr->qclass;
1782 len += sizeof(u_int16_t);
1784 fakesr->len = len;
1786 SLIST_INSERT_HEAD(&recurseshead, fakesr, recurses_entry);
1788 return (0);
1792 * REPLY_RAW -
1797 void
1798 reply_raw(DB *db, struct recurses *sr, struct domain *sd, int *raw)
1800 int so;
1801 struct sreply sreply;
1803 dolog(LOG_DEBUG, "reply_raw called");
1805 switch (sr->af) {
1806 case AF_INET:
1807 so = raw[0];
1808 break;
1809 case AF_INET6:
1810 so = raw[1];
1811 break;
1812 default:
1813 dolog(LOG_ERR, "reply_raw(): unknown address family in struct recurses");
1814 return;
1817 switch (sr->proto) {
1818 case IPPROTO_UDP:
1819 break;
1820 default:
1821 dolog(LOG_ERR, "reply_raw(): can't do any protocol other than udp right now");
1822 return;
1825 build_reply(&sreply, so, sr->query, sr->len, sr->question, NULL, 0, sd, NULL, 0xff, 0, 0, sr);
1827 switch (ntohs(sr->question->hdr->qtype)) {
1828 case DNS_TYPE_A:
1829 reply_a(&sreply, db);
1830 break;
1831 case DNS_TYPE_AAAA:
1832 reply_aaaa(&sreply, db);
1833 break;
1834 case DNS_TYPE_NS:
1835 reply_ns(&sreply, db);
1836 break;
1837 case DNS_TYPE_PTR:
1838 reply_ptr(&sreply);
1839 break;
1840 case DNS_TYPE_MX:
1841 reply_mx(&sreply, db);
1842 break;
1843 case DNS_TYPE_SOA:
1844 reply_soa(&sreply);
1845 break;
1846 case DNS_TYPE_CNAME:
1847 reply_cname(&sreply);
1848 break;
1849 case DNS_TYPE_TXT:
1850 reply_txt(&sreply);
1851 break;
1852 default:
1853 dolog(LOG_ERR, "other types have not been implemented yet");
1854 break;
1857 return;
1860 void
1861 reply_raw_cname(DB *db, struct recurses *sr, struct domain *sd, int *raw)
1863 int so;
1864 struct sreply sreply;
1866 dolog(LOG_DEBUG, "reply_raw called");
1868 switch (sr->af) {
1869 case AF_INET:
1870 so = raw[0];
1871 break;
1872 case AF_INET6:
1873 so = raw[1];
1874 break;
1875 default:
1876 dolog(LOG_ERR, "reply_raw_cname(): unknown address family in struct recurses");
1877 return;
1880 switch (sr->proto) {
1881 case IPPROTO_UDP:
1882 break;
1883 default:
1884 dolog(LOG_ERR, "reply_raw_cname(): can't do any protocol other than udp right now");
1885 return;
1888 build_reply(&sreply, so, sr->query, sr->len, sr->question, NULL, 0, sd, NULL, 0xff, 0, 0, sr);
1890 reply_cname(&sreply);
1892 return;
1896 * REMOVE_ZONE - remove a zone from the database (it probably expired)
1901 void
1902 remove_zone(DB *db, struct domain *sd)
1904 DBT key;
1905 char *zone;
1906 int zonelen;
1908 zone = sd->zone;
1909 zonelen = sd->zonelen;
1911 key.data = (char *)zone;
1912 key.size = zonelen;
1914 if (db->del(db, NULL, &key, 0) != 0) {
1915 dolog(LOG_ERR, "could not delete zone %s: %m", zone);
1918 dolog(LOG_DEBUG, "deleting zone %s\n", zone);
1920 free(zone);
1922 return;
1925 void
1926 reply_raw_noerror(DB *db, struct recurses *sr, struct domain *sd, int *raw)
1928 int so;
1929 struct sreply sreply;
1931 dolog(LOG_DEBUG, "reply_raw_noerror called");
1933 switch (sr->af) {
1934 case AF_INET:
1935 so = raw[0];
1936 break;
1937 case AF_INET6:
1938 so = raw[1];
1939 break;
1940 default:
1941 dolog(LOG_ERR, "reply_raw_noerror(): unknown address family in struct recurses");
1942 return;
1945 switch (sr->proto) {
1946 case IPPROTO_UDP:
1947 break;
1948 default:
1949 dolog(LOG_ERR, "reply_raw_noerror(): can't do any protocol other than udp right now");
1950 return;
1953 build_reply(&sreply, so, sr->query, sr->len, sr->question, NULL, 0, sd, NULL, 0xff, 0, 0, sr);
1955 reply_noerror(&sreply);
1957 return;
1960 void
1961 reply_raw_nxdomain(DB *db, struct recurses *sr, struct domain *sd, int *raw)
1963 int so;
1964 struct sreply sreply;
1966 dolog(LOG_DEBUG, "reply_raw_nxdomain called");
1968 switch (sr->af) {
1969 case AF_INET:
1970 so = raw[0];
1971 break;
1972 case AF_INET6:
1973 so = raw[1];
1974 break;
1975 default:
1976 dolog(LOG_ERR, "reply_raw_nxdomain(): unknown address family in struct recurses");
1977 return;
1980 switch (sr->proto) {
1981 case IPPROTO_UDP:
1982 break;
1983 default:
1984 dolog(LOG_ERR, "reply_raw_nxdomain(): can't do any protocol other than udp right now");
1985 return;
1988 build_reply(&sreply, so, sr->query, sr->len, sr->question, NULL, 0, sd, NULL, 0xff, 0, 0, sr);
1990 reply_nxdomain(&sreply);
1992 return;
1997 * LEVEL - traverse a domain name and count how many levels it has
1998 * first level is a TLD, then a 2nd level domain and so on.
2001 int
2002 level(u_char *p)
2004 int level = 0;
2006 while (*p) {
2007 level++;
2008 p += ((*p) + 1);
2011 return (level);
2015 * CONTAINS - check if domain name A is contained in domain name B
2019 int
2020 contains(u_char *a, u_char *b)
2022 u_char *p = a;
2023 u_char *q = b;
2024 u_int plen = 0, qlen = 0;
2026 while (*p) {
2027 plen += (*p) + 1;
2028 p += ((*p) + 1);
2031 while (*q) {
2032 qlen += ((*q) + 1);
2033 q += ((*q) + 1);
2036 p = a;
2037 q = b;
2039 while (*q) {
2040 if ((plen == qlen) && memcasecmp((u_char *)p, (u_char *)q, qlen) == 0)
2041 return (1);
2043 qlen -= ((*q) + 1);
2044 q += ((*q) + 1);
2047 return (0);
2051 * NETLOOKUP6 - do an ipv6 lookup of the requested internet record
2055 int
2056 netlookup6(DB *db, struct recurses *sr)
2058 struct sockaddr_in6 sin6;
2059 struct dns_header *dh;
2061 char buf[2048];
2062 int flag;
2064 if (sr->so != -1) {
2065 if (close(sr->so) < 0)
2066 dolog(LOG_ERR, "close: %m");
2069 sr->so = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
2070 if (sr->so < 0) {
2071 dolog(LOG_ERR, "socket6: %m");
2072 sr->so = -1;
2073 return (-1);
2076 sr->port = arc4random() & 0xffff;
2078 * we have to avoid picking servers already
2079 * running ..
2081 if (sr->port < 1024)
2082 sr->port += 1024;
2084 sr->id = arc4random() & 0xffff;
2086 memset(&sin6, 0, sizeof(sin6));
2087 sin6.sin6_family = AF_INET6;
2088 sin6.sin6_port = htons(sr->port);
2089 #ifndef __linux__
2090 sin6.sin6_len = sizeof(struct sockaddr_in6);
2091 #endif
2093 if (bind(sr->so, (struct sockaddr *)&sin6, sizeof(sin6)) < 0) {
2094 dolog(LOG_ERR, "bind: %m");
2095 if (close(sr->so) < 0) {
2096 dolog(LOG_ERR, "close: %m");
2098 sr->so = -1;
2099 return (-1);
2103 * make this socket nonblocking
2106 if ((flag = fcntl(sr->so, F_GETFL)) < 0) {
2107 dolog(LOG_INFO, "fcntl 3: %m");
2109 flag |= O_NONBLOCK;
2110 if (fcntl(sr->so, F_SETFL, flag) < 0) {
2111 dolog(LOG_INFO, "fcntl 4: %m");
2114 if (lookup_ns(db, sr) <= 0) {
2115 dolog(LOG_ERR, "can't establish any servers to reach for zone \"%s\"", sr->question->converted_name);
2116 if (close(sr->so) < 0) {
2117 dolog(LOG_ERR, "close: %m");
2119 sr->so = -1;
2120 return (-1);
2123 memset(&sin6, 0, sizeof(sin6));
2124 sin6.sin6_family = AF_INET6;
2125 sin6.sin6_port = htons(53);
2127 /* pjp */
2128 memcpy((char *)&sin6.sin6_addr, (char *)&sr->aaaa[0], sizeof(struct in6_addr));
2130 /* XXX we use buf here in order to preserve
2131 * the state of query...
2133 memcpy(buf, sr->query, sr->len);
2134 dh = (struct dns_header *)&buf[0];
2135 NTOHS(dh->query);
2136 UNSET_DNS_RECURSION(dh);
2137 HTONS(dh->query);
2138 dh->id = htons(sr->id);
2140 #if 1
2141 dolog(LOG_INFO, "sending request with id %u\n", sr->id);
2143 #endif
2145 if (sendto(sr->so, buf, sr->len, 0, (struct sockaddr *)&sin6, sizeof(sin6)) < 0) {
2146 dolog(LOG_ERR, "sendto6: %m");
2147 if (close(sr->so) < 0) {
2148 dolog(LOG_ERR, "close: %m");
2150 sr->so = -1;
2151 return (-1);
2154 sr->sent_last_query = time(NULL);
2155 sr->packetcount++;
2158 return (0);
2162 * LOOKUP_AAAA - given a path, lookup the AAAA record in that record
2166 int
2167 lookup_aaaa(DB *db, struct recurses *sr, struct ns *ns)
2169 int ret, plen;
2170 char *p;
2172 DBT key, data;
2174 struct domain *sd, sdomain;
2175 int found = 0;
2177 p = ns->nsserver;
2178 plen = ns->nslen;
2180 memset(&key, 0, sizeof(key));
2181 memset(&data, 0, sizeof(data));
2183 key.data = (char *)p;
2184 key.size = plen;
2186 data.data = NULL;
2187 data.size = 0;
2189 found = 0;
2191 ret = db->get(db, NULL, &key, &data, 0);
2192 if (ret == 0) {
2193 if (data.size != sizeof(struct domain)) {
2194 dolog(LOG_ERR, "btree db is damaged");
2195 return (-1);
2198 memcpy((char*)&sdomain, data.data, sizeof(struct domain));
2199 sd = &sdomain;
2201 if ((sd->flags & DOMAIN_HAVE_AAAA) == DOMAIN_HAVE_AAAA) {
2202 memcpy((char *)&sr->aaaa[sr->aaaa_count], (char *)&sd->aaaa[0], sizeof(struct in6_addr));
2203 sd->a_count++;
2204 found = 1;
2209 if (! found) {
2210 dolog(LOG_DEBUG, "calling fakerecurse");
2211 fakerecurse(db, sr, ns, DNS_TYPE_AAAA);
2212 return (-1);
2215 return (0);