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