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