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