tftp.c revision 209550
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 209550 2010-06-27 14:11:03Z gavin $");
42
43/* Many bug fixes are from Jim Guyton <guyton@rand-unix> */
44
45/*
46 * TFTP User Program -- Protocol Machines
47 */
48#include <sys/socket.h>
49#include <sys/stat.h>
50
51#include <netinet/in.h>
52
53#include <arpa/tftp.h>
54
55#include <err.h>
56#include <netdb.h>
57#include <stdio.h>
58#include <stdlib.h>
59#include <string.h>
60#include <syslog.h>
61
62#include "tftp.h"
63#include "tftp-file.h"
64#include "tftp-utils.h"
65#include "tftp-io.h"
66#include "tftp-transfer.h"
67#include "tftp-options.h"
68
69/*
70 * Send the requested file.
71 */
72void
73xmitfile(int peer, char *port, int fd, char *name, char *mode)
74{
75	struct tftphdr *rp;
76	int n, i;
77	uint16_t block;
78	uint32_t amount;
79	struct sockaddr_storage serv;	/* valid server port number */
80	char recvbuffer[MAXPKTSIZE];
81	struct tftp_stats tftp_stats;
82
83	stats_init(&tftp_stats);
84
85	memset(&serv, 0, sizeof(serv));
86	rp = (struct tftphdr *)recvbuffer;
87
88	if (port == NULL) {
89		struct servent *se;
90		se = getservbyname("tftp", "udp");
91		((struct sockaddr_in *)&peer_sock)->sin_port = se->s_port;
92	} else
93		((struct sockaddr_in *)&peer_sock)->sin_port =
94		    htons(atoi(port));
95
96	for (i = 0; i < 12; i++) {
97		struct sockaddr_storage from;
98
99		/* Tell the other side what we want to do */
100		if (debug&DEBUG_SIMPLE)
101			printf("Sending %s\n", name);
102
103		n = send_wrq(peer, name, mode);
104		if (n > 0) {
105			printf("Cannot send WRQ packet\n");
106			return;
107		}
108
109		/*
110		 * The first packet we receive has the new destination port
111		 * we have to send the next packets to.
112		 */
113		n = receive_packet(peer, recvbuffer,
114		    MAXPKTSIZE, &from, timeoutpacket);
115
116		/* We got some data! */
117		if (n >= 0) {
118			((struct sockaddr_in *)&peer_sock)->sin_port =
119			    ((struct sockaddr_in *)&from)->sin_port;
120			break;
121		}
122
123		/* This should be retried */
124		if (n == RP_TIMEOUT) {
125			printf("Try %d, didn't receive answer from remote.\n",
126			    i + 1);
127			continue;
128		}
129
130		/* Everything else is fatal */
131		break;
132	}
133	if (i == 12) {
134		printf("Transfer timed out.\n");
135		return;
136	}
137	if (rp->th_opcode == ERROR) {
138		printf("Got ERROR, aborted\n");
139		return;
140	}
141
142	/*
143	 * If the first packet is an OACK instead of an ACK packet,
144	 * handle it different.
145	 */
146	if (rp->th_opcode == OACK) {
147		if (!options_rfc_enabled) {
148			printf("Got OACK while options are not enabled!\n");
149			send_error(peer, EBADOP);
150			return;
151		}
152
153		parse_options(peer, rp->th_stuff, n + 2);
154	}
155
156	if (read_init(fd, NULL, mode) < 0) {
157		warn("read_init()");
158		return;
159	}
160
161	block = 1;
162	tftp_send(peer, &block, &tftp_stats);
163
164	read_close();
165	if (amount > 0)
166		printstats("Sent", verbose, &tftp_stats);
167
168	txrx_error = 1;
169}
170
171/*
172 * Receive a file.
173 */
174void
175recvfile(int peer, char *port, int fd, char *name, char *mode)
176{
177	struct tftphdr *rp;
178	uint16_t block;
179	char recvbuffer[MAXPKTSIZE];
180	int n, i;
181	struct tftp_stats tftp_stats;
182
183	stats_init(&tftp_stats);
184
185	rp = (struct tftphdr *)recvbuffer;
186
187	if (port == NULL) {
188		struct servent *se;
189		se = getservbyname("tftp", "udp");
190		((struct sockaddr_in *)&peer_sock)->sin_port = se->s_port;
191	} else
192		((struct sockaddr_in *)&peer_sock)->sin_port =
193		    htons(atoi(port));
194
195	for (i = 0; i < 12; i++) {
196		struct sockaddr_storage from;
197
198		/* Tell the other side what we want to do */
199		if (debug&DEBUG_SIMPLE)
200			printf("Requesting %s\n", name);
201
202		n = send_rrq(peer, name, mode);
203		if (n > 0) {
204			printf("Cannot send RRQ packet\n");
205			return;
206		}
207
208		/*
209		 * The first packet we receive has the new destination port
210		 * we have to send the next packets to.
211		 */
212		n = receive_packet(peer, recvbuffer,
213		    MAXPKTSIZE, &from, timeoutpacket);
214
215		/* We got something useful! */
216		if (n >= 0) {
217			((struct sockaddr_in *)&peer_sock)->sin_port =
218			    ((struct sockaddr_in *)&from)->sin_port;
219			break;
220		}
221
222		/* We should retry if this happens */
223		if (n == RP_TIMEOUT) {
224			printf("Try %d, didn't receive answer from remote.\n",
225			    i + 1);
226			continue;
227		}
228
229		/* Otherwise it is a fatal error */
230		break;
231	}
232	if (i == 12) {
233		printf("Transfer timed out.\n");
234		return;
235	}
236	if (rp->th_opcode == ERROR) {
237		tftp_log(LOG_ERR, "Error code %d: %s", rp->th_code, rp->th_msg);
238		return;
239	}
240
241	if (write_init(fd, NULL, mode) < 0) {
242		warn("write_init");
243		return;
244	}
245
246	stats_init(&tftp_stats);
247
248	/*
249	 * If the first packet is an OACK packet instead of an DATA packet,
250	 * handle it different.
251	 */
252	if (rp->th_opcode == OACK) {
253		if (!options_rfc_enabled) {
254			printf("Got OACK while options are not enabled!\n");
255			send_error(peer, EBADOP);
256			return;
257		}
258
259		parse_options(peer, rp->th_stuff, n + 2);
260
261		n = send_ack(peer, 0);
262		if (n > 0) {
263			printf("Cannot send ACK on OACK.\n");
264			return;
265		}
266		block = 0;
267		tftp_receive(peer, &block, &tftp_stats, NULL, 0);
268	} else {
269		block = 1;
270		tftp_receive(peer, &block, &tftp_stats, rp, n);
271	}
272
273	write_close();
274	if (tftp_stats.amount > 0)
275		printstats("Received", verbose, &tftp_stats);
276	return;
277}
278