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