]> Zhao Yanbai Git Server - minix.git/commitdiff
Retire MINIX nonamed(8) 37/3437/1
authorDavid van Moolenbroek <david@minix3.org>
Tue, 14 Feb 2017 17:34:20 +0000 (17:34 +0000)
committerDavid van Moolenbroek <david@minix3.org>
Thu, 9 Mar 2017 23:40:01 +0000 (23:40 +0000)
Also retire support for the MINIX versions of /etc/hosts and
/etc/resolv.conf.  These files will be brought back with NetBSD
imports, although like NetBSD, MINIX 3 will be using external
resolvers directly from then on.  Since resolv.conf is hand-created
rather than installed, we do not mark it as obsolete.

Change-Id: Ie6154d5a4d8d977c19b9754bf920ae868680e9d1

14 files changed:
distrib/sets/lists/minix-base/mi
distrib/sets/lists/minix-debug/mi
distrib/sets/lists/minix-man/mi
etc/Makefile
etc/resolv.conf [deleted file]
minix/commands/Makefile
minix/commands/nonamed/Makefile [deleted file]
minix/commands/nonamed/nonamed.8 [deleted file]
minix/commands/nonamed/nonamed.c [deleted file]
minix/man/man5/Makefile
minix/man/man5/hosts.5 [deleted file]
minix/man/man5/resolv.conf.5 [deleted file]
minix/man/man5/resolver.5 [deleted file]
minix/man/man7/hier.7

index 67c8d0c43121b19eded441aec431c586c1092fc7..7d53b7ced62b91007a9057fa5e0a398e6550cee5 100644 (file)
 ./etc/rc.shutdown                                       minix-base
 ./etc/rc.subr                                           minix-base
 ./etc/release                                           minix-base
-./etc/resolv.conf                                       minix-base
 ./etc/rs.inet                                           minix-base      obsolete
 ./etc/rs.single                                         minix-base
 ./etc/saslc.d                                           minix-base      crypto
 ./usr/bin/nice                                          minix-base
 ./usr/bin/nl                                            minix-base
 ./usr/bin/nohup                                         minix-base
-./usr/bin/nonamed                                       minix-base
+./usr/bin/nonamed                                       minix-base      obsolete
 ./usr/bin/nroff                                         minix-base
 ./usr/bin/od                                            minix-base
 ./usr/bin/openssl                                       minix-base      crypto
index d6eed1c280c008e48a9d123debe63bbf4a04ad10..45091f0b3d5ff6cc610ffd2a23610fb68921242a 100644 (file)
 ./usr/libdata/debug/usr/bin/nl.debug                    minix-debug     debug
 ./usr/libdata/debug/usr/bin/nm.debug                    minix-debug     debug
 ./usr/libdata/debug/usr/bin/nohup.debug                 minix-debug     debug
-./usr/libdata/debug/usr/bin/nonamed.debug               minix-debug     debug
+./usr/libdata/debug/usr/bin/nonamed.debug               minix-debug     debug,obsolete
 ./usr/libdata/debug/usr/bin/objcopy.debug               minix-debug     debug
 ./usr/libdata/debug/usr/bin/objdump.debug               minix-debug     debug
 ./usr/libdata/debug/usr/bin/openssl.debug               minix-debug     debug
index 78ef9f05d218eaf22945c82e117902663d46cce0..09b09732fb51eb4c21096e3a17455fbd487ef28e 100644 (file)
 ./usr/man/man5/ftpusers.5                               minix-man
 ./usr/man/man5/gettytab.5                               minix-man
 ./usr/man/man5/group.5                                  minix-man
-./usr/man/man5/hosts.5                                  minix-man
+./usr/man/man5/hosts.5                                  minix-man       obsolete
 ./usr/man/man5/http_status.5                            minix-man       obsolete
 ./usr/man/man5/httpd.conf.5                             minix-man       obsolete
 ./usr/man/man5/info.5                                   minix-man
 ./usr/man/man5/pkg_install.conf.5                       minix-man
 ./usr/man/man5/pkg_summary.5                            minix-man
 ./usr/man/man5/rc.conf.5                                minix-man
-./usr/man/man5/resolv.conf.5                            minix-man
-./usr/man/man5/resolver.5                               minix-man
+./usr/man/man5/resolv.conf.5                            minix-man       obsolete
+./usr/man/man5/resolver.5                               minix-man       obsolete
 ./usr/man/man5/rhosts.5                                 minix-man
 ./usr/man/man5/serv.access.5                            minix-man
 ./usr/man/man5/statvfs.5                                minix-man
 ./usr/man/man8/newfs_v7fs.8                             minix-man
 ./usr/man/man8/newroot.8                                minix-man       obsolete
 ./usr/man/man8/nologin.8                                minix-man
-./usr/man/man8/nonamed.8                                minix-man
+./usr/man/man8/nonamed.8                                minix-man       obsolete
 ./usr/man/man8/ossdevlinks.8                            minix-man       obsolete
 ./usr/man/man8/part.8                                   minix-man
 ./usr/man/man8/partition.8                              minix-man
index 1adef736a15ade5cbb933c6c12449b06eb0ab5fd..e25a70daeed568845415e7d95392b2279d3db8f0 100644 (file)
@@ -321,7 +321,6 @@ install-etc-files: .PHONY .MAKE check_DESTDIR MAKEDEV
                ${BINOWN} ${BINGRP}     ${BINMODE}      ${NETBSDSRCDIR}/etc/ ${DESTDIR}/etc/ group \
                ${BINOWN} ${BINGRP}     ${BINMODE}      ${NETBSDSRCDIR}/etc/ ${DESTDIR}/etc/ hostname.file \
                ${BINOWN} ${BINGRP}     ${BINMODE}      ${NETBSDSRCDIR}/etc/ ${DESTDIR}/etc/ mk.conf \
-               ${BINOWN} ${BINGRP}     ${BINMODE}      ${NETBSDSRCDIR}/etc/ ${DESTDIR}/etc/ resolv.conf \
                ${BINOWN} ${BINGRP}     ${BINMODE}      ${NETBSDSRCDIR}/etc/ ${DESTDIR}/etc/ motd \
                ${BINOWN} ${BINGRP}     ${BINMODE}      ${NETBSDSRCDIR}/etc/ ${DESTDIR}/etc/ rc.conf \
                ${BINOWN} ${BINGRP}     ${BINMODE}      ${NETBSDSRCDIR}/etc/ ${DESTDIR}/etc/ shrc \
diff --git a/etc/resolv.conf b/etc/resolv.conf
deleted file mode 100644 (file)
index bcc6e44..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-# Use Minix nonamed for now
-nameserver 127.0.0.1
index d38e5d5b97e5ae910a165ad50dcf0f0395f23a57..8befee314f85f94286eddec16fe8956782d9ea0f 100644 (file)
@@ -16,7 +16,6 @@ SUBDIR=       arp at backup \
        loadkeys loadramdisk logger look lp \
        lpd lspci mail MAKEDEV \
        minix-service mount mt netconf \
-       nonamed \
        prep printroot \
        profile progressbar \
        ramdisk rarpd rawspeed readclock \
