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