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