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