[rt-commit] r215 - in /redwax-tool/trunk: ChangeLog configure.ac redwax-tool.c redwax-tool.h redwax-tool.spec.in redwax_ldns.c redwax_openssl.c redwax_unbound.c
rt-commit at redwax.eu
rt-commit at redwax.eu
Wed Apr 2 15:10:28 CEST 2025
Author: minfrin at redwax.eu
Date: Wed Apr 2 15:10:26 2025
New Revision: 215
Log:
Add --tls-in to read certificates from endpoints
pointed to by A and AAAA DNS records.
Modified:
redwax-tool/trunk/ChangeLog
redwax-tool/trunk/configure.ac
redwax-tool/trunk/redwax-tool.c
redwax-tool/trunk/redwax-tool.h
redwax-tool/trunk/redwax-tool.spec.in
redwax-tool/trunk/redwax_ldns.c
redwax-tool/trunk/redwax_openssl.c
redwax-tool/trunk/redwax_unbound.c
Modified: redwax-tool/trunk/ChangeLog
==============================================================================
--- redwax-tool/trunk/ChangeLog (original)
+++ redwax-tool/trunk/ChangeLog Wed Apr 2 15:10:26 2025
@@ -1,3 +1,9 @@
+
+Changes with v0.9.10
+
+ *) Add --tls-in to read certificates from endpoints
+ pointed to by A and AAAA DNS records.
+ [Graham Leggett]
Changes with v0.9.9
Modified: redwax-tool/trunk/configure.ac
==============================================================================
--- redwax-tool/trunk/configure.ac (original)
+++ redwax-tool/trunk/configure.ac Wed Apr 2 15:10:26 2025
@@ -2,7 +2,7 @@
# Process this file with autoconf to produce a configure script.
AC_PREREQ(2.61)
-AC_INIT(redwax-tool, 0.9.9, minfrin at sharp.fm)
+AC_INIT(redwax-tool, 0.9.10, minfrin at sharp.fm)
AC_CONFIG_AUX_DIR(build-aux)
AC_CONFIG_MACRO_DIRS([m4])
AM_INIT_AUTOMAKE([dist-bzip2])
Modified: redwax-tool/trunk/redwax-tool.c
==============================================================================
--- redwax-tool/trunk/redwax-tool.c (original)
+++ redwax-tool/trunk/redwax-tool.c Wed Apr 2 15:10:26 2025
@@ -98,6 +98,8 @@
APR_HOOK_LINK(process_pkcs12_in)
APR_HOOK_LINK(complete_keychain_in)
APR_HOOK_LINK(process_keychain_in)
+ APR_HOOK_LINK(set_tls_in)
+ APR_HOOK_LINK(process_tls_in)
APR_HOOK_LINK(complete_filter)
APR_HOOK_LINK(process_filter)
APR_HOOK_LINK(complete_nss_out)
@@ -183,6 +185,10 @@
(redwax_tool_t * r, const char *arg), (r, arg), DECLINED)
APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(rt, REDWAX, int, complete_keychain_in,
(redwax_tool_t * r, const char *url, apr_hash_t *urls), (r, url, urls), DECLINED)
+APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(rt, REDWAX, int, set_tls_in,
+ (redwax_tool_t * r, const char *arg), (r, arg), DECLINED)
+APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(rt, REDWAX, int, process_tls_in,
+ (redwax_tool_t * r, redwax_dns_t *dns), (r, dns), DECLINED)
APR_IMPLEMENT_EXTERNAL_HOOK_RUN_ALL(rt, REDWAX, int, complete_filter,
(redwax_tool_t * r, apr_hash_t *filters), (r, filters), OK, DECLINED)
APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(rt, REDWAX, int, process_filter,
@@ -257,69 +263,70 @@
#define REDWAX_TOOL_PKCS11_MODULE_IN 259
#define REDWAX_TOOL_PKCS12_IN 260
#define REDWAX_TOOL_KEYCHAIN_IN 261
-#define REDWAX_TOOL_FILTER 262
-#define REDWAX_TOOL_FILTER_EMAIL 263
-#define REDWAX_TOOL_FILTER_HOSTNAME 264
-#define REDWAX_TOOL_FILTER_IP 265
-#define REDWAX_TOOL_FILTER_CURRENT 266
-#define REDWAX_TOOL_FILTER_DATE 267
-#define REDWAX_TOOL_FILTER_EXPIRY 268
-#define REDWAX_TOOL_FILTER_DANE 269
-#define REDWAX_TOOL_FILTER_PURPOSE 270
-#define REDWAX_TOOL_FILTER_VERIFY_PARAM 271
-#define REDWAX_TOOL_FILTER_VERIFY_TLSA 272
-#define REDWAX_TOOL_FILTER_VERIFY_SMIMEA 273
-#define REDWAX_TOOL_CERT_OUT 274
-#define REDWAX_TOOL_NO_CERT_OUT 275
-#define REDWAX_TOOL_CHAIN_OUT 276
-#define REDWAX_TOOL_NO_CHAIN_OUT 277
-#define REDWAX_TOOL_ROOT_OUT 278
-#define REDWAX_TOOL_NO_ROOT_OUT 279
-#define REDWAX_TOOL_TRUST_OUT 280
-#define REDWAX_TOOL_NO_TRUST_OUT 281
-#define REDWAX_TOOL_CRL_OUT 282
-#define REDWAX_TOOL_NO_CRL_OUT 283
-#define REDWAX_TOOL_PARAM_OUT 284
-#define REDWAX_TOOL_NO_PARAM_OUT 285
-#define REDWAX_TOOL_KEY_IN 286
-#define REDWAX_TOOL_NO_KEY_IN 287
-#define REDWAX_TOOL_KEY_OUT 288
-#define REDWAX_TOOL_NO_KEY_OUT 289
-#define REDWAX_TOOL_AUTO_OUT 290
-#define REDWAX_TOOL_NO_AUTO_OUT 291
-#define REDWAX_TOOL_SECRET_SUFFIX_IN 292
-#define REDWAX_TOOL_SECRET_SUFFIX_OUT 293
-#define REDWAX_TOOL_SECRET_TOKEN_IN 294
-#define REDWAX_TOOL_SECRET_TOKEN_OUT 295
-#define REDWAX_TOOL_LABEL_OUT 296
-#define REDWAX_TOOL_NSS_OUT 297
-#define REDWAX_TOOL_NSS_SLOT_OUT 298
-#define REDWAX_TOOL_DER_OUT 299
-#define REDWAX_TOOL_PEM_OUT 300
-#define REDWAX_TOOL_PKCS12_OUT 301
-#define REDWAX_TOOL_PKCS11_OUT 302
-#define REDWAX_TOOL_PKCS11_MODULE_OUT 303
-#define REDWAX_TOOL_METADATA_OUT 304
-#define REDWAX_TOOL_METADATA_THRESHOLD 305
-#define REDWAX_TOOL_FORMAT_OUT 306
-#define REDWAX_TOOL_CALENDAR_OUT 307
-#define REDWAX_TOOL_CALENDAR_ALARM 308
-#define REDWAX_TOOL_REMINDER_OUT 309
-#define REDWAX_TOOL_JWKS_OUT 310
-#define REDWAX_TOOL_TEXT_OUT 311
-#define REDWAX_TOOL_NO_TEXT_OUT 312
-#define REDWAX_TOOL_SSH_PRIVATE_OUT 313
-#define REDWAX_TOOL_SSH_PUBLIC_OUT 314
-#define REDWAX_TOOL_SMIMEA_OUT 315
-#define REDWAX_TOOL_SSHFP_OUT 316
-#define REDWAX_TOOL_TLSA_OUT 317
-#define REDWAX_TOOL_USER_IN 318
-#define REDWAX_TOOL_USER_OUT 319
-#define REDWAX_TOOL_GROUP_IN 320
-#define REDWAX_TOOL_GROUP_OUT 321
-#define REDWAX_TOOL_ORDER_OUT 322
-#define REDWAX_TOOL_DNS_SERVER 323
-#define REDWAX_TOOL_DNS_TRUST_ANCHOR 324
+#define REDWAX_TOOL_TLS_IN 262
+#define REDWAX_TOOL_FILTER 264
+#define REDWAX_TOOL_FILTER_EMAIL 265
+#define REDWAX_TOOL_FILTER_HOSTNAME 266
+#define REDWAX_TOOL_FILTER_IP 267
+#define REDWAX_TOOL_FILTER_CURRENT 268
+#define REDWAX_TOOL_FILTER_DATE 269
+#define REDWAX_TOOL_FILTER_EXPIRY 270
+#define REDWAX_TOOL_FILTER_DANE 271
+#define REDWAX_TOOL_FILTER_PURPOSE 272
+#define REDWAX_TOOL_FILTER_VERIFY_PARAM 273
+#define REDWAX_TOOL_FILTER_VERIFY_TLSA 274
+#define REDWAX_TOOL_FILTER_VERIFY_SMIMEA 275
+#define REDWAX_TOOL_CERT_OUT 276
+#define REDWAX_TOOL_NO_CERT_OUT 277
+#define REDWAX_TOOL_CHAIN_OUT 278
+#define REDWAX_TOOL_NO_CHAIN_OUT 279
+#define REDWAX_TOOL_ROOT_OUT 280
+#define REDWAX_TOOL_NO_ROOT_OUT 281
+#define REDWAX_TOOL_TRUST_OUT 282
+#define REDWAX_TOOL_NO_TRUST_OUT 283
+#define REDWAX_TOOL_CRL_OUT 284
+#define REDWAX_TOOL_NO_CRL_OUT 285
+#define REDWAX_TOOL_PARAM_OUT 286
+#define REDWAX_TOOL_NO_PARAM_OUT 287
+#define REDWAX_TOOL_KEY_IN 288
+#define REDWAX_TOOL_NO_KEY_IN 289
+#define REDWAX_TOOL_KEY_OUT 290
+#define REDWAX_TOOL_NO_KEY_OUT 291
+#define REDWAX_TOOL_AUTO_OUT 292
+#define REDWAX_TOOL_NO_AUTO_OUT 293
+#define REDWAX_TOOL_SECRET_SUFFIX_IN 294
+#define REDWAX_TOOL_SECRET_SUFFIX_OUT 295
+#define REDWAX_TOOL_SECRET_TOKEN_IN 296
+#define REDWAX_TOOL_SECRET_TOKEN_OUT 297
+#define REDWAX_TOOL_LABEL_OUT 298
+#define REDWAX_TOOL_NSS_OUT 299
+#define REDWAX_TOOL_NSS_SLOT_OUT 300
+#define REDWAX_TOOL_DER_OUT 301
+#define REDWAX_TOOL_PEM_OUT 302
+#define REDWAX_TOOL_PKCS12_OUT 303
+#define REDWAX_TOOL_PKCS11_OUT 304
+#define REDWAX_TOOL_PKCS11_MODULE_OUT 305
+#define REDWAX_TOOL_METADATA_OUT 306
+#define REDWAX_TOOL_METADATA_THRESHOLD 307
+#define REDWAX_TOOL_FORMAT_OUT 308
+#define REDWAX_TOOL_CALENDAR_OUT 309
+#define REDWAX_TOOL_CALENDAR_ALARM 310
+#define REDWAX_TOOL_REMINDER_OUT 311
+#define REDWAX_TOOL_JWKS_OUT 312
+#define REDWAX_TOOL_TEXT_OUT 313
+#define REDWAX_TOOL_NO_TEXT_OUT 314
+#define REDWAX_TOOL_SSH_PRIVATE_OUT 315
+#define REDWAX_TOOL_SSH_PUBLIC_OUT 316
+#define REDWAX_TOOL_SMIMEA_OUT 317
+#define REDWAX_TOOL_SSHFP_OUT 318
+#define REDWAX_TOOL_TLSA_OUT 319
+#define REDWAX_TOOL_USER_IN 320
+#define REDWAX_TOOL_USER_OUT 321
+#define REDWAX_TOOL_GROUP_IN 322
+#define REDWAX_TOOL_GROUP_OUT 323
+#define REDWAX_TOOL_ORDER_OUT 324
+#define REDWAX_TOOL_DNS_SERVER 325
+#define REDWAX_TOOL_DNS_TRUST_ANCHOR 326
#define REDWAX_EXIT_OK 0
#define REDWAX_EXIT_INIT 1
@@ -408,6 +415,9 @@
" --keychain-in=keychain\tRead certificates, intermediate certificates,\n"
"\t\t\t\troot certificates, crls, and keys from a MacOS\n"
"\t\t\t\tkeychain identified by the given name." },
+ { "tls-in", REDWAX_TOOL_TLS_IN, 1,
+ " --tls-in=url\t\t\tRead certificates and intermediate certificates\n"
+ "\t\t\t\tfrom a TLS or DTLS endpoint at the given url." },
{ "filter", REDWAX_TOOL_FILTER, 1,
" --filter=type\t\t\tApply the given filter to pass inputs to the\n"
"\t\t\t\toutputs. \"search\" will pass through all\n"
@@ -2675,6 +2685,14 @@
return redwax_process(r, status, name);
}
+static apr_status_t redwax_tls_in(redwax_tool_t *r, const char *arg, const char *name)
+{
+
+ apr_status_t status = rt_run_set_tls_in(r, arg);
+
+ return redwax_process(r, status, name);
+}
+
void redwax_add_default_hooks(apr_pool_t *pool)
{
rt_hook_filter_poll(redwax_filter_poll, NULL, NULL, APR_HOOK_LAST);
@@ -2806,6 +2824,12 @@
}
case REDWAX_TOOL_KEYCHAIN_IN: {
if (redwax_keychain_in(r, optarg, "keychain-in")) {
+ return r->rc;
+ }
+ break;
+ }
+ case REDWAX_TOOL_TLS_IN: {
+ if (redwax_tls_in(r, optarg, "tls-in")) {
return r->rc;
}
break;
Modified: redwax-tool/trunk/redwax-tool.h
==============================================================================
--- redwax-tool/trunk/redwax-tool.h (original)
+++ redwax-tool/trunk/redwax-tool.h Wed Apr 2 15:10:26 2025
@@ -26,9 +26,11 @@
#include <apr_file_io.h>
#include <apr_hash.h>
#include <apr_hooks.h>
+#include <apr_network_io.h>
#include <apr_poll.h>
#include <apr_pools.h>
#include <apr_tables.h>
+#include <apr_uri.h>
#define REDWAX_DECLARE_MODULE(foo) \
module foo##_module
@@ -167,6 +169,11 @@
int dane_nxdomain;
int dane_malformed;
int dane_record;
+ int tls_bogus;
+ int tls_insecure;
+ int tls_nxdomain;
+ int tls_malformed;
+ int tls_record;
int poll_work;
int current;
int cert_out;
@@ -356,6 +363,14 @@
typedef apr_status_t(* redwax_dns_cb_t) (redwax_tool_t *r, redwax_dns_t *dns);
+typedef struct redwax_rdata_a_t {
+ apr_sockaddr_t *sockaddr;
+} redwax_rdata_a_t;
+
+typedef struct redwax_rdata_aaaa_t {
+ apr_sockaddr_t *sockaddr;
+} redwax_rdata_aaaa_t;
+
typedef struct redwax_rdata_tlsa_t {
const char *usage_name;
const char *selector_name;
@@ -371,6 +386,8 @@
unsigned char *data;
int len;
union {
+ redwax_rdata_a_t a;
+ redwax_rdata_a_t aaaa;
redwax_rdata_tlsa_t tlsa;
} rr;
} redwax_rdata_t;
@@ -388,9 +405,18 @@
int secure;
int bogus;
const char *why_bogus;
+ int port;
redwax_dns_cb_t cb;
void *ctx;
} redwax_dns_t;
+
+typedef struct redwax_tls_t {
+ const char *unparsed_uri;
+ apr_uri_t uri;
+ int family;
+ int type;
+ int protocol;
+} redwax_tls_t;
typedef apr_status_t(* redwax_pollfd_cb_t) (redwax_tool_t *r, void *ctx, apr_pollfd_t *descriptor);
@@ -580,6 +606,22 @@
(redwax_tool_t *r, const char *arg))
/**
+ * Hook to trigger a DNS lookup to read certificates from a TLS connection.
+ *
+ * @param r The redwax-tool context.
+ */
+APR_DECLARE_EXTERNAL_HOOK(rt, REDWAX, apr_status_t, set_tls_in,
+ (redwax_tool_t *r, const char *arg))
+
+/**
+ * Hook to read certificates from a DTLS connection.
+ *
+ * @param r The redwax-tool context.
+ */
+APR_DECLARE_EXTERNAL_HOOK(rt, REDWAX, apr_status_t, process_tls_in,
+ (redwax_tool_t *r, redwax_dns_t *dns))
+
+/**
* Hook to complete incoming certificates / intermediates / keys
* and pass filtered results to the outgoing certificates /
* intermediates / keys.
Modified: redwax-tool/trunk/redwax-tool.spec.in
==============================================================================
--- redwax-tool/trunk/redwax-tool.spec.in (original)
+++ redwax-tool/trunk/redwax-tool.spec.in Wed Apr 2 15:10:26 2025
@@ -47,6 +47,16 @@
%license COPYING
%changelog
+* Thu Feb 27 2025 Graham Leggett <minfrin at sharp.fm> 0.9.9-1
+- Bugfix release
+* Thu Feb 27 2025 Graham Leggett <minfrin at sharp.fm> 0.9.8-1
+- Bugfix release
+* Thu Nov 28 2024 Graham Leggett <minfrin at sharp.fm> 0.9.7-1
+- Bugfix release
+* Mon Nov 11 2024 Graham Leggett <minfrin at sharp.fm> 0.9.6-1
+- Feature release
+* Mon Nov 11 2024 Graham Leggett <minfrin at sharp.fm> 0.9.5-1
+- Feature release
* Thu Feb 08 2024 Graham Leggett <minfrin at sharp.fm> 0.9.4-1
- Feature release
* Sun Oct 15 2023 Graham Leggett <minfrin at sharp.fm> 0.9.3-1
Modified: redwax-tool/trunk/redwax_ldns.c
==============================================================================
--- redwax-tool/trunk/redwax_ldns.c (original)
+++ redwax-tool/trunk/redwax_ldns.c Wed Apr 2 15:10:26 2025
@@ -128,6 +128,8 @@
{
const unsigned char *der = cert->der;
+ /* ldns is tightly bound to openssl */
+
X509 *x = d2i_X509(NULL, &der, cert->len);
if (!x) {
@@ -178,6 +180,58 @@
{
switch (dns->rrtype) {
+ case LDNS_RR_TYPE_A: {
+
+ apr_sockaddr_t *new_sa = apr_pcalloc(r->pool, sizeof(apr_sockaddr_t));
+
+ new_sa->pool = r->pool;
+
+ new_sa->family = AF_INET;
+ new_sa->sa.sin.sin_family = AF_INET;
+
+ new_sa->sa.sin.sin_addr = *(struct in_addr *)rdata->data;
+
+ new_sa->hostname = apr_pstrdup(r->pool, dns->qname);
+
+ new_sa->salen = sizeof(struct sockaddr_in);
+ new_sa->addr_str_len = 16;
+ new_sa->ipaddr_ptr = &(new_sa->sa.sin.sin_addr);
+ new_sa->ipaddr_len = sizeof(struct in_addr);
+
+ new_sa->sa.sin.sin_port = htons(dns->port);
+ new_sa->port = dns->port;
+
+ rdata->rr.a.sockaddr = new_sa;
+
+ break;
+ }
+ case LDNS_RR_TYPE_AAAA: {
+
+ redwax_tls_t *tls = dns->ctx;
+
+ apr_sockaddr_t *new_sa = apr_pcalloc(r->pool, sizeof(apr_sockaddr_t));
+
+ new_sa->pool = r->pool;
+
+ new_sa->family = AF_INET6;
+ new_sa->sa.sin.sin_family = AF_INET6;
+
+ new_sa->sa.sin6.sin6_addr = *(struct in6_addr *)rdata->data;
+
+ new_sa->hostname = apr_pstrdup(r->pool, dns->qname);
+
+ new_sa->salen = sizeof(struct sockaddr_in6);
+ new_sa->addr_str_len = 46;
+ new_sa->ipaddr_ptr = &(new_sa->sa.sin6.sin6_addr);
+ new_sa->ipaddr_len = sizeof(struct in6_addr);
+
+ new_sa->sa.sin6.sin6_port = htons(dns->port);
+ new_sa->port = dns->port;
+
+ rdata->rr.aaaa.sockaddr = new_sa;
+
+ break;
+ }
case LDNS_RR_TYPE_TLSA: {
ldns_buffer buffer;
Modified: redwax-tool/trunk/redwax_openssl.c
==============================================================================
--- redwax-tool/trunk/redwax_openssl.c (original)
+++ redwax-tool/trunk/redwax_openssl.c Wed Apr 2 15:10:26 2025
@@ -22,6 +22,7 @@
#include <apr_crypto.h>
#include <apr_lib.h>
+#include <apr_portable.h>
#include <apr_strings.h>
#include "config.h"
@@ -72,6 +73,8 @@
typedef struct {
SSL_CTX *dane_ctx;
SSL *dane_ssl;
+ apr_array_header_t *tls_hosts;
+ int tls_in_flight;
} openssl_config_t;
typedef struct {
@@ -82,6 +85,20 @@
typedef struct {
/* nothing yet */
} openssl_key_config_t;
+
+typedef struct {
+ apr_pool_t *pool;
+ redwax_tls_t *tls;
+ const char *hostname;
+ apr_sockaddr_t *sockaddr;
+ apr_socket_t *socket;
+ apr_pollfd_t socket_read;
+ redwax_pollfd_t read_ctx;
+ apr_pollfd_t socket_write;
+ redwax_pollfd_t write_ctx;
+ SSL *ssl;
+ int do_shutdown;
+} openssl_tls_config_t;
// move to config above
static STACK_OF(X509) *cert_index;
@@ -1129,6 +1146,8 @@
apr_pool_cleanup_register(r->pool, config->dane_ssl, cleanup_SSL,
apr_pool_cleanup_null);
+ config->tls_hosts = apr_array_make(r->pool, 16, sizeof(openssl_tls_config_t *));
+
cert_index = sk_X509_new_null();
chain_index = sk_X509_new_null();
trusted_index = sk_X509_new_null();
@@ -3743,6 +3762,490 @@
}
return APR_SUCCESS;
+}
+
+static apr_status_t tls_connect(redwax_tool_t *r);
+
+static apr_status_t cleanup_tls_in_flight(void *dummy)
+{
+ if (dummy) {
+ openssl_config_t *config = dummy;
+
+ config->tls_in_flight--;
+ }
+
+ return APR_SUCCESS;
+}
+
+static apr_status_t tls_in_flight(redwax_tool_t *r, openssl_tls_config_t *tls_host)
+{
+ openssl_config_t *config = redwax_get_module_config(r->per_module, &openssl_module);
+
+ config->tls_in_flight++;
+
+ apr_pool_cleanup_register(tls_host->pool, config, cleanup_tls_in_flight,
+ apr_pool_cleanup_null);
+
+ return APR_SUCCESS;
+}
+
+static apr_status_t redwax_openssl_handshake_cb(redwax_tool_t *r, openssl_tls_config_t *tls_host)
+{
+ apr_status_t status = APR_SUCCESS;
+
+ int rv, label_len;
+
+ redwax_tls_t *tls = tls_host->tls;
+
+ rv = SSL_do_handshake(tls_host->ssl);
+
+ if (1 == rv || 2 == rv) {
+
+ STACK_OF(X509) *sk;
+
+ int i;
+
+ /* negotiated connection took place */
+
+ sk = SSL_get_peer_cert_chain(tls_host->ssl);
+
+ for (i = 0; i < sk_X509_num(sk); i++) {
+
+ unsigned char *der = NULL;
+ const char *label;
+
+ redwax_certificate_t *cert;
+
+ X509 *x = sk_X509_value(sk, i);
+
+ if (!x) {
+ redwax_print_error(r, "tls-in: could not read certificate from %pI (%s), skipping.\n",
+ tls_host->sockaddr, tls->unparsed_uri);
+ redwax_openssl_print_errors(r);
+ continue;
+ }
+
+ if (X509_check_ca(x)) {
+
+ cert = apr_array_push(r->intermediates_in);
+
+ apr_pool_create(&cert->pool, r->pool);
+
+ cert->common.type = REDWAX_CERTIFICATE_X509;
+ cert->common.category = REDWAX_CERTIFICATE_INTERMEDIATE;
+
+ redwax_print_error(r, "tls-in: intermediate: %s\n",
+ redwax_openssl_name(cert->pool,
+ X509_get_subject_name(x)));
+ }
+ else {
+
+ cert = apr_array_push(r->certs_in);
+
+ apr_pool_create(&cert->pool, r->pool);
+
+ cert->common.type = REDWAX_CERTIFICATE_X509;
+ cert->common.category = REDWAX_CERTIFICATE_END_ENTITY;
+
+ redwax_print_error(r, "tls-in: certificate: %s\n",
+ redwax_openssl_name(cert->pool,
+ X509_get_subject_name(x)));
+
+ }
+
+ cert->per_module = redwax_create_module_config(cert->pool);
+
+ label = (const char *)X509_alias_get0(x, &label_len);
+ cert->label = apr_pstrndup(cert->pool, label, label_len);
+ cert->label_len = label_len;
+
+ cert->len = i2d_X509(x, &der);
+ cert->der = der;
+
+ apr_pool_cleanup_register(cert->pool, der, cleanup_alloc,
+ apr_pool_cleanup_null);
+
+ cert->origin = apr_pstrdup(cert->pool, tls_host->hostname);
+
+ rt_run_normalise_certificate(r, cert, 1);
+
+ }
+
+ tls_host->do_shutdown = 1;
+
+ status = apr_pollcb_add(r->poll, &tls_host->socket_write);
+
+ if (APR_SUCCESS != status) {
+ redwax_print_error(r,
+ "tls-in: could not add write descriptor to poll: %pm\n", &status);
+ return status;
+ }
+
+ /* req writecb work */
+ r->poll_work++;
+
+ }
+ else {
+ switch (SSL_get_error(tls_host->ssl, rv)) {
+ case SSL_ERROR_WANT_WRITE: {
+
+ status = apr_pollcb_add(r->poll, &tls_host->socket_write);
+
+ if (APR_SUCCESS != status) {
+ redwax_print_error(r,
+ "tls-in: could not add write descriptor to poll: %pm\n", &status);
+ return status;
+ }
+
+ /* req writecb work */
+ r->poll_work++;
+
+ break;
+ }
+ case SSL_ERROR_WANT_ASYNC: {
+
+ redwax_print_error(r,
+ "tls-in: unexpected SSL_ERROR_WANT_ASYNC in the bagging area\n");
+ return APR_EGENERAL;
+ }
+ case SSL_ERROR_WANT_READ: {
+
+ status = apr_pollcb_add(r->poll, &tls_host->socket_read);
+
+ if (APR_SUCCESS != status) {
+ redwax_print_error(r,
+ "tls-in: could not add read descriptor to poll: %pm\n", &status);
+ return status;
+ }
+
+ /* req writecb work */
+ r->poll_work++;
+
+ break;
+ }
+ default:
+
+ redwax_print_error(r,
+ "tls-in: TLS handshake of '%s' connection to %pI failed, ingoring.\n",
+ tls->unparsed_uri, tls_host->sockaddr);
+ redwax_openssl_print_errors(r);
+
+ return APR_SUCCESS;
+ }
+ }
+
+ return status;
+}
+
+static apr_status_t redwax_openssl_shutdown_cb(redwax_tool_t *r, openssl_tls_config_t *tls_host)
+{
+ apr_status_t status = APR_SUCCESS;
+
+ int rv;
+
+ rv = SSL_shutdown(tls_host->ssl);
+
+ if (0 == rv) {
+
+ /* not yet done, wait for the response */
+ status = apr_pollcb_add(r->poll, &tls_host->socket_read);
+
+ if (APR_SUCCESS != status) {
+ redwax_print_error(r,
+ "tls-in: could not add read descriptor to poll: %pm\n", &status);
+ return status;
+ }
+
+ /* req writecb work */
+ r->poll_work++;
+
+ }
+ else if (1 == rv) {
+
+ /* we're done */
+ apr_pool_destroy(tls_host->pool);
+
+ /* line up the next connection(s) */
+ return tls_connect(r);
+ }
+ else {
+
+ redwax_tls_t *tls = tls_host->tls;
+
+ switch (SSL_get_error(tls_host->ssl, rv)) {
+ case SSL_ERROR_WANT_WRITE: {
+
+ status = apr_pollcb_add(r->poll, &tls_host->socket_write);
+
+ if (APR_SUCCESS != status) {
+ redwax_print_error(r,
+ "tls-in: could not add write descriptor to poll: %pm\n", &status);
+ return status;
+ }
+
+ /* req writecb work */
+ r->poll_work++;
+
+ break;
+ }
+ case SSL_ERROR_WANT_ASYNC: {
+
+ redwax_print_error(r,
+ "tls-in: unexpected SSL_ERROR_WANT_ASYNC in the bagging area\n");
+ return APR_EGENERAL;
+ }
+ case SSL_ERROR_WANT_READ: {
+
+ status = apr_pollcb_add(r->poll, &tls_host->socket_read);
+
+ if (APR_SUCCESS != status) {
+ redwax_print_error(r,
+ "tls-in: could not add read descriptor to poll: %pm\n", &status);
+ return status;
+ }
+
+ /* req writecb work */
+ r->poll_work++;
+
+ break;
+ }
+ default:
+
+ redwax_print_error(r,
+ "tls-in: shutdown of '%s' connection to %pI failed, ingoring.\n",
+ tls->unparsed_uri, tls_host->sockaddr);
+ redwax_openssl_print_errors(r);
+
+ return APR_SUCCESS;
+ }
+ }
+
+ return status;
+}
+
+static apr_status_t redwax_openssl_filter_read_cb(redwax_tool_t *r, void *baton, apr_pollfd_t *descriptor)
+{
+
+ redwax_pollfd_t *read_ctx = descriptor->client_data;
+ openssl_tls_config_t *tls_host = read_ctx->ctx;
+
+ /* ack readcb work */
+ r->poll_work--;
+
+ if (tls_host->do_shutdown) {
+ return redwax_openssl_shutdown_cb(r, tls_host);
+ }
+
+ return redwax_openssl_handshake_cb(r, tls_host);
+}
+
+static apr_status_t redwax_openssl_filter_write_cb(redwax_tool_t *r, void *baton, apr_pollfd_t *descriptor)
+{
+
+ redwax_pollfd_t *write_ctx = descriptor->client_data;
+ openssl_tls_config_t *tls_host = write_ctx->ctx;
+ redwax_tls_t *tls = tls_host->tls;
+
+ apr_status_t status = APR_SUCCESS;
+
+ /* ack readcb work */
+ r->poll_work--;
+
+ if (!tls_host->ssl) {
+
+ SSL_CTX *ctx;
+
+ apr_os_sock_t fd;
+
+ status = apr_os_sock_get(&fd, tls_host->socket);
+
+ if (APR_SUCCESS != status) {
+ redwax_print_error(r,
+ "tls-in: could not convert SSL fd: %pm\n", &status);
+ return status;
+ }
+
+ if (tls->protocol == APR_PROTO_TCP) {
+ ctx = SSL_CTX_new(TLS_client_method());
+ }
+ else {
+ ctx = SSL_CTX_new(DTLS_client_method());
+ }
+
+ apr_pool_cleanup_register(tls_host->pool, ctx, cleanup_SSL_CTX,
+ apr_pool_cleanup_null);
+
+ tls_host->ssl = SSL_new(ctx);
+
+ apr_pool_cleanup_register(tls_host->pool, tls_host->ssl, cleanup_SSL,
+ apr_pool_cleanup_null);
+
+ SSL_set_connect_state(tls_host->ssl);
+
+ SSL_set_verify(tls_host->ssl, SSL_VERIFY_NONE, NULL);
+
+ if (SSL_set_fd(tls_host->ssl, fd) <= 0) {
+ redwax_openssl_print_errors(r);
+ return APR_EGENERAL;
+ }
+
+ if (SSL_set1_host(tls_host->ssl, tls_host->hostname) <= 0) {
+ redwax_openssl_print_errors(r);
+ return APR_EGENERAL;
+ }
+
+ if (SSL_set_tlsext_host_name(tls_host->ssl, tls_host->hostname) <= 0) {
+ redwax_openssl_print_errors(r);
+ return APR_EGENERAL;
+ }
+
+ /* todo: SSL_set_alpn_protos */
+ }
+
+ if (tls_host->do_shutdown) {
+ return redwax_openssl_shutdown_cb(r, tls_host);
+ }
+
+ return redwax_openssl_handshake_cb(r, tls_host);
+}
+
+static apr_status_t tls_connect(redwax_tool_t *r)
+{
+ openssl_config_t *config = redwax_get_module_config(r->per_module, &openssl_module);
+
+ apr_status_t status;
+
+ while (config->tls_hosts->nelts && config->tls_in_flight < 2) {
+
+ openssl_tls_config_t **ts = apr_array_pop(config->tls_hosts);
+ openssl_tls_config_t *tls_host = *ts;
+
+ tls_in_flight(r, tls_host);
+
+ status = apr_socket_create(&tls_host->socket, tls_host->sockaddr->family, SOCK_STREAM, APR_PROTO_TCP,
+ tls_host->pool);
+
+ status = apr_socket_connect(tls_host->socket, tls_host->sockaddr);
+
+ status = apr_socket_opt_set(tls_host->socket, APR_SO_NONBLOCK, 1);
+
+ /* set up the read callback */
+ tls_host->read_ctx.cb = redwax_openssl_filter_read_cb;
+ tls_host->read_ctx.ctx = tls_host;
+
+ /* set up read descriptor */
+ tls_host->socket_read.desc_type = APR_POLL_SOCKET;
+ tls_host->socket_read.reqevents = APR_POLLIN;
+ tls_host->socket_read.desc.s = tls_host->socket;
+ tls_host->socket_read.client_data = &tls_host->read_ctx;
+
+ /* set up the write callback */
+ tls_host->write_ctx.cb = redwax_openssl_filter_write_cb;
+ tls_host->write_ctx.ctx = tls_host;
+
+ /* set up write descriptor */
+ tls_host->socket_write.desc_type = APR_POLL_SOCKET;
+ tls_host->socket_write.reqevents = APR_POLLOUT;
+ tls_host->socket_write.desc.s = tls_host->socket;
+ tls_host->socket_write.client_data = &tls_host->write_ctx;
+
+ status = apr_pollcb_add(r->poll, &tls_host->socket_write);
+
+ if (APR_SUCCESS != status) {
+ redwax_print_error(r,
+ "tls-in: could not add write descriptor to poll: %pm\n", &status);
+ return status;
+ }
+
+ /* req writecb work */
+ r->poll_work++;
+
+ }
+
+ return APR_SUCCESS;
+}
+
+static apr_status_t redwax_openssl_process_tls_in(redwax_tool_t *r, redwax_dns_t *dns)
+{
+ openssl_config_t *config = redwax_get_module_config(r->per_module, &openssl_module);
+
+ if (dns->bogus) {
+
+ redwax_print_error(r,
+ "tls-in: DNS response for '%s' failed "
+ "DNSSEC validation, ignoring: %s.\n", dns->qname, dns->why_bogus);
+
+ r->tls_bogus++;
+
+ return APR_SUCCESS;
+ }
+
+ if (!dns->secure) {
+
+ redwax_print_error(r,
+ "tls-in: warning: DNS response for '%s' is "
+ "not DNSSEC secured.\n", dns->qname);
+
+ r->tls_insecure++;
+ }
+
+ if (dns->nxdomain) {
+
+ redwax_print_error(r,
+ "tls-in: DNS record for '%s' does "
+ "not exist, ignoring.\n", dns->qname);
+
+ r->tls_nxdomain++;
+
+ return APR_SUCCESS;
+ }
+
+ if (dns->rdata) {
+
+ int i;
+
+ redwax_tls_t *tls = dns->ctx;
+
+ for (i = 0; i < dns->rdata->nelts; i++)
+ {
+ apr_pool_t *pool;
+ openssl_tls_config_t *tls_host;
+
+ const redwax_rdata_t *rdata = &APR_ARRAY_IDX(dns->rdata, i,
+ const redwax_rdata_t);
+
+ openssl_tls_config_t **ts = apr_array_push(config->tls_hosts);
+
+ apr_pool_create(&pool, r->pool);
+
+ *ts = tls_host = apr_pcalloc(pool, sizeof(openssl_tls_config_t));
+
+ tls_host->pool = pool;
+
+ switch (dns->rrtype) {
+ case 1:
+ tls_host->sockaddr = rdata->rr.a.sockaddr;
+ tls_host->hostname = dns->qname;
+ break;
+ case 28:
+ tls_host->sockaddr = rdata->rr.aaaa.sockaddr;
+ tls_host->hostname = dns->qname;
+ break;
+ default:
+ return APR_EINVAL;
+ }
+
+ tls_host->tls = tls;
+
+ redwax_print_error(r,
+ "tls-in: DNS record for '%s' maps to %pI.\n",
+ tls->unparsed_uri, tls_host->sockaddr);
+
+ }
+
+ }
+
+ return tls_connect(r);
}
static apr_status_t redwax_openssl_process_pkcs12_out(redwax_tool_t *r,
@@ -6398,6 +6901,7 @@
rt_hook_process_pem_in(redwax_openssl_process_pem_in, NULL, NULL, APR_HOOK_MIDDLE);
rt_hook_process_trust_pem_in(redwax_openssl_process_trust_pem_in, NULL, NULL, APR_HOOK_MIDDLE);
rt_hook_process_pkcs12_in(redwax_openssl_process_pkcs12_in, NULL, NULL, APR_HOOK_MIDDLE);
+ rt_hook_process_tls_in(redwax_openssl_process_tls_in, NULL, NULL, APR_HOOK_MIDDLE);
rt_hook_set_tlsa(redwax_openssl_set_tlsa, NULL, NULL, APR_HOOK_LAST);
rt_hook_process_tlsa(redwax_openssl_process_tlsa, NULL, NULL, APR_HOOK_MIDDLE);
rt_hook_complete_filter(redwax_openssl_complete_filter_verify, NULL, NULL, APR_HOOK_MIDDLE);
Modified: redwax-tool/trunk/redwax_unbound.c
==============================================================================
--- redwax-tool/trunk/redwax_unbound.c (original)
+++ redwax-tool/trunk/redwax_unbound.c Wed Apr 2 15:10:26 2025
@@ -41,12 +41,9 @@
typedef struct {
struct ub_ctx *ctx;
- apr_pollfd_t socket_read;
- redwax_pollfd_t read_ctx;
- apr_pollfd_t socket_write;
- redwax_pollfd_t write_ctx;
+ apr_pollfd_t socket_poll;
+ redwax_pollfd_t poll_ctx;
apr_status_t status;
- int socket_read_work;
int dns_work;
int dns_server_set;
int dns_trust_anchor_set;
@@ -162,6 +159,7 @@
dns->rrtype = 52 /* TYPE TLSA */;
dns->rrclass = 1 /* CLASS IN (internet) */;
dns->cb = rt_run_process_tlsa;
+ dns->port = uri.port;
qname = apr_array_push(r->tlsa_qnames);
*qname = tlsa;
@@ -201,6 +199,7 @@
dns->rrtype = 52 /* TYPE TLSA */;
dns->rrclass = 1 /* CLASS IN (internet) */;
dns->cb = rt_run_process_tlsa;
+ dns->port = uri.port ? uri.port : ntohs(ent->s_port);
qname = apr_array_push(r->tlsa_qnames);
*qname = tlsa;
@@ -237,6 +236,164 @@
return APR_ENOTIMPL;
}
+static apr_status_t redwax_unbound_set_tls_in(redwax_tool_t *r, const char *arg)
+{
+ apr_uri_t uri;
+
+ apr_status_t status;
+
+ int protocol = 0;
+ int type = 0;
+
+ status = apr_uri_parse(r->pool, arg, &uri);
+ if (APR_SUCCESS != status) {
+ redwax_print_error(r,
+ "Could not parse URI '%s': %pm\n", arg, &status);
+ return status;
+ }
+
+ if (!uri.hostname) {
+ redwax_print_error(r,
+ "URI '%s': hostname missing\n", arg);
+ return APR_EINVAL;
+ }
+
+ /* fixme: Use inet_pton to detect raw IP/IPv6 addresses and skip lookup */
+
+ if (!strcmp(uri.scheme, "tcp")) {
+ protocol = APR_PROTO_TCP;
+ type = SOCK_STREAM;
+ }
+ else if (!strcmp(uri.scheme, "udp")) {
+ protocol = APR_PROTO_UDP;
+ type = SOCK_DGRAM;
+ }
+ else if (!strcmp(uri.scheme, "sctp")) {
+ protocol = APR_PROTO_SCTP;
+ type= SOCK_SEQPACKET;
+ }
+
+ if (protocol) {
+
+ if (uri.port) {
+
+ redwax_dns_t *dns;
+ redwax_tls_t *tls;
+
+ tls = apr_palloc(r->pool, sizeof(redwax_tls_t));
+
+ tls->unparsed_uri = arg;
+ tls->uri = uri;
+ tls->protocol = protocol;
+ tls->type = type;
+
+ dns = apr_array_push(r->dns_requests);
+
+ dns->r = r;
+ dns->basename = uri.hostname;
+ dns->qname = uri.hostname;
+ dns->rrtype = 28 /* TYPE AAAA */;
+ dns->rrclass = 1 /* CLASS IN (internet) */;
+ dns->cb = rt_run_process_tls_in;
+ dns->ctx = tls;
+ dns->port = uri.port;
+
+ dns = apr_array_push(r->dns_requests);
+
+ dns->r = r;
+ dns->basename = uri.hostname;
+ dns->qname = uri.hostname;
+ dns->rrtype = 1 /* TYPE A */;
+ dns->rrclass = 1 /* CLASS IN (internet) */;
+ dns->cb = rt_run_process_tls_in;
+ dns->ctx = tls;
+ dns->port = uri.port;
+
+ return APR_SUCCESS;
+ }
+ else {
+ redwax_print_error(r,
+ "URI '%s': port missing\n", arg);
+ return APR_EINVAL;
+ }
+ }
+
+ else if (uri.scheme) {
+
+ int found = 0;
+
+ setservent(1);
+
+ struct servent *ent;
+
+ while ((ent = getservent())) {
+
+ if (!strcmp(uri.scheme, ent->s_name)) {
+
+ redwax_dns_t *dns;
+ redwax_tls_t *tls;
+
+ tls = apr_palloc(r->pool, sizeof(redwax_tls_t));
+
+ /* we hard code the protocol to TCP, as the services
+ * file contacts lots of bogus tcp/udp entries.
+ */
+
+ tls->unparsed_uri = arg;
+ tls->uri = uri;
+ tls->protocol = APR_PROTO_TCP;
+ tls->type = SOCK_STREAM;
+
+ dns = apr_array_push(r->dns_requests);
+
+ dns->r = r;
+ dns->basename = uri.hostname;
+ dns->qname = uri.hostname;
+ dns->rrtype = 28 /* TYPE AAAA */;
+ dns->rrclass = 1 /* CLASS IN (internet) */;
+ dns->cb = rt_run_process_tls_in;
+ dns->ctx = tls;
+ dns->port = uri.port ? uri.port : ntohs(ent->s_port);
+
+ dns = apr_array_push(r->dns_requests);
+
+ dns->r = r;
+ dns->basename = uri.hostname;
+ dns->qname = uri.hostname;
+ dns->rrtype = 1 /* TYPE A */;
+ dns->rrclass = 1 /* CLASS IN (internet) */;
+ dns->cb = rt_run_process_tls_in;
+ dns->ctx = tls;
+ dns->port = uri.port ? uri.port : ntohs(ent->s_port);
+
+ found = 1;
+
+ break;
+ }
+
+ }
+
+ endservent();
+
+ if (!found) {
+ redwax_print_error(r,
+ "URI '%s': protocol '%s' not found\n", arg, uri.scheme);
+ return APR_EINVAL;
+ }
+ else {
+ return APR_SUCCESS;
+ }
+
+ }
+
+ else {
+ redwax_print_error(r,
+ "URI '%s': scheme missing\n", arg);
+ return APR_EINVAL;
+ }
+
+}
+
static apr_status_t redwax_unbound_filter_read_cb(redwax_tool_t *r, void *baton, apr_pollfd_t *descriptor)
{
unbound_config_t *config = redwax_get_module_config(r->per_module, &unbound_module);
@@ -246,9 +403,6 @@
/* ack readcb work */
r->poll_work--;
-
- /* ack socket read work */
- config->socket_read_work--;
rv = ub_process(config->ctx);
@@ -265,19 +419,17 @@
if (config->dns_work) {
- status = apr_pollcb_add(r->poll, &config->socket_read);
+ /* line up next event */
+ status = apr_pollcb_add(r->poll, &config->socket_poll);
if (APR_SUCCESS != status) {
redwax_print_error(r,
- "Could not add read descriptor to poll: %pm\n", &status);
+ "unbound: could not add descriptor to poll: %pm\n", &status);
return status;
}
/* req readcb work */
r->poll_work++;
-
- /* req socket read work */
- config->socket_read_work++;
}
return status;
@@ -361,40 +513,42 @@
/* req dns lookup work */
config->dns_work++;
- if (!config->socket_read_work) {
-
- status = apr_pollcb_add(r->poll, &config->socket_read);
-
- if (APR_SUCCESS != status) {
- redwax_print_error(r,
- "Could not add read descriptor to poll: %pm\n", &status);
- return status;
- }
-
- /* req readcb work */
- r->poll_work++;
-
- /* req socket read work */
- config->socket_read_work++;
-
- }
-
- if (r->dns_requests->nelts) {
-
- status = apr_pollcb_add(r->poll, &config->socket_write);
-
- if (APR_SUCCESS != status) {
- redwax_print_error(r,
- "Could not add write descriptor to poll: %pm\n", &status);
- return status;
- }
-
- /* req writecb work */
- r->poll_work++;
-
- }
+ /* we are done sending requests, poll for responses only */
+ if (!r->dns_requests->nelts) {
+ config->socket_poll.reqevents = APR_POLLIN;
+ }
+
+ /* line up next event */
+ status = apr_pollcb_add(r->poll, &config->socket_poll);
+
+ if (APR_SUCCESS != status) {
+ redwax_print_error(r,
+ "Could not add read descriptor to poll: %pm\n", &status);
+ return status;
+ }
+
+ /* req readcb work */
+ r->poll_work++;
return status;
+}
+
+static apr_status_t redwax_unbound_filter_cb(redwax_tool_t *r, void *baton, apr_pollfd_t *descriptor)
+{
+ if (descriptor->rtnevents & APR_POLLIN) {
+ /* process reads first if available to relieve pressure */
+ return redwax_unbound_filter_read_cb(r, baton, descriptor);
+ }
+ else if (descriptor->rtnevents & APR_POLLOUT) {
+ /* do writes if needed */
+ return redwax_unbound_filter_write_cb(r, baton, descriptor);
+ }
+ else {
+ /* errors are handled by ub_process() */
+ return redwax_unbound_filter_read_cb(r, baton, descriptor);
+ }
+
+ return APR_SUCCESS;
}
static apr_status_t redwax_unbound_filter_poll(redwax_tool_t *r)
@@ -458,25 +612,17 @@
return status;
}
- /* set up the read callback */
- config->read_ctx.cb = redwax_unbound_filter_read_cb;
-
- /* set up read descriptor */
- config->socket_read.desc_type = APR_POLL_SOCKET;
- config->socket_read.reqevents = APR_POLLIN;
- config->socket_read.desc.s = s;
- config->socket_read.client_data = &config->read_ctx;
-
- /* set up the write callback */
- config->write_ctx.cb = redwax_unbound_filter_write_cb;
-
- /* set up write descriptor */
- config->socket_write.desc_type = APR_POLL_SOCKET;
- config->socket_write.reqevents = APR_POLLOUT;
- config->socket_write.desc.s = s;
- config->socket_write.client_data = &config->write_ctx;
-
- status = apr_pollcb_add(r->poll, &config->socket_write);
+ /* set up the read/write callback */
+
+ config->poll_ctx.cb = redwax_unbound_filter_cb;
+
+ /* set up read/write descriptor */
+ config->socket_poll.desc_type = APR_POLL_SOCKET;
+ config->socket_poll.reqevents = APR_POLLIN | APR_POLLOUT;
+ config->socket_poll.desc.s = s;
+ config->socket_poll.client_data = &config->poll_ctx;
+
+ status = apr_pollcb_add(r->poll, &config->socket_poll);
if (APR_SUCCESS != status) {
redwax_print_error(r,
@@ -497,6 +643,7 @@
rt_hook_set_dns_trust_anchor(redwax_unbound_set_dns_trust_anchor, NULL, NULL, APR_HOOK_MIDDLE);
rt_hook_set_tlsa(redwax_unbound_set_tlsa, NULL, NULL, APR_HOOK_MIDDLE);
rt_hook_set_smimea(redwax_unbound_set_smimea, NULL, NULL, APR_HOOK_MIDDLE);
+ rt_hook_set_tls_in(redwax_unbound_set_tls_in, NULL, NULL, APR_HOOK_MIDDLE);
rt_hook_filter_poll(redwax_unbound_filter_poll, NULL, NULL, APR_HOOK_MIDDLE);
}
More information about the rt-commit
mailing list