Fix some edge cases wth domains and --address and --server. v2.92test5
authorSimon Kelley <simon@thekelleys.org.uk>
Tue, 29 Apr 2025 15:33:22 +0000 (16:33 +0100)
committerSimon Kelley <simon@thekelleys.org.uk>
Tue, 29 Apr 2025 15:33:22 +0000 (16:33 +0100)
Consider what happens when the same domain appears in
--address and --server.

This commit fixes the order, I think correctly like this:
highest to lowest priority.

--address with a IPv4 or IPv6 address (as long as the query matches the type)
--address with # for all-zeros, as long as the query is A or AAAA)
--address with no address, which returns NXDOMAIN or NOERROR for all types.
--server with address set to # to use the unqualified servers.
--server with matching domain.
--server without domain or from /etc/resolv.conf.

Note that the above is only valid when same domain appears.
The domain being matched is determined first, and has a higher
priority, so you can send google.com to a server and force com
to return NXDOMAIN and for google.com the server config will
override the address config, because there's a longer match.

src/dnsmasq.h
src/domain-match.c
src/network.c

index c310597..5c4fd4c 100644 (file)
@@ -558,9 +558,9 @@ union mysockaddr {
 
 
 /* The actual values here matter, since we sort on them to get records in the order
-   IPv6 addr, IPv4 addr, all zero return, resolvconf servers, upstream server, no-data return  */
-#define SERV_LITERAL_ADDRESS    1  /* addr is the answer, or NoDATA is the answer, depending on the next four flags */
-#define SERV_USE_RESOLV         2  /* forward this domain in the normal way */
+   IPv6 addr, IPv4 addr, all zero return, no-data return, resolvconf servers, upstream server */
+#define SERV_USE_RESOLV         1  /* forward this domain in the normal way */
+#define SERV_LITERAL_ADDRESS    2  /* addr is the answer, or NoDATA is the answer, depending on the next four flags */
 #define SERV_ALL_ZEROS          4  /* return all zeros for A and AAAA */
 #define SERV_4ADDR              8  /* addr is IPv4 */
 #define SERV_6ADDR             16  /* addr is IPv6 */
index cc8aa9b..a2e2266 100644 (file)
@@ -20,7 +20,7 @@ static int order(char *qdomain, size_t qlen, struct server *serv);
 static int order_qsort(const void *a, const void *b);
 static int order_servers(struct server *s, struct server *s2);
 
-/* If the server is USE_RESOLV or LITERAL_ADDRES, it lives on the local_domains chain. */
+/* If the server is USE_RESOLV or LITERAL_ADDRESS, it lives on the local_domains chain. */
 #define SERV_IS_LOCAL (SERV_USE_RESOLV | SERV_LITERAL_ADDRESS)
 
 void build_server_array(void)
@@ -259,7 +259,6 @@ int lookup_domain(char *domain, int flags, int *lowout, int *highout)
   return 1;
 }
 