diff --git a/minix/commands/nonamed/Makefile b/minix/commands/nonamed/Makefile
deleted file mode 100644 (file)
index b9a10b1..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-PROG=  nonamed
-MAN=   nonamed.8
-
-.include <bsd.prog.mk>
diff --git a/minix/commands/nonamed/nonamed.8 b/minix/commands/nonamed/nonamed.8
deleted file mode 100644 (file)
index a3e0155..0000000
+++ /dev/null
@@ -1,309 +0,0 @@
-.\" These numbers should match those in nonamed.c:
-.ds ST "two seconds"
-.ds MT "four seconds"
-.ds LT "five minutes"
-.ds HT "one hour"
-.ds NI "256"
-.TH NONAMED 8
-.SH NAME
-nonamed \- not a name daemon, but acts like one
-.SH SYNOPSIS
-.B nonamed
-.RB [ \-Lqs ]
-.RB [ \-d [\fIlevel\fP]]
-.RB [ \-p
-.IR port ]
-.SH DESCRIPTION
-.de SP
-.if t .sp 0.4
-.if n .sp
-..
-.B Nonamed
-is not a name daemon.  It can answer simple queries from
-.BR /etc/hosts ,
-but anything else is relayed to a real name daemon.
-.B Nonamed
-maintaines a small cache of replies it has seen from a name daemon, and will
-use this cache to minimize traffic if the machine is permanently connected
-to the Internet, or to answer requests if the machine is often disconnected
-from the Internet, i.e. a computer at home.
-.PP
-On startup
-.B nonamed
-sends a simple query to each of its name servers to see if one is up.  This
-is repeated every \*(LT in an "at home" situation, or when necessary if the
-current name daemon doesn't respond.  The first name server to answer is
-used as the current name server to answer queries.
-.PP
-If no name servers are found in the DHCP data or
-.BR /etc/hosts
-then only the hosts file is used to answer queries, and any query for a name
-not in that file gets a failure response.
-.PP
-.B Nonamed
-accepts both UDP and TCP queries under Minix-vmd.  Under standard MINIX 3
-only UDP queries are accepted.  \*(NI relayed UDP queries can be outstanding
-before it forgets where the first one came from.
-.PP
-Using the hosts file,
-.B nonamed
-can answer simple DNS queries to translate a host name to an IP address, or
-an IP address to a host name.  Suppose
-.B /etc/hosts
-looks like this:
-.PP
-.RS
-.ta +15n
-.nf
-10.0.0.1       flotsam.cs.vu.nl\0www
-10.0.0.2       jetsam.cs.vu.nl
-.fi
-.RE
-.PP
-Then queries for the host names listed can be answered with the IP addresses
-to the left of them.  An alias like "www" above is seen as a CNAME for the
-first host name on the line, in the same domain as the first host name if
-unqualified (no dots).  A reverse lookup for an IP address on the left is
-answered by the first host name on the right.  If more than one match is
-possible then all matches are put in the answer, so all IP addresses of
-multihomed hosts can be listed by multiple entries in the hosts file.
-.PP
-Requests for names like "flotsam.cs.vu.nl.cs.vu.nl" that are often generated
-on a domain search for an already fully qualified domain name
-are recognized and made to fail.  This kludge avoids a lot of unnecessary
-requests to possibly unreachable name servers and client timeouts.
-.PP
-The name "localhost" in any domain is given the IP address 127.0.0.1.
-.PP
-.B Nonamed
-employs several timeouts for efficient operation:
-.PP
-If no UDP reply is seen in \*(MT then a new search is started for a name
-server in the hope of finding one that does work.
-A failing TCP connection will also invoke a search, the
-TCP connection is then made to the new name server.  A client using UDP will
-retry eventually, a client using TCP will notice nothing but a short delay.
-If a TCP connection fails after 5 tries then an answer is sought in the
-hosts file, and failing that the connection is closed.
-.PP
-Any TCP operation is given \*(LT to show any action before the connection is
-aborted.
-.PP
-UDP replies from a name server are put in a cache of by default 8 (16-bit
-system) or 16 kilobytes (32-bit system).  New queries are
-first sought in the cache, and if found answered from the cache.  An entry
-in the cache is expired when the resource record with the smallest TTL (time
-to live) expires, unless its expire time is artificially extended by the
-"%stale" parameter (see below).  An answer from the cache has all TTLs
-appropriately lowered, and the AA bit ("answer authoritive") is cleared.
-Any request answered by stale data is refreshed as soon as
-.B nonamed
-notices that one of the external name daemons is reachable.
-.PP
-Data is only cached if it is has "no error" result code, or a "no such
-domain" result code with a SOA record in the name server section, and all
-records have a nonzero TTL.  The %stale parameter has no effect on the
-decision to cache a result.
-.PP
-The cache is rewritten to the cache file \*(LT after a new entry has been
-added.  Mere changes to the order in the cache don't cause a rewrite.
-.SS Configuration through /etc/hosts
-The real name servers, stale data extension, and cache size can be
-configured by special entries in the hosts file.  For example:
-.PP
-.RS
-.ta +\w'172.16.24.3'u+2m +\w'%nameserver'u+2m
-.nf
-86400  %ttl    # Answers from this file get this TTL
-2419200        %stale  # Stale data may linger on for 4 weeks
-32768  %memory # 32k cache size
-10.0.0.1       %nameserver     # flotsam
-172.16.24.3    %nameserver     # dns1.example.com
-172.16.24.6    %nameserver     # dns2.example.com
-.SP
-10.0.0.1       flotsam.home.example.com\0www
-10.0.0.2       jetsam.home.example.com
-.fi
-.RE
-.PP
-In this example we have two machines, flotsam and jetsam, that are at home.
-Answers from the hosts file get a TTL of one day, by default this is \*(HT.
-Normally there is no connection to the Internet, so any stale data in the
-cache is allowed to linger on for 2419200 seconds (4 weeks) before it is
-finally discarded.  The cache size is set to 32 kilobytes.  The first name
-server is the flotsam.  On the flotsam itself this entry is ignored, but the
-jetsam will now run its requests through flotsam if possible.  This means
-that both flotsam and jetsam use the cache of the flotsam.  The other
-nameserver entries are external name servers of the Internet provider.
-.PP
-If no nameservers are listed in the hosts file then they are obtained from
-data gathered by DHCP.  This is the preferred situation.
-.PP
-If the hosts file contains a line that says:
-.PP
-.RS
-.BI include " file"
-.RE
-.PP
-Then the current hosts file is closed and the file named is read next.
-.SS "Automatic calling"
-If your connection to the Internet is set up on demand, either in software
-on the machine that has the modem, or by a special box such as an ISDN
-router, then you need to filter the name server probes that
-.B nonamed
-sends out every \*(LT to see if a real name daemon is reachable.  These
-probes need to be recognized as packets that must not trigger a call, and
-that must not keep the line up.  You can either filter all IP packets
-destined for port 53 decimal (the
-.B domain
-port).  This may be a bit too much, the first packet out is often a normal
-DNS request (not a probe), so you may want to do better.  A probe by
-.B nonamed
-is a nonrecursive request for the name servers of the root domain.  You
-can recognize them by looking at the flags, they are all off.  Here is a
-typical probe in hex (twenty octets per line), followed by the names of
-interesting fields, and the octets values you should look for:
-.PP
-.RS
-.nf
-45 00 00 2D C8 19 00 00 1D 11 53 18 AC 10 66 41 AC 10 18 03
-00 35 00 35 00 19 79 93 00 00 00 00 00 01 00 00 00 00 00 00
-00 00 02 00 01
-.SP
-ip ip ip ip ip ip ip ip ip ip ip ip si si si si di di di di
-sp sp dp dp xx xx xx xx id id fl fl qd qd an an ns ns ar ar
-dn ty ty cl cl
-.SP
-45 xx xx xx xx xx xx xx xx 11 xx xx xx xx xx xx xx xx xx xx
-xx xx 00 35 xx xx xx xx xx xx 00 00 xx xx xx xx xx xx xx xx
-xx xx xx xx xx
-.SP
-.fi
-(ip = IP header, si = source IP, di = dest IP, sp = source port, dp = dest
-port, id = DNS ID, fl = DNS flags, qd = query count, an = answer count, ns =
-nameserver count, ar = additional records count, dn = domain (""), ty = type
-(NS), cl = class (IN).)
-.RE
-.PP
-So if a packet has octets 45, 11, 00 35, and 00 00 at the appropriate places
-then don't let it cause a call.  Read the documentation of your software/router
-to find out how to do this.  Hopefully it is possible to view the contents of
-the packet that triggered the last call.  If so you simply let
-.B nonamed
-bring up the line once with a probe.
-.SS "Remote information"
-The program version and name servers it is working with can be obtained with:
-.PP
-.RS
-host \-r \-v \-c chaos \-t txt version.bind. \fIserver\fP
-.RE
-.PP
-.I Server
-is the name or IP address of the host whose name server you want to know
-this of.
-(This call is really an undocumented hack to ask the version numbers of the
-BIND name daemon.  It just had to be implemented for
-.B nonamed
-as well.)
-.PP
-The % variables in the hosts file can be viewed like this:
-.PP
-.RS
-host \-r \-t a %nameserver. \fIserver\fP
-.RE
-.PP
-Don't forget the dot at the end of the name.  %ttl and %stale will be shown
-as a dotted quad, e.g. 0.36.234.0.  The proper value can be computed as 36 *
-65536 + 234 * 256 + 0 = 2419200.
-.SH OPTIONS
-The options are only useful when debugging
-.BR nonamed ,
-although it can be very instructive to watch DNS queries being done.
-.TP
-.BR \-d [\fIlevel\fP]
-Set debugging level to
-.I level
-(by default
-.BR 1 .)
-Debug mode 1 makes
-.B nonamed
-decode and display the DNS queries and replies that it receives, sends and
-relays.  In debug mode 2 it prints tracing information about the internal
-jobs it executes.  In debug mode 3 it core dumps when an error causes it to
-exit.  The debugging level may also be increased by 1 at runtime by sending
-signal
-.B SIGUSR1
-or turned off (set to 0) with
-.BR SIGUSR2 .
-.TP
-.RB [ \-L ]
-Only accept queries coming from the local host.
-.TP
-.RB [ \-p " \fIport\fP]
-Port to listen on instead of the normal
-.B domain
-port.
-.TP
-.RB [ \-q ]
-Read the cache file with the debug level set to 2, causing its contents to
-be printed, then exit.
-.TP
-.RB [ \-s ]
-Run single: ignore hosts or cache file, only use the DHCP information.  This
-allows another
-.B nonamed
-to be run on a different interface to serve a few programs that run there.
-.SH FILES
-.TP 15n
-/etc/hosts
-Hosts to address translation table and configuration file.
-.TP
-/usr/run/nonamed.pid
-Process ID of the currently running
-.BR nonamed .
-.TP
-/usr/adm/nonamed.cache
-Copy of the cache.  Read when the program starts, written \*(LT after
-something has been added to it, and written when a SIGTERM signal is
-received, which is normally sent at system shutdown.
-.TP
-/usr/adm/dhcp.cache
-Data gathered by the DHCP daemon.  Among lots of other junk it lists name
-servers that we should use.
-.SH "SEE ALSO"
-.BR gethostbyname (3),
-.BR resolver (3),
-.BR hosts (5),
-.BR inet (8),
-.BR boot (8),
-.BR inetd (8),
-.BR dhcpd (8).
-.SP
-.BR RFC-1034
-and
-.BR RFC-1035 .
-.SH NOTES
-Do not use the %stale parameter for a PC that is directly connected to the
-Internet.  You run the risk of getting wrong answers, a risk that is only
-worth taking for a system that is mostly disconnected from the Internet.
-.PP
-You can specify one or more remote name servers in
-.B /etc/resolv.conf
-so that nonamed isn't needed.  This will save memory, but you'll lose
-.BR nonamed 's
-cache and its "offline" tricks.  That's no problem if you can use a
-neighbouring name daemon on another PC at home.
-.PP
-The default cache size seems to be more than enough for normal use, but if
-you do decide to make it larger then don't forget to increase the stack size
-of the program under standard MINIX 3.
-.PP
-Don't let two
-.BR nonamed 's
-forward queries to each other.  They will pingpong a query over the
-network as fast as they can.
-.SH BUGS
-The idea of serving "stale DNS data" will probably make some purists
-violently sick...
-.SH AUTHOR
-Kees J. Bot (kjb@cs.vu.nl)
diff --git a/minix/commands/nonamed/nonamed.c b/minix/commands/nonamed/nonamed.c
deleted file mode 100644 (file)
index 5c8fe4c..0000000
+++ /dev/null
@@ -1,2227 +0,0 @@
-/*     nonamed - Not a name daemon, but plays one on TV.
- *                                                     Author: Kees J. Bot
- *                                                             29 Nov 1994
- */
-static const char version[] = "2.7";
-
-/* Use the file reading gethostent() family of functions. */
-#define sethostent     _sethostent
-#define gethostent     _gethostent
-#define endhostent     _endhostent
-
-#define nil ((void*)0)
-#include <sys/types.h>
-#include <stdio.h>
-#include <syslog.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <string.h>
-#include <time.h>
-#include <limits.h>
-#include <signal.h>
-#include <assert.h>
-#include <sys/stat.h>
-#include <sys/ioctl.h>
-#include <sys/asynchio.h>
-#include <netinet/in.h>
-#include <arpa/nameser.h>
-#include <net/hton.h>
-#include <net/netlib.h>
-#include <net/gen/in.h>
-#include <net/gen/inet.h>
-#include <arpa/nameser.h>
-#include <resolv.h>
-#include <netdb.h>
-#include <net/gen/socket.h>
-#include <net/gen/tcp.h>
-#include <net/gen/tcp_io.h>
-#include <net/gen/udp.h>
-#include <net/gen/udp_hdr.h>
-#include <net/gen/udp_io.h>
-#include <net/gen/dhcp.h>
-#include <arpa/inet.h>
-
-#include <paths.h>
-#include <minix/paths.h>
-
-#undef HTONL
-#undef HTONS
-#define HTONL htonl
-#define HTONS htons
-
-#define HTTL           3600L   /* Default time to live for /etc/hosts data. */
-#define SHORT_TIMEOUT     2    /* If you expect an answer soon. */
-#define MEDIUM_TIMEOUT    4    /* Soon, but not that soon. */
-#define LONG_TIMEOUT    300    /* For stream connections to a real named. */
-#define N_IDS           256    /* Keep track of this many queries. */
-#define N_DATAMAX (4096*sizeof(char *))        /* Default response cache size. */
-#define N_NAMEDS          8    /* Max # name daemons we can keep track of. */
-#define NO_FD          (-1)    /* No name daemon channel here. */
-#define T_NXD  ((u16_t) -1)    /* A "type" signalling a nonexistent domain. */
-
-/* Can't do async I/O under standard Minix, so forget about TCP. */
-#define DO_TCP (__minix_vmd || !__minix)
-
-/* Host data, file to store our process id in, our cache, DHCP's cache. */
-static char HOSTS[]=   _PATH_HOSTS;
-static char PIDFILE[]= "/usr/run/nonamed.pid";
-static char NNCACHE[]= "/usr/adm/nonamed.cache";
-static char DHCPCACHE[]= _PATH_DHCPCACHE;
-
-/* Magic string to head the cache file. */
-static char MAGIC[4]=  "NND\2";
-
-#define arraysize(a)   (sizeof(a) / sizeof((a)[0]))
-#define arraylimit(a)  ((a) + arraysize(a))
-#define between(a, c, z) ((unsigned) ((c) - (a)) <= (unsigned) ((z) - (a)))
-
-/* The start of time and the far future. */
-#define IMMEDIATE      ((time_t) 0)
-#define NEVER          ((time_t) ((time_t) -1 < 0 ? LONG_MAX : ULONG_MAX))
-
-static unsigned debug;         /* Debug level. */
-static time_t now;             /* Current time. */
-static u32_t stale;            /* Extension time for stale data. */
-static u32_t httl;             /* TTL for /etc/hosts data. */
-static int reinit, done;       /* Reinit config / program is done. */
-static int single;             /* Run single on a nondefault interface. */
-static int localonly;          /* Only accept local queries. */
-#define LOCALHOST      0x7F000001
-
-static void report(const char *label)
-{
-    fprintf(stderr, "nonamed: %s: %s\n", label, strerror(errno));
-}
-
-static void fatal(const char *label)
-{
-    report(label);
-    if (debug >= 3) { fflush(nil); abort(); }
-    exit(1);
-}
-
-static void *allocate(void *mem, size_t size)
-{
-    if ((mem= realloc(mem, size)) == nil) fatal("malloc()");
-    return mem;
-}
-
-static void deallocate(void *mem)
-{
-    free(mem);
-}
-
-static char *timegmt(time_t t)
-/* Simple "time in seconds to GMT time today" converter. */
-{
-    unsigned h, m, s;
-    static char asctime[sizeof("00:00:00")];
-
-    s= t % 60;
-    t /= 60;
-    m= t % 60;
-    t /= 60;
-    h= t % 24;
-    sprintf(asctime, "%02u:%02u:%02u", h, m, s);
-    return asctime;
-}
-
-static char *nowgmt(void)
-{
-    return timegmt(now);
-}
-
-static char *my_ntoa(ipaddr_t addr)
-{
-    return inet_ntoa(*(struct in_addr *)&addr);
-}
-
-#define PC(n)  ((void) sizeof(char [sizeof(*(n)) == 1]), (char *) (n))
-#define namecpy(n1, n2)                strcpy(PC(n1), PC(n2))
-#define namecat(n1, n2)                strcat(PC(n1), PC(n2))
-#define namechr(n, c)          ((u8_t *) strchr(PC(n), (c)))
-#define namecmp(n1, n2)                strcasecmp(PC(n1), PC(n2))
-#define namencmp(n1, n2, len)  strncasecmp(PC(n1), PC(n2), len)
-
-typedef struct dns {           /* A DNS packet. */
-       HEADER          hdr;            /* DNS header. */
-       u8_t            data[PACKETSZ - sizeof(HEADER)];        /* DNS data. */
-} dns_t;
-
-/* Addres of DNS packet to octet address, or vv. */
-#define dns2oct(dp)            ((u8_t *) (dp))
-#define oct2dns(dp)            ((dns_t *) (dp))
-
-typedef struct query {         /* One cached answer to a query. */
-       struct query    *less;          /* Less recently used. */
-       struct query    *more;          /* More recently used. */
-       time_t          age;            /* Time it was added. */
-       time_t          stale;          /* Time it goes stale by TTL. */
-       u16_t           usage;          /* Counts of queries answered. */
-       u8_t            flags;          /* QF_REFRESH. */
-       size_t          size;           /* Size of DNS packet. */
-       dns_t           dns;            /* Answer to query as a DNS packet. */
-} query_t;
-
-#define QF_REFRESH     0x01            /* This stale data must be refreshed. */
-#define QU_SHIFT       1               /* To shift usage by when evicting. */
-
-/* Size of new query_t or existing query_t. */
-#define query_allocsize(dnssize)       (offsetof(query_t, dns) + (dnssize))
-#define query_size(qp)                 query_allocsize((qp)->size)
-
-static query_t *mru, *lru;     /* Most and least recently used answers. */
-static int q_refresh;          /* Set when an entry needs refreshing. */
-
-static void pack16(u8_t *buf, u16_t s)
-/* Pack a 16 bit value into a byte array. */
-{
-    buf[0]= ((u8_t *) &s)[0];
-    buf[1]= ((u8_t *) &s)[1];
-}
-
-static void pack32(u8_t *buf, u32_t l)
-/* Pack a 32 bit value into a byte array. */
-{
-    buf[0]= ((u8_t *) &l)[0];
-    buf[1]= ((u8_t *) &l)[1];
-    buf[2]= ((u8_t *) &l)[2];
-    buf[3]= ((u8_t *) &l)[3];
-}
-
-static u16_t upack16(u8_t *buf)
-/* Unpack a 16 bit value from a byte array. */
-{
-    u16_t s;
-
-    ((u8_t *) &s)[0]= buf[0];
-    ((u8_t *) &s)[1]= buf[1];
-    return s;
-}
-
-static u32_t upack32(u8_t *buf)
-/* Unpack a 32 bit value from a byte array. */
-{
-    u32_t l;
-
-    ((u8_t *) &l)[0]= buf[0];
-    ((u8_t *) &l)[1]= buf[1];
-    ((u8_t *) &l)[2]= buf[2];
-    ((u8_t *) &l)[3]= buf[3];
-    return l;
-}
-
-/* Encoding of RRs: i(paddr), d(omain), l(ong), c(har), s(tring), (s)h(ort). */
-static char *encoding[] = {
-       "c*",           /* anything unknown is c* */
-       "i",            /* A */
-       "d",            /* NS */
-       "d",            /* MD */
-       "d",            /* MF */
-       "d",            /* CNAME */
-       "ddlllll",      /* SOA */
-       "d",            /* MB */
-       "d",            /* MG */
-       "d",            /* MR */
-       "c*",           /* NULL */
-       "icc*",         /* WKS */
-       "d",            /* PTR */
-       "ss",           /* HINFO */
-       "dd",           /* MINFO */
-       "hd",           /* MX */
-       "s*",           /* TXT */
-};
-
-static char *itoa(char *fmt, u32_t i)
-{
-    static char output[32 + 3 * sizeof(i)];
-
-    sprintf(output, fmt, (unsigned long) i);
-    return output;
-}
-
-static char *classname(unsigned class)
-/* Class name of a resource record, for debug purposes. */
-{
-    static char *classes[] = { "IN", "CS", "CHAOS", "HS" };
-
-    if ((class - C_IN) < arraysize(classes)) return classes[class - C_IN];
-    return itoa("C_%u", class);
-}
-
-static char *typename(unsigned type)
-/* Type name of a resource record, for debug purposes. */
-{
-    static char type_A[][6] = {
-       "A", "NS", "MD", "MF", "CNAME", "SOA", "MB", "MG", "MR", "NULL",
-       "WKS", "PTR", "HINFO", "MINFO", "MX", "TXT",
-    };
-    static char type_AXFR[][6] = {
-       "AXFR", "MAILB", "MAILA", "ANY",
-    };
-    if ((type - T_A) < arraysize(type_A)) return type_A[type - T_A];
-    if ((type - T_AXFR) < arraysize(type_AXFR)) return type_AXFR[type - T_AXFR];
-    return itoa("T_%u", type);
-}
-
-static int print_qrr(dns_t *dp, size_t size, u8_t *cp0, int q)
-/* Print a query (q) or resource record (!q) from 'cp0' in a DNS packet for
- * debug purposes.  Return number of bytes skipped or -1 on error.
- */
-{
-    u8_t name[MAXDNAME+1];
-    u8_t *cp;
-    char *ep;
-    u8_t *dlim, *rlim;
-    u16_t type, class, rdlength;
-    u32_t ttl;
-    int r;
-
-    cp= cp0;
-    dlim= dns2oct(dp) + size;
-    r= dn_expand(dns2oct(dp), dlim, cp, name, MAXDNAME);
-    if (r == -1) return -1;
-    cp += r;
-    if (cp + 2 * sizeof(u16_t) > dlim) return -1;
-    type= ntohs(upack16(cp));
-    cp += sizeof(u16_t);
-    class= ntohs(upack16(cp));
-    cp += sizeof(u16_t);
-    printf("%-25s", (char *) name);
-    if (q) {
-       /* We're just printing a query segment, stop right here. */
-       printf(" %8s", classname(class));
-       printf(" %-5s", typename(type));
-       return cp - cp0;
-    }
-    if (cp + sizeof(u32_t) + sizeof(u16_t) > dlim) return -1;
-    ttl= ntohl(upack32(cp));
-    cp += sizeof(u32_t);
-    rdlength= ntohs(upack16(cp));
-    cp += sizeof(u16_t);
-    if (cp + rdlength > dlim) return -1;
-    rlim = cp + rdlength;
-    printf(" %5lu", (unsigned long) ttl);
-    printf(" %s", classname(class));
-    printf(" %-5s", typename(type));
-    ep= type < arraysize(encoding) ? encoding[type] : encoding[0];
-    while (*ep != 0) {
-       switch (*ep++) {
-       case 'i':
-           if (cp + sizeof(u32_t) > rlim) return -1;
-           printf(" %s", my_ntoa(upack32(cp)));
-           cp += sizeof(u32_t);
-           break;
-       case 'l':
-           if (cp + sizeof(u32_t) > rlim) return -1;
-           printf(" %ld", (long)(i32_t) ntohl(upack32(cp)));
-           cp += sizeof(u32_t);
-           break;
-       case 'd':
-           r= dn_expand(dns2oct(dp), dlim, cp, name, MAXDNAME);
-           if (r == -1) return -1;
-           printf(" %s", (char *) name);
-           cp += r;
-           break;
-       case 'c':
-           if (cp >= rlim) return -1;
-           printf(" %02X", *cp++);
-           break;
-       case 's':
-           r= *cp + 1;
-           if (cp + r > rlim) return -1;
-           printf(" \"%.*s\"", *cp, (char *) (cp + 1));
-           cp += r;
-           break;
-       case 'h':
-           if (cp + sizeof(u16_t) > rlim) return -1;
-           printf(" %u", ntohs(upack16(cp)));
-           cp += sizeof(u16_t);
-           break;
-       }
-       if (*ep == '*') ep= cp < rlim ? ep-1 : ep+1;
-    }
-    return cp - cp0;
-}
-
-static void dns_tell(int indent, dns_t *dp, size_t size)
-/* Explain a DNS packet, for debug purposes. */
-{
-    u8_t *cp;
-    int r, i;
-    unsigned count[4];
-    static char label[4][4]= { "QD:", "AN:", "NS:", "AR:" };
-    static char rcodes[][9] = {
-       "NOERROR", "FORMERR", "SERVFAIL", "NXDOMAIN", "NOTIMP", "REFUSED"
-    };
-
-    if (size < sizeof(HEADER)) return;
-
-    printf("%*s", indent, "");
-    printf("DNS %s:", (dp->hdr.qr) ? "reply" : "query");
-    r = dp->hdr.rcode;
-    printf(" %s", r < arraysize(rcodes) ? rcodes[r] : itoa("ERR_%lu", r));
-    if (dp->hdr.aa) printf(" AA");
-    if (dp->hdr.tc) printf(" TC");
-    if (dp->hdr.rd) printf(" RD");
-    if (dp->hdr.ra) printf(" RA");
-    if (dp->hdr.ad) printf(" AD");
-    if (dp->hdr.cd) printf(" CD");
-    fputc('\n', stdout);
-
-    count[0]= ntohs(dp->hdr.qdcount);
-    count[1]= ntohs(dp->hdr.ancount);
-    count[2]= ntohs(dp->hdr.nscount);
-    count[3]= ntohs(dp->hdr.arcount);
-    cp = dp->data;
-    for (i= 0; i < 4; i++) {
-       while (count[i] > 0) {
-           printf("%*s", indent, "");
-           printf(" %s ", label[i]);
-           r= print_qrr(dp, size, cp, (i == 0));
-           fputc('\n', stdout);
-           if (r == -1) return;
-           cp += r;
-           count[i]--;
-       }
-    }
-}
-
-static u32_t dns_ttl(dns_t *dp, size_t size, u32_t delta)
-/* Compute the minimum TTL of all RRs in a DNS packet and subtract delta from
- * all TTLs.  (We are actually only interested in the minimum (delta = 0) or
- * the subtraction (delta > 0).  It was easier to roll this into one routine.)
- */
-{
-    u8_t *cp, *rdp, *dlim;
-    int r, i, hasttl, hassoa;
-    unsigned type, count[4];
-    u32_t ttl, minimum, minttl;
-    unsigned rcode;
-    u8_t name[MAXDNAME+1];
-
-    hasttl= hassoa= 0;
-    minttl= 365*24*3600L;
-    dlim= dns2oct(dp) + size;
-    if (size < sizeof(HEADER)) return 0;
-
-    rcode= dp->hdr.rcode;
-    count[0]= ntohs(dp->hdr.qdcount);
-    count[1]= ntohs(dp->hdr.ancount);
-    count[2]= ntohs(dp->hdr.nscount);
-    count[3]= ntohs(dp->hdr.arcount);
-    cp = dp->data;
-    for (i= 0; i < 4 && cp < dlim; i++) {
-       while (count[i] > 0) {
-           r= dn_expand(dns2oct(dp), dlim, cp, name, MAXDNAME);
-           if (r == -1) break;
-           cp += r + 2 * sizeof(u16_t);
-           if (i != 0) {
-               if (cp + sizeof(u32_t) + sizeof(u16_t) > dlim) break;
-               type= upack16(cp - 2 * sizeof(u16_t));
-               ttl= ntohl(upack32(cp));
-               ttl= ttl < delta ? 0 : ttl - delta;
-               if (rcode == NXDOMAIN && i == 2 && type == HTONS(T_SOA)) {
-                   rdp= cp + sizeof(u32_t) + sizeof(u16_t);
-                   r= dn_expand(dns2oct(dp), dlim, rdp, name, MAXDNAME);
-                   if (r == -1) break;
-                   rdp += r;
-                   r= dn_expand(dns2oct(dp), dlim, rdp, name, MAXDNAME);
-                   if (r == -1) break;
-                   rdp += r + 4 * sizeof(u32_t);
-                   if (rdp + sizeof(u32_t) > dlim) break;
-                   minimum= ntohl(upack32(rdp));
-                   if (ttl > minimum) ttl= minimum;
-                   hassoa= 1;
-               }
-               if (delta != 0) pack32(cp, htonl(ttl));
-               if (ttl < minttl) minttl= ttl;
-               hasttl= 1;
-               cp += sizeof(u32_t);
-               cp += sizeof(u16_t) + ntohs(upack16(cp));
-           }
-           count[i]--;
-       }
-    }
-    return ((rcode == NOERROR && hasttl) || (rcode == NXDOMAIN && hassoa))
-               ? minttl : 0;
-}
-
-/* Total cached query data. */
-static size_t n_datamax= N_DATAMAX;
-static size_t n_data;
-
-static query_t *extract_query(query_t *qp)
-/* Take a query out of the query cache. */
-{
-    assert(qp != nil);
-    *(qp->less != nil ? &qp->less->more : &lru) = qp->more;
-    *(qp->more != nil ? &qp->more->less : &mru) = qp->less;
-    n_data -= query_size(qp);
-    return qp;
-}
-
-static query_t *get_query(u8_t *name, unsigned type)
-/* Find a query and if so remove it from the cache and return it. */
-{
-    query_t *qp, *less;
-    u8_t qname[MAXDNAME+1];
-    int r;
-
-    for (qp= mru; qp != nil; qp= less) {
-       less= qp->less;
-       if (qp->stale <= now - stale) {
-           /* This answer has expired. */
-           deallocate(extract_query(qp));
-       } else {
-           r= dn_expand(dns2oct(&qp->dns), dns2oct(&qp->dns) + qp->size,
-               qp->dns.data, qname, MAXDNAME);
-           if (r == -1) continue;
-           if (namecmp(qname, name) == 0 && upack16(qp->dns.data+r) == type) {
-               /* Found an answer to the query. */
-               return extract_query(qp);
-           }
-       }
-    }
-    return nil;
-}
-
-static void insert_query(query_t *qp)
-/* (Re)insert a query into the cache. */
-{
-    *(qp->less != nil ? &qp->less->more : &lru) = qp;
-    *(qp->more != nil ? &qp->more->less : &mru) = qp;
-    n_data += query_size(qp);
-
-    /* Try to delete the LRU while there is too much memory in use.  If
-     * its usage count is too high then it gets a second chance.
-     */
-    while (n_data > n_datamax && lru != nil) {
-       if ((lru->usage >>= QU_SHIFT) == 0 || lru->stale <= now - stale) {
-           deallocate(extract_query(lru));
-       } else {
-           lru->less= mru;     /* Make list circular. */
-           mru->more= lru;
-           mru= lru;           /* Move one over, making LRU the MRU. */
-           lru= lru->more;
-           lru->less= nil;     /* Break the circle. */
-           mru->more= nil;
-       }
-    }
-
-    if (debug >= 2) {
-       unsigned n= 0;
-       for (qp= mru; qp != nil; qp= qp->less) n++;
-       printf("%u cached repl%s, %u bytes, sbrk(0) = %u\n",
-           n, n == 1 ? "y" : "ies",
-           (unsigned) n_data,
-           (unsigned) sbrk(0));
-    }
-}
-
-static void put_query(query_t *qp)
-/* Add a new query to the cache as the MRU. */
-{
-    qp->less= mru;
-    qp->more= nil;
-    insert_query(qp);
-}
-
-static void cache2file(void)
-/* Store the cached data into the cache file. */
-{
-    FILE *fp;
-    query_t *qp;
-    u8_t data[4+1+2+2];
-    u16_t usage;
-    char newcache[sizeof(NNCACHE) + sizeof(".new")];
-
-    if (single) return;
-
-    strcpy(newcache, NNCACHE);
-    strcat(newcache, ".new");
-
-    if ((fp= fopen(newcache, "w")) == nil) {
-       if ((errno != ENOENT && errno != EROFS) || debug >= 2) report(newcache);
-       return;
-    }
-    if (debug >= 2) printf("Writing %s:\n", newcache);
-
-    /* Magic number: */
-    fwrite(MAGIC, 1, sizeof(MAGIC), fp);
-
-    for (qp= lru; qp != nil; qp= qp->more) {
-       if (qp->stale <= now - stale) continue;
-       if (debug >= 2) {
-           printf("Usage = %u, Age = %ld, Flags = %02X:\n",
-               qp->usage, (long) (now - qp->age), qp->flags);
-           dns_tell(2, &qp->dns, qp->size);
-       }
-       pack32(data+0, htonl(qp->age));
-       data[4]= qp->flags;
-       pack16(data+5, htons(qp->size));
-       pack16(data+7, htons(qp->usage));
-       fwrite(data, 1, sizeof(data), fp);
-       fwrite(&qp->dns, 1, qp->size, fp);
-       if (ferror(fp)) break;
-    }
-
-    if (ferror(fp) || fclose(fp) == EOF) {
-       report(newcache);
-       (void) unlink(newcache);
-       return;
-    }
-
-    if (debug >= 2) printf("mv %s %s\n", newcache, NNCACHE);
-    if (rename(newcache, NNCACHE) < 0) {
-       fprintf(stderr, "nonamed: mv %s %s: %s\n",
-           newcache, NNCACHE, strerror(errno));
-       (void) unlink(newcache);
-    }
-}
-
-static void file2cache(void)
-/* Read cached data from the cache file. */
-{
-    query_t *qp;
-    FILE *fp;
-    u8_t data[4+1+2+2];
-    size_t dlen;
-
-    if (single) return;
-
-    if ((fp= fopen(NNCACHE, "r")) == nil) {
-       if (errno != ENOENT || debug >= 2) report(NNCACHE);
-       return;
-    }
-    if (debug >= 2) printf("Reading %s:\n", NNCACHE);
-
-    /* Magic number? */
-    fread(data, 1, sizeof(MAGIC), fp);
-    if (ferror(fp) || memcmp(MAGIC, data, sizeof(MAGIC)) != 0) goto err;
-
-    for (;;) {
-       fread(data, 1, sizeof(data), fp);
-       if (feof(fp) || ferror(fp)) break;
-       dlen= ntohs(upack16(data+5));
-       qp= allocate(nil, query_allocsize(dlen));
-       qp->age= htonl(upack32(data+0));
-       qp->flags= data[4];
-       if (qp->flags & QF_REFRESH) q_refresh= 1;
-       qp->size= dlen;
-       qp->usage= htons(upack16(data+7));
-       fread(&qp->dns, 1, qp->size, fp);
-       if (feof(fp) || ferror(fp)) {
-           deallocate(qp);
-           goto err;
-       }
-       qp->stale= qp->age + dns_ttl(&qp->dns, dlen, 0);
-       if (debug >= 2) {
-           printf("Usage = %u, Age = %ld, Flags = %02X:\n",
-               qp->usage, (long) (now - qp->age), qp->flags);
-           dns_tell(2, &qp->dns, dlen);
-       }
-       put_query(qp);
-    }
-    if (ferror(fp)) {
-    err:
-       /* The cache file did not end at EOF or is otherwise a mess. */
-       fprintf(stderr, "nonamed: %s: %s\n", NNCACHE,
-               ferror(fp) ? strerror(errno) : "Corrupt");
-       while (lru != nil) deallocate(extract_query(lru));
-    }
-    fclose(fp);
-}
-
-typedef int handler_t(void *data, int expired);
-
-/* All actions are in the form of "jobs". */
-typedef struct job {
-       struct job      *next, **prev;  /* To make a job queue. */
-       handler_t       *handler;       /* Function to handle this job. */
-       time_t          timeout;        /* Moment it times out. */
-       void            *data;          /* Data associated with the job. */
-} job_t;
-
-static job_t *queue;           /* Main job queue. */
-
-static void newjob(handler_t *handler, time_t timeout, void *data)
-/* Create a new job with the given handler, timeout time and data. */
-{
-    job_t *job, **prev;
-
-    job= allocate(nil, sizeof(*job));
-    job->handler= handler;
-    job->timeout= timeout;
-    job->data= data;
-
-    for (prev= &queue; *prev != nil; prev= &(*prev)->next) {
-       if (job->timeout < (*prev)->timeout) break;
-    }
-    job->next= *prev;
-    job->prev= prev;
-    *prev= job;
-    if (job->next != nil) job->next->prev= &job->next;
-}
-
-static int execjob(job_t *job, int expired)
-/* Execute a job by calling the handler.  Remove the job if it returns true,
- * indicating that it is done.  Expired is set if the job timed out.  It is
- * otherwise called to check for I/O.
- */
-{
-    if ((*job->handler)(job->data, expired)) {
-       *job->prev= job->next;
-       if (job->next != nil) job->next->prev= job->prev;
-       deallocate(job);
-       return 1;
-    }
-    return 0;
-}
-
-static void force_expire(handler_t *handler)
-/* Force jobs to expire immediately, the named searcher for instance. */
-{
-    job_t *job, **prev= &queue;
-
-    while ((job= *prev) != nil) {
-       if (job->handler == handler && job->timeout != IMMEDIATE) {
-           *prev= job->next;
-           if (job->next != nil) job->next->prev= prev;
-           newjob(job->handler, IMMEDIATE, job->data);
-           deallocate(job);
-       } else {
-           prev= &job->next;
-       }
-    }
-}
-
-static int nxdomain(u8_t *name)
-/* True iff the two top level components in a name are repeated in the name,
- * or if in-addr.arpa is found within a name.  Such things happen often in a
- * search for an already fully qualified local name.  For instance:
- * flotsam.cs.vu.nl.cs.vu.nl.  (We don't want this at boot time.)
- */
-{
-    u8_t *end, *top, *p;
-    size_t n;
-
-    end= namechr(name, 0);
-    top= end;
-    while (top > name && *--top != '.') {}
-    while (top > name && *--top != '.') {}
-    n= end - top;
-    p= top;
-    for (;;) {
-       if (p == name) return 0;
-       if (*--p == '.') {
-           if (namencmp(p, top, n) == 0 && p[n] == '.') return 1;
-           if (namencmp(p, ".in-addr.arpa.", 14) == 0) return 1;
-       }
-    }
-}
-
-typedef struct id2id {
-       u16_t           id;             /* ID of old query. */
-       u16_t           port;           /* Reply port. */
-       ipaddr_t        ip;             /* Reply address. */
-} id2id_t;
-
-static id2id_t id2id[N_IDS];
-static u16_t id_counter;
-
-static u16_t new_id(u16_t in_id, u16_t in_port, ipaddr_t in_ip)
-/* An incoming UDP query must be relabeled with a new ID before it can be
- * send on to a real name daemon.
- */
-{
-    id2id_t *idp;
-    u16_t id;
-
-    id= id_counter++;
-    idp= &id2id[id % N_IDS];
-    idp->id= in_id;
-    idp->port= in_port;
-    idp->ip= in_ip;
-    return htons(id);
-}
-
-static int old_id(u16_t id, u16_t *out_id, u16_t *out_port, ipaddr_t *out_ip)
-/* Translate a reply id back to the id, port, and address used in the query.
- * Return true if the translation is possible.
- */
-{
-    id= ntohs(id);
-    if ((u16_t) (id_counter - id) > N_IDS) {
-       /* Too old. */
-       return 0;
-    } else {
-       /* We know this one. */
-       id2id_t *idp= &id2id[id % N_IDS];
-
-       if (idp->port == 0) return 0;   /* Named is trying to fool us? */
-       *out_id= idp->id;
-       *out_port= idp->port;
-       *out_ip= idp->ip;
-       idp->port= 0;
-       return 1;
-    }
-}
-
-/* IDs used to mark my own queries to name servers, must be new_id translated
- * to make them unique "on the wire".
- */
-#define ID_IPSELF      HTONL(0)        /* "I did it myself" address. */
-#define ID_PROBE       HTONS(0)        /* Name server probe. */
-#define ID_REFRESH     HTONS(1)        /* Query to refresh a cache entry. */
-
-static char *tcp_device, *udp_device;  /* TCP and UDP device names. */
-static int udp_fd;                     /* To send or receive UDP packets. */
-static asynchio_t asyn;                        /* For I/O in progress. */
-static ipaddr_t my_ip;                 /* My IP address. */
-static u16_t my_port, named_port;      /* Port numbers, normally "domain". */
-
-static ipaddr_t named[N_NAMEDS];       /* Addresses of all name servers. */
-static unsigned n_nameds;              /* Number of configured name daemons. */
-static unsigned i_named;               /* Index to current name server. */
-static int expect;                     /* Set when we expect an answer. */
-static int search_ct= -1;              /* Named search count and state. */
-static int dirty;                      /* True when new entry put in cache. */
-
-#define current_named()                (+named[i_named])
-#define searching()            (search_ct > 0)
-#define start_searching()      ((void) (search_ct= -1))
-#define stop_searching()       ((void) (search_ct= 0))
-#define expecting()            (+expect)
-#define start_expecting()      ((void) (expect= 1))
-#define stop_expecting()       ((void) (expect= 0))
-
-static time_t filetime(const char *file)
-/* Get the modified time of a file. */
-{
-    struct stat st;
-
-    return stat(file, &st) == 0 ? st.st_mtime : 0;
-}
-
-static void init_config(ipaddr_t ifip)
-/* Read name daemon list and other special stuff from the hosts file. */
-{
-    struct hostent *he;
-    u32_t nip, hip;
-    static time_t hosts_time, dhcp_time;
-    time_t ht, dt;
-
-    /* See if anything really changed. */
-    if (((ifip ^ HTONL(LOCALHOST)) & HTONL(0xFF000000)) == 0) ifip= my_ip;
-    ht= filetime(HOSTS);
-    dt= filetime(DHCPCACHE);
-    if (ifip == my_ip && ht == hosts_time && dt == dhcp_time) return;
-    my_ip= ifip;
-    hosts_time= ht;
-    dhcp_time= dt;
-
-    if (debug >= 2) {
-       printf("%s: I am nonamed %s at %s:%u\n",
-           nowgmt(), version, my_ntoa(my_ip), ntohs(my_port));
-    }
-
-    httl= HTONL(HTTL);
-    stale= 0;
-    n_nameds= 0;
-
-    if (!single) {
-       sethostent(0);
-       while ((he= gethostent()) != nil) {
-           memcpy(&nip, he->h_addr, sizeof(u32_t));
-           hip= ntohl(nip);
-           if (namecmp(he->h_name, "%ttl") == 0) httl= nip;
-           if (namecmp(he->h_name, "%stale") == 0) stale= hip;
-           if (namecmp(he->h_name, "%memory") == 0) n_datamax= hip;
-           if (namecmp(he->h_name, "%nameserver") == 0) {
-               if (nip != my_ip || named_port != my_port) {
-                   if (n_nameds < N_NAMEDS) named[n_nameds++]= nip;
-               }
-           }
-       }
-       endhostent();
-    }
-
-    if (n_nameds == 0) {
-       /* No name daemons found in the host file.  What about DHCP? */
-       int fd;
-       dhcp_t d;
-       ssize_t r;
-       u8_t *data;
-       size_t len;
-
-       if ((fd= open(DHCPCACHE, O_RDONLY)) < 0) {
-           if (errno != ENOENT) fatal(DHCPCACHE);
-       } else {
-           while ((r= read(fd, &d, sizeof(d))) == sizeof(d)) {
-               if (d.yiaddr == my_ip) break;
-           }
-           if (r < 0) fatal(DHCPCACHE);
-           close(fd);
-
-           if (r == sizeof(d) && dhcp_gettag(&d, DHCP_TAG_DNS, &data, &len)) {
-               while (len >= sizeof(nip)) {
-                   memcpy(&nip, data, sizeof(nip));
-                   data += sizeof(nip);
-                   len -= sizeof(nip);
-                   if (nip != my_ip || named_port != my_port) {
-                       if (n_nameds < N_NAMEDS) named[n_nameds++]= nip;
-                   }
-               }
-           }
-       }
-    }
-    i_named= 0;
-}
-
-static handler_t job_save_cache, job_read_udp, job_find_named, job_expect_named;
-#if DO_TCP
-static handler_t job_setup_listen, job_listen, job_setup_connect, job_connect;
-static handler_t job_read_query, job_write_query;
-static handler_t job_read_reply, job_write_reply;
-#endif
-
-static int query_hosts(u8_t *qname, unsigned type, dns_t *dp, size_t *pdlen)
-/* Read the /etc/hosts file to try and answer an A or PTR query.  Return
- * true iff an answer can be found, with the answer copied to *dp.
- */
-{
-    struct hostent *he;
-    int i, r;
-    dns_t dns;
-    u8_t *domain;
-    u8_t *cp;
-    u8_t name[MAXDNAME+1];
-    u8_t *dnvec[40];
-    unsigned ancount;
-    struct hostent localhost;
-    static char *noaliases[]= { nil };
-    static ipaddr_t localaddr;
-    static char *localaddrlist[]= { (char *) &localaddr, nil };
-
-    localaddr = HTONL(LOCALHOST);
-
-    if (single) return 0;
-
-    /* Assume we can answer. */
-    dns.hdr.qr = 1;
-    dns.hdr.opcode = 0;
-    dns.hdr.aa = 1;
-    dns.hdr.tc = 0;
-    dns.hdr.rd = 0;
-    dns.hdr.ra = 1;
-    dns.hdr.unused = 0;
-    dns.hdr.ad = 0;
-    dns.hdr.cd = 0;
-    dns.hdr.rcode = 0;
-    dns.hdr.qdcount= HTONS(1);
-    ancount= 0;
-    dns.hdr.nscount= HTONS(0);
-    dns.hdr.arcount= HTONS(0);
-
-    dnvec[0]= dns2oct(&dns);
-    dnvec[1]= nil;
-    cp= dns.data;
-    r= dn_comp(qname, cp, arraysize(dns.data), dnvec, arraylimit(dnvec));
-    if (r == -1) return 0;
-    cp += r;
-    pack16(cp, type);
-    cp += sizeof(u16_t);
-    pack16(cp, HTONS(C_IN));
-    cp += sizeof(u16_t);
-
-    /* Localhost is fixed to 127.0.0.1. */
-    localhost.h_name=
-       namencmp(qname, "localhost.", 10) == 0 ? (char *) qname : "localhost";
-    localhost.h_aliases= noaliases;
-    localhost.h_addr_list= localaddrlist;
-    he= &localhost;
-
-    sethostent(0);
-    do {
-       int type_host = NTOHS(type);
-       switch (type_host) {
-       case T_A:
-           if (namecmp(qname, he->h_name) == 0) {
-             addA:
-               r= dn_comp((u8_t *) he->h_name, cp, arraylimit(dns.data) - cp,
-                   dnvec, arraylimit(dnvec));
-               if (r == -1) return 0;
-               cp += r;
-               if (cp + 3 * sizeof(u16_t) + 2 * sizeof(u32_t)
-                   > arraylimit(dns.data)) { r= -1; break; }
-               pack16(cp, HTONS(T_A));
-               cp += sizeof(u16_t);
-               pack16(cp, HTONS(C_IN));
-               cp += sizeof(u16_t);
-               pack32(cp, httl);
-               cp += sizeof(u32_t);
-               pack16(cp, HTONS(sizeof(u32_t)));
-               cp += sizeof(u16_t);
-               memcpy(cp, he->h_addr, sizeof(u32_t));
-               cp += sizeof(u32_t);
-               ancount++;
-               break;
-           }
-           /*FALL THROUGH*/
-       case T_CNAME:
-           domain= namechr(he->h_name, '.');
-           for (i= 0; he->h_aliases[i] != nil; i++) {
-               namecpy(name, he->h_aliases[i]);
-               if (domain != nil && namechr(name, '.') == nil) {
-                   namecat(name, domain);
-               }
-               if (namecmp(qname, name) == 0) {
-                   r= dn_comp(name, cp, arraylimit(dns.data) - cp,
-                       dnvec, arraylimit(dnvec));
-                   if (r == -1) break;
-                   cp += r;
-                   if (cp + 3 * sizeof(u16_t)
-                       + 1 * sizeof(u32_t) > arraylimit(dns.data)) return 0;
-                   pack16(cp, HTONS(T_CNAME));
-                   cp += sizeof(u16_t);
-                   pack16(cp, HTONS(C_IN));
-                   cp += sizeof(u16_t);
-                   pack32(cp, httl);
-                   cp += sizeof(u32_t);
-                   /* pack16(cp, htonl(RDLENGTH)) */
-                   cp += sizeof(u16_t);
-                   r= dn_comp((u8_t *) he->h_name, cp,
-                       arraylimit(dns.data) - cp,
-                       dnvec, arraylimit(dnvec));
-                   if (r == -1) break;
-                   pack16(cp - sizeof(u16_t), htons(r));
-                   cp += r;
-                   ancount++;
-                   if (type == HTONS(T_A)) goto addA;  /* really wants A */
-                   break;
-               }
-           }
-           break;
-       case T_PTR:
-           if (ancount > 0) break;
-           if (he->h_name[0] == '%') break;
-           sprintf((char *) name, "%d.%d.%d.%d.in-addr.arpa",
-                   ((u8_t *) he->h_addr)[3],
-                   ((u8_t *) he->h_addr)[2],
-                   ((u8_t *) he->h_addr)[1],
-                   ((u8_t *) he->h_addr)[0]);
-           if (namecmp(qname, name) == 0) {
-               r= dn_comp(name, cp, arraylimit(dns.data) - cp,
-                   dnvec, arraylimit(dnvec));
-               if (r == -1) break;
-               cp += r;
-               if (cp + 3 * sizeof(u16_t) + 1 * sizeof(u32_t)
-                   > arraylimit(dns.data)) { r= -1; break; }
-               pack16(cp, HTONS(T_PTR));
-               cp += sizeof(u16_t);
-               pack16(cp, HTONS(C_IN));
-               cp += sizeof(u16_t);
-               pack32(cp, httl);
-               cp += sizeof(u32_t);
-               /* pack16(cp, htonl(RDLENGTH)) */
-               cp += sizeof(u16_t);
-               r= dn_comp((u8_t *) he->h_name, cp,
-                   arraylimit(dns.data) - cp, dnvec, arraylimit(dnvec));
-               if (r == -1) return 0;
-               pack16(cp - sizeof(u16_t), htons(r));
-               cp += r;
-               ancount++;
-           }
-           break;
-       }
-    } while (r != -1 && (he= gethostent()) != nil);
-    endhostent();
-
-    if (r == -1 || ancount == 0) return 0;
-
-    dns.hdr.ancount= htons(ancount);
-    memcpy(dp, &dns, *pdlen= cp - dns2oct(&dns));
-    return 1;
-}
-
-static int query_chaos(u8_t *qname, unsigned type, dns_t *dp, size_t *pdlen)
-/* Report my version.  Can't let BIND take all the credit. :-) */
-{
-    int i, n, r;
-    dns_t dns;
-    u8_t *cp;
-    u8_t *dnvec[40];
-
-    if (type != HTONS(T_TXT) || namecmp(qname, "version.bind") != 0) return 0;
-
-    dns.hdr.qr = 1;
-    dns.hdr.opcode = 0;
-    dns.hdr.aa = 1;
-    dns.hdr.tc = 0;
-    dns.hdr.rd = 0;
-    dns.hdr.ra = 1;
-    dns.hdr.unused = 0;
-    dns.hdr.ad = 0;
-    dns.hdr.cd = 0;
-    dns.hdr.rcode = 0;
-    dns.hdr.qdcount= HTONS(1);
-    dns.hdr.ancount= HTONS(1);
-    dns.hdr.nscount= HTONS(0);
-    dns.hdr.arcount= htons(n_nameds);
-
-    dnvec[0]= dns2oct(&dns);
-    dnvec[1]= nil;
-    cp= dns.data;
-    r= dn_comp(qname, cp, arraysize(dns.data), dnvec, arraylimit(dnvec));
-    if (r == -1) return 0;
-    cp += r;
-    pack16(cp, type);
-    cp += sizeof(u16_t);
-    pack16(cp, HTONS(C_CHAOS));
-    cp += sizeof(u16_t);
-
-    r= dn_comp(qname, cp, arraylimit(dns.data) - cp, dnvec, arraylimit(dnvec));
-    if (r == -1) return 0;
-    cp += r;
-    pack16(cp, HTONS(T_TXT));
-    cp += sizeof(u16_t);
-    pack16(cp, HTONS(C_CHAOS));
-    cp += sizeof(u16_t);
-    pack32(cp, HTONL(0));
-    cp += sizeof(u32_t);
-    /* pack16(cp, htonl(RDLENGTH)) */
-    cp += sizeof(u16_t);
-    sprintf((char *) cp + 1, "nonamed %s at %s:%u",
-           version, my_ntoa(my_ip), ntohs(my_port));
-    r= strlen((char *) cp + 1) + 1;
-    pack16(cp - sizeof(u16_t), htons(r));
-    *cp= r-1;
-    cp += r;
-    for (n= 0, i= i_named; n < n_nameds; n++, i= (i+1) % n_nameds) {
-       r= dn_comp((u8_t *) "%nameserver", cp, arraylimit(dns.data) - cp,
-           dnvec, arraylimit(dnvec));
-       if (r == -1) return 0;
-       cp += r;
-       if (cp + 3 * sizeof(u16_t)
-           + 2 * sizeof(u32_t) > arraylimit(dns.data)) return 0;
-       pack16(cp, HTONS(T_A));
-       cp += sizeof(u16_t);
-       pack16(cp, HTONS(C_IN));
-       cp += sizeof(u16_t);
-       pack32(cp, HTONL(0));
-       cp += sizeof(u32_t);
-       pack16(cp, HTONS(sizeof(u32_t)));
-       cp += sizeof(u16_t);
-       memcpy(cp, &named[i], sizeof(u32_t));
-       cp += sizeof(u32_t);
-    }
-
-    memcpy(dp, &dns, *pdlen= cp - dns2oct(&dns));
-    return 1;
-}
-
-static void cache_reply(dns_t *dp, size_t dlen)
-/* Store a DNS packet in the cache. */
-{
-    int r;
-    query_t *qp, *less, *more;
-    unsigned usage;
-    u16_t type;
-    u8_t *cp;
-    u8_t name[MAXDNAME];
-    u32_t minttl;
-
-    if ((dp->hdr.rd && !dp->hdr.tc)) return;
-    if (dp->hdr.qdcount != HTONS(1)) return;
-    cp= dp->data;
-    r= dn_expand(dns2oct(dp), dns2oct(dp) + dlen, cp, name, MAXDNAME);
-    if (r == -1) return;
-    cp += r;
-    type= upack16(cp);
-    cp += sizeof(u16_t);
-    if (upack16(cp) != HTONS(C_IN)) return;
-
-    /* Delete old cached data, if any.  Note where it is in the LRU. */
-    if ((qp= get_query(name, type)) != nil) {
-       less= qp->less;
-       more= qp->more;
-       usage= qp->usage;
-       deallocate(qp);
-    } else {
-       /* Not yet in the cache. */
-       less= mru;
-       more= nil;
-       usage= 1;
-    }
-
-    /* Determine minimum TTL.  Discard if zero, never cache zero TTLs. */
-    if ((minttl= dns_ttl(dp, dlen, 0)) == 0) return;
-
-    /* Enter new reply in cache. */
-    qp= allocate(nil, query_allocsize(dlen));
-    qp->less= less;
-    qp->more= more;
-    qp->age= now;
-    qp->flags= 0;
-    qp->usage= usage;
-    qp->size= dlen;
-    memcpy(&qp->dns, dp, dlen);
-    qp->stale= qp->age + minttl;
-    insert_query(qp);
-    if (debug >= 1) printf("Answer cached\n");
-
-    /* Save the cache soon. */
-    if (!dirty) {
-       dirty= 1;
-       newjob(job_save_cache, now + LONG_TIMEOUT, nil);
-    }
-}
-
-static int job_save_cache(void *data, int expired)
-/* Some time after the cache is changed it is written back to disk. */
-{
-    if (!expired) return 0;
-    cache2file();
-    dirty= 0;
-    return 1;
-}
-
-static int compose_reply(dns_t *dp, size_t *pdlen)
-/* Try to compose a reply to a request in *dp using the hosts file or
- * cached data.  Return answer in *dp with its size in *pdlen.  Return true
- * iff an answer is given.
- */
-{
-    size_t dlen= *pdlen;
-    int r, rd;
-    query_t *qp;
-    unsigned id, type, class;
-    u8_t *cp;
-    u8_t name[MAXDNAME];
-
-    cp= dp->data;
-    r= dn_expand(dns2oct(dp), dns2oct(dp) + dlen, cp, name, MAXDNAME);
-    if (r != -1) {
-       cp += r;
-       if (cp + 2 * sizeof(u16_t) > dns2oct(dp) + dlen) {
-           r= -1;
-       } else {
-           type= upack16(cp);
-           cp += sizeof(u16_t);
-           class= upack16(cp);
-           cp += sizeof(u16_t);
-       }
-    }
-
-    /* Remember ID and RD. */
-    id= dp->hdr.id;
-    rd= dp->hdr.rd;
-
-    if (r == -1) {
-       /* Malformed query, reply "FORMERR". */
-       dp->hdr.tc = 0;
-       dp->hdr.qr = 1;
-       dp->hdr.aa = 1;
-       dp->hdr.unused = 0;
-       dp->hdr.ra = 1;
-       dp->hdr.rcode = FORMERR;
-    } else
-    if (class == HTONS(C_IN) && query_hosts(name, type, dp, pdlen)) {
-       /* Answer to this query is in the hosts file. */
-       dlen= *pdlen;
-    } else
-    if (class == HTONS(C_IN) && (qp= get_query(name, type)) != nil) {
-       /* Answer to this query is present in the cache. */
-       memcpy(dp, &qp->dns, dlen= qp->size);
-       dp->hdr.aa = 1;
-       (void) dns_ttl(dp, dlen, now - qp->age);
-       if (rd) {
-           if (qp->stale <= now) {
-               qp->flags |= QF_REFRESH;
-               q_refresh= 1;
-           }
-           qp->usage++;
-       }
-       put_query(qp);
-    } else
-    if (class == HTONS(C_CHAOS) && query_chaos(name, type, dp, pdlen)) {
-       /* Return our version numbers. */
-       dlen= *pdlen;
-    } else
-    if (n_nameds == 0 || nxdomain(name)) {
-       /* No real name daemon present, or this name has a repeated top level
-        * domain sequence.  Reply "no such domain".
-        */
-       dp->hdr.tc = 0;
-       dp->hdr.qr = 1;
-       dp->hdr.aa = 1;
-       dp->hdr.unused = 0;
-       dp->hdr.ra = 1;
-       dp->hdr.rcode = NXDOMAIN;
-    } else
-    if (!rd) {
-       /* "Recursion Desired" is off, so don't bother to relay. */
-       dp->hdr.tc = 0;
-       dp->hdr.qr = 1;
-       dp->hdr.unused = 0;
-       dp->hdr.ra = 1;
-       dp->hdr.rcode = NOERROR;
-    } else {
-       /* Caller needs to consult with a real name daemon. */
-       return 0;
-    }
-
-    /* Copy ID and RD back to answer. */
-    dp->hdr.id= id;
-    dp->hdr.rd = rd;
-    *pdlen= dlen;
-    return 1;
-}
-
-typedef struct udp_dns {       /* One DNS packet over UDP. */
-       udp_io_hdr_t    hdr;            /* UDP header (source/destination). */
-       dns_t           dns;            /* DNS packet. */
-} udp_dns_t;
-
-static void refresh_cache(void)
-/* Find a stale entry in the cache that was used to answer a query, and send
- * a request to a name server that should refresh this entry.
- */
-{
-    query_t *qp;
-    unsigned type;
-    int r;
-    u8_t *cp;
-    size_t dlen, ulen;
-    u8_t qname[MAXDNAME+1];
-    u8_t *dnvec[40];
-    udp_dns_t udp;
-
-    if (!q_refresh) return;
-    for (qp= lru; qp != nil; qp= qp->more) {
-       if ((qp->flags & QF_REFRESH) && qp->stale > now - stale) break;
-    }
-    if (qp == nil) {
-       q_refresh= 0;
-       return;
-    }
-
-    /* Found one to refresh. */
-    qp->flags &= ~QF_REFRESH;
-    r= dn_expand(dns2oct(&qp->dns), dns2oct(&qp->dns) + qp->size,
-       qp->dns.data, qname, MAXDNAME);
-    if (r == -1) return;
-    type= upack16(qp->dns.data+r);
-
-    dnvec[0]= dns2oct(&udp.dns);
-    dnvec[1]= nil;
-    cp= udp.dns.data;
-    r= dn_comp(qname, cp, arraysize(udp.dns.data), dnvec, arraylimit(dnvec));
-    if (r == -1) return;
-    cp += r;
-    pack16(cp, type);
-    cp += sizeof(u16_t);
-    pack16(cp, HTONS(C_IN));
-    cp += sizeof(u16_t);
-    dlen= cp - dns2oct(&udp.dns);
-
-    udp.dns.hdr.id= new_id(ID_REFRESH, my_port, ID_IPSELF);
-    udp.dns.hdr.qr = 0;
-    udp.dns.hdr.opcode = 0;
-    udp.dns.hdr.aa = 0;
-    udp.dns.hdr.tc = 0;
-    udp.dns.hdr.rd = 1;
-
-    udp.dns.hdr.ra = 0;
-    udp.dns.hdr.unused = 0;
-    udp.dns.hdr.ad = 0;
-    udp.dns.hdr.cd = 0;
-    udp.dns.hdr.rcode = 0;
-    udp.dns.hdr.qdcount= HTONS(1);
-    udp.dns.hdr.ancount= HTONS(0);
-    udp.dns.hdr.nscount= HTONS(0);
-    udp.dns.hdr.arcount= HTONS(0);
-
-    udp.hdr.uih_dst_addr= current_named();
-    udp.hdr.uih_dst_port= named_port;
-    udp.hdr.uih_ip_opt_len= 0;
-    udp.hdr.uih_data_len= dlen;
-
-    if (debug >= 1) {
-       printf("Refresh to %s:%u:\n",
-           my_ntoa(current_named()), ntohs(named_port));
-       dns_tell(0, &udp.dns, dlen);
-    }
-    ulen= offsetof(udp_dns_t, dns) + dlen;
-    if (write(udp_fd, &udp, ulen) < 0) fatal(udp_device);
-}
-
-static int job_read_udp(void *data, int expired)
-/* Read UDP queries and replies. */
-{
-    ssize_t ulen;
-    size_t dlen;
-    static udp_dns_t udp;
-    u16_t id, port;
-    ipaddr_t ip;
-    time_t dtime;
-
-    assert(!expired);
-
-    /* Try to read a packet. */
-    ulen= asyn_read(&asyn, udp_fd, &udp, sizeof(udp));
-    dlen= ulen - offsetof(udp_dns_t, dns);
-
-    if (ulen == -1) {
-       if (errno == EINPROGRESS && !expired) return 0;
-       if (errno == EIO) fatal(udp_device);
-
-       if (debug >= 2) {
-           printf("%s: UDP read: %s\n", nowgmt(), strerror(errno));
-       }
-    } else {
-       if (debug >= 2) {
-           printf("%s: UDP read, %d bytes\n", nowgmt(), (int) ulen);
-       }
-    }
-
-    /* Restart this job no matter what. */
-    newjob(job_read_udp, NEVER, nil);
-
-    if (ulen < (ssize_t) (sizeof(udp_io_hdr_t) + sizeof(HEADER))) return 1;
-
-    if (debug >= 1) {
-       printf("%s:%u UDP ", my_ntoa(udp.hdr.uih_src_addr),
-                               ntohs(udp.hdr.uih_src_port));
-       dns_tell(0, &udp.dns, dlen);
-    }
-
-    /* Check, and if necessary reinitialize my configuration. */
-    init_config(udp.hdr.uih_dst_addr);
-
-    if (udp.dns.hdr.qr) {
-       /* This is a remote named reply, not a query. */
-
-       /* Response to a query previously relayed? */
-       if (!old_id(udp.dns.hdr.id, &id, &port, &ip)) return 1;
-
-       if (ip == ID_IPSELF && id == ID_PROBE) {
-           if (searching()) {
-               /* We have found a name server! */
-               int i;
-
-               /* In my list? */
-               for (i= 0; i < n_nameds; i++) {
-                   if (named[i] == udp.hdr.uih_src_addr) {
-                       i_named= i;
-                       if (debug >= 1) {
-                           printf("Current named = %s\n",
-                               my_ntoa(current_named()));
-                       }
-                       stop_searching();
-                       force_expire(job_find_named);
-                   }
-               }
-           }
-       }
-
-       /* We got an answer, so stop worrying. */
-       if (expecting()) {
-           stop_expecting();
-           force_expire(job_expect_named);
-       }
-
-       /* Put the information in the cache. */
-       cache_reply(&udp.dns, dlen);
-
-       /* Refresh a cached entry that was used when stale. */
-       refresh_cache();
-
-       /* Discard reply to myself. */
-       if (ip == ID_IPSELF) return 1;
-
-       /* Send the reply to the process that asked for it. */
-       udp.dns.hdr.id= id;
-       udp.hdr.uih_dst_addr= ip;
-       udp.hdr.uih_dst_port= port;
-       if (debug >= 1) printf("To client %s:%u\n", my_ntoa(ip), ntohs(port));
-    } else {
-       /* A query. */
-       if (udp.dns.hdr.qdcount != HTONS(1)) return 1;
-
-       if(localonly) {
-               /* Check if it's a local query. */
-               if(ntohl(udp.hdr.uih_src_addr) != LOCALHOST) {
-                       syslog(LOG_WARNING, "nonamed: dropped query from %s",
-                               my_ntoa(udp.hdr.uih_src_addr));
-                       return 1;
-               }
-       }
-
-       /* Try to compose a reply from local data. */
-       if (compose_reply(&udp.dns, &dlen)) {
-           udp.hdr.uih_dst_addr= udp.hdr.uih_src_addr;
-           udp.hdr.uih_dst_port= udp.hdr.uih_src_port;
-           udp.hdr.uih_ip_opt_len= 0;
-           udp.hdr.uih_data_len= dlen;
-           ulen= offsetof(udp_dns_t, dns) + dlen;
-
-           /* Send an UDP DNS reply. */
-           if (debug >= 1) {
-               printf("%s:%u UDP ", my_ntoa(udp.hdr.uih_dst_addr),
-                                           ntohs(udp.hdr.uih_dst_port));
-               dns_tell(0, &udp.dns, dlen);
-           }
-       } else {
-           /* Let a real name daemon handle the query. */
-           udp.dns.hdr.id= new_id(udp.dns.hdr.id,
-                               udp.hdr.uih_src_port, udp.hdr.uih_src_addr);
-           udp.hdr.uih_dst_addr= current_named();
-           udp.hdr.uih_dst_port= named_port;
-           if (!expecting()) {
-               start_expecting();
-               newjob(job_expect_named, now + MEDIUM_TIMEOUT, nil);
-           }
-           if (debug >= 1) {
-               printf("To named %s:%u\n",
-                   my_ntoa(current_named()), ntohs(named_port));
-           }
-       }
-    }
-    if (write(udp_fd, &udp, ulen) < 0) fatal(udp_device);
-    return 1;
-}
-
-#if DO_TCP
-
-typedef struct data_cl {       /* Data for connect or listen jobs. */
-       int             fd;             /* Open TCP channel. */
-       int             dn_fd;          /* TCP channel to the name daemon. */
-       int             retry;          /* Retrying a connect? */
-       nwio_tcpcl_t    tcpcl;          /* Flags. */
-} data_cl_t;
-
-typedef struct data_rw {       /* Data for TCP read or write jobs. */
-       int             r_fd;           /* Read from this TCP channel. */
-       int             w_fd;           /* And write to this TCP channel. */
-       struct data_rw  *rev;           /* Optional reverse TCP channel. */
-       u8_t            *buf;           /* Buffer for bytes to transfer. */
-       ssize_t         offset;         /* Offset in buf to r/w at. */
-       size_t          size;           /* Size of buf. */
-} data_rw_t;
-
-static int job_setup_listen(void *data, int expired)
-/* Set up a listening channel for TCP DNS queries. */
-{
-    data_cl_t *data_cl= data;
-    nwio_tcpconf_t tcpconf;
-    nwio_tcpopt_t tcpopt;
-    int fd;
-
-    if (!expired) return 0;
-    if (debug >= 2) printf("%s: Setup listen\n", nowgmt());
-
-    if (data_cl == nil) {
-       if ((fd= open(tcp_device, O_RDWR)) < 0) {
-           if (errno != EMFILE) report(tcp_device);
-           newjob(job_setup_listen, now + SHORT_TIMEOUT, nil);
-           return 1;
-       }
-
-       tcpconf.nwtc_flags= NWTC_SHARED | NWTC_LP_SET | NWTC_UNSET_RA
-                                                       | NWTC_UNSET_RP;
-       tcpconf.nwtc_locport= my_port;
-       if (ioctl(fd, NWIOSTCPCONF, &tcpconf) == -1) fatal(tcp_device);
-
-       tcpopt.nwto_flags= NWTO_DEL_RST;
-       if (ioctl(fd, NWIOSTCPOPT, &tcpopt) == -1) fatal(tcp_device);
-
-       data_cl= allocate(nil, sizeof(*data_cl));
-       data_cl->fd= fd;
-       data_cl->tcpcl.nwtcl_flags= 0;
-    }
-    /* And listen. */
-    newjob(job_listen, NEVER, data_cl);
-    return 1;
-}
-
-static int job_listen(void *data, int expired)
-/* A connection on the TCP DNS query channel. */
-{
-    data_cl_t *data_cl= data;
-
-    /* Wait for a client. */
-    if (asyn_ioctl(&asyn, data_cl->fd, NWIOTCPLISTEN, &data_cl->tcpcl) < 0) {
-       if (errno == EINPROGRESS) return 0;
-       report(tcp_device);
-
-       /* Try again after a short time. */
-       newjob(job_setup_listen, now + SHORT_TIMEOUT, data_cl);
-       return 1;
-    }
-    if (debug >= 2) printf("%s: Listen\n", nowgmt());
-
-    /* Immediately resume listening. */
-    newjob(job_setup_listen, IMMEDIATE, nil);
-
-    /* Set up a connect to the real name daemon. */
-    data_cl->retry= 0;
-    newjob(job_setup_connect, IMMEDIATE, data_cl);
-    return 1;
-}
-
-static void start_relay(int fd, int dn_fd)
-/* Start one or two read jobs after job_setup_connect() or job_connect(). */
-{
-    data_rw_t *query;  /* Client to DNS daemon relay. */
-    data_rw_t *reply;  /* DNS daemon to client relay. */
-
-    query= allocate(nil, sizeof(*query));
-    query->r_fd= fd;
-    query->buf= allocate(nil, sizeof(u16_t));
-    query->offset= 0;
-    query->size= sizeof(u16_t);
-    if (dn_fd == NO_FD) {
-       /* Answer mode. */
-       query->w_fd= fd;
-       query->rev= nil;
-    } else {
-       /* Relay mode. */
-       reply= allocate(nil, sizeof(*reply));
-       reply->r_fd= dn_fd;
-       reply->w_fd= fd;
-       reply->buf= allocate(nil, sizeof(u16_t));
-       reply->offset= 0;
-       reply->size= sizeof(u16_t);
-       reply->rev= query;
-       query->w_fd= dn_fd;
-       query->rev= reply;
-       newjob(job_read_reply, now + LONG_TIMEOUT, reply);
-    }
-    newjob(job_read_query, now + LONG_TIMEOUT, query);
-}
-
-static void close_relay(data_rw_t *data_rw)
-/* Close a relay channel. */
-{
-    if (data_rw->rev != nil) {
-       /* Other end still active, signal EOF. */
-       (void) ioctl(data_rw->w_fd, NWIOTCPSHUTDOWN, nil);
-       data_rw->rev->rev= nil;
-    } else {
-       /* Close both ends down. */
-       asyn_close(&asyn, data_rw->r_fd);
-       close(data_rw->r_fd);
-       if (data_rw->w_fd != data_rw->r_fd) {
-           asyn_close(&asyn, data_rw->w_fd);
-           close(data_rw->w_fd);
-       }
-    }
-    deallocate(data_rw->buf);
-    deallocate(data_rw);
-}
-
-static int job_setup_connect(void *data, int expired)
-/* Set up a connect for a TCP channel to the real name daemon. */
-{
-    nwio_tcpconf_t tcpconf;
-    int dn_fd;
-    data_cl_t *data_cl= data;
-
-    if (!expired) return 0;
-    if (debug >= 2) printf("%s: Setup connect\n", nowgmt());
-
-    if (n_nameds == 0) {
-       /* No name daemons to relay to, answer myself. */
-       start_relay(data_cl->fd, NO_FD);
-       deallocate(data_cl);
-       return 1;
-    }
-
-    if ((dn_fd= open(tcp_device, O_RDWR)) < 0) {
-       if (errno != EMFILE) report(tcp_device);
-       if (++data_cl->retry < 5) {
-           /* Retry. */
-           newjob(job_setup_connect, now + SHORT_TIMEOUT, data_cl);
-       } else {
-           /* Reply myself (bound to fail). */
-           start_relay(data_cl->fd, NO_FD);
-           deallocate(data_cl);
-       }
-       return 1;
-    }
-
-    tcpconf.nwtc_flags= NWTC_LP_SEL | NWTC_SET_RA | NWTC_SET_RP;
-    tcpconf.nwtc_remaddr= current_named();
-    tcpconf.nwtc_remport= named_port;
-    if (ioctl(dn_fd, NWIOSTCPCONF, &tcpconf) == -1) fatal(tcp_device);
-
-    /* And connect. */
-    data_cl->dn_fd= dn_fd;
-    data_cl->tcpcl.nwtcl_flags= 0;
-    newjob(job_connect, NEVER, data_cl);
-    return 1;
-}
-
-static int job_connect(void *data, int expired)
-/* Connect to a TCP DNS query channel. */
-{
-    data_cl_t *data_cl= data;
-
-    /* Try to connect. */
-    if (asyn_ioctl(&asyn, data_cl->dn_fd, NWIOTCPCONN, &data_cl->tcpcl) < 0) {
-       if (errno == EINPROGRESS) return 0;
-       if (errno == EIO) fatal(tcp_device);
-
-       /* Connection refused. */
-       if (debug >= 2) printf("%s: Connect: %s\n", nowgmt(), strerror(errno));
-       asyn_close(&asyn, data_cl->dn_fd);
-       close(data_cl->dn_fd);
-       data_cl->dn_fd= NO_FD;
-       if (++data_cl->retry < 5) {
-           /* Search a new name daemon. */
-           if (!searching()) {
-               start_searching();
-               force_expire(job_find_named);
-           }
-           newjob(job_setup_connect, NEVER, data_cl);
-           return 1;
-       }
-       /* Reply with a failure eventually. */
-    }
-    if (debug >= 2) printf("%s: Connect\n", nowgmt());
-
-    /* Read the query from the user, send on to the name daemon, etc. */
-    start_relay(data_cl->fd, data_cl->dn_fd);
-    deallocate(data_cl);
-    return 1;
-}
-
-static void tcp_dns_tell(int fd, u8_t *buf)
-/* Tell about a DNS packet on a TCP channel. */
-{
-    nwio_tcpconf_t tcpconf;
-
-    if (ioctl(fd, NWIOGTCPCONF, &tcpconf) < 0) {
-       printf("??\?:?? TCP ");
-    } else {
-       printf("%s:%u TCP ", my_ntoa(tcpconf.nwtc_remaddr),
-                               ntohs(tcpconf.nwtc_remport));
-    }
-    dns_tell(0, oct2dns(buf + sizeof(u16_t)), ntohs(upack16(buf)));
-}
-
-static int job_read_query(void *data, int expired)
-/* Read TCP queries from the client. */
-{
-    data_rw_t *data_rw= data;
-    ssize_t count;
-
-    /* Try to read count bytes. */
-    count= asyn_read(&asyn, data_rw->r_fd,
-                               data_rw->buf + data_rw->offset,
-                               data_rw->size - data_rw->offset);
-
-    if (count < 0) {
-       if (errno == EINPROGRESS && !expired) return 0;
-       if (errno == EIO) fatal(tcp_device);
-
-       /* Remote end is late, or an error occurred. */
-       if (debug >= 2) {
-           printf("%s: TCP read query: %s\n", nowgmt(), strerror(errno));
-       }
-       close_relay(data_rw);
-       return 1;
-    }
-
-    if (debug >= 2) {
-       printf("%s: TCP read query, %d/%u bytes\n",
-           nowgmt(), data_rw->offset + count, data_rw->size);
-    }
-    if (count == 0) {
-       /* EOF. */
-       close_relay(data_rw);
-       return 1;
-    }
-    data_rw->offset += count;
-    if (data_rw->offset == data_rw->size) {
-       data_rw->size= sizeof(u16_t) + ntohs(upack16(data_rw->buf));
-       if (data_rw->size < sizeof(u16_t)) {
-           /* Malformed. */
-           close_relay(data_rw);
-           return 1;
-       }
-       if (data_rw->offset < data_rw->size) {
-           /* Query not complete, read more. */
-           data_rw->buf= allocate(data_rw->buf, data_rw->size);
-           newjob(job_read_query, now + LONG_TIMEOUT, data_rw);
-           return 1;
-       }
-    }
-
-    if (data_rw->size < sizeof(u16_t) + sizeof(dns_hdr_t)) {
-       close_relay(data_rw);
-       return 1;
-    }
-    if (debug >= 1) tcp_dns_tell(data_rw->r_fd, data_rw->buf);
-
-    /* Relay or reply. */
-    if (data_rw->w_fd != data_rw->r_fd) {
-       /* We have a real name daemon to do the work. */
-       data_rw->offset= 0;
-       newjob(job_write_query, now + LONG_TIMEOUT, data_rw);
-    } else {
-       /* No real name daemons or none reachable, so use the hosts file. */
-       dns_t *dp;
-       size_t dlen;
-
-       if (data_rw->size < sizeof(u16_t) + PACKETSZ) {
-           data_rw->buf= allocate(data_rw->buf, sizeof(u16_t) + PACKETSZ);
-       }
-
-       /* Build a reply packet. */
-       dp= oct2dns(data_rw->buf + sizeof(u16_t));
-       dlen= data_rw->size - sizeof(u16_t);
-       if (!compose_reply(dp, &dlen)) {
-           /* We're told to ask a name daemon, but that won't work. */
-           close_relay(data_rw);
-           return 1;
-       }
-
-       /* Start a reply write. */
-       pack16(data_rw->buf, htons(dlen));
-       data_rw->size= sizeof(u16_t) + dlen;
-       data_rw->buf= allocate(data_rw->buf, data_rw->size);
-       data_rw->offset= 0;
-       newjob(job_write_reply, now + LONG_TIMEOUT, data_rw);
-    }
-    return 1;
-}
-
-static int job_write_query(void *data, int expired)
-/* Relay a TCP query to the name daemon. */
-{
-    data_rw_t *data_rw= data;
-    ssize_t count;
-
-    /* Try to write count bytes to the name daemon. */
-    count= asyn_write(&asyn, data_rw->w_fd,
-                               data_rw->buf + data_rw->offset,
-                               data_rw->size - data_rw->offset);
-
-    if (count <= 0) {
-       if (errno == EINPROGRESS && !expired) return 0;
-       if (errno == EIO) fatal(tcp_device);
-
-       /* A write expired or failed (usually a broken connection.) */
-       if (debug >= 2) {
-           printf("%s: TCP write query: %s\n", nowgmt(), strerror(errno));
-       }
-       close_relay(data_rw);
-       return 1;
-    }
-
-    if (debug >= 2) {
-       printf("%s: TCP write query, %d/%u bytes\n",
-           nowgmt(), data_rw->offset + count, data_rw->size);
-    }
-    data_rw->offset += count;
-    if (data_rw->offset < data_rw->size) {
-       /* Partial write, continue. */
-       newjob(job_write_query, now + LONG_TIMEOUT, data_rw);
-       return 1;
-    }
-    if (debug >= 1) tcp_dns_tell(data_rw->w_fd, data_rw->buf);
-
-    /* Query fully send on, go read more queries. */
-    data_rw->offset= 0;
-    data_rw->size= sizeof(u16_t);
-    newjob(job_read_query, now + LONG_TIMEOUT, data_rw);
-    return 1;
-}
-
-static int job_read_reply(void *data, int expired)
-/* Read a TCP reply from the real name daemon. */
-{
-    data_rw_t *data_rw= data;
-    ssize_t count;
-
-    /* Try to read count bytes. */
-    count= asyn_read(&asyn, data_rw->r_fd,
-                               data_rw->buf + data_rw->offset,
-                               data_rw->size - data_rw->offset);
-
-    if (count < 0) {
-       if (errno == EINPROGRESS && !expired) return 0;
-       if (errno == EIO) fatal(tcp_device);
-
-       /* Remote end is late, or an error occurred. */
-       if (debug >= 2) {
-           printf("%s: TCP read reply: %s\n", nowgmt(), strerror(errno));
-       }
-       close_relay(data_rw);
-       return 1;
-    }
-
-    if (debug >= 2) {
-       printf("%s: TCP read reply, %d/%u bytes\n",
-           nowgmt(), data_rw->offset + count, data_rw->size);
-    }
-    if (count == 0) {
-       /* EOF. */
-       close_relay(data_rw);
-       return 1;
-    }
-    data_rw->offset += count;
-    if (data_rw->offset == data_rw->size) {
-       data_rw->size= sizeof(u16_t) + ntohs(upack16(data_rw->buf));
-       if (data_rw->size < sizeof(u16_t)) {
-           /* Malformed. */
-           close_relay(data_rw);
-           return 1;
-       }
-       if (data_rw->offset < data_rw->size) {
-           /* Reply not complete, read more. */
-           data_rw->buf= allocate(data_rw->buf, data_rw->size);
-           newjob(job_read_reply, now + LONG_TIMEOUT, data_rw);
-           return 1;
-       }
-    }
-    if (debug >= 1) tcp_dns_tell(data_rw->r_fd, data_rw->buf);
-
-    /* Reply fully read, send it on. */
-    data_rw->offset= 0;
-    newjob(job_write_reply, now + LONG_TIMEOUT, data_rw);
-    return 1;
-}
-
-static int job_write_reply(void *data, int expired)
-/* Send a TCP reply to the client. */
-{
-    data_rw_t *data_rw= data;
-    ssize_t count;
-
-    /* Try to write count bytes to the client. */
-    count= asyn_write(&asyn, data_rw->w_fd,
-                               data_rw->buf + data_rw->offset,
-                               data_rw->size - data_rw->offset);
-
-    if (count <= 0) {
-       if (errno == EINPROGRESS && !expired) return 0;
-       if (errno == EIO) fatal(tcp_device);
-
-       /* A write expired or failed (usually a broken connection.) */
-       if (debug >= 2) {
-           printf("%s: TCP write reply: %s\n", nowgmt(), strerror(errno));
-       }
-       close_relay(data_rw);
-       return 1;
-    }
-
-    if (debug >= 2) {
-       printf("%s: TCP write reply, %d/%u bytes\n",
-           nowgmt(), data_rw->offset + count, data_rw->size);
-    }
-    data_rw->offset += count;
-    if (data_rw->offset < data_rw->size) {
-       /* Partial write, continue. */
-       newjob(job_write_reply, now + LONG_TIMEOUT, data_rw);
-       return 1;
-    }
-    if (debug >= 1) tcp_dns_tell(data_rw->w_fd, data_rw->buf);
-
-    /* Reply fully send on, go read more replies (or queries). */
-    data_rw->offset= 0;
-    data_rw->size= sizeof(u16_t);
-    newjob(data_rw->w_fd != data_rw->r_fd ? job_read_reply : job_read_query,
-               now + LONG_TIMEOUT, data_rw);
-    return 1;
-}
-#else /* !DO_TCP */
-
-static int job_dummy(void *data, int expired)
-{
-    return 1;
-}
-#define job_setup_listen       job_dummy
-#define job_setup_connect      job_dummy
-#endif /* !DO_TCP */
-
-static void named_probe(ipaddr_t ip)
-/* Send a probe to a name daemon, like 'host -r -t ns . <ip>'. */
-{
-    udp_dns_t udp;
-#   define dlen (offsetof(dns_t, data) + 5)
-#   define ulen (offsetof(udp_dns_t, dns) + dlen)
-
-    /* Send a simple DNS query that all name servers can answer easily:
-     * "What are the name servers for the root domain?"
-     */
-    udp.dns.hdr.id= new_id(ID_PROBE, my_port, ID_IPSELF);
-    udp.dns.hdr.qr = 0;
-    udp.dns.hdr.opcode = 0;
-    udp.dns.hdr.aa = 0;
-    udp.dns.hdr.tc = 0;
-    udp.dns.hdr.rd = 0;
-    udp.dns.hdr.ra = 0;
-    udp.dns.hdr.unused = 0;
-    udp.dns.hdr.ad = 0;
-    udp.dns.hdr.cd = 0;
-    udp.dns.hdr.rcode = 0;
-    udp.dns.hdr.qdcount= HTONS(1);
-    udp.dns.hdr.ancount= HTONS(0);
-    udp.dns.hdr.nscount= HTONS(0);
-    udp.dns.hdr.arcount= HTONS(0);
-
-    udp.dns.data[0] = 0;       /* Null name. */
-    pack16(udp.dns.data+1, HTONS(T_NS));
-    pack16(udp.dns.data+3, HTONS(C_IN));
-    if (debug >= 1) {
-       printf("PROBE %s ", my_ntoa(ip));
-       dns_tell(0, &udp.dns, dlen);
-    }
-
-    udp.hdr.uih_dst_addr= ip;
-    udp.hdr.uih_dst_port= named_port;
-    udp.hdr.uih_ip_opt_len= 0;
-    udp.hdr.uih_data_len= dlen;
-
-    if (write(udp_fd, &udp, ulen) < 0) fatal(udp_device);
-#undef dlen
-#undef ulen
-}
-
-static int job_find_named(void *data, int expired)
-/* Look for a real name daemon to answer real DNS queries. */
-{
-    if (!expired) return 0;
-    if (debug >= 2) printf("%s: Find named\n", nowgmt());
-
-    /* New search? */
-    if (search_ct < 0) {
-       search_ct= n_nameds;
-       i_named= -1;
-    }
-
-    if (--search_ct < 0) {
-       /* Forced end of search (named response!), or end of search with
-        * nothing found.  Search again after a long time.
-        */
-       newjob(job_find_named,
-           (stale > 0 || i_named > 0) ? now + LONG_TIMEOUT : NEVER, nil);
-       force_expire(job_setup_connect);
-       return 1;
-    }
-
-    /* Send a named probe. */
-    i_named= (i_named+1) % n_nameds;
-    named_probe(current_named());
-
-    /* Schedule the next call. */
-    newjob(job_find_named, now + SHORT_TIMEOUT, nil);
-    return 1;
-}
-
-static int job_expect_named(void *data, int expired)
-/* The real name server is expected to answer by now. */
-{
-    if (!expired) return 0;
-    if (debug >= 2) printf("%s: Expect named\n", nowgmt());
-
-    if (expecting() && !searching()) {
-       /* No answer yet, start searching. */
-       start_searching();
-       force_expire(job_find_named);
-    }
-    return 1;
-}
-
-static void sig_handler(int sig)
-/* A signal forces a search for a real name daemon, etc. */
-{
-    switch (sig) {
-    case SIGINT:
-    case SIGTERM:      done= 1;                break;
-    case SIGHUP:       reinit= 1;              break;
-    case SIGUSR1:      debug++;                break;
-    case SIGUSR2:      debug= 0;               break;
-    }
-}
-
-static void usage(void)
-{
-    fprintf(stderr, "Usage: nonamed [-qs] [-d[level]] [-p port]\n");
-    exit(1);
-}
-
-int main(int argc, char **argv)
-{
-    job_t *job;
-    nwio_udpopt_t udpopt;
-    int i;
-    struct servent *servent;
-    struct sigaction sa;
-    FILE *fp;
-    int quit= 0;
-
-    /* Debug output must be line buffered. */
-    setvbuf(stdout, nil, _IOLBF, 0);
-
-    /* DNS service port number? */
-    if ((servent= getservbyname("domain", nil)) == nil) {
-       fprintf(stderr, "nonamed: \"domain\": unknown service\n");
-       exit(1);
-    }
-    my_port= servent->s_port;
-    named_port= servent->s_port;
-
-    i= 1;
-    while (i < argc && argv[i][0] == '-') {
-       char *opt= argv[i++] + 1, *end;
-
-       if (opt[0] == '-' && opt[1] == 0) break;
-
-       switch (*opt++) {
-       case 'd':               /* Debug level. */
-           debug= 1;
-           if (between('0', *opt, '9')) debug= strtoul(opt, &opt, 10);
-           break;
-       case 'p':               /* Port to listen to (for testing.) */
-           if (*opt == 0) {
-               if (i == argc) usage();
-               opt= argv[i++];
-           }
-           my_port= htons(strtoul(opt, &end, 0));
-           if (opt == end || *end != 0) usage();
-           opt= end;
-           break;
-       case 's':
-           single= 1;
-           break;
-       case 'q':               /* Quit after printing cache contents. */
-           quit= 1;
-           break;
-       case 'L':
-           localonly= 1;
-           break;
-       default:
-           usage();
-       }
-    }
-    if (i != argc) usage();
-
-    if (quit) {
-       /* Oops, just having a look at the cache. */
-       debug= 2;
-       now= time(nil);
-       n_datamax= -1;
-       file2cache();
-       return 0;
-    }
-
-    /* Don't die on broken pipes, reinitialize on hangup, etc. */
-    sa.sa_handler= SIG_IGN;
-    sigemptyset(&sa.sa_mask);
-    sa.sa_flags= 0;
-    sigaction(SIGPIPE, &sa, nil);
-    sa.sa_handler= sig_handler;
-    sigaction(SIGINT, &sa, nil);
-    sigaction(SIGHUP, &sa, nil);
-    sigaction(SIGUSR1, &sa, nil);
-    sigaction(SIGUSR2, &sa, nil);
-    sigaction(SIGTERM, &sa, nil);
-
-    /* TCP and UDP device names. */
-    if ((tcp_device= getenv("TCP_DEVICE")) == nil) tcp_device= TCP_DEVICE;
-    if ((udp_device= getenv("UDP_DEVICE")) == nil) udp_device= UDP_DEVICE;
-
-    /* Open an UDP channel for incoming DNS queries. */
-    if ((udp_fd= open(udp_device, O_RDWR)) < 0) fatal(udp_device);
-
-    udpopt.nwuo_flags= NWUO_EXCL | NWUO_LP_SET | NWUO_EN_LOC | NWUO_DI_BROAD
-               | NWUO_RP_ANY | NWUO_RA_ANY | NWUO_RWDATALL | NWUO_DI_IPOPT;
-    udpopt.nwuo_locport= my_port;
-    if (ioctl(udp_fd, NWIOSUDPOPT, &udpopt) == -1
-       || ioctl(udp_fd, NWIOGUDPOPT, &udpopt) == -1
-    ) {
-       fatal(udp_device);
-    }
-
-    /* The current time is... */
-    now= time(nil);
-
-    /* Read configuration and data cached by the previous nonamed. */
-    init_config(udpopt.nwuo_locaddr);
-    file2cache();
-
-    if (!single) {
-       /* Save process id. */
-       if ((fp= fopen(PIDFILE, "w")) != nil) {
-           fprintf(fp, "%u\n", (unsigned) getpid());
-           fclose(fp);
-       }
-    }
-
-    /* Jobs that start the ball rolling. */
-    newjob(job_read_udp, NEVER, nil);
-    newjob(job_setup_listen, IMMEDIATE, nil);
-    newjob(job_find_named, IMMEDIATE, nil);
-
-    /* Open syslog. */
-    openlog("nonamed", LOG_PID, LOG_DAEMON);
-
-    while (!done) {
-       /* There is always something in the queue. */
-       assert(queue != nil);
-
-       /* Any expired jobs? */
-       while (queue->timeout <= now) {
-           (void) execjob(queue, 1);
-           assert(queue != nil);
-       }
-
-       /* Check I/O jobs. */
-       for (job= queue; job != nil; job= job->next) {
-           if (execjob(job, 0)) break;
-       }
-
-       if (queue->timeout != IMMEDIATE) {
-           struct timeval tv, *tvp;
-
-           if (debug >= 2) printf("%s: I/O wait", nowgmt());
-
-           if (queue->timeout != NEVER) {
-               tv.tv_sec= queue->timeout;
-               tv.tv_usec= 0;
-               tvp= &tv;
-               if (debug >= 2) printf(" (expires %s)\n", timegmt(tv.tv_sec));
-           } else {
-               tvp= nil;
-               if (debug >= 2) fputc('\n', stdout);
-           }
-           fflush(stdout);
-
-           if (asyn_wait(&asyn, 0, tvp) < 0) {
-               if (errno != EINTR && errno != EAGAIN) fatal("fwait()");
-           }
-           now= time(nil);
-       }
-
-       if (reinit) {
-           /* A hangup makes us go back to square one. */
-           reinit= 0;
-           if (ioctl(udp_fd, NWIOGUDPOPT, &udpopt) == -1) fatal(udp_device);
-           init_config(udpopt.nwuo_locaddr);
-           start_searching();
-           force_expire(job_find_named);
-       }
-    }
-    cache2file();
-    (void) unlink(PIDFILE);
-    if (debug >= 2) printf("sbrk(0) = %u\n", (unsigned) sbrk(0));
-    return 0;
-}
index 8c34332c339bfc20f39453ecc1b6a8ac38d523c4..a01f107c35e70ad4e2362f0f9b50c1ef41bd5ff7 100644 (file)
@@ -1,6 +1,6 @@
 MAN=   boot.cfg.5 configfile.5 crontab.5 ethers.5 \
-       fstab.5 hosts.5 keymap.5 \
-       passwd.5 resolv.conf.5 resolver.5 rhosts.5 statvfs.5 serv.access.5 \
+       fstab.5 keymap.5 \
+       passwd.5 rhosts.5 statvfs.5 serv.access.5 \
        termcap.5 ttytab.5 TZ.5 utmp.5 \
        pkg_install.conf.5 pkg_summary.5
 
diff --git a/minix/man/man5/hosts.5 b/minix/man/man5/hosts.5
deleted file mode 100644 (file)
index 621be2c..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-.TH HOSTS 5
-.SH NAME
-hosts \- hostname to IP address database
-.SH SYNOPSIS
-.B /etc/hosts
-.SH DESCRIPTION
-The hosts database lists the IP addresses and the hostnames that translate
-to these IP addresses.  It is used by
-.BR nonamed (8)
-in a network without name servers.  A simple
-.B /etc/hosts
-may look like this:
-.PP
-.RS
-.ta +15n
-.nf
-10.0.0.1       flotsam
-10.0.0.2       jetsam
-.fi
-.RE
-.PP
-These two entries give names to two IP addresses.  The file may contain
-comments marked with '#'.
-.PP
-You can have aliases (more hostnames on the same line) to give a machine
-more than one name, like
-.BR www ,
-if you run a web server on one.
-.PP
-If your PC is Internet connected then you can specify the name server(s)
-to get more information from with %nameserver entries:
-.PP
-.RS
-.ta +\w'172.16.24.3'u+4m +\w'%nameserver'u+4m
-.nf
-172.16.24.3    %nameserver     # dns1.example.com
-172.16.24.6    %nameserver     # dns2.example.com
-.fi
-.RE
-.PP
-Read
-.BR nonamed (8)
-for all the details on special host file entries that configure
-.B nonamed
-for use on the Internet, and on home machines that are occasionally
-connected to the Internet.
-.SH FILES
-.TP 15n
-/etc/hosts
-Hosts database.
-.SH "SEE ALSO"
-.BR ethers (5),
-.BR nonamed (8),
-.BR dhcpd (8),
-.BR boot (8).
-.SH AUTHOR
-Kees J. Bot (kjb@cs.vu.nl)
diff --git a/minix/man/man5/resolv.conf.5 b/minix/man/man5/resolv.conf.5
deleted file mode 100644 (file)
index eb6c4d2..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-.TH RESOLV.CONF 5
-.SH NAME
-resolv.conf \- Domain Name System resolver configuration
-.SH SYNOPSIS
-.B /etc/resolv.conf
-.SH DESCRIPTION
-.de SP
-.if t .sp 0.4
-.if n .sp
-..
-The
-.B /etc/resolv.conf
-is used to configure how the host will use the Domain Name System to resolve
-hostnames to IP addresses.  It may contain these two lines:
-.PP
-.RS
-.ta +15n
-nameserver     \fIIP-address\fP
-.br
-domain \fIdomain-name\fP
-.RE
-.PP
-The nameserver entry tells the IP address of the host to use for DNS
-queries.  If it is set to 127.0.0.1 (which is the default) then the local
-name daemon is used that may use the
-.B /etc/hosts
-database to translate host names.  You normally only need a nameserver entry
-if the name server is at the other side of a router.  The default
-.B nonamed
-name server can't look beyond the local network.
-.PP
-The domain entry tells the default domain to use for unqualified hostnames.
-This entry is usually not given in which case the domain of the local host
-is used.
-.PP
-The long version of this story can be found in
-.BR resolver (5).
-.SH FILES
-.TP 20n
-/etc/resolv.conf
-DNS resolver configuration file.
-.SH "SEE ALSO"
-.BR resolver (5),
-.BR hosts (5),
-.BR nonamed (8),
-.BR boot (8).
-.SH AUTHOR
-Kees J. Bot (kjb@cs.vu.nl)
diff --git a/minix/man/man5/resolver.5 b/minix/man/man5/resolver.5
deleted file mode 100644 (file)
index 4a48caf..0000000
+++ /dev/null
@@ -1,98 +0,0 @@
-.\" Copyright (c) 1986 The Regents of the University of California.
-.\" All rights reserved.
-.\"
-.\" Redistribution and use in source and binary forms are permitted
-.\" provided that the above copyright notice and this paragraph are
-.\" duplicated in all such forms and that any documentation,
-.\" advertising materials, and other materials related to such
-.\" distribution and use acknowledge that the software was developed
-.\" by the University of California, Berkeley.  The name of the
-.\" University may not be used to endorse or promote products derived
-.\" from this software without specific prior written permission.
-.\" THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
-.\" IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
-.\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
-.\"
-.\"    @(#)resolver.5  5.9 (Berkeley) 12/14/89
-.\"
-.TH RESOLVER 5 "December 14, 1989"
-.UC 4
-.SH NAME
-resolver \- resolver configuration file
-.SH SYNOPSIS
-/etc/resolv.conf
-.SH DESCRIPTION
-.LP
-The
-.I resolver
-is a set of routines in the C library (\c
-.IR resolv (3))
-that provide access to the Internet Domain Name System.
-The resolver configuration file contains information that is read
-by the resolver routines the first time they are invoked by a process.
-The file is designed to be human readable and contains a list of
-keywords with values that provide various types of resolver information.
-.LP
-On a normally configured system this file should not be necessary.
-The only name server to be queried will be on the local machine,
-the domain name is determined from the host name,
-and the domain search path is constructed from the domain name.
-.LP
-The different configuration options are:
-.TP
-\fBnameserver\fP
-Internet address (in dot notation) of a name server
-that the resolver should query.
-Up to MAXNS (currently 3) name servers may be listed,
-one per keyword.
-If there are multiple servers,
-the resolver library queries them in the order listed.
-If no \fBnameserver\fP entries are present,
-the default is to use the name server on the local machine.
-(The algorithm used is to try a name server, and if the query times out,
-try the next, until out of name servers,
-then repeat trying all the name servers
-until a maximum number of retries are made).
-.TP
-\fBdomain\fP
-Local domain name.
-Most queries for names within this domain can use short names
-relative to the local domain.
-If no \fBdomain\fP entry is present, the domain is determined
-from the local host name returned by
-\fIgethostname\fP\|(2);
-the domain part is taken to be everything after the first `.'.
-Finally, if the host name does not contain a domain part, the root
-domain is assumed.
-.TP
-\fBsearch\fP
-Search list for host-name lookup.
-The search list is normally determined from the local domain name;
-by default, it begins with the local domain name, then successive
-parent domains that have at least two components in their names.
-This may be changed by listing the desired domain search path
-following the \fIsearch\fP keyword with spaces or tabs separating
-the names.
-Most resolver queries will be attempted using each component
-of the search path in turn until a match is found.
-Note that this process may be slow and will generate a lot of network
-traffic if the servers for the listed domains are not local,
-and that queries will time out if no server is available
-for one of the domains.
-.IP
-The search list is currently limited to six domains
-with a total of 256 characters.
-.LP
-The \fIdomain\fP and \fIsearch\fP keywords are mutually exclusive.
-If more than one instance of these keywords is present,
-the last instance will override.
-.LP
-The keyword and value must appear on a single line, and the keyword
-(e.g. \fBnameserver\fP) must start the line.  The value follows
-the keyword, separated by white space.
-.SH FILES
-.I /etc/resolv.conf
-.SH SEE ALSO
-gethostbyname(3N), resolver(3), hostname(7), named(8)
-.br
-Name Server Operations Guide for BIND
index 50902ab49e18186ca3bc3ba4d7b8279d587239ea..e2582b960c92dc6d1876ea5003a75bd3a1f7f094 100644 (file)
@@ -53,8 +53,6 @@ System configuration and data files; see also \fB/usr/etc/\fP.
          \fBpasswd\fP  user database, \fBpasswd\fP(1) 
          \fBprofile\fP system wide shell profile
          \fBrc\fP      system startup script, \fBboot\fP(8)
-         \fBresolv.conf\fP
-                       TCP/IP domain name system, \fBresolv.conf\fP(5)
          \fBservices\fP
                        TCP/IP names to services
          \fBserv.access\fP