]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/ndb/cs.c
ip/ipconfig: default onlink and autoflag to 1
[plan9front.git] / sys / src / cmd / ndb / cs.c
1 #include <u.h>
2 #include <libc.h>
3 #include <auth.h>
4 #include <fcall.h>
5 #include <bio.h>
6 #include <ctype.h>
7 #include <ndb.h>
8 #include <ip.h>
9 #include <String.h>
10
11 enum
12 {
13         Nreply=                 20,
14         Maxreply=               256,
15         Maxrequest=             128,
16         Maxpath=                128,
17         Maxfdata=               8192,
18         Maxhost=                64,             /* maximum host name size */
19         Maxservice=             64,             /* maximum service name size */
20         Maxactive=              200,            /* maximum number of active slave procs */
21
22         Qdir=                   0,
23         Qcs=                    1,
24 };
25
26 typedef struct Mfile    Mfile;
27 typedef struct Mlist    Mlist;
28 typedef struct Network  Network;
29 typedef struct Flushreq Flushreq;
30 typedef struct Job      Job;
31
32 int vers;               /* incremented each clone/attach */
33
34 struct Mfile
35 {
36         int             busy;   /* fid in use */
37         int             ref;    /* cleanup when drops to zero */
38
39         char            *user;
40         Qid             qid;
41         int             fid;
42
43         /*
44          *  current request
45          */
46         char            *net;
47         char            *host;
48         char            *serv;
49         char            *rem;
50
51         /*
52          *  result of the last lookup
53          */
54         Network         *nextnet;
55         int             nreply;
56         char            *reply[Nreply];
57         int             replylen[Nreply];
58 };
59
60 struct Mlist
61 {
62         Mlist   *next;
63         Mfile   mf;
64 };
65
66
67 /*
68  *  active requests
69  */
70 struct Job
71 {
72         Job     *next;
73         int     flushed;
74         Fcall   request;
75         Fcall   reply;
76 };
77 QLock   joblock;
78 Job     *joblist;
79
80 Mlist   *mlist;
81 int     mfd[2];
82 int     debug;
83
84 jmp_buf masterjmp;      /* return through here after a slave process has been created */
85 int     *isslave;       /* *isslave non-zero means this is a slave process */
86 long    active;         /* number of active slaves */
87 char    *dbfile;
88 Ndb     *db, *netdb;
89 char    *csuser;
90
91 void    rversion(Job*);
92 void    rflush(Job*);
93 void    rattach(Job*, Mfile*);
94 char*   rwalk(Job*, Mfile*);
95 void    ropen(Job*, Mfile*);
96 void    rcreate(Job*, Mfile*);
97 void    rread(Job*, Mfile*);
98 void    rwrite(Job*, Mfile*);
99 void    rclunk(Job*, Mfile*);
100 void    rremove(Job*, Mfile*);
101 void    rstat(Job*, Mfile*);
102 void    rwstat(Job*, Mfile*);
103 void    rauth(Job*);
104 void    sendmsg(Job*, char*);
105 void    error(char*);
106 void    mountinit(char*, char*);
107 void    io(void);
108 void    ndbinit(void);
109 void    netinit(int);
110 void    netadd(char*);
111 char    *genquery(Mfile*, char*);
112 char*   ipinfoquery(Mfile*, char**, int);
113 int     needproto(Network*, Ndbtuple*);
114 int     lookup(Mfile*);
115 void    ipid(void);
116 void    readipinterfaces(void);
117 void*   emalloc(int);
118 char*   estrdup(char*);
119 Job*    newjob(void);
120 void    freejob(Job*);
121 void    setext(char*, int, char*);
122 void    cleanmf(Mfile*);
123
124 QLock   dblock;         /* mutex on database operations */
125 QLock   netlock;        /* mutex for netinit() */
126
127 char    *logfile = "cs";
128
129 char    mntpt[Maxpath];
130 char    netndb[Maxpath];
131
132 /*
133  *  Network specific translators
134  */
135 Ndbtuple*       iplookup(Network*, char*, char*);
136 char*           iptrans(Ndbtuple*, Network*, char*, char*, int);
137 Ndbtuple*       telcolookup(Network*, char*, char*);
138 char*           telcotrans(Ndbtuple*, Network*, char*, char*, int);
139
140 Ndbtuple*       dnsiplookup(char*, Ndbs*, int);
141 Ndbtuple*       myipinfo(Ndb *db, char **list, int n);
142
143 struct Network
144 {
145         char            *net;
146         Ndbtuple        *(*lookup)(Network*, char*, char*);
147         char            *(*trans)(Ndbtuple*, Network*, char*, char*, int);
148
149         char            considered;             /* flag: ignored for "net!"? */
150         char            fasttimeout;            /* flag. was for IL */
151         char            ipvers;                 /* flag: V4, V6 */
152
153         Network         *next;
154 };
155
156 enum {
157         Ntcp = 1,
158
159         V4 = 1,
160         V6 = 2,
161 };
162
163 /*
164  *  net doesn't apply to (r)udp, icmp(v6), or telco (for speed).
165  */
166 Network network[] = {
167         { "il",         iplookup,       iptrans,        0, 1, V4,       },
168         { "tcp",        iplookup,       iptrans,        0, 0, V4|V6,    },
169         { "il",         iplookup,       iptrans,        0, 0, V4,       },
170         { "udp",        iplookup,       iptrans,        1, 0, V4|V6,    },
171         { "icmp",       iplookup,       iptrans,        1, 0, V4,       },
172         { "icmpv6",     iplookup,       iptrans,        1, 0, V6,       },
173         { "rudp",       iplookup,       iptrans,        1, 0, V4,       },
174         { "ssh",        iplookup,       iptrans,        1, 0, V4|V6,    },
175         { "telco",      telcolookup,    telcotrans,     1, 0, 0,        },
176         { 0 },
177 };
178
179 QLock ipifclock;
180 Ipifc *ipifcs;
181 int confipvers;
182 int dnsipvers;
183 int lookipvers = V4|V6;
184
185 char *mysysname;
186
187 Network *netlist;               /* networks ordered by preference */
188 Network *last;
189
190 static void
191 nstrcpy(char *to, char *from, int len)
192 {
193         strncpy(to, from, len);
194         to[len-1] = 0;
195 }
196
197 void
198 usage(void)
199 {
200         fprint(2, "usage: %s [-46dn] [-f ndb-file] [-x netmtpt]\n", argv0);
201         exits("usage");
202 }
203
204 void
205 main(int argc, char *argv[])
206 {
207         int justsetname;
208         char ext[Maxpath], servefile[Maxpath];
209
210         justsetname = 0;
211         setnetmtpt(mntpt, sizeof(mntpt), nil);
212         ext[0] = 0;
213         ARGBEGIN{
214         case '4':
215                 lookipvers = V4;
216                 break;
217         case '6':
218                 lookipvers = V6;
219                 break;
220         case 'd':
221                 debug = 1;
222                 break;
223         case 'f':
224                 dbfile = EARGF(usage());
225                 break;
226         case 'n':
227                 justsetname = 1;
228                 break;
229         case 'x':
230                 setnetmtpt(mntpt, sizeof(mntpt), EARGF(usage()));
231                 setext(ext, sizeof(ext), mntpt);
232                 break;
233         }ARGEND
234         USED(argc);
235         USED(argv);
236
237         snprint(netndb, sizeof(netndb), "%s/ndb", mntpt);
238
239         fmtinstall('E', eipfmt);
240         fmtinstall('I', eipfmt);
241         fmtinstall('M', eipfmt);
242         fmtinstall('F', fcallfmt);
243
244         ndbinit();
245         netinit(0);
246
247         if(!justsetname){
248                 snprint(servefile, sizeof(servefile), "#s/cs%s", ext);
249                 unmount(servefile, mntpt);
250                 remove(servefile);
251
252                 rfork(RFREND|RFNOTEG);
253                 csuser = estrdup(getuser());
254                 mountinit(servefile, mntpt);
255                 io();
256         }
257         exits(0);
258 }
259
260 /*
261  *  if a mount point is specified, set the cs extention to be the mount point
262  *  with '_'s replacing '/'s
263  */
264 void
265 setext(char *ext, int n, char *p)
266 {
267         int i, c;
268
269         n--;
270         for(i = 0; i < n; i++){
271                 c = p[i];
272                 if(c == 0)
273                         break;
274                 if(c == '/')
275                         c = '_';
276                 ext[i] = c;
277         }
278         ext[i] = 0;
279 }
280
281 void
282 mountinit(char *service, char *mntpt)
283 {
284         int f;
285         int p[2];
286         char buf[32];
287
288         if(pipe(p) < 0)
289                 error("pipe failed");
290
291         /*
292          *  make a /srv/cs
293          */
294         f = create(service, OWRITE|ORCLOSE, 0666);
295         if(f < 0)
296                 error(service);
297         snprint(buf, sizeof(buf), "%d", p[1]);
298         if(write(f, buf, strlen(buf)) != strlen(buf))
299                 error("write /srv/cs");
300
301         switch(rfork(RFFDG|RFPROC|RFNAMEG)){
302         case 0:
303                 close(p[1]);
304                 procsetname("%s", mntpt);
305                 break;
306         case -1:
307                 error("fork failed");
308         default:
309                 /*
310                  *  put ourselves into the file system
311                  */
312                 close(p[0]);
313                 if(mount(p[1], -1, mntpt, MAFTER, "") < 0)
314                         error("mount failed");
315                 _exits(0);
316         }
317         mfd[0] = mfd[1] = p[0];
318 }
319
320 void
321 ndbinit(void)
322 {
323         db = ndbopen(dbfile);
324         if(db == nil)
325                 error("can't open network database");
326
327         for(netdb = db; netdb != nil; netdb = netdb->next)
328                 if(strcmp(netdb->file, netndb) == 0)
329                         return;
330
331         netdb = ndbopen(netndb);
332         if(netdb != nil){
333                 netdb->nohash = 1;
334                 db = ndbcat(netdb, db);
335         }
336 }
337
338 Mfile*
339 newfid(int fid)
340 {
341         Mlist *f, *ff;
342         Mfile *mf;
343
344         ff = nil;
345         for(f = mlist; f != nil; f = f->next)
346                 if(f->mf.busy && f->mf.fid == fid)
347                         return &f->mf;
348                 else if(ff == nil && !f->mf.busy && !f->mf.ref)
349                         ff = f;
350         if(ff == nil){
351                 ff = emalloc(sizeof *f);
352                 ff->next = mlist;
353                 mlist = ff;
354         }
355         mf = &ff->mf;
356         memset(mf, 0, sizeof *mf);
357         mf->fid = fid;
358         return mf;
359 }
360
361 Job*
362 newjob(void)
363 {
364         Job *job;
365
366         job = emalloc(sizeof *job);
367         qlock(&joblock);
368         job->next = joblist;
369         joblist = job;
370         job->request.tag = -1;
371         qunlock(&joblock);
372         return job;
373 }
374
375 void
376 freejob(Job *job)
377 {
378         Job **l;
379
380         qlock(&joblock);
381         for(l = &joblist; *l != nil; l = &(*l)->next){
382                 if((*l) == job){
383                         *l = job->next;
384                         break;
385                 }
386         }
387         qunlock(&joblock);
388         free(job);
389 }
390
391 void
392 flushjob(int tag)
393 {
394         Job *job;
395
396         qlock(&joblock);
397         for(job = joblist; job != nil; job = job->next){
398                 if(job->request.tag == tag && job->request.type != Tflush){
399                         job->flushed = 1;
400                         break;
401                 }
402         }
403         qunlock(&joblock);
404 }
405
406 void
407 io(void)
408 {
409         long n;
410         Mfile *mf;
411         int slaveflag;
412         uchar mdata[IOHDRSZ + Maxfdata];
413         Job *job;
414
415         /*
416          *  if we ask dns to fulfill requests,
417          *  a slave process is created to wait for replies.  The
418          *  master process returns immediately via a longjmp
419          *  through 'masterjmp'.
420          *
421          *  *isslave is a pointer into the call stack to a variable
422          *  that tells whether or not the current process is a slave.
423          */
424         slaveflag = 0;          /* init slave variable */
425         isslave = &slaveflag;
426         setjmp(masterjmp);
427
428         for(;;){
429                 n = read9pmsg(mfd[0], mdata, sizeof mdata);
430                 if(n < 0)
431                         error("mount read");
432                 if(n == 0)
433                         break;
434                 job = newjob();
435                 if(convM2S(mdata, n, &job->request) != n){
436                         syslog(1, logfile, "format error %ux %ux %ux %ux %ux",
437                                 mdata[0], mdata[1], mdata[2], mdata[3], mdata[4]);
438                         freejob(job);
439                         break;
440                 }
441                 qlock(&dblock);
442                 mf = newfid(job->request.fid);
443                 if(debug)
444                         syslog(0, logfile, "%F", &job->request);
445
446                 switch(job->request.type){
447                 default:
448                         syslog(1, logfile, "unknown request type %d", job->request.type);
449                         break;
450                 case Tversion:
451                         rversion(job);
452                         break;
453                 case Tauth:
454                         rauth(job);
455                         break;
456                 case Tflush:
457                         rflush(job);
458                         break;
459                 case Tattach:
460                         rattach(job, mf);
461                         break;
462                 case Twalk:
463                         rwalk(job, mf);
464                         break;
465                 case Topen:
466                         ropen(job, mf);
467                         break;
468                 case Tcreate:
469                         rcreate(job, mf);
470                         break;
471                 case Tread:
472                         rread(job, mf);
473                         break;
474                 case Twrite:
475                         rwrite(job, mf);
476                         break;
477                 case Tclunk:
478                         rclunk(job, mf);
479                         break;
480                 case Tremove:
481                         rremove(job, mf);
482                         break;
483                 case Tstat:
484                         rstat(job, mf);
485                         break;
486                 case Twstat:
487                         rwstat(job, mf);
488                         break;
489                 }
490                 qunlock(&dblock);
491
492                 freejob(job);
493
494                 /*
495                  *  slave processes die after replying
496                  */
497                 if(*isslave){
498                         if(debug)
499                                 syslog(0, logfile, "slave death %d", getpid());
500                         adec(&active);
501                         _exits(0);
502                 }
503         }
504 }
505
506 void
507 rversion(Job *job)
508 {
509         if(job->request.msize > IOHDRSZ + Maxfdata)
510                 job->reply.msize = IOHDRSZ + Maxfdata;
511         else
512                 job->reply.msize = job->request.msize;
513         if(strncmp(job->request.version, "9P2000", 6) != 0)
514                 sendmsg(job, "unknown 9P version");
515         else{
516                 job->reply.version = "9P2000";
517                 sendmsg(job, nil);
518         }
519 }
520
521 void
522 rauth(Job *job)
523 {
524         sendmsg(job, "cs: authentication not required");
525 }
526
527 /*
528  *  don't flush till all the slaves are done
529  */
530 void
531 rflush(Job *job)
532 {
533         flushjob(job->request.oldtag);
534         sendmsg(job, nil);
535 }
536
537 void
538 rattach(Job *job, Mfile *mf)
539 {
540         if(mf->busy == 0){
541                 mf->busy = 1;
542                 mf->user = estrdup(job->request.uname);
543         }
544         mf->qid.vers = vers++;
545         mf->qid.type = QTDIR;
546         mf->qid.path = 0LL;
547         job->reply.qid = mf->qid;
548         sendmsg(job, nil);
549 }
550
551
552 char*
553 rwalk(Job *job, Mfile *mf)
554 {
555         char *err;
556         char **elems;
557         int nelems;
558         int i;
559         Mfile *nmf;
560         Qid qid;
561
562         err = nil;
563         nmf = nil;
564         elems = job->request.wname;
565         nelems = job->request.nwname;
566         job->reply.nwqid = 0;
567
568         if(job->request.newfid != job->request.fid){
569                 /* clone fid */
570                 nmf = newfid(job->request.newfid);
571                 if(nmf->busy){
572                         nmf = nil;
573                         err = "clone to used channel";
574                         goto send;
575                 }
576                 *nmf = *mf;
577                 nmf->user = estrdup(mf->user);
578                 nmf->fid = job->request.newfid;
579                 nmf->qid.vers = vers++;
580                 mf = nmf;
581         }
582         /* else nmf will be nil */
583
584         qid = mf->qid;
585         if(nelems > 0){
586                 /* walk fid */
587                 for(i=0; i<nelems && i<MAXWELEM; i++){
588                         if((qid.type & QTDIR) == 0){
589                                 err = "not a directory";
590                                 break;
591                         }
592                         if(strcmp(elems[i], "..") == 0 || strcmp(elems[i], ".") == 0){
593                                 qid.type = QTDIR;
594                                 qid.path = Qdir;
595     Found:
596                                 job->reply.wqid[i] = qid;
597                                 job->reply.nwqid++;
598                                 continue;
599                         }
600                         if(strcmp(elems[i], "cs") == 0){
601                                 qid.type = QTFILE;
602                                 qid.path = Qcs;
603                                 goto Found;
604                         }
605                         err = "file does not exist";
606                         break;
607                 }
608         }
609
610     send:
611         if(nmf != nil && (err!=nil || job->reply.nwqid<nelems)){
612                 cleanmf(nmf);
613                 free(nmf->user);
614                 nmf->user = nil;
615                 nmf->busy = 0;
616                 nmf->fid = 0;
617         }
618         if(err == nil)
619                 mf->qid = qid;
620         sendmsg(job, err);
621         return err;
622 }
623
624 void
625 ropen(Job *job, Mfile *mf)
626 {
627         int mode;
628         char *err;
629
630         err = nil;
631         mode = job->request.mode;
632         if(mf->qid.type & QTDIR){
633                 if(mode)
634                         err = "permission denied";
635         }
636         job->reply.qid = mf->qid;
637         job->reply.iounit = 0;
638         sendmsg(job, err);
639 }
640
641 void
642 rcreate(Job *job, Mfile *mf)
643 {
644         USED(mf);
645         sendmsg(job, "creation permission denied");
646 }
647
648 void
649 rread(Job *job, Mfile *mf)
650 {
651         int i, n, cnt;
652         long off, toff, clock;
653         Dir dir;
654         uchar buf[Maxfdata];
655         char *err;
656
657         n = 0;
658         err = nil;
659         off = job->request.offset;
660         cnt = job->request.count;
661         mf->ref++;
662
663         if(mf->qid.type & QTDIR){
664                 clock = time(0);
665                 if(off == 0){
666                         memset(&dir, 0, sizeof dir);
667                         dir.name = "cs";
668                         dir.qid.type = QTFILE;
669                         dir.qid.vers = vers;
670                         dir.qid.path = Qcs;
671                         dir.mode = 0666;
672                         dir.length = 0;
673                         dir.uid = mf->user;
674                         dir.gid = mf->user;
675                         dir.muid = mf->user;
676                         dir.atime = clock;      /* wrong */
677                         dir.mtime = clock;      /* wrong */
678                         n = convD2M(&dir, buf, sizeof buf);
679                 }
680                 job->reply.data = (char*)buf;
681                 goto send;
682         }
683
684         for(;;){
685                 /* look for an answer at the right offset */
686                 toff = 0;
687                 for(i = 0; mf->reply[i] != nil && i < mf->nreply; i++){
688                         n = mf->replylen[i];
689                         if(off < toff + n)
690                                 break;
691                         toff += n;
692                 }
693                 if(i < mf->nreply)
694                         break;          /* got something to return */
695
696                 /* try looking up more answers */
697                 if(lookup(mf) == 0 || job->flushed){
698                         /* no more */
699                         n = 0;
700                         goto send;
701                 }
702         }
703
704         /* give back a single reply (or part of one) */
705         job->reply.data = mf->reply[i] + (off - toff);
706         if(cnt > toff - off + n)
707                 n = toff - off + n;
708         else
709                 n = cnt;
710
711 send:
712         job->reply.count = n;
713         sendmsg(job, err);
714
715         if(--mf->ref == 0 && mf->busy == 0)
716                 cleanmf(mf);
717 }
718
719 void
720 cleanmf(Mfile *mf)
721 {
722         int i;
723
724         if(mf->net != nil){
725                 free(mf->net);
726                 mf->net = nil;
727         }
728         if(mf->host != nil){
729                 free(mf->host);
730                 mf->host = nil;
731         }
732         if(mf->serv != nil){
733                 free(mf->serv);
734                 mf->serv = nil;
735         }
736         if(mf->rem != nil){
737                 free(mf->rem);
738                 mf->rem = nil;
739         }
740         for(i = 0; i < mf->nreply; i++){
741                 free(mf->reply[i]);
742                 mf->reply[i] = nil;
743                 mf->replylen[i] = 0;
744         }
745         mf->nreply = 0;
746         mf->nextnet = netlist;
747 }
748
749 void
750 rwrite(Job *job, Mfile *mf)
751 {
752         int cnt, n;
753         char *err;
754         char *field[4];
755         char curerr[64];
756
757         err = nil;
758         cnt = job->request.count;
759         if(mf->qid.type & QTDIR){
760                 err = "can't write directory";
761                 goto send;
762         }
763         if(cnt >= Maxrequest){
764                 err = "request too long";
765                 goto send;
766         }
767         job->request.data[cnt] = 0;
768
769         if(strcmp(mf->user, "none") == 0 || strcmp(mf->user, csuser) != 0)
770                 goto query;     /* skip special commands if not owner */
771
772         /*
773          *  toggle debugging
774          */
775         if(strncmp(job->request.data, "debug", 5)==0){
776                 debug ^= 1;
777                 syslog(1, logfile, "debug %d", debug);
778                 goto send;
779         }
780
781         /*
782          *  toggle ipv4 lookups
783          */
784         if(strncmp(job->request.data, "ipv4", 4)==0){
785                 lookipvers ^= V4;
786                 syslog(1, logfile, "ipv4lookups %d", (lookipvers & V4) != 0);
787                 goto send;
788         }
789
790         /*
791          *  toggle ipv6 lookups
792          */
793         if(strncmp(job->request.data, "ipv6", 4)==0){
794                 lookipvers ^= V6;
795                 syslog(1, logfile, "ipv6lookups %d", (lookipvers & V6) != 0);
796                 goto send;
797         }
798
799         /*
800          *  add networks to the default list
801          */
802         if(strncmp(job->request.data, "add ", 4)==0){
803                 if(job->request.data[cnt-1] == '\n')
804                         job->request.data[cnt-1] = 0;
805                 netadd(job->request.data+4);
806                 readipinterfaces();
807                 goto send;
808         }
809
810         /*
811          *  refresh all state
812          */
813         if(strncmp(job->request.data, "refresh", 7)==0){
814                 netinit(1);
815                 goto send;
816         }
817
818 query:
819         if(mf->ref){
820                 err = "query already in progress";
821                 goto send;
822         }
823         mf->ref++;
824
825         /* start transaction with a clean slate */
826         cleanmf(mf);
827
828         /*
829          *  look for a general query
830          */
831         if(*job->request.data == '!'){
832                 err = genquery(mf, job->request.data+1);
833                 goto done;
834         }
835
836         if(debug)
837                 syslog(0, logfile, "write %s", job->request.data);
838         /*
839          *  break up name
840          */
841         n = getfields(job->request.data, field, 4, 1, "!");
842         switch(n){
843         case 1:
844                 mf->net = estrdup("net");
845                 mf->host = estrdup(field[0]);
846                 break;
847         case 4:
848                 mf->rem = estrdup(field[3]);
849                 /* fall through */
850         case 3:
851                 mf->serv = estrdup(field[2]);
852                 /* fall through */
853         case 2:
854                 mf->host = estrdup(field[1]);
855                 mf->net = estrdup(field[0]);
856                 break;
857         }
858
859         /*
860          *  do the first net worth of lookup
861          */
862         if(lookup(mf) == 0){
863                 rerrstr(curerr, sizeof curerr);
864                 err = curerr;
865         }
866
867 done:
868         if(--mf->ref == 0 && mf->busy == 0)
869                 cleanmf(mf);
870
871 send:
872         job->reply.count = cnt;
873         sendmsg(job, err);
874 }
875
876 void
877 rclunk(Job *job, Mfile *mf)
878 {
879         if(mf->ref == 0)
880                 cleanmf(mf);
881         free(mf->user);
882         mf->user = nil;
883         mf->fid = 0;
884         mf->busy = 0;
885         sendmsg(job, nil);
886 }
887
888 void
889 rremove(Job *job, Mfile *mf)
890 {
891         USED(mf);
892         sendmsg(job, "remove permission denied");
893 }
894
895 void
896 rstat(Job *job, Mfile *mf)
897 {
898         Dir dir;
899         uchar buf[IOHDRSZ+Maxfdata];
900
901         memset(&dir, 0, sizeof dir);
902         if(mf->qid.type & QTDIR){
903                 dir.name = ".";
904                 dir.mode = DMDIR|0555;
905         } else {
906                 dir.name = "cs";
907                 dir.mode = 0666;
908         }
909         dir.qid = mf->qid;
910         dir.length = 0;
911         dir.uid = mf->user;
912         dir.gid = mf->user;
913         dir.muid = mf->user;
914         dir.atime = dir.mtime = time(0);
915         job->reply.nstat = convD2M(&dir, buf, sizeof buf);
916         job->reply.stat = buf;
917         sendmsg(job, nil);
918 }
919
920 void
921 rwstat(Job *job, Mfile *mf)
922 {
923         USED(mf);
924         sendmsg(job, "wstat permission denied");
925 }
926
927 void
928 sendmsg(Job *job, char *err)
929 {
930         int n;
931         uchar mdata[IOHDRSZ + Maxfdata];
932         char ename[ERRMAX];
933
934         if(err){
935                 job->reply.type = Rerror;
936                 snprint(ename, sizeof(ename), "cs: %s", err);
937                 job->reply.ename = ename;
938         }else{
939                 job->reply.type = job->request.type+1;
940         }
941         job->reply.tag = job->request.tag;
942         n = convS2M(&job->reply, mdata, sizeof mdata);
943         if(n == 0){
944                 syslog(1, logfile, "sendmsg convS2M of %F returns 0", &job->reply);
945                 abort();
946         }
947         qlock(&joblock);
948         if(job->flushed == 0)
949                 if(write(mfd[1], mdata, n)!=n)
950                         error("mount write");
951         qunlock(&joblock);
952         if(debug)
953                 syslog(0, logfile, "%F %d", &job->reply, n);
954 }
955
956 void
957 error(char *s)
958 {
959         syslog(1, logfile, "%s: %r", s);
960         _exits(0);
961 }
962
963 static uchar loopbacknet[IPaddrlen] = {
964         0, 0, 0, 0,
965         0, 0, 0, 0,
966         0, 0, 0xff, 0xff,
967         127, 0, 0, 0
968 };
969 static uchar loopbackmask[IPaddrlen] = {
970         0xff, 0xff, 0xff, 0xff,
971         0xff, 0xff, 0xff, 0xff,
972         0xff, 0xff, 0xff, 0xff,
973         0xff, 0, 0, 0
974 };
975 static uchar loopback6[IPaddrlen] = {
976         0, 0, 0, 0,
977         0, 0, 0, 0,
978         0, 0, 0, 0,
979         0, 0, 0, 1
980 };
981
982 void
983 readipinterfaces(void)
984 {
985         uchar mynet[IPaddrlen];
986         Ipifc *ifc;
987         Iplifc *lifc;
988         int conf, dns;
989
990         conf = dns = 0;
991         qlock(&ipifclock);
992         ipifcs = readipifc(mntpt, ipifcs, -1);
993         for(ifc = ipifcs; ifc != nil; ifc = ifc->next){
994                 for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next){
995                         if(ipcmp(lifc->ip, IPnoaddr) == 0)
996                                 continue;
997                         if(isv4(lifc->ip)){
998                                 conf |= V4;
999                                 maskip(lifc->ip, loopbackmask, mynet);
1000                                 if(ipcmp(mynet, loopbacknet) == 0)
1001                                         continue;
1002                                 dns |= V4;
1003                         } else {
1004                                 conf |= V6;
1005                                 if(ISIPV6LINKLOCAL(lifc->ip))
1006                                         continue;
1007                                 if(ipcmp(lifc->ip, loopback6) == 0)
1008                                         continue;
1009                                 dns |= V6;
1010                         }
1011                 }
1012         }
1013         qunlock(&ipifclock);
1014         confipvers = conf;
1015         dnsipvers = dns;
1016 }
1017
1018 /*
1019  *  get the system name
1020  */
1021 void
1022 ipid(void)
1023 {
1024         char eaddr[16], buf[Maxpath];
1025         uchar addr[6];
1026         Ndbtuple *t, *tt;
1027         char *p, *attr;
1028         Ndbs s;
1029         int f, n;
1030         Dir *d;
1031
1032         if(mysysname != nil)
1033                 return;
1034
1035         /*
1036          *  environment has priority.
1037          *
1038          *  on the sgi power the default system name
1039          *  is the ip address.  ignore that.
1040          *
1041          */
1042         p = getenv("sysname");
1043         if(p != nil && *p != 0){
1044                 attr = ipattr(p);
1045                 if(strcmp(attr, "ip") != 0) {
1046                         mysysname = p;
1047                         goto setsys;
1048                 }
1049                 free(p);
1050         }
1051
1052         /* try configured interfaces */
1053         attr = "sys";
1054         t = s.t = myipinfo(db, &attr, 1);
1055         if(t != nil)
1056                 goto found;
1057
1058         /* try ethernet interfaces */
1059         n = 0;
1060         d = nil;
1061         f = open(mntpt, OREAD);
1062         if(f >= 0){
1063                 n = dirreadall(f, &d);
1064                 close(f);
1065         }
1066         for(f = 0; f < n; f++){
1067                 if((d[f].mode & DMDIR) == 0 || strncmp(d[f].name, "ether", 5) != 0)
1068                         continue;
1069                 snprint(buf, sizeof buf, "%s/%s", mntpt, d[f].name);
1070                 if(myetheraddr(addr, buf) >= 0){
1071                         snprint(eaddr, sizeof(eaddr), "%E", addr);
1072                         free(ndbgetvalue(db, &s, "ether", eaddr, "sys", &t));
1073                         if(t != nil){
1074                                 free(d);
1075                                 goto found;
1076                         }
1077                 }
1078         }
1079         free(d);
1080
1081         /* nothing else worked, use ip address */
1082         attr = "ip";
1083         t = s.t = myipinfo(db, &attr, 1);
1084         if(t == nil)
1085                 return;
1086         
1087 found:
1088         /* found in database */
1089         if((tt = ndbfindattr(t, s.t, "sys")) != nil)
1090                 mysysname = estrdup(tt->val);
1091         else if((tt = ndbfindattr(t, s.t, "ip")) != nil)
1092                 mysysname = estrdup(tt->val);
1093         ndbfree(t);
1094
1095         if(mysysname == nil)
1096                 return;
1097
1098 setsys:
1099         /* set /dev/sysname if we now know it */
1100         f = open("/dev/sysname", OWRITE);
1101         if(f >= 0){
1102                 write(f, mysysname, strlen(mysysname));
1103                 close(f);
1104         }
1105 }
1106
1107 /*
1108  *  Set up a list of default networks by looking for
1109  *  /net/^*^/clone.
1110  */
1111 void
1112 netinit(int background)
1113 {
1114         char clone[Maxpath];
1115         Network *np;
1116
1117         if(background){
1118                 if(rfork(RFPROC|RFNOTEG|RFMEM|RFNOWAIT) != 0)
1119                         return;
1120                 qlock(&netlock);
1121         }
1122
1123         /* add the mounted networks to the default list */
1124         for(np = network; np->net != nil; np++){
1125                 if(np->considered)
1126                         continue;
1127                 snprint(clone, sizeof(clone), "%s/%s/clone", mntpt, np->net);
1128                 if(access(clone, AEXIST) < 0)
1129                         continue;
1130                 if(netlist != nil)
1131                         last->next = np;
1132                 else
1133                         netlist = np;
1134                 last = np;
1135                 np->next = nil;
1136                 np->considered = 1;
1137         }
1138
1139         /* find out what our ip addresses are */
1140         readipinterfaces();
1141
1142         /* set the system name if we need to, these days ip is all we have */
1143         ipid();
1144
1145         if(debug)
1146                 syslog(0, logfile, "mysysname %s", mysysname?mysysname:"???");
1147
1148         if(background){
1149                 qunlock(&netlock);
1150                 _exits(0);
1151         }
1152 }
1153
1154 /*
1155  *  add networks to the standard list
1156  */
1157 void
1158 netadd(char *p)
1159 {
1160         Network *np;
1161         char *field[12];
1162         int i, n;
1163
1164         n = getfields(p, field, 12, 1, " ");
1165         for(i = 0; i < n; i++){
1166                 for(np = network; np->net != nil; np++){
1167                         if(strcmp(field[i], np->net) != 0)
1168                                 continue;
1169                         if(np->considered)
1170                                 break;
1171                         if(netlist != nil)
1172                                 last->next = np;
1173                         else
1174                                 netlist = np;
1175                         last = np;
1176                         np->next = nil;
1177                         np->considered = 1;
1178                 }
1179         }
1180 }
1181
1182 int
1183 lookforproto(Ndbtuple *t, char *proto)
1184 {
1185         for(; t != nil; t = t->entry)
1186                 if(strcmp(t->attr, "proto") == 0 && strcmp(t->val, proto) == 0)
1187                         return 1;
1188         return 0;
1189 }
1190
1191 /*
1192  *  lookup a request.  the network "net" means we should pick the
1193  *  best network to get there.
1194  */
1195 int
1196 lookup(Mfile *mf)
1197 {
1198         Network *np;
1199         char *cp;
1200         Ndbtuple *nt, *t;
1201         char reply[Maxreply];
1202         int i, rv, fasttimeout;
1203
1204         /* open up the standard db files */
1205         if(db == nil)
1206                 ndbinit();
1207         if(db == nil)
1208                 error("can't open mf->network database\n");
1209
1210         if(mf->net == nil)
1211                 return 0;       /* must have been a genquery */
1212
1213         rv = 0;
1214         if(strcmp(mf->net, "net") == 0){
1215                 /*
1216                  *  go through set of default nets
1217                  */
1218                 for(np = mf->nextnet; np != nil && rv == 0; np = np->next){
1219                         nt = (*np->lookup)(np, mf->host, mf->serv);
1220                         if(nt == nil)
1221                                 continue;
1222                         fasttimeout = np->fasttimeout && !lookforproto(nt, np->net);
1223                         for(t = nt; mf->nreply < Nreply && t != nil; t = t->entry){
1224                                 cp = (*np->trans)(t, np, mf->serv, mf->rem, fasttimeout);
1225                                 if(cp != nil){
1226                                         /* avoid duplicates */
1227                                         for(i = 0; i < mf->nreply; i++)
1228                                                 if(strcmp(mf->reply[i], cp) == 0)
1229                                                         break;
1230                                         if(i == mf->nreply){
1231                                                 /* save the reply */
1232                                                 mf->replylen[mf->nreply] = strlen(cp);
1233                                                 mf->reply[mf->nreply++] = cp;
1234                                                 rv++;
1235                                         } else
1236                                                 free(cp);
1237                                 }
1238                         }
1239                         ndbfree(nt);
1240                 }
1241                 mf->nextnet = np;
1242                 return rv;
1243         }
1244
1245         /*
1246          *  if not /net, we only get one lookup
1247          */
1248         if(mf->nreply != 0)
1249                 return 0;
1250
1251         /*
1252          *  look for a specific network
1253          */
1254         for(np = network; np->net != nil; np++){
1255                 if(np->fasttimeout)
1256                         continue;
1257                 if(strcmp(np->net, mf->net) == 0)
1258                         break;
1259         }
1260
1261         if(np->net != nil){
1262                 /*
1263                  *  known network
1264                  */
1265                 nt = (*np->lookup)(np, mf->host, mf->serv);
1266                 for(t = nt; mf->nreply < Nreply && t != nil; t = t->entry){
1267                         cp = (*np->trans)(t, np, mf->serv, mf->rem, 0);
1268                         if(cp != nil){
1269                                 mf->replylen[mf->nreply] = strlen(cp);
1270                                 mf->reply[mf->nreply++] = cp;
1271                                 rv++;
1272                         }
1273                 }
1274                 ndbfree(nt);
1275                 return rv;
1276         } else {
1277                 /*
1278                  *  not a known network, don't translate host or service
1279                  */
1280                 if(mf->serv != nil)
1281                         snprint(reply, sizeof(reply), "%s/%s/clone %s!%s",
1282                                 mntpt, mf->net, mf->host, mf->serv);
1283                 else
1284                         snprint(reply, sizeof(reply), "%s/%s/clone %s",
1285                                 mntpt, mf->net, mf->host);
1286                 mf->reply[0] = estrdup(reply);
1287                 mf->replylen[0] = strlen(reply);
1288                 mf->nreply = 1;
1289                 return 1;
1290         }
1291 }
1292
1293 /*
1294  *  translate an ip service name into a port number.  If it's a numeric port
1295  *  number, look for restricted access.
1296  *
1297  *  the service '*' needs no translation.
1298  */
1299 char*
1300 ipserv(Network *np, char *name, char *buf, int blen)
1301 {
1302         char *p;
1303         int alpha = 0;
1304         int restr = 0;
1305         Ndbtuple *t, *nt;
1306         Ndbs s;
1307
1308         /* '*' means any service */
1309         if(strcmp(name, "*") == 0){
1310                 nstrcpy(buf, name, blen);
1311                 return buf;
1312         }
1313
1314         /*  see if it's numeric or symbolic */
1315         for(p = name; *p; p++){
1316                 if(isdigit(*p))
1317                         {}
1318                 else if(isalpha(*p) || *p == '-' || *p == '$')
1319                         alpha = 1;
1320                 else
1321                         return nil;
1322         }
1323         t = nil;
1324         p = nil;
1325         if(alpha){
1326                 p = ndbgetvalue(db, &s, np->net, name, "port", &t);
1327                 if(p == nil)
1328                         return nil;
1329         } else {
1330                 /* look up only for tcp ports < 1024 to get the restricted
1331                  * attribute
1332                  */
1333                 if(atoi(name) < 1024 && strcmp(np->net, "tcp") == 0)
1334                         p = ndbgetvalue(db, &s, "port", name, "port", &t);
1335                 if(p == nil)
1336                         p = estrdup(name);
1337         }
1338
1339         if(t){
1340                 for(nt = t; nt != nil; nt = nt->entry)
1341                         if(strcmp(nt->attr, "restricted") == 0)
1342                                 restr = 1;
1343                 ndbfree(t);
1344         }
1345         snprint(buf, blen, "%s%s", p, restr ? "!r" : "");
1346         free(p);
1347
1348         return buf;
1349 }
1350
1351 Ndbtuple*
1352 myipinfo(Ndb *db, char **list, int n)
1353 {
1354         Ndbtuple *t, *nt;
1355         char ip[64];
1356         Ipifc *ifc;
1357         Iplifc *lifc;
1358
1359         t = nil;
1360         qlock(&ipifclock);
1361         for(ifc = ipifcs; ifc != nil; ifc = ifc->next){
1362                 for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next){
1363                         snprint(ip, sizeof(ip), "%I", lifc->ip);
1364                         nt = ndbipinfo(db, "ip", ip, list, n);
1365                         t = ndbconcatenate(t, nt);
1366                 }
1367         }
1368         qunlock(&ipifclock);
1369
1370         return ndbdedup(t);
1371 }
1372
1373 /*
1374  * reorder according to our interfaces
1375  */
1376 static Ndbtuple*
1377 ipreorder(Ndbtuple *t)
1378 {
1379         Ndbtuple *nt;
1380         uchar ip[IPaddrlen];
1381         uchar net[IPaddrlen];
1382         uchar tnet[IPaddrlen];
1383         Ipifc *ifc;
1384         Iplifc *lifc;
1385
1386         if(t == nil)
1387                 return nil;
1388
1389         qlock(&ipifclock);
1390         for(ifc = ipifcs; ifc != nil; ifc = ifc->next){
1391                 for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next){
1392                         maskip(lifc->ip, lifc->mask, net);
1393                         for(nt = t; nt != nil; nt = nt->entry){
1394                                 if(strcmp(nt->attr, "ip") != 0)
1395                                         continue;
1396                                 if(parseip(ip, nt->val) == -1)
1397                                         continue;
1398                                 maskip(ip, lifc->mask, tnet);
1399                                 if(memcmp(net, tnet, IPaddrlen) == 0){
1400                                         qunlock(&ipifclock);
1401                                         return ndbreorder(t, nt);
1402                                 }
1403                         }
1404                 }
1405         }
1406         qunlock(&ipifclock);
1407
1408         return t;
1409 }
1410
1411 static Ndbtuple*
1412 ndbline(Ndbtuple *t)
1413 {
1414         Ndbtuple *nt;
1415
1416         for(nt = t; nt != nil; nt = nt->entry){
1417                 if(nt->entry == nil)
1418                         nt->line = t;
1419                 else
1420                         nt->line = nt->entry;
1421         }
1422         return t;
1423 }
1424
1425 /*
1426  *  lookup an ip destination
1427  */
1428 static Ndbtuple*
1429 iplookuphost(Network *np, char *host)
1430 {
1431         char *attr, *dnsname;
1432         Ndbtuple *t, *nt;
1433         Ndbs s;
1434
1435         /*
1436          *  turn '[ip address]' into just 'ip address'
1437          */
1438         if(*host == '['){
1439                 char tmp[Maxhost], *x;
1440
1441                 nstrcpy(tmp, host, sizeof tmp);
1442                 host = tmp;
1443                 if((x = strchr(++host, ']')) != nil)
1444                         *x = 0;
1445         }
1446
1447         /* for dial strings with no host */
1448         if(strcmp(host, "*") == 0)
1449                 return ndbline(ndbnew("ip", "*"));
1450
1451         /*
1452          *  hack till we go v6 :: = 0.0.0.0
1453          */
1454         if(strcmp("::", host) == 0)
1455                 return ndbline(ndbnew("ip", "*"));
1456
1457         /*
1458          *  just accept addresses
1459          */
1460         attr = ipattr(host);
1461         if(strcmp(attr, "ip") == 0)
1462                 return ndbline(ndbnew("ip", host));
1463
1464         /*
1465          *  give the domain name server the first opportunity to
1466          *  resolve domain names.  if that fails try the database.
1467          */
1468         t = nil;
1469         if(strcmp(attr, "dom") == 0)
1470                 t = dnsiplookup(host, &s, np->ipvers);
1471         if(t == nil){
1472                 for(nt = ndbsearch(db, &s, attr, host); nt != nil; nt = ndbsnext(&s, attr, host)){
1473                         if(ndbfindattr(nt, s.t, "ip") == nil){
1474                                 ndbfree(nt);
1475                                 continue;
1476                         }
1477                         t = ndbconcatenate(t, ndbreorder(nt, s.t));
1478                 }
1479                 s.t = t;
1480         }
1481         if(t == nil){
1482                 if(strcmp(attr, "dom") != 0){
1483                         dnsname = ndbgetvalue(db, &s, attr, host, "dom", nil);
1484                         if(dnsname != nil){
1485                                 t = dnsiplookup(dnsname, &s, np->ipvers);
1486                                 free(dnsname);
1487                         }
1488                 }
1489                 if(t == nil)
1490                         t = dnsiplookup(host, &s, np->ipvers);
1491         }
1492         if(t == nil)
1493                 return nil;
1494
1495         /*
1496          *  reorder the tuple to have the matched line first and
1497          *  save that in the request structure.
1498          */
1499         return ndbreorder(t, s.t);
1500 }
1501
1502
1503 Ndbtuple*
1504 iplookup(Network *np, char *host, char *serv)
1505 {
1506         Ndbtuple *l, *t, *nt;
1507         char ts[Maxservice], *attr;
1508
1509         /*
1510          *  start with the service since it's the most likely to fail
1511          *  and costs the least
1512          */
1513         if(serv == nil || ipserv(np, serv, ts, sizeof ts) == nil){
1514                 werrstr("can't translate service");
1515                 return nil;
1516         }
1517
1518         /*
1519          *  '$' means the rest of the name is an attribute that we
1520          *  need to search for
1521          */
1522         werrstr("can't translate address");
1523         if(*host == '$'){
1524                 t = nil;
1525                 attr = host+1;
1526                 l = myipinfo(db, &attr, 1);
1527                 for(nt = l; nt != nil; nt = nt->entry){
1528                         if(strcmp(nt->attr, attr) == 0)
1529                                 t = ndbconcatenate(t, iplookuphost(np, nt->val));
1530                 }
1531                 ndbfree(l);
1532         } else
1533                 t = iplookuphost(np, host);
1534
1535         return ipreorder(t);
1536 }
1537
1538
1539 /*
1540  *  translate an ip address
1541  */
1542 char*
1543 iptrans(Ndbtuple *t, Network *np, char *serv, char *rem, int fasttimeout)
1544 {
1545         char ts[Maxservice];
1546         char reply[Maxreply];
1547         char x[Maxservice];
1548         uchar ip[IPaddrlen];
1549
1550         if(strcmp(t->attr, "ip") != 0)
1551                 return nil;
1552
1553         if(serv == nil || ipserv(np, serv, ts, sizeof ts) == nil){
1554                 werrstr("can't translate service");
1555                 return nil;
1556         }
1557
1558         if(rem != nil)
1559                 snprint(x, sizeof(x), "!%s", rem);
1560         else
1561                 *x = 0;
1562
1563         if(*t->val == '*')
1564                 snprint(reply, sizeof(reply), "%s/%s/clone %s%s",
1565                         mntpt, np->net, ts, x);
1566         else {
1567                 if(parseip(ip, t->val) == -1)
1568                         return nil;
1569                 if((np->ipvers & confipvers & (isv4(ip) ? V4 : V6)) == 0)
1570                         return nil;
1571                 snprint(reply, sizeof(reply), "%s/%s/clone %I!%s%s%s",
1572                         mntpt, np->net, ip, ts, x, fasttimeout? "!fasttimeout": "");
1573         }
1574
1575         return estrdup(reply);
1576 }
1577
1578 /*
1579  *  lookup a telephone number
1580  */
1581 Ndbtuple*
1582 telcolookup(Network *np, char *host, char *serv)
1583 {
1584         Ndbtuple *t;
1585         Ndbs s;
1586
1587         USED(np, serv);
1588
1589         werrstr("can't translate address");
1590         free(ndbgetvalue(db, &s, "sys", host, "telco", &t));
1591         if(t == nil)
1592                 return ndbline(ndbnew("telco", host));
1593
1594         return ndbreorder(t, s.t);
1595 }
1596
1597 /*
1598  *  translate a telephone address
1599  */
1600 char*
1601 telcotrans(Ndbtuple *t, Network *np, char *serv, char *rem, int)
1602 {
1603         char reply[Maxreply];
1604         char x[Maxservice];
1605
1606         if(strcmp(t->attr, "telco") != 0)
1607                 return nil;
1608
1609         if(rem != nil)
1610                 snprint(x, sizeof(x), "!%s", rem);
1611         else
1612                 *x = 0;
1613         if(serv != nil)
1614                 snprint(reply, sizeof(reply), "%s/%s/clone %s!%s%s", mntpt, np->net,
1615                         t->val, serv, x);
1616         else
1617                 snprint(reply, sizeof(reply), "%s/%s/clone %s%s", mntpt, np->net,
1618                         t->val, x);
1619         return estrdup(reply);
1620 }
1621
1622 /*
1623  *  create a slave process to handle a request to avoid one request blocking
1624  *  another.  parent returns to job loop.
1625  */
1626 void
1627 slave(char *host)
1628 {
1629         if(*isslave)
1630                 return;         /* we're already a slave process */
1631         if(ainc(&active) >= Maxactive){
1632                 adec(&active);
1633                 return;
1634         }
1635         switch(rfork(RFPROC|RFNOTEG|RFMEM|RFNOWAIT)){
1636         case -1:
1637                 adec(&active);
1638                 break;
1639         case 0:
1640                 *isslave = 1;
1641                 if(debug)
1642                         syslog(0, logfile, "slave %d", getpid());
1643                 procsetname("%s", host);
1644                 break;
1645         default:
1646                 longjmp(masterjmp, 1);
1647         }
1648
1649 }
1650
1651 static int
1652 mountdns(void)
1653 {
1654         static QLock mountlock;
1655         static int mounted;
1656         char buf[128], *p;
1657         int fd;
1658
1659         if(mounted)
1660                 return 0;
1661
1662         qlock(&mountlock);
1663         snprint(buf, sizeof(buf), "%s/dns", mntpt);
1664         if(access(buf, AEXIST) == 0)
1665                 goto done;
1666         if(strcmp(mntpt, "/net") == 0)
1667                 snprint(buf, sizeof(buf), "/srv/dns");
1668         else {
1669                 snprint(buf, sizeof(buf), "/srv/dns%s", mntpt);
1670                 while((p = strchr(buf+8, '/')) != nil)
1671                         *p = '_';
1672         }
1673         if((fd = open(buf, ORDWR)) < 0){
1674 err:
1675                 qunlock(&mountlock);
1676                 return -1;      
1677         }
1678         if(mount(fd, -1, mntpt, MAFTER, "") < 0){
1679                 close(fd);
1680                 goto err;
1681         }
1682 done:
1683         mounted = 1;
1684         qunlock(&mountlock);
1685         return 0;
1686 }
1687
1688 static Ndbtuple*
1689 dnsip6lookup(char *mntpt, char *buf, Ndbtuple *t)
1690 {
1691         Ndbtuple *t6, *tt;
1692
1693         t6 = dnsquery(mntpt, buf, "ipv6");      /* lookup AAAA dns RRs */
1694         if (t6 == nil)
1695                 return t;
1696
1697         /* convert ipv6 attr to ip */
1698         for (tt = t6; tt != nil; tt = tt->entry)
1699                 if (strcmp(tt->attr, "ipv6") == 0)
1700                         strcpy(tt->attr, "ip");
1701
1702         /* append t6 list to t list */
1703         return ndbconcatenate(t, t6);
1704 }
1705
1706 /*
1707  *  call the dns process and have it try to translate a name
1708  */
1709 Ndbtuple*
1710 dnsiplookup(char *host, Ndbs *s, int ipvers)
1711 {
1712         char buf[Maxreply];
1713         Ndbtuple *t;
1714
1715         ipvers &= dnsipvers & lookipvers;
1716         if(ipvers == 0){
1717                 werrstr("no ip address");
1718                 return nil;
1719         }
1720         qunlock(&dblock);
1721         slave(host);
1722         if(*isslave == 0){
1723                 qlock(&dblock);
1724                 werrstr("too much activity");
1725                 return nil;
1726         }
1727
1728         if(mountdns() < 0){
1729                 qlock(&dblock);
1730                 return nil;
1731         }
1732
1733         if(strcmp(ipattr(host), "ip") == 0)
1734                 t = dnsquery(mntpt, host, "ptr");
1735         else {
1736                 t = nil;
1737                 if(ipvers & V4)
1738                         t = dnsquery(mntpt, host, "ip");
1739                 if(ipvers & V6)
1740                         t = dnsip6lookup(mntpt, host, t);
1741         }
1742         s->t = t;
1743
1744         if(t == nil){
1745                 rerrstr(buf, sizeof buf);
1746                 if(strstr(buf, "exist") != nil)
1747                         werrstr("can't translate address: %s", buf);
1748                 else if(strstr(buf, "dns failure") != nil)
1749                         werrstr("temporary problem: %s", buf);
1750         }
1751
1752         qlock(&dblock);
1753         return t;
1754 }
1755
1756 int
1757 qmatch(Ndbtuple *t, char **attr, char **val, int n)
1758 {
1759         int i, found;
1760         Ndbtuple *nt;
1761
1762         for(i = 1; i < n; i++){
1763                 found = 0;
1764                 for(nt = t; nt != nil; nt = nt->entry)
1765                         if(strcmp(attr[i], nt->attr) == 0)
1766                                 if(strcmp(val[i], "*") == 0
1767                                 || strcmp(val[i], nt->val) == 0){
1768                                         found = 1;
1769                                         break;
1770                                 }
1771                 if(found == 0)
1772                         break;
1773         }
1774         return i == n;
1775 }
1776
1777 void
1778 qreply(Mfile *mf, Ndbtuple *t)
1779 {
1780         Ndbtuple *nt;
1781         String *s;
1782
1783         s = s_new();
1784         for(nt = t; mf->nreply < Nreply && nt != nil; nt = nt->entry){
1785                 s_append(s, nt->attr);
1786                 s_append(s, "=");
1787                 s_append(s, nt->val);
1788
1789                 if(nt->line != nt->entry){
1790                         mf->replylen[mf->nreply] = s_len(s);
1791                         mf->reply[mf->nreply++] = estrdup(s_to_c(s));
1792                         s_restart(s);
1793                 } else
1794                         s_append(s, " ");
1795         }
1796         s_free(s);
1797 }
1798
1799 enum
1800 {
1801         Maxattr=        32,
1802 };
1803
1804 /*
1805  *  generic query lookup.  The query is of one of the following
1806  *  forms:
1807  *
1808  *  attr1=val1 attr2=val2 attr3=val3 ...
1809  *
1810  *  returns the matching tuple
1811  *
1812  *  ipinfo attr=val attr1 attr2 attr3 ...
1813  *
1814  *  is like ipinfo and returns the attr{1-n}
1815  *  associated with the ip address.
1816  */
1817 char*
1818 genquery(Mfile *mf, char *query)
1819 {
1820         int i, n;
1821         char *p;
1822         char *attr[Maxattr];
1823         char *val[Maxattr];
1824         Ndbtuple *t;
1825         Ndbs s;
1826
1827         n = getfields(query, attr, nelem(attr), 1, " ");
1828         if(n == 0)
1829                 return "bad query";
1830
1831         if(strcmp(attr[0], "ipinfo") == 0)
1832                 return ipinfoquery(mf, attr, n);
1833
1834         /* parse pairs */
1835         for(i = 0; i < n; i++){
1836                 p = strchr(attr[i], '=');
1837                 if(p == nil)
1838                         return "bad query";
1839                 *p++ = 0;
1840                 val[i] = p;
1841         }
1842
1843         /* give dns a chance */
1844         if((strcmp(attr[0], "dom") == 0 || strcmp(attr[0], "ip") == 0) && val[0]){
1845                 t = dnsiplookup(val[0], &s, lookipvers);
1846                 if(t != nil){
1847                         if(qmatch(t, attr, val, n)){
1848                                 qreply(mf, t);
1849                                 ndbfree(t);
1850                                 return nil;
1851                         }
1852                         ndbfree(t);
1853                 }
1854         }
1855
1856         /* first pair is always the key.  It can't be a '*' */
1857         t = ndbsearch(db, &s, attr[0], val[0]);
1858
1859         /* search is the and of all the pairs */
1860         while(t != nil){
1861                 if(qmatch(t, attr, val, n)){
1862                         qreply(mf, t);
1863                         ndbfree(t);
1864                         return nil;
1865                 }
1866
1867                 ndbfree(t);
1868                 t = ndbsnext(&s, attr[0], val[0]);
1869         }
1870
1871         return "no match";
1872 }
1873
1874 /*
1875  *  resolve an ip address
1876  */
1877 static Ndbtuple*
1878 ipresolve(char *attr, char *host)
1879 {
1880         Ndbtuple *t, *nt, **l;
1881
1882         t = iplookup(&network[Ntcp], host, "*");
1883         for(l = &t; *l != nil; ){
1884                 nt = *l;
1885                 if(strcmp(nt->attr, "ip") != 0){
1886                         *l = nt->entry;
1887                         nt->entry = nil;
1888                         ndbfree(nt);
1889                         continue;
1890                 }
1891                 nstrcpy(nt->attr, attr, sizeof(nt->attr));
1892                 l = &nt->entry;
1893         }
1894         return t;
1895 }
1896
1897 char*
1898 ipinfoquery(Mfile *mf, char **list, int n)
1899 {
1900         int i, nresolve;
1901         uchar resolve[Maxattr];
1902         Ndbtuple *t, *nt, **l;
1903         char *attr, *val;
1904
1905         /* skip 'ipinfo' */
1906         list++; n--;
1907
1908         if(n < 1)
1909                 return "bad query";
1910
1911         /* get search attribute=value, or assume myip */
1912         attr = *list;
1913         if((val = strchr(attr, '=')) != nil){
1914                 *val++ = 0;
1915                 list++;
1916                 n--;
1917         }else{
1918                 attr = nil;
1919                 val = nil;
1920         }
1921         if(n < 1)
1922                 return "bad query";
1923
1924
1925         /*
1926          *  don't let ndbipinfo resolve the addresses, we're
1927          *  better at it.
1928          */
1929         nresolve = 0;
1930         for(i = 0; i < n; i++)
1931                 if(*list[i] == '@'){            /* @attr=val ? */
1932                         list[i]++;
1933                         resolve[i] = 1;         /* we'll resolve it */
1934                         nresolve++;
1935                 } else
1936                         resolve[i] = 0;
1937
1938         if(attr == nil)
1939                 t = myipinfo(db, list, n);
1940         else
1941                 t = ndbipinfo(db, attr, val, list, n);
1942
1943         if(t == nil)
1944                 return "no match";
1945
1946         if(nresolve != 0){
1947                 for(l = &t; *l != nil;){
1948                         nt = *l;
1949
1950                         /* already an address? */
1951                         if(strcmp(ipattr(nt->val), "ip") == 0){
1952                                 l = &(*l)->entry;
1953                                 continue;
1954                         }
1955
1956                         /* user wants it resolved? */
1957                         for(i = 0; i < n; i++)
1958                                 if(strcmp(list[i], nt->attr) == 0)
1959                                         break;
1960                         if(i >= n || resolve[i] == 0){
1961                                 l = &(*l)->entry;
1962                                 continue;
1963                         }
1964
1965                         /* resolve address and replace entry */
1966                         *l = ipresolve(nt->attr, nt->val);
1967                         while(*l != nil)
1968                                 l = &(*l)->entry;
1969                         *l = nt->entry;
1970
1971                         nt->entry = nil;
1972                         ndbfree(nt);
1973                 }
1974
1975                 t = ndbdedup(t);
1976         }
1977
1978         /* make it all one line */
1979         t = ndbline(t);
1980
1981         qreply(mf, t);
1982
1983         return nil;
1984 }
1985
1986 void*
1987 emalloc(int size)
1988 {
1989         void *x;
1990
1991         x = malloc(size);
1992         if(x == nil)
1993                 error("out of memory");
1994         memset(x, 0, size);
1995         return x;
1996 }
1997
1998 char*
1999 estrdup(char *s)
2000 {
2001         int size;
2002         char *p;
2003
2004         size = strlen(s);
2005         p = malloc(size+1);
2006         if(p == nil)
2007                 error("out of memory");
2008         memmove(p, s, size);
2009         p[size] = 0;
2010         return p;
2011 }