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

1.1     ! misho       1: /* dnsmasq is Copyright (c) 2000-2016 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 an 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 is 0 to remove EDNS0, 1 to filter DNSSEC RRs */
        !           160: size_t rrfilter(struct dns_header *header, size_t plen, int mode)
        !           161: {
        !           162:   static unsigned char **rrs;
        !           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 explicity 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:       /* Don't remove the answer. */
        !           196:       if (i < ntohs(header->ancount) && type == qtype && class == qclass)
        !           197:        continue;
        !           198:       
        !           199:       if (mode == 0) /* EDNS */
        !           200:        {
        !           201:          /* EDNS mode, remove T_OPT from additional section only */
        !           202:          if (i < (ntohs(header->nscount) + ntohs(header->ancount)) || type != T_OPT)
        !           203:            continue;
        !           204:        }
        !           205:       else if (type != T_NSEC && type != T_NSEC3 && type != T_RRSIG)
        !           206:        /* DNSSEC mode, remove SIGs and NSECs from all three sections. */
        !           207:        continue;
        !           208:       
        !           209:       
        !           210:       if (!expand_workspace(&rrs, &rr_sz, rr_found + 1))
        !           211:        return plen; 
        !           212:       
        !           213:       rrs[rr_found++] = pstart;
        !           214:       rrs[rr_found++] = p;
        !           215:       
        !           216:       if (i < ntohs(header->ancount))
        !           217:        chop_an++;
        !           218:       else if (i < (ntohs(header->nscount) + ntohs(header->ancount)))
        !           219:        chop_ns++;
        !           220:       else
        !           221:        chop_ar++;
        !           222:     }
        !           223:   
        !           224:   /* Nothing to do. */
        !           225:   if (rr_found == 0)
        !           226:     return plen;
        !           227: 
        !           228:   /* Second pass, look for pointers in names in the records we're keeping and make sure they don't
        !           229:      point to records we're going to elide. This is theoretically possible, but unlikely. If
        !           230:      it happens, we give up and leave the answer unchanged. */
        !           231:   p = (unsigned char *)(header+1);
        !           232:   
        !           233:   /* question first */
        !           234:   if (!check_name(&p, header, plen, 0, rrs, rr_found))
        !           235:     return plen;
        !           236:   p += 4; /* qclass, qtype */
        !           237:   
        !           238:   /* Now answers and NS */
        !           239:   if (!check_rrs(p, header, plen, 0, rrs, rr_found))
        !           240:     return plen;
        !           241:   
        !           242:   /* Third pass, elide records */
        !           243:   for (p = rrs[0], i = 1; i < rr_found; i += 2)
        !           244:     {
        !           245:       unsigned char *start = rrs[i];
        !           246:       unsigned char *end = (i != rr_found - 1) ? rrs[i+1] : ((unsigned char *)header) + plen;
        !           247:       
        !           248:       memmove(p, start, end-start);
        !           249:       p += end-start;
        !           250:     }
        !           251:      
        !           252:   plen = p - (unsigned char *)header;
        !           253:   header->ancount = htons(ntohs(header->ancount) - chop_an);
        !           254:   header->nscount = htons(ntohs(header->nscount) - chop_ns);
        !           255:   header->arcount = htons(ntohs(header->arcount) - chop_ar);
        !           256: 
        !           257:   /* Fourth pass, fix up pointers in the remaining records */
        !           258:   p = (unsigned char *)(header+1);
        !           259:   
        !           260:   check_name(&p, header, plen, 1, rrs, rr_found);
        !           261:   p += 4; /* qclass, qtype */
        !           262:   
        !           263:   check_rrs(p, header, plen, 1, rrs, rr_found);
        !           264:   
        !           265:   return plen;
        !           266: }
        !           267: 
        !           268: /* This is used in the DNSSEC code too, hence it's exported */
        !           269: u16 *rrfilter_desc(int type)
        !           270: {
        !           271:   /* List of RRtypes which include domains in the data.
        !           272:      0 -> domain
        !           273:      integer -> no of plain bytes
        !           274:      -1 -> end
        !           275: 
        !           276:      zero is not a valid RRtype, so the final entry is returned for
        !           277:      anything which needs no mangling.
        !           278:   */
        !           279:   
        !           280:   static u16 rr_desc[] = 
        !           281:     { 
        !           282:       T_NS, 0, -1, 
        !           283:       T_MD, 0, -1,
        !           284:       T_MF, 0, -1,
        !           285:       T_CNAME, 0, -1,
        !           286:       T_SOA, 0, 0, -1,
        !           287:       T_MB, 0, -1,
        !           288:       T_MG, 0, -1,
        !           289:       T_MR, 0, -1,
        !           290:       T_PTR, 0, -1,
        !           291:       T_MINFO, 0, 0, -1,
        !           292:       T_MX, 2, 0, -1,
        !           293:       T_RP, 0, 0, -1,
        !           294:       T_AFSDB, 2, 0, -1,
        !           295:       T_RT, 2, 0, -1,
        !           296:       T_SIG, 18, 0, -1,
        !           297:       T_PX, 2, 0, 0, -1,
        !           298:       T_NXT, 0, -1,
        !           299:       T_KX, 2, 0, -1,
        !           300:       T_SRV, 6, 0, -1,
        !           301:       T_DNAME, 0, -1,
        !           302:       0, -1 /* wildcard/catchall */
        !           303:     }; 
        !           304:   
        !           305:   u16 *p = rr_desc;
        !           306:   
        !           307:   while (*p != type && *p != 0)
        !           308:     while (*p++ != (u16)-1);
        !           309: 
        !           310:   return p+1;
        !           311: }
        !           312: 
        !           313: int expand_workspace(unsigned char ***wkspc, int *szp, int new)
        !           314: {
        !           315:   unsigned char **p;
        !           316:   int old = *szp;
        !           317: 
        !           318:   if (old >= new+1)
        !           319:     return 1;
        !           320: 
        !           321:   if (new >= 100)
        !           322:     return 0;
        !           323: 
        !           324:   new += 5;
        !           325:   
        !           326:   if (!(p = whine_malloc(new * sizeof(unsigned char *))))
        !           327:     return 0;  
        !           328:   
        !           329:   if (old != 0 && *wkspc)
        !           330:     {
        !           331:       memcpy(p, *wkspc, old * sizeof(unsigned char *));
        !           332:       free(*wkspc);
        !           333:     }
        !           334:   
        !           335:   *wkspc = p;
        !           336:   *szp = new;
        !           337: 
        !           338:   return 1;
        !           339: }

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