Annotation of embedaddon/dnsmasq/src/edns0.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: #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. */
! 147: if (i + len > rdlen)
! 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;
! 162: memcpy(p, p+len+4, rdlen - i);
! 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 */
! 181: plen = rrfilter(header, plen, 0);
! 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)))
! 195: return plen;
! 196: *p++ = 0; /* empty name */
! 197: PUTSHORT(T_OPT, p);
! 198: PUTSHORT(udp_sz, p); /* max packet length, 512 if not given in EDNS0 header */
! 199: PUTSHORT(rcode, p); /* extended RCODE and version */
! 200: PUTSHORT(flags, p); /* DO flag */
! 201: lenp = p;
! 202: PUTSHORT(rdlen, p); /* RDLEN */
! 203: datap = p;
! 204: /* Copy back any options */
! 205: if (buff)
! 206: {
! 207: memcpy(p, buff, rdlen);
! 208: free(buff);
! 209: p += rdlen;
! 210: }
! 211: header->arcount = htons(ntohs(header->arcount) + 1);
! 212: }
! 213:
! 214: if (((ssize_t)optlen) > (limit - (p + 4)))
! 215: return plen; /* Too big */
! 216:
! 217: /* Add new option */
! 218: if (optno != 0 && replace != 2)
! 219: {
! 220: PUTSHORT(optno, p);
! 221: PUTSHORT(optlen, p);
! 222: memcpy(p, opt, optlen);
! 223: p += optlen;
! 224: PUTSHORT(p - datap, lenp);
! 225: }
! 226: return p - (unsigned char *)header;
! 227: }
! 228:
! 229: size_t add_do_bit(struct dns_header *header, size_t plen, unsigned char *limit)
! 230: {
! 231: return add_pseudoheader(header, plen, (unsigned char *)limit, PACKETSZ, 0, NULL, 0, 1, 0);
! 232: }
! 233:
! 234: static unsigned char char64(unsigned char c)
! 235: {
! 236: return "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[c & 0x3f];
! 237: }
! 238:
! 239: static void encoder(unsigned char *in, char *out)
! 240: {
! 241: out[0] = char64(in[0]>>2);
! 242: out[1] = char64((in[0]<<4) | (in[1]>>4));
! 243: out[2] = char64((in[1]<<2) | (in[2]>>6));
! 244: out[3] = char64(in[2]);
! 245: }
! 246:
! 247: static size_t add_dns_client(struct dns_header *header, size_t plen, unsigned char *limit, union mysockaddr *l3, time_t now)
! 248: {
! 249: int maclen, replace = 2; /* can't get mac address, just delete any incoming. */
! 250: unsigned char mac[DHCP_CHADDR_MAX];
! 251: char encode[18]; /* handle 6 byte MACs */
! 252:
! 253: if ((maclen = find_mac(l3, mac, 1, now)) == 6)
! 254: {
! 255: replace = 1;
! 256:
! 257: if (option_bool(OPT_MAC_HEX))
! 258: print_mac(encode, mac, maclen);
! 259: else
! 260: {
! 261: encoder(mac, encode);
! 262: encoder(mac+3, encode+4);
! 263: encode[8] = 0;
! 264: }
! 265: }
! 266:
! 267: return add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_NOMDEVICEID, (unsigned char *)encode, strlen(encode), 0, replace);
! 268: }
! 269:
! 270:
! 271: static size_t add_mac(struct dns_header *header, size_t plen, unsigned char *limit, union mysockaddr *l3, time_t now)
! 272: {
! 273: int maclen;
! 274: unsigned char mac[DHCP_CHADDR_MAX];
! 275:
! 276: if ((maclen = find_mac(l3, mac, 1, now)) != 0)
! 277: plen = add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_MAC, mac, maclen, 0, 0);
! 278:
! 279: return plen;
! 280: }
! 281:
! 282: struct subnet_opt {
! 283: u16 family;
! 284: u8 source_netmask, scope_netmask;
! 285: #ifdef HAVE_IPV6
! 286: u8 addr[IN6ADDRSZ];
! 287: #else
! 288: u8 addr[INADDRSZ];
! 289: #endif
! 290: };
! 291:
! 292: static void *get_addrp(union mysockaddr *addr, const short family)
! 293: {
! 294: #ifdef HAVE_IPV6
! 295: if (family == AF_INET6)
! 296: return &addr->in6.sin6_addr;
! 297: #endif
! 298:
! 299: return &addr->in.sin_addr;
! 300: }
! 301:
! 302: static size_t calc_subnet_opt(struct subnet_opt *opt, union mysockaddr *source)
! 303: {
! 304: /* http://tools.ietf.org/html/draft-vandergaast-edns-client-subnet-02 */
! 305:
! 306: int len;
! 307: void *addrp;
! 308: int sa_family = source->sa.sa_family;
! 309:
! 310: opt->source_netmask = 0;
! 311: opt->scope_netmask = 0;
! 312:
! 313: #ifdef HAVE_IPV6
! 314: if (source->sa.sa_family == AF_INET6 && daemon->add_subnet6)
! 315: {
! 316: opt->source_netmask = daemon->add_subnet6->mask;
! 317: if (daemon->add_subnet6->addr_used)
! 318: {
! 319: sa_family = daemon->add_subnet6->addr.sa.sa_family;
! 320: addrp = get_addrp(&daemon->add_subnet6->addr, sa_family);
! 321: }
! 322: else
! 323: addrp = &source->in6.sin6_addr;
! 324: }
! 325: #endif
! 326:
! 327: if (source->sa.sa_family == AF_INET && daemon->add_subnet4)
! 328: {
! 329: opt->source_netmask = daemon->add_subnet4->mask;
! 330: if (daemon->add_subnet4->addr_used)
! 331: {
! 332: sa_family = daemon->add_subnet4->addr.sa.sa_family;
! 333: addrp = get_addrp(&daemon->add_subnet4->addr, sa_family);
! 334: }
! 335: else
! 336: addrp = &source->in.sin_addr;
! 337: }
! 338:
! 339: #ifdef HAVE_IPV6
! 340: opt->family = htons(sa_family == AF_INET6 ? 2 : 1);
! 341: #else
! 342: opt->family = htons(1);
! 343: #endif
! 344:
! 345: len = 0;
! 346:
! 347: if (opt->source_netmask != 0)
! 348: {
! 349: len = ((opt->source_netmask - 1) >> 3) + 1;
! 350: memcpy(opt->addr, addrp, len);
! 351: if (opt->source_netmask & 7)
! 352: opt->addr[len-1] &= 0xff << (8 - (opt->source_netmask & 7));
! 353: }
! 354:
! 355: return len + 4;
! 356: }
! 357:
! 358: static size_t add_source_addr(struct dns_header *header, size_t plen, unsigned char *limit, union mysockaddr *source)
! 359: {
! 360: /* http://tools.ietf.org/html/draft-vandergaast-edns-client-subnet-02 */
! 361:
! 362: int len;
! 363: struct subnet_opt opt;
! 364:
! 365: len = calc_subnet_opt(&opt, source);
! 366: return add_pseudoheader(header, plen, (unsigned char *)limit, PACKETSZ, EDNS0_OPTION_CLIENT_SUBNET, (unsigned char *)&opt, len, 0, 0);
! 367: }
! 368:
! 369: int check_source(struct dns_header *header, size_t plen, unsigned char *pseudoheader, union mysockaddr *peer)
! 370: {
! 371: /* Section 9.2, Check that subnet option in reply matches. */
! 372:
! 373: int len, calc_len;
! 374: struct subnet_opt opt;
! 375: unsigned char *p;
! 376: int code, i, rdlen;
! 377:
! 378: calc_len = calc_subnet_opt(&opt, peer);
! 379:
! 380: if (!(p = skip_name(pseudoheader, header, plen, 10)))
! 381: return 1;
! 382:
! 383: p += 8; /* skip UDP length and RCODE */
! 384:
! 385: GETSHORT(rdlen, p);
! 386: if (!CHECK_LEN(header, p, plen, rdlen))
! 387: return 1; /* bad packet */
! 388:
! 389: /* check if option there */
! 390: for (i = 0; i + 4 < rdlen; i += len + 4)
! 391: {
! 392: GETSHORT(code, p);
! 393: GETSHORT(len, p);
! 394: if (code == EDNS0_OPTION_CLIENT_SUBNET)
! 395: {
! 396: /* make sure this doesn't mismatch. */
! 397: opt.scope_netmask = p[3];
! 398: if (len != calc_len || memcmp(p, &opt, len) != 0)
! 399: return 0;
! 400: }
! 401: p += len;
! 402: }
! 403:
! 404: return 1;
! 405: }
! 406:
! 407: size_t add_edns0_config(struct dns_header *header, size_t plen, unsigned char *limit,
! 408: union mysockaddr *source, time_t now, int *check_subnet)
! 409: {
! 410: *check_subnet = 0;
! 411:
! 412: if (option_bool(OPT_ADD_MAC))
! 413: plen = add_mac(header, plen, limit, source, now);
! 414:
! 415: if (option_bool(OPT_MAC_B64) || option_bool(OPT_MAC_HEX))
! 416: plen = add_dns_client(header, plen, limit, source, now);
! 417:
! 418: if (daemon->dns_client_id)
! 419: plen = add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_NOMCPEID,
! 420: (unsigned char *)daemon->dns_client_id, strlen(daemon->dns_client_id), 0, 1);
! 421:
! 422: if (option_bool(OPT_CLIENT_SUBNET))
! 423: {
! 424: plen = add_source_addr(header, plen, limit, source);
! 425: *check_subnet = 1;
! 426: }
! 427:
! 428: return plen;
! 429: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>