]> git.lizzy.rs Git - plan9front.git/blobdiff - sys/src/cmd/ndb/cs.c
merge
[plan9front.git] / sys / src / cmd / ndb / cs.c
index 30800724735f6dcd88f49612738d4719aea9d317..3fc9d0b3e6c6f70e19ce9a7ee5a7b054252823ac 100644 (file)
@@ -17,6 +17,7 @@ enum
        Maxfdata=               8192,
        Maxhost=                64,             /* maximum host name size */
        Maxservice=             64,             /* maximum service name size */
+       Maxactive=              200,            /* maximum number of active slave procs */
 
        Qdir=                   0,
        Qcs=                    1,
@@ -79,12 +80,13 @@ Job *joblist;
 Mlist  *mlist;
 int    mfd[2];
 int    debug;
-int    paranoia;
-int    ipv6lookups = 1;
+
 jmp_buf        masterjmp;      /* return through here after a slave process has been created */
 int    *isslave;       /* *isslave non-zero means this is a slave process */
+long   active;         /* number of active slaves */
 char   *dbfile;
 Ndb    *db, *netdb;
+char   *csuser;
 
 void   rversion(Job*);
 void   rflush(Job*);
@@ -110,7 +112,6 @@ char        *genquery(Mfile*, char*);
 char*  ipinfoquery(Mfile*, char**, int);
 int    needproto(Network*, Ndbtuple*);
 int    lookup(Mfile*);
-Ndbtuple*      reorder(Ndbtuple*, Ndbtuple*);
 void   ipid(void);
 void   readipinterfaces(void);
 void*  emalloc(int);
@@ -120,13 +121,10 @@ void      freejob(Job*);
 void   setext(char*, int, char*);
 void   cleanmf(Mfile*);
 
-extern void    paralloc(void);
-
 QLock  dblock;         /* mutex on database operations */
 QLock  netlock;        /* mutex for netinit() */
 
 char   *logfile = "cs";
-char   *paranoiafile = "cs.paranoia";
 
 char   mntpt[Maxpath];
 char   netndb[Maxpath];
@@ -134,47 +132,56 @@ char      netndb[Maxpath];
 /*
  *  Network specific translators
  */
-Ndbtuple*      iplookup(Network*, char*, char*, int);
+Ndbtuple*      iplookup(Network*, char*, char*);
 char*          iptrans(Ndbtuple*, Network*, char*, char*, int);
-Ndbtuple*      telcolookup(Network*, char*, char*, int);
+Ndbtuple*      telcolookup(Network*, char*, char*);
 char*          telcotrans(Ndbtuple*, Network*, char*, char*, int);
-Ndbtuple*      dnsiplookup(char*, Ndbs*);
+
+Ndbtuple*      dnsiplookup(char*, Ndbs*, int);
+Ndbtuple*      myipinfo(Ndb *db, char **list, int n);
 
 struct Network
 {
        char            *net;
-       Ndbtuple        *(*lookup)(Network*, char*, char*, int);
+       Ndbtuple        *(*lookup)(Network*, char*, char*);
        char            *(*trans)(Ndbtuple*, Network*, char*, char*, int);
-       int             considered;             /* flag: ignored for "net!"? */
-       int             fasttimeouthack;        /* flag. was for IL */
+
+       char            considered;             /* flag: ignored for "net!"? */
+       char            fasttimeout;            /* flag. was for IL */
+       char            ipvers;                 /* flag: V4, V6 */
+
        Network         *next;
 };
 
