Commit Diff
Diff:
/dev/null
e09edfff240da6c7a6adc0256c1363253941b110
Commit:
e09edfff240da6c7a6adc0256c1363253941b110
Tree:
c724d35f1faa9b10057cf697d9c01ab53ba023ff
Author:
pjp <pjp@delphinusdns.org>
Committer:
pjp <pjp@delphinusdns.org>
Date:
Fri Nov 14 08:09:04 2014 UTC
Message:
initial import after namechange from wildcarddnsd
blob - /dev/null
blob + f6bbd65fdc33d1bf1a88bf9e82d54ad0960bd05c (mode 644)
--- /dev/null
+++ CHANGES
@@ -0,0 +1,54 @@
+Changes in RELEASE_1_0 from BETA_9
+
+- name change from wildcarddnsd to delphinusdnsd
+
+Changes in BETA_9 from BETA_8
+
+- we have replaced parse.c with parse.y
+- Linux now relies on the libressl 2.1.1 library, details in the configure
+- commandline options have been moved to configuration file options to coexist
+- options keyword in the configfile
+- SPF (RR 99) RFC 4408 support
+- fix a bug related to truncation
+- SSHFP (RR 44) RFC 4255 (+ RFC 6594) support
+- defineable ratelimit between 1 and 127 pps , see example8.conf for sample
+- EDNS0 (RFC 6891) get detected and replied upon
+- NAPTR (RFC 2915) support
+- Notify (RFC 1996) master-only support
+- underlying BerkeleyDB databases are now stored in /var/db/wdns/[pid]/*
+ instead of /var/db/wdns, cleanup code for this was written as well.
+- filter and whitelist ip ranges
+- a wildcarddns.conf(5) manpage based on BNF similar to pf.conf(5) with
+ reference from parse.y
+- we now log received bytes and sent bytes per query, log at the end of a
+ log message.
+
+
+Changes in BETA_8 from BETA_7
+
+- we have a non-forking debug mode now with verbosity flag
+- make W compile and run on the Raspberry Pi (great projects with this)
+
+Changes in BETA_7 from BETA_6
+
+- remote (sys)-logging feature with HMAC message verification
+- change to Berkeley DB 4.6+ on BSD hosts (Linux had it already)
+- allow numerous copies of wildcardnsd to pre-fork (-n flag)
+- AXFR master code (this allows a bind9 slave to work with wildcarddnsd)
+- fix an integer overrun in parse.c which prevented big endian hosts to
+ make use of ANY replies and AXFR
+- SRV RR support
+- left sourceforge.net around August 14, 2012
+- Mac OS X compiles now, but has not been tested with queries
+
+Changes in BETA_6 from BETA_5
+
+- Addition of the utterly broken recursive lookup code
+- conf file is at version 4 now
+- inclusion of a "root hints" entry in some example.conf's
+- "ANY" RR type replies
+- pidfiles written to /var/run
+- master process which can be HUP'ed for reloading the nameserver
+- compress_label fix from possible overflow
+- DNS TTL logging fix for OpenBSD
+- TCP filedescriptor fix (so -> tnp->so)
blob - /dev/null
blob + b0e6cd5d7d2947a3ca1b3a291bee7ada3890425e (mode 644)
--- /dev/null
+++ Makefile.freebsd
@@ -0,0 +1,16 @@
+PROG=delphinusdnsd
+
+SRCS=main.c parse.y reply.c additional.c region.c wildcard.c recurse.c log.c axfr.c filter.c ratelimit.c whitelist.c
+
+CFLAGS= -Wall -g -I/usr/local/include/db5/
+LDFLAGS= -Wall -g -L/usr/local/lib/db5/
+LDADD= -lcrypto -lssl -ldb
+
+OBJDIR=.
+BINDIR=/usr/local/sbin
+
+MANDIR=/usr/local/man/man
+MAN= delphinusdnsd.8
+MAN+= delphinusdns.conf.5
+
+.include <bsd.prog.mk>
blob - /dev/null
blob + 2237748822f59e2173e43724fc6995ad4582d9aa (mode 644)
--- /dev/null
+++ Makefile.linux
@@ -0,0 +1,38 @@
+CC=gcc
+#CFLAGS=-DDEBUG
+CFLAGS=
+LDADD= -ldb -lresolv -lcrypto -lrt
+YACC=bison
+AR=ar
+
+
+
+build:
+ $(YACC) $(CFLAGS) -y -d parse.y
+ mv -f y.tab.c parse.c
+ $(CC) $(CFLAGS) -c additional.c
+ $(CC) $(CFLAGS) -c main.c
+ $(CC) $(CFLAGS) -c parse.c
+ $(CC) $(CFLAGS) -c reply.c
+ $(CC) $(CFLAGS) -c region.c
+ $(CC) $(CFLAGS) -c wildcard.c
+ $(CC) $(CFLAGS) -c recurse.c
+ $(CC) $(CFLAGS) -c log.c
+ $(CC) $(CFLAGS) -c axfr.c
+ $(CC) $(CFLAGS) -c filter.c
+ $(CC) $(CFLAGS) -c ratelimit.c
+ $(CC) $(CFLAGS) -c whitelist.c
+ $(AR) -x libressllibcrypto.a libcompat_la-arc4random.o
+ $(AR) -x libressllibcrypto.a libcompat_la-getentropy_linux.o
+ $(AR) -x libressllibcrypto.a libcompatnoopt_la-explicit_bzero.o
+ $(CC) $(CFLAGS) -o delphinusdnsd additional.o main.o parse.o reply.o region.o wildcard.o recurse.o log.o axfr.o filter.o ratelimit.o whitelist.o libcompat_la-arc4random.o libcompat_la-getentropy_linux.o libcompatnoopt_la-explicit_bzero.o $(LDADD)
+
+
+install:
+ test -f delphinusdnsd && install -m 555 delphinusdnsd /usr/local/sbin/
+ mkdir -p /usr/local/man/man8 && install -m 444 delphinusdnsd.8 /usr/local/man/man8
+ mkdir -p /usr/local/man/man5 && install -m 444 delphinusdns.conf.5 /usr/local/man/man5
+
+
+clean:
+ rm -f *.o delphinusdnsd
blob - /dev/null
blob + 7a141e7728f4d9441d5014ee11a3f7442b428390 (mode 644)
--- /dev/null
+++ Makefile.macosx
@@ -0,0 +1,25 @@
+PROG=delphinusdnsd
+CC=gcc
+CFLAGS=-I/opt/local/include/db48 -L/opt/local/lib/db48 -D__APPLE_USE_RFC_3542=1 -DDEFAULT_PRIVILEGE='"nobody"'
+LDADD=-ldb -lssl -lcrypto -DDEFAULT_PRIVILEGE='"nobody"'
+YACC=bison
+
+build:
+ $(YACC) -y -d parse.y
+ mv -f y.tab.c parse.c
+ $(CC) $(CFLAGS) -c parse.c
+ $(CC) $(CFLAGS) -c additional.c
+ $(CC) $(CFLAGS) -c main.c
+ $(CC) $(CFLAGS) -c reply.c
+ $(CC) $(CFLAGS) -c region.c
+ $(CC) $(CFLAGS) -c wildcard.c
+ $(CC) $(CFLAGS) -c log.c
+ $(CC) $(CFLAGS) -c recurse.c
+ $(CC) $(CFLAGS) -c axfr.c
+ $(CC) $(CFLAGS) -c filter.c
+ $(CC) $(CFLAGS) -c ratelimit.c
+ $(CC) $(CFLAGS) -c whitelist.c
+ $(CC) $(CFLAGS) -o delphinusdnsd additional.o main.o parse.o reply.o region.o wildcard.o log.o recurse.o axfr.o filter.o ratelimit.o whitelist.o $(LDADD)
+
+clean:
+ rm -f *.o delphinusdnsd config.h Makefile
blob - /dev/null
blob + 35c515c20e012918cd5f9a2373f8fdb9f1a95d23 (mode 644)
--- /dev/null
+++ Makefile.netbsd
@@ -0,0 +1,16 @@
+PROG=delphinusdnsd
+
+SRCS=main.c parse.y reply.c additional.c region.c wildcard.c recurse.c log.c axfr.c filter.c ratelimit.c whitelist.c
+
+CFLAGS= -Wall -g -I/usr/pkg/include/db5/
+LDFLAGS= -Wall -g -L/usr/pkg/lib -R/usr/pkg/lib
+LDADD= -lcrypto -lssl -ldb5
+
+OBJDIR=.
+BINDIR=/usr/local/sbin
+MANDIR=/usr/local/man/man
+
+MAN= delphinusdnsd.8
+MAN+= delphinusdns.conf.5
+
+.include <bsd.prog.mk>
blob - /dev/null
blob + 8a367010f76277fd9197ae727b505ea540dbcce7 (mode 644)
--- /dev/null
+++ Makefile.openbsd
@@ -0,0 +1,17 @@
+PROG=delphinusdnsd
+
+SRCS=main.c parse.y reply.c additional.c region.c wildcard.c recurse.c log.c axfr.c filter.c ratelimit.c whitelist.c
+
+#CFLAGS= -DDEBUG -g -Wall
+CFLAGS= -Wall -g -I/usr/local/include/db4
+LDFLAGS= -Wall -g -L/usr/local/lib/db4
+LDADD= -lcrypto -lssl -ldb
+
+OBJDIR=.
+BINDIR=/usr/local/sbin
+MANDIR=/usr/local/man/man
+
+MAN= delphinusdnsd.8
+MAN+= delphinusdns.conf.5
+
+.include <bsd.prog.mk>
blob - /dev/null
blob + a2cbea6049708ee6b2188ee4ec0484a0f87f338d (mode 644)
--- /dev/null
+++ README
@@ -0,0 +1,214 @@
+$Id: README,v 1.1.1.1 2014/11/14 08:09:04 pjp Exp $
+
+1. README
+2. WHY DELPINUSDNS?
+3. INSTALL HINTS
+ 3.1 Linux
+ 3.2 FreeBSD
+ 3.3 OpenBSD
+ 3.4 NetBSD
+ 3.5 Mac OS X
+4. COMPATIBILITY
+5. EXAMPLES
+6. WHAT IT CAN'T DO
+
+1. README
+---------
+
+Delphinusdns is a small authoritative nameserver. It does not recurse nor
+search. This program is written to a BSD Style License. Sleepycat's
+BerkeleyDB is also used for the main in-memory database.
+
+2. WHY DELPHINUSDNS?
+-------------------
+
+DNS is simple. Yet implementation of DNS servers is not so simple.
+Delphinusdns is written for research into the DNS system so that perhaps one
+day the author has a better understanding of it. Comparing delphinusdns to
+other DNS implementations is not fair since many of those implementation
+fix Delphinusdns's faults on the Internet. It is recommended that if this
+daemon is used on the Internet that logging be turned on to learn from it
+and the DNS system.
+
+Use the tool "dig" that comes with bind9 to debug Delphinusdns. If you like to
+program, then you can fork Delphinusdns and make your own creation, or you
+can send patches to the author who may implement them into the code.
+
+
+3. INSTALL HINTS
+----------------
+
+To install, type sh ./configure on BSD and just ./configure on Linux. This
+will copy the proper Makefile to ./Makefile. Then you would type make,
+followed by su'ing and make install. Delphinusdnsd installs to /usr/local/sbin.
+
+By default installation the configuration file is not installed you need to
+do this manually. Also by default the config file is specified as
+/etc/delphinusdns.conf this can be changed by adding the -f option to
+delphinusdnsd.
+
+A sample config file exists with the sources. example7.conf was a real life
+config once.
+
+3.1 Linux
+---------
+
+In Linux we rely on LibreSSL 2.1.1, you must download it (configure will
+tell you from where in ./configure) and place it in the directory besides
+delphinusdnsd root directory. You do not have to install it but you have
+to ./configure && make check at least.
+
+In Linux MINT you need to apt-get install build-essential.
+
+## configure the platform
+$ ./configure
+## this will install the development programs you'll need (as root)
+$ apt-get install bison cvs gcc libdb5.3-dev libssl-dev
+## add a privsep user with a chroot directory (option -m) (as root)
+$ useradd -m wdnsd
+## create the db directory (as root)
+$ mkdir -p /var/db/wdns
+## make the program
+$ make
+## install the binary (as root)
+$ make install
+## done, create a config file and start delphinusdnsd
+
+
+3.2 FreeBSD
+-----------
+
+## configure the platform
+$ sh configure
+## this will install the development programs you'll need (as root)
+$ cd /usr/ports/databases/db5 && make install clean
+## add a privsep user with a chroot directory (as root)
+$ vipw
+## or
+$ adduser
+## create the db directory (as root)
+$ mkdir -p /var/db/wdns
+## make the program
+$ make
+## install the binary (as root)
+$ make install
+## done, create a config file and start delphinusdnsd
+
+Jail users, if you get a:
+
+Aug 18 13:43:47 io delphinusdnsd[54350]: starting up
+Aug 18 13:43:47 io delphinusdnsd[54350]: dbenv->open failed: Function not implemented
+
+Similar message, that's probably because your jail must be allowing sysvipc.
+Set this:
+
+jail_example_parameters="allow.sysvipc=1"
+
+in your /etc/rc.conf and reboot the jail.
+
+
+3.3 OpenBSD
+-----------
+
+## configure the platform
+$ sh configure
+## this will install the development programs you'll need (as root)
+$ pkg_add db-4.6.21p0v0
+## add a privsep user with a chroot directory (as root)
+$ vipw
+## or
+$ adduser
+## create the db directory (as root)
+$ mkdir -p /var/db/wdns
+## make the program
+$ make
+## install the binary (as root)
+$ make install
+## done, create a config file and start delphinusdnsd
+
+3.4 NetBSD
+----------
+
+The tests for this were done on NetBSD 6.1.5
+
+## configure the platform
+$ sh configure
+## the following will install the development programs you'll need (as root)
+$ cd /usr/pkgsrc/databases/db5 && make install
+## add a privsep user with a chroot directory (as root)
+$ useradd -m wdnsd
+## create the db directory (as root)
+$ mkdir -p /var/db/wdns
+## make the program
+$ make
+## install the binary (as root)
+$ make install
+## done, create a config file and start delphinusdnsd
+
+3.5 Mac OS X
+------------
+
+## configure for Mac OS X
+$ sh configure
+## Installed berkeley db 4.8 through Mac Ports.
+$ sudo port install db48
+## The binary is made with nobody as the unprivileged user
+$ make
+## cp the binary into /usr/local/sbin/
+$ sudo cp delphinusdnsd /usr/local/sbin/
+## make the /var/db/wdns directory
+$ mkdir -p /var/db/wdns
+## done, create config file and start delphinusdnsd
+
+
+4. COMPATIBILITY
+----------------
+
+------------------+--------------------+---------------------+-------------+
+Operating System**| makes and compiles | responds to queries | Berkeley DB |
+------------------+--------------------+---------------------+-------------+
+FreeBSD 10 | yes | yes | version 5 |
+------------------+--------------------+---------------------+-------------+
+NetBSD 6.1.5 | yes* | yes | version 5 |
+------------------+--------------------+---------------------+-------------+
+OpenBSD 5.6 | yes | yes | version 4.6 |
+------------------+--------------------+---------------------+-------------+
+Linux | yes | yes | version 5 |
+------------------+--------------------+---------------------+-------------+
+Mac OS X Yosemite | yes | yes | version 4.8 |
+with MacPorts | | | |
+------------------+--------------------+---------------------+-------------+
+Rasbian OS | yes *** | yes | version 5 |
+------------------+--------------------+---------------------+-------------+
+
+* there was a period in time that NetBSD didn't compile
+** All Operating Systems require Berkeley DB 4.6 or higher.
+*** Raspbian OS (Raspberry Pi) only has support for Berkeley DB 5.0 or higher
+
+5. EXAMPLES
+-----------
+
+in the directory "examples" are a few examples from working configs.
+
+
+6. WHAT IT CAN'T DO
+-------------------
+
+* CNAME recursion. When you look up www.yahoo.com you see this:
+
+;; ANSWER SECTION:
+www.yahoo.com. 58 IN CNAME fp.wg1.b.yahoo.com.
+fp.wg1.b.yahoo.com. 2802 IN CNAME eu-fp.wa1.b.yahoo.com.
+eu-fp.wa1.b.yahoo.com. 8 IN A 87.248.122.122
+eu-fp.wa1.b.yahoo.com. 8 IN A 87.248.112.181
+
+The recursive part of delphinusdnsd isn't able to recurse CNAME's and it
+also doesn't have loop detection of CNAME's. This means that some lookups
+will fail on a UNIX host using delphinusdnsd recursively, so it's useless
+this needs fixing and the fix isn't simple.
+
+* DNSSEC. Signed zones are becoming more and more common on the Internet.
+Delphinusdnsd can't do DNSSEC yet, unfortunately.
+
+* Solaris. Unless you port some functions to solaris it won't compile on
+there.
blob - /dev/null
blob + 3d56f4fbb54e4bdd9f38006ef76a4e858b7dc9c4 (mode 644)
--- /dev/null
+++ TODO
@@ -0,0 +1,35 @@
+The TODO's here are for delphinusdnsd which is the successor of wildcarddnsd.
+Please see http://delphinusdns.centroid.eu for updates/news.
+
+For 1.0.0 release
+
+- TSIG (RFC 2845) support
+- partition the struct domain, so that we have selective sizes based on
+ what records exist. This is a lot of work but will make records under 44KB
+ again.
+- DNSSEC (RFC 4034 / DNSKEY RR, DS RR, RRSIG RR, NSEC RR)
+- Notify needs to be rewritten for allowing notifies to different slaves.
+- A zone signer possibly written in ruby.
+
+
+Anything else would be nice but I have to put it on the backburner...
+
+* Slave AXFR would be nice (advanced)
+* HINFO support (junior devels)
+* recursing and caching support rewrite (super advanced)
+* DNSSEC support (super advanced)
+* openbsd port support (for 5.0/5.1 perhaps?) (junior/intermediate)
+* recursing/caching todo's: (super advanced)
+
+ - add CNAME traversal so that www.yahoo.com would be listing both CNAME's and
+ the A RR. While doing that it should check for CNAME loops.
+ - zone expiry in then DB database on a timely basis...
+ - retransmissions of queries in both IPv4 and IPv6
+ - a way to turn off IPv6 on hosts that have IPv6 disabled.
+ - a good debug why a lookup of solarscale.de flooded the nameserverservice.de
+ nameservers until abort().
+ - error catchings if a server fails (SERVFAIL).
+ - pthread support
+
+* windows port (advanced)
+* Dynamic DNS support (advanced)
blob - /dev/null
blob + 5b995a8c96df3519c14c30555ed13efdce81648c (mode 644)
--- /dev/null
+++ additional.c
@@ -0,0 +1,470 @@
+/*
+ * Copyright (c) 2005-2014 Peter J. Philipp
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+#include "include.h"
+#include "dns.h"
+#include "db.h"
+
+int additional_a(char *, int, struct domain *, char *, int, int, int *);
+int additional_aaaa(char *, int, struct domain *, char *, int, int, int *);
+int additional_mx(char *, int, struct domain *, char *, int, int, int *);
+int additional_opt(struct question *, char *, int, int);
+int additional_ptr(char *, int, struct domain *, char *, int, int, int *);
+
+extern int compress_label(u_char *, int, int);
+
+static const char rcsid[] = "$Id: additional.c,v 1.1.1.1 2014/11/14 08:09:04 pjp Exp $";
+
+
+/*
+ * ADDITIONAL_A - tag on an additional set of A records to packet
+ */
+
+int
+additional_a(char *name, int namelen, struct domain *sd, char *reply, int replylen, int offset, int *retcount)
+{
+ int a_count;
+ int tmplen;
+ int rroffset = offset;
+
+ struct answer {
+ u_int16_t type;
+ u_int16_t class;
+ u_int32_t ttl;
+ u_int16_t rdlength; /* 12 */
+ in_addr_t rdata; /* 16 */
+ } __attribute__((packed));
+
+ struct answer *answer;
+
+ *retcount = 0;
+
+ /*
+ * We loop through our sd->a entries starting at the ptr offset
+ * first in the first loop and at the beginning until the ptr
+ * in the last loop. This will shift answers based on a_ptr.
+ */
+
+ for (a_count = sd->a_ptr; a_count < sd->a_count; a_count++) {
+ rroffset = offset;
+ if ((offset + namelen) > replylen)
+ goto out;
+
+ memcpy(&reply[offset], name, namelen);
+ offset += namelen;
+ tmplen = compress_label((u_char*)reply, offset, namelen);
+
+ if (tmplen != 0) {
+ offset = tmplen;
+ }
+ if ((offset + sizeof(struct answer)) > replylen) {
+ offset = rroffset;
+ goto out;
+ }
+
+ answer = (struct answer *)&reply[offset];
+
+ answer->type = htons(DNS_TYPE_A);
+ answer->class = htons(DNS_CLASS_IN);
+ answer->ttl = htonl(sd->ttl);
+
+ answer->rdlength = htons(sizeof(in_addr_t));
+
+ memcpy((char *)&answer->rdata, (char *)&sd->a[a_count], sizeof(in_addr_t));
+ offset += sizeof(struct answer);
+ (*retcount)++;
+
+ }
+
+ for (a_count = 0; a_count < sd->a_ptr; a_count++) {
+ rroffset = offset;
+ if ((offset + namelen) > replylen)
+ goto out;
+
+ memcpy(&reply[offset], name, namelen);
+ offset += namelen;
+ tmplen = compress_label((u_char*)reply, offset, namelen);
+
+ if (tmplen != 0) {
+ offset = tmplen;
+ }
+ if ((offset + sizeof(struct answer)) > replylen) {
+ offset = rroffset;
+ goto out;
+ }
+
+ answer = (struct answer *)&reply[offset];
+
+ answer->type = htons(DNS_TYPE_A);
+ answer->class = htons(DNS_CLASS_IN);
+ answer->ttl = htonl(sd->ttl);
+
+ answer->rdlength = htons(sizeof(in_addr_t));
+
+ memcpy((char *)&answer->rdata, (char *)&sd->a[a_count], sizeof(in_addr_t));
+ offset += sizeof(struct answer);
+ (*retcount)++;
+ }
+
+
+out:
+ return (offset);
+
+}
+
+/*
+ * ADDITIONAL_AAAA - tag on an additional set of AAAA records to packet
+ */
+
+int
+additional_aaaa(char *name, int namelen, struct domain *sd, char *reply, int replylen, int offset, int *retcount)
+{
+ int aaaa_count;
+ int tmplen;
+ int rroffset = offset;
+
+ struct answer {
+ u_int16_t type;
+ u_int16_t class;
+ u_int32_t ttl;
+ u_int16_t rdlength;
+ struct in6_addr rdata;
+ } __attribute__((packed));
+
+ struct answer *answer;
+
+ *retcount = 0;
+
+ /*
+ * We loop through our sd->aaaa entries starting at the ptr offset
+ * first in the first loop and at the beginning until the ptr
+ * in the last loop. This will shift answers based on a_ptr.
+ */
+
+ for (aaaa_count = sd->aaaa_ptr; aaaa_count < sd->aaaa_count; aaaa_count++) {
+ rroffset = offset;
+ if ((offset + namelen) > replylen)
+ goto out;
+
+ memcpy(&reply[offset], name, namelen);
+ offset += namelen;
+ tmplen = compress_label((u_char*)reply, offset, namelen);
+
+ if (tmplen != 0) {
+ offset = tmplen;
+ }
+
+ if ((offset + sizeof(struct answer)) > replylen) {
+ offset = rroffset;
+ goto out;
+ }
+
+ answer = (struct answer *)&reply[offset];
+
+ answer->type = htons(DNS_TYPE_AAAA);
+ answer->class = htons(DNS_CLASS_IN);
+ answer->ttl = htonl(sd->ttl);
+
+ answer->rdlength = htons(sizeof(struct in6_addr));
+
+ memcpy((char *)&answer->rdata, (char *)&sd->aaaa[aaaa_count], sizeof(struct in6_addr));
+ offset += sizeof(struct answer);
+ (*retcount)++;
+
+ }
+
+ for (aaaa_count = 0; aaaa_count < sd->aaaa_ptr; aaaa_count++) {
+ rroffset = offset;
+ if ((offset + namelen) > replylen)
+ goto out;
+
+
+ memcpy(&reply[offset], name, namelen);
+ offset += namelen;
+ tmplen = compress_label((u_char*)reply, offset, namelen);
+
+ if (tmplen != 0) {
+ offset = tmplen;
+ }
+ if ((offset + sizeof(struct answer)) > replylen) {
+ offset = rroffset;
+ goto out;
+ }
+
+ answer = (struct answer *)&reply[offset];
+
+ answer->type = htons(DNS_TYPE_AAAA);
+ answer->class = htons(DNS_CLASS_IN);
+ answer->ttl = htonl(sd->ttl);
+
+ answer->rdlength = htons(sizeof(struct in6_addr));
+
+
+ memcpy((char *)&answer->rdata, (char *)&sd->aaaa[aaaa_count], sizeof(struct in6_addr));
+ offset += sizeof(struct answer);
+ (*retcount)++;
+ }
+
+
+out:
+ return (offset);
+
+}
+
+/*
+ * ADDITIONAL_MX() - replies a DNS question (*q) on socket (so)
+ *
+ */
+
+int
+additional_mx(char *name, int namelen, struct domain *sd, char *reply, int replylen, int offset, int *retcount)
+{
+ int mx_count;
+ int tmplen;
+ int rroffset = offset;
+
+ struct answer {
+ u_int16_t type;
+ u_int16_t class;
+ u_int32_t ttl;
+ u_int16_t rdlength;
+ u_int16_t mx_priority;
+ } __attribute__((packed));
+
+ struct answer *answer;
+
+ *retcount = 0;
+
+ /*
+ * We loop through our sd->mx entries starting at the ptr offset
+ * first in the first loop and at the beginning until the ptr
+ * in the last loop. This will shift answers based on mx_ptr.
+ */
+
+ for (mx_count = sd->mx_ptr; mx_count < sd->mx_count; mx_count++) {
+ rroffset = offset;
+
+ if ((offset + namelen) > replylen)
+ goto out;
+
+ memcpy(&reply[offset], name, namelen);
+ offset += namelen;
+ tmplen = compress_label((u_char*)reply, offset, namelen);
+
+ if (tmplen != 0) {
+ offset = tmplen;
+ }
+
+ if ((offset + sizeof(struct answer)) > replylen) {
+ offset = rroffset;
+ goto out;
+ }
+
+ answer = (struct answer *)&reply[offset];
+
+ answer->type = htons(DNS_TYPE_MX);
+ answer->class = htons(DNS_CLASS_IN);
+ answer->ttl = htonl(sd->ttl);
+ answer->mx_priority = htons(sd->mx[mx_count].preference);
+
+ offset += sizeof(struct answer);
+
+ if ((offset + sd->mx[mx_count].exchangelen) > replylen) {
+ offset = rroffset;
+ goto out;
+ }
+
+ memcpy((char *)&reply[offset], (char *)sd->mx[mx_count].exchange, sd->mx[mx_count].exchangelen);
+
+ offset += sd->mx[mx_count].exchangelen;
+ tmplen = compress_label((u_char*)reply, offset, sd->mx[mx_count].exchangelen);
+
+ if (tmplen != 0) {
+ answer->rdlength = htons((sd->mx[mx_count].exchangelen - (offset - tmplen)) + sizeof(u_int16_t));
+ offset = tmplen;
+ } else
+ answer->rdlength = htons(sd->mx[mx_count].exchangelen + sizeof(u_int16_t));
+
+
+ (*retcount)++;
+
+ }
+
+ for (mx_count = 0; mx_count < sd->mx_ptr; mx_count++) {
+ rroffset = offset;
+
+ if ((offset + namelen) > replylen)
+ goto out;
+
+
+ memcpy(&reply[offset], name, namelen);
+ offset += namelen;
+ tmplen = compress_label((u_char*)reply, offset, namelen);
+
+ if (tmplen != 0) {
+ offset = tmplen;
+ }
+
+ if ((offset + sizeof(struct answer)) > replylen) {
+ offset = rroffset;
+ goto out;
+ }
+
+ answer = (struct answer *)&reply[offset];
+
+ answer->type = htons(DNS_TYPE_A);
+ answer->class = htons(DNS_CLASS_IN);
+ answer->ttl = htonl(sd->ttl);
+
+ offset += sizeof(struct answer);
+
+ if ((offset + sd->mx[mx_count].exchangelen) > replylen) {
+ offset = rroffset;
+ goto out;
+ }
+
+ memcpy((char *)&reply[offset], (char *)sd->mx[mx_count].exchange, sd->mx[mx_count].exchangelen);
+
+ offset += sd->mx[mx_count].exchangelen;
+ tmplen = compress_label((u_char *)reply, offset, sd->mx[mx_count].exchangelen);
+
+ if (tmplen != 0) {
+
+ answer->rdlength = htons((sd->mx[mx_count].exchangelen - (offset - tmplen)) + sizeof(u_int16_t));
+ offset = tmplen;
+ } else
+ answer->rdlength = htons(sd->mx[mx_count].exchangelen + sizeof(u_int16_t));
+
+ (*retcount)++;
+ }
+
+
+out:
+ return (offset);
+
+}
+
+/*
+ * ADDITIONAL_PTR() - replies a DNS question (*q) on socket (so)
+ *
+ */
+
+
+int
+additional_ptr(char *name, int namelen, struct domain *sd, char *reply, int replylen, int offset, int *retcount)
+{
+ int tmplen;
+ int rroffset = offset;
+
+ struct answer {
+ u_int16_t type;
+ u_int16_t class;
+ u_int32_t ttl;
+ u_int16_t rdlength;
+ } __attribute__((packed));
+
+ struct answer *answer;
+
+ *retcount = 0;
+
+
+ if ((offset + namelen) > replylen)
+ goto out;
+
+ memcpy(&reply[offset], name, namelen);
+ offset += namelen;
+ tmplen = compress_label((u_char*)reply, offset, namelen);
+
+ if (tmplen != 0) {
+ offset = tmplen;
+ }
+
+ if ((offset + sizeof(struct answer)) > replylen) {
+ offset = rroffset;
+ goto out;
+ }
+
+ answer = (struct answer *)&reply[offset];
+
+ answer->type = htons(DNS_TYPE_PTR);
+ answer->class = htons(DNS_CLASS_IN);
+ answer->ttl = htonl(sd->ttl);
+
+ offset += sizeof(struct answer);
+
+ if ((offset + sd->ptrlen) > replylen) {
+ offset = rroffset;
+ goto out;
+ }
+
+ memcpy((char *)&reply[offset], (char *)sd->ptr, sd->ptrlen);
+
+ offset += sd->ptrlen;
+ tmplen = compress_label((u_char*)reply, offset, sd->ptrlen);
+
+ if (tmplen != 0) {
+ answer->rdlength = htons(sd->ptrlen - (offset - tmplen));
+ offset = tmplen;
+ } else
+ answer->rdlength = htons(sd->ptrlen);
+
+
+ (*retcount)++;
+
+out:
+ return (offset);
+
+}
+
+/*
+ * ADDITIONAL_OPT - tag on an additional EDNS0 (OPT) record to packet
+ */
+
+int
+additional_opt(struct question *question, char *reply, int replylen, int offset)
+{
+ struct dns_optrr *answer;
+
+ if ((offset + sizeof(struct dns_optrr)) > replylen) {
+ goto out;
+ }
+
+ answer = (struct dns_optrr *)&reply[offset];
+
+ memset(answer->name, 0, sizeof(answer->name));
+ answer->type = htons(DNS_TYPE_OPT);
+ answer->class = htons(question->edns0len);
+ answer->ttl = htonl(0);
+
+ answer->rdlen = htons(0);
+
+ offset += sizeof(struct dns_optrr);
+
+out:
+ return (offset);
+
+}
blob - /dev/null
blob + 050348851818063e80ea00e39ed656ba70723e16 (mode 644)
--- /dev/null
+++ axfr.c
@@ -0,0 +1,1363 @@
+/*
+ * Copyright (c) 2011-2014 Peter J. Philipp
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+#include "include.h"
+#include "dns.h"
+#include "db.h"
+
+
+void axfrloop(int *, int, char **, DB *);
+void axfr_connection(int, char *, int, DB *);
+int build_header(DB *, char *, char *, struct question *, int);
+int build_soa(DB *, char *, int, struct domain *, struct question *);
+int checklabel(DB *, struct domain *, struct domain *, struct question *);
+void gather_notifydomains(DB *);
+void init_axfr(void);
+void init_notifyslave(void);
+int insert_axfr(char *, char *);
+int insert_notifyslave(char *, char *);
+void notifypacket(int, void *, void *, int);
+void notifyslaves(int *);
+void reap(int);
+
+extern in_addr_t getmask(int);
+extern int getmask6(int, struct sockaddr_in6 *);
+extern void reply_fmterror(struct sreply *);
+extern void reply_nxdomain(struct sreply *);
+extern int get_soa(DB *, struct question *, struct domain *, int);
+extern int compress_label(u_char *, int, int);
+extern u_int16_t create_anyreply(struct sreply *, char *, int, int, int);
+extern struct question *build_fake_question(char *, int, u_int16_t);
+extern struct question *build_question(char *, int, int);
+extern int free_question(struct question *);
+extern void dolog(int, char *, ...);
+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 *);
+
+
+int notify = 0; /* do not notify when set to 0 */
+
+extern int debug, verbose;
+extern time_t time_changed;
+
+SLIST_HEAD(listhead, axfrentry) axfrhead;
+
+static struct axfrentry {
+ char name[INET6_ADDRSTRLEN];
+ int family;
+ struct sockaddr_storage hostmask;
+ struct sockaddr_storage netmask;
+ u_int8_t prefixlen;
+ SLIST_ENTRY(axfrentry) axfr_entry;
+} *an2, *anp;
+
+SLIST_HEAD(notifyslavelisthead, notifyslaveentry) notifyslavehead;
+
+static struct notifyslaveentry {
+ char name[INET6_ADDRSTRLEN];
+ int family;
+ struct sockaddr_storage hostmask;
+ struct sockaddr_storage netmask;
+ u_int8_t prefixlen;
+ SLIST_ENTRY(notifyslaveentry) notifyslave_entry;
+} *nfslnp2, *nfslnp;
+
+
+
+SLIST_HEAD(notifylisthead, notifyentry) notifyhead;
+
+static struct notifyentry {
+ char domain[DNS_MAXNAME];
+ int domainlen;
+ u_int16_t *ids;
+ u_int16_t *attempts;
+ SLIST_ENTRY(notifyentry) notify_entry;
+} *notn2, *notnp;
+
+
+static const char rcsid[] = "$Id: axfr.c,v 1.1.1.1 2014/11/14 08:09:04 pjp Exp $";
+
+/*
+ * INIT_AXFR - initialize the axfr singly linked list
+ */
+
+void
+init_axfr(void)
+{
+ SLIST_INIT(&axfrhead);
+ return;
+}
+
+/*
+ * INSERT_AXFR - insert an address and prefixlen into the axfr slist
+ */
+
+int
+insert_axfr(char *address, char *prefixlen)
+{
+ struct sockaddr_in *sin;
+ struct sockaddr_in6 *sin6;
+ int pnum;
+ int ret;
+
+ pnum = atoi(prefixlen);
+ an2 = malloc(sizeof(struct axfrentry)); /* Insert after. */
+
+ if (strchr(address, ':') != NULL) {
+ an2->family = AF_INET6;
+ sin6 = (struct sockaddr_in6 *)&an2->hostmask;
+ if ((ret = inet_pton(AF_INET6, address, &sin6->sin6_addr.s6_addr)) != 1)
+ return (-1);
+ sin6->sin6_family = AF_INET6;
+ sin6 = (struct sockaddr_in6 *)&an2->netmask;
+ sin6->sin6_family = AF_INET6;
+ if (getmask6(pnum, sin6) < 0)
+ return(-1);
+ an2->prefixlen = pnum;
+ } else {
+
+ an2->family = AF_INET;
+ sin = (struct sockaddr_in *)&an2->hostmask;
+ sin->sin_family = AF_INET;
+ sin->sin_addr.s_addr = inet_addr(address);
+ sin = (struct sockaddr_in *)&an2->netmask;
+ sin->sin_family = AF_INET;
+ sin->sin_addr.s_addr = getmask(pnum);
+ an2->prefixlen = pnum;
+
+ }
+
+ SLIST_INSERT_HEAD(&axfrhead, an2, axfr_entry);
+
+ return (0);
+}
+
+/*
+ * FIND_AXFR - walk the axfr list and find the correponding network
+ * if a network matches return 1, if no match is found return
+ * 0.
+ */
+
+int
+find_axfr(struct sockaddr_storage *sst, int family)
+{
+ struct sockaddr_in *sin, *sin0;
+ struct sockaddr_in6 *sin6, *sin60, *sin61;
+ u_int32_t hostmask, netmask;
+ u_int32_t a;
+#ifdef __amd64
+ u_int64_t *hm[2], *nm[2], *a6[2];
+#else
+ u_int32_t *hm[4], *nm[4], *a6[4];
+#endif
+
+ SLIST_FOREACH(anp, &axfrhead, axfr_entry) {
+ if (anp->family == AF_INET) {
+ if (family != AF_INET)
+ continue;
+ sin = (struct sockaddr_in *)sst;
+ a = sin->sin_addr.s_addr;
+ sin = (struct sockaddr_in *)&anp->hostmask;
+ sin0 = (struct sockaddr_in *)&anp->netmask;
+ hostmask = sin->sin_addr.s_addr;
+ netmask = sin0->sin_addr.s_addr;
+ if ((hostmask & netmask) == (a & netmask)) {
+ return (1);
+ } /* if hostmask */
+ } else if (anp->family == AF_INET6) {
+ if (family != AF_INET6)
+ continue;
+ sin6 = (struct sockaddr_in6 *)sst;
+ sin60 = (struct sockaddr_in6 *)&anp->hostmask;
+ sin61 = (struct sockaddr_in6 *)&anp->netmask;
+#ifdef __amd64
+ /*
+ * If this is on a 64 bit machine, we'll benefit
+ * by using 64 bit registers, this should make it
+ * a tad faster...
+ */
+ hm[0] = (u_int64_t *)&sin60->sin6_addr.s6_addr;
+ hm[1] = (hm[0] + 1);
+ nm[0] = (u_int64_t *)&sin61->sin6_addr.s6_addr;
+ nm[1] = (nm[0] + 1);
+ a6[0] = (u_int64_t *)&sin6->sin6_addr.s6_addr;
+ a6[1] = (a6[0] + 1);
+ if ( ((*hm[0] & *nm[0]) == (*a6[0] & *nm[0]))&&
+ ((*hm[1] & *nm[1]) == (*a6[1] & *nm[1]))) {
+#else
+ hm[0] = (u_int32_t *)&sin60->sin6_addr.s6_addr;
+ hm[1] = (hm[0] + 1); hm[2] = (hm[1] + 1);
+ hm[3] = (hm[2] + 1);
+ nm[0] = (u_int32_t *)&sin61->sin6_addr.s6_addr;
+ nm[1] = (nm[0] + 1); nm[2] = (nm[1] + 1);
+ nm[3] = (nm[2] + 1);
+ a6[0] = (u_int32_t *)&sin6->sin6_addr.s6_addr;
+ a6[1] = (a6[0] + 1); a6[2] = (a6[1] + 1);
+ a6[3] = (a6[2] + 1);
+
+ if ( ((*hm[0] & *nm[0]) == (*a6[0] & *nm[0]))&&
+ ((*hm[1] & *nm[1]) == (*a6[1] & *nm[1]))&&
+ ((*hm[2] & *nm[2]) == (*a6[2] & *nm[2]))&&
+ ((*hm[3] & *nm[3]) == (*a6[3] & *nm[3]))) {
+#endif
+
+ return (1);
+ } /* if ip6 address */
+
+ } /* if AF_INET6 */
+ } /* SLIST */
+
+ return (0);
+}
+
+/*
+ * INIT_NOTIFYSLAVE - initialize the axfr singly linked list
+ */
+
+void
+init_notifyslave(void)
+{
+ SLIST_INIT(&notifyslavehead);
+ return;
+}
+
+/*
+ * INSERT_NOTIFYSLAVE - insert an address and prefixlen into the notifyslave slist
+ */
+
+int
+insert_notifyslave(char *address, char *prefixlen)
+{
+ struct sockaddr_in *sin;
+ struct sockaddr_in6 *sin6;
+ int pnum;
+ int ret;
+
+ pnum = atoi(prefixlen);
+ nfslnp2 = calloc(1, sizeof(struct notifyslaveentry)); /* Insert after. */
+ if (nfslnp2 == NULL)
+ return (-1);
+
+
+ if (strchr(address, ':') != NULL) {
+ nfslnp2->family = AF_INET6;
+ sin6 = (struct sockaddr_in6 *)&nfslnp2->hostmask;
+ if ((ret = inet_pton(AF_INET6, address, &sin6->sin6_addr.s6_addr)) != 1)
+ return (-1);
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_port = htons(53);
+ sin6 = (struct sockaddr_in6 *)&nfslnp2->netmask;
+ sin6->sin6_family = AF_INET6;
+ if (getmask6(pnum, sin6) < 0)
+ return(-1);
+ nfslnp2->prefixlen = pnum;
+ } else {
+
+ nfslnp2->family = AF_INET;
+ sin = (struct sockaddr_in *)&nfslnp2->hostmask;
+ sin->sin_family = AF_INET;
+ sin->sin_addr.s_addr = inet_addr(address);
+ sin->sin_port = htons(53);
+ sin = (struct sockaddr_in *)&nfslnp2->netmask;
+ sin->sin_family = AF_INET;
+ sin->sin_addr.s_addr = getmask(pnum);
+ nfslnp2->prefixlen = pnum;
+
+ }
+
+ SLIST_INSERT_HEAD(&notifyslavehead, nfslnp2, notifyslave_entry);
+
+ return (0);
+}
+
+void
+axfrloop(int *afd, int sockcount, char **ident, DB *db)
+{
+ fd_set rset;
+
+ struct timeval tv;
+ struct sockaddr_storage from;
+ struct sockaddr_in6 *sin6, *sin62;
+ struct sockaddr_in *sin, *sin2;
+ struct dns_header *dh;
+ struct question *question;
+
+ int i, so, len;
+ int sel, maxso = 0;
+ int is_ipv6, axfr_acl;
+ int notifyfd[2];
+
+ socklen_t fromlen;
+ char buf[512];
+
+ time_t now;
+ pid_t pid;
+
+ char address[INET6_ADDRSTRLEN];
+
+ signal(SIGCHLD, reap);
+
+ for (i = 0; i < sockcount; i++) {
+ listen(afd[i], 5);
+ }
+
+ if (notify) {
+ /*
+ * If a zonefile has changed in the last half hour then
+ * gather all notifydomains and start the notify process
+ */
+
+ notifyfd[0] = -1;
+ notifyfd[1] = -1;
+
+ now = time(NULL);
+ if (difftime(now, time_changed) <= 1800) {
+ gather_notifydomains(db);
+ notifyfd[0] = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ notifyfd[1] = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
+
+ memset((char *)&from, 0, sizeof(from));
+ sin = (struct sockaddr_in *)&from;
+ sin->sin_family = AF_INET;
+ sin->sin_port = htons(0);
+
+ if (bind(notifyfd[0], (struct sockaddr *)sin, sizeof(*sin)) < 0) {
+ dolog(LOG_INFO, "bind notify: %s\n", strerror(errno));
+ }
+
+ memset((char *)&from, 0, sizeof(from));
+ sin6 = (struct sockaddr_in6 *)&from;
+ sin->sin_family = AF_INET6;
+ sin->sin_port = htons(0);
+
+ if (bind(notifyfd[1], (struct sockaddr *)sin, sizeof(*sin6)) < 0) {
+ dolog(LOG_INFO, "bind notify6: %s\n", strerror(errno));
+ }
+
+ memset((char *)&from, 0, sizeof(from));
+
+ notifyslaves((int *)&notifyfd);
+ }
+ }
+
+ for (;;) {
+
+ FD_ZERO(&rset);
+ maxso = 0;
+
+ for (i = 0; i < sockcount; i++) {
+ FD_SET(afd[i], &rset);
+ if (maxso < afd[i])
+ maxso = afd[i];
+ }
+
+ if (notify) {
+ if (notifyfd[0] > -1) {
+ FD_SET(notifyfd[0], &rset);
+ if (maxso < notifyfd[0])
+ maxso = notifyfd[0];
+ }
+
+ if (notifyfd[1] > -1) {
+ FD_SET(notifyfd[1], &rset);
+ if (maxso < notifyfd[1])
+ maxso = notifyfd[1];
+ }
+ }
+
+ tv.tv_sec = 10;
+ tv.tv_usec = 0;
+
+ sel = select(maxso + 1, &rset, NULL, NULL, &tv);
+
+ if (sel == 0) {
+ if (notify) {
+ if (notifyfd[0] > -1 || notifyfd[1] > -1)
+ notifyslaves((int *)&notifyfd);
+ }
+
+ continue;
+ }
+ if (sel < 0) {
+ dolog(LOG_INFO, "select: %s\n", strerror(errno));
+ continue;
+ }
+
+ for (i = 0; i < sockcount; i++) {
+ if (FD_ISSET(afd[i], &rset)) {
+ fromlen = sizeof(struct sockaddr_storage);
+
+ so = accept(afd[i], (struct sockaddr*)&from, &fromlen);
+ if (so < 0) {
+ dolog(LOG_INFO, "afd accept: %s\n", strerror(errno));
+ continue;
+ }
+
+ if (from.ss_family == AF_INET6) {
+ is_ipv6 = 1;
+
+ fromlen = sizeof(struct sockaddr_in6);
+ sin6 = (struct sockaddr_in6 *)&from;
+ inet_ntop(AF_INET6, (void*)&sin6->sin6_addr, (char*)&address, sizeof(address));
+ axfr_acl = find_axfr((struct sockaddr_storage *)sin6, AF_INET6);
+
+ } else if (from.ss_family == AF_INET) {
+ is_ipv6 = 0;
+
+ fromlen = sizeof(struct sockaddr_in);
+ sin = (struct sockaddr_in *)&from;
+ inet_ntop(AF_INET, (void*)&sin->sin_addr, (char*)&address, sizeof(address));
+
+ axfr_acl = find_axfr((struct sockaddr_storage *)sin, AF_INET);
+
+ } else {
+ dolog(LOG_INFO, "afd accept unknown family %d, close\n", from.ss_family);
+ close(so);
+ continue;
+ }
+
+ if (! axfr_acl) {
+ dolog(LOG_INFO, "connection from %s was not in our axfr acl, drop\n", address);
+ close(so);
+ continue;
+ }
+
+ dolog(LOG_INFO, "AXFR connection from %s on interface \"%s\"\n", address, ident[i]);
+
+ switch (pid = fork()) {
+ case 0:
+ axfr_connection(so, address, is_ipv6, db);
+ exit(0);
+ /*NOTREACHED*/
+ default:
+ close(so);
+ break;
+ }
+
+ } /* if(FD_ISSET..) */
+
+ } /* for (i.. */
+
+ if (notify) {
+ if (notifyfd[0] > -1 && FD_ISSET(notifyfd[0], &rset)) {
+ fromlen = sizeof(struct sockaddr_storage);
+ len = recvfrom(notifyfd[0], buf, sizeof(buf), 0, (struct sockaddr *)&from, &fromlen);
+ if (len < 0) {
+ dolog(LOG_INFO, "recvfrom: %s\n", strerror(errno));
+ }
+
+ if (len < sizeof(struct dns_header)) {
+ dolog(LOG_INFO, "received bogus reply on notify port, drop\n");
+ continue;
+ }
+
+ dh = (struct dns_header *)&buf[0];
+ if (ntohs(dh->question) != 1) {
+ dolog(LOG_INFO, "question header on notify reply not 1, drop\n");
+ continue;
+ }
+
+ if (! (ntohs(dh->query) & DNS_REPLY)) {
+ dolog(LOG_INFO, "question header is not a reply, drop\n");
+ continue;
+ }
+
+ question = build_question(buf, len, ntohs(dh->additional));
+ if (question == NULL) {
+ dolog(LOG_INFO, "build_question failed on notify reply, drop\n");
+ continue;
+ }
+
+ sin = (struct sockaddr_in *)&from;
+ inet_ntop(AF_INET, (void*)&sin->sin_addr, (char*)&address, sizeof(address));
+
+#ifdef __linux__
+ SLIST_FOREACH(notnp, &notifyhead, notify_entry) {
+#else
+ SLIST_FOREACH_SAFE(notnp, &notifyhead, notify_entry, notn2) {
+#endif
+
+ for (i = 0; i < notify; i++) {
+ if (ntohs(dh->id) == notnp->ids[i] &&
+ (ntohs(dh->query) & DNS_NOTIFY) &&
+ (ntohs(dh->query) & DNS_AUTH) &&
+ ntohs(question->hdr->qtype) == DNS_TYPE_SOA &&
+ ntohs(question->hdr->qclass) == DNS_CLASS_IN &&
+ question->hdr->namelen == notnp->domainlen &&
+ memcmp(question->hdr->name, notnp->domain, notnp->domainlen) == 0) {
+#ifdef __linux__
+ SLIST_FOREACH(nfslnp, &notifyslavehead, notifyslave_entry) {
+#else
+ SLIST_FOREACH_SAFE(nfslnp, &notifyslavehead, notifyslave_entry, nfslnp2) {
+#endif
+ if (nfslnp->family != AF_INET)
+ continue;
+
+ sin2 = (struct sockaddr_in *)&nfslnp->hostmask;
+ if (sin->sin_addr.s_addr == sin2->sin_addr.s_addr) {
+ dolog(LOG_INFO, "notify success! removing address \"%s\" from notify contact list\n", address);
+ SLIST_REMOVE(&notifyslavehead, nfslnp, notifyslaveentry, notifyslave_entry);
+ }
+ }
+ } else {
+ 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));
+ }
+ }
+ }
+
+ free_question(question);
+
+ if (SLIST_EMPTY(&notifyslavehead)) {
+ dolog(LOG_INFO, "notifys have been completed, closing notify descriptors!\n");
+ if (notifyfd[0] > -1)
+ close(notifyfd[0]);
+
+ if (notifyfd[1] > -1)
+ close(notifyfd[1]);
+
+ notifyfd[0] = -1;
+ notifyfd[1] = -1;
+ }
+ }
+
+ if (notifyfd[1] > -1 && FD_ISSET(notifyfd[1], &rset)) {
+ fromlen = sizeof(struct sockaddr_storage);
+ len = recvfrom(notifyfd[1], buf, sizeof(buf), 0, (struct sockaddr *)&from, &fromlen);
+ if (len < 0) {
+ dolog(LOG_INFO, "recvfrom: %s\n", strerror(errno));
+ }
+
+ if (len < sizeof(struct dns_header)) {
+ dolog(LOG_INFO, "received bogus reply on notify port, drop\n");
+ continue;
+ }
+
+ dh = (struct dns_header *)&buf[0];
+ if (ntohs(dh->question) != 1) {
+ dolog(LOG_INFO, "question header on notify reply not 1, drop\n");
+ continue;
+ }
+
+ if (! (ntohs(dh->query) & DNS_REPLY)) {
+ dolog(LOG_INFO, "question header is not a reply, drop\n");
+ continue;
+ }
+
+ question = build_question(buf, len, ntohs(dh->additional));
+ if (question == NULL) {
+ dolog(LOG_INFO, "build_question failed on notify reply, drop\n");
+ continue;
+ }
+
+ sin6 = (struct sockaddr_in6 *)&from;
+ inet_ntop(AF_INET6, (void*)&sin6->sin6_addr, (char*)&address, sizeof(address));
+
+#ifdef __linux
+ SLIST_FOREACH(notnp, &notifyhead, notify_entry) {
+#else
+ SLIST_FOREACH_SAFE(notnp, &notifyhead, notify_entry, notn2) {
+#endif
+ for (i = 0; i < notify; i++) {
+ if (ntohs(dh->id) == notnp->ids[i] &&
+ (ntohs(dh->query) & DNS_NOTIFY) &&
+ (ntohs(dh->query) & DNS_AUTH) &&
+ ntohs(question->hdr->qtype) == DNS_TYPE_SOA &&
+ ntohs(question->hdr->qclass) == DNS_CLASS_IN &&
+ question->hdr->namelen == notnp->domainlen &&
+ memcmp(question->hdr->name, notnp->domain, notnp->domainlen) == 0) {
+#ifdef __linux__
+ SLIST_FOREACH(nfslnp, &notifyslavehead, notifyslave_entry) {
+#else
+ SLIST_FOREACH_SAFE(nfslnp, &notifyslavehead, notifyslave_entry, nfslnp2) {
+#endif
+ if (nfslnp->family != AF_INET6)
+ continue;
+
+ sin62 = (struct sockaddr_in6 *)&nfslnp->hostmask;
+ if (memcmp(&sin6->sin6_addr, &sin62->sin6_addr, 16) == 0) {
+ dolog(LOG_INFO, "notify success! removing address \"%s\" from notify contact list\n", address);
+ SLIST_REMOVE(&notifyslavehead, nfslnp, notifyslaveentry, notifyslave_entry);
+ }
+ }
+ } else {
+ 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));
+ }
+ }
+ }
+
+ free_question(question);
+
+ if (SLIST_EMPTY(&notifyslavehead)) {
+ dolog(LOG_INFO, "notifys have been completed, closing notify descriptors!\n");
+ if (notifyfd[0] > -1)
+ close(notifyfd[0]);
+
+ if (notifyfd[1] > -1)
+ close(notifyfd[1]);
+
+ notifyfd[0] = -1;
+ notifyfd[1] = -1;
+ }
+ }
+
+ }
+
+ } /* for (;;) */
+
+}
+
+/*
+ * AXFR_CONNECTION - this is the main core of AXFR engine, forked
+ *
+ */
+
+void
+axfr_connection(int so, char *address, int is_ipv6, DB *db)
+{
+
+ char buf[4000];
+ char *p = &buf[0];
+ char *q;
+ char *reply;
+
+ int len, dnslen;
+ int offset = 0;
+ int ret;
+ int qlen;
+ int outlen, i;
+ int rrcount;
+
+ u_int16_t *tmp;
+
+ struct dns_header *dh, *odh;
+ struct sreply sreply;
+ struct question *question, *fq;
+ struct domain soa, sdomain, nsdomain, savesd;
+
+ DBT key, data;
+ DBC *cursor;
+
+ for (;;) {
+ len = recv(so, p + offset, sizeof(buf) - offset, 0);
+ if (len <= 0) {
+ close(so);
+ exit(1);
+ }
+
+ /*
+ * do a little dance here because we don't know if the
+ * input is fragmented or not...
+ */
+ if (offset + len >= 2) {
+ tmp = (u_int16_t *)p;
+ dnslen = ntohs(*tmp);
+ } else {
+ offset += len;
+ continue;
+ }
+ if (dnslen + 2 != offset + len) {
+ offset += len;
+ continue;
+ }
+
+
+ /* by now the packet should be normalized */
+
+ dh = (struct dns_header *)(p + 2);
+
+ if ((ntohs(dh->query) & DNS_REPLY)) {
+ dolog(LOG_INFO, "AXFR dns packet is not a question, drop\n");
+ goto drop;
+ }
+
+ if (ntohs(dh->question) != 1) {
+ dolog(LOG_INFO, "AXFR dns packet does not have a question count of 1 (RFC 5936, page 9), reply fmterror\n");
+
+ build_reply(&sreply, so, (p + 2), dnslen, NULL, NULL, 0, NULL, NULL, 0xff, 1, 0, NULL);
+
+ reply_fmterror(&sreply);
+ goto drop;
+ }
+
+ if ((question = build_question((p + 2), dnslen, 0)) == NULL) {
+ dolog(LOG_INFO, "AXFR malformed question, drop\n");
+ goto drop;
+ }
+
+ if (ntohs(question->hdr->qclass) != DNS_CLASS_IN) {
+ dolog(LOG_INFO, "AXFR question wasn't for class DNS_CLASS_IN, drop\n");
+ goto drop;
+ }
+
+ switch (ntohs(question->hdr->qtype)) {
+ case DNS_TYPE_AXFR:
+ case DNS_TYPE_IXFR:
+ case DNS_TYPE_SOA:
+ break;
+ default:
+ dolog(LOG_INFO, "AXFR question wasn't for valid types (ixfr, axfr, soa) with requested type %d, drop\n", ntohs(question->hdr->qtype));
+ goto drop;
+
+ }
+
+ /* now we can be reasonably sure that it's an AXFR for us */
+
+ reply = calloc(1, 65538);
+ if (reply == NULL) {
+ dolog(LOG_INFO, "internal error: %s\n", strerror(errno));
+ goto drop;
+ }
+
+ odh = (struct dns_header *)(reply + 2);
+
+ q = question->hdr->name;
+ qlen = question->hdr->namelen;
+
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+
+ key.data = (char *)q;
+ key.size = qlen;
+
+ data.data = NULL;
+ data.size = 0;
+
+ ret = db->get(db, NULL, &key, &data, 0);
+
+ if (ret != 0) {
+ memset(&sdomain, 0, sizeof(sdomain));
+ (void)get_soa(db, question, &sdomain, 0);
+ build_reply(&sreply, so, (p + 2), dnslen, question, NULL, 0, &sdomain, NULL, 0xff, 1, 0, NULL);
+ reply_nxdomain(&sreply);
+ dolog(LOG_INFO, "AXFR request for zone %s, no db entry, nxdomain -> drop\n", question->converted_name);
+ goto drop;
+ }
+
+ if (data.size != sizeof(struct domain)) {
+ dolog(LOG_INFO, "AXFR btree db is damaged, drop\n");
+ goto drop;
+ }
+
+ memcpy((char *)&soa, (char *)data.data, data.size);
+
+ /*
+ * check if we have an SOA record
+ */
+
+ if (! (soa.flags & DOMAIN_HAVE_SOA)) {
+ memset(&sdomain, 0, sizeof(sdomain));
+ (void)get_soa(db, question, &sdomain, 0);
+ build_reply(&sreply, so, (p + 2), dnslen, question, NULL, 0, &sdomain, NULL, 0xff, 1, 0, NULL);
+ reply_nxdomain(&sreply);
+
+ dolog(LOG_INFO, "AXFR request for zone %s, which has no SOA for the zone, nxdomain -> drop\n", question->converted_name);
+ goto drop;
+ }
+
+ if (ntohs(question->hdr->qtype) == DNS_TYPE_SOA) {
+ dolog(LOG_INFO, "TCP SOA request for zone \"%s\", replying...\n", question->converted_name);
+ outlen = 0;
+ outlen = build_header(db, (reply + 2), (p + 2), question, 1);
+ outlen = build_soa(db, (reply + 2), outlen, &soa, question);
+
+ tmp = (u_int16_t *)reply;
+ *tmp = htons(outlen);
+
+ len = send(so, reply, outlen + 2, 0);
+ if (len <= 0) {
+ goto drop;
+ }
+
+ outlen = 0;
+ offset = 0;
+ p = &buf[0];
+
+ free (reply);
+
+ continue;
+ }
+
+ dolog(LOG_INFO, "%s request for zone \"%s\", replying...\n",
+ (ntohs(question->hdr->qtype) == DNS_TYPE_AXFR ? "AXFR"
+ : "IXFR"), question->converted_name);
+
+ outlen = build_header(db, (reply + 2), (p + 2), question, 0);
+ outlen = build_soa(db, (reply + 2), outlen, &soa, question);
+ rrcount = 1;
+
+ if (db->cursor(db, NULL, &cursor, 0) != 0) {
+ dolog(LOG_INFO, "db->cursor: %s\n", strerror(errno));
+ goto drop;
+ }
+
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+
+
+ if (cursor->c_get(cursor, &key, &data, DB_FIRST) != 0) {
+ dolog(LOG_INFO, "cursor->c_get: %s\n", strerror(errno));
+ goto drop;
+ }
+
+ do {
+ if (data.size != sizeof(struct domain)) {
+ dolog(LOG_INFO, "AXFR btree db is damaged (%d), drop\n", __LINE__);
+ goto drop;
+ }
+
+ memcpy((char *)&sdomain, (char *)data.data, data.size);
+ memcpy((char *)&savesd, (char *)data.data, data.size);
+
+ if (checklabel(db, &sdomain, &soa, question)) {
+ fq = build_fake_question(sdomain.zone, sdomain.zonelen, 0);
+ build_reply(&sreply, so, (p + 2), dnslen, fq, NULL, 0, &sdomain, NULL, 0xff, 1, 0, NULL);
+ outlen = create_anyreply(&sreply, (reply + 2), 65535, outlen, 0);
+ free_question(fq);
+
+ if ((savesd.flags & DOMAIN_HAVE_NS) &&
+ (savesd.ns_type & NS_TYPE_DELEGATE)) {
+ for (i = 0; i < savesd.ns_count; i++) {
+ fq = build_fake_question(savesd.ns[i].nsserver,
+ savesd.ns[i].nslen, 0);
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+
+ key.data = fq->hdr->name;
+ key.size = fq->hdr->namelen;
+
+ data.data = NULL;
+ data.size = 0;
+
+ ret = db->get(db, NULL, &key, &data, 0);
+ if (ret != 0) {
+ free_question(fq);
+ continue;
+ }
+
+ if (data.size != sizeof(struct domain)) {
+ dolog(LOG_INFO, "AXFR btree db is damaged (%d), drop\n", __LINE__);
+ goto drop;
+ }
+
+ memcpy((char *)&nsdomain, (char*)data.data, data.size);
+
+ build_reply(&sreply, so, (p + 2), dnslen, fq, NULL, 0, &nsdomain, NULL, 0xff, 1, 0, NULL);
+ outlen = create_anyreply(&sreply, (reply + 2), 65535, outlen, 0);
+ free_question(fq);
+
+ } /* for (i.. */
+ } /* if (sdomain.flags */
+
+ } /* if (checklabel */
+
+ /*
+ * if we accumulate 60000 bytes out of the maximum
+ * 65535 bytes then we fragment.
+ */
+ /* XXX */
+ if (outlen > 60000) {
+ tmp = (u_int16_t *)reply;
+ *tmp = htons(outlen);
+
+ /* set the rrcount in there */
+
+ NTOHS(odh->answer);
+ odh->answer += rrcount;
+ HTONS(odh->answer);
+
+ len = send(so, reply, outlen + 2, 0);
+ if (len <= 0) {
+ goto drop;
+ }
+
+ rrcount = 0;
+ outlen = build_header(db, (reply + 2), (p + 2), question, 0);
+ }
+
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+ } while (cursor->c_get(cursor, &key, &data, DB_NEXT) == 0);
+
+ cursor->c_close(cursor);
+
+ outlen = build_soa(db, (reply + 2), outlen, &soa, question);
+ rrcount++;
+
+ tmp = (u_int16_t *)reply;
+ *tmp = htons(outlen);
+
+ /* set the rrcount in there */
+
+ NTOHS(odh->answer);
+ odh->answer += rrcount;
+ HTONS(odh->answer);
+
+ len = send(so, reply, outlen + 2, 0);
+ if (len <= 0)
+ goto drop;
+
+ goto drop;
+
+ } /* for(;;) */
+
+
+
+drop:
+ close(so);
+ exit(0);
+}
+
+/*
+ * REAP - reap the child that is zombied by now, this is a sighandler for
+ * SIGCHLD
+ */
+
+void
+reap(int sig)
+{
+ int status;
+ pid_t pid;
+
+ while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
+ }
+}
+
+
+/*
+ * build_header - build a header reply
+ *
+ */
+
+int
+build_header(DB *db, char *reply, char *buf, struct question *q, int answercount)
+{
+ struct dns_header *odh;
+ u_int16_t outlen;
+
+ odh = (struct dns_header *)reply;
+ outlen = sizeof(struct dns_header);
+
+ /* copy question to reply */
+ memcpy(reply, buf, sizeof(struct dns_header) + q->hdr->namelen + 4);
+ /* blank query */
+ memset((char *)&odh->query, 0, sizeof(u_int16_t));
+
+ outlen += (q->hdr->namelen + 4);
+
+ SET_DNS_REPLY(odh);
+ SET_DNS_AUTHORITATIVE(odh);
+
+ NTOHS(odh->query);
+
+ odh->question = htons(1);
+ odh->answer = htons(answercount);
+ odh->nsrr = 0;
+ odh->additional = 0;
+
+ return (outlen);
+}
+
+
+
+/*
+ * BUILD_SOA - build an SOA answer
+ */
+
+int
+build_soa(DB *db, char *reply, int offset, struct domain *sd, struct question *q)
+{
+ char *p;
+ char *label;
+ char *plabel;
+
+ int labellen;
+ int tmplen;
+ u_int32_t *soa_val;
+
+ struct answer {
+ char name[2];
+ u_int16_t type;
+ u_int16_t class;
+ u_int32_t ttl;
+ u_int16_t rdlength; /* 12 */
+ char rdata;
+ } __attribute__((packed));
+
+ struct answer *answer;
+ answer = (struct answer *)(&reply[offset]);
+
+ answer->name[0] = 0xc0;
+ answer->name[1] = 0x0c;
+ answer->type = htons(DNS_TYPE_SOA);
+ answer->class = htons(DNS_CLASS_IN);
+ answer->ttl = htonl(sd->ttl);
+
+ offset += 12; /* up to rdata length */
+
+ p = (char *)&answer->rdata;
+
+
+ label = sd->soa.nsserver;
+ labellen = sd->soa.nsserver_len;
+
+ plabel = label;
+
+ if (offset + labellen <= 65535)
+ memcpy(&reply[offset], (char *)plabel, labellen);
+ else
+ return (offset); /* XXX */
+
+ offset += labellen;
+
+ /* compress the label if possible */
+ if ((tmplen = compress_label((u_char*)reply, offset, labellen)) > 0) {
+ offset = tmplen;
+ }
+
+ label = sd->soa.responsible_person;
+ labellen = sd->soa.rp_len;
+ plabel = label;
+
+ if (offset + labellen <= 65535)
+ memcpy(&reply[offset], (char *)plabel, labellen);
+ else
+ return (offset); /* XXX */
+
+ offset += labellen;
+
+ /* 2 compress the label if possible */
+
+ if ((tmplen = compress_label((u_char*)reply, offset, labellen)) > 0) {
+ offset = tmplen;
+ }
+
+
+ /* XXX */
+ if ((offset + sizeof(sd->soa.serial)) >= 65535 ) {
+ /* XXX server error reply? */
+ return (offset);
+ }
+ soa_val = (u_int32_t *)&reply[offset];
+ *soa_val = htonl(sd->soa.serial);
+ offset += sizeof(sd->soa.serial); /* XXX */
+
+ /* XXX */
+ if ((offset + sizeof(sd->soa.refresh)) >= 65535 ) {
+ return (offset);
+ }
+ soa_val = (u_int32_t *)&reply[offset];
+ *soa_val = htonl(sd->soa.refresh);
+ offset += sizeof(sd->soa.refresh); /* XXX */
+
+ if ((offset + sizeof(sd->soa.retry)) >= 65535 ) {
+ return (offset);
+ }
+ soa_val = (u_int32_t *)&reply[offset];
+ *soa_val = htonl(sd->soa.retry);
+ offset += sizeof(sd->soa.retry); /* XXX */
+
+ if ((offset + sizeof(sd->soa.expire)) >= 65535 ) {
+ return (offset);
+ }
+ soa_val = (u_int32_t *)&reply[offset];
+ *soa_val = htonl(sd->soa.expire);
+ offset += sizeof(sd->soa.expire);
+
+ if ((offset + sizeof(sd->soa.minttl)) > 65535 ) {
+ return (offset);
+ }
+ soa_val = (u_int32_t *)&reply[offset];
+ *soa_val = htonl(sd->soa.minttl);
+ offset += sizeof(sd->soa.minttl);
+
+ answer->rdlength = htons(&reply[offset] - &answer->rdata);
+
+ return (offset);
+}
+
+int
+checklabel(DB *db, struct domain *sd, struct domain *soa, struct question *q)
+{
+ struct domain tmpsd;
+ char *p;
+ int plen, ret;
+
+ DBT key, data;
+
+ if (memcmp(sd, soa, sizeof(struct domain)) == 0)
+ return 1;
+
+ p = sd->zone;
+ plen = sd->zonelen;
+
+ do {
+ if (*p == '\0')
+ return (0);
+
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+
+ key.data = (char *)p;
+ key.size = plen;
+
+ data.data = NULL;
+ data.size = 0;
+
+ ret = db->get(db, NULL, &key, &data, 0);
+ if (ret == DB_NOTFOUND) {
+ plen -= (*p + 1);
+ p = (p + (*p + 1));
+
+ continue;
+ }
+
+ if (data.size != sizeof(struct domain)) {
+ dolog(LOG_INFO, "AXFR btree db is damaged (%d), drop\n", __LINE__);
+ return (0);
+ }
+
+ memcpy(&tmpsd, data.data, sizeof(tmpsd));
+
+ /*
+ * the encountered label has an SOA before we got to the
+ * root, so we skip this record entirely...
+ */
+
+ if (tmpsd.flags & DOMAIN_HAVE_SOA)
+ return (0);
+
+
+ /*
+ * and check the next label...
+ */
+
+ plen -= (*p + 1);
+ p = (p + (*p + 1));
+
+
+ } while (memcmp(p, q->hdr->name, q->hdr->namelen) != 0);
+
+
+ return (1);
+}
+
+void
+gather_notifydomains(DB *db)
+{
+ DBT key, data;
+ DBC *cursor;
+
+ time_t now, soatime;
+ struct tm *tm;
+
+ char timestring[128];
+ char buf[128];
+
+ struct domain *sd;
+
+
+ SLIST_INIT(&notifyhead);
+
+ now = time(NULL);
+ tm = localtime(&now);
+ if (tm != NULL)
+ strftime(timestring, sizeof(timestring), "%Y%m%d", tm);
+ else
+ snprintf(timestring, sizeof(timestring), "19700101");
+
+ now = time(NULL);
+
+ if (db->cursor(db, NULL, &cursor, 0) != 0) {
+ dolog(LOG_INFO, "db->cursor: %s\n", strerror(errno));
+ return;
+ }
+
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+
+
+ if (cursor->c_get(cursor, &key, &data, DB_FIRST) != 0) {
+ dolog(LOG_INFO, "cursor->c_get: %s\n", strerror(errno));
+ cursor->c_close(cursor);
+ return;
+ }
+
+ do {
+ if (data.size != sizeof(struct domain)) {
+ dolog(LOG_INFO, "btree db is damaged\n");
+ cursor->c_close(cursor);
+ return;
+ }
+
+ sd = (struct domain *)data.data;
+
+ if ((sd->flags & DOMAIN_HAVE_SOA) == DOMAIN_HAVE_SOA) {
+ notn2 = malloc(sizeof(struct notifyentry));
+ if (notn2 == NULL) {
+ continue;
+ }
+
+ notn2->ids = calloc(notify, sizeof(u_int16_t));
+ if (notn2->ids == NULL) {
+ free(notn2);
+ continue;
+ }
+
+ notn2->attempts = calloc(notify, sizeof(u_int16_t));
+ if (notn2->attempts == NULL) {
+ free(notn2);
+ continue;
+ }
+
+ memcpy(notn2->domain, sd->zone, sd->zonelen);
+ notn2->domainlen = sd->zonelen;
+
+ soatime = (time_t)sd->soa.serial;
+ snprintf(buf, sizeof(buf), "%u", sd->soa.serial);
+
+ if (strncmp(buf, timestring, strlen(timestring)) == 0) {
+ dolog(LOG_INFO, "inserting zone \"%s\" for notification...\n", sd->zonename);
+ SLIST_INSERT_HEAD(&notifyhead, notn2, notify_entry);
+ } else if (difftime(now, soatime) < 1800 && difftime(now, soatime) > 0) {
+ dolog(LOG_INFO, "2 inserting zone \"%s\" for notification...\n", sd->zonename);
+ SLIST_INSERT_HEAD(&notifyhead, notn2, notify_entry);
+ } else {
+#if 0
+ dolog(LOG_INFO, "SOA serial for zone \"%s\" did not make sense (%s), not notifying\n", sd->zonename, buf);
+#endif
+ free(notn2);
+ }
+ }
+
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+ } while (cursor->c_get(cursor, &key, &data, DB_NEXT) == 0);
+
+ cursor->c_close(cursor);
+
+ return;
+}
+
+void
+notifyslaves(int *notifyfd)
+{
+ int so;
+ int i;
+
+ i = 0;
+ SLIST_FOREACH(nfslnp, &notifyslavehead, notifyslave_entry) {
+ if (nfslnp->family == AF_INET6) {
+ so = notifyfd[1];
+ } else {
+ so = notifyfd[0];
+ }
+#if 0
+ dolog(LOG_INFO, "notifying %s...\n", nfslnp->name);
+#endif
+
+#ifdef __linux__
+ SLIST_FOREACH(notnp, &notifyhead, notify_entry) {
+#else
+ SLIST_FOREACH_SAFE(notnp, &notifyhead, notify_entry, notn2) {
+#endif
+ notnp->ids[i] = arc4random() & 0xffff;
+ notnp->attempts[i]++;
+ if (notnp->attempts[i] > 10) {
+ dolog(LOG_INFO, "notify entry removed due to timeout\n");
+ SLIST_REMOVE(&notifyhead, notnp, notifyentry, notify_entry);
+ }
+
+ notifypacket(so, nfslnp, notnp, i);
+ }
+
+ i++;
+ }
+
+ return;
+}
+
+void
+notifypacket(int so, void *vnse, void *vnotnp, int packetcount)
+{
+ struct notifyslaveentry *nse = (struct notifyslaveentry *)vnse;
+ struct notifyentry *notnp = (struct notifyentry *)vnotnp;
+ struct sockaddr_in bsin, *sin;
+ struct sockaddr_in6 bsin6, *sin6;
+ char packet[512];
+ char *questionname;
+ u_int16_t *classtype;
+ struct dns_header *dnh;
+ int outlen = 0, slen, ret;
+
+ memset(&packet, 0, sizeof(packet));
+ dnh = (struct dns_header *)&packet[0];
+
+ dnh->id = htons(notnp->ids[packetcount]);
+ SET_DNS_NOTIFY(dnh);
+ SET_DNS_AUTHORITATIVE(dnh);
+ SET_DNS_QUERY(dnh);
+ HTONS(dnh->query);
+
+ dnh->question = htons(1);
+
+ outlen += sizeof(struct dns_header);
+ questionname = (char *)&packet[outlen];
+
+ memcpy(questionname, notnp->domain, notnp->domainlen);
+ outlen += notnp->domainlen;
+
+ classtype = (u_int16_t *)&packet[outlen];
+ classtype[0] = htons(DNS_TYPE_SOA);
+ classtype[1] = htons(DNS_CLASS_IN);
+
+ outlen += (2 * sizeof(u_int16_t));
+
+ if (nse->family == AF_INET) {
+ slen = sizeof(struct sockaddr_in);
+ sin = (struct sockaddr_in *)&nse->hostmask;
+ memset(&bsin, 0, sizeof(bsin));
+ bsin.sin_family = AF_INET;
+ bsin.sin_port = htons(53);
+ bsin.sin_addr.s_addr = sin->sin_addr.s_addr;
+
+ ret = sendto(so, packet, outlen, 0, (struct sockaddr *)&bsin, slen);
+ } else {
+ slen = sizeof(struct sockaddr_in6);
+ sin6 = (struct sockaddr_in6 *)&nse->hostmask;
+ memset(&bsin6, 0, sizeof(bsin6));
+ bsin6.sin6_family = AF_INET6;
+ bsin6.sin6_port = htons(53);
+ memcpy(&bsin6.sin6_addr, &sin6->sin6_addr, 16);
+
+ ret = sendto(so, packet, outlen, 0, (struct sockaddr *)sin6, slen);
+ }
+
+ if (ret < 0) {
+ dolog(LOG_INFO, "sendto: %s\n", strerror(errno));
+ }
+
+ return;
+}
blob - /dev/null
blob + a847cd1a14e85a3f1e1ad7938778ab11fc9b81e8 (mode 755)
--- /dev/null
+++ configure
@@ -0,0 +1,87 @@
+#!/bin/sh
+
+
+echo "/* config.h - autogenerated by configure */" > config.h
+
+until [ -z $1 ]
+do
+ case $1 in
+ --user=*)
+ user=`echo $1 | sed -e 's/--user=//g'`
+ echo "#define DEFAULT_PRIVILEGE \"$user\"" >> config.h
+ ;;
+ *)
+ echo "usage: configure [--user=USERNAME]"
+ exit
+ ;;
+ esac
+ shift
+done
+
+openbsd() {
+ echo configuring for OpenBSD
+ cp Makefile.openbsd Makefile
+}
+
+netbsd() {
+ echo configuring for NetBSD
+ cp Makefile.netbsd Makefile
+}
+
+freebsd() {
+ echo configuring for FreeBSD
+ cp Makefile.freebsd Makefile
+}
+
+linux() {
+ echo configuring for Linux
+ cp Makefile.linux Makefile
+
+ # libressl
+
+ if [ ! -d ../libressl-2.1.1 ] ; then
+ echo Please install libressl-2.1.1 in the directory before this one!
+ echo You can get it from http://ftp.openbsd.org/pub/OpenBSD/LibreSSL/
+ echo "Please extract it and ./configure && make check, this will not "
+ echo install it into your system only into that directory.
+ echo
+ echo Due to Linux distros not supporting libressl yet, we have to
+ echo make it this way, hopeing for someone making inroads one day!
+ echo cleaning up...
+ rm -f Makefile
+ exit 1
+ else
+ echo Now making a symlink for libcrypto and libssl...
+ ln -s ../libressl-2.1.1/crypto/.libs/libcrypto.a libressllibcrypto.a
+ ln -s ../libressl-2.1.1/crypto/.libs/libcompat.a libressllibcompat.a
+ ln -s ../libressl-2.1.1/ssl/.libs/libssl.a libressllibssl.a
+ ln -s ../libressl-2.1.1/crypto/.libs/libcompatnoopt.a libressllibcompatnoopt.a
+ echo done.
+ fi
+}
+
+darwin() {
+ echo configuring for Darwin
+ cp Makefile.macosx Makefile
+}
+
+
+case "`uname -s`" in
+Darwin)
+ darwin;
+ ;;
+OpenBSD)
+ openbsd;
+ ;;
+FreeBSD)
+ freebsd
+ ;;
+NetBSD)
+ netbsd;
+ ;;
+Linux)
+ linux
+ ;;
+esac
+
+echo done. Type make to make programs.
blob - /dev/null
blob + da3f0499f4ada9864c5345a2b987cc416e510163 (mode 644)
--- /dev/null
+++ db.h
@@ -0,0 +1,244 @@
+/*
+ * Copyright (c) 2005-2014 Peter J. Philipp
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+#ifndef _DB_H
+#define _DB_H
+
+#define CONFFILE "/etc/wildcarddns.conf"
+#define DEFAULT_SOCKET 64
+
+#define ERR_DROP 0x1
+#define ERR_NXDOMAIN 0x2
+#define ERR_NOERROR 0x4
+
+#define RECORD_COUNT 20
+#define NEGATIVE_CACHE_TIME 600 /* DNS & Bind 3rd edition page 35 */
+
+/* db stuff */
+
+struct soa {
+ char nsserver[DNS_MAXNAME];
+ u_int8_t nsserver_len;
+ char responsible_person[DNS_MAXNAME];
+ u_int8_t rp_len;
+ u_int32_t serial;
+ u_int32_t refresh;
+ u_int32_t retry;
+ u_int32_t expire;
+ u_int32_t minttl;
+} __attribute__((packed));
+
+struct smx {
+ u_int16_t preference; /* MX preference */
+ char exchange[DNS_MAXNAME]; /* name of exchange server */
+ int exchangelen; /* length of exchange server name */
+} __attribute__((packed));
+
+struct ns {
+ char nsserver[DNS_MAXNAME]; /* NS name */
+ int nslen; /* length of NS */
+} __attribute__((packed));
+
+struct srv {
+ u_int16_t priority; /* SRV 16 bit priority */
+ u_int16_t weight; /* 16 bit weight */
+ u_int16_t port; /* 16 bit port */
+ char target[DNS_MAXNAME]; /* SRV target name */
+ int targetlen; /* SRV target name length */
+} __attribute__((packed));
+
+struct sshfp {
+ u_int8_t algorithm; /* SSHFP algorithm */
+ u_int8_t fptype; /* SSHFP fingerprint type */
+ char fingerprint[DNS_MAXNAME]; /* fingerprint */
+ int fplen; /* fingerprint length */
+} __attribute__((packed));
+
+struct naptr {
+ u_int16_t order; /* NAPTR 16 bit order */
+ u_int16_t preference; /* 16 bit preference */
+ char flags[DNS_MAXNAME]; /* flags 255 bytes */
+ int flagslen; /* flags length */
+ char services[DNS_MAXNAME]; /* services */
+ int serviceslen; /* services length */
+ char regexp[DNS_MAXNAME]; /* regexp */
+ int regexplen; /* regexp len */
+ char replacement[DNS_MAXNAME]; /* replacement this is a domain */
+ int replacementlen;
+} __attribute__((packed));
+
+struct domain {
+ char zone[DNS_MAXNAME]; /* name of zone in dns name format */
+ int zonelen; /* length of zone, above */
+ char zonename[DNS_MAXNAME + 1]; /* name of zone in human readable */
+ u_int32_t flags; /* flags of zone */
+#define DOMAIN_HAVE_A 0x1
+#define DOMAIN_HAVE_SOA 0x2
+#define DOMAIN_HAVE_CNAME 0x4
+#define DOMAIN_HAVE_PTR 0x8
+#define DOMAIN_HAVE_MX 0x10
+#define DOMAIN_HAVE_AAAA 0x20
+#define DOMAIN_HAVE_NS 0x40
+#define DOMAIN_HAVE_TXT 0x80
+#define DOMAIN_STATIC_ZONE 0x100
+#define DOMAIN_NEGATIVE_CACHE 0x200
+#define DOMAIN_HAVE_SRV 0x400
+#define DOMAIN_HAVE_SPF 0x800
+#define DOMAIN_HAVE_SSHFP 0x1000
+#define DOMAIN_HAVE_NAPTR 0x2000
+ struct soa soa; /* start of authority */
+ u_int32_t ttl; /* time to live */
+ time_t created; /* time created, for dynamic zones */
+ in_addr_t a[RECORD_COUNT]; /* IP addresses */
+ u_int8_t region[RECORD_COUNT]; /* region of IP address */
+ int a_count; /* IP address count (max 10) */
+ int a_ptr; /* pointer to last used address */
+ struct in6_addr aaaa[RECORD_COUNT]; /* IPv6 addresses */
+ int aaaa_count; /* IPv6 address count (max 10) */
+ int aaaa_ptr; /* pointer to last used IPv6 address */
+ struct smx mx[RECORD_COUNT]; /* MX addresses */
+ int mx_count; /* MX address count, max 10 */
+ int mx_ptr; /* pointer to last used MX adddress */
+ struct ns ns[RECORD_COUNT]; /* NS resource records (max 10) */
+ int ns_count; /* count of NS records, (max 10) */
+ int ns_ptr; /* pointer to last used NS address */
+ int ns_type; /* set if it's a delegation */
+#define NS_TYPE_DELEGATE 0x1
+#define NS_TYPE_HINT 0x2
+ char cname[DNS_MAXNAME]; /* CNAME RR */
+ int cnamelen; /* len of CNAME */
+ char ptr[DNS_MAXNAME]; /* PTR RR */
+ int ptrlen; /* len of PTR */
+ char txt[DNS_MAXNAME]; /* TXT string */
+ int txtlen; /* len of TXT */
+ char spf[DNS_MAXNAME]; /* SPF string */
+ int spflen; /* len of SPF */
+ struct srv srv[RECORD_COUNT]; /* SRV resource record */
+ int srv_count; /* count of SRV RR */
+ struct sshfp sshfp[RECORD_COUNT]; /* SSHFP resource record */
+ int sshfp_count; /* SSHFP RR count */
+ struct naptr naptr[RECORD_COUNT]; /* NAPTR RR, eek 20K! */
+ int naptr_count;
+} __attribute__((packed));
+
+struct sreply {
+ int so; /* socket */
+ char *buf; /* question packet */
+ int len; /* question packet length */
+ struct question *q; /* struct question */
+ struct sockaddr *sa; /* struct sockaddr of question */
+ int salen; /* length of struct sockaddr */
+ struct domain *sd1; /* first resolved domain */
+ struct domain *sd2; /* CNAME to second resolved domain */
+ u_int8_t region; /* region of question */
+ int istcp; /* when set it's tcp */
+ int wildcard; /* wildcarding boolean */
+ struct recurses *sr; /* recurses struct for raw sockets */
+ char *replybuf; /* reply buffer */
+};
+
+struct srecurseheader {
+ int af; /* address family */
+ int proto; /* protocol UDP/TCP */
+ struct sockaddr_storage source; /* source + port */
+ struct sockaddr_storage dest; /* dest + port */
+ int len; /* length of question */
+ char buf[512]; /* question buffer */
+};
+
+
+SLIST_HEAD(listhead2, recurses) recurseshead;
+
+struct recurses {
+ char query[512]; /* the query we received */
+ int len; /* length of query */
+
+ int isfake; /* received or faked */
+ int launched; /* is launched */
+ int replied; /* we replied to this question */
+ int packetcount; /* packet count of requests */
+ int af; /* address family */
+ int proto; /* protocol UDP/TCP */
+ struct sockaddr_storage source; /* source + port */
+ struct sockaddr_storage dest; /* dest + port */
+
+ time_t received; /* received request time */
+ time_t sent_last_query; /* the last time we did a lookup */
+
+ char upperlower[32]; /* uppercase / lowercase bitmap */
+ int so; /* the socket we did a lookup with */
+ u_short port; /* port used on outgoing */
+ u_int16_t id; /* last id used */
+
+ /* the below get loaded from the database upon each lookup */
+ in_addr_t a[RECORD_COUNT]; /* IPv4 addresses of nameservers */
+ int a_count; /* IPv4 address count */
+ int a_ptr; /* pointer to last used address */
+ struct in6_addr aaaa[RECORD_COUNT]; /* IPv6 addresses of nameservers */
+ int aaaa_count; /* IPv6 address count */
+ int aaaa_ptr; /* pointer to last used IPv6 address */
+
+ /* the below is our indicator which part of the lookup we're at */
+
+ u_char *lookrecord; /* what zone lookup is it from */
+ int indicator; /* indicator of ns lookup */
+ int authoritative; /* last reply was authoritative, type */
+ int hascallback; /* some request has callback don't remove */
+
+ struct question *question; /* question struct */
+ SLIST_ENTRY(recurses) recurses_entry;
+ struct recurses *callback; /* callback */
+} *sr, *sr1, *sr2;
+
+struct logging {
+ int active;
+ char *hostname;
+ int bind;
+ char *loghost;
+ struct sockaddr_storage loghost2;
+ char *logport;
+ u_int16_t logport2;
+ char *logpasswd;
+};
+
+struct cfg {
+ int udp[DEFAULT_SOCKET]; /* udp sockets */
+ int tcp[DEFAULT_SOCKET]; /* tcp socket */
+ int axfr[DEFAULT_SOCKET]; /* axfr udp socket */
+ char *ident[DEFAULT_SOCKET]; /* identification of interface */
+ int recurse; /* recurse socket */
+ int log; /* logging socket */
+ int sockcount; /* set sockets */
+ DB *db; /* database */
+};
+
+
+int parse_file(DB *db, char *);
+DB * opendatabase(DB *);
+
+
+#endif /* _DB_H */
blob - /dev/null
blob + d90ddfbced77977c424f72768aead98442db91cd (mode 644)
--- /dev/null
+++ delphinusdns.conf
@@ -0,0 +1,153 @@
+; sample config file that is in production.
+;
+version "6";
+
+; this is for the host dione.centroid.eu which is in Panama
+; it serves best for the Americas and Australia (?)
+
+region "LACNIC" {
+ ;
+ ; ARIN and LACNIC blocks below
+ ;
+ ; lacnic netblocks
+ 186.0.0.0/8;
+ 187.0.0.0/8;
+ 189.0.0.0/8;
+ 190.0.0.0/8;
+ 191.0.0.0/8;
+ 200.0.0.0/8;
+ 201.0.0.0/8;
+ ; arin netblocks
+ 216.0.0.0/8;
+ 209.0.0.0/8;
+ 208.0.0.0/8;
+ 207.0.0.0/8;
+ 206.0.0.0/8;
+ 205.0.0.0/8;
+ 204.0.0.0/8;
+ 199.0.0.0/8;
+ 198.0.0.0/8;
+ 192.0.0.0/8;
+ 184.0.0.0/8;
+ 173.0.0.0/8;
+ 172.0.0.0/8;
+ 170.0.0.0/8;
+ 169.0.0.0/8;
+ 168.0.0.0/8;
+ 167.0.0.0/8;
+ 166.0.0.0/8;
+ 165.0.0.0/8;
+ 164.0.0.0/8;
+ 162.0.0.0/8;
+ 161.0.0.0/8;
+ 160.0.0.0/8;
+ 159.0.0.0/8;
+ 158.0.0.0/8;
+ 157.0.0.0/8;
+ 156.0.0.0/8;
+ 155.0.0.0/8;
+ 152.0.0.0/8;
+ 149.0.0.0/8;
+ 148.0.0.0/8;
+ 147.0.0.0/8;
+ 146.0.0.0/8;
+ 144.0.0.0/8;
+ 143.0.0.0/8;
+ 140.0.0.0/8;
+ 139.0.0.0/8;
+ 138.0.0.0/8;
+ 137.0.0.0/8;
+ 136.0.0.0/8;
+ 135.0.0.0/8;
+ 134.0.0.0/8;
+ 132.0.0.0/8;
+ 131.0.0.0/8;
+ 130.0.0.0/8;
+ 129.0.0.0/8;
+ 128.0.0.0/8;
+ 108.0.0.0/8;
+ 99.0.0.0/8;
+ 98.0.0.0/8;
+ 97.0.0.0/8;
+ 96.0.0.0/8;
+ 76.0.0.0/8;
+ 75.0.0.0/8;
+ 74.0.0.0/8;
+ 73.0.0.0/8;
+ 72.0.0.0/8;
+ 71.0.0.0/8;
+ 70.0.0.0/8;
+ 69.0.0.0/8;
+ 68.0.0.0/8;
+ 67.0.0.0/8;
+ 66.0.0.0/8;
+ 65.0.0.0/8;
+ 64.0.0.0/8;
+ 63.0.0.0/8;
+ 24.0.0.0/8;
+ 7.0.0.0/8;
+ ; Australia likes going through the US, LACNIC is closer
+ ; these two blocks are just some of many
+ 202.0.0.0/8;
+ 203.0.0.0/8;
+
+}
+;
+; This region is served by proteus.solarscale.de which resides in RIPE
+; it serves regions RIPE, AFRINIC and APNIC although the traceroutes
+; from China seem to indicate as much latency for proteus as there is
+; for dione
+;
+region "RIPE" {
+ ;
+ ; for RIPE we'll just put the prefix 0.0.0.0/0
+ ; this should work because the higher the prefix the more
+ ; precedence the region has, let's give it a shot.
+ ;
+
+ 0.0.0.0/0;
+ 192.168.0.0/16;
+ 147.102.0.0/16;
+}
+zone "centroid.eu" {
+ centroid.eu,soa,3600,uranus.centroid.eu.,pjp.solarscale.de.,1258740680,3600,1800,7200,3600
+ centroid.eu,ns,3600,proteus.solarscale.de.
+ centroid.eu,ns,3600,uranus.centroid.eu.
+ centroid.eu,ns,3600,dione.solarscale.de.
+ ; balance these two
+ centroid.eu,balance,3600,62.75.160.180
+ centroid.eu,balance,3600,200.46.208.61
+ ;
+ centroid.eu,mx,3600,10,proteus.solarscale.de.
+ centroid.eu,aaaa,3600,2001:a60:f074::8
+ ; and these two
+ www.centroid.eu,balance,3600,62.75.160.180
+ www.centroid.eu,balance,3600,200.46.208.61
+ ;
+ www.centroid.eu,aaaa,3600,2001:a60:f074::8
+ uranus.centroid.eu,a,3600,212.114.251.91
+ uranus.centroid.eu,aaaa,3600,2001:a60:f000:99::2
+ proteus.centroid.eu,a,3600,62.75.160.180
+ dione.centroid.eu,a,3600,200.46.208.61
+ rosalind.centroid.eu,aaaa,3600,2001:a60:f074::8
+ irc.centroid.eu,a,3600,212.114.251.91
+ irc.centroid.eu,aaaa,3600,2001:a60:f074::24
+ irc2.centroid.eu,a,3600,200.46.208.61
+ ipv6.centroid.eu,soa,3600,uranus.centroid.eu.,pjp.solarscale.de.,1244725285,3600,1800,7200,3600
+ ipv6.centroid.eu,aaaa,3600,2001:a60:f074::8
+ www.ipv6.centroid.eu,aaaa,3600,2001:a60:f074::8
+ localhost.centroid.eu,a,3600,127.0.0.1
+ localhost.centroid.eu,aaaa,3600,::1
+}
+zone "ipv6.solarscale.de" {
+ ipv6.solarscale.de,soa,3600,proteus.solarscale.de.,pjp.solarscale.de.,1234896563,3600,1800,7200,3600
+ ipv6.solarscale.de,ns,3600,proteus.solarscale.de.
+ ipv6.solarscale.de,ns,3600,uranus.centroid.eu.
+ ipv6.solarscale.de,aaaa,3600,2001:a60:f074::8
+ www.ipv6.solarscale.de,aaaa,3600,2001:a60:f074::8
+}
+zone "reverse dns" {
+ 0.4.7.0.f.0.6.a.0.1.0.0.2.ip6.arpa.,soa,3600,proteus.solarscale.de.,pjp.solarscale.de.,1234896562,3600,1800,7200,3600
+ 1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.4.7.0.f.0.6.a.0.1.0.0.2.ip6.arpa.,ptr,3600,uranus.centroid.eu.
+ 2.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.4.7.0.f.0.6.a.0.1.0.0.2.ip6.arpa.,ptr,3600,setebos.centroid.eu.
+}
blob - /dev/null
blob + aa3275d931b6d71f20bc9275d6bffe5225a9542c (mode 644)
--- /dev/null
+++ delphinusdns.conf.5
@@ -0,0 +1,249 @@
+.\" Copyright (c) 2014 Peter J. Philipp
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. The name of the author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.Dd November 14, 2014
+.Dt DELPHINUSDNS.CONF 5
+.Os
+.Sh NAME
+.Nm delphinusdns.conf
+.Nd the delphinus DNS daemon configuration file
+.Sh DESCRIPTION
+The
+.Xr delphinusdnsd 8
+has a default configfile it is called
+.Nm
+and it is found in /etc. You may wonder why there is a missing d, so I ask
+you to look up
+.Xr syslogd 8
+and
+.Xr syslog.conf 5
+which also drop the d in the config file. This follows that name convention.
+.Sh EXAMPLES
+Here is a sample
+.Nm
+file:
+.Bd -literal
+; sample config file that is in production.
+;
+version "6";
+options "cool stuff" {
+ interface "lo0";
+ interface "em0";
+
+ ;bind 127.0.0.1;
+ ;bind 192.168.34.4;
+
+ ratelimit-pps 6;
+
+ port 53;
+
+ ;fork 2;
+ log;
+}
+
+axfrport "10053";
+
+axfr-for "these hosts" {
+ 127.0.0.1;
+ ::1;
+ 192.168.0.0/16;
+}
+
+notify "these hosts" {
+ 192.168.34.1;
+ 192.168.35.1;
+ ::1;
+}
+
+
+zone "centroid.eu" {
+ centroid.eu,soa,3600,uranus.centroid.eu.,pjp.solarscale.de.,2014051701,3600,1800,7200,3600
+ centroid.eu,ns,3600,proteus.solarscale.de.
+ centroid.eu,ns,3600,uranus.centroid.eu.
+ centroid.eu,ns,3600,dione.solarscale.de.
+ ;
+ centroid.eu,a,3600,200.46.208.61
+ centroid.eu,a,3600,62.75.160.180
+ ;
+ centroid.eu,SSHFP,3600,1,1,"9b3624f96a6766e73dc88505f945a564dee82e59"
+ centroid.eu,SSHFP,3600,1,2,"03310030886404f8f61d2e4dc445412788284ab04d0858bfa375eb80fc0d34f6"
+ centroid.eu,SSHFP,3600,2,1,"3de1bbae4de5ebd3571a169a50d6a5a4145dd7a9"
+ centroid.eu,SSHFP,3600,2,2,"bbd96ff7e7f629377d655d7fa9662502837b66cf9a2d24590df77acef66fe405"
+ centroid.eu,SSHFP,3600,3,1,"0ca455de4800c0f6c681e805e7bd86e511efb1fc"
+ centroid.eu,SSHFP,3600,3,2,"b330fd2288cf9e13c9ef68b1b0c0b20a10c16cc55f03cd3869cf1a49f06bed43"
+ ;
+ centroid.eu,mx,3600,10,proteus.solarscale.de.
+ centroid.eu,aaaa,3600,2001:a60:f074::8
+ ;
+ centroid.eu,spf,3600,"v=spf1 ip4:200.46.208.61 ~all"
+ ;
+ ;
+ ; NAPTR trial
+ centroid.eu,naptr,3600,10,10,"u","E2U+sip","!^.*$!sip:1234@sip.example.com!",centroid.eu
+ centroid.eu,naptr,3600,10,20,"u","E2U+sip","!^.*$!sip:1234@sip.example.com!",.
+
+ ;
+ www.centroid.eu,a,3600,62.75.160.180
+ www.centroid.eu,a,3600,200.46.208.61
+ ;
+ www.centroid.eu,aaaa,3600,2001:a60:f074::8
+ uranus.centroid.eu,a,3600,212.114.251.91
+ uranus.centroid.eu,aaaa,3600,2001:a60:f000:99::2
+ proteus.centroid.eu,a,3600,62.75.160.180
+ dione.centroid.eu,a,3600,200.46.208.61
+ rosalind.centroid.eu,aaaa,3600,2001:a60:f074::8
+ irc.centroid.eu,a,3600,212.114.251.91
+ irc.centroid.eu,aaaa,3600,2001:a60:f074::24
+ irc2.centroid.eu,a,3600,200.46.208.61
+ localhost.centroid.eu,a,3600,127.0.0.1
+ localhost.centroid.eu,aaaa,3600,::1
+ ; the below are new delegations (NS) records
+ ;delegation.centroid.eu,delegate,3600,ns1.server.com.
+ ; glue record
+ ;ns1.server.com.,a,3600,10.0.0.1
+ ;delegation.centroid.eu,delegate,3600,ns2.server.com.
+ ; glue record
+ ;ns2.server.com.,a,3600,10.0.0.2
+}
+zone "ipv6.centroid.eu" {
+ ipv6.centroid.eu,soa,3600,uranus.centroid.eu.,pjp.solarscale.de.,1244725285,3600,1800,7200,3600
+ ipv6.centroid.eu,aaaa,3600,2001:a60:f074::8
+ www.ipv6.centroid.eu,aaaa,3600,2001:a60:f074::8
+}
+zone "ipv6.solarscale.de" {
+ ipv6.solarscale.de,soa,3600,proteus.solarscale.de.,pjp.solarscale.de.,1234896563,3600,1800,7200,3600
+ ipv6.solarscale.de,ns,3600,proteus.solarscale.de.
+ ipv6.solarscale.de,ns,3600,uranus.centroid.eu.
+ ipv6.solarscale.de,aaaa,3600,2001:a60:f074::8
+ www.ipv6.solarscale.de,aaaa,3600,2001:a60:f074::8
+}
+zone "reverse dns" {
+ 0.4.7.0.f.0.6.a.0.1.0.0.2.ip6.arpa.,soa,3600,proteus.solarscale.de.,pjp.solarscale.de.,1234896562,3600,1800,7200,3600
+ 1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.4.7.0.f.0.6.a.0.1.0.0.2.ip6.arpa.,ptr,3600,uranus.centroid.eu.
+ 2.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.4.7.0.f.0.6.a.0.1.0.0.2.ip6.arpa.,ptr,3600,setebos.centroid.eu.
+}
+
+zone "root hints" {
+ .,hint,3600000,A.ROOT-SERVERS.NET.
+ A.ROOT-SERVERS.NET.,a,3600000,198.41.0.4
+ A.ROOT-SERVERS.NET.,aaaa,3600000,2001:503:BA3E::2:30
+ .,hint,3600000,B.ROOT-SERVERS.NET.
+ B.ROOT-SERVERS.NET.,a,3600000,192.228.79.201
+ .,hint,3600000,C.ROOT-SERVERS.NET.
+ C.ROOT-SERVERS.NET.,a,3600000,192.33.4.12
+ .,hint,3600000,D.ROOT-SERVERS.NET.
+ D.ROOT-SERVERS.NET.,a,3600000,199.7.91.13
+ D.ROOT-SERVERS.NET.,aaaa,3600000,2001:500:2D::D
+ .,hint,3600000,E.ROOT-SERVERS.NET.
+ E.ROOT-SERVERS.NET.,a,3600000,192.203.230.10
+ .,hint,3600000,F.ROOT-SERVERS.NET.
+ F.ROOT-SERVERS.NET.,a,3600000,192.5.5.241
+ F.ROOT-SERVERS.NET.,aaaa,3600000,2001:500:2f::f
+ .,hint,3600000,G.ROOT-SERVERS.NET.
+ G.ROOT-SERVERS.NET.,a,3600000,192.112.36.4
+ .,hint,3600000,H.ROOT-SERVERS.NET.
+ H.ROOT-SERVERS.NET.,a,3600000,128.63.2.53
+ H.ROOT-SERVERS.NET.,aaaa,3600000,2001:500:1::803f:235
+ .,hint,3600000,I.ROOT-SERVERS.NET.
+ I.ROOT-SERVERS.NET.,a,3600000,192.36.148.17
+ .,hint,3600000,J.ROOT-SERVERS.NET.
+ J.ROOT-SERVERS.NET.,a,3600000,192.58.128.30
+ J.ROOT-SERVERS.NET.,aaaa,3600000,2001:503:C27::2:30
+ .,hint,3600000,K.ROOT-SERVERS.NET.
+ K.ROOT-SERVERS.NET.,a,3600000,193.0.14.129
+ K.ROOT-SERVERS.NET.,aaaa,3600000,2001:7fd::1
+ .,hint,3600000,L.ROOT-SERVERS.NET.
+ L.ROOT-SERVERS.NET.,a,3600000,199.7.83.42
+ L.ROOT-SERVERS.NET.,aaaa,3600000,2001:500:3::42
+ .,hint,3600000,M.ROOT-SERVERS.NET.
+ M.ROOT-SERVERS.NET.,a,3600000,202.12.27.33
+ M.ROOT-SERVERS.NET.,aaaa,3600000,2001:dc3::35
+}
+
+.Ed
+.Sh GRAMMAR
+Syntax for
+.Nm
+in BNF:
+.Bd -literal
+line = ( version | axfrport | include | zone | region |
+ wof | axfr | notify | whitelist | filter | recurse | logging
+ | comment | options )
+
+version = "version" ("number") semicolon
+
+axfrport = "axfrport" ("port number") semicolon
+
+include = "include" ("filename") semicolon
+
+zone = "zone" ("string") [ "{" zonedata "}" ]
+zonedata = { [hostname] [ "," dnstype] [ "," ttl ] ["," variablednsdata] }
+hostname = string
+dnstype = ( "a" | "aaaa" | "ptr" | "mx" | "soa" | "ns" | "sshfp" | "spf" |
+ "naptr" | "txt" | "delegate" | "hint" )
+ttl = number
+
+region = "region" ("string") [ "{" cidrlist "}"
+
+cidrlist = { [ cidr-address ] semicolon ... }
+
+wof = "wildcard-only-for" ("string") [ "{" cidrlist "}" ]
+
+axfr = "axfr-for" ("string") [ "{" cidrlist "}" ]
+
+notify = "notify" ("string") [ "{" cidrlist "}" ]
+
+whitelist = "whitelist" ("string") [ "{" cidrlist "}" ]
+
+filter = "filter" ("string") [ "{" cidrlist "}" ]
+
+recurse = "recurse-for" ("string") [ "{" cidrlist "}" ]
+
+logging = "logging" ("string") [ "{" logstatements "}" ]
+logstatements = ( "logbind" | "logpasswd" | "logport" | "loghost" )
+logbind = "logbind" ("string") semicolon
+logpasswd = "logpasswd" ("string") semicolon
+logport = "logport" number semicolon
+loghost = "loghost" cidr-address semicolon
+
+comment = ( semicolon | pound ) line
+
+options = "options" ("string") [ "{" optionlist "}" ]
+optionlist = ( optrecurse | optinterface | optfork | optport |
+ optratelimit | optbind )
+optrecurse = "recurse" semicolon
+optinterface = "interface" ("string") semicolon
+optfork = "fork" number semicolon
+optratelimit = "ratelimit-pps" number semicolon
+optbind = "bind" cidr-address semicolon
+.Ed
+.Sh FILES
+.Pa /etc/delphinusdns.conf
+.Sh SEE ALSO
+.Xr delphinusdnsd 8
+.Sh AUTHORS
+This software and manual was written by
+.An Peter J. Philipp Aq pjp@centroid.eu
+
blob - /dev/null
blob + d0bf75038fe6a59154452b3c02fb9e8ffd6fe6df (mode 644)
--- /dev/null
+++ delphinusdnsd.8
@@ -0,0 +1,132 @@
+.\" Copyright (c) 2005-2014 Peter J. Philipp
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. The name of the author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.Dd November 14, 2014
+.Dt DELPHINUSDNSD 8
+.Os
+.Sh NAME
+.Nm delphinusdnsd
+.Nd the Wildcard DNS daemon
+.Sh SYNOPSIS
+.Nm delphinusdnsd
+.Op Fl b Ar address
+.Op Fl c Ar size
+.Op Fl d
+.Op Fl f Ar file
+.Op Fl i Ar interface
+.Op Fl l
+.Op Fl n Ar instances
+.Op Fl p Ar port
+.Op Fl r
+.Op Fl v
+.Sh DESCRIPTION
+.Nm
+is the daemon that runs Wildcard DNS.
+.Pp
+The options are as follows:
+.Pp
+.Bl -tag -width Ds
+.It Xo
+.Fl b
+.Ar address
+.Xc
+Adds an
+.Ar address
+that
+.Nm
+should
+.Xr bind 2
+to. This option can be repeated multiple times to add more. May
+not be used in conjunction with
+.Fl i .
+.It Xo
+.Fl c
+.Ar size
+Specifies a size in bytes for the SYSV shared memory cache that DB4 is
+using.
+.It Xo
+.Fl d
+Turn on debug mode.
+.Nm
+does not fork into the background with this. Messages are printed in the
+foreground.
+.It Xo
+.Fl f
+.Ar file
+.Xc
+The configuration file to be used is found at
+.Ar file .
+.It Xo
+.Fl i
+.Ar interface
+.Xc
+The particular
+.Ar interface
+that
+.Nm
+should listen on. This option can be specified multiple times. May not be
+used in conjunction with
+.Fl b .
+.It Fl l
+Turn logging of queries on.
+.It Xo
+.Fl n
+.Ar instances
+is the number of instances plus one that should be forked for
+.Nm .
+This may be good for multi-cpu/core boxes.
+.It Xo
+.Fl p
+.Ar port
+.Xc
+Specifies the
+.Ar port
+that
+.Nm
+should listen on. This can be useful when using
+.Nm
+with
+.Xr pf 4
+.It Xo
+.Fl r
+.Xc
+Go into recursive mode. This is broken, please don't use.
+.It Xo
+.Fl v
+.Xc
+Be more verbose in debug mode. This shows configfile parsing logic.
+.El
+.Sh FILES
+.Pa /etc/delphinusdns.conf
+.Sh SEE ALSO
+.Xr delphinusdns.conf 5 ,
+.Xr bind 2 ,
+.Xr fork 2 ,
+.Xr daemon 3 ,
+.Xr pf 4
+.Sh AUTHORS
+This software was written by
+.An Peter J. Philipp Aq pjp@centroid.eu
+
blob - /dev/null
blob + 87b4c645578284b279ad74c40708fb545d9279f5 (mode 644)
--- /dev/null
+++ dns.h
@@ -0,0 +1,214 @@
+/*
+ * Copyright (c) 2002-2014 Peter J. Philipp
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+#ifndef _DNS_H
+#define _DNS_H
+
+
+/* RFC 1035 - page 26 */
+
+struct dns_header {
+ u_int16_t id; /* ID of header */
+ u_int16_t query;
+ u_int16_t question; /* # of question entries */
+ u_int16_t answer; /* # of answer RR's */
+ u_int16_t nsrr; /* # of NS RR's */
+ u_int16_t additional; /* # additional RR's */
+};
+
+struct dns_hints {
+ int proto;
+ u_int16_t id;
+ u_int16_t query;
+ u_int16_t question;
+ u_int16_t answer;
+ u_int16_t nsrr;
+ u_int16_t additional;
+};
+
+/*
+ * resource record structure
+ * RFC 1035 - page 9
+ */
+
+struct dns_rr {
+ char *name; /* name of zone */
+ char *question; /* pointer to question */
+ u_int16_t type; /* type of RR */
+ u_int16_t class; /* class of reply */
+ u_int32_t ttl; /* ttl of record */
+ u_int16_t rdlen; /* length of record */
+ char *rdata; /* data of record */
+};
+
+/*
+ * EDNS0 OPT RR, based on dns_rr
+ * RFC 6891 - page 7
+ */
+
+struct dns_optrr {
+ char name[1]; /* always 0 */
+ u_int16_t type; /* must be 41 */
+ u_int16_t class; /* UDP payload size (4096) */
+ u_int32_t ttl; /* extended RCODE */
+ u_int16_t rdlen; /* length of all RDATA */
+ char rdata[0]; /* attribute, value pairs */
+}__attribute__((packed));
+
+/* RFC 1035 - page 28 */
+struct dns_question_hdr {
+ char *name;
+ u_int namelen;
+ u_int16_t qtype;
+ u_int16_t qclass;
+};
+
+
+
+/*
+ * flags RFC 1035, page 26
+ */
+
+#define DNS_REPLY 0x8000 /* if set response if not set query */
+#define DNS_NOTIFY 0x2000 /* a NOTIFY query RFC 1996 */
+#define DNS_SREQ 0x1000 /* if set a server status request (STATUS) */
+#define DNS_INV 0x800 /* if set an inverse query */
+#define DNS_AUTH 0x400 /* Authoritative Answer (AA) in replies */
+#define DNS_TRUNC 0x200 /* Truncated (TC) */
+#define DNS_RECURSE 0x100 /* if set Recursion Desired (RD) */
+#define DNS_RECAVAIL 0x80 /* if set Recursion Available (RA) */
+#define DNS_BADTIME 0x12 /* RCODE (18) BADTIME RFC 2845 p. 3 */
+#define DNS_BADKEY 0x11 /* RCODE (17) BADKEY RFC 2845 p. 3 */
+#define DNS_BADSIG 0x10 /* RCODE (16) BADSIG RFC 2845 p. 3 */
+#define DNS_REFUSED 0x5 /* RCODE - Refused */
+#define DNS_NOTIMPL 0x4 /* RCODE - Not Implemented */
+#define DNS_NAMEERR 0x3 /* RCODE - Name Error, NXDOMAIN */
+#define DNS_SERVFAIL 0x2 /* RCODE - Server Failure */
+#define DNS_FORMATERR 0x1 /* RCODE - Format Error */
+#define DNS_NOERR 0x0 /* RCODE - No error */
+
+/*
+ * macros to set flags (must be converted to network byte order after)
+ */
+
+#define SET_DNS_REPLY(x) ((x)->query |= (DNS_REPLY))
+#define SET_DNS_QUERY(x) ((x)->query &= ~(DNS_REPLY))
+#define SET_DNS_NOTIFY(x) ((x)->query |= (DNS_NOTIFY))
+#define SET_DNS_STATUS_REQ(x) ((x)->query |= (DNS_SREQ))
+#define SET_DNS_INVERSE_QUERY(x) ((x)->query |= (DNS_INV))
+#define SET_DNS_AUTHORITATIVE(x) ((x)->query |= (DNS_AUTH))
+#define SET_DNS_TRUNCATION(x) ((x)->query |= (DNS_TRUNC))
+#define SET_DNS_RECURSION(x) ((x)->query |= (DNS_RECURSE))
+#define SET_DNS_RECURSION_AVAIL(x) ((x)->query |= (DNS_RECAVAIL))
+#define SET_DNS_RCODE_REFUSED(x) ((x)->query |= (DNS_REFUSED))
+#define SET_DNS_RCODE_NOTIMPL(x) ((x)->query |= (DNS_NOTIMPL))
+#define SET_DNS_RCODE_NAMEERR(x) ((x)->query |= (DNS_NAMEERR))
+#define SET_DNS_RCODE_SERVFAIL(x) ((x)->query |= (DNS_SERVFAIL))
+#define SET_DNS_RCODE_FORMATERR(x) ((x)->query |= (DNS_FORMATERR))
+#define SET_DNS_RCODE_NOERR(x) ((x)->query |= (DNS_NOERR))
+
+#define UNSET_DNS_NOTIFY(x) ((x)->query &= ~(DNS_NOTIFY))
+#define UNSET_DNS_STATUS_REQ(x) ((x)->query &= ~(DNS_SREQ))
+#define UNSET_DNS_INVERSE_QUERY(x) ((x)->query &= ~(DNS_INV))
+#define UNSET_DNS_AUTHORITATIVE(x) ((x)->query &= ~(DNS_AUTH))
+#define UNSET_DNS_TRUNCATION(x) ((x)->query &= ~(DNS_TRUNC))
+#define UNSET_DNS_RECURSION(x) ((x)->query &= ~(DNS_RECURSE))
+#define UNSET_DNS_RECURSION_AVAIL(x) ((x)->query &= ~(DNS_RECAVAIL))
+#define UNSET_DNS_RCODE_REFUSED(x) ((x)->query &= ~(DNS_REFUSED))
+#define UNSET_DNS_RCODE_NOTIMPL(x) ((x)->query &= ~(DNS_NOTIMPL))
+#define UNSET_DNS_RCODE_NAMEERR(x) ((x)->query &= ~(DNS_NAMEERR))
+#define UNSET_DNS_RCODE_SERVFAIL(x) ((x)->query &= ~(DNS_SERVFAIL))
+#define UNSET_DNS_RCODE_FORMATERR(x) ((x)->query &= ~(DNS_FORMATERR))
+#define UNSET_DNS_RCODE_NOERR(x) ((x)->query &= ~(DNS_NOERR))
+
+/* DNSSEC/EDNS0 options RFC 3225 */
+
+#define DNSSEC_OK 0x8000
+
+#define SET_DNS_ERCODE_DNSSECOK(x) ((x)->ttl |= (DNSSEC_OK))
+#define UNSET_DNS_ERCODE_DNSSECOK(x) ((x)->ttl &= ~(DNSSEC_OK))
+
+/* DNS types - RFC 1035 page 12 */
+
+#define DNS_TYPE_A 1
+#define DNS_TYPE_NS 2
+#define DNS_TYPE_CNAME 5
+#define DNS_TYPE_SOA 6
+#define DNS_TYPE_PTR 12
+#define DNS_TYPE_MX 15
+#define DNS_TYPE_TXT 16
+
+#define DNS_TYPE_SRV 33 /* RFC 2782, page 8 */
+#define DNS_TYPE_NAPTR 35 /* RFC 2915, page 3 */
+#define DNS_TYPE_OPT 41 /* RFC 6891, page 7 */
+#define DNS_TYPE_SSHFP 44 /* RFC 4255 */
+
+#define DNS_TYPE_SPF 99 /* RFC 4408 */
+
+#define DNS_TYPE_TSIG 250 /* RFC 2845, page 3 */
+#define DNS_TYPE_IXFR 251 /* RFC 1995, page 2 */
+#define DNS_TYPE_AXFR 252 /* RFC 5936, page 10 */
+#define DNS_TYPE_ANY 255
+
+/* DNS types 0xff00 -> 0xfffe (private use) RFC 5395, page 8 */
+
+#define DNS_TYPE_BALANCE 0xfffe /* split horizon dns */
+#define DNS_TYPE_DELEGATE 0xfffd /* ns delegations */
+#define DNS_TYPE_HINT 0xfffc /* root hint */
+
+/* quad A - RFC 3596 */
+#define DNS_TYPE_AAAA 28
+
+
+/* DNS CLASSES - RFC 1035 page 13 */
+
+#define DNS_CLASS_IN 1 /* internet */
+#define DNS_CLASS_CH 3 /* chaos */
+#define DNS_CLASS_HS 4 /* hesiod */
+
+#define DNS_CLASS_ANY 255 /* any class */
+
+/* limits */
+
+#define DNS_MAXLABEL 63
+#define DNS_MAXNAME 255
+#define DNS_MAXUDP 512
+
+/* SSHFP fingerprint sizes */
+
+#define DNS_SSHFP_SIZE_SHA1 20 /* RFC 4255 */
+#define DNS_SSHFP_SIZE_SHA256 32 /* RFC 6594 */
+
+
+struct question {
+ struct dns_question_hdr *hdr;
+ char *converted_name;
+ int edns0len;
+ int dnssecok;
+};
+
+#endif /* DNS_H */
blob - /dev/null
blob + 798c19177b8d4db6f23189bf515b40b1b0ce8056 (mode 644)
--- /dev/null
+++ endian.h
@@ -0,0 +1,40 @@
+/* $OpenBSD: endian.h,v 1.18 2006/03/27 07:09:24 otto Exp $ */
+
+/*-
+ * Copyright (c) 1997 Niklas Hallqvist. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Generic definitions for little- and big-endian systems. Other endianesses
+ * has to be dealt with in the specific machine/endian.h file for that port.
+ *
+ * This file is meant to be included from a little- or big-endian port's
+ * machine/endian.h after setting _BYTE_ORDER to either 1234 for little endian
+ * or 4321 for big..
+ */
+
+#define NTOHL(x) (x) = ntohl((u_int32_t)(x))
+#define NTOHS(x) (x) = ntohs((u_int16_t)(x))
+#define HTONL(x) (x) = htonl((u_int32_t)(x))
+#define HTONS(x) (x) = htons((u_int16_t)(x))
+
blob - /dev/null
blob + 4e05f31d6fff8bf76687348129062a4c349f23e4 (mode 644)
--- /dev/null
+++ examples/example1.conf
@@ -0,0 +1,84 @@
+#
+#
+# wildcarddns - RR configuration file
+#
+#
+
+version "6";
+
+; WARNING - the way wildcarddnsd originally implemented wildcarding is
+; wrong and can cause damage on the Internet (DoS), it can
+; also cause damage on someone using wildcarding, so it's best
+; to turn it off. By default anyone wanting to turn wildcarding
+; on should enable the "wildcard-only-for" block as shown below.
+;
+; comment the below block to turn wildcarding off
+;
+wildcard-only-for "my IP's" {
+ 2001:a60:f074::8/128;
+ ::1/128;
+ ; this is a comment
+ ; if this option isn't mentioned, then wildcarding is off
+ ; this will remove the -W option in the command line
+}
+
+
+zone "default" {
+ ; comments must be at the beginning of a new line, # and ; is allowed
+ ;
+ ;
+ ; ALL RR's begine with: zone,RR,time to live, ...
+ ;
+ ; names can be expressed with no trailing . or with trailing dot either way
+ ; they'll be modified to have a trailing dot.
+ ; example: www.google.com or www.google.com. becomes www.google.com.
+ ;
+ ; soa RR consists of: zone, SOA, ttl of RR, nameserver, responsible person,
+ ; serial, refresh, retry, expire, zone time to live (no spaces)
+ ;
+ *,soa,3600,neptune.ATLAS.,pbug.neptune.ATLAS,1,3600,1800,7200,3600
+ ;
+ ; a RR consists of: zone, A, ttl of RR, IP (no spaces)
+ ; up to 10 addresses allowed
+ ;
+ *,a,3600,10.0.0.2
+ *,a,3600,10.0.0.1
+ ;
+ ; MX RR consists of: zone, MX, ttl of RR, priority, name of MX (no spaces)
+ ; up to 10 addresses allowed
+ ;
+ *,mx,3600,1,atlas.local
+ *,mx,3600,1,neptune.local
+ ;
+ ; AAAA RR consists of: zone, AAAA, ttl of RR, IPv6 address (no spaces)
+ ; up to 10 addresses allowed
+ ;
+ *,aaaa,3600,::1
+ *,aaaa,3600,3ffe:b00:1022::
+ ;
+ ; all of *.com looks like this:
+ ;
+ com,soa,3600,a.com,b.com,1,3600,1800,300000,3600
+ ;
+ ; CNAME RR consists of: zone, CNAME, ttl of RR, name (no spaces)
+ ; CNAME support is currently not complete since a CNAME must attach the
+ ; records it is pointing to... something to be done in the future I guess..
+ ;
+ *,cname,3600,neptune.local
+ ;
+ ;
+ ; PTR RR consists of: zone, PTR, ttl of RR, name (no spaces)
+ ;
+ arpa.,ptr,3600,neptune.local.
+ ;
+ ; NS RR consists of: zone, NS, ttl of RR, name (no spaces)
+ ;
+ *,ns,3600,ns.neptune.local.
+ ;
+ ; TXT RR's have this format
+ *,txt,3600,"this is a text"
+
+; close zone
+}
+
+; pretty straight forward
blob - /dev/null
blob + 5b86a472a9f70b2f48cbbeec2349a3859faf6b38 (mode 644)
--- /dev/null
+++ examples/example11.conf
@@ -0,0 +1,123 @@
+; sample config file that is in production.
+;
+version "6";
+
+; WARNING - the way wildcarddnsd originally implemented wildcarding is
+; wrong and can cause damage on the Internet (DoS), it can
+; also cause damage on someone using wildcarding, so it's best
+; to turn it off. By default anyone wanting to turn wildcarding
+; on should enable the "wildcard-only-for" block as shown below.
+;
+; To enable wildcarding uncomment the below...
+;
+;wildcard-only-for "wildcard everyone" {
+; ::/0;
+; 0.0.0.0/0;
+; ; this is a comment
+; ; if this option isn't mentioned, then wildcarding is off
+; ; this will remove the -W option in the command line
+;}
+
+;
+; AXFR turned on...
+;
+
+axfr-for "these hosts" {
+ ::1/128;
+ 127.0.0.1/32;
+}
+
+
+axfrport "10053";
+
+
+zone "centroid.eu" {
+ centroid.eu,soa,3600,uranus.centroid.eu.,pjp.solarscale.de.,1258740680,3600,1800,7200,3600
+ centroid.eu,ns,3600,proteus.solarscale.de.
+ centroid.eu,ns,3600,uranus.centroid.eu.
+ centroid.eu,ns,3600,dione.solarscale.de.
+ ; balance these two
+ centroid.eu,a,3600,200.46.208.61
+ centroid.eu,a,3600,62.75.160.180
+ ;
+ centroid.eu,mx,3600,10,proteus.solarscale.de.
+ centroid.eu,aaaa,3600,2001:a60:f074::8
+ ; and these two
+ www.centroid.eu,a,3600,62.75.160.180
+ www.centroid.eu,a,3600,200.46.208.61
+ ;
+ www.centroid.eu,aaaa,3600,2001:a60:f074::8
+ uranus.centroid.eu,a,3600,212.114.251.91
+ uranus.centroid.eu,aaaa,3600,2001:a60:f000:99::2
+ proteus.centroid.eu,a,3600,62.75.160.180
+ dione.centroid.eu,a,3600,200.46.208.61
+ rosalind.centroid.eu,aaaa,3600,2001:a60:f074::8
+ irc.centroid.eu,a,3600,212.114.251.91
+ irc.centroid.eu,aaaa,3600,2001:a60:f074::24
+ irc2.centroid.eu,a,3600,200.46.208.61
+ localhost.centroid.eu,a,3600,127.0.0.1
+ localhost.centroid.eu,aaaa,3600,::1
+ ; the below are new delegations (NS) records
+ delegation.centroid.eu,delegate,3600,ns1.server.com.
+ ; glue record
+ ns1.server.com.,a,3600,10.0.0.1
+ delegation.centroid.eu,delegate,3600,ns2.server.com.
+ ; glue record
+ ns2.server.com.,a,3600,10.0.0.2
+}
+zone "ipv6.centroid.eu" {
+ ipv6.centroid.eu,soa,3600,uranus.centroid.eu.,pjp.solarscale.de.,1244725285,3600,1800,7200,3600
+ ipv6.centroid.eu,aaaa,3600,2001:a60:f074::8
+ www.ipv6.centroid.eu,aaaa,3600,2001:a60:f074::8
+}
+zone "ipv6.solarscale.de" {
+ ipv6.solarscale.de,soa,3600,proteus.solarscale.de.,pjp.solarscale.de.,1234896563,3600,1800,7200,3600
+ ipv6.solarscale.de,ns,3600,proteus.solarscale.de.
+ ipv6.solarscale.de,ns,3600,uranus.centroid.eu.
+ ipv6.solarscale.de,aaaa,3600,2001:a60:f074::8
+ www.ipv6.solarscale.de,aaaa,3600,2001:a60:f074::8
+}
+zone "reverse dns" {
+ 0.4.7.0.f.0.6.a.0.1.0.0.2.ip6.arpa.,soa,3600,proteus.solarscale.de.,pjp.solarscale.de.,1234896562,3600,1800,7200,3600
+ 1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.4.7.0.f.0.6.a.0.1.0.0.2.ip6.arpa.,ptr,3600,uranus.centroid.eu.
+ 2.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.4.7.0.f.0.6.a.0.1.0.0.2.ip6.arpa.,ptr,3600,setebos.centroid.eu.
+}
+
+; keep root hints disabled until we have a working resolver, even then it
+; should be REFUSED to 3rd parties, disabled after watching a reflection attack
+;zone "root hints" {
+; .,hint,3600000,A.ROOT-SERVERS.NET.
+; A.ROOT-SERVERS.NET.,a,3600000,198.41.0.4
+; A.ROOT-SERVERS.NET.,aaaa,3600000,2001:503:BA3E::2:30
+; .,hint,3600000,B.ROOT-SERVERS.NET.
+; B.ROOT-SERVERS.NET.,a,3600000,192.228.79.201
+; .,hint,3600000,C.ROOT-SERVERS.NET.
+; C.ROOT-SERVERS.NET.,a,3600000,192.33.4.12
+; .,hint,3600000,D.ROOT-SERVERS.NET.
+; D.ROOT-SERVERS.NET.,a,3600000,199.7.91.13
+; D.ROOT-SERVERS.NET.,aaaa,3600000,2001:500:2D::D
+; .,hint,3600000,E.ROOT-SERVERS.NET.
+; E.ROOT-SERVERS.NET.,a,3600000,192.203.230.10
+; .,hint,3600000,F.ROOT-SERVERS.NET.
+; F.ROOT-SERVERS.NET.,a,3600000,192.5.5.241
+; F.ROOT-SERVERS.NET.,aaaa,3600000,2001:500:2f::f
+; .,hint,3600000,G.ROOT-SERVERS.NET.
+; G.ROOT-SERVERS.NET.,a,3600000,192.112.36.4
+; .,hint,3600000,H.ROOT-SERVERS.NET.
+; H.ROOT-SERVERS.NET.,a,3600000,128.63.2.53
+; H.ROOT-SERVERS.NET.,aaaa,3600000,2001:500:1::803f:235
+; .,hint,3600000,I.ROOT-SERVERS.NET.
+; I.ROOT-SERVERS.NET.,a,3600000,192.36.148.17
+; .,hint,3600000,J.ROOT-SERVERS.NET.
+; J.ROOT-SERVERS.NET.,a,3600000,192.58.128.30
+; J.ROOT-SERVERS.NET.,aaaa,3600000,2001:503:C27::2:30
+; .,hint,3600000,K.ROOT-SERVERS.NET.
+; K.ROOT-SERVERS.NET.,a,3600000,193.0.14.129
+; K.ROOT-SERVERS.NET.,aaaa,3600000,2001:7fd::1
+; .,hint,3600000,L.ROOT-SERVERS.NET.
+; L.ROOT-SERVERS.NET.,a,3600000,199.7.83.42
+; L.ROOT-SERVERS.NET.,aaaa,3600000,2001:500:3::42
+; .,hint,3600000,M.ROOT-SERVERS.NET.
+; M.ROOT-SERVERS.NET.,a,3600000,202.12.27.33
+; M.ROOT-SERVERS.NET.,aaaa,3600000,2001:dc3::35
+;}
blob - /dev/null
blob + a4476a364230acbc2c25daef97b07f54d46c1995 (mode 644)
--- /dev/null
+++ examples/example2.conf
@@ -0,0 +1,103 @@
+#
+#
+# wildcarddns - RR configuration file
+#
+#
+
+version "6";
+
+; WARNING - the way wildcarddnsd originally implemented wildcarding is
+; wrong and can cause damage on the Internet (DoS), it can
+; also cause damage on someone using wildcarding, so it's best
+; to turn it off. By default anyone wanting to turn wildcarding
+; on should enable the "wildcard-only-for" block as shown below.
+;
+; comment the below to turn wildcarding off
+wildcard-only-for "wildcard everyone" {
+ 0.0.0.0/0;
+ ::/0;
+ ; this is a comment
+ ; if this option isn't mentioned, then wildcarding is off
+ ; this will remove the -W option in the command line
+}
+
+zone "default" {
+ ;
+ ; comments must be at the beginning of a new line, # and ; is allowed
+ ;
+ ;
+ ; ALL RR's begine with: zone,RR,time to live, ...
+ ;
+ ; names can be expressed with no trailing . or with trailing dot either way
+ ; they'll be modified to have a trailing dot.
+ ; example: www.google.com or www.google.com. becomes www.google.com.
+ ;
+ ; soa RR consists of: zone, SOA, ttl of RR, nameserver, responsible person,
+ ; serial, refresh, retry, expire, zone time to live (no spaces)
+ ;
+ *,soa,3600,miranda.solarscale.de.,pjp.solarscale.de.,1,3600,1800,7200,3600
+ ;
+ ; a RR consists of: zone, A, ttl of RR, IP (no spaces)
+ ; up to 10 addresses allowed
+ ;
+ ; miranda.solarscale.de
+ *,a,3600,192.168.0.20
+ ;
+ ; MX RR consists of: zone, MX, ttl of RR, priority, name of MX (no spaces)
+ ; up to 10 addresses allowed
+ ;
+ *,mx,3600,1,miranda.solarscale.de.
+ ;
+ ; AAAA RR consists of: zone, AAAA, ttl of RR, IPv6 address (no spaces)
+ ; up to 10 addresses allowed
+ ;
+ ;*,aaaa,3600,::1
+ ;*,aaaa,3600,3ffe:b00:1022::
+ ;
+ ; all of *.com looks like this:
+ ;
+ ;com,soa,3600,a.com,b.com,1,3600,1800,300000,3600
+ ;
+ ; CNAME RR consists of: zone, CNAME, ttl of RR, name (no spaces)
+ ; CNAME support is currently not complete since a CNAME must attach the
+ ; records it is pointing to... something to be done in the future I guess..
+ ;
+ ;*,cname,3600,neptune.local
+ ;
+ ;
+ ; PTR RR consists of: zone, PTR, ttl of RR, name (no spaces)
+ ;
+ arpa.,ptr,3600,miranda.solarscale.de.
+ ;
+ ; NS RR consists of: zone, NS, ttl of RR, name (no spaces)
+ ;
+ *,ns,3600,miranda.solarscale.de.
+ ;
+ ; Got it? good.
+ ferdinand.solarscale.de,a,3600,192.168.0.21
+ 21.0.168.192.in-addr.arpa.,ptr,3600,ferdinand.solarscale.de.
+ ariel.solarscale.de,a,3600,192.168.0.17
+ 17.0.168.192.in-addr.arpa.,ptr,3600,ariel.solarscale.de.
+ uranus.solarscale.de,a,3600,192.168.0.1
+ 1.0.168.192.in-addr.arpa.,ptr,3600,uranus.solarscale.de.
+ area5.solarscale.de,a,3600,192.168.0.11
+ 11.0.168.192.in-addr.arpa.,ptr,3600,area5.solarscale.de.
+ perdita.solarscale.de,a,3600,192.168.0.23
+ perdita.solarscale.de,txt,3600,"also does IPv6"
+ 23.0.168.192.in-addr.arpa,ptr,3600,perdita.solarscale.de.
+ itojun.solarscale.de,cname,3600,perdita.solarscale.de.
+ desdemona.solarscale.de,a,3600,192.168.0.15
+ 15.0.168.192.in-addr.arpa,ptr,3600,desdemona.solarscale.de.
+ rosalind.solarscale.de,a,3600,192.168.0.19
+ 19.0.168.192.in-addr.arpa,ptr,3600,rosalind.solarscale.de.
+ titania.solarscale.de,a,3600,192.168.0.16
+ 16.0.168.192.in-addr.arpa,ptr,3600,titania.solarscale.de.
+ cupid.solarscale.de,a,3600,192.168.0.12
+ 12.0.168.192.in-addr.arpa,ptr,3600,cupid.solarscale.de.
+ ;
+ ; cvs
+ cvs.solarscale.de,cname,3600,miranda.solarscale.de.
+ ; irc
+ irc.solarscale.de,cname,3600,ariel.solarscale.de.
+}
+; end
blob - /dev/null
blob + b9ea0396853bd925dcdf5ff418fb74982a1bd808 (mode 644)
--- /dev/null
+++ examples/example3.conf
@@ -0,0 +1,46 @@
+# wildcarddns - RR configuration file
+version "6";
+
+zone "centroid.eu" {
+ # serial, refresh, retry, expire, zone time to live (no spaces)
+ margaret.centroid.eu,soa,3600,proteus.solarscale.de.,pjp.solarscale.de.,1234896561,3600,1800,7200,3600
+ margaret.centroid.eu,ns,3600,proteus.solarscale.de.
+ margaret.centroid.eu,ns,3600,uranus.centroid.eu.
+ ;
+ margaret.centroid.eu,a,3600,212.114.251.91
+ margaret.centroid.eu,aaaa,3600,2001:a60:f074::2
+ ;
+}
+
+zone "ipv6.solarscale.de" {
+ ipv6.solarscale.de,soa,3600,proteus.solarscale.de.,pjp.solarscale.de.,1234896561,3600,1800,7200,3600
+ ipv6.solarscale.de,ns,3600,proteus.solarscale.de.
+ ipv6.solarscale.de,ns,3600,uranus.centroid.eu.
+ ipv6.solarscale.de,ns,3600,margaret.centroid.eu.
+ ;
+ ipv6.solarscale.de,aaaa,3600,2001:a60:f074::2
+ www.ipv6.solarscale.de,aaaa,3600,2001:a60:f074::2
+ ;
+}
+
+zone "ipv6.centroid.eu" {
+ ipv6.centroid.eu,soa,3600,proteus.solarscale.de.,pjp.solarscale.de.,1234896561,3600,1800,7200,3600
+ ipv6.centroid.eu,ns,3600,proteus.solarscale.de.
+ ipv6.centroid.eu,ns,3600,uranus.centroid.eu.
+ ipv6.centroid.eu,ns,3600,margaret.centroid.eu.
+ ;
+ ipv6.centroid.eu,aaaa,3600,2001:a60:f074::2
+ www.ipv6.centroid.eu,aaaa,3600,2001:a60:f074::2
+ ;
+ ;
+}
+
+zone "ip6.arpa" {
+ # reverse dns for IPv6
+ 0.4.7.0.f.0.6.a.0.1.0.0.2.ip6.arpa.,soa,3600,proteus.solarscale.de.,pjp.solarscale.de.,1234896561,3600,1800,7200,3600
+ ;
+ 1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.4.7.0.f.0.6.a.0.1.0.0.2.ip6.arpa.,ptr,3600,uranus.centroid.eu.
+ 2.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.4.7.0.f.0.6.a.0.1.0.0.2.ip6.arpa.,ptr,3600,margaret.centroid.eu.
+}
+
+; end
blob - /dev/null
blob + 5fc6e57a1ea23fd2d9b4a21b214c2950abc1f7d6 (mode 644)
--- /dev/null
+++ examples/example4.conf
@@ -0,0 +1,126 @@
+; sample config file that is in production.
+;
+version "6";
+
+; WARNING - the way wildcarddnsd originally implemented wildcarding is
+; wrong and can cause damage on the Internet (DoS), it can
+; also cause damage on someone using wildcarding, so it's best
+; to turn it off. By default anyone wanting to turn wildcarding
+; on should enable the "wildcard-only-for" block as shown below.
+;
+; To enable wildcarding uncomment the below...
+;
+;wildcard-only-for "wildcard everyone" {
+; ::/0;
+; 0.0.0.0/0;
+; ; this is a comment
+; ; if this option isn't mentioned, then wildcarding is off
+; ; this will remove the -W option in the command line
+;}
+
+; this is for the host dione.centroid.eu which is in Panama
+; it serves best for the Americas and Australia (?)
+
+region "LACNIC" {
+ 192.168.0.0/16;
+ 2001:A60:F074::/64;
+ ; arin below
+ 2001:0400::/23;
+ 2001:1800::/23;
+ 2001:4800::/23;
+ 2600:0000::/12;
+ 2610:0000::/23;
+ 2620:0000::/23;
+ ; lacnic
+ 2001:1200::/23;
+ 2800:0000::/12;
+}
+region "RIPE" {
+ ;
+ ; for RIPE we'll just put the prefix 0.0.0.0/0
+ ; this should work because the higher the prefix the more
+ ; precedence the region has, let's give it a shot.
+ ;
+ 0.0.0.0/0;
+ ::/0;
+ ; RIPE
+ 2001:0600::/23;
+ 2001:0800::/23;
+ 2001:0A00::/23;
+ 2001:1400::/23;
+ 2001:1600::/23;
+ 2001:1A00::/23;
+ 2001:1C00::/22;
+ 2001:2000::/20;
+ 2001:3000::/21;
+ 2001:3800::/22;
+ 2001:4000::/23;
+ 2001:4600::/23;
+ 2001:4A00::/23;
+ 2001:4C00::/23;
+ 2001:5000::/20;
+ 2003:0000::/18;
+ 2A00:0000::/12;
+ ; APNIC
+ 2001:0200::/23;
+ 2001:0C00::/23;
+ 2001:0E00::/23;
+ 2001:4400::/23;
+ 2001:8000::/19;
+ 2001:A000::/20;
+ 2001:B000::/20;
+ 2400:0000::/12;
+ ; AfriNIC
+ 2001:4200::/23;
+ 2C00:0000::/12;
+}
+zone "centroid.eu" {
+ centroid.eu,soa,3600,uranus.centroid.eu.,pjp.solarscale.de.,1258740680,3600,1800,7200,3600
+ centroid.eu,ns,3600,proteus.solarscale.de.
+ centroid.eu,ns,3600,uranus.centroid.eu.
+ centroid.eu,ns,3600,dione.solarscale.de.
+ ; balance these two
+ centroid.eu,balance,3600,62.75.160.180
+ centroid.eu,balance,3600,192.168.0.24
+ centroid.eu,txt,3600,"1234\"\"1234"
+ text.centroid.eu,txt,3600,",,,,this is valid,,,too,,,"
+}
+
+; keep root hints disabled until we have a working resolver, even then it
+; should be REFUSED to 3rd parties, disabled after watching a reflection attack
+;zone "root hints" {
+; .,hint,3600000,A.ROOT-SERVERS.NET.
+; A.ROOT-SERVERS.NET.,a,3600000,198.41.0.4
+; A.ROOT-SERVERS.NET.,aaaa,3600000,2001:503:BA3E::2:30
+; .,hint,3600000,B.ROOT-SERVERS.NET.
+; B.ROOT-SERVERS.NET.,a,3600000,192.228.79.201
+; .,hint,3600000,C.ROOT-SERVERS.NET.
+; C.ROOT-SERVERS.NET.,a,3600000,192.33.4.12
+; .,hint,3600000,D.ROOT-SERVERS.NET.
+; D.ROOT-SERVERS.NET.,a,3600000,199.7.91.13
+; D.ROOT-SERVERS.NET.,aaaa,3600000,2001:500:2D::D
+; .,hint,3600000,E.ROOT-SERVERS.NET.
+; E.ROOT-SERVERS.NET.,a,3600000,192.203.230.10
+; .,hint,3600000,F.ROOT-SERVERS.NET.
+; F.ROOT-SERVERS.NET.,a,3600000,192.5.5.241
+; F.ROOT-SERVERS.NET.,aaaa,3600000,2001:500:2f::f
+; .,hint,3600000,G.ROOT-SERVERS.NET.
+; G.ROOT-SERVERS.NET.,a,3600000,192.112.36.4
+; .,hint,3600000,H.ROOT-SERVERS.NET.
+; H.ROOT-SERVERS.NET.,a,3600000,128.63.2.53
+; H.ROOT-SERVERS.NET.,aaaa,3600000,2001:500:1::803f:235
+; .,hint,3600000,I.ROOT-SERVERS.NET.
+; I.ROOT-SERVERS.NET.,a,3600000,192.36.148.17
+; .,hint,3600000,J.ROOT-SERVERS.NET.
+; J.ROOT-SERVERS.NET.,a,3600000,192.58.128.30
+; J.ROOT-SERVERS.NET.,aaaa,3600000,2001:503:C27::2:30
+; .,hint,3600000,K.ROOT-SERVERS.NET.
+; K.ROOT-SERVERS.NET.,a,3600000,193.0.14.129
+; K.ROOT-SERVERS.NET.,aaaa,3600000,2001:7fd::1
+; .,hint,3600000,L.ROOT-SERVERS.NET.
+; L.ROOT-SERVERS.NET.,a,3600000,199.7.83.42
+; L.ROOT-SERVERS.NET.,aaaa,3600000,2001:500:3::42
+; .,hint,3600000,M.ROOT-SERVERS.NET.
+; M.ROOT-SERVERS.NET.,a,3600000,202.12.27.33
+; M.ROOT-SERVERS.NET.,aaaa,3600000,2001:dc3::35
+;}
blob - /dev/null
blob + fb29589792bad2db850aac91fed19f12862c7bc7 (mode 644)
--- /dev/null
+++ examples/example6.conf
@@ -0,0 +1,47 @@
+; sample config file that is in production.
+;
+version "6";
+
+recurse-for "these hosts" {
+ 127.0.0.1/32;
+ ::1/128;
+}
+
+; keep root hints disabled until we have a working resolver, even then it
+; should be REFUSED to 3rd parties, disabled after watching a reflection attack
+;zone "root hints" {
+; .,hint,3600000,A.ROOT-SERVERS.NET.
+; A.ROOT-SERVERS.NET.,a,3600000,198.41.0.4
+; A.ROOT-SERVERS.NET.,aaaa,3600000,2001:503:BA3E::2:30
+; .,hint,3600000,B.ROOT-SERVERS.NET.
+; B.ROOT-SERVERS.NET.,a,3600000,192.228.79.201
+; .,hint,3600000,C.ROOT-SERVERS.NET.
+; C.ROOT-SERVERS.NET.,a,3600000,192.33.4.12
+; .,hint,3600000,D.ROOT-SERVERS.NET.
+; D.ROOT-SERVERS.NET.,a,3600000,199.7.91.13
+; D.ROOT-SERVERS.NET.,aaaa,3600000,2001:500:2D::D
+; .,hint,3600000,E.ROOT-SERVERS.NET.
+; E.ROOT-SERVERS.NET.,a,3600000,192.203.230.10
+; .,hint,3600000,F.ROOT-SERVERS.NET.
+; F.ROOT-SERVERS.NET.,a,3600000,192.5.5.241
+; F.ROOT-SERVERS.NET.,aaaa,3600000,2001:500:2f::f
+; .,hint,3600000,G.ROOT-SERVERS.NET.
+; G.ROOT-SERVERS.NET.,a,3600000,192.112.36.4
+; .,hint,3600000,H.ROOT-SERVERS.NET.
+; H.ROOT-SERVERS.NET.,a,3600000,128.63.2.53
+; H.ROOT-SERVERS.NET.,aaaa,3600000,2001:500:1::803f:235
+; .,hint,3600000,I.ROOT-SERVERS.NET.
+; I.ROOT-SERVERS.NET.,a,3600000,192.36.148.17
+; .,hint,3600000,J.ROOT-SERVERS.NET.
+; J.ROOT-SERVERS.NET.,a,3600000,192.58.128.30
+; J.ROOT-SERVERS.NET.,aaaa,3600000,2001:503:C27::2:30
+; .,hint,3600000,K.ROOT-SERVERS.NET.
+; K.ROOT-SERVERS.NET.,a,3600000,193.0.14.129
+; K.ROOT-SERVERS.NET.,aaaa,3600000,2001:7fd::1
+; .,hint,3600000,L.ROOT-SERVERS.NET.
+; L.ROOT-SERVERS.NET.,a,3600000,199.7.83.42
+; L.ROOT-SERVERS.NET.,aaaa,3600000,2001:500:3::42
+; .,hint,3600000,M.ROOT-SERVERS.NET.
+; M.ROOT-SERVERS.NET.,a,3600000,202.12.27.33
+; M.ROOT-SERVERS.NET.,aaaa,3600000,2001:dc3::35
+;}
blob - /dev/null
blob + 8369bfe67630069f39502afcd7e978e9d1e19658 (mode 644)
--- /dev/null
+++ examples/example7.conf
@@ -0,0 +1,219 @@
+; sample config file that is in production.
+;
+version "6";
+
+; WARNING - the way wildcarddnsd originally implemented wildcarding is
+; wrong and can cause damage on the Internet (DoS), it can
+; also cause damage on someone using wildcarding, so it's best
+; to turn it off. By default anyone wanting to turn wildcarding
+; on should enable the "wildcard-only-for" block as shown below.
+;
+; To enable wildcarding uncomment the below...
+;
+;wildcard-only-for "wildcard everyone" {
+; ::/0;
+; 0.0.0.0/0;
+; ; this is a comment
+; ; if this option isn't mentioned, then wildcarding is off
+; ; this will remove the -W option in the command line
+;}
+
+
+
+; this is for the host dione.centroid.eu which is in Panama
+; it serves best for the Americas and Australia (?)
+
+region "LACNIC" {
+ ;
+ ; ARIN and LACNIC blocks below
+ ;
+ ; lacnic netblocks
+ 186.0.0.0/8;
+ 187.0.0.0/8;
+ 189.0.0.0/8;
+ 190.0.0.0/8;
+ 191.0.0.0/8;
+ 200.0.0.0/8;
+ 201.0.0.0/8;
+ ; arin netblocks
+ 216.0.0.0/8;
+ 209.0.0.0/8;
+ 208.0.0.0/8;
+ 207.0.0.0/8;
+ 206.0.0.0/8;
+ 205.0.0.0/8;
+ 204.0.0.0/8;
+ 199.0.0.0/8;
+ 198.0.0.0/8;
+ 192.0.0.0/8;
+ 184.0.0.0/8;
+ 173.0.0.0/8;
+ 172.0.0.0/8;
+ 170.0.0.0/8;
+ 169.0.0.0/8;
+ 168.0.0.0/8;
+ 167.0.0.0/8;
+ 166.0.0.0/8;
+ 165.0.0.0/8;
+ 164.0.0.0/8;
+ 162.0.0.0/8;
+ 161.0.0.0/8;
+ 160.0.0.0/8;
+ 159.0.0.0/8;
+ 158.0.0.0/8;
+ 157.0.0.0/8;
+ 156.0.0.0/8;
+ 155.0.0.0/8;
+ 152.0.0.0/8;
+ 149.0.0.0/8;
+ 148.0.0.0/8;
+ 147.0.0.0/8;
+ 146.0.0.0/8;
+ 144.0.0.0/8;
+ 143.0.0.0/8;
+ 140.0.0.0/8;
+ 139.0.0.0/8;
+ 138.0.0.0/8;
+ 137.0.0.0/8;
+ 136.0.0.0/8;
+ 135.0.0.0/8;
+ 134.0.0.0/8;
+ 132.0.0.0/8;
+ 131.0.0.0/8;
+ 130.0.0.0/8;
+ 129.0.0.0/8;
+ 128.0.0.0/8;
+ 108.0.0.0/8;
+ 99.0.0.0/8;
+ 98.0.0.0/8;
+ 97.0.0.0/8;
+ 96.0.0.0/8;
+ 76.0.0.0/8;
+ 75.0.0.0/8;
+ 74.0.0.0/8;
+ 73.0.0.0/8;
+ 72.0.0.0/8;
+ 71.0.0.0/8;
+ 70.0.0.0/8;
+ 69.0.0.0/8;
+ 68.0.0.0/8;
+ 67.0.0.0/8;
+ 66.0.0.0/8;
+ 65.0.0.0/8;
+ 64.0.0.0/8;
+ 63.0.0.0/8;
+ 24.0.0.0/8;
+ 7.0.0.0/8;
+ ; Australia likes going through the US, LACNIC is closer
+ ; these two blocks are just some of many
+ 202.0.0.0/8;
+ 203.0.0.0/8;
+
+}
+;
+; This region is served by proteus.solarscale.de which resides in RIPE
+; it serves regions RIPE, AFRINIC and APNIC although the traceroutes
+; from China seem to indicate as much latency for proteus as there is
+; for dione
+;
+region "RIPE" {
+ ;
+ ; for RIPE we'll just put the prefix 0.0.0.0/0
+ ; this should work because the higher the prefix the more
+ ; precedence the region has, let's give it a shot.
+ ;
+ ::/0;
+ 0.0.0.0/0;
+ 192.168.0.0/16;
+ 147.102.0.0/16;
+}
+zone "centroid.eu" {
+ centroid.eu,soa,3600,uranus.centroid.eu.,pjp.solarscale.de.,1258740680,3600,1800,7200,3600
+ centroid.eu,ns,3600,proteus.solarscale.de.
+ centroid.eu,ns,3600,uranus.centroid.eu.
+ centroid.eu,ns,3600,dione.solarscale.de.
+ ; balance these two
+ centroid.eu,balance,3600,200.46.208.61
+ centroid.eu,balance,3600,62.75.160.180
+ ;
+ centroid.eu,mx,3600,10,proteus.solarscale.de.
+ centroid.eu,aaaa,3600,2001:a60:f074::8
+ ; and these two
+ www.centroid.eu,balance,3600,62.75.160.180
+ www.centroid.eu,balance,3600,200.46.208.61
+ ;
+ www.centroid.eu,aaaa,3600,2001:a60:f074::8
+ uranus.centroid.eu,a,3600,212.114.251.91
+ uranus.centroid.eu,aaaa,3600,2001:a60:f000:99::2
+ proteus.centroid.eu,a,3600,62.75.160.180
+ dione.centroid.eu,a,3600,200.46.208.61
+ rosalind.centroid.eu,aaaa,3600,2001:a60:f074::8
+ irc.centroid.eu,a,3600,212.114.251.91
+ irc.centroid.eu,aaaa,3600,2001:a60:f074::24
+ irc2.centroid.eu,a,3600,200.46.208.61
+ localhost.centroid.eu,a,3600,127.0.0.1
+ localhost.centroid.eu,aaaa,3600,::1
+ ; the below are new delegations (NS) records
+ ;delegation.centroid.eu,delegate,3600,ns1.server.com.
+ ; glue record
+ ;ns1.server.com.,a,3600,10.0.0.1
+ ;delegation.centroid.eu,delegate,3600,ns2.server.com.
+ ; glue record
+ ;ns2.server.com.,a,3600,10.0.0.2
+}
+zone "ipv6.centroid.eu" {
+ ipv6.centroid.eu,soa,3600,uranus.centroid.eu.,pjp.solarscale.de.,1244725285,3600,1800,7200,3600
+ ipv6.centroid.eu,aaaa,3600,2001:a60:f074::8
+ www.ipv6.centroid.eu,aaaa,3600,2001:a60:f074::8
+}
+zone "ipv6.solarscale.de" {
+ ipv6.solarscale.de,soa,3600,proteus.solarscale.de.,pjp.solarscale.de.,1234896563,3600,1800,7200,3600
+ ipv6.solarscale.de,ns,3600,proteus.solarscale.de.
+ ipv6.solarscale.de,ns,3600,uranus.centroid.eu.
+ ipv6.solarscale.de,aaaa,3600,2001:a60:f074::8
+ www.ipv6.solarscale.de,aaaa,3600,2001:a60:f074::8
+}
+zone "reverse dns" {
+ 0.4.7.0.f.0.6.a.0.1.0.0.2.ip6.arpa.,soa,3600,proteus.solarscale.de.,pjp.solarscale.de.,1234896562,3600,1800,7200,3600
+ 1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.4.7.0.f.0.6.a.0.1.0.0.2.ip6.arpa.,ptr,3600,uranus.centroid.eu.
+ 2.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.4.7.0.f.0.6.a.0.1.0.0.2.ip6.arpa.,ptr,3600,setebos.centroid.eu.
+}
+
+; keep root hints disabled until we have a working resolver, even then it
+; should be REFUSED to 3rd parties, disabled after watching a reflection attack
+;zone "root hints" {
+; .,hint,3600000,A.ROOT-SERVERS.NET.
+; A.ROOT-SERVERS.NET.,a,3600000,198.41.0.4
+; A.ROOT-SERVERS.NET.,aaaa,3600000,2001:503:BA3E::2:30
+; .,hint,3600000,B.ROOT-SERVERS.NET.
+; B.ROOT-SERVERS.NET.,a,3600000,192.228.79.201
+; .,hint,3600000,C.ROOT-SERVERS.NET.
+; C.ROOT-SERVERS.NET.,a,3600000,192.33.4.12
+; .,hint,3600000,D.ROOT-SERVERS.NET.
+; D.ROOT-SERVERS.NET.,a,3600000,199.7.91.13
+; D.ROOT-SERVERS.NET.,aaaa,3600000,2001:500:2D::D
+; .,hint,3600000,E.ROOT-SERVERS.NET.
+; E.ROOT-SERVERS.NET.,a,3600000,192.203.230.10
+; .,hint,3600000,F.ROOT-SERVERS.NET.
+; F.ROOT-SERVERS.NET.,a,3600000,192.5.5.241
+; F.ROOT-SERVERS.NET.,aaaa,3600000,2001:500:2f::f
+; .,hint,3600000,G.ROOT-SERVERS.NET.
+; G.ROOT-SERVERS.NET.,a,3600000,192.112.36.4
+; .,hint,3600000,H.ROOT-SERVERS.NET.
+; H.ROOT-SERVERS.NET.,a,3600000,128.63.2.53
+; H.ROOT-SERVERS.NET.,aaaa,3600000,2001:500:1::803f:235
+; .,hint,3600000,I.ROOT-SERVERS.NET.
+; I.ROOT-SERVERS.NET.,a,3600000,192.36.148.17
+; .,hint,3600000,J.ROOT-SERVERS.NET.
+; J.ROOT-SERVERS.NET.,a,3600000,192.58.128.30
+; J.ROOT-SERVERS.NET.,aaaa,3600000,2001:503:C27::2:30
+; .,hint,3600000,K.ROOT-SERVERS.NET.
+; K.ROOT-SERVERS.NET.,a,3600000,193.0.14.129
+; K.ROOT-SERVERS.NET.,aaaa,3600000,2001:7fd::1
+; .,hint,3600000,L.ROOT-SERVERS.NET.
+; L.ROOT-SERVERS.NET.,a,3600000,199.7.83.42
+; L.ROOT-SERVERS.NET.,aaaa,3600000,2001:500:3::42
+; .,hint,3600000,M.ROOT-SERVERS.NET.
+; M.ROOT-SERVERS.NET.,a,3600000,202.12.27.33
+; M.ROOT-SERVERS.NET.,aaaa,3600000,2001:dc3::35
+;}
blob - /dev/null
blob + 690de3a7d6eded3b2056d2affdbdc426894651f6 (mode 644)
--- /dev/null
+++ examples/example8.conf
@@ -0,0 +1,139 @@
+; sample config file that is in production.
+;
+version "6";
+options "cool stuff" {
+ interface "lo0";
+ interface "em0";
+
+ ;bind 127.0.0.1;
+ ;bind 192.168.34.4;
+
+ ratelimit-pps 6;
+
+ port 53;
+
+ ;fork 2;
+ log;
+}
+
+axfrport "10053";
+
+axfr-for "these hosts" {
+ 127.0.0.1;
+ ::1;
+ 192.168.0.0/16;
+}
+
+notify "these hosts" {
+ 192.168.34.1;
+ 192.168.35.1;
+ ::1;
+}
+
+
+zone "centroid.eu" {
+ centroid.eu,soa,3600,uranus.centroid.eu.,pjp.solarscale.de.,2014051701,3600,1800,7200,3600
+ centroid.eu,ns,3600,proteus.solarscale.de.
+ centroid.eu,ns,3600,uranus.centroid.eu.
+ centroid.eu,ns,3600,dione.solarscale.de.
+ ;
+ centroid.eu,a,3600,200.46.208.61
+ centroid.eu,a,3600,62.75.160.180
+ ;
+ centroid.eu,SSHFP,3600,1,1,"9b3624f96a6766e73dc88505f945a564dee82e59"
+ centroid.eu,SSHFP,3600,1,2,"03310030886404f8f61d2e4dc445412788284ab04d0858bfa375eb80fc0d34f6"
+ centroid.eu,SSHFP,3600,2,1,"3de1bbae4de5ebd3571a169a50d6a5a4145dd7a9"
+ centroid.eu,SSHFP,3600,2,2,"bbd96ff7e7f629377d655d7fa9662502837b66cf9a2d24590df77acef66fe405"
+ centroid.eu,SSHFP,3600,3,1,"0ca455de4800c0f6c681e805e7bd86e511efb1fc"
+ centroid.eu,SSHFP,3600,3,2,"b330fd2288cf9e13c9ef68b1b0c0b20a10c16cc55f03cd3869cf1a49f06bed43"
+ ;
+ centroid.eu,mx,3600,10,proteus.solarscale.de.
+ centroid.eu,aaaa,3600,2001:a60:f074::8
+ ;
+ centroid.eu,spf,3600,"v=spf1 ip4:200.46.208.61 ~all"
+ ;
+ ;
+ ; NAPTR trial
+ centroid.eu,naptr,3600,10,10,"u","E2U+sip","!^.*$!sip:1234@sip.example.com!",centroid.eu
+ centroid.eu,naptr,3600,10,20,"u","E2U+sip","!^.*$!sip:1234@sip.example.com!",.
+
+ ;
+ www.centroid.eu,a,3600,62.75.160.180
+ www.centroid.eu,a,3600,200.46.208.61
+ ;
+ www.centroid.eu,aaaa,3600,2001:a60:f074::8
+ uranus.centroid.eu,a,3600,212.114.251.91
+ uranus.centroid.eu,aaaa,3600,2001:a60:f000:99::2
+ proteus.centroid.eu,a,3600,62.75.160.180
+ dione.centroid.eu,a,3600,200.46.208.61
+ rosalind.centroid.eu,aaaa,3600,2001:a60:f074::8
+ irc.centroid.eu,a,3600,212.114.251.91
+ irc.centroid.eu,aaaa,3600,2001:a60:f074::24
+ irc2.centroid.eu,a,3600,200.46.208.61
+ localhost.centroid.eu,a,3600,127.0.0.1
+ localhost.centroid.eu,aaaa,3600,::1
+ ; the below are new delegations (NS) records
+ ;delegation.centroid.eu,delegate,3600,ns1.server.com.
+ ; glue record
+ ;ns1.server.com.,a,3600,10.0.0.1
+ ;delegation.centroid.eu,delegate,3600,ns2.server.com.
+ ; glue record
+ ;ns2.server.com.,a,3600,10.0.0.2
+}
+zone "ipv6.centroid.eu" {
+ ipv6.centroid.eu,soa,3600,uranus.centroid.eu.,pjp.solarscale.de.,1244725285,3600,1800,7200,3600
+ ipv6.centroid.eu,aaaa,3600,2001:a60:f074::8
+ www.ipv6.centroid.eu,aaaa,3600,2001:a60:f074::8
+}
+zone "ipv6.solarscale.de" {
+ ipv6.solarscale.de,soa,3600,proteus.solarscale.de.,pjp.solarscale.de.,1234896563,3600,1800,7200,3600
+ ipv6.solarscale.de,ns,3600,proteus.solarscale.de.
+ ipv6.solarscale.de,ns,3600,uranus.centroid.eu.
+ ipv6.solarscale.de,aaaa,3600,2001:a60:f074::8
+ www.ipv6.solarscale.de,aaaa,3600,2001:a60:f074::8
+}
+zone "reverse dns" {
+ 0.4.7.0.f.0.6.a.0.1.0.0.2.ip6.arpa.,soa,3600,proteus.solarscale.de.,pjp.solarscale.de.,1234896562,3600,1800,7200,3600
+ 1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.4.7.0.f.0.6.a.0.1.0.0.2.ip6.arpa.,ptr,3600,uranus.centroid.eu.
+ 2.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.4.7.0.f.0.6.a.0.1.0.0.2.ip6.arpa.,ptr,3600,setebos.centroid.eu.
+}
+
+; keep root hints disabled until we have a working resolver, even then it
+; should be REFUSED to 3rd parties, disabled after watching a reflection attack
+;zone "root hints" {
+; .,hint,3600000,A.ROOT-SERVERS.NET.
+; A.ROOT-SERVERS.NET.,a,3600000,198.41.0.4
+; A.ROOT-SERVERS.NET.,aaaa,3600000,2001:503:BA3E::2:30
+; .,hint,3600000,B.ROOT-SERVERS.NET.
+; B.ROOT-SERVERS.NET.,a,3600000,192.228.79.201
+; .,hint,3600000,C.ROOT-SERVERS.NET.
+; C.ROOT-SERVERS.NET.,a,3600000,192.33.4.12
+; .,hint,3600000,D.ROOT-SERVERS.NET.
+; D.ROOT-SERVERS.NET.,a,3600000,199.7.91.13
+; D.ROOT-SERVERS.NET.,aaaa,3600000,2001:500:2D::D
+; .,hint,3600000,E.ROOT-SERVERS.NET.
+; E.ROOT-SERVERS.NET.,a,3600000,192.203.230.10
+; .,hint,3600000,F.ROOT-SERVERS.NET.
+; F.ROOT-SERVERS.NET.,a,3600000,192.5.5.241
+; F.ROOT-SERVERS.NET.,aaaa,3600000,2001:500:2f::f
+; .,hint,3600000,G.ROOT-SERVERS.NET.
+; G.ROOT-SERVERS.NET.,a,3600000,192.112.36.4
+; .,hint,3600000,H.ROOT-SERVERS.NET.
+; H.ROOT-SERVERS.NET.,a,3600000,128.63.2.53
+; H.ROOT-SERVERS.NET.,aaaa,3600000,2001:500:1::803f:235
+; .,hint,3600000,I.ROOT-SERVERS.NET.
+; I.ROOT-SERVERS.NET.,a,3600000,192.36.148.17
+; .,hint,3600000,J.ROOT-SERVERS.NET.
+; J.ROOT-SERVERS.NET.,a,3600000,192.58.128.30
+; J.ROOT-SERVERS.NET.,aaaa,3600000,2001:503:C27::2:30
+; .,hint,3600000,K.ROOT-SERVERS.NET.
+; K.ROOT-SERVERS.NET.,a,3600000,193.0.14.129
+; K.ROOT-SERVERS.NET.,aaaa,3600000,2001:7fd::1
+; .,hint,3600000,L.ROOT-SERVERS.NET.
+; L.ROOT-SERVERS.NET.,a,3600000,199.7.83.42
+; L.ROOT-SERVERS.NET.,aaaa,3600000,2001:500:3::42
+; .,hint,3600000,M.ROOT-SERVERS.NET.
+; M.ROOT-SERVERS.NET.,a,3600000,202.12.27.33
+; M.ROOT-SERVERS.NET.,aaaa,3600000,2001:dc3::35
+;}
+;
blob - /dev/null
blob + ef736deb1b2b89fb7dec2b791e54641a62e4e003 (mode 644)
--- /dev/null
+++ filter.c
@@ -0,0 +1,187 @@
+/*
+ * Copyright (c) 2014 Peter J. Philipp
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+#include "include.h"
+#include "dns.h"
+#include "db.h"
+
+int find_filter(struct sockaddr_storage *, int);
+void init_filter(void);
+int insert_filter(char *, char *);
+
+extern void dolog(int, char *, ...);
+extern in_addr_t getmask(int);
+extern int getmask6(int, struct sockaddr_in6 *);
+
+extern int debug, verbose;
+
+SLIST_HEAD(listhead, filterentry) filterhead;
+
+static struct filterentry {
+ char name[INET6_ADDRSTRLEN];
+ int family;
+ struct sockaddr_storage hostmask;
+ struct sockaddr_storage netmask;
+ u_int8_t prefixlen;
+ SLIST_ENTRY(filterentry) filter_entry;
+} *fn2, *fnp;
+
+
+static const char rcsid[] = "$Id: filter.c,v 1.1.1.1 2014/11/14 08:09:04 pjp Exp $";
+
+/*
+ * INIT_FILTER - initialize the filter singly linked list
+ */
+
+void
+init_filter(void)
+{
+ SLIST_INIT(&filterhead);
+ return;
+}
+
+/*
+ * INSERT_FILTER - insert an address and prefixlen into the filter slist
+ */
+
+int
+insert_filter(char *address, char *prefixlen)
+{
+ struct sockaddr_in *sin;
+ struct sockaddr_in6 *sin6;
+ int pnum;
+ int ret;
+
+ pnum = atoi(prefixlen);
+ fn2 = malloc(sizeof(struct filterentry)); /* Insert after. */
+
+ if (strchr(address, ':') != NULL) {
+ fn2->family = AF_INET6;
+ sin6 = (struct sockaddr_in6 *)&fn2->hostmask;
+ if ((ret = inet_pton(AF_INET6, address, &sin6->sin6_addr.s6_addr)) != 1)
+ return (-1);
+ sin6->sin6_family = AF_INET6;
+ sin6 = (struct sockaddr_in6 *)&fn2->netmask;
+ sin6->sin6_family = AF_INET6;
+ if (getmask6(pnum, sin6) < 0)
+ return(-1);
+ fn2->prefixlen = pnum;
+ } else {
+
+ fn2->family = AF_INET;
+ sin = (struct sockaddr_in *)&fn2->hostmask;
+ sin->sin_family = AF_INET;
+ sin->sin_addr.s_addr = inet_addr(address);
+ sin = (struct sockaddr_in *)&fn2->netmask;
+ sin->sin_family = AF_INET;
+ sin->sin_addr.s_addr = getmask(pnum);
+ fn2->prefixlen = pnum;
+
+ }
+
+ SLIST_INSERT_HEAD(&filterhead, fn2, filter_entry);
+
+ return (0);
+}
+
+/*
+ * FIND_FILTER - walk the filter list and find the correponding network
+ * if a network matches return 1, if no match is found return
+ * 0.
+ */
+
+int
+find_filter(struct sockaddr_storage *sst, int family)
+{
+ struct sockaddr_in *sin, *sin0;
+ struct sockaddr_in6 *sin6, *sin60, *sin61;
+ u_int32_t hostmask, netmask;
+ u_int32_t a;
+#ifdef __amd64
+ u_int64_t *hm[2], *nm[2], *a6[2];
+#else
+ u_int32_t *hm[4], *nm[4], *a6[4];
+#endif
+
+ SLIST_FOREACH(fnp, &filterhead, filter_entry) {
+ if (fnp->family == AF_INET) {
+ if (family != AF_INET)
+ continue;
+ sin = (struct sockaddr_in *)sst;
+ a = sin->sin_addr.s_addr;
+ sin = (struct sockaddr_in *)&fnp->hostmask;
+ sin0 = (struct sockaddr_in *)&fnp->netmask;
+ hostmask = sin->sin_addr.s_addr;
+ netmask = sin0->sin_addr.s_addr;
+ if ((hostmask & netmask) == (a & netmask)) {
+ return (1);
+ } /* if hostmask */
+ } else if (fnp->family == AF_INET6) {
+ if (family != AF_INET6)
+ continue;
+ sin6 = (struct sockaddr_in6 *)sst;
+ sin60 = (struct sockaddr_in6 *)&fnp->hostmask;
+ sin61 = (struct sockaddr_in6 *)&fnp->netmask;
+#ifdef __amd64
+ /*
+ * If this is on a 64 bit machine, we'll benefit
+ * by using 64 bit registers, this should make it
+ * a tad faster...
+ */
+ hm[0] = (u_int64_t *)&sin60->sin6_addr.s6_addr;
+ hm[1] = (hm[0] + 1);
+ nm[0] = (u_int64_t *)&sin61->sin6_addr.s6_addr;
+ nm[1] = (nm[0] + 1);
+ a6[0] = (u_int64_t *)&sin6->sin6_addr.s6_addr;
+ a6[1] = (a6[0] + 1);
+ if ( ((*hm[0] & *nm[0]) == (*a6[0] & *nm[0]))&&
+ ((*hm[1] & *nm[1]) == (*a6[1] & *nm[1]))) {
+#else
+ hm[0] = (u_int32_t *)&sin60->sin6_addr.s6_addr;
+ hm[1] = (hm[0] + 1); hm[2] = (hm[1] + 1);
+ hm[3] = (hm[2] + 1);
+ nm[0] = (u_int32_t *)&sin61->sin6_addr.s6_addr;
+ nm[1] = (nm[0] + 1); nm[2] = (nm[1] + 1);
+ nm[3] = (nm[2] + 1);
+ a6[0] = (u_int32_t *)&sin6->sin6_addr.s6_addr;
+ a6[1] = (a6[0] + 1); a6[2] = (a6[1] + 1);
+ a6[3] = (a6[2] + 1);
+
+ if ( ((*hm[0] & *nm[0]) == (*a6[0] & *nm[0]))&&
+ ((*hm[1] & *nm[1]) == (*a6[1] & *nm[1]))&&
+ ((*hm[2] & *nm[2]) == (*a6[2] & *nm[2]))&&
+ ((*hm[3] & *nm[3]) == (*a6[3] & *nm[3]))) {
+#endif
+
+ return (1);
+ } /* if ip6 address */
+
+ } /* if AF_INET6 */
+ } /* SLIST */
+
+ return (0);
+}
blob - /dev/null
blob + b95e6d9971b64f80c5cae228f88b0552e16c30f0 (mode 644)
--- /dev/null
+++ include.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2005-2014 Peter J. Philipp
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+#ifndef _INCLUDES_H
+#define _INCLUDES_H
+
+#include <sys/param.h>
+#include <sys/ipc.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <sys/queue.h>
+#include <sys/mman.h>
+#include <sys/wait.h>
+
+#include <net/if.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/udp.h>
+#define _KERNEL 1
+#include <netinet/ip6.h>
+#undef _KERNEL
+
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <syslog.h>
+#include <ctype.h>
+#include <db.h>
+#include <pwd.h>
+#include <ifaddrs.h>
+#include <dirent.h>
+#include <signal.h>
+
+#ifdef __linux__
+#include <grp.h>
+#endif
+
+#ifndef NTOHS
+#include "endian.h"
+#endif
+
+#if !defined __OpenBSD__ && !defined __NetBSD__
+struct ip6_hdr_pseudo {
+ struct in6_addr ip6ph_src;
+ struct in6_addr ip6ph_dst;
+ u_int32_t ip6ph_len;
+ u_int8_t ip6ph_zero[3];
+ u_int8_t ip6ph_nxt;
+} __packed;
+#endif
+
+#endif
blob - /dev/null
blob + 84c68f9e08e6567489c357485b15f20495328fba (mode 644)
--- /dev/null
+++ log.c
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2011-2014 Peter J. Philipp
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "include.h"
+#include "dns.h"
+#include "db.h"
+
+#include <openssl/evp.h>
+#include <openssl/hmac.h>
+
+extern struct logging logging;
+extern int debug;
+extern int verbose;
+
+void dolog(int pri, char *fmt, ...);
+void receivelog(char *buf, int len);
+int remotelog(int fd, char *fmt, ...);
+
+
+/*
+ * dolog() - is a wrapper to syslog and printf depending on debug flag
+ *
+ */
+
+void
+dolog(int pri, char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+
+ /*
+ * if the message is a debug message and verbose (-v) is set
+ * then print it, otherwise
+ */
+
+ if (pri == LOG_DEBUG) {
+ if (verbose && debug)
+ vprintf(fmt, ap);
+ else if (verbose)
+ vsyslog(pri, fmt, ap);
+ } else {
+ if (debug)
+ vprintf(fmt, ap);
+ else
+ vsyslog(pri, fmt, ap);
+ }
+
+ va_end(ap);
+
+}
+
+/*
+ * remotelog() - is like syslog() only the first argument is a filedescriptor
+ * instead of severity, it will send a packet to the loghost
+ * signed.
+ */
+
+int
+remotelog(int fd, char *fmt, ...)
+{
+ va_list ap;
+ static char buf[1500];
+ static char outbuf[1500];
+ char sign[20];
+ char *p;
+ u_int rlen;
+ static u_int64_t sequence = 0;
+
+
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+
+#ifdef __NetBSD__
+ snprintf(outbuf, sizeof(outbuf), "XXXXXXXXXXXXXXXXXXXX%lu %s %s",
+#else
+ snprintf(outbuf, sizeof(outbuf), "XXXXXXXXXXXXXXXXXXXX%llu %s %s",
+#endif
+ sequence++, logging.hostname, buf);
+
+ p = &outbuf[20];
+
+
+ HMAC(EVP_sha1(), logging.logpasswd, strlen(logging.logpasswd),
+ (unsigned char *)p, strlen(p), (unsigned char *)&sign,
+ &rlen);
+
+ memcpy(outbuf, sign, 20);
+
+ return (send(fd, outbuf, strlen(outbuf), 0));
+}
+
+
+void
+receivelog(char *buf, int len)
+{
+ static char inbuf[1500];
+ char sign[20];
+ char *p;
+ int rlen;
+
+ if (len < 21 || len > 1450)
+ return;
+
+ memcpy(&inbuf, buf, len);
+ inbuf[len] = '\0';
+
+ p = &inbuf[20];
+
+ HMAC(EVP_sha1(), logging.logpasswd, strlen(logging.logpasswd),
+ (unsigned char *)p, strlen(p), (unsigned char *)&sign,
+ (unsigned int *)&rlen);
+
+ if (memcmp(inbuf, sign, 20) != 0)
+ return;
+
+ /* skip sequence number */
+ p = strchr(p, ' ');
+ if (p == NULL)
+ return;
+
+ p++;
+
+ syslog(LOG_INFO, "%s", p);
+
+ return;
+}
blob - /dev/null
blob + 20995730060390f55aa744ff4739c537eb939e24 (mode 644)
--- /dev/null
+++ main.c
@@ -0,0 +1,3682 @@
+/*
+ * Copyright (c) 2002-2014 Peter J. Philipp
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+#include "include.h"
+#include "dns.h"
+#include "db.h"
+#include "config.h"
+
+/* prototypes */
+
+extern void add_rrlimit(int, u_int16_t *, int, char *);
+extern void axfrloop(int *, int, char **, DB *);
+extern int check_rrlimit(int, u_int16_t *, int, char *);
+extern void collects_init(void);
+extern void dolog(int, char *, ...);
+extern int find_filter(struct sockaddr_storage *, int);
+extern int find_recurse(struct sockaddr_storage *, int);
+extern u_int8_t find_region(struct sockaddr_storage *, int);
+extern int find_whitelist(struct sockaddr_storage *, int);
+extern int find_wildcard(struct sockaddr_storage *, int);
+extern void init_wildcard(void);
+extern void init_recurse(void);
+extern void init_region(void);
+extern void init_filter(void);
+extern void init_notifyslave(void);
+extern void init_whitelist(void);
+extern void recurseloop(int sp, int *, DB *);
+extern void receivelog(char *, int);
+extern int reply_a(struct sreply *, DB *);
+extern int reply_aaaa(struct sreply *, DB *);
+extern int reply_any(struct sreply *);
+extern int reply_cname(struct sreply *);
+extern int reply_fmterror(struct sreply *);
+extern int reply_notimpl(struct sreply *);
+extern int reply_nxdomain(struct sreply *);
+extern int reply_noerror(struct sreply *);
+extern int reply_soa(struct sreply *);
+extern int reply_mx(struct sreply *, DB *);
+extern int reply_naptr(struct sreply *, DB *);
+extern int reply_ns(struct sreply *, DB *);
+extern int reply_ptr(struct sreply *);
+extern int reply_refused(struct sreply *);
+extern int reply_spf(struct sreply *);
+extern int reply_srv(struct sreply *, DB *);
+extern int reply_sshfp(struct sreply *);
+extern int reply_txt(struct sreply *);
+extern int remotelog(int, char *, ...);
+extern char *rrlimit_setup(int);
+
+struct question *build_fake_question(char *, int, u_int16_t);
+struct question *build_question(char *, int, int);
+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 *, char *);
+int compress_label(u_char *, u_int16_t, int);
+u_int16_t check_qtype(struct domain *, u_int16_t, int, int *);
+char *dns_label(char *, int *);
+int free_question(struct question *);
+char *get_dns_type(int dnstype);
+int get_soa(DB *, struct question *, struct domain *, int);
+int lookup_zone(DB *, struct question *, struct domain *, int *, char *, int);
+void mainloop(struct cfg *);
+void master_reload(int);
+void master_shutdown(int);
+int memcasecmp(u_char *, u_char *, int);
+void recurseheader(struct srecurseheader *, int, struct sockaddr_storage *, struct sockaddr_storage *, int);
+void setup_master(DB *, DB_ENV *, char **);
+void slave_signal(int);
+void slave_shutdown(void);
+
+/* aliases */
+
+#ifndef DEFAULT_PRIVILEGE
+#define DEFAULT_PRIVILEGE "_ddd"
+#endif
+
+#define PIDFILE "/var/run/delphinusdnsd.pid"
+#define MYDB_PATH "/var/db/delphinusdns"
+
+
+struct typetable {
+ char *type;
+ int number;
+} TT[] = {
+ { "A", DNS_TYPE_A},
+ { "NS", DNS_TYPE_NS},
+ { "CNAME", DNS_TYPE_CNAME},
+ { "SOA", DNS_TYPE_SOA},
+ { "PTR", DNS_TYPE_PTR},
+ { "MX", DNS_TYPE_MX},
+ { "TXT", DNS_TYPE_TXT},
+ { "AAAA", DNS_TYPE_AAAA},
+ { "ANY", DNS_TYPE_ANY },
+ { "SRV", DNS_TYPE_SRV },
+ { "SPF", DNS_TYPE_SPF },
+ { "SSHFP", DNS_TYPE_SSHFP },
+ { "NAPTR", DNS_TYPE_NAPTR },
+ { NULL, 0}
+};
+
+
+/* global variables */
+
+extern char *__progname;
+extern struct logging logging;
+extern int axfrport;
+extern int ratelimit;
+extern int ratelimit_packets_per_second;
+extern int whitelist;
+
+static int *ptr = NULL;
+static int reload = 0;
+static int mshutdown = 0;
+static int msig;
+static char *database;
+static char mydatabase[512];
+static char *rptr;
+static int ratelimit_backlog;
+
+int debug = 0;
+int verbose = 0;
+int bflag = 0;
+int iflag = 0;
+int lflag = 0;
+int nflag = 0;
+int rflag = 0;
+int bcount = 0;
+int icount = 0;
+u_int16_t port = 53;
+u_int32_t cachesize = 0;
+char *bind_list[255];
+char *interface_list[255];
+
+/* singly linked list for tcp operations */
+SLIST_HEAD(listhead, tcps) tcpshead;
+
+static struct tcps {
+ char *input;
+ char *ident;
+ char *address;
+ int offset;
+ int length;
+ int maxlen;
+ int so;
+ int isv6;
+ u_int8_t region;
+ int wildcard;
+ time_t time;
+ SLIST_ENTRY(tcps) tcps_entry;
+} *tn1, *tnp, *tntmp;
+
+
+static const char rcsid[] = "$Id: main.c,v 1.1.1.1 2014/11/14 08:09:04 pjp Exp $";
+
+/*
+ * MAIN - set up arguments, set up database, set up sockets, call mainloop
+ *
+ */
+
+int
+main(int argc, char *argv[])
+{
+ static int udp[DEFAULT_SOCKET];
+ static int tcp[DEFAULT_SOCKET];
+ static int afd[DEFAULT_SOCKET];
+ static int uafd[DEFAULT_SOCKET];
+ int raw[2];
+ int lfd = -1;
+ int fd, n;
+
+ int ch, i, j;
+ int gai_error;
+ int salen, ret;
+ int found = 0;
+ int on = 1;
+ int sp[2];
+
+ pid_t pid;
+
+ static char *ident[DEFAULT_SOCKET];
+ char *conffile = CONFFILE;
+ char buf[512];
+ char **av = NULL;
+
+ struct passwd *pw;
+ struct addrinfo hints, *res0, *res;
+ struct ifaddrs *ifap, *pifap;
+ struct sockaddr_in *sin;
+ struct sockaddr_in6 *sin6;
+ struct cfg *cfg;
+
+ static DB_ENV *dbenv;
+ static DB *db;
+
+ key_t key;
+
+ if (geteuid() != 0) {
+ fprintf(stderr, "must be started as root\n"); /* .. dolt */
+ exit(1);
+ }
+
+ av = argv;
+
+ while ((ch = getopt(argc, argv, "b:c:df:i:ln:p:rv")) != -1) {
+ switch (ch) {
+ case 'b':
+ bflag = 1;
+ if (bcount > 253) {
+ fprintf(stderr, "too many -b flags\n");
+ exit(1);
+ }
+ bind_list[bcount++] = optarg;
+ break;
+ case 'c':
+#if !defined __OpenBSD__
+ cachesize = atoi(optarg);
+#else
+ cachesize = strtonum(optarg, 1, 0xffffffff, NULL);
+#endif
+ break;
+ case 'd':
+ debug = 1;
+ break;
+ case 'f':
+ conffile = optarg;
+ break;
+ case 'i':
+ iflag = 1;
+ if (icount > 254) {
+ fprintf(stderr, "too many -i flags\n");
+ exit(1);
+ }
+ interface_list[icount++] = optarg;
+ break;
+ case 'l':
+ lflag = 1;
+ break;
+ case 'n':
+ nflag = atoi(optarg);
+ break;
+ case 'p':
+ port = atoi(optarg) & 0xffff;
+ break;
+ case 'r':
+ rflag = 1;
+ break;
+ case 'v':
+ verbose++;
+ break;
+ default:
+ fprintf(stderr, "usage: delphinusdnsd [-i interface] [-b bindaddress] [-f configfile] [-p portnumber] [-drv]\n");
+ exit (1);
+ }
+ }
+
+ if (bflag && iflag) {
+ fprintf(stderr, "you may specify -i or -b but not both\n");
+ exit(1);
+ }
+
+ /*
+ * calling daemon before a sleuth of configurations ala rwhod.c
+ */
+
+ if (! debug)
+ daemon(0,0);
+
+
+ openlog(__progname, LOG_PID | LOG_NDELAY, LOG_DAEMON);
+ dolog(LOG_INFO, "starting up\n");
+
+ /* cfg struct */
+ cfg = calloc(1, sizeof(struct cfg));
+ if (cfg == NULL) {
+ dolog(LOG_ERR, "calloc: %s\n", strerror(errno));
+ exit(1);
+ }
+
+ /*
+ * make a shared memory segment for signaling kills between
+ * processes...
+ */
+
+
+ ptr = mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED |\
+ MAP_ANON, -1, 0);
+
+ if (ptr == MAP_FAILED) {
+ dolog(LOG_ERR, "failed to setup mmap segment, exit\n");
+ exit(1);
+ }
+
+ *ptr = 0;
+
+ if ((ret = db_env_create(&dbenv, 0)) != 0) {
+ dolog(LOG_INFO, "db_env_create: %s\n", db_strerror(ret));
+ slave_shutdown();
+ exit(1);
+ }
+
+ key = ftok("/usr/local/sbin/delphinusdnsd", 1);
+ if (key == (key_t)-1) {
+ dolog(LOG_INFO, "ftok failed, does /usr/local/sbin/delphinusdnsd exist?\n");
+ slave_shutdown();
+ exit(1);
+ }
+
+
+ if ((ret = dbenv->set_shm_key(dbenv, key)) != 0) {
+ dolog(LOG_INFO, "dbenv->set_shm_key failed\n");
+ slave_shutdown();
+ exit(1);
+ }
+
+ /* set cache size , if requested */
+
+ if (cachesize) {
+ if ((ret = dbenv->set_cachesize(dbenv, 0, cachesize, 0)) != 0) {
+ dolog(LOG_INFO, "dbenv->set_cachesize: %s\n",
+ db_strerror(ret));
+ slave_shutdown();
+ exit(1);
+ }
+ }
+
+ (void)mkdir(MYDB_PATH, 0700);
+ snprintf(mydatabase, sizeof(mydatabase), "%s/%ld",
+ MYDB_PATH, (long)getpid());
+
+ if (mkdir(mydatabase, 0750) < 0) {
+ if (errno != EEXIST) {
+ dolog(LOG_ERR, "mkdir: %s\n", strerror(errno));
+ exit(1);
+ }
+ }
+
+ if ((ret = dbenv->open(dbenv, mydatabase, DB_CREATE | \
+ DB_INIT_LOCK | DB_INIT_MPOOL | DB_SYSTEM_MEM, \
+ S_IRUSR | S_IWUSR)) != 0) {
+ dolog(LOG_INFO, "dbenv->open failed: %s\n", db_strerror(ret));
+ slave_shutdown();
+ exit(1);
+ }
+
+ if (db_create((DB **)&db, (DB_ENV *)dbenv, 0) != 0) {
+ dolog(LOG_INFO, "db_create: %s\n", strerror(errno));
+ slave_shutdown();
+ exit(1);
+ }
+
+ /*
+ * we want to run multiple instances of different versions so we'll
+ * make a temporary database...
+ */
+
+
+ snprintf(mydatabase, sizeof(mydatabase), "%s/%ld/wdns.db",
+ MYDB_PATH, (long)getpid());
+
+ (void)unlink(mydatabase);
+
+ database = mydatabase;
+
+
+ fd = open(database, O_WRONLY | O_CREAT, 0600);
+ if (fd < 0) {
+ dolog(LOG_INFO, "open: %s\n", strerror(errno));
+ }
+ close(fd);
+
+ if (db->open(db, NULL, database, NULL, DB_BTREE, DB_CREATE, 0600) != 0) {
+ dolog(LOG_INFO, "db->open: %s\n", strerror(errno));
+ db->close(db, DB_NOSYNC);
+ slave_shutdown();
+ exit(1);
+ }
+
+ /* make a master program that holds the pidfile, boss of ... eek */
+
+ pid = fork();
+ switch (pid) {
+ case -1:
+ dolog(LOG_ERR, "fork(): %s\n", strerror(errno));
+ exit(1);
+ case 0:
+ break;
+ default:
+ setup_master(db, dbenv, av);
+ /* NOTREACHED */
+ exit(1);
+ }
+
+ /* end of setup_master code */
+
+ init_wildcard();
+ init_recurse();
+ init_region();
+ init_filter();
+ init_whitelist();
+ init_notifyslave();
+
+ if (parse_file(db, conffile) < 0) {
+ dolog(LOG_INFO, "parsing config file failed\n");
+ slave_shutdown();
+ exit(1);
+ }
+
+ /* ratelimiting setup */
+ if (ratelimit) {
+ ratelimit_backlog = ratelimit_packets_per_second * 2;
+ rptr = rrlimit_setup(ratelimit_backlog);
+ if (rptr == NULL) {
+ dolog(LOG_INFO, "ratelimiting error\n");
+ slave_shutdown();
+ exit(1);
+ }
+ }
+
+
+ pw = getpwnam(DEFAULT_PRIVILEGE);
+ if (pw == NULL) {
+ dolog(LOG_INFO, "getpwnam: %s\n", strerror(errno));
+ slave_shutdown();
+ exit(1);
+ }
+
+ if (bcount > DEFAULT_SOCKET) {
+ dolog(LOG_INFO, "not enough sockets available\n");
+ slave_shutdown();
+ exit(1);
+ }
+
+ if (bflag) {
+ for (i = 0; i < bcount; i++) {
+ memset(&hints, 0, sizeof(hints));
+
+ if (strchr(bind_list[i], ':') != NULL) {
+ hints.ai_family = AF_INET6;
+ } else {
+ hints.ai_family = AF_INET;
+ }
+
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_protocol = IPPROTO_UDP;
+ hints.ai_flags = AI_NUMERICHOST;
+
+ snprintf(buf, sizeof(buf) - 1, "%u", port);
+
+ if ((gai_error = getaddrinfo(bind_list[i], buf, &hints, &res0)) != 0) {
+ dolog(LOG_INFO, "getaddrinfo: %s\n", gai_strerror(gai_error));
+ slave_shutdown();
+ exit (1);
+ }
+
+ res = res0;
+
+ if ((udp[i] = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) < 0) {
+ dolog(LOG_INFO, "socket: %s\n", strerror(errno));
+ slave_shutdown();
+ exit(1);
+ }
+
+ if (bind(udp[i], res->ai_addr, res->ai_addrlen) < 0) {
+ dolog(LOG_INFO, "bind: %s\n", strerror(errno));
+ slave_shutdown();
+ exit(1);
+ }
+
+ if (res->ai_family == AF_INET) {
+#ifdef __NetBSD__
+ if (setsockopt(udp[i], IPPROTO_IP, IP_TTL,
+ &on, sizeof(on)) < 0) {
+#else
+ if (setsockopt(udp[i], IPPROTO_IP, IP_RECVTTL,
+ &on, sizeof(on)) < 0) {
+#endif
+ dolog(LOG_INFO, "setsockopt: %s\n", strerror(errno));
+ }
+ } else if (res->ai_family == AF_INET6) {
+ /* RFC 3542 page 30 */
+ on = 1;
+ if (setsockopt(udp[i], IPPROTO_IPV6,
+ IPV6_RECVHOPLIMIT, &on, sizeof(on)) < 0) {
+ dolog(LOG_INFO, "setsockopt: %s\n", strerror(errno));
+ }
+ }
+
+ ident[i] = bind_list[i];
+
+ /* tcp below */
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = IPPROTO_TCP;
+ hints.ai_flags = AI_NUMERICHOST;
+
+ snprintf(buf, sizeof(buf) - 1, "%u", port);
+
+ if ((gai_error = getaddrinfo(bind_list[i], buf, &hints, &res0)) != 0) {
+ dolog(LOG_INFO, "getaddrinfo: %s\n", gai_strerror(gai_error));
+ slave_shutdown();
+ exit (1);
+ }
+
+ res = res0;
+
+ if ((tcp[i] = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) < 0) {
+ dolog(LOG_INFO, "tcp socket: %s\n", strerror(errno));
+ slave_shutdown();
+ exit(1);
+ }
+ if (setsockopt(tcp[i], SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
+ dolog(LOG_INFO, "setsockopt: %s\n", strerror(errno));
+ slave_shutdown();
+ exit(1);
+ }
+ if (bind(tcp[i], res->ai_addr, res->ai_addrlen) < 0) {
+ dolog(LOG_INFO, "tcp bind: %s\n", strerror(errno));
+ slave_shutdown();
+ exit(1);
+ }
+
+ if (axfrport) {
+ /* axfr port below */
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = IPPROTO_TCP;
+ hints.ai_flags = AI_NUMERICHOST;
+
+ snprintf(buf, sizeof(buf) - 1, "%u", axfrport);
+
+ if ((gai_error = getaddrinfo(bind_list[i], buf, &hints, &res0)) != 0) {
+ dolog(LOG_INFO, "getaddrinfo: %s\n", gai_strerror(gai_error));
+ slave_shutdown();
+ exit (1);
+ }
+
+ res = res0;
+
+ if ((afd[i] = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) < 0) {
+ dolog(LOG_INFO, "tcp socket: %s\n", strerror(errno));
+ slave_shutdown();
+ exit(1);
+ }
+ if (setsockopt(afd[i], SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
+ dolog(LOG_INFO, "setsockopt: %s\n", strerror(errno));
+ slave_shutdown();
+ exit(1);
+ }
+ if (bind(afd[i], res->ai_addr, res->ai_addrlen) < 0) {
+ dolog(LOG_INFO, "tcp bind: %s\n", strerror(errno));
+ slave_shutdown();
+ exit(1);
+ }
+
+ if ((uafd[i] = socket(res->ai_family, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
+ dolog(LOG_INFO, "axfr udp socket: %s\n", strerror(errno));
+ slave_shutdown();
+ exit(1);
+ }
+ if (bind(uafd[i], res->ai_addr, res->ai_addrlen) < 0) {
+ dolog(LOG_INFO, "axfr udp socket bind: %s\n", strerror(errno));
+ slave_shutdown();
+ exit(1);
+ }
+ } /* axfrport */
+
+ } /* for .. bcount */
+
+ } else {
+ if (getifaddrs(&ifap) < 0) {
+ dolog(LOG_INFO, "getifaddrs\n");
+ slave_shutdown();
+ exit(1);
+ }
+
+ for (pifap = ifap, i = 0; i < DEFAULT_SOCKET && pifap; pifap = pifap->ifa_next, i++) {
+
+ found = 0;
+
+ /* we want only one interface not the rest */
+ if (icount > 0) {
+ for (j = 0; j < icount; j++) {
+ if (strcmp(pifap->ifa_name, interface_list[j]) == 0) {
+ found = 1;
+ }
+ }
+
+ if (! found) {
+ i--;
+ continue;
+ }
+
+ }
+ if ((pifap->ifa_flags & IFF_UP) != IFF_UP) {
+ dolog(LOG_INFO, "skipping interface %s\n", pifap->ifa_name);
+ i--;
+ continue;
+ }
+
+ if (pifap->ifa_addr->sa_family == AF_INET) {
+ sin = (struct sockaddr_in *)pifap->ifa_addr;
+ sin->sin_port = htons(port);
+ salen = sizeof(struct sockaddr_in);
+ /* no address bound to this interface */
+ if (sin->sin_addr.s_addr == INADDR_ANY) {
+ i--;
+ continue;
+ }
+ } else if (pifap->ifa_addr->sa_family == AF_INET6) {
+ sin6 = (struct sockaddr_in6 *)pifap->ifa_addr;
+ sin6->sin6_port = htons(port);
+ /* no address bound to this interface */
+ salen = sizeof(struct sockaddr_in6);
+
+ } else {
+ dolog(LOG_DEBUG, "unknown address family %d\n", pifap->ifa_addr->sa_family);
+ i--;
+ continue;
+ }
+
+
+ if ((udp[i] = socket(pifap->ifa_addr->sa_family, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
+ dolog(LOG_INFO, "socket: %s\n", strerror(errno));
+ slave_shutdown();
+ exit(1);
+ }
+
+ if (bind(udp[i], (struct sockaddr *)pifap->ifa_addr, salen) < 0) {
+ dolog(LOG_INFO, "bind: %s\n", strerror(errno));
+ slave_shutdown();
+ exit(1);
+ }
+
+ if (pifap->ifa_addr->sa_family == AF_INET) {
+#ifdef __NetBSD__
+ if (setsockopt(udp[i], IPPROTO_IP, IP_TTL,
+ &on, sizeof(on)) < 0) {
+#else
+ if (setsockopt(udp[i], IPPROTO_IP, IP_RECVTTL,
+ &on, sizeof(on)) < 0) {
+#endif
+ dolog(LOG_INFO, "setsockopt: %s\n", strerror(errno));
+ }
+ } else if (pifap->ifa_addr->sa_family == AF_INET6) {
+ /* RFC 3542 page 30 */
+ on = 1;
+ if (setsockopt(udp[i], IPPROTO_IPV6,
+ IPV6_RECVHOPLIMIT, &on, sizeof(on)) < 0) {
+ dolog(LOG_INFO, "setsockopt: %s\n", strerror(errno));
+ }
+ }
+
+
+ ident[i] = pifap->ifa_name;
+
+ if ((tcp[i] = socket(pifap->ifa_addr->sa_family, SOCK_STREAM, IPPROTO_TCP)) < 0) {
+ dolog(LOG_INFO, "tcp socket: %s\n", strerror(errno));
+ slave_shutdown();
+ exit(1);
+ }
+ if (setsockopt(tcp[i], SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
+ dolog(LOG_INFO, "setsockopt: %s\n", strerror(errno));
+ slave_shutdown();
+ exit(1);
+ }
+
+ if (bind(tcp[i], (struct sockaddr *)pifap->ifa_addr, salen) < 0) {
+ dolog(LOG_INFO, "tcp bind: %s\n", strerror(errno));
+ slave_shutdown();
+ exit(1);
+ }
+
+
+ /* axfr socket */
+ if (axfrport) {
+ if ((afd[i] = socket(pifap->ifa_addr->sa_family, SOCK_STREAM, IPPROTO_TCP)) < 0) {
+ dolog(LOG_INFO, "tcp socket: %s\n", strerror(errno));
+ slave_shutdown();
+ exit(1);
+ }
+ if (setsockopt(afd[i], SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
+ dolog(LOG_INFO, "setsockopt: %s\n", strerror(errno));
+ slave_shutdown();
+ exit(1);
+ }
+
+ ((struct sockaddr_in *)pifap->ifa_addr)->sin_port = htons(axfrport);
+
+ if (bind(afd[i], (struct sockaddr *)pifap->ifa_addr, salen) < 0) {
+ dolog(LOG_INFO, "tcp bind: %s\n", strerror(errno));
+ slave_shutdown();
+ exit(1);
+ }
+ if ((uafd[i] = socket(pifap->ifa_addr->sa_family, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
+ dolog(LOG_INFO, "axfr udp socket: %s\n", strerror(errno));
+ slave_shutdown();
+ exit(1);
+ }
+ if (bind(uafd[i], (struct sockaddr *)pifap->ifa_addr, salen) < 0) {
+ dolog(LOG_INFO, "udp axfr bind: %s\n", strerror(errno));
+ slave_shutdown();
+ exit(1);
+ }
+ } /* axfrport */
+
+ } /* AF_INET */
+
+ if (i >= DEFAULT_SOCKET) {
+ dolog(LOG_INFO, "not enough sockets available\n");
+ slave_shutdown();
+ exit(1);
+ }
+ } /* if bflag? */
+
+ if (rflag == 1) {
+ if ((raw[0] = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) {
+ dolog(LOG_INFO, "raw socket: %s\n", strerror(errno));
+ slave_shutdown();
+ exit(1);
+ }
+
+ if (setsockopt(raw[0], IPPROTO_IP, IP_HDRINCL, &on, sizeof(on)) < 0) {
+ dolog(LOG_INFO, "raw setsockopt: %s\n", strerror(errno));
+ slave_shutdown();
+ exit(1);
+ }
+
+ if ((raw[1] = socket(AF_INET6, SOCK_RAW, IPPROTO_UDP)) < 0) {
+ dolog(LOG_INFO, "raw socket[1]: %s\n", strerror(errno));
+ slave_shutdown();
+ exit(1);
+ }
+
+ } /* rflag */
+
+
+ /* if we are binding a log socket do it now */
+ if (logging.bind == 1 || logging.active == 1) {
+ switch (logging.loghost2.ss_family) {
+ case AF_INET:
+ lfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (lfd < 0) {
+ dolog(LOG_INFO, "logging socket: %s\n", strerror(errno));
+ slave_shutdown();
+ exit(1);
+ }
+ sin = (struct sockaddr_in *)&logging.loghost2;
+ sin->sin_port = htons(logging.logport2);
+ break;
+ case AF_INET6:
+ lfd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
+ if (lfd < 0) {
+ dolog(LOG_INFO, "logging socket: %s\n", strerror(errno));
+ slave_shutdown();
+ exit(1);
+ }
+ sin6 = (struct sockaddr_in6 *)&logging.loghost2;
+ sin6->sin6_port = htons(logging.logport2);
+ break;
+ }
+
+ if (logging.bind == 1) {
+ if (bind(lfd, (struct sockaddr *)&logging.loghost2,
+ ((logging.loghost2.ss_family == AF_INET6) ?
+ sizeof(struct sockaddr_in6) :
+ sizeof(struct sockaddr_in))
+ ) < 0) {
+ dolog(LOG_INFO, "binding log socket: %s\n", strerror(errno));
+ slave_shutdown();
+ exit(1);
+ }
+
+#ifndef __linux__
+ if (shutdown(lfd, SHUT_WR) < 0) {
+ dolog(LOG_INFO, "shutdown log socket: %s\n", strerror(errno));
+ slave_shutdown();
+ exit(1);
+ }
+#endif
+
+ } else {
+ if (connect(lfd, (struct sockaddr *)&logging.loghost2,
+ ((logging.loghost2.ss_family == AF_INET6) ?
+ sizeof(struct sockaddr_in6) :
+ sizeof(struct sockaddr_in))) < 0) {
+ dolog(LOG_INFO, "connecting log socket: %s\n", strerror(errno));
+ slave_shutdown();
+ exit(1);
+ }
+
+ if (shutdown(lfd, SHUT_RD) < 0) {
+ dolog(LOG_INFO, "shutdown log socket: %s\n", strerror(errno));
+ slave_shutdown();
+ exit(1);
+ }
+
+ } /* if logging.bind */
+
+ } /* if logging.bind */
+
+ /* chroot to the drop priv user home directory */
+ if (chroot(pw->pw_dir) < 0) {
+ dolog(LOG_INFO, "chroot: %s\n", strerror(errno));
+ slave_shutdown();
+ exit(1);
+ }
+
+ if (chdir("/") < 0) {
+ dolog(LOG_INFO, "chdir: %s\n", strerror(errno));
+ slave_shutdown();
+ exit(1);
+ }
+
+ /*
+ * add signals
+ */
+
+ signal(SIGPIPE, SIG_IGN);
+
+ signal(SIGTERM, slave_signal);
+ signal(SIGINT, slave_signal);
+ signal(SIGQUIT, slave_signal);
+
+ /*
+ * I open the log again after the chroot just in case I can't
+ * reach the old /dev/log anymore.
+ */
+
+ closelog();
+ openlog(__progname, LOG_PID | LOG_NDELAY, LOG_DAEMON);
+
+ /* set groups */
+
+ if (setgroups(1, &pw->pw_gid) < 0) {
+ dolog(LOG_INFO, "setgroups: %s\n", strerror(errno));
+ slave_shutdown();
+ exit(1);
+ }
+
+#if defined __OpenBSD__ || defined __FreeBSD__
+ if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) < 0) {
+ dolog(LOG_INFO, "setresgid: %s\n", strerror(errno));
+ slave_shutdown();
+ exit(1);
+ }
+
+ if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) < 0) {
+ dolog(LOG_INFO, "setresuid: %s\n", strerror(errno));
+ slave_shutdown();
+ exit(1);
+ }
+
+#else
+ if (setgid(pw->pw_gid) < 0) {
+ dolog(LOG_INFO, "setgid: %s\n", strerror(errno));
+ slave_shutdown();
+ exit(1);
+ }
+ if (setuid(pw->pw_uid) < 0) {
+ dolog(LOG_INFO, "setuid: %s\n", strerror(errno));
+ slave_shutdown();
+ exit(1);
+ }
+#endif
+
+ /*
+ * start our axfr process
+ */
+
+ if (axfrport) {
+ switch (pid = fork()) {
+ case 0:
+ /* close descriptors that we don't need */
+ for (j = 0; j < i; j++) {
+ close(tcp[j]);
+ close(udp[j]);
+ close(uafd[j]);
+ }
+
+ if (rflag) {
+ close(raw[0]);
+ close(raw[1]);
+ }
+
+#if !defined __linux__ && !defined __APPLE__
+ setproctitle("AXFR engine on port %d", axfrport);
+#endif
+
+ axfrloop(afd, i, ident, db);
+ /* NOTREACHED */
+ exit(1);
+ default:
+ /* close afd descriptors, they aren't needed here */
+ for (j = 0; j < i; j++) {
+ close(afd[j]);
+ }
+
+ break;
+ }
+
+ } /* axfrport */
+
+ /* what follows is a bit mangled code, we set up nflag + 1 amount of
+ * server instances (1 per cpu?) and if we're recursive we also set up
+ * the same amount of recursive instances all connected through a
+ * socketpair() so that it looks somewhat like this (with 4 instances):
+ *
+ * replies <--- [] ---- [] recursive end
+ * |
+ * replies <--- [] ---- []
+ * request * ---> |
+ * replies <--- [] ---- []
+ * |
+ * replies <--- [] ---- []
+ *
+ */
+
+ for (n = 0; n < nflag; n++) {
+ switch (pid = fork()) {
+ case 0:
+ if (rflag) {
+ /*
+ * set up socket pair
+ */
+
+ if (socketpair(AF_UNIX, SOCK_DGRAM, 0, (int *)&sp) < 0) {
+ dolog(LOG_INFO, "socketpair: %s\n", strerror(errno));
+ slave_shutdown();
+ exit(1);
+ }
+
+ switch (pid = fork()) {
+ case -1:
+ dolog(LOG_INFO, "fork: %s\n", strerror(errno));
+ slave_shutdown();
+ exit(1);
+
+ case 0:
+ for (j = 0; j < i; j++) {
+ close(tcp[j]);
+ close(udp[j]);
+ }
+ close (sp[1]);
+
+ recurseloop(sp[0], (int *)&raw, db);
+ /* NOTREACHED */
+ break;
+
+ default:
+ close(raw[0]);
+ close(raw[1]);
+ close (sp[0]);
+ break;
+ } /* switch */
+ } /* rflag */
+
+
+ cfg->sockcount = i;
+ cfg->db = db;
+ for (i = 0; i < cfg->sockcount; i++) {
+ cfg->udp[i] = udp[i];
+ cfg->tcp[i] = tcp[i];
+
+ if (axfrport)
+ cfg->axfr[i] = uafd[i];
+
+ cfg->ident[i] = strdup(ident[i]);
+ }
+ cfg->recurse = (rflag ? sp[1] : -1);
+ cfg->log = lfd;
+
+
+ (void)mainloop(cfg);
+
+ /* NOTREACHED */
+ default:
+ break;
+ } /* switch pid= fork */
+ } /* for (.. nflag */
+
+ if (rflag) {
+ /*
+ * set up socket pair
+ */
+
+ if (socketpair(AF_UNIX, SOCK_DGRAM, 0, (int *)&sp) < 0) {
+ dolog(LOG_INFO, "socketpair: %s\n", strerror(errno));
+ slave_shutdown();
+ exit(1);
+ }
+
+ switch (pid = fork()) {
+ case -1:
+ dolog(LOG_INFO, "fork: %s\n", strerror(errno));
+ slave_shutdown();
+ exit(1);
+
+ case 0:
+ for (j = 0; j < i; j++) {
+ close(tcp[j]);
+ close(udp[j]);
+ close(uafd[j]);
+ }
+ close (sp[1]);
+
+ recurseloop(sp[0], (int *)&raw, db);
+ /* NOTREACHED */
+ break;
+
+ default:
+ close(raw[0]);
+ close(raw[1]);
+ close (sp[0]);
+ break;
+ } /* switch */
+
+ } /* rflag */
+
+
+ cfg->sockcount = i;
+ cfg->db = db;
+ for (i = 0; i < cfg->sockcount; i++) {
+ cfg->udp[i] = udp[i];
+ cfg->tcp[i] = tcp[i];
+
+ if (axfrport)
+ cfg->axfr[i] = uafd[i];
+
+ cfg->ident[i] = strdup(ident[i]);
+ }
+ cfg->recurse = (rflag ? sp[1] : -1);
+ cfg->log = lfd;
+
+
+ (void)mainloop(cfg);
+
+ /* NOTREACHED */
+ return (0);
+}
+
+
+/*
+ * BUILD_QUESTION - fill the question structure with the DNS query.
+ */
+
+struct question *
+build_question(char *buf, int len, int additional)
+{
+ u_int i;
+ u_int namelen = 0;
+ u_int16_t *qtype, *qclass;
+ int num_label;
+
+ char *p, *end_name = NULL;
+
+ struct dns_optrr *opt = NULL;
+ struct question *q = NULL;
+
+ /* find the end of name */
+ for (i = sizeof(struct dns_header); i < len; i++) {
+ /* XXX */
+ if (buf[i] == 0) {
+ end_name = &buf[i];
+ break;
+ }
+ }
+
+ /*
+ * implies i >= len , because end_name still points to NULL and not
+ * &buf[i]
+ */
+
+ if (end_name == NULL) {
+ dolog(LOG_INFO, "query name is not null terminated\n");
+ return NULL;
+ }
+
+ /* parse the size of the name */
+ for (i = sizeof(struct dns_header), num_label = 0; i < len && &buf[i] < end_name;) {
+ u_int labellen;
+
+ ++num_label;
+
+ labellen = (u_int)buf[i];
+
+ /*
+ * do some checks on the label, if it's 0 or over 63 it's
+ * illegal, also if it reaches beyond the entire name it's
+ * also illegal.
+ */
+ if (labellen == 0) {
+ dolog(LOG_INFO, "illegal label len (0)\n");
+ return NULL;
+ }
+ if (labellen > DNS_MAXLABEL) {
+ dolog(LOG_INFO, "illegal label len (> 63)\n");
+ return NULL;
+ }
+ if (labellen > (end_name - &buf[i])) {
+ dolog(LOG_INFO, "label len extends beyond name\n");
+ return NULL;
+ }
+
+ i += (labellen + 1);
+ namelen += labellen;
+ }
+
+ if (&buf[i] != end_name || i >= len) {
+ dolog(LOG_INFO, "query name is maliciously malformed\n");
+ return NULL;
+ }
+
+ if (i > DNS_MAXNAME) {
+ dolog(LOG_INFO, "query name is too long (%u)\n", i);
+ return NULL;
+ }
+
+
+ /* check if there is space for qtype and qclass */
+ if (len < ((end_name - &buf[0]) + (2 * sizeof(u_int16_t)))) {
+ dolog(LOG_INFO, "question rr is truncated\n");
+ return NULL;
+ }
+
+
+ q = (void *)calloc(1, sizeof(struct question));
+ if (q == NULL) {
+ dolog(LOG_INFO, "calloc: %s\n", strerror(errno));
+ return NULL;
+ }
+ q->hdr = (void *)calloc(1, sizeof(struct dns_question_hdr));
+ if (q->hdr == NULL) {
+ dolog(LOG_INFO, "calloc: %s\n", strerror(errno));
+ free(q);
+ return NULL;
+ }
+ q->hdr->namelen = (end_name - &buf[sizeof(struct dns_header)]) + 1; /* XXX */
+ q->hdr->name = (void *) calloc(1, q->hdr->namelen);
+ if (q->hdr->name == NULL) {
+ dolog(LOG_INFO, "calloc: %s\n", strerror(errno));
+ free(q->hdr);
+ free(q);
+ return NULL;
+ }
+ q->converted_name = (void *)calloc(1, namelen + num_label + 2);
+ if (q->converted_name == NULL) {
+ dolog(LOG_INFO, "calloc: %s\n", strerror(errno));
+ free(q->hdr->name);
+ free(q->hdr);
+ free(q);
+ return NULL;
+ }
+
+ p = q->converted_name;
+
+ /*
+ * parse the name again this time filling the labels
+ * XXX this is expensive going over the buffer twice
+ */
+ for (i = sizeof(struct dns_header); i < len && &buf[i] < end_name;) {
+ u_int labelend;
+
+
+ /* check for compression */
+ if ((buf[i] & 0xc0) == 0xc0) {
+ dolog(LOG_INFO, "question has compressed name, drop\n");
+ free_question(q);
+ return NULL; /* XXX should say error */
+ }
+
+ labelend = (u_int)buf[i] + 1 + i; /* i = offset, plus contents of buf[i], + 1 */
+
+ /*
+ * i is reused here to count every character, this is not
+ * a bug!
+ */
+
+ for (i++; i < labelend; i++) {
+ int c0;
+
+ c0 = buf[i];
+ *p++ = tolower(c0);
+ }
+
+ *p++ = '.';
+ }
+
+ /* XXX */
+ if (&buf[sizeof(struct dns_header)] == end_name)
+ *p++ = '.';
+
+ *p = '\0';
+
+ /* check for edns0 opt rr */
+ do {
+ /* if we don't have an additional section, break */
+ if (additional != 1)
+ break;
+
+ i += (2 * sizeof(u_int16_t)) + 1;
+
+ /* check that the minimum optrr fits */
+ /* 10 */
+ if (i + sizeof(struct dns_optrr) > len)
+ break;
+
+ opt = (struct dns_optrr *)&buf[i];
+ if (opt->name[0] != 0)
+ break;
+
+ if (ntohs(opt->type) != DNS_TYPE_OPT)
+ break;
+
+ /* if we got options here I don't want to know about them */
+ if (ntohs(opt->rdlen) > 0)
+ break;
+
+ /* RFC 3225 */
+ if (ntohl(opt->ttl) & DNSSEC_OK)
+ q->dnssecok = 1;
+ else if (ntohl(opt->ttl) != 0)
+ break;
+
+ q->edns0len = ntohs(opt->class);
+ if (q->edns0len < 512)
+ q->edns0len = 512; /* RFC 6891 - page 10 */
+
+ } while (0);
+
+ /* fill our name into the dns header struct */
+
+ memcpy(q->hdr->name, &buf[sizeof(struct dns_header)], q->hdr->namelen);
+
+ /* make it lower case */
+
+ for (i = 0; i < q->hdr->namelen; i++) {
+ int c0;
+
+ c0 = q->hdr->name[i];
+ if (isalpha(c0)) {
+ q->hdr->name[i] = tolower(c0);
+ }
+ }
+
+ /* parse type and class from the question */
+
+ qtype = (u_int16_t *)(end_name + 1);
+ qclass = (u_int16_t *)(end_name + sizeof(u_int16_t) + 1);
+
+ memcpy((char *)&q->hdr->qtype, (char *)qtype, sizeof(u_int16_t));
+ memcpy((char *)&q->hdr->qclass, (char *)qclass, sizeof(u_int16_t));
+
+
+ return (q);
+}
+
+/*
+ * FREE_QUESTION - free a question struct
+ *
+ */
+
+int
+free_question(struct question *q)
+{
+ free(q->hdr->name);
+ free(q->hdr);
+ free(q->converted_name);
+ free(q);
+
+ return 0;
+}
+
+/*
+ * DNS_LABEL - build a DNS NAME (with labels) from a canonical name
+ *
+ */
+
+char *
+dns_label(char *name, int *returnlen)
+{
+ int len, newlen = 0;
+ int i, lc = 0; /* lc = label count */
+
+ char *dnslabel, *p;
+ char *labels[255];
+ char **pl;
+ char tname[DNS_MAXNAME + 1]; /* 255 bytes + 1*/
+ char *pt = &tname[0];
+
+
+ if (name == NULL)
+ return NULL;
+
+#if __linux__
+ strncpy(tname, name, sizeof(tname));
+ tname[sizeof(tname) - 1] = 0;
+#else
+ strlcpy(tname, name, sizeof(tname));
+#endif
+
+ len = strlen(tname);
+ if (tname[len - 1] == '.')
+ tname[len - 1] = '\0';
+
+ for (pl=labels;pl<&labels[254]&&(*pl=strsep(&pt,"."))!= NULL;pl++,lc++)
+ newlen += strlen(*pl);
+
+ newlen += lc; /* add label count to length */
+
+
+ /* make the buffer space, add 1 for trailing NULL */
+ if ((dnslabel = malloc(newlen + 1)) == NULL) {
+ return NULL;
+ }
+
+ *returnlen = newlen + 1;
+ dnslabel[newlen] = '\0'; /* trailing NULL */
+
+ for (i = 0, p = dnslabel; i < lc; i++) {
+ len = strlen(labels[i]);
+ *p++ = len;
+#if __linux__
+ /* XXX */
+ strncpy(p, labels[i], newlen - (p - dnslabel) + 1);
+ p[newlen - (p - dnslabel)] = 0;
+#else
+ strlcpy(p, labels[i], newlen - (p - dnslabel) + 1);
+#endif
+ p += len;
+ }
+
+ /*
+ * XXX hack to make all DNS names lower case, we only preserve
+ * case on compressed answers..
+ */
+
+ for (i = 0, p = dnslabel; i < *returnlen; i++) {
+ int c;
+
+ c = *p;
+ if (isalpha(c))
+ *p = tolower(c);
+ p++;
+ }
+
+ dolog(LOG_DEBUG, "converting name= %s\n", name);
+
+ return dnslabel;
+}
+
+/*
+ * COMPRESS_LABEL - compress a DNS name, must be passed an entire reply
+ * with the to be compressed name before the offset of
+ * that reply.
+ */
+
+int
+compress_label(u_char *buf, u_int16_t offset, int labellen)
+{
+ u_char *label[256]; /* should be enough */
+ u_char *end = &buf[offset];
+ struct question {
+ u_int16_t type;
+ u_int16_t class;
+ } __attribute__((packed));
+ struct answer {
+ u_int16_t type;
+ u_int16_t class;
+ u_int32_t ttl;
+ u_int16_t rdlength;
+ } __attribute__((packed));
+ struct soa {
+ u_int32_t serial;
+ u_int32_t refresh;
+ u_int32_t retry;
+ u_int32_t expire;
+ u_int32_t minttl;
+ } __attribute__((packed));
+
+ struct answer *a;
+
+ u_int i, j;
+ u_int checklen;
+ u_int16_t *compressor;
+
+ u_char *p, *e;
+ u_char *compressmark;
+
+
+ p = &buf[sizeof(struct dns_header)];
+ label[0] = p;
+
+ while (p <= end && *p) {
+ p += *p;
+ p++;
+ }
+
+ /*
+ * the question label was bogus, we'll just get out of there, return 0
+ */
+
+ if (p >= end)
+ return (0);
+
+ p += sizeof(struct question);
+ p++; /* one more */
+ /* start of answer/additional/authoritative */
+
+ for (i = 1; i < 100; i++) {
+ label[i] = p;
+
+ while (p <= end && *p) {
+ if ((*p & 0xc0) == 0xc0) {
+ p++;
+ break;
+ }
+ p += *p;
+ p++;
+
+ if (p >= end)
+ goto end;
+ }
+
+ p++; /* one more */
+
+
+ a = (struct answer *)p;
+ p += sizeof(struct answer);
+
+ /* Thanks FreeLogic! */
+ if (p >= end)
+ goto end;
+
+ switch (ntohs(a->type)) {
+ case DNS_TYPE_A:
+ p += sizeof(in_addr_t);
+ break;
+ case DNS_TYPE_AAAA:
+ p += 16; /* sizeof 4 * 32 bit */
+ break;
+ case DNS_TYPE_TXT:
+ case DNS_TYPE_SPF:
+ p += *p;
+ p++;
+ break;
+ case DNS_TYPE_SSHFP:
+ p++;
+ switch (*p) {
+ case 1:
+ p += DNS_SSHFP_SIZE_SHA1 + 1;
+ break;
+ case 2:
+ p += DNS_SSHFP_SIZE_SHA256 + 1;
+ break;
+ default:
+ /* XXX */
+ goto end;
+ }
+
+ break;
+ case DNS_TYPE_SRV:
+ p += (2 * sizeof(u_int16_t)); /* priority, weight */
+ /* the port will be assumed in the fall through for
+ mx_priority..
+ */
+ /* FALLTHROUGH */
+ case DNS_TYPE_MX:
+ p += sizeof(u_int16_t); /* mx_priority */
+ /* FALLTHROUGH */
+ case DNS_TYPE_NS:
+ case DNS_TYPE_PTR:
+ case DNS_TYPE_CNAME:
+ label[++i] = p;
+ while (p <= end && *p) {
+ if ((*p & 0xc0) == 0xc0) {
+ p++;
+ break;
+ }
+ p += *p;
+ p++;
+
+ if (p >= end)
+ goto end;
+ }
+
+ p++; /* one more */
+ break;
+ case DNS_TYPE_SOA:
+ /* nsserver */
+ label[++i] = p;
+ while (p <= end && *p) {
+ if ((*p & 0xc0) == 0xc0) {
+ p++;
+ break;
+ }
+ p += *p;
+ p++;
+ if (p >= end)
+ goto end;
+ }
+
+ p++; /* one more */
+
+ if (p >= end)
+ break;
+
+ /* responsible person */
+ label[++i] = p;
+ while (p <= end && *p) {
+ if ((*p & 0xc0) == 0xc0) {
+ p++;
+ break;
+ }
+ p += *p;
+ p++;
+ }
+
+ p++; /* one more */
+
+ if (p >= end)
+ break;
+
+ p += sizeof(struct soa); /* advance struct soa */
+
+ break;
+ case DNS_TYPE_NAPTR:
+ p += (2 * sizeof(u_int16_t)); /* order and preference */
+ p += *p; /* flags */
+ p++;
+ p += *p; /* services */
+ p++;
+ p += *p; /* regexp */
+ p++;
+
+ label[++i] = p;
+ while (p <= end && *p) {
+ if ((*p & 0xc0) == 0xc0) {
+ p++;
+ break;
+ }
+ p += *p;
+ p++;
+
+ if (p >= end)
+ goto end;
+ }
+
+ p++; /* one more */
+ break;
+
+ default:
+ break;
+ /* XXX */
+ } /* switch */
+
+ if (p >= end)
+ break;
+ } /* for (i *) */
+
+end:
+
+ p = &buf[offset - labellen];
+ checklen = labellen;
+
+ for (;*p != 0;) {
+ for (j = 0; j < i; j++) {
+ for (e = label[j]; *e; e += *e, e++) {
+ if ((*e & 0xc0) == 0xc0)
+ break;
+
+ if (memcasecmp(e, p, checklen) == 0) {
+ /* e is now our compress offset */
+ compressmark = e;
+ goto out; /* found one */
+ }
+ } /* for (e .. */
+
+ } /* for (j .. */
+
+ if (*p > DNS_MAXLABEL)
+ return 0; /* totally bogus label */
+
+ checklen -= *p;
+ p += *p;
+ checklen--;
+ p++;
+ }
+
+ return (0); /* no compression possible */
+
+out:
+ /* take off our compress length */
+ offset -= checklen;
+ /* write compressed label */
+ compressor = (u_int16_t *)&buf[offset];
+
+ *compressor = (compressmark - &buf[0]);
+ *compressor |= 0xc000;
+
+ /* network byte order */
+ HTONS(*compressor);
+
+ offset += sizeof(u_int16_t);
+
+ return (offset);
+}
+
+/*
+ * MEMCASECMP - check if buffer is identical to another buffer with
+ * one exception if a character is alphabetic it's
+ * compared to it's lower case value so that heLLo is
+ * the same as hello
+ */
+
+int
+memcasecmp(u_char *b1, u_char *b2, int len)
+{
+ int i;
+ int identical = 1;
+
+ for (i = 0; i < len; i++) {
+ int c0, c1;
+
+ c0 = b1[i];
+ c1 = b2[i];
+
+ if ((isalpha(c0) ? tolower(c0) : c0) !=
+ (isalpha(c1) ? tolower(c1) : c1)) {
+ identical = 0;
+ break;
+ }
+ }
+
+ if (identical)
+ return 0;
+
+ return 1; /* XXX */
+}
+
+
+/*
+ * LOOKUP_ZONE - look up a zone filling sd and returning RR TYPE, if error
+ * occurs returns -1, and sets errno on what type of error.
+ */
+
+
+int
+lookup_zone(DB *db, struct question *question, struct domain *sd, int *lzerrno, char *replystring, int wildcard)
+{
+
+ int plen, onemore = 0;
+ int ret = 0;
+ int returnval, error;
+ int w = 0;
+
+ char *wildlookup = "*";
+ char *p;
+
+ DBT key, data;
+
+ /*
+ * if the asked domain name is foo.bar.baz.org then
+ * lookup foo.bar.baz.org, bar.baz.org, baz.org,
+ * org and if there is a match return that.
+ */
+
+ p = question->hdr->name;
+ plen = question->hdr->namelen;
+ onemore = 0;
+
+ returnval = 0;
+
+ do {
+
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+
+ key.data = (char *)p;
+ key.size = plen;
+
+ data.data = NULL;
+ data.size = 0;
+
+ ret = db->get(db, NULL, &key, &data, 0);
+
+ if (ret != 0) {
+ if (! wildcard)
+ w = 1;
+ /* next label */
+ if (*p != 0) {
+ plen -= (*p + 1);
+ p = (p + (*p + 1));
+ } else if (*p == 0 && ! onemore) {
+ plen = 1;
+ onemore = 1;
+ continue;
+ }
+ } else {
+ /* we have a match check if the type has an answer, if not we leave */
+ if (data.size != sizeof(struct domain)) {
+ dolog(LOG_INFO, "btree db is damaged, drop\n");
+ *lzerrno = ERR_DROP;
+ return -1;
+ }
+
+ memcpy((char *)sd, (char *)data.data, data.size);
+ snprintf(replystring, DNS_MAXNAME, "%s", sd->zonename);
+
+ /*
+ * If we're not wildcarding and ns_type is 0, NXDOMAIN
+ */
+ if (! wildcard)
+ if (w && sd->ns_type == 0) {
+ *lzerrno = ERR_NXDOMAIN;
+ return -1;
+ }
+
+ /*
+ * we're of ns_type > 0, return an NS record
+ */
+
+ if (sd->ns_type > 0) {
+ returnval = DNS_TYPE_NS;
+ *lzerrno = ERR_NOERROR;
+ goto out;
+ }
+
+ /*
+ * check if our record is dynamic (non-static)
+ * if so, we'll hand it down to the recurse
+ * process later on...
+ */
+
+ if (! (sd->flags & DOMAIN_STATIC_ZONE)) {
+ dolog(LOG_INFO, "non-static zone %s passed to recurse process\n", sd->zonename);
+ *lzerrno = ERR_NOERROR;
+ return (-1);
+ }
+
+
+ returnval = check_qtype(sd, ntohs(question->hdr->qtype), 0, &error);
+ if (returnval == 0) {
+ *lzerrno = ERR_NOERROR;
+ return (-1);
+ }
+
+ break;
+ }
+
+
+ } while (*p != 0 && ret != 0);
+
+ if (ret != 0) {
+ /*
+ * somehow we managed to get here and wildcardding is off
+ * return with NXDOMAIN
+ */
+ if (! wildcard) {
+ *lzerrno = ERR_NXDOMAIN;
+ return -1;
+ }
+
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+
+ key.data = wildlookup;
+ key.size = 1;
+
+ if ((ret = db->get(db, NULL, &key, &data, 0)) != 0) {
+ db->err(db, ret, "db->get");
+ dolog(LOG_INFO, "don't have wildcard answer\n");
+ *lzerrno = ERR_NXDOMAIN;
+ return -1;
+
+ }
+ if (data.size != sizeof(struct domain)) {
+ dolog(LOG_INFO, "btree db is damaged, drop\n");
+ *lzerrno = ERR_DROP;
+ return -1;
+ }
+
+ memcpy((char *)sd, (char *)data.data, data.size);
+
+ if (sd->ns_type > 0) {
+ returnval = DNS_TYPE_NS;
+ goto out;
+ }
+
+ returnval = check_qtype(sd, ntohs(question->hdr->qtype), 1, &error);
+ if (returnval == 0) {
+ switch (error) {
+ case -2:
+ *lzerrno = ERR_NXDOMAIN;
+ break;
+ case -1:
+ *lzerrno = ERR_NOERROR;
+ break;
+ }
+
+ return (-1);
+ }
+
+ snprintf(replystring, DNS_MAXNAME, "*");
+ }
+
+out:
+ return returnval;
+}
+
+/*
+ * BUILD_FAKE_QUESTION - fill the fake question structure with the DNS query.
+ */
+
+struct question *
+build_fake_question(char *name, int namelen, u_int16_t type)
+{
+ struct question *q;
+
+ q = (void *)calloc(1, sizeof(struct question));
+ if (q == NULL) {
+ dolog(LOG_INFO, "calloc: %s\n", strerror(errno));
+ return NULL;
+ }
+
+ q->hdr = (void *)calloc(1, sizeof(struct dns_question_hdr));
+ if (q->hdr == NULL) {
+ dolog(LOG_INFO, "calloc: %s\n", strerror(errno));
+ free(q);
+ return NULL;
+ }
+ q->hdr->namelen = namelen;
+ q->hdr->name = (void *) calloc(1, q->hdr->namelen);
+ if (q->hdr->name == NULL) {
+ dolog(LOG_INFO, "calloc: %s\n", strerror(errno));
+ free(q->hdr);
+ free(q);
+ return NULL;
+ }
+ q->converted_name = NULL;
+
+ /* fill our name into the dns header struct */
+
+ memcpy(q->hdr->name, name, q->hdr->namelen);
+
+ q->hdr->qtype = type;
+ q->hdr->qclass = htons(DNS_CLASS_IN);
+
+ return (q);
+}
+
+/*
+ * GET_SOA - get authoritative soa for a particular domain
+ */
+
+int
+get_soa(DB *db, struct question *question, struct domain *sd, int wildcard)
+{
+ int plen;
+ int ret = 0;
+
+ DBT key, data;
+
+ char *p;
+
+ p = question->hdr->name;
+ plen = question->hdr->namelen;
+
+ do {
+
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+
+ key.data = (char *)p;
+ key.size = plen;
+
+ data.data = NULL;
+ data.size = 0;
+
+ ret = db->get(db, NULL, &key, &data, 0);
+ if (ret != 0) {
+ /*
+ * If we're not wildcarding end the search here and
+ * return with -1
+ */
+ if (! wildcard)
+ return -1;
+
+ plen -= (*p + 1);
+ p = (p + (*p + 1));
+ continue;
+ }
+
+ if (data.size != sizeof(struct domain)) {
+ dolog(LOG_INFO, "btree db is damaged, drop\n");
+ return -1;
+ }
+
+ memcpy((char *)sd, (char *)data.data, data.size);
+
+ if ((sd->flags & DOMAIN_HAVE_SOA) == DOMAIN_HAVE_SOA) {
+ /* we'll take this one */
+ return 0;
+ } else {
+ plen -= (*p + 1);
+ p = (p + (*p + 1));
+ }
+
+ } while (*p);
+
+ return -1;
+}
+
+/*
+ * GET_DNS_TYPE - take integer and compare to table, then spit back a static
+ * string with the result. This function can't fail.
+ */
+
+char *
+get_dns_type(int dnstype)
+{
+ static char type[128];
+ struct typetable *t;
+
+ t = TT;
+
+ while (t->type != NULL) {
+ if (dnstype == t->number)
+ break;
+
+ t = (t + 1);
+ }
+
+ if (t->type == NULL) {
+ snprintf(type, sizeof(type) - 1, "%u", dnstype);
+ } else
+ snprintf(type, sizeof(type) - 1, "%s(%u)", t->type, dnstype);
+
+ return (type);
+}
+
+/*
+ * MAINLOOP - does the polling of tcp & udp descriptors and if ready receives the
+ * requests, builds the question and calls for replies, loops
+ *
+ */
+
+void
+mainloop(struct cfg *cfg)
+{
+ fd_set rset;
+ int sel;
+ int len, slen;
+ int is_ipv6;
+ int i;
+ int istcp = 1;
+ int maxso;
+ int so;
+ int type0, type1;
+ int lzerrno;
+ int wildcard = 0;
+ int filter = 0;
+ int rcheck = 0;
+ int blacklist = 1;
+ int sp;
+ int lfd;
+
+ u_int32_t received_ttl;
+#if defined __FreeBSD__ || defined __OpenBSD__
+ u_char *ttlptr;
+#else
+ int *ttlptr;
+#endif
+
+ u_int8_t aregion; /* region where the address comes from */
+
+ char *pbuf;
+ char buf[4096];
+ char *replybuf = NULL;
+ char address[INET6_ADDRSTRLEN];
+ char replystring[DNS_MAXNAME + 1];
+ char fakereplystring[DNS_MAXNAME + 1];
+ char controlbuf[64];
+
+ union {
+ struct sockaddr sa;
+ struct sockaddr_in sin;
+ struct sockaddr_in6 sin6;
+ } sockaddr_large;
+
+ socklen_t fromlen = sizeof(sockaddr_large);
+ socklen_t namelen = sizeof(struct sockaddr_storage);
+ socklen_t logfromlen = sizeof(struct sockaddr_storage);
+
+ struct sockaddr *from = (void *)&sockaddr_large;
+ struct sockaddr_in *sin;
+ struct sockaddr_in6 *sin6;
+ struct sockaddr_storage sto;
+ struct sockaddr_storage logfrom;
+
+ struct dns_header *dh;
+ struct question *question, *fakequestion;
+ struct domain sd0, sd1;
+
+ struct sreply sreply;
+ struct srecurseheader rh;
+ struct timeval tv = { 10, 0};
+
+ struct msghdr msgh;
+ struct cmsghdr *cmsg;
+ struct iovec iov;
+
+ int flag;
+ int recursion = 0;
+
+
+ SLIST_INIT(&tcpshead);
+ collects_init();
+
+ replybuf = calloc(1, 65536);
+ if (replybuf == NULL) {
+ dolog(LOG_ERR, "calloc: %s\n", strerror(errno));
+ slave_shutdown();
+ exit(1);
+ }
+
+
+ sp = cfg->recurse;
+ lfd = cfg->log;
+
+ /*
+ * set descriptors nonblocking, and listen on them
+ */
+
+ for (i = 0; i < cfg->sockcount; i++) {
+ listen(cfg->tcp[i], 5);
+ }
+
+ for (;;) {
+ is_ipv6 = 0;
+ maxso = 0;
+ /*
+ * check for timeouts
+ */
+
+#ifdef __linux__
+ SLIST_FOREACH(tnp, &tcpshead, tcps_entry) {
+#else
+ SLIST_FOREACH_SAFE(tnp, &tcpshead, tcps_entry, tntmp) {
+#endif
+ if ((tnp->time + 10) < time(NULL)) {
+ free(tnp->input);
+ free(tnp->ident);
+ free(tnp->address);
+ close(tnp->so);
+ SLIST_REMOVE(&tcpshead, tnp, tcps, tcps_entry);
+ free(tnp);
+ }
+ }
+
+ FD_ZERO(&rset);
+ for (i = 0; i < cfg->sockcount; i++) {
+ if (maxso < cfg->tcp[i])
+ maxso = cfg->tcp[i];
+
+ if (maxso < cfg->udp[i])
+ maxso = cfg->udp[i];
+
+ if (axfrport && maxso < cfg->axfr[i])
+ maxso = cfg->axfr[i];
+
+ FD_SET(cfg->tcp[i], &rset);
+ FD_SET(cfg->udp[i], &rset);
+
+ if (axfrport)
+ FD_SET(cfg->axfr[i], &rset);
+ }
+
+ SLIST_FOREACH(tnp, &tcpshead, tcps_entry) {
+ if (maxso < tnp->so)
+ maxso = tnp->so;
+
+ FD_SET(tnp->so, &rset);
+ }
+
+ if (logging.bind == 1) {
+ if (maxso < lfd)
+ maxso = lfd;
+ FD_SET(lfd, &rset);
+ }
+
+ tv.tv_sec = 10;
+ tv.tv_usec = 0;
+
+ sel = select(maxso + 1, &rset, NULL, NULL, &tv);
+
+ if (sel < 0) {
+ dolog(LOG_INFO, "select: %s\n", strerror(errno));
+ continue;
+ }
+
+ if (sel == 0) {
+#ifdef __linux__
+ SLIST_FOREACH(tnp, &tcpshead, tcps_entry) {
+#else
+ SLIST_FOREACH_SAFE(tnp, &tcpshead, tcps_entry, tntmp) {
+#endif
+ if ((tnp->time + 10) < time(NULL)) {
+ free(tnp->input);
+ free(tnp->ident);
+ free(tnp->address);
+ close(tnp->so);
+ SLIST_REMOVE(&tcpshead, tnp, tcps, tcps_entry);
+ free(tnp);
+ }
+ }
+ continue;
+ }
+
+ for (i = 0; i < cfg->sockcount; i++) {
+ if (FD_ISSET(cfg->tcp[i], &rset)) {
+ fromlen = sizeof(sockaddr_large);
+
+ so = accept(cfg->tcp[i], (struct sockaddr*)from, &fromlen);
+
+ if (so < 0) {
+ dolog(LOG_INFO, "tcp accept: %s\n", strerror(errno));
+ continue;
+ }
+
+ if (from->sa_family == AF_INET6) {
+ is_ipv6 = 1;
+
+ fromlen = sizeof(struct sockaddr_in6);
+ sin6 = (struct sockaddr_in6 *)from;
+ inet_ntop(AF_INET6, (void *)&sin6->sin6_addr, (char *)&address, sizeof(address));
+ aregion = find_region((struct sockaddr_storage *)sin6, AF_INET6);
+ wildcard = find_wildcard((struct sockaddr_storage *)sin6, AF_INET6);
+ filter = find_filter((struct sockaddr_storage *)sin6, AF_INET6);
+ if (whitelist) {
+ blacklist = find_whitelist((struct sockaddr_storage *)sin6, AF_INET6);
+ }
+ } else if (from->sa_family == AF_INET) {
+ is_ipv6 = 0;
+
+ fromlen = sizeof(struct sockaddr_in);
+ sin = (struct sockaddr_in *)from;
+ inet_ntop(AF_INET, (void *)&sin->sin_addr, (char *)&address, sizeof(address));
+ wildcard = find_wildcard((struct sockaddr_storage *)sin, AF_INET);
+ aregion = find_region((struct sockaddr_storage *)sin, AF_INET);
+ filter = find_filter((struct sockaddr_storage *)sin, AF_INET);
+ if (whitelist) {
+ blacklist = find_whitelist((struct sockaddr_storage *)sin, AF_INET);
+ }
+ } else {
+ dolog(LOG_INFO, "TCP packet received on descriptor %u interface \"%s\" had weird address family (%u), drop\n", so, cfg->ident[i], from->sa_family);
+ close(so);
+ continue;
+ }
+
+
+ if (filter) {
+ dolog(LOG_INFO, "TCP connection refused on descriptor %u interface \"%s\" from %s, filter policy\n", so, cfg->ident[i], address);
+ close(so);
+ continue;
+ }
+
+ if (whitelist && blacklist == 0) {
+ dolog(LOG_INFO, "TCP connection refused on descriptor %u interface \"%s\" from %s, whitelist policy\n", so, cfg->ident[i], address);
+ close(so);
+ continue;
+ }
+
+
+
+
+ /*
+ * make this socket nonblocking
+ */
+
+ if ((flag = fcntl(so, F_GETFL)) < 0) {
+ dolog(LOG_INFO, "fcntl: %s\n", strerror(errno));
+ }
+ flag |= O_NONBLOCK;
+ if (fcntl(so, F_SETFL, flag) < 0) {
+ dolog(LOG_INFO, "fcntl 2: %s\n", strerror(errno));
+ }
+
+
+ /* fill the tcps struct */
+
+ tn1 = malloc(sizeof(struct tcps));
+ if (tn1 == NULL) {
+ dolog(LOG_INFO, "malloc: %s\n", strerror(errno));
+ close(so);
+ continue;
+ }
+
+ tn1->input = (char *)malloc(0xffff + 2);
+ if (tn1->input == NULL) {
+ dolog(LOG_INFO, "tcp malloc 2: %s\n", strerror(errno));
+ close(so);
+ continue;
+ }
+
+ tn1->offset = 0;
+ tn1->length = 0;
+ tn1->maxlen = 0xffff + 2;
+ tn1->so = so;
+ tn1->isv6 = is_ipv6;
+ tn1->ident = strdup(cfg->ident[i]);
+ tn1->address = strdup(address);
+ tn1->region = aregion;
+ tn1->wildcard = wildcard;
+ tn1->time = time(NULL);
+
+ SLIST_INSERT_HEAD(&tcpshead, tn1, tcps_entry);
+
+ } /* FD_ISSET(); */
+ } /* if sockcount */
+
+#ifdef __linux__
+ SLIST_FOREACH(tnp, &tcpshead, tcps_entry) {
+#else
+ SLIST_FOREACH_SAFE(tnp, &tcpshead, tcps_entry, tntmp) {
+#endif
+ if (FD_ISSET(tnp->so, &rset)) {
+
+ istcp = 1;
+ len = recv(tnp->so, tnp->input + tnp->offset, tnp->maxlen - tnp->offset, 0);
+ if (len < 0) {
+ if (errno == EWOULDBLOCK)
+ continue;
+ else {
+ free(tnp->input);
+ free(tnp->ident);
+ free(tnp->address);
+ close(tnp->so);
+ SLIST_REMOVE(&tcpshead, tnp, tcps, tcps_entry);
+ free(tnp);
+ continue;
+ }
+ } /* if len */
+
+ if (len == 0) {
+ free(tnp->input);
+ free(tnp->ident);
+ free(tnp->address);
+ close(tnp->so);
+ SLIST_REMOVE(&tcpshead, tnp, tcps, tcps_entry);
+ free(tnp);
+ continue;
+ }
+
+ tnp->offset += len;
+ tnp->time = time(NULL);
+
+ if (tnp->offset >= 2) {
+ tnp->length = ntohs(*((u_int16_t *) tnp->input));
+ }
+
+ /*
+ * only go on if the full packet was written
+ */
+
+ if (tnp->length + 2 != tnp->offset)
+ continue;
+
+ len = tnp->length;
+ pbuf = tnp->input + 2;
+
+ /* if UDP packet check length for minimum / maximum */
+ if (len > DNS_MAXUDP || len < sizeof(struct dns_header)){
+ dolog(LOG_INFO, "TCP packet on descriptor %u interface \"%s\" illegal dns packet length from %s, drop\n", tnp->so, tnp->ident, tnp->address);
+ goto drop;
+ }
+
+ dh = (struct dns_header *)&pbuf[0];
+
+ /* check if we're a question or reply, drop replies */
+ if ((ntohs(dh->query) & DNS_REPLY)) {
+ dolog(LOG_INFO, "TCP packet on descriptor %u interface \"%s\" dns header from %s is not a question, drop\n", tnp->so, tnp->ident, tnp->address);
+ goto drop;
+ }
+
+ /*
+ * if questions aren't exactly 1 then drop
+ */
+
+ if (ntohs(dh->question) != 1) {
+ dolog(LOG_INFO, "TCP packet on descriptor %u interface \"%s\" header from %s has no question, drop\n", tnp->so, tnp->ident, tnp->address);
+
+ /* format error */
+ build_reply( &sreply, tnp->so, pbuf, len, NULL,
+ from, fromlen, NULL, NULL, tnp->region,
+ istcp, tnp->wildcard, NULL, replybuf);
+
+ slen = reply_fmterror(&sreply);
+ dolog(LOG_INFO, "TCP question on descriptor %d interface \"%s\" from %s, did not have question of 1 replying format error\n", tnp->so, tnp->ident, tnp->address);
+ goto drop;
+ }
+
+
+ if ((question = build_question(pbuf, len, 0)) == NULL) {
+ dolog(LOG_INFO, "TCP packet on descriptor %u interface \"%s\" malformed question from %s, drop\n", tnp->so, tnp->ident, tnp->address);
+ goto drop;
+ }
+
+ /* goto drop beyond this point should goto out instead */
+ fakequestion = NULL;
+
+ if ((type0 = lookup_zone(cfg->db, question, &sd0, &lzerrno, (char *)&replystring, wildcard)) < 0) {
+
+ switch (lzerrno) {
+ default:
+ dolog(LOG_INFO, "invalid lzerrno! dropping\n");
+ /* FALLTHROUGH */
+ case ERR_DROP:
+ snprintf(replystring, DNS_MAXNAME, "DROP");
+ goto tcpout;
+
+ case ERR_NXDOMAIN:
+ goto tcpnxdomain;
+ case ERR_NOERROR:
+ /*
+ * this is hackish not sure if this should be here
+ */
+
+ snprintf(replystring, DNS_MAXNAME, "NOERROR");
+
+ /*
+ * lookup an authoritative soa
+ */
+
+ memset(&sd0, 0, sizeof(sd0));
+ (void)get_soa(cfg->db, question, &sd0, wildcard);
+
+ build_reply( &sreply, tnp->so, pbuf, len,
+ question, from, fromlen,
+ &sd0, NULL, tnp->region, istcp,
+ tnp->wildcard, NULL, replybuf);
+
+ slen = reply_noerror(&sreply);
+ goto tcpout;
+
+ }
+ }
+
+ switch (type0) {
+ case 0:
+ /*
+ * lookup_zone could not find an RR for the
+ * question at all -> nxdomain
+ */
+tcpnxdomain:
+ snprintf(replystring, DNS_MAXNAME, "NXDOMAIN");
+
+ /*
+ * lookup an authoritative soa
+ */
+
+ memset(&sd0, 0, sizeof(sd0));
+ (void)get_soa(cfg->db, question, &sd0, wildcard);
+
+ build_reply( &sreply, tnp->so, pbuf, len, question,
+ from, fromlen, &sd0, NULL,
+ tnp->region, istcp, tnp->wildcard, NULL,
+ replybuf);
+
+ slen = reply_nxdomain(&sreply);
+ goto tcpout;
+ case DNS_TYPE_CNAME:
+ fakequestion = build_fake_question(sd0.cname, sd0.cnamelen, question->hdr->qtype);
+ if (fakequestion == NULL) {
+ dolog(LOG_INFO, "fakequestion failed\n");
+ break;
+ }
+
+ type1 = lookup_zone(cfg->db, fakequestion, &sd1, &lzerrno, (char *)&fakereplystring, wildcard);
+ /* break CNAMES pointing to CNAMES */
+ if (type1 == DNS_TYPE_CNAME)
+ type1 = 0;
+
+ break;
+ default:
+
+ break;
+ }
+
+ /*
+ * Allow CLASS IN, CHAOS and others are
+ * not implemented and so we build a reply for
+ * that and go out.
+ */
+
+ switch (ntohs(question->hdr->qclass)) {
+ case DNS_CLASS_IN:
+ break;
+ default:
+ build_reply( &sreply, tnp->so, pbuf, len, question,
+ from, fromlen, NULL, NULL, tnp->region,
+ istcp, tnp->wildcard, NULL, replybuf);
+
+ slen = reply_notimpl(&sreply);
+ snprintf(replystring, DNS_MAXNAME, "NOTIMPL");
+ goto tcpout;
+ }
+
+ switch (ntohs(question->hdr->qtype)) {
+ case DNS_TYPE_A:
+ if (type0 == DNS_TYPE_CNAME) {
+ build_reply(&sreply, tnp->so, pbuf, len, question, from, \
+ fromlen, &sd0, ((type1 > 0) ? &sd1 : NULL), \
+ tnp->region, istcp, tnp->wildcard, NULL, replybuf);
+ slen = reply_cname(&sreply);
+ } else if (type0 == DNS_TYPE_NS) {
+
+ build_reply(&sreply, tnp->so, pbuf, len, question,
+ from, fromlen, &sd0, NULL,
+ tnp->region, istcp, tnp->wildcard, NULL,
+ replybuf);
+
+ slen = reply_ns(&sreply, cfg->db);
+ break;
+ } else if (type0 == DNS_TYPE_A) {
+ build_reply(&sreply, tnp->so, pbuf, len, question, from, \
+ fromlen, &sd0, NULL, tnp->region, istcp, tnp->wildcard,
+ NULL, replybuf);
+ slen = reply_a(&sreply, cfg->db);
+ break; /* must break here */
+ }
+
+ break;
+
+ case DNS_TYPE_ANY:
+ build_reply(&sreply, tnp->so, pbuf, len, question, from, \
+ fromlen, &sd0, NULL, tnp->region, istcp, tnp->wildcard,
+ NULL, replybuf);
+
+ slen = reply_any(&sreply);
+ break; /* must break here */
+
+ case DNS_TYPE_AAAA:
+
+ if (type0 == DNS_TYPE_CNAME) {
+ build_reply(&sreply, tnp->so, pbuf, len, question, from, \
+ fromlen, &sd0, ((type1 > 0) ? &sd1 : NULL), \
+ tnp->region, istcp, tnp->wildcard, NULL, replybuf);
+ slen = reply_cname(&sreply);
+ } else if (type0 == DNS_TYPE_NS) {
+ build_reply(&sreply, tnp->so, pbuf, len, question, from, \
+ fromlen, &sd0, NULL, tnp->region, istcp,
+ tnp->wildcard, NULL, replybuf);
+
+ slen = reply_ns(&sreply, cfg->db);
+ break;
+ } else if (type0 == DNS_TYPE_AAAA) {
+ build_reply(&sreply, tnp->so, pbuf, len, question, from,
+ fromlen, &sd0, NULL, tnp->region, istcp,
+ tnp->wildcard, NULL, replybuf);
+
+ slen = reply_aaaa(&sreply, cfg->db);
+ break; /* must break here */
+ }
+
+ break;
+ case DNS_TYPE_MX:
+
+ if (type0 == DNS_TYPE_CNAME) {
+ build_reply(&sreply, tnp->so, pbuf, len, question, from, \
+ fromlen, &sd0, ((type1 > 0) ? &sd1 : NULL), \
+ tnp->region, istcp, tnp->wildcard, NULL, replybuf);
+
+ slen = reply_cname(&sreply);
+
+ } else if (type0 == DNS_TYPE_NS) {
+ build_reply(&sreply, tnp->so, pbuf, len, question, from, \
+ fromlen, &sd0, NULL, tnp->region, istcp,
+ tnp->wildcard, NULL, replybuf);
+
+ slen = reply_ns(&sreply, cfg->db);
+
+ break;
+ } else if (type0 == DNS_TYPE_MX) {
+ build_reply(&sreply, tnp->so, pbuf, len, question, from, \
+ fromlen, &sd0, NULL, tnp->region, istcp,
+ tnp->wildcard, NULL, replybuf);
+
+ slen = reply_mx(&sreply, cfg->db);
+ break; /* must break here */
+ }
+
+ break;
+ case DNS_TYPE_SOA:
+ if (type0 == DNS_TYPE_SOA) {
+ build_reply(&sreply, tnp->so, pbuf, len, question, from, \
+ fromlen, &sd0, NULL, tnp->region, istcp,
+ tnp->wildcard, NULL, replybuf);
+
+ slen = reply_soa(&sreply);
+ } else if (type0 == DNS_TYPE_NS) {
+ build_reply(&sreply, tnp->so, pbuf, len, question, from, \
+ fromlen, &sd0, NULL, tnp->region, istcp,
+ tnp->wildcard, NULL, replybuf);
+
+ slen = reply_ns(&sreply, cfg->db);
+ break;
+ }
+ break;
+ case DNS_TYPE_NS:
+ if (type0 == DNS_TYPE_NS) {
+ build_reply(&sreply, tnp->so, pbuf, len, question, from, \
+ fromlen, &sd0, NULL, tnp->region, istcp,
+ tnp->wildcard, NULL, replybuf);
+
+ slen = reply_ns(&sreply, cfg->db);
+ }
+ break;
+
+ case DNS_TYPE_SSHFP:
+ if (type0 == DNS_TYPE_SSHFP) {
+ build_reply(&sreply, tnp->so, pbuf, len, question, from, \
+ fromlen, &sd0, NULL, tnp->region, istcp,
+ tnp->wildcard, NULL, replybuf);
+
+ slen = reply_sshfp(&sreply);
+ }
+ break;
+
+
+ case DNS_TYPE_SRV:
+ if (type0 == DNS_TYPE_SRV) {
+ build_reply(&sreply, tnp->so, pbuf, len, question, from, \
+ fromlen, &sd0, NULL, tnp->region, istcp,
+ tnp->wildcard, NULL, replybuf);
+
+ slen = reply_srv(&sreply, cfg->db);
+ }
+ break;
+
+ case DNS_TYPE_NAPTR:
+ if (type0 == DNS_TYPE_NAPTR) {
+ build_reply(&sreply, tnp->so, pbuf, len, question, from, \
+ fromlen, &sd0, NULL, tnp->region, istcp,
+ tnp->wildcard, NULL, replybuf);
+
+ slen = reply_naptr(&sreply, cfg->db);
+ }
+ break;
+
+ case DNS_TYPE_CNAME:
+ if (type0 == DNS_TYPE_CNAME) {
+ build_reply(&sreply, tnp->so, pbuf, len, question, from, \
+ fromlen, &sd0, NULL, tnp->region, istcp,
+ tnp->wildcard, NULL, replybuf);
+
+ slen = reply_cname(&sreply);
+ } else if (type0 == DNS_TYPE_NS) {
+ build_reply(&sreply, tnp->so, pbuf, len, question, from, \
+ fromlen, &sd0, NULL, tnp->region, istcp,
+ tnp->wildcard, NULL, replybuf);
+
+ slen = reply_ns(&sreply, cfg->db);
+ break;
+ }
+ break;
+
+ case DNS_TYPE_PTR:
+ if (type0 == DNS_TYPE_CNAME) {
+ build_reply(&sreply, tnp->so, pbuf, len, question, from, \
+ fromlen, &sd0, ((type1 > 0) ? &sd1 : NULL) \
+ , tnp->region, istcp, tnp->wildcard, NULL,
+ replybuf);
+
+ slen = reply_cname(&sreply);
+
+ } else if (type0 == DNS_TYPE_NS) {
+
+ build_reply(&sreply, tnp->so, pbuf, len, question, from, \
+ fromlen, &sd0, NULL, tnp->region, istcp,
+ tnp->wildcard, NULL, replybuf);
+
+ slen = reply_ns(&sreply, cfg->db);
+
+ break;
+ } else if (type0 == DNS_TYPE_PTR) {
+
+ build_reply(&sreply, tnp->so, pbuf, len, question, from,
+ fromlen, &sd0, NULL, tnp->region, istcp,
+ tnp->wildcard, NULL, replybuf);
+
+ slen = reply_ptr(&sreply);
+ break; /* must break here */
+ }
+ break;
+
+ case DNS_TYPE_TXT:
+ if (type0 == DNS_TYPE_TXT) {
+
+ build_reply(&sreply, tnp->so, pbuf, len, question, from, \
+ fromlen, &sd0, NULL, tnp->region, istcp,
+ tnp->wildcard, NULL, replybuf);
+
+ slen = reply_txt(&sreply);
+ }
+ break;
+
+ case DNS_TYPE_SPF:
+ if (type0 == DNS_TYPE_SPF) {
+
+ build_reply(&sreply, tnp->so, pbuf, len, question, from, \
+ fromlen, &sd0, NULL, tnp->region, istcp,
+ tnp->wildcard, NULL, replybuf);
+
+ slen = reply_spf(&sreply);
+ }
+ break;
+
+
+ default:
+
+ /*
+ * ANY unknown RR TYPE gets a NOTIMPL
+ */
+
+ /*
+ * except for delegations
+ */
+
+ if (type0 == DNS_TYPE_NS) {
+ build_reply(&sreply, tnp->so, pbuf, len, question, from, \
+ fromlen, &sd0, NULL, aregion, istcp,
+ tnp->wildcard, NULL, replybuf);
+
+ slen = reply_ns(&sreply, cfg->db);
+
+ } else {
+
+ build_reply(&sreply, tnp->so, pbuf, len, question, from, \
+ fromlen, NULL, NULL, tnp->region, istcp,
+ tnp->wildcard, NULL, replybuf);
+
+ slen = reply_notimpl(&sreply);
+ snprintf(replystring, DNS_MAXNAME, "NOTIMPL");
+ }
+ break;
+ }
+
+ tcpout:
+ if (lflag)
+ dolog(LOG_INFO, "request on descriptor %u interface \"%s\" from %s (ttl=TCP, region=%d) for \"%s\" type=%s class=%u, answering \"%s\" (%d/%d)\n", tnp->so, tnp->ident, tnp->address, tnp->region, question->converted_name, get_dns_type(ntohs(question->hdr->qtype)), ntohs(question->hdr->qclass), replystring, len, slen);
+
+
+ if (fakequestion != NULL) {
+ free_question(fakequestion);
+ }
+
+ free_question(question);
+
+ } /* END ISSET */
+
+ memset(tnp->input, 0, tnp->maxlen);
+ tnp->offset = 0;
+
+ } /* SLIST_FOREACH */
+
+ /* UDP marriage */
+ for (i = 0; i < cfg->sockcount; i++) {
+ if (axfrport && FD_ISSET(cfg->axfr[i], &rset)) {
+ istcp = 0;
+ so = cfg->axfr[i];
+
+ goto axfrentry;
+ }
+
+ if (FD_ISSET(cfg->udp[i], &rset)) {
+ istcp = 0;
+ so = cfg->udp[i];
+axfrentry:
+ fromlen = sizeof(sockaddr_large);
+
+ memset(&msgh, 0, sizeof(msgh));
+ iov.iov_base = buf;
+ iov.iov_len = sizeof(buf);
+ msgh.msg_name = from;
+ msgh.msg_namelen = fromlen;
+ msgh.msg_iov = &iov;
+ msgh.msg_iovlen = 1;
+ msgh.msg_control = (struct cmsghdr*)&controlbuf;
+ msgh.msg_controllen = sizeof(controlbuf);
+
+ len = recvmsg(so, &msgh, 0);
+ if (len < 0) {
+ dolog(LOG_INFO, "recvmsg: on descriptor %u interface \"%s\" %s\n", so, cfg->ident[i], strerror(errno));
+ continue;
+ }
+
+ received_ttl = 0;
+
+ for (cmsg = CMSG_FIRSTHDR(&msgh);
+ cmsg != NULL;
+ cmsg = CMSG_NXTHDR(&msgh,cmsg)) {
+ if (cmsg->cmsg_level == IPPROTO_IP
+#ifdef __linux__
+ && cmsg->cmsg_type == IP_TTL) {
+#elif defined __NetBSD__
+ && cmsg->cmsg_type == IP_TTL) {
+
+#else
+
+ && cmsg->cmsg_type == IP_RECVTTL) {
+#endif
+
+#if defined __FreeBSD__ || defined __OpenBSD__
+
+ ttlptr = (u_char *) CMSG_DATA(cmsg);
+ received_ttl = (u_int)*ttlptr;
+#else
+
+ ttlptr = (int *) CMSG_DATA(cmsg);
+ received_ttl = (u_int)*ttlptr;
+#endif
+ }
+
+ if (cmsg->cmsg_level == IPPROTO_IPV6 &&
+ cmsg->cmsg_type == IPV6_HOPLIMIT) {
+
+ if (cmsg->cmsg_len !=
+ CMSG_LEN(sizeof(int))) {
+ dolog(LOG_INFO, "cmsg->cmsg_len == %d\n", cmsg->cmsg_len);
+ continue;
+ }
+
+#ifdef __NetBSD__
+ ttlptr = (int *) CMSG_DATA(cmsg);
+#else
+ ttlptr = (u_char *) CMSG_DATA(cmsg);
+#endif
+
+
+ received_ttl = (u_int)*ttlptr;
+ }
+ }
+
+ if (rflag) {
+ if (getsockname(so, (struct sockaddr*)&sto, &namelen) < 0) {
+ dolog(LOG_INFO, "getsockname failed: %s\n", strerror(errno));
+ }
+
+ memset(&rh, 0, sizeof(rh));
+ }
+
+ if (from->sa_family == AF_INET6) {
+ is_ipv6 = 1;
+
+ fromlen = sizeof(struct sockaddr_in6);
+ sin6 = (struct sockaddr_in6 *)from;
+ inet_ntop(AF_INET6, (void *)&sin6->sin6_addr, (char *)&address, sizeof(address));
+ if (ratelimit) {
+ add_rrlimit(ratelimit_backlog, (u_int16_t *)&sin6->sin6_addr, sizeof(sin6->sin6_addr), rptr);
+
+ rcheck = check_rrlimit(ratelimit_backlog, (u_int16_t *)&sin6->sin6_addr, sizeof(sin6->sin6_addr), rptr);
+ }
+
+ aregion = find_region((struct sockaddr_storage *)sin6, AF_INET6);
+ wildcard = find_wildcard((struct sockaddr_storage *)sin6, AF_INET6);
+ filter = find_filter((struct sockaddr_storage *)sin6, AF_INET6);
+ if (whitelist) {
+ blacklist = find_whitelist((struct sockaddr_storage *)sin6, AF_INET6);
+ }
+ if (rflag) {
+ recursion = find_recurse((struct sockaddr_storage *)sin6, AF_INET6);
+ recurseheader(&rh, IPPROTO_UDP, (struct sockaddr_storage*)sin6, &sto, AF_INET6);
+ }
+ } else if (from->sa_family == AF_INET) {
+ is_ipv6 = 0;
+
+ fromlen = sizeof(struct sockaddr_in);
+ sin = (struct sockaddr_in *)from;
+ inet_ntop(AF_INET, (void *)&sin->sin_addr, (char *)&address, sizeof(address));
+ if (ratelimit) {
+ add_rrlimit(ratelimit_backlog, (u_int16_t *)&sin->sin_addr.s_addr, sizeof(sin->sin_addr.s_addr), rptr);
+
+ rcheck = check_rrlimit(ratelimit_backlog, (u_int16_t *)&sin->sin_addr.s_addr, sizeof(sin->sin_addr.s_addr), rptr);
+ }
+
+ aregion = find_region((struct sockaddr_storage *)sin, AF_INET);
+ wildcard = find_wildcard((struct sockaddr_storage *)sin, AF_INET);
+ filter = find_filter((struct sockaddr_storage *)sin, AF_INET);
+ if (whitelist) {
+ blacklist = find_whitelist((struct sockaddr_storage *)sin, AF_INET);
+ }
+
+ if (rflag) {
+ recursion = find_recurse((struct sockaddr_storage *)sin, AF_INET);
+ recurseheader(&rh, IPPROTO_UDP, (struct sockaddr_storage*)sin, &sto, AF_INET);
+ }
+ } else {
+ dolog(LOG_INFO, "packet received on descriptor %u interface \"%s\" had weird address family (%u), drop\n", so, cfg->ident[i], from->sa_family);
+ goto drop;
+ }
+
+ /* if UDP packet check length for minimum / maximum */
+ if (len > DNS_MAXUDP || len < sizeof(struct dns_header)){
+ dolog(LOG_INFO, "on descriptor %u interface \"%s\" illegal dns packet length from %s, drop\n", so, cfg->ident[i], address);
+ goto drop;
+ }
+
+ dh = (struct dns_header *)&buf[0];
+
+ /* check if we're a question or reply, drop replies */
+ if ((ntohs(dh->query) & DNS_REPLY)) {
+ dolog(LOG_INFO, "on descriptor %u interface \"%s\" dns header from %s is not a question, drop\n", so, cfg->ident[i], address);
+ goto drop;
+ }
+
+ /*
+ * if questions aren't exactly 1 then drop
+ */
+
+ if (ntohs(dh->question) != 1) {
+ dolog(LOG_INFO, "on descriptor %u interface \"%s\" header from %s has no question, drop\n", so, cfg->ident[i], address);
+
+ /* format error */
+ build_reply(&sreply, so, buf, len, NULL, from, fromlen, NULL, NULL, aregion, istcp, wildcard, NULL, replybuf);
+
+ slen = reply_fmterror(&sreply);
+ dolog(LOG_INFO, "question on descriptor %d interface \"%s\" from %s, did not have question of 1 replying format error\n", so, cfg->ident[i], address);
+ goto drop;
+ }
+
+ if (filter) {
+
+ build_reply(&sreply, so, buf, len, NULL, from, fromlen, NULL, NULL, aregion, istcp, wildcard, NULL, replybuf);
+ slen = reply_refused(&sreply);
+
+ dolog(LOG_INFO, "UDP connection refused on descriptor %u interface \"%s\" from %s (ttl=%d, region=%d) replying REFUSED, filter policy\n", so, cfg->ident[i], address, received_ttl, aregion);
+ goto drop;
+ }
+
+ if (whitelist && blacklist == 0) {
+
+ build_reply(&sreply, so, buf, len, NULL, from, fromlen, NULL, NULL, aregion, istcp, wildcard, NULL, replybuf);
+ slen = reply_refused(&sreply);
+
+ dolog(LOG_INFO, "UDP connection refused on descriptor %u interface \"%s\" from %s (ttl=%d, region=%d) replying REFUSED, whitelist policy\n", so, cfg->ident[i], address, received_ttl, aregion);
+ goto drop;
+ }
+
+ if (ratelimit && rcheck) {
+ dolog(LOG_INFO, "UDP connection refused on descriptor %u interface \"%s\" from %s (ttl=%d, region=%d) ratelimit policy dropping packet\n", so, cfg->ident[i], address, received_ttl, aregion);
+ goto drop;
+ }
+
+ if (rflag && recursion) {
+ memcpy(&rh.buf, buf, len);
+ rh.len = len;
+ }
+
+ if ((question = build_question(buf, len, ntohs(dh->additional))) == NULL) {
+ dolog(LOG_INFO, "on descriptor %u interface \"%s\" malformed question from %s, drop\n", so, cfg->ident[i], address);
+ goto drop;
+ }
+
+ /* goto drop beyond this point should goto out instead */
+ fakequestion = NULL;
+
+ if ((type0 = lookup_zone(cfg->db, question, &sd0, &lzerrno, (char *)&replystring, wildcard)) < 0) {
+ switch (lzerrno) {
+ default:
+ dolog(LOG_INFO, "invalid lzerrno! dropping\n");
+ /* FALLTHROUGH */
+ case ERR_DROP:
+ snprintf(replystring, DNS_MAXNAME, "DROP");
+ goto udpout;
+
+ case ERR_NXDOMAIN:
+ goto udpnxdomain;
+ case ERR_NOERROR:
+ if (rflag && recursion) {
+ snprintf(replystring, DNS_MAXNAME, "RECURSE");
+ if (send(sp, (char *)&rh, sizeof(rh), 0) < 0) {
+ dolog(LOG_INFO, "send sp: %s\n", strerror(errno));
+ }
+
+ goto udpout;
+ } else {
+ /*
+ * this is hackish not sure if this should be here
+ */
+
+ snprintf(replystring, DNS_MAXNAME, "NOERROR");
+
+ /*
+ * lookup an authoritative soa
+ */
+
+ memset(&sd0, 0, sizeof(sd0));
+ (void)get_soa(cfg->db, question, &sd0, wildcard);
+
+ build_reply(&sreply, so, buf, len, question, from, \
+ fromlen, &sd0, NULL, aregion, istcp, wildcard,
+ NULL, replybuf);
+
+ slen = reply_noerror(&sreply);
+ goto udpout;
+ } /* else rflag */
+ }
+ }
+
+ switch (type0) {
+ case 0:
+udpnxdomain:
+ if (rflag && recursion) {
+ snprintf(replystring, DNS_MAXNAME, "RECURSE");
+ if (send(sp, (char *)&rh, sizeof(rh), 0) < 0) {
+ dolog(LOG_INFO, "send sp: %s\n", strerror(errno));
+ }
+
+ goto udpout;
+ } else {
+
+ /*
+ * lookup_zone could not find an RR for the
+ * question at all -> nxdomain
+ */
+ snprintf(replystring, DNS_MAXNAME, "NXDOMAIN");
+
+ /*
+ * lookup an authoritative soa
+ */
+
+ memset(&sd0, 0, sizeof(sd0));
+ (void)get_soa(cfg->db, question, &sd0, wildcard);
+
+ build_reply(&sreply, so, buf, len, question, from, \
+ fromlen, &sd0, NULL, aregion, istcp, \
+ wildcard, NULL, replybuf);
+
+ slen = reply_nxdomain(&sreply);
+ goto udpout;
+ } /* else rflag */
+ case DNS_TYPE_CNAME:
+ fakequestion = build_fake_question(sd0.cname, sd0.cnamelen, question->hdr->qtype);
+ if (fakequestion == NULL) {
+ dolog(LOG_INFO, "fakequestion failed\n");
+ break;
+ }
+
+ type1 = lookup_zone(cfg->db, fakequestion, &sd1, &lzerrno, (char *)&fakereplystring, wildcard);
+ /* break CNAMES pointing to CNAMES */
+ if (type1 == DNS_TYPE_CNAME)
+ type1 = 0;
+
+ break;
+ default:
+
+ break;
+ }
+
+ /*
+ * Allow CLASS IN, CHAOS and others are
+ * not implemented and so we build a reply for
+ * that and go out.
+ */
+
+ switch (ntohs(question->hdr->qclass)) {
+ case DNS_CLASS_IN:
+ break;
+ default:
+ build_reply(&sreply, so, buf, len, question, from, \
+ fromlen, NULL, NULL, aregion, istcp, wildcard, \
+ NULL, replybuf);
+
+ slen = reply_notimpl(&sreply);
+ snprintf(replystring, DNS_MAXNAME, "NOTIMPL");
+ goto udpout;
+ }
+
+ switch (ntohs(question->hdr->qtype)) {
+ case DNS_TYPE_A:
+ if (type0 == DNS_TYPE_CNAME) {
+
+ build_reply(&sreply, so, buf, len, question, from, \
+ fromlen, &sd0, ((type1 > 0) ? &sd1 : NULL), \
+ aregion, istcp, wildcard, NULL, replybuf);
+
+ slen = reply_cname(&sreply);
+ } else if (type0 == DNS_TYPE_NS) {
+
+ build_reply(&sreply, so, buf, len, question, from, \
+ fromlen, &sd0, NULL, aregion, istcp, wildcard, \
+ NULL, replybuf);
+
+ slen = reply_ns(&sreply, cfg->db);
+ break;
+ } else if (type0 == DNS_TYPE_A) {
+
+ build_reply(&sreply, so, buf, len, question, from, \
+ fromlen, &sd0, NULL, aregion, istcp, wildcard,
+ NULL, replybuf);
+
+ slen = reply_a(&sreply, cfg->db);
+ break; /* must break here */
+ }
+
+ break;
+
+ case DNS_TYPE_ANY:
+
+ build_reply(&sreply, so, buf, len, question, from, \
+ fromlen, &sd0, NULL, aregion, istcp, wildcard, NULL,
+ replybuf);
+
+ slen = reply_any(&sreply);
+ break; /* must break here */
+
+ case DNS_TYPE_AAAA:
+
+ if (type0 == DNS_TYPE_CNAME) {
+
+ build_reply(&sreply, so, buf, len, question, from, \
+ fromlen, &sd0, ((type1 > 0) ? &sd1 : NULL), \
+ aregion, istcp, wildcard, NULL, replybuf);
+
+ slen = reply_cname(&sreply);
+ } else if (type0 == DNS_TYPE_NS) {
+
+ build_reply(&sreply, so, buf, len, question, from, \
+ fromlen, &sd0, NULL, aregion, istcp, wildcard, \
+ NULL, replybuf);
+
+ slen = reply_ns(&sreply, cfg->db);
+ break;
+ } else if (type0 == DNS_TYPE_AAAA) {
+
+ build_reply(&sreply, so, buf, len, question, from,
+ fromlen, &sd0, NULL, aregion, istcp, wildcard,
+ NULL, replybuf);
+
+ slen = reply_aaaa(&sreply, cfg->db);
+ break; /* must break here */
+ }
+
+ break;
+ case DNS_TYPE_MX:
+
+ if (type0 == DNS_TYPE_CNAME) {
+
+ build_reply(&sreply, so, buf, len, question, from, \
+ fromlen, &sd0, ((type1 > 0) ? &sd1 : NULL), \
+ aregion, istcp, wildcard, NULL, replybuf);
+
+ slen = reply_cname(&sreply);
+ } else if (type0 == DNS_TYPE_NS) {
+
+ build_reply(&sreply, so, buf, len, question, from, \
+ fromlen, &sd0, NULL, aregion, istcp, wildcard, \
+ NULL, replybuf);
+
+ slen = reply_ns(&sreply, cfg->db);
+ break;
+ } else if (type0 == DNS_TYPE_MX) {
+ build_reply(&sreply, so, buf, len, question, from, \
+ fromlen, &sd0, NULL, aregion, istcp, wildcard, \
+ NULL, replybuf);
+ slen = reply_mx(&sreply, cfg->db);
+ break; /* must break here */
+ }
+
+ break;
+ case DNS_TYPE_SOA:
+ if (type0 == DNS_TYPE_SOA) {
+
+ build_reply(&sreply, so, buf, len, question, from, \
+ fromlen, &sd0, NULL, aregion, istcp, wildcard, \
+ NULL, replybuf);
+
+ slen = reply_soa(&sreply);
+ } else if (type0 == DNS_TYPE_NS) {
+
+ build_reply(&sreply, so, buf, len, question, from, \
+ fromlen, &sd0, NULL, aregion, istcp, wildcard, \
+ NULL, replybuf);
+
+ slen = reply_ns(&sreply, cfg->db);
+ break;
+ }
+ break;
+ case DNS_TYPE_NS:
+ if (type0 == DNS_TYPE_NS) {
+
+ build_reply(&sreply, so, buf, len, question, from, \
+ fromlen, &sd0, NULL, aregion, istcp, wildcard, \
+ NULL, replybuf);
+
+ slen = reply_ns(&sreply, cfg->db);
+ }
+ break;
+
+ case DNS_TYPE_SSHFP:
+ if (type0 == DNS_TYPE_SSHFP) {
+ build_reply(&sreply, so, buf, len, question, from, \
+ fromlen, &sd0, NULL, aregion, istcp, wildcard, \
+ NULL, replybuf);
+
+ slen = reply_sshfp(&sreply);
+ }
+ break;
+
+
+ case DNS_TYPE_SRV:
+ if (type0 == DNS_TYPE_SRV) {
+
+ build_reply(&sreply, so, buf, len, question, from, \
+ fromlen, &sd0, NULL, aregion, istcp, wildcard, \
+ NULL, replybuf);
+
+ slen = reply_srv(&sreply, cfg->db);
+ }
+ break;
+
+ case DNS_TYPE_NAPTR:
+ if (type0 == DNS_TYPE_NAPTR) {
+
+ build_reply(&sreply, so, buf, len, question, from, \
+ fromlen, &sd0, NULL, aregion, istcp, wildcard, \
+ NULL, replybuf);
+
+ slen = reply_naptr(&sreply, cfg->db);
+ }
+ break;
+
+ case DNS_TYPE_CNAME:
+ if (type0 == DNS_TYPE_CNAME) {
+
+ build_reply(&sreply, so, buf, len, question, from, \
+ fromlen, &sd0, NULL, aregion, istcp, wildcard, \
+ NULL, replybuf);
+
+ slen = reply_cname(&sreply);
+ } else if (type0 == DNS_TYPE_NS) {
+
+ build_reply(&sreply, so, buf, len, question, from, \
+ fromlen, &sd0, NULL, aregion, istcp, wildcard, \
+ NULL, replybuf);
+
+ slen = reply_ns(&sreply, cfg->db);
+ break;
+ }
+ break;
+
+ case DNS_TYPE_PTR:
+ if (type0 == DNS_TYPE_CNAME) {
+
+ build_reply(&sreply, so, buf, len, question, from, \
+ fromlen, &sd0, ((type1 > 0) ? &sd1 : NULL) \
+ , aregion, istcp, wildcard, NULL, replybuf);
+
+ slen = reply_cname(&sreply);
+ } else if (type0 == DNS_TYPE_NS) {
+
+ build_reply(&sreply, so, buf, len, question, from, \
+ fromlen, &sd0, NULL, aregion, istcp, wildcard, \
+ NULL, replybuf);
+
+ slen = reply_ns(&sreply, cfg->db);
+ break;
+ } else if (type0 == DNS_TYPE_PTR) {
+
+ build_reply(&sreply, so, buf, len, question, from,
+ fromlen, &sd0, NULL, aregion, istcp, wildcard, \
+ NULL, replybuf);
+
+ slen = reply_ptr(&sreply);
+ break; /* must break here */
+ }
+ break;
+ case DNS_TYPE_TXT:
+ if (type0 == DNS_TYPE_TXT) {
+
+ build_reply(&sreply, so, buf, len, question, from, \
+ fromlen, &sd0, NULL, aregion, istcp, wildcard, \
+ NULL, replybuf);
+
+ slen = reply_txt(&sreply);
+ }
+ break;
+ case DNS_TYPE_SPF:
+ if (type0 == DNS_TYPE_SPF) {
+
+ build_reply(&sreply, so, buf, len, question, from, \
+ fromlen, &sd0, NULL, aregion, istcp, wildcard, \
+ NULL, replybuf);
+
+ slen = reply_spf(&sreply);
+ }
+ break;
+ default:
+
+ /*
+ * ANY unkown RR TYPE gets a NOTIMPL
+ */
+ /*
+ * except for delegations
+ */
+
+ if (type0 == DNS_TYPE_NS) {
+
+ build_reply(&sreply, so, buf, len, question, from, \
+ fromlen, &sd0, NULL, aregion, istcp, wildcard, \
+ NULL, replybuf);
+
+ slen = reply_ns(&sreply, cfg->db);
+ } else {
+
+
+ build_reply(&sreply, so, buf, len, question, from, \
+ fromlen, NULL, NULL, aregion, istcp, wildcard, \
+ NULL, replybuf);
+
+ slen = reply_notimpl(&sreply);
+ snprintf(replystring, DNS_MAXNAME, "NOTIMPL");
+ }
+ break;
+ }
+
+ udpout:
+ if (lflag) {
+ dolog(LOG_INFO, "request on descriptor %u interface \"%s\" from %s (ttl=%u, region=%d) for \"%s\" type=%s class=%u, %s%sanswering \"%s\" (%d/%d)\n", so, cfg->ident[i], address, received_ttl, aregion, question->converted_name, get_dns_type(ntohs(question->hdr->qtype)), ntohs(question->hdr->qclass), (question->edns0len ? "edns0, " : ""), (question->dnssecok ? "dnssecok, " : "") , replystring, len, slen);
+
+ }
+
+ if (logging.active == 1 && logging.bind == 0) {
+ remotelog(lfd, "request on descriptor %u interface \"%s\" from %s (ttl=%u, region=%d) for \"%s\" type=%s class=%u, %s%sanswering \"%s\" (%d/%d)", so, cfg->ident[i], address, received_ttl, aregion, question->converted_name, get_dns_type(ntohs(question->hdr->qtype)), ntohs(question->hdr->qclass), (question->edns0len ? "edns0, ": ""), (question->dnssecok ? "dnssecok" : ""), replystring, len, slen);
+ }
+
+ if (fakequestion != NULL) {
+ free_question(fakequestion);
+ }
+
+ free_question(question);
+
+ } /* END ISSET */
+
+ } /* for */
+
+ if (logging.bind == 1 && FD_ISSET(lfd, &rset)) {
+ logfromlen = sizeof(struct sockaddr_storage);
+ len = recvfrom(lfd, buf, sizeof(buf), 0, (struct sockaddr *)&logfrom, &logfromlen);
+ if (len < 0) {
+ dolog(LOG_INFO, "recvfrom: logging %s\n", strerror(errno));
+ } else
+ receivelog(buf, len);
+ }
+
+ drop:
+
+ continue;
+ } /* for (;;) */
+
+ /* NOTREACHED */
+}
+
+/*
+ * BUILD_REPLY - a function that populates struct reply from arguments, doesn't
+ * return anything. This replaces the alias BUILD_REPLY.
+ *
+ */
+
+void
+build_reply(struct sreply *reply, int so, char *buf, int len, struct question *q, struct sockaddr *sa, socklen_t slen, struct domain *sd1, struct domain *sd2, u_int8_t region, int istcp, int wildcard, struct recurses *sr, char *replybuf)
+{
+ reply->so = so;
+ reply->buf = buf;
+ reply->len = len;
+ reply->q = q;
+ reply->sa = sa;
+ reply->salen = slen;
+ reply->sd1 = sd1;
+ reply->sd2 = sd2;
+ reply->region = region;
+ reply->istcp = istcp;
+ reply->wildcard = wildcard;
+ reply->sr = sr;
+ reply->replybuf = replybuf;
+
+ return;
+}
+
+
+void
+recurseheader(struct srecurseheader *rh, int proto, struct sockaddr_storage *src, struct sockaddr_storage *dst, int family)
+{
+ struct sockaddr_in *sin, *sin0;
+ struct sockaddr_in6 *sin6, *sin60;
+
+ rh->af = family;
+ rh->proto = proto;
+
+ if (family == AF_INET) {
+ sin = (struct sockaddr_in *)&rh->dest;
+ sin0 = (struct sockaddr_in *)dst;
+ sin->sin_family = sin0->sin_family;
+ sin->sin_port = sin0->sin_port;
+ memcpy((char *)&sin->sin_addr.s_addr,
+ (char *)&sin0->sin_addr.s_addr,
+ sizeof(sin->sin_addr.s_addr));
+ sin = (struct sockaddr_in *)&rh->source;
+ sin0 = (struct sockaddr_in *)src;
+ sin->sin_family = sin0->sin_family;
+ sin->sin_port = sin0->sin_port;
+ memcpy((char *)&sin->sin_addr.s_addr,
+ (char *)&sin0->sin_addr.s_addr,
+ sizeof(sin->sin_addr.s_addr));
+ } else if (family == AF_INET6) {
+ sin6 = (struct sockaddr_in6 *)&rh->dest;
+ sin60 = (struct sockaddr_in6 *)dst;
+
+ sin6->sin6_family = sin60->sin6_family;
+ sin6->sin6_port = sin60->sin6_port;
+
+ memcpy((char *)&sin6->sin6_addr,
+ (char *)&sin60->sin6_addr,
+ sizeof(sin6->sin6_addr));
+
+ sin6 = (struct sockaddr_in6 *)&rh->source;
+ sin60 = (struct sockaddr_in6 *)src;
+
+ sin6->sin6_family = sin60->sin6_family;
+ sin6->sin6_port = sin60->sin6_port;
+
+ memcpy((char *)&sin6->sin6_addr,
+ (char *)&sin60->sin6_addr,
+ sizeof(sin6->sin6_addr));
+ }
+
+
+ return;
+}
+
+/*
+ * The master process, waits to be killed, if any other processes are killed
+ * and they indicate shutdown through the shared memory segment it will kill
+ * the rest of processes in the parent group.
+ */
+
+void
+setup_master(DB *db, DB_ENV *dbenv, char **av)
+{
+ DB *destroy;
+ char buf[512];
+ pid_t pid;
+ int fd, ret;
+
+#if !defined __linux__ && !defined __APPLE__
+ setproctitle("delphinusdnsd master");
+#endif
+
+ fd = open(PIDFILE, O_WRONLY | O_APPEND | O_CREAT, 0644);
+ if (fd < 0) {
+ dolog(LOG_ERR, "couldn't install pid file, exiting...\n");
+ pid = getpgrp();
+ killpg(pid, SIGTERM);
+ exit(1);
+ }
+
+ pid = getpid();
+ snprintf(buf, sizeof(buf), "%u\n", pid);
+
+ write(fd, buf, strlen(buf));
+ close(fd);
+
+ signal(SIGTERM, master_shutdown);
+ signal(SIGINT, master_shutdown);
+ signal(SIGQUIT, master_shutdown);
+ signal(SIGHUP, master_reload);
+
+ for (;;) {
+ sleep(1);
+
+ if (*ptr) {
+ dolog(LOG_INFO, "pid %u died, killing delphinusdnsd\n", *ptr);
+ master_shutdown(SIGTERM);
+ }
+
+ if (mshutdown) {
+ dolog(LOG_INFO, "shutting down on signal %d\n", msig);
+ unlink(PIDFILE);
+ db->close(db, 0);
+
+
+ if (db_create((DB **)&destroy, (DB_ENV *)dbenv, 0) != 0) {
+ dolog(LOG_INFO, "db_create: %s\n", strerror(errno));
+ }
+
+ ret = destroy->remove(destroy, database, NULL, 0);
+ if (ret != 0) {
+ dolog(LOG_INFO, "db->remove: %s\n", db_strerror(ret));
+ }
+
+ dbenv->close(dbenv, 0);
+
+ /* clean up our database */
+ pid = getpid();
+ snprintf(buf, sizeof(buf), "%s/%lu/__db.001", MYDB_PATH,
+ (long)getpid());
+ unlink(buf);
+ snprintf(buf, sizeof(buf), "%s/%lu", MYDB_PATH,
+ (long)getpid());
+
+ rmdir(buf);
+
+ pid = getpgrp();
+ killpg(pid, msig);
+
+ exit(0);
+ }
+
+ if (reload) {
+
+ signal(SIGTERM, SIG_IGN);
+
+ pid = getpgrp();
+ killpg(pid, SIGTERM);
+ if (munmap(ptr, sizeof(int)) < 0) {
+ dolog(LOG_ERR, "munmap: %s\n", strerror(errno));
+ }
+
+ unlink(PIDFILE);
+ db->close(db, 0);
+
+ if (db_create((DB **)&destroy, (DB_ENV *)dbenv, 0) != 0) {
+ dolog(LOG_INFO, "db_create: %s\n", strerror(errno));
+ }
+
+ ret = destroy->remove(destroy, database, NULL, 0);
+ if (ret != 0) {
+ dolog(LOG_INFO, "db->remove: %s\n", db_strerror(ret));
+ }
+
+ dbenv->close(dbenv, 0);
+
+ /* clean up our database */
+ pid = getpid();
+ snprintf(buf, sizeof(buf), "%s/%lu/__db.001", MYDB_PATH,
+ (long)getpid());
+ unlink(buf);
+ snprintf(buf, sizeof(buf), "%s/%lu", MYDB_PATH,
+ (long)getpid());
+
+ rmdir(buf);
+
+ dolog(LOG_INFO, "restarting on SIGHUP\n");
+
+ closelog();
+ if (execvp("/usr/local/sbin/delphinusdnsd", av) < 0) {
+ dolog(LOG_ERR, "execvp: %s\n", strerror(errno));
+ }
+ /* NOTREACHED */
+ exit(1);
+ }
+ }
+
+ /* NOTREACHED */
+}
+
+/*
+ * master_shutdown - unlink pid file and kill parent group
+ */
+
+void
+master_shutdown(int sig)
+{
+ msig = sig;
+ mshutdown = 1;
+}
+
+/*
+ * slave_shutdown - a slave wishes to shutdown, enter its pid into the
+ * shutdown shared memory and return.
+ */
+
+void
+slave_shutdown(void)
+{
+ pid_t pid;
+
+ pid = getpid();
+
+ *ptr = pid;
+}
+
+/*
+ * slave_signal - a slave got a signal, call slave_shutdown and exit..
+ */
+
+void
+slave_signal(int sig)
+{
+ slave_shutdown();
+ dolog(LOG_INFO, "shutting down on signal\n");
+ exit(1);
+}
+
+/*
+ * master_reload - reload the delphinusdnsd system
+ */
+
+void
+master_reload(int sig)
+{
+ reload = 1;
+}
+
+/*
+ * CHECK_QTYPE - check the query type and return appropriately if we have
+ * such a record in our DB..
+ * returns 0 on error, or the DNS TYPE from 1 through 65535
+ * when the return is 0 the error variable is set with the error
+ * code (-1 or -2)
+ */
+
+u_int16_t
+check_qtype(struct domain *sd, u_int16_t type, int nxdomain, int *error)
+{
+ u_int16_t returnval;
+
+ switch (type) {
+
+ case DNS_TYPE_ANY:
+ returnval = DNS_TYPE_ANY;
+ break;
+
+ case DNS_TYPE_A:
+ if ((sd->flags & DOMAIN_HAVE_A) == DOMAIN_HAVE_A) {
+ returnval = DNS_TYPE_A;
+ break;
+ } else if ((sd->flags & DOMAIN_HAVE_CNAME) == DOMAIN_HAVE_CNAME) {
+ returnval = DNS_TYPE_CNAME;
+ break;
+ }
+
+ *error = -1;
+ return 0;
+ case DNS_TYPE_AAAA:
+ if ((sd->flags & DOMAIN_HAVE_AAAA) == DOMAIN_HAVE_AAAA) {
+ returnval = DNS_TYPE_AAAA;
+ break;
+ } else if ((sd->flags & DOMAIN_HAVE_CNAME) == DOMAIN_HAVE_CNAME) {
+ returnval = DNS_TYPE_CNAME;
+ break;
+ }
+
+ *error = -1;
+ return 0;
+ case DNS_TYPE_MX:
+ if ((sd->flags & DOMAIN_HAVE_MX) ==
+ DOMAIN_HAVE_MX) {
+ returnval = DNS_TYPE_MX;
+ break;
+ } else if ((sd->flags & DOMAIN_HAVE_CNAME) == DOMAIN_HAVE_CNAME) {
+ returnval = DNS_TYPE_CNAME;
+ break;
+ }
+
+ *error = -1;
+ return 0;
+ case DNS_TYPE_PTR:
+ if ((sd->flags & DOMAIN_HAVE_PTR) == DOMAIN_HAVE_PTR) {
+ returnval = DNS_TYPE_PTR;
+ break;
+ } else if ((sd->flags & DOMAIN_HAVE_CNAME) == DOMAIN_HAVE_CNAME) {
+ returnval = DNS_TYPE_CNAME;
+ break;
+ }
+
+ *error = -1;
+ return 0;
+
+ case DNS_TYPE_SOA:
+ if ((sd->flags & DOMAIN_HAVE_SOA) == DOMAIN_HAVE_SOA) {
+
+ returnval = DNS_TYPE_SOA;
+ break;
+ }
+
+ if (nxdomain)
+ *error = -2;
+ else
+ *error = -1;
+
+ return 0;
+
+ case DNS_TYPE_SSHFP:
+ if ((sd->flags & DOMAIN_HAVE_SSHFP) == DOMAIN_HAVE_SSHFP) {
+ returnval = DNS_TYPE_SSHFP;
+ break;
+ }
+
+ *error = -1;
+ return 0;
+
+ case DNS_TYPE_SRV:
+ if ((sd->flags & DOMAIN_HAVE_SRV) == DOMAIN_HAVE_SRV) {
+ returnval = DNS_TYPE_SRV;
+ break;
+ }
+
+ *error = -1;
+ return 0;
+
+ case DNS_TYPE_NAPTR:
+ if ((sd->flags & DOMAIN_HAVE_NAPTR) == DOMAIN_HAVE_NAPTR) {
+ returnval = DNS_TYPE_NAPTR;
+ break;
+ }
+
+ *error = -1;
+ return 0;
+ case DNS_TYPE_CNAME:
+ if ((sd->flags & DOMAIN_HAVE_CNAME) == DOMAIN_HAVE_CNAME) {
+ returnval = DNS_TYPE_CNAME;
+ break;
+ }
+
+ *error = -1;
+ return 0;
+
+ case DNS_TYPE_NS:
+ if ((sd->flags & DOMAIN_HAVE_NS) == DOMAIN_HAVE_NS) {
+ returnval = DNS_TYPE_NS;
+ break;
+ }
+
+ *error = -1;
+ return 0;
+ case DNS_TYPE_TXT:
+ if ((sd->flags & DOMAIN_HAVE_TXT) == DOMAIN_HAVE_TXT) {
+ returnval = DNS_TYPE_TXT;
+ break;
+ }
+
+ *error = -1;
+ return 0;
+ case DNS_TYPE_SPF:
+ if ((sd->flags & DOMAIN_HAVE_SPF) == DOMAIN_HAVE_SPF) {
+ returnval = DNS_TYPE_SPF;
+ break;
+ }
+
+ *error = -1;
+ return 0;
+
+ default: /* RR's that we don't support, but have a zone for */
+
+ *error = -1;
+ return 0;
+ break;
+ }
+
+ return (returnval);
+}
blob - /dev/null
blob + 606ea517d40d0d456c34166428272cda53cb8c62 (mode 644)
--- /dev/null
+++ parse.y
@@ -0,0 +1,2787 @@
+/*
+ * Copyright (c) 2014 Peter J. Philipp. All rights reserved.
+ * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
+ * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
+ * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
+ * Copyright (c) 2001 Markus Friedl. All rights reserved.
+ * Copyright (c) 2001 Daniel Hartmeier. All rights reserved.
+ * Copyright (c) 2001 Theo de Raadt. All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+%{
+#include "include.h"
+#include "dns.h"
+#include "db.h"
+
+
+extern void dolog(int, char *, ...);
+extern char *dns_label(char *, int *);
+extern u_int8_t find_region(struct sockaddr_storage *, int);
+extern int insert_region(char *, char *, u_int8_t);
+extern int insert_axfr(char *, char *);
+extern int insert_notifyslave(char *, char *);
+extern int insert_filter(char *, char *);
+extern int insert_recurse(char *, char *);
+extern int insert_whitelist(char *, char *);
+extern int insert_wildcard(char *, char *);
+extern void slave_shutdown(void);
+void yyerror(const char *);
+
+extern int whitelist;
+extern int notify;
+extern int errno;
+extern int debug;
+extern int verbose;
+extern int bflag;
+extern int iflag;
+extern int lflag;
+extern int nflag;
+extern int rflag;
+extern int bcount;
+extern int icount;
+extern int ratelimit;
+extern int ratelimit_packets_per_second;
+extern u_int16_t port;
+extern u_int32_t cachesize;
+extern char *bind_list[255];
+extern char *interface_list[255];
+
+
+
+TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files);
+static struct file {
+ TAILQ_ENTRY(file) entry;
+ FILE *stream;
+ char *name;
+ int lineno;
+ int errors;
+} *file, *topfile;
+
+#define STATE_IP 1
+#define STATE_ZONE 2
+
+#define WILDCARDVERSION 6
+
+#define CONFIG_START 0x1
+#define CONFIG_VERSION 0x2
+#define CONFIG_REGION 0x4
+#define CONFIG_ZONE 0x8
+#define CONFIG_INCLUDE 0x10
+#define CONFIG_WILDCARDONLYFOR 0x20
+#define CONFIG_RECURSEFOR 0x40
+#define CONFIG_LOGGING 0x80
+#define CONFIG_AXFRFOR 0x100
+#define CONFIG_AXFRPORT 0x200
+
+typedef struct {
+ union {
+ char *string;
+ int intval;
+ } v;
+ int lineno;
+} YYSTYPE;
+
+#ifdef __APPLE__
+#define YYSTYPE_IS_DECLARED 1
+#endif
+
+static const char rcsid[] = "$Id: parse.y,v 1.1.1.1 2014/11/14 08:09:04 pjp Exp $";
+static int version = 0;
+static int state = 0;
+static uint8_t region = 0;
+static uint64_t confstatus = 0;
+static DB *mydb;
+
+YYSTYPE yylval;
+
+
+char *converted_name;
+int converted_namelen;
+DBT key, data;
+struct logging logging;
+int axfrport = 0;
+time_t time_changed;
+
+
+
+char *check_rr(char *, char *, int, int *);
+int fill_a(char *, char *, int, char *);
+int fill_aaaa(char *, char *, int, char *);
+int fill_balance(char *, char *, int, char *);
+int fill_ptr(char *, char *, int, char *);
+int fill_cname(char *, char *, int, char *);
+int fill_mx(char *, char *, int, int, char *);
+int fill_naptr(char *, char *, int, int, int, char *, char *, char *, char *);
+int fill_ns(char *, char *, int, char *);
+int fill_soa(char *, char *, int, char *, char *, int, int, int, int, int);
+int fill_spf(char *, char *, int, char *);
+int fill_sshfp(char *, char *, int, int, int, char *);
+int fill_srv(char *, char *, int, int, int, int, char *);
+int fill_txt(char *, char *, int, char *);
+int findeol(void);
+int get_ip(char *, int);
+char *get_prefixlen(char *, char *, int);
+int get_quotedstring(char *, int);
+int get_record(struct domain *, char *, int);
+int get_string(char *, int);
+int lgetc(int);
+struct tab * lookup(struct tab *, char *);
+int lungetc(int);
+int parse_file(DB *, char *);
+struct file *pushfile(const char *, int);
+int popfile(void);
+struct rrtab *rrlookup(struct rrtab *, char *);
+void set_record(struct domain *, char *, int);
+static int temp_inet_net_pton_ipv6(const char *, void *, size_t);
+int yyparse(void);
+
+
+struct rrtab {
+ char *name;
+ u_int16_t type;
+} myrrtab[] = {
+ { "a", DNS_TYPE_A } ,
+ { "soa", DNS_TYPE_SOA },
+ { "cname", DNS_TYPE_CNAME },
+ { "ptr", DNS_TYPE_PTR },
+ { "mx", DNS_TYPE_MX },
+ { "aaaa", DNS_TYPE_AAAA },
+ { "ns", DNS_TYPE_NS },
+ { "txt", DNS_TYPE_TXT },
+ { "hint", DNS_TYPE_HINT },
+ { "delegate", DNS_TYPE_DELEGATE },
+ { "balance", DNS_TYPE_BALANCE },
+ { "srv", DNS_TYPE_SRV },
+ { "spf", DNS_TYPE_SPF },
+ { "sshfp", DNS_TYPE_SSHFP },
+ { "naptr", DNS_TYPE_NAPTR },
+ { NULL, 0 },
+};
+
+
+
+
+%}
+
+
+%token VERSION OBRACE EBRACE REGION AXFRFOR RECURSEFOR
+%token DOT COLON TEXT WOF INCLUDE ZONE COMMA CRLF
+%token ERROR AXFRPORT LOGGING OPTIONS FILTER NOTIFY
+%token WHITELIST
+
+%token <v.string> POUND
+%token <v.string> SEMICOLON
+%token <v.string> STRING
+%token <v.string> IP
+%token <v.string> IPV6
+%token <v.string> SLASH
+%token <v.string> QUOTEDSTRING
+
+%token <v.intval> NUMBER
+
+%type <v.string> quotednumber quotedfilename ipcidr
+
+%start cmd_list
+
+%%
+cmd_list:
+ | cmd_list cmd
+ ;
+
+cmd :
+ version
+ | axfrport
+ | include
+ | zone
+ | region CRLF
+ | wof CRLF
+ | axfr CRLF
+ | notify CRLF
+ | whitelist CRLF
+ | filter CRLF
+ | recurse CRLF
+ | logging
+ | comment CRLF
+ | options
+ ;
+
+
+comment:
+ comment comments
+ | comments
+ ;
+
+comments:
+ SEMICOLON
+ | POUND
+ ;
+
+version:
+ VERSION quotednumber SEMICOLON CRLF
+ {
+ version = atoi($2);
+ if (version != WILDCARDVERSION) {
+ dolog(LOG_ERR, "version of configfile is wrong,"
+ " must be \"%d\"!\n", WILDCARDVERSION);
+ return (-1);
+ }
+ free ($2);
+
+ confstatus |= CONFIG_VERSION;
+ }
+ ;
+
+axfrport:
+ AXFRPORT quotednumber SEMICOLON CRLF
+ {
+ if ((confstatus & CONFIG_VERSION) != CONFIG_VERSION) {
+ dolog(LOG_INFO, "There must be a version at the top of the first configfile\n");
+ return (-1);
+ }
+
+ axfrport = atoi($2);
+ free ($2);
+ }
+ ;
+
+quotednumber:
+ QUOTEDSTRING
+ {
+ if (debug)
+ printf("quotednumber is %s\n", $$);
+ }
+ ;
+
+include:
+ includes CRLF
+ ;
+
+includes:
+ INCLUDE quotedfilename SEMICOLON {
+ struct file *nfile;
+
+ if ((confstatus & CONFIG_VERSION) != CONFIG_VERSION) {
+ dolog(LOG_INFO, "There must be a version at the top of the first configfile\n");
+ return (-1);
+ }
+
+ if ((nfile = pushfile($2, 0)) == NULL) {
+ fprintf(stderr, "failed to include file %s\n", $2);
+ free($2);
+ return (-1);
+ }
+
+ free($2);
+
+ file = nfile;
+ }
+ ;
+
+quotedfilename:
+ QUOTEDSTRING
+ {
+ if (debug)
+ printf("quotedfilename is %s\n", $$);
+ }
+ ;
+
+/* zone */
+
+zone:
+ ZONE zonelabel zonecontent
+ {
+ if ((confstatus & CONFIG_VERSION) != CONFIG_VERSION) {
+ dolog(LOG_INFO, "There must be a version at the top of the first configfile\n");
+ return (-1);
+ }
+ }
+ ;
+
+zonelabel:
+ QUOTEDSTRING
+ ;
+
+zonecontent:
+ OBRACE zonestatements EBRACE CRLF
+ | OBRACE CRLF zonestatements EBRACE CRLF
+ ;
+
+
+zonestatements :
+ zonestatements zonestatement
+ | zonestatement
+ ;
+
+
+zonestatement:
+
+ /* centroid.eu,soa,3600,uranus.centroid.eu.,pjp.solarscale.de.,1258740680,3600,1800,7200,3600 */
+
+ STRING COMMA STRING COMMA NUMBER COMMA STRING COMMA STRING COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER CRLF
+ {
+ if (strcasecmp($3, "soa") == 0) {
+ if (fill_soa($1, $3, $5, $7, $9, $11, $13, $15, $17, $19) < 0) {
+ return -1;
+ }
+
+ if (debug)
+ printf("%s SOA\n", $1);
+ } else {
+ if (debug)
+ printf("soa error\n");
+ return -1;
+ }
+
+
+ free ($1);
+ free ($3);
+ free ($7);
+ free ($9);
+ }
+ |
+ STRING COMMA STRING COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA QUOTEDSTRING CRLF
+ {
+ if (strcasecmp($3, "sshfp") == 0) {
+ if (fill_sshfp($1, $3, $5, $7, $9, $11) < 0) {
+ return -1;
+ }
+
+ } else {
+ if (debug)
+ printf("another sshfp record I don't know about?");
+ return (-1);
+ }
+
+ free ($1);
+ free ($3);
+ free ($11);
+ }
+ |
+ STRING COMMA STRING COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA STRING CRLF
+ {
+ if (strcasecmp($3, "srv") == 0) {
+ if (fill_srv($1, $3, $5, $7, $9, $11, $13) < 0) {
+ return -1;
+ }
+
+ } else {
+ if (debug)
+ printf("2 another record I don't know about?");
+ return (-1);
+ }
+
+ free ($1);
+ free ($3);
+ free ($13);
+ }
+ |
+ STRING COMMA STRING COMMA NUMBER COMMA STRING CRLF {
+ if (strcasecmp($3, "ns") == 0 ||
+ strcasecmp($3, "delegate") == 0 ||
+ strcasecmp($3, "hint") == 0) {
+ if (fill_ns($1, $3, $5, $7) < 0) {
+ return -1;
+ }
+
+ if (debug)
+ printf("%s NS\n", $1);
+
+ } else if (strcasecmp($3, "ptr") == 0) {
+ if (fill_ptr($1, $3, $5, $7) < 0) {
+ return -1;
+ }
+
+ if (debug)
+ printf("%s PTR\n", $1);
+
+ } else if (strcasecmp($3, "cname") == 0) {
+ if (fill_cname($1, $3, $5, $7) < 0) {
+ return -1;
+ }
+
+ if (debug)
+ printf("%s CNAME\n", $3);
+
+ } else {
+ if (debug)
+ printf("%s other\n", $3);
+ return -1;
+ }
+
+ free ($1);
+ free ($3);
+ free ($7);
+ }
+ |
+ STRING COMMA STRING COMMA NUMBER COMMA IPV6 CRLF {
+ if (strcasecmp($3, "aaaa") == 0) {
+ if (fill_aaaa($1, $3, $5, $7) < 0) {
+ return -1;
+ }
+
+ if (debug)
+ printf("%s AAAA\n", $1);
+ } else {
+
+ if (debug)
+ printf("error AAAA\n");
+ return (-1);
+ }
+ free ($1);
+ free ($3);
+ free ($7);
+ }
+ |
+ STRING COMMA STRING COMMA NUMBER COMMA IP CRLF
+ {
+ if (strcasecmp($3, "a") == 0) {
+ if (fill_a($1, $3, $5, $7) < 0) {
+ return -1;
+ }
+
+ if (debug)
+ printf("%s A\n", $1);
+
+ } else if (strcasecmp($3, "balance") == 0) {
+ if (fill_balance($1, $3, $5, $7) < 0) {
+ return -1;
+ }
+
+ if (debug)
+ printf("a balance record?\n");
+ } else {
+ if (debug)
+ printf("another a record?\n");
+ return -1;
+ }
+
+ free ($1);
+ free ($3);
+ free ($7);
+ }
+ |
+ STRING COMMA STRING COMMA NUMBER COMMA NUMBER COMMA STRING CRLF
+ {
+ if (strcasecmp($3, "mx") == 0) {
+ if (fill_mx($1, $3, $5, $7, $9) < 0) {
+ return -1;
+ }
+
+ if (debug)
+ printf("%s MX -> %d %s\n", $1, $7, $9);
+
+ } else {
+ if (debug)
+ printf("another record I don't know about?");
+ return (-1);
+ }
+
+ free ($1);
+ free ($3);
+ free ($9);
+ }
+ |
+ STRING COMMA STRING COMMA NUMBER COMMA QUOTEDSTRING CRLF
+ {
+ if (strcasecmp($3, "txt") == 0) {
+ if (fill_txt($1, $3, $5, $7) < 0) {
+ return -1;
+ }
+
+ if (debug)
+ printf(" %s TXT -> %s\n", $1, $7);
+ } else if (strcasecmp($3, "spf") == 0) {
+ if (fill_spf($1, $3, $5, $7) < 0) {
+ return -1;
+ }
+
+ if (debug)
+ printf(" %s SPF -> %s\n", $1, $7);
+ } else {
+ if (debug)
+ printf("another txt/spf like record I don't know?\n");
+ return (-1);
+ }
+
+ free ($1);
+ free ($3);
+ free ($7);
+ }
+ |
+ STRING COMMA STRING COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA QUOTEDSTRING COMMA QUOTEDSTRING COMMA QUOTEDSTRING COMMA STRING CRLF
+ {
+ if (strcasecmp($3, "naptr") == 0) {
+ if (fill_naptr($1, $3, $5, $7, $9, $11, $13, $15, $17) < 0) {
+ return -1;
+ }
+
+ if (debug)
+ printf(" %s NAPTR\n", $1);
+ } else {
+ if (debug)
+ printf("another naptr like record I don't know?\n");
+ return (-1);
+ }
+
+ free ($1);
+ free ($3);
+ free ($11);
+ free ($13);
+ free ($15);
+ free ($17);
+ }
+ | comment CRLF
+ ;
+
+
+options:
+ OPTIONS optionslabel optionscontent
+ {
+ if ((confstatus & CONFIG_VERSION) != CONFIG_VERSION) {
+ dolog(LOG_INFO, "There must be a version at the top of the first configfile\n");
+ return (-1);
+ }
+ }
+ ;
+
+optionslabel:
+ QUOTEDSTRING
+ ;
+
+optionscontent:
+ OBRACE optionsstatements EBRACE CRLF
+ | OBRACE CRLF optionsstatements EBRACE CRLF
+ ;
+
+optionsstatements:
+ optionsstatement
+ | optionsstatements optionsstatement
+ ;
+
+optionsstatement:
+
+ STRING SEMICOLON CRLF
+ {
+ if (strcasecmp($1, "recurse") == 0) {
+ dolog(LOG_DEBUG, "recursive server on\n");
+ rflag = 0; /* keep it off please! */
+ } else if (strcasecmp($1, "log") == 0) {
+ dolog(LOG_DEBUG, "logging on\n");
+ lflag = 1;
+ }
+ }
+ |
+ STRING QUOTEDSTRING SEMICOLON CRLF
+ {
+ if (strcasecmp($1, "interface") == 0) {
+ iflag = 1;
+ if (icount > 253) {
+ dolog(LOG_ERR, "too many interface keywords in options\n");
+ return (-1);
+ }
+
+ dolog(LOG_DEBUG, "interface \"%s\" added\n", $2);
+ interface_list[icount++] = $2;
+ }
+
+ }
+ |
+ STRING NUMBER SEMICOLON CRLF
+ {
+ if (strcasecmp($1, "fork") == 0) {
+ dolog(LOG_DEBUG, "forking %d times\n", $2);
+ nflag = $2;
+ } else if (strcasecmp($1, "port") == 0) {
+ port = $2 & 0xffff;
+ dolog(LOG_DEBUG, "listening on port %d\n", port);
+ } else if (strcasecmp($1, "ratelimit-pps") == 0) {
+ if ($2 > 127 || $2 < 1) {
+ dolog(LOG_ERR, "ratelimit packets per second must be between 1 and 127, or leave it off!\n");
+ return -1;
+ }
+ ratelimit = 1;
+ ratelimit_packets_per_second = $2;
+ dolog(LOG_DEBUG, "ratelimiting to %d packets per second", ratelimit_packets_per_second);
+ }
+
+ }
+ |
+ STRING ipcidr SEMICOLON CRLF
+ {
+ if (strcasecmp($1, "bind") == 0) {
+ bflag = 1;
+ if (bcount > 253) {
+ dolog(LOG_ERR, "too many bind keywords in options\n");
+ return (-1);
+ }
+ dolog(LOG_DEBUG, "binding to %s\n", $2);
+ bind_list[bcount++] = $2;
+ }
+
+ }
+ | comment CRLF
+ ;
+
+/* logging below */
+
+logging:
+ LOGGING logginglabel loggingcontent
+ {
+ if ((confstatus & CONFIG_VERSION) != CONFIG_VERSION) {
+ dolog(LOG_INFO, "There must be a version at the top of the first configfile\n");
+ return (-1);
+ }
+ }
+ ;
+
+logginglabel:
+ QUOTEDSTRING
+ ;
+
+loggingcontent:
+ OBRACE loggingstatements EBRACE CRLF
+ | OBRACE CRLF loggingstatements EBRACE CRLF
+ ;
+
+loggingstatements:
+ loggingstatement CRLF
+ | loggingstatements loggingstatement CRLF
+ ;
+
+loggingstatement:
+ STRING STRING SEMICOLON
+ {
+ char buf[512];
+
+ if (strcasecmp($1, "logbind") == 0) {
+ logging.active = 1;
+ logging.bind = 0;
+
+ gethostname(buf, sizeof(buf));
+ logging.hostname = strdup(buf);
+ if (logging.hostname == NULL) {
+ dolog(LOG_ERR, "strdup failed\n");
+ return (-1);
+ }
+
+ if (strcmp($2, "yes") == 0) {
+ logging.bind = 1;
+ }
+ } else if (strcasecmp($1, "logpasswd") == 0) {
+
+ logging.logpasswd = strdup($2);
+
+ if (logging.logpasswd == NULL) {
+ dolog(LOG_ERR, "strdup failed\n");
+ return (-1);
+ }
+
+ } else {
+ if (debug)
+ printf("another logging statement I don't know?\n");
+ return (-1);
+ }
+
+ }
+ |
+ STRING NUMBER SEMICOLON
+ {
+ char buf[16];
+
+ if (strcasecmp($1, "logport") == 0) {
+ snprintf(buf, sizeof(buf), "%d", $2);
+ logging.logport = strdup(buf);
+ if (logging.logport == NULL) {
+ dolog(LOG_ERR, "strdup failed\n");
+ return (-1);
+ }
+ logging.logport2 = $2;
+ }
+ }
+ |
+ STRING ipcidr SEMICOLON
+ {
+ struct addrinfo hints, *res0;
+ struct sockaddr_in6 *psin6;
+ struct sockaddr_in *psin;
+ int error;
+
+ if (strcasecmp($1, "loghost") == 0) {
+ logging.loghost = strdup($2);
+ if (logging.loghost == NULL) {
+ dolog(LOG_ERR, "strdup failed\n");
+
+ return (-1);
+ }
+
+ if (strchr($2, ':') != NULL) {
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_INET6;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_NUMERICHOST;
+
+ error = getaddrinfo($2, "www", &hints, &res0);
+ if (error) {
+ dolog(LOG_ERR, "%s line %d: %s\n",
+ file->name, file->lineno,
+ gai_strerror(error));
+
+ return (-1);
+ }
+
+ if (res0 == NULL) {
+ dolog(LOG_ERR, "%s line %d: could not"
+ " determine IPv6 address\n"
+ , file->name, file->lineno);
+ return (-1);
+ }
+
+ psin6 = (struct sockaddr_in6 *)&logging.loghost2;
+ psin6->sin6_family = res0->ai_family;
+ memcpy(psin6, res0->ai_addr, res0->ai_addrlen);
+ freeaddrinfo(res0);
+ } else {
+ memset(&hints, 0, sizeof(hints));
+
+ hints.ai_family = AF_INET;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_NUMERICHOST;
+
+ error = getaddrinfo($2, "www", &hints, &res0);
+ if (error) {
+ dolog(LOG_ERR, "%s line %d: %s\n",
+ file->name, file->lineno,
+ gai_strerror(error));
+
+ return (-1);
+ }
+
+ if (res0 == NULL) {
+ dolog(LOG_ERR, "%s line %d: could not"
+ " determine IPv6 address\n"
+ , file->name, file->lineno);
+ return (-1);
+ }
+
+ psin = (struct sockaddr_in *)&logging.loghost2;
+ psin->sin_family = res0->ai_family;
+ memcpy(psin, res0->ai_addr, res0->ai_addrlen);
+
+ freeaddrinfo(res0);
+ }
+ } else {
+ if (debug)
+ printf("2 another logging statement I don't know?\n");
+ return (-1);
+ }
+ }
+ | comment CRLF
+ ;
+
+/* whitelist "these hosts" { .. } */
+
+whitelist:
+ WHITELIST whitelistlabel whitelistcontent
+ {
+ if ((confstatus & CONFIG_VERSION) != CONFIG_VERSION) {
+ dolog(LOG_INFO, "There must be a version at the top of the first configfile\n");
+ return (-1);
+ }
+ }
+ ;
+
+whitelistlabel:
+ QUOTEDSTRING
+ ;
+
+whitelistcontent:
+ OBRACE whiteliststatements EBRACE
+ | OBRACE CRLF whiteliststatements EBRACE
+ ;
+
+whiteliststatements :
+ whiteliststatements whiteliststatement
+ | whiteliststatement
+ ;
+
+whiteliststatement : ipcidr SEMICOLON CRLF
+ {
+ char prefixlength[INET_ADDRSTRLEN];
+ char *dst;
+
+
+ if ((dst = get_prefixlen($1, (char *)&prefixlength, sizeof(prefixlength))) == NULL) {
+ return (-1);
+ }
+
+ if (insert_whitelist(dst, prefixlength) < 0) {
+ dolog(LOG_ERR, "insert_whitelist, line %d\n", file->lineno);
+ return (-1);
+ }
+
+ if (debug)
+ printf("recurse inserted %s address\n", $1);
+
+ whitelist = 1;
+
+ free (dst);
+ free ($1);
+ }
+ | comment CRLF
+ ;
+
+/* filter "these hosts" { .. } */
+
+filter:
+ FILTER filterlabel filtercontent
+ {
+ if ((confstatus & CONFIG_VERSION) != CONFIG_VERSION) {
+ dolog(LOG_INFO, "There must be a version at the top of the first configfile\n");
+ return (-1);
+ }
+ }
+ ;
+
+filterlabel:
+ QUOTEDSTRING
+ ;
+
+filtercontent:
+ OBRACE filterstatements EBRACE
+ | OBRACE CRLF filterstatements EBRACE
+ ;
+
+filterstatements :
+ filterstatements filterstatement
+ | filterstatement
+ ;
+
+filterstatement : ipcidr SEMICOLON CRLF
+ {
+ char prefixlength[INET_ADDRSTRLEN];
+ char *dst;
+
+
+ if ((dst = get_prefixlen($1, (char *)&prefixlength, sizeof(prefixlength))) == NULL) {
+ return (-1);
+ }
+
+ if (insert_filter(dst, prefixlength) < 0) {
+ dolog(LOG_ERR, "insert_filter, line %d\n", file->lineno);
+ return (-1);
+ }
+
+ if (debug)
+ printf("recurse inserted %s address\n", $1);
+
+ free (dst);
+ free ($1);
+ }
+ | comment CRLF
+ ;
+
+
+/* notify "these hosts" { .. } */
+
+notify:
+ NOTIFY notifylabel notifycontent
+ {
+ if ((confstatus & CONFIG_VERSION) != CONFIG_VERSION) {
+ dolog(LOG_INFO, "There must be a version at the top of the first configfile\n");
+ return (-1);
+ }
+ }
+ ;
+
+notifylabel:
+ QUOTEDSTRING
+ ;
+
+notifycontent:
+ OBRACE notifystatements EBRACE
+ | OBRACE CRLF notifystatements EBRACE
+ ;
+
+notifystatements :
+ notifystatements notifystatement
+ | notifystatement
+ ;
+
+notifystatement : ipcidr SEMICOLON CRLF
+ {
+ char prefixlength[INET_ADDRSTRLEN];
+ char *dst;
+
+
+ if ((dst = get_prefixlen($1, (char *)&prefixlength, sizeof(prefixlength))) == NULL) {
+ return (-1);
+ }
+
+ if (insert_notifyslave(dst, prefixlength) < 0) {
+ dolog(LOG_ERR, "insert_notifyslave, line %d\n", file->lineno);
+ return (-1);
+ }
+
+ notify++;
+
+ free (dst);
+ free ($1);
+ }
+ | comment CRLF
+ ;
+
+/* axfr-for "these hosts" { .. } */
+
+axfr:
+ AXFRFOR axfrlabel axfrcontent
+ {
+ if ((confstatus & CONFIG_VERSION) != CONFIG_VERSION) {
+ dolog(LOG_INFO, "There must be a version at the top of the first configfile\n");
+ return (-1);
+ }
+ }
+ ;
+
+axfrlabel:
+ QUOTEDSTRING
+ ;
+
+axfrcontent:
+ OBRACE axfrstatements EBRACE
+ | OBRACE CRLF axfrstatements EBRACE
+ ;
+
+axfrstatements :
+ axfrstatements axfrstatement
+ | axfrstatement
+ ;
+
+axfrstatement : ipcidr SEMICOLON CRLF
+ {
+ char prefixlength[INET_ADDRSTRLEN];
+ char *dst;
+
+
+ if ((dst = get_prefixlen($1, (char *)&prefixlength, sizeof(prefixlength))) == NULL) {
+ return (-1);
+ }
+
+ if (insert_axfr(dst, prefixlength) < 0) {
+ dolog(LOG_ERR, "insert_axfr, line %d\n", file->lineno);
+ return (-1);
+ }
+
+ if (debug)
+ printf("recurse inserted %s address\n", $1);
+
+ free (dst);
+ free ($1);
+ }
+ | comment CRLF
+ ;
+
+/* recurse-for "these hosts" { .. } */
+
+recurse:
+ RECURSEFOR recurselabel recursecontent
+ {
+ if ((confstatus & CONFIG_VERSION) != CONFIG_VERSION) {
+ dolog(LOG_INFO, "There must be a version at the top of the first configfile\n");
+ return (-1);
+ }
+ }
+ ;
+
+recurselabel:
+ QUOTEDSTRING
+ ;
+
+recursecontent:
+ OBRACE recursestatements EBRACE
+ | OBRACE CRLF recursestatements EBRACE
+ ;
+
+recursestatements :
+ recursestatements recursestatement
+ | recursestatement
+ ;
+
+recursestatement : ipcidr SEMICOLON CRLF
+ {
+ char prefixlength[INET_ADDRSTRLEN];
+ char *dst;
+
+ if ((dst = get_prefixlen($1, (char *)&prefixlength, sizeof(prefixlength))) == NULL) {
+ return (-1);
+ }
+
+ if (insert_recurse(dst, prefixlength) < 0) {
+ dolog(LOG_ERR, "insert_recurse, line %d\n", file->lineno);
+ return (-1);
+ }
+
+ if (debug)
+ printf("recurse inserted %s address\n", $1);
+
+ free (dst);
+ free ($1);
+ }
+ | comment CRLF
+ ;
+
+
+/* wildcard-only-for "these hosts" { .. } */
+
+wof:
+ WOF woflabel wofcontent
+ {
+ if ((confstatus & CONFIG_VERSION) != CONFIG_VERSION) {
+ dolog(LOG_INFO, "There must be a version at the top of the first configfile\n");
+ return (-1);
+ }
+ }
+ ;
+
+woflabel:
+ QUOTEDSTRING
+ ;
+
+wofcontent:
+ OBRACE wofstatements EBRACE
+ | OBRACE CRLF wofstatements EBRACE
+ ;
+
+wofstatements :
+ wofstatements wofstatement
+ | wofstatement
+ ;
+
+wofstatement : ipcidr SEMICOLON CRLF
+ {
+ char prefixlength[INET_ADDRSTRLEN];
+ char *dst;
+
+ if ((dst = get_prefixlen($1, (char *)&prefixlength, sizeof(prefixlength))) == NULL) {
+
+ return (-1);
+ }
+
+ if (insert_wildcard(dst, prefixlength) < 0) {
+ dolog(LOG_ERR, "insert_wildcard, line %d\n", file->lineno);
+ return (-1);
+ }
+
+ if (debug)
+ printf("wildcard inserted %s address\n", $1);
+
+ free (dst);
+ free ($1);
+ }
+ | comment CRLF
+ ;
+
+
+/* region "lacnic" { .. } */
+
+region:
+ REGION regionlabel regioncontent
+ {
+ if ((confstatus & CONFIG_VERSION) != CONFIG_VERSION) {
+ dolog(LOG_INFO, "There must be a version at the top of the first configfile\n");
+ return (-1);
+ }
+
+ region++;
+ }
+ ;
+
+regionlabel:
+ QUOTEDSTRING
+ ;
+
+regioncontent:
+ OBRACE regionstatements EBRACE
+ | OBRACE CRLF regionstatements EBRACE
+ ;
+
+regionstatements :
+ regionstatements regionstatement
+ | regionstatement
+ ;
+
+regionstatement : ipcidr SEMICOLON CRLF
+ {
+ char prefixlength[INET_ADDRSTRLEN];
+ char *dst;
+
+ if ((dst = get_prefixlen($1, (char *)&prefixlength, sizeof(prefixlength))) == NULL) {
+ return (-1);
+ }
+
+ if (insert_region(dst, prefixlength, region) < 0) {
+ dolog(LOG_ERR, "insert_region, line %d\n", file->lineno);
+ return (-1);
+ }
+
+ if (debug)
+ printf("%s ipv4 address\n", dst);
+
+ free (dst);
+ free ($1);
+ }
+ | comment CRLF
+ ;
+
+ipcidr:
+ IP
+ | IPV6
+ ;
+
+
+%%
+
+struct tab {
+ char *val;
+ int num;
+ int state;
+};
+
+
+struct tab cmdtab[] = {
+ { "axfrport", AXFRPORT, 0},
+ { "axfr-for", AXFRFOR, STATE_IP },
+ { "whitelist", WHITELIST, STATE_IP },
+ { "filter", FILTER, STATE_IP },
+ { "include", INCLUDE, 0 },
+ { "logging", LOGGING, 0 },
+ { "options", OPTIONS, 0 },
+ { "recurse-for", RECURSEFOR, STATE_IP },
+ { "region", REGION, STATE_IP },
+ { "wildcard-only-for", WOF, STATE_IP },
+ { "version", VERSION, 0 },
+ { "zone", ZONE, 0 },
+ { "notify", NOTIFY, STATE_IP },
+ { NULL, 0, 0}};
+
+
+
+void
+yyerror(const char *str)
+{
+ dolog(LOG_ERR, "%s file: %s line: %d\n", str, file->name, file->lineno);
+ slave_shutdown();
+ exit (1);
+}
+
+int
+yywrap()
+{
+ return 1;
+}
+
+int
+parse_file(DB *db, char *filename)
+{
+ int errors;
+
+ mydb = db;
+
+ memset(&logging, 0, sizeof(struct logging));
+ logging.active = 0;
+
+
+ if ((file = pushfile(filename, 0)) == NULL) {
+ return (-1);
+ }
+
+ topfile = file;
+
+ if (yyparse() < 0) {
+ dolog(LOG_ERR, "error: %s line: %d\n", file->name, file->lineno);
+ return (-1);
+ }
+ errors = file->errors;
+ popfile();
+
+ dolog(LOG_INFO, "configuration file read\n");
+
+ return 0;
+}
+
+int
+yylex()
+{
+ struct tab *p;
+ static char buf[512];
+ static char dst[INET6_ADDRSTRLEN];
+ char *cp = NULL;
+ int c, cpos;
+ static int setupstate = 0;
+
+
+ do {
+ c = lgetc(0);
+ } while ((c == ' ') || (c == '\t'));
+
+ if (c == EOF)
+ return 0;
+
+ if (c == '\n') {
+ file->lineno++;
+
+ while ((c = lgetc(0)) != EOF && (c == '\n' || c == '\t'))
+ if (c == '\n')
+ file->lineno++;
+ lungetc(c);
+
+
+#ifdef LEXDEBUG
+ if (debug)
+ printf("returning %s\n", "crlf");
+#endif
+
+ return CRLF;
+ }
+
+ switch (state) {
+ case STATE_IP:
+ if (c == ':' || isalnum(c)) {
+ lungetc(c);
+ get_ip(buf, sizeof(buf) - 1);
+
+ yylval.v.string = strdup(buf);
+ if (yylval.v.string == NULL) {
+ dolog(LOG_ERR, "yylex: %s\n", strerror(errno));
+ slave_shutdown();
+ exit(1);
+ }
+#ifdef LEXDEBUG
+ if (debug)
+ printf("returning %s\n", "IP");
+#endif
+ return (IP);
+ }
+ /* FALLTHROUGH */
+ default:
+ if (c == '}') {
+#ifdef LEXDEBUG
+ if (debug)
+ printf("returning %s\n", "ebrace");
+#endif
+ setupstate = 0;
+ state = 0;
+ return EBRACE;
+ }
+
+ if (c == '{') {
+ if (setupstate)
+ state = setupstate;
+#ifdef LEXDEBUG
+ if (debug)
+ printf("returning %s\n", "obrace");
+#endif
+ return OBRACE;
+ }
+
+ if (c == '/') {
+#ifdef LEXDEBUG
+ if (debug)
+ printf("returning %s\n", "slash");
+#endif
+ return SLASH;
+ }
+
+ if (c == ',') {
+#ifdef LEXDEBUG
+ if (debug)
+ printf("returning %s\n", "comma");
+#endif
+ return COMMA;
+ }
+
+
+ if (c == ';') {
+ while ((c = lgetc(0)) != EOF && c != '\n');
+ lungetc(c);
+#ifdef LEXDEBUG
+ if (debug)
+ printf("returning %s\n", "semicolon");
+#endif
+ return SEMICOLON;
+ }
+
+ if (c == '#') {
+ while ((c = lgetc(0)) != EOF && c != '\n');
+ lungetc(c);
+#ifdef LEXDEBUG
+ if (debug)
+ printf("returning %s\n", "pound");
+#endif
+ return POUND;
+ }
+
+ if (c == '"') {
+ get_quotedstring(buf, sizeof(buf) - 1);
+
+ if ((cp = strrchr(buf, '"'))) {
+ cpos = cp - buf;
+ c = buf[cpos];
+ buf[cpos] = '\0';
+ }
+
+ yylval.v.string = strdup(buf);
+ if (yylval.v.string == NULL) {
+ dolog(LOG_ERR, "yylex: %s\n", strerror(errno));
+ slave_shutdown();
+ exit(1);
+ }
+
+#ifdef LEXDEBUG
+ if (debug)
+ printf("returning %s\n", "quotedstring");
+#endif
+ return QUOTEDSTRING;
+ }
+
+ if (c == '*') {
+ yylval.v.string = strdup("*");
+ if (yylval.v.string == NULL) {
+ dolog(LOG_ERR, "yylex: %s\n", strerror(errno));
+ slave_shutdown();
+ exit(1);
+ }
+#ifdef LEXDEBUG
+ if (debug)
+ printf("returning %s\n", "string");
+#endif
+ return STRING;
+ }
+
+ if (isalnum(c) || c == '.' || c == ':' || c == '-' || c == '_') {
+ lungetc(c);
+ get_string(buf, sizeof(buf) - 1);
+
+ if ((cp = strpbrk(buf, " \n"))) {
+ cpos = cp - buf;
+ c = buf[cpos];
+ buf[cpos] = '\0';
+ }
+
+ p = lookup(cmdtab, buf);
+ if (p != NULL) {
+#ifdef LEXDEBUG
+ if (debug)
+ printf("returning %s\n", p->val);
+#endif
+ yylval.v.string = strdup(p->val);
+ if (yylval.v.string == NULL) {
+ dolog(LOG_ERR, "yylex: %s\n", strerror(errno));
+ slave_shutdown();
+ exit(1);
+ }
+ setupstate = p->state;
+ return (p->num);
+ }
+
+ yylval.v.string = strdup(buf);
+ if (yylval.v.string == NULL) {
+ dolog(LOG_ERR, "yylex: %s\n", strerror(errno));
+ slave_shutdown();
+ exit(1);
+ }
+
+
+ memset(&dst, 0, sizeof(dst));
+ if (strchr(buf, ':') != NULL) {
+ if (inet_net_pton(AF_INET6, buf, &dst, sizeof(dst)) == -1) {
+ if (errno == EAFNOSUPPORT &&
+ temp_inet_net_pton_ipv6(buf, &dst, sizeof(dst)) != -1)
+#if LEXDEBUG
+ if (debug)
+ printf("returning IPV6\n");
+#endif
+ return IPV6;
+ } else {
+
+#if LEXDEBUG
+ if (debug)
+ printf("returning IPV6\n");
+#endif
+ return IPV6;
+ }
+ }
+
+ memset(&dst, 0, sizeof(dst));
+ if (strchr(buf, '.') != NULL &&
+ inet_net_pton(AF_INET, buf, &dst, sizeof(dst)) != -1){
+#if LEXDEBUG
+ if (debug)
+ printf("returning %s\n", "IP");
+#endif
+ return IP;
+ }
+
+ for (cp = &buf[0]; *cp != '\0'; cp++) {
+ if ((! isdigit((int)*cp)) && (*cp != '.'))
+ break;
+ }
+
+ if (*cp != '\0' || (buf[0] == '.' && buf[1] == '\0')) {
+#ifdef LEXDEBUG
+ printf("returning %s (%s)\n", "STRING", buf);
+#endif
+ return (STRING);
+ }
+
+#ifdef LEXDEBUG
+ dolog(LOG_DEBUG, "returning %s\n", "NUMBER");
+#endif
+
+ free (yylval.v.string);
+ yylval.v.intval = atoi(buf);
+
+ return (NUMBER);
+ }
+
+ break;
+ }
+
+ return (c);
+}
+
+int
+get_quotedstring(char *buf, int n)
+{
+ int i, c;
+ int stack = 0;
+ char *cs;
+
+ cs = buf;
+
+ for (i = 0; --n > 0; ++i) {
+ c = lgetc(0);
+ if (c == '\n') {
+ *cs = '\0';
+ lungetc(c);
+ return (0);
+ } else if (c == '"') {
+ if (stack == 0) {
+ *cs++ = c;
+ *cs = '\0';
+ return (0);
+ } else {
+ stack--;
+ }
+ } else if (c == '\\') {
+ if (stack == 0) {
+ stack++;
+ continue;
+ } else {
+ stack--;
+ }
+ } else
+ stack = 0;
+
+
+ *cs++ = c;
+ }
+
+ return (1);
+}
+
+int
+get_string(char *buf, int n)
+{
+ int i, c;
+ char *cs;
+
+ cs = buf;
+
+ for (i = 0; --n > 0; ++i) {
+ c = lgetc(0);
+ if (c == '\n' || c == ' ' || c == ',' || c == ';' || ! (isprint(c) || c == '-' || c == '_')) {
+ *cs = '\0';
+ lungetc(c);
+ return (0);
+ }
+
+ *cs++ = c;
+ }
+
+ return (1);
+}
+
+struct rrtab *
+rrlookup(struct rrtab *p, char *keyword)
+{
+
+ for (; p->name != NULL; p++) {
+ if (strcasecmp(p->name, keyword) == 0)
+ return (p);
+ }
+
+ return (NULL);
+}
+
+struct tab *
+lookup(struct tab *cmdtab, char *keyword)
+{
+ struct tab *p;
+
+ for (p = cmdtab; p->val != NULL; p++) {
+ if (strcmp(p->val, keyword) == 0)
+ return (p);
+ }
+
+ return (NULL);
+}
+
+int
+get_ip(char *buf, int n)
+{
+ int i, c;
+ char *cs;
+
+ cs = buf;
+
+ for (i = 0; --n > 0; ++i) {
+ c = lgetc(0);
+ if (c == ',' || c == '\n' || ! (isalnum(c) || c == '/' || c == ':' || c == '.')) {
+ *cs = '\0';
+ lungetc(c);
+ return (0);
+ }
+
+ *cs++ = c;
+ }
+
+ return (1);
+}
+
+char *
+check_rr(char *domainname, char *mytype, int itype, int *converted_namelen)
+{
+ struct rrtab *rr;
+ char *converted_name, *p;
+ int i;
+
+
+ if ((rr = rrlookup(myrrtab, mytype)) == NULL) {
+ dolog(LOG_ERR, "error input line %d\n", file->lineno);
+ slave_shutdown();
+ exit(1);
+ }
+
+ if (rr->type != itype) {
+ dolog(LOG_ERR, "error input line %d, expected itype = %d, had %d\n", file->lineno, itype, rr->type);
+ return NULL;
+ }
+
+ if (strlen(domainname) > (DNS_MAXNAME - 2)) {
+ dolog(LOG_ERR, "domain name too long, line %d\n", file->lineno);
+ slave_shutdown();
+ exit(1);
+ }
+
+ for (i = 0, p = domainname; i < strlen(domainname); i++) {
+ *p = tolower((int)*p);
+ p++;
+ }
+
+ if ((strlen(domainname) == 1) && (domainname[0] == '.')) {
+ converted_name = malloc(1);
+ if (converted_name == NULL) {
+ dolog(LOG_ERR, "malloc failed\n");
+ slave_shutdown();
+ exit(1);
+ }
+
+ *converted_namelen = 1;
+ *converted_name = '\0';
+ } else if ((strlen(domainname) == 1) && (domainname[0] == '*')) {
+ converted_name = malloc(1);
+ if (converted_name == NULL) {
+ dolog(LOG_ERR, "malloc failed\n");
+ slave_shutdown();
+ exit(1);
+ }
+
+ *converted_namelen = 1;
+ *converted_name = '*';
+ } else {
+ converted_name = dns_label(domainname, converted_namelen);
+
+ if (converted_name == NULL) {
+ dolog(LOG_ERR, "error processing domain name line %d\n", file->lineno);
+ slave_shutdown();
+ exit(1);
+ }
+ }
+
+ return (converted_name);
+}
+
+int
+fill_cname(char *name, char *type, int myttl, char *hostname)
+{
+ struct domain sdomain;
+ char *myname, *converted_name;
+ int len, converted_namelen;
+ int i ;
+
+ for (i = 0; i < strlen(name); i++) {
+ name[i] = tolower((int)name[i]);
+ }
+
+ converted_name = check_rr(name, type, DNS_TYPE_CNAME, &converted_namelen);
+ if (converted_name == NULL) {
+ return -1;
+ }
+
+ memset(&sdomain, 0, sizeof(sdomain));
+ if (get_record(&sdomain, converted_name, converted_namelen) < 0) {
+ return (-1);
+ }
+
+#ifdef __linux__
+ strncpy((char *)sdomain.zonename, (char *)name, DNS_MAXNAME + 1);
+ sdomain.zonename[DNS_MAXNAME] = '\0';
+#else
+ strlcpy((char *)sdomain.zonename, (char *)name, DNS_MAXNAME + 1);
+#endif
+ memcpy(sdomain.zone, converted_name, converted_namelen);
+ sdomain.zonelen = converted_namelen;
+
+ sdomain.ttl = myttl;
+
+ myname = dns_label(hostname, (int *)&len);
+ if (myname == NULL) {
+ dolog(LOG_INFO, "illegal nameserver, skipping line %d\n", file->lineno);
+ return 0;
+ }
+
+ if (len > 0xff || len < 0) {
+ dolog(LOG_INFO, "illegal len value , line %d\n", file->lineno);
+ return -1;
+ }
+
+ sdomain.cnamelen = len;
+ memcpy((char *)&sdomain.cname[0], myname, len);
+
+ free(myname);
+
+ sdomain.flags |= DOMAIN_HAVE_CNAME;
+
+ set_record(&sdomain, converted_name, converted_namelen);
+
+ if (converted_name)
+ free (converted_name);
+
+ return (0);
+
+}
+
+int
+fill_ptr(char *name, char *type, int myttl, char *hostname)
+{
+ struct domain sdomain;
+ int len, converted_namelen;
+ char *myname, *converted_name;
+ int i;
+
+ for (i = 0; i < strlen(name); i++) {
+ name[i] = tolower((int)name[i]);
+ }
+
+ converted_name = check_rr(name, type, DNS_TYPE_PTR, &converted_namelen);
+ if (converted_name == NULL) {
+ return -1;
+ }
+
+ memset(&sdomain, 0, sizeof(sdomain));
+ if (get_record(&sdomain, converted_name, converted_namelen) < 0) {
+ return (-1);
+ }
+
+#ifdef __linux__
+ strncpy((char *)sdomain.zonename, (char *)name, DNS_MAXNAME + 1);
+ sdomain.zonename[DNS_MAXNAME] = '\0';
+#else
+ strlcpy((char *)sdomain.zonename, (char *)name, DNS_MAXNAME + 1);
+#endif
+ memcpy(sdomain.zone, converted_name, converted_namelen);
+ sdomain.zonelen = converted_namelen;
+
+ sdomain.ttl = myttl;
+
+ myname = dns_label(hostname, (int *)&len);
+ if (myname == NULL) {
+ dolog(LOG_INFO, "illegal nameserver, skipping line %d\n", file->lineno);
+ return 0;
+ }
+
+ if (len > 0xff || len < 0) {
+ dolog(LOG_INFO, "illegal len value , line %d\n", file->lineno);
+ return -1;
+ }
+
+ sdomain.ptrlen = len;
+ memcpy((char *)&sdomain.ptr[0], myname, len);
+
+ free(myname);
+
+ sdomain.flags |= DOMAIN_HAVE_PTR;
+
+ set_record(&sdomain, converted_name, converted_namelen);
+
+ if (converted_name)
+ free (converted_name);
+
+ return (0);
+
+}
+
+/* based on fill_txt */
+int
+fill_spf(char *name, char *type, int myttl, char *msg)
+{
+ struct domain sdomain;
+ int converted_namelen;
+ char *converted_name;
+ int len, i;
+
+ for (i = 0; i < strlen(name); i++) {
+ name[i] = tolower((int)name[i]);
+ }
+
+ if ((len = strlen(msg)) > 255) {
+ dolog(LOG_ERR, "SPF record too long line %d\n", file->lineno);
+ return (-1);
+ }
+
+ converted_name = check_rr(name, type, DNS_TYPE_SPF, &converted_namelen);
+ if (converted_name == NULL) {
+ return -1;
+ }
+
+ memset(&sdomain, 0, sizeof(sdomain));
+ if (get_record(&sdomain, converted_name, converted_namelen) < 0) {
+ return (-1);
+ }
+
+#ifdef __linux__
+ strncpy((char *)sdomain.zonename, (char *)name, DNS_MAXNAME + 1);
+ sdomain.zonename[DNS_MAXNAME] = '\0';
+#else
+ strlcpy((char *)sdomain.zonename, (char *)name, DNS_MAXNAME + 1);
+#endif
+ memcpy(sdomain.zone, converted_name, converted_namelen);
+ sdomain.zonelen = converted_namelen;
+
+ sdomain.ttl = myttl;
+
+ memcpy(&sdomain.spf, msg, len);
+ sdomain.spflen = len;
+
+ sdomain.flags |= DOMAIN_HAVE_SPF;
+
+ set_record(&sdomain, converted_name, converted_namelen);
+
+ if (converted_name)
+ free (converted_name);
+
+ return (0);
+
+}
+
+
+int
+fill_naptr(char *name, char *type, int myttl, int order, int preference, char *flags, char *services, char *regexp, char *replacement)
+{
+ struct domain sdomain;
+ int converted_namelen;
+ char *converted_name, *naptrname;
+ int flagslen, serviceslen, regexplen, replacementlen;
+ int i, naptr_namelen;
+
+ for (i = 0; i < strlen(name); i++) {
+ name[i] = tolower((int)name[i]);
+ }
+
+ if ((flagslen = strlen(flags)) > 255 ||
+ (serviceslen = strlen(services)) > 255 ||
+ (regexplen = strlen(regexp)) > 255 ||
+ (replacementlen = strlen(replacement)) > 255) {
+
+ dolog(LOG_ERR, "NAPTR record too long line %d\n", file->lineno);
+ return (-1);
+ }
+
+ converted_name = check_rr(name, type, DNS_TYPE_NAPTR, &converted_namelen);
+ if (converted_name == NULL) {
+ return -1;
+ }
+
+ memset(&sdomain, 0, sizeof(sdomain));
+ if (get_record(&sdomain, converted_name, converted_namelen) < 0) {
+ return (-1);
+ }
+
+#ifdef __linux__
+ strncpy((char *)sdomain.zonename, (char *)name, DNS_MAXNAME + 1);
+ sdomain.zonename[DNS_MAXNAME] = '\0';
+#else
+ strlcpy((char *)sdomain.zonename, (char *)name, DNS_MAXNAME + 1);
+#endif
+ memcpy(sdomain.zone, converted_name, converted_namelen);
+ sdomain.zonelen = converted_namelen;
+
+ sdomain.ttl = myttl;
+
+ sdomain.naptr[sdomain.naptr_count].order = order;
+ sdomain.naptr[sdomain.naptr_count].preference = preference;
+
+ memcpy(&sdomain.naptr[sdomain.naptr_count].flags, flags, flagslen);
+ sdomain.naptr[sdomain.naptr_count].flagslen = flagslen;
+
+ memcpy(&sdomain.naptr[sdomain.naptr_count].services, services, serviceslen);
+ sdomain.naptr[sdomain.naptr_count].serviceslen = serviceslen;
+
+ memcpy(&sdomain.naptr[sdomain.naptr_count].regexp, regexp, regexplen);
+ sdomain.naptr[sdomain.naptr_count].regexplen = regexplen;
+
+ naptrname = check_rr(replacement, type, DNS_TYPE_NAPTR, &naptr_namelen);
+ if (naptrname == NULL) {
+ return -1;
+ }
+
+ memcpy(&sdomain.naptr[sdomain.naptr_count].replacement, naptrname, naptr_namelen);
+ sdomain.naptr[sdomain.naptr_count].replacementlen = naptr_namelen;
+
+ sdomain.naptr_count++;
+
+ sdomain.flags |= DOMAIN_HAVE_NAPTR;
+
+ set_record(&sdomain, converted_name, converted_namelen);
+
+ if (naptrname)
+ free (naptrname);
+
+ if (converted_name)
+ free (converted_name);
+
+ return (0);
+
+}
+
+int
+fill_txt(char *name, char *type, int myttl, char *msg)
+{
+ struct domain sdomain;
+ int converted_namelen;
+ char *converted_name;
+ int len, i;
+
+ for (i = 0; i < strlen(name); i++) {
+ name[i] = tolower((int)name[i]);
+ }
+
+ if ((len = strlen(msg)) > 255) {
+ dolog(LOG_ERR, "TXT record too long line %d\n", file->lineno);
+ return (-1);
+ }
+
+ converted_name = check_rr(name, type, DNS_TYPE_TXT, &converted_namelen);
+ if (converted_name == NULL) {
+ return -1;
+ }
+
+ memset(&sdomain, 0, sizeof(sdomain));
+ if (get_record(&sdomain, converted_name, converted_namelen) < 0) {
+ return (-1);
+ }
+
+#ifdef __linux__
+ strncpy((char *)sdomain.zonename, (char *)name, DNS_MAXNAME + 1);
+ sdomain.zonename[DNS_MAXNAME] = '\0';
+#else
+ strlcpy((char *)sdomain.zonename, (char *)name, DNS_MAXNAME + 1);
+#endif
+ memcpy(sdomain.zone, converted_name, converted_namelen);
+ sdomain.zonelen = converted_namelen;
+
+ sdomain.ttl = myttl;
+
+ memcpy(&sdomain.txt, msg, len);
+ sdomain.txtlen = len;
+
+ sdomain.flags |= DOMAIN_HAVE_TXT;
+
+ set_record(&sdomain, converted_name, converted_namelen);
+
+ if (converted_name)
+ free (converted_name);
+
+ return (0);
+
+}
+
+/* based on fill_srv */
+int
+fill_sshfp(char *name, char *type, int myttl, int alg, int fptype, char *fingerprint)
+{
+ struct domain sdomain;
+ int converted_namelen;
+ char *converted_name;
+ char *p, *ep, save;
+ int len, i;
+
+ for (i = 0; i < strlen(name); i++) {
+ name[i] = tolower((int)name[i]);
+ }
+
+ converted_name = check_rr(name, type, DNS_TYPE_SSHFP, &converted_namelen);
+ if (converted_name == NULL) {
+ return -1;
+ }
+
+ memset(&sdomain, 0, sizeof(sdomain));
+ if (get_record(&sdomain, converted_name, converted_namelen) < 0) {
+ return (-1);
+ }
+
+#ifdef __linux__
+ strncpy((char *)sdomain.zonename, (char *)name, DNS_MAXNAME + 1);
+ sdomain.zonename[DNS_MAXNAME] = '\0';
+#else
+ strlcpy((char *)sdomain.zonename, (char *)name, DNS_MAXNAME + 1);
+#endif
+ memcpy(sdomain.zone, converted_name, converted_namelen);
+ sdomain.zonelen = converted_namelen;
+
+ if (sdomain.sshfp_count >= RECORD_COUNT) {
+ dolog(LOG_INFO, "%s: too many SSHFP records for zone \"%s\", skipping line %d\n", file->name, name, file->lineno);
+ return (-1);
+ }
+
+ sdomain.ttl = myttl;
+
+ sdomain.sshfp[sdomain.sshfp_count].algorithm = alg;
+ sdomain.sshfp[sdomain.sshfp_count].fptype = fptype;
+
+ switch (fptype) {
+ case 1:
+ len = sdomain.sshfp[sdomain.sshfp_count].fplen = DNS_SSHFP_SIZE_SHA1;
+ break;
+ case 2:
+ len = sdomain.sshfp[sdomain.sshfp_count].fplen = DNS_SSHFP_SIZE_SHA256;
+ break;
+ default:
+ dolog(LOG_ERR, "sshfp: unknown fingerprint type!\n");
+ return -1;
+ }
+
+ p = fingerprint;
+ for (i = 0; i < len; i++) {
+ save = p[2];
+ p[2] = '\0';
+ sdomain.sshfp[sdomain.sshfp_count].fingerprint[i] = strtol(p, &ep, 16);
+ p[2] = save;
+ p += 2;
+ }
+
+
+ sdomain.sshfp_count++;
+
+ sdomain.flags |= DOMAIN_HAVE_SSHFP;
+
+ set_record(&sdomain, converted_name, converted_namelen);
+
+ if (converted_name)
+ free (converted_name);
+
+ return (0);
+
+}
+
+int
+fill_srv(char *name, char *type, int myttl, int priority, int weight, int port, char *srvhost)
+{
+ struct domain sdomain;
+ int converted_namelen;
+ char *converted_name;
+ char *srvname;
+ int len, i;
+
+ for (i = 0; i < strlen(name); i++) {
+ name[i] = tolower((int)name[i]);
+ }
+
+ converted_name = check_rr(name, type, DNS_TYPE_SRV, &converted_namelen);
+ if (converted_name == NULL) {
+ return -1;
+ }
+
+ memset(&sdomain, 0, sizeof(sdomain));
+ if (get_record(&sdomain, converted_name, converted_namelen) < 0) {
+ return (-1);
+ }
+
+#ifdef __linux__
+ strncpy((char *)sdomain.zonename, (char *)name, DNS_MAXNAME + 1);
+ sdomain.zonename[DNS_MAXNAME] = '\0';
+#else
+ strlcpy((char *)sdomain.zonename, (char *)name, DNS_MAXNAME + 1);
+#endif
+ memcpy(sdomain.zone, converted_name, converted_namelen);
+ sdomain.zonelen = converted_namelen;
+
+ if (sdomain.srv_count >= RECORD_COUNT) {
+ dolog(LOG_INFO, "%s: too many SRV records for zone \"%s\", skipping line %d\n", file->name, name, file->lineno);
+ return (-1);
+ }
+
+ sdomain.ttl = myttl;
+
+ sdomain.srv[sdomain.srv_count].priority = priority;
+ sdomain.srv[sdomain.srv_count].weight = weight;
+ sdomain.srv[sdomain.srv_count].port = port;
+
+ srvname = dns_label(srvhost, &len);
+ if (srvname == NULL) {
+ dolog(LOG_INFO, "illegal srv server, skipping line %d\n", file->lineno);
+ return (-1);
+ }
+
+
+ sdomain.srv[sdomain.srv_count].targetlen = len;
+ memcpy((char *)&sdomain.srv[sdomain.srv_count].target, srvname, len);
+
+ /* bad hack workaround !!! */
+ if (strcmp(srvhost, ".") == 0 && len > 1)
+ sdomain.srv[sdomain.srv_count].targetlen = 1;
+
+ free (srvname);
+
+ sdomain.srv_count++;
+
+ sdomain.flags |= DOMAIN_HAVE_SRV;
+
+ set_record(&sdomain, converted_name, converted_namelen);
+
+ if (converted_name)
+ free (converted_name);
+
+ return (0);
+
+}
+
+int
+fill_mx(char *name, char *type, int myttl, int priority, char *mxhost)
+{
+ struct domain sdomain;
+ int converted_namelen;
+ char *converted_name;
+ char *mxname;
+ int len, i;
+
+ for (i = 0; i < strlen(name); i++) {
+ name[i] = tolower((int)name[i]);
+ }
+
+ converted_name = check_rr(name, type, DNS_TYPE_MX, &converted_namelen);
+ if (converted_name == NULL) {
+ return -1;
+ }
+
+ memset(&sdomain, 0, sizeof(sdomain));
+ if (get_record(&sdomain, converted_name, converted_namelen) < 0) {
+ return (-1);
+ }
+
+#ifdef __linux__
+ strncpy((char *)sdomain.zonename, (char *)name, DNS_MAXNAME + 1);
+ sdomain.zonename[DNS_MAXNAME] = '\0';
+#else
+ strlcpy((char *)sdomain.zonename, (char *)name, DNS_MAXNAME + 1);
+#endif
+ memcpy(sdomain.zone, converted_name, converted_namelen);
+ sdomain.zonelen = converted_namelen;
+
+ if (sdomain.mx_count >= RECORD_COUNT) {
+ dolog(LOG_INFO, "%s: too many MX records for zone \"%s\", skipping line %d\n", file->name, name, file->lineno);
+ return (-1);
+ }
+
+ sdomain.ttl = myttl;
+ sdomain.mx[sdomain.mx_count].preference = priority;
+
+ mxname = dns_label(mxhost, &len);
+ if (mxname == NULL) {
+ dolog(LOG_INFO, "illegal mx server, skipping line %d\n", file->lineno);
+ return (-1);
+ }
+
+ sdomain.mx[sdomain.mx_count].exchangelen = len;
+ memcpy((char *)&sdomain.mx[sdomain.mx_count].exchange, mxname, len);
+ free (mxname);
+
+ sdomain.mx_count++;
+
+ sdomain.flags |= DOMAIN_HAVE_MX;
+
+ set_record(&sdomain, converted_name, converted_namelen);
+
+ if (converted_name)
+ free (converted_name);
+
+ return (0);
+
+}
+
+int
+fill_balance(char *name, char *type, int myttl, char *a)
+{
+ struct domain sdomain;
+ int converted_namelen;
+ char *converted_name;
+ struct sockaddr_in sin;
+ in_addr_t *ia;
+ int i;
+
+ for (i = 0; i < strlen(name); i++) {
+ name[i] = tolower((int)name[i]);
+ }
+
+ converted_name = check_rr(name, type, DNS_TYPE_BALANCE, &converted_namelen);
+ if (converted_name == NULL) {
+ return -1;
+ }
+
+ memset(&sdomain, 0, sizeof(sdomain));
+ if (get_record(&sdomain, converted_name, converted_namelen) < 0) {
+ return (-1);
+ }
+
+#ifdef __linux__
+ strncpy((char *)sdomain.zonename, (char *)name, DNS_MAXNAME + 1);
+ sdomain.zonename[DNS_MAXNAME] = '\0';
+#else
+ strlcpy((char *)sdomain.zonename, (char *)name, DNS_MAXNAME + 1);
+#endif
+ memcpy(sdomain.zone, converted_name, converted_namelen);
+ sdomain.zonelen = converted_namelen;
+
+ if (sdomain.a_count >= RECORD_COUNT) {
+ dolog(LOG_INFO, "%s: too many BALANCE records for zone \"%s\", skipping line %d\n", file->name, name, file->lineno);
+ return (-1);
+ }
+
+ sdomain.ttl = myttl;
+ ia = (in_addr_t *)&sdomain.a[sdomain.a_count];
+
+ if ((*ia = inet_addr(a)) == INADDR_ANY) {
+ dolog(LOG_INFO, "could not parse BALANCE record on line %d\n", file->lineno);
+ return (-1);
+ }
+
+
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_addr.s_addr = *ia;
+ sin.sin_family = AF_INET;
+ sdomain.region[sdomain.a_count] = find_region((struct sockaddr_storage *)&sin, AF_INET);
+
+ sdomain.a_count++;
+ sdomain.a_ptr = 0;
+
+ sdomain.flags |= DOMAIN_HAVE_A;
+
+ set_record(&sdomain, converted_name, converted_namelen);
+
+ if (converted_name)
+ free (converted_name);
+
+ return (0);
+
+}
+
+int
+fill_a(char *name, char *type, int myttl, char *a)
+{
+ struct domain sdomain;
+ int converted_namelen;
+ char *converted_name;
+ in_addr_t *ia;
+ int i;
+
+ for (i = 0; i < strlen(name); i++) {
+ name[i] = tolower((int)name[i]);
+ }
+
+ converted_name = check_rr(name, type, DNS_TYPE_A, &converted_namelen);
+ if (converted_name == NULL) {
+ return -1;
+ }
+
+ memset(&sdomain, 0, sizeof(sdomain));
+ if (get_record(&sdomain, converted_name, converted_namelen) < 0) {
+ return (-1);
+ }
+
+#ifdef __linux__
+ strncpy((char *)sdomain.zonename, (char *)name, DNS_MAXNAME + 1);
+ sdomain.zonename[DNS_MAXNAME] = '\0';
+#else
+ strlcpy((char *)sdomain.zonename, (char *)name, DNS_MAXNAME + 1);
+#endif
+ memcpy(sdomain.zone, converted_name, converted_namelen);
+ sdomain.zonelen = converted_namelen;
+
+ if (sdomain.a_count >= RECORD_COUNT) {
+ dolog(LOG_INFO, "%s: too many A records for zone \"%s\", skipping line %d\n", file->name, name, file->lineno);
+ return (-1);
+ }
+
+ sdomain.ttl = myttl;
+ ia = (in_addr_t *)&sdomain.a[sdomain.a_count];
+
+ if ((*ia = inet_addr(a)) == INADDR_ANY) {
+ dolog(LOG_INFO, "could not parse A record on line %d\n", file->lineno);
+ return (-1);
+ }
+
+ sdomain.region[sdomain.a_count] = 0xff;
+
+ sdomain.a_count++;
+ sdomain.a_ptr = 0;
+
+ sdomain.flags |= DOMAIN_HAVE_A;
+
+ set_record(&sdomain, converted_name, converted_namelen);
+
+ if (converted_name)
+ free (converted_name);
+
+ return (0);
+
+}
+
+
+int
+fill_aaaa(char *name, char *type, int myttl, char *aaaa)
+{
+ struct domain sdomain;
+ int converted_namelen;
+ char *converted_name;
+ struct in6_addr *ia6;
+ int i;
+
+
+ for (i = 0; i < strlen(name); i++) {
+ name[i] = tolower((int)name[i]);
+ }
+
+ converted_name = check_rr(name, type, DNS_TYPE_AAAA, &converted_namelen);
+ if (converted_name == NULL) {
+ return -1;
+ }
+
+ memset(&sdomain, 0, sizeof(sdomain));
+ if (get_record(&sdomain, converted_name, converted_namelen) < 0) {
+ return (-1);
+ }
+
+#ifdef __linux__
+ strncpy((char *)sdomain.zonename, (char *)name, DNS_MAXNAME + 1);
+ sdomain.zonename[DNS_MAXNAME] = '\0';
+#else
+ strlcpy((char *)sdomain.zonename, (char *)name, DNS_MAXNAME + 1);
+#endif
+ memcpy(sdomain.zone, converted_name, converted_namelen);
+ sdomain.zonelen = converted_namelen;
+
+ if (sdomain.aaaa_count >= RECORD_COUNT) {
+ dolog(LOG_INFO, "%s: too many AAAA records for zone \"%s\", skipping line %d\n", file->name, name, file->lineno);
+ return (-1);
+ }
+
+ sdomain.ttl = myttl;
+ ia6 = (struct in6_addr *)&sdomain.aaaa[sdomain.aaaa_count];
+ if (inet_pton(AF_INET6, (char *)aaaa, (char *)ia6) != 1) {
+ dolog(LOG_INFO, "AAAA \"%s\" unparseable line %d\n", aaaa, file->lineno);
+ return -1;
+ }
+
+ sdomain.aaaa_count++;
+ sdomain.aaaa_ptr = 0;
+
+ sdomain.flags |= DOMAIN_HAVE_AAAA;
+
+ set_record(&sdomain, converted_name, converted_namelen);
+
+ if (converted_name)
+ free (converted_name);
+
+ return (0);
+
+}
+
+
+int
+fill_ns(char *name, char *type, int myttl, char *nameserver)
+{
+ struct domain sdomain;
+ int len, converted_namelen;
+ char *myname, *converted_name;
+ char *n;
+ int nstype, i;
+
+
+ for (i = 0; i < strlen(name); i++) {
+ name[i] = tolower((int)name[i]);
+ }
+
+ if (strcasecmp(type, "ns") == 0) {
+ converted_name = check_rr(name, type, DNS_TYPE_NS, &converted_namelen);
+ nstype = 0;
+ } else if (strcasecmp(type, "hint") == 0) {
+ converted_name = check_rr(name, type, DNS_TYPE_HINT, &converted_namelen);
+ nstype = NS_TYPE_HINT;
+ } else {
+ converted_name = check_rr(name, type, DNS_TYPE_DELEGATE, &converted_namelen);
+ nstype = NS_TYPE_DELEGATE;
+ }
+
+ if (converted_name == NULL) {
+ return -1;
+ }
+
+ memset(&sdomain, 0, sizeof(sdomain));
+ if (get_record(&sdomain, converted_name, converted_namelen) < 0) {
+ return (-1);
+ }
+
+#ifdef __linux__
+ strncpy((char *)sdomain.zonename, (char *)name, DNS_MAXNAME + 1);
+ sdomain.zonename[DNS_MAXNAME] = '\0';
+#else
+ strlcpy((char *)sdomain.zonename, (char *)name, DNS_MAXNAME + 1);
+#endif
+ memcpy(sdomain.zone, converted_name, converted_namelen);
+ sdomain.zonelen = converted_namelen;
+
+ if (sdomain.ns_count >= RECORD_COUNT) {
+ dolog(LOG_INFO, "%s: too many NS records for zone \"%s\", skipping line %d\n", file->name, name, file->lineno);
+ return (-1);
+ }
+
+ sdomain.ttl = myttl;
+
+ myname = dns_label(nameserver, (int *)&len);
+ if (myname == NULL) {
+ dolog(LOG_INFO, "illegal nameserver, skipping line %d\n", file->lineno);
+ return 0;
+ }
+
+ if (len > 0xff || len < 0) {
+ dolog(LOG_INFO, "illegal len value , line %d\n", file->lineno);
+ return -1;
+ }
+
+ n = (char *)sdomain.ns[sdomain.ns_count].nsserver;
+ sdomain.ns[sdomain.ns_count].nslen = len;
+ memcpy((char *)n, myname, sdomain.ns[sdomain.ns_count].nslen);
+
+ free(myname);
+
+ sdomain.ns_count++;
+ sdomain.ns_ptr = 0;
+ sdomain.ns_type = nstype;
+
+ sdomain.flags |= DOMAIN_HAVE_NS;
+
+ set_record(&sdomain, converted_name, converted_namelen);
+
+ if (converted_name)
+ free (converted_name);
+
+ return (0);
+
+}
+
+
+/* centroid.eu,soa,3600,uranus.centroid.eu.,pjp.solarscale.de.,1258740680,3600,1800,7200,3600 */
+int
+fill_soa(char *name, char *type, int myttl, char *auth, char *contact, int serial, int retry, int refresh, int expire, int ttl)
+{
+ struct domain sdomain;
+ int len, converted_namelen;
+ char *myname, *converted_name;
+ int i;
+
+ for (i = 0; i < strlen(name); i++) {
+ name[i] = tolower((int)name[i]);
+ }
+
+ converted_name = check_rr(name, type, DNS_TYPE_SOA, &converted_namelen);
+ if (converted_name == NULL) {
+ dolog(LOG_ERR, "error input line %d\n", file->lineno);
+ return (-1);
+ }
+
+ memset(&sdomain, 0, sizeof(sdomain));
+ if (get_record(&sdomain, converted_name, converted_namelen) < 0) {
+ return (-1);
+ }
+
+#ifdef __linux__
+ strncpy((char *)sdomain.zonename, (char *)name, DNS_MAXNAME + 1);
+ sdomain.zonename[DNS_MAXNAME] = '\0';
+#else
+ strlcpy((char *)sdomain.zonename, (char *)name, DNS_MAXNAME + 1);
+#endif
+ memcpy(sdomain.zone, converted_name, converted_namelen);
+ sdomain.zonelen = converted_namelen;
+
+ sdomain.ttl = myttl;
+
+ myname = dns_label(auth, (int *)&len);
+ if (myname == NULL) {
+ dolog(LOG_INFO, "illegal nameserver, skipping line %d\n", file->lineno);
+ return 0;
+ }
+
+ if (len > 0xff || len < 0) {
+ dolog(LOG_INFO, "illegal len value , line %d\n", file->lineno);
+ return -1;
+ }
+
+ sdomain.soa.nsserver_len = len;
+ memcpy((char *)&sdomain.soa.nsserver[0], myname, len);
+
+ free(myname);
+
+ myname = dns_label(contact, (int *)&len);
+ if (myname == NULL) {
+ dolog(LOG_INFO, "illegal nameserver, skipping line %d\n", file->lineno);
+ return 0;
+ }
+
+ if (len > 0xff || len < 0) {
+ dolog(LOG_INFO, "illegal len value , line %d\n", file->lineno);
+ return -1;
+ }
+
+ sdomain.soa.rp_len = len;
+ memcpy((char *)&sdomain.soa.responsible_person[0], myname, len);
+
+ free (myname);
+
+ sdomain.soa.serial = serial;
+ sdomain.soa.refresh = refresh;
+ sdomain.soa.retry = retry;
+ sdomain.soa.expire = expire;
+ sdomain.soa.minttl = ttl;
+
+ sdomain.flags |= DOMAIN_HAVE_SOA;
+
+ set_record(&sdomain, converted_name, converted_namelen);
+
+ if (converted_name)
+ free (converted_name);
+
+ return (0);
+
+}
+
+
+int
+get_record(struct domain *sdomain, char *converted_name, int converted_namelen)
+{
+ DB *db = mydb; /* XXX */
+
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+
+ key.data = (char *)converted_name;
+ key.size = converted_namelen;
+
+ data.data = NULL;
+ data.size = 0;
+
+ if (db->get(db, NULL, &key, &data, 0) == 0) {
+
+ if (data.size != sizeof(struct domain)) {
+ dolog(LOG_INFO, "damaged btree database\n");
+ return -1;
+ }
+
+ memcpy((char *)sdomain, (char *)data.data, data.size);
+ } else {
+ if (debug)
+ dolog(LOG_INFO, "db->get: %s\n", strerror(errno));
+ }
+
+ return 0;
+}
+
+
+void
+set_record(struct domain *sdomain, char *converted_name, int converted_namelen)
+{
+ DB *db = mydb; /* XXX */
+ int ret;
+
+ /* everythign in parse.y should get this flag! */
+ sdomain->flags |= DOMAIN_STATIC_ZONE;
+
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+
+ key.data = (char *)converted_name;
+ key.size = converted_namelen;
+
+ data.data = (void*)sdomain;
+ data.size = sizeof(struct domain);
+
+ if ((ret = db->put(db, NULL, &key, &data, 0)) != 0) {
+ dolog(LOG_INFO, "db->put: %s\n" , db_strerror(ret));
+ return;
+ }
+
+ return;
+}
+
+
+struct file *
+pushfile(const char *name, int secret)
+{
+ struct stat sb;
+ struct file *nfile;
+ int fd;
+
+ if ((nfile = calloc(1, sizeof(struct file))) == NULL) {
+ dolog(LOG_INFO, "warn: malloc\n");
+ return (NULL);
+ }
+ if ((nfile->name = strdup(name)) == NULL) {
+ dolog(LOG_INFO, "warn: malloc\n");
+ free(nfile);
+ return (NULL);
+ }
+ if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
+ dolog(LOG_INFO, "warn: %s\n", nfile->name);
+ free(nfile->name);
+ free(nfile);
+ return (NULL);
+ }
+
+ fd = fileno(nfile->stream);
+ if (fstat(fd, &sb) < 0) {
+ dolog(LOG_INFO, "warn: %s\n", strerror(errno));
+ }
+
+ /* get the highest time of all included files */
+ if (time_changed < sb.st_ctime)
+ time_changed = (time_t)sb.st_ctime; /* ufs1 is only 32 bits */
+
+ nfile->lineno = 1;
+ TAILQ_INSERT_TAIL(&files, nfile, entry);
+ return (nfile);
+}
+
+#define MAXPUSHBACK 128
+
+char *parsebuf;
+int parseindex;
+char pushback_buffer[MAXPUSHBACK];
+int pushback_index = 0;
+
+
+int
+lgetc(int quotec)
+{
+ int c;
+
+ if (parsebuf) {
+ /* Read character from the parsebuffer instead of input. */
+ if (parseindex >= 0) {
+ c = parsebuf[parseindex++];
+ if (c != '\0')
+ return (c);
+ parsebuf = NULL;
+ } else
+ parseindex++;
+ }
+
+ if (pushback_index)
+ return (pushback_buffer[--pushback_index]);
+
+ if (quotec) {
+ if ((c = getc(file->stream)) == EOF) {
+ yyerror("reached end of file while parsing "
+ "quoted string");
+ if (file == topfile || popfile() == EOF)
+ return (EOF);
+ return (quotec);
+ }
+ return (c);
+ }
+
+ while ((c = getc(file->stream)) == EOF) {
+ if (file == topfile || popfile() == EOF)
+ return (EOF);
+ }
+ return (c);
+}
+
+int
+popfile(void)
+{
+ struct file *prev;
+
+ if ((prev = TAILQ_PREV(file, files, entry)) != NULL)
+ prev->errors += file->errors;
+
+ TAILQ_REMOVE(&files, file, entry);
+ fclose(file->stream);
+ free(file->name);
+ free(file);
+ file = prev;
+ return (file ? 0 : EOF);
+}
+
+
+int
+lungetc(int c)
+{
+ if (c == EOF)
+ return (EOF);
+ if (parsebuf) {
+ parseindex--;
+ if (parseindex >= 0)
+ return (c);
+ }
+ if (pushback_index < MAXPUSHBACK-1)
+ return (pushback_buffer[pushback_index++] = c);
+ else
+ return (EOF);
+}
+
+int
+findeol(void)
+{
+ int c;
+
+ parsebuf = NULL;
+ pushback_index = 0;
+
+ /* skip to either EOF or the first real EOL */
+ while (1) {
+ c = lgetc(0);
+ if (c == '\n') {
+ file->lineno++;
+ break;
+ }
+ if (c == EOF)
+ break;
+ }
+ return (ERROR);
+}
+
+
+/*
+ * from opensmtpd, the license at the top is compatible with this stuff
+ */
+
+static int
+temp_inet_net_pton_ipv6(const char *src, void *dst, size_t size)
+{
+ int ret;
+ int bits;
+ char buf[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255:255:255:255/128")];
+ char *sep;
+#if defined __OpenBSD__ || defined __FreeBSD__
+ const char *errstr;
+#endif
+
+#ifndef __linux__
+ if (strlcpy(buf, src, sizeof buf) >= sizeof buf) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+#else
+ strncpy(buf, src, sizeof(buf));
+ buf[sizeof(buf) - 1] = '\0';
+#endif
+
+ sep = strchr(buf, '/');
+ if (sep != NULL)
+ *sep++ = '\0';
+
+ ret = inet_pton(AF_INET6, buf, dst);
+ if (ret != 1) {
+ return (-1);
+ }
+
+ if (sep == NULL)
+ return 128;
+
+#if ! defined __linux__ && ! defined __APPLE__ && ! defined __NetBSD__
+ bits = strtonum(sep, 0, 128, &errstr);
+ if (errstr)
+ return (-1);
+#else
+ bits = atoi(sep);
+#endif
+
+ return bits;
+}
+
+
+char *
+get_prefixlen(char *input, char *prefixlength, int plsize)
+{
+ int af = AF_INET;
+ int prefixlen;
+ char *ret, *p;
+
+ if (strchr(input, ':') != NULL)
+ af = AF_INET6;
+
+ prefixlen = inet_net_pton(af, input, prefixlength, plsize);
+ if (prefixlen < 0) {
+ if (errno == EAFNOSUPPORT) {
+ prefixlen = temp_inet_net_pton_ipv6(input, prefixlength, plsize);
+ } else {
+ if (debug)
+ printf("not address family %d (%s)\n", af, input);
+ return (NULL);
+ }
+ }
+
+ if ((p = strchr(input, '/')) != NULL) {
+ *p++ = '\0';
+ } else {
+ if (af == AF_INET)
+ p = "32";
+ else
+ p = "128";
+ }
+
+ snprintf(prefixlength, plsize, "%s", p);
+ ret = strdup(input);
+ return (ret);
+}
blob - /dev/null
blob + d9d9c2ea0de84858118c3783e8beef7e4bcfa6c6 (mode 644)
--- /dev/null
+++ ratelimit.c
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2014 Peter J. Philipp
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+#include "include.h"
+#include "dns.h"
+#include "db.h"
+
+void add_rrlimit(int, u_int16_t *, int, char *);
+int check_rrlimit(int, u_int16_t *, int, char *);
+extern void dolog(int, char *, ...);
+static u_int16_t hash_rrlimit(u_int16_t *, int);
+char *rrlimit_setup(int);
+
+struct rrlimit {
+ u_int8_t pointer;
+ time_t times[256];
+};
+
+int ratelimit = 0;
+int ratelimit_packets_per_second = 6;
+
+char *
+rrlimit_setup(int size)
+{
+ char *ptr;
+
+ if (size > 255)
+ return NULL;
+
+ size = 65536 * ((size * sizeof(time_t)) + sizeof(u_int8_t));
+
+ ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED |\
+ MAP_ANON, -1, 0);
+
+ if (ptr == MAP_FAILED) {
+ dolog(LOG_ERR, "failed to setup rlimit mmap segment, exit\n");
+ exit(1);
+ }
+
+ memset(ptr, 0, size);
+
+ return (ptr);
+}
+
+int
+check_rrlimit(int size, u_int16_t *ip, int sizeip, char *rrlimit_ptr)
+{
+ struct rrlimit *rl;
+ u_int16_t hash;
+ int count = 0, i;
+ u_int8_t offset;
+ time_t now;
+ char *tmp;
+
+ hash = hash_rrlimit(ip, sizeip);
+
+ tmp = rrlimit_ptr + (hash * ((size * sizeof(time_t)) + sizeof(u_int8_t)));
+ rl = (struct rrlimit *)tmp;
+
+ offset = rl->pointer;
+
+ now = time(NULL);
+
+ for (i = 0; i < size; i++) {
+ if (difftime(now, rl->times[(offset + i) % size]) <= 1)
+ count++;
+ else
+ break;
+ }
+
+ if (count > ratelimit_packets_per_second)
+ return 1;
+
+ return 0;
+}
+
+
+void
+add_rrlimit(int size, u_int16_t *ip, int sizeip, char *rrlimit_ptr)
+{
+ struct rrlimit *rl;
+ u_int16_t hash;
+ int offset;
+ time_t now;
+ char *tmp;
+
+ hash = hash_rrlimit(ip, sizeip);
+
+ tmp = rrlimit_ptr + (hash * ((size * sizeof(time_t)) + sizeof(u_int8_t)));
+ rl = (struct rrlimit *)tmp;
+
+ offset = rl->pointer;
+
+ offset--;
+ if (offset < 0)
+ offset = size - 1;
+
+ now = time(NULL);
+
+ rl->times[offset] = now;
+ rl->pointer = offset; /* XXX race */
+
+}
+
+static u_int16_t
+hash_rrlimit(u_int16_t *ip, int size)
+{
+ u_int64_t total = 0;
+ int i, j;
+
+ for (i = 0, j = 0; i < size; i += 2) {
+ total += (u_int64_t)ip[j++];
+ }
+
+ total %= 0xffff;
+
+ return ((u_int16_t)total);
+}
blob - /dev/null
blob + d609a491902fe5715181f1ee318c69d10a1cc198 (mode 644)
--- /dev/null
+++ recurse.c
@@ -0,0 +1,2216 @@
+/*
+ * Copyright (c) 2010-2014 Peter J. Philipp
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+#include "include.h"
+#include "dns.h"
+#include "db.h"
+
+extern struct question *build_fake_question(char *, int, u_int16_t);
+extern struct question *build_question(char *, int);
+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 *);
+extern void dolog(int, char *, ...);
+extern int free_question(struct question *);
+extern int get_soa(DB *, struct question *, struct domain *, int);
+extern in_addr_t getmask(int);
+extern int getmask6(int, struct sockaddr_in6 *);
+extern int lookup_zone(DB *, struct question *, struct domain *, int *, char *, int);
+extern int memcasecmp(u_char *, u_char *, int);
+extern void reply_a(struct sreply *, DB *);
+extern void reply_aaaa(struct sreply *, DB *);
+extern void reply_cname(struct sreply *);
+extern void reply_mx(struct sreply *, DB *);
+extern void reply_ns(struct sreply *, DB *);
+extern void reply_noerror(struct sreply *);
+extern void reply_nxdomain(struct sreply *);
+extern void reply_ptr(struct sreply *);
+extern void reply_soa(struct sreply *);
+extern void reply_txt(struct sreply *sreply);
+extern void slave_shutdown(void);
+extern void update_db(DB *, struct domain *);
+
+int contains(u_char *, u_char *);
+void init_recurse(void);
+int insert_recurse(char *, char *);
+int fakerecurse(DB *, struct recurses *, struct ns *, int);
+int find_recurse(struct sockaddr_storage *, int);
+int level(u_char *);
+int lookup_a(DB *, struct recurses *, struct ns *);
+int lookup_aaaa(DB *, struct recurses *, struct ns *);
+int lookup_ns(DB *, struct recurses *);
+int negative_cache(DB *, struct recurses *);
+int netlookup(DB *, struct recurses *);
+int netlookup6(DB *, struct recurses *);
+void recurseloop(int, int *, DB *);
+int recurse_parse(DB *, struct recurses *, u_char *, u_int16_t);
+void reply_raw(DB *, struct recurses *, struct domain *, int *);
+void reply_raw_cname(DB *, struct recurses *, struct domain *, int *);
+void reply_raw_noerror(DB *, struct recurses *, struct domain *, int *);
+void reply_raw_nxdomain(DB *, struct recurses *, struct domain *, int *);
+void remove_zone(DB *, struct domain *);
+
+extern int debug, verbose;
+
+#ifndef MIN
+#define MIN(a,b) ((a < b) ? a : b)
+#endif
+
+SLIST_HEAD(listhead, recurseentry) recursehead;
+
+static struct recurseentry {
+ char name[INET6_ADDRSTRLEN];
+ int family;
+ struct sockaddr_storage hostmask;
+ struct sockaddr_storage netmask;
+ u_int8_t prefixlen;
+ SLIST_ENTRY(recurseentry) recurse_entry;
+} *rn2, *rnp;
+
+
+static const char rcsid[] = "$Id: recurse.c,v 1.1.1.1 2014/11/14 08:09:04 pjp Exp $";
+
+/*
+ * INIT_RECURSE - initialize the recurse singly linked list
+ */
+
+void
+init_recurse(void)
+{
+ SLIST_INIT(&recursehead);
+ return;
+}
+
+/*
+ * INSERT_RECURSE - insert an address and prefixlen into the recurse slist
+ */
+
+int
+insert_recurse(char *address, char *prefixlen)
+{
+ struct sockaddr_in *sin;
+ struct sockaddr_in6 *sin6;
+ int pnum;
+ int ret;
+
+ pnum = atoi(prefixlen);
+ rn2 = malloc(sizeof(struct recurseentry)); /* Insert after. */
+
+ if (strchr(address, ':') != NULL) {
+ rn2->family = AF_INET6;
+ sin6 = (struct sockaddr_in6 *)&rn2->hostmask;
+ if ((ret = inet_pton(AF_INET6, address, &sin6->sin6_addr.s6_addr)) != 1)
+ return (-1);
+ sin6->sin6_family = AF_INET6;
+ sin6 = (struct sockaddr_in6 *)&rn2->netmask;
+ sin6->sin6_family = AF_INET6;
+ if (getmask6(pnum, sin6) < 0)
+ return(-1);
+ rn2->prefixlen = pnum;
+ } else {
+
+ rn2->family = AF_INET;
+ sin = (struct sockaddr_in *)&rn2->hostmask;
+ sin->sin_family = AF_INET;
+ sin->sin_addr.s_addr = inet_addr(address);
+ sin = (struct sockaddr_in *)&rn2->netmask;
+ sin->sin_family = AF_INET;
+ sin->sin_addr.s_addr = getmask(pnum);
+ rn2->prefixlen = pnum;
+
+ }
+
+ SLIST_INSERT_HEAD(&recursehead, rn2, recurse_entry);
+
+ return (0);
+}
+
+/*
+ * FIND_RECURSE - walk the recurse list and find the correponding network
+ * if a network matches return 1, if no match is found return
+ * 0.
+ */
+
+int
+find_recurse(struct sockaddr_storage *sst, int family)
+{
+ struct sockaddr_in *sin, *sin0;
+ struct sockaddr_in6 *sin6, *sin60, *sin61;
+ u_int32_t hostmask, netmask;
+ u_int32_t a;
+#ifdef __amd64
+ u_int64_t *hm[2], *nm[2], *a6[2];
+#else
+ u_int32_t *hm[4], *nm[4], *a6[4];
+#endif
+
+ SLIST_FOREACH(rnp, &recursehead, recurse_entry) {
+ if (rnp->family == AF_INET) {
+ if (family != AF_INET)
+ continue;
+ sin = (struct sockaddr_in *)sst;
+ a = sin->sin_addr.s_addr;
+ sin = (struct sockaddr_in *)&rnp->hostmask;
+ sin0 = (struct sockaddr_in *)&rnp->netmask;
+ hostmask = sin->sin_addr.s_addr;
+ netmask = sin0->sin_addr.s_addr;
+ if ((hostmask & netmask) == (a & netmask)) {
+ return (1);
+ } /* if hostmask */
+ } else if (rnp->family == AF_INET6) {
+ if (family != AF_INET6)
+ continue;
+ sin6 = (struct sockaddr_in6 *)sst;
+ sin60 = (struct sockaddr_in6 *)&rnp->hostmask;
+ sin61 = (struct sockaddr_in6 *)&rnp->netmask;
+#ifdef __amd64
+ /*
+ * If this is on a 64 bit machine, we'll benefit
+ * by using 64 bit registers, this should make it
+ * a tad faster...
+ */
+ hm[0] = (u_int64_t *)&sin60->sin6_addr.s6_addr;
+ hm[1] = (hm[0] + 1);
+ nm[0] = (u_int64_t *)&sin61->sin6_addr.s6_addr;
+ nm[1] = (nm[0] + 1);
+ a6[0] = (u_int64_t *)&sin6->sin6_addr.s6_addr;
+ a6[1] = (a6[0] + 1);
+ if ( ((*hm[0] & *nm[0]) == (*a6[0] & *nm[0]))&&
+ ((*hm[1] & *nm[1]) == (*a6[1] & *nm[1]))) {
+#else
+ hm[0] = (u_int32_t *)&sin60->sin6_addr.s6_addr;
+ hm[1] = (hm[0] + 1); hm[2] = (hm[1] + 1);
+ hm[3] = (hm[2] + 1);
+ nm[0] = (u_int32_t *)&sin61->sin6_addr.s6_addr;
+ nm[1] = (nm[0] + 1); nm[2] = (nm[1] + 1);
+ nm[3] = (nm[2] + 1);
+ a6[0] = (u_int32_t *)&sin6->sin6_addr.s6_addr;
+ a6[1] = (a6[0] + 1); a6[2] = (a6[1] + 1);
+ a6[3] = (a6[2] + 1);
+
+ if ( ((*hm[0] & *nm[0]) == (*a6[0] & *nm[0]))&&
+ ((*hm[1] & *nm[1]) == (*a6[1] & *nm[1]))&&
+ ((*hm[2] & *nm[2]) == (*a6[2] & *nm[2]))&&
+ ((*hm[3] & *nm[3]) == (*a6[3] & *nm[3]))) {
+#endif
+
+ return (1);
+ } /* if ip6 address */
+
+ } /* if AF_INET6 */
+ } /* SLIST */
+
+ return (0);
+}
+
+void
+recurseloop(int sp, int *raw, DB *db)
+{
+ int sel, ret;
+ int maxso, len;
+ socklen_t slen = sizeof(struct sockaddr_storage);
+ fd_set rset;
+ struct timeval tv;
+ struct srecurseheader rh;
+ struct domain sd;
+ struct dns_header *dh;
+ struct sockaddr_storage ssin;
+ struct sockaddr_in *sin;
+ struct sockaddr_in6 *sin6;
+
+ int type, lzerrno, wildcard = 0;
+
+ char fakereplystring[DNS_MAXNAME + 1];
+ char buf[2048];
+ char address[INET6_ADDRSTRLEN];
+
+ SLIST_INIT(&recurseshead);
+
+ for (;;) {
+ /*
+ * launch all fakesr requests
+ */
+ SLIST_FOREACH(sr1, &recurseshead, recurses_entry) {
+ if (sr1->isfake && !sr1->launched) {
+ dolog(LOG_DEBUG, "launching question (fakesr) for %s", sr1->question->hdr->name);
+ sr1->launched = 1;
+ type = lookup_zone(db, sr1->question, &sd, &lzerrno, (char *)fakereplystring, wildcard);
+ if (type < 0) {
+ netlookup(db, sr1);
+ } else {
+ SLIST_REMOVE(&recurseshead, sr1, recurses, recurses_entry);
+ sr1->callback->hascallback--;
+ free_question(sr1->question);
+ free(sr1);
+ }
+ }
+
+ /*
+ * while we're going through the list to look for
+ * fakesr launches we may as well expire recurses
+ * that have timed out (> 10 seconds)
+ */
+ if (difftime(time(NULL), sr1->received) >= 30) {
+ /* only remove if we don't have any callbacks
+ * outstanding...
+ */
+ if (! sr1->hascallback) {
+ dolog(LOG_DEBUG, "removing recurses struct");
+ SLIST_REMOVE(&recurseshead, sr1, recurses, recurses_entry);
+ if (sr1->so != -1) {
+ if (close(sr1->so) < 0)
+ dolog(LOG_ERR, "close: %m");
+ sr1->so = -1;
+ }
+
+ if (sr1->callback)
+ sr1->callback->hascallback--;
+
+ free_question(sr1->question);
+ free(sr1);
+ }
+ }
+ }
+ FD_ZERO(&rset);
+
+ maxso = sp;
+ FD_SET(sp, &rset);
+
+ /* XXX remember recurseshead is for struct recurses */
+ SLIST_FOREACH(sr1, &recurseshead, recurses_entry) {
+ if (sr1->so != -1) {
+ if (maxso < sr1->so)
+ maxso = sr1->so;
+
+ FD_SET(sr1->so, &rset);
+ }
+ }
+
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+ sel = select(maxso + 1, &rset, NULL, NULL, &tv);
+ if (sel < 0) {
+ dolog(LOG_INFO, "select: %m");
+ continue;
+ } else if (sel == 0) {
+ /* timeout */
+ continue;
+ }
+
+ if (FD_ISSET(sp, &rset)) {
+ ret = recv(sp, (char *)&rh, sizeof(rh), 0);
+ if (ret < 0) {
+ dolog(LOG_INFO, "recv: %m");
+ continue;
+ }
+
+ /* place request on struct recurses linked list */
+
+ sr = calloc(sizeof(struct recurses), 1);
+ if (sr == NULL) {
+ dolog(LOG_ERR, "calloc: %m");
+ continue;
+ }
+
+ memcpy(&sr->query, &rh.buf, 512);
+ sr->len = rh.len;
+ sr->af = rh.af;
+ sr->proto = rh.proto;
+ sr->so = -1;
+ sr->callback = NULL;
+ sr->hascallback = 0;
+ sr->isfake = 0;
+ sr->packetcount = 0;
+ sr->lookrecord = NULL;
+ memcpy(&sr->source, &rh.source, sizeof(struct sockaddr_storage));
+ memcpy(&sr->dest, &rh.dest, sizeof(struct sockaddr_storage));
+ sr->received = time(NULL);
+
+ sr->question = build_question(sr->query, sr->len);
+ if (sr->question == NULL) {
+ dolog(LOG_ERR, "malformed question in recurse.c");
+ free(sr);
+ continue;
+ }
+
+ type = lookup_zone(db, sr->question, &sd, &lzerrno, (char *)fakereplystring, wildcard);
+ if (type < 0) {
+ if (lzerrno == ERR_NOERROR &&
+ (sd.flags & DOMAIN_NEGATIVE_CACHE) ==
+ DOMAIN_NEGATIVE_CACHE) {
+
+ reply_raw_nxdomain(db, sr, &sd, raw);
+ free_question(sr->question);
+ free(sr);
+ continue;
+
+ }
+ if (netlookup(db, sr) < 0)
+ continue;
+
+ SLIST_INSERT_HEAD(&recurseshead, sr, recurses_entry);
+ } else {
+ dolog(LOG_DEBUG, "we had the record in our cache, reply action");
+ /* check if zone is expired */
+
+ if ((! (sd.flags & DOMAIN_STATIC_ZONE)) &&
+ (sd.created + sd.ttl < time(NULL))) {
+ remove_zone(db, &sd);
+
+ /* continue with netlookup */
+
+ if (netlookup(db, sr) < 0)
+ continue;
+
+ SLIST_INSERT_HEAD(&recurseshead, sr, recurses_entry);
+ continue;
+ }
+
+ if (type == DNS_TYPE_CNAME)
+ reply_raw_cname(db, sr, &sd, raw);
+ else
+ reply_raw(db, sr, &sd, raw);
+
+ free_question(sr->question);
+ free(sr);
+ continue;
+ }
+
+ } /* FD_ISSET(sp) */
+
+ SLIST_FOREACH(sr1, &recurseshead, recurses_entry) {
+ if (sr1->so != -1 && FD_ISSET(sr1->so, &rset)) {
+ /*
+ * we got a reply from the nameserver we
+ * queried, now we must parse the input
+ */
+
+ slen = sizeof(struct sockaddr_storage);
+ if ((len = recvfrom(sr1->so, buf, sizeof(buf), 0, (struct sockaddr *)&ssin, &slen)) < 0) {
+ if (errno != EWOULDBLOCK)
+ dolog(LOG_ERR, "recvfrom: %m");
+ continue;
+ }
+
+#if 1
+ /* XXX do some checking of expected IP address */
+
+ switch (ssin.ss_family) {
+ case AF_INET:
+ sin = (struct sockaddr_in *)&ssin;
+ if (sin->sin_addr.s_addr != sr1->a[0]) {
+ dolog(LOG_ERR, "return address is not from right nameserver");
+ continue;
+ }
+ break;
+ case AF_INET6:
+ sin6 = (struct sockaddr_in6*)&ssin;
+ if (memcmp((char *)&sin6->sin6_addr, (char *)&sr1->aaaa[0], sizeof(struct in6_addr)) != 0) {
+ inet_ntop(AF_INET6, &sin6->sin6_addr, address, sizeof(address));
+
+ dolog(LOG_ERR, "return IPv6 address (%s) is not from right nameserver", address);
+ continue;
+ }
+ break;
+ }
+#endif
+
+ if (len < sizeof(struct dns_header)) {
+ dolog(LOG_ERR, "size malformed on reply len=%d", len);
+ /* on error, we just go out and wait for the real ID, this sucks! XXX */
+ continue;
+ }
+
+ dh = (struct dns_header*)&buf[0];
+
+ if (ntohs(dh->id) != sr1->id) {
+ dolog(LOG_ERR, "unexpected dns ID (%u != %u)", ntohs(dh->id), sr1->id);
+ /* on error, we just go out and wait for the real ID, this sucks! XXX */
+ continue;
+ }
+
+ if (! (ntohs(dh->query) & DNS_REPLY)) {
+ dolog(LOG_ERR, "reply is not a DNS reply");
+ continue;
+ }
+
+ /* XXX */
+
+ if (close(sr1->so) < 0)
+ dolog(LOG_ERR, "close: %m");
+
+ sr1->so = -1;
+
+ if (ntohs(dh->query) & DNS_NAMEERR) {
+ negative_cache(db, sr1);
+ dolog(LOG_DEBUG, "added negative cache for domain \"%s\"", sr1->question->converted_name);
+ /* reply negatively */
+ reply_raw_nxdomain(db, sr1, &sd, raw);
+ goto remove;
+ }
+
+ sr1->authoritative = 0;
+ recurse_parse(db, sr1, (u_char*)&buf, len);
+
+ /* check if we're flooding anything */
+ if (sr1->packetcount > 50) {
+ dolog(LOG_ERR, "packetcount is over 50, I think I'm flooding something, abort()");
+ slave_shutdown();
+ abort();
+ }
+
+ type = lookup_zone(db, sr1->question, &sd, &lzerrno, (char *)fakereplystring, wildcard);
+ if (type < 0) {
+ dolog(LOG_DEBUG, "lookup_zone failed, doing netlookup");
+
+ if (sr1->authoritative == DNS_TYPE_NS &&
+ netlookup(db, sr1) < 0) {
+ dolog(LOG_DEBUG, "subsequent netlookup failed");
+
+ }
+
+ if (sr1->authoritative == DNS_TYPE_SOA) {
+ dolog(LOG_DEBUG, "got an authoritative SOA answer, we'd reply an SOA here");
+ memset(&sd, 0, sizeof(struct domain));
+ get_soa(db, sr1->question, &sd, wildcard);
+
+ reply_raw_noerror(db, sr, &sd, raw);
+ if (sr1->callback)
+ sr1->callback->hascallback--;
+ goto remove;
+ }
+
+ continue;
+ } else {
+ /* we've found the record we're looking
+ * for do something with it..
+ */
+
+ if (sr1->isfake) {
+ /* do another netlookup with the callback */
+ dolog(LOG_DEBUG, "sr is fake, doing netlookup on the callback");
+
+ if (netlookup(db, sr1->callback) < 0) {
+ dolog(LOG_DEBUG, "callback netlookup failed");
+ }
+
+ sr1->callback->hascallback--;
+ /* XXX continue; */
+
+
+ } else {
+ if (type == DNS_TYPE_CNAME)
+ reply_raw_cname(db, sr, &sd, raw);
+ else
+ reply_raw(db, sr1, &sd, raw);
+ }
+ }
+remove:
+ /* only remove if we don't have any callbacks
+ * outstanding...
+ */
+ if (! sr1->hascallback) {
+ SLIST_REMOVE(&recurseshead, sr1, recurses, recurses_entry);
+ free_question(sr1->question);
+ free(sr1);
+ }
+
+ } /* FD_ISSET(sr1->so */
+ } /* SLIST_FOREACH(sr... */
+
+#if 0
+/*
+ I drew this on a notepad one night, I think that's supposed to how
+ it shoudl go...
+
+ +----------------+ +--------------------------+
+ | | | |
+ | v v |
+ | -------------------- select ------------------- |
+ | || || |
+ | || || |
+ | +----+ +----+ |
+ | | | take request | | parse reply |
+ | +----+ from authoritative +----+ and insert |
+ | || side / new record |
+ | || / |
+ | || / |
+ | || +---------------+ |
+ | || / |
+ | || / bad or expired |
+ | +----+=====================>+----+ lookup record |
+ | | | lookup name in db | | on the net |
+ | +----+ +----+-------------------+
+ | ||
+ | || good
+ | ||
+ | +----+
+ | | | reply
+ | +----+
+ | ||
+ | ||
+ | ||
+ | +----+
+ +--------| | cleanup
+ +----+
+
+*/
+
+#endif
+
+
+ } /* for(;;) */
+
+ /* NOTREACHED */
+}
+
+/*
+ * LOOKUP_NS - given an address try to look up the nameservers anywhere along
+ * its path. return number of servers reachable or -1 on error.
+ */
+
+int
+lookup_ns(DB *db, struct recurses *sr)
+{
+ int ret, plen, i;
+ int onemore = 0;
+ char *p;
+
+ DBT key, data;
+
+ struct domain *sd, mydomain;
+
+ p = sr->question->hdr->name;
+ plen = sr->question->hdr->namelen;
+
+ do {
+again:
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+
+ key.data = (char *)p;
+ key.size = plen;
+
+ data.data = NULL;
+ data.size = 0;
+
+ ret = db->get(db, NULL, &key, &data, 0);
+ if (ret != 0) {
+ if (*p != 0) {
+ plen -= (*p + 1);
+ p = (p + (*p + 1));
+ sr->indicator++;
+ }
+
+ /* XXX this is different from lookup_zone(), not
+ * sure how it even works there...
+ */
+ if (*p == 0 && ! onemore) {
+ plen = 1;
+ onemore = 1;
+ sr->indicator++;
+ goto again; /* XXX */
+ }
+ } else {
+ /* we have a lookup */
+
+ if (data.size != sizeof(struct domain)) {
+ dolog(LOG_ERR, "btree db is damaged");
+ return (-1);
+ }
+
+#if 0
+ dolog(LOG_DEBUG, "we gots a lookup, yay!\n");
+#endif
+
+ /*
+ * record which record we used
+ */
+
+ sr->lookrecord = (u_char *)p;
+
+ memcpy((char *)&mydomain, (char *)data.data, sizeof(struct domain));
+ sd = (struct domain *)&mydomain;
+
+ /*
+ * If we're not a static zone (like hints) and we're
+ * expired then we go on to the next indicator..
+ * .. but first we must remove this zone...
+ */
+ if ((! (sd->flags & DOMAIN_STATIC_ZONE)) &&
+ (time(NULL) > (sd->created + sd->ttl))) {
+
+ remove_zone(db, sd);
+
+ if (*p != 0) {
+ plen -= (*p + 1);
+ p = (p + (*p + 1));
+ sr->indicator++;
+ continue;
+ } else {
+ return (-1);
+ }
+ }
+ /*
+ * If we have a negative cache, then just return with
+ * error.
+ */
+ if ((sd->flags & DOMAIN_NEGATIVE_CACHE) &&
+ (time(NULL) <= (sd->created + sd->ttl))) {
+ return (-1);
+ }
+
+ sr->aaaa_count = 0;
+ sr->a_count = 0;
+ sr->a_ptr = 0;
+
+ for (i = 0; i < sd->ns_count; i++) {
+ if (sr->af == AF_INET6) {
+ if (lookup_aaaa(db, sr, &sd->ns[(sd->ns_ptr + i) % sd->ns_count] ) < 0)
+ continue;
+ sr->aaaa_count++;
+ } else {
+ if (lookup_a(db, sr, &sd->ns[(sd->ns_ptr + i) % sd->ns_count] ) < 0)
+ continue;
+ sr->a_count++;
+ }
+ }
+
+ if (sd->ns_count)
+ sd->ns_ptr = (sd->ns_ptr + 1) % sd->ns_count;
+ else
+ sd->ns_ptr = 0;
+
+ update_db(db, sd);
+
+ break;
+ }
+
+ } while (*p != 0 && ret != 0);
+
+#if 1
+ dolog(LOG_DEBUG, "got %d addresses for %s, indicator %d\n", sr->a_count, sr->question->hdr->name, sr->indicator);
+
+#endif
+
+ return ((sr->af == AF_INET6) ? sr->aaaa_count : sr->a_count);
+}
+
+
+/*
+ * LOOKUP_A - given a path, lookup the A record in that record
+ *
+ */
+
+int
+lookup_a(DB *db, struct recurses *sr, struct ns *ns)
+{
+ int ret, plen;
+ char *p;
+
+ DBT key, data;
+
+ struct domain *sd, sdomain;
+ int found = 0;
+
+ p = ns->nsserver;
+ plen = ns->nslen;
+
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+
+ key.data = (char *)p;
+ key.size = plen;
+
+ data.data = NULL;
+ data.size = 0;
+
+ found = 0;
+
+ ret = db->get(db, NULL, &key, &data, 0);
+ if (ret == 0) {
+ if (data.size != sizeof(struct domain)) {
+ dolog(LOG_ERR, "btree db is damaged");
+ return (-1);
+ }
+
+ memcpy((char*)&sdomain, data.data, sizeof(struct domain));
+ sd = &sdomain;
+
+ if ((sd->flags & DOMAIN_HAVE_A) == DOMAIN_HAVE_A) {
+ memcpy((char *)&sr->a[sr->a_count], (char *)&sd->a[0], sizeof(in_addr_t));
+ sd->a_count++;
+ found = 1;
+
+ }
+ }
+
+ if (! found) {
+ dolog(LOG_DEBUG, "calling fakerecurse");
+ fakerecurse(db, sr, ns, DNS_TYPE_A);
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*
+ * NEGATIVE_CACHE - cache a lookup as negative (NXDOMAIN)
+ *
+ */
+
+int
+negative_cache(DB *db, struct recurses *sr)
+{
+ struct domain sd;
+
+ memset(&sd, 0, sizeof(sd));
+
+ sd.zonelen = sr->question->hdr->namelen;
+
+ memcpy((char *)&sd.zone, (char *)sr->question->hdr->name, sd.zonelen);
+
+#if __linux__
+ strncpy((char *)&sd.zonename, (char *)sr->question->converted_name, DNS_MAXNAME);
+ sd.zonename[DNS_MAXNAME] = 0;
+#else
+ strlcpy((char *)&sd.zonename, (char *)sr->question->converted_name, DNS_MAXNAME + 1);
+#endif
+
+ sd.created = time(NULL);
+ sd.ttl = NEGATIVE_CACHE_TIME; /* 10 minutes */
+
+ sd.flags |= DOMAIN_NEGATIVE_CACHE;
+
+ update_db(db, &sd);
+
+ return (0);
+}
+
+/*
+ * RECURSE_PARSE - based on compress_label.
+ *
+ */
+
+int
+recurse_parse(DB *db, struct recurses *sr, u_char *buf, u_int16_t offset)
+{
+ u_char *label[256]; /* should be enough */
+ static u_char converted_name[256][256];
+ u_int8_t cn_len[256];
+ u_char *end = &buf[offset];
+ int update;
+ int rrcount[3]; /* RR count answer, authoritative, additional */
+ int pointer = 0; /* default answer */
+ int txtlen;
+
+ char abuf[INET6_ADDRSTRLEN];
+
+ DBT key, data;
+
+ struct domain sdomain;
+ struct dns_header *dh;
+ struct question {
+ u_int16_t type;
+ u_int16_t class;
+ } __attribute__((packed));
+ struct answer {
+ u_int16_t type;
+ u_int16_t class;
+ u_int32_t ttl;
+ u_int16_t rdlength;
+ } __attribute__((packed));
+ struct mysoa {
+ u_int32_t serial;
+ u_int32_t refresh;
+ u_int32_t retry;
+ u_int32_t expire;
+ u_int32_t minttl;
+ } __attribute__((packed));
+
+ struct answer *a;
+ struct mysoa *mysoa;
+ struct soa *soa;
+ struct ns ns;
+
+ u_int i, j, k;
+ u_int16_t *compressor;
+ u_int16_t c;
+ u_int16_t *preference;
+
+ int found = 0;
+
+ u_char *p, *q, *r;
+
+ dh = (struct dns_header*)&buf[0];
+ rrcount[pointer++] = ntohs(dh->answer);
+ rrcount[pointer++] = ntohs(dh->nsrr);
+ rrcount[pointer++] = ntohs(dh->additional);
+
+ pointer = 0;
+ while (rrcount[pointer] == 0) {
+ pointer++;
+ if (pointer > 2)
+ return (-1);
+ }
+
+ p = &buf[sizeof(struct dns_header)];
+ label[0] = p;
+
+ while (p <= end && *p) {
+ p += *p;
+ p++;
+ }
+
+ /*
+ * the question label was bogus, we'll just get out of there, return 0
+ */
+
+ if (p > end)
+ return (-1);
+
+ p += sizeof(struct question);
+ p++; /* one more */
+ /* start of answer/additional/authoritative */
+
+ for (i = 1; i < 100; i++) {
+ label[i] = p;
+
+ while (p <= end && *p) {
+ if ((*p & 0xc0) == 0xc0) {
+ p++;
+ break;
+ }
+ p += *p;
+ p++;
+
+ if (p > end)
+ goto end;
+ }
+
+ p++; /* one more */
+
+
+ a = (struct answer *)p;
+ p += sizeof(struct answer);
+
+ if (p > end)
+ goto end;
+
+ switch (ntohs(a->type)) {
+ case DNS_TYPE_A:
+ p += sizeof(in_addr_t);
+ break;
+ case DNS_TYPE_AAAA:
+ p += 16; /* sizeof 4 * 32 bit */
+ break;
+ case DNS_TYPE_TXT:
+ p += *p;
+ p++;
+ break;
+ case DNS_TYPE_MX:
+ p += sizeof(u_int16_t); /* mx_priority */
+ /* FALLTHROUGH */
+ case DNS_TYPE_NS:
+ case DNS_TYPE_PTR:
+ case DNS_TYPE_CNAME:
+ label[++i] = p;
+ while (p <= end && *p) {
+ if ((*p & 0xc0) == 0xc0) {
+ p++;
+ break;
+ }
+ p += *p;
+ p++;
+
+ if (p > end)
+ goto end;
+ }
+
+ p++; /* one more */
+ break;
+ case DNS_TYPE_SOA:
+ /* nsserver */
+ label[++i] = p;
+ while (p <= end && *p) {
+ if ((*p & 0xc0) == 0xc0) {
+ p++;
+ break;
+ }
+ p += *p;
+ p++;
+ if (p > end)
+ goto end;
+ }
+
+ p++; /* one more */
+
+ if (p > end)
+ goto end;
+
+ /* responsible person */
+ label[++i] = p;
+ while (p <= end && *p) {
+ if ((*p & 0xc0) == 0xc0) {
+ p++;
+ break;
+ }
+ p += *p;
+ p++;
+ }
+
+ p++; /* one more */
+
+ if (p > end)
+ goto end;
+
+ p += sizeof(struct mysoa); /* advance struct soa */
+
+ break;
+ default:
+ break;
+ /* XXX */
+ } /* switch */
+
+ if (p >= end)
+ break;
+ } /* for (i *) */
+
+ /*
+ * go through our list of labels and expand them from possible
+ * compression, then we make our next pass..
+ */
+
+ for (j = 0; j <= i; j++) {
+ q = converted_name[j];
+ p = label[j];
+again:
+ for (; *p ; p += *p, p++) {
+ if ((*p & 0xc0) == 0xc0)
+ break;
+
+ r = (p + 1);
+
+ *q++ = *p;
+
+ for (k = 0; k < *p; k++)
+ *q++ = tolower(r[k]);
+ }
+
+ if ((*p & 0xc0) == 0xc0) {
+ compressor = (u_int16_t *)p;
+ c = (ntohs(*compressor) & ~(0xc000));
+ for (k = 0; k <= i; k++) {
+ found = 0;
+ for (r = label[k]; *r; r += *r, r++) {
+ if (r - buf == c) {
+ p = r;
+ found = 1;
+ }
+
+
+ /*
+ * we're searching for an offset in
+ * non-compressed form, but we
+ * encountered compression, so we
+ * break and go to the next label
+ */
+
+ if ((*r & 0xc0) == 0xc0) {
+ break;
+ }
+ }
+
+ if (found) {
+ /*
+ * pretend we found a match but we
+ * have compression inside pointing
+ * down, then break this, it's corrupt
+ * it's a possible loop attempt
+ */
+ if ((*r & 0xc0) == 0xc0) {
+ compressor = (u_int16_t *)r;
+ if ((ntohs(*compressor) & ~0xc000) >= c)
+ break;
+ }
+
+ goto again;
+ }
+ }
+
+ /*
+ * if we fall through the for loop, we didn't find the
+ * recursive label... corrupt.
+ */
+
+ dolog(LOG_ERR, "corrupt compression");
+ return (-1);
+ }
+
+ *q++ = '\0'; /* don't forget this */
+ cn_len[j] = (q - converted_name[j]);
+
+ } /* for (j .. */
+
+#if 0
+ for (j = 0; j <= i; j++) {
+ dolog(LOG_DEBUG, "%s with length %u", converted_name[j], cn_len[j]);
+ }
+#endif
+
+ p = &buf[sizeof(struct dns_header)];
+ label[0] = p;
+
+ while (p <= end && *p) {
+ p += *p;
+ p++;
+ }
+
+ /*
+ * the question label was bogus, we'll just get out of there, return 0
+ */
+
+ if (p > end)
+ return (-1);
+
+ p += sizeof(struct question);
+ p++; /* one more */
+ /* start of answer/additional/authoritative */
+
+ for (i = 1; i < 100; i++) {
+ label[i] = p;
+
+ while (p <= end && *p) {
+ if ((*p & 0xc0) == 0xc0) {
+ p++;
+ break;
+ }
+ p += *p;
+ p++;
+
+ if (p > end)
+ goto end;
+ }
+
+ p++; /* one more */
+
+
+ a = (struct answer *)p;
+ p += sizeof(struct answer);
+
+ /* load our record */
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+
+ key.data = (char *)converted_name[i];
+ key.size = cn_len[i];
+
+ data.data = NULL;
+ data.size = 0;
+
+ memset((char *)&sdomain, 0, sizeof(struct domain));
+ if (db->get(db, NULL, &key, &data, 0) == 0) {
+ if (data.size != sizeof(struct domain)) {
+ dolog(LOG_INFO, "damaged btree database");
+ return -1;
+ }
+
+ memcpy((char *)&sdomain, (char *)data.data, data.size);
+
+ }
+
+
+ if (sdomain.zone == NULL) {
+ memcpy(&sdomain.zone, converted_name[i], cn_len[i]);
+ sdomain.zonelen = cn_len[i];
+ }
+
+
+ switch (ntohs(a->type)) {
+ case DNS_TYPE_A:
+ /*
+ * scan addresses in this struct domain and check if
+ * this one exists already...
+ */
+
+ update = 1;
+ for (j = 0; j < sdomain.a_count; j++) {
+ if (memcmp(&sdomain.a[j], p, sizeof(in_addr_t)) == 0) {
+#if 0
+ dolog(LOG_INFO, "record exists already");
+#endif
+ update = 0;
+ }
+ }
+
+ if (j >= RECORD_COUNT) {
+ dolog(LOG_INFO, "db can't hold any more records\n");
+ update = 0;
+ }
+
+ /*
+ * check if we're a 2nd level domain or higher and
+ * if we were directly querying the zone...
+ */
+
+ if (update) {
+ if (level(sr->lookrecord) > 1) {
+ if (!contains(sr->lookrecord, converted_name[i])) {
+ memcpy(ns.nsserver, converted_name[i], cn_len[i]);
+ ns.nslen = cn_len[i];
+
+ fakerecurse(db, sr, &ns, DNS_TYPE_A);
+ update = 0;
+ }
+ }
+ }
+
+
+ if (update) {
+ memcpy(&sdomain.a[j], p, sizeof(in_addr_t));
+ sdomain.a_count++;
+ sdomain.region[j] = 0xff;
+ sdomain.a_ptr = 0;
+ sdomain.flags |= DOMAIN_HAVE_A;
+ sdomain.flags &= ~(DOMAIN_NEGATIVE_CACHE);
+ sdomain.created = time(NULL);
+ sdomain.ttl = ntohl(a->ttl);
+
+ if (! (sdomain.flags & DOMAIN_STATIC_ZONE)) {
+ update_db(db, &sdomain);
+ inet_ntop(AF_INET, p, abuf, sizeof(abuf));
+ dolog(LOG_DEBUG, "updateing zone %s with address %s ttl= %u, lookrecord = %s", converted_name[i], abuf, sdomain.ttl, sr->lookrecord);
+ }
+ }
+
+ p += sizeof(in_addr_t);
+ if (pointer > 2) {
+ dolog(LOG_ERR, "there is more records than indicated in the header!!!");
+ return (-1);
+ }
+ rrcount[pointer]--;
+ if (rrcount[pointer] == 0)
+ pointer++;
+
+ break;
+ case DNS_TYPE_AAAA:
+ /*
+ * scan addresses in this struct domain and check if
+ * this one exists already...
+ */
+
+ update = 1;
+ for (j = 0; j < sdomain.aaaa_count; j++) {
+ if (memcmp(&sdomain.aaaa[j], p, sizeof(struct in6_addr)) == 0) {
+#if 0
+ dolog(LOG_INFO, "record exists already");
+#endif
+ update = 0;
+ }
+ }
+
+ if (j >= RECORD_COUNT) {
+ dolog(LOG_INFO, "db can't hold any more records\n");
+ update = 0;
+ }
+
+ /*
+ * check if we're a 2nd level domain or higher and
+ * if we were directly querying the zone...
+ */
+
+ if (update) {
+ if (level(sr->lookrecord) > 1) {
+ if (!contains(sr->lookrecord, converted_name[i])) {
+ memcpy(ns.nsserver, converted_name[i], cn_len[i]);
+ ns.nslen = cn_len[i];
+
+ fakerecurse(db, sr, &ns, DNS_TYPE_AAAA);
+ update = 0;
+ }
+ }
+ }
+
+ if (update) {
+ memcpy(&sdomain.aaaa[j], p, sizeof(struct in6_addr));
+ sdomain.aaaa_count++;
+ sdomain.aaaa_ptr = 0;
+ sdomain.flags |= DOMAIN_HAVE_AAAA;
+ sdomain.flags &= ~(DOMAIN_NEGATIVE_CACHE);
+ sdomain.created = time(NULL);
+ sdomain.ttl = ntohl(a->ttl);
+
+ if (! (sdomain.flags & DOMAIN_STATIC_ZONE)) {
+ update_db(db, &sdomain);
+ inet_ntop(AF_INET6, p, abuf, sizeof(abuf));
+ dolog(LOG_DEBUG, "updateing zone %s with address %s ttl= %u\n", converted_name[i], abuf, sdomain.ttl);
+ }
+ }
+
+ if (pointer > 2) {
+ dolog(LOG_ERR, "there is more records than indicated in the header!!!");
+ return (-1);
+ }
+ rrcount[pointer]--;
+ if (rrcount[pointer] == 0)
+ pointer++;
+
+ p += 16; /* sizeof 4 * 32 bit */
+ break;
+ case DNS_TYPE_TXT:
+ txtlen = (*p);
+
+ memcpy(&sdomain.txt, (p + 1), txtlen);
+ sdomain.txtlen = txtlen;
+
+ sdomain.flags |= DOMAIN_HAVE_TXT;
+ sdomain.flags &= ~(DOMAIN_NEGATIVE_CACHE);
+ sdomain.created = time(NULL);
+ sdomain.ttl = ntohl(a->ttl);
+
+ if (! (sdomain.flags & DOMAIN_STATIC_ZONE)) {
+ update_db(db, &sdomain);
+ }
+
+ if (pointer > 2) {
+ dolog(LOG_ERR, "there is more records than indicated in the header!!!");
+ return (-1);
+ }
+
+ rrcount[pointer]--;
+ if (rrcount[pointer] == 0)
+ pointer++;
+
+
+ p += *p;
+ p++;
+ break;
+ case DNS_TYPE_MX:
+ preference = (u_int16_t *)p;
+ p += sizeof(u_int16_t); /* mx_priority */
+ /* FALLTHROUGH */
+ case DNS_TYPE_NS:
+ case DNS_TYPE_PTR:
+ case DNS_TYPE_CNAME:
+ label[++i] = p;
+ while (p <= end && *p) {
+ if ((*p & 0xc0) == 0xc0) {
+ p++;
+ break;
+ }
+ p += *p;
+ p++;
+
+ if (p > end)
+ goto end;
+ }
+
+ if (ntohs(a->type) == DNS_TYPE_CNAME) {
+ /*
+ * we check this as well as the A and AAAA
+ * types since it may be possible to glue
+ * a CNAME instead of an A and thus poison
+ * our cache...
+ */
+ if (level(sr->lookrecord) > 1) {
+ if (!contains(sr->lookrecord, converted_name[i - 1])) {
+
+ memcpy(ns.nsserver, converted_name[i - 1], cn_len[i - 1]);
+ ns.nslen = cn_len[i];
+
+ fakerecurse(db, sr, &ns, DNS_TYPE_A);
+ rrcount[pointer]--;
+ if (rrcount[pointer] == 0)
+ pointer++;
+ p++;
+ break;
+ }
+ }
+
+ memcpy(&sdomain.cname, converted_name[i], cn_len[i]);
+ sdomain.cnamelen = cn_len[i];
+
+ sdomain.flags |= DOMAIN_HAVE_CNAME;
+ sdomain.flags &= ~(DOMAIN_NEGATIVE_CACHE);
+ sdomain.created = time(NULL);
+ sdomain.ttl = ntohl(a->ttl);
+
+ if (! (sdomain.flags & DOMAIN_STATIC_ZONE)) {
+ update_db(db, &sdomain);
+#if 1
+ dolog(LOG_DEBUG, "updateing zone %s with PTR name %s ttl= %u\n", converted_name[i - 1], converted_name[i], sdomain.ttl);
+#endif
+ }
+
+ if (pointer > 2) {
+ dolog(LOG_ERR, "there is more records than indicated in the header!!!");
+ return (-1);
+ }
+
+ rrcount[pointer]--;
+ if (rrcount[pointer] == 0)
+ pointer++;
+ } else if (ntohs(a->type) == DNS_TYPE_PTR) {
+ memcpy(&sdomain.ptr, converted_name[i], cn_len[i]);
+ sdomain.ptrlen = cn_len[i];
+
+ sdomain.flags |= DOMAIN_HAVE_PTR;
+ sdomain.flags &= ~(DOMAIN_NEGATIVE_CACHE);
+ sdomain.created = time(NULL);
+ sdomain.ttl = ntohl(a->ttl);
+
+ if (! (sdomain.flags & DOMAIN_STATIC_ZONE)) {
+ update_db(db, &sdomain);
+#if 1
+ dolog(LOG_DEBUG, "updateing zone %s with PTR name %s ttl= %u\n", converted_name[i - 1], converted_name[i], sdomain.ttl);
+#endif
+ }
+
+ if (pointer > 2) {
+ dolog(LOG_ERR, "there is more records than indicated in the header!!!");
+ return (-1);
+ }
+
+ rrcount[pointer]--;
+ if (rrcount[pointer] == 0)
+ pointer++;
+ } else if (ntohs(a->type) == DNS_TYPE_NS) {
+ update = 1;
+ for (j = 0; j < sdomain.ns_count; j++) {
+ if (memcasecmp((u_char *)sdomain.ns[j].nsserver, (u_char *)converted_name[i], MIN(cn_len[i], sdomain.ns[j].nslen)) == 0) {
+#if 0
+ dolog(LOG_INFO, "record exists already");
+#endif
+ update = 0;
+ }
+ }
+
+ if (j >= RECORD_COUNT) {
+ dolog(LOG_INFO, "db can't hold any more records\n");
+ update = 0;
+ }
+
+ if (update) {
+ memcpy(sdomain.ns[j].nsserver, converted_name[i], cn_len[i]);
+ sdomain.ns[j].nslen = cn_len[i];
+
+ sdomain.ns_count++;
+ sdomain.ns_ptr = 0;
+ sdomain.flags |= DOMAIN_HAVE_NS;
+ sdomain.flags &= ~(DOMAIN_NEGATIVE_CACHE);
+ sdomain.created = time(NULL);
+ sdomain.ttl = ntohl(a->ttl);
+
+ if (! (sdomain.flags & DOMAIN_STATIC_ZONE)) {
+ update_db(db, &sdomain);
+#if 0
+ dolog(LOG_DEBUG, "updateing zone %s with NS name %s ttl= %u\n", converted_name[i - 1], converted_name[i], sdomain.ttl);
+#endif
+ }
+ } /* if update */
+ if (pointer > 2) {
+ dolog(LOG_ERR, "there is more records than indicated in the header!!!");
+ return (-1);
+ }
+
+ rrcount[pointer]--;
+ if (pointer == 1) /* authoritative */
+ sr->authoritative = DNS_TYPE_NS;
+ if (rrcount[pointer] == 0)
+ pointer++;
+
+ } else if (ntohs(a->type) == DNS_TYPE_MX) {
+ update = 1;
+ for (j = 0; j < sdomain.mx_count; j++) {
+ if (memcasecmp((u_char *)sdomain.mx[j].exchange, (u_char *)converted_name[i], MIN(cn_len[i], sdomain.mx[j].exchangelen)) == 0) {
+ update = 0;
+ }
+ }
+
+ if (j >= RECORD_COUNT) {
+ dolog(LOG_INFO, "db can't hold any more records\n");
+ update = 0;
+ }
+
+ if (update) {
+ memcpy(&sdomain.mx[j].exchange, converted_name[i], cn_len[i]);
+ sdomain.mx[j].exchangelen = cn_len[i];
+ sdomain.mx[j].preference = ntohs(*preference);
+
+ sdomain.mx_count++;
+ sdomain.mx_ptr = 0;
+ sdomain.flags |= DOMAIN_HAVE_MX;
+ sdomain.flags &= ~(DOMAIN_NEGATIVE_CACHE);
+ sdomain.created = time(NULL);
+ sdomain.ttl = ntohl(a->ttl);
+
+ if (! (sdomain.flags & DOMAIN_STATIC_ZONE)) {
+ update_db(db, &sdomain);
+#if 0
+ dolog(LOG_DEBUG, "updateing zone %s with MX name %s ttl= %u\n", converted_name[i - 1], converted_name[i], sdomain.ttl);
+#endif
+ }
+ } /* if update */
+ if (pointer > 2) {
+ dolog(LOG_ERR, "there is more records than indicated in the header!!!");
+ return (-1);
+ }
+
+ rrcount[pointer]--;
+#if 0
+ if (pointer == 1) /* authoritative */
+ sr->authoritative = DNS_TYPE_MX;
+#endif
+ if (rrcount[pointer] == 0)
+ pointer++;
+
+ } /* if type ns */
+
+ p++; /* one more */
+ break;
+ case DNS_TYPE_SOA:
+ /* nsserver */
+ label[++i] = p;
+ while (p <= end && *p) {
+ if ((*p & 0xc0) == 0xc0) {
+ p++;
+ break;
+ }
+ p += *p;
+ p++;
+ if (p > end)
+ goto end;
+ }
+
+ p++; /* one more */
+
+ if (p > end)
+ goto end;
+
+ /* responsible person */
+ label[++i] = p;
+ while (p <= end && *p) {
+ if ((*p & 0xc0) == 0xc0) {
+ p++;
+ break;
+ }
+ p += *p;
+ p++;
+ }
+
+ p++; /* one more */
+
+ if (p > end)
+ goto end;
+
+ mysoa = (struct mysoa *)p;
+ p += sizeof(struct mysoa); /* advance struct soa */
+
+ /* malloc struct soa */
+
+ soa = malloc(sizeof(struct soa));
+ if (soa == NULL) {
+ dolog(LOG_ERR, "malloc: %m");
+ return (-1);
+ }
+
+ memcpy(soa->nsserver, converted_name[i - 1], cn_len[i - 1]);
+ soa->nsserver_len = cn_len[i - 1];
+ memcpy(soa->responsible_person, converted_name[i], cn_len[i]);
+ soa->rp_len = cn_len[i];
+ soa->serial = ntohl(mysoa->serial);
+ soa->refresh = ntohl(mysoa->refresh);
+ soa->retry = ntohl(mysoa->retry);
+ soa->expire = ntohl(mysoa->expire);
+ soa->minttl = ntohl(mysoa->minttl);
+
+ memcpy(&sdomain.soa, soa, sizeof(sdomain.soa));
+ free(soa);
+
+ sdomain.flags |= DOMAIN_HAVE_SOA;
+ sdomain.created = time(NULL);
+ sdomain.ttl = htonl(a->ttl);
+
+ if (! (sdomain.flags & DOMAIN_STATIC_ZONE)) {
+ update_db(db, &sdomain);
+ }
+
+ if (pointer > 2) {
+ dolog(LOG_ERR, "there is more records than indicated in the header!!!");
+ return (-1);
+ }
+
+ rrcount[pointer]--;
+ if (pointer == 1) /* authoritative */
+ sr->authoritative = DNS_TYPE_SOA;
+ if (rrcount[pointer] == 0)
+ pointer++;
+
+
+ break;
+ default:
+ break;
+ /* XXX */
+ } /* switch */
+
+ if (p >= end)
+ break;
+ } /* for (i *) */
+
+
+ return (0);
+
+end:
+ dolog(LOG_DEBUG, "mangled input packet");
+ return (-1);
+
+}
+
+
+/*
+ * NETLOOKUP - do a internet lookup of the requested internet record
+ *
+ */
+
+int
+netlookup(DB *db, struct recurses *sr)
+{
+ struct sockaddr_in sin;
+ struct dns_header *dh;
+
+ char buf[2048];
+ int flag;
+
+
+ /* do the network stuff then */
+ /* XXX should be IPv6 ready */
+
+ if (sr->af == AF_INET6)
+ return (netlookup6(db, sr));
+
+ if (sr->so != -1) {
+ if (close(sr->so) < 0)
+ dolog(LOG_ERR, "close: %m");
+ }
+
+ sr->so = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (sr->so < 0) {
+ dolog(LOG_ERR, "socket: %m");
+ sr->so = -1;
+ return (-1);
+ }
+
+ sr->port = arc4random() & 0xffff;
+ /*
+ * we have to avoid picking servers already
+ * running ..
+ */
+ if (sr->port < 1024)
+ sr->port += 1024;
+
+ sr->id = arc4random() & 0xffff;
+
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(sr->port);
+ sin.sin_addr.s_addr = INADDR_ANY;
+
+ if (bind(sr->so, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
+ dolog(LOG_ERR, "bind: %m");
+ if (close(sr->so) < 0) {
+ dolog(LOG_ERR, "close: %m");
+ }
+ sr->so = -1;
+ return (-1);
+ }
+
+ /*
+ * make this socket nonblocking
+ */
+
+ if ((flag = fcntl(sr->so, F_GETFL)) < 0) {
+ dolog(LOG_INFO, "fcntl 3: %m");
+ }
+ flag |= O_NONBLOCK;
+ if (fcntl(sr->so, F_SETFL, flag) < 0) {
+ dolog(LOG_INFO, "fcntl 4: %m");
+ }
+
+ if (lookup_ns(db, sr) <= 0) {
+ dolog(LOG_ERR, "can't establish any servers to reach for zone \"%s\"", sr->question->converted_name);
+ if (close(sr->so) < 0) {
+ dolog(LOG_ERR, "close: %m");
+ }
+ sr->so = -1;
+ return (-1);
+ }
+
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(53);
+
+ sin.sin_addr.s_addr = sr->a[0];
+
+ /* XXX we use buf here in order to preserve
+ * the state of query...
+ */
+ memcpy(buf, sr->query, sr->len);
+ dh = (struct dns_header *)&buf[0];
+ NTOHS(dh->query);
+ UNSET_DNS_RECURSION(dh);
+ HTONS(dh->query);
+ dh->id = htons(sr->id);
+
+#if 1
+ dolog(LOG_INFO, "sending request with id %u\n", sr->id);
+
+#endif
+
+ if (sendto(sr->so, buf, sr->len, 0, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
+ dolog(LOG_ERR, "sendto: %m");
+ if (close(sr->so) < 0) {
+ dolog(LOG_ERR, "close: %m");
+ }
+ sr->so = -1;
+ return (-1);
+ }
+
+ sr->sent_last_query = time(NULL);
+ sr->packetcount++;
+
+
+ return (0);
+}
+
+/*
+ * FAKERECURSE - create a fake query of type A, for a nameserver that has
+ * no glued A record, attach a callback to the struct recurses
+ * that did the initiation for this...
+ */
+
+int
+fakerecurse(DB *db, struct recurses *sr, struct ns *ns, int type)
+{
+ struct recurses *fakesr;
+ struct dns_header *dh;
+
+ char *p;
+
+ int len;
+ u_int16_t *qtype, *qclass;
+
+ /* check if we have already started a fakerecurse on the same name */
+
+ SLIST_FOREACH(sr2, &recurseshead, recurses_entry) {
+ if (memcasecmp((u_char *)ns->nsserver, (u_char *)sr2->question->hdr->name, MIN(ns->nslen, sr2->question->hdr->namelen)) == 0) {
+ dolog(LOG_INFO, "already have a fakerecurse structure with name %s, drop\n", ns->nsserver);
+ return (-1);
+ }
+ }
+
+
+ /* place request on struct recurses linked list */
+
+ fakesr = calloc(sizeof(struct recurses), 1);
+ if (fakesr == NULL) {
+ dolog(LOG_ERR, "calloc: %m");
+ return (-1);
+ }
+
+
+ fakesr->af = sr->af;
+ fakesr->proto = sr->proto;
+ fakesr->so = -1;
+ fakesr->callback = sr;
+ sr->hascallback++;
+ fakesr->hascallback = 0;
+ fakesr->isfake = 1;
+ fakesr->launched = 0;
+ fakesr->received = time(NULL);
+ fakesr->packetcount = 0;
+ fakesr->lookrecord = NULL;
+
+ fakesr->question = build_fake_question(ns->nsserver, ns->nslen, htons(type));
+ if (fakesr->question == NULL) {
+ dolog(LOG_ERR, "malformed question in recurse.c");
+ free(fakesr);
+ return (-1);
+ }
+
+ /* construct the question packet */
+
+ len = sizeof(struct dns_header);
+ dh = (struct dns_header *)fakesr->query;
+ dh->id = htons(1);
+ SET_DNS_QUERY(dh);
+ HTONS(dh->query);
+ dh->question = htons(1);
+ dh->answer = 0;
+ dh->nsrr = 0;
+ dh->additional = 0;
+
+ p = (char *)&fakesr->query[len];
+ memcpy(p, ns->nsserver, ns->nslen);
+ len += ns->nslen;
+ qtype = (u_int16_t *)&fakesr->query[len];
+ *qtype = fakesr->question->hdr->qtype;
+ len += sizeof(u_int16_t);
+ qclass = (u_int16_t *)&fakesr->query[len];
+ *qclass = fakesr->question->hdr->qclass;
+ len += sizeof(u_int16_t);
+
+ fakesr->len = len;
+
+ SLIST_INSERT_HEAD(&recurseshead, fakesr, recurses_entry);
+
+ return (0);
+}
+
+/*
+ * REPLY_RAW -
+ *
+ *
+ */
+
+void
+reply_raw(DB *db, struct recurses *sr, struct domain *sd, int *raw)
+{
+ int so;
+ struct sreply sreply;
+
+ dolog(LOG_DEBUG, "reply_raw called");
+
+ switch (sr->af) {
+ case AF_INET:
+ so = raw[0];
+ break;
+ case AF_INET6:
+ so = raw[1];
+ break;
+ default:
+ dolog(LOG_ERR, "reply_raw(): unknown address family in struct recurses");
+ return;
+ }
+
+ switch (sr->proto) {
+ case IPPROTO_UDP:
+ break;
+ default:
+ dolog(LOG_ERR, "reply_raw(): can't do any protocol other than udp right now");
+ return;
+ }
+
+ build_reply(&sreply, so, sr->query, sr->len, sr->question, NULL, 0, sd, NULL, 0xff, 0, 0, sr);
+
+ switch (ntohs(sr->question->hdr->qtype)) {
+ case DNS_TYPE_A:
+ reply_a(&sreply, db);
+ break;
+ case DNS_TYPE_AAAA:
+ reply_aaaa(&sreply, db);
+ break;
+ case DNS_TYPE_NS:
+ reply_ns(&sreply, db);
+ break;
+ case DNS_TYPE_PTR:
+ reply_ptr(&sreply);
+ break;
+ case DNS_TYPE_MX:
+ reply_mx(&sreply, db);
+ break;
+ case DNS_TYPE_SOA:
+ reply_soa(&sreply);
+ break;
+ case DNS_TYPE_CNAME:
+ reply_cname(&sreply);
+ break;
+ case DNS_TYPE_TXT:
+ reply_txt(&sreply);
+ break;
+ default:
+ dolog(LOG_ERR, "other types have not been implemented yet");
+ break;
+ }
+
+ return;
+}
+
+void
+reply_raw_cname(DB *db, struct recurses *sr, struct domain *sd, int *raw)
+{
+ int so;
+ struct sreply sreply;
+
+ dolog(LOG_DEBUG, "reply_raw called");
+
+ switch (sr->af) {
+ case AF_INET:
+ so = raw[0];
+ break;
+ case AF_INET6:
+ so = raw[1];
+ break;
+ default:
+ dolog(LOG_ERR, "reply_raw_cname(): unknown address family in struct recurses");
+ return;
+ }
+
+ switch (sr->proto) {
+ case IPPROTO_UDP:
+ break;
+ default:
+ dolog(LOG_ERR, "reply_raw_cname(): can't do any protocol other than udp right now");
+ return;
+ }
+
+ build_reply(&sreply, so, sr->query, sr->len, sr->question, NULL, 0, sd, NULL, 0xff, 0, 0, sr);
+
+ reply_cname(&sreply);
+
+ return;
+}
+
+/*
+ * REMOVE_ZONE - remove a zone from the database (it probably expired)
+ *
+ *
+ */
+
+void
+remove_zone(DB *db, struct domain *sd)
+{
+ DBT key;
+ char *zone;
+ int zonelen;
+
+ zone = sd->zone;
+ zonelen = sd->zonelen;
+
+ key.data = (char *)zone;
+ key.size = zonelen;
+
+ if (db->del(db, NULL, &key, 0) != 0) {
+ dolog(LOG_ERR, "could not delete zone %s: %m", zone);
+ }
+
+ dolog(LOG_DEBUG, "deleting zone %s\n", zone);
+
+ free(zone);
+
+ return;
+}
+
+void
+reply_raw_noerror(DB *db, struct recurses *sr, struct domain *sd, int *raw)
+{
+ int so;
+ struct sreply sreply;
+
+ dolog(LOG_DEBUG, "reply_raw_noerror called");
+
+ switch (sr->af) {
+ case AF_INET:
+ so = raw[0];
+ break;
+ case AF_INET6:
+ so = raw[1];
+ break;
+ default:
+ dolog(LOG_ERR, "reply_raw_noerror(): unknown address family in struct recurses");
+ return;
+ }
+
+ switch (sr->proto) {
+ case IPPROTO_UDP:
+ break;
+ default:
+ dolog(LOG_ERR, "reply_raw_noerror(): can't do any protocol other than udp right now");
+ return;
+ }
+
+ build_reply(&sreply, so, sr->query, sr->len, sr->question, NULL, 0, sd, NULL, 0xff, 0, 0, sr);
+
+ reply_noerror(&sreply);
+
+ return;
+}
+
+void
+reply_raw_nxdomain(DB *db, struct recurses *sr, struct domain *sd, int *raw)
+{
+ int so;
+ struct sreply sreply;
+
+ dolog(LOG_DEBUG, "reply_raw_nxdomain called");
+
+ switch (sr->af) {
+ case AF_INET:
+ so = raw[0];
+ break;
+ case AF_INET6:
+ so = raw[1];
+ break;
+ default:
+ dolog(LOG_ERR, "reply_raw_nxdomain(): unknown address family in struct recurses");
+ return;
+ }
+
+ switch (sr->proto) {
+ case IPPROTO_UDP:
+ break;
+ default:
+ dolog(LOG_ERR, "reply_raw_nxdomain(): can't do any protocol other than udp right now");
+ return;
+ }
+
+ build_reply(&sreply, so, sr->query, sr->len, sr->question, NULL, 0, sd, NULL, 0xff, 0, 0, sr);
+
+ reply_nxdomain(&sreply);
+
+ return;
+}
+
+
+/*
+ * LEVEL - traverse a domain name and count how many levels it has
+ * first level is a TLD, then a 2nd level domain and so on.
+ */
+
+int
+level(u_char *p)
+{
+ int level = 0;
+
+ while (*p) {
+ level++;
+ p += ((*p) + 1);
+ }
+
+ return (level);
+}
+
+/*
+ * CONTAINS - check if domain name A is contained in domain name B
+ *
+ */
+
+int
+contains(u_char *a, u_char *b)
+{
+ u_char *p = a;
+ u_char *q = b;
+ u_int plen = 0, qlen = 0;
+
+ while (*p) {
+ plen += (*p) + 1;
+ p += ((*p) + 1);
+ }
+
+ while (*q) {
+ qlen += ((*q) + 1);
+ q += ((*q) + 1);
+ }
+
+ p = a;
+ q = b;
+
+ while (*q) {
+ if ((plen == qlen) && memcasecmp((u_char *)p, (u_char *)q, qlen) == 0)
+ return (1);
+
+ qlen -= ((*q) + 1);
+ q += ((*q) + 1);
+ }
+
+ return (0);
+}
+
+/*
+ * NETLOOKUP6 - do an ipv6 lookup of the requested internet record
+ *
+ */
+
+int
+netlookup6(DB *db, struct recurses *sr)
+{
+ struct sockaddr_in6 sin6;
+ struct dns_header *dh;
+
+ char buf[2048];
+ int flag;
+
+ if (sr->so != -1) {
+ if (close(sr->so) < 0)
+ dolog(LOG_ERR, "close: %m");
+ }
+
+ sr->so = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
+ if (sr->so < 0) {
+ dolog(LOG_ERR, "socket6: %m");
+ sr->so = -1;
+ return (-1);
+ }
+
+ sr->port = arc4random() & 0xffff;
+ /*
+ * we have to avoid picking servers already
+ * running ..
+ */
+ if (sr->port < 1024)
+ sr->port += 1024;
+
+ sr->id = arc4random() & 0xffff;
+
+ memset(&sin6, 0, sizeof(sin6));
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_port = htons(sr->port);
+#ifndef __linux__
+ sin6.sin6_len = sizeof(struct sockaddr_in6);
+#endif
+
+ if (bind(sr->so, (struct sockaddr *)&sin6, sizeof(sin6)) < 0) {
+ dolog(LOG_ERR, "bind: %m");
+ if (close(sr->so) < 0) {
+ dolog(LOG_ERR, "close: %m");
+ }
+ sr->so = -1;
+ return (-1);
+ }
+
+ /*
+ * make this socket nonblocking
+ */
+
+ if ((flag = fcntl(sr->so, F_GETFL)) < 0) {
+ dolog(LOG_INFO, "fcntl 3: %m");
+ }
+ flag |= O_NONBLOCK;
+ if (fcntl(sr->so, F_SETFL, flag) < 0) {
+ dolog(LOG_INFO, "fcntl 4: %m");
+ }
+
+ if (lookup_ns(db, sr) <= 0) {
+ dolog(LOG_ERR, "can't establish any servers to reach for zone \"%s\"", sr->question->converted_name);
+ if (close(sr->so) < 0) {
+ dolog(LOG_ERR, "close: %m");
+ }
+ sr->so = -1;
+ return (-1);
+ }
+
+ memset(&sin6, 0, sizeof(sin6));
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_port = htons(53);
+
+ /* pjp */
+ memcpy((char *)&sin6.sin6_addr, (char *)&sr->aaaa[0], sizeof(struct in6_addr));
+
+ /* XXX we use buf here in order to preserve
+ * the state of query...
+ */
+ memcpy(buf, sr->query, sr->len);
+ dh = (struct dns_header *)&buf[0];
+ NTOHS(dh->query);
+ UNSET_DNS_RECURSION(dh);
+ HTONS(dh->query);
+ dh->id = htons(sr->id);
+
+#if 1
+ dolog(LOG_INFO, "sending request with id %u\n", sr->id);
+
+#endif
+
+ if (sendto(sr->so, buf, sr->len, 0, (struct sockaddr *)&sin6, sizeof(sin6)) < 0) {
+ dolog(LOG_ERR, "sendto6: %m");
+ if (close(sr->so) < 0) {
+ dolog(LOG_ERR, "close: %m");
+ }
+ sr->so = -1;
+ return (-1);
+ }
+
+ sr->sent_last_query = time(NULL);
+ sr->packetcount++;
+
+
+ return (0);
+}
+
+/*
+ * LOOKUP_AAAA - given a path, lookup the AAAA record in that record
+ *
+ */
+
+int
+lookup_aaaa(DB *db, struct recurses *sr, struct ns *ns)
+{
+ int ret, plen;
+ char *p;
+
+ DBT key, data;
+
+ struct domain *sd, sdomain;
+ int found = 0;
+
+ p = ns->nsserver;
+ plen = ns->nslen;
+
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+
+ key.data = (char *)p;
+ key.size = plen;
+
+ data.data = NULL;
+ data.size = 0;
+
+ found = 0;
+
+ ret = db->get(db, NULL, &key, &data, 0);
+ if (ret == 0) {
+ if (data.size != sizeof(struct domain)) {
+ dolog(LOG_ERR, "btree db is damaged");
+ return (-1);
+ }
+
+ memcpy((char*)&sdomain, data.data, sizeof(struct domain));
+ sd = &sdomain;
+
+ if ((sd->flags & DOMAIN_HAVE_AAAA) == DOMAIN_HAVE_AAAA) {
+ memcpy((char *)&sr->aaaa[sr->aaaa_count], (char *)&sd->aaaa[0], sizeof(struct in6_addr));
+ sd->a_count++;
+ found = 1;
+
+ }
+ }
+
+ if (! found) {
+ dolog(LOG_DEBUG, "calling fakerecurse");
+ fakerecurse(db, sr, ns, DNS_TYPE_AAAA);
+ return (-1);
+ }
+
+ return (0);
+}
blob - /dev/null
blob + cf14938c59291a1ae4a1e41fae96f4caf0e47d20 (mode 644)
--- /dev/null
+++ region.c
@@ -0,0 +1,255 @@
+/*
+ * Copyright (c) 2010-2014 Peter J. Philipp
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+#include "include.h"
+#include "dns.h"
+#include "db.h"
+
+u_int8_t find_region(struct sockaddr_storage *, int);
+in_addr_t getmask(int);
+int getmask6(int, struct sockaddr_in6 *);
+void init_region(void);
+int insert_region(char *, char *, u_int8_t);
+
+SLIST_HEAD(listhead, entry) head;
+
+static struct entry {
+ char name[INET6_ADDRSTRLEN];
+ int family;
+ struct sockaddr_storage hostmask;
+ struct sockaddr_storage netmask;
+ u_int8_t region;
+ u_int8_t prefixlen;
+ SLIST_ENTRY(entry) region_entry;
+} *n2, *np;
+
+
+static const char rcsid[] = "$Id: region.c,v 1.1.1.1 2014/11/14 08:09:04 pjp Exp $";
+
+/*
+ * INIT_REGION - initialize the region singly linked list
+ */
+
+void
+init_region(void)
+{
+ SLIST_INIT(&head);
+ return;
+}
+
+/*
+ * INSERT_REGION - insert particular address and prefix length and region
+ * into the
+ * singly linked list at "head", if the address contains
+ * a colon then it is assumed to be an IPv6 address.
+ * return -1 on error, 0 on successful insertion
+ */
+
+int
+insert_region(char *address, char *prefixlen, u_int8_t region)
+{
+ struct sockaddr_in *sin;
+ struct sockaddr_in6 *sin6;
+ int pnum;
+ int ret;
+
+ pnum = atoi(prefixlen);
+ n2 = malloc(sizeof(struct entry)); /* Insert after. */
+
+ if (strchr(address, ':') != NULL) {
+ n2->family = AF_INET6;
+ sin6 = (struct sockaddr_in6 *)&n2->hostmask;
+ if ((ret = inet_pton(AF_INET6, address, &sin6->sin6_addr.s6_addr)) != 1)
+ return (-1);
+ sin6->sin6_family = AF_INET6;
+ sin6 = (struct sockaddr_in6 *)&n2->netmask;
+ sin6->sin6_family = AF_INET6;
+ if (getmask6(pnum, sin6) < 0)
+ return(-1);
+ n2->region = region;
+ n2->prefixlen = pnum;
+ } else {
+
+ n2->family = AF_INET;
+ sin = (struct sockaddr_in *)&n2->hostmask;
+ sin->sin_family = AF_INET;
+ sin->sin_addr.s_addr = inet_addr(address);
+ sin = (struct sockaddr_in *)&n2->netmask;
+ sin->sin_family = AF_INET;
+ sin->sin_addr.s_addr = getmask(pnum);
+ n2->region = region;
+ n2->prefixlen = pnum;
+
+ }
+
+ SLIST_INSERT_HEAD(&head, n2, region_entry);
+
+ return (0);
+}
+
+/*
+ * FIND_REGION - walk the region list and find the correponding network with
+ * the highest prefix length, so that a /24 has more precedence
+ * than
+ * a /8 for example. IPv6 and IPv4 addresses are kept seperate
+ */
+
+u_int8_t
+find_region(struct sockaddr_storage *sst, int family)
+{
+ struct sockaddr_in *sin, *sin0;
+ struct sockaddr_in6 *sin6, *sin60, *sin61;
+ u_int32_t hostmask, netmask;
+ u_int32_t a;
+#ifdef __amd64
+ u_int64_t *hm[2], *nm[2], *a6[2];
+#else
+ u_int32_t *hm[4], *nm[4], *a6[4];
+#endif
+ u_int8_t region = 0xff;
+ u_int8_t prefixlen = 0;
+
+ SLIST_FOREACH(np, &head, region_entry) {
+ if (np->family == AF_INET) {
+ if (family != AF_INET)
+ continue;
+ sin = (struct sockaddr_in *)sst;
+ a = sin->sin_addr.s_addr;
+ sin = (struct sockaddr_in *)&np->hostmask;
+ sin0 = (struct sockaddr_in *)&np->netmask;
+ hostmask = sin->sin_addr.s_addr;
+ netmask = sin0->sin_addr.s_addr;
+ if ((hostmask & netmask) == (a & netmask)) {
+ if (np->prefixlen >= prefixlen) {
+ region = np->region;
+ prefixlen = np->prefixlen;
+ }
+ } /* if hostmask */
+ } else if (np->family == AF_INET6) {
+ if (family != AF_INET6)
+ continue;
+ sin6 = (struct sockaddr_in6 *)sst;
+ sin60 = (struct sockaddr_in6 *)&np->hostmask;
+ sin61 = (struct sockaddr_in6 *)&np->netmask;
+#ifdef __amd64
+ /*
+ * If this is on a 64 bit machine, we'll benefit
+ * by using 64 bit registers, this should make it
+ * a tad faster...
+ */
+ hm[0] = (u_int64_t *)&sin60->sin6_addr.s6_addr;
+ hm[1] = (hm[0] + 1);
+ nm[0] = (u_int64_t *)&sin61->sin6_addr.s6_addr;
+ nm[1] = (nm[0] + 1);
+ a6[0] = (u_int64_t *)&sin6->sin6_addr.s6_addr;
+ a6[1] = (a6[0] + 1);
+ if ( ((*hm[0] & *nm[0]) == (*a6[0] & *nm[0]))&&
+ ((*hm[1] & *nm[1]) == (*a6[1] & *nm[1]))) {
+#else
+ hm[0] = (u_int32_t *)&sin60->sin6_addr.s6_addr;
+ hm[1] = (hm[0] + 1); hm[2] = (hm[1] + 1);
+ hm[3] = (hm[2] + 1);
+ nm[0] = (u_int32_t *)&sin61->sin6_addr.s6_addr;
+ nm[1] = (nm[0] + 1); nm[2] = (nm[1] + 1);
+ nm[3] = (nm[2] + 1);
+ a6[0] = (u_int32_t *)&sin6->sin6_addr.s6_addr;
+ a6[1] = (a6[0] + 1); a6[2] = (a6[1] + 1);
+ a6[3] = (a6[2] + 1);
+ if ( ((*hm[0] & *nm[0]) == (*a6[0] & *nm[0]))&&
+ ((*hm[1] & *nm[1]) == (*a6[1] & *nm[1]))&&
+ ((*hm[2] & *nm[2]) == (*a6[2] & *nm[2]))&&
+ ((*hm[3] & *nm[3]) == (*a6[3] & *nm[3]))) {
+#endif
+
+ if (np->prefixlen >= prefixlen) {
+ region = np->region;
+ prefixlen = np->prefixlen;
+ }
+ } /* if ip6 address */
+
+ } /* if AF_INET6 */
+ } /* SLIST */
+
+ return (region);
+}
+
+/*
+ * GETMASK - get the v4 netmask given by prefix length, return netmask in
+ * network byte order, function can't fail unless prefix length
+ * supplied is > 32
+ */
+
+in_addr_t
+getmask(int prefixlen)
+{
+ in_addr_t ret = 0xffffffff;
+
+ /* I know it's cheating */
+ if (prefixlen > 31)
+ return (htonl(ret));
+
+ ret >>= prefixlen; /* 0x00ffffff */
+ ret = ~ret; /* 0xff000000 */
+
+ return (htonl(ret));
+}
+
+/*
+ * GETMASK6 - like getmask() but works on a supplied sockaddr_in6 instead of
+ * returning results as return address. Function cannot fail
+ * unless prefix length supplied is > 128. At which point a buffer
+ * overflow is possible.
+ */
+
+int
+getmask6(int prefixlen, struct sockaddr_in6 *sin6)
+{
+ int i, j;
+ u_int32_t *nm[4];
+
+ if (prefixlen > 128 || prefixlen < 0)
+ return (-1);
+
+ memset(&sin6->sin6_addr.s6_addr, 0xff, sizeof(sin6->sin6_addr.s6_addr));
+ nm[0] = (u_int32_t *)sin6->sin6_addr.s6_addr;
+ nm[1] = (nm[0] + 1); nm[2] = (nm[1] + 1);
+ nm[3] = (nm[2] + 1);
+
+ for (i = 0, j = 0; j < prefixlen; j++) {
+ if (*nm[i] == 1) {
+ *nm[i] = 0;
+ i++;
+ } else
+ *nm[i] >>= 1;
+ }
+ *nm[0] = htonl(~ *nm[0]);
+ *nm[1] = htonl(~ *nm[1]);
+ *nm[2] = htonl(~ *nm[2]);
+ *nm[3] = htonl(~ *nm[3]);
+
+ return (0);
+}
blob - /dev/null
blob + 086de393b0045fe81bc0bbb4acef677033be008f (mode 644)
--- /dev/null
+++ reply.c
@@ -0,0 +1,4328 @@
+/*
+ * Copyright (c) 2005-2014 Peter J. Philipp
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+#include "include.h"
+#include "dns.h"
+#include "db.h"
+
+/* prototypes */
+
+extern int additional_a(char *, int, struct domain *, char *, int, int, int *);
+extern int additional_aaaa(char *, int, struct domain *, char *, int, int, int *);
+extern int additional_mx(char *, int, struct domain *, char *, int, int, int *);
+extern int additional_ptr(char *, int, struct domain *, char *, int, int, int *);
+extern int additional_opt(struct question *, char *, int, int);
+extern struct question *build_fake_question(char *, int, u_int16_t);
+extern int compress_label(u_char *, int, int);
+extern void dolog(int, char *, ...);
+extern int free_question(struct question *);
+extern int lookup_zone(DB *, struct question *, struct domain *, int *, char *, int);
+extern void slave_shutdown(void);
+
+
+struct domain *Lookup_zone(DB *, char *, u_int16_t, u_int16_t, int);
+void collects_init(void);
+u_int16_t create_anyreply(struct sreply *, char *, int, int, int);
+u_short in_cksum(const u_short *, register int, int);
+int reply_a(struct sreply *, DB *);
+int reply_aaaa(struct sreply *, DB *);
+int reply_mx(struct sreply *, DB *);
+int reply_ns(struct sreply *, DB *);
+int reply_notimpl(struct sreply *);
+int reply_nxdomain(struct sreply *);
+int reply_noerror(struct sreply *);
+int reply_soa(struct sreply *);
+int reply_ptr(struct sreply *);
+int reply_txt(struct sreply *);
+int reply_spf(struct sreply *);
+int reply_srv(struct sreply *, DB *);
+int reply_naptr(struct sreply *, DB *);
+int reply_sshfp(struct sreply *);
+int reply_cname(struct sreply *);
+int reply_any(struct sreply *);
+int reply_refused(struct sreply *);
+int reply_fmterror(struct sreply *);
+int reply_raw2(int, char *, int, struct recurses *);
+int reply_raw6(int, char *, int, struct recurses *);
+void update_db(DB *, struct domain *);
+
+#ifdef __linux__
+static int udp_cksum(const struct iphdr *, const struct udphdr *, int);
+#else
+static int udp_cksum(const struct ip *, const struct udphdr *, int);
+#endif
+
+SLIST_HEAD(listhead, collects) collectshead;
+
+struct collects {
+ char *name;
+ u_int16_t namelen;
+ u_int16_t type;
+ struct domain *sd;
+ SLIST_ENTRY(collects) collect_entry;
+} *cn1, *cn2, *cnp;
+
+extern int debug, verbose;
+
+
+static const char rcsid[] = "$Id: reply.c,v 1.1.1.1 2014/11/14 08:09:04 pjp Exp $";
+
+/*
+ * REPLY_A() - replies a DNS question (*q) on socket (so)
+ *
+ */
+
+int
+reply_a(struct sreply *sreply, DB *db)
+{
+ char *reply = sreply->replybuf;
+ struct dns_header *odh;
+ u_int16_t outlen;
+ int a_count;
+ int mod, pos;
+ int ttlhack = 0;
+
+ struct answer {
+ char name[2];
+ u_int16_t type;
+ u_int16_t class;
+ u_int32_t ttl;
+ u_int16_t rdlength; /* 12 */
+ in_addr_t rdata; /* 16 */
+ } __attribute__((packed));
+
+ struct answer *answer;
+
+ int so = sreply->so;
+ char *buf = sreply->buf;
+ int len = sreply->len;
+ struct question *q = sreply->q;
+ struct sockaddr *sa = sreply->sa;
+ int salen = sreply->salen;
+ struct domain *sd = sreply->sd1;
+
+ u_int8_t region = sreply->region;
+ int istcp = sreply->istcp;
+ int replysize = 512;
+ int retlen = -1;
+
+ if (istcp) {
+ replysize = 65535;
+ }
+
+ if (q->edns0len > 512)
+ replysize = q->edns0len;
+
+ odh = (struct dns_header *)&reply[0];
+
+ outlen = sizeof(struct dns_header);
+
+ if (len > replysize) {
+ return (retlen);
+ }
+
+ memcpy(reply, buf, sizeof(struct dns_header) + q->hdr->namelen + 4);
+ memset((char *)&odh->query, 0, sizeof(u_int16_t));
+
+ outlen += (q->hdr->namelen + 4);
+
+ SET_DNS_REPLY(odh);
+ if (sreply->sr == NULL)
+ SET_DNS_AUTHORITATIVE(odh);
+ else
+ SET_DNS_RECURSION_AVAIL(odh);
+
+ HTONS(odh->query);
+
+ odh->question = htons(1);
+ odh->answer = htons(sd->a_count);
+ odh->nsrr = 0;
+ odh->additional = 0;
+
+ /* skip dns header, question name, qtype and qclass */
+ answer = (struct answer *)(&reply[0] + sizeof(struct dns_header) +
+ q->hdr->namelen + 4);
+
+ /* if we aren't a balance record our region code is 0xff so check */
+ if (sd->region[sd->a_ptr] != 0xff) {
+ ttlhack = 1;
+ }
+
+ a_count = 0;
+ pos = sd->a_ptr;
+ mod = sd->a_count;
+
+ do {
+ /*
+ * skip records that are not in the needed region
+ */
+ if (ttlhack && sd->region[pos % mod] != region) {
+ pos++;
+ continue;
+ }
+
+ /*
+ * answer->name is a pointer to the request (0xc00c)
+ */
+
+ answer->name[0] = 0xc0; /* 1 byte */
+ answer->name[1] = 0x0c; /* 2 bytes */
+ answer->type = q->hdr->qtype; /* 4 bytes */
+ answer->class = q->hdr->qclass; /* 6 bytes */
+ if (sreply->sr != NULL)
+ answer->ttl = htonl(sd->ttl - (time(NULL) - sd->created));
+ else
+ answer->ttl = htonl(sd->ttl); /* 10 bytes */
+
+ answer->rdlength = htons(sizeof(in_addr_t)); /* 12 bytes */
+
+ memcpy((char *)&answer->rdata, (char *)&sd->a[pos++ % mod],
+ sizeof(in_addr_t)); /* 16 bytes */
+
+ a_count++;
+ outlen += 16;
+
+ /* can we afford to write another header? if no truncate */
+ if (sd->a_count > 1 && outlen + 16 > replysize) {
+ NTOHS(odh->query);
+ SET_DNS_TRUNCATION(odh);
+ HTONS(odh->query);
+ goto out;
+ }
+
+
+ /* set new offset for answer */
+ answer = (struct answer *)&reply[outlen];
+ } while (a_count < RECORD_COUNT && --sd->a_count);
+
+ if (ttlhack) {
+ odh->answer = htons(a_count);
+ }
+
+ if (q->edns0len) {
+ /* tag on edns0 opt record */
+ odh->additional = htons(1);
+ outlen = additional_opt(q, reply, replysize, outlen);
+ }
+
+out:
+ if (sreply->sr != NULL) {
+ retlen = reply_raw2(so, reply, outlen, sreply->sr);
+ } else {
+ if (istcp) {
+ char *tmpbuf;
+ u_int16_t *plen;
+
+ tmpbuf = malloc(outlen + 2);
+ if (tmpbuf == NULL) {
+ dolog(LOG_INFO, "malloc: %s\n", strerror(errno));
+ }
+ plen = (u_int16_t *)tmpbuf;
+ *plen = htons(outlen);
+
+ memcpy(&tmpbuf[2], reply, outlen);
+
+ if ((retlen = send(so, tmpbuf, outlen + 2, 0)) < 0) {
+ dolog(LOG_INFO, "send: %s\n", strerror(errno));
+ }
+ free(tmpbuf);
+ } else {
+ if ((retlen = sendto(so, reply, outlen, 0, sa, salen)) < 0) {
+ dolog(LOG_INFO, "sendto: %s\n", strerror(errno));
+ }
+ }
+
+ } /* if (->sr) */
+ /*
+ * update a_ptr setting
+ */
+
+ sd->a_ptr = (sd->a_ptr + 1) % mod;
+ sd->a_count = mod; /* I know I know */
+ update_db(db, sd);
+
+ return (retlen);
+}
+
+/*
+ * REPLY_AAAA() - replies a DNS question (*q) on socket (so)
+ *
+ */
+
+int
+reply_aaaa(struct sreply *sreply, DB *db)
+{
+ char *reply = sreply->replybuf;
+ struct dns_header *odh;
+ u_int16_t outlen;
+ int aaaa_count;
+ int mod, pos;
+
+ struct answer {
+ char name[2];
+ u_int16_t type;
+ u_int16_t class;
+ u_int32_t ttl;
+ u_int16_t rdlength; /* 12 */
+ char rdata; /* 12 + 16 */
+ } __attribute__((packed));
+
+ struct answer *answer;
+
+ int so = sreply->so;
+ char *buf = sreply->buf;
+ int len = sreply->len;
+ struct question *q = sreply->q;
+ struct sockaddr *sa = sreply->sa;
+ int salen = sreply->salen;
+ struct domain *sd = sreply->sd1;
+ int istcp = sreply->istcp;
+ int replysize = 512;
+ int retlen = -1;
+
+ if (istcp) {
+ replysize = 65535;
+ }
+
+ if (q->edns0len > 512)
+ replysize = q->edns0len;
+
+
+ odh = (struct dns_header *)&reply[0];
+
+ outlen = sizeof(struct dns_header);
+
+ if (len > replysize) {
+ return (retlen);
+ }
+
+ memcpy(reply, buf, sizeof(struct dns_header) + q->hdr->namelen + 4);
+ memset((char *)&odh->query, 0, sizeof(u_int16_t));
+
+ outlen += (q->hdr->namelen + 4);
+
+ SET_DNS_REPLY(odh);
+ if (sreply->sr == NULL)
+ SET_DNS_AUTHORITATIVE(odh);
+ else
+ SET_DNS_RECURSION_AVAIL(odh);
+
+ HTONS(odh->query);
+
+ odh->question = htons(1);
+ odh->answer = htons(sd->aaaa_count);
+ odh->nsrr = 0;
+ odh->additional = 0;
+
+ /* skip dns header, question name, qtype and qclass */
+ answer = (struct answer *)(&reply[0] + sizeof(struct dns_header) +
+ q->hdr->namelen + 4);
+
+
+ aaaa_count = 0;
+ pos = sd->aaaa_ptr;
+ mod = sd->aaaa_count;
+
+ do {
+ answer->name[0] = 0xc0;
+ answer->name[1] = 0x0c;
+ answer->type = q->hdr->qtype;
+ answer->class = q->hdr->qclass;
+ if (sreply->sr != NULL)
+ answer->ttl = htonl(sd->ttl - (time(NULL) - sd->created));
+ else
+ answer->ttl = htonl(sd->ttl); /* 10 bytes */
+
+ answer->rdlength = htons(sizeof(struct in6_addr));
+
+ memcpy((char *)&answer->rdata, (char *)&sd->aaaa[pos++ % mod], sizeof(struct in6_addr));
+ outlen += 28;
+
+ /* can we afford to write another header? if no truncate */
+ if (sd->aaaa_count > 1 && outlen + 28 > replysize) {
+ NTOHS(odh->query);
+ SET_DNS_TRUNCATION(odh);
+ HTONS(odh->query);
+ goto out;
+ }
+
+ aaaa_count++;
+
+ /* set new offset for answer */
+ answer = (struct answer *)&reply[outlen];
+ } while (aaaa_count < RECORD_COUNT && --sd->aaaa_count);
+
+ if (q->edns0len) {
+ /* tag on edns0 opt record */
+ odh->additional = htons(1);
+ outlen = additional_opt(q, reply, replysize, outlen);
+ }
+
+out:
+ if (sreply->sr != NULL) {
+ retlen = reply_raw2(so, reply, outlen, sreply->sr);
+ } else {
+ if (istcp) {
+ char *tmpbuf;
+ u_int16_t *plen;
+
+ tmpbuf = malloc(outlen + 2);
+ if (tmpbuf == NULL) {
+ dolog(LOG_INFO, "malloc: %s\n", strerror(errno));
+ }
+ plen = (u_int16_t *)tmpbuf;
+ *plen = htons(outlen);
+
+ memcpy(&tmpbuf[2], reply, outlen);
+
+ if ((retlen = send(so, tmpbuf, outlen + 2, 0)) < 0) {
+ dolog(LOG_INFO, "send: %s\n", strerror(errno));
+ }
+ free(tmpbuf);
+ } else {
+ if ((retlen = sendto(so, reply, outlen, 0, sa, salen)) < 0) {
+ dolog(LOG_INFO, "sendto: %s\n", strerror(errno));
+ }
+ }
+ }
+
+ sd->aaaa_ptr = (sd->aaaa_ptr + 1) % mod;
+ sd->aaaa_count = mod;
+ update_db(db, sd);
+
+ return (retlen);
+}
+
+/*
+ * REPLY_MX() - replies a DNS question (*q) on socket (so)
+ *
+ */
+
+int
+reply_mx(struct sreply *sreply, DB *db)
+{
+ char *reply = sreply->replybuf;
+ struct dns_header *odh;
+ struct domain *sd0;
+ int mx_count;
+ u_int16_t *plen;
+ char *name;
+ u_int16_t outlen;
+ u_int16_t namelen;
+ int additional = 0;
+
+ struct answer {
+ char name[2];
+ u_int16_t type;
+ u_int16_t class;
+ u_int32_t ttl;
+ u_int16_t rdlength; /* 12 */
+ u_int16_t mx_priority;
+ char exchange;
+ } __attribute__((packed));
+
+ struct answer *answer;
+
+ int so = sreply->so;
+ char *buf = sreply->buf;
+ int len = sreply->len;
+ struct question *q = sreply->q;
+ struct sockaddr *sa = sreply->sa;
+ int salen = sreply->salen;
+ struct domain *sd = sreply->sd1;
+ int istcp = sreply->istcp;
+ int wildcard = sreply->wildcard;
+ int replysize = 512;
+ int retlen = -1;
+
+ if (istcp) {
+ replysize = 65535;
+ }
+
+ if (q->edns0len > 512)
+ replysize = q->edns0len;
+
+ odh = (struct dns_header *)&reply[0];
+
+ outlen = sizeof(struct dns_header);
+
+ if (len > replysize) {
+ return (retlen);
+ }
+
+ memcpy(reply, buf, sizeof(struct dns_header) + q->hdr->namelen + 4);
+ memset((char *)&odh->query, 0, sizeof(u_int16_t));
+
+ outlen += (q->hdr->namelen + 4);
+
+ SET_DNS_REPLY(odh);
+
+ if (sreply->sr == NULL) {
+ SET_DNS_AUTHORITATIVE(odh);
+ } else
+ SET_DNS_RECURSION_AVAIL(odh);
+
+ HTONS(odh->query);
+
+ odh->question = htons(1);
+ odh->answer = htons(sd->mx_count);
+ odh->nsrr = 0;
+ odh->additional = 0;
+
+ /* skip dns header, question name, qtype and qclass */
+ answer = (struct answer *)(&reply[0] + sizeof(struct dns_header) +
+ q->hdr->namelen + 4);
+
+ mx_count = 0;
+ do {
+ answer->name[0] = 0xc0;
+ answer->name[1] = 0x0c;
+ answer->type = q->hdr->qtype;
+ answer->class = q->hdr->qclass;
+ if (sreply->sr != NULL)
+ answer->ttl = htonl(sd->ttl - (time(NULL) - sd->created));
+ else
+ answer->ttl = htonl(sd->ttl);
+
+ answer->rdlength = htons(sizeof(u_int16_t) + sd->mx[mx_count].exchangelen);
+
+ answer->mx_priority = htons(sd->mx[mx_count].preference);
+ memcpy((char *)&answer->exchange, (char *)sd->mx[mx_count].exchange, sd->mx[mx_count].exchangelen);
+
+ name = sd->mx[mx_count].exchange;
+ namelen = sd->mx[mx_count].exchangelen;
+
+ sd0 = Lookup_zone(db, name, namelen, htons(DNS_TYPE_A), wildcard);
+ if (sd0 != NULL) {
+ cn1 = malloc(sizeof(struct collects));
+ if (cn1 != NULL) {
+ cn1->name = malloc(namelen);
+ if (cn1->name != NULL) {
+ memcpy(cn1->name, name, namelen);
+ cn1->namelen = namelen;
+ cn1->sd = sd0;
+ cn1->type = DNS_TYPE_A;
+
+ SLIST_INSERT_HEAD(&collectshead, cn1, collect_entry);
+ }
+ }
+ }
+ sd0 = Lookup_zone(db, name, namelen, htons(DNS_TYPE_AAAA), wildcard);
+ if (sd0 != NULL) {
+ cn1 = malloc(sizeof(struct collects));
+ if (cn1 != NULL) {
+ cn1->name = malloc(namelen);
+ if (cn1->name != NULL) {
+ memcpy(cn1->name, name, namelen);
+ cn1->namelen = namelen;
+ cn1->sd = sd0;
+ cn1->type = DNS_TYPE_AAAA;
+
+ SLIST_INSERT_HEAD(&collectshead, cn1, collect_entry);
+ }
+ }
+ }
+
+ outlen += (12 + 2 + sd->mx[mx_count].exchangelen);
+
+ /* can we afford to write another header? if no truncate */
+ if (sd->mx_count > 1 && (outlen + 12 + 2 + sd->mx[mx_count].exchangelen) > replysize) {
+ NTOHS(odh->query);
+ SET_DNS_TRUNCATION(odh);
+ HTONS(odh->query);
+ goto out;
+ }
+
+ /* set new offset for answer */
+ answer = (struct answer *)&reply[outlen];
+ } while (++mx_count < RECORD_COUNT && --sd->mx_count);
+
+ /* write additional */
+
+ SLIST_FOREACH(cnp, &collectshead, collect_entry) {
+ int addcount;
+ int tmplen;
+
+ switch (cnp->type) {
+ case DNS_TYPE_A:
+ tmplen = additional_a(cnp->name, cnp->namelen, cnp->sd, reply, replysize, outlen, &addcount);
+ additional += addcount;
+ break;
+ case DNS_TYPE_AAAA:
+ tmplen = additional_aaaa(cnp->name, cnp->namelen, cnp->sd, reply, replysize, outlen, &addcount);
+ additional += addcount;
+ break;
+ }
+
+ if (tmplen > 0) {
+ outlen = tmplen;
+ }
+ }
+
+ odh->additional = htons(additional);
+
+ while (!SLIST_EMPTY(&collectshead)) {
+ cn1 = SLIST_FIRST(&collectshead);
+ SLIST_REMOVE_HEAD(&collectshead, collect_entry);
+ free(cn1->name);
+ free(cn1->sd);
+ free(cn1);
+ }
+
+ if (q->edns0len) {
+ /* tag on edns0 opt record */
+ NTOHS(odh->additional);
+ odh->additional++;
+ HTONS(odh->additional);
+
+ outlen = additional_opt(q, reply, replysize, outlen);
+ }
+
+out:
+ if (sreply->sr != NULL) {
+ retlen = reply_raw2(so, reply, outlen, sreply->sr);
+ } else {