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