Hey Simon,

I'm resubmitting the patch with these changes:

0. Submit the correct patch with the correct memory allocation

1. Rebased onto Petr's change to option.c

2. Removed the explain feature

3. Added failsafe checks to the mallocs

4. Added a hint in network.c when there are more servers to be
shown that we log (this is limited by SERVERS_LOGGED = 30)


Best,
Dominik
From 58343618ffee18c24a9c81b7a668cb5dcc7e847d Mon Sep 17 00:00:00 2001
From: Dominik DL6ER <dl...@dl6er.de>
Date: Mon, 6 Sep 2021 22:27:00 +0200
Subject: [PATCH] --rev-server: Add support for arbitrary IPv4/6
 prefix-lengths. So far, the prefix was limited to [8,16,24,32] for IPv4 and
 to multiples of 4 for IPv6. This patch also makes the prefix-length optional.

Signed-off-by: DL6ER <dl...@dl6er.de>
---
 man/dnsmasq.8 |   3 +-
 src/network.c |   3 +
 src/option.c  | 210 +++++++++++++++++++++++++++++++++++++-------------
 3 files changed, 162 insertions(+), 54 deletions(-)

diff --git a/man/dnsmasq.8 b/man/dnsmasq.8
index 7ffccad..1448ba2 100644
--- a/man/dnsmasq.8
+++ b/man/dnsmasq.8
@@ -505,13 +505,14 @@ source address specified but the port may be specified directly as
 part of the source address. Forcing queries to an interface is not
 implemented on all platforms supported by dnsmasq.
 .TP
