summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Kelley <simon@thekelleys.org.uk>2022-09-09 17:09:32 +0100
committerSimon Kelley <simon@thekelleys.org.uk>2022-09-09 17:09:32 +0100
commit3f56bb8ba195f72a6bf1d572f8add07b4fc44ec0 (patch)
treef8c65ac06c7b063c8261065f75200dda0964e928
parente518e87533345f53fb59e1b9e99994dd73eb8942 (diff)
downloaddnsmasq-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.h1
-rw-r--r--src/forward.c46
-rw-r--r--src/network.c13
-rw-r--r--src/util.c13
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)
diff --git a/src/util.c b/src/util.c
index 5165fd6..e0ce67d 100644
--- a/src/util.c
+++ b/src/util.c
@@ -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