From: Artem Boldariev Date: Thu, 23 Dec 2021 10:01:34 +0000 (+0200) Subject: Use the TLS context cache for server-side contexts X-Git-Tag: v9.17.22~21^2~2 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=5b7d4341fe19be0f1dce3e575383860ab64bde3b;p=thirdparty%2Fbind9.git Use the TLS context cache for server-side contexts Using the TLS context cache for server-side contexts could reduce the number of contexts to initialise in the configurations when e.g. the same 'tls' entry is used in multiple 'listen-on' statements for the same DNS transport, binding to multiple IP addresses. In such a case, only one TLS context will be created, instead of a context per IP address, which could reduce the initialisation time, as initialising even a non-ephemeral TLS context introduces some delay, which can be *visually* noticeable by log activity. Also, this change lays down a foundation for Mutual TLS (when the server validates a client certificate, additionally to a client validating the server), as the TLS context cache can be extended to store additional data required for validation (like intermediates CA chain). Additionally to the above, the change ensures that the contexts are not being changed after initialisation, as such a practice is frowned upon. Previously we would set the supported ALPN tags within isc_nm_listenhttp() and isc_nm_listentlsdns(). We do not do that for client-side contexts, so that appears to be an overlook. Now we set the supported ALPN tags right after server-side contexts creation, similarly how we do for client-side ones. --- diff --git a/bin/named/include/named/server.h b/bin/named/include/named/server.h index 089145c29c1..dd0d6701185 100644 --- a/bin/named/include/named/server.h +++ b/bin/named/include/named/server.h @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -109,6 +110,8 @@ struct named_server { dns_dtenv_t *dtenv; /*%< Dnstap environment */ char *lockfile; + + isc_tlsctx_cache_t *tlsctx_server_cache; }; #define NAMED_SERVER_MAGIC ISC_MAGIC('S', 'V', 'E', 'R') diff --git a/bin/named/server.c b/bin/named/server.c index d4641516fc3..0ac53c9a763 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -403,19 +403,21 @@ named_server_reload(isc_task_t *task, isc_event_t *event); #ifdef HAVE_LIBNGHTTP2 static isc_result_t -listenelt_http(const cfg_obj_t *http, bool tls, - const ns_listen_tls_params_t *tls_params, in_port_t port, +listenelt_http(const cfg_obj_t *http, const uint16_t family, bool tls, + const ns_listen_tls_params_t *tls_params, + isc_tlsctx_cache_t *tlsctx_cache, in_port_t port, isc_mem_t *mctx, ns_listenelt_t **target); #endif static isc_result_t listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config, cfg_aclconfctx_t *actx, isc_mem_t *mctx, uint16_t family, - ns_listenelt_t **target); + isc_tlsctx_cache_t *tlsctx_cache, ns_listenelt_t **target); static isc_result_t listenlist_fromconfig(const cfg_obj_t *listenlist, const cfg_obj_t *config, cfg_aclconfctx_t *actx, isc_mem_t *mctx, uint16_t family, + isc_tlsctx_cache_t *tlsctx_cache, ns_listenlist_t **target); static isc_result_t @@ -8422,6 +8424,13 @@ load_configuration(const char *filename, named_server_t *server, */ CHECK(bind9_check_namedconf(config, false, named_g_lctx, named_g_mctx)); + /* Let's recreate the TLS context cache */ + if (server->tlsctx_server_cache != NULL) { + isc_tlsctx_cache_detach(&server->tlsctx_server_cache); + } + + server->tlsctx_server_cache = isc_tlsctx_cache_new(named_g_mctx); + /* * Fill in the maps array, used for resolving defaults. */ @@ -8874,13 +8883,15 @@ load_configuration(const char *filename, named_server_t *server, if (clistenon != NULL) { CHECK(listenlist_fromconfig( clistenon, config, named_g_aclconfctx, - named_g_mctx, AF_INET, &listenon)); + named_g_mctx, AF_INET, + server->tlsctx_server_cache, &listenon)); } else { /* * Not specified, use default. */ CHECK(ns_listenlist_default(named_g_mctx, listen_port, - -1, true, &listenon)); + -1, true, AF_INET, + &listenon)); } if (listenon != NULL) { ns_interfacemgr_setlistenon4(server->interfacemgr, @@ -8901,13 +8912,15 @@ load_configuration(const char *filename, named_server_t *server, if (clistenon != NULL) { CHECK(listenlist_fromconfig( clistenon, config, named_g_aclconfctx, - named_g_mctx, AF_INET6, &listenon)); + named_g_mctx, AF_INET6, + server->tlsctx_server_cache, &listenon)); } else { /* * Not specified, use default. */ CHECK(ns_listenlist_default(named_g_mctx, listen_port, - -1, true, &listenon)); + -1, true, AF_INET6, + &listenon)); } if (listenon != NULL) { ns_interfacemgr_setlistenon6(server->interfacemgr, @@ -10167,6 +10180,9 @@ named_server_create(isc_mem_t *mctx, named_server_t **serverp) { server->dtenv = NULL; server->magic = NAMED_SERVER_MAGIC; + + server->tlsctx_server_cache = NULL; + *serverp = server; } @@ -10221,6 +10237,10 @@ named_server_destroy(named_server_t **serverp) { INSIST(ISC_LIST_EMPTY(server->viewlist)); INSIST(ISC_LIST_EMPTY(server->cachelist)); + if (server->tlsctx_server_cache != NULL) { + isc_tlsctx_cache_detach(&server->tlsctx_server_cache); + } + server->magic = 0; isc_mem_put(server->mctx, server, sizeof(*server)); *serverp = NULL; @@ -10860,6 +10880,7 @@ named_server_togglequerylog(named_server_t *server, isc_lex_t *lex) { static isc_result_t listenlist_fromconfig(const cfg_obj_t *listenlist, const cfg_obj_t *config, cfg_aclconfctx_t *actx, isc_mem_t *mctx, uint16_t family, + isc_tlsctx_cache_t *tlsctx_cache, ns_listenlist_t **target) { isc_result_t result; const cfg_listelt_t *element; @@ -10878,7 +10899,7 @@ listenlist_fromconfig(const cfg_obj_t *listenlist, const cfg_obj_t *config, ns_listenelt_t *delt = NULL; const cfg_obj_t *listener = cfg_listelt_value(element); result = listenelt_fromconfig(listener, config, actx, mctx, - family, &delt); + family, tlsctx_cache, &delt); if (result != ISC_R_SUCCESS) { goto cleanup; } @@ -10925,6 +10946,7 @@ find_maplist(const cfg_obj_t *config, const char *listname, const char *name) { static isc_result_t listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config, cfg_aclconfctx_t *actx, isc_mem_t *mctx, uint16_t family, + isc_tlsctx_cache_t *tlsctx_cache, ns_listenelt_t **target) { isc_result_t result; const cfg_obj_t *ltup = NULL; @@ -10942,6 +10964,7 @@ listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config, ns_listenelt_t *delt = NULL; uint32_t tls_protos = 0; ns_listen_tls_params_t tls_params = { 0 }; + const char *tlsname = NULL; REQUIRE(target != NULL && *target == NULL); @@ -10950,7 +10973,7 @@ listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config, tlsobj = cfg_tuple_get(ltup, "tls"); if (tlsobj != NULL && cfg_obj_isstring(tlsobj)) { - const char *tlsname = cfg_obj_asstring(tlsobj); + tlsname = cfg_obj_asstring(tlsobj); if (strcasecmp(tlsname, "none") == 0) { no_tls = true; @@ -11033,6 +11056,7 @@ listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config, } tls_params = (ns_listen_tls_params_t){ + .name = tlsname, .key = key, .cert = cert, .protocols = tls_protos, @@ -11126,14 +11150,15 @@ listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config, #ifdef HAVE_LIBNGHTTP2 if (http) { - CHECK(listenelt_http(http_server, do_tls, &tls_params, port, - mctx, &delt)); + CHECK(listenelt_http(http_server, family, do_tls, &tls_params, + tlsctx_cache, port, mctx, &delt)); } #endif /* HAVE_LIBNGHTTP2 */ if (!http) { - CHECK(ns_listenelt_create(mctx, port, dscp, NULL, do_tls, - &tls_params, &delt)); + CHECK(ns_listenelt_create(mctx, port, dscp, NULL, family, + do_tls, &tls_params, tlsctx_cache, + &delt)); } result = cfg_acl_fromconfig2(cfg_tuple_get(listener, "acl"), config, @@ -11151,8 +11176,9 @@ cleanup: #ifdef HAVE_LIBNGHTTP2 static isc_result_t -listenelt_http(const cfg_obj_t *http, bool tls, - const ns_listen_tls_params_t *tls_params, in_port_t port, +listenelt_http(const cfg_obj_t *http, const uint16_t family, bool tls, + const ns_listen_tls_params_t *tls_params, + isc_tlsctx_cache_t *tlsctx_cache, in_port_t port, isc_mem_t *mctx, ns_listenelt_t **target) { isc_result_t result = ISC_R_SUCCESS; ns_listenelt_t *delt = NULL; @@ -11224,9 +11250,9 @@ listenelt_http(const cfg_obj_t *http, bool tls, quota = isc_mem_get(mctx, sizeof(isc_quota_t)); isc_quota_init(quota, max_clients); } - result = ns_listenelt_create_http(mctx, port, named_g_dscp, NULL, tls, - tls_params, endpoints, len, quota, - max_streams, &delt); + result = ns_listenelt_create_http( + mctx, port, named_g_dscp, NULL, family, tls, tls_params, + tlsctx_cache, endpoints, len, quota, max_streams, &delt); if (result != ISC_R_SUCCESS) { goto error; } diff --git a/lib/isc/netmgr/http.c b/lib/isc/netmgr/http.c index 539fef6fd31..e31180bd02e 100644 --- a/lib/isc/netmgr/http.c +++ b/lib/isc/netmgr/http.c @@ -2454,7 +2454,6 @@ isc_nm_listenhttp(isc_nm_t *mgr, isc_sockaddr_t *iface, int backlog, isc_nm_http_endpoints_attach(eps, &sock->h2.listener_endpoints); if (ctx != NULL) { - isc_tlsctx_enable_http2server_alpn(ctx); result = isc_nm_listentls(mgr, iface, httplisten_acceptcb, sock, sizeof(isc_nm_http_session_t), backlog, quota, ctx, &sock->outer); diff --git a/lib/isc/netmgr/tlsdns.c b/lib/isc/netmgr/tlsdns.c index 0e34b95011c..54848102b2e 100644 --- a/lib/isc/netmgr/tlsdns.c +++ b/lib/isc/netmgr/tlsdns.c @@ -494,8 +494,6 @@ isc_nm_listentlsdns(isc_nm_t *mgr, isc_sockaddr_t *iface, sock->tid = 0; sock->fd = -1; - isc_tlsctx_enable_dot_server_alpn(sslctx); - #if !HAVE_SO_REUSEPORT_LB fd = isc__nm_tlsdns_lb_socket(iface->type.sa.sa_family); #endif diff --git a/lib/isc/tests/doh_test.c b/lib/isc/tests/doh_test.c index 7a2c06571b2..a5694979f8c 100644 --- a/lib/isc/tests/doh_test.c +++ b/lib/isc/tests/doh_test.c @@ -332,6 +332,7 @@ nm_setup(void **state) { server_tlsctx = NULL; isc_tlsctx_createserver(NULL, NULL, &server_tlsctx); + isc_tlsctx_enable_http2server_alpn(server_tlsctx); client_tlsctx = NULL; isc_tlsctx_createclient(&client_tlsctx); isc_tlsctx_enable_http2client_alpn(client_tlsctx); diff --git a/lib/ns/include/ns/listenlist.h b/lib/ns/include/ns/listenlist.h index 0b052b6a70e..936061e4578 100644 --- a/lib/ns/include/ns/listenlist.h +++ b/lib/ns/include/ns/listenlist.h @@ -40,15 +40,16 @@ typedef struct ns_listenlist ns_listenlist_t; struct ns_listenelt { isc_mem_t *mctx; - in_port_t port; - bool is_http; - isc_dscp_t dscp; /* -1 = not set, 0..63 */ + in_port_t port; + bool is_http; + isc_dscp_t dscp; /* -1 = not set, 0..63 */ dns_acl_t *acl; - isc_tlsctx_t *sslctx; - char **http_endpoints; - size_t http_endpoints_number; - isc_quota_t *http_quota; - uint32_t max_concurrent_streams; + isc_tlsctx_t *sslctx; + isc_tlsctx_cache_t *sslctx_cache; + char **http_endpoints; + size_t http_endpoints_number; + isc_quota_t *http_quota; + uint32_t max_concurrent_streams; ISC_LINK(ns_listenelt_t) link; }; @@ -59,6 +60,7 @@ struct ns_listenlist { }; typedef struct ns_listen_tls_params { + const char *name; const char *key; const char *cert; uint32_t protocols; @@ -76,24 +78,25 @@ typedef struct ns_listen_tls_params { isc_result_t ns_listenelt_create(isc_mem_t *mctx, in_port_t port, isc_dscp_t dscp, - dns_acl_t *acl, bool tls, + dns_acl_t *acl, const uint16_t family, bool tls, const ns_listen_tls_params_t *tls_params, - ns_listenelt_t **target); + isc_tlsctx_cache_t *tlsctx_cache, ns_listenelt_t **target); /*%< * Create a listen-on list element. * * Requires: * \li 'targetp' is a valid pointer to a pointer containing 'NULL'; * \li 'tls_params' is a valid, non-'NULL' pointer if 'tls' equals 'true'. + * \li 'tlsctx_cache' is a valid, non-'NULL' pointer if 'tls' equals 'true'. */ 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, + dns_acl_t *acl, const uint16_t family, 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); + isc_tlsctx_cache_t *tlsctx_cache, char **endpoints, + size_t nendpoints, isc_quota_t *quota, + const uint32_t max_streams, ns_listenelt_t **target); /*%< * Create a listen-on list element for HTTP(S). */ @@ -124,7 +127,8 @@ ns_listenlist_detach(ns_listenlist_t **listp); isc_result_t ns_listenlist_default(isc_mem_t *mctx, in_port_t port, isc_dscp_t dscp, - bool enabled, ns_listenlist_t **target); + bool enabled, const uint16_t family, + ns_listenlist_t **target); /*%< * Create a listen-on list with default contents, matching * all addresses with port 'port' (if 'enabled' is true), diff --git a/lib/ns/listenlist.c b/lib/ns/listenlist.c index 6aa67dbd2bc..602d9c3807d 100644 --- a/lib/ns/listenlist.c +++ b/lib/ns/listenlist.c @@ -24,50 +24,92 @@ static void destroy(ns_listenlist_t *list); -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) { +static isc_result_t +listenelt_create(isc_mem_t *mctx, in_port_t port, isc_dscp_t dscp, + dns_acl_t *acl, const uint16_t family, const bool is_http, + bool tls, const ns_listen_tls_params_t *tls_params, + isc_tlsctx_cache_t *tlsctx_cache, ns_listenelt_t **target) { ns_listenelt_t *elt = NULL; isc_result_t result = ISC_R_SUCCESS; isc_tlsctx_t *sslctx = NULL; REQUIRE(target != NULL && *target == NULL); - REQUIRE(!tls || tls_params != NULL); + REQUIRE(!tls || (tls_params != NULL && tlsctx_cache != NULL)); if (tls) { - result = isc_tlsctx_createserver(tls_params->key, - tls_params->cert, &sslctx); + const isc_tlsctx_cache_transport_t transport = + is_http ? isc_tlsctx_cache_https : isc_tlsctx_cache_tls; + + /* + * Let's try to reuse the existing context from the cache in + * order to avoid excessive TLS contexts creation. + */ + result = isc_tlsctx_cache_find(tlsctx_cache, tls_params->name, + transport, family, &sslctx); if (result != ISC_R_SUCCESS) { - return (result); - } + /* + * The lookup failed, let's try to create a new context + * and store it within the cache. + */ + INSIST(tls_params->name != NULL && + *tls_params->name != '\0'); - if (tls_params->protocols != 0) { - isc_tlsctx_set_protocols(sslctx, tls_params->protocols); - } + result = isc_tlsctx_createserver( + tls_params->key, tls_params->cert, &sslctx); + if (result != ISC_R_SUCCESS) { + return (result); + } - if (tls_params->dhparam_file != NULL) { - if (!isc_tlsctx_load_dhparams(sslctx, - tls_params->dhparam_file)) - { - isc_tlsctx_free(&sslctx); - return (ISC_R_FAILURE); + if (tls_params->protocols != 0) { + isc_tlsctx_set_protocols(sslctx, + tls_params->protocols); } - } - if (tls_params->ciphers != NULL) { - isc_tlsctx_set_cipherlist(sslctx, tls_params->ciphers); - } + if (tls_params->dhparam_file != NULL) { + if (!isc_tlsctx_load_dhparams( + sslctx, tls_params->dhparam_file)) { + isc_tlsctx_free(&sslctx); + return (ISC_R_FAILURE); + } + } - if (tls_params->prefer_server_ciphers_set) { - isc_tlsctx_prefer_server_ciphers( - sslctx, tls_params->prefer_server_ciphers); - } + if (tls_params->ciphers != NULL) { + isc_tlsctx_set_cipherlist(sslctx, + tls_params->ciphers); + } + + if (tls_params->prefer_server_ciphers_set) { + isc_tlsctx_prefer_server_ciphers( + sslctx, + tls_params->prefer_server_ciphers); + } - if (tls_params->session_tickets_set) { - isc_tlsctx_session_tickets(sslctx, - tls_params->session_tickets); + if (tls_params->session_tickets_set) { + isc_tlsctx_session_tickets( + sslctx, tls_params->session_tickets); + } + +#ifdef HAVE_LIBNGHTTP2 + if (is_http) { + isc_tlsctx_enable_http2server_alpn(sslctx); + } +#endif /* HAVE_LIBNGHTTP2 */ + + if (!is_http) { + isc_tlsctx_enable_dot_server_alpn(sslctx); + } + + /* + * The storing in the cache should not fail because the + * (re)initialisation happens from within a single + * thread. + */ + RUNTIME_CHECK(isc_tlsctx_cache_add( + tlsctx_cache, tls_params->name, + transport, family, sslctx, + NULL) == ISC_R_SUCCESS); + } else { + INSIST(sslctx != NULL); } } @@ -79,6 +121,10 @@ ns_listenelt_create(isc_mem_t *mctx, in_port_t port, isc_dscp_t dscp, elt->dscp = dscp; elt->acl = acl; elt->sslctx = sslctx; + elt->sslctx_cache = NULL; + if (sslctx != NULL && tlsctx_cache != NULL) { + isc_tlsctx_cache_attach(tlsctx_cache, &elt->sslctx_cache); + } elt->http_endpoints = NULL; elt->http_endpoints_number = 0; elt->http_quota = NULL; @@ -87,21 +133,30 @@ ns_listenelt_create(isc_mem_t *mctx, in_port_t port, isc_dscp_t dscp, return (ISC_R_SUCCESS); } +isc_result_t +ns_listenelt_create(isc_mem_t *mctx, in_port_t port, isc_dscp_t dscp, + dns_acl_t *acl, const uint16_t family, bool tls, + const ns_listen_tls_params_t *tls_params, + isc_tlsctx_cache_t *tlsctx_cache, ns_listenelt_t **target) { + return listenelt_create(mctx, port, dscp, acl, family, false, tls, + tls_params, tlsctx_cache, target); +} + 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, + dns_acl_t *acl, const uint16_t family, 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) { + isc_tlsctx_cache_t *tlsctx_cache, char **endpoints, + size_t nendpoints, isc_quota_t *quota, + const uint32_t max_streams, ns_listenelt_t **target) { isc_result_t result; REQUIRE(target != NULL && *target == NULL); REQUIRE(endpoints != NULL && *endpoints != NULL); REQUIRE(nendpoints > 0); - result = ns_listenelt_create(mctx, http_port, dscp, acl, tls, - tls_params, target); + result = listenelt_create(mctx, http_port, dscp, acl, family, true, tls, + tls_params, tlsctx_cache, target); if (result == ISC_R_SUCCESS) { (*target)->is_http = true; (*target)->http_endpoints = endpoints; @@ -123,8 +178,11 @@ ns_listenelt_destroy(ns_listenelt_t *elt) { if (elt->acl != NULL) { dns_acl_detach(&elt->acl); } - if (elt->sslctx != NULL) { - isc_tlsctx_free(&elt->sslctx); + + elt->sslctx = NULL; /* this one is going to be destroyed alongside the + sslctx_cache */ + if (elt->sslctx_cache != NULL) { + isc_tlsctx_cache_detach(&elt->sslctx_cache); } if (elt->http_endpoints != NULL) { size_t i; @@ -179,7 +237,8 @@ ns_listenlist_detach(ns_listenlist_t **listp) { isc_result_t ns_listenlist_default(isc_mem_t *mctx, in_port_t port, isc_dscp_t dscp, - bool enabled, ns_listenlist_t **target) { + bool enabled, const uint16_t family, + ns_listenlist_t **target) { isc_result_t result; dns_acl_t *acl = NULL; ns_listenelt_t *elt = NULL; @@ -195,7 +254,8 @@ ns_listenlist_default(isc_mem_t *mctx, in_port_t port, isc_dscp_t dscp, goto cleanup; } - result = ns_listenelt_create(mctx, port, dscp, acl, false, NULL, &elt); + result = ns_listenelt_create(mctx, port, dscp, acl, family, false, NULL, + NULL, &elt); if (result != ISC_R_SUCCESS) { goto cleanup_acl; } diff --git a/lib/ns/tests/listenlist_test.c b/lib/ns/tests/listenlist_test.c index 94012e9fdc2..a197590f0ae 100644 --- a/lib/ns/tests/listenlist_test.c +++ b/lib/ns/tests/listenlist_test.c @@ -71,7 +71,7 @@ ns_listenlist_default_test(void **state) { UNUSED(state); - result = ns_listenlist_default(mctx, port, -1, false, &list); + result = ns_listenlist_default(mctx, port, -1, false, AF_INET, &list); assert_int_equal(result, ISC_R_SUCCESS); assert_non_null(list); @@ -98,7 +98,7 @@ ns_listenlist_default_test(void **state) { ns_listenlist_detach(&list); - result = ns_listenlist_default(mctx, port, -1, true, &list); + result = ns_listenlist_default(mctx, port, -1, true, AF_INET, &list); assert_int_equal(result, ISC_R_SUCCESS); assert_false(ISC_LIST_EMPTY(list->elts)); diff --git a/lib/ns/tests/nstest.c b/lib/ns/tests/nstest.c index 3c458ca1d61..5202b2e9b31 100644 --- a/lib/ns/tests/nstest.c +++ b/lib/ns/tests/nstest.c @@ -11,7 +11,6 @@ /*! \file */ -#include "nstest.h" #include #include #include @@ -50,6 +49,8 @@ #include #include +#include "nstest.h" + isc_mem_t *mctx = NULL; isc_log_t *lctx = NULL; isc_nm_t *netmgr = NULL; @@ -236,7 +237,7 @@ create_managers(void) { dispatchmgr, maintask, NULL, ncpus, false, &interfacemgr)); - CHECK(ns_listenlist_default(mctx, port, -1, true, &listenon)); + CHECK(ns_listenlist_default(mctx, port, -1, true, AF_INET, &listenon)); ns_interfacemgr_setlistenon4(interfacemgr, listenon); ns_listenlist_detach(&listenon);