xref: /csrg-svn/usr.bin/tftp/tftp.c (revision 22404)
1*22404Sdist /*
2*22404Sdist  * Copyright (c) 1983 Regents of the University of California.
3*22404Sdist  * All rights reserved.  The Berkeley software License Agreement
4*22404Sdist  * specifies the terms and conditions for redistribution.
5*22404Sdist  */
6*22404Sdist 
714554Ssam #ifndef lint
8*22404Sdist static char sccsid[] = "@(#)tftp.c	5.1 (Berkeley) 06/06/85";
9*22404Sdist #endif not lint
107770Ssam 
117770Ssam /*
127770Ssam  * TFTP User Program -- Protocol Machines
137770Ssam  */
147770Ssam #include <sys/types.h>
157770Ssam #include <sys/socket.h>
169220Ssam 
179220Ssam #include <netinet/in.h>
1816384Ssam #include <arpa/inet.h>
1912399Ssam #include <arpa/tftp.h>
2012399Ssam 
217770Ssam #include <signal.h>
227770Ssam #include <stdio.h>
237770Ssam #include <errno.h>
247770Ssam #include <setjmp.h>
2516384Ssam #include <netdb.h>
269220Ssam 
277770Ssam extern	int errno;
287770Ssam extern	struct sockaddr_in sin;
297770Ssam extern	char mode[];
307770Ssam int	f;
317770Ssam int	trace;
327770Ssam int	connected;
3317269Ssam char	sbuf[BUFSIZ];			/* send buffer */
3417269Ssam char	rbuf[BUFSIZ];			/* receive buffer */
3513018Ssam int	rexmtval;
3613018Ssam int	maxtimeout;
377770Ssam int	timeout;
387770Ssam jmp_buf	toplevel;
3913018Ssam jmp_buf	timeoutbuf;
407770Ssam 
417770Ssam timer()
427770Ssam {
4313018Ssam 
4413018Ssam 	timeout += rexmtval;
4513018Ssam 	if (timeout >= maxtimeout) {
467770Ssam 		printf("Transfer timed out.\n");
477770Ssam 		longjmp(toplevel, -1);
487770Ssam 	}
4913018Ssam 	longjmp(timeoutbuf, 1);
507770Ssam }
517770Ssam 
527770Ssam /*
537770Ssam  * Send the requested file.
547770Ssam  */
557770Ssam sendfile(fd, name)
567770Ssam 	int fd;
577770Ssam 	char *name;
587770Ssam {
5917269Ssam 	register struct tftphdr *stp = (struct tftphdr *)sbuf;
6017269Ssam 	register struct tftphdr *rtp = (struct tftphdr *)rbuf;
617770Ssam 	register int block = 0, size, n, amount = 0;
6216384Ssam 	struct sockaddr_in from, to;
637770Ssam 	time_t start = time(0), delta;
6416384Ssam 	int fromlen, aborted = 0;
657770Ssam 
6616384Ssam 	to = sin;
6713018Ssam 	signal(SIGALRM, timer);
687770Ssam 	do {
6913018Ssam 		if (block == 0)
7013018Ssam 			size = makerequest(WRQ, name) - 4;
7113018Ssam 		else {
7217269Ssam 			size = read(fd, stp->th_data, SEGSIZE);
737770Ssam 			if (size < 0) {
7416384Ssam 				nak(&to, errno + 100);
757770Ssam 				break;
767770Ssam 			}
7717269Ssam 			stp->th_opcode = htons((u_short)DATA);
7817269Ssam 			stp->th_block = htons((u_short)block);
797770Ssam 		}
807770Ssam 		timeout = 0;
8113018Ssam 		(void) setjmp(timeoutbuf);
827770Ssam 		if (trace)
8317269Ssam 			tpacket("sent", &to, stp, size + 4);
8417269Ssam 		n = sendto(f, sbuf, size + 4, 0, (caddr_t)&to, sizeof (to));
859220Ssam 		if (n != size + 4) {
8613018Ssam 			perror("tftp: sendto");
8716384Ssam 			aborted = 1;
8816384Ssam 			goto done;
897770Ssam 		}
9013018Ssam 		do {
9116384Ssam again:
9213018Ssam 			alarm(rexmtval);
9313018Ssam 			do {
9413018Ssam 				fromlen = sizeof (from);
9517269Ssam 				n = recvfrom(f, rbuf, sizeof (rbuf), 0,
9613018Ssam 				    (caddr_t)&from, &fromlen);
9713018Ssam 			} while (n <= 0);
987770Ssam 			alarm(0);
9913018Ssam 			if (n < 0) {
10013018Ssam 				perror("tftp: recvfrom");
10116384Ssam 				aborted = 1;
10216384Ssam 				goto done;
10313018Ssam 			}
10416384Ssam 			if (to.sin_addr.s_addr != from.sin_addr.s_addr) {
10517269Ssam 				tpacket("discarded (wrong host)",
10617269Ssam 				    &from, rtp, n);
10716384Ssam 				goto again;
10816384Ssam 			}
10916384Ssam 			if (to.sin_port = sin.sin_port)
11016384Ssam 				to.sin_port = from.sin_port;
11116384Ssam 			if (to.sin_port != from.sin_port) {
11217269Ssam 				tpacket("discarded (wrong port)",
11317269Ssam 				    &from, rtp, n);
11416384Ssam 				goto again;
11516384Ssam 			}
11613018Ssam 			if (trace)
11717269Ssam 				tpacket("received", &from, rtp, n);
11813018Ssam 			/* should verify packet came from server */
11917269Ssam 			rtp->th_opcode = ntohs(rtp->th_opcode);
12017269Ssam 			rtp->th_block = ntohs(rtp->th_block);
12117269Ssam 			if (rtp->th_opcode == ERROR) {
12217269Ssam 				printf("Error code %d: %s\n", rtp->th_code,
12317269Ssam 					rtp->th_msg);
12416384Ssam 				aborted = 1;
12516384Ssam 				goto done;
12613018Ssam 			}
12717269Ssam 		} while (rtp->th_opcode != ACK && block != rtp->th_block);
1287770Ssam 		if (block > 0)
1297770Ssam 			amount += size;
1307770Ssam 		block++;
1317770Ssam 	} while (size == SEGSIZE || block == 1);
13216384Ssam 	if (!aborted && amount > 0) {
1337770Ssam 		delta = time(0) - start;
1347770Ssam 		printf("Sent %d bytes in %d seconds.\n", amount, delta);
1357770Ssam 	}
13616384Ssam done:
13716384Ssam 	(void) close(fd);
13816384Ssam 	return (aborted);
1397770Ssam }
1407770Ssam 
1417770Ssam /*
1427770Ssam  * Receive a file.
1437770Ssam  */
1447770Ssam recvfile(fd, name)
1457770Ssam 	int fd;
1467770Ssam 	char *name;
1477770Ssam {
14817269Ssam 	register struct tftphdr *stp = (struct tftphdr *)sbuf;
14917269Ssam 	register struct tftphdr *rtp = (struct tftphdr *)rbuf;
1507770Ssam 	register int block = 1, n, size, amount = 0;
15116384Ssam 	struct sockaddr_in from, to;
1527770Ssam 	time_t start = time(0), delta;
15316384Ssam 	int fromlen, firsttrip = 1, aborted = 0;
1547770Ssam 
15516384Ssam 	to = sin;
15613018Ssam 	signal(SIGALRM, timer);
1577770Ssam 	do {
15813018Ssam 		if (firsttrip) {
15913018Ssam 			size = makerequest(RRQ, name);
16013018Ssam 			firsttrip = 0;
16113018Ssam 		} else {
16217269Ssam 			stp->th_opcode = htons((u_short)ACK);
16317269Ssam 			stp->th_block = htons((u_short)(block));
16413018Ssam 			size = 4;
16513018Ssam 			block++;
16613018Ssam 		}
1677770Ssam 		timeout = 0;
16813018Ssam 		(void) setjmp(timeoutbuf);
1697770Ssam 		if (trace)
17017269Ssam 			tpacket("sent", &to, stp, size);
17117269Ssam 		if (sendto(f, sbuf, size, 0, (caddr_t)&to,
17216384Ssam 		    sizeof (to)) != size) {
17313018Ssam 			alarm(0);
17413018Ssam 			perror("tftp: sendto");
17516384Ssam 			aborted = 1;
17616384Ssam 			goto done;
1777770Ssam 		}
17813018Ssam 		do {
17916384Ssam again:
18013018Ssam 			alarm(rexmtval);
18116384Ssam 			do {
18216384Ssam 				fromlen = sizeof (from);
18317269Ssam 				n = recvfrom(f, rbuf, sizeof (rbuf), 0,
18413018Ssam 				    (caddr_t)&from, &fromlen);
18516384Ssam 			} while (n <= 0);
1867770Ssam 			alarm(0);
18713018Ssam 			if (n < 0) {
18813018Ssam 				perror("tftp: recvfrom");
18916384Ssam 				aborted = 1;
19016384Ssam 				goto done;
19113018Ssam 			}
19216384Ssam 			if (to.sin_addr.s_addr != from.sin_addr.s_addr) {
19317269Ssam 				tpacket("discarded (wrong host)",
19417269Ssam 				    &from, rtp, n);
19516384Ssam 				goto again;
19616384Ssam 			}
19716384Ssam 			if (to.sin_port = sin.sin_port)
19816384Ssam 				to.sin_port = from.sin_port;
19916384Ssam 			if (to.sin_port != from.sin_port) {
20017269Ssam 				tpacket("discarded (wrong port)",
20117269Ssam 				    &from, rtp, n);
20216384Ssam 				goto again;
20316384Ssam 			}
20413018Ssam 			if (trace)
20517269Ssam 				tpacket("received", &from, rtp, n);
20617269Ssam 			rtp->th_opcode = ntohs(rtp->th_opcode);
20717269Ssam 			rtp->th_block = ntohs(rtp->th_block);
20817269Ssam 			if (rtp->th_opcode == ERROR) {
20917269Ssam 				printf("Error code %d: %s\n", rtp->th_code,
21017269Ssam 					rtp->th_msg);
21116384Ssam 				aborted = 1;
21216384Ssam 				goto done;
21313018Ssam 			}
21417269Ssam 		} while (rtp->th_opcode != DATA && rtp->th_block != block);
21517269Ssam 		size = write(fd, rtp->th_data, n - 4);
2167770Ssam 		if (size < 0) {
21716384Ssam 			perror("tftp: write");
21816384Ssam 			nak(&to, errno + 100);
21916384Ssam 			aborted = 1;
22016384Ssam 			goto done;
2217770Ssam 		}
2227770Ssam 		amount += size;
2237770Ssam 	} while (size == SEGSIZE);
22416384Ssam done:
22517269Ssam 	stp->th_opcode = htons((u_short)ACK);
22617269Ssam 	stp->th_block = htons((u_short)block);
22717269Ssam 	(void) sendto(f, sbuf, 4, 0, &to, sizeof (to));
2287770Ssam 	(void) close(fd);
22916384Ssam 	if (!aborted && amount > 0) {
2307770Ssam 		delta = time(0) - start;
2317770Ssam 		printf("Received %d bytes in %d seconds.\n", amount, delta);
2327770Ssam 	}
23316384Ssam 	return (aborted);
2347770Ssam }
2357770Ssam 
2367770Ssam makerequest(request, name)
2377770Ssam 	int request;
2387770Ssam 	char *name;
2397770Ssam {
24017269Ssam 	register struct tftphdr *stp;
2417770Ssam 	int size;
2427770Ssam 	register char *cp;
2437770Ssam 
24417269Ssam 	stp = (struct tftphdr *)sbuf;
24517269Ssam 	stp->th_opcode = htons((u_short)request);
24617269Ssam 	strcpy(stp->th_stuff, name);
2477770Ssam 	size = strlen(name);
24817269Ssam 	cp = stp->th_stuff + strlen(name);
2497770Ssam 	*cp++ = '\0';
2507770Ssam 	strcpy(cp, mode);
2517770Ssam 	cp += sizeof ("netascii") - 1;
2527770Ssam 	*cp++ = '\0';
25317269Ssam 	return (cp - sbuf);
2547770Ssam }
2557770Ssam 
2567770Ssam struct errmsg {
2577770Ssam 	int	e_code;
2587770Ssam 	char	*e_msg;
2597770Ssam } errmsgs[] = {
2607770Ssam 	{ EUNDEF,	"Undefined error code" },
2617770Ssam 	{ ENOTFOUND,	"File not found" },
2627770Ssam 	{ EACCESS,	"Access violation" },
2637770Ssam 	{ ENOSPACE,	"Disk full or allocation exceeded" },
2647770Ssam 	{ EBADOP,	"Illegal TFTP operation" },
2657770Ssam 	{ EBADID,	"Unknown transfer ID" },
2667770Ssam 	{ EEXISTS,	"File already exists" },
2677770Ssam 	{ ENOUSER,	"No such user" },
2687770Ssam 	{ -1,		0 }
2697770Ssam };
2707770Ssam 
2717770Ssam /*
2727770Ssam  * Send a nak packet (error message).
2737770Ssam  * Error code passed in is one of the
2747770Ssam  * standard TFTP codes, or a UNIX errno
2757770Ssam  * offset by 100.
2767770Ssam  */
27716384Ssam nak(to, error)
27816384Ssam 	struct sockaddr_in *to;
2797770Ssam 	int error;
2807770Ssam {
28117269Ssam 	register struct tftphdr *stp;
2827770Ssam 	int length;
2837770Ssam 	register struct errmsg *pe;
2847770Ssam 	extern char *sys_errlist[];
2857770Ssam 
28617269Ssam 	stp = (struct tftphdr *)sbuf;
28717269Ssam 	stp->th_opcode = htons((u_short)ERROR);
28817269Ssam 	stp->th_code = htons((u_short)error);
2897770Ssam 	for (pe = errmsgs; pe->e_code >= 0; pe++)
2907770Ssam 		if (pe->e_code == error)
2917770Ssam 			break;
2927770Ssam 	if (pe->e_code < 0)
2937770Ssam 		pe->e_msg = sys_errlist[error - 100];
29417269Ssam 	strcpy(stp->th_msg, pe->e_msg);
2957770Ssam 	length = strlen(pe->e_msg) + 4;
2967770Ssam 	if (trace)
29717269Ssam 		tpacket("sent", to, stp, length);
29817269Ssam 	if (sendto(f, sbuf, length, 0, to, sizeof (*to)) != length)
29916384Ssam 		perror("tftp: nak");
3007770Ssam }
3017770Ssam 
30216384Ssam tpacket(s, sin, tp, n)
30316384Ssam 	struct sockaddr_in *sin;
3047770Ssam 	struct tftphdr *tp;
3057770Ssam 	int n;
3067770Ssam {
3077770Ssam 	static char *opcodes[] =
3087770Ssam 	   { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR" };
3097770Ssam 	register char *cp, *file;
3107770Ssam 	u_short op = ntohs(tp->th_opcode);
3117770Ssam 	char *index();
3127770Ssam 
31316384Ssam 	printf("%s ", s);
31416384Ssam 	if (sin) {
31516384Ssam 		struct hostent *hp = gethostbyaddr(&sin->sin_addr,
31616384Ssam 		     sizeof (sin->sin_addr), AF_INET);
31716384Ssam 
31816384Ssam 		printf("%s.%d ",
31916384Ssam 		    hp == 0 ? inet_ntoa(sin->sin_addr) : hp->h_name,
32016384Ssam 		    ntohs(sin->sin_port));
32116384Ssam 	}
3227770Ssam 	if (op < RRQ || op > ERROR)
32316384Ssam 		printf("opcode=%x ", op);
3247770Ssam 	else
32516384Ssam 		printf("%s ", opcodes[op]);
3267770Ssam 	switch (op) {
3277770Ssam 
3287770Ssam 	case RRQ:
3297770Ssam 	case WRQ:
3307770Ssam 		n -= 2;
3317770Ssam 		file = cp = tp->th_stuff;
3327770Ssam 		cp = index(cp, '\0');
3337770Ssam 		printf("<file=%s, mode=%s>\n", file, cp + 1);
3347770Ssam 		break;
3357770Ssam 
3367770Ssam 	case DATA:
3377770Ssam 		printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), n - 4);
3387770Ssam 		break;
3397770Ssam 
3407770Ssam 	case ACK:
3417770Ssam 		printf("<block=%d>\n", ntohs(tp->th_block));
3427770Ssam 		break;
3437770Ssam 
3447770Ssam 	case ERROR:
3457770Ssam 		printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg);
3467770Ssam 		break;
34717268Ssam 
34817268Ssam 	default:
34917268Ssam 		putchar('\n');
35017268Ssam 		break;
3517770Ssam 	}
3527770Ssam }
353