1 /* $NetBSD: transportconf.c,v 1.4 2026/01/29 18:36:27 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 <inttypes.h> 19 20 #include <isc/buffer.h> 21 #include <isc/string.h> 22 #include <isc/util.h> 23 24 #include <dns/name.h> 25 #include <dns/transport.h> 26 27 #include <isccfg/cfg.h> 28 29 #include <named/log.h> 30 #include <named/transportconf.h> 31 32 #define create_name(id, name) \ 33 isc_buffer_t namesrc, namebuf; \ 34 char namedata[DNS_NAME_FORMATSIZE + 1]; \ 35 dns_name_init(name, NULL); \ 36 isc_buffer_constinit(&namesrc, id, strlen(id)); \ 37 isc_buffer_add(&namesrc, strlen(id)); \ 38 isc_buffer_init(&namebuf, namedata, sizeof(namedata)); \ 39 CHECK(dns_name_fromtext(name, &namesrc, dns_rootname, \ 40 DNS_NAME_DOWNCASE, &namebuf)); 41 42 #define parse_transport_option(map, transport, name, setter) \ 43 { \ 44 const cfg_obj_t *obj = NULL; \ 45 cfg_map_get(map, name, &obj); \ 46 if (obj != NULL) { \ 47 setter(transport, cfg_obj_asstring(obj)); \ 48 } \ 49 } 50 51 #define parse_transport_tls_versions(map, transport, name, setter) \ 52 { \ 53 const cfg_obj_t *obj = NULL; \ 54 cfg_map_get(map, name, &obj); \ 55 if (obj != NULL) { \ 56 { \ 57 uint32_t tls_protos = 0; \ 58 const cfg_listelt_t *proto = NULL; \ 59 INSIST(obj != NULL); \ 60 for (proto = cfg_list_first(obj); proto != 0; \ 61 proto = cfg_list_next(proto)) \ 62 { \ 63 const cfg_obj_t *tls_proto_obj = \ 64 cfg_listelt_value(proto); \ 65 const char *tls_sver = \ 66 cfg_obj_asstring( \ 67 tls_proto_obj); \ 68 const isc_tls_protocol_version_t ver = \ 69 isc_tls_protocol_name_to_version( \ 70 tls_sver); \ 71 INSIST(ver != \ 72 ISC_TLS_PROTO_VER_UNDEFINED); \ 73 INSIST(isc_tls_protocol_supported( \ 74 ver)); \ 75 tls_protos |= ver; \ 76 } \ 77 if (tls_protos != 0) { \ 78 setter(transport, tls_protos); \ 79 } \ 80 } \ 81 } \ 82 } 83 84 #define parse_transport_bool_option(map, transport, name, setter) \ 85 { \ 86 const cfg_obj_t *obj = NULL; \ 87 cfg_map_get(map, name, &obj); \ 88 if (obj != NULL) { \ 89 setter(transport, cfg_obj_asboolean(obj)); \ 90 } \ 91 } 92 93 static isc_result_t 94 add_doh_transports(const cfg_obj_t *transportlist, dns_transport_list_t *list) { 95 const cfg_obj_t *doh = NULL; 96 const char *dohid = NULL; 97 isc_result_t result; 98 99 for (const cfg_listelt_t *element = cfg_list_first(transportlist); 100 element != NULL; element = cfg_list_next(element)) 101 { 102 dns_name_t dohname; 103 dns_transport_t *transport; 104 105 doh = cfg_listelt_value(element); 106 dohid = cfg_obj_asstring(cfg_map_getname(doh)); 107 108 create_name(dohid, &dohname); 109 110 transport = dns_transport_new(&dohname, DNS_TRANSPORT_HTTP, 111 list); 112 113 dns_transport_set_tlsname(transport, dohid); 114 parse_transport_option(doh, transport, "key-file", 115 dns_transport_set_keyfile); 116 parse_transport_option(doh, transport, "cert-file", 117 dns_transport_set_certfile); 118 parse_transport_tls_versions(doh, transport, "protocols", 119 dns_transport_set_tls_versions); 120 parse_transport_option(doh, transport, "ciphers", 121 dns_transport_set_ciphers); 122 parse_transport_option(doh, transport, "cipher-suites", 123 dns_transport_set_cipher_suites); 124 parse_transport_bool_option( 125 doh, transport, "prefer-server-ciphers", 126 dns_transport_set_prefer_server_ciphers); 127 parse_transport_option(doh, transport, "ca-file", 128 dns_transport_set_cafile); 129 parse_transport_option(doh, transport, "remote-hostname", 130 dns_transport_set_remote_hostname); 131 } 132 133 return ISC_R_SUCCESS; 134 cleanup: 135 cfg_obj_log(doh, named_g_lctx, ISC_LOG_ERROR, 136 "configuring DoH '%s': %s", dohid, 137 isc_result_totext(result)); 138 139 return result; 140 } 141 142 static isc_result_t 143 add_tls_transports(const cfg_obj_t *transportlist, dns_transport_list_t *list) { 144 const cfg_obj_t *tls = NULL; 145 const char *tlsid = NULL; 146 isc_result_t result; 147 148 for (const cfg_listelt_t *element = cfg_list_first(transportlist); 149 element != NULL; element = cfg_list_next(element)) 150 { 151 dns_name_t tlsname; 152 dns_transport_t *transport; 153 154 tls = cfg_listelt_value(element); 155 tlsid = cfg_obj_asstring(cfg_map_getname(tls)); 156 157 if (!strcmp(tlsid, "ephemeral")) { 158 CHECK(ISC_R_UNEXPECTEDTOKEN); 159 } 160 161 create_name(tlsid, &tlsname); 162 163 transport = dns_transport_new(&tlsname, DNS_TRANSPORT_TLS, 164 list); 165 166 dns_transport_set_tlsname(transport, tlsid); 167 parse_transport_option(tls, transport, "key-file", 168 dns_transport_set_keyfile); 169 parse_transport_option(tls, transport, "cert-file", 170 dns_transport_set_certfile); 171 parse_transport_tls_versions(tls, transport, "protocols", 172 dns_transport_set_tls_versions); 173 parse_transport_option(tls, transport, "ciphers", 174 dns_transport_set_ciphers); 175 parse_transport_option(tls, transport, "cipher-suites", 176 dns_transport_set_cipher_suites); 177 parse_transport_bool_option( 178 tls, transport, "prefer-server-ciphers", 179 dns_transport_set_prefer_server_ciphers); 180 parse_transport_option(tls, transport, "ca-file", 181 dns_transport_set_cafile); 182 parse_transport_option(tls, transport, "remote-hostname", 183 dns_transport_set_remote_hostname); 184 } 185 186 return ISC_R_SUCCESS; 187 cleanup: 188 cfg_obj_log(tls, named_g_lctx, ISC_LOG_ERROR, 189 "configuring tls '%s': %s", tlsid, 190 isc_result_totext(result)); 191 192 return result; 193 } 194 195 static isc_result_t 196 transport_list_fromconfig(const cfg_obj_t *config, dns_transport_list_t *list) { 197 const cfg_obj_t *obj = NULL; 198 isc_result_t result = ISC_R_SUCCESS; 199 200 if (result == ISC_R_SUCCESS && 201 cfg_map_get(config, "tls", &obj) == ISC_R_SUCCESS) 202 { 203 result = add_tls_transports(obj, list); 204 obj = NULL; 205 } 206 207 if (result == ISC_R_SUCCESS && 208 cfg_map_get(config, "doh", &obj) == ISC_R_SUCCESS) 209 { 210 result = add_doh_transports(obj, list); 211 obj = NULL; 212 } 213 214 return result; 215 } 216 217 static void 218 transport_list_add_ephemeral(dns_transport_list_t *list) { 219 isc_result_t result; 220 dns_name_t tlsname; 221 dns_transport_t *transport; 222 223 create_name("ephemeral", &tlsname); 224 225 transport = dns_transport_new(&tlsname, DNS_TRANSPORT_TLS, list); 226 dns_transport_set_tlsname(transport, "ephemeral"); 227 228 return; 229 cleanup: 230 RUNTIME_CHECK(result == ISC_R_SUCCESS); 231 } 232 233 isc_result_t 234 named_transports_fromconfig(const cfg_obj_t *config, const cfg_obj_t *vconfig, 235 isc_mem_t *mctx, dns_transport_list_t **listp) { 236 isc_result_t result; 237 dns_transport_list_t *list = dns_transport_list_new(mctx); 238 239 REQUIRE(listp != NULL && *listp == NULL); 240 241 transport_list_add_ephemeral(list); 242 243 if (config != NULL) { 244 CHECK(transport_list_fromconfig(config, list)); 245 } 246 247 if (vconfig != NULL) { 248 config = cfg_tuple_get(vconfig, "options"); 249 transport_list_fromconfig(config, list); 250 } 251 252 *listp = list; 253 return ISC_R_SUCCESS; 254 cleanup: 255 dns_transport_list_detach(&list); 256 return result; 257 } 258