Add --tftp-single-port option.

This commit is contained in:
Simon Kelley committed 2020-01-05 16:21:24 +00:00
1 parent 18a6bdd541
commit 66f62650c3
6 files changed
+113 -64

No files matched your search

+7 -6
View File
@@ -1107,7 +1107,7 @@ int main (int argc, char **argv)
#endif
/* must do this just before select(), when we know no
/* must do this just before do_poll(), when we know no
more calls to my_syslog() can occur */
set_log_writer();
@@ -1670,11 +1670,12 @@ static int set_dns_listeners(time_t now)
#ifdef HAVE_TFTP
int tftp = 0;
struct tftp_transfer *transfer;
for (transfer = daemon->tftp_trans; transfer; transfer = transfer->next)
{
tftp++;
poll_listen(transfer->sockfd, POLLIN);
}
if (!option_bool(OPT_SINGLE_PORT))
for (transfer = daemon->tftp_trans; transfer; transfer = transfer->next)
{
tftp++;
poll_listen(transfer->sockfd, POLLIN);
}
#endif
/* will we be able to get memory? */
+4 -1
View File
@@ -262,7 +262,8 @@ struct event_desc {
#define OPT_RAPID_COMMIT 57
#define OPT_UBUS 58
#define OPT_IGNORE_CLID 59
#define OPT_LAST 60
#define OPT_SINGLE_PORT 60
#define OPT_LAST 61
#define OPTION_BITS (sizeof(unsigned int)*8)
#define OPTION_SIZE ( (OPT_LAST/OPTION_BITS)+((OPT_LAST%OPTION_BITS)!=0) )
@@ -960,6 +961,8 @@ struct tftp_transfer {
unsigned int block, blocksize, expansion;
off_t offset;
union mysockaddr peer;
union all_addr source;
int if_index;
char opt_blocksize, opt_transize, netascii, carrylf;
struct tftp_file *file;
struct tftp_transfer *next;
+3
View File
@@ -168,6 +168,7 @@ struct myoption {
#define LOPT_CAA 356
#define LOPT_SHARED_NET 357
#define LOPT_IGNORE_CLID 358
#define LOPT_SINGLE_PORT 359
#ifdef HAVE_GETOPT_LONG
static const struct option opts[] =
@@ -258,6 +259,7 @@ static const struct myoption opts[] =
{ "tftp-max", 1, 0, LOPT_TFTP_MAX },
{ "tftp-mtu", 1, 0, LOPT_TFTP_MTU },
{ "tftp-lowercase", 0, 0, LOPT_TFTP_LC },
{ "tftp-single-port", 0, 0, LOPT_SINGLE_PORT },
{ "ptr-record", 1, 0, LOPT_PTR },
{ "naptr-record", 1, 0, LOPT_NAPTR },
{ "bridge-interface", 1, 0 , LOPT_BRIDGE },
@@ -458,6 +460,7 @@ static struct {
{ LOPT_NOBLOCK, OPT_TFTP_NOBLOCK, NULL, gettext_noop("Disable the TFTP blocksize extension."), NULL },
{ LOPT_TFTP_LC, OPT_TFTP_LC, NULL, gettext_noop("Convert TFTP filenames to lowercase"), NULL },
{ LOPT_TFTPPORTS, ARG_ONE, "<start>,<end>", gettext_noop("Ephemeral port range for use by TFTP transfers."), NULL },
{ LOPT_SINGLE_PORT, OPT_SINGLE_PORT, NULL, gettext_noop("Use only one port for TFTP server."), NULL },
{ LOPT_LOG_OPTS, OPT_LOG_OPTS, NULL, gettext_noop("Extra logging for DHCP."), NULL },
{ LOPT_MAX_LOGS, ARG_ONE, "[=<integer>]", gettext_noop("Enable async. logging; optionally set queue length."), NULL },
{ LOPT_REBIND, OPT_NO_REBIND, NULL, gettext_noop("Stop DNS rebinding. Filter private IP ranges when resolving."), NULL },
+91 -56
View File
@@ -18,6 +18,7 @@
#ifdef HAVE_TFTP
static void handle_tftp(time_t now, struct tftp_transfer *transfer, ssize_t len);
static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix);
static void free_transfer(struct tftp_transfer *transfer);
static ssize_t tftp_err(int err, char *packet, char *message, char *file);
@@ -50,7 +51,7 @@ void tftp_request(struct listener *listen, time_t now)
struct ifreq ifr;
int is_err = 1, if_index = 0, mtu = 0;
struct iname *tmp;
struct tftp_transfer *transfer;
struct tftp_transfer *transfer = NULL, **up;
int port = daemon->start_tftp_port; /* may be zero to use ephemeral port */
#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
int mtuflag = IP_PMTUDISC_DONT;
@@ -239,6 +240,25 @@ void tftp_request(struct listener *listen, time_t now)
if (mtu == 0)
mtu = daemon->tftp_mtu;
/* data transfer via server listening socket */
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))
{
if (ntohs(*((unsigned short *)packet)) == OP_RRQ)
{
/* Handle repeated RRQ or abandoned transfer from same host and port
by unlinking and reusing the struct transfer. */
*up = transfer->next;
break;
}
else
{
handle_tftp(now, transfer, len);
return;
}
}
if (name)
{
/* check for per-interface prefix */
@@ -264,16 +284,21 @@ void tftp_request(struct listener *listen, time_t now)
#endif
}
if (!(transfer = whine_malloc(sizeof(struct tftp_transfer))))
/* May reuse struct transfer from abandoned transfer in single port mode. */
if (!transfer && !(transfer = whine_malloc(sizeof(struct tftp_transfer))))
return;
if ((transfer->sockfd = socket(listen->family, SOCK_DGRAM, 0)) == -1)
if (option_bool(OPT_SINGLE_PORT))
transfer->sockfd = listen->tftpfd;
else if ((transfer->sockfd = socket(listen->family, SOCK_DGRAM, 0)) == -1)
{
free(transfer);
return;
}
transfer->peer = peer;
transfer->source = addra;
transfer->if_index = if_index;
transfer->timeout = now + 2;
transfer->backoff = 1;
transfer->block = 1;
@@ -286,7 +311,7 @@ void tftp_request(struct listener *listen, time_t now)
prettyprint_addr(&peer, daemon->addrbuff);
/* if we have a nailed-down range, iterate until we find a free one. */
while (1)
while (!option_bool(OPT_SINGLE_PORT))
{
if (bind(transfer->sockfd, &addr.sa, sa_len(&addr)) == -1 ||
#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
@@ -315,7 +340,7 @@ void tftp_request(struct listener *listen, time_t now)
p = packet + 2;
end = packet + len;
if (ntohs(*((unsigned short *)packet)) != OP_RRQ ||
!(filename = next(&p, end)) ||
!(mode = next(&p, end)) ||
@@ -439,9 +464,8 @@ void tftp_request(struct listener *listen, time_t now)
is_err = 0;
}
}
while (sendto(transfer->sockfd, packet, len, 0,
(struct sockaddr *)&peer, sa_len(&peer)) == -1 && errno == EINTR);
send_from(transfer->sockfd, !option_bool(OPT_SINGLE_PORT), packet, len, &peer, &addra, if_index);
if (is_err)
free_transfer(transfer);
@@ -538,60 +562,25 @@ static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix)
void check_tftp_listeners(time_t now)
{
struct tftp_transfer *transfer, *tmp, **up;
ssize_t len;
struct ack {
unsigned short op, block;
} *mess = (struct ack *)daemon->packet;
/* Check for activity on any existing transfers */
for (transfer = daemon->tftp_trans, up = &daemon->tftp_trans; transfer; transfer = tmp)
{
tmp = transfer->next;
prettyprint_addr(&transfer->peer, daemon->addrbuff);
/* In single port mode, all packets come via port 69 and tftp_request() */
if (!option_bool(OPT_SINGLE_PORT))
for (transfer = daemon->tftp_trans; transfer; transfer = transfer->next)
if (poll_check(transfer->sockfd, POLLIN))
{
/* we overwrote the buffer... */
daemon->srv_save = NULL;
if ((len = recv(transfer->sockfd, daemon->packet, daemon->packet_buff_sz, 0)) >= (ssize_t)sizeof(struct ack))
{
if (ntohs(mess->op) == OP_ACK && ntohs(mess->block) == (unsigned short)transfer->block)
{
/* Got ack, ensure we take the (re)transmit path */
transfer->timeout = now;
transfer->backoff = 0;
if (transfer->block++ != 0)
transfer->offset += transfer->blocksize - transfer->expansion;
}
else if (ntohs(mess->op) == OP_ERR)
{
char *p = daemon->packet + sizeof(struct ack);
char *end = daemon->packet + len;
char *err = next(&p, end);
/* Sanitise error message */
if (!err)
err = "";
else
sanitise(err);
my_syslog(MS_TFTP | LOG_ERR, _("error %d %s received from %s"),
(int)ntohs(mess->block), err,
daemon->addrbuff);
/* Got err, ensure we take abort */
transfer->timeout = now;
transfer->backoff = 100;
}
}
handle_tftp(now, transfer, recv(transfer->sockfd, daemon->packet, daemon->packet_buff_sz, 0));
}
for (transfer = daemon->tftp_trans, up = &daemon->tftp_trans; transfer; transfer = tmp)
{
tmp = transfer->next;
if (difftime(now, transfer->timeout) >= 0.0)
{
int endcon = 0;
ssize_t len;
/* timeout, retransmit */
transfer->timeout += 1 + (1<<transfer->backoff);
@@ -613,13 +602,14 @@ void check_tftp_listeners(time_t now)
}
if (len != 0)
while(sendto(transfer->sockfd, daemon->packet, len, 0,
(struct sockaddr *)&transfer->peer, sa_len(&transfer->peer)) == -1 && errno == EINTR);
send_from(transfer->sockfd, !option_bool(OPT_SINGLE_PORT), daemon->packet, len,
&transfer->peer, &transfer->source, transfer->if_index);
if (endcon || len == 0)
{
strcpy(daemon->namebuff, transfer->file->filename);
sanitise(daemon->namebuff);
prettyprint_addr(&transfer->peer, daemon->addrbuff);
my_syslog(MS_TFTP | LOG_INFO, endcon ? _("failed sending %s to %s") : _("sent %s to %s"), daemon->namebuff, daemon->addrbuff);
/* unlink */
*up = tmp;
@@ -638,15 +628,60 @@ void check_tftp_listeners(time_t now)
up = &transfer->next;
}
}
/* packet in daemon->packet as this is called. */
static void handle_tftp(time_t now, struct tftp_transfer *transfer, ssize_t len)
{
struct ack {
unsigned short op, block;
} *mess = (struct ack *)daemon->packet;
if (len >= (ssize_t)sizeof(struct ack))
{
if (ntohs(mess->op) == OP_ACK && ntohs(mess->block) == (unsigned short)transfer->block)
{
/* Got ack, ensure we take the (re)transmit path */
transfer->timeout = now;
transfer->backoff = 0;
if (transfer->block++ != 0)
transfer->offset += transfer->blocksize - transfer->expansion;
}
else if (ntohs(mess->op) == OP_ERR)
{
char *p = daemon->packet + sizeof(struct ack);
char *end = daemon->packet + len;
char *err = next(&p, end);
prettyprint_addr(&transfer->peer, daemon->addrbuff);
/* Sanitise error message */
if (!err)
err = "";
else
sanitise(err);
my_syslog(MS_TFTP | LOG_ERR, _("error %d %s received from %s"),
(int)ntohs(mess->block), err,
daemon->addrbuff);
/* Got err, ensure we take abort */
transfer->timeout = now;
transfer->backoff = 100;
}
}
}
static void free_transfer(struct tftp_transfer *transfer)
{
close(transfer->sockfd);
if (!option_bool(OPT_SINGLE_PORT))
close(transfer->sockfd);
if (transfer->file && (--transfer->file->refcount) == 0)
{
close(transfer->file->fd);
free(transfer->file);
}
free(transfer);
}