A name which is not listed can be a CNAME for one which is. That is 
how some trackers get past name-based blocking. 

An answer whose CNAMEs reach a listed name is now turned into NXDOMAIN
with EDE "Blocked", as --bogus-nxdomain does, and not cached.

For instance:

With HaGeZi Ultimate on my BPI-R3 router, smetrics.target.com and
smetrics.walmart.com, which the list does not have, are CNAMEs into
omtrdc.net and 2o7.net, which it does. With this patch both are
blocked.

 man/dnsmasq.8   |  3 +++
 src/blocklist.c | 36 ++++++++++++++++++++++++++++++++++++
 src/dnsmasq.h   |  1 +
 src/forward.c   | 30 ++++++++++++++++++++++++++++++
 4 files changed, 70 insertions(+)

diff --git a/man/dnsmasq.8 b/man/dnsmasq.8
index d7a2c98..2ba2ef7 100644
--- a/man/dnsmasq.8
+++ b/man/dnsmasq.8
@@ -628,6 +628,9 @@ or sent to a
 \fB--server\fP given for that domain with an address, are not blocked.
 A \fB--server\fP using # does not exempt a name, since it hands the
 query back to the default servers.
+An answer from upstream which goes through a CNAME to a listed name is
+blocked as well, and not cached. One for a name with a \fB--server\fP of
+its own is passed on, but not cached.
 
 The file is read again on SIGHUP; if it cannot be read then, the old list
 is kept. Available only if dnsmasq
diff --git a/src/blocklist.c b/src/blocklist.c
index 22cc4ba..338c717 100644
--- a/src/blocklist.c
+++ b/src/blocklist.c
@@ -290,4 +290,40 @@ int blocklist_match(char *name)
   return 0;
 }
 
+/* Does an answer go through a CNAME to a listed name? Caching it would
+   put the listed name in the cache, where a query for that name would
+   be answered without being checked. */
+int blocklist_cname(struct dns_header *header, size_t plen)
+{
+  unsigned char *p;
+  int i;
+
+  if (!(p = skip_questions(header, plen)))
+    return 0;
+
+  for (i = ntohs(header->ancount); i != 0; i--)
+    {
+      unsigned short type, rdlen;
+      unsigned char *rdata;
+
+      if (!(p = skip_name(p, header, plen, 10)))
+       return 0;
+
+      GETSHORT(type, p);
+      p += 6; /* class and TTL */
+      GETSHORT(rdlen, p);
+      rdata = p;
+
+      if (type == T_CNAME &&
+         extract_name(header, plen, &rdata, daemon->workspacename, 
EXTR_NAME_EXTRACT, 0) &&
+         blocklist_match(daemon->workspacename))
+       return 1;
+
+      if (!ADD_RDLEN(header, p, plen, rdlen))
+       return 0;
+    }
+
+  return 0;
+}
+
 #endif
diff --git a/src/dnsmasq.h b/src/dnsmasq.h
index 02357fd..141d135 100644
--- a/src/dnsmasq.h
+++ b/src/dnsmasq.h
@@ -1971,6 +1971,7 @@ void dump_packet_icmp(int mask, void *packet, size_t len, 
union mysockaddr *src,
 #ifdef HAVE_BLOCKLIST
 void blocklist_init(int reload);
 int blocklist_match(char *name);
+int blocklist_cname(struct dns_header *header, size_t plen);
 #endif
 
 /* domain-match.c */
diff --git a/src/forward.c b/src/forward.c
index cfb4685..91ef1f4 100644
--- a/src/forward.c
+++ b/src/forward.c
@@ -732,6 +732,9 @@ static size_t process_reply(struct dns_header *header, 
time_t now, struct server
   int is_sign;
   unsigned int rcode = RCODE(header);
   size_t plen; 
+#ifdef HAVE_BLOCKLIST
+  int blocked = 0;
+#endif
     
   (void)ad_reqd;
   (void)do_bit;
@@ -836,6 +839,33 @@ static size_t process_reply(struct dns_header *header, 
time_t now, struct server
       if (daemon->doctors && do_doctor(header, n, daemon->namebuff))
        cache_secure = 0;
       
+#ifdef HAVE_BLOCKLIST
+      /* An answer through a CNAME to a listed name is blocked like the
+        name. It is not cached either, or a query for the listed name
+        would find it there. One for a name with a --server of its own is
+        passed on, but still not cached. */
+      if (daemon->blocklist_file && blocklist_cname(header, n))
+       {
+         if (server && server->domain_len != 0)
+           no_cache = 1;
+         else
+           blocked = 1;
+       }
+
+      if (blocked)
+       {
+         header->ancount = htons(0);
+         header->nscount = htons(0);
+         header->arcount = htons(0);
+         SET_RCODE(header, NXDOMAIN);
+         header->hb3 &= ~HB3_AA;
+         cache_secure = 0;
+         ede = EDE_BLOCKED;
+         if (extract_name(header, n, NULL, daemon->namebuff, 
EXTR_NAME_EXTRACT, 0))
+           log_query(F_NXDOMAIN | F_NEG | F_FORWARD | F_HOSTS, 
daemon->namebuff, NULL, "blocklist", 0);
+       }
+      else
+#endif
       /* check_for_bogus_wildcard() does its own caching, so
         don't call extract_addresses() if it triggers. */
       if (daemon->bogus_addr && rcode != NXDOMAIN &&
-- 
2.53.0


_______________________________________________
Dnsmasq-discuss mailing list
[email protected]
https://lists.thekelleys.org.uk/cgi-bin/mailman/listinfo/dnsmasq-discuss

Reply via email to