xref: /csrg-svn/usr.bin/tftp/tftp.c (revision 17268)
114554Ssam #ifndef lint
2*17268Ssam static char sccsid[] = "@(#)tftp.c	4.9 (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;
277770Ssam char	buf[BUFSIZ];
2813018Ssam int	rexmtval;
2913018Ssam int	maxtimeout;
307770Ssam int	timeout;
317770Ssam jmp_buf	toplevel;
3213018Ssam jmp_buf	timeoutbuf;
337770Ssam 
347770Ssam timer()
357770Ssam {
3613018Ssam 
3713018Ssam 	timeout += rexmtval;
3813018Ssam 	if (timeout >= maxtimeout) {
397770Ssam 		printf("Transfer timed out.\n");
407770Ssam 		longjmp(toplevel, -1);
417770Ssam 	}
4213018Ssam 	longjmp(timeoutbuf, 1);
437770Ssam }
447770Ssam 
457770Ssam /*
467770Ssam  * Send the requested file.
477770Ssam  */
487770Ssam sendfile(fd, name)
497770Ssam 	int fd;
507770Ssam 	char *name;
517770Ssam {
527770Ssam 	register struct tftphdr *tp = (struct tftphdr *)buf;
537770Ssam 	register int block = 0, size, n, amount = 0;
5416384Ssam 	struct sockaddr_in from, to;
557770Ssam 	time_t start = time(0), delta;
5616384Ssam 	int fromlen, aborted = 0;
577770Ssam 
5816384Ssam 	to = sin;
5913018Ssam 	signal(SIGALRM, timer);
607770Ssam 	do {
6113018Ssam 		if (block == 0)
6213018Ssam 			size = makerequest(WRQ, name) - 4;
6313018Ssam 		else {
647770Ssam 			size = read(fd, tp->th_data, SEGSIZE);
657770Ssam 			if (size < 0) {
6616384Ssam 				nak(&to, errno + 100);
677770Ssam 				break;
687770Ssam 			}
697770Ssam 			tp->th_opcode = htons((u_short)DATA);
707770Ssam 			tp->th_block = htons((u_short)block);
717770Ssam 		}
727770Ssam 		timeout = 0;
7313018Ssam 		(void) setjmp(timeoutbuf);
747770Ssam 		if (trace)
7516384Ssam 			tpacket("sent", &to, tp, size + 4);
7616384Ssam 		n = sendto(f, buf, size + 4, 0, (caddr_t)&to, sizeof (to));
779220Ssam 		if (n != size + 4) {
7813018Ssam 			perror("tftp: sendto");
7916384Ssam 			aborted = 1;
8016384Ssam 			goto done;
817770Ssam 		}
8213018Ssam 		do {
8316384Ssam again:
8413018Ssam 			alarm(rexmtval);
8513018Ssam 			do {
8613018Ssam 				fromlen = sizeof (from);
8713018Ssam 				n = recvfrom(f, buf, sizeof (buf), 0,
8813018Ssam 				    (caddr_t)&from, &fromlen);
8913018Ssam 			} while (n <= 0);
907770Ssam 			alarm(0);
9113018Ssam 			if (n < 0) {
9213018Ssam 				perror("tftp: recvfrom");
9316384Ssam 				aborted = 1;
9416384Ssam 				goto done;
9513018Ssam 			}
9616384Ssam 			if (to.sin_addr.s_addr != from.sin_addr.s_addr) {
9716384Ssam 				tpacket("discarded (wrong host)", &from, tp, n);
9816384Ssam 				goto again;
9916384Ssam 			}
10016384Ssam 			if (to.sin_port = sin.sin_port)
10116384Ssam 				to.sin_port = from.sin_port;
10216384Ssam 			if (to.sin_port != from.sin_port) {
10316384Ssam 				tpacket("discarded (wrong port)", &from, tp, n);
10416384Ssam 				goto again;
10516384Ssam 			}
10613018Ssam 			if (trace)
10716384Ssam 				tpacket("received", &from, tp, n);
10813018Ssam 			/* should verify packet came from server */
10913018Ssam 			tp->th_opcode = ntohs(tp->th_opcode);
11013018Ssam 			tp->th_block = ntohs(tp->th_block);
11113018Ssam 			if (tp->th_opcode == ERROR) {
11213018Ssam 				printf("Error code %d: %s\n", tp->th_code,
11313018Ssam 					tp->th_msg);
11416384Ssam 				aborted = 1;
11516384Ssam 				goto done;
11613018Ssam 			}
11713018Ssam 		} while (tp->th_opcode != ACK && block != tp->th_block);
1187770Ssam 		if (block > 0)
1197770Ssam 			amount += size;
1207770Ssam 		block++;
1217770Ssam 	} while (size == SEGSIZE || block == 1);
12216384Ssam 	if (!aborted && amount > 0) {
1237770Ssam 		delta = time(0) - start;
1247770Ssam 		printf("Sent %d bytes in %d seconds.\n", amount, delta);
1257770Ssam 	}
12616384Ssam done:
12716384Ssam 	(void) close(fd);
12816384Ssam 	return (aborted);
1297770Ssam }
1307770Ssam 
1317770Ssam /*
1327770Ssam  * Receive a file.
1337770Ssam  */
1347770Ssam recvfile(fd, name)
1357770Ssam 	int fd;
1367770Ssam 	char *name;
1377770Ssam {
1387770Ssam 	register struct tftphdr *tp = (struct tftphdr *)buf;
1397770Ssam 	register int block = 1, n, size, amount = 0;
14016384Ssam 	struct sockaddr_in from, to;
1417770Ssam 	time_t start = time(0), delta;
14216384Ssam 	int fromlen, firsttrip = 1, aborted = 0;
1437770Ssam 
14416384Ssam 	to = sin;
14513018Ssam 	signal(SIGALRM, timer);
1467770Ssam 	do {
14713018Ssam 		if (firsttrip) {
14813018Ssam 			size = makerequest(RRQ, name);
14913018Ssam 			firsttrip = 0;
15013018Ssam 		} else {
15113018Ssam 			tp->th_opcode = htons((u_short)ACK);
15213018Ssam 			tp->th_block = htons((u_short)(block));
15313018Ssam 			size = 4;
15413018Ssam 			block++;
15513018Ssam 		}
1567770Ssam 		timeout = 0;
15713018Ssam 		(void) setjmp(timeoutbuf);
1587770Ssam 		if (trace)
15916384Ssam 			tpacket("sent", &to, tp, size);
16016384Ssam 		if (sendto(f, buf, size, 0, (caddr_t)&to,
16116384Ssam 		    sizeof (to)) != size) {
16213018Ssam 			alarm(0);
16313018Ssam 			perror("tftp: sendto");
16416384Ssam 			aborted = 1;
16516384Ssam 			goto done;
1667770Ssam 		}
16713018Ssam 		do {
16816384Ssam again:
16913018Ssam 			alarm(rexmtval);
17016384Ssam 			do {
17116384Ssam 				fromlen = sizeof (from);
17213018Ssam 				n = recvfrom(f, buf, sizeof (buf), 0,
17313018Ssam 				    (caddr_t)&from, &fromlen);
17416384Ssam 			} while (n <= 0);
1757770Ssam 			alarm(0);
17613018Ssam 			if (n < 0) {
17713018Ssam 				perror("tftp: recvfrom");
17816384Ssam 				aborted = 1;
17916384Ssam 				goto done;
18013018Ssam 			}
18116384Ssam 			if (to.sin_addr.s_addr != from.sin_addr.s_addr) {
18216384Ssam 				tpacket("discarded (wrong host)", &from, tp, n);
18316384Ssam 				goto again;
18416384Ssam 			}
18516384Ssam 			if (to.sin_port = sin.sin_port)
18616384Ssam 				to.sin_port = from.sin_port;
18716384Ssam 			if (to.sin_port != from.sin_port) {
18816384Ssam 				tpacket("discarded (wrong port)", &from, tp, n);
18916384Ssam 				goto again;
19016384Ssam 			}
19113018Ssam 			if (trace)
19216384Ssam 				tpacket("received", &from, tp, n);
19313018Ssam 			tp->th_opcode = ntohs(tp->th_opcode);
19413018Ssam 			tp->th_block = ntohs(tp->th_block);
19513018Ssam 			if (tp->th_opcode == ERROR) {
19613018Ssam 				printf("Error code %d: %s\n", tp->th_code,
19713018Ssam 					tp->th_msg);
19816384Ssam 				aborted = 1;
19916384Ssam 				goto done;
20013018Ssam 			}
20116384Ssam 		} while (tp->th_opcode != DATA && tp->th_block != block);
2027770Ssam 		size = write(fd, tp->th_data, n - 4);
2037770Ssam 		if (size < 0) {
20416384Ssam 			perror("tftp: write");
20516384Ssam 			nak(&to, errno + 100);
20616384Ssam 			aborted = 1;
20716384Ssam 			goto done;
2087770Ssam 		}
2097770Ssam 		amount += size;
2107770Ssam 	} while (size == SEGSIZE);
21116384Ssam done:
2127770Ssam 	tp->th_opcode = htons((u_short)ACK);
2137770Ssam 	tp->th_block = htons((u_short)block);
21416384Ssam 	(void) sendto(f, buf, 4, 0, &to, sizeof (to));
2157770Ssam 	(void) close(fd);
21616384Ssam 	if (!aborted && amount > 0) {
2177770Ssam 		delta = time(0) - start;
2187770Ssam 		printf("Received %d bytes in %d seconds.\n", amount, delta);
2197770Ssam 	}
22016384Ssam 	return (aborted);
2217770Ssam }
2227770Ssam 
2237770Ssam makerequest(request, name)
2247770Ssam 	int request;
2257770Ssam 	char *name;
2267770Ssam {
2277770Ssam 	register struct tftphdr *tp;
2287770Ssam 	int size;
2297770Ssam 	register char *cp;
2307770Ssam 
2317770Ssam 	tp = (struct tftphdr *)buf;
2327770Ssam 	tp->th_opcode = htons((u_short)request);
2337770Ssam 	strcpy(tp->th_stuff, name);
2347770Ssam 	size = strlen(name);
2357770Ssam 	cp = tp->th_stuff + strlen(name);
2367770Ssam 	*cp++ = '\0';
2377770Ssam 	strcpy(cp, mode);
2387770Ssam 	cp += sizeof ("netascii") - 1;
2397770Ssam 	*cp++ = '\0';
2407770Ssam 	return (cp - buf);
2417770Ssam }
2427770Ssam 
2437770Ssam struct errmsg {
2447770Ssam 	int	e_code;
2457770Ssam 	char	*e_msg;
2467770Ssam } errmsgs[] = {
2477770Ssam 	{ EUNDEF,	"Undefined error code" },
2487770Ssam 	{ ENOTFOUND,	"File not found" },
2497770Ssam 	{ EACCESS,	"Access violation" },
2507770Ssam 	{ ENOSPACE,	"Disk full or allocation exceeded" },
2517770Ssam 	{ EBADOP,	"Illegal TFTP operation" },
2527770Ssam 	{ EBADID,	"Unknown transfer ID" },
2537770Ssam 	{ EEXISTS,	"File already exists" },
2547770Ssam 	{ ENOUSER,	"No such user" },
2557770Ssam 	{ -1,		0 }
2567770Ssam };
2577770Ssam 
2587770Ssam /*
2597770Ssam  * Send a nak packet (error message).
2607770Ssam  * Error code passed in is one of the
2617770Ssam  * standard TFTP codes, or a UNIX errno
2627770Ssam  * offset by 100.
2637770Ssam  */
26416384Ssam nak(to, error)
26516384Ssam 	struct sockaddr_in *to;
2667770Ssam 	int error;
2677770Ssam {
2687770Ssam 	register struct tftphdr *tp;
2697770Ssam 	int length;
2707770Ssam 	register struct errmsg *pe;
2717770Ssam 	extern char *sys_errlist[];
2727770Ssam 
2737770Ssam 	tp = (struct tftphdr *)buf;
2747770Ssam 	tp->th_opcode = htons((u_short)ERROR);
2757770Ssam 	tp->th_code = htons((u_short)error);
2767770Ssam 	for (pe = errmsgs; pe->e_code >= 0; pe++)
2777770Ssam 		if (pe->e_code == error)
2787770Ssam 			break;
2797770Ssam 	if (pe->e_code < 0)
2807770Ssam 		pe->e_msg = sys_errlist[error - 100];
2817770Ssam 	strcpy(tp->th_msg, pe->e_msg);
2827770Ssam 	length = strlen(pe->e_msg) + 4;
2837770Ssam 	if (trace)
28416384Ssam 		tpacket("sent", to, tp, length);
28516384Ssam 	if (sendto(f, buf, length, 0, to, sizeof (*to)) != length)
28616384Ssam 		perror("tftp: nak");
2877770Ssam }
2887770Ssam 
28916384Ssam tpacket(s, sin, tp, n)
29016384Ssam 	struct sockaddr_in *sin;
2917770Ssam 	struct tftphdr *tp;
2927770Ssam 	int n;
2937770Ssam {
2947770Ssam 	static char *opcodes[] =
2957770Ssam 	   { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR" };
2967770Ssam 	register char *cp, *file;
2977770Ssam 	u_short op = ntohs(tp->th_opcode);
2987770Ssam 	char *index();
2997770Ssam 
30016384Ssam 	printf("%s ", s);
30116384Ssam 	if (sin) {
30216384Ssam 		struct hostent *hp = gethostbyaddr(&sin->sin_addr,
30316384Ssam 		     sizeof (sin->sin_addr), AF_INET);
30416384Ssam 
30516384Ssam 		printf("%s.%d ",
30616384Ssam 		    hp == 0 ? inet_ntoa(sin->sin_addr) : hp->h_name,
30716384Ssam 		    ntohs(sin->sin_port));
30816384Ssam 	}
3097770Ssam 	if (op < RRQ || op > ERROR)
31016384Ssam 		printf("opcode=%x ", op);
3117770Ssam 	else
31216384Ssam 		printf("%s ", opcodes[op]);
3137770Ssam 	switch (op) {
3147770Ssam 
3157770Ssam 	case RRQ:
3167770Ssam 	case WRQ:
3177770Ssam 		n -= 2;
3187770Ssam 		file = cp = tp->th_stuff;
3197770Ssam 		cp = index(cp, '\0');
3207770Ssam 		printf("<file=%s, mode=%s>\n", file, cp + 1);
3217770Ssam 		break;
3227770Ssam 
3237770Ssam 	case DATA:
3247770Ssam 		printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), n - 4);
3257770Ssam 		break;
3267770Ssam 
3277770Ssam 	case ACK:
3287770Ssam 		printf("<block=%d>\n", ntohs(tp->th_block));
3297770Ssam 		break;
3307770Ssam 
3317770Ssam 	case ERROR:
3327770Ssam 		printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg);
3337770Ssam 		break;
334*17268Ssam 
335*17268Ssam 	default:
336*17268Ssam 		putchar('\n');
337*17268Ssam 		break;
3387770Ssam 	}
3397770Ssam }
340