xref: /plan9/sys/src/cmd/ndb/cs.c (revision e0d6d19cdffb15d5c5f1e7337cee05064ead1fd0)
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 		if(job->request.fid<0)
436 			error("fid out of range");
437 		lock(&dblock);
438 		mf = newfid(job->request.fid);
439 		if(debug)
440 			syslog(0, logfile, "%F", &job->request);
441 
442 
443 		switch(job->request.type){
444 		default:
445 			syslog(1, logfile, "unknown request type %d", job->request.type);
446 			break;
447 		case Tversion:
448 			rversion(job);
449 			break;
450 		case Tauth:
451 			rauth(job);
452 			break;
453 		case Tflush:
454 			rflush(job);
455 			break;
456 		case Tattach:
457 			rattach(job, mf);
458 			break;
459 		case Twalk:
460 			rwalk(job, mf);
461 			break;
462 		case Topen:
463 			ropen(job, mf);
464 			break;
465 		case Tcreate:
466 			rcreate(job, mf);
467 			break;
468 		case Tread:
469 			rread(job, mf);
470 			break;
471 		case Twrite:
472 			rwrite(job, mf);
473 			break;
474 		case Tclunk:
475 			rclunk(job, mf);
476 			break;
477 		case Tremove:
478 			rremove(job, mf);
479 			break;
480 		case Tstat:
481 			rstat(job, mf);
482 			break;
483 		case Twstat:
484 			rwstat(job, mf);
485 			break;
486 		}
487 		unlock(&dblock);
488 
489 		freejob(job);
490 
491 		/*
492 		 *  slave processes die after replying
493 		 */
494 		if(*isslave){
495 			if(debug)
496 				syslog(0, logfile, "slave death %d", getpid());
497 			_exits(0);
498 		}
499 	}
500 }
501 
502 void
503 rversion(Job *job)
504 {
505 	if(job->request.msize > IOHDRSZ + Maxfdata)
506 		job->reply.msize = IOHDRSZ + Maxfdata;
507 	else
508 		job->reply.msize = job->request.msize;
509 	if(strncmp(job->request.version, "9P2000", 6) != 0)
510 		sendmsg(job, "unknown 9P version");
511 	else{
512 		job->reply.version = "9P2000";
513 		sendmsg(job, 0);
514 	}
515 }
516 
517 void
518 rauth(Job *job)
519 {
520 	sendmsg(job, "cs: authentication not required");
521 }
522 
523 /*
524  *  don't flush till all the slaves are done
525  */
526 void
527 rflush(Job *job)
528 {
529 	flushjob(job->request.oldtag);
530 	sendmsg(job, 0);
531 }
532 
533 void
534 rattach(Job *job, Mfile *mf)
535 {
536 	if(mf->busy == 0){
537 		mf->busy = 1;
538 		mf->user = estrdup(job->request.uname);
539 	}
540 	mf->qid.vers = vers++;
541 	mf->qid.type = QTDIR;
542 	mf->qid.path = 0LL;
543 	job->reply.qid = mf->qid;
544 	sendmsg(job, 0);
545 }
546 
547 
548 char*
549 rwalk(Job *job, Mfile *mf)
550 {
551 	char *err;
552 	char **elems;
553 	int nelems;
554 	int i;
555 	Mfile *nmf;
556 	Qid qid;
557 
558 	err = 0;
559 	nmf = nil;
560 	elems = job->request.wname;
561 	nelems = job->request.nwname;
562 	job->reply.nwqid = 0;
563 
564 	if(job->request.newfid != job->request.fid){
565 		/* clone fid */
566 		if(job->request.newfid<0){
567 			err = "clone newfid out of range";
568 			goto send;
569 		}
570 		nmf = newfid(job->request.newfid);
571 		if(nmf->busy){
572 			nmf = nil;
573 			err = "clone to used channel";
574 			goto send;
575 		}
576 		*nmf = *mf;
577 		nmf->user = estrdup(mf->user);
578 		nmf->fid = job->request.newfid;
579 		nmf->qid.vers = vers++;
580 		mf = nmf;
581 	}
582 	/* else nmf will be nil */
583 
584 	qid = mf->qid;
585 	if(nelems > 0){
586 		/* walk fid */
587 		for(i=0; i<nelems && i<MAXWELEM; i++){
588 			if((qid.type & QTDIR) == 0){
589 				err = "not a directory";
590 				break;
591 			}
592 			if(strcmp(elems[i], "..") == 0 || strcmp(elems[i], ".") == 0){
593 				qid.type = QTDIR;
594 				qid.path = Qdir;
595     Found:
596 				job->reply.wqid[i] = qid;
597 				job->reply.nwqid++;
598 				continue;
599 			}
600 			if(strcmp(elems[i], "cs") == 0){
601 				qid.type = QTFILE;
602 				qid.path = Qcs;
603 				goto Found;
604 			}
605 			err = "file does not exist";
606 			break;
607 		}
608 	}
609 
610     send:
611 	if(nmf != nil && (err!=nil || job->reply.nwqid<nelems)){
612 		cleanmf(nmf);
613 		free(nmf->user);
614 		nmf->user = 0;
615 		nmf->busy = 0;
616 		nmf->fid = 0;
617 	}
618 	if(err == nil)
619 		mf->qid = qid;
620 	sendmsg(job, err);
621 	return err;
622 }
623 
624 void
625 ropen(Job *job, Mfile *mf)
626 {
627 	int mode;
628 	char *err;
629 
630 	err = 0;
631 	mode = job->request.mode;
632 	if(mf->qid.type & QTDIR){
633 		if(mode)
634 			err = "permission denied";
635 	}
636 	job->reply.qid = mf->qid;
637 	job->reply.iounit = 0;
638 	sendmsg(job, err);
639 }
640 
641 void
642 rcreate(Job *job, Mfile *mf)
643 {
644 	USED(mf);
645 	sendmsg(job, "creation permission denied");
646 }
647 
648 void
649 rread(Job *job, Mfile *mf)
650 {
651 	int i, n, cnt;
652 	long off, toff, clock;
653 	Dir dir;
654 	uchar buf[Maxfdata];
655 	char *err;
656 
657 	n = 0;
658 	err = 0;
659 	off = job->request.offset;
660 	cnt = job->request.count;
661 	if(mf->qid.type & QTDIR){
662 		clock = time(0);
663 		if(off == 0){
664 			dir.name = "cs";
665 			dir.qid.type = QTFILE;
666 			dir.qid.vers = vers;
667 			dir.qid.path = Qcs;
668 			dir.mode = 0666;
669 			dir.length = 0;
670 			dir.uid = mf->user;
671 			dir.gid = mf->user;
672 			dir.muid = mf->user;
673 			dir.atime = clock;	/* wrong */
674 			dir.mtime = clock;	/* wrong */
675 			n = convD2M(&dir, buf, sizeof buf);
676 		}
677 		job->reply.data = (char*)buf;
678 	} else {
679 		for(;;){
680 			/* look for an answer at the right offset */
681 			toff = 0;
682 			for(i = 0; mf->reply[i] && i < mf->nreply; i++){
683 				n = mf->replylen[i];
684 				if(off < toff + n)
685 					break;
686 				toff += n;
687 			}
688 			if(i < mf->nreply)
689 				break;		/* got something to return */
690 
691 			/* try looking up more answers */
692 			if(lookup(mf) == 0){
693 				/* no more */
694 				n = 0;
695 				goto send;
696 			}
697 		}
698 
699 		/* give back a single reply (or part of one) */
700 		job->reply.data = mf->reply[i] + (off - toff);
701 		if(cnt > toff - off + n)
702 			n = toff - off + n;
703 		else
704 			n = cnt;
705 	}
706 send:
707 	job->reply.count = n;
708 	sendmsg(job, err);
709 }
710 void
711 cleanmf(Mfile *mf)
712 {
713 	int i;
714 
715 	if(mf->net != nil){
716 		free(mf->net);
717 		mf->net = nil;
718 	}
719 	if(mf->host != nil){
720 		free(mf->host);
721 		mf->host = nil;
722 	}
723 	if(mf->serv != nil){
724 		free(mf->serv);
725 		mf->serv = nil;
726 	}
727 	if(mf->rem != nil){
728 		free(mf->rem);
729 		mf->rem = nil;
730 	}
731 	for(i = 0; i < mf->nreply; i++){
732 		free(mf->reply[i]);
733 		mf->reply[i] = nil;
734 		mf->replylen[i] = 0;
735 	}
736 	mf->nreply = 0;
737 	mf->nextnet = netlist;
738 }
739 
740 void
741 rwrite(Job *job, Mfile *mf)
742 {
743 	int cnt, n;
744 	char *err;
745 	char *field[4];
746 	char curerr[64];
747 
748 	err = 0;
749 	cnt = job->request.count;
750 	if(mf->qid.type & QTDIR){
751 		err = "can't write directory";
752 		goto send;
753 	}
754 	if(cnt >= Maxrequest){
755 		err = "request too long";
756 		goto send;
757 	}
758 	job->request.data[cnt] = 0;
759 
760 	/*
761 	 *  toggle debugging
762 	 */
763 	if(strncmp(job->request.data, "debug", 5)==0){
764 		debug ^= 1;
765 		syslog(1, logfile, "debug %d", debug);
766 		goto send;
767 	}
768 
769 	/*
770 	 *  toggle debugging
771 	 */
772 	if(strncmp(job->request.data, "paranoia", 8)==0){
773 		paranoia ^= 1;
774 		syslog(1, logfile, "paranoia %d", paranoia);
775 		goto send;
776 	}
777 
778 	/*
779 	 *  add networks to the default list
780 	 */
781 	if(strncmp(job->request.data, "add ", 4)==0){
782 		if(job->request.data[cnt-1] == '\n')
783 			job->request.data[cnt-1] = 0;
784 		netadd(job->request.data+4);
785 		readipinterfaces();
786 		goto send;
787 	}
788 
789 	/*
790 	 *  refresh all state
791 	 */
792 	if(strncmp(job->request.data, "refresh", 7)==0){
793 		netinit(1);
794 		goto send;
795 	}
796 
797 	/* start transaction with a clean slate */
798 	cleanmf(mf);
799 
800 	/*
801 	 *  look for a general query
802 	 */
803 	if(*job->request.data == '!'){
804 		err = genquery(mf, job->request.data+1);
805 		goto send;
806 	}
807 
808 	if(debug)
809 		syslog(0, logfile, "write %s", job->request.data);
810 	if(paranoia)
811 		syslog(0, paranoiafile, "write %s by %s", job->request.data, mf->user);
812 
813 	/*
814 	 *  break up name
815 	 */
816 	n = getfields(job->request.data, field, 4, 1, "!");
817 	switch(n){
818 	case 1:
819 		mf->net = strdup("net");
820 		mf->host = strdup(field[0]);
821 		break;
822 	case 4:
823 		mf->rem = strdup(field[3]);
824 		/* fall through */
825 	case 3:
826 		mf->serv = strdup(field[2]);
827 		/* fall through */
828 	case 2:
829 		mf->host = strdup(field[1]);
830 		mf->net = strdup(field[0]);
831 		break;
832 	}
833 
834 	/*
835 	 *  do the first net worth of lookup
836 	 */
837 	if(lookup(mf) == 0){
838 		rerrstr(curerr, sizeof curerr);
839 		err = curerr;
840 	}
841 send:
842 	job->reply.count = cnt;
843 	sendmsg(job, err);
844 }
845 
846 void
847 rclunk(Job *job, Mfile *mf)
848 {
849 	cleanmf(mf);
850 	free(mf->user);
851 	mf->user = 0;
852 	mf->busy = 0;
853 	mf->fid = 0;
854 	sendmsg(job, 0);
855 }
856 
857 void
858 rremove(Job *job, Mfile *mf)
859 {
860 	USED(mf);
861 	sendmsg(job, "remove permission denied");
862 }
863 
864 void
865 rstat(Job *job, Mfile *mf)
866 {
867 	Dir dir;
868 	uchar buf[IOHDRSZ+Maxfdata];
869 
870 	if(mf->qid.type & QTDIR){
871 		dir.name = ".";
872 		dir.mode = DMDIR|0555;
873 	} else {
874 		dir.name = "cs";
875 		dir.mode = 0666;
876 	}
877 	dir.qid = mf->qid;
878 	dir.length = 0;
879 	dir.uid = mf->user;
880 	dir.gid = mf->user;
881 	dir.muid = mf->user;
882 	dir.atime = dir.mtime = time(0);
883 	job->reply.nstat = convD2M(&dir, buf, sizeof buf);
884 	job->reply.stat = buf;
885 	sendmsg(job, 0);
886 }
887 
888 void
889 rwstat(Job *job, Mfile *mf)
890 {
891 	USED(mf);
892 	sendmsg(job, "wstat permission denied");
893 }
894 
895 void
896 sendmsg(Job *job, char *err)
897 {
898 	int n;
899 	uchar mdata[IOHDRSZ + Maxfdata];
900 	char ename[ERRMAX];
901 
902 	if(err){
903 		job->reply.type = Rerror;
904 		snprint(ename, sizeof(ename), "cs: %s", err);
905 		job->reply.ename = ename;
906 	}else{
907 		job->reply.type = job->request.type+1;
908 	}
909 	job->reply.tag = job->request.tag;
910 	n = convS2M(&job->reply, mdata, sizeof mdata);
911 	if(n == 0){
912 		syslog(1, logfile, "sendmsg convS2M of %F returns 0", &job->reply);
913 		abort();
914 	}
915 	lock(&joblock);
916 	if(job->flushed == 0)
917 		if(write(mfd[1], mdata, n)!=n)
918 			error("mount write");
919 	unlock(&joblock);
920 	if(debug)
921 		syslog(0, logfile, "%F %d", &job->reply, n);
922 }
923 
924 void
925 error(char *s)
926 {
927 	syslog(1, "cs", "%s: %r", s);
928 	_exits(0);
929 }
930 
931 static int
932 isvalidip(uchar *ip)
933 {
934 	return ipcmp(ip, IPnoaddr) != 0 && ipcmp(ip, v4prefix) != 0;
935 }
936 
937 static uchar loopbacknet[IPaddrlen] = {
938 	0, 0, 0, 0,
939 	0, 0, 0, 0,
940 	0, 0, 0xff, 0xff,
941 	127, 0, 0, 0
942 };
943 static uchar loopbackmask[IPaddrlen] = {
944 	0xff, 0xff, 0xff, 0xff,
945 	0xff, 0xff, 0xff, 0xff,
946 	0xff, 0xff, 0xff, 0xff,
947 	0xff, 0, 0, 0
948 };
949 
950 void
951 readipinterfaces(void)
952 {
953 	if(myipaddr(ipa, mntpt) != 0)
954 		ipmove(ipa, IPnoaddr);
955 	sprint(ipaddr, "%I", ipa);
956 	if (debug)
957 		syslog(0, "dns", "ipaddr is %s\n", ipaddr);
958 }
959 
960 /*
961  *  get the system name
962  */
963 void
964 ipid(void)
965 {
966 	uchar addr[6];
967 	Ndbtuple *t, *tt;
968 	char *p, *attr;
969 	Ndbs s;
970 	int f;
971 	char buf[Maxpath];
972 
973 	/* use environment, ether addr, or ipaddr to get system name */
974 	if(mysysname == 0){
975 		/*
976 		 *  environment has priority.
977 		 *
978 		 *  on the sgi power the default system name
979 		 *  is the ip address.  ignore that.
980 		 *
981 		 */
982 		p = getenv("sysname");
983 		if(p && *p){
984 			attr = ipattr(p);
985 			if(strcmp(attr, "ip") != 0)
986 				mysysname = strdup(p);
987 		}
988 
989 		/*
990 		 *  the /net/ndb contains what the network
991 		 *  figured out from DHCP.  use that name if
992 		 *  there is one.
993 		 */
994 		if(mysysname == 0 && netdb != nil){
995 			ndbreopen(netdb);
996 			for(tt = t = ndbparse(netdb); t != nil; t = t->entry){
997 				if(strcmp(t->attr, "sys") == 0){
998 					mysysname = strdup(t->val);
999 					break;
1000 				}
1001 			}
1002 			ndbfree(tt);
1003 		}
1004 
1005 		/* next network database, ip address, and ether address to find a name */
1006 		if(mysysname == 0){
1007 			t = nil;
1008 			if(isvalidip(ipa))
1009 				free(ndbgetvalue(db, &s, "ip", ipaddr, "sys", &t));
1010 			if(t == nil){
1011 				for(f = 0; f < 3; f++){
1012 					snprint(buf, sizeof buf, "%s/ether%d", mntpt, f);
1013 					if(myetheraddr(addr, buf) >= 0){
1014 						snprint(eaddr, sizeof(eaddr), "%E", addr);
1015 						free(ndbgetvalue(db, &s, "ether", eaddr, "sys", &t));
1016 						if(t != nil)
1017 							break;
1018 					}
1019 				}
1020 			}
1021 			for(tt = t; tt != nil; tt = tt->entry){
1022 				if(strcmp(tt->attr, "sys") == 0){
1023 					mysysname = strdup(tt->val);
1024 					break;
1025 				}
1026 			}
1027 			ndbfree(t);
1028 		}
1029 
1030 		/* nothing else worked, use the ip address */
1031 		if(mysysname == 0 && isvalidip(ipa))
1032 			mysysname = strdup(ipaddr);
1033 
1034 
1035 		/* set /dev/sysname if we now know it */
1036 		if(mysysname){
1037 			f = open("/dev/sysname", OWRITE);
1038 			if(f >= 0){
1039 				write(f, mysysname, strlen(mysysname));
1040 				close(f);
1041 			}
1042 		}
1043 	}
1044 }
1045 
1046 /*
1047  *  Set up a list of default networks by looking for
1048  *  /net/ * /clone.
1049  */
1050 void
1051 netinit(int background)
1052 {
1053 	char clone[Maxpath];
1054 	Network *np;
1055 	static int working;
1056 
1057 	if(background){
1058 		switch(rfork(RFPROC|RFNOTEG|RFMEM|RFNOWAIT)){
1059 		case 0:
1060 			break;
1061 		default:
1062 			return;
1063 		}
1064 		lock(&netlock);
1065 	}
1066 
1067 	/* add the mounted networks to the default list */
1068 	for(np = network; np->net; np++){
1069 		if(np->considered)
1070 			continue;
1071 		snprint(clone, sizeof(clone), "%s/%s/clone", mntpt, np->net);
1072 		if(access(clone, AEXIST) < 0)
1073 			continue;
1074 		if(netlist)
1075 			last->next = np;
1076 		else
1077 			netlist = np;
1078 		last = np;
1079 		np->next = 0;
1080 		np->considered = 1;
1081 	}
1082 
1083 	/* find out what our ip address is */
1084 	readipinterfaces();
1085 
1086 	/* set the system name if we need to, these says ip is all we have */
1087 	ipid();
1088 
1089 	if(debug)
1090 		syslog(0, logfile, "mysysname %s eaddr %s ipaddr %s ipa %I\n",
1091 			mysysname?mysysname:"???", eaddr, ipaddr, ipa);
1092 
1093 	if(background){
1094 		unlock(&netlock);
1095 		_exits(0);
1096 	}
1097 }
1098 
1099 /*
1100  *  add networks to the standard list
1101  */
1102 void
1103 netadd(char *p)
1104 {
1105 	Network *np;
1106 	char *field[12];
1107 	int i, n;
1108 
1109 	n = getfields(p, field, 12, 1, " ");
1110 	for(i = 0; i < n; i++){
1111 		for(np = network; np->net; np++){
1112 			if(strcmp(field[i], np->net) != 0)
1113 				continue;
1114 			if(np->considered)
1115 				break;
1116 			if(netlist)
1117 				last->next = np;
1118 			else
1119 				netlist = np;
1120 			last = np;
1121 			np->next = 0;
1122 			np->considered = 1;
1123 		}
1124 	}
1125 }
1126 
1127 int
1128 lookforproto(Ndbtuple *t, char *proto)
1129 {
1130 	for(; t != nil; t = t->entry)
1131 		if(strcmp(t->attr, "proto") == 0 && strcmp(t->val, proto) == 0)
1132 			return 1;
1133 	return 0;
1134 }
1135 
1136 /*
1137  *  lookup a request.  the network "net" means we should pick the
1138  *  best network to get there.
1139  */
1140 int
1141 lookup(Mfile *mf)
1142 {
1143 	Network *np;
1144 	char *cp;
1145 	Ndbtuple *nt, *t;
1146 	char reply[Maxreply];
1147 	int i, rv;
1148 	int hack;
1149 
1150 	/* open up the standard db files */
1151 	if(db == 0)
1152 		ndbinit();
1153 	if(db == 0)
1154 		error("can't open mf->network database\n");
1155 
1156 	rv = 0;
1157 
1158 	if(mf->net == nil)
1159 		return 0;	/* must have been a genquery */
1160 
1161 	if(strcmp(mf->net, "net") == 0){
1162 		/*
1163 		 *  go through set of default nets
1164 		 */
1165 		for(np = mf->nextnet; np; np = np->next){
1166 			nt = (*np->lookup)(np, mf->host, mf->serv, 1);
1167 			if(nt == nil)
1168 				continue;
1169 			hack = np->fasttimeouthack && !lookforproto(nt, np->net);
1170 			for(t = nt; mf->nreply < Nreply && t; t = t->entry){
1171 				cp = (*np->trans)(t, np, mf->serv, mf->rem, hack);
1172 				if(cp){
1173 					/* avoid duplicates */
1174 					for(i = 0; i < mf->nreply; i++)
1175 						if(strcmp(mf->reply[i], cp) == 0)
1176 							break;
1177 					if(i == mf->nreply){
1178 						/* save the reply */
1179 						mf->replylen[mf->nreply] = strlen(cp);
1180 						mf->reply[mf->nreply++] = cp;
1181 						rv++;
1182 					}
1183 				}
1184 			}
1185 			ndbfree(nt);
1186 			np = np->next;
1187 			break;
1188 		}
1189 		mf->nextnet = np;
1190 		return rv;
1191 	}
1192 
1193 	/*
1194 	 *  if not /net, we only get one lookup
1195 	 */
1196 	if(mf->nreply != 0)
1197 		return 0;
1198 	/*
1199 	 *  look for a specific network
1200 	 */
1201 	for(np = netlist; np && np->net != nil; np++){
1202 		if(np->fasttimeouthack)
1203 			continue;
1204 		if(strcmp(np->net, mf->net) == 0)
1205 			break;
1206 	}
1207 
1208 	if(np && np->net != nil){
1209 		/*
1210 		 *  known network
1211 		 */
1212 		nt = (*np->lookup)(np, mf->host, mf->serv, 1);
1213 		for(t = nt; mf->nreply < Nreply && t; t = t->entry){
1214 			cp = (*np->trans)(t, np, mf->serv, mf->rem, 0);
1215 			if(cp){
1216 				mf->replylen[mf->nreply] = strlen(cp);
1217 				mf->reply[mf->nreply++] = cp;
1218 				rv++;
1219 			}
1220 		}
1221 		ndbfree(nt);
1222 		return rv;
1223 	} else {
1224 		/*
1225 		 *  not a known network, don't translate host or service
1226 		 */
1227 		if(mf->serv)
1228 			snprint(reply, sizeof(reply), "%s/%s/clone %s!%s",
1229 				mntpt, mf->net, mf->host, mf->serv);
1230 		else
1231 			snprint(reply, sizeof(reply), "%s/%s/clone %s",
1232 				mntpt, mf->net, mf->host);
1233 		mf->reply[0] = strdup(reply);
1234 		mf->replylen[0] = strlen(reply);
1235 		mf->nreply = 1;
1236 		return 1;
1237 	}
1238 }
1239 
1240 /*
1241  *  translate an ip service name into a port number.  If it's a numeric port
1242  *  number, look for restricted access.
1243  *
1244  *  the service '*' needs no translation.
1245  */
1246 char*
1247 ipserv(Network *np, char *name, char *buf, int blen)
1248 {
1249 	char *p;
1250 	int alpha = 0;
1251 	int restr = 0;
1252 	char port[10];
1253 	Ndbtuple *t, *nt;
1254 	Ndbs s;
1255 
1256 	/* '*' means any service */
1257 	if(strcmp(name, "*")==0){
1258 		strcpy(buf, name);
1259 		return buf;
1260 	}
1261 
1262 	/*  see if it's numeric or symbolic */
1263 	port[0] = 0;
1264 	for(p = name; *p; p++){
1265 		if(isdigit(*p))
1266 			{}
1267 		else if(isalpha(*p) || *p == '-' || *p == '$')
1268 			alpha = 1;
1269 		else
1270 			return 0;
1271 	}
1272 	t = nil;
1273 	p = nil;
1274 	if(alpha){
1275 		p = ndbgetvalue(db, &s, np->net, name, "port", &t);
1276 		if(p == nil)
1277 			return 0;
1278 	} else {
1279 		/* look up only for tcp ports < 1024 to get the restricted
1280 		 * attribute
1281 		 */
1282 		if(atoi(name) < 1024 && strcmp(np->net, "tcp") == 0)
1283 			p = ndbgetvalue(db, &s, "port", name, "port", &t);
1284 		if(p == nil)
1285 			p = strdup(name);
1286 	}
1287 
1288 	if(t){
1289 		for(nt = t; nt; nt = nt->entry)
1290 			if(strcmp(nt->attr, "restricted") == 0)
1291 				restr = 1;
1292 		ndbfree(t);
1293 	}
1294 	snprint(buf, blen, "%s%s", p, restr ? "!r" : "");
1295 	free(p);
1296 
1297 	return buf;
1298 }
1299 
1300 /*
1301  *  lookup an ip attribute
1302  */
1303 int
1304 ipattrlookup(Ndb *db, char *ipa, char *attr, char *val, int vlen)
1305 {
1306 
1307 	Ndbtuple *t, *nt;
1308 	char *alist[2];
1309 
1310 	alist[0] = attr;
1311 	t = ndbipinfo(db, "ip", ipa, alist, 1);
1312 	if(t == nil)
1313 		return 0;
1314 	for(nt = t; nt != nil; nt = nt->entry){
1315 		if(strcmp(nt->attr, attr) == 0){
1316 			nstrcpy(val, nt->val, vlen);
1317 			ndbfree(t);
1318 			return 1;
1319 		}
1320 	}
1321 
1322 	/* we shouldn't get here */
1323 	ndbfree(t);
1324 	return 0;
1325 }
1326 
1327 /*
1328  *  lookup (and translate) an ip destination
1329  */
1330 Ndbtuple*
1331 iplookup(Network *np, char *host, char *serv, int nolookup)
1332 {
1333 	char *attr;
1334 	Ndbtuple *t, *nt;
1335 	Ndbs s;
1336 	char ts[Maxservice];
1337 	char dollar[Maxhost];
1338 	uchar ip[IPaddrlen];
1339 	uchar net[IPaddrlen];
1340 	uchar tnet[IPaddrlen];
1341 	Ipifc *ifc;
1342 	Iplifc *lifc;
1343 
1344 	USED(nolookup);
1345 
1346 	/*
1347 	 *  start with the service since it's the most likely to fail
1348 	 *  and costs the least
1349 	 */
1350 	werrstr("can't translate address");
1351 	if(serv==0 || ipserv(np, serv, ts, sizeof ts) == 0){
1352 		werrstr("can't translate service");
1353 		return 0;
1354 	}
1355 
1356 	/* for dial strings with no host */
1357 	if(strcmp(host, "*") == 0)
1358 		return ndbnew("ip", "*");
1359 
1360 	/*
1361 	 *  hack till we go v6 :: = 0.0.0.0
1362 	 */
1363 	if(strcmp("::", host) == 0)
1364 		return ndbnew("ip", "*");
1365 
1366 	/*
1367 	 *  '$' means the rest of the name is an attribute that we
1368 	 *  need to search for
1369 	 */
1370 	if(*host == '$'){
1371 		if(ipattrlookup(db, ipaddr, host+1, dollar, sizeof dollar))
1372 			host = dollar;
1373 	}
1374 
1375 	/*
1376 	 *  turn '[ip address]' into just 'ip address'
1377 	 */
1378 	if(*host == '[' && host[strlen(host)-1] == ']'){
1379 		host++;
1380 		host[strlen(host)-1] = 0;
1381 	}
1382 
1383 	/*
1384 	 *  just accept addresses
1385 	 */
1386 	attr = ipattr(host);
1387 	if(strcmp(attr, "ip") == 0)
1388 		return ndbnew("ip", host);
1389 
1390 	/*
1391 	 *  give the domain name server the first opportunity to
1392 	 *  resolve domain names.  if that fails try the database.
1393 	 */
1394 	t = 0;
1395 	werrstr("can't translate address");
1396 	if(strcmp(attr, "dom") == 0)
1397 		t = dnsiplookup(host, &s);
1398 	if(t == 0)
1399 		free(ndbgetvalue(db, &s, attr, host, "ip", &t));
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 < 2)
1751 		return "bad query";
1752 
1753 	/* get search attribute=value */
1754 	attr = *list++; n--;
1755 	val = strchr(attr, '=');
1756 	if(val == nil)
1757 		return "bad query";
1758 	*val++ = 0;
1759 
1760 	/*
1761 	 *  don't let ndbipinfo resolve the addresses, we're
1762 	 *  better at it.
1763 	 */
1764 	nresolve = 0;
1765 	for(i = 0; i < n; i++)
1766 		if(*list[i] == '@'){
1767 			list[i]++;
1768 			resolve[i] = 1;
1769 			nresolve++;
1770 		} else
1771 			resolve[i] = 0;
1772 
1773 	t = ndbipinfo(db, attr, val, list, n);
1774 	if(t == nil)
1775 		return "no match";
1776 
1777 	if(nresolve != 0){
1778 		for(l = &t; *l != nil;){
1779 			nt = *l;
1780 
1781 			/* already an address? */
1782 			if(strcmp(ipattr(nt->val), "ip") == 0){
1783 				l = &(*l)->entry;
1784 				continue;
1785 			}
1786 
1787 			/* user wants it resolved? */
1788 			for(i = 0; i < n; i++)
1789 				if(strcmp(list[i], nt->attr) == 0)
1790 					break;
1791 			if(i >= n || resolve[i] == 0){
1792 				l = &(*l)->entry;
1793 				continue;
1794 			}
1795 
1796 			/* resolve address and replace entry */
1797 			*l = ipresolve(nt->attr, nt->val);
1798 			while(*l != nil)
1799 				l = &(*l)->entry;
1800 			*l = nt->entry;
1801 
1802 			nt->entry = nil;
1803 			ndbfree(nt);
1804 		}
1805 	}
1806 
1807 	/* make it all one line */
1808 	for(nt = t; nt != nil; nt = nt->entry){
1809 		if(nt->entry == nil)
1810 			nt->line = t;
1811 		else
1812 			nt->line = nt->entry;
1813 	}
1814 
1815 	qreply(mf, t);
1816 
1817 	return nil;
1818 }
1819 
1820 void*
1821 emalloc(int size)
1822 {
1823 	void *x;
1824 
1825 	x = malloc(size);
1826 	if(x == nil)
1827 		abort();
1828 	memset(x, 0, size);
1829 	return x;
1830 }
1831 
1832 char*
1833 estrdup(char *s)
1834 {
1835 	int size;
1836 	char *p;
1837 
1838 	size = strlen(s)+1;
1839 	p = malloc(size);
1840 	if(p == nil)
1841 		abort();
1842 	memmove(p, s, size);
1843 	return p;
1844 }
1845