xref: /freebsd/libexec/tftpd/tftp-transfer.c (revision e3b4cb1b32c05ec668b16fe4e858e78b61fe5805)
1e6209940SPedro F. Giffuni /*-
2e6209940SPedro F. Giffuni  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3e6209940SPedro F. Giffuni  *
4e7ff5475SWarner Losh  * Copyright (C) 2008 Edwin Groothuis. All rights reserved.
5e7ff5475SWarner Losh  *
6e7ff5475SWarner Losh  * Redistribution and use in source and binary forms, with or without
7e7ff5475SWarner Losh  * modification, are permitted provided that the following conditions
8e7ff5475SWarner Losh  * are met:
9e7ff5475SWarner Losh  * 1. Redistributions of source code must retain the above copyright
10e7ff5475SWarner Losh  *    notice, this list of conditions and the following disclaimer.
11e7ff5475SWarner Losh  * 2. Redistributions in binary form must reproduce the above copyright
12e7ff5475SWarner Losh  *    notice, this list of conditions and the following disclaimer in the
13e7ff5475SWarner Losh  *    documentation and/or other materials provided with the distribution.
14e7ff5475SWarner Losh  *
15e7ff5475SWarner Losh  * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16e7ff5475SWarner Losh  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17e7ff5475SWarner Losh  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18e7ff5475SWarner Losh  * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
19e7ff5475SWarner Losh  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20e7ff5475SWarner Losh  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21e7ff5475SWarner Losh  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22e7ff5475SWarner Losh  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23e7ff5475SWarner Losh  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24e7ff5475SWarner Losh  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25e7ff5475SWarner Losh  * SUCH DAMAGE.
26e7ff5475SWarner Losh  */
27e7ff5475SWarner Losh 
28e7ff5475SWarner Losh #include <sys/cdefs.h>
29e7ff5475SWarner Losh __FBSDID("$FreeBSD$");
30e7ff5475SWarner Losh 
31e7ff5475SWarner Losh #include <sys/param.h>
32e7ff5475SWarner Losh #include <sys/ioctl.h>
33e7ff5475SWarner Losh #include <sys/socket.h>
34eb0292d9SDag-Erling Smørgrav #include <sys/stat.h>
35eb0292d9SDag-Erling Smørgrav #include <sys/time.h>
36e7ff5475SWarner Losh 
37e7ff5475SWarner Losh #include <netinet/in.h>
38e7ff5475SWarner Losh #include <arpa/tftp.h>
39e7ff5475SWarner Losh 
40e7ff5475SWarner Losh #include <errno.h>
41e7ff5475SWarner Losh #include <stdio.h>
42e7ff5475SWarner Losh #include <stdlib.h>
430c011985SJohn Baldwin #include <string.h>
44e7ff5475SWarner Losh #include <syslog.h>
45e7ff5475SWarner Losh 
46e7ff5475SWarner Losh #include "tftp-file.h"
47e7ff5475SWarner Losh #include "tftp-io.h"
48e7ff5475SWarner Losh #include "tftp-utils.h"
49e7ff5475SWarner Losh #include "tftp-options.h"
50e7ff5475SWarner Losh #include "tftp-transfer.h"
51e7ff5475SWarner Losh 
52fdf929ffSJohn Baldwin struct block_data {
53fdf929ffSJohn Baldwin 	off_t offset;
54fdf929ffSJohn Baldwin 	uint16_t block;
55fdf929ffSJohn Baldwin 	int size;
56fdf929ffSJohn Baldwin };
57fdf929ffSJohn Baldwin 
58e7ff5475SWarner Losh /*
59e7ff5475SWarner Losh  * Send a file via the TFTP data session.
60e7ff5475SWarner Losh  */
6136242fc0SDag-Erling Smørgrav int
62e7ff5475SWarner Losh tftp_send(int peer, uint16_t *block, struct tftp_stats *ts)
63e7ff5475SWarner Losh {
64e7ff5475SWarner Losh 	struct tftphdr *rp;
65fdf929ffSJohn Baldwin 	int size, n_data, n_ack, sendtry, acktry;
66fdf929ffSJohn Baldwin 	u_int i, j;
67fdf929ffSJohn Baldwin 	uint16_t oldblock, windowblock;
68e7ff5475SWarner Losh 	char sendbuffer[MAXPKTSIZE];
69e7ff5475SWarner Losh 	char recvbuffer[MAXPKTSIZE];
70fdf929ffSJohn Baldwin 	struct block_data window[WINDOWSIZE_MAX];
71e7ff5475SWarner Losh 
72e7ff5475SWarner Losh 	rp = (struct tftphdr *)recvbuffer;
73e7ff5475SWarner Losh 	*block = 1;
74e7ff5475SWarner Losh 	ts->amount = 0;
75fdf929ffSJohn Baldwin 	windowblock = 0;
76fdf929ffSJohn Baldwin 	acktry = 0;
77e7ff5475SWarner Losh 	do {
78fdf929ffSJohn Baldwin read_block:
79e7ff5475SWarner Losh 		if (debug & DEBUG_SIMPLE)
80fdf929ffSJohn Baldwin 			tftp_log(LOG_DEBUG, "Sending block %d (window block %d)",
81fdf929ffSJohn Baldwin 			    *block, windowblock);
82e7ff5475SWarner Losh 
83fdf929ffSJohn Baldwin 		window[windowblock].offset = tell_file();
84fdf929ffSJohn Baldwin 		window[windowblock].block = *block;
85e7ff5475SWarner Losh 		size = read_file(sendbuffer, segsize);
86e7ff5475SWarner Losh 		if (size < 0) {
87e7ff5475SWarner Losh 			tftp_log(LOG_ERR, "read_file returned %d", size);
88e7ff5475SWarner Losh 			send_error(peer, errno + 100);
8936242fc0SDag-Erling Smørgrav 			return -1;
90e7ff5475SWarner Losh 		}
91fdf929ffSJohn Baldwin 		window[windowblock].size = size;
92fdf929ffSJohn Baldwin 		windowblock++;
93e7ff5475SWarner Losh 
94fdf929ffSJohn Baldwin 		for (sendtry = 0; ; sendtry++) {
95e7ff5475SWarner Losh 			n_data = send_data(peer, *block, sendbuffer, size);
96fdf929ffSJohn Baldwin 			if (n_data == 0)
97fdf929ffSJohn Baldwin 				break;
98fdf929ffSJohn Baldwin 
99fdf929ffSJohn Baldwin 			if (sendtry == maxtimeouts) {
100e7ff5475SWarner Losh 				tftp_log(LOG_ERR,
101e7ff5475SWarner Losh 				    "Cannot send DATA packet #%d, "
102e7ff5475SWarner Losh 				    "giving up", *block);
10336242fc0SDag-Erling Smørgrav 				return -1;
104e7ff5475SWarner Losh 			}
105e7ff5475SWarner Losh 			tftp_log(LOG_ERR,
106e7ff5475SWarner Losh 			    "Cannot send DATA packet #%d, trying again",
107e7ff5475SWarner Losh 			    *block);
108e7ff5475SWarner Losh 		}
109e7ff5475SWarner Losh 
110fdf929ffSJohn Baldwin 		/* Only check for ACK for last block in window. */
111fdf929ffSJohn Baldwin 		if (windowblock == windowsize || size != segsize) {
112e7ff5475SWarner Losh 			n_ack = receive_packet(peer, recvbuffer,
113e7ff5475SWarner Losh 			    MAXPKTSIZE, NULL, timeoutpacket);
114e7ff5475SWarner Losh 			if (n_ack < 0) {
115e7ff5475SWarner Losh 				if (n_ack == RP_TIMEOUT) {
116fdf929ffSJohn Baldwin 					if (acktry == maxtimeouts) {
117e7ff5475SWarner Losh 						tftp_log(LOG_ERR,
118e7ff5475SWarner Losh 						    "Timeout #%d send ACK %d "
119fdf929ffSJohn Baldwin 						    "giving up", acktry, *block);
12036242fc0SDag-Erling Smørgrav 						return -1;
121e7ff5475SWarner Losh 					}
122e7ff5475SWarner Losh 					tftp_log(LOG_WARNING,
123e7ff5475SWarner Losh 					    "Timeout #%d on ACK %d",
124fdf929ffSJohn Baldwin 					    acktry, *block);
125fdf929ffSJohn Baldwin 
126fdf929ffSJohn Baldwin 					acktry++;
127fdf929ffSJohn Baldwin 					ts->retries++;
1280c011985SJohn Baldwin 					if (seek_file(window[0].offset) != 0) {
1290c011985SJohn Baldwin 						tftp_log(LOG_ERR,
1300c011985SJohn Baldwin 						    "seek_file failed: %s",
1310c011985SJohn Baldwin 						    strerror(errno));
1320c011985SJohn Baldwin 						send_error(peer, errno + 100);
13336242fc0SDag-Erling Smørgrav 						return -1;
1340c011985SJohn Baldwin 					}
135fdf929ffSJohn Baldwin 					*block = window[0].block;
136fdf929ffSJohn Baldwin 					windowblock = 0;
137fdf929ffSJohn Baldwin 					goto read_block;
138e7ff5475SWarner Losh 				}
139e7ff5475SWarner Losh 
140e7ff5475SWarner Losh 				/* Either read failure or ERROR packet */
141e7ff5475SWarner Losh 				if (debug & DEBUG_SIMPLE)
142e7ff5475SWarner Losh 					tftp_log(LOG_ERR, "Aborting: %s",
143e7ff5475SWarner Losh 					    rp_strerror(n_ack));
14436242fc0SDag-Erling Smørgrav 				return -1;
145e7ff5475SWarner Losh 			}
146e7ff5475SWarner Losh 			if (rp->th_opcode == ACK) {
147fdf929ffSJohn Baldwin 				/*
148fdf929ffSJohn Baldwin 				 * Look for the ACKed block in our open
149fdf929ffSJohn Baldwin 				 * window.
150fdf929ffSJohn Baldwin 				 */
151fdf929ffSJohn Baldwin 				for (i = 0; i < windowblock; i++) {
152fdf929ffSJohn Baldwin 					if (rp->th_block == window[i].block)
153e7ff5475SWarner Losh 						break;
154e7ff5475SWarner Losh 				}
155e7ff5475SWarner Losh 
156fdf929ffSJohn Baldwin 				if (i == windowblock) {
157fdf929ffSJohn Baldwin 					/* Did not recognize ACK. */
158fdf929ffSJohn Baldwin 					if (debug & DEBUG_SIMPLE)
159fdf929ffSJohn Baldwin 						tftp_log(LOG_DEBUG,
160fdf929ffSJohn Baldwin 						    "ACK %d out of window",
161fdf929ffSJohn Baldwin 						    rp->th_block);
162fdf929ffSJohn Baldwin 
163e7ff5475SWarner Losh 					/* Re-synchronize with the other side */
164e7ff5475SWarner Losh 					(void) synchnet(peer);
165fdf929ffSJohn Baldwin 
166fdf929ffSJohn Baldwin 					/* Resend the current window. */
167e7ff5475SWarner Losh 					ts->retries++;
1680c011985SJohn Baldwin 					if (seek_file(window[0].offset) != 0) {
1690c011985SJohn Baldwin 						tftp_log(LOG_ERR,
1700c011985SJohn Baldwin 						    "seek_file failed: %s",
1710c011985SJohn Baldwin 						    strerror(errno));
1720c011985SJohn Baldwin 						send_error(peer, errno + 100);
17336242fc0SDag-Erling Smørgrav 						return -1;
1740c011985SJohn Baldwin 					}
175fdf929ffSJohn Baldwin 					*block = window[0].block;
176fdf929ffSJohn Baldwin 					windowblock = 0;
177fdf929ffSJohn Baldwin 					goto read_block;
178e7ff5475SWarner Losh 				}
179fdf929ffSJohn Baldwin 
180fdf929ffSJohn Baldwin 				/* ACKed at least some data. */
181fdf929ffSJohn Baldwin 				acktry = 0;
182fdf929ffSJohn Baldwin 				for (j = 0; j <= i; j++) {
183fdf929ffSJohn Baldwin 					if (debug & DEBUG_SIMPLE)
184fdf929ffSJohn Baldwin 						tftp_log(LOG_DEBUG,
185fdf929ffSJohn Baldwin 						    "ACKed block %d",
186fdf929ffSJohn Baldwin 						    window[j].block);
187fdf929ffSJohn Baldwin 					ts->blocks++;
188fdf929ffSJohn Baldwin 					ts->amount += window[j].size;
189fdf929ffSJohn Baldwin 				}
190fdf929ffSJohn Baldwin 
191fdf929ffSJohn Baldwin 				/*
192fdf929ffSJohn Baldwin 				 * Partial ACK.  Rewind state to first
193fdf929ffSJohn Baldwin 				 * un-ACKed block.
194fdf929ffSJohn Baldwin 				 */
195fdf929ffSJohn Baldwin 				if (i + 1 != windowblock) {
196fdf929ffSJohn Baldwin 					if (debug & DEBUG_SIMPLE)
197fdf929ffSJohn Baldwin 						tftp_log(LOG_DEBUG,
198fdf929ffSJohn Baldwin 						    "Partial ACK");
1990c011985SJohn Baldwin 					if (seek_file(window[i + 1].offset) !=
2000c011985SJohn Baldwin 					    0) {
2010c011985SJohn Baldwin 						tftp_log(LOG_ERR,
2020c011985SJohn Baldwin 						    "seek_file failed: %s",
2030c011985SJohn Baldwin 						    strerror(errno));
2040c011985SJohn Baldwin 						send_error(peer, errno + 100);
20536242fc0SDag-Erling Smørgrav 						return -1;
2060c011985SJohn Baldwin 					}
207fdf929ffSJohn Baldwin 					*block = window[i + 1].block;
208fdf929ffSJohn Baldwin 					windowblock = 0;
209fdf929ffSJohn Baldwin 					ts->retries++;
210fdf929ffSJohn Baldwin 					goto read_block;
211fdf929ffSJohn Baldwin 				}
212fdf929ffSJohn Baldwin 
213fdf929ffSJohn Baldwin 				windowblock = 0;
214e7ff5475SWarner Losh 			}
215e7ff5475SWarner Losh 
216e7ff5475SWarner Losh 		}
217e7ff5475SWarner Losh 		oldblock = *block;
218e7ff5475SWarner Losh 		(*block)++;
219e7ff5475SWarner Losh 		if (oldblock > *block) {
220e7ff5475SWarner Losh 			if (options[OPT_ROLLOVER].o_request == NULL) {
22138bd7db3SCraig Rodrigues 				/*
22238bd7db3SCraig Rodrigues 				 * "rollover" option not specified in
22338bd7db3SCraig Rodrigues 				 * tftp client.  Default to rolling block
22438bd7db3SCraig Rodrigues 				 * counter to 0.
22538bd7db3SCraig Rodrigues 				 */
22638bd7db3SCraig Rodrigues 				*block = 0;
22738bd7db3SCraig Rodrigues 			} else {
22838bd7db3SCraig Rodrigues 				*block = atoi(options[OPT_ROLLOVER].o_request);
229e7ff5475SWarner Losh 			}
230e7ff5475SWarner Losh 
231e7ff5475SWarner Losh 			ts->rollovers++;
232e7ff5475SWarner Losh 		}
233e7ff5475SWarner Losh 		gettimeofday(&(ts->tstop), NULL);
234e7ff5475SWarner Losh 	} while (size == segsize);
23536242fc0SDag-Erling Smørgrav 	return 0;
236e7ff5475SWarner Losh }
237e7ff5475SWarner Losh 
238e7ff5475SWarner Losh /*
239e7ff5475SWarner Losh  * Receive a file via the TFTP data session.
240e7ff5475SWarner Losh  *
241e7ff5475SWarner Losh  * - It could be that the first block has already arrived while
242e7ff5475SWarner Losh  *   trying to figure out if we were receiving options or not. In
243e7ff5475SWarner Losh  *   that case it is passed to this function.
244e7ff5475SWarner Losh  */
24536242fc0SDag-Erling Smørgrav int
246e7ff5475SWarner Losh tftp_receive(int peer, uint16_t *block, struct tftp_stats *ts,
247e7ff5475SWarner Losh     struct tftphdr *firstblock, size_t fb_size)
248e7ff5475SWarner Losh {
249e7ff5475SWarner Losh 	struct tftphdr *rp;
250fdf929ffSJohn Baldwin 	uint16_t oldblock, windowstart;
251fdf929ffSJohn Baldwin 	int n_data, n_ack, writesize, i, retry, windowblock;
252e7ff5475SWarner Losh 	char recvbuffer[MAXPKTSIZE];
253e7ff5475SWarner Losh 
254e7ff5475SWarner Losh 	ts->amount = 0;
255fdf929ffSJohn Baldwin 	windowblock = 0;
256e7ff5475SWarner Losh 
257e7ff5475SWarner Losh 	if (firstblock != NULL) {
258e7ff5475SWarner Losh 		writesize = write_file(firstblock->th_data, fb_size);
259e7ff5475SWarner Losh 		ts->amount += writesize;
2603696db92SMichael Tuexen 		ts->blocks++;
261fdf929ffSJohn Baldwin 		windowblock++;
262fdf929ffSJohn Baldwin 		if (windowsize == 1 || fb_size != segsize) {
263e7ff5475SWarner Losh 			for (i = 0; ; i++) {
264e7ff5475SWarner Losh 				n_ack = send_ack(peer, *block);
265e7ff5475SWarner Losh 				if (n_ack > 0) {
266e7ff5475SWarner Losh 					if (i == maxtimeouts) {
267e7ff5475SWarner Losh 						tftp_log(LOG_ERR,
268e7ff5475SWarner Losh 						    "Cannot send ACK packet #%d, "
269e7ff5475SWarner Losh 						    "giving up", *block);
27036242fc0SDag-Erling Smørgrav 						return -1;
271e7ff5475SWarner Losh 					}
272e7ff5475SWarner Losh 					tftp_log(LOG_ERR,
273e7ff5475SWarner Losh 					    "Cannot send ACK packet #%d, trying again",
274e7ff5475SWarner Losh 					    *block);
275e7ff5475SWarner Losh 					continue;
276e7ff5475SWarner Losh 				}
277e7ff5475SWarner Losh 
278e7ff5475SWarner Losh 				break;
279e7ff5475SWarner Losh 			}
280fdf929ffSJohn Baldwin 		}
281e7ff5475SWarner Losh 
282e7ff5475SWarner Losh 		if (fb_size != segsize) {
2831f67c37cSMichael Tuexen 			write_close();
284e7ff5475SWarner Losh 			gettimeofday(&(ts->tstop), NULL);
28536242fc0SDag-Erling Smørgrav 			return 0;
286e7ff5475SWarner Losh 		}
287e7ff5475SWarner Losh 	}
288e7ff5475SWarner Losh 
289e7ff5475SWarner Losh 	rp = (struct tftphdr *)recvbuffer;
290e7ff5475SWarner Losh 	do {
291e7ff5475SWarner Losh 		oldblock = *block;
292e7ff5475SWarner Losh 		(*block)++;
293e7ff5475SWarner Losh 		if (oldblock > *block) {
294e7ff5475SWarner Losh 			if (options[OPT_ROLLOVER].o_request == NULL) {
29538bd7db3SCraig Rodrigues 				/*
29638bd7db3SCraig Rodrigues 				 * "rollover" option not specified in
29738bd7db3SCraig Rodrigues 				 * tftp client.  Default to rolling block
29838bd7db3SCraig Rodrigues 				 * counter to 0.
29938bd7db3SCraig Rodrigues 				 */
30038bd7db3SCraig Rodrigues 				*block = 0;
30138bd7db3SCraig Rodrigues 			} else {
30238bd7db3SCraig Rodrigues 				*block = atoi(options[OPT_ROLLOVER].o_request);
303e7ff5475SWarner Losh 			}
304e7ff5475SWarner Losh 
305e7ff5475SWarner Losh 			ts->rollovers++;
306e7ff5475SWarner Losh 		}
307e7ff5475SWarner Losh 
308e7ff5475SWarner Losh 		for (retry = 0; ; retry++) {
309e7ff5475SWarner Losh 			if (debug & DEBUG_SIMPLE)
310e7ff5475SWarner Losh 				tftp_log(LOG_DEBUG,
311fdf929ffSJohn Baldwin 				    "Receiving DATA block %d (window block %d)",
312fdf929ffSJohn Baldwin 				    *block, windowblock);
313e7ff5475SWarner Losh 
314e7ff5475SWarner Losh 			n_data = receive_packet(peer, recvbuffer,
315e7ff5475SWarner Losh 			    MAXPKTSIZE, NULL, timeoutpacket);
316e7ff5475SWarner Losh 			if (n_data < 0) {
317e7ff5475SWarner Losh 				if (retry == maxtimeouts) {
318e7ff5475SWarner Losh 					tftp_log(LOG_ERR,
319e7ff5475SWarner Losh 					    "Timeout #%d on DATA block %d, "
320e7ff5475SWarner Losh 					    "giving up", retry, *block);
32136242fc0SDag-Erling Smørgrav 					return -1;
322e7ff5475SWarner Losh 				}
323e7ff5475SWarner Losh 				if (n_data == RP_TIMEOUT) {
324e7ff5475SWarner Losh 					tftp_log(LOG_WARNING,
325e7ff5475SWarner Losh 					    "Timeout #%d on DATA block %d",
326e7ff5475SWarner Losh 					    retry, *block);
327e7ff5475SWarner Losh 					send_ack(peer, oldblock);
328fdf929ffSJohn Baldwin 					windowblock = 0;
329e7ff5475SWarner Losh 					continue;
330e7ff5475SWarner Losh 				}
331e7ff5475SWarner Losh 
332e7ff5475SWarner Losh 				/* Either read failure or ERROR packet */
333e7ff5475SWarner Losh 				if (debug & DEBUG_SIMPLE)
334e7ff5475SWarner Losh 					tftp_log(LOG_DEBUG, "Aborting: %s",
335e7ff5475SWarner Losh 					    rp_strerror(n_data));
33636242fc0SDag-Erling Smørgrav 				return -1;
337e7ff5475SWarner Losh 			}
338e7ff5475SWarner Losh 			if (rp->th_opcode == DATA) {
339e7ff5475SWarner Losh 				ts->blocks++;
340e7ff5475SWarner Losh 
341e7ff5475SWarner Losh 				if (rp->th_block == *block)
342e7ff5475SWarner Losh 					break;
343e7ff5475SWarner Losh 
344fdf929ffSJohn Baldwin 				/*
345fdf929ffSJohn Baldwin 				 * Ignore duplicate blocks within the
346fdf929ffSJohn Baldwin 				 * window.
347fdf929ffSJohn Baldwin 				 *
348fdf929ffSJohn Baldwin 				 * This does not handle duplicate
349fdf929ffSJohn Baldwin 				 * blocks during a rollover as
350fdf929ffSJohn Baldwin 				 * gracefully, but that should still
351fdf929ffSJohn Baldwin 				 * recover eventually.
352fdf929ffSJohn Baldwin 				 */
353fdf929ffSJohn Baldwin 				if (*block > windowsize)
354fdf929ffSJohn Baldwin 					windowstart = *block - windowsize;
355fdf929ffSJohn Baldwin 				else
356fdf929ffSJohn Baldwin 					windowstart = 0;
357fdf929ffSJohn Baldwin 				if (rp->th_block > windowstart &&
358fdf929ffSJohn Baldwin 				    rp->th_block < *block) {
359fdf929ffSJohn Baldwin 					if (debug & DEBUG_SIMPLE)
360fdf929ffSJohn Baldwin 						tftp_log(LOG_DEBUG,
361fdf929ffSJohn Baldwin 					    "Ignoring duplicate DATA block %d",
362fdf929ffSJohn Baldwin 						    rp->th_block);
363fdf929ffSJohn Baldwin 					windowblock++;
364fdf929ffSJohn Baldwin 					retry = 0;
365fdf929ffSJohn Baldwin 					continue;
366fdf929ffSJohn Baldwin 				}
367fdf929ffSJohn Baldwin 
368e7ff5475SWarner Losh 				tftp_log(LOG_WARNING,
369e7ff5475SWarner Losh 				    "Expected DATA block %d, got block %d",
370e7ff5475SWarner Losh 				    *block, rp->th_block);
371e7ff5475SWarner Losh 
372e7ff5475SWarner Losh 				/* Re-synchronize with the other side */
373e7ff5475SWarner Losh 				(void) synchnet(peer);
374fdf929ffSJohn Baldwin 
375e7ff5475SWarner Losh 				tftp_log(LOG_INFO, "Trying to sync");
376e7ff5475SWarner Losh 				*block = oldblock;
377e7ff5475SWarner Losh 				ts->retries++;
378e7ff5475SWarner Losh 				goto send_ack;	/* rexmit */
379e7ff5475SWarner Losh 
380e7ff5475SWarner Losh 			} else {
381e7ff5475SWarner Losh 				tftp_log(LOG_WARNING,
382e7ff5475SWarner Losh 				    "Expected DATA block, got %s block",
383e7ff5475SWarner Losh 				    packettype(rp->th_opcode));
384e7ff5475SWarner Losh 			}
385e7ff5475SWarner Losh 		}
386e7ff5475SWarner Losh 
387e7ff5475SWarner Losh 		if (n_data > 0) {
388e7ff5475SWarner Losh 			writesize = write_file(rp->th_data, n_data);
389e7ff5475SWarner Losh 			ts->amount += writesize;
390e7ff5475SWarner Losh 			if (writesize <= 0) {
391e7ff5475SWarner Losh 				tftp_log(LOG_ERR,
392e7ff5475SWarner Losh 				    "write_file returned %d", writesize);
393e7ff5475SWarner Losh 				if (writesize < 0)
394e7ff5475SWarner Losh 					send_error(peer, errno + 100);
395e7ff5475SWarner Losh 				else
396e7ff5475SWarner Losh 					send_error(peer, ENOSPACE);
39736242fc0SDag-Erling Smørgrav 				return -1;
398e7ff5475SWarner Losh 			}
3991d0272a6SMichael Tuexen 		}
4007378015bSAlan Somers 		if (n_data != segsize)
4017378015bSAlan Somers 			write_close();
402fdf929ffSJohn Baldwin 		windowblock++;
403e7ff5475SWarner Losh 
404fdf929ffSJohn Baldwin 		/* Only send ACKs for the last block in the window. */
405fdf929ffSJohn Baldwin 		if (windowblock < windowsize && n_data == segsize)
406fdf929ffSJohn Baldwin 			continue;
407e7ff5475SWarner Losh send_ack:
408e7ff5475SWarner Losh 		for (i = 0; ; i++) {
409e7ff5475SWarner Losh 			n_ack = send_ack(peer, *block);
410e7ff5475SWarner Losh 			if (n_ack > 0) {
411e7ff5475SWarner Losh 
412e7ff5475SWarner Losh 				if (i == maxtimeouts) {
413e7ff5475SWarner Losh 					tftp_log(LOG_ERR,
414e7ff5475SWarner Losh 					    "Cannot send ACK packet #%d, "
415e7ff5475SWarner Losh 					    "giving up", *block);
41636242fc0SDag-Erling Smørgrav 					return -1;
417e7ff5475SWarner Losh 				}
418e7ff5475SWarner Losh 
419e7ff5475SWarner Losh 				tftp_log(LOG_ERR,
420e7ff5475SWarner Losh 				    "Cannot send ACK packet #%d, trying again",
421e7ff5475SWarner Losh 				    *block);
422e7ff5475SWarner Losh 				continue;
423e7ff5475SWarner Losh 			}
424e7ff5475SWarner Losh 
425fdf929ffSJohn Baldwin 			if (debug & DEBUG_SIMPLE)
426fdf929ffSJohn Baldwin 				tftp_log(LOG_DEBUG, "Sent ACK for %d", *block);
427fdf929ffSJohn Baldwin 			windowblock = 0;
428e7ff5475SWarner Losh 			break;
429e7ff5475SWarner Losh 		}
430e7ff5475SWarner Losh 		gettimeofday(&(ts->tstop), NULL);
431e7ff5475SWarner Losh 	} while (n_data == segsize);
432e7ff5475SWarner Losh 
433e7ff5475SWarner Losh 	/* Don't do late packet management for the client implementation */
434e7ff5475SWarner Losh 	if (acting_as_client)
43536242fc0SDag-Erling Smørgrav 		return 0;
436e7ff5475SWarner Losh 
437e7ff5475SWarner Losh 	for (i = 0; ; i++) {
438e7ff5475SWarner Losh 		n_data = receive_packet(peer, (char *)rp, pktsize,
439*e3b4cb1bSDag-Erling Smørgrav 		    NULL, -timeoutpacket);
440e7ff5475SWarner Losh 		if (n_data <= 0)
441e7ff5475SWarner Losh 			break;
442e7ff5475SWarner Losh 		if (n_data > 0 &&
443e7ff5475SWarner Losh 		    rp->th_opcode == DATA &&	/* and got a data block */
444e7ff5475SWarner Losh 		    *block == rp->th_block)	/* then my last ack was lost */
445e7ff5475SWarner Losh 			send_ack(peer, *block);	/* resend final ack */
446e7ff5475SWarner Losh 	}
447e7ff5475SWarner Losh 
44836242fc0SDag-Erling Smørgrav 	return 0;
449e7ff5475SWarner Losh }
450