]> git.ipfire.org Git - people/ms/dnsmasq.git/commitdiff
Handle extending EDNS0 OPT RR.
authorSimon Kelley <simon@thekelleys.org.uk>
Mon, 21 Dec 2015 16:23:47 +0000 (16:23 +0000)
committerSimon Kelley <simon@thekelleys.org.uk>
Mon, 21 Dec 2015 16:23:47 +0000 (16:23 +0000)
src/dnsmasq.h
src/dnssec.c
src/edns0.c
src/forward.c

index a41c8cc5f5fbc1d4e9a260e9336de375e56b2717..982881960ff1f1054e0db599adaae6f29dd05a98 100644 (file)
@@ -1117,8 +1117,6 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
 int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name, 
                             struct bogus_addr *addr, time_t now);
 int check_for_ignored_address(struct dns_header *header, size_t qlen, struct bogus_addr *baddr);
-unsigned char *find_pseudoheader(struct dns_header *header, size_t plen,
-                                size_t *len, unsigned char **p, int *is_sign);
 int check_for_local_domain(char *name, time_t now);
 unsigned int questions_crc(struct dns_header *header, size_t plen, char *buff);
 size_t resize_packet(struct dns_header *header, size_t plen, 
@@ -1514,6 +1512,8 @@ u16 *rrfilter_desc(int type);
 int expand_workspace(unsigned char ***wkspc, int *szp, int new);
 
 /* edns0.c */
+unsigned char *find_pseudoheader(struct dns_header *header, size_t plen,
+                                  size_t *len, unsigned char **p, int *is_sign, int *is_last);
 size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *limit, 
                        unsigned short udp_sz, int optno, unsigned char *opt, size_t optlen, int set_do);
 size_t add_mac(struct dns_header *header, size_t plen, char *limit, union mysockaddr *l3);