-/* Return first server in group of equivalent servers; this is the "master" record. */
 int server_samegroup(struct server *a, struct server *b)
 {
   return order_servers(a, b) == 0;
@@ -296,11 +295,13 @@ int filter_servers(int seed, int flags, int *lowout, int *highout)
     }
   else
     {
-      /* Now the servers are on order between low and high, in the order
-        IPv6 addr, IPv4 addr, return zero for both, resolvconf servers, send upstream, no-data return.
+      /* Now the matching server records are all between low and high.
+        order_qsort() ensures that they are in the order
+        IPv6 addr, IPv4 addr, return zero for both, no-data return,
+        "use resolvconf" servers, domain-specific upstream servers.
         
         See which of those match our query in that priority order and narrow (low, high) */
-      
+
       for (i = nlow; i < nhigh && (daemon->serverarray[i]->flags & SERV_6ADDR); i++);
       
       if (!(flags & F_SERVER) && i != nlow && (flags & F_IPV6))
@@ -325,29 +326,27 @@ int filter_servers(int seed, int flags, int *lowout, int *highout)
                {
                  nlow = i;
                  
-                 /* Short to resolv.conf servers */
-                 for (i = nlow; i < nhigh && (daemon->serverarray[i]->flags & SERV_USE_RESOLV); i++);
+                 /* now look for a NXDOMAIN answer  --local=/domain/ */
+                 for (i = nlow; i < nhigh && (daemon->serverarray[i]->flags & SERV_LITERAL_ADDRESS); i++);
                  
-                 if (i != nlow)
+                 if (!(flags & (F_DOMAINSRV | F_SERVER)) && i != nlow)
                    nhigh = i;
                  else
                    {
-                     /* now look for a server */
-                     for (i = nlow; i < nhigh && !(daemon->serverarray[i]->flags & SERV_LITERAL_ADDRESS); i++);
+                     nlow = i;
+                 
+                     /* return "use resolv.conf servers" if they exist */
+                     for (i = nlow; i < nhigh && (daemon->serverarray[i]->flags & SERV_USE_RESOLV); i++);
                      
                      if (i != nlow)
+                       nhigh = i;
+                     else
                        {
                          /* If we want a server for a particular domain, and this one isn't, return nothing. */
                          if ((flags & F_DOMAINSRV) && daemon->serverarray[nlow]->domain_len == 0)
                            nlow = nhigh;
                          else
-                           nhigh = i;
-                       }
-                     else
-                       {
-                         /* --local=/domain/, only return if we don't need a server. */
-                         if (flags & (F_DOMAINSRV | F_SERVER))
-                           nhigh = i;
+                           nlow = i;
                        }
                    }
                }
@@ -543,12 +542,14 @@ static int order_qsort(const void *a, const void *b)
   rc = order_servers(s1, s2);
 
   /* Sort all literal NODATA and local IPV4 or IPV6 responses together,
-     in a very specific order. We flip the SERV_LITERAL_ADDRESS bit
-     so the order is IPv6 literal, IPv4 literal, all-zero literal, 
-     unqualified servers, upstream server, NXDOMAIN literal. */
+     in a very specific order  IPv6 literal, IPv4 literal, all-zero literal,
+     NXDOMAIN literal. We also include SERV_USE_RESOLV in this, so that
+     use-standard servers sort before ordinary servers. (SERV_USR_RESOLV set
+     implies that none of SERV_LITERAL_ADDRESS,SERV_4ADDR,SERV_6ADDR,SERV_ALL_ZEROS
+     are set) */
   if (rc == 0)
-    rc = ((s2->flags & (SERV_LITERAL_ADDRESS | SERV_4ADDR | SERV_6ADDR | SERV_USE_RESOLV | SERV_ALL_ZEROS)) ^ SERV_LITERAL_ADDRESS) -
-      ((s1->flags & (SERV_LITERAL_ADDRESS | SERV_4ADDR | SERV_6ADDR | SERV_USE_RESOLV | SERV_ALL_ZEROS)) ^ SERV_LITERAL_ADDRESS);
+    rc = ((s2->flags & (SERV_LITERAL_ADDRESS | SERV_4ADDR | SERV_6ADDR | SERV_ALL_ZEROS | SERV_USE_RESOLV))) -
+      ((s1->flags & (SERV_LITERAL_ADDRESS | SERV_4ADDR | SERV_6ADDR | SERV_ALL_ZEROS | SERV_USE_RESOLV)));
 
   /* Finally, order by appearance in /etc/resolv.conf etc, for --strict-order */
   if (rc == 0)
index 61742f6..68bc273 100644 (file)
@@ -1665,7 +1665,7 @@ void check_servers(int no_loop_check)
           if (++locals <= LOCALS_LOGGED)
             my_syslog(LOG_INFO, _("using only locally-known addresses for %s"), serv->domain);
         }
-       else if (serv->flags & SERV_USE_RESOLV)
+       else if (serv->flags & SERV_USE_RESOLV && serv->domain_len != 0)
         my_syslog(LOG_INFO, _("using standard nameservers for %s"), serv->domain);
     }