xref: /plan9/sys/src/cmd/ip/tftpd.c (revision 6d2133e0d4be943e9ce2e416f00a3b046838334e)
1 /*
2  * tftpd - tftp service, see /lib/rfc/rfc783 (now rfc1350 + 234[789])
3  */
4 #include <u.h>
5 #include <libc.h>
6 #include <auth.h>
7 #include <bio.h>
8 #include <ip.h>
9 #include <ndb.h>
10 
11 enum
12 {
13 	Maxpath=	128,
14 	Maxerr=		256,
15 
16 	Debug=		0,
17 
18 	Opsize=		sizeof(short),
19 	Blksize=	sizeof(short),
20 	Hdrsize=	Opsize + Blksize,
21 
22 	Ackerr=		-1,
23 	Ackok=		0,
24 	Ackrexmit=	1,
25 
26 	/* op codes */
27 	Tftp_READ	= 1,
28 	Tftp_WRITE	= 2,
29 	Tftp_DATA	= 3,
30 	Tftp_ACK	= 4,
31 	Tftp_ERROR	= 5,
32 	Tftp_OACK	= 6,		/* option acknowledge */
33 
34 	Errnotdef	= 0,		/* see textual error instead */
35 	Errnotfound	= 1,
36 	Errnoaccess	= 2,
37 	Errdiskfull	= 3,
38 	Errbadop	= 4,
39 	Errbadtid	= 5,
40 	Errexists	= 6,
41 	Errnouser	= 7,
42 	Errbadopt	= 8,		/* really bad option value */
43 
44 	Defsegsize	= 512,
45 	Maxsegsize	= 65464,	/* from rfc2348 */
46 
47 	/*
48 	 * bandt (viaduct) tunnels use smaller mtu than ether's
49 	 * (1400 bytes for tcp mss of 1300 bytes).
50 	 */
51 	Bandtmtu	= 1400,
52 	/*
53 	 * maximum size of block's data content, excludes hdrs,
54 	 * notably IP/UDP and TFTP, using worst-case (IPv6) sizes.
55 	 */
56 	Bandtblksz	= Bandtmtu - 40 - 8,
57 };
58 
59 typedef struct Opt Opt;
60 struct Opt {
61 	char	*name;
62 	int	*valp;		/* set to client's value if within bounds */
63 	int	min;
64 	int	max;
65 };
66 
67 int 	dbg;
68 int	restricted;
69 int	pid;
70 
71 /* options */
72 int	blksize = Defsegsize;		/* excluding 4-byte header */
73 int	timeout = 5;			/* seconds */
74 int	tsize;
75 static Opt option[] = {
76 	"timeout",	&timeout,	1,	255,
77 	/* see "hack" below */
78 	"blksize",	&blksize,	8,	Maxsegsize,
79 	"tsize",	&tsize,		0,	~0UL >> 1,
80 };
81 
82 void	sendfile(int, char*, char*, int);
83 void	recvfile(int, char*, char*);
84 void	nak(int, int, char*);
85 void	ack(int, ushort);
86 void	clrcon(void);
87 void	setuser(void);
88 char*	sunkernel(char*);
89 void	remoteaddr(char*, char*, int);
90 void	doserve(int);
91 
92 char	bigbuf[32768];
93 char	raddr[64];
94 
95 char	*dir = "/lib/tftpd";
96 char	*dirsl;
97 int	dirsllen;
98 char	flog[] = "ipboot";
99 char	net[Maxpath];
100 
101 static char *opnames[] = {
102 [Tftp_READ]	"read",
103 [Tftp_WRITE]	"write",
104 [Tftp_DATA]	"data",
105 [Tftp_ACK]	"ack",
106 [Tftp_ERROR]	"error",
107 [Tftp_OACK]	"oack",
108 };
109 
110 void
111 usage(void)
112 {
113 	fprint(2, "usage: %s [-dr] [-h homedir] [-s svc] [-x netmtpt]\n",
114 		argv0);
115 	exits("usage");
116 }
117 
118 void
119 main(int argc, char **argv)
120 {
121 	char buf[64];
122 	char adir[64], ldir[64];
123 	int cfd, lcfd, dfd;
124 	char *svc = "69";
125 
126 	setnetmtpt(net, sizeof net, nil);
127 	ARGBEGIN{
128 	case 'd':
129 		dbg++;
130 		break;
131 	case 'h':
132 		dir = EARGF(usage());
133 		break;
134 	case 'r':
135 		restricted = 1;
136 		break;
137 	case 's':
138 		svc = EARGF(usage());
139 		break;
140 	case 'x':
141 		setnetmtpt(net, sizeof net, EARGF(usage()));
142 		break;
143 	default:
144 		usage();
145 	}ARGEND
146 
147 	snprint(buf, sizeof buf, "%s/", dir);
148 	dirsl = strdup(buf);
149 	dirsllen = strlen(dirsl);
150 
151 	fmtinstall('E', eipfmt);
152 	fmtinstall('I', eipfmt);
153 
154 	/*
155 	 * setuser calls newns, and typical /lib/namespace files contain
156 	 * "cd /usr/$user", so call setuser before chdir.
157 	 */
158 	setuser();
159 	if(chdir(dir) < 0)
160 		sysfatal("can't get to directory %s: %r", dir);
161 
162 	if(!dbg)
163 		switch(rfork(RFNOTEG|RFPROC|RFFDG)) {
164 		case -1:
165 			sysfatal("fork: %r");
166 		case 0:
167 			break;
168 		default:
169 			exits(0);
170 		}
171 
172 	snprint(buf, sizeof buf, "%s/udp!*!%s", net, svc);
173 	cfd = announce(buf, adir);
174 	if (cfd < 0)
175 		sysfatal("announcing on %s: %r", buf);
176 	syslog(dbg, flog, "tftpd started on %s dir %s", buf, adir);
177 //	setuser();
178 	for(;;) {
179 		lcfd = listen(adir, ldir);
180 		if(lcfd < 0)
181 			sysfatal("listening on %s: %r", adir);
182 
183 		switch(fork()) {
184 		case -1:
185 			sysfatal("fork: %r");
186 		case 0:
187 			dfd = accept(lcfd, ldir);
188 			if(dfd < 0)
189  				exits(0);
190 			remoteaddr(ldir, raddr, sizeof(raddr));
191 			pid = getpid();
192 			syslog(0, flog, "tftp %d connection from %s dir %s",
193 				pid, raddr, ldir);
194 			doserve(dfd);
195 			exits("done");
196 			break;
197 		default:
198 			close(lcfd);
199 			continue;
200 		}
201 	}
202 }
203 
204 static Opt *
205 handleopt(int fd, char *name, char *val)
206 {
207 	int n;
208 	Opt *op;
209 
210 	for (op = option; op < option + nelem(option); op++)
211 		if(cistrcmp(name, op->name) == 0) {
212 			n = strtol(val, nil, 10);
213 			if (n < op->min || n > op->max) {
214 				nak(fd, Errbadopt, "option value out of range");
215 				syslog(dbg, flog, "tftp bad option value from "
216 					"client: %s %s", name, val);
217 				sysfatal("bad option value from client: %s %s",
218 					name, val);
219 			}
220 			*op->valp = n;
221 			syslog(dbg, flog, "tftpd %d setting %s to %d",
222 				pid, name, n);
223 			return op;
224 		}
225 	return nil;
226 }
227 
228 static vlong
229 filesize(char *file)
230 {
231 	vlong size;
232 	Dir *dp;
233 
234 	dp = dirstat(file);
235 	if (dp == nil)
236 		return 0;
237 	size = dp->length;
238 	free(dp);
239 	return size;
240 }
241 
242 static int
243 options(int fd, char *buf, char *file, ushort oper, char *p, int dlen)
244 {
245 	int nmlen, vallen, nopts;
246 	vlong size;
247 	char *val, *bp;
248 	Opt *op;
249 
250 	buf[0] = 0;
251 	buf[1] = Tftp_OACK;
252 	bp = buf + Opsize;
253 	nopts = 0;
254 	while (dlen > 0 && *p != '\0') {
255 		nmlen = strlen(p) + 1;		/* include NUL */
256 		if (nmlen > dlen)
257 			break;
258 		dlen -= nmlen;
259 		val = p + nmlen;
260 		if (dlen <= 0 || *val == '\0')
261 			break;
262 
263 		vallen = strlen(val) + 1;
264 		if (vallen > dlen)
265 			break;
266 		dlen -= vallen;
267 
268 		nopts++;
269 		op = handleopt(fd, p, val);
270 		if (op) {
271 			/* append OACK response to buf */
272 			sprint(bp, "%s", p);
273 			bp += nmlen;
274 			if (oper == Tftp_READ && cistrcmp(p, "tsize") == 0) {
275 				size = filesize(file);
276 				sprint(bp, "%lld", size);
277 				syslog(dbg, flog, "tftpd %d %s tsize is %,lld",
278 					pid, file, size);
279 			}
280 			/*
281 			 * hack: bandt (viaducts) uses smaller mtu than ether's
282 			 * (1400 bytes for tcp mss of 1300 bytes),
283 			 * so offer at most bandt's mtu minus headers,
284 			 * to avoid failure of pxe booting via viaduct.
285 			 */
286 			else if (oper == Tftp_READ &&
287 			    cistrcmp(p, "blksize") == 0 &&
288 			    blksize > Bandtblksz) {
289 				blksize = Bandtblksz;
290 				sprint(bp, "%d", blksize);
291 				syslog(dbg, flog,
292 					"tftpd %d overriding blksize to %d",
293 					pid, blksize);
294 			} else
295 				strcpy(bp, val);  /* use requested value */
296 			bp += strlen(bp) + 1;
297 		}
298 		p = val + vallen;
299 	}
300 	if (nopts == 0)
301 		return 0;		/* no options actually seen */
302 	*bp++ = '\0';
303 	*bp++ = '\0';			/* overkill */
304 	*bp++ = '\0';
305 	if (write(fd, buf, bp - buf) < bp - buf) {
306 		syslog(dbg, flog, "tftpd network write error on oack to %s: %r",
307 			raddr);
308 		sysfatal("tftpd: network write error: %r");
309 	}
310 	if(Debug)
311 		syslog(dbg, flog, "tftpd oack: options to %s", raddr);
312 	return nopts;
313 }
314 
315 /* this doesn't stop the cavium from barging ahead */
316 //static void
317 //sendnoopts(int fd, char *name)
318 //{
319 //	char buf[64];
320 //
321 //	memset(buf, 0, sizeof buf);
322 //	buf[0] = 0;
323 //	buf[1] = Tftp_OACK;
324 //
325 //	if(write(fd, buf, sizeof buf) < sizeof buf) {
326 //		syslog(dbg, flog, "tftpd network write error on %s oack to %s: %r",
327 //			name, raddr);
328 //		sysfatal("tftpd: network write error: %r");
329 //	}
330 //	if(Debug)
331 //		syslog(dbg, flog, "tftpd oack: no options to %s", raddr);
332 //}
333 
334 static void
335 optlog(char *bytes, char *p, int dlen)
336 {
337 	char *bp;
338 
339 	bp = bytes;
340 	sprint(bp, "tftpd %d option bytes: ", dlen);
341 	bp += strlen(bp);
342 	for (; dlen > 0; dlen--, p++)
343 		*bp++ = *p? *p: ' ';
344 	*bp = '\0';
345 	syslog(dbg, flog, "%s", bytes);
346 }
347 
348 void
349 doserve(int fd)
350 {
351 	int dlen, opts;
352 	char *mode, *p, *file;
353 	short op;
354 
355 	dlen = read(fd, bigbuf, sizeof(bigbuf)-1);
356 	if(dlen < 0)
357 		sysfatal("listen read: %r");
358 
359 	bigbuf[dlen] = '\0';
360 	op = (bigbuf[0]<<8) | bigbuf[1];
361 	dlen -= Opsize;
362 	mode = file = bigbuf + Opsize;
363 	while(*mode != '\0' && dlen--)
364 		mode++;
365 	mode++;
366 	p = mode;
367 	while(*p && dlen--)
368 		p++;
369 	if(dlen == 0) {
370 		nak(fd, 0, "bad tftpmode");
371 		close(fd);
372 		syslog(dbg, flog, "tftpd %d bad mode %s for file %s from %s",
373 			pid, mode, file, raddr);
374 		return;
375 	}
376 
377 	if(op != Tftp_READ && op != Tftp_WRITE) {
378 		nak(fd, Errbadop, "Illegal TFTP operation");
379 		close(fd);
380 		syslog(dbg, flog, "tftpd %d bad request %d %s", pid, op, raddr);
381 		return;
382 	}
383 
384 	if(restricted){
385 		if(file[0] == '#' || strncmp(file, "../", 3) == 0 ||
386 		  strstr(file, "/../") != nil ||
387 		  (file[0] == '/' && strncmp(file, dirsl, dirsllen) != 0)){
388 			nak(fd, Errnoaccess, "Permission denied");
389 			close(fd);
390 			syslog(dbg, flog, "tftpd %d bad request %d from %s file %s",
391 				pid, op, raddr, file);
392 			return;
393 		}
394 	}
395 
396 	/*
397 	 * options are supposed to be negotiated, but the cavium board's
398 	 * u-boot really wants us to use a block size of 1432 bytes and won't
399 	 * take `no' for an answer.
400 	 */
401 	p++;				/* skip NUL after mode */
402 	dlen--;
403 	opts = 0;
404 	if(dlen > 0) {			/* might have options */
405 		char bytes[32*1024];
406 
407 		if(Debug)
408 			optlog(bytes, p, dlen);
409 		opts = options(fd, bytes, file, op, p, dlen);
410 	}
411 	if(op == Tftp_READ)
412 		sendfile(fd, file, mode, opts);
413 	else
414 		recvfile(fd, file, mode);
415 }
416 
417 void
418 catcher(void *junk, char *msg)
419 {
420 	USED(junk);
421 
422 	if(strncmp(msg, "exit", 4) == 0)
423 		noted(NDFLT);
424 	noted(NCONT);
425 }
426 
427 static int
428 awaitack(int fd, int block)
429 {
430 	int ackblock, al, rxl;
431 	ushort op;
432 	uchar ack[1024];
433 
434 	for(rxl = 0; rxl < 10; rxl++) {
435 		memset(ack, 0, Hdrsize);
436 		alarm(1000);
437 		al = read(fd, ack, sizeof(ack));
438 		alarm(0);
439 		if(al < 0) {
440 			if (Debug)
441 				syslog(dbg, flog, "tftpd %d timed out "
442 					"waiting for ack from %s", pid, raddr);
443 			return Ackrexmit;
444 		}
445 		op = ack[0]<<8|ack[1];
446 		if(op == Tftp_ERROR) {
447 			if (Debug)
448 				syslog(dbg, flog, "tftpd %d got error "
449 					"waiting for ack from %s", pid, raddr);
450 			return Ackerr;
451 		} else if(op != Tftp_ACK) {
452 			syslog(dbg, flog, "tftpd %d rcvd %s op from %s", pid,
453 				(op < nelem(opnames)? opnames[op]: "gok"),
454 				raddr);
455 			return Ackerr;
456 		}
457 		ackblock = ack[2]<<8|ack[3];
458 		if (Debug)
459 			syslog(dbg, flog, "tftpd %d read ack of %d bytes "
460 				"for block %d", pid, al, ackblock);
461 		if(ackblock == block)
462 			return Ackok;		/* for block just sent */
463 		else if(ackblock == block + 1)	/* intel pxe eof bug */
464 			return Ackok;
465 		else if(ackblock == 0xffff)
466 			return Ackrexmit;
467 		else
468 			/* ack is for some other block; ignore it, try again */
469 			syslog(dbg, flog, "tftpd %d expected ack for block %d, "
470 				"got %d", pid, block, ackblock);
471 	}
472 	return Ackrexmit;
473 }
474 
475 void
476 sendfile(int fd, char *name, char *mode, int opts)
477 {
478 	int file, block, ret, rexmit, n, txtry;
479 	uchar buf[Maxsegsize+Hdrsize];
480 	char errbuf[Maxerr];
481 
482 	syslog(dbg, flog, "tftpd %d send file '%s' %s to %s",
483 		pid, name, mode, raddr);
484 	name = sunkernel(name);
485 	if(name == 0){
486 		nak(fd, 0, "not in our database");
487 		return;
488 	}
489 
490 	notify(catcher);
491 
492 	file = open(name, OREAD);
493 	if(file < 0) {
494 		errstr(errbuf, sizeof errbuf);
495 		nak(fd, 0, errbuf);
496 		return;
497 	}
498 	block = 0;
499 	rexmit = Ackok;
500 	n = 0;
501 	/*
502 	 * if we sent an oack previously, wait for the client's ack or error.
503 	 * if we get no ack for our oack, it could be that we returned
504 	 * a tsize that the client can't handle, or it could be intel
505 	 * pxe just read-with-tsize to get size, couldn't be bothered to
506 	 * ack our oack and has just gone ahead and issued another read.
507 	 */
508 	if(opts && awaitack(fd, 0) != Ackok)
509 		goto error;
510 
511 	for(txtry = 0; txtry < timeout;) {
512 		if(rexmit == Ackok) {
513 			block++;
514 			buf[0] = 0;
515 			buf[1] = Tftp_DATA;
516 			buf[2] = block>>8;
517 			buf[3] = block;
518 			n = read(file, buf+Hdrsize, blksize);
519 			if(n < 0) {
520 				errstr(errbuf, sizeof errbuf);
521 				nak(fd, 0, errbuf);
522 				return;
523 			}
524 			txtry = 0;
525 		}
526 		else {
527 			syslog(dbg, flog, "tftpd %d rexmit %d %s:%d to %s",
528 				pid, Hdrsize+n, name, block, raddr);
529 			txtry++;
530 		}
531 
532 		ret = write(fd, buf, Hdrsize+n);
533 		if(ret < Hdrsize+n) {
534 			syslog(dbg, flog,
535 				"tftpd network write error on %s to %s: %r",
536 				name, raddr);
537 			sysfatal("tftpd: network write error: %r");
538 		}
539 		if (Debug)
540 			syslog(dbg, flog, "tftpd %d sent block %d", pid, block);
541 
542 		rexmit = awaitack(fd, block);
543 		if (rexmit == Ackerr)
544 			break;
545 		if(ret != blksize+Hdrsize && rexmit == Ackok)
546 			break;
547 	}
548 error:
549 	close(fd);
550 	close(file);
551 }
552 
553 void
554 recvfile(int fd, char *name, char *mode)
555 {
556 	ushort op, block, inblock;
557 	uchar buf[Maxsegsize+8];
558 	char errbuf[Maxerr];
559 	int n, ret, file;
560 
561 	syslog(dbg, flog, "receive file '%s' %s from %s", name, mode, raddr);
562 
563 	file = create(name, OWRITE, 0666);
564 	if(file < 0) {
565 		errstr(errbuf, sizeof errbuf);
566 		nak(fd, 0, errbuf);
567 		syslog(dbg, flog, "can't create %s: %r", name);
568 		return;
569 	}
570 
571 	block = 0;
572 	ack(fd, block);
573 	block++;
574 
575 	for (;;) {
576 		alarm(15000);
577 		n = read(fd, buf, blksize+8);
578 		alarm(0);
579 		if(n < 0) {
580 			syslog(dbg, flog, "tftpd: network error reading %s: %r",
581 				name);
582 			goto error;
583 		}
584 		if(n <= Hdrsize) {
585 			syslog(dbg, flog,
586 				"tftpd: short read from network, reading %s",
587 				name);
588 			goto error;
589 		}
590 		op = buf[0]<<8|buf[1];
591 		if(op == Tftp_ERROR) {
592 			syslog(dbg, flog, "tftpd: tftp error reading %s", name);
593 			goto error;
594 		}
595 
596 		n -= Hdrsize;
597 		inblock = buf[2]<<8|buf[3];
598 		if(op == Tftp_DATA) {
599 			if(inblock == block) {
600 				ret = write(file, buf+Hdrsize, n);
601 				if(ret != n) {
602 					errstr(errbuf, sizeof errbuf);
603 					nak(fd, 0, errbuf);
604 					syslog(dbg, flog,
605 					    "tftpd: error writing %s: %s",
606 						name, errbuf);
607 					goto error;
608 				}
609 				ack(fd, block);
610 				block++;
611 			} else
612 				ack(fd, 0xffff);	/* tell him to resend */
613 		}
614 	}
615 error:
616 	close(file);
617 }
618 
619 void
620 ack(int fd, ushort block)
621 {
622 	uchar ack[4];
623 	int n;
624 
625 	ack[0] = 0;
626 	ack[1] = Tftp_ACK;
627 	ack[2] = block>>8;
628 	ack[3] = block;
629 
630 	n = write(fd, ack, 4);
631 	if(n < 4)
632 		sysfatal("network write: %r");
633 }
634 
635 void
636 nak(int fd, int code, char *msg)
637 {
638 	char buf[128];
639 	int n;
640 
641 	buf[0] = 0;
642 	buf[1] = Tftp_ERROR;
643 	buf[2] = 0;
644 	buf[3] = code;
645 	strcpy(buf+4, msg);
646 	n = strlen(msg) + 4 + 1;
647 	if(write(fd, buf, n) < n)
648 		sysfatal("write nak: %r");
649 }
650 
651 void
652 setuser(void)
653 {
654 	int fd;
655 
656 	fd = open("#c/user", OWRITE);
657 	if(fd < 0 || write(fd, "none", strlen("none")) < 0)
658 		sysfatal("can't become none: %r");
659 	close(fd);
660 	if(newns("none", nil) < 0)
661 		sysfatal("can't build namespace: %r");
662 }
663 
664 char*
665 lookup(char *sattr, char *sval, char *tattr, char *tval, int len)
666 {
667 	static Ndb *db;
668 	char *attrs[1];
669 	Ndbtuple *t;
670 
671 	if(db == nil)
672 		db = ndbopen(0);
673 	if(db == nil)
674 		return nil;
675 
676 	if(sattr == nil)
677 		sattr = ipattr(sval);
678 
679 	attrs[0] = tattr;
680 	t = ndbipinfo(db, sattr, sval, attrs, 1);
681 	if(t == nil)
682 		return nil;
683 	strncpy(tval, t->val, len);
684 	tval[len-1] = 0;
685 	ndbfree(t);
686 	return tval;
687 }
688 
689 /*
690  *  for sun kernel boots, replace the requested file name with
691  *  a one from our database.  If the database doesn't specify a file,
692  *  don't answer.
693  */
694 char*
695 sunkernel(char *name)
696 {
697 	ulong addr;
698 	uchar v4[IPv4addrlen];
699 	uchar v6[IPaddrlen];
700 	char buf[256];
701 	char ipbuf[128];
702 	char *suffix;
703 
704 	addr = strtoul(name, &suffix, 16);
705 	if(suffix-name != 8 || (strcmp(suffix, "") != 0 && strcmp(suffix, ".SUN") != 0))
706 		return name;
707 
708 	v4[0] = addr>>24;
709 	v4[1] = addr>>16;
710 	v4[2] = addr>>8;
711 	v4[3] = addr;
712 	v4tov6(v6, v4);
713 	sprint(ipbuf, "%I", v6);
714 	return lookup("ip", ipbuf, "bootf", buf, sizeof buf);
715 }
716 
717 void
718 remoteaddr(char *dir, char *raddr, int len)
719 {
720 	char buf[64];
721 	int fd, n;
722 
723 	snprint(buf, sizeof(buf), "%s/remote", dir);
724 	fd = open(buf, OREAD);
725 	if(fd < 0){
726 		snprint(raddr, sizeof(raddr), "unknown");
727 		return;
728 	}
729 	n = read(fd, raddr, len-1);
730 	close(fd);
731 	if(n <= 0){
732 		snprint(raddr, sizeof(raddr), "unknown");
733 		return;
734 	}
735 	if(n > 0)
736 		n--;
737 	raddr[n] = 0;
738 }
739