Home | History | Annotate | Line # | Download | only in dns
      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