tftp.c revision 93428
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#if 0
35#ifndef lint
36static char sccsid[] = "@(#)tftp.c	8.1 (Berkeley) 6/6/93";
37#endif /* not lint */
38#endif
39
40#include <sys/cdefs.h>
41__FBSDID("$FreeBSD: head/usr.bin/tftp/tftp.c 93428 2002-03-30 14:18:15Z dwmalone $");
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/inet.h>
55#include <arpa/tftp.h>
56
57#include <err.h>
58#include <errno.h>
59#include <setjmp.h>
60#include <signal.h>
61#include <stdio.h>
62#include <string.h>
63#include <unistd.h>
64
65#include "extern.h"
66#include "tftpsubs.h"
67
68extern  struct sockaddr_in peeraddr;	/* filled in by main */
69extern  int     f;			/* the opened socket */
70extern  int     trace;
71extern  int     verbose;
72extern  int     rexmtval;
73extern  int     maxtimeout;
74
75#define PKTSIZE    SEGSIZE+4
76char    ackbuf[PKTSIZE];
77int	timeout;
78jmp_buf	toplevel;
79jmp_buf	timeoutbuf;
80
81static void nak(int);
82static int makerequest(int, const char *, struct tftphdr *, const char *);
83static void printstats(const char *, unsigned long);
84static void startclock(void);
85static void stopclock(void);
86static void timer(int);
87static void tpacket(const char *, struct tftphdr *, int);
88
89/*
90 * Send the requested file.
91 */
92void
93xmitfile(fd, name, mode)
94	int fd;
95	char *name;
96	char *mode;
97{
98	struct tftphdr *ap;	   /* data and ack packets */
99	struct tftphdr *dp;
100	int n;
101	volatile unsigned short block;
102	volatile int 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);
132send_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			warn("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				warn("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);
188abort:
189	fclose(file);
190	stopclock();
191	if (amount > 0)
192		printstats("Sent", amount);
193}
194
195/*
196 * Receive a file.
197 */
198void
199recvfile(fd, name, mode)
200	int fd;
201	char *name;
202	char *mode;
203{
204	struct tftphdr *ap;
205	struct tftphdr *dp;
206	int n;
207	volatile unsigned short block;
208	volatile int size, firsttrip;
209	volatile unsigned long amount;
210	struct sockaddr_in from;
211	int fromlen;
212	FILE *file;
213	volatile int convert;		/* true if converting crlf -> lf */
214
215	startclock();
216	dp = w_init();
217	ap = (struct tftphdr *)ackbuf;
218	file = fdopen(fd, "w");
219	convert = !strcmp(mode, "netascii");
220	block = 1;
221	firsttrip = 1;
222	amount = 0;
223
224	signal(SIGALRM, timer);
225	do {
226		if (firsttrip) {
227			size = makerequest(RRQ, name, ap, mode);
228			firsttrip = 0;
229		} else {
230			ap->th_opcode = htons((u_short)ACK);
231			ap->th_block = htons((u_short)(block));
232			size = 4;
233			block++;
234		}
235		timeout = 0;
236		(void) setjmp(timeoutbuf);
237send_ack:
238		if (trace)
239			tpacket("sent", ap, size);
240		if (sendto(f, ackbuf, size, 0, (struct sockaddr *)&peeraddr,
241		    sizeof(peeraddr)) != size) {
242			alarm(0);
243			warn("sendto");
244			goto abort;
245		}
246		write_behind(file, convert);
247		for ( ; ; ) {
248			alarm(rexmtval);
249			do  {
250				fromlen = sizeof(from);
251				n = recvfrom(f, dp, PKTSIZE, 0,
252				    (struct sockaddr *)&from, &fromlen);
253			} while (n <= 0);
254			alarm(0);
255			if (n < 0) {
256				warn("recvfrom");
257				goto abort;
258			}
259			peeraddr.sin_port = from.sin_port;	/* added */
260			if (trace)
261				tpacket("received", dp, n);
262			/* should verify client address */
263			dp->th_opcode = ntohs(dp->th_opcode);
264			dp->th_block = ntohs(dp->th_block);
265			if (dp->th_opcode == ERROR) {
266				printf("Error code %d: %s\n", dp->th_code,
267					dp->th_msg);
268				goto abort;
269			}
270			if (dp->th_opcode == DATA) {
271				int j;
272
273				if (dp->th_block == block) {
274					break;		/* have next packet */
275				}
276				/* On an error, try to synchronize
277				 * both sides.
278				 */
279				j = synchnet(f);
280				if (j && trace) {
281					printf("discarded %d packets\n", j);
282				}
283				if (dp->th_block == (block-1)) {
284					goto send_ack;	/* resend ack */
285				}
286			}
287		}
288	/*	size = write(fd, dp->th_data, n - 4); */
289		size = writeit(file, &dp, n - 4, convert);
290		if (size < 0) {
291			nak(errno + 100);
292			break;
293		}
294		amount += size;
295	} while (size == SEGSIZE);
296abort:						/* ok to ack, since user */
297	ap->th_opcode = htons((u_short)ACK);	/* has seen err msg */
298	ap->th_block = htons((u_short)block);
299	(void) sendto(f, ackbuf, 4, 0, (struct sockaddr *)&peeraddr,
300	    sizeof(peeraddr));
301	write_behind(file, convert);		/* flush last buffer */
302	fclose(file);
303	stopclock();
304	if (amount > 0)
305		printstats("Received", amount);
306}
307
308static int
309makerequest(request, name, tp, mode)
310	int request;
311	const char *name;
312	struct tftphdr *tp;
313	const char *mode;
314{
315	char *cp;
316
317	tp->th_opcode = htons((u_short)request);
318	cp = tp->th_stuff;
319	strcpy(cp, name);
320	cp += strlen(name);
321	*cp++ = '\0';
322	strcpy(cp, mode);
323	cp += strlen(mode);
324	*cp++ = '\0';
325	return (cp - (char *)tp);
326}
327
328struct errmsg {
329	int	e_code;
330	const char	*e_msg;
331} errmsgs[] = {
332	{ EUNDEF,	"Undefined error code" },
333	{ ENOTFOUND,	"File not found" },
334	{ EACCESS,	"Access violation" },
335	{ ENOSPACE,	"Disk full or allocation exceeded" },
336	{ EBADOP,	"Illegal TFTP operation" },
337	{ EBADID,	"Unknown transfer ID" },
338	{ EEXISTS,	"File already exists" },
339	{ ENOUSER,	"No such user" },
340	{ -1,		0 }
341};
342
343/*
344 * Send a nak packet (error message).
345 * Error code passed in is one of the
346 * standard TFTP codes, or a UNIX errno
347 * offset by 100.
348 */
349static void
350nak(error)
351	int error;
352{
353	struct errmsg *pe;
354	struct tftphdr *tp;
355	int length;
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		warn("nak");
374}
375
376static void
377tpacket(s, tp, n)
378	const char *s;
379	struct tftphdr *tp;
380	int n;
381{
382	static const char *opcodes[] =
383	   { "#0", "RRQ", "WRQ", "DATA", "ACK", "ERROR" };
384	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 = index(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
415struct timeval tstart;
416struct timeval tstop;
417
418static void
419startclock()
420{
421
422	(void)gettimeofday(&tstart, NULL);
423}
424
425static void
426stopclock()
427{
428
429	(void)gettimeofday(&tstop, NULL);
430}
431
432static void
433printstats(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 %ld bytes in %.1f seconds", direction, amount, delta);
443	if (verbose)
444		printf(" [%.0f bits/sec]", (amount*8.)/delta);
445	putchar('\n');
446}
447
448static void
449timer(sig)
450	int sig __unused;
451{
452
453	timeout += rexmtval;
454	if (timeout >= maxtimeout) {
455		printf("Transfer timed out.\n");
456		longjmp(toplevel, -1);
457	}
458	longjmp(timeoutbuf, 1);
459}
460