Imported Upstream version 1.05 upstream upstream/1.05
authorClinton Ebadi <clinton@unknownlamer.org>
Wed, 30 Apr 2014 01:04:28 +0000 (21:04 -0400)
committerClinton Ebadi <clinton@unknownlamer.org>
Wed, 30 Apr 2014 01:04:28 +0000 (21:04 -0400)
243 files changed:
CHANGES [new file with mode: 0644]
FILES [new file with mode: 0644]
Makefile [new file with mode: 0644]
README [new file with mode: 0644]
SYSDEPS [new file with mode: 0644]
TARGETS [new file with mode: 0644]
TINYDNS [new file with mode: 0644]
TODO [new file with mode: 0644]
VERSION [new file with mode: 0644]
alloc.c [new file with mode: 0644]
alloc.h [new file with mode: 0644]
alloc_re.c [new file with mode: 0644]
auto-str.c [new file with mode: 0644]
auto_home.h [new file with mode: 0644]
axfr-get.c [new file with mode: 0644]
axfrdns-conf.c [new file with mode: 0644]
axfrdns.c [new file with mode: 0644]
buffer.c [new file with mode: 0644]
buffer.h [new file with mode: 0644]
buffer_1.c [new file with mode: 0644]
buffer_2.c [new file with mode: 0644]
buffer_copy.c [new file with mode: 0644]
buffer_get.c [new file with mode: 0644]
buffer_put.c [new file with mode: 0644]
buffer_read.c [new file with mode: 0644]
buffer_write.c [new file with mode: 0644]
byte.h [new file with mode: 0644]
byte_chr.c [new file with mode: 0644]
byte_copy.c [new file with mode: 0644]
byte_cr.c [new file with mode: 0644]
byte_diff.c [new file with mode: 0644]
byte_zero.c [new file with mode: 0644]
cache.c [new file with mode: 0644]
cache.h [new file with mode: 0644]
cachetest.c [new file with mode: 0644]
case.h [new file with mode: 0644]
case_diffb.c [new file with mode: 0644]
case_diffs.c [new file with mode: 0644]
case_lowerb.c [new file with mode: 0644]
cdb.c [new file with mode: 0644]
cdb.h [new file with mode: 0644]
cdb_hash.c [new file with mode: 0644]
cdb_make.c [new file with mode: 0644]
cdb_make.h [new file with mode: 0644]
chkshsgr.c [new file with mode: 0644]
choose.sh [new file with mode: 0644]
conf-cc [new file with mode: 0644]
conf-home [new file with mode: 0644]
conf-ld [new file with mode: 0644]
dd.c [new file with mode: 0644]
dd.h [new file with mode: 0644]
direntry.h1 [new file with mode: 0644]
direntry.h2 [new file with mode: 0644]
dns.h [new file with mode: 0644]
dns_dfd.c [new file with mode: 0644]
dns_domain.c [new file with mode: 0644]
dns_dtda.c [new file with mode: 0644]
dns_ip.c [new file with mode: 0644]
dns_ipq.c [new file with mode: 0644]
dns_mx.c [new file with mode: 0644]
dns_name.c [new file with mode: 0644]
dns_nd.c [new file with mode: 0644]
dns_packet.c [new file with mode: 0644]
dns_random.c [new file with mode: 0644]
dns_rcip.c [new file with mode: 0644]
dns_rcrw.c [new file with mode: 0644]
dns_resolve.c [new file with mode: 0644]
dns_sortip.c [new file with mode: 0644]
dns_transmit.c [new file with mode: 0644]
dns_txt.c [new file with mode: 0644]
dnscache-conf.c [new file with mode: 0644]
dnscache.c [new file with mode: 0644]
dnsfilter.c [new file with mode: 0644]
dnsip.c [new file with mode: 0644]
dnsipq.c [new file with mode: 0644]
dnsmx.c [new file with mode: 0644]
dnsname.c [new file with mode: 0644]
dnsq.c [new file with mode: 0644]
dnsqr.c [new file with mode: 0644]
dnsroots.global [new file with mode: 0644]
dnstrace.c [new file with mode: 0644]
dnstracesort.sh [new file with mode: 0644]
dnstxt.c [new file with mode: 0644]
droproot.c [new file with mode: 0644]
droproot.h [new file with mode: 0644]
env.c [new file with mode: 0644]
env.h [new file with mode: 0644]
error.c [new file with mode: 0644]
error.h [new file with mode: 0644]
error_str.c [new file with mode: 0644]
exit.h [new file with mode: 0644]
find-systype.sh [new file with mode: 0644]
fmt.h [new file with mode: 0644]
fmt_ulong.c [new file with mode: 0644]
gen_alloc.h [new file with mode: 0644]
gen_allocdefs.h [new file with mode: 0644]
generic-conf.c [new file with mode: 0644]
generic-conf.h [new file with mode: 0644]
getln.c [new file with mode: 0644]
getln.h [new file with mode: 0644]
getln2.c [new file with mode: 0644]
hasdevtcp.h1 [new file with mode: 0644]
hasdevtcp.h2 [new file with mode: 0644]
hasshsgr.h1 [new file with mode: 0644]
hasshsgr.h2 [new file with mode: 0644]
hier.c [new file with mode: 0644]
install.c [new file with mode: 0644]
instcheck.c [new file with mode: 0644]
iopause.c [new file with mode: 0644]
iopause.h1 [new file with mode: 0644]
iopause.h2 [new file with mode: 0644]
ip4.h [new file with mode: 0644]
ip4_fmt.c [new file with mode: 0644]
ip4_scan.c [new file with mode: 0644]
log.c [new file with mode: 0644]
log.h [new file with mode: 0644]
ndelay.h [new file with mode: 0644]
ndelay_off.c [new file with mode: 0644]
ndelay_on.c [new file with mode: 0644]
okclient.c [new file with mode: 0644]
okclient.h [new file with mode: 0644]
open.h [new file with mode: 0644]
open_read.c [new file with mode: 0644]
open_trunc.c [new file with mode: 0644]
openreadclose.c [new file with mode: 0644]
openreadclose.h [new file with mode: 0644]
parsetype.c [new file with mode: 0644]
parsetype.h [new file with mode: 0644]
pickdns-conf.c [new file with mode: 0644]
pickdns-data.c [new file with mode: 0644]
pickdns.c [new file with mode: 0644]
printpacket.c [new file with mode: 0644]
printpacket.h [new file with mode: 0644]
printrecord.c [new file with mode: 0644]
printrecord.h [new file with mode: 0644]
prot.c [new file with mode: 0644]
prot.h [new file with mode: 0644]
qlog.c [new file with mode: 0644]
qlog.h [new file with mode: 0644]
query.c [new file with mode: 0644]
query.h [new file with mode: 0644]
random-ip.c [new file with mode: 0644]
rbldns-conf.c [new file with mode: 0644]
rbldns-data.c [new file with mode: 0644]
rbldns.c [new file with mode: 0644]
readclose.c [new file with mode: 0644]
readclose.h [new file with mode: 0644]
response.c [new file with mode: 0644]
response.h [new file with mode: 0644]
roots.c [new file with mode: 0644]
roots.h [new file with mode: 0644]
rts.exp [new file with mode: 0644]
rts.sh [new file with mode: 0644]
rts.tests [new file with mode: 0644]
scan.h [new file with mode: 0644]
scan_ulong.c [new file with mode: 0644]
seek.h [new file with mode: 0644]
seek_set.c [new file with mode: 0644]
select.h1 [new file with mode: 0644]
select.h2 [new file with mode: 0644]
server.c [new file with mode: 0644]
sgetopt.c [new file with mode: 0644]
sgetopt.h [new file with mode: 0644]
socket.h [new file with mode: 0644]
socket_accept.c [new file with mode: 0644]
socket_bind.c [new file with mode: 0644]
socket_conn.c [new file with mode: 0644]
socket_listen.c [new file with mode: 0644]
socket_recv.c [new file with mode: 0644]
socket_send.c [new file with mode: 0644]
socket_tcp.c [new file with mode: 0644]
socket_udp.c [new file with mode: 0644]
str.h [new file with mode: 0644]
str_chr.c [new file with mode: 0644]
str_diff.c [new file with mode: 0644]
str_len.c [new file with mode: 0644]
str_rchr.c [new file with mode: 0644]
str_start.c [new file with mode: 0644]
stralloc.h [new file with mode: 0644]
stralloc_cat.c [new file with mode: 0644]
stralloc_catb.c [new file with mode: 0644]
stralloc_cats.c [new file with mode: 0644]
stralloc_copy.c [new file with mode: 0644]
stralloc_eady.c [new file with mode: 0644]
stralloc_num.c [new file with mode: 0644]
stralloc_opyb.c [new file with mode: 0644]
stralloc_opys.c [new file with mode: 0644]
stralloc_pend.c [new file with mode: 0644]
strerr.h [new file with mode: 0644]
strerr_die.c [new file with mode: 0644]
strerr_sys.c [new file with mode: 0644]
subgetopt.c [new file with mode: 0644]
subgetopt.h [new file with mode: 0644]
tai.h [new file with mode: 0644]
tai_add.c [new file with mode: 0644]
tai_now.c [new file with mode: 0644]
tai_pack.c [new file with mode: 0644]
tai_sub.c [new file with mode: 0644]
tai_uint.c [new file with mode: 0644]
tai_unpack.c [new file with mode: 0644]
taia.h [new file with mode: 0644]
taia_add.c [new file with mode: 0644]
taia_approx.c [new file with mode: 0644]
taia_frac.c [new file with mode: 0644]
taia_less.c [new file with mode: 0644]
taia_now.c [new file with mode: 0644]
taia_pack.c [new file with mode: 0644]
taia_sub.c [new file with mode: 0644]
taia_tai.c [new file with mode: 0644]
taia_uint.c [new file with mode: 0644]
tdlookup.c [new file with mode: 0644]
timeoutread.c [new file with mode: 0644]
timeoutread.h [new file with mode: 0644]
timeoutwrite.c [new file with mode: 0644]
timeoutwrite.h [new file with mode: 0644]
tinydns-conf.c [new file with mode: 0644]
tinydns-data.c [new file with mode: 0644]
tinydns-edit.c [new file with mode: 0644]
tinydns-get.c [new file with mode: 0644]
tinydns.c [new file with mode: 0644]
trycpp.c [new file with mode: 0644]
trydrent.c [new file with mode: 0644]
trylsock.c [new file with mode: 0644]
trypoll.c [new file with mode: 0644]
tryshsgr.c [new file with mode: 0644]
trysysel.c [new file with mode: 0644]
tryulong32.c [new file with mode: 0644]
tryulong64.c [new file with mode: 0644]
uint16.h [new file with mode: 0644]
uint16_pack.c [new file with mode: 0644]
uint16_unpack.c [new file with mode: 0644]
uint32.h1 [new file with mode: 0644]
uint32.h2 [new file with mode: 0644]
uint32_pack.c [new file with mode: 0644]
uint32_unpack.c [new file with mode: 0644]
uint64.h1 [new file with mode: 0644]
uint64.h2 [new file with mode: 0644]
utime.c [new file with mode: 0644]
walldns-conf.c [new file with mode: 0644]
walldns.c [new file with mode: 0644]
warn-auto.sh [new file with mode: 0644]
warn-shsgr [new file with mode: 0644]
x86cpuid.c [new file with mode: 0644]

