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