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