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