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