Annotation of embedaddon/dnsmasq/src/rrfilter.c, revision 1.1.1.3

1.1.1.3 ! misho       1: /* dnsmasq is Copyright (c) 2000-2022 Simon Kelley
1.1       misho       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: 
1.1.1.2   misho      17: /* Code to safely remove RRs from a DNS answer */ 
1.1       misho      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: 
1.1.1.3 ! misho     159: /* mode may be remove EDNS0 or DNSSEC RRs or remove A or AAAA from answer section. */
1.1       misho     160: size_t rrfilter(struct dns_header *header, size_t plen, int mode)
                    161: {
1.1.1.3 ! misho     162:   static unsigned char **rrs = NULL;
1.1       misho     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:
1.1.1.2   misho     176:      records added for DNSSEC, unless explicitly queried for */
1.1       misho     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: 
1.1.1.3 ! misho     195:       if (mode == RRFILTER_EDNS0) /* EDNS */
1.1       misho     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:        }
1.1.1.3 ! misho     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:        }
1.1       misho     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:   
1.1.1.2   misho     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 */
1.1       misho     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
1.1.1.2   misho     290:      integer -> no. of plain bytes
1.1       misho     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;
1.1.1.3 ! misho     342: 
        !           343:   if (!(p = whine_realloc(*wkspc, new * sizeof(unsigned char *))))
        !           344:     return 0;
        !           345: 
        !           346:   memset(p+old, 0, new-old);
1.1       misho     347:   
                    348:   *wkspc = p;
                    349:   *szp = new;
                    350: 
                    351:   return 1;
                    352: }

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