Blob


1 /*
2 * Copyright (c) 2011-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"
33 void axfrloop(int *, int, char **, DB *);
34 void axfr_connection(int, char *, int, DB *);
35 int build_header(DB *, char *, char *, struct question *, int);
36 int build_soa(DB *, char *, int, struct domain *, struct question *);
37 int checklabel(DB *, struct domain *, struct domain *, struct question *);
38 void gather_notifydomains(DB *);
39 void init_axfr(void);
40 void init_notifyslave(void);
41 int insert_axfr(char *, char *);
42 int insert_notifyslave(char *, char *);
43 void notifypacket(int, void *, void *, int);
44 void notifyslaves(int *);
45 void reap(int);
47 extern in_addr_t getmask(int);
48 extern int getmask6(int, struct sockaddr_in6 *);
49 extern void reply_fmterror(struct sreply *);
50 extern void reply_nxdomain(struct sreply *);
51 extern int get_soa(DB *, struct question *, struct domain *, int);
52 extern int compress_label(u_char *, int, int);
53 extern u_int16_t create_anyreply(struct sreply *, char *, int, int, int);
54 extern struct question *build_fake_question(char *, int, u_int16_t);
55 extern struct question *build_question(char *, int, int);
56 extern int free_question(struct question *);
57 extern void dolog(int, char *, ...);
58 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 *);
61 int notify = 0; /* do not notify when set to 0 */
63 extern int debug, verbose;
64 extern time_t time_changed;
66 SLIST_HEAD(listhead, axfrentry) axfrhead;
68 static struct axfrentry {
69 char name[INET6_ADDRSTRLEN];
70 int family;
71 struct sockaddr_storage hostmask;
72 struct sockaddr_storage netmask;
73 u_int8_t prefixlen;
74 SLIST_ENTRY(axfrentry) axfr_entry;
75 } *an2, *anp;
77 SLIST_HEAD(notifyslavelisthead, notifyslaveentry) notifyslavehead;
79 static struct notifyslaveentry {
80 char name[INET6_ADDRSTRLEN];
81 int family;
82 struct sockaddr_storage hostmask;
83 struct sockaddr_storage netmask;
84 u_int8_t prefixlen;
85 SLIST_ENTRY(notifyslaveentry) notifyslave_entry;
86 } *nfslnp2, *nfslnp;
90 SLIST_HEAD(notifylisthead, notifyentry) notifyhead;
92 static struct notifyentry {
93 char domain[DNS_MAXNAME];
94 int domainlen;
95 u_int16_t *ids;
96 u_int16_t *attempts;
97 SLIST_ENTRY(notifyentry) notify_entry;
98 } *notn2, *notnp;
101 static const char rcsid[] = "$Id: axfr.c,v 1.10 2014/05/18 17:14:05 pjp Exp $";
103 /*
104 * INIT_AXFR - initialize the axfr singly linked list
105 */
107 void
108 init_axfr(void)
110 SLIST_INIT(&axfrhead);
111 return;
114 /*
115 * INSERT_AXFR - insert an address and prefixlen into the axfr slist
116 */
118 int
119 insert_axfr(char *address, char *prefixlen)
121 struct sockaddr_in *sin;
122 struct sockaddr_in6 *sin6;
123 int pnum;
124 int ret;
126 pnum = atoi(prefixlen);
127 an2 = malloc(sizeof(struct axfrentry)); /* Insert after. */
129 if (strchr(address, ':') != NULL) {
130 an2->family = AF_INET6;
131 sin6 = (struct sockaddr_in6 *)&an2->hostmask;
132 if ((ret = inet_pton(AF_INET6, address, &sin6->sin6_addr.s6_addr)) != 1)
133 return (-1);
134 sin6->sin6_family = AF_INET6;
135 sin6 = (struct sockaddr_in6 *)&an2->netmask;
136 sin6->sin6_family = AF_INET6;
137 if (getmask6(pnum, sin6) < 0)
138 return(-1);
139 an2->prefixlen = pnum;
140 } else {
142 an2->family = AF_INET;
143 sin = (struct sockaddr_in *)&an2->hostmask;
144 sin->sin_family = AF_INET;
145 sin->sin_addr.s_addr = inet_addr(address);
146 sin = (struct sockaddr_in *)&an2->netmask;
147 sin->sin_family = AF_INET;
148 sin->sin_addr.s_addr = getmask(pnum);
149 an2->prefixlen = pnum;
153 SLIST_INSERT_HEAD(&axfrhead, an2, axfr_entry);
155 return (0);
158 /*
159 * FIND_AXFR - walk the axfr list and find the correponding network
160 * if a network matches return 1, if no match is found return
161 * 0.
162 */
164 int
165 find_axfr(struct sockaddr_storage *sst, int family)
167 struct sockaddr_in *sin, *sin0;
168 struct sockaddr_in6 *sin6, *sin60, *sin61;
169 u_int32_t hostmask, netmask;
170 u_int32_t a;
171 #ifdef __amd64
172 u_int64_t *hm[2], *nm[2], *a6[2];
173 #else
174 u_int32_t *hm[4], *nm[4], *a6[4];
175 #endif
177 SLIST_FOREACH(anp, &axfrhead, axfr_entry) {
178 if (anp->family == AF_INET) {
179 if (family != AF_INET)
180 continue;
181 sin = (struct sockaddr_in *)sst;
182 a = sin->sin_addr.s_addr;
183 sin = (struct sockaddr_in *)&anp->hostmask;
184 sin0 = (struct sockaddr_in *)&anp->netmask;
185 hostmask = sin->sin_addr.s_addr;
186 netmask = sin0->sin_addr.s_addr;
187 if ((hostmask & netmask) == (a & netmask)) {
188 return (1);
189 } /* if hostmask */
190 } else if (anp->family == AF_INET6) {
191 if (family != AF_INET6)
192 continue;
193 sin6 = (struct sockaddr_in6 *)sst;
194 sin60 = (struct sockaddr_in6 *)&anp->hostmask;
195 sin61 = (struct sockaddr_in6 *)&anp->netmask;
196 #ifdef __amd64
197 /*
198 * If this is on a 64 bit machine, we'll benefit
199 * by using 64 bit registers, this should make it
200 * a tad faster...
201 */
202 hm[0] = (u_int64_t *)&sin60->sin6_addr.s6_addr;
203 hm[1] = (hm[0] + 1);
204 nm[0] = (u_int64_t *)&sin61->sin6_addr.s6_addr;
205 nm[1] = (nm[0] + 1);
206 a6[0] = (u_int64_t *)&sin6->sin6_addr.s6_addr;
207 a6[1] = (a6[0] + 1);
208 if ( ((*hm[0] & *nm[0]) == (*a6[0] & *nm[0]))&&
209 ((*hm[1] & *nm[1]) == (*a6[1] & *nm[1]))) {
210 #else
211 hm[0] = (u_int32_t *)&sin60->sin6_addr.s6_addr;
212 hm[1] = (hm[0] + 1); hm[2] = (hm[1] + 1);
213 hm[3] = (hm[2] + 1);
214 nm[0] = (u_int32_t *)&sin61->sin6_addr.s6_addr;
215 nm[1] = (nm[0] + 1); nm[2] = (nm[1] + 1);
216 nm[3] = (nm[2] + 1);
217 a6[0] = (u_int32_t *)&sin6->sin6_addr.s6_addr;
218 a6[1] = (a6[0] + 1); a6[2] = (a6[1] + 1);
219 a6[3] = (a6[2] + 1);
221 if ( ((*hm[0] & *nm[0]) == (*a6[0] & *nm[0]))&&
222 ((*hm[1] & *nm[1]) == (*a6[1] & *nm[1]))&&
223 ((*hm[2] & *nm[2]) == (*a6[2] & *nm[2]))&&
224 ((*hm[3] & *nm[3]) == (*a6[3] & *nm[3]))) {
225 #endif
227 return (1);
228 } /* if ip6 address */
230 } /* if AF_INET6 */
231 } /* SLIST */
233 return (0);
236 /*
237 * INIT_NOTIFYSLAVE - initialize the axfr singly linked list
238 */
240 void
241 init_notifyslave(void)
243 SLIST_INIT(&notifyslavehead);
244 return;
247 /*
248 * INSERT_NOTIFYSLAVE - insert an address and prefixlen into the notifyslave slist
249 */
251 int
252 insert_notifyslave(char *address, char *prefixlen)
254 struct sockaddr_in *sin;
255 struct sockaddr_in6 *sin6;
256 int pnum;
257 int ret;
259 pnum = atoi(prefixlen);
260 nfslnp2 = calloc(1, sizeof(struct notifyslaveentry)); /* Insert after. */
261 if (nfslnp2 == NULL)
262 return (-1);
265 if (strchr(address, ':') != NULL) {
266 nfslnp2->family = AF_INET6;
267 sin6 = (struct sockaddr_in6 *)&nfslnp2->hostmask;
268 if ((ret = inet_pton(AF_INET6, address, &sin6->sin6_addr.s6_addr)) != 1)
269 return (-1);
270 sin6->sin6_family = AF_INET6;
271 sin6->sin6_port = htons(53);
272 sin6 = (struct sockaddr_in6 *)&nfslnp2->netmask;
273 sin6->sin6_family = AF_INET6;
274 if (getmask6(pnum, sin6) < 0)
275 return(-1);
276 nfslnp2->prefixlen = pnum;
277 } else {
279 nfslnp2->family = AF_INET;
280 sin = (struct sockaddr_in *)&nfslnp2->hostmask;
281 sin->sin_family = AF_INET;
282 sin->sin_addr.s_addr = inet_addr(address);
283 sin->sin_port = htons(53);
284 sin = (struct sockaddr_in *)&nfslnp2->netmask;
285 sin->sin_family = AF_INET;
286 sin->sin_addr.s_addr = getmask(pnum);
287 nfslnp2->prefixlen = pnum;
291 SLIST_INSERT_HEAD(&notifyslavehead, nfslnp2, notifyslave_entry);
293 return (0);
296 void
297 axfrloop(int *afd, int sockcount, char **ident, DB *db)
299 fd_set rset;
301 struct timeval tv;
302 struct sockaddr_storage from;
303 struct sockaddr_in6 *sin6, *sin62;
304 struct sockaddr_in *sin, *sin2;
305 struct dns_header *dh;
306 struct question *question;
308 int i, so, len;
309 int sel, maxso = 0;
310 int is_ipv6, axfr_acl;
311 int notifyfd[2];
313 socklen_t fromlen;
314 char buf[512];
316 time_t now;
317 pid_t pid;
319 char address[INET6_ADDRSTRLEN];
321 signal(SIGCHLD, reap);
323 for (i = 0; i < sockcount; i++) {
324 listen(afd[i], 5);
327 if (notify) {
328 /*
329 * If a zonefile has changed in the last half hour then
330 * gather all notifydomains and start the notify process
331 */
333 notifyfd[0] = -1;
334 notifyfd[1] = -1;
336 now = time(NULL);
337 if (difftime(now, time_changed) <= 1800) {
338 gather_notifydomains(db);
339 notifyfd[0] = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
340 notifyfd[1] = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
342 memset((char *)&from, 0, sizeof(from));
343 sin = (struct sockaddr_in *)&from;
344 sin->sin_family = AF_INET;
345 sin->sin_port = htons(0);
347 if (bind(notifyfd[0], (struct sockaddr *)sin, sizeof(*sin)) < 0) {
348 dolog(LOG_INFO, "bind notify: %s\n", strerror(errno));
351 memset((char *)&from, 0, sizeof(from));
352 sin6 = (struct sockaddr_in6 *)&from;
353 sin->sin_family = AF_INET6;
354 sin->sin_port = htons(0);
356 if (bind(notifyfd[1], (struct sockaddr *)sin, sizeof(*sin6)) < 0) {
357 dolog(LOG_INFO, "bind notify6: %s\n", strerror(errno));
360 memset((char *)&from, 0, sizeof(from));
362 notifyslaves((int *)&notifyfd);
366 for (;;) {
368 FD_ZERO(&rset);
369 maxso = 0;
371 for (i = 0; i < sockcount; i++) {
372 FD_SET(afd[i], &rset);
373 if (maxso < afd[i])
374 maxso = afd[i];
377 if (notify) {
378 if (notifyfd[0] > -1) {
379 FD_SET(notifyfd[0], &rset);
380 if (maxso < notifyfd[0])
381 maxso = notifyfd[0];
384 if (notifyfd[1] > -1) {
385 FD_SET(notifyfd[1], &rset);
386 if (maxso < notifyfd[1])
387 maxso = notifyfd[1];
391 tv.tv_sec = 10;
392 tv.tv_usec = 0;
394 sel = select(maxso + 1, &rset, NULL, NULL, &tv);
396 if (sel == 0) {
397 if (notify) {
398 if (notifyfd[0] > -1 || notifyfd[1] > -1)
399 notifyslaves((int *)&notifyfd);
402 continue;
404 if (sel < 0) {
405 dolog(LOG_INFO, "select: %s\n", strerror(errno));
406 continue;
409 for (i = 0; i < sockcount; i++) {
410 if (FD_ISSET(afd[i], &rset)) {
411 fromlen = sizeof(struct sockaddr_storage);
413 so = accept(afd[i], (struct sockaddr*)&from, &fromlen);
414 if (so < 0) {
415 dolog(LOG_INFO, "afd accept: %s\n", strerror(errno));
416 continue;
419 if (from.ss_family == AF_INET6) {
420 is_ipv6 = 1;
422 fromlen = sizeof(struct sockaddr_in6);
423 sin6 = (struct sockaddr_in6 *)&from;
424 inet_ntop(AF_INET6, (void*)&sin6->sin6_addr, (char*)&address, sizeof(address));
425 axfr_acl = find_axfr((struct sockaddr_storage *)sin6, AF_INET6);
427 } else if (from.ss_family == AF_INET) {
428 is_ipv6 = 0;
430 fromlen = sizeof(struct sockaddr_in);
431 sin = (struct sockaddr_in *)&from;
432 inet_ntop(AF_INET, (void*)&sin->sin_addr, (char*)&address, sizeof(address));
434 axfr_acl = find_axfr((struct sockaddr_storage *)sin, AF_INET);
436 } else {
437 dolog(LOG_INFO, "afd accept unknown family %d, close\n", from.ss_family);
438 close(so);
439 continue;
442 if (! axfr_acl) {
443 dolog(LOG_INFO, "connection from %s was not in our axfr acl, drop\n", address);
444 close(so);
445 continue;
448 dolog(LOG_INFO, "AXFR connection from %s on interface \"%s\"\n", address, ident[i]);
450 switch (pid = fork()) {
451 case 0:
452 axfr_connection(so, address, is_ipv6, db);
453 exit(0);
454 /*NOTREACHED*/
455 default:
456 close(so);
457 break;
460 } /* if(FD_ISSET..) */
462 } /* for (i.. */
464 if (notify) {
465 if (notifyfd[0] > -1 && FD_ISSET(notifyfd[0], &rset)) {
466 fromlen = sizeof(struct sockaddr_storage);
467 len = recvfrom(notifyfd[0], buf, sizeof(buf), 0, (struct sockaddr *)&from, &fromlen);
468 if (len < 0) {
469 dolog(LOG_INFO, "recvfrom: %s\n", strerror(errno));
472 if (len < sizeof(struct dns_header)) {
473 dolog(LOG_INFO, "received bogus reply on notify port, drop\n");
474 continue;
477 dh = (struct dns_header *)&buf[0];
478 if (ntohs(dh->question) != 1) {
479 dolog(LOG_INFO, "question header on notify reply not 1, drop\n");
480 continue;
483 if (! (ntohs(dh->query) & DNS_REPLY)) {
484 dolog(LOG_INFO, "question header is not a reply, drop\n");
485 continue;
488 question = build_question(buf, len, ntohs(dh->additional));
489 if (question == NULL) {
490 dolog(LOG_INFO, "build_question failed on notify reply, drop\n");
491 continue;
494 sin = (struct sockaddr_in *)&from;
495 inet_ntop(AF_INET, (void*)&sin->sin_addr, (char*)&address, sizeof(address));
497 #ifdef __linux__
498 SLIST_FOREACH(notnp, &notifyhead, notify_entry) {
499 #else
500 SLIST_FOREACH_SAFE(notnp, &notifyhead, notify_entry, notn2) {
501 #endif
503 for (i = 0; i < notify; i++) {
504 if (ntohs(dh->id) == notnp->ids[i] &&
505 (ntohs(dh->query) & DNS_NOTIFY) &&
506 (ntohs(dh->query) & DNS_AUTH) &&
507 ntohs(question->hdr->qtype) == DNS_TYPE_SOA &&
508 ntohs(question->hdr->qclass) == DNS_CLASS_IN &&
509 question->hdr->namelen == notnp->domainlen &&
510 memcmp(question->hdr->name, notnp->domain, notnp->domainlen) == 0) {
511 #ifdef __linux__
512 SLIST_FOREACH(nfslnp, &notifyslavehead, notifyslave_entry) {
513 #else
514 SLIST_FOREACH_SAFE(nfslnp, &notifyslavehead, notifyslave_entry, nfslnp2) {
515 #endif
516 if (nfslnp->family != AF_INET)
517 continue;
519 sin2 = (struct sockaddr_in *)&nfslnp->hostmask;
520 if (sin->sin_addr.s_addr == sin2->sin_addr.s_addr) {
521 dolog(LOG_INFO, "notify success! removing address \"%s\" from notify contact list\n", address);
522 SLIST_REMOVE(&notifyslavehead, nfslnp, notifyslaveentry, notifyslave_entry);
525 } else {
526 dolog(LOG_INFO, "got a reply from a notify host (%s) DNS->ID %u that says: %04x\n", address, ntohs(dh->id), ntohs(dh->query));
531 free_question(question);
533 if (SLIST_EMPTY(&notifyslavehead)) {
534 dolog(LOG_INFO, "notifys have been completed, closing notify descriptors!\n");
535 if (notifyfd[0] > -1)
536 close(notifyfd[0]);
538 if (notifyfd[1] > -1)
539 close(notifyfd[1]);
541 notifyfd[0] = -1;
542 notifyfd[1] = -1;
546 if (notifyfd[1] > -1 && FD_ISSET(notifyfd[1], &rset)) {
547 fromlen = sizeof(struct sockaddr_storage);
548 len = recvfrom(notifyfd[1], buf, sizeof(buf), 0, (struct sockaddr *)&from, &fromlen);
549 if (len < 0) {
550 dolog(LOG_INFO, "recvfrom: %s\n", strerror(errno));
553 if (len < sizeof(struct dns_header)) {
554 dolog(LOG_INFO, "received bogus reply on notify port, drop\n");
555 continue;
558 dh = (struct dns_header *)&buf[0];
559 if (ntohs(dh->question) != 1) {
560 dolog(LOG_INFO, "question header on notify reply not 1, drop\n");
561 continue;
564 if (! (ntohs(dh->query) & DNS_REPLY)) {
565 dolog(LOG_INFO, "question header is not a reply, drop\n");
566 continue;
569 question = build_question(buf, len, ntohs(dh->additional));
570 if (question == NULL) {
571 dolog(LOG_INFO, "build_question failed on notify reply, drop\n");
572 continue;
575 sin6 = (struct sockaddr_in6 *)&from;
576 inet_ntop(AF_INET6, (void*)&sin6->sin6_addr, (char*)&address, sizeof(address));
578 #ifdef __linux
579 SLIST_FOREACH(notnp, &notifyhead, notify_entry) {
580 #else
581 SLIST_FOREACH_SAFE(notnp, &notifyhead, notify_entry, notn2) {
582 #endif
583 for (i = 0; i < notify; i++) {
584 if (ntohs(dh->id) == notnp->ids[i] &&
585 (ntohs(dh->query) & DNS_NOTIFY) &&
586 (ntohs(dh->query) & DNS_AUTH) &&
587 ntohs(question->hdr->qtype) == DNS_TYPE_SOA &&
588 ntohs(question->hdr->qclass) == DNS_CLASS_IN &&
589 question->hdr->namelen == notnp->domainlen &&
590 memcmp(question->hdr->name, notnp->domain, notnp->domainlen) == 0) {
591 #ifdef __linux__
592 SLIST_FOREACH(nfslnp, &notifyslavehead, notifyslave_entry) {
593 #else
594 SLIST_FOREACH_SAFE(nfslnp, &notifyslavehead, notifyslave_entry, nfslnp2) {
595 #endif
596 if (nfslnp->family != AF_INET6)
597 continue;
599 sin62 = (struct sockaddr_in6 *)&nfslnp->hostmask;
600 if (memcmp(&sin6->sin6_addr, &sin62->sin6_addr, 16) == 0) {
601 dolog(LOG_INFO, "notify success! removing address \"%s\" from notify contact list\n", address);
602 SLIST_REMOVE(&notifyslavehead, nfslnp, notifyslaveentry, notifyslave_entry);
605 } else {
606 dolog(LOG_INFO, "got a reply from a notify host (%s) DNS->ID %u that says: %04x\n", address, ntohs(dh->id), ntohs(dh->query));
611 free_question(question);
613 if (SLIST_EMPTY(&notifyslavehead)) {
614 dolog(LOG_INFO, "notifys have been completed, closing notify descriptors!\n");
615 if (notifyfd[0] > -1)
616 close(notifyfd[0]);
618 if (notifyfd[1] > -1)
619 close(notifyfd[1]);
621 notifyfd[0] = -1;
622 notifyfd[1] = -1;
628 } /* for (;;) */
632 /*
633 * AXFR_CONNECTION - this is the main core of AXFR engine, forked
635 */
637 void
638 axfr_connection(int so, char *address, int is_ipv6, DB *db)
641 char buf[4000];
642 char *p = &buf[0];
643 char *q;
644 char *reply;
646 int len, dnslen;
647 int offset = 0;
648 int ret;
649 int qlen;
650 int outlen, i;
651 int rrcount;
653 u_int16_t *tmp;
655 struct dns_header *dh, *odh;
656 struct sreply sreply;
657 struct question *question, *fq;
658 struct domain soa, sdomain, nsdomain, savesd;
660 DBT key, data;
661 DBC *cursor;
663 for (;;) {
664 len = recv(so, p + offset, sizeof(buf) - offset, 0);
665 if (len <= 0) {
666 close(so);
667 exit(1);
670 /*
671 * do a little dance here because we don't know if the
672 * input is fragmented or not...
673 */
674 if (offset + len >= 2) {
675 tmp = (u_int16_t *)p;
676 dnslen = ntohs(*tmp);
677 } else {
678 offset += len;
679 continue;
681 if (dnslen + 2 != offset + len) {
682 offset += len;
683 continue;
687 /* by now the packet should be normalized */
689 dh = (struct dns_header *)(p + 2);
691 if ((ntohs(dh->query) & DNS_REPLY)) {
692 dolog(LOG_INFO, "AXFR dns packet is not a question, drop\n");
693 goto drop;
696 if (ntohs(dh->question) != 1) {
697 dolog(LOG_INFO, "AXFR dns packet does not have a question count of 1 (RFC 5936, page 9), reply fmterror\n");
699 build_reply(&sreply, so, (p + 2), dnslen, NULL, NULL, 0, NULL, NULL, 0xff, 1, 0, NULL);
701 reply_fmterror(&sreply);
702 goto drop;
705 if ((question = build_question((p + 2), dnslen, 0)) == NULL) {
706 dolog(LOG_INFO, "AXFR malformed question, drop\n");
707 goto drop;
710 if (ntohs(question->hdr->qclass) != DNS_CLASS_IN) {
711 dolog(LOG_INFO, "AXFR question wasn't for class DNS_CLASS_IN, drop\n");
712 goto drop;
715 switch (ntohs(question->hdr->qtype)) {
716 case DNS_TYPE_AXFR:
717 case DNS_TYPE_IXFR:
718 case DNS_TYPE_SOA:
719 break;
720 default:
721 dolog(LOG_INFO, "AXFR question wasn't for valid types (ixfr, axfr, soa) with requested type %d, drop\n", ntohs(question->hdr->qtype));
722 goto drop;
726 /* now we can be reasonably sure that it's an AXFR for us */
728 reply = calloc(1, 65538);
729 if (reply == NULL) {
730 dolog(LOG_INFO, "internal error: %s\n", strerror(errno));
731 goto drop;
734 odh = (struct dns_header *)(reply + 2);
736 q = question->hdr->name;
737 qlen = question->hdr->namelen;
739 memset(&key, 0, sizeof(key));
740 memset(&data, 0, sizeof(data));
742 key.data = (char *)q;
743 key.size = qlen;
745 data.data = NULL;
746 data.size = 0;
748 ret = db->get(db, NULL, &key, &data, 0);
750 if (ret != 0) {
751 memset(&sdomain, 0, sizeof(sdomain));
752 (void)get_soa(db, question, &sdomain, 0);
753 build_reply(&sreply, so, (p + 2), dnslen, question, NULL, 0, &sdomain, NULL, 0xff, 1, 0, NULL);
754 reply_nxdomain(&sreply);
755 dolog(LOG_INFO, "AXFR request for zone %s, no db entry, nxdomain -> drop\n", question->converted_name);
756 goto drop;
759 if (data.size != sizeof(struct domain)) {
760 dolog(LOG_INFO, "AXFR btree db is damaged, drop\n");
761 goto drop;
764 memcpy((char *)&soa, (char *)data.data, data.size);
766 /*
767 * check if we have an SOA record
768 */
770 if (! (soa.flags & DOMAIN_HAVE_SOA)) {
771 memset(&sdomain, 0, sizeof(sdomain));
772 (void)get_soa(db, question, &sdomain, 0);
773 build_reply(&sreply, so, (p + 2), dnslen, question, NULL, 0, &sdomain, NULL, 0xff, 1, 0, NULL);
774 reply_nxdomain(&sreply);
776 dolog(LOG_INFO, "AXFR request for zone %s, which has no SOA for the zone, nxdomain -> drop\n", question->converted_name);
777 goto drop;
780 if (ntohs(question->hdr->qtype) == DNS_TYPE_SOA) {
781 dolog(LOG_INFO, "TCP SOA request for zone \"%s\", replying...\n", question->converted_name);
782 outlen = 0;
783 outlen = build_header(db, (reply + 2), (p + 2), question, 1);
784 outlen = build_soa(db, (reply + 2), outlen, &soa, question);
786 tmp = (u_int16_t *)reply;
787 *tmp = htons(outlen);
789 len = send(so, reply, outlen + 2, 0);
790 if (len <= 0) {
791 goto drop;
794 outlen = 0;
795 offset = 0;
796 p = &buf[0];
798 free (reply);
800 continue;
803 dolog(LOG_INFO, "%s request for zone \"%s\", replying...\n",
804 (ntohs(question->hdr->qtype) == DNS_TYPE_AXFR ? "AXFR"
805 : "IXFR"), question->converted_name);
807 outlen = build_header(db, (reply + 2), (p + 2), question, 0);
808 outlen = build_soa(db, (reply + 2), outlen, &soa, question);
809 rrcount = 1;
811 if (db->cursor(db, NULL, &cursor, 0) != 0) {
812 dolog(LOG_INFO, "db->cursor: %s\n", strerror(errno));
813 goto drop;
816 memset(&key, 0, sizeof(key));
817 memset(&data, 0, sizeof(data));
820 if (cursor->c_get(cursor, &key, &data, DB_FIRST) != 0) {
821 dolog(LOG_INFO, "cursor->c_get: %s\n", strerror(errno));
822 goto drop;
825 do {
826 if (data.size != sizeof(struct domain)) {
827 dolog(LOG_INFO, "AXFR btree db is damaged (%d), drop\n", __LINE__);
828 goto drop;
831 memcpy((char *)&sdomain, (char *)data.data, data.size);
832 memcpy((char *)&savesd, (char *)data.data, data.size);
834 if (checklabel(db, &sdomain, &soa, question)) {
835 fq = build_fake_question(sdomain.zone, sdomain.zonelen, 0);
836 build_reply(&sreply, so, (p + 2), dnslen, fq, NULL, 0, &sdomain, NULL, 0xff, 1, 0, NULL);
837 outlen = create_anyreply(&sreply, (reply + 2), 65535, outlen, 0);
838 free_question(fq);
840 if ((savesd.flags & DOMAIN_HAVE_NS) &&
841 (savesd.ns_type & NS_TYPE_DELEGATE)) {
842 for (i = 0; i < savesd.ns_count; i++) {
843 fq = build_fake_question(savesd.ns[i].nsserver,
844 savesd.ns[i].nslen, 0);
845 memset(&key, 0, sizeof(key));
846 memset(&data, 0, sizeof(data));
848 key.data = fq->hdr->name;
849 key.size = fq->hdr->namelen;
851 data.data = NULL;
852 data.size = 0;
854 ret = db->get(db, NULL, &key, &data, 0);
855 if (ret != 0) {
856 free_question(fq);
857 continue;
860 if (data.size != sizeof(struct domain)) {
861 dolog(LOG_INFO, "AXFR btree db is damaged (%d), drop\n", __LINE__);
862 goto drop;
865 memcpy((char *)&nsdomain, (char*)data.data, data.size);
867 build_reply(&sreply, so, (p + 2), dnslen, fq, NULL, 0, &nsdomain, NULL, 0xff, 1, 0, NULL);
868 outlen = create_anyreply(&sreply, (reply + 2), 65535, outlen, 0);
869 free_question(fq);
871 } /* for (i.. */
872 } /* if (sdomain.flags */
874 } /* if (checklabel */
876 /*
877 * if we accumulate 60000 bytes out of the maximum
878 * 65535 bytes then we fragment.
879 */
880 /* XXX */
881 if (outlen > 60000) {
882 tmp = (u_int16_t *)reply;
883 *tmp = htons(outlen);
885 /* set the rrcount in there */
887 NTOHS(odh->answer);
888 odh->answer += rrcount;
889 HTONS(odh->answer);
891 len = send(so, reply, outlen + 2, 0);
892 if (len <= 0) {
893 goto drop;
896 rrcount = 0;
897 outlen = build_header(db, (reply + 2), (p + 2), question, 0);
900 memset(&key, 0, sizeof(key));
901 memset(&data, 0, sizeof(data));
902 } while (cursor->c_get(cursor, &key, &data, DB_NEXT) == 0);
904 cursor->c_close(cursor);
906 outlen = build_soa(db, (reply + 2), outlen, &soa, question);
907 rrcount++;
909 tmp = (u_int16_t *)reply;
910 *tmp = htons(outlen);
912 /* set the rrcount in there */
914 NTOHS(odh->answer);
915 odh->answer += rrcount;
916 HTONS(odh->answer);
918 len = send(so, reply, outlen + 2, 0);
919 if (len <= 0)
920 goto drop;
922 goto drop;
924 } /* for(;;) */
928 drop:
929 close(so);
930 exit(0);
933 /*
934 * REAP - reap the child that is zombied by now, this is a sighandler for
935 * SIGCHLD
936 */
938 void
939 reap(int sig)
941 int status;
942 pid_t pid;
944 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
949 /*
950 * build_header - build a header reply
952 */
954 int
955 build_header(DB *db, char *reply, char *buf, struct question *q, int answercount)
957 struct dns_header *odh;
958 u_int16_t outlen;
960 odh = (struct dns_header *)reply;
961 outlen = sizeof(struct dns_header);
963 /* copy question to reply */
964 memcpy(reply, buf, sizeof(struct dns_header) + q->hdr->namelen + 4);
965 /* blank query */
966 memset((char *)&odh->query, 0, sizeof(u_int16_t));
968 outlen += (q->hdr->namelen + 4);
970 SET_DNS_REPLY(odh);
971 SET_DNS_AUTHORITATIVE(odh);
973 NTOHS(odh->query);
975 odh->question = htons(1);
976 odh->answer = htons(answercount);
977 odh->nsrr = 0;
978 odh->additional = 0;
980 return (outlen);
985 /*
986 * BUILD_SOA - build an SOA answer
987 */
989 int
990 build_soa(DB *db, char *reply, int offset, struct domain *sd, struct question *q)
992 char *p;
993 char *label;
994 char *plabel;
996 int labellen;
997 int tmplen;
998 u_int32_t *soa_val;
1000 struct answer {
1001 char name[2];
1002 u_int16_t type;
1003 u_int16_t class;
1004 u_int32_t ttl;
1005 u_int16_t rdlength; /* 12 */
1006 char rdata;
1007 } __attribute__((packed));
1009 struct answer *answer;
1010 answer = (struct answer *)(&reply[offset]);
1012 answer->name[0] = 0xc0;
1013 answer->name[1] = 0x0c;
1014 answer->type = htons(DNS_TYPE_SOA);
1015 answer->class = htons(DNS_CLASS_IN);
1016 answer->ttl = htonl(sd->ttl);
1018 offset += 12; /* up to rdata length */
1020 p = (char *)&answer->rdata;
1023 label = sd->soa.nsserver;
1024 labellen = sd->soa.nsserver_len;
1026 plabel = label;
1028 if (offset + labellen <= 65535)
1029 memcpy(&reply[offset], (char *)plabel, labellen);
1030 else
1031 return (offset); /* XXX */
1033 offset += labellen;
1035 /* compress the label if possible */
1036 if ((tmplen = compress_label((u_char*)reply, offset, labellen)) > 0) {
1037 offset = tmplen;
1040 label = sd->soa.responsible_person;
1041 labellen = sd->soa.rp_len;
1042 plabel = label;
1044 if (offset + labellen <= 65535)
1045 memcpy(&reply[offset], (char *)plabel, labellen);
1046 else
1047 return (offset); /* XXX */
1049 offset += labellen;
1051 /* 2 compress the label if possible */
1053 if ((tmplen = compress_label((u_char*)reply, offset, labellen)) > 0) {
1054 offset = tmplen;
1058 /* XXX */
1059 if ((offset + sizeof(sd->soa.serial)) >= 65535 ) {
1060 /* XXX server error reply? */
1061 return (offset);
1063 soa_val = (u_int32_t *)&reply[offset];
1064 *soa_val = htonl(sd->soa.serial);
1065 offset += sizeof(sd->soa.serial); /* XXX */
1067 /* XXX */
1068 if ((offset + sizeof(sd->soa.refresh)) >= 65535 ) {
1069 return (offset);
1071 soa_val = (u_int32_t *)&reply[offset];
1072 *soa_val = htonl(sd->soa.refresh);
1073 offset += sizeof(sd->soa.refresh); /* XXX */
1075 if ((offset + sizeof(sd->soa.retry)) >= 65535 ) {
1076 return (offset);
1078 soa_val = (u_int32_t *)&reply[offset];
1079 *soa_val = htonl(sd->soa.retry);
1080 offset += sizeof(sd->soa.retry); /* XXX */
1082 if ((offset + sizeof(sd->soa.expire)) >= 65535 ) {
1083 return (offset);
1085 soa_val = (u_int32_t *)&reply[offset];
1086 *soa_val = htonl(sd->soa.expire);
1087 offset += sizeof(sd->soa.expire);
1089 if ((offset + sizeof(sd->soa.minttl)) > 65535 ) {
1090 return (offset);
1092 soa_val = (u_int32_t *)&reply[offset];
1093 *soa_val = htonl(sd->soa.minttl);
1094 offset += sizeof(sd->soa.minttl);
1096 answer->rdlength = htons(&reply[offset] - &answer->rdata);
1098 return (offset);
1101 int
1102 checklabel(DB *db, struct domain *sd, struct domain *soa, struct question *q)
1104 struct domain tmpsd;
1105 char *p;
1106 int plen, ret;
1108 DBT key, data;
1110 if (memcmp(sd, soa, sizeof(struct domain)) == 0)
1111 return 1;
1113 p = sd->zone;
1114 plen = sd->zonelen;
1116 do {
1117 if (*p == '\0')
1118 return (0);
1120 memset(&key, 0, sizeof(key));
1121 memset(&data, 0, sizeof(data));
1123 key.data = (char *)p;
1124 key.size = plen;
1126 data.data = NULL;
1127 data.size = 0;
1129 ret = db->get(db, NULL, &key, &data, 0);
1130 if (ret == DB_NOTFOUND) {
1131 plen -= (*p + 1);
1132 p = (p + (*p + 1));
1134 continue;
1137 if (data.size != sizeof(struct domain)) {
1138 dolog(LOG_INFO, "AXFR btree db is damaged (%d), drop\n", __LINE__);
1139 return (0);
1142 memcpy(&tmpsd, data.data, sizeof(tmpsd));
1145 * the encountered label has an SOA before we got to the
1146 * root, so we skip this record entirely...
1149 if (tmpsd.flags & DOMAIN_HAVE_SOA)
1150 return (0);
1154 * and check the next label...
1157 plen -= (*p + 1);
1158 p = (p + (*p + 1));
1161 } while (memcmp(p, q->hdr->name, q->hdr->namelen) != 0);
1164 return (1);
1167 void
1168 gather_notifydomains(DB *db)
1170 DBT key, data;
1171 DBC *cursor;
1173 time_t now, soatime;
1174 struct tm *tm;
1176 char timestring[128];
1177 char buf[128];
1179 struct domain *sd;
1182 SLIST_INIT(&notifyhead);
1184 now = time(NULL);
1185 tm = localtime(&now);
1186 if (tm != NULL)
1187 strftime(timestring, sizeof(timestring), "%Y%m%d", tm);
1188 else
1189 snprintf(timestring, sizeof(timestring), "19700101");
1191 now = time(NULL);
1193 if (db->cursor(db, NULL, &cursor, 0) != 0) {
1194 dolog(LOG_INFO, "db->cursor: %s\n", strerror(errno));
1195 return;
1198 memset(&key, 0, sizeof(key));
1199 memset(&data, 0, sizeof(data));
1202 if (cursor->c_get(cursor, &key, &data, DB_FIRST) != 0) {
1203 dolog(LOG_INFO, "cursor->c_get: %s\n", strerror(errno));
1204 cursor->c_close(cursor);
1205 return;
1208 do {
1209 if (data.size != sizeof(struct domain)) {
1210 dolog(LOG_INFO, "btree db is damaged\n");
1211 cursor->c_close(cursor);
1212 return;
1215 sd = (struct domain *)data.data;
1217 if ((sd->flags & DOMAIN_HAVE_SOA) == DOMAIN_HAVE_SOA) {
1218 notn2 = malloc(sizeof(struct notifyentry));
1219 if (notn2 == NULL) {
1220 continue;
1223 notn2->ids = calloc(notify, sizeof(u_int16_t));
1224 if (notn2->ids == NULL) {
1225 free(notn2);
1226 continue;
1229 notn2->attempts = calloc(notify, sizeof(u_int16_t));
1230 if (notn2->attempts == NULL) {
1231 free(notn2);
1232 continue;
1235 memcpy(notn2->domain, sd->zone, sd->zonelen);
1236 notn2->domainlen = sd->zonelen;
1238 soatime = (time_t)sd->soa.serial;
1239 snprintf(buf, sizeof(buf), "%u", sd->soa.serial);
1241 if (strncmp(buf, timestring, strlen(timestring)) == 0) {
1242 dolog(LOG_INFO, "inserting zone \"%s\" for notification...\n", sd->zonename);
1243 SLIST_INSERT_HEAD(&notifyhead, notn2, notify_entry);
1244 } else if (difftime(now, soatime) < 1800 && difftime(now, soatime) > 0) {
1245 dolog(LOG_INFO, "2 inserting zone \"%s\" for notification...\n", sd->zonename);
1246 SLIST_INSERT_HEAD(&notifyhead, notn2, notify_entry);
1247 } else {
1248 #if 0
1249 dolog(LOG_INFO, "SOA serial for zone \"%s\" did not make sense (%s), not notifying\n", sd->zonename, buf);
1250 #endif
1251 free(notn2);
1255 memset(&key, 0, sizeof(key));
1256 memset(&data, 0, sizeof(data));
1257 } while (cursor->c_get(cursor, &key, &data, DB_NEXT) == 0);
1259 cursor->c_close(cursor);
1261 return;
1264 void
1265 notifyslaves(int *notifyfd)
1267 int so;
1268 int i;
1270 i = 0;
1271 SLIST_FOREACH(nfslnp, &notifyslavehead, notifyslave_entry) {
1272 if (nfslnp->family == AF_INET6) {
1273 so = notifyfd[1];
1274 } else {
1275 so = notifyfd[0];
1277 #if 0
1278 dolog(LOG_INFO, "notifying %s...\n", nfslnp->name);
1279 #endif
1281 #ifdef __linux__
1282 SLIST_FOREACH(notnp, &notifyhead, notify_entry) {
1283 #else
1284 SLIST_FOREACH_SAFE(notnp, &notifyhead, notify_entry, notn2) {
1285 #endif
1286 notnp->ids[i] = arc4random() & 0xffff;
1287 notnp->attempts[i]++;
1288 if (notnp->attempts[i] > 10) {
1289 dolog(LOG_INFO, "notify entry removed due to timeout\n");
1290 SLIST_REMOVE(&notifyhead, notnp, notifyentry, notify_entry);
1293 notifypacket(so, nfslnp, notnp, i);
1296 i++;
1299 return;
1302 void
1303 notifypacket(int so, void *vnse, void *vnotnp, int packetcount)
1305 struct notifyslaveentry *nse = (struct notifyslaveentry *)vnse;
1306 struct notifyentry *notnp = (struct notifyentry *)vnotnp;
1307 struct sockaddr_in bsin, *sin;
1308 struct sockaddr_in6 bsin6, *sin6;
1309 char packet[512];
1310 char *questionname;
1311 u_int16_t *classtype;
1312 struct dns_header *dnh;
1313 int outlen = 0, slen, ret;
1315 memset(&packet, 0, sizeof(packet));
1316 dnh = (struct dns_header *)&packet[0];
1318 dnh->id = htons(notnp->ids[packetcount]);
1319 SET_DNS_NOTIFY(dnh);
1320 SET_DNS_AUTHORITATIVE(dnh);
1321 SET_DNS_QUERY(dnh);
1322 HTONS(dnh->query);
1324 dnh->question = htons(1);
1326 outlen += sizeof(struct dns_header);
1327 questionname = (char *)&packet[outlen];
1329 memcpy(questionname, notnp->domain, notnp->domainlen);
1330 outlen += notnp->domainlen;
1332 classtype = (u_int16_t *)&packet[outlen];
1333 classtype[0] = htons(DNS_TYPE_SOA);
1334 classtype[1] = htons(DNS_CLASS_IN);
1336 outlen += (2 * sizeof(u_int16_t));
1338 if (nse->family == AF_INET) {
1339 slen = sizeof(struct sockaddr_in);
1340 sin = (struct sockaddr_in *)&nse->hostmask;
1341 memset(&bsin, 0, sizeof(bsin));
1342 bsin.sin_family = AF_INET;
1343 bsin.sin_port = htons(53);
1344 bsin.sin_addr.s_addr = sin->sin_addr.s_addr;
1346 ret = sendto(so, packet, outlen, 0, (struct sockaddr *)&bsin, slen);
1347 } else {
1348 slen = sizeof(struct sockaddr_in6);
1349 sin6 = (struct sockaddr_in6 *)&nse->hostmask;
1350 memset(&bsin6, 0, sizeof(bsin6));
1351 bsin6.sin6_family = AF_INET6;
1352 bsin6.sin6_port = htons(53);
1353 memcpy(&bsin6.sin6_addr, &sin6->sin6_addr, 16);
1355 ret = sendto(so, packet, outlen, 0, (struct sockaddr *)sin6, slen);
1358 if (ret < 0) {
1359 dolog(LOG_INFO, "sendto: %s\n", strerror(errno));
1362 return;