xref: /illumos-gate/usr/src/cmd/cmd-inet/usr.bin/tftp/tftp.c (revision 5422785d352a2bb398daceab3d1898a8aa64d006)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
27 /*	  All Rights Reserved  	*/
28 
29 /*
30  * University Copyright- Copyright (c) 1982, 1986, 1988
31  * The Regents of the University of California
32  * All Rights Reserved
33  *
34  * University Acknowledgment- Portions of this document are derived from
35  * software developed by the University of California, Berkeley, and its
36  * contributors.
37  */
38 
39 #pragma ident	"%Z%%M%	%I%	%E% SMI"
40 
41 /*
42  * TFTP User Program -- Protocol Machines
43  */
44 #include <sys/types.h>
45 #include <sys/socket.h>
46 #include <sys/time.h>
47 #include <sys/stat.h>
48 
49 #include <signal.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <unistd.h>
53 #include <errno.h>
54 #include <string.h>
55 #include <stddef.h>
56 #include <inttypes.h>
57 
58 #include "tftpcommon.h"
59 #include "tftpprivate.h"
60 
61 static char	*blksize_str(void);
62 static char	*timeout_str(void);
63 static char	*tsize_str(void);
64 static int	blksize_handler(char *);
65 static int	timeout_handler(char *);
66 static int	tsize_handler(char *);
67 static int	add_options(char *, char *);
68 static int	process_oack(tftpbuf *, int);
69 static void	nak(int);
70 static void	startclock(void);
71 static void	stopclock(void);
72 static void	printstats(char *, off_t);
73 static int	makerequest(int, char *, struct tftphdr *, char *);
74 static void	tpacket(char *, struct tftphdr *, int);
75 
76 static struct options {
77 	char	*opt_name;
78 	char	*(*opt_str)(void);
79 	int	(*opt_handler)(char *);
80 } options[] = {
81 	{ "blksize",	blksize_str, blksize_handler },
82 	{ "timeout",	timeout_str, timeout_handler },
83 	{ "tsize",	tsize_str, tsize_handler },
84 	{ NULL }
85 };
86 
87 static char		optbuf[MAX_OPTVAL_LEN];
88 static boolean_t	tsize_set;
89 
90 static tftpbuf	ackbuf;
91 static int	timeout;
92 static off_t	tsize;
93 static jmp_buf	timeoutbuf;
94 
95 int	blocksize = SEGSIZE;	/* Number of data bytes in a DATA packet */
96 
97 /*ARGSUSED*/
98 static void
99 timer(int signum)
100 {
101 	timeout += rexmtval;
102 	if (timeout >= maxtimeout) {
103 		(void) fputs("Transfer timed out.\n", stderr);
104 		longjmp(toplevel, -1);
105 	}
106 	(void) signal(SIGALRM, timer);
107 	longjmp(timeoutbuf, 1);
108 }
109 
110 /*
111  * Send the requested file.
112  */
113 void
114 tftp_sendfile(int fd, char *name, char *mode)
115 {
116 	struct tftphdr *ap;	/* data and ack packets */
117 	struct tftphdr *dp;
118 	int count = 0, size, n;
119 	ushort_t block = 0;
120 	off_t amount = 0;
121 	struct sockaddr_in6 from;
122 	socklen_t fromlen;
123 	int convert;	/* true if doing nl->crlf conversion */
124 	FILE *file;
125 	struct stat statb;
126 	int errcode;
127 
128 	startclock();	/* start stat's clock */
129 	dp = r_init();	/* reset fillbuf/read-ahead code */
130 	ap = &ackbuf.tb_hdr;
131 	file = fdopen(fd, "r");
132 	convert = (strcmp(mode, "netascii") == 0);
133 
134 	tsize_set = ((tsize_opt != 0) && !convert && (fstat(fd, &statb) == 0));
135 	if (tsize_set)
136 		tsize = statb.st_size;
137 
138 	do {
139 		(void) signal(SIGALRM, timer);
140 		if (count == 0) {
141 			if ((size = makerequest(WRQ, name, dp, mode)) == -1) {
142 				(void) fprintf(stderr,
143 				    "tftp: Error: Write request packet too "
144 				    "big\n");
145 				(void) fclose(file);
146 				return;
147 			}
148 			size -= 4;
149 		} else {
150 			size = readit(file, &dp, convert);
151 			if (size < 0) {
152 				nak(errno + 100);
153 				break;
154 			}
155 			dp->th_opcode = htons((ushort_t)DATA);
156 			dp->th_block = htons((ushort_t)block);
157 		}
158 		timeout = 0;
159 		(void) setjmp(timeoutbuf);
160 		if (trace)
161 			tpacket("sent", dp, size + 4);
162 		n = sendto(f, dp, size + 4, 0,
163 		    (struct sockaddr *)&sin6, sizeof (sin6));
164 		if (n != size + 4) {
165 			perror("tftp: sendto");
166 			goto abort;
167 		}
168 		/* Can't read-ahead first block as OACK may change blocksize */
169 		if (count != 0)
170 			read_ahead(file, convert);
171 		(void) alarm(rexmtval);
172 		for (; ; ) {
173 			(void) sigrelse(SIGALRM);
174 			do {
175 				fromlen = (socklen_t)sizeof (from);
176 				n = recvfrom(f, ackbuf.tb_data,
177 				    sizeof (ackbuf.tb_data), 0,
178 				    (struct sockaddr *)&from, &fromlen);
179 				if (n < 0) {
180 					perror("tftp: recvfrom");
181 					goto abort;
182 				}
183 			} while (n < offsetof(struct tftphdr, th_data));
184 			(void) sighold(SIGALRM);
185 			sin6.sin6_port = from.sin6_port;   /* added */
186 			if (trace)
187 				tpacket("received", ap, n);
188 			/* should verify packet came from server */
189 			ap->th_opcode = ntohs(ap->th_opcode);
190 			if (ap->th_opcode == ERROR) {
191 				ap->th_code = ntohs(ap->th_code);
192 				(void) fprintf(stderr,
193 					"Error code %d", ap->th_code);
194 				if (n > offsetof(struct tftphdr, th_data))
195 					(void) fprintf(stderr, ": %.*s", n -
196 					    offsetof(struct tftphdr, th_data),
197 					    ap->th_msg);
198 				(void) fputc('\n', stderr);
199 				goto abort;
200 			}
201 			if ((count == 0) && (ap->th_opcode == OACK)) {
202 				errcode = process_oack(&ackbuf, n);
203 				if (errcode >= 0) {
204 					nak(errcode);
205 					(void) fputs("Rejected OACK\n",
206 					    stderr);
207 					goto abort;
208 				}
209 				break;
210 			}
211 			if (ap->th_opcode == ACK) {
212 				ap->th_block = ntohs(ap->th_block);
213 				if (ap->th_block == block) {
214 					break;
215 				}
216 				/*
217 				 * Never resend the current DATA packet on
218 				 * receipt of a duplicate ACK, doing so would
219 				 * cause the "Sorcerer's Apprentice Syndrome".
220 				 */
221 			}
222 		}
223 		cancel_alarm();
224 		if (count > 0)
225 			amount += size;
226 		block++;
227 		count++;
228 	} while (size == blocksize || count == 1);
229 abort:
230 	cancel_alarm();
231 	(void) fclose(file);
232 	stopclock();
233 	if (amount > 0)
234 		printstats("Sent", amount);
235 }
236 
237 /*
238  * Receive a file.
239  */
240 void
241 tftp_recvfile(int fd, char *name, char *mode)
242 {
243 	struct tftphdr *ap;
244 	struct tftphdr *dp;
245 	ushort_t block = 1;
246 	int n, size;
247 	unsigned long amount = 0;
248 	struct sockaddr_in6 from;
249 	socklen_t fromlen;
250 	boolean_t firsttrip = B_TRUE;
251 	FILE *file;
252 	int convert;	/* true if converting crlf -> lf */
253 	int errcode;
254 
255 	startclock();
256 	dp = w_init();
257 	ap = &ackbuf.tb_hdr;
258 	file = fdopen(fd, "w");
259 	convert = (strcmp(mode, "netascii") == 0);
260 
261 	tsize_set = (tsize_opt != 0);
262 	if (tsize_set)
263 		tsize = 0;
264 
265 	if ((size = makerequest(RRQ, name, ap, mode)) == -1) {
266 		(void) fprintf(stderr,
267 		    "tftp: Error: Read request packet too big\n");
268 		(void) fclose(file);
269 		return;
270 	}
271 
272 	do {
273 		(void) signal(SIGALRM, timer);
274 		if (firsttrip) {
275 			firsttrip = B_FALSE;
276 		} else {
277 			ap->th_opcode = htons((ushort_t)ACK);
278 			ap->th_block = htons((ushort_t)(block));
279 			size = 4;
280 			block++;
281 		}
282 
283 send_oack_ack:
284 		timeout = 0;
285 		(void) setjmp(timeoutbuf);
286 send_ack:
287 		if (trace)
288 			tpacket("sent", ap, size);
289 		if (sendto(f, ackbuf.tb_data, size, 0, (struct sockaddr *)&sin6,
290 		    sizeof (sin6)) != size) {
291 			(void) alarm(0);
292 			perror("tftp: sendto");
293 			goto abort;
294 		}
295 		if (write_behind(file, convert) < 0) {
296 			nak(errno + 100);
297 			goto abort;
298 		}
299 		(void) alarm(rexmtval);
300 		for (; ; ) {
301 			(void) sigrelse(SIGALRM);
302 			do  {
303 				fromlen = (socklen_t)sizeof (from);
304 				n = recvfrom(f, dp, blocksize + 4, 0,
305 				    (struct sockaddr *)&from, &fromlen);
306 				if (n < 0) {
307 					perror("tftp: recvfrom");
308 					goto abort;
309 				}
310 			} while (n < offsetof(struct tftphdr, th_data));
311 			(void) sighold(SIGALRM);
312 			sin6.sin6_port = from.sin6_port;   /* added */
313 			if (trace)
314 				tpacket("received", dp, n);
315 			/* should verify client address */
316 			dp->th_opcode = ntohs(dp->th_opcode);
317 			if (dp->th_opcode == ERROR) {
318 				dp->th_code = ntohs(dp->th_code);
319 				(void) fprintf(stderr, "Error code %d",
320 				    dp->th_code);
321 				if (n > offsetof(struct tftphdr, th_data))
322 					(void) fprintf(stderr, ": %.*s", n -
323 					    offsetof(struct tftphdr, th_data),
324 					    dp->th_msg);
325 				(void) fputc('\n', stderr);
326 				goto abort;
327 			}
328 			if ((block == 1) && (dp->th_opcode == OACK)) {
329 				errcode = process_oack((tftpbuf *)dp, n);
330 				if (errcode >= 0) {
331 					cancel_alarm();
332 					nak(errcode);
333 					(void) fputs("Rejected OACK\n",
334 					    stderr);
335 					(void) fclose(file);
336 					return;
337 				}
338 				ap->th_opcode = htons((ushort_t)ACK);
339 				ap->th_block = htons(0);
340 				size = 4;
341 				goto send_oack_ack;
342 			}
343 			if (dp->th_opcode == DATA) {
344 				int j;
345 
346 				dp->th_block = ntohs(dp->th_block);
347 				if (dp->th_block == block) {
348 					break;	/* have next packet */
349 				}
350 				/*
351 				 * On an error, try to synchronize
352 				 * both sides.
353 				 */
354 				j = synchnet(f);
355 				if (j < 0) {
356 					perror("tftp: recvfrom");
357 					goto abort;
358 				}
359 				if ((j > 0) && trace) {
360 					(void) printf("discarded %d packets\n",
361 					    j);
362 				}
363 				if (dp->th_block == (block-1)) {
364 					goto send_ack;  /* resend ack */
365 				}
366 			}
367 		}
368 		cancel_alarm();
369 		size = writeit(file, &dp, n - 4, convert);
370 		if (size < 0) {
371 			nak(errno + 100);
372 			goto abort;
373 		}
374 		amount += size;
375 	} while (size == blocksize);
376 
377 	cancel_alarm();
378 	if (write_behind(file, convert) < 0) {	/* flush last buffer */
379 		nak(errno + 100);
380 		goto abort;
381 	}
382 	n = fclose(file);
383 	file = NULL;
384 	if (n == EOF) {
385 		nak(errno + 100);
386 		goto abort;
387 	}
388 
389 	/* ok to ack, since user has seen err msg */
390 	ap->th_opcode = htons((ushort_t)ACK);
391 	ap->th_block = htons((ushort_t)block);
392 	if (trace)
393 		tpacket("sent", ap, 4);
394 	if (sendto(f, ackbuf.tb_data, 4, 0,
395 	    (struct sockaddr *)&sin6, sizeof (sin6)) != 4)
396 		perror("tftp: sendto");
397 
398 abort:
399 	cancel_alarm();
400 	if (file != NULL)
401 		(void) fclose(file);
402 	stopclock();
403 	if (amount > 0)
404 		printstats("Received", amount);
405 }
406 
407 static int
408 makerequest(int request, char *name, struct tftphdr *tp, char *mode)
409 {
410 	char *cp, *cpend;
411 	int len;
412 
413 	tp->th_opcode = htons((ushort_t)request);
414 	cp = (char *)&tp->th_stuff;
415 
416 	/* Maximum size of a request packet is 512 bytes (RFC 2347) */
417 	cpend = (char *)tp + SEGSIZE;
418 
419 	len = strlcpy(cp, name, cpend - cp) + 1;
420 	cp += len;
421 	if (cp > cpend)
422 		return (-1);
423 
424 	len = strlcpy(cp, mode, cpend - cp) + 1;
425 	cp += len;
426 	if (cp > cpend)
427 		return (-1);
428 
429 	len = add_options(cp, cpend);
430 	if (len == -1)
431 		return (-1);
432 	cp += len;
433 
434 	return (cp - (char *)tp);
435 }
436 
437 /*
438  * Return the blksize option value string to include in the request packet.
439  */
440 static char *
441 blksize_str(void)
442 {
443 	blocksize = SEGSIZE;
444 	if (blksize == 0)
445 		return (NULL);
446 
447 	(void) snprintf(optbuf, sizeof (optbuf), "%d", blksize);
448 	return (optbuf);
449 }
450 
451 /*
452  * Return the timeout option value string to include in the request packet.
453  */
454 static char *
455 timeout_str(void)
456 {
457 	if (srexmtval == 0)
458 		return (NULL);
459 
460 	(void) snprintf(optbuf, sizeof (optbuf), "%d", srexmtval);
461 	return (optbuf);
462 }
463 
464 /*
465  * Return the tsize option value string to include in the request packet.
466  */
467 static char *
468 tsize_str(void)
469 {
470 	if (tsize_set == B_FALSE)
471 		return (NULL);
472 
473 	(void) snprintf(optbuf, sizeof (optbuf), OFF_T_FMT, tsize);
474 	return (optbuf);
475 }
476 
477 /*
478  * Validate and action the blksize option value string from the OACK packet.
479  * Returns -1 on success or an error code on failure.
480  */
481 static int
482 blksize_handler(char *optstr)
483 {
484 	char *endp;
485 	int value;
486 
487 	/* Make sure the option was requested */
488 	if (blksize == 0)
489 		return (EOPTNEG);
490 	errno = 0;
491 	value = (int)strtol(optstr, &endp, 10);
492 	if (errno != 0 || value < MIN_BLKSIZE || value > blksize ||
493 	    *endp != '\0')
494 		return (EOPTNEG);
495 	blocksize = value;
496 	return (-1);
497 }
498 
499 /*
500  * Validate and action the timeout option value string from the OACK packet.
501  * Returns -1 on success or an error code on failure.
502  */
503 static int
504 timeout_handler(char *optstr)
505 {
506 	char *endp;
507 	int value;
508 
509 	/* Make sure the option was requested */
510 	if (srexmtval == 0)
511 		return (EOPTNEG);
512 	errno = 0;
513 	value = (int)strtol(optstr, &endp, 10);
514 	if (errno != 0 || value != srexmtval || *endp != '\0')
515 		return (EOPTNEG);
516 	/*
517 	 * Nothing to set, client and server retransmission intervals are
518 	 * set separately in the client.
519 	 */
520 	return (-1);
521 }
522 
523 /*
524  * Validate and action the tsize option value string from the OACK packet.
525  * Returns -1 on success or an error code on failure.
526  */
527 static int
528 tsize_handler(char *optstr)
529 {
530 	char *endp;
531 	longlong_t value;
532 
533 	/* Make sure the option was requested */
534 	if (tsize_set == B_FALSE)
535 		return (EOPTNEG);
536 	errno = 0;
537 	value = strtoll(optstr, &endp, 10);
538 	if (errno != 0 || value < 0 || *endp != '\0')
539 		return (EOPTNEG);
540 #if _FILE_OFFSET_BITS == 32
541 	if (value > MAXOFF_T)
542 		return (ENOSPACE);
543 #endif
544 	/*
545 	 * Don't bother checking the tsize value we specified in a write
546 	 * request is echoed back in the OACK.
547 	 */
548 	if (tsize == 0)
549 		tsize = value;
550 	return (-1);
551 }
552 
553 /*
554  * Add TFTP options to a request packet.
555  */
556 static int
557 add_options(char *obuf, char *obufend)
558 {
559 	int i;
560 	char *cp, *ostr;
561 
562 	cp = obuf;
563 	for (i = 0; options[i].opt_name != NULL; i++) {
564 		ostr = options[i].opt_str();
565 		if (ostr != NULL) {
566 			cp += strlcpy(cp, options[i].opt_name, obufend - cp)
567 			    + 1;
568 			if (cp > obufend)
569 				return (-1);
570 
571 			cp += strlcpy(cp, ostr, obufend - cp) + 1;
572 			if (cp > obufend)
573 				return (-1);
574 		}
575 	}
576 	return (cp - obuf);
577 }
578 
579 /*
580  * Process OACK packet sent by server in response to options in the request
581  * packet. Returns -1 on success or an error code on failure.
582  */
583 static int
584 process_oack(tftpbuf *oackbuf, int n)
585 {
586 	char *cp, *oackend, *optname, *optval;
587 	struct tftphdr *oackp;
588 	int i, errcode;
589 
590 	oackp = &oackbuf->tb_hdr;
591 	cp = (char *)&oackp->th_stuff;
592 	oackend = (char *)oackbuf + n;
593 
594 	while (cp < oackend) {
595 		optname = cp;
596 		if ((optval = next_field(optname, oackend)) == NULL)
597 			return (EOPTNEG);
598 		if ((cp = next_field(optval, oackend)) == NULL)
599 			return (EOPTNEG);
600 		for (i = 0; options[i].opt_name != NULL; i++) {
601 			if (strcasecmp(optname, options[i].opt_name) == 0)
602 				break;
603 		}
604 		if (options[i].opt_name == NULL)
605 			return (EOPTNEG);
606 		errcode = options[i].opt_handler(optval);
607 		if (errcode >= 0)
608 			return (errcode);
609 	}
610 	return (-1);
611 }
612 
613 /*
614  * Send a nak packet (error message).
615  * Error code passed in is one of the
616  * standard TFTP codes, or a UNIX errno
617  * offset by 100.
618  */
619 static void
620 nak(int error)
621 {
622 	struct tftphdr *tp;
623 	int length;
624 	struct errmsg *pe;
625 
626 	tp = &ackbuf.tb_hdr;
627 	tp->th_opcode = htons((ushort_t)ERROR);
628 	tp->th_code = htons((ushort_t)error);
629 	for (pe = errmsgs; pe->e_code >= 0; pe++)
630 		if (pe->e_code == error)
631 			break;
632 	if (pe->e_code < 0) {
633 		pe->e_msg = strerror(error - 100);
634 		tp->th_code = EUNDEF;
635 	}
636 	(void) strlcpy(tp->th_msg, pe->e_msg,
637 	    sizeof (ackbuf) - sizeof (struct tftphdr));
638 	length = strlen(pe->e_msg) + 4;
639 	if (trace)
640 		tpacket("sent", tp, length);
641 	if (sendto(f, ackbuf.tb_data, length, 0,
642 	    (struct sockaddr *)&sin6, sizeof (sin6)) != length)
643 		perror("nak");
644 }
645 
646 static void
647 tpacket(char *s, struct tftphdr *tp, int n)
648 {
649 	static char *opcodes[] = \
650 		{ "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR", "OACK" };
651 	char *cp, *file, *mode;
652 	ushort_t op = ntohs(tp->th_opcode);
653 	char *tpend;
654 
655 	if (op < RRQ || op > OACK)
656 		(void) printf("%s opcode=%x ", s, op);
657 	else
658 		(void) printf("%s %s ", s, opcodes[op]);
659 
660 	switch (op) {
661 	case RRQ:
662 	case WRQ:
663 		tpend = (char *)tp + n;
664 		n -= sizeof (tp->th_opcode);
665 		file = (char *)&tp->th_stuff;
666 		if ((mode = next_field(file, tpend)) == NULL) {
667 			(void) printf("<file=%.*s>\n", n, file);
668 			break;
669 		}
670 		n -= mode - file;
671 		if ((cp = next_field(mode, tpend)) == NULL) {
672 			(void) printf("<file=%s, mode=%.*s>\n", file, n, mode);
673 			break;
674 		}
675 		(void) printf("<file=%s, mode=%s", file, mode);
676 		n -= cp - mode;
677 		if (n > 0) {
678 			(void) printf(", options: ");
679 			print_options(stdout, cp, n);
680 		}
681 		(void) puts(">");
682 		break;
683 
684 	case DATA:
685 		(void) printf("<block=%d, %d bytes>\n", ntohs(tp->th_block),
686 		    n - sizeof (tp->th_opcode) - sizeof (tp->th_block));
687 		break;
688 
689 	case ACK:
690 		(void) printf("<block=%d>\n", ntohs(tp->th_block));
691 		break;
692 
693 	case OACK:
694 		(void) printf("<options: ");
695 		print_options(stdout, (char *)&tp->th_stuff,
696 		    n - sizeof (tp->th_opcode));
697 		(void) puts(">");
698 		break;
699 
700 	case ERROR:
701 		(void) printf("<code=%d", ntohs(tp->th_code));
702 		n = n - sizeof (tp->th_opcode) - sizeof (tp->th_code);
703 		if (n > 0)
704 			(void) printf(", msg=%.*s", n, tp->th_msg);
705 		(void) puts(">");
706 		break;
707 	}
708 }
709 
710 static hrtime_t	tstart, tstop;
711 
712 static void
713 startclock(void)
714 {
715 	tstart = gethrtime();
716 }
717 
718 static void
719 stopclock(void)
720 {
721 	tstop = gethrtime();
722 }
723 
724 static void
725 printstats(char *direction, off_t amount)
726 {
727 	hrtime_t	delta, tenths;
728 
729 	delta = tstop - tstart;
730 	tenths = delta / (NANOSEC / 10);
731 	(void) printf("%s " OFF_T_FMT " bytes in %" PRId64 ".%" PRId64
732 	    " seconds", direction, amount, tenths / 10, tenths % 10);
733 	if (verbose)
734 		(void) printf(" [%" PRId64 " bits/sec]\n",
735 		    ((hrtime_t)amount * 8 * NANOSEC) / delta);
736 	else
737 		(void) putchar('\n');
738 }
739