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>