diff --git a/CHANGES b/CHANGES
new file mode 100644 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
index 0000000..feb8b49
--- /dev/null
@@ -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 (file)
index 0000000..374af92
--- /dev/null
@@ -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 (file)
index 0000000..bd59284
--- /dev/null
@@ -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 (file)
index 0000000..75db627
--- /dev/null
@@ -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 (file)
index 0000000..4dc8657
--- /dev/null
@@ -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 (file)
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 (file)
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 (file)
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 (file)
index 0000000..2b6464a
--- /dev/null
@@ -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 (file)
index 0000000..268de19
--- /dev/null
@@ -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 (file)
index 0000000..dc4d4b1
--- /dev/null
@@ -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 (file)
index 0000000..937b75e
--- /dev/null
@@ -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 (file)
index 0000000..f875f3f
--- /dev/null
@@ -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 (file)
index 0000000..286a06c
--- /dev/null
@@ -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 (file)
index 0000000..fbd26d0
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..f81dde8
--- /dev/null
@@ -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 (file)
index 0000000..eaad11b
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..cdbd760
--- /dev/null
@@ -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 (file)
index 0000000..92009ba
--- /dev/null
@@ -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 (file)
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 (file)
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 (file)
index 0000000..c689862
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..b62a4b2
--- /dev/null
@@ -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 (file)
index 0000000..683977a
--- /dev/null
@@ -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 (file)
index 0000000..829c981
--- /dev/null
@@ -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 (file)
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 (file)
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 (file)
index 0000000..71102e1
--- /dev/null
@@ -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 (file)
index 0000000..278420f
--- /dev/null
@@ -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 (file)
index 0000000..49ea719
--- /dev/null
@@ -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 (file)
index 0000000..2b942d8
--- /dev/null
@@ -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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
index 0000000..446d5c7
--- /dev/null
@@ -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 (file)
index 0000000..d1628a9
--- /dev/null
@@ -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 (file)
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 (file)
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 (file)
index 0000000..b931f1d
--- /dev/null
@@ -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 (file)
index 0000000..ba1db4f
--- /dev/null
@@ -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 (file)
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 (file)
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 (file)
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 (file)
index 0000000..6f7cdc3
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..6d66eeb
--- /dev/null
@@ -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 (file)
index 0000000..2158ed4
--- /dev/null
@@ -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 (file)
index 0000000..97bd8f5
--- /dev/null
@@ -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 (file)
index 0000000..a43f39f
--- /dev/null
@@ -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 (file)
index 0000000..8bdea0d
--- /dev/null
@@ -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 (file)
index 0000000..af9b235
--- /dev/null
@@ -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 (file)
index 0000000..4d6e39f
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..e8c12be
--- /dev/null
@@ -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 (file)
index 0000000..8c899a3
--- /dev/null
@@ -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 (file)
index 0000000..9e6863a
--- /dev/null
@@ -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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
index 0000000..3b567e1
--- /dev/null
@@ -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 (file)
index 0000000..3f2159b
--- /dev/null
@@ -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 (file)
index 0000000..e57359c
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..33e8f18
--- /dev/null
@@ -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 (file)
index 0000000..b8a53a7
--- /dev/null
@@ -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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
index 0000000..74e1330
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..9f6e565
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..db48bfd
--- /dev/null
@@ -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 (file)
index 0000000..b94a956
--- /dev/null
@@ -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 (file)
index 0000000..5e136c0
--- /dev/null
@@ -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 (file)
index 0000000..db4aa1d
--- /dev/null
@@ -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 (file)
index 0000000..41dbeea
--- /dev/null
@@ -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 (file)
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 (file)
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 (file)
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 (file)
index 0000000..65e880e
--- /dev/null
@@ -0,0 +1 @@
+/* sysdep: -devtcp */
diff --git a/hasdevtcp.h2 b/hasdevtcp.h2
new file mode 100644 (file)
index 0000000..b12ffe8
--- /dev/null
@@ -0,0 +1,2 @@
+/* sysdep: +devtcp */
+#define HASDEVTCP 1
diff --git a/hasshsgr.h1 b/hasshsgr.h1
new file mode 100644 (file)
index 0000000..d11c988
--- /dev/null
@@ -0,0 +1 @@
+/* sysdep: -shortsetgroups */
diff --git a/hasshsgr.h2 b/hasshsgr.h2
new file mode 100644 (file)
index 0000000..db6a830
--- /dev/null
@@ -0,0 +1,2 @@
+/* sysdep: +shortsetgroups */
+#define HASSHORTSETGROUPS 1
diff --git a/hier.c b/hier.c
new file mode 100644 (file)
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 (file)
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 (file)
index 0000000..06ed547
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..dae0a33
--- /dev/null
@@ -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 (file)
index 0000000..2cf5cf8
--- /dev/null
@@ -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 (file)
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 (file)
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 (file)
index 0000000..e9538ec
--- /dev/null
@@ -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 (file)
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 (file)
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 (file)
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 (file)
index 0000000..9daa8cd
--- /dev/null
@@ -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 (file)
index 0000000..eccd8c8
--- /dev/null
@@ -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 (file)
index 0000000..a648c02
--- /dev/null
@@ -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 (file)
index 0000000..e9b7dd6
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..2a63a25
--- /dev/null
@@ -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 (file)
index 0000000..9d0f1dc
--- /dev/null
@@ -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 (file)
index 0000000..cbc5c6c
--- /dev/null
@@ -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 (file)
index 0000000..2d4042e
--- /dev/null
@@ -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 (file)
index 0000000..167aaa4
--- /dev/null
@@ -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 (file)
index 0000000..4851725
--- /dev/null
@@ -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 (file)
index 0000000..9edd184
--- /dev/null
@@ -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 (file)
index 0000000..60cabb0
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..7571e08
--- /dev/null
@@ -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 (file)
index 0000000..8c8946d
--- /dev/null
@@ -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 (file)
index 0000000..ed0b42d
--- /dev/null
@@ -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 (file)
index 0000000..f6bc9f7
--- /dev/null
@@ -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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
index 0000000..bfd516c
--- /dev/null
@@ -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 (file)
index 0000000..79d446f
--- /dev/null
@@ -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 (file)
index 0000000..ed495db
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..b9368cf
--- /dev/null
@@ -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 (file)
index 0000000..49afd6c
--- /dev/null
@@ -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 (file)
index 0000000..ba90c89
--- /dev/null
@@ -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 (file)
index 0000000..206b1d4
--- /dev/null
@@ -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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
index 0000000..d70b334
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..d08d4f3
--- /dev/null
@@ -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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
index 0000000..22c44d4
--- /dev/null
@@ -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 (file)
index 0000000..20830a4
--- /dev/null
@@ -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 (file)
index 0000000..46423cb
--- /dev/null
@@ -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 (file)
index 0000000..abdb483
--- /dev/null
@@ -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 (file)
index 0000000..8bc59c5
--- /dev/null
@@ -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 (file)
index 0000000..9ffbd5a
--- /dev/null
@@ -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 (file)
index 0000000..c200e2b
--- /dev/null
@@ -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 (file)
index 0000000..d71d3e4
--- /dev/null
@@ -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 (file)
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 (file)
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 (file)
index 0000000..071e7f5
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..b128c4c
--- /dev/null
@@ -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 (file)
index 0000000..757189d
--- /dev/null
@@ -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 (file)
index 0000000..d88f631
--- /dev/null
@@ -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 (file)
index 0000000..9bbb119
--- /dev/null
@@ -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 (file)
index 0000000..b606e32
--- /dev/null
@@ -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 (file)
index 0000000..92cb66e
--- /dev/null
@@ -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 (file)
index 0000000..6b9ae42
--- /dev/null
@@ -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 (file)
index 0000000..3a31f4b
--- /dev/null
@@ -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 (file)
index 0000000..64b25fa
--- /dev/null
@@ -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 (file)
index 0000000..593029d
--- /dev/null
@@ -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 (file)
index 0000000..860c7e0
--- /dev/null
@@ -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 (file)
index 0000000..a3443b8
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..ad93eb4
--- /dev/null
@@ -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 (file)
index 0000000..b484197
--- /dev/null
@@ -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 (file)
index 0000000..96c2631
--- /dev/null
@@ -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 (file)
index 0000000..65da0fb
--- /dev/null
@@ -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 (file)
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 (file)
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 (file)
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 (file)
index 0000000..0a2bc06
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..b01184c
--- /dev/null
@@ -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 (file)
index 0000000..b725ae0
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..3044a26
--- /dev/null
@@ -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 (file)
index 0000000..2a3b429
--- /dev/null
@@ -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 (file)
index 0000000..b6b48bc
--- /dev/null
@@ -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 (file)
index 0000000..2d889c8
--- /dev/null
@@ -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 (file)
index 0000000..ccc260d
--- /dev/null
@@ -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 (file)
index 0000000..89e2c16
--- /dev/null
@@ -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 (file)
index 0000000..6944689
--- /dev/null
@@ -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 (file)
index 0000000..ef4d4fc
--- /dev/null
@@ -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 (file)
index 0000000..167936c
--- /dev/null
@@ -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 (file)
index 0000000..da7420d
--- /dev/null
@@ -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 (file)
index 0000000..85a36e9
--- /dev/null
@@ -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 (file)
index 0000000..20d3bfc
--- /dev/null
@@ -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 (file)
index 0000000..4f665f1
--- /dev/null
@@ -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 (file)
index 0000000..4725861
--- /dev/null
@@ -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 (file)
index 0000000..d3a4ce5
--- /dev/null
@@ -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 (file)
index 0000000..ba82f84
--- /dev/null
@@ -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 (file)
index 0000000..126a7e0
--- /dev/null
@@ -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 (file)
index 0000000..f7fd67f
--- /dev/null
@@ -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 (file)
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 (file)
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 (file)
index 0000000..c778176
--- /dev/null
@@ -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 (file)
index 0000000..c32bd40
--- /dev/null
@@ -0,0 +1,4 @@
+int main()
+{
+  ;
+}
diff --git a/trypoll.c b/trypoll.c
new file mode 100644 (file)
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 (file)
index 0000000..81b395c
--- /dev/null
@@ -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 (file)
index 0000000..f6ed055
--- /dev/null
@@ -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 (file)
index 0000000..20683d6
--- /dev/null
@@ -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 (file)
index 0000000..479e4be
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..17dbfe6
--- /dev/null
@@ -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 (file)
index 0000000..518b9e3
--- /dev/null
@@ -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 (file)
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 (file)
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 (file)
index 0000000..76bc670
--- /dev/null
@@ -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 (file)
index 0000000..f5635d3
--- /dev/null
@@ -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 (file)
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 (file)
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 (file)
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 (file)
index 0000000..b46f19a
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..36d2313
--- /dev/null
@@ -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 (file)
index 0000000..37c351e
--- /dev/null
@@ -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 (file)
index 0000000..98e37db
--- /dev/null
@@ -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;
+}