xref: /csrg-svn/usr.bin/tftp/tftp.c (revision 26104)
1 /*
2  * Copyright (c) 1985 Regents of the University of California.
3  * All rights reserved.  The Berkeley software License Agreement
4  * specifies the terms and conditions for redistribution.
5  */
6 
7 #ifndef lint
8 static char sccsid[] = "@(#)tftp.c	5.3 (Berkeley) 02/07/86";
9 #endif not lint
10 
11 /* Many bug fixes are from Jim Guyton <guyton@rand-unix> */
12 
13 /*
14  * TFTP User Program -- Protocol Machines
15  */
16 #include <sys/types.h>
17 #include <sys/socket.h>
18 #include <sys/ioctl.h>
19 #include <sys/time.h>
20 
21 #include <netinet/in.h>
22 
23 #include <arpa/tftp.h>
24 
25 #include <signal.h>
26 #include <stdio.h>
27 #include <errno.h>
28 #include <setjmp.h>
29 
30 extern	int errno;
31 
32 extern  struct sockaddr_in sin;         /* filled in by main */
33 extern  int     f;                      /* the opened socket */
34 extern  int     trace;
35 extern  int     verbose;
36 extern  int     rexmtval;
37 extern  int     maxtimeout;
38 
39 #define PKTSIZE    SEGSIZE+4
40 char    ackbuf[PKTSIZE];
41 int	timeout;
42 jmp_buf	toplevel;
43 jmp_buf	timeoutbuf;
44 
45 timer()
46 {
47 
48 	timeout += rexmtval;
49 	if (timeout >= maxtimeout) {
50 		printf("Transfer timed out.\n");
51 		longjmp(toplevel, -1);
52 	}
53 	longjmp(timeoutbuf, 1);
54 }
55 
56 /*
57  * Send the requested file.
58  */
59 sendfile(fd, name, mode)
60 	int fd;
61 	char *name;
62 	char *mode;
63 {
64 	register struct tftphdr *ap;       /* data and ack packets */
65 	struct tftphdr *r_init(), *dp;
66 	register int block = 0, size, n;
67 	register unsigned long amount = 0;
68 	struct sockaddr_in from;
69 	int fromlen;
70 	int convert;            /* true if doing nl->crlf conversion */
71 	FILE *file;
72 
73 	startclock();           /* start stat's clock */
74 	dp = r_init();          /* reset fillbuf/read-ahead code */
75 	ap = (struct tftphdr *)ackbuf;
76 	file = fdopen(fd, "r");
77 	convert = !strcmp(mode, "netascii");
78 
79 	signal(SIGALRM, timer);
80 	do {
81 		if (block == 0)
82 			size = makerequest(WRQ, name, dp, mode) - 4;
83 		else {
84 		/*      size = read(fd, dp->th_data, SEGSIZE);   */
85 			size = readit(file, &dp, convert);
86 			if (size < 0) {
87 				nak(errno + 100);
88 				break;
89 			}
90 			dp->th_opcode = htons((u_short)DATA);
91 			dp->th_block = htons((u_short)block);
92 		}
93 		timeout = 0;
94 		(void) setjmp(timeoutbuf);
95 send_data:
96 		/* Now, we flush anything pending to be read */
97 		/* This is to try to keep in synch between the two sides */
98 		while (1) {
99 			int i, j = 0;
100 			char rbuf[PKTSIZE];
101 
102 			(void) ioctl(f, FIONREAD, &i);
103 			if (i) {
104 				j++;
105 				fromlen = sizeof from;
106 				n = recvfrom(f, rbuf, sizeof (rbuf), 0,
107 					(caddr_t)&from, &fromlen);
108 			} else {
109 				if (j && trace) {
110 					printf("discarded %d packets\n", j);
111 				}
112 				break;
113 			}
114 		}
115 		if (trace)
116 			tpacket("sent", dp, size + 4);
117 		n = sendto(f, dp, size + 4, 0, (caddr_t)&sin, sizeof (sin));
118 		if (n != size + 4) {
119 			perror("tftp: sendto");
120 			goto abort;
121 		}
122 		read_ahead(file, convert);
123 		for ( ; ; ) {
124 			alarm(rexmtval);
125 			do {
126 				fromlen = sizeof (from);
127 				n = recvfrom(f, ackbuf, sizeof (ackbuf), 0,
128 				    (caddr_t)&from, &fromlen);
129 			} while (n <= 0);
130 			alarm(0);
131 			if (n < 0) {
132 				perror("tftp: recvfrom");
133 				goto abort;
134 			}
135 			sin.sin_port = from.sin_port;   /* added */
136 			if (trace)
137 				tpacket("received", ap, n);
138 			/* should verify packet came from server */
139 			ap->th_opcode = ntohs(ap->th_opcode);
140 			ap->th_block = ntohs(ap->th_block);
141 			if (ap->th_opcode == ERROR) {
142 				printf("Error code %d: %s\n", ap->th_code,
143 					ap->th_msg);
144 				goto abort;
145 			}
146 			if (ap->th_opcode == ACK) {
147 				if (ap->th_block == block) {
148 					break;
149 				} else if (ap->th_block == (block-1)) {
150 					goto send_data;	/* resend packet */
151 				}
152 			}
153 		}
154 		if (block > 0)
155 			amount += size;
156 		block++;
157 	} while (size == SEGSIZE || block == 1);
158 abort:
159 	fclose(file);
160 	stopclock();
161 	if (amount > 0)
162 		printstats("Sent", amount);
163 }
164 
165 /*
166  * Receive a file.
167  */
168 recvfile(fd, name, mode)
169 	int fd;
170 	char *name;
171 	char *mode;
172 {
173 	register struct tftphdr *ap;
174 	struct tftphdr *dp, *w_init();
175 	register int block = 1, n, size;
176 	unsigned long amount = 0;
177 	struct sockaddr_in from;
178 	int fromlen, firsttrip = 1;
179 	FILE *file;
180 	int convert;                    /* true if converting crlf -> lf */
181 
182 	startclock();
183 	dp = w_init();
184 	ap = (struct tftphdr *)ackbuf;
185 	file = fdopen(fd, "w");
186 	convert = !strcmp(mode, "netascii");
187 
188 	signal(SIGALRM, timer);
189 	do {
190 		if (firsttrip) {
191 			size = makerequest(RRQ, name, ap, mode);
192 			firsttrip = 0;
193 		} else {
194 			ap->th_opcode = htons((u_short)ACK);
195 			ap->th_block = htons((u_short)(block));
196 			size = 4;
197 			block++;
198 		}
199 		timeout = 0;
200 		(void) setjmp(timeoutbuf);
201 send_ack:
202 		/* Now, we flush anything pending to be read */
203 		/* This is to try to keep in synch between the two sides */
204 		while (1) {
205 			int i, j = 0;
206 			char rbuf[PKTSIZE];
207 
208 			(void) ioctl(f, FIONREAD, &i);
209 			if (i) {
210 				j++;
211 				fromlen = sizeof from;
212 				n = recvfrom(f, rbuf, sizeof (rbuf), 0,
213 					(caddr_t)&from, &fromlen);
214 			} else {
215 				if (j && trace) {
216 					printf("discarded %d packets\n", j);
217 				}
218 				break;
219 			}
220 		}
221 		if (trace)
222 			tpacket("sent", ap, size);
223 		if (sendto(f, ackbuf, size, 0, (caddr_t)&sin,
224 		    sizeof (sin)) != size) {
225 			alarm(0);
226 			perror("tftp: sendto");
227 			goto abort;
228 		}
229 		write_behind(file, convert);
230 		for ( ; ; ) {
231 			alarm(rexmtval);
232 			do  {
233 				fromlen = sizeof (from);
234 				n = recvfrom(f, dp, PKTSIZE, 0,
235 				    (caddr_t)&from, &fromlen);
236 			} while (n <= 0);
237 			alarm(0);
238 			if (n < 0) {
239 				perror("tftp: recvfrom");
240 				goto abort;
241 			}
242 			sin.sin_port = from.sin_port;   /* added */
243 			if (trace)
244 				tpacket("received", dp, n);
245 			/* should verify client address */
246 			dp->th_opcode = ntohs(dp->th_opcode);
247 			dp->th_block = ntohs(dp->th_block);
248 			if (dp->th_opcode == ERROR) {
249 				printf("Error code %d: %s\n", dp->th_code,
250 					dp->th_msg);
251 				goto abort;
252 			}
253 			if (dp->th_opcode == DATA) {
254 				if (dp->th_block == block)
255 					break;          /* have next packet */
256 				if (dp->th_block == (block-1))
257 					goto send_ack;  /* resend ack */
258 			}
259 		}
260 	/*      size = write(fd, dp->th_data, n - 4); */
261 		size = writeit(file, &dp, n - 4, convert);
262 		if (size < 0) {
263 			nak(errno + 100);
264 			break;
265 		}
266 		amount += size;
267 	} while (size == SEGSIZE);
268 abort:                                          /* ok to ack, since user */
269 	ap->th_opcode = htons((u_short)ACK);    /* has seen err msg */
270 	ap->th_block = htons((u_short)block);
271 	(void) sendto(f, ackbuf, 4, 0, &sin, sizeof (sin));
272 	write_behind(file, convert);            /* flush last buffer */
273 	fclose(file);
274 	stopclock();
275 	if (amount > 0)
276 		printstats("Received", amount);
277 }
278 
279 makerequest(request, name, tp, mode)
280 	int request;
281 	char *name, *mode;
282 	struct tftphdr *tp;
283 {
284 	register char *cp;
285 
286 	tp->th_opcode = htons((u_short)request);
287 	cp = tp->th_stuff;
288 	strcpy(cp, name);
289 	cp += strlen(name);
290 	*cp++ = '\0';
291 	strcpy(cp, mode);
292 	cp += strlen(mode);
293 	*cp++ = '\0';
294 	return (cp - (char *)tp);
295 }
296 
297 struct errmsg {
298 	int	e_code;
299 	char	*e_msg;
300 } errmsgs[] = {
301 	{ EUNDEF,	"Undefined error code" },
302 	{ ENOTFOUND,	"File not found" },
303 	{ EACCESS,	"Access violation" },
304 	{ ENOSPACE,	"Disk full or allocation exceeded" },
305 	{ EBADOP,	"Illegal TFTP operation" },
306 	{ EBADID,	"Unknown transfer ID" },
307 	{ EEXISTS,	"File already exists" },
308 	{ ENOUSER,	"No such user" },
309 	{ -1,		0 }
310 };
311 
312 /*
313  * Send a nak packet (error message).
314  * Error code passed in is one of the
315  * standard TFTP codes, or a UNIX errno
316  * offset by 100.
317  */
318 nak(error)
319 	int error;
320 {
321 	register struct tftphdr *tp;
322 	int length;
323 	register struct errmsg *pe;
324 	extern char *sys_errlist[];
325 
326 	tp = (struct tftphdr *)ackbuf;
327 	tp->th_opcode = htons((u_short)ERROR);
328 	tp->th_code = htons((u_short)error);
329 	for (pe = errmsgs; pe->e_code >= 0; pe++)
330 		if (pe->e_code == error)
331 			break;
332 	if (pe->e_code < 0) {
333 		pe->e_msg = sys_errlist[error - 100];
334 		tp->th_code = EUNDEF;
335 	}
336 	strcpy(tp->th_msg, pe->e_msg);
337 	length = strlen(pe->e_msg) + 4;
338 	if (trace)
339 		tpacket("sent", tp, length);
340 	if (sendto(f, ackbuf, length, 0, &sin, sizeof (sin)) != length)
341 		perror("nak");
342 }
343 
344 tpacket(s, tp, n)
345 	struct tftphdr *tp;
346 	int n;
347 {
348 	static char *opcodes[] =
349 	   { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR" };
350 	register char *cp, *file;
351 	u_short op = ntohs(tp->th_opcode);
352 	char *index();
353 
354 	if (op < RRQ || op > ERROR)
355 		printf("%s opcode=%x ", s, op);
356 	else
357 		printf("%s %s ", s, opcodes[op]);
358 	switch (op) {
359 
360 	case RRQ:
361 	case WRQ:
362 		n -= 2;
363 		file = cp = tp->th_stuff;
364 		cp = index(cp, '\0');
365 		printf("<file=%s, mode=%s>\n", file, cp + 1);
366 		break;
367 
368 	case DATA:
369 		printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), n - 4);
370 		break;
371 
372 	case ACK:
373 		printf("<block=%d>\n", ntohs(tp->th_block));
374 		break;
375 
376 	case ERROR:
377 		printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg);
378 		break;
379 	}
380 }
381 
382 struct timeval tstart;
383 struct timeval tstop;
384 struct timezone zone;
385 
386 startclock() {
387 	gettimeofday(&tstart, &zone);
388 }
389 
390 stopclock() {
391 	gettimeofday(&tstop, &zone);
392 }
393 
394 printstats(direction, amount)
395 char *direction;
396 unsigned long amount;
397 {
398 	double delta;
399 			/* compute delta in 1/10's second units */
400 	delta = ((tstop.tv_sec*10.)+(tstop.tv_usec/100000)) -
401 		((tstart.tv_sec*10.)+(tstart.tv_usec/100000));
402 	delta = delta/10.;      /* back to seconds */
403 	printf("%s %d bytes in %.1f seconds", direction, amount, delta);
404 	if (verbose)
405 		printf(" [%.0f bits/sec]", (amount*8.)/delta);
406 	putchar('\n');
407 }
408 
409