1 /* $NetBSD: forward.c,v 1.10 2025/01/26 16:25:22 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/magic.h> 19 #include <isc/mem.h> 20 #include <isc/result.h> 21 #include <isc/util.h> 22 23 #include <dns/fixedname.h> 24 #include <dns/forward.h> 25 #include <dns/name.h> 26 #include <dns/qp.h> 27 #include <dns/types.h> 28 #include <dns/view.h> 29 30 struct dns_fwdtable { 31 /* Unlocked. */ 32 unsigned int magic; 33 isc_mem_t *mctx; 34 dns_qpmulti_t *table; 35 }; 36 37 #define FWDTABLEMAGIC ISC_MAGIC('F', 'w', 'd', 'T') 38 #define VALID_FWDTABLE(ft) ISC_MAGIC_VALID(ft, FWDTABLEMAGIC) 39 40 static void 41 qp_attach(void *uctx, void *pval, uint32_t ival); 42 static void 43 qp_detach(void *uctx, void *pval, uint32_t ival); 44 static size_t 45 qp_makekey(dns_qpkey_t key, void *uctx, void *pval, uint32_t ival); 46 static void 47 qp_triename(void *uctx, char *buf, size_t size); 48 49 static dns_qpmethods_t qpmethods = { 50 qp_attach, 51 qp_detach, 52 qp_makekey, 53 qp_triename, 54 }; 55 56 void 57 dns_fwdtable_create(isc_mem_t *mctx, dns_view_t *view, 58 dns_fwdtable_t **fwdtablep) { 59 dns_fwdtable_t *fwdtable = NULL; 60 61 REQUIRE(fwdtablep != NULL && *fwdtablep == NULL); 62 63 fwdtable = isc_mem_get(mctx, sizeof(*fwdtable)); 64 *fwdtable = (dns_fwdtable_t){ .magic = FWDTABLEMAGIC }; 65 66 dns_qpmulti_create(mctx, &qpmethods, view, &fwdtable->table); 67 68 isc_mem_attach(mctx, &fwdtable->mctx); 69 *fwdtablep = fwdtable; 70 } 71 72 static dns_forwarders_t * 73 new_forwarders(isc_mem_t *mctx, const dns_name_t *name, 74 dns_fwdpolicy_t fwdpolicy) { 75 dns_forwarders_t *forwarders = NULL; 76 77 forwarders = isc_mem_get(mctx, sizeof(*forwarders)); 78 *forwarders = (dns_forwarders_t){ 79 .fwdpolicy = fwdpolicy, 80 .name = DNS_NAME_INITEMPTY, 81 .fwdrs = ISC_LIST_INITIALIZER, 82 }; 83 isc_mem_attach(mctx, &forwarders->mctx); 84 isc_refcount_init(&forwarders->references, 1); 85 86 dns_name_dupwithoffsets(name, mctx, &forwarders->name); 87 88 return forwarders; 89 } 90 91 isc_result_t 92 dns_fwdtable_addfwd(dns_fwdtable_t *fwdtable, const dns_name_t *name, 93 dns_forwarderlist_t *fwdrs, dns_fwdpolicy_t fwdpolicy) { 94 isc_result_t result; 95 dns_forwarders_t *forwarders = NULL; 96 dns_forwarder_t *fwd = NULL, *nfwd = NULL; 97 dns_qp_t *qp = NULL; 98 99 REQUIRE(VALID_FWDTABLE(fwdtable)); 100 101 forwarders = new_forwarders(fwdtable->mctx, name, fwdpolicy); 102 103 for (fwd = ISC_LIST_HEAD(*fwdrs); fwd != NULL; 104 fwd = ISC_LIST_NEXT(fwd, link)) 105 { 106 nfwd = isc_mem_get(fwdtable->mctx, sizeof(*nfwd)); 107 *nfwd = *fwd; 108 109 if (fwd->tlsname != NULL) { 110 nfwd->tlsname = isc_mem_get(fwdtable->mctx, 111 sizeof(*nfwd->tlsname)); 112 dns_name_init(nfwd->tlsname, NULL); 113 dns_name_dup(fwd->tlsname, fwdtable->mctx, 114 nfwd->tlsname); 115 } 116 117 ISC_LINK_INIT(nfwd, link); 118 ISC_LIST_APPEND(forwarders->fwdrs, nfwd, link); 119 } 120 121 dns_qpmulti_write(fwdtable->table, &qp); 122 result = dns_qp_insert(qp, forwarders, 0); 123 dns_qp_compact(qp, DNS_QPGC_MAYBE); 124 dns_qpmulti_commit(fwdtable->table, &qp); 125 126 dns_forwarders_detach(&forwarders); 127 128 return result; 129 } 130 131 isc_result_t 132 dns_fwdtable_add(dns_fwdtable_t *fwdtable, const dns_name_t *name, 133 isc_sockaddrlist_t *addrs, dns_fwdpolicy_t fwdpolicy) { 134 isc_result_t result; 135 dns_forwarders_t *forwarders = NULL; 136 dns_forwarder_t *fwd = NULL; 137 isc_sockaddr_t *sa = NULL; 138 dns_qp_t *qp = NULL; 139 140 REQUIRE(VALID_FWDTABLE(fwdtable)); 141 142 forwarders = new_forwarders(fwdtable->mctx, name, fwdpolicy); 143 144 for (sa = ISC_LIST_HEAD(*addrs); sa != NULL; 145 sa = ISC_LIST_NEXT(sa, link)) 146 { 147 fwd = isc_mem_get(fwdtable->mctx, sizeof(*fwd)); 148 *fwd = (dns_forwarder_t){ .addr = *sa, 149 .link = ISC_LINK_INITIALIZER }; 150 ISC_LIST_APPEND(forwarders->fwdrs, fwd, link); 151 } 152 153 dns_qpmulti_write(fwdtable->table, &qp); 154 result = dns_qp_insert(qp, forwarders, 0); 155 dns_qp_compact(qp, DNS_QPGC_MAYBE); 156 dns_qpmulti_commit(fwdtable->table, &qp); 157 158 dns_forwarders_detach(&forwarders); 159 160 return result; 161 } 162 163 isc_result_t 164 dns_fwdtable_find(dns_fwdtable_t *fwdtable, const dns_name_t *name, 165 dns_forwarders_t **forwardersp) { 166 isc_result_t result; 167 dns_qpread_t qpr; 168 void *pval = NULL; 169 170 REQUIRE(VALID_FWDTABLE(fwdtable)); 171 172 dns_qpmulti_query(fwdtable->table, &qpr); 173 result = dns_qp_lookup(&qpr, name, NULL, NULL, NULL, &pval, NULL); 174 if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) { 175 dns_forwarders_t *fwdrs = pval; 176 *forwardersp = fwdrs; 177 dns_forwarders_ref(fwdrs); 178 } 179 dns_qpread_destroy(fwdtable->table, &qpr); 180 181 return result; 182 } 183 184 void 185 dns_fwdtable_destroy(dns_fwdtable_t **fwdtablep) { 186 dns_fwdtable_t *fwdtable = NULL; 187 188 REQUIRE(fwdtablep != NULL && VALID_FWDTABLE(*fwdtablep)); 189 190 fwdtable = *fwdtablep; 191 *fwdtablep = NULL; 192 193 dns_qpmulti_destroy(&fwdtable->table); 194 fwdtable->magic = 0; 195 196 isc_mem_putanddetach(&fwdtable->mctx, fwdtable, sizeof(*fwdtable)); 197 } 198 199 /*** 200 *** Private 201 ***/ 202 203 static void 204 destroy_forwarders(dns_forwarders_t *forwarders) { 205 dns_forwarder_t *fwd = NULL; 206 207 while (!ISC_LIST_EMPTY(forwarders->fwdrs)) { 208 fwd = ISC_LIST_HEAD(forwarders->fwdrs); 209 ISC_LIST_UNLINK(forwarders->fwdrs, fwd, link); 210 if (fwd->tlsname != NULL) { 211 dns_name_free(fwd->tlsname, forwarders->mctx); 212 isc_mem_put(forwarders->mctx, fwd->tlsname, 213 sizeof(*fwd->tlsname)); 214 } 215 isc_mem_put(forwarders->mctx, fwd, sizeof(*fwd)); 216 } 217 dns_name_free(&forwarders->name, forwarders->mctx); 218 isc_mem_putanddetach(&forwarders->mctx, forwarders, 219 sizeof(*forwarders)); 220 } 221 222 #if DNS_FORWARD_TRACE 223 ISC_REFCOUNT_TRACE_IMPL(dns_forwarders, destroy_forwarders); 224 #else 225 ISC_REFCOUNT_IMPL(dns_forwarders, destroy_forwarders); 226 #endif 227 228 static void 229 qp_attach(void *uctx ISC_ATTR_UNUSED, void *pval, 230 uint32_t ival ISC_ATTR_UNUSED) { 231 dns_forwarders_t *forwarders = pval; 232 dns_forwarders_ref(forwarders); 233 } 234 235 static void 236 qp_detach(void *uctx ISC_ATTR_UNUSED, void *pval, 237 uint32_t ival ISC_ATTR_UNUSED) { 238 dns_forwarders_t *forwarders = pval; 239 dns_forwarders_detach(&forwarders); 240 } 241 242 static size_t 243 qp_makekey(dns_qpkey_t key, void *uctx ISC_ATTR_UNUSED, void *pval, 244 uint32_t ival ISC_ATTR_UNUSED) { 245 dns_forwarders_t *fwd = pval; 246 return dns_qpkey_fromname(key, &fwd->name); 247 } 248 249 static void 250 qp_triename(void *uctx, char *buf, size_t size) { 251 dns_view_t *view = uctx; 252 snprintf(buf, size, "view %s forwarder table", view->name); 253 } 254