mirror of
https://github.com/pi-hole/dnsmasq.git
synced 2025-12-19 18:28:25 +00:00
Add --tftp-single-port option.
This commit is contained in:
@@ -60,6 +60,8 @@ version 2.81
|
||||
Fix bug which caused very rarely caused zero-length DHCPv6 packets.
|
||||
Thanks to Dereck Higgins for spotting this.
|
||||
|
||||
Add --tftp-single-port option.
|
||||
|
||||
|
||||
version 2.80
|
||||
Add support for RFC 4039 DHCP rapid commit. Thanks to Ashram Method
|
||||
|
||||
@@ -1943,7 +1943,12 @@ specifies a range of ports for use by TFTP transfers. This can be
|
||||
useful when TFTP has to traverse a firewall. The start of the range
|
||||
cannot be lower than 1025 unless dnsmasq is running as root. The number
|
||||
of concurrent TFTP connections is limited by the size of the port range.
|
||||
.TP
|
||||
.TP
|
||||
.B --tftp-single-port
|
||||
Run in a mode where the TFTP server uses ONLY the well-known port (69) for its end
|
||||
of the TFTP transfer. This allows TFTP to work when there in NAT is the path between client and server. Note that
|
||||
this is not strictly compliant with the RFCs specifying the TFTP protocol: use at your own risk.
|
||||
.TP
|
||||
.B \-C, --conf-file=<file>
|
||||
Specify a configuration file. The presence of this option stops dnsmasq from reading the default configuration
|
||||
file (normally /etc/dnsmasq.conf). Multiple files may be specified by repeating the option
|
||||
|
||||
@@ -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? */
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 },
|
||||
|
||||
147
src/tftp.c
147
src/tftp.c
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user