xref: /plan9/sys/src/cmd/9nfs/server.c (revision f9e1cf08d3be51592e03e639fc848a68dc31a55e)
1 #include "all.h"
2 #include <ndb.h>
3 #include <ip.h>
4 
5 static int	alarmflag;
6 
7 static int	Iconv(Fmt*);
8 static void	openudp(int);
9 static void	cachereply(Rpccall*, void*, int);
10 static int	replycache(int, Rpccall*, long (*)(int, void*, long));
11 static void	udpserver(int, Progmap*);
12 static void	tcpserver(int, Progmap*);
13 static void	getendpoints(Udphdr*, char*);
14 static long	readtcp(int, void*, long);
15 static long	writetcp(int, void*, long);
16 static int	servemsg(int, long (*)(int, void*, long), long (*)(int, void*, long),
17 		int, Progmap*);
18 void	(*rpcalarm)(void);
19 int	rpcdebug;
20 int	rejectall;
21 int	p9debug;
22 
23 int	nocache;
24 
25 uchar	buf[9000];
26 uchar	rbuf[9000];
27 uchar	resultbuf[9000];
28 
29 static int tcp;
30 
31 char *commonopts = "[-9CDrtv]";			/* for usage() messages */
32 
33 /*
34  * this recognises common, nominally rcp-related options.
35  * they may not take arguments.
36  */
37 int
38 argopt(int c)
39 {
40 	switch(c){
41 	case '9':
42 		++p9debug;
43 		return 0;
44 	case 'C':
45 		++nocache;
46 		return 0;
47 	case 'D':
48 		++rpcdebug;
49 		return 0;
50 	case 'r':
51 		++rejectall;
52 		return 0;
53 	case 't':
54 		tcp = 1;
55 		return 0;
56 	case 'v':
57 		++chatty;
58 		return 0;
59 	default:
60 		return -1;
61 	}
62 }
63 
64 /*
65  * all option parsing is now done in (*pg->init)(), which can call back
66  * here to argopt for common options.
67  */
68 void
69 server(int argc, char **argv, int myport, Progmap *progmap)
70 {
71 	Progmap *pg;
72 
73 	fmtinstall('I', Iconv);
74 	fmtinstall('F', fcallfmt);
75 	fmtinstall('D', dirfmt);
76 
77 	switch(rfork(RFNOWAIT|RFENVG|RFNAMEG|RFNOTEG|RFFDG|RFPROC)){
78 	case -1:
79 		panic("fork");
80 	default:
81 		_exits(0);
82 	case 0:
83 		break;
84 	}
85 
86 	switch(rfork(RFMEM|RFPROC)){
87 	case 0:
88 		for(;;){
89 			sleep(30*1000);
90 			alarmflag = 1;
91 		}
92 	case -1:
93 		sysfatal("rfork: %r");
94 	}
95 
96 	for(pg=progmap; pg->init; pg++)
97 		(*pg->init)(argc, argv);
98 	if(tcp)
99 		tcpserver(myport, progmap);
100 	else
101 		udpserver(myport, progmap);
102 }
103 
104 static void
105 udpserver(int myport, Progmap *progmap)
106 {
107 	char service[128];
108 	char data[128];
109 	char devdir[40];
110 	int ctlfd, datafd;
111 
112 	snprint(service, sizeof service, "udp!*!%d", myport);
113 	ctlfd = announce(service, devdir);
114 	if(ctlfd < 0)
115 		panic("can't announce %s: %r\n", service);
116 	if(fprint(ctlfd, "headers") < 0)
117 		panic("can't set header mode: %r\n");
118 
119 	snprint(data, sizeof data, "%s/data", devdir);
120 	datafd = open(data, ORDWR);
121 	if(datafd < 0)
122 		panic("can't open udp data: %r\n");
123 	close(ctlfd);
124 
125 	chatsrv(0);
126 	clog("%s: listening to port %d\n", argv0, myport);
127 	while (servemsg(datafd, read, write, myport, progmap) >= 0)
128 		continue;
129 	exits(0);
130 }
131 
132 static void
133 tcpserver(int myport, Progmap *progmap)
134 {
135 	char adir[40];
136 	char ldir[40];
137 	char ds[40];
138 	int actl, lctl, data;
139 
140 	snprint(ds, sizeof ds, "tcp!*!%d", myport);
141 	chatsrv(0);
142 	actl = -1;
143 	for(;;){
144 		if(actl < 0){
145 			actl = announce(ds, adir);
146 			if(actl < 0){
147 				clog("%s: listening to tcp port %d\n", argv0, myport);
148 				clog("announcing: %r");
149 				break;
150 			}
151 		}
152 		lctl = listen(adir, ldir);
153 		if(lctl < 0){
154 			close(actl);
155 			actl = -1;
156 			continue;
157 		}
158 		switch(fork()){
159 		case -1:
160 			clog("%s!%d: %r\n", argv0, myport);
161 			/* fall through */
162 		default:
163 			close(lctl);
164 			continue;
165 		case 0:
166 			close(actl);
167 			data = accept(lctl, ldir);
168 			close(lctl);
169 			if(data < 0)
170 				exits(0);
171 
172 			getendpoints((Udphdr*)buf, ldir);
173 
174 			for(;;){
175 				if(servemsg(data, readtcp, writetcp, myport, progmap) < 0)
176 					break;
177 			}
178 			close(data);
179 			exits(0);
180 		}
181 	}
182 	exits(0);
183 }
184 
185 static int
186 servemsg(int fd, long (*readmsg)(int, void*, long), long (*writemsg)(int, void*, long),
187 		int myport, Progmap * progmap)
188 {
189 	int i, n, nreply;
190 	Rpccall rcall, rreply;
191 	int vlo, vhi;
192 	Progmap *pg;
193 	Procmap *pp;
194 	char errbuf[ERRMAX];
195 
196 	if(alarmflag){
197 		alarmflag = 0;
198 		if(rpcalarm)
199 			(*rpcalarm)();
200 	}
201 	n = (*readmsg)(fd, buf, sizeof buf);
202 	if(n < 0){
203 		errstr(errbuf, sizeof errbuf);
204 		if(strcmp(errbuf, "interrupted") == 0)
205 			return 0;
206 		clog("port %d: error: %s\n", myport, errbuf);
207 		return -1;
208 	}
209 	if(n == 0){
210 		clog("port %d: EOF\n", myport);
211 		return -1;
212 	}
213 	if(rpcdebug == 1)
214 		fprint(2, "%s: rpc from %d.%d.%d.%d/%d\n",
215 			argv0, buf[12], buf[13], buf[14], buf[15],
216 			(buf[32]<<8)|buf[33]);
217 	i = rpcM2S(buf, &rcall, n);
218 	if(i != 0){
219 		clog("udp port %d: message format error %d\n",
220 			myport, i);
221 		return 0;
222 	}
223 	if(rpcdebug > 1)
224 		rpcprint(2, &rcall);
225 	if(rcall.mtype != CALL)
226 		return 0;
227 	if(replycache(fd, &rcall, writemsg))
228 		return 0;
229 	nreply = 0;
230 	rreply.host = rcall.host;
231 	rreply.port = rcall.port;
232 	rreply.lhost = rcall.lhost;
233 	rreply.lport = rcall.lport;
234 	rreply.xid = rcall.xid;
235 	rreply.mtype = REPLY;
236 	if(rcall.rpcvers != 2){
237 		rreply.stat = MSG_DENIED;
238 		rreply.rstat = RPC_MISMATCH;
239 		rreply.rlow = 2;
240 		rreply.rhigh = 2;
241 		goto send_reply;
242 	}
243 	if(rejectall){
244 		rreply.stat = MSG_DENIED;
245 		rreply.rstat = AUTH_ERROR;
246 		rreply.authstat = AUTH_TOOWEAK;
247 		goto send_reply;
248 	}
249 	i = n - (((uchar *)rcall.args) - buf);
250 	if(rpcdebug > 1)
251 		fprint(2, "arg size = %d\n", i);
252 	rreply.stat = MSG_ACCEPTED;
253 	rreply.averf.flavor = 0;
254 	rreply.averf.count = 0;
255 	rreply.results = resultbuf;
256 	vlo = 0x7fffffff;
257 	vhi = -1;
258 	for(pg=progmap; pg->pmap; pg++){
259 		if(pg->progno != rcall.prog)
260 			continue;
261 		if(pg->vers == rcall.vers)
262 			break;
263 		if(pg->vers < vlo)
264 			vlo = pg->vers;
265 		if(pg->vers > vhi)
266 			vhi = pg->vers;
267 	}
268 	if(pg->pmap == 0){
269 		if(vhi < 0)
270 			rreply.astat = PROG_UNAVAIL;
271 		else{
272 			rreply.astat = PROG_MISMATCH;
273 			rreply.plow = vlo;
274 			rreply.phigh = vhi;
275 		}
276 		goto send_reply;
277 	}
278 	for(pp = pg->pmap; pp->procp; pp++)
279 		if(rcall.proc == pp->procno){
280 			if(rpcdebug > 1)
281 				fprint(2, "process %d\n", pp->procno);
282 			rreply.astat = SUCCESS;
283 			nreply = (*pp->procp)(i, &rcall, &rreply);
284 			goto send_reply;
285 		}
286 	rreply.astat = PROC_UNAVAIL;
287 send_reply:
288 	if(nreply >= 0){
289 		i = rpcS2M(&rreply, nreply, rbuf);
290 		if(rpcdebug > 1)
291 			rpcprint(2, &rreply);
292 		(*writemsg)(fd, rbuf, i);
293 		cachereply(&rreply, rbuf, i);
294 	}
295 	return 0;
296 }
297 
298 static void
299 getendpoint(char *dir, char *file, uchar *addr, uchar *port)
300 {
301 	int fd, n;
302 	char buf[128];
303 	char *sys, *serv;
304 
305 	sys = serv = 0;
306 
307 	snprint(buf, sizeof buf, "%s/%s", dir, file);
308 	fd = open(buf, OREAD);
309 	if(fd >= 0){
310 		n = read(fd, buf, sizeof(buf)-1);
311 		if(n>0){
312 			buf[n-1] = 0;
313 			serv = strchr(buf, '!');
314 			if(serv){
315 				*serv++ = 0;
316 				serv = strdup(serv);
317 			}
318 			sys = strdup(buf);
319 		}
320 		close(fd);
321 	}
322 	if(serv == 0)
323 		serv = strdup("unknown");
324 	if(sys == 0)
325 		sys = strdup("unknown");
326 	parseip(addr, sys);
327 	n = atoi(serv);
328 	hnputs(port, n);
329 }
330 
331 static void
332 getendpoints(Udphdr *ep, char *dir)
333 {
334 	getendpoint(dir, "local", ep->laddr, ep->lport);
335 	getendpoint(dir, "remote", ep->raddr, ep->rport);
336 }
337 
338 static long
339 readtcp(int fd, void *vbuf, long blen)
340 {
341 	uchar mk[4];
342 	int n, m, sofar;
343 	ulong done;
344 	char *buf;
345 
346 	buf = vbuf;
347 	buf += Udphdrsize;
348 	blen -= Udphdrsize;
349 
350 	done = 0;
351 	for(sofar = 0; !done; sofar += n){
352 		m = readn(fd, mk, 4);
353 		if(m < 4)
354 			return 0;
355 		done = (mk[0]<<24)|(mk[1]<<16)|(mk[2]<<8)|mk[3];
356 		m = done & 0x7fffffff;
357 		done &= 0x80000000;
358 		if(m > blen-sofar)
359 			return -1;
360 		n = readn(fd, buf+sofar, m);
361 		if(m != n)
362 			return 0;
363 	}
364 	return sofar + Udphdrsize;
365 }
366 
367 static long
368 writetcp(int fd, void *vbuf, long len)
369 {
370 	char *buf;
371 
372 	buf = vbuf;
373 	buf += Udphdrsize;
374 	len -= Udphdrsize;
375 
376 	buf -= 4;
377 	buf[0] = 0x80 | (len>>24);
378 	buf[1] = len>>16;
379 	buf[2] = len>>8;
380 	buf[3] = len;
381 	len += 4;
382 	return write(fd, buf, len);
383 }
384 /*
385  *long
386  *niwrite(int fd, void *buf, long count)
387  *{
388  *	char errbuf[ERRLEN];
389  *	long n;
390  *
391  *	for(;;){
392  *		n = write(fd, buf, count);
393  *		if(n < 0){
394  *			errstr(errbuf);
395  *			if(strcmp(errbuf, "interrupted") == 0)
396  *				continue;
397  *			clog("niwrite error: %s\n", errbuf);
398  *			werrstr(errbuf);
399  *		}
400  *		break;
401  *	}
402  *	return n;
403  *}
404  */
405 long
406 niwrite(int fd, void *buf, long n)
407 {
408 //	int savalarm;
409 
410 // 	savalarm = alarm(0);
411 	n = write(fd, buf, n);
412 // 	if(savalarm > 0)
413 //		alarm(savalarm);
414 	return n;
415 }
416 
417 typedef struct Namecache	Namecache;
418 struct Namecache {
419 	char dom[256];
420 	ulong ipaddr;
421 	Namecache *next;
422 };
423 
424 Namecache *dnscache;
425 
426 static Namecache*
427 domlookupl(void *name, int len)
428 {
429 	Namecache *n, **ln;
430 
431 	if(len >= sizeof(n->dom))
432 		return nil;
433 
434 	for(ln=&dnscache, n=*ln; n; ln=&(*ln)->next, n=*ln) {
435 		if(strncmp(n->dom, name, len) == 0 && n->dom[len] == 0) {
436 			*ln = n->next;
437 			n->next = dnscache;
438 			dnscache = n;
439 			return n;
440 		}
441 	}
442 	return nil;
443 }
444 
445 static Namecache*
446 domlookup(void *name)
447 {
448 	return domlookupl(name, strlen(name));
449 }
450 
451 static Namecache*
452 iplookup(ulong ip)
453 {
454 	Namecache *n, **ln;
455 
456 	for(ln=&dnscache, n=*ln; n; ln=&(*ln)->next, n=*ln) {
457 		if(n->ipaddr == ip) {
458 			*ln = n->next;
459 			n->next = dnscache;
460 			dnscache = n;
461 			return n;
462 		}
463 	}
464 	return nil;
465 }
466 
467 static Namecache*
468 addcacheentry(void *name, int len, ulong ip)
469 {
470 	Namecache *n;
471 
472 	if(len >= sizeof(n->dom))
473 		return nil;
474 
475 	n = malloc(sizeof(*n));
476 	if(n == nil)
477 		return nil;
478 	strncpy(n->dom, name, len);
479 	n->dom[len] = 0;
480 	n->ipaddr = ip;
481 	n->next = dnscache;
482 	dnscache = n;
483 	return nil;
484 }
485 
486 int
487 getdnsdom(ulong ip, char *name, int len)
488 {
489 	char buf[128];
490 	Namecache *nc;
491 	char *p;
492 
493 	if(nc=iplookup(ip)) {
494 		strncpy(name, nc->dom, len);
495 		name[len-1] = 0;
496 		return 0;
497 	}
498 	clog("getdnsdom: %I\n", ip);
499 	snprint(buf, sizeof buf, "%I", ip);
500 	p = csgetvalue("/net", "ip", buf, "dom", nil);
501 	if(p == nil)
502 		return -1;
503 	strncpy(name, p, len-1);
504 	name[len] = 0;
505 	free(p);
506 	addcacheentry(name, strlen(name), ip);
507 	return 0;
508 }
509 
510 int
511 getdom(ulong ip, char *dom, int len)
512 {
513 	int i;
514 	static char *prefix[] = { "", "gate-", "fddi-", "u-", 0 };
515 	char **pr;
516 
517 	if(getdnsdom(ip, dom, len)<0)
518 		return -1;
519 
520 	for(pr=prefix; *pr; pr++){
521 		i = strlen(*pr);
522 		if(strncmp(dom, *pr, i) == 0) {
523 			memmove(dom, dom+i, len-i);
524 			break;
525 		}
526 	}
527 	return 0;
528 }
529 
530 #define	MAXCACHE	64
531 
532 static Rpccache *head, *tail;
533 static int	ncache;
534 
535 static void
536 cachereply(Rpccall *rp, void *buf, int len)
537 {
538 	Rpccache *cp;
539 
540 	if(nocache)
541 		return;
542 
543 	if(ncache >= MAXCACHE){
544 		if(rpcdebug)
545 			fprint(2, "%s: drop  %I/%ld, xid %uld, len %d\n",
546 				argv0, tail->host,
547 				tail->port, tail->xid, tail->n);
548 		tail = tail->prev;
549 		free(tail->next);
550 		tail->next = 0;
551 		--ncache;
552 	}
553 	cp = malloc(sizeof(Rpccache)+len-4);
554 	if(cp == 0){
555 		clog("cachereply: malloc %d failed\n", len);
556 		return;
557 	}
558 	++ncache;
559 	cp->prev = 0;
560 	cp->next = head;
561 	if(head)
562 		head->prev = cp;
563 	else
564 		tail = cp;
565 	head = cp;
566 	cp->host = rp->host;
567 	cp->port = rp->port;
568 	cp->xid = rp->xid;
569 	cp->n = len;
570 	memmove(cp->data, buf, len);
571 	if(rpcdebug)
572 		fprint(2, "%s: cache %I/%ld, xid %uld, len %d\n",
573 			argv0, cp->host, cp->port, cp->xid, cp->n);
574 }
575 
576 static int
577 replycache(int fd, Rpccall *rp, long (*writemsg)(int, void*, long))
578 {
579 	Rpccache *cp;
580 
581 	for(cp=head; cp; cp=cp->next)
582 		if(cp->host == rp->host &&
583 		   cp->port == rp->port &&
584 		   cp->xid == rp->xid)
585 			break;
586 	if(cp == 0)
587 		return 0;
588 	if(cp->prev){	/* move to front */
589 		cp->prev->next = cp->next;
590 		if(cp->next)
591 			cp->next->prev = cp->prev;
592 		else
593 			tail = cp->prev;
594 		cp->prev = 0;
595 		cp->next = head;
596 		head->prev = cp;
597 		head = cp;
598 	}
599 	(*writemsg)(fd, cp->data, cp->n);
600 	if(rpcdebug)
601 		fprint(2, "%s: reply %I/%ld, xid %uld, len %d\n",
602 			argv0, cp->host, cp->port, cp->xid, cp->n);
603 	return 1;
604 }
605 
606 static int
607 Iconv(Fmt *f)
608 {
609 	char buf[16];
610 	ulong h;
611 
612 	h = va_arg(f->args, ulong);
613 	snprint(buf, sizeof buf, "%ld.%ld.%ld.%ld",
614 		(h>>24)&0xff, (h>>16)&0xff,
615 		(h>>8)&0xff, h&0xff);
616 	return fmtstrcpy(f, buf);
617 }
618