tftp-transfer.c revision 207614
1/*
2 * Copyright (C) 2008 Edwin Groothuis. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23 * SUCH DAMAGE.
24 */
25
26#include <sys/cdefs.h>
27__FBSDID("$FreeBSD: head/libexec/tftpd/tftp-transfer.c 207614 2010-05-04 13:07:40Z imp $");
28
29#include <sys/types.h>
30#include <sys/param.h>
31#include <sys/ioctl.h>
32#include <sys/stat.h>
33#include <sys/socket.h>
34
35#include <netinet/in.h>
36#include <arpa/tftp.h>
37
38#include <errno.h>
39#include <stdio.h>
40#include <stdlib.h>
41#include <syslog.h>
42
43#include "tftp-file.h"
44#include "tftp-io.h"
45#include "tftp-utils.h"
46#include "tftp-options.h"
47#include "tftp-transfer.h"
48
49/*
50 * Send a file via the TFTP data session.
51 */
52void
53tftp_send(int peer, uint16_t *block, struct tftp_stats *ts)
54{
55	struct tftphdr *rp;
56	int size, n_data, n_ack, try;
57	uint16_t oldblock;
58	char sendbuffer[MAXPKTSIZE];
59	char recvbuffer[MAXPKTSIZE];
60
61	rp = (struct tftphdr *)recvbuffer;
62	*block = 1;
63	ts->amount = 0;
64	do {
65		if (debug&DEBUG_SIMPLE)
66			tftp_log(LOG_DEBUG, "Sending block %d", *block);
67
68		size = read_file(sendbuffer, segsize);
69		if (size < 0) {
70			tftp_log(LOG_ERR, "read_file returned %d", size);
71			send_error(peer, errno + 100);
72			goto abort;
73		}
74
75		for (try = 0; ; try++) {
76			n_data = send_data(peer, *block, sendbuffer, size);
77			if (n_data > 0) {
78				if (try == maxtimeouts) {
79					tftp_log(LOG_ERR,
80					    "Cannot send DATA packet #%d, "
81					    "giving up", *block);
82					return;
83				}
84				tftp_log(LOG_ERR,
85				    "Cannot send DATA packet #%d, trying again",
86				    *block);
87				continue;
88			}
89
90			n_ack = receive_packet(peer, recvbuffer,
91			    MAXPKTSIZE, NULL, timeoutpacket);
92			if (n_ack < 0) {
93				if (n_ack == RP_TIMEOUT) {
94					if (try == maxtimeouts) {
95						tftp_log(LOG_ERR,
96						    "Timeout #%d send ACK %d "
97						    "giving up", try, *block);
98						return;
99					}
100					tftp_log(LOG_WARNING,
101					    "Timeout #%d on ACK %d",
102					    try, *block);
103					continue;
104				}
105
106				/* Either read failure or ERROR packet */
107				if (debug&DEBUG_SIMPLE)
108					tftp_log(LOG_ERR, "Aborting: %s",
109					    rp_strerror(n_ack));
110				goto abort;
111			}
112			if (rp->th_opcode == ACK) {
113				ts->blocks++;
114				if (rp->th_block == *block) {
115					ts->amount += size;
116					break;
117				}
118
119				/* Re-synchronize with the other side */
120				(void) synchnet(peer);
121				if (rp->th_block == (*block - 1)) {
122					ts->retries++;
123					continue;
124				}
125			}
126
127		}
128		oldblock = *block;
129		(*block)++;
130		if (oldblock > *block) {
131			if (options[OPT_ROLLOVER].o_request == NULL) {
132				tftp_log(LOG_ERR,
133				    "Block rollover but not allowed.");
134				send_error(peer, EBADOP);
135				gettimeofday(&(ts->tstop), NULL);
136				return;
137			}
138
139			*block = atoi(options[OPT_ROLLOVER].o_request);
140			ts->rollovers++;
141		}
142		gettimeofday(&(ts->tstop), NULL);
143	} while (size == segsize);
144abort:
145	return;
146}
147
148/*
149 * Receive a file via the TFTP data session.
150 *
151 * - It could be that the first block has already arrived while
152 *   trying to figure out if we were receiving options or not. In
153 *   that case it is passed to this function.
154 */
155void
156tftp_receive(int peer, uint16_t *block, struct tftp_stats *ts,
157    struct tftphdr *firstblock, size_t fb_size)
158{
159	struct tftphdr *rp;
160	uint16_t oldblock;
161	int n_data, n_ack, writesize, i, retry;
162	char recvbuffer[MAXPKTSIZE];
163
164	ts->amount = 0;
165
166	if (firstblock != NULL) {
167		writesize = write_file(firstblock->th_data, fb_size);
168		ts->amount += writesize;
169		for (i = 0; ; i++) {
170			n_ack = send_ack(peer, *block);
171			if (n_ack > 0) {
172				if (i == maxtimeouts) {
173					tftp_log(LOG_ERR,
174					    "Cannot send ACK packet #%d, "
175					    "giving up", *block);
176					return;
177				}
178				tftp_log(LOG_ERR,
179				    "Cannot send ACK packet #%d, trying again",
180				    *block);
181				continue;
182			}
183
184			break;
185		}
186
187		if (fb_size != segsize) {
188			gettimeofday(&(ts->tstop), NULL);
189			return;
190		}
191	}
192
193	rp = (struct tftphdr *)recvbuffer;
194	do {
195		oldblock = *block;
196		(*block)++;
197		if (oldblock > *block) {
198			if (options[OPT_ROLLOVER].o_request == NULL) {
199				tftp_log(LOG_ERR,
200				    "Block rollover but not allowed.");
201				send_error(peer, EBADOP);
202				gettimeofday(&(ts->tstop), NULL);
203				return;
204			}
205
206			*block = atoi(options[OPT_ROLLOVER].o_request);
207			ts->rollovers++;
208		}
209
210		for (retry = 0; ; retry++) {
211			if (debug&DEBUG_SIMPLE)
212				tftp_log(LOG_DEBUG,
213				    "Receiving DATA block %d", *block);
214
215			n_data = receive_packet(peer, recvbuffer,
216			    MAXPKTSIZE, NULL, timeoutpacket);
217			if (n_data < 0) {
218				if (retry == maxtimeouts) {
219					tftp_log(LOG_ERR,
220					    "Timeout #%d on DATA block %d, "
221					    "giving up", retry, *block);
222					return;
223				}
224				if (n_data == RP_TIMEOUT) {
225					tftp_log(LOG_WARNING,
226					    "Timeout #%d on DATA block %d",
227					    retry, *block);
228					send_ack(peer, oldblock);
229					continue;
230				}
231
232				/* Either read failure or ERROR packet */
233				if (debug&DEBUG_SIMPLE)
234					tftp_log(LOG_DEBUG, "Aborting: %s",
235					    rp_strerror(n_data));
236				goto abort;
237			}
238			if (rp->th_opcode == DATA) {
239				ts->blocks++;
240
241				if (rp->th_block == *block)
242					break;
243
244				tftp_log(LOG_WARNING,
245				    "Expected DATA block %d, got block %d",
246				    *block, rp->th_block);
247
248				/* Re-synchronize with the other side */
249				(void) synchnet(peer);
250				if (rp->th_block == (*block-1)) {
251					tftp_log(LOG_INFO, "Trying to sync");
252					*block = oldblock;
253					ts->retries++;
254					goto send_ack;	/* rexmit */
255				}
256
257			} else {
258				tftp_log(LOG_WARNING,
259				    "Expected DATA block, got %s block",
260				    packettype(rp->th_opcode));
261			}
262		}
263
264		if (n_data > 0) {
265			writesize = write_file(rp->th_data, n_data);
266			ts->amount += writesize;
267			if (writesize <= 0) {
268				tftp_log(LOG_ERR,
269				    "write_file returned %d", writesize);
270				if (writesize < 0)
271					send_error(peer, errno + 100);
272				else
273					send_error(peer, ENOSPACE);
274				goto abort;
275			}
276		}
277
278send_ack:
279		for (i = 0; ; i++) {
280			n_ack = send_ack(peer, *block);
281			if (n_ack > 0) {
282
283				if (i == maxtimeouts) {
284					tftp_log(LOG_ERR,
285					    "Cannot send ACK packet #%d, "
286					    "giving up", *block);
287					return;
288				}
289
290				tftp_log(LOG_ERR,
291				    "Cannot send ACK packet #%d, trying again",
292				    *block);
293				continue;
294			}
295
296			break;
297		}
298		gettimeofday(&(ts->tstop), NULL);
299	} while (n_data == segsize);
300
301	/* Don't do late packet management for the client implementation */
302	if (acting_as_client)
303		return;
304
305	for (i = 0; ; i++) {
306		n_data = receive_packet(peer, (char *)rp, pktsize,
307		    NULL, timeoutpacket);
308		if (n_data <= 0)
309			break;
310		if (n_data > 0 &&
311		    rp->th_opcode == DATA &&	/* and got a data block */
312		    *block == rp->th_block)	/* then my last ack was lost */
313			send_ack(peer, *block);	/* resend final ack */
314	}
315
316abort:
317	return;
318}
319