xref: /csrg-svn/usr.bin/tftp/tftp.c (revision 17269)
114554Ssam #ifndef lint
2*17269Ssam static char sccsid[] = "@(#)tftp.c	4.10 (Berkeley) 10/18/84";
314554Ssam #endif
47770Ssam 
57770Ssam /*
67770Ssam  * TFTP User Program -- Protocol Machines
77770Ssam  */
87770Ssam #include <sys/types.h>
97770Ssam #include <sys/socket.h>
109220Ssam 
119220Ssam #include <netinet/in.h>
1216384Ssam #include <arpa/inet.h>
1312399Ssam #include <arpa/tftp.h>
1412399Ssam 
157770Ssam #include <signal.h>
167770Ssam #include <stdio.h>
177770Ssam #include <errno.h>
187770Ssam #include <setjmp.h>
1916384Ssam #include <netdb.h>
209220Ssam 
217770Ssam extern	int errno;
227770Ssam extern	struct sockaddr_in sin;
237770Ssam extern	char mode[];
247770Ssam int	f;
257770Ssam int	trace;
267770Ssam int	connected;
27*17269Ssam char	sbuf[BUFSIZ];			/* send buffer */
28*17269Ssam char	rbuf[BUFSIZ];			/* receive buffer */
2913018Ssam int	rexmtval;
3013018Ssam int	maxtimeout;
317770Ssam int	timeout;
327770Ssam jmp_buf	toplevel;
3313018Ssam jmp_buf	timeoutbuf;
347770Ssam 
357770Ssam timer()
367770Ssam {
3713018Ssam 
3813018Ssam 	timeout += rexmtval;
3913018Ssam 	if (timeout >= maxtimeout) {
407770Ssam 		printf("Transfer timed out.\n");
417770Ssam 		longjmp(toplevel, -1);
427770Ssam 	}
4313018Ssam 	longjmp(timeoutbuf, 1);
447770Ssam }
457770Ssam 
467770Ssam /*
477770Ssam  * Send the requested file.
487770Ssam  */
497770Ssam sendfile(fd, name)
507770Ssam 	int fd;
517770Ssam 	char *name;
527770Ssam {
53*17269Ssam 	register struct tftphdr *stp = (struct tftphdr *)sbuf;
54*17269Ssam 	register struct tftphdr *rtp = (struct tftphdr *)rbuf;
557770Ssam 	register int block = 0, size, n, amount = 0;
5616384Ssam 	struct sockaddr_in from, to;
577770Ssam 	time_t start = time(0), delta;
5816384Ssam 	int fromlen, aborted = 0;
597770Ssam 
6016384Ssam 	to = sin;
6113018Ssam 	signal(SIGALRM, timer);
627770Ssam 	do {
6313018Ssam 		if (block == 0)
6413018Ssam 			size = makerequest(WRQ, name) - 4;
6513018Ssam 		else {
66*17269Ssam 			size = read(fd, stp->th_data, SEGSIZE);
677770Ssam 			if (size < 0) {
6816384Ssam 				nak(&to, errno + 100);
697770Ssam 				break;
707770Ssam 			}
71*17269Ssam 			stp->th_opcode = htons((u_short)DATA);
72*17269Ssam 			stp->th_block = htons((u_short)block);
737770Ssam 		}
747770Ssam 		timeout = 0;
7513018Ssam 		(void) setjmp(timeoutbuf);
767770Ssam 		if (trace)
77*17269Ssam 			tpacket("sent", &to, stp, size + 4);
78*17269Ssam 		n = sendto(f, sbuf, size + 4, 0, (caddr_t)&to, sizeof (to));
799220Ssam 		if (n != size + 4) {
8013018Ssam 			perror("tftp: sendto");
8116384Ssam 			aborted = 1;
8216384Ssam 			goto done;
837770Ssam 		}
8413018Ssam 		do {
8516384Ssam again:
8613018Ssam 			alarm(rexmtval);
8713018Ssam 			do {
8813018Ssam 				fromlen = sizeof (from);
89*17269Ssam 				n = recvfrom(f, rbuf, sizeof (rbuf), 0,
9013018Ssam 				    (caddr_t)&from, &fromlen);
9113018Ssam 			} while (n <= 0);
927770Ssam 			alarm(0);
9313018Ssam 			if (n < 0) {
9413018Ssam 				perror("tftp: recvfrom");
9516384Ssam 				aborted = 1;
9616384Ssam 				goto done;
9713018Ssam 			}
9816384Ssam 			if (to.sin_addr.s_addr != from.sin_addr.s_addr) {
99*17269Ssam 				tpacket("discarded (wrong host)",
100*17269Ssam 				    &from, rtp, n);
10116384Ssam 				goto again;
10216384Ssam 			}
10316384Ssam 			if (to.sin_port = sin.sin_port)
10416384Ssam 				to.sin_port = from.sin_port;
10516384Ssam 			if (to.sin_port != from.sin_port) {
106*17269Ssam 				tpacket("discarded (wrong port)",
107*17269Ssam 				    &from, rtp, n);
10816384Ssam 				goto again;
10916384Ssam 			}
11013018Ssam 			if (trace)
111*17269Ssam 				tpacket("received", &from, rtp, n);
11213018Ssam 			/* should verify packet came from server */
113*17269Ssam 			rtp->th_opcode = ntohs(rtp->th_opcode);
114*17269Ssam 			rtp->th_block = ntohs(rtp->th_block);
115*17269Ssam 			if (rtp->th_opcode == ERROR) {
116*17269Ssam 				printf("Error code %d: %s\n", rtp->th_code,
117*17269Ssam 					rtp->th_msg);
11816384Ssam 				aborted = 1;
11916384Ssam 				goto done;
12013018Ssam 			}
121*17269Ssam 		} while (rtp->th_opcode != ACK && block != rtp->th_block);
1227770Ssam 		if (block > 0)
1237770Ssam 			amount += size;
1247770Ssam 		block++;
1257770Ssam 	} while (size == SEGSIZE || block == 1);
12616384Ssam 	if (!aborted && amount > 0) {
1277770Ssam 		delta = time(0) - start;
1287770Ssam 		printf("Sent %d bytes in %d seconds.\n", amount, delta);
1297770Ssam 	}
13016384Ssam done:
13116384Ssam 	(void) close(fd);
13216384Ssam 	return (aborted);
1337770Ssam }
1347770Ssam 
1357770Ssam /*
1367770Ssam  * Receive a file.
1377770Ssam  */
1387770Ssam recvfile(fd, name)
1397770Ssam 	int fd;
1407770Ssam 	char *name;
1417770Ssam {
142*17269Ssam 	register struct tftphdr *stp = (struct tftphdr *)sbuf;
143*17269Ssam 	register struct tftphdr *rtp = (struct tftphdr *)rbuf;
1447770Ssam 	register int block = 1, n, size, amount = 0;
14516384Ssam 	struct sockaddr_in from, to;
1467770Ssam 	time_t start = time(0), delta;
14716384Ssam 	int fromlen, firsttrip = 1, aborted = 0;
1487770Ssam 
14916384Ssam 	to = sin;
15013018Ssam 	signal(SIGALRM, timer);
1517770Ssam 	do {
15213018Ssam 		if (firsttrip) {
15313018Ssam 			size = makerequest(RRQ, name);
15413018Ssam 			firsttrip = 0;
15513018Ssam 		} else {
156*17269Ssam 			stp->th_opcode = htons((u_short)ACK);
157*17269Ssam 			stp->th_block = htons((u_short)(block));
15813018Ssam 			size = 4;
15913018Ssam 			block++;
16013018Ssam 		}
1617770Ssam 		timeout = 0;
16213018Ssam 		(void) setjmp(timeoutbuf);
1637770Ssam 		if (trace)
164*17269Ssam 			tpacket("sent", &to, stp, size);
165*17269Ssam 		if (sendto(f, sbuf, size, 0, (caddr_t)&to,
16616384Ssam 		    sizeof (to)) != size) {
16713018Ssam 			alarm(0);
16813018Ssam 			perror("tftp: sendto");
16916384Ssam 			aborted = 1;
17016384Ssam 			goto done;
1717770Ssam 		}
17213018Ssam 		do {
17316384Ssam again:
17413018Ssam 			alarm(rexmtval);
17516384Ssam 			do {
17616384Ssam 				fromlen = sizeof (from);
177*17269Ssam 				n = recvfrom(f, rbuf, sizeof (rbuf), 0,
17813018Ssam 				    (caddr_t)&from, &fromlen);
17916384Ssam 			} while (n <= 0);
1807770Ssam 			alarm(0);
18113018Ssam 			if (n < 0) {
18213018Ssam 				perror("tftp: recvfrom");
18316384Ssam 				aborted = 1;
18416384Ssam 				goto done;
18513018Ssam 			}
18616384Ssam 			if (to.sin_addr.s_addr != from.sin_addr.s_addr) {
187*17269Ssam 				tpacket("discarded (wrong host)",
188*17269Ssam 				    &from, rtp, n);
18916384Ssam 				goto again;
19016384Ssam 			}
19116384Ssam 			if (to.sin_port = sin.sin_port)
19216384Ssam 				to.sin_port = from.sin_port;
19316384Ssam 			if (to.sin_port != from.sin_port) {
194*17269Ssam 				tpacket("discarded (wrong port)",
195*17269Ssam 				    &from, rtp, n);
19616384Ssam 				goto again;
19716384Ssam 			}
19813018Ssam 			if (trace)
199*17269Ssam 				tpacket("received", &from, rtp, n);
200*17269Ssam 			rtp->th_opcode = ntohs(rtp->th_opcode);
201*17269Ssam 			rtp->th_block = ntohs(rtp->th_block);
202*17269Ssam 			if (rtp->th_opcode == ERROR) {
203*17269Ssam 				printf("Error code %d: %s\n", rtp->th_code,
204*17269Ssam 					rtp->th_msg);
20516384Ssam 				aborted = 1;
20616384Ssam 				goto done;
20713018Ssam 			}
208*17269Ssam 		} while (rtp->th_opcode != DATA && rtp->th_block != block);
209*17269Ssam 		size = write(fd, rtp->th_data, n - 4);
2107770Ssam 		if (size < 0) {
21116384Ssam 			perror("tftp: write");
21216384Ssam 			nak(&to, errno + 100);
21316384Ssam 			aborted = 1;
21416384Ssam 			goto done;
2157770Ssam 		}
2167770Ssam 		amount += size;
2177770Ssam 	} while (size == SEGSIZE);
21816384Ssam done:
219*17269Ssam 	stp->th_opcode = htons((u_short)ACK);
220*17269Ssam 	stp->th_block = htons((u_short)block);
221*17269Ssam 	(void) sendto(f, sbuf, 4, 0, &to, sizeof (to));
2227770Ssam 	(void) close(fd);
22316384Ssam 	if (!aborted && amount > 0) {
2247770Ssam 		delta = time(0) - start;
2257770Ssam 		printf("Received %d bytes in %d seconds.\n", amount, delta);
2267770Ssam 	}
22716384Ssam 	return (aborted);
2287770Ssam }
2297770Ssam 
2307770Ssam makerequest(request, name)
2317770Ssam 	int request;
2327770Ssam 	char *name;
2337770Ssam {
234*17269Ssam 	register struct tftphdr *stp;
2357770Ssam 	int size;
2367770Ssam 	register char *cp;
2377770Ssam 
238*17269Ssam 	stp = (struct tftphdr *)sbuf;
239*17269Ssam 	stp->th_opcode = htons((u_short)request);
240*17269Ssam 	strcpy(stp->th_stuff, name);
2417770Ssam 	size = strlen(name);
242*17269Ssam 	cp = stp->th_stuff + strlen(name);
2437770Ssam 	*cp++ = '\0';
2447770Ssam 	strcpy(cp, mode);
2457770Ssam 	cp += sizeof ("netascii") - 1;
2467770Ssam 	*cp++ = '\0';
247*17269Ssam 	return (cp - sbuf);
2487770Ssam }
2497770Ssam 
2507770Ssam struct errmsg {
2517770Ssam 	int	e_code;
2527770Ssam 	char	*e_msg;
2537770Ssam } errmsgs[] = {
2547770Ssam 	{ EUNDEF,	"Undefined error code" },
2557770Ssam 	{ ENOTFOUND,	"File not found" },
2567770Ssam 	{ EACCESS,	"Access violation" },
2577770Ssam 	{ ENOSPACE,	"Disk full or allocation exceeded" },
2587770Ssam 	{ EBADOP,	"Illegal TFTP operation" },
2597770Ssam 	{ EBADID,	"Unknown transfer ID" },
2607770Ssam 	{ EEXISTS,	"File already exists" },
2617770Ssam 	{ ENOUSER,	"No such user" },
2627770Ssam 	{ -1,		0 }
2637770Ssam };
2647770Ssam 
2657770Ssam /*
2667770Ssam  * Send a nak packet (error message).
2677770Ssam  * Error code passed in is one of the
2687770Ssam  * standard TFTP codes, or a UNIX errno
2697770Ssam  * offset by 100.
2707770Ssam  */
27116384Ssam nak(to, error)
27216384Ssam 	struct sockaddr_in *to;
2737770Ssam 	int error;
2747770Ssam {
275*17269Ssam 	register struct tftphdr *stp;
2767770Ssam 	int length;
2777770Ssam 	register struct errmsg *pe;
2787770Ssam 	extern char *sys_errlist[];
2797770Ssam 
280*17269Ssam 	stp = (struct tftphdr *)sbuf;
281*17269Ssam 	stp->th_opcode = htons((u_short)ERROR);
282*17269Ssam 	stp->th_code = htons((u_short)error);
2837770Ssam 	for (pe = errmsgs; pe->e_code >= 0; pe++)
2847770Ssam 		if (pe->e_code == error)
2857770Ssam 			break;
2867770Ssam 	if (pe->e_code < 0)
2877770Ssam 		pe->e_msg = sys_errlist[error - 100];
288*17269Ssam 	strcpy(stp->th_msg, pe->e_msg);
2897770Ssam 	length = strlen(pe->e_msg) + 4;
2907770Ssam 	if (trace)
291*17269Ssam 		tpacket("sent", to, stp, length);
292*17269Ssam 	if (sendto(f, sbuf, length, 0, to, sizeof (*to)) != length)
29316384Ssam 		perror("tftp: nak");
2947770Ssam }
2957770Ssam 
29616384Ssam tpacket(s, sin, tp, n)
29716384Ssam 	struct sockaddr_in *sin;
2987770Ssam 	struct tftphdr *tp;
2997770Ssam 	int n;
3007770Ssam {
3017770Ssam 	static char *opcodes[] =
3027770Ssam 	   { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR" };
3037770Ssam 	register char *cp, *file;
3047770Ssam 	u_short op = ntohs(tp->th_opcode);
3057770Ssam 	char *index();
3067770Ssam 
30716384Ssam 	printf("%s ", s);
30816384Ssam 	if (sin) {
30916384Ssam 		struct hostent *hp = gethostbyaddr(&sin->sin_addr,
31016384Ssam 		     sizeof (sin->sin_addr), AF_INET);
31116384Ssam 
31216384Ssam 		printf("%s.%d ",
31316384Ssam 		    hp == 0 ? inet_ntoa(sin->sin_addr) : hp->h_name,
31416384Ssam 		    ntohs(sin->sin_port));
31516384Ssam 	}
3167770Ssam 	if (op < RRQ || op > ERROR)
31716384Ssam 		printf("opcode=%x ", op);
3187770Ssam 	else
31916384Ssam 		printf("%s ", opcodes[op]);
3207770Ssam 	switch (op) {
3217770Ssam 
3227770Ssam 	case RRQ:
3237770Ssam 	case WRQ:
3247770Ssam 		n -= 2;
3257770Ssam 		file = cp = tp->th_stuff;
3267770Ssam 		cp = index(cp, '\0');
3277770Ssam 		printf("<file=%s, mode=%s>\n", file, cp + 1);
3287770Ssam 		break;
3297770Ssam 
3307770Ssam 	case DATA:
3317770Ssam 		printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), n - 4);
3327770Ssam 		break;
3337770Ssam 
3347770Ssam 	case ACK:
3357770Ssam 		printf("<block=%d>\n", ntohs(tp->th_block));
3367770Ssam 		break;
3377770Ssam 
3387770Ssam 	case ERROR:
3397770Ssam 		printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg);
3407770Ssam 		break;
34117268Ssam 
34217268Ssam 	default:
34317268Ssam 		putchar('\n');
34417268Ssam 		break;
3457770Ssam 	}
3467770Ssam }
347