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