summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Kelley <simon@thekelleys.org.uk>2022-08-17 15:33:15 +0100
committerSimon Kelley <simon@thekelleys.org.uk>2022-09-06 22:43:33 +0100
commit24c3b5b3d49b52d4bc954b4a820a4c3ec8947b2c (patch)
treec14c42af15741a5398c2b2a895e6c55da9e940fa
parent4447d48bb9ad3c19234594f0a5eb81f7959cbb10 (diff)
downloaddnsmasq-24c3b5b3d49b52d4bc954b4a820a4c3ec8947b2c.tar.gz
Add --port-limit option.
By default, when sending a query via random ports to multiple upstream servers or retrying a query dnsmasq will use a single random port for all the tries/retries. This option allows a larger number of ports to be used, which can increase robustness in certain network configurations. Note that increasing this to more than two or three can have security and resource implications and should only be done with understanding of those.
-rw-r--r--man/dnsmasq.810
-rw-r--r--src/dnsmasq.h1
-rw-r--r--src/forward.c55
-rw-r--r--src/option.c9
4 files changed, 64 insertions, 11 deletions
diff --git a/man/dnsmasq.8 b/man/dnsmasq.8
index adb10a4..e0a1117 100644
--- a/man/dnsmasq.8
+++ b/man/dnsmasq.8
@@ -180,7 +180,15 @@ specific UDP port <query_port> instead of using random ports. NOTE
that using this option will make dnsmasq less secure against DNS
spoofing attacks but it may be faster and use less resources. Setting this option
to zero makes dnsmasq use a single port allocated to it by the
-OS: this was the default behaviour in versions prior to 2.43.
+OS: this was the default behaviour in versions prior to 2.43.
+.TP
+.B --port-limit=<#ports>
+By default, when sending a query via random ports to multiple upstream servers or
+retrying a query dnsmasq will use a single random port for all the tries/retries.
+This option allows a larger number of ports to be used, which can increase robustness
+in certain network configurations. Note that increasing this to more than
+two or three can have security and resource implications and should only
+be done with understanding of those.
.TP
.B --min-port=<port>
Do not use ports less than that given as source for outbound DNS
diff --git a/src/dnsmasq.h b/src/dnsmasq.h
index 36d17fe..a1bbdc0 100644
--- a/src/dnsmasq.h
+++ b/src/dnsmasq.h
@@ -1139,6 +1139,7 @@ extern struct daemon {
int log_fac; /* log facility */
char *log_file; /* optional log file */
int max_logs; /* queue limit */
+ int randport_limit; /* Maximum number of source ports for query. */
int cachesize, ftabsize;
int port, query_port, min_port, max_port;
unsigned long local_ttl, neg_ttl, max_ttl, min_cache_ttl, max_cache_ttl, auth_ttl, dhcp_ttl, use_dhcp_ttl;
diff --git a/src/forward.c b/src/forward.c
index 8562b2d..801e93a 100644
--- a/src/forward.c
+++ b/src/forward.c
@@ -2311,7 +2311,7 @@ int allocate_rfd(struct randfd_list **fdlp, struct server *serv)
{
static int finger = 0;
int i, j = 0;
- struct randfd_list *rfl;
+ struct randfd_list **up, *rfl, *found, **found_link;
struct randfd *rfd = NULL;
int fd = 0;
@@ -2319,17 +2319,27 @@ int allocate_rfd(struct randfd_list **fdlp, struct server *serv)
if (serv->sfd)
return serv->sfd->fd;
- /* existing suitable random port socket linked to this transaction? */
- for (rfl = *fdlp; rfl; rfl = rfl->next)
+ /* existing suitable random port socket linked to this transaction?
+ Find the last one in the list and count how many there are. */
+ for (found = NULL, found_link = NULL, i = 0, up = fdlp, rfl = *fdlp; rfl; up = &rfl->next, rfl = rfl->next)
if (server_isequal(serv, rfl->rfd->serv))
- return rfl->rfd->fd;
+ {
+ i++;
+ found = rfl;
+ found_link = up;
+ }
- /* No. need new link. */
- if ((rfl = daemon->rfl_spare))
- daemon->rfl_spare = rfl->next;
- else if (!(rfl = whine_malloc(sizeof(struct randfd_list))))
- return -1;
-
+ /* We have the maximum number for this query already. Promote
+ the last one on the list to the head, to circulate them,
+ and return it. */
+ if (found && i >= daemon->randport_limit)
+ {
+ *found_link = found->next;
+ found->next = *fdlp;
+ *fdlp = found;
+ return found->rfd->fd;
+ }
+
/* limit the number of sockets we have open to avoid starvation of
(eg) TFTP. Once we have a reasonable number, randomness should be OK */
for (i = 0; i < daemon->numrrand; i++)
@@ -2344,6 +2354,31 @@ int allocate_rfd(struct randfd_list **fdlp, struct server *serv)
}
break;
}
+
+ /* We've hit the global limit on sockets before hitting the query limit,
+ use an exsiting socket as source in that case. */
+ if (!rfd && found)
+ {
+ *found_link = found->next;
+ found->next = *fdlp;
+ *fdlp = found;
+ return found->rfd->fd;
+ }
+
+ /* No good existing. Need new link. */
+ if ((rfl = daemon->rfl_spare))
+ daemon->rfl_spare = rfl->next;
+ else if (!(rfl = whine_malloc(sizeof(struct randfd_list))))
+ {
+ /* malloc failed, don't leak allocated sock */
+ if (rfd)
+ {
+ close(rfd->fd);
+ rfd->refcount = 0;
+ }
+
+ return -1;
+ }
/* No free ones or cannot get new socket, grab an existing one */
if (!rfd)
diff --git a/src/option.c b/src/option.c
index c5e8cb4..4030465 100644
--- a/src/option.c
+++ b/src/option.c
@@ -181,6 +181,7 @@ struct myoption {
#define LOPT_STRIP_MAC 372
#define LOPT_CONF_OPT 373
#define LOPT_CONF_SCRIPT 374
+#define LOPT_RANDPORT_LIM 375
#ifdef HAVE_GETOPT_LONG
static const struct option opts[] =
@@ -366,6 +367,7 @@ static const struct myoption opts[] =
{ "log-debug", 0, 0, LOPT_LOG_DEBUG },
{ "umbrella", 2, 0, LOPT_UMBRELLA },
{ "quiet-tftp", 0, 0, LOPT_QUIET_TFTP },
+ { "port-limit", 1, 0, LOPT_RANDPORT_LIM },
{ NULL, 0, 0, 0 }
};
@@ -430,6 +432,7 @@ static struct {
{ 'P', ARG_ONE, "<integer>", gettext_noop("Maximum supported UDP packet size for EDNS.0 (defaults to %s)."), "*" },
{ 'q', ARG_DUP, NULL, gettext_noop("Log DNS queries."), NULL },
{ 'Q', ARG_ONE, "<integer>", gettext_noop("Force the originating port for upstream DNS queries."), NULL },
+ { LOPT_RANDPORT_LIM, ARG_ONE, "#ports", gettext_noop("Set maximum number of random originating ports for a query."), NULL },
{ 'R', OPT_NO_RESOLV, NULL, gettext_noop("Do NOT read resolv.conf."), NULL },
{ 'r', ARG_DUP, "<path>", gettext_noop("Specify path to resolv.conf (defaults to %s)."), RESOLVFILE },
{ LOPT_SERVERS_FILE, ARG_ONE, "<path>", gettext_noop("Specify path to file with server= options"), NULL },
@@ -3179,6 +3182,11 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
if (daemon->query_port == 0)
daemon->osport = 1;
break;
+
+ case LOPT_RANDPORT_LIM: /* --port-limit */
+ if (!atoi_check(arg, &daemon->randport_limit) || (daemon->randport_limit < 1))
+ ret_err(gen_err);
+ break;
case 'T': /* --local-ttl */
case LOPT_NEGTTL: /* --neg-ttl */
@@ -5494,6 +5502,7 @@ void read_opts(int argc, char **argv, char *compile_opts)
daemon->soa_refresh = SOA_REFRESH;
daemon->soa_retry = SOA_RETRY;
daemon->soa_expiry = SOA_EXPIRY;
+ daemon->randport_limit = 1;
#ifndef NO_ID
add_txt("version.bind", "dnsmasq-" VERSION, 0 );