tadd dns - plan9port - [fork] Plan 9 from user space
 (HTM) git clone git://src.adamsgaard.dk/plan9port
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit 3e0d8fb3ea83b2b65a6425c65beda887140f9349
 (DIR) parent cff43a06f21a674b2b16e72f8853aac5fd24f48d
 (HTM) Author: rsc <devnull@localhost>
       Date:   Tue, 27 Dec 2005 04:30:05 +0000
       
       add dns
       
       Diffstat:
         A src/cmd/ndb/convDNS2M.c             |     380 ++++++++++++++++++++++++++++++
         A src/cmd/ndb/convM2DNS.c             |     460 +++++++++++++++++++++++++++++++
         A src/cmd/ndb/dblookup.c              |     946 ++++++++++++++++++++++++++++++
         A src/cmd/ndb/dn.c                    |    1563 +++++++++++++++++++++++++++++++
         A src/cmd/ndb/dnarea.c                |     130 +++++++++++++++++++++++++++++++
         A src/cmd/ndb/dnnotify.c              |     160 +++++++++++++++++++++++++++++++
         A src/cmd/ndb/dnresolve.c             |     753 +++++++++++++++++++++++++++++++
         A src/cmd/ndb/dns.c                   |     882 +++++++++++++++++++++++++++++++
         A src/cmd/ndb/dns.h                   |     403 +++++++++++++++++++++++++++++++
         A src/cmd/ndb/dnsdebug.c              |     473 ++++++++++++++++++++++++++++++
         A src/cmd/ndb/dnserver.c              |     178 +++++++++++++++++++++++++++++++
         A src/cmd/ndb/dnsquery.c              |     113 +++++++++++++++++++++++++++++++
         A src/cmd/ndb/dnstcp.c                |     362 +++++++++++++++++++++++++++++++
         A src/cmd/ndb/dnudpserver.c           |     228 +++++++++++++++++++++++++++++++
         M src/cmd/ndb/mkfile                  |      16 ++++++++++++++++
       
       15 files changed, 7047 insertions(+), 0 deletions(-)
       ---
 (DIR) diff --git a/src/cmd/ndb/convDNS2M.c b/src/cmd/ndb/convDNS2M.c
       t@@ -0,0 +1,380 @@
       +#include <u.h>
       +#include <libc.h>
       +#include <ip.h>
       +#include <bio.h>
       +#include <ndb.h>
       +#include "dns.h"
       +
       +/*
       + *  a dictionary of domain names for packing messages
       + */
       +enum
       +{
       +        Ndict=        64,
       +};
       +typedef struct Dict        Dict;
       +struct Dict
       +{
       +        struct {
       +                ushort        offset;                /* pointer to packed name in message */
       +                char        *name;                /* pointer to unpacked name in buf */
       +        } x[Ndict];
       +        int n;                        /* size of dictionary */
       +        uchar *start;                /* start of packed message */
       +        char buf[4*1024];        /* buffer for unpacked names */
       +        char *ep;                /* first free char in buf */
       +};
       +
       +#define NAME(x)                p = pname(p, ep, x, dp)
       +#define SYMBOL(x)        p = psym(p, ep, x)
       +#define STRING(x)        p = pstr(p, ep, x)
       +#define BYTES(x, n)        p = pbytes(p, ep, x, n)
       +#define USHORT(x)        p = pushort(p, ep, x)
       +#define UCHAR(x)        p = puchar(p, ep, x)
       +#define ULONG(x)        p = pulong(p, ep, x)
       +#define V4ADDR(x)        p = pv4addr(p, ep, x)
       +#define V6ADDR(x)        p = pv6addr(p, ep, x)
       +
       +static uchar*
       +psym(uchar *p, uchar *ep, char *np)
       +{
       +        int n;
       +
       +        n = strlen(np);
       +        if(n >= Strlen)                        /* DNS maximum length string */
       +                n = Strlen - 1;
       +        if(ep - p < n+1)                /* see if it fits in the buffer */
       +                return ep+1;
       +        *p++ = n;
       +        memcpy(p, np, n);
       +        return p + n;
       +}
       +
       +static uchar*
       +pstr(uchar *p, uchar *ep, char *np)
       +{
       +        int n;
       +
       +        n = strlen(np);
       +        if(n >= Strlen)                        /* DNS maximum length string */
       +                n = Strlen - 1;
       +        if(ep - p < n+1)                /* see if it fits in the buffer */
       +                return ep+1;
       +        *p++ = n;
       +        memcpy(p, np, n);
       +        return p + n;
       +}
       +
       +static uchar*
       +pbytes(uchar *p, uchar *ep, uchar *np, int n)
       +{
       +        if(ep - p < n)
       +                return ep+1;
       +        memcpy(p, np, n);
       +        return p + n;
       +}
       +
       +static uchar*
       +puchar(uchar *p, uchar *ep, int val)
       +{
       +        if(ep - p < 1)
       +                return ep+1;
       +        *p++ = val;
       +        return p;
       +}
       +
       +static uchar*
       +pushort(uchar *p, uchar *ep, int val)
       +{
       +        if(ep - p < 2)
       +                return ep+1;
       +        *p++ = val>>8;
       +        *p++ = val;
       +        return p;
       +}
       +
       +static uchar*
       +pulong(uchar *p, uchar *ep, int val)
       +{
       +        if(ep - p < 4)
       +                return ep+1;
       +        *p++ = val>>24;
       +        *p++ = val>>16;
       +        *p++ = val>>8;
       +        *p++ = val;
       +        return p;
       +}
       +
       +static uchar*
       +pv4addr(uchar *p, uchar *ep, char *name)
       +{
       +        uchar ip[IPaddrlen];
       +
       +        if(ep - p < 4)
       +                return ep+1;
       +        parseip(ip, name);
       +        v6tov4(p, ip);
       +        return p + 4;
       +
       +}
       +
       +static uchar*
       +pv6addr(uchar *p, uchar *ep, char *name)
       +{
       +        if(ep - p < IPaddrlen)
       +                return ep+1;
       +        parseip(p, name);
       +        return p + IPaddrlen;
       +
       +}
       +
       +static uchar*
       +pname(uchar *p, uchar *ep, char *np, Dict *dp)
       +{
       +        char *cp;
       +        int i;
       +        char *last;                /* last component packed */
       +
       +        if(strlen(np) >= Domlen)        /* make sure we don't exceed DNS limits */
       +                return ep+1;
       +
       +        last = 0;
       +        while(*np){
       +                /* look through every component in the dictionary for a match */
       +                for(i = 0; i < dp->n; i++){
       +                        if(strcmp(np, dp->x[i].name) == 0){
       +                                if(ep - p < 2)
       +                                        return ep+1;
       +                                *p++ = (dp->x[i].offset>>8) | 0xc0;
       +                                *p++ = dp->x[i].offset;
       +                                return p;
       +                        }
       +                }
       +
       +                /* if there's room, enter this name in dictionary */
       +                if(dp->n < Ndict){
       +                        if(last){
       +                                /* the whole name is already in dp->buf */
       +                                last = strchr(last, '.') + 1;
       +                                dp->x[dp->n].name = last;
       +                                dp->x[dp->n].offset = p - dp->start;
       +                                dp->n++;
       +                        } else {
       +                                /* add to dp->buf */
       +                                i = strlen(np);
       +                                if(dp->ep + i + 1 < &dp->buf[sizeof(dp->buf)]){
       +                                        strcpy(dp->ep, np);
       +                                        dp->x[dp->n].name = dp->ep;
       +                                        last = dp->ep;
       +                                        dp->x[dp->n].offset = p - dp->start;
       +                                        dp->ep += i + 1;
       +                                        dp->n++;
       +                                }
       +                        }
       +                }
       +
       +                /* put next component into message */
       +                cp = strchr(np, '.');
       +                if(cp == 0){
       +                        i = strlen(np);
       +                        cp = np + i;        /* point to null terminator */
       +                } else {
       +                        i = cp - np;
       +                        cp++;                /* point past '.' */
       +                }
       +                if(ep-p < i+1)
       +                        return ep+1;
       +                *p++ = i;                /* count of chars in label */
       +                memcpy(p, np, i);
       +                np = cp;
       +                p += i;
       +        }
       +
       +        if(p >= ep)
       +                return ep+1;
       +        *p++ = 0;        /* add top level domain */
       +
       +        return p;
       +}
       +
       +static uchar*
       +convRR2M(RR *rp, uchar *p, uchar *ep, Dict *dp)
       +{
       +        uchar *lp, *data;
       +        int len, ttl;
       +        Txt *t;
       +
       +        NAME(rp->owner->name);
       +        USHORT(rp->type);
       +        USHORT(rp->owner->class);
       +
       +        /* egregious overuse of ttl (it's absolute time in the cache) */
       +        if(rp->db)
       +                ttl = rp->ttl;
       +        else
       +                ttl = rp->ttl - now;
       +        if(ttl < 0)
       +                ttl = 0;
       +        ULONG(ttl);
       +
       +        lp = p;                        /* leave room for the rdata length */
       +        p += 2;
       +        data = p;
       +
       +        if(data >= ep)
       +                return p+1;
       +
       +        switch(rp->type){
       +        case Thinfo:
       +                SYMBOL(rp->cpu->name);
       +                SYMBOL(rp->os->name);
       +                break;
       +        case Tcname:
       +        case Tmb:
       +        case Tmd:
       +        case Tmf:
       +        case Tns:
       +                NAME(rp->host->name);
       +                break;
       +        case Tmg:
       +        case Tmr:
       +                NAME(rp->mb->name);
       +                break;
       +        case Tminfo:
       +                NAME(rp->rmb->name);
       +                NAME(rp->mb->name);
       +                break;
       +        case Tmx:
       +                USHORT(rp->pref);
       +                NAME(rp->host->name);
       +                break;
       +        case Ta:
       +                V4ADDR(rp->ip->name);
       +                break;
       +        case Taaaa:
       +                V6ADDR(rp->ip->name);
       +                break;
       +        case Tptr:
       +                NAME(rp->ptr->name);
       +                break;
       +        case Tsoa:
       +                NAME(rp->host->name);
       +                NAME(rp->rmb->name);
       +                ULONG(rp->soa->serial);
       +                ULONG(rp->soa->refresh);
       +                ULONG(rp->soa->retry);
       +                ULONG(rp->soa->expire);
       +                ULONG(rp->soa->minttl);
       +                break;
       +        case Ttxt:
       +                for(t = rp->txt; t != nil; t = t->next)
       +                        STRING(t->p);
       +                break;
       +        case Tnull:
       +                BYTES(rp->null->data, rp->null->dlen);
       +                break;
       +        case Trp:
       +                NAME(rp->rmb->name);
       +                NAME(rp->rp->name);
       +                break;
       +        case Tkey:
       +                USHORT(rp->key->flags);
       +                UCHAR(rp->key->proto);
       +                UCHAR(rp->key->alg);
       +                BYTES(rp->key->data, rp->key->dlen);
       +                break;
       +        case Tsig:
       +                USHORT(rp->sig->type);
       +                UCHAR(rp->sig->alg);
       +                UCHAR(rp->sig->labels);
       +                ULONG(rp->sig->ttl);
       +                ULONG(rp->sig->exp);
       +                ULONG(rp->sig->incep);
       +                USHORT(rp->sig->tag);
       +                NAME(rp->sig->signer->name);
       +                BYTES(rp->sig->data, rp->sig->dlen);
       +                break;
       +        case Tcert:
       +                USHORT(rp->cert->type);
       +                USHORT(rp->cert->tag);
       +                UCHAR(rp->cert->alg);
       +                BYTES(rp->cert->data, rp->cert->dlen);
       +                break;
       +        }
       +
       +        /* stuff in the rdata section length */
       +        len = p - data;
       +        *lp++ = len >> 8;
       +        *lp = len;
       +
       +        return p;
       +}
       +
       +static uchar*
       +convQ2M(RR *rp, uchar *p, uchar *ep, Dict *dp)
       +{
       +        NAME(rp->owner->name);
       +        USHORT(rp->type);
       +        USHORT(rp->owner->class);
       +        return p;
       +}
       +
       +static uchar*
       +rrloop(RR *rp, int *countp, uchar *p, uchar *ep, Dict *dp, int quest)
       +{
       +        uchar *np;
       +
       +        *countp = 0;
       +        for(; rp && p < ep; rp = rp->next){
       +                if(quest)
       +                        np = convQ2M(rp, p, ep, dp);
       +                else
       +                        np = convRR2M(rp, p, ep, dp);
       +                if(np > ep)
       +                        break;
       +                p = np;
       +                (*countp)++;
       +        }
       +        return p;
       +}
       +
       +/*
       + *  convert into a message
       + */
       +int
       +convDNS2M(DNSmsg *m, uchar *buf, int len)
       +{
       +        uchar *p, *ep, *np;
       +        Dict d;
       +
       +        d.n = 0;
       +        d.start = buf;
       +        d.ep = d.buf;
       +        memset(buf, 0, len);
       +        m->qdcount = m->ancount = m->nscount = m->arcount = 0;
       +
       +        /* first pack in the RR's so we can get real counts */
       +        p = buf + 12;
       +        ep = buf + len;
       +        p = rrloop(m->qd, &m->qdcount, p, ep, &d, 1);
       +        p = rrloop(m->an, &m->ancount, p, ep, &d, 0);
       +        p = rrloop(m->ns, &m->nscount, p, ep, &d, 0);
       +        p = rrloop(m->ar, &m->arcount, p, ep, &d, 0);
       +        if(p > ep)
       +                return -1;
       +
       +        /* now pack the rest */
       +        np = p;
       +        p = buf;
       +        ep = buf + len;
       +        USHORT(m->id);
       +        USHORT(m->flags);
       +        USHORT(m->qdcount);
       +        USHORT(m->ancount);
       +        USHORT(m->nscount);
       +        USHORT(m->arcount);
       +        if(p > ep)
       +                return -1;
       +
       +        return np - buf;
       +}
 (DIR) diff --git a/src/cmd/ndb/convM2DNS.c b/src/cmd/ndb/convM2DNS.c
       t@@ -0,0 +1,460 @@
       +#include <u.h>
       +#include <libc.h>
       +#include <ip.h>
       +#include <bio.h>
       +#include <ndb.h>
       +#include "dns.h"
       +
       +typedef struct Scan        Scan;
       +struct Scan
       +{
       +        uchar        *base;
       +        uchar        *p;
       +        uchar        *ep;
       +        char        *err;
       +};
       +
       +#define NAME(x)                gname(x, sp)
       +#define SYMBOL(x)        (x = gsym(sp))
       +#define STRING(x)        (x = gstr(sp))
       +#define USHORT(x)        (x = gshort(sp))
       +#define ULONG(x)        (x = glong(sp))
       +#define UCHAR(x)        (x = gchar(sp))
       +#define V4ADDR(x)        (x = gv4addr(sp))
       +#define V6ADDR(x)        (x = gv6addr(sp))
       +#define BYTES(x, y)        (y = gbytes(sp, &x, len - (sp->p - data)))
       +
       +static char *toolong = "too long";
       +
       +/*
       + *  get a ushort/ulong
       + */
       +static ushort
       +gchar(Scan *sp)
       +{
       +        ushort x;
       +
       +        if(sp->err)
       +                return 0;
       +        if(sp->ep - sp->p < 1){
       +                sp->err = toolong;
       +                return 0;
       +        }
       +        x = sp->p[0];
       +        sp->p += 1;
       +        return x;
       +}
       +static ushort
       +gshort(Scan *sp)
       +{
       +        ushort x;
       +
       +        if(sp->err)
       +                return 0;
       +        if(sp->ep - sp->p < 2){
       +                sp->err = toolong;
       +                return 0;
       +        }
       +        x = (sp->p[0]<<8) | sp->p[1];
       +        sp->p += 2;
       +        return x;
       +}
       +static ulong
       +glong(Scan *sp)
       +{
       +        ulong x;
       +
       +        if(sp->err)
       +                return 0;
       +        if(sp->ep - sp->p < 4){
       +                sp->err = toolong;
       +                return 0;
       +        }
       +        x = (sp->p[0]<<24) | (sp->p[1]<<16) | (sp->p[2]<<8) | sp->p[3];
       +        sp->p += 4;
       +        return x;
       +}
       +
       +/*
       + *  get an ip address
       + */
       +static DN*
       +gv4addr(Scan *sp)
       +{
       +        char addr[32];
       +
       +        if(sp->err)
       +                return 0;
       +        if(sp->ep - sp->p < 4){
       +                sp->err = toolong;
       +                return 0;
       +        }
       +        snprint(addr, sizeof(addr), "%V", sp->p);
       +        sp->p += 4;
       +
       +        return dnlookup(addr, Cin, 1);
       +}
       +static DN*
       +gv6addr(Scan *sp)
       +{
       +        char addr[64];
       +
       +        if(sp->err)
       +                return 0;
       +        if(sp->ep - sp->p < IPaddrlen){
       +                sp->err = toolong;
       +                return 0;
       +        }
       +        snprint(addr, sizeof(addr), "%I", sp->p);
       +        sp->p += IPaddrlen;
       +
       +        return dnlookup(addr, Cin, 1);
       +}
       +
       +/*
       + *  get a string.  make it an internal symbol.
       + */
       +static DN*
       +gsym(Scan *sp)
       +{
       +        int n;
       +        char sym[Strlen+1];
       +
       +        if(sp->err)
       +                return 0;
       +        n = *(sp->p++);
       +        if(sp->p+n > sp->ep){
       +                sp->err = toolong;
       +                return 0;
       +        }
       +
       +        if(n > Strlen){
       +                sp->err = "illegal string";
       +                return 0;
       +        }
       +        strncpy(sym, (char*)sp->p, n);
       +        sym[n] = 0;
       +        sp->p += n;
       +
       +        return dnlookup(sym, Csym, 1);
       +}
       +
       +/*
       + *  get a string.  don't make it an internal symbol.
       + */
       +static Txt*
       +gstr(Scan *sp)
       +{
       +        int n;
       +        char sym[Strlen+1];
       +        Txt *t;
       +
       +        if(sp->err)
       +                return 0;
       +        n = *(sp->p++);
       +        if(sp->p+n > sp->ep){
       +                sp->err = toolong;
       +                return 0;
       +        }
       +
       +        if(n > Strlen){
       +                sp->err = "illegal string";
       +                return 0;
       +        }
       +        strncpy(sym, (char*)sp->p, n);
       +        sym[n] = 0;
       +        sp->p += n;
       +
       +        t = emalloc(sizeof(*t));
       +        t->next = nil;
       +        t->p = estrdup(sym);
       +        return t;
       +}
       +
       +/*
       + *  get a sequence of bytes
       + */
       +static int
       +gbytes(Scan *sp, uchar **p, int n)
       +{
       +        if(sp->err)
       +                return 0;
       +        if(sp->p+n > sp->ep || n < 0){
       +                sp->err = toolong;
       +                return 0;
       +        }
       +        *p = emalloc(n);
       +        memmove(*p, sp->p, n);
       +        sp->p += n;
       +
       +        return n;
       +}
       +
       +/*
       + *  get a domain name.  'to' must point to a buffer at least Domlen+1 long.
       + */
       +static char*
       +gname(char *to, Scan *sp)
       +{
       +        int len, off;
       +        int pointer;
       +        int n;
       +        char *tostart;
       +        char *toend;
       +        uchar *p;
       +
       +        tostart = to;
       +        if(sp->err)
       +                goto err;
       +        pointer = 0;
       +        p = sp->p;
       +        toend = to + Domlen;
       +        for(len = 0; *p; len += pointer ? 0 : (n+1)){
       +                if((*p & 0xc0) == 0xc0){
       +                        /* pointer to other spot in message */
       +                        if(pointer++ > 10){
       +                                sp->err = "pointer loop";
       +                                goto err;
       +                        }
       +                        off = ((p[0]<<8) + p[1]) & 0x3ff;
       +                        p = sp->base + off;
       +                        if(p >= sp->ep){
       +                                sp->err = "bad pointer";
       +                                goto err;
       +                        }
       +                        n = 0;
       +                        continue;
       +                }
       +                n = *p++;
       +                if(len + n < Domlen - 1){
       +                        if(to + n > toend){
       +                                sp->err = toolong;
       +                                goto err;
       +                        }
       +                        memmove(to, p, n);
       +                        to += n;
       +                }
       +                p += n;
       +                if(*p){
       +                        if(to >= toend){
       +                                sp->err = toolong;
       +                                goto err;
       +                        }
       +                        *to++ = '.';
       +                }
       +        }
       +        *to = 0;
       +        if(pointer)
       +                sp->p += len + 2;        /* + 2 for pointer */
       +        else
       +                sp->p += len + 1;        /* + 1 for the null domain */
       +        return tostart;
       +err:
       +        *tostart = 0;
       +        return tostart;
       +}
       +
       +/*
       + *  convert the next RR from a message
       + */
       +static RR*
       +convM2RR(Scan *sp)
       +{
       +        RR *rp;
       +        int type;
       +        int class;
       +        uchar *data;
       +        int len;
       +        char dname[Domlen+1];
       +        Txt *t, **l;
       +
       +retry:
       +        NAME(dname);
       +        USHORT(type);
       +        USHORT(class);
       +
       +        rp = rralloc(type);
       +        rp->owner = dnlookup(dname, class, 1);
       +        rp->type = type;
       +
       +        ULONG(rp->ttl);
       +        rp->ttl += now;
       +        USHORT(len);
       +        data = sp->p;
       +
       +        if(sp->p + len > sp->ep)
       +                sp->err = toolong;
       +        if(sp->err){
       +                rrfree(rp);
       +                return 0;
       +        }
       +
       +        switch(type){
       +        default:
       +                /* unknown type, just ignore it */
       +                sp->p = data + len;
       +                rrfree(rp);
       +                goto retry;
       +        case Thinfo:
       +                SYMBOL(rp->cpu);
       +                SYMBOL(rp->os);
       +                break;
       +        case Tcname:
       +        case Tmb:
       +        case Tmd:
       +        case Tmf:
       +        case Tns:
       +                rp->host = dnlookup(NAME(dname), Cin, 1);
       +                break;
       +        case Tmg:
       +        case Tmr:
       +                rp->mb = dnlookup(NAME(dname), Cin, 1);
       +                break;
       +        case Tminfo:
       +                rp->rmb = dnlookup(NAME(dname), Cin, 1);
       +                rp->mb = dnlookup(NAME(dname), Cin, 1);
       +                break;
       +        case Tmx:
       +                USHORT(rp->pref);
       +                rp->host = dnlookup(NAME(dname), Cin, 1);
       +                break;
       +        case Ta:
       +                V4ADDR(rp->ip);
       +                break;
       +        case Taaaa:
       +                V6ADDR(rp->ip);
       +                break;
       +        case Tptr:
       +                rp->ptr = dnlookup(NAME(dname), Cin, 1);
       +                break;
       +        case Tsoa:
       +                rp->host = dnlookup(NAME(dname), Cin, 1);
       +                rp->rmb = dnlookup(NAME(dname), Cin, 1);
       +                ULONG(rp->soa->serial);
       +                ULONG(rp->soa->refresh);
       +                ULONG(rp->soa->retry);
       +                ULONG(rp->soa->expire);
       +                ULONG(rp->soa->minttl);
       +                break;
       +        case Ttxt:
       +                l = &rp->txt;
       +                *l = nil;
       +                while(sp->p-data < len){
       +                        STRING(t);
       +                        *l = t;
       +                        l = &t->next;
       +                }
       +                break;
       +        case Tnull:
       +                BYTES(rp->null->data, rp->null->dlen);
       +                break;
       +        case Trp:
       +                rp->rmb = dnlookup(NAME(dname), Cin, 1);
       +                rp->rp = dnlookup(NAME(dname), Cin, 1);
       +                break;
       +        case Tkey:
       +                USHORT(rp->key->flags);
       +                UCHAR(rp->key->proto);
       +                UCHAR(rp->key->alg);
       +                BYTES(rp->key->data, rp->key->dlen);
       +                break;
       +        case Tsig:
       +                USHORT(rp->sig->type);
       +                UCHAR(rp->sig->alg);
       +                UCHAR(rp->sig->labels);
       +                ULONG(rp->sig->ttl);
       +                ULONG(rp->sig->exp);
       +                ULONG(rp->sig->incep);
       +                USHORT(rp->sig->tag);
       +                rp->sig->signer = dnlookup(NAME(dname), Cin, 1);
       +                BYTES(rp->sig->data, rp->sig->dlen);
       +                break;
       +        case Tcert:
       +                USHORT(rp->cert->type);
       +                USHORT(rp->cert->tag);
       +                UCHAR(rp->cert->alg);
       +                BYTES(rp->cert->data, rp->cert->dlen);
       +                break;
       +        }
       +        if(sp->p - data != len)
       +                sp->err = "bad RR len";
       +        return rp;
       +}
       +
       +/*
       + *  convert the next question from a message
       + */
       +static RR*
       +convM2Q(Scan *sp)
       +{
       +        char dname[Domlen+1];
       +        int type;
       +        int class;
       +        RR *rp;
       +
       +        NAME(dname);
       +        USHORT(type);
       +        USHORT(class);
       +        if(sp->err)
       +                return 0;
       +
       +        rp = rralloc(type);
       +        rp->owner = dnlookup(dname, class, 1);
       +
       +        return rp;
       +}
       +
       +static RR*
       +rrloop(Scan *sp, int count, int quest)
       +{
       +        int i;
       +        static char errbuf[64];
       +        RR *first, *rp, **l;
       +
       +        if(sp->err)
       +                return 0;
       +        l = &first;
       +        first = 0;
       +        for(i = 0; i < count; i++){
       +                rp = quest ? convM2Q(sp) : convM2RR(sp);
       +                if(rp == 0)
       +                        break;
       +                if(sp->err){
       +                        rrfree(rp);
       +                        break;
       +                }
       +                *l = rp;
       +                l = &rp->next;
       +        }
       +        return first;
       +}
       +
       +/*
       + *  convert the next DNS from a message stream
       + */
       +char*
       +convM2DNS(uchar *buf, int len, DNSmsg *m)
       +{
       +        Scan scan;
       +        Scan *sp;
       +        char *err;
       +
       +        scan.base = buf;
       +        scan.p = buf;
       +        scan.ep = buf + len;
       +        scan.err = 0;
       +        sp = &scan;
       +        memset(m, 0, sizeof(DNSmsg));
       +        USHORT(m->id);
       +        USHORT(m->flags);
       +        USHORT(m->qdcount);
       +        USHORT(m->ancount);
       +        USHORT(m->nscount);
       +        USHORT(m->arcount);
       +        m->qd = rrloop(sp, m->qdcount, 1);
       +        m->an = rrloop(sp, m->ancount, 0);
       +        m->ns = rrloop(sp, m->nscount, 0);
       +        err = scan.err;                                /* live with bad ar's */
       +        m->ar = rrloop(sp, m->arcount, 0);
       +        return err;
       +}
 (DIR) diff --git a/src/cmd/ndb/dblookup.c b/src/cmd/ndb/dblookup.c
       t@@ -0,0 +1,946 @@
       +#include <u.h>
       +#include <libc.h>
       +#include <bio.h>
       +#include <ndb.h>
       +#include <ip.h>
       +#include "dns.h"
       +
       +static Ndb *db;
       +
       +static RR*        dblookup1(char*, int, int, int);
       +static RR*        addrrr(Ndbtuple*, Ndbtuple*);
       +static RR*        nsrr(Ndbtuple*, Ndbtuple*);
       +static RR*        cnamerr(Ndbtuple*, Ndbtuple*);
       +static RR*        mxrr(Ndbtuple*, Ndbtuple*);
       +static RR*        soarr(Ndbtuple*, Ndbtuple*);
       +static RR*        ptrrr(Ndbtuple*, Ndbtuple*);
       +static Ndbtuple* look(Ndbtuple*, Ndbtuple*, char*);
       +static RR*        doaxfr(Ndb*, char*);
       +static RR*        nullrr(Ndbtuple *entry, Ndbtuple *pair);
       +static RR*        txtrr(Ndbtuple *entry, Ndbtuple *pair);
       +static Lock        dblock;
       +static void        createptrs(void);
       +
       +static int        implemented[Tall] =
       +{
       +        [Ta]                1,
       +        [Tns]                1,
       +        [Tsoa]                1,
       +        [Tmx]                1,
       +        [Tptr]                1,
       +        [Tcname]        1,
       +        [Tnull]                1,
       +        [Ttxt]                1,
       +};
       +
       +static void
       +nstrcpy(char *to, char *from, int len)
       +{
       +        strncpy(to, from, len);
       +        to[len-1] = 0;
       +}
       +
       +int
       +opendatabase(void)
       +{
       +        char buf[256];
       +        Ndb *xdb;
       +
       +        if(db == nil){
       +                snprint(buf, sizeof(buf), "%s/ndb", mntpt);
       +                xdb = ndbopen(dbfile);
       +                if(xdb != nil)
       +                        xdb->nohash = 1;
       +                db = ndbcat(ndbopen(buf), xdb);
       +        }
       +        if(db == nil)
       +                return -1;
       +        else
       +                return 0;
       +}
       +
       +/*
       + *  lookup an RR in the network database, look for matches
       + *  against both the domain name and the wildcarded domain name.
       + *
       + *  the lock makes sure only one process can be accessing the data
       + *  base at a time.  This is important since there's a lot of
       + *  shared state there.
       + *
       + *  e.g. for x.research.bell-labs.com, first look for a match against
       + *       the x.research.bell-labs.com.  If nothing matches, try *.research.bell-labs.com.
       + */
       +RR*
       +dblookup(char *name, int class, int type, int auth, int ttl)
       +{
       +        RR *rp, *tp;
       +        char buf[256];
       +        char *wild, *cp;
       +        DN *dp, *ndp;
       +        int err;
       +        static int parallel;
       +        static int parfd[2];
       +        static char token[1];
       +
       +        /* so far only internet lookups are implemented */
       +        if(class != Cin)
       +                return 0;
       +
       +        err = Rname;
       +
       +        if(type == Tall){
       +                rp = 0;
       +                for (type = Ta; type < Tall; type++)
       +                        if(implemented[type])
       +                                rrcat(&rp, dblookup(name, class, type, auth, ttl));
       +                return rp;
       +        }
       +
       +        lock(&dblock);
       +        dp = dnlookup(name, class, 1);
       +        if(opendatabase() < 0)
       +                goto out;
       +        if(dp->rr)
       +                err = 0;
       +
       +        /* first try the given name */
       +        rp = 0;
       +        if(cachedb)
       +                rp = rrlookup(dp, type, NOneg);
       +        else
       +                rp = dblookup1(name, type, auth, ttl);
       +        if(rp)
       +                goto out;
       +
       +        /* try lower case version */
       +        for(cp = name; *cp; cp++)
       +                *cp = tolower(*cp);
       +        if(cachedb)
       +                rp = rrlookup(dp, type, NOneg);
       +        else
       +                rp = dblookup1(name, type, auth, ttl);
       +        if(rp)
       +                goto out;
       +
       +        /* walk the domain name trying the wildcard '*' at each position */
       +        for(wild = strchr(name, '.'); wild; wild = strchr(wild+1, '.')){
       +                snprint(buf, sizeof(buf), "*%s", wild);
       +                ndp = dnlookup(buf, class, 1);
       +                if(ndp->rr)
       +                        err = 0;
       +                if(cachedb)
       +                        rp = rrlookup(ndp, type, NOneg);
       +                else
       +                        rp = dblookup1(buf, type, auth, ttl);
       +                if(rp)
       +                        break;
       +        }
       +out:
       +        /* add owner to uncached records */
       +        if(rp){
       +                for(tp = rp; tp; tp = tp->next)
       +                        tp->owner = dp;
       +        } else {
       +                /* don't call it non-existent if it's not ours */
       +                if(err == Rname && !inmyarea(name))
       +                        err = Rserver;
       +                dp->nonexistent = err;
       +        }
       +
       +        unlock(&dblock);
       +        return rp;
       +}
       +
       +/*
       + *  lookup an RR in the network database
       + */
       +static RR*
       +dblookup1(char *name, int type, int auth, int ttl)
       +{
       +        Ndbtuple *t, *nt;
       +        RR *rp, *list, **l;
       +        Ndbs s;
       +        char dname[Domlen];
       +        char *attr;
       +        DN *dp;
       +        RR *(*f)(Ndbtuple*, Ndbtuple*);
       +        int found, x;
       +
       +        dp = 0;
       +        switch(type){
       +        case Tptr:
       +                attr = "ptr";
       +                f = ptrrr;
       +                break;
       +        case Ta:
       +                attr = "ip";
       +                f = addrrr;
       +                break;
       +        case Tnull:
       +                attr = "nullrr";
       +                f = nullrr;
       +                break;
       +        case Tns:
       +                attr = "ns";
       +                f = nsrr;
       +                break;
       +        case Tsoa:
       +                attr = "soa";
       +                f = soarr;
       +                break;
       +        case Tmx:
       +                attr = "mx";
       +                f = mxrr;
       +                break;
       +        case Tcname:
       +                attr = "cname";
       +                f = cnamerr;
       +                break;
       +        case Taxfr:
       +        case Tixfr:
       +                return doaxfr(db, name);
       +        default:
       +                return nil;
       +        }
       +
       +        /*
       +         *  find a matching entry in the database
       +         */
       +        free(ndbgetvalue(db, &s, "dom", name, attr, &t));
       +
       +        /*
       +         *  hack for local names
       +         */
       +        if(t == 0 && strchr(name, '.') == 0)
       +                free(ndbgetvalue(db, &s, "sys", name, attr, &t));
       +        if(t == 0)
       +                return nil;
       +
       +        /* search whole entry for default domain name */
       +        strncpy(dname, name, sizeof dname);
       +        for(nt = t; nt; nt = nt->entry)
       +                if(strcmp(nt->attr, "dom") == 0){
       +                        nstrcpy(dname, nt->val, sizeof dname);
       +                        break;
       +                }
       +
       +        /* ttl is maximum of soa minttl and entry's ttl ala rfc883 */
       +        nt = look(t, s.t, "ttl");
       +        if(nt){
       +                x = atoi(nt->val);
       +                if(x > ttl)
       +                        ttl = x;
       +        }
       +
       +        /* default ttl is one day */
       +        if(ttl < 0)
       +                ttl = DEFTTL;
       +
       +        /*
       +         *  The database has 2 levels of precedence; line and entry.
       +         *  Pairs on the same line bind tighter than pairs in the
       +         *  same entry, so we search the line first.
       +         */
       +        found = 0;
       +        list = 0;
       +        l = &list;
       +        for(nt = s.t;; ){
       +                if(found == 0 && strcmp(nt->attr, "dom") == 0){
       +                        nstrcpy(dname, nt->val, sizeof dname);
       +                        found = 1;
       +                }
       +                if(cistrcmp(attr, nt->attr) == 0){
       +                        rp = (*f)(t, nt);
       +                        rp->auth = auth;
       +                        rp->db = 1;
       +                        if(ttl)
       +                                rp->ttl = ttl;
       +                        if(dp == 0)
       +                                dp = dnlookup(dname, Cin, 1);
       +                        rp->owner = dp;
       +                        *l = rp;
       +                        l = &rp->next;
       +                        nt->ptr = 1;
       +                }
       +                nt = nt->line;
       +                if(nt == s.t)
       +                        break;
       +        }
       +
       +        /* search whole entry */
       +        for(nt = t; nt; nt = nt->entry)
       +                if(nt->ptr == 0 && cistrcmp(attr, nt->attr) == 0){
       +                        rp = (*f)(t, nt);
       +                        rp->db = 1;
       +                        if(ttl)
       +                                rp->ttl = ttl;
       +                        rp->auth = auth;
       +                        if(dp == 0)
       +                                dp = dnlookup(dname, Cin, 1);
       +                        rp->owner = dp;
       +                        *l = rp;
       +                        l = &rp->next;
       +                }
       +        ndbfree(t);
       +
       +        return list;
       +}
       +
       +/*
       + *  make various types of resource records from a database entry
       + */
       +static RR*
       +addrrr(Ndbtuple *entry, Ndbtuple *pair)
       +{
       +        RR *rp;
       +        uchar addr[IPaddrlen];
       +
       +        USED(entry);
       +        parseip(addr, pair->val);
       +        if(isv4(addr))
       +                rp = rralloc(Ta);
       +        else
       +                rp = rralloc(Taaaa);
       +        rp->ip = dnlookup(pair->val, Cin, 1);
       +        return rp;
       +}
       +static RR*
       +nullrr(Ndbtuple *entry, Ndbtuple *pair)
       +{
       +        RR *rp;
       +
       +        USED(entry);
       +        rp = rralloc(Tnull);
       +        rp->null->data = (uchar*)estrdup(pair->val);
       +        rp->null->dlen = strlen((char*)rp->null->data);
       +        return rp;
       +}
       +/*
       + *  txt rr strings are at most 255 bytes long.  one
       + *  can represent longer strings by multiple concatenated
       + *  <= 255 byte ones.
       + */
       +static RR*
       +txtrr(Ndbtuple *entry, Ndbtuple *pair)
       +{
       +        RR *rp;
       +        Txt *t, **l;
       +        int i, len, sofar;
       +
       +        USED(entry);
       +        rp = rralloc(Ttxt);
       +        l = &rp->txt;
       +        rp->txt = nil;
       +        len = strlen(pair->val);
       +        sofar = 0;
       +        while(len > sofar){
       +                t = emalloc(sizeof(*t));
       +                t->next = nil;
       +
       +                i = len-sofar;
       +                if(i > 255)
       +                        i = 255;
       +
       +                t->p = emalloc(i+1);
       +                memmove(t->p, pair->val+sofar, i);
       +                t->p[i] = 0;
       +                sofar += i;
       +
       +                *l = t;
       +                l = &t->next;
       +        }
       +        return rp;
       +}
       +static RR*
       +cnamerr(Ndbtuple *entry, Ndbtuple *pair)
       +{
       +        RR *rp;
       +
       +        USED(entry);
       +        rp = rralloc(Tcname);
       +        rp->host = dnlookup(pair->val, Cin, 1);
       +        return rp;
       +}
       +static RR*
       +mxrr(Ndbtuple *entry, Ndbtuple *pair)
       +{
       +        RR * rp;
       +
       +        rp = rralloc(Tmx);
       +        rp->host = dnlookup(pair->val, Cin, 1);
       +        pair = look(entry, pair, "pref");
       +        if(pair)
       +                rp->pref = atoi(pair->val);
       +        else
       +                rp->pref = 1;
       +        return rp;
       +}
       +static RR*
       +nsrr(Ndbtuple *entry, Ndbtuple *pair)
       +{
       +        RR *rp;
       +        Ndbtuple *t;
       +
       +        rp = rralloc(Tns);
       +        rp->host = dnlookup(pair->val, Cin, 1);
       +        t = look(entry, pair, "soa");
       +        if(t && t->val[0] == 0)
       +                rp->local = 1;
       +        return rp;
       +}
       +static RR*
       +ptrrr(Ndbtuple *entry, Ndbtuple *pair)
       +{
       +        RR *rp;
       +
       +        USED(entry);
       +        rp = rralloc(Tns);
       +        rp->ptr = dnlookup(pair->val, Cin, 1);
       +        return rp;
       +}
       +static RR*
       +soarr(Ndbtuple *entry, Ndbtuple *pair)
       +{
       +        RR *rp;
       +        Ndbtuple *ns, *mb, *t;
       +        char mailbox[Domlen];
       +        Ndb *ndb;
       +        char *p;
       +
       +        rp = rralloc(Tsoa);
       +        rp->soa->serial = 1;
       +        for(ndb = db; ndb; ndb = ndb->next)
       +                if(ndb->mtime > rp->soa->serial)
       +                        rp->soa->serial = ndb->mtime;
       +        rp->soa->refresh = Day;
       +        rp->soa->retry = Hour;
       +        rp->soa->expire = Day;
       +        rp->soa->minttl = Day;
       +        t = look(entry, pair, "ttl");
       +        if(t)
       +                rp->soa->minttl = atoi(t->val);
       +        t = look(entry, pair, "refresh");
       +        if(t)
       +                rp->soa->refresh = atoi(t->val);
       +        t = look(entry, pair, "serial");
       +        if(t)
       +                rp->soa->serial = strtoul(t->val, 0, 10);
       +
       +        ns = look(entry, pair, "ns");
       +        if(ns == 0)
       +                ns = look(entry, pair, "dom");
       +        rp->host = dnlookup(ns->val, Cin, 1);
       +
       +        /* accept all of:
       +         *  mbox=person
       +         *  mbox=person@machine.dom
       +         *  mbox=person.machine.dom
       +         */
       +        mb = look(entry, pair, "mbox");
       +        if(mb == nil)
       +                mb = look(entry, pair, "mb");
       +        if(mb){
       +                if(strchr(mb->val, '.')) {
       +                        p = strchr(mb->val, '@');
       +                        if(p != nil)
       +                                *p = '.';
       +                        rp->rmb = dnlookup(mb->val, Cin, 1);
       +                } else {
       +                        snprint(mailbox, sizeof(mailbox), "%s.%s",
       +                                mb->val, ns->val);
       +                        rp->rmb = dnlookup(mailbox, Cin, 1);
       +                }
       +        } else {
       +                snprint(mailbox, sizeof(mailbox), "postmaster.%s",
       +                        ns->val);
       +                rp->rmb = dnlookup(mailbox, Cin, 1);
       +        }
       +
       +        /*  hang dns slaves off of the soa.  this is 
       +         *  for managing the area.
       +         */
       +        for(t = entry; t != nil; t = t->entry)
       +                if(strcmp(t->attr, "dnsslave") == 0)
       +                        addserver(&rp->soa->slaves, t->val);
       +                        
       +        return rp;
       +}
       +
       +/*
       + *  Look for a pair with the given attribute.  look first on the same line,
       + *  then in the whole entry.
       + */
       +static Ndbtuple*
       +look(Ndbtuple *entry, Ndbtuple *line, char *attr)
       +{
       +        Ndbtuple *nt;
       +
       +        /* first look on same line (closer binding) */
       +        for(nt = line;;){
       +                if(cistrcmp(attr, nt->attr) == 0)
       +                        return nt;
       +                nt = nt->line;
       +                if(nt == line)
       +                        break;
       +        }
       +        /* search whole tuple */
       +        for(nt = entry; nt; nt = nt->entry)
       +                if(cistrcmp(attr, nt->attr) == 0)
       +                        return nt;
       +        return 0;
       +}
       +
       +static RR**
       +linkrr(RR *rp, DN *dp, RR **l)
       +{
       +        rp->owner = dp;
       +        rp->auth = 1;
       +        rp->db = 1;
       +        *l = rp;
       +        return &rp->next;
       +}
       +
       +/* these are answered specially by the tcp version */
       +static RR*
       +doaxfr(Ndb *db, char *name)
       +{
       +        USED(db);
       +        USED(name);
       +        return 0;
       +}
       +
       +
       +/*
       + *  read the all the soa's from the database to determine area's.
       + *  this is only used when we're not caching the database.
       + */
       +static void
       +dbfile2area(Ndb *db)
       +{
       +        Ndbtuple *t;
       +
       +        if(debug)
       +                syslog(0, logfile, "rereading %s", db->file);
       +        Bseek(&db->b, 0, 0);
       +        while(t = ndbparse(db)){
       +                ndbfree(t);
       +        }
       +}
       +
       +/*
       + *  read the database into the cache
       + */
       +static void
       +dbpair2cache(DN *dp, Ndbtuple *entry, Ndbtuple *pair)
       +{
       +        RR *rp;
       +        Ndbtuple *t;
       +        static ulong ord;
       +
       +        rp = 0;
       +        if(cistrcmp(pair->attr, "ip") == 0){
       +                dp->ordinal = ord++;
       +                rp = addrrr(entry, pair);
       +        } else         if(cistrcmp(pair->attr, "ns") == 0){
       +                rp = nsrr(entry, pair);
       +        } else if(cistrcmp(pair->attr, "soa") == 0){
       +                rp = soarr(entry, pair);
       +                addarea(dp, rp, pair);
       +        } else if(cistrcmp(pair->attr, "mx") == 0){
       +                rp = mxrr(entry, pair);
       +        } else if(cistrcmp(pair->attr, "cname") == 0){
       +                rp = cnamerr(entry, pair);
       +        } else if(cistrcmp(pair->attr, "nullrr") == 0){
       +                rp = nullrr(entry, pair);
       +        } else if(cistrcmp(pair->attr, "txtrr") == 0){
       +                rp = txtrr(entry, pair);
       +        }
       +
       +        if(rp == 0)
       +                return;
       +
       +        rp->owner = dp;
       +        rp->db = 1;
       +        t = look(entry, pair, "ttl");
       +        if(t)
       +                rp->ttl = atoi(t->val);
       +        rrattach(rp, 0);
       +}
       +static void
       +dbtuple2cache(Ndbtuple *t)
       +{
       +        Ndbtuple *et, *nt;
       +        DN *dp;
       +
       +        for(et = t; et; et = et->entry){
       +                if(strcmp(et->attr, "dom") == 0){
       +                        dp = dnlookup(et->val, Cin, 1);
       +
       +                        /* first same line */
       +                        for(nt = et->line; nt != et; nt = nt->line){
       +                                dbpair2cache(dp, t, nt);
       +                                nt->ptr = 1;
       +                        }
       +
       +                        /* then rest of entry */
       +                        for(nt = t; nt; nt = nt->entry){
       +                                if(nt->ptr == 0)
       +                                        dbpair2cache(dp, t, nt);
       +                                nt->ptr = 0;
       +                        }
       +                }
       +        }
       +}
       +static void
       +dbfile2cache(Ndb *db)
       +{
       +        Ndbtuple *t;
       +
       +        if(debug)
       +                syslog(0, logfile, "rereading %s", db->file);
       +        Bseek(&db->b, 0, 0);
       +        while(t = ndbparse(db)){
       +                dbtuple2cache(t);
       +                ndbfree(t);
       +        }
       +}
       +void
       +db2cache(int doit)
       +{
       +        Ndb *ndb;
       +        Dir *d;
       +        ulong youngest, temp;
       +        static ulong lastcheck;
       +        static ulong lastyoungest;
       +
       +        /* no faster than once every 2 minutes */
       +        if(now < lastcheck + 2*Min && !doit)
       +                return;
       +
       +        refresh_areas(owned);
       +
       +        lock(&dblock);
       +
       +        if(opendatabase() < 0){
       +                unlock(&dblock);
       +                return;
       +        }
       +
       +        /*
       +         *  file may be changing as we are reading it, so loop till
       +         *  mod times are consistent.
       +         *
       +         *  we don't use the times in the ndb records because they may
       +         *  change outside of refreshing our cached knowledge.
       +         */
       +        for(;;){
       +                lastcheck = now;
       +                youngest = 0;
       +                for(ndb = db; ndb; ndb = ndb->next){
       +                        /* the dirfstat avoids walking the mount table each time */
       +                        if((d = dirfstat(Bfildes(&ndb->b))) != nil ||
       +                           (d = dirstat(ndb->file)) != nil){
       +                                temp = d->mtime;                /* ulong vs int crap */
       +                                if(temp > youngest)
       +                                        youngest = temp;
       +                                free(d);
       +                        }
       +                }
       +                if(!doit && youngest == lastyoungest){
       +                        unlock(&dblock);
       +                        return;
       +                }
       +        
       +                /* forget our area definition */
       +                freearea(&owned);
       +                freearea(&delegated);
       +        
       +                /* reopen all the files (to get oldest for time stamp) */
       +                for(ndb = db; ndb; ndb = ndb->next)
       +                        ndbreopen(ndb);
       +
       +                if(cachedb){
       +                        /* mark all db records as timed out */
       +                        dnagedb();
       +        
       +                        /* read in new entries */
       +                        for(ndb = db; ndb; ndb = ndb->next)
       +                                dbfile2cache(ndb);
       +        
       +                        /* mark as authentic anything in our domain */
       +                        dnauthdb();
       +        
       +                        /* remove old entries */
       +                        dnageall(1);
       +                } else {
       +                        /* read all the soa's to get database defaults */
       +                        for(ndb = db; ndb; ndb = ndb->next)
       +                                dbfile2area(ndb);
       +                }
       +
       +                doit = 0;
       +                lastyoungest = youngest;
       +                createptrs();
       +        }
       +
       +        unlock(&dblock);
       +}
       +
       +extern uchar        ipaddr[IPaddrlen];
       +
       +/*
       + *  get all my xxx
       + */
       +Ndbtuple*
       +lookupinfo(char *attr)
       +{
       +        char buf[64];
       +        char *a[2];
       +        static Ndbtuple *t;
       +
       +        snprint(buf, sizeof buf, "%I", ipaddr);
       +        a[0] = attr;
       +        
       +        lock(&dblock);
       +        if(opendatabase() < 0){
       +                unlock(&dblock);
       +                return nil;
       +        }
       +        t = ndbipinfo(db, "ip", buf, a, 1);
       +        unlock(&dblock);
       +        return t;
       +}
       +
       +char *localservers = "local#dns#servers";
       +char *localserverprefix = "local#dns#server";
       +
       +/*
       + *  return non-zero is this is a bad delegation
       + */
       +int
       +baddelegation(RR *rp, RR *nsrp, uchar *addr)
       +{
       +        Ndbtuple *nt;
       +        static Ndbtuple *t;
       +
       +        if(t == nil)
       +                t = lookupinfo("dom");
       +        if(t == nil)
       +                return 0;
       +
       +        for(; rp; rp = rp->next){
       +                if(rp->type != Tns)
       +                        continue;
       +
       +                /* see if delegation is looping */
       +                if(nsrp)
       +                if(rp->owner != nsrp->owner)
       +                if(subsume(rp->owner->name, nsrp->owner->name) &&
       +                   strcmp(nsrp->owner->name, localservers) != 0){
       +                        syslog(0, logfile, "delegation loop %R -> %R from %I", nsrp, rp, addr);
       +                        return 1;
       +                }
       +
       +                /* see if delegating to us what we don't own */
       +                for(nt = t; nt != nil; nt = nt->entry)
       +                        if(rp->host && cistrcmp(rp->host->name, nt->val) == 0)
       +                                break;
       +                if(nt != nil && !inmyarea(rp->owner->name)){
       +                        syslog(0, logfile, "bad delegation %R from %I", rp, addr);
       +                        return 1;
       +                }
       +        }
       +
       +        return 0;
       +}
       +
       +static void
       +addlocaldnsserver(DN *dp, int class, char *ipaddr, int i)
       +{
       +        DN *nsdp;
       +        RR *rp;
       +        char buf[32];
       +
       +        /* ns record for name server, make up an impossible name */
       +        rp = rralloc(Tns);
       +        snprint(buf, sizeof(buf), "%s%d", localserverprefix, i);
       +        nsdp = dnlookup(buf, class, 1);
       +        rp->host = nsdp;
       +        rp->owner = dp;
       +        rp->local = 1;
       +        rp->db = 1;
       +        rp->ttl = 10*Min;
       +        rrattach(rp, 1);
       +
       +print("dns %s\n", ipaddr);
       +        /* A record */
       +        rp = rralloc(Ta);
       +        rp->ip = dnlookup(ipaddr, class, 1);
       +        rp->owner = nsdp;
       +        rp->local = 1;
       +        rp->db = 1;
       +        rp->ttl = 10*Min;
       +        rrattach(rp, 1);
       +}
       +
       +/*
       + *  return list of dns server addresses to use when
       + *  acting just as a resolver.
       + */
       +RR*
       +dnsservers(int class)
       +{
       +        Ndbtuple *t, *nt;
       +        RR *nsrp;
       +        DN *dp;
       +        char *p;
       +        int i, n;
       +        char *buf, *args[5];
       +
       +        dp = dnlookup(localservers, class, 1);
       +        nsrp = rrlookup(dp, Tns, NOneg);
       +        if(nsrp != nil)
       +                return nsrp;
       +
       +        p = getenv("DNSSERVER");
       +        if(p != nil){
       +                buf = estrdup(p);
       +                n = tokenize(buf, args, nelem(args));
       +                for(i = 0; i < n; i++)
       +                        addlocaldnsserver(dp, class, args[i], i);
       +                free(buf);
       +        } else {
       +                t = lookupinfo("@dns");
       +                if(t == nil)
       +                        return nil;
       +                i = 0;
       +                for(nt = t; nt != nil; nt = nt->entry){
       +                        addlocaldnsserver(dp, class, nt->val, i);
       +                        i++;
       +                }
       +                ndbfree(t);
       +        }
       +
       +        return rrlookup(dp, Tns, NOneg);
       +}
       +
       +static void
       +addlocaldnsdomain(DN *dp, int class, char *domain)
       +{
       +        RR *rp;
       +
       +        /* A record */
       +        rp = rralloc(Tptr);
       +        rp->ptr = dnlookup(domain, class, 1);
       +        rp->owner = dp;
       +        rp->db = 1;
       +        rp->ttl = 10*Min;
       +        rrattach(rp, 1);
       +}
       +
       +/*
       + *  return list of domains to use when resolving names without '.'s
       + */
       +RR*
       +domainlist(int class)
       +{
       +        Ndbtuple *t, *nt;
       +        RR *rp;
       +        DN *dp;
       +
       +        dp = dnlookup("local#dns#domains", class, 1);
       +        rp = rrlookup(dp, Tptr, NOneg);
       +        if(rp != nil)
       +                return rp;
       +
       +        t = lookupinfo("dnsdomain");
       +        if(t == nil)
       +                return nil;
       +        for(nt = t; nt != nil; nt = nt->entry)
       +                addlocaldnsdomain(dp, class, nt->val);
       +        ndbfree(t);
       +
       +        return rrlookup(dp, Tptr, NOneg);
       +}
       +
       +char *v4ptrdom = ".in-addr.arpa";
       +char *v6ptrdom = ".ip6.arpa";                /* ip6.int deprecated, rfc 3152 */
       +
       +char *attribs[] = {
       +        "ipmask",
       +        0
       +};
       +
       +/*
       + *  create ptrs that are in our areas
       + */
       +static void
       +createptrs(void)
       +{
       +        int len, dlen, n;
       +        Area *s;
       +        char *f[40];
       +        char buf[Domlen+1];
       +        uchar net[IPaddrlen];
       +        uchar mask[IPaddrlen];
       +        char ipa[48];
       +        Ndbtuple *t, *nt;
       +
       +        dlen = strlen(v4ptrdom);
       +        for(s = owned; s; s = s->next){
       +                len = strlen(s->soarr->owner->name);
       +                if(len <= dlen)
       +                        continue;
       +                if(cistrcmp(s->soarr->owner->name+len-dlen, v4ptrdom) != 0)
       +                        continue;
       +
       +                /* get mask and net value */
       +                strncpy(buf, s->soarr->owner->name, sizeof(buf));
       +                buf[sizeof(buf)-1] = 0;
       +                n = getfields(buf, f, nelem(f), 0, ".");
       +                memset(mask, 0xff, IPaddrlen);
       +                ipmove(net, v4prefix);
       +                switch(n){
       +                case 3: /* /8 */
       +                        net[IPv4off] = atoi(f[0]);
       +                        mask[IPv4off+1] = 0;
       +                        mask[IPv4off+2] = 0;
       +                        mask[IPv4off+3] = 0;
       +                        break;
       +                case 4: /* /16 */
       +                        net[IPv4off] = atoi(f[1]);
       +                        net[IPv4off+1] = atoi(f[0]);
       +                        mask[IPv4off+2] = 0;
       +                        mask[IPv4off+3] = 0;
       +                        break;
       +                case 5: /* /24 */
       +                        net[IPv4off] = atoi(f[2]);
       +                        net[IPv4off+1] = atoi(f[1]);
       +                        net[IPv4off+2] = atoi(f[0]);
       +                        mask[IPv4off+3] = 0;
       +                        break;
       +                case 6:        /* rfc2317 */
       +                        net[IPv4off] = atoi(f[3]);
       +                        net[IPv4off+1] = atoi(f[2]);
       +                        net[IPv4off+2] = atoi(f[1]);
       +                        net[IPv4off+3] = atoi(f[0]);
       +                        sprint(ipa, "%I", net);
       +                        t = ndbipinfo(db, "ip", ipa, attribs, 1);
       +                        if(t == nil) /* could be a reverse with no forward */
       +                                continue;
       +                        nt = look(t, t, "ipmask");
       +                        if(nt == nil){        /* we're confused */
       +                                ndbfree(t);
       +                                continue;
       +                        }
       +                        parseipmask(mask, nt->val);
       +                        n = 5;
       +                        break;
       +                default:
       +                        continue;
       +                }
       +
       +                /* go through all domain entries looking for RR's in this network and create ptrs */
       +                dnptr(net, mask, s->soarr->owner->name, 6-n, 0);
       +        }
       +}
 (DIR) diff --git a/src/cmd/ndb/dn.c b/src/cmd/ndb/dn.c
       t@@ -0,0 +1,1563 @@
       +#include <u.h>
       +#include <libc.h>
       +#include <ip.h>
       +#include <ctype.h>
       +#include <bio.h>
       +#include <ndb.h>
       +#include "dns.h"
       +
       +/*
       + *  Hash table for domain names.  The hash is based only on the
       + *  first element of the domain name.
       + */
       +DN        *ht[HTLEN];
       +
       +
       +static struct
       +{
       +        Lock        lk;
       +        ulong        names;        /* names allocated */
       +        ulong        oldest;        /* longest we'll leave a name around */
       +        int        active;
       +        int        mutex;
       +        int        id;
       +} dnvars;
       +
       +/* names of RR types */
       +char *rrtname[] =
       +{
       +[Ta]                "ip",
       +[Tns]                "ns",
       +[Tmd]                "md",
       +[Tmf]                "mf",
       +[Tcname]        "cname",
       +[Tsoa]                "soa",
       +[Tmb]                "mb",
       +[Tmg]                "mg",
       +[Tmr]                "mr",
       +[Tnull]                "null",
       +[Twks]                "wks",
       +[Tptr]                "ptr",
       +[Thinfo]        "hinfo",
       +[Tminfo]        "minfo",
       +[Tmx]                "mx",
       +[Ttxt]                "txt",
       +[Trp]                "rp",
       +[Tkey]                "key",
       +[Tcert]                "cert",
       +[Tsig]                "sig",
       +[Taaaa]                "ipv6",
       +[Tixfr]                "ixfr",
       +[Taxfr]                "axfr",
       +[Tall]                "all",
       +                0,
       +};
       +
       +/* names of response codes */
       +char *rname[Rmask+1] =
       +{
       +[Rok]                        "ok",
       +[Rformat]                "format error",
       +[Rserver]                "server failure",
       +[Rname]                        "bad name",
       +[Runimplimented]        "unimplemented",
       +[Rrefused]                "we don't like you",
       +};
       +
       +/* names of op codes */
       +char *opname[] =
       +{
       +[Oquery]        "query",
       +[Oinverse]        "inverse",
       +[Ostatus]        "status",
       +};
       +
       +Lock        dnlock;
       +
       +static int sencodefmt(Fmt*);
       +
       +/*
       + *  set up a pipe to use as a lock
       + */
       +void
       +dninit(void)
       +{
       +        fmtinstall('E', eipfmt);
       +        fmtinstall('I', eipfmt);
       +        fmtinstall('V', eipfmt);
       +        fmtinstall('R', rrfmt);
       +        fmtinstall('Q', rravfmt);
       +        fmtinstall('H', sencodefmt);
       +
       +        dnvars.oldest = maxage;
       +        dnvars.names = 0;
       +}
       +
       +/*
       + *  hash for a domain name
       + */
       +static ulong
       +dnhash(char *name)
       +{
       +        ulong hash;
       +        uchar *val = (uchar*)name;
       +
       +        for(hash = 0; *val; val++)
       +                hash = (hash*13) + tolower(*val)-'a';
       +        return hash % HTLEN;
       +}
       +
       +/*
       + *  lookup a symbol.  if enter is not zero and the name is
       + *  not found, create it.
       + */
       +DN*
       +dnlookup(char *name, int class, int enter)
       +{
       +        DN **l;
       +        DN *dp;
       +
       +        l = &ht[dnhash(name)];
       +        lock(&dnlock);
       +        for(dp = *l; dp; dp = dp->next) {
       +                assert(dp->magic == DNmagic);
       +                if(dp->class == class && cistrcmp(dp->name, name) == 0){
       +                        dp->referenced = now;
       +                        unlock(&dnlock);
       +                        return dp;
       +                }
       +                l = &dp->next;
       +        }
       +        if(enter == 0){
       +                unlock(&dnlock);
       +                return 0;
       +        }
       +        dnvars.names++;
       +        dp = emalloc(sizeof(*dp));
       +        dp->magic = DNmagic;
       +        dp->name = estrdup(name);
       +        assert(dp->name != 0);
       +        dp->class = class;
       +        dp->rr = 0;
       +        dp->next = 0;
       +        dp->referenced = now;
       +        *l = dp;
       +        unlock(&dnlock);
       +
       +        return dp;
       +}
       +
       +/*
       + *  dump the cache
       + */
       +void
       +dndump(char *file)
       +{
       +        DN *dp;
       +        int i, fd;
       +        RR *rp;
       +
       +        fd = open(file, OWRITE|OTRUNC);
       +        if(fd < 0)
       +                return;
       +        lock(&dnlock);
       +        for(i = 0; i < HTLEN; i++){
       +                for(dp = ht[i]; dp; dp = dp->next){
       +                        fprint(fd, "%s\n", dp->name);
       +                        for(rp = dp->rr; rp; rp = rp->next)
       +                                fprint(fd, "        %R %c%c %lud/%lud\n", rp, rp->auth?'A':'U',
       +                                        rp->db?'D':'N', rp->expire, rp->ttl);
       +                }
       +        }
       +        unlock(&dnlock);
       +        close(fd);
       +}
       +
       +/*
       + *  purge all records
       + */
       +void
       +dnpurge(void)
       +{
       +        DN *dp;
       +        RR *rp, *srp;
       +        int i;
       +
       +        lock(&dnlock);
       +
       +        for(i = 0; i < HTLEN; i++)
       +                for(dp = ht[i]; dp; dp = dp->next){
       +                        srp = rp = dp->rr;
       +                        dp->rr = nil;
       +                        for(; rp != nil; rp = rp->next)
       +                                rp->cached = 0;
       +                        rrfreelist(srp);
       +                }
       +
       +        unlock(&dnlock);
       +}
       +
       +/*
       + *  check the age of resource records, free any that have timed out
       + */
       +void
       +dnage(DN *dp)
       +{
       +        RR **l;
       +        RR *rp, *next;
       +        ulong diff;
       +
       +        diff = now - dp->referenced;
       +        if(diff < Reserved)
       +                return;
       +
       +        l = &dp->rr;
       +        for(rp = dp->rr; rp; rp = next){
       +                assert(rp->magic == RRmagic && rp->cached);
       +                next = rp->next;
       +                if(!rp->db)
       +                if(rp->expire < now || diff > dnvars.oldest){
       +                        *l = next;
       +                        rp->cached = 0;
       +                        rrfree(rp);
       +                        continue;
       +                }
       +                l = &rp->next;
       +        }
       +}
       +
       +#define REF(x) if(x) x->refs++
       +
       +/*
       + *  our target is 4000 names cached, this should be larger on large servers
       + */
       +#define TARGET 4000
       +
       +/*
       + *  periodicly sweep for old records and remove unreferenced domain names
       + *
       + *  only called when all other threads are locked out
       + */
       +void
       +dnageall(int doit)
       +{
       +        DN *dp, **l;
       +        int i;
       +        RR *rp;
       +        static ulong nextage;
       +
       +        if(dnvars.names < TARGET && now < nextage && !doit){
       +                dnvars.oldest = maxage;
       +                return;
       +        }
       +
       +        if(dnvars.names > TARGET)
       +                dnvars.oldest /= 2;
       +        nextage = now + maxage;
       +
       +        lock(&dnlock);
       +
       +        /* time out all old entries (and set refs to 0) */
       +        for(i = 0; i < HTLEN; i++)
       +                for(dp = ht[i]; dp; dp = dp->next){
       +                        dp->refs = 0;
       +                        dnage(dp);
       +                }
       +
       +        /* mark all referenced domain names */
       +        for(i = 0; i < HTLEN; i++)
       +                for(dp = ht[i]; dp; dp = dp->next)
       +                        for(rp = dp->rr; rp; rp = rp->next){
       +                                REF(rp->owner);
       +                                if(rp->negative){
       +                                        REF(rp->negsoaowner);
       +                                        continue;
       +                                }
       +                                switch(rp->type){
       +                                case Thinfo:
       +                                        REF(rp->cpu);
       +                                        REF(rp->os);
       +                                        break;
       +                                case Ttxt:
       +                                        break;
       +                                case Tcname:
       +                                case Tmb:
       +                                case Tmd:
       +                                case Tmf:
       +                                case Tns:
       +                                        REF(rp->host);
       +                                        break;
       +                                case Tmg:
       +                                case Tmr:
       +                                        REF(rp->mb);
       +                                        break;
       +                                case Tminfo:
       +                                        REF(rp->rmb);
       +                                        REF(rp->mb);
       +                                        break;
       +                                case Trp:
       +                                        REF(rp->rmb);
       +                                        REF(rp->rp);
       +                                        break;
       +                                case Tmx:
       +                                        REF(rp->host);
       +                                        break;
       +                                case Ta:
       +                                case Taaaa:
       +                                        REF(rp->ip);
       +                                        break;
       +                                case Tptr:
       +                                        REF(rp->ptr);
       +                                        break;
       +                                case Tsoa:
       +                                        REF(rp->host);
       +                                        REF(rp->rmb);
       +                                        break;
       +                                }
       +                        }
       +
       +        /* sweep and remove unreferenced domain names */
       +        for(i = 0; i < HTLEN; i++){
       +                l = &ht[i];
       +                for(dp = *l; dp; dp = *l){
       +                        if(dp->rr == 0 && dp->refs == 0){
       +                                assert(dp->magic == DNmagic);
       +                                *l = dp->next;
       +                                if(dp->name)
       +                                        free(dp->name);
       +                                dp->magic = ~dp->magic;
       +                                dnvars.names--;
       +                                free(dp);
       +                                continue;
       +                        }
       +                        l = &dp->next;
       +                }
       +        }
       +
       +        unlock(&dnlock);
       +}
       +
       +/*
       + *  timeout all database records (used when rereading db)
       + */
       +void
       +dnagedb(void)
       +{
       +        DN *dp;
       +        int i;
       +        RR *rp;
       +        static ulong nextage;
       +
       +        lock(&dnlock);
       +
       +        /* time out all database entries */
       +        for(i = 0; i < HTLEN; i++)
       +                for(dp = ht[i]; dp; dp = dp->next)
       +                        for(rp = dp->rr; rp; rp = rp->next)
       +                                if(rp->db)
       +                                        rp->expire = 0;
       +
       +        unlock(&dnlock);
       +}
       +
       +/*
       + *  mark all local db records about my area as authoritative, time out any others
       + */
       +void
       +dnauthdb(void)
       +{
       +        DN *dp;
       +        int i;
       +        Area *area;
       +        RR *rp;
       +        static ulong nextage;
       +
       +        lock(&dnlock);
       +
       +        /* time out all database entries */
       +        for(i = 0; i < HTLEN; i++)
       +                for(dp = ht[i]; dp; dp = dp->next){
       +                        area = inmyarea(dp->name);
       +                        for(rp = dp->rr; rp; rp = rp->next)
       +                                if(rp->db){
       +                                        if(area){
       +                                                if(rp->ttl < area->soarr->soa->minttl)
       +                                                        rp->ttl = area->soarr->soa->minttl;
       +                                                rp->auth = 1;
       +                                        }
       +                                        if(rp->expire == 0){
       +                                                rp->db = 0;
       +                                                dp->referenced = now - Reserved - 1;
       +                                        }
       +                                }
       +                }
       +
       +        unlock(&dnlock);
       +}
       +
       +/*
       + *  keep track of other processes to know if we can
       + *  garbage collect.  block while garbage collecting.
       + */
       +int
       +getactivity(Request *req)
       +{
       +        int rv;
       +
       +        if(traceactivity) syslog(0, "dns", "get %d by %d", dnvars.active, getpid());
       +        lock(&dnvars.lk);
       +        while(dnvars.mutex){
       +                unlock(&dnvars.lk);
       +                sleep(200);
       +                lock(&dnvars.lk);
       +        }
       +        rv = ++dnvars.active;
       +        now = time(0);
       +        req->id = ++dnvars.id;
       +        unlock(&dnvars.lk);
       +
       +        return rv;
       +}
       +void
       +putactivity(void)
       +{
       +        static ulong lastclean;
       +
       +        if(traceactivity) syslog(0, "dns", "put %d by %d", dnvars.active, getpid());
       +        lock(&dnvars.lk);
       +        dnvars.active--;
       +        assert(dnvars.active >= 0); /* "dnvars.active %d", dnvars.active */;
       +
       +        /*
       +         *  clean out old entries and check for new db periodicly
       +         */
       +        if(dnvars.mutex || (needrefresh == 0 && dnvars.active > 0)){
       +                unlock(&dnvars.lk);
       +                return;
       +        }
       +
       +        /* wait till we're alone */
       +        dnvars.mutex = 1;
       +        while(dnvars.active > 0){
       +                unlock(&dnvars.lk);
       +                sleep(100);
       +                lock(&dnvars.lk);
       +        }
       +        unlock(&dnvars.lk);
       +
       +        db2cache(needrefresh);
       +        dnageall(0);
       +
       +        /* let others back in */
       +        lastclean = now;
       +        needrefresh = 0;
       +        dnvars.mutex = 0;
       +}
       +
       +/*
       + *  Attach a single resource record to a domain name.
       + *        - Avoid duplicates with already present RR's
       + *        - Chain all RR's of the same type adjacent to one another
       + *        - chain authoritative RR's ahead of non-authoritative ones
       + */
       +static void
       +rrattach1(RR *new, int auth)
       +{
       +        RR **l;
       +        RR *rp;
       +        DN *dp;
       +
       +        assert(new->magic == RRmagic && !new->cached);
       +
       +        if(!new->db)
       +                new->expire = new->ttl;
       +        else
       +                new->expire = now + Year;
       +        dp = new->owner;
       +        assert(dp->magic == DNmagic);
       +        new->auth |= auth;
       +        new->next = 0;
       +
       +        /*
       +         *  find first rr of the right type
       +         */
       +        l = &dp->rr;
       +        for(rp = *l; rp; rp = *l){
       +                assert(rp->magic == RRmagic && rp->cached);
       +                if(rp->type == new->type)
       +                        break;
       +                l = &rp->next;
       +        }
       +
       +        /*
       +         *  negative entries replace positive entries
       +         *  positive entries replace negative entries
       +         *  newer entries replace older entries with the same fields
       +         */
       +        for(rp = *l; rp; rp = *l){
       +                assert(rp->magic == RRmagic && rp->cached);
       +                if(rp->type != new->type)
       +                        break;
       +
       +                if(rp->db == new->db && rp->auth == new->auth){
       +                        /* negative drives out positive and vice versa */
       +                        if(rp->negative != new->negative){
       +                                *l = rp->next;
       +                                rp->cached = 0;
       +                                rrfree(rp);
       +                                continue;
       +                        }
       +
       +                        /* all things equal, pick the newer one */
       +                        if(rp->arg0 == new->arg0 && rp->arg1 == new->arg1){
       +                                /* new drives out old */
       +                                if(new->ttl > rp->ttl || new->expire > rp->expire){
       +                                        *l = rp->next;
       +                                        rp->cached = 0;
       +                                        rrfree(rp);
       +                                        continue;
       +                                } else {
       +                                        rrfree(new);
       +                                        return;
       +                                }
       +                        }
       +
       +                        /*  Hack for pointer records.  This makes sure
       +                         *  the ordering in the list reflects the ordering
       +                         *  received or read from the database
       +                         */
       +                        if(rp->type == Tptr){
       +                                if(!rp->negative && !new->negative
       +                                && rp->ptr->ordinal > new->ptr->ordinal)
       +                                        break;
       +                        }
       +                }
       +                l = &rp->next;
       +        }
       +
       +        /*
       +         *  add to chain
       +         */
       +        new->cached = 1;
       +        new->next = *l;
       +        *l = new;
       +}
       +
       +/*
       + *  Attach a list of resource records to a domain name.
       + *        - Avoid duplicates with already present RR's
       + *        - Chain all RR's of the same type adjacent to one another
       + *        - chain authoritative RR's ahead of non-authoritative ones
       + *        - remove any expired RR's
       + */
       +void
       +rrattach(RR *rp, int auth)
       +{
       +        RR *next;
       +
       +        lock(&dnlock);
       +        for(; rp; rp = next){
       +                next = rp->next;
       +                rp->next = 0;
       +
       +                /* avoid any outside spoofing */
       +                if(cachedb && !rp->db && inmyarea(rp->owner->name))
       +                        rrfree(rp);
       +                else
       +                        rrattach1(rp, auth);
       +        }
       +        unlock(&dnlock);
       +}
       +
       +/*
       + *  allocate a resource record of a given type
       + */
       +RR*
       +rralloc(int type)
       +{
       +        RR *rp;
       +
       +        rp = emalloc(sizeof(*rp));
       +        rp->magic = RRmagic;
       +        rp->pc = getcallerpc(&type);
       +        rp->type = type;
       +        switch(type){
       +        case Tsoa:
       +                rp->soa = emalloc(sizeof(*rp->soa));
       +                rp->soa->slaves = nil;
       +                break;
       +        case Tkey:
       +                rp->key = emalloc(sizeof(*rp->key));
       +                break;
       +        case Tcert:
       +                rp->cert = emalloc(sizeof(*rp->cert));
       +                break;
       +        case Tsig:
       +                rp->sig = emalloc(sizeof(*rp->sig));
       +                break;
       +        case Tnull:
       +                rp->null = emalloc(sizeof(*rp->null));
       +                break;
       +        }
       +        rp->ttl = 0;
       +        rp->expire = 0;
       +        rp->next = 0;
       +        return rp;
       +}
       +
       +/*
       + *  free a resource record and any related structs
       + */
       +void
       +rrfree(RR *rp)
       +{
       +        DN *dp;
       +        RR *nrp;
       +        Txt *t;
       +
       +        assert(rp->magic = RRmagic);
       +        assert(!rp->cached);
       +
       +        dp = rp->owner;
       +        if(dp){
       +                assert(dp->magic == DNmagic);
       +                for(nrp = dp->rr; nrp; nrp = nrp->next)
       +                        assert(nrp != rp); /* "rrfree of live rr" */;
       +        }
       +
       +        switch(rp->type){
       +        case Tsoa:
       +                freeserverlist(rp->soa->slaves);
       +                free(rp->soa);
       +                break;
       +        case Tkey:
       +                free(rp->key->data);
       +                free(rp->key);
       +                break;
       +        case Tcert:
       +                free(rp->cert->data);
       +                free(rp->cert);
       +                break;
       +        case Tsig:
       +                free(rp->sig->data);
       +                free(rp->sig);
       +                break;
       +        case Tnull:
       +                free(rp->null->data);
       +                free(rp->null);
       +                break;
       +        case Ttxt:
       +                while(rp->txt != nil){
       +                        t = rp->txt;
       +                        rp->txt = t->next;
       +                        free(t->p);
       +                        free(t);
       +                }
       +                break;
       +        }
       +
       +        rp->magic = ~rp->magic;
       +        free(rp);
       +}
       +
       +/*
       + *  free a list of resource records and any related structs
       + */
       +void
       +rrfreelist(RR *rp)
       +{
       +        RR *next;
       +
       +        for(; rp; rp = next){
       +                next = rp->next;
       +                rrfree(rp);
       +        }
       +}
       +
       +extern RR**
       +rrcopy(RR *rp, RR **last)
       +{
       +        RR *nrp;
       +        SOA *soa;
       +        Key *key;
       +        Cert *cert;
       +        Sig *sig;
       +        Null *null;
       +        Txt *t, *nt, **l;
       +
       +        nrp = rralloc(rp->type);
       +        switch(rp->type){
       +        case Ttxt:
       +                *nrp = *rp;
       +                l = &nrp->txt;
       +                *l = nil;
       +                for(t = rp->txt; t != nil; t = t->next){
       +                        nt = emalloc(sizeof(*nt));
       +                        nt->p = estrdup(t->p);
       +                        nt->next = nil;
       +                        *l = nt;
       +                        l = &nt->next;
       +                }
       +                break;
       +        case Tsoa:
       +                soa = nrp->soa;
       +                *nrp = *rp;
       +                nrp->soa = soa;
       +                *nrp->soa = *rp->soa;
       +                nrp->soa->slaves = copyserverlist(rp->soa->slaves);
       +                break;
       +        case Tkey:
       +                key = nrp->key;
       +                *nrp = *rp;
       +                nrp->key = key;
       +                *key = *rp->key;
       +                key->data = emalloc(key->dlen);
       +                memmove(key->data, rp->key->data, rp->key->dlen);
       +                break;
       +        case Tsig:
       +                sig = nrp->sig;
       +                *nrp = *rp;
       +                nrp->sig = sig;
       +                *sig = *rp->sig;
       +                sig->data = emalloc(sig->dlen);
       +                memmove(sig->data, rp->sig->data, rp->sig->dlen);
       +                break;
       +        case Tcert:
       +                cert = nrp->cert;
       +                *nrp = *rp;
       +                nrp->cert = cert;
       +                *cert = *rp->cert;
       +                cert->data = emalloc(cert->dlen);
       +                memmove(cert->data, rp->cert->data, rp->cert->dlen);
       +                break;
       +        case Tnull:
       +                null = nrp->null;
       +                *nrp = *rp;
       +                nrp->null = null;
       +                *null = *rp->null;
       +                null->data = emalloc(null->dlen);
       +                memmove(null->data, rp->null->data, rp->null->dlen);
       +                break;
       +        default:
       +                *nrp = *rp;
       +                break;
       +        }
       +        nrp->cached = 0;
       +        nrp->next = 0;
       +        *last = nrp;
       +        return &nrp->next;
       +}
       +
       +/*
       + *  lookup a resource record of a particular type and
       + *  class attached to a domain name.  Return copies.
       + *
       + *  Priority ordering is:
       + *        db authoritative
       + *        not timed out network authoritative
       + *        not timed out network unauthoritative
       + *        unauthoritative db
       + *
       + *  if flag NOneg is set, don't return negative cached entries.
       + *  return nothing instead.
       + */
       +RR*
       +rrlookup(DN *dp, int type, int flag)
       +{
       +        RR *rp, *first, **last;
       +
       +        assert(dp->magic == DNmagic);
       +
       +        first = 0;
       +        last = &first;
       +        lock(&dnlock);
       +
       +        /* try for an authoritative db entry */
       +        for(rp = dp->rr; rp; rp = rp->next){
       +                assert(rp->magic == RRmagic && rp->cached);
       +                if(rp->db)
       +                if(rp->auth)
       +                if(tsame(type, rp->type))
       +                        last = rrcopy(rp, last);
       +        }
       +        if(first)
       +                goto out;
       +
       +        /* try for an living authoritative network entry */
       +        for(rp = dp->rr; rp; rp = rp->next){
       +                if(!rp->db)
       +                if(rp->auth)
       +                if(rp->ttl + 60 > now)
       +                if(tsame(type, rp->type)){
       +                        if(flag == NOneg && rp->negative)
       +                                goto out;
       +                        last = rrcopy(rp, last);
       +                }
       +        }
       +        if(first)
       +                goto out;
       +
       +        /* try for an living unauthoritative network entry */
       +        for(rp = dp->rr; rp; rp = rp->next){
       +                if(!rp->db)
       +                if(rp->ttl + 60 > now)
       +                if(tsame(type, rp->type)){
       +                        if(flag == NOneg && rp->negative)
       +                                goto out;
       +                        last = rrcopy(rp, last);
       +                }
       +        }
       +        if(first)
       +                goto out;
       +
       +        /* try for an unauthoritative db entry */
       +        for(rp = dp->rr; rp; rp = rp->next){
       +                if(rp->db)
       +                if(tsame(type, rp->type))
       +                        last = rrcopy(rp, last);
       +        }
       +        if(first)
       +                goto out;
       +
       +        /* otherwise, settle for anything we got (except for negative caches)  */
       +        for(rp = dp->rr; rp; rp = rp->next){
       +                if(tsame(type, rp->type)){
       +                        if(rp->negative)
       +                                goto out;
       +                        last = rrcopy(rp, last);
       +                }
       +        }
       +
       +out:
       +        unlock(&dnlock);
       +        unique(first);
       +        return first;
       +}
       +
       +/*
       + *  convert an ascii RR type name to its integer representation
       + */
       +int
       +rrtype(char *atype)
       +{
       +        int i;
       +
       +        for(i = 0; i <= Tall; i++)
       +                if(rrtname[i] && strcmp(rrtname[i], atype) == 0)
       +                        return i;
       +
       +        // make any a synonym for all
       +        if(strcmp(atype, "any") == 0)
       +                return Tall;
       +        return atoi(atype);
       +}
       +
       +/*
       + *  convert an integer RR type to it's ascii name
       + */
       +char*
       +rrname(int type, char *buf, int len)
       +{
       +        char *t;
       +
       +        t = 0;
       +        if(type <= Tall)
       +                t = rrtname[type];
       +        if(t==0){
       +                snprint(buf, len, "%d", type);
       +                t = buf;
       +        }
       +        return t;
       +}
       +
       +/*
       + *  return 0 if not a supported rr type
       + */
       +int
       +rrsupported(int type)
       +{
       +        if(type < 0 || type >Tall)
       +                return 0;
       +        return rrtname[type] != 0;
       +}
       +
       +/*
       + *  compare 2 types
       + */
       +int
       +tsame(int t1, int t2)
       +{
       +        return t1 == t2 || t1 == Tall;
       +}
       +
       +/*
       + *  Add resource records to a list, duplicate them if they are cached
       + *  RR's since these are shared.
       + */
       +RR*
       +rrcat(RR **start, RR *rp)
       +{
       +        RR **last;
       +
       +        last = start;
       +        while(*last != 0)
       +                last = &(*last)->next;
       +
       +        *last = rp;
       +        return *start;
       +}
       +
       +/*
       + *  remove negative cache rr's from an rr list
       + */
       +RR*
       +rrremneg(RR **l)
       +{
       +        RR **nl, *rp;
       +        RR *first;
       +
       +        first = nil;
       +        nl = &first;
       +        while(*l != nil){
       +                rp = *l;
       +                if(rp->negative){
       +                        *l = rp->next;
       +                        *nl = rp;
       +                        nl = &rp->next;
       +                        *nl = nil;
       +                } else
       +                        l = &rp->next;
       +        }
       +
       +        return first;
       +}
       +
       +/*
       + *  remove rr's of a particular type from an rr list
       + */
       +RR*
       +rrremtype(RR **l, int type)
       +{
       +        RR **nl, *rp;
       +        RR *first;
       +
       +        first = nil;
       +        nl = &first;
       +        while(*l != nil){
       +                rp = *l;
       +                if(rp->type == type){
       +                        *l = rp->next;
       +                        *nl = rp;
       +                        nl = &rp->next;
       +                        *nl = nil;
       +                } else
       +                        l = &(*l)->next;
       +        }
       +
       +        return first;
       +}
       +
       +/*
       + *  print conversion for rr records
       + */
       +int
       +rrfmt(Fmt *f)
       +{
       +        RR *rp;
       +        char *strp;
       +        Fmt fstr;
       +        int rv;
       +        char buf[Domlen];
       +        Server *s;
       +        Txt *t;
       +
       +        fmtstrinit(&fstr);
       +
       +        rp = va_arg(f->args, RR*);
       +        if(rp == 0){
       +                fmtprint(&fstr, "<null>");
       +                goto out;
       +        }
       +
       +        fmtprint(&fstr, "%s %s", rp->owner->name,
       +                rrname(rp->type, buf, sizeof buf));
       +
       +        if(rp->negative){
       +                fmtprint(&fstr, "\tnegative - rcode %d", rp->negrcode);
       +                goto out;
       +        }
       +
       +        switch(rp->type){
       +        case Thinfo:
       +                fmtprint(&fstr, "\t%s %s", rp->cpu->name, rp->os->name);
       +                break;
       +        case Tcname:
       +        case Tmb:
       +        case Tmd:
       +        case Tmf:
       +        case Tns:
       +                fmtprint(&fstr, "\t%s", rp->host->name);
       +                break;
       +        case Tmg:
       +        case Tmr:
       +                fmtprint(&fstr, "\t%s", rp->mb->name);
       +                break;
       +        case Tminfo:
       +                fmtprint(&fstr, "\t%s %s", rp->mb->name, rp->rmb->name);
       +                break;
       +        case Tmx:
       +                fmtprint(&fstr, "\t%lud %s", rp->pref, rp->host->name);
       +                break;
       +        case Ta:
       +        case Taaaa:
       +                fmtprint(&fstr, "\t%s", rp->ip->name);
       +                break;
       +        case Tptr:
       +//                fmtprint(&fstr, "\t%s(%lud)", rp->ptr->name, rp->ptr->ordinal);
       +                fmtprint(&fstr, "\t%s", rp->ptr->name);
       +                break;
       +        case Tsoa:
       +                fmtprint(&fstr, "\t%s %s %lud %lud %lud %lud %lud", rp->host->name,
       +                        rp->rmb->name, rp->soa->serial, rp->soa->refresh, rp->soa->retry,
       +                        rp->soa->expire, rp->soa->minttl);
       +                for(s = rp->soa->slaves; s != nil; s = s->next)
       +                        fmtprint(&fstr, " %s", s->name);
       +                break;
       +        case Tnull:
       +                fmtprint(&fstr, "\t%.*H", rp->null->dlen, rp->null->data);
       +                break;
       +        case Ttxt:
       +                fmtprint(&fstr, "\t");
       +                for(t = rp->txt; t != nil; t = t->next)
       +                        fmtprint(&fstr, "%s", t->p);
       +                break;
       +        case Trp:
       +                fmtprint(&fstr, "\t%s %s", rp->rmb->name, rp->rp->name);
       +                break;
       +        case Tkey:
       +                fmtprint(&fstr, "\t%d %d %d", rp->key->flags, rp->key->proto,
       +                        rp->key->alg);
       +                break;
       +        case Tsig:
       +                fmtprint(&fstr, "\t%d %d %d %lud %lud %lud %d %s",
       +                        rp->sig->type, rp->sig->alg, rp->sig->labels, rp->sig->ttl,
       +                        rp->sig->exp, rp->sig->incep, rp->sig->tag, rp->sig->signer->name);
       +                break;
       +        case Tcert:
       +                fmtprint(&fstr, "\t%d %d %d",
       +                        rp->sig->type, rp->sig->tag, rp->sig->alg);
       +                break;
       +        default:
       +                break;
       +        }
       +out:
       +        strp = fmtstrflush(&fstr);
       +        rv = fmtstrcpy(f, strp);
       +        free(strp);
       +        return rv;
       +}
       +
       +/*
       + *  print conversion for rr records in attribute value form
       + */
       +int
       +rravfmt(Fmt *f)
       +{
       +        RR *rp;
       +        char *strp;
       +        Fmt fstr;
       +        int rv;
       +        Server *s;
       +        Txt *t;
       +        int quote;
       +
       +        fmtstrinit(&fstr);
       +
       +        rp = va_arg(f->args, RR*);
       +        if(rp == 0){
       +                fmtprint(&fstr, "<null>");
       +                goto out;
       +        }
       +
       +        if(rp->type == Tptr)
       +                fmtprint(&fstr, "ptr=%s", rp->owner->name);
       +        else
       +                fmtprint(&fstr, "dom=%s", rp->owner->name);
       +
       +        switch(rp->type){
       +        case Thinfo:
       +                fmtprint(&fstr, " cpu=%s os=%s", rp->cpu->name, rp->os->name);
       +                break;
       +        case Tcname:
       +                fmtprint(&fstr, " cname=%s", rp->host->name);
       +                break;
       +        case Tmb:
       +        case Tmd:
       +        case Tmf:
       +                fmtprint(&fstr, " mbox=%s", rp->host->name);
       +                break;
       +        case Tns:
       +                fmtprint(&fstr,  " ns=%s", rp->host->name);
       +                break;
       +        case Tmg:
       +        case Tmr:
       +                fmtprint(&fstr, " mbox=%s", rp->mb->name);
       +                break;
       +        case Tminfo:
       +                fmtprint(&fstr, " mbox=%s mbox=%s", rp->mb->name, rp->rmb->name);
       +                break;
       +        case Tmx:
       +                fmtprint(&fstr, " pref=%lud mx=%s", rp->pref, rp->host->name);
       +                break;
       +        case Ta:
       +        case Taaaa:
       +                fmtprint(&fstr, " ip=%s", rp->ip->name);
       +                break;
       +        case Tptr:
       +                fmtprint(&fstr, " dom=%s", rp->ptr->name);
       +                break;
       +        case Tsoa:
       +                fmtprint(&fstr, " ns=%s mbox=%s serial=%lud refresh=%lud retry=%lud expire=%lud ttl=%lud",
       +                        rp->host->name, rp->rmb->name, rp->soa->serial,
       +                        rp->soa->refresh, rp->soa->retry,
       +                        rp->soa->expire, rp->soa->minttl);
       +                for(s = rp->soa->slaves; s != nil; s = s->next)
       +                        fmtprint(&fstr, " dnsslave=%s", s->name);
       +                break;
       +        case Tnull:
       +                fmtprint(&fstr, " null=%.*H", rp->null->dlen, rp->null->data);
       +                break;
       +        case Ttxt:
       +                fmtprint(&fstr, " txt=");
       +                quote = 0;
       +                for(t = rp->txt; t != nil; t = t->next)
       +                        if(strchr(t->p, ' '))
       +                                quote = 1;
       +                if(quote)
       +                        fmtprint(&fstr, "\"");
       +                for(t = rp->txt; t != nil; t = t->next)
       +                        fmtprint(&fstr, "%s", t->p);
       +                if(quote)
       +                        fmtprint(&fstr, "\"");
       +                break;
       +        case Trp:
       +                fmtprint(&fstr, " rp=%s txt=%s", rp->rmb->name, rp->rp->name);
       +                break;
       +        case Tkey:
       +                fmtprint(&fstr, " flags=%d proto=%d alg=%d",
       +                        rp->key->flags, rp->key->proto, rp->key->alg);
       +                break;
       +        case Tsig:
       +                fmtprint(&fstr, " type=%d alg=%d labels=%d ttl=%lud exp=%lud incep=%lud tag=%d signer=%s",
       +                        rp->sig->type, rp->sig->alg, rp->sig->labels, rp->sig->ttl,
       +                        rp->sig->exp, rp->sig->incep, rp->sig->tag, rp->sig->signer->name);
       +                break;
       +        case Tcert:
       +                fmtprint(&fstr, " type=%d tag=%d alg=%d",
       +                        rp->sig->type, rp->sig->tag, rp->sig->alg);
       +                break;
       +        default:
       +                break;
       +        }
       +out:
       +        strp = fmtstrflush(&fstr);
       +        rv = fmtstrcpy(f, strp);
       +        free(strp);
       +        return rv;
       +}
       +
       +void
       +warning(char *fmt, ...)
       +{
       +        char dnserr[128];
       +        va_list arg;
       +
       +        va_start(arg, fmt);
       +        vseprint(dnserr, dnserr+sizeof(dnserr), fmt, arg);
       +        va_end(arg);
       +        syslog(1, "dns", dnserr);
       +}
       +
       +/*
       + *  create a slave process to handle a request to avoid one request blocking
       + *  another
       + */
       +void
       +slave(Request *req)
       +{
       +        static int slaveid;
       +
       +        if(req->isslave)
       +                return;                /* we're already a slave process */
       +
       +        /* limit parallelism */
       +        if(getactivity(req) > Maxactive){
       +                putactivity();
       +                return;
       +        }
       +
       +        switch(rfork(RFPROC|RFNOTEG|RFMEM|RFNOWAIT)){
       +        case -1:
       +                putactivity();
       +                break;
       +        case 0:
       +                req->isslave = 1;
       +                break;
       +        default:
       +                longjmp(req->mret, 1);
       +        }
       +}
       +
       +/*
       + *  chasing down double free's
       + */
       +void
       +dncheck(void *p, int dolock)
       +{
       +        int i;
       +        DN *dp;
       +        RR *rp;
       +
       +        if(p != nil){
       +                dp = p;
       +                assert(dp->magic == DNmagic);
       +        }
       +
       +        if(!testing)
       +                return;
       +
       +        if(dolock)
       +                lock(&dnlock);
       +        for(i = 0; i < HTLEN; i++)
       +                for(dp = ht[i]; dp; dp = dp->next){
       +                        assert(dp != p);
       +                        assert(dp->magic == DNmagic);
       +                        for(rp = dp->rr; rp; rp = rp->next){
       +                                assert(rp->magic == RRmagic);
       +                                assert(rp->cached);
       +                                assert(rp->owner == dp);
       +                        }
       +                }
       +        if(dolock)
       +                unlock(&dnlock);
       +}
       +
       +static int
       +rrequiv(RR *r1, RR *r2)
       +{
       +        return r1->owner == r2->owner
       +                && r1->type == r2->type
       +                && r1->arg0 == r2->arg0
       +                && r1->arg1 == r2->arg1;
       +}
       +
       +void
       +unique(RR *rp)
       +{
       +        RR **l, *nrp;
       +
       +        for(; rp; rp = rp->next){
       +                l = &rp->next;
       +                for(nrp = *l; nrp; nrp = *l){
       +                        if(rrequiv(rp, nrp)){
       +                                *l = nrp->next;
       +                                rrfree(nrp);
       +                        } else
       +                                l = &nrp->next;
       +                }
       +        }
       +}
       +
       +/*
       + *  true if second domain is subsumed by the first
       + */
       +int
       +subsume(char *higher, char *lower)
       +{
       +        int hn, ln;
       +
       +        ln = strlen(lower);
       +        hn = strlen(higher);
       +        if(ln < hn)
       +                return 0;
       +
       +        if(cistrcmp(lower + ln - hn, higher) != 0)
       +                return 0;
       +
       +        if(ln > hn && hn != 0 && lower[ln - hn - 1] != '.')
       +                return 0;
       +
       +        return 1;
       +}
       +
       +/*
       + *  randomize the order we return items to provide some
       + *  load balancing for servers.
       + *
       + *  only randomize the first class of entries
       + */
       +RR*
       +randomize(RR *rp)
       +{
       +        RR *first, *last, *x, *base;
       +        ulong n;
       +
       +        if(rp == nil || rp->next == nil)
       +                return rp;
       +
       +        /* just randomize addresses and mx's */
       +        for(x = rp; x; x = x->next)
       +                if(x->type != Ta && x->type != Tmx && x->type != Tns)
       +                        return rp;
       +
       +        base = rp; 
       +
       +        n = rand();
       +        last = first = nil;
       +        while(rp != nil){
       +                /* stop randomizing if we've moved past our class */
       +                if(base->auth != rp->auth || base->db != rp->db){
       +                        last->next = rp;
       +                        break;
       +                }
       +
       +                /* unchain */
       +                x = rp;
       +                rp = x->next;
       +                x->next = nil;
       +
       +                if(n&1){
       +                        /* add to tail */
       +                        if(last == nil)
       +                                first = x;
       +                        else
       +                                last->next = x;
       +                        last = x;
       +                } else {
       +                        /* add to head */
       +                        if(last == nil)
       +                                last = x;
       +                        x->next = first;
       +                        first = x;
       +                }
       +
       +                /* reroll the dice */
       +                n >>= 1;
       +        }
       +        return first;
       +}
       +
       +static int
       +sencodefmt(Fmt *f)
       +{
       +        char *out;
       +        char *buf;
       +        int i, len;
       +        int ilen;
       +        int rv;
       +        uchar *b;
       +        char obuf[64];        // rsc optimization
       +
       +        if(!(f->flags&FmtPrec) || f->prec < 1)
       +                goto error;
       +
       +        b = va_arg(f->args, uchar*);
       +        if(b == nil)
       +                goto error;
       +
       +        /* if it's a printable, go for it */
       +        len = f->prec;
       +        for(i = 0; i < len; i++)
       +                if(!isprint(b[i]))
       +                        break;
       +        if(i == len){
       +                if(len >= sizeof obuf)
       +                        len = sizeof(obuf)-1;
       +                memmove(obuf, b, len);
       +                obuf[len] = 0;
       +                fmtstrcpy(f, obuf);
       +                return 0;
       +        }
       +
       +        ilen = f->prec;
       +        f->prec = 0;
       +        f->flags &= ~FmtPrec;
       +        switch(f->r){
       +        case '<':
       +                len = (8*ilen+4)/5 + 3;
       +                break;
       +        case '[':
       +                len = (8*ilen+5)/6 + 4;
       +                break;
       +        case 'H':
       +                len = 2*ilen + 1;
       +                break;
       +        default:
       +                goto error;
       +        }
       +
       +        if(len > sizeof(obuf)){
       +                buf = malloc(len);
       +                if(buf == nil)
       +                        goto error;
       +        } else
       +                buf = obuf;
       +
       +        // convert
       +        out = buf;
       +        switch(f->r){
       +        case '<':
       +                rv = enc32(out, len, b, ilen);
       +                break;
       +        case '[':
       +                rv = enc64(out, len, b, ilen);
       +                break;
       +        case 'H':
       +                rv = enc16(out, len, b, ilen);
       +                break;
       +        default:
       +                rv = -1;
       +                break;
       +        }
       +        if(rv < 0)
       +                goto error;
       +
       +        fmtstrcpy(f, buf);
       +        if(buf != obuf)
       +                free(buf);
       +        return 0;
       +
       +error:
       +        return fmtstrcpy(f, "<encodefmt>");
       +
       +}
       +
       +void*
       +emalloc(int size)
       +{
       +        char *x;
       +
       +        x = mallocz(size, 1);
       +        if(x == nil)
       +                abort();
       +        setmalloctag(x, getcallerpc(&size));
       +        return x;
       +}
       +
       +char*
       +estrdup(char *s)
       +{
       +        int size;
       +        char *p;
       +
       +        size = strlen(s)+1;
       +        p = mallocz(size, 0);
       +        if(p == nil)
       +                abort();
       +        memmove(p, s, size);
       +        setmalloctag(p, getcallerpc(&s));
       +        return p;
       +}
       +
       +/*
       + *  create a pointer record
       + */
       +static RR*
       +mkptr(DN *dp, char *ptr, ulong ttl)
       +{
       +        DN *ipdp;
       +        RR *rp;
       +
       +        ipdp = dnlookup(ptr, Cin, 1);
       +
       +        rp = rralloc(Tptr);
       +        rp->ptr = dp;
       +        rp->owner = ipdp;
       +        rp->db = 1;
       +        if(ttl)
       +                rp->ttl = ttl;
       +        return rp;
       +}
       +
       +/*
       + *  look for all ip addresses in this network and make
       + *  pointer records for them.
       + */
       +void
       +dnptr(uchar *net, uchar *mask, char *dom, int bytes, int ttl)
       +{
       +        int i, j;
       +        DN *dp;
       +        RR *rp, *nrp, *first, **l;
       +        uchar ip[IPaddrlen];
       +        uchar nnet[IPaddrlen];
       +        char ptr[Domlen];
       +        char *p, *e;
       +
       +        l = &first;
       +        first = nil;
       +        for(i = 0; i < HTLEN; i++){
       +                for(dp = ht[i]; dp; dp = dp->next){
       +                        for(rp = dp->rr; rp; rp = rp->next){
       +                                if(rp->type != Ta || rp->negative)
       +                                        continue;
       +                                parseip(ip, rp->ip->name);
       +                                maskip(ip, mask, nnet);
       +                                if(ipcmp(net, nnet) != 0)
       +                                        continue;
       +                                p = ptr;
       +                                e = ptr+sizeof(ptr);
       +                                for(j = IPaddrlen-1; j >= IPaddrlen-bytes; j--)
       +                                        p = seprint(p, e, "%d.", ip[j]);
       +                                seprint(p, e, "%s", dom);
       +                                nrp = mkptr(dp, ptr, ttl);
       +                                *l = nrp;
       +                                l = &nrp->next;
       +                        }
       +                }
       +        }
       +
       +        for(rp = first; rp != nil; rp = nrp){
       +                nrp = rp->next;
       +                rp->next = nil;
       +                rrattach(rp, 1);
       +        }
       +}
       +
       +void
       +freeserverlist(Server *s)
       +{
       +        Server *next;
       +
       +        for(; s != nil; s = next){
       +                next = s->next;
       +                free(s);
       +        }
       +}
       +
       +void
       +addserver(Server **l, char *name)
       +{
       +        Server *s;
       +
       +        while(*l)
       +                l = &(*l)->next;
       +        s = malloc(sizeof(Server)+strlen(name)+1);
       +        if(s == nil)
       +                return;
       +        s->name = (char*)(s+1);
       +        strcpy(s->name, name);
       +        s->next = nil;
       +        *l = s;
       +}
       +
       +Server*
       +copyserverlist(Server *s)
       +{
       +        Server *ns;
       +
       +        
       +        for(ns = nil; s != nil; s = s->next)
       +                addserver(&ns, s->name);
       +        return ns;
       +}
 (DIR) diff --git a/src/cmd/ndb/dnarea.c b/src/cmd/ndb/dnarea.c
       t@@ -0,0 +1,130 @@
       +#include <u.h>
       +#include <libc.h>
       +#include <bio.h>
       +#include <ndb.h>
       +#include <ip.h>
       +#include "dns.h"
       +
       +Area *owned;
       +Area *delegated;
       +
       +/*
       + *  true if a name is in our area
       + */
       +Area*
       +inmyarea(char *name)
       +{
       +        int len;
       +        Area *s, *d;
       +
       +        len = strlen(name);
       +        for(s = owned; s; s = s->next){
       +                if(s->len > len)
       +                        continue;
       +                if(cistrcmp(s->soarr->owner->name, name + len - s->len) == 0)
       +                        if(len == s->len || name[len - s->len - 1] == '.')
       +                                break;
       +        }
       +        if(s == 0)
       +                return 0;
       +
       +        for(d = delegated; d; d = d->next){
       +                if(d->len > len)
       +                        continue;
       +                if(cistrcmp(d->soarr->owner->name, name + len - d->len) == 0)
       +                        if(len == d->len || name[len - d->len - 1] == '.')
       +                                return 0;
       +        }
       +
       +        return s;
       +}
       +
       +/*
       + *  our area is the part of the domain tree that
       + *  we serve
       + */
       +void
       +addarea(DN *dp, RR *rp, Ndbtuple *t)
       +{
       +        Area **l, *s;
       +
       +        if(t->val[0])
       +                l = &delegated;
       +        else
       +                l = &owned;
       +
       +        /*
       +         *  The area contains a copy of the soa rr that created it.
       +         *  The owner of the the soa rr should stick around as long
       +         *  as the area does.
       +         */
       +        s = emalloc(sizeof(*s));
       +        s->len = strlen(dp->name);
       +        rrcopy(rp, &s->soarr);
       +        s->soarr->owner = dp;
       +        s->soarr->db = 1;
       +        s->soarr->ttl = Hour;
       +        s->neednotify = 1;
       +        s->needrefresh = 0;
       +
       +syslog(0, logfile, "new area %s", dp->name);
       +
       +        s->next = *l;
       +        *l = s;
       +}
       +
       +void
       +freearea(Area **l)
       +{
       +        Area *s;
       +
       +        while(s = *l){
       +                *l = s->next;
       +                rrfree(s->soarr);
       +                free(s);
       +        }
       +}
       +
       +/*
       + * refresh all areas that need it
       + *  this entails running a command 'zonerefreshprogram'.  This could
       + *  copy over databases from elsewhere or just do a zone transfer.
       + */
       +void
       +refresh_areas(Area *s)
       +{
       +        int pid;
       +        Waitmsg *w;
       +
       +        for(; s != nil; s = s->next){
       +                if(!s->needrefresh)
       +                        continue;
       +
       +                if(zonerefreshprogram == nil){
       +                        s->needrefresh = 0;
       +                        continue;
       +                }
       +
       +                switch(pid = fork()){
       +                case -1:
       +                        break;
       +                case 0:
       +                        execl(zonerefreshprogram, "zonerefresh", s->soarr->owner->name, 0);
       +                        exits(0);
       +                        break;
       +                default:
       +                        for(;;){
       +                                w = wait();
       +                                if(w == nil)
       +                                        break;
       +                                if(w->pid == pid){
       +                                        if(w->msg == nil || *w->msg == 0)
       +                                                s->needrefresh = 0;
       +                                        free(w);
       +                                        break;
       +                                }
       +                                free(w);
       +                        }
       +                }
       +        }
       +}
 (DIR) diff --git a/src/cmd/ndb/dnnotify.c b/src/cmd/ndb/dnnotify.c
       t@@ -0,0 +1,160 @@
       +#include <u.h>
       +#include <libc.h>
       +#include <ip.h>
       +#include <bio.h>
       +#include <ndb.h>
       +#include "dns.h"
       +
       +/* get a notification from another system of a changed zone */
       +void
       +dnnotify(DNSmsg *reqp, DNSmsg *repp, Request *r)
       +{
       +        RR *tp;
       +        Area *a;
       +
       +        USED(r);
       +        /* move one question from reqp to repp */
       +        memset(repp, 0, sizeof(*repp));
       +        tp = reqp->qd;
       +        reqp->qd = tp->next;
       +        tp->next = 0;
       +        repp->qd = tp;
       +        repp->id = reqp->id;
       +        repp->flags = Fresp  | Onotify | Fauth;
       +
       +        /* anything to do? */
       +        if(zonerefreshprogram == nil)
       +                return;
       +
       +        /* make sure its the right type */
       +        if(repp->qd->type != Tsoa)
       +                return;
       +
       +syslog(0, logfile, "notification for %s", repp->qd->owner->name);
       +
       +        /* is it something we care about? */
       +        a = inmyarea(repp->qd->owner->name);
       +        if(a == nil)
       +                return;
       +
       +syslog(0, logfile, "serial old %lud new %lud", a->soarr->soa->serial, repp->qd->soa->serial);
       +
       +        /* do nothing if it didn't change */
       +        if(a->soarr->soa->serial== repp->qd->soa->serial)
       +                return;
       +
       +        a->needrefresh = 1;
       +}
       +
       +static void
       +ding(void *u, char *msg)
       +{
       +        USED(u);
       +
       +        if(strstr(msg, "alarm"))
       +                noted(NCONT);
       +        else
       +                noted(NDFLT);
       +}
       +
       +/* notify a slave that an area has changed. */
       +static void
       +send_notify(char *slave, RR *soa, Request *req)
       +{
       +        int i, len, n, reqno, status, fd;
       +        uchar obuf[Maxudp+OUdphdrsize];
       +        uchar ibuf[Maxudp+OUdphdrsize];
       +        RR *rp;
       +        OUdphdr *up = (OUdphdr*)obuf;
       +        char *err;
       +        DNSmsg repmsg;
       +
       +        /* create the request */
       +        reqno = rand();
       +        n = mkreq(soa->owner, Cin, obuf, Fauth | Onotify, reqno);
       +
       +        /* get an address */
       +        if(strcmp(ipattr(slave), "ip") == 0) {
       +                parseip(up->raddr, slave);
       +        } else {
       +                rp = dnresolve(slave, Cin, Ta, req, nil, 0, 1, 1, &status);
       +                if(rp == nil)
       +                        return;
       +                parseip(up->raddr, rp->ip->name);
       +                rrfree(rp);
       +        }
       +
       +        fd = udpport();
       +        if(fd < 0)
       +                return;
       +
       +        notify(ding);
       +
       +        /* send 3 times or until we get anything back */
       +        for(i = 0; i < 3; i++){
       +syslog(0, logfile, "sending %d byte notify to %s/%I.%d about %s", n, slave, up->raddr, nhgets(up->rport), soa->owner->name);
       +                if(udpwrite(fd, (OUdphdr*)obuf, obuf+OUdphdrsize, n) != n)
       +                        break;
       +                alarm(2*1000);
       +                len = udpread(fd, (Udphdr*)ibuf, ibuf+OUdphdrsize, Maxudp);
       +                alarm(0);
       +                if(len <= OUdphdrsize)
       +                        continue;
       +                err = convM2DNS(&ibuf[OUdphdrsize], len, &repmsg);
       +                if(err != nil)
       +                        continue;
       +                if(repmsg.id == reqno && (repmsg.flags & Omask) == Onotify)
       +                        break;
       +        }
       +
       +        close(fd);
       +}
       +
       +/* send notifies for any updated areas */
       +static void
       +notify_areas(Area *a, Request *req)
       +{
       +        Server *s;
       +
       +        for(; a != nil; a = a->next){
       +                if(!a->neednotify)
       +                        continue;
       +
       +                /* send notifies to all slaves */
       +                for(s = a->soarr->soa->slaves; s != nil; s = s->next)
       +                        send_notify(s->name, a->soarr, req);
       +                a->neednotify = 0;
       +        }
       +}
       +
       +/*
       + *  process to notify other servers of changes
       + *  (also reads in new databases)
       + */
       +void
       +notifyproc(void)
       +{
       +        Request req;
       +        static int already;
       +
       +        if(already)
       +                return;
       +
       +        switch(rfork(RFPROC|RFNOTEG|RFMEM|RFNOWAIT)){
       +        case -1:
       +                return;
       +        case 0:
       +                break;
       +        default:
       +                return;
       +        }
       +
       +        req.isslave = 1;        /* son't fork off subprocesses */
       +
       +        for(;;){
       +                getactivity(&req);
       +                notify_areas(owned, &req);
       +                putactivity();
       +                sleep(60*1000);
       +        }
       +}
 (DIR) diff --git a/src/cmd/ndb/dnresolve.c b/src/cmd/ndb/dnresolve.c
       t@@ -0,0 +1,753 @@
       +#include <u.h>
       +#include <libc.h>
       +#include <ip.h>
       +#include <bio.h>
       +#include <ndb.h>
       +#include "dns.h"
       +
       +enum
       +{
       +        Maxdest=        24,        /* maximum destinations for a request message */
       +        Maxtrans=        3,        /* maximum transmissions to a server */
       +};
       +
       +static int        netquery(DN*, int, RR*, Request*, int);
       +static RR*        dnresolve1(char*, int, int, Request*, int, int);
       +
       +char *LOG = "dns";
       +
       +/*
       + *  lookup 'type' info for domain name 'name'.  If it doesn't exist, try
       + *  looking it up as a canonical name.
       + */
       +RR*
       +dnresolve(char *name, int class, int type, Request *req, RR **cn, int depth, int recurse, int rooted, int *status)
       +{
       +        RR *rp, *nrp, *drp;
       +        DN *dp;
       +        int loops;
       +        char nname[Domlen];
       +
       +        if(status)
       +                *status = 0;
       +
       +        /*
       +         *  hack for systems that don't have resolve search
       +         *  lists.  Just look up the simple name in the database.
       +         */
       +        if(!rooted && strchr(name, '.') == 0){
       +                rp = nil;
       +                drp = domainlist(class);
       +                for(nrp = drp; nrp != nil; nrp = nrp->next){
       +                        snprint(nname, sizeof(nname), "%s.%s", name, nrp->ptr->name);
       +                        rp = dnresolve(nname, class, type, req,cn, depth, recurse, rooted, status);
       +                        rrfreelist(rrremneg(&rp));
       +                        if(rp != nil)
       +                                break;
       +                }
       +                if(drp != nil)
       +                        rrfree(drp);
       +                return rp;
       +        }
       +
       +        /*
       +         *  try the name directly
       +         */
       +        rp = dnresolve1(name, class, type, req, depth, recurse);
       +        if(rp)
       +                return randomize(rp);
       +
       +        /* try it as a canonical name if we weren't told the name didn't exist */
       +        dp = dnlookup(name, class, 0);
       +        if(type != Tptr && dp->nonexistent != Rname){
       +                for(loops=0; rp == nil && loops < 32; loops++){
       +                        rp = dnresolve1(name, class, Tcname, req, depth, recurse);
       +                        if(rp == nil)
       +                                break;
       +
       +                        if(rp->negative){
       +                                rrfreelist(rp);
       +                                rp = nil;
       +                                break;
       +                        }
       +        
       +                        name = rp->host->name;
       +                        if(cn)
       +                                rrcat(cn, rp);
       +                        else
       +                                rrfreelist(rp);
       +        
       +                        rp = dnresolve1(name, class, type, req, depth, recurse);
       +                }
       +        }
       +
       +        /* distinction between not found and not good */
       +        if(rp == 0 && status != 0 && dp->nonexistent != 0)
       +                *status = dp->nonexistent;
       +
       +        return randomize(rp);
       +}
       +
       +static RR*
       +dnresolve1(char *name, int class, int type, Request *req, int depth, int recurse)
       +{
       +        DN *dp, *nsdp;
       +        RR *rp, *nsrp, *dbnsrp;
       +        char *cp;
       +
       +        if(debug)
       +                syslog(0, LOG, "dnresolve1 %s %d %d", name, type, class);
       +
       +        /* only class Cin implemented so far */
       +        if(class != Cin)
       +                return 0;
       +
       +        dp = dnlookup(name, class, 1);
       +
       +        /*
       +         *  Try the cache first
       +         */
       +        rp = rrlookup(dp, type, OKneg);
       +        if(rp){
       +                if(rp->db){
       +                        /* unauthenticated db entries are hints */
       +                        if(rp->auth)
       +                                return rp;
       +                } else {
       +                        /* cached entry must still be valid */
       +                        if(rp->ttl > now){
       +                                /* but Tall entries are special */
       +                                if(type != Tall || rp->query == Tall)
       +                                        return rp;
       +                        }
       +                }
       +        }
       +        rrfreelist(rp);
       +
       +        /*
       +         * try the cache for a canonical name. if found punt 
       +         * since we'll find it during the canonical name search
       +         * in dnresolve().
       +         */
       +        if(type != Tcname){
       +                rp = rrlookup(dp, Tcname, NOneg);
       +                rrfreelist(rp);
       +                if(rp)
       +                        return 0;
       +        }
       +
       +        /*
       +         *  if we're running as just a resolver, go to our
       +         *  designated name servers
       +         */
       +        if(resolver){
       +                nsrp = randomize(getdnsservers(class));
       +                if(nsrp != nil) {
       +                        if(netquery(dp, type, nsrp, req, depth+1)){
       +                                rrfreelist(nsrp);
       +                                return rrlookup(dp, type, OKneg);
       +                        }
       +                        rrfreelist(nsrp);
       +                }
       +        }
       +
       +        /*
       +          *  walk up the domain name looking for
       +         *  a name server for the domain.
       +         */
       +        for(cp = name; cp; cp = walkup(cp)){
       +                /*
       +                 *  if this is a local (served by us) domain,
       +                 *  return answer
       +                 */
       +                dbnsrp = randomize(dblookup(cp, class, Tns, 0, 0));
       +                if(dbnsrp && dbnsrp->local){
       +                        rp = dblookup(name, class, type, 1, dbnsrp->ttl);
       +                        rrfreelist(dbnsrp);
       +                        return rp;
       +                }
       +
       +                /*
       +                 *  if recursion isn't set, just accept local
       +                 *  entries
       +                 */
       +                if(recurse == Dontrecurse){
       +                        if(dbnsrp)
       +                                rrfreelist(dbnsrp);
       +                        continue;
       +                }
       +
       +                /* look for ns in cache */
       +                nsdp = dnlookup(cp, class, 0);
       +                nsrp = nil;
       +                if(nsdp)
       +                        nsrp = randomize(rrlookup(nsdp, Tns, NOneg));
       +
       +                /* if the entry timed out, ignore it */
       +                if(nsrp && nsrp->ttl < now){
       +                        rrfreelist(nsrp);
       +                        nsrp = nil;
       +                }
       +
       +                if(nsrp){
       +                        rrfreelist(dbnsrp);
       +
       +                        /* try the name servers found in cache */
       +                        if(netquery(dp, type, nsrp, req, depth+1)){
       +                                rrfreelist(nsrp);
       +                                return rrlookup(dp, type, OKneg);
       +                        }
       +                        rrfreelist(nsrp);
       +                        continue;
       +                }
       +
       +                /* use ns from db */
       +                if(dbnsrp){
       +                        /* try the name servers found in db */
       +                        if(netquery(dp, type, dbnsrp, req, depth+1)){
       +                                /* we got an answer */
       +                                rrfreelist(dbnsrp);
       +                                return rrlookup(dp, type, NOneg);
       +                        }
       +                        rrfreelist(dbnsrp);
       +                }
       +        }
       +
       +        /* settle for a non-authoritative answer */
       +        rp = rrlookup(dp, type, OKneg);
       +        if(rp)
       +                return rp;
       +
       +        /* noone answered.  try the database, we might have a chance. */
       +        return dblookup(name, class, type, 0, 0);
       +}
       +
       +/*
       + *  walk a domain name one element to the right.  return a pointer to that element.
       + *  in other words, return a pointer to the parent domain name.
       + */
       +char*
       +walkup(char *name)
       +{
       +        char *cp;
       +
       +        cp = strchr(name, '.');
       +        if(cp)
       +                return cp+1;
       +        else if(*name)
       +                return "";
       +        else
       +                return 0;
       +}
       +
       +/*
       + *  Get a udpport for requests and replies. 
       + */
       +int
       +udpport(void)
       +{
       +        int fd;
       +
       +        if((fd = dial("udp!0!53", nil, nil, nil)) < 0)
       +                warning("can't get udp port");
       +        return fd;
       +}
       +
       +int
       +mkreq(DN *dp, int type, uchar *buf, int flags, ushort reqno)
       +{
       +        DNSmsg m;
       +        int len;
       +        OUdphdr *uh = (OUdphdr*)buf;
       +
       +        /* stuff port number into output buffer */
       +        memset(uh, 0, sizeof(*uh));
       +        hnputs(uh->rport, 53);
       +
       +        /* make request and convert it to output format */
       +        memset(&m, 0, sizeof(m));
       +        m.flags = flags;
       +        m.id = reqno;
       +        m.qd = rralloc(type);
       +        m.qd->owner = dp;
       +        m.qd->type = type;
       +        len = convDNS2M(&m, &buf[OUdphdrsize], Maxudp);
       +        if(len < 0)
       +                abort(); /* "can't convert" */;
       +        rrfree(m.qd);
       +        return len;
       +}
       +
       +/* for alarms in readreply */
       +static void
       +ding(void *x, char *msg)
       +{
       +        USED(x);
       +        if(strcmp(msg, "alarm") == 0)
       +                noted(NCONT);
       +        else
       +                noted(NDFLT);
       +}
       +
       +static void
       +freeanswers(DNSmsg *mp)
       +{
       +        rrfreelist(mp->qd);
       +        rrfreelist(mp->an);
       +        rrfreelist(mp->ns);
       +        rrfreelist(mp->ar);
       +}
       +
       +/*
       + *  read replies to a request.  ignore any of the wrong type.  wait at most 5 seconds.
       + */
       +static int
       +readreply(int fd, DN *dp, int type, ushort req,
       +          uchar *ibuf, DNSmsg *mp, ulong endtime, Request *reqp)
       +{
       +        char *err;
       +        int len;
       +        ulong now;
       +        RR *rp;
       +
       +        notify(ding);
       +
       +        for(; ; freeanswers(mp)){
       +                now = time(0);
       +                if(now >= endtime)
       +                        return -1;        /* timed out */
       +
       +                /* timed read */
       +                alarm((endtime - now) * 1000);
       +                len = udpread(fd, (OUdphdr*)ibuf, ibuf+OUdphdrsize, Maxudpin);
       +                alarm(0);
       +                if(len < 0)
       +                        return -1;        /* timed out */
       +                
       +                /* convert into internal format  */
       +                memset(mp, 0, sizeof(*mp));
       +                err = convM2DNS(&ibuf[OUdphdrsize], len, mp);
       +                if(err){
       +                        syslog(0, LOG, "input err %s: %I", err, ibuf);
       +                        continue;
       +                }
       +                if(debug)
       +                        logreply(reqp->id, ibuf, mp);
       +
       +                /* answering the right question? */
       +                if(mp->id != req){
       +                        syslog(0, LOG, "%d: id %d instead of %d: %I", reqp->id,
       +                                        mp->id, req, ibuf);
       +                        continue;
       +                }
       +                if(mp->qd == 0){
       +                        syslog(0, LOG, "%d: no question RR: %I", reqp->id, ibuf);
       +                        continue;
       +                }
       +                if(mp->qd->owner != dp){
       +                        syslog(0, LOG, "%d: owner %s instead of %s: %I", reqp->id,
       +                                mp->qd->owner->name, dp->name, ibuf);
       +                        continue;
       +                }
       +                if(mp->qd->type != type){
       +                        syslog(0, LOG, "%d: type %d instead of %d: %I", reqp->id,
       +                                mp->qd->type, type, ibuf);
       +                        continue;
       +                }
       +
       +                /* remember what request this is in answer to */
       +                for(rp = mp->an; rp; rp = rp->next)
       +                        rp->query = type;
       +
       +                return 0;
       +        }
       +
       +        return 0;        /* never reached */
       +}
       +
       +/*
       + *        return non-0 if first list includes second list
       + */
       +int
       +contains(RR *rp1, RR *rp2)
       +{
       +        RR *trp1, *trp2;
       +
       +        for(trp2 = rp2; trp2; trp2 = trp2->next){
       +                for(trp1 = rp1; trp1; trp1 = trp1->next){
       +                        if(trp1->type == trp2->type)
       +                        if(trp1->host == trp2->host)
       +                        if(trp1->owner == trp2->owner)
       +                                break;
       +                }
       +                if(trp1 == 0)
       +                        return 0;
       +        }
       +
       +        return 1;
       +}
       +
       +
       +typedef struct Dest        Dest;
       +struct Dest
       +{
       +        uchar        a[IPaddrlen];        /* ip address */
       +        DN        *s;                /* name server */
       +        int        nx;                /* number of transmissions */
       +        int        code;
       +};
       +
       +
       +/*
       + *  return multicast version if any
       + */
       +int
       +ipisbm(uchar *ip)
       +{
       +        if(isv4(ip)){
       +                if(ip[IPv4off] >= 0xe0 && ip[IPv4off] < 0xf0)
       +                        return 4;
       +                if(ipcmp(ip, IPv4bcast) == 0)
       +                        return 4;
       +        } else {
       +                if(ip[0] == 0xff)
       +                        return 6;
       +        }
       +        return 0;
       +}
       +
       +/*
       + *  Get next server address
       + */
       +static int
       +serveraddrs(RR *nsrp, Dest *dest, int nd, int depth, Request *reqp)
       +{
       +        RR *rp, *arp, *trp;
       +        Dest *cur;
       +
       +        if(nd >= Maxdest)
       +                return 0;
       +
       +        /*
       +         *  look for a server whose address we already know.
       +         *  if we find one, mark it so we ignore this on
       +         *  subsequent passes.
       +         */
       +        arp = 0;
       +        for(rp = nsrp; rp; rp = rp->next){
       +                assert(rp->magic == RRmagic);
       +                if(rp->marker)
       +                        continue;
       +                arp = rrlookup(rp->host, Ta, NOneg);
       +                if(arp){
       +                        rp->marker = 1;
       +                        break;
       +                }
       +                arp = dblookup(rp->host->name, Cin, Ta, 0, 0);
       +                if(arp){
       +                        rp->marker = 1;
       +                        break;
       +                }
       +        }
       +
       +        /*
       +         *  if the cache and database lookup didn't find any new
       +         *  server addresses, try resolving one via the network.
       +         *  Mark any we try to resolve so we don't try a second time.
       +         */
       +        if(arp == 0){
       +                for(rp = nsrp; rp; rp = rp->next){
       +                        if(rp->marker)
       +                                continue;
       +                        rp->marker = 1;
       +
       +                        /*
       +                         *  avoid loops looking up a server under itself
       +                         */
       +                        if(subsume(rp->owner->name, rp->host->name))
       +                                continue;
       +
       +                        arp = dnresolve(rp->host->name, Cin, Ta, reqp, 0, depth+1, Recurse, 1, 0);
       +                        rrfreelist(rrremneg(&arp));
       +                        if(arp)
       +                                break;
       +                }
       +        }
       +
       +        /* use any addresses that we found */
       +        for(trp = arp; trp; trp = trp->next){
       +                if(nd >= Maxdest)
       +                        break;
       +                cur = &dest[nd];
       +                parseip(cur->a, trp->ip->name);
       +                if(ipisbm(cur->a))
       +                        continue;
       +                cur->nx = 0;
       +                cur->s = trp->owner;
       +                cur->code = Rtimeout;
       +                nd++;
       +        }
       +        rrfreelist(arp);
       +        return nd;
       +}
       +
       +/*
       + *  cache negative responses
       + */
       +static void
       +cacheneg(DN *dp, int type, int rcode, RR *soarr)
       +{
       +        RR *rp;
       +        DN *soaowner;
       +        ulong ttl;
       +
       +        /* no cache time specified, don' make anything up */
       +        if(soarr != nil){
       +                if(soarr->next != nil){
       +                        rrfreelist(soarr->next);
       +                        soarr->next = nil;
       +                }
       +                soaowner = soarr->owner;
       +        } else 
       +                soaowner = nil;
       +
       +        /* the attach can cause soarr to be freed so mine it now */
       +        if(soarr != nil && soarr->soa != nil)
       +                ttl = soarr->soa->minttl+now;
       +        else
       +                ttl = 5*Min;
       +
       +        /* add soa and negative RR to the database */
       +        rrattach(soarr, 1);
       +
       +        rp = rralloc(type);
       +        rp->owner = dp;
       +        rp->negative = 1;
       +        rp->negsoaowner = soaowner;
       +        rp->negrcode = rcode;
       +        rp->ttl = ttl;
       +        rrattach(rp, 1);
       +}
       +
       +/*
       + *  query name servers.  If the name server returns a pointer to another
       + *  name server, recurse.
       + */
       +static int
       +netquery1(int fd, DN *dp, int type, RR *nsrp, Request *reqp, int depth, uchar *ibuf, uchar *obuf)
       +{
       +        int ndest, j, len, replywaits, rv;
       +        ushort req;
       +        RR *tp, *soarr;
       +        Dest *p, *l, *np;
       +        DN *ndp;
       +        Dest dest[Maxdest];
       +        DNSmsg m;
       +        ulong endtime;
       +
       +        /* pack request into a message */
       +        req = rand();
       +        len = mkreq(dp, type, obuf, Frecurse|Oquery, req);
       +
       +        /* no server addresses yet */
       +        l = dest;
       +
       +        /*
       +         *  transmit requests and wait for answers.
       +         *  at most Maxtrans attempts to each address.
       +         *  each cycle send one more message than the previous.
       +         */
       +        for(ndest = 1; ndest < Maxdest; ndest++){
       +                p = dest;
       +
       +                endtime = time(0);
       +                if(endtime >= reqp->aborttime)
       +                        break;
       +
       +                /* get a server address if we need one */
       +                if(ndest > l - p){
       +                        j = serveraddrs(nsrp, dest, l - p, depth, reqp);
       +                        l = &dest[j];
       +                }
       +
       +                /* no servers, punt */
       +                if(l == dest)
       +                        break;
       +
       +                /* send to first 'ndest' destinations */
       +                j = 0;
       +                for(; p < &dest[ndest] && p < l; p++){
       +                        /* skip destinations we've finished with */
       +                        if(p->nx >= Maxtrans)
       +                                continue;
       +
       +                        j++;
       +
       +                        /* exponential backoff of requests */
       +                        if((1<<p->nx) > ndest)
       +                                continue;
       +
       +                        memmove(obuf, p->a, sizeof(p->a));
       +                        if(debug)
       +                                logsend(reqp->id, depth, obuf, p->s->name,
       +                                        dp->name, type);
       +{Udphdr *uh = (Udphdr*)obuf;
       +print("send %I %I %d %d\n", uh->raddr, uh->laddr, nhgets(uh->rport), nhgets(uh->lport));
       +}
       +                        if(udpwrite(fd, (OUdphdr*)obuf, obuf+OUdphdrsize, len) < 0)
       +                                warning("sending udp msg %r");
       +                        p->nx++;
       +                }
       +                if(j == 0)
       +                        break;                /* no destinations left */
       +
       +                /* wait up to 5 seconds for replies */
       +                endtime = time(0) + 5;
       +                if(endtime > reqp->aborttime)
       +                        endtime = reqp->aborttime;
       +
       +                for(replywaits = 0; replywaits < ndest; replywaits++){
       +                        if(readreply(fd, dp, type, req, ibuf, &m, endtime, reqp) < 0)
       +                                break;                /* timed out */
       +
       +                        /* find responder */
       +                        for(p = dest; p < l; p++)
       +                                if(memcmp(p->a, ibuf, sizeof(p->a)) == 0)
       +                                        break;
       +
       +                        /* remove all addrs of responding server from list */
       +                        for(np = dest; np < l; np++)
       +                                if(np->s == p->s)
       +                                        p->nx = Maxtrans;
       +
       +                        /* ignore any error replies */
       +                        if((m.flags & Rmask) == Rserver){
       +                                rrfreelist(m.qd);
       +                                rrfreelist(m.an);
       +                                rrfreelist(m.ar);
       +                                rrfreelist(m.ns);
       +                                if(p != l)
       +                                        p->code = Rserver;
       +                                continue;
       +                        }
       +
       +                        /* ignore any bad delegations */
       +                        if(m.ns && baddelegation(m.ns, nsrp, ibuf)){
       +                                rrfreelist(m.ns);
       +                                m.ns = nil;
       +                                if(m.an == nil){
       +                                        rrfreelist(m.qd);
       +                                        rrfreelist(m.ar);
       +                                        if(p != l)
       +                                                p->code = Rserver;
       +                                        continue;
       +                                }
       +                        }
       +
       +
       +                        /* remove any soa's from the authority section */
       +                        soarr = rrremtype(&m.ns, Tsoa);
       +
       +                        /* incorporate answers */
       +                        if(m.an)
       +                                rrattach(m.an, (m.flags & Fauth) ? 1 : 0);
       +                        if(m.ar)
       +                                rrattach(m.ar, 0);
       +                        if(m.ns){
       +                                ndp = m.ns->owner;
       +                                rrattach(m.ns, 0);
       +                        } else
       +                                ndp = 0;
       +
       +                        /* free the question */
       +                        if(m.qd)
       +                                rrfreelist(m.qd);
       +
       +                        /*
       +                         *  Any reply from an authoritative server,
       +                         *  or a positive reply terminates the search
       +                         */
       +                        if(m.an != nil || (m.flags & Fauth)){
       +                                if(m.an == nil && (m.flags & Rmask) == Rname)
       +                                        dp->nonexistent = Rname;
       +                                else
       +                                        dp->nonexistent = 0;
       +
       +                                /*
       +                                 *  cache any negative responses, free soarr
       +                                 */
       +                                if((m.flags & Fauth) && m.an == nil)
       +                                        cacheneg(dp, type, (m.flags & Rmask), soarr);
       +                                else
       +                                        rrfreelist(soarr);
       +                                return 1;
       +                        }
       +                        rrfreelist(soarr);
       +
       +                        /*
       +                         *  if we've been given better name servers
       +                         *  recurse
       +                         */
       +                        if(m.ns){
       +                                tp = rrlookup(ndp, Tns, NOneg);
       +                                if(!contains(nsrp, tp)){
       +                                        rv = netquery(dp, type, tp, reqp, depth+1);
       +                                        rrfreelist(tp);
       +                                        return rv;
       +                                } else
       +                                        rrfreelist(tp);
       +                        }
       +                }
       +        }
       +
       +        /* if all servers returned failure, propogate it */
       +        dp->nonexistent = Rserver;
       +        for(p = dest; p < l; p++)
       +                if(p->code != Rserver)
       +                        dp->nonexistent = 0;
       +
       +        return 0;
       +}
       +
       +typedef struct Qarg Qarg;
       +struct Qarg
       +{
       +        DN *dp;
       +        int type;
       +        RR *nsrp;
       +        Request *reqp;
       +        int depth;
       +};
       +
       +
       +static int
       +netquery(DN *dp, int type, RR *nsrp, Request *reqp, int depth)
       +{
       +        uchar *obuf;
       +        uchar *ibuf;
       +        RR *rp;
       +        int fd, rv;
       +
       +        if(depth > 12)
       +                return 0;
       +
       +        /* use alloced buffers rather than ones from the stack */
       +        ibuf = emalloc(Maxudpin+OUdphdrsize);
       +        obuf = emalloc(Maxudp+OUdphdrsize);
       +
       +        slave(reqp);
       +
       +        /* prepare server RR's for incremental lookup */
       +        for(rp = nsrp; rp; rp = rp->next)
       +                rp->marker = 0;
       +
       +        fd = udpport();
       +        if(fd < 0)
       +                return 0;
       +        rv = netquery1(fd, dp, type, nsrp, reqp, depth, ibuf, obuf);
       +        close(fd);
       +        free(ibuf);
       +        free(obuf);
       +
       +        return rv;
       +}
 (DIR) diff --git a/src/cmd/ndb/dns.c b/src/cmd/ndb/dns.c
       t@@ -0,0 +1,882 @@
       +#include <u.h>
       +#include <libc.h>
       +#include <auth.h>
       +#include <fcall.h>
       +#include <bio.h>
       +#include <ctype.h>
       +#include <ip.h>
       +#include <ndb.h>
       +#include "dns.h"
       +
       +enum
       +{
       +        Maxrequest=                1024,
       +        Ncache=                        8,
       +        Maxpath=                128,
       +        Maxreply=                512,
       +        Maxrrr=                        16,
       +        Maxfdata=                8192,
       +
       +        Qdir=                        0,
       +        Qdns=                        1,
       +};
       +
       +typedef struct Mfile        Mfile;
       +typedef struct Job        Job;
       +typedef struct Network        Network;
       +
       +int vers;                /* incremented each clone/attach */
       +
       +struct Mfile
       +{
       +        Mfile                *next;                /* next free mfile */
       +        int                ref;
       +
       +        char                *user;
       +        Qid                qid;
       +        int                fid;
       +
       +        int                type;                /* reply type */
       +        char                reply[Maxreply];
       +        ushort                rr[Maxrrr];        /* offset of rr's */
       +        ushort                nrr;                /* number of rr's */
       +};
       +
       +//
       +//  active local requests
       +//
       +struct Job
       +{
       +        Job        *next;
       +        int        flushed;
       +        Fcall        request;
       +        Fcall        reply;
       +};
       +Lock        joblock;
       +Job        *joblist;
       +
       +struct {
       +        Lock        lk;
       +        Mfile        *inuse;                /* active mfile's */
       +} mfalloc;
       +
       +int        haveip;
       +int        mfd[2];
       +int        debug;
       +int traceactivity;
       +int        cachedb;
       +ulong        now;
       +int        testing;
       +char        *trace;
       +int        needrefresh;
       +int        resolver;
       +uchar        ipaddr[IPaddrlen];        /* my ip address */
       +int        maxage;
       +char        *zonerefreshprogram;
       +int        sendnotifies;
       +
       +void        rversion(Job*);
       +void        rauth(Job*);
       +void        rflush(Job*);
       +void        rattach(Job*, Mfile*);
       +char*        rwalk(Job*, Mfile*);
       +void        ropen(Job*, Mfile*);
       +void        rcreate(Job*, Mfile*);
       +void        rread(Job*, Mfile*);
       +void        rwrite(Job*, Mfile*, Request*);
       +void        rclunk(Job*, Mfile*);
       +void        rremove(Job*, Mfile*);
       +void        rstat(Job*, Mfile*);
       +void        rwstat(Job*, Mfile*);
       +void        sendmsg(Job*, char*);
       +void        mountinit(char*, char*);
       +void        io(void);
       +int        fillreply(Mfile*, int);
       +Job*        newjob(void);
       +void        freejob(Job*);
       +void        setext(char*, int, char*);
       +
       +char        *logfile = "dns";
       +char        *dbfile;
       +char        mntpt[Maxpath];
       +char        *LOG;
       +
       +void
       +usage(void)
       +{
       +        fprint(2, "usage: %s [-rs] [-f ndb-file] [-x netmtpt]\n", argv0);
       +        exits("usage");
       +}
       +
       +void
       +main(int argc, char *argv[])
       +{
       +        int        serve;
       +        char        servefile[Maxpath];
       +        char        ext[Maxpath];
       +        char        *p;
       +
       +        serve = 0;
       +//        setnetmtpt(mntpt, sizeof(mntpt), nil);
       +        ext[0] = 0;
       +        ARGBEGIN{
       +        case 'd':
       +                debug = 1;
       +                traceactivity = 1;
       +                break;
       +        case 'f':
       +                p = ARGF();
       +                if(p == nil)
       +                        usage();
       +                dbfile = p;
       +                break;
       +        case 'i':
       +                haveip = 1;
       +                parseip(ipaddr, EARGF(usage()));
       +                break;
       +//        case 'x':
       +        //        p = ARGF();
       +        //        if(p == nil)
       +        //                usage();
       +        //        setnetmtpt(mntpt, sizeof(mntpt), p);
       +        //        setext(ext, sizeof(ext), mntpt);
       +        //        break;
       +        case 'r':
       +                resolver = 1;
       +                break;
       +        case 's':
       +                serve = 1;        /* serve network */
       +                cachedb = 1;
       +                break;
       +        case 'a':
       +                p = ARGF();
       +                if(p == nil)
       +                        usage();
       +                maxage = atoi(p);
       +                break;
       +        case 't':
       +                testing = 1;
       +                break;
       +        case 'z':
       +                zonerefreshprogram = ARGF();
       +                break;
       +        case 'n':
       +                sendnotifies = 1;
       +                break;
       +        }ARGEND
       +        USED(argc);
       +        USED(argv);
       +
       +//if(testing) mainmem->flags |= POOL_NOREUSE;
       +#define RFREND 0
       +        rfork(RFREND|RFNOTEG);
       +
       +        /* start syslog before we fork */
       +        fmtinstall('F', fcallfmt);
       +        dninit();
       +        if(!haveip && myipaddr(ipaddr, mntpt) < 0)
       +                sysfatal("can't read my ip address");
       +
       +        syslog(0, logfile, "starting dns on %I", ipaddr);
       +
       +        opendatabase();
       +
       +/*
       +        snprint(servefile, sizeof(servefile), "#s/dns%s", ext);
       +        unmount(servefile, mntpt);
       +        remove(servefile);
       +*/
       +        mountinit(servefile, mntpt);
       +
       +        now = time(0);
       +        srand(now*getpid());
       +        db2cache(1);
       +
       +        if(serve)
       +                proccreate(dnudpserver, mntpt, STACK);
       +        if(sendnotifies)
       +                notifyproc();
       +
       +        io();
       +        syslog(0, logfile, "io returned, exiting");
       +        exits(0);
       +}
       +
       +int
       +myipaddr(uchar *ip, char *net)
       +{
       +        ipmove(ip, ipaddr);
       +        return 0;
       +}
       +
       +/*
       + *  if a mount point is specified, set the cs extention to be the mount point
       + *  with '_'s replacing '/'s
       + */
       +void
       +setext(char *ext, int n, char *p)
       +{
       +        int i, c;
       +
       +        n--;
       +        for(i = 0; i < n; i++){
       +                c = p[i];
       +                if(c == 0)
       +                        break;
       +                if(c == '/')
       +                        c = '_';
       +                ext[i] = c;
       +        }
       +        ext[i] = 0;
       +}
       +
       +void
       +mountinit(char *service, char *mntpt)
       +{
       +        int p[2];
       +
       +        if(pipe(p) < 0)
       +                abort(); /* "pipe failed" */;
       +        switch(rfork(RFFDG|RFPROC|RFNAMEG)){
       +        case 0:
       +                close(p[1]);
       +                break;
       +        case -1:
       +                abort(); /* "fork failed\n" */;
       +        default:
       +                close(p[0]);
       +
       +                if(post9pservice(p[1], "dns") < 0)
       +                        fprint(2, "post9pservice dns: %r\n");
       +                _exits(0);
       +        }
       +        mfd[0] = mfd[1] = p[0];
       +}
       +
       +Mfile*
       +newfid(int fid, int needunused)
       +{
       +        Mfile *mf;
       +
       +        lock(&mfalloc.lk);
       +        for(mf = mfalloc.inuse; mf != nil; mf = mf->next){
       +                if(mf->fid == fid){
       +                        unlock(&mfalloc.lk);
       +                        if(needunused)
       +                                return nil;
       +                        return mf;
       +                }
       +        }
       +        mf = emalloc(sizeof(*mf));
       +        if(mf == nil)
       +                sysfatal("out of memory");
       +        mf->fid = fid;
       +        mf->next = mfalloc.inuse;
       +        mfalloc.inuse = mf;
       +        unlock(&mfalloc.lk);
       +        return mf;
       +}
       +
       +void
       +freefid(Mfile *mf)
       +{
       +        Mfile **l;
       +
       +        lock(&mfalloc.lk);
       +        for(l = &mfalloc.inuse; *l != nil; l = &(*l)->next){
       +                if(*l == mf){
       +                        *l = mf->next;
       +                        if(mf->user)
       +                                free(mf->user);
       +                        free(mf);
       +                        unlock(&mfalloc.lk);
       +                        return;
       +                }
       +        }
       +        sysfatal("freeing unused fid");
       +}
       +
       +Mfile*
       +copyfid(Mfile *mf, int fid)
       +{
       +        Mfile *nmf;
       +
       +        nmf = newfid(fid, 1);
       +        if(nmf == nil)
       +                return nil;
       +        nmf->fid = fid;
       +        nmf->user = estrdup(mf->user);
       +        nmf->qid.type = mf->qid.type;
       +        nmf->qid.path = mf->qid.path;
       +        nmf->qid.vers = vers++;
       +        return nmf;
       +}
       +
       +Job*
       +newjob(void)
       +{
       +        Job *job;
       +
       +        job = emalloc(sizeof(*job));
       +        lock(&joblock);
       +        job->next = joblist;
       +        joblist = job;
       +        job->request.tag = -1;
       +        unlock(&joblock);
       +        return job;
       +}
       +
       +void
       +freejob(Job *job)
       +{
       +        Job **l;
       +
       +        lock(&joblock);
       +        for(l = &joblist; *l; l = &(*l)->next){
       +                if((*l) == job){
       +                        *l = job->next;
       +                        free(job);
       +                        break;
       +                }
       +        }
       +        unlock(&joblock);
       +}
       +
       +void
       +flushjob(int tag)
       +{
       +        Job *job;
       +
       +        lock(&joblock);
       +        for(job = joblist; job; job = job->next){
       +                if(job->request.tag == tag && job->request.type != Tflush){
       +                        job->flushed = 1;
       +                        break;
       +                }
       +        }
       +        unlock(&joblock);
       +}
       +
       +void
       +io(void)
       +{
       +        long n;
       +        Mfile *mf;
       +        uchar mdata[IOHDRSZ + Maxfdata];
       +        Request req;
       +        Job *job;
       +
       +        /*
       +         *  a slave process is sometimes forked to wait for replies from other
       +         *  servers.  The master process returns immediately via a longjmp
       +         *  through 'mret'.
       +         */
       +        if(setjmp(req.mret))
       +                putactivity();
       +        req.isslave = 0;
       +        for(;;){
       +                n = read9pmsg(mfd[0], mdata, sizeof mdata);
       +                if(n<=0){
       +                        syslog(0, logfile, "error reading mntpt: %r");
       +                        exits(0);
       +                }
       +                job = newjob();
       +                if(convM2S(mdata, n, &job->request) != n){
       +                        freejob(job);
       +                        continue;
       +                }
       +                mf = newfid(job->request.fid, 0);
       +                if(debug)
       +                        syslog(0, logfile, "%F", &job->request);
       +
       +                getactivity(&req);
       +                req.aborttime = now + 60;        /* don't spend more than 60 seconds */
       +
       +                switch(job->request.type){
       +                default:
       +                        syslog(1, logfile, "unknown request type %d", job->request.type);
       +                        break;
       +                case Tversion:
       +                        rversion(job);
       +                        break;
       +                case Tauth:
       +                        rauth(job);
       +                        break;
       +                case Tflush:
       +                        rflush(job);
       +                        break;
       +                case Tattach:
       +                        rattach(job, mf);
       +                        break;
       +                case Twalk:
       +                        rwalk(job, mf);
       +                        break;
       +                case Topen:
       +                        ropen(job, mf);
       +                        break;
       +                case Tcreate:
       +                        rcreate(job, mf);
       +                        break;
       +                case Tread:
       +                        rread(job, mf);
       +                        break;
       +                case Twrite:
       +                        rwrite(job, mf, &req);
       +                        break;
       +                case Tclunk:
       +                        rclunk(job, mf);
       +                        break;
       +                case Tremove:
       +                        rremove(job, mf);
       +                        break;
       +                case Tstat:
       +                        rstat(job, mf);
       +                        break;
       +                case Twstat:
       +                        rwstat(job, mf);
       +                        break;
       +                }
       +
       +                freejob(job);
       +        
       +                /*
       +                 *  slave processes die after replying
       +                 */
       +                if(req.isslave){
       +                        putactivity();
       +                        _exits(0);
       +                }
       +        
       +                putactivity();
       +        }
       +}
       +
       +void
       +rversion(Job *job)
       +{
       +        if(job->request.msize > IOHDRSZ + Maxfdata)
       +                job->reply.msize = IOHDRSZ + Maxfdata;
       +        else
       +                job->reply.msize = job->request.msize;
       +        if(strncmp(job->request.version, "9P2000", 6) != 0)
       +                sendmsg(job, "unknown 9P version");
       +        else{
       +                job->reply.version = "9P2000";
       +                sendmsg(job, 0);
       +        }
       +}
       +
       +void
       +rauth(Job *job)
       +{
       +        sendmsg(job, "dns: authentication not required");
       +}
       +
       +/*
       + *  don't flush till all the slaves are done
       + */
       +void
       +rflush(Job *job)
       +{
       +        flushjob(job->request.oldtag);
       +        sendmsg(job, 0);
       +}
       +
       +void
       +rattach(Job *job, Mfile *mf)
       +{
       +        if(mf->user != nil)
       +                free(mf->user);
       +        mf->user = estrdup(job->request.uname);
       +        mf->qid.vers = vers++;
       +        mf->qid.type = QTDIR;
       +        mf->qid.path = 0LL;
       +        job->reply.qid = mf->qid;
       +        sendmsg(job, 0);
       +}
       +
       +char*
       +rwalk(Job *job, Mfile *mf)
       +{
       +        char *err;
       +        char **elems;
       +        int nelems;
       +        int i;
       +        Mfile *nmf;
       +        Qid qid;
       +
       +        err = 0;
       +        nmf = nil;
       +        elems = job->request.wname;
       +        nelems = job->request.nwname;
       +        job->reply.nwqid = 0;
       +
       +        if(job->request.newfid != job->request.fid){
       +                /* clone fid */
       +                if(job->request.newfid<0){
       +                        err = "clone newfid out of range";
       +                        goto send;
       +                }
       +                nmf = copyfid(mf, job->request.newfid);
       +                if(nmf == nil){
       +                        err = "clone bad newfid";
       +                        goto send;
       +                }
       +                mf = nmf;
       +        }
       +        /* else nmf will be nil */
       +
       +        qid = mf->qid;
       +        if(nelems > 0){
       +                /* walk fid */
       +                for(i=0; i<nelems && i<MAXWELEM; i++){
       +                        if((qid.type & QTDIR) == 0){
       +                                err = "not a directory";
       +                                break;
       +                        }
       +                        if(strcmp(elems[i], "..") == 0 || strcmp(elems[i], ".") == 0){
       +                                qid.type = QTDIR;
       +                                qid.path = Qdir;
       +    Found:
       +                                job->reply.wqid[i] = qid;
       +                                job->reply.nwqid++;
       +                                continue;
       +                        }
       +                        if(strcmp(elems[i], "dns") == 0){
       +                                qid.type = QTFILE;
       +                                qid.path = Qdns;
       +                                goto Found;
       +                        }
       +                        err = "file does not exist";
       +                        break;
       +                }
       +        }
       +
       +    send:
       +        if(nmf != nil && (err!=nil || job->reply.nwqid<nelems))
       +                freefid(nmf);
       +        if(err == nil)
       +                mf->qid = qid;
       +        sendmsg(job, err);
       +        return err;
       +}
       +
       +void
       +ropen(Job *job, Mfile *mf)
       +{
       +        int mode;
       +        char *err;
       +
       +        err = 0;
       +        mode = job->request.mode;
       +        if(mf->qid.type & QTDIR){
       +                if(mode)
       +                        err = "permission denied";
       +        }
       +        job->reply.qid = mf->qid;
       +        job->reply.iounit = 0;
       +        sendmsg(job, err);
       +}
       +
       +void
       +rcreate(Job *job, Mfile *mf)
       +{
       +        USED(mf);
       +        sendmsg(job, "creation permission denied");
       +}
       +
       +void
       +rread(Job *job, Mfile *mf)
       +{
       +        int i, n, cnt;
       +        long off;
       +        Dir dir;
       +        uchar buf[Maxfdata];
       +        char *err;
       +        long clock;
       +
       +        n = 0;
       +        err = 0;
       +        off = job->request.offset;
       +        cnt = job->request.count;
       +        if(mf->qid.type & QTDIR){
       +                clock = time(0);
       +                if(off == 0){
       +                        dir.name = "dns";
       +                        dir.qid.type = QTFILE;
       +                        dir.qid.vers = vers;
       +                        dir.qid.path = Qdns;
       +                        dir.mode = 0666;
       +                        dir.length = 0;
       +                        dir.uid = mf->user;
       +                        dir.gid = mf->user;
       +                        dir.muid = mf->user;
       +                        dir.atime = clock;        /* wrong */
       +                        dir.mtime = clock;        /* wrong */
       +                        n = convD2M(&dir, buf, sizeof buf);
       +                }
       +                job->reply.data = (char*)buf;
       +        } else {
       +                for(i = 1; i <= mf->nrr; i++)
       +                        if(mf->rr[i] > off)
       +                                break;
       +                if(i > mf->nrr)
       +                        goto send;
       +                if(off + cnt > mf->rr[i])
       +                        n = mf->rr[i] - off;
       +                else
       +                        n = cnt;
       +                job->reply.data = mf->reply + off;
       +        }
       +send:
       +        job->reply.count = n;
       +        sendmsg(job, err);
       +}
       +
       +void
       +rwrite(Job *job, Mfile *mf, Request *req)
       +{
       +        int cnt, rooted, status;
       +        long n;
       +        char *err, *p, *atype;
       +        RR *rp, *tp, *neg;
       +        int wantsav;
       +
       +        err = 0;
       +        cnt = job->request.count;
       +        if(mf->qid.type & QTDIR){
       +                err = "can't write directory";
       +                goto send;
       +        }
       +        if(cnt >= Maxrequest){
       +                err = "request too long";
       +                goto send;
       +        }
       +        job->request.data[cnt] = 0;
       +        if(cnt > 0 && job->request.data[cnt-1] == '\n')
       +                job->request.data[cnt-1] = 0;
       +
       +        /*
       +         *  special commands
       +         */
       +        if(strncmp(job->request.data, "debug", 5)==0 && job->request.data[5] == 0){
       +                debug ^= 1;
       +                goto send;
       +        } else if(strncmp(job->request.data, "dump", 4)==0 && job->request.data[4] == 0){
       +                dndump("/lib/ndb/dnsdump");
       +                goto send;
       +        } else if(strncmp(job->request.data, "refresh", 7)==0 && job->request.data[7] == 0){
       +                needrefresh = 1;
       +                goto send;
       +//        } else if(strncmp(job->request.data, "poolcheck", 9)==0 && job->request.data[9] == 0){
       +//                poolcheck(mainmem);
       +//                goto send;
       +        }
       +
       +        /*
       +         *  kill previous reply
       +         */
       +        mf->nrr = 0;
       +        mf->rr[0] = 0;
       +
       +        /*
       +         *  break up request (into a name and a type)
       +         */
       +        atype = strchr(job->request.data, ' ');
       +        if(atype == 0){
       +                err = "illegal request";
       +                goto send;
       +        } else
       +                *atype++ = 0;
       +
       +        /*
       +         *  tracing request
       +         */
       +        if(strcmp(atype, "trace") == 0){
       +                if(trace)
       +                        free(trace);
       +                if(*job->request.data)
       +                        trace = estrdup(job->request.data);
       +                else
       +                        trace = 0;
       +                goto send;
       +        }
       +
       +        mf->type = rrtype(atype);
       +        if(mf->type < 0){
       +                err = "unknown type";
       +                goto send;
       +        }
       +
       +        p = atype - 2;
       +        if(p >= job->request.data && *p == '.'){
       +                rooted = 1;
       +                *p = 0;
       +        } else
       +                rooted = 0;
       +
       +        p = job->request.data;
       +        if(*p == '!'){
       +                wantsav = 1;
       +                p++;
       +        } else
       +                wantsav = 0;
       +        dncheck(0, 1);
       +        rp = dnresolve(p, Cin, mf->type, req, 0, 0, Recurse, rooted, &status);
       +        dncheck(0, 1);
       +        neg = rrremneg(&rp);
       +        if(neg){
       +                status = neg->negrcode;
       +                rrfreelist(neg);
       +        }
       +        if(rp == 0){
       +                switch(status){
       +                case Rname:
       +                        err = "name does not exist";
       +                        break;
       +                case Rserver:
       +                        err = "dns failure";
       +                        break;
       +                default:
       +                        err = "resource does not exist";
       +                        break;
       +                }
       +        } else {
       +                lock(&joblock);
       +                if(!job->flushed){
       +                        /* format data to be read later */
       +                        n = 0;
       +                        mf->nrr = 0;
       +                        for(tp = rp; mf->nrr < Maxrrr-1 && n < Maxreply && tp &&
       +                                        tsame(mf->type, tp->type); tp = tp->next){
       +                                mf->rr[mf->nrr++] = n;
       +                                if(wantsav)
       +                                        n += snprint(mf->reply+n, Maxreply-n, "%Q", tp);
       +                                else
       +                                        n += snprint(mf->reply+n, Maxreply-n, "%R", tp);
       +                        }
       +                        mf->rr[mf->nrr] = n;
       +                }
       +                unlock(&joblock);
       +                rrfreelist(rp);
       +        }
       +
       +    send:
       +        dncheck(0, 1);
       +        job->reply.count = cnt;
       +        sendmsg(job, err);
       +}
       +
       +void
       +rclunk(Job *job, Mfile *mf)
       +{
       +        freefid(mf);
       +        sendmsg(job, 0);
       +}
       +
       +void
       +rremove(Job *job, Mfile *mf)
       +{
       +        USED(mf);
       +        sendmsg(job, "remove permission denied");
       +}
       +
       +void
       +rstat(Job *job, Mfile *mf)
       +{
       +        Dir dir;
       +        uchar buf[IOHDRSZ+Maxfdata];
       +
       +        if(mf->qid.type & QTDIR){
       +                dir.name = ".";
       +                dir.mode = DMDIR|0555;
       +        } else {
       +                dir.name = "dns";
       +                dir.mode = 0666;
       +        }
       +        dir.qid = mf->qid;
       +        dir.length = 0;
       +        dir.uid = mf->user;
       +        dir.gid = mf->user;
       +        dir.muid = mf->user;
       +        dir.atime = dir.mtime = time(0);
       +        job->reply.nstat = convD2M(&dir, buf, sizeof buf);
       +        job->reply.stat = buf;
       +        sendmsg(job, 0);
       +}
       +
       +void
       +rwstat(Job *job, Mfile *mf)
       +{
       +        USED(mf);
       +        sendmsg(job, "wstat permission denied");
       +}
       +
       +void
       +sendmsg(Job *job, char *err)
       +{
       +        int n;
       +        uchar mdata[IOHDRSZ + Maxfdata];
       +        char ename[ERRMAX];
       +
       +        if(err){
       +                job->reply.type = Rerror;
       +                snprint(ename, sizeof(ename), "dns: %s", err);
       +                job->reply.ename = ename;
       +        }else{
       +                job->reply.type = job->request.type+1;
       +        }
       +        job->reply.tag = job->request.tag;
       +        n = convS2M(&job->reply, mdata, sizeof mdata);
       +        if(n == 0){
       +                syslog(1, logfile, "sendmsg convS2M of %F returns 0", &job->reply);
       +                abort();
       +        }
       +        lock(&joblock);
       +        if(job->flushed == 0)
       +                if(write(mfd[1], mdata, n)!=n)
       +                        sysfatal("mount write");
       +        unlock(&joblock);
       +        if(debug)
       +                syslog(0, logfile, "%F %d", &job->reply, n);
       +}
       +
       +/*
       + *  the following varies between dnsdebug and dns
       + */
       +void
       +logreply(int id, uchar *addr, DNSmsg *mp)
       +{
       +        RR *rp;
       +
       +        syslog(0, LOG, "%d: rcvd %I flags:%s%s%s%s%s", id, addr,
       +                mp->flags & Fauth ? " auth" : "",
       +                mp->flags & Ftrunc ? " trunc" : "",
       +                mp->flags & Frecurse ? " rd" : "",
       +                mp->flags & Fcanrec ? " ra" : "",
       +                mp->flags & (Fauth|Rname) == (Fauth|Rname) ?
       +                " nx" : "");
       +        for(rp = mp->qd; rp != nil; rp = rp->next)
       +                syslog(0, LOG, "%d: rcvd %I qd %s", id, addr, rp->owner->name);
       +        for(rp = mp->an; rp != nil; rp = rp->next)
       +                syslog(0, LOG, "%d: rcvd %I an %R", id, addr, rp);
       +        for(rp = mp->ns; rp != nil; rp = rp->next)
       +                syslog(0, LOG, "%d: rcvd %I ns %R", id, addr, rp);
       +        for(rp = mp->ar; rp != nil; rp = rp->next)
       +                syslog(0, LOG, "%d: rcvd %I ar %R", id, addr, rp);
       +}
       +
       +void
       +logsend(int id, int subid, uchar *addr, char *sname, char *rname, int type)
       +{
       +        char buf[12];
       +
       +        syslog(0, LOG, "%d.%d: sending to %I/%s %s %s",
       +                id, subid, addr, sname, rname, rrname(type, buf, sizeof buf));
       +}
       +
       +RR*
       +getdnsservers(int class)
       +{
       +        return dnsservers(class);
       +}
 (DIR) diff --git a/src/cmd/ndb/dns.h b/src/cmd/ndb/dns.h
       t@@ -0,0 +1,403 @@
       +#define OUdphdrsize Udphdrsize
       +#define OUdphdr Udphdr
       +
       +enum
       +{
       +        /* RR types */
       +        Ta=        1,
       +        Tns=        2,
       +        Tmd=        3,
       +        Tmf=        4,
       +        Tcname=        5,
       +        Tsoa=        6,
       +        Tmb=        7,
       +        Tmg=        8,
       +        Tmr=        9,
       +        Tnull=        10,
       +        Twks=        11,
       +        Tptr=        12,
       +        Thinfo=        13,
       +        Tminfo=        14,
       +        Tmx=        15,
       +        Ttxt=        16,
       +        Trp=        17,
       +        Tsig=        24,
       +        Tkey=        25,
       +        Taaaa=        28,
       +        Tcert=        37,
       +
       +        /* query types (all RR types are also queries) */
       +        Tixfr=        251,        /* incremental zone transfer */
       +        Taxfr=        252,        /* zone transfer */
       +        Tmailb=        253,        /* { Tmb, Tmg, Tmr } */        
       +        Tall=        255,        /* all records */
       +
       +        /* classes */
       +        Csym=        0,        /* internal symbols */
       +        Cin=        1,        /* internet */
       +        Ccs,                /* CSNET (obsolete) */
       +        Cch,                /* Chaos net */
       +        Chs,                /* Hesiod (?) */
       +
       +        /* class queries (all class types are also queries) */
       +        Call=        255,        /* all classes */
       +
       +        /* opcodes */
       +        Oquery=                0<<11,                /* normal query */
       +        Oinverse=        1<<11,                /* inverse query */
       +        Ostatus=        2<<11,                /* status request */
       +        Onotify=        4<<11,                /* notify slaves of updates */
       +        Omask=                0xf<<11,        /* mask for opcode */
       +
       +        /* response codes */
       +        Rok=                0,
       +        Rformat=        1,        /* format error */
       +        Rserver=        2,        /* server failure (e.g. no answer from something) */
       +        Rname=                3,        /* bad name */
       +        Runimplimented=        4,        /* unimplemented */
       +        Rrefused=        5,        /* we don't like you */
       +        Rmask=                0xf,        /* mask for response */
       +        Rtimeout=        0x10,        /* timeout sending (for internal use only) */
       +
       +        /* bits in flag word (other than opcode and response) */
       +        Fresp=                1<<15,        /* message is a response */
       +        Fauth=                1<<10,        /* true if an authoritative response */
       +        Ftrunc=                1<<9,        /* truncated message */
       +        Frecurse=        1<<8,        /* request recursion */
       +        Fcanrec=        1<<7,        /* server can recurse */
       +
       +        Domlen=                256,        /* max domain name length (with NULL) */
       +        Labellen=        256,        /* max domain label length (with NULL) */
       +        Strlen=                256,        /* max string length (with NULL) */
       +        Iplen=                32,        /* max ascii ip address length (with NULL) */
       +
       +        /* time to live values (in seconds) */
       +        Min=                60,
       +        Hour=                60*Min,                /* */
       +        Day=                24*Hour,        /* Ta, Tmx */
       +        Week=                7*Day,                /* Tsoa, Tns */
       +        Year=                52*Week,
       +        DEFTTL=                Day,
       +
       +        /* reserved time (can't be timed out earlier) */
       +        Reserved=        5*Min,
       +
       +        /* packet sizes */
       +        Maxudp=                512,        /* maximum bytes per udp message */
       +        Maxudpin=        2048,        /* maximum bytes per udp message */
       +
       +        /* length of domain name hash table */
       +        HTLEN=                 4*1024,
       +
       +        RRmagic=        0xdeadbabe,
       +        DNmagic=        0xa110a110,
       +
       +        /* parallelism */
       +        Maxactive=        32,
       +};
       +
       +typedef struct DN        DN;
       +typedef struct DNSmsg        DNSmsg;
       +typedef struct RR        RR;
       +typedef struct SOA        SOA;
       +typedef struct Area        Area;
       +typedef struct Request        Request;
       +typedef struct Key        Key;
       +typedef struct Cert        Cert;
       +typedef struct Sig        Sig;
       +typedef struct Null        Null;
       +typedef struct Server        Server;
       +typedef struct Txt        Txt;
       +
       +/*
       + *  a structure to track a request and any slave process handling it
       + */
       +struct Request
       +{
       +        int        isslave;        /* pid of slave */
       +        ulong        aborttime;        /* time at which we give up */
       +        jmp_buf        mret;                /* where master jumps to after starting a slave */
       +        int        id;
       +};
       +
       +/*
       + *  a domain name
       + */
       +struct DN
       +{
       +        DN        *next;                /* hash collision list */
       +        ulong        magic;
       +        char        *name;                /* owner */
       +        RR        *rr;                /* resource records off this name */
       +        ulong        referenced;        /* time last referenced */
       +        ulong        lookuptime;        /* last time we tried to get a better value */
       +        ushort        class;                /* RR class */
       +        char        refs;                /* for mark and sweep */
       +        char        nonexistent;        /* true if we get an authoritative nx for this domain */
       +        ulong        ordinal;
       +};
       +
       +/*
       + *  security info
       + */
       +struct Key
       +{
       +        int        flags;
       +        int        proto;
       +        int        alg;
       +        int        dlen;
       +        uchar        *data;
       +};
       +struct Cert
       +{
       +        int        type;
       +        int        tag;
       +        int        alg;
       +        int        dlen;
       +        uchar        *data;
       +};
       +struct Sig
       +{
       +        int        type;
       +        int        alg;
       +        int        labels;
       +        ulong        ttl;
       +        ulong        exp;
       +        ulong        incep;
       +        int        tag;
       +        DN        *signer;
       +        int        dlen;
       +        uchar        *data;
       +};
       +struct Null
       +{
       +        int        dlen;
       +        uchar        *data;
       +};
       +
       +/*
       + *  text strings
       + */
       +struct Txt
       +{
       +        Txt        *next;
       +        char        *p;
       +};
       +
       +/*
       + *  an unpacked resource record
       + */
       +struct RR
       +{
       +        RR        *next;
       +        ulong        magic;
       +        DN        *owner;                /* domain that owns this resource record */
       +        uchar        negative;        /* this is a cached negative response */
       +        ulong        pc;
       +        ulong        ttl;                /* time to live to be passed on */
       +        ulong        expire;                /* time this entry expires locally */
       +        ushort        type;                /* RR type */
       +        ushort        query;                /* query tyis is in response to */
       +        uchar        auth;                /* authoritative */
       +        uchar        db;                /* from database */
       +        uchar        cached;                /* rr in cache */
       +        ulong        marker;                /* used locally when scanning rrlists */
       +        union {
       +                DN        *negsoaowner;        /* soa for cached negative response */
       +                DN        *host;        /* hostname - soa, cname, mb, md, mf, mx, ns */
       +                DN        *cpu;        /* cpu type - hinfo */
       +                DN        *mb;        /* mailbox - mg, minfo */
       +                DN        *ip;        /* ip addrss - a */
       +                DN        *rp;        /* rp arg - rp */
       +                int        cruftlen;
       +                ulong        arg0;
       +        };
       +        union {
       +                int        negrcode;        /* response code for cached negative response */
       +                DN        *rmb;        /* responsible maibox - minfo, soa, rp */
       +                DN        *ptr;        /* pointer to domain name - ptr */
       +                DN        *os;        /* operating system - hinfo */
       +                ulong        pref;        /* preference value - mx */
       +                ulong        local;        /* ns served from local database - ns */
       +                ulong        arg1;
       +        };
       +        union {
       +                SOA        *soa;        /* soa timers - soa */
       +                Key        *key;
       +                Cert        *cert;
       +                Sig        *sig;
       +                Null        *null;
       +                Txt        *txt;
       +        };
       +};
       +
       +/*
       + *  list of servers
       + */
       +struct Server
       +{
       +        Server        *next;
       +        char        *name;
       +};
       +
       +/*
       + *  timers for a start of authenticated record
       + */
       +struct SOA
       +{
       +        ulong        serial;                /* zone serial # (sec) - soa */
       +        ulong        refresh;        /* zone refresh interval (sec) - soa */
       +        ulong        retry;                /* zone retry interval (sec) - soa */
       +        ulong        expire;                /* time to expiration (sec) - soa */
       +        ulong        minttl;                /* minimum time to live for any entry (sec) - soa */
       +        Server        *slaves;        /* slave servers */
       +};
       +
       +/*
       + *  domain messages
       + */
       +struct DNSmsg
       +{
       +        ushort        id;
       +        int        flags;
       +        int        qdcount;        /* questions */
       +        RR         *qd;
       +        int        ancount;        /* answers */
       +        RR        *an;
       +        int        nscount;        /* name servers */
       +        RR        *ns;
       +        int        arcount;        /* hints */
       +        RR        *ar;
       +};
       +
       +/*
       + *  definition of local area for dblookup
       + */
       +struct Area
       +{
       +        Area                *next;
       +
       +        int                len;                /* strlen(area->soarr->owner->name) */
       +        RR                *soarr;                /* soa defining this area */
       +        int                neednotify;
       +        int                needrefresh;
       +};
       +
       +enum
       +{
       +        Recurse,
       +        Dontrecurse,
       +        NOneg,
       +        OKneg,
       +};
       +
       +/* dn.c */
       +extern char        *rrtname[];
       +extern char        *rname[];
       +extern char        *opname[];
       +extern void        db2cache(int);
       +extern void        dninit(void);
       +extern DN*        dnlookup(char*, int, int);
       +extern void        dnage(DN*);
       +extern void        dnageall(int);
       +extern void        dnagedb(void);
       +extern void        dnauthdb(void);
       +extern void        dnget(void);
       +extern void        dnpurge(void);
       +extern void        dnput(void);
       +extern Area*        inmyarea(char*);
       +extern void        rrattach(RR*, int);
       +extern RR*        rralloc(int);
       +extern void        rrfree(RR*);
       +extern void        rrfreelist(RR*);
       +extern RR*        rrlookup(DN*, int, int);
       +extern RR*        rrcat(RR**, RR*);
       +extern RR**        rrcopy(RR*, RR**);
       +extern RR*        rrremneg(RR**);
       +extern RR*        rrremtype(RR**, int);
       +extern int        rrfmt(Fmt*);
       +extern int        rravfmt(Fmt*);
       +extern int        rrsupported(int);
       +extern int        rrtype(char*);
       +extern char*        rrname(int, char*, int);
       +extern int        tsame(int, int);
       +extern void        dndump(char*);
       +extern int        getactivity(Request*);
       +extern void        putactivity(void);
       +extern void        abort(); /* char*, ... */;
       +extern void        warning(char*, ...);
       +extern void        slave(Request*);
       +extern void        dncheck(void*, int);
       +extern void        unique(RR*);
       +extern int        subsume(char*, char*);
       +extern RR*        randomize(RR*);
       +extern void*        emalloc(int);
       +extern char*        estrdup(char*);
       +extern void        dnptr(uchar*, uchar*, char*, int, int);
       +extern void        addserver(Server**, char*);
       +extern Server*        copyserverlist(Server*);
       +extern void        freeserverlist(Server*);
       +
       +/* dnarea.c */
       +extern void        refresh_areas(Area*);
       +extern void        freearea(Area**);
       +extern void        addarea(DN *dp, RR *rp, Ndbtuple *t);
       +
       +/* dblookup.c */
       +extern RR*        dblookup(char*, int, int, int, int);
       +extern RR*        dbinaddr(DN*, int);
       +extern int        baddelegation(RR*, RR*, uchar*);
       +extern RR*        dnsservers(int);
       +extern RR*        domainlist(int);
       +extern int        opendatabase(void);
       +
       +/* dns.c */
       +extern char*        walkup(char*);
       +extern RR*        getdnsservers(int);
       +extern void        logreply(int, uchar*, DNSmsg*);
       +extern void        logsend(int, int, uchar*, char*, char*, int);
       +
       +/* dnresolve.c */
       +extern RR*        dnresolve(char*, int, int, Request*, RR**, int, int, int, int*);
       +extern int        udpport(void);
       +extern int        mkreq(DN *dp, int type, uchar *buf, int flags, ushort reqno);
       +
       +/* dnserver.c */
       +extern void        dnserver(DNSmsg*, DNSmsg*, Request*);
       +extern void        dnudpserver(void*);
       +extern void        dntcpserver(char*);
       +
       +/* dnnotify.c */
       +extern void        dnnotify(DNSmsg*, DNSmsg*, Request*);
       +extern void        notifyproc(void);
       +
       +/* convDNS2M.c */
       +extern int        convDNS2M(DNSmsg*, uchar*, int);
       +
       +/* convM2DNS.c */
       +extern char*        convM2DNS(uchar*, int, DNSmsg*);
       +
       +/* malloc.c */
       +extern void        mallocsanity(void*);
       +extern void        lasthist(void*, int, ulong);
       +
       +extern int debug;
       +extern int traceactivity;
       +extern char        *trace;
       +extern int        testing;        /* test cache whenever removing a DN */
       +extern int        cachedb;
       +extern int        needrefresh;
       +extern char        *dbfile;
       +extern char        mntpt[];
       +extern char        *logfile;
       +extern int        resolver;
       +extern int        maxage;                /* age of oldest entry in cache (secs) */
       +extern char        *zonerefreshprogram;
       +extern int        sendnotifies;
       +extern ulong        now;                /* time base */
       +extern Area        *owned;
       +extern Area        *delegated;
       +
       +#pragma        varargck        type        "R"        RR*
       +#pragma        varargck        type        "Q"        RR*
       +
 (DIR) diff --git a/src/cmd/ndb/dnsdebug.c b/src/cmd/ndb/dnsdebug.c
       t@@ -0,0 +1,473 @@
       +#include <u.h>
       +#include <libc.h>
       +#include <bio.h>
       +#include <ctype.h>
       +#include <ip.h>
       +#include <ndb.h>
       +#include "dns.h"
       +
       +enum
       +{
       +        Maxrequest=                128,
       +        Ncache=                        8,
       +        Maxpath=                128,
       +        Maxreply=                512,
       +        Maxrrr=                        16,
       +};
       +
       +static char *servername;
       +static RR *serverrr;
       +static RR *serveraddrs;
       +
       +int        debug;
       +int        cachedb;
       +ulong        now;
       +int        testing;
       +int traceactivity;
       +char        *trace;
       +int        needrefresh;
       +int        resolver;
       +uchar        ipaddr[IPaddrlen];        /* my ip address */
       +int        maxage;
       +char        *logfile = "dns";
       +char        *dbfile;
       +char        mntpt[Maxpath];
       +char        *zonerefreshprogram;
       +
       +int prettyrrfmt(Fmt*);
       +void preloadserveraddrs(void);
       +void squirrelserveraddrs(void);
       +int setserver(char*);
       +void doquery(char*, char*);
       +void docmd(int, char**);
       +
       +void
       +main(int argc, char *argv[])
       +{
       +        int n;
       +        Biobuf in;
       +        char *p;
       +        char *f[4];
       +
       +        strcpy(mntpt, "/net");
       +
       +        ARGBEGIN{
       +        case 'r':
       +                resolver = 1;
       +                break;
       +        case 'x':
       +                dbfile = "/lib/ndb/external";
       +                strcpy(mntpt, "/net.alt");
       +                break;
       +        case 'f':
       +                dbfile = ARGF();
       +                break;
       +        }ARGEND
       +
       +        now = time(0);
       +        dninit();
       +        fmtinstall('R', prettyrrfmt);
       +        if(myipaddr(ipaddr, mntpt) < 0)
       +                sysfatal("can't read my ip address");
       +        opendatabase();
       +
       +        if(resolver)
       +                squirrelserveraddrs();
       +
       +        debug = 1;
       +
       +        if(argc > 0){
       +                docmd(argc, argv);
       +                exits(0);
       +        }
       +
       +        Binit(&in, 0, OREAD);
       +        for(print("> "); p = Brdline(&in, '\n'); print("> ")){
       +                p[Blinelen(&in)-1] = 0;
       +                n = tokenize(p, f, 3);
       +                if(n<1)
       +                        continue;
       +
       +                /* flush the cache */
       +                dnpurge();
       +
       +                docmd(n, f);
       +
       +        }
       +        exits(0);
       +}
       +
       +static char*
       +longtime(long t)
       +{
       +        int d, h, m, n;
       +        static char x[128];
       +
       +        for(d = 0; t >= 24*60*60; t -= 24*60*60)
       +                d++;
       +        for(h = 0; t >= 60*60; t -= 60*60)
       +                h++;
       +        for(m = 0; t >= 60; t -= 60)
       +                m++;
       +        n = 0;
       +        if(d)
       +                n += sprint(x, "%d day ", d);
       +        if(h)
       +                n += sprint(x+n, "%d hr ", h);
       +        if(m)
       +                n += sprint(x+n, "%d min ", m);
       +        if(t || n == 0)
       +                sprint(x+n, "%ld sec", t);
       +        return x;
       +}
       +
       +int
       +prettyrrfmt(Fmt *f)
       +{
       +        RR *rp;
       +        char buf[3*Domlen];
       +        char *p, *e;
       +        Txt *t;
       +
       +        rp = va_arg(f->args, RR*);
       +        if(rp == 0){
       +                strcpy(buf, "<null>");
       +                goto out;
       +        }
       +
       +        p = buf;
       +        e = buf + sizeof(buf);
       +        p = seprint(p, e, "%-32.32s %-15.15s %-5.5s", rp->owner->name,
       +                longtime(rp->db ? rp->ttl : (rp->ttl-now)),
       +                rrname(rp->type, buf, sizeof buf));
       +
       +        if(rp->negative){
       +                seprint(p, e, "negative rcode %d\n", rp->negrcode);
       +                goto out;
       +        }
       +
       +        switch(rp->type){
       +        case Thinfo:
       +                seprint(p, e, "\t%s %s", rp->cpu->name, rp->os->name);
       +                break;
       +        case Tcname:
       +        case Tmb:
       +        case Tmd:
       +        case Tmf:
       +        case Tns:
       +                seprint(p, e, "\t%s", rp->host->name);
       +                break;
       +        case Tmg:
       +        case Tmr:
       +                seprint(p, e, "\t%s", rp->mb->name);
       +                break;
       +        case Tminfo:
       +                seprint(p, e, "\t%s %s", rp->mb->name, rp->rmb->name);
       +                break;
       +        case Tmx:
       +                seprint(p, e, "\t%lud %s", rp->pref, rp->host->name);
       +                break;
       +        case Ta:
       +        case Taaaa:
       +                seprint(p, e, "\t%s", rp->ip->name);
       +                break;
       +        case Tptr:
       +                seprint(p, e, "\t%s", rp->ptr->name);
       +                break;
       +        case Tsoa:
       +                seprint(p, e, "\t%s %s %lud %lud %lud %lud %lud", rp->host->name,
       +                        rp->rmb->name, rp->soa->serial, rp->soa->refresh, rp->soa->retry,
       +                        rp->soa->expire, rp->soa->minttl);
       +                break;
       +        case Tnull:
       +                seprint(p, e, "\t%.*H", rp->null->dlen, rp->null->data);
       +                break;
       +        case Ttxt:
       +                p = seprint(p, e, "\t");
       +                for(t = rp->txt; t != nil; t = t->next)
       +                        p = seprint(p, e, "%s", t->p);
       +                break;
       +        case Trp:
       +                seprint(p, e, "\t%s %s", rp->rmb->name, rp->rp->name);
       +                break;
       +        case Tkey:
       +                seprint(p, e, "\t%d %d %d", rp->key->flags, rp->key->proto,
       +                        rp->key->alg);
       +                break;
       +        case Tsig:
       +                seprint(p, e, "\t%d %d %d %lud %lud %lud %d %s",
       +                        rp->sig->type, rp->sig->alg, rp->sig->labels, rp->sig->ttl,
       +                        rp->sig->exp, rp->sig->incep, rp->sig->tag, rp->sig->signer->name);
       +                break;
       +        case Tcert:
       +                seprint(p, e, "\t%d %d %d",
       +                        rp->sig->type, rp->sig->tag, rp->sig->alg);
       +                break;
       +        default:
       +                break;
       +        }
       +out:
       +        return fmtstrcpy(f, buf);
       +}
       +
       +void
       +logsection(char *flag, RR *rp)
       +{
       +        if(rp == nil)
       +                return;
       +        print("\t%s%R\n", flag, rp);
       +        for(rp = rp->next; rp != nil; rp = rp->next)
       +                print("\t      %R\n", rp);
       +}
       +
       +void
       +logreply(int id, uchar *addr, DNSmsg *mp)
       +{
       +        RR *rp;
       +        char buf[12];
       +        char resp[32];
       +
       +        switch(mp->flags & Rmask){
       +        case Rok:
       +                strcpy(resp, "OK");
       +                break;
       +        case Rformat:
       +                strcpy(resp, "Format error");
       +                break;
       +        case Rserver:
       +                strcpy(resp, "Server failed");
       +                break;
       +        case Rname:
       +                strcpy(resp, "Nonexistent");
       +                break;
       +        case Runimplimented:
       +                strcpy(resp, "Unimplemented");
       +                break;
       +        case Rrefused:
       +                strcpy(resp, "Refused");
       +                break;
       +        default:
       +                sprint(resp, "%d", mp->flags & Rmask);
       +                break;
       +        }
       +
       +        print("%d: rcvd %s from %I (%s%s%s%s%s)\n", id, resp, addr,
       +                mp->flags & Fauth ? "authoritative" : "",
       +                mp->flags & Ftrunc ? " truncated" : "",
       +                mp->flags & Frecurse ? " recurse" : "",
       +                mp->flags & Fcanrec ? " can_recurse" : "",
       +                mp->flags & (Fauth|Rname) == (Fauth|Rname) ?
       +                " nx" : "");
       +        for(rp = mp->qd; rp != nil; rp = rp->next)
       +                print("\tQ:    %s %s\n", rp->owner->name, rrname(rp->type, buf, sizeof buf));
       +        logsection("Ans:  ", mp->an);
       +        logsection("Auth: ", mp->ns);
       +        logsection("Hint: ", mp->ar);
       +}
       +
       +void
       +logsend(int id, int subid, uchar *addr, char *sname, char *rname, int type)
       +{
       +        char buf[12];
       +
       +        print("%d.%d: sending to %I/%s %s %s\n", id, subid,
       +                addr, sname, rname, rrname(type, buf, sizeof buf));
       +}
       +
       +RR*
       +getdnsservers(int class)
       +{
       +        RR *rr;
       +
       +        if(servername == nil)
       +                return dnsservers(class);
       +
       +        rr = rralloc(Tns);
       +        rr->owner = dnlookup("local#dns#servers", class, 1);
       +        rr->host = dnlookup(servername, class, 1);
       +
       +        return rr;
       +}
       +
       +void
       +squirrelserveraddrs(void)
       +{
       +        RR *rr, *rp, **l;
       +        Request req;
       +
       +        /* look up the resolver address first */
       +        resolver = 0;
       +        debug = 0;
       +        if(serveraddrs)
       +                rrfreelist(serveraddrs);
       +        serveraddrs = nil;
       +        rr = getdnsservers(Cin);
       +        l = &serveraddrs;
       +        for(rp = rr; rp != nil; rp = rp->next){
       +                if(strcmp(ipattr(rp->host->name), "ip") == 0){
       +                        *l = rralloc(Ta);
       +                        (*l)->owner = rp->host;
       +                        (*l)->ip = rp->host;
       +                        l = &(*l)->next;
       +                        continue;
       +                }
       +                req.isslave = 1;
       +                req.aborttime = now + 60;        /* don't spend more than 60 seconds */
       +                *l = dnresolve(rp->host->name, Cin, Ta, &req, 0, 0, Recurse, 0, 0);
       +                while(*l != nil)
       +                        l = &(*l)->next;
       +        }
       +        resolver = 1;
       +        debug = 1;
       +}
       +
       +void
       +preloadserveraddrs(void)
       +{
       +        RR *rp, **l, *first;
       +        
       +        l = &first;
       +        for(rp = serveraddrs; rp != nil; rp = rp->next){
       +                rrcopy(rp, l);
       +                rrattach(first, 1);
       +        }
       +}
       +
       +int
       +setserver(char *server)
       +{
       +        if(servername != nil){
       +                free(servername);
       +                servername = nil;
       +                resolver = 0;
       +        }
       +        if(server == nil || *server == 0)
       +                return 0;
       +        servername = strdup(server);
       +        squirrelserveraddrs();
       +        if(serveraddrs == nil){
       +                print("can't resolve %s\n", servername);
       +                resolver = 0;
       +        } else {
       +                resolver = 1;
       +        }
       +        return resolver ? 0 : -1;
       +}
       +
       +void
       +doquery(char *name, char *tstr)
       +{
       +        Request req;
       +        RR *rr, *rp;
       +        int len, type;
       +        char *p, *np;
       +        int rooted;
       +        char buf[1024];
       +
       +        if(resolver)
       +                preloadserveraddrs();
       +
       +        /* default to an "ip" request if alpha, "ptr" if numeric */
       +        if(tstr == nil || *tstr == 0) {
       +                if(strcmp(ipattr(name), "ip") == 0)
       +                        tstr = "ptr";
       +                else
       +                        tstr = "ip";
       +        }
       +
       +        /* if name end in '.', remove it */
       +        len = strlen(name);
       +        if(len > 0 && name[len-1] == '.'){
       +                rooted = 1;
       +                name[len-1] = 0;
       +        } else
       +                rooted = 0;
       +
       +        /* inverse queries may need to be permuted */
       +        strncpy(buf, name, sizeof buf);
       +        if(strcmp("ptr", tstr) == 0
       +        && strstr(name, "IN-ADDR") == 0
       +        && strstr(name, "in-addr") == 0){
       +                for(p = name; *p; p++)
       +                        ;
       +                *p = '.';
       +                np = buf;
       +                len = 0;
       +                while(p >= name){
       +                        len++;
       +                        p--;
       +                        if(*p == '.'){
       +                                memmove(np, p+1, len);
       +                                np += len;
       +                                len = 0;
       +                        }
       +                }
       +                memmove(np, p+1, len);
       +                np += len;
       +                strcpy(np, "in-addr.arpa");
       +        }
       +
       +        /* look it up */
       +        type = rrtype(tstr);
       +        if(type < 0){
       +                print("!unknown type %s\n", tstr);
       +                return;
       +        }
       +
       +        getactivity(&req);
       +        req.isslave = 1;
       +        req.aborttime = now + 60;        /* don't spend more than 60 seconds */
       +        rr = dnresolve(buf, Cin, type, &req, 0, 0, Recurse, rooted, 0);
       +        if(rr){
       +                print("----------------------------\n");
       +                for(rp = rr; rp; rp = rp->next)
       +                        print("answer %R\n", rp);
       +                print("----------------------------\n");
       +        }
       +        rrfreelist(rr);
       +
       +        putactivity();
       +}
       +
       +void
       +docmd(int n, char **f)
       +{
       +        int tmpsrv;
       +        char *name, *type;
       +
       +        name = nil;
       +        type = nil;
       +        tmpsrv = 0;
       +
       +        if(*f[0] == '@') {
       +                if(setserver(f[0]+1) < 0)
       +                        return;
       +
       +                switch(n){
       +                case 3:
       +                        type = f[2];
       +                        /* fall through */
       +                case 2:
       +                        name = f[1];
       +                        tmpsrv = 1;
       +                        break;
       +                }
       +        } else {
       +                switch(n){
       +                case 2:
       +                        type = f[1];
       +                        /* fall through */
       +                case 1:
       +                        name = f[0];
       +                        break;
       +                }
       +        }
       +
       +        if(name == nil)
       +                return;
       +
       +        doquery(name, type);
       +
       +        if(tmpsrv)
       +                setserver("");
       +}
 (DIR) diff --git a/src/cmd/ndb/dnserver.c b/src/cmd/ndb/dnserver.c
       t@@ -0,0 +1,178 @@
       +#include <u.h>
       +#include <libc.h>
       +#include <ip.h>
       +#include <bio.h>
       +#include <ndb.h>
       +#include "dns.h"
       +
       +static RR*        doextquery(DNSmsg*, Request*, int);
       +static void        hint(RR**, RR*);
       +
       +extern char *logfile;
       +
       +/*
       + *  answer a dns request
       + */
       +void
       +dnserver(DNSmsg *reqp, DNSmsg *repp, Request *req)
       +{
       +        RR *tp, *neg;
       +        char *cp;
       +        DN *nsdp, *dp;
       +        Area *myarea;
       +        char tname[32];
       +
       +        dncheck(nil, 1);
       +
       +        memset(repp, 0, sizeof(*repp));
       +        repp->id = reqp->id;
       +        repp->flags = Fresp | Fcanrec | Oquery;
       +
       +        /* move one question from reqp to repp */
       +        tp = reqp->qd;
       +        reqp->qd = tp->next;
       +        tp->next = 0;
       +        repp->qd = tp;
       +
       +        if(!rrsupported(repp->qd->type)){
       +                syslog(0, logfile, "server: request %s", rrname(repp->qd->type, tname, sizeof tname));
       +                repp->flags = Runimplimented | Fresp | Fcanrec | Oquery;
       +                return;
       +        }
       +
       +        if(repp->qd->owner->class != Cin){
       +                syslog(0, logfile, "server: class %d", repp->qd->owner->class);
       +                repp->flags = Runimplimented | Fresp | Fcanrec | Oquery;
       +                return;
       +        }
       +
       +        myarea = inmyarea(repp->qd->owner->name);
       +        if(myarea != nil && (repp->qd->type == Tixfr || repp->qd->type == Taxfr)){
       +                syslog(0, logfile, "server: request %s", rrname(repp->qd->type, tname, sizeof tname));
       +                repp->flags = Runimplimented | Fresp | Fcanrec | Oquery;
       +                return;
       +        }
       +
       +        /*
       +         *  get the answer if we can
       +         */
       +        if(reqp->flags & Frecurse)
       +                neg = doextquery(repp, req, Recurse);
       +        else
       +                neg = doextquery(repp, req, Dontrecurse);
       +
       +        /* authority is transitive */
       +        if(myarea != nil || (repp->an && repp->an->auth))
       +                repp->flags |= Fauth;
       +
       +        /* pass on error codes */
       +        if(repp->an == 0){
       +                dp = dnlookup(repp->qd->owner->name, repp->qd->owner->class, 0);
       +                if(dp->rr == 0)
       +                        if(reqp->flags & Frecurse)
       +                                repp->flags |= dp->nonexistent|Fauth;
       +        }
       +
       +        if(myarea == nil){
       +                /*
       +                 *  add name server if we know
       +                 */
       +                for(cp = repp->qd->owner->name; cp; cp = walkup(cp)){
       +                        nsdp = dnlookup(cp, repp->qd->owner->class, 0);
       +                        if(nsdp == 0)
       +                                continue;
       +        
       +                        repp->ns = rrlookup(nsdp, Tns, OKneg);
       +                        if(repp->ns){
       +                                /* don't pass on anything we know is wrong */
       +                                if(repp->ns->negative){
       +                                        rrfreelist(repp->ns);
       +                                        repp->ns = nil;
       +                                }
       +                                break;
       +                        }
       +        
       +                        repp->ns = dblookup(cp, repp->qd->owner->class, Tns, 0, 0);
       +                        if(repp->ns)
       +                                break;
       +                }
       +        }
       +
       +        /*
       +         *  add ip addresses as hints
       +         */
       +        if(repp->qd->type != Taxfr && repp->qd->type != Tixfr){
       +                for(tp = repp->ns; tp; tp = tp->next)
       +                        hint(&repp->ar, tp);
       +                for(tp = repp->an; tp; tp = tp->next)
       +                        hint(&repp->ar, tp);
       +        }
       +
       +        /*
       +         *  add an soa to the authority section to help client with negative caching
       +         */
       +        if(repp->an == nil){
       +                if(myarea != nil){
       +                        rrcopy(myarea->soarr, &tp);
       +                        rrcat(&repp->ns, tp);
       +                } else if(neg != nil) {
       +                        if(neg->negsoaowner != nil)
       +                                rrcat(&repp->ns, rrlookup(neg->negsoaowner, Tsoa, NOneg));
       +                        repp->flags |= neg->negrcode;
       +                }
       +        }
       +
       +        /*
       +         *  get rid of duplicates
       +         */
       +        unique(repp->an);
       +        unique(repp->ns);
       +        unique(repp->ar);
       +
       +        rrfreelist(neg);
       +
       +        dncheck(nil, 1);
       +}
       +
       +/*
       + *  satisfy a recursive request.  dnlookup will handle cnames.
       + */
       +static RR*
       +doextquery(DNSmsg *mp, Request *req, int recurse)
       +{
       +        int type;
       +        char *name;
       +        RR *rp, *neg;
       +
       +        name = mp->qd->owner->name;
       +        type = mp->qd->type;
       +        rp = dnresolve(name, Cin, type, req, &mp->an, 0, recurse, 1, 0);
       +
       +        /* don't return soa hints as answers, it's wrong */
       +        if(rp && rp->db && !rp->auth && rp->type == Tsoa)
       +                rrfreelist(rp);
       +
       +        /* don't let negative cached entries escape */
       +        neg = rrremneg(&rp);
       +        rrcat(&mp->an, rp);
       +        return neg;
       +}
       +
       +static void
       +hint(RR **last, RR *rp)
       +{
       +        RR *hp;
       +
       +        switch(rp->type){
       +        case Tns:
       +        case Tmx:
       +        case Tmb:
       +        case Tmf:
       +        case Tmd:
       +                hp = rrlookup(rp->host, Ta, NOneg);
       +                if(hp == nil)
       +                        hp = dblookup(rp->host->name, Cin, Ta, 0, 0);
       +                rrcat(last, hp);
       +                break;
       +        }
       +}
 (DIR) diff --git a/src/cmd/ndb/dnsquery.c b/src/cmd/ndb/dnsquery.c
       t@@ -0,0 +1,113 @@
       +#include <u.h>
       +#include <libc.h>
       +#include <bio.h>
       +#include <ndb.h>
       +#include "dns.h"
       +#include "ip.h"
       +
       +void
       +main(int argc, char *argv[])
       +{
       +        int fd, n, len, domount;
       +        Biobuf in;
       +        char line[1024], *lp, *p, *np, *mtpt, *srv, *dns;
       +        char buf[1024];
       +
       +        dns = "/net/dns";
       +        mtpt = "/net";
       +        srv = "/srv/dns";
       +        domount = 1;
       +        ARGBEGIN {
       +        case 'x':
       +                dns = "/net.alt/dns";
       +                mtpt = "/net.alt";
       +                srv = "/srv/dns_net.alt";
       +                break;
       +        default:
       +                fprint(2, "usage: %s -x [dns-mount-point]\n", argv0);
       +                exits("usage");
       +        } ARGEND;
       +
       +        if(argc == 1){
       +                domount = 0;
       +                mtpt = argv[0];
       +        }
       +
       +        fd = open(dns, ORDWR);
       +        if(fd < 0){
       +                if(domount == 0){
       +                        fprint(2, "can't open %s: %r\n", mtpt);
       +                        exits(0);
       +                }
       +                fd = open(srv, ORDWR);
       +                if(fd < 0){
       +                        print("can't open %s: %r\n", srv);
       +                        exits(0);
       +                }
       +                if(mount(fd, -1, mtpt, MBEFORE, "") < 0){
       +                        print("can't mount(%s, %s): %r\n", srv, mtpt);
       +                        exits(0);
       +                }
       +                fd = open(mtpt, ORDWR);
       +                if(fd < 0){
       +                        print("can't open %s: %r\n", mtpt);
       +                        exits(0);
       +                }
       +        }
       +        Binit(&in, 0, OREAD);
       +        for(print("> "); lp = Brdline(&in, '\n'); print("> ")){
       +                n = Blinelen(&in)-1;
       +                strncpy(line, lp, n);
       +                line[n] = 0;
       +                if (n<=1)
       +                        continue;
       +                /* default to an "ip" request if alpha, "ptr" if numeric */
       +                if (strchr(line, ' ')==0) {
       +                        if(strcmp(ipattr(line), "ip") == 0) {
       +                                strcat(line, " ptr");
       +                                n += 4;
       +                        } else {
       +                                strcat(line, " ip");
       +                                n += 3;
       +                        }
       +                }
       +
       +                /* inverse queries may need to be permuted */
       +                if(n > 4 && strcmp("ptr", &line[n-3]) == 0
       +                && strstr(line, "IN-ADDR") == 0 && strstr(line, "in-addr") == 0){
       +                        for(p = line; *p; p++)
       +                                if(*p == ' '){
       +                                        *p = '.';
       +                                        break;
       +                                }
       +                        np = buf;
       +                        len = 0;
       +                        while(p >= line){
       +                                len++;
       +                                p--;
       +                                if(*p == '.'){
       +                                        memmove(np, p+1, len);
       +                                        np += len;
       +                                        len = 0;
       +                                }
       +                        }
       +                        memmove(np, p+1, len);
       +                        np += len;
       +                        strcpy(np, "in-addr.arpa ptr");
       +                        strcpy(line, buf);
       +                        n = strlen(line);
       +                }
       +
       +                seek(fd, 0, 0);
       +                if(write(fd, line, n) < 0) {
       +                        print("!%r\n");
       +                        continue;
       +                }
       +                seek(fd, 0, 0);
       +                while((n = read(fd, buf, sizeof(buf))) > 0){
       +                        buf[n] = 0;
       +                        print("%s\n", buf);
       +                }
       +        }
       +        exits(0);
       +}
 (DIR) diff --git a/src/cmd/ndb/dnstcp.c b/src/cmd/ndb/dnstcp.c
       t@@ -0,0 +1,362 @@
       +#include <u.h>
       +#include <libc.h>
       +#include <ip.h>
       +#include "dns.h"
       +
       +enum
       +{
       +        Maxpath=                128,
       +};
       +
       +char        *logfile = "dns";
       +char        *dbfile;
       +int        debug;
       +int        cachedb = 1;
       +int        testing;
       +int traceactivity;
       +int        needrefresh;
       +int         resolver;
       +char        mntpt[Maxpath];
       +char        *caller = "";
       +ulong        now;
       +int        maxage;
       +uchar        ipaddr[IPaddrlen];        /* my ip address */
       +char        *LOG;
       +char        *zonerefreshprogram;
       +
       +static int        readmsg(int, uchar*, int);
       +static void        reply(int, DNSmsg*, Request*);
       +static void        dnzone(DNSmsg*, DNSmsg*, Request*);
       +static void        getcaller(char*);
       +static void        refreshmain(char*);
       +
       +void
       +main(int argc, char *argv[])
       +{
       +        int len;
       +        Request req;
       +        DNSmsg reqmsg, repmsg;
       +        uchar buf[512];
       +        char tname[32];
       +        char *err;
       +        char *ext = "";
       +
       +        ARGBEGIN{
       +        case 'd':
       +                debug++;
       +                break;
       +        case 'f':
       +                dbfile = ARGF();
       +                break;
       +        case 'r':
       +                resolver = 1;
       +                break;
       +        case 'x':
       +                ext = ARGF();
       +                break;
       +        }ARGEND
       +
       +        if(debug < 2)
       +                debug = 0;
       +
       +        if(argc > 0)
       +                getcaller(argv[0]);
       +
       +        dninit();
       +
       +        snprint(mntpt, sizeof(mntpt), "/net%s", ext);
       +        if(myipaddr(ipaddr, mntpt) < 0)
       +                sysfatal("can't read my ip address");
       +        syslog(0, logfile, "dnstcp call from %s to %I", caller, ipaddr);
       +
       +        db2cache(1);
       +
       +        setjmp(req.mret);
       +        req.isslave = 0;
       +
       +        /* loop on requests */
       +        for(;; putactivity()){
       +                now = time(0);
       +                memset(&repmsg, 0, sizeof(repmsg));
       +                alarm(10*60*1000);
       +                len = readmsg(0, buf, sizeof(buf));
       +                alarm(0);
       +                if(len <= 0)
       +                        break;
       +                getactivity(&req);
       +                req.aborttime = now + 15*Min;
       +                err = convM2DNS(buf, len, &reqmsg);
       +                if(err){
       +                        syslog(0, logfile, "server: input error: %s from %I", err, buf);
       +                        break;
       +                }
       +                if(reqmsg.qdcount < 1){
       +                        syslog(0, logfile, "server: no questions from %I", buf);
       +                        break;
       +                }
       +                if(reqmsg.flags & Fresp){
       +                        syslog(0, logfile, "server: reply not request from %I", buf);
       +                        break;
       +                }
       +                if((reqmsg.flags & Omask) != Oquery){
       +                        syslog(0, logfile, "server: op %d from %I", reqmsg.flags & Omask, buf);
       +                        break;
       +                }
       +
       +                if(debug)
       +                        syslog(0, logfile, "%d: serve (%s) %d %s %s",
       +                                req.id, caller,
       +                                reqmsg.id,
       +                                reqmsg.qd->owner->name,
       +                                rrname(reqmsg.qd->type, tname, sizeof tname));
       +
       +                /* loop through each question */
       +                while(reqmsg.qd){
       +                        if(reqmsg.qd->type == Taxfr){
       +                                dnzone(&reqmsg, &repmsg, &req);
       +                        } else {
       +                                dnserver(&reqmsg, &repmsg, &req);
       +                                reply(1, &repmsg, &req);
       +                                rrfreelist(repmsg.qd);
       +                                rrfreelist(repmsg.an);
       +                                rrfreelist(repmsg.ns);
       +                                rrfreelist(repmsg.ar);
       +                        }
       +                }
       +
       +                rrfreelist(reqmsg.qd);
       +                rrfreelist(reqmsg.an);
       +                rrfreelist(reqmsg.ns);
       +                rrfreelist(reqmsg.ar);
       +
       +                if(req.isslave){
       +                        putactivity();
       +                        _exits(0);
       +                }
       +        }
       +        refreshmain(mntpt);
       +}
       +
       +static int
       +readmsg(int fd, uchar *buf, int max)
       +{
       +        int n;
       +        uchar x[2];
       +
       +        if(readn(fd, x, 2) != 2)
       +                return -1;
       +        n = (x[0]<<8) | x[1];
       +        if(n > max)
       +                return -1;
       +        if(readn(fd, buf, n) != n)
       +                return -1;
       +        return n;
       +}
       +
       +static void
       +reply(int fd, DNSmsg *rep, Request *req)
       +{
       +        int len, rv;
       +        char tname[32];
       +        uchar buf[4096];
       +        RR *rp;
       +
       +        if(debug){
       +                syslog(0, logfile, "%d: reply (%s) %s %s %ux",
       +                        req->id, caller,
       +                        rep->qd->owner->name,
       +                        rrname(rep->qd->type, tname, sizeof tname),
       +                        rep->flags);
       +                for(rp = rep->an; rp; rp = rp->next)
       +                        syslog(0, logfile, "an %R", rp);
       +                for(rp = rep->ns; rp; rp = rp->next)
       +                        syslog(0, logfile, "ns %R", rp);
       +                for(rp = rep->ar; rp; rp = rp->next)
       +                        syslog(0, logfile, "ar %R", rp);
       +        }
       +
       +
       +        len = convDNS2M(rep, buf+2, sizeof(buf) - 2);
       +        if(len <= 0)
       +                abort(); /* "dnserver: converting reply" */;
       +        buf[0] = len>>8;
       +        buf[1] = len;
       +        rv = write(fd, buf, len+2);
       +        if(rv != len+2){
       +                syslog(0, logfile, "sending reply: %d instead of %d", rv, len+2);
       +                exits(0);
       +        }
       +}
       +
       +/*
       + *  Hash table for domain names.  The hash is based only on the
       + *  first element of the domain name.
       + */
       +extern DN        *ht[HTLEN];
       +
       +static int
       +numelem(char *name)
       +{
       +        int i;
       +
       +        i = 1;
       +        for(; *name; name++)
       +                if(*name == '.')
       +                        i++;
       +        return i;
       +}
       +
       +int
       +inzone(DN *dp, char *name, int namelen, int depth)
       +{
       +        int n;
       +
       +        if(dp->name == 0)
       +                return 0;
       +        if(numelem(dp->name) != depth)
       +                return 0;
       +        n = strlen(dp->name);
       +        if(n < namelen)
       +                return 0;
       +        if(strcmp(name, dp->name + n - namelen) != 0)
       +                return 0;
       +        if(n > namelen && dp->name[n - namelen - 1] != '.')
       +                return 0;
       +        return 1;
       +}
       +
       +static void
       +dnzone(DNSmsg *reqp, DNSmsg *repp, Request *req)
       +{
       +        DN *dp, *ndp;
       +        RR r, *rp;
       +        int h, depth, found, nlen;
       +
       +        memset(repp, 0, sizeof(*repp));
       +        repp->id = reqp->id;
       +        repp->flags = Fauth | Fresp | Fcanrec | Oquery;
       +        repp->qd = reqp->qd;
       +        reqp->qd = reqp->qd->next;
       +        repp->qd->next = 0;
       +        dp = repp->qd->owner;
       +
       +        /* send the soa */
       +        repp->an = rrlookup(dp, Tsoa, NOneg);
       +        reply(1, repp, req);
       +        if(repp->an == 0)
       +                goto out;
       +        rrfreelist(repp->an);
       +
       +        nlen = strlen(dp->name);
       +
       +        /* construct a breadth first search of the name space (hard with a hash) */
       +        repp->an = &r;
       +        for(depth = numelem(dp->name); ; depth++){
       +                found = 0;
       +                for(h = 0; h < HTLEN; h++)
       +                        for(ndp = ht[h]; ndp; ndp = ndp->next)
       +                                if(inzone(ndp, dp->name, nlen, depth)){
       +                                        for(rp = ndp->rr; rp; rp = rp->next){
       +                                                /* there shouldn't be negatives, but just in case */
       +                                                if(rp->negative)
       +                                                        continue;
       +
       +                                                /* don't send an soa's, ns's are enough */
       +                                                if(rp->type == Tsoa)
       +                                                        continue;
       +
       +                                                r = *rp;
       +                                                r.next = 0;
       +                                                reply(1, repp, req);
       +                                        }
       +                                        found = 1;
       +                                }
       +                if(!found)
       +                        break;
       +        }
       +
       +        /* resend the soa */
       +        repp->an = rrlookup(dp, Tsoa, NOneg);
       +        reply(1, repp, req);
       +        rrfreelist(repp->an);
       +out:
       +        rrfree(repp->qd);
       +}
       +
       +static void
       +getcaller(char *dir)
       +{
       +        int fd, n;
       +        static char remote[128];
       +
       +        snprint(remote, sizeof(remote), "%s/remote", dir);
       +        fd = open(remote, OREAD);
       +        if(fd < 0)
       +                return;
       +        n = read(fd, remote, sizeof(remote)-1);
       +        close(fd);
       +        if(n <= 0)
       +                return;
       +        if(remote[n-1] == '\n')
       +                n--;
       +        remote[n] = 0;
       +        caller = remote;
       +}
       +
       +static void
       +refreshmain(char *net)
       +{
       +        int fd;
       +        char file[128];
       +
       +        snprint(file, sizeof(file), "%s/dns", net);
       +        if(debug)
       +                syslog(0, logfile, "refreshing %s", file);
       +        fd = open(file, ORDWR);
       +        if(fd < 0){
       +                syslog(0, logfile, "can't refresh %s", file);
       +                return;
       +        }
       +        fprint(fd, "refresh");
       +        close(fd);
       +}
       +
       +/*
       + *  the following varies between dnsdebug and dns
       + */
       +void
       +logreply(int id, uchar *addr, DNSmsg *mp)
       +{
       +        RR *rp;
       +
       +        syslog(0, LOG, "%d: rcvd %I flags:%s%s%s%s%s", id, addr,
       +                mp->flags & Fauth ? " auth" : "",
       +                mp->flags & Ftrunc ? " trunc" : "",
       +                mp->flags & Frecurse ? " rd" : "",
       +                mp->flags & Fcanrec ? " ra" : "",
       +                mp->flags & (Fauth|Rname) == (Fauth|Rname) ?
       +                " nx" : "");
       +        for(rp = mp->qd; rp != nil; rp = rp->next)
       +                syslog(0, LOG, "%d: rcvd %I qd %s", id, addr, rp->owner->name);
       +        for(rp = mp->an; rp != nil; rp = rp->next)
       +                syslog(0, LOG, "%d: rcvd %I an %R", id, addr, rp);
       +        for(rp = mp->ns; rp != nil; rp = rp->next)
       +                syslog(0, LOG, "%d: rcvd %I ns %R", id, addr, rp);
       +        for(rp = mp->ar; rp != nil; rp = rp->next)
       +                syslog(0, LOG, "%d: rcvd %I ar %R", id, addr, rp);
       +}
       +
       +void
       +logsend(int id, int subid, uchar *addr, char *sname, char *rname, int type)
       +{
       +        char buf[12];
       +
       +        syslog(0, LOG, "%d.%d: sending to %I/%s %s %s",
       +                id, subid, addr, sname, rname, rrname(type, buf, sizeof buf));
       +}
       +
       +RR*
       +getdnsservers(int class)
       +{
       +        return dnsservers(class);
       +}
 (DIR) diff --git a/src/cmd/ndb/dnudpserver.c b/src/cmd/ndb/dnudpserver.c
       t@@ -0,0 +1,228 @@
       +#include <u.h>
       +#include <libc.h>
       +#include <ip.h>
       +#include <bio.h>
       +#include <ndb.h>
       +#include "dns.h"
       +
       +static int        udpannounce(char*);
       +static void        reply(int, uchar*, DNSmsg*, Request*);
       +
       +extern char *logfile;
       +
       +static void
       +ding(void *x, char *msg)
       +{
       +        USED(x);
       +        if(strcmp(msg, "alarm") == 0)
       +                noted(NCONT);
       +        else
       +                noted(NDFLT);
       +}
       +
       +typedef struct Inprogress Inprogress;
       +struct Inprogress
       +{
       +        int        inuse;
       +        OUdphdr        uh;
       +        DN        *owner;
       +        int        type;
       +        int        id;
       +};
       +Inprogress inprog[Maxactive+2];
       +
       +/*
       + *  record client id and ignore retransmissions.
       + *  we're still single thread at this point.
       + */
       +static Inprogress*
       +clientrxmit(DNSmsg *req, uchar *buf)
       +{
       +        Inprogress *p, *empty;
       +        OUdphdr *uh;
       +
       +        uh = (OUdphdr *)buf;
       +        empty = 0;
       +        for(p = inprog; p < &inprog[Maxactive]; p++){
       +                if(p->inuse == 0){
       +                        if(empty == 0)
       +                                empty = p;
       +                        continue;
       +                }
       +                if(req->id == p->id)
       +                if(req->qd->owner == p->owner)
       +                if(req->qd->type == p->type)
       +                if(memcmp(uh, &p->uh, OUdphdrsize) == 0)
       +                        return 0;
       +        }
       +        if(empty == 0)
       +                return 0;        /* shouldn't happen - see slave() and definition of Maxactive */
       +
       +        empty->id = req->id;
       +        empty->owner = req->qd->owner;
       +        empty->type = req->qd->type;
       +        memmove(&empty->uh, uh, OUdphdrsize);
       +        empty->inuse = 1;
       +        return empty;
       +}
       +
       +/*
       + *  a process to act as a dns server for outside reqeusts
       + */
       +void
       +dnudpserver(char *mntpt)
       +{
       +        int fd, len, op;
       +        Request req;
       +        DNSmsg reqmsg, repmsg;
       +        uchar buf[OUdphdrsize + Maxudp + 1024];
       +        char *err;
       +        Inprogress *p;
       +        char tname[32];
       +        OUdphdr *uh;
       +
       +        /* fork sharing text, data, and bss with parent */
       +        switch(rfork(RFPROC|RFNOTEG|RFMEM|RFNOWAIT)){
       +        case -1:
       +                break;
       +        case 0:
       +                break;
       +        default:
       +                return;
       +        }
       +
       +        fd = -1;
       +        notify(ding);
       +restart:
       +        if(fd >= 0)
       +                close(fd);
       +        while((fd = udpannounce(mntpt)) < 0)
       +                sleep(5000);
       +        if(setjmp(req.mret))
       +                putactivity();
       +        req.isslave = 0;
       +
       +        /* loop on requests */
       +        for(;; putactivity()){
       +                memset(&repmsg, 0, sizeof(repmsg));
       +                memset(&reqmsg, 0, sizeof(reqmsg));
       +                alarm(60*1000);
       +                len = udpread(fd, (OUdphdr*)buf, buf+OUdphdrsize, sizeof(buf)-OUdphdrsize);
       +                alarm(0);
       +                if(len <= 0)
       +                        goto restart;
       +                uh = (OUdphdr*)buf;
       +                getactivity(&req);
       +                req.aborttime = now + 30;        /* don't spend more than 30 seconds */
       +                err = convM2DNS(&buf[OUdphdrsize], len, &reqmsg);
       +                if(err){
       +                        syslog(0, logfile, "server: input error: %s from %I", err, buf);
       +                        continue;
       +                }
       +                if(reqmsg.qdcount < 1){
       +                        syslog(0, logfile, "server: no questions from %I", buf);
       +                        goto freereq;
       +                }
       +                if(reqmsg.flags & Fresp){
       +                        syslog(0, logfile, "server: reply not request from %I", buf);
       +                        goto freereq;
       +                }
       +                op = reqmsg.flags & Omask;
       +                if(op != Oquery && op != Onotify){
       +                        syslog(0, logfile, "server: op %d from %I", reqmsg.flags & Omask, buf);
       +                        goto freereq;
       +                }
       +
       +                if(debug || (trace && subsume(trace, reqmsg.qd->owner->name))){
       +                        syslog(0, logfile, "%d: serve (%I/%d) %d %s %s",
       +                                req.id, buf, ((uh->rport[0])<<8)+uh->rport[1],
       +                                reqmsg.id,
       +                                reqmsg.qd->owner->name,
       +                                rrname(reqmsg.qd->type, tname, sizeof tname));
       +                }
       +
       +                p = clientrxmit(&reqmsg, buf);
       +                if(p == 0){
       +                        if(debug)
       +                                syslog(0, logfile, "%d: duplicate", req.id);
       +                        goto freereq;
       +                }
       +
       +                /* loop through each question */
       +                while(reqmsg.qd){
       +                        memset(&repmsg, 0, sizeof(repmsg));
       +                        switch(op){
       +                        case Oquery:
       +                                dnserver(&reqmsg, &repmsg, &req);
       +                                break;
       +                        case Onotify:
       +                                dnnotify(&reqmsg, &repmsg, &req);
       +                                break;
       +                        }
       +                        reply(fd, buf, &repmsg, &req);
       +                        rrfreelist(repmsg.qd);
       +                        rrfreelist(repmsg.an);
       +                        rrfreelist(repmsg.ns);
       +                        rrfreelist(repmsg.ar);
       +                }
       +
       +                p->inuse = 0;
       +
       +freereq:
       +                rrfreelist(reqmsg.qd);
       +                rrfreelist(reqmsg.an);
       +                rrfreelist(reqmsg.ns);
       +                rrfreelist(reqmsg.ar);
       +
       +                if(req.isslave){
       +                        putactivity();
       +                        _exits(0);
       +                }
       +
       +        }
       +}
       +
       +/*
       + *  announce on udp port
       + */
       +static int
       +udpannounce(char *mntpt)
       +{
       +        int fd;
       +        char buf[40];
       +        
       +        USED(mntpt);
       +        
       +        if((fd=announce("udp!*!nameserver", buf)) < 0)
       +                warning("can't announce on dns udp port");
       +        return fd;
       +}
       +
       +static void
       +reply(int fd, uchar *buf, DNSmsg *rep, Request *reqp)
       +{
       +        int len;
       +        char tname[32];
       +        RR *rp;
       +
       +        if(debug || (trace && subsume(trace, rep->qd->owner->name)))
       +                syslog(0, logfile, "%d: reply (%I/%d) %d %s %s an %R ns %R ar %R",
       +                        reqp->id, buf, ((buf[4])<<8)+buf[5],
       +                        rep->id, rep->qd->owner->name,
       +                        rrname(rep->qd->type, tname, sizeof tname), rep->an, rep->ns, rep->ar);
       +
       +        len = convDNS2M(rep, &buf[OUdphdrsize], Maxudp);
       +        if(len <= 0){
       +                syslog(0, logfile, "error converting reply: %s %d", rep->qd->owner->name,
       +                        rep->qd->type);
       +                for(rp = rep->an; rp; rp = rp->next)
       +                        syslog(0, logfile, "an %R", rp);
       +                for(rp = rep->ns; rp; rp = rp->next)
       +                        syslog(0, logfile, "ns %R", rp);
       +                for(rp = rep->ar; rp; rp = rp->next)
       +                        syslog(0, logfile, "ar %R", rp);
       +                return;
       +        }
       +        if(udpwrite(fd, (OUdphdr*)buf, buf+OUdphdrsize, len) != len)
       +                syslog(0, logfile, "error sending reply: %r");
       +}
 (DIR) diff --git a/src/cmd/ndb/mkfile b/src/cmd/ndb/mkfile
       t@@ -1,6 +1,7 @@
        <$PLAN9/src/mkhdr
        
        TARG=\
       +#        dns\
                ndbmkdb\
                ndbquery\
                ndbmkhash\
       t@@ -11,3 +12,18 @@ LIB=$PLAN9/lib/libndb.a
        
        <$PLAN9/src/mkmany
        
       +DNSOFILES=\
       +        convDNS2M.$O\
       +        convM2DNS.$O\
       +        dblookup.$O\
       +        dnarea.$O\
       +        dn.$O\
       +        dnresolve.$O\
       +        dnserver.$O\
       +
       +$DNSOFILES dns.$O dnstcp.$O dnsdebug.$O: dns.h
       +
       +$O.dns: $DNSOFILES dnnotify.$O dnudpserver.$O
       +$O.dnstcp: $DNSOFILES
       +$O.dnsdebug: $DNSOFILES
       +