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