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