aboutsummaryrefslogtreecommitdiff
path: root/libexec/tftpd
diff options
context:
space:
mode:
Diffstat (limited to 'libexec/tftpd')
-rw-r--r--libexec/tftpd/tests/functional.c202
-rw-r--r--libexec/tftpd/tftp-file.c14
-rw-r--r--libexec/tftpd/tftp-file.h3
-rw-r--r--libexec/tftpd/tftp-options.c36
-rw-r--r--libexec/tftpd/tftp-options.h2
-rw-r--r--libexec/tftpd/tftp-transfer.c199
-rw-r--r--libexec/tftpd/tftp-utils.c1
-rw-r--r--libexec/tftpd/tftp-utils.h6
-rw-r--r--libexec/tftpd/tftpd.88
9 files changed, 422 insertions, 49 deletions
diff --git a/libexec/tftpd/tests/functional.c b/libexec/tftpd/tests/functional.c
index 58fd4cfc696b..8c35daf5cd65 100644
--- a/libexec/tftpd/tests/functional.c
+++ b/libexec/tftpd/tests/functional.c
@@ -38,6 +38,7 @@ __FBSDID("$FreeBSD$");
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
+#include <stdalign.h>
#include <stdio.h>
#include <unistd.h>
@@ -89,6 +90,13 @@ recv_ack(uint16_t blocknum)
RECV(hdr, NULL, 0);
}
+static void
+recv_oack(const char *options, size_t options_len)
+{
+ char hdr[] = {0, 6};
+ RECV(hdr, options, options_len);
+}
+
/*
* Receive a data packet from tftpd
* @param blocknum Expected block number to be received
@@ -159,6 +167,11 @@ send_ack(uint16_t blocknum)
}
+/*
+ * build an option string
+ */
+#define OPTION_STR(name, value) name "\000" value "\000"
+
/*
* send a read request to tftpd.
* @param filename filename as a string, absolute or relative
@@ -166,6 +179,11 @@ send_ack(uint16_t blocknum)
*/
#define SEND_RRQ(filename, mode) SEND_STR("\0\001" filename "\0" mode "\0")
+/*
+ * send a read request with options
+ */
+#define SEND_RRQ_OPT(filename, mode, options) SEND_STR("\0\001" filename "\0" mode "\000" options)
+
/*
* send a write request to tftpd.
* @param filename filename as a string, absolute or relative
@@ -173,6 +191,11 @@ send_ack(uint16_t blocknum)
*/
#define SEND_WRQ(filename, mode) SEND_STR("\0\002" filename "\0" mode "\0")
+/*
+ * send a write request with options
+ */
+#define SEND_WRQ_OPT(filename, mode, options) SEND_STR("\0\002" filename "\0" mode "\000" options)
+
/* Define a test case, for both IPv4 and IPv6 */
#define TFTPD_TC_DEFINE(name, head, ...) \
static void \
@@ -573,6 +596,32 @@ TFTPD_TC_DEFINE(rrq_medium,)
}
/*
+ * Read a medium file with a window size of 2.
+ */
+TFTPD_TC_DEFINE(rrq_medium_window,)
+{
+ int fd;
+ size_t i;
+ uint32_t contents[192];
+ char options[] = OPTION_STR("windowsize", "2");
+
+ for (i = 0; i < nitems(contents); i++)
+ contents[i] = i;
+
+ fd = open("medium.txt", O_RDWR | O_CREAT, 0644);
+ ATF_REQUIRE(fd >= 0);
+ write_all(fd, contents, sizeof(contents));
+ close(fd);
+
+ SEND_RRQ_OPT("medium.txt", "octet", OPTION_STR("windowsize", "2"));
+ recv_oack(options, sizeof(options) - 1);
+ send_ack(0);
+ recv_data(1, (const char*)&contents[0], 512);
+ recv_data(2, (const char*)&contents[128], 256);
+ send_ack(2);
+}
+
+/*
* Read a file in netascii format
*/
TFTPD_TC_DEFINE(rrq_netascii,)
@@ -652,6 +701,59 @@ TFTPD_TC_DEFINE(rrq_small,)
}
/*
+ * Read a file following the example in RFC 7440.
+ */
+TFTPD_TC_DEFINE(rrq_window_rfc7440,)
+{
+ int fd;
+ size_t i;
+ char options[] = OPTION_STR("windowsize", "4");
+ alignas(uint32_t) char contents[13 * 512 - 4];
+ uint32_t *u32p;
+
+ u32p = (uint32_t *)contents;
+ for (i = 0; i < sizeof(contents) / sizeof(uint32_t); i++)
+ u32p[i] = i;
+
+ fd = open("rfc7440.txt", O_RDWR | O_CREAT, 0644);
+ ATF_REQUIRE(fd >= 0);
+ write_all(fd, contents, sizeof(contents));
+ close(fd);
+
+ SEND_RRQ_OPT("rfc7440.txt", "octet", OPTION_STR("windowsize", "4"));
+ recv_oack(options, sizeof(options) - 1);
+ send_ack(0);
+ recv_data(1, &contents[0 * 512], 512);
+ recv_data(2, &contents[1 * 512], 512);
+ recv_data(3, &contents[2 * 512], 512);
+ recv_data(4, &contents[3 * 512], 512);
+ send_ack(4);
+ recv_data(5, &contents[4 * 512], 512);
+ recv_data(6, &contents[5 * 512], 512);
+ recv_data(7, &contents[6 * 512], 512);
+ recv_data(8, &contents[7 * 512], 512);
+
+ /* ACK 5 as if 6-8 were dropped. */
+ send_ack(5);
+ recv_data(6, &contents[5 * 512], 512);
+ recv_data(7, &contents[6 * 512], 512);
+ recv_data(8, &contents[7 * 512], 512);
+ recv_data(9, &contents[8 * 512], 512);
+ send_ack(9);
+ recv_data(10, &contents[9 * 512], 512);
+ recv_data(11, &contents[10 * 512], 512);
+ recv_data(12, &contents[11 * 512], 512);
+ recv_data(13, &contents[12 * 512], 508);
+
+ /* Drop ACK and after timeout receive 10-13. */
+ recv_data(10, &contents[9 * 512], 512);
+ recv_data(11, &contents[10 * 512], 512);
+ recv_data(12, &contents[11 * 512], 512);
+ recv_data(13, &contents[12 * 512], 508);
+ send_ack(13);
+}
+
+/*
* Try to transfer a file with an unknown mode.
*/
TFTPD_TC_DEFINE(unknown_modes,)
@@ -872,6 +974,38 @@ TFTPD_TC_DEFINE(wrq_medium,)
}
/*
+ * Write a medium file with a window size of 2.
+ */
+TFTPD_TC_DEFINE(wrq_medium_window,)
+{
+ int fd;
+ size_t i;
+ ssize_t r;
+ uint32_t contents[192];
+ char buffer[1024];
+ char options[] = OPTION_STR("windowsize", "2");
+
+ for (i = 0; i < nitems(contents); i++)
+ contents[i] = i;
+
+ fd = open("medium.txt", O_RDWR | O_CREAT, 0666);
+ ATF_REQUIRE(fd >= 0);
+ close(fd);
+
+ SEND_WRQ_OPT("medium.txt", "octet", OPTION_STR("windowsize", "2"));
+ recv_oack(options, sizeof(options) - 1);
+ send_data(1, (const char*)&contents[0], 512);
+ send_data(2, (const char*)&contents[128], 256);
+ recv_ack(2);
+
+ fd = open("medium.txt", O_RDONLY);
+ ATF_REQUIRE(fd >= 0);
+ r = read(fd, buffer, sizeof(buffer));
+ close(fd);
+ require_bufeq((const char*)contents, 768, buffer, r);
+}
+
+/*
* Write a file in netascii format
*/
TFTPD_TC_DEFINE(wrq_netascii,)
@@ -965,6 +1099,70 @@ TFTPD_TC_DEFINE(wrq_truncate,)
ATF_REQUIRE_EQ(sb.st_size, 0);
}
+/*
+ * Write a file following the example in RFC 7440.
+ */
+TFTPD_TC_DEFINE(wrq_window_rfc7440,)
+{
+ int fd;
+ size_t i;
+ ssize_t r;
+ char options[] = OPTION_STR("windowsize", "4");
+ alignas(uint32_t) char contents[13 * 512 - 4];
+ char buffer[sizeof(contents)];
+ uint32_t *u32p;
+
+ u32p = (uint32_t *)contents;
+ for (i = 0; i < sizeof(contents) / sizeof(uint32_t); i++)
+ u32p[i] = i;
+
+ fd = open("rfc7440.txt", O_RDWR | O_CREAT, 0666);
+ ATF_REQUIRE(fd >= 0);
+ close(fd);
+
+ SEND_WRQ_OPT("rfc7440.txt", "octet", OPTION_STR("windowsize", "4"));
+ recv_oack(options, sizeof(options) - 1);
+ send_data(1, &contents[0 * 512], 512);
+ send_data(2, &contents[1 * 512], 512);
+ send_data(3, &contents[2 * 512], 512);
+ send_data(4, &contents[3 * 512], 512);
+ recv_ack(4);
+ send_data(5, &contents[4 * 512], 512);
+
+ /* Drop 6-8. */
+ recv_ack(5);
+ send_data(6, &contents[5 * 512], 512);
+ send_data(7, &contents[6 * 512], 512);
+ send_data(8, &contents[7 * 512], 512);
+ send_data(9, &contents[8 * 512], 512);
+ recv_ack(9);
+
+ /* Drop 11. */
+ send_data(10, &contents[9 * 512], 512);
+ send_data(12, &contents[11 * 512], 512);
+
+ /*
+ * We can't send 13 here as tftpd has probably already seen 12
+ * and sent the ACK of 10 if running locally. While it would
+ * recover by sending another ACK of 10, our state machine
+ * would be out of sync.
+ */
+
+ /* Ignore ACK for 10 and resend 10-13. */
+ recv_ack(10);
+ send_data(10, &contents[9 * 512], 512);
+ send_data(11, &contents[10 * 512], 512);
+ send_data(12, &contents[11 * 512], 512);
+ send_data(13, &contents[12 * 512], 508);
+ recv_ack(13);
+
+ fd = open("rfc7440.txt", O_RDONLY);
+ ATF_REQUIRE(fd >= 0);
+ r = read(fd, buffer, sizeof(buffer));
+ close(fd);
+ require_bufeq(contents, sizeof(contents), buffer, r);
+}
+
/*
* Main
@@ -981,10 +1179,12 @@ ATF_TP_ADD_TCS(tp)
TFTPD_TC_ADD(tp, rrq_eaccess);
TFTPD_TC_ADD(tp, rrq_empty);
TFTPD_TC_ADD(tp, rrq_medium);
+ TFTPD_TC_ADD(tp, rrq_medium_window);
TFTPD_TC_ADD(tp, rrq_netascii);
TFTPD_TC_ADD(tp, rrq_nonexistent);
TFTPD_TC_ADD(tp, rrq_path_max);
TFTPD_TC_ADD(tp, rrq_small);
+ TFTPD_TC_ADD(tp, rrq_window_rfc7440);
TFTPD_TC_ADD(tp, unknown_modes);
TFTPD_TC_ADD(tp, unknown_opcode);
TFTPD_TC_ADD(tp, w_flag);
@@ -994,10 +1194,12 @@ ATF_TP_ADD_TCS(tp)
TFTPD_TC_ADD(tp, wrq_eaccess);
TFTPD_TC_ADD(tp, wrq_eaccess_world_readable);
TFTPD_TC_ADD(tp, wrq_medium);
+ TFTPD_TC_ADD(tp, wrq_medium_window);
TFTPD_TC_ADD(tp, wrq_netascii);
TFTPD_TC_ADD(tp, wrq_nonexistent);
TFTPD_TC_ADD(tp, wrq_small);
TFTPD_TC_ADD(tp, wrq_truncate);
+ TFTPD_TC_ADD(tp, wrq_window_rfc7440);
return (atf_no_error());
}
diff --git a/libexec/tftpd/tftp-file.c b/libexec/tftpd/tftp-file.c
index a8e7bcf9228b..4ca075836f8d 100644
--- a/libexec/tftpd/tftp-file.c
+++ b/libexec/tftpd/tftp-file.c
@@ -214,6 +214,20 @@ write_close(void)
return 0;
}
+off_t
+tell_file(void)
+{
+
+ return ftello(file);
+}
+
+int
+seek_file(off_t offset)
+{
+
+ return fseeko(file, offset, SEEK_SET);
+}
+
int
read_init(int fd, FILE *f, const char *mode)
{
diff --git a/libexec/tftpd/tftp-file.h b/libexec/tftpd/tftp-file.h
index f1f6397a92f5..c9ce5e33b723 100644
--- a/libexec/tftpd/tftp-file.h
+++ b/libexec/tftpd/tftp-file.h
@@ -36,4 +36,7 @@ int read_init(int fd, FILE *f, const char *mode);
size_t read_file(char *buffer, int count);
int read_close(void);
+int seek_file(off_t offset);
+off_t tell_file(void);
+
int synchnet(int peer);
diff --git a/libexec/tftpd/tftp-options.c b/libexec/tftpd/tftp-options.c
index 78bdcfae7d33..10fb112654fd 100644
--- a/libexec/tftpd/tftp-options.c
+++ b/libexec/tftpd/tftp-options.c
@@ -56,6 +56,7 @@ struct options options[] = {
{ "blksize", NULL, NULL, option_blksize, 1 },
{ "blksize2", NULL, NULL, option_blksize2, 0 },
{ "rollover", NULL, NULL, option_rollover, 0 },
+ { "windowsize", NULL, NULL, option_windowsize, 1 },
{ NULL, NULL, NULL, NULL, 0 }
};
@@ -275,6 +276,41 @@ option_blksize2(int peer __unused)
return (0);
}
+int
+option_windowsize(int peer)
+{
+ int size;
+
+ if (options[OPT_WINDOWSIZE].o_request == NULL)
+ return (0);
+
+ size = atoi(options[OPT_WINDOWSIZE].o_request);
+ if (size < WINDOWSIZE_MIN || size > WINDOWSIZE_MAX) {
+ if (acting_as_client) {
+ tftp_log(LOG_ERR,
+ "Invalid windowsize (%d blocks), aborting",
+ size);
+ send_error(peer, EBADOP);
+ return (1);
+ } else {
+ tftp_log(LOG_WARNING,
+ "Invalid windowsize (%d blocks), ignoring request",
+ size);
+ return (0);
+ }
+ }
+
+ /* XXX: Should force a windowsize of 1 for non-seekable files. */
+ asprintf(&options[OPT_WINDOWSIZE].o_reply, "%d", size);
+ windowsize = size;
+
+ if (debug&DEBUG_OPTIONS)
+ tftp_log(LOG_DEBUG, "Setting windowsize to '%s'",
+ options[OPT_WINDOWSIZE].o_reply);
+
+ return (0);
+}
+
/*
* Append the available options to the header
*/
diff --git a/libexec/tftpd/tftp-options.h b/libexec/tftpd/tftp-options.h
index 8768d1ceb3d4..713cbb12e439 100644
--- a/libexec/tftpd/tftp-options.h
+++ b/libexec/tftpd/tftp-options.h
@@ -42,6 +42,7 @@ int option_timeout(int peer);
int option_blksize(int peer);
int option_blksize2(int peer);
int option_rollover(int peer);
+int option_windowsize(int peer);
extern int options_extra_enabled;
extern int options_rfc_enabled;
@@ -61,4 +62,5 @@ enum opt_enum {
OPT_BLKSIZE,
OPT_BLKSIZE2,
OPT_ROLLOVER,
+ OPT_WINDOWSIZE,
};
diff --git a/libexec/tftpd/tftp-transfer.c b/libexec/tftpd/tftp-transfer.c
index c2c9e0793354..67b2289305a0 100644
--- a/libexec/tftpd/tftp-transfer.c
+++ b/libexec/tftpd/tftp-transfer.c
@@ -48,6 +48,12 @@ __FBSDID("$FreeBSD$");
#include "tftp-options.h"
#include "tftp-transfer.h"
+struct block_data {
+ off_t offset;
+ uint16_t block;
+ int size;
+};
+
/*
* Send a file via the TFTP data session.
*/
@@ -55,54 +61,73 @@ void
tftp_send(int peer, uint16_t *block, struct tftp_stats *ts)
{
struct tftphdr *rp;
- int size, n_data, n_ack, try;
- uint16_t oldblock;
+ int size, n_data, n_ack, sendtry, acktry;
+ u_int i, j;
+ uint16_t oldblock, windowblock;
char sendbuffer[MAXPKTSIZE];
char recvbuffer[MAXPKTSIZE];
+ struct block_data window[WINDOWSIZE_MAX];
rp = (struct tftphdr *)recvbuffer;
*block = 1;
ts->amount = 0;
+ windowblock = 0;
+ acktry = 0;
do {
+read_block:
if (debug&DEBUG_SIMPLE)
- tftp_log(LOG_DEBUG, "Sending block %d", *block);
+ tftp_log(LOG_DEBUG, "Sending block %d (window block %d)",
+ *block, windowblock);
+ window[windowblock].offset = tell_file();
+ window[windowblock].block = *block;
size = read_file(sendbuffer, segsize);
if (size < 0) {
tftp_log(LOG_ERR, "read_file returned %d", size);
send_error(peer, errno + 100);
goto abort;
}
+ window[windowblock].size = size;
+ windowblock++;
- for (try = 0; ; try++) {
+ for (sendtry = 0; ; sendtry++) {
n_data = send_data(peer, *block, sendbuffer, size);
- if (n_data > 0) {
- if (try == maxtimeouts) {
- tftp_log(LOG_ERR,
- "Cannot send DATA packet #%d, "
- "giving up", *block);
- return;
- }
+ if (n_data == 0)
+ break;
+
+ if (sendtry == maxtimeouts) {
tftp_log(LOG_ERR,
- "Cannot send DATA packet #%d, trying again",
- *block);
- continue;
+ "Cannot send DATA packet #%d, "
+ "giving up", *block);
+ return;
}
+ tftp_log(LOG_ERR,
+ "Cannot send DATA packet #%d, trying again",
+ *block);
+ }
+ /* Only check for ACK for last block in window. */
+ if (windowblock == windowsize || size != segsize) {
n_ack = receive_packet(peer, recvbuffer,
MAXPKTSIZE, NULL, timeoutpacket);
if (n_ack < 0) {
if (n_ack == RP_TIMEOUT) {
- if (try == maxtimeouts) {
+ if (acktry == maxtimeouts) {
tftp_log(LOG_ERR,
"Timeout #%d send ACK %d "
- "giving up", try, *block);
+ "giving up", acktry, *block);
return;
}
tftp_log(LOG_WARNING,
"Timeout #%d on ACK %d",
- try, *block);
- continue;
+ acktry, *block);
+
+ acktry++;
+ ts->retries++;
+ seek_file(window[0].offset);
+ *block = window[0].block;
+ windowblock = 0;
+ goto read_block;
}
/* Either read failure or ERROR packet */
@@ -112,18 +137,60 @@ tftp_send(int peer, uint16_t *block, struct tftp_stats *ts)
goto abort;
}
if (rp->th_opcode == ACK) {
- ts->blocks++;
- if (rp->th_block == *block) {
- ts->amount += size;
- break;
+ /*
+ * Look for the ACKed block in our open
+ * window.
+ */
+ for (i = 0; i < windowblock; i++) {
+ if (rp->th_block == window[i].block)
+ break;
}
- /* Re-synchronize with the other side */
- (void) synchnet(peer);
- if (rp->th_block == (*block - 1)) {
+ if (i == windowblock) {
+ /* Did not recognize ACK. */
+ if (debug&DEBUG_SIMPLE)
+ tftp_log(LOG_DEBUG,
+ "ACK %d out of window",
+ rp->th_block);
+
+ /* Re-synchronize with the other side */
+ (void) synchnet(peer);
+
+ /* Resend the current window. */
ts->retries++;
- continue;
+ seek_file(window[0].offset);
+ *block = window[0].block;
+ windowblock = 0;
+ goto read_block;
+ }
+
+ /* ACKed at least some data. */
+ acktry = 0;
+ for (j = 0; j <= i; j++) {
+ if (debug&DEBUG_SIMPLE)
+ tftp_log(LOG_DEBUG,
+ "ACKed block %d",
+ window[j].block);
+ ts->blocks++;
+ ts->amount += window[j].size;
+ }
+
+ /*
+ * Partial ACK. Rewind state to first
+ * un-ACKed block.
+ */
+ if (i + 1 != windowblock) {
+ if (debug&DEBUG_SIMPLE)
+ tftp_log(LOG_DEBUG,
+ "Partial ACK");
+ seek_file(window[i + 1].offset);
+ *block = window[i + 1].block;
+ windowblock = 0;
+ ts->retries++;
+ goto read_block;
}
+
+ windowblock = 0;
}
}
@@ -161,31 +228,35 @@ tftp_receive(int peer, uint16_t *block, struct tftp_stats *ts,
struct tftphdr *firstblock, size_t fb_size)
{
struct tftphdr *rp;
- uint16_t oldblock;
- int n_data, n_ack, writesize, i, retry;
+ uint16_t oldblock, windowstart;
+ int n_data, n_ack, writesize, i, retry, windowblock;
char recvbuffer[MAXPKTSIZE];
ts->amount = 0;
+ windowblock = 0;
if (firstblock != NULL) {
writesize = write_file(firstblock->th_data, fb_size);
ts->amount += writesize;
- for (i = 0; ; i++) {
- n_ack = send_ack(peer, *block);
- if (n_ack > 0) {
- if (i == maxtimeouts) {
+ windowblock++;
+ if (windowsize == 1 || fb_size != segsize) {
+ for (i = 0; ; i++) {
+ n_ack = send_ack(peer, *block);
+ if (n_ack > 0) {
+ if (i == maxtimeouts) {
+ tftp_log(LOG_ERR,
+ "Cannot send ACK packet #%d, "
+ "giving up", *block);
+ return;
+ }
tftp_log(LOG_ERR,
- "Cannot send ACK packet #%d, "
- "giving up", *block);
- return;
+ "Cannot send ACK packet #%d, trying again",
+ *block);
+ continue;
}
- tftp_log(LOG_ERR,
- "Cannot send ACK packet #%d, trying again",
- *block);
- continue;
- }
- break;
+ break;
+ }
}
if (fb_size != segsize) {
@@ -216,7 +287,8 @@ tftp_receive(int peer, uint16_t *block, struct tftp_stats *ts,
for (retry = 0; ; retry++) {
if (debug&DEBUG_SIMPLE)
tftp_log(LOG_DEBUG,
- "Receiving DATA block %d", *block);
+ "Receiving DATA block %d (window block %d)",
+ *block, windowblock);
n_data = receive_packet(peer, recvbuffer,
MAXPKTSIZE, NULL, timeoutpacket);
@@ -232,6 +304,7 @@ tftp_receive(int peer, uint16_t *block, struct tftp_stats *ts,
"Timeout #%d on DATA block %d",
retry, *block);
send_ack(peer, oldblock);
+ windowblock = 0;
continue;
}
@@ -247,18 +320,41 @@ tftp_receive(int peer, uint16_t *block, struct tftp_stats *ts,
if (rp->th_block == *block)
break;
+ /*
+ * Ignore duplicate blocks within the
+ * window.
+ *
+ * This does not handle duplicate
+ * blocks during a rollover as
+ * gracefully, but that should still
+ * recover eventually.
+ */
+ if (*block > windowsize)
+ windowstart = *block - windowsize;
+ else
+ windowstart = 0;
+ if (rp->th_block > windowstart &&
+ rp->th_block < *block) {
+ if (debug&DEBUG_SIMPLE)
+ tftp_log(LOG_DEBUG,
+ "Ignoring duplicate DATA block %d",
+ rp->th_block);
+ windowblock++;
+ retry = 0;
+ continue;
+ }
+
tftp_log(LOG_WARNING,
"Expected DATA block %d, got block %d",
*block, rp->th_block);
/* Re-synchronize with the other side */
(void) synchnet(peer);
- if (rp->th_block == (*block-1)) {
- tftp_log(LOG_INFO, "Trying to sync");
- *block = oldblock;
- ts->retries++;
- goto send_ack; /* rexmit */
- }
+
+ tftp_log(LOG_INFO, "Trying to sync");
+ *block = oldblock;
+ ts->retries++;
+ goto send_ack; /* rexmit */
} else {
tftp_log(LOG_WARNING,
@@ -282,7 +378,11 @@ tftp_receive(int peer, uint16_t *block, struct tftp_stats *ts,
if (n_data != segsize)
write_close();
}
+ windowblock++;
+ /* Only send ACKs for the last block in the window. */
+ if (windowblock < windowsize && n_data == segsize)
+ continue;
send_ack:
for (i = 0; ; i++) {
n_ack = send_ack(peer, *block);
@@ -301,6 +401,9 @@ send_ack:
continue;
}
+ if (debug&DEBUG_SIMPLE)
+ tftp_log(LOG_DEBUG, "Sent ACK for %d", *block);
+ windowblock = 0;
break;
}
gettimeofday(&(ts->tstop), NULL);
diff --git a/libexec/tftpd/tftp-utils.c b/libexec/tftpd/tftp-utils.c
index 2111e35bb722..d706639d51e8 100644
--- a/libexec/tftpd/tftp-utils.c
+++ b/libexec/tftpd/tftp-utils.c
@@ -51,6 +51,7 @@ int timeoutnetwork = MAX_TIMEOUTS * TIMEOUT;
int maxtimeouts = MAX_TIMEOUTS;
uint16_t segsize = SEGSIZE;
uint16_t pktsize = SEGSIZE + 4;
+uint16_t windowsize = WINDOWSIZE;
int acting_as_client;
diff --git a/libexec/tftpd/tftp-utils.h b/libexec/tftpd/tftp-utils.h
index 479faa8175a7..0d251874f1fc 100644
--- a/libexec/tftpd/tftp-utils.h
+++ b/libexec/tftpd/tftp-utils.h
@@ -46,6 +46,11 @@ __FBSDID("$FreeBSD$");
#define TIMEOUT_MAX 255 /* Maximum timeout value */
#define MIN_TIMEOUTS 3
+/* For the windowsize option */
+#define WINDOWSIZE 1
+#define WINDOWSIZE_MIN 1
+#define WINDOWSIZE_MAX 65535
+
extern int timeoutpacket;
extern int timeoutnetwork;
extern int maxtimeouts;
@@ -53,6 +58,7 @@ int settimeouts(int timeoutpacket, int timeoutnetwork, int maxtimeouts);
extern uint16_t segsize;
extern uint16_t pktsize;
+extern uint16_t windowsize;
extern int acting_as_client;
diff --git a/libexec/tftpd/tftpd.8 b/libexec/tftpd/tftpd.8
index 0071264ef338..e4f5ab94a2fe 100644
--- a/libexec/tftpd/tftpd.8
+++ b/libexec/tftpd/tftpd.8
@@ -28,7 +28,7 @@
.\" @(#)tftpd.8 8.1 (Berkeley) 6/4/93
.\" $FreeBSD$
.\"
-.Dd June 22, 2011
+.Dd March 2, 2020
.Dt TFTPD 8
.Os
.Sh NAME
@@ -245,6 +245,9 @@ The following RFC's are supported:
.Rs
.%T RFC 2349: TFTP Timeout Interval and Transfer Size Options
.Re
+.Rs
+.%T RFC 7440: TFTP Windowsize Option
+.Re
.Pp
The non-standard
.Cm rollover
@@ -291,6 +294,9 @@ Edwin Groothuis <edwin@FreeBSD.org> performed a major rewrite of the
and
.Xr tftp 1
code to support RFC2348.
+.Pp
+Support for the windowsize option (RFC7440) was introduced in
+.Fx 13.0 .
.Sh NOTES
Files larger than 33,553,919 octets (65535 blocks, last one <512
octets) cannot be correctly transferred without client and server