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