1e6209940SPedro F. Giffuni /*-
2*4d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
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/param.h>
29e7ff5475SWarner Losh #include <sys/ioctl.h>
30e7ff5475SWarner Losh #include <sys/socket.h>
31eb0292d9SDag-Erling Smørgrav #include <sys/stat.h>
32eb0292d9SDag-Erling Smørgrav #include <sys/time.h>
33e7ff5475SWarner Losh
34e7ff5475SWarner Losh #include <netinet/in.h>
35e7ff5475SWarner Losh #include <arpa/tftp.h>
36e7ff5475SWarner Losh
37e7ff5475SWarner Losh #include <errno.h>
38e7ff5475SWarner Losh #include <stdio.h>
39e7ff5475SWarner Losh #include <stdlib.h>
400c011985SJohn Baldwin #include <string.h>
41e7ff5475SWarner Losh #include <syslog.h>
42e7ff5475SWarner Losh
43e7ff5475SWarner Losh #include "tftp-file.h"
44e7ff5475SWarner Losh #include "tftp-io.h"
45e7ff5475SWarner Losh #include "tftp-utils.h"
46e7ff5475SWarner Losh #include "tftp-options.h"
47e7ff5475SWarner Losh #include "tftp-transfer.h"
48e7ff5475SWarner Losh
49fdf929ffSJohn Baldwin struct block_data {
50fdf929ffSJohn Baldwin off_t offset;
51fdf929ffSJohn Baldwin uint16_t block;
52fdf929ffSJohn Baldwin int size;
53fdf929ffSJohn Baldwin };
54fdf929ffSJohn Baldwin
55e7ff5475SWarner Losh /*
56e7ff5475SWarner Losh * Send a file via the TFTP data session.
57e7ff5475SWarner Losh */
5836242fc0SDag-Erling Smørgrav int
tftp_send(int peer,uint16_t * block,struct tftp_stats * ts)59e7ff5475SWarner Losh tftp_send(int peer, uint16_t *block, struct tftp_stats *ts)
60e7ff5475SWarner Losh {
61e7ff5475SWarner Losh struct tftphdr *rp;
62fdf929ffSJohn Baldwin int size, n_data, n_ack, sendtry, acktry;
63fdf929ffSJohn Baldwin u_int i, j;
64fdf929ffSJohn Baldwin uint16_t oldblock, windowblock;
65e7ff5475SWarner Losh char sendbuffer[MAXPKTSIZE];
66e7ff5475SWarner Losh char recvbuffer[MAXPKTSIZE];
67fdf929ffSJohn Baldwin struct block_data window[WINDOWSIZE_MAX];
68e7ff5475SWarner Losh
69e7ff5475SWarner Losh rp = (struct tftphdr *)recvbuffer;
70e7ff5475SWarner Losh *block = 1;
71e7ff5475SWarner Losh ts->amount = 0;
72fdf929ffSJohn Baldwin windowblock = 0;
73fdf929ffSJohn Baldwin acktry = 0;
74e7ff5475SWarner Losh do {
75fdf929ffSJohn Baldwin read_block:
76e7ff5475SWarner Losh if (debug & DEBUG_SIMPLE)
77fdf929ffSJohn Baldwin tftp_log(LOG_DEBUG, "Sending block %d (window block %d)",
78fdf929ffSJohn Baldwin *block, windowblock);
79e7ff5475SWarner Losh
80fdf929ffSJohn Baldwin window[windowblock].offset = tell_file();
81fdf929ffSJohn Baldwin window[windowblock].block = *block;
82e7ff5475SWarner Losh size = read_file(sendbuffer, segsize);
83e7ff5475SWarner Losh if (size < 0) {
84e7ff5475SWarner Losh tftp_log(LOG_ERR, "read_file returned %d", size);
85e7ff5475SWarner Losh send_error(peer, errno + 100);
8636242fc0SDag-Erling Smørgrav return -1;
87e7ff5475SWarner Losh }
88fdf929ffSJohn Baldwin window[windowblock].size = size;
89fdf929ffSJohn Baldwin windowblock++;
90e7ff5475SWarner Losh
91fdf929ffSJohn Baldwin for (sendtry = 0; ; sendtry++) {
92e7ff5475SWarner Losh n_data = send_data(peer, *block, sendbuffer, size);
93fdf929ffSJohn Baldwin if (n_data == 0)
94fdf929ffSJohn Baldwin break;
95fdf929ffSJohn Baldwin
96fdf929ffSJohn Baldwin if (sendtry == maxtimeouts) {
97e7ff5475SWarner Losh tftp_log(LOG_ERR,
98e7ff5475SWarner Losh "Cannot send DATA packet #%d, "
99e7ff5475SWarner Losh "giving up", *block);
10036242fc0SDag-Erling Smørgrav return -1;
101e7ff5475SWarner Losh }
102e7ff5475SWarner Losh tftp_log(LOG_ERR,
103e7ff5475SWarner Losh "Cannot send DATA packet #%d, trying again",
104e7ff5475SWarner Losh *block);
105e7ff5475SWarner Losh }
106e7ff5475SWarner Losh
107fdf929ffSJohn Baldwin /* Only check for ACK for last block in window. */
108fdf929ffSJohn Baldwin if (windowblock == windowsize || size != segsize) {
109e7ff5475SWarner Losh n_ack = receive_packet(peer, recvbuffer,
110e7ff5475SWarner Losh MAXPKTSIZE, NULL, timeoutpacket);
111e7ff5475SWarner Losh if (n_ack < 0) {
112e7ff5475SWarner Losh if (n_ack == RP_TIMEOUT) {
113fdf929ffSJohn Baldwin if (acktry == maxtimeouts) {
114e7ff5475SWarner Losh tftp_log(LOG_ERR,
115e7ff5475SWarner Losh "Timeout #%d send ACK %d "
116fdf929ffSJohn Baldwin "giving up", acktry, *block);
11736242fc0SDag-Erling Smørgrav return -1;
118e7ff5475SWarner Losh }
119e7ff5475SWarner Losh tftp_log(LOG_WARNING,
120e7ff5475SWarner Losh "Timeout #%d on ACK %d",
121fdf929ffSJohn Baldwin acktry, *block);
122fdf929ffSJohn Baldwin
123fdf929ffSJohn Baldwin acktry++;
124fdf929ffSJohn Baldwin ts->retries++;
1250c011985SJohn Baldwin if (seek_file(window[0].offset) != 0) {
1260c011985SJohn Baldwin tftp_log(LOG_ERR,
1270c011985SJohn Baldwin "seek_file failed: %s",
1280c011985SJohn Baldwin strerror(errno));
1290c011985SJohn Baldwin send_error(peer, errno + 100);
13036242fc0SDag-Erling Smørgrav return -1;
1310c011985SJohn Baldwin }
132fdf929ffSJohn Baldwin *block = window[0].block;
133fdf929ffSJohn Baldwin windowblock = 0;
134fdf929ffSJohn Baldwin goto read_block;
135e7ff5475SWarner Losh }
136e7ff5475SWarner Losh
137e7ff5475SWarner Losh /* Either read failure or ERROR packet */
138e7ff5475SWarner Losh if (debug & DEBUG_SIMPLE)
139e7ff5475SWarner Losh tftp_log(LOG_ERR, "Aborting: %s",
140e7ff5475SWarner Losh rp_strerror(n_ack));
14136242fc0SDag-Erling Smørgrav return -1;
142e7ff5475SWarner Losh }
143e7ff5475SWarner Losh if (rp->th_opcode == ACK) {
144fdf929ffSJohn Baldwin /*
145fdf929ffSJohn Baldwin * Look for the ACKed block in our open
146fdf929ffSJohn Baldwin * window.
147fdf929ffSJohn Baldwin */
148fdf929ffSJohn Baldwin for (i = 0; i < windowblock; i++) {
149fdf929ffSJohn Baldwin if (rp->th_block == window[i].block)
150e7ff5475SWarner Losh break;
151e7ff5475SWarner Losh }
152e7ff5475SWarner Losh
153fdf929ffSJohn Baldwin if (i == windowblock) {
154fdf929ffSJohn Baldwin /* Did not recognize ACK. */
155fdf929ffSJohn Baldwin if (debug & DEBUG_SIMPLE)
156fdf929ffSJohn Baldwin tftp_log(LOG_DEBUG,
157fdf929ffSJohn Baldwin "ACK %d out of window",
158fdf929ffSJohn Baldwin rp->th_block);
159fdf929ffSJohn Baldwin
160e7ff5475SWarner Losh /* Re-synchronize with the other side */
161e7ff5475SWarner Losh (void) synchnet(peer);
162fdf929ffSJohn Baldwin
163fdf929ffSJohn Baldwin /* Resend the current window. */
164e7ff5475SWarner Losh ts->retries++;
1650c011985SJohn Baldwin if (seek_file(window[0].offset) != 0) {
1660c011985SJohn Baldwin tftp_log(LOG_ERR,
1670c011985SJohn Baldwin "seek_file failed: %s",
1680c011985SJohn Baldwin strerror(errno));
1690c011985SJohn Baldwin send_error(peer, errno + 100);
17036242fc0SDag-Erling Smørgrav return -1;
1710c011985SJohn Baldwin }
172fdf929ffSJohn Baldwin *block = window[0].block;
173fdf929ffSJohn Baldwin windowblock = 0;
174fdf929ffSJohn Baldwin goto read_block;
175e7ff5475SWarner Losh }
176fdf929ffSJohn Baldwin
177fdf929ffSJohn Baldwin /* ACKed at least some data. */
178fdf929ffSJohn Baldwin acktry = 0;
179fdf929ffSJohn Baldwin for (j = 0; j <= i; j++) {
180fdf929ffSJohn Baldwin if (debug & DEBUG_SIMPLE)
181fdf929ffSJohn Baldwin tftp_log(LOG_DEBUG,
182fdf929ffSJohn Baldwin "ACKed block %d",
183fdf929ffSJohn Baldwin window[j].block);
184fdf929ffSJohn Baldwin ts->blocks++;
185fdf929ffSJohn Baldwin ts->amount += window[j].size;
186fdf929ffSJohn Baldwin }
187fdf929ffSJohn Baldwin
188fdf929ffSJohn Baldwin /*
189fdf929ffSJohn Baldwin * Partial ACK. Rewind state to first
190fdf929ffSJohn Baldwin * un-ACKed block.
191fdf929ffSJohn Baldwin */
192fdf929ffSJohn Baldwin if (i + 1 != windowblock) {
193fdf929ffSJohn Baldwin if (debug & DEBUG_SIMPLE)
194fdf929ffSJohn Baldwin tftp_log(LOG_DEBUG,
195fdf929ffSJohn Baldwin "Partial ACK");
1960c011985SJohn Baldwin if (seek_file(window[i + 1].offset) !=
1970c011985SJohn Baldwin 0) {
1980c011985SJohn Baldwin tftp_log(LOG_ERR,
1990c011985SJohn Baldwin "seek_file failed: %s",
2000c011985SJohn Baldwin strerror(errno));
2010c011985SJohn Baldwin send_error(peer, errno + 100);
20236242fc0SDag-Erling Smørgrav return -1;
2030c011985SJohn Baldwin }
204fdf929ffSJohn Baldwin *block = window[i + 1].block;
205fdf929ffSJohn Baldwin windowblock = 0;
206fdf929ffSJohn Baldwin ts->retries++;
207fdf929ffSJohn Baldwin goto read_block;
208fdf929ffSJohn Baldwin }
209fdf929ffSJohn Baldwin
210fdf929ffSJohn Baldwin windowblock = 0;
211e7ff5475SWarner Losh }
212e7ff5475SWarner Losh
213e7ff5475SWarner Losh }
214e7ff5475SWarner Losh oldblock = *block;
215e7ff5475SWarner Losh (*block)++;
216e7ff5475SWarner Losh if (oldblock > *block) {
217e7ff5475SWarner Losh if (options[OPT_ROLLOVER].o_request == NULL) {
21838bd7db3SCraig Rodrigues /*
21938bd7db3SCraig Rodrigues * "rollover" option not specified in
22038bd7db3SCraig Rodrigues * tftp client. Default to rolling block
22138bd7db3SCraig Rodrigues * counter to 0.
22238bd7db3SCraig Rodrigues */
22338bd7db3SCraig Rodrigues *block = 0;
22438bd7db3SCraig Rodrigues } else {
22538bd7db3SCraig Rodrigues *block = atoi(options[OPT_ROLLOVER].o_request);
226e7ff5475SWarner Losh }
227e7ff5475SWarner Losh
228e7ff5475SWarner Losh ts->rollovers++;
229e7ff5475SWarner Losh }
230e7ff5475SWarner Losh gettimeofday(&(ts->tstop), NULL);
231e7ff5475SWarner Losh } while (size == segsize);
23236242fc0SDag-Erling Smørgrav return 0;
233e7ff5475SWarner Losh }
234e7ff5475SWarner Losh
235e7ff5475SWarner Losh /*
236e7ff5475SWarner Losh * Receive a file via the TFTP data session.
237e7ff5475SWarner Losh *
238e7ff5475SWarner Losh * - It could be that the first block has already arrived while
239e7ff5475SWarner Losh * trying to figure out if we were receiving options or not. In
240e7ff5475SWarner Losh * that case it is passed to this function.
241e7ff5475SWarner Losh */
24236242fc0SDag-Erling Smørgrav int
tftp_receive(int peer,uint16_t * block,struct tftp_stats * ts,struct tftphdr * firstblock,size_t fb_size)243e7ff5475SWarner Losh tftp_receive(int peer, uint16_t *block, struct tftp_stats *ts,
244e7ff5475SWarner Losh struct tftphdr *firstblock, size_t fb_size)
245e7ff5475SWarner Losh {
246e7ff5475SWarner Losh struct tftphdr *rp;
247fdf929ffSJohn Baldwin uint16_t oldblock, windowstart;
248fdf929ffSJohn Baldwin int n_data, n_ack, writesize, i, retry, windowblock;
249e7ff5475SWarner Losh char recvbuffer[MAXPKTSIZE];
250e7ff5475SWarner Losh
251e7ff5475SWarner Losh ts->amount = 0;
252fdf929ffSJohn Baldwin windowblock = 0;
253e7ff5475SWarner Losh
254e7ff5475SWarner Losh if (firstblock != NULL) {
255e7ff5475SWarner Losh writesize = write_file(firstblock->th_data, fb_size);
256e7ff5475SWarner Losh ts->amount += writesize;
2573696db92SMichael Tuexen ts->blocks++;
258fdf929ffSJohn Baldwin windowblock++;
259fdf929ffSJohn Baldwin if (windowsize == 1 || fb_size != segsize) {
260e7ff5475SWarner Losh for (i = 0; ; i++) {
261e7ff5475SWarner Losh n_ack = send_ack(peer, *block);
262e7ff5475SWarner Losh if (n_ack > 0) {
263e7ff5475SWarner Losh if (i == maxtimeouts) {
264e7ff5475SWarner Losh tftp_log(LOG_ERR,
265e7ff5475SWarner Losh "Cannot send ACK packet #%d, "
266e7ff5475SWarner Losh "giving up", *block);
26736242fc0SDag-Erling Smørgrav return -1;
268e7ff5475SWarner Losh }
269e7ff5475SWarner Losh tftp_log(LOG_ERR,
270e7ff5475SWarner Losh "Cannot send ACK packet #%d, trying again",
271e7ff5475SWarner Losh *block);
272e7ff5475SWarner Losh continue;
273e7ff5475SWarner Losh }
274e7ff5475SWarner Losh
275e7ff5475SWarner Losh break;
276e7ff5475SWarner Losh }
277fdf929ffSJohn Baldwin }
278e7ff5475SWarner Losh
279e7ff5475SWarner Losh if (fb_size != segsize) {
2801f67c37cSMichael Tuexen write_close();
281e7ff5475SWarner Losh gettimeofday(&(ts->tstop), NULL);
28236242fc0SDag-Erling Smørgrav return 0;
283e7ff5475SWarner Losh }
284e7ff5475SWarner Losh }
285e7ff5475SWarner Losh
286e7ff5475SWarner Losh rp = (struct tftphdr *)recvbuffer;
287e7ff5475SWarner Losh do {
288e7ff5475SWarner Losh oldblock = *block;
289e7ff5475SWarner Losh (*block)++;
290e7ff5475SWarner Losh if (oldblock > *block) {
291e7ff5475SWarner Losh if (options[OPT_ROLLOVER].o_request == NULL) {
29238bd7db3SCraig Rodrigues /*
29338bd7db3SCraig Rodrigues * "rollover" option not specified in
29438bd7db3SCraig Rodrigues * tftp client. Default to rolling block
29538bd7db3SCraig Rodrigues * counter to 0.
29638bd7db3SCraig Rodrigues */
29738bd7db3SCraig Rodrigues *block = 0;
29838bd7db3SCraig Rodrigues } else {
29938bd7db3SCraig Rodrigues *block = atoi(options[OPT_ROLLOVER].o_request);
300e7ff5475SWarner Losh }
301e7ff5475SWarner Losh
302e7ff5475SWarner Losh ts->rollovers++;
303e7ff5475SWarner Losh }
304e7ff5475SWarner Losh
305e7ff5475SWarner Losh for (retry = 0; ; retry++) {
306e7ff5475SWarner Losh if (debug & DEBUG_SIMPLE)
307e7ff5475SWarner Losh tftp_log(LOG_DEBUG,
308fdf929ffSJohn Baldwin "Receiving DATA block %d (window block %d)",
309fdf929ffSJohn Baldwin *block, windowblock);
310e7ff5475SWarner Losh
311e7ff5475SWarner Losh n_data = receive_packet(peer, recvbuffer,
312e7ff5475SWarner Losh MAXPKTSIZE, NULL, timeoutpacket);
313e7ff5475SWarner Losh if (n_data < 0) {
314e7ff5475SWarner Losh if (retry == maxtimeouts) {
315e7ff5475SWarner Losh tftp_log(LOG_ERR,
316e7ff5475SWarner Losh "Timeout #%d on DATA block %d, "
317e7ff5475SWarner Losh "giving up", retry, *block);
31836242fc0SDag-Erling Smørgrav return -1;
319e7ff5475SWarner Losh }
320e7ff5475SWarner Losh if (n_data == RP_TIMEOUT) {
321e7ff5475SWarner Losh tftp_log(LOG_WARNING,
322e7ff5475SWarner Losh "Timeout #%d on DATA block %d",
323e7ff5475SWarner Losh retry, *block);
324e7ff5475SWarner Losh send_ack(peer, oldblock);
325fdf929ffSJohn Baldwin windowblock = 0;
326e7ff5475SWarner Losh continue;
327e7ff5475SWarner Losh }
328e7ff5475SWarner Losh
329e7ff5475SWarner Losh /* Either read failure or ERROR packet */
330e7ff5475SWarner Losh if (debug & DEBUG_SIMPLE)
331e7ff5475SWarner Losh tftp_log(LOG_DEBUG, "Aborting: %s",
332e7ff5475SWarner Losh rp_strerror(n_data));
33336242fc0SDag-Erling Smørgrav return -1;
334e7ff5475SWarner Losh }
335e7ff5475SWarner Losh if (rp->th_opcode == DATA) {
336e7ff5475SWarner Losh ts->blocks++;
337e7ff5475SWarner Losh
338e7ff5475SWarner Losh if (rp->th_block == *block)
339e7ff5475SWarner Losh break;
340e7ff5475SWarner Losh
341fdf929ffSJohn Baldwin /*
342fdf929ffSJohn Baldwin * Ignore duplicate blocks within the
343fdf929ffSJohn Baldwin * window.
344fdf929ffSJohn Baldwin *
345fdf929ffSJohn Baldwin * This does not handle duplicate
346fdf929ffSJohn Baldwin * blocks during a rollover as
347fdf929ffSJohn Baldwin * gracefully, but that should still
348fdf929ffSJohn Baldwin * recover eventually.
349fdf929ffSJohn Baldwin */
350fdf929ffSJohn Baldwin if (*block > windowsize)
351fdf929ffSJohn Baldwin windowstart = *block - windowsize;
352fdf929ffSJohn Baldwin else
353fdf929ffSJohn Baldwin windowstart = 0;
354fdf929ffSJohn Baldwin if (rp->th_block > windowstart &&
355fdf929ffSJohn Baldwin rp->th_block < *block) {
356fdf929ffSJohn Baldwin if (debug & DEBUG_SIMPLE)
357fdf929ffSJohn Baldwin tftp_log(LOG_DEBUG,
358fdf929ffSJohn Baldwin "Ignoring duplicate DATA block %d",
359fdf929ffSJohn Baldwin rp->th_block);
360fdf929ffSJohn Baldwin windowblock++;
361fdf929ffSJohn Baldwin retry = 0;
362fdf929ffSJohn Baldwin continue;
363fdf929ffSJohn Baldwin }
364fdf929ffSJohn Baldwin
365e7ff5475SWarner Losh tftp_log(LOG_WARNING,
366e7ff5475SWarner Losh "Expected DATA block %d, got block %d",
367e7ff5475SWarner Losh *block, rp->th_block);
368e7ff5475SWarner Losh
369e7ff5475SWarner Losh /* Re-synchronize with the other side */
370e7ff5475SWarner Losh (void) synchnet(peer);
371fdf929ffSJohn Baldwin
372e7ff5475SWarner Losh tftp_log(LOG_INFO, "Trying to sync");
373e7ff5475SWarner Losh *block = oldblock;
374e7ff5475SWarner Losh ts->retries++;
375e7ff5475SWarner Losh goto send_ack; /* rexmit */
376e7ff5475SWarner Losh
377e7ff5475SWarner Losh } else {
378e7ff5475SWarner Losh tftp_log(LOG_WARNING,
379e7ff5475SWarner Losh "Expected DATA block, got %s block",
380e7ff5475SWarner Losh packettype(rp->th_opcode));
381e7ff5475SWarner Losh }
382e7ff5475SWarner Losh }
383e7ff5475SWarner Losh
384e7ff5475SWarner Losh if (n_data > 0) {
385e7ff5475SWarner Losh writesize = write_file(rp->th_data, n_data);
386e7ff5475SWarner Losh ts->amount += writesize;
387e7ff5475SWarner Losh if (writesize <= 0) {
388e7ff5475SWarner Losh tftp_log(LOG_ERR,
389e7ff5475SWarner Losh "write_file returned %d", writesize);
390e7ff5475SWarner Losh if (writesize < 0)
391e7ff5475SWarner Losh send_error(peer, errno + 100);
392e7ff5475SWarner Losh else
393e7ff5475SWarner Losh send_error(peer, ENOSPACE);
39436242fc0SDag-Erling Smørgrav return -1;
395e7ff5475SWarner Losh }
3961d0272a6SMichael Tuexen }
3977378015bSAlan Somers if (n_data != segsize)
3987378015bSAlan Somers write_close();
399fdf929ffSJohn Baldwin windowblock++;
400e7ff5475SWarner Losh
401fdf929ffSJohn Baldwin /* Only send ACKs for the last block in the window. */
402fdf929ffSJohn Baldwin if (windowblock < windowsize && n_data == segsize)
403fdf929ffSJohn Baldwin continue;
404e7ff5475SWarner Losh send_ack:
405e7ff5475SWarner Losh for (i = 0; ; i++) {
406e7ff5475SWarner Losh n_ack = send_ack(peer, *block);
407e7ff5475SWarner Losh if (n_ack > 0) {
408e7ff5475SWarner Losh
409e7ff5475SWarner Losh if (i == maxtimeouts) {
410e7ff5475SWarner Losh tftp_log(LOG_ERR,
411e7ff5475SWarner Losh "Cannot send ACK packet #%d, "
412e7ff5475SWarner Losh "giving up", *block);
41336242fc0SDag-Erling Smørgrav return -1;
414e7ff5475SWarner Losh }
415e7ff5475SWarner Losh
416e7ff5475SWarner Losh tftp_log(LOG_ERR,
417e7ff5475SWarner Losh "Cannot send ACK packet #%d, trying again",
418e7ff5475SWarner Losh *block);
419e7ff5475SWarner Losh continue;
420e7ff5475SWarner Losh }
421e7ff5475SWarner Losh
422fdf929ffSJohn Baldwin if (debug & DEBUG_SIMPLE)
423fdf929ffSJohn Baldwin tftp_log(LOG_DEBUG, "Sent ACK for %d", *block);
424fdf929ffSJohn Baldwin windowblock = 0;
425e7ff5475SWarner Losh break;
426e7ff5475SWarner Losh }
427e7ff5475SWarner Losh gettimeofday(&(ts->tstop), NULL);
428e7ff5475SWarner Losh } while (n_data == segsize);
429e7ff5475SWarner Losh
430e7ff5475SWarner Losh /* Don't do late packet management for the client implementation */
431e7ff5475SWarner Losh if (acting_as_client)
43236242fc0SDag-Erling Smørgrav return 0;
433e7ff5475SWarner Losh
434e7ff5475SWarner Losh for (i = 0; ; i++) {
435e7ff5475SWarner Losh n_data = receive_packet(peer, (char *)rp, pktsize,
436e3b4cb1bSDag-Erling Smørgrav NULL, -timeoutpacket);
437e7ff5475SWarner Losh if (n_data <= 0)
438e7ff5475SWarner Losh break;
439e7ff5475SWarner Losh if (n_data > 0 &&
440e7ff5475SWarner Losh rp->th_opcode == DATA && /* and got a data block */
441e7ff5475SWarner Losh *block == rp->th_block) /* then my last ack was lost */
442e7ff5475SWarner Losh send_ack(peer, *block); /* resend final ack */
443e7ff5475SWarner Losh }
444e7ff5475SWarner Losh
44536242fc0SDag-Erling Smørgrav return 0;
446e7ff5475SWarner Losh }
447