xref: /plan9/sys/src/cmd/ndb/cs.c (revision ff8c3af2f44d95267f67219afa20ba82ff6cf7e4)
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",
418 				mdata[0], mdata[1], mdata[2], mdata[3], mdata[4]);
419 			freejob(job);
420 			continue;
421 		}
422 		if(job->request.fid<0)
423 			error("fid out of range");
424 		lock(&dblock);
425 		mf = newfid(job->request.fid);
426 		if(debug)
427 			syslog(0, logfile, "%F", &job->request);
428 
429 
430 		switch(job->request.type){
431 		default:
432 			syslog(1, logfile, "unknown request type %d", job->request.type);
433 			break;
434 		case Tversion:
435 			rversion(job);
436 			break;
437 		case Tauth:
438 			rauth(job);
439 			break;
440 		case Tflush:
441 			rflush(job);
442 			break;
443 		case Tattach:
444 			rattach(job, mf);
445 			break;
446 		case Twalk:
447 			rwalk(job, mf);
448 			break;
449 		case Topen:
450 			ropen(job, mf);
451 			break;
452 		case Tcreate:
453 			rcreate(job, mf);
454 			break;
455 		case Tread:
456 			rread(job, mf);
457 			break;
458 		case Twrite:
459 			rwrite(job, mf);
460 			break;
461 		case Tclunk:
462 			rclunk(job, mf);
463 			break;
464 		case Tremove:
465 			rremove(job, mf);
466 			break;
467 		case Tstat:
468 			rstat(job, mf);
469 			break;
470 		case Twstat:
471 			rwstat(job, mf);
472 			break;
473 		}
474 		unlock(&dblock);
475 
476 		freejob(job);
477 
478 		/*
479 		 *  slave processes die after replying
480 		 */
481 		if(*isslave){
482 			if(debug)
483 				syslog(0, logfile, "slave death %d", getpid());
484 			_exits(0);
485 		}
486 	}
487 }
488 
489 void
490 rversion(Job *job)
491 {
492 	if(job->request.msize > IOHDRSZ + Maxfdata)
493 		job->reply.msize = IOHDRSZ + Maxfdata;
494 	else
495 		job->reply.msize = job->request.msize;
496 	if(strncmp(job->request.version, "9P2000", 6) != 0)
497 		sendmsg(job, "unknown 9P version");
498 	else{
499 		job->reply.version = "9P2000";
500 		sendmsg(job, 0);
501 	}
502 }
503 
504 void
505 rauth(Job *job)
506 {
507 	sendmsg(job, "cs: authentication not required");
508 }
509 
510 /*
511  *  don't flush till all the slaves are done
512  */
513 void
514 rflush(Job *job)
515 {
516 	flushjob(job->request.oldtag);
517 	sendmsg(job, 0);
518 }
519 
520 void
521 rattach(Job *job, Mfile *mf)
522 {
523 	if(mf->busy == 0){
524 		mf->busy = 1;
525 		mf->user = estrdup(job->request.uname);
526 	}
527 	mf->qid.vers = vers++;
528 	mf->qid.type = QTDIR;
529 	mf->qid.path = 0LL;
530 	job->reply.qid = mf->qid;
531 	sendmsg(job, 0);
532 }
533 
534 
535 char*
536 rwalk(Job *job, Mfile *mf)
537 {
538 	char *err;
539 	char **elems;
540 	int nelems;
541 	int i;
542 	Mfile *nmf;
543 	Qid qid;
544 
545 	err = 0;
546 	nmf = nil;
547 	elems = job->request.wname;
548 	nelems = job->request.nwname;
549 	job->reply.nwqid = 0;
550 
551 	if(job->request.newfid != job->request.fid){
552 		/* clone fid */
553 		if(job->request.newfid<0){
554 			err = "clone newfid out of range";
555 			goto send;
556 		}
557 		nmf = newfid(job->request.newfid);
558 		if(nmf->busy){
559 			nmf = nil;
560 			err = "clone to used channel";
561 			goto send;
562 		}
563 		*nmf = *mf;
564 		nmf->user = estrdup(mf->user);
565 		nmf->fid = job->request.newfid;
566 		nmf->qid.vers = vers++;
567 		mf = nmf;
568 	}
569 	/* else nmf will be nil */
570 
571 	qid = mf->qid;
572 	if(nelems > 0){
573 		/* walk fid */
574 		for(i=0; i<nelems && i<MAXWELEM; i++){
575 			if((qid.type & QTDIR) == 0){
576 				err = "not a directory";
577 				break;
578 			}
579 			if(strcmp(elems[i], "..") == 0 || strcmp(elems[i], ".") == 0){
580 				qid.type = QTDIR;
581 				qid.path = Qdir;
582     Found:
583 				job->reply.wqid[i] = qid;
584 				job->reply.nwqid++;
585 				continue;
586 			}
587 			if(strcmp(elems[i], "cs") == 0){
588 				qid.type = QTFILE;
589 				qid.path = Qcs;
590 				goto Found;
591 			}
592 			err = "file does not exist";
593 			break;
594 		}
595 	}
596 
597     send:
598 	if(nmf != nil && (err!=nil || job->reply.nwqid<nelems)){
599 		nmf->busy = 0;
600 		nmf->fid = 0;
601 	}
602 	if(err == nil)
603 		mf->qid = qid;
604 	sendmsg(job, err);
605 	return err;
606 }
607 
608 void
609 ropen(Job *job, Mfile *mf)
610 {
611 	int mode;
612 	char *err;
613 
614 	err = 0;
615 	mode = job->request.mode;
616 	if(mf->qid.type & QTDIR){
617 		if(mode)
618 			err = "permission denied";
619 	}
620 	job->reply.qid = mf->qid;
621 	job->reply.iounit = 0;
622 	sendmsg(job, err);
623 }
624 
625 void
626 rcreate(Job *job, Mfile *mf)
627 {
628 	USED(mf);
629 	sendmsg(job, "creation permission denied");
630 }
631 
632 void
633 rread(Job *job, Mfile *mf)
634 {
635 	int i, n, cnt;
636 	long off, toff, clock;
637 	Dir dir;
638 	uchar buf[Maxfdata];
639 	char *err;
640 
641 	n = 0;
642 	err = 0;
643 	off = job->request.offset;
644 	cnt = job->request.count;
645 	if(mf->qid.type & QTDIR){
646 		clock = time(0);
647 		if(off == 0){
648 			dir.name = "cs";
649 			dir.qid.type = QTFILE;
650 			dir.qid.vers = vers;
651 			dir.qid.path = Qcs;
652 			dir.mode = 0666;
653 			dir.length = 0;
654 			dir.uid = mf->user;
655 			dir.gid = mf->user;
656 			dir.muid = mf->user;
657 			dir.atime = clock;	/* wrong */
658 			dir.mtime = clock;	/* wrong */
659 			n = convD2M(&dir, buf, sizeof buf);
660 		}
661 		job->reply.data = (char*)buf;
662 	} else {
663 		for(;;){
664 			/* look for an answer at the right offset */
665 			toff = 0;
666 			for(i = 0; mf->reply[i] && i < mf->nreply; i++){
667 				n = mf->replylen[i];
668 				if(off < toff + n)
669 					break;
670 				toff += n;
671 			}
672 			if(i < mf->nreply)
673 				break;		/* got something to return */
674 
675 			/* try looking up more answers */
676 			if(lookup(mf) == 0){
677 				/* no more */
678 				n = 0;
679 				goto send;
680 			}
681 		}
682 
683 		/* give back a single reply (or part of one) */
684 		job->reply.data = mf->reply[i] + (off - toff);
685 		if(cnt > toff - off + n)
686 			n = toff - off + n;
687 		else
688 			n = cnt;
689 	}
690 send:
691 	job->reply.count = n;
692 	sendmsg(job, err);
693 }
694 void
695 cleanmf(Mfile *mf)
696 {
697 	int i;
698 
699 	if(mf->net != nil){
700 		free(mf->net);
701 		mf->net = nil;
702 	}
703 	if(mf->host != nil){
704 		free(mf->host);
705 		mf->host = nil;
706 	}
707 	if(mf->serv != nil){
708 		free(mf->serv);
709 		mf->serv = nil;
710 	}
711 	if(mf->rem != nil){
712 		free(mf->rem);
713 		mf->rem = nil;
714 	}
715 	if(mf->user != nil){
716 		free(mf->user);
717 		mf->user = nil;
718 	}
719 	for(i = 0; i < mf->nreply; i++){
720 		free(mf->reply[i]);
721 		mf->reply[i] = nil;
722 		mf->replylen[i] = 0;
723 	}
724 	mf->nreply = 0;
725 	mf->nextnet = netlist;
726 }
727 
728 void
729 rwrite(Job *job, Mfile *mf)
730 {
731 	int cnt, n;
732 	char *err;
733 	char *field[4];
734 	char curerr[64];
735 
736 	err = 0;
737 	cnt = job->request.count;
738 	if(mf->qid.type & QTDIR){
739 		err = "can't write directory";
740 		goto send;
741 	}
742 	if(cnt >= Maxrequest){
743 		err = "request too long";
744 		goto send;
745 	}
746 	job->request.data[cnt] = 0;
747 
748 	/*
749 	 *  toggle debugging
750 	 */
751 	if(strncmp(job->request.data, "debug", 5)==0){
752 		debug ^= 1;
753 		syslog(1, logfile, "debug %d", debug);
754 		goto send;
755 	}
756 
757 	/*
758 	 *  toggle debugging
759 	 */
760 	if(strncmp(job->request.data, "paranoia", 8)==0){
761 		paranoia ^= 1;
762 		syslog(1, logfile, "paranoia %d", paranoia);
763 		goto send;
764 	}
765 
766 	/*
767 	 *  add networks to the default list
768 	 */
769 	if(strncmp(job->request.data, "add ", 4)==0){
770 		if(job->request.data[cnt-1] == '\n')
771 			job->request.data[cnt-1] = 0;
772 		netadd(job->request.data+4);
773 		readipinterfaces();
774 		goto send;
775 	}
776 
777 	/*
778 	 *  refresh all state
779 	 */
780 	if(strncmp(job->request.data, "refresh", 7)==0){
781 		netinit(1);
782 		goto send;
783 	}
784 
785 	/* start transaction with a clean slate */
786 	cleanmf(mf);
787 
788 	/*
789 	 *  look for a general query
790 	 */
791 	if(*job->request.data == '!'){
792 		err = genquery(mf, job->request.data+1);
793 		goto send;
794 	}
795 
796 	if(debug)
797 		syslog(0, logfile, "write %s", job->request.data);
798 	if(paranoia)
799 		syslog(0, paranoiafile, "write %s by %s", job->request.data, mf->user);
800 
801 	/*
802 	 *  break up name
803 	 */
804 	n = getfields(job->request.data, field, 4, 1, "!");
805 	switch(n){
806 	case 1:
807 		mf->net = strdup("net");
808 		mf->host = strdup(field[0]);
809 		break;
810 	case 4:
811 		mf->rem = strdup(field[3]);
812 		/* fall through */
813 	case 3:
814 		mf->serv = strdup(field[2]);
815 		/* fall through */
816 	case 2:
817 		mf->host = strdup(field[1]);
818 		mf->net = strdup(field[0]);
819 		break;
820 	}
821 
822 	/*
823 	 *  do the first net worth of lookup
824 	 */
825 	if(lookup(mf) == 0){
826 		rerrstr(curerr, sizeof curerr);
827 		err = curerr;
828 	}
829 send:
830 	job->reply.count = cnt;
831 	sendmsg(job, err);
832 }
833 
834 void
835 rclunk(Job *job, Mfile *mf)
836 {
837 	cleanmf(mf);
838 	mf->busy = 0;
839 	mf->fid = 0;
840 	sendmsg(job, 0);
841 }
842 
843 void
844 rremove(Job *job, Mfile *mf)
845 {
846 	USED(mf);
847 	sendmsg(job, "remove permission denied");
848 }
849 
850 void
851 rstat(Job *job, Mfile *mf)
852 {
853 	Dir dir;
854 	uchar buf[IOHDRSZ+Maxfdata];
855 
856 	if(mf->qid.type & QTDIR){
857 		dir.name = ".";
858 		dir.mode = DMDIR|0555;
859 	} else {
860 		dir.name = "cs";
861 		dir.mode = 0666;
862 	}
863 	dir.qid = mf->qid;
864 	dir.length = 0;
865 	dir.uid = mf->user;
866 	dir.gid = mf->user;
867 	dir.muid = mf->user;
868 	dir.atime = dir.mtime = time(0);
869 	job->reply.nstat = convD2M(&dir, buf, sizeof buf);
870 	job->reply.stat = buf;
871 	sendmsg(job, 0);
872 }
873 
874 void
875 rwstat(Job *job, Mfile *mf)
876 {
877 	USED(mf);
878 	sendmsg(job, "wstat permission denied");
879 }
880 
881 void
882 sendmsg(Job *job, char *err)
883 {
884 	int n;
885 	uchar mdata[IOHDRSZ + Maxfdata];
886 	char ename[ERRMAX];
887 
888 	if(err){
889 		job->reply.type = Rerror;
890 		snprint(ename, sizeof(ename), "cs: %s", err);
891 		job->reply.ename = ename;
892 	}else{
893 		job->reply.type = job->request.type+1;
894 	}
895 	job->reply.tag = job->request.tag;
896 	n = convS2M(&job->reply, mdata, sizeof 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(write(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 static int
918 isvalidip(uchar *ip)
919 {
920 	return ipcmp(ip, IPnoaddr) != 0 && ipcmp(ip, v4prefix) != 0;
921 }
922 
923 static uchar loopbacknet[IPaddrlen] = {
924 	0, 0, 0, 0,
925 	0, 0, 0, 0,
926 	0, 0, 0xff, 0xff,
927 	127, 0, 0, 0
928 };
929 static uchar loopbackmask[IPaddrlen] = {
930 	0xff, 0xff, 0xff, 0xff,
931 	0xff, 0xff, 0xff, 0xff,
932 	0xff, 0xff, 0xff, 0xff,
933 	0xff, 0, 0, 0
934 };
935 
936 void
937 readipinterfaces(void)
938 {
939 	if(myipaddr(ipa, mntpt) != 0)
940 		ipmove(ipa, IPnoaddr);
941 	sprint(ipaddr, "%I", ipa);
942 	if (debug)
943 		syslog(0, "dns", "ipaddr is %s\n", ipaddr);
944 }
945 
946 /*
947  *  get the system name
948  */
949 void
950 ipid(void)
951 {
952 	uchar addr[6];
953 	Ndbtuple *t;
954 	char *p, *attr;
955 	Ndbs s;
956 	int f;
957 	char buf[Maxpath];
958 
959 
960 	/* use environment, ether addr, or ipaddr to get system name */
961 	if(*mysysname == 0){
962 		/*
963 		 *  environment has priority.
964 		 *
965 		 *  on the sgi power the default system name
966 		 *  is the ip address.  ignore that.
967 		 *
968 		 */
969 		p = getenv("sysname");
970 		if(p){
971 			attr = ipattr(p);
972 			if(strcmp(attr, "ip") != 0)
973 				strcpy(mysysname, p);
974 		}
975 
976 		/*
977 		 *  the /net/ndb contains what the network
978 		 *  figured out from DHCP.  use that name if
979 		 *  there is one.
980 		 */
981 		if(*mysysname == 0 && netdb != nil){
982 			ndbreopen(netdb);
983 			for(t = ndbparse(netdb); t != nil; t = t->entry){
984 				if(strcmp(t->attr, "sys") == 0){
985 					strcpy(mysysname, t->val);
986 					break;
987 				}
988 			}
989 			ndbfree(t);
990 		}
991 
992 		/* next network database, ip address, and ether address to find a name */
993 		if(*mysysname == 0){
994 			t = nil;
995 			if(isvalidip(ipa))
996 				t = ndbgetval(db, &s, "ip", ipaddr, "sys", mysysname);
997 			else {
998 				for(f = 0; f < 3; f++){
999 					snprint(buf, sizeof buf, "%s/ether%d", mntpt, f);
1000 					if(myetheraddr(addr, buf) >= 0){
1001 						snprint(eaddr, sizeof(eaddr), "%E", addr);
1002 						t = ndbgetval(db, &s, "ether", eaddr, "sys",
1003 							mysysname);
1004 						if(t != nil)
1005 							break;
1006 					}
1007 				}
1008 			}
1009 			ndbfree(t);
1010 		}
1011 
1012 		/* nothing else worked, use the ip address */
1013 		if(*mysysname == 0 && isvalidip(ipa))
1014 			strcpy(mysysname, ipaddr);
1015 
1016 
1017 		/* set /dev/sysname if we now know it */
1018 		if(*mysysname){
1019 			f = open("/dev/sysname", OWRITE);
1020 			if(f >= 0){
1021 				write(f, mysysname, strlen(mysysname));
1022 				close(f);
1023 			}
1024 		}
1025 	}
1026 }
1027 
1028 /*
1029  *  Set up a list of default networks by looking for
1030  *  /net/ * /clone.
1031  */
1032 void
1033 netinit(int background)
1034 {
1035 	char clone[Maxpath];
1036 	Network *np;
1037 	static int working;
1038 
1039 	if(background){
1040 		switch(rfork(RFPROC|RFNOTEG|RFMEM|RFNOWAIT)){
1041 		case 0:
1042 			break;
1043 		default:
1044 			return;
1045 		}
1046 		lock(&netlock);
1047 	}
1048 
1049 	/* add the mounted networks to the default list */
1050 	for(np = network; np->net; np++){
1051 		if(np->considered)
1052 			continue;
1053 		snprint(clone, sizeof(clone), "%s/%s/clone", mntpt, np->net);
1054 		if(access(clone, AEXIST) < 0)
1055 			continue;
1056 		if(netlist)
1057 			last->next = np;
1058 		else
1059 			netlist = np;
1060 		last = np;
1061 		np->next = 0;
1062 		np->considered = 1;
1063 	}
1064 
1065 	/* find out what our ip address is */
1066 	readipinterfaces();
1067 
1068 	/* set the system name if we need to, these says ip is all we have */
1069 	ipid();
1070 
1071 	if(debug)
1072 		syslog(0, logfile, "mysysname %s eaddr %s ipaddr %s ipa %I\n",
1073 			mysysname, eaddr, ipaddr, ipa);
1074 
1075 	if(background){
1076 		unlock(&netlock);
1077 		_exits(0);
1078 	}
1079 }
1080 
1081 /*
1082  *  add networks to the standard list
1083  */
1084 void
1085 netadd(char *p)
1086 {
1087 	Network *np;
1088 	char *field[12];
1089 	int i, n;
1090 
1091 	n = getfields(p, field, 12, 1, " ");
1092 	for(i = 0; i < n; i++){
1093 		for(np = network; np->net; np++){
1094 			if(strcmp(field[i], np->net) != 0)
1095 				continue;
1096 			if(np->considered)
1097 				break;
1098 			if(netlist)
1099 				last->next = np;
1100 			else
1101 				netlist = np;
1102 			last = np;
1103 			np->next = 0;
1104 			np->considered = 1;
1105 		}
1106 	}
1107 }
1108 
1109 /*
1110  *  make a tuple
1111  */
1112 Ndbtuple*
1113 mktuple(char *attr, char *val)
1114 {
1115 	Ndbtuple *t;
1116 
1117 	t = emalloc(sizeof(Ndbtuple));
1118 	strcpy(t->attr, attr);
1119 	strncpy(t->val, val, sizeof(t->val));
1120 	t->val[sizeof(t->val)-1] = 0;
1121 	t->line = t;
1122 	t->entry = 0;
1123 	return t;
1124 }
1125 
1126 int
1127 lookforproto(Ndbtuple *t, char *proto)
1128 {
1129 	for(; t != nil; t = t->entry)
1130 		if(strcmp(t->attr, "proto") == 0 && strcmp(t->val, proto) == 0)
1131 			return 1;
1132 	return 0;
1133 }
1134 
1135 /*
1136  *  lookup a request.  the network "net" means we should pick the
1137  *  best network to get there.
1138  */
1139 int
1140 lookup(Mfile *mf)
1141 {
1142 	Network *np;
1143 	char *cp;
1144 	Ndbtuple *nt, *t;
1145 	char reply[Maxreply];
1146 	int i, rv;
1147 	int hack;
1148 
1149 	/* open up the standard db files */
1150 	if(db == 0)
1151 		ndbinit();
1152 	if(db == 0)
1153 		error("can't open mf->network database\n");
1154 
1155 	rv = 0;
1156 
1157 	if(mf->net == nil)
1158 		return 0;	/* must have been a genquery */
1159 
1160 	if(strcmp(mf->net, "net") == 0){
1161 		/*
1162 		 *  go through set of default nets
1163 		 */
1164 		for(np = mf->nextnet; np; np = np->next){
1165 			nt = (*np->lookup)(np, mf->host, mf->serv, 1);
1166 			if(nt == nil)
1167 				continue;
1168 			hack = np->fasttimeouthack && !lookforproto(nt, np->net);
1169 			for(t = nt; mf->nreply < Nreply && t; t = t->entry){
1170 				cp = (*np->trans)(t, np, mf->serv, mf->rem, hack);
1171 				if(cp){
1172 					/* avoid duplicates */
1173 					for(i = 0; i < mf->nreply; i++)
1174 						if(strcmp(mf->reply[i], cp) == 0)
1175 							break;
1176 					if(i == mf->nreply){
1177 						/* save the reply */
1178 						mf->replylen[mf->nreply] = strlen(cp);
1179 						mf->reply[mf->nreply++] = cp;
1180 						rv++;
1181 					}
1182 				}
1183 			}
1184 			ndbfree(nt);
1185 			np = np->next;
1186 			break;
1187 		}
1188 		mf->nextnet = np;
1189 		return rv;
1190 	}
1191 
1192 	/*
1193 	 *  if not /net, we only get one lookup
1194 	 */
1195 	if(mf->nreply != 0)
1196 		return 0;
1197 
1198 	/*
1199 	 *  look for a specific network
1200 	 */
1201 	for(np = netlist; np->net != nil; np++){
1202 		if(np->fasttimeouthack)
1203 			continue;
1204 		if(strcmp(np->net, mf->net) == 0)
1205 			break;
1206 	}
1207 
1208 	if(np->net != nil){
1209 		/*
1210 		 *  known network
1211 		 */
1212 		nt = (*np->lookup)(np, mf->host, mf->serv, 1);
1213 		for(t = nt; mf->nreply < Nreply && t; t = t->entry){
1214 			cp = (*np->trans)(t, np, mf->serv, mf->rem, 0);
1215 			if(cp){
1216 				mf->replylen[mf->nreply] = strlen(cp);
1217 				mf->reply[mf->nreply++] = cp;
1218 				rv++;
1219 			}
1220 		}
1221 		ndbfree(nt);
1222 		return rv;
1223 	} else {
1224 		/*
1225 		 *  not a known network, don't translate host or service
1226 		 */
1227 		if(mf->serv)
1228 			snprint(reply, sizeof(reply), "%s/%s/clone %s!%s",
1229 				mntpt, mf->net, mf->host, mf->serv);
1230 		else
1231 			snprint(reply, sizeof(reply), "%s/%s/clone %s",
1232 				mntpt, mf->net, mf->host);
1233 		mf->reply[0] = strdup(reply);
1234 		mf->replylen[0] = strlen(reply);
1235 		mf->nreply = 1;
1236 		return 1;
1237 	}
1238 }
1239 
1240 /*
1241  *  translate an ip service name into a port number.  If it's a numeric port
1242  *  number, look for restricted access.
1243  *
1244  *  the service '*' needs no translation.
1245  */
1246 char*
1247 ipserv(Network *np, char *name, char *buf)
1248 {
1249 	char *p;
1250 	int alpha = 0;
1251 	int restr = 0;
1252 	char port[Ndbvlen];
1253 	Ndbtuple *t, *nt;
1254 	Ndbs s;
1255 
1256 	/* '*' means any service */
1257 	if(strcmp(name, "*")==0){
1258 		strcpy(buf, name);
1259 		return buf;
1260 	}
1261 
1262 	/*  see if it's numeric or symbolic */
1263 	port[0] = 0;
1264 	for(p = name; *p; p++){
1265 		if(isdigit(*p))
1266 			{}
1267 		else if(isalpha(*p) || *p == '-' || *p == '$')
1268 			alpha = 1;
1269 		else
1270 			return 0;
1271 	}
1272 	if(alpha){
1273 		t = ndbgetval(db, &s, np->net, name, "port", port);
1274 		if(t == 0)
1275 			return 0;
1276 	} else {
1277 		/* look up only for tcp ports < 1024 to get the restricted
1278 		 * attribute
1279 		 */
1280 		t = nil;
1281 		if(atoi(name) < 1024 && strcmp(np->net, "tcp") == 0)
1282 			t = ndbgetval(db, &s, "port", name, "port", port);
1283 		if(t == nil){
1284 			strncpy(port, name, sizeof(port));
1285 			port[sizeof(port)-1] = 0;
1286 		}
1287 	}
1288 
1289 	if(t){
1290 		for(nt = t; nt; nt = nt->entry)
1291 			if(strcmp(nt->attr, "restricted") == 0)
1292 				restr = 1;
1293 		ndbfree(t);
1294 	}
1295 	sprint(buf, "%s%s", port, restr ? "!r" : "");
1296 	return buf;
1297 }
1298 
1299 /*
1300  *  lookup an ip attribute
1301  */
1302 int
1303 ipattrlookup(Ndb *db, char *ipa, char *attr, char *val)
1304 {
1305 
1306 	Ndbtuple *t, *nt;
1307 	char *alist[2];
1308 
1309 	alist[0] = attr;
1310 	t = ndbipinfo(db, "ip", ipa, alist, 1);
1311 	if(t == nil)
1312 		return 0;
1313 	for(nt = t; nt != nil; nt = nt->entry){
1314 		if(strcmp(nt->attr, attr) == 0){
1315 			strcpy(val, nt->val);
1316 			ndbfree(t);
1317 			return 1;
1318 		}
1319 	}
1320 
1321 	/* we shouldn't get here */
1322 	ndbfree(t);
1323 	return 0;
1324 }
1325 
1326 /*
1327  *  lookup (and translate) an ip destination
1328  */
1329 Ndbtuple*
1330 iplookup(Network *np, char *host, char *serv, int nolookup)
1331 {
1332 	char *attr;
1333 	Ndbtuple *t, *nt;
1334 	Ndbs s;
1335 	char ts[Ndbvlen+1];
1336 	char th[Ndbvlen+1];
1337 	char dollar[Ndbvlen+1];
1338 	uchar ip[IPaddrlen];
1339 	uchar net[IPaddrlen];
1340 	uchar tnet[IPaddrlen];
1341 	Ipifc *ifc;
1342 	Iplifc *lifc;
1343 
1344 	USED(nolookup);
1345 
1346 	/*
1347 	 *  start with the service since it's the most likely to fail
1348 	 *  and costs the least
1349 	 */
1350 	werrstr("can't translate address");
1351 	if(serv==0 || ipserv(np, serv, ts) == 0){
1352 		werrstr("can't translate service");
1353 		return 0;
1354 	}
1355 
1356 	/* for dial strings with no host */
1357 	if(strcmp(host, "*") == 0)
1358 		return mktuple("ip", "*");
1359 
1360 	/*
1361 	 *  hack till we go v6 :: = 0.0.0.0
1362 	 */
1363 	if(strcmp("::", host) == 0)
1364 		return mktuple("ip", "*");
1365 
1366 	/*
1367 	 *  '$' means the rest of the name is an attribute that we
1368 	 *  need to search for
1369 	 */
1370 	if(*host == '$'){
1371 		if(ipattrlookup(db, ipaddr, host+1, dollar))
1372 			host = dollar;
1373 	}
1374 
1375 	/*
1376 	 *  turn '[ip address]' into just 'ip address'
1377 	 */
1378 	if(*host == '[' && host[strlen(host)-1] == ']'){
1379 		host++;
1380 		host[strlen(host)-1] = 0;
1381 	}
1382 
1383 	/*
1384 	 *  just accept addresses
1385 	 */
1386 	attr = ipattr(host);
1387 	if(strcmp(attr, "ip") == 0)
1388 		return mktuple("ip", host);
1389 
1390 	/*
1391 	 *  give the domain name server the first opportunity to
1392 	 *  resolve domain names.  if that fails try the database.
1393 	 */
1394 	t = 0;
1395 	werrstr("can't translate address");
1396 	if(strcmp(attr, "dom") == 0)
1397 		t = dnsiplookup(host, &s);
1398 	if(t == 0)
1399 		t = ndbgetval(db, &s, attr, host, "ip", th);
1400 	if(t == 0)
1401 		t = dnsiplookup(host, &s);
1402 	if(t == 0 && strcmp(attr, "dom") != 0)
1403 		t = dnsiplookup(host, &s);
1404 	if(t == 0)
1405 		return 0;
1406 
1407 	/*
1408 	 *  reorder the tuple to have the matched line first and
1409 	 *  save that in the request structure.
1410 	 */
1411 	t = reorder(t, s.t);
1412 
1413 	/*
1414 	 * reorder according to our interfaces
1415 	 */
1416 	lock(&ipifclock);
1417 	for(ifc = ipifcs; ifc != nil; ifc = ifc->next){
1418 		for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next){
1419 			maskip(lifc->ip, lifc->mask, net);
1420 			for(nt = t; nt; nt = nt->entry){
1421 				if(strcmp(nt->attr, "ip") != 0)
1422 					continue;
1423 				parseip(ip, nt->val);
1424 				maskip(ip, lifc->mask, tnet);
1425 				if(memcmp(net, tnet, IPaddrlen) == 0){
1426 					t = reorder(t, nt);
1427 					unlock(&ipifclock);
1428 					return t;
1429 				}
1430 			}
1431 		}
1432 	}
1433 	unlock(&ipifclock);
1434 
1435 	return t;
1436 }
1437 
1438 /*
1439  *  translate an ip address
1440  */
1441 char*
1442 iptrans(Ndbtuple *t, Network *np, char *serv, char *rem, int hack)
1443 {
1444 	char ts[Ndbvlen+1];
1445 	char reply[Maxreply];
1446 	char x[Ndbvlen+1];
1447 
1448 	if(strcmp(t->attr, "ip") != 0)
1449 		return 0;
1450 
1451 	if(serv == 0 || ipserv(np, serv, ts) == 0){
1452 		werrstr("can't translate service");
1453 		return 0;
1454 	}
1455 	if(rem != nil)
1456 		snprint(x, sizeof(x), "!%s", rem);
1457 	else
1458 		*x = 0;
1459 
1460 	if(*t->val == '*')
1461 		snprint(reply, sizeof(reply), "%s/%s/clone %s%s",
1462 			mntpt, np->net, ts, x);
1463 	else
1464 		snprint(reply, sizeof(reply), "%s/%s/clone %s!%s%s%s",
1465 			mntpt, np->net, t->val, ts, x, hack?"!fasttimeout":"");
1466 
1467 	return strdup(reply);
1468 }
1469 
1470 /*
1471  *  lookup a telephone number
1472  */
1473 Ndbtuple*
1474 telcolookup(Network *np, char *host, char *serv, int nolookup)
1475 {
1476 	Ndbtuple *t;
1477 	Ndbs s;
1478 	char th[Ndbvlen+1];
1479 
1480 	USED(np, nolookup, serv);
1481 
1482 	werrstr("can't translate address");
1483 	t = ndbgetval(db, &s, "sys", host, "telco", th);
1484 	if(t == 0)
1485 		return mktuple("telco", host);
1486 
1487 	return reorder(t, s.t);
1488 }
1489 
1490 /*
1491  *  translate a telephone address
1492  */
1493 char*
1494 telcotrans(Ndbtuple *t, Network *np, char *serv, char *rem, int)
1495 {
1496 	char reply[Maxreply];
1497 	char x[Ndbvlen+1];
1498 
1499 	if(strcmp(t->attr, "telco") != 0)
1500 		return 0;
1501 
1502 	if(rem != nil)
1503 		snprint(x, sizeof(x), "!%s", rem);
1504 	else
1505 		*x = 0;
1506 	if(serv)
1507 		snprint(reply, sizeof(reply), "%s/%s/clone %s!%s%s", mntpt, np->net,
1508 			t->val, serv, x);
1509 	else
1510 		snprint(reply, sizeof(reply), "%s/%s/clone %s%s", mntpt, np->net,
1511 			t->val, x);
1512 	return strdup(reply);
1513 }
1514 
1515 /*
1516  *  reorder the tuple to put x's line first in the entry
1517  */
1518 Ndbtuple*
1519 reorder(Ndbtuple *t, Ndbtuple *x)
1520 {
1521 	Ndbtuple *nt;
1522 	Ndbtuple *line;
1523 
1524 	/* find start of this entry's line */
1525 	for(line = x; line->entry == line->line; line = line->line)
1526 		;
1527 	line = line->line;
1528 	if(line == t)
1529 		return t;	/* already the first line */
1530 
1531 	/* remove this line and everything after it from the entry */
1532 	for(nt = t; nt->entry != line; nt = nt->entry)
1533 		;
1534 	nt->entry = 0;
1535 
1536 	/* make that the start of the entry */
1537 	for(nt = line; nt->entry; nt = nt->entry)
1538 		;
1539 	nt->entry = t;
1540 	return line;
1541 }
1542 
1543 /*
1544  *  create a slave process to handle a request to avoid one request blocking
1545  *  another.  parent returns to job loop.
1546  */
1547 void
1548 slave(void)
1549 {
1550 	if(*isslave)
1551 		return;		/* we're already a slave process */
1552 
1553 	switch(rfork(RFPROC|RFNOTEG|RFMEM|RFNOWAIT)){
1554 	case -1:
1555 		break;
1556 	case 0:
1557 		if(debug)
1558 			syslog(0, logfile, "slave %d", getpid());
1559 		*isslave = 1;
1560 		break;
1561 	default:
1562 		longjmp(masterjmp, 1);
1563 	}
1564 
1565 }
1566 
1567 /*
1568  *  call the dns process and have it try to translate a name
1569  */
1570 Ndbtuple*
1571 dnsiplookup(char *host, Ndbs *s)
1572 {
1573 	char buf[Ndbvlen + 4];
1574 	Ndbtuple *t;
1575 
1576 	unlock(&dblock);
1577 
1578 	/* save the name before starting a slave */
1579 	snprint(buf, sizeof(buf), "%s", host);
1580 
1581 	slave();
1582 
1583 	if(strcmp(ipattr(buf), "ip") == 0)
1584 		t = dnsquery(mntpt, buf, "ptr");
1585 	else
1586 		t = dnsquery(mntpt, buf, "ip");
1587 	s->t = t;
1588 
1589 	if(t == nil){
1590 		rerrstr(buf, sizeof buf);
1591 		if(strstr(buf, "exist"))
1592 			werrstr("can't translate address: %s", buf);
1593 		else if(strstr(buf, "dns failure"))
1594 			werrstr("temporary problem: %s", buf);
1595 	}
1596 
1597 	lock(&dblock);
1598 	return t;
1599 }
1600 
1601 int
1602 qmatch(Ndbtuple *t, char **attr, char **val, int n)
1603 {
1604 	int i, found;
1605 	Ndbtuple *nt;
1606 
1607 	for(i = 1; i < n; i++){
1608 		found = 0;
1609 		for(nt = t; nt; nt = nt->entry)
1610 			if(strcmp(attr[i], nt->attr) == 0)
1611 				if(strcmp(val[i], "*") == 0
1612 				|| strcmp(val[i], nt->val) == 0){
1613 					found = 1;
1614 					break;
1615 				}
1616 		if(found == 0)
1617 			break;
1618 	}
1619 	return i == n;
1620 }
1621 
1622 void
1623 qreply(Mfile *mf, Ndbtuple *t)
1624 {
1625 	int i;
1626 	Ndbtuple *nt;
1627 	char buf[2048];
1628 
1629 	buf[0] = 0;
1630 	for(nt = t; mf->nreply < Nreply && nt; nt = nt->entry){
1631 		strcat(buf, nt->attr);
1632 		strcat(buf, "=");
1633 		strcat(buf, nt->val);
1634 		i = strlen(buf);
1635 		if(nt->line != nt->entry || sizeof(buf) - i < 2*Ndbvlen+2){
1636 			mf->replylen[mf->nreply] = strlen(buf);
1637 			mf->reply[mf->nreply++] = strdup(buf);
1638 			buf[0] = 0;
1639 		} else
1640 			strcat(buf, " ");
1641 	}
1642 }
1643 
1644 enum
1645 {
1646 	Maxattr=	32,
1647 };
1648 
1649 /*
1650  *  generic query lookup.  The query is of one of the following
1651  *  forms:
1652  *
1653  *  attr1=val1 attr2=val2 attr3=val3 ...
1654  *
1655  *  returns the matching tuple
1656  *
1657  *  ipinfo attr=val attr1 attr2 attr3 ...
1658  *
1659  *  is like ipinfo and returns the attr{1-n}
1660  *  associated with the ip address.
1661  */
1662 char*
1663 genquery(Mfile *mf, char *query)
1664 {
1665 	int i, n;
1666 	char *p;
1667 	char *attr[Maxattr];
1668 	char *val[Maxattr];
1669 	Ndbtuple *t;
1670 	Ndbs s;
1671 
1672 	n = getfields(query, attr, 32, 1, " ");
1673 	if(n == 0)
1674 		return "bad query";
1675 
1676 	if(strcmp(attr[0], "ipinfo") == 0)
1677 		return ipinfoquery(mf, attr, n);
1678 
1679 	/* parse pairs */
1680 	for(i = 0; i < n; i++){
1681 		p = strchr(attr[i], '=');
1682 		if(p == 0)
1683 			return "bad query";
1684 		*p++ = 0;
1685 		val[i] = p;
1686 	}
1687 
1688 	/* give dns a chance */
1689 	if((strcmp(attr[0], "dom") == 0 || strcmp(attr[0], "ip") == 0) && val[0]){
1690 		t = dnsiplookup(val[0], &s);
1691 		if(t){
1692 			if(qmatch(t, attr, val, n)){
1693 				qreply(mf, t);
1694 				ndbfree(t);
1695 				return 0;
1696 			}
1697 			ndbfree(t);
1698 		}
1699 	}
1700 
1701 	/* first pair is always the key.  It can't be a '*' */
1702 	t = ndbsearch(db, &s, attr[0], val[0]);
1703 
1704 	/* search is the and of all the pairs */
1705 	while(t){
1706 		if(qmatch(t, attr, val, n)){
1707 			qreply(mf, t);
1708 			ndbfree(t);
1709 			return 0;
1710 		}
1711 
1712 		ndbfree(t);
1713 		t = ndbsnext(&s, attr[0], val[0]);
1714 	}
1715 
1716 	return "no match";
1717 }
1718 
1719 /*
1720  *  resolve an ip address
1721  */
1722 static Ndbtuple*
1723 ipresolve(char *attr, char *host)
1724 {
1725 	Ndbtuple *t, *nt, **l;
1726 
1727 	t = iplookup(&network[Ntcp], host, "*", 0);
1728 	for(l = &t; *l != nil; ){
1729 		nt = *l;
1730 		if(strcmp(nt->attr, "ip") != 0){
1731 			*l = nt->entry;
1732 			nt->entry = nil;
1733 			ndbfree(nt);
1734 			continue;
1735 		}
1736 		strcpy(nt->attr, attr);
1737 		l = &nt->entry;
1738 	}
1739 	return t;
1740 }
1741 
1742 char*
1743 ipinfoquery(Mfile *mf, char **list, int n)
1744 {
1745 	int i, nresolve;
1746 	int resolve[Maxattr];
1747 	Ndbtuple *t, *nt, **l;
1748 	char *attr, *val;
1749 
1750 	/* skip 'ipinfo' */
1751 	list++; n--;
1752 
1753 	if(n < 2)
1754 		return "bad query";
1755 
1756 	/* get search attribute=value */
1757 	attr = *list++; n--;
1758 	val = strchr(attr, '=');
1759 	if(val == nil)
1760 		return "bad query";
1761 	*val++ = 0;
1762 
1763 	/*
1764 	 *  don't let ndbipinfo resolve the addresses, we're
1765 	 *  better at it.
1766 	 */
1767 	nresolve = 0;
1768 	for(i = 0; i < n; i++)
1769 		if(*list[i] == '@'){
1770 			list[i]++;
1771 			resolve[i] = 1;
1772 			nresolve++;
1773 		} else
1774 			resolve[i] = 0;
1775 
1776 	t = ndbipinfo(db, attr, val, list, n);
1777 	if(t == nil)
1778 		return "no match";
1779 
1780 	if(nresolve != 0){
1781 		for(l = &t; *l != nil;){
1782 			nt = *l;
1783 
1784 			/* already an address? */
1785 			if(strcmp(ipattr(nt->val), "ip") == 0){
1786 				l = &(*l)->entry;
1787 				continue;
1788 			}
1789 
1790 			/* user wants it resolved? */
1791 			for(i = 0; i < n; i++)
1792 				if(strcmp(list[i], nt->attr) == 0)
1793 					break;
1794 			if(i >= n || resolve[i] == 0){
1795 				l = &(*l)->entry;
1796 				continue;
1797 			}
1798 
1799 			/* resolve address and replace entry */
1800 			*l = ipresolve(nt->attr, nt->val);
1801 			while(*l != nil)
1802 				l = &(*l)->entry;
1803 			*l = nt->entry;
1804 
1805 			nt->entry = nil;
1806 			ndbfree(nt);
1807 		}
1808 	}
1809 
1810 	/* make it all one line */
1811 	for(nt = t; nt != nil; nt = nt->entry){
1812 		if(nt->entry == nil)
1813 			nt->line = t;
1814 		else
1815 			nt->line = nt->entry;
1816 	}
1817 
1818 	qreply(mf, t);
1819 
1820 	return nil;
1821 }
1822 
1823 void*
1824 emalloc(int size)
1825 {
1826 	void *x;
1827 
1828 	x = malloc(size);
1829 	if(x == nil)
1830 		abort();
1831 	memset(x, 0, size);
1832 	return x;
1833 }
1834 
1835 char*
1836 estrdup(char *s)
1837 {
1838 	int size;
1839 	char *p;
1840 
1841 	size = strlen(s)+1;
1842 	p = malloc(size);
1843 	if(p == nil)
1844 		abort();
1845 	memmove(p, s, size);
1846 	return p;
1847 }
1848