Annotation of embedaddon/dnsmasq/src/edns0.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:
17: #include "dnsmasq.h"
18:
19: unsigned char *find_pseudoheader(struct dns_header *header, size_t plen, size_t *len, unsigned char **p, int *is_sign, int *is_last)
20: {
21: /* See if packet has an RFC2671 pseudoheader, and if so return a pointer to it.
22: also return length of pseudoheader in *len and pointer to the UDP size in *p
23: Finally, check to see if a packet is signed. If it is we cannot change a single bit before
24: forwarding. We look for TSIG in the addition section, and TKEY queries (for GSS-TSIG) */
25:
26: int i, arcount = ntohs(header->arcount);
27: unsigned char *ansp = (unsigned char *)(header+1);
28: unsigned short rdlen, type, class;
29: unsigned char *ret = NULL;
30:
31: if (is_sign)
32: {
33: *is_sign = 0;
34:
35: if (OPCODE(header) == QUERY)
36: {
37: for (i = ntohs(header->qdcount); i != 0; i--)
38: {
39: if (!(ansp = skip_name(ansp, header, plen, 4)))
40: return NULL;
41:
42: GETSHORT(type, ansp);
43: GETSHORT(class, ansp);
44:
45: if (class == C_IN && type == T_TKEY)
46: *is_sign = 1;
47: }
48: }
49: }
50: else
51: {
52: if (!(ansp = skip_questions(header, plen)))
53: return NULL;
54: }
55:
56: if (arcount == 0)
57: return NULL;
58:
59: if (!(ansp = skip_section(ansp, ntohs(header->ancount) + ntohs(header->nscount), header, plen)))
60: return NULL;
61:
62: for (i = 0; i < arcount; i++)
63: {
64: unsigned char *save, *start = ansp;
65: if (!(ansp = skip_name(ansp, header, plen, 10)))
66: return NULL;
67:
68: GETSHORT(type, ansp);
69: save = ansp;
70: GETSHORT(class, ansp);
71: ansp += 4; /* TTL */
72: GETSHORT(rdlen, ansp);
73: if (!ADD_RDLEN(header, ansp, plen, rdlen))
74: return NULL;
75: if (type == T_OPT)
76: {
77: if (len)
78: *len = ansp - start;
79:
80: if (p)
81: *p = save;
82:
83: if (is_last)
84: *is_last = (i == arcount-1);
85:
86: ret = start;
87: }
88: else if (is_sign &&
89: i == arcount - 1 &&
90: class == C_ANY &&
91: type == T_TSIG)
92: *is_sign = 1;
93: }
94:
95: return ret;
96: }
97:
98:
99: /* replace == 2 ->delete existing option only. */
100: size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *limit,
101: unsigned short udp_sz, int optno, unsigned char *opt, size_t optlen, int set_do, int replace)
102: {
103: unsigned char *lenp, *datap, *p, *udp_len, *buff = NULL;
104: int rdlen = 0, is_sign, is_last;
105: unsigned short flags = set_do ? 0x8000 : 0, rcode = 0;
106:
107: p = find_pseudoheader(header, plen, NULL, &udp_len, &is_sign, &is_last);
108:
109: if (is_sign)
110: return plen;
111:
112: if (p)
113: {
114: /* Existing header */
115: int i;
116: unsigned short code, len;
117:
118: p = udp_len;
119: GETSHORT(udp_sz, p);
120: GETSHORT(rcode, p);
121: GETSHORT(flags, p);
122:
123: if (set_do)
124: {
125: p -= 2;
126: flags |= 0x8000;
127: PUTSHORT(flags, p);
128: }
129:
130: lenp = p;
131: GETSHORT(rdlen, p);
132: if (!CHECK_LEN(header, p, plen, rdlen))
133: return plen; /* bad packet */
134: datap = p;
135:
136: /* no option to add */
137: if (optno == 0)
138: return plen;
139:
140: /* check if option already there */
141: for (i = 0; i + 4 < rdlen;)
142: {
143: GETSHORT(code, p);
144: GETSHORT(len, p);
145:
146: /* malformed option, delete the whole OPT RR and start again. */
1.1.1.2 misho 147: if (i + 4 + len > rdlen)
1.1 misho 148: {
149: rdlen = 0;
150: is_last = 0;
151: break;
152: }
153:
154: if (code == optno)
155: {
156: if (replace == 0)
157: return plen;
158:
159: /* delete option if we're to replace it. */
160: p -= 4;
161: rdlen -= len + 4;
1.1.1.2 misho 162: memmove(p, p+len+4, rdlen - i);
1.1 misho 163: PUTSHORT(rdlen, lenp);
164: lenp -= 2;
165: }
166: else
167: {
168: p += len;
169: i += len + 4;
170: }
171: }
172:
173: /* If we're going to extend the RR, it has to be the last RR in the packet */
174: if (!is_last)
175: {
176: /* First, take a copy of the options. */
177: if (rdlen != 0 && (buff = whine_malloc(rdlen)))
178: memcpy(buff, datap, rdlen);
179:
180: /* now, delete OPT RR */
1.1.1.3 ! misho 181: plen = rrfilter(header, plen, RRFILTER_EDNS0);
1.1 misho 182:
183: /* Now, force addition of a new one */
184: p = NULL;
185: }
186: }
187:
188: if (!p)
189: {
190: /* We are (re)adding the pseudoheader */
191: if (!(p = skip_questions(header, plen)) ||
192: !(p = skip_section(p,
193: ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount),
194: header, plen)))
1.1.1.2 misho 195: {
196: free(buff);
1.1 misho 197: return plen;
1.1.1.2 misho 198: }
199: if (p + 11 > limit)
200: {
201: free(buff);
202: return plen; /* Too big */
203: }
1.1 misho 204: *p++ = 0; /* empty name */
205: PUTSHORT(T_OPT, p);
206: PUTSHORT(udp_sz, p); /* max packet length, 512 if not given in EDNS0 header */
207: PUTSHORT(rcode, p); /* extended RCODE and version */
208: PUTSHORT(flags, p); /* DO flag */
209: lenp = p;
210: PUTSHORT(rdlen, p); /* RDLEN */
211: datap = p;
212: /* Copy back any options */
213: if (buff)
214: {
1.1.1.2 misho 215: if (p + rdlen > limit)
216: {
217: free(buff);
218: return plen; /* Too big */
219: }
1.1 misho 220: memcpy(p, buff, rdlen);
221: free(buff);
222: p += rdlen;
223: }
1.1.1.2 misho 224:
225: /* Only bump arcount if RR is going to fit */
226: if (((ssize_t)optlen) <= (limit - (p + 4)))
227: header->arcount = htons(ntohs(header->arcount) + 1);
1.1 misho 228: }
229:
230: if (((ssize_t)optlen) > (limit - (p + 4)))
231: return plen; /* Too big */
232:
233: /* Add new option */
234: if (optno != 0 && replace != 2)
235: {
1.1.1.2 misho 236: if (p + 4 > limit)
237: return plen; /* Too big */
1.1 misho 238: PUTSHORT(optno, p);
239: PUTSHORT(optlen, p);
1.1.1.2 misho 240: if (p + optlen > limit)
241: return plen; /* Too big */
1.1 misho 242: memcpy(p, opt, optlen);
243: p += optlen;
244: PUTSHORT(p - datap, lenp);
245: }
246: return p - (unsigned char *)header;
247: }
248:
249: size_t add_do_bit(struct dns_header *header, size_t plen, unsigned char *limit)
250: {
251: return add_pseudoheader(header, plen, (unsigned char *)limit, PACKETSZ, 0, NULL, 0, 1, 0);
252: }
253:
254: static unsigned char char64(unsigned char c)
255: {
256: return "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[c & 0x3f];
257: }
258:
259: static void encoder(unsigned char *in, char *out)
260: {
261: out[0] = char64(in[0]>>2);
262: out[1] = char64((in[0]<<4) | (in[1]>>4));
263: out[2] = char64((in[1]<<2) | (in[2]>>6));
264: out[3] = char64(in[2]);
265: }
266:
1.1.1.3 ! misho 267: /* OPT_ADD_MAC = MAC is added (if available)
! 268: OPT_ADD_MAC + OPT_STRIP_MAC = MAC is replaced, if not available, it is only removed
! 269: OPT_STRIP_MAC = MAC is removed */
1.1.1.2 misho 270: static size_t add_dns_client(struct dns_header *header, size_t plen, unsigned char *limit,
271: union mysockaddr *l3, time_t now, int *cacheablep)
1.1 misho 272: {
1.1.1.3 ! misho 273: int replace = 0, maclen = 0;
1.1 misho 274: unsigned char mac[DHCP_CHADDR_MAX];
1.1.1.3 ! misho 275: char encode[18]; /* handle 6 byte MACs ONLY */
1.1 misho 276:
1.1.1.3 ! misho 277: if ((option_bool(OPT_MAC_B64) || option_bool(OPT_MAC_HEX)) && (maclen = find_mac(l3, mac, 1, now)) == 6)
1.1 misho 278: {
1.1.1.3 ! misho 279: if (option_bool(OPT_STRIP_MAC))
! 280: replace = 1;
! 281: *cacheablep = 0;
! 282:
! 283: if (option_bool(OPT_MAC_HEX))
! 284: print_mac(encode, mac, maclen);
! 285: else
! 286: {
! 287: encoder(mac, encode);
! 288: encoder(mac+3, encode+4);
! 289: encode[8] = 0;
! 290: }
1.1 misho 291: }
1.1.1.3 ! misho 292: else if (option_bool(OPT_STRIP_MAC))
! 293: replace = 2;
! 294:
! 295: if (replace != 0 || maclen == 6)
! 296: plen = add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_NOMDEVICEID, (unsigned char *)encode, strlen(encode), 0, replace);
1.1 misho 297:
1.1.1.3 ! misho 298: return plen;
1.1 misho 299: }
300:
301:
1.1.1.3 ! misho 302: /* OPT_ADD_MAC = MAC is added (if available)
! 303: OPT_ADD_MAC + OPT_STRIP_MAC = MAC is replaced, if not available, it is only removed
! 304: OPT_STRIP_MAC = MAC is removed */
1.1.1.2 misho 305: static size_t add_mac(struct dns_header *header, size_t plen, unsigned char *limit,
306: union mysockaddr *l3, time_t now, int *cacheablep)
1.1 misho 307: {
1.1.1.3 ! misho 308: int maclen = 0, replace = 0;
1.1 misho 309: unsigned char mac[DHCP_CHADDR_MAX];
1.1.1.3 ! misho 310:
! 311: if (option_bool(OPT_ADD_MAC) && (maclen = find_mac(l3, mac, 1, now)) != 0)
1.1.1.2 misho 312: {
313: *cacheablep = 0;
1.1.1.3 ! misho 314: if (option_bool(OPT_STRIP_MAC))
! 315: replace = 1;
1.1.1.2 misho 316: }
1.1.1.3 ! misho 317: else if (option_bool(OPT_STRIP_MAC))
! 318: replace = 2;
1.1.1.2 misho 319:
1.1.1.3 ! misho 320: if (replace != 0 || maclen != 0)
! 321: plen = add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_MAC, mac, maclen, 0, replace);
! 322:
1.1 misho 323: return plen;
324: }
325:
326: struct subnet_opt {
327: u16 family;
1.1.1.2 misho 328: u8 source_netmask, scope_netmask;
1.1 misho 329: u8 addr[IN6ADDRSZ];
330: };
331:
332: static void *get_addrp(union mysockaddr *addr, const short family)
333: {
334: if (family == AF_INET6)
335: return &addr->in6.sin6_addr;
336:
337: return &addr->in.sin_addr;
338: }
339:
1.1.1.2 misho 340: static size_t calc_subnet_opt(struct subnet_opt *opt, union mysockaddr *source, int *cacheablep)
1.1 misho 341: {
342: /* http://tools.ietf.org/html/draft-vandergaast-edns-client-subnet-02 */
343:
344: int len;
1.1.1.2 misho 345: void *addrp = NULL;
1.1 misho 346: int sa_family = source->sa.sa_family;
1.1.1.2 misho 347: int cacheable = 0;
348:
1.1 misho 349: opt->source_netmask = 0;
350: opt->scope_netmask = 0;
1.1.1.2 misho 351:
1.1 misho 352: if (source->sa.sa_family == AF_INET6 && daemon->add_subnet6)
353: {
354: opt->source_netmask = daemon->add_subnet6->mask;
355: if (daemon->add_subnet6->addr_used)
356: {
357: sa_family = daemon->add_subnet6->addr.sa.sa_family;
358: addrp = get_addrp(&daemon->add_subnet6->addr, sa_family);
1.1.1.2 misho 359: cacheable = 1;
1.1 misho 360: }
361: else
362: addrp = &source->in6.sin6_addr;
363: }
364:
365: if (source->sa.sa_family == AF_INET && daemon->add_subnet4)
366: {
367: opt->source_netmask = daemon->add_subnet4->mask;
368: if (daemon->add_subnet4->addr_used)
369: {
370: sa_family = daemon->add_subnet4->addr.sa.sa_family;
371: addrp = get_addrp(&daemon->add_subnet4->addr, sa_family);
1.1.1.2 misho 372: cacheable = 1; /* Address is constant */
1.1 misho 373: }
374: else
375: addrp = &source->in.sin_addr;
376: }
377:
378: opt->family = htons(sa_family == AF_INET6 ? 2 : 1);
379:
1.1.1.2 misho 380: if (addrp && opt->source_netmask != 0)
1.1 misho 381: {
382: len = ((opt->source_netmask - 1) >> 3) + 1;
383: memcpy(opt->addr, addrp, len);
384: if (opt->source_netmask & 7)
385: opt->addr[len-1] &= 0xff << (8 - (opt->source_netmask & 7));
386: }
1.1.1.2 misho 387: else
388: {
389: cacheable = 1; /* No address ever supplied. */
390: len = 0;
391: }
392:
393: if (cacheablep)
394: *cacheablep = cacheable;
1.1 misho 395:
396: return len + 4;
397: }
398:
1.1.1.3 ! misho 399: /* OPT_CLIENT_SUBNET = client subnet is added
! 400: OPT_CLIENT_SUBNET + OPT_STRIP_ECS = client subnet is replaced
! 401: OPT_STRIP_ECS = client subnet is removed */
! 402: static size_t add_source_addr(struct dns_header *header, size_t plen, unsigned char *limit,
! 403: union mysockaddr *source, int *cacheable)
1.1 misho 404: {
405: /* http://tools.ietf.org/html/draft-vandergaast-edns-client-subnet-02 */
406:
1.1.1.3 ! misho 407: int replace = 0, len = 0;
1.1 misho 408: struct subnet_opt opt;
409:
1.1.1.3 ! misho 410: if (option_bool(OPT_CLIENT_SUBNET))
! 411: {
! 412: if (option_bool(OPT_STRIP_ECS))
! 413: replace = 1;
! 414: len = calc_subnet_opt(&opt, source, cacheable);
! 415: }
! 416: else if (option_bool(OPT_STRIP_ECS))
! 417: replace = 2;
! 418: else
! 419: return plen;
! 420:
! 421: return add_pseudoheader(header, plen, (unsigned char *)limit, PACKETSZ, EDNS0_OPTION_CLIENT_SUBNET, (unsigned char *)&opt, len, 0, replace);
1.1 misho 422: }
423:
424: int check_source(struct dns_header *header, size_t plen, unsigned char *pseudoheader, union mysockaddr *peer)
425: {
426: /* Section 9.2, Check that subnet option in reply matches. */
427:
428: int len, calc_len;
429: struct subnet_opt opt;
430: unsigned char *p;
431: int code, i, rdlen;
432:
1.1.1.2 misho 433: calc_len = calc_subnet_opt(&opt, peer, NULL);
1.1 misho 434:
1.1.1.2 misho 435: if (!(p = skip_name(pseudoheader, header, plen, 10)))
436: return 1;
437:
438: p += 8; /* skip UDP length and RCODE */
439:
440: GETSHORT(rdlen, p);
441: if (!CHECK_LEN(header, p, plen, rdlen))
442: return 1; /* bad packet */
443:
444: /* check if option there */
1.1 misho 445: for (i = 0; i + 4 < rdlen; i += len + 4)
446: {
447: GETSHORT(code, p);
448: GETSHORT(len, p);
449: if (code == EDNS0_OPTION_CLIENT_SUBNET)
450: {
451: /* make sure this doesn't mismatch. */
452: opt.scope_netmask = p[3];
453: if (len != calc_len || memcmp(p, &opt, len) != 0)
454: return 0;
455: }
456: p += len;
457: }
458:
459: return 1;
460: }
461:
1.1.1.3 ! misho 462: /* See https://docs.umbrella.com/umbrella-api/docs/identifying-dns-traffic for
! 463: * detailed information on packet formating.
! 464: */
! 465: #define UMBRELLA_VERSION 1
! 466: #define UMBRELLA_TYPESZ 2
! 467:
! 468: #define UMBRELLA_ASSET 0x0004
! 469: #define UMBRELLA_ASSETSZ sizeof(daemon->umbrella_asset)
! 470: #define UMBRELLA_ORG 0x0008
! 471: #define UMBRELLA_ORGSZ sizeof(daemon->umbrella_org)
! 472: #define UMBRELLA_IPV4 0x0010
! 473: #define UMBRELLA_IPV6 0x0020
! 474: #define UMBRELLA_DEVICE 0x0040
! 475: #define UMBRELLA_DEVICESZ sizeof(daemon->umbrella_device)
! 476:
! 477: struct umbrella_opt {
! 478: u8 magic[4];
! 479: u8 version;
! 480: u8 flags;
! 481: /* We have 4 possible fields since we'll never send both IPv4 and
! 482: * IPv6, so using the larger of the two to calculate max buffer size.
! 483: * Each field also has a type header. So the following accounts for
! 484: * the type headers and each field size to get a max buffer size.
! 485: */
! 486: u8 fields[4 * UMBRELLA_TYPESZ + UMBRELLA_ORGSZ + IN6ADDRSZ + UMBRELLA_DEVICESZ + UMBRELLA_ASSETSZ];
! 487: };
! 488:
! 489: static size_t add_umbrella_opt(struct dns_header *header, size_t plen, unsigned char *limit, union mysockaddr *source, int *cacheable)
! 490: {
! 491: *cacheable = 0;
! 492:
! 493: struct umbrella_opt opt = {{"ODNS"}, UMBRELLA_VERSION, 0, {}};
! 494: u8 *u = &opt.fields[0];
! 495: int family = source->sa.sa_family;
! 496: int size = family == AF_INET ? INADDRSZ : IN6ADDRSZ;
! 497:
! 498: if (daemon->umbrella_org)
! 499: {
! 500: PUTSHORT(UMBRELLA_ORG, u);
! 501: PUTLONG(daemon->umbrella_org, u);
! 502: }
! 503:
! 504: PUTSHORT(family == AF_INET ? UMBRELLA_IPV4 : UMBRELLA_IPV6, u);
! 505: memcpy(u, get_addrp(source, family), size);
! 506: u += size;
! 507:
! 508: if (option_bool(OPT_UMBRELLA_DEVID))
! 509: {
! 510: PUTSHORT(UMBRELLA_DEVICE, u);
! 511: memcpy(u, (char *)&daemon->umbrella_device, UMBRELLA_DEVICESZ);
! 512: u += UMBRELLA_DEVICESZ;
! 513: }
! 514:
! 515: if (daemon->umbrella_asset)
! 516: {
! 517: PUTSHORT(UMBRELLA_ASSET, u);
! 518: PUTLONG(daemon->umbrella_asset, u);
! 519: }
! 520:
! 521: return add_pseudoheader(header, plen, (unsigned char *)limit, PACKETSZ, EDNS0_OPTION_UMBRELLA, (unsigned char *)&opt, u - (u8 *)&opt, 0, 1);
! 522: }
! 523:
1.1.1.2 misho 524: /* Set *check_subnet if we add a client subnet option, which needs to checked
525: in the reply. Set *cacheable to zero if we add an option which the answer
526: may depend on. */
1.1 misho 527: size_t add_edns0_config(struct dns_header *header, size_t plen, unsigned char *limit,
1.1.1.3 ! misho 528: union mysockaddr *source, time_t now, int *cacheable)
1.1 misho 529: {
1.1.1.2 misho 530: *cacheable = 1;
531:
1.1.1.3 ! misho 532: plen = add_mac(header, plen, limit, source, now, cacheable);
! 533: plen = add_dns_client(header, plen, limit, source, now, cacheable);
1.1.1.2 misho 534:
1.1 misho 535: if (daemon->dns_client_id)
536: plen = add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_NOMCPEID,
537: (unsigned char *)daemon->dns_client_id, strlen(daemon->dns_client_id), 0, 1);
1.1.1.3 ! misho 538:
! 539: if (option_bool(OPT_UMBRELLA))
! 540: plen = add_umbrella_opt(header, plen, limit, source, cacheable);
1.1 misho 541:
1.1.1.3 ! misho 542: plen = add_source_addr(header, plen, limit, source, cacheable);
! 543:
1.1 misho 544: return plen;
545: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>