diff options
author | Simon Kelley <simon@thekelleys.org.uk> | 2022-09-09 17:09:32 +0100 |
---|---|---|
committer | Simon Kelley <simon@thekelleys.org.uk> | 2022-09-09 17:09:32 +0100 |
commit | 3f56bb8ba195f72a6bf1d572f8add07b4fc44ec0 (patch) | |
tree | f8c65ac06c7b063c8261065f75200dda0964e928 | |
parent | e518e87533345f53fb59e1b9e99994dd73eb8942 (diff) | |
download | dnsmasq-3f56bb8ba195f72a6bf1d572f8add07b4fc44ec0.tar.gz |
Second try at port-limit option.
1) It's expected to fail to bind a new source port when they
are scarce, suppress warning in log in this case.
2) Optimse bind_local when max_port - min_port is small. There's no
randomness in this case, so we try all possible source ports
rather than poking at random ones for an arbitrary number of tries.
3) In allocate_rfd() handle the case that all available source ports
are already open. In this case we need to pick an existing
socket/port to use, such that it has a different port from any we
already hold. This gives the required property that the set of ports
utilised by any given query is set by --port-limit and we don't
re-use any until we have port-limit different ones.
-rw-r--r-- | src/dnsmasq.h | 1 | ||||
-rw-r--r-- | src/forward.c | 46 | ||||
-rw-r--r-- | src/network.c | 13 | ||||
-rw-r--r-- | src/util.c | 13 |
4 files changed, 50 insertions, 23 deletions
diff --git a/src/dnsmasq.h b/src/dnsmasq.h index 1bfb965..6f02368 100644 --- a/src/dnsmasq.h +++ b/src/dnsmasq.h @@ -1405,6 +1405,7 @@ void *whine_malloc(size_t size); void *whine_realloc(void *ptr, size_t size); int sa_len(union mysockaddr *addr); int sockaddr_isequal(const union mysockaddr *s1, const union mysockaddr *s2); +int sockaddr_isnull(const union mysockaddr *s); int hostname_order(const char *a, const char *b); int hostname_isequal(const char *a, const char *b); int hostname_issubdomain(char *a, char *b); diff --git a/src/forward.c b/src/forward.c index 9d1f005..681ee90 100644 --- a/src/forward.c +++ b/src/forward.c @@ -2438,13 +2438,18 @@ static int random_sock(struct server *s) if (local_bind(fd, &s->source_addr, s->interface, s->ifindex, 0)) return fd; - if (s->interface[0] == 0) - (void)prettyprint_addr(&s->source_addr, daemon->addrbuff); - else - safe_strncpy(daemon->addrbuff, s->interface, ADDRSTRLEN); - - my_syslog(LOG_ERR, _("failed to bind server socket to %s: %s"), - daemon->addrbuff, strerror(errno)); + /* don't log errors due to running out of available ports, we handle those. */ + if (!sockaddr_isnull(&s->source_addr) || errno != EADDRINUSE) + { + if (s->interface[0] == 0) + (void)prettyprint_addr(&s->source_addr, daemon->addrbuff); + else + safe_strncpy(daemon->addrbuff, s->interface, ADDRSTRLEN); + + my_syslog(LOG_ERR, _("failed to bind server socket to %s: %s"), + daemon->addrbuff, strerror(errno)); + } + close(fd); } @@ -2518,16 +2523,6 @@ 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; @@ -2552,10 +2547,19 @@ int allocate_rfd(struct randfd_list **fdlp, struct server *serv) server_isequal(serv, daemon->randomsocks[i].serv) && daemon->randomsocks[i].refcount != 0xfffe) { - finger = i + 1; - rfd = &daemon->randomsocks[i]; - rfd->refcount++; - break; + struct randfd_list *rl; + /* Don't pick one we already have. */ + for (rl = *fdlp; rl; rl = rl->next) + if (rl->rfd == &daemon->randomsocks[i]) + break; + + if (!rl) + { + finger = i + 1; + rfd = &daemon->randomsocks[i]; + rfd->refcount++; + break; + } } } diff --git a/src/network.c b/src/network.c index 6166484..9ecb9c0 100644 --- a/src/network.c +++ b/src/network.c @@ -1371,7 +1371,7 @@ int local_bind(int fd, union mysockaddr *addr, char *intname, unsigned int ifind 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; + tries = (ports_avail < 30) ? ports_avail : 100; port = htons(daemon->min_port + (rand16() % ports_avail)); } @@ -1400,7 +1400,16 @@ int local_bind(int fd, union mysockaddr *addr, char *intname, unsigned int ifind if (--tries == 0) return 0; - port = htons(daemon->min_port + (rand16() % ports_avail)); + /* For small ranges, do a systematic search, not a random one. */ + if (ports_avail < 30) + { + unsigned short hport = ntohs(port); + if (hport++ == daemon->max_port) + hport = daemon->min_port; + port = htons(hport); + } + else + port = htons(daemon->min_port + (rand16() % ports_avail)); } if (!is_tcp && ifindex > 0) @@ -364,6 +364,19 @@ int sockaddr_isequal(const union mysockaddr *s1, const union mysockaddr *s2) return 0; } +int sockaddr_isnull(const union mysockaddr *s) +{ + if (s->sa.sa_family == AF_INET && + s->in.sin_addr.s_addr == 0) + return 1; + + if (s->sa.sa_family == AF_INET6 && + IN6_IS_ADDR_UNSPECIFIED(&s->in6.sin6_addr)) + return 1; + + return 0; +} + int sa_len(union mysockaddr *addr) { #ifdef HAVE_SOCKADDR_SA_LEN |