Use a simpler arrangement for the all_addr union to avoid
authorSimon Kelley <simon@thekelleys.org.uk>
Sat, 1 Apr 2023 20:35:26 +0000 (21:35 +0100)
committerSimon Kelley <simon@thekelleys.org.uk>
Sat, 1 Apr 2023 20:35:26 +0000 (21:35 +0100)
the compiler padding it with an extra 8 bytes.

Use the F_KEYTAG flag in a a cache record to discriminate between
an arbitrary RR stored entirely in the addr union and one
which has a point to block storage.

src/cache.c
src/dnsmasq.h
src/rfc1035.c

index 06ed672..fd10240 100644 (file)
@@ -275,8 +275,8 @@ static void cache_blockdata_free(struct crec *crecp)
 {
   if (!(crecp->flags & F_NEG))
     {
-      if (crecp->flags & F_RR && crecp->addr.rr.len == -1)
-       blockdata_free(crecp->addr.rr.u.block.rrdata);
+      if ((crecp->flags & F_RR) && (crecp->flags & F_KEYTAG))
+       blockdata_free(crecp->addr.rrblock.rrdata);
 #ifdef HAVE_DNSSEC
       else if (crecp->flags & F_DNSKEY)
        blockdata_free(crecp->addr.key.keydata);
@@ -469,10 +469,20 @@ static struct crec *cache_scan_free(char *name, union all_addr *addr, unsigned s
        {
          if ((crecp->flags & F_FORWARD) && hostname_isequal(cache_get_name(crecp), name))
            {
+             int rrmatch = 0;
+             if (crecp->flags & flags & F_RR)
+               {
+                 unsigned short rrc = (crecp->flags & F_KEYTAG) ? crecp->addr.rrblock.rrtype : crecp->addr.rrdata.rrtype;
+                 unsigned short rra = (flags & F_KEYTAG) ? addr->rrblock.rrtype : addr->rrdata.rrtype;
+
+                 if (rrc == rra)
+                   rrmatch = 1;
+               }
+
              /* Don't delete DNSSEC in favour of a CNAME, they can co-exist */
-             if ((flags & crecp->flags & (F_IPV4 | F_IPV6 | F_RR | F_NXDOMAIN)) || 
+             if ((flags & crecp->flags & (F_IPV4 | F_IPV6 | F_NXDOMAIN)) || 
                  (((crecp->flags | flags) & F_CNAME) && !(crecp->flags & (F_DNSKEY | F_DS))) ||
-                 ((crecp->flags & flags & F_RR) && addr->rr.rrtype == crecp->addr.rr.rrtype))
+                 rrmatch)
                {
                  if (crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG))
                    return crecp;
@@ -795,8 +805,8 @@ void cache_end_insert(void)
              if (flags & F_RR)
                {
                  /* A negative RR entry is possible and has no data, obviously. */
-                 if (!(flags & F_NEG) && new_chain->addr.rr.len == -1)
-                   blockdata_write(new_chain->addr.rr.u.block.rrdata, new_chain->addr.rr.u.block.datalen, daemon->pipe_to_parent);
+                 if (!(flags & F_NEG) && (flags & F_KEYTAG))
+                   blockdata_write(new_chain->addr.rrblock.rrdata, new_chain->addr.rrblock.datalen, daemon->pipe_to_parent);
                }
 #ifdef HAVE_DNSSEC
              if (flags & F_DNSKEY)
@@ -869,8 +879,8 @@ int cache_recv_insert(time_t now, int fd)
          if (!read_write(fd, (unsigned char *)&addr, sizeof(addr), 1))
            return 0;
          
-         if ((flags & F_RR) && !(flags & F_NEG) &&
-             addr.rr.len == -1 && !(addr.rr.u.block.rrdata = blockdata_read(fd, addr.rr.u.block.datalen)))
+         if ((flags & F_RR) && !(flags & F_NEG) && (flags & F_KEYTAG)
+             && !(addr.rrblock.rrdata = blockdata_read(fd, addr.rrblock.datalen)))
            return 0;
 #ifdef HAVE_DNSSEC
           if (flags & F_DNSKEY)
@@ -1796,7 +1806,12 @@ static void dump_cache_entry(struct crec *cache, time_t now)
   if ((cache->flags & F_CNAME) && !is_outdated_cname_pointer(cache))
     a = sanitise(cache_get_cname_target(cache));
   else if (cache->flags & F_RR)
-    sprintf(a, "%s", querystr(NULL, cache->addr.rr.rrtype));
+    {
+      if (cache->flags & F_KEYTAG)
+       sprintf(a, "%s", querystr(NULL, cache->addr.rrblock.rrtype));
+      else
+       sprintf(a, "%s", querystr(NULL, cache->addr.rrdata.rrtype));
+    }
 #ifdef HAVE_DNSSEC
   else if (cache->flags & F_DS)
     {
@@ -2052,7 +2067,14 @@ void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg,
     {
       dest = daemon->addrbuff;
 
-      if (flags & F_KEYTAG)
+       if (flags & F_RR)
+        {
+          if (flags & F_KEYTAG)
+            dest = querystr(NULL, addr->rrblock.rrtype);
+          else
+            dest = querystr(NULL, addr->rrdata.rrtype);
+        }
+       else if (flags & F_KEYTAG)
        sprintf(daemon->addrbuff, arg, addr->log.keytag, addr->log.algo, addr->log.digest);
       else if (flags & F_RCODE)
        {
@@ -2083,8 +2105,6 @@ void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg,
              sprintf(portstring, "#%u", type);
            }
        }
-      else if (flags & F_RR)
-       dest = querystr(NULL, addr->rr.rrtype);
       else
        dest = arg;
     }
index b6712f0..b7196f0 100644 (file)
@@ -329,21 +329,23 @@ union all_addr {
     unsigned short keytag, algo, digest, rcode;
     int ede;
   } log;
-  /* for arbitrary RR record. */
+  /* for arbitrary RR record stored in block */
   struct {
-#define RR_IMDATALEN 13 /* 16 - sizeof(short) - sizeof (char) */
     unsigned short rrtype;
-    char len; /* -1 for blockdata */
-    union {
-      char data[RR_IMDATALEN];
-      struct {
-       unsigned short datalen;
-       struct blockdata *rrdata;
-      } block;
-    } u;
-  } rr;
+    unsigned short datalen; 
+    struct blockdata *rrdata;
+  } rrblock;
+  /* for arbitrary RR record small enough to go in addr.
+     NOTE: rrblock and rrdata are discriminated by the F_KEYTAG bit
+     in the cache flags. */
+  struct datablock {
+    unsigned short rrtype;
+    unsigned char datalen;
+    char data[];
+  } rrdata;
 };
 
+#define RR_IMDATALEN (sizeof(union all_addr) - offsetof(struct datablock, data))
 
 struct bogus_addr {
   int is6, prefix;
index d4c6a26..ccf88df 100644 (file)
@@ -806,20 +806,20 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
                  if (!CHECK_LEN(header, p1, qlen, ardlen))
                    return 2; /* bad packet */
                  
-                 addr.rr.rrtype = aqtype;
-
                  /* If the data has no names and is small enough, store it in
                     the crec address field rather than allocate a block. */
-                 if (*rrdesc == -1 && ardlen <= RR_IMDATALEN)
+                 if (*rrdesc == -1 && ardlen <= (int)RR_IMDATALEN)
                    {
-                     addr.rr.len = (char)ardlen;
-                     if (ardlen != 0)
-                       memcpy(addr.rr.u.data, p1, ardlen);
+                      addr.rrdata.rrtype = aqtype;
+                      addr.rrdata.datalen = (char)ardlen;
+                      if (ardlen != 0)
+                        memcpy(addr.rrdata.data, p1, ardlen);
                    }
                  else
                    {
-                     addr.rr.len = -1;
-                     addr.rr.u.block.datalen = 0;
+                     addr.rrblock.rrtype = aqtype;
+                     addr.rrblock.datalen = 0;
+                     flags |= F_KEYTAG; /* discriminates between rrdata and rrblock */
                      
                      /* The RR data may include names, and those names may include
                         compression, which will be rendered meaningless when
@@ -827,7 +827,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
                         Here we go through a description of the packet type to
                         find the names, and extract them to a c-string and then
                         re-encode them to standalone DNS format without compression. */
-                     if (!(addr.rr.u.block.rrdata = blockdata_alloc(NULL, 0)))
+                     if (!(addr.rrblock.rrdata = blockdata_alloc(NULL, 0)))
                        return 0;
                      do
                        {
@@ -836,9 +836,9 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
                          if (desc == -1)
                            {
                              /* Copy the rest of the RR and end. */
-                             if (!blockdata_expand(addr.rr.u.block.rrdata, addr.rr.u.block.datalen, (char *)p1, endrr - p1))
+                             if (!blockdata_expand(addr.rrblock.rrdata, addr.rrblock.datalen, (char *)p1, endrr - p1))
                                return 0;
-                             addr.rr.u.block.datalen += endrr - p1;
+                             addr.rrblock.datalen += endrr - p1;
                            }
                          else if (desc == 0)
                            {
@@ -849,18 +849,18 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
                                return 2;
                              
                              len = to_wire(name);
-                             if (!blockdata_expand(addr.rr.u.block.rrdata, addr.rr.u.block.datalen, name, len))
+                             if (!blockdata_expand(addr.rrblock.rrdata, addr.rrblock.datalen, name, len))
                                return 0;
-                             addr.rr.u.block.datalen += len;
+                             addr.rrblock.datalen += len;
                            }
                          else
                            {
                              /* desc is length of a block of data to be used as-is */
                              if (desc > endrr - p1)
                                desc = endrr - p1;
-                             if (!blockdata_expand(addr.rr.u.block.rrdata, addr.rr.u.block.datalen, (char *)p1, desc))
+                             if (!blockdata_expand(addr.rrblock.rrdata, addr.rrblock.datalen, (char *)p1, desc))
                                return 0;
-                             addr.rr.u.block.datalen += desc;
+                             addr.rrblock.datalen += desc;
                              p1 += desc;
                            }
                        } while (desc != -1);
@@ -968,7 +968,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
                ttl = cttl;
              
              if (flags & F_RR)
-               addr.rr.rrtype = qtype;
+               addr.rrdata.rrtype = qtype;
 
              newc = cache_insert(name, &addr, C_IN, now, ttl, F_FORWARD | F_NEG | flags | (secure ? F_DNSSECOK : 0));  
              if (newc && cpp)
@@ -2103,8 +2103,14 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
                 do
                   {
                     int flags = crecp->flags;
+                    unsigned short rrtype;
+                    
+                     if (flags & F_KEYTAG)
+                       rrtype = crecp->addr.rrblock.rrtype;
+                     else
+                       rrtype = crecp->addr.rrdata.rrtype;
 
-                    if ((flags & F_NXDOMAIN) || crecp->addr.rr.rrtype == qtype)
+                     if ((flags & F_NXDOMAIN) || rrtype == qtype)
                       {
                         if (crec_isstale(crecp, now))
                           {
@@ -2127,23 +2133,28 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
                         
                         if (!dryrun)
                           {
-                            char *rrdata = crecp->addr.rr.u.data;
-                            unsigned short rrlen = crecp->addr.rr.len;
-                            
+                            char *rrdata = NULL;
+                            unsigned short rrlen = 0;
+
                             if (!(flags & F_NEG))
                               {
-                                if (crecp->addr.rr.len == -1)
+                                if (flags & F_KEYTAG)
+                                  {
+                                    rrlen = crecp->addr.rrblock.datalen;
+                                    rrdata = blockdata_retrieve(crecp->addr.rrblock.rrdata, crecp->addr.rrblock.datalen, NULL);
+                                  }
+                                else
                                   {
-                                    rrlen = crecp->addr.rr.u.block.datalen;
-                                    rrdata = blockdata_retrieve(crecp->addr.rr.u.block.rrdata, crecp->addr.rr.u.block.datalen, NULL);
+                                    rrlen = crecp->addr.rrdata.datalen;
+                                    rrdata = crecp->addr.rrdata.data;
                                   }
-                                
-                                if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, 
-                                                        crec_ttl(crecp, now), NULL, qtype, C_IN, "t",
-                                                        rrlen, rrdata))
-                                  anscount++;
                               }
                             
+                            if (!(flags & F_NEG) && add_resource_record(header, limit, &trunc, nameoffset, &ansp, 
+                                                                        crec_ttl(crecp, now), NULL, qtype, C_IN, "t",
+                                                                        rrlen, rrdata))
+                              anscount++;
+                                                    
                             /* log after cache insertion as log_txt mangles rrdata */
                             if (qtype == T_TXT && !(crecp->flags & F_NEG))
                               log_txt(name, (unsigned char *)rrdata, rrlen, crecp->flags & F_DNSSECOK);