xref: /openbsd-src/usr.bin/tftp/tftp.c (revision 7bbe964f6b7d22ad07ca46292495604f942eba4e)
1 /*	$OpenBSD: tftp.c,v 1.22 2009/10/27 23:59:44 deraadt 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 <unistd.h>
56 
57 #include "extern.h"
58 #include "tftpsubs.h"
59 
60 static int	makerequest(int, const char *, struct tftphdr *, const char *);
61 static void	nak(int);
62 static void 	tpacket(const char *, struct tftphdr *, int);
63 static void	startclock(void);
64 static void	stopclock(void);
65 static void	printstats(const char *, unsigned long);
66 static void	printtimeout(void);
67 static void	oack(struct tftphdr *, int, int);
68 static int	oack_set(const char *, const char *);
69 
70 extern struct sockaddr_in	 peeraddr;	/* filled in by main */
71 extern int			 f;		/* the opened socket */
72 extern int			 trace;
73 extern int			 verbose;
74 extern int			 rexmtval;
75 extern int			 maxtimeout;
76 extern FILE			*file;
77 extern volatile sig_atomic_t	 intrflag;
78 extern char			*ackbuf;
79 extern int			 has_options;
80 extern int			 opt_tsize;
81 extern int			 opt_tout;
82 extern int			 opt_blksize;
83 
84 struct timeval	tstart;
85 struct timeval	tstop;
86 unsigned int	segment_size = SEGSIZE;
87 unsigned int	packet_size = SEGSIZE + 4;
88 
89 struct errmsg {
90 	int	 e_code;
91 	char	*e_msg;
92 } errmsgs[] = {
93 	{ EUNDEF,	"Undefined error code" },
94 	{ ENOTFOUND,	"File not found" },
95 	{ EACCESS,	"Access violation" },
96 	{ ENOSPACE,	"Disk full or allocation exceeded" },
97 	{ EBADOP,	"Illegal TFTP operation" },
98 	{ EBADID,	"Unknown transfer ID" },
99 	{ EEXISTS,	"File already exists" },
100 	{ ENOUSER,	"No such user" },
101 	{ EOPTNEG,	"Option negotiation failed" },
102 	{ -1,		NULL }
103 };
104 
105 struct options {
106 	const char      *o_type;
107 } options[] = {
108 	{ "tsize" },
109 	{ "timeout" },
110 	{ "blksize" },
111 	{ NULL }
112 };
113 
114 enum opt_enum {
115 	OPT_TSIZE = 0,
116 	OPT_TIMEOUT,
117 	OPT_BLKSIZE
118 };
119 
120 /*
121  * Send the requested file.
122  */
123 void
124 sendfile(int fd, char *name, char *mode)
125 {
126 	struct tftphdr		*dp, *ap; /* data and ack packets */
127 	struct sockaddr_in	 from;
128 	struct pollfd		 pfd[1];
129 	unsigned long		 amount;
130 	socklen_t		 fromlen;
131 	int			 convert; /* true if converting crlf -> lf */
132 	int			 n, nfds, error, timeouts, block, size;
133 
134 	startclock();		/* start stat's clock */
135 	dp = r_init();		/* reset fillbuf/read-ahead code */
136 	ap = (struct tftphdr *)ackbuf;
137 	file = fdopen(fd, "r");
138 	convert = !strcmp(mode, "netascii");
139 	block = 0;
140 	amount = 0;
141 
142 	do {
143 		/* read data from file */
144 		if (!block)
145 			size = makerequest(WRQ, name, dp, mode) - 4;
146 		else {
147 			size = readit(file, &dp, convert, segment_size);
148 			if (size < 0) {
149 				nak(errno + 100);
150 				break;
151 			}
152 			dp->th_opcode = htons((u_short)DATA);
153 			dp->th_block = htons((u_short)block);
154 		}
155 
156 		/* send data to server and wait for server ACK */
157 		for (timeouts = 0, error = 0; !intrflag;) {
158 			if (timeouts >= maxtimeout) {
159 				printtimeout();
160 				goto abort;
161 			}
162 
163 			if (!error) {
164 				if (trace)
165 					tpacket("sent", dp, size + 4);
166 				if (sendto(f, dp, size + 4, 0,
167 		    		    (struct sockaddr *)&peeraddr,
168 				    sizeof(peeraddr)) != size + 4) {
169 					warn("sendto");
170 					goto abort;
171 				}
172 				if (block > 0)
173 					read_ahead(file, convert, segment_size);
174 			}
175 			error = 0;
176 
177 			pfd[0].fd = f;
178 			pfd[0].events = POLLIN;
179 			nfds = poll(pfd, 1, rexmtval * 1000);
180 			if (nfds == 0) {
181 				timeouts += rexmtval;
182 				continue;
183 			}
184 			if (nfds == -1) {
185 				error = 1;
186 				if (errno == EINTR)
187 					continue;
188 				warn("poll");
189 				goto abort;
190 			}
191 			fromlen = sizeof(from);
192 			n = recvfrom(f, ackbuf, packet_size, 0,
193 			    (struct sockaddr *)&from, &fromlen);
194 			if (n == 0) {
195 				warn("recvfrom");
196 				goto abort;
197 			}
198 			if (n == -1) {
199 				error = 1;
200 				if (errno == EINTR)
201 					continue;
202 				warn("recvfrom");
203 				goto abort;
204 			}
205 			peeraddr.sin_port = from.sin_port;	/* added */
206 			if (trace)
207 				tpacket("received", ap, n);
208 
209 			ap->th_opcode = ntohs(ap->th_opcode);
210 
211 			if (ap->th_opcode == OACK) {
212 				oack(ap, n, 0);
213 				break;
214 			}
215 
216 			ap->th_block = ntohs(ap->th_block);
217 
218 			if (ap->th_opcode == ERROR) {
219 				printf("Error code %d: %s\n",
220 				    ap->th_code, ap->th_msg);
221 				goto abort;
222 			}
223 			if (ap->th_opcode == ACK) {
224 				int j;
225 				if (ap->th_block == block)
226 					break;
227 				/* re-synchronize with other side */
228 				j = synchnet(f);
229 				if (j && trace)
230 					printf("discarded %d packets\n", j);
231 				if (ap->th_block == (block - 1))
232 					continue;
233 			}
234 			error = 1;	/* received packet does not match */
235 		}
236 
237 		if (block > 0)
238 			amount += size;
239 		block++;
240 	} while ((size == segment_size || block == 1) && !intrflag);
241 
242 abort:
243 	fclose(file);
244 	stopclock();
245 	if (amount > 0) {
246 		if (intrflag)
247 			putchar('\n');
248 		printstats("Sent", amount);
249 	}
250 }
251 
252 /*
253  * Receive a file.
254  */
255 void
256 recvfile(int fd, char *name, char *mode)
257 {
258 	struct tftphdr		*dp, *ap; /* data and ack packets */
259 	struct sockaddr_in	 from;
260 	struct pollfd		 pfd[1];
261 	unsigned long		 amount;
262 	socklen_t		 fromlen;
263 	int			 convert; /* true if converting crlf -> lf */
264 	int			 n, nfds, error, timeouts, block, size;
265 	int			 firsttrip;
266 
267 	startclock();		/* start stat's clock */
268 	dp = w_init();		/* reset fillbuf/read-ahead code */
269 	ap = (struct tftphdr *)ackbuf;
270 	file = fdopen(fd, "w");
271 	convert = !strcmp(mode, "netascii");
272 	n = 0;
273 	block = 1;
274 	amount = 0;
275 	firsttrip = 1;
276 
277 options:
278 	do {
279 		/* create new ACK packet */
280 		if (firsttrip) {
281 			size = makerequest(RRQ, name, ap, mode);
282 			firsttrip = 0;
283 		} else {
284 			ap->th_opcode = htons((u_short)ACK);
285 			ap->th_block = htons((u_short)(block));
286 			size = 4;
287 			block++;
288 		}
289 
290 		/* send ACK to server and wait for server data */
291 		for (timeouts = 0, error = 0; !intrflag;) {
292 			if (timeouts >= maxtimeout) {
293 				printtimeout();
294 				goto abort;
295 			}
296 
297 			if (!error) {
298 				if (trace)
299 					tpacket("sent", ap, size);
300 				if (sendto(f, ackbuf, size, 0,
301 			    	    (struct sockaddr *)&peeraddr,
302 				    sizeof(peeraddr)) != size) {
303 					warn("sendto");
304 					goto abort;
305 				}
306 				write_behind(file, convert);
307 			}
308 			error = 0;
309 
310 			pfd[0].fd = f;
311 			pfd[0].events = POLLIN;
312 			nfds = poll(pfd, 1, rexmtval * 1000);
313 			if (nfds == 0) {
314 				timeouts += rexmtval;
315 				continue;
316 			}
317 			if (nfds == -1) {
318 				error = 1;
319 				if (errno == EINTR)
320 					continue;
321 				warn("poll");
322 				goto abort;
323 			}
324 			fromlen = sizeof(from);
325 			n = recvfrom(f, dp, packet_size, 0,
326 			    (struct sockaddr *)&from, &fromlen);
327 			if (n == 0) {
328 				warn("recvfrom");
329 				goto abort;
330 			}
331 			if (n == -1) {
332 				error = 1;
333 				if (errno == EINTR)
334 					continue;
335 				warn("recvfrom");
336 				goto abort;
337 			}
338 			peeraddr.sin_port = from.sin_port;	/* added */
339 			if (trace)
340 				tpacket("received", dp, n);
341 
342 			dp->th_opcode = ntohs(dp->th_opcode);
343 
344 			if (dp->th_opcode == OACK) {
345 				oack(dp, n, 0);
346 				block = 0;
347 				goto options;
348 			}
349 
350 			dp->th_block = ntohs(dp->th_block);
351 
352 			if (dp->th_opcode == ERROR) {
353 				printf("Error code %d: %s\n",
354 				    dp->th_code, dp->th_msg);
355 				goto abort;
356 			}
357 			if (dp->th_opcode == DATA) {
358 				int j;
359 				if (dp->th_block == block)
360 					break;
361 				/* re-synchronize with other side */
362 				j = synchnet(f);
363 				if (j && trace)
364 					printf("discarded %d packets\n", j);
365 				if (dp->th_block == (block - 1))
366 					continue;
367 			}
368 			error = 1;	/* received packet does not match */
369 		}
370 
371 		/* write data to file */
372 		size = writeit(file, &dp, n - 4, convert);
373 		if (size < 0) {
374 			nak(errno + 100);
375 			break;
376 		}
377 		amount += size;
378 	} while (size == segment_size && !intrflag);
379 
380 abort:
381 	/* ok to ack, since user has seen err msg */
382 	ap->th_opcode = htons((u_short)ACK);
383 	ap->th_block = htons((u_short)block);
384 	(void)sendto(f, ackbuf, 4, 0, (struct sockaddr *)&peeraddr,
385 	    sizeof(peeraddr));
386 	write_behind(file, convert);	/* flush last buffer */
387 
388 	fclose(file);
389 	stopclock();
390 	if (amount > 0) {
391 		if (intrflag)
392 			putchar('\n');
393 		printstats("Received", amount);
394 	}
395 }
396 
397 static int
398 makerequest(int request, const char *name, struct tftphdr *tp,
399     const char *mode)
400 {
401 	char		*cp;
402 	int		 len, pktlen;
403 	off_t		 fsize = 0;
404 	struct stat	 st;
405 
406 	tp->th_opcode = htons((u_short)request);
407 	cp = tp->th_stuff;
408 	pktlen = packet_size - offsetof(struct tftphdr, th_stuff);
409 	len = strlen(name) + 1;
410 	strlcpy(cp, name, pktlen);
411 	strlcpy(cp + len, mode, pktlen - len);
412 	len += strlen(mode) + 1;
413 
414 	if (opt_tsize) {
415 		if (request == WRQ) {
416 			stat(name, &st);
417 			fsize = st.st_size;
418 		}
419 		len += snprintf(cp + len, pktlen - len, "%s%c%lld%c",
420 		    options[OPT_TSIZE].o_type, 0, fsize, 0);
421 	}
422 	if (opt_tout)
423 		len += snprintf(cp + len, pktlen - len, "%s%c%d%c",
424 		    options[OPT_TIMEOUT].o_type, 0, rexmtval, 0);
425 	if (opt_blksize)
426 		len += snprintf(cp + len, pktlen - len, "%s%c%d%c",
427 		    options[OPT_BLKSIZE].o_type, 0, opt_blksize, 0);
428 
429 	return (cp + len - (char *)tp);
430 }
431 
432 /*
433  * Send a nak packet (error message).
434  * Error code passed in is one of the
435  * standard TFTP codes, or a UNIX errno
436  * offset by 100.
437  */
438 static void
439 nak(int error)
440 {
441 	struct errmsg	*pe;
442 	struct tftphdr	*tp;
443 	int		 length;
444 
445 	tp = (struct tftphdr *)ackbuf;
446 	tp->th_opcode = htons((u_short)ERROR);
447 	tp->th_code = htons((u_short)error);
448 	for (pe = errmsgs; pe->e_code >= 0; pe++)
449 		if (pe->e_code == error)
450 			break;
451 	if (pe->e_code < 0) {
452 		pe->e_msg = strerror(error - 100);
453 		tp->th_code = EUNDEF;
454 	}
455 	length = strlcpy(tp->th_msg, pe->e_msg, packet_size) + 5;
456 	if (length > packet_size)
457 		length = packet_size;
458 	if (trace)
459 		tpacket("sent", tp, length);
460 	if (sendto(f, ackbuf, length, 0, (struct sockaddr *)&peeraddr,
461 	    sizeof(peeraddr)) != length)
462 		warn("nak");
463 }
464 
465 static void
466 tpacket(const char *s, struct tftphdr *tp, int n)
467 {
468 	char		*cp, *file;
469 	static char	*opcodes[] =
470 	    { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR", "OACK" };
471 
472 	u_short op = ntohs(tp->th_opcode);
473 
474 	if (op < RRQ || op > OACK)
475 		printf("%s opcode=%x ", s, op);
476 	else
477 		printf("%s %s ", s, opcodes[op]);
478 
479 	switch (op) {
480 	case RRQ:
481 	case WRQ:
482 		n -= 2;
483 		file = cp = tp->th_stuff;
484 		cp = strchr(cp, '\0');
485 		printf("<file=%s, mode=%s", file, cp + 1);
486 		if (has_options)
487 			oack(tp, n, 1);
488 		printf(">\n");
489 		break;
490 	case DATA:
491 		printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), n - 4);
492 		break;
493 	case ACK:
494 		printf("<block=%d>\n", ntohs(tp->th_block));
495 		break;
496 	case ERROR:
497 		printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg);
498 		break;
499 	case OACK:
500 		printf("<");
501 		oack(tp, n, 1);
502 		printf(">\n");
503 		break;
504 	}
505 }
506 
507 static void
508 startclock(void)
509 {
510 	(void)gettimeofday(&tstart, NULL);
511 }
512 
513 static void
514 stopclock(void)
515 {
516 	(void)gettimeofday(&tstop, NULL);
517 }
518 
519 static void
520 printstats(const char *direction, unsigned long amount)
521 {
522 	double	delta;
523 
524 	/* compute delta in 1/10's second units */
525 	delta = ((tstop.tv_sec * 10.) + (tstop.tv_usec / 100000)) -
526 	    ((tstart.tv_sec * 10.) + (tstart.tv_usec / 100000));
527 	delta = delta / 10.;	/* back to seconds */
528 	printf("%s %lu bytes in %.1f seconds", direction, amount, delta);
529 	if (verbose)
530 		printf(" [%.0f bits/sec]", (amount * 8.) / delta);
531 	putchar('\n');
532 }
533 
534 static void
535 printtimeout(void)
536 {
537 	printf("Transfer timed out.\n");
538 }
539 
540 static void
541 oack(struct tftphdr *tp, int size, int trace)
542 {
543 	int	 i, len, off;
544 	char	*opt, *val;
545 
546 	u_short op = ntohs(tp->th_opcode);
547 
548 	opt = tp->th_u.tu_stuff;
549 	val = tp->th_u.tu_stuff;
550 
551 	if (op == RRQ || op == WRQ) {
552 		len = strlen(opt) + 1;
553 		opt = strchr(opt, '\0');
554 		opt++;
555 		len += strlen(opt) + 1;
556 		opt = strchr(opt, '\0');
557 		opt++;
558 		val = opt;
559 		off = len;
560 		if (trace)
561 			printf(", ");
562 	} else
563 		off = 2;
564 
565 	for (i = off, len = 0; i < size - 1; i++) {
566 		if (*val != '\0') {
567 			val++;
568 			continue;
569 		}
570 		/* got option and value */
571 		val++;
572 		if (trace)
573 			printf("%s=%s", opt, val);
574 		else
575 			if (oack_set(opt, val) == -1)
576 				break;
577 		len = strlen(val) + 1;
578 		val += len;
579 		opt = val;
580 		i += len;
581 		if (trace && i < size - 1)
582 			printf(", ");
583 	}
584 }
585 
586 int
587 oack_set(const char *option, const char *value)
588 {
589 	int		 i, n;
590 	const char	*errstr;
591 
592 	for (i = 0; options[i].o_type != NULL; i++) {
593 		if (!strcasecmp(options[i].o_type, option)) {
594 			if (i == OPT_TSIZE) {
595 				/* XXX verify OACK response */
596 			}
597 			if (i == OPT_TIMEOUT) {
598 				/* verify OACK response */
599 				n = strtonum(value, TIMEOUT_MIN, TIMEOUT_MAX,
600 				    &errstr);
601 				if (errstr || rexmtval != n ||
602 				    opt_tout == 0) {
603 					nak(EOPTNEG);
604 					intrflag = 1;
605 					return (-1);
606 				}
607 				/* OK */
608 			}
609 			if (i == OPT_BLKSIZE) {
610 				/* verify OACK response */
611 				n = strtonum(value, SEGSIZE_MIN, SEGSIZE_MAX,
612 				    &errstr);
613 				if (errstr || opt_blksize != n ||
614 				    opt_blksize == 0) {
615 					nak(EOPTNEG);
616 					intrflag = 1;
617 					return (-1);
618 				}
619 				/* OK, set option */
620 				segment_size = n;
621 				packet_size = segment_size + 4;
622 			}
623 		}
624 	}
625 
626 	return (1);
627 }
628