Diff for /embedaddon/dnsmasq/src/edns0.c between versions 1.1.1.1 and 1.1.1.3

version 1.1.1.1, 2016/11/02 09:57:01 version 1.1.1.3, 2023/09/27 11:02:07
Line 1 Line 1
/* dnsmasq is Copyright (c) 2000-2016 Simon Kelley/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley
   
    This program is free software; you can redistribute it and/or modify     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     it under the terms of the GNU General Public License as published by
Line 144  size_t add_pseudoheader(struct dns_header *header, siz Line 144  size_t add_pseudoheader(struct dns_header *header, siz
           GETSHORT(len, p);            GETSHORT(len, p);
                       
           /* malformed option, delete the whole OPT RR and start again. */            /* malformed option, delete the whole OPT RR and start again. */
          if (i + len > rdlen)          if (i + 4 + len > rdlen)
             {              {
               rdlen = 0;                rdlen = 0;
               is_last = 0;                is_last = 0;
Line 159  size_t add_pseudoheader(struct dns_header *header, siz Line 159  size_t add_pseudoheader(struct dns_header *header, siz
               /* delete option if we're to replace it. */                /* delete option if we're to replace it. */
               p -= 4;                p -= 4;
               rdlen -= len + 4;                rdlen -= len + 4;
              memcpy(p, p+len+4, rdlen - i);              memmove(p, p+len+4, rdlen - i);
               PUTSHORT(rdlen, lenp);                PUTSHORT(rdlen, lenp);
               lenp -= 2;                lenp -= 2;
             }              }
Line 178  size_t add_pseudoheader(struct dns_header *header, siz Line 178  size_t add_pseudoheader(struct dns_header *header, siz
             memcpy(buff, datap, rdlen);                     memcpy(buff, datap, rdlen);       
                       
           /* now, delete OPT RR */            /* now, delete OPT RR */
          plen = rrfilter(header, plen, 0);          plen = rrfilter(header, plen, RRFILTER_EDNS0);
                       
           /* Now, force addition of a new one */            /* Now, force addition of a new one */
           p = NULL;                   p = NULL;       
Line 192  size_t add_pseudoheader(struct dns_header *header, siz Line 192  size_t add_pseudoheader(struct dns_header *header, siz
           !(p = skip_section(p,             !(p = skip_section(p, 
                              ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount),                                ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount), 
                              header, plen)))                               header, plen)))
         {
           free(buff);
         return plen;          return plen;
         }
         if (p + 11 > limit)
         {
           free(buff);
           return plen; /* Too big */
         }
       *p++ = 0; /* empty name */        *p++ = 0; /* empty name */
       PUTSHORT(T_OPT, p);        PUTSHORT(T_OPT, p);
       PUTSHORT(udp_sz, p); /* max packet length, 512 if not given in EDNS0 header */        PUTSHORT(udp_sz, p); /* max packet length, 512 if not given in EDNS0 header */
Line 204  size_t add_pseudoheader(struct dns_header *header, siz Line 212  size_t add_pseudoheader(struct dns_header *header, siz
       /* Copy back any options */        /* Copy back any options */
       if (buff)        if (buff)
         {          {
             if (p + rdlen > limit)
             {
               free(buff);
               return plen; /* Too big */
             }
           memcpy(p, buff, rdlen);            memcpy(p, buff, rdlen);
           free(buff);            free(buff);
           p += rdlen;            p += rdlen;
         }          }
      header->arcount = htons(ntohs(header->arcount) + 1);      
       /* Only bump arcount if RR is going to fit */ 
       if (((ssize_t)optlen) <= (limit - (p + 4)))
         header->arcount = htons(ntohs(header->arcount) + 1);
     }      }
       
   if (((ssize_t)optlen) > (limit - (p + 4)))    if (((ssize_t)optlen) > (limit - (p + 4)))
Line 217  size_t add_pseudoheader(struct dns_header *header, siz Line 233  size_t add_pseudoheader(struct dns_header *header, siz
   /* Add new option */    /* Add new option */
   if (optno != 0 && replace != 2)    if (optno != 0 && replace != 2)
     {      {
         if (p + 4 > limit)
          return plen; /* Too big */
       PUTSHORT(optno, p);        PUTSHORT(optno, p);
       PUTSHORT(optlen, p);        PUTSHORT(optlen, p);
         if (p + optlen > limit)
          return plen; /* Too big */
       memcpy(p, opt, optlen);        memcpy(p, opt, optlen);
       p += optlen;          p += optlen;  
       PUTSHORT(p - datap, lenp);        PUTSHORT(p - datap, lenp);
Line 244  static void encoder(unsigned char *in, char *out) Line 264  static void encoder(unsigned char *in, char *out)
   out[3] = char64(in[2]);    out[3] = char64(in[2]);
 }  }
   
static size_t add_dns_client(struct dns_header *header, size_t plen, unsigned char *limit, union mysockaddr *l3, time_t now)/* 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];    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;      if (option_bool(OPT_STRIP_MAC))
         replace = 1;
      if (option_bool(OPT_MAC_HEX))       *cacheablep = 0;
        print_mac(encode, mac, maclen);    
      else       if (option_bool(OPT_MAC_HEX))
        {         print_mac(encode, mac, maclen);
          encoder(mac, encode);       else
          encoder(mac+3, encode+4);         {
          encode[8] = 0;           encoder(mac, encode);
        }           encoder(mac+3, encode+4);
            encode[8] = 0;
          }
     }      }
     else if (option_bool(OPT_STRIP_MAC))
       replace = 2;
   
  return add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_NOMDEVICEID, (unsigned char *)encode, strlen(encode), 0, replace);   if (replace != 0 || maclen == 6)
     plen = add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_NOMDEVICEID, (unsigned char *)encode, strlen(encode), 0, replace);
 
   return plen;
 }  }
   
   
static size_t add_mac(struct dns_header *header, size_t plen, unsigned char *limit, union mysockaddr *l3, time_t now)/* 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];    unsigned char mac[DHCP_CHADDR_MAX];
   
   if ((maclen = find_mac(l3, mac, 1, now)) != 0)  
     plen = add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_MAC, mac, maclen, 0, 0);   
           
     if (option_bool(OPT_ADD_MAC) && (maclen = find_mac(l3, mac, 1, now)) != 0)
       {
         *cacheablep = 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;     return plen; 
 }  }
   
 struct subnet_opt {  struct subnet_opt {
   u16 family;    u16 family;
  u8 source_netmask, scope_netmask;  u8 source_netmask, scope_netmask; 
#ifdef HAVE_IPV6  
   u8 addr[IN6ADDRSZ];    u8 addr[IN6ADDRSZ];
 #else  
   u8 addr[INADDRSZ];  
 #endif  
 };  };
   
 static void *get_addrp(union mysockaddr *addr, const short family)   static void *get_addrp(union mysockaddr *addr, const short family) 
 {  {
 #ifdef HAVE_IPV6  
   if (family == AF_INET6)    if (family == AF_INET6)
     return &addr->in6.sin6_addr;      return &addr->in6.sin6_addr;
 #endif  
   
   return &addr->in.sin_addr;    return &addr->in.sin_addr;
 }  }
   
static size_t calc_subnet_opt(struct subnet_opt *opt, union mysockaddr *source)static size_t calc_subnet_opt(struct subnet_opt *opt, union mysockaddr *source, int *cacheablep)
 {  {
   /* http://tools.ietf.org/html/draft-vandergaast-edns-client-subnet-02 */    /* http://tools.ietf.org/html/draft-vandergaast-edns-client-subnet-02 */
       
   int len;    int len;
  void *addrp;  void *addrp = NULL;
   int sa_family = source->sa.sa_family;    int sa_family = source->sa.sa_family;
  int cacheable = 0;
   
   opt->source_netmask = 0;    opt->source_netmask = 0;
   opt->scope_netmask = 0;    opt->scope_netmask = 0;
    
#ifdef HAVE_IPV6 
   if (source->sa.sa_family == AF_INET6 && daemon->add_subnet6)    if (source->sa.sa_family == AF_INET6 && daemon->add_subnet6)
     {      {
       opt->source_netmask = daemon->add_subnet6->mask;        opt->source_netmask = daemon->add_subnet6->mask;
Line 318  static size_t calc_subnet_opt(struct subnet_opt *opt,  Line 356  static size_t calc_subnet_opt(struct subnet_opt *opt, 
         {          {
           sa_family = daemon->add_subnet6->addr.sa.sa_family;            sa_family = daemon->add_subnet6->addr.sa.sa_family;
           addrp = get_addrp(&daemon->add_subnet6->addr, sa_family);            addrp = get_addrp(&daemon->add_subnet6->addr, sa_family);
             cacheable = 1;
         }           } 
       else         else 
         addrp = &source->in6.sin6_addr;          addrp = &source->in6.sin6_addr;
     }      }
 #endif  
   
   if (source->sa.sa_family == AF_INET && daemon->add_subnet4)    if (source->sa.sa_family == AF_INET && daemon->add_subnet4)
     {      {
Line 331  static size_t calc_subnet_opt(struct subnet_opt *opt,  Line 369  static size_t calc_subnet_opt(struct subnet_opt *opt, 
         {          {
           sa_family = daemon->add_subnet4->addr.sa.sa_family;            sa_family = daemon->add_subnet4->addr.sa.sa_family;
           addrp = get_addrp(&daemon->add_subnet4->addr, sa_family);            addrp = get_addrp(&daemon->add_subnet4->addr, sa_family);
             cacheable = 1; /* Address is constant */
         }           } 
         else           else 
           addrp = &source->in.sin_addr;            addrp = &source->in.sin_addr;
     }      }
       
 #ifdef HAVE_IPV6  
   opt->family = htons(sa_family == AF_INET6 ? 2 : 1);    opt->family = htons(sa_family == AF_INET6 ? 2 : 1);
 #else  
   opt->family = htons(1);  
 #endif  
       
  len = 0;  if (addrp && opt->source_netmask != 0)
   
  if (opt->source_netmask != 0) 
     {      {
       len = ((opt->source_netmask - 1) >> 3) + 1;        len = ((opt->source_netmask - 1) >> 3) + 1;
       memcpy(opt->addr, addrp, len);        memcpy(opt->addr, addrp, len);
       if (opt->source_netmask & 7)        if (opt->source_netmask & 7)
         opt->addr[len-1] &= 0xff << (8 - (opt->source_netmask & 7));          opt->addr[len-1] &= 0xff << (8 - (opt->source_netmask & 7));
     }      }
     else
       {
         cacheable = 1; /* No address ever supplied. */
         len = 0;
       }
   
     if (cacheablep)
       *cacheablep = cacheable;
       
   return len + 4;    return len + 4;
 }  }
     
static size_t add_source_addr(struct dns_header *header, size_t plen, unsigned char *limit, union mysockaddr *source)/* 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 */    /* http://tools.ietf.org/html/draft-vandergaast-edns-client-subnet-02 */
       
  int len;  int replace = 0, len = 0;
   struct subnet_opt opt;    struct subnet_opt opt;
       
  len = calc_subnet_opt(&opt, source);  if (option_bool(OPT_CLIENT_SUBNET))
  return add_pseudoheader(header, plen, (unsigned char *)limit, PACKETSZ, EDNS0_OPTION_CLIENT_SUBNET, (unsigned char *)&opt, len, 0, 0);    {
       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)  int check_source(struct dns_header *header, size_t plen, unsigned char *pseudoheader, union mysockaddr *peer)
Line 375  int check_source(struct dns_header *header, size_t ple Line 430  int check_source(struct dns_header *header, size_t ple
   unsigned char *p;    unsigned char *p;
   int code, i, rdlen;    int code, i, rdlen;
       
   calc_len = calc_subnet_opt(&opt, peer);  calc_len = calc_subnet_opt(&opt, peer, NULL);
         
   if (!(p = skip_name(pseudoheader, header, plen, 10)))  if (!(p = skip_name(pseudoheader, header, plen, 10)))
     return 1;    return 1;
     
   p += 8; /* skip UDP length and RCODE */  p += 8; /* skip UDP length and RCODE */
     
   GETSHORT(rdlen, p);  GETSHORT(rdlen, p);
   if (!CHECK_LEN(header, p, plen, rdlen))  if (!CHECK_LEN(header, p, plen, rdlen))
     return 1; /* bad packet */    return 1; /* bad packet */
     
   /* check if option there */  /* check if option there */
    for (i = 0; i + 4 < rdlen; i += len + 4)     for (i = 0; i + 4 < rdlen; i += len + 4)
      {       {
        GETSHORT(code, p);         GETSHORT(code, p);
Line 404  int check_source(struct dns_header *header, size_t ple Line 459  int check_source(struct dns_header *header, size_t ple
    return 1;     return 1;
 }  }
   
size_t add_edns0_config(struct dns_header *header, size_t plen, unsigned char *limit, /* See https://docs.umbrella.com/umbrella-api/docs/identifying-dns-traffic for
                        union mysockaddr *source, time_t now, int *check_subnet)     * detailed information on packet formating.
  */
 #define UMBRELLA_VERSION    1
 #define UMBRELLA_TYPESZ     2
 
 #define UMBRELLA_ASSET      0x0004
 #define UMBRELLA_ASSETSZ    sizeof(daemon->umbrella_asset)
 #define UMBRELLA_ORG        0x0008
 #define UMBRELLA_ORGSZ      sizeof(daemon->umbrella_org)
 #define UMBRELLA_IPV4       0x0010
 #define UMBRELLA_IPV6       0x0020
 #define UMBRELLA_DEVICE     0x0040
 #define UMBRELLA_DEVICESZ   sizeof(daemon->umbrella_device)
 
 struct umbrella_opt {
   u8 magic[4];
   u8 version;
   u8 flags;
   /* We have 4 possible fields since we'll never send both IPv4 and
    * IPv6, so using the larger of the two to calculate max buffer size.
    * Each field also has a type header.  So the following accounts for
    * the type headers and each field size to get a max buffer size.
    */
   u8 fields[4 * UMBRELLA_TYPESZ + UMBRELLA_ORGSZ + IN6ADDRSZ + UMBRELLA_DEVICESZ + UMBRELLA_ASSETSZ];
 };
 
 static size_t add_umbrella_opt(struct dns_header *header, size_t plen, unsigned char *limit, union mysockaddr *source, int *cacheable)
 {  {
  *check_subnet = 0;  *cacheable = 0;
   
  if (option_bool(OPT_ADD_MAC))  struct umbrella_opt opt = {{"ODNS"}, UMBRELLA_VERSION, 0, {}};
    plen  = add_mac(header, plen, limit, source, now);  u8 *u = &opt.fields[0];
   int family = source->sa.sa_family;
   int size = family == AF_INET ? INADDRSZ : IN6ADDRSZ;
 
   if (daemon->umbrella_org)
     {
       PUTSHORT(UMBRELLA_ORG, u);
       PUTLONG(daemon->umbrella_org, u);
     }
       
  if (option_bool(OPT_MAC_B64) || option_bool(OPT_MAC_HEX))  PUTSHORT(family == AF_INET ? UMBRELLA_IPV4 : UMBRELLA_IPV6, u);
    plen = add_dns_client(header, plen, limit, source, now);  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 (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 *cacheable)    
   {
     *cacheable = 1;
     
     plen  = add_mac(header, plen, limit, source, now, cacheable);
     plen = add_dns_client(header, plen, limit, source, now, cacheable);
     
   if (daemon->dns_client_id)    if (daemon->dns_client_id)
     plen = add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_NOMCPEID,       plen = add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_NOMCPEID, 
                             (unsigned char *)daemon->dns_client_id, strlen(daemon->dns_client_id), 0, 1);                              (unsigned char *)daemon->dns_client_id, strlen(daemon->dns_client_id), 0, 1);
   
     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);
    {          
      plen = add_source_addr(header, plen, limit, source);  
      *check_subnet = 1; 
    } 
           
   return plen;    return plen;
 }  }

Removed from v.1.1.1.1  
changed lines
  Added in v.1.1.1.3


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>