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
61static char	*blksize_str(void);
62static char	*timeout_str(void);
63static char	*tsize_str(void);
64static int	blksize_handler(char *);
65static int	timeout_handler(char *);
66static int	tsize_handler(char *);
67static int	add_options(char *, char *);
68static int	process_oack(tftpbuf *, int);
69static void	nak(int);
70static void	startclock(void);
71static void	stopclock(void);
72static void	printstats(char *, off_t);
73static int	makerequest(int, char *, struct tftphdr *, char *);
74static void	tpacket(char *, struct tftphdr *, int);
75
76static 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
87static char		optbuf[MAX_OPTVAL_LEN];
88static boolean_t	tsize_set;
89
90static tftpbuf	ackbuf;
91static int	timeout;
92static off_t	tsize;
93static jmp_buf	timeoutbuf;
94
95int	blocksize = SEGSIZE;	/* Number of data bytes in a DATA packet */
96
97/*ARGSUSED*/
98static void
99timer(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 */
113void
114tftp_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);
229abort:
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 */
240void
241tftp_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
283send_oack_ack:
284		timeout = 0;
285		(void) setjmp(timeoutbuf);
286send_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
398abort:
399	cancel_alarm();
400	if (file != NULL)
401		(void) fclose(file);
402	stopclock();
403	if (amount > 0)
404		printstats("Received", amount);
405}
406
407static int
408makerequest(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 */
440static char *
441blksize_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 */
454static char *
455timeout_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 */
467static char *
468tsize_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 */
481static int
482blksize_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 */
503static int
504timeout_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 */
527static int
528tsize_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 */
556static int
557add_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 */
583static int
584process_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 */
619static void
620nak(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
646static void
647tpacket(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
710static hrtime_t	tstart, tstop;
711
712static void
713startclock(void)
714{
715	tstart = gethrtime();
716}
717
718static void
719stopclock(void)
720{
721	tstop = gethrtime();
722}
723
724static void
725printstats(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