1224090Sdougb/* 2262706Serwin * Copyright (C) 2011-2014 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 17234010Sdougb/* $Id$ */ 18224090Sdougb 19254402Serwin 20224090Sdougb/*! \file */ 21224090Sdougb 22224090Sdougb#include <config.h> 23224090Sdougb 24224090Sdougb#include <isc/buffer.h> 25224090Sdougb#include <isc/mem.h> 26224090Sdougb#include <isc/net.h> 27224090Sdougb#include <isc/netaddr.h> 28224090Sdougb#include <isc/print.h> 29224090Sdougb#include <isc/stdlib.h> 30224090Sdougb#include <isc/string.h> 31224090Sdougb#include <isc/util.h> 32224090Sdougb 33224090Sdougb#include <dns/db.h> 34224090Sdougb#include <dns/fixedname.h> 35224090Sdougb#include <dns/log.h> 36224090Sdougb#include <dns/rdata.h> 37224090Sdougb#include <dns/rdataset.h> 38224090Sdougb#include <dns/rdatastruct.h> 39224090Sdougb#include <dns/result.h> 40224090Sdougb#include <dns/rpz.h> 41224090Sdougb#include <dns/view.h> 42224090Sdougb 43224090Sdougb 44224090Sdougb/* 45224090Sdougb * Parallel radix trees for databases of response policy IP addresses 46224090Sdougb * 47224090Sdougb * The radix or Patricia trees are somewhat specialized to handle response 48224090Sdougb * policy addresses by representing the two test of IP IP addresses and name 49224090Sdougb * server IP addresses in a single tree. 50224090Sdougb * 51224090Sdougb * Each leaf indicates that an IP address is listed in the IP address or the 52224090Sdougb * name server IP address policy sub-zone (or both) of the corresponding 53224090Sdougb * response response zone. The policy data such as a CNAME or an A record 54224090Sdougb * is kept in the policy zone. After an IP address has been found in a radix 55224090Sdougb * tree, the node in the policy zone's database is found by converting 56224090Sdougb * the IP address to a domain name in a canonical form. 57224090Sdougb * 58224090Sdougb * The response policy zone canonical form of IPv6 addresses is one of: 59224090Sdougb * prefix.W.W.W.W.W.W.W.W 60224090Sdougb * prefix.WORDS.zz 61224090Sdougb * prefix.WORDS.zz.WORDS 62224090Sdougb * prefix.zz.WORDS 63224090Sdougb * where 64224090Sdougb * prefix is the prefix length of the IPv6 address between 1 and 128 65224090Sdougb * W is a number between 0 and 65535 66224090Sdougb * WORDS is one or more numbers W separated with "." 67224090Sdougb * zz corresponds to :: in the standard IPv6 text representation 68224090Sdougb * 69224090Sdougb * The canonical form of IPv4 addresses is: 70224090Sdougb * prefix.B.B.B.B 71224090Sdougb * where 72224090Sdougb * prefix is the prefix length of the address between 1 and 32 73224090Sdougb * B is a number between 0 and 255 74224090Sdougb * 75224090Sdougb * IPv4 addresses are distinguished from IPv6 addresses by having 76224090Sdougb * 5 labels all of which are numbers, and a prefix between 1 and 32. 77224090Sdougb */ 78224090Sdougb 79224090Sdougb 80224090Sdougb/* 81224090Sdougb * Use a private definition of IPv6 addresses because s6_addr32 is not 82224090Sdougb * always defined and our IPv6 addresses are in non-standard byte order 83224090Sdougb */ 84224090Sdougbtypedef isc_uint32_t dns_rpz_cidr_word_t; 85224090Sdougb#define DNS_RPZ_CIDR_WORD_BITS ((int)sizeof(dns_rpz_cidr_word_t)*8) 86224090Sdougb#define DNS_RPZ_CIDR_KEY_BITS ((int)sizeof(dns_rpz_cidr_key_t)*8) 87224090Sdougb#define DNS_RPZ_CIDR_WORDS (128/DNS_RPZ_CIDR_WORD_BITS) 88224090Sdougbtypedef struct { 89224090Sdougb dns_rpz_cidr_word_t w[DNS_RPZ_CIDR_WORDS]; 90224090Sdougb} dns_rpz_cidr_key_t; 91224090Sdougb 92224090Sdougb#define ADDR_V4MAPPED 0xffff 93224090Sdougb 94224090Sdougb#define DNS_RPZ_WORD_MASK(b) \ 95224090Sdougb ((b) == 0 ? (dns_rpz_cidr_word_t)(-1) \ 96224090Sdougb : ((dns_rpz_cidr_word_t)(-1) \ 97224090Sdougb << (DNS_RPZ_CIDR_WORD_BITS - (b)))) 98224090Sdougb 99224090Sdougb#define DNS_RPZ_IP_BIT(ip, bitno) \ 100224090Sdougb (1 & ((ip)->w[(bitno)/DNS_RPZ_CIDR_WORD_BITS] >> \ 101224090Sdougb (DNS_RPZ_CIDR_WORD_BITS - 1 - ((bitno) % DNS_RPZ_CIDR_WORD_BITS)))) 102224090Sdougb 103224090Sdougbtypedef struct dns_rpz_cidr_node dns_rpz_cidr_node_t; 104224090Sdougbtypedef isc_uint8_t dns_rpz_cidr_flags_t; 105224090Sdougbstruct dns_rpz_cidr_node { 106224090Sdougb dns_rpz_cidr_node_t *parent; 107224090Sdougb dns_rpz_cidr_node_t *child[2]; 108224090Sdougb dns_rpz_cidr_key_t ip; 109224090Sdougb dns_rpz_cidr_bits_t bits; 110224090Sdougb dns_rpz_cidr_flags_t flags; 111224090Sdougb#define DNS_RPZ_CIDR_FG_IP 0x01 /* has IP data or is parent of IP */ 112224090Sdougb#define DNS_RPZ_CIDR_FG_IP_DATA 0x02 /* has IP data */ 113224090Sdougb#define DNS_RPZ_CIDR_FG_NSIPv4 0x04 /* has or is parent of NSIPv4 data */ 114224090Sdougb#define DNS_RPZ_CIDR_FG_NSIPv6 0x08 /* has or is parent of NSIPv6 data */ 115224090Sdougb#define DNS_RPZ_CIDR_FG_NSIP_DATA 0x10 /* has NSIP data */ 116224090Sdougb}; 117224090Sdougb 118224090Sdougbstruct dns_rpz_cidr { 119224090Sdougb isc_mem_t *mctx; 120234010Sdougb isc_boolean_t have_nsdname; /* zone has NSDNAME record */ 121224090Sdougb dns_rpz_cidr_node_t *root; 122245163Serwin dns_name_t ip_name; /* RPZ_IP_ZONE.origin. */ 123245163Serwin dns_name_t nsip_name; /* RPZ_NSIP_ZONE.origin. */ 124245163Serwin dns_name_t nsdname_name; /* RPZ_NSDNAME_ZONE.origin */ 125224090Sdougb}; 126224090Sdougb 127224090Sdougbconst char * 128234010Sdougbdns_rpz_type2str(dns_rpz_type_t type) { 129224090Sdougb switch (type) { 130224090Sdougb case DNS_RPZ_TYPE_QNAME: 131224090Sdougb return ("QNAME"); 132224090Sdougb case DNS_RPZ_TYPE_IP: 133224090Sdougb return ("IP"); 134224090Sdougb case DNS_RPZ_TYPE_NSIP: 135224090Sdougb return ("NSIP"); 136224090Sdougb case DNS_RPZ_TYPE_NSDNAME: 137224090Sdougb return ("NSDNAME"); 138224090Sdougb case DNS_RPZ_TYPE_BAD: 139224090Sdougb break; 140224090Sdougb } 141224090Sdougb FATAL_ERROR(__FILE__, __LINE__, 142234010Sdougb "impossible rpz type %d", type); 143224090Sdougb return ("impossible"); 144224090Sdougb} 145224090Sdougb 146224090Sdougbdns_rpz_policy_t 147234010Sdougbdns_rpz_str2policy(const char *str) { 148224090Sdougb if (str == NULL) 149224090Sdougb return (DNS_RPZ_POLICY_ERROR); 150224090Sdougb if (!strcasecmp(str, "given")) 151224090Sdougb return (DNS_RPZ_POLICY_GIVEN); 152234010Sdougb if (!strcasecmp(str, "disabled")) 153234010Sdougb return (DNS_RPZ_POLICY_DISABLED); 154234010Sdougb if (!strcasecmp(str, "passthru")) 155234010Sdougb return (DNS_RPZ_POLICY_PASSTHRU); 156224090Sdougb if (!strcasecmp(str, "nxdomain")) 157224090Sdougb return (DNS_RPZ_POLICY_NXDOMAIN); 158224090Sdougb if (!strcasecmp(str, "nodata")) 159224090Sdougb return (DNS_RPZ_POLICY_NODATA); 160224090Sdougb if (!strcasecmp(str, "cname")) 161224090Sdougb return (DNS_RPZ_POLICY_CNAME); 162234010Sdougb /* 163234010Sdougb * Obsolete 164234010Sdougb */ 165234010Sdougb if (!strcasecmp(str, "no-op")) 166234010Sdougb return (DNS_RPZ_POLICY_PASSTHRU); 167224090Sdougb return (DNS_RPZ_POLICY_ERROR); 168224090Sdougb} 169224090Sdougb 170234010Sdougbconst char * 171234010Sdougbdns_rpz_policy2str(dns_rpz_policy_t policy) { 172234010Sdougb const char *str; 173224090Sdougb 174234010Sdougb switch (policy) { 175234010Sdougb case DNS_RPZ_POLICY_PASSTHRU: 176234010Sdougb str = "PASSTHRU"; 177234010Sdougb break; 178234010Sdougb case DNS_RPZ_POLICY_NXDOMAIN: 179234010Sdougb str = "NXDOMAIN"; 180234010Sdougb break; 181234010Sdougb case DNS_RPZ_POLICY_NODATA: 182234010Sdougb str = "NODATA"; 183234010Sdougb break; 184234010Sdougb case DNS_RPZ_POLICY_RECORD: 185245163Serwin str = "Local-Data"; 186234010Sdougb break; 187234010Sdougb case DNS_RPZ_POLICY_CNAME: 188234010Sdougb case DNS_RPZ_POLICY_WILDCNAME: 189234010Sdougb str = "CNAME"; 190234010Sdougb break; 191234010Sdougb default: 192234010Sdougb str = ""; 193254402Serwin POST(str); 194234010Sdougb INSIST(0); 195234010Sdougb } 196234010Sdougb return (str); 197234010Sdougb} 198224090Sdougb 199224090Sdougb/* 200224090Sdougb * Free the radix tree of a response policy database. 201224090Sdougb */ 202224090Sdougbvoid 203224090Sdougbdns_rpz_cidr_free(dns_rpz_cidr_t **cidrp) { 204224090Sdougb dns_rpz_cidr_node_t *cur, *child, *parent; 205224090Sdougb dns_rpz_cidr_t *cidr; 206224090Sdougb 207224090Sdougb REQUIRE(cidrp != NULL); 208224090Sdougb 209224090Sdougb cidr = *cidrp; 210224090Sdougb if (cidr == NULL) 211224090Sdougb return; 212224090Sdougb 213224090Sdougb cur = cidr->root; 214224090Sdougb while (cur != NULL) { 215224090Sdougb /* Depth first. */ 216224090Sdougb child = cur->child[0]; 217224090Sdougb if (child != NULL) { 218224090Sdougb cur = child; 219224090Sdougb continue; 220224090Sdougb } 221224090Sdougb child = cur->child[1]; 222224090Sdougb if (child != NULL) { 223224090Sdougb cur = child; 224224090Sdougb continue; 225224090Sdougb } 226224090Sdougb 227224090Sdougb /* Delete this leaf and go up. */ 228224090Sdougb parent = cur->parent; 229224090Sdougb if (parent == NULL) 230224090Sdougb cidr->root = NULL; 231224090Sdougb else 232224090Sdougb parent->child[parent->child[1] == cur] = NULL; 233224090Sdougb isc_mem_put(cidr->mctx, cur, sizeof(*cur)); 234224090Sdougb cur = parent; 235224090Sdougb } 236224090Sdougb 237224090Sdougb dns_name_free(&cidr->ip_name, cidr->mctx); 238224090Sdougb dns_name_free(&cidr->nsip_name, cidr->mctx); 239224090Sdougb dns_name_free(&cidr->nsdname_name, cidr->mctx); 240224090Sdougb isc_mem_put(cidr->mctx, cidr, sizeof(*cidr)); 241224090Sdougb *cidrp = NULL; 242224090Sdougb} 243224090Sdougb 244224090Sdougb/* 245224090Sdougb * Forget a view's list of policy zones. 246224090Sdougb */ 247224090Sdougbvoid 248224090Sdougbdns_rpz_view_destroy(dns_view_t *view) { 249224090Sdougb dns_rpz_zone_t *zone; 250224090Sdougb 251224090Sdougb REQUIRE(view != NULL); 252224090Sdougb 253224090Sdougb while (!ISC_LIST_EMPTY(view->rpz_zones)) { 254224090Sdougb zone = ISC_LIST_HEAD(view->rpz_zones); 255224090Sdougb ISC_LIST_UNLINK(view->rpz_zones, zone, link); 256224090Sdougb if (dns_name_dynamic(&zone->origin)) 257224090Sdougb dns_name_free(&zone->origin, view->mctx); 258245163Serwin if (dns_name_dynamic(&zone->passthru)) 259245163Serwin dns_name_free(&zone->passthru, view->mctx); 260224090Sdougb if (dns_name_dynamic(&zone->nsdname)) 261224090Sdougb dns_name_free(&zone->nsdname, view->mctx); 262224090Sdougb if (dns_name_dynamic(&zone->cname)) 263224090Sdougb dns_name_free(&zone->cname, view->mctx); 264224090Sdougb isc_mem_put(view->mctx, zone, sizeof(*zone)); 265224090Sdougb } 266224090Sdougb} 267224090Sdougb 268224090Sdougb/* 269224090Sdougb * Start a new radix tree for a response policy zone. 270224090Sdougb */ 271224090Sdougbisc_result_t 272224090Sdougbdns_rpz_new_cidr(isc_mem_t *mctx, dns_name_t *origin, 273224090Sdougb dns_rpz_cidr_t **rbtdb_cidr) 274224090Sdougb{ 275224090Sdougb isc_result_t result; 276224090Sdougb dns_rpz_cidr_t *cidr; 277224090Sdougb 278224090Sdougb REQUIRE(rbtdb_cidr != NULL && *rbtdb_cidr == NULL); 279224090Sdougb 280224090Sdougb cidr = isc_mem_get(mctx, sizeof(*cidr)); 281224090Sdougb if (cidr == NULL) 282224090Sdougb return (ISC_R_NOMEMORY); 283224090Sdougb memset(cidr, 0, sizeof(*cidr)); 284224090Sdougb cidr->mctx = mctx; 285224090Sdougb 286224090Sdougb dns_name_init(&cidr->ip_name, NULL); 287224090Sdougb result = dns_name_fromstring2(&cidr->ip_name, DNS_RPZ_IP_ZONE, origin, 288224090Sdougb DNS_NAME_DOWNCASE, mctx); 289224090Sdougb if (result != ISC_R_SUCCESS) { 290224090Sdougb isc_mem_put(mctx, cidr, sizeof(*cidr)); 291224090Sdougb return (result); 292224090Sdougb } 293224090Sdougb 294224090Sdougb dns_name_init(&cidr->nsip_name, NULL); 295224090Sdougb result = dns_name_fromstring2(&cidr->nsip_name, DNS_RPZ_NSIP_ZONE, 296224090Sdougb origin, DNS_NAME_DOWNCASE, mctx); 297224090Sdougb if (result != ISC_R_SUCCESS) { 298224090Sdougb dns_name_free(&cidr->ip_name, mctx); 299224090Sdougb isc_mem_put(mctx, cidr, sizeof(*cidr)); 300224090Sdougb return (result); 301224090Sdougb } 302224090Sdougb 303224090Sdougb dns_name_init(&cidr->nsdname_name, NULL); 304224090Sdougb result = dns_name_fromstring2(&cidr->nsdname_name, DNS_RPZ_NSDNAME_ZONE, 305224090Sdougb origin, DNS_NAME_DOWNCASE, mctx); 306224090Sdougb if (result != ISC_R_SUCCESS) { 307224090Sdougb dns_name_free(&cidr->nsip_name, mctx); 308224090Sdougb dns_name_free(&cidr->ip_name, mctx); 309224090Sdougb isc_mem_put(mctx, cidr, sizeof(*cidr)); 310224090Sdougb return (result); 311224090Sdougb } 312224090Sdougb 313224090Sdougb *rbtdb_cidr = cidr; 314224090Sdougb return (ISC_R_SUCCESS); 315224090Sdougb} 316224090Sdougb 317224090Sdougb/* 318224090Sdougb * See if a policy zone has IP, NSIP, or NSDNAME rules or records. 319224090Sdougb */ 320224090Sdougbvoid 321254402Serwindns_rpz_enabled_get(dns_rpz_cidr_t *cidr, dns_rpz_st_t *st) { 322234010Sdougb if (cidr == NULL) 323234010Sdougb return; 324224090Sdougb if (cidr->root != NULL && 325224090Sdougb (cidr->root->flags & DNS_RPZ_CIDR_FG_IP) != 0) 326224090Sdougb st->state |= DNS_RPZ_HAVE_IP; 327224090Sdougb if (cidr->root != NULL && 328224090Sdougb (cidr->root->flags & DNS_RPZ_CIDR_FG_NSIPv4) != 0) 329224090Sdougb st->state |= DNS_RPZ_HAVE_NSIPv4; 330224090Sdougb if (cidr->root != NULL && 331224090Sdougb (cidr->root->flags & DNS_RPZ_CIDR_FG_NSIPv6) != 0) 332224090Sdougb st->state |= DNS_RPZ_HAVE_NSIPv6; 333234010Sdougb if (cidr->have_nsdname) 334234010Sdougb st->state |= DNS_RPZ_HAVE_NSDNAME; 335224090Sdougb} 336224090Sdougb 337224090Sdougbstatic inline dns_rpz_cidr_flags_t 338224090Sdougbget_flags(const dns_rpz_cidr_key_t *ip, dns_rpz_cidr_bits_t prefix, 339224090Sdougb dns_rpz_type_t rpz_type) 340224090Sdougb{ 341224090Sdougb if (rpz_type == DNS_RPZ_TYPE_NSIP) { 342224090Sdougb if (prefix >= 96 && 343224090Sdougb ip->w[0] == 0 && ip->w[1] == 0 && 344224090Sdougb ip->w[2] == ADDR_V4MAPPED) 345224090Sdougb return (DNS_RPZ_CIDR_FG_NSIP_DATA | 346224090Sdougb DNS_RPZ_CIDR_FG_NSIPv4); 347224090Sdougb else 348224090Sdougb return (DNS_RPZ_CIDR_FG_NSIP_DATA | 349224090Sdougb DNS_RPZ_CIDR_FG_NSIPv6); 350224090Sdougb } else { 351224090Sdougb return (DNS_RPZ_CIDR_FG_IP | DNS_RPZ_CIDR_FG_IP_DATA); 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 * Make a radix tree node. 376224090Sdougb */ 377224090Sdougbstatic dns_rpz_cidr_node_t * 378224090Sdougbnew_node(dns_rpz_cidr_t *cidr, const dns_rpz_cidr_key_t *ip, 379224090Sdougb dns_rpz_cidr_bits_t bits, dns_rpz_cidr_flags_t flags) 380224090Sdougb{ 381224090Sdougb dns_rpz_cidr_node_t *node; 382224090Sdougb int i, words, wlen; 383224090Sdougb 384224090Sdougb node = isc_mem_get(cidr->mctx, sizeof(*node)); 385224090Sdougb if (node == NULL) 386224090Sdougb return (NULL); 387224090Sdougb memset(node, 0, sizeof(*node)); 388224090Sdougb 389224090Sdougb node->flags = flags & ~(DNS_RPZ_CIDR_FG_IP_DATA | 390224090Sdougb DNS_RPZ_CIDR_FG_NSIP_DATA); 391224090Sdougb 392224090Sdougb node->bits = bits; 393224090Sdougb words = bits / DNS_RPZ_CIDR_WORD_BITS; 394224090Sdougb wlen = bits % DNS_RPZ_CIDR_WORD_BITS; 395224090Sdougb i = 0; 396224090Sdougb while (i < words) { 397224090Sdougb node->ip.w[i] = ip->w[i]; 398224090Sdougb ++i; 399224090Sdougb } 400224090Sdougb if (wlen != 0) { 401224090Sdougb node->ip.w[i] = ip->w[i] & DNS_RPZ_WORD_MASK(wlen); 402224090Sdougb ++i; 403224090Sdougb } 404224090Sdougb while (i < DNS_RPZ_CIDR_WORDS) 405224090Sdougb node->ip.w[i++] = 0; 406224090Sdougb 407224090Sdougb return (node); 408224090Sdougb} 409224090Sdougb 410224090Sdougbstatic void 411245163Serwinbadname(int level, dns_name_t *name, const char *str1, const char *str2) { 412224090Sdougb char printname[DNS_NAME_FORMATSIZE]; 413224090Sdougb 414254402Serwin /* 415254402Serwin * bin/tests/system/rpz/tests.sh looks for "invalid rpz". 416254402Serwin */ 417245163Serwin if (level < DNS_RPZ_DEBUG_QUIET 418245163Serwin && isc_log_wouldlog(dns_lctx, level)) { 419224090Sdougb dns_name_format(name, printname, sizeof(printname)); 420234010Sdougb isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ, 421224090Sdougb DNS_LOGMODULE_RBTDB, level, 422245163Serwin "invalid rpz IP address \"%s\"%s%s", 423245163Serwin printname, str1, str2); 424224090Sdougb } 425224090Sdougb} 426224090Sdougb 427224090Sdougb/* 428224090Sdougb * Convert an IP address from radix tree binary (host byte order) to 429224090Sdougb * to its canonical response policy domain name and its name in the 430224090Sdougb * policy zone. 431224090Sdougb */ 432224090Sdougbstatic isc_result_t 433224090Sdougbip2name(dns_rpz_cidr_t *cidr, const dns_rpz_cidr_key_t *tgt_ip, 434224090Sdougb dns_rpz_cidr_bits_t tgt_prefix, dns_rpz_type_t type, 435224090Sdougb dns_name_t *canon_name, dns_name_t *search_name) 436224090Sdougb{ 437224090Sdougb#ifndef INET6_ADDRSTRLEN 438224090Sdougb#define INET6_ADDRSTRLEN 46 439224090Sdougb#endif 440224090Sdougb int w[DNS_RPZ_CIDR_WORDS*2]; 441224090Sdougb char str[1+8+1+INET6_ADDRSTRLEN+1]; 442224090Sdougb isc_buffer_t buffer; 443224090Sdougb dns_name_t *name; 444224090Sdougb isc_result_t result; 445224090Sdougb isc_boolean_t zeros; 446224090Sdougb int i, n, len; 447224090Sdougb 448224090Sdougb if (tgt_prefix > 96 && 449224090Sdougb tgt_ip->w[0] == 0 && 450224090Sdougb tgt_ip->w[1] == 0 && 451224090Sdougb tgt_ip->w[2] == ADDR_V4MAPPED) { 452224090Sdougb len = snprintf(str, sizeof(str), "%d.%d.%d.%d.%d", 453224090Sdougb tgt_prefix - 96, 454224090Sdougb tgt_ip->w[3] & 0xff, 455224090Sdougb (tgt_ip->w[3]>>8) & 0xff, 456224090Sdougb (tgt_ip->w[3]>>16) & 0xff, 457224090Sdougb (tgt_ip->w[3]>>24) & 0xff); 458224090Sdougb if (len == -1 || len > (int)sizeof(str)) 459224090Sdougb return (ISC_R_FAILURE); 460224090Sdougb } else { 461224090Sdougb for (i = 0; i < DNS_RPZ_CIDR_WORDS; i++) { 462224090Sdougb w[i*2+1] = ((tgt_ip->w[DNS_RPZ_CIDR_WORDS-1-i] >> 16) 463224090Sdougb & 0xffff); 464224090Sdougb w[i*2] = tgt_ip->w[DNS_RPZ_CIDR_WORDS-1-i] & 0xffff; 465224090Sdougb } 466224090Sdougb zeros = ISC_FALSE; 467224090Sdougb len = snprintf(str, sizeof(str), "%d", tgt_prefix); 468224090Sdougb if (len == -1) 469224090Sdougb return (ISC_R_FAILURE); 470224090Sdougb i = 0; 471224090Sdougb while (i < DNS_RPZ_CIDR_WORDS * 2) { 472224090Sdougb if (w[i] != 0 || zeros 473224090Sdougb || i >= DNS_RPZ_CIDR_WORDS * 2 - 1 474224090Sdougb || w[i+1] != 0) { 475224090Sdougb INSIST((size_t)len <= sizeof(str)); 476224090Sdougb n = snprintf(&str[len], sizeof(str) - len, 477224090Sdougb ".%x", w[i++]); 478224090Sdougb if (n < 0) 479224090Sdougb return (ISC_R_FAILURE); 480224090Sdougb len += n; 481224090Sdougb } else { 482224090Sdougb zeros = ISC_TRUE; 483224090Sdougb INSIST((size_t)len <= sizeof(str)); 484224090Sdougb n = snprintf(&str[len], sizeof(str) - len, 485224090Sdougb ".zz"); 486224090Sdougb if (n < 0) 487224090Sdougb return (ISC_R_FAILURE); 488224090Sdougb len += n; 489224090Sdougb i += 2; 490224090Sdougb while (i < DNS_RPZ_CIDR_WORDS * 2 && w[i] == 0) 491224090Sdougb ++i; 492224090Sdougb } 493254402Serwin if (len >= (int)sizeof(str)) 494224090Sdougb return (ISC_R_FAILURE); 495224090Sdougb } 496224090Sdougb } 497224090Sdougb 498224090Sdougb if (canon_name != NULL) { 499224090Sdougb isc__buffer_init(&buffer, str, sizeof(str)); 500224090Sdougb isc__buffer_add(&buffer, len); 501224090Sdougb result = dns_name_fromtext(canon_name, &buffer, 502224090Sdougb dns_rootname, 0, NULL); 503224090Sdougb if (result != ISC_R_SUCCESS) 504224090Sdougb return (result); 505224090Sdougb } 506224090Sdougb if (search_name != NULL) { 507224090Sdougb isc__buffer_init(&buffer, str, sizeof(str)); 508224090Sdougb isc__buffer_add(&buffer, len); 509224090Sdougb if (type == DNS_RPZ_TYPE_NSIP) 510224090Sdougb name = &cidr->nsip_name; 511224090Sdougb else 512224090Sdougb name = &cidr->ip_name; 513224090Sdougb result = dns_name_fromtext(search_name, &buffer, name, 0, NULL); 514224090Sdougb if (result != ISC_R_SUCCESS) 515224090Sdougb return (result); 516224090Sdougb } 517224090Sdougb return (ISC_R_SUCCESS); 518224090Sdougb} 519224090Sdougb 520224090Sdougb/* 521224090Sdougb * Decide which kind of IP address response policy zone a name is in. 522224090Sdougb */ 523224090Sdougbstatic dns_rpz_type_t 524224090Sdougbset_type(dns_rpz_cidr_t *cidr, dns_name_t *name) { 525224090Sdougb 526224090Sdougb if (dns_name_issubdomain(name, &cidr->ip_name)) 527224090Sdougb return (DNS_RPZ_TYPE_IP); 528224090Sdougb 529224090Sdougb /* 530224090Sdougb * Require `./configure --enable-rpz-nsip` and nsdname 531224090Sdougb * until consistency problems are resolved. 532224090Sdougb */ 533224090Sdougb#ifdef ENABLE_RPZ_NSIP 534224090Sdougb if (dns_name_issubdomain(name, &cidr->nsip_name)) 535224090Sdougb return (DNS_RPZ_TYPE_NSIP); 536224090Sdougb#endif 537224090Sdougb 538224090Sdougb#ifdef ENABLE_RPZ_NSDNAME 539224090Sdougb if (dns_name_issubdomain(name, &cidr->nsdname_name)) 540224090Sdougb return (DNS_RPZ_TYPE_NSDNAME); 541224090Sdougb#endif 542224090Sdougb 543224090Sdougb return (DNS_RPZ_TYPE_QNAME); 544224090Sdougb} 545224090Sdougb 546224090Sdougb/* 547224090Sdougb * Convert an IP address from canonical response policy domain name form 548224090Sdougb * to radix tree binary (host byte order). 549224090Sdougb */ 550224090Sdougbstatic isc_result_t 551224090Sdougbname2ipkey(dns_rpz_cidr_t *cidr, int level, dns_name_t *src_name, 552224090Sdougb dns_rpz_type_t type, dns_rpz_cidr_key_t *tgt_ip, 553224090Sdougb dns_rpz_cidr_bits_t *tgt_prefix) 554224090Sdougb{ 555245163Serwin isc_result_t result; 556224090Sdougb dns_fixedname_t fname; 557245163Serwin dns_name_t *ipname; 558245163Serwin char ipstr[DNS_NAME_FORMATSIZE]; 559245163Serwin const char *prefix_str, *cp, *end; 560224090Sdougb char *cp2; 561224090Sdougb int ip_labels; 562224090Sdougb dns_rpz_cidr_bits_t bits; 563224090Sdougb unsigned long prefix, l; 564224090Sdougb int i; 565224090Sdougb 566224090Sdougb /* 567224090Sdougb * Need at least enough labels for the shortest name, 568224090Sdougb * :: or 128.*.RPZ_x_ZONE.rpz.LOCALHOST. 569224090Sdougb */ 570224090Sdougb ip_labels = dns_name_countlabels(src_name); 571224090Sdougb ip_labels -= dns_name_countlabels(&cidr->ip_name); 572224090Sdougb ip_labels--; 573224090Sdougb if (ip_labels < 1) { 574245163Serwin badname(level, src_name, "; too short", ""); 575224090Sdougb return (ISC_R_FAILURE); 576224090Sdougb } 577224090Sdougb 578224090Sdougb /* 579245163Serwin * Get text for the IP address 580224090Sdougb */ 581224090Sdougb dns_fixedname_init(&fname); 582245163Serwin ipname = dns_fixedname_name(&fname); 583224090Sdougb dns_name_split(src_name, dns_name_countlabels(&cidr->ip_name), 584245163Serwin ipname, NULL); 585245163Serwin dns_name_format(ipname, ipstr, sizeof(ipstr)); 586245163Serwin end = &ipstr[strlen(ipstr)+1]; 587245163Serwin prefix_str = ipstr; 588224090Sdougb 589245163Serwin prefix = strtoul(prefix_str, &cp2, 10); 590245163Serwin if (*cp2 != '.') { 591245163Serwin badname(level, src_name, 592245163Serwin "; invalid leading prefix length", ""); 593224090Sdougb return (ISC_R_FAILURE); 594224090Sdougb } 595245163Serwin *cp2 = '\0'; 596245163Serwin if (prefix < 1U || prefix > 128U) { 597245163Serwin badname(level, src_name, 598245163Serwin "; invalid prefix length of ", prefix_str); 599245163Serwin return (ISC_R_FAILURE); 600245163Serwin } 601224090Sdougb cp = cp2+1; 602224090Sdougb 603224090Sdougb if (ip_labels == 4 && !strchr(cp, 'z')) { 604224090Sdougb /* 605224090Sdougb * Convert an IPv4 address 606224090Sdougb * from the form "prefix.w.z.y.x" 607224090Sdougb */ 608224090Sdougb if (prefix > 32U) { 609245163Serwin badname(level, src_name, 610245163Serwin "; invalid IPv4 prefix length of ", prefix_str); 611224090Sdougb return (ISC_R_FAILURE); 612224090Sdougb } 613224090Sdougb prefix += 96; 614224090Sdougb *tgt_prefix = (dns_rpz_cidr_bits_t)prefix; 615224090Sdougb tgt_ip->w[0] = 0; 616224090Sdougb tgt_ip->w[1] = 0; 617224090Sdougb tgt_ip->w[2] = ADDR_V4MAPPED; 618224090Sdougb tgt_ip->w[3] = 0; 619224090Sdougb for (i = 0; i < 32; i += 8) { 620224090Sdougb l = strtoul(cp, &cp2, 10); 621224090Sdougb if (l > 255U || (*cp2 != '.' && *cp2 != '\0')) { 622245163Serwin if (*cp2 == '.') 623245163Serwin *cp2 = '\0'; 624245163Serwin badname(level, src_name, 625245163Serwin "; invalid IPv4 octet ", cp); 626224090Sdougb return (ISC_R_FAILURE); 627224090Sdougb } 628224090Sdougb tgt_ip->w[3] |= l << i; 629224090Sdougb cp = cp2 + 1; 630224090Sdougb } 631224090Sdougb } else { 632224090Sdougb /* 633224090Sdougb * Convert a text IPv6 address. 634224090Sdougb */ 635224090Sdougb *tgt_prefix = (dns_rpz_cidr_bits_t)prefix; 636224090Sdougb for (i = 0; 637224090Sdougb ip_labels > 0 && i < DNS_RPZ_CIDR_WORDS * 2; 638224090Sdougb ip_labels--) { 639224090Sdougb if (cp[0] == 'z' && cp[1] == 'z' && 640224090Sdougb (cp[2] == '.' || cp[2] == '\0') && 641224090Sdougb i <= 6) { 642224090Sdougb do { 643224090Sdougb if ((i & 1) == 0) 644224090Sdougb tgt_ip->w[3-i/2] = 0; 645224090Sdougb ++i; 646224090Sdougb } while (ip_labels + i <= 8); 647224090Sdougb cp += 3; 648224090Sdougb } else { 649224090Sdougb l = strtoul(cp, &cp2, 16); 650224090Sdougb if (l > 0xffffu || 651224090Sdougb (*cp2 != '.' && *cp2 != '\0')) { 652245163Serwin if (*cp2 == '.') 653245163Serwin *cp2 = '\0'; 654245163Serwin badname(level, src_name, 655245163Serwin "; invalid IPv6 word ", cp); 656224090Sdougb return (ISC_R_FAILURE); 657224090Sdougb } 658224090Sdougb if ((i & 1) == 0) 659224090Sdougb tgt_ip->w[3-i/2] = l; 660224090Sdougb else 661224090Sdougb tgt_ip->w[3-i/2] |= l << 16; 662224090Sdougb i++; 663224090Sdougb cp = cp2 + 1; 664224090Sdougb } 665224090Sdougb } 666224090Sdougb } 667224090Sdougb if (cp != end) { 668245163Serwin badname(level, src_name, "", ""); 669224090Sdougb return (ISC_R_FAILURE); 670224090Sdougb } 671224090Sdougb 672224090Sdougb /* 673224090Sdougb * Check for 1s after the prefix length. 674224090Sdougb */ 675224090Sdougb bits = (dns_rpz_cidr_bits_t)prefix; 676224090Sdougb while (bits < DNS_RPZ_CIDR_KEY_BITS) { 677224090Sdougb dns_rpz_cidr_word_t aword; 678224090Sdougb 679224090Sdougb i = bits % DNS_RPZ_CIDR_WORD_BITS; 680224090Sdougb aword = tgt_ip->w[bits / DNS_RPZ_CIDR_WORD_BITS]; 681224090Sdougb if ((aword & ~DNS_RPZ_WORD_MASK(i)) != 0) { 682245163Serwin badname(level, src_name, 683245163Serwin "; too small prefix length of ", prefix_str); 684224090Sdougb return (ISC_R_FAILURE); 685224090Sdougb } 686224090Sdougb bits -= i; 687224090Sdougb bits += DNS_RPZ_CIDR_WORD_BITS; 688224090Sdougb } 689224090Sdougb 690224090Sdougb /* 691245163Serwin * Convert the address back to a canonical policy domain name 692224090Sdougb * to ensure that it is in canonical form. 693224090Sdougb */ 694245163Serwin result = ip2name(cidr, tgt_ip, (dns_rpz_cidr_bits_t) prefix, 695245163Serwin type, NULL, ipname); 696245163Serwin if (result != ISC_R_SUCCESS || !dns_name_equal(src_name, ipname)) { 697245163Serwin badname(level, src_name, "; not canonical", ""); 698224090Sdougb return (ISC_R_FAILURE); 699224090Sdougb } 700224090Sdougb 701224090Sdougb return (ISC_R_SUCCESS); 702224090Sdougb} 703224090Sdougb 704224090Sdougb/* 705234010Sdougb * Find first differing bit. 706224090Sdougb */ 707224090Sdougbstatic int 708224090Sdougbffbit(dns_rpz_cidr_word_t w) { 709224090Sdougb int bit; 710224090Sdougb 711234010Sdougb bit = DNS_RPZ_CIDR_WORD_BITS-1; 712234010Sdougb if ((w & 0xffff0000) != 0) { 713234010Sdougb w >>= 16; 714234010Sdougb bit -= 16; 715234010Sdougb } 716234010Sdougb if ((w & 0xff00) != 0) { 717234010Sdougb w >>= 8; 718234010Sdougb bit -= 8; 719234010Sdougb } 720234010Sdougb if ((w & 0xf0) != 0) { 721234010Sdougb w >>= 4; 722234010Sdougb bit -= 4; 723234010Sdougb } 724234010Sdougb if ((w & 0xc) != 0) { 725234010Sdougb w >>= 2; 726234010Sdougb bit -= 2; 727234010Sdougb } 728234010Sdougb if ((w & 2) != 0) 729234010Sdougb --bit; 730224090Sdougb return (bit); 731224090Sdougb} 732224090Sdougb 733224090Sdougb/* 734234010Sdougb * Find the first differing bit in two keys. 735224090Sdougb */ 736224090Sdougbstatic int 737224090Sdougbdiff_keys(const dns_rpz_cidr_key_t *key1, dns_rpz_cidr_bits_t bits1, 738224090Sdougb const dns_rpz_cidr_key_t *key2, dns_rpz_cidr_bits_t bits2) 739224090Sdougb{ 740224090Sdougb dns_rpz_cidr_word_t delta; 741224090Sdougb dns_rpz_cidr_bits_t maxbit, bit; 742224090Sdougb int i; 743224090Sdougb 744224090Sdougb maxbit = ISC_MIN(bits1, bits2); 745224090Sdougb 746224090Sdougb /* 747224090Sdougb * find the first differing words 748224090Sdougb */ 749224090Sdougb for (i = 0, bit = 0; 750224090Sdougb bit <= maxbit; 751224090Sdougb i++, bit += DNS_RPZ_CIDR_WORD_BITS) { 752224090Sdougb delta = key1->w[i] ^ key2->w[i]; 753224090Sdougb if (delta != 0) { 754224090Sdougb bit += ffbit(delta); 755224090Sdougb break; 756224090Sdougb } 757224090Sdougb } 758224090Sdougb return (ISC_MIN(bit, maxbit)); 759224090Sdougb} 760224090Sdougb 761224090Sdougb/* 762224090Sdougb * Search a radix tree for an IP address for ordinary lookup 763224090Sdougb * or for a CIDR block adding or deleting an entry 764224090Sdougb * The tree read (for simple search) or write lock must be held by the caller. 765224090Sdougb * 766234010Sdougb * Return ISC_R_SUCCESS, ISC_R_NOTFOUND, DNS_R_PARTIALMATCH, ISC_R_EXISTS, 767224090Sdougb * ISC_R_NOMEMORY 768224090Sdougb */ 769224090Sdougbstatic isc_result_t 770224090Sdougbsearch(dns_rpz_cidr_t *cidr, const dns_rpz_cidr_key_t *tgt_ip, 771224090Sdougb dns_rpz_cidr_bits_t tgt_prefix, dns_rpz_type_t type, 772224090Sdougb isc_boolean_t create, 773224090Sdougb dns_rpz_cidr_node_t **found) /* NULL or longest match node */ 774224090Sdougb{ 775224090Sdougb dns_rpz_cidr_node_t *cur, *parent, *child, *new_parent, *sibling; 776224090Sdougb int cur_num, child_num; 777224090Sdougb dns_rpz_cidr_bits_t dbit; 778224090Sdougb dns_rpz_cidr_flags_t flags, data_flag; 779224090Sdougb isc_result_t find_result; 780224090Sdougb 781224090Sdougb flags = get_flags(tgt_ip, tgt_prefix, type); 782224090Sdougb data_flag = flags & (DNS_RPZ_CIDR_FG_IP_DATA | 783224090Sdougb DNS_RPZ_CIDR_FG_NSIP_DATA); 784224090Sdougb 785224090Sdougb find_result = ISC_R_NOTFOUND; 786224090Sdougb if (found != NULL) 787224090Sdougb *found = NULL; 788224090Sdougb cur = cidr->root; 789224090Sdougb parent = NULL; 790224090Sdougb cur_num = 0; 791224090Sdougb for (;;) { 792224090Sdougb if (cur == NULL) { 793224090Sdougb /* 794224090Sdougb * No child so we cannot go down. Fail or 795224090Sdougb * add the target as a child of the current parent. 796224090Sdougb */ 797224090Sdougb if (!create) 798224090Sdougb return (find_result); 799224090Sdougb child = new_node(cidr, tgt_ip, tgt_prefix, 0); 800224090Sdougb if (child == NULL) 801224090Sdougb return (ISC_R_NOMEMORY); 802224090Sdougb if (parent == NULL) 803224090Sdougb cidr->root = child; 804224090Sdougb else 805224090Sdougb parent->child[cur_num] = child; 806224090Sdougb child->parent = parent; 807224090Sdougb set_node_flags(child, type); 808224090Sdougb if (found != NULL) 809224090Sdougb *found = cur; 810224090Sdougb return (ISC_R_SUCCESS); 811224090Sdougb } 812224090Sdougb 813224090Sdougb /* 814224090Sdougb * Pretend a node not in the correct tree does not exist 815224090Sdougb * if we are not adding to the tree, 816224090Sdougb * If we are adding, then continue down to eventually 817224090Sdougb * add a node and mark/put this node in the correct tree. 818224090Sdougb */ 819224090Sdougb if ((cur->flags & flags) == 0 && !create) 820224090Sdougb return (find_result); 821224090Sdougb 822224090Sdougb dbit = diff_keys(tgt_ip, tgt_prefix, &cur->ip, cur->bits); 823224090Sdougb /* 824224090Sdougb * dbit <= tgt_prefix and dbit <= cur->bits always. 825224090Sdougb * We are finished searching if we matched all of the target. 826224090Sdougb */ 827224090Sdougb if (dbit == tgt_prefix) { 828224090Sdougb if (tgt_prefix == cur->bits) { 829224090Sdougb /* 830224090Sdougb * The current node matches the target exactly. 831224090Sdougb * It is the answer if it has data. 832224090Sdougb */ 833224090Sdougb if ((cur->flags & data_flag) != 0) { 834224090Sdougb if (create) 835224090Sdougb return (ISC_R_EXISTS); 836224090Sdougb if (found != NULL) 837224090Sdougb *found = cur; 838224090Sdougb return (ISC_R_SUCCESS); 839224090Sdougb } else if (create) { 840224090Sdougb /* 841224090Sdougb * The node had no data but does now. 842224090Sdougb */ 843224090Sdougb set_node_flags(cur, type); 844224090Sdougb if (found != NULL) 845224090Sdougb *found = cur; 846224090Sdougb return (ISC_R_SUCCESS); 847224090Sdougb } 848224090Sdougb return (find_result); 849224090Sdougb } 850224090Sdougb 851224090Sdougb /* 852224090Sdougb * We know tgt_prefix < cur_bits which means that 853224090Sdougb * the target is shorter than the current node. 854224090Sdougb * Add the target as the current node's parent. 855224090Sdougb */ 856224090Sdougb if (!create) 857224090Sdougb return (find_result); 858224090Sdougb 859224090Sdougb new_parent = new_node(cidr, tgt_ip, tgt_prefix, 860224090Sdougb cur->flags); 861224090Sdougb if (new_parent == NULL) 862224090Sdougb return (ISC_R_NOMEMORY); 863224090Sdougb new_parent->parent = parent; 864224090Sdougb if (parent == NULL) 865224090Sdougb cidr->root = new_parent; 866224090Sdougb else 867224090Sdougb parent->child[cur_num] = new_parent; 868224090Sdougb child_num = DNS_RPZ_IP_BIT(&cur->ip, tgt_prefix+1); 869224090Sdougb new_parent->child[child_num] = cur; 870224090Sdougb cur->parent = new_parent; 871224090Sdougb set_node_flags(new_parent, type); 872224090Sdougb if (found != NULL) 873224090Sdougb *found = new_parent; 874224090Sdougb return (ISC_R_SUCCESS); 875224090Sdougb } 876224090Sdougb 877224090Sdougb if (dbit == cur->bits) { 878224090Sdougb /* 879224090Sdougb * We have a partial match by matching of all of the 880224090Sdougb * current node but only part of the target. 881224090Sdougb * Try to go down. 882224090Sdougb */ 883224090Sdougb if ((cur->flags & data_flag) != 0) { 884224090Sdougb find_result = DNS_R_PARTIALMATCH; 885224090Sdougb if (found != NULL) 886224090Sdougb *found = cur; 887224090Sdougb } 888224090Sdougb 889224090Sdougb parent = cur; 890224090Sdougb cur_num = DNS_RPZ_IP_BIT(tgt_ip, dbit); 891224090Sdougb cur = cur->child[cur_num]; 892224090Sdougb continue; 893224090Sdougb } 894224090Sdougb 895224090Sdougb 896224090Sdougb /* 897224090Sdougb * dbit < tgt_prefix and dbit < cur->bits, 898224090Sdougb * so we failed to match both the target and the current node. 899224090Sdougb * Insert a fork of a parent above the current node and 900224090Sdougb * add the target as a sibling of the current node 901224090Sdougb */ 902224090Sdougb if (!create) 903224090Sdougb return (find_result); 904224090Sdougb 905224090Sdougb sibling = new_node(cidr, tgt_ip, tgt_prefix, 0); 906224090Sdougb if (sibling == NULL) 907224090Sdougb return (ISC_R_NOMEMORY); 908224090Sdougb new_parent = new_node(cidr, tgt_ip, dbit, cur->flags); 909224090Sdougb if (new_parent == NULL) { 910224090Sdougb isc_mem_put(cidr->mctx, sibling, sizeof(*sibling)); 911224090Sdougb return (ISC_R_NOMEMORY); 912224090Sdougb } 913224090Sdougb new_parent->parent = parent; 914224090Sdougb if (parent == NULL) 915224090Sdougb cidr->root = new_parent; 916224090Sdougb else 917224090Sdougb parent->child[cur_num] = new_parent; 918224090Sdougb child_num = DNS_RPZ_IP_BIT(tgt_ip, dbit); 919224090Sdougb new_parent->child[child_num] = sibling; 920224090Sdougb new_parent->child[1-child_num] = cur; 921224090Sdougb cur->parent = new_parent; 922224090Sdougb sibling->parent = new_parent; 923224090Sdougb set_node_flags(sibling, type); 924224090Sdougb if (found != NULL) 925224090Sdougb *found = sibling; 926224090Sdougb return (ISC_R_SUCCESS); 927224090Sdougb } 928224090Sdougb} 929224090Sdougb 930224090Sdougb/* 931224090Sdougb * Add an IP address to the radix tree of a response policy database. 932224090Sdougb * The tree write lock must be held by the caller. 933224090Sdougb */ 934224090Sdougbvoid 935234010Sdougbdns_rpz_cidr_addip(dns_rpz_cidr_t *cidr, dns_name_t *name) { 936245163Serwin isc_result_t result; 937224090Sdougb dns_rpz_cidr_key_t tgt_ip; 938224090Sdougb dns_rpz_cidr_bits_t tgt_prefix; 939224090Sdougb dns_rpz_type_t type; 940224090Sdougb 941254402Serwin REQUIRE(cidr != NULL); 942224090Sdougb 943224090Sdougb /* 944234010Sdougb * No worries if the new name is not an IP address. 945224090Sdougb */ 946224090Sdougb type = set_type(cidr, name); 947224090Sdougb switch (type) { 948224090Sdougb case DNS_RPZ_TYPE_IP: 949224090Sdougb case DNS_RPZ_TYPE_NSIP: 950224090Sdougb break; 951224090Sdougb case DNS_RPZ_TYPE_NSDNAME: 952234010Sdougb cidr->have_nsdname = ISC_TRUE; 953224090Sdougb return; 954224090Sdougb case DNS_RPZ_TYPE_QNAME: 955224090Sdougb case DNS_RPZ_TYPE_BAD: 956224090Sdougb return; 957224090Sdougb } 958245163Serwin result = name2ipkey(cidr, DNS_RPZ_ERROR_LEVEL, name, 959245163Serwin type, &tgt_ip, &tgt_prefix); 960245163Serwin if (result != ISC_R_SUCCESS) 961224090Sdougb return; 962224090Sdougb 963245163Serwin result = search(cidr, &tgt_ip, tgt_prefix, type, ISC_TRUE, NULL); 964245163Serwin if (result == ISC_R_EXISTS && 965245163Serwin isc_log_wouldlog(dns_lctx, DNS_RPZ_ERROR_LEVEL)) 966245163Serwin { 967224090Sdougb char printname[DNS_NAME_FORMATSIZE]; 968224090Sdougb 969254402Serwin /* 970254402Serwin * bin/tests/system/rpz/tests.sh looks for "rpz.*failed". 971254402Serwin */ 972224090Sdougb dns_name_format(name, printname, sizeof(printname)); 973234010Sdougb isc_log_write(dns_lctx, DNS_LOGCATEGORY_RPZ, 974224090Sdougb DNS_LOGMODULE_RBTDB, DNS_RPZ_ERROR_LEVEL, 975245163Serwin "rpz add failed; \"%s\" is a duplicate name", 976245163Serwin printname); 977224090Sdougb } 978224090Sdougb} 979224090Sdougb 980224090Sdougb/* 981224090Sdougb * Delete an IP address from the radix tree of a response policy database. 982224090Sdougb * The tree write lock must be held by the caller. 983224090Sdougb */ 984224090Sdougbvoid 985224090Sdougbdns_rpz_cidr_deleteip(dns_rpz_cidr_t *cidr, dns_name_t *name) { 986245163Serwin isc_result_t result; 987224090Sdougb dns_rpz_cidr_key_t tgt_ip; 988224090Sdougb dns_rpz_cidr_bits_t tgt_prefix; 989224090Sdougb dns_rpz_type_t type; 990224090Sdougb dns_rpz_cidr_node_t *tgt = NULL, *parent, *child; 991224090Sdougb dns_rpz_cidr_flags_t flags, data_flag; 992224090Sdougb 993224090Sdougb if (cidr == NULL) 994224090Sdougb return; 995224090Sdougb 996224090Sdougb /* 997224090Sdougb * Decide which kind of policy zone IP address it is, if either 998224090Sdougb * and then find its node. 999224090Sdougb */ 1000224090Sdougb type = set_type(cidr, name); 1001224090Sdougb switch (type) { 1002224090Sdougb case DNS_RPZ_TYPE_IP: 1003224090Sdougb case DNS_RPZ_TYPE_NSIP: 1004224090Sdougb break; 1005224090Sdougb case DNS_RPZ_TYPE_NSDNAME: 1006224090Sdougb /* 1007224090Sdougb * We cannot easily count nsdnames because 1008224090Sdougb * internal rbt nodes get deleted. 1009224090Sdougb */ 1010224090Sdougb return; 1011224090Sdougb case DNS_RPZ_TYPE_QNAME: 1012224090Sdougb case DNS_RPZ_TYPE_BAD: 1013224090Sdougb return; 1014224090Sdougb } 1015224090Sdougb 1016224090Sdougb /* 1017224090Sdougb * Do not get excited about the deletion of interior rbt nodes. 1018224090Sdougb */ 1019245163Serwin result = name2ipkey(cidr, DNS_RPZ_DEBUG_QUIET, name, 1020245163Serwin type, &tgt_ip, &tgt_prefix); 1021245163Serwin if (result != ISC_R_SUCCESS) 1022224090Sdougb return; 1023224090Sdougb 1024245163Serwin result = search(cidr, &tgt_ip, tgt_prefix, type, ISC_FALSE, &tgt); 1025245163Serwin if (result != ISC_R_SUCCESS) { 1026245163Serwin badname(DNS_RPZ_ERROR_LEVEL, name, "; missing rpz node", ""); 1027224090Sdougb return; 1028224090Sdougb } 1029224090Sdougb 1030224090Sdougb /* 1031224090Sdougb * Mark the node and its parents to reflect the deleted IP address. 1032224090Sdougb */ 1033224090Sdougb flags = get_flags(&tgt_ip, tgt_prefix, type); 1034224090Sdougb data_flag = flags & (DNS_RPZ_CIDR_FG_IP_DATA | 1035224090Sdougb DNS_RPZ_CIDR_FG_NSIP_DATA); 1036224090Sdougb tgt->flags &= ~data_flag; 1037224090Sdougb for (parent = tgt; parent != NULL; parent = parent->parent) { 1038224090Sdougb if ((parent->flags & data_flag) != 0 || 1039224090Sdougb (parent->child[0] != NULL && 1040224090Sdougb (parent->child[0]->flags & flags) != 0) || 1041224090Sdougb (parent->child[1] != NULL && 1042224090Sdougb (parent->child[1]->flags & flags) != 0)) 1043224090Sdougb break; 1044224090Sdougb parent->flags &= ~flags; 1045224090Sdougb } 1046224090Sdougb 1047224090Sdougb /* 1048224090Sdougb * We might need to delete 2 nodes. 1049224090Sdougb */ 1050224090Sdougb do { 1051224090Sdougb /* 1052224090Sdougb * The node is now useless if it has no data of its own 1053224090Sdougb * and 0 or 1 children. We are finished if it is not useless. 1054224090Sdougb */ 1055224090Sdougb if ((child = tgt->child[0]) != NULL) { 1056224090Sdougb if (tgt->child[1] != NULL) 1057224090Sdougb return; 1058224090Sdougb } else { 1059224090Sdougb child = tgt->child[1]; 1060224090Sdougb } 1061224090Sdougb if ((tgt->flags & (DNS_RPZ_CIDR_FG_IP_DATA | 1062224090Sdougb DNS_RPZ_CIDR_FG_NSIP_DATA)) != 0) 1063224090Sdougb return; 1064224090Sdougb 1065224090Sdougb /* 1066224090Sdougb * Replace the pointer to this node in the parent with 1067224090Sdougb * the remaining child or NULL. 1068224090Sdougb */ 1069224090Sdougb parent = tgt->parent; 1070224090Sdougb if (parent == NULL) { 1071224090Sdougb cidr->root = child; 1072224090Sdougb } else { 1073224090Sdougb parent->child[parent->child[1] == tgt] = child; 1074224090Sdougb } 1075224090Sdougb /* 1076224090Sdougb * If the child exists fix up its parent pointer. 1077224090Sdougb */ 1078224090Sdougb if (child != NULL) 1079224090Sdougb child->parent = parent; 1080224090Sdougb isc_mem_put(cidr->mctx, tgt, sizeof(*tgt)); 1081224090Sdougb 1082224090Sdougb tgt = parent; 1083224090Sdougb } while (tgt != NULL); 1084224090Sdougb} 1085224090Sdougb 1086224090Sdougb/* 1087224090Sdougb * Caller must hold tree lock. 1088224090Sdougb * Return ISC_R_NOTFOUND 1089224090Sdougb * or ISC_R_SUCCESS and the found entry's canonical and search names 1090224090Sdougb * and its prefix length 1091224090Sdougb */ 1092224090Sdougbisc_result_t 1093224090Sdougbdns_rpz_cidr_find(dns_rpz_cidr_t *cidr, const isc_netaddr_t *netaddr, 1094224090Sdougb dns_rpz_type_t type, dns_name_t *canon_name, 1095224090Sdougb dns_name_t *search_name, dns_rpz_cidr_bits_t *prefix) 1096224090Sdougb{ 1097224090Sdougb dns_rpz_cidr_key_t tgt_ip; 1098224090Sdougb isc_result_t result; 1099224090Sdougb dns_rpz_cidr_node_t *found; 1100224090Sdougb int i; 1101224090Sdougb 1102224090Sdougb /* 1103224090Sdougb * Convert IP address to CIDR tree key. 1104224090Sdougb */ 1105224090Sdougb if (netaddr->family == AF_INET) { 1106224090Sdougb tgt_ip.w[0] = 0; 1107224090Sdougb tgt_ip.w[1] = 0; 1108224090Sdougb tgt_ip.w[2] = ADDR_V4MAPPED; 1109224090Sdougb tgt_ip.w[3] = ntohl(netaddr->type.in.s_addr); 1110224090Sdougb } else if (netaddr->family == AF_INET6) { 1111224090Sdougb dns_rpz_cidr_key_t src_ip6; 1112224090Sdougb 1113224090Sdougb /* 1114224090Sdougb * Given the int aligned struct in_addr member of netaddr->type 1115224090Sdougb * one could cast netaddr->type.in6 to dns_rpz_cidr_key_t *, 1116224090Sdougb * but there are objections. 1117224090Sdougb */ 1118262706Serwin memmove(src_ip6.w, &netaddr->type.in6, sizeof(src_ip6.w)); 1119224090Sdougb for (i = 0; i < 4; i++) { 1120224090Sdougb tgt_ip.w[i] = ntohl(src_ip6.w[i]); 1121224090Sdougb } 1122224090Sdougb } else { 1123224090Sdougb return (ISC_R_NOTFOUND); 1124224090Sdougb } 1125224090Sdougb 1126224090Sdougb result = search(cidr, &tgt_ip, 128, type, ISC_FALSE, &found); 1127224090Sdougb if (result != ISC_R_SUCCESS && result != DNS_R_PARTIALMATCH) 1128224090Sdougb return (result); 1129224090Sdougb 1130224090Sdougb *prefix = found->bits; 1131224090Sdougb return (ip2name(cidr, &found->ip, found->bits, type, 1132224090Sdougb canon_name, search_name)); 1133224090Sdougb} 1134224090Sdougb 1135224090Sdougb/* 1136224090Sdougb * Translate CNAME rdata to a QNAME response policy action. 1137224090Sdougb */ 1138224090Sdougbdns_rpz_policy_t 1139245163Serwindns_rpz_decode_cname(dns_rpz_zone_t *rpz, dns_rdataset_t *rdataset, 1140245163Serwin dns_name_t *selfname) 1141245163Serwin{ 1142224090Sdougb dns_rdata_t rdata = DNS_RDATA_INIT; 1143224090Sdougb dns_rdata_cname_t cname; 1144224090Sdougb isc_result_t result; 1145224090Sdougb 1146224090Sdougb result = dns_rdataset_first(rdataset); 1147224090Sdougb RUNTIME_CHECK(result == ISC_R_SUCCESS); 1148224090Sdougb dns_rdataset_current(rdataset, &rdata); 1149224090Sdougb result = dns_rdata_tostruct(&rdata, &cname, NULL); 1150224090Sdougb RUNTIME_CHECK(result == ISC_R_SUCCESS); 1151224090Sdougb dns_rdata_reset(&rdata); 1152224090Sdougb 1153224090Sdougb /* 1154224090Sdougb * CNAME . means NXDOMAIN 1155224090Sdougb */ 1156224090Sdougb if (dns_name_equal(&cname.cname, dns_rootname)) 1157224090Sdougb return (DNS_RPZ_POLICY_NXDOMAIN); 1158224090Sdougb 1159234010Sdougb if (dns_name_iswildcard(&cname.cname)) { 1160234010Sdougb /* 1161234010Sdougb * CNAME *. means NODATA 1162234010Sdougb */ 1163234010Sdougb if (dns_name_countlabels(&cname.cname) == 2) 1164234010Sdougb return (DNS_RPZ_POLICY_NODATA); 1165224090Sdougb 1166234010Sdougb /* 1167234010Sdougb * A qname of www.evil.com and a policy of 1168234010Sdougb * *.evil.com CNAME *.garden.net 1169234010Sdougb * gives a result of 1170234010Sdougb * evil.com CNAME evil.com.garden.net 1171234010Sdougb */ 1172234010Sdougb if (dns_name_countlabels(&cname.cname) > 2) 1173234010Sdougb return (DNS_RPZ_POLICY_WILDCNAME); 1174234010Sdougb } 1175234010Sdougb 1176224090Sdougb /* 1177245163Serwin * CNAME PASSTHRU.origin means "do not rewrite. 1178224090Sdougb */ 1179245163Serwin if (dns_name_equal(&cname.cname, &rpz->passthru)) 1180245163Serwin return (DNS_RPZ_POLICY_PASSTHRU); 1181245163Serwin 1182245163Serwin /* 1183245163Serwin * 128.1.0.127.rpz-ip CNAME 128.1.0.0.127. is obsolete PASSTHRU 1184245163Serwin */ 1185224090Sdougb if (selfname != NULL && dns_name_equal(&cname.cname, selfname)) 1186234010Sdougb return (DNS_RPZ_POLICY_PASSTHRU); 1187224090Sdougb 1188224090Sdougb /* 1189234010Sdougb * Any other rdata gives a response consisting of the rdata. 1190224090Sdougb */ 1191224090Sdougb return (DNS_RPZ_POLICY_RECORD); 1192224090Sdougb} 1193