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 snprint(ipaddr, sizeof 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 }else
1201 free(cp);
1202 }
1203 }
1204 ndbfree(nt);
1205 np = np->next;
1206 break;
1207 }
1208 mf->nextnet = np;
1209 return rv;
1210 }
1211
1212 /*
1213 * if not /net, we only get one lookup
1214 */
1215 if(mf->nreply != 0)
1216 return 0;
1217 /*
1218 * look for a specific network
1219 */
1220 for(np = netlist; np && np->net != nil; np++){
1221 if(np->fasttimeouthack)
1222 continue;
1223 if(strcmp(np->net, mf->net) == 0)
1224 break;
1225 }
1226
1227 if(np && np->net != nil){
1228 /*
1229 * known network
1230 */
1231 nt = (*np->lookup)(np, mf->host, mf->serv, 1);
1232 for(t = nt; mf->nreply < Nreply && t; t = t->entry){
1233 cp = (*np->trans)(t, np, mf->serv, mf->rem, 0);
1234 if(cp){
1235 mf->replylen[mf->nreply] = strlen(cp);
1236 mf->reply[mf->nreply++] = cp;
1237 rv++;
1238 }
1239 }
1240 ndbfree(nt);
1241 return rv;
1242 } else {
1243 /*
1244 * not a known network, don't translate host or service
1245 */
1246 if(mf->serv)
1247 snprint(reply, sizeof(reply), "%s/%s/clone %s!%s",
1248 mntpt, mf->net, mf->host, mf->serv);
1249 else
1250 snprint(reply, sizeof(reply), "%s/%s/clone %s",
1251 mntpt, mf->net, mf->host);
1252 mf->reply[0] = strdup(reply);
1253 mf->replylen[0] = strlen(reply);
1254 mf->nreply = 1;
1255 return 1;
1256 }
1257 }
1258
1259 /*
1260 * translate an ip service name into a port number. If it's a numeric port
1261 * number, look for restricted access.
1262 *
1263 * the service '*' needs no translation.
1264 */
1265 char*
ipserv(Network * np,char * name,char * buf,int blen)1266 ipserv(Network *np, char *name, char *buf, int blen)
1267 {
1268 char *p;
1269 int alpha = 0;
1270 int restr = 0;
1271 char port[10];
1272 Ndbtuple *t, *nt;
1273 Ndbs s;
1274
1275 /* '*' means any service */
1276 if(strcmp(name, "*")==0){
1277 strcpy(buf, name);
1278 return buf;
1279 }
1280
1281 /* see if it's numeric or symbolic */
1282 port[0] = 0;
1283 for(p = name; *p; p++){
1284 if(isdigit(*p))
1285 {}
1286 else if(isalpha(*p) || *p == '-' || *p == '$')
1287 alpha = 1;
1288 else
1289 return 0;
1290 }
1291 t = nil;
1292 p = nil;
1293 if(alpha){
1294 p = ndbgetvalue(db, &s, np->net, name, "port", &t);
1295 if(p == nil)
1296 return 0;
1297 } else {
1298 /* look up only for tcp ports < 1024 to get the restricted
1299 * attribute
1300 */
1301 if(atoi(name) < 1024 && strcmp(np->net, "tcp") == 0)
1302 p = ndbgetvalue(db, &s, "port", name, "port", &t);
1303 if(p == nil)
1304 p = strdup(name);
1305 }
1306
1307 if(t){
1308 for(nt = t; nt; nt = nt->entry)
1309 if(strcmp(nt->attr, "restricted") == 0)
1310 restr = 1;
1311 ndbfree(t);
1312 }
1313 snprint(buf, blen, "%s%s", p, restr ? "!r" : "");
1314 free(p);
1315
1316 return buf;
1317 }
1318
1319 /*
1320 * lookup an ip attribute
1321 */
1322 int
ipattrlookup(Ndb * db,char * ipa,char * attr,char * val,int vlen)1323 ipattrlookup(Ndb *db, char *ipa, char *attr, char *val, int vlen)
1324 {
1325
1326 Ndbtuple *t, *nt;
1327 char *alist[2];
1328
1329 alist[0] = attr;
1330 t = ndbipinfo(db, "ip", ipa, alist, 1);
1331 if(t == nil)
1332 return 0;
1333 for(nt = t; nt != nil; nt = nt->entry){
1334 if(strcmp(nt->attr, attr) == 0){
1335 nstrcpy(val, nt->val, vlen);
1336 ndbfree(t);
1337 return 1;
1338 }
1339 }
1340
1341 /* we shouldn't get here */
1342 ndbfree(t);
1343 return 0;
1344 }
1345
1346 /*
1347 * lookup (and translate) an ip destination
1348 */
1349 Ndbtuple*
iplookup(Network * np,char * host,char * serv,int nolookup)1350 iplookup(Network *np, char *host, char *serv, int nolookup)
1351 {
1352 char *attr, *dnsname;
1353 Ndbtuple *t, *nt;
1354 Ndbs s;
1355 char ts[Maxservice];
1356 char dollar[Maxhost];
1357 uchar ip[IPaddrlen];
1358 uchar net[IPaddrlen];
1359 uchar tnet[IPaddrlen];
1360 Ipifc *ifc;
1361 Iplifc *lifc;
1362
1363 USED(nolookup);
1364
1365 /*
1366 * start with the service since it's the most likely to fail
1367 * and costs the least
1368 */
1369 werrstr("can't translate address");
1370 if(serv==0 || ipserv(np, serv, ts, sizeof ts) == 0){
1371 werrstr("can't translate service");
1372 return 0;
1373 }
1374
1375 /* for dial strings with no host */
1376 if(strcmp(host, "*") == 0)
1377 return ndbnew("ip", "*");
1378
1379 /*
1380 * hack till we go v6 :: = 0.0.0.0
1381 */
1382 if(strcmp("::", host) == 0)
1383 return ndbnew("ip", "*");
1384
1385 /*
1386 * '$' means the rest of the name is an attribute that we
1387 * need to search for
1388 */
1389 if(*host == '$'){
1390 if(ipattrlookup(db, ipaddr, host+1, dollar, sizeof dollar))
1391 host = dollar;
1392 }
1393
1394 /*
1395 * turn '[ip address]' into just 'ip address'
1396 */
1397 if(*host == '[' && host[strlen(host)-1] == ']'){
1398 host++;
1399 host[strlen(host)-1] = 0;
1400 }
1401
1402 /*
1403 * just accept addresses
1404 */
1405 attr = ipattr(host);
1406 if(strcmp(attr, "ip") == 0)
1407 return ndbnew("ip", host);
1408
1409 /*
1410 * give the domain name server the first opportunity to
1411 * resolve domain names. if that fails try the database.
1412 */
1413 t = 0;
1414 werrstr("can't translate address");
1415 if(strcmp(attr, "dom") == 0)
1416 t = dnsiplookup(host, &s);
1417 if(t == 0)
1418 free(ndbgetvalue(db, &s, attr, host, "ip", &t));
1419 if(t == 0){
1420 dnsname = ndbgetvalue(db, &s, attr, host, "dom", nil);
1421 if(dnsname){
1422 t = dnsiplookup(dnsname, &s);
1423 free(dnsname);
1424 }
1425 }
1426 if(t == 0)
1427 t = dnsiplookup(host, &s);
1428 if(t == 0)
1429 return 0;
1430
1431 /*
1432 * reorder the tuple to have the matched line first and
1433 * save that in the request structure.
1434 */
1435 t = reorder(t, s.t);
1436
1437 /*
1438 * reorder according to our interfaces
1439 */
1440 lock(&ipifclock);
1441 for(ifc = ipifcs; ifc != nil; ifc = ifc->next){
1442 for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next){
1443 maskip(lifc->ip, lifc->mask, net);
1444 for(nt = t; nt; nt = nt->entry){
1445 if(strcmp(nt->attr, "ip") != 0)
1446 continue;
1447 parseip(ip, nt->val);
1448 maskip(ip, lifc->mask, tnet);
1449 if(memcmp(net, tnet, IPaddrlen) == 0){
1450 t = reorder(t, nt);
1451 unlock(&ipifclock);
1452 return t;
1453 }
1454 }
1455 }
1456 }
1457 unlock(&ipifclock);
1458
1459 return t;
1460 }
1461
1462 /*
1463 * translate an ip address
1464 */
1465 char*
iptrans(Ndbtuple * t,Network * np,char * serv,char * rem,int hack)1466 iptrans(Ndbtuple *t, Network *np, char *serv, char *rem, int hack)
1467 {
1468 char ts[Maxservice];
1469 char reply[Maxreply];
1470 char x[Maxservice];
1471
1472 if(strcmp(t->attr, "ip") != 0)
1473 return 0;
1474
1475 if(serv == 0 || ipserv(np, serv, ts, sizeof ts) == 0){
1476 werrstr("can't translate service");
1477 return 0;
1478 }
1479 if(rem != nil)
1480 snprint(x, sizeof(x), "!%s", rem);
1481 else
1482 *x = 0;
1483
1484 if(*t->val == '*')
1485 snprint(reply, sizeof(reply), "%s/%s/clone %s%s",
1486 mntpt, np->net, ts, x);
1487 else
1488 snprint(reply, sizeof(reply), "%s/%s/clone %s!%s%s%s",
1489 mntpt, np->net, t->val, ts, x, hack? "!fasttimeout": "");
1490
1491 return strdup(reply);
1492 }
1493
1494 /*
1495 * lookup a telephone number
1496 */
1497 Ndbtuple*
telcolookup(Network * np,char * host,char * serv,int nolookup)1498 telcolookup(Network *np, char *host, char *serv, int nolookup)
1499 {
1500 Ndbtuple *t;
1501 Ndbs s;
1502
1503 USED(np, nolookup, serv);
1504
1505 werrstr("can't translate address");
1506 free(ndbgetvalue(db, &s, "sys", host, "telco", &t));
1507 if(t == 0)
1508 return ndbnew("telco", host);
1509
1510 return reorder(t, s.t);
1511 }
1512
1513 /*
1514 * translate a telephone address
1515 */
1516 char*
telcotrans(Ndbtuple * t,Network * np,char * serv,char * rem,int)1517 telcotrans(Ndbtuple *t, Network *np, char *serv, char *rem, int)
1518 {
1519 char reply[Maxreply];
1520 char x[Maxservice];
1521
1522 if(strcmp(t->attr, "telco") != 0)
1523 return 0;
1524
1525 if(rem != nil)
1526 snprint(x, sizeof(x), "!%s", rem);
1527 else
1528 *x = 0;
1529 if(serv)
1530 snprint(reply, sizeof(reply), "%s/%s/clone %s!%s%s", mntpt, np->net,
1531 t->val, serv, x);
1532 else
1533 snprint(reply, sizeof(reply), "%s/%s/clone %s%s", mntpt, np->net,
1534 t->val, x);
1535 return strdup(reply);
1536 }
1537
1538 /*
1539 * reorder the tuple to put x's line first in the entry
1540 */
1541 Ndbtuple*
reorder(Ndbtuple * t,Ndbtuple * x)1542 reorder(Ndbtuple *t, Ndbtuple *x)
1543 {
1544 Ndbtuple *nt;
1545 Ndbtuple *line;
1546
1547 /* find start of this entry's line */
1548 for(line = x; line->entry == line->line; line = line->line)
1549 ;
1550 line = line->line;
1551 if(line == t)
1552 return t; /* already the first line */
1553
1554 /* remove this line and everything after it from the entry */
1555 for(nt = t; nt->entry != line; nt = nt->entry)
1556 ;
1557 nt->entry = 0;
1558
1559 /* make that the start of the entry */
1560 for(nt = line; nt->entry; nt = nt->entry)
1561 ;
1562 nt->entry = t;
1563 return line;
1564 }
1565
1566 /*
1567 * create a slave process to handle a request to avoid one request blocking
1568 * another. parent returns to job loop.
1569 */
1570 void
slave(char * host)1571 slave(char *host)
1572 {
1573 if(*isslave)
1574 return; /* we're already a slave process */
1575
1576 switch(rfork(RFPROC|RFNOTEG|RFMEM|RFNOWAIT)){
1577 case -1:
1578 break;
1579 case 0:
1580 if(debug)
1581 syslog(0, logfile, "slave %d", getpid());
1582 procsetname("%s", host);
1583 *isslave = 1;
1584 break;
1585 default:
1586 longjmp(masterjmp, 1);
1587 }
1588
1589 }
1590
1591 static Ndbtuple*
dnsip6lookup(char * mntpt,char * buf,Ndbtuple * t)1592 dnsip6lookup(char *mntpt, char *buf, Ndbtuple *t)
1593 {
1594 Ndbtuple *t6, *tt;
1595
1596 t6 = dnsquery(mntpt, buf, "ipv6"); /* lookup AAAA dns RRs */
1597 if (t6 == nil)
1598 return t;
1599
1600 /* convert ipv6 attr to ip */
1601 for (tt = t6; tt != nil; tt = tt->entry)
1602 if (strcmp(tt->attr, "ipv6") == 0)
1603 strncpy(tt->attr, "ip", sizeof tt->attr - 1);
1604
1605 if (t == nil)
1606 return t6;
1607
1608 /* append t6 list to t list */
1609 for (tt = t; tt->entry != nil; tt = tt->entry)
1610 ;
1611 tt->entry = t6;
1612 return t;
1613 }
1614
1615 /*
1616 * call the dns process and have it try to translate a name
1617 */
1618 Ndbtuple*
dnsiplookup(char * host,Ndbs * s)1619 dnsiplookup(char *host, Ndbs *s)
1620 {
1621 char buf[Maxreply];
1622 Ndbtuple *t;
1623
1624 unlock(&dblock);
1625
1626 /* save the name before starting a slave */
1627 snprint(buf, sizeof(buf), "%s", host);
1628
1629 slave(host);
1630
1631 if(strcmp(ipattr(buf), "ip") == 0)
1632 t = dnsquery(mntpt, buf, "ptr");
1633 else {
1634 t = dnsquery(mntpt, buf, "ip");
1635 /* special case: query ipv6 (AAAA dns RR) too */
1636 if (ipv6lookups)
1637 t = dnsip6lookup(mntpt, buf, t);
1638 }
1639 s->t = t;
1640
1641 if(t == nil){
1642 rerrstr(buf, sizeof buf);
1643 if(strstr(buf, "exist"))
1644 werrstr("can't translate address: %s", buf);
1645 else if(strstr(buf, "dns failure"))
1646 werrstr("temporary problem: %s", buf);
1647 }
1648
1649 lock(&dblock);
1650 return t;
1651 }
1652
1653 int
qmatch(Ndbtuple * t,char ** attr,char ** val,int n)1654 qmatch(Ndbtuple *t, char **attr, char **val, int n)
1655 {
1656 int i, found;
1657 Ndbtuple *nt;
1658
1659 for(i = 1; i < n; i++){
1660 found = 0;
1661 for(nt = t; nt; nt = nt->entry)
1662 if(strcmp(attr[i], nt->attr) == 0)
1663 if(strcmp(val[i], "*") == 0
1664 || strcmp(val[i], nt->val) == 0){
1665 found = 1;
1666 break;
1667 }
1668 if(found == 0)
1669 break;
1670 }
1671 return i == n;
1672 }
1673
1674 void
qreply(Mfile * mf,Ndbtuple * t)1675 qreply(Mfile *mf, Ndbtuple *t)
1676 {
1677 Ndbtuple *nt;
1678 String *s;
1679
1680 s = s_new();
1681 for(nt = t; mf->nreply < Nreply && nt; nt = nt->entry){
1682 s_append(s, nt->attr);
1683 s_append(s, "=");
1684 s_append(s, nt->val);
1685
1686 if(nt->line != nt->entry){
1687 mf->replylen[mf->nreply] = s_len(s);
1688 mf->reply[mf->nreply++] = strdup(s_to_c(s));
1689 s_restart(s);
1690 } else
1691 s_append(s, " ");
1692 }
1693 s_free(s);
1694 }
1695
1696 enum
1697 {
1698 Maxattr= 32,
1699 };
1700
1701 /*
1702 * generic query lookup. The query is of one of the following
1703 * forms:
1704 *
1705 * attr1=val1 attr2=val2 attr3=val3 ...
1706 *
1707 * returns the matching tuple
1708 *
1709 * ipinfo attr=val attr1 attr2 attr3 ...
1710 *
1711 * is like ipinfo and returns the attr{1-n}
1712 * associated with the ip address.
1713 */
1714 char*
genquery(Mfile * mf,char * query)1715 genquery(Mfile *mf, char *query)
1716 {
1717 int i, n;
1718 char *p;
1719 char *attr[Maxattr];
1720 char *val[Maxattr];
1721 Ndbtuple *t;
1722 Ndbs s;
1723
1724 n = getfields(query, attr, nelem(attr), 1, " ");
1725 if(n == 0)
1726 return "bad query";
1727
1728 if(strcmp(attr[0], "ipinfo") == 0)
1729 return ipinfoquery(mf, attr, n);
1730
1731 /* parse pairs */
1732 for(i = 0; i < n; i++){
1733 p = strchr(attr[i], '=');
1734 if(p == 0)
1735 return "bad query";
1736 *p++ = 0;
1737 val[i] = p;
1738 }
1739
1740 /* give dns a chance */
1741 if((strcmp(attr[0], "dom") == 0 || strcmp(attr[0], "ip") == 0) && val[0]){
1742 t = dnsiplookup(val[0], &s);
1743 if(t){
1744 if(qmatch(t, attr, val, n)){
1745 qreply(mf, t);
1746 ndbfree(t);
1747 return 0;
1748 }
1749 ndbfree(t);
1750 }
1751 }
1752
1753 /* first pair is always the key. It can't be a '*' */
1754 t = ndbsearch(db, &s, attr[0], val[0]);
1755
1756 /* search is the and of all the pairs */
1757 while(t){
1758 if(qmatch(t, attr, val, n)){
1759 qreply(mf, t);
1760 ndbfree(t);
1761 return 0;
1762 }
1763
1764 ndbfree(t);
1765 t = ndbsnext(&s, attr[0], val[0]);
1766 }
1767
1768 return "no match";
1769 }
1770
1771 /*
1772 * resolve an ip address
1773 */
1774 static Ndbtuple*
ipresolve(char * attr,char * host)1775 ipresolve(char *attr, char *host)
1776 {
1777 Ndbtuple *t, *nt, **l;
1778
1779 t = iplookup(&network[Ntcp], host, "*", 0);
1780 for(l = &t; *l != nil; ){
1781 nt = *l;
1782 if(strcmp(nt->attr, "ip") != 0){
1783 *l = nt->entry;
1784 nt->entry = nil;
1785 ndbfree(nt);
1786 continue;
1787 }
1788 strcpy(nt->attr, attr);
1789 l = &nt->entry;
1790 }
1791 return t;
1792 }
1793
1794 char*
ipinfoquery(Mfile * mf,char ** list,int n)1795 ipinfoquery(Mfile *mf, char **list, int n)
1796 {
1797 int i, nresolve;
1798 int resolve[Maxattr];
1799 Ndbtuple *t, *nt, **l;
1800 char *attr, *val;
1801
1802 /* skip 'ipinfo' */
1803 list++; n--;
1804
1805 if(n < 1)
1806 return "bad query";
1807
1808 /* get search attribute=value, or assume ip=myipaddr */
1809 attr = *list;
1810 if((val = strchr(attr, '=')) != nil){
1811 *val++ = 0;
1812 list++;
1813 n--;
1814 }else{
1815 attr = "ip";
1816 val = ipaddr;
1817 }
1818
1819 if(n < 1)
1820 return "bad query";
1821
1822 /*
1823 * don't let ndbipinfo resolve the addresses, we're
1824 * better at it.
1825 */
1826 nresolve = 0;
1827 for(i = 0; i < n; i++)
1828 if(*list[i] == '@'){ /* @attr=val ? */
1829 list[i]++;
1830 resolve[i] = 1; /* we'll resolve it */
1831 nresolve++;
1832 } else
1833 resolve[i] = 0;
1834
1835 t = ndbipinfo(db, attr, val, list, n);
1836 if(t == nil)
1837 return "no match";
1838
1839 if(nresolve != 0){
1840 for(l = &t; *l != nil;){
1841 nt = *l;
1842
1843 /* already an address? */
1844 if(strcmp(ipattr(nt->val), "ip") == 0){
1845 l = &(*l)->entry;
1846 continue;
1847 }
1848
1849 /* user wants it resolved? */
1850 for(i = 0; i < n; i++)
1851 if(strcmp(list[i], nt->attr) == 0)
1852 break;
1853 if(i >= n || resolve[i] == 0){
1854 l = &(*l)->entry;
1855 continue;
1856 }
1857
1858 /* resolve address and replace entry */
1859 *l = ipresolve(nt->attr, nt->val);
1860 while(*l != nil)
1861 l = &(*l)->entry;
1862 *l = nt->entry;
1863
1864 nt->entry = nil;
1865 ndbfree(nt);
1866 }
1867 }
1868
1869 /* make it all one line */
1870 for(nt = t; nt != nil; nt = nt->entry){
1871 if(nt->entry == nil)
1872 nt->line = t;
1873 else
1874 nt->line = nt->entry;
1875 }
1876
1877 qreply(mf, t);
1878
1879 return nil;
1880 }
1881
1882 void*
emalloc(int size)1883 emalloc(int size)
1884 {
1885 void *x;
1886
1887 x = malloc(size);
1888 if(x == nil)
1889 abort();
1890 memset(x, 0, size);
1891 return x;
1892 }
1893
1894 char*
estrdup(char * s)1895 estrdup(char *s)
1896 {
1897 int size;
1898 char *p;
1899
1900 size = strlen(s)+1;
1901 p = malloc(size);
1902 if(p == nil)
1903 abort();
1904 memmove(p, s, size);
1905 return p;
1906 }
1907