xref: /csrg-svn/usr.bin/tftp/tftp.c (revision 62309)
122404Sdist /*
2*62309Sbostic  * Copyright (c) 1983, 1993
3*62309Sbostic  *	The Regents of the University of California.  All rights reserved.
433821Sbostic  *
542770Sbostic  * %sccs.include.redist.c%
622404Sdist  */
722404Sdist 
814554Ssam #ifndef lint
9*62309Sbostic static char sccsid[] = "@(#)tftp.c	8.1 (Berkeley) 06/06/93";
1033821Sbostic #endif /* not lint */
117770Ssam 
1226094Sminshall /* Many bug fixes are from Jim Guyton <guyton@rand-unix> */
1326094Sminshall 
147770Ssam /*
157770Ssam  * TFTP User Program -- Protocol Machines
167770Ssam  */
177770Ssam #include <sys/types.h>
187770Ssam #include <sys/socket.h>
1926094Sminshall #include <sys/time.h>
209220Ssam 
219220Ssam #include <netinet/in.h>
2226094Sminshall 
2312399Ssam #include <arpa/tftp.h>
2412399Ssam 
2560062Storek #include <errno.h>
2660062Storek #include <setjmp.h>
277770Ssam #include <signal.h>
287770Ssam #include <stdio.h>
2960062Storek #include <unistd.h>
309220Ssam 
3160062Storek #include "extern.h"
3260062Storek #include "tftpsubs.h"
3360062Storek 
347770Ssam extern	int errno;
3526094Sminshall 
3660062Storek extern  struct sockaddr_in peeraddr;	/* filled in by main */
3760062Storek extern  int     f;			/* the opened socket */
3826094Sminshall extern  int     trace;
3926094Sminshall extern  int     verbose;
4026094Sminshall extern  int     rexmtval;
4126094Sminshall extern  int     maxtimeout;
4226094Sminshall 
4326094Sminshall #define PKTSIZE    SEGSIZE+4
4426094Sminshall char    ackbuf[PKTSIZE];
457770Ssam int	timeout;
467770Ssam jmp_buf	toplevel;
4713018Ssam jmp_buf	timeoutbuf;
487770Ssam 
4960062Storek static void nak __P((int));
5060062Storek static int makerequest __P((int, const char *, struct tftphdr *, const char *));
5160062Storek static void printstats __P((const char *, unsigned long));
5260062Storek static void startclock __P((void));
5360062Storek static void stopclock __P((void));
5460062Storek static void timer __P((int));
5560062Storek static void tpacket __P((const char *, struct tftphdr *, int));
567770Ssam 
577770Ssam /*
587770Ssam  * Send the requested file.
597770Ssam  */
6060062Storek void
sendfile(fd,name,mode)6126094Sminshall sendfile(fd, name, mode)
627770Ssam 	int fd;
637770Ssam 	char *name;
6426094Sminshall 	char *mode;
657770Ssam {
6660062Storek 	register struct tftphdr *ap;	   /* data and ack packets */
6726094Sminshall 	struct tftphdr *r_init(), *dp;
6860062Storek 	register int n;
6960062Storek 	volatile int block, size, convert;
7060062Storek 	volatile unsigned long amount;
7126094Sminshall 	struct sockaddr_in from;
7226094Sminshall 	int fromlen;
7326094Sminshall 	FILE *file;
747770Ssam 
7560062Storek 	startclock();		/* start stat's clock */
7660062Storek 	dp = r_init();		/* reset fillbuf/read-ahead code */
7726094Sminshall 	ap = (struct tftphdr *)ackbuf;
7826094Sminshall 	file = fdopen(fd, "r");
7926094Sminshall 	convert = !strcmp(mode, "netascii");
8060062Storek 	block = 0;
8160062Storek 	amount = 0;
8226094Sminshall 
8313018Ssam 	signal(SIGALRM, timer);
847770Ssam 	do {
8513018Ssam 		if (block == 0)
8626094Sminshall 			size = makerequest(WRQ, name, dp, mode) - 4;
8713018Ssam 		else {
8860062Storek 		/*	size = read(fd, dp->th_data, SEGSIZE);	 */
8926094Sminshall 			size = readit(file, &dp, convert);
907770Ssam 			if (size < 0) {
9126094Sminshall 				nak(errno + 100);
927770Ssam 				break;
937770Ssam 			}
9426094Sminshall 			dp->th_opcode = htons((u_short)DATA);
9526094Sminshall 			dp->th_block = htons((u_short)block);
967770Ssam 		}
977770Ssam 		timeout = 0;
9813018Ssam 		(void) setjmp(timeoutbuf);
9926104Sminshall send_data:
1007770Ssam 		if (trace)
10126094Sminshall 			tpacket("sent", dp, size + 4);
10246861Sbostic 		n = sendto(f, dp, size + 4, 0,
10360062Storek 		    (struct sockaddr *)&peeraddr, sizeof(peeraddr));
1049220Ssam 		if (n != size + 4) {
10513018Ssam 			perror("tftp: sendto");
10626094Sminshall 			goto abort;
1077770Ssam 		}
10826094Sminshall 		read_ahead(file, convert);
10926104Sminshall 		for ( ; ; ) {
11013018Ssam 			alarm(rexmtval);
11113018Ssam 			do {
11260062Storek 				fromlen = sizeof(from);
11360062Storek 				n = recvfrom(f, ackbuf, sizeof(ackbuf), 0,
11446861Sbostic 				    (struct sockaddr *)&from, &fromlen);
11513018Ssam 			} while (n <= 0);
1167770Ssam 			alarm(0);
11713018Ssam 			if (n < 0) {
11813018Ssam 				perror("tftp: recvfrom");
11926094Sminshall 				goto abort;
12013018Ssam 			}
12160062Storek 			peeraddr.sin_port = from.sin_port;	/* added */
12213018Ssam 			if (trace)
12326094Sminshall 				tpacket("received", ap, n);
12413018Ssam 			/* should verify packet came from server */
12526094Sminshall 			ap->th_opcode = ntohs(ap->th_opcode);
12626094Sminshall 			ap->th_block = ntohs(ap->th_block);
12726094Sminshall 			if (ap->th_opcode == ERROR) {
12826094Sminshall 				printf("Error code %d: %s\n", ap->th_code,
12926094Sminshall 					ap->th_msg);
13026094Sminshall 				goto abort;
13113018Ssam 			}
13226104Sminshall 			if (ap->th_opcode == ACK) {
13326113Sminshall 				int j;
13426113Sminshall 
13526104Sminshall 				if (ap->th_block == block) {
13626104Sminshall 					break;
13726104Sminshall 				}
13826113Sminshall 				/* On an error, try to synchronize
13926113Sminshall 				 * both sides.
14026113Sminshall 				 */
14126113Sminshall 				j = synchnet(f);
14226113Sminshall 				if (j && trace) {
14326113Sminshall 					printf("discarded %d packets\n",
14426113Sminshall 							j);
14526113Sminshall 				}
14626113Sminshall 				if (ap->th_block == (block-1)) {
14726113Sminshall 					goto send_data;
14826113Sminshall 				}
14926104Sminshall 			}
15026104Sminshall 		}
1517770Ssam 		if (block > 0)
1527770Ssam 			amount += size;
1537770Ssam 		block++;
1547770Ssam 	} while (size == SEGSIZE || block == 1);
15526094Sminshall abort:
15626094Sminshall 	fclose(file);
15726094Sminshall 	stopclock();
15826094Sminshall 	if (amount > 0)
15926094Sminshall 		printstats("Sent", amount);
1607770Ssam }
1617770Ssam 
1627770Ssam /*
1637770Ssam  * Receive a file.
1647770Ssam  */
16560062Storek void
recvfile(fd,name,mode)16626094Sminshall recvfile(fd, name, mode)
1677770Ssam 	int fd;
1687770Ssam 	char *name;
16926094Sminshall 	char *mode;
1707770Ssam {
17126094Sminshall 	register struct tftphdr *ap;
17226094Sminshall 	struct tftphdr *dp, *w_init();
17360062Storek 	register int n;
17460062Storek 	volatile int block, size, firsttrip;
17560062Storek 	volatile unsigned long amount;
17626094Sminshall 	struct sockaddr_in from;
17760062Storek 	int fromlen;
17826094Sminshall 	FILE *file;
17960062Storek 	volatile int convert;		/* true if converting crlf -> lf */
1807770Ssam 
18126094Sminshall 	startclock();
18226094Sminshall 	dp = w_init();
18326094Sminshall 	ap = (struct tftphdr *)ackbuf;
18426094Sminshall 	file = fdopen(fd, "w");
18526094Sminshall 	convert = !strcmp(mode, "netascii");
18660062Storek 	block = 1;
18760062Storek 	firsttrip = 1;
18860062Storek 	amount = 0;
18926094Sminshall 
19013018Ssam 	signal(SIGALRM, timer);
1917770Ssam 	do {
19213018Ssam 		if (firsttrip) {
19326094Sminshall 			size = makerequest(RRQ, name, ap, mode);
19413018Ssam 			firsttrip = 0;
19513018Ssam 		} else {
19626094Sminshall 			ap->th_opcode = htons((u_short)ACK);
19726094Sminshall 			ap->th_block = htons((u_short)(block));
19813018Ssam 			size = 4;
19913018Ssam 			block++;
20013018Ssam 		}
2017770Ssam 		timeout = 0;
20213018Ssam 		(void) setjmp(timeoutbuf);
20326094Sminshall send_ack:
2047770Ssam 		if (trace)
20526094Sminshall 			tpacket("sent", ap, size);
20660062Storek 		if (sendto(f, ackbuf, size, 0, (struct sockaddr *)&peeraddr,
20760062Storek 		    sizeof(peeraddr)) != size) {
20813018Ssam 			alarm(0);
20913018Ssam 			perror("tftp: sendto");
21026094Sminshall 			goto abort;
2117770Ssam 		}
21226094Sminshall 		write_behind(file, convert);
21326094Sminshall 		for ( ; ; ) {
21413018Ssam 			alarm(rexmtval);
21526094Sminshall 			do  {
21660062Storek 				fromlen = sizeof(from);
21726094Sminshall 				n = recvfrom(f, dp, PKTSIZE, 0,
21846861Sbostic 				    (struct sockaddr *)&from, &fromlen);
21916384Ssam 			} while (n <= 0);
2207770Ssam 			alarm(0);
22113018Ssam 			if (n < 0) {
22213018Ssam 				perror("tftp: recvfrom");
22326094Sminshall 				goto abort;
22413018Ssam 			}
22560062Storek 			peeraddr.sin_port = from.sin_port;	/* added */
22626094Sminshall 			if (trace)
22726094Sminshall 				tpacket("received", dp, n);
22826094Sminshall 			/* should verify client address */
22926094Sminshall 			dp->th_opcode = ntohs(dp->th_opcode);
23026094Sminshall 			dp->th_block = ntohs(dp->th_block);
23126094Sminshall 			if (dp->th_opcode == ERROR) {
23226094Sminshall 				printf("Error code %d: %s\n", dp->th_code,
23326094Sminshall 					dp->th_msg);
23426094Sminshall 				goto abort;
23516384Ssam 			}
23626094Sminshall 			if (dp->th_opcode == DATA) {
23726113Sminshall 				int j;
23826113Sminshall 
23926113Sminshall 				if (dp->th_block == block) {
24060062Storek 					break;		/* have next packet */
24126113Sminshall 				}
24226113Sminshall 				/* On an error, try to synchronize
24326113Sminshall 				 * both sides.
24426113Sminshall 				 */
24526113Sminshall 				j = synchnet(f);
24626113Sminshall 				if (j && trace) {
24726113Sminshall 					printf("discarded %d packets\n", j);
24826113Sminshall 				}
24926113Sminshall 				if (dp->th_block == (block-1)) {
25060062Storek 					goto send_ack;	/* resend ack */
25126113Sminshall 				}
25216384Ssam 			}
25326094Sminshall 		}
25460062Storek 	/*	size = write(fd, dp->th_data, n - 4); */
25526094Sminshall 		size = writeit(file, &dp, n - 4, convert);
2567770Ssam 		if (size < 0) {
25726094Sminshall 			nak(errno + 100);
25826094Sminshall 			break;
2597770Ssam 		}
2607770Ssam 		amount += size;
2617770Ssam 	} while (size == SEGSIZE);
26260062Storek abort:						/* ok to ack, since user */
26360062Storek 	ap->th_opcode = htons((u_short)ACK);	/* has seen err msg */
26426094Sminshall 	ap->th_block = htons((u_short)block);
26560062Storek 	(void) sendto(f, ackbuf, 4, 0, (struct sockaddr *)&peeraddr,
26660062Storek 	    sizeof(peeraddr));
26760062Storek 	write_behind(file, convert);		/* flush last buffer */
26826094Sminshall 	fclose(file);
26926094Sminshall 	stopclock();
27026094Sminshall 	if (amount > 0)
27126094Sminshall 		printstats("Received", amount);
2727770Ssam }
2737770Ssam 
27460062Storek static int
makerequest(request,name,tp,mode)27526094Sminshall makerequest(request, name, tp, mode)
2767770Ssam 	int request;
27760062Storek 	const char *name;
27826094Sminshall 	struct tftphdr *tp;
27960062Storek 	const char *mode;
2807770Ssam {
2817770Ssam 	register char *cp;
2827770Ssam 
28326094Sminshall 	tp->th_opcode = htons((u_short)request);
28426094Sminshall 	cp = tp->th_stuff;
28526094Sminshall 	strcpy(cp, name);
28626094Sminshall 	cp += strlen(name);
2877770Ssam 	*cp++ = '\0';
2887770Ssam 	strcpy(cp, mode);
28926094Sminshall 	cp += strlen(mode);
2907770Ssam 	*cp++ = '\0';
29126094Sminshall 	return (cp - (char *)tp);
2927770Ssam }
2937770Ssam 
2947770Ssam struct errmsg {
2957770Ssam 	int	e_code;
2967770Ssam 	char	*e_msg;
2977770Ssam } errmsgs[] = {
2987770Ssam 	{ EUNDEF,	"Undefined error code" },
2997770Ssam 	{ ENOTFOUND,	"File not found" },
3007770Ssam 	{ EACCESS,	"Access violation" },
3017770Ssam 	{ ENOSPACE,	"Disk full or allocation exceeded" },
3027770Ssam 	{ EBADOP,	"Illegal TFTP operation" },
3037770Ssam 	{ EBADID,	"Unknown transfer ID" },
3047770Ssam 	{ EEXISTS,	"File already exists" },
3057770Ssam 	{ ENOUSER,	"No such user" },
3067770Ssam 	{ -1,		0 }
3077770Ssam };
3087770Ssam 
3097770Ssam /*
3107770Ssam  * Send a nak packet (error message).
3117770Ssam  * Error code passed in is one of the
3127770Ssam  * standard TFTP codes, or a UNIX errno
3137770Ssam  * offset by 100.
3147770Ssam  */
31560062Storek static void
nak(error)31626094Sminshall nak(error)
3177770Ssam 	int error;
3187770Ssam {
31942423Sbostic 	register struct errmsg *pe;
32026094Sminshall 	register struct tftphdr *tp;
3217770Ssam 	int length;
32242423Sbostic 	char *strerror();
3237770Ssam 
32426094Sminshall 	tp = (struct tftphdr *)ackbuf;
32526094Sminshall 	tp->th_opcode = htons((u_short)ERROR);
32626094Sminshall 	tp->th_code = htons((u_short)error);
3277770Ssam 	for (pe = errmsgs; pe->e_code >= 0; pe++)
3287770Ssam 		if (pe->e_code == error)
3297770Ssam 			break;
33026094Sminshall 	if (pe->e_code < 0) {
33142423Sbostic 		pe->e_msg = strerror(error - 100);
33226094Sminshall 		tp->th_code = EUNDEF;
33326094Sminshall 	}
33426094Sminshall 	strcpy(tp->th_msg, pe->e_msg);
3357770Ssam 	length = strlen(pe->e_msg) + 4;
3367770Ssam 	if (trace)
33726094Sminshall 		tpacket("sent", tp, length);
33860062Storek 	if (sendto(f, ackbuf, length, 0, (struct sockaddr *)&peeraddr,
33960062Storek 	    sizeof(peeraddr)) != length)
34026094Sminshall 		perror("nak");
3417770Ssam }
3427770Ssam 
34360062Storek static void
tpacket(s,tp,n)34426094Sminshall tpacket(s, tp, n)
34560062Storek 	const char *s;
3467770Ssam 	struct tftphdr *tp;
3477770Ssam 	int n;
3487770Ssam {
3497770Ssam 	static char *opcodes[] =
3507770Ssam 	   { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR" };
3517770Ssam 	register char *cp, *file;
3527770Ssam 	u_short op = ntohs(tp->th_opcode);
3537770Ssam 	char *index();
3547770Ssam 
3557770Ssam 	if (op < RRQ || op > ERROR)
35626094Sminshall 		printf("%s opcode=%x ", s, op);
3577770Ssam 	else
35826094Sminshall 		printf("%s %s ", s, opcodes[op]);
3597770Ssam 	switch (op) {
3607770Ssam 
3617770Ssam 	case RRQ:
3627770Ssam 	case WRQ:
3637770Ssam 		n -= 2;
3647770Ssam 		file = cp = tp->th_stuff;
3657770Ssam 		cp = index(cp, '\0');
3667770Ssam 		printf("<file=%s, mode=%s>\n", file, cp + 1);
3677770Ssam 		break;
3687770Ssam 
3697770Ssam 	case DATA:
3707770Ssam 		printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), n - 4);
3717770Ssam 		break;
3727770Ssam 
3737770Ssam 	case ACK:
3747770Ssam 		printf("<block=%d>\n", ntohs(tp->th_block));
3757770Ssam 		break;
3767770Ssam 
3777770Ssam 	case ERROR:
3787770Ssam 		printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg);
3797770Ssam 		break;
3807770Ssam 	}
3817770Ssam }
38226094Sminshall 
38326094Sminshall struct timeval tstart;
38426094Sminshall struct timeval tstop;
38526094Sminshall 
38660062Storek static void
startclock()38760062Storek startclock()
38860062Storek {
38960062Storek 
39060062Storek 	(void)gettimeofday(&tstart, NULL);
39126094Sminshall }
39226094Sminshall 
39360062Storek static void
stopclock()39460062Storek stopclock()
39560062Storek {
39660062Storek 
39760062Storek 	(void)gettimeofday(&tstop, NULL);
39826094Sminshall }
39926094Sminshall 
40060062Storek static void
printstats(direction,amount)40126094Sminshall printstats(direction, amount)
40260062Storek 	const char *direction;
40360062Storek 	unsigned long amount;
40426094Sminshall {
40526094Sminshall 	double delta;
40626094Sminshall 			/* compute delta in 1/10's second units */
40726094Sminshall 	delta = ((tstop.tv_sec*10.)+(tstop.tv_usec/100000)) -
40826094Sminshall 		((tstart.tv_sec*10.)+(tstart.tv_usec/100000));
40926094Sminshall 	delta = delta/10.;      /* back to seconds */
41026094Sminshall 	printf("%s %d bytes in %.1f seconds", direction, amount, delta);
41126094Sminshall 	if (verbose)
41226094Sminshall 		printf(" [%.0f bits/sec]", (amount*8.)/delta);
41326094Sminshall 	putchar('\n');
41426094Sminshall }
41526094Sminshall 
41660062Storek static void
timer(sig)41760062Storek timer(sig)
41860062Storek 	int sig;
41960062Storek {
42060062Storek 
42160062Storek 	timeout += rexmtval;
42260062Storek 	if (timeout >= maxtimeout) {
42360062Storek 		printf("Transfer timed out.\n");
42460062Storek 		longjmp(toplevel, -1);
42560062Storek 	}
42660062Storek 	longjmp(timeoutbuf, 1);
42760062Storek }
428