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