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