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