-enum
-{
-       Ntcp = 0,
+enum {
+       Ntcp = 1,
+
+       V4 = 1,
+       V6 = 2,
 };
 
 /*
  *  net doesn't apply to (r)udp, icmp(v6), or telco (for speed).
  */
 Network network[] = {
-[Ntcp] { "tcp",        iplookup,       iptrans,        0 },
-       { "udp",        iplookup,       iptrans,        1 },
-       { "icmp",       iplookup,       iptrans,        1 },
-       { "icmpv6",     iplookup,       iptrans,        1 },
-       { "rudp",       iplookup,       iptrans,        1 },
-       { "telco",      telcolookup,    telcotrans,     1 },
+       { "il",         iplookup,       iptrans,        0, 1, V4,       },
+       { "tcp",        iplookup,       iptrans,        0, 0, V4|V6,    },
+       { "il",         iplookup,       iptrans,        0, 0, V4,       },
+       { "udp",        iplookup,       iptrans,        1, 0, V4|V6,    },
+       { "icmp",       iplookup,       iptrans,        1, 0, V4,       },
+       { "icmpv6",     iplookup,       iptrans,        1, 0, V6,       },
+       { "rudp",       iplookup,       iptrans,        1, 0, V4,       },
+       { "ssh",        iplookup,       iptrans,        1, 0, V4|V6,    },
+       { "telco",      telcolookup,    telcotrans,     1, 0, 0,        },
        { 0 },
 };
 
 QLock ipifclock;
 Ipifc *ipifcs;
+int confipvers;
+int lookipvers = V4|V6;
 
-char   eaddr[16];              /* ascii ethernet address */
-char   ipaddr[64];             /* ascii internet address */
-uchar  ipa[IPaddrlen];         /* binary internet address */
-char   *mysysname;
+char *mysysname;
 
 Network *netlist;              /* networks ordered by preference */
 Network *last;
@@ -189,7 +196,7 @@ nstrcpy(char *to, char *from, int len)
 void
 usage(void)
 {
-       fprint(2, "usage: %s [-dn] [-f ndb-file] [-x netmtpt]\n", argv0);
+       fprint(2, "usage: %s [-46dn] [-f ndb-file] [-x netmtpt]\n", argv0);
        exits("usage");
 }
 
@@ -229,7 +236,10 @@ main(int argc, char *argv[])
        ext[0] = 0;
        ARGBEGIN{
        case '4':
-               ipv6lookups = 0;
+               lookipvers = V4;
+               break;
+       case '6':
+               lookipvers = V6;
                break;
        case 'd':
                debug = 1;
@@ -248,12 +258,7 @@ main(int argc, char *argv[])
        USED(argc);
        USED(argv);
 
-       rfork(RFREND|RFNOTEG);
-
-       snprint(servefile, sizeof(servefile), "#s/cs%s", ext);
        snprint(netndb, sizeof(netndb), "%s/ndb", mntpt);
-       unmount(servefile, mntpt);
-       remove(servefile);
 
        fmtinstall('E', eipfmt);
        fmtinstall('I', eipfmt);
@@ -264,6 +269,12 @@ main(int argc, char *argv[])
        netinit(0);
 
        if(!justsetname){
+               snprint(servefile, sizeof(servefile), "#s/cs%s", ext);
+               unmount(servefile, mntpt);
+               remove(servefile);
+
+               rfork(RFREND|RFNOTEG);
+               csuser = estrdup(getuser());
                mountinit(servefile, mntpt);
                io();
        }
@@ -317,14 +328,14 @@ mountinit(char *service, char *mntpt)
                procsetname("%s", mntpt);
                break;
        case -1:
-               error("fork failed\n");
+               error("fork failed");
        default:
                /*
                 *  put ourselves into the file system
                 */
                close(p[0]);
                if(mount(p[1], -1, mntpt, MAFTER, "") < 0)
-                       error("mount failed\n");
+                       error("mount failed");
                _exits(0);
        }
        mfd[0] = mfd[1] = p[0];
@@ -337,7 +348,7 @@ ndbinit(void)
        if(db == nil)
                error("can't open network database");
 
-       for(netdb = db; netdb; netdb = netdb->next)
+       for(netdb = db; netdb != nil; netdb = netdb->next)
                if(strcmp(netdb->file, netndb) == 0)
                        return;
 
@@ -354,13 +365,13 @@ newfid(int fid)
        Mlist *f, *ff;
        Mfile *mf;
 
-       ff = 0;
-       for(f = mlist; f; f = f->next)
+       ff = nil;
+       for(f = mlist; f != nil; f = f->next)
                if(f->mf.busy && f->mf.fid == fid)
                        return &f->mf;
-               else if(!ff && !f->mf.busy && !f->mf.ref)
+               else if(ff == nil && !f->mf.busy && !f->mf.ref)
                        ff = f;
-       if(ff == 0){
+       if(ff == nil){
                ff = emalloc(sizeof *f);
                ff->next = mlist;
                mlist = ff;
@@ -376,7 +387,7 @@ newjob(void)
 {
        Job *job;
 
-       job = mallocz(sizeof(Job), 1);
+       job = emalloc(sizeof *job);
        qlock(&joblock);
        job->next = joblist;
        joblist = job;
@@ -391,7 +402,7 @@ freejob(Job *job)
        Job **l;
 
        qlock(&joblock);
-       for(l = &joblist; *l; l = &(*l)->next){
+       for(l = &joblist; *l != nil; l = &(*l)->next){
                if((*l) == job){
                        *l = job->next;
                        break;
@@ -407,7 +418,7 @@ flushjob(int tag)
        Job *job;
 
        qlock(&joblock);
-       for(job = joblist; job; job = job->next){
+       for(job = joblist; job != nil; job = job->next){
                if(job->request.tag == tag && job->request.type != Tflush){
                        job->flushed = 1;
                        break;
@@ -440,21 +451,22 @@ io(void)
 
        for(;;){
                n = read9pmsg(mfd[0], mdata, sizeof mdata);
-               if(n<=0)
+               if(n < 0)
                        error("mount read");
+               if(n == 0)
+                       break;
                job = newjob();
                if(convM2S(mdata, n, &job->request) != n){
                        syslog(1, logfile, "format error %ux %ux %ux %ux %ux",
                                mdata[0], mdata[1], mdata[2], mdata[3], mdata[4]);
                        freejob(job);
-                       continue;
+                       break;
                }
                qlock(&dblock);
                mf = newfid(job->request.fid);
                if(debug)
                        syslog(0, logfile, "%F", &job->request);
 
-
                switch(job->request.type){
                default:
                        syslog(1, logfile, "unknown request type %d", job->request.type);
@@ -509,6 +521,7 @@ io(void)
                if(*isslave){
                        if(debug)
                                syslog(0, logfile, "slave death %d", getpid());
+                       adec(&active);
                        _exits(0);
                }
        }
@@ -525,7 +538,7 @@ rversion(Job *job)
                sendmsg(job, "unknown 9P version");
        else{
                job->reply.version = "9P2000";
-               sendmsg(job, 0);
+               sendmsg(job, nil);
        }
 }
 
@@ -542,7 +555,7 @@ void
 rflush(Job *job)
 {
        flushjob(job->request.oldtag);
-       sendmsg(job, 0);
+       sendmsg(job, nil);
 }
 
 void
@@ -556,7 +569,7 @@ rattach(Job *job, Mfile *mf)
        mf->qid.type = QTDIR;
        mf->qid.path = 0LL;
        job->reply.qid = mf->qid;
-       sendmsg(job, 0);
+       sendmsg(job, nil);
 }
 
 
@@ -570,7 +583,7 @@ rwalk(Job *job, Mfile *mf)
        Mfile *nmf;
        Qid qid;
 
-       err = 0;
+       err = nil;
        nmf = nil;
        elems = job->request.wname;
        nelems = job->request.nwname;
@@ -622,7 +635,7 @@ rwalk(Job *job, Mfile *mf)
        if(nmf != nil && (err!=nil || job->reply.nwqid<nelems)){
                cleanmf(nmf);
                free(nmf->user);
-               nmf->user = 0;
+               nmf->user = nil;
                nmf->busy = 0;
                nmf->fid = 0;
        }
@@ -638,7 +651,7 @@ ropen(Job *job, Mfile *mf)
        int mode;
        char *err;
 
-       err = 0;
+       err = nil;
        mode = job->request.mode;
        if(mf->qid.type & QTDIR){
                if(mode)
@@ -666,7 +679,7 @@ rread(Job *job, Mfile *mf)
        char *err;
 
        n = 0;
-       err = 0;
+       err = nil;
        off = job->request.offset;
        cnt = job->request.count;
        mf->ref++;
@@ -695,7 +708,7 @@ rread(Job *job, Mfile *mf)
        for(;;){
                /* look for an answer at the right offset */
                toff = 0;
-               for(i = 0; mf->reply[i] && i < mf->nreply; i++){
+               for(i = 0; mf->reply[i] != nil && i < mf->nreply; i++){
                        n = mf->replylen[i];
                        if(off < toff + n)
                                break;
@@ -765,7 +778,7 @@ rwrite(Job *job, Mfile *mf)
        char *field[4];
        char curerr[64];
 
-       err = 0;
+       err = nil;
        cnt = job->request.count;
        if(mf->qid.type & QTDIR){
                err = "can't write directory";
@@ -777,6 +790,9 @@ rwrite(Job *job, Mfile *mf)
        }
        job->request.data[cnt] = 0;
 
+       if(strcmp(mf->user, "none") == 0 || strcmp(mf->user, csuser) != 0)
+               goto query;     /* skip special commands if not owner */
+
        /*
         *  toggle debugging
         */
@@ -787,20 +803,20 @@ rwrite(Job *job, Mfile *mf)
        }
 
        /*
-        *  toggle ipv6 lookups
+        *  toggle ipv4 lookups
         */
-       if(strncmp(job->request.data, "ipv6", 4)==0){
-               ipv6lookups ^= 1;
-               syslog(1, logfile, "ipv6lookups %d", ipv6lookups);
+       if(strncmp(job->request.data, "ipv4", 4)==0){
+               lookipvers ^= V4;
+               syslog(1, logfile, "ipv4lookups %d", (lookipvers & V4) != 0);
                goto send;
        }
 
        /*
-        *  toggle debugging
+        *  toggle ipv6 lookups
         */
-       if(strncmp(job->request.data, "paranoia", 8)==0){
-               paranoia ^= 1;
-               syslog(1, logfile, "paranoia %d", paranoia);
+       if(strncmp(job->request.data, "ipv6", 4)==0){
+               lookipvers ^= V6;
+               syslog(1, logfile, "ipv6lookups %d", (lookipvers & V6) != 0);
                goto send;
        }
 
@@ -823,6 +839,7 @@ rwrite(Job *job, Mfile *mf)
                goto send;
        }
 
+query:
        if(mf->ref){
                err = "query already in progress";
                goto send;
@@ -842,26 +859,24 @@ rwrite(Job *job, Mfile *mf)
 
        if(debug)
                syslog(0, logfile, "write %s", job->request.data);
-       if(paranoia)
-               syslog(0, paranoiafile, "write %s by %s", job->request.data, mf->user);
        /*
         *  break up name
         */
        n = getfields(job->request.data, field, 4, 1, "!");
        switch(n){
        case 1:
-               mf->net = strdup("net");
-               mf->host = strdup(field[0]);
+               mf->net = estrdup("net");
+               mf->host = estrdup(field[0]);
                break;
        case 4:
-               mf->rem = strdup(field[3]);
+               mf->rem = estrdup(field[3]);
                /* fall through */
        case 3:
-               mf->serv = strdup(field[2]);
+               mf->serv = estrdup(field[2]);
                /* fall through */
        case 2:
-               mf->host = strdup(field[1]);
-               mf->net = strdup(field[0]);
+               mf->host = estrdup(field[1]);
+               mf->net = estrdup(field[0]);
                break;
        }
 
@@ -888,10 +903,10 @@ rclunk(Job *job, Mfile *mf)
        if(mf->ref == 0)
                cleanmf(mf);
        free(mf->user);
-       mf->user = 0;
+       mf->user = nil;
        mf->fid = 0;
        mf->busy = 0;
-       sendmsg(job, 0);
+       sendmsg(job, nil);
 }
 
 void
@@ -923,7 +938,7 @@ rstat(Job *job, Mfile *mf)
        dir.atime = dir.mtime = time(0);
        job->reply.nstat = convD2M(&dir, buf, sizeof buf);
        job->reply.stat = buf;
-       sendmsg(job, 0);
+       sendmsg(job, nil);
 }
 
 void
@@ -965,37 +980,30 @@ sendmsg(Job *job, char *err)
 void
 error(char *s)
 {
-       syslog(1, "cs", "%s: %r", s);
+       syslog(1, logfile, "%s: %r", s);
        _exits(0);
 }
 
-static int
-isvalidip(uchar *ip)
-{
-       return ipcmp(ip, IPnoaddr) != 0 && ipcmp(ip, v4prefix) != 0;
-}
-
-static uchar loopbacknet[IPaddrlen] = {
-       0, 0, 0, 0,
-       0, 0, 0, 0,
-       0, 0, 0xff, 0xff,
-       127, 0, 0, 0
-};
-static uchar loopbackmask[IPaddrlen] = {
-       0xff, 0xff, 0xff, 0xff,
-       0xff, 0xff, 0xff, 0xff,
-       0xff, 0xff, 0xff, 0xff,
-       0xff, 0, 0, 0
-};
-
 void
 readipinterfaces(void)
 {
-       if(myipaddr(ipa, mntpt) != 0)
-               ipmove(ipa, IPnoaddr);
-       sprint(ipaddr, "%I", ipa);
-       if (debug)
-               syslog(0, "dns", "ipaddr is %s\n", ipaddr);
+       Ipifc *ifc;
+       Iplifc *lifc;
+       int v;
+
+       v = 0;
+       qlock(&ipifclock);
+       ipifcs = readipifc(mntpt, ipifcs, -1);
+       for(ifc = ipifcs; ifc != nil; ifc = ifc->next){
+               for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next){
+                       if(isv4(lifc->ip))
+                               v |= V4;
+                       else
+                               v |= V6;
+               }
+       }
+       qunlock(&ipifclock);
+       confipvers = v;
 }
 
 /*
@@ -1004,83 +1012,86 @@ readipinterfaces(void)
 void
 ipid(void)
 {
+       char eaddr[16], buf[Maxpath];
        uchar addr[6];
        Ndbtuple *t, *tt;
        char *p, *attr;
        Ndbs s;
-       int f;
-       char buf[Maxpath];
+       int f, n;
+       Dir *d;
 
-       /* use environment, ether addr, or ipaddr to get system name */
-       if(mysysname == 0){
-               /*
-                *  environment has priority.
-                *
-                *  on the sgi power the default system name
-                *  is the ip address.  ignore that.
-                *
-                */
-               p = getenv("sysname");
-               if(p && *p){
-                       attr = ipattr(p);
-                       if(strcmp(attr, "ip") != 0)
-                               mysysname = strdup(p);
-               }
+       if(mysysname != nil)
+               return;
 
-               /*
-                *  the /net/ndb contains what the network
-                *  figured out from DHCP.  use that name if
-                *  there is one.
-                */
-               if(mysysname == 0 && netdb != nil){
-                       ndbreopen(netdb);
-                       for(tt = t = ndbparse(netdb); t != nil; t = t->entry){
-                               if(strcmp(t->attr, "sys") == 0){
-                                       mysysname = strdup(t->val);
-                                       break;
-                               }
-                       }
-                       ndbfree(tt);
+       /*
+        *  environment has priority.
+        *
+        *  on the sgi power the default system name
+        *  is the ip address.  ignore that.
+        *
+        */
+       p = getenv("sysname");
+       if(p != nil && *p != 0){
+               attr = ipattr(p);
+               if(strcmp(attr, "ip") != 0) {
+                       mysysname = p;
+                       goto setsys;
                }
+               free(p);
+       }
 
-               /* next network database, ip address, and ether address to find a name */
-               if(mysysname == 0){
-                       t = nil;
-                       if(isvalidip(ipa))
-                               free(ndbgetvalue(db, &s, "ip", ipaddr, "sys", &t));
-                       if(t == nil){
-                               for(f = 0; f < 3; f++){
-                                       snprint(buf, sizeof buf, "%s/ether%d", mntpt, f);
-                                       if(myetheraddr(addr, buf) >= 0){
-                                               snprint(eaddr, sizeof(eaddr), "%E", addr);
-                                               free(ndbgetvalue(db, &s, "ether", eaddr, "sys", &t));
-                                               if(t != nil)
-                                                       break;
-                                       }
-                               }
-                       }
-                       for(tt = t; tt != nil; tt = tt->entry){
-                               if(strcmp(tt->attr, "sys") == 0){
-                                       mysysname = strdup(tt->val);
-                                       break;
-                               }
+       /* try configured interfaces */
+       attr = "sys";
+       t = s.t = myipinfo(db, &attr, 1);
+       if(t != nil)
+               goto found;
+
+       /* try ethernet interfaces */
+       n = 0;
+       d = nil;
+       f = open(mntpt, OREAD);
+       if(f >= 0){
+               n = dirreadall(f, &d);
+               close(f);
+       }
+       for(f = 0; f < n; f++){
+               if((d[f].mode & DMDIR) == 0 || strncmp(d[f].name, "ether", 5) != 0)
+                       continue;
+               snprint(buf, sizeof buf, "%s/%s", mntpt, d[f].name);
+               if(myetheraddr(addr, buf) >= 0){
+                       snprint(eaddr, sizeof(eaddr), "%E", addr);
+                       free(ndbgetvalue(db, &s, "ether", eaddr, "sys", &t));
+                       if(t != nil){
+                               free(d);
+                               goto found;
                        }
-                       ndbfree(t);
                }
+       }
+       free(d);
 
-               /* nothing else worked, use the ip address */
-               if(mysysname == 0 && isvalidip(ipa))
-                       mysysname = strdup(ipaddr);
+       /* nothing else worked, use ip address */
+       attr = "ip";
+       t = s.t = myipinfo(db, &attr, 1);
+       if(t == nil)
+               return;
+       
+found:
+       /* found in database */
+       if((tt = ndbfindattr(t, s.t, "sys")) != nil)
+               mysysname = estrdup(tt->val);
+       else if((tt = ndbfindattr(t, s.t, "ip")) != nil)
+               mysysname = estrdup(tt->val);
+       ndbfree(t);
 
+       if(mysysname == nil)
+               return;
 
-               /* set /dev/sysname if we now know it */
-               if(mysysname){
-                       f = open("/dev/sysname", OWRITE);
-                       if(f >= 0){
-                               write(f, mysysname, strlen(mysysname));
-                               close(f);
-                       }
-               }
+setsys:
+       /* set /dev/sysname if we now know it */
+       f = open("/dev/sysname", OWRITE);
+       if(f >= 0){
+               write(f, mysysname, strlen(mysysname));
+               close(f);
        }
 }
 
@@ -1093,43 +1104,37 @@ netinit(int background)
 {
        char clone[Maxpath];
        Network *np;
-       static int working;
 
        if(background){
-               switch(rfork(RFPROC|RFNOTEG|RFMEM|RFNOWAIT)){
-               case 0:
-                       break;
-               default:
+               if(rfork(RFPROC|RFNOTEG|RFMEM|RFNOWAIT) != 0)
                        return;
-               }
                qlock(&netlock);
        }
 
        /* add the mounted networks to the default list */
-       for(np = network; np->net; np++){
+       for(np = network; np->net != nil; np++){
                if(np->considered)
                        continue;
                snprint(clone, sizeof(clone), "%s/%s/clone", mntpt, np->net);
                if(access(clone, AEXIST) < 0)
                        continue;
-               if(netlist)
+               if(netlist != nil)
                        last->next = np;
                else
                        netlist = np;
                last = np;
-               np->next = 0;
+               np->next = nil;
                np->considered = 1;
        }
 
-       /* find out what our ip address is */
+       /* find out what our ip addresses are */
        readipinterfaces();
 
        /* set the system name if we need to, these days ip is all we have */
        ipid();
 
        if(debug)
-               syslog(0, logfile, "mysysname %s eaddr %s ipaddr %s ipa %I\n",
-                       mysysname?mysysname:"???", eaddr, ipaddr, ipa);
+               syslog(0, logfile, "mysysname %s", mysysname?mysysname:"???");
 
        if(background){
                qunlock(&netlock);
@@ -1149,17 +1154,17 @@ netadd(char *p)
 
        n = getfields(p, field, 12, 1, " ");
        for(i = 0; i < n; i++){
-               for(np = network; np->net; np++){
+               for(np = network; np->net != nil; np++){
                        if(strcmp(field[i], np->net) != 0)
                                continue;
                        if(np->considered)
                                break;
-                       if(netlist)
+                       if(netlist != nil)
                                last->next = np;
                        else
                                netlist = np;
                        last = np;
-                       np->next = 0;
+                       np->next = nil;
                        np->considered = 1;
                }
        }
@@ -1185,32 +1190,30 @@ lookup(Mfile *mf)
        char *cp;
        Ndbtuple *nt, *t;
        char reply[Maxreply];
-       int i, rv;
-       int hack;
+       int i, rv, fasttimeout;
 
        /* open up the standard db files */
-       if(db == 0)
+       if(db == nil)
                ndbinit();
-       if(db == 0)
+       if(db == nil)
                error("can't open mf->network database\n");
 
-       rv = 0;
-
        if(mf->net == nil)
                return 0;       /* must have been a genquery */
 
+       rv = 0;
        if(strcmp(mf->net, "net") == 0){
                /*
                 *  go through set of default nets
                 */
-               for(np = mf->nextnet; np; np = np->next){
-                       nt = (*np->lookup)(np, mf->host, mf->serv, 1);
+               for(np = mf->nextnet; np != nil && rv == 0; np = np->next){
+                       nt = (*np->lookup)(np, mf->host, mf->serv);
                        if(nt == nil)
                                continue;
-                       hack = np->fasttimeouthack && !lookforproto(nt, np->net);
-                       for(t = nt; mf->nreply < Nreply && t; t = t->entry){
-                               cp = (*np->trans)(t, np, mf->serv, mf->rem, hack);
-                               if(cp){
+                       fasttimeout = np->fasttimeout && !lookforproto(nt, np->net);
+                       for(t = nt; mf->nreply < Nreply && t != nil; t = t->entry){
+                               cp = (*np->trans)(t, np, mf->serv, mf->rem, fasttimeout);
+                               if(cp != nil){
                                        /* avoid duplicates */
                                        for(i = 0; i < mf->nreply; i++)
                                                if(strcmp(mf->reply[i], cp) == 0)
@@ -1220,12 +1223,11 @@ lookup(Mfile *mf)
                                                mf->replylen[mf->nreply] = strlen(cp);
                                                mf->reply[mf->nreply++] = cp;
                                                rv++;
-                                       }
+                                       } else
+                                               free(cp);
                                }
                        }
                        ndbfree(nt);
-                       np = np->next;
-                       break;
                }
                mf->nextnet = np;
                return rv;
@@ -1236,24 +1238,25 @@ lookup(Mfile *mf)
         */
        if(mf->nreply != 0)
                return 0;
+
        /*
         *  look for a specific network
         */
-       for(np = netlist; np && np->net != nil; np++){
-               if(np->fasttimeouthack)
+       for(np = network; np->net != nil; np++){
+               if(np->fasttimeout)
                        continue;
                if(strcmp(np->net, mf->net) == 0)
                        break;
        }
 
-       if(np && np->net != nil){
+       if(np->net != nil){
                /*
                 *  known network
                 */
-               nt = (*np->lookup)(np, mf->host, mf->serv, 1);
-               for(t = nt; mf->nreply < Nreply && t; t = t->entry){
+               nt = (*np->lookup)(np, mf->host, mf->serv);
+               for(t = nt; mf->nreply < Nreply && t != nil; t = t->entry){
                        cp = (*np->trans)(t, np, mf->serv, mf->rem, 0);
-                       if(cp){
+                       if(cp != nil){
                                mf->replylen[mf->nreply] = strlen(cp);
                                mf->reply[mf->nreply++] = cp;
                                rv++;
@@ -1265,13 +1268,13 @@ lookup(Mfile *mf)
                /*
                 *  not a known network, don't translate host or service
                 */
-               if(mf->serv)
+               if(mf->serv != nil)
                        snprint(reply, sizeof(reply), "%s/%s/clone %s!%s",
                                mntpt, mf->net, mf->host, mf->serv);
                else
                        snprint(reply, sizeof(reply), "%s/%s/clone %s",
                                mntpt, mf->net, mf->host);
-               mf->reply[0] = strdup(reply);
+               mf->reply[0] = estrdup(reply);
                mf->replylen[0] = strlen(reply);
                mf->nreply = 1;
                return 1;
@@ -1290,32 +1293,30 @@ ipserv(Network *np, char *name, char *buf, int blen)
        char *p;
        int alpha = 0;
        int restr = 0;
-       char port[10];
        Ndbtuple *t, *nt;
        Ndbs s;
 
        /* '*' means any service */
-       if(strcmp(name, "*")==0){
+       if(strcmp(name, "*") == 0){
                nstrcpy(buf, name, blen);
                return buf;
        }
 
        /*  see if it's numeric or symbolic */
-       port[0] = 0;
        for(p = name; *p; p++){
                if(isdigit(*p))
                        {}
                else if(isalpha(*p) || *p == '-' || *p == '$')
                        alpha = 1;
                else
-                       return 0;
+                       return nil;
        }
        t = nil;
        p = nil;
        if(alpha){
                p = ndbgetvalue(db, &s, np->net, name, "port", &t);
                if(p == nil)
-                       return 0;
+                       return nil;
        } else {
                /* look up only for tcp ports < 1024 to get the restricted
                 * attribute
@@ -1323,11 +1324,11 @@ ipserv(Network *np, char *name, char *buf, int blen)
                if(atoi(name) < 1024 && strcmp(np->net, "tcp") == 0)
                        p = ndbgetvalue(db, &s, "port", name, "port", &t);
                if(p == nil)
-                       p = strdup(name);
+                       p = estrdup(name);
        }
 
        if(t){
-               for(nt = t; nt; nt = nt->entry)
+               for(nt = t; nt != nil; nt = nt->entry)
                        if(strcmp(nt->attr, "restricted") == 0)
                                restr = 1;
                ndbfree(t);
@@ -1338,173 +1339,213 @@ ipserv(Network *np, char *name, char *buf, int blen)
        return buf;
 }
 
-/*
- *  lookup an ip attribute
- */
-int
-ipattrlookup(Ndb *db, char *ipa, char *attr, char *val, int vlen)
+Ndbtuple*
+myipinfo(Ndb *db, char **list, int n)
 {
-
        Ndbtuple *t, *nt;
-       char *alist[2];
+       char ip[64];
+       Ipifc *ifc;
+       Iplifc *lifc;
 
-       alist[0] = attr;
-       t = ndbipinfo(db, "ip", ipa, alist, 1);
-       if(t == nil)
-               return 0;
-       for(nt = t; nt != nil; nt = nt->entry){
-               if(strcmp(nt->attr, attr) == 0){
-                       nstrcpy(val, nt->val, vlen);
-                       ndbfree(t);
-                       return 1;
+       t = nil;
+       qlock(&ipifclock);
+       for(ifc = ipifcs; ifc != nil; ifc = ifc->next){
+               for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next){
+                       snprint(ip, sizeof(ip), "%I", lifc->ip);
+                       nt = ndbipinfo(db, "ip", ip, list, n);
+                       t = ndbconcatenate(t, nt);
                }
        }
+       qunlock(&ipifclock);
 
-       /* we shouldn't get here */
-       ndbfree(t);
-       return 0;
+       return t;
 }
 
 /*
- *  lookup (and translate) an ip destination
+ * reorder according to our interfaces
  */
-Ndbtuple*
-iplookup(Network *np, char *host, char *serv, int nolookup)
+static Ndbtuple*
+ipreorder(Ndbtuple *t)
 {
-       char *attr, *dnsname;
-       Ndbtuple *t, *nt;
-       Ndbs s;
-       char ts[Maxservice];
-       char dollar[Maxhost];
+       Ndbtuple *nt;
        uchar ip[IPaddrlen];
        uchar net[IPaddrlen];
        uchar tnet[IPaddrlen];
        Ipifc *ifc;
        Iplifc *lifc;
 
-       USED(nolookup);
+       if(t == nil)
+               return nil;
 
-       /*
-        *  start with the service since it's the most likely to fail
-        *  and costs the least
-        */
-       werrstr("can't translate address");
-       if(serv==0 || ipserv(np, serv, ts, sizeof ts) == 0){
-               werrstr("can't translate service");
-               return 0;
+       qlock(&ipifclock);
+       for(ifc = ipifcs; ifc != nil; ifc = ifc->next){
+               for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next){
+                       maskip(lifc->ip, lifc->mask, net);
+                       for(nt = t; nt != nil; nt = nt->entry){
+                               if(strcmp(nt->attr, "ip") != 0)
+                                       continue;
+                               if(parseip(ip, nt->val) == -1)
+                                       continue;
+                               maskip(ip, lifc->mask, tnet);
+                               if(memcmp(net, tnet, IPaddrlen) == 0){
+                                       qunlock(&ipifclock);
+                                       return ndbreorder(t, nt);
+                               }
+                       }
+               }
        }
+       qunlock(&ipifclock);
 
-       /* for dial strings with no host */
-       if(strcmp(host, "*") == 0)
-               return ndbnew("ip", "*");
-
-       /*
-        *  hack till we go v6 :: = 0.0.0.0
-        */
-       if(strcmp("::", host) == 0)
-               return ndbnew("ip", "*");
+       return t;
+}
 
+static Ndbtuple*
+ndbline(Ndbtuple *t)
+{
+       Ndbtuple *nt;
 
-       /*
-        *  '$' means the rest of the name is an attribute that we
-        *  need to search for
-        */
-       if(*host == '$'){
-               if(ipattrlookup(db, ipaddr, host+1, dollar, sizeof dollar))
-                       host = dollar;
+       for(nt = t; nt != nil; nt = nt->entry){
+               if(nt->entry == nil)
+                       nt->line = t;
+               else
+                       nt->line = nt->entry;
        }
+       return t;
+}
+
+/*
+ *  lookup an ip destination
+ */
+static Ndbtuple*
+iplookuphost(Network *np, char *host)
+{
+       char *attr, *dnsname;
+       Ndbtuple *t, *nt;
+       Ndbs s;
 
        /*
         *  turn '[ip address]' into just 'ip address'
         */
        if(*host == '['){
-               char *x;
+               char tmp[Maxhost], *x;
 
-               if(host != dollar){
-                       nstrcpy(dollar, host, sizeof dollar);
-                       host = dollar;
-               }
-               if(x = strchr(++host, ']'))
+               nstrcpy(tmp, host, sizeof tmp);
+               host = tmp;
+               if((x = strchr(++host, ']')) != nil)
                        *x = 0;
        }
 
+       /* for dial strings with no host */
+       if(strcmp(host, "*") == 0)
+               return ndbline(ndbnew("ip", "*"));
+
+       /*
+        *  hack till we go v6 :: = 0.0.0.0
+        */
+       if(strcmp("::", host) == 0)
+               return ndbline(ndbnew("ip", "*"));
+
        /*
         *  just accept addresses
         */
        attr = ipattr(host);
        if(strcmp(attr, "ip") == 0)
-               return ndbnew("ip", host);
+               return ndbline(ndbnew("ip", host));
 
        /*
         *  give the domain name server the first opportunity to
         *  resolve domain names.  if that fails try the database.
         */
-       t = 0;
-       werrstr("can't translate address");
+       t = nil;
        if(strcmp(attr, "dom") == 0)
-               t = dnsiplookup(host, &s);
-       if(t == 0)
-               free(ndbgetvalue(db, &s, attr, host, "ip", &t));
-       if(t == 0){
-               dnsname = ndbgetvalue(db, &s, attr, host, "dom", nil);
-               if(dnsname){
-                       t = dnsiplookup(dnsname, &s);
-                       free(dnsname);
+               t = dnsiplookup(host, &s, np->ipvers);
+       if(t == nil){
+               for(nt = ndbsearch(db, &s, attr, host); nt != nil; nt = ndbsnext(&s, attr, host)){
+                       if(ndbfindattr(nt, s.t, "ip") == nil){
+                               ndbfree(nt);
+                               continue;
+                       }
+                       t = ndbconcatenate(t, ndbreorder(nt, s.t));
                }
+               s.t = t;
        }
-       if(t == 0)
-               t = dnsiplookup(host, &s);
-       if(t == 0)
-               return 0;
+       if(t == nil){
+               if(strcmp(attr, "dom") != 0){
+                       dnsname = ndbgetvalue(db, &s, attr, host, "dom", nil);
+                       if(dnsname != nil){
+                               t = dnsiplookup(dnsname, &s, np->ipvers);
+                               free(dnsname);
+                       }
+               }
+               if(t == nil)
+                       t = dnsiplookup(host, &s, np->ipvers);
+       }
+       if(t == nil)
+               return nil;
 
        /*
         *  reorder the tuple to have the matched line first and
         *  save that in the request structure.
         */
-       t = reorder(t, s.t);
+       return ndbreorder(t, s.t);
+}
+
+
+Ndbtuple*
+iplookup(Network *np, char *host, char *serv)
+{
+       Ndbtuple *l, *t, *nt;
+       char ts[Maxservice], *attr;
 
        /*
-        * reorder according to our interfaces
+        *  start with the service since it's the most likely to fail
+        *  and costs the least
         */
-       qlock(&ipifclock);
-       for(ifc = ipifcs; ifc != nil; ifc = ifc->next){
-               for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next){
-                       maskip(lifc->ip, lifc->mask, net);
-                       for(nt = t; nt; nt = nt->entry){
-                               if(strcmp(nt->attr, "ip") != 0)
-                                       continue;
-                               parseip(ip, nt->val);
-                               maskip(ip, lifc->mask, tnet);
-                               if(memcmp(net, tnet, IPaddrlen) == 0){
-                                       t = reorder(t, nt);
-                                       qunlock(&ipifclock);
-                                       return t;
-                               }
-                       }
-               }
+       if(serv == nil || ipserv(np, serv, ts, sizeof ts) == nil){
+               werrstr("can't translate service");
+               return nil;
        }
-       qunlock(&ipifclock);
 
-       return t;
+       /*
+        *  '$' means the rest of the name is an attribute that we
+        *  need to search for
+        */
+       werrstr("can't translate address");
+       if(*host == '$'){
+               t = nil;
+               attr = host+1;
+               l = myipinfo(db, &attr, 1);
+               for(nt = l; nt != nil; nt = nt->entry){
+                       if(strcmp(nt->attr, attr) == 0)
+                               t = ndbconcatenate(t, iplookuphost(np, nt->val));
+               }
+               ndbfree(l);
+       } else
+               t = iplookuphost(np, host);
+
+       return ipreorder(t);
 }
 
+
 /*
  *  translate an ip address
  */
 char*
-iptrans(Ndbtuple *t, Network *np, char *serv, char *rem, int hack)
+iptrans(Ndbtuple *t, Network *np, char *serv, char *rem, int fasttimeout)
 {
        char ts[Maxservice];
        char reply[Maxreply];
        char x[Maxservice];
+       uchar ip[IPaddrlen];
 
        if(strcmp(t->attr, "ip") != 0)
-               return 0;
+               return nil;
 
-       if(serv == 0 || ipserv(np, serv, ts, sizeof ts) == 0){
+       if(serv == nil || ipserv(np, serv, ts, sizeof ts) == nil){
                werrstr("can't translate service");
-               return 0;
+               return nil;
        }
+
        if(rem != nil)
                snprint(x, sizeof(x), "!%s", rem);
        else
@@ -1513,30 +1554,35 @@ iptrans(Ndbtuple *t, Network *np, char *serv, char *rem, int hack)
        if(*t->val == '*')
                snprint(reply, sizeof(reply), "%s/%s/clone %s%s",
                        mntpt, np->net, ts, x);
-       else
-               snprint(reply, sizeof(reply), "%s/%s/clone %s!%s%s%s",
-                       mntpt, np->net, t->val, ts, x, hack? "!fasttimeout": "");
+       else {
+               if(parseip(ip, t->val) == -1)
+                       return nil;
+               if((np->ipvers & confipvers & (isv4(ip) ? V4 : V6)) == 0)
+                       return nil;
+               snprint(reply, sizeof(reply), "%s/%s/clone %I!%s%s%s",
+                       mntpt, np->net, ip, ts, x, fasttimeout? "!fasttimeout": "");
+       }
 
-       return strdup(reply);
+       return estrdup(reply);
 }
 
 /*
  *  lookup a telephone number
  */
 Ndbtuple*
-telcolookup(Network *np, char *host, char *serv, int nolookup)
+telcolookup(Network *np, char *host, char *serv)
 {
        Ndbtuple *t;
        Ndbs s;
 
-       USED(np, nolookup, serv);
+       USED(np, serv);
 
        werrstr("can't translate address");
        free(ndbgetvalue(db, &s, "sys", host, "telco", &t));
-       if(t == 0)
-               return ndbnew("telco", host);
+       if(t == nil)
+               return ndbline(ndbnew("telco", host));
 
-       return reorder(t, s.t);
+       return ndbreorder(t, s.t);
 }
 
 /*
@@ -1549,47 +1595,19 @@ telcotrans(Ndbtuple *t, Network *np, char *serv, char *rem, int)
        char x[Maxservice];
 
        if(strcmp(t->attr, "telco") != 0)
-               return 0;
+               return nil;
 
        if(rem != nil)
                snprint(x, sizeof(x), "!%s", rem);
        else
                *x = 0;
-       if(serv)
+       if(serv != nil)
                snprint(reply, sizeof(reply), "%s/%s/clone %s!%s%s", mntpt, np->net,
                        t->val, serv, x);
        else
                snprint(reply, sizeof(reply), "%s/%s/clone %s%s", mntpt, np->net,
                        t->val, x);
-       return strdup(reply);
-}
-
-/*
- *  reorder the tuple to put x's line first in the entry
- */
-Ndbtuple*
-reorder(Ndbtuple *t, Ndbtuple *x)
-{
-       Ndbtuple *nt;
-       Ndbtuple *line;
-
-       /* find start of this entry's line */
-       for(line = x; line->entry == line->line; line = line->line)
-               ;
-       line = line->line;
-       if(line == t)
-               return t;       /* already the first line */
-
-       /* remove this line and everything after it from the entry */
-       for(nt = t; nt->entry != line; nt = nt->entry)
-               ;
-       nt->entry = 0;
-
-       /* make that the start of the entry */
-       for(nt = line; nt->entry; nt = nt->entry)
-               ;
-       nt->entry = t;
-       return line;
+       return estrdup(reply);
 }
 
 /*
@@ -1601,15 +1619,19 @@ slave(char *host)
 {
        if(*isslave)
                return;         /* we're already a slave process */
-
+       if(ainc(&active) >= Maxactive){
+               adec(&active);
+               return;
+       }
        switch(rfork(RFPROC|RFNOTEG|RFMEM|RFNOWAIT)){
        case -1:
+               adec(&active);
                break;
        case 0:
+               *isslave = 1;
                if(debug)
                        syslog(0, logfile, "slave %d", getpid());
                procsetname("%s", host);
-               *isslave = 1;
                break;
        default:
                longjmp(masterjmp, 1);
@@ -1617,6 +1639,43 @@ slave(char *host)
 
 }
 
+static int
+mountdns(void)
+{
+       static QLock mountlock;
+       static int mounted;
+       char buf[128], *p;
+       int fd;
+
+       if(mounted)
+               return 0;
+
+       qlock(&mountlock);
+       snprint(buf, sizeof(buf), "%s/dns", mntpt);
+       if(access(buf, AEXIST) == 0)
+               goto done;
+       if(strcmp(mntpt, "/net") == 0)
+               snprint(buf, sizeof(buf), "/srv/dns");
+       else {
+               snprint(buf, sizeof(buf), "/srv/dns%s", mntpt);
+               while((p = strchr(buf+8, '/')) != nil)
+                       *p = '_';
+       }
+       if((fd = open(buf, ORDWR)) < 0){
+err:
+               qunlock(&mountlock);
+               return -1;      
+       }
+       if(mount(fd, -1, mntpt, MAFTER, "") < 0){
+               close(fd);
+               goto err;
+       }
+done:
+       mounted = 1;
+       qunlock(&mountlock);
+       return 0;
+}
+
 static Ndbtuple*
 dnsip6lookup(char *mntpt, char *buf, Ndbtuple *t)
 {
@@ -1631,43 +1690,53 @@ dnsip6lookup(char *mntpt, char *buf, Ndbtuple *t)
                if (strcmp(tt->attr, "ipv6") == 0)
                        strcpy(tt->attr, "ip");
 
-       if (t == nil)
-               return t6;
-
        /* append t6 list to t list */
-       for (tt = t; tt->entry != nil; tt = tt->entry)
-               ;
-       tt->entry = t6;
-       return t;
+       return ndbconcatenate(t, t6);
 }
 
 /*
  *  call the dns process and have it try to translate a name
  */
 Ndbtuple*
-dnsiplookup(char *host, Ndbs *s)
+dnsiplookup(char *host, Ndbs *s, int ipvers)
 {
        char buf[Maxreply];
        Ndbtuple *t;
 
+       ipvers &= confipvers & lookipvers;
+       if(ipvers == 0){
+               werrstr("no ip address");
+               return nil;
+       }
        qunlock(&dblock);
        slave(host);
+       if(*isslave == 0){
+               qlock(&dblock);
+               werrstr("too much activity");
+               return nil;
+       }
+
+       if(mountdns() < 0){
+               qlock(&dblock);
+               return nil;
+       }
 
        if(strcmp(ipattr(host), "ip") == 0)
                t = dnsquery(mntpt, host, "ptr");
        else {
-               t = dnsquery(mntpt, host, "ip");
-               /* special case: query ipv6 (AAAA dns RR) too */
-               if (ipv6lookups)
+               t = nil;
+               if(ipvers & V4)
+                       t = dnsquery(mntpt, host, "ip");
+               if(ipvers & V6)
                        t = dnsip6lookup(mntpt, host, t);
        }
        s->t = t;
 
        if(t == nil){
                rerrstr(buf, sizeof buf);
-               if(strstr(buf, "exist"))
+               if(strstr(buf, "exist") != nil)
                        werrstr("can't translate address: %s", buf);
-               else if(strstr(buf, "dns failure"))
+               else if(strstr(buf, "dns failure") != nil)
                        werrstr("temporary problem: %s", buf);
        }
 
@@ -1683,7 +1752,7 @@ qmatch(Ndbtuple *t, char **attr, char **val, int n)
 
        for(i = 1; i < n; i++){
                found = 0;
-               for(nt = t; nt; nt = nt->entry)
+               for(nt = t; nt != nil; nt = nt->entry)
                        if(strcmp(attr[i], nt->attr) == 0)
                                if(strcmp(val[i], "*") == 0
                                || strcmp(val[i], nt->val) == 0){
@@ -1703,14 +1772,14 @@ qreply(Mfile *mf, Ndbtuple *t)
        String *s;
 
        s = s_new();
-       for(nt = t; mf->nreply < Nreply && nt; nt = nt->entry){
+       for(nt = t; mf->nreply < Nreply && nt != nil; nt = nt->entry){
                s_append(s, nt->attr);
                s_append(s, "=");
                s_append(s, nt->val);
 
                if(nt->line != nt->entry){
                        mf->replylen[mf->nreply] = s_len(s);
-                       mf->reply[mf->nreply++] = strdup(s_to_c(s));
+                       mf->reply[mf->nreply++] = estrdup(s_to_c(s));
                        s_restart(s);
                } else
                        s_append(s, " ");
@@ -1756,7 +1825,7 @@ genquery(Mfile *mf, char *query)
        /* parse pairs */
        for(i = 0; i < n; i++){
                p = strchr(attr[i], '=');
-               if(p == 0)
+               if(p == nil)
                        return "bad query";
                *p++ = 0;
                val[i] = p;
@@ -1764,12 +1833,12 @@ genquery(Mfile *mf, char *query)
 
        /* give dns a chance */
        if((strcmp(attr[0], "dom") == 0 || strcmp(attr[0], "ip") == 0) && val[0]){
-               t = dnsiplookup(val[0], &s);
-               if(t){
+               t = dnsiplookup(val[0], &s, lookipvers);
+               if(t != nil){
                        if(qmatch(t, attr, val, n)){
                                qreply(mf, t);
                                ndbfree(t);
-                               return 0;
+                               return nil;
                        }
                        ndbfree(t);
                }
@@ -1779,11 +1848,11 @@ genquery(Mfile *mf, char *query)
        t = ndbsearch(db, &s, attr[0], val[0]);
 
        /* search is the and of all the pairs */
-       while(t){
+       while(t != nil){
                if(qmatch(t, attr, val, n)){
                        qreply(mf, t);
                        ndbfree(t);
-                       return 0;
+                       return nil;
                }
 
                ndbfree(t);
@@ -1801,7 +1870,7 @@ ipresolve(char *attr, char *host)
 {
        Ndbtuple *t, *nt, **l;
 
-       t = iplookup(&network[Ntcp], host, "*", 0);
+       t = iplookup(&network[Ntcp], host, "*");
        for(l = &t; *l != nil; ){
                nt = *l;
                if(strcmp(nt->attr, "ip") != 0){
@@ -1816,11 +1885,34 @@ ipresolve(char *attr, char *host)
        return t;
 }
 
+/*
+ *  remove duplicates
+ */
+static Ndbtuple*
+ndbdedup(Ndbtuple *t)
+{
+       Ndbtuple *tt, *nt, **l;
+
+       for(nt = t; nt != nil; nt = nt->entry){
+               for(l = &nt->entry; (tt = *l) != nil;){
+                       if(strcmp(nt->attr, tt->attr) != 0
+                       || strcmp(nt->val, tt->val) != 0){
+                               l = &tt->entry;
+                               continue;
+                       }
+                       *l = tt->entry;
+                       tt->entry = nil;
+                       ndbfree(tt);
+               }
+       }
+       return t;
+}
+
 char*
 ipinfoquery(Mfile *mf, char **list, int n)
 {
        int i, nresolve;
-       int resolve[Maxattr];
+       uchar resolve[Maxattr];
        Ndbtuple *t, *nt, **l;
        char *attr, *val;
 
@@ -1830,20 +1922,20 @@ ipinfoquery(Mfile *mf, char **list, int n)
        if(n < 1)
                return "bad query";
 
-       /* get search attribute=value, or assume ip=myipaddr */
+       /* get search attribute=value, or assume myip */
        attr = *list;
        if((val = strchr(attr, '=')) != nil){
                *val++ = 0;
                list++;
                n--;
        }else{
-               attr = "ip";
-               val = ipaddr;
+               attr = nil;
+               val = nil;
        }
-
        if(n < 1)
                return "bad query";
 
+
        /*
         *  don't let ndbipinfo resolve the addresses, we're
         *  better at it.
@@ -1857,11 +1949,16 @@ ipinfoquery(Mfile *mf, char **list, int n)
                } else
                        resolve[i] = 0;
 
-       t = ndbipinfo(db, attr, val, list, n);
+       if(attr == nil)
+               t = myipinfo(db, list, n);
+       else
+               t = ndbipinfo(db, attr, val, list, n);
+
        if(t == nil)
                return "no match";
 
        if(nresolve != 0){
+               t = ndbdedup(t);
                for(l = &t; *l != nil;){
                        nt = *l;
 
@@ -1890,14 +1987,10 @@ ipinfoquery(Mfile *mf, char **list, int n)
                        ndbfree(nt);
                }
        }
+       t = ndbdedup(t);
 
        /* make it all one line */
-       for(nt = t; nt != nil; nt = nt->entry){
-               if(nt->entry == nil)
-                       nt->line = t;
-               else
-                       nt->line = nt->entry;
-       }
+       t = ndbline(t);
 
        qreply(mf, t);
 
@@ -1911,7 +2004,7 @@ emalloc(int size)
 
        x = malloc(size);
        if(x == nil)
-               abort();
+               error("out of memory");
        memset(x, 0, size);
        return x;
 }
@@ -1922,10 +2015,11 @@ estrdup(char *s)
        int size;
        char *p;
 
-       size = strlen(s)+1;
-       p = malloc(size);
+       size = strlen(s);
+       p = malloc(size+1);
        if(p == nil)
-               abort();
+               error("out of memory");
        memmove(p, s, size);
+       p[size] = 0;
        return p;
 }