1 /* $NetBSD: sortlist.c,v 1.9 2025/01/26 16:25:46 christos Exp $ */ 2 3 /* 4 * Copyright (C) Internet Systems Consortium, Inc. ("ISC") 5 * 6 * SPDX-License-Identifier: MPL-2.0 7 * 8 * This Source Code Form is subject to the terms of the Mozilla Public 9 * License, v. 2.0. If a copy of the MPL was not distributed with this 10 * file, you can obtain one at https://mozilla.org/MPL/2.0/. 11 * 12 * See the COPYRIGHT file distributed with this work for additional 13 * information regarding copyright ownership. 14 */ 15 16 /*! \file */ 17 18 #include <isc/mem.h> 19 #include <isc/result.h> 20 #include <isc/util.h> 21 22 #include <dns/acl.h> 23 #include <dns/message.h> 24 25 #include <ns/server.h> 26 #include <ns/sortlist.h> 27 28 ns_sortlisttype_t 29 ns_sortlist_setup(dns_acl_t *acl, dns_aclenv_t *env, isc_netaddr_t *clientaddr, 30 void **argp) { 31 if (acl == NULL) { 32 goto dont_sort; 33 } 34 35 for (size_t i = 0; i < acl->length; i++) { 36 /* 37 * 'e' refers to the current 'top level statement' 38 * in the sortlist (see ARM). 39 */ 40 dns_aclelement_t *e = &acl->elements[i]; 41 dns_aclelement_t *try_elt; 42 dns_aclelement_t *order_elt = NULL; 43 dns_aclelement_t *matched_elt = NULL; 44 45 if (e->type == dns_aclelementtype_nestedacl) { 46 dns_acl_t *inner = e->nestedacl; 47 48 if (inner->length == 0) { 49 try_elt = e; 50 } else if (inner->length > 2) { 51 goto dont_sort; 52 } else if (inner->elements[0].negative) { 53 goto dont_sort; 54 } else { 55 try_elt = &inner->elements[0]; 56 if (inner->length == 2) { 57 order_elt = &inner->elements[1]; 58 } 59 } 60 } else { 61 /* 62 * BIND 8 allows bare elements at the top level 63 * as an undocumented feature. 64 */ 65 try_elt = e; 66 } 67 68 if (!dns_aclelement_match( 69 clientaddr, NULL, try_elt, env, 70 (const dns_aclelement_t **)&matched_elt)) 71 { 72 continue; 73 } 74 75 if (order_elt == NULL) { 76 INSIST(matched_elt != NULL); 77 *argp = matched_elt; 78 return NS_SORTLISTTYPE_1ELEMENT; 79 } 80 81 if (order_elt->type == dns_aclelementtype_nestedacl) { 82 dns_acl_t *inner = NULL; 83 dns_acl_attach(order_elt->nestedacl, &inner); 84 *argp = inner; 85 return NS_SORTLISTTYPE_2ELEMENT; 86 } 87 88 if (order_elt->type == dns_aclelementtype_localhost) { 89 rcu_read_lock(); 90 dns_acl_t *inner = rcu_dereference(env->localhost); 91 if (inner != NULL) { 92 *argp = dns_acl_ref(inner); 93 rcu_read_unlock(); 94 return NS_SORTLISTTYPE_2ELEMENT; 95 } 96 rcu_read_unlock(); 97 } 98 99 if (order_elt->type == dns_aclelementtype_localnets) { 100 rcu_read_lock(); 101 dns_acl_t *inner = rcu_dereference(env->localhost); 102 if (inner != NULL) { 103 *argp = dns_acl_ref(inner); 104 rcu_read_unlock(); 105 return NS_SORTLISTTYPE_2ELEMENT; 106 } 107 rcu_read_unlock(); 108 } 109 110 /* 111 * BIND 8 allows a bare IP prefix as 112 * the 2nd element of a 2-element 113 * sortlist statement. 114 */ 115 *argp = order_elt; 116 return NS_SORTLISTTYPE_1ELEMENT; 117 } 118 119 dont_sort: 120 *argp = NULL; 121 return NS_SORTLISTTYPE_NONE; 122 } 123 124 int 125 ns_sortlist_addrorder2(const isc_netaddr_t *addr, const void *arg) { 126 const dns_sortlist_arg_t *sla = (const dns_sortlist_arg_t *)arg; 127 dns_aclenv_t *env = sla->env; 128 const dns_acl_t *sortacl = sla->acl; 129 int match; 130 131 (void)dns_acl_match(addr, NULL, sortacl, env, &match, NULL); 132 if (match > 0) { 133 return match; 134 } else if (match < 0) { 135 return INT_MAX - (-match); 136 } else { 137 return INT_MAX / 2; 138 } 139 } 140 141 int 142 ns_sortlist_addrorder1(const isc_netaddr_t *addr, const void *arg) { 143 const dns_sortlist_arg_t *sla = (const dns_sortlist_arg_t *)arg; 144 dns_aclenv_t *env = sla->env; 145 const dns_aclelement_t *element = sla->element; 146 147 if (dns_aclelement_match(addr, NULL, element, env, NULL)) { 148 return 0; 149 } 150 151 return INT_MAX; 152 } 153