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