Hello,

we discovered our dnsmasq  were using also privileged source ports when
sending queries. Interesting enough, it has right to do it, because it
has to listen also on privileged port. It never drops such privilege.

It was fixed in commit [1]. But my question is, why is there even custom
generator or random ports, when OS can do it itself? And usually far
better? So I dug a bit into it and came with patch, that would use
random ports from OS by default.

When I tested it, I got the same results when skipping bind() call on
random ports at all. Is there some reason, why dnsmasq does not follow
OS policy for source outgoing port and choses its own range by itself?

1.
http://thekelleys.org.uk/gitweb/?p=dnsmasq.git;a=commit;h=baf553db0cdb50707ddab464fb3eff7786ea576c
-- 
Petr Menšík
Software Engineer
Red Hat, http://www.redhat.com/
email: pemen...@redhat.com  PGP: 65C6C973
From 57b19ecc3fdd1f7357ea4472bfa4fe731e28c3cf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Petr=20Men=C5=A1=C3=ADk?= <pemen...@redhat.com>
Date: Thu, 9 Aug 2018 18:17:26 +0200
Subject: [PATCH 1/2] Use OS random ports by default

Unless max-port or min-port is given, let OS allocate random ports for
DNS queries. Randomize similar to --query-port=0, but for each query
separately. Would use port according to system policy.
---
 src/dnsmasq.c |  2 +-
 src/network.c | 15 ++++++++++++---
 src/option.c  |  4 +++-
 3 files changed, 16 insertions(+), 5 deletions(-)

diff --git a/src/dnsmasq.c b/src/dnsmasq.c
index 7a8e891..02fa003 100644
--- a/src/dnsmasq.c
+++ b/src/dnsmasq.c
@@ -230,7 +230,7 @@ int main (int argc, char **argv)
     die(_("Ubus not available: set HAVE_UBUS in src/config.h"), NULL, EC_BADCONF);
 #endif
   
-  if (daemon->max_port < daemon->min_port)
+  if (daemon->max_port >= 0 && daemon->max_port < daemon->min_port)
     die(_("max_port cannot be smaller than min_port"), NULL, EC_BADCONF);
 
   now = dnsmasq_time();
diff --git a/src/network.c b/src/network.c
index b405458..e1b60d1 100644
--- a/src/network.c
+++ b/src/network.c
@@ -1138,18 +1138,27 @@ int random_sock(int family)
   if ((fd = socket(family, SOCK_DGRAM, 0)) != -1)
     {
       union mysockaddr addr;
-      unsigned int ports_avail = ((unsigned short)daemon->max_port - (unsigned short)daemon->min_port) + 1;
-      int tries = ports_avail < 30 ? 3 * ports_avail : 100;
+      unsigned short ports_avail = 0;
+      int tries = 100;
+      unsigned short port = 0;
 
       memset(&addr, 0, sizeof(addr));
       addr.sa.sa_family = family;
 
+      if (daemon->max_port >= 0)
+        {
+          ports_avail = ((unsigned short)daemon->max_port - (unsigned short)daemon->min_port) + 1;
+          if (ports_avail < 30)
+            tries = 3 * ports_avail;
+        }
+
       /* don't loop forever if all ports in use. */
 
       if (fix_fd(fd))
 	while(tries--)
 	  {
-	    unsigned short port = htons(daemon->min_port + (rand16() % ((unsigned short)ports_avail)));
+            if (ports_avail)
+	      port = htons(daemon->min_port + (rand16() % ports_avail));
 	    
 	    if (family == AF_INET) 
 	      {
diff --git a/src/option.c b/src/option.c
index c203826..f77f5aa 100644
--- a/src/option.c
+++ b/src/option.c
@@ -2620,6 +2620,8 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
     case LOPT_MINPORT:  /* --min-port */
       if (!atoi_check16(arg, &daemon->min_port))
 	ret_err(gen_err);
+      if (daemon->max_port < 0)
+        daemon->max_port = MAX_PORT;
       break;
 
     case LOPT_MAXPORT:  /* --max-port */
@@ -4698,7 +4700,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->max_port = -1;
   daemon->min_port = MIN_PORT;
 
 #ifndef NO_ID
-- 
2.14.4

From b54e4550f8ebf3ee5aaaca41e8f7ccf278539bcb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Petr=20Men=C5=A1=C3=ADk?= <pemen...@redhat.com>
Date: Thu, 9 Aug 2018 20:57:07 +0200
Subject: [PATCH 2/2] Simplify random ports generator

Do not bind random port with any address at all, just leave socket
unbound. Rely on sendto() to connect it first time.
---
 src/network.c | 28 +++++++++++++---------------
 1 file changed, 13 insertions(+), 15 deletions(-)

diff --git a/src/network.c b/src/network.c
index e1b60d1..71f5186 100644
--- a/src/network.c
+++ b/src/network.c
@@ -1138,27 +1138,25 @@ int random_sock(int family)
   if ((fd = socket(family, SOCK_DGRAM, 0)) != -1)
     {
       union mysockaddr addr;
-      unsigned short ports_avail = 0;
-      int tries = 100;
-      unsigned short port = 0;
+      unsigned short ports_avail;
+      int tries;
+      unsigned short port;
+
+      if (!fix_fd(fd))
+        goto close_fd;
+
+      if (daemon->max_port < 0)
+          return fd;
 
       memset(&addr, 0, sizeof(addr));
       addr.sa.sa_family = family;
-
-      if (daemon->max_port >= 0)
-        {
-          ports_avail = ((unsigned short)daemon->max_port - (unsigned short)daemon->min_port) + 1;
-          if (ports_avail < 30)
-            tries = 3 * ports_avail;
-        }
+      ports_avail = ((unsigned short)daemon->max_port - (unsigned short)daemon->min_port) + 1;
+      tries = (ports_avail < 30) ? 3 * ports_avail : 100;
 
       /* don't loop forever if all ports in use. */
-
-      if (fix_fd(fd))
 	while(tries--)
 	  {
-            if (ports_avail)
-	      port = htons(daemon->min_port + (rand16() % ports_avail));
+	    port = htons(daemon->min_port + (rand16() % ports_avail));
 	    
 	    if (family == AF_INET) 
 	      {
@@ -1185,7 +1183,7 @@ int random_sock(int family)
 	    if (errno != EADDRINUSE && errno != EACCES)
 	      break;
 	  }
-
+close_fd:
       close(fd);
     }
 
-- 
2.14.4

_______________________________________________
Dnsmasq-discuss mailing list
Dnsmasq-discuss@lists.thekelleys.org.uk
http://lists.thekelleys.org.uk/mailman/listinfo/dnsmasq-discuss

Reply via email to