xref: /plan9/sys/src/cmd/9nfs/server.c (revision ea58ad6fbee60d5a3fca57ac646881779dd8f0ea)
1 #include "all.h"
2 #include <ndb.h>
3 
4 static int	alarmflag;
5 
6 static int	Iconv(Fmt*);
7 static void	openudp(int);
8 static void	cachereply(Rpccall*, void*, int);
9 static int	replycache(int, Rpccall*, long (*)(int, void*, long));
10 static void	udpserver(int, Progmap*);
11 static void	tcpserver(int, Progmap*);
12 static void	getendpoints(Udphdr*, char*);
13 static long	readtcp(int, void*, long);
14 static long	writetcp(int, void*, long);
15 static int	servemsg(int, long (*)(int, void*, long), long (*)(int, void*, long),
16 		int, Progmap*);
17 void	(*rpcalarm)(void);
18 int	rpcdebug;
19 int	rejectall;
20 int	p9debug;
21 
22 int	nocache;
23 
24 uchar	buf[9000];
25 uchar	rbuf[9000];
26 uchar	resultbuf[9000];
27 
28 static int tcp;
29 
30 char *commonopts = "[-9CDrtv]";			/* for usage() messages */
31 
32 /*
33  * this recognises common, nominally rcp-related options.
34  * they may not take arguments.
35  */
36 int
argopt(int c)37 argopt(int c)
38 {
39 	switch(c){
40 	case '9':
41 		++p9debug;
42 		return 0;
43 	case 'C':
44 		++nocache;
45 		return 0;
46 	case 'D':
47 		++rpcdebug;
48 		return 0;
49 	case 'r':
50 		++rejectall;
51 		return 0;
52 	case 't':
53 		tcp = 1;
54 		return 0;
55 	case 'v':
56 		++chatty;
57 		return 0;
58 	default:
59 		return -1;
60 	}
61 }
62 
63 /*
64  * all option parsing is now done in (*pg->init)(), which can call back
65  * here to argopt for common options.
66  */
67 void
server(int argc,char ** argv,int myport,Progmap * progmap)68 server(int argc, char **argv, int myport, Progmap *progmap)
69 {
70 	Progmap *pg;
71 
72 	fmtinstall('I', Iconv);
73 	fmtinstall('F', fcallfmt);
74 	fmtinstall('D', dirfmt);
75 
76 	switch(rfork(RFNOWAIT|RFENVG|RFNAMEG|RFNOTEG|RFFDG|RFPROC)){
77 	case -1:
78 		panic("fork");
79 	default:
80 		_exits(0);
81 	case 0:
82 		break;
83 	}
84 
85 	switch(rfork(RFMEM|RFPROC)){
86 	case 0:
87 		for(;;){
88 			sleep(30*1000);
89 			alarmflag = 1;
90 		}
91 	case -1:
92 		sysfatal("rfork: %r");
93 	}
94 
95 	for(pg=progmap; pg->init; pg++)
96 		(*pg->init)(argc, argv);
97 	if(tcp)
98 		tcpserver(myport, progmap);
99 	else
100 		udpserver(myport, progmap);
101 }
102 
103 static void
udpserver(int myport,Progmap * progmap)104 udpserver(int myport, Progmap *progmap)
105 {
106 	char service[128];
107 	char data[128];
108 	char devdir[40];
109 	int ctlfd, datafd;
110 
111 	snprint(service, sizeof service, "udp!*!%d", myport);
112 	ctlfd = announce(service, devdir);
113 	if(ctlfd < 0)
114 		panic("can't announce %s: %r\n", service);
115 	if(fprint(ctlfd, "headers") < 0)
116 		panic("can't set header mode: %r\n");
117 
118 	snprint(data, sizeof data, "%s/data", devdir);
119 	datafd = open(data, ORDWR);
120 	if(datafd < 0)
121 		panic("can't open udp data: %r\n");
122 	close(ctlfd);
123 
124 	chatsrv(0);
125 	clog("%s: listening to port %d\n", argv0, myport);
126 	while (servemsg(datafd, read, write, myport, progmap) >= 0)
127 		continue;
128 	exits(0);
129 }
130 
131 static void
tcpserver(int myport,Progmap * progmap)132 tcpserver(int myport, Progmap *progmap)
133 {
134 	char adir[40];
135 	char ldir[40];
136 	char ds[40];
137 	int actl, lctl, data;
138 
139 	snprint(ds, sizeof ds, "tcp!*!%d", myport);
140 	chatsrv(0);
141 	actl = -1;
142 	for(;;){
143 		if(actl < 0){
144 			actl = announce(ds, adir);
145 			if(actl < 0){
146 				clog("%s: listening to tcp port %d\n",
147 					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 			/* pretend it's udp; fill in Udphdr */
173 			getendpoints((Udphdr*)buf, ldir);
174 
175 			while (servemsg(data, readtcp, writetcp, myport,
176 			    progmap) >= 0)
177 				continue;
178 			close(data);
179 			exits(0);
180 		}
181 	}
182 	exits(0);
183 }
184 
185 static int
servemsg(int fd,long (* readmsg)(int,void *,long),long (* writemsg)(int,void *,long),int myport,Progmap * progmap)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
getendpoint(char * dir,char * file,uchar * addr,uchar * port)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 /* set Udphdr values from protocol dir local & remote files */
332 static void
getendpoints(Udphdr * ep,char * dir)333 getendpoints(Udphdr *ep, char *dir)
334 {
335 	getendpoint(dir, "local", ep->laddr, ep->lport);
336 	getendpoint(dir, "remote", ep->raddr, ep->rport);
337 }
338 
339 static long
readtcp(int fd,void * vbuf,long blen)340 readtcp(int fd, void *vbuf, long blen)
341 {
342 	uchar mk[4];
343 	int n, m, sofar;
344 	ulong done;
345 	char *buf;
346 
347 	buf = vbuf;
348 	buf += Udphdrsize;
349 	blen -= Udphdrsize;
350 
351 	done = 0;
352 	for(sofar = 0; !done; sofar += n){
353 		m = readn(fd, mk, 4);
354 		if(m < 4)
355 			return 0;
356 		done = (mk[0]<<24)|(mk[1]<<16)|(mk[2]<<8)|mk[3];
357 		m = done & 0x7fffffff;
358 		done &= 0x80000000;
359 		if(m > blen-sofar)
360 			return -1;
361 		n = readn(fd, buf+sofar, m);
362 		if(m != n)
363 			return 0;
364 	}
365 	return sofar + Udphdrsize;
366 }
367 
368 static long
writetcp(int fd,void * vbuf,long len)369 writetcp(int fd, void *vbuf, long len)
370 {
371 	char *buf;
372 
373 	buf = vbuf;
374 	buf += Udphdrsize;
375 	len -= Udphdrsize;
376 
377 	buf -= 4;
378 	buf[0] = 0x80 | (len>>24);
379 	buf[1] = len>>16;
380 	buf[2] = len>>8;
381 	buf[3] = len;
382 	len += 4;
383 	return write(fd, buf, len);
384 }
385 /*
386  *long
387  *niwrite(int fd, void *buf, long count)
388  *{
389  *	char errbuf[ERRLEN];
390  *	long n;
391  *
392  *	for(;;){
393  *		n = write(fd, buf, count);
394  *		if(n < 0){
395  *			errstr(errbuf);
396  *			if(strcmp(errbuf, "interrupted") == 0)
397  *				continue;
398  *			clog("niwrite error: %s\n", errbuf);
399  *			werrstr(errbuf);
400  *		}
401  *		break;
402  *	}
403  *	return n;
404  *}
405  */
406 long
niwrite(int fd,void * buf,long n)407 niwrite(int fd, void *buf, long n)
408 {
409 //	int savalarm;
410 
411 // 	savalarm = alarm(0);
412 	n = write(fd, buf, n);
413 // 	if(savalarm > 0)
414 //		alarm(savalarm);
415 	return n;
416 }
417 
418 typedef struct Namecache	Namecache;
419 struct Namecache {
420 	char dom[256];
421 	ulong ipaddr;
422 	Namecache *next;
423 };
424 
425 Namecache *dnscache;
426 
427 static Namecache*
domlookupl(void * name,int len)428 domlookupl(void *name, int len)
429 {
430 	Namecache *n, **ln;
431 
432 	if(len >= sizeof(n->dom))
433 		return nil;
434 
435 	for(ln=&dnscache, n=*ln; n; ln=&(*ln)->next, n=*ln) {
436 		if(strncmp(n->dom, name, len) == 0 && n->dom[len] == 0) {
437 			*ln = n->next;
438 			n->next = dnscache;
439 			dnscache = n;
440 			return n;
441 		}
442 	}
443 	return nil;
444 }
445 
446 static Namecache*
domlookup(void * name)447 domlookup(void *name)
448 {
449 	return domlookupl(name, strlen(name));
450 }
451 
452 static Namecache*
iplookup(ulong ip)453 iplookup(ulong ip)
454 {
455 	Namecache *n, **ln;
456 
457 	for(ln=&dnscache, n=*ln; n; ln=&(*ln)->next, n=*ln) {
458 		if(n->ipaddr == ip) {
459 			*ln = n->next;
460 			n->next = dnscache;
461 			dnscache = n;
462 			return n;
463 		}
464 	}
465 	return nil;
466 }
467 
468 static Namecache*
addcacheentry(void * name,int len,ulong ip)469 addcacheentry(void *name, int len, ulong ip)
470 {
471 	Namecache *n;
472 
473 	if(len >= sizeof(n->dom))
474 		return nil;
475 
476 	n = malloc(sizeof(*n));
477 	if(n == nil)
478 		return nil;
479 	strncpy(n->dom, name, len);
480 	n->dom[len] = 0;
481 	n->ipaddr = ip;
482 	n->next = dnscache;
483 	dnscache = n;
484 	return nil;
485 }
486 
487 int
getdnsdom(ulong ip,char * name,int len)488 getdnsdom(ulong ip, char *name, int len)
489 {
490 	char buf[128];
491 	Namecache *nc;
492 	char *p;
493 
494 	if(nc=iplookup(ip)) {
495 		strncpy(name, nc->dom, len);
496 		name[len-1] = 0;
497 		return 0;
498 	}
499 	clog("getdnsdom: %I\n", ip);
500 	snprint(buf, sizeof buf, "%I", ip);
501 	p = csgetvalue("/net", "ip", buf, "dom", nil);
502 	if(p == nil)
503 		return -1;
504 	strncpy(name, p, len-1);
505 	name[len] = 0;
506 	free(p);
507 	addcacheentry(name, strlen(name), ip);
508 	return 0;
509 }
510 
511 int
getdom(ulong ip,char * dom,int len)512 getdom(ulong ip, char *dom, int len)
513 {
514 	int i;
515 	static char *prefix[] = { "", "gate-", "fddi-", "u-", 0 };
516 	char **pr;
517 
518 	if(getdnsdom(ip, dom, len)<0)
519 		return -1;
520 
521 	for(pr=prefix; *pr; pr++){
522 		i = strlen(*pr);
523 		if(strncmp(dom, *pr, i) == 0) {
524 			memmove(dom, dom+i, len-i);
525 			break;
526 		}
527 	}
528 	return 0;
529 }
530 
531 #define	MAXCACHE	64
532 
533 static Rpccache *head, *tail;
534 static int	ncache;
535 
536 static void
cachereply(Rpccall * rp,void * buf,int len)537 cachereply(Rpccall *rp, void *buf, int len)
538 {
539 	Rpccache *cp;
540 
541 	if(nocache)
542 		return;
543 
544 	if(ncache >= MAXCACHE){
545 		if(rpcdebug)
546 			fprint(2, "%s: drop  %I/%ld, xid %uld, len %d\n",
547 				argv0, tail->host,
548 				tail->port, tail->xid, tail->n);
549 		tail = tail->prev;
550 		free(tail->next);
551 		tail->next = 0;
552 		--ncache;
553 	}
554 	cp = malloc(sizeof(Rpccache)+len-4);
555 	if(cp == 0){
556 		clog("cachereply: malloc %d failed\n", len);
557 		return;
558 	}
559 	++ncache;
560 	cp->prev = 0;
561 	cp->next = head;
562 	if(head)
563 		head->prev = cp;
564 	else
565 		tail = cp;
566 	head = cp;
567 	cp->host = rp->host;
568 	cp->port = rp->port;
569 	cp->xid = rp->xid;
570 	cp->n = len;
571 	memmove(cp->data, buf, len);
572 	if(rpcdebug)
573 		fprint(2, "%s: cache %I/%ld, xid %uld, len %d\n",
574 			argv0, cp->host, cp->port, cp->xid, cp->n);
575 }
576 
577 static int
replycache(int fd,Rpccall * rp,long (* writemsg)(int,void *,long))578 replycache(int fd, Rpccall *rp, long (*writemsg)(int, void*, long))
579 {
580 	Rpccache *cp;
581 
582 	for(cp=head; cp; cp=cp->next)
583 		if(cp->host == rp->host &&
584 		   cp->port == rp->port &&
585 		   cp->xid == rp->xid)
586 			break;
587 	if(cp == 0)
588 		return 0;
589 	if(cp->prev){	/* move to front */
590 		cp->prev->next = cp->next;
591 		if(cp->next)
592 			cp->next->prev = cp->prev;
593 		else
594 			tail = cp->prev;
595 		cp->prev = 0;
596 		cp->next = head;
597 		head->prev = cp;
598 		head = cp;
599 	}
600 	(*writemsg)(fd, cp->data, cp->n);
601 	if(rpcdebug)
602 		fprint(2, "%s: reply %I/%ld, xid %uld, len %d\n",
603 			argv0, cp->host, cp->port, cp->xid, cp->n);
604 	return 1;
605 }
606 
607 static int
Iconv(Fmt * f)608 Iconv(Fmt *f)
609 {
610 	char buf[16];
611 	ulong h;
612 
613 	h = va_arg(f->args, ulong);
614 	snprint(buf, sizeof buf, "%ld.%ld.%ld.%ld",
615 		(h>>24)&0xff, (h>>16)&0xff,
616 		(h>>8)&0xff, h&0xff);
617 	return fmtstrcpy(f, buf);
618 }
619