xref: /openbsd-src/usr.bin/tftp/tftp.c (revision 7fe43c8a3e7098a2ca02cfe06d86ca5465f15f6f)
1 /*	$OpenBSD: tftp.c,v 1.25 2017/09/10 07:29:39 tb Exp $	*/
2 /*	$NetBSD: tftp.c,v 1.5 1995/04/29 05:55:25 cgd Exp $	*/
3 
4 /*
5  * Copyright (c) 1983, 1993
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 /*
34  * TFTP User Program -- Protocol Machines
35  *
36  * This version includes many modifications by Jim Guyton <guyton@rand-unix>
37  */
38 
39 #include <sys/types.h>
40 #include <sys/socket.h>
41 #include <sys/time.h>
42 #include <sys/stat.h>
43 
44 #include <netinet/in.h>
45 #include <arpa/tftp.h>
46 
47 #include <err.h>
48 #include <errno.h>
49 #include <poll.h>
50 #include <signal.h>
51 #include <stdio.h>
52 #include <stddef.h>
53 #include <stdlib.h>
54 #include <string.h>
55 #include <time.h>
56 #include <unistd.h>
57 #include <netdb.h>
58 
59 #include "extern.h"
60 #include "tftpsubs.h"
61 
62 static int	cmpport(struct sockaddr *, struct sockaddr *);
63 static int	makerequest(int, const char *, struct tftphdr *, const char *);
64 static void	nak(int, struct sockaddr *);
65 static void 	tpacket(const char *, struct tftphdr *, int);
66 static void	startclock(void);
67 static void	stopclock(void);
68 static void	printstats(const char *, unsigned long);
69 static void	printtimeout(void);
70 static void	oack(struct tftphdr *, int, int);
71 static int	oack_set(const char *, const char *);
72 
73 extern struct sockaddr_storage	 peeraddr;	/* filled in by main */
74 extern int			 f;		/* the opened socket */
75 extern int			 trace;
76 extern int			 verbose;
77 extern int			 rexmtval;
78 extern int			 maxtimeout;
79 extern FILE			*file;
80 extern volatile sig_atomic_t	 intrflag;
81 extern char			*ackbuf;
82 extern int			 has_options;
83 extern int			 opt_tsize;
84 extern int			 opt_tout;
85 extern int			 opt_blksize;
86 
87 struct timespec	tstart;
88 struct timespec	tstop;
89 unsigned int	segment_size = SEGSIZE;
90 unsigned int	packet_size = SEGSIZE + 4;
91 
92 struct errmsg {
93 	int	 e_code;
94 	char	*e_msg;
95 } errmsgs[] = {
96 	{ EUNDEF,	"Undefined error code" },
97 	{ ENOTFOUND,	"File not found" },
98 	{ EACCESS,	"Access violation" },
99 	{ ENOSPACE,	"Disk full or allocation exceeded" },
100 	{ EBADOP,	"Illegal TFTP operation" },
101 	{ EBADID,	"Unknown transfer ID" },
102 	{ EEXISTS,	"File already exists" },
103 	{ ENOUSER,	"No such user" },
104 	{ EOPTNEG,	"Option negotiation failed" },
105 	{ -1,		NULL }
106 };
107 
108 struct options {
109 	const char      *o_type;
110 } options[] = {
111 	{ "tsize" },
112 	{ "timeout" },
113 	{ "blksize" },
114 	{ NULL }
115 };
116 
117 enum opt_enum {
118 	OPT_TSIZE = 0,
119 	OPT_TIMEOUT,
120 	OPT_BLKSIZE
121 };
122 
123 /*
124  * Send the requested file.
125  */
126 void
sendfile(int fd,char * name,char * mode)127 sendfile(int fd, char *name, char *mode)
128 {
129 	struct tftphdr		*dp, *ap; /* data and ack packets */
130 	struct sockaddr_storage	 from, peer;
131 	struct sockaddr_storage	 serv; /* valid server port number */
132 	struct pollfd		 pfd[1];
133 	unsigned long		 amount;
134 	socklen_t		 fromlen;
135 	int			 convert; /* true if converting crlf -> lf */
136 	int			 n, nfds, error, timeouts, size;
137 	uint16_t		 block = 0;
138 	int			 firsttrip = 1;
139 
140 	startclock();		/* start stat's clock */
141 	dp = r_init();		/* reset fillbuf/read-ahead code */
142 	ap = (struct tftphdr *)ackbuf;
143 	file = fdopen(fd, "r");
144 	convert = !strcmp(mode, "netascii");
145 	amount = 0;
146 	memcpy(&peer, &peeraddr, peeraddr.ss_len);
147 	memset(&serv, 0, sizeof(serv));
148 
149 	do {
150 		/* read data from file */
151 		if (firsttrip)
152 			size = makerequest(WRQ, name, dp, mode) - 4;
153 		else {
154 			size = readit(file, &dp, convert, segment_size);
155 			if (size < 0) {
156 				nak(errno + 100, (struct sockaddr *)&peer);
157 				break;
158 			}
159 			dp->th_opcode = htons((u_short)DATA);
160 			dp->th_block = htons(block);
161 		}
162 
163 		/* send data to server and wait for server ACK */
164 		for (timeouts = 0, error = 0; !intrflag;) {
165 			if (timeouts >= maxtimeout) {
166 				printtimeout();
167 				goto abort;
168 			}
169 
170 			if (!error) {
171 				if (trace)
172 					tpacket("sent", dp, size + 4);
173 				if (sendto(f, dp, size + 4, 0,
174 				    (struct sockaddr *)&peer,
175 				    peer.ss_len) != size + 4) {
176 					warn("sendto");
177 					goto abort;
178 				}
179 				if (!firsttrip)
180 					read_ahead(file, convert, segment_size);
181 			}
182 			error = 0;
183 
184 			pfd[0].fd = f;
185 			pfd[0].events = POLLIN;
186 			nfds = poll(pfd, 1, rexmtval * 1000);
187 			if (nfds == 0) {
188 				timeouts += rexmtval;
189 				continue;
190 			}
191 			if (nfds == -1) {
192 				error = 1;
193 				if (errno == EINTR)
194 					continue;
195 				warn("poll");
196 				goto abort;
197 			}
198 			fromlen = sizeof(from);
199 			n = recvfrom(f, ackbuf, packet_size, 0,
200 			    (struct sockaddr *)&from, &fromlen);
201 			if (n == 0) {
202 				warn("recvfrom");
203 				goto abort;
204 			}
205 			if (n == -1) {
206 				error = 1;
207 				if (errno == EINTR)
208 					continue;
209 				warn("recvfrom");
210 				goto abort;
211 			}
212 			if (!serv.ss_family)
213 				serv = from;
214 			else if (!cmpport((struct sockaddr *)&serv,
215 			    (struct sockaddr *)&from)) {
216 				warn("server port mismatch");
217 				goto abort;
218 			}
219 			peer = from;
220 			if (trace)
221 				tpacket("received", ap, n);
222 
223 			ap->th_opcode = ntohs(ap->th_opcode);
224 
225 			if (ap->th_opcode == OACK) {
226 				oack(ap, n, 0);
227 				break;
228 			}
229 
230 			ap->th_block = ntohs(ap->th_block);
231 
232 			if (ap->th_opcode == ERROR) {
233 				printf("Error code %d: %s\n",
234 				    ap->th_code, ap->th_msg);
235 				goto abort;
236 			}
237 			if (ap->th_opcode == ACK) {
238 				int j;
239 				if (ap->th_block == block)
240 					break;
241 				/* re-synchronize with other side */
242 				j = synchnet(f);
243 				if (j && trace)
244 					printf("discarded %d packets\n", j);
245 				if (ap->th_block == (block - 1))
246 					continue;
247 			}
248 			error = 1;	/* received packet does not match */
249 		}
250 
251 		if (firsttrip) {
252 			size = segment_size;
253 			firsttrip = 0;
254 		} else
255 			amount += size;
256 		block++;
257 	} while ((size == segment_size) && !intrflag);
258 
259 abort:
260 	fclose(file);
261 	stopclock();
262 	if (amount > 0) {
263 		if (intrflag)
264 			putchar('\n');
265 		printstats("Sent", amount);
266 	}
267 }
268 
269 /*
270  * Receive a file.
271  */
272 void
recvfile(int fd,char * name,char * mode)273 recvfile(int fd, char *name, char *mode)
274 {
275 	struct tftphdr		*dp, *ap; /* data and ack packets */
276 	struct sockaddr_storage	 from, peer;
277 	struct sockaddr_storage	 serv; /* valid server port number */
278 	struct pollfd		 pfd[1];
279 	unsigned long		 amount;
280 	socklen_t		 fromlen;
281 	int			 convert; /* true if converting crlf -> lf */
282 	int			 n, nfds, error, timeouts, size;
283 	int			 firsttrip;
284 	uint16_t		 block;
285 
286 	startclock();		/* start stat's clock */
287 	dp = w_init();		/* reset fillbuf/read-ahead code */
288 	ap = (struct tftphdr *)ackbuf;
289 	file = fdopen(fd, "w");
290 	convert = !strcmp(mode, "netascii");
291 	n = 0;
292 	block = 1;
293 	amount = 0;
294 	firsttrip = 1;
295 	memcpy(&peer, &peeraddr, peeraddr.ss_len);
296 	memset(&serv, 0, sizeof(serv));
297 
298 options:
299 	do {
300 		/* create new ACK packet */
301 		if (firsttrip) {
302 			size = makerequest(RRQ, name, ap, mode);
303 			firsttrip = 0;
304 		} else {
305 			ap->th_opcode = htons((u_short)ACK);
306 			ap->th_block = htons(block);
307 			size = 4;
308 			block++;
309 		}
310 
311 		/* send ACK to server and wait for server data */
312 		for (timeouts = 0, error = 0; !intrflag;) {
313 			if (timeouts >= maxtimeout) {
314 				printtimeout();
315 				goto abort;
316 			}
317 
318 			if (!error) {
319 				if (trace)
320 					tpacket("sent", ap, size);
321 				if (sendto(f, ackbuf, size, 0,
322 				    (struct sockaddr *)&peer,
323 				    peer.ss_len) != size) {
324 					warn("sendto");
325 					goto abort;
326 				}
327 				write_behind(file, convert);
328 			}
329 			error = 0;
330 
331 			pfd[0].fd = f;
332 			pfd[0].events = POLLIN;
333 			nfds = poll(pfd, 1, rexmtval * 1000);
334 			if (nfds == 0) {
335 				timeouts += rexmtval;
336 				continue;
337 			}
338 			if (nfds == -1) {
339 				error = 1;
340 				if (errno == EINTR)
341 					continue;
342 				warn("poll");
343 				goto abort;
344 			}
345 			fromlen = sizeof(from);
346 			n = recvfrom(f, dp, packet_size, 0,
347 			    (struct sockaddr *)&from, &fromlen);
348 			if (n == 0) {
349 				warn("recvfrom");
350 				goto abort;
351 			}
352 			if (n == -1) {
353 				error = 1;
354 				if (errno == EINTR)
355 					continue;
356 				warn("recvfrom");
357 				goto abort;
358 			}
359 			if (!serv.ss_family)
360 				serv = from;
361 			else if (!cmpport((struct sockaddr *)&serv,
362 			    (struct sockaddr *)&from)) {
363 				warn("server port mismatch");
364 				goto abort;
365 			}
366 			peer = from;
367 			if (trace)
368 				tpacket("received", dp, n);
369 
370 			dp->th_opcode = ntohs(dp->th_opcode);
371 
372 			if (dp->th_opcode == OACK) {
373 				oack(dp, n, 0);
374 				block = 0;
375 				goto options;
376 			}
377 
378 			dp->th_block = ntohs(dp->th_block);
379 
380 			if (dp->th_opcode == ERROR) {
381 				printf("Error code %d: %s\n",
382 				    dp->th_code, dp->th_msg);
383 				goto abort;
384 			}
385 			if (dp->th_opcode == DATA) {
386 				int j;
387 				if (dp->th_block == block)
388 					break;
389 				/* re-synchronize with other side */
390 				j = synchnet(f);
391 				if (j && trace)
392 					printf("discarded %d packets\n", j);
393 				if (dp->th_block == (block - 1))
394 					continue;
395 			}
396 			error = 1;	/* received packet does not match */
397 		}
398 
399 		/* write data to file */
400 		size = writeit(file, &dp, n - 4, convert);
401 		if (size < 0) {
402 			nak(errno + 100, (struct sockaddr *)&peer);
403 			break;
404 		}
405 		amount += size;
406 	} while (size == segment_size && !intrflag);
407 
408 abort:
409 	/* ok to ack, since user has seen err msg */
410 	ap->th_opcode = htons((u_short)ACK);
411 	ap->th_block = htons(block);
412 	(void)sendto(f, ackbuf, 4, 0, (struct sockaddr *)&peer,
413 	    peer.ss_len);
414 	write_behind(file, convert);	/* flush last buffer */
415 
416 	fclose(file);
417 	stopclock();
418 	if (amount > 0) {
419 		if (intrflag)
420 			putchar('\n');
421 		printstats("Received", amount);
422 	}
423 }
424 
425 static int
cmpport(struct sockaddr * sa,struct sockaddr * sb)426 cmpport(struct sockaddr *sa, struct sockaddr *sb)
427 {
428 	char a[NI_MAXSERV], b[NI_MAXSERV];
429 	if (getnameinfo(sa, sa->sa_len, NULL, 0, a, sizeof(a), NI_NUMERICSERV))
430 		return (0);
431 	if (getnameinfo(sb, sb->sa_len, NULL, 0, b, sizeof(b), NI_NUMERICSERV))
432 		return (0);
433 	if (strcmp(a, b) != 0)
434 		return (0);
435 
436 	return (1);
437 }
438 
439 static int
makerequest(int request,const char * name,struct tftphdr * tp,const char * mode)440 makerequest(int request, const char *name, struct tftphdr *tp,
441     const char *mode)
442 {
443 	char		*cp;
444 	int		 len, pktlen;
445 	off_t		 fsize = 0;
446 	struct stat	 st;
447 
448 	tp->th_opcode = htons((u_short)request);
449 	cp = tp->th_stuff;
450 	pktlen = packet_size - offsetof(struct tftphdr, th_stuff);
451 	len = strlen(name) + 1;
452 	strlcpy(cp, name, pktlen);
453 	strlcpy(cp + len, mode, pktlen - len);
454 	len += strlen(mode) + 1;
455 
456 	if (opt_tsize) {
457 		if (request == WRQ) {
458 			stat(name, &st);
459 			fsize = st.st_size;
460 		}
461 		len += snprintf(cp + len, pktlen - len, "%s%c%lld%c",
462 		    options[OPT_TSIZE].o_type, 0, fsize, 0);
463 	}
464 	if (opt_tout)
465 		len += snprintf(cp + len, pktlen - len, "%s%c%d%c",
466 		    options[OPT_TIMEOUT].o_type, 0, rexmtval, 0);
467 	if (opt_blksize)
468 		len += snprintf(cp + len, pktlen - len, "%s%c%d%c",
469 		    options[OPT_BLKSIZE].o_type, 0, opt_blksize, 0);
470 
471 	return (cp + len - (char *)tp);
472 }
473 
474 /*
475  * Send a nak packet (error message).
476  * Error code passed in is one of the
477  * standard TFTP codes, or a UNIX errno
478  * offset by 100.
479  */
480 static void
nak(int error,struct sockaddr * peer)481 nak(int error, struct sockaddr *peer)
482 {
483 	struct errmsg	*pe;
484 	struct tftphdr	*tp;
485 	int		 length;
486 
487 	tp = (struct tftphdr *)ackbuf;
488 	tp->th_opcode = htons((u_short)ERROR);
489 	tp->th_code = htons((u_short)error);
490 	for (pe = errmsgs; pe->e_code >= 0; pe++)
491 		if (pe->e_code == error)
492 			break;
493 	if (pe->e_code < 0) {
494 		pe->e_msg = strerror(error - 100);
495 		tp->th_code = EUNDEF;
496 	}
497 	length = strlcpy(tp->th_msg, pe->e_msg, packet_size) + 5;
498 	if (length > packet_size)
499 		length = packet_size;
500 	if (trace)
501 		tpacket("sent", tp, length);
502 	if (sendto(f, ackbuf, length, 0, peer,
503 	    peer->sa_len) != length)
504 		warn("nak");
505 }
506 
507 static void
tpacket(const char * s,struct tftphdr * tp,int n)508 tpacket(const char *s, struct tftphdr *tp, int n)
509 {
510 	char		*cp, *file;
511 	static char	*opcodes[] =
512 	    { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR", "OACK" };
513 
514 	u_short op = ntohs(tp->th_opcode);
515 
516 	if (op < RRQ || op > OACK)
517 		printf("%s opcode=%x ", s, op);
518 	else
519 		printf("%s %s ", s, opcodes[op]);
520 
521 	switch (op) {
522 	case RRQ:
523 	case WRQ:
524 		n -= 2;
525 		file = cp = tp->th_stuff;
526 		cp = strchr(cp, '\0');
527 		printf("<file=%s, mode=%s", file, cp + 1);
528 		if (has_options)
529 			oack(tp, n, 1);
530 		printf(">\n");
531 		break;
532 	case DATA:
533 		printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), n - 4);
534 		break;
535 	case ACK:
536 		printf("<block=%d>\n", ntohs(tp->th_block));
537 		break;
538 	case ERROR:
539 		printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg);
540 		break;
541 	case OACK:
542 		printf("<");
543 		oack(tp, n, 1);
544 		printf(">\n");
545 		break;
546 	}
547 }
548 
549 static void
startclock(void)550 startclock(void)
551 {
552 	clock_gettime(CLOCK_MONOTONIC, &tstart);
553 }
554 
555 static void
stopclock(void)556 stopclock(void)
557 {
558 	clock_gettime(CLOCK_MONOTONIC, &tstop);
559 }
560 
561 static void
printstats(const char * direction,unsigned long amount)562 printstats(const char *direction, unsigned long amount)
563 {
564 	double	delta;
565 
566 	/* compute delta in 1/10's second units */
567 	delta = ((tstop.tv_sec * 10.) + (tstop.tv_nsec / 100000000)) -
568 	    ((tstart.tv_sec * 10.) + (tstart.tv_nsec / 100000000));
569 	delta = delta / 10.;	/* back to seconds */
570 	printf("%s %lu bytes in %.1f seconds", direction, amount, delta);
571 	if (verbose)
572 		printf(" [%.0f bits/sec]", (amount * 8.) / delta);
573 	putchar('\n');
574 }
575 
576 static void
printtimeout(void)577 printtimeout(void)
578 {
579 	printf("Transfer timed out.\n");
580 }
581 
582 static void
oack(struct tftphdr * tp,int size,int trace)583 oack(struct tftphdr *tp, int size, int trace)
584 {
585 	int	 i, len, off;
586 	char	*opt, *val;
587 
588 	u_short op = ntohs(tp->th_opcode);
589 
590 	opt = tp->th_u.tu_stuff;
591 	val = tp->th_u.tu_stuff;
592 
593 	if (op == RRQ || op == WRQ) {
594 		len = strlen(opt) + 1;
595 		opt = strchr(opt, '\0');
596 		opt++;
597 		len += strlen(opt) + 1;
598 		opt = strchr(opt, '\0');
599 		opt++;
600 		val = opt;
601 		off = len;
602 		if (trace)
603 			printf(", ");
604 	} else
605 		off = 2;
606 
607 	for (i = off, len = 0; i < size - 1; i++) {
608 		if (*val != '\0') {
609 			val++;
610 			continue;
611 		}
612 		/* got option and value */
613 		val++;
614 		if (trace)
615 			printf("%s=%s", opt, val);
616 		else
617 			if (oack_set(opt, val) == -1)
618 				break;
619 		len = strlen(val) + 1;
620 		val += len;
621 		opt = val;
622 		i += len;
623 		if (trace && i < size - 1)
624 			printf(", ");
625 	}
626 }
627 
628 int
oack_set(const char * option,const char * value)629 oack_set(const char *option, const char *value)
630 {
631 	int		 i, n;
632 	const char	*errstr;
633 	struct sockaddr_storage peer;
634 	memcpy(&peer, &peeraddr, peeraddr.ss_len);
635 
636 	for (i = 0; options[i].o_type != NULL; i++) {
637 		if (!strcasecmp(options[i].o_type, option)) {
638 			if (i == OPT_TSIZE) {
639 				/* XXX verify OACK response */
640 			}
641 			if (i == OPT_TIMEOUT) {
642 				/* verify OACK response */
643 				n = strtonum(value, TIMEOUT_MIN, TIMEOUT_MAX,
644 				    &errstr);
645 				if (errstr || rexmtval != n ||
646 				    opt_tout == 0) {
647 					nak(EOPTNEG, (struct sockaddr *)&peer);
648 					intrflag = 1;
649 					return (-1);
650 				}
651 				/* OK */
652 			}
653 			if (i == OPT_BLKSIZE) {
654 				/* verify OACK response */
655 				n = strtonum(value, SEGSIZE_MIN, SEGSIZE_MAX,
656 				    &errstr);
657 				if (errstr || opt_blksize != n ||
658 				    opt_blksize == 0) {
659 					nak(EOPTNEG, (struct sockaddr *)&peer);
660 					intrflag = 1;
661 					return (-1);
662 				}
663 				/* OK, set option */
664 				segment_size = n;
665 				packet_size = segment_size + 4;
666 			}
667 		}
668 	}
669 
670 	return (1);
671 }
672