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