xref: /csrg-svn/usr.bin/tftp/tftp.c (revision 9245)
1*9245Ssam /*	tftp.c	4.4	82/11/15	*/
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 
117770Ssam #include <signal.h>
127770Ssam #include <stdio.h>
137770Ssam #include <errno.h>
147770Ssam #include <setjmp.h>
159220Ssam 
167770Ssam #include "tftp.h"
177770Ssam 
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];
267770Ssam int	timeout;
277770Ssam jmp_buf	toplevel;
287770Ssam 
297770Ssam timer()
307770Ssam {
317770Ssam 	timeout += TIMEOUT;
327770Ssam 	if (timeout >= MAXTIMEOUT) {
337770Ssam 		printf("Transfer timed out.\n");
347770Ssam 		longjmp(toplevel, -1);
357770Ssam 	}
367770Ssam 	alarm(TIMEOUT);
377770Ssam }
387770Ssam 
397770Ssam /*
407770Ssam  * Send the requested file.
417770Ssam  */
427770Ssam sendfile(fd, name)
437770Ssam 	int fd;
447770Ssam 	char *name;
457770Ssam {
467770Ssam 	register struct tftphdr *tp = (struct tftphdr *)buf;
477770Ssam 	register int block = 0, size, n, amount = 0;
487770Ssam 	struct sockaddr_in from;
497770Ssam 	time_t start = time(0), delta;
509220Ssam 	int fromlen;
517770Ssam 
527770Ssam 	size = makerequest(WRQ, name) - 4;
537778Ssam 	timeout = 0;
547770Ssam 	sigset(SIGALRM, timer);
557770Ssam 	do {
567770Ssam 		if (block != 0) {
577770Ssam 			size = read(fd, tp->th_data, SEGSIZE);
587770Ssam 			if (size < 0) {
597770Ssam 				nak(errno + 100);
607770Ssam 				break;
617770Ssam 			}
627770Ssam 			tp->th_opcode = htons((u_short)DATA);
637770Ssam 			tp->th_block = htons((u_short)block);
647770Ssam 		}
657770Ssam 		timeout = 0;
667770Ssam 		alarm(TIMEOUT);
677770Ssam rexmt:
687770Ssam 		if (trace)
697770Ssam 			tpacket("sent", tp, size + 4);
70*9245Ssam 		n = sendto(f, buf, size + 4, 0, (caddr_t)&sin, sizeof (sin));
719220Ssam 		if (n != size + 4) {
727770Ssam 			perror("send");
737770Ssam 			break;
747770Ssam 		}
757770Ssam again:
769220Ssam 		fromlen = sizeof (from);
77*9245Ssam 		n = recvfrom(f, buf, sizeof (buf), 0, (caddr_t)&from, &fromlen);
787770Ssam 		if (n <= 0) {
797770Ssam 			if (n == 0)
807770Ssam 				goto again;
817770Ssam 			if (errno == EINTR)
827770Ssam 				goto rexmt;
837770Ssam 			alarm(0);
847770Ssam 			perror("receive");
857770Ssam 			break;
867770Ssam 		}
877770Ssam 		alarm(0);
887770Ssam 		if (trace)
897770Ssam 			tpacket("received", tp, n);
909220Ssam 		/* should verify packet came from server */
917770Ssam #if vax || pdp11
927770Ssam 		tp->th_opcode = ntohs(tp->th_opcode);
937770Ssam 		tp->th_block = ntohs(tp->th_block);
947770Ssam #endif
957770Ssam 		if (tp->th_opcode == ERROR) {
967770Ssam 			printf("Error code %d: %s\n", tp->th_code,
977770Ssam 				tp->th_msg);
987770Ssam 			break;
997770Ssam 		}
1007770Ssam 		if (tp->th_opcode != ACK || block != tp->th_block)
1017770Ssam 			goto again;
1027770Ssam 		if (block > 0)
1037770Ssam 			amount += size;
1047770Ssam 		block++;
1057770Ssam 	} while (size == SEGSIZE || block == 1);
1067770Ssam 	alarm(0);
1077770Ssam 	(void) close(fd);
1087770Ssam 	if (amount > 0) {
1097770Ssam 		delta = time(0) - start;
1107770Ssam 		printf("Sent %d bytes in %d seconds.\n", amount, delta);
1117770Ssam 	}
1127770Ssam }
1137770Ssam 
1147770Ssam /*
1157770Ssam  * Receive a file.
1167770Ssam  */
1177770Ssam recvfile(fd, name)
1187770Ssam 	int fd;
1197770Ssam 	char *name;
1207770Ssam {
1217770Ssam 	register struct tftphdr *tp = (struct tftphdr *)buf;
1227770Ssam 	register int block = 1, n, size, amount = 0;
1237770Ssam 	struct sockaddr_in from;
1247770Ssam 	time_t start = time(0), delta;
1259220Ssam 	int fromlen;
1267770Ssam 
1277770Ssam 	size = makerequest(RRQ, name);
1287778Ssam 	timeout = 0;
1297770Ssam 	sigset(SIGALRM, timer);
1307770Ssam 	alarm(TIMEOUT);
1317770Ssam 	goto rexmt;
1327770Ssam 	do {
1337770Ssam 		timeout = 0;
1347770Ssam 		alarm(TIMEOUT);
1357770Ssam 		tp->th_opcode = htons((u_short)ACK);
1367770Ssam 		tp->th_block = htons((u_short)(block));
1377770Ssam 		size = 4;
1387770Ssam 		block++;
1397770Ssam rexmt:
1407770Ssam 		if (trace)
1417770Ssam 			tpacket("sent", tp, size);
142*9245Ssam 		if (sendto(f, buf, size, 0, (caddr_t)&sin, sizeof (sin)) != size) {
1437770Ssam 			perror("send");
1447770Ssam 			break;
1457770Ssam 		}
1467770Ssam again:
147*9245Ssam 		n = recvfrom(f, buf, sizeof (buf), 0, (caddr_t)&from, &fromlen);
1487770Ssam 		if (n <= 0) {
1497770Ssam 			if (n == 0)
1507770Ssam 				goto again;
1517770Ssam 			if (errno == EINTR)
1527770Ssam 				goto rexmt;
1537770Ssam 			alarm(0);
1547770Ssam 			perror("receive");
1557770Ssam 			break;
1567770Ssam 		}
1577770Ssam 		alarm(0);
1587770Ssam 		if (trace)
1597770Ssam 			tpacket("received", tp, n);
1609220Ssam 		/* should verify client address */
1617770Ssam #if vax || pdp11
1627770Ssam 		tp->th_opcode = ntohs(tp->th_opcode);
1637770Ssam 		tp->th_block = ntohs(tp->th_block);
1647770Ssam #endif
1657770Ssam 		if (tp->th_opcode == ERROR) {
1667770Ssam 			printf("Error code %d: %s\n", tp->th_code,
1677770Ssam 				tp->th_msg);
1687770Ssam 			break;
1697770Ssam 		}
1707770Ssam 		if (tp->th_opcode != DATA || block != tp->th_block)
1717770Ssam 			goto again;
1727770Ssam 		size = write(fd, tp->th_data, n - 4);
1737770Ssam 		if (size < 0) {
1747770Ssam 			nak(errno + 100);
1757770Ssam 			break;
1767770Ssam 		}
1777770Ssam 		amount += size;
1787770Ssam 	} while (size == SEGSIZE);
1797770Ssam 	alarm(0);
1807770Ssam 	tp->th_opcode = htons((u_short)ACK);
1817770Ssam 	tp->th_block = htons((u_short)block);
182*9245Ssam 	(void) sendto(f, buf, 4, 0, &sin, sizeof (sin));
1837770Ssam 	(void) close(fd);
1847770Ssam 	if (amount > 0) {
1857770Ssam 		delta = time(0) - start;
1867770Ssam 		printf("Received %d bytes in %d seconds.\n", amount, delta);
1877770Ssam 	}
1887770Ssam }
1897770Ssam 
1907770Ssam makerequest(request, name)
1917770Ssam 	int request;
1927770Ssam 	char *name;
1937770Ssam {
1947770Ssam 	register struct tftphdr *tp;
1957770Ssam 	int size;
1967770Ssam 	register char *cp;
1977770Ssam 
1987770Ssam 	tp = (struct tftphdr *)buf;
1997770Ssam 	tp->th_opcode = htons((u_short)request);
2007770Ssam 	strcpy(tp->th_stuff, name);
2017770Ssam 	size = strlen(name);
2027770Ssam 	cp = tp->th_stuff + strlen(name);
2037770Ssam 	*cp++ = '\0';
2047770Ssam 	strcpy(cp, mode);
2057770Ssam 	cp += sizeof ("netascii") - 1;
2067770Ssam 	*cp++ = '\0';
2077770Ssam 	return (cp - buf);
2087770Ssam }
2097770Ssam 
2107770Ssam struct errmsg {
2117770Ssam 	int	e_code;
2127770Ssam 	char	*e_msg;
2137770Ssam } errmsgs[] = {
2147770Ssam 	{ EUNDEF,	"Undefined error code" },
2157770Ssam 	{ ENOTFOUND,	"File not found" },
2167770Ssam 	{ EACCESS,	"Access violation" },
2177770Ssam 	{ ENOSPACE,	"Disk full or allocation exceeded" },
2187770Ssam 	{ EBADOP,	"Illegal TFTP operation" },
2197770Ssam 	{ EBADID,	"Unknown transfer ID" },
2207770Ssam 	{ EEXISTS,	"File already exists" },
2217770Ssam 	{ ENOUSER,	"No such user" },
2227770Ssam 	{ -1,		0 }
2237770Ssam };
2247770Ssam 
2257770Ssam /*
2267770Ssam  * Send a nak packet (error message).
2277770Ssam  * Error code passed in is one of the
2287770Ssam  * standard TFTP codes, or a UNIX errno
2297770Ssam  * offset by 100.
2307770Ssam  */
2317770Ssam nak(error)
2327770Ssam 	int error;
2337770Ssam {
2347770Ssam 	register struct tftphdr *tp;
2357770Ssam 	int length;
2367770Ssam 	register struct errmsg *pe;
2377770Ssam 	extern char *sys_errlist[];
2387770Ssam 
2397770Ssam 	tp = (struct tftphdr *)buf;
2407770Ssam 	tp->th_opcode = htons((u_short)ERROR);
2417770Ssam 	tp->th_code = htons((u_short)error);
2427770Ssam 	for (pe = errmsgs; pe->e_code >= 0; pe++)
2437770Ssam 		if (pe->e_code == error)
2447770Ssam 			break;
2457770Ssam 	if (pe->e_code < 0)
2467770Ssam 		pe->e_msg = sys_errlist[error - 100];
2477770Ssam 	strcpy(tp->th_msg, pe->e_msg);
2487770Ssam 	length = strlen(pe->e_msg) + 4;
2497770Ssam 	if (trace)
2507770Ssam 		tpacket("sent", tp, length);
2517770Ssam 	if (send(f, &sin, buf, length) != length)
2527770Ssam 		perror("nak");
2537770Ssam }
2547770Ssam 
2557770Ssam tpacket(s, tp, n)
2567770Ssam 	struct tftphdr *tp;
2577770Ssam 	int n;
2587770Ssam {
2597770Ssam 	static char *opcodes[] =
2607770Ssam 	   { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR" };
2617770Ssam 	register char *cp, *file;
2627770Ssam 	u_short op = ntohs(tp->th_opcode);
2637770Ssam 	char *index();
2647770Ssam 
2657770Ssam 	if (op < RRQ || op > ERROR)
2667770Ssam 		printf("%s opcode=%x ", s, op);
2677770Ssam 	else
2687770Ssam 		printf("%s %s ", s, opcodes[op]);
2697770Ssam 	switch (op) {
2707770Ssam 
2717770Ssam 	case RRQ:
2727770Ssam 	case WRQ:
2737770Ssam 		n -= 2;
2747770Ssam 		file = cp = tp->th_stuff;
2757770Ssam 		cp = index(cp, '\0');
2767770Ssam 		printf("<file=%s, mode=%s>\n", file, cp + 1);
2777770Ssam 		break;
2787770Ssam 
2797770Ssam 	case DATA:
2807770Ssam 		printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), n - 4);
2817770Ssam 		break;
2827770Ssam 
2837770Ssam 	case ACK:
2847770Ssam 		printf("<block=%d>\n", ntohs(tp->th_block));
2857770Ssam 		break;
2867770Ssam 
2877770Ssam 	case ERROR:
2887770Ssam 		printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg);
2897770Ssam 		break;
2907770Ssam 	}
2917770Ssam }
292