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>