/* * Copyright (C) Internet Systems Consortium, Inc. ("ISC") * * SPDX-License-Identifier: MPL-2.0 * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, you can obtain one at https://mozilla.org/MPL/2.0/. * * See the COPYRIGHT file distributed with this work for additional * information regarding copyright ownership. */ /*! \file */ #include #include #include #include #include #include #include #include #include #include struct dns_fwdtable { /* Unlocked. */ unsigned int magic; isc_mem_t *mctx; dns_qpmulti_t *table; }; #define FWDTABLEMAGIC ISC_MAGIC('F', 'w', 'd', 'T') #define VALID_FWDTABLE(ft) ISC_MAGIC_VALID(ft, FWDTABLEMAGIC) static void qp_attach(void *uctx, void *pval, uint32_t ival); static void qp_detach(void *uctx, void *pval, uint32_t ival); static size_t qp_makekey(dns_qpkey_t key, void *uctx, void *pval, uint32_t ival); static void qp_triename(void *uctx, char *buf, size_t size); static dns_qpmethods_t qpmethods = { qp_attach, qp_detach, qp_makekey, qp_triename, }; void dns_fwdtable_create(isc_mem_t *mctx, dns_view_t *view, dns_fwdtable_t **fwdtablep) { dns_fwdtable_t *fwdtable = NULL; REQUIRE(fwdtablep != NULL && *fwdtablep == NULL); fwdtable = isc_mem_get(mctx, sizeof(*fwdtable)); *fwdtable = (dns_fwdtable_t){ .magic = FWDTABLEMAGIC }; dns_qpmulti_create(mctx, &qpmethods, view, &fwdtable->table); isc_mem_attach(mctx, &fwdtable->mctx); *fwdtablep = fwdtable; } static dns_forwarders_t * new_forwarders(isc_mem_t *mctx, const dns_name_t *name, dns_fwdpolicy_t fwdpolicy) { dns_forwarders_t *forwarders = NULL; forwarders = isc_mem_get(mctx, sizeof(*forwarders)); *forwarders = (dns_forwarders_t){ .fwdpolicy = fwdpolicy, .name = DNS_NAME_INITEMPTY, .fwdrs = ISC_LIST_INITIALIZER, }; isc_mem_attach(mctx, &forwarders->mctx); isc_refcount_init(&forwarders->references, 1); dns_name_dup(name, mctx, &forwarders->name); return forwarders; } isc_result_t dns_fwdtable_addfwd(dns_fwdtable_t *fwdtable, const dns_name_t *name, dns_forwarderlist_t *fwdrs, dns_fwdpolicy_t fwdpolicy) { isc_result_t result; dns_forwarders_t *forwarders = NULL; dns_qp_t *qp = NULL; REQUIRE(VALID_FWDTABLE(fwdtable)); forwarders = new_forwarders(fwdtable->mctx, name, fwdpolicy); ISC_LIST_FOREACH(*fwdrs, fwd, link) { dns_forwarder_t *nfwd = isc_mem_get(fwdtable->mctx, sizeof(*nfwd)); *nfwd = *fwd; if (fwd->tlsname != NULL) { nfwd->tlsname = isc_mem_get(fwdtable->mctx, sizeof(*nfwd->tlsname)); dns_name_init(nfwd->tlsname); dns_name_dup(fwd->tlsname, fwdtable->mctx, nfwd->tlsname); } ISC_LINK_INIT(nfwd, link); ISC_LIST_APPEND(forwarders->fwdrs, nfwd, link); } dns_qpmulti_write(fwdtable->table, &qp); result = dns_qp_insert(qp, forwarders, 0); dns_qp_compact(qp, DNS_QPGC_MAYBE); dns_qpmulti_commit(fwdtable->table, &qp); dns_forwarders_detach(&forwarders); return result; } isc_result_t dns_fwdtable_add(dns_fwdtable_t *fwdtable, const dns_name_t *name, isc_sockaddrlist_t *addrs, dns_fwdpolicy_t fwdpolicy) { isc_result_t result; dns_forwarders_t *forwarders = NULL; dns_qp_t *qp = NULL; REQUIRE(VALID_FWDTABLE(fwdtable)); forwarders = new_forwarders(fwdtable->mctx, name, fwdpolicy); ISC_LIST_FOREACH(*addrs, sa, link) { dns_forwarder_t *fwd = isc_mem_get(fwdtable->mctx, sizeof(*fwd)); *fwd = (dns_forwarder_t){ .addr = *sa, .link = ISC_LINK_INITIALIZER }; ISC_LIST_APPEND(forwarders->fwdrs, fwd, link); } dns_qpmulti_write(fwdtable->table, &qp); result = dns_qp_insert(qp, forwarders, 0); dns_qp_compact(qp, DNS_QPGC_MAYBE); dns_qpmulti_commit(fwdtable->table, &qp); dns_forwarders_detach(&forwarders); return result; } isc_result_t dns_fwdtable_find(dns_fwdtable_t *fwdtable, const dns_name_t *name, dns_forwarders_t **forwardersp) { isc_result_t result; dns_qpread_t qpr; void *pval = NULL; REQUIRE(VALID_FWDTABLE(fwdtable)); dns_qpmulti_query(fwdtable->table, &qpr); result = dns_qp_lookup(&qpr, name, DNS_DBNAMESPACE_NORMAL, NULL, NULL, NULL, &pval, NULL); if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH) { dns_forwarders_t *fwdrs = pval; *forwardersp = fwdrs; dns_forwarders_ref(fwdrs); } dns_qpread_destroy(fwdtable->table, &qpr); return result; } void dns_fwdtable_destroy(dns_fwdtable_t **fwdtablep) { dns_fwdtable_t *fwdtable = NULL; REQUIRE(fwdtablep != NULL && VALID_FWDTABLE(*fwdtablep)); fwdtable = *fwdtablep; *fwdtablep = NULL; dns_qpmulti_destroy(&fwdtable->table); fwdtable->magic = 0; isc_mem_putanddetach(&fwdtable->mctx, fwdtable, sizeof(*fwdtable)); } /*** *** Private ***/ static void destroy_forwarders(dns_forwarders_t *forwarders) { ISC_LIST_FOREACH(forwarders->fwdrs, fwd, link) { ISC_LIST_UNLINK(forwarders->fwdrs, fwd, link); if (fwd->tlsname != NULL) { dns_name_free(fwd->tlsname, forwarders->mctx); isc_mem_put(forwarders->mctx, fwd->tlsname, sizeof(*fwd->tlsname)); } isc_mem_put(forwarders->mctx, fwd, sizeof(*fwd)); } dns_name_free(&forwarders->name, forwarders->mctx); isc_mem_putanddetach(&forwarders->mctx, forwarders, sizeof(*forwarders)); } #if DNS_FORWARD_TRACE ISC_REFCOUNT_TRACE_IMPL(dns_forwarders, destroy_forwarders); #else ISC_REFCOUNT_IMPL(dns_forwarders, destroy_forwarders); #endif static void qp_attach(void *uctx ISC_ATTR_UNUSED, void *pval, uint32_t ival ISC_ATTR_UNUSED) { dns_forwarders_t *forwarders = pval; dns_forwarders_ref(forwarders); } static void qp_detach(void *uctx ISC_ATTR_UNUSED, void *pval, uint32_t ival ISC_ATTR_UNUSED) { dns_forwarders_t *forwarders = pval; dns_forwarders_detach(&forwarders); } static size_t qp_makekey(dns_qpkey_t key, void *uctx ISC_ATTR_UNUSED, void *pval, uint32_t ival ISC_ATTR_UNUSED) { dns_forwarders_t *fwd = pval; return dns_qpkey_fromname(key, &fwd->name, DNS_DBNAMESPACE_NORMAL); } static void qp_triename(void *uctx, char *buf, size_t size) { dns_view_t *view = uctx; snprintf(buf, size, "view %s forwarder table", view->name); }