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