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