rpz.c revision 224090
1224090Sdougb/* 2224090Sdougb * Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") 3224090Sdougb * 4224090Sdougb * Permission to use, copy, modify, and/or distribute this software for any 5224090Sdougb * purpose with or without fee is hereby granted, provided that the above 6224090Sdougb * copyright notice and this permission notice appear in all copies. 7224090Sdougb * 8224090Sdougb * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 9224090Sdougb * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10224090Sdougb * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 11224090Sdougb * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12224090Sdougb * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13224090Sdougb * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14224090Sdougb * PERFORMANCE OF THIS SOFTWARE. 15224090Sdougb */ 16224090Sdougb 17224090Sdougb/* $Id: rpz.c,v 1.7 2011-01-17 04:27:23 marka Exp $ */ 18224090Sdougb 19224090Sdougb/*! \file */ 20224090Sdougb 21224090Sdougb#include <config.h> 22224090Sdougb 23224090Sdougb#include <isc/buffer.h> 24224090Sdougb#include <isc/mem.h> 25224090Sdougb#include <isc/net.h> 26224090Sdougb#include <isc/netaddr.h> 27224090Sdougb#include <isc/print.h> 28224090Sdougb#include <isc/stdlib.h> 29224090Sdougb#include <isc/string.h> 30224090Sdougb#include <isc/util.h> 31224090Sdougb 32224090Sdougb#include <dns/db.h> 33224090Sdougb#include <dns/fixedname.h> 34224090Sdougb#include <dns/log.h> 35224090Sdougb#include <dns/rdata.h> 36224090Sdougb#include <dns/rdataset.h> 37224090Sdougb#include <dns/rdatastruct.h> 38224090Sdougb#include <dns/result.h> 39224090Sdougb#include <dns/rpz.h> 40224090Sdougb#include <dns/view.h> 41224090Sdougb 42224090Sdougb 43224090Sdougb/* 44224090Sdougb * Parallel radix trees for databases of response policy IP addresses 45224090Sdougb * 46224090Sdougb * The radix or Patricia trees are somewhat specialized to handle response 47224090Sdougb * policy addresses by representing the two test of IP IP addresses and name 48224090Sdougb * server IP addresses in a single tree. 49224090Sdougb * 50224090Sdougb * Each leaf indicates that an IP address is listed in the IP address or the 51224090Sdougb * name server IP address policy sub-zone (or both) of the corresponding 52224090Sdougb * response response zone. The policy data such as a CNAME or an A record 53224090Sdougb * is kept in the policy zone. After an IP address has been found in a radix 54224090Sdougb * tree, the node in the policy zone's database is found by converting 55224090Sdougb * the IP address to a domain name in a canonical form. 56224090Sdougb * 57224090Sdougb * The response policy zone canonical form of IPv6 addresses is one of: 58224090Sdougb * prefix.W.W.W.W.W.W.W.W 59224090Sdougb * prefix.WORDS.zz 60224090Sdougb * prefix.WORDS.zz.WORDS 61224090Sdougb * prefix.zz.WORDS 62224090Sdougb * where 63224090Sdougb * prefix is the prefix length of the IPv6 address between 1 and 128 64224090Sdougb * W is a number between 0 and 65535 65224090Sdougb * WORDS is one or more numbers W separated with "." 66224090Sdougb * zz corresponds to :: in the standard IPv6 text representation 67224090Sdougb * 68224090Sdougb * The canonical form of IPv4 addresses is: 69224090Sdougb * prefix.B.B.B.B 70224090Sdougb * where 71224090Sdougb * prefix is the prefix length of the address between 1 and 32 72224090Sdougb * B is a number between 0 and 255 73224090Sdougb * 74224090Sdougb * IPv4 addresses are distinguished from IPv6 addresses by having 75224090Sdougb * 5 labels all of which are numbers, and a prefix between 1 and 32. 76224090Sdougb */ 77224090Sdougb 78224090Sdougb 79224090Sdougb/* 80224090Sdougb * Use a private definition of IPv6 addresses because s6_addr32 is not 81224090Sdougb * always defined and our IPv6 addresses are in non-standard byte order 82224090Sdougb */ 83224090Sdougbtypedef isc_uint32_t dns_rpz_cidr_word_t; 84224090Sdougb#define DNS_RPZ_CIDR_WORD_BITS ((int)sizeof(dns_rpz_cidr_word_t)*8) 85224090Sdougb#define DNS_RPZ_CIDR_KEY_BITS ((int)sizeof(dns_rpz_cidr_key_t)*8) 86224090Sdougb#define DNS_RPZ_CIDR_WORDS (128/DNS_RPZ_CIDR_WORD_BITS) 87224090Sdougbtypedef struct { 88224090Sdougb dns_rpz_cidr_word_t w[DNS_RPZ_CIDR_WORDS]; 89224090Sdougb} dns_rpz_cidr_key_t; 90224090Sdougb 91224090Sdougb#define ADDR_V4MAPPED 0xffff 92224090Sdougb 93224090Sdougb#define DNS_RPZ_WORD_MASK(b) \ 94224090Sdougb ((b) == 0 ? (dns_rpz_cidr_word_t)(-1) \ 95224090Sdougb : ((dns_rpz_cidr_word_t)(-1) \ 96224090Sdougb << (DNS_RPZ_CIDR_WORD_BITS - (b)))) 97224090Sdougb 98224090Sdougb#define DNS_RPZ_IP_BIT(ip, bitno) \ 99224090Sdougb (1 & ((ip)->w[(bitno)/DNS_RPZ_CIDR_WORD_BITS] >> \ 100224090Sdougb (DNS_RPZ_CIDR_WORD_BITS - 1 - ((bitno) % DNS_RPZ_CIDR_WORD_BITS)))) 101224090Sdougb 102224090Sdougbtypedef struct dns_rpz_cidr_node dns_rpz_cidr_node_t; 103224090Sdougbtypedef isc_uint8_t dns_rpz_cidr_flags_t; 104224090Sdougbstruct dns_rpz_cidr_node { 105224090Sdougb dns_rpz_cidr_node_t *parent; 106224090Sdougb dns_rpz_cidr_node_t *child[2]; 107224090Sdougb dns_rpz_cidr_key_t ip; 108224090Sdougb dns_rpz_cidr_bits_t bits; 109224090Sdougb dns_rpz_cidr_flags_t flags; 110224090Sdougb#define DNS_RPZ_CIDR_FG_IP 0x01 /* has IP data or is parent of IP */ 111224090Sdougb#define DNS_RPZ_CIDR_FG_IP_DATA 0x02 /* has IP data */ 112224090Sdougb#define DNS_RPZ_CIDR_FG_NSIPv4 0x04 /* has or is parent of NSIPv4 data */ 113224090Sdougb#define DNS_RPZ_CIDR_FG_NSIPv6 0x08 /* has or is parent of NSIPv6 data */ 114224090Sdougb#define DNS_RPZ_CIDR_FG_NSIP_DATA 0x10 /* has NSIP data */ 115224090Sdougb}; 116224090Sdougb 117224090Sdougbstruct dns_rpz_cidr { 118224090Sdougb isc_mem_t *mctx; 119224090Sdougb isc_boolean_t had_nsdname; 120224090Sdougb dns_rpz_cidr_node_t *root; 121224090Sdougb dns_name_t ip_name; /* RPZ_IP_ZONE.LOCALHOST. */ 122224090Sdougb dns_name_t nsip_name; /* RPZ_NSIP_ZONE.LOCALHOST. */ 123224090Sdougb dns_name_t nsdname_name; /* RPZ_NSDNAME_ZONE.LOCALHOST */ 124224090Sdougb}; 125224090Sdougb 126224090Sdougb 127224090Sdougbstatic isc_boolean_t have_rpz_zones = ISC_FALSE; 128224090Sdougb 129224090Sdougb 130224090Sdougbconst char * 131224090Sdougbdns_rpz_type2str(dns_rpz_type_t type) 132224090Sdougb{ 133224090Sdougb switch (type) { 134224090Sdougb case DNS_RPZ_TYPE_QNAME: 135224090Sdougb return ("QNAME"); 136224090Sdougb case DNS_RPZ_TYPE_IP: 137224090Sdougb return ("IP"); 138224090Sdougb case DNS_RPZ_TYPE_NSIP: 139224090Sdougb return ("NSIP"); 140224090Sdougb case DNS_RPZ_TYPE_NSDNAME: 141224090Sdougb return ("NSDNAME"); 142224090Sdougb case DNS_RPZ_TYPE_BAD: 143224090Sdougb break; 144224090Sdougb } 145224090Sdougb FATAL_ERROR(__FILE__, __LINE__, 146224090Sdougb "impossible response policy zone type %d", type); 147224090Sdougb return ("impossible"); 148224090Sdougb} 149224090Sdougb 150224090Sdougb 151224090Sdougb 152224090Sdougbdns_rpz_policy_t 153224090Sdougbdns_rpz_str2policy(const char *str) 154224090Sdougb{ 155224090Sdougb if (str == NULL) 156224090Sdougb return (DNS_RPZ_POLICY_ERROR); 157224090Sdougb if (!strcasecmp(str, "given")) 158224090Sdougb return (DNS_RPZ_POLICY_GIVEN); 159224090Sdougb if (!strcasecmp(str, "no-op")) 160224090Sdougb return (DNS_RPZ_POLICY_NO_OP); 161224090Sdougb if (!strcasecmp(str, "nxdomain")) 162224090Sdougb return (DNS_RPZ_POLICY_NXDOMAIN); 163224090Sdougb if (!strcasecmp(str, "nodata")) 164224090Sdougb return (DNS_RPZ_POLICY_NODATA); 165224090Sdougb if (!strcasecmp(str, "cname")) 166224090Sdougb return (DNS_RPZ_POLICY_CNAME); 167224090Sdougb return (DNS_RPZ_POLICY_ERROR); 168224090Sdougb} 169224090Sdougb 170224090Sdougb 171224090Sdougb 172224090Sdougb/* 173224090Sdougb * Free the radix tree of a response policy database. 174224090Sdougb */ 175224090Sdougbvoid 176224090Sdougbdns_rpz_cidr_free(dns_rpz_cidr_t **cidrp) { 177224090Sdougb dns_rpz_cidr_node_t *cur, *child, *parent; 178224090Sdougb dns_rpz_cidr_t *cidr; 179224090Sdougb 180224090Sdougb REQUIRE(cidrp != NULL); 181224090Sdougb 182224090Sdougb cidr = *cidrp; 183224090Sdougb if (cidr == NULL) 184224090Sdougb return; 185224090Sdougb 186224090Sdougb cur = cidr->root; 187224090Sdougb while (cur != NULL) { 188224090Sdougb /* Depth first. */ 189224090Sdougb child = cur->child[0]; 190224090Sdougb if (child != NULL) { 191224090Sdougb cur = child; 192224090Sdougb continue; 193224090Sdougb } 194224090Sdougb child = cur->child[1]; 195224090Sdougb if (child != NULL) { 196224090Sdougb cur = child; 197224090Sdougb continue; 198224090Sdougb } 199224090Sdougb 200224090Sdougb /* Delete this leaf and go up. */ 201224090Sdougb parent = cur->parent; 202224090Sdougb if (parent == NULL) 203224090Sdougb cidr->root = NULL; 204224090Sdougb else 205224090Sdougb parent->child[parent->child[1] == cur] = NULL; 206224090Sdougb isc_mem_put(cidr->mctx, cur, sizeof(*cur)); 207224090Sdougb cur = parent; 208224090Sdougb } 209224090Sdougb 210224090Sdougb dns_name_free(&cidr->ip_name, cidr->mctx); 211224090Sdougb dns_name_free(&cidr->nsip_name, cidr->mctx); 212224090Sdougb dns_name_free(&cidr->nsdname_name, cidr->mctx); 213224090Sdougb isc_mem_put(cidr->mctx, cidr, sizeof(*cidr)); 214224090Sdougb *cidrp = NULL; 215224090Sdougb} 216224090Sdougb 217224090Sdougb 218224090Sdougb 219224090Sdougb/* 220224090Sdougb * Forget a view's list of policy zones. 221224090Sdougb */ 222224090Sdougbvoid 223224090Sdougbdns_rpz_view_destroy(dns_view_t *view) { 224224090Sdougb dns_rpz_zone_t *zone; 225224090Sdougb 226224090Sdougb REQUIRE(view != NULL); 227224090Sdougb 228224090Sdougb while (!ISC_LIST_EMPTY(view->rpz_zones)) { 229224090Sdougb zone = ISC_LIST_HEAD(view->rpz_zones); 230224090Sdougb ISC_LIST_UNLINK(view->rpz_zones, zone, link); 231224090Sdougb if (dns_name_dynamic(&zone->origin)) 232224090Sdougb dns_name_free(&zone->origin, view->mctx); 233224090Sdougb if (dns_name_dynamic(&zone->nsdname)) 234224090Sdougb dns_name_free(&zone->nsdname, view->mctx); 235224090Sdougb if (dns_name_dynamic(&zone->cname)) 236224090Sdougb dns_name_free(&zone->cname, view->mctx); 237224090Sdougb isc_mem_put(view->mctx, zone, sizeof(*zone)); 238224090Sdougb } 239224090Sdougb} 240224090Sdougb 241224090Sdougb/* 242224090Sdougb * Note that we have at least one response policy zone. 243224090Sdougb * It would be better for something to tell the rbtdb code that the 244224090Sdougb * zone is in at least one view's list of policy zones. 245224090Sdougb */ 246224090Sdougbvoid 247224090Sdougbdns_rpz_set_need(isc_boolean_t need) 248224090Sdougb{ 249224090Sdougb have_rpz_zones = need; 250224090Sdougb} 251224090Sdougb 252224090Sdougb 253224090Sdougbisc_boolean_t 254224090Sdougbdns_rpz_needed(void) 255224090Sdougb{ 256224090Sdougb return (have_rpz_zones); 257224090Sdougb} 258224090Sdougb 259224090Sdougb 260224090Sdougb 261224090Sdougb/* 262224090Sdougb * Start a new radix tree for a response policy zone. 263224090Sdougb */ 264224090Sdougbisc_result_t 265224090Sdougbdns_rpz_new_cidr(isc_mem_t *mctx, dns_name_t *origin, 266224090Sdougb dns_rpz_cidr_t **rbtdb_cidr) 267224090Sdougb{ 268224090Sdougb isc_result_t result; 269224090Sdougb dns_rpz_cidr_t *cidr; 270224090Sdougb 271224090Sdougb REQUIRE(rbtdb_cidr != NULL && *rbtdb_cidr == NULL); 272224090Sdougb 273224090Sdougb /* 274224090Sdougb * Only if there is at least one response policy zone. 275224090Sdougb */ 276224090Sdougb if (!have_rpz_zones) 277224090Sdougb return (ISC_R_SUCCESS); 278224090Sdougb 279224090Sdougb cidr = isc_mem_get(mctx, sizeof(*cidr)); 280224090Sdougb if (cidr == NULL) 281224090Sdougb return (ISC_R_NOMEMORY); 282224090Sdougb memset(cidr, 0, sizeof(*cidr)); 283224090Sdougb cidr->mctx = mctx; 284224090Sdougb 285224090Sdougb dns_name_init(&cidr->ip_name, NULL); 286224090Sdougb result = dns_name_fromstring2(&cidr->ip_name, DNS_RPZ_IP_ZONE, origin, 287224090Sdougb DNS_NAME_DOWNCASE, mctx); 288224090Sdougb if (result != ISC_R_SUCCESS) { 289224090Sdougb isc_mem_put(mctx, cidr, sizeof(*cidr)); 290224090Sdougb return (result); 291224090Sdougb } 292224090Sdougb 293224090Sdougb dns_name_init(&cidr->nsip_name, NULL); 294224090Sdougb result = dns_name_fromstring2(&cidr->nsip_name, DNS_RPZ_NSIP_ZONE, 295224090Sdougb origin, DNS_NAME_DOWNCASE, mctx); 296224090Sdougb if (result != ISC_R_SUCCESS) { 297224090Sdougb dns_name_free(&cidr->ip_name, mctx); 298224090Sdougb isc_mem_put(mctx, cidr, sizeof(*cidr)); 299224090Sdougb return (result); 300224090Sdougb } 301224090Sdougb 302224090Sdougb dns_name_init(&cidr->nsdname_name, NULL); 303224090Sdougb result = dns_name_fromstring2(&cidr->nsdname_name, DNS_RPZ_NSDNAME_ZONE, 304224090Sdougb origin, DNS_NAME_DOWNCASE, mctx); 305224090Sdougb if (result != ISC_R_SUCCESS) { 306224090Sdougb dns_name_free(&cidr->nsip_name, mctx); 307224090Sdougb dns_name_free(&cidr->ip_name, mctx); 308224090Sdougb isc_mem_put(mctx, cidr, sizeof(*cidr)); 309224090Sdougb return (result); 310224090Sdougb } 311224090Sdougb 312224090Sdougb *rbtdb_cidr = cidr; 313224090Sdougb return (ISC_R_SUCCESS); 314224090Sdougb} 315224090Sdougb 316224090Sdougb 317224090Sdougb/* 318224090Sdougb * See if a policy zone has IP, NSIP, or NSDNAME rules or records. 319224090Sdougb */ 320224090Sdougbvoid 321224090Sdougbdns_rpz_enabled(dns_rpz_cidr_t *cidr, dns_rpz_st_t *st) { 322224090Sdougb if (cidr->root != NULL && 323224090Sdougb (cidr->root->flags & DNS_RPZ_CIDR_FG_IP) != 0) 324224090Sdougb st->state |= DNS_RPZ_HAVE_IP; 325224090Sdougb if (cidr->root != NULL && 326224090Sdougb (cidr->root->flags & DNS_RPZ_CIDR_FG_NSIPv4) != 0) 327224090Sdougb st->state |= DNS_RPZ_HAVE_NSIPv4; 328224090Sdougb if (cidr->root != NULL && 329224090Sdougb (cidr->root->flags & DNS_RPZ_CIDR_FG_NSIPv6) != 0) 330224090Sdougb st->state |= DNS_RPZ_HAVE_NSIPv6; 331224090Sdougb if (cidr->had_nsdname) 332224090Sdougb st->state |= DNS_RPZ_HAD_NSDNAME; 333224090Sdougb} 334224090Sdougb 335224090Sdougbstatic inline dns_rpz_cidr_flags_t 336224090Sdougbget_flags(const dns_rpz_cidr_key_t *ip, dns_rpz_cidr_bits_t prefix, 337224090Sdougb dns_rpz_type_t rpz_type) 338224090Sdougb{ 339224090Sdougb if (rpz_type == DNS_RPZ_TYPE_NSIP) { 340224090Sdougb if (prefix >= 96 && 341224090Sdougb ip->w[0] == 0 && ip->w[1] == 0 && 342224090Sdougb ip->w[2] == ADDR_V4MAPPED) 343224090Sdougb return (DNS_RPZ_CIDR_FG_NSIP_DATA | 344224090Sdougb DNS_RPZ_CIDR_FG_NSIPv4); 345224090Sdougb else 346224090Sdougb return (DNS_RPZ_CIDR_FG_NSIP_DATA | 347224090Sdougb DNS_RPZ_CIDR_FG_NSIPv6); 348224090Sdougb } else { 349224090Sdougb return (DNS_RPZ_CIDR_FG_IP | DNS_RPZ_CIDR_FG_IP_DATA); 350224090Sdougb } 351224090Sdougb} 352224090Sdougb 353224090Sdougb 354224090Sdougb 355224090Sdougb/* 356224090Sdougb * Mark a node as having IP or NSIP data and all of its parents 357224090Sdougb * as members of the IP or NSIP tree. 358224090Sdougb */ 359224090Sdougbstatic void 360224090Sdougbset_node_flags(dns_rpz_cidr_node_t *node, dns_rpz_type_t rpz_type) { 361224090Sdougb dns_rpz_cidr_flags_t flags; 362224090Sdougb 363224090Sdougb flags = get_flags(&node->ip, node->bits, rpz_type); 364224090Sdougb node->flags |= flags; 365224090Sdougb flags &= ~(DNS_RPZ_CIDR_FG_NSIP_DATA | DNS_RPZ_CIDR_FG_IP_DATA); 366224090Sdougb for (;;) { 367224090Sdougb node = node->parent; 368224090Sdougb if (node == NULL) 369224090Sdougb return; 370224090Sdougb node->flags |= flags; 371224090Sdougb } 372224090Sdougb} 373224090Sdougb 374224090Sdougb 375224090Sdougb 376224090Sdougb/* 377224090Sdougb * Make a radix tree node. 378224090Sdougb */ 379224090Sdougbstatic dns_rpz_cidr_node_t * 380224090Sdougbnew_node(dns_rpz_cidr_t *cidr, const dns_rpz_cidr_key_t *ip, 381224090Sdougb dns_rpz_cidr_bits_t bits, dns_rpz_cidr_flags_t flags) 382224090Sdougb{ 383224090Sdougb dns_rpz_cidr_node_t *node; 384224090Sdougb int i, words, wlen; 385224090Sdougb 386224090Sdougb node = isc_mem_get(cidr->mctx, sizeof(*node)); 387224090Sdougb if (node == NULL) 388224090Sdougb return (NULL); 389224090Sdougb memset(node, 0, sizeof(*node)); 390224090Sdougb 391224090Sdougb node->flags = flags & ~(DNS_RPZ_CIDR_FG_IP_DATA | 392224090Sdougb DNS_RPZ_CIDR_FG_NSIP_DATA); 393224090Sdougb 394224090Sdougb node->bits = bits; 395224090Sdougb words = bits / DNS_RPZ_CIDR_WORD_BITS; 396224090Sdougb wlen = bits % DNS_RPZ_CIDR_WORD_BITS; 397224090Sdougb i = 0; 398224090Sdougb while (i < words) { 399224090Sdougb node->ip.w[i] = ip->w[i]; 400224090Sdougb ++i; 401224090Sdougb } 402224090Sdougb if (wlen != 0) { 403224090Sdougb node->ip.w[i] = ip->w[i] & DNS_RPZ_WORD_MASK(wlen); 404224090Sdougb ++i; 405224090Sdougb } 406224090Sdougb while (i < DNS_RPZ_CIDR_WORDS) 407224090Sdougb node->ip.w[i++] = 0; 408224090Sdougb 409224090Sdougb return (node); 410224090Sdougb} 411224090Sdougb 412224090Sdougb 413224090Sdougb 414224090Sdougbstatic void 415224090Sdougbbadname(int level, dns_name_t *name, const char *comment) 416224090Sdougb{ 417224090Sdougb char printname[DNS_NAME_FORMATSIZE]; 418224090Sdougb 419224090Sdougb if (isc_log_wouldlog(dns_lctx, level)) { 420224090Sdougb dns_name_format(name, printname, sizeof(printname)); 421224090Sdougb isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 422224090Sdougb DNS_LOGMODULE_RBTDB, level, 423224090Sdougb "invalid response policy name \"%s\"%s", 424224090Sdougb printname, comment); 425224090Sdougb } 426224090Sdougb} 427224090Sdougb 428224090Sdougb 429224090Sdougb 430224090Sdougb/* 431224090Sdougb * Convert an IP address from radix tree binary (host byte order) to 432224090Sdougb * to its canonical response policy domain name and its name in the 433224090Sdougb * policy zone. 434224090Sdougb */ 435224090Sdougbstatic isc_result_t 436224090Sdougbip2name(dns_rpz_cidr_t *cidr, const dns_rpz_cidr_key_t *tgt_ip, 437224090Sdougb dns_rpz_cidr_bits_t tgt_prefix, dns_rpz_type_t type, 438224090Sdougb dns_name_t *canon_name, dns_name_t *search_name) 439224090Sdougb{ 440224090Sdougb#ifndef INET6_ADDRSTRLEN 441224090Sdougb#define INET6_ADDRSTRLEN 46 442224090Sdougb#endif 443224090Sdougb int w[DNS_RPZ_CIDR_WORDS*2]; 444224090Sdougb char str[1+8+1+INET6_ADDRSTRLEN+1]; 445224090Sdougb isc_buffer_t buffer; 446224090Sdougb dns_name_t *name; 447224090Sdougb isc_result_t result; 448224090Sdougb isc_boolean_t zeros; 449224090Sdougb int i, n, len; 450224090Sdougb 451224090Sdougb if (tgt_prefix > 96 && 452224090Sdougb tgt_ip->w[0] == 0 && 453224090Sdougb tgt_ip->w[1] == 0 && 454224090Sdougb tgt_ip->w[2] == ADDR_V4MAPPED) { 455224090Sdougb len = snprintf(str, sizeof(str), "%d.%d.%d.%d.%d", 456224090Sdougb tgt_prefix - 96, 457224090Sdougb tgt_ip->w[3] & 0xff, 458224090Sdougb (tgt_ip->w[3]>>8) & 0xff, 459224090Sdougb (tgt_ip->w[3]>>16) & 0xff, 460224090Sdougb (tgt_ip->w[3]>>24) & 0xff); 461224090Sdougb if (len == -1 || len > (int)sizeof(str)) 462224090Sdougb return (ISC_R_FAILURE); 463224090Sdougb } else { 464224090Sdougb for (i = 0; i < DNS_RPZ_CIDR_WORDS; i++) { 465224090Sdougb w[i*2+1] = ((tgt_ip->w[DNS_RPZ_CIDR_WORDS-1-i] >> 16) 466224090Sdougb & 0xffff); 467224090Sdougb w[i*2] = tgt_ip->w[DNS_RPZ_CIDR_WORDS-1-i] & 0xffff; 468224090Sdougb } 469224090Sdougb zeros = ISC_FALSE; 470224090Sdougb len = snprintf(str, sizeof(str), "%d", tgt_prefix); 471224090Sdougb if (len == -1) 472224090Sdougb return (ISC_R_FAILURE); 473224090Sdougb i = 0; 474224090Sdougb while (i < DNS_RPZ_CIDR_WORDS * 2) { 475224090Sdougb if (w[i] != 0 || zeros 476224090Sdougb || i >= DNS_RPZ_CIDR_WORDS * 2 - 1 477224090Sdougb || w[i+1] != 0) { 478224090Sdougb INSIST((size_t)len <= sizeof(str)); 479224090Sdougb n = snprintf(&str[len], sizeof(str) - len, 480224090Sdougb ".%x", w[i++]); 481224090Sdougb if (n < 0) 482224090Sdougb return (ISC_R_FAILURE); 483224090Sdougb len += n; 484224090Sdougb } else { 485224090Sdougb zeros = ISC_TRUE; 486224090Sdougb INSIST((size_t)len <= sizeof(str)); 487224090Sdougb n = snprintf(&str[len], sizeof(str) - len, 488224090Sdougb ".zz"); 489224090Sdougb if (n < 0) 490224090Sdougb return (ISC_R_FAILURE); 491224090Sdougb len += n; 492224090Sdougb i += 2; 493224090Sdougb while (i < DNS_RPZ_CIDR_WORDS * 2 && w[i] == 0) 494224090Sdougb ++i; 495224090Sdougb } 496224090Sdougb if (len > (int)sizeof(str)) 497224090Sdougb return (ISC_R_FAILURE); 498224090Sdougb } 499224090Sdougb } 500224090Sdougb 501224090Sdougb if (canon_name != NULL) { 502224090Sdougb isc__buffer_init(&buffer, str, sizeof(str)); 503224090Sdougb isc__buffer_add(&buffer, len); 504224090Sdougb result = dns_name_fromtext(canon_name, &buffer, 505224090Sdougb dns_rootname, 0, NULL); 506224090Sdougb if (result != ISC_R_SUCCESS) 507224090Sdougb return (result); 508224090Sdougb } 509224090Sdougb if (search_name != NULL) { 510224090Sdougb isc__buffer_init(&buffer, str, sizeof(str)); 511224090Sdougb isc__buffer_add(&buffer, len); 512224090Sdougb if (type == DNS_RPZ_TYPE_NSIP) 513224090Sdougb name = &cidr->nsip_name; 514224090Sdougb else 515224090Sdougb name = &cidr->ip_name; 516224090Sdougb result = dns_name_fromtext(search_name, &buffer, name, 0, NULL); 517224090Sdougb if (result != ISC_R_SUCCESS) 518224090Sdougb return (result); 519224090Sdougb } 520224090Sdougb return (ISC_R_SUCCESS); 521224090Sdougb} 522224090Sdougb 523224090Sdougb 524224090Sdougb 525224090Sdougb/* 526224090Sdougb * Decide which kind of IP address response policy zone a name is in. 527224090Sdougb */ 528224090Sdougbstatic dns_rpz_type_t 529224090Sdougbset_type(dns_rpz_cidr_t *cidr, dns_name_t *name) { 530224090Sdougb 531224090Sdougb if (dns_name_issubdomain(name, &cidr->ip_name)) 532224090Sdougb return (DNS_RPZ_TYPE_IP); 533224090Sdougb 534224090Sdougb /* 535224090Sdougb * Require `./configure --enable-rpz-nsip` and nsdname 536224090Sdougb * until consistency problems are resolved. 537224090Sdougb */ 538224090Sdougb#ifdef ENABLE_RPZ_NSIP 539224090Sdougb if (dns_name_issubdomain(name, &cidr->nsip_name)) 540224090Sdougb return (DNS_RPZ_TYPE_NSIP); 541224090Sdougb#endif 542224090Sdougb 543224090Sdougb#ifdef ENABLE_RPZ_NSDNAME 544224090Sdougb if (dns_name_issubdomain(name, &cidr->nsdname_name)) 545224090Sdougb return (DNS_RPZ_TYPE_NSDNAME); 546224090Sdougb#endif 547224090Sdougb 548224090Sdougb return (DNS_RPZ_TYPE_QNAME); 549224090Sdougb} 550224090Sdougb 551224090Sdougb 552224090Sdougb 553224090Sdougb/* 554224090Sdougb * Convert an IP address from canonical response policy domain name form 555224090Sdougb * to radix tree binary (host byte order). 556224090Sdougb */ 557224090Sdougbstatic isc_result_t 558224090Sdougbname2ipkey(dns_rpz_cidr_t *cidr, int level, dns_name_t *src_name, 559224090Sdougb dns_rpz_type_t type, dns_rpz_cidr_key_t *tgt_ip, 560224090Sdougb dns_rpz_cidr_bits_t *tgt_prefix) 561224090Sdougb{ 562224090Sdougb isc_buffer_t buffer; 563224090Sdougb unsigned char data[DNS_NAME_MAXWIRE+1]; 564224090Sdougb dns_fixedname_t fname; 565224090Sdougb dns_name_t *name; 566224090Sdougb const char *cp, *end; 567224090Sdougb char *cp2; 568224090Sdougb int ip_labels; 569224090Sdougb dns_rpz_cidr_bits_t bits; 570224090Sdougb unsigned long prefix, l; 571224090Sdougb int i; 572224090Sdougb 573224090Sdougb /* 574224090Sdougb * Need at least enough labels for the shortest name, 575224090Sdougb * :: or 128.*.RPZ_x_ZONE.rpz.LOCALHOST. 576224090Sdougb */ 577224090Sdougb ip_labels = dns_name_countlabels(src_name); 578224090Sdougb ip_labels -= dns_name_countlabels(&cidr->ip_name); 579224090Sdougb ip_labels--; 580224090Sdougb if (ip_labels < 1) { 581224090Sdougb badname(level, src_name, ", too short"); 582224090Sdougb return (ISC_R_FAILURE); 583224090Sdougb } 584224090Sdougb 585224090Sdougb /* 586224090Sdougb * Get text for the IP address without RPZ_x_ZONE.rpz.LOCALHOST. 587224090Sdougb */ 588224090Sdougb dns_fixedname_init(&fname); 589224090Sdougb name = dns_fixedname_name(&fname); 590224090Sdougb dns_name_split(src_name, dns_name_countlabels(&cidr->ip_name), 591224090Sdougb name, NULL); 592224090Sdougb isc_buffer_init(&buffer, data, sizeof(data)); 593224090Sdougb dns_name_totext(name, ISC_TRUE, &buffer); 594224090Sdougb isc_buffer_putuint8(&buffer, '\0'); 595224090Sdougb cp = isc_buffer_base(&buffer); 596224090Sdougb 597224090Sdougb prefix = strtoul(cp, &cp2, 10); 598224090Sdougb if (prefix < 1U || prefix > 128U || *cp2 != '.') { 599224090Sdougb badname(level, src_name, ", bad prefix length"); 600224090Sdougb return (ISC_R_FAILURE); 601224090Sdougb } 602224090Sdougb cp = cp2+1; 603224090Sdougb 604224090Sdougb end = isc_buffer_used(&buffer); 605224090Sdougb if (ip_labels == 4 && !strchr(cp, 'z')) { 606224090Sdougb /* 607224090Sdougb * Convert an IPv4 address 608224090Sdougb * from the form "prefix.w.z.y.x" 609224090Sdougb */ 610224090Sdougb if (prefix > 32U) { 611224090Sdougb badname(level, src_name, "; bad IPv4 prefix length"); 612224090Sdougb return (ISC_R_FAILURE); 613224090Sdougb } 614224090Sdougb prefix += 96; 615224090Sdougb *tgt_prefix = (dns_rpz_cidr_bits_t)prefix; 616224090Sdougb tgt_ip->w[0] = 0; 617224090Sdougb tgt_ip->w[1] = 0; 618224090Sdougb tgt_ip->w[2] = ADDR_V4MAPPED; 619224090Sdougb tgt_ip->w[3] = 0; 620224090Sdougb for (i = 0; i < 32; i += 8) { 621224090Sdougb l = strtoul(cp, &cp2, 10); 622224090Sdougb if (l > 255U || (*cp2 != '.' && *cp2 != '\0')) { 623224090Sdougb badname(level, src_name, "; bad IPv4 address"); 624224090Sdougb return (ISC_R_FAILURE); 625224090Sdougb } 626224090Sdougb tgt_ip->w[3] |= l << i; 627224090Sdougb cp = cp2 + 1; 628224090Sdougb } 629224090Sdougb } else { 630224090Sdougb /* 631224090Sdougb * Convert a text IPv6 address. 632224090Sdougb */ 633224090Sdougb *tgt_prefix = (dns_rpz_cidr_bits_t)prefix; 634224090Sdougb for (i = 0; 635224090Sdougb ip_labels > 0 && i < DNS_RPZ_CIDR_WORDS * 2; 636224090Sdougb ip_labels--) { 637224090Sdougb if (cp[0] == 'z' && cp[1] == 'z' && 638224090Sdougb (cp[2] == '.' || cp[2] == '\0') && 639224090Sdougb i <= 6) { 640224090Sdougb do { 641224090Sdougb if ((i & 1) == 0) 642224090Sdougb tgt_ip->w[3-i/2] = 0; 643224090Sdougb ++i; 644224090Sdougb } while (ip_labels + i <= 8); 645224090Sdougb cp += 3; 646224090Sdougb } else { 647224090Sdougb l = strtoul(cp, &cp2, 16); 648224090Sdougb if (l > 0xffffu || 649224090Sdougb (*cp2 != '.' && *cp2 != '\0')) { 650224090Sdougb badname(level, src_name, ""); 651224090Sdougb return (ISC_R_FAILURE); 652224090Sdougb } 653224090Sdougb if ((i & 1) == 0) 654224090Sdougb tgt_ip->w[3-i/2] = l; 655224090Sdougb else 656224090Sdougb tgt_ip->w[3-i/2] |= l << 16; 657224090Sdougb i++; 658224090Sdougb cp = cp2 + 1; 659224090Sdougb } 660224090Sdougb } 661224090Sdougb } 662224090Sdougb if (cp != end) { 663224090Sdougb badname(level, src_name, ""); 664224090Sdougb return (ISC_R_FAILURE); 665224090Sdougb } 666224090Sdougb 667224090Sdougb /* 668224090Sdougb * Check for 1s after the prefix length. 669224090Sdougb */ 670224090Sdougb bits = (dns_rpz_cidr_bits_t)prefix; 671224090Sdougb while (bits < DNS_RPZ_CIDR_KEY_BITS) { 672224090Sdougb dns_rpz_cidr_word_t aword; 673224090Sdougb 674224090Sdougb i = bits % DNS_RPZ_CIDR_WORD_BITS; 675224090Sdougb aword = tgt_ip->w[bits / DNS_RPZ_CIDR_WORD_BITS]; 676224090Sdougb if ((aword & ~DNS_RPZ_WORD_MASK(i)) != 0) { 677224090Sdougb badname(level, src_name, "; wrong prefix length"); 678224090Sdougb return (ISC_R_FAILURE); 679224090Sdougb } 680224090Sdougb bits -= i; 681224090Sdougb bits += DNS_RPZ_CIDR_WORD_BITS; 682224090Sdougb } 683224090Sdougb 684224090Sdougb /* 685224090Sdougb * Convert the IPv6 address back to a canonical policy domain name 686224090Sdougb * to ensure that it is in canonical form. 687224090Sdougb */ 688224090Sdougb if (ISC_R_SUCCESS != ip2name(cidr, tgt_ip, (dns_rpz_cidr_bits_t)prefix, 689224090Sdougb type, NULL, name) || 690224090Sdougb !dns_name_equal(src_name, name)) { 691224090Sdougb badname(level, src_name, "; not canonical"); 692224090Sdougb return (ISC_R_FAILURE); 693224090Sdougb } 694224090Sdougb 695224090Sdougb return (ISC_R_SUCCESS); 696224090Sdougb} 697224090Sdougb 698224090Sdougb 699224090Sdougb 700224090Sdougb/* 701224090Sdougb * find first differing bit 702224090Sdougb */ 703224090Sdougbstatic int 704224090Sdougbffbit(dns_rpz_cidr_word_t w) { 705224090Sdougb int bit; 706224090Sdougb 707224090Sdougb if (w == 0) 708224090Sdougb return (DNS_RPZ_CIDR_WORD_BITS); 709224090Sdougb for (bit = 0; (w & (1U << (DNS_RPZ_CIDR_WORD_BITS-1))) == 0; bit++) 710224090Sdougb w <<= 1; 711224090Sdougb return (bit); 712224090Sdougb} 713224090Sdougb 714224090Sdougb 715224090Sdougb 716224090Sdougb/* 717224090Sdougb * find the first differing bit in two keys 718224090Sdougb */ 719224090Sdougbstatic int 720224090Sdougbdiff_keys(const dns_rpz_cidr_key_t *key1, dns_rpz_cidr_bits_t bits1, 721224090Sdougb const dns_rpz_cidr_key_t *key2, dns_rpz_cidr_bits_t bits2) 722224090Sdougb{ 723224090Sdougb dns_rpz_cidr_word_t delta; 724224090Sdougb dns_rpz_cidr_bits_t maxbit, bit; 725224090Sdougb int i; 726224090Sdougb 727224090Sdougb maxbit = ISC_MIN(bits1, bits2); 728224090Sdougb 729224090Sdougb /* 730224090Sdougb * find the first differing words 731224090Sdougb */ 732224090Sdougb for (i = 0, bit = 0; 733224090Sdougb bit <= maxbit; 734224090Sdougb i++, bit += DNS_RPZ_CIDR_WORD_BITS) { 735224090Sdougb delta = key1->w[i] ^ key2->w[i]; 736224090Sdougb if (delta != 0) { 737224090Sdougb bit += ffbit(delta); 738224090Sdougb break; 739224090Sdougb } 740224090Sdougb } 741224090Sdougb return (ISC_MIN(bit, maxbit)); 742224090Sdougb} 743224090Sdougb 744224090Sdougb 745224090Sdougb 746224090Sdougb/* 747224090Sdougb * Search a radix tree for an IP address for ordinary lookup 748224090Sdougb * or for a CIDR block adding or deleting an entry 749224090Sdougb * The tree read (for simple search) or write lock must be held by the caller. 750224090Sdougb * 751224090Sdougb * return ISC_R_SUCCESS, ISC_R_NOTFOUND, DNS_R_PARTIALMATCH, ISC_R_EXISTS, 752224090Sdougb * ISC_R_NOMEMORY 753224090Sdougb */ 754224090Sdougbstatic isc_result_t 755224090Sdougbsearch(dns_rpz_cidr_t *cidr, const dns_rpz_cidr_key_t *tgt_ip, 756224090Sdougb dns_rpz_cidr_bits_t tgt_prefix, dns_rpz_type_t type, 757224090Sdougb isc_boolean_t create, 758224090Sdougb dns_rpz_cidr_node_t **found) /* NULL or longest match node */ 759224090Sdougb{ 760224090Sdougb dns_rpz_cidr_node_t *cur, *parent, *child, *new_parent, *sibling; 761224090Sdougb int cur_num, child_num; 762224090Sdougb dns_rpz_cidr_bits_t dbit; 763224090Sdougb dns_rpz_cidr_flags_t flags, data_flag; 764224090Sdougb isc_result_t find_result; 765224090Sdougb 766224090Sdougb flags = get_flags(tgt_ip, tgt_prefix, type); 767224090Sdougb data_flag = flags & (DNS_RPZ_CIDR_FG_IP_DATA | 768224090Sdougb DNS_RPZ_CIDR_FG_NSIP_DATA); 769224090Sdougb 770224090Sdougb find_result = ISC_R_NOTFOUND; 771224090Sdougb if (found != NULL) 772224090Sdougb *found = NULL; 773224090Sdougb cur = cidr->root; 774224090Sdougb parent = NULL; 775224090Sdougb cur_num = 0; 776224090Sdougb for (;;) { 777224090Sdougb if (cur == NULL) { 778224090Sdougb /* 779224090Sdougb * No child so we cannot go down. Fail or 780224090Sdougb * add the target as a child of the current parent. 781224090Sdougb */ 782224090Sdougb if (!create) 783224090Sdougb return (find_result); 784224090Sdougb child = new_node(cidr, tgt_ip, tgt_prefix, 0); 785224090Sdougb if (child == NULL) 786224090Sdougb return (ISC_R_NOMEMORY); 787224090Sdougb if (parent == NULL) 788224090Sdougb cidr->root = child; 789224090Sdougb else 790224090Sdougb parent->child[cur_num] = child; 791224090Sdougb child->parent = parent; 792224090Sdougb set_node_flags(child, type); 793224090Sdougb if (found != NULL) 794224090Sdougb *found = cur; 795224090Sdougb return (ISC_R_SUCCESS); 796224090Sdougb } 797224090Sdougb 798224090Sdougb /* 799224090Sdougb * Pretend a node not in the correct tree does not exist 800224090Sdougb * if we are not adding to the tree, 801224090Sdougb * If we are adding, then continue down to eventually 802224090Sdougb * add a node and mark/put this node in the correct tree. 803224090Sdougb */ 804224090Sdougb if ((cur->flags & flags) == 0 && !create) 805224090Sdougb return (find_result); 806224090Sdougb 807224090Sdougb dbit = diff_keys(tgt_ip, tgt_prefix, &cur->ip, cur->bits); 808224090Sdougb /* 809224090Sdougb * dbit <= tgt_prefix and dbit <= cur->bits always. 810224090Sdougb * We are finished searching if we matched all of the target. 811224090Sdougb */ 812224090Sdougb if (dbit == tgt_prefix) { 813224090Sdougb if (tgt_prefix == cur->bits) { 814224090Sdougb /* 815224090Sdougb * The current node matches the target exactly. 816224090Sdougb * It is the answer if it has data. 817224090Sdougb */ 818224090Sdougb if ((cur->flags & data_flag) != 0) { 819224090Sdougb if (create) 820224090Sdougb return (ISC_R_EXISTS); 821224090Sdougb if (found != NULL) 822224090Sdougb *found = cur; 823224090Sdougb return (ISC_R_SUCCESS); 824224090Sdougb } else if (create) { 825224090Sdougb /* 826224090Sdougb * The node had no data but does now. 827224090Sdougb */ 828224090Sdougb set_node_flags(cur, type); 829224090Sdougb if (found != NULL) 830224090Sdougb *found = cur; 831224090Sdougb return (ISC_R_SUCCESS); 832224090Sdougb } 833224090Sdougb return (find_result); 834224090Sdougb } 835224090Sdougb 836224090Sdougb /* 837224090Sdougb * We know tgt_prefix < cur_bits which means that 838224090Sdougb * the target is shorter than the current node. 839224090Sdougb * Add the target as the current node's parent. 840224090Sdougb */ 841224090Sdougb if (!create) 842224090Sdougb return (find_result); 843224090Sdougb 844224090Sdougb new_parent = new_node(cidr, tgt_ip, tgt_prefix, 845224090Sdougb cur->flags); 846224090Sdougb if (new_parent == NULL) 847224090Sdougb return (ISC_R_NOMEMORY); 848224090Sdougb new_parent->parent = parent; 849224090Sdougb if (parent == NULL) 850224090Sdougb cidr->root = new_parent; 851224090Sdougb else 852224090Sdougb parent->child[cur_num] = new_parent; 853224090Sdougb child_num = DNS_RPZ_IP_BIT(&cur->ip, tgt_prefix+1); 854224090Sdougb new_parent->child[child_num] = cur; 855224090Sdougb cur->parent = new_parent; 856224090Sdougb set_node_flags(new_parent, type); 857224090Sdougb if (found != NULL) 858224090Sdougb *found = new_parent; 859224090Sdougb return (ISC_R_SUCCESS); 860224090Sdougb } 861224090Sdougb 862224090Sdougb if (dbit == cur->bits) { 863224090Sdougb /* 864224090Sdougb * We have a partial match by matching of all of the 865224090Sdougb * current node but only part of the target. 866224090Sdougb * Try to go down. 867224090Sdougb */ 868224090Sdougb if ((cur->flags & data_flag) != 0) { 869224090Sdougb find_result = DNS_R_PARTIALMATCH; 870224090Sdougb if (found != NULL) 871224090Sdougb *found = cur; 872224090Sdougb } 873224090Sdougb 874224090Sdougb parent = cur; 875224090Sdougb cur_num = DNS_RPZ_IP_BIT(tgt_ip, dbit); 876224090Sdougb cur = cur->child[cur_num]; 877224090Sdougb continue; 878224090Sdougb } 879224090Sdougb 880224090Sdougb 881224090Sdougb /* 882224090Sdougb * dbit < tgt_prefix and dbit < cur->bits, 883224090Sdougb * so we failed to match both the target and the current node. 884224090Sdougb * Insert a fork of a parent above the current node and 885224090Sdougb * add the target as a sibling of the current node 886224090Sdougb */ 887224090Sdougb if (!create) 888224090Sdougb return (find_result); 889224090Sdougb 890224090Sdougb sibling = new_node(cidr, tgt_ip, tgt_prefix, 0); 891224090Sdougb if (sibling == NULL) 892224090Sdougb return (ISC_R_NOMEMORY); 893224090Sdougb new_parent = new_node(cidr, tgt_ip, dbit, cur->flags); 894224090Sdougb if (new_parent == NULL) { 895224090Sdougb isc_mem_put(cidr->mctx, sibling, sizeof(*sibling)); 896224090Sdougb return (ISC_R_NOMEMORY); 897224090Sdougb } 898224090Sdougb new_parent->parent = parent; 899224090Sdougb if (parent == NULL) 900224090Sdougb cidr->root = new_parent; 901224090Sdougb else 902224090Sdougb parent->child[cur_num] = new_parent; 903224090Sdougb child_num = DNS_RPZ_IP_BIT(tgt_ip, dbit); 904224090Sdougb new_parent->child[child_num] = sibling; 905224090Sdougb new_parent->child[1-child_num] = cur; 906224090Sdougb cur->parent = new_parent; 907224090Sdougb sibling->parent = new_parent; 908224090Sdougb set_node_flags(sibling, type); 909224090Sdougb if (found != NULL) 910224090Sdougb *found = sibling; 911224090Sdougb return (ISC_R_SUCCESS); 912224090Sdougb } 913224090Sdougb} 914224090Sdougb 915224090Sdougb 916224090Sdougb 917224090Sdougb/* 918224090Sdougb * Add an IP address to the radix tree of a response policy database. 919224090Sdougb * The tree write lock must be held by the caller. 920224090Sdougb */ 921224090Sdougbvoid 922224090Sdougbdns_rpz_cidr_addip(dns_rpz_cidr_t *cidr, dns_name_t *name) 923224090Sdougb{ 924224090Sdougb dns_rpz_cidr_key_t tgt_ip; 925224090Sdougb dns_rpz_cidr_bits_t tgt_prefix; 926224090Sdougb dns_rpz_type_t type; 927224090Sdougb 928224090Sdougb if (cidr == NULL) 929224090Sdougb return; 930224090Sdougb 931224090Sdougb /* 932224090Sdougb * no worries if the new name is not an IP address 933224090Sdougb */ 934224090Sdougb type = set_type(cidr, name); 935224090Sdougb switch (type) { 936224090Sdougb case DNS_RPZ_TYPE_IP: 937224090Sdougb case DNS_RPZ_TYPE_NSIP: 938224090Sdougb break; 939224090Sdougb case DNS_RPZ_TYPE_NSDNAME: 940224090Sdougb cidr->had_nsdname = ISC_TRUE; 941224090Sdougb return; 942224090Sdougb case DNS_RPZ_TYPE_QNAME: 943224090Sdougb case DNS_RPZ_TYPE_BAD: 944224090Sdougb return; 945224090Sdougb } 946224090Sdougb if (ISC_R_SUCCESS != name2ipkey(cidr, DNS_RPZ_ERROR_LEVEL, name, 947224090Sdougb type, &tgt_ip, &tgt_prefix)) 948224090Sdougb return; 949224090Sdougb 950224090Sdougb if (ISC_R_EXISTS == search(cidr, &tgt_ip, tgt_prefix, type, 951224090Sdougb ISC_TRUE, NULL) && 952224090Sdougb isc_log_wouldlog(dns_lctx, DNS_RPZ_ERROR_LEVEL)) { 953224090Sdougb char printname[DNS_NAME_FORMATSIZE]; 954224090Sdougb 955224090Sdougb dns_name_format(name, printname, sizeof(printname)); 956224090Sdougb isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 957224090Sdougb DNS_LOGMODULE_RBTDB, DNS_RPZ_ERROR_LEVEL, 958224090Sdougb "duplicate response policy name \"%s\"", 959224090Sdougb printname); 960224090Sdougb } 961224090Sdougb} 962224090Sdougb 963224090Sdougb 964224090Sdougb 965224090Sdougb/* 966224090Sdougb * Delete an IP address from the radix tree of a response policy database. 967224090Sdougb * The tree write lock must be held by the caller. 968224090Sdougb */ 969224090Sdougbvoid 970224090Sdougbdns_rpz_cidr_deleteip(dns_rpz_cidr_t *cidr, dns_name_t *name) { 971224090Sdougb dns_rpz_cidr_key_t tgt_ip; 972224090Sdougb dns_rpz_cidr_bits_t tgt_prefix; 973224090Sdougb dns_rpz_type_t type; 974224090Sdougb dns_rpz_cidr_node_t *tgt = NULL, *parent, *child; 975224090Sdougb dns_rpz_cidr_flags_t flags, data_flag; 976224090Sdougb 977224090Sdougb if (cidr == NULL) 978224090Sdougb return; 979224090Sdougb 980224090Sdougb /* 981224090Sdougb * Decide which kind of policy zone IP address it is, if either 982224090Sdougb * and then find its node. 983224090Sdougb */ 984224090Sdougb type = set_type(cidr, name); 985224090Sdougb switch (type) { 986224090Sdougb case DNS_RPZ_TYPE_IP: 987224090Sdougb case DNS_RPZ_TYPE_NSIP: 988224090Sdougb break; 989224090Sdougb case DNS_RPZ_TYPE_NSDNAME: 990224090Sdougb /* 991224090Sdougb * We cannot easily count nsdnames because 992224090Sdougb * internal rbt nodes get deleted. 993224090Sdougb */ 994224090Sdougb return; 995224090Sdougb case DNS_RPZ_TYPE_QNAME: 996224090Sdougb case DNS_RPZ_TYPE_BAD: 997224090Sdougb return; 998224090Sdougb } 999224090Sdougb 1000224090Sdougb /* 1001224090Sdougb * Do not get excited about the deletion of interior rbt nodes. 1002224090Sdougb */ 1003224090Sdougb if (ISC_R_SUCCESS != name2ipkey(cidr, DNS_RPZ_DEBUG_LEVEL2, name, 1004224090Sdougb type, &tgt_ip, &tgt_prefix)) 1005224090Sdougb return; 1006224090Sdougb if (ISC_R_SUCCESS != search(cidr, &tgt_ip, tgt_prefix, type, 1007224090Sdougb ISC_FALSE, &tgt)) { 1008224090Sdougb if (isc_log_wouldlog(dns_lctx, DNS_RPZ_ERROR_LEVEL)) { 1009224090Sdougb char printname[DNS_NAME_FORMATSIZE]; 1010224090Sdougb 1011224090Sdougb dns_name_format(name, printname, sizeof(printname)); 1012224090Sdougb isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, 1013224090Sdougb DNS_LOGMODULE_RBTDB, DNS_RPZ_ERROR_LEVEL, 1014224090Sdougb "missing response policy node \"%s\"", 1015224090Sdougb printname); 1016224090Sdougb } 1017224090Sdougb return; 1018224090Sdougb } 1019224090Sdougb 1020224090Sdougb /* 1021224090Sdougb * Mark the node and its parents to reflect the deleted IP address. 1022224090Sdougb */ 1023224090Sdougb flags = get_flags(&tgt_ip, tgt_prefix, type); 1024224090Sdougb data_flag = flags & (DNS_RPZ_CIDR_FG_IP_DATA | 1025224090Sdougb DNS_RPZ_CIDR_FG_NSIP_DATA); 1026224090Sdougb tgt->flags &= ~data_flag; 1027224090Sdougb for (parent = tgt; parent != NULL; parent = parent->parent) { 1028224090Sdougb if ((parent->flags & data_flag) != 0 || 1029224090Sdougb (parent->child[0] != NULL && 1030224090Sdougb (parent->child[0]->flags & flags) != 0) || 1031224090Sdougb (parent->child[1] != NULL && 1032224090Sdougb (parent->child[1]->flags & flags) != 0)) 1033224090Sdougb break; 1034224090Sdougb parent->flags &= ~flags; 1035224090Sdougb } 1036224090Sdougb 1037224090Sdougb /* 1038224090Sdougb * We might need to delete 2 nodes. 1039224090Sdougb */ 1040224090Sdougb do { 1041224090Sdougb /* 1042224090Sdougb * The node is now useless if it has no data of its own 1043224090Sdougb * and 0 or 1 children. We are finished if it is not useless. 1044224090Sdougb */ 1045224090Sdougb if ((child = tgt->child[0]) != NULL) { 1046224090Sdougb if (tgt->child[1] != NULL) 1047224090Sdougb return; 1048224090Sdougb } else { 1049224090Sdougb child = tgt->child[1]; 1050224090Sdougb } 1051224090Sdougb if ((tgt->flags & (DNS_RPZ_CIDR_FG_IP_DATA | 1052224090Sdougb DNS_RPZ_CIDR_FG_NSIP_DATA)) != 0) 1053224090Sdougb return; 1054224090Sdougb 1055224090Sdougb /* 1056224090Sdougb * Replace the pointer to this node in the parent with 1057224090Sdougb * the remaining child or NULL. 1058224090Sdougb */ 1059224090Sdougb parent = tgt->parent; 1060224090Sdougb if (parent == NULL) { 1061224090Sdougb cidr->root = child; 1062224090Sdougb } else { 1063224090Sdougb parent->child[parent->child[1] == tgt] = child; 1064224090Sdougb } 1065224090Sdougb /* 1066224090Sdougb * If the child exists fix up its parent pointer. 1067224090Sdougb */ 1068224090Sdougb if (child != NULL) 1069224090Sdougb child->parent = parent; 1070224090Sdougb isc_mem_put(cidr->mctx, tgt, sizeof(*tgt)); 1071224090Sdougb 1072224090Sdougb tgt = parent; 1073224090Sdougb } while (tgt != NULL); 1074224090Sdougb} 1075224090Sdougb 1076224090Sdougb 1077224090Sdougb 1078224090Sdougb/* 1079224090Sdougb * Caller must hold tree lock. 1080224090Sdougb * Return ISC_R_NOTFOUND 1081224090Sdougb * or ISC_R_SUCCESS and the found entry's canonical and search names 1082224090Sdougb * and its prefix length 1083224090Sdougb */ 1084224090Sdougbisc_result_t 1085224090Sdougbdns_rpz_cidr_find(dns_rpz_cidr_t *cidr, const isc_netaddr_t *netaddr, 1086224090Sdougb dns_rpz_type_t type, dns_name_t *canon_name, 1087224090Sdougb dns_name_t *search_name, dns_rpz_cidr_bits_t *prefix) 1088224090Sdougb{ 1089224090Sdougb dns_rpz_cidr_key_t tgt_ip; 1090224090Sdougb isc_result_t result; 1091224090Sdougb dns_rpz_cidr_node_t *found; 1092224090Sdougb int i; 1093224090Sdougb 1094224090Sdougb /* 1095224090Sdougb * Convert IP address to CIDR tree key. 1096224090Sdougb */ 1097224090Sdougb if (netaddr->family == AF_INET) { 1098224090Sdougb tgt_ip.w[0] = 0; 1099224090Sdougb tgt_ip.w[1] = 0; 1100224090Sdougb tgt_ip.w[2] = ADDR_V4MAPPED; 1101224090Sdougb tgt_ip.w[3] = ntohl(netaddr->type.in.s_addr); 1102224090Sdougb } else if (netaddr->family == AF_INET6) { 1103224090Sdougb dns_rpz_cidr_key_t src_ip6; 1104224090Sdougb 1105224090Sdougb /* 1106224090Sdougb * Given the int aligned struct in_addr member of netaddr->type 1107224090Sdougb * one could cast netaddr->type.in6 to dns_rpz_cidr_key_t *, 1108224090Sdougb * but there are objections. 1109224090Sdougb */ 1110224090Sdougb memcpy(src_ip6.w, &netaddr->type.in6, sizeof(src_ip6.w)); 1111224090Sdougb for (i = 0; i < 4; i++) { 1112224090Sdougb tgt_ip.w[i] = ntohl(src_ip6.w[i]); 1113224090Sdougb } 1114224090Sdougb } else { 1115224090Sdougb return (ISC_R_NOTFOUND); 1116224090Sdougb } 1117224090Sdougb 1118224090Sdougb result = search(cidr, &tgt_ip, 128, type, ISC_FALSE, &found); 1119224090Sdougb if (result != ISC_R_SUCCESS && result != DNS_R_PARTIALMATCH) 1120224090Sdougb return (result); 1121224090Sdougb 1122224090Sdougb *prefix = found->bits; 1123224090Sdougb return (ip2name(cidr, &found->ip, found->bits, type, 1124224090Sdougb canon_name, search_name)); 1125224090Sdougb} 1126224090Sdougb 1127224090Sdougb 1128224090Sdougb 1129224090Sdougb/* 1130224090Sdougb * Translate CNAME rdata to a QNAME response policy action. 1131224090Sdougb */ 1132224090Sdougbdns_rpz_policy_t 1133224090Sdougbdns_rpz_decode_cname(dns_rdataset_t *rdataset, dns_name_t *selfname) { 1134224090Sdougb dns_rdata_t rdata = DNS_RDATA_INIT; 1135224090Sdougb dns_rdata_cname_t cname; 1136224090Sdougb isc_result_t result; 1137224090Sdougb 1138224090Sdougb result = dns_rdataset_first(rdataset); 1139224090Sdougb RUNTIME_CHECK(result == ISC_R_SUCCESS); 1140224090Sdougb dns_rdataset_current(rdataset, &rdata); 1141224090Sdougb result = dns_rdata_tostruct(&rdata, &cname, NULL); 1142224090Sdougb RUNTIME_CHECK(result == ISC_R_SUCCESS); 1143224090Sdougb dns_rdata_reset(&rdata); 1144224090Sdougb 1145224090Sdougb /* 1146224090Sdougb * CNAME . means NXDOMAIN 1147224090Sdougb */ 1148224090Sdougb if (dns_name_equal(&cname.cname, dns_rootname)) 1149224090Sdougb return (DNS_RPZ_POLICY_NXDOMAIN); 1150224090Sdougb 1151224090Sdougb /* 1152224090Sdougb * CNAME *. means NODATA 1153224090Sdougb */ 1154224090Sdougb if (dns_name_countlabels(&cname.cname) == 2 1155224090Sdougb && dns_name_iswildcard(&cname.cname)) 1156224090Sdougb return (DNS_RPZ_POLICY_NODATA); 1157224090Sdougb 1158224090Sdougb /* 1159224090Sdougb * 128.1.0.127.rpz-ip CNAME 128.1.0.0.127. means "do not rewrite" 1160224090Sdougb */ 1161224090Sdougb if (selfname != NULL && dns_name_equal(&cname.cname, selfname)) 1162224090Sdougb return (DNS_RPZ_POLICY_NO_OP); 1163224090Sdougb 1164224090Sdougb /* 1165224090Sdougb * evil.com CNAME garden.net rewrites www.evil.com to www.garden.net. 1166224090Sdougb */ 1167224090Sdougb return (DNS_RPZ_POLICY_RECORD); 1168224090Sdougb} 1169