xref: /csrg-svn/usr.bin/tftp/tftp.c (revision 26094)
122404Sdist /*
2*26094Sminshall  * Copyright (c) 1985 Regents of the University of California.
322404Sdist  * All rights reserved.  The Berkeley software License Agreement
422404Sdist  * specifies the terms and conditions for redistribution.
522404Sdist  */
622404Sdist 
714554Ssam #ifndef lint
8*26094Sminshall static char sccsid[] = "@(#)tftp.c	5.2 (Berkeley) 02/06/86";
922404Sdist #endif not lint
107770Ssam 
11*26094Sminshall /* Many bug fixes are from Jim Guyton <guyton@rand-unix> */
12*26094Sminshall 
137770Ssam /*
147770Ssam  * TFTP User Program -- Protocol Machines
157770Ssam  */
167770Ssam #include <sys/types.h>
177770Ssam #include <sys/socket.h>
18*26094Sminshall #include <sys/time.h>
199220Ssam 
209220Ssam #include <netinet/in.h>
21*26094Sminshall 
2212399Ssam #include <arpa/tftp.h>
2312399Ssam 
247770Ssam #include <signal.h>
257770Ssam #include <stdio.h>
267770Ssam #include <errno.h>
277770Ssam #include <setjmp.h>
289220Ssam 
297770Ssam extern	int errno;
30*26094Sminshall 
31*26094Sminshall extern  struct sockaddr_in sin;         /* filled in by main */
32*26094Sminshall extern  int     f;                      /* the opened socket */
33*26094Sminshall extern  int     trace;
34*26094Sminshall extern  int     verbose;
35*26094Sminshall extern  int     rexmtval;
36*26094Sminshall extern  int     maxtimeout;
37*26094Sminshall 
38*26094Sminshall #define PKTSIZE    SEGSIZE+4
39*26094Sminshall char    ackbuf[PKTSIZE];
407770Ssam int	timeout;
417770Ssam jmp_buf	toplevel;
4213018Ssam jmp_buf	timeoutbuf;
437770Ssam 
447770Ssam timer()
457770Ssam {
4613018Ssam 
4713018Ssam 	timeout += rexmtval;
4813018Ssam 	if (timeout >= maxtimeout) {
497770Ssam 		printf("Transfer timed out.\n");
507770Ssam 		longjmp(toplevel, -1);
517770Ssam 	}
5213018Ssam 	longjmp(timeoutbuf, 1);
537770Ssam }
547770Ssam 
557770Ssam /*
567770Ssam  * Send the requested file.
577770Ssam  */
58*26094Sminshall sendfile(fd, name, mode)
597770Ssam 	int fd;
607770Ssam 	char *name;
61*26094Sminshall 	char *mode;
627770Ssam {
63*26094Sminshall 	register struct tftphdr *ap;       /* data and ack packets */
64*26094Sminshall 	struct tftphdr *r_init(), *dp;
65*26094Sminshall 	register int block = 0, size, n;
66*26094Sminshall 	register unsigned long amount = 0;
67*26094Sminshall 	struct sockaddr_in from;
68*26094Sminshall 	int fromlen;
69*26094Sminshall 	int convert;            /* true if doing nl->crlf conversion */
70*26094Sminshall 	FILE *file;
717770Ssam 
72*26094Sminshall 	startclock();           /* start stat's clock */
73*26094Sminshall 	dp = r_init();          /* reset fillbuf/read-ahead code */
74*26094Sminshall 	ap = (struct tftphdr *)ackbuf;
75*26094Sminshall 	file = fdopen(fd, "r");
76*26094Sminshall 	convert = !strcmp(mode, "netascii");
77*26094Sminshall 
7813018Ssam 	signal(SIGALRM, timer);
797770Ssam 	do {
8013018Ssam 		if (block == 0)
81*26094Sminshall 			size = makerequest(WRQ, name, dp, mode) - 4;
8213018Ssam 		else {
83*26094Sminshall 		/*      size = read(fd, dp->th_data, SEGSIZE);   */
84*26094Sminshall 			size = readit(file, &dp, convert);
857770Ssam 			if (size < 0) {
86*26094Sminshall 				nak(errno + 100);
877770Ssam 				break;
887770Ssam 			}
89*26094Sminshall 			dp->th_opcode = htons((u_short)DATA);
90*26094Sminshall 			dp->th_block = htons((u_short)block);
917770Ssam 		}
927770Ssam 		timeout = 0;
9313018Ssam 		(void) setjmp(timeoutbuf);
947770Ssam 		if (trace)
95*26094Sminshall 			tpacket("sent", dp, size + 4);
96*26094Sminshall 		n = sendto(f, dp, size + 4, 0, (caddr_t)&sin, sizeof (sin));
979220Ssam 		if (n != size + 4) {
9813018Ssam 			perror("tftp: sendto");
99*26094Sminshall 			goto abort;
1007770Ssam 		}
101*26094Sminshall 		read_ahead(file, convert);
10213018Ssam 		do {
10313018Ssam 			alarm(rexmtval);
10413018Ssam 			do {
10513018Ssam 				fromlen = sizeof (from);
106*26094Sminshall 				n = recvfrom(f, ackbuf, sizeof (ackbuf), 0,
10713018Ssam 				    (caddr_t)&from, &fromlen);
10813018Ssam 			} while (n <= 0);
1097770Ssam 			alarm(0);
11013018Ssam 			if (n < 0) {
11113018Ssam 				perror("tftp: recvfrom");
112*26094Sminshall 				goto abort;
11313018Ssam 			}
114*26094Sminshall 			sin.sin_port = from.sin_port;   /* added */
11513018Ssam 			if (trace)
116*26094Sminshall 				tpacket("received", ap, n);
11713018Ssam 			/* should verify packet came from server */
118*26094Sminshall 			ap->th_opcode = ntohs(ap->th_opcode);
119*26094Sminshall 			ap->th_block = ntohs(ap->th_block);
120*26094Sminshall 			if (ap->th_opcode == ERROR) {
121*26094Sminshall 				printf("Error code %d: %s\n", ap->th_code,
122*26094Sminshall 					ap->th_msg);
123*26094Sminshall 				goto abort;
12413018Ssam 			}
125*26094Sminshall 		} while (ap->th_opcode != ACK || block != ap->th_block);
1267770Ssam 		if (block > 0)
1277770Ssam 			amount += size;
1287770Ssam 		block++;
1297770Ssam 	} while (size == SEGSIZE || block == 1);
130*26094Sminshall abort:
131*26094Sminshall 	fclose(file);
132*26094Sminshall 	stopclock();
133*26094Sminshall 	if (amount > 0)
134*26094Sminshall 		printstats("Sent", amount);
1357770Ssam }
1367770Ssam 
1377770Ssam /*
1387770Ssam  * Receive a file.
1397770Ssam  */
140*26094Sminshall recvfile(fd, name, mode)
1417770Ssam 	int fd;
1427770Ssam 	char *name;
143*26094Sminshall 	char *mode;
1447770Ssam {
145*26094Sminshall 	register struct tftphdr *ap;
146*26094Sminshall 	struct tftphdr *dp, *w_init();
147*26094Sminshall 	register int block = 1, n, size;
148*26094Sminshall 	unsigned long amount = 0;
149*26094Sminshall 	struct sockaddr_in from;
150*26094Sminshall 	int fromlen, firsttrip = 1;
151*26094Sminshall 	FILE *file;
152*26094Sminshall 	int convert;                    /* true if converting crlf -> lf */
1537770Ssam 
154*26094Sminshall 	startclock();
155*26094Sminshall 	dp = w_init();
156*26094Sminshall 	ap = (struct tftphdr *)ackbuf;
157*26094Sminshall 	file = fdopen(fd, "w");
158*26094Sminshall 	convert = !strcmp(mode, "netascii");
159*26094Sminshall 
16013018Ssam 	signal(SIGALRM, timer);
1617770Ssam 	do {
16213018Ssam 		if (firsttrip) {
163*26094Sminshall 			size = makerequest(RRQ, name, ap, mode);
16413018Ssam 			firsttrip = 0;
16513018Ssam 		} else {
166*26094Sminshall 			ap->th_opcode = htons((u_short)ACK);
167*26094Sminshall 			ap->th_block = htons((u_short)(block));
16813018Ssam 			size = 4;
16913018Ssam 			block++;
17013018Ssam 		}
1717770Ssam 		timeout = 0;
17213018Ssam 		(void) setjmp(timeoutbuf);
173*26094Sminshall send_ack:
1747770Ssam 		if (trace)
175*26094Sminshall 			tpacket("sent", ap, size);
176*26094Sminshall 		if (sendto(f, ackbuf, size, 0, (caddr_t)&sin,
177*26094Sminshall 		    sizeof (sin)) != size) {
17813018Ssam 			alarm(0);
17913018Ssam 			perror("tftp: sendto");
180*26094Sminshall 			goto abort;
1817770Ssam 		}
182*26094Sminshall 		write_behind(file, convert);
183*26094Sminshall 		for ( ; ; ) {
18413018Ssam 			alarm(rexmtval);
185*26094Sminshall 			do  {
18616384Ssam 				fromlen = sizeof (from);
187*26094Sminshall 				n = recvfrom(f, dp, PKTSIZE, 0,
18813018Ssam 				    (caddr_t)&from, &fromlen);
18916384Ssam 			} while (n <= 0);
1907770Ssam 			alarm(0);
19113018Ssam 			if (n < 0) {
19213018Ssam 				perror("tftp: recvfrom");
193*26094Sminshall 				goto abort;
19413018Ssam 			}
195*26094Sminshall 			sin.sin_port = from.sin_port;   /* added */
196*26094Sminshall 			if (trace)
197*26094Sminshall 				tpacket("received", dp, n);
198*26094Sminshall 			/* should verify client address */
199*26094Sminshall 			dp->th_opcode = ntohs(dp->th_opcode);
200*26094Sminshall 			dp->th_block = ntohs(dp->th_block);
201*26094Sminshall 			if (dp->th_opcode == ERROR) {
202*26094Sminshall 				printf("Error code %d: %s\n", dp->th_code,
203*26094Sminshall 					dp->th_msg);
204*26094Sminshall 				goto abort;
20516384Ssam 			}
206*26094Sminshall 			if (dp->th_opcode == DATA) {
207*26094Sminshall 				if (dp->th_block == block)
208*26094Sminshall 					break;          /* have next packet */
209*26094Sminshall 				if (dp->th_block == (block-1))
210*26094Sminshall 					goto send_ack;  /* resend ack */
21116384Ssam 			}
212*26094Sminshall 		}
213*26094Sminshall 	/*      size = write(fd, dp->th_data, n - 4); */
214*26094Sminshall 		size = writeit(file, &dp, n - 4, convert);
2157770Ssam 		if (size < 0) {
216*26094Sminshall 			nak(errno + 100);
217*26094Sminshall 			break;
2187770Ssam 		}
2197770Ssam 		amount += size;
2207770Ssam 	} while (size == SEGSIZE);
221*26094Sminshall abort:                                          /* ok to ack, since user */
222*26094Sminshall 	ap->th_opcode = htons((u_short)ACK);    /* has seen err msg */
223*26094Sminshall 	ap->th_block = htons((u_short)block);
224*26094Sminshall 	(void) sendto(f, ackbuf, 4, 0, &sin, sizeof (sin));
225*26094Sminshall 	write_behind(file, convert);            /* flush last buffer */
226*26094Sminshall 	fclose(file);
227*26094Sminshall 	stopclock();
228*26094Sminshall 	if (amount > 0)
229*26094Sminshall 		printstats("Received", amount);
2307770Ssam }
2317770Ssam 
232*26094Sminshall makerequest(request, name, tp, mode)
2337770Ssam 	int request;
234*26094Sminshall 	char *name, *mode;
235*26094Sminshall 	struct tftphdr *tp;
2367770Ssam {
2377770Ssam 	register char *cp;
2387770Ssam 
239*26094Sminshall 	tp->th_opcode = htons((u_short)request);
240*26094Sminshall 	cp = tp->th_stuff;
241*26094Sminshall 	strcpy(cp, name);
242*26094Sminshall 	cp += strlen(name);
2437770Ssam 	*cp++ = '\0';
2447770Ssam 	strcpy(cp, mode);
245*26094Sminshall 	cp += strlen(mode);
2467770Ssam 	*cp++ = '\0';
247*26094Sminshall 	return (cp - (char *)tp);
2487770Ssam }
2497770Ssam 
2507770Ssam struct errmsg {
2517770Ssam 	int	e_code;
2527770Ssam 	char	*e_msg;
2537770Ssam } errmsgs[] = {
2547770Ssam 	{ EUNDEF,	"Undefined error code" },
2557770Ssam 	{ ENOTFOUND,	"File not found" },
2567770Ssam 	{ EACCESS,	"Access violation" },
2577770Ssam 	{ ENOSPACE,	"Disk full or allocation exceeded" },
2587770Ssam 	{ EBADOP,	"Illegal TFTP operation" },
2597770Ssam 	{ EBADID,	"Unknown transfer ID" },
2607770Ssam 	{ EEXISTS,	"File already exists" },
2617770Ssam 	{ ENOUSER,	"No such user" },
2627770Ssam 	{ -1,		0 }
2637770Ssam };
2647770Ssam 
2657770Ssam /*
2667770Ssam  * Send a nak packet (error message).
2677770Ssam  * Error code passed in is one of the
2687770Ssam  * standard TFTP codes, or a UNIX errno
2697770Ssam  * offset by 100.
2707770Ssam  */
271*26094Sminshall nak(error)
2727770Ssam 	int error;
2737770Ssam {
274*26094Sminshall 	register struct tftphdr *tp;
2757770Ssam 	int length;
2767770Ssam 	register struct errmsg *pe;
2777770Ssam 	extern char *sys_errlist[];
2787770Ssam 
279*26094Sminshall 	tp = (struct tftphdr *)ackbuf;
280*26094Sminshall 	tp->th_opcode = htons((u_short)ERROR);
281*26094Sminshall 	tp->th_code = htons((u_short)error);
2827770Ssam 	for (pe = errmsgs; pe->e_code >= 0; pe++)
2837770Ssam 		if (pe->e_code == error)
2847770Ssam 			break;
285*26094Sminshall 	if (pe->e_code < 0) {
2867770Ssam 		pe->e_msg = sys_errlist[error - 100];
287*26094Sminshall 		tp->th_code = EUNDEF;
288*26094Sminshall 	}
289*26094Sminshall 	strcpy(tp->th_msg, pe->e_msg);
2907770Ssam 	length = strlen(pe->e_msg) + 4;
2917770Ssam 	if (trace)
292*26094Sminshall 		tpacket("sent", tp, length);
293*26094Sminshall 	if (sendto(f, ackbuf, length, 0, &sin, sizeof (sin)) != length)
294*26094Sminshall 		perror("nak");
2957770Ssam }
2967770Ssam 
297*26094Sminshall tpacket(s, tp, n)
2987770Ssam 	struct tftphdr *tp;
2997770Ssam 	int n;
3007770Ssam {
3017770Ssam 	static char *opcodes[] =
3027770Ssam 	   { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR" };
3037770Ssam 	register char *cp, *file;
3047770Ssam 	u_short op = ntohs(tp->th_opcode);
3057770Ssam 	char *index();
3067770Ssam 
3077770Ssam 	if (op < RRQ || op > ERROR)
308*26094Sminshall 		printf("%s opcode=%x ", s, op);
3097770Ssam 	else
310*26094Sminshall 		printf("%s %s ", s, opcodes[op]);
3117770Ssam 	switch (op) {
3127770Ssam 
3137770Ssam 	case RRQ:
3147770Ssam 	case WRQ:
3157770Ssam 		n -= 2;
3167770Ssam 		file = cp = tp->th_stuff;
3177770Ssam 		cp = index(cp, '\0');
3187770Ssam 		printf("<file=%s, mode=%s>\n", file, cp + 1);
3197770Ssam 		break;
3207770Ssam 
3217770Ssam 	case DATA:
3227770Ssam 		printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), n - 4);
3237770Ssam 		break;
3247770Ssam 
3257770Ssam 	case ACK:
3267770Ssam 		printf("<block=%d>\n", ntohs(tp->th_block));
3277770Ssam 		break;
3287770Ssam 
3297770Ssam 	case ERROR:
3307770Ssam 		printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg);
3317770Ssam 		break;
3327770Ssam 	}
3337770Ssam }
334*26094Sminshall 
335*26094Sminshall struct timeval tstart;
336*26094Sminshall struct timeval tstop;
337*26094Sminshall struct timezone zone;
338*26094Sminshall 
339*26094Sminshall startclock() {
340*26094Sminshall 	gettimeofday(&tstart, &zone);
341*26094Sminshall }
342*26094Sminshall 
343*26094Sminshall stopclock() {
344*26094Sminshall 	gettimeofday(&tstop, &zone);
345*26094Sminshall }
346*26094Sminshall 
347*26094Sminshall printstats(direction, amount)
348*26094Sminshall char *direction;
349*26094Sminshall unsigned long amount;
350*26094Sminshall {
351*26094Sminshall 	double delta;
352*26094Sminshall 			/* compute delta in 1/10's second units */
353*26094Sminshall 	delta = ((tstop.tv_sec*10.)+(tstop.tv_usec/100000)) -
354*26094Sminshall 		((tstart.tv_sec*10.)+(tstart.tv_usec/100000));
355*26094Sminshall 	delta = delta/10.;      /* back to seconds */
356*26094Sminshall 	printf("%s %d bytes in %.1f seconds", direction, amount, delta);
357*26094Sminshall 	if (verbose)
358*26094Sminshall 		printf(" [%.0f bits/sec]", (amount*8.)/delta);
359*26094Sminshall 	putchar('\n');
360*26094Sminshall }
361*26094Sminshall 
362