xref: /netbsd-src/external/mpl/bind/dist/lib/ns/sortlist.c (revision bcda20f65a8566e103791ec395f7f499ef322704)
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