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