File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / dnsmasq / src / rrfilter.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 (9 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: /* Code to safely remove RRs from a DNS answer */ 
   18: 
   19: #include "dnsmasq.h"
   20: 
   21: /* Go through a domain name, find "pointers" and fix them up based on how many bytes
   22:    we've chopped out of the packet, or check they don't point into an elided part.  */
   23: static int check_name(unsigned char **namep, struct dns_header *header, size_t plen, int fixup, unsigned char **rrs, int rr_count)
   24: {
   25:   unsigned char *ansp = *namep;
   26: 
   27:   while(1)
   28:     {
   29:       unsigned int label_type;
   30:       
   31:       if (!CHECK_LEN(header, ansp, plen, 1))
   32: 	return 0;
   33:       
   34:       label_type = (*ansp) & 0xc0;
   35: 
   36:       if (label_type == 0xc0)
   37: 	{
   38: 	  /* pointer for compression. */
   39: 	  unsigned int offset;
   40: 	  int i;
   41: 	  unsigned char *p;
   42: 	  
   43: 	  if (!CHECK_LEN(header, ansp, plen, 2))
   44: 	    return 0;
   45: 
   46: 	  offset = ((*ansp++) & 0x3f) << 8;
   47: 	  offset |= *ansp++;
   48: 
   49: 	  p = offset + (unsigned char *)header;
   50: 	  
   51: 	  for (i = 0; i < rr_count; i++)
   52: 	    if (p < rrs[i])
   53: 	      break;
   54: 	    else
   55: 	      if (i & 1)
   56: 		offset -= rrs[i] - rrs[i-1];
   57: 
   58: 	  /* does the pointer end up in an elided RR? */
   59: 	  if (i & 1)
   60: 	    return 0;
   61: 
   62: 	  /* No, scale the pointer */
   63: 	  if (fixup)
   64: 	    {
   65: 	      ansp -= 2;
   66: 	      *ansp++ = (offset >> 8) | 0xc0;
   67: 	      *ansp++ = offset & 0xff;
   68: 	    }
   69: 	  break;
   70: 	}
   71:       else if (label_type == 0x80)
   72: 	return 0; /* reserved */
   73:       else if (label_type == 0x40)
   74: 	{
   75: 	  /* Extended label type */
   76: 	  unsigned int count;
   77: 	  
   78: 	  if (!CHECK_LEN(header, ansp, plen, 2))
   79: 	    return 0;
   80: 	  
   81: 	  if (((*ansp++) & 0x3f) != 1)
   82: 	    return 0; /* we only understand bitstrings */
   83: 	  
   84: 	  count = *(ansp++); /* Bits in bitstring */
   85: 	  
   86: 	  if (count == 0) /* count == 0 means 256 bits */
   87: 	    ansp += 32;
   88: 	  else
   89: 	    ansp += ((count-1)>>3)+1;
   90: 	}
   91:       else
   92: 	{ /* label type == 0 Bottom six bits is length */
   93: 	  unsigned int len = (*ansp++) & 0x3f;
   94: 	  
   95: 	  if (!ADD_RDLEN(header, ansp, plen, len))
   96: 	    return 0;
   97: 
   98: 	  if (len == 0)
   99: 	    break; /* zero length label marks the end. */
  100: 	}
  101:     }
  102: 
  103:   *namep = ansp;
  104: 
  105:   return 1;
  106: }
  107: 
  108: /* Go through RRs and check or fixup the domain names contained within */
  109: static int check_rrs(unsigned char *p, struct dns_header *header, size_t plen, int fixup, unsigned char **rrs, int rr_count)
  110: {
  111:   int i, j, type, class, rdlen;
  112:   unsigned char *pp;
  113:   
  114:   for (i = 0; i < ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount); i++)
  115:     {
  116:       pp = p;
  117: 
  118:       if (!(p = skip_name(p, header, plen, 10)))
  119: 	return 0;
  120:       
  121:       GETSHORT(type, p); 
  122:       GETSHORT(class, p);
  123:       p += 4; /* TTL */
  124:       GETSHORT(rdlen, p);
  125: 
  126:       /* If this RR is to be elided, don't fix up its contents */
  127:       for (j = 0; j < rr_count; j += 2)
  128: 	if (rrs[j] == pp)
  129: 	  break;
  130: 
  131:       if (j >= rr_count)
  132: 	{
  133: 	  /* fixup name of RR */
  134: 	  if (!check_name(&pp, header, plen, fixup, rrs, rr_count))
  135: 	    return 0;
  136: 	  
  137: 	  if (class == C_IN)
  138: 	    {
  139: 	      u16 *d;
  140:  
  141: 	      for (pp = p, d = rrfilter_desc(type); *d != (u16)-1; d++)
  142: 		{
  143: 		  if (*d != 0)
  144: 		    pp += *d;
  145: 		  else if (!check_name(&pp, header, plen, fixup, rrs, rr_count))
  146: 		    return 0;
  147: 		}
  148: 	    }
  149: 	}
  150:       
  151:       if (!ADD_RDLEN(header, p, plen, rdlen))
  152: 	return 0;
  153:     }
  154:   
  155:   return 1;
  156: }
  157: 	
  158: 
  159: /* mode may be remove EDNS0 or DNSSEC RRs or remove A or AAAA from answer section. */
  160: size_t rrfilter(struct dns_header *header, size_t plen, int mode)
  161: {
  162:   static unsigned char **rrs = NULL;
  163:   static int rr_sz = 0;
  164: 
  165:   unsigned char *p = (unsigned char *)(header+1);
  166:   int i, rdlen, qtype, qclass, rr_found, chop_an, chop_ns, chop_ar;
  167: 
  168:   if (ntohs(header->qdcount) != 1 ||
  169:       !(p = skip_name(p, header, plen, 4)))
  170:     return plen;
  171:   
  172:   GETSHORT(qtype, p);
  173:   GETSHORT(qclass, p);
  174: 
  175:   /* First pass, find pointers to start and end of all the records we wish to elide:
  176:      records added for DNSSEC, unless explicitly queried for */
  177:   for (rr_found = 0, chop_ns = 0, chop_an = 0, chop_ar = 0, i = 0; 
  178:        i < ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount);
  179:        i++)
  180:     {
  181:       unsigned char *pstart = p;
  182:       int type, class;
  183: 
  184:       if (!(p = skip_name(p, header, plen, 10)))
  185: 	return plen;
  186:       
  187:       GETSHORT(type, p); 
  188:       GETSHORT(class, p);
  189:       p += 4; /* TTL */
  190:       GETSHORT(rdlen, p);
  191:         
  192:       if (!ADD_RDLEN(header, p, plen, rdlen))
  193: 	return plen;
  194: 
  195:       if (mode == RRFILTER_EDNS0) /* EDNS */
  196: 	{
  197: 	  /* EDNS mode, remove T_OPT from additional section only */
  198: 	  if (i < (ntohs(header->nscount) + ntohs(header->ancount)) || type != T_OPT)
  199: 	    continue;
  200: 	}
  201:       else if (mode == RRFILTER_DNSSEC)
  202: 	{
  203: 	  if (type != T_NSEC && type != T_NSEC3 && type != T_RRSIG)
  204: 	    /* DNSSEC mode, remove SIGs and NSECs from all three sections. */
  205: 	    continue;
  206: 
  207: 	  /* Don't remove the answer. */
  208: 	  if (i < ntohs(header->ancount) && type == qtype && class == qclass)
  209: 	    continue;
  210: 	}
  211:       else
  212: 	{
  213: 	  /* Only looking at answer section now. */
  214: 	  if (i >= ntohs(header->ancount))
  215: 	    break;
  216: 
  217: 	  if (class != C_IN)
  218: 	    continue;
  219: 	  
  220: 	  if (mode == RRFILTER_A && type != T_A)
  221: 	    continue;
  222: 
  223: 	  if (mode == RRFILTER_AAAA && type != T_AAAA)
  224: 	    continue;
  225: 	}
  226:       
  227:       if (!expand_workspace(&rrs, &rr_sz, rr_found + 1))
  228: 	return plen; 
  229:       
  230:       rrs[rr_found++] = pstart;
  231:       rrs[rr_found++] = p;
  232:       
  233:       if (i < ntohs(header->ancount))
  234: 	chop_an++;
  235:       else if (i < (ntohs(header->nscount) + ntohs(header->ancount)))
  236: 	chop_ns++;
  237:       else
  238: 	chop_ar++;
  239:     }
  240:   
  241:   /* Nothing to do. */
  242:   if (rr_found == 0)
  243:     return plen;
  244: 
  245:   /* Second pass, look for pointers in names in the records we're keeping and make sure they don't
  246:      point to records we're going to elide. This is theoretically possible, but unlikely. If
  247:      it happens, we give up and leave the answer unchanged. */
  248:   p = (unsigned char *)(header+1);
  249:   
  250:   /* question first */
  251:   if (!check_name(&p, header, plen, 0, rrs, rr_found))
  252:     return plen;
  253:   p += 4; /* qclass, qtype */
  254:   
  255:   /* Now answers and NS */
  256:   if (!check_rrs(p, header, plen, 0, rrs, rr_found))
  257:     return plen;
  258:   
  259:   /* Third pass, actually fix up pointers in the records */
  260:   p = (unsigned char *)(header+1);
  261:   
  262:   check_name(&p, header, plen, 1, rrs, rr_found);
  263:   p += 4; /* qclass, qtype */
  264:   
  265:   check_rrs(p, header, plen, 1, rrs, rr_found);
  266: 
  267:   /* Fourth pass, elide records */
  268:   for (p = rrs[0], i = 1; i < rr_found; i += 2)
  269:     {
  270:       unsigned char *start = rrs[i];
  271:       unsigned char *end = (i != rr_found - 1) ? rrs[i+1] : ((unsigned char *)header) + plen;
  272:       
  273:       memmove(p, start, end-start);
  274:       p += end-start;
  275:     }
  276:      
  277:   plen = p - (unsigned char *)header;
  278:   header->ancount = htons(ntohs(header->ancount) - chop_an);
  279:   header->nscount = htons(ntohs(header->nscount) - chop_ns);
  280:   header->arcount = htons(ntohs(header->arcount) - chop_ar);
  281: 
  282:   return plen;
  283: }
  284: 
  285: /* This is used in the DNSSEC code too, hence it's exported */
  286: u16 *rrfilter_desc(int type)
  287: {
  288:   /* List of RRtypes which include domains in the data.
  289:      0 -> domain
  290:      integer -> no. of plain bytes
  291:      -1 -> end
  292: 
  293:      zero is not a valid RRtype, so the final entry is returned for
  294:      anything which needs no mangling.
  295:   */
  296:   
  297:   static u16 rr_desc[] = 
  298:     { 
  299:       T_NS, 0, -1, 
  300:       T_MD, 0, -1,
  301:       T_MF, 0, -1,
  302:       T_CNAME, 0, -1,
  303:       T_SOA, 0, 0, -1,
  304:       T_MB, 0, -1,
  305:       T_MG, 0, -1,
  306:       T_MR, 0, -1,
  307:       T_PTR, 0, -1,
  308:       T_MINFO, 0, 0, -1,
  309:       T_MX, 2, 0, -1,
  310:       T_RP, 0, 0, -1,
  311:       T_AFSDB, 2, 0, -1,
  312:       T_RT, 2, 0, -1,
  313:       T_SIG, 18, 0, -1,
  314:       T_PX, 2, 0, 0, -1,
  315:       T_NXT, 0, -1,
  316:       T_KX, 2, 0, -1,
  317:       T_SRV, 6, 0, -1,
  318:       T_DNAME, 0, -1,
  319:       0, -1 /* wildcard/catchall */
  320:     }; 
  321:   
  322:   u16 *p = rr_desc;
  323:   
  324:   while (*p != type && *p != 0)
  325:     while (*p++ != (u16)-1);
  326: 
  327:   return p+1;
  328: }
  329: 
  330: int expand_workspace(unsigned char ***wkspc, int *szp, int new)
  331: {
  332:   unsigned char **p;
  333:   int old = *szp;
  334: 
  335:   if (old >= new+1)
  336:     return 1;
  337: 
  338:   if (new >= 100)
  339:     return 0;
  340: 
  341:   new += 5;
  342: 
  343:   if (!(p = whine_realloc(*wkspc, new * sizeof(unsigned char *))))
  344:     return 0;
  345: 
  346:   memset(p+old, 0, new-old);
  347:   
  348:   *wkspc = p;
  349:   *szp = new;
  350: 
  351:   return 1;
  352: }

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