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