xref: /csrg-svn/usr.bin/tftp/tftp.c (revision 13018)
1*13018Ssam /*	tftp.c	4.6	83/06/12	*/
27770Ssam 
37770Ssam /*
47770Ssam  * TFTP User Program -- Protocol Machines
57770Ssam  */
67770Ssam #include <sys/types.h>
77770Ssam #include <sys/socket.h>
89220Ssam 
99220Ssam #include <netinet/in.h>
109220Ssam 
1112399Ssam #include <arpa/tftp.h>
1212399Ssam 
137770Ssam #include <signal.h>
147770Ssam #include <stdio.h>
157770Ssam #include <errno.h>
167770Ssam #include <setjmp.h>
179220Ssam 
187770Ssam extern	int errno;
197770Ssam extern	struct sockaddr_in sin;
207770Ssam extern	char mode[];
217770Ssam int	f;
227770Ssam int	trace;
237770Ssam int	verbose;
247770Ssam int	connected;
257770Ssam char	buf[BUFSIZ];
26*13018Ssam int	rexmtval;
27*13018Ssam int	maxtimeout;
287770Ssam int	timeout;
297770Ssam jmp_buf	toplevel;
30*13018Ssam jmp_buf	timeoutbuf;
317770Ssam 
327770Ssam timer()
337770Ssam {
34*13018Ssam 
35*13018Ssam 	timeout += rexmtval;
36*13018Ssam 	if (timeout >= maxtimeout) {
377770Ssam 		printf("Transfer timed out.\n");
387770Ssam 		longjmp(toplevel, -1);
397770Ssam 	}
40*13018Ssam 	longjmp(timeoutbuf, 1);
417770Ssam }
427770Ssam 
437770Ssam /*
447770Ssam  * Send the requested file.
457770Ssam  */
467770Ssam sendfile(fd, name)
477770Ssam 	int fd;
487770Ssam 	char *name;
497770Ssam {
507770Ssam 	register struct tftphdr *tp = (struct tftphdr *)buf;
517770Ssam 	register int block = 0, size, n, amount = 0;
527770Ssam 	struct sockaddr_in from;
537770Ssam 	time_t start = time(0), delta;
549220Ssam 	int fromlen;
557770Ssam 
56*13018Ssam 	signal(SIGALRM, timer);
577770Ssam 	do {
58*13018Ssam 		if (block == 0)
59*13018Ssam 			size = makerequest(WRQ, name) - 4;
60*13018Ssam 		else {
617770Ssam 			size = read(fd, tp->th_data, SEGSIZE);
627770Ssam 			if (size < 0) {
637770Ssam 				nak(errno + 100);
647770Ssam 				break;
657770Ssam 			}
667770Ssam 			tp->th_opcode = htons((u_short)DATA);
677770Ssam 			tp->th_block = htons((u_short)block);
687770Ssam 		}
697770Ssam 		timeout = 0;
70*13018Ssam 		(void) setjmp(timeoutbuf);
717770Ssam 		if (trace)
727770Ssam 			tpacket("sent", tp, size + 4);
739245Ssam 		n = sendto(f, buf, size + 4, 0, (caddr_t)&sin, sizeof (sin));
749220Ssam 		if (n != size + 4) {
75*13018Ssam 			perror("tftp: sendto");
76*13018Ssam 			goto abort;
777770Ssam 		}
78*13018Ssam 		do {
79*13018Ssam 			alarm(rexmtval);
80*13018Ssam 			do {
81*13018Ssam 				fromlen = sizeof (from);
82*13018Ssam 				n = recvfrom(f, buf, sizeof (buf), 0,
83*13018Ssam 				    (caddr_t)&from, &fromlen);
84*13018Ssam 			} while (n <= 0);
857770Ssam 			alarm(0);
86*13018Ssam 			if (n < 0) {
87*13018Ssam 				perror("tftp: recvfrom");
88*13018Ssam 				goto abort;
89*13018Ssam 			}
90*13018Ssam 			if (trace)
91*13018Ssam 				tpacket("received", tp, n);
92*13018Ssam 			/* should verify packet came from server */
93*13018Ssam 			tp->th_opcode = ntohs(tp->th_opcode);
94*13018Ssam 			tp->th_block = ntohs(tp->th_block);
95*13018Ssam 			if (tp->th_opcode == ERROR) {
96*13018Ssam 				printf("Error code %d: %s\n", tp->th_code,
97*13018Ssam 					tp->th_msg);
98*13018Ssam 				goto abort;
99*13018Ssam 			}
100*13018Ssam 		} while (tp->th_opcode != ACK && block != tp->th_block);
1017770Ssam 		if (block > 0)
1027770Ssam 			amount += size;
1037770Ssam 		block++;
1047770Ssam 	} while (size == SEGSIZE || block == 1);
105*13018Ssam abort:
1067770Ssam 	(void) close(fd);
1077770Ssam 	if (amount > 0) {
1087770Ssam 		delta = time(0) - start;
1097770Ssam 		printf("Sent %d bytes in %d seconds.\n", amount, delta);
1107770Ssam 	}
1117770Ssam }
1127770Ssam 
1137770Ssam /*
1147770Ssam  * Receive a file.
1157770Ssam  */
1167770Ssam recvfile(fd, name)
1177770Ssam 	int fd;
1187770Ssam 	char *name;
1197770Ssam {
1207770Ssam 	register struct tftphdr *tp = (struct tftphdr *)buf;
1217770Ssam 	register int block = 1, n, size, amount = 0;
1227770Ssam 	struct sockaddr_in from;
1237770Ssam 	time_t start = time(0), delta;
124*13018Ssam 	int fromlen, firsttrip = 1;
1257770Ssam 
126*13018Ssam 	signal(SIGALRM, timer);
1277770Ssam 	do {
128*13018Ssam 		if (firsttrip) {
129*13018Ssam 			size = makerequest(RRQ, name);
130*13018Ssam 			firsttrip = 0;
131*13018Ssam 		} else {
132*13018Ssam 			tp->th_opcode = htons((u_short)ACK);
133*13018Ssam 			tp->th_block = htons((u_short)(block));
134*13018Ssam 			size = 4;
135*13018Ssam 			block++;
136*13018Ssam 		}
1377770Ssam 		timeout = 0;
138*13018Ssam 		(void) setjmp(timeoutbuf);
1397770Ssam 		if (trace)
1407770Ssam 			tpacket("sent", tp, size);
141*13018Ssam 		if (sendto(f, buf, size, 0, (caddr_t)&sin,
142*13018Ssam 		    sizeof (sin)) != size) {
143*13018Ssam 			alarm(0);
144*13018Ssam 			perror("tftp: sendto");
145*13018Ssam 			goto abort;
1467770Ssam 		}
147*13018Ssam 		do {
148*13018Ssam 			alarm(rexmtval);
149*13018Ssam 			do
150*13018Ssam 				n = recvfrom(f, buf, sizeof (buf), 0,
151*13018Ssam 				    (caddr_t)&from, &fromlen);
152*13018Ssam 			while (n <= 0);
1537770Ssam 			alarm(0);
154*13018Ssam 			if (n < 0) {
155*13018Ssam 				perror("tftp: recvfrom");
156*13018Ssam 				goto abort;
157*13018Ssam 			}
158*13018Ssam 			if (trace)
159*13018Ssam 				tpacket("received", tp, n);
160*13018Ssam 			/* should verify client address */
161*13018Ssam 			tp->th_opcode = ntohs(tp->th_opcode);
162*13018Ssam 			tp->th_block = ntohs(tp->th_block);
163*13018Ssam 			if (tp->th_opcode == ERROR) {
164*13018Ssam 				printf("Error code %d: %s\n", tp->th_code,
165*13018Ssam 					tp->th_msg);
166*13018Ssam 				goto abort;
167*13018Ssam 			}
168*13018Ssam 		} while (tp->th_opcode != DATA && block != tp->th_block);
1697770Ssam 		size = write(fd, tp->th_data, n - 4);
1707770Ssam 		if (size < 0) {
1717770Ssam 			nak(errno + 100);
1727770Ssam 			break;
1737770Ssam 		}
1747770Ssam 		amount += size;
1757770Ssam 	} while (size == SEGSIZE);
176*13018Ssam abort:
1777770Ssam 	tp->th_opcode = htons((u_short)ACK);
1787770Ssam 	tp->th_block = htons((u_short)block);
1799245Ssam 	(void) sendto(f, buf, 4, 0, &sin, sizeof (sin));
1807770Ssam 	(void) close(fd);
1817770Ssam 	if (amount > 0) {
1827770Ssam 		delta = time(0) - start;
1837770Ssam 		printf("Received %d bytes in %d seconds.\n", amount, delta);
1847770Ssam 	}
1857770Ssam }
1867770Ssam 
1877770Ssam makerequest(request, name)
1887770Ssam 	int request;
1897770Ssam 	char *name;
1907770Ssam {
1917770Ssam 	register struct tftphdr *tp;
1927770Ssam 	int size;
1937770Ssam 	register char *cp;
1947770Ssam 
1957770Ssam 	tp = (struct tftphdr *)buf;
1967770Ssam 	tp->th_opcode = htons((u_short)request);
1977770Ssam 	strcpy(tp->th_stuff, name);
1987770Ssam 	size = strlen(name);
1997770Ssam 	cp = tp->th_stuff + strlen(name);
2007770Ssam 	*cp++ = '\0';
2017770Ssam 	strcpy(cp, mode);
2027770Ssam 	cp += sizeof ("netascii") - 1;
2037770Ssam 	*cp++ = '\0';
2047770Ssam 	return (cp - buf);
2057770Ssam }
2067770Ssam 
2077770Ssam struct errmsg {
2087770Ssam 	int	e_code;
2097770Ssam 	char	*e_msg;
2107770Ssam } errmsgs[] = {
2117770Ssam 	{ EUNDEF,	"Undefined error code" },
2127770Ssam 	{ ENOTFOUND,	"File not found" },
2137770Ssam 	{ EACCESS,	"Access violation" },
2147770Ssam 	{ ENOSPACE,	"Disk full or allocation exceeded" },
2157770Ssam 	{ EBADOP,	"Illegal TFTP operation" },
2167770Ssam 	{ EBADID,	"Unknown transfer ID" },
2177770Ssam 	{ EEXISTS,	"File already exists" },
2187770Ssam 	{ ENOUSER,	"No such user" },
2197770Ssam 	{ -1,		0 }
2207770Ssam };
2217770Ssam 
2227770Ssam /*
2237770Ssam  * Send a nak packet (error message).
2247770Ssam  * Error code passed in is one of the
2257770Ssam  * standard TFTP codes, or a UNIX errno
2267770Ssam  * offset by 100.
2277770Ssam  */
2287770Ssam nak(error)
2297770Ssam 	int error;
2307770Ssam {
2317770Ssam 	register struct tftphdr *tp;
2327770Ssam 	int length;
2337770Ssam 	register struct errmsg *pe;
2347770Ssam 	extern char *sys_errlist[];
2357770Ssam 
2367770Ssam 	tp = (struct tftphdr *)buf;
2377770Ssam 	tp->th_opcode = htons((u_short)ERROR);
2387770Ssam 	tp->th_code = htons((u_short)error);
2397770Ssam 	for (pe = errmsgs; pe->e_code >= 0; pe++)
2407770Ssam 		if (pe->e_code == error)
2417770Ssam 			break;
2427770Ssam 	if (pe->e_code < 0)
2437770Ssam 		pe->e_msg = sys_errlist[error - 100];
2447770Ssam 	strcpy(tp->th_msg, pe->e_msg);
2457770Ssam 	length = strlen(pe->e_msg) + 4;
2467770Ssam 	if (trace)
2477770Ssam 		tpacket("sent", tp, length);
2487770Ssam 	if (send(f, &sin, buf, length) != length)
2497770Ssam 		perror("nak");
2507770Ssam }
2517770Ssam 
2527770Ssam tpacket(s, tp, n)
2537770Ssam 	struct tftphdr *tp;
2547770Ssam 	int n;
2557770Ssam {
2567770Ssam 	static char *opcodes[] =
2577770Ssam 	   { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR" };
2587770Ssam 	register char *cp, *file;
2597770Ssam 	u_short op = ntohs(tp->th_opcode);
2607770Ssam 	char *index();
2617770Ssam 
2627770Ssam 	if (op < RRQ || op > ERROR)
2637770Ssam 		printf("%s opcode=%x ", s, op);
2647770Ssam 	else
2657770Ssam 		printf("%s %s ", s, opcodes[op]);
2667770Ssam 	switch (op) {
2677770Ssam 
2687770Ssam 	case RRQ:
2697770Ssam 	case WRQ:
2707770Ssam 		n -= 2;
2717770Ssam 		file = cp = tp->th_stuff;
2727770Ssam 		cp = index(cp, '\0');
2737770Ssam 		printf("<file=%s, mode=%s>\n", file, cp + 1);
2747770Ssam 		break;
2757770Ssam 
2767770Ssam 	case DATA:
2777770Ssam 		printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), n - 4);
2787770Ssam 		break;
2797770Ssam 
2807770Ssam 	case ACK:
2817770Ssam 		printf("<block=%d>\n", ntohs(tp->th_block));
2827770Ssam 		break;
2837770Ssam 
2847770Ssam 	case ERROR:
2857770Ssam 		printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg);
2867770Ssam 		break;
2877770Ssam 	}
2887770Ssam }
289