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