xref: /freebsd/usr.bin/tftp/tftp.c (revision 0de89efe5c443f213c7ea28773ef2dc6cf3af2ed)
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$";
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 <unistd.h>
61 
62 #include "extern.h"
63 #include "tftpsubs.h"
64 
65 extern  struct sockaddr_in peeraddr;	/* filled in by main */
66 extern  int     f;			/* the opened socket */
67 extern  int     trace;
68 extern  int     verbose;
69 extern  int     rexmtval;
70 extern  int     maxtimeout;
71 
72 #define PKTSIZE    SEGSIZE+4
73 char    ackbuf[PKTSIZE];
74 int	timeout;
75 jmp_buf	toplevel;
76 jmp_buf	timeoutbuf;
77 
78 static void nak __P((int));
79 static int makerequest __P((int, const char *, struct tftphdr *, const char *));
80 static void printstats __P((const char *, unsigned long));
81 static void startclock __P((void));
82 static void stopclock __P((void));
83 static void timer __P((int));
84 static void tpacket __P((const char *, struct tftphdr *, int));
85 
86 /*
87  * Send the requested file.
88  */
89 void
90 sendfile(fd, name, mode)
91 	int fd;
92 	char *name;
93 	char *mode;
94 {
95 	register struct tftphdr *ap;	   /* data and ack packets */
96 	struct tftphdr *r_init(), *dp;
97 	register int n;
98 	volatile int block, size, convert;
99 	volatile unsigned long amount;
100 	struct sockaddr_in from;
101 	int fromlen;
102 	FILE *file;
103 
104 	startclock();		/* start stat's clock */
105 	dp = r_init();		/* reset fillbuf/read-ahead code */
106 	ap = (struct tftphdr *)ackbuf;
107 	file = fdopen(fd, "r");
108 	convert = !strcmp(mode, "netascii");
109 	block = 0;
110 	amount = 0;
111 
112 	signal(SIGALRM, timer);
113 	do {
114 		if (block == 0)
115 			size = makerequest(WRQ, name, dp, mode) - 4;
116 		else {
117 		/*	size = read(fd, dp->th_data, SEGSIZE);	 */
118 			size = readit(file, &dp, convert);
119 			if (size < 0) {
120 				nak(errno + 100);
121 				break;
122 			}
123 			dp->th_opcode = htons((u_short)DATA);
124 			dp->th_block = htons((u_short)block);
125 		}
126 		timeout = 0;
127 		(void) setjmp(timeoutbuf);
128 send_data:
129 		if (trace)
130 			tpacket("sent", dp, size + 4);
131 		n = sendto(f, dp, size + 4, 0,
132 		    (struct sockaddr *)&peeraddr, sizeof(peeraddr));
133 		if (n != size + 4) {
134 			warn("sendto");
135 			goto abort;
136 		}
137 		read_ahead(file, convert);
138 		for ( ; ; ) {
139 			alarm(rexmtval);
140 			do {
141 				fromlen = sizeof(from);
142 				n = recvfrom(f, ackbuf, sizeof(ackbuf), 0,
143 				    (struct sockaddr *)&from, &fromlen);
144 			} while (n <= 0);
145 			alarm(0);
146 			if (n < 0) {
147 				warn("recvfrom");
148 				goto abort;
149 			}
150 			peeraddr.sin_port = from.sin_port;	/* added */
151 			if (trace)
152 				tpacket("received", ap, n);
153 			/* should verify packet came from server */
154 			ap->th_opcode = ntohs(ap->th_opcode);
155 			ap->th_block = ntohs(ap->th_block);
156 			if (ap->th_opcode == ERROR) {
157 				printf("Error code %d: %s\n", ap->th_code,
158 					ap->th_msg);
159 				goto abort;
160 			}
161 			if (ap->th_opcode == ACK) {
162 				int j;
163 
164 				if (ap->th_block == block) {
165 					break;
166 				}
167 				/* On an error, try to synchronize
168 				 * both sides.
169 				 */
170 				j = synchnet(f);
171 				if (j && trace) {
172 					printf("discarded %d packets\n",
173 							j);
174 				}
175 				if (ap->th_block == (block-1)) {
176 					goto send_data;
177 				}
178 			}
179 		}
180 		if (block > 0)
181 			amount += size;
182 		block++;
183 	} while (size == SEGSIZE || block == 1);
184 abort:
185 	fclose(file);
186 	stopclock();
187 	if (amount > 0)
188 		printstats("Sent", amount);
189 }
190 
191 /*
192  * Receive a file.
193  */
194 void
195 recvfile(fd, name, mode)
196 	int fd;
197 	char *name;
198 	char *mode;
199 {
200 	register struct tftphdr *ap;
201 	struct tftphdr *dp, *w_init();
202 	register int n;
203 	volatile int block, size, firsttrip;
204 	volatile unsigned long amount;
205 	struct sockaddr_in from;
206 	int fromlen;
207 	FILE *file;
208 	volatile int convert;		/* true if converting crlf -> lf */
209 
210 	startclock();
211 	dp = w_init();
212 	ap = (struct tftphdr *)ackbuf;
213 	file = fdopen(fd, "w");
214 	convert = !strcmp(mode, "netascii");
215 	block = 1;
216 	firsttrip = 1;
217 	amount = 0;
218 
219 	signal(SIGALRM, timer);
220 	do {
221 		if (firsttrip) {
222 			size = makerequest(RRQ, name, ap, mode);
223 			firsttrip = 0;
224 		} else {
225 			ap->th_opcode = htons((u_short)ACK);
226 			ap->th_block = htons((u_short)(block));
227 			size = 4;
228 			block++;
229 		}
230 		timeout = 0;
231 		(void) setjmp(timeoutbuf);
232 send_ack:
233 		if (trace)
234 			tpacket("sent", ap, size);
235 		if (sendto(f, ackbuf, size, 0, (struct sockaddr *)&peeraddr,
236 		    sizeof(peeraddr)) != size) {
237 			alarm(0);
238 			warn("sendto");
239 			goto abort;
240 		}
241 		write_behind(file, convert);
242 		for ( ; ; ) {
243 			alarm(rexmtval);
244 			do  {
245 				fromlen = sizeof(from);
246 				n = recvfrom(f, dp, PKTSIZE, 0,
247 				    (struct sockaddr *)&from, &fromlen);
248 			} while (n <= 0);
249 			alarm(0);
250 			if (n < 0) {
251 				warn("recvfrom");
252 				goto abort;
253 			}
254 			peeraddr.sin_port = from.sin_port;	/* added */
255 			if (trace)
256 				tpacket("received", dp, n);
257 			/* should verify client address */
258 			dp->th_opcode = ntohs(dp->th_opcode);
259 			dp->th_block = ntohs(dp->th_block);
260 			if (dp->th_opcode == ERROR) {
261 				printf("Error code %d: %s\n", dp->th_code,
262 					dp->th_msg);
263 				goto abort;
264 			}
265 			if (dp->th_opcode == DATA) {
266 				int j;
267 
268 				if (dp->th_block == block) {
269 					break;		/* have next packet */
270 				}
271 				/* On an error, try to synchronize
272 				 * both sides.
273 				 */
274 				j = synchnet(f);
275 				if (j && trace) {
276 					printf("discarded %d packets\n", j);
277 				}
278 				if (dp->th_block == (block-1)) {
279 					goto send_ack;	/* resend ack */
280 				}
281 			}
282 		}
283 	/*	size = write(fd, dp->th_data, n - 4); */
284 		size = writeit(file, &dp, n - 4, convert);
285 		if (size < 0) {
286 			nak(errno + 100);
287 			break;
288 		}
289 		amount += size;
290 	} while (size == SEGSIZE);
291 abort:						/* ok to ack, since user */
292 	ap->th_opcode = htons((u_short)ACK);	/* has seen err msg */
293 	ap->th_block = htons((u_short)block);
294 	(void) sendto(f, ackbuf, 4, 0, (struct sockaddr *)&peeraddr,
295 	    sizeof(peeraddr));
296 	write_behind(file, convert);		/* flush last buffer */
297 	fclose(file);
298 	stopclock();
299 	if (amount > 0)
300 		printstats("Received", amount);
301 }
302 
303 static int
304 makerequest(request, name, tp, mode)
305 	int request;
306 	const char *name;
307 	struct tftphdr *tp;
308 	const char *mode;
309 {
310 	register char *cp;
311 
312 	tp->th_opcode = htons((u_short)request);
313 	cp = tp->th_stuff;
314 	strcpy(cp, name);
315 	cp += strlen(name);
316 	*cp++ = '\0';
317 	strcpy(cp, mode);
318 	cp += strlen(mode);
319 	*cp++ = '\0';
320 	return (cp - (char *)tp);
321 }
322 
323 struct errmsg {
324 	int	e_code;
325 	char	*e_msg;
326 } errmsgs[] = {
327 	{ EUNDEF,	"Undefined error code" },
328 	{ ENOTFOUND,	"File not found" },
329 	{ EACCESS,	"Access violation" },
330 	{ ENOSPACE,	"Disk full or allocation exceeded" },
331 	{ EBADOP,	"Illegal TFTP operation" },
332 	{ EBADID,	"Unknown transfer ID" },
333 	{ EEXISTS,	"File already exists" },
334 	{ ENOUSER,	"No such user" },
335 	{ -1,		0 }
336 };
337 
338 /*
339  * Send a nak packet (error message).
340  * Error code passed in is one of the
341  * standard TFTP codes, or a UNIX errno
342  * offset by 100.
343  */
344 static void
345 nak(error)
346 	int error;
347 {
348 	register struct errmsg *pe;
349 	register struct tftphdr *tp;
350 	int length;
351 	char *strerror();
352 
353 	tp = (struct tftphdr *)ackbuf;
354 	tp->th_opcode = htons((u_short)ERROR);
355 	tp->th_code = htons((u_short)error);
356 	for (pe = errmsgs; pe->e_code >= 0; pe++)
357 		if (pe->e_code == error)
358 			break;
359 	if (pe->e_code < 0) {
360 		pe->e_msg = strerror(error - 100);
361 		tp->th_code = EUNDEF;
362 	}
363 	strcpy(tp->th_msg, pe->e_msg);
364 	length = strlen(pe->e_msg) + 4;
365 	if (trace)
366 		tpacket("sent", tp, length);
367 	if (sendto(f, ackbuf, length, 0, (struct sockaddr *)&peeraddr,
368 	    sizeof(peeraddr)) != length)
369 		warn("nak");
370 }
371 
372 static void
373 tpacket(s, tp, n)
374 	const char *s;
375 	struct tftphdr *tp;
376 	int n;
377 {
378 	static char *opcodes[] =
379 	   { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR" };
380 	register char *cp, *file;
381 	u_short op = ntohs(tp->th_opcode);
382 	char *index();
383 
384 	if (op < RRQ || op > ERROR)
385 		printf("%s opcode=%x ", s, op);
386 	else
387 		printf("%s %s ", s, opcodes[op]);
388 	switch (op) {
389 
390 	case RRQ:
391 	case WRQ:
392 		n -= 2;
393 		file = cp = tp->th_stuff;
394 		cp = index(cp, '\0');
395 		printf("<file=%s, mode=%s>\n", file, cp + 1);
396 		break;
397 
398 	case DATA:
399 		printf("<block=%d, %d bytes>\n", ntohs(tp->th_block), n - 4);
400 		break;
401 
402 	case ACK:
403 		printf("<block=%d>\n", ntohs(tp->th_block));
404 		break;
405 
406 	case ERROR:
407 		printf("<code=%d, msg=%s>\n", ntohs(tp->th_code), tp->th_msg);
408 		break;
409 	}
410 }
411 
412 struct timeval tstart;
413 struct timeval tstop;
414 
415 static void
416 startclock()
417 {
418 
419 	(void)gettimeofday(&tstart, NULL);
420 }
421 
422 static void
423 stopclock()
424 {
425 
426 	(void)gettimeofday(&tstop, NULL);
427 }
428 
429 static void
430 printstats(direction, amount)
431 	const char *direction;
432 	unsigned long amount;
433 {
434 	double delta;
435 			/* compute delta in 1/10's second units */
436 	delta = ((tstop.tv_sec*10.)+(tstop.tv_usec/100000)) -
437 		((tstart.tv_sec*10.)+(tstart.tv_usec/100000));
438 	delta = delta/10.;      /* back to seconds */
439 	printf("%s %d bytes in %.1f seconds", direction, amount, delta);
440 	if (verbose)
441 		printf(" [%.0f bits/sec]", (amount*8.)/delta);
442 	putchar('\n');
443 }
444 
445 static void
446 timer(sig)
447 	int sig;
448 {
449 
450 	timeout += rexmtval;
451 	if (timeout >= maxtimeout) {
452 		printf("Transfer timed out.\n");
453 		longjmp(toplevel, -1);
454 	}
455 	longjmp(timeoutbuf, 1);
456 }
457