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