Tweaks to TFTP.

Fail on overlarge files (block numbers are limited to 16 bits)
Honour tftp-max setting in single port mode.
Tweak timeouts, and fix logic which suppresses errors if the
last ACK is missing.
This commit is contained in:
Simon Kelley
2020-01-06 23:39:33 +00:00
parent a914d0aa6a
commit 2ac4cf0146
2 changed files with 48 additions and 24 deletions

View File

@@ -956,10 +956,11 @@ int main (int argc, char **argv)
{ {
struct tftp_prefix *p; struct tftp_prefix *p;
my_syslog(MS_TFTP | LOG_INFO, "TFTP %s%s %s", my_syslog(MS_TFTP | LOG_INFO, "TFTP %s%s %s %s",
daemon->tftp_prefix ? _("root is ") : _("enabled"), daemon->tftp_prefix ? _("root is ") : _("enabled"),
daemon->tftp_prefix ? daemon->tftp_prefix: "", daemon->tftp_prefix ? daemon->tftp_prefix : "",
option_bool(OPT_TFTP_SECURE) ? _("secure mode") : ""); option_bool(OPT_TFTP_SECURE) ? _("secure mode") : "",
option_bool(OPT_SINGLE_PORT) ? _("single port mode") : "");
if (tftp_prefix_missing) if (tftp_prefix_missing)
my_syslog(MS_TFTP | LOG_WARNING, _("warning: %s inaccessible"), daemon->tftp_prefix); my_syslog(MS_TFTP | LOG_WARNING, _("warning: %s inaccessible"), daemon->tftp_prefix);
@@ -977,7 +978,7 @@ int main (int argc, char **argv)
if (max_fd < 0) if (max_fd < 0)
max_fd = 5; max_fd = 5;
else if (max_fd < 100) else if (max_fd < 100 && !option_bool(OPT_SINGLE_PORT))
max_fd = max_fd/2; max_fd = max_fd/2;
else else
max_fd = max_fd - 20; max_fd = max_fd - 20;
@@ -1707,6 +1708,7 @@ static int set_dns_listeners(time_t now)
} }
#ifdef HAVE_TFTP #ifdef HAVE_TFTP
/* tftp == 0 in single-port mode. */
if (tftp <= daemon->tftp_max && listener->tftpfd != -1) if (tftp <= daemon->tftp_max && listener->tftpfd != -1)
poll_listen(listener->tftpfd, POLLIN); poll_listen(listener->tftpfd, POLLIN);
#endif #endif

View File

@@ -242,23 +242,37 @@ void tftp_request(struct listener *listen, time_t now)
/* data transfer via server listening socket */ /* data transfer via server listening socket */
if (option_bool(OPT_SINGLE_PORT)) if (option_bool(OPT_SINGLE_PORT))
for (transfer = daemon->tftp_trans, up = &daemon->tftp_trans; transfer; up = &transfer->next, transfer = transfer->next) {
if (sockaddr_isequal(&peer, &transfer->peer)) int tftp_cnt;
for (tftp_cnt = 0, transfer = daemon->tftp_trans, up = &daemon->tftp_trans; transfer; up = &transfer->next, transfer = transfer->next)
{ {
if (ntohs(*((unsigned short *)packet)) == OP_RRQ) tftp_cnt++;
if (sockaddr_isequal(&peer, &transfer->peer))
{ {
/* Handle repeated RRQ or abandoned transfer from same host and port if (ntohs(*((unsigned short *)packet)) == OP_RRQ)
by unlinking and reusing the struct transfer. */ {
*up = transfer->next; /* Handle repeated RRQ or abandoned transfer from same host and port
break; by unlinking and reusing the struct transfer. */
} *up = transfer->next;
else break;
{ }
handle_tftp(now, transfer, len); else
return; {
handle_tftp(now, transfer, len);
return;
}
} }
} }
/* Enforce simultaneous transfer limit. In non-single-port mode
this is doene by not listening on the server socket when
too many transfers are in progress. */
if (!transfer && tftp_cnt >= daemon->tftp_max)
return;
}
if (name) if (name)
{ {
/* check for per-interface prefix */ /* check for per-interface prefix */
@@ -583,21 +597,28 @@ void check_tftp_listeners(time_t now)
ssize_t len; ssize_t len;
/* timeout, retransmit */ /* timeout, retransmit */
transfer->timeout += 1 + (1<<transfer->backoff); transfer->timeout += 1 + (1<<(transfer->backoff/2));
/* we overwrote the buffer... */ /* we overwrote the buffer... */
daemon->srv_save = NULL; daemon->srv_save = NULL;
if ((len = get_block(daemon->packet, transfer)) == -1) /* check for wrap of 16 bit block no. */
if (transfer->block == 0x10000)
{
len = tftp_err(ERR_ILL, daemon->packet, _("too many blocks"), NULL);
endcon = 1;
}
else if ((len = get_block(daemon->packet, transfer)) == -1)
{ {
len = tftp_err_oops(daemon->packet, transfer->file->filename); len = tftp_err_oops(daemon->packet, transfer->file->filename);
endcon = 1; endcon = 1;
} }
/* don't complain about timeout when we're awaiting the last else if (++transfer->backoff > 7)
ACK, some clients never send it */
else if (++transfer->backoff > 7 && len != 0)
{ {
endcon = 1; /* don't complain about timeout when we're awaiting the last
ACK, some clients never send it */
if (len == transfer->blocksize + 4)
endcon = 1;
len = 0; len = 0;
} }
@@ -720,7 +741,8 @@ static ssize_t tftp_err(int err, char *packet, char *message, char *file)
char *errstr = strerror(errno); char *errstr = strerror(errno);
memset(packet, 0, daemon->packet_buff_sz); memset(packet, 0, daemon->packet_buff_sz);
sanitise(file); if (file)
sanitise(file);
mess->op = htons(OP_ERR); mess->op = htons(OP_ERR);
mess->err = htons(err); mess->err = htons(err);