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