File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / dnsmasq / src / edns0.c
Revision 1.1.1.3 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Sep 27 11:02:07 2023 UTC (10 months, 2 weeks ago) by misho
Branches: dnsmasq, MAIN
CVS tags: v8_2p1, HEAD
Version 8.2p1

    1: /* dnsmasq is Copyright (c) 2000-2022 Simon Kelley
    2: 
    3:    This program is free software; you can redistribute it and/or modify
    4:    it under the terms of the GNU General Public License as published by
    5:    the Free Software Foundation; version 2 dated June, 1991, or
    6:    (at your option) version 3 dated 29 June, 2007.
    7:  
    8:    This program is distributed in the hope that it will be useful,
    9:    but WITHOUT ANY WARRANTY; without even the implied warranty of
   10:    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   11:    GNU General Public License for more details.
   12:      
   13:    You should have received a copy of the GNU General Public License
   14:    along with this program.  If not, see <http://www.gnu.org/licenses/>.
   15: */
   16: 
   17: #include "dnsmasq.h"
   18: 
   19: unsigned char *find_pseudoheader(struct dns_header *header, size_t plen, size_t  *len, unsigned char **p, int *is_sign, int *is_last)
   20: {
   21:   /* See if packet has an RFC2671 pseudoheader, and if so return a pointer to it. 
   22:      also return length of pseudoheader in *len and pointer to the UDP size in *p
   23:      Finally, check to see if a packet is signed. If it is we cannot change a single bit before
   24:      forwarding. We look for TSIG in the addition section, and TKEY queries (for GSS-TSIG) */
   25:   
   26:   int i, arcount = ntohs(header->arcount);
   27:   unsigned char *ansp = (unsigned char *)(header+1);
   28:   unsigned short rdlen, type, class;
   29:   unsigned char *ret = NULL;
   30: 
   31:   if (is_sign)
   32:     {
   33:       *is_sign = 0;
   34: 
   35:       if (OPCODE(header) == QUERY)
   36: 	{
   37: 	  for (i = ntohs(header->qdcount); i != 0; i--)
   38: 	    {
   39: 	      if (!(ansp = skip_name(ansp, header, plen, 4)))
   40: 		return NULL;
   41: 	      
   42: 	      GETSHORT(type, ansp); 
   43: 	      GETSHORT(class, ansp);
   44: 	      
   45: 	      if (class == C_IN && type == T_TKEY)
   46: 		*is_sign = 1;
   47: 	    }
   48: 	}
   49:     }
   50:   else
   51:     {
   52:       if (!(ansp = skip_questions(header, plen)))
   53: 	return NULL;
   54:     }
   55:     
   56:   if (arcount == 0)
   57:     return NULL;
   58:   
   59:   if (!(ansp = skip_section(ansp, ntohs(header->ancount) + ntohs(header->nscount), header, plen)))
   60:     return NULL; 
   61:   
   62:   for (i = 0; i < arcount; i++)
   63:     {
   64:       unsigned char *save, *start = ansp;
   65:       if (!(ansp = skip_name(ansp, header, plen, 10)))
   66: 	return NULL; 
   67: 
   68:       GETSHORT(type, ansp);
   69:       save = ansp;
   70:       GETSHORT(class, ansp);
   71:       ansp += 4; /* TTL */
   72:       GETSHORT(rdlen, ansp);
   73:       if (!ADD_RDLEN(header, ansp, plen, rdlen))
   74: 	return NULL;
   75:       if (type == T_OPT)
   76: 	{
   77: 	  if (len)
   78: 	    *len = ansp - start;
   79: 
   80: 	  if (p)
   81: 	    *p = save;
   82: 	  
   83: 	  if (is_last)
   84: 	    *is_last = (i == arcount-1);
   85: 
   86: 	  ret = start;
   87: 	}
   88:       else if (is_sign && 
   89: 	       i == arcount - 1 && 
   90: 	       class == C_ANY && 
   91: 	       type == T_TSIG)
   92: 	*is_sign = 1;
   93:     }
   94:   
   95:   return ret;
   96: }
   97:  
   98: 
   99: /* replace == 2 ->delete existing option only. */
  100: size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *limit, 
  101: 			unsigned short udp_sz, int optno, unsigned char *opt, size_t optlen, int set_do, int replace)
  102: { 
  103:   unsigned char *lenp, *datap, *p, *udp_len, *buff = NULL;
  104:   int rdlen = 0, is_sign, is_last;
  105:   unsigned short flags = set_do ? 0x8000 : 0, rcode = 0;
  106: 
  107:   p = find_pseudoheader(header, plen, NULL, &udp_len, &is_sign, &is_last);
  108:   
  109:   if (is_sign)
  110:     return plen;
  111: 
  112:   if (p)
  113:     {
  114:       /* Existing header */
  115:       int i;
  116:       unsigned short code, len;
  117: 
  118:       p = udp_len;
  119:       GETSHORT(udp_sz, p);
  120:       GETSHORT(rcode, p);
  121:       GETSHORT(flags, p);
  122: 
  123:       if (set_do)
  124: 	{
  125: 	  p -= 2;
  126: 	  flags |= 0x8000;
  127: 	  PUTSHORT(flags, p);
  128: 	}
  129: 
  130:       lenp = p;
  131:       GETSHORT(rdlen, p);
  132:       if (!CHECK_LEN(header, p, plen, rdlen))
  133: 	return plen; /* bad packet */
  134:       datap = p;
  135: 
  136:        /* no option to add */
  137:       if (optno == 0)
  138: 	return plen;
  139:       	  
  140:       /* check if option already there */
  141:       for (i = 0; i + 4 < rdlen;)
  142: 	{
  143: 	  GETSHORT(code, p);
  144: 	  GETSHORT(len, p);
  145: 	  
  146: 	  /* malformed option, delete the whole OPT RR and start again. */
  147: 	  if (i + 4 + len > rdlen)
  148: 	    {
  149: 	      rdlen = 0;
  150: 	      is_last = 0;
  151: 	      break;
  152: 	    }
  153: 	  
  154: 	  if (code == optno)
  155: 	    {
  156: 	      if (replace == 0)
  157: 		return plen;
  158: 
  159: 	      /* delete option if we're to replace it. */
  160: 	      p -= 4;
  161: 	      rdlen -= len + 4;
  162: 	      memmove(p, p+len+4, rdlen - i);
  163: 	      PUTSHORT(rdlen, lenp);
  164: 	      lenp -= 2;
  165: 	    }
  166: 	  else
  167: 	    {
  168: 	      p += len;
  169: 	      i += len + 4;
  170: 	    }
  171: 	}
  172: 
  173:       /* If we're going to extend the RR, it has to be the last RR in the packet */
  174:       if (!is_last)
  175: 	{
  176: 	  /* First, take a copy of the options. */
  177: 	  if (rdlen != 0 && (buff = whine_malloc(rdlen)))
  178: 	    memcpy(buff, datap, rdlen);	      
  179: 	  
  180: 	  /* now, delete OPT RR */
  181: 	  plen = rrfilter(header, plen, RRFILTER_EDNS0);
  182: 	  
  183: 	  /* Now, force addition of a new one */
  184: 	  p = NULL;	  
  185: 	}
  186:     }
  187:   
  188:   if (!p)
  189:     {
  190:       /* We are (re)adding the pseudoheader */
  191:       if (!(p = skip_questions(header, plen)) ||
  192: 	  !(p = skip_section(p, 
  193: 			     ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount), 
  194: 			     header, plen)))
  195:       {
  196: 	free(buff);
  197: 	return plen;
  198:       }
  199:       if (p + 11 > limit)
  200:       {
  201:         free(buff);
  202:         return plen; /* Too big */
  203:       }
  204:       *p++ = 0; /* empty name */
  205:       PUTSHORT(T_OPT, p);
  206:       PUTSHORT(udp_sz, p); /* max packet length, 512 if not given in EDNS0 header */
  207:       PUTSHORT(rcode, p);    /* extended RCODE and version */
  208:       PUTSHORT(flags, p); /* DO flag */
  209:       lenp = p;
  210:       PUTSHORT(rdlen, p);    /* RDLEN */
  211:       datap = p;
  212:       /* Copy back any options */
  213:       if (buff)
  214: 	{
  215:           if (p + rdlen > limit)
  216:           {
  217:             free(buff);
  218:             return plen; /* Too big */
  219:           }
  220: 	  memcpy(p, buff, rdlen);
  221: 	  free(buff);
  222: 	  p += rdlen;
  223: 	}
  224:       
  225:       /* Only bump arcount if RR is going to fit */ 
  226:       if (((ssize_t)optlen) <= (limit - (p + 4)))
  227: 	header->arcount = htons(ntohs(header->arcount) + 1);
  228:     }
  229:   
  230:   if (((ssize_t)optlen) > (limit - (p + 4)))
  231:     return plen; /* Too big */
  232:   
  233:   /* Add new option */
  234:   if (optno != 0 && replace != 2)
  235:     {
  236:       if (p + 4 > limit)
  237:        return plen; /* Too big */
  238:       PUTSHORT(optno, p);
  239:       PUTSHORT(optlen, p);
  240:       if (p + optlen > limit)
  241:        return plen; /* Too big */
  242:       memcpy(p, opt, optlen);
  243:       p += optlen;  
  244:       PUTSHORT(p - datap, lenp);
  245:     }
  246:   return p - (unsigned char *)header;
  247: }
  248: 
  249: size_t add_do_bit(struct dns_header *header, size_t plen, unsigned char *limit)
  250: {
  251:   return add_pseudoheader(header, plen, (unsigned char *)limit, PACKETSZ, 0, NULL, 0, 1, 0);
  252: }
  253: 
  254: static unsigned char char64(unsigned char c)
  255: {
  256:   return "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[c & 0x3f];
  257: }
  258: 
  259: static void encoder(unsigned char *in, char *out)
  260: {
  261:   out[0] = char64(in[0]>>2);
  262:   out[1] = char64((in[0]<<4) | (in[1]>>4));
  263:   out[2] = char64((in[1]<<2) | (in[2]>>6));
  264:   out[3] = char64(in[2]);
  265: }
  266: 
  267: /* OPT_ADD_MAC = MAC is added (if available)
  268:    OPT_ADD_MAC + OPT_STRIP_MAC = MAC is replaced, if not available, it is only removed
  269:    OPT_STRIP_MAC = MAC is removed */
  270: static size_t add_dns_client(struct dns_header *header, size_t plen, unsigned char *limit,
  271: 			     union mysockaddr *l3, time_t now, int *cacheablep)
  272: {
  273:   int replace = 0, maclen = 0;
  274:   unsigned char mac[DHCP_CHADDR_MAX];
  275:   char encode[18]; /* handle 6 byte MACs ONLY */
  276: 
  277:   if ((option_bool(OPT_MAC_B64) || option_bool(OPT_MAC_HEX)) && (maclen = find_mac(l3, mac, 1, now)) == 6)
  278:     {
  279:       if (option_bool(OPT_STRIP_MAC))
  280: 	 replace = 1;
  281:        *cacheablep = 0;
  282:     
  283:        if (option_bool(OPT_MAC_HEX))
  284: 	 print_mac(encode, mac, maclen);
  285:        else
  286: 	 {
  287: 	   encoder(mac, encode);
  288: 	   encoder(mac+3, encode+4);
  289: 	   encode[8] = 0;
  290: 	 }
  291:     }
  292:   else if (option_bool(OPT_STRIP_MAC))
  293:     replace = 2;
  294: 
  295:   if (replace != 0 || maclen == 6)
  296:     plen = add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_NOMDEVICEID, (unsigned char *)encode, strlen(encode), 0, replace);
  297: 
  298:   return plen;
  299: }
  300: 
  301: 
  302: /* OPT_ADD_MAC = MAC is added (if available)
  303:    OPT_ADD_MAC + OPT_STRIP_MAC = MAC is replaced, if not available, it is only removed
  304:    OPT_STRIP_MAC = MAC is removed */
  305: static size_t add_mac(struct dns_header *header, size_t plen, unsigned char *limit,
  306: 		      union mysockaddr *l3, time_t now, int *cacheablep)
  307: {
  308:   int maclen = 0, replace = 0;
  309:   unsigned char mac[DHCP_CHADDR_MAX];
  310:     
  311:   if (option_bool(OPT_ADD_MAC) && (maclen = find_mac(l3, mac, 1, now)) != 0)
  312:     {
  313:       *cacheablep = 0;
  314:       if (option_bool(OPT_STRIP_MAC))
  315: 	replace = 1;
  316:     }
  317:   else if (option_bool(OPT_STRIP_MAC))
  318:     replace = 2;
  319:   
  320:   if (replace != 0 || maclen != 0)
  321:     plen = add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_MAC, mac, maclen, 0, replace);
  322: 
  323:   return plen; 
  324: }
  325: 
  326: struct subnet_opt {
  327:   u16 family;
  328:   u8 source_netmask, scope_netmask; 
  329:   u8 addr[IN6ADDRSZ];
  330: };
  331: 
  332: static void *get_addrp(union mysockaddr *addr, const short family) 
  333: {
  334:   if (family == AF_INET6)
  335:     return &addr->in6.sin6_addr;
  336: 
  337:   return &addr->in.sin_addr;
  338: }
  339: 
  340: static size_t calc_subnet_opt(struct subnet_opt *opt, union mysockaddr *source, int *cacheablep)
  341: {
  342:   /* http://tools.ietf.org/html/draft-vandergaast-edns-client-subnet-02 */
  343:   
  344:   int len;
  345:   void *addrp = NULL;
  346:   int sa_family = source->sa.sa_family;
  347:   int cacheable = 0;
  348:   
  349:   opt->source_netmask = 0;
  350:   opt->scope_netmask = 0;
  351:     
  352:   if (source->sa.sa_family == AF_INET6 && daemon->add_subnet6)
  353:     {
  354:       opt->source_netmask = daemon->add_subnet6->mask;
  355:       if (daemon->add_subnet6->addr_used) 
  356: 	{
  357: 	  sa_family = daemon->add_subnet6->addr.sa.sa_family;
  358: 	  addrp = get_addrp(&daemon->add_subnet6->addr, sa_family);
  359: 	  cacheable = 1;
  360: 	} 
  361:       else 
  362: 	addrp = &source->in6.sin6_addr;
  363:     }
  364: 
  365:   if (source->sa.sa_family == AF_INET && daemon->add_subnet4)
  366:     {
  367:       opt->source_netmask = daemon->add_subnet4->mask;
  368:       if (daemon->add_subnet4->addr_used)
  369: 	{
  370: 	  sa_family = daemon->add_subnet4->addr.sa.sa_family;
  371: 	  addrp = get_addrp(&daemon->add_subnet4->addr, sa_family);
  372: 	  cacheable = 1; /* Address is constant */
  373: 	} 
  374: 	else 
  375: 	  addrp = &source->in.sin_addr;
  376:     }
  377:   
  378:   opt->family = htons(sa_family == AF_INET6 ? 2 : 1);
  379:   
  380:   if (addrp && opt->source_netmask != 0)
  381:     {
  382:       len = ((opt->source_netmask - 1) >> 3) + 1;
  383:       memcpy(opt->addr, addrp, len);
  384:       if (opt->source_netmask & 7)
  385: 	opt->addr[len-1] &= 0xff << (8 - (opt->source_netmask & 7));
  386:     }
  387:   else
  388:     {
  389:       cacheable = 1; /* No address ever supplied. */
  390:       len = 0;
  391:     }
  392: 
  393:   if (cacheablep)
  394:     *cacheablep = cacheable;
  395:   
  396:   return len + 4;
  397: }
  398:  
  399: /* OPT_CLIENT_SUBNET = client subnet is added
  400:    OPT_CLIENT_SUBNET + OPT_STRIP_ECS = client subnet is replaced
  401:    OPT_STRIP_ECS = client subnet is removed */
  402: static size_t add_source_addr(struct dns_header *header, size_t plen, unsigned char *limit,
  403: 			      union mysockaddr *source, int *cacheable)
  404: {
  405:   /* http://tools.ietf.org/html/draft-vandergaast-edns-client-subnet-02 */
  406:   
  407:   int replace = 0, len = 0;
  408:   struct subnet_opt opt;
  409:   
  410:   if (option_bool(OPT_CLIENT_SUBNET))
  411:     {
  412:       if (option_bool(OPT_STRIP_ECS))
  413: 	replace = 1;
  414:       len = calc_subnet_opt(&opt, source, cacheable);
  415:     }
  416:   else if (option_bool(OPT_STRIP_ECS))
  417:     replace = 2;
  418:   else
  419:     return plen;
  420: 
  421:   return add_pseudoheader(header, plen, (unsigned char *)limit, PACKETSZ, EDNS0_OPTION_CLIENT_SUBNET, (unsigned char *)&opt, len, 0, replace);
  422: }
  423: 
  424: int check_source(struct dns_header *header, size_t plen, unsigned char *pseudoheader, union mysockaddr *peer)
  425: {
  426:   /* Section 9.2, Check that subnet option in reply matches. */
  427:   
  428:   int len, calc_len;
  429:   struct subnet_opt opt;
  430:   unsigned char *p;
  431:   int code, i, rdlen;
  432:   
  433:   calc_len = calc_subnet_opt(&opt, peer, NULL);
  434:    
  435:   if (!(p = skip_name(pseudoheader, header, plen, 10)))
  436:     return 1;
  437:   
  438:   p += 8; /* skip UDP length and RCODE */
  439:   
  440:   GETSHORT(rdlen, p);
  441:   if (!CHECK_LEN(header, p, plen, rdlen))
  442:     return 1; /* bad packet */
  443:   
  444:   /* check if option there */
  445:    for (i = 0; i + 4 < rdlen; i += len + 4)
  446:      {
  447:        GETSHORT(code, p);
  448:        GETSHORT(len, p);
  449:        if (code == EDNS0_OPTION_CLIENT_SUBNET)
  450: 	 {
  451: 	   /* make sure this doesn't mismatch. */
  452: 	   opt.scope_netmask = p[3];
  453: 	   if (len != calc_len || memcmp(p, &opt, len) != 0)
  454: 	     return 0;
  455: 	 }
  456:        p += len;
  457:      }
  458:    
  459:    return 1;
  460: }
  461: 
  462: /* See https://docs.umbrella.com/umbrella-api/docs/identifying-dns-traffic for
  463:  * detailed information on packet formating.
  464:  */
  465: #define UMBRELLA_VERSION    1
  466: #define UMBRELLA_TYPESZ     2
  467: 
  468: #define UMBRELLA_ASSET      0x0004
  469: #define UMBRELLA_ASSETSZ    sizeof(daemon->umbrella_asset)
  470: #define UMBRELLA_ORG        0x0008
  471: #define UMBRELLA_ORGSZ      sizeof(daemon->umbrella_org)
  472: #define UMBRELLA_IPV4       0x0010
  473: #define UMBRELLA_IPV6       0x0020
  474: #define UMBRELLA_DEVICE     0x0040
  475: #define UMBRELLA_DEVICESZ   sizeof(daemon->umbrella_device)
  476: 
  477: struct umbrella_opt {
  478:   u8 magic[4];
  479:   u8 version;
  480:   u8 flags;
  481:   /* We have 4 possible fields since we'll never send both IPv4 and
  482:    * IPv6, so using the larger of the two to calculate max buffer size.
  483:    * Each field also has a type header.  So the following accounts for
  484:    * the type headers and each field size to get a max buffer size.
  485:    */
  486:   u8 fields[4 * UMBRELLA_TYPESZ + UMBRELLA_ORGSZ + IN6ADDRSZ + UMBRELLA_DEVICESZ + UMBRELLA_ASSETSZ];
  487: };
  488: 
  489: static size_t add_umbrella_opt(struct dns_header *header, size_t plen, unsigned char *limit, union mysockaddr *source, int *cacheable)
  490: {
  491:   *cacheable = 0;
  492: 
  493:   struct umbrella_opt opt = {{"ODNS"}, UMBRELLA_VERSION, 0, {}};
  494:   u8 *u = &opt.fields[0];
  495:   int family = source->sa.sa_family;
  496:   int size = family == AF_INET ? INADDRSZ : IN6ADDRSZ;
  497: 
  498:   if (daemon->umbrella_org)
  499:     {
  500:       PUTSHORT(UMBRELLA_ORG, u);
  501:       PUTLONG(daemon->umbrella_org, u);
  502:     }
  503:   
  504:   PUTSHORT(family == AF_INET ? UMBRELLA_IPV4 : UMBRELLA_IPV6, u);
  505:   memcpy(u, get_addrp(source, family), size);
  506:   u += size;
  507:   
  508:   if (option_bool(OPT_UMBRELLA_DEVID))
  509:     {
  510:       PUTSHORT(UMBRELLA_DEVICE, u);
  511:       memcpy(u, (char *)&daemon->umbrella_device, UMBRELLA_DEVICESZ);
  512:       u += UMBRELLA_DEVICESZ;
  513:     }
  514: 
  515:   if (daemon->umbrella_asset)
  516:     {
  517:       PUTSHORT(UMBRELLA_ASSET, u);
  518:       PUTLONG(daemon->umbrella_asset, u);
  519:     }
  520:   
  521:   return add_pseudoheader(header, plen, (unsigned char *)limit, PACKETSZ, EDNS0_OPTION_UMBRELLA, (unsigned char *)&opt, u - (u8 *)&opt, 0, 1);
  522: }
  523: 
  524: /* Set *check_subnet if we add a client subnet option, which needs to checked 
  525:    in the reply. Set *cacheable to zero if we add an option which the answer
  526:    may depend on. */
  527: size_t add_edns0_config(struct dns_header *header, size_t plen, unsigned char *limit, 
  528: 			union mysockaddr *source, time_t now, int *cacheable)    
  529: {
  530:   *cacheable = 1;
  531:   
  532:   plen  = add_mac(header, plen, limit, source, now, cacheable);
  533:   plen = add_dns_client(header, plen, limit, source, now, cacheable);
  534:   
  535:   if (daemon->dns_client_id)
  536:     plen = add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_NOMCPEID, 
  537: 			    (unsigned char *)daemon->dns_client_id, strlen(daemon->dns_client_id), 0, 1);
  538: 
  539:   if (option_bool(OPT_UMBRELLA))
  540:     plen = add_umbrella_opt(header, plen, limit, source, cacheable);
  541:   
  542:   plen = add_source_addr(header, plen, limit, source, cacheable);
  543:   	  
  544:   return plen;
  545: }

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