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