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