1 /*
2  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3  *
4  * This Source Code Form is subject to the terms of the Mozilla Public
5  * License, v. 2.0. If a copy of the MPL was not distributed with this
6  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
7  *
8  * See the COPYRIGHT file distributed with this work for additional
9  * information regarding copyright ownership.
10  */
11 
12 /*! \file */
13 
14 #include <stdbool.h>
15 
16 #include <isc/mem.h>
17 #include <isc/netmgr.h>
18 #include <isc/util.h>
19 
20 #include <dns/acl.h>
21 
22 #include <ns/listenlist.h>
23 
24 static void
25 destroy(ns_listenlist_t *list);
26 
27 isc_result_t
ns_listenelt_create(isc_mem_t * mctx,in_port_t port,isc_dscp_t dscp,dns_acl_t * acl,bool tls,const ns_listen_tls_params_t * tls_params,ns_listenelt_t ** target)28 ns_listenelt_create(isc_mem_t *mctx, in_port_t port, isc_dscp_t dscp,
29 		    dns_acl_t *acl, bool tls,
30 		    const ns_listen_tls_params_t *tls_params,
31 		    ns_listenelt_t **target) {
32 	ns_listenelt_t *elt = NULL;
33 	isc_result_t result = ISC_R_SUCCESS;
34 	isc_tlsctx_t *sslctx = NULL;
35 
36 	REQUIRE(target != NULL && *target == NULL);
37 	REQUIRE(!tls || tls_params != NULL);
38 
39 	if (tls) {
40 		result = isc_tlsctx_createserver(tls_params->key,
41 						 tls_params->cert, &sslctx);
42 		if (result != ISC_R_SUCCESS) {
43 			return (result);
44 		}
45 
46 		if (tls_params->protocols != 0) {
47 			isc_tlsctx_set_protocols(sslctx, tls_params->protocols);
48 		}
49 
50 		if (tls_params->dhparam_file != NULL) {
51 			if (!isc_tlsctx_load_dhparams(sslctx,
52 						      tls_params->dhparam_file))
53 			{
54 				isc_tlsctx_free(&sslctx);
55 				return (ISC_R_FAILURE);
56 			}
57 		}
58 
59 		if (tls_params->ciphers != NULL) {
60 			isc_tlsctx_set_cipherlist(sslctx, tls_params->ciphers);
61 		}
62 
63 		if (tls_params->prefer_server_ciphers_set) {
64 			isc_tlsctx_prefer_server_ciphers(
65 				sslctx, tls_params->prefer_server_ciphers);
66 		}
67 
68 		if (tls_params->session_tickets_set) {
69 			isc_tlsctx_session_tickets(sslctx,
70 						   tls_params->session_tickets);
71 		}
72 	}
73 
74 	elt = isc_mem_get(mctx, sizeof(*elt));
75 	elt->mctx = mctx;
76 	ISC_LINK_INIT(elt, link);
77 	elt->port = port;
78 	elt->is_http = false;
79 	elt->dscp = dscp;
80 	elt->acl = acl;
81 	elt->sslctx = sslctx;
82 	elt->http_endpoints = NULL;
83 	elt->http_endpoints_number = 0;
84 	elt->http_quota = NULL;
85 
86 	*target = elt;
87 	return (ISC_R_SUCCESS);
88 }
89 
90 isc_result_t
ns_listenelt_create_http(isc_mem_t * mctx,in_port_t http_port,isc_dscp_t dscp,dns_acl_t * acl,bool tls,const ns_listen_tls_params_t * tls_params,char ** endpoints,size_t nendpoints,isc_quota_t * quota,const uint32_t max_streams,ns_listenelt_t ** target)91 ns_listenelt_create_http(isc_mem_t *mctx, in_port_t http_port, isc_dscp_t dscp,
92 			 dns_acl_t *acl, bool tls,
93 			 const ns_listen_tls_params_t *tls_params,
94 			 char **endpoints, size_t nendpoints,
95 			 isc_quota_t *quota, const uint32_t max_streams,
96 			 ns_listenelt_t **target) {
97 	isc_result_t result;
98 
99 	REQUIRE(target != NULL && *target == NULL);
100 	REQUIRE(endpoints != NULL && *endpoints != NULL);
101 	REQUIRE(nendpoints > 0);
102 
103 	result = ns_listenelt_create(mctx, http_port, dscp, acl, tls,
104 				     tls_params, target);
105 	if (result == ISC_R_SUCCESS) {
106 		(*target)->is_http = true;
107 		(*target)->http_endpoints = endpoints;
108 		(*target)->http_endpoints_number = nendpoints;
109 		(*target)->http_quota = quota;
110 		(*target)->max_concurrent_streams = max_streams;
111 	} else {
112 		size_t i;
113 		for (i = 0; i < nendpoints; i++) {
114 			isc_mem_free(mctx, endpoints[i]);
115 		}
116 		isc_mem_free(mctx, endpoints);
117 	}
118 	return (result);
119 }
120 
121 void
ns_listenelt_destroy(ns_listenelt_t * elt)122 ns_listenelt_destroy(ns_listenelt_t *elt) {
123 	if (elt->acl != NULL) {
124 		dns_acl_detach(&elt->acl);
125 	}
126 	if (elt->sslctx != NULL) {
127 		isc_tlsctx_free(&elt->sslctx);
128 	}
129 	if (elt->http_endpoints != NULL) {
130 		size_t i;
131 		INSIST(elt->http_endpoints_number > 0);
132 		for (i = 0; i < elt->http_endpoints_number; i++) {
133 			isc_mem_free(elt->mctx, elt->http_endpoints[i]);
134 		}
135 		isc_mem_free(elt->mctx, elt->http_endpoints);
136 	}
137 	isc_mem_put(elt->mctx, elt, sizeof(*elt));
138 }
139 
140 isc_result_t
ns_listenlist_create(isc_mem_t * mctx,ns_listenlist_t ** target)141 ns_listenlist_create(isc_mem_t *mctx, ns_listenlist_t **target) {
142 	ns_listenlist_t *list = NULL;
143 	REQUIRE(target != NULL && *target == NULL);
144 	list = isc_mem_get(mctx, sizeof(*list));
145 	list->mctx = mctx;
146 	list->refcount = 1;
147 	ISC_LIST_INIT(list->elts);
148 	*target = list;
149 	return (ISC_R_SUCCESS);
150 }
151 
152 static void
destroy(ns_listenlist_t * list)153 destroy(ns_listenlist_t *list) {
154 	ns_listenelt_t *elt, *next;
155 	for (elt = ISC_LIST_HEAD(list->elts); elt != NULL; elt = next) {
156 		next = ISC_LIST_NEXT(elt, link);
157 		ns_listenelt_destroy(elt);
158 	}
159 	isc_mem_put(list->mctx, list, sizeof(*list));
160 }
161 
162 void
ns_listenlist_attach(ns_listenlist_t * source,ns_listenlist_t ** target)163 ns_listenlist_attach(ns_listenlist_t *source, ns_listenlist_t **target) {
164 	INSIST(source->refcount > 0);
165 	source->refcount++;
166 	*target = source;
167 }
168 
169 void
ns_listenlist_detach(ns_listenlist_t ** listp)170 ns_listenlist_detach(ns_listenlist_t **listp) {
171 	ns_listenlist_t *list = *listp;
172 	*listp = NULL;
173 	INSIST(list->refcount > 0);
174 	list->refcount--;
175 	if (list->refcount == 0) {
176 		destroy(list);
177 	}
178 }
179 
180 isc_result_t
ns_listenlist_default(isc_mem_t * mctx,in_port_t port,isc_dscp_t dscp,bool enabled,ns_listenlist_t ** target)181 ns_listenlist_default(isc_mem_t *mctx, in_port_t port, isc_dscp_t dscp,
182 		      bool enabled, ns_listenlist_t **target) {
183 	isc_result_t result;
184 	dns_acl_t *acl = NULL;
185 	ns_listenelt_t *elt = NULL;
186 	ns_listenlist_t *list = NULL;
187 
188 	REQUIRE(target != NULL && *target == NULL);
189 	if (enabled) {
190 		result = dns_acl_any(mctx, &acl);
191 	} else {
192 		result = dns_acl_none(mctx, &acl);
193 	}
194 	if (result != ISC_R_SUCCESS) {
195 		goto cleanup;
196 	}
197 
198 	result = ns_listenelt_create(mctx, port, dscp, acl, false, NULL, &elt);
199 	if (result != ISC_R_SUCCESS) {
200 		goto cleanup_acl;
201 	}
202 
203 	result = ns_listenlist_create(mctx, &list);
204 	if (result != ISC_R_SUCCESS) {
205 		goto cleanup_listenelt;
206 	}
207 
208 	ISC_LIST_APPEND(list->elts, elt, link);
209 
210 	*target = list;
211 	return (ISC_R_SUCCESS);
212 
213 cleanup_listenelt:
214 	ns_listenelt_destroy(elt);
215 cleanup_acl:
216 	dns_acl_detach(&acl);
217 cleanup:
218 	return (result);
219 }
220