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: stable/11/libexec/tftpd/tftp-transfer.c 339049 2018-10-01 15:43:56Z asomers $");
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				/*
133				 * "rollover" option not specified in
134				 * tftp client.  Default to rolling block
135				 * counter to 0.
136				 */
137				*block = 0;
138			} else {
139				*block = atoi(options[OPT_ROLLOVER].o_request);
140			}
141
142			ts->rollovers++;
143		}
144		gettimeofday(&(ts->tstop), NULL);
145	} while (size == segsize);
146abort:
147	return;
148}
149
150/*
151 * Receive a file via the TFTP data session.
152 *
153 * - It could be that the first block has already arrived while
154 *   trying to figure out if we were receiving options or not. In
155 *   that case it is passed to this function.
156 */
157void
158tftp_receive(int peer, uint16_t *block, struct tftp_stats *ts,
159    struct tftphdr *firstblock, size_t fb_size)
160{
161	struct tftphdr *rp;
162	uint16_t oldblock;
163	int n_data, n_ack, writesize, i, retry;
164	char recvbuffer[MAXPKTSIZE];
165
166	ts->amount = 0;
167
168	if (firstblock != NULL) {
169		writesize = write_file(firstblock->th_data, fb_size);
170		ts->amount += writesize;
171		for (i = 0; ; i++) {
172			n_ack = send_ack(peer, *block);
173			if (n_ack > 0) {
174				if (i == maxtimeouts) {
175					tftp_log(LOG_ERR,
176					    "Cannot send ACK packet #%d, "
177					    "giving up", *block);
178					return;
179				}
180				tftp_log(LOG_ERR,
181				    "Cannot send ACK packet #%d, trying again",
182				    *block);
183				continue;
184			}
185
186			break;
187		}
188
189		if (fb_size != segsize) {
190			gettimeofday(&(ts->tstop), NULL);
191			return;
192		}
193	}
194
195	rp = (struct tftphdr *)recvbuffer;
196	do {
197		oldblock = *block;
198		(*block)++;
199		if (oldblock > *block) {
200			if (options[OPT_ROLLOVER].o_request == NULL) {
201				/*
202				 * "rollover" option not specified in
203				 * tftp client.  Default to rolling block
204				 * counter to 0.
205				 */
206				*block = 0;
207			} else {
208				*block = atoi(options[OPT_ROLLOVER].o_request);
209			}
210
211			ts->rollovers++;
212		}
213
214		for (retry = 0; ; retry++) {
215			if (debug&DEBUG_SIMPLE)
216				tftp_log(LOG_DEBUG,
217				    "Receiving DATA block %d", *block);
218
219			n_data = receive_packet(peer, recvbuffer,
220			    MAXPKTSIZE, NULL, timeoutpacket);
221			if (n_data < 0) {
222				if (retry == maxtimeouts) {
223					tftp_log(LOG_ERR,
224					    "Timeout #%d on DATA block %d, "
225					    "giving up", retry, *block);
226					return;
227				}
228				if (n_data == RP_TIMEOUT) {
229					tftp_log(LOG_WARNING,
230					    "Timeout #%d on DATA block %d",
231					    retry, *block);
232					send_ack(peer, oldblock);
233					continue;
234				}
235
236				/* Either read failure or ERROR packet */
237				if (debug&DEBUG_SIMPLE)
238					tftp_log(LOG_DEBUG, "Aborting: %s",
239					    rp_strerror(n_data));
240				goto abort;
241			}
242			if (rp->th_opcode == DATA) {
243				ts->blocks++;
244
245				if (rp->th_block == *block)
246					break;
247
248				tftp_log(LOG_WARNING,
249				    "Expected DATA block %d, got block %d",
250				    *block, rp->th_block);
251
252				/* Re-synchronize with the other side */
253				(void) synchnet(peer);
254				if (rp->th_block == (*block-1)) {
255					tftp_log(LOG_INFO, "Trying to sync");
256					*block = oldblock;
257					ts->retries++;
258					goto send_ack;	/* rexmit */
259				}
260
261			} else {
262				tftp_log(LOG_WARNING,
263				    "Expected DATA block, got %s block",
264				    packettype(rp->th_opcode));
265			}
266		}
267
268		if (n_data > 0) {
269			writesize = write_file(rp->th_data, n_data);
270			ts->amount += writesize;
271			if (writesize <= 0) {
272				tftp_log(LOG_ERR,
273				    "write_file returned %d", writesize);
274				if (writesize < 0)
275					send_error(peer, errno + 100);
276				else
277					send_error(peer, ENOSPACE);
278				goto abort;
279			}
280			if (n_data != segsize)
281				write_close();
282		}
283
284send_ack:
285		for (i = 0; ; i++) {
286			n_ack = send_ack(peer, *block);
287			if (n_ack > 0) {
288
289				if (i == maxtimeouts) {
290					tftp_log(LOG_ERR,
291					    "Cannot send ACK packet #%d, "
292					    "giving up", *block);
293					return;
294				}
295
296				tftp_log(LOG_ERR,
297				    "Cannot send ACK packet #%d, trying again",
298				    *block);
299				continue;
300			}
301
302			break;
303		}
304		gettimeofday(&(ts->tstop), NULL);
305	} while (n_data == segsize);
306
307	/* Don't do late packet management for the client implementation */
308	if (acting_as_client)
309		return;
310
311	for (i = 0; ; i++) {
312		n_data = receive_packet(peer, (char *)rp, pktsize,
313		    NULL, timeoutpacket);
314		if (n_data <= 0)
315			break;
316		if (n_data > 0 &&
317		    rp->th_opcode == DATA &&	/* and got a data block */
318		    *block == rp->th_block)	/* then my last ack was lost */
319			send_ack(peer, *block);	/* resend final ack */
320	}
321
322abort:
323	return;
324}
325