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