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