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