Imported Upstream version 2.88
[platform/upstream/dnsmasq.git] / src / edns0.c
index 7bd26b8..c498eb1 100644 (file)
@@ -1,4 +1,4 @@
-/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
+/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -178,7 +178,7 @@ size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *l
            memcpy(buff, datap, rdlen);       
          
          /* now, delete OPT RR */
-         plen = rrfilter(header, plen, 0);
+         plen = rrfilter(header, plen, RRFILTER_EDNS0);
          
          /* Now, force addition of a new one */
          p = NULL;       
@@ -264,44 +264,62 @@ static void encoder(unsigned char *in, char *out)
   out[3] = char64(in[2]);
 }
 
+/* OPT_ADD_MAC = MAC is added (if available)
+   OPT_ADD_MAC + OPT_STRIP_MAC = MAC is replaced, if not available, it is only removed
+   OPT_STRIP_MAC = MAC is removed */
 static size_t add_dns_client(struct dns_header *header, size_t plen, unsigned char *limit,
                             union mysockaddr *l3, time_t now, int *cacheablep)
 {
-  int maclen, replace = 2; /* can't get mac address, just delete any incoming. */
+  int replace = 0, maclen = 0;
   unsigned char mac[DHCP_CHADDR_MAX];
-  char encode[18]; /* handle 6 byte MACs */
+  char encode[18]; /* handle 6 byte MACs ONLY */
 
-  if ((maclen = find_mac(l3, mac, 1, now)) == 6)
+  if ((option_bool(OPT_MAC_B64) || option_bool(OPT_MAC_HEX)) && (maclen = find_mac(l3, mac, 1, now)) == 6)
     {
-      replace = 1;
-      *cacheablep = 0;
-
-      if (option_bool(OPT_MAC_HEX))
-       print_mac(encode, mac, maclen);
-      else
-       {
-         encoder(mac, encode);
-         encoder(mac+3, encode+4);
-         encode[8] = 0;
-       }
+      if (option_bool(OPT_STRIP_MAC))
+        replace = 1;
+       *cacheablep = 0;
+    
+       if (option_bool(OPT_MAC_HEX))
+        print_mac(encode, mac, maclen);
+       else
+        {
+          encoder(mac, encode);
+          encoder(mac+3, encode+4);
+          encode[8] = 0;
+        }
     }
+  else if (option_bool(OPT_STRIP_MAC))
+    replace = 2;
+
+  if (replace != 0 || maclen == 6)
+    plen = add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_NOMDEVICEID, (unsigned char *)encode, strlen(encode), 0, replace);
 
-  return add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_NOMDEVICEID, (unsigned char *)encode, strlen(encode), 0, replace); 
+  return plen;
 }
 
 
+/* OPT_ADD_MAC = MAC is added (if available)
+   OPT_ADD_MAC + OPT_STRIP_MAC = MAC is replaced, if not available, it is only removed
+   OPT_STRIP_MAC = MAC is removed */
 static size_t add_mac(struct dns_header *header, size_t plen, unsigned char *limit,
                      union mysockaddr *l3, time_t now, int *cacheablep)
 {
-  int maclen;
+  int maclen = 0, replace = 0;
   unsigned char mac[DHCP_CHADDR_MAX];
-
-  if ((maclen = find_mac(l3, mac, 1, now)) != 0)
+    
+  if (option_bool(OPT_ADD_MAC) && (maclen = find_mac(l3, mac, 1, now)) != 0)
     {
       *cacheablep = 0;
-      plen = add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_MAC, mac, maclen, 0, 0); 
+      if (option_bool(OPT_STRIP_MAC))
+       replace = 1;
     }
+  else if (option_bool(OPT_STRIP_MAC))
+    replace = 2;
   
+  if (replace != 0 || maclen != 0)
+    plen = add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_MAC, mac, maclen, 0, replace);
+
   return plen; 
 }
 
@@ -378,15 +396,29 @@ static size_t calc_subnet_opt(struct subnet_opt *opt, union mysockaddr *source,
   return len + 4;
 }
  
-static size_t add_source_addr(struct dns_header *header, size_t plen, unsigned char *limit, union mysockaddr *source, int *cacheable)
+/* OPT_CLIENT_SUBNET = client subnet is added
+   OPT_CLIENT_SUBNET + OPT_STRIP_ECS = client subnet is replaced
+   OPT_STRIP_ECS = client subnet is removed */
+static size_t add_source_addr(struct dns_header *header, size_t plen, unsigned char *limit,
+                             union mysockaddr *source, int *cacheable)
 {
   /* http://tools.ietf.org/html/draft-vandergaast-edns-client-subnet-02 */
   
-  int len;
+  int replace = 0, len = 0;
   struct subnet_opt opt;
   
-  len = calc_subnet_opt(&opt, source, cacheable);
-  return add_pseudoheader(header, plen, (unsigned char *)limit, PACKETSZ, EDNS0_OPTION_CLIENT_SUBNET, (unsigned char *)&opt, len, 0, 0);
+  if (option_bool(OPT_CLIENT_SUBNET))
+    {
+      if (option_bool(OPT_STRIP_ECS))
+       replace = 1;
+      len = calc_subnet_opt(&opt, source, cacheable);
+    }
+  else if (option_bool(OPT_STRIP_ECS))
+    replace = 2;
+  else
+    return plen;
+
+  return add_pseudoheader(header, plen, (unsigned char *)limit, PACKETSZ, EDNS0_OPTION_CLIENT_SUBNET, (unsigned char *)&opt, len, 0, replace);
 }
 
 int check_source(struct dns_header *header, size_t plen, unsigned char *pseudoheader, union mysockaddr *peer)
