xref: /plan9-contrib/sys/src/cmd/ndb/cs.c (revision cc2a5f79738a9406bc9e2aff71cbf406fce52b91)
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