diff --git a/CHANGELOG b/CHANGELOG index 155fc96..6072921 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -49,7 +49,20 @@ version 2.85 source address errors from fatal to run-time. The error will be logged and communiction with the server not possible. + Change the method of allocation of random source ports for DNS. + Previously, without min-port or max-port configured, dnsmasq would + default to the compiled in defaults for those, which are 1024 and + 65535. Now, when neither are configured, it defaults instead to + the kernel's ephemeral port range, which is typically + 32768 to 60999 on Linux systems. This change eliminates the + possibility that dnsmasq may be using a registered port > 1024 + when a long-running daemon starts up and wishes to claim it. + This change does likely slighly reduce the number of random ports + and therefore the protection from reply spoofing. The older + behaviour can be restored using the min-port and max-port config + switches should that be a concern. + version 2.84 Fix a problem, introduced in 2.83, which could see DNS replies being sent via the wrong socket. On machines running both diff --git a/src/dnsmasq.c b/src/dnsmasq.c index d1806fc..fa9ae15 100644 --- a/src/dnsmasq.c +++ b/src/dnsmasq.c @@ -237,9 +237,16 @@ int main (int argc, char **argv) die(_("Ubus not available: set HAVE_UBUS in src/config.h"), NULL, EC_BADCONF); #endif + /* Handle only one of min_port/max_port being set. */ + if (daemon->min_port != 0 && daemon->max_port == 0) + daemon->max_port = MAX_PORT; + + if (daemon->max_port != 0 && daemon->min_port == 0) + daemon->min_port = MIN_PORT; + if (daemon->max_port < daemon->min_port) die(_("max_port cannot be smaller than min_port"), NULL, EC_BADCONF); - + now = dnsmasq_time(); if (daemon->auth_zones) diff --git a/src/network.c b/src/network.c index b4a8d7d..3600250 100644 --- a/src/network.c +++ b/src/network.c @@ -1310,9 +1310,9 @@ int local_bind(int fd, union mysockaddr *addr, char *intname, unsigned int ifind { union mysockaddr addr_copy = *addr; unsigned short port; - int tries = 1, done = 0; - unsigned int ports_avail = ((unsigned short)daemon->max_port - (unsigned short)daemon->min_port) + 1; - + int tries = 1; + unsigned short ports_avail = 1; + if (addr_copy.sa.sa_family == AF_INET) port = addr_copy.in.sin_port; else @@ -1321,34 +1321,43 @@ int local_bind(int fd, union mysockaddr *addr, char *intname, unsigned int ifind /* cannot set source _port_ for TCP connections. */ if (is_tcp) port = 0; - else if (port == 0) + else if (port == 0 && daemon->max_port != 0) { - /* Bind a random port within the range given by min-port and max-port */ + /* Bind a random port within the range given by min-port and max-port if either + or both are set. Otherwise use the OS's random ephemeral port allocation by + leaving port == 0 and tries == 1 */ + ports_avail = daemon->max_port - daemon->min_port + 1; tries = ports_avail < 30 ? 3 * ports_avail : 100; - port = htons(daemon->min_port + (rand16() % ((unsigned short)ports_avail))); + port = htons(daemon->min_port + (rand16() % ports_avail)); } - while (tries--) + while (1) { + /* elide bind() call if it's to port 0, address 0 */ if (addr_copy.sa.sa_family == AF_INET) - addr_copy.in.sin_port = port; - else - addr_copy.in6.sin6_port = port; - - if (bind(fd, (struct sockaddr *)&addr_copy, sa_len(&addr_copy)) != -1) { - done = 1; - break; + if (port == 0 && addr_copy.in.sin_addr.s_addr == 0) + break; + addr_copy.in.sin_port = port; + } + else + { + if (port == 0 && IN6_IS_ADDR_UNSPECIFIED(&addr_copy.in6.sin6_addr)) + break; + addr_copy.in6.sin6_port = port; } - if (errno != EADDRINUSE && errno != EACCES) - return 0; + if (bind(fd, (struct sockaddr *)&addr_copy, sa_len(&addr_copy)) != -1) + break; - port = htons(daemon->min_port + (rand16() % ((unsigned short)ports_avail))); - } + if (errno != EADDRINUSE && errno != EACCES) + return 0; - if (!done) - return 0; + if (--tries == 0) + return 0; + + port = htons(daemon->min_port + (rand16() % ports_avail)); + } if (!is_tcp && ifindex > 0) { diff --git a/src/option.c b/src/option.c index cbe6e30..6de5914 100644 --- a/src/option.c +++ b/src/option.c @@ -5076,9 +5076,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->max_port = MAX_PORT; - daemon->min_port = MIN_PORT; - + #ifndef NO_ID add_txt("version.bind", "dnsmasq-" VERSION, 0 ); add_txt("authors.bind", "Simon Kelley", 0);