xref: /freebsd/usr.bin/tftp/tftp.c (revision 0640d357f29fb1c0daaaffadd0416c5981413afd)
1 /*
2  * Copyright (c) 1983, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *	This product includes software developed by the University of
16  *	California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 #ifndef lint
35 #if 0
36 static char sccsid[] = "@(#)tftp.c	8.1 (Berkeley) 6/6/93";
37 #endif
38 static const char rcsid[] =
39 	"$Id: tftp.c,v 1.3 1998/02/20 04:30:34 jb Exp $";
40 #endif /* not lint */
41 
42 /* Many bug fixes are from Jim Guyton <guyton@rand-unix> */
43 
44 /*
45  * TFTP User Program -- Protocol Machines
46  */
47 #include <sys/types.h>
48 #include <sys/socket.h>
49 #include <sys/time.h>
50 
51 #include <netinet/in.h>
52 
53 #include <arpa/tftp.h>
54 
55 #include <err.h>
56 #include <errno.h>
57 #include <setjmp.h>
58 #include <signal.h>
59 #include <stdio.h>
60 #include <string.h>
61 #include <unistd.h>
62 
63 #include "extern.h"
64 #include "tftpsubs.h"
65 
66 extern  struct sockaddr_in peeraddr;	/* filled in by main */
67 extern  int     f;			/* the opened socket */
68 extern  int     trace;
69 extern  int     verbose;
70 extern  int     rexmtval;
71 extern  int     maxtimeout;
72 
73 #define PKTSIZE    SEGSIZE+4
74 char    ackbuf[PKTSIZE];
75 int	timeout;
76 jmp_buf	toplevel;
77 jmp_buf	timeoutbuf;
78 
79 static void nak __P((int));
80 static int makerequest __P((int, const char *, struct tftphdr *, const char *));
81 static void printstats __P((const char *, unsigned long));
82 static void startclock __P((void));
83 static void stopclock __P((void));
84 static void timer __P((int));
85 static void tpacket __P((const char *, struct tftphdr *, int));
86 
87 /*
88  * Send the requested file.
89  */
90 void
91 xmitfile(fd, name, mode)
92 	int fd;
93 	char *name;
94 	char *mode;
95 {
96 	register struct tftphdr *ap;	   /* data and ack packets */
97 	struct tftphdr *r_init(), *dp;
98 	register int n;
99 	volatile int block, size, convert;
100 	volatile unsigned long amount;
101 	struct sockaddr_in from;
102 	int fromlen;
103 	FILE *file;
104 
105 	startclock();		/* start stat's clock */
106 	dp = r_init();		/* reset fillbuf/read-ahead code */
107 	ap = (struct tftphdr *)ackbuf;
108 	file = fdopen(fd, "r");
109 	convert = !strcmp(mode, "netascii");
110 	block = 0;
111 	amount = 0;
112 
113 	signal(SIGALRM, timer);
114 	do {
115 		if (block == 0)
116 			size = makerequest(WRQ, name, dp, mode) - 4;
117 		else {
118 		/*	size = read(fd, dp->th_data, SEGSIZE);	 */
119 			size = readit(file, &dp, convert);
120 			if (size < 0) {
121 				nak(errno + 100);
122 				break;
123 			}
124 			dp->th_opcode = htons((u_short)DATA);
125 			dp->th_block = htons((u_short)block);
126 		}
127 		timeout = 0;
128 		(void) setjmp(timeoutbuf);
129 send_data:
130 		if (trace)
131 			tpacket("sent", dp, size + 4);
132 		n = sendto(f, dp, size + 4, 0,
133 		    (struct sockaddr *)&peeraddr, sizeof(peeraddr));
134 		if (n != size + 4) {
135 			warn("sendto");
136 			goto abort;
137 		}
138 		read_ahead(file, convert);
139 		for ( ; ; ) {
140 			alarm(rexmtval);
141 			do {
142 				fromlen = sizeof(from);
143 				n = recvfrom(f, ackbuf, sizeof(ackbuf), 0,
144 				    (struct sockaddr *)&from, &fromlen);
145 			} while (n <= 0);
146 			alarm(0);
147 			if (n < 0) {
148 				warn("recvfrom");
149 				goto abort;
150 			}
151 			peeraddr.sin_port = from.sin_port;	/* added */
152 			if (trace)
153 				tpacket("received", ap, n);
154 			/* should verify packet came from server */
155 			ap->th_opcode = ntohs(ap->th_opcode);
156 			ap->th_block = ntohs(ap->th_block);
157 			if (ap->th_opcode == ERROR) {
158 				printf("Error code %d: %s\n", ap->th_code,
159 					ap->th_msg);
160 				goto abort;
161 			}
162 			if (ap->th_opcode == ACK) {
163 				int j;
164 
165 				if (ap->th_block == block) {
166 					break;
167 				}
168 				/* On an error, try to synchronize
169 				 * both sides.
170 				 */
171 				j = synchnet(f);
172 				if (j && trace) {
173 					printf("discarded %d packets\n",
174 							j);
175 				}
176 				if (ap->th_block == (block-1)) {
177 					goto send_data;
178 				}
179 			}
180 		}
181 		if (block > 0)
182 			amount += size;
183 		block++;
184 	} while (size == SEGSIZE || block == 1);
185 abort:
186 	fclose(file);
187 	stopclock();
188 	if (amount > 0)
189 		printstats("Sent", amount);
190 }
191 
192 /*
193  * Receive a file.
194  */
195 void
196 recvfile(fd, name, mode)
197 	int fd;
198 	char *name;
199 	char *mode;
200 {
201 	register struct tftphdr *ap;
202 	struct tftphdr *dp, *w_init();
203 	register int n;
204 	volatile int block, size, firsttrip;
205 	volatile unsigned long amount;
206 	struct sockaddr_in from;
207 	int fromlen;
208 	FILE *file;
209 	volatile int convert;		/* true if converting crlf -> lf */
210 
211 	startclock();
212 	dp = w_init();
213 	ap = (struct tftphdr *)ackbuf;
214 	file = fdopen(fd, "w");
215 	convert = !strcmp(mode, "netascii");
216 	block = 1;
217 	firsttrip = 1;
218 	amount = 0;
219 
220 	signal(SIGALRM, timer);
221 	do {
222 		if (firsttrip) {
223 			size = makerequest(RRQ, name, ap, mode);
224 			firsttrip = 0;
225 		} else {
226 			ap->th_opcode = htons((u_short)ACK);
227 			ap->th_block = htons((u_short)(block));
228 			size = 4;
229 			block++;
230 		}
231 		timeout = 0;
232 		(void) setjmp(timeoutbuf);
233 send_ack:
234 		if (trace)
235 			tpacket("sent", ap, size);
236 		if (sendto(f, ackbuf, size, 0, (struct sockaddr *)&peeraddr,
237 		    sizeof(peeraddr)) != size) {
238 			alarm(0);
239 			warn("sendto");
240 			goto abort;
241 		}
242 		write_behind(file, convert);
243 		for ( ; ; ) {
244 			alarm(rexmtval);
245 			do  {
246 				fromlen = sizeof(from);
247 				n = recvfrom(f, dp, PKTSIZE, 0,
248 				    (struct sockaddr *)&from, &fromlen);
249 			} while (n <= 0);
250 			alarm(0);
251 			if (n < 0) {
252 				warn("recvfrom");
253 				goto abort;
254 			}
255 			peeraddr.sin_port = from.sin_port;	/* added */
256 			if (trace)
257 				tpacket("received", dp, n);
258 			/* should verify client address */
259 			dp->th_opcode = ntohs(dp->th_opcode);
260 			dp->th_block = ntohs(dp->th_block);
261 			if (dp->th_opcode == ERROR) {
262 				printf("Error code %d: %s\n", dp->th_code,
263 					dp->th_msg);
264 				goto abort;
265 			}
266 			if (dp->th_opcode == DATA) {
267 				int j;
268 
269 				if (dp->th_block == block) {
270 					break;		/* have next packet */
271 				}
272 				/* On an error, try to synchronize
273 				 * both sides.
274 				 */
275 				j = synchnet(f);
276 				if (j && trace) {
277 					printf("discarded %d packets\n", j);
278 				}
279 				if (dp->th_block == (block-1)) {
280 					goto send_ack;	/* resend ack */
281 				}
282 			}
283 		}
284 	/*	size = write(fd, dp->th_data, n - 4); */
285 		size = writeit(file, &dp, n - 4, convert);
286 		if (size < 0) {
287 			nak(errno + 100);
288 			break;
289 		}
290 		amount += size;
291 	} while (size == SEGSIZE);
292 abort:						/* ok to ack, since user */
293 	ap->th_opcode = htons((u_short)ACK);	/* has seen err msg */
294 	ap->th_block = htons((u_short)block);
295 	(void) sendto(f, ackbuf, 4, 0, (struct sockaddr *)&peeraddr,
296 	    sizeof(peeraddr));
297 	write_behind(file, convert);		/* flush last buffer */
298 	fclose(file);
299 	stopclock();
300 	if (amount > 0)
301 		printstats("Received", amount);
302 }
303 
304 static int
305 makerequest(request, name, tp, mode)
306 	int request;
307 	const char *name;
308 	struct tftphdr *tp;
309 	const char *mode;
310 {
311 	register char *cp;
312 
313 	tp->th_opcode = htons((u_short)request);
314 	cp = tp->th_stuff;
315 	strcpy(cp, name);
316 	cp += strlen(name);
317 	*cp++ = '\0';
318 	strcpy(cp, mode);
319 	cp += strlen(mode);
320 	*cp++ = '\0';
321 	return (cp - (char *)tp);
322 }
323 
324 struct errmsg {
325 	int	e_code;
326 	char	*e_msg;
327 } errmsgs[] = {
328 	{ EUNDEF,	"Undefined error code" },
329 	{ ENOTFOUND,	"File not found" },
330 	{ EACCESS,	"Access violation" },
331 	{ ENOSPACE,	"Disk full or allocation exceeded" },
332 	{ EBADOP,	"Illegal TFTP operation" },
333 	{ EBADID,	"Unknown transfer ID" },
334 	{ EEXISTS,	"File already exists" },
335 	{ ENOUSER,	"No such user" },
336 	{ -1,		0 }
337 };
338 
339 /*
340  * Send a nak packet (error message).
341  * Error code passed in is one of the
342  * standard TFTP codes, or a UNIX errno
343  * offset by 100.
344  */
345 static void
346 nak(error)
347 	int error;
348 {
349 	register struct errmsg *pe;
350 	register struct tftphdr *tp;
351 	int length;
352 	char *strerror();
353 
354 	tp = (struct tftphdr *)ackbuf;
355 	tp->th_opcode = htons((u_short)ERROR);
356 	tp->th_code = htons((u_short)error);
357 	for (pe = errmsgs; pe->e_code >= 0; pe++)
358 		if (pe->e_code == error)
359 			break;
360 	if (pe->e_code < 0) {
361 		pe->e_msg = strerror(error - 100);
362 		tp->th_code = EUNDEF;
363 	}
364 	strcpy(tp->th_msg, pe->e_msg);
365 	length = strlen(pe->e_msg) + 4;
366 	if (trace)
367 		tpacket("sent", tp, length);
368 	if (sendto(f, ackbuf, length, 0, (struct sockaddr *)&peeraddr,
369 	    sizeof(peeraddr)) != length)
370 		warn("nak");
371 }
372 
373 static void
374 tpacket(s, tp, n)
375 	const char *s;
376 	struct tftphdr *tp;
377 	int n;
378 {
379 	static char *opcodes[] =
380 	   { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR" };
381 	register char *cp, *file;
382 	u_short op = ntohs(tp->th_opcode);
383 	char *index();
384 
385 	if (op < RRQ || op > ERROR)
386 		printf("%s opcode=%x ", s, op);
387 	else
388 		printf("%s %s ", s, opcodes[op]);
389 	switch (op) {
390 
391 	case RRQ:
392 	case WRQ:
393 		n -= 2;
394 		file = cp = tp->th_stuff;
395 		cp = index(cp, '\0');
396 		printf("<file=%s, mode=%s>\n", file, cp + 1);
397 		break;
398 
399 	case DATA:
400 		printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), n - 4);
401 		break;
402 
403 	case ACK:
404 		printf("<block=%d>\n", ntohs(tp->th_block));
405 		break;
406 
407 	case ERROR:
408 		printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg);
409 		break;
410 	}
411 }
412 
413 struct timeval tstart;
414 struct timeval tstop;
415 
416 static void
417 startclock()
418 {
419 
420 	(void)gettimeofday(&tstart, NULL);
421 }
422 
423 static void
424 stopclock()
425 {
426 
427 	(void)gettimeofday(&tstop, NULL);
428 }
429 
430 static void
431 printstats(direction, amount)
432 	const char *direction;
433 	unsigned long amount;
434 {
435 	double delta;
436 			/* compute delta in 1/10's second units */
437 	delta = ((tstop.tv_sec*10.)+(tstop.tv_usec/100000)) -
438 		((tstart.tv_sec*10.)+(tstart.tv_usec/100000));
439 	delta = delta/10.;      /* back to seconds */
440 	printf("%s %d bytes in %.1f seconds", direction, amount, delta);
441 	if (verbose)
442 		printf(" [%.0f bits/sec]", (amount*8.)/delta);
443 	putchar('\n');
444 }
445 
446 static void
447 timer(sig)
448 	int sig;
449 {
450 
451 	timeout += rexmtval;
452 	if (timeout >= maxtimeout) {
453 		printf("Transfer timed out.\n");
454 		longjmp(toplevel, -1);
455 	}
456 	longjmp(timeoutbuf, 1);
457 }
458