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