2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (C) 2008 Edwin Groothuis. All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
31 #include <sys/types.h>
32 #include <sys/param.h>
33 #include <sys/ioctl.h>
35 #include <sys/socket.h>
37 #include <netinet/in.h>
38 #include <arpa/tftp.h>
46 #include "tftp-file.h"
48 #include "tftp-utils.h"
49 #include "tftp-options.h"
50 #include "tftp-transfer.h"
59 * Send a file via the TFTP data session.
62 tftp_send(int peer, uint16_t *block, struct tftp_stats *ts)
65 int size, n_data, n_ack, sendtry, acktry;
67 uint16_t oldblock, windowblock;
68 char sendbuffer[MAXPKTSIZE];
69 char recvbuffer[MAXPKTSIZE];
70 struct block_data window[WINDOWSIZE_MAX];
72 rp = (struct tftphdr *)recvbuffer;
79 if (debug&DEBUG_SIMPLE)
80 tftp_log(LOG_DEBUG, "Sending block %d (window block %d)",
83 window[windowblock].offset = tell_file();
84 window[windowblock].block = *block;
85 size = read_file(sendbuffer, segsize);
87 tftp_log(LOG_ERR, "read_file returned %d", size);
88 send_error(peer, errno + 100);
91 window[windowblock].size = size;
94 for (sendtry = 0; ; sendtry++) {
95 n_data = send_data(peer, *block, sendbuffer, size);
99 if (sendtry == maxtimeouts) {
101 "Cannot send DATA packet #%d, "
102 "giving up", *block);
106 "Cannot send DATA packet #%d, trying again",
110 /* Only check for ACK for last block in window. */
111 if (windowblock == windowsize || size != segsize) {
112 n_ack = receive_packet(peer, recvbuffer,
113 MAXPKTSIZE, NULL, timeoutpacket);
115 if (n_ack == RP_TIMEOUT) {
116 if (acktry == maxtimeouts) {
118 "Timeout #%d send ACK %d "
119 "giving up", acktry, *block);
122 tftp_log(LOG_WARNING,
123 "Timeout #%d on ACK %d",
128 if (seek_file(window[0].offset) != 0) {
130 "seek_file failed: %s",
132 send_error(peer, errno + 100);
135 *block = window[0].block;
140 /* Either read failure or ERROR packet */
141 if (debug&DEBUG_SIMPLE)
142 tftp_log(LOG_ERR, "Aborting: %s",
146 if (rp->th_opcode == ACK) {
148 * Look for the ACKed block in our open
151 for (i = 0; i < windowblock; i++) {
152 if (rp->th_block == window[i].block)
156 if (i == windowblock) {
157 /* Did not recognize ACK. */
158 if (debug&DEBUG_SIMPLE)
160 "ACK %d out of window",
163 /* Re-synchronize with the other side */
164 (void) synchnet(peer);
166 /* Resend the current window. */
168 if (seek_file(window[0].offset) != 0) {
170 "seek_file failed: %s",
172 send_error(peer, errno + 100);
175 *block = window[0].block;
180 /* ACKed at least some data. */
182 for (j = 0; j <= i; j++) {
183 if (debug&DEBUG_SIMPLE)
188 ts->amount += window[j].size;
192 * Partial ACK. Rewind state to first
195 if (i + 1 != windowblock) {
196 if (debug&DEBUG_SIMPLE)
199 if (seek_file(window[i + 1].offset) !=
202 "seek_file failed: %s",
204 send_error(peer, errno + 100);
207 *block = window[i + 1].block;
219 if (oldblock > *block) {
220 if (options[OPT_ROLLOVER].o_request == NULL) {
222 * "rollover" option not specified in
223 * tftp client. Default to rolling block
228 *block = atoi(options[OPT_ROLLOVER].o_request);
233 gettimeofday(&(ts->tstop), NULL);
234 } while (size == segsize);
240 * Receive a file via the TFTP data session.
242 * - It could be that the first block has already arrived while
243 * trying to figure out if we were receiving options or not. In
244 * that case it is passed to this function.
247 tftp_receive(int peer, uint16_t *block, struct tftp_stats *ts,
248 struct tftphdr *firstblock, size_t fb_size)
251 uint16_t oldblock, windowstart;
252 int n_data, n_ack, writesize, i, retry, windowblock;
253 char recvbuffer[MAXPKTSIZE];
258 if (firstblock != NULL) {
259 writesize = write_file(firstblock->th_data, fb_size);
260 ts->amount += writesize;
262 if (windowsize == 1 || fb_size != segsize) {
264 n_ack = send_ack(peer, *block);
266 if (i == maxtimeouts) {
268 "Cannot send ACK packet #%d, "
269 "giving up", *block);
273 "Cannot send ACK packet #%d, trying again",
282 if (fb_size != segsize) {
283 gettimeofday(&(ts->tstop), NULL);
288 rp = (struct tftphdr *)recvbuffer;
292 if (oldblock > *block) {
293 if (options[OPT_ROLLOVER].o_request == NULL) {
295 * "rollover" option not specified in
296 * tftp client. Default to rolling block
301 *block = atoi(options[OPT_ROLLOVER].o_request);
307 for (retry = 0; ; retry++) {
308 if (debug&DEBUG_SIMPLE)
310 "Receiving DATA block %d (window block %d)",
311 *block, windowblock);
313 n_data = receive_packet(peer, recvbuffer,
314 MAXPKTSIZE, NULL, timeoutpacket);
316 if (retry == maxtimeouts) {
318 "Timeout #%d on DATA block %d, "
319 "giving up", retry, *block);
322 if (n_data == RP_TIMEOUT) {
323 tftp_log(LOG_WARNING,
324 "Timeout #%d on DATA block %d",
326 send_ack(peer, oldblock);
331 /* Either read failure or ERROR packet */
332 if (debug&DEBUG_SIMPLE)
333 tftp_log(LOG_DEBUG, "Aborting: %s",
334 rp_strerror(n_data));
337 if (rp->th_opcode == DATA) {
340 if (rp->th_block == *block)
344 * Ignore duplicate blocks within the
347 * This does not handle duplicate
348 * blocks during a rollover as
349 * gracefully, but that should still
350 * recover eventually.
352 if (*block > windowsize)
353 windowstart = *block - windowsize;
356 if (rp->th_block > windowstart &&
357 rp->th_block < *block) {
358 if (debug&DEBUG_SIMPLE)
360 "Ignoring duplicate DATA block %d",
367 tftp_log(LOG_WARNING,
368 "Expected DATA block %d, got block %d",
369 *block, rp->th_block);
371 /* Re-synchronize with the other side */
372 (void) synchnet(peer);
374 tftp_log(LOG_INFO, "Trying to sync");
377 goto send_ack; /* rexmit */
380 tftp_log(LOG_WARNING,
381 "Expected DATA block, got %s block",
382 packettype(rp->th_opcode));
387 writesize = write_file(rp->th_data, n_data);
388 ts->amount += writesize;
389 if (writesize <= 0) {
391 "write_file returned %d", writesize);
393 send_error(peer, errno + 100);
395 send_error(peer, ENOSPACE);
398 if (n_data != segsize)
403 /* Only send ACKs for the last block in the window. */
404 if (windowblock < windowsize && n_data == segsize)
408 n_ack = send_ack(peer, *block);
411 if (i == maxtimeouts) {
413 "Cannot send ACK packet #%d, "
414 "giving up", *block);
419 "Cannot send ACK packet #%d, trying again",
424 if (debug&DEBUG_SIMPLE)
425 tftp_log(LOG_DEBUG, "Sent ACK for %d", *block);
429 gettimeofday(&(ts->tstop), NULL);
430 } while (n_data == segsize);
432 /* Don't do late packet management for the client implementation */
433 if (acting_as_client)
437 n_data = receive_packet(peer, (char *)rp, pktsize,
438 NULL, timeoutpacket);
442 rp->th_opcode == DATA && /* and got a data block */
443 *block == rp->th_block) /* then my last ack was lost */
444 send_ack(peer, *block); /* resend final ack */