index 486e4221abe32ecb0a69306f574fccefb9b25b32..e0b7f39ded6c91859b253182263d4884ee2a7323 100644 (file)
@@ -2206,7 +2206,7 @@ size_t dnssec_generate_query(struct dns_header *header, char *end, char *name, i
 
   ret = add_do_bit(header, p - (unsigned char *)header, end);
 
-  if (find_pseudoheader(header, ret, NULL, &p, NULL))
+  if (find_pseudoheader(header, ret, NULL, &p, NULL, NULL))
     PUTSHORT(edns_pktsz, p);
 
   return ret;
index f348b01fe1408dc6d1287cf3c891868ae369660f..d1a11e788c1fbda948b2456566a970b6ed0b3ae7 100644 (file)
 
 #include "dnsmasq.h"
 
-unsigned char *find_pseudoheader(struct dns_header *header, size_t plen, size_t  *len, unsigned char **p, int *is_sign)
+unsigned char *find_pseudoheader(struct dns_header *header, size_t plen, size_t  *len, unsigned char **p, int *is_sign, int *is_last)
 {
   /* See if packet has an RFC2671 pseudoheader, and if so return a pointer to it. 
      also return length of pseudoheader in *len and pointer to the UDP size in *p
      Finally, check to see if a packet is signed. If it is we cannot change a single bit before
-     forwarding. We look for SIG and TSIG in the addition section, and TKEY queries (for GSS-TSIG) */
+     forwarding. We look for TSIG in the addition section, and TKEY queries (for GSS-TSIG) */
   
   int i, arcount = ntohs(header->arcount);
   unsigned char *ansp = (unsigned char *)(header+1);
@@ -76,8 +76,13 @@ unsigned char *find_pseudoheader(struct dns_header *header, size_t plen, size_t
        {
          if (len)
            *len = ansp - start;
+
          if (p)
            *p = save;
+         
+         if (is_last)
+           *is_last = (i == arcount-1);
+
          ret = start;
        }
       else if (is_sign && 
@@ -100,50 +105,31 @@ struct macparm {
 size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *limit, 
                        unsigned short udp_sz, int optno, unsigned char *opt, size_t optlen, int set_do)
 { 
-  unsigned char *lenp, *datap, *p;
-  int rdlen, is_sign;
+  unsigned char *lenp, *datap, *p, *udp_len, *buff = NULL;
+  int rdlen = 0, is_sign, is_last;
+  unsigned short flags = set_do ? 0x8000 : 0, rcode = 0;
+
+  p = find_pseudoheader(header, plen, NULL, &udp_len, &is_sign, &is_last);
   
-  if (!(p = find_pseudoheader(header, plen, NULL, NULL, &is_sign)))
-    {
-      if (is_sign)
-       return plen;
+  if (is_sign)
+    return plen;
 
-      /* We are adding the pseudoheader */
-      if (!(p = skip_questions(header, plen)) ||
-         !(p = skip_section(p, 
-                            ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount), 
-                            header, plen)))
-       return plen;
-      *p++ = 0; /* empty name */
-      PUTSHORT(T_OPT, p);
-      PUTSHORT(udp_sz, p); /* max packet length, 512 if not given in EDNS0 header */
-      PUTSHORT(0, p);    /* extended RCODE and version */
-      PUTSHORT(set_do ? 0x8000 : 0, p); /* DO flag */
-      lenp = p;
-      PUTSHORT(0, p);    /* RDLEN */
-      rdlen = 0;
-      if (((ssize_t)optlen) > (limit - (p + 4)))
-       return plen; /* Too big */
-      header->arcount = htons(ntohs(header->arcount) + 1);
-      datap = p;
-    }
-  else
+  if (p)
     {
+      /* Existing header */
       int i;
-      unsigned short code, len, flags;
-      
-      /* Must be at the end, if exists */
-      if (ntohs(header->arcount) != 1 ||
-         is_sign ||
-         (!(p = skip_name(p, header, plen, 10))))
-       return plen;
-      
-      p += 6; /* skip UDP length and RCODE */
+      unsigned short code, len;
+
+      p = udp_len;
+      GETSHORT(udp_sz, p);
+      GETSHORT(rcode, p);
       GETSHORT(flags, p);
+
       if (set_do)
        {
          p -=2;
-         PUTSHORT(flags | 0x8000, p);
+         flags |= 0x8000;
+         PUTSHORT(flags, p);
        }
 
       lenp = p;
@@ -165,22 +151,61 @@ size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *l
            return plen;
          p += len;
        }
-      
-      if (((ssize_t)optlen) > (limit - (p + 4)))
-       return plen; /* Too big */
+
+      /* If we're going to extend the RR, it has to be the last RR in the packet */
+      if (!is_last)
+       {
+         /* First, take a copy of the options. */
+         if (rdlen != 0 && (buff = whine_malloc(rdlen)))
+           memcpy(buff, datap, rdlen);       
+         
+         /* now, delete OPT RR */
+         plen = rrfilter(header, plen, 0);
+         
+         /* Now, force addition of a new one */
+         p = NULL;       
+       }
+    }
+  
+  if (!p)
+    {
+      /* We are (re)adding the pseudoheader */
+      if (!(p = skip_questions(header, plen)) ||
+         !(p = skip_section(p, 
+                            ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount), 
+                            header, plen)))
+       return plen;
+      *p++ = 0; /* empty name */
+      PUTSHORT(T_OPT, p);
+      PUTSHORT(udp_sz, p); /* max packet length, 512 if not given in EDNS0 header */
+      PUTSHORT(rcode, p);    /* extended RCODE and version */
+      PUTSHORT(flags, p); /* DO flag */
+      lenp = p;
+      PUTSHORT(rdlen, p);    /* RDLEN */
+      datap = p;
+      /* Copy back any options */
+      if (buff)
+       {
+         memcpy(p, buff, rdlen);
+         free(buff);
+         p += rdlen;
+       }
+      header->arcount = htons(ntohs(header->arcount) + 1);
     }
   
+  if (((ssize_t)optlen) > (limit - (p + 4)))
+    return plen; /* Too big */
+  
+  /* Add new option */
   if (optno != 0)
     {
       PUTSHORT(optno, p);
       PUTSHORT(optlen, p);
       memcpy(p, opt, optlen);
       p += optlen;  
+      PUTSHORT(p - datap, lenp);
     }
-
-  PUTSHORT(p - datap, lenp);
   return p - (unsigned char *)header;
-  
 }
 
 static int filter_mac(int family, char *addrp, char *mac, size_t maclen, void *parmv)
index 041353c37c8227924f2577e7e0afd213daf81639..2ca3c86f2d295e3d52d4deed3fd8e5612c99e868 100644 (file)
@@ -276,7 +276,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
          blockdata_retrieve(forward->stash, forward->stash_len, (void *)header);
          plen = forward->stash_len;
          
-         if (find_pseudoheader(header, plen, NULL, &pheader, &is_sign) && !is_sign)
+         if (find_pseudoheader(header, plen, NULL, &pheader, &is_sign, NULL) && !is_sign)
            PUTSHORT(SAFE_PKTSZ, pheader);
 
          if (forward->sentto->addr.sa.sa_family == AF_INET) 
@@ -479,7 +479,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
                }
              
 #ifdef HAVE_DNSSEC
-             if (option_bool(OPT_DNSSEC_VALID) && !do_bit)
+             if (option_bool(OPT_DNSSEC_VALID) && (forward->flags & FREC_ADDED_PHEADER))
                {
                  /* Difficult one here. If our client didn't send EDNS0, we will have set the UDP
                     packet size to 512. But that won't provide space for the RRSIGS in many cases.
@@ -489,7 +489,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
                     the truncated bit? */                
                  unsigned char *pheader;
                  int is_sign;
-                 if (find_pseudoheader(header, plen, NULL, &pheader, &is_sign))
+                 if (find_pseudoheader(header, plen, NULL, &pheader, &is_sign, NULL) && !is_sign)
                    PUTSHORT(start->edns_pktsz, pheader);
                }
 #endif
@@ -584,7 +584,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
     }
 #endif
   
-  if ((pheader = find_pseudoheader(header, n, &plen, &sizep, &is_sign)))
+  if ((pheader = find_pseudoheader(header, n, &plen, &sizep, &is_sign, NULL)))
     {
       if (check_subnet && !check_source(header, plen, pheader, query_source))
        {
@@ -779,7 +779,7 @@ void reply_query(int fd, int family, time_t now)
       int is_sign;
       
       /* recreate query from reply */
-      pheader = find_pseudoheader(header, (size_t)n, &plen, NULL, &is_sign);
+      pheader = find_pseudoheader(header, (size_t)n, &plen, NULL, &is_sign, NULL);
       if (!is_sign)
        {
          header->ancount = htons(0);
@@ -1313,7 +1313,7 @@ void receive_query(struct listener *listen, time_t now)
 #endif
     }
   
-  if (find_pseudoheader(header, (size_t)n, NULL, &pheader, NULL))
+  if (find_pseudoheader(header, (size_t)n, NULL, &pheader, NULL, NULL))
     { 
       unsigned short flags;
       
@@ -1569,7 +1569,7 @@ unsigned char *tcp_request(int confd, time_t now,
       
       do_bit = 0;
 
-      if (find_pseudoheader(header, (size_t)size, NULL, &pheader, NULL))
+      if (find_pseudoheader(header, (size_t)size, NULL, &pheader, NULL, NULL))
        { 
          unsigned short flags;
          
@@ -1578,7 +1578,7 @@ unsigned char *tcp_request(int confd, time_t now,
          GETSHORT(flags, pheader);
       
          if (flags & 0x8000)
-           do_bit = 1;/* do bit */ 
+           do_bit = 1; /* do bit */ 
        }
 
 #ifdef HAVE_AUTH