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