-.B --rev-server=<ip-address>/<prefix-len>[,<ipaddr>][#<port>][@<interface>][@<source-ip>[#<port>]]
+.B --rev-server=<ip-address>[/<prefix-len>][,<ipaddr>][#<port>][@<interface>][@<source-ip>[#<port>]]
 This is functionally the same as 
 .B --server, 
 but provides some syntactic sugar to make specifying address-to-name queries easier. For example
 .B --rev-server=1.2.3.0/24,192.168.0.1
 is exactly equivalent to 
 .B --server=/3.2.1.in-addr.arpa/192.168.0.1
+Allowed prefix lengths are 1-32 (IPv4) and 1-128 (IPv6). If the prefix length is omitted, dnsmasq substitutes either 32 (IPv4) or 128 (IPv6).
 .TP
 .B \-A, --address=/<domain>[/<domain>...]/[<ipaddr>]
 Specify an IP address to return for any host in the given domains.
diff --git a/src/network.c b/src/network.c
index 3fc179d..f92d06d 100644
--- a/src/network.c
+++ b/src/network.c
@@ -1586,6 +1586,9 @@ void check_servers(int no_loop_check)
       if (serv->sfd)
 	serv->sfd->used = 1;
       
+      if (count == SERVERS_LOGGED)
+	my_syslog(LOG_INFO, _("more servers are defined but not logged"));
+
       if (++count > SERVERS_LOGGED)
 	continue;
       
diff --git a/src/option.c b/src/option.c
index 11655fd..778b90a 100644
--- a/src/option.c
+++ b/src/option.c
@@ -935,52 +935,146 @@ char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_a
   return NULL;
 }
 
-static int domain_rev4(char *domain, struct in_addr addr, int msize)
+static int gen_rev4(struct in_addr *addr, int msize, char ***domains, char *errstr)
 {
-  in_addr_t a = ntohl(addr.s_addr);
- 
-  *domain = 0;
-  
-  switch (msize)
+  unsigned int i, mina, maxa, octnum, N;
+  struct in_addr bitmask, a_min, a_max;
+
+  if(msize < 1 || msize > 32)
+    ret_err("bad IPv4 prefix");
+
+  /* Construct binary mask from specified prefix-length */
+  bitmask.s_addr = ~htonl((1 << (32 - msize)) - 1);
+
+  /* Apply bitmask to address to compute subnet address range */
+  a_min.s_addr = (*addr).s_addr & bitmask.s_addr;
+  a_max.s_addr = a_min.s_addr + ~bitmask.s_addr;
+
+  /* Extract IP address octets in host-format for in-addr.arpa string-assembly */
+  in_addr_t a = ntohl((*addr).s_addr);
+
+  /* Derive number of octets to be printed during string-assembly */
+  octnum = ((msize-1)/8);
+
+  /* Extract min and max address range for string-assembly loop */
+  mina = (ntohl(a_min.s_addr) >> 8*(3-octnum)) & 0xFF;
+  maxa = (ntohl(a_max.s_addr) >> 8*(3-octnum)) & 0xFF;
+
+  /* Get number of domains we are going to generate here */
+  N = maxa - mina + 1;
+
+  /* Allocate space for domain array */
+  *domains = opt_malloc(N * sizeof(char*));
+  if(!domains)
+    return 0;
+
+  /* Generate the individual domains */
+  for(i = 0; i < N; i++)
     {
-    case 32:
-      domain += sprintf(domain, "%u.", a & 0xff);
-      /* fall through */
-    case 24:
-      domain += sprintf(domain, "%d.", (a >> 8) & 0xff);
-      /* fall through */
-    case 16:
-      domain += sprintf(domain, "%d.", (a >> 16) & 0xff);
-      /* fall through */
-    case 8:
-      domain += sprintf(domain, "%d.", (a >> 24) & 0xff);
-      break;
-    default:
-      return 0;
+      char *domain = opt_malloc(29); /* strlen("xxx.yyy.zzz.ttt.in-addr.arpa")+1 */
+      if(!domain)
+	return 0;
+      unsigned char j = i + mina;
+
+      /* Build in-addr.arpa address */
+      if(octnum == 3)
+	sprintf(domain, "%u.%u.%u.%u.in-addr.arpa", j, (a >> 8) & 0xff, (a >> 16) & 0xff, (a >> 24) & 0xff);
+      else if(octnum == 2)
+	sprintf(domain, "%u.%u.%u.in-addr.arpa", j, (a >> 16) & 0xff, (a >> 24) & 0xff);
+      else if(octnum == 1)
+	sprintf(domain, "%u.%u.in-addr.arpa", j, (a >> 24) & 0xff);
+      else
+	sprintf(domain, "%u.in-addr.arpa", j);
+
+      /* Append domain to array */
+      (*domains)[i] = domain;
     }
-  
-  sprintf(domain, "in-addr.arpa");
-  
-  return 1;
+
+  /* Return number of generated rev-domains (> 0 if successful) */
+  return N;
 }
 
-static int domain_rev6(char *domain, struct in6_addr *addr, int msize)
+static int gen_rev6(struct in6_addr *addr, int msize, char ***domains, char *errstr)
 {
-  int i;
+  unsigned int mina, maxa, N;
+  int i, idx = 31;
+  char *p, *top_domain;
+  unsigned char a_min8[32], a_max8[32];
+  struct in6_addr bitmask, a_min, a_max;
+  memset(&bitmask, 0, sizeof(bitmask));
 
-  if (msize > 128 || msize%4)
+  if(msize < 1 || msize > 128)
+     ret_err("bad IPv6 prefix");
+
+  for(i = 0; i < (int)sizeof(bitmask.s6_addr); i++)
+    {
+      /* Construct binary mask from specified prefix-length */
+      if(msize >= 8*(i+1))
+	bitmask.s6_addr[i] = 0xFF;
+      else if(msize > 8*i)
+	bitmask.s6_addr[i] = 0xFF << (8-msize%8);
+
+      /* Apply bitmask to address to compute subnet address range, generate
+	 auxiliary 8bit address array for easier handling of IPv6 addresses,
+	 and memorize where addresses differ */
+      a_min.s6_addr[i] = (*addr).s6_addr[i] & bitmask.s6_addr[i];
+      a_max.s6_addr[i] = a_min.s6_addr[i] + ~bitmask.s6_addr[i];
+      a_min8[2*i+1] =  a_min.s6_addr[i] & 0x0F;
+      a_min8[2*i  ] = (a_min.s6_addr[i] & 0xF0) >> 4;
+      a_max8[2*i+1] =  a_max.s6_addr[i] & 0x0F;
+      a_max8[2*i  ] = (a_max.s6_addr[i] & 0xF0) >> 4;
+
+      /* Memorize where addresses differ (if they differ) */
+      if(idx == 31 && a_min8[2*i] != a_max8[2*i])
+	idx = 2*i;
+      else if(idx == 31 && a_min8[2*i+1] != a_max8[2*i+1])
+	idx = 2*i+1;
+    }
+
+  /* Construct top-level domain "....ip6.arpa" */
+  p = top_domain = opt_malloc(73);
+  if(!top_domain)
     return 0;
-  
-  *domain = 0;
+  for (i = idx-1; i >= 0; i--)
+    p += sprintf(p, "%.1x.", a_min8[i]);
+  sprintf(p, "ip6.arpa");
 
-  for (i = msize-1; i >= 0; i -= 4)
-    { 
-      int dig = ((unsigned char *)addr)[i>>3];
-      domain += sprintf(domain, "%.1x.", (i>>2) & 1 ? dig & 15 : dig >> 4);
+  /* Extract min and max address range for string-assembly loop */
+  mina = a_min8[idx];
+  maxa = a_max8[idx];
+
+  /* If 0.ip6.arpa to f.ip6.arpa would all be added, we prevent this from happening */
+  if(mina == 0x0 && maxa == 0xF)
+      maxa = mina;
+
+  /* Get number of domains we are going to generate here */
+  N = maxa - mina + 1;
+
+  /* Allocate space for domain array */
+  *domains = opt_malloc(N * sizeof(char*));
+  if(!*domains)
+    return 0;
+
+  /* Build ip6.arpa addresses by walking last hex nibble */
+  for(i = 0; i < (int)N; i++)
+    {
+      unsigned char j = i + mina;
+      char *domain = opt_malloc(73); /* strlen("32*<n.>ip6.arpa")+1 */
+      if(!domain)
+	return 0;
+
+      /* Build ip6.arpa address */
+      if(mina == 0 && maxa == 0)
+	sprintf(domain, "%s", top_domain); /* only top level */
+      else
+	sprintf(domain, "%.1x.%s", j, top_domain);
+
+      /* Append server information, return 0 in case of an error */
+      (*domains)[i] = domain;
     }
-  sprintf(domain, "ip6.arpa");
-  
-  return 1;
+
+  /* Return number of generated rev-domains (> 0 if successful) */
+  return N;
 }
 
 #ifdef HAVE_DHCP
@@ -2313,11 +2407,10 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
 				ret_err_free(gen_err, new);
 			      else
 				{
-				  char domain[29]; /* strlen("xxx.yyy.zzz.ttt.in-addr.arpa")+1 */
 				  /* local=/xxx.yyy.zzz.in-addr.arpa/ */
-				  /* domain_rev4 can't fail here, msize checked above. */
-				  domain_rev4(domain, new->start, msize);
-				  add_update_server(SERV_LITERAL_ADDRESS, NULL, NULL, NULL, domain, NULL);
+				  char **domains = NULL;
+				  if(gen_rev4(&new->start, msize, &domains, errstr) > 0)
+				    add_update_server(SERV_LITERAL_ADDRESS, NULL, NULL, NULL, domains[0], NULL);
 				  
 				  /* local=/<domain>/ */
 				  /* d_raw can't failed to canonicalise here, checked above. */
@@ -2357,11 +2450,11 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
 				ret_err_free(gen_err, new);
 			      else 
 				{
-				  char domain[73]; /* strlen("32*<n.>ip6.arpa")+1 */
 				  /* generate the equivalent of
 				     local=/xxx.yyy.zzz.ip6.arpa/ */
-				  domain_rev6(domain, &new->start6, msize);
-				  add_update_server(SERV_LITERAL_ADDRESS, NULL, NULL, NULL, domain, NULL);
+				  char **domains = NULL;
+				  if(gen_rev4(&new->start, msize, &domains, errstr) > 0)
+				    add_update_server(SERV_LITERAL_ADDRESS, NULL, NULL, NULL, domains[0], NULL);
 
 				  /* local=/<domain>/ */
 				  /* d_raw can't failed to canonicalise here, checked above. */
@@ -2734,31 +2827,35 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
     case LOPT_REV_SERV: /* --rev-server */
       {
 	char *string;
-	int size;
+	int size, N, i;
 	u16 flags = 0;
-	char domain[73]; /* strlen("32*<n.>ip6.arpa")+1 */
 	struct in_addr addr4;
 	struct in6_addr addr6;
  	union mysockaddr serv_addr, source_addr;
 	char interface[IF_NAMESIZE+1];
-	
+	char **domains;
+
 	unhide_metas(arg);
 	if (!arg)
 	  ret_err(gen_err);
-	
+
 	comma=split(arg);
 
 	if (!(string = split_chr(arg, '/')) || !atoi_check(string, &size))
-	  ret_err(gen_err);
-	
+	  size = -1;
+
 	if (inet_pton(AF_INET, arg, &addr4))
 	  {
-	    if (!domain_rev4(domain, addr4, size))
+	    if (size == -1)
+	      size = 32; /* Assume 32 bit prefix-length for an exact IPv4 address */
+	    N = gen_rev4(&addr4, size, &domains, errstr);
+	    if(!N)
 	      ret_err(_("bad IPv4 prefix"));
 	  }
 	else if (inet_pton(AF_INET6, arg, &addr6))
 	  {
-	    if (!domain_rev6(domain, &addr6, size))
+	    N = gen_rev6(&addr6, size, &domains, errstr);
+	    if(!N)
 	      ret_err(_("bad IPv6 prefix"));
 	  }
 	else
@@ -2772,8 +2869,15 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
 	if (servers_only)
 	  flags |= SERV_FROM_FILE;
 
-	if (!add_update_server(flags, &serv_addr, &source_addr, interface, domain, NULL))
-	  ret_err(gen_err);
+	/* Loop over rev domains obtained above */
+	for(i=0; i < N; i++)
+	  {
+	    if (size == -1)
+	      size = 128; /* Assume 128 bit prefix-length for an exact IPv6 address */
+	    if (!add_update_server(flags, &serv_addr, &source_addr, interface, domains[i], NULL))
+	      ret_err(gen_err);
+	    free(domains[i]);
+	  }
 	
 	break;
       }
-- 
2.25.1

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

Reply via email to