aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES376
-rw-r--r--FILES243
-rw-r--r--Makefile1106
-rw-r--r--README7
-rw-r--r--SYSDEPS10
-rw-r--r--TARGETS216
-rw-r--r--TINYDNS25
-rw-r--r--TODO12
-rw-r--r--VERSION1
-rw-r--r--alloc.c31
-rw-r--r--alloc.h8
-rw-r--r--alloc_re.c17
-rw-r--r--auto-str.c40
-rw-r--r--auto_home.h6
-rw-r--r--axfr-get.c373
-rw-r--r--axfrdns-conf.c71
-rw-r--r--axfrdns.c378
-rw-r--r--buffer.c10
-rw-r--r--buffer.h59
-rw-r--r--buffer_1.c5
-rw-r--r--buffer_2.c5
-rw-r--r--buffer_copy.c16
-rw-r--r--buffer_get.c67
-rw-r--r--buffer_put.c88
-rw-r--r--buffer_read.c7
-rw-r--r--buffer_write.c7
-rw-r--r--byte.h13
-rw-r--r--byte_chr.c20
-rw-r--r--byte_copy.c14
-rw-r--r--byte_cr.c16
-rw-r--r--byte_diff.c16
-rw-r--r--byte_zero.c13
-rw-r--r--cache.c207
-rw-r--r--cache.h12
-rw-r--r--cachetest.c32
-rw-r--r--case.h13
-rw-r--r--case_diffb.c18
-rw-r--r--case_diffs.c17
-rw-r--r--case_lowerb.c12
-rw-r--r--cdb.c136
-rw-r--r--cdb.h37
-rw-r--r--cdb_hash.c21
-rw-r--r--cdb_make.c152
-rw-r--r--cdb_make.h39
-rw-r--r--chkshsgr.c10
-rw-r--r--choose.sh18
-rw-r--r--conf-cc3
-rw-r--r--conf-home4
-rw-r--r--conf-ld3
-rw-r--r--dd.c36
-rw-r--r--dd.h6
-rw-r--r--direntry.h110
-rw-r--r--direntry.h210
-rw-r--r--dns.h84
-rw-r--r--dns_dfd.c69
-rw-r--r--dns_domain.c74
-rw-r--r--dns_dtda.c35
-rw-r--r--dns_ip.c75
-rw-r--r--dns_ipq.c71
-rw-r--r--dns_mx.c49
-rw-r--r--dns_name.c48
-rw-r--r--dns_nd.c24
-rw-r--r--dns_packet.c77
-rw-r--r--dns_random.c63
-rw-r--r--dns_rcip.c86
-rw-r--r--dns_rcrw.c131
-rw-r--r--dns_resolve.c29
-rw-r--r--dns_sortip.c20
-rw-r--r--dns_transmit.c366
-rw-r--r--dns_txt.c59
-rw-r--r--dnscache-conf.c169
-rw-r--r--dnscache.c447
-rw-r--r--dnsfilter.c214
-rw-r--r--dnsip.c40
-rw-r--r--dnsipq.c43
-rw-r--r--dnsmx.c64
-rw-r--r--dnsname.c34
-rw-r--r--dnsq.c98
-rw-r--r--dnsqr.c66
-rw-r--r--dnsroots.global13
-rw-r--r--dnstrace.c474
-rw-r--r--dnstracesort.sh51
-rw-r--r--dnstxt.c33
-rw-r--r--droproot.c33
-rw-r--r--droproot.h6
-rw-r--r--env.c15
-rw-r--r--env.h8
-rw-r--r--error.c123
-rw-r--r--error.h27
-rw-r--r--error_str.c267
-rw-r--r--exit.h6
-rw-r--r--find-systype.sh143
-rw-r--r--fmt.h25
-rw-r--r--fmt_ulong.c13
-rw-r--r--gen_alloc.h7
-rw-r--r--gen_allocdefs.h34
-rw-r--r--generic-conf.c99
-rw-r--r--generic-conf.h20
-rw-r--r--getln.c14
-rw-r--r--getln.h10
-rw-r--r--getln2.c24
-rw-r--r--hasdevtcp.h11
-rw-r--r--hasdevtcp.h22
-rw-r--r--hasshsgr.h11
-rw-r--r--hasshsgr.h22
-rw-r--r--hier.c42
-rw-r--r--install.c151
-rw-r--r--instcheck.c108
-rw-r--r--iopause.c76
-rw-r--r--iopause.h119
-rw-r--r--iopause.h218
-rw-r--r--ip4.h9
-rw-r--r--ip4_fmt.c18
-rw-r--r--ip4_scan.c19
-rw-r--r--log.c288
-rw-r--r--log.h36
-rw-r--r--ndelay.h7
-rw-r--r--ndelay_off.c12
-rw-r--r--ndelay_on.c12
-rw-r--r--okclient.c26
-rw-r--r--okclient.h6
-rw-r--r--open.h10
-rw-r--r--open_read.c6
-rw-r--r--open_trunc.c6
-rw-r--r--openreadclose.c16
-rw-r--r--openreadclose.h8
-rw-r--r--parsetype.c31
-rw-r--r--parsetype.h6
-rw-r--r--pickdns-conf.c66
-rw-r--r--pickdns-data.c230
-rw-r--r--pickdns.c101
-rw-r--r--printpacket.c90
-rw-r--r--printpacket.h8
-rw-r--r--printrecord.c115
-rw-r--r--printrecord.h9
-rw-r--r--prot.c19
-rw-r--r--prot.h7
-rw-r--r--qlog.c63
-rw-r--r--qlog.h8
-rw-r--r--query.c851
-rw-r--r--query.h32
-rw-r--r--random-ip.c80
-rw-r--r--rbldns-conf.c71
-rw-r--r--rbldns-data.c128
-rw-r--r--rbldns.c116
-rw-r--r--readclose.c21
-rw-r--r--readclose.h9
-rw-r--r--response.c121
-rw-r--r--response.h27
-rw-r--r--roots.c127
-rw-r--r--roots.h8
-rw-r--r--rts.exp1072
-rw-r--r--rts.sh1
-rw-r--r--rts.tests767
-rw-r--r--scan.h28
-rw-r--r--scan_ulong.c14
-rw-r--r--seek.h15
-rw-r--r--seek_set.c7
-rw-r--r--select.h110
-rw-r--r--select.h211
-rw-r--r--server.c116
-rw-r--r--sgetopt.c51
-rw-r--r--sgetopt.h21
-rw-r--r--socket.h22
-rw-r--r--socket_accept.c21
-rw-r--r--socket_bind.c33
-rw-r--r--socket_conn.c33
-rw-r--r--socket_listen.c10
-rw-r--r--socket_recv.c21
-rw-r--r--socket_send.c18
-rw-r--r--socket_tcp.c17
-rw-r--r--socket_udp.c17
-rw-r--r--str.h14
-rw-r--r--str_chr.c17
-rw-r--r--str_diff.c15
-rw-r--r--str_len.c14
-rw-r--r--str_rchr.c20
-rw-r--r--str_start.c13
-rw-r--r--stralloc.h29
-rw-r--r--stralloc_cat.c7
-rw-r--r--stralloc_catb.c12
-rw-r--r--stralloc_cats.c8
-rw-r--r--stralloc_copy.c7
-rw-r--r--stralloc_eady.c6
-rw-r--r--stralloc_num.c29
-rw-r--r--stralloc_opyb.c11
-rw-r--r--stralloc_opys.c8
-rw-r--r--stralloc_pend.c5
-rw-r--r--strerr.h78
-rw-r--r--strerr_die.c31
-rw-r--r--strerr_sys.c12
-rw-r--r--subgetopt.c65
-rw-r--r--subgetopt.h24
-rw-r--r--tai.h26
-rw-r--r--tai_add.c6
-rw-r--r--tai_now.c7
-rw-r--r--tai_pack.c16
-rw-r--r--tai_sub.c6
-rw-r--r--tai_uint.c6
-rw-r--r--tai_unpack.c16
-rw-r--r--taia.h34
-rw-r--r--taia_add.c18
-rw-r--r--taia_approx.c6
-rw-r--r--taia_frac.c6
-rw-r--r--taia_less.c12
-rw-r--r--taia_now.c12
-rw-r--r--taia_pack.c20
-rw-r--r--taia_sub.c21
-rw-r--r--taia_tai.c6
-rw-r--r--taia_uint.c10
-rw-r--r--tdlookup.c310
-rw-r--r--timeoutread.c28
-rw-r--r--timeoutread.h6
-rw-r--r--timeoutwrite.c28
-rw-r--r--timeoutwrite.h6
-rw-r--r--tinydns-conf.c98
-rw-r--r--tinydns-data.c456
-rw-r--r--tinydns-edit.c257
-rw-r--r--tinydns-get.c76
-rw-r--r--tinydns.c11
-rw-r--r--trycpp.c7
-rw-r--r--trydrent.c8
-rw-r--r--trylsock.c4
-rw-r--r--trypoll.c18
-rw-r--r--tryshsgr.c14
-rw-r--r--trysysel.c8
-rw-r--r--tryulong32.c11
-rw-r--r--tryulong64.c11
-rw-r--r--uint16.h11
-rw-r--r--uint16_pack.c13
-rw-r--r--uint16_unpack.c23
-rw-r--r--uint32.h111
-rw-r--r--uint32.h211
-rw-r--r--uint32_pack.c21
-rw-r--r--uint32_unpack.c31
-rw-r--r--uint64.h18
-rw-r--r--uint64.h28
-rw-r--r--utime.c24
-rw-r--r--walldns-conf.c58
-rw-r--r--walldns.c57
-rw-r--r--warn-auto.sh2
-rw-r--r--warn-shsgr3
-rw-r--r--x86cpuid.c38
243 files changed, 15929 insertions, 0 deletions
diff --git a/CHANGES b/CHANGES
new file mode 100644
index 0000000..2442a0c
--- /dev/null
+++ b/CHANGES
@@ -0,0 +1,376 @@
+19991129
+ version: dnscache 0.50, alpha. not released yet.
+19991223
+ version: dnscache 0.60, alpha.
+19991224
+ internal: dns_sortip() takes length argument.
+ api: dns_ip4() sorts output. currently this means just random.
+ api: added socket_bind4_reuse(). removed reuse from bind4().
+ ui: used bind4_reuse() for port 53, bind4() otherwise.
+ internal: eliminated some unused variables.
+ internal: prototypes in cdb.h, cdbmake.h, cdbmss.h.
+ internal: prototypes in case.h, env.h, fmt.h, scan.h, str.h.
+ internal: prototypes in stralloc.h.
+ internal: prototypes in error.h, strerr.h.
+ internal: prototypes in ndelay.h, open.h, seek.h.
+ internal: prototypes in sgetopt.h, subgetopt.h.
+ internal: prototypes in tai.h, taia.h.
+ internal: added some missing declarations.
+ bug: query.c checked void response_finishanswer() return code.
+ impact: cached responses were dropped on systems that
+ didn't follow the traditional C return behavior. fix:
+ obvious. tnx Giles Lean.
+ internal: switched from taia_addsec() to taia_uint().
+ api: switched to uint16 for socket_* port numbers.
+ internal: integrated uint16_pack() and friends.
+ ui: dnscache allows (recursive) queries from port 53.
+ ui: dnscache has 10-second idle timer on TCP read/write.
+ ui: dnscache limits itself to 20 concurrent TCP connections.
+ internal: moved dns_domain_fromdot() to separate file.
+ ui: supported \X, \1, \12, \123 in dns_domain_fromdot().
+ ui: supported \123 in dns_domain_todot_append().
+ version: dnscache 0.61, alpha.
+19991230
+ api: added dns_ip4_qualify().
+ api: added dns_resolvconfrewrite().
+ ui: added dnsipq.
+ api: dns_ip4() checks for (strings of) IP addresses.
+20000106
+ port: Solaris needs /dev/udp, not just /dev/tcp. impact:
+ dnscache and tinydns would stop immediately under
+ Solaris. fix: create /dev/udp in configure; and have
+ tinydns create socket before chroot. tnx Louis Theran.
+ internal: moved dns_name4_domain() to dns_nd.c.
+ ui: tinydns no longer excludes screwy queries from its log.
+ internal: moved respond() to tdlookup.c under new name.
+ ui: added tinydns-get.
+ ui: rewrote tinydns-data for new data format.
+ internal: expanded rts to cover tinydns-data using tinydns-get.
+20000107
+ ui: tinydns-data allows arbitrary case in domain names.
+ ui: dnscache supports preconfigured servers for non-root
+ domains.
+ ui: dnscache uses textual addresses for preconfigured servers.
+20000108
+ ui: tinydns-data excludes the additional and authority sections
+ if doing so helps meet the 512-byte UDP limit.
+ version: dnscache 0.70, beta.
+20000114
+ internal: in log.c, ulong() now prints a uint64.
+ internal: added cache_motion, query_count, log_stats.
+ ui: dnscache now prints queries/motion stats after typical
+ response packets.
+20000115
+ internal: added droproot.c. used in tinydns and dnscache.
+ internal: moved tinydns log() to qlog.c under new name.
+ ui: added walldns, configure-wd.
+ ui: configure-td now creates an empty root/data.
+ ui: added tinydns-edit.
+ ui: configure-td now sets up root/add-{ns,childns,host,mx}.
+20000116
+ ui: renamed configure* as *-conf.
+ ui: added axfrdns, axfrdns-conf.
+ ui: added axfr-get.
+ ui: dnscache-conf 10.* or 192.168.* now sets IPSEND=0.0.0.0.
+20000117
+ ui: added pickdns, pickdns-conf, pickdns-data.
+ version: dnscache 0.75, beta.
+20000118
+ internal: address* -> address_* in pickdns-data.c.
+ internal: start writing cdb earlier in pickdns-data.c.
+ internal: keep track of namelen in pickdns-data.c.
+ ui: added client-location variability to pickdns, pickdns-data.
+ ui: qlog logs short packets.
+ ui: qlog logs header if RD or other unusual bits are set.
+ ui: qlog logs non-Internet classes.
+ api: dns_domain_todot_append() -> dns_domain_todot_cat().
+ ui: axfr-get prints A records more nicely. tnx Russ Nelson.
+ ui: tinydns, pickdns, and walldns respond REFUSED to multiple
+ queries, strange classes, and strange header bits.
+ pickdns and walldns also respond REFUSED to unrecognized
+ domain names.
+20000120
+ ui: dns_resolvconfip() and dns_resolvconfrewrite() reread after
+ 10 minutes or 10000 uses.
+ ui: dns_resolvconfrewrite() treats "domain" like "search".
+ ui: dns_resolvconfrewrite() supports $LOCALDOMAIN.
+ ui: dns_resolvconfrewrite() supports gethostname().
+ api: dns_ip4_qualify() -> dns_ip4_qualify_rules(). new function
+ under the old name uses dns_resolvconfrewrite().
+ internal: cleaned up log.h.
+20000121
+ port: the gcc 2.95.2 -O2 optimizer can destroy parameters in a
+ function that calls another function with a long long
+ argument. impact: gcc 2.95.2 kills dnscache in
+ log_query(). fix: pass log_stats() inputs by reference,
+ and pass uint64's through a variable inside log.c.
+ internal: introduced x_* in axfr-get.
+ internal: more format verification in axfr-get.
+ ui: minimal Z support in tinydns-data.
+ ui: axfr-get prints Z lines.
+ ui: juggled axfr-get to support BIND 8's many-answers option.
+ ui: axfr-get prints common characters readably rather than in
+ octal. tnx Karsten Thygesen.
+ ui: install copies VERSION into .../etc.
+20000122
+ ui: dns_domain_todot_cat() now lowercases everything.
+ internal: split printrecord.c out of tinydns-get.
+ ui: added dnstrace.
+20000123
+ version: dnscache 0.76, beta.
+20000124
+ port: Solaris needs socket libraries for dnstrace. impact:
+ couldn't compile under Solaris. fix: use socket.lib.
+ tnx Karsten Thygesen.
+20000126
+ ui: dns_resolvconfip() supports $DNSCACHEIP.
+ ui: changed tinydns-get arg order.
+ internal: split printpacket.c out of tinydns-get.
+ ui: added dnsquery.
+ internal: merged case.a, fs.a, str.a, uint.a, ip4.a into byte.a.
+ internal: merged strerr.a into buffer.a.
+ internal: merged stralloc.a, getln.a into alloc.a.
+ internal: merged error.a, open.a, seek.a, ndelay.a, socket.a
+ into unix.a.
+ internal: used catulong in axfr-get.c.
+ ui: packet-parsing errors produce error_proto.
+ ui: axfr-get goes out of its way to reject wildcards.
+ internal: introduced generic-conf.c.
+ internal: upgraded timeoutread and timeoutwrite to iopause.
+20000127
+ ui: revamped details of the log formats.
+ ui: full Z support in tinydns-data.
+ ui: axfr-get accepts authority records and additional records.
+ ui: axfrdns tries to imitate BIND's handling of glue.
+ internal: expanded rts to try out the servers and *-conf.
+ ui: added rbldns.
+20000128
+ ui: increased MAXNS to 16 in query.h.
+20000129
+ version: DNScache 0.80, beta.
+20000205
+ ui: tinydns-data supports ^, for the benefit of people stuck
+ behind reverse CNAMEs. tnx Petr Novotny.
+20000206
+ ui: rbldns supports $.
+ ui: tinydns-data supports C. CNAME is overridden by NS; CNAME
+ overrides other records; no multiple CNAMEs.
+ ui: axfr-get supports C.
+ ui: axfr-get no longer rejects wildcards, except for NS.
+ internal: eliminated flagempty from tinydns-data.
+ internal: cleaned up delegation/NXDOMAIN loops in tinydns-data.
+ internal: reorganized packet_start interface in tinydns-data.
+ ui: tinydns-data supports BIND-style wildcards, except for NS.
+ version: DNScache 0.81, beta.
+20000207
+ ui: renamed dnsquery as dnsq, to eliminate name conflict with
+ Beecher dnsquery program. tnx Anand Buddhdev.
+20000208
+ ui: tinydns-edit supports add alias.
+ ui: tinydns-conf sets up root/add-alias.
+20000209
+ ui: dnscache-conf now sets IPSEND=0.0.0.0 in all cases.
+ ui: dnsq and dnstrace allow server names.
+ ui: dnsq and dnstrace allow type names.
+20000210
+ internal: response_tc() reduces len, simplifying udprespond().
+ ui: response_tc() now truncates immediately after query. this
+ should work around the Squid parsing bug reported by
+ Stuart Henderson.
+20000211
+ ui: tinydns-get allows type names.
+ ui: tinydns-data prints query name for >512 error. tnx Uwe Ohse.
+ version: DNScache 0.82, beta.
+20000212
+ ui: dns_transmit starts with loop 1 for recursive queries.
+ ui: dnscache tries to allocate 128K of incoming UDP buffer
+ space. tnx Jeremy Hansen.
+20000213
+ ui: tinydns tries to allocate 64K of incoming UDP buffer space.
+ internal: renamed response_*answer as response_r*.
+ internal: expanded response_rfinish to allow au and ar.
+ internal: expanded response_rstart to allow any ttl.
+ internal: rewrote tinydns-data, tinydns, tinydns-get, axfrdns
+ for compact new data.cdb format. a few ui effects: empty
+ nodes produce NXDOMAIN; wildcards affect empty nodes.
+ ui: response_addname() tries more extensive compression.
+20000215
+ ui: tinydns-edit takes fn arguments. tnx Jason R. Mastaler.
+20000218
+ internal: upgraded to new cdb library.
+ internal: added globalip().
+ ui: dnscache assigns IP addresses to dotted-decimal domain
+ names in canonical form.
+ internal: merged handling of C and ^ in tinydns-data.
+ port: FreeBSD 3.4-RELEASE poll() doesn't think that regular
+ files are readable. impact: under FreeBSD 3.4-RELEASE,
+ dnsfilter hangs waiting to read from regular files. tnx
+ Kenji Rikitake. fix: check for this bug in trypoll.c.
+20000219
+ ui: tinydns-data supports time-to-die.
+ ui: changed home directory from /usr/local/dnscache to
+ /usr/local; moved @ from home/etc to home/etc/dnscache.
+ internal: reorganized response.c.
+20000220
+ ui: tinydns-data allows omitted numeric fields in Z lines. tnx
+ Timothy L. Mayo.
+ version: DNScache 0.85, beta.
+20000222
+ ui: dns_transmit_get() pauses after server failure, if udploop
+ is 2.
+ internal: sped up name handling in response.c.
+20000223
+ ui: dnscache ignores some garbage in queries: AA, !RD, RA, Z,
+ RCODE, AN, AU, AR. (note that responses still say RD.)
+ this allows bogus queries from Ultrix versions of BIND.
+ internal: split dd.c out of query.c.
+ internal: split server.c out of tinydns.
+ internal: rewrote walldns, pickdns, rbldns to use server.c.
+ ui: server.c allows some garbage in queries: RA, Z, RCODE, AN,
+ AU, AR.
+ ui: axfrdns logs packets.
+ ui: walldns supports dotted-decimal IP addresses.
+20000224
+ ui: revamped qlog, again.
+ ui: better error message in dnscache-conf.c. tnx Chris Johnson.
+20000225
+ version: DNScache 0.90, gamma.
+20000226
+ internal: dnscache-conf sets up dnscache/run to avoid env. tnx
+ Chris Cappuccio.
+20000227
+ ui: tinydns-data uses server name instead of a.ns.domain for
+ automatic primary in SOA. tnx Frank Tegtmeyer.
+20000228
+ bug: axfrdns doesn't set aa bit in responses. impact: named-xfer
+ refuses to do zone transfers from axfrdns. fix: set aa
+ bit. tnx Peter Hunter.
+ ui: server.c now accepts packets from low ports. sigh.
+20000229
+ version: DNScache 0.91, gamma.
+20000307
+ internal: switched from slurp to openreadclose.
+20000308
+ ui: dns_transmit_get() pauses after recv() failure (such as
+ connection-refused), if udploop is 2.
+ ui: tinydns-data uses refresh 16384, retry 2048, expire 1048576.
+ tnx Frank Tegtmeyer.
+ version: DNScache 0.92, gamma.
+20000314
+ portability problem: the poll() emulation in RedHat 5.1 doesn't
+ clear revents when select() returns 0. tnx Petr Novotny.
+ impact: dns_transmit_get() never times out;
+ dns_resolve() busy-loops. fix: clear revents before
+ poll().
+20000315
+ ui: axfr-get grabs zones when serials drop. tnx Frank Tegtmeyer.
+ version: DNScache 0.93, gamma.
+20000323
+ ui: dns_rcip() accepts 0.0.0.0 in /etc/resolv.conf as 127.0.0.1.
+ tnx Chris Saia.
+20000325
+ version: DNScache 1.00.
+20000914
+ ui: axfr-get decodes PTR. tnx to various people.
+ ui: added dnsqr.
+20000915
+ portability problem: on some buggy kernels, accept() fails to
+ copy O_NONBLOCK. tnx Pavel Kankovsky. impact: with these
+ kernels, dnscache hangs if a TCP connection times out.
+ fix: ndelay_on() after accept().
+ ui: dnscache discards non-recursive queries.
+ ui: *-conf use envdir in */run.
+ internal: reorganized seed_addtime() calls in dnscache-conf.
+ ui: tinydns-data prohibits PTR in generic records.
+20000917
+ ui: dns_transmit_get() does not pause after most recv() errors.
+ still pauses after connection-refused when udploop is 2.
+ version: djbdns 1.01.
+20000922
+ portability problem: Linux distributions use bash as /bin/sh;
+ bash destroys $UID. dorks. impact: dnscache and axfrdns
+ run as root. fix: envdir, then sh, then envuidgid. but
+ /bin/sh really has to stop polluting the environment.
+20000923
+ ui: install /etc/dnsroots.global. dnscache-conf tries
+ dnsroots.local, then dnsroots.global.
+ ui: no longer install home/etc/dnscache.
+ version: djbdns 1.02.
+20001224
+ ui: new dnstrace output format.
+ ui: dnstrace shows all servers providing each ns/a line.
+ ui: added dnstracesort.
+20001225
+ internal: response_rstart() and response_cname() use uint32 ttl.
+ internal: added response_hidettl().
+ internal: cache_get() returns ttl.
+ internal: dnscache keeps track of ttls for aliases.
+ ui: dnscache returns ttl unless $HIDETTL is set.
+ ui: dnscache returns ttl 655360 for localhost et al.
+20001226
+ ui: dnscache supports $FORWARDONLY. tnx to several people for
+ the suggestion. tnx Dan Peterson for sample code.
+ ui: dnscache now logs sequential query numbers, not indices.
+ internal: revamped dnscache to separate udp from tcp.
+ ui: dnscache reports uactive, tactive separately.
+ ui: dnscache reports tcpopen/tcpclose by port and ip.
+ ui: dnscache artificially times out oldest UDP query if UDP
+ table is full, and oldest TCP connection if TCP table is
+ full.
+ ui: dnscache reports broken pipe when a TCP client sends FIN.
+20001228
+ ui: dnstrace supports dd.
+ ui: dnscache logs stats when it handles 1.0.0.127.in-addr.arpa.
+ ui: pickdns actively refuses queries for unknown types.
+ ui: pickdns responds to MX queries. tnx Mike Batchelor.
+ internal: added const at various places.
+ internal: removed some unused variables.
+ internal: used time_t in tai_now.c.
+ internal: used stdlib.h in alloc.c.
+ api: split dns_domain_suffix() into suffix(), suffixpos().
+ internal: switched to buffer_unix*.
+ internal: included unistd.h for various declarations.
+20010103
+ ui: increased maximum data size from 512 bytes to 32767 bytes in
+ tinydns, tinydns-get, axfrdns. allows big TXT records.
+ ui: dnsmx reformats name when it prints an artificial 0 MX.
+20010105
+ ui: increased MAXLEVEL to 5. the Internet is becoming more
+ glueless every day.
+20010106
+ version: djbdns 1.03.
+20010113
+ ui: increased MAXALIAS to 16.
+ ui: dnscache no longer caches SERVFAIL. per-ip is obviously the
+ way to go.
+ ui: tinydns et al. now respond FORMERR to non-Internet-class
+ queries.
+ ui: tdlookup now returns A records in a random order in the
+ answer section, and truncates the list after 8 records.
+ ui: tinydns-data skips lines starting -.
+20010114
+ internal: documented the tinydns data.cdb format.
+ ui: tinydns-data, tinydns, tinydns-get, axfrdns support client
+ differentiation.
+ ui: dnsqr aborts if it is given an extra argument.
+20010117
+ ui: dnstracesort removes duplicate lines.
+ ui: dnstracesort prints glue.
+ ui: dnstrace uses a ``start'' IP address for the root glue.
+20010121
+ version: djbdns 1.04.
+20010206
+ internal: response_query() takes a class argument.
+ internal: query_start() takes a class argument.
+ internal: packetquery() takes a class argument.
+ ui: tinydns et al., axfrdns, and dnscache repeat qclass * in
+ response to bogus * queries. tnx Mike Batchelor.
+ ui: axfrdns rejects queries for weird classes.
+ ui: axfrdns uses query ID instead of ID 0 in the series of AXFR
+ response messages between the SOAs, to support the AXFR
+ client in BIND 9.
+ ui: axfrdns sets AA in the series of AXFR response messages.
+20010211
+ ui: servers print starting message.
+ internal: some respond() declarations.
+ version: djbdns 1.05.
diff --git a/FILES b/FILES
new file mode 100644
index 0000000..7adf6d4
--- /dev/null
+++ b/FILES
@@ -0,0 +1,243 @@
+README
+TODO
+CHANGES
+VERSION
+FILES
+SYSDEPS
+TARGETS
+Makefile
+dnsroots.global
+TINYDNS
+conf-cc
+conf-ld
+conf-home
+rts.sh
+rts.tests
+rts.exp
+dnscache-conf.c
+hasdevtcp.h1
+hasdevtcp.h2
+dnscache.c
+server.c
+walldns-conf.c
+walldns.c
+rbldns-conf.c
+rbldns.c
+rbldns-data.c
+pickdns-conf.c
+pickdns.c
+pickdns-data.c
+dnsipq.c
+tinydns-conf.c
+tinydns.c
+tdlookup.c
+tinydns-get.c
+tinydns-data.c
+tinydns-edit.c
+axfrdns-conf.c
+axfrdns.c
+axfr-get.c
+dnsip.c
+dnsname.c
+dnstxt.c
+dnsmx.c
+dnsfilter.c
+random-ip.c
+dnsqr.c
+dnsq.c
+dnstrace.c
+dnstracesort.sh
+utime.c
+cachetest.c
+generic-conf.h
+generic-conf.c
+dd.h
+dd.c
+droproot.h
+droproot.c
+response.h
+response.c
+query.h
+query.c
+cache.h
+cache.c
+log.h
+log.c
+okclient.h
+okclient.c
+roots.h
+roots.c
+qlog.h
+qlog.c
+printrecord.h
+printrecord.c
+printpacket.h
+printpacket.c
+parsetype.h
+parsetype.c
+dns.h
+dns_dfd.c
+dns_domain.c
+dns_dtda.c
+dns_ip.c
+dns_ipq.c
+dns_mx.c
+dns_name.c
+dns_nd.c
+dns_packet.c
+dns_random.c
+dns_rcip.c
+dns_rcrw.c
+dns_resolve.c
+dns_sortip.c
+dns_transmit.c
+dns_txt.c
+choose.sh
+warn-auto.sh
+find-systype.sh
+trycpp.c
+x86cpuid.c
+alloc.c
+alloc.h
+alloc_re.c
+auto-str.c
+auto_home.h
+buffer.c
+buffer.h
+buffer_1.c
+buffer_2.c
+buffer_copy.c
+buffer_get.c
+buffer_put.c
+byte.h
+byte_chr.c
+byte_copy.c
+byte_cr.c
+byte_diff.c
+byte_zero.c
+case.h
+case_diffb.c
+case_diffs.c
+case_lowerb.c
+cdb.c
+cdb.h
+cdb_hash.c
+cdb_make.c
+cdb_make.h
+chkshsgr.c
+direntry.h1
+direntry.h2
+env.c
+env.h
+error.c
+error.h
+error_str.c
+exit.h
+fmt.h
+fmt_ulong.c
+gen_alloc.h
+gen_allocdefs.h
+getln.c
+getln.h
+getln2.c
+hasshsgr.h1
+hasshsgr.h2
+hier.c
+install.c
+instcheck.c
+iopause.c
+iopause.h1
+iopause.h2
+ip4.h
+ip4_fmt.c
+ip4_scan.c
+ndelay.h
+ndelay_off.c
+ndelay_on.c
+open.h
+open_read.c
+open_trunc.c
+openreadclose.c
+openreadclose.h
+prot.c
+prot.h
+readclose.c
+readclose.h
+scan.h
+scan_ulong.c
+seek.h
+seek_set.c
+select.h1
+select.h2
+sgetopt.c
+sgetopt.h
+socket.h
+socket_accept.c
+socket_bind.c
+socket_conn.c
+socket_listen.c
+socket_recv.c
+socket_send.c
+socket_tcp.c
+socket_udp.c
+str.h
+str_chr.c
+str_diff.c
+str_len.c
+str_rchr.c
+str_start.c
+stralloc.h
+stralloc_cat.c
+stralloc_catb.c
+stralloc_cats.c
+stralloc_copy.c
+stralloc_eady.c
+stralloc_num.c
+stralloc_opyb.c
+stralloc_opys.c
+stralloc_pend.c
+strerr.h
+strerr_die.c
+strerr_sys.c
+subgetopt.c
+subgetopt.h
+tai.h
+tai_add.c
+tai_now.c
+tai_pack.c
+tai_sub.c
+tai_uint.c
+tai_unpack.c
+taia.h
+taia_add.c
+taia_approx.c
+taia_frac.c
+taia_less.c
+taia_now.c
+taia_pack.c
+taia_sub.c
+taia_tai.c
+taia_uint.c
+timeoutread.c
+timeoutread.h
+timeoutwrite.c
+timeoutwrite.h
+trydrent.c
+trylsock.c
+trypoll.c
+tryshsgr.c
+trysysel.c
+tryulong32.c
+tryulong64.c
+uint16.h
+uint16_pack.c
+uint16_unpack.c
+uint32.h1
+uint32.h2
+uint32_pack.c
+uint32_unpack.c
+uint64.h1
+uint64.h2
+warn-shsgr
+buffer_read.c
+buffer_write.c
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..1429643
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,1106 @@
+# Don't edit Makefile! Use conf-* for configuration.
+
+SHELL=/bin/sh
+
+default: it
+
+alloc.a: \
+makelib alloc.o alloc_re.o getln.o getln2.o stralloc_cat.o \
+stralloc_catb.o stralloc_cats.o stralloc_copy.o stralloc_eady.o \
+stralloc_num.o stralloc_opyb.o stralloc_opys.o stralloc_pend.o
+ ./makelib alloc.a alloc.o alloc_re.o getln.o getln2.o \
+ stralloc_cat.o stralloc_catb.o stralloc_cats.o \
+ stralloc_copy.o stralloc_eady.o stralloc_num.o \
+ stralloc_opyb.o stralloc_opys.o stralloc_pend.o
+
+alloc.o: \
+compile alloc.c alloc.h error.h
+ ./compile alloc.c
+
+alloc_re.o: \
+compile alloc_re.c alloc.h byte.h
+ ./compile alloc_re.c
+
+auto-str: \
+load auto-str.o buffer.a unix.a byte.a
+ ./load auto-str buffer.a unix.a byte.a
+
+auto-str.o: \
+compile auto-str.c buffer.h exit.h
+ ./compile auto-str.c
+
+auto_home.c: \
+auto-str conf-home
+ ./auto-str auto_home `head -1 conf-home` > auto_home.c
+
+auto_home.o: \
+compile auto_home.c
+ ./compile auto_home.c
+
+axfr-get: \
+load axfr-get.o iopause.o timeoutread.o timeoutwrite.o dns.a libtai.a \
+alloc.a buffer.a unix.a byte.a
+ ./load axfr-get iopause.o timeoutread.o timeoutwrite.o \
+ dns.a libtai.a alloc.a buffer.a unix.a byte.a
+
+axfr-get.o: \
+compile axfr-get.c uint32.h uint16.h stralloc.h gen_alloc.h error.h \
+strerr.h getln.h buffer.h stralloc.h buffer.h exit.h open.h scan.h \
+byte.h str.h ip4.h timeoutread.h timeoutwrite.h dns.h stralloc.h \
+iopause.h taia.h tai.h uint64.h taia.h
+ ./compile axfr-get.c
+
+axfrdns: \
+load axfrdns.o iopause.o droproot.o tdlookup.o response.o qlog.o \
+prot.o timeoutread.o timeoutwrite.o dns.a libtai.a alloc.a env.a \
+cdb.a buffer.a unix.a byte.a
+ ./load axfrdns iopause.o droproot.o tdlookup.o response.o \
+ qlog.o prot.o timeoutread.o timeoutwrite.o dns.a libtai.a \
+ alloc.a env.a cdb.a buffer.a unix.a byte.a
+
+axfrdns-conf: \
+load axfrdns-conf.o generic-conf.o auto_home.o buffer.a unix.a byte.a
+ ./load axfrdns-conf generic-conf.o auto_home.o buffer.a \
+ unix.a byte.a
+
+axfrdns-conf.o: \
+compile axfrdns-conf.c strerr.h exit.h auto_home.h generic-conf.h \
+buffer.h
+ ./compile axfrdns-conf.c
+
+axfrdns.o: \
+compile axfrdns.c droproot.h exit.h env.h uint32.h uint16.h ip4.h \
+tai.h uint64.h buffer.h timeoutread.h timeoutwrite.h open.h seek.h \
+cdb.h uint32.h stralloc.h gen_alloc.h strerr.h str.h byte.h case.h \
+dns.h stralloc.h iopause.h taia.h tai.h taia.h scan.h qlog.h uint16.h \
+response.h uint32.h
+ ./compile axfrdns.c
+
+buffer.a: \
+makelib buffer.o buffer_1.o buffer_2.o buffer_copy.o buffer_get.o \
+buffer_put.o strerr_die.o strerr_sys.o
+ ./makelib buffer.a buffer.o buffer_1.o buffer_2.o \
+ buffer_copy.o buffer_get.o buffer_put.o strerr_die.o \
+ strerr_sys.o
+
+buffer.o: \
+compile buffer.c buffer.h
+ ./compile buffer.c
+
+buffer_1.o: \
+compile buffer_1.c buffer.h
+ ./compile buffer_1.c
+
+buffer_2.o: \
+compile buffer_2.c buffer.h
+ ./compile buffer_2.c
+
+buffer_copy.o: \
+compile buffer_copy.c buffer.h
+ ./compile buffer_copy.c
+
+buffer_get.o: \
+compile buffer_get.c buffer.h byte.h error.h
+ ./compile buffer_get.c
+
+buffer_put.o: \
+compile buffer_put.c buffer.h str.h byte.h error.h
+ ./compile buffer_put.c
+
+buffer_read.o: \
+compile buffer_read.c buffer.h
+ ./compile buffer_read.c
+
+buffer_write.o: \
+compile buffer_write.c buffer.h
+ ./compile buffer_write.c
+
+byte.a: \
+makelib byte_chr.o byte_copy.o byte_cr.o byte_diff.o byte_zero.o \
+case_diffb.o case_diffs.o case_lowerb.o fmt_ulong.o ip4_fmt.o \
+ip4_scan.o scan_ulong.o str_chr.o str_diff.o str_len.o str_rchr.o \
+str_start.o uint16_pack.o uint16_unpack.o uint32_pack.o \
+uint32_unpack.o
+ ./makelib byte.a byte_chr.o byte_copy.o byte_cr.o \
+ byte_diff.o byte_zero.o case_diffb.o case_diffs.o \
+ case_lowerb.o fmt_ulong.o ip4_fmt.o ip4_scan.o scan_ulong.o \
+ str_chr.o str_diff.o str_len.o str_rchr.o str_start.o \
+ uint16_pack.o uint16_unpack.o uint32_pack.o uint32_unpack.o
+
+byte_chr.o: \
+compile byte_chr.c byte.h
+ ./compile byte_chr.c
+
+byte_copy.o: \
+compile byte_copy.c byte.h
+ ./compile byte_copy.c
+
+byte_cr.o: \
+compile byte_cr.c byte.h
+ ./compile byte_cr.c
+
+byte_diff.o: \
+compile byte_diff.c byte.h
+ ./compile byte_diff.c
+
+byte_zero.o: \
+compile byte_zero.c byte.h
+ ./compile byte_zero.c
+
+cache.o: \
+compile cache.c alloc.h byte.h uint32.h exit.h tai.h uint64.h cache.h \
+uint32.h uint64.h
+ ./compile cache.c
+
+cachetest: \
+load cachetest.o cache.o libtai.a buffer.a alloc.a unix.a byte.a
+ ./load cachetest cache.o libtai.a buffer.a alloc.a unix.a \
+ byte.a
+
+cachetest.o: \
+compile cachetest.c buffer.h exit.h cache.h uint32.h uint64.h str.h
+ ./compile cachetest.c
+
+case_diffb.o: \
+compile case_diffb.c case.h
+ ./compile case_diffb.c
+
+case_diffs.o: \
+compile case_diffs.c case.h
+ ./compile case_diffs.c
+
+case_lowerb.o: \
+compile case_lowerb.c case.h
+ ./compile case_lowerb.c
+
+cdb.a: \
+makelib cdb.o cdb_hash.o cdb_make.o
+ ./makelib cdb.a cdb.o cdb_hash.o cdb_make.o
+
+cdb.o: \
+compile cdb.c error.h seek.h byte.h cdb.h uint32.h
+ ./compile cdb.c
+
+cdb_hash.o: \
+compile cdb_hash.c cdb.h uint32.h
+ ./compile cdb_hash.c
+
+cdb_make.o: \
+compile cdb_make.c seek.h error.h alloc.h cdb.h uint32.h cdb_make.h \
+buffer.h uint32.h
+ ./compile cdb_make.c
+
+check: \
+it instcheck
+ ./instcheck
+
+chkshsgr: \
+load chkshsgr.o
+ ./load chkshsgr
+
+chkshsgr.o: \
+compile chkshsgr.c exit.h
+ ./compile chkshsgr.c
+
+choose: \
+warn-auto.sh choose.sh conf-home
+ cat warn-auto.sh choose.sh \
+ | sed s}HOME}"`head -1 conf-home`"}g \
+ > choose
+ chmod 755 choose
+
+compile: \
+warn-auto.sh conf-cc
+ ( cat warn-auto.sh; \
+ echo exec "`head -1 conf-cc`" '-c $${1+"$$@"}' \
+ ) > compile
+ chmod 755 compile
+
+dd.o: \
+compile dd.c dns.h stralloc.h gen_alloc.h iopause.h taia.h tai.h \
+uint64.h taia.h dd.h
+ ./compile dd.c
+
+direntry.h: \
+choose compile trydrent.c direntry.h1 direntry.h2
+ ./choose c trydrent direntry.h1 direntry.h2 > direntry.h
+
+dns.a: \
+makelib dns_dfd.o dns_domain.o dns_dtda.o dns_ip.o dns_ipq.o dns_mx.o \
+dns_name.o dns_nd.o dns_packet.o dns_random.o dns_rcip.o dns_rcrw.o \
+dns_resolve.o dns_sortip.o dns_transmit.o dns_txt.o
+ ./makelib dns.a dns_dfd.o dns_domain.o dns_dtda.o dns_ip.o \
+ dns_ipq.o dns_mx.o dns_name.o dns_nd.o dns_packet.o \
+ dns_random.o dns_rcip.o dns_rcrw.o dns_resolve.o \
+ dns_sortip.o dns_transmit.o dns_txt.o
+
+dns_dfd.o: \
+compile dns_dfd.c error.h alloc.h byte.h dns.h stralloc.h gen_alloc.h \
+iopause.h taia.h tai.h uint64.h taia.h
+ ./compile dns_dfd.c
+
+dns_domain.o: \
+compile dns_domain.c error.h alloc.h case.h byte.h dns.h stralloc.h \
+gen_alloc.h iopause.h taia.h tai.h uint64.h taia.h
+ ./compile dns_domain.c
+
+dns_dtda.o: \
+compile dns_dtda.c stralloc.h gen_alloc.h dns.h stralloc.h iopause.h \
+taia.h tai.h uint64.h taia.h
+ ./compile dns_dtda.c
+
+dns_ip.o: \
+compile dns_ip.c stralloc.h gen_alloc.h uint16.h byte.h dns.h \
+stralloc.h iopause.h taia.h tai.h uint64.h taia.h
+ ./compile dns_ip.c
+
+dns_ipq.o: \
+compile dns_ipq.c stralloc.h gen_alloc.h case.h byte.h str.h dns.h \
+stralloc.h iopause.h taia.h tai.h uint64.h taia.h
+ ./compile dns_ipq.c
+
+dns_mx.o: \
+compile dns_mx.c stralloc.h gen_alloc.h byte.h uint16.h dns.h \
+stralloc.h iopause.h taia.h tai.h uint64.h taia.h
+ ./compile dns_mx.c
+
+dns_name.o: \
+compile dns_name.c stralloc.h gen_alloc.h uint16.h byte.h dns.h \
+stralloc.h iopause.h taia.h tai.h uint64.h taia.h
+ ./compile dns_name.c
+
+dns_nd.o: \
+compile dns_nd.c byte.h fmt.h dns.h stralloc.h gen_alloc.h iopause.h \
+taia.h tai.h uint64.h taia.h
+ ./compile dns_nd.c
+
+dns_packet.o: \
+compile dns_packet.c error.h dns.h stralloc.h gen_alloc.h iopause.h \
+taia.h tai.h uint64.h taia.h
+ ./compile dns_packet.c
+
+dns_random.o: \
+compile dns_random.c dns.h stralloc.h gen_alloc.h iopause.h taia.h \
+tai.h uint64.h taia.h taia.h uint32.h
+ ./compile dns_random.c
+
+dns_rcip.o: \
+compile dns_rcip.c taia.h tai.h uint64.h openreadclose.h stralloc.h \
+gen_alloc.h byte.h ip4.h env.h dns.h stralloc.h iopause.h taia.h \
+taia.h
+ ./compile dns_rcip.c
+
+dns_rcrw.o: \
+compile dns_rcrw.c taia.h tai.h uint64.h env.h byte.h str.h \
+openreadclose.h stralloc.h gen_alloc.h dns.h stralloc.h iopause.h \
+taia.h taia.h
+ ./compile dns_rcrw.c
+
+dns_resolve.o: \
+compile dns_resolve.c iopause.h taia.h tai.h uint64.h taia.h byte.h \
+dns.h stralloc.h gen_alloc.h iopause.h taia.h
+ ./compile dns_resolve.c
+
+dns_sortip.o: \
+compile dns_sortip.c byte.h dns.h stralloc.h gen_alloc.h iopause.h \
+taia.h tai.h uint64.h taia.h
+ ./compile dns_sortip.c
+
+dns_transmit.o: \
+compile dns_transmit.c socket.h uint16.h alloc.h error.h byte.h \
+uint16.h dns.h stralloc.h gen_alloc.h iopause.h taia.h tai.h uint64.h \
+taia.h
+ ./compile dns_transmit.c
+
+dns_txt.o: \
+compile dns_txt.c stralloc.h gen_alloc.h uint16.h byte.h dns.h \
+stralloc.h iopause.h taia.h tai.h uint64.h taia.h
+ ./compile dns_txt.c
+
+dnscache: \
+load dnscache.o droproot.o okclient.o log.o cache.o query.o \
+response.o dd.o roots.o iopause.o prot.o dns.a env.a alloc.a buffer.a \
+libtai.a unix.a byte.a socket.lib
+ ./load dnscache droproot.o okclient.o log.o cache.o \
+ query.o response.o dd.o roots.o iopause.o prot.o dns.a \
+ env.a alloc.a buffer.a libtai.a unix.a byte.a `cat \
+ socket.lib`
+
+dnscache-conf: \
+load dnscache-conf.o generic-conf.o auto_home.o libtai.a buffer.a \
+unix.a byte.a
+ ./load dnscache-conf generic-conf.o auto_home.o libtai.a \
+ buffer.a unix.a byte.a
+
+dnscache-conf.o: \
+compile dnscache-conf.c hasdevtcp.h strerr.h buffer.h uint32.h taia.h \
+tai.h uint64.h str.h open.h error.h exit.h auto_home.h generic-conf.h \
+buffer.h
+ ./compile dnscache-conf.c
+
+dnscache.o: \
+compile dnscache.c env.h exit.h scan.h strerr.h error.h ip4.h \
+uint16.h uint64.h socket.h uint16.h dns.h stralloc.h gen_alloc.h \
+iopause.h taia.h tai.h uint64.h taia.h taia.h byte.h roots.h fmt.h \
+iopause.h query.h dns.h uint32.h alloc.h response.h uint32.h cache.h \
+uint32.h uint64.h ndelay.h log.h uint64.h okclient.h droproot.h
+ ./compile dnscache.c
+
+dnsfilter: \
+load dnsfilter.o iopause.o getopt.a dns.a env.a libtai.a alloc.a \
+buffer.a unix.a byte.a socket.lib
+ ./load dnsfilter iopause.o getopt.a dns.a env.a libtai.a \
+ alloc.a buffer.a unix.a byte.a `cat socket.lib`
+
+dnsfilter.o: \
+compile dnsfilter.c strerr.h buffer.h stralloc.h gen_alloc.h alloc.h \
+dns.h stralloc.h iopause.h taia.h tai.h uint64.h taia.h ip4.h byte.h \
+scan.h taia.h sgetopt.h subgetopt.h iopause.h error.h exit.h
+ ./compile dnsfilter.c
+
+dnsip: \
+load dnsip.o iopause.o dns.a env.a libtai.a alloc.a buffer.a unix.a \
+byte.a socket.lib
+ ./load dnsip iopause.o dns.a env.a libtai.a alloc.a \
+ buffer.a unix.a byte.a `cat socket.lib`
+
+dnsip.o: \
+compile dnsip.c buffer.h exit.h strerr.h ip4.h dns.h stralloc.h \
+gen_alloc.h iopause.h taia.h tai.h uint64.h taia.h
+ ./compile dnsip.c
+
+dnsipq: \
+load dnsipq.o iopause.o dns.a env.a libtai.a alloc.a buffer.a unix.a \
+byte.a socket.lib
+ ./load dnsipq iopause.o dns.a env.a libtai.a alloc.a \
+ buffer.a unix.a byte.a `cat socket.lib`
+
+dnsipq.o: \
+compile dnsipq.c buffer.h exit.h strerr.h ip4.h dns.h stralloc.h \
+gen_alloc.h iopause.h taia.h tai.h uint64.h taia.h
+ ./compile dnsipq.c
+
+dnsmx: \
+load dnsmx.o iopause.o dns.a env.a libtai.a alloc.a buffer.a unix.a \
+byte.a socket.lib
+ ./load dnsmx iopause.o dns.a env.a libtai.a alloc.a \
+ buffer.a unix.a byte.a `cat socket.lib`
+
+dnsmx.o: \
+compile dnsmx.c buffer.h exit.h strerr.h uint16.h byte.h str.h fmt.h \
+dns.h stralloc.h gen_alloc.h iopause.h taia.h tai.h uint64.h taia.h
+ ./compile dnsmx.c
+
+dnsname: \
+load dnsname.o iopause.o dns.a env.a libtai.a alloc.a buffer.a unix.a \
+byte.a socket.lib
+ ./load dnsname iopause.o dns.a env.a libtai.a alloc.a \
+ buffer.a unix.a byte.a `cat socket.lib`
+
+dnsname.o: \
+compile dnsname.c buffer.h exit.h strerr.h ip4.h dns.h stralloc.h \
+gen_alloc.h iopause.h taia.h tai.h uint64.h taia.h
+ ./compile dnsname.c
+
+dnsq: \
+load dnsq.o iopause.o printrecord.o printpacket.o parsetype.o dns.a \
+env.a libtai.a buffer.a alloc.a unix.a byte.a socket.lib
+ ./load dnsq iopause.o printrecord.o printpacket.o \
+ parsetype.o dns.a env.a libtai.a buffer.a alloc.a unix.a \
+ byte.a `cat socket.lib`
+
+dnsq.o: \
+compile dnsq.c uint16.h strerr.h buffer.h scan.h str.h byte.h error.h \
+ip4.h iopause.h taia.h tai.h uint64.h printpacket.h stralloc.h \
+gen_alloc.h parsetype.h dns.h stralloc.h iopause.h taia.h
+ ./compile dnsq.c
+
+dnsqr: \
+load dnsqr.o iopause.o printrecord.o printpacket.o parsetype.o dns.a \
+env.a libtai.a buffer.a alloc.a unix.a byte.a socket.lib
+ ./load dnsqr iopause.o printrecord.o printpacket.o \
+ parsetype.o dns.a env.a libtai.a buffer.a alloc.a unix.a \
+ byte.a `cat socket.lib`
+
+dnsqr.o: \
+compile dnsqr.c uint16.h strerr.h buffer.h scan.h str.h byte.h \
+error.h iopause.h taia.h tai.h uint64.h printpacket.h stralloc.h \
+gen_alloc.h parsetype.h dns.h stralloc.h iopause.h taia.h
+ ./compile dnsqr.c
+
+dnstrace: \
+load dnstrace.o dd.o iopause.o printrecord.o parsetype.o dns.a env.a \
+libtai.a alloc.a buffer.a unix.a byte.a socket.lib
+ ./load dnstrace dd.o iopause.o printrecord.o parsetype.o \
+ dns.a env.a libtai.a alloc.a buffer.a unix.a byte.a `cat \
+ socket.lib`
+
+dnstrace.o: \
+compile dnstrace.c uint16.h uint32.h fmt.h str.h byte.h ip4.h \
+gen_alloc.h gen_allocdefs.h exit.h buffer.h stralloc.h gen_alloc.h \
+error.h strerr.h iopause.h taia.h tai.h uint64.h printrecord.h \
+stralloc.h alloc.h parsetype.h dd.h dns.h stralloc.h iopause.h taia.h
+ ./compile dnstrace.c
+
+dnstracesort: \
+warn-auto.sh dnstracesort.sh conf-home
+ cat warn-auto.sh dnstracesort.sh \
+ | sed s}HOME}"`head -1 conf-home`"}g \
+ > dnstracesort
+ chmod 755 dnstracesort
+
+dnstxt: \
+load dnstxt.o iopause.o dns.a env.a libtai.a alloc.a buffer.a unix.a \
+byte.a socket.lib
+ ./load dnstxt iopause.o dns.a env.a libtai.a alloc.a \
+ buffer.a unix.a byte.a `cat socket.lib`
+
+dnstxt.o: \
+compile dnstxt.c buffer.h exit.h strerr.h dns.h stralloc.h \
+gen_alloc.h iopause.h taia.h tai.h uint64.h taia.h
+ ./compile dnstxt.c
+
+droproot.o: \
+compile droproot.c env.h scan.h prot.h strerr.h
+ ./compile droproot.c
+
+env.a: \
+makelib env.o
+ ./makelib env.a env.o
+
+env.o: \
+compile env.c str.h env.h
+ ./compile env.c
+
+error.o: \
+compile error.c error.h
+ ./compile error.c
+
+error_str.o: \
+compile error_str.c error.h
+ ./compile error_str.c
+
+fmt_ulong.o: \
+compile fmt_ulong.c fmt.h
+ ./compile fmt_ulong.c
+
+generic-conf.o: \
+compile generic-conf.c strerr.h buffer.h open.h generic-conf.h \
+buffer.h
+ ./compile generic-conf.c
+
+getln.o: \
+compile getln.c byte.h getln.h buffer.h stralloc.h gen_alloc.h
+ ./compile getln.c
+
+getln2.o: \
+compile getln2.c byte.h getln.h buffer.h stralloc.h gen_alloc.h
+ ./compile getln2.c
+
+getopt.a: \
+makelib sgetopt.o subgetopt.o
+ ./makelib getopt.a sgetopt.o subgetopt.o
+
+hasdevtcp.h: \
+systype hasdevtcp.h1 hasdevtcp.h2
+ ( case "`cat systype`" in \
+ sunos-5.*) cat hasdevtcp.h2 ;; \
+ *) cat hasdevtcp.h1 ;; \
+ esac ) > hasdevtcp.h
+
+hasshsgr.h: \
+choose compile load tryshsgr.c hasshsgr.h1 hasshsgr.h2 chkshsgr \
+warn-shsgr
+ ./chkshsgr || ( cat warn-shsgr; exit 1 )
+ ./choose clr tryshsgr hasshsgr.h1 hasshsgr.h2 > hasshsgr.h
+
+hier.o: \
+compile hier.c auto_home.h
+ ./compile hier.c
+
+install: \
+load install.o hier.o auto_home.o buffer.a unix.a byte.a
+ ./load install hier.o auto_home.o buffer.a unix.a byte.a
+
+install.o: \
+compile install.c buffer.h strerr.h error.h open.h exit.h
+ ./compile install.c
+
+instcheck: \
+load instcheck.o hier.o auto_home.o buffer.a unix.a byte.a
+ ./load instcheck hier.o auto_home.o buffer.a unix.a byte.a
+
+instcheck.o: \
+compile instcheck.c strerr.h error.h exit.h
+ ./compile instcheck.c
+
+iopause.h: \
+choose compile load trypoll.c iopause.h1 iopause.h2
+ ./choose clr trypoll iopause.h1 iopause.h2 > iopause.h
+
+iopause.o: \
+compile iopause.c taia.h tai.h uint64.h select.h iopause.h taia.h
+ ./compile iopause.c
+
+ip4_fmt.o: \
+compile ip4_fmt.c fmt.h ip4.h
+ ./compile ip4_fmt.c
+
+ip4_scan.o: \
+compile ip4_scan.c scan.h ip4.h
+ ./compile ip4_scan.c
+
+it: \
+prog install instcheck
+
+libtai.a: \
+makelib tai_add.o tai_now.o tai_pack.o tai_sub.o tai_uint.o \
+tai_unpack.o taia_add.o taia_approx.o taia_frac.o taia_less.o \
+taia_now.o taia_pack.o taia_sub.o taia_tai.o taia_uint.o
+ ./makelib libtai.a tai_add.o tai_now.o tai_pack.o \
+ tai_sub.o tai_uint.o tai_unpack.o taia_add.o taia_approx.o \
+ taia_frac.o taia_less.o taia_now.o taia_pack.o taia_sub.o \
+ taia_tai.o taia_uint.o
+
+load: \
+warn-auto.sh conf-ld
+ ( cat warn-auto.sh; \
+ echo 'main="$$1"; shift'; \
+ echo exec "`head -1 conf-ld`" \
+ '-o "$$main" "$$main".o $${1+"$$@"}' \
+ ) > load
+ chmod 755 load
+
+log.o: \
+compile log.c buffer.h uint32.h uint16.h error.h byte.h log.h \
+uint64.h
+ ./compile log.c
+
+makelib: \
+warn-auto.sh systype
+ ( cat warn-auto.sh; \
+ echo 'main="$$1"; shift'; \
+ echo 'rm -f "$$main"'; \
+ echo 'ar cr "$$main" $${1+"$$@"}'; \
+ case "`cat systype`" in \
+ sunos-5.*) ;; \
+ unix_sv*) ;; \
+ irix64-*) ;; \
+ irix-*) ;; \
+ dgux-*) ;; \
+ hp-ux-*) ;; \
+ sco*) ;; \
+ *) echo 'ranlib "$$main"' ;; \
+ esac \
+ ) > makelib
+ chmod 755 makelib
+
+ndelay_off.o: \
+compile ndelay_off.c ndelay.h
+ ./compile ndelay_off.c
+
+ndelay_on.o: \
+compile ndelay_on.c ndelay.h
+ ./compile ndelay_on.c
+
+okclient.o: \
+compile okclient.c str.h ip4.h okclient.h
+ ./compile okclient.c
+
+open_read.o: \
+compile open_read.c open.h
+ ./compile open_read.c
+
+open_trunc.o: \
+compile open_trunc.c open.h
+ ./compile open_trunc.c
+
+openreadclose.o: \
+compile openreadclose.c error.h open.h readclose.h stralloc.h \
+gen_alloc.h openreadclose.h stralloc.h
+ ./compile openreadclose.c
+
+parsetype.o: \
+compile parsetype.c scan.h byte.h case.h dns.h stralloc.h gen_alloc.h \
+iopause.h taia.h tai.h uint64.h taia.h uint16.h parsetype.h
+ ./compile parsetype.c
+
+pickdns: \
+load pickdns.o server.o response.o droproot.o qlog.o prot.o dns.a \
+env.a libtai.a cdb.a alloc.a buffer.a unix.a byte.a socket.lib
+ ./load pickdns server.o response.o droproot.o qlog.o \
+ prot.o dns.a env.a libtai.a cdb.a alloc.a buffer.a unix.a \
+ byte.a `cat socket.lib`
+
+pickdns-conf: \
+load pickdns-conf.o generic-conf.o auto_home.o buffer.a unix.a byte.a
+ ./load pickdns-conf generic-conf.o auto_home.o buffer.a \
+ unix.a byte.a
+
+pickdns-conf.o: \
+compile pickdns-conf.c strerr.h exit.h auto_home.h generic-conf.h \
+buffer.h
+ ./compile pickdns-conf.c
+
+pickdns-data: \
+load pickdns-data.o cdb.a dns.a alloc.a buffer.a unix.a byte.a
+ ./load pickdns-data cdb.a dns.a alloc.a buffer.a unix.a \
+ byte.a
+
+pickdns-data.o: \
+compile pickdns-data.c buffer.h exit.h cdb_make.h buffer.h uint32.h \
+open.h alloc.h gen_allocdefs.h stralloc.h gen_alloc.h getln.h \
+buffer.h stralloc.h case.h strerr.h str.h byte.h scan.h fmt.h ip4.h \
+dns.h stralloc.h iopause.h taia.h tai.h uint64.h taia.h
+ ./compile pickdns-data.c
+
+pickdns.o: \
+compile pickdns.c byte.h case.h dns.h stralloc.h gen_alloc.h \
+iopause.h taia.h tai.h uint64.h taia.h open.h cdb.h uint32.h \
+response.h uint32.h
+ ./compile pickdns.c
+
+printpacket.o: \
+compile printpacket.c uint16.h uint32.h error.h byte.h dns.h \
+stralloc.h gen_alloc.h iopause.h taia.h tai.h uint64.h taia.h \
+printrecord.h stralloc.h printpacket.h stralloc.h
+ ./compile printpacket.c
+
+printrecord.o: \
+compile printrecord.c uint16.h uint32.h error.h byte.h dns.h \
+stralloc.h gen_alloc.h iopause.h taia.h tai.h uint64.h taia.h \
+printrecord.h stralloc.h
+ ./compile printrecord.c
+
+prog: \
+dnscache-conf dnscache walldns-conf walldns rbldns-conf rbldns \
+rbldns-data pickdns-conf pickdns pickdns-data tinydns-conf tinydns \
+tinydns-data tinydns-get tinydns-edit axfr-get axfrdns-conf axfrdns \
+dnsip dnsipq dnsname dnstxt dnsmx dnsfilter random-ip dnsqr dnsq \
+dnstrace dnstracesort cachetest utime rts
+
+prot.o: \
+compile prot.c hasshsgr.h prot.h
+ ./compile prot.c
+
+qlog.o: \
+compile qlog.c buffer.h qlog.h uint16.h
+ ./compile qlog.c
+
+query.o: \
+compile query.c error.h roots.h log.h uint64.h case.h cache.h \
+uint32.h uint64.h byte.h dns.h stralloc.h gen_alloc.h iopause.h \
+taia.h tai.h uint64.h taia.h uint64.h uint32.h uint16.h dd.h alloc.h \
+response.h uint32.h query.h dns.h uint32.h
+ ./compile query.c
+
+random-ip: \
+load random-ip.o dns.a libtai.a buffer.a unix.a byte.a
+ ./load random-ip dns.a libtai.a buffer.a unix.a byte.a
+
+random-ip.o: \
+compile random-ip.c buffer.h exit.h fmt.h scan.h dns.h stralloc.h \
+gen_alloc.h iopause.h taia.h tai.h uint64.h taia.h
+ ./compile random-ip.c
+
+rbldns: \
+load rbldns.o server.o response.o dd.o droproot.o qlog.o prot.o dns.a \
+env.a libtai.a cdb.a alloc.a buffer.a unix.a byte.a socket.lib
+ ./load rbldns server.o response.o dd.o droproot.o qlog.o \
+ prot.o dns.a env.a libtai.a cdb.a alloc.a buffer.a unix.a \
+ byte.a `cat socket.lib`
+
+rbldns-conf: \
+load rbldns-conf.o generic-conf.o auto_home.o buffer.a unix.a byte.a
+ ./load rbldns-conf generic-conf.o auto_home.o buffer.a \
+ unix.a byte.a
+
+rbldns-conf.o: \
+compile rbldns-conf.c strerr.h exit.h auto_home.h generic-conf.h \
+buffer.h
+ ./compile rbldns-conf.c
+
+rbldns-data: \
+load rbldns-data.o cdb.a alloc.a buffer.a unix.a byte.a
+ ./load rbldns-data cdb.a alloc.a buffer.a unix.a byte.a
+
+rbldns-data.o: \
+compile rbldns-data.c buffer.h exit.h cdb_make.h buffer.h uint32.h \
+open.h stralloc.h gen_alloc.h getln.h buffer.h stralloc.h strerr.h \
+byte.h scan.h fmt.h ip4.h
+ ./compile rbldns-data.c
+
+rbldns.o: \
+compile rbldns.c str.h byte.h ip4.h open.h env.h cdb.h uint32.h dns.h \
+stralloc.h gen_alloc.h iopause.h taia.h tai.h uint64.h taia.h dd.h \
+strerr.h response.h uint32.h
+ ./compile rbldns.c
+
+readclose.o: \
+compile readclose.c error.h readclose.h stralloc.h gen_alloc.h
+ ./compile readclose.c
+
+response.o: \
+compile response.c dns.h stralloc.h gen_alloc.h iopause.h taia.h \
+tai.h uint64.h taia.h byte.h uint16.h response.h uint32.h
+ ./compile response.c
+
+roots.o: \
+compile roots.c open.h error.h str.h byte.h error.h direntry.h ip4.h \
+dns.h stralloc.h gen_alloc.h iopause.h taia.h tai.h uint64.h taia.h \
+openreadclose.h stralloc.h roots.h
+ ./compile roots.c
+
+rts: \
+warn-auto.sh rts.sh conf-home
+ cat warn-auto.sh rts.sh \
+ | sed s}HOME}"`head -1 conf-home`"}g \
+ > rts
+ chmod 755 rts
+
+scan_ulong.o: \
+compile scan_ulong.c scan.h
+ ./compile scan_ulong.c
+
+seek_set.o: \
+compile seek_set.c seek.h
+ ./compile seek_set.c
+
+select.h: \
+choose compile trysysel.c select.h1 select.h2
+ ./choose c trysysel select.h1 select.h2 > select.h
+
+server.o: \
+compile server.c byte.h case.h env.h buffer.h strerr.h ip4.h uint16.h \
+ndelay.h socket.h uint16.h droproot.h qlog.h uint16.h response.h \
+uint32.h dns.h stralloc.h gen_alloc.h iopause.h taia.h tai.h uint64.h \
+taia.h
+ ./compile server.c
+
+setup: \
+it install
+ ./install
+
+sgetopt.o: \
+compile sgetopt.c buffer.h sgetopt.h subgetopt.h subgetopt.h
+ ./compile sgetopt.c
+
+socket.lib: \
+trylsock.c compile load
+ ( ( ./compile trylsock.c && \
+ ./load trylsock -lsocket -lnsl ) >/dev/null 2>&1 \
+ && echo -lsocket -lnsl || exit 0 ) > socket.lib
+ rm -f trylsock.o trylsock
+
+socket_accept.o: \
+compile socket_accept.c byte.h socket.h uint16.h
+ ./compile socket_accept.c
+
+socket_bind.o: \
+compile socket_bind.c byte.h socket.h uint16.h
+ ./compile socket_bind.c
+
+socket_conn.o: \
+compile socket_conn.c byte.h socket.h uint16.h
+ ./compile socket_conn.c
+
+socket_listen.o: \
+compile socket_listen.c socket.h uint16.h
+ ./compile socket_listen.c
+
+socket_recv.o: \
+compile socket_recv.c byte.h socket.h uint16.h
+ ./compile socket_recv.c
+
+socket_send.o: \
+compile socket_send.c byte.h socket.h uint16.h
+ ./compile socket_send.c
+
+socket_tcp.o: \
+compile socket_tcp.c ndelay.h socket.h uint16.h
+ ./compile socket_tcp.c
+
+socket_udp.o: \
+compile socket_udp.c ndelay.h socket.h uint16.h
+ ./compile socket_udp.c
+
+str_chr.o: \
+compile str_chr.c str.h
+ ./compile str_chr.c
+
+str_diff.o: \
+compile str_diff.c str.h
+ ./compile str_diff.c
+
+str_len.o: \
+compile str_len.c str.h
+ ./compile str_len.c
+
+str_rchr.o: \
+compile str_rchr.c str.h
+ ./compile str_rchr.c
+
+str_start.o: \
+compile str_start.c str.h
+ ./compile str_start.c
+
+stralloc_cat.o: \
+compile stralloc_cat.c byte.h stralloc.h gen_alloc.h
+ ./compile stralloc_cat.c
+
+stralloc_catb.o: \
+compile stralloc_catb.c stralloc.h gen_alloc.h byte.h
+ ./compile stralloc_catb.c
+
+stralloc_cats.o: \
+compile stralloc_cats.c byte.h str.h stralloc.h gen_alloc.h
+ ./compile stralloc_cats.c
+
+stralloc_copy.o: \
+compile stralloc_copy.c byte.h stralloc.h gen_alloc.h
+ ./compile stralloc_copy.c
+
+stralloc_eady.o: \
+compile stralloc_eady.c alloc.h stralloc.h gen_alloc.h \
+gen_allocdefs.h
+ ./compile stralloc_eady.c
+
+stralloc_num.o: \
+compile stralloc_num.c stralloc.h gen_alloc.h
+ ./compile stralloc_num.c
+
+stralloc_opyb.o: \
+compile stralloc_opyb.c stralloc.h gen_alloc.h byte.h
+ ./compile stralloc_opyb.c
+
+stralloc_opys.o: \
+compile stralloc_opys.c byte.h str.h stralloc.h gen_alloc.h
+ ./compile stralloc_opys.c
+
+stralloc_pend.o: \
+compile stralloc_pend.c alloc.h stralloc.h gen_alloc.h \
+gen_allocdefs.h
+ ./compile stralloc_pend.c
+
+strerr_die.o: \
+compile strerr_die.c buffer.h exit.h strerr.h
+ ./compile strerr_die.c
+
+strerr_sys.o: \
+compile strerr_sys.c error.h strerr.h
+ ./compile strerr_sys.c
+
+subgetopt.o: \
+compile subgetopt.c subgetopt.h
+ ./compile subgetopt.c
+
+systype: \
+find-systype.sh conf-cc conf-ld trycpp.c x86cpuid.c
+ ( cat warn-auto.sh; \
+ echo CC=\'`head -1 conf-cc`\'; \
+ echo LD=\'`head -1 conf-ld`\'; \
+ cat find-systype.sh; \
+ ) | sh > systype
+
+tai_add.o: \
+compile tai_add.c tai.h uint64.h
+ ./compile tai_add.c
+
+tai_now.o: \
+compile tai_now.c tai.h uint64.h
+ ./compile tai_now.c
+
+tai_pack.o: \
+compile tai_pack.c tai.h uint64.h
+ ./compile tai_pack.c
+
+tai_sub.o: \
+compile tai_sub.c tai.h uint64.h
+ ./compile tai_sub.c
+
+tai_uint.o: \
+compile tai_uint.c tai.h uint64.h
+ ./compile tai_uint.c
+
+tai_unpack.o: \
+compile tai_unpack.c tai.h uint64.h
+ ./compile tai_unpack.c
+
+taia_add.o: \
+compile taia_add.c taia.h tai.h uint64.h
+ ./compile taia_add.c
+
+taia_approx.o: \
+compile taia_approx.c taia.h tai.h uint64.h
+ ./compile taia_approx.c
+
+taia_frac.o: \
+compile taia_frac.c taia.h tai.h uint64.h
+ ./compile taia_frac.c
+
+taia_less.o: \
+compile taia_less.c taia.h tai.h uint64.h
+ ./compile taia_less.c
+
+taia_now.o: \
+compile taia_now.c taia.h tai.h uint64.h
+ ./compile taia_now.c
+
+taia_pack.o: \
+compile taia_pack.c taia.h tai.h uint64.h
+ ./compile taia_pack.c
+
+taia_sub.o: \
+compile taia_sub.c taia.h tai.h uint64.h
+ ./compile taia_sub.c
+
+taia_tai.o: \
+compile taia_tai.c taia.h tai.h uint64.h
+ ./compile taia_tai.c
+
+taia_uint.o: \
+compile taia_uint.c taia.h tai.h uint64.h
+ ./compile taia_uint.c
+
+tdlookup.o: \
+compile tdlookup.c uint16.h open.h tai.h uint64.h cdb.h uint32.h \
+byte.h case.h dns.h stralloc.h gen_alloc.h iopause.h taia.h tai.h \
+taia.h seek.h response.h uint32.h
+ ./compile tdlookup.c
+
+timeoutread.o: \
+compile timeoutread.c error.h iopause.h taia.h tai.h uint64.h \
+timeoutread.h
+ ./compile timeoutread.c
+
+timeoutwrite.o: \
+compile timeoutwrite.c error.h iopause.h taia.h tai.h uint64.h \
+timeoutwrite.h
+ ./compile timeoutwrite.c
+
+tinydns: \
+load tinydns.o server.o droproot.o tdlookup.o response.o qlog.o \
+prot.o dns.a libtai.a env.a cdb.a alloc.a buffer.a unix.a byte.a \
+socket.lib
+ ./load tinydns server.o droproot.o tdlookup.o response.o \
+ qlog.o prot.o dns.a libtai.a env.a cdb.a alloc.a buffer.a \
+ unix.a byte.a `cat socket.lib`
+
+tinydns-conf: \
+load tinydns-conf.o generic-conf.o auto_home.o buffer.a unix.a byte.a
+ ./load tinydns-conf generic-conf.o auto_home.o buffer.a \
+ unix.a byte.a
+
+tinydns-conf.o: \
+compile tinydns-conf.c strerr.h exit.h auto_home.h generic-conf.h \
+buffer.h
+ ./compile tinydns-conf.c
+
+tinydns-data: \
+load tinydns-data.o cdb.a dns.a alloc.a buffer.a unix.a byte.a
+ ./load tinydns-data cdb.a dns.a alloc.a buffer.a unix.a \
+ byte.a
+
+tinydns-data.o: \
+compile tinydns-data.c uint16.h uint32.h str.h byte.h fmt.h ip4.h \
+exit.h case.h scan.h buffer.h strerr.h getln.h buffer.h stralloc.h \
+gen_alloc.h cdb_make.h buffer.h uint32.h stralloc.h open.h dns.h \
+stralloc.h iopause.h taia.h tai.h uint64.h taia.h
+ ./compile tinydns-data.c
+
+tinydns-edit: \
+load tinydns-edit.o dns.a alloc.a buffer.a unix.a byte.a
+ ./load tinydns-edit dns.a alloc.a buffer.a unix.a byte.a
+
+tinydns-edit.o: \
+compile tinydns-edit.c stralloc.h gen_alloc.h buffer.h exit.h open.h \
+getln.h buffer.h stralloc.h strerr.h scan.h byte.h str.h fmt.h ip4.h \
+dns.h stralloc.h iopause.h taia.h tai.h uint64.h taia.h
+ ./compile tinydns-edit.c
+
+tinydns-get: \
+load tinydns-get.o tdlookup.o response.o printpacket.o printrecord.o \
+parsetype.o dns.a libtai.a cdb.a buffer.a alloc.a unix.a byte.a
+ ./load tinydns-get tdlookup.o response.o printpacket.o \
+ printrecord.o parsetype.o dns.a libtai.a cdb.a buffer.a \
+ alloc.a unix.a byte.a
+
+tinydns-get.o: \
+compile tinydns-get.c str.h byte.h scan.h exit.h stralloc.h \
+gen_alloc.h buffer.h strerr.h uint16.h response.h uint32.h case.h \
+printpacket.h stralloc.h parsetype.h ip4.h dns.h stralloc.h iopause.h \
+taia.h tai.h uint64.h taia.h
+ ./compile tinydns-get.c
+
+tinydns.o: \
+compile tinydns.c dns.h stralloc.h gen_alloc.h iopause.h taia.h tai.h \
+uint64.h taia.h
+ ./compile tinydns.c
+
+uint16_pack.o: \
+compile uint16_pack.c uint16.h
+ ./compile uint16_pack.c
+
+uint16_unpack.o: \
+compile uint16_unpack.c uint16.h
+ ./compile uint16_unpack.c
+
+uint32.h: \
+tryulong32.c compile load uint32.h1 uint32.h2
+ ( ( ./compile tryulong32.c && ./load tryulong32 && \
+ ./tryulong32 ) >/dev/null 2>&1 \
+ && cat uint32.h2 || cat uint32.h1 ) > uint32.h
+ rm -f tryulong32.o tryulong32
+
+uint32_pack.o: \
+compile uint32_pack.c uint32.h
+ ./compile uint32_pack.c
+
+uint32_unpack.o: \
+compile uint32_unpack.c uint32.h
+ ./compile uint32_unpack.c
+
+uint64.h: \
+choose compile load tryulong64.c uint64.h1 uint64.h2
+ ./choose clr tryulong64 uint64.h1 uint64.h2 > uint64.h
+
+unix.a: \
+makelib buffer_read.o buffer_write.o error.o error_str.o ndelay_off.o \
+ndelay_on.o open_read.o open_trunc.o openreadclose.o readclose.o \
+seek_set.o socket_accept.o socket_bind.o socket_conn.o \
+socket_listen.o socket_recv.o socket_send.o socket_tcp.o socket_udp.o
+ ./makelib unix.a buffer_read.o buffer_write.o error.o \
+ error_str.o ndelay_off.o ndelay_on.o open_read.o \
+ open_trunc.o openreadclose.o readclose.o seek_set.o \
+ socket_accept.o socket_bind.o socket_conn.o socket_listen.o \
+ socket_recv.o socket_send.o socket_tcp.o socket_udp.o
+
+utime: \
+load utime.o byte.a
+ ./load utime byte.a
+
+utime.o: \
+compile utime.c scan.h exit.h
+ ./compile utime.c
+
+walldns: \
+load walldns.o server.o response.o droproot.o qlog.o prot.o dd.o \
+dns.a env.a cdb.a alloc.a buffer.a unix.a byte.a socket.lib
+ ./load walldns server.o response.o droproot.o qlog.o \
+ prot.o dd.o dns.a env.a cdb.a alloc.a buffer.a unix.a \
+ byte.a `cat socket.lib`
+
+walldns-conf: \
+load walldns-conf.o generic-conf.o auto_home.o buffer.a unix.a byte.a
+ ./load walldns-conf generic-conf.o auto_home.o buffer.a \
+ unix.a byte.a
+
+walldns-conf.o: \
+compile walldns-conf.c strerr.h exit.h auto_home.h generic-conf.h \
+buffer.h
+ ./compile walldns-conf.c
+
+walldns.o: \
+compile walldns.c byte.h dns.h stralloc.h gen_alloc.h iopause.h \
+taia.h tai.h uint64.h taia.h dd.h response.h uint32.h
+ ./compile walldns.c
diff --git a/README b/README
new file mode 100644
index 0000000..a9617eb
--- /dev/null
+++ b/README
@@ -0,0 +1,7 @@
+djbdns 1.05
+20010211
+Copyright 2001
+D. J. Bernstein
+
+djbdns home page: http://cr.yp.to/djbdns.html
+Installation instructions: http://cr.yp.to/djbdns/install.html
diff --git a/SYSDEPS b/SYSDEPS
new file mode 100644
index 0000000..060bbc0
--- /dev/null
+++ b/SYSDEPS
@@ -0,0 +1,10 @@
+VERSION
+systype
+uint32.h
+uint64.h
+select.h
+iopause.h
+direntry.h
+hasshsgr.h
+hasdevtcp.h
+socket.lib
diff --git a/TARGETS b/TARGETS
new file mode 100644
index 0000000..2490b1a
--- /dev/null
+++ b/TARGETS
@@ -0,0 +1,216 @@
+load
+compile
+systype
+hasdevtcp.h
+uint32.h
+choose
+uint64.h
+dnscache-conf.o
+generic-conf.o
+auto-str.o
+makelib
+buffer.o
+buffer_1.o
+buffer_2.o
+buffer_copy.o
+buffer_get.o
+buffer_put.o
+strerr_die.o
+strerr_sys.o
+buffer.a
+buffer_read.o
+buffer_write.o
+error.o
+error_str.o
+ndelay_off.o
+ndelay_on.o
+open_read.o
+open_trunc.o
+openreadclose.o
+readclose.o
+seek_set.o
+socket_accept.o
+socket_bind.o
+socket_conn.o
+socket_listen.o
+socket_recv.o
+socket_send.o
+socket_tcp.o
+socket_udp.o
+unix.a
+byte_chr.o
+byte_copy.o
+byte_cr.o
+byte_diff.o
+byte_zero.o
+case_diffb.o
+case_diffs.o
+case_lowerb.o
+fmt_ulong.o
+ip4_fmt.o
+ip4_scan.o
+scan_ulong.o
+str_chr.o
+str_diff.o
+str_len.o
+str_rchr.o
+str_start.o
+uint16_pack.o
+uint16_unpack.o
+uint32_pack.o
+uint32_unpack.o
+byte.a
+auto-str
+auto_home.c
+auto_home.o
+tai_add.o
+tai_now.o
+tai_pack.o
+tai_sub.o
+tai_uint.o
+tai_unpack.o
+taia_add.o
+taia_approx.o
+taia_frac.o
+taia_less.o
+taia_now.o
+taia_pack.o
+taia_sub.o
+taia_tai.o
+taia_uint.o
+libtai.a
+dnscache-conf
+iopause.h
+dnscache.o
+droproot.o
+okclient.o
+log.o
+cache.o
+query.o
+response.o
+dd.o
+direntry.h
+roots.o
+select.h
+iopause.o
+chkshsgr.o
+chkshsgr
+hasshsgr.h
+prot.o
+dns_dfd.o
+dns_domain.o
+dns_dtda.o
+dns_ip.o
+dns_ipq.o
+dns_mx.o
+dns_name.o
+dns_nd.o
+dns_packet.o
+dns_random.o
+dns_rcip.o
+dns_rcrw.o
+dns_resolve.o
+dns_sortip.o
+dns_transmit.o
+dns_txt.o
+dns.a
+env.o
+env.a
+alloc.o
+alloc_re.o
+getln.o
+getln2.o
+stralloc_cat.o
+stralloc_catb.o
+stralloc_cats.o
+stralloc_copy.o
+stralloc_eady.o
+stralloc_num.o
+stralloc_opyb.o
+stralloc_opys.o
+stralloc_pend.o
+alloc.a
+socket.lib
+dnscache
+walldns-conf.o
+walldns-conf
+walldns.o
+server.o
+qlog.o
+cdb.o
+cdb_hash.o
+cdb_make.o
+cdb.a
+walldns
+rbldns-conf.o
+rbldns-conf
+rbldns.o
+rbldns
+rbldns-data.o
+rbldns-data
+pickdns-conf.o
+pickdns-conf
+pickdns.o
+pickdns
+pickdns-data.o
+pickdns-data
+tinydns-conf.o
+tinydns-conf
+tinydns.o
+tdlookup.o
+tinydns
+tinydns-data.o
+tinydns-data
+tinydns-get.o
+printpacket.o
+printrecord.o
+parsetype.o
+tinydns-get
+tinydns-edit.o
+tinydns-edit
+axfr-get.o
+timeoutread.o
+timeoutwrite.o
+axfr-get
+axfrdns-conf.o
+axfrdns-conf
+axfrdns.o
+axfrdns
+dnsip.o
+dnsip
+dnsipq.o
+dnsipq
+dnsname.o
+dnsname
+dnstxt.o
+dnstxt
+dnsmx.o
+dnsmx
+dnsfilter.o
+sgetopt.o
+subgetopt.o
+getopt.a
+dnsfilter
+random-ip.o
+random-ip
+dnsqr.o
+dnsqr
+dnsq.o
+dnsq
+dnstrace.o
+dnstrace
+dnstracesort
+cachetest.o
+cachetest
+utime.o
+utime
+rts
+prog
+install.o
+hier.o
+install
+instcheck.o
+instcheck
+it
+setup
+check
diff --git a/TINYDNS b/TINYDNS
new file mode 100644
index 0000000..2e41a9a
--- /dev/null
+++ b/TINYDNS
@@ -0,0 +1,25 @@
+The tinydns data.cdb format is subject to change. If you want to write
+code that relies on something here, let me know.
+
+Keys starting with the two bytes \000\045 are locations. The rest of the
+key is an IP prefix, normally between 0 and 4 bytes long. The data is a
+2-byte location.
+
+Other keys are owner names for DNS records. The data begins with a
+header in the following format:
+
+ * a 2-byte type;
+ * either \075, or \076 with a 2-byte location;
+ * a 4-byte TTL;
+ * an 8-byte timestamp.
+
+(Exception: Wildcard records replace \075 with \052 and \076 with \053;
+also, the owner name omits the wildcard.) The data continues in a
+type-specific format:
+
+ * SOA: first domain name, second domain name, 20-byte miscellany.
+ * NS or PTR or CNAME: domain name.
+ * MX: 2-byte preference, domain name.
+ * Other types: no special structure.
+
+Domain names, types, and numbers are in DNS packet format.
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..8931a9a
--- /dev/null
+++ b/TODO
@@ -0,0 +1,12 @@
+end-to-end nym-based security
+link-level security
+
+try to get the root authorities to set up a secure, usable NS-list system
+have dnscache-conf keep track of copies of dnsroots.global
+incorporate automatic NS-list upgrades
+
+consider dead-server table in dnscache or in kernel
+
+IPv6 lookups
+maybe reverse IPv6 lookups; what a mess
+DNS over IPv6
diff --git a/VERSION b/VERSION
new file mode 100644
index 0000000..835d795
--- /dev/null
+++ b/VERSION
@@ -0,0 +1 @@
+djbdns 1.05
diff --git a/alloc.c b/alloc.c
new file mode 100644
index 0000000..b94e23a
--- /dev/null
+++ b/alloc.c
@@ -0,0 +1,31 @@
+#include <stdlib.h>
+#include "alloc.h"
+#include "error.h"
+
+#define ALIGNMENT 16 /* XXX: assuming that this alignment is enough */
+#define SPACE 2048 /* must be multiple of ALIGNMENT */
+
+typedef union { char irrelevant[ALIGNMENT]; double d; } aligned;
+static aligned realspace[SPACE / ALIGNMENT];
+#define space ((char *) realspace)
+static unsigned int avail = SPACE; /* multiple of ALIGNMENT; 0<=avail<=SPACE */
+
+/*@null@*//*@out@*/char *alloc(n)
+unsigned int n;
+{
+ char *x;
+ n = ALIGNMENT + n - (n & (ALIGNMENT - 1)); /* XXX: could overflow */
+ if (n <= avail) { avail -= n; return space + avail; }
+ x = malloc(n);
+ if (!x) errno = error_nomem;
+ return x;
+}
+
+void alloc_free(x)
+char *x;
+{
+ if (x >= space)
+ if (x < space + SPACE)
+ return; /* XXX: assuming that pointers are flat */
+ free(x);
+}
diff --git a/alloc.h b/alloc.h
new file mode 100644
index 0000000..1b1d893
--- /dev/null
+++ b/alloc.h
@@ -0,0 +1,8 @@
+#ifndef ALLOC_H
+#define ALLOC_H
+
+extern /*@null@*//*@out@*/char *alloc();
+extern void alloc_free();
+extern int alloc_re();
+
+#endif
diff --git a/alloc_re.c b/alloc_re.c
new file mode 100644
index 0000000..feb8b49
--- /dev/null
+++ b/alloc_re.c
@@ -0,0 +1,17 @@
+#include "alloc.h"
+#include "byte.h"
+
+int alloc_re(x,m,n)
+char **x;
+unsigned int m;
+unsigned int n;
+{
+ char *y;
+
+ y = alloc(n);
+ if (!y) return 0;
+ byte_copy(y,m,*x);
+ alloc_free(*x);
+ *x = y;
+ return 1;
+}
diff --git a/auto-str.c b/auto-str.c
new file mode 100644
index 0000000..374af92
--- /dev/null
+++ b/auto-str.c
@@ -0,0 +1,40 @@
+#include "buffer.h"
+#include "exit.h"
+
+char bspace[256];
+buffer b = BUFFER_INIT(buffer_unixwrite,1,bspace,sizeof bspace);
+
+void puts(const char *s)
+{
+ if (buffer_puts(&b,s) == -1) _exit(111);
+}
+
+int main(int argc,char **argv)
+{
+ char *name;
+ char *value;
+ unsigned char ch;
+ char octal[4];
+
+ name = argv[1];
+ if (!name) _exit(100);
+ value = argv[2];
+ if (!value) _exit(100);
+
+ puts("const char ");
+ puts(name);
+ puts("[] = \"\\\n");
+
+ while (ch = *value++) {
+ puts("\\");
+ octal[3] = 0;
+ octal[2] = '0' + (ch & 7); ch >>= 3;
+ octal[1] = '0' + (ch & 7); ch >>= 3;
+ octal[0] = '0' + (ch & 7);
+ puts(octal);
+ }
+
+ puts("\\\n\";\n");
+ if (buffer_flush(&b) == -1) _exit(111);
+ _exit(0);
+}
diff --git a/auto_home.h b/auto_home.h
new file mode 100644
index 0000000..bd59284
--- /dev/null
+++ b/auto_home.h
@@ -0,0 +1,6 @@
+#ifndef AUTO_HOME_H
+#define AUTO_HOME_H
+
+extern const char auto_home[];
+
+#endif
diff --git a/axfr-get.c b/axfr-get.c
new file mode 100644
index 0000000..75db627
--- /dev/null
+++ b/axfr-get.c
@@ -0,0 +1,373 @@
+#include <stdio.h>
+#include <unistd.h>
+#include "uint32.h"
+#include "uint16.h"
+#include "stralloc.h"
+#include "error.h"
+#include "strerr.h"
+#include "getln.h"
+#include "buffer.h"
+#include "exit.h"
+#include "open.h"
+#include "scan.h"
+#include "byte.h"
+#include "str.h"
+#include "ip4.h"
+#include "timeoutread.h"
+#include "timeoutwrite.h"
+#include "dns.h"
+
+#define FATAL "axfr-get: fatal: "
+
+void die_usage(void)
+{
+ strerr_die1x(100,"axfr-get: usage: axfr-get zone fn fn.tmp");
+}
+void die_generate(void)
+{
+ strerr_die2sys(111,FATAL,"unable to generate AXFR query: ");
+}
+void die_parse(void)
+{
+ strerr_die2sys(111,FATAL,"unable to parse AXFR results: ");
+}
+unsigned int x_copy(char *buf,unsigned int len,unsigned int pos,char *out,unsigned int outlen)
+{
+ pos = dns_packet_copy(buf,len,pos,out,outlen);
+ if (!pos) die_parse();
+ return pos;
+}
+unsigned int x_getname(char *buf,unsigned int len,unsigned int pos,char **out)
+{
+ pos = dns_packet_getname(buf,len,pos,out);
+ if (!pos) die_parse();
+ return pos;
+}
+unsigned int x_skipname(char *buf,unsigned int len,unsigned int pos)
+{
+ pos = dns_packet_skipname(buf,len,pos);
+ if (!pos) die_parse();
+ return pos;
+}
+
+static char *zone;
+unsigned int zonelen;
+char *fn;
+char *fntmp;
+
+void die_netread(void)
+{
+ strerr_die2sys(111,FATAL,"unable to read from network: ");
+}
+void die_netwrite(void)
+{
+ strerr_die2sys(111,FATAL,"unable to write to network: ");
+}
+void die_read(void)
+{
+ strerr_die4sys(111,FATAL,"unable to read ",fn,": ");
+}
+void die_write(void)
+{
+ strerr_die4sys(111,FATAL,"unable to write ",fntmp,": ");
+}
+
+int saferead(int fd,char *buf,unsigned int len)
+{
+ int r;
+ r = timeoutread(60,fd,buf,len);
+ if (r == 0) { errno = error_proto; die_parse(); }
+ if (r <= 0) die_netread();
+ return r;
+}
+int safewrite(int fd,char *buf,unsigned int len)
+{
+ int r;
+ r = timeoutwrite(60,fd,buf,len);
+ if (r <= 0) die_netwrite();
+ return r;
+}
+char netreadspace[1024];
+buffer netread = BUFFER_INIT(saferead,6,netreadspace,sizeof netreadspace);
+char netwritespace[1024];
+buffer netwrite = BUFFER_INIT(safewrite,7,netwritespace,sizeof netwritespace);
+
+void netget(char *buf,unsigned int len)
+{
+ int r;
+
+ while (len > 0) {
+ r = buffer_get(&netread,buf,len);
+ buf += r; len -= r;
+ }
+}
+
+int fd;
+buffer b;
+char bspace[1024];
+
+void put(char *buf,unsigned int len)
+{
+ if (buffer_put(&b,buf,len) == -1) die_write();
+}
+
+int printable(char ch)
+{
+ if (ch == '.') return 1;
+ if ((ch >= 'a') && (ch <= 'z')) return 1;
+ if ((ch >= '0') && (ch <= '9')) return 1;
+ if ((ch >= 'A') && (ch <= 'Z')) return 1;
+ if (ch == '-') return 1;
+ return 0;
+}
+
+static char *d1;
+static char *d2;
+static char *d3;
+
+stralloc line;
+int match;
+
+int numsoa;
+
+unsigned int doit(char *buf,unsigned int len,unsigned int pos)
+{
+ char data[20];
+ uint32 ttl;
+ uint16 dlen;
+ uint16 typenum;
+ uint32 u32;
+ int i;
+
+ pos = x_getname(buf,len,pos,&d1);
+ pos = x_copy(buf,len,pos,data,10);
+ uint16_unpack_big(data,&typenum);
+ uint32_unpack_big(data + 4,&ttl);
+ uint16_unpack_big(data + 8,&dlen);
+ if (len - pos < dlen) { errno = error_proto; return 0; }
+ len = pos + dlen;
+
+ if (!dns_domain_suffix(d1,zone)) return len;
+ if (byte_diff(data + 2,2,DNS_C_IN)) return len;
+
+ if (byte_equal(data,2,DNS_T_SOA)) {
+ if (++numsoa >= 2) return len;
+ pos = x_getname(buf,len,pos,&d2);
+ pos = x_getname(buf,len,pos,&d3);
+ x_copy(buf,len,pos,data,20);
+ uint32_unpack_big(data,&u32);
+ if (!stralloc_copys(&line,"#")) return 0;
+ if (!stralloc_catulong0(&line,u32,0)) return 0;
+ if (!stralloc_cats(&line," auto axfr-get\n")) return 0;
+ if (!stralloc_cats(&line,"Z")) return 0;
+ if (!dns_domain_todot_cat(&line,d1)) return 0;
+ if (!stralloc_cats(&line,":")) return 0;
+ if (!dns_domain_todot_cat(&line,d2)) return 0;
+ if (!stralloc_cats(&line,".:")) return 0;
+ if (!dns_domain_todot_cat(&line,d3)) return 0;
+ if (!stralloc_cats(&line,".")) return 0;
+ for (i = 0;i < 5;++i) {
+ uint32_unpack_big(data + 4 * i,&u32);
+ if (!stralloc_cats(&line,":")) return 0;
+ if (!stralloc_catulong0(&line,u32,0)) return 0;
+ }
+ }
+ else if (byte_equal(data,2,DNS_T_NS)) {
+ if (!stralloc_copys(&line,"&")) return 0;
+ if (byte_equal(d1,2,"\1*")) { errno = error_proto; return 0; }
+ if (!dns_domain_todot_cat(&line,d1)) return 0;
+ if (!stralloc_cats(&line,"::")) return 0;
+ x_getname(buf,len,pos,&d1);
+ if (!dns_domain_todot_cat(&line,d1)) return 0;
+ if (!stralloc_cats(&line,".")) return 0;
+ }
+ else if (byte_equal(data,2,DNS_T_CNAME)) {
+ if (!stralloc_copys(&line,"C")) return 0;
+ if (!dns_domain_todot_cat(&line,d1)) return 0;
+ if (!stralloc_cats(&line,":")) return 0;
+ x_getname(buf,len,pos,&d1);
+ if (!dns_domain_todot_cat(&line,d1)) return 0;
+ if (!stralloc_cats(&line,".")) return 0;
+ }
+ else if (byte_equal(data,2,DNS_T_PTR)) {
+ if (!stralloc_copys(&line,"^")) return 0;
+ if (!dns_domain_todot_cat(&line,d1)) return 0;
+ if (!stralloc_cats(&line,":")) return 0;
+ x_getname(buf,len,pos,&d1);
+ if (!dns_domain_todot_cat(&line,d1)) return 0;
+ if (!stralloc_cats(&line,".")) return 0;
+ }
+ else if (byte_equal(data,2,DNS_T_MX)) {
+ uint16 dist;
+ if (!stralloc_copys(&line,"@")) return 0;
+ if (!dns_domain_todot_cat(&line,d1)) return 0;
+ if (!stralloc_cats(&line,"::")) return 0;
+ pos = x_copy(buf,len,pos,data,2);
+ uint16_unpack_big(data,&dist);
+ x_getname(buf,len,pos,&d1);
+ if (!dns_domain_todot_cat(&line,d1)) return 0;
+ if (!stralloc_cats(&line,".:")) return 0;
+ if (!stralloc_catulong0(&line,dist,0)) return 0;
+ }
+ else if (byte_equal(data,2,DNS_T_A) && (dlen == 4)) {
+ char ipstr[IP4_FMT];
+ if (!stralloc_copys(&line,"+")) return 0;
+ if (!dns_domain_todot_cat(&line,d1)) return 0;
+ if (!stralloc_cats(&line,":")) return 0;
+ x_copy(buf,len,pos,data,4);
+ if (!stralloc_catb(&line,ipstr,ip4_fmt(ipstr,data))) return 0;
+ }
+ else {
+ unsigned char ch;
+ unsigned char ch2;
+ if (!stralloc_copys(&line,":")) return 0;
+ if (!dns_domain_todot_cat(&line,d1)) return 0;
+ if (!stralloc_cats(&line,":")) return 0;
+ if (!stralloc_catulong0(&line,typenum,0)) return 0;
+ if (!stralloc_cats(&line,":")) return 0;
+ for (i = 0;i < dlen;++i) {
+ pos = x_copy(buf,len,pos,data,1);
+ ch = data[0];
+ if (printable(ch)) {
+ if (!stralloc_catb(&line,&ch,1)) return 0;
+ }
+ else {
+ if (!stralloc_cats(&line,"\\")) return 0;
+ ch2 = '0' + ((ch >> 6) & 7);
+ if (!stralloc_catb(&line,&ch2,1)) return 0;
+ ch2 = '0' + ((ch >> 3) & 7);
+ if (!stralloc_catb(&line,&ch2,1)) return 0;
+ ch2 = '0' + (ch & 7);
+ if (!stralloc_catb(&line,&ch2,1)) return 0;
+ }
+ }
+ }
+ if (!stralloc_cats(&line,":")) return 0;
+ if (!stralloc_catulong0(&line,ttl,0)) return 0;
+ if (!stralloc_cats(&line,"\n")) return 0;
+ put(line.s,line.len);
+
+ return len;
+}
+
+stralloc packet;
+
+int main(int argc,char **argv)
+{
+ char out[20];
+ unsigned long u;
+ uint16 dlen;
+ unsigned int pos;
+ uint32 oldserial = 0;
+ uint32 newserial = 0;
+ uint16 numqueries;
+ uint16 numanswers;
+
+ if (!*argv) die_usage();
+
+ if (!*++argv) die_usage();
+ if (!dns_domain_fromdot(&zone,*argv,str_len(*argv))) die_generate();
+ zonelen = dns_domain_length(zone);
+
+ if (!*++argv) die_usage();
+ fn = *argv;
+ if (!*++argv) die_usage();
+ fntmp = *argv;
+
+ fd = open_read(fn);
+ if (fd == -1) {
+ if (errno != error_noent) die_read();
+ }
+ else {
+ buffer_init(&b,buffer_unixread,fd,bspace,sizeof bspace);
+ if (getln(&b,&line,&match,'\n') == -1) die_read();
+ if (!stralloc_0(&line)) die_read();
+ if (line.s[0] == '#') {
+ scan_ulong(line.s + 1,&u);
+ oldserial = u;
+ }
+ close(fd);
+ }
+
+ if (!stralloc_copyb(&packet,"\0\0\0\0\0\1\0\0\0\0\0\0",12)) die_generate();
+ if (!stralloc_catb(&packet,zone,zonelen)) die_generate();
+ if (!stralloc_catb(&packet,DNS_T_SOA DNS_C_IN,4)) die_generate();
+ uint16_pack_big(out,packet.len);
+ buffer_put(&netwrite,out,2);
+ buffer_put(&netwrite,packet.s,packet.len);
+ buffer_flush(&netwrite);
+
+ netget(out,2);
+ uint16_unpack_big(out,&dlen);
+ if (!stralloc_ready(&packet,dlen)) die_parse();
+ netget(packet.s,dlen);
+ packet.len = dlen;
+
+ pos = x_copy(packet.s,packet.len,0,out,12);
+ uint16_unpack_big(out + 4,&numqueries);
+ uint16_unpack_big(out + 6,&numanswers);
+
+ while (numqueries) {
+ --numqueries;
+ pos = x_skipname(packet.s,packet.len,pos);
+ pos += 4;
+ }
+
+ if (!numanswers) { errno = error_proto; die_parse(); }
+ pos = x_getname(packet.s,packet.len,pos,&d1);
+ if (!dns_domain_equal(zone,d1)) { errno = error_proto; die_parse(); }
+ pos = x_copy(packet.s,packet.len,pos,out,10);
+ if (byte_diff(out,4,DNS_T_SOA DNS_C_IN)) { errno = error_proto; die_parse(); }
+ pos = x_skipname(packet.s,packet.len,pos);
+ pos = x_skipname(packet.s,packet.len,pos);
+ pos = x_copy(packet.s,packet.len,pos,out,4);
+
+ uint32_unpack_big(out,&newserial);
+
+
+ if (oldserial && newserial) /* allow 0 for very recently modified zones */
+ if (oldserial == newserial) /* allow serial numbers to move backwards */
+ _exit(0);
+
+
+ fd = open_trunc(fntmp);
+ if (fd == -1) die_write();
+ buffer_init(&b,buffer_unixwrite,fd,bspace,sizeof bspace);
+
+ if (!stralloc_copyb(&packet,"\0\0\0\0\0\1\0\0\0\0\0\0",12)) die_generate();
+ if (!stralloc_catb(&packet,zone,zonelen)) die_generate();
+ if (!stralloc_catb(&packet,DNS_T_AXFR DNS_C_IN,4)) die_generate();
+ uint16_pack_big(out,packet.len);
+ buffer_put(&netwrite,out,2);
+ buffer_put(&netwrite,packet.s,packet.len);
+ buffer_flush(&netwrite);
+
+ numsoa = 0;
+ while (numsoa < 2) {
+ netget(out,2);
+ uint16_unpack_big(out,&dlen);
+ if (!stralloc_ready(&packet,dlen)) die_parse();
+ netget(packet.s,dlen);
+ packet.len = dlen;
+
+ pos = x_copy(packet.s,packet.len,0,out,12);
+ uint16_unpack_big(out + 4,&numqueries);
+
+ while (numqueries) {
+ --numqueries;
+ pos = x_skipname(packet.s,packet.len,pos);
+ pos += 4;
+ }
+ while (pos < packet.len) {
+ pos = doit(packet.s,packet.len,pos);
+ if (!pos) die_parse();
+ }
+ }
+
+ if (buffer_flush(&b) == -1) die_write();
+ if (fsync(fd) == -1) die_write();
+ if (close(fd) == -1) die_write(); /* NFS dorks */
+ if (rename(fntmp,fn) == -1)
+ strerr_die6sys(111,FATAL,"unable to move ",fntmp," to ",fn,": ");
+ _exit(0);
+}
diff --git a/axfrdns-conf.c b/axfrdns-conf.c
new file mode 100644
index 0000000..4dc8657
--- /dev/null
+++ b/axfrdns-conf.c
@@ -0,0 +1,71 @@
+#include <unistd.h>
+#include <pwd.h>
+#include "strerr.h"
+#include "exit.h"
+#include "auto_home.h"
+#include "generic-conf.h"
+
+#define FATAL "axfrdns-conf: fatal: "
+
+void usage(void)
+{
+ strerr_die1x(100,"axfrdns-conf: usage: axfrdns-conf acct logacct /axfrdns /tinydns myip");
+}
+
+char *dir;
+char *user;
+char *loguser;
+struct passwd *pw;
+char *myip;
+char *tinydns;
+
+int main(int argc,char **argv)
+{
+ user = argv[1];
+ if (!user) usage();
+ loguser = argv[2];
+ if (!loguser) usage();
+ dir = argv[3];
+ if (!dir) usage();
+ if (dir[0] != '/') usage();
+ tinydns = argv[4];
+ if (!tinydns) usage();
+ if (tinydns[0] != '/') usage();
+ myip = argv[5];
+ if (!myip) usage();
+
+ pw = getpwnam(loguser);
+ if (!pw)
+ strerr_die3x(111,FATAL,"unknown account ",loguser);
+
+ init(dir,FATAL);
+ makelog(loguser,pw->pw_uid,pw->pw_gid);
+
+ makedir("env");
+ perm(02755);
+ start("env/ROOT"); outs(tinydns); outs("/root\n"); finish();
+ perm(0644);
+ start("env/IP"); outs(myip); outs("\n"); finish();
+ perm(0644);
+
+ start("run");
+ outs("#!/bin/sh\nexec 2>&1\nexec envdir ./env sh -c '\n exec envuidgid "); outs(user);
+ outs(" softlimit -d300000 tcpserver -vDRHl0 -x tcp.cdb -- \"$IP\" 53 ");
+ outs(auto_home); outs("/bin/axfrdns\n'\n");
+ finish();
+ perm(0755);
+
+ start("Makefile");
+ outs("tcp.cdb: tcp\n");
+ outs("\ttcprules tcp.cdb tcp.tmp < tcp\n");
+ finish();
+ perm(0644);
+
+ start("tcp");
+ outs("# sample line: 1.2.3.4:allow,AXFR=\"heaven.af.mil/3.2.1.in-addr.arpa\"\n");
+ outs(":deny\n");
+ finish();
+ perm(0644);
+
+ _exit(0);
+}
diff --git a/axfrdns.c b/axfrdns.c
new file mode 100644
index 0000000..7079850
--- /dev/null
+++ b/axfrdns.c
@@ -0,0 +1,378 @@
+#include <unistd.h>
+#include "droproot.h"
+#include "exit.h"
+#include "env.h"
+#include "uint32.h"
+#include "uint16.h"
+#include "ip4.h"
+#include "tai.h"
+#include "buffer.h"
+#include "timeoutread.h"
+#include "timeoutwrite.h"
+#include "open.h"
+#include "seek.h"
+#include "cdb.h"
+#include "stralloc.h"
+#include "strerr.h"
+#include "str.h"
+#include "byte.h"
+#include "case.h"
+#include "dns.h"
+#include "scan.h"
+#include "qlog.h"
+#include "response.h"
+
+extern int respond(char *,char *,char *);
+
+#define FATAL "axfrdns: fatal: "
+
+void nomem()
+{
+ strerr_die2x(111,FATAL,"out of memory");
+}
+void die_truncated()
+{
+ strerr_die2x(111,FATAL,"truncated request");
+}
+void die_netwrite()
+{
+ strerr_die2sys(111,FATAL,"unable to write to network: ");
+}
+void die_netread()
+{
+ strerr_die2sys(111,FATAL,"unable to read from network: ");
+}
+void die_outside()
+{
+ strerr_die2x(111,FATAL,"unable to locate information in data.cdb");
+}
+void die_cdbread()
+{
+ strerr_die2sys(111,FATAL,"unable to read data.cdb: ");
+}
+void die_cdbformat()
+{
+ strerr_die3x(111,FATAL,"unable to read data.cdb: ","format error");
+}
+
+int safewrite(int fd,char *buf,unsigned int len)
+{
+ int w;
+
+ w = timeoutwrite(60,fd,buf,len);
+ if (w <= 0) die_netwrite();
+ return w;
+}
+
+char netwritespace[1024];
+buffer netwrite = BUFFER_INIT(safewrite,1,netwritespace,sizeof netwritespace);
+
+void print(char *buf,unsigned int len)
+{
+ char tcpheader[2];
+ uint16_pack_big(tcpheader,len);
+ buffer_put(&netwrite,tcpheader,2);
+ buffer_put(&netwrite,buf,len);
+ buffer_flush(&netwrite);
+}
+
+char *axfr;
+static char *axfrok;
+
+void axfrcheck(char *q)
+{
+ int i;
+ int j;
+
+ if (!axfr) return;
+
+ i = j = 0;
+ for (;;) {
+ if (!axfr[i] || (axfr[i] == '/')) {
+ if (i > j) {
+ if (!dns_domain_fromdot(&axfrok,axfr + j,i - j)) nomem();
+ if (dns_domain_equal(q,axfrok)) return;
+ }
+ j = i + 1;
+ }
+ if (!axfr[i]) break;
+ ++i;
+ }
+
+ strerr_die2x(111,FATAL,"disallowed zone transfer request");
+}
+
+static char *zone;
+unsigned int zonelen;
+char typeclass[4];
+
+int fdcdb;
+buffer bcdb;
+char bcdbspace[1024];
+
+void get(char *buf,unsigned int len)
+{
+ int r;
+
+ while (len > 0) {
+ r = buffer_get(&bcdb,buf,len);
+ if (r < 0) die_cdbread();
+ if (!r) die_cdbformat();
+ buf += r;
+ len -= r;
+ }
+}
+
+char ip[4];
+unsigned long port;
+char clientloc[2];
+
+struct tai now;
+char data[32767];
+uint32 dlen;
+uint32 dpos;
+
+void copy(char *buf,unsigned int len)
+{
+ dpos = dns_packet_copy(data,dlen,dpos,buf,len);
+ if (!dpos) die_cdbread();
+}
+
+void doname(stralloc *sa)
+{
+ static char *d;
+ dpos = dns_packet_getname(data,dlen,dpos,&d);
+ if (!dpos) die_cdbread();
+ if (!stralloc_catb(sa,d,dns_domain_length(d))) nomem();
+}
+
+int build(stralloc *sa,char *q,int flagsoa,char id[2])
+{
+ unsigned int rdatapos;
+ char misc[20];
+ char type[2];
+ char recordloc[2];
+ char ttl[4];
+ char ttd[8];
+ struct tai cutoff;
+
+ dpos = 0;
+ copy(type,2);
+ if (flagsoa) if (byte_diff(type,2,DNS_T_SOA)) return 0;
+ if (!flagsoa) if (byte_equal(type,2,DNS_T_SOA)) return 0;
+
+ if (!stralloc_copyb(sa,id,2)) nomem();
+ if (!stralloc_catb(sa,"\204\000\0\0\0\1\0\0\0\0",10)) nomem();
+ copy(misc,1);
+ if ((misc[0] == '=' + 1) || (misc[0] == '*' + 1)) {
+ --misc[0];
+ copy(recordloc,2);
+ if (byte_diff(recordloc,2,clientloc)) return 0;
+ }
+ if (misc[0] == '*') {
+ if (flagsoa) return 0;
+ if (!stralloc_catb(sa,"\1*",2)) nomem();
+ }
+ if (!stralloc_catb(sa,q,dns_domain_length(q))) nomem();
+ if (!stralloc_catb(sa,type,2)) nomem();
+
+ copy(ttl,4);
+ copy(ttd,8);
+ if (byte_diff(ttd,8,"\0\0\0\0\0\0\0\0")) {
+ tai_unpack(ttd,&cutoff);
+ if (byte_equal(ttl,4,"\0\0\0\0")) {
+ if (tai_less(&cutoff,&now)) return 0;
+ uint32_pack_big(ttl,2);
+ }
+ else
+ if (!tai_less(&cutoff,&now)) return 0;
+ }
+
+ if (!stralloc_catb(sa,DNS_C_IN,2)) nomem();
+ if (!stralloc_catb(sa,ttl,4)) nomem();
+ if (!stralloc_catb(sa,"\0\0",2)) nomem();
+ rdatapos = sa->len;
+
+ if (byte_equal(type,2,DNS_T_SOA)) {
+ doname(sa);
+ doname(sa);
+ copy(misc,20);
+ if (!stralloc_catb(sa,misc,20)) nomem();
+ }
+ else if (byte_equal(type,2,DNS_T_NS) || byte_equal(type,2,DNS_T_PTR) || byte_equal(type,2,DNS_T_CNAME)) {
+ doname(sa);
+ }
+ else if (byte_equal(type,2,DNS_T_MX)) {
+ copy(misc,2);
+ if (!stralloc_catb(sa,misc,2)) nomem();
+ doname(sa);
+ }
+ else
+ if (!stralloc_catb(sa,data + dpos,dlen - dpos)) nomem();
+
+ if (sa->len > 65535) die_cdbformat();
+ uint16_pack_big(sa->s + rdatapos - 2,sa->len - rdatapos);
+ return 1;
+}
+
+static struct cdb c;
+static char *q;
+static stralloc soa;
+static stralloc message;
+
+void doaxfr(char id[2])
+{
+ char key[512];
+ uint32 klen;
+ char num[4];
+ uint32 eod;
+ uint32 pos;
+ int r;
+
+ axfrcheck(zone);
+
+ tai_now(&now);
+ cdb_init(&c,fdcdb);
+
+ byte_zero(clientloc,2);
+ key[0] = 0;
+ key[1] = '%';
+ byte_copy(key + 2,4,ip);
+ r = cdb_find(&c,key,6);
+ if (!r) r = cdb_find(&c,key,5);
+ if (!r) r = cdb_find(&c,key,4);
+ if (!r) r = cdb_find(&c,key,3);
+ if (!r) r = cdb_find(&c,key,2);
+ if (r == -1) die_cdbread();
+ if (r && (cdb_datalen(&c) == 2))
+ if (cdb_read(&c,clientloc,2,cdb_datapos(&c)) == -1) die_cdbread();
+
+ cdb_findstart(&c);
+ for (;;) {
+ r = cdb_findnext(&c,zone,zonelen);
+ if (r == -1) die_cdbread();
+ if (!r) die_outside();
+ dlen = cdb_datalen(&c);
+ if (dlen > sizeof data) die_cdbformat();
+ if (cdb_read(&c,data,dlen,cdb_datapos(&c)) == -1) die_cdbformat();
+ if (build(&soa,zone,1,id)) break;
+ }
+
+ cdb_free(&c);
+ print(soa.s,soa.len);
+
+ seek_begin(fdcdb);
+ buffer_init(&bcdb,buffer_unixread,fdcdb,bcdbspace,sizeof bcdbspace);
+
+ pos = 0;
+ get(num,4); pos += 4;
+ uint32_unpack(num,&eod);
+ while (pos < 2048) { get(num,4); pos += 4; }
+
+ while (pos < eod) {
+ if (eod - pos < 8) die_cdbformat();
+ get(num,4); pos += 4;
+ uint32_unpack(num,&klen);
+ get(num,4); pos += 4;
+ uint32_unpack(num,&dlen);
+ if (eod - pos < klen) die_cdbformat();
+ pos += klen;
+ if (eod - pos < dlen) die_cdbformat();
+ pos += dlen;
+
+ if (klen > sizeof key) die_cdbformat();
+ get(key,klen);
+ if (dlen > sizeof data) die_cdbformat();
+ get(data,dlen);
+
+ if ((klen > 1) && (key[0] == 0)) continue; /* location */
+ if (klen < 1) die_cdbformat();
+ if (dns_packet_getname(key,klen,0,&q) != klen) die_cdbformat();
+ if (!dns_domain_suffix(q,zone)) continue;
+ if (!build(&message,q,0,id)) continue;
+ print(message.s,message.len);
+ }
+
+ print(soa.s,soa.len);
+}
+
+void netread(char *buf,unsigned int len)
+{
+ int r;
+
+ while (len > 0) {
+ r = timeoutread(60,0,buf,len);
+ if (r == 0) _exit(0);
+ if (r < 0) die_netread();
+ buf += r; len -= r;
+ }
+}
+
+char tcpheader[2];
+char buf[512];
+uint16 len;
+
+static char seed[128];
+
+int main()
+{
+ unsigned int pos;
+ char header[12];
+ char qtype[2];
+ char qclass[2];
+ const char *x;
+
+ droproot(FATAL);
+ dns_random_init(seed);
+
+ axfr = env_get("AXFR");
+
+ x = env_get("TCPREMOTEIP");
+ if (x && ip4_scan(x,ip))
+ ;
+ else
+ byte_zero(ip,4);
+
+ x = env_get("TCPREMOTEPORT");
+ if (!x) x = "0";
+ scan_ulong(x,&port);
+
+ for (;;) {
+ netread(tcpheader,2);
+ uint16_unpack_big(tcpheader,&len);
+ if (len > 512) strerr_die2x(111,FATAL,"excessively large request");
+ netread(buf,len);
+
+ pos = dns_packet_copy(buf,len,0,header,12); if (!pos) die_truncated();
+ if (header[2] & 254) strerr_die2x(111,FATAL,"bogus query");
+ if (header[4] || (header[5] != 1)) strerr_die2x(111,FATAL,"bogus query");
+
+ pos = dns_packet_getname(buf,len,pos,&zone); if (!pos) die_truncated();
+ zonelen = dns_domain_length(zone);
+ pos = dns_packet_copy(buf,len,pos,qtype,2); if (!pos) die_truncated();
+ pos = dns_packet_copy(buf,len,pos,qclass,2); if (!pos) die_truncated();
+
+ if (byte_diff(qclass,2,DNS_C_IN) && byte_diff(qclass,2,DNS_C_ANY))
+ strerr_die2x(111,FATAL,"bogus query: bad class");
+
+ qlog(ip,port,header,zone,qtype," ");
+
+ if (byte_equal(qtype,2,DNS_T_AXFR)) {
+ case_lowerb(zone,zonelen);
+ fdcdb = open_read("data.cdb");
+ if (fdcdb == -1) die_cdbread();
+ doaxfr(header);
+ close(fdcdb);
+ }
+ else {
+ if (!response_query(zone,qtype,qclass)) nomem();
+ response[2] |= 4;
+ case_lowerb(zone,zonelen);
+ response_id(header);
+ response[3] &= ~128;
+ if (!(header[2] & 1)) response[2] &= ~1;
+ if (!respond(zone,qtype,ip)) die_outside();
+ print(response,response_len);
+ }
+ }
+}
diff --git a/buffer.c b/buffer.c
new file mode 100644
index 0000000..f44a697
--- /dev/null
+++ b/buffer.c
@@ -0,0 +1,10 @@
+#include "buffer.h"
+
+void buffer_init(buffer *s,int (*op)(),int fd,char *buf,unsigned int len)
+{
+ s->x = buf;
+ s->fd = fd;
+ s->op = op;
+ s->p = 0;
+ s->n = len;
+}
diff --git a/buffer.h b/buffer.h
new file mode 100644
index 0000000..fcdc253
--- /dev/null
+++ b/buffer.h
@@ -0,0 +1,59 @@
+#ifndef BUFFER_H
+#define BUFFER_H
+
+typedef struct buffer {
+ char *x;
+ unsigned int p;
+ unsigned int n;
+ int fd;
+ int (*op)();
+} buffer;
+
+#define BUFFER_INIT(op,fd,buf,len) { (buf), 0, (len), (fd), (op) }
+#define BUFFER_INSIZE 8192
+#define BUFFER_OUTSIZE 8192
+
+extern void buffer_init(buffer *,int (*)(),int,char *,unsigned int);
+
+extern int buffer_flush(buffer *);
+extern int buffer_put(buffer *,const char *,unsigned int);
+extern int buffer_putalign(buffer *,const char *,unsigned int);
+extern int buffer_putflush(buffer *,const char *,unsigned int);
+extern int buffer_puts(buffer *,const char *);
+extern int buffer_putsalign(buffer *,const char *);
+extern int buffer_putsflush(buffer *,const char *);
+
+#define buffer_PUTC(s,c) \
+ ( ((s)->n != (s)->p) \
+ ? ( (s)->x[(s)->p++] = (c), 0 ) \
+ : buffer_put((s),&(c),1) \
+ )
+
+extern int buffer_get(buffer *,char *,unsigned int);
+extern int buffer_bget(buffer *,char *,unsigned int);
+extern int buffer_feed(buffer *);
+
+extern char *buffer_peek(buffer *);
+extern void buffer_seek(buffer *,unsigned int);
+
+#define buffer_PEEK(s) ( (s)->x + (s)->n )
+#define buffer_SEEK(s,len) ( ( (s)->p -= (len) ) , ( (s)->n += (len) ) )
+
+#define buffer_GETC(s,c) \
+ ( ((s)->p > 0) \
+ ? ( *(c) = (s)->x[(s)->n], buffer_SEEK((s),1), 1 ) \
+ : buffer_get((s),(c),1) \
+ )
+
+extern int buffer_copy(buffer *,buffer *);
+
+extern int buffer_unixread(int,char *,unsigned int);
+extern int buffer_unixwrite(int,const char *,unsigned int);
+
+extern buffer *buffer_0;
+extern buffer *buffer_0small;
+extern buffer *buffer_1;
+extern buffer *buffer_1small;
+extern buffer *buffer_2;
+
+#endif
diff --git a/buffer_1.c b/buffer_1.c
new file mode 100644
index 0000000..2b6464a
--- /dev/null
+++ b/buffer_1.c
@@ -0,0 +1,5 @@
+#include "buffer.h"
+
+char buffer_1_space[BUFFER_OUTSIZE];
+static buffer it = BUFFER_INIT(buffer_unixwrite,1,buffer_1_space,sizeof buffer_1_space);
+buffer *buffer_1 = &it;
diff --git a/buffer_2.c b/buffer_2.c
new file mode 100644
index 0000000..268de19
--- /dev/null
+++ b/buffer_2.c
@@ -0,0 +1,5 @@
+#include "buffer.h"
+
+char buffer_2_space[256];
+static buffer it = BUFFER_INIT(buffer_unixwrite,2,buffer_2_space,sizeof buffer_2_space);
+buffer *buffer_2 = &it;
diff --git a/buffer_copy.c b/buffer_copy.c
new file mode 100644
index 0000000..dc4d4b1
--- /dev/null
+++ b/buffer_copy.c
@@ -0,0 +1,16 @@
+#include "buffer.h"
+
+int buffer_copy(buffer *bout,buffer *bin)
+{
+ int n;
+ char *x;
+
+ for (;;) {
+ n = buffer_feed(bin);
+ if (n < 0) return -2;
+ if (!n) return 0;
+ x = buffer_PEEK(bin);
+ if (buffer_put(bout,x,n) == -1) return -3;
+ buffer_SEEK(bin,n);
+ }
+}
diff --git a/buffer_get.c b/buffer_get.c
new file mode 100644
index 0000000..937b75e
--- /dev/null
+++ b/buffer_get.c
@@ -0,0 +1,67 @@
+#include "buffer.h"
+#include "byte.h"
+#include "error.h"
+
+static int oneread(int (*op)(),int fd,char *buf,unsigned int len)
+{
+ int r;
+
+ for (;;) {
+ r = op(fd,buf,len);
+ if (r == -1) if (errno == error_intr) continue;
+ return r;
+ }
+}
+
+static int getthis(buffer *s,char *buf,unsigned int len)
+{
+ if (len > s->p) len = s->p;
+ s->p -= len;
+ byte_copy(buf,len,s->x + s->n);
+ s->n += len;
+ return len;
+}
+
+int buffer_feed(buffer *s)
+{
+ int r;
+
+ if (s->p) return s->p;
+ r = oneread(s->op,s->fd,s->x,s->n);
+ if (r <= 0) return r;
+ s->p = r;
+ s->n -= r;
+ if (s->n > 0) byte_copyr(s->x + s->n,r,s->x);
+ return r;
+}
+
+int buffer_bget(buffer *s,char *buf,unsigned int len)
+{
+ int r;
+
+ if (s->p > 0) return getthis(s,buf,len);
+ if (s->n <= len) return oneread(s->op,s->fd,buf,s->n);
+ r = buffer_feed(s); if (r <= 0) return r;
+ return getthis(s,buf,len);
+}
+
+int buffer_get(buffer *s,char *buf,unsigned int len)
+{
+ int r;
+
+ if (s->p > 0) return getthis(s,buf,len);
+ if (s->n <= len) return oneread(s->op,s->fd,buf,len);
+ r = buffer_feed(s); if (r <= 0) return r;
+ return getthis(s,buf,len);
+}
+
+char *buffer_peek(buffer *s)
+{
+ return s->x + s->n;
+}
+
+void buffer_seek(buffer *s,unsigned int len)
+{
+ s->n += len;
+ s->p -= len;
+}
diff --git a/buffer_put.c b/buffer_put.c
new file mode 100644
index 0000000..f875f3f
--- /dev/null
+++ b/buffer_put.c
@@ -0,0 +1,88 @@
+#include "buffer.h"
+#include "str.h"
+#include "byte.h"
+#include "error.h"
+
+static int allwrite(int (*op)(),int fd,const char *buf,unsigned int len)
+{
+ int w;
+
+ while (len) {
+ w = op(fd,buf,len);
+ if (w == -1) {
+ if (errno == error_intr) continue;
+ return -1; /* note that some data may have been written */
+ }
+ if (w == 0) ; /* luser's fault */
+ buf += w;
+ len -= w;
+ }
+ return 0;
+}
+
+int buffer_flush(buffer *s)
+{
+ int p;
+
+ p = s->p;
+ if (!p) return 0;
+ s->p = 0;
+ return allwrite(s->op,s->fd,s->x,p);
+}
+
+int buffer_putalign(buffer *s,const char *buf,unsigned int len)
+{
+ unsigned int n;
+
+ while (len > (n = s->n - s->p)) {
+ byte_copy(s->x + s->p,n,buf); s->p += n; buf += n; len -= n;
+ if (buffer_flush(s) == -1) return -1;
+ }
+ /* now len <= s->n - s->p */
+ byte_copy(s->x + s->p,len,buf);
+ s->p += len;
+ return 0;
+}
+
+int buffer_put(buffer *s,const char *buf,unsigned int len)
+{
+ unsigned int n;
+
+ n = s->n;
+ if (len > n - s->p) {
+ if (buffer_flush(s) == -1) return -1;
+ /* now s->p == 0 */
+ if (n < BUFFER_OUTSIZE) n = BUFFER_OUTSIZE;
+ while (len > s->n) {
+ if (n > len) n = len;
+ if (allwrite(s->op,s->fd,buf,n) == -1) return -1;
+ buf += n;
+ len -= n;
+ }
+ }
+ /* now len <= s->n - s->p */
+ byte_copy(s->x + s->p,len,buf);
+ s->p += len;
+ return 0;
+}
+
+int buffer_putflush(buffer *s,const char *buf,unsigned int len)
+{
+ if (buffer_flush(s) == -1) return -1;
+ return allwrite(s->op,s->fd,buf,len);
+}
+
+int buffer_putsalign(buffer *s,const char *buf)
+{
+ return buffer_putalign(s,buf,str_len(buf));
+}
+
+int buffer_puts(buffer *s,const char *buf)
+{
+ return buffer_put(s,buf,str_len(buf));
+}
+
+int buffer_putsflush(buffer *s,const char *buf)
+{
+ return buffer_putflush(s,buf,str_len(buf));
+}
diff --git a/buffer_read.c b/buffer_read.c
new file mode 100644
index 0000000..286a06c
--- /dev/null
+++ b/buffer_read.c
@@ -0,0 +1,7 @@
+#include <unistd.h>
+#include "buffer.h"
+
+int buffer_unixread(int fd,char *buf,unsigned int len)
+{
+ return read(fd,buf,len);
+}
diff --git a/buffer_write.c b/buffer_write.c
new file mode 100644
index 0000000..fbd26d0
--- /dev/null
+++ b/buffer_write.c
@@ -0,0 +1,7 @@
+#include <unistd.h>
+#include "buffer.h"
+
+int buffer_unixwrite(int fd,const char *buf,unsigned int len)
+{
+ return write(fd,buf,len);
+}
diff --git a/byte.h b/byte.h
new file mode 100644
index 0000000..de06c69
--- /dev/null
+++ b/byte.h
@@ -0,0 +1,13 @@
+#ifndef BYTE_H
+#define BYTE_H
+
+extern unsigned int byte_chr();
+extern unsigned int byte_rchr();
+extern void byte_copy();
+extern void byte_copyr();
+extern int byte_diff();
+extern void byte_zero();
+
+#define byte_equal(s,n,t) (!byte_diff((s),(n),(t)))
+
+#endif
diff --git a/byte_chr.c b/byte_chr.c
new file mode 100644
index 0000000..f81dde8
--- /dev/null
+++ b/byte_chr.c
@@ -0,0 +1,20 @@
+#include "byte.h"
+
+unsigned int byte_chr(s,n,c)
+char *s;
+register unsigned int n;
+int c;
+{
+ register char ch;
+ register char *t;
+
+ ch = c;
+ t = s;
+ for (;;) {
+ if (!n) break; if (*t == ch) break; ++t; --n;
+ if (!n) break; if (*t == ch) break; ++t; --n;
+ if (!n) break; if (*t == ch) break; ++t; --n;
+ if (!n) break; if (*t == ch) break; ++t; --n;
+ }
+ return t - s;
+}
diff --git a/byte_copy.c b/byte_copy.c
new file mode 100644
index 0000000..eaad11b
--- /dev/null
+++ b/byte_copy.c
@@ -0,0 +1,14 @@
+#include "byte.h"
+
+void byte_copy(to,n,from)
+register char *to;
+register unsigned int n;
+register char *from;
+{
+ for (;;) {
+ if (!n) return; *to++ = *from++; --n;
+ if (!n) return; *to++ = *from++; --n;
+ if (!n) return; *to++ = *from++; --n;
+ if (!n) return; *to++ = *from++; --n;
+ }
+}
diff --git a/byte_cr.c b/byte_cr.c
new file mode 100644
index 0000000..3e7a1d5
--- /dev/null
+++ b/byte_cr.c
@@ -0,0 +1,16 @@
+#include "byte.h"
+
+void byte_copyr(to,n,from)
+register char *to;
+register unsigned int n;
+register char *from;
+{
+ to += n;
+ from += n;
+ for (;;) {
+ if (!n) return; *--to = *--from; --n;
+ if (!n) return; *--to = *--from; --n;
+ if (!n) return; *--to = *--from; --n;
+ if (!n) return; *--to = *--from; --n;
+ }
+}
diff --git a/byte_diff.c b/byte_diff.c
new file mode 100644
index 0000000..cdbd760
--- /dev/null
+++ b/byte_diff.c
@@ -0,0 +1,16 @@
+#include "byte.h"
+
+int byte_diff(s,n,t)
+register char *s;
+register unsigned int n;
+register char *t;
+{
+ for (;;) {
+ if (!n) return 0; if (*s != *t) break; ++s; ++t; --n;
+ if (!n) return 0; if (*s != *t) break; ++s; ++t; --n;
+ if (!n) return 0; if (*s != *t) break; ++s; ++t; --n;
+ if (!n) return 0; if (*s != *t) break; ++s; ++t; --n;
+ }
+ return ((int)(unsigned int)(unsigned char) *s)
+ - ((int)(unsigned int)(unsigned char) *t);
+}
diff --git a/byte_zero.c b/byte_zero.c
new file mode 100644
index 0000000..92009ba
--- /dev/null
+++ b/byte_zero.c
@@ -0,0 +1,13 @@
+#include "byte.h"
+
+void byte_zero(s,n)
+char *s;
+register unsigned int n;
+{
+ for (;;) {
+ if (!n) break; *s++ = 0; --n;
+ if (!n) break; *s++ = 0; --n;
+ if (!n) break; *s++ = 0; --n;
+ if (!n) break; *s++ = 0; --n;
+ }
+}
diff --git a/cache.c b/cache.c
new file mode 100644
index 0000000..6302428
--- /dev/null
+++ b/cache.c
@@ -0,0 +1,207 @@
+#include "alloc.h"
+#include "byte.h"
+#include "uint32.h"
+#include "exit.h"
+#include "tai.h"
+#include "cache.h"
+
+uint64 cache_motion = 0;
+
+static char *x = 0;
+static uint32 size;
+static uint32 hsize;
+static uint32 writer;
+static uint32 oldest;
+static uint32 unused;
+
+/*
+100 <= size <= 1000000000.
+4 <= hsize <= size/16.
+hsize is a power of 2.
+
+hsize <= writer <= oldest <= unused <= size.
+If oldest == unused then unused == size.
+
+x is a hash table with the following structure:
+x[0...hsize-1]: hsize/4 head links.
+x[hsize...writer-1]: consecutive entries, newest entry on the right.
+x[writer...oldest-1]: free space for new entries.
+x[oldest...unused-1]: consecutive entries, oldest entry on the left.
+x[unused...size-1]: unused.
+
+Each hash bucket is a linked list containing the following items:
+the head link, the newest entry, the second-newest entry, etc.
+Each link is a 4-byte number giving the xor of
+the positions of the adjacent items in the list.
+
+Entries are always inserted immediately after the head and removed at the tail.
+
+Each entry contains the following information:
+4-byte link; 4-byte keylen; 4-byte datalen; 8-byte expire time; key; data.
+*/
+
+#define MAXKEYLEN 1000
+#define MAXDATALEN 1000000
+
+static void cache_impossible(void)
+{
+ _exit(111);
+}
+
+static void set4(uint32 pos,uint32 u)
+{
+ if (pos > size - 4) cache_impossible();
+ uint32_pack(x + pos,u);
+}
+
+static uint32 get4(uint32 pos)
+{
+ uint32 result;
+ if (pos > size - 4) cache_impossible();
+ uint32_unpack(x + pos,&result);
+ return result;
+}
+
+static unsigned int hash(const char *key,unsigned int keylen)
+{
+ unsigned int result = 5381;
+
+ while (keylen) {
+ result = (result << 5) + result;
+ result ^= (unsigned char) *key;
+ ++key;
+ --keylen;
+ }
+ result <<= 2;
+ result &= hsize - 4;
+ return result;
+}
+
+char *cache_get(const char *key,unsigned int keylen,unsigned int *datalen,uint32 *ttl)
+{
+ struct tai expire;
+ struct tai now;
+ uint32 pos;
+ uint32 prevpos;
+ uint32 nextpos;
+ uint32 u;
+ unsigned int loop;
+ double d;
+
+ if (!x) return 0;
+ if (keylen > MAXKEYLEN) return 0;
+
+ prevpos = hash(key,keylen);
+ pos = get4(prevpos);
+ loop = 0;
+
+ while (pos) {
+ if (get4(pos + 4) == keylen) {
+ if (pos + 20 + keylen > size) cache_impossible();
+ if (byte_equal(key,keylen,x + pos + 20)) {
+ tai_unpack(x + pos + 12,&expire);
+ tai_now(&now);
+ if (tai_less(&expire,&now)) return 0;
+
+ tai_sub(&expire,&expire,&now);
+ d = tai_approx(&expire);
+ if (d > 604800) d = 604800;
+ *ttl = d;
+
+ u = get4(pos + 8);
+ if (u > size - pos - 20 - keylen) cache_impossible();
+ *datalen = u;
+
+ return x + pos + 20 + keylen;
+ }
+ }
+ nextpos = prevpos ^ get4(pos);
+ prevpos = pos;
+ pos = nextpos;
+ if (++loop > 100) return 0; /* to protect against hash flooding */
+ }
+
+ return 0;
+}
+
+void cache_set(const char *key,unsigned int keylen,const char *data,unsigned int datalen,uint32 ttl)
+{
+ struct tai now;
+ struct tai expire;
+ unsigned int entrylen;
+ unsigned int keyhash;
+ uint32 pos;
+
+ if (!x) return;
+ if (keylen > MAXKEYLEN) return;
+ if (datalen > MAXDATALEN) return;
+
+ if (!ttl) return;
+ if (ttl > 604800) ttl = 604800;
+
+ entrylen = keylen + datalen + 20;
+
+ while (writer + entrylen > oldest) {
+ if (oldest == unused) {
+ if (writer <= hsize) return;
+ unused = writer;
+ oldest = hsize;
+ writer = hsize;
+ }
+
+ pos = get4(oldest);
+ set4(pos,get4(pos) ^ oldest);
+
+ oldest += get4(oldest + 4) + get4(oldest + 8) + 20;
+ if (oldest > unused) cache_impossible();
+ if (oldest == unused) {
+ unused = size;
+ oldest = size;
+ }
+ }
+
+ keyhash = hash(key,keylen);
+
+ tai_now(&now);
+ tai_uint(&expire,ttl);
+ tai_add(&expire,&expire,&now);
+
+ pos = get4(keyhash);
+ if (pos)
+ set4(pos,get4(pos) ^ keyhash ^ writer);
+ set4(writer,pos ^ keyhash);
+ set4(writer + 4,keylen);
+ set4(writer + 8,datalen);
+ tai_pack(x + writer + 12,&expire);
+ byte_copy(x + writer + 20,keylen,key);
+ byte_copy(x + writer + 20 + keylen,datalen,data);
+
+ set4(keyhash,writer);
+ writer += entrylen;
+ cache_motion += entrylen;
+}
+
+int cache_init(unsigned int cachesize)
+{
+ if (x) {
+ alloc_free(x);
+ x = 0;
+ }
+
+ if (cachesize > 1000000000) cachesize = 1000000000;
+ if (cachesize < 100) cachesize = 100;
+ size = cachesize;
+
+ hsize = 4;
+ while (hsize <= (size >> 5)) hsize <<= 1;
+
+ x = alloc(size);
+ if (!x) return 0;
+ byte_zero(x,size);
+
+ writer = hsize;
+ oldest = size;
+ unused = size;
+
+ return 1;
+}
diff --git a/cache.h b/cache.h
new file mode 100644
index 0000000..f5306c5
--- /dev/null
+++ b/cache.h
@@ -0,0 +1,12 @@
+#ifndef CACHE_H
+#define CACHE_H
+
+#include "uint32.h"
+#include "uint64.h"
+
+extern uint64 cache_motion;
+extern int cache_init(unsigned int);
+extern void cache_set(const char *,unsigned int,const char *,unsigned int,uint32);
+extern char *cache_get(const char *,unsigned int,unsigned int *,uint32 *);
+
+#endif
diff --git a/cachetest.c b/cachetest.c
new file mode 100644
index 0000000..c689862
--- /dev/null
+++ b/cachetest.c
@@ -0,0 +1,32 @@
+#include "buffer.h"
+#include "exit.h"
+#include "cache.h"
+#include "str.h"
+
+int main(int argc,char **argv)
+{
+ int i;
+ char *x;
+ char *y;
+ unsigned int u;
+ uint32 ttl;
+
+ if (!cache_init(200)) _exit(111);
+
+ if (*argv) ++argv;
+
+ while (x = *argv++) {
+ i = str_chr(x,':');
+ if (x[i])
+ cache_set(x,i,x + i + 1,str_len(x) - i - 1,86400);
+ else {
+ y = cache_get(x,i,&u,&ttl);
+ if (y)
+ buffer_put(buffer_1,y,u);
+ buffer_puts(buffer_1,"\n");
+ }
+ }
+
+ buffer_flush(buffer_1);
+ _exit(0);
+}
diff --git a/case.h b/case.h
new file mode 100644
index 0000000..8293a04
--- /dev/null
+++ b/case.h
@@ -0,0 +1,13 @@
+#ifndef CASE_H
+#define CASE_H
+
+extern void case_lowers(char *);
+extern void case_lowerb(char *,unsigned int);
+extern int case_diffs(const char *,const char *);
+extern int case_diffb(const char *,unsigned int,const char *);
+extern int case_starts(const char *,const char *);
+extern int case_startb(const char *,unsigned int,const char *);
+
+#define case_equals(s,t) (!case_diffs((s),(t)))
+
+#endif
diff --git a/case_diffb.c b/case_diffb.c
new file mode 100644
index 0000000..b62a4b2
--- /dev/null
+++ b/case_diffb.c
@@ -0,0 +1,18 @@
+#include "case.h"
+
+int case_diffb(register const char *s,register unsigned int len,register const char *t)
+{
+ register unsigned char x;
+ register unsigned char y;
+
+ while (len > 0) {
+ --len;
+ x = *s++ - 'A';
+ if (x <= 'Z' - 'A') x += 'a'; else x += 'A';
+ y = *t++ - 'A';
+ if (y <= 'Z' - 'A') y += 'a'; else y += 'A';
+ if (x != y)
+ return ((int)(unsigned int) x) - ((int)(unsigned int) y);
+ }
+ return 0;
+}
diff --git a/case_diffs.c b/case_diffs.c
new file mode 100644
index 0000000..683977a
--- /dev/null
+++ b/case_diffs.c
@@ -0,0 +1,17 @@
+#include "case.h"
+
+int case_diffs(register const char *s,register const char *t)
+{
+ register unsigned char x;
+ register unsigned char y;
+
+ for (;;) {
+ x = *s++ - 'A';
+ if (x <= 'Z' - 'A') x += 'a'; else x += 'A';
+ y = *t++ - 'A';
+ if (y <= 'Z' - 'A') y += 'a'; else y += 'A';
+ if (x != y) break;
+ if (!x) break;
+ }
+ return ((int)(unsigned int) x) - ((int)(unsigned int) y);
+}
diff --git a/case_lowerb.c b/case_lowerb.c
new file mode 100644
index 0000000..829c981
--- /dev/null
+++ b/case_lowerb.c
@@ -0,0 +1,12 @@
+#include "case.h"
+
+void case_lowerb(char *s,unsigned int len)
+{
+ unsigned char x;
+ while (len > 0) {
+ --len;
+ x = *s - 'A';
+ if (x <= 'Z' - 'A') *s = x + 'a';
+ ++s;
+ }
+}
diff --git a/cdb.c b/cdb.c
new file mode 100644
index 0000000..3ba1ea3
--- /dev/null
+++ b/cdb.c
@@ -0,0 +1,136 @@
+/* Public domain. */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include "error.h"
+#include "seek.h"
+#include "byte.h"
+#include "cdb.h"
+
+void cdb_free(struct cdb *c)
+{
+ if (c->map) {
+ munmap(c->map,c->size);
+ c->map = 0;
+ }
+}
+
+void cdb_findstart(struct cdb *c)
+{
+ c->loop = 0;
+}
+
+void cdb_init(struct cdb *c,int fd)
+{
+ struct stat st;
+ char *x;
+
+ cdb_free(c);
+ cdb_findstart(c);
+ c->fd = fd;
+
+ if (fstat(fd,&st) == 0)
+ if (st.st_size <= 0xffffffff) {
+ x = mmap(0,st.st_size,PROT_READ,MAP_SHARED,fd,0);
+ if (x + 1) {
+ c->size = st.st_size;
+ c->map = x;
+ }
+ }
+}
+
+int cdb_read(struct cdb *c,char *buf,unsigned int len,uint32 pos)
+{
+ if (c->map) {
+ if ((pos > c->size) || (c->size - pos < len)) goto FORMAT;
+ byte_copy(buf,len,c->map + pos);
+ }
+ else {
+ if (seek_set(c->fd,pos) == -1) return -1;
+ while (len > 0) {
+ int r;
+ do
+ r = read(c->fd,buf,len);
+ while ((r == -1) && (errno == error_intr));
+ if (r == -1) return -1;
+ if (r == 0) goto FORMAT;
+ buf += r;
+ len -= r;
+ }
+ }
+ return 0;
+
+ FORMAT:
+ errno = error_proto;
+ return -1;
+}
+
+static int match(struct cdb *c,const char *key,unsigned int len,uint32 pos)
+{
+ char buf[32];
+ int n;
+
+ while (len > 0) {
+ n = sizeof buf;
+ if (n > len) n = len;
+ if (cdb_read(c,buf,n,pos) == -1) return -1;
+ if (byte_diff(buf,n,key)) return 0;
+ pos += n;
+ key += n;
+ len -= n;
+ }
+ return 1;
+}
+
+int cdb_findnext(struct cdb *c,const char *key,unsigned int len)
+{
+ char buf[8];
+ uint32 pos;
+ uint32 u;
+
+ if (!c->loop) {
+ u = cdb_hash(key,len);
+ if (cdb_read(c,buf,8,(u << 3) & 2047) == -1) return -1;
+ uint32_unpack(buf + 4,&c->hslots);
+ if (!c->hslots) return 0;
+ uint32_unpack(buf,&c->hpos);
+ c->khash = u;
+ u >>= 8;
+ u %= c->hslots;
+ u <<= 3;
+ c->kpos = c->hpos + u;
+ }
+
+ while (c->loop < c->hslots) {
+ if (cdb_read(c,buf,8,c->kpos) == -1) return -1;
+ uint32_unpack(buf + 4,&pos);
+ if (!pos) return 0;
+ c->loop += 1;
+ c->kpos += 8;
+ if (c->kpos == c->hpos + (c->hslots << 3)) c->kpos = c->hpos;
+ uint32_unpack(buf,&u);
+ if (u == c->khash) {
+ if (cdb_read(c,buf,8,pos) == -1) return -1;
+ uint32_unpack(buf,&u);
+ if (u == len)
+ switch(match(c,key,len,pos + 8)) {
+ case -1:
+ return -1;
+ case 1:
+ uint32_unpack(buf + 4,&c->dlen);
+ c->dpos = pos + 8 + len;
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+int cdb_find(struct cdb *c,const char *key,unsigned int len)
+{
+ cdb_findstart(c);
+ return cdb_findnext(c,key,len);
+}
diff --git a/cdb.h b/cdb.h
new file mode 100644
index 0000000..65d0b1a
--- /dev/null
+++ b/cdb.h
@@ -0,0 +1,37 @@
+/* Public domain. */
+
+#ifndef CDB_H
+#define CDB_H
+
+#include "uint32.h"
+
+#define CDB_HASHSTART 5381
+extern uint32 cdb_hashadd(uint32,unsigned char);
+extern uint32 cdb_hash(const char *,unsigned int);
+
+struct cdb {
+ char *map; /* 0 if no map is available */
+ int fd;
+ uint32 size; /* initialized if map is nonzero */
+ uint32 loop; /* number of hash slots searched under this key */
+ uint32 khash; /* initialized if loop is nonzero */
+ uint32 kpos; /* initialized if loop is nonzero */
+ uint32 hpos; /* initialized if loop is nonzero */
+ uint32 hslots; /* initialized if loop is nonzero */
+ uint32 dpos; /* initialized if cdb_findnext() returns 1 */
+ uint32 dlen; /* initialized if cdb_findnext() returns 1 */
+} ;
+
+extern void cdb_free(struct cdb *);
+extern void cdb_init(struct cdb *,int fd);
+
+extern int cdb_read(struct cdb *,char *,unsigned int,uint32);
+
+extern void cdb_findstart(struct cdb *);
+extern int cdb_findnext(struct cdb *,const char *,unsigned int);
+extern int cdb_find(struct cdb *,const char *,unsigned int);
+
+#define cdb_datapos(c) ((c)->dpos)
+#define cdb_datalen(c) ((c)->dlen)
+
+#endif
diff --git a/cdb_hash.c b/cdb_hash.c
new file mode 100644
index 0000000..71102e1
--- /dev/null
+++ b/cdb_hash.c
@@ -0,0 +1,21 @@
+/* Public domain. */
+
+#include "cdb.h"
+
+uint32 cdb_hashadd(uint32 h,unsigned char c)
+{
+ h += (h << 5);
+ return h ^ c;
+}
+
+uint32 cdb_hash(const char *buf,unsigned int len)
+{
+ uint32 h;
+
+ h = CDB_HASHSTART;
+ while (len) {
+ h = cdb_hashadd(h,*buf++);
+ --len;
+ }
+ return h;
+}
diff --git a/cdb_make.c b/cdb_make.c
new file mode 100644
index 0000000..278420f
--- /dev/null
+++ b/cdb_make.c
@@ -0,0 +1,152 @@
+/* Public domain. */
+
+#include "seek.h"
+#include "error.h"
+#include "alloc.h"
+#include "cdb.h"
+#include "cdb_make.h"
+
+int cdb_make_start(struct cdb_make *c,int fd)
+{
+ c->head = 0;
+ c->split = 0;
+ c->hash = 0;
+ c->numentries = 0;
+ c->fd = fd;
+ c->pos = sizeof c->final;
+ buffer_init(&c->b,buffer_unixwrite,fd,c->bspace,sizeof c->bspace);
+ return seek_set(fd,c->pos);
+}
+
+static int posplus(struct cdb_make *c,uint32 len)
+{
+ uint32 newpos = c->pos + len;
+ if (newpos < len) { errno = error_nomem; return -1; }
+ c->pos = newpos;
+ return 0;
+}
+
+int cdb_make_addend(struct cdb_make *c,unsigned int keylen,unsigned int datalen,uint32 h)
+{
+ struct cdb_hplist *head;
+
+ head = c->head;
+ if (!head || (head->num >= CDB_HPLIST)) {
+ head = (struct cdb_hplist *) alloc(sizeof(struct cdb_hplist));
+ if (!head) return -1;
+ head->num = 0;
+ head->next = c->head;
+ c->head = head;
+ }
+ head->hp[head->num].h = h;
+ head->hp[head->num].p = c->pos;
+ ++head->num;
+ ++c->numentries;
+ if (posplus(c,8) == -1) return -1;
+ if (posplus(c,keylen) == -1) return -1;
+ if (posplus(c,datalen) == -1) return -1;
+ return 0;
+}
+
+int cdb_make_addbegin(struct cdb_make *c,unsigned int keylen,unsigned int datalen)
+{
+ char buf[8];
+
+ if (keylen > 0xffffffff) { errno = error_nomem; return -1; }
+ if (datalen > 0xffffffff) { errno = error_nomem; return -1; }
+
+ uint32_pack(buf,keylen);
+ uint32_pack(buf + 4,datalen);
+ if (buffer_putalign(&c->b,buf,8) == -1) return -1;
+ return 0;
+}
+
+int cdb_make_add(struct cdb_make *c,const char *key,unsigned int keylen,const char *data,unsigned int datalen)
+{
+ if (cdb_make_addbegin(c,keylen,datalen) == -1) return -1;
+ if (buffer_putalign(&c->b,key,keylen) == -1) return -1;
+ if (buffer_putalign(&c->b,data,datalen) == -1) return -1;
+ return cdb_make_addend(c,keylen,datalen,cdb_hash(key,keylen));
+}
+
+int cdb_make_finish(struct cdb_make *c)
+{
+ char buf[8];
+ int i;
+ uint32 len;
+ uint32 u;
+ uint32 memsize;
+ uint32 count;
+ uint32 where;
+ struct cdb_hplist *x;
+ struct cdb_hp *hp;
+
+ for (i = 0;i < 256;++i)
+ c->count[i] = 0;
+
+ for (x = c->head;x;x = x->next) {
+ i = x->num;
+ while (i--)
+ ++c->count[255 & x->hp[i].h];
+ }
+
+ memsize = 1;
+ for (i = 0;i < 256;++i) {
+ u = c->count[i] * 2;
+ if (u > memsize)
+ memsize = u;
+ }
+
+ memsize += c->numentries; /* no overflow possible up to now */
+ u = (uint32) 0 - (uint32) 1;
+ u /= sizeof(struct cdb_hp);
+ if (memsize > u) { errno = error_nomem; return -1; }
+
+ c->split = (struct cdb_hp *) alloc(memsize * sizeof(struct cdb_hp));
+ if (!c->split) return -1;
+
+ c->hash = c->split + c->numentries;
+
+ u = 0;
+ for (i = 0;i < 256;++i) {
+ u += c->count[i]; /* bounded by numentries, so no overflow */
+ c->start[i] = u;
+ }
+
+ for (x = c->head;x;x = x->next) {
+ i = x->num;
+ while (i--)
+ c->split[--c->start[255 & x->hp[i].h]] = x->hp[i];
+ }
+
+ for (i = 0;i < 256;++i) {
+ count = c->count[i];
+
+ len = count + count; /* no overflow possible */
+ uint32_pack(c->final + 8 * i,c->pos);
+ uint32_pack(c->final + 8 * i + 4,len);
+
+ for (u = 0;u < len;++u)
+ c->hash[u].h = c->hash[u].p = 0;
+
+ hp = c->split + c->start[i];
+ for (u = 0;u < count;++u) {
+ where = (hp->h >> 8) % len;
+ while (c->hash[where].p)
+ if (++where == len)
+ where = 0;
+ c->hash[where] = *hp++;
+ }
+
+ for (u = 0;u < len;++u) {
+ uint32_pack(buf,c->hash[u].h);
+ uint32_pack(buf + 4,c->hash[u].p);
+ if (buffer_putalign(&c->b,buf,8) == -1) return -1;
+ if (posplus(c,8) == -1) return -1;
+ }
+ }
+
+ if (buffer_flush(&c->b) == -1) return -1;
+ if (seek_begin(c->fd) == -1) return -1;
+ return buffer_putflush(&c->b,c->final,sizeof c->final);
+}
diff --git a/cdb_make.h b/cdb_make.h
new file mode 100644
index 0000000..49ea719
--- /dev/null
+++ b/cdb_make.h
@@ -0,0 +1,39 @@
+/* Public domain. */
+
+#ifndef CDB_MAKE_H
+#define CDB_MAKE_H
+
+#include "buffer.h"
+#include "uint32.h"
+
+#define CDB_HPLIST 1000
+
+struct cdb_hp { uint32 h; uint32 p; } ;
+
+struct cdb_hplist {
+ struct cdb_hp hp[CDB_HPLIST];
+ struct cdb_hplist *next;
+ int num;
+} ;
+
+struct cdb_make {
+ char bspace[8192];
+ char final[2048];
+ uint32 count[256];
+ uint32 start[256];
+ struct cdb_hplist *head;
+ struct cdb_hp *split; /* includes space for hash */
+ struct cdb_hp *hash;
+ uint32 numentries;
+ buffer b;
+ uint32 pos;
+ int fd;
+} ;
+
+extern int cdb_make_start(struct cdb_make *,int);
+extern int cdb_make_addbegin(struct cdb_make *,unsigned int,unsigned int);
+extern int cdb_make_addend(struct cdb_make *,unsigned int,unsigned int,uint32);
+extern int cdb_make_add(struct cdb_make *,const char *,unsigned int,const char *,unsigned int);
+extern int cdb_make_finish(struct cdb_make *);
+
+#endif
diff --git a/chkshsgr.c b/chkshsgr.c
new file mode 100644
index 0000000..2b942d8
--- /dev/null
+++ b/chkshsgr.c
@@ -0,0 +1,10 @@
+#include "exit.h"
+
+int main()
+{
+ short x[4];
+
+ x[0] = x[1] = 0;
+ if (getgroups(1,x) == 0) if (setgroups(1,x) == -1) _exit(1);
+ _exit(0);
+}
diff --git a/choose.sh b/choose.sh
new file mode 100644
index 0000000..feff2da
--- /dev/null
+++ b/choose.sh
@@ -0,0 +1,18 @@
+
+result="$4"
+
+case "$1" in
+ *c*) ./compile $2.c >/dev/null 2>&1 || result="$3" ;;
+esac
+
+case "$1" in
+ *l*) ./load $2 >/dev/null 2>&1 || result="$3" ;;
+esac
+
+case "$1" in
+ *r*) ./$2 >/dev/null 2>&1 || result="$3" ;;
+esac
+
+rm -f $2.o $2
+
+exec cat "$result"
diff --git a/conf-cc b/conf-cc
new file mode 100644
index 0000000..b315ecb
--- /dev/null
+++ b/conf-cc
@@ -0,0 +1,3 @@
+gcc -O2 -Wimplicit -Wunused -Wcomment -Wchar-subscripts -Wuninitialized -Wshadow -Wcast-qual -Wcast-align -Wwrite-strings
+
+This will be used to compile .c files.
diff --git a/conf-home b/conf-home
new file mode 100644
index 0000000..e5e30ed
--- /dev/null
+++ b/conf-home
@@ -0,0 +1,4 @@
+/usr/local
+
+This is the dnscache home directory. Programs will be installed in
+.../bin.
diff --git a/conf-ld b/conf-ld
new file mode 100644
index 0000000..59a0de7
--- /dev/null
+++ b/conf-ld
@@ -0,0 +1,3 @@
+gcc -s
+
+This will be used to link .o files into an executable.
diff --git a/dd.c b/dd.c
new file mode 100644
index 0000000..778cbdc
--- /dev/null
+++ b/dd.c
@@ -0,0 +1,36 @@
+#include "dns.h"
+#include "dd.h"
+
+int dd(const char *q,const char *base,char ip[4])
+{
+ int j;
+ unsigned int x;
+
+ for (j = 0;;++j) {
+ if (dns_domain_equal(q,base)) return j;
+ if (j >= 4) return -1;
+
+ if (*q <= 0) return -1;
+ if (*q >= 4) return -1;
+ if ((q[1] < '0') || (q[1] > '9')) return -1;
+ x = q[1] - '0';
+ if (*q == 1) {
+ ip[j] = x;
+ q += 2;
+ continue;
+ }
+ if (!x) return -1;
+ if ((q[2] < '0') || (q[2] > '9')) return -1;
+ x = x * 10 + (q[2] - '0');
+ if (*q == 2) {
+ ip[j] = x;
+ q += 3;
+ continue;
+ }
+ if ((q[3] < '0') || (q[3] > '9')) return -1;
+ x = x * 10 + (q[3] - '0');
+ if (x > 255) return -1;
+ ip[j] = x;
+ q += 4;
+ }
+}
diff --git a/dd.h b/dd.h
new file mode 100644
index 0000000..c090358
--- /dev/null
+++ b/dd.h
@@ -0,0 +1,6 @@
+#ifndef DD_H
+#define DD_H
+
+extern int dd(const char *,const char *,char *);
+
+#endif
diff --git a/direntry.h1 b/direntry.h1
new file mode 100644
index 0000000..446d5c7
--- /dev/null
+++ b/direntry.h1
@@ -0,0 +1,10 @@
+#ifndef DIRENTRY_H
+#define DIRENTRY_H
+
+/* sysdep: -dirent */
+
+#include <sys/types.h>
+#include <sys/dir.h>
+#define direntry struct direct
+
+#endif
diff --git a/direntry.h2 b/direntry.h2
new file mode 100644
index 0000000..d1628a9
--- /dev/null
+++ b/direntry.h2
@@ -0,0 +1,10 @@
+#ifndef DIRENTRY_H
+#define DIRENTRY_H
+
+/* sysdep: +dirent */
+
+#include <sys/types.h>
+#include <dirent.h>
+#define direntry struct dirent
+
+#endif
diff --git a/dns.h b/dns.h
new file mode 100644
index 0000000..2f899ef
--- /dev/null
+++ b/dns.h
@@ -0,0 +1,84 @@
+#ifndef DNS_H
+#define DNS_H
+
+#include "stralloc.h"
+#include "iopause.h"
+#include "taia.h"
+
+#define DNS_C_IN "\0\1"
+#define DNS_C_ANY "\0\377"
+
+#define DNS_T_A "\0\1"
+#define DNS_T_NS "\0\2"
+#define DNS_T_CNAME "\0\5"
+#define DNS_T_SOA "\0\6"
+#define DNS_T_PTR "\0\14"
+#define DNS_T_HINFO "\0\15"
+#define DNS_T_MX "\0\17"
+#define DNS_T_TXT "\0\20"
+#define DNS_T_RP "\0\21"
+#define DNS_T_SIG "\0\30"
+#define DNS_T_KEY "\0\31"
+#define DNS_T_AAAA "\0\34"
+#define DNS_T_AXFR "\0\374"
+#define DNS_T_ANY "\0\377"
+
+struct dns_transmit {
+ char *query; /* 0, or dynamically allocated */
+ unsigned int querylen;
+ char *packet; /* 0, or dynamically allocated */
+ unsigned int packetlen;
+ int s1; /* 0, or 1 + an open file descriptor */
+ int tcpstate;
+ unsigned int udploop;
+ unsigned int curserver;
+ struct taia deadline;
+ unsigned int pos;
+ const char *servers;
+ char localip[4];
+ char qtype[2];
+} ;
+
+extern void dns_random_init(const char *);
+extern unsigned int dns_random(unsigned int);
+
+extern void dns_sortip(char *,unsigned int);
+
+extern void dns_domain_free(char **);
+extern int dns_domain_copy(char **,const char *);
+extern unsigned int dns_domain_length(const char *);
+extern int dns_domain_equal(const char *,const char *);
+extern int dns_domain_suffix(const char *,const char *);
+extern unsigned int dns_domain_suffixpos(const char *,const char *);
+extern int dns_domain_fromdot(char **,const char *,unsigned int);
+extern int dns_domain_todot_cat(stralloc *,const char *);
+
+extern unsigned int dns_packet_copy(const char *,unsigned int,unsigned int,char *,unsigned int);
+extern unsigned int dns_packet_getname(const char *,unsigned int,unsigned int,char **);
+extern unsigned int dns_packet_skipname(const char *,unsigned int,unsigned int);
+
+extern int dns_transmit_start(struct dns_transmit *,const char *,int,const char *,const char *,const char *);
+extern void dns_transmit_free(struct dns_transmit *);
+extern void dns_transmit_io(struct dns_transmit *,iopause_fd *,struct taia *);
+extern int dns_transmit_get(struct dns_transmit *,const iopause_fd *,const struct taia *);
+
+extern int dns_resolvconfip(char *);
+extern int dns_resolve(const char *,const char *);
+extern struct dns_transmit dns_resolve_tx;
+
+extern int dns_ip4_packet(stralloc *,const char *,unsigned int);
+extern int dns_ip4(stralloc *,const stralloc *);
+extern int dns_name_packet(stralloc *,const char *,unsigned int);
+extern void dns_name4_domain(char *,const char *);
+#define DNS_NAME4_DOMAIN 31
+extern int dns_name4(stralloc *,const char *);
+extern int dns_txt_packet(stralloc *,const char *,unsigned int);
+extern int dns_txt(stralloc *,const stralloc *);
+extern int dns_mx_packet(stralloc *,const char *,unsigned int);
+extern int dns_mx(stralloc *,const stralloc *);
+
+extern int dns_resolvconfrewrite(stralloc *);
+extern int dns_ip4_qualify_rules(stralloc *,stralloc *,const stralloc *,const stralloc *);
+extern int dns_ip4_qualify(stralloc *,stralloc *,const stralloc *);
+
+#endif
diff --git a/dns_dfd.c b/dns_dfd.c
new file mode 100644
index 0000000..7edef6f
--- /dev/null
+++ b/dns_dfd.c
@@ -0,0 +1,69 @@
+#include "error.h"
+#include "alloc.h"
+#include "byte.h"
+#include "dns.h"
+
+int dns_domain_fromdot(char **out,const char *buf,unsigned int n)
+{
+ char label[63];
+ unsigned int labellen = 0; /* <= sizeof label */
+ char name[255];
+ unsigned int namelen = 0; /* <= sizeof name */
+ char ch;
+ char *x;
+
+ errno = error_proto;
+
+ for (;;) {
+ if (!n) break;
+ ch = *buf++; --n;
+ if (ch == '.') {
+ if (labellen) {
+ if (namelen + labellen + 1 > sizeof name) return 0;
+ name[namelen++] = labellen;
+ byte_copy(name + namelen,labellen,label);
+ namelen += labellen;
+ labellen = 0;
+ }
+ continue;
+ }
+ if (ch == '\\') {
+ if (!n) break;
+ ch = *buf++; --n;
+ if ((ch >= '0') && (ch <= '7')) {
+ ch -= '0';
+ if (n && (*buf >= '0') && (*buf <= '7')) {
+ ch <<= 3;
+ ch += *buf - '0';
+ ++buf; --n;
+ if (n && (*buf >= '0') && (*buf <= '7')) {
+ ch <<= 3;
+ ch += *buf - '0';
+ ++buf; --n;
+ }
+ }
+ }
+ }
+ if (labellen >= sizeof label) return 0;
+ label[labellen++] = ch;
+ }
+
+ if (labellen) {
+ if (namelen + labellen + 1 > sizeof name) return 0;
+ name[namelen++] = labellen;
+ byte_copy(name + namelen,labellen,label);
+ namelen += labellen;
+ labellen = 0;
+ }
+
+ if (namelen + 1 > sizeof name) return 0;
+ name[namelen++] = 0;
+
+ x = alloc(namelen);
+ if (!x) return 0;
+ byte_copy(x,namelen,name);
+
+ if (*out) alloc_free(*out);
+ *out = x;
+ return 1;
+}
diff --git a/dns_domain.c b/dns_domain.c
new file mode 100644
index 0000000..b931f1d
--- /dev/null
+++ b/dns_domain.c
@@ -0,0 +1,74 @@
+#include "error.h"
+#include "alloc.h"
+#include "case.h"
+#include "byte.h"
+#include "dns.h"
+
+unsigned int dns_domain_length(const char *dn)
+{
+ const char *x;
+ unsigned char c;
+
+ x = dn;
+ while (c = *x++)
+ x += (unsigned int) c;
+ return x - dn;
+}
+
+void dns_domain_free(char **out)
+{
+ if (*out) {
+ alloc_free(*out);
+ *out = 0;
+ }
+}
+
+int dns_domain_copy(char **out,const char *in)
+{
+ unsigned int len;
+ char *x;
+
+ len = dns_domain_length(in);
+ x = alloc(len);
+ if (!x) return 0;
+ byte_copy(x,len,in);
+ if (*out) alloc_free(*out);
+ *out = x;
+ return 1;
+}
+
+int dns_domain_equal(const char *dn1,const char *dn2)
+{
+ unsigned int len;
+
+ len = dns_domain_length(dn1);
+ if (len != dns_domain_length(dn2)) return 0;
+
+ if (case_diffb(dn1,len,dn2)) return 0; /* safe since 63 < 'A' */
+ return 1;
+}
+
+int dns_domain_suffix(const char *big,const char *little)
+{
+ unsigned char c;
+
+ for (;;) {
+ if (dns_domain_equal(big,little)) return 1;
+ c = *big++;
+ if (!c) return 0;
+ big += c;
+ }
+}
+
+unsigned int dns_domain_suffixpos(const char *big,const char *little)
+{
+ const char *orig = big;
+ unsigned char c;
+
+ for (;;) {
+ if (dns_domain_equal(big,little)) return big - orig;
+ c = *big++;
+ if (!c) return 0;
+ big += c;
+ }
+}
diff --git a/dns_dtda.c b/dns_dtda.c
new file mode 100644
index 0000000..ba1db4f
--- /dev/null
+++ b/dns_dtda.c
@@ -0,0 +1,35 @@
+#include "stralloc.h"
+#include "dns.h"
+
+int dns_domain_todot_cat(stralloc *out,const char *d)
+{
+ char ch;
+ char ch2;
+ unsigned char ch3;
+ char buf[4];
+
+ if (!*d)
+ return stralloc_append(out,".");
+
+ for (;;) {
+ ch = *d++;
+ while (ch--) {
+ ch2 = *d++;
+ if ((ch2 >= 'A') && (ch2 <= 'Z'))
+ ch2 += 32;
+ if (((ch2 >= 'a') && (ch2 <= 'z')) || ((ch2 >= '0') && (ch2 <= '9')) || (ch2 == '-') || (ch2 == '_')) {
+ if (!stralloc_append(out,&ch2)) return 0;
+ }
+ else {
+ ch3 = ch2;
+ buf[3] = '0' + (ch3 & 7); ch3 >>= 3;
+ buf[2] = '0' + (ch3 & 7); ch3 >>= 3;
+ buf[1] = '0' + (ch3 & 7);
+ buf[0] = '\\';
+ if (!stralloc_catb(out,buf,4)) return 0;
+ }
+ }
+ if (!*d) return 1;
+ if (!stralloc_append(out,".")) return 0;
+ }
+}
diff --git a/dns_ip.c b/dns_ip.c
new file mode 100644
index 0000000..e7c3a9a
--- /dev/null
+++ b/dns_ip.c
@@ -0,0 +1,75 @@
+#include "stralloc.h"
+#include "uint16.h"
+#include "byte.h"
+#include "dns.h"
+
+int dns_ip4_packet(stralloc *out,const char *buf,unsigned int len)
+{
+ unsigned int pos;
+ char header[12];
+ uint16 numanswers;
+ uint16 datalen;
+
+ if (!stralloc_copys(out,"")) return -1;
+
+ pos = dns_packet_copy(buf,len,0,header,12); if (!pos) return -1;
+ uint16_unpack_big(header + 6,&numanswers);
+ pos = dns_packet_skipname(buf,len,pos); if (!pos) return -1;
+ pos += 4;
+
+ while (numanswers--) {
+ pos = dns_packet_skipname(buf,len,pos); if (!pos) return -1;
+ pos = dns_packet_copy(buf,len,pos,header,10); if (!pos) return -1;
+ uint16_unpack_big(header + 8,&datalen);
+ if (byte_equal(header,2,DNS_T_A))
+ if (byte_equal(header + 2,2,DNS_C_IN))
+ if (datalen == 4) {
+ if (!dns_packet_copy(buf,len,pos,header,4)) return -1;
+ if (!stralloc_catb(out,header,4)) return -1;
+ }
+ pos += datalen;
+ }
+
+ dns_sortip(out->s,out->len);
+ return 0;
+}
+
+static char *q = 0;
+
+int dns_ip4(stralloc *out,const stralloc *fqdn)
+{
+ unsigned int i;
+ char code;
+ char ch;
+
+ if (!stralloc_copys(out,"")) return -1;
+ code = 0;
+ for (i = 0;i <= fqdn->len;++i) {
+ if (i < fqdn->len)
+ ch = fqdn->s[i];
+ else
+ ch = '.';
+
+ if ((ch == '[') || (ch == ']')) continue;
+ if (ch == '.') {
+ if (!stralloc_append(out,&code)) return -1;
+ code = 0;
+ continue;
+ }
+ if ((ch >= '0') && (ch <= '9')) {
+ code *= 10;
+ code += ch - '0';
+ continue;
+ }
+
+ if (!dns_domain_fromdot(&q,fqdn->s,fqdn->len)) return -1;
+ if (dns_resolve(q,DNS_T_A) == -1) return -1;
+ if (dns_ip4_packet(out,dns_resolve_tx.packet,dns_resolve_tx.packetlen) == -1) return -1;
+ dns_transmit_free(&dns_resolve_tx);
+ dns_domain_free(&q);
+ return 0;
+ }
+
+ out->len &= ~3;
+ return 0;
+}
diff --git a/dns_ipq.c b/dns_ipq.c
new file mode 100644
index 0000000..5b65e23
--- /dev/null
+++ b/dns_ipq.c
@@ -0,0 +1,71 @@
+#include "stralloc.h"
+#include "case.h"
+#include "byte.h"
+#include "str.h"
+#include "dns.h"
+
+static int doit(stralloc *work,const char *rule)
+{
+ char ch;
+ unsigned int colon;
+ unsigned int prefixlen;
+
+ ch = *rule++;
+ if ((ch != '?') && (ch != '=') && (ch != '*') && (ch != '-')) return 1;
+ colon = str_chr(rule,':');
+ if (!rule[colon]) return 1;
+
+ if (work->len < colon) return 1;
+ prefixlen = work->len - colon;
+ if ((ch == '=') && prefixlen) return 1;
+ if (case_diffb(rule,colon,work->s + prefixlen)) return 1;
+ if (ch == '?') {
+ if (byte_chr(work->s,prefixlen,'.') < prefixlen) return 1;
+ if (byte_chr(work->s,prefixlen,'[') < prefixlen) return 1;
+ if (byte_chr(work->s,prefixlen,']') < prefixlen) return 1;
+ }
+
+ work->len = prefixlen;
+ if (ch == '-') work->len = 0;
+ return stralloc_cats(work,rule + colon + 1);
+}
+
+int dns_ip4_qualify_rules(stralloc *out,stralloc *fqdn,const stralloc *in,const stralloc *rules)
+{
+ unsigned int i;
+ unsigned int j;
+ unsigned int plus;
+ unsigned int fqdnlen;
+
+ if (!stralloc_copy(fqdn,in)) return -1;
+
+ for (j = i = 0;j < rules->len;++j)
+ if (!rules->s[j]) {
+ if (!doit(fqdn,rules->s + i)) return -1;
+ i = j + 1;
+ }
+
+ fqdnlen = fqdn->len;
+ plus = byte_chr(fqdn->s,fqdnlen,'+');
+ if (plus >= fqdnlen)
+ return dns_ip4(out,fqdn);
+
+ i = plus + 1;
+ for (;;) {
+ j = byte_chr(fqdn->s + i,fqdnlen - i,'+');
+ byte_copy(fqdn->s + plus,j,fqdn->s + i);
+ fqdn->len = plus + j;
+ if (dns_ip4(out,fqdn) == -1) return -1;
+ if (out->len) return 0;
+ i += j;
+ if (i >= fqdnlen) return 0;
+ ++i;
+ }
+}
+
+int dns_ip4_qualify(stralloc *out,stralloc *fqdn,const stralloc *in)
+{
+ static stralloc rules;
+ if (dns_resolvconfrewrite(&rules) == -1) return -1;
+ return dns_ip4_qualify_rules(out,fqdn,in,&rules);
+}
diff --git a/dns_mx.c b/dns_mx.c
new file mode 100644
index 0000000..8d38a7f
--- /dev/null
+++ b/dns_mx.c
@@ -0,0 +1,49 @@
+#include "stralloc.h"
+#include "byte.h"
+#include "uint16.h"
+#include "dns.h"
+
+static char *q = 0;
+
+int dns_mx_packet(stralloc *out,const char *buf,unsigned int len)
+{
+ unsigned int pos;
+ char header[12];
+ char pref[2];
+ uint16 numanswers;
+ uint16 datalen;
+
+ if (!stralloc_copys(out,"")) return -1;
+
+ pos = dns_packet_copy(buf,len,0,header,12); if (!pos) return -1;
+ uint16_unpack_big(header + 6,&numanswers);
+ pos = dns_packet_skipname(buf,len,pos); if (!pos) return -1;
+ pos += 4;
+
+ while (numanswers--) {
+ pos = dns_packet_skipname(buf,len,pos); if (!pos) return -1;
+ pos = dns_packet_copy(buf,len,pos,header,10); if (!pos) return -1;
+ uint16_unpack_big(header + 8,&datalen);
+ if (byte_equal(header,2,DNS_T_MX))
+ if (byte_equal(header + 2,2,DNS_C_IN)) {
+ if (!dns_packet_copy(buf,len,pos,pref,2)) return -1;
+ if (!dns_packet_getname(buf,len,pos + 2,&q)) return -1;
+ if (!stralloc_catb(out,pref,2)) return -1;
+ if (!dns_domain_todot_cat(out,q)) return -1;
+ if (!stralloc_0(out)) return -1;
+ }
+ pos += datalen;
+ }
+
+ return 0;
+}
+
+int dns_mx(stralloc *out,const stralloc *fqdn)
+{
+ if (!dns_domain_fromdot(&q,fqdn->s,fqdn->len)) return -1;
+ if (dns_resolve(q,DNS_T_MX) == -1) return -1;
+ if (dns_mx_packet(out,dns_resolve_tx.packet,dns_resolve_tx.packetlen) == -1) return -1;
+ dns_transmit_free(&dns_resolve_tx);
+ dns_domain_free(&q);
+ return 0;
+}
diff --git a/dns_name.c b/dns_name.c
new file mode 100644
index 0000000..6f7cdc3
--- /dev/null
+++ b/dns_name.c
@@ -0,0 +1,48 @@
+#include "stralloc.h"
+#include "uint16.h"
+#include "byte.h"
+#include "dns.h"
+
+static char *q = 0;
+
+int dns_name_packet(stralloc *out,const char *buf,unsigned int len)
+{
+ unsigned int pos;
+ char header[12];
+ uint16 numanswers;
+ uint16 datalen;
+
+ if (!stralloc_copys(out,"")) return -1;
+
+ pos = dns_packet_copy(buf,len,0,header,12); if (!pos) return -1;
+ uint16_unpack_big(header + 6,&numanswers);
+ pos = dns_packet_skipname(buf,len,pos); if (!pos) return -1;
+ pos += 4;
+
+ while (numanswers--) {
+ pos = dns_packet_skipname(buf,len,pos); if (!pos) return -1;
+ pos = dns_packet_copy(buf,len,pos,header,10); if (!pos) return -1;
+ uint16_unpack_big(header + 8,&datalen);
+ if (byte_equal(header,2,DNS_T_PTR))
+ if (byte_equal(header + 2,2,DNS_C_IN)) {
+ if (!dns_packet_getname(buf,len,pos,&q)) return -1;
+ if (!dns_domain_todot_cat(out,q)) return -1;
+ return 0;
+ }
+ pos += datalen;
+ }
+
+ return 0;
+}
+
+int dns_name4(stralloc *out,const char ip[4])
+{
+ char name[DNS_NAME4_DOMAIN];
+
+ dns_name4_domain(name,ip);
+ if (dns_resolve(name,DNS_T_PTR) == -1) return -1;
+ if (dns_name_packet(out,dns_resolve_tx.packet,dns_resolve_tx.packetlen) == -1) return -1;
+ dns_transmit_free(&dns_resolve_tx);
+ dns_domain_free(&q);
+ return 0;
+}
diff --git a/dns_nd.c b/dns_nd.c
new file mode 100644
index 0000000..aa54e5d
--- /dev/null
+++ b/dns_nd.c
@@ -0,0 +1,24 @@
+#include "byte.h"
+#include "fmt.h"
+#include "dns.h"
+
+void dns_name4_domain(char name[DNS_NAME4_DOMAIN],const char ip[4])
+{
+ unsigned int namelen;
+ unsigned int i;
+
+ namelen = 0;
+ i = fmt_ulong(name + namelen + 1,(unsigned long) (unsigned char) ip[3]);
+ name[namelen++] = i;
+ namelen += i;
+ i = fmt_ulong(name + namelen + 1,(unsigned long) (unsigned char) ip[2]);
+ name[namelen++] = i;
+ namelen += i;
+ i = fmt_ulong(name + namelen + 1,(unsigned long) (unsigned char) ip[1]);
+ name[namelen++] = i;
+ namelen += i;
+ i = fmt_ulong(name + namelen + 1,(unsigned long) (unsigned char) ip[0]);
+ name[namelen++] = i;
+ namelen += i;
+ byte_copy(name + namelen,14,"\7in-addr\4arpa\0");
+}
diff --git a/dns_packet.c b/dns_packet.c
new file mode 100644
index 0000000..6d66eeb
--- /dev/null
+++ b/dns_packet.c
@@ -0,0 +1,77 @@
+/*
+DNS should have used LZ77 instead of its own sophomoric compression algorithm.
+*/
+
+#include "error.h"
+#include "dns.h"
+
+unsigned int dns_packet_copy(const char *buf,unsigned int len,unsigned int pos,char *out,unsigned int outlen)
+{
+ while (outlen) {
+ if (pos >= len) { errno = error_proto; return 0; }
+ *out = buf[pos++];
+ ++out; --outlen;
+ }
+ return pos;
+}
+
+unsigned int dns_packet_skipname(const char *buf,unsigned int len,unsigned int pos)
+{
+ unsigned char ch;
+
+ for (;;) {
+ if (pos >= len) break;
+ ch = buf[pos++];
+ if (ch >= 192) return pos + 1;
+ if (ch >= 64) break;
+ if (!ch) return pos;
+ pos += ch;
+ }
+
+ errno = error_proto;
+ return 0;
+}
+
+unsigned int dns_packet_getname(const char *buf,unsigned int len,unsigned int pos,char **d)
+{
+ unsigned int loop = 0;
+ unsigned int state = 0;
+ unsigned int firstcompress = 0;
+ unsigned int where;
+ unsigned char ch;
+ char name[255];
+ unsigned int namelen = 0;
+
+ for (;;) {
+ if (pos >= len) goto PROTO; ch = buf[pos++];
+ if (++loop >= 1000) goto PROTO;
+
+ if (state) {
+ if (namelen + 1 > sizeof name) goto PROTO; name[namelen++] = ch;
+ --state;
+ }
+ else {
+ while (ch >= 192) {
+ where = ch; where -= 192; where <<= 8;
+ if (pos >= len) goto PROTO; ch = buf[pos++];
+ if (!firstcompress) firstcompress = pos;
+ pos = where + ch;
+ if (pos >= len) goto PROTO; ch = buf[pos++];
+ if (++loop >= 1000) goto PROTO;
+ }
+ if (ch >= 64) goto PROTO;
+ if (namelen + 1 > sizeof name) goto PROTO; name[namelen++] = ch;
+ if (!ch) break;
+ state = ch;
+ }
+ }
+
+ if (!dns_domain_copy(d,name)) return 0;
+
+ if (firstcompress) return firstcompress;
+ return pos;
+
+ PROTO:
+ errno = error_proto;
+ return 0;
+}
diff --git a/dns_random.c b/dns_random.c
new file mode 100644
index 0000000..2158ed4
--- /dev/null
+++ b/dns_random.c
@@ -0,0 +1,63 @@
+#include <unistd.h>
+#include "dns.h"
+#include "taia.h"
+#include "uint32.h"
+
+static uint32 seed[32];
+static uint32 in[12];
+static uint32 out[8];
+static int outleft = 0;
+
+#define ROTATE(x,b) (((x) << (b)) | ((x) >> (32 - (b))))
+#define MUSH(i,b) x = t[i] += (((x ^ seed[i]) + sum) ^ ROTATE(x,b));
+
+static void surf(void)
+{
+ uint32 t[12]; uint32 x; uint32 sum = 0;
+ int r; int i; int loop;
+
+ for (i = 0;i < 12;++i) t[i] = in[i] ^ seed[12 + i];
+ for (i = 0;i < 8;++i) out[i] = seed[24 + i];
+ x = t[11];
+ for (loop = 0;loop < 2;++loop) {
+ for (r = 0;r < 16;++r) {
+ sum += 0x9e3779b9;
+ MUSH(0,5) MUSH(1,7) MUSH(2,9) MUSH(3,13)
+ MUSH(4,5) MUSH(5,7) MUSH(6,9) MUSH(7,13)
+ MUSH(8,5) MUSH(9,7) MUSH(10,9) MUSH(11,13)
+ }
+ for (i = 0;i < 8;++i) out[i] ^= t[i + 4];
+ }
+}
+
+void dns_random_init(const char data[128])
+{
+ int i;
+ struct taia t;
+ char tpack[16];
+
+ for (i = 0;i < 32;++i)
+ uint32_unpack(data + 4 * i,seed + i);
+
+ taia_now(&t);
+ taia_pack(tpack,&t);
+ for (i = 0;i < 4;++i)
+ uint32_unpack(tpack + 4 * i,in + 4 + i);
+
+ in[8] = getpid();
+ in[9] = getppid();
+ /* more space in 10 and 11, but this is probably enough */
+}
+
+unsigned int dns_random(unsigned int n)
+{
+ if (!n) return 0;
+
+ if (!outleft) {
+ if (!++in[0]) if (!++in[1]) if (!++in[2]) ++in[3];
+ surf();
+ outleft = 8;
+ }
+
+ return out[--outleft] % n;
+}
diff --git a/dns_rcip.c b/dns_rcip.c
new file mode 100644
index 0000000..97bd8f5
--- /dev/null
+++ b/dns_rcip.c
@@ -0,0 +1,86 @@
+#include "taia.h"
+#include "openreadclose.h"
+#include "byte.h"
+#include "ip4.h"
+#include "env.h"
+#include "dns.h"
+
+static stralloc data = {0};
+
+static int init(char ip[64])
+{
+ int i;
+ int j;
+ int iplen = 0;
+ char *x;
+
+ x = env_get("DNSCACHEIP");
+ if (x)
+ while (iplen <= 60) {
+ if (*x == '.')
+ ++x;
+ else {
+ i = ip4_scan(x,ip + iplen);
+ if (!i) break;
+ x += i;
+ iplen += 4;
+ }
+ }
+
+ if (!iplen) {
+ i = openreadclose("/etc/resolv.conf",&data,64);
+ if (i == -1) return -1;
+ if (i) {
+ if (!stralloc_append(&data,"\n")) return -1;
+ i = 0;
+ for (j = 0;j < data.len;++j)
+ if (data.s[j] == '\n') {
+ if (byte_equal("nameserver ",11,data.s + i) || byte_equal("nameserver\t",11,data.s + i)) {
+ i += 10;
+ while ((data.s[i] == ' ') || (data.s[i] == '\t'))
+ ++i;
+ if (iplen <= 60)
+ if (ip4_scan(data.s + i,ip + iplen)) {
+ if (byte_equal(ip + iplen,4,"\0\0\0\0"))
+ byte_copy(ip + iplen,4,"\177\0\0\1");
+ iplen += 4;
+ }
+ }
+ i = j + 1;
+ }
+ }
+ }
+
+ if (!iplen) {
+ byte_copy(ip,4,"\177\0\0\1");
+ iplen = 4;
+ }
+ byte_zero(ip + iplen,64 - iplen);
+ return 0;
+}
+
+static int ok = 0;
+static unsigned int uses;
+static struct taia deadline;
+static char ip[64]; /* defined if ok */
+
+int dns_resolvconfip(char s[64])
+{
+ struct taia now;
+
+ taia_now(&now);
+ if (taia_less(&deadline,&now)) ok = 0;
+ if (!uses) ok = 0;
+
+ if (!ok) {
+ if (init(ip) == -1) return -1;
+ taia_uint(&deadline,600);
+ taia_add(&deadline,&now,&deadline);
+ uses = 10000;
+ ok = 1;
+ }
+
+ --uses;
+ byte_copy(s,64,ip);
+ return 0;
+}
diff --git a/dns_rcrw.c b/dns_rcrw.c
new file mode 100644
index 0000000..a43f39f
--- /dev/null
+++ b/dns_rcrw.c
@@ -0,0 +1,131 @@
+#include <unistd.h>
+#include "taia.h"
+#include "env.h"
+#include "byte.h"
+#include "str.h"
+#include "openreadclose.h"
+#include "dns.h"
+
+static stralloc data = {0};
+
+static int init(stralloc *rules)
+{
+ char host[256];
+ const char *x;
+ int i;
+ int j;
+ int k;
+
+ if (!stralloc_copys(rules,"")) return -1;
+
+ x = env_get("DNSREWRITEFILE");
+ if (!x) x = "/etc/dnsrewrite";
+
+ i = openreadclose(x,&data,64);
+ if (i == -1) return -1;
+
+ if (i) {
+ if (!stralloc_append(&data,"\n")) return -1;
+ i = 0;
+ for (j = 0;j < data.len;++j)
+ if (data.s[j] == '\n') {
+ if (!stralloc_catb(rules,data.s + i,j - i)) return -1;
+ while (rules->len) {
+ if (rules->s[rules->len - 1] != ' ')
+ if (rules->s[rules->len - 1] != '\t')
+ if (rules->s[rules->len - 1] != '\r')
+ break;
+ --rules->len;
+ }
+ if (!stralloc_0(rules)) return -1;
+ i = j + 1;
+ }
+ return 0;
+ }
+
+ x = env_get("LOCALDOMAIN");
+ if (x) {
+ if (!stralloc_copys(&data,x)) return -1;
+ if (!stralloc_append(&data," ")) return -1;
+ if (!stralloc_copys(rules,"?:")) return -1;
+ i = 0;
+ for (j = 0;j < data.len;++j)
+ if (data.s[j] == ' ') {
+ if (!stralloc_cats(rules,"+.")) return -1;
+ if (!stralloc_catb(rules,data.s + i,j - i)) return -1;
+ i = j + 1;
+ }
+ if (!stralloc_0(rules)) return -1;
+ if (!stralloc_cats(rules,"*.:")) return -1;
+ if (!stralloc_0(rules)) return -1;
+ return 0;
+ }
+
+ i = openreadclose("/etc/resolv.conf",&data,64);
+ if (i == -1) return -1;
+
+ if (i) {
+ if (!stralloc_append(&data,"\n")) return -1;
+ i = 0;
+ for (j = 0;j < data.len;++j)
+ if (data.s[j] == '\n') {
+ if (byte_equal("search ",7,data.s + i) || byte_equal("search\t",7,data.s + i) || byte_equal("domain ",7,data.s + i) || byte_equal("domain\t",7,data.s + i)) {
+ if (!stralloc_copys(rules,"?:")) return -1;
+ i += 7;
+ while (i < j) {
+ k = byte_chr(data.s + i,j - i,' ');
+ k = byte_chr(data.s + i,k,'\t');
+ if (!k) { ++i; continue; }
+ if (!stralloc_cats(rules,"+.")) return -1;
+ if (!stralloc_catb(rules,data.s + i,k)) return -1;
+ i += k;
+ }
+ if (!stralloc_0(rules)) return -1;
+ if (!stralloc_cats(rules,"*.:")) return -1;
+ if (!stralloc_0(rules)) return -1;
+ return 0;
+ }
+ i = j + 1;
+ }
+ }
+
+ host[0] = 0;
+ if (gethostname(host,sizeof host) == -1) return -1;
+ host[(sizeof host) - 1] = 0;
+ i = str_chr(host,'.');
+ if (host[i]) {
+ if (!stralloc_copys(rules,"?:")) return -1;
+ if (!stralloc_cats(rules,host + i)) return -1;
+ if (!stralloc_0(rules)) return -1;
+ }
+ if (!stralloc_cats(rules,"*.:")) return -1;
+ if (!stralloc_0(rules)) return -1;
+
+ return 0;
+}
+
+static int ok = 0;
+static unsigned int uses;
+static struct taia deadline;
+static stralloc rules = {0}; /* defined if ok */
+
+int dns_resolvconfrewrite(stralloc *out)
+{
+ struct taia now;
+
+ taia_now(&now);
+ if (taia_less(&deadline,&now)) ok = 0;
+ if (!uses) ok = 0;
+
+ if (!ok) {
+ if (init(&rules) == -1) return -1;
+ taia_uint(&deadline,600);
+ taia_add(&deadline,&now,&deadline);
+ uses = 10000;
+ ok = 1;
+ }
+
+ --uses;
+ if (!stralloc_copy(out,&rules)) return -1;
+ return 0;
+}
diff --git a/dns_resolve.c b/dns_resolve.c
new file mode 100644
index 0000000..8bdea0d
--- /dev/null
+++ b/dns_resolve.c
@@ -0,0 +1,29 @@
+#include "iopause.h"
+#include "taia.h"
+#include "byte.h"
+#include "dns.h"
+
+struct dns_transmit dns_resolve_tx = {0};
+
+int dns_resolve(const char *q,const char qtype[2])
+{
+ struct taia stamp;
+ struct taia deadline;
+ char servers[64];
+ iopause_fd x[1];
+ int r;
+
+ if (dns_resolvconfip(servers) == -1) return -1;
+ if (dns_transmit_start(&dns_resolve_tx,servers,1,q,qtype,"\0\0\0\0") == -1) return -1;
+
+ for (;;) {
+ taia_now(&stamp);
+ taia_uint(&deadline,120);
+ taia_add(&deadline,&deadline,&stamp);
+ dns_transmit_io(&dns_resolve_tx,x,&deadline);
+ iopause(x,1,&deadline,&stamp);
+ r = dns_transmit_get(&dns_resolve_tx,x,&stamp);
+ if (r == -1) return -1;
+ if (r == 1) return 0;
+ }
+}
diff --git a/dns_sortip.c b/dns_sortip.c
new file mode 100644
index 0000000..af9b235
--- /dev/null
+++ b/dns_sortip.c
@@ -0,0 +1,20 @@
+#include "byte.h"
+#include "dns.h"
+
+/* XXX: sort servers by configurable notion of closeness? */
+/* XXX: pay attention to competence of each server? */
+
+void dns_sortip(char *s,unsigned int n)
+{
+ unsigned int i;
+ char tmp[4];
+
+ n >>= 2;
+ while (n > 1) {
+ i = dns_random(n);
+ --n;
+ byte_copy(tmp,4,s + (i << 2));
+ byte_copy(s + (i << 2),4,s + (n << 2));
+ byte_copy(s + (n << 2),4,tmp);
+ }
+}
diff --git a/dns_transmit.c b/dns_transmit.c
new file mode 100644
index 0000000..4d6e39f
--- /dev/null
+++ b/dns_transmit.c
@@ -0,0 +1,366 @@
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include "socket.h"
+#include "alloc.h"
+#include "error.h"
+#include "byte.h"
+#include "uint16.h"
+#include "dns.h"
+
+static int serverwantstcp(const char *buf,unsigned int len)
+{
+ char out[12];
+
+ if (!dns_packet_copy(buf,len,0,out,12)) return 1;
+ if (out[2] & 2) return 1;
+ return 0;
+}
+
+static int serverfailed(const char *buf,unsigned int len)
+{
+ char out[12];
+ unsigned int rcode;
+
+ if (!dns_packet_copy(buf,len,0,out,12)) return 1;
+ rcode = out[3];
+ rcode &= 15;
+ if (rcode && (rcode != 3)) { errno = error_again; return 1; }
+ return 0;
+}
+
+static int irrelevant(const struct dns_transmit *d,const char *buf,unsigned int len)
+{
+ char out[12];
+ char *dn;
+ unsigned int pos;
+
+ pos = dns_packet_copy(buf,len,0,out,12); if (!pos) return 1;
+ if (byte_diff(out,2,d->query + 2)) return 1;
+ if (out[4] != 0) return 1;
+ if (out[5] != 1) return 1;
+
+ dn = 0;
+ pos = dns_packet_getname(buf,len,pos,&dn); if (!pos) return 1;
+ if (!dns_domain_equal(dn,d->query + 14)) { alloc_free(dn); return 1; }
+ alloc_free(dn);
+
+ pos = dns_packet_copy(buf,len,pos,out,4); if (!pos) return 1;
+ if (byte_diff(out,2,d->qtype)) return 1;
+ if (byte_diff(out + 2,2,DNS_C_IN)) return 1;
+
+ return 0;
+}
+
+static void packetfree(struct dns_transmit *d)
+{
+ if (!d->packet) return;
+ alloc_free(d->packet);
+ d->packet = 0;
+}
+
+static void queryfree(struct dns_transmit *d)
+{
+ if (!d->query) return;
+ alloc_free(d->query);
+ d->query = 0;
+}
+
+static void socketfree(struct dns_transmit *d)
+{
+ if (!d->s1) return;
+ close(d->s1 - 1);
+ d->s1 = 0;
+}
+
+void dns_transmit_free(struct dns_transmit *d)
+{
+ queryfree(d);
+ socketfree(d);
+ packetfree(d);
+}
+
+static int randombind(struct dns_transmit *d)
+{
+ int j;
+
+ for (j = 0;j < 10;++j)
+ if (socket_bind4(d->s1 - 1,d->localip,1025 + dns_random(64510)) == 0)
+ return 0;
+ if (socket_bind4(d->s1 - 1,d->localip,0) == 0)
+ return 0;
+ return -1;
+}
+
+static const int timeouts[4] = { 1, 3, 11, 45 };
+
+static int thisudp(struct dns_transmit *d)
+{
+ const char *ip;
+
+ socketfree(d);
+
+ while (d->udploop < 4) {
+ for (;d->curserver < 16;++d->curserver) {
+ ip = d->servers + 4 * d->curserver;
+ if (byte_diff(ip,4,"\0\0\0\0")) {
+ d->query[2] = dns_random(256);
+ d->query[3] = dns_random(256);
+
+ d->s1 = 1 + socket_udp();
+ if (!d->s1) { dns_transmit_free(d); return -1; }
+ if (randombind(d) == -1) { dns_transmit_free(d); return -1; }
+
+ if (socket_connect4(d->s1 - 1,ip,53) == 0)
+ if (send(d->s1 - 1,d->query + 2,d->querylen - 2,0) == d->querylen - 2) {
+ struct taia now;
+ taia_now(&now);
+ taia_uint(&d->deadline,timeouts[d->udploop]);
+ taia_add(&d->deadline,&d->deadline,&now);
+ d->tcpstate = 0;
+ return 0;
+ }
+
+ socketfree(d);
+ }
+ }
+
+ ++d->udploop;
+ d->curserver = 0;
+ }
+
+ dns_transmit_free(d); return -1;
+}
+
+static int firstudp(struct dns_transmit *d)
+{
+ d->curserver = 0;
+ return thisudp(d);
+}
+
+static int nextudp(struct dns_transmit *d)
+{
+ ++d->curserver;
+ return thisudp(d);
+}
+
+static int thistcp(struct dns_transmit *d)
+{
+ struct taia now;
+ const char *ip;
+
+ socketfree(d);
+ packetfree(d);
+
+ for (;d->curserver < 16;++d->curserver) {
+ ip = d->servers + 4 * d->curserver;
+ if (byte_diff(ip,4,"\0\0\0\0")) {
+ d->query[2] = dns_random(256);
+ d->query[3] = dns_random(256);
+
+ d->s1 = 1 + socket_tcp();
+ if (!d->s1) { dns_transmit_free(d); return -1; }
+ if (randombind(d) == -1) { dns_transmit_free(d); return -1; }
+
+ taia_now(&now);
+ taia_uint(&d->deadline,10);
+ taia_add(&d->deadline,&d->deadline,&now);
+ if (socket_connect4(d->s1 - 1,ip,53) == 0) {
+ d->tcpstate = 2;
+ return 0;
+ }
+ if ((errno == error_inprogress) || (errno == error_wouldblock)) {
+ d->tcpstate = 1;
+ return 0;
+ }
+
+ socketfree(d);
+ }
+ }
+
+ dns_transmit_free(d); return -1;
+}
+
+static int firsttcp(struct dns_transmit *d)
+{
+ d->curserver = 0;
+ return thistcp(d);
+}
+
+static int nexttcp(struct dns_transmit *d)
+{
+ ++d->curserver;
+ return thistcp(d);
+}
+
+int dns_transmit_start(struct dns_transmit *d,const char servers[64],int flagrecursive,const char *q,const char qtype[2],const char localip[4])
+{
+ unsigned int len;
+
+ dns_transmit_free(d);
+ errno = error_io;
+
+ len = dns_domain_length(q);
+ d->querylen = len + 18;
+ d->query = alloc(d->querylen);
+ if (!d->query) return -1;
+
+ uint16_pack_big(d->query,len + 16);
+ byte_copy(d->query + 2,12,flagrecursive ? "\0\0\1\0\0\1\0\0\0\0\0\0" : "\0\0\0\0\0\1\0\0\0\0\0\0gcc-bug-workaround");
+ byte_copy(d->query + 14,len,q);
+ byte_copy(d->query + 14 + len,2,qtype);
+ byte_copy(d->query + 16 + len,2,DNS_C_IN);
+
+ byte_copy(d->qtype,2,qtype);
+ d->servers = servers;
+ byte_copy(d->localip,4,localip);
+
+ d->udploop = flagrecursive ? 1 : 0;
+
+ if (len + 16 > 512) return firsttcp(d);
+ return firstudp(d);
+}
+
+void dns_transmit_io(struct dns_transmit *d,iopause_fd *x,struct taia *deadline)
+{
+ x->fd = d->s1 - 1;
+
+ switch(d->tcpstate) {
+ case 0: case 3: case 4: case 5:
+ x->events = IOPAUSE_READ;
+ break;
+ case 1: case 2:
+ x->events = IOPAUSE_WRITE;
+ break;
+ }
+
+ if (taia_less(&d->deadline,deadline))
+ *deadline = d->deadline;
+}
+
+int dns_transmit_get(struct dns_transmit *d,const iopause_fd *x,const struct taia *when)
+{
+ char udpbuf[513];
+ unsigned char ch;
+ int r;
+ int fd;
+
+ errno = error_io;
+ fd = d->s1 - 1;
+
+ if (!x->revents) {
+ if (taia_less(when,&d->deadline)) return 0;
+ errno = error_timeout;
+ if (d->tcpstate == 0) return nextudp(d);
+ return nexttcp(d);
+ }
+
+ if (d->tcpstate == 0) {
+/*
+have attempted to send UDP query to each server udploop times
+have sent query to curserver on UDP socket s
+*/
+ r = recv(fd,udpbuf,sizeof udpbuf,0);
+ if (r <= 0) {
+ if (errno == error_connrefused) if (d->udploop == 2) return 0;
+ return nextudp(d);
+ }
+ if (r + 1 > sizeof udpbuf) return 0;
+
+ if (irrelevant(d,udpbuf,r)) return 0;
+ if (serverwantstcp(udpbuf,r)) return firsttcp(d);
+ if (serverfailed(udpbuf,r)) {
+ if (d->udploop == 2) return 0;
+ return nextudp(d);
+ }
+ socketfree(d);
+
+ d->packetlen = r;
+ d->packet = alloc(d->packetlen);
+ if (!d->packet) { dns_transmit_free(d); return -1; }
+ byte_copy(d->packet,d->packetlen,udpbuf);
+ queryfree(d);
+ return 1;
+ }
+
+ if (d->tcpstate == 1) {
+/*
+have sent connection attempt to curserver on TCP socket s
+pos not defined
+*/
+ if (!socket_connected(fd)) return nexttcp(d);
+ d->pos = 0;
+ d->tcpstate = 2;
+ return 0;
+ }
+
+ if (d->tcpstate == 2) {
+/*
+have connection to curserver on TCP socket s
+have sent pos bytes of query
+*/
+ r = write(fd,d->query + d->pos,d->querylen - d->pos);
+ if (r <= 0) return nexttcp(d);
+ d->pos += r;
+ if (d->pos == d->querylen) {
+ struct taia now;
+ taia_now(&now);
+ taia_uint(&d->deadline,10);
+ taia_add(&d->deadline,&d->deadline,&now);
+ d->tcpstate = 3;
+ }
+ return 0;
+ }
+
+ if (d->tcpstate == 3) {
+/*
+have sent entire query to curserver on TCP socket s
+pos not defined
+*/
+ r = read(fd,&ch,1);
+ if (r <= 0) return nexttcp(d);
+ d->packetlen = ch;
+ d->tcpstate = 4;
+ return 0;
+ }
+
+ if (d->tcpstate == 4) {
+/*
+have sent entire query to curserver on TCP socket s
+pos not defined
+have received one byte of packet length into packetlen
+*/
+ r = read(fd,&ch,1);
+ if (r <= 0) return nexttcp(d);
+ d->packetlen <<= 8;
+ d->packetlen += ch;
+ d->tcpstate = 5;
+ d->pos = 0;
+ d->packet = alloc(d->packetlen);
+ if (!d->packet) { dns_transmit_free(d); return -1; }
+ return 0;
+ }
+
+ if (d->tcpstate == 5) {
+/*
+have sent entire query to curserver on TCP socket s
+have received entire packet length into packetlen
+packet is allocated
+have received pos bytes of packet
+*/
+ r = read(fd,d->packet + d->pos,d->packetlen - d->pos);
+ if (r <= 0) return nexttcp(d);
+ d->pos += r;
+ if (d->pos < d->packetlen) return 0;
+
+ socketfree(d);
+ if (irrelevant(d,d->packet,d->packetlen)) return nexttcp(d);
+ if (serverwantstcp(d->packet,d->packetlen)) return nexttcp(d);
+ if (serverfailed(d->packet,d->packetlen)) return nexttcp(d);
+
+ queryfree(d);
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/dns_txt.c b/dns_txt.c
new file mode 100644
index 0000000..44deafe
--- /dev/null
+++ b/dns_txt.c
@@ -0,0 +1,59 @@
+#include "stralloc.h"
+#include "uint16.h"
+#include "byte.h"
+#include "dns.h"
+
+int dns_txt_packet(stralloc *out,const char *buf,unsigned int len)
+{
+ unsigned int pos;
+ char header[12];
+ uint16 numanswers;
+ uint16 datalen;
+ char ch;
+ unsigned int txtlen;
+ int i;
+
+ if (!stralloc_copys(out,"")) return -1;
+
+ pos = dns_packet_copy(buf,len,0,header,12); if (!pos) return -1;
+ uint16_unpack_big(header + 6,&numanswers);
+ pos = dns_packet_skipname(buf,len,pos); if (!pos) return -1;
+ pos += 4;
+
+ while (numanswers--) {
+ pos = dns_packet_skipname(buf,len,pos); if (!pos) return -1;
+ pos = dns_packet_copy(buf,len,pos,header,10); if (!pos) return -1;
+ uint16_unpack_big(header + 8,&datalen);
+ if (byte_equal(header,2,DNS_T_TXT))
+ if (byte_equal(header + 2,2,DNS_C_IN)) {
+ if (pos + datalen > len) return -1;
+ txtlen = 0;
+ for (i = 0;i < datalen;++i) {
+ ch = buf[pos + i];
+ if (!txtlen)
+ txtlen = (unsigned char) ch;
+ else {
+ --txtlen;
+ if (ch < 32) ch = '?';
+ if (ch > 126) ch = '?';
+ if (!stralloc_append(out,&ch)) return -1;
+ }
+ }
+ }
+ pos += datalen;
+ }
+
+ return 0;
+}
+
+static char *q = 0;
+
+int dns_txt(stralloc *out,const stralloc *fqdn)
+{
+ if (!dns_domain_fromdot(&q,fqdn->s,fqdn->len)) return -1;
+ if (dns_resolve(q,DNS_T_TXT) == -1) return -1;
+ if (dns_txt_packet(out,dns_resolve_tx.packet,dns_resolve_tx.packetlen) == -1) return -1;
+ dns_transmit_free(&dns_resolve_tx);
+ dns_domain_free(&q);
+ return 0;
+}
diff --git a/dnscache-conf.c b/dnscache-conf.c
new file mode 100644
index 0000000..e8c12be
--- /dev/null
+++ b/dnscache-conf.c
@@ -0,0 +1,169 @@
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "hasdevtcp.h"
+#ifdef HASDEVTCP
+#include <sys/mkdev.h>
+#endif
+#include <pwd.h>
+#include "strerr.h"
+#include "buffer.h"
+#include "uint32.h"
+#include "taia.h"
+#include "str.h"
+#include "open.h"
+#include "error.h"
+#include "exit.h"
+#include "auto_home.h"
+#include "generic-conf.h"
+
+#define FATAL "dnscache-conf: fatal: "
+
+void usage(void)
+{
+ strerr_die1x(100,"dnscache-conf: usage: dnscache-conf acct logacct /dnscache [ myip ]");
+}
+
+int fdrootservers;
+char rootserversbuf[64];
+buffer ssrootservers;
+
+char *dir;
+char *user;
+char *loguser;
+struct passwd *pw;
+const char *myip;
+
+uint32 seed[32];
+int seedpos = 0;
+
+void seed_adduint32(uint32 u)
+{
+ int i;
+
+ seed[seedpos] += u;
+ if (++seedpos == 32) {
+ for (i = 0;i < 32;++i) {
+ u = ((u ^ seed[i]) + 0x9e3779b9) ^ (u << 7) ^ (u >> 25);
+ seed[i] = u;
+ }
+ seedpos = 0;
+ }
+}
+
+void seed_addtime(void)
+{
+ struct taia t;
+ char tpack[TAIA_PACK];
+ int i;
+
+ taia_now(&t);
+ taia_pack(tpack,&t);
+ for (i = 0;i < TAIA_PACK;++i)
+ seed_adduint32(tpack[i]);
+}
+
+int main(int argc,char **argv)
+{
+ seed_addtime();
+ seed_adduint32(getpid());
+ seed_adduint32(getppid());
+ seed_adduint32(getuid());
+ seed_adduint32(getgid());
+
+ user = argv[1];
+ if (!user) usage();
+ loguser = argv[2];
+ if (!loguser) usage();
+ dir = argv[3];
+ if (!dir) usage();
+ if (dir[0] != '/') usage();
+ myip = argv[4];
+ if (!myip) myip = "127.0.0.1";
+
+ pw = getpwnam(loguser);
+ seed_addtime();
+ if (!pw)
+ strerr_die3x(111,FATAL,"unknown account ",loguser);
+
+ if (chdir(auto_home) == -1)
+ strerr_die4sys(111,FATAL,"unable to switch to ",auto_home,": ");
+
+ fdrootservers = open_read("/etc/dnsroots.local");
+ if (fdrootservers == -1) {
+ if (errno != error_noent)
+ strerr_die2sys(111,FATAL,"unable to open /etc/dnsroots.local: ");
+ fdrootservers = open_read("/etc/dnsroots.global");
+ if (fdrootservers == -1)
+ strerr_die2sys(111,FATAL,"unable to open /etc/dnsroots.global: ");
+ }
+
+ init(dir,FATAL);
+
+ seed_addtime(); makedir("log");
+ seed_addtime(); perm(02755);
+ seed_addtime(); makedir("log/main");
+ seed_addtime(); owner(pw->pw_uid,pw->pw_gid);
+ seed_addtime(); perm(02755);
+ seed_addtime(); start("log/status"); finish();
+ seed_addtime(); owner(pw->pw_uid,pw->pw_gid);
+ seed_addtime(); perm(0644);
+ seed_addtime(); makedir("env");
+ seed_addtime(); perm(02755);
+ seed_addtime(); start("env/ROOT"); outs(dir); outs("/root\n"); finish();
+ seed_addtime(); perm(0644);
+ seed_addtime(); start("env/IP"); outs(myip); outs("\n"); finish();
+ seed_addtime(); perm(0644);
+ seed_addtime(); start("env/IPSEND"); outs("0.0.0.0\n"); finish();
+ seed_addtime(); perm(0644);
+ seed_addtime(); start("env/CACHESIZE"); outs("1000000\n"); finish();
+ seed_addtime(); perm(0644);
+ seed_addtime(); start("env/DATALIMIT"); outs("3000000\n"); finish();
+ seed_addtime(); perm(0644);
+ seed_addtime(); start("run");
+ outs("#!/bin/sh\nexec 2>&1\nexec <seed\nexec envdir ./env sh -c '\n exec envuidgid "); outs(user);
+ outs(" softlimit -o250 -d \"$DATALIMIT\" ");
+ outs(auto_home); outs("/bin/dnscache\n'\n"); finish();
+ seed_addtime(); perm(0755);
+ seed_addtime(); start("log/run");
+ outs("#!/bin/sh\nexec setuidgid "); outs(loguser);
+ outs(" multilog t ./main\n"); finish();
+ seed_addtime(); perm(0755);
+ seed_addtime(); makedir("root");
+ seed_addtime(); perm(02755);
+ seed_addtime(); makedir("root/ip");
+ seed_addtime(); perm(02755);
+ seed_addtime(); start("root/ip/127.0.0.1"); finish();
+ seed_addtime(); perm(0600);
+ seed_addtime(); makedir("root/servers");
+ seed_addtime(); perm(02755);
+ seed_addtime(); start("root/servers/@");
+ buffer_init(&ssrootservers,buffer_unixread,fdrootservers,rootserversbuf,sizeof rootserversbuf);
+ copyfrom(&ssrootservers);
+ finish();
+ seed_addtime(); perm(0644);
+ seed_addtime();
+
+ start("seed"); out((char *) seed,128); finish();
+ perm(0600);
+
+#ifdef HASDEVTCP
+ makedir("root/etc");
+ perm(02755);
+ makedir("root/dev");
+ perm(02755);
+ start("root/etc/netconfig");
+ outs("tcp tpi_cots_ord v inet tcp /dev/tcp -\n");
+ outs("udp tpi_clts v inet udp /dev/udp -\n");
+ finish();
+ perm(0645);
+ umask(000);
+ if (mknod("root/dev/tcp",S_IFCHR | 0667,makedev(11,42)) == -1)
+ strerr_die4sys(111,FATAL,"unable to create device ",dir,"/root/dev/tcp: ");
+ if (mknod("root/dev/udp",S_IFCHR | 0667,makedev(11,41)) == -1)
+ strerr_die4sys(111,FATAL,"unable to create device ",dir,"/root/dev/udp: ");
+ umask(022);
+#endif
+
+ _exit(0);
+}
diff --git a/dnscache.c b/dnscache.c
new file mode 100644
index 0000000..8c899a3
--- /dev/null
+++ b/dnscache.c
@@ -0,0 +1,447 @@
+#include <unistd.h>
+#include "env.h"
+#include "exit.h"
+#include "scan.h"
+#include "strerr.h"
+#include "error.h"
+#include "ip4.h"
+#include "uint16.h"
+#include "uint64.h"
+#include "socket.h"
+#include "dns.h"
+#include "taia.h"
+#include "byte.h"
+#include "roots.h"
+#include "fmt.h"
+#include "iopause.h"
+#include "query.h"
+#include "alloc.h"
+#include "response.h"
+#include "cache.h"
+#include "ndelay.h"
+#include "log.h"
+#include "okclient.h"
+#include "droproot.h"
+
+static int packetquery(char *buf,unsigned int len,char **q,char qtype[2],char qclass[2],char id[2])
+{
+ unsigned int pos;
+ char header[12];
+
+ errno = error_proto;
+ pos = dns_packet_copy(buf,len,0,header,12); if (!pos) return 0;
+ if (header[2] & 128) return 0; /* must not respond to responses */
+ if (!(header[2] & 1)) return 0; /* do not respond to non-recursive queries */
+ if (header[2] & 120) return 0;
+ if (header[2] & 2) return 0;
+ if (byte_diff(header + 4,2,"\0\1")) return 0;
+
+ pos = dns_packet_getname(buf,len,pos,q); if (!pos) return 0;
+ pos = dns_packet_copy(buf,len,pos,qtype,2); if (!pos) return 0;
+ pos = dns_packet_copy(buf,len,pos,qclass,2); if (!pos) return 0;
+ if (byte_diff(qclass,2,DNS_C_IN) && byte_diff(qclass,2,DNS_C_ANY)) return 0;
+
+ byte_copy(id,2,header);
+ return 1;
+}
+
+
+static char myipoutgoing[4];
+static char myipincoming[4];
+static char buf[1024];
+uint64 numqueries = 0;
+
+
+static int udp53;
+
+#define MAXUDP 200
+static struct udpclient {
+ struct query q;
+ struct taia start;
+ uint64 active; /* query number, if active; otherwise 0 */
+ iopause_fd *io;
+ char ip[4];
+ uint16 port;
+ char id[2];
+} u[MAXUDP];
+int uactive = 0;
+
+void u_drop(int j)
+{
+ if (!u[j].active) return;
+ log_querydrop(&u[j].active);
+ u[j].active = 0; --uactive;
+}
+
+void u_respond(int j)
+{
+ if (!u[j].active) return;
+ response_id(u[j].id);
+ if (response_len > 512) response_tc();
+ socket_send4(udp53,response,response_len,u[j].ip,u[j].port);
+ log_querydone(&u[j].active,response_len);
+ u[j].active = 0; --uactive;
+}
+
+void u_new(void)
+{
+ int j;
+ int i;
+ struct udpclient *x;
+ int len;
+ static char *q = 0;
+ char qtype[2];
+ char qclass[2];
+
+ for (j = 0;j < MAXUDP;++j)
+ if (!u[j].active)
+ break;
+
+ if (j >= MAXUDP) {
+ j = 0;
+ for (i = 1;i < MAXUDP;++i)
+ if (taia_less(&u[i].start,&u[j].start))
+ j = i;
+ errno = error_timeout;
+ u_drop(j);
+ }
+
+ x = u + j;
+ taia_now(&x->start);
+
+ len = socket_recv4(udp53,buf,sizeof buf,x->ip,&x->port);
+ if (len == -1) return;
+ if (len >= sizeof buf) return;
+ if (x->port < 1024) if (x->port != 53) return;
+ if (!okclient(x->ip)) return;
+
+ if (!packetquery(buf,len,&q,qtype,qclass,x->id)) return;
+
+ x->active = ++numqueries; ++uactive;
+ log_query(&x->active,x->ip,x->port,x->id,q,qtype);
+ switch(query_start(&x->q,q,qtype,qclass,myipoutgoing)) {
+ case -1:
+ u_drop(j);
+ return;
+ case 1:
+ u_respond(j);
+ }
+}
+
+
+static int tcp53;
+
+#define MAXTCP 20
+struct tcpclient {
+ struct query q;
+ struct taia start;
+ struct taia timeout;
+ uint64 active; /* query number or 1, if active; otherwise 0 */
+ iopause_fd *io;
+ char ip[4]; /* send response to this address */
+ uint16 port; /* send response to this port */
+ char id[2];
+ int tcp; /* open TCP socket, if active */
+ int state;
+ char *buf; /* 0, or dynamically allocated of length len */
+ unsigned int len;
+ unsigned int pos;
+} t[MAXTCP];
+int tactive = 0;
+
+/*
+state 1: buf 0; normal state at beginning of TCP connection
+state 2: buf 0; have read 1 byte of query packet length into len
+state 3: buf allocated; have read pos bytes of buf
+state 0: buf 0; handling query in q
+state -1: buf allocated; have written pos bytes
+*/
+
+void t_free(int j)
+{
+ if (!t[j].buf) return;
+ alloc_free(t[j].buf);
+ t[j].buf = 0;
+}
+
+void t_timeout(int j)
+{
+ struct taia now;
+ if (!t[j].active) return;
+ taia_now(&now);
+ taia_uint(&t[j].timeout,10);
+ taia_add(&t[j].timeout,&t[j].timeout,&now);
+}
+
+void t_close(int j)
+{
+ if (!t[j].active) return;
+ t_free(j);
+ log_tcpclose(t[j].ip,t[j].port);
+ close(t[j].tcp);
+ t[j].active = 0; --tactive;
+}
+
+void t_drop(int j)
+{
+ log_querydrop(&t[j].active);
+ errno = error_pipe;
+ t_close(j);
+}
+
+void t_respond(int j)
+{
+ if (!t[j].active) return;
+ log_querydone(&t[j].active,response_len);
+ response_id(t[j].id);
+ t[j].len = response_len + 2;
+ t_free(j);
+ t[j].buf = alloc(response_len + 2);
+ if (!t[j].buf) { t_close(j); return; }
+ uint16_pack_big(t[j].buf,response_len);
+ byte_copy(t[j].buf + 2,response_len,response);
+ t[j].pos = 0;
+ t[j].state = -1;
+}
+
+void t_rw(int j)
+{
+ struct tcpclient *x;
+ char ch;
+ static char *q = 0;
+ char qtype[2];
+ char qclass[2];
+ int r;
+
+ x = t + j;
+ if (x->state == -1) {
+ r = write(x->tcp,x->buf + x->pos,x->len - x->pos);
+ if (r <= 0) { t_close(j); return; }
+ x->pos += r;
+ if (x->pos == x->len) {
+ t_free(j);
+ x->state = 1; /* could drop connection immediately */
+ }
+ return;
+ }
+
+ r = read(x->tcp,&ch,1);
+ if (r == 0) { errno = error_pipe; t_close(j); return; }
+ if (r < 0) { t_close(j); return; }
+
+ if (x->state == 1) {
+ x->len = (unsigned char) ch;
+ x->len <<= 8;
+ x->state = 2;
+ return;
+ }
+ if (x->state == 2) {
+ x->len += (unsigned char) ch;
+ if (!x->len) { errno = error_proto; t_close(j); return; }
+ x->buf = alloc(x->len);
+ if (!x->buf) { t_close(j); return; }
+ x->pos = 0;
+ x->state = 3;
+ return;
+ }
+
+ if (x->state != 3) return; /* impossible */
+
+ x->buf[x->pos++] = ch;
+ if (x->pos < x->len) return;
+
+ if (!packetquery(x->buf,x->len,&q,qtype,qclass,x->id)) { t_close(j); return; }
+
+ x->active = ++numqueries;
+ log_query(&x->active,x->ip,x->port,x->id,q,qtype);
+ switch(query_start(&x->q,q,qtype,qclass,myipoutgoing)) {
+ case -1:
+ t_drop(j);
+ return;
+ case 1:
+ t_respond(j);
+ return;
+ }
+ t_free(j);
+ x->state = 0;
+}
+
+void t_new(void)
+{
+ int i;
+ int j;
+ struct tcpclient *x;
+
+ for (j = 0;j < MAXTCP;++j)
+ if (!t[j].active)
+ break;
+
+ if (j >= MAXTCP) {
+ j = 0;
+ for (i = 1;i < MAXTCP;++i)
+ if (taia_less(&t[i].start,&t[j].start))
+ j = i;
+ errno = error_timeout;
+ if (t[j].state == 0)
+ t_drop(j);
+ else
+ t_close(j);
+ }
+
+ x = t + j;
+ taia_now(&x->start);
+
+ x->tcp = socket_accept4(tcp53,x->ip,&x->port);
+ if (x->tcp == -1) return;
+ if (x->port < 1024) if (x->port != 53) { close(x->tcp); return; }
+ if (!okclient(x->ip)) { close(x->tcp); return; }
+ if (ndelay_on(x->tcp) == -1) { close(x->tcp); return; } /* Linux bug */
+
+ x->active = 1; ++tactive;
+ x->state = 1;
+ t_timeout(j);
+
+ log_tcpopen(x->ip,x->port);
+}
+
+
+iopause_fd io[3 + MAXUDP + MAXTCP];
+iopause_fd *udp53io;
+iopause_fd *tcp53io;
+
+static void doit(void)
+{
+ int j;
+ struct taia deadline;
+ struct taia stamp;
+ int iolen;
+ int r;
+
+ for (;;) {
+ taia_now(&stamp);
+ taia_uint(&deadline,120);
+ taia_add(&deadline,&deadline,&stamp);
+
+ iolen = 0;
+
+ udp53io = io + iolen++;
+ udp53io->fd = udp53;
+ udp53io->events = IOPAUSE_READ;
+
+ tcp53io = io + iolen++;
+ tcp53io->fd = tcp53;
+ tcp53io->events = IOPAUSE_READ;
+
+ for (j = 0;j < MAXUDP;++j)
+ if (u[j].active) {
+ u[j].io = io + iolen++;
+ query_io(&u[j].q,u[j].io,&deadline);
+ }
+ for (j = 0;j < MAXTCP;++j)
+ if (t[j].active) {
+ t[j].io = io + iolen++;
+ if (t[j].state == 0)
+ query_io(&t[j].q,t[j].io,&deadline);
+ else {
+ if (taia_less(&t[j].timeout,&deadline)) deadline = t[j].timeout;
+ t[j].io->fd = t[j].tcp;
+ t[j].io->events = (t[j].state > 0) ? IOPAUSE_READ : IOPAUSE_WRITE;
+ }
+ }
+
+ iopause(io,iolen,&deadline,&stamp);
+
+ for (j = 0;j < MAXUDP;++j)
+ if (u[j].active) {
+ r = query_get(&u[j].q,u[j].io,&stamp);
+ if (r == -1) u_drop(j);
+ if (r == 1) u_respond(j);
+ }
+
+ for (j = 0;j < MAXTCP;++j)
+ if (t[j].active) {
+ if (t[j].io->revents)
+ t_timeout(j);
+ if (t[j].state == 0) {
+ r = query_get(&t[j].q,t[j].io,&stamp);
+ if (r == -1) t_drop(j);
+ if (r == 1) t_respond(j);
+ }
+ else
+ if (t[j].io->revents || taia_less(&t[j].timeout,&stamp))
+ t_rw(j);
+ }
+
+ if (udp53io)
+ if (udp53io->revents)
+ u_new();
+
+ if (tcp53io)
+ if (tcp53io->revents)
+ t_new();
+ }
+}
+
+#define FATAL "dnscache: fatal: "
+
+char seed[128];
+
+int main()
+{
+ char *x;
+ unsigned long cachesize;
+
+ x = env_get("IP");
+ if (!x)
+ strerr_die2x(111,FATAL,"$IP not set");
+ if (!ip4_scan(x,myipincoming))
+ strerr_die3x(111,FATAL,"unable to parse IP address ",x);
+
+ udp53 = socket_udp();
+ if (udp53 == -1)
+ strerr_die2sys(111,FATAL,"unable to create UDP socket: ");
+ if (socket_bind4_reuse(udp53,myipincoming,53) == -1)
+ strerr_die2sys(111,FATAL,"unable to bind UDP socket: ");
+
+ tcp53 = socket_tcp();
+ if (tcp53 == -1)
+ strerr_die2sys(111,FATAL,"unable to create TCP socket: ");
+ if (socket_bind4_reuse(tcp53,myipincoming,53) == -1)
+ strerr_die2sys(111,FATAL,"unable to bind TCP socket: ");
+
+ droproot(FATAL);
+
+ socket_tryreservein(udp53,131072);
+
+ byte_zero(seed,sizeof seed);
+ read(0,seed,sizeof seed);
+ dns_random_init(seed);
+ close(0);
+
+ x = env_get("IPSEND");
+ if (!x)
+ strerr_die2x(111,FATAL,"$IPSEND not set");
+ if (!ip4_scan(x,myipoutgoing))
+ strerr_die3x(111,FATAL,"unable to parse IP address ",x);
+
+ x = env_get("CACHESIZE");
+ if (!x)
+ strerr_die2x(111,FATAL,"$CACHESIZE not set");
+ scan_ulong(x,&cachesize);
+ if (!cache_init(cachesize))
+ strerr_die3x(111,FATAL,"not enough memory for cache of size ",x);
+
+ if (env_get("HIDETTL"))
+ response_hidettl();
+ if (env_get("FORWARDONLY"))
+ query_forwardonly();
+
+ if (!roots_init())
+ strerr_die2sys(111,FATAL,"unable to read servers: ");
+
+ if (socket_listen(tcp53,20) == -1)
+ strerr_die2sys(111,FATAL,"unable to listen on TCP socket: ");
+
+ log_startup();
+ doit();
+}
diff --git a/dnsfilter.c b/dnsfilter.c
new file mode 100644
index 0000000..9e6863a
--- /dev/null
+++ b/dnsfilter.c
@@ -0,0 +1,214 @@
+#include <unistd.h>
+#include "strerr.h"
+#include "buffer.h"
+#include "stralloc.h"
+#include "alloc.h"
+#include "dns.h"
+#include "ip4.h"
+#include "byte.h"
+#include "scan.h"
+#include "taia.h"
+#include "sgetopt.h"
+#include "iopause.h"
+#include "error.h"
+#include "exit.h"
+
+#define FATAL "dnsfilter: fatal: "
+
+void nomem(void)
+{
+ strerr_die2x(111,FATAL,"out of memory");
+}
+
+struct line {
+ stralloc left;
+ stralloc middle;
+ stralloc right;
+ struct dns_transmit dt;
+ int flagactive;
+ iopause_fd *io;
+} *x;
+struct line tmp;
+unsigned int xmax = 1000;
+unsigned int xnum = 0;
+unsigned int numactive = 0;
+unsigned int maxactive = 10;
+
+static stralloc partial;
+
+char inbuf[1024];
+int inbuflen = 0;
+iopause_fd *inio;
+int flag0 = 1;
+
+iopause_fd *io;
+int iolen;
+
+char servers[64];
+char ip[4];
+char name[DNS_NAME4_DOMAIN];
+
+void errout(int i)
+{
+ int j;
+
+ if (!stralloc_copys(&x[i].middle,":")) nomem();
+ if (!stralloc_cats(&x[i].middle,error_str(errno))) nomem();
+ for (j = 0;j < x[i].middle.len;++j)
+ if (x[i].middle.s[j] == ' ')
+ x[i].middle.s[j] = '-';
+}
+
+int main(int argc,char **argv)
+{
+ struct taia stamp;
+ struct taia deadline;
+ int opt;
+ unsigned long u;
+ int i;
+ int j;
+ int r;
+
+ while ((opt = getopt(argc,argv,"c:l:")) != opteof)
+ switch(opt) {
+ case 'c':
+ scan_ulong(optarg,&u);
+ if (u < 1) u = 1;
+ if (u > 1000) u = 1000;
+ maxactive = u;
+ break;
+ case 'l':
+ scan_ulong(optarg,&u);
+ if (u < 1) u = 1;
+ if (u > 1000000) u = 1000000;
+ xmax = u;
+ break;
+ default:
+ strerr_die1x(111,"dnsfilter: usage: dnsfilter [ -c concurrency ] [ -l lines ]");
+ }
+
+ x = (struct line *) alloc(xmax * sizeof(struct line));
+ if (!x) nomem();
+ byte_zero(x,xmax * sizeof(struct line));
+
+ io = (iopause_fd *) alloc((xmax + 1) * sizeof(iopause_fd));
+ if (!io) nomem();
+
+ if (!stralloc_copys(&partial,"")) nomem();
+
+
+ while (flag0 || inbuflen || partial.len || xnum) {
+ taia_now(&stamp);
+ taia_uint(&deadline,120);
+ taia_add(&deadline,&deadline,&stamp);
+
+ iolen = 0;
+
+ if (flag0)
+ if (inbuflen < sizeof inbuf) {
+ inio = io + iolen++;
+ inio->fd = 0;
+ inio->events = IOPAUSE_READ;
+ }
+
+ for (i = 0;i < xnum;++i)
+ if (x[i].flagactive) {
+ x[i].io = io + iolen++;
+ dns_transmit_io(&x[i].dt,x[i].io,&deadline);
+ }
+
+ iopause(io,iolen,&deadline,&stamp);
+
+ if (flag0)
+ if (inbuflen < sizeof inbuf)
+ if (inio->revents) {
+ r = read(0,inbuf + inbuflen,(sizeof inbuf) - inbuflen);
+ if (r <= 0)
+ flag0 = 0;
+ else
+ inbuflen += r;
+ }
+
+ for (i = 0;i < xnum;++i)
+ if (x[i].flagactive) {
+ r = dns_transmit_get(&x[i].dt,x[i].io,&stamp);
+ if (r == -1) {
+ errout(i);
+ x[i].flagactive = 0;
+ --numactive;
+ }
+ else if (r == 1) {
+ if (dns_name_packet(&x[i].middle,x[i].dt.packet,x[i].dt.packetlen) == -1)
+ errout(i);
+ if (x[i].middle.len)
+ if (!stralloc_cats(&x[i].left,"=")) nomem();
+ x[i].flagactive = 0;
+ --numactive;
+ }
+ }
+
+ for (;;) {
+
+ if (xnum && !x[0].flagactive) {
+ buffer_put(buffer_1,x[0].left.s,x[0].left.len);
+ buffer_put(buffer_1,x[0].middle.s,x[0].middle.len);
+ buffer_put(buffer_1,x[0].right.s,x[0].right.len);
+ buffer_flush(buffer_1);
+ --xnum;
+ tmp = x[0];
+ for (i = 0;i < xnum;++i) x[i] = x[i + 1];
+ x[xnum] = tmp;
+ continue;
+ }
+
+ if ((xnum < xmax) && (numactive < maxactive)) {
+ i = byte_chr(inbuf,inbuflen,'\n');
+ if (inbuflen && (i == inbuflen)) {
+ if (!stralloc_catb(&partial,inbuf,inbuflen)) nomem();
+ inbuflen = 0;
+ continue;
+ }
+
+ if ((i < inbuflen) || (!flag0 && partial.len)) {
+ if (i < inbuflen) ++i;
+ if (!stralloc_catb(&partial,inbuf,i)) nomem();
+ inbuflen -= i;
+ for (j = 0;j < inbuflen;++j) inbuf[j] = inbuf[j + i];
+
+ if (partial.len) {
+ i = byte_chr(partial.s,partial.len,'\n');
+ i = byte_chr(partial.s,i,'\t');
+ i = byte_chr(partial.s,i,' ');
+
+ if (!stralloc_copyb(&x[xnum].left,partial.s,i)) nomem();
+ if (!stralloc_copys(&x[xnum].middle,"")) nomem();
+ if (!stralloc_copyb(&x[xnum].right,partial.s + i,partial.len - i)) nomem();
+ x[xnum].flagactive = 0;
+
+ partial.len = i;
+ if (!stralloc_0(&partial)) nomem();
+ if (ip4_scan(partial.s,ip)) {
+ dns_name4_domain(name,ip);
+ if (dns_resolvconfip(servers) == -1)
+ strerr_die2sys(111,FATAL,"unable to read /etc/resolv.conf: ");
+ if (dns_transmit_start(&x[xnum].dt,servers,1,name,DNS_T_PTR,"\0\0\0\0") == -1)
+ errout(xnum);
+ else {
+ x[xnum].flagactive = 1;
+ ++numactive;
+ }
+ }
+ ++xnum;
+ }
+
+ partial.len = 0;
+ continue;
+ }
+ }
+
+ break;
+ }
+ }
+
+ _exit(0);
+}
diff --git a/dnsip.c b/dnsip.c
new file mode 100644
index 0000000..60c5d3d
--- /dev/null
+++ b/dnsip.c
@@ -0,0 +1,40 @@
+#include "buffer.h"
+#include "exit.h"
+#include "strerr.h"
+#include "ip4.h"
+#include "dns.h"
+
+#define FATAL "dnsip: fatal: "
+
+static char seed[128];
+
+static stralloc fqdn;
+static stralloc out;
+char str[IP4_FMT];
+
+int main(int argc,char **argv)
+{
+ int i;
+
+ dns_random_init(seed);
+
+ if (*argv) ++argv;
+
+ while (*argv) {
+ if (!stralloc_copys(&fqdn,*argv))
+ strerr_die2x(111,FATAL,"out of memory");
+ if (dns_ip4(&out,&fqdn) == -1)
+ strerr_die4sys(111,FATAL,"unable to find IP address for ",*argv,": ");
+
+ for (i = 0;i + 4 <= out.len;i += 4) {
+ buffer_put(buffer_1,str,ip4_fmt(str,out.s + i));
+ buffer_puts(buffer_1," ");
+ }
+ buffer_puts(buffer_1,"\n");
+
+ ++argv;
+ }
+
+ buffer_flush(buffer_1);
+ _exit(0);
+}
diff --git a/dnsipq.c b/dnsipq.c
new file mode 100644
index 0000000..8e34928
--- /dev/null
+++ b/dnsipq.c
@@ -0,0 +1,43 @@
+#include "buffer.h"
+#include "exit.h"
+#include "strerr.h"
+#include "ip4.h"
+#include "dns.h"
+
+#define FATAL "dnsipq: fatal: "
+
+static char seed[128];
+
+static stralloc in;
+static stralloc fqdn;
+static stralloc out;
+char str[IP4_FMT];
+
+int main(int argc,char **argv)
+{
+ int i;
+
+ dns_random_init(seed);
+
+ if (*argv) ++argv;
+
+ while (*argv) {
+ if (!stralloc_copys(&in,*argv))
+ strerr_die2x(111,FATAL,"out of memory");
+ if (dns_ip4_qualify(&out,&fqdn,&in) == -1)
+ strerr_die4sys(111,FATAL,"unable to find IP address for ",*argv,": ");
+
+ buffer_put(buffer_1,fqdn.s,fqdn.len);
+ buffer_puts(buffer_1," ");
+ for (i = 0;i + 4 <= out.len;i += 4) {
+ buffer_put(buffer_1,str,ip4_fmt(str,out.s + i));
+ buffer_puts(buffer_1," ");
+ }
+ buffer_puts(buffer_1,"\n");
+
+ ++argv;
+ }
+
+ buffer_flush(buffer_1);
+ _exit(0);
+}
diff --git a/dnsmx.c b/dnsmx.c
new file mode 100644
index 0000000..5d75d39
--- /dev/null
+++ b/dnsmx.c
@@ -0,0 +1,64 @@
+#include "buffer.h"
+#include "exit.h"
+#include "strerr.h"
+#include "uint16.h"
+#include "byte.h"
+#include "str.h"
+#include "fmt.h"
+#include "dns.h"
+
+#define FATAL "dnsmx: fatal: "
+
+void nomem(void)
+{
+ strerr_die2x(111,FATAL,"out of memory");
+}
+
+static char seed[128];
+
+static stralloc fqdn;
+static char *q;
+static stralloc out;
+char strnum[FMT_ULONG];
+
+int main(int argc,char **argv)
+{
+ int i;
+ int j;
+ uint16 pref;
+
+ dns_random_init(seed);
+
+ if (*argv) ++argv;
+
+ while (*argv) {
+ if (!stralloc_copys(&fqdn,*argv)) nomem();
+ if (dns_mx(&out,&fqdn) == -1)
+ strerr_die4sys(111,FATAL,"unable to find MX records for ",*argv,": ");
+
+ if (!out.len) {
+ if (!dns_domain_fromdot(&q,*argv,str_len(*argv))) nomem();
+ if (!stralloc_copys(&out,"0 ")) nomem();
+ if (!dns_domain_todot_cat(&out,q)) nomem();
+ if (!stralloc_cats(&out,"\n")) nomem();
+ buffer_put(buffer_1,out.s,out.len);
+ }
+ else {
+ i = 0;
+ while (i + 2 < out.len) {
+ j = byte_chr(out.s + i + 2,out.len - i - 2,0);
+ uint16_unpack_big(out.s + i,&pref);
+ buffer_put(buffer_1,strnum,fmt_ulong(strnum,pref));
+ buffer_puts(buffer_1," ");
+ buffer_put(buffer_1,out.s + i + 2,j);
+ buffer_puts(buffer_1,"\n");
+ i += j + 3;
+ }
+ }
+
+ ++argv;
+ }
+
+ buffer_flush(buffer_1);
+ _exit(0);
+}
diff --git a/dnsname.c b/dnsname.c
new file mode 100644
index 0000000..0e5eb26
--- /dev/null
+++ b/dnsname.c
@@ -0,0 +1,34 @@
+#include "buffer.h"
+#include "exit.h"
+#include "strerr.h"
+#include "ip4.h"
+#include "dns.h"
+
+#define FATAL "dnsname: fatal: "
+
+static char seed[128];
+
+char ip[4];
+static stralloc out;
+
+int main(int argc,char **argv)
+{
+ dns_random_init(seed);
+
+ if (*argv) ++argv;
+
+ while (*argv) {
+ if (!ip4_scan(*argv,ip))
+ strerr_die3x(111,FATAL,"unable to parse IP address ",*argv);
+ if (dns_name4(&out,ip) == -1)
+ strerr_die4sys(111,FATAL,"unable to find host name for ",*argv,": ");
+
+ buffer_put(buffer_1,out.s,out.len);
+ buffer_puts(buffer_1,"\n");
+
+ ++argv;
+ }
+
+ buffer_flush(buffer_1);
+ _exit(0);
+}
diff --git a/dnsq.c b/dnsq.c
new file mode 100644
index 0000000..533e6af
--- /dev/null
+++ b/dnsq.c
@@ -0,0 +1,98 @@
+#include "uint16.h"
+#include "strerr.h"
+#include "buffer.h"
+#include "scan.h"
+#include "str.h"
+#include "byte.h"
+#include "error.h"
+#include "ip4.h"
+#include "iopause.h"
+#include "printpacket.h"
+#include "parsetype.h"
+#include "dns.h"
+
+#define FATAL "dnsq: fatal: "
+
+void usage(void)
+{
+ strerr_die1x(100,"dnsq: usage: dnsq type name server");
+}
+void oops(void)
+{
+ strerr_die2sys(111,FATAL,"unable to parse: ");
+}
+
+static struct dns_transmit tx;
+
+int resolve(char *q,char qtype[2],char servers[64])
+{
+ struct taia stamp;
+ struct taia deadline;
+ iopause_fd x[1];
+ int r;
+
+ if (dns_transmit_start(&tx,servers,0,q,qtype,"\0\0\0\0") == -1) return -1;
+
+ for (;;) {
+ taia_now(&stamp);
+ taia_uint(&deadline,120);
+ taia_add(&deadline,&deadline,&stamp);
+ dns_transmit_io(&tx,x,&deadline);
+ iopause(x,1,&deadline,&stamp);
+ r = dns_transmit_get(&tx,x,&stamp);
+ if (r == -1) return -1;
+ if (r == 1) break;
+ }
+
+ return 0;
+}
+
+char servers[64];
+static stralloc ip;
+static stralloc fqdn;
+
+char type[2];
+static char *q;
+
+static stralloc out;
+
+static char seed[128];
+
+int main(int argc,char **argv)
+{
+ uint16 u16;
+
+ dns_random_init(seed);
+
+ if (!*argv) usage();
+ if (!*++argv) usage();
+ if (!parsetype(*argv,type)) usage();
+
+ if (!*++argv) usage();
+ if (!dns_domain_fromdot(&q,*argv,str_len(*argv))) oops();
+
+ if (!*++argv) usage();
+ if (!stralloc_copys(&out,*argv)) oops();
+ if (dns_ip4_qualify(&ip,&fqdn,&out) == -1) oops();
+ if (ip.len >= 64) ip.len = 64;
+ byte_zero(servers,64);
+ byte_copy(servers,ip.len,ip.s);
+
+ if (!stralloc_copys(&out,"")) oops();
+ uint16_unpack_big(type,&u16);
+ if (!stralloc_catulong0(&out,u16,0)) oops();
+ if (!stralloc_cats(&out," ")) oops();
+ if (!dns_domain_todot_cat(&out,q)) oops();
+ if (!stralloc_cats(&out,":\n")) oops();
+
+ if (resolve(q,type,servers) == -1) {
+ if (!stralloc_cats(&out,error_str(errno))) oops();
+ if (!stralloc_cats(&out,"\n")) oops();
+ }
+ else {
+ if (!printpacket_cat(&out,tx.packet,tx.packetlen)) oops();
+ }
+
+ buffer_putflush(buffer_1,out.s,out.len);
+ _exit(0);
+}
diff --git a/dnsqr.c b/dnsqr.c
new file mode 100644
index 0000000..ff8ea6e
--- /dev/null
+++ b/dnsqr.c
@@ -0,0 +1,66 @@
+#include "uint16.h"
+#include "strerr.h"
+#include "buffer.h"
+#include "scan.h"
+#include "str.h"
+#include "byte.h"
+#include "error.h"
+#include "iopause.h"
+#include "printpacket.h"
+#include "parsetype.h"
+#include "dns.h"
+
+#define FATAL "dnsqr: fatal: "
+
+void usage(void)
+{
+ strerr_die1x(100,"dnsqr: usage: dnsqr type name");
+}
+void oops(void)
+{
+ strerr_die2sys(111,FATAL,"unable to parse: ");
+}
+
+char type[2];
+static char *q;
+
+static stralloc out;
+
+static char seed[128];
+
+int main(int argc,char **argv)
+{
+ uint16 u16;
+
+ dns_random_init(seed);
+
+ if (!*argv) usage();
+ if (!*++argv) usage();
+ if (!parsetype(*argv,type)) usage();
+
+ if (!*++argv) usage();
+ if (!dns_domain_fromdot(&q,*argv,str_len(*argv))) oops();
+
+ if (*++argv) usage();
+
+ if (!stralloc_copys(&out,"")) oops();
+ uint16_unpack_big(type,&u16);
+ if (!stralloc_catulong0(&out,u16,0)) oops();
+ if (!stralloc_cats(&out," ")) oops();
+ if (!dns_domain_todot_cat(&out,q)) oops();
+ if (!stralloc_cats(&out,":\n")) oops();
+
+ if (dns_resolve(q,type) == -1) {
+ if (!stralloc_cats(&out,error_str(errno))) oops();
+ if (!stralloc_cats(&out,"\n")) oops();
+ }
+ else {
+ if (dns_resolve_tx.packetlen < 4) oops();
+ dns_resolve_tx.packet[2] &= ~1;
+ dns_resolve_tx.packet[3] &= ~128;
+ if (!printpacket_cat(&out,dns_resolve_tx.packet,dns_resolve_tx.packetlen)) oops();
+ }
+
+ buffer_putflush(buffer_1,out.s,out.len);
+ _exit(0);
+}
diff --git a/dnsroots.global b/dnsroots.global
new file mode 100644
index 0000000..3b567e1
--- /dev/null
+++ b/dnsroots.global
@@ -0,0 +1,13 @@
+198.41.0.4
+128.9.0.107
+192.33.4.12
+128.8.10.90
+192.203.230.10
+192.5.5.241
+192.112.36.4
+128.63.2.53
+192.36.148.17
+198.41.0.10
+193.0.14.129
+198.32.64.12
+202.12.27.33
diff --git a/dnstrace.c b/dnstrace.c
new file mode 100644
index 0000000..3f2159b
--- /dev/null
+++ b/dnstrace.c
@@ -0,0 +1,474 @@
+#include "uint16.h"
+#include "uint32.h"
+#include "fmt.h"
+#include "str.h"
+#include "byte.h"
+#include "ip4.h"
+#include "gen_alloc.h"
+#include "gen_allocdefs.h"
+#include "exit.h"
+#include "buffer.h"
+#include "stralloc.h"
+#include "error.h"
+#include "strerr.h"
+#include "iopause.h"
+#include "printrecord.h"
+#include "alloc.h"
+#include "parsetype.h"
+#include "dd.h"
+#include "dns.h"
+
+#define FATAL "dnstrace: fatal: "
+
+void nomem(void)
+{
+ strerr_die2x(111,FATAL,"out of memory");
+}
+void usage(void)
+{
+ strerr_die1x(100,"dnstrace: usage: dnstrace type name rootip ...");
+}
+
+static stralloc querystr;
+char ipstr[IP4_FMT];
+static stralloc tmp;
+
+void printdomain(const char *d)
+{
+ if (!stralloc_copys(&tmp,"")) nomem();
+ if (!dns_domain_todot_cat(&tmp,d)) nomem();
+ buffer_put(buffer_1,tmp.s,tmp.len);
+}
+
+static struct dns_transmit tx;
+
+int resolve(char *q,char qtype[2],char ip[4])
+{
+ struct taia start;
+ struct taia stamp;
+ struct taia deadline;
+ char servers[64];
+ iopause_fd x[1];
+ int r;
+
+ taia_now(&start);
+
+ byte_zero(servers,64);
+ byte_copy(servers,4,ip);
+
+ if (dns_transmit_start(&tx,servers,0,q,qtype,"\0\0\0\0") == -1) return -1;
+
+ for (;;) {
+ taia_now(&stamp);
+ taia_uint(&deadline,120);
+ taia_add(&deadline,&deadline,&stamp);
+ dns_transmit_io(&tx,x,&deadline);
+ iopause(x,1,&deadline,&stamp);
+ r = dns_transmit_get(&tx,x,&stamp);
+ if (r == -1) return -1;
+ if (r == 1) break;
+ }
+
+ taia_now(&stamp);
+ taia_sub(&stamp,&stamp,&start);
+ taia_uint(&deadline,1);
+ if (taia_less(&deadline,&stamp)) {
+ buffer_put(buffer_1,querystr.s,querystr.len);
+ buffer_puts(buffer_1,"ALERT:took more than 1 second\n");
+ }
+
+ return 0;
+}
+
+struct address {
+ char *owner;
+ char ip[4];
+} ;
+
+GEN_ALLOC_typedef(address_alloc,struct address,s,len,a)
+GEN_ALLOC_readyplus(address_alloc,struct address,s,len,a,i,n,x,30,address_alloc_readyplus)
+GEN_ALLOC_append(address_alloc,struct address,s,len,a,i,n,x,30,address_alloc_readyplus,address_alloc_append)
+
+static address_alloc address;
+
+struct ns {
+ char *owner;
+ char *ns;
+} ;
+
+GEN_ALLOC_typedef(ns_alloc,struct ns,s,len,a)
+GEN_ALLOC_readyplus(ns_alloc,struct ns,s,len,a,i,n,x,30,ns_alloc_readyplus)
+GEN_ALLOC_append(ns_alloc,struct ns,s,len,a,i,n,x,30,ns_alloc_readyplus,ns_alloc_append)
+
+static ns_alloc ns;
+
+struct query {
+ char *owner;
+ char type[2];
+} ;
+
+GEN_ALLOC_typedef(query_alloc,struct query,s,len,a)
+GEN_ALLOC_readyplus(query_alloc,struct query,s,len,a,i,n,x,30,query_alloc_readyplus)
+GEN_ALLOC_append(query_alloc,struct query,s,len,a,i,n,x,30,query_alloc_readyplus,query_alloc_append)
+
+static query_alloc query;
+
+struct qt {
+ char *owner;
+ char type[2];
+ char *control;
+ char ip[4];
+} ;
+
+GEN_ALLOC_typedef(qt_alloc,struct qt,s,len,a)
+GEN_ALLOC_readyplus(qt_alloc,struct qt,s,len,a,i,n,x,30,qt_alloc_readyplus)
+GEN_ALLOC_append(qt_alloc,struct qt,s,len,a,i,n,x,30,qt_alloc_readyplus,qt_alloc_append)
+
+static qt_alloc qt;
+
+void qt_add(const char *q,const char type[2],const char *control,const char ip[4])
+{
+ struct qt x;
+ int i;
+
+ if (!*q) return; /* don't ask the roots about our artificial . host */
+
+ for (i = 0;i < qt.len;++i)
+ if (dns_domain_equal(qt.s[i].owner,q))
+ if (dns_domain_equal(qt.s[i].control,control))
+ if (byte_equal(qt.s[i].type,2,type))
+ if (byte_equal(qt.s[i].ip,4,ip))
+ return;
+
+ byte_zero(&x,sizeof x);
+ if (!dns_domain_copy(&x.owner,q)) nomem();
+ if (!dns_domain_copy(&x.control,control)) nomem();
+ byte_copy(x.type,2,type);
+ byte_copy(x.ip,4,ip);
+ if (!qt_alloc_append(&qt,&x)) nomem();
+}
+
+void query_add(const char *owner,const char type[2])
+{
+ struct query x;
+ int i;
+ int j;
+
+ for (i = 0;i < query.len;++i)
+ if (dns_domain_equal(query.s[i].owner,owner))
+ if (byte_equal(query.s[i].type,2,type))
+ return;
+
+ byte_zero(&x,sizeof x);
+ if (!dns_domain_copy(&x.owner,owner)) nomem();
+ byte_copy(x.type,2,type);
+ if (!query_alloc_append(&query,&x)) nomem();
+
+ for (i = 0;i < ns.len;++i)
+ if (dns_domain_suffix(owner,ns.s[i].owner))
+ for (j = 0;j < address.len;++j)
+ if (dns_domain_equal(ns.s[i].ns,address.s[j].owner))
+ qt_add(owner,type,ns.s[i].owner,address.s[j].ip);
+}
+
+void ns_add(const char *owner,const char *server)
+{
+ struct ns x;
+ int i;
+ int j;
+
+ buffer_put(buffer_1,querystr.s,querystr.len);
+ buffer_puts(buffer_1,"NS:");
+ printdomain(owner);
+ buffer_puts(buffer_1,":");
+ printdomain(server);
+ buffer_puts(buffer_1,"\n");
+
+ for (i = 0;i < ns.len;++i)
+ if (dns_domain_equal(ns.s[i].owner,owner))
+ if (dns_domain_equal(ns.s[i].ns,server))
+ return;
+
+ query_add(server,DNS_T_A);
+
+ byte_zero(&x,sizeof x);
+ if (!dns_domain_copy(&x.owner,owner)) nomem();
+ if (!dns_domain_copy(&x.ns,server)) nomem();
+ if (!ns_alloc_append(&ns,&x)) nomem();
+
+ for (i = 0;i < query.len;++i)
+ if (dns_domain_suffix(query.s[i].owner,owner))
+ for (j = 0;j < address.len;++j)
+ if (dns_domain_equal(server,address.s[j].owner))
+ qt_add(query.s[i].owner,query.s[i].type,owner,address.s[j].ip);
+}
+
+void address_add(const char *owner,const char ip[4])
+{
+ struct address x;
+ int i;
+ int j;
+
+ buffer_put(buffer_1,querystr.s,querystr.len);
+ buffer_puts(buffer_1,"A:");
+ printdomain(owner);
+ buffer_puts(buffer_1,":");
+ buffer_put(buffer_1,ipstr,ip4_fmt(ipstr,ip));
+ buffer_puts(buffer_1,"\n");
+
+ for (i = 0;i < address.len;++i)
+ if (dns_domain_equal(address.s[i].owner,owner))
+ if (byte_equal(address.s[i].ip,4,ip))
+ return;
+
+ byte_zero(&x,sizeof x);
+ if (!dns_domain_copy(&x.owner,owner)) nomem();
+ byte_copy(x.ip,4,ip);
+ if (!address_alloc_append(&address,&x)) nomem();
+
+ for (i = 0;i < ns.len;++i)
+ if (dns_domain_equal(ns.s[i].ns,owner))
+ for (j = 0;j < query.len;++j)
+ if (dns_domain_suffix(query.s[j].owner,ns.s[i].owner))
+ qt_add(query.s[j].owner,query.s[j].type,ns.s[i].owner,ip);
+}
+
+char seed[128];
+
+static char *t1;
+static char *t2;
+static char *referral;
+static char *cname;
+
+static int typematch(const char rtype[2],const char qtype[2])
+{
+ return byte_equal(qtype,2,rtype) || byte_equal(qtype,2,DNS_T_ANY);
+}
+
+void parsepacket(const char *buf,unsigned int len,const char *d,const char dtype[2],const char *control)
+{
+ char misc[20];
+ char header[12];
+ unsigned int pos;
+ uint16 numanswers;
+ unsigned int posanswers;
+ uint16 numauthority;
+ unsigned int posauthority;
+ uint16 numglue;
+ unsigned int posglue;
+ uint16 datalen;
+ unsigned int rcode;
+ int flagout;
+ int flagcname;
+ int flagreferral;
+ int flagsoa;
+ int j;
+ const char *x;
+
+ pos = dns_packet_copy(buf,len,0,header,12); if (!pos) goto DIE;
+ pos = dns_packet_skipname(buf,len,pos); if (!pos) goto DIE;
+ pos += 4;
+
+ uint16_unpack_big(header + 6,&numanswers);
+ uint16_unpack_big(header + 8,&numauthority);
+ uint16_unpack_big(header + 10,&numglue);
+
+ rcode = header[3] & 15;
+ if (rcode && (rcode != 3)) { errno = error_proto; goto DIE; } /* impossible */
+
+ flagout = 0;
+ flagcname = 0;
+ flagreferral = 0;
+ flagsoa = 0;
+ posanswers = pos;
+ for (j = 0;j < numanswers;++j) {
+ pos = dns_packet_getname(buf,len,pos,&t1); if (!pos) goto DIE;
+ pos = dns_packet_copy(buf,len,pos,header,10); if (!pos) goto DIE;
+ if (dns_domain_equal(t1,d))
+ if (byte_equal(header + 2,2,DNS_C_IN))
+ if (typematch(header,dtype))
+ flagout = 1;
+ else if (typematch(header,DNS_T_CNAME)) {
+ if (!dns_packet_getname(buf,len,pos,&cname)) goto DIE;
+ flagcname = 1;
+ }
+ uint16_unpack_big(header + 8,&datalen);
+ pos += datalen;
+ }
+ posauthority = pos;
+ for (j = 0;j < numauthority;++j) {
+ pos = dns_packet_getname(buf,len,pos,&t1); if (!pos) goto DIE;
+ pos = dns_packet_copy(buf,len,pos,header,10); if (!pos) goto DIE;
+ if (typematch(header,DNS_T_SOA))
+ flagsoa = 1;
+ else if (typematch(header,DNS_T_NS)) {
+ flagreferral = 1;
+ if (!dns_domain_copy(&referral,t1)) goto DIE;
+ }
+ uint16_unpack_big(header + 8,&datalen);
+ pos += datalen;
+ }
+ posglue = pos;
+
+ if (!flagcname && !rcode && !flagout && flagreferral && !flagsoa)
+ if (dns_domain_equal(referral,control) || !dns_domain_suffix(referral,control)) {
+ buffer_put(buffer_1,querystr.s,querystr.len);
+ buffer_puts(buffer_1,"ALERT:lame server; refers to ");
+ printdomain(referral);
+ buffer_puts(buffer_1,"\n");
+ return;
+ }
+
+ pos = posanswers;
+ for (j = 0;j < numanswers + numauthority + numglue;++j) {
+ pos = dns_packet_getname(buf,len,pos,&t1); if (!pos) goto DIE;
+ pos = dns_packet_copy(buf,len,pos,header,10); if (!pos) goto DIE;
+ uint16_unpack_big(header + 8,&datalen);
+ if (dns_domain_suffix(t1,control))
+ if (byte_equal(header + 2,2,DNS_C_IN)) {
+ if (typematch(header,DNS_T_NS)) {
+ if (!dns_packet_getname(buf,len,pos,&t2)) goto DIE;
+ ns_add(t1,t2);
+ }
+ else if (typematch(header,DNS_T_A) && datalen == 4) {
+ if (!dns_packet_copy(buf,len,pos,misc,4)) goto DIE;
+ address_add(t1,misc);
+ }
+ }
+ pos += datalen;
+ }
+
+
+ if (flagcname) {
+ query_add(cname,dtype);
+ buffer_put(buffer_1,querystr.s,querystr.len);
+ buffer_puts(buffer_1,"CNAME:");
+ printdomain(cname);
+ buffer_puts(buffer_1,"\n");
+ return;
+ }
+ if (rcode == 3) {
+ buffer_put(buffer_1,querystr.s,querystr.len);
+ buffer_puts(buffer_1,"NXDOMAIN\n");
+ return;
+ }
+ if (flagout || flagsoa || !flagreferral) {
+ if (!flagout) {
+ buffer_put(buffer_1,querystr.s,querystr.len);
+ buffer_puts(buffer_1,"NODATA\n");
+ return;
+ }
+ pos = posanswers;
+ for (j = 0;j < numanswers + numauthority + numglue;++j) {
+ pos = printrecord(&tmp,buf,len,pos,d,dtype);
+ if (!pos) goto DIE;
+ if (tmp.len) {
+ buffer_put(buffer_1,querystr.s,querystr.len);
+ buffer_puts(buffer_1,"answer:");
+ buffer_put(buffer_1,tmp.s,tmp.len); /* includes \n */
+ }
+ }
+ return;
+ }
+
+ if (!dns_domain_suffix(d,referral)) goto DIE;
+ buffer_put(buffer_1,querystr.s,querystr.len);
+ buffer_puts(buffer_1,"see:");
+ printdomain(referral);
+ buffer_puts(buffer_1,"\n");
+ return;
+
+ DIE:
+ x = error_str(errno);
+ buffer_put(buffer_1,querystr.s,querystr.len);
+ buffer_puts(buffer_1,"ALERT:unable to parse response packet; ");
+ buffer_puts(buffer_1,x);
+ buffer_puts(buffer_1,"\n");
+}
+
+int main(int argc,char **argv)
+{
+ static stralloc out;
+ static stralloc fqdn;
+ static stralloc udn;
+ static char *q;
+ char *control;
+ char type[2];
+ char ip[64];
+ int i;
+ uint16 u16;
+
+ dns_random_init(seed);
+
+ if (!stralloc_copys(&querystr,"0:.:.:start:")) nomem();
+
+ if (!address_alloc_readyplus(&address,1)) nomem();
+ if (!query_alloc_readyplus(&query,1)) nomem();
+ if (!ns_alloc_readyplus(&ns,1)) nomem();
+ if (!qt_alloc_readyplus(&qt,1)) nomem();
+
+ if (!*argv) usage();
+ if (!*++argv) usage();
+ if (!parsetype(*argv,type)) usage();
+
+ if (!*++argv) usage();
+ if (!dns_domain_fromdot(&q,*argv,str_len(*argv))) nomem();
+
+ query_add(q,type);
+ ns_add("","");
+
+ while (*++argv) {
+ if (!stralloc_copys(&udn,*argv)) nomem();
+ if (dns_ip4_qualify(&out,&fqdn,&udn) == -1) nomem(); /* XXX */
+ for (i = 0;i + 4 <= out.len;i += 4)
+ address_add("",out.s + i);
+ }
+
+ for (i = 0;i < qt.len;++i) {
+ if (!dns_domain_copy(&q,qt.s[i].owner)) nomem();
+ control = qt.s[i].control;
+ if (!dns_domain_suffix(q,control)) continue;
+ byte_copy(type,2,qt.s[i].type);
+ byte_copy(ip,4,qt.s[i].ip);
+
+ if (!stralloc_copys(&querystr,"")) nomem();
+ uint16_unpack_big(type,&u16);
+ if (!stralloc_catulong0(&querystr,u16,0)) nomem();
+ if (!stralloc_cats(&querystr,":")) nomem();
+ if (!dns_domain_todot_cat(&querystr,q)) nomem();
+ if (!stralloc_cats(&querystr,":")) nomem();
+ if (!dns_domain_todot_cat(&querystr,control)) nomem();
+ if (!stralloc_cats(&querystr,":")) nomem();
+ if (!stralloc_catb(&querystr,ipstr,ip4_fmt(ipstr,ip))) nomem();
+ if (!stralloc_cats(&querystr,":")) nomem();
+
+ buffer_put(buffer_1,querystr.s,querystr.len);
+ buffer_puts(buffer_1,"tx\n");
+ buffer_flush(buffer_1);
+
+ if (resolve(q,type,ip) == -1) {
+ const char *x = error_str(errno);
+ buffer_put(buffer_1,querystr.s,querystr.len);
+ buffer_puts(buffer_1,"ALERT:query failed; ");
+ buffer_puts(buffer_1,x);
+ buffer_puts(buffer_1,"\n");
+ }
+ else
+ parsepacket(tx.packet,tx.packetlen,q,type,control);
+
+ if (dns_domain_equal(q,"\011localhost\0")) {
+ buffer_put(buffer_1,querystr.s,querystr.len);
+ buffer_puts(buffer_1,"ALERT:some caches do not handle localhost internally\n");
+ address_add(q,"\177\0\0\1");
+ }
+ if (dd(q,"",ip) == 4) {
+ buffer_put(buffer_1,querystr.s,querystr.len);
+ buffer_puts(buffer_1,"ALERT:some caches do not handle IP addresses internally\n");
+ address_add(q,ip);
+ }
+
+ buffer_flush(buffer_1);
+ }
+
+ _exit(0);
+}
diff --git a/dnstracesort.sh b/dnstracesort.sh
new file mode 100644
index 0000000..e57359c
--- /dev/null
+++ b/dnstracesort.sh
@@ -0,0 +1,51 @@
+awk -F: '
+ BEGIN { OFS=":" }
+ {
+ if ($5 == "tx") next
+ if ($5 == "A") {
+ print "glue",$6,$3,$4,"answer",$6" A "$7
+ next
+ }
+ if ($5 == "NS") {
+ print "glue",$6,$3,$4,"answer",$6" NS "$7
+ next
+ }
+ print
+ }
+' | sort -t: +0 -2 +4 +3 -4 +2 -3 | uniq | awk -F: '
+ {
+ type = $1
+ q = $2
+ c = $3
+ ip = sprintf("%-16s",$4)
+
+ if (q != lastq) { print ""; lastq = q }
+
+ if ($5 == "ALERT") {
+ result = "A\bAL\bLE\bER\bRT\bT:\b: " $6
+ }
+ else if ($5 == "answer") {
+ if (index($6,q" ") == 1)
+ $6 = substr($6,length(q) + 2)
+ result = $6
+ }
+ else if ($5 == "see") {
+ result = "see " $6
+ }
+ else if ($5 == "CNAME") {
+ result = "CNAME "$6
+ }
+ else
+ result = $5
+
+ if (c != ".") {
+ q = substr(q,1,length(q) - length(c))
+ for (i = 1;i <= length(c);++i) {
+ ci = substr(c,i,1)
+ q = q "_\b" ci
+ }
+ }
+
+ print type,q,ip,result
+ }
+'
diff --git a/dnstxt.c b/dnstxt.c
new file mode 100644
index 0000000..0880b30
--- /dev/null
+++ b/dnstxt.c
@@ -0,0 +1,33 @@
+#include "buffer.h"
+#include "exit.h"
+#include "strerr.h"
+#include "dns.h"
+
+#define FATAL "dnstxt: fatal: "
+
+static char seed[128];
+
+static stralloc fqdn;
+static stralloc out;
+
+int main(int argc,char **argv)
+{
+ dns_random_init(seed);
+
+ if (*argv) ++argv;
+
+ while (*argv) {
+ if (!stralloc_copys(&fqdn,*argv))
+ strerr_die2x(111,FATAL,"out of memory");
+ if (dns_txt(&out,&fqdn) == -1)
+ strerr_die4sys(111,FATAL,"unable to find TXT records for ",*argv,": ");
+
+ buffer_put(buffer_1,out.s,out.len);
+ buffer_puts(buffer_1,"\n");
+
+ ++argv;
+ }
+
+ buffer_flush(buffer_1);
+ _exit(0);
+}
diff --git a/droproot.c b/droproot.c
new file mode 100644
index 0000000..33e8f18
--- /dev/null
+++ b/droproot.c
@@ -0,0 +1,33 @@
+#include <unistd.h>
+#include "env.h"
+#include "scan.h"
+#include "prot.h"
+#include "strerr.h"
+
+void droproot(const char *fatal)
+{
+ char *x;
+ unsigned long id;
+
+ x = env_get("ROOT");
+ if (!x)
+ strerr_die2x(111,fatal,"$ROOT not set");
+ if (chdir(x) == -1)
+ strerr_die4sys(111,fatal,"unable to chdir to ",x,": ");
+ if (chroot(".") == -1)
+ strerr_die4sys(111,fatal,"unable to chroot to ",x,": ");
+
+ x = env_get("GID");
+ if (!x)
+ strerr_die2x(111,fatal,"$GID not set");
+ scan_ulong(x,&id);
+ if (prot_gid((int) id) == -1)
+ strerr_die2sys(111,fatal,"unable to setgid: ");
+
+ x = env_get("UID");
+ if (!x)
+ strerr_die2x(111,fatal,"$UID not set");
+ scan_ulong(x,&id);
+ if (prot_uid((int) id) == -1)
+ strerr_die2sys(111,fatal,"unable to setuid: ");
+}
diff --git a/droproot.h b/droproot.h
new file mode 100644
index 0000000..b8a53a7
--- /dev/null
+++ b/droproot.h
@@ -0,0 +1,6 @@
+#ifndef DROPROOT_H
+#define DROPROOT_H
+
+extern void droproot(const char *);
+
+#endif
diff --git a/env.c b/env.c
new file mode 100644
index 0000000..86849a2
--- /dev/null
+++ b/env.c
@@ -0,0 +1,15 @@
+#include "str.h"
+#include "env.h"
+
+extern /*@null@*/char *env_get(const char *s)
+{
+ int i;
+ unsigned int len;
+
+ if (!s) return 0;
+ len = str_len(s);
+ for (i = 0;environ[i];++i)
+ if (str_start(environ[i],s) && (environ[i][len] == '='))
+ return environ[i] + len + 1;
+ return 0;
+}
diff --git a/env.h b/env.h
new file mode 100644
index 0000000..d7ecf48
--- /dev/null
+++ b/env.h
@@ -0,0 +1,8 @@
+#ifndef ENV_H
+#define ENV_H
+
+extern char **environ;
+
+extern /*@null@*/char *env_get(const char *);
+
+#endif
diff --git a/error.c b/error.c
new file mode 100644
index 0000000..14adef0
--- /dev/null
+++ b/error.c
@@ -0,0 +1,123 @@
+#include <errno.h>
+#include "error.h"
+
+/* warning: as coverage improves here, should update error_{str,temp} */
+
+int error_intr =
+#ifdef EINTR
+EINTR;
+#else
+-1;
+#endif
+
+int error_nomem =
+#ifdef ENOMEM
+ENOMEM;
+#else
+-2;
+#endif
+
+int error_noent =
+#ifdef ENOENT
+ENOENT;
+#else
+-3;
+#endif
+
+int error_txtbsy =
+#ifdef ETXTBSY
+ETXTBSY;
+#else
+-4;
+#endif
+
+int error_io =
+#ifdef EIO
+EIO;
+#else
+-5;
+#endif
+
+int error_exist =
+#ifdef EEXIST
+EEXIST;
+#else
+-6;
+#endif
+
+int error_timeout =
+#ifdef ETIMEDOUT
+ETIMEDOUT;
+#else
+-7;
+#endif
+
+int error_inprogress =
+#ifdef EINPROGRESS
+EINPROGRESS;
+#else
+-8;
+#endif
+
+int error_wouldblock =
+#ifdef EWOULDBLOCK
+EWOULDBLOCK;
+#else
+-9;
+#endif
+
+int error_again =
+#ifdef EAGAIN
+EAGAIN;
+#else
+-10;
+#endif
+
+int error_pipe =
+#ifdef EPIPE
+EPIPE;
+#else
+-11;
+#endif
+
+int error_perm =
+#ifdef EPERM
+EPERM;
+#else
+-12;
+#endif
+
+int error_acces =
+#ifdef EACCES
+EACCES;
+#else
+-13;
+#endif
+
+int error_nodevice =
+#ifdef ENXIO
+ENXIO;
+#else
+-14;
+#endif
+
+int error_proto =
+#ifdef EPROTO
+EPROTO;
+#else
+-15;
+#endif
+
+int error_isdir =
+#ifdef EISDIR
+EISDIR;
+#else
+-16;
+#endif
+
+int error_connrefused =
+#ifdef ECONNREFUSED
+ECONNREFUSED;
+#else
+-17;
+#endif
diff --git a/error.h b/error.h
new file mode 100644
index 0000000..35c976e
--- /dev/null
+++ b/error.h
@@ -0,0 +1,27 @@
+#ifndef ERROR_H
+#define ERROR_H
+
+extern int errno;
+
+extern int error_intr;
+extern int error_nomem;
+extern int error_noent;
+extern int error_txtbsy;
+extern int error_io;
+extern int error_exist;
+extern int error_timeout;
+extern int error_inprogress;
+extern int error_wouldblock;
+extern int error_again;
+extern int error_pipe;
+extern int error_perm;
+extern int error_acces;
+extern int error_nodevice;
+extern int error_proto;
+extern int error_isdir;
+extern int error_connrefused;
+
+extern const char *error_str(int);
+extern int error_temp(int);
+
+#endif
diff --git a/error_str.c b/error_str.c
new file mode 100644
index 0000000..74e1330
--- /dev/null
+++ b/error_str.c
@@ -0,0 +1,267 @@
+#include <errno.h>
+#include "error.h"
+
+#define X(e,s) if (i == e) return s;
+
+const char *error_str(int i)
+{
+ X(0,"no error")
+ X(error_intr,"interrupted system call")
+ X(error_nomem,"out of memory")
+ X(error_noent,"file does not exist")
+ X(error_txtbsy,"text busy")
+ X(error_io,"input/output error")
+ X(error_exist,"file already exists")
+ X(error_timeout,"timed out")
+ X(error_inprogress,"operation in progress")
+ X(error_again,"temporary failure")
+ X(error_wouldblock,"input/output would block")
+ X(error_pipe,"broken pipe")
+ X(error_perm,"permission denied")
+ X(error_acces,"access denied")
+ X(error_nodevice,"device not configured")
+ X(error_proto,"protocol error")
+ X(error_isdir,"is a directory")
+ X(error_connrefused,"connection refused")
+#ifdef ESRCH
+ X(ESRCH,"no such process")
+#endif
+#ifdef E2BIG
+ X(E2BIG,"argument list too long")
+#endif
+#ifdef ENOEXEC
+ X(ENOEXEC,"exec format error")
+#endif
+#ifdef EBADF
+ X(EBADF,"file descriptor not open")
+#endif
+#ifdef ECHILD
+ X(ECHILD,"no child processes")
+#endif
+#ifdef EDEADLK
+ X(EDEADLK,"operation would cause deadlock")
+#endif
+#ifdef EFAULT
+ X(EFAULT,"bad address")
+#endif
+#ifdef ENOTBLK
+ X(ENOTBLK,"not a block device")
+#endif
+#ifdef EBUSY
+ X(EBUSY,"device busy")
+#endif
+#ifdef EXDEV
+ X(EXDEV,"cross-device link")
+#endif
+#ifdef ENODEV
+ X(ENODEV,"device does not support operation")
+#endif
+#ifdef ENOTDIR
+ X(ENOTDIR,"not a directory")
+#endif
+#ifdef EINVAL
+ X(EINVAL,"invalid argument")
+#endif
+#ifdef ENFILE
+ X(ENFILE,"system cannot open more files")
+#endif
+#ifdef EMFILE
+ X(EMFILE,"process cannot open more files")
+#endif
+#ifdef ENOTTY
+ X(ENOTTY,"not a tty")
+#endif
+#ifdef EFBIG
+ X(EFBIG,"file too big")
+#endif
+#ifdef ENOSPC
+ X(ENOSPC,"out of disk space")
+#endif
+#ifdef ESPIPE
+ X(ESPIPE,"unseekable descriptor")
+#endif
+#ifdef EROFS
+ X(EROFS,"read-only file system")
+#endif
+#ifdef EMLINK
+ X(EMLINK,"too many links")
+#endif
+#ifdef EDOM
+ X(EDOM,"input out of range")
+#endif
+#ifdef ERANGE
+ X(ERANGE,"output out of range")
+#endif
+#ifdef EALREADY
+ X(EALREADY,"operation already in progress")
+#endif
+#ifdef ENOTSOCK
+ X(ENOTSOCK,"not a socket")
+#endif
+#ifdef EDESTADDRREQ
+ X(EDESTADDRREQ,"destination address required")
+#endif
+#ifdef EMSGSIZE
+ X(EMSGSIZE,"message too long")
+#endif
+#ifdef EPROTOTYPE
+ X(EPROTOTYPE,"incorrect protocol type")
+#endif
+#ifdef ENOPROTOOPT
+ X(ENOPROTOOPT,"protocol not available")
+#endif
+#ifdef EPROTONOSUPPORT
+ X(EPROTONOSUPPORT,"protocol not supported")
+#endif
+#ifdef ESOCKTNOSUPPORT
+ X(ESOCKTNOSUPPORT,"socket type not supported")
+#endif
+#ifdef EOPNOTSUPP
+ X(EOPNOTSUPP,"operation not supported")
+#endif
+#ifdef EPFNOSUPPORT
+ X(EPFNOSUPPORT,"protocol family not supported")
+#endif
+#ifdef EAFNOSUPPORT
+ X(EAFNOSUPPORT,"address family not supported")
+#endif
+#ifdef EADDRINUSE
+ X(EADDRINUSE,"address already used")
+#endif
+#ifdef EADDRNOTAVAIL
+ X(EADDRNOTAVAIL,"address not available")
+#endif
+#ifdef ENETDOWN
+ X(ENETDOWN,"network down")
+#endif
+#ifdef ENETUNREACH
+ X(ENETUNREACH,"network unreachable")
+#endif
+#ifdef ENETRESET
+ X(ENETRESET,"network reset")
+#endif
+#ifdef ECONNABORTED
+ X(ECONNABORTED,"connection aborted")
+#endif
+#ifdef ECONNRESET
+ X(ECONNRESET,"connection reset")
+#endif
+#ifdef ENOBUFS
+ X(ENOBUFS,"out of buffer space")
+#endif
+#ifdef EISCONN
+ X(EISCONN,"already connected")
+#endif
+#ifdef ENOTCONN
+ X(ENOTCONN,"not connected")
+#endif
+#ifdef ESHUTDOWN
+ X(ESHUTDOWN,"socket shut down")
+#endif
+#ifdef ETOOMANYREFS
+ X(ETOOMANYREFS,"too many references")
+#endif
+#ifdef ELOOP
+ X(ELOOP,"symbolic link loop")
+#endif
+#ifdef ENAMETOOLONG
+ X(ENAMETOOLONG,"file name too long")
+#endif
+#ifdef EHOSTDOWN
+ X(EHOSTDOWN,"host down")
+#endif
+#ifdef EHOSTUNREACH
+ X(EHOSTUNREACH,"host unreachable")
+#endif
+#ifdef ENOTEMPTY
+ X(ENOTEMPTY,"directory not empty")
+#endif
+#ifdef EPROCLIM
+ X(EPROCLIM,"too many processes")
+#endif
+#ifdef EUSERS
+ X(EUSERS,"too many users")
+#endif
+#ifdef EDQUOT
+ X(EDQUOT,"disk quota exceeded")
+#endif
+#ifdef ESTALE
+ X(ESTALE,"stale NFS file handle")
+#endif
+#ifdef EREMOTE
+ X(EREMOTE,"too many levels of remote in path")
+#endif
+#ifdef EBADRPC
+ X(EBADRPC,"RPC structure is bad")
+#endif
+#ifdef ERPCMISMATCH
+ X(ERPCMISMATCH,"RPC version mismatch")
+#endif
+#ifdef EPROGUNAVAIL
+ X(EPROGUNAVAIL,"RPC program unavailable")
+#endif
+#ifdef EPROGMISMATCH
+ X(EPROGMISMATCH,"program version mismatch")
+#endif
+#ifdef EPROCUNAVAIL
+ X(EPROCUNAVAIL,"bad procedure for program")
+#endif
+#ifdef ENOLCK
+ X(ENOLCK,"no locks available")
+#endif
+#ifdef ENOSYS
+ X(ENOSYS,"system call not available")
+#endif
+#ifdef EFTYPE
+ X(EFTYPE,"bad file type")
+#endif
+#ifdef EAUTH
+ X(EAUTH,"authentication error")
+#endif
+#ifdef ENEEDAUTH
+ X(ENEEDAUTH,"not authenticated")
+#endif
+#ifdef ENOSTR
+ X(ENOSTR,"not a stream device")
+#endif
+#ifdef ETIME
+ X(ETIME,"timer expired")
+#endif
+#ifdef ENOSR
+ X(ENOSR,"out of stream resources")
+#endif
+#ifdef ENOMSG
+ X(ENOMSG,"no message of desired type")
+#endif
+#ifdef EBADMSG
+ X(EBADMSG,"bad message type")
+#endif
+#ifdef EIDRM
+ X(EIDRM,"identifier removed")
+#endif
+#ifdef ENONET
+ X(ENONET,"machine not on network")
+#endif
+#ifdef ERREMOTE
+ X(ERREMOTE,"object not local")
+#endif
+#ifdef ENOLINK
+ X(ENOLINK,"link severed")
+#endif
+#ifdef EADV
+ X(EADV,"advertise error")
+#endif
+#ifdef ESRMNT
+ X(ESRMNT,"srmount error")
+#endif
+#ifdef ECOMM
+ X(ECOMM,"communication error")
+#endif
+#ifdef EMULTIHOP
+ X(EMULTIHOP,"multihop attempted")
+#endif
+#ifdef EREMCHG
+ X(EREMCHG,"remote address changed")
+#endif
+ return "unknown error";
+}
diff --git a/exit.h b/exit.h
new file mode 100644
index 0000000..39011c8
--- /dev/null
+++ b/exit.h
@@ -0,0 +1,6 @@
+#ifndef EXIT_H
+#define EXIT_H
+
+extern void _exit();
+
+#endif
diff --git a/find-systype.sh b/find-systype.sh
new file mode 100644
index 0000000..9f6e565
--- /dev/null
+++ b/find-systype.sh
@@ -0,0 +1,143 @@
+# oper-:arch-:syst-:chip-:kern-
+# oper = operating system type; e.g., sunos-4.1.4
+# arch = machine language; e.g., sparc
+# syst = which binaries can run; e.g., sun4
+# chip = chip model; e.g., micro-2-80
+# kern = kernel version; e.g., sun4m
+# dependence: arch --- chip
+# \ \
+# oper --- syst --- kern
+# so, for example, syst is interpreted in light of oper, but chip is not.
+# anyway, no slashes, no extra colons, no uppercase letters.
+# the point of the extra -'s is to ease parsing: can add hierarchies later.
+# e.g., *:i386-*:*:pentium-*:* would handle pentium-100 as well as pentium,
+# and i386-486 (486s do have more instructions, you know) as well as i386.
+# the idea here is to include ALL useful available information.
+
+exec 2>/dev/null
+
+sys="`uname -s | tr '/:[A-Z]' '..[a-z]'`"
+if [ x"$sys" != x ]
+then
+ unamer="`uname -r | tr /: ..`"
+ unamem="`uname -m | tr /: ..`"
+ unamev="`uname -v | tr /: ..`"
+
+ case "$sys" in
+ bsd.os|freebsd|netbsd|openbsd)
+ # in bsd 4.4, uname -v does not have useful info.
+ # in bsd 4.4, uname -m is arch, not chip.
+ oper="$sys-$unamer"
+ arch="$unamem"
+ syst=""
+ chip="`sysctl -n hw.model`" # hopefully
+ kern=""
+ ;;
+ linux)
+ # as in bsd 4.4, uname -v does not have useful info.
+ oper="$sys-$unamer"
+ syst=""
+ chip="$unamem"
+ kern=""
+ case "$chip" in
+ i386|i486|i586|i686)
+ arch="i386"
+ ;;
+ alpha)
+ arch="alpha"
+ ;;
+ esac
+ ;;
+ aix)
+ # naturally IBM has to get uname -r and uname -v backwards. dorks.
+ oper="$sys-$unamev-$unamer"
+ arch="`arch | tr /: ..`"
+ syst=""
+ chip="$unamem"
+ kern=""
+ ;;
+ sunos)
+ oper="$sys-$unamer-$unamev"
+ arch="`(uname -p || mach) | tr /: ..`"
+ syst="`arch | tr /: ..`"
+ chip="$unamem" # this is wrong; is there any way to get the real info?
+ kern="`arch -k | tr /: ..`"
+ ;;
+ unix_sv)
+ oper="$sys-$unamer-$unamev"
+ arch="`uname -m`"
+ syst=""
+ chip="$unamem"
+ kern=""
+ ;;
+ *)
+ oper="$sys-$unamer-$unamev"
+ arch="`arch | tr /: ..`"
+ syst=""
+ chip="$unamem"
+ kern=""
+ ;;
+ esac
+else
+ gcc -c trycpp.c
+ gcc -o trycpp trycpp.o
+ case `./trycpp` in
+ nextstep)
+ oper="nextstep-`hostinfo | sed -n 's/^[ ]*NeXT Mach \([^:]*\):.*$/\1/p'`"
+ arch="`hostinfo | sed -n 's/^Processor type: \(.*\) (.*)$/\1/p' | tr /: ..`"
+ syst=""
+ chip="`hostinfo | sed -n 's/^Processor type: .* (\(.*\))$/\1/p' | tr ' /:' '...'`"
+ kern=""
+ ;;
+ *)
+ oper="unknown"
+ arch=""
+ syst=""
+ chip=""
+ kern=""
+ ;;
+ esac
+ rm -f trycpp.o trycpp
+fi
+
+case "$chip" in
+80486)
+ # let's try to be consistent here. (BSD/OS)
+ chip=i486
+ ;;
+i486DX)
+ # respect the hyphen hierarchy. (FreeBSD)
+ chip=i486-dx
+ ;;
+i486.DX2)
+ # respect the hyphen hierarchy. (FreeBSD)
+ chip=i486-dx2
+ ;;
+Intel.586)
+ # no, you nitwits, there is no such chip. (NeXTStep)
+ chip=pentium
+ ;;
+i586)
+ # no, you nitwits, there is no such chip. (Linux)
+ chip=pentium
+ ;;
+i686)
+ # STOP SAYING THAT! (Linux)
+ chip=ppro
+esac
+
+if gcc -c x86cpuid.c
+then
+ if gcc -o x86cpuid x86cpuid.o
+ then
+ x86cpuid="`./x86cpuid | tr /: ..`"
+ case "$x86cpuid" in
+ ?*)
+ chip="$x86cpuid"
+ ;;
+ esac
+ fi
+fi
+rm -f x86cpuid x86cpuid.o
+
+echo "$oper-:$arch-:$syst-:$chip-:$kern-" | tr ' [A-Z]' '.[a-z]'
diff --git a/fmt.h b/fmt.h
new file mode 100644
index 0000000..b0bfce5
--- /dev/null
+++ b/fmt.h
@@ -0,0 +1,25 @@
+#ifndef FMT_H
+#define FMT_H
+
+#define FMT_ULONG 40 /* enough space to hold 2^128 - 1 in decimal, plus \0 */
+#define FMT_LEN ((char *) 0) /* convenient abbreviation */
+
+extern unsigned int fmt_uint(char *,unsigned int);
+extern unsigned int fmt_uint0(char *,unsigned int,unsigned int);
+extern unsigned int fmt_xint(char *,unsigned int);
+extern unsigned int fmt_nbbint(char *,unsigned int,unsigned int,unsigned int,unsigned int);
+extern unsigned int fmt_ushort(char *,unsigned short);
+extern unsigned int fmt_xshort(char *,unsigned short);
+extern unsigned int fmt_nbbshort(char *,unsigned int,unsigned int,unsigned int,unsigned short);
+extern unsigned int fmt_ulong(char *,unsigned long);
+extern unsigned int fmt_xlong(char *,unsigned long);
+extern unsigned int fmt_nbblong(char *,unsigned int,unsigned int,unsigned int,unsigned long);
+
+extern unsigned int fmt_plusminus(char *,int);
+extern unsigned int fmt_minus(char *,int);
+extern unsigned int fmt_0x(char *,int);
+
+extern unsigned int fmt_str(char *,const char *);
+extern unsigned int fmt_strn(char *,const char *,unsigned int);
+
+#endif
diff --git a/fmt_ulong.c b/fmt_ulong.c
new file mode 100644
index 0000000..db48bfd
--- /dev/null
+++ b/fmt_ulong.c
@@ -0,0 +1,13 @@
+#include "fmt.h"
+
+unsigned int fmt_ulong(register char *s,register unsigned long u)
+{
+ register unsigned int len; register unsigned long q;
+ len = 1; q = u;
+ while (q > 9) { ++len; q /= 10; }
+ if (s) {
+ s += len;
+ do { *--s = '0' + (u % 10); u /= 10; } while(u); /* handles u == 0 */
+ }
+ return len;
+}
diff --git a/gen_alloc.h b/gen_alloc.h
new file mode 100644
index 0000000..b94a956
--- /dev/null
+++ b/gen_alloc.h
@@ -0,0 +1,7 @@
+#ifndef GEN_ALLOC_H
+#define GEN_ALLOC_H
+
+#define GEN_ALLOC_typedef(ta,type,field,len,a) \
+ typedef struct ta { type *field; unsigned int len; unsigned int a; } ta;
+
+#endif
diff --git a/gen_allocdefs.h b/gen_allocdefs.h
new file mode 100644
index 0000000..5e136c0
--- /dev/null
+++ b/gen_allocdefs.h
@@ -0,0 +1,34 @@
+#ifndef GEN_ALLOC_DEFS_H
+#define GEN_ALLOC_DEFS_H
+
+#define GEN_ALLOC_ready(ta,type,field,len,a,i,n,x,base,ta_ready) \
+int ta_ready(register ta *x,register unsigned int n) \
+{ register unsigned int i; \
+ if (x->field) { \
+ i = x->a; \
+ if (n > i) { \
+ x->a = base + n + (n >> 3); \
+ if (alloc_re(&x->field,i * sizeof(type),x->a * sizeof(type))) return 1; \
+ x->a = i; return 0; } \
+ return 1; } \
+ x->len = 0; \
+ return !!(x->field = (type *) alloc((x->a = n) * sizeof(type))); }
+
+#define GEN_ALLOC_readyplus(ta,type,field,len,a,i,n,x,base,ta_rplus) \
+int ta_rplus(register ta *x,register unsigned int n) \
+{ register unsigned int i; \
+ if (x->field) { \
+ i = x->a; n += x->len; \
+ if (n > i) { \
+ x->a = base + n + (n >> 3); \
+ if (alloc_re(&x->field,i * sizeof(type),x->a * sizeof(type))) return 1; \
+ x->a = i; return 0; } \
+ return 1; } \
+ x->len = 0; \
+ return !!(x->field = (type *) alloc((x->a = n) * sizeof(type))); }
+
+#define GEN_ALLOC_append(ta,type,field,len,a,i,n,x,base,ta_rplus,ta_append) \
+int ta_append(register ta *x,register const type *i) \
+{ if (!ta_rplus(x,1)) return 0; x->field[x->len++] = *i; return 1; }
+
+#endif
diff --git a/generic-conf.c b/generic-conf.c
new file mode 100644
index 0000000..db4aa1d
--- /dev/null
+++ b/generic-conf.c
@@ -0,0 +1,99 @@
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "strerr.h"
+#include "buffer.h"
+#include "open.h"
+#include "generic-conf.h"
+
+static const char *fatal;
+static const char *dir;
+static const char *fn;
+
+static int fd;
+static char buf[1024];
+static buffer ss;
+
+void init(const char *d,const char *f)
+{
+ dir = d;
+ fatal = f;
+ umask(022);
+ if (mkdir(dir,0700) == -1)
+ strerr_die4sys(111,fatal,"unable to create ",dir,": ");
+ if (chmod(dir,03755) == -1)
+ strerr_die4sys(111,fatal,"unable to set mode of ",dir,": ");
+ if (chdir(dir) == -1)
+ strerr_die4sys(111,fatal,"unable to switch to ",dir,": ");
+}
+
+void fail(void)
+{
+ strerr_die6sys(111,fatal,"unable to create ",dir,"/",fn,": ");
+}
+
+void makedir(const char *s)
+{
+ fn = s;
+ if (mkdir(fn,0700) == -1) fail();
+}
+
+void start(const char *s)
+{
+ fn = s;
+ fd = open_trunc(fn);
+ if (fd == -1) fail();
+ buffer_init(&ss,buffer_unixwrite,fd,buf,sizeof buf);
+}
+
+void outs(const char *s)
+{
+ if (buffer_puts(&ss,s) == -1) fail();
+}
+
+void out(const char *s,unsigned int len)
+{
+ if (buffer_put(&ss,s,len) == -1) fail();
+}
+
+void copyfrom(buffer *b)
+{
+ if (buffer_copy(&ss,b) < 0) fail();
+}
+
+void finish(void)
+{
+ if (buffer_flush(&ss) == -1) fail();
+ if (fsync(fd) == -1) fail();
+ close(fd);
+}
+
+void perm(int mode)
+{
+ if (chmod(fn,mode) == -1) fail();
+}
+
+void owner(int uid,int gid)
+{
+ if (chown(fn,uid,gid) == -1) fail();
+}
+
+void makelog(const char *user,int uid,int gid)
+{
+ makedir("log");
+ perm(02755);
+ makedir("log/main");
+ owner(uid,gid);
+ perm(02755);
+ start("log/status");
+ finish();
+ owner(uid,gid);
+ perm(0644);
+
+ start("log/run");
+ outs("#!/bin/sh\nexec");
+ outs(" setuidgid "); outs(user);
+ outs(" multilog t ./main\n");
+ finish();
+ perm(0755);
+}
diff --git a/generic-conf.h b/generic-conf.h
new file mode 100644
index 0000000..41dbeea
--- /dev/null
+++ b/generic-conf.h
@@ -0,0 +1,20 @@
+#ifndef GENERIC_CONF_H
+#define GENERIC_CONF_H
+
+#include "buffer.h"
+
+extern void init(const char *,const char *);
+
+extern void makedir(const char *);
+
+extern void start(const char *);
+extern void outs(const char *);
+extern void out(const char *,unsigned int);
+extern void copyfrom(buffer *);
+extern void finish(void);
+
+extern void perm(int);
+extern void owner(int,int);
+extern void makelog(const char *,int,int);
+
+#endif
diff --git a/getln.c b/getln.c
new file mode 100644
index 0000000..489621c
--- /dev/null
+++ b/getln.c
@@ -0,0 +1,14 @@
+#include "byte.h"
+#include "getln.h"
+
+int getln(buffer *ss,stralloc *sa,int *match,int sep)
+{
+ char *cont;
+ unsigned int clen;
+
+ if (getln2(ss,sa,&cont,&clen,sep) == -1) return -1;
+ if (!clen) { *match = 0; return 0; }
+ if (!stralloc_catb(sa,cont,clen)) return -1;
+ *match = 1;
+ return 0;
+}
diff --git a/getln.h b/getln.h
new file mode 100644
index 0000000..3cae45f
--- /dev/null
+++ b/getln.h
@@ -0,0 +1,10 @@
+#ifndef GETLN_H
+#define GETLN_H
+
+#include "buffer.h"
+#include "stralloc.h"
+
+extern int getln(buffer *,stralloc *,int *,int);
+extern int getln2(buffer *,stralloc *,char **,unsigned int *,int);
+
+#endif
diff --git a/getln2.c b/getln2.c
new file mode 100644
index 0000000..bf622a4
--- /dev/null
+++ b/getln2.c
@@ -0,0 +1,24 @@
+#include "byte.h"
+#include "getln.h"
+
+int getln2(buffer *ss,stralloc *sa,char **cont,unsigned int *clen,int sep)
+{
+ register char *x;
+ register unsigned int i;
+ int n;
+
+ if (!stralloc_ready(sa,0)) return -1;
+ sa->len = 0;
+
+ for (;;) {
+ n = buffer_feed(ss);
+ if (n < 0) return -1;
+ if (n == 0) { *clen = 0; return 0; }
+ x = buffer_PEEK(ss);
+ i = byte_chr(x,n,sep);
+ if (i < n) { buffer_SEEK(ss,*clen = i + 1); *cont = x; return 0; }
+ if (!stralloc_readyplus(sa,n)) return -1;
+ i = sa->len;
+ sa->len = i + buffer_get(ss,sa->s + i,n);
+ }
+}
diff --git a/hasdevtcp.h1 b/hasdevtcp.h1
new file mode 100644
index 0000000..65e880e
--- /dev/null
+++ b/hasdevtcp.h1
@@ -0,0 +1 @@
+/* sysdep: -devtcp */
diff --git a/hasdevtcp.h2 b/hasdevtcp.h2
new file mode 100644
index 0000000..b12ffe8
--- /dev/null
+++ b/hasdevtcp.h2
@@ -0,0 +1,2 @@
+/* sysdep: +devtcp */
+#define HASDEVTCP 1
diff --git a/hasshsgr.h1 b/hasshsgr.h1
new file mode 100644
index 0000000..d11c988
--- /dev/null
+++ b/hasshsgr.h1
@@ -0,0 +1 @@
+/* sysdep: -shortsetgroups */
diff --git a/hasshsgr.h2 b/hasshsgr.h2
new file mode 100644
index 0000000..db6a830
--- /dev/null
+++ b/hasshsgr.h2
@@ -0,0 +1,2 @@
+/* sysdep: +shortsetgroups */
+#define HASSHORTSETGROUPS 1
diff --git a/hier.c b/hier.c
new file mode 100644
index 0000000..4aef75b
--- /dev/null
+++ b/hier.c
@@ -0,0 +1,42 @@
+#include "auto_home.h"
+
+void hier()
+{
+ c("/","etc","dnsroots.global",-1,-1,0644);
+
+ h(auto_home,-1,-1,02755);
+ d(auto_home,"bin",-1,-1,02755);
+
+ c(auto_home,"bin","dnscache-conf",-1,-1,0755);
+ c(auto_home,"bin","tinydns-conf",-1,-1,0755);
+ c(auto_home,"bin","walldns-conf",-1,-1,0755);
+ c(auto_home,"bin","rbldns-conf",-1,-1,0755);
+ c(auto_home,"bin","pickdns-conf",-1,-1,0755);
+ c(auto_home,"bin","axfrdns-conf",-1,-1,0755);
+
+ c(auto_home,"bin","dnscache",-1,-1,0755);
+ c(auto_home,"bin","tinydns",-1,-1,0755);
+ c(auto_home,"bin","walldns",-1,-1,0755);
+ c(auto_home,"bin","rbldns",-1,-1,0755);
+ c(auto_home,"bin","pickdns",-1,-1,0755);
+ c(auto_home,"bin","axfrdns",-1,-1,0755);
+
+ c(auto_home,"bin","tinydns-get",-1,-1,0755);
+ c(auto_home,"bin","tinydns-data",-1,-1,0755);
+ c(auto_home,"bin","tinydns-edit",-1,-1,0755);
+ c(auto_home,"bin","rbldns-data",-1,-1,0755);
+ c(auto_home,"bin","pickdns-data",-1,-1,0755);
+ c(auto_home,"bin","axfr-get",-1,-1,0755);
+
+ c(auto_home,"bin","dnsip",-1,-1,0755);
+ c(auto_home,"bin","dnsipq",-1,-1,0755);
+ c(auto_home,"bin","dnsname",-1,-1,0755);
+ c(auto_home,"bin","dnstxt",-1,-1,0755);
+ c(auto_home,"bin","dnsmx",-1,-1,0755);
+ c(auto_home,"bin","dnsfilter",-1,-1,0755);
+ c(auto_home,"bin","random-ip",-1,-1,0755);
+ c(auto_home,"bin","dnsqr",-1,-1,0755);
+ c(auto_home,"bin","dnsq",-1,-1,0755);
+ c(auto_home,"bin","dnstrace",-1,-1,0755);
+ c(auto_home,"bin","dnstracesort",-1,-1,0755);
+}
diff --git a/install.c b/install.c
new file mode 100644
index 0000000..62f0e7f
--- /dev/null
+++ b/install.c
@@ -0,0 +1,151 @@
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "buffer.h"
+#include "strerr.h"
+#include "error.h"
+#include "open.h"
+#include "exit.h"
+
+extern void hier();
+
+#define FATAL "install: fatal: "
+
+int fdsourcedir = -1;
+
+void h(home,uid,gid,mode)
+char *home;
+int uid;
+int gid;
+int mode;
+{
+ if (mkdir(home,0700) == -1)
+ if (errno != error_exist)
+ strerr_die4sys(111,FATAL,"unable to mkdir ",home,": ");
+ if (chown(home,uid,gid) == -1)
+ strerr_die4sys(111,FATAL,"unable to chown ",home,": ");
+ if (chmod(home,mode) == -1)
+ strerr_die4sys(111,FATAL,"unable to chmod ",home,": ");
+}
+
+void d(home,subdir,uid,gid,mode)
+char *home;
+char *subdir;
+int uid;
+int gid;
+int mode;
+{
+ if (chdir(home) == -1)
+ strerr_die4sys(111,FATAL,"unable to switch to ",home,": ");
+ if (mkdir(subdir,0700) == -1)
+ if (errno != error_exist)
+ strerr_die6sys(111,FATAL,"unable to mkdir ",home,"/",subdir,": ");
+ if (chown(subdir,uid,gid) == -1)
+ strerr_die6sys(111,FATAL,"unable to chown ",home,"/",subdir,": ");
+ if (chmod(subdir,mode) == -1)
+ strerr_die6sys(111,FATAL,"unable to chmod ",home,"/",subdir,": ");
+}
+
+char inbuf[BUFFER_INSIZE];
+char outbuf[BUFFER_OUTSIZE];
+buffer ssin;
+buffer ssout;
+
+void c(home,subdir,file,uid,gid,mode)
+char *home;
+char *subdir;
+char *file;
+int uid;
+int gid;
+int mode;
+{
+ int fdin;
+ int fdout;
+
+ if (fchdir(fdsourcedir) == -1)
+ strerr_die2sys(111,FATAL,"unable to switch back to source directory: ");
+
+ fdin = open_read(file);
+ if (fdin == -1)
+ strerr_die4sys(111,FATAL,"unable to read ",file,": ");
+ buffer_init(&ssin,buffer_unixread,fdin,inbuf,sizeof inbuf);
+
+ if (chdir(home) == -1)
+ strerr_die4sys(111,FATAL,"unable to switch to ",home,": ");
+ if (chdir(subdir) == -1)
+ strerr_die6sys(111,FATAL,"unable to switch to ",home,"/",subdir,": ");
+
+ fdout = open_trunc(file);
+ if (fdout == -1)
+ strerr_die6sys(111,FATAL,"unable to write .../",subdir,"/",file,": ");
+ buffer_init(&ssout,buffer_unixwrite,fdout,outbuf,sizeof outbuf);
+
+ switch(buffer_copy(&ssout,&ssin)) {
+ case -2:
+ strerr_die4sys(111,FATAL,"unable to read ",file,": ");
+ case -3:
+ strerr_die6sys(111,FATAL,"unable to write .../",subdir,"/",file,": ");
+ }
+
+ close(fdin);
+ if (buffer_flush(&ssout) == -1)
+ strerr_die6sys(111,FATAL,"unable to write .../",subdir,"/",file,": ");
+ if (fsync(fdout) == -1)
+ strerr_die6sys(111,FATAL,"unable to write .../",subdir,"/",file,": ");
+ if (close(fdout) == -1) /* NFS silliness */
+ strerr_die6sys(111,FATAL,"unable to write .../",subdir,"/",file,": ");
+
+ if (chown(file,uid,gid) == -1)
+ strerr_die6sys(111,FATAL,"unable to chown .../",subdir,"/",file,": ");
+ if (chmod(file,mode) == -1)
+ strerr_die6sys(111,FATAL,"unable to chmod .../",subdir,"/",file,": ");
+}
+
+void z(home,subdir,file,len,uid,gid,mode)
+char *home;
+char *subdir;
+char *file;
+int len;
+int uid;
+int gid;
+int mode;
+{
+ int fdout;
+
+ if (chdir(home) == -1)
+ strerr_die4sys(111,FATAL,"unable to switch to ",home,": ");
+ if (chdir(subdir) == -1)
+ strerr_die6sys(111,FATAL,"unable to switch to ",home,"/",subdir,": ");
+
+ fdout = open_trunc(file);
+ if (fdout == -1)
+ strerr_die6sys(111,FATAL,"unable to write .../",subdir,"/",file,": ");
+ buffer_init(&ssout,buffer_unixwrite,fdout,outbuf,sizeof outbuf);
+
+ while (len-- > 0)
+ if (buffer_put(&ssout,"",1) == -1)
+ strerr_die6sys(111,FATAL,"unable to write .../",subdir,"/",file,": ");
+
+ if (buffer_flush(&ssout) == -1)
+ strerr_die6sys(111,FATAL,"unable to write .../",subdir,"/",file,": ");
+ if (fsync(fdout) == -1)
+ strerr_die6sys(111,FATAL,"unable to write .../",subdir,"/",file,": ");
+ if (close(fdout) == -1) /* NFS silliness */
+ strerr_die6sys(111,FATAL,"unable to write .../",subdir,"/",file,": ");
+
+ if (chown(file,uid,gid) == -1)
+ strerr_die6sys(111,FATAL,"unable to chown .../",subdir,"/",file,": ");
+ if (chmod(file,mode) == -1)
+ strerr_die6sys(111,FATAL,"unable to chmod .../",subdir,"/",file,": ");
+}
+
+int main()
+{
+ fdsourcedir = open_read(".");
+ if (fdsourcedir == -1)
+ strerr_die2sys(111,FATAL,"unable to open current directory: ");
+
+ umask(077);
+ hier();
+ _exit(0);
+}
diff --git a/instcheck.c b/instcheck.c
new file mode 100644
index 0000000..06ed547
--- /dev/null
+++ b/instcheck.c
@@ -0,0 +1,108 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include "strerr.h"
+#include "error.h"
+#include "exit.h"
+
+extern void hier();
+
+#define FATAL "instcheck: fatal: "
+#define WARNING "instcheck: warning: "
+
+void perm(prefix1,prefix2,prefix3,file,type,uid,gid,mode)
+char *prefix1;
+char *prefix2;
+char *prefix3;
+char *file;
+int type;
+int uid;
+int gid;
+int mode;
+{
+ struct stat st;
+
+ if (stat(file,&st) == -1) {
+ if (errno == error_noent)
+ strerr_warn6(WARNING,prefix1,prefix2,prefix3,file," does not exist",0);
+ else
+ strerr_warn4(WARNING,"unable to stat .../",file,": ",&strerr_sys);
+ return;
+ }
+
+ if ((uid != -1) && (st.st_uid != uid))
+ strerr_warn6(WARNING,prefix1,prefix2,prefix3,file," has wrong owner",0);
+ if ((gid != -1) && (st.st_gid != gid))
+ strerr_warn6(WARNING,prefix1,prefix2,prefix3,file," has wrong group",0);
+ if ((st.st_mode & 07777) != mode)
+ strerr_warn6(WARNING,prefix1,prefix2,prefix3,file," has wrong permissions",0);
+ if ((st.st_mode & S_IFMT) != type)
+ strerr_warn6(WARNING,prefix1,prefix2,prefix3,file," has wrong type",0);
+}
+
+void h(home,uid,gid,mode)
+char *home;
+int uid;
+int gid;
+int mode;
+{
+ perm("","","",home,S_IFDIR,uid,gid,mode);
+}
+
+void d(home,subdir,uid,gid,mode)
+char *home;
+char *subdir;
+int uid;
+int gid;
+int mode;
+{
+ if (chdir(home) == -1)
+ strerr_die4sys(111,FATAL,"unable to switch to ",home,": ");
+ perm("",home,"/",subdir,S_IFDIR,uid,gid,mode);
+}
+
+void p(home,fifo,uid,gid,mode)
+char *home;
+char *fifo;
+int uid;
+int gid;
+int mode;
+{
+ if (chdir(home) == -1)
+ strerr_die4sys(111,FATAL,"unable to switch to ",home,": ");
+ perm("",home,"/",fifo,S_IFIFO,uid,gid,mode);
+}
+
+void c(home,subdir,file,uid,gid,mode)
+char *home;
+char *subdir;
+char *file;
+int uid;
+int gid;
+int mode;
+{
+ if (chdir(home) == -1)
+ strerr_die4sys(111,FATAL,"unable to switch to ",home,": ");
+ if (chdir(subdir) == -1)
+ strerr_die6sys(111,FATAL,"unable to switch to ",home,"/",subdir,": ");
+ perm(".../",subdir,"/",file,S_IFREG,uid,gid,mode);
+}
+
+void z(home,file,len,uid,gid,mode)
+char *home;
+char *file;
+int len;
+int uid;
+int gid;
+int mode;
+{
+ if (chdir(home) == -1)
+ strerr_die4sys(111,FATAL,"unable to switch to ",home,": ");
+ perm("",home,"/",file,S_IFREG,uid,gid,mode);
+}
+
+int main()
+{
+ hier();
+ _exit(0);
+}
diff --git a/iopause.c b/iopause.c
new file mode 100644
index 0000000..b8034de
--- /dev/null
+++ b/iopause.c
@@ -0,0 +1,76 @@
+#include "taia.h"
+#include "select.h"
+#include "iopause.h"
+
+void iopause(iopause_fd *x,unsigned int len,struct taia *deadline,struct taia *stamp)
+{
+ struct taia t;
+ int millisecs;
+ double d;
+ int i;
+
+ if (taia_less(deadline,stamp))
+ millisecs = 0;
+ else {
+ t = *stamp;
+ taia_sub(&t,deadline,&t);
+ d = taia_approx(&t);
+ if (d > 1000.0) d = 1000.0;
+ millisecs = d * 1000.0 + 20.0;
+ }
+
+ for (i = 0;i < len;++i)
+ x[i].revents = 0;
+
+#ifdef IOPAUSE_POLL
+
+ poll(x,len,millisecs);
+ /* XXX: some kernels apparently need x[0] even if len is 0 */
+ /* XXX: how to handle EAGAIN? are kernels really this dumb? */
+ /* XXX: how to handle EINVAL? when exactly can this happen? */
+
+#else
+{
+
+ struct timeval tv;
+ fd_set rfds;
+ fd_set wfds;
+ int nfds;
+ int fd;
+
+ FD_ZERO(&rfds);
+ FD_ZERO(&wfds);
+
+ nfds = 1;
+ for (i = 0;i < len;++i) {
+ fd = x[i].fd;
+ if (fd < 0) continue;
+ if (fd >= 8 * sizeof(fd_set)) continue; /*XXX*/
+
+ if (fd >= nfds) nfds = fd + 1;
+ if (x[i].events & IOPAUSE_READ) FD_SET(fd,&rfds);
+ if (x[i].events & IOPAUSE_WRITE) FD_SET(fd,&wfds);
+ }
+
+ tv.tv_sec = millisecs / 1000;
+ tv.tv_usec = 1000 * (millisecs % 1000);
+
+ if (select(nfds,&rfds,&wfds,(fd_set *) 0,&tv) <= 0)
+ return;
+ /* XXX: for EBADF, could seek out and destroy the bad descriptor */
+
+ for (i = 0;i < len;++i) {
+ fd = x[i].fd;
+ if (fd < 0) continue;
+ if (fd >= 8 * sizeof(fd_set)) continue; /*XXX*/
+
+ if (x[i].events & IOPAUSE_READ)
+ if (FD_ISSET(fd,&rfds)) x[i].revents |= IOPAUSE_READ;
+ if (x[i].events & IOPAUSE_WRITE)
+ if (FD_ISSET(fd,&wfds)) x[i].revents |= IOPAUSE_WRITE;
+ }
+
+}
+#endif
+
+}
diff --git a/iopause.h1 b/iopause.h1
new file mode 100644
index 0000000..dae0a33
--- /dev/null
+++ b/iopause.h1
@@ -0,0 +1,19 @@
+#ifndef IOPAUSE_H
+#define IOPAUSE_H
+
+/* sysdep: -poll */
+
+typedef struct {
+ int fd;
+ short events;
+ short revents;
+} iopause_fd;
+
+#define IOPAUSE_READ 1
+#define IOPAUSE_WRITE 4
+
+#include "taia.h"
+
+extern void iopause(iopause_fd *,unsigned int,struct taia *,struct taia *);
+
+#endif
diff --git a/iopause.h2 b/iopause.h2
new file mode 100644
index 0000000..2cf5cf8
--- /dev/null
+++ b/iopause.h2
@@ -0,0 +1,18 @@
+#ifndef IOPAUSE_H
+#define IOPAUSE_H
+
+/* sysdep: +poll */
+#define IOPAUSE_POLL
+
+#include <sys/types.h>
+#include <poll.h>
+
+typedef struct pollfd iopause_fd;
+#define IOPAUSE_READ POLLIN
+#define IOPAUSE_WRITE POLLOUT
+
+#include "taia.h"
+
+extern void iopause(iopause_fd *,unsigned int,struct taia *,struct taia *);
+
+#endif
diff --git a/ip4.h b/ip4.h
new file mode 100644
index 0000000..923d0ed
--- /dev/null
+++ b/ip4.h
@@ -0,0 +1,9 @@
+#ifndef IP4_H
+#define IP4_H
+
+extern unsigned int ip4_scan(const char *,char *);
+extern unsigned int ip4_fmt(char *,const char *);
+
+#define IP4_FMT 20
+
+#endif
diff --git a/ip4_fmt.c b/ip4_fmt.c
new file mode 100644
index 0000000..bbad4c7
--- /dev/null
+++ b/ip4_fmt.c
@@ -0,0 +1,18 @@
+#include "fmt.h"
+#include "ip4.h"
+
+unsigned int ip4_fmt(char *s,const char ip[4])
+{
+ unsigned int len;
+ unsigned int i;
+
+ len = 0;
+ i = fmt_ulong(s,(unsigned long) (unsigned char) ip[0]); len += i; if (s) s += i;
+ if (s) *s++ = '.'; ++len;
+ i = fmt_ulong(s,(unsigned long) (unsigned char) ip[1]); len += i; if (s) s += i;
+ if (s) *s++ = '.'; ++len;
+ i = fmt_ulong(s,(unsigned long) (unsigned char) ip[2]); len += i; if (s) s += i;
+ if (s) *s++ = '.'; ++len;
+ i = fmt_ulong(s,(unsigned long) (unsigned char) ip[3]); len += i; if (s) s += i;
+ return len;
+}
diff --git a/ip4_scan.c b/ip4_scan.c
new file mode 100644
index 0000000..e9538ec
--- /dev/null
+++ b/ip4_scan.c
@@ -0,0 +1,19 @@
+#include "scan.h"
+#include "ip4.h"
+
+unsigned int ip4_scan(const char *s,char ip[4])
+{
+ unsigned int i;
+ unsigned int len;
+ unsigned long u;
+
+ len = 0;
+ i = scan_ulong(s,&u); if (!i) return 0; ip[0] = u; s += i; len += i;
+ if (*s != '.') return 0; ++s; ++len;
+ i = scan_ulong(s,&u); if (!i) return 0; ip[1] = u; s += i; len += i;
+ if (*s != '.') return 0; ++s; ++len;
+ i = scan_ulong(s,&u); if (!i) return 0; ip[2] = u; s += i; len += i;
+ if (*s != '.') return 0; ++s; ++len;
+ i = scan_ulong(s,&u); if (!i) return 0; ip[3] = u; s += i; len += i;
+ return len;
+}
diff --git a/log.c b/log.c
new file mode 100644
index 0000000..c43e8b0
--- /dev/null
+++ b/log.c
@@ -0,0 +1,288 @@
+#include "buffer.h"
+#include "uint32.h"
+#include "uint16.h"
+#include "error.h"
+#include "byte.h"
+#include "log.h"
+
+/* work around gcc 2.95.2 bug */
+#define number(x) ( (u64 = (x)), u64_print() )
+static uint64 u64;
+static void u64_print(void)
+{
+ char buf[20];
+ unsigned int pos;
+
+ pos = sizeof buf;
+ do {
+ if (!pos) break;
+ buf[--pos] = '0' + (u64 % 10);
+ u64 /= 10;
+ } while(u64);
+
+ buffer_put(buffer_2,buf + pos,sizeof buf - pos);
+}
+
+static void hex(unsigned char c)
+{
+ buffer_put(buffer_2,"0123456789abcdef" + (c >> 4),1);
+ buffer_put(buffer_2,"0123456789abcdef" + (c & 15),1);
+}
+
+static void string(const char *s)
+{
+ buffer_puts(buffer_2,s);
+}
+
+static void line(void)
+{
+ string("\n");
+ buffer_flush(buffer_2);
+}
+
+static void space(void)
+{
+ string(" ");
+}
+
+static void ip(const char i[4])
+{
+ hex(i[0]);
+ hex(i[1]);
+ hex(i[2]);
+ hex(i[3]);
+}
+
+static void logid(const char id[2])
+{
+ hex(id[0]);
+ hex(id[1]);
+}
+
+static void logtype(const char type[2])
+{
+ uint16 u;
+
+ uint16_unpack_big(type,&u);
+ number(u);
+}
+
+static void name(const char *q)
+{
+ char ch;
+ int state;
+
+ if (!*q) {
+ string(".");
+ return;
+ }
+ while (state = *q++) {
+ while (state) {
+ ch = *q++;
+ --state;
+ if ((ch <= 32) || (ch > 126)) ch = '?';
+ if ((ch >= 'A') && (ch <= 'Z')) ch += 32;
+ buffer_put(buffer_2,&ch,1);
+ }
+ string(".");
+ }
+}
+
+void log_startup(void)
+{
+ string("starting");
+ line();
+}
+
+void log_query(uint64 *qnum,const char client[4],unsigned int port,const char id[2],const char *q,const char qtype[2])
+{
+ string("query "); number(*qnum); space();
+ ip(client); string(":"); hex(port >> 8); hex(port & 255);
+ string(":"); logid(id); space();
+ logtype(qtype); space(); name(q);
+ line();
+}
+
+void log_querydone(uint64 *qnum,unsigned int len)
+{
+ string("sent "); number(*qnum); space();
+ number(len);
+ line();
+}
+
+void log_querydrop(uint64 *qnum)
+{
+ const char *x = error_str(errno);
+
+ string("drop "); number(*qnum); space();
+ string(x);
+ line();
+}
+
+void log_tcpopen(const char client[4],unsigned int port)
+{
+ string("tcpopen ");
+ ip(client); string(":"); hex(port >> 8); hex(port & 255);
+ line();
+}
+
+void log_tcpclose(const char client[4],unsigned int port)
+{
+ const char *x = error_str(errno);
+ string("tcpclose ");
+ ip(client); string(":"); hex(port >> 8); hex(port & 255); space();
+ string(x);
+ line();
+}
+
+void log_tx(const char *q,const char qtype[2],const char *control,const char servers[64],unsigned int gluelessness)
+{
+ int i;
+
+ string("tx "); number(gluelessness); space();
+ logtype(qtype); space(); name(q); space();
+ name(control);
+ for (i = 0;i < 64;i += 4)
+ if (byte_diff(servers + i,4,"\0\0\0\0")) {
+ space();
+ ip(servers + i);
+ }
+ line();
+}
+
+void log_cachedanswer(const char *q,const char type[2])
+{
+ string("cached "); logtype(type); space();
+ name(q);
+ line();
+}
+
+void log_cachedcname(const char *dn,const char *dn2)
+{
+ string("cached cname "); name(dn); space(); name(dn2);
+ line();
+}
+
+void log_cachedns(const char *control,const char *ns)
+{
+ string("cached ns "); name(control); space(); name(ns);
+ line();
+}
+
+void log_cachednxdomain(const char *dn)
+{
+ string("cached nxdomain "); name(dn);
+ line();
+}
+
+void log_nxdomain(const char server[4],const char *q,unsigned int ttl)
+{
+ string("nxdomain "); ip(server); space(); number(ttl); space();
+ name(q);
+ line();
+}
+
+void log_nodata(const char server[4],const char *q,const char qtype[2],unsigned int ttl)
+{
+ string("nodata "); ip(server); space(); number(ttl); space();
+ logtype(qtype); space(); name(q);
+ line();
+}
+
+void log_lame(const char server[4],const char *control,const char *referral)
+{
+ string("lame "); ip(server); space();
+ name(control); space(); name(referral);
+ line();
+}
+
+void log_servfail(const char *dn)
+{
+ const char *x = error_str(errno);
+
+ string("servfail "); name(dn); space();
+ string(x);
+ line();
+}
+
+void log_rr(const char server[4],const char *q,const char type[2],const char *buf,unsigned int len,unsigned int ttl)
+{
+ int i;
+
+ string("rr "); ip(server); space(); number(ttl); space();
+ logtype(type); space(); name(q); space();
+
+ for (i = 0;i < len;++i) {
+ hex(buf[i]);
+ if (i > 30) {
+ string("...");
+ break;
+ }
+ }
+ line();
+}
+
+void log_rrns(const char server[4],const char *q,const char *data,unsigned int ttl)
+{
+ string("rr "); ip(server); space(); number(ttl);
+ string(" ns "); name(q); space();
+ name(data);
+ line();
+}
+
+void log_rrcname(const char server[4],const char *q,const char *data,unsigned int ttl)
+{
+ string("rr "); ip(server); space(); number(ttl);
+ string(" cname "); name(q); space();
+ name(data);
+ line();
+}
+
+void log_rrptr(const char server[4],const char *q,const char *data,unsigned int ttl)
+{
+ string("rr "); ip(server); space(); number(ttl);
+ string(" ptr "); name(q); space();
+ name(data);
+ line();
+}
+
+void log_rrmx(const char server[4],const char *q,const char *mx,const char pref[2],unsigned int ttl)
+{
+ uint16 u;
+
+ string("rr "); ip(server); space(); number(ttl);
+ string(" mx "); name(q); space();
+ uint16_unpack_big(pref,&u);
+ number(u); space(); name(mx);
+ line();
+}
+
+void log_rrsoa(const char server[4],const char *q,const char *n1,const char *n2,const char misc[20],unsigned int ttl)
+{
+ uint32 u;
+ int i;
+
+ string("rr "); ip(server); space(); number(ttl);
+ string(" soa "); name(q); space();
+ name(n1); space(); name(n2);
+ for (i = 0;i < 20;i += 4) {
+ uint32_unpack_big(misc + i,&u);
+ space(); number(u);
+ }
+ line();
+}
+
+void log_stats(void)
+{
+ extern uint64 numqueries;
+ extern uint64 cache_motion;
+ extern int uactive;
+ extern int tactive;
+
+ string("stats ");
+ number(numqueries); space();
+ number(cache_motion); space();
+ number(uactive); space();
+ number(tactive);
+ line();
+}
diff --git a/log.h b/log.h
new file mode 100644
index 0000000..fe62fa3
--- /dev/null
+++ b/log.h
@@ -0,0 +1,36 @@
+#ifndef LOG_H
+#define LOG_H
+
+#include "uint64.h"
+
+extern void log_startup(void);
+
+extern void log_query(uint64 *,const char *,unsigned int,const char *,const char *,const char *);
+extern void log_querydrop(uint64 *);
+extern void log_querydone(uint64 *,unsigned int);
+
+extern void log_tcpopen(const char *,unsigned int);
+extern void log_tcpclose(const char *,unsigned int);
+
+extern void log_cachedanswer(const char *,const char *);
+extern void log_cachedcname(const char *,const char *);
+extern void log_cachednxdomain(const char *);
+extern void log_cachedns(const char *,const char *);
+
+extern void log_tx(const char *,const char *,const char *,const char *,unsigned int);
+
+extern void log_nxdomain(const char *,const char *,unsigned int);
+extern void log_nodata(const char *,const char *,const char *,unsigned int);
+extern void log_servfail(const char *);
+extern void log_lame(const char *,const char *,const char *);
+
+extern void log_rr(const char *,const char *,const char *,const char *,unsigned int,unsigned int);
+extern void log_rrns(const char *,const char *,const char *,unsigned int);
+extern void log_rrcname(const char *,const char *,const char *,unsigned int);
+extern void log_rrptr(const char *,const char *,const char *,unsigned int);
+extern void log_rrmx(const char *,const char *,const char *,const char *,unsigned int);
+extern void log_rrsoa(const char *,const char *,const char *,const char *,const char *,unsigned int);
+
+extern void log_stats(void);
+
+#endif
diff --git a/ndelay.h b/ndelay.h
new file mode 100644
index 0000000..60b788c
--- /dev/null
+++ b/ndelay.h
@@ -0,0 +1,7 @@
+#ifndef NDELAY_H
+#define NDELAY_H
+
+extern int ndelay_on(int);
+extern int ndelay_off(int);
+
+#endif
diff --git a/ndelay_off.c b/ndelay_off.c
new file mode 100644
index 0000000..9daa8cd
--- /dev/null
+++ b/ndelay_off.c
@@ -0,0 +1,12 @@
+#include <sys/types.h>
+#include <fcntl.h>
+#include "ndelay.h"
+
+#ifndef O_NONBLOCK
+#define O_NONBLOCK O_NDELAY
+#endif
+
+int ndelay_off(int fd)
+{
+ return fcntl(fd,F_SETFL,fcntl(fd,F_GETFL,0) & ~O_NONBLOCK);
+}
diff --git a/ndelay_on.c b/ndelay_on.c
new file mode 100644
index 0000000..eccd8c8
--- /dev/null
+++ b/ndelay_on.c
@@ -0,0 +1,12 @@
+#include <sys/types.h>
+#include <fcntl.h>
+#include "ndelay.h"
+
+#ifndef O_NONBLOCK
+#define O_NONBLOCK O_NDELAY
+#endif
+
+int ndelay_on(int fd)
+{
+ return fcntl(fd,F_SETFL,fcntl(fd,F_GETFL,0) | O_NONBLOCK);
+}
diff --git a/okclient.c b/okclient.c
new file mode 100644
index 0000000..a648c02
--- /dev/null
+++ b/okclient.c
@@ -0,0 +1,26 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "str.h"
+#include "ip4.h"
+#include "okclient.h"
+
+static char fn[3 + IP4_FMT];
+
+int okclient(char ip[4])
+{
+ struct stat st;
+ int i;
+
+ fn[0] = 'i';
+ fn[1] = 'p';
+ fn[2] = '/';
+ fn[3 + ip4_fmt(fn + 3,ip)] = 0;
+
+ for (;;) {
+ if (stat(fn,&st) == 0) return 1;
+ /* treat temporary error as rejection */
+ i = str_rchr(fn,'.');
+ if (!fn[i]) return 0;
+ fn[i] = 0;
+ }
+}
diff --git a/okclient.h b/okclient.h
new file mode 100644
index 0000000..e9b7dd6
--- /dev/null
+++ b/okclient.h
@@ -0,0 +1,6 @@
+#ifndef OKCLIENT_H
+#define OKCLIENT_H
+
+extern int okclient(char *);
+
+#endif
diff --git a/open.h b/open.h
new file mode 100644
index 0000000..1fcd99f
--- /dev/null
+++ b/open.h
@@ -0,0 +1,10 @@
+#ifndef OPEN_H
+#define OPEN_H
+
+extern int open_read(const char *);
+extern int open_excl(const char *);
+extern int open_append(const char *);
+extern int open_trunc(const char *);
+extern int open_write(const char *);
+
+#endif
diff --git a/open_read.c b/open_read.c
new file mode 100644
index 0000000..2a63a25
--- /dev/null
+++ b/open_read.c
@@ -0,0 +1,6 @@
+#include <sys/types.h>
+#include <fcntl.h>
+#include "open.h"
+
+int open_read(const char *fn)
+{ return open(fn,O_RDONLY | O_NDELAY); }
diff --git a/open_trunc.c b/open_trunc.c
new file mode 100644
index 0000000..9d0f1dc
--- /dev/null
+++ b/open_trunc.c
@@ -0,0 +1,6 @@
+#include <sys/types.h>
+#include <fcntl.h>
+#include "open.h"
+
+int open_trunc(const char *fn)
+{ return open(fn,O_WRONLY | O_NDELAY | O_TRUNC | O_CREAT,0644); }
diff --git a/openreadclose.c b/openreadclose.c
new file mode 100644
index 0000000..cbc5c6c
--- /dev/null
+++ b/openreadclose.c
@@ -0,0 +1,16 @@
+#include "error.h"
+#include "open.h"
+#include "readclose.h"
+#include "openreadclose.h"
+
+int openreadclose(const char *fn,stralloc *sa,unsigned int bufsize)
+{
+ int fd;
+ fd = open_read(fn);
+ if (fd == -1) {
+ if (errno == error_noent) return 0;
+ return -1;
+ }
+ if (readclose(fd,sa,bufsize) == -1) return -1;
+ return 1;
+}
diff --git a/openreadclose.h b/openreadclose.h
new file mode 100644
index 0000000..2d4042e
--- /dev/null
+++ b/openreadclose.h
@@ -0,0 +1,8 @@
+#ifndef OPENREADCLOSE_H
+#define OPENREADCLOSE_H
+
+#include "stralloc.h"
+
+extern int openreadclose(const char *,stralloc *,unsigned int);
+
+#endif
diff --git a/parsetype.c b/parsetype.c
new file mode 100644
index 0000000..167aaa4
--- /dev/null
+++ b/parsetype.c
@@ -0,0 +1,31 @@
+#include "scan.h"
+#include "byte.h"
+#include "case.h"
+#include "dns.h"
+#include "uint16.h"
+#include "parsetype.h"
+
+int parsetype(char *s,char type[2])
+{
+ unsigned long u;
+
+ if (!s[scan_ulong(s,&u)]) uint16_pack_big(type,u);
+ else if (case_equals(s,"any")) byte_copy(type,2,DNS_T_ANY);
+ else if (case_equals(s,"a")) byte_copy(type,2,DNS_T_A);
+ else if (case_equals(s,"ns")) byte_copy(type,2,DNS_T_NS);
+ else if (case_equals(s,"mx")) byte_copy(type,2,DNS_T_MX);
+ else if (case_equals(s,"ptr")) byte_copy(type,2,DNS_T_PTR);
+ else if (case_equals(s,"txt")) byte_copy(type,2,DNS_T_TXT);
+ else if (case_equals(s,"cname")) byte_copy(type,2,DNS_T_CNAME);
+ else if (case_equals(s,"soa")) byte_copy(type,2,DNS_T_SOA);
+ else if (case_equals(s,"hinfo")) byte_copy(type,2,DNS_T_HINFO);
+ else if (case_equals(s,"rp")) byte_copy(type,2,DNS_T_RP);
+ else if (case_equals(s,"sig")) byte_copy(type,2,DNS_T_SIG);
+ else if (case_equals(s,"key")) byte_copy(type,2,DNS_T_KEY);
+ else if (case_equals(s,"aaaa")) byte_copy(type,2,DNS_T_AAAA);
+ else if (case_equals(s,"axfr")) byte_copy(type,2,DNS_T_AXFR);
+ else
+ return 0;
+
+ return 1;
+}
diff --git a/parsetype.h b/parsetype.h
new file mode 100644
index 0000000..4851725
--- /dev/null
+++ b/parsetype.h
@@ -0,0 +1,6 @@
+#ifndef PARSETYPE_H
+#define PARSETYPE_H
+
+extern int parsetype(char *,char *);
+
+#endif
diff --git a/pickdns-conf.c b/pickdns-conf.c
new file mode 100644
index 0000000..9edd184
--- /dev/null
+++ b/pickdns-conf.c
@@ -0,0 +1,66 @@
+#include <unistd.h>
+#include <pwd.h>
+#include "strerr.h"
+#include "exit.h"
+#include "auto_home.h"
+#include "generic-conf.h"
+
+#define FATAL "pickdns-conf: fatal: "
+
+void usage(void)
+{
+ strerr_die1x(100,"pickdns-conf: usage: pickdns-conf acct logacct /pickdns myip");
+}
+
+char *dir;
+char *user;
+char *loguser;
+struct passwd *pw;
+char *myip;
+
+int main(int argc,char **argv)
+{
+ user = argv[1];
+ if (!user) usage();
+ loguser = argv[2];
+ if (!loguser) usage();
+ dir = argv[3];
+ if (!dir) usage();
+ if (dir[0] != '/') usage();
+ myip = argv[4];
+ if (!myip) usage();
+
+ pw = getpwnam(loguser);
+ if (!pw)
+ strerr_die3x(111,FATAL,"unknown account ",loguser);
+
+ init(dir,FATAL);
+ makelog(loguser,pw->pw_uid,pw->pw_gid);
+
+ makedir("env");
+ perm(02755);
+ start("env/ROOT"); outs(dir); outs("/root\n"); finish();
+ perm(0644);
+ start("env/IP"); outs(myip); outs("\n"); finish();
+ perm(0644);
+
+ start("run");
+ outs("#!/bin/sh\nexec 2>&1\nexec envuidgid "); outs(user);
+ outs(" envdir ./env softlimit -d250000 ");
+ outs(auto_home); outs("/bin/pickdns\n");
+ finish();
+ perm(0755);
+
+ makedir("root");
+ perm(02755);
+ start("root/data");
+ finish();
+ perm(0644);
+ start("root/Makefile");
+ outs("data.cdb: data\n");
+ outs("\t"); outs(auto_home); outs("/bin/pickdns-data\n");
+ finish();
+ perm(0644);
+
+ _exit(0);
+}
diff --git a/pickdns-data.c b/pickdns-data.c
new file mode 100644
index 0000000..60cabb0
--- /dev/null
+++ b/pickdns-data.c
@@ -0,0 +1,230 @@
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include "buffer.h"
+#include "exit.h"
+#include "cdb_make.h"
+#include "open.h"
+#include "alloc.h"
+#include "gen_allocdefs.h"
+#include "stralloc.h"
+#include "getln.h"
+#include "case.h"
+#include "strerr.h"
+#include "str.h"
+#include "byte.h"
+#include "scan.h"
+#include "fmt.h"
+#include "ip4.h"
+#include "dns.h"
+
+#define FATAL "pickdns-data: fatal: "
+
+void nomem(void)
+{
+ strerr_die2x(111,FATAL,"out of memory");
+}
+
+void ipprefix_cat(stralloc *out,char *s)
+{
+ unsigned long u;
+ char ch;
+ unsigned int j;
+
+ for (;;)
+ if (*s == '.')
+ ++s;
+ else {
+ j = scan_ulong(s,&u);
+ if (!j) return;
+ s += j;
+ ch = u;
+ if (!stralloc_catb(out,&ch,1)) nomem();
+ }
+}
+
+struct address {
+ char *name;
+ unsigned int namelen;
+ char ip[4];
+ char location[2];
+} ;
+
+int address_diff(struct address *p,struct address *q)
+{
+ int r;
+
+ r = byte_diff(p->location,2,q->location);
+ if (r < 0) return -1;
+ if (r > 0) return 1;
+ if (p->namelen < q->namelen) return -1;
+ if (p->namelen > q->namelen) return 1;
+ return case_diffb(p->name,p->namelen,q->name);
+}
+
+void address_sort(struct address *z,unsigned int n)
+{
+ unsigned int i;
+ unsigned int j;
+ unsigned int p;
+ unsigned int q;
+ struct address t;
+
+ i = j = n;
+ --z;
+
+ while (j > 1) {
+ if (i > 1) { --i; t = z[i]; }
+ else { t = z[j]; z[j] = z[i]; --j; }
+ q = i;
+ while ((p = q * 2) < j) {
+ if (address_diff(&z[p + 1],&z[p]) >= 0) ++p;
+ z[q] = z[p]; q = p;
+ }
+ if (p == j) {
+ z[q] = z[p]; q = p;
+ }
+ while ((q > i) && (address_diff(&t,&z[p = q/2]) > 0)) {
+ z[q] = z[p]; q = p;
+ }
+ z[q] = t;
+ }
+}
+
+GEN_ALLOC_typedef(address_alloc,struct address,s,len,a)
+GEN_ALLOC_readyplus(address_alloc,struct address,s,len,a,i,n,x,30,address_alloc_readyplus)
+GEN_ALLOC_append(address_alloc,struct address,s,len,a,i,n,x,30,address_alloc_readyplus,address_alloc_append)
+
+static address_alloc x;
+
+int fd;
+buffer b;
+char bspace[1024];
+
+int fdcdb;
+struct cdb_make cdb;
+static stralloc key;
+static stralloc result;
+
+static stralloc line;
+int match = 1;
+unsigned long linenum = 0;
+
+#define NUMFIELDS 3
+static stralloc f[NUMFIELDS];
+
+char strnum[FMT_ULONG];
+
+void syntaxerror(const char *why)
+{
+ strnum[fmt_ulong(strnum,linenum)] = 0;
+ strerr_die4x(111,FATAL,"unable to parse data line ",strnum,why);
+}
+void die_datatmp(void)
+{
+ strerr_die2sys(111,FATAL,"unable to create data.tmp: ");
+}
+
+int main()
+{
+ struct address t;
+ int i;
+ int j;
+ int k;
+ char ch;
+
+ umask(022);
+
+ if (!address_alloc_readyplus(&x,0)) nomem();
+
+ fd = open_read("data");
+ if (fd == -1) strerr_die2sys(111,FATAL,"unable to open data: ");
+ buffer_init(&b,buffer_unixread,fd,bspace,sizeof bspace);
+
+ fdcdb = open_trunc("data.tmp");
+ if (fdcdb == -1) die_datatmp();
+ if (cdb_make_start(&cdb,fdcdb) == -1) die_datatmp();
+
+ while (match) {
+ ++linenum;
+ if (getln(&b,&line,&match,'\n') == -1)
+ strerr_die2sys(111,FATAL,"unable to read line: ");
+
+ while (line.len) {
+ ch = line.s[line.len - 1];
+ if ((ch != ' ') && (ch != '\t') && (ch != '\n')) break;
+ --line.len;
+ }
+ if (!line.len) continue;
+
+ j = 1;
+ for (i = 0;i < NUMFIELDS;++i) {
+ if (j >= line.len) {
+ if (!stralloc_copys(&f[i],"")) nomem();
+ }
+ else {
+ k = byte_chr(line.s + j,line.len - j,':');
+ if (!stralloc_copyb(&f[i],line.s + j,k)) nomem();
+ j += k + 1;
+ }
+ }
+
+ switch(line.s[0]) {
+ default:
+ syntaxerror(": unrecognized leading character");
+ case '#':
+ break;
+ case '-':
+ break;
+ case '+':
+ byte_zero(&t,sizeof t);
+ if (!dns_domain_fromdot(&t.name,f[0].s,f[0].len)) nomem();
+ t.namelen = dns_domain_length(t.name);
+ case_lowerb(t.name,t.namelen);
+ if (!stralloc_0(&f[1])) nomem();
+ if (!ip4_scan(f[1].s,t.ip)) syntaxerror(": malformed IP address");
+ if (!stralloc_0(&f[2])) nomem();
+ if (!stralloc_0(&f[2])) nomem();
+ byte_copy(t.location,2,f[2].s);
+ if (!address_alloc_append(&x,&t)) nomem();
+ break;
+ case '%':
+ if (!stralloc_0(&f[0])) nomem();
+ if (!stralloc_0(&f[0])) nomem();
+ if (!stralloc_copyb(&result,f[0].s,2)) nomem();
+ if (!stralloc_0(&f[1])) nomem();
+ if (!stralloc_copys(&key,"%")) nomem();
+ ipprefix_cat(&key,f[1].s);
+ if (cdb_make_add(&cdb,key.s,key.len,result.s,result.len) == -1)
+ die_datatmp();
+ break;
+ }
+ }
+
+ close(fd);
+ address_sort(x.s,x.len);
+
+ i = 0;
+ while (i < x.len) {
+ for (j = i + 1;j < x.len;++j)
+ if (address_diff(x.s + i,x.s + j))
+ break;
+ if (!stralloc_copys(&key,"+")) nomem();
+ if (!stralloc_catb(&key,x.s[i].location,2)) nomem();
+ if (!stralloc_catb(&key,x.s[i].name,x.s[i].namelen)) nomem();
+ if (!stralloc_copys(&result,"")) nomem();
+ while (i < j)
+ if (!stralloc_catb(&result,x.s[i++].ip,4)) nomem();
+ if (cdb_make_add(&cdb,key.s,key.len,result.s,result.len) == -1)
+ die_datatmp();
+ }
+
+ if (cdb_make_finish(&cdb) == -1) die_datatmp();
+ if (fsync(fdcdb) == -1) die_datatmp();
+ if (close(fdcdb) == -1) die_datatmp(); /* NFS stupidity */
+ if (rename("data.tmp","data.cdb") == -1)
+ strerr_die2sys(111,FATAL,"unable to move data.tmp to data.cdb: ");
+
+ _exit(0);
+}
diff --git a/pickdns.c b/pickdns.c
new file mode 100644
index 0000000..28c4ba5
--- /dev/null
+++ b/pickdns.c
@@ -0,0 +1,101 @@
+#include <unistd.h>
+#include "byte.h"
+#include "case.h"
+#include "dns.h"
+#include "open.h"
+#include "cdb.h"
+#include "response.h"
+
+const char *fatal = "pickdns: fatal: ";
+const char *starting = "starting pickdns\n";
+
+static char seed[128];
+
+void initialize(void)
+{
+ dns_random_init(seed);
+}
+
+static struct cdb c;
+static char key[258];
+static char data[512];
+
+static int doit(char *q,char qtype[2],char ip[4])
+{
+ int r;
+ uint32 dlen;
+ unsigned int qlen;
+ int flaga;
+ int flagmx;
+
+ qlen = dns_domain_length(q);
+ if (qlen > 255) return 0; /* impossible */
+
+ flaga = byte_equal(qtype,2,DNS_T_A);
+ flagmx = byte_equal(qtype,2,DNS_T_MX);
+ if (byte_equal(qtype,2,DNS_T_ANY)) flaga = flagmx = 1;
+ if (!flaga && !flagmx) goto REFUSE;
+
+ key[0] = '%';
+ byte_copy(key + 1,4,ip);
+
+ r = cdb_find(&c,key,5);
+ if (!r) r = cdb_find(&c,key,4);
+ if (!r) r = cdb_find(&c,key,3);
+ if (!r) r = cdb_find(&c,key,2);
+ if (r == -1) return 0;
+
+ key[0] = '+';
+ byte_zero(key + 1,2);
+ if (r && (cdb_datalen(&c) == 2))
+ if (cdb_read(&c,key + 1,2,cdb_datapos(&c)) == -1) return 0;
+
+ byte_copy(key + 3,qlen,q);
+ case_lowerb(key + 3,qlen + 3);
+
+ r = cdb_find(&c,key,qlen + 3);
+ if (!r) {
+ byte_zero(key + 1,2);
+ r = cdb_find(&c,key,qlen + 3);
+ }
+ if (!r) goto REFUSE;
+ if (r == -1) return 0;
+ dlen = cdb_datalen(&c);
+
+ if (dlen > 512) dlen = 512;
+ if (cdb_read(&c,data,dlen,cdb_datapos(&c)) == -1) return 0;
+
+ if (flaga) {
+ dns_sortip(data,dlen);
+ if (dlen > 12) dlen = 12;
+ while (dlen >= 4) {
+ dlen -= 4;
+ if (!response_rstart(q,DNS_T_A,5)) return 0;
+ if (!response_addbytes(data + dlen,4)) return 0;
+ response_rfinish(RESPONSE_ANSWER);
+ }
+ }
+
+ return 1;
+
+
+ REFUSE:
+ response[2] &= ~4;
+ response[3] &= ~15;
+ response[3] |= 5;
+ return 1;
+}
+
+int respond(char *q,char qtype[2],char ip[4])
+{
+ int fd;
+ int result;
+
+ fd = open_read("data.cdb");
+ if (fd == -1) return 0;
+ cdb_init(&c,fd);
+ result = doit(q,qtype,ip);
+ cdb_free(&c);
+ close(fd);
+ return result;
+}
diff --git a/printpacket.c b/printpacket.c
new file mode 100644
index 0000000..7571e08
--- /dev/null
+++ b/printpacket.c
@@ -0,0 +1,90 @@
+#include "uint16.h"
+#include "uint32.h"
+#include "error.h"
+#include "byte.h"
+#include "dns.h"
+#include "printrecord.h"
+#include "printpacket.h"
+
+static char *d;
+
+#define X(s) if (!stralloc_cats(out,s)) return 0;
+#define NUM(u) if (!stralloc_catulong0(out,u,0)) return 0;
+
+unsigned int printpacket_cat(stralloc *out,char *buf,unsigned int len)
+{
+ uint16 numqueries;
+ uint16 numanswers;
+ uint16 numauthority;
+ uint16 numglue;
+ unsigned int pos;
+ char data[12];
+ uint16 type;
+
+ pos = dns_packet_copy(buf,len,0,data,12); if (!pos) return 0;
+
+ uint16_unpack_big(data + 4,&numqueries);
+ uint16_unpack_big(data + 6,&numanswers);
+ uint16_unpack_big(data + 8,&numauthority);
+ uint16_unpack_big(data + 10,&numglue);
+
+ NUM(len)
+ X(" bytes, ")
+ NUM(numqueries)
+ X("+")
+ NUM(numanswers)
+ X("+")
+ NUM(numauthority)
+ X("+")
+ NUM(numglue)
+ X(" records")
+
+ if (data[2] & 128) X(", response")
+ if (data[2] & 120) X(", weird op")
+ if (data[2] & 4) X(", authoritative")
+ if (data[2] & 2) X(", truncated")
+ if (data[2] & 1) X(", weird rd")
+ if (data[3] & 128) X(", weird ra")
+ switch(data[3] & 15) {
+ case 0: X(", noerror"); break;
+ case 3: X(", nxdomain"); break;
+ case 4: X(", notimp"); break;
+ case 5: X(", refused"); break;
+ default: X(", weird rcode");
+ }
+ if (data[3] & 112) X(", weird z")
+
+ X("\n")
+
+ while (numqueries) {
+ --numqueries;
+ X("query: ")
+
+ pos = dns_packet_getname(buf,len,pos,&d); if (!pos) return 0;
+ pos = dns_packet_copy(buf,len,pos,data,4); if (!pos) return 0;
+
+ if (byte_diff(data + 2,2,DNS_C_IN)) {
+ X("weird class")
+ }
+ else {
+ uint16_unpack_big(data,&type);
+ NUM(type)
+ X(" ")
+ if (!dns_domain_todot_cat(out,d)) return 0;
+ }
+ X("\n")
+ }
+
+ for (;;) {
+ if (numanswers) { --numanswers; X("answer: ") }
+ else if (numauthority) { --numauthority; X("authority: ") }
+ else if (numglue) { --numglue; X("additional: ") }
+ else break;
+
+ pos = printrecord_cat(out,buf,len,pos,0,0);
+ if (!pos) return 0;
+ }
+
+ if (pos != len) { errno = error_proto; return 0; }
+ return 1;
+}
diff --git a/printpacket.h b/printpacket.h
new file mode 100644
index 0000000..8c8946d
--- /dev/null
+++ b/printpacket.h
@@ -0,0 +1,8 @@
+#ifndef PRINTPACKET_H
+#define PRINTPACKET_H
+
+#include "stralloc.h"
+
+extern unsigned int printpacket_cat(stralloc *,char *,unsigned int);
+
+#endif
diff --git a/printrecord.c b/printrecord.c
new file mode 100644
index 0000000..ed0b42d
--- /dev/null
+++ b/printrecord.c
@@ -0,0 +1,115 @@
+#include "uint16.h"
+#include "uint32.h"
+#include "error.h"
+#include "byte.h"
+#include "dns.h"
+#include "printrecord.h"
+
+static char *d;
+
+unsigned int printrecord_cat(stralloc *out,const char *buf,unsigned int len,unsigned int pos,const char *q,const char qtype[2])
+{
+ const char *x;
+ char misc[20];
+ uint16 datalen;
+ uint16 u16;
+ uint32 u32;
+ unsigned int newpos;
+ int i;
+ unsigned char ch;
+
+ pos = dns_packet_getname(buf,len,pos,&d); if (!pos) return 0;
+ pos = dns_packet_copy(buf,len,pos,misc,10); if (!pos) return 0;
+ uint16_unpack_big(misc + 8,&datalen);
+ newpos = pos + datalen;
+
+ if (q) {
+ if (!dns_domain_equal(d,q))
+ return newpos;
+ if (byte_diff(qtype,2,misc) && byte_diff(qtype,2,DNS_T_ANY))
+ return newpos;
+ }
+
+ if (!dns_domain_todot_cat(out,d)) return 0;
+ if (!stralloc_cats(out," ")) return 0;
+ uint32_unpack_big(misc + 4,&u32);
+ if (!stralloc_catulong0(out,u32,0)) return 0;
+
+ if (byte_diff(misc + 2,2,DNS_C_IN)) {
+ if (!stralloc_cats(out," weird class\n")) return 0;
+ return newpos;
+ }
+
+ x = 0;
+ if (byte_equal(misc,2,DNS_T_NS)) x = " NS ";
+ if (byte_equal(misc,2,DNS_T_PTR)) x = " PTR ";
+ if (byte_equal(misc,2,DNS_T_CNAME)) x = " CNAME ";
+ if (x) {
+ pos = dns_packet_getname(buf,len,pos,&d); if (!pos) return 0;
+ if (!stralloc_cats(out,x)) return 0;
+ if (!dns_domain_todot_cat(out,d)) return 0;
+ }
+ else if (byte_equal(misc,2,DNS_T_MX)) {
+ if (!stralloc_cats(out," MX ")) return 0;
+ pos = dns_packet_copy(buf,len,pos,misc,2); if (!pos) return 0;
+ pos = dns_packet_getname(buf,len,pos,&d); if (!pos) return 0;
+ uint16_unpack_big(misc,&u16);
+ if (!stralloc_catulong0(out,u16,0)) return 0;
+ if (!stralloc_cats(out," ")) return 0;
+ if (!dns_domain_todot_cat(out,d)) return 0;
+ }
+ else if (byte_equal(misc,2,DNS_T_SOA)) {
+ if (!stralloc_cats(out," SOA ")) return 0;
+ pos = dns_packet_getname(buf,len,pos,&d); if (!pos) return 0;
+ if (!dns_domain_todot_cat(out,d)) return 0;
+ if (!stralloc_cats(out," ")) return 0;
+ pos = dns_packet_getname(buf,len,pos,&d); if (!pos) return 0;
+ if (!dns_domain_todot_cat(out,d)) return 0;
+ pos = dns_packet_copy(buf,len,pos,misc,20); if (!pos) return 0;
+ for (i = 0;i < 5;++i) {
+ if (!stralloc_cats(out," ")) return 0;
+ uint32_unpack_big(misc + 4 * i,&u32);
+ if (!stralloc_catulong0(out,u32,0)) return 0;
+ }
+ }
+ else if (byte_equal(misc,2,DNS_T_A)) {
+ if (datalen != 4) { errno = error_proto; return 0; }
+ if (!stralloc_cats(out," A ")) return 0;
+ pos = dns_packet_copy(buf,len,pos,misc,4); if (!pos) return 0;
+ for (i = 0;i < 4;++i) {
+ ch = misc[i];
+ if (i) if (!stralloc_cats(out,".")) return 0;
+ if (!stralloc_catulong0(out,ch,0)) return 0;
+ }
+ }
+ else {
+ if (!stralloc_cats(out," ")) return 0;
+ uint16_unpack_big(misc,&u16);
+ if (!stralloc_catulong0(out,u16,0)) return 0;
+ if (!stralloc_cats(out," ")) return 0;
+ while (datalen--) {
+ pos = dns_packet_copy(buf,len,pos,misc,1); if (!pos) return 0;
+ if ((misc[0] >= 33) && (misc[0] <= 126) && (misc[0] != '\\')) {
+ if (!stralloc_catb(out,misc,1)) return 0;
+ }
+ else {
+ ch = misc[0];
+ misc[3] = '0' + (7 & ch); ch >>= 3;
+ misc[2] = '0' + (7 & ch); ch >>= 3;
+ misc[1] = '0' + (7 & ch);
+ misc[0] = '\\';
+ if (!stralloc_catb(out,misc,4)) return 0;
+ }
+ }
+ }
+
+ if (!stralloc_cats(out,"\n")) return 0;
+ if (pos != newpos) { errno = error_proto; return 0; }
+ return newpos;
+}
+
+unsigned int printrecord(stralloc *out,const char *buf,unsigned int len,unsigned int pos,const char *q,const char qtype[2])
+{
+ if (!stralloc_copys(out,"")) return 0;
+ return printrecord_cat(out,buf,len,pos,q,qtype);
+}
diff --git a/printrecord.h b/printrecord.h
new file mode 100644
index 0000000..f6bc9f7
--- /dev/null
+++ b/printrecord.h
@@ -0,0 +1,9 @@
+#ifndef PRINTRECORD_H
+#define PRINTRECORD_H
+
+#include "stralloc.h"
+
+extern unsigned int printrecord_cat(stralloc *,const char *,unsigned int,unsigned int,const char *,const char *);
+extern unsigned int printrecord(stralloc *,const char *,unsigned int,unsigned int,const char *,const char *);
+
+#endif
diff --git a/prot.c b/prot.c
new file mode 100644
index 0000000..0a8a373
--- /dev/null
+++ b/prot.c
@@ -0,0 +1,19 @@
+#include "hasshsgr.h"
+#include "prot.h"
+
+int prot_gid(int gid)
+{
+#ifdef HASSHORTSETGROUPS
+ short x[2];
+ x[0] = gid; x[1] = 73; /* catch errors */
+ if (setgroups(1,x) == -1) return -1;
+#else
+ if (setgroups(1,&gid) == -1) return -1;
+#endif
+ return setgid(gid); /* _should_ be redundant, but on some systems it isn't */
+}
+
+int prot_uid(int uid)
+{
+ return setuid(uid);
+}
diff --git a/prot.h b/prot.h
new file mode 100644
index 0000000..7dd0503
--- /dev/null
+++ b/prot.h
@@ -0,0 +1,7 @@
+#ifndef PROT_H
+#define PROT_H
+
+extern int prot_gid(int);
+extern int prot_uid(int);
+
+#endif
diff --git a/qlog.c b/qlog.c
new file mode 100644
index 0000000..5c5c7ba
--- /dev/null
+++ b/qlog.c
@@ -0,0 +1,63 @@
+#include "buffer.h"
+#include "qlog.h"
+
+static void put(char c)
+{
+ buffer_put(buffer_2,&c,1);
+}
+
+static void hex(unsigned char c)
+{
+ put("0123456789abcdef"[(c >> 4) & 15]);
+ put("0123456789abcdef"[c & 15]);
+}
+
+static void octal(unsigned char c)
+{
+ put('\\');
+ put('0' + ((c >> 6) & 7));
+ put('0' + ((c >> 3) & 7));
+ put('0' + (c & 7));
+}
+
+void qlog(const char ip[4],uint16 port,const char id[2],const char *q,const char qtype[2],const char *result)
+{
+ char ch;
+ char ch2;
+
+ hex(ip[0]);
+ hex(ip[1]);
+ hex(ip[2]);
+ hex(ip[3]);
+ put(':');
+ hex(port >> 8);
+ hex(port & 255);
+ put(':');
+ hex(id[0]);
+ hex(id[1]);
+ buffer_puts(buffer_2,result);
+ hex(qtype[0]);
+ hex(qtype[1]);
+ put(' ');
+
+ if (!*q)
+ put('.');
+ else
+ for (;;) {
+ ch = *q++;
+ while (ch--) {
+ ch2 = *q++;
+ if ((ch2 >= 'A') && (ch2 <= 'Z'))
+ ch2 += 32;
+ if (((ch2 >= 'a') && (ch2 <= 'z')) || ((ch2 >= '0') && (ch2 <= '9')) || (ch2 == '-') || (ch2 == '_'))
+ put(ch2);
+ else
+ octal(ch2);
+ }
+ if (!*q) break;
+ put('.');
+ }
+
+ put('\n');
+ buffer_flush(buffer_2);
+}
diff --git a/qlog.h b/qlog.h
new file mode 100644
index 0000000..a1eb206
--- /dev/null
+++ b/qlog.h
@@ -0,0 +1,8 @@
+#ifndef QLOG_H
+#define QLOG_H
+
+#include "uint16.h"
+
+extern void qlog(const char *,uint16,const char *,const char *,const char *,const char *);
+
+#endif
diff --git a/query.c b/query.c
new file mode 100644
index 0000000..46cdc00
--- /dev/null
+++ b/query.c
@@ -0,0 +1,851 @@
+#include "error.h"
+#include "roots.h"
+#include "log.h"
+#include "case.h"
+#include "cache.h"
+#include "byte.h"
+#include "dns.h"
+#include "uint64.h"
+#include "uint32.h"
+#include "uint16.h"
+#include "dd.h"
+#include "alloc.h"
+#include "response.h"
+#include "query.h"
+
+static int flagforwardonly = 0;
+
+void query_forwardonly(void)
+{
+ flagforwardonly = 1;
+}
+
+static void cachegeneric(const char type[2],const char *d,const char *data,unsigned int datalen,uint32 ttl)
+{
+ unsigned int len;
+ char key[257];
+
+ len = dns_domain_length(d);
+ if (len > 255) return;
+
+ byte_copy(key,2,type);
+ byte_copy(key + 2,len,d);
+ case_lowerb(key + 2,len);
+
+ cache_set(key,len + 2,data,datalen,ttl);
+}
+
+static char save_buf[8192];
+static unsigned int save_len;
+static unsigned int save_ok;
+
+static void save_start(void)
+{
+ save_len = 0;
+ save_ok = 1;
+}
+
+static void save_data(const char *buf,unsigned int len)
+{
+ if (!save_ok) return;
+ if (len > (sizeof save_buf) - save_len) { save_ok = 0; return; }
+ byte_copy(save_buf + save_len,len,buf);
+ save_len += len;
+}
+
+static void save_finish(const char type[2],const char *d,uint32 ttl)
+{
+ if (!save_ok) return;
+ cachegeneric(type,d,save_buf,save_len,ttl);
+}
+
+
+static int typematch(const char rtype[2],const char qtype[2])
+{
+ return byte_equal(qtype,2,rtype) || byte_equal(qtype,2,DNS_T_ANY);
+}
+
+static uint32 ttlget(char buf[4])
+{
+ uint32 ttl;
+
+ uint32_unpack_big(buf,&ttl);
+ if (ttl > 1000000000) return 0;
+ if (ttl > 604800) return 604800;
+ return ttl;
+}
+
+
+static void cleanup(struct query *z)
+{
+ int j;
+ int k;
+
+ dns_transmit_free(&z->dt);
+ for (j = 0;j < QUERY_MAXALIAS;++j)
+ dns_domain_free(&z->alias[j]);
+ for (j = 0;j < QUERY_MAXLEVEL;++j) {
+ dns_domain_free(&z->name[j]);
+ for (k = 0;k < QUERY_MAXNS;++k)
+ dns_domain_free(&z->ns[j][k]);
+ }
+}
+
+static int rqa(struct query *z)
+{
+ int i;
+
+ for (i = QUERY_MAXALIAS - 1;i >= 0;--i)
+ if (z->alias[i]) {
+ if (!response_query(z->alias[i],z->type,z->class)) return 0;
+ while (i > 0) {
+ if (!response_cname(z->alias[i],z->alias[i - 1],z->aliasttl[i])) return 0;
+ --i;
+ }
+ if (!response_cname(z->alias[0],z->name[0],z->aliasttl[0])) return 0;
+ return 1;
+ }
+
+ if (!response_query(z->name[0],z->type,z->class)) return 0;
+ return 1;
+}
+
+static int globalip(char *d,char ip[4])
+{
+ if (dns_domain_equal(d,"\011localhost\0")) {
+ byte_copy(ip,4,"\177\0\0\1");
+ return 1;
+ }
+ if (dd(d,"",ip) == 4) return 1;
+ return 0;
+}
+
+static char *t1 = 0;
+static char *t2 = 0;
+static char *t3 = 0;
+static char *cname = 0;
+static char *referral = 0;
+static unsigned int *records = 0;
+
+static int smaller(char *buf,unsigned int len,unsigned int pos1,unsigned int pos2)
+{
+ char header1[12];
+ char header2[12];
+ int r;
+ unsigned int len1;
+ unsigned int len2;
+
+ pos1 = dns_packet_getname(buf,len,pos1,&t1);
+ dns_packet_copy(buf,len,pos1,header1,10);
+ pos2 = dns_packet_getname(buf,len,pos2,&t2);
+ dns_packet_copy(buf,len,pos2,header2,10);
+
+ r = byte_diff(header1,4,header2);
+ if (r < 0) return 1;
+ if (r > 0) return 0;
+
+ len1 = dns_domain_length(t1);
+ len2 = dns_domain_length(t2);
+ if (len1 < len2) return 1;
+ if (len1 > len2) return 0;
+
+ r = case_diffb(t1,len1,t2);
+ if (r < 0) return 1;
+ if (r > 0) return 0;
+
+ if (pos1 < pos2) return 1;
+ return 0;
+}
+
+static int doit(struct query *z,int state)
+{
+ char key[257];
+ char *cached;
+ unsigned int cachedlen;
+ char *buf;
+ unsigned int len;
+ const char *whichserver;
+ char header[12];
+ char misc[20];
+ unsigned int rcode;
+ unsigned int posanswers;
+ uint16 numanswers;
+ unsigned int posauthority;
+ uint16 numauthority;
+ unsigned int posglue;
+ uint16 numglue;
+ unsigned int pos;
+ unsigned int pos2;
+ uint16 datalen;
+ char *control;
+ char *d;
+ const char *dtype;
+ unsigned int dlen;
+ int flagout;
+ int flagcname;
+ int flagreferral;
+ int flagsoa;
+ uint32 ttl;
+ uint32 soattl;
+ uint32 cnamettl;
+ int i;
+ int j;
+ int k;
+ int p;
+ int q;
+
+ errno = error_io;
+ if (state == 1) goto HAVEPACKET;
+ if (state == -1) {
+ log_servfail(z->name[z->level]);
+ goto SERVFAIL;
+ }
+
+
+ NEWNAME:
+ if (++z->loop == 100) goto DIE;
+ d = z->name[z->level];
+ dtype = z->level ? DNS_T_A : z->type;
+ dlen = dns_domain_length(d);
+
+ if (globalip(d,misc)) {
+ if (z->level) {
+ for (k = 0;k < 64;k += 4)
+ if (byte_equal(z->servers[z->level - 1] + k,4,"\0\0\0\0")) {
+ byte_copy(z->servers[z->level - 1] + k,4,misc);
+ break;
+ }
+ goto LOWERLEVEL;
+ }
+ if (!rqa(z)) goto DIE;
+ if (typematch(DNS_T_A,dtype)) {
+ if (!response_rstart(d,DNS_T_A,655360)) goto DIE;
+ if (!response_addbytes(misc,4)) goto DIE;
+ response_rfinish(RESPONSE_ANSWER);
+ }
+ cleanup(z);
+ return 1;
+ }
+
+ if (dns_domain_equal(d,"\0011\0010\0010\003127\7in-addr\4arpa\0")) {
+ if (z->level) goto LOWERLEVEL;
+ if (!rqa(z)) goto DIE;
+ if (typematch(DNS_T_PTR,dtype)) {
+ if (!response_rstart(d,DNS_T_PTR,655360)) goto DIE;
+ if (!response_addname("\011localhost\0")) goto DIE;
+ response_rfinish(RESPONSE_ANSWER);
+ }
+ cleanup(z);
+ log_stats();
+ return 1;
+ }
+
+ if (dlen <= 255) {
+ byte_copy(key,2,DNS_T_ANY);
+ byte_copy(key + 2,dlen,d);
+ case_lowerb(key + 2,dlen);
+ cached = cache_get(key,dlen + 2,&cachedlen,&ttl);
+ if (cached) {
+ log_cachednxdomain(d);
+ goto NXDOMAIN;
+ }
+
+ byte_copy(key,2,DNS_T_CNAME);
+ cached = cache_get(key,dlen + 2,&cachedlen,&ttl);
+ if (cached) {
+ if (typematch(DNS_T_CNAME,dtype)) {
+ log_cachedanswer(d,DNS_T_CNAME);
+ if (!rqa(z)) goto DIE;
+ if (!response_cname(z->name[0],cached,ttl)) goto DIE;
+ cleanup(z);
+ return 1;
+ }
+ log_cachedcname(d,cached);
+ if (!dns_domain_copy(&cname,cached)) goto DIE;
+ goto CNAME;
+ }
+
+ if (typematch(DNS_T_NS,dtype)) {
+ byte_copy(key,2,DNS_T_NS);
+ cached = cache_get(key,dlen + 2,&cachedlen,&ttl);
+ if (cached && (cachedlen || byte_diff(dtype,2,DNS_T_ANY))) {
+ log_cachedanswer(d,DNS_T_NS);
+ if (!rqa(z)) goto DIE;
+ pos = 0;
+ while (pos = dns_packet_getname(cached,cachedlen,pos,&t2)) {
+ if (!response_rstart(d,DNS_T_NS,ttl)) goto DIE;
+ if (!response_addname(t2)) goto DIE;
+ response_rfinish(RESPONSE_ANSWER);
+ }
+ cleanup(z);
+ return 1;
+ }
+ }
+
+ if (typematch(DNS_T_PTR,dtype)) {
+ byte_copy(key,2,DNS_T_PTR);
+ cached = cache_get(key,dlen + 2,&cachedlen,&ttl);
+ if (cached && (cachedlen || byte_diff(dtype,2,DNS_T_ANY))) {
+ log_cachedanswer(d,DNS_T_PTR);
+ if (!rqa(z)) goto DIE;
+ pos = 0;
+ while (pos = dns_packet_getname(cached,cachedlen,pos,&t2)) {
+ if (!response_rstart(d,DNS_T_PTR,ttl)) goto DIE;
+ if (!response_addname(t2)) goto DIE;
+ response_rfinish(RESPONSE_ANSWER);
+ }
+ cleanup(z);
+ return 1;
+ }
+ }
+
+ if (typematch(DNS_T_MX,dtype)) {
+ byte_copy(key,2,DNS_T_MX);
+ cached = cache_get(key,dlen + 2,&cachedlen,&ttl);
+ if (cached && (cachedlen || byte_diff(dtype,2,DNS_T_ANY))) {
+ log_cachedanswer(d,DNS_T_MX);
+ if (!rqa(z)) goto DIE;
+ pos = 0;
+ while (pos = dns_packet_copy(cached,cachedlen,pos,misc,2)) {
+ pos = dns_packet_getname(cached,cachedlen,pos,&t2);
+ if (!pos) break;
+ if (!response_rstart(d,DNS_T_MX,ttl)) goto DIE;
+ if (!response_addbytes(misc,2)) goto DIE;
+ if (!response_addname(t2)) goto DIE;
+ response_rfinish(RESPONSE_ANSWER);
+ }
+ cleanup(z);
+ return 1;
+ }
+ }
+
+ if (typematch(DNS_T_A,dtype)) {
+ byte_copy(key,2,DNS_T_A);
+ cached = cache_get(key,dlen + 2,&cachedlen,&ttl);
+ if (cached && (cachedlen || byte_diff(dtype,2,DNS_T_ANY))) {
+ if (z->level) {
+ log_cachedanswer(d,DNS_T_A);
+ while (cachedlen >= 4) {
+ for (k = 0;k < 64;k += 4)
+ if (byte_equal(z->servers[z->level - 1] + k,4,"\0\0\0\0")) {
+ byte_copy(z->servers[z->level - 1] + k,4,cached);
+ break;
+ }
+ cached += 4;
+ cachedlen -= 4;
+ }
+ goto LOWERLEVEL;
+ }
+
+ log_cachedanswer(d,DNS_T_A);
+ if (!rqa(z)) goto DIE;
+ while (cachedlen >= 4) {
+ if (!response_rstart(d,DNS_T_A,ttl)) goto DIE;
+ if (!response_addbytes(cached,4)) goto DIE;
+ response_rfinish(RESPONSE_ANSWER);
+ cached += 4;
+ cachedlen -= 4;
+ }
+ cleanup(z);
+ return 1;
+ }
+ }
+
+ if (!typematch(DNS_T_ANY,dtype) && !typematch(DNS_T_AXFR,dtype) && !typematch(DNS_T_CNAME,dtype) && !typematch(DNS_T_NS,dtype) && !typematch(DNS_T_PTR,dtype) && !typematch(DNS_T_A,dtype) && !typematch(DNS_T_MX,dtype)) {
+ byte_copy(key,2,dtype);
+ cached = cache_get(key,dlen + 2,&cachedlen,&ttl);
+ if (cached && (cachedlen || byte_diff(dtype,2,DNS_T_ANY))) {
+ log_cachedanswer(d,dtype);
+ if (!rqa(z)) goto DIE;
+ while (cachedlen >= 2) {
+ uint16_unpack_big(cached,&datalen);
+ cached += 2;
+ cachedlen -= 2;
+ if (datalen > cachedlen) goto DIE;
+ if (!response_rstart(d,dtype,ttl)) goto DIE;
+ if (!response_addbytes(cached,datalen)) goto DIE;
+ response_rfinish(RESPONSE_ANSWER);
+ cached += datalen;
+ cachedlen -= datalen;
+ }
+ cleanup(z);
+ return 1;
+ }
+ }
+ }
+
+ for (;;) {
+ if (roots(z->servers[z->level],d)) {
+ for (j = 0;j < QUERY_MAXNS;++j)
+ dns_domain_free(&z->ns[z->level][j]);
+ z->control[z->level] = d;
+ break;
+ }
+
+ if (!flagforwardonly && (z->level < 2))
+ if (dlen < 255) {
+ byte_copy(key,2,DNS_T_NS);
+ byte_copy(key + 2,dlen,d);
+ case_lowerb(key + 2,dlen);
+ cached = cache_get(key,dlen + 2,&cachedlen,&ttl);
+ if (cached && cachedlen) {
+ z->control[z->level] = d;
+ byte_zero(z->servers[z->level],64);
+ for (j = 0;j < QUERY_MAXNS;++j)
+ dns_domain_free(&z->ns[z->level][j]);
+ pos = 0;
+ j = 0;
+ while (pos = dns_packet_getname(cached,cachedlen,pos,&t1)) {
+ log_cachedns(d,t1);
+ if (j < QUERY_MAXNS)
+ if (!dns_domain_copy(&z->ns[z->level][j++],t1)) goto DIE;
+ }
+ break;
+ }
+ }
+
+ if (!*d) goto DIE;
+ j = 1 + (unsigned int) (unsigned char) *d;
+ dlen -= j;
+ d += j;
+ }
+
+
+ HAVENS:
+ for (j = 0;j < QUERY_MAXNS;++j)
+ if (z->ns[z->level][j]) {
+ if (z->level + 1 < QUERY_MAXLEVEL) {
+ if (!dns_domain_copy(&z->name[z->level + 1],z->ns[z->level][j])) goto DIE;
+ dns_domain_free(&z->ns[z->level][j]);
+ ++z->level;
+ goto NEWNAME;
+ }
+ dns_domain_free(&z->ns[z->level][j]);
+ }
+
+ for (j = 0;j < 64;j += 4)
+ if (byte_diff(z->servers[z->level] + j,4,"\0\0\0\0"))
+ break;
+ if (j == 64) goto SERVFAIL;
+
+ dns_sortip(z->servers[z->level],64);
+ if (z->level) {
+ log_tx(z->name[z->level],DNS_T_A,z->control[z->level],z->servers[z->level],z->level);
+ if (dns_transmit_start(&z->dt,z->servers[z->level],flagforwardonly,z->name[z->level],DNS_T_A,z->localip) == -1) goto DIE;
+ }
+ else {
+ log_tx(z->name[0],z->type,z->control[0],z->servers[0],0);
+ if (dns_transmit_start(&z->dt,z->servers[0],flagforwardonly,z->name[0],z->type,z->localip) == -1) goto DIE;
+ }
+ return 0;
+
+
+ LOWERLEVEL:
+ dns_domain_free(&z->name[z->level]);
+ for (j = 0;j < QUERY_MAXNS;++j)
+ dns_domain_free(&z->ns[z->level][j]);
+ --z->level;
+ goto HAVENS;
+
+
+ HAVEPACKET:
+ if (++z->loop == 100) goto DIE;
+ buf = z->dt.packet;
+ len = z->dt.packetlen;
+
+ whichserver = z->dt.servers + 4 * z->dt.curserver;
+ control = z->control[z->level];
+ d = z->name[z->level];
+ dtype = z->level ? DNS_T_A : z->type;
+
+ pos = dns_packet_copy(buf,len,0,header,12); if (!pos) goto DIE;
+ pos = dns_packet_skipname(buf,len,pos); if (!pos) goto DIE;
+ pos += 4;
+ posanswers = pos;
+
+ uint16_unpack_big(header + 6,&numanswers);
+ uint16_unpack_big(header + 8,&numauthority);
+ uint16_unpack_big(header + 10,&numglue);
+
+ rcode = header[3] & 15;
+ if (rcode && (rcode != 3)) goto DIE; /* impossible; see irrelevant() */
+
+ flagout = 0;
+ flagcname = 0;
+ flagreferral = 0;
+ flagsoa = 0;
+ soattl = 0;
+ cnamettl = 0;
+ for (j = 0;j < numanswers;++j) {
+ pos = dns_packet_getname(buf,len,pos,&t1); if (!pos) goto DIE;
+ pos = dns_packet_copy(buf,len,pos,header,10); if (!pos) goto DIE;
+
+ if (dns_domain_equal(t1,d))
+ if (byte_equal(header + 2,2,DNS_C_IN)) { /* should always be true */
+ if (typematch(header,dtype))
+ flagout = 1;
+ else if (typematch(header,DNS_T_CNAME)) {
+ if (!dns_packet_getname(buf,len,pos,&cname)) goto DIE;
+ flagcname = 1;
+ cnamettl = ttlget(header + 4);
+ }
+ }
+
+ uint16_unpack_big(header + 8,&datalen);
+ pos += datalen;
+ }
+ posauthority = pos;
+
+ for (j = 0;j < numauthority;++j) {
+ pos = dns_packet_getname(buf,len,pos,&t1); if (!pos) goto DIE;
+ pos = dns_packet_copy(buf,len,pos,header,10); if (!pos) goto DIE;
+
+ if (typematch(header,DNS_T_SOA)) {
+ flagsoa = 1;
+ soattl = ttlget(header + 4);
+ if (soattl > 3600) soattl = 3600;
+ }
+ else if (typematch(header,DNS_T_NS)) {
+ flagreferral = 1;
+ if (!dns_domain_copy(&referral,t1)) goto DIE;
+ }
+
+ uint16_unpack_big(header + 8,&datalen);
+ pos += datalen;
+ }
+ posglue = pos;
+
+
+ if (!flagcname && !rcode && !flagout && flagreferral && !flagsoa)
+ if (dns_domain_equal(referral,control) || !dns_domain_suffix(referral,control)) {
+ log_lame(whichserver,control,referral);
+ byte_zero(whichserver,4);
+ goto HAVENS;
+ }
+
+
+ if (records) { alloc_free(records); records = 0; }
+
+ k = numanswers + numauthority + numglue;
+ records = (unsigned int *) alloc(k * sizeof(unsigned int));
+ if (!records) goto DIE;
+
+ pos = posanswers;
+ for (j = 0;j < k;++j) {
+ records[j] = pos;
+ pos = dns_packet_getname(buf,len,pos,&t1); if (!pos) goto DIE;
+ pos = dns_packet_copy(buf,len,pos,header,10); if (!pos) goto DIE;
+ uint16_unpack_big(header + 8,&datalen);
+ pos += datalen;
+ }
+
+ i = j = k;
+ while (j > 1) {
+ if (i > 1) { --i; pos = records[i - 1]; }
+ else { pos = records[j - 1]; records[j - 1] = records[i - 1]; --j; }
+
+ q = i;
+ while ((p = q * 2) < j) {
+ if (!smaller(buf,len,records[p],records[p - 1])) ++p;
+ records[q - 1] = records[p - 1]; q = p;
+ }
+ if (p == j) {
+ records[q - 1] = records[p - 1]; q = p;
+ }
+ while ((q > i) && smaller(buf,len,records[(p = q/2) - 1],pos)) {
+ records[q - 1] = records[p - 1]; q = p;
+ }
+ records[q - 1] = pos;
+ }
+
+ i = 0;
+ while (i < k) {
+ char type[2];
+
+ pos = dns_packet_getname(buf,len,records[i],&t1); if (!pos) goto DIE;
+ pos = dns_packet_copy(buf,len,pos,header,10); if (!pos) goto DIE;
+ ttl = ttlget(header + 4);
+
+ byte_copy(type,2,header);
+ if (byte_diff(header + 2,2,DNS_C_IN)) { ++i; continue; }
+
+ for (j = i + 1;j < k;++j) {
+ pos = dns_packet_getname(buf,len,records[j],&t2); if (!pos) goto DIE;
+ pos = dns_packet_copy(buf,len,pos,header,10); if (!pos) goto DIE;
+ if (!dns_domain_equal(t1,t2)) break;
+ if (byte_diff(header,2,type)) break;
+ if (byte_diff(header + 2,2,DNS_C_IN)) break;
+ }
+
+ if (!dns_domain_suffix(t1,control)) { i = j; continue; }
+ if (!roots_same(t1,control)) { i = j; continue; }
+
+ if (byte_equal(type,2,DNS_T_ANY))
+ ;
+ else if (byte_equal(type,2,DNS_T_AXFR))
+ ;
+ else if (byte_equal(type,2,DNS_T_SOA)) {
+ while (i < j) {
+ pos = dns_packet_skipname(buf,len,records[i]); if (!pos) goto DIE;
+ pos = dns_packet_getname(buf,len,pos + 10,&t2); if (!pos) goto DIE;
+ pos = dns_packet_getname(buf,len,pos,&t3); if (!pos) goto DIE;
+ pos = dns_packet_copy(buf,len,pos,misc,20); if (!pos) goto DIE;
+ if (records[i] < posauthority)
+ log_rrsoa(whichserver,t1,t2,t3,misc,ttl);
+ ++i;
+ }
+ }
+ else if (byte_equal(type,2,DNS_T_CNAME)) {
+ pos = dns_packet_skipname(buf,len,records[j - 1]); if (!pos) goto DIE;
+ pos = dns_packet_getname(buf,len,pos + 10,&t2); if (!pos) goto DIE;
+ log_rrcname(whichserver,t1,t2,ttl);
+ cachegeneric(DNS_T_CNAME,t1,t2,dns_domain_length(t2),ttl);
+ }
+ else if (byte_equal(type,2,DNS_T_PTR)) {
+ save_start();
+ while (i < j) {
+ pos = dns_packet_skipname(buf,len,records[i]); if (!pos) goto DIE;
+ pos = dns_packet_getname(buf,len,pos + 10,&t2); if (!pos) goto DIE;
+ log_rrptr(whichserver,t1,t2,ttl);
+ save_data(t2,dns_domain_length(t2));
+ ++i;
+ }
+ save_finish(DNS_T_PTR,t1,ttl);
+ }
+ else if (byte_equal(type,2,DNS_T_NS)) {
+ save_start();
+ while (i < j) {
+ pos = dns_packet_skipname(buf,len,records[i]); if (!pos) goto DIE;
+ pos = dns_packet_getname(buf,len,pos + 10,&t2); if (!pos) goto DIE;
+ log_rrns(whichserver,t1,t2,ttl);
+ save_data(t2,dns_domain_length(t2));
+ ++i;
+ }
+ save_finish(DNS_T_NS,t1,ttl);
+ }
+ else if (byte_equal(type,2,DNS_T_MX)) {
+ save_start();
+ while (i < j) {
+ pos = dns_packet_skipname(buf,len,records[i]); if (!pos) goto DIE;
+ pos = dns_packet_copy(buf,len,pos + 10,misc,2); if (!pos) goto DIE;
+ pos = dns_packet_getname(buf,len,pos,&t2); if (!pos) goto DIE;
+ log_rrmx(whichserver,t1,t2,misc,ttl);
+ save_data(misc,2);
+ save_data(t2,dns_domain_length(t2));
+ ++i;
+ }
+ save_finish(DNS_T_MX,t1,ttl);
+ }
+ else if (byte_equal(type,2,DNS_T_A)) {
+ save_start();
+ while (i < j) {
+ pos = dns_packet_skipname(buf,len,records[i]); if (!pos) goto DIE;
+ pos = dns_packet_copy(buf,len,pos,header,10); if (!pos) goto DIE;
+ if (byte_equal(header + 8,2,"\0\4")) {
+ pos = dns_packet_copy(buf,len,pos,header,4); if (!pos) goto DIE;
+ save_data(header,4);
+ log_rr(whichserver,t1,DNS_T_A,header,4,ttl);
+ }
+ ++i;
+ }
+ save_finish(DNS_T_A,t1,ttl);
+ }
+ else {
+ save_start();
+ while (i < j) {
+ pos = dns_packet_skipname(buf,len,records[i]); if (!pos) goto DIE;
+ pos = dns_packet_copy(buf,len,pos,header,10); if (!pos) goto DIE;
+ uint16_unpack_big(header + 8,&datalen);
+ if (datalen > len - pos) goto DIE;
+ save_data(header + 8,2);
+ save_data(buf + pos,datalen);
+ log_rr(whichserver,t1,type,buf + pos,datalen,ttl);
+ ++i;
+ }
+ save_finish(type,t1,ttl);
+ }
+
+ i = j;
+ }
+
+ alloc_free(records); records = 0;
+
+
+ if (flagcname) {
+ ttl = cnamettl;
+ CNAME:
+ if (!z->level) {
+ if (z->alias[QUERY_MAXALIAS - 1]) goto DIE;
+ for (j = QUERY_MAXALIAS - 1;j > 0;--j)
+ z->alias[j] = z->alias[j - 1];
+ for (j = QUERY_MAXALIAS - 1;j > 0;--j)
+ z->aliasttl[j] = z->aliasttl[j - 1];
+ z->alias[0] = z->name[0];
+ z->aliasttl[0] = ttl;
+ z->name[0] = 0;
+ }
+ if (!dns_domain_copy(&z->name[z->level],cname)) goto DIE;
+ goto NEWNAME;
+ }
+
+ if (rcode == 3) {
+ log_nxdomain(whichserver,d,soattl);
+ cachegeneric(DNS_T_ANY,d,"",0,soattl);
+
+ NXDOMAIN:
+ if (z->level) goto LOWERLEVEL;
+ if (!rqa(z)) goto DIE;
+ response_nxdomain();
+ cleanup(z);
+ return 1;
+ }
+
+ if (!flagout && flagsoa)
+ if (byte_diff(DNS_T_ANY,2,dtype))
+ if (byte_diff(DNS_T_AXFR,2,dtype))
+ if (byte_diff(DNS_T_CNAME,2,dtype)) {
+ save_start();
+ save_finish(dtype,d,soattl);
+ log_nodata(whichserver,d,dtype,soattl);
+ }
+
+ log_stats();
+
+
+ if (flagout || flagsoa || !flagreferral) {
+ if (z->level) {
+ pos = posanswers;
+ for (j = 0;j < numanswers;++j) {
+ pos = dns_packet_getname(buf,len,pos,&t1); if (!pos) goto DIE;
+ pos = dns_packet_copy(buf,len,pos,header,10); if (!pos) goto DIE;
+ uint16_unpack_big(header + 8,&datalen);
+ if (dns_domain_equal(t1,d))
+ if (typematch(header,DNS_T_A))
+ if (byte_equal(header + 2,2,DNS_C_IN)) /* should always be true */
+ if (datalen == 4)
+ for (k = 0;k < 64;k += 4)
+ if (byte_equal(z->servers[z->level - 1] + k,4,"\0\0\0\0")) {
+ if (!dns_packet_copy(buf,len,pos,z->servers[z->level - 1] + k,4)) goto DIE;
+ break;
+ }
+ pos += datalen;
+ }
+ goto LOWERLEVEL;
+ }
+
+ if (!rqa(z)) goto DIE;
+
+ pos = posanswers;
+ for (j = 0;j < numanswers;++j) {
+ pos = dns_packet_getname(buf,len,pos,&t1); if (!pos) goto DIE;
+ pos = dns_packet_copy(buf,len,pos,header,10); if (!pos) goto DIE;
+ ttl = ttlget(header + 4);
+ uint16_unpack_big(header + 8,&datalen);
+ if (dns_domain_equal(t1,d))
+ if (byte_equal(header + 2,2,DNS_C_IN)) /* should always be true */
+ if (typematch(header,dtype)) {
+ if (!response_rstart(t1,header,ttl)) goto DIE;
+
+ if (typematch(header,DNS_T_NS) || typematch(header,DNS_T_CNAME) || typematch(header,DNS_T_PTR)) {
+ if (!dns_packet_getname(buf,len,pos,&t2)) goto DIE;
+ if (!response_addname(t2)) goto DIE;
+ }
+ else if (typematch(header,DNS_T_MX)) {
+ pos2 = dns_packet_copy(buf,len,pos,misc,2); if (!pos2) goto DIE;
+ if (!response_addbytes(misc,2)) goto DIE;
+ if (!dns_packet_getname(buf,len,pos2,&t2)) goto DIE;
+ if (!response_addname(t2)) goto DIE;
+ }
+ else if (typematch(header,DNS_T_SOA)) {
+ pos2 = dns_packet_getname(buf,len,pos,&t2); if (!pos2) goto DIE;
+ if (!response_addname(t2)) goto DIE;
+ pos2 = dns_packet_getname(buf,len,pos2,&t3); if (!pos2) goto DIE;
+ if (!response_addname(t3)) goto DIE;
+ pos2 = dns_packet_copy(buf,len,pos2,misc,20); if (!pos2) goto DIE;
+ if (!response_addbytes(misc,20)) goto DIE;
+ }
+ else {
+ if (pos + datalen > len) goto DIE;
+ if (!response_addbytes(buf + pos,datalen)) goto DIE;
+ }
+
+ response_rfinish(RESPONSE_ANSWER);
+ }
+
+ pos += datalen;
+ }
+
+ cleanup(z);
+ return 1;
+ }
+
+
+ if (!dns_domain_suffix(d,referral)) goto DIE;
+ control = d + dns_domain_suffixpos(d,referral);
+ z->control[z->level] = control;
+ byte_zero(z->servers[z->level],64);
+ for (j = 0;j < QUERY_MAXNS;++j)
+ dns_domain_free(&z->ns[z->level][j]);
+ k = 0;
+
+ pos = posauthority;
+ for (j = 0;j < numauthority;++j) {
+ pos = dns_packet_getname(buf,len,pos,&t1); if (!pos) goto DIE;
+ pos = dns_packet_copy(buf,len,pos,header,10); if (!pos) goto DIE;
+ uint16_unpack_big(header + 8,&datalen);
+ if (dns_domain_equal(referral,t1)) /* should always be true */
+ if (typematch(header,DNS_T_NS)) /* should always be true */
+ if (byte_equal(header + 2,2,DNS_C_IN)) /* should always be true */
+ if (k < QUERY_MAXNS)
+ if (!dns_packet_getname(buf,len,pos,&z->ns[z->level][k++])) goto DIE;
+ pos += datalen;
+ }
+
+ goto HAVENS;
+
+
+ SERVFAIL:
+ if (z->level) goto LOWERLEVEL;
+ if (!rqa(z)) goto DIE;
+ response_servfail();
+ cleanup(z);
+ return 1;
+
+
+ DIE:
+ cleanup(z);
+ if (records) { alloc_free(records); records = 0; }
+ return -1;
+}
+
+int query_start(struct query *z,char *dn,char type[2],char class[2],char localip[4])
+{
+ if (byte_equal(type,2,DNS_T_AXFR)) { errno = error_perm; return -1; }
+
+ cleanup(z);
+ z->level = 0;
+ z->loop = 0;
+
+ if (!dns_domain_copy(&z->name[0],dn)) return -1;
+ byte_copy(z->type,2,type);
+ byte_copy(z->class,2,class);
+ byte_copy(z->localip,4,localip);
+
+ return doit(z,0);
+}
+
+int query_get(struct query *z,iopause_fd *x,struct taia *stamp)
+{
+ switch(dns_transmit_get(&z->dt,x,stamp)) {
+ case 1:
+ return doit(z,1);
+ case -1:
+ return doit(z,-1);
+ }
+ return 0;
+}
+
+void query_io(struct query *z,iopause_fd *x,struct taia *deadline)
+{
+ dns_transmit_io(&z->dt,x,deadline);
+}
diff --git a/query.h b/query.h
new file mode 100644
index 0000000..eff68b2
--- /dev/null
+++ b/query.h
@@ -0,0 +1,32 @@
+#ifndef QUERY_H
+#define QUERY_H
+
+#include "dns.h"
+#include "uint32.h"
+
+#define QUERY_MAXLEVEL 5
+#define QUERY_MAXALIAS 16
+#define QUERY_MAXNS 16
+
+struct query {
+ unsigned int loop;
+ unsigned int level;
+ char *name[QUERY_MAXLEVEL];
+ char *control[QUERY_MAXLEVEL]; /* pointing inside name */
+ char *ns[QUERY_MAXLEVEL][QUERY_MAXNS];
+ char servers[QUERY_MAXLEVEL][64];
+ char *alias[QUERY_MAXALIAS];
+ uint32 aliasttl[QUERY_MAXALIAS];
+ char localip[4];
+ char type[2];
+ char class[2];
+ struct dns_transmit dt;
+} ;
+
+extern int query_start(struct query *,char *,char *,char *,char *);
+extern void query_io(struct query *,iopause_fd *,struct taia *);
+extern int query_get(struct query *,iopause_fd *,struct taia *);
+
+extern void query_forwardonly(void);
+
+#endif
diff --git a/random-ip.c b/random-ip.c
new file mode 100644
index 0000000..bfd516c
--- /dev/null
+++ b/random-ip.c
@@ -0,0 +1,80 @@
+#include "buffer.h"
+#include "exit.h"
+#include "fmt.h"
+#include "scan.h"
+#include "dns.h"
+
+char ip[4];
+int ipfixed = 0;
+unsigned long loops = 10000;
+unsigned char tab[256];
+
+char strnum[FMT_ULONG];
+
+char seed[128];
+
+int main(int argc,char **argv)
+{
+ unsigned long u;
+ int i;
+ int j;
+ unsigned char c;
+
+ dns_random_init(seed);
+
+ for (i = 0;i < 256;++i) tab[i] = i;
+ for (j = 256;j > 0;--j) {
+ i = dns_random(j);
+ c = tab[j - 1];
+ tab[j - 1] = tab[i];
+ tab[i] = c;
+ }
+
+ if (*argv) ++argv;
+ if (*argv) scan_ulong(*argv++,&loops);
+ if (*argv) { scan_ulong(*argv++,&u); ip[0] = u; ipfixed = 1; }
+ if (*argv) { scan_ulong(*argv++,&u); ip[1] = u; ipfixed = 2; }
+ if (*argv) { scan_ulong(*argv++,&u); ip[2] = u; ipfixed = 3; }
+ if (*argv) { scan_ulong(*argv++,&u); ip[3] = u; ipfixed = 4; }
+
+ if (ipfixed >= 1) if (loops > 16777216) loops = 16777216;
+ if (ipfixed >= 2) if (loops > 65536) loops = 65536;
+ if (ipfixed >= 3) if (loops > 256) loops = 256;
+ if (ipfixed >= 4) if (loops > 1) loops = 1;
+
+ while (loops) {
+ --loops;
+ u = loops;
+ for (i = ipfixed;i < 4;++i) { ip[i] = u & 255; u >>= 8; }
+ if (ipfixed == 3) {
+ c = ip[3];
+ ip[3] = tab[c];
+ }
+ else if (ipfixed < 3) {
+ c = 0;
+ for (j = 0;j < 100;++j) {
+ for (i = ipfixed;i < 4;++i) {
+ c ^= (unsigned char) ip[i];
+ c = tab[c];
+ ip[i] = c;
+ }
+ }
+ }
+
+ u = (unsigned char) ip[0];
+ buffer_put(buffer_1,strnum,fmt_ulong(strnum,u));
+ buffer_puts(buffer_1,".");
+ u = (unsigned char) ip[1];
+ buffer_put(buffer_1,strnum,fmt_ulong(strnum,u));
+ buffer_puts(buffer_1,".");
+ u = (unsigned char) ip[2];
+ buffer_put(buffer_1,strnum,fmt_ulong(strnum,u));
+ buffer_puts(buffer_1,".");
+ u = (unsigned char) ip[3];
+ buffer_put(buffer_1,strnum,fmt_ulong(strnum,u));
+ buffer_puts(buffer_1,"\n");
+ }
+
+ buffer_flush(buffer_1);
+ _exit(0);
+}
diff --git a/rbldns-conf.c b/rbldns-conf.c
new file mode 100644
index 0000000..79d446f
--- /dev/null
+++ b/rbldns-conf.c
@@ -0,0 +1,71 @@
+#include <unistd.h>
+#include <pwd.h>
+#include "strerr.h"
+#include "exit.h"
+#include "auto_home.h"
+#include "generic-conf.h"
+
+#define FATAL "rbldns-conf: fatal: "
+
+void usage(void)
+{
+ strerr_die1x(100,"rbldns-conf: usage: rbldns-conf acct logacct /rbldns myip base");
+}
+
+char *dir;
+char *user;
+char *loguser;
+struct passwd *pw;
+char *myip;
+char *base;
+
+int main(int argc,char **argv)
+{
+ user = argv[1];
+ if (!user) usage();
+ loguser = argv[2];
+ if (!loguser) usage();
+ dir = argv[3];
+ if (!dir) usage();
+ if (dir[0] != '/') usage();
+ myip = argv[4];
+ if (!myip) usage();
+ base = argv[5];
+ if (!base) usage();
+
+ pw = getpwnam(loguser);
+ if (!pw)
+ strerr_die3x(111,FATAL,"unknown account ",loguser);
+
+ init(dir,FATAL);
+ makelog(loguser,pw->pw_uid,pw->pw_gid);
+
+ makedir("env");
+ perm(02755);
+ start("env/ROOT"); outs(dir); outs("/root\n"); finish();
+ perm(0644);
+ start("env/IP"); outs(myip); outs("\n"); finish();
+ perm(0644);
+ start("env/BASE"); outs(base); outs("\n"); finish();
+ perm(0644);
+
+ start("run");
+ outs("#!/bin/sh\nexec 2>&1\nexec envuidgid "); outs(user);
+ outs(" envdir ./env softlimit -d250000 ");
+ outs(auto_home); outs("/bin/rbldns\n");
+ finish();
+ perm(0755);
+
+ makedir("root");
+ perm(02755);
+ start("root/data");
+ finish();
+ perm(0644);
+ start("root/Makefile");
+ outs("data.cdb: data\n");
+ outs("\t"); outs(auto_home); outs("/bin/rbldns-data\n");
+ finish();
+ perm(0644);
+
+ _exit(0);
+}
diff --git a/rbldns-data.c b/rbldns-data.c
new file mode 100644
index 0000000..ed495db
--- /dev/null
+++ b/rbldns-data.c
@@ -0,0 +1,128 @@
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include "buffer.h"
+#include "exit.h"
+#include "cdb_make.h"
+#include "open.h"
+#include "stralloc.h"
+#include "getln.h"
+#include "strerr.h"
+#include "byte.h"
+#include "scan.h"
+#include "fmt.h"
+#include "ip4.h"
+
+#define FATAL "rbldns-data: fatal: "
+
+void nomem(void)
+{
+ strerr_die2x(111,FATAL,"out of memory");
+}
+
+int fd;
+buffer b;
+char bspace[1024];
+
+int fdcdb;
+struct cdb_make cdb;
+static stralloc tmp;
+
+static stralloc line;
+int match = 1;
+unsigned long linenum = 0;
+
+char strnum[FMT_ULONG];
+
+void syntaxerror(const char *why)
+{
+ strnum[fmt_ulong(strnum,linenum)] = 0;
+ strerr_die4x(111,FATAL,"unable to parse data line ",strnum,why);
+}
+void die_datatmp(void)
+{
+ strerr_die2sys(111,FATAL,"unable to create data.tmp: ");
+}
+
+int main()
+{
+ char ip[4];
+ unsigned long u;
+ unsigned int j;
+ unsigned int k;
+ char ch;
+
+ umask(022);
+
+ fd = open_read("data");
+ if (fd == -1) strerr_die2sys(111,FATAL,"unable to open data: ");
+ buffer_init(&b,buffer_unixread,fd,bspace,sizeof bspace);
+
+ fdcdb = open_trunc("data.tmp");
+ if (fdcdb == -1) die_datatmp();
+ if (cdb_make_start(&cdb,fdcdb) == -1) die_datatmp();
+
+ while (match) {
+ ++linenum;
+ if (getln(&b,&line,&match,'\n') == -1)
+ strerr_die2sys(111,FATAL,"unable to read line: ");
+
+ while (line.len) {
+ ch = line.s[line.len - 1];
+ if ((ch != ' ') && (ch != '\t') && (ch != '\n')) break;
+ --line.len;
+ }
+ if (!line.len) continue;
+
+ switch(line.s[0]) {
+ default:
+ syntaxerror(": unrecognized leading character");
+ case '#':
+ break;
+ case ':':
+ j = byte_chr(line.s + 1,line.len - 1,':');
+ if (j >= line.len - 1) syntaxerror(": missing colon");
+ if (ip4_scan(line.s + 1,ip) != j) syntaxerror(": malformed IP address");
+ if (!stralloc_copyb(&tmp,ip,4)) nomem();
+ if (!stralloc_catb(&tmp,line.s + j + 2,line.len - j - 2)) nomem();
+ if (cdb_make_add(&cdb,"",0,tmp.s,tmp.len) == -1)
+ die_datatmp();
+ break;
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ if (!stralloc_0(&line)) nomem();
+ j = 0;
+ if (!stralloc_copys(&tmp,"")) nomem();
+ for (;;) {
+ k = scan_ulong(line.s + j,&u);
+ if (!k) break;
+ ch = u;
+ if (!stralloc_catb(&tmp,&ch,1)) nomem();
+ j += k;
+ if (line.s[j] != '.') break;
+ ++j;
+ }
+ if (!stralloc_catb(&tmp,"\0\0\0\0",4)) nomem();
+ tmp.len = 4;
+ if (line.s[j] == '/')
+ scan_ulong(line.s + j + 1,&u);
+ else
+ u = 32;
+ if (u > 32) u = 32;
+ ch = u;
+ if (!stralloc_catb(&tmp,&ch,1)) nomem();
+ if (cdb_make_add(&cdb,tmp.s,tmp.len,"",0) == -1)
+ die_datatmp();
+ break;
+ }
+ }
+
+ if (cdb_make_finish(&cdb) == -1) die_datatmp();
+ if (fsync(fdcdb) == -1) die_datatmp();
+ if (close(fdcdb) == -1) die_datatmp(); /* NFS stupidity */
+ if (rename("data.tmp","data.cdb") == -1)
+ strerr_die2sys(111,FATAL,"unable to move data.tmp to data.cdb: ");
+
+ _exit(0);
+}
diff --git a/rbldns.c b/rbldns.c
new file mode 100644
index 0000000..2c13c27
--- /dev/null
+++ b/rbldns.c
@@ -0,0 +1,116 @@
+#include <unistd.h>
+#include "str.h"
+#include "byte.h"
+#include "ip4.h"
+#include "open.h"
+#include "env.h"
+#include "cdb.h"
+#include "dns.h"
+#include "dd.h"
+#include "strerr.h"
+#include "response.h"
+
+static char *base;
+
+static struct cdb c;
+static char key[5];
+static char data[100 + IP4_FMT];
+
+static int doit(char *q,char qtype[2])
+{
+ int flaga;
+ int flagtxt;
+ char ch;
+ char reverseip[4];
+ char ip[4];
+ uint32 ipnum;
+ int r;
+ uint32 dlen;
+ int i;
+
+ flaga = byte_equal(qtype,2,DNS_T_A);
+ flagtxt = byte_equal(qtype,2,DNS_T_TXT);
+ if (byte_equal(qtype,2,DNS_T_ANY)) flaga = flagtxt = 1;
+ if (!flaga && !flagtxt) goto REFUSE;
+
+ if (dd(q,base,reverseip) != 4) goto REFUSE;
+ uint32_unpack(reverseip,&ipnum);
+ uint32_pack_big(ip,ipnum);
+
+ for (i = 0;i <= 24;++i) {
+ ipnum >>= i;
+ ipnum <<= i;
+ uint32_pack_big(key,ipnum);
+ key[4] = 32 - i;
+ r = cdb_find(&c,key,5);
+ if (r == -1) return 0;
+ if (r) break;
+ }
+ if (!r) { response_nxdomain(); return 1; }
+
+ r = cdb_find(&c,"",0);
+ if (r == -1) return 0;
+ if (r && ((dlen = cdb_datalen(&c)) >= 4)) {
+ if (dlen > 100) dlen = 100;
+ if (cdb_read(&c,data,dlen,cdb_datapos(&c)) == -1) return 0;
+ }
+ else {
+ dlen = 12;
+ byte_copy(data,dlen,"\177\0\0\2Listed $");
+ }
+
+ if ((dlen >= 5) && (data[dlen - 1] == '$')) {
+ --dlen;
+ dlen += ip4_fmt(data + dlen,ip);
+ }
+
+ if (flaga) {
+ if (!response_rstart(q,DNS_T_A,2048)) return 0;
+ if (!response_addbytes(data,4)) return 0;
+ response_rfinish(RESPONSE_ANSWER);
+ }
+ if (flagtxt) {
+ if (!response_rstart(q,DNS_T_TXT,2048)) return 0;
+ ch = dlen - 4;
+ if (!response_addbytes(&ch,1)) return 0;
+ if (!response_addbytes(data + 4,dlen - 4)) return 0;
+ response_rfinish(RESPONSE_ANSWER);
+ }
+
+ return 1;
+
+
+ REFUSE:
+ response[2] &= ~4;
+ response[3] &= ~15;
+ response[3] |= 5;
+ return 1;
+}
+
+int respond(char *q,char qtype[2],char ip[4])
+{
+ int fd;
+ int result;
+
+ fd = open_read("data.cdb");
+ if (fd == -1) return 0;
+ cdb_init(&c,fd);
+ result = doit(q,qtype);
+ cdb_free(&c);
+ close(fd);
+ return result;
+}
+
+const char *fatal = "rbldns: fatal: ";
+const char *starting = "starting rbldns\n";
+
+void initialize(void)
+{
+ char *x;
+
+ x = env_get("BASE");
+ if (!x)
+ strerr_die2x(111,fatal,"$BASE not set");
+ if (!dns_domain_fromdot(&base,x,str_len(x)))
+ strerr_die2x(111,fatal,"unable to parse $BASE");
+}
diff --git a/readclose.c b/readclose.c
new file mode 100644
index 0000000..b9368cf
--- /dev/null
+++ b/readclose.c
@@ -0,0 +1,21 @@
+#include <unistd.h>
+#include "error.h"
+#include "readclose.h"
+
+int readclose_append(int fd,stralloc *sa,unsigned int bufsize)
+{
+ int r;
+ for (;;) {
+ if (!stralloc_readyplus(sa,bufsize)) { close(fd); return -1; }
+ r = read(fd,sa->s + sa->len,bufsize);
+ if (r == -1) if (errno == error_intr) continue;
+ if (r <= 0) { close(fd); return r; }
+ sa->len += r;
+ }
+}
+
+int readclose(int fd,stralloc *sa,unsigned int bufsize)
+{
+ if (!stralloc_copys(sa,"")) { close(fd); return -1; }
+ return readclose_append(fd,sa,bufsize);
+}
diff --git a/readclose.h b/readclose.h
new file mode 100644
index 0000000..49afd6c
--- /dev/null
+++ b/readclose.h
@@ -0,0 +1,9 @@
+#ifndef READCLOSE_H
+#define READCLOSE_H
+
+#include "stralloc.h"
+
+extern int readclose_append(int,stralloc *,unsigned int);
+extern int readclose(int,stralloc *,unsigned int);
+
+#endif
diff --git a/response.c b/response.c
new file mode 100644
index 0000000..ba90c89
--- /dev/null
+++ b/response.c
@@ -0,0 +1,121 @@
+#include "dns.h"
+#include "byte.h"
+#include "uint16.h"
+#include "response.h"
+
+char response[65535];
+unsigned int response_len = 0; /* <= 65535 */
+static unsigned int tctarget;
+
+#define NAMES 100
+static char name[NAMES][128];
+static unsigned int name_ptr[NAMES]; /* each < 16384 */
+static unsigned int name_num;
+
+int response_addbytes(const char *buf,unsigned int len)
+{
+ if (len > 65535 - response_len) return 0;
+ byte_copy(response + response_len,len,buf);
+ response_len += len;
+ return 1;
+}
+
+int response_addname(const char *d)
+{
+ unsigned int dlen;
+ unsigned int i;
+ char buf[2];
+
+ dlen = dns_domain_length(d);
+
+ while (*d) {
+ for (i = 0;i < name_num;++i)
+ if (dns_domain_equal(d,name[i])) {
+ uint16_pack_big(buf,49152 + name_ptr[i]);
+ return response_addbytes(buf,2);
+ }
+ if (dlen <= 128)
+ if (name_num < NAMES) {
+ byte_copy(name[name_num],dlen,d);
+ name_ptr[name_num] = response_len;
+ ++name_num;
+ }
+ i = (unsigned char) *d;
+ ++i;
+ if (!response_addbytes(d,i)) return 0;
+ d += i;
+ dlen -= i;
+ }
+ return response_addbytes(d,1);
+}
+
+int response_query(const char *q,const char qtype[2],const char qclass[2])
+{
+ response_len = 0;
+ name_num = 0;
+ if (!response_addbytes("\0\0\201\200\0\1\0\0\0\0\0\0",12)) return 0;
+ if (!response_addname(q)) return 0;
+ if (!response_addbytes(qtype,2)) return 0;
+ if (!response_addbytes(qclass,2)) return 0;
+ tctarget = response_len;
+ return 1;
+}
+
+static unsigned int dpos;
+
+static int flaghidettl = 0;
+
+void response_hidettl(void)
+{
+ flaghidettl = 1;
+}
+
+int response_rstart(const char *d,const char type[2],uint32 ttl)
+{
+ char ttlstr[4];
+ if (!response_addname(d)) return 0;
+ if (!response_addbytes(type,2)) return 0;
+ if (!response_addbytes(DNS_C_IN,2)) return 0;
+ if (flaghidettl) ttl = 0;
+ uint32_pack_big(ttlstr,ttl);
+ if (!response_addbytes(ttlstr,4)) return 0;
+ if (!response_addbytes("\0\0",2)) return 0;
+ dpos = response_len;
+ return 1;
+}
+
+void response_rfinish(int x)
+{
+ uint16_pack_big(response + dpos - 2,response_len - dpos);
+ if (!++response[x + 1]) ++response[x];
+}
+
+int response_cname(const char *c,const char *d,uint32 ttl)
+{
+ if (!response_rstart(c,DNS_T_CNAME,ttl)) return 0;
+ if (!response_addname(d)) return 0;
+ response_rfinish(RESPONSE_ANSWER);
+ return 1;
+}
+
+void response_nxdomain(void)
+{
+ response[3] |= 3;
+ response[2] |= 4;
+}
+
+void response_servfail(void)
+{
+ response[3] |= 2;
+}
+
+void response_id(const char id[2])
+{
+ byte_copy(response,2,id);
+}
+
+void response_tc(void)
+{
+ response[2] |= 2;
+ response_len = tctarget;
+}
diff --git a/response.h b/response.h
new file mode 100644
index 0000000..206b1d4
--- /dev/null
+++ b/response.h
@@ -0,0 +1,27 @@
+#ifndef RESPONSE_H
+#define RESPONSE_H
+
+#include "uint32.h"
+
+extern char response[];
+extern unsigned int response_len;
+
+extern int response_query(const char *,const char *,const char *);
+extern void response_nxdomain(void);
+extern void response_servfail(void);
+extern void response_id(const char *);
+extern void response_tc(void);
+
+extern int response_addbytes(const char *,unsigned int);
+extern int response_addname(const char *);
+extern void response_hidettl(void);
+extern int response_rstart(const char *,const char *,uint32);
+extern void response_rfinish(int);
+
+#define RESPONSE_ANSWER 6
+#define RESPONSE_AUTHORITY 8
+#define RESPONSE_ADDITIONAL 10
+
+extern int response_cname(const char *,const char *,uint32);
+
+#endif
diff --git a/roots.c b/roots.c
new file mode 100644
index 0000000..3cfe959
--- /dev/null
+++ b/roots.c
@@ -0,0 +1,127 @@
+#include <unistd.h>
+#include "open.h"
+#include "error.h"
+#include "str.h"
+#include "byte.h"
+#include "error.h"
+#include "direntry.h"
+#include "ip4.h"
+#include "dns.h"
+#include "openreadclose.h"
+#include "roots.h"
+
+static stralloc data;
+
+static int roots_find(char *q)
+{
+ int i;
+ int j;
+
+ i = 0;
+ while (i < data.len) {
+ j = dns_domain_length(data.s + i);
+ if (dns_domain_equal(data.s + i,q)) return i + j;
+ i += j;
+ i += 64;
+ }
+ return -1;
+}
+
+static int roots_search(char *q)
+{
+ int r;
+
+ for (;;) {
+ r = roots_find(q);
+ if (r >= 0) return r;
+ if (!*q) return -1; /* user misconfiguration */
+ q += *q;
+ q += 1;
+ }
+}
+
+int roots(char servers[64],char *q)
+{
+ int r;
+ r = roots_find(q);
+ if (r == -1) return 0;
+ byte_copy(servers,64,data.s + r);
+ return 1;
+}
+
+int roots_same(char *q,char *q2)
+{
+ return roots_search(q) == roots_search(q2);
+}
+
+static int init2(DIR *dir)
+{
+ direntry *d;
+ const char *fqdn;
+ static char *q;
+ static stralloc text;
+ char servers[64];
+ int serverslen;
+ int i;
+ int j;
+
+ for (;;) {
+ errno = 0;
+ d = readdir(dir);
+ if (!d) {
+ if (errno) return 0;
+ return 1;
+ }
+
+ if (d->d_name[0] != '.') {
+ if (openreadclose(d->d_name,&text,32) != 1) return 0;
+ if (!stralloc_append(&text,"\n")) return 0;
+
+ fqdn = d->d_name;
+ if (str_equal(fqdn,"@")) fqdn = ".";
+ if (!dns_domain_fromdot(&q,fqdn,str_len(fqdn))) return 0;
+
+ serverslen = 0;
+ j = 0;
+ for (i = 0;i < text.len;++i)
+ if (text.s[i] == '\n') {
+ if (serverslen <= 60)
+ if (ip4_scan(text.s + j,servers + serverslen))
+ serverslen += 4;
+ j = i + 1;
+ }
+ byte_zero(servers + serverslen,64 - serverslen);
+
+ if (!stralloc_catb(&data,q,dns_domain_length(q))) return 0;
+ if (!stralloc_catb(&data,servers,64)) return 0;
+ }
+ }
+}
+
+static int init1(void)
+{
+ DIR *dir;
+ int r;
+
+ if (chdir("servers") == -1) return 0;
+ dir = opendir(".");
+ if (!dir) return 0;
+ r = init2(dir);
+ closedir(dir);
+ return r;
+}
+
+int roots_init(void)
+{
+ int fddir;
+ int r;
+
+ if (!stralloc_copys(&data,"")) return 0;
+
+ fddir = open_read(".");
+ if (fddir == -1) return 0;
+ r = init1();
+ if (fchdir(fddir) == -1) r = 0;
+ close(fddir);
+ return r;
+}
diff --git a/roots.h b/roots.h
new file mode 100644
index 0000000..5f89142
--- /dev/null
+++ b/roots.h
@@ -0,0 +1,8 @@
+#ifndef ROOTS_H
+#define ROOTS_H
+
+extern int roots(char *,char *);
+extern int roots_same(char *,char *);
+extern int roots_init(void);
+
+#endif
diff --git a/rts.exp b/rts.exp
new file mode 100644
index 0000000..fd40964
--- /dev/null
+++ b/rts.exp
@@ -0,0 +1,1072 @@
+--- dnscache-conf works
+--- tinydns-conf works
+--- pickdns-conf works
+--- walldns-conf works
+--- rbldns-conf works
+--- axfrdns-conf works
+--- cache handles simple example
+
+
+
+
+
+un
+
+
+
+
+un
+deux
+
+
+
+un
+deux
+trois
+
+
+un
+deux
+trois
+quatre
+
+un
+deux
+trois
+quatre
+cinq
+een
+deux
+trois
+quatre
+cinq
+een
+twee
+trois
+quatre
+cinq
+een
+twee
+drie
+quatre
+cinq
+een
+twee
+drie
+vier
+cinq
+een
+twee
+drie
+vier
+vijf
+0
+--- cache handles overwriting
+
+
+
+
+
+un
+
+
+
+
+een
+
+
+
+
+een
+deux
+
+
+
+een
+twee
+
+
+
+een
+twee
+trois
+
+
+een
+twee
+drie
+
+
+
+twee
+drie
+quatre
+
+
+twee
+drie
+vier
+
+
+
+drie
+vier
+cinq
+
+
+drie
+vier
+vijf
+0
+--- cache handles long chains
+1
+2
+3
+4
+5
+6
+7
+8
+9
+0
+--- dnsip finds IP address of network-surveys.cr.yp.to
+131.193.178.100
+0
+--- dnsip does not find nonexistent.cr.yp.to
+
+0
+--- dnsip rejects overly long domain names
+dnsip: fatal: unable to find IP address for x.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789: protocol error
+111
+--- dnsip handles IP address on input
+1.2.3.4
+127.0.0.1
+10.43.166.133
+10.43.166.133
+0
+--- dnsip allows 0 to be omitted
+127.0.0.1
+0
+--- dnsip handles multiple IP addresses on input
+1.2.3.4 5.6.7.8 9.10.11.12 13.14.15.16
+0
+--- dnsipq handles simple examples
+1.2.3.4 1.2.3.4
+localhost 127.0.0.1
+localhost 127.0.0.1
+5.6.7.8 5.6.7.8
+network-surveys.cr.yp.to 131.193.178.100
+nonexistent.whatever.cr.yp.to
+0
+--- dnsmx finds MX record for network-surveys.cr.yp.to
+0 a.mx.network-surveys.cr.yp.to
+0
+--- dnsmx manufactures MX record for nonexistent.cr.yp.to
+0 nonexistent.cr.yp.to
+0
+--- dnsmx rejects overly long domain names
+dnsmx: fatal: unable to find MX records for 0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789: protocol error
+111
+--- dnstxt finds TXT record for leap.yp.to
+8222222206660602022066620620.
+0
+--- dnstxt does not find nonexistent.cr.yp.to
+
+0
+--- dnstxt rejects overly long domain names
+dnstxt: fatal: unable to find TXT records for 0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789: protocol error
+111
+--- dnsname finds host name of 131.193.178.100
+network-surveys.cr.yp.to
+0
+--- dnsname does not find 127.5.6.7
+
+0
+--- dnsname rejects misformatted IP addresses
+dnsname: fatal: unable to parse IP address 1.2.3
+111
+--- dnsfilter finds some host names
+131.193.178.100+one=network-surveys.cr.yp.to two three
+127.5.6.7+one two three
+10+one two three
+0
+--- tinydns-data complains about unrecognized initial characters
+tinydns-data: fatal: unable to parse data line 3: unrecognized leading character
+111
+--- tinydns-data complains if it cannot create data.tmp
+tinydns-data: fatal: unable to create data.tmp: symbolic link loop
+111
+--- tinydns-data handles simple example
+0
+--- tinydns-data produces A records
+1 wormhole.movie.edu:
+117 bytes, 1+2+2+1 records, response, authoritative, noerror
+additional: a.ns.movie.edu 259200 A 192.249.249.3
+answer: wormhole.movie.edu 86400 A 192.249.249.1
+answer: wormhole.movie.edu 86400 A 192.253.253.1
+authority: movie.edu 259200 NS a.ns.movie.edu
+authority: movie.edu 259200 NS wormhole.movie.edu
+query: 1 wormhole.movie.edu
+0
+--- tinydns-data produces NS records
+2 movie.edu:
+117 bytes, 1+2+0+3 records, response, authoritative, noerror
+query: 2 movie.edu
+answer: movie.edu 259200 NS a.ns.movie.edu
+answer: movie.edu 259200 NS wormhole.movie.edu
+additional: a.ns.movie.edu 259200 A 192.249.249.3
+additional: wormhole.movie.edu 86400 A 192.249.249.1
+additional: wormhole.movie.edu 86400 A 192.253.253.1
+0
+--- tinydns-data produces SOA records
+6 movie.edu:
+164 bytes, 1+1+2+3 records, response, authoritative, noerror
+query: 6 movie.edu
+answer: movie.edu 2560 SOA a.ns.movie.edu hostmaster.movie.edu 987654321 16384 2048 1048576 2560
+authority: movie.edu 259200 NS a.ns.movie.edu
+authority: movie.edu 259200 NS wormhole.movie.edu
+additional: a.ns.movie.edu 259200 A 192.249.249.3
+additional: wormhole.movie.edu 86400 A 192.249.249.1
+additional: wormhole.movie.edu 86400 A 192.253.253.1
+0
+--- tinydns-data produces PTR records
+12 1.253.253.192.in-addr.arpa:
+175 bytes, 1+1+3+3 records, response, authoritative, noerror
+query: 12 1.253.253.192.in-addr.arpa
+answer: 1.253.253.192.in-addr.arpa 86400 PTR wormhole.movie.edu
+authority: 253.253.192.in-addr.arpa 259200 NS a.ns.253.253.192.in-addr.arpa
+authority: 253.253.192.in-addr.arpa 259200 NS b.ns.253.253.192.in-addr.arpa
+authority: 253.253.192.in-addr.arpa 259200 NS c.ns.253.253.192.in-addr.arpa
+additional: a.ns.253.253.192.in-addr.arpa 259200 A 192.249.249.3
+additional: b.ns.253.253.192.in-addr.arpa 259200 A 192.249.249.1
+additional: c.ns.253.253.192.in-addr.arpa 259200 A 192.253.253.1
+0
+--- tinydns-data produces MX records
+15 movie.edu:
+154 bytes, 1+1+2+4 records, response, authoritative, noerror
+query: 15 movie.edu
+answer: movie.edu 86400 MX 0 a.mx.movie.edu
+authority: movie.edu 259200 NS a.ns.movie.edu
+authority: movie.edu 259200 NS wormhole.movie.edu
+additional: a.mx.movie.edu 86400 A 192.249.249.1
+additional: a.ns.movie.edu 259200 A 192.249.249.3
+additional: wormhole.movie.edu 86400 A 192.249.249.1
+additional: wormhole.movie.edu 86400 A 192.253.253.1
+0
+--- tinydns-data produces TXT records
+16 movie.edu:
+146 bytes, 1+1+2+3 records, response, authoritative, noerror
+query: 16 movie.edu
+answer: movie.edu 86400 16 \020Movie\040University
+authority: movie.edu 259200 NS a.ns.movie.edu
+authority: movie.edu 259200 NS wormhole.movie.edu
+additional: a.ns.movie.edu 259200 A 192.249.249.3
+additional: wormhole.movie.edu 86400 A 192.249.249.1
+additional: wormhole.movie.edu 86400 A 192.253.253.1
+0
+--- tinydns-data produces AXFR responses
+252 movie.edu:
+27 bytes, 1+0+0+0 records, response, authoritative, notimp
+query: 252 movie.edu
+0
+--- tinydns-data produces ANY responses
+255 movie.edu:
+293 bytes, 1+9+0+4 records, response, authoritative, noerror
+query: 255 movie.edu
+answer: movie.edu 2560 SOA a.ns.movie.edu hostmaster.movie.edu 987654321 16384 2048 1048576 2560
+answer: movie.edu 259200 NS a.ns.movie.edu
+answer: movie.edu 259200 NS wormhole.movie.edu
+answer: movie.edu 86400 MX 0 a.mx.movie.edu
+answer: movie.edu 86400 16 \020Movie\040University
+answer: movie.edu 86400 12345 One
+answer: movie.edu 86400 12345 Two
+answer: movie.edu 86400 12346 Three
+answer: movie.edu 86400 12346 Four
+additional: a.ns.movie.edu 259200 A 192.249.249.3
+additional: wormhole.movie.edu 86400 A 192.249.249.1
+additional: wormhole.movie.edu 86400 A 192.253.253.1
+additional: a.mx.movie.edu 86400 A 192.249.249.1
+0
+--- tinydns-data produces records of any type
+12345 movie.edu:
+147 bytes, 1+2+2+3 records, response, authoritative, noerror
+query: 12345 movie.edu
+answer: movie.edu 86400 12345 One
+answer: movie.edu 86400 12345 Two
+authority: movie.edu 259200 NS a.ns.movie.edu
+authority: movie.edu 259200 NS wormhole.movie.edu
+additional: a.ns.movie.edu 259200 A 192.249.249.3
+additional: wormhole.movie.edu 86400 A 192.249.249.1
+additional: wormhole.movie.edu 86400 A 192.253.253.1
+0
+12346 movie.edu:
+150 bytes, 1+2+2+3 records, response, authoritative, noerror
+query: 12346 movie.edu
+answer: movie.edu 86400 12346 Three
+answer: movie.edu 86400 12346 Four
+authority: movie.edu 259200 NS a.ns.movie.edu
+authority: movie.edu 259200 NS wormhole.movie.edu
+additional: a.ns.movie.edu 259200 A 192.249.249.3
+additional: wormhole.movie.edu 86400 A 192.249.249.1
+additional: wormhole.movie.edu 86400 A 192.253.253.1
+0
+--- tinydns-data produces NODATA responses
+54321 movie.edu:
+79 bytes, 1+0+1+0 records, response, authoritative, noerror
+query: 54321 movie.edu
+authority: movie.edu 2560 SOA a.ns.movie.edu hostmaster.movie.edu 987654321 16384 2048 1048576 2560
+0
+--- tinydns-data produces NXDOMAIN responses
+1 this.does.not.exist.movie.edu:
+99 bytes, 1+0+1+0 records, response, authoritative, nxdomain
+query: 1 this.does.not.exist.movie.edu
+authority: movie.edu 2560 SOA a.ns.movie.edu hostmaster.movie.edu 987654321 16384 2048 1048576 2560
+0
+--- tinydns-data produces NXDOMAIN responses for suffixes
+1 ns.movie.edu:
+79 bytes, 1+0+1+0 records, response, authoritative, nxdomain
+query: 1 ns.movie.edu
+authority: movie.edu 2560 SOA a.ns.movie.edu hostmaster.movie.edu 987654321 16384 2048 1048576 2560
+0
+--- tinydns-data produces NXDOMAIN ANY responses for suffixes
+255 ns.movie.edu:
+79 bytes, 1+0+1+0 records, response, authoritative, nxdomain
+query: 255 ns.movie.edu
+authority: movie.edu 2560 SOA a.ns.movie.edu hostmaster.movie.edu 987654321 16384 2048 1048576 2560
+0
+--- tinydns-data does not produce responses outside its bailiwick
+1 edu:
+0
+--- tinydns-data does not include TXT in additional sections
+1 blah.movie.edu:
+62 bytes, 1+1+1+0 records, response, authoritative, noerror
+query: 1 blah.movie.edu
+answer: blah.movie.edu 259200 A 1.2.3.4
+authority: blah.movie.edu 259200 NS blah.movie.edu
+0
+--- tinydns-data handles another example
+0
+--- tinydns-data uses serial 1 for mtime 0
+255 test:
+152 bytes, 1+3+0+3 records, response, authoritative, noerror
+query: 255 test
+answer: test 2560 SOA a.ns.test hostmaster.test 1 16384 2048 1048576 2560
+answer: test 259200 NS a.ns.test
+answer: test 259200 NS b.ns.test
+additional: a.ns.test 259200 A 10.2.3.4
+additional: b.ns.test 259200 A 10.2.3.6
+additional: b.ns.test 259200 A 10.2.3.5
+0
+--- tinydns-data does not split size-127 TXT records
+16 127.test:
+249 bytes, 1+1+2+3 records, response, authoritative, noerror
+query: 16 127.test
+answer: 127.test 86400 16 \1770123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456
+authority: test 259200 NS a.ns.test
+authority: test 259200 NS b.ns.test
+additional: a.ns.test 259200 A 10.2.3.4
+additional: b.ns.test 259200 A 10.2.3.6
+additional: b.ns.test 259200 A 10.2.3.5
+0
+--- tinydns-data splits size-128 TXT records
+16 128.test:
+251 bytes, 1+1+2+3 records, response, authoritative, noerror
+query: 16 128.test
+answer: 128.test 86400 16 \1770123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456\0017
+authority: test 259200 NS a.ns.test
+authority: test 259200 NS b.ns.test
+additional: a.ns.test 259200 A 10.2.3.4
+additional: b.ns.test 259200 A 10.2.3.6
+additional: b.ns.test 259200 A 10.2.3.5
+0
+--- tinydns-data splits size-254 TXT records
+16 254.test:
+377 bytes, 1+1+2+3 records, response, authoritative, noerror
+query: 16 254.test
+answer: 254.test 86400 16 \1770123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456\1777890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123
+authority: test 259200 NS a.ns.test
+authority: test 259200 NS b.ns.test
+additional: a.ns.test 259200 A 10.2.3.4
+additional: b.ns.test 259200 A 10.2.3.6
+additional: b.ns.test 259200 A 10.2.3.5
+0
+--- tinydns-data doubly splits size-255 TXT records
+16 255.test:
+379 bytes, 1+1+2+3 records, response, authoritative, noerror
+query: 16 255.test
+answer: 255.test 86400 16 \1770123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456\1777890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123\0014
+authority: test 259200 NS a.ns.test
+authority: test 259200 NS b.ns.test
+additional: a.ns.test 259200 A 10.2.3.4
+additional: b.ns.test 259200 A 10.2.3.6
+additional: b.ns.test 259200 A 10.2.3.5
+0
+--- tinydns-data excludes the additional section if necessary
+16 387.test:
+512 bytes, 1+1+2+3 records, response, authoritative, noerror
+query: 16 387.test
+answer: 387.test 86400 16 \1770123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456\1777890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123\1774567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\006123456
+authority: test 259200 NS a.ns.test
+authority: test 259200 NS b.ns.test
+additional: a.ns.test 259200 A 10.2.3.4
+additional: b.ns.test 259200 A 10.2.3.6
+additional: b.ns.test 259200 A 10.2.3.5
+0
+16 388.test:
+465 bytes, 1+1+2+0 records, response, authoritative, noerror
+query: 16 388.test
+answer: 388.test 86400 16 \1770123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456\1777890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123\1774567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\0071234567
+authority: test 259200 NS a.ns.test
+authority: test 259200 NS b.ns.test
+0
+--- tinydns-data excludes the authority section if necessary
+16 435.test:
+512 bytes, 1+1+2+0 records, response, authoritative, noerror
+query: 16 435.test
+answer: 435.test 86400 16 \1770123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456\1777890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123\17745678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678906123456789012345678901234567890123456789012345678901234
+authority: test 259200 NS a.ns.test
+authority: test 259200 NS b.ns.test
+0
+16 436.test:
+478 bytes, 1+1+0+0 records, response, authoritative, noerror
+query: 16 436.test
+answer: 436.test 86400 16 \1770123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456\1777890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123\177456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789071234567890123456789012345678901234567890123456789012345
+0
+--- tinydns-data handles size-1000 TXT records
+16 1000.test:
+1047 bytes, 1+1+0+0 records, response, authoritative, noerror
+query: 16 1000.test
+answer: 1000.test 86400 16 \1770123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456\1777890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123\1774567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\1771234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567\1778901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234\1775678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901\1772345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678o901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
+0
+--- tinydns-data handles unusual characters in owner names
+1 \000\001\177\200\277\056\056\056.test:
+130 bytes, 1+1+2+3 records, response, authoritative, noerror
+query: 1 \000\001\177\200\277\056\056\056.test
+answer: \000\001\177\200\277\056\056\056.test 86400 A 10.5.6.7
+authority: test 259200 NS a.ns.test
+authority: test 259200 NS b.ns.test
+additional: a.ns.test 259200 A 10.2.3.4
+additional: b.ns.test 259200 A 10.2.3.6
+additional: b.ns.test 259200 A 10.2.3.5
+0
+--- tinydns-data handles unusual characters in PTR results
+12 7.6.5.10.in-addr.arpa:
+99 bytes, 1+1+1+1 records, response, authoritative, noerror
+query: 12 7.6.5.10.in-addr.arpa
+answer: 7.6.5.10.in-addr.arpa 86400 PTR \000\001\177\200\277\056\056\056.test
+authority: 7.6.5.10.in-addr.arpa 259200 NS ns.7.6.5.10.in-addr.arpa
+additional: ns.7.6.5.10.in-addr.arpa 259200 A 10.5.6.7
+0
+--- tinydns-data handles delegations
+1 x.\000\001\177\200\277\056\056\056.test:
+66 bytes, 1+0+1+1 records, response, noerror
+query: 1 x.\000\001\177\200\277\056\056\056.test
+authority: x.\000\001\177\200\277\056\056\056.test 259200 NS ns.x.\000\001\177\200\277\056\056\056.test
+additional: ns.x.\000\001\177\200\277\056\056\056.test 259200 A 10.8.9.10
+0
+1 ns.x.\000\001\177\200\277\056\056\056.test:
+66 bytes, 1+0+1+1 records, response, noerror
+query: 1 ns.x.\000\001\177\200\277\056\056\056.test
+authority: x.\000\001\177\200\277\056\056\056.test 259200 NS ns.x.\000\001\177\200\277\056\056\056.test
+additional: ns.x.\000\001\177\200\277\056\056\056.test 259200 A 10.8.9.10
+0
+1 z.y.x.\000\001\177\200\277\056\056\056.test:
+70 bytes, 1+0+1+1 records, response, noerror
+query: 1 z.y.x.\000\001\177\200\277\056\056\056.test
+authority: x.\000\001\177\200\277\056\056\056.test 259200 NS ns.x.\000\001\177\200\277\056\056\056.test
+additional: ns.x.\000\001\177\200\277\056\056\056.test 259200 A 10.8.9.10
+0
+--- tinydns-data handles another example
+0
+--- tinydns-data handles TTLs
+255 test:
+202 bytes, 1+6+0+2 records, response, authoritative, noerror
+query: 255 test
+answer: test 98765 SOA primary.server host.master 1234567 2345678 3456789 4567890 5678901
+answer: test 37 NS ns.test
+answer: test 41 MX 0 mx.test
+answer: test 42 16 \004Text
+answer: test 43 12345 Binary
+answer: test 39 A 1.2.3.4
+additional: ns.test 37 A 1.2.3.4
+additional: mx.test 41 A 1.2.3.4
+0
+255 www.test:
+75 bytes, 1+1+1+1 records, response, authoritative, noerror
+query: 255 www.test
+answer: www.test 40 A 1.2.3.4
+authority: test 37 NS ns.test
+additional: ns.test 37 A 1.2.3.4
+0
+255 child.test:
+61 bytes, 1+0+1+1 records, response, noerror
+query: 255 child.test
+authority: child.test 38 NS ns.child.test
+additional: ns.child.test 38 A 1.2.3.5
+0
+--- tinydns-data handles CNAMEs
+255 mail.test:
+78 bytes, 1+1+1+1 records, response, authoritative, noerror
+query: 255 mail.test
+answer: mail.test 44 CNAME www.test
+authority: test 37 NS ns.test
+additional: ns.test 37 A 1.2.3.4
+0
+5 mail.test:
+78 bytes, 1+1+1+1 records, response, authoritative, noerror
+query: 5 mail.test
+answer: mail.test 44 CNAME www.test
+authority: test 37 NS ns.test
+additional: ns.test 37 A 1.2.3.4
+0
+1 mail.test:
+78 bytes, 1+1+1+1 records, response, authoritative, noerror
+query: 1 mail.test
+answer: mail.test 44 CNAME www.test
+authority: test 37 NS ns.test
+additional: ns.test 37 A 1.2.3.4
+0
+255 foo.mail.test:
+92 bytes, 1+0+1+0 records, response, authoritative, nxdomain
+query: 255 foo.mail.test
+authority: test 98765 SOA primary.server host.master 1234567 2345678 3456789 4567890 5678901
+0
+--- tinydns-data does not apply wildcard A to base name
+1 wild.test:
+88 bytes, 1+0+1+0 records, response, authoritative, nxdomain
+query: 1 wild.test
+authority: test 98765 SOA primary.server host.master 1234567 2345678 3456789 4567890 5678901
+0
+--- tinydns-data handles wildcard A records
+1 x.wild.test:
+78 bytes, 1+1+1+1 records, response, authoritative, noerror
+query: 1 x.wild.test
+answer: x.wild.test 45 A 1.2.3.6
+authority: test 37 NS ns.test
+additional: ns.test 37 A 1.2.3.4
+0
+1 xy.wild.test:
+79 bytes, 1+1+1+1 records, response, authoritative, noerror
+query: 1 xy.wild.test
+answer: xy.wild.test 45 A 1.2.3.6
+authority: test 37 NS ns.test
+additional: ns.test 37 A 1.2.3.4
+0
+1 x.z.wild.test:
+80 bytes, 1+1+1+1 records, response, authoritative, noerror
+query: 1 x.z.wild.test
+answer: x.z.wild.test 45 A 1.2.3.6
+authority: test 37 NS ns.test
+additional: ns.test 37 A 1.2.3.4
+0
+--- tinydns-data handles wildcard MX records
+255 wild.test:
+88 bytes, 1+0+1+0 records, response, authoritative, nxdomain
+query: 255 wild.test
+authority: test 98765 SOA primary.server host.master 1234567 2345678 3456789 4567890 5678901
+0
+--- tinydns-data does not apply wildcard MX to base name
+255 x.wild.test:
+115 bytes, 1+2+1+2 records, response, authoritative, noerror
+query: 255 x.wild.test
+answer: x.wild.test 46 MX 54321 mail.wild.test
+answer: x.wild.test 45 A 1.2.3.6
+authority: test 37 NS ns.test
+additional: mail.wild.test 46 A 1.2.3.7
+additional: ns.test 37 A 1.2.3.4
+0
+255 xy.wild.test:
+116 bytes, 1+2+1+2 records, response, authoritative, noerror
+query: 255 xy.wild.test
+answer: xy.wild.test 46 MX 54321 mail.wild.test
+answer: xy.wild.test 45 A 1.2.3.6
+authority: test 37 NS ns.test
+additional: mail.wild.test 46 A 1.2.3.7
+additional: ns.test 37 A 1.2.3.4
+0
+15 x.z.wild.test:
+101 bytes, 1+1+1+2 records, response, authoritative, noerror
+query: 15 x.z.wild.test
+answer: x.z.wild.test 46 MX 54321 mail.wild.test
+authority: test 37 NS ns.test
+additional: mail.wild.test 46 A 1.2.3.7
+additional: ns.test 37 A 1.2.3.4
+0
+255 x.z.wild.test:
+117 bytes, 1+2+1+2 records, response, authoritative, noerror
+query: 255 x.z.wild.test
+answer: x.z.wild.test 46 MX 54321 mail.wild.test
+answer: x.z.wild.test 45 A 1.2.3.6
+authority: test 37 NS ns.test
+additional: mail.wild.test 46 A 1.2.3.7
+additional: ns.test 37 A 1.2.3.4
+0
+255 \052.wild.test:
+115 bytes, 1+2+1+2 records, response, authoritative, noerror
+query: 255 \052.wild.test
+answer: \052.wild.test 46 MX 54321 mail.wild.test
+answer: \052.wild.test 45 A 1.2.3.6
+authority: test 37 NS ns.test
+additional: mail.wild.test 46 A 1.2.3.7
+additional: ns.test 37 A 1.2.3.4
+0
+--- tinydns-data uses wildcard under base of sub-wildcard
+255 alias.wild.test:
+119 bytes, 1+2+1+2 records, response, authoritative, noerror
+query: 255 alias.wild.test
+answer: alias.wild.test 46 MX 54321 mail.wild.test
+answer: alias.wild.test 45 A 1.2.3.6
+authority: test 37 NS ns.test
+additional: mail.wild.test 46 A 1.2.3.7
+additional: ns.test 37 A 1.2.3.4
+0
+--- tinydns-data handles wildcard CNAME records
+255 xyz.alias.wild.test:
+84 bytes, 1+1+1+1 records, response, authoritative, noerror
+query: 255 xyz.alias.wild.test
+answer: xyz.alias.wild.test 50 CNAME wild.test
+authority: test 37 NS ns.test
+additional: ns.test 37 A 1.2.3.4
+0
+255 \052.alias.wild.test:
+82 bytes, 1+1+1+1 records, response, authoritative, noerror
+query: 255 \052.alias.wild.test
+answer: \052.alias.wild.test 50 CNAME wild.test
+authority: test 37 NS ns.test
+additional: ns.test 37 A 1.2.3.4
+0
+--- tinydns-data lets explicit record override wildcard
+255 override.wild.test:
+85 bytes, 1+1+1+1 records, response, authoritative, noerror
+query: 255 override.wild.test
+answer: override.wild.test 47 A 1.2.3.8
+authority: test 37 NS ns.test
+additional: ns.test 37 A 1.2.3.4
+0
+--- tinydns-data handles overrides sanely
+255 x.override.wild.test:
+124 bytes, 1+2+1+2 records, response, authoritative, noerror
+query: 255 x.override.wild.test
+answer: x.override.wild.test 46 MX 54321 mail.wild.test
+answer: x.override.wild.test 45 A 1.2.3.6
+authority: test 37 NS ns.test
+additional: mail.wild.test 46 A 1.2.3.7
+additional: ns.test 37 A 1.2.3.4
+0
+--- tinydns-data overrides wildcard with subdomain wildcard
+255 x.wild.wild.test:
+83 bytes, 1+1+1+1 records, response, authoritative, noerror
+query: 255 x.wild.wild.test
+answer: x.wild.wild.test 48 A 1.2.3.9
+authority: test 37 NS ns.test
+additional: ns.test 37 A 1.2.3.4
+0
+--- tinydns-data overrides wildcard with delegation
+255 child.wild.test:
+69 bytes, 1+0+1+1 records, response, noerror
+query: 255 child.wild.test
+authority: child.wild.test 259200 NS 49.ns.child.wild.test
+additional: 49.ns.child.wild.test 259200 A 1.2.3.10
+0
+255 x.child.wild.test:
+71 bytes, 1+0+1+1 records, response, noerror
+query: 255 x.child.wild.test
+authority: child.wild.test 259200 NS 49.ns.child.wild.test
+additional: 49.ns.child.wild.test 259200 A 1.2.3.10
+0
+--- tinydns-data handles another example
+0
+--- tinydns-data handles ending time
+255 www.four:
+0
+255 www.six:
+74 bytes, 1+1+1+1 records, response, authoritative, noerror
+query: 255 www.six
+answer: www.six 3600 A 1.2.3.6
+authority: six 3600 NS ns.six
+additional: ns.six 3600 A 1.2.3.6
+0
+--- tinydns-data handles starting time
+255 www.five:
+75 bytes, 1+1+1+1 records, response, authoritative, noerror
+query: 255 www.five
+answer: www.five 86400 A 1.2.3.5
+authority: five 259200 NS ns.five
+additional: ns.five 259200 A 1.2.3.5
+0
+255 www.seven:
+0
+--- tinydns-edit handles simple examples
+0
+0
+0
+0
+0
+0
+0
+0
+0
+0
+
+.heaven.af.mil:1.2.3.5:a:259200
+.heaven.af.mil:1.2.3.6:b:259200
+&sub.heaven.af.mil:1.2.10.11:a:259200
+&sub.heaven.af.mil:1.2.10.12:b:259200
+=lion.heaven.af.mil:1.2.3.4:86400
+=tiger.heaven.af.mil:1.2.3.5:86400
+=bear.heaven.af.mil:1.2.3.6:86400
++www.heaven.af.mil:1.2.3.4:86400
+@heaven.af.mil:1.2.3.4:a::86400
+@heaven.af.mil:1.2.3.7:b::86400
+--- tinydns-edit rejects hosts with old names or IP addresses
+tinydns-edit: fatal: IP address already used
+100
+tinydns-edit: fatal: host name already used
+100
+
+.heaven.af.mil:1.2.3.5:a:259200
+.heaven.af.mil:1.2.3.6:b:259200
+&sub.heaven.af.mil:1.2.10.11:a:259200
+&sub.heaven.af.mil:1.2.10.12:b:259200
+=lion.heaven.af.mil:1.2.3.4:86400
+=tiger.heaven.af.mil:1.2.3.5:86400
+=bear.heaven.af.mil:1.2.3.6:86400
++www.heaven.af.mil:1.2.3.4:86400
+@heaven.af.mil:1.2.3.4:a::86400
+@heaven.af.mil:1.2.3.7:b::86400
+--- tinydns-edit recognizes alternate forms of host names
+tinydns-edit: fatal: host name already used
+100
+
+.heaven.af.mil:1.2.3.5:a:259200
+.heaven.af.mil:1.2.3.6:b:259200
+&sub.heaven.af.mil:1.2.10.11:a:259200
+&sub.heaven.af.mil:1.2.10.12:b:259200
+=lion.heaven.af.mil:1.2.3.4:86400
+=tiger.heaven.af.mil:1.2.3.5:86400
+=bear.heaven.af.mil:1.2.3.6:86400
++www.heaven.af.mil:1.2.3.4:86400
+@heaven.af.mil:1.2.3.4:a::86400
+@heaven.af.mil:1.2.3.7:b::86400
+--- tinydns-edit copies TTLs from previous NS records
+0
+.test:1.2.3.4:a:3600
+.test:1.2.3.5:b:3600
+--- dnscache handles dotted-decimal names
+255 127.43.123.234:
+48 bytes, 1+1+0+0 records, response, noerror
+query: 255 127.43.123.234
+answer: 127.43.123.234 655360 A 127.43.123.234
+0
+--- tinydns works
+
+127.43.0.100
+127.43.0.101
+0
+1234 a.mx.test
+45678 b.mx.test
+0
+255 www.test:
+91 bytes, 1+2+1+1 records, response, authoritative, noerror
+additional: ns.test 259200 A 127.43.0.2
+answer: www.test 86400 A 127.43.0.100
+answer: www.test 86400 A 127.43.0.101
+authority: test 259200 NS ns.test
+query: 255 www.test
+0
+255 test:
+173 bytes, 1+4+0+3 records, response, authoritative, noerror
+query: 255 test
+answer: test 2560 SOA ns.test hostmaster.test 987654321 16384 2048 1048576 2560
+answer: test 259200 NS ns.test
+answer: test 86400 MX 1234 a.mx.test
+answer: test 86400 MX 45678 b.mx.test
+additional: ns.test 259200 A 127.43.0.2
+additional: a.mx.test 86400 A 127.43.0.100
+additional: b.mx.test 86400 A 127.43.0.101
+0
+--- dnscache handles large TXT records
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
+--- walldns handles in-addr.arpa names
+7.6.43.127.in-addr.arpa
+0
+234.123.43.127.in-addr.arpa
+0
+127.43.123.234
+0
+255 234.123.43.127.in-addr.arpa:
+75 bytes, 1+2+0+0 records, response, authoritative, noerror
+query: 255 234.123.43.127.in-addr.arpa
+answer: 234.123.43.127.in-addr.arpa 655360 A 127.43.123.234
+answer: 234.123.43.127.in-addr.arpa 655360 PTR 234.123.43.127.in-addr.arpa
+0
+--- walldns handles dotted-decimal names
+255 127.43.123.234:
+48 bytes, 1+1+0+0 records, response, authoritative, noerror
+query: 255 127.43.123.234
+answer: 127.43.123.234 655360 A 127.43.123.234
+0
+--- walldns rejects other names
+255 blah.test:
+temporary failure
+0
+--- rbldns works
+127.0.0.3
+0
+See http://www.rbl.test/5.4.3.2
+0
+255 2.3.4.5.rbl.test:
+94 bytes, 1+2+0+0 records, response, authoritative, noerror
+query: 255 2.3.4.5.rbl.test
+answer: 2.3.4.5.rbl.test 2048 A 127.0.0.3
+answer: 2.3.4.5.rbl.test 2048 16 \037See\040http://www.rbl.test/5.4.3.2
+0
+127.0.0.3
+0
+See http://www.rbl.test/4.1.255.200
+0
+255 200.255.1.4.rbl.test:
+102 bytes, 1+2+0+0 records, response, authoritative, noerror
+query: 255 200.255.1.4.rbl.test
+answer: 200.255.1.4.rbl.test 2048 A 127.0.0.3
+answer: 200.255.1.4.rbl.test 2048 16 #See\040http://www.rbl.test/4.1.255.200
+0
+127.0.0.3
+0
+See http://www.rbl.test/4.0.255.200
+0
+255 200.255.0.4.rbl.test:
+102 bytes, 1+2+0+0 records, response, authoritative, noerror
+query: 255 200.255.0.4.rbl.test
+answer: 200.255.0.4.rbl.test 2048 A 127.0.0.3
+answer: 200.255.0.4.rbl.test 2048 16 #See\040http://www.rbl.test/4.0.255.200
+0
+127.0.0.3
+0
+See http://www.rbl.test/4.0.0.1
+0
+255 1.0.0.4.rbl.test:
+94 bytes, 1+2+0+0 records, response, authoritative, noerror
+query: 255 1.0.0.4.rbl.test
+answer: 1.0.0.4.rbl.test 2048 A 127.0.0.3
+answer: 1.0.0.4.rbl.test 2048 16 \037See\040http://www.rbl.test/4.0.0.1
+0
+
+0
+
+0
+255 0.0.0.4.rbl.test:
+34 bytes, 1+0+0+0 records, response, authoritative, nxdomain
+query: 255 0.0.0.4.rbl.test
+0
+--- tinydns handles differentiation
+
+0
+127.43.0.102
+0
+
+127.43.0.100
+127.43.0.102
+0
+
+127.43.0.100
+127.43.0.102
+0
+255 pick.test5:
+81 bytes, 1+0+1+0 records, response, authoritative, nxdomain
+query: 255 pick.test5
+authority: test5 2560 SOA me.ns.test5 hostmaster.test5 987654321 16384 2048 1048576 2560
+0
+255 pick2.test5:
+81 bytes, 1+1+1+1 records, response, authoritative, noerror
+query: 255 pick2.test5
+answer: pick2.test5 86400 A 127.43.0.102
+authority: test5 259200 NS me.ns.test5
+additional: me.ns.test5 259200 A 127.43.0.2
+0
+255 pick3.test5:
+97 bytes, 1+2+1+1 records, response, authoritative, noerror
+additional: me.ns.test5 259200 A 127.43.0.2
+answer: pick3.test5 86400 A 127.43.0.100
+answer: pick3.test5 86400 A 127.43.0.102
+authority: test5 259200 NS me.ns.test5
+query: 255 pick3.test5
+0
+103 bytes, 1+2+1+1 records, response, authoritative, noerror
+255 really.wild.test5:
+additional: me.ns.test5 259200 A 127.43.0.2
+answer: really.wild.test5 86400 A 127.43.0.100
+answer: really.wild.test5 86400 A 127.43.0.102
+authority: test5 259200 NS me.ns.test5
+query: 255 really.wild.test5
+0
+--- tinydns-get handles differentiation
+255 pick.test5:
+80 bytes, 1+1+1+1 records, response, authoritative, noerror
+query: 255 pick.test5
+answer: pick.test5 86400 A 127.43.0.101
+authority: test5 259200 NS ex.ns.test5
+additional: ex.ns.test5 259200 A 127.43.0.2
+0
+255 pick2.test5:
+82 bytes, 1+0+1+0 records, response, authoritative, nxdomain
+query: 255 pick2.test5
+authority: test5 2560 SOA ex.ns.test5 hostmaster.test5 987654321 16384 2048 1048576 2560
+0
+255 pick3.test5:
+97 bytes, 1+2+1+1 records, response, authoritative, noerror
+additional: ex.ns.test5 259200 A 127.43.0.2
+answer: pick3.test5 86400 A 127.43.0.100
+answer: pick3.test5 86400 A 127.43.0.101
+authority: test5 259200 NS ex.ns.test5
+query: 255 pick3.test5
+0
+103 bytes, 1+2+1+1 records, response, authoritative, noerror
+255 really.wild.test5:
+additional: ex.ns.test5 259200 A 127.43.0.2
+answer: really.wild.test5 86400 A 127.43.0.100
+answer: really.wild.test5 86400 A 127.43.0.101
+authority: test5 259200 NS ex.ns.test5
+query: 255 really.wild.test5
+0
+255 pick.test5:
+81 bytes, 1+0+1+0 records, response, authoritative, nxdomain
+query: 255 pick.test5
+authority: test5 2560 SOA i4.ns.test5 hostmaster.test5 987654321 16384 2048 1048576 2560
+0
+255 pick2.test5:
+82 bytes, 1+0+1+0 records, response, authoritative, nxdomain
+query: 255 pick2.test5
+authority: test5 2560 SOA i4.ns.test5 hostmaster.test5 987654321 16384 2048 1048576 2560
+0
+255 pick3.test5:
+97 bytes, 1+2+1+1 records, response, authoritative, noerror
+additional: i4.ns.test5 259200 A 127.43.0.2
+answer: pick3.test5 86400 A 127.43.0.100
+answer: pick3.test5 86400 A 127.43.0.104
+authority: test5 259200 NS i4.ns.test5
+query: 255 pick3.test5
+0
+103 bytes, 1+2+1+1 records, response, authoritative, noerror
+255 really.wild.test5:
+additional: i4.ns.test5 259200 A 127.43.0.2
+answer: really.wild.test5 86400 A 127.43.0.100
+answer: really.wild.test5 86400 A 127.43.0.104
+authority: test5 259200 NS i4.ns.test5
+query: 255 really.wild.test5
+0
+--- pickdns works
+127.43.0.101
+0
+127.43.0.102
+0
+255 pick.test:
+43 bytes, 1+1+0+0 records, response, authoritative, noerror
+query: 255 pick.test
+answer: pick.test 5 A 127.43.0.101
+0
+--- pickdns answers MX
+0 pick.test
+0
+--- pickdns rejects queries for unknown information
+255 pick11.test:
+temporary failure
+0
+16 pick2.test:
+temporary failure
+0
+--- axfrdns rejects unauthorized transfer attempts
+axfr-get: fatal: unable to parse AXFR results: protocol error
+111
+axfr-get: fatal: unable to parse AXFR results: protocol error
+111
+--- axfrdns works
+0
+#987654321 auto axfr-get
+Ztest:ns.test.:hostmaster.test.:987654321:16384:2048:1048576:2560:2560
+&test::ns.test.:259200
++ns.test:127.43.0.2:259200
++www.test:127.43.0.100:86400
++www.test:127.43.0.101:86400
+@test::a.mx.test.:1234:86400
++a.mx.test:127.43.0.100:86400
+@test::b.mx.test.:45678:86400
++b.mx.test:127.43.0.101:86400
+&pick.test::ns.pick.test.:259200
++ns.pick.test:127.43.0.3:259200
+&pick2.test::ns.pick2.test.:259200
++ns.pick2.test:127.43.0.3:259200
+&rbl.test::ns.rbl.test.:259200
++ns.rbl.test:127.43.0.5:259200
+:big.test:16:\1770123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456\1777890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123\1774567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\1771234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567\1778901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234\1775678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901\1772345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678o901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789:86400
+--- axfrdns handles differentiation
+0
+#987654321 auto axfr-get
+Ztest5:me.ns.test5.:hostmaster.test5.:987654321:16384:2048:1048576:2560:2560
+&test5::me.ns.test5.:259200
++me.ns.test5:127.43.0.2:259200
++pick2.test5:127.43.0.102:86400
++pick3.test5:127.43.0.100:86400
++pick3.test5:127.43.0.102:86400
++\052.wild.test5:127.43.0.100:86400
++\052.wild.test5:127.43.0.102:86400
+0
+#987654321 auto axfr-get
+Ztest5:i3.ns.test5.:hostmaster.test5.:987654321:16384:2048:1048576:2560:2560
+&test5::i3.ns.test5.:259200
++i3.ns.test5:127.43.0.2:259200
++pick3.test5:127.43.0.100:86400
++pick3.test5:127.43.0.103:86400
++\052.wild.test5:127.43.0.100:86400
++\052.wild.test5:127.43.0.103:86400
+0
+#987654321 auto axfr-get
+Ztest5:i4.ns.test5.:hostmaster.test5.:987654321:16384:2048:1048576:2560:2560
+&test5::i4.ns.test5.:259200
++i4.ns.test5:127.43.0.2:259200
++pick3.test5:127.43.0.100:86400
++pick3.test5:127.43.0.104:86400
++\052.wild.test5:127.43.0.100:86400
++\052.wild.test5:127.43.0.104:86400
+0
+#987654321 auto axfr-get
+Ztest5:i5.ns.test5.:hostmaster.test5.:987654321:16384:2048:1048576:2560:2560
+&test5::i5.ns.test5.:259200
++i5.ns.test5:127.43.0.2:259200
++pick3.test5:127.43.0.100:86400
++pick3.test5:127.43.0.105:86400
++\052.wild.test5:127.43.0.100:86400
++\052.wild.test5:127.43.0.105:86400
+--- axfrdns gives authoritative answers
+255 test4:
+727 bytes, 1+12+0+0 records, response, authoritative, noerror
+query: 255 test4
+answer: test4 2560 SOA ns.test4 hostmaster.test4 987654321 16384 2048 1048576 2560
+answer: test4 259200 NS ns.test4
+answer: test4 86400 16 3001234567890123456789012345678901234567890123456789
+answer: test4 86400 16 3101234567890123456789012345678901234567890123456789
+answer: test4 86400 16 3201234567890123456789012345678901234567890123456789
+answer: test4 86400 16 3301234567890123456789012345678901234567890123456789
+answer: test4 86400 16 3401234567890123456789012345678901234567890123456789
+answer: test4 86400 16 3501234567890123456789012345678901234567890123456789
+answer: test4 86400 16 3601234567890123456789012345678901234567890123456789
+answer: test4 86400 16 3701234567890123456789012345678901234567890123456789
+answer: test4 86400 16 3801234567890123456789012345678901234567890123456789
+answer: test4 86400 16 3901234567890123456789012345678901234567890123456789
+0
+--- axfrdns handles size-1000 TXT records
+255 big.test:
+1046 bytes, 1+1+0+0 records, response, authoritative, noerror
+query: 255 big.test
+answer: big.test 86400 16 \1770123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456\1777890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123\1774567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\1771234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567\1778901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234\1775678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901\1772345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678o901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
+0
+--- axfr-get handles zones with wildcards
+0
+#987654321 auto axfr-get
+Ztest2:ns.test2.:hostmaster.test2.:987654321:16384:2048:1048576:2560:2560
+&test2::ns.test2.:259200
++ns.test2:127.43.0.2:259200
++\052.test2:127.43.0.102:86400
+C\052.www.test2:www.test2.:5000
++one.test2:127.43.0.103:86400
++two.test2:127.43.0.104:2
diff --git a/rts.sh b/rts.sh
new file mode 100644
index 0000000..c71e839
--- /dev/null
+++ b/rts.sh
@@ -0,0 +1 @@
+env - PATH="`pwd`:$PATH" sh rts.tests 2>&1 | cat -v
diff --git a/rts.tests b/rts.tests
new file mode 100644
index 0000000..ee2be85
--- /dev/null
+++ b/rts.tests
@@ -0,0 +1,767 @@
+# Requirements:
+# You are running as root.
+# You have dns{cache,log}, {tiny,pick,wall,axfr,rbl}dns accounts.
+# You have local IP addresses 127.43.0.{1,2,3,4,5}.
+# You are connected to the Internet.
+#
+# Some features not tested here:
+# dns_random works.
+# random-ip works.
+# dnstrace works.
+# dnstracesort works.
+# dns_resolvconfrewrite rereads after 10 minutes or 10000 uses.
+# dns_resolvconfip rereads after 10 minutes or 10000 uses.
+# /etc/resolv.conf is parsed properly.
+# dns_transmit handles timeouts properly.
+# dns_transmit falls back to TCP properly.
+# dns_transmit handles various strange situations: e.g., NOTIMP.
+
+
+umask 022
+
+rm -rf rts-tmp
+service=`pwd`/rts-tmp/service
+
+mkdir rts-tmp
+mkdir $service
+
+echo '
+*.b:.2.3.4
+=localhost:localhost.
+-.localhost:localhost.
+?:+.yp.to+.cr.yp.to+.whatever.cr.yp.to
+*.:
+' > rts-tmp/rewrite
+
+DNSREWRITEFILE=rts-tmp/rewrite; export DNSREWRITEFILE
+DNSCACHEIP=127.555.0.1; export DNSCACHEIP
+
+
+echo '--- dnscache-conf works'
+dnscache-conf dnscache dnslog $service/dnscache 127.555.0.1
+echo 127.555.0.2 > $service/dnscache/root/servers/tEST
+echo 127.555.0.2 > $service/dnscache/root/servers/tEST5
+echo 127.555.0.4 > $service/dnscache/root/servers/43.127.iN-aDDR.aRPA
+touch $service/dnscache/root/ip/127.43.0.1
+supervise $service/dnscache | supervise $service/dnscache/log &
+
+echo '--- tinydns-conf works'
+tinydns-conf tinydns dnslog $service/tinydns 127.555.0.2
+supervise $service/tinydns | supervise $service/tinydns/log &
+
+echo '--- pickdns-conf works'
+pickdns-conf pickdns dnslog $service/pickdns 127.555.0.3
+supervise $service/pickdns | supervise $service/pickdns/log &
+
+echo '--- walldns-conf works'
+walldns-conf walldns dnslog $service/walldns 127.555.0.4
+supervise $service/walldns | supervise $service/walldns/log &
+
+echo '--- rbldns-conf works'
+rbldns-conf rbldns dnslog $service/rbldns 127.555.0.5 RbL.TeSt
+supervise $service/rbldns | supervise $service/rbldns/log &
+
+echo '--- axfrdns-conf works'
+axfrdns-conf axfrdns dnslog $service/axfrdns $service/tinydns 127.555.0.2
+supervise $service/axfrdns | supervise $service/axfrdns/log &
+
+sleep 1
+
+
+echo '--- cache handles simple example'
+cachetest \
+one two three four five \
+one:un one two three four five \
+two:deux one two three four five \
+three:trois one two three four five \
+four:quatre one two three four five \
+five:cinq one two three four five \
+one:een one two three four five \
+two:twee one two three four five \
+three:drie one two three four five \
+four:vier one two three four five \
+five:vijf one two three four five
+echo $?
+
+echo '--- cache handles overwriting'
+cachetest \
+one two three four five \
+one:un one two three four five \
+one:een one two three four five \
+two:deux one two three four five \
+two:twee one two three four five \
+three:trois one two three four five \
+three:drie one two three four five \
+four:quatre one two three four five \
+four:vier one two three four five \
+five:cinq one two three four five \
+five:vijf one two three four five
+echo $?
+
+echo '--- cache handles long chains'
+cachetest \
+a:1 a \
+a:2 a \
+a:3 a \
+a:4 a \
+a:5 a \
+a:6 a \
+a:7 a \
+a:8 a \
+a:9 a
+echo $?
+
+
+echo '--- dnsip finds IP address of network-surveys.cr.yp.to'
+dnsip network-surveys.cr.yp.to
+echo $?
+
+echo '--- dnsip does not find nonexistent.cr.yp.to'
+dnsip nonexistent.cr.yp.to
+echo $?
+
+echo '--- dnsip rejects overly long domain names'
+dnsip x.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789
+echo $?
+
+echo '--- dnsip handles IP address on input'
+dnsip 1.2.3.4 127.0.0.1 10.555.678.901 '[010.0555.0678.0901]'
+echo $?
+
+echo '--- dnsip allows 0 to be omitted'
+dnsip 127...1
+echo $?
+
+echo '--- dnsip handles multiple IP addresses on input'
+dnsip 1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16
+echo $?
+
+echo '--- dnsipq handles simple examples'
+dnsipq 1.b localhost anything.localhost 5.6.7.8 network-surveys nonexistent
+echo $?
+
+echo '--- dnsmx finds MX record for network-surveys.cr.yp.to'
+dnsmx network-surveys.cr.yp.to
+echo $?
+
+echo '--- dnsmx manufactures MX record for nonexistent.cr.yp.to'
+dnsmx NONexistent.cr.yp.to
+echo $?
+
+echo '--- dnsmx rejects overly long domain names'
+dnsmx 0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789
+echo $?
+
+echo '--- dnstxt finds TXT record for leap.yp.to'
+dnstxt leap.yp.to
+echo $?
+
+echo '--- dnstxt does not find nonexistent.cr.yp.to'
+dnstxt nonexistent.cr.yp.to
+echo $?
+
+echo '--- dnstxt rejects overly long domain names'
+dnstxt 0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789.0123456789
+echo $?
+
+echo '--- dnsname finds host name of 131.193.178.100'
+dnsname 131.193.178.100
+echo $?
+
+echo '--- dnsname does not find 127.5.6.7'
+dnsname 127.5.6.7
+echo $?
+
+echo '--- dnsname rejects misformatted IP addresses'
+dnsname 1.2.3
+echo $?
+
+echo '--- dnsfilter finds some host names'
+echo '131.193.178.100+one two three
+127.5.6.7+one two three
+10+one two three' | dnsfilter
+echo $?
+
+
+echo '
+=movie.edu:1.2.3.4
+*star
+' > rts-tmp/data
+
+echo '--- tinydns-data complains about unrecognized initial characters'
+( cd rts-tmp; tinydns-data; echo $? )
+
+echo '
+.movie.edu:192.249.249.3:a
+.movie.edu::wormhole.movie.edu
+&fx.movie.edu:192.253.254.2:a
+&fx.movie.edu:192.253.254.3:b
+.249.249.192.in-addr.arpa:192.249.249.3:a
+.249.249.192.in-addr.arpa::wormhole.movie.edu
+.253.253.192.in-addr.arpa:192.249.249.3:a
+.253.253.192.in-addr.arpa:192.249.249.1:b
+.253.253.192.in-addr.arpa:192.253.253.1:c
+.254.253.192.in-addr.arpa:192.253.254.2:a
+.254.253.192.in-addr.arpa:192.253.254.3:b
+
++localhost.movie.edu:127.0.0.1
+
+@movie.edu:192.249.249.1:a
+
+'\''movie.edu:Movie University
+:movie.edu:12345:One
+:movie.edu:12345:Two
+:movie.edu:12346:Three
+:movie.edu:12346:Four
+
+=wormhole.movie.edu:192.249.249.1
++wh249.movie.edu:192.249.249.1
+=robocop.movie.edu:192.249.249.2
+=terminator.movie.edu:192.249.249.3
++bigt.movie.edu:192.249.249.3
+=diehard.movie.edu:192.249.294.4
++dh.movie.edu:192.249.294.4
+
+=wormhole.movie.edu:192.253.253.1
++wh253.movie.edu:192.253.253.1
++wh.movie.edu:192.253.253.1
++wh.movie.edu:192.253.253.1
+=misery.movie.edu:192.253.253.2
+=shining.movie.edu:192.253.253.3
+=carrie.movie.edu:192.253.253.4
+
+.blah.movie.edu:1.2.3.4:blah.movie.edu
+'\''blah.movie.edu:Text
+' > rts-tmp/data
+utime rts-tmp/data 987654321
+
+echo '--- tinydns-data complains if it cannot create data.tmp'
+rm -f rts-tmp/data.tmp
+ln -s data.tmp rts-tmp/data.tmp
+( cd rts-tmp; tinydns-data; echo $? )
+rm -f rts-tmp/data.tmp
+
+echo '--- tinydns-data handles simple example'
+( cd rts-tmp; tinydns-data; echo $? )
+
+echo '--- tinydns-data produces A records'
+( cd rts-tmp; tinydns-get 1 wormhole.movie.edu | sort; echo $? )
+
+echo '--- tinydns-data produces NS records'
+( cd rts-tmp; tinydns-get 2 movie.edu; echo $? )
+
+echo '--- tinydns-data produces SOA records'
+( cd rts-tmp; tinydns-get 6 movie.edu; echo $? )
+
+echo '--- tinydns-data produces PTR records'
+( cd rts-tmp; tinydns-get 12 1.253.253.192.in-addr.arpa; echo $? )
+
+echo '--- tinydns-data produces MX records'
+( cd rts-tmp; tinydns-get 15 movie.edu; echo $? )
+
+echo '--- tinydns-data produces TXT records'
+( cd rts-tmp; tinydns-get 16 movie.edu; echo $? )
+
+echo '--- tinydns-data produces AXFR responses'
+( cd rts-tmp; tinydns-get 252 movie.edu; echo $? )
+
+echo '--- tinydns-data produces ANY responses'
+( cd rts-tmp; tinydns-get 255 movie.edu; echo $? )
+
+echo '--- tinydns-data produces records of any type'
+( cd rts-tmp; tinydns-get 12345 movie.edu; echo $? )
+( cd rts-tmp; tinydns-get 12346 movie.edu; echo $? )
+
+echo '--- tinydns-data produces NODATA responses'
+( cd rts-tmp; tinydns-get 54321 movie.edu; echo $? )
+
+echo '--- tinydns-data produces NXDOMAIN responses'
+( cd rts-tmp; tinydns-get 1 this.does.not.exist.movie.edu; echo $? )
+
+echo '--- tinydns-data produces NXDOMAIN responses for suffixes'
+( cd rts-tmp; tinydns-get 1 ns.movie.edu; echo $? )
+
+echo '--- tinydns-data produces NXDOMAIN ANY responses for suffixes'
+( cd rts-tmp; tinydns-get 255 ns.movie.edu; echo $? )
+
+echo '--- tinydns-data does not produce responses outside its bailiwick'
+( cd rts-tmp; tinydns-get 1 edu; echo $? )
+
+echo '--- tinydns-data does not include TXT in additional sections'
+( cd rts-tmp; tinydns-get 1 blah.movie.edu; echo $? )
+
+
+echo '
+.test:10.2.3.4:a
++b.ns.test:10.2.3.6:259200
+.test:10.2.3.5:b
+'\''127.test:0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456
+'\''128.test:01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567
+'\''254.test:01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123
+'\''255.test:012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234
+'\''387.test:012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456
+'\''388.test:0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567
+'\''400.test:0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
+'\''410.test:01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
+'\''420.test:012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
+'\''430.test:0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
+'\''435.test:012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234
+'\''436.test:0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345
+'\''1000.test:0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
+=\000\001\177\200\277\.\.\..test:10.5.6.7
+.7.6.5.10.in-addr.arpa:10.5.6.7
+
+&x.\0\1\177\200\277\.\.\..test:10.8.9.10
+' > rts-tmp/data
+utime rts-tmp/data 0
+
+echo '--- tinydns-data handles another example'
+( cd rts-tmp; tinydns-data; echo $? )
+
+echo '--- tinydns-data uses serial 1 for mtime 0'
+( cd rts-tmp; tinydns-get Any test; echo $? )
+
+echo '--- tinydns-data does not split size-127 TXT records'
+( cd rts-tmp; tinydns-get Txt 127.test; echo $? )
+
+echo '--- tinydns-data splits size-128 TXT records'
+( cd rts-tmp; tinydns-get 16 128.test; echo $? )
+
+echo '--- tinydns-data splits size-254 TXT records'
+( cd rts-tmp; tinydns-get 16 254.test; echo $? )
+
+echo '--- tinydns-data doubly splits size-255 TXT records'
+( cd rts-tmp; tinydns-get 16 255.test; echo $? )
+
+echo '--- tinydns-data excludes the additional section if necessary'
+( cd rts-tmp; tinydns-get 16 387.test; echo $? )
+( cd rts-tmp; tinydns-get 16 388.test; echo $? )
+
+echo '--- tinydns-data excludes the authority section if necessary'
+( cd rts-tmp; tinydns-get 16 435.test; echo $? )
+( cd rts-tmp; tinydns-get 16 436.test; echo $? )
+
+echo '--- tinydns-data handles size-1000 TXT records'
+( cd rts-tmp; tinydns-get 16 1000.test; echo $? )
+
+echo '--- tinydns-data handles unusual characters in owner names'
+( cd rts-tmp; tinydns-get A '\0\1\177\200\277\56\56\56.test'; echo $? )
+
+echo '--- tinydns-data handles unusual characters in PTR results'
+( cd rts-tmp; tinydns-get Ptr 7.6.5.10.in-addr.arpa; echo $? )
+
+echo '--- tinydns-data handles delegations'
+( cd rts-tmp; tinydns-get 1 'x.\0\1\177\200\277\56\56\56.test'; echo $? )
+( cd rts-tmp; tinydns-get 1 'ns.x.\0\1\177\200\277\56\56\56.test'; echo $? )
+( cd rts-tmp; tinydns-get 1 'z.y.x.\0\1\177\200\277\56\56\56.test'; echo $? )
+
+
+echo '
+Ztest:Primary.Server:Host.Master:1234567:2345678:3456789:4567890:5678901:98765
+&test:1.2.3.4::37
+@*.wild.test:1.2.3.7:mail.wild.test:54321:46
+&child.test:1.2.3.5::38
+@test:1.2.3.4:::41
+=test:1.2.3.4:39
++www.test:1.2.3.4:40
+'\''test:Text:42
+:test:12345:Binary:43
+Cmail.test:www.test:44
++*.wild.test:1.2.3.6:45
+=override.wild.test:1.2.3.8:47
++*.wild.wild.test:1.2.3.9:48
+&child.wild.test:1.2.3.10:49
+C*.alias.wild.test:wild.test:50
+' > rts-tmp/data
+utime rts-tmp/data 0
+
+echo '--- tinydns-data handles another example'
+( cd rts-tmp; tinydns-data; echo $? )
+
+echo '--- tinydns-data handles TTLs'
+( cd rts-tmp; tinydns-get 255 test; echo $? )
+( cd rts-tmp; tinydns-get 255 www.test; echo $? )
+( cd rts-tmp; tinydns-get 255 child.test; echo $? )
+
+echo '--- tinydns-data handles CNAMEs'
+( cd rts-tmp; tinydns-get 255 mail.test; echo $? )
+( cd rts-tmp; tinydns-get 5 mail.test; echo $? )
+( cd rts-tmp; tinydns-get 1 mail.test; echo $? )
+( cd rts-tmp; tinydns-get 255 foo.mail.test; echo $? )
+
+echo '--- tinydns-data does not apply wildcard A to base name'
+( cd rts-tmp; tinydns-get 1 wild.test; echo $? )
+
+echo '--- tinydns-data handles wildcard A records'
+( cd rts-tmp; tinydns-get 1 x.wild.test; echo $? )
+( cd rts-tmp; tinydns-get 1 xy.wild.test; echo $? )
+( cd rts-tmp; tinydns-get 1 x.z.wild.test; echo $? )
+
+echo '--- tinydns-data handles wildcard MX records'
+( cd rts-tmp; tinydns-get 255 wild.test; echo $? )
+
+echo '--- tinydns-data does not apply wildcard MX to base name'
+( cd rts-tmp; tinydns-get 255 x.wild.test; echo $? )
+( cd rts-tmp; tinydns-get 255 xy.wild.test; echo $? )
+( cd rts-tmp; tinydns-get 15 x.z.wild.test; echo $? )
+( cd rts-tmp; tinydns-get 255 x.z.wild.test; echo $? )
+( cd rts-tmp; tinydns-get 255 '*'.wild.test; echo $? )
+
+echo '--- tinydns-data uses wildcard under base of sub-wildcard'
+( cd rts-tmp; tinydns-get 255 alias.wild.test; echo $? )
+
+echo '--- tinydns-data handles wildcard CNAME records'
+( cd rts-tmp; tinydns-get 255 xyz.alias.wild.test; echo $? )
+( cd rts-tmp; tinydns-get 255 '*'.alias.wild.test; echo $? )
+
+echo '--- tinydns-data lets explicit record override wildcard'
+( cd rts-tmp; tinydns-get 255 override.wild.test; echo $? )
+
+echo '--- tinydns-data handles overrides sanely'
+( cd rts-tmp; tinydns-get 255 x.override.wild.test; echo $? )
+
+echo '--- tinydns-data overrides wildcard with subdomain wildcard'
+( cd rts-tmp; tinydns-get 255 x.wild.wild.test; echo $? )
+
+echo '--- tinydns-data overrides wildcard with delegation'
+( cd rts-tmp; tinydns-get 255 child.wild.test; echo $? )
+( cd rts-tmp; tinydns-get 255 x.child.wild.test; echo $? )
+
+
+echo '
+.four:1.2.3.4::0:30000000fedcba98
++www.four:1.2.3.4:0:30000000fedcba98
+.five:1.2.3.5:::30000000fedcba98
++www.five:1.2.3.5::30000000fedcba98
+.six:1.2.3.6::0:50000000fedcba98
++www.six:1.2.3.6:0:50000000fedcba98
+.seven:1.2.3.7:::50000000fedcba98
++www.seven:1.2.3.7::50000000fedcba98
+' > rts-tmp/data
+utime rts-tmp/data 7654321
+
+echo '--- tinydns-data handles another example'
+( cd rts-tmp; tinydns-data; echo $? )
+
+echo '--- tinydns-data handles ending time'
+( cd rts-tmp; tinydns-get 255 www.four; echo $? )
+( cd rts-tmp; tinydns-get 255 www.six; echo $? )
+
+echo '--- tinydns-data handles starting time'
+( cd rts-tmp; tinydns-get 255 www.five; echo $? )
+( cd rts-tmp; tinydns-get 255 www.seven; echo $? )
+
+
+echo '--- tinydns-edit handles simple examples'
+echo '' > rts-tmp/data
+( cd rts-tmp; tinydns-edit data data.new add ns heaven.af.mil 1.2.3.5; echo $? )
+( cd rts-tmp; tinydns-edit data data.new add ns heaven.af.mil 1.2.3.6; echo $? )
+( cd rts-tmp; tinydns-edit data data.new add childns sub.heaven.af.mil 1.2.10.11; echo $? )
+( cd rts-tmp; tinydns-edit data data.new add childns sub.heaven.af.mil 1.2.10.12; echo $? )
+( cd rts-tmp; tinydns-edit data data.new add host lion.heaven.af.mil 1.2.3.4; echo $? )
+( cd rts-tmp; tinydns-edit data data.new add host tiger.heaven.af.mil 1.2.3.5; echo $? )
+( cd rts-tmp; tinydns-edit data data.new add host bear.heaven.af.mil 1.2.3.6; echo $? )
+( cd rts-tmp; tinydns-edit data data.new add alias www.heaven.af.mil 1.2.3.4; echo $? )
+( cd rts-tmp; tinydns-edit data data.new add mx heaven.af.mil 1.2.3.4; echo $? )
+( cd rts-tmp; tinydns-edit data data.new add mx heaven.af.mil 1.2.3.7; echo $? )
+cat rts-tmp/data
+
+echo '--- tinydns-edit rejects hosts with old names or IP addresses'
+( cd rts-tmp; tinydns-edit data data.new add host panda.heaven.af.mil 1.2.3.6; echo $? )
+( cd rts-tmp; tinydns-edit data data.new add host bear.heaven.af.mil 1.2.3.8; echo $? )
+cat rts-tmp/data
+
+echo '--- tinydns-edit recognizes alternate forms of host names'
+( cd rts-tmp; tinydns-edit data data.new add host 'BE\101r.Heaven.AF..Mil.' 1.2.3.8; echo $? )
+cat rts-tmp/data
+
+echo '--- tinydns-edit copies TTLs from previous NS records'
+echo '.test:1.2.3.4:a:3600' > rts-tmp/data
+( cd rts-tmp; tinydns-edit data data.new add ns test 1.2.3.5; echo $? )
+cat rts-tmp/data
+
+
+
+echo '
+.Test:127.555.0.2
+=Www.Test:127.555.0.100
+=Www.Test:127.555.0.101
+@Test:127.555.0.100:a:1234
+@Test:127.555.0.101:b:45678
+&Pick.Test:127.555.0.3
+&Pick2.Test:127.555.0.3
+&Rbl.Test:127.555.0.5
+.Test2:127.555.0.2
++*.Test2:127.555.0.102
+C*.Www.Test2:Www.Test2:5000
+=one.Test2:127.555.0.103::300000003456789a
+=two.Test2:127.555.0.104:0:500000003456789a
+.Test3:127.555.0.2
+=Www.Test3:127.0.0.106
+.Test4:127.555.0.2
+'\''Test4:001234567890123456789012345678901234567890123456789
+'\''Test4:101234567890123456789012345678901234567890123456789
+'\''Test4:201234567890123456789012345678901234567890123456789
+'\''Test4:301234567890123456789012345678901234567890123456789
+'\''Test4:401234567890123456789012345678901234567890123456789
+'\''Test4:501234567890123456789012345678901234567890123456789
+'\''Test4:601234567890123456789012345678901234567890123456789
+'\''Test4:701234567890123456789012345678901234567890123456789
+'\''Test4:801234567890123456789012345678901234567890123456789
+'\''Test4:901234567890123456789012345678901234567890123456789
+'\''Big.Test:0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
+%i3:127.555.0.3
+%i4:127.555.0.4
+%i5:127.555.0.5
+%ME:127
+%EX
+.Test5:127.555.0.2:ex:::EX
+.Test5:127.555.0.2:me:::ME
+.Test5:127.555.0.2:i3:::i3
+.Test5:127.555.0.2:i4:::i4
+.Test5:127.555.0.2:i5:::i5
+-Pick.Test5:127.555.0.100:::EX
++Pick.Test5:127.555.0.101:::EX
+-Pick2.Test5:127.555.0.102:::ME
++Pick2.Test5:127.555.0.102:::ME
++Pick3.Test5:127.555.0.100
++Pick3.Test5:127.555.0.101:::EX
++Pick3.Test5:127.555.0.102:::ME
++Pick3.Test5:127.555.0.103:::i3
++Pick3.Test5:127.555.0.104:::i4
++Pick3.Test5:127.555.0.105:::i5
++*.Wild.Test5:127.555.0.100
++*.Wild.Test5:127.555.0.101:::EX
++*.Wild.Test5:127.555.0.102:::ME
++*.Wild.Test5:127.555.0.103:::i3
++*.Wild.Test5:127.555.0.104:::i4
++*.Wild.Test5:127.555.0.105:::i5
+' > $service/tinydns/root/data
+utime $service/tinydns/root/data 987654321
+( cd $service/tinydns/root; tinydns-data )
+
+echo '
+-Pick.Test:127.555.0.100
++Pick.Test:127.555.0.101
+-Pick2.Test:127.555.0.102:ME
++Pick2.Test:127.555.0.102:ME
+%ME:127
+' > $service/pickdns/root/data
+( cd $service/pickdns/root; pickdns-data )
+
+echo '
+4.0.0.1
+4.0.0.2/31
+4.0.0.4/30
+4.0.0.8/29
+4.0.0.16/28
+4.0.0.32/27
+4.0.0.64/26
+4.0.0.128/25
+4.0.1.0/24
+4.0.2.0/23
+4.0.4.0/22
+4.0.8.0/21
+4.0.16.0/20
+4.0.32.0/19
+4.0.64.0/18
+4.0.128.0/17
+4.1.0.0/16
+4.2.0.0/15
+4.4.0.0/14
+4.8.0.0/13
+4.16.0.0/12
+4.32.0.0/11
+4.64.0.0/10
+4.128.0.0/9
+5.0.0.0/8
+:127.0.0.3:See http://www.rbl.test/$
+' > $service/rbldns/root/data
+( cd $service/rbldns/root; rbldns-data )
+
+echo '
+127.:allow,AXFR="tEsT/TeSt2/TEst5"
+:deny
+' > $service/axfrdns/tcp
+( cd $service/axfrdns; tcprules tcp.cdb tcp.tmp < tcp )
+
+
+echo '--- dnscache handles dotted-decimal names'
+dnsqr 255 127.43.123.234
+echo $?
+
+echo '--- tinydns works'
+dnsip WWW.TEST | tr ' ' '\012' | sort
+echo $?
+dnsmx TEST
+echo $?
+dnsq 255 WWW.TEST 127.555.0.2 | sort
+echo $?
+dnsq Any TEST 127.555.0.2
+echo $?
+
+echo '--- dnscache handles large TXT records'
+dnstxt BIG.Test
+
+echo '--- walldns handles in-addr.arpa names'
+dnsname 127.555.6.7
+echo $?
+dnsname 127.555.123.234
+echo $?
+dnsip 234.123.43.127.IN-ADDR.ARPA
+echo $?
+dnsq 255 234.123.43.127.IN-ADDR.ARPA 127.555.0.4
+echo $?
+
+echo '--- walldns handles dotted-decimal names'
+dnsq 255 127.43.123.234 127.555.0.4
+echo $?
+
+echo '--- walldns rejects other names'
+dnsq 255 BLAH.TEST 127.555.0.4
+echo $?
+
+echo '--- rbldns works'
+dnsip 2.3.4.5.rbl.test
+echo $?
+dnstxt 2.3.4.5.rbl.test
+echo $?
+dnsq 255 2.3.4.5.rbl.test 127.555.0.5
+echo $?
+dnsip 200.255.1.4.rbl.test
+echo $?
+dnstxt 200.255.1.4.rbl.test
+echo $?
+dnsq 255 200.255.1.4.rbl.test 127.555.0.5
+echo $?
+dnsip 200.255.0.4.rbl.test
+echo $?
+dnstxt 200.255.0.4.rbl.test
+echo $?
+dnsq 255 200.255.0.4.rbl.test 127.555.0.5
+echo $?
+dnsip 1.0.0.4.rbl.test
+echo $?
+dnstxt 1.0.0.4.rbl.test
+echo $?
+dnsq 255 1.0.0.4.rbl.test 127.555.0.5
+echo $?
+dnsip 0.0.0.4.rbl.test
+echo $?
+dnstxt 0.0.0.4.rbl.test
+echo $?
+dnsq 255 0.0.0.4.rbl.test 127.555.0.5
+echo $?
+
+echo '--- tinydns handles differentiation'
+dnsip PICK.TEST5
+echo $?
+dnsip PICK2.TEST5
+echo $?
+dnsip PICK3.TEST5 | tr ' ' '\012' | sort
+echo $?
+dnsip REALLY.WILD.TEST5 | tr ' ' '\012' | sort
+echo $?
+dnsq 255 PICK.TEST5 127.555.0.2
+echo $?
+dnsq 255 PICK2.TEST5 127.555.0.2
+echo $?
+dnsq 255 PICK3.TEST5 127.555.0.2 | sort
+echo $?
+dnsq 255 REALLY.WILD.TEST5 127.555.0.2 | sort
+echo $?
+
+echo '--- tinydns-get handles differentiation'
+( cd rts-tmp/service/tinydns/root
+ tinydns-get 255 PICK.TEST5 1.2.3.4; echo $?
+ tinydns-get 255 PICK2.TEST5 1.2.3.4; echo $?
+ tinydns-get 255 PICK3.TEST5 1.2.3.4 | sort; echo $?
+ tinydns-get 255 REALLY.WILD.TEST5 1.2.3.4 | sort; echo $?
+ tinydns-get 255 PICK.TEST5 127.555.0.4; echo $?
+ tinydns-get 255 PICK2.TEST5 127.555.0.4; echo $?
+ tinydns-get 255 PICK3.TEST5 127.555.0.4 | sort; echo $?
+ tinydns-get 255 REALLY.WILD.TEST5 127.555.0.4 | sort; echo $?
+)
+
+echo '--- pickdns works'
+dnsip PICK.TEST
+echo $?
+dnsip PICK2.TEST
+echo $?
+dnsq 255 PICK.TEST 127.555.0.3
+echo $?
+
+echo '--- pickdns answers MX'
+dnsmx PICK.TEST
+echo $?
+
+echo '--- pickdns rejects queries for unknown information'
+dnsq 255 PICK11.TEST 127.555.0.3
+echo $?
+dnsq Txt PICK2.TEST 127.555.0.3
+echo $?
+
+echo '--- axfrdns rejects unauthorized transfer attempts'
+tcpclient -RHl0 127.43.0.2 53 axfr-get TEST3 rts-tmp/zone rts-tmp/zone.tmp
+echo $?
+tcpclient -RHl0 127.43.0.2 53 axfr-get TEST4 rts-tmp/zone2 rts-tmp/zone2.tmp
+echo $?
+
+echo '--- axfrdns works'
+tcpclient -RHl0 127.43.0.2 53 axfr-get TEST rts-tmp/zone rts-tmp/zone.tmp
+echo $?
+cat rts-tmp/zone
+
+echo '--- axfrdns handles differentiation'
+tcpclient -RHl0 -i 127.43.0.2 127.43.0.2 53 axfr-get TEST5 rts-tmp/zone5 rts-tmp/zone5.tmp
+echo $?
+cat rts-tmp/zone5
+rm rts-tmp/zone5
+tcpclient -RHl0 -i 127.43.0.3 127.43.0.2 53 axfr-get TEST5 rts-tmp/zone5 rts-tmp/zone5.tmp
+echo $?
+cat rts-tmp/zone5
+rm rts-tmp/zone5
+tcpclient -RHl0 -i 127.43.0.4 127.43.0.2 53 axfr-get TEST5 rts-tmp/zone5 rts-tmp/zone5.tmp
+echo $?
+cat rts-tmp/zone5
+rm rts-tmp/zone5
+tcpclient -RHl0 -i 127.43.0.5 127.43.0.2 53 axfr-get TEST5 rts-tmp/zone5 rts-tmp/zone5.tmp
+echo $?
+cat rts-tmp/zone5
+
+echo '--- axfrdns gives authoritative answers'
+dnsq any Test4 127.43.0.2
+echo $?
+
+echo '--- axfrdns handles size-1000 TXT records'
+dnsq any BIG.TEST 127.43.0.2
+echo $?
+
+echo '--- axfr-get handles zones with wildcards'
+tcpclient -RHl0 127.43.0.2 53 axfr-get TEST2 rts-tmp/zone2 rts-tmp/zone2.tmp
+echo $?
+cat rts-tmp/zone2
+
+
+svc -dx $service/dnscache
+svc -dx $service/tinydns
+svc -dx $service/pickdns
+svc -dx $service/walldns
+svc -dx $service/rbldns
+svc -dx $service/axfrdns
+
+svc -dx $service/dnscache/log
+svc -dx $service/tinydns/log
+svc -dx $service/pickdns/log
+svc -dx $service/walldns/log
+svc -dx $service/rbldns/log
+svc -dx $service/axfrdns/log
+
+wait
+wait
+wait
+wait
+wait
+wait
+
+exit 0
diff --git a/scan.h b/scan.h
new file mode 100644
index 0000000..fd383ee
--- /dev/null
+++ b/scan.h
@@ -0,0 +1,28 @@
+#ifndef SCAN_H
+#define SCAN_H
+
+extern unsigned int scan_uint(const char *,unsigned int *);
+extern unsigned int scan_xint(const char *,unsigned int *);
+extern unsigned int scan_nbbint(const char *,unsigned int,unsigned int,unsigned int,unsigned int *);
+extern unsigned int scan_ushort(const char *,unsigned short *);
+extern unsigned int scan_xshort(const char *,unsigned short *);
+extern unsigned int scan_nbbshort(const char *,unsigned int,unsigned int,unsigned int,unsigned short *);
+extern unsigned int scan_ulong(const char *,unsigned long *);
+extern unsigned int scan_xlong(const char *,unsigned long *);
+extern unsigned int scan_nbblong(const char *,unsigned int,unsigned int,unsigned int,unsigned long *);
+
+extern unsigned int scan_plusminus(const char *,int *);
+extern unsigned int scan_0x(const char *,unsigned int *);
+
+extern unsigned int scan_whitenskip(const char *,unsigned int);
+extern unsigned int scan_nonwhitenskip(const char *,unsigned int);
+extern unsigned int scan_charsetnskip(const char *,const char *,unsigned int);
+extern unsigned int scan_noncharsetnskip(const char *,const char *,unsigned int);
+
+extern unsigned int scan_strncmp(const char *,const char *,unsigned int);
+extern unsigned int scan_memcmp(const char *,const char *,unsigned int);
+
+extern unsigned int scan_long(const char *,long *);
+extern unsigned int scan_8long(const char *,unsigned long *);
+
+#endif
diff --git a/scan_ulong.c b/scan_ulong.c
new file mode 100644
index 0000000..d70b334
--- /dev/null
+++ b/scan_ulong.c
@@ -0,0 +1,14 @@
+#include "scan.h"
+
+unsigned int scan_ulong(register const char *s,register unsigned long *u)
+{
+ register unsigned int pos = 0;
+ register unsigned long result = 0;
+ register unsigned long c;
+ while ((c = (unsigned long) (unsigned char) (s[pos] - '0')) < 10) {
+ result = result * 10 + c;
+ ++pos;
+ }
+ *u = result;
+ return pos;
+}
diff --git a/seek.h b/seek.h
new file mode 100644
index 0000000..06aad97
--- /dev/null
+++ b/seek.h
@@ -0,0 +1,15 @@
+#ifndef SEEK_H
+#define SEEK_H
+
+typedef unsigned long seek_pos;
+
+extern seek_pos seek_cur(int);
+
+extern int seek_set(int,seek_pos);
+extern int seek_end(int);
+
+extern int seek_trunc(int,seek_pos);
+
+#define seek_begin(fd) (seek_set((fd),(seek_pos) 0))
+
+#endif
diff --git a/seek_set.c b/seek_set.c
new file mode 100644
index 0000000..d08d4f3
--- /dev/null
+++ b/seek_set.c
@@ -0,0 +1,7 @@
+#include <sys/types.h>
+#include "seek.h"
+
+#define SET 0 /* sigh */
+
+int seek_set(int fd,seek_pos pos)
+{ if (lseek(fd,(off_t) pos,SET) == -1) return -1; return 0; }
diff --git a/select.h1 b/select.h1
new file mode 100644
index 0000000..fe725b6
--- /dev/null
+++ b/select.h1
@@ -0,0 +1,10 @@
+#ifndef SELECT_H
+#define SELECT_H
+
+/* sysdep: -sysselect */
+
+#include <sys/types.h>
+#include <sys/time.h>
+extern int select();
+
+#endif
diff --git a/select.h2 b/select.h2
new file mode 100644
index 0000000..2bc2044
--- /dev/null
+++ b/select.h2
@@ -0,0 +1,11 @@
+#ifndef SELECT_H
+#define SELECT_H
+
+/* sysdep: +sysselect */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/select.h>
+extern int select();
+
+#endif
diff --git a/server.c b/server.c
new file mode 100644
index 0000000..e486fe1
--- /dev/null
+++ b/server.c
@@ -0,0 +1,116 @@
+#include "byte.h"
+#include "case.h"
+#include "env.h"
+#include "buffer.h"
+#include "strerr.h"
+#include "ip4.h"
+#include "uint16.h"
+#include "ndelay.h"
+#include "socket.h"
+#include "droproot.h"
+#include "qlog.h"
+#include "response.h"
+#include "dns.h"
+
+extern char *fatal;
+extern char *starting;
+extern int respond(char *,char *,char *);
+extern void initialize(void);
+
+static char ip[4];
+static uint16 port;
+
+static char buf[513];
+static int len;
+
+static char *q;
+
+static int doit(void)
+{
+ unsigned int pos;
+ char header[12];
+ char qtype[2];
+ char qclass[2];
+
+ if (len >= sizeof buf) goto NOQ;
+ pos = dns_packet_copy(buf,len,0,header,12); if (!pos) goto NOQ;
+ if (header[2] & 128) goto NOQ;
+ if (header[4]) goto NOQ;
+ if (header[5] != 1) goto NOQ;
+
+ pos = dns_packet_getname(buf,len,pos,&q); if (!pos) goto NOQ;
+ pos = dns_packet_copy(buf,len,pos,qtype,2); if (!pos) goto NOQ;
+ pos = dns_packet_copy(buf,len,pos,qclass,2); if (!pos) goto NOQ;
+
+ if (!response_query(q,qtype,qclass)) goto NOQ;
+ response_id(header);
+ if (byte_equal(qclass,2,DNS_C_IN))
+ response[2] |= 4;
+ else
+ if (byte_diff(qclass,2,DNS_C_ANY)) goto WEIRDCLASS;
+ response[3] &= ~128;
+ if (!(header[2] & 1)) response[2] &= ~1;
+
+ if (header[2] & 126) goto NOTIMP;
+ if (byte_equal(qtype,2,DNS_T_AXFR)) goto NOTIMP;
+
+ case_lowerb(q,dns_domain_length(q));
+ if (!respond(q,qtype,ip)) {
+ qlog(ip,port,header,q,qtype," - ");
+ return 0;
+ }
+ qlog(ip,port,header,q,qtype," + ");
+ return 1;
+
+ NOTIMP:
+ response[3] &= ~15;
+ response[3] |= 4;
+ qlog(ip,port,header,q,qtype," I ");
+ return 1;
+
+ WEIRDCLASS:
+ response[3] &= ~15;
+ response[3] |= 1;
+ qlog(ip,port,header,q,qtype," C ");
+ return 1;
+
+ NOQ:
+ qlog(ip,port,"\0\0","","\0\0"," / ");
+ return 0;
+}
+
+int main()
+{
+ char *x;
+ int udp53;
+
+ x = env_get("IP");
+ if (!x)
+ strerr_die2x(111,fatal,"$IP not set");
+ if (!ip4_scan(x,ip))
+ strerr_die3x(111,fatal,"unable to parse IP address ",x);
+
+ udp53 = socket_udp();
+ if (udp53 == -1)
+ strerr_die2sys(111,fatal,"unable to create UDP socket: ");
+ if (socket_bind4_reuse(udp53,ip,53) == -1)
+ strerr_die2sys(111,fatal,"unable to bind UDP socket: ");
+
+ droproot(fatal);
+
+ initialize();
+
+ ndelay_off(udp53);
+ socket_tryreservein(udp53,65536);
+
+ buffer_putsflush(buffer_2,starting);
+
+ for (;;) {
+ len = socket_recv4(udp53,buf,sizeof buf,ip,&port);
+ if (len < 0) continue;
+ if (!doit()) continue;
+ if (response_len > 512) response_tc();
+ socket_send4(udp53,response,response_len,ip,port);
+ /* may block for buffer space; if it fails, too bad */
+ }
+}
diff --git a/sgetopt.c b/sgetopt.c
new file mode 100644
index 0000000..e02d92d
--- /dev/null
+++ b/sgetopt.c
@@ -0,0 +1,51 @@
+/* sgetopt.c, sgetopt.h: (yet another) improved getopt clone, outer layer
+D. J. Bernstein, djb@pobox.com.
+Depends on subgetopt.h, buffer.h.
+No system requirements.
+19991219: Switched to buffer.h.
+19970208: Cleanups.
+931201: Baseline.
+No known patent problems.
+
+Documentation in sgetopt.3.
+*/
+
+#include "buffer.h"
+#define SGETOPTNOSHORT
+#include "sgetopt.h"
+#define SUBGETOPTNOSHORT
+#include "subgetopt.h"
+
+#define getopt sgetoptmine
+#define optind subgetoptind
+#define opterr sgetopterr
+#define optproblem subgetoptproblem
+#define optprogname sgetoptprogname
+
+int opterr = 1;
+const char *optprogname = 0;
+
+int getopt(int argc,char **argv,const char *opts)
+{
+ int c;
+ const char *s;
+
+ if (!optprogname) {
+ optprogname = *argv;
+ if (!optprogname) optprogname = "";
+ for (s = optprogname;*s;++s) if (*s == '/') optprogname = s + 1;
+ }
+ c = subgetopt(argc,argv,opts);
+ if (opterr)
+ if (c == '?') {
+ char chp[2]; chp[0] = optproblem; chp[1] = '\n';
+ buffer_puts(buffer_2,optprogname);
+ if (argv[optind] && (optind < argc))
+ buffer_puts(buffer_2,": illegal option -- ");
+ else
+ buffer_puts(buffer_2,": option requires an argument -- ");
+ buffer_put(buffer_2,chp,2);
+ buffer_flush(buffer_2);
+ }
+ return c;
+}
diff --git a/sgetopt.h b/sgetopt.h
new file mode 100644
index 0000000..234a13b
--- /dev/null
+++ b/sgetopt.h
@@ -0,0 +1,21 @@
+#ifndef SGETOPT_H
+#define SGETOPT_H
+
+#ifndef SGETOPTNOSHORT
+#define getopt sgetoptmine
+#define optarg subgetoptarg
+#define optind subgetoptind
+#define optpos subgetoptpos
+#define opterr sgetopterr
+#define optproblem subgetoptproblem
+#define optprogname sgetoptprogname
+#define opteof subgetoptdone
+#endif
+
+#include "subgetopt.h"
+
+extern int sgetoptmine(int,char **,const char *);
+extern int sgetopterr;
+extern const char *sgetoptprogname;
+
+#endif
diff --git a/socket.h b/socket.h
new file mode 100644
index 0000000..95e2a7c
--- /dev/null
+++ b/socket.h
@@ -0,0 +1,22 @@
+#ifndef SOCKET_H
+#define SOCKET_H
+
+#include "uint16.h"
+
+extern int socket_tcp(void);
+extern int socket_udp(void);
+
+extern int socket_connect4(int,const char *,uint16);
+extern int socket_connected(int);
+extern int socket_bind4(int,char *,uint16);
+extern int socket_bind4_reuse(int,char *,uint16);
+extern int socket_listen(int,int);
+extern int socket_accept4(int,char *,uint16 *);
+extern int socket_recv4(int,char *,int,char *,uint16 *);
+extern int socket_send4(int,const char *,int,const char *,uint16);
+extern int socket_local4(int,char *,uint16 *);
+extern int socket_remote4(int,char *,uint16 *);
+
+extern void socket_tryreservein(int,int);
+
+#endif
diff --git a/socket_accept.c b/socket_accept.c
new file mode 100644
index 0000000..22c44d4
--- /dev/null
+++ b/socket_accept.c
@@ -0,0 +1,21 @@
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include "byte.h"
+#include "socket.h"
+
+int socket_accept4(int s,char ip[4],uint16 *port)
+{
+ struct sockaddr_in sa;
+ int dummy = sizeof sa;
+ int fd;
+
+ fd = accept(s,(struct sockaddr *) &sa,&dummy);
+ if (fd == -1) return -1;
+
+ byte_copy(ip,4,(char *) &sa.sin_addr);
+ uint16_unpack_big((char *) &sa.sin_port,port);
+
+ return fd;
+}
diff --git a/socket_bind.c b/socket_bind.c
new file mode 100644
index 0000000..20830a4
--- /dev/null
+++ b/socket_bind.c
@@ -0,0 +1,33 @@
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include "byte.h"
+#include "socket.h"
+
+int socket_bind4(int s,char ip[4],uint16 port)
+{
+ struct sockaddr_in sa;
+
+ byte_zero(&sa,sizeof sa);
+ sa.sin_family = AF_INET;
+ uint16_pack_big((char *) &sa.sin_port,port);
+ byte_copy((char *) &sa.sin_addr,4,ip);
+
+ return bind(s,(struct sockaddr *) &sa,sizeof sa);
+}
+
+int socket_bind4_reuse(int s,char ip[4],uint16 port)
+{
+ int opt = 1;
+ setsockopt(s,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof opt);
+ return socket_bind4(s,ip,port);
+}
+
+void socket_tryreservein(int s,int size)
+{
+ while (size >= 1024) {
+ if (setsockopt(s,SOL_SOCKET,SO_RCVBUF,&size,sizeof size) == 0) return;
+ size -= (size >> 5);
+ }
+}
diff --git a/socket_conn.c b/socket_conn.c
new file mode 100644
index 0000000..46423cb
--- /dev/null
+++ b/socket_conn.c
@@ -0,0 +1,33 @@
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <unistd.h>
+#include "byte.h"
+#include "socket.h"
+
+int socket_connect4(int s,const char ip[4],uint16 port)
+{
+ struct sockaddr_in sa;
+
+ byte_zero(&sa,sizeof sa);
+ sa.sin_family = AF_INET;
+ uint16_pack_big((char *) &sa.sin_port,port);
+ byte_copy((char *) &sa.sin_addr,4,ip);
+
+ return connect(s,(struct sockaddr *) &sa,sizeof sa);
+}
+
+int socket_connected(int s)
+{
+ struct sockaddr_in sa;
+ int dummy;
+ char ch;
+
+ dummy = sizeof sa;
+ if (getpeername(s,(struct sockaddr *) &sa,&dummy) == -1) {
+ read(s,&ch,1); /* sets errno */
+ return 0;
+ }
+ return 1;
+}
diff --git a/socket_listen.c b/socket_listen.c
new file mode 100644
index 0000000..abdb483
--- /dev/null
+++ b/socket_listen.c
@@ -0,0 +1,10 @@
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include "socket.h"
+
+int socket_listen(int s,int backlog)
+{
+ return listen(s,backlog);
+}
diff --git a/socket_recv.c b/socket_recv.c
new file mode 100644
index 0000000..8bc59c5
--- /dev/null
+++ b/socket_recv.c
@@ -0,0 +1,21 @@
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include "byte.h"
+#include "socket.h"
+
+int socket_recv4(int s,char *buf,int len,char ip[4],uint16 *port)
+{
+ struct sockaddr_in sa;
+ int dummy = sizeof sa;
+ int r;
+
+ r = recvfrom(s,buf,len,0,(struct sockaddr *) &sa,&dummy);
+ if (r == -1) return -1;
+
+ byte_copy(ip,4,(char *) &sa.sin_addr);
+ uint16_unpack_big((char *) &sa.sin_port,port);
+
+ return r;
+}
diff --git a/socket_send.c b/socket_send.c
new file mode 100644
index 0000000..9ffbd5a
--- /dev/null
+++ b/socket_send.c
@@ -0,0 +1,18 @@
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include "byte.h"
+#include "socket.h"
+
+int socket_send4(int s,const char *buf,int len,const char ip[4],uint16 port)
+{
+ struct sockaddr_in sa;
+
+ byte_zero(&sa,sizeof sa);
+ sa.sin_family = AF_INET;
+ uint16_pack_big((char *) &sa.sin_port,port);
+ byte_copy((char *) &sa.sin_addr,4,ip);
+
+ return sendto(s,buf,len,0,(struct sockaddr *) &sa,sizeof sa);
+}
diff --git a/socket_tcp.c b/socket_tcp.c
new file mode 100644
index 0000000..c200e2b
--- /dev/null
+++ b/socket_tcp.c
@@ -0,0 +1,17 @@
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <unistd.h>
+#include "ndelay.h"
+#include "socket.h"
+
+int socket_tcp(void)
+{
+ int s;
+
+ s = socket(AF_INET,SOCK_STREAM,0);
+ if (s == -1) return -1;
+ if (ndelay_on(s) == -1) { close(s); return -1; }
+ return s;
+}
diff --git a/socket_udp.c b/socket_udp.c
new file mode 100644
index 0000000..d71d3e4
--- /dev/null
+++ b/socket_udp.c
@@ -0,0 +1,17 @@
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <unistd.h>
+#include "ndelay.h"
+#include "socket.h"
+
+int socket_udp(void)
+{
+ int s;
+
+ s = socket(AF_INET,SOCK_DGRAM,0);
+ if (s == -1) return -1;
+ if (ndelay_on(s) == -1) { close(s); return -1; }
+ return s;
+}
diff --git a/str.h b/str.h
new file mode 100644
index 0000000..a2a4b75
--- /dev/null
+++ b/str.h
@@ -0,0 +1,14 @@
+#ifndef STR_H
+#define STR_H
+
+extern unsigned int str_copy(char *,const char *);
+extern int str_diff(const char *,const char *);
+extern int str_diffn(const char *,const char *,unsigned int);
+extern unsigned int str_len(const char *);
+extern unsigned int str_chr(const char *,int);
+extern unsigned int str_rchr(const char *,int);
+extern int str_start(const char *,const char *);
+
+#define str_equal(s,t) (!str_diff((s),(t)))
+
+#endif
diff --git a/str_chr.c b/str_chr.c
new file mode 100644
index 0000000..042dfa2
--- /dev/null
+++ b/str_chr.c
@@ -0,0 +1,17 @@
+#include "str.h"
+
+unsigned int str_chr(register const char *s,int c)
+{
+ register char ch;
+ register const char *t;
+
+ ch = c;
+ t = s;
+ for (;;) {
+ if (!*t) break; if (*t == ch) break; ++t;
+ if (!*t) break; if (*t == ch) break; ++t;
+ if (!*t) break; if (*t == ch) break; ++t;
+ if (!*t) break; if (*t == ch) break; ++t;
+ }
+ return t - s;
+}
diff --git a/str_diff.c b/str_diff.c
new file mode 100644
index 0000000..071e7f5
--- /dev/null
+++ b/str_diff.c
@@ -0,0 +1,15 @@
+#include "str.h"
+
+int str_diff(register const char *s,register const char *t)
+{
+ register char x;
+
+ for (;;) {
+ x = *s; if (x != *t) break; if (!x) break; ++s; ++t;
+ x = *s; if (x != *t) break; if (!x) break; ++s; ++t;
+ x = *s; if (x != *t) break; if (!x) break; ++s; ++t;
+ x = *s; if (x != *t) break; if (!x) break; ++s; ++t;
+ }
+ return ((int)(unsigned int)(unsigned char) x)
+ - ((int)(unsigned int)(unsigned char) *t);
+}
diff --git a/str_len.c b/str_len.c
new file mode 100644
index 0000000..8411ebf
--- /dev/null
+++ b/str_len.c
@@ -0,0 +1,14 @@
+#include "str.h"
+
+unsigned int str_len(const char *s)
+{
+ register const char *t;
+
+ t = s;
+ for (;;) {
+ if (!*t) return t - s; ++t;
+ if (!*t) return t - s; ++t;
+ if (!*t) return t - s; ++t;
+ if (!*t) return t - s; ++t;
+ }
+}
diff --git a/str_rchr.c b/str_rchr.c
new file mode 100644
index 0000000..b128c4c
--- /dev/null
+++ b/str_rchr.c
@@ -0,0 +1,20 @@
+#include "str.h"
+
+unsigned int str_rchr(register const char *s,int c)
+{
+ register char ch;
+ register const char *t;
+ register const char *u;
+
+ ch = c;
+ t = s;
+ u = 0;
+ for (;;) {
+ if (!*t) break; if (*t == ch) u = t; ++t;
+ if (!*t) break; if (*t == ch) u = t; ++t;
+ if (!*t) break; if (*t == ch) u = t; ++t;
+ if (!*t) break; if (*t == ch) u = t; ++t;
+ }
+ if (!u) u = t;
+ return u - s;
+}
diff --git a/str_start.c b/str_start.c
new file mode 100644
index 0000000..757189d
--- /dev/null
+++ b/str_start.c
@@ -0,0 +1,13 @@
+#include "str.h"
+
+int str_start(register const char *s,register const char *t)
+{
+ register char x;
+
+ for (;;) {
+ x = *t++; if (!x) return 1; if (x != *s++) return 0;
+ x = *t++; if (!x) return 1; if (x != *s++) return 0;
+ x = *t++; if (!x) return 1; if (x != *s++) return 0;
+ x = *t++; if (!x) return 1; if (x != *s++) return 0;
+ }
+}
diff --git a/stralloc.h b/stralloc.h
new file mode 100644
index 0000000..d88f631
--- /dev/null
+++ b/stralloc.h
@@ -0,0 +1,29 @@
+#ifndef STRALLOC_H
+#define STRALLOC_H
+
+#include "gen_alloc.h"
+
+GEN_ALLOC_typedef(stralloc,char,s,len,a)
+
+extern int stralloc_ready(stralloc *,unsigned int);
+extern int stralloc_readyplus(stralloc *,unsigned int);
+extern int stralloc_copy(stralloc *,const stralloc *);
+extern int stralloc_cat(stralloc *,const stralloc *);
+extern int stralloc_copys(stralloc *,const char *);
+extern int stralloc_cats(stralloc *,const char *);
+extern int stralloc_copyb(stralloc *,const char *,unsigned int);
+extern int stralloc_catb(stralloc *,const char *,unsigned int);
+extern int stralloc_append(stralloc *,const char *); /* beware: this takes a pointer to 1 char */
+extern int stralloc_starts(stralloc *,const char *);
+
+#define stralloc_0(sa) stralloc_append(sa,"")
+
+extern int stralloc_catulong0(stralloc *,unsigned long,unsigned int);
+extern int stralloc_catlong0(stralloc *,long,unsigned int);
+
+#define stralloc_catlong(sa,l) (stralloc_catlong0((sa),(l),0))
+#define stralloc_catuint0(sa,i,n) (stralloc_catulong0((sa),(i),(n)))
+#define stralloc_catint0(sa,i,n) (stralloc_catlong0((sa),(i),(n)))
+#define stralloc_catint(sa,i) (stralloc_catlong0((sa),(i),0))
+
+#endif
diff --git a/stralloc_cat.c b/stralloc_cat.c
new file mode 100644
index 0000000..9bbb119
--- /dev/null
+++ b/stralloc_cat.c
@@ -0,0 +1,7 @@
+#include "byte.h"
+#include "stralloc.h"
+
+int stralloc_cat(stralloc *sato,const stralloc *safrom)
+{
+ return stralloc_catb(sato,safrom->s,safrom->len);
+}
diff --git a/stralloc_catb.c b/stralloc_catb.c
new file mode 100644
index 0000000..b606e32
--- /dev/null
+++ b/stralloc_catb.c
@@ -0,0 +1,12 @@
+#include "stralloc.h"
+#include "byte.h"
+
+int stralloc_catb(stralloc *sa,const char *s,unsigned int n)
+{
+ if (!sa->s) return stralloc_copyb(sa,s,n);
+ if (!stralloc_readyplus(sa,n + 1)) return 0;
+ byte_copy(sa->s + sa->len,n,s);
+ sa->len += n;
+ sa->s[sa->len] = 'Z'; /* ``offensive programming'' */
+ return 1;
+}
diff --git a/stralloc_cats.c b/stralloc_cats.c
new file mode 100644
index 0000000..92cb66e
--- /dev/null
+++ b/stralloc_cats.c
@@ -0,0 +1,8 @@
+#include "byte.h"
+#include "str.h"
+#include "stralloc.h"
+
+int stralloc_cats(stralloc *sa,const char *s)
+{
+ return stralloc_catb(sa,s,str_len(s));
+}
diff --git a/stralloc_copy.c b/stralloc_copy.c
new file mode 100644
index 0000000..6b9ae42
--- /dev/null
+++ b/stralloc_copy.c
@@ -0,0 +1,7 @@
+#include "byte.h"
+#include "stralloc.h"
+
+int stralloc_copy(stralloc *sato,const stralloc *safrom)
+{
+ return stralloc_copyb(sato,safrom->s,safrom->len);
+}
diff --git a/stralloc_eady.c b/stralloc_eady.c
new file mode 100644
index 0000000..3a31f4b
--- /dev/null
+++ b/stralloc_eady.c
@@ -0,0 +1,6 @@
+#include "alloc.h"
+#include "stralloc.h"
+#include "gen_allocdefs.h"
+
+GEN_ALLOC_ready(stralloc,char,s,len,a,i,n,x,30,stralloc_ready)
+GEN_ALLOC_readyplus(stralloc,char,s,len,a,i,n,x,30,stralloc_readyplus)
diff --git a/stralloc_num.c b/stralloc_num.c
new file mode 100644
index 0000000..64b25fa
--- /dev/null
+++ b/stralloc_num.c
@@ -0,0 +1,29 @@
+#include "stralloc.h"
+
+int stralloc_catulong0(stralloc *sa,unsigned long u,unsigned int n)
+{
+ unsigned int len;
+ unsigned long q;
+ char *s;
+
+ len = 1;
+ q = u;
+ while (q > 9) { ++len; q /= 10; }
+ if (len < n) len = n;
+
+ if (!stralloc_readyplus(sa,len)) return 0;
+ s = sa->s + sa->len;
+ sa->len += len;
+ while (len) { s[--len] = '0' + (u % 10); u /= 10; }
+
+ return 1;
+}
+
+int stralloc_catlong0(stralloc *sa,long l,unsigned int n)
+{
+ if (l < 0) {
+ if (!stralloc_append(sa,"-")) return 0;
+ l = -l;
+ }
+ return stralloc_catulong0(sa,l,n);
+}
diff --git a/stralloc_opyb.c b/stralloc_opyb.c
new file mode 100644
index 0000000..593029d
--- /dev/null
+++ b/stralloc_opyb.c
@@ -0,0 +1,11 @@
+#include "stralloc.h"
+#include "byte.h"
+
+int stralloc_copyb(stralloc *sa,const char *s,unsigned int n)
+{
+ if (!stralloc_ready(sa,n + 1)) return 0;
+ byte_copy(sa->s,n,s);
+ sa->len = n;
+ sa->s[n] = 'Z'; /* ``offensive programming'' */
+ return 1;
+}
diff --git a/stralloc_opys.c b/stralloc_opys.c
new file mode 100644
index 0000000..860c7e0
--- /dev/null
+++ b/stralloc_opys.c
@@ -0,0 +1,8 @@
+#include "byte.h"
+#include "str.h"
+#include "stralloc.h"
+
+int stralloc_copys(stralloc *sa,const char *s)
+{
+ return stralloc_copyb(sa,s,str_len(s));
+}
diff --git a/stralloc_pend.c b/stralloc_pend.c
new file mode 100644
index 0000000..a3443b8
--- /dev/null
+++ b/stralloc_pend.c
@@ -0,0 +1,5 @@
+#include "alloc.h"
+#include "stralloc.h"
+#include "gen_allocdefs.h"
+
+GEN_ALLOC_append(stralloc,char,s,len,a,i,n,x,30,stralloc_readyplus,stralloc_append)
diff --git a/strerr.h b/strerr.h
new file mode 100644
index 0000000..6c4895a
--- /dev/null
+++ b/strerr.h
@@ -0,0 +1,78 @@
+#ifndef STRERR_H
+#define STRERR_H
+
+struct strerr {
+ struct strerr *who;
+ const char *x;
+ const char *y;
+ const char *z;
+} ;
+
+extern struct strerr strerr_sys;
+extern void strerr_sysinit(void);
+
+extern const char *strerr(const struct strerr *);
+extern void strerr_warn(const char *,const char *,const char *,const char *,const char *,const char *,const struct strerr *);
+extern void strerr_die(int,const char *,const char *,const char *,const char *,const char *,const char *,const struct strerr *);
+
+#define STRERR(r,se,a) \
+{ se.who = 0; se.x = a; se.y = 0; se.z = 0; return r; }
+
+#define STRERR_SYS(r,se,a) \
+{ se.who = &strerr_sys; se.x = a; se.y = 0; se.z = 0; return r; }
+#define STRERR_SYS3(r,se,a,b,c) \
+{ se.who = &strerr_sys; se.x = a; se.y = b; se.z = c; return r; }
+
+#define strerr_warn6(x1,x2,x3,x4,x5,x6,se) \
+strerr_warn((x1),(x2),(x3),(x4),(x5),(x6),(se))
+#define strerr_warn5(x1,x2,x3,x4,x5,se) \
+strerr_warn((x1),(x2),(x3),(x4),(x5),0,(se))
+#define strerr_warn4(x1,x2,x3,x4,se) \
+strerr_warn((x1),(x2),(x3),(x4),0,0,(se))
+#define strerr_warn3(x1,x2,x3,se) \
+strerr_warn((x1),(x2),(x3),0,0,0,(se))
+#define strerr_warn2(x1,x2,se) \
+strerr_warn((x1),(x2),0,0,0,0,(se))
+#define strerr_warn1(x1,se) \
+strerr_warn((x1),0,0,0,0,0,(se))
+
+#define strerr_die6(e,x1,x2,x3,x4,x5,x6,se) \
+strerr_die((e),(x1),(x2),(x3),(x4),(x5),(x6),(se))
+#define strerr_die5(e,x1,x2,x3,x4,x5,se) \
+strerr_die((e),(x1),(x2),(x3),(x4),(x5),0,(se))
+#define strerr_die4(e,x1,x2,x3,x4,se) \
+strerr_die((e),(x1),(x2),(x3),(x4),0,0,(se))
+#define strerr_die3(e,x1,x2,x3,se) \
+strerr_die((e),(x1),(x2),(x3),0,0,0,(se))
+#define strerr_die2(e,x1,x2,se) \
+strerr_die((e),(x1),(x2),0,0,0,0,(se))
+#define strerr_die1(e,x1,se) \
+strerr_die((e),(x1),0,0,0,0,0,(se))
+
+#define strerr_die6sys(e,x1,x2,x3,x4,x5,x6) \
+strerr_die((e),(x1),(x2),(x3),(x4),(x5),(x6),&strerr_sys)
+#define strerr_die5sys(e,x1,x2,x3,x4,x5) \
+strerr_die((e),(x1),(x2),(x3),(x4),(x5),0,&strerr_sys)
+#define strerr_die4sys(e,x1,x2,x3,x4) \
+strerr_die((e),(x1),(x2),(x3),(x4),0,0,&strerr_sys)
+#define strerr_die3sys(e,x1,x2,x3) \
+strerr_die((e),(x1),(x2),(x3),0,0,0,&strerr_sys)
+#define strerr_die2sys(e,x1,x2) \
+strerr_die((e),(x1),(x2),0,0,0,0,&strerr_sys)
+#define strerr_die1sys(e,x1) \
+strerr_die((e),(x1),0,0,0,0,0,&strerr_sys)
+
+#define strerr_die6x(e,x1,x2,x3,x4,x5,x6) \
+strerr_die((e),(x1),(x2),(x3),(x4),(x5),(x6),0)
+#define strerr_die5x(e,x1,x2,x3,x4,x5) \
+strerr_die((e),(x1),(x2),(x3),(x4),(x5),0,0)
+#define strerr_die4x(e,x1,x2,x3,x4) \
+strerr_die((e),(x1),(x2),(x3),(x4),0,0,0)
+#define strerr_die3x(e,x1,x2,x3) \
+strerr_die((e),(x1),(x2),(x3),0,0,0,0)
+#define strerr_die2x(e,x1,x2) \
+strerr_die((e),(x1),(x2),0,0,0,0,0)
+#define strerr_die1x(e,x1) \
+strerr_die((e),(x1),0,0,0,0,0,0)
+
+#endif
diff --git a/strerr_die.c b/strerr_die.c
new file mode 100644
index 0000000..ad93eb4
--- /dev/null
+++ b/strerr_die.c
@@ -0,0 +1,31 @@
+#include "buffer.h"
+#include "exit.h"
+#include "strerr.h"
+
+void strerr_warn(const char *x1,const char *x2,const char *x3,const char *x4,const char *x5,const char *x6,const struct strerr *se)
+{
+ strerr_sysinit();
+
+ if (x1) buffer_puts(buffer_2,x1);
+ if (x2) buffer_puts(buffer_2,x2);
+ if (x3) buffer_puts(buffer_2,x3);
+ if (x4) buffer_puts(buffer_2,x4);
+ if (x5) buffer_puts(buffer_2,x5);
+ if (x6) buffer_puts(buffer_2,x6);
+
+ while(se) {
+ if (se->x) buffer_puts(buffer_2,se->x);
+ if (se->y) buffer_puts(buffer_2,se->y);
+ if (se->z) buffer_puts(buffer_2,se->z);
+ se = se->who;
+ }
+
+ buffer_puts(buffer_2,"\n");
+ buffer_flush(buffer_2);
+}
+
+void strerr_die(int e,const char *x1,const char *x2,const char *x3,const char *x4,const char *x5,const char *x6,const struct strerr *se)
+{
+ strerr_warn(x1,x2,x3,x4,x5,x6,se);
+ _exit(e);
+}
diff --git a/strerr_sys.c b/strerr_sys.c
new file mode 100644
index 0000000..b484197
--- /dev/null
+++ b/strerr_sys.c
@@ -0,0 +1,12 @@
+#include "error.h"
+#include "strerr.h"
+
+struct strerr strerr_sys;
+
+void strerr_sysinit(void)
+{
+ strerr_sys.who = 0;
+ strerr_sys.x = error_str(errno);
+ strerr_sys.y = "";
+ strerr_sys.z = "";
+}
diff --git a/subgetopt.c b/subgetopt.c
new file mode 100644
index 0000000..96c2631
--- /dev/null
+++ b/subgetopt.c
@@ -0,0 +1,65 @@
+#define SUBGETOPTNOSHORT
+#include "subgetopt.h"
+
+#define sgopt subgetopt
+#define optind subgetoptind
+#define optpos subgetoptpos
+#define optarg subgetoptarg
+#define optproblem subgetoptproblem
+#define optdone subgetoptdone
+
+int optind = 1;
+int optpos = 0;
+char *optarg = 0;
+int optproblem = 0;
+int optdone = SUBGETOPTDONE;
+
+int sgopt(int argc,char **argv,const char *opts)
+{
+ int c;
+ const char *s;
+
+ optarg = 0;
+ if (!argv || (optind >= argc) || !argv[optind]) return optdone;
+ if (optpos && !argv[optind][optpos]) {
+ ++optind;
+ optpos = 0;
+ if ((optind >= argc) || !argv[optind]) return optdone;
+ }
+ if (!optpos) {
+ if (argv[optind][0] != '-') return optdone;
+ ++optpos;
+ c = argv[optind][1];
+ if ((c == '-') || (c == 0)) {
+ if (c) ++optind;
+ optpos = 0;
+ return optdone;
+ }
+ /* otherwise c is reassigned below */
+ }
+ c = argv[optind][optpos];
+ ++optpos;
+ s = opts;
+ while (*s) {
+ if (c == *s) {
+ if (s[1] == ':') {
+ optarg = argv[optind] + optpos;
+ ++optind;
+ optpos = 0;
+ if (!*optarg) {
+ optarg = argv[optind];
+ if ((optind >= argc) || !optarg) { /* argument past end */
+ optproblem = c;
+ return '?';
+ }
+ ++optind;
+ }
+ }
+ return c;
+ }
+ ++s;
+ if (*s == ':') ++s;
+ }
+ optproblem = c;
+ return '?';
+}
diff --git a/subgetopt.h b/subgetopt.h
new file mode 100644
index 0000000..65da0fb
--- /dev/null
+++ b/subgetopt.h
@@ -0,0 +1,24 @@
+#ifndef SUBGETOPT_H
+#define SUBGETOPT_H
+
+#ifndef SUBGETOPTNOSHORT
+#define sgopt subgetopt
+#define sgoptarg subgetoptarg
+#define sgoptind subgetoptind
+#define sgoptpos subgetoptpos
+#define sgoptproblem subgetoptproblem
+#define sgoptprogname subgetoptprogname
+#define sgoptdone subgetoptdone
+#endif
+
+#define SUBGETOPTDONE -1
+
+extern int subgetopt(int,char **,const char *);
+extern char *subgetoptarg;
+extern int subgetoptind;
+extern int subgetoptpos;
+extern int subgetoptproblem;
+extern char *subgetoptprogname;
+extern int subgetoptdone;
+
+#endif
diff --git a/tai.h b/tai.h
new file mode 100644
index 0000000..b8db5e5
--- /dev/null
+++ b/tai.h
@@ -0,0 +1,26 @@
+#ifndef TAI_H
+#define TAI_H
+
+#include "uint64.h"
+
+struct tai {
+ uint64 x;
+} ;
+
+#define tai_unix(t,u) ((void) ((t)->x = 4611686018427387914ULL + (uint64) (u)))
+
+extern void tai_now(struct tai *);
+
+#define tai_approx(t) ((double) ((t)->x))
+
+extern void tai_add(struct tai *,const struct tai *,const struct tai *);
+extern void tai_sub(struct tai *,const struct tai *,const struct tai *);
+#define tai_less(t,u) ((t)->x < (u)->x)
+
+#define TAI_PACK 8
+extern void tai_pack(char *,const struct tai *);
+extern void tai_unpack(const char *,struct tai *);
+
+extern void tai_uint(struct tai *,unsigned int);
+
+#endif
diff --git a/tai_add.c b/tai_add.c
new file mode 100644
index 0000000..4226ab4
--- /dev/null
+++ b/tai_add.c
@@ -0,0 +1,6 @@
+#include "tai.h"
+
+void tai_add(struct tai *t,const struct tai *u,const struct tai *v)
+{
+ t->x = u->x + v->x;
+}
diff --git a/tai_now.c b/tai_now.c
new file mode 100644
index 0000000..91e84da
--- /dev/null
+++ b/tai_now.c
@@ -0,0 +1,7 @@
+#include <time.h>
+#include "tai.h"
+
+void tai_now(struct tai *t)
+{
+ tai_unix(t,time((time_t *) 0));
+}
diff --git a/tai_pack.c b/tai_pack.c
new file mode 100644
index 0000000..0a2bc06
--- /dev/null
+++ b/tai_pack.c
@@ -0,0 +1,16 @@
+#include "tai.h"
+
+void tai_pack(char *s,const struct tai *t)
+{
+ uint64 x;
+
+ x = t->x;
+ s[7] = x & 255; x >>= 8;
+ s[6] = x & 255; x >>= 8;
+ s[5] = x & 255; x >>= 8;
+ s[4] = x & 255; x >>= 8;
+ s[3] = x & 255; x >>= 8;
+ s[2] = x & 255; x >>= 8;
+ s[1] = x & 255; x >>= 8;
+ s[0] = x;
+}
diff --git a/tai_sub.c b/tai_sub.c
new file mode 100644
index 0000000..6ebf7b2
--- /dev/null
+++ b/tai_sub.c
@@ -0,0 +1,6 @@
+#include "tai.h"
+
+void tai_sub(struct tai *t,const struct tai *u,const struct tai *v)
+{
+ t->x = u->x - v->x;
+}
diff --git a/tai_uint.c b/tai_uint.c
new file mode 100644
index 0000000..b01184c
--- /dev/null
+++ b/tai_uint.c
@@ -0,0 +1,6 @@
+#include "tai.h"
+
+void tai_uint(struct tai *t,unsigned int u)
+{
+ t->x = u;
+}
diff --git a/tai_unpack.c b/tai_unpack.c
new file mode 100644
index 0000000..b725ae0
--- /dev/null
+++ b/tai_unpack.c
@@ -0,0 +1,16 @@
+#include "tai.h"
+
+void tai_unpack(const char *s,struct tai *t)
+{
+ uint64 x;
+
+ x = (unsigned char) s[0];
+ x <<= 8; x += (unsigned char) s[1];
+ x <<= 8; x += (unsigned char) s[2];
+ x <<= 8; x += (unsigned char) s[3];
+ x <<= 8; x += (unsigned char) s[4];
+ x <<= 8; x += (unsigned char) s[5];
+ x <<= 8; x += (unsigned char) s[6];
+ x <<= 8; x += (unsigned char) s[7];
+ t->x = x;
+}
diff --git a/taia.h b/taia.h
new file mode 100644
index 0000000..4d37ef2
--- /dev/null
+++ b/taia.h
@@ -0,0 +1,34 @@
+#ifndef TAIA_H
+#define TAIA_H
+
+#include "tai.h"
+
+struct taia {
+ struct tai sec;
+ unsigned long nano; /* 0...999999999 */
+ unsigned long atto; /* 0...999999999 */
+} ;
+
+extern void taia_tai(const struct taia *,struct tai *);
+
+extern void taia_now(struct taia *);
+
+extern double taia_approx(const struct taia *);
+extern double taia_frac(const struct taia *);
+
+extern void taia_add(struct taia *,const struct taia *,const struct taia *);
+extern void taia_addsec(struct taia *,const struct taia *,int);
+extern void taia_sub(struct taia *,const struct taia *,const struct taia *);
+extern void taia_half(struct taia *,const struct taia *);
+extern int taia_less(const struct taia *,const struct taia *);
+
+#define TAIA_PACK 16
+extern void taia_pack(char *,const struct taia *);
+extern void taia_unpack(const char *,struct taia *);
+
+#define TAIA_FMTFRAC 19
+extern unsigned int taia_fmtfrac(char *,const struct taia *);
+
+extern void taia_uint(struct taia *,unsigned int);
+
+#endif
diff --git a/taia_add.c b/taia_add.c
new file mode 100644
index 0000000..3044a26
--- /dev/null
+++ b/taia_add.c
@@ -0,0 +1,18 @@
+#include "taia.h"
+
+/* XXX: breaks tai encapsulation */
+
+void taia_add(struct taia *t,const struct taia *u,const struct taia *v)
+{
+ t->sec.x = u->sec.x + v->sec.x;
+ t->nano = u->nano + v->nano;
+ t->atto = u->atto + v->atto;
+ if (t->atto > 999999999UL) {
+ t->atto -= 1000000000UL;
+ ++t->nano;
+ }
+ if (t->nano > 999999999UL) {
+ t->nano -= 1000000000UL;
+ ++t->sec.x;
+ }
+}
diff --git a/taia_approx.c b/taia_approx.c
new file mode 100644
index 0000000..2a3b429
--- /dev/null
+++ b/taia_approx.c
@@ -0,0 +1,6 @@
+#include "taia.h"
+
+double taia_approx(const struct taia *t)
+{
+ return tai_approx(&t->sec) + taia_frac(t);
+}
diff --git a/taia_frac.c b/taia_frac.c
new file mode 100644
index 0000000..b6b48bc
--- /dev/null
+++ b/taia_frac.c
@@ -0,0 +1,6 @@
+#include "taia.h"
+
+double taia_frac(const struct taia *t)
+{
+ return (t->atto * 0.000000001 + t->nano) * 0.000000001;
+}
diff --git a/taia_less.c b/taia_less.c
new file mode 100644
index 0000000..2d889c8
--- /dev/null
+++ b/taia_less.c
@@ -0,0 +1,12 @@
+#include "taia.h"
+
+/* XXX: breaks tai encapsulation */
+
+int taia_less(const struct taia *t,const struct taia *u)
+{
+ if (t->sec.x < u->sec.x) return 1;
+ if (t->sec.x > u->sec.x) return 0;
+ if (t->nano < u->nano) return 1;
+ if (t->nano > u->nano) return 0;
+ return t->atto < u->atto;
+}
diff --git a/taia_now.c b/taia_now.c
new file mode 100644
index 0000000..ccc260d
--- /dev/null
+++ b/taia_now.c
@@ -0,0 +1,12 @@
+#include <sys/types.h>
+#include <sys/time.h>
+#include "taia.h"
+
+void taia_now(struct taia *t)
+{
+ struct timeval now;
+ gettimeofday(&now,(struct timezone *) 0);
+ tai_unix(&t->sec,now.tv_sec);
+ t->nano = 1000 * now.tv_usec + 500;
+ t->atto = 0;
+}
diff --git a/taia_pack.c b/taia_pack.c
new file mode 100644
index 0000000..89e2c16
--- /dev/null
+++ b/taia_pack.c
@@ -0,0 +1,20 @@
+#include "taia.h"
+
+void taia_pack(char *s,const struct taia *t)
+{
+ unsigned long x;
+
+ tai_pack(s,&t->sec);
+ s += 8;
+
+ x = t->atto;
+ s[7] = x & 255; x >>= 8;
+ s[6] = x & 255; x >>= 8;
+ s[5] = x & 255; x >>= 8;
+ s[4] = x;
+ x = t->nano;
+ s[3] = x & 255; x >>= 8;
+ s[2] = x & 255; x >>= 8;
+ s[1] = x & 255; x >>= 8;
+ s[0] = x;
+}
diff --git a/taia_sub.c b/taia_sub.c
new file mode 100644
index 0000000..6944689
--- /dev/null
+++ b/taia_sub.c
@@ -0,0 +1,21 @@
+#include "taia.h"
+
+/* XXX: breaks tai encapsulation */
+
+void taia_sub(struct taia *t,const struct taia *u,const struct taia *v)
+{
+ unsigned long unano = u->nano;
+ unsigned long uatto = u->atto;
+
+ t->sec.x = u->sec.x - v->sec.x;
+ t->nano = unano - v->nano;
+ t->atto = uatto - v->atto;
+ if (t->atto > uatto) {
+ t->atto += 1000000000UL;
+ --t->nano;
+ }
+ if (t->nano > unano) {
+ t->nano += 1000000000UL;
+ --t->sec.x;
+ }
+}
diff --git a/taia_tai.c b/taia_tai.c
new file mode 100644
index 0000000..ef4d4fc
--- /dev/null
+++ b/taia_tai.c
@@ -0,0 +1,6 @@
+#include "taia.h"
+
+void taia_tai(const struct taia *ta,struct tai *t)
+{
+ *t = ta->sec;
+}
diff --git a/taia_uint.c b/taia_uint.c
new file mode 100644
index 0000000..167936c
--- /dev/null
+++ b/taia_uint.c
@@ -0,0 +1,10 @@
+#include "taia.h"
+
+/* XXX: breaks tai encapsulation */
+
+void taia_uint(struct taia *t,unsigned int s)
+{
+ t->sec.x = s;
+ t->nano = 0;
+ t->atto = 0;
+}
diff --git a/tdlookup.c b/tdlookup.c
new file mode 100644
index 0000000..da7420d
--- /dev/null
+++ b/tdlookup.c
@@ -0,0 +1,310 @@
+#include <unistd.h>
+#include "uint16.h"
+#include "open.h"
+#include "tai.h"
+#include "cdb.h"
+#include "byte.h"
+#include "case.h"
+#include "dns.h"
+#include "seek.h"
+#include "response.h"
+
+static int want(const char *owner,const char type[2])
+{
+ unsigned int pos;
+ static char *d;
+ char x[10];
+ uint16 datalen;
+
+ pos = dns_packet_skipname(response,response_len,12); if (!pos) return 0;
+ pos += 4;
+
+ while (pos < response_len) {
+ pos = dns_packet_getname(response,response_len,pos,&d); if (!pos) return 0;
+ pos = dns_packet_copy(response,response_len,pos,x,10); if (!pos) return 0;
+ if (dns_domain_equal(d,owner))
+ if (byte_equal(type,2,x))
+ return 0;
+ uint16_unpack_big(x + 8,&datalen);
+ pos += datalen;
+ }
+ return 1;
+}
+
+static char *d1;
+
+static char clientloc[2];
+static struct tai now;
+static struct cdb c;
+
+static char data[32767];
+static uint32 dlen;
+static unsigned int dpos;
+static char type[2];
+static uint32 ttl;
+
+static int find(char *d,int flagwild)
+{
+ int r;
+ char ch;
+ struct tai cutoff;
+ char ttd[8];
+ char ttlstr[4];
+ char recordloc[2];
+ double newttl;
+
+ for (;;) {
+ r = cdb_findnext(&c,d,dns_domain_length(d));
+ if (r <= 0) return r;
+ dlen = cdb_datalen(&c);
+ if (dlen > sizeof data) return -1;
+ if (cdb_read(&c,data,dlen,cdb_datapos(&c)) == -1) return -1;
+ dpos = dns_packet_copy(data,dlen,0,type,2); if (!dpos) return -1;
+ dpos = dns_packet_copy(data,dlen,dpos,&ch,1); if (!dpos) return -1;
+ if ((ch == '=' + 1) || (ch == '*' + 1)) {
+ --ch;
+ dpos = dns_packet_copy(data,dlen,dpos,recordloc,2); if (!dpos) return -1;
+ if (byte_diff(recordloc,2,clientloc)) continue;
+ }
+ if (flagwild != (ch == '*')) continue;
+ dpos = dns_packet_copy(data,dlen,dpos,ttlstr,4); if (!dpos) return -1;
+ uint32_unpack_big(ttlstr,&ttl);
+ dpos = dns_packet_copy(data,dlen,dpos,ttd,8); if (!dpos) return -1;
+ if (byte_diff(ttd,8,"\0\0\0\0\0\0\0\0")) {
+ tai_unpack(ttd,&cutoff);
+ if (ttl == 0) {
+ if (tai_less(&cutoff,&now)) continue;
+ tai_sub(&cutoff,&cutoff,&now);
+ newttl = tai_approx(&cutoff);
+ if (newttl <= 2.0) newttl = 2.0;
+ if (newttl >= 3600.0) newttl = 3600.0;
+ ttl = newttl;
+ }
+ else
+ if (!tai_less(&cutoff,&now)) continue;
+ }
+ return 1;
+ }
+}
+
+static int dobytes(unsigned int len)
+{
+ char buf[20];
+ if (len > 20) return 0;
+ dpos = dns_packet_copy(data,dlen,dpos,buf,len);
+ if (!dpos) return 0;
+ return response_addbytes(buf,len);
+}
+
+static int doname(void)
+{
+ dpos = dns_packet_getname(data,dlen,dpos,&d1);
+ if (!dpos) return 0;
+ return response_addname(d1);
+}
+
+static int doit(char *q,char qtype[2])
+{
+ unsigned int bpos;
+ unsigned int anpos;
+ unsigned int aupos;
+ unsigned int arpos;
+ char *control;
+ char *wild;
+ int flaggavesoa;
+ int flagfound;
+ int r;
+ int flagns;
+ int flagauthoritative;
+ char x[20];
+ uint16 u16;
+ char addr[8][4];
+ int addrnum;
+ uint32 addrttl;
+ int i;
+
+ anpos = response_len;
+
+ control = q;
+ for (;;) {
+ flagns = 0;
+ flagauthoritative = 0;
+ cdb_findstart(&c);
+ while (r = find(control,0)) {
+ if (r == -1) return 0;
+ if (byte_equal(type,2,DNS_T_SOA)) flagauthoritative = 1;
+ if (byte_equal(type,2,DNS_T_NS)) flagns = 1;
+ }
+ if (flagns) break;
+ if (!*control) return 0; /* q is not within our bailiwick */
+ control += *control;
+ control += 1;
+ }
+
+ if (!flagauthoritative) {
+ response[2] &= ~4;
+ goto AUTHORITY; /* q is in a child zone */
+ }
+
+
+ flaggavesoa = 0;
+ flagfound = 0;
+ wild = q;
+
+ for (;;) {
+ addrnum = 0;
+ addrttl = 0;
+ cdb_findstart(&c);
+ while (r = find(wild,wild != q)) {
+ if (r == -1) return 0;
+ flagfound = 1;
+ if (flaggavesoa && byte_equal(type,2,DNS_T_SOA)) continue;
+ if (byte_diff(type,2,qtype) && byte_diff(qtype,2,DNS_T_ANY) && byte_diff(type,2,DNS_T_CNAME)) continue;
+ if (byte_equal(type,2,DNS_T_A) && (dlen - dpos == 4)) {
+ addrttl = ttl;
+ i = dns_random(addrnum + 1);
+ if (i < 8) {
+ if ((i < addrnum) && (addrnum < 8))
+ byte_copy(addr[addrnum],4,addr[i]);
+ byte_copy(addr[i],4,data + dpos);
+ }
+ if (addrnum < 1000000) ++addrnum;
+ continue;
+ }
+ if (!response_rstart(q,type,ttl)) return 0;
+ if (byte_equal(type,2,DNS_T_NS) || byte_equal(type,2,DNS_T_CNAME) || byte_equal(type,2,DNS_T_PTR)) {
+ if (!doname()) return 0;
+ }
+ else if (byte_equal(type,2,DNS_T_MX)) {
+ if (!dobytes(2)) return 0;
+ if (!doname()) return 0;
+ }
+ else if (byte_equal(type,2,DNS_T_SOA)) {
+ if (!doname()) return 0;
+ if (!doname()) return 0;
+ if (!dobytes(20)) return 0;
+ flaggavesoa = 1;
+ }
+ else
+ if (!response_addbytes(data + dpos,dlen - dpos)) return 0;
+ response_rfinish(RESPONSE_ANSWER);
+ }
+ for (i = 0;i < addrnum;++i)
+ if (i < 8) {
+ if (!response_rstart(q,DNS_T_A,addrttl)) return 0;
+ if (!response_addbytes(addr[i],4)) return 0;
+ response_rfinish(RESPONSE_ANSWER);
+ }
+
+ if (flagfound) break;
+ if (wild == control) break;
+ if (!*wild) break; /* impossible */
+ wild += *wild;
+ wild += 1;
+ }
+
+ if (!flagfound)
+ response_nxdomain();
+
+
+ AUTHORITY:
+ aupos = response_len;
+
+ if (flagauthoritative && (aupos == anpos)) {
+ cdb_findstart(&c);
+ while (r = find(control,0)) {
+ if (r == -1) return 0;
+ if (byte_equal(type,2,DNS_T_SOA)) {
+ if (!response_rstart(control,DNS_T_SOA,ttl)) return 0;
+ if (!doname()) return 0;
+ if (!doname()) return 0;
+ if (!dobytes(20)) return 0;
+ response_rfinish(RESPONSE_AUTHORITY);
+ break;
+ }
+ }
+ }
+ else
+ if (want(control,DNS_T_NS)) {
+ cdb_findstart(&c);
+ while (r = find(control,0)) {
+ if (r == -1) return 0;
+ if (byte_equal(type,2,DNS_T_NS)) {
+ if (!response_rstart(control,DNS_T_NS,ttl)) return 0;
+ if (!doname()) return 0;
+ response_rfinish(RESPONSE_AUTHORITY);
+ }
+ }
+ }
+
+ arpos = response_len;
+
+ bpos = anpos;
+ while (bpos < arpos) {
+ bpos = dns_packet_skipname(response,arpos,bpos); if (!bpos) return 0;
+ bpos = dns_packet_copy(response,arpos,bpos,x,10); if (!bpos) return 0;
+ if (byte_equal(x,2,DNS_T_NS) || byte_equal(x,2,DNS_T_MX)) {
+ if (byte_equal(x,2,DNS_T_NS)) {
+ if (!dns_packet_getname(response,arpos,bpos,&d1)) return 0;
+ }
+ else
+ if (!dns_packet_getname(response,arpos,bpos + 2,&d1)) return 0;
+ case_lowerb(d1,dns_domain_length(d1));
+ if (want(d1,DNS_T_A)) {
+ cdb_findstart(&c);
+ while (r = find(d1,0)) {
+ if (r == -1) return 0;
+ if (byte_equal(type,2,DNS_T_A)) {
+ if (!response_rstart(d1,DNS_T_A,ttl)) return 0;
+ if (!dobytes(4)) return 0;
+ response_rfinish(RESPONSE_ADDITIONAL);
+ }
+ }
+ }
+ }
+ uint16_unpack_big(x + 8,&u16);
+ bpos += u16;
+ }
+
+ if (flagauthoritative && (response_len > 512)) {
+ byte_zero(response + RESPONSE_ADDITIONAL,2);
+ response_len = arpos;
+ if (response_len > 512) {
+ byte_zero(response + RESPONSE_AUTHORITY,2);
+ response_len = aupos;
+ }
+ }
+
+ return 1;
+}
+
+int respond(char *q,char qtype[2],char ip[4])
+{
+ int fd;
+ int r;
+ char key[6];
+
+ tai_now(&now);
+ fd = open_read("data.cdb");
+ if (fd == -1) return 0;
+ cdb_init(&c,fd);
+
+ byte_zero(clientloc,2);
+ key[0] = 0;
+ key[1] = '%';
+ byte_copy(key + 2,4,ip);
+ r = cdb_find(&c,key,6);
+ if (!r) r = cdb_find(&c,key,5);
+ if (!r) r = cdb_find(&c,key,4);
+ if (!r) r = cdb_find(&c,key,3);
+ if (!r) r = cdb_find(&c,key,2);
+ if (r == -1) return 0;
+ if (r && (cdb_datalen(&c) == 2))
+ if (cdb_read(&c,clientloc,2,cdb_datapos(&c)) == -1) return 0;
+
+ r = doit(q,qtype);
+
+ cdb_free(&c);
+ close(fd);
+ return r;
+}
diff --git a/timeoutread.c b/timeoutread.c
new file mode 100644
index 0000000..85a36e9
--- /dev/null
+++ b/timeoutread.c
@@ -0,0 +1,28 @@
+#include <unistd.h>
+#include "error.h"
+#include "iopause.h"
+#include "timeoutread.h"
+
+int timeoutread(int t,int fd,char *buf,int len)
+{
+ struct taia now;
+ struct taia deadline;
+ iopause_fd x;
+
+ taia_now(&now);
+ taia_uint(&deadline,t);
+ taia_add(&deadline,&now,&deadline);
+
+ x.fd = fd;
+ x.events = IOPAUSE_READ;
+ for (;;) {
+ taia_now(&now);
+ iopause(&x,1,&deadline,&now);
+ if (x.revents) break;
+ if (taia_less(&deadline,&now)) {
+ errno = error_timeout;
+ return -1;
+ }
+ }
+ return read(fd,buf,len);
+}
diff --git a/timeoutread.h b/timeoutread.h
new file mode 100644
index 0000000..20d3bfc
--- /dev/null
+++ b/timeoutread.h
@@ -0,0 +1,6 @@
+#ifndef TIMEOUTREAD_H
+#define TIMEOUTREAD_H
+
+extern int timeoutread();
+
+#endif
diff --git a/timeoutwrite.c b/timeoutwrite.c
new file mode 100644
index 0000000..4f665f1
--- /dev/null
+++ b/timeoutwrite.c
@@ -0,0 +1,28 @@
+#include <unistd.h>
+#include "error.h"
+#include "iopause.h"
+#include "timeoutwrite.h"
+
+int timeoutwrite(int t,int fd,char *buf,int len)
+{
+ struct taia now;
+ struct taia deadline;
+ iopause_fd x;
+
+ taia_now(&now);
+ taia_uint(&deadline,t);
+ taia_add(&deadline,&now,&deadline);
+
+ x.fd = fd;
+ x.events = IOPAUSE_WRITE;
+ for (;;) {
+ taia_now(&now);
+ iopause(&x,1,&deadline,&now);
+ if (x.revents) break;
+ if (taia_less(&deadline,&now)) {
+ errno = error_timeout;
+ return -1;
+ }
+ }
+ return write(fd,buf,len);
+}
diff --git a/timeoutwrite.h b/timeoutwrite.h
new file mode 100644
index 0000000..4725861
--- /dev/null
+++ b/timeoutwrite.h
@@ -0,0 +1,6 @@
+#ifndef TIMEOUTWRITE_H
+#define TIMEOUTWRITE_H
+
+extern int timeoutwrite();
+
+#endif
diff --git a/tinydns-conf.c b/tinydns-conf.c
new file mode 100644
index 0000000..d3a4ce5
--- /dev/null
+++ b/tinydns-conf.c
@@ -0,0 +1,98 @@
+#include <unistd.h>
+#include <pwd.h>
+#include "strerr.h"
+#include "exit.h"
+#include "auto_home.h"
+#include "generic-conf.h"
+
+#define FATAL "tinydns-conf: fatal: "
+
+void usage(void)
+{
+ strerr_die1x(100,"tinydns-conf: usage: tinydns-conf acct logacct /tinydns myip");
+}
+
+char *dir;
+char *user;
+char *loguser;
+struct passwd *pw;
+char *myip;
+
+int main(int argc,char **argv)
+{
+ user = argv[1];
+ if (!user) usage();
+ loguser = argv[2];
+ if (!loguser) usage();
+ dir = argv[3];
+ if (!dir) usage();
+ if (dir[0] != '/') usage();
+ myip = argv[4];
+ if (!myip) usage();
+
+ pw = getpwnam(loguser);
+ if (!pw)
+ strerr_die3x(111,FATAL,"unknown account ",loguser);
+
+ init(dir,FATAL);
+ makelog(loguser,pw->pw_uid,pw->pw_gid);
+
+ makedir("env");
+ perm(02755);
+ start("env/ROOT"); outs(dir); outs("/root\n"); finish();
+ perm(0644);
+ start("env/IP"); outs(myip); outs("\n"); finish();
+ perm(0644);
+
+ start("run");
+ outs("#!/bin/sh\nexec 2>&1\nexec envuidgid "); outs(user);
+ outs(" envdir ./env softlimit -d300000 ");
+ outs(auto_home); outs("/bin/tinydns\n");
+ finish();
+ perm(0755);
+
+ makedir("root");
+ perm(02755);
+
+ start("root/data");
+ finish();
+ perm(0644);
+
+ start("root/add-ns");
+ outs("#!/bin/sh\nexec ");
+ outs(auto_home); outs("/bin/tinydns-edit data data.new add ns ${1+\"$@\"}\n");
+ finish();
+ perm(0755);
+
+ start("root/add-childns");
+ outs("#!/bin/sh\nexec ");
+ outs(auto_home); outs("/bin/tinydns-edit data data.new add childns ${1+\"$@\"}\n");
+ finish();
+ perm(0755);
+
+ start("root/add-host");
+ outs("#!/bin/sh\nexec ");
+ outs(auto_home); outs("/bin/tinydns-edit data data.new add host ${1+\"$@\"}\n");
+ finish();
+ perm(0755);
+
+ start("root/add-alias");
+ outs("#!/bin/sh\nexec ");
+ outs(auto_home); outs("/bin/tinydns-edit data data.new add alias ${1+\"$@\"}\n");
+ finish();
+ perm(0755);
+
+ start("root/add-mx");
+ outs("#!/bin/sh\nexec ");
+ outs(auto_home); outs("/bin/tinydns-edit data data.new add mx ${1+\"$@\"}\n");
+ finish();
+ perm(0755);
+
+ start("root/Makefile");
+ outs("data.cdb: data\n");
+ outs("\t"); outs(auto_home); outs("/bin/tinydns-data\n");
+ finish();
+ perm(0644);
+
+ _exit(0);
+}
diff --git a/tinydns-data.c b/tinydns-data.c
new file mode 100644
index 0000000..ba82f84
--- /dev/null
+++ b/tinydns-data.c
@@ -0,0 +1,456 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "uint16.h"
+#include "uint32.h"
+#include "str.h"
+#include "byte.h"
+#include "fmt.h"
+#include "ip4.h"
+#include "exit.h"
+#include "case.h"
+#include "scan.h"
+#include "buffer.h"
+#include "strerr.h"
+#include "getln.h"
+#include "cdb_make.h"
+#include "stralloc.h"
+#include "open.h"
+#include "dns.h"
+
+#define TTL_NS 259200
+#define TTL_POSITIVE 86400
+#define TTL_NEGATIVE 2560
+
+#define FATAL "tinydns-data: fatal: "
+
+void die_datatmp(void)
+{
+ strerr_die2sys(111,FATAL,"unable to create data.tmp: ");
+}
+void nomem(void)
+{
+ strerr_die1sys(111,FATAL);
+}
+
+void ttdparse(stralloc *sa,char ttd[8])
+{
+ unsigned int i;
+ char ch;
+
+ byte_zero(ttd,8);
+ for (i = 0;(i < 16) && (i < sa->len);++i) {
+ ch = sa->s[i];
+ if ((ch >= '0') && (ch <= '9'))
+ ch -= '0';
+ else if ((ch >= 'a') && (ch <= 'f'))
+ ch -= 'a' - 10;
+ else
+ ch = 0;
+ if (!(i & 1)) ch <<= 4;
+ ttd[i >> 1] |= ch;
+ }
+}
+
+void locparse(stralloc *sa,char loc[2])
+{
+ loc[0] = (sa->len > 0) ? sa->s[0] : 0;
+ loc[1] = (sa->len > 1) ? sa->s[1] : 0;
+}
+
+void ipprefix_cat(stralloc *out,char *s)
+{
+ unsigned long u;
+ char ch;
+ unsigned int j;
+
+ for (;;)
+ if (*s == '.')
+ ++s;
+ else {
+ j = scan_ulong(s,&u);
+ if (!j) return;
+ s += j;
+ ch = u;
+ if (!stralloc_catb(out,&ch,1)) nomem();
+ }
+}
+
+void txtparse(stralloc *sa)
+{
+ char ch;
+ unsigned int i;
+ unsigned int j;
+
+ j = 0;
+ i = 0;
+ while (i < sa->len) {
+ ch = sa->s[i++];
+ if (ch == '\\') {
+ if (i >= sa->len) break;
+ ch = sa->s[i++];
+ if ((ch >= '0') && (ch <= '7')) {
+ ch -= '0';
+ if ((i < sa->len) && (sa->s[i] >= '0') && (sa->s[i] <= '7')) {
+ ch <<= 3;
+ ch += sa->s[i++] - '0';
+ if ((i < sa->len) && (sa->s[i] >= '0') && (sa->s[i] <= '7')) {
+ ch <<= 3;
+ ch += sa->s[i++] - '0';
+ }
+ }
+ }
+ }
+ sa->s[j++] = ch;
+ }
+ sa->len = j;
+}
+
+char defaultsoa[20];
+
+void defaultsoa_init(int fd)
+{
+ struct stat st;
+ if (fstat(fd,&st) == -1)
+ strerr_die2sys(111,FATAL,"unable to stat data: ");
+ uint32_pack_big(defaultsoa,st.st_mtime);
+ if (byte_equal(defaultsoa,4,"\0\0\0\0"))
+ defaultsoa[3] = 1;
+ byte_copy(defaultsoa + 4,16,"\0\0\100\000\0\0\010\000\0\020\000\000\0\0\012\000");
+}
+
+int fdcdb;
+struct cdb_make cdb;
+static stralloc key;
+static stralloc result;
+
+void rr_add(const char *buf,unsigned int len)
+{
+ if (!stralloc_catb(&result,buf,len)) nomem();
+}
+void rr_addname(const char *d)
+{
+ rr_add(d,dns_domain_length(d));
+}
+void rr_start(const char type[2],unsigned long ttl,const char ttd[8],const char loc[2])
+{
+ char buf[4];
+ if (!stralloc_copyb(&result,type,2)) nomem();
+ if (byte_equal(loc,2,"\0\0"))
+ rr_add("=",1);
+ else {
+ rr_add(">",1);
+ rr_add(loc,2);
+ }
+ uint32_pack_big(buf,ttl);
+ rr_add(buf,4);
+ rr_add(ttd,8);
+}
+void rr_finish(const char *owner)
+{
+ if (byte_equal(owner,2,"\1*")) {
+ owner += 2;
+ result.s[2] -= 19;
+ }
+ if (!stralloc_copyb(&key,owner,dns_domain_length(owner))) nomem();
+ case_lowerb(key.s,key.len);
+ if (cdb_make_add(&cdb,key.s,key.len,result.s,result.len) == -1)
+ die_datatmp();
+}
+
+buffer b;
+char bspace[1024];
+
+static stralloc line;
+int match = 1;
+unsigned long linenum = 0;
+
+#define NUMFIELDS 15
+static stralloc f[NUMFIELDS];
+
+static char *d1;
+static char *d2;
+char dptr[DNS_NAME4_DOMAIN];
+
+char strnum[FMT_ULONG];
+
+void syntaxerror(const char *why)
+{
+ strnum[fmt_ulong(strnum,linenum)] = 0;
+ strerr_die4x(111,FATAL,"unable to parse data line ",strnum,why);
+}
+
+int main()
+{
+ int fddata;
+ int i;
+ int j;
+ int k;
+ char ch;
+ unsigned long ttl;
+ char ttd[8];
+ char loc[2];
+ unsigned long u;
+ char ip[4];
+ char type[2];
+ char soa[20];
+ char buf[4];
+
+ umask(022);
+
+ fddata = open_read("data");
+ if (fddata == -1)
+ strerr_die2sys(111,FATAL,"unable to open data: ");
+ defaultsoa_init(fddata);
+
+ buffer_init(&b,buffer_unixread,fddata,bspace,sizeof bspace);
+
+ fdcdb = open_trunc("data.tmp");
+ if (fdcdb == -1) die_datatmp();
+ if (cdb_make_start(&cdb,fdcdb) == -1) die_datatmp();
+
+ while (match) {
+ ++linenum;
+ if (getln(&b,&line,&match,'\n') == -1)
+ strerr_die2sys(111,FATAL,"unable to read line: ");
+
+ while (line.len) {
+ ch = line.s[line.len - 1];
+ if ((ch != ' ') && (ch != '\t') && (ch != '\n')) break;
+ --line.len;
+ }
+ if (!line.len) continue;
+ if (line.s[0] == '#') continue;
+ if (line.s[0] == '-') continue;
+
+ j = 1;
+ for (i = 0;i < NUMFIELDS;++i) {
+ if (j >= line.len) {
+ if (!stralloc_copys(&f[i],"")) nomem();
+ }
+ else {
+ k = byte_chr(line.s + j,line.len - j,':');
+ if (!stralloc_copyb(&f[i],line.s + j,k)) nomem();
+ j += k + 1;
+ }
+ }
+
+ switch(line.s[0]) {
+
+ case '%':
+ locparse(&f[0],loc);
+ if (!stralloc_copyb(&key,"\0%",2)) nomem();
+ if (!stralloc_0(&f[1])) nomem();
+ ipprefix_cat(&key,f[1].s);
+ if (cdb_make_add(&cdb,key.s,key.len,loc,2) == -1)
+ die_datatmp();
+ break;
+
+ case 'Z':
+ if (!dns_domain_fromdot(&d1,f[0].s,f[0].len)) nomem();
+
+ if (!stralloc_0(&f[3])) nomem();
+ if (!scan_ulong(f[3].s,&u)) uint32_unpack_big(defaultsoa,&u);
+ uint32_pack_big(soa,u);
+ if (!stralloc_0(&f[4])) nomem();
+ if (!scan_ulong(f[4].s,&u)) uint32_unpack_big(defaultsoa + 4,&u);
+ uint32_pack_big(soa + 4,u);
+ if (!stralloc_0(&f[5])) nomem();
+ if (!scan_ulong(f[5].s,&u)) uint32_unpack_big(defaultsoa + 8,&u);
+ uint32_pack_big(soa + 8,u);
+ if (!stralloc_0(&f[6])) nomem();
+ if (!scan_ulong(f[6].s,&u)) uint32_unpack_big(defaultsoa + 12,&u);
+ uint32_pack_big(soa + 12,u);
+ if (!stralloc_0(&f[7])) nomem();
+ if (!scan_ulong(f[7].s,&u)) uint32_unpack_big(defaultsoa + 16,&u);
+ uint32_pack_big(soa + 16,u);
+
+ if (!stralloc_0(&f[8])) nomem();
+ if (!scan_ulong(f[8].s,&ttl)) ttl = TTL_NEGATIVE;
+ ttdparse(&f[9],ttd);
+ locparse(&f[10],loc);
+
+ rr_start(DNS_T_SOA,ttl,ttd,loc);
+ if (!dns_domain_fromdot(&d2,f[1].s,f[1].len)) nomem();
+ rr_addname(d2);
+ if (!dns_domain_fromdot(&d2,f[2].s,f[2].len)) nomem();
+ rr_addname(d2);
+ rr_add(soa,20);
+ rr_finish(d1);
+ break;
+
+ case '.': case '&':
+ if (!dns_domain_fromdot(&d1,f[0].s,f[0].len)) nomem();
+ if (!stralloc_0(&f[3])) nomem();
+ if (!scan_ulong(f[3].s,&ttl)) ttl = TTL_NS;
+ ttdparse(&f[4],ttd);
+ locparse(&f[5],loc);
+
+ if (!stralloc_0(&f[1])) nomem();
+
+ if (byte_chr(f[2].s,f[2].len,'.') >= f[2].len) {
+ if (!stralloc_cats(&f[2],".ns.")) nomem();
+ if (!stralloc_catb(&f[2],f[0].s,f[0].len)) nomem();
+ }
+ if (!dns_domain_fromdot(&d2,f[2].s,f[2].len)) nomem();
+
+ if (line.s[0] == '.') {
+ rr_start(DNS_T_SOA,ttl ? TTL_NEGATIVE : 0,ttd,loc);
+ rr_addname(d2);
+ rr_add("\12hostmaster",11);
+ rr_addname(d1);
+ rr_add(defaultsoa,20);
+ rr_finish(d1);
+ }
+
+ rr_start(DNS_T_NS,ttl,ttd,loc);
+ rr_addname(d2);
+ rr_finish(d1);
+
+ if (ip4_scan(f[1].s,ip)) {
+ rr_start(DNS_T_A,ttl,ttd,loc);
+ rr_add(ip,4);
+ rr_finish(d2);
+ }
+
+ break;
+
+ case '+': case '=':
+ if (!dns_domain_fromdot(&d1,f[0].s,f[0].len)) nomem();
+ if (!stralloc_0(&f[2])) nomem();
+ if (!scan_ulong(f[2].s,&ttl)) ttl = TTL_POSITIVE;
+ ttdparse(&f[3],ttd);
+ locparse(&f[4],loc);
+
+ if (!stralloc_0(&f[1])) nomem();
+
+ if (ip4_scan(f[1].s,ip)) {
+ rr_start(DNS_T_A,ttl,ttd,loc);
+ rr_add(ip,4);
+ rr_finish(d1);
+
+ if (line.s[0] == '=') {
+ dns_name4_domain(dptr,ip);
+ rr_start(DNS_T_PTR,ttl,ttd,loc);
+ rr_addname(d1);
+ rr_finish(dptr);
+ }
+ }
+ break;
+
+ case '@':
+ if (!dns_domain_fromdot(&d1,f[0].s,f[0].len)) nomem();
+ if (!stralloc_0(&f[4])) nomem();
+ if (!scan_ulong(f[4].s,&ttl)) ttl = TTL_POSITIVE;
+ ttdparse(&f[5],ttd);
+ locparse(&f[6],loc);
+
+ if (!stralloc_0(&f[1])) nomem();
+
+ if (byte_chr(f[2].s,f[2].len,'.') >= f[2].len) {
+ if (!stralloc_cats(&f[2],".mx.")) nomem();
+ if (!stralloc_catb(&f[2],f[0].s,f[0].len)) nomem();
+ }
+ if (!dns_domain_fromdot(&d2,f[2].s,f[2].len)) nomem();
+
+ if (!stralloc_0(&f[3])) nomem();
+ if (!scan_ulong(f[3].s,&u)) u = 0;
+
+ rr_start(DNS_T_MX,ttl,ttd,loc);
+ uint16_pack_big(buf,u);
+ rr_add(buf,2);
+ rr_addname(d2);
+ rr_finish(d1);
+
+ if (ip4_scan(f[1].s,ip)) {
+ rr_start(DNS_T_A,ttl,ttd,loc);
+ rr_add(ip,4);
+ rr_finish(d2);
+ }
+ break;
+
+ case '^': case 'C':
+ if (!dns_domain_fromdot(&d1,f[0].s,f[0].len)) nomem();
+ if (!dns_domain_fromdot(&d2,f[1].s,f[1].len)) nomem();
+ if (!stralloc_0(&f[2])) nomem();
+ if (!scan_ulong(f[2].s,&ttl)) ttl = TTL_POSITIVE;
+ ttdparse(&f[3],ttd);
+ locparse(&f[4],loc);
+
+ if (line.s[0] == 'C')
+ rr_start(DNS_T_CNAME,ttl,ttd,loc);
+ else
+ rr_start(DNS_T_PTR,ttl,ttd,loc);
+ rr_addname(d2);
+ rr_finish(d1);
+ break;
+
+ case '\'':
+ if (!dns_domain_fromdot(&d1,f[0].s,f[0].len)) nomem();
+ if (!stralloc_0(&f[2])) nomem();
+ if (!scan_ulong(f[2].s,&ttl)) ttl = TTL_POSITIVE;
+ ttdparse(&f[3],ttd);
+ locparse(&f[4],loc);
+
+ rr_start(DNS_T_TXT,ttl,ttd,loc);
+
+ txtparse(&f[1]);
+ i = 0;
+ while (i < f[1].len) {
+ k = f[1].len - i;
+ if (k > 127) k = 127;
+ ch = k;
+ rr_add(&ch,1);
+ rr_add(f[1].s + i,k);
+ i += k;
+ }
+
+ rr_finish(d1);
+ break;
+
+ case ':':
+ if (!dns_domain_fromdot(&d1,f[0].s,f[0].len)) nomem();
+ if (!stralloc_0(&f[3])) nomem();
+ if (!scan_ulong(f[3].s,&ttl)) ttl = TTL_POSITIVE;
+ ttdparse(&f[4],ttd);
+ locparse(&f[5],loc);
+
+ if (!stralloc_0(&f[1])) nomem();
+ scan_ulong(f[1].s,&u);
+ uint16_pack_big(type,u);
+ if (byte_equal(type,2,DNS_T_AXFR))
+ syntaxerror(": type AXFR prohibited");
+ if (byte_equal(type,2,"\0\0"))
+ syntaxerror(": type 0 prohibited");
+ if (byte_equal(type,2,DNS_T_SOA))
+ syntaxerror(": type SOA prohibited");
+ if (byte_equal(type,2,DNS_T_NS))
+ syntaxerror(": type NS prohibited");
+ if (byte_equal(type,2,DNS_T_CNAME))
+ syntaxerror(": type CNAME prohibited");
+ if (byte_equal(type,2,DNS_T_PTR))
+ syntaxerror(": type PTR prohibited");
+ if (byte_equal(type,2,DNS_T_MX))
+ syntaxerror(": type MX prohibited");
+
+ txtparse(&f[2]);
+
+ rr_start(type,ttl,ttd,loc);
+ rr_add(f[2].s,f[2].len);
+ rr_finish(d1);
+ break;
+
+ default:
+ syntaxerror(": unrecognized leading character");
+ }
+ }
+
+ if (cdb_make_finish(&cdb) == -1) die_datatmp();
+ if (fsync(fdcdb) == -1) die_datatmp();
+ if (close(fdcdb) == -1) die_datatmp(); /* NFS stupidity */
+ if (rename("data.tmp","data.cdb") == -1)
+ strerr_die2sys(111,FATAL,"unable to move data.tmp to data.cdb: ");
+
+ _exit(0);
+}
diff --git a/tinydns-edit.c b/tinydns-edit.c
new file mode 100644
index 0000000..126a7e0
--- /dev/null
+++ b/tinydns-edit.c
@@ -0,0 +1,257 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "stralloc.h"
+#include "buffer.h"
+#include "exit.h"
+#include "open.h"
+#include "getln.h"
+#include "strerr.h"
+#include "scan.h"
+#include "byte.h"
+#include "str.h"
+#include "fmt.h"
+#include "ip4.h"
+#include "dns.h"
+
+#define FATAL "tinydns-edit: fatal: "
+
+#define TTL_NS 259200
+#define TTL_POSITIVE 86400
+
+char *fn;
+char *fnnew;
+
+void die_usage()
+{
+ strerr_die1x(100,"tinydns-edit: usage: tinydns-edit data data.new add [ns|childns|host|alias|mx] domain a.b.c.d");
+}
+void nomem()
+{
+ strerr_die2x(111,FATAL,"out of memory");
+}
+void die_read()
+{
+ strerr_die4sys(100,FATAL,"tinydns-edit: fatal: unable to read ",fn,": ");
+}
+void die_write()
+{
+ strerr_die4sys(100,FATAL,"tinydns-edit: fatal: unable to write ",fnnew,": ");
+}
+
+char mode;
+static char *target;
+char targetip[4];
+
+int fd;
+buffer b;
+char bspace[1024];
+
+int fdnew;
+buffer bnew;
+char bnewspace[1024];
+
+static stralloc line;
+int match = 1;
+
+#define NUMFIELDS 10
+static stralloc f[NUMFIELDS];
+
+static char *d1;
+static char *d2;
+char ip[4];
+char ipstr[IP4_FMT];
+char strnum[FMT_ULONG];
+
+static char *names[26];
+static int used[26];
+
+void put(const char *buf,unsigned int len)
+{
+ if (buffer_putalign(&bnew,buf,len) == -1) die_write();
+}
+
+int main(int argc,char **argv)
+{
+ unsigned long ttl;
+ struct stat st;
+ int i;
+ int j;
+ int k;
+ char ch;
+
+ if (!*argv) die_usage();
+
+ if (!*++argv) die_usage();
+ fn = *argv;
+
+ if (!*++argv) die_usage();
+ fnnew = *argv;
+
+ if (!*++argv) die_usage();
+ if (str_diff(*argv,"add")) die_usage();
+
+ if (!*++argv) die_usage();
+ if (str_equal(*argv,"ns")) mode = '.';
+ else if (str_equal(*argv,"childns")) mode = '&';
+ else if (str_equal(*argv,"host")) mode = '=';
+ else if (str_equal(*argv,"alias")) mode = '+';
+ else if (str_equal(*argv,"mx")) mode = '@';
+ else die_usage();
+
+ if (!*++argv) die_usage();
+ if (!dns_domain_fromdot(&target,*argv,str_len(*argv))) nomem();
+
+ if (!*++argv) die_usage();
+ if (!ip4_scan(*argv,targetip)) die_usage();
+
+ umask(077);
+
+ fd = open_read(fn);
+ if (fd == -1) die_read();
+ if (fstat(fd,&st) == -1) die_read();
+ buffer_init(&b,buffer_unixread,fd,bspace,sizeof bspace);
+
+ fdnew = open_trunc(fnnew);
+ if (fdnew == -1) die_write();
+ if (fchmod(fdnew,st.st_mode & 0644) == -1) die_write();
+ buffer_init(&bnew,buffer_unixwrite,fdnew,bnewspace,sizeof bnewspace);
+
+ switch(mode) {
+ case '.': case '&':
+ ttl = TTL_NS;
+ for (i = 0;i < 26;++i) {
+ ch = 'a' + i;
+ if (!stralloc_copyb(&f[0],&ch,1)) nomem();
+ if (!stralloc_cats(&f[0],".ns.")) nomem();
+ if (!dns_domain_todot_cat(&f[0],target)) nomem();
+ if (!dns_domain_fromdot(&names[i],f[0].s,f[0].len)) nomem();
+ }
+ break;
+ case '+': case '=':
+ ttl = TTL_POSITIVE;
+ break;
+ case '@':
+ ttl = TTL_POSITIVE;
+ for (i = 0;i < 26;++i) {
+ ch = 'a' + i;
+ if (!stralloc_copyb(&f[0],&ch,1)) nomem();
+ if (!stralloc_cats(&f[0],".mx.")) nomem();
+ if (!dns_domain_todot_cat(&f[0],target)) nomem();
+ if (!dns_domain_fromdot(&names[i],f[0].s,f[0].len)) nomem();
+ }
+ break;
+ }
+
+ while (match) {
+ if (getln(&b,&line,&match,'\n') == -1) die_read();
+
+ put(line.s,line.len);
+ if (line.len && !match) put("\n",1);
+
+ while (line.len) {
+ ch = line.s[line.len - 1];
+ if ((ch != ' ') && (ch != '\t') && (ch != '\n')) break;
+ --line.len;
+ }
+ if (!line.len) continue;
+ if (line.s[0] == '#') continue;
+
+ j = 1;
+ for (i = 0;i < NUMFIELDS;++i) {
+ if (j >= line.len) {
+ if (!stralloc_copys(&f[i],"")) nomem();
+ }
+ else {
+ k = byte_chr(line.s + j,line.len - j,':');
+ if (!stralloc_copyb(&f[i],line.s + j,k)) nomem();
+ j += k + 1;
+ }
+ }
+
+ switch(mode) {
+ case '.': case '&':
+ if (line.s[0] == mode) {
+ if (!dns_domain_fromdot(&d1,f[0].s,f[0].len)) nomem();
+ if (dns_domain_equal(d1,target)) {
+ if (byte_chr(f[2].s,f[2].len,'.') >= f[2].len) {
+ if (!stralloc_cats(&f[2],".ns.")) nomem();
+ if (!stralloc_catb(&f[2],f[0].s,f[0].len)) nomem();
+ }
+ if (!dns_domain_fromdot(&d2,f[2].s,f[2].len)) nomem();
+ if (!stralloc_0(&f[3])) nomem();
+ if (!scan_ulong(f[3].s,&ttl)) ttl = TTL_NS;
+ for (i = 0;i < 26;++i)
+ if (dns_domain_equal(d2,names[i])) {
+ used[i] = 1;
+ break;
+ }
+ }
+ }
+ break;
+
+ case '=':
+ if (line.s[0] == '=') {
+ if (!dns_domain_fromdot(&d1,f[0].s,f[0].len)) nomem();
+ if (dns_domain_equal(d1,target))
+ strerr_die2x(100,FATAL,"host name already used");
+ if (!stralloc_0(&f[1])) nomem();
+ if (ip4_scan(f[1].s,ip))
+ if (byte_equal(ip,4,targetip))
+ strerr_die2x(100,FATAL,"IP address already used");
+ }
+ break;
+
+ case '@':
+ if (line.s[0] == '@') {
+ if (!dns_domain_fromdot(&d1,f[0].s,f[0].len)) nomem();
+ if (dns_domain_equal(d1,target)) {
+ if (byte_chr(f[2].s,f[2].len,'.') >= f[2].len) {
+ if (!stralloc_cats(&f[2],".mx.")) nomem();
+ if (!stralloc_catb(&f[2],f[0].s,f[0].len)) nomem();
+ }
+ if (!dns_domain_fromdot(&d2,f[2].s,f[2].len)) nomem();
+ if (!stralloc_0(&f[4])) nomem();
+ if (!scan_ulong(f[4].s,&ttl)) ttl = TTL_POSITIVE;
+ for (i = 0;i < 26;++i)
+ if (dns_domain_equal(d2,names[i])) {
+ used[i] = 1;
+ break;
+ }
+ }
+ }
+ break;
+ }
+ }
+
+ if (!stralloc_copyb(&f[0],&mode,1)) nomem();
+ if (!dns_domain_todot_cat(&f[0],target)) nomem();
+ if (!stralloc_cats(&f[0],":")) nomem();
+ if (!stralloc_catb(&f[0],ipstr,ip4_fmt(ipstr,targetip))) nomem();
+ switch(mode) {
+ case '.': case '&': case '@':
+ for (i = 0;i < 26;++i)
+ if (!used[i])
+ break;
+ if (i >= 26)
+ strerr_die2x(100,FATAL,"too many records for that domain");
+ ch = 'a' + i;
+ if (!stralloc_cats(&f[0],":")) nomem();
+ if (!stralloc_catb(&f[0],&ch,1)) nomem();
+ if (mode == '@')
+ if (!stralloc_cats(&f[0],":")) nomem();
+ break;
+ }
+ if (!stralloc_cats(&f[0],":")) nomem();
+ if (!stralloc_catb(&f[0],strnum,fmt_ulong(strnum,ttl))) nomem();
+ if (!stralloc_cats(&f[0],"\n")) nomem();
+ put(f[0].s,f[0].len);
+
+ if (buffer_flush(&bnew) == -1) die_write();
+ if (fsync(fdnew) == -1) die_write();
+ if (close(fdnew) == -1) die_write(); /* NFS dorks */
+ if (rename(fnnew,fn) == -1)
+ strerr_die6sys(111,FATAL,"unable to move ",fnnew," to ",fn,": ");
+ _exit(0);
+}
diff --git a/tinydns-get.c b/tinydns-get.c
new file mode 100644
index 0000000..f7fd67f
--- /dev/null
+++ b/tinydns-get.c
@@ -0,0 +1,76 @@
+#include "str.h"
+#include "byte.h"
+#include "scan.h"
+#include "exit.h"
+#include "stralloc.h"
+#include "buffer.h"
+#include "strerr.h"
+#include "uint16.h"
+#include "response.h"
+#include "case.h"
+#include "printpacket.h"
+#include "parsetype.h"
+#include "ip4.h"
+#include "dns.h"
+
+extern int respond(char *,char *,char *);
+
+#define FATAL "tinydns-get: fatal: "
+
+void usage(void)
+{
+ strerr_die1x(100,"tinydns-get: usage: tinydns-get type name [ip]");
+}
+void oops(void)
+{
+ strerr_die2sys(111,FATAL,"unable to parse: ");
+}
+
+static char ip[4];
+static char type[2];
+static char *q;
+
+static stralloc out;
+
+int main(int argc,char **argv)
+{
+ uint16 u16;
+
+ if (!*argv) usage();
+
+ if (!*++argv) usage();
+ if (!parsetype(*argv,type)) usage();
+
+ if (!*++argv) usage();
+ if (!dns_domain_fromdot(&q,*argv,str_len(*argv))) oops();
+
+ if (*++argv) {
+ if (!ip4_scan(*argv,ip)) usage();
+ }
+
+ if (!stralloc_copys(&out,"")) oops();
+ uint16_unpack_big(type,&u16);
+ if (!stralloc_catulong0(&out,u16,0)) oops();
+ if (!stralloc_cats(&out," ")) oops();
+ if (!dns_domain_todot_cat(&out,q)) oops();
+ if (!stralloc_cats(&out,":\n")) oops();
+
+ if (!response_query(q,type,DNS_C_IN)) oops();
+ response[3] &= ~128;
+ response[2] &= ~1;
+ response[2] |= 4;
+ case_lowerb(q,dns_domain_length(q));
+
+ if (byte_equal(type,2,DNS_T_AXFR)) {
+ response[3] &= ~15;
+ response[3] |= 4;
+ }
+ else
+ if (!respond(q,type,ip)) goto DONE;
+
+ if (!printpacket_cat(&out,response,response_len)) oops();
+
+ DONE:
+ buffer_putflush(buffer_1,out.s,out.len);
+ _exit(0);
+}
diff --git a/tinydns.c b/tinydns.c
new file mode 100644
index 0000000..2a5b560
--- /dev/null
+++ b/tinydns.c
@@ -0,0 +1,11 @@
+#include "dns.h"
+
+const char *fatal = "tinydns: fatal: ";
+const char *starting = "starting tinydns\n";
+
+static char seed[128];
+
+void initialize(void)
+{
+ dns_random_init(seed);
+}
diff --git a/trycpp.c b/trycpp.c
new file mode 100644
index 0000000..690f2f3
--- /dev/null
+++ b/trycpp.c
@@ -0,0 +1,7 @@
+int main()
+{
+#ifdef NeXT
+ printf("nextstep\n"); exit(0);
+#endif
+ printf("unknown\n"); exit(0);
+}
diff --git a/trydrent.c b/trydrent.c
new file mode 100644
index 0000000..c778176
--- /dev/null
+++ b/trydrent.c
@@ -0,0 +1,8 @@
+#include <sys/types.h>
+#include <dirent.h>
+
+void foo()
+{
+ DIR *dir;
+ struct dirent *d;
+}
diff --git a/trylsock.c b/trylsock.c
new file mode 100644
index 0000000..c32bd40
--- /dev/null
+++ b/trylsock.c
@@ -0,0 +1,4 @@
+int main()
+{
+ ;
+}
diff --git a/trypoll.c b/trypoll.c
new file mode 100644
index 0000000..30bea3d
--- /dev/null
+++ b/trypoll.c
@@ -0,0 +1,18 @@
+#include <sys/types.h>
+#include <fcntl.h>
+#include <poll.h>
+
+int main()
+{
+ struct pollfd x;
+
+ x.fd = open("trypoll.c",O_RDONLY);
+ if (x.fd == -1) _exit(111);
+ x.events = POLLIN;
+ if (poll(&x,1,10) == -1) _exit(1);
+ if (x.revents != POLLIN) _exit(1);
+
+ /* XXX: try to detect and avoid poll() imitation libraries */
+
+ _exit(0);
+}
diff --git a/tryshsgr.c b/tryshsgr.c
new file mode 100644
index 0000000..81b395c
--- /dev/null
+++ b/tryshsgr.c
@@ -0,0 +1,14 @@
+int main()
+{
+ short x[4];
+
+ x[0] = x[1] = 1;
+ if (getgroups(1,x) == 0) if (setgroups(1,x) == -1) _exit(1);
+
+ if (getgroups(1,x) == -1) _exit(1);
+ if (x[1] != 1) _exit(1);
+ x[1] = 2;
+ if (getgroups(1,x) == -1) _exit(1);
+ if (x[1] != 2) _exit(1);
+ _exit(0);
+}
diff --git a/trysysel.c b/trysysel.c
new file mode 100644
index 0000000..f6ed055
--- /dev/null
+++ b/trysysel.c
@@ -0,0 +1,8 @@
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/select.h> /* SVR4 silliness */
+
+void foo()
+{
+ ;
+}
diff --git a/tryulong32.c b/tryulong32.c
new file mode 100644
index 0000000..20683d6
--- /dev/null
+++ b/tryulong32.c
@@ -0,0 +1,11 @@
+int main()
+{
+ unsigned long u;
+ u = 1;
+ u += u; u += u; u += u; u += u; u += u; u += u; u += u; u += u;
+ u += u; u += u; u += u; u += u; u += u; u += u; u += u; u += u;
+ u += u; u += u; u += u; u += u; u += u; u += u; u += u; u += u;
+ u += u; u += u; u += u; u += u; u += u; u += u; u += u; u += u;
+ if (!u) _exit(0);
+ _exit(1);
+}
diff --git a/tryulong64.c b/tryulong64.c
new file mode 100644
index 0000000..479e4be
--- /dev/null
+++ b/tryulong64.c
@@ -0,0 +1,11 @@
+int main()
+{
+ unsigned long u;
+ u = 1;
+ u += u; u += u; u += u; u += u; u += u; u += u; u += u; u += u;
+ u += u; u += u; u += u; u += u; u += u; u += u; u += u; u += u;
+ u += u; u += u; u += u; u += u; u += u; u += u; u += u; u += u;
+ u += u; u += u; u += u; u += u; u += u; u += u; u += u; u += u;
+ if (!u) _exit(1);
+ _exit(0);
+}
diff --git a/uint16.h b/uint16.h
new file mode 100644
index 0000000..af314fd
--- /dev/null
+++ b/uint16.h
@@ -0,0 +1,11 @@
+#ifndef UINT16_H
+#define UINT16_H
+
+typedef unsigned short uint16;
+
+extern void uint16_pack(char *,uint16);
+extern void uint16_pack_big(char *,uint16);
+extern void uint16_unpack(const char *,uint16 *);
+extern void uint16_unpack_big(const char *,uint16 *);
+
+#endif
diff --git a/uint16_pack.c b/uint16_pack.c
new file mode 100644
index 0000000..17dbfe6
--- /dev/null
+++ b/uint16_pack.c
@@ -0,0 +1,13 @@
+#include "uint16.h"
+
+void uint16_pack(char s[2],uint16 u)
+{
+ s[0] = u & 255;
+ s[1] = u >> 8;
+}
+
+void uint16_pack_big(char s[2],uint16 u)
+{
+ s[1] = u & 255;
+ s[0] = u >> 8;
+}
diff --git a/uint16_unpack.c b/uint16_unpack.c
new file mode 100644
index 0000000..518b9e3
--- /dev/null
+++ b/uint16_unpack.c
@@ -0,0 +1,23 @@
+#include "uint16.h"
+
+void uint16_unpack(const char s[2],uint16 *u)
+{
+ uint16 result;
+
+ result = (unsigned char) s[1];
+ result <<= 8;
+ result += (unsigned char) s[0];
+
+ *u = result;
+}
+
+void uint16_unpack_big(const char s[2],uint16 *u)
+{
+ uint16 result;
+
+ result = (unsigned char) s[0];
+ result <<= 8;
+ result += (unsigned char) s[1];
+
+ *u = result;
+}
diff --git a/uint32.h1 b/uint32.h1
new file mode 100644
index 0000000..6ee0172
--- /dev/null
+++ b/uint32.h1
@@ -0,0 +1,11 @@
+#ifndef UINT32_H
+#define UINT32_H
+
+typedef unsigned int uint32;
+
+extern void uint32_pack(char *,uint32);
+extern void uint32_pack_big(char *,uint32);
+extern void uint32_unpack(const char *,uint32 *);
+extern void uint32_unpack_big(const char *,uint32 *);
+
+#endif
diff --git a/uint32.h2 b/uint32.h2
new file mode 100644
index 0000000..7df3ddb
--- /dev/null
+++ b/uint32.h2
@@ -0,0 +1,11 @@
+#ifndef UINT32_H
+#define UINT32_H
+
+typedef unsigned long uint32;
+
+extern void uint32_pack(char *,uint32);
+extern void uint32_pack_big(char *,uint32);
+extern void uint32_unpack(const char *,uint32 *);
+extern void uint32_unpack_big(const char *,uint32 *);
+
+#endif
diff --git a/uint32_pack.c b/uint32_pack.c
new file mode 100644
index 0000000..76bc670
--- /dev/null
+++ b/uint32_pack.c
@@ -0,0 +1,21 @@
+#include "uint32.h"
+
+void uint32_pack(char s[4],uint32 u)
+{
+ s[0] = u & 255;
+ u >>= 8;
+ s[1] = u & 255;
+ u >>= 8;
+ s[2] = u & 255;
+ s[3] = u >> 8;
+}
+
+void uint32_pack_big(char s[4],uint32 u)
+{
+ s[3] = u & 255;
+ u >>= 8;
+ s[2] = u & 255;
+ u >>= 8;
+ s[1] = u & 255;
+ s[0] = u >> 8;
+}
diff --git a/uint32_unpack.c b/uint32_unpack.c
new file mode 100644
index 0000000..f5635d3
--- /dev/null
+++ b/uint32_unpack.c
@@ -0,0 +1,31 @@
+#include "uint32.h"
+
+void uint32_unpack(const char s[4],uint32 *u)
+{
+ uint32 result;
+
+ result = (unsigned char) s[3];
+ result <<= 8;
+ result += (unsigned char) s[2];
+ result <<= 8;
+ result += (unsigned char) s[1];
+ result <<= 8;
+ result += (unsigned char) s[0];
+
+ *u = result;
+}
+
+void uint32_unpack_big(const char s[4],uint32 *u)
+{
+ uint32 result;
+
+ result = (unsigned char) s[0];
+ result <<= 8;
+ result += (unsigned char) s[1];
+ result <<= 8;
+ result += (unsigned char) s[2];
+ result <<= 8;
+ result += (unsigned char) s[3];
+
+ *u = result;
+}
diff --git a/uint64.h1 b/uint64.h1
new file mode 100644
index 0000000..206fc09
--- /dev/null
+++ b/uint64.h1
@@ -0,0 +1,8 @@
+#ifndef UINT64_H
+#define UINT64_H
+
+/* sysdep: -ulong64 */
+
+typedef unsigned long long uint64;
+
+#endif
diff --git a/uint64.h2 b/uint64.h2
new file mode 100644
index 0000000..8a0f315
--- /dev/null
+++ b/uint64.h2
@@ -0,0 +1,8 @@
+#ifndef UINT64_H
+#define UINT64_H
+
+/* sysdep: +ulong64 */
+
+typedef unsigned long uint64;
+
+#endif
diff --git a/utime.c b/utime.c
new file mode 100644
index 0000000..4b7984f
--- /dev/null
+++ b/utime.c
@@ -0,0 +1,24 @@
+#include <sys/types.h>
+#include <sys/time.h>
+#include "scan.h"
+#include "exit.h"
+
+char *fn;
+
+char *ustr;
+unsigned long u;
+time_t ut[2];
+
+int main(int argc,char **argv)
+{
+ fn = argv[1];
+ if (!fn) _exit(100);
+
+ ustr = argv[2];
+ if (!ustr) _exit(100);
+ scan_ulong(ustr,&u);
+
+ ut[0] = ut[1] = u;
+ if (utime(fn,ut) == -1) _exit(111);
+ _exit(0);
+}
diff --git a/walldns-conf.c b/walldns-conf.c
new file mode 100644
index 0000000..b46f19a
--- /dev/null
+++ b/walldns-conf.c
@@ -0,0 +1,58 @@
+#include <unistd.h>
+#include <pwd.h>
+#include "strerr.h"
+#include "exit.h"
+#include "auto_home.h"
+#include "generic-conf.h"
+
+#define FATAL "walldns-conf: fatal: "
+
+void usage(void)
+{
+ strerr_die1x(100,"walldns-conf: usage: walldns-conf acct logacct /walldns myip");
+}
+
+char *dir;
+char *user;
+char *loguser;
+struct passwd *pw;
+char *myip;
+
+int main(int argc,char **argv)
+{
+ user = argv[1];
+ if (!user) usage();
+ loguser = argv[2];
+ if (!loguser) usage();
+ dir = argv[3];
+ if (!dir) usage();
+ if (dir[0] != '/') usage();
+ myip = argv[4];
+ if (!myip) usage();
+
+ pw = getpwnam(loguser);
+ if (!pw)
+ strerr_die3x(111,FATAL,"unknown account ",loguser);
+
+ init(dir,FATAL);
+ makelog(loguser,pw->pw_uid,pw->pw_gid);
+
+ makedir("env");
+ perm(02755);
+ start("env/ROOT"); outs(dir); outs("/root\n"); finish();
+ perm(0644);
+ start("env/IP"); outs(myip); outs("\n"); finish();
+ perm(0644);
+
+ start("run");
+ outs("#!/bin/sh\nexec 2>&1\nexec envuidgid "); outs(user);
+ outs(" envdir ./env softlimit -d250000 ");
+ outs(auto_home); outs("/bin/walldns\n");
+ finish();
+ perm(0755);
+
+ makedir("root");
+ perm(02755);
+
+ _exit(0);
+}
diff --git a/walldns.c b/walldns.c
new file mode 100644
index 0000000..3cdaa72
--- /dev/null
+++ b/walldns.c
@@ -0,0 +1,57 @@
+#include "byte.h"
+#include "dns.h"
+#include "dd.h"
+#include "response.h"
+
+const char *fatal = "walldns: fatal: ";
+const char *starting = "starting walldns\n";
+
+void initialize(void)
+{
+ ;
+}
+
+int respond(char *q,char qtype[2])
+{
+ int flaga;
+ int flagptr;
+ char ip[4];
+ int j;
+
+ flaga = byte_equal(qtype,2,DNS_T_A);
+ flagptr = byte_equal(qtype,2,DNS_T_PTR);
+ if (byte_equal(qtype,2,DNS_T_ANY)) flaga = flagptr = 1;
+
+ if (flaga || flagptr) {
+ if (dd(q,"",ip) == 4) {
+ if (flaga) {
+ if (!response_rstart(q,DNS_T_A,655360)) return 0;
+ if (!response_addbytes(ip,4)) return 0;
+ response_rfinish(RESPONSE_ANSWER);
+ }
+ return 1;
+ }
+ j = dd(q,"\7in-addr\4arpa",ip);
+ if (j >= 0) {
+ if (flaga && (j == 4)) {
+ if (!response_rstart(q,DNS_T_A,655360)) return 0;
+ if (!response_addbytes(ip + 3,1)) return 0;
+ if (!response_addbytes(ip + 2,1)) return 0;
+ if (!response_addbytes(ip + 1,1)) return 0;
+ if (!response_addbytes(ip + 0,1)) return 0;
+ response_rfinish(RESPONSE_ANSWER);
+ }
+ if (flagptr) {
+ if (!response_rstart(q,DNS_T_PTR,655360)) return 0;
+ if (!response_addname(q)) return 0;
+ response_rfinish(RESPONSE_ANSWER);
+ }
+ return 1;
+ }
+ }
+
+ response[2] &= ~4;
+ response[3] &= ~15;
+ response[3] |= 5;
+ return 1;
+}
diff --git a/warn-auto.sh b/warn-auto.sh
new file mode 100644
index 0000000..36d2313
--- /dev/null
+++ b/warn-auto.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+# WARNING: This file was auto-generated. Do not edit!
diff --git a/warn-shsgr b/warn-shsgr
new file mode 100644
index 0000000..37c351e
--- /dev/null
+++ b/warn-shsgr
@@ -0,0 +1,3 @@
+Oops. Your getgroups() returned 0, and setgroups() failed; this means
+that I can't reliably do my shsgr test. Please either ``make'' as root
+or ``make'' while you're in one or more supplementary groups.
diff --git a/x86cpuid.c b/x86cpuid.c
new file mode 100644
index 0000000..98e37db
--- /dev/null
+++ b/x86cpuid.c
@@ -0,0 +1,38 @@
+#include <signal.h>
+
+void nope()
+{
+ exit(1);
+}
+
+int main()
+{
+ unsigned long x[4];
+ unsigned long y[4];
+ int i;
+ int j;
+ char c;
+
+ signal(SIGILL,nope);
+
+ x[0] = 0;
+ x[1] = 0;
+ x[2] = 0;
+ x[3] = 0;
+
+ asm volatile(".byte 15;.byte 162" : "=a"(x[0]),"=b"(x[1]),"=c"(x[3]),"=d"(x[2]) : "0"(0) );
+ if (!x[0]) return 0;
+ asm volatile(".byte 15;.byte 162" : "=a"(y[0]),"=b"(y[1]),"=c"(y[2]),"=d"(y[3]) : "0"(1) );
+
+ for (i = 1;i < 4;++i)
+ for (j = 0;j < 4;++j) {
+ c = x[i] >> (8 * j);
+ if (c < 32) c = 32;
+ if (c > 126) c = 126;
+ putchar(c);
+ }
+
+ printf("-%08x-%08x\n",y[0],y[3]);
+
+ return 0;
+}