xref: /plan9-contrib/sys/src/cmd/ndb/dns.c (revision 219b2ee8daee37f4aad58d63f21287faa8e4ffdc)
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 "dns.h"
8 
9 enum
10 {
11 	Maxrequest=		4*NAMELEN,
12 	Ncache=			8,
13 
14 	Qdns=			1,
15 };
16 
17 typedef struct Mfile	Mfile;
18 typedef struct Network	Network;
19 
20 int vers;		/* incremented each clone/attach */
21 
22 struct Mfile
23 {
24 	int		busy;
25 	char		user[NAMELEN];
26 	Qid		qid;
27 	int		fid;
28 
29 	int		tag;		/* tag of current request */
30 	RR		*rp;		/* start of reply */
31 	int		type;		/* reply type */
32 };
33 
34 Mfile	*mfile;
35 int	nmfile = 0;
36 int	mfd[2];
37 char	user[NAMELEN];
38 Fcall	*rhp;
39 Fcall	*thp;
40 int	debug;
41 
42 void	rsession(void);
43 void	rsimple(void);
44 void	rflush(int tag);
45 void	rattach(Mfile*);
46 void	rclone(Mfile*);
47 char*	rwalk(Mfile*);
48 void	rclwalk(Mfile*);
49 void	ropen(Mfile*);
50 void	rcreate(Mfile*);
51 void	rread(Mfile*);
52 void	rwrite(Mfile*, Request*);
53 void	rclunk(Mfile*);
54 void	rremove(Mfile*);
55 void	rstat(Mfile*);
56 void	rauth(void);
57 void	rwstat(Mfile*);
58 void	sendmsg(char*);
59 void	mountinit(char*);
60 void	io(void);
61 int	lookup(Mfile*, char*, char*, char*);
62 int	fillreply(Mfile*, int);
63 int	mygetfields(char*, char**, int, char);
64 
65 char *mname[]={
66 	[Tnop]		"Tnop",
67 	[Tsession]	"Tsession",
68 	[Tflush]	"Tflush",
69 	[Tattach]	"Tattach",
70 	[Tclone]	"Tclone",
71 	[Twalk]		"Twalk",
72 	[Topen]		"Topen",
73 	[Tcreate]	"Tcreate",
74 	[Tclunk]	"Tclunk",
75 	[Tread]		"Tread",
76 	[Twrite]	"Twrite",
77 	[Tremove]	"Tremove",
78 	[Tstat]		"Tstat",
79 	[Twstat]	"Twstat",
80 			0,
81 };
82 
83 char *logfile = "dns";
84 char *dbfile;
85 
86 void
87 main(int argc, char *argv[])
88 {
89 	Fcall	rhdr;
90 	Fcall	thdr;
91 	int	serve;
92 
93 
94 	serve = 0;
95 	rhp = &rhdr;
96 	thp = &thdr;
97 	ARGBEGIN{
98 	case 'd':
99 		debug = 1;
100 		break;
101 	case 'f':
102 		dbfile = ARGF();
103 		break;
104 	case 's':
105 		serve = 1;	/* serve network */
106 		break;
107 	}ARGEND
108 	USED(argc);
109 	USED(argv);
110 
111 	remove("#s/dns");
112 	unmount("/net/dns", "/net");
113 	mountinit("#s/dns");
114 
115 	fmtinstall('F', fcallconv);
116 	dninit();
117 
118 	syslog(1, logfile, "started%s%s", serve?" serving":"",
119 		debug?" debuging":"");
120 	if(serve)
121 		dnserver();
122 	io();
123 	exits(0);
124 }
125 
126 void
127 mountinit(char *service)
128 {
129 	int f;
130 	int p[2];
131 	char buf[32];
132 
133 	if(pipe(p) < 0)
134 		fatal("pipe failed");
135 	switch(rfork(RFFDG|RFPROC|RFNAMEG)){
136 	case 0:
137 		close(p[1]);
138 		break;
139 	case -1:
140 		fatal("fork failed\n");
141 	default:
142 		close(p[0]);
143 
144 		/*
145 		 *  make a /srv/dns
146 		 */
147 		f = create(service, 1, 0666);
148 		if(f < 0)
149 			fatal(service);
150 		snprint(buf, sizeof(buf), "%d", p[1]);
151 		if(write(f, buf, strlen(buf)) != strlen(buf))
152 			fatal("write %s", service);
153 		close(f);
154 
155 		/*
156 		 *  put ourselves into the file system
157 		 */
158 		if(mount(p[1], "/net", MAFTER, "") < 0)
159 			fatal("mount failed\n");
160 		_exits(0);
161 	}
162 	mfd[0] = mfd[1] = p[0];
163 }
164 
165 #define INC 4
166 Mfile*
167 newfid(int fid)
168 {
169 	Mfile *mf;
170 	Mfile *freemf;
171 	int old;
172 	Mfile *o;
173 
174 	freemf = 0;
175 	for(mf = mfile; mf < &mfile[nmfile]; mf++){
176 		if(mf->fid==fid)
177 			return mf;
178 		if(mf->busy == 0)
179 			freemf = mf;
180 	}
181 	if(freemf == 0){
182 		old = nmfile;
183 		nmfile += INC;
184 		o = mfile;
185 		mfile = realloc(mfile, nmfile*sizeof(Mfile));
186 		if(mfile == 0){
187 			fprint(2, "dns: realloc(0x%lux, %d)\n", o, nmfile*sizeof(Mfile));
188 			fatal("realloc failure");
189 		}
190 		mf = &mfile[old];
191 		memset(mf, 0, INC*sizeof(Mfile));
192 	} else
193 		mf = freemf;
194 
195 	memset(mf, 0, sizeof mfile[0]);
196 	mf->fid = fid;
197 	return mf;
198 }
199 
200 void
201 io(void)
202 {
203 	long n;
204 	Mfile *mf;
205 	char mdata[MAXFDATA + MAXMSG];
206 	Request req;
207 
208 	/*
209 	 *  a slave process is sometimes forked to wait for replies from other
210 	 *  servers.  The master process returns immediately via a longjmp
211 	 *  through 'mret'.
212 	 */
213 	setjmp(req.mret);
214 	req.isslave = 0;
215     loop:
216 	n = read(mfd[0], mdata, sizeof mdata);
217 	if(n<=0)
218 		fatal("mount read");
219 	if(convM2S(mdata, rhp, n) == 0)
220 		goto loop;
221 	if(rhp->fid<0)
222 		fatal("fid out of range");
223 	mf = newfid(rhp->fid);
224 	if(debug)
225 		syslog(0, logfile, "%F", rhp);
226 	mf->tag = rhp->tag;
227 	switch(rhp->type){
228 	default:
229 		fatal("type");
230 		break;
231 	case Tsession:
232 		rsession();
233 		break;
234 	case Tnop:
235 		rsimple();
236 		break;
237 	case Tflush:
238 		rflush(rhp->tag);
239 		break;
240 	case Tattach:
241 		rattach(mf);
242 		break;
243 	case Tclone:
244 		rclone(mf);
245 		break;
246 	case Twalk:
247 		rwalk(mf);
248 		break;
249 	case Tclwalk:
250 		rclwalk(mf);
251 		break;
252 	case Topen:
253 		ropen(mf);
254 		break;
255 	case Tcreate:
256 		rcreate(mf);
257 		break;
258 	case Tread:
259 		rread(mf);
260 		break;
261 	case Twrite:
262 		rwrite(mf, &req);
263 		break;
264 	case Tclunk:
265 		rclunk(mf);
266 		break;
267 	case Tremove:
268 		rremove(mf);
269 		break;
270 	case Tstat:
271 		rstat(mf);
272 		break;
273 	case Twstat:
274 		rwstat(mf);
275 		break;
276 	}
277 	/*
278 	 *  slave processes die after replying
279 	 */
280 	if(req.isslave)
281 		_exits(0);
282 	goto loop;
283 }
284 
285 void
286 rsession(void)
287 {
288 	memset(thp->authid, 0, sizeof(thp->authid));
289 	memset(thp->authdom, 0, sizeof(thp->authdom));
290 	memset(thp->chal, 0, sizeof(thp->chal));
291 	sendmsg(0);
292 }
293 
294 void
295 rsimple(void)
296 {
297 	sendmsg(0);
298 }
299 
300 void
301 rflush(int tag)
302 {
303 	Mfile *mf;
304 
305 	for(mf = mfile; mf < &mfile[nmfile]; mf++){
306 		if(mf->busy == 0)
307 			continue;
308 		if(mf->tag == tag){
309 			mf->tag = -1;
310 			break;
311 		}
312 	}
313 	sendmsg(0);
314 }
315 
316 void
317 rauth(void)
318 {
319 	sendmsg("Authentication failed");
320 }
321 
322 void
323 rattach(Mfile *mf)
324 {
325 	if(mf->busy == 0){
326 		mf->busy = 1;
327 		strcpy(mf->user, rhp->uname);
328 	}
329 	mf->qid.vers = vers++;
330 	mf->qid.path = CHDIR;
331 	thp->qid = mf->qid;
332 	sendmsg(0);
333 }
334 
335 void
336 rclone(Mfile *mf)
337 {
338 	Mfile *nmf;
339 	char *err=0;
340 
341 	if(rhp->newfid<0){
342 		err = "clone nfid out of range";
343 		goto send;
344 	}
345 	nmf = newfid(rhp->newfid);
346 	if(nmf->busy){
347 		err = "clone to used channel";
348 		goto send;
349 	}
350 	*nmf = *mf;
351 	nmf->fid = rhp->newfid;
352 	nmf->qid.vers = vers++;
353     send:
354 	sendmsg(err);
355 }
356 
357 void
358 rclwalk(Mfile *mf)
359 {
360 	Mfile *nmf;
361 
362 	if(rhp->newfid<0){
363 		sendmsg("clone nfid out of range");
364 		return;
365 	}
366 	nmf = newfid(rhp->newfid);
367 	if(nmf->busy){
368 		sendmsg("clone to used channel");
369 		return;
370 	}
371 	*nmf = *mf;
372 	nmf->fid = rhp->newfid;
373 	rhp->fid = rhp->newfid;
374 	nmf->qid.vers = vers++;
375 	if(rwalk(nmf))
376 		nmf->busy = 0;
377 }
378 
379 char*
380 rwalk(Mfile *mf)
381 {
382 	char *err;
383 	char *name;
384 
385 	err = 0;
386 	name = rhp->name;
387 	if((mf->qid.path & CHDIR) == 0){
388 		err = "not a directory";
389 		goto send;
390 	}
391 	if(strcmp(name, ".") == 0){
392 		mf->qid.path = CHDIR;
393 		goto send;
394 	}
395 	if(strcmp(name, "dns") == 0){
396 		mf->qid.path = Qdns;
397 		goto send;
398 	}
399 	err = "nonexistent file";
400     send:
401 	thp->qid = mf->qid;
402 	sendmsg(err);
403 	return err;
404 }
405 
406 void
407 ropen(Mfile *mf)
408 {
409 	int mode;
410 	char *err;
411 
412 	err = 0;
413 	mode = rhp->mode;
414 	if(mf->qid.path & CHDIR){
415 		if(mode)
416 			err = "permission denied";
417 	}
418     send:
419 	thp->qid = mf->qid;
420 	sendmsg(err);
421 }
422 
423 void
424 rcreate(Mfile *mf)
425 {
426 	USED(mf);
427 	sendmsg("creation permission denied");
428 }
429 
430 void
431 rread(Mfile *mf)
432 {
433 	int n, cnt, len;
434 	long toff, off, clock;
435 	Dir dir;
436 	char buf[MAXFDATA];
437 	char *err;
438 	RR *rp;
439 
440 	n = 0;
441 	err = 0;
442 	off = rhp->offset;
443 	cnt = rhp->count;
444 	if(mf->qid.path & CHDIR){
445 		if(off%DIRLEN || cnt%DIRLEN){
446 			err = "bad offset";
447 			goto send;
448 		}
449 		clock = time(0);
450 		if(off == 0){
451 			memmove(dir.name, "dns", NAMELEN);
452 			dir.qid.vers = vers;
453 			dir.qid.path = Qdns;
454 			dir.mode = 0666;
455 			dir.length = 0;
456 			dir.hlength = 0;
457 			strcpy(dir.uid, mf->user);
458 			strcpy(dir.gid, mf->user);
459 			dir.atime = clock;	/* wrong */
460 			dir.mtime = clock;	/* wrong */
461 			convD2M(&dir, buf+n);
462 			n += DIRLEN;
463 		}
464 		thp->data = buf;
465 	} else {
466 		toff = 0;
467 		n = 0;
468 		thp->data = buf;
469 		for(rp = mf->rp; rp && tsame(mf->type, rp->type); rp = rp->next){
470 			len = snprint(buf, sizeof(buf), "%R", rp);
471 			if(toff + len > off){
472 				toff = off - toff;
473 				if(cnt > len - toff)
474 					cnt = len - toff;
475 				thp->data = buf + toff;
476 				n = cnt;
477 				break;
478 			}
479 			toff += len;
480 		}
481 	}
482 send:
483 	thp->count = n;
484 	sendmsg(err);
485 }
486 
487 void
488 rwrite(Mfile *mf, Request *req)
489 {
490 	int cnt;
491 	char *err;
492 	char *atype;
493 
494 	err = 0;
495 	cnt = rhp->count;
496 	if(mf->qid.path & CHDIR){
497 		err = "can't write directory";
498 		goto send;
499 	}
500 	if(cnt >= Maxrequest){
501 		err = "request too long";
502 		goto send;
503 	}
504 	rhp->data[cnt] = 0;
505 
506 	/*
507 	 *  toggle debugging
508 	 */
509 	if(strncmp(rhp->data, "debug", 5)==0){
510 		debug ^= 1;
511 		goto send;
512 	} else if(strncmp(rhp->data, "dump", 4)==0){
513 		dndump("/lib/ndb/dnsdump");
514 		goto send;
515 	}
516 
517 	/*
518 	 *  break up request (into a name and a type)
519 	 */
520 	atype = strchr(rhp->data, ' ');
521 	if(atype == 0){
522 		err = "illegal request";
523 		goto send;
524 	} else
525 		*atype++ = 0;
526 
527 	mf->type = rrtype(atype);
528 	if(mf->type < 0){
529 		err = "unknown type";
530 		goto send;
531 	}
532 
533 	mf->rp = dnresolve(rhp->data, Cin, mf->type, req, 0);
534 	if(mf->rp == 0){
535 		err = "not found";
536 	}
537 
538 	/* don't reply if the request was flushed */
539 	if(mf->tag < 0)
540 		return;
541 
542     send:
543 	thp->count = cnt;
544 	sendmsg(err);
545 }
546 
547 void
548 rclunk(Mfile *mf)
549 {
550 	if(mf->rp){
551 		/* free resource records from the database */
552 		if(mf->rp->db)
553 			rrfreelist(mf->rp);
554 		mf->rp = 0;
555 	}
556 	mf->busy = 0;
557 	mf->fid = 0;
558 	sendmsg(0);
559 }
560 
561 void
562 rremove(Mfile *mf)
563 {
564 	USED(mf);
565 	sendmsg("remove permission denied");
566 }
567 
568 void
569 rstat(Mfile *mf)
570 {
571 	Dir dir;
572 
573 	if(mf->qid.path & CHDIR){
574 		strcpy(dir.name, ".");
575 		dir.mode = CHDIR|0555;
576 	} else {
577 		strcpy(dir.name, "dns");
578 		dir.mode = 0666;
579 	}
580 	dir.qid = mf->qid;
581 	dir.length = 0;
582 	dir.hlength = 0;
583 	strcpy(dir.uid, mf->user);
584 	strcpy(dir.gid, mf->user);
585 	dir.atime = dir.mtime = time(0);
586 	convD2M(&dir, (char*)thp->stat);
587 	sendmsg(0);
588 }
589 
590 void
591 rwstat(Mfile *mf)
592 {
593 	USED(mf);
594 	sendmsg("wstat permission denied");
595 }
596 
597 void
598 sendmsg(char *err)
599 {
600 	int n;
601 	char mdata[MAXFDATA + MAXMSG];
602 
603 	if(err){
604 		thp->type = Rerror;
605 		snprint(thp->ename, sizeof(thp->ename), "dns: %s", err);
606 	}else{
607 		thp->type = rhp->type+1;
608 		thp->fid = rhp->fid;
609 	}
610 	thp->tag = rhp->tag;
611 	if(debug)
612 		syslog(0, logfile, "%F", thp);
613 	n = convS2M(thp, mdata);
614 	if(write(mfd[1], mdata, n)!=n)
615 		fatal("mount write");
616 }
617 
618 int
619 mygetfields(char *lp, char **fields, int n, char sep)
620 {
621 	int i;
622 	char sep2=0;
623 
624 	if(sep == ' ')
625 		sep2 = '\t';
626 	for(i=0; lp && *lp && i<n; i++){
627 		if(*lp==sep || *lp==sep2)
628 			*lp++=0;
629 		if(*lp == 0)
630 			break;
631 		fields[i]=lp;
632 		while(*lp && *lp!=sep && *lp!=sep2)
633 			lp++;
634 	}
635 	return i;
636 }
637 
638 void
639 warning(char *fmt, ...)
640 {
641 	int n;
642 	char dnserr[128];
643 
644 	n = doprint(dnserr, dnserr+sizeof(dnserr), fmt, &fmt+1) - dnserr;
645 	dnserr[n] = 0;
646 	syslog(1, "dns", dnserr);
647 }
648 
649 void
650 fatal(char *fmt, ...)
651 {
652 	int n;
653 	char dnserr[128];
654 
655 	n = doprint(dnserr, dnserr+sizeof(dnserr), fmt, &fmt+1) - dnserr;
656 	dnserr[n] = 0;
657 	syslog(1, "dns", dnserr);
658 	abort();
659 }
660 
661 /*
662  *  create a slave process to handle a request to avoid one request blocking
663  *  another
664  */
665 void
666 slave(Request *req)
667 {
668 	if(req->isslave)
669 		return;		/* we're already a slave process */
670 
671 	switch(rfork(RFPROC|RFNOTEG|RFMEM|RFNOWAIT)){
672 	case -1:
673 		break;
674 	case 0:
675 		req->isslave = 1;
676 		break;
677 	default:
678 		longjmp(req->mret, 1);
679 	}
680 }
681