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