xref: /csrg-svn/usr.bin/tftp/tftp.c (revision 14554)
1*14554Ssam #ifndef lint
2*14554Ssam static char sccsid[] = "@(#)tftp.c	4.7 (Berkeley) 08/11/83";
3*14554Ssam #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>
129220Ssam 
1312399Ssam #include <arpa/tftp.h>
1412399Ssam 
157770Ssam #include <signal.h>
167770Ssam #include <stdio.h>
177770Ssam #include <errno.h>
187770Ssam #include <setjmp.h>
199220Ssam 
207770Ssam extern	int errno;
217770Ssam extern	struct sockaddr_in sin;
227770Ssam extern	char mode[];
237770Ssam int	f;
247770Ssam int	trace;
257770Ssam int	verbose;
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;
547770Ssam 	struct sockaddr_in from;
557770Ssam 	time_t start = time(0), delta;
569220Ssam 	int fromlen;
577770Ssam 
5813018Ssam 	signal(SIGALRM, timer);
597770Ssam 	do {
6013018Ssam 		if (block == 0)
6113018Ssam 			size = makerequest(WRQ, name) - 4;
6213018Ssam 		else {
637770Ssam 			size = read(fd, tp->th_data, SEGSIZE);
647770Ssam 			if (size < 0) {
657770Ssam 				nak(errno + 100);
667770Ssam 				break;
677770Ssam 			}
687770Ssam 			tp->th_opcode = htons((u_short)DATA);
697770Ssam 			tp->th_block = htons((u_short)block);
707770Ssam 		}
717770Ssam 		timeout = 0;
7213018Ssam 		(void) setjmp(timeoutbuf);
737770Ssam 		if (trace)
747770Ssam 			tpacket("sent", tp, size + 4);
759245Ssam 		n = sendto(f, buf, size + 4, 0, (caddr_t)&sin, sizeof (sin));
769220Ssam 		if (n != size + 4) {
7713018Ssam 			perror("tftp: sendto");
7813018Ssam 			goto abort;
797770Ssam 		}
8013018Ssam 		do {
8113018Ssam 			alarm(rexmtval);
8213018Ssam 			do {
8313018Ssam 				fromlen = sizeof (from);
8413018Ssam 				n = recvfrom(f, buf, sizeof (buf), 0,
8513018Ssam 				    (caddr_t)&from, &fromlen);
8613018Ssam 			} while (n <= 0);
877770Ssam 			alarm(0);
8813018Ssam 			if (n < 0) {
8913018Ssam 				perror("tftp: recvfrom");
9013018Ssam 				goto abort;
9113018Ssam 			}
9213018Ssam 			if (trace)
9313018Ssam 				tpacket("received", tp, n);
9413018Ssam 			/* should verify packet came from server */
9513018Ssam 			tp->th_opcode = ntohs(tp->th_opcode);
9613018Ssam 			tp->th_block = ntohs(tp->th_block);
9713018Ssam 			if (tp->th_opcode == ERROR) {
9813018Ssam 				printf("Error code %d: %s\n", tp->th_code,
9913018Ssam 					tp->th_msg);
10013018Ssam 				goto abort;
10113018Ssam 			}
10213018Ssam 		} while (tp->th_opcode != ACK && block != tp->th_block);
1037770Ssam 		if (block > 0)
1047770Ssam 			amount += size;
1057770Ssam 		block++;
1067770Ssam 	} while (size == SEGSIZE || block == 1);
10713018Ssam abort:
1087770Ssam 	(void) close(fd);
1097770Ssam 	if (amount > 0) {
1107770Ssam 		delta = time(0) - start;
1117770Ssam 		printf("Sent %d bytes in %d seconds.\n", amount, delta);
1127770Ssam 	}
1137770Ssam }
1147770Ssam 
1157770Ssam /*
1167770Ssam  * Receive a file.
1177770Ssam  */
1187770Ssam recvfile(fd, name)
1197770Ssam 	int fd;
1207770Ssam 	char *name;
1217770Ssam {
1227770Ssam 	register struct tftphdr *tp = (struct tftphdr *)buf;
1237770Ssam 	register int block = 1, n, size, amount = 0;
1247770Ssam 	struct sockaddr_in from;
1257770Ssam 	time_t start = time(0), delta;
12613018Ssam 	int fromlen, firsttrip = 1;
1277770Ssam 
12813018Ssam 	signal(SIGALRM, timer);
1297770Ssam 	do {
13013018Ssam 		if (firsttrip) {
13113018Ssam 			size = makerequest(RRQ, name);
13213018Ssam 			firsttrip = 0;
13313018Ssam 		} else {
13413018Ssam 			tp->th_opcode = htons((u_short)ACK);
13513018Ssam 			tp->th_block = htons((u_short)(block));
13613018Ssam 			size = 4;
13713018Ssam 			block++;
13813018Ssam 		}
1397770Ssam 		timeout = 0;
14013018Ssam 		(void) setjmp(timeoutbuf);
1417770Ssam 		if (trace)
1427770Ssam 			tpacket("sent", tp, size);
14313018Ssam 		if (sendto(f, buf, size, 0, (caddr_t)&sin,
14413018Ssam 		    sizeof (sin)) != size) {
14513018Ssam 			alarm(0);
14613018Ssam 			perror("tftp: sendto");
14713018Ssam 			goto abort;
1487770Ssam 		}
14913018Ssam 		do {
15013018Ssam 			alarm(rexmtval);
15113018Ssam 			do
15213018Ssam 				n = recvfrom(f, buf, sizeof (buf), 0,
15313018Ssam 				    (caddr_t)&from, &fromlen);
15413018Ssam 			while (n <= 0);
1557770Ssam 			alarm(0);
15613018Ssam 			if (n < 0) {
15713018Ssam 				perror("tftp: recvfrom");
15813018Ssam 				goto abort;
15913018Ssam 			}
16013018Ssam 			if (trace)
16113018Ssam 				tpacket("received", tp, n);
16213018Ssam 			/* should verify client address */
16313018Ssam 			tp->th_opcode = ntohs(tp->th_opcode);
16413018Ssam 			tp->th_block = ntohs(tp->th_block);
16513018Ssam 			if (tp->th_opcode == ERROR) {
16613018Ssam 				printf("Error code %d: %s\n", tp->th_code,
16713018Ssam 					tp->th_msg);
16813018Ssam 				goto abort;
16913018Ssam 			}
17013018Ssam 		} while (tp->th_opcode != DATA && block != tp->th_block);
1717770Ssam 		size = write(fd, tp->th_data, n - 4);
1727770Ssam 		if (size < 0) {
1737770Ssam 			nak(errno + 100);
1747770Ssam 			break;
1757770Ssam 		}
1767770Ssam 		amount += size;
1777770Ssam 	} while (size == SEGSIZE);
17813018Ssam abort:
1797770Ssam 	tp->th_opcode = htons((u_short)ACK);
1807770Ssam 	tp->th_block = htons((u_short)block);
1819245Ssam 	(void) sendto(f, buf, 4, 0, &sin, sizeof (sin));
1827770Ssam 	(void) close(fd);
1837770Ssam 	if (amount > 0) {
1847770Ssam 		delta = time(0) - start;
1857770Ssam 		printf("Received %d bytes in %d seconds.\n", amount, delta);
1867770Ssam 	}
1877770Ssam }
1887770Ssam 
1897770Ssam makerequest(request, name)
1907770Ssam 	int request;
1917770Ssam 	char *name;
1927770Ssam {
1937770Ssam 	register struct tftphdr *tp;
1947770Ssam 	int size;
1957770Ssam 	register char *cp;
1967770Ssam 
1977770Ssam 	tp = (struct tftphdr *)buf;
1987770Ssam 	tp->th_opcode = htons((u_short)request);
1997770Ssam 	strcpy(tp->th_stuff, name);
2007770Ssam 	size = strlen(name);
2017770Ssam 	cp = tp->th_stuff + strlen(name);
2027770Ssam 	*cp++ = '\0';
2037770Ssam 	strcpy(cp, mode);
2047770Ssam 	cp += sizeof ("netascii") - 1;
2057770Ssam 	*cp++ = '\0';
2067770Ssam 	return (cp - buf);
2077770Ssam }
2087770Ssam 
2097770Ssam struct errmsg {
2107770Ssam 	int	e_code;
2117770Ssam 	char	*e_msg;
2127770Ssam } errmsgs[] = {
2137770Ssam 	{ EUNDEF,	"Undefined error code" },
2147770Ssam 	{ ENOTFOUND,	"File not found" },
2157770Ssam 	{ EACCESS,	"Access violation" },
2167770Ssam 	{ ENOSPACE,	"Disk full or allocation exceeded" },
2177770Ssam 	{ EBADOP,	"Illegal TFTP operation" },
2187770Ssam 	{ EBADID,	"Unknown transfer ID" },
2197770Ssam 	{ EEXISTS,	"File already exists" },
2207770Ssam 	{ ENOUSER,	"No such user" },
2217770Ssam 	{ -1,		0 }
2227770Ssam };
2237770Ssam 
2247770Ssam /*
2257770Ssam  * Send a nak packet (error message).
2267770Ssam  * Error code passed in is one of the
2277770Ssam  * standard TFTP codes, or a UNIX errno
2287770Ssam  * offset by 100.
2297770Ssam  */
2307770Ssam nak(error)
2317770Ssam 	int error;
2327770Ssam {
2337770Ssam 	register struct tftphdr *tp;
2347770Ssam 	int length;
2357770Ssam 	register struct errmsg *pe;
2367770Ssam 	extern char *sys_errlist[];
2377770Ssam 
2387770Ssam 	tp = (struct tftphdr *)buf;
2397770Ssam 	tp->th_opcode = htons((u_short)ERROR);
2407770Ssam 	tp->th_code = htons((u_short)error);
2417770Ssam 	for (pe = errmsgs; pe->e_code >= 0; pe++)
2427770Ssam 		if (pe->e_code == error)
2437770Ssam 			break;
2447770Ssam 	if (pe->e_code < 0)
2457770Ssam 		pe->e_msg = sys_errlist[error - 100];
2467770Ssam 	strcpy(tp->th_msg, pe->e_msg);
2477770Ssam 	length = strlen(pe->e_msg) + 4;
2487770Ssam 	if (trace)
2497770Ssam 		tpacket("sent", tp, length);
2507770Ssam 	if (send(f, &sin, buf, length) != length)
2517770Ssam 		perror("nak");
2527770Ssam }
2537770Ssam 
2547770Ssam tpacket(s, tp, n)
2557770Ssam 	struct tftphdr *tp;
2567770Ssam 	int n;
2577770Ssam {
2587770Ssam 	static char *opcodes[] =
2597770Ssam 	   { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR" };
2607770Ssam 	register char *cp, *file;
2617770Ssam 	u_short op = ntohs(tp->th_opcode);
2627770Ssam 	char *index();
2637770Ssam 
2647770Ssam 	if (op < RRQ || op > ERROR)
2657770Ssam 		printf("%s opcode=%x ", s, op);
2667770Ssam 	else
2677770Ssam 		printf("%s %s ", s, opcodes[op]);
2687770Ssam 	switch (op) {
2697770Ssam 
2707770Ssam 	case RRQ:
2717770Ssam 	case WRQ:
2727770Ssam 		n -= 2;
2737770Ssam 		file = cp = tp->th_stuff;
2747770Ssam 		cp = index(cp, '\0');
2757770Ssam 		printf("<file=%s, mode=%s>\n", file, cp + 1);
2767770Ssam 		break;
2777770Ssam 
2787770Ssam 	case DATA:
2797770Ssam 		printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), n - 4);
2807770Ssam 		break;
2817770Ssam 
2827770Ssam 	case ACK:
2837770Ssam 		printf("<block=%d>\n", ntohs(tp->th_block));
2847770Ssam 		break;
2857770Ssam 
2867770Ssam 	case ERROR:
2877770Ssam 		printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg);
2887770Ssam 		break;
2897770Ssam 	}
2907770Ssam }
291