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