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