@@ -460,47 +492,45 @@ static size_t add_umbrella_opt(struct dns_header *header, size_t plen, unsigned
 
   struct umbrella_opt opt = {{"ODNS"}, UMBRELLA_VERSION, 0, {}};
   u8 *u = &opt.fields[0];
-
-  if (daemon->umbrella_org) {
-    PUTSHORT(UMBRELLA_ORG, u);
-    PUTLONG(daemon->umbrella_org, u);
-  }
-
   int family = source->sa.sa_family;
-  PUTSHORT(family == AF_INET ? UMBRELLA_IPV4 : UMBRELLA_IPV6, u);
   int size = family == AF_INET ? INADDRSZ : IN6ADDRSZ;
+
+  if (daemon->umbrella_org)
+    {
+      PUTSHORT(UMBRELLA_ORG, u);
+      PUTLONG(daemon->umbrella_org, u);
+    }
+  
+  PUTSHORT(family == AF_INET ? UMBRELLA_IPV4 : UMBRELLA_IPV6, u);
   memcpy(u, get_addrp(source, family), size);
   u += size;
+  
+  if (option_bool(OPT_UMBRELLA_DEVID))
+    {
+      PUTSHORT(UMBRELLA_DEVICE, u);
+      memcpy(u, (char *)&daemon->umbrella_device, UMBRELLA_DEVICESZ);
+      u += UMBRELLA_DEVICESZ;
+    }
 
-  if (option_bool(OPT_UMBRELLA_DEVID)) {
-    PUTSHORT(UMBRELLA_DEVICE, u);
-    memcpy(u, (char *)&daemon->umbrella_device, UMBRELLA_DEVICESZ);
-    u += UMBRELLA_DEVICESZ;
-  }
-
-  if (daemon->umbrella_asset) {
-    PUTSHORT(UMBRELLA_ASSET, u);
-    PUTLONG(daemon->umbrella_asset, u);
-  }
-
-  int len = u - &opt.magic[0];
-  return add_pseudoheader(header, plen, (unsigned char *)limit, PACKETSZ, EDNS0_OPTION_UMBRELLA, (unsigned char *)&opt, len, 0, 1);
+  if (daemon->umbrella_asset)
+    {
+      PUTSHORT(UMBRELLA_ASSET, u);
+      PUTLONG(daemon->umbrella_asset, u);
+    }
+  
+  return add_pseudoheader(header, plen, (unsigned char *)limit, PACKETSZ, EDNS0_OPTION_UMBRELLA, (unsigned char *)&opt, u - (u8 *)&opt, 0, 1);
 }
 
 /* Set *check_subnet if we add a client subnet option, which needs to checked 
    in the reply. Set *cacheable to zero if we add an option which the answer
    may depend on. */
 size_t add_edns0_config(struct dns_header *header, size_t plen, unsigned char *limit, 
-                       union mysockaddr *source, time_t now, int *check_subnet, int *cacheable)    
+                       union mysockaddr *source, time_t now, int *cacheable)    
 {
-  *check_subnet = 0;
   *cacheable = 1;
   
-  if (option_bool(OPT_ADD_MAC))
-    plen  = add_mac(header, plen, limit, source, now, cacheable);
-  
-  if (option_bool(OPT_MAC_B64) || option_bool(OPT_MAC_HEX))
-    plen = add_dns_client(header, plen, limit, source, now, cacheable);
+  plen  = add_mac(header, plen, limit, source, now, cacheable);
+  plen = add_dns_client(header, plen, limit, source, now, cacheable);
   
   if (daemon->dns_client_id)
     plen = add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_NOMCPEID, 
@@ -509,11 +539,7 @@ size_t add_edns0_config(struct dns_header *header, size_t plen, unsigned char *l
   if (option_bool(OPT_UMBRELLA))
     plen = add_umbrella_opt(header, plen, limit, source, cacheable);
   
-  if (option_bool(OPT_CLIENT_SUBNET))
-    {
-      plen = add_source_addr(header, plen, limit, source, cacheable); 
-      *check_subnet = 1;
-    }
-         
+  plen = add_source_addr(header, plen, limit, source, cacheable);
+         
   return plen;
 }