You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by ic...@apache.org on 2021/11/30 16:29:21 UTC

svn commit: r1895432 [4/5] - in /httpd/httpd/trunk: changes-entries/ modules/tls/

Added: httpd/httpd/trunk/modules/tls/tls_filter.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/tls/tls_filter.c?rev=1895432&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/tls/tls_filter.c (added)
+++ httpd/httpd/trunk/modules/tls/tls_filter.c Tue Nov 30 16:29:20 2021
@@ -0,0 +1,1017 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <assert.h>
+#include <apr_lib.h>
+#include <apr_strings.h>
+
+#include <httpd.h>
+#include <http_connection.h>
+#include <http_core.h>
+#include <http_request.h>
+#include <http_log.h>
+#include <ap_socache.h>
+
+#include <rustls.h>
+
+#include "tls_proto.h"
+#include "tls_conf.h"
+#include "tls_core.h"
+#include "tls_filter.h"
+#include "tls_util.h"
+
+
+extern module AP_MODULE_DECLARE_DATA tls_module;
+APLOG_USE_MODULE(tls);
+
+
+static rustls_io_result tls_read_callback(
+    void *userdata, unsigned char *buf, size_t n, size_t *out_n)
+{
+    tls_data_t *d = userdata;
+    size_t len = d->len > n? n : d->len;
+    memcpy(buf, d->data, len);
+    *out_n = len;
+    return 0;
+}
+
+/**
+ * Provide TLS encrypted data to the rustls server_session in <fctx->cc->rustls_connection>.
+ *
+ * If <fctx->fin_tls_bb> holds data, take it from there. Otherwise perform a
+ * read via the network filters below us into that brigade.
+ *
+ * <fctx->fin_block> determines if we do a blocking read inititally or not.
+ * If the first read did to not produce enough data, any secondary read is done
+ * non-blocking.
+ *
+ * Had any data been added to <fctx->cc->rustls_connection>, call its "processing"
+ * function to handle the added data before leaving.
+ */
+static apr_status_t read_tls_to_rustls(
+    tls_filter_ctx_t *fctx, apr_size_t len, apr_read_type_e block, int errors_expected)
+{
+    tls_data_t d;
+    apr_size_t rlen;
+    apr_off_t passed = 0;
+    rustls_result rr = RUSTLS_RESULT_OK;
+    int os_err;
+    apr_status_t rv = APR_SUCCESS;
+
+    if (APR_BRIGADE_EMPTY(fctx->fin_tls_bb)) {
+        ap_log_error(APLOG_MARK, APLOG_TRACE2, rv, fctx->cc->server,
+            "read_tls_to_rustls, get data from network, block=%d", block);
+        rv = ap_get_brigade(fctx->fin_ctx->next, fctx->fin_tls_bb,
+                            AP_MODE_READBYTES, block, (apr_off_t)len);
+        if (APR_SUCCESS != rv) {
+            goto cleanup;
+        }
+    }
+
+    while (!APR_BRIGADE_EMPTY(fctx->fin_tls_bb) && passed < (apr_off_t)len) {
+        apr_bucket *b = APR_BRIGADE_FIRST(fctx->fin_tls_bb);
+
+        if (APR_BUCKET_IS_EOS(b)) {
+            ap_log_error(APLOG_MARK, APLOG_TRACE2, rv, fctx->cc->server,
+                "read_tls_to_rustls, EOS");
+            if (fctx->fin_tls_buffer_bb) {
+                apr_brigade_cleanup(fctx->fin_tls_buffer_bb);
+            }
+            rv = APR_EOF; goto cleanup;
+        }
+
+        rv = apr_bucket_read(b, (const char**)&d.data, &d.len, block);
+        if (APR_STATUS_IS_EOF(rv)) {
+            apr_bucket_delete(b);
+            continue;
+        }
+        else if (APR_SUCCESS != rv) {
+            goto cleanup;
+        }
+
+        if (d.len > 0) {
+            /* got something, do not block on getting more */
+            block = APR_NONBLOCK_READ;
+
+            os_err = rustls_connection_read_tls(fctx->cc->rustls_connection,
+                                tls_read_callback, &d, &rlen);
+            if (os_err) {
+                rv = APR_FROM_OS_ERROR(os_err);
+                goto cleanup;
+            }
+
+            if (fctx->fin_tls_buffer_bb) {
+                /* we buffer for later replay on the 'real' rustls_connection */
+                apr_brigade_write(fctx->fin_tls_buffer_bb, NULL, NULL, (const char*)d.data, rlen);
+            }
+            if (rlen >= d.len) {
+                apr_bucket_delete(b);
+            }
+            else {
+                b->start += (apr_off_t)rlen;
+                b->length -= rlen;
+            }
+            fctx->fin_bytes_in_rustls += (apr_off_t)d.len;
+            passed += (apr_off_t)rlen;
+        }
+        else if (d.len == 0) {
+            apr_bucket_delete(b);
+        }
+    }
+
+    if (passed > 0) {
+        rr = rustls_connection_process_new_packets(fctx->cc->rustls_connection);
+        if (rr != RUSTLS_RESULT_OK) goto cleanup;
+    }
+
+cleanup:
+    if (rr != RUSTLS_RESULT_OK) {
+        rv = APR_ECONNRESET;
+        if (!errors_expected) {
+            const char *err_descr = "";
+            rv = tls_core_error(fctx->c, rr, &err_descr);
+            ap_log_cerror(APLOG_MARK, APLOG_WARNING, rv, fctx->c, APLOGNO(10353)
+                         "processing TLS data: [%d] %s", (int)rr, err_descr);
+        }
+    }
+    else if (APR_STATUS_IS_EOF(rv) && passed > 0) {
+        /* encountering EOF while actually having read sth is a success. */
+        rv = APR_SUCCESS;
+    }
+    else if (APR_SUCCESS == rv && passed == 0 && fctx->fin_block == APR_NONBLOCK_READ) {
+        rv = APR_EAGAIN;
+    }
+    else {
+        ap_log_error(APLOG_MARK, APLOG_TRACE2, rv, fctx->cc->server,
+            "read_tls_to_rustls, passed %ld bytes to rustls", (long)passed);
+    }
+    return rv;
+}
+
+static apr_status_t fout_pass_tls_to_net(tls_filter_ctx_t *fctx)
+{
+    apr_status_t rv = APR_SUCCESS;
+
+    if (!APR_BRIGADE_EMPTY(fctx->fout_tls_bb)) {
+        rv = ap_pass_brigade(fctx->fout_ctx->next, fctx->fout_tls_bb);
+        if (APR_SUCCESS == rv && fctx->c->aborted) {
+            rv = APR_ECONNRESET;
+        }
+        fctx->fout_bytes_in_tls_bb = 0;
+        apr_brigade_cleanup(fctx->fout_tls_bb);
+    }
+    return rv;
+}
+
+static apr_status_t fout_pass_all_to_net(
+    tls_filter_ctx_t *fctx, int flush);
+
+static apr_status_t filter_abort(
+    tls_filter_ctx_t *fctx)
+{
+    apr_status_t rv;
+
+    if (fctx->cc->state != TLS_CONN_ST_DONE) {
+        if (fctx->cc->state > TLS_CONN_ST_CLIENT_HELLO) {
+            rustls_connection_send_close_notify(fctx->cc->rustls_connection);
+            rv = fout_pass_all_to_net(fctx, 1);
+            ap_log_cerror(APLOG_MARK, APLOG_TRACE2, rv, fctx->c, "filter_abort, flushed output");
+        }
+        fctx->c->aborted = 1;
+        fctx->cc->state = TLS_CONN_ST_DONE;
+    }
+    return APR_ECONNABORTED;
+}
+
+static apr_status_t filter_recv_client_hello(tls_filter_ctx_t *fctx)
+{
+    apr_status_t rv = APR_SUCCESS;
+
+    ap_log_error(APLOG_MARK, APLOG_TRACE2, 0, fctx->cc->server,
+        "tls_filter, server=%s, recv client hello", fctx->cc->server->server_hostname);
+    /* only for incoming connections */
+    ap_assert(!fctx->cc->outgoing);
+
+    if (rustls_connection_is_handshaking(fctx->cc->rustls_connection)) {
+        apr_bucket_brigade *bb_tmp;
+
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE2, rv, fctx->c, "filter_recv_client_hello: start");
+        fctx->fin_tls_buffer_bb = apr_brigade_create(fctx->c->pool, fctx->c->bucket_alloc);
+        do {
+            if (rustls_connection_wants_read(fctx->cc->rustls_connection)) {
+                rv = read_tls_to_rustls(fctx, fctx->fin_max_in_rustls, APR_BLOCK_READ, 1);
+                if (APR_SUCCESS != rv) {
+                    if (fctx->cc->client_hello_seen) {
+                        rv = APR_EAGAIN;  /* we got what we needed */
+                        break;
+                    }
+                    /* Something went wrong before we saw the client hello.
+                     * This is a real error on which we should not continue. */
+                    goto cleanup;
+                }
+            }
+            /* Notice: we never write here to the client. We just want to inspect
+             * the client hello. */
+        } while (!fctx->cc->client_hello_seen);
+
+        /* We have seen the client hello and selected the server (vhost) to use
+         * on this connection. Set up the 'real' rustls_connection based on the
+         * servers 'real' rustls_config. */
+        rv = tls_core_conn_seen_client_hello(fctx->c);
+        if (APR_SUCCESS != rv) goto cleanup;
+
+        bb_tmp = fctx->fin_tls_bb; /* data we have yet to feed to rustls */
+        fctx->fin_tls_bb = fctx->fin_tls_buffer_bb; /* data we already fed to the pre_session */
+        fctx->fin_tls_buffer_bb = NULL;
+        APR_BRIGADE_CONCAT(fctx->fin_tls_bb, bb_tmp); /* all tls data from the cleint so far, reloaded */
+        apr_brigade_destroy(bb_tmp);
+        rv = APR_SUCCESS;
+    }
+
+cleanup:
+    ap_log_cerror(APLOG_MARK, APLOG_TRACE2, rv, fctx->c, "filter_recv_client_hello: done");
+    return rv;
+}
+
+static apr_status_t filter_send_client_hello(tls_filter_ctx_t *fctx)
+{
+    apr_status_t rv = APR_SUCCESS;
+
+    ap_log_error(APLOG_MARK, APLOG_TRACE2, 0, fctx->cc->server,
+        "tls_filter, server=%s, send client hello", fctx->cc->server->server_hostname);
+    /* Only for outgoing connections */
+    ap_assert(fctx->cc->outgoing);
+    if (rustls_connection_is_handshaking(fctx->cc->rustls_connection)) {
+        while (rustls_connection_wants_write(fctx->cc->rustls_connection)) {
+            /* write flushed, so it really gets out */
+            rv = fout_pass_all_to_net(fctx, 1);
+            if (APR_SUCCESS != rv) goto cleanup;
+        }
+    }
+
+cleanup:
+    return rv;
+}
+
+/**
+ * While <fctx->cc->rustls_connection> indicates that a handshake is ongoing,
+ * write TLS data from and read network TLS data to the server session.
+ *
+ * @return APR_SUCCESS when the handshake is completed
+ */
+static apr_status_t filter_do_handshake(
+    tls_filter_ctx_t *fctx)
+{
+    apr_status_t rv = APR_SUCCESS;
+
+    ap_log_error(APLOG_MARK, APLOG_TRACE2, 0, fctx->cc->server,
+        "tls_filter, server=%s, do handshake", fctx->cc->server->server_hostname);
+    if (rustls_connection_is_handshaking(fctx->cc->rustls_connection)) {
+        do {
+            if (rustls_connection_wants_write(fctx->cc->rustls_connection)) {
+                rv = fout_pass_all_to_net(fctx, 1);
+                if (APR_SUCCESS != rv) goto cleanup;
+            }
+            else if (rustls_connection_wants_read(fctx->cc->rustls_connection)) {
+                rv = read_tls_to_rustls(fctx, fctx->fin_max_in_rustls, APR_BLOCK_READ, 0);
+                if (APR_SUCCESS != rv) goto cleanup;
+            }
+        }
+        while (rustls_connection_is_handshaking(fctx->cc->rustls_connection));
+
+        /* rustls reports the TLS handshake to be done, when it *internally* has
+         * processed everything into its buffers. Not when the buffers have been
+         * send to the other side. */
+        if (rustls_connection_wants_write(fctx->cc->rustls_connection)) {
+            rv = fout_pass_all_to_net(fctx, 1);
+            if (APR_SUCCESS != rv) goto cleanup;
+        }
+    }
+cleanup:
+    ap_log_error(APLOG_MARK, APLOG_TRACE2, rv, fctx->cc->server,
+        "tls_filter, server=%s, handshake done", fctx->cc->server->server_hostname);
+    if (APR_SUCCESS != rv) {
+        if (fctx->cc->last_error_descr) {
+            ap_log_cerror(APLOG_MARK, APLOG_INFO, APR_ECONNABORTED, fctx->c, APLOGNO(10354)
+                "handshake failed: %s", fctx->cc->last_error_descr);
+        }
+    }
+    return rv;
+}
+
+static apr_status_t progress_tls_atleast_to(tls_filter_ctx_t *fctx, tls_conn_state_t state)
+{
+    apr_status_t rv = APR_SUCCESS;
+
+    /* handle termination immediately */
+    if (state == TLS_CONN_ST_DONE) {
+        rv = APR_ECONNABORTED;
+        goto cleanup;
+    }
+
+    if (state > TLS_CONN_ST_CLIENT_HELLO
+        && TLS_CONN_ST_CLIENT_HELLO == fctx->cc->state) {
+        rv = tls_core_conn_init(fctx->c);
+        if (APR_SUCCESS != rv) goto cleanup;
+
+        if (fctx->cc->outgoing) {
+            rv = filter_send_client_hello(fctx);
+        }
+        else {
+            rv = filter_recv_client_hello(fctx);
+        }
+        if (APR_SUCCESS != rv) goto cleanup;
+        fctx->cc->state = TLS_CONN_ST_HANDSHAKE;
+    }
+
+    if (state > TLS_CONN_ST_HANDSHAKE
+        && TLS_CONN_ST_HANDSHAKE== fctx->cc->state) {
+        rv = filter_do_handshake(fctx);
+        if (APR_SUCCESS != rv) goto cleanup;
+        rv = tls_core_conn_post_handshake(fctx->c);
+        if (APR_SUCCESS != rv) goto cleanup;
+        fctx->cc->state = TLS_CONN_ST_TRAFFIC;
+    }
+
+    if (state < fctx->cc->state) {
+        rv = APR_ECONNABORTED;
+    }
+
+cleanup:
+    if (APR_SUCCESS != rv) {
+        filter_abort(fctx); /* does change the state itself */
+    }
+    return rv;
+}
+
+/**
+ * The connection filter converting TLS encrypted network data into plain, unencrpyted
+ * traffic data to be processed by filters above it in the filter chain.
+ *
+ * Unfortunately, Apache's filter infrastructure places a heavy implementation
+ * complexity on its input filters for the various use cases its HTTP/1.x parser
+ * (mainly) finds convenient:
+ *
+ * <bb>      the bucket brigade to place the data into.
+ * <mode>    one of
+ *     - AP_MODE_READBYTES: just add up to <readbytes> data into <bb>
+ *     - AP_MODE_GETLINE: make a best effort to get data up to and including a CRLF.
+ *                        it can be less, but not more t than that.
+ *     - AP_MODE_EATCRLF: never used, we puke on it.
+ *     - AP_MODE_SPECULATIVE: read data without consuming it.
+ *     - AP_MODE_EXHAUSTIVE: never used, we puke on it.
+ *     - AP_MODE_INIT: called once on a connection. needs to pass down the filter
+ *                      chain, giving every filter the change to "INIT".
+ * <block>   do blocking or non-blocking reads
+ * <readbytes> max amount of data to add to <bb>, seems to be 0 for GETLINE
+ */
+static apr_status_t filter_conn_input(
+    ap_filter_t *f, apr_bucket_brigade *bb, ap_input_mode_t mode,
+    apr_read_type_e block, apr_off_t readbytes)
+{
+    tls_filter_ctx_t *fctx = f->ctx;
+    apr_status_t rv = APR_SUCCESS;
+    apr_off_t passed = 0, nlen;
+    rustls_result rr = RUSTLS_RESULT_OK;
+    apr_size_t in_buf_len;
+    char *in_buf = NULL;
+
+    fctx->fin_block = block;
+    if (f->c->aborted) {
+        rv = filter_abort(fctx); goto cleanup;
+    }
+
+    ap_log_error(APLOG_MARK, APLOG_TRACE2, 0, fctx->cc->server,
+        "tls_filter_conn_input, server=%s, mode=%d, block=%d, readbytes=%ld",
+        fctx->cc->server->server_hostname, mode, block, (long)readbytes);
+
+    rv = progress_tls_atleast_to(fctx, TLS_CONN_ST_TRAFFIC);
+    if (APR_SUCCESS != rv) goto cleanup; /* this also leaves on APR_EAGAIN */
+
+    if (!fctx->cc->rustls_connection) {
+        return ap_get_brigade(f->next, bb, mode, block, readbytes);
+    }
+
+#if AP_MODULE_MAGIC_AT_LEAST(20200420, 1)
+    ap_filter_reinstate_brigade(f, fctx->fin_plain_bb, NULL);
+#endif
+
+    if (AP_MODE_INIT == mode) {
+        /* INIT is used to trigger the handshake, it does not return any traffic data. */
+        goto cleanup;
+    }
+
+    /* If we have nothing buffered, try getting more input.
+     * a) ask rustls_connection for decrypted data, if it has any.
+     *    Note that only full records can be decrypted. We might have
+     *    written TLS data to the session, but that does not mean it
+     *    can give unencryted data out again.
+     * b) read TLS bytes from the network and feed them to the rustls session.
+     * c) go back to a) if b) added data.
+     */
+    while (APR_BRIGADE_EMPTY(fctx->fin_plain_bb)) {
+        apr_size_t rlen = 0;
+        apr_bucket *b;
+
+        if (fctx->fin_bytes_in_rustls > 0) {
+            in_buf_len = APR_BUCKET_BUFF_SIZE;
+            in_buf = ap_calloc(in_buf_len, sizeof(char));
+            rr = rustls_connection_read(fctx->cc->rustls_connection,
+                (unsigned char*)in_buf, in_buf_len, &rlen);
+            if (rr == RUSTLS_RESULT_PLAINTEXT_EMPTY) {
+                rr = RUSTLS_RESULT_OK;
+                rlen = 0;
+            }
+            if (rr != RUSTLS_RESULT_OK) goto cleanup;
+            ap_log_cerror(APLOG_MARK, APLOG_TRACE2, rv, fctx->c,
+                         "tls_filter_conn_input: got %ld plain bytes from rustls", (long)rlen);
+            if (rlen > 0) {
+                b = apr_bucket_heap_create(in_buf, rlen, free, fctx->c->bucket_alloc);
+                APR_BRIGADE_INSERT_TAIL(fctx->fin_plain_bb, b);
+            }
+            else {
+                free(in_buf);
+            }
+            in_buf = NULL;
+        }
+        if (rlen == 0) {
+            /* that did not produce anything either. try getting more
+             * TLS data from the network into the rustls session. */
+            fctx->fin_bytes_in_rustls = 0;
+            rv = read_tls_to_rustls(fctx, fctx->fin_max_in_rustls, block, 0);
+            if (APR_SUCCESS != rv) goto cleanup; /* this also leave on APR_EAGAIN */
+        }
+    }
+
+    if (AP_MODE_GETLINE == mode) {
+        if (readbytes <= 0) readbytes = HUGE_STRING_LEN;
+        rv = tls_util_brigade_split_line(bb, fctx->fin_plain_bb, block, readbytes, &nlen);
+        if (APR_SUCCESS != rv) goto cleanup;
+        passed += nlen;
+    }
+    else if (AP_MODE_READBYTES == mode) {
+        ap_assert(readbytes > 0);
+        rv = tls_util_brigade_transfer(bb, fctx->fin_plain_bb, readbytes, &nlen);
+        if (APR_SUCCESS != rv) goto cleanup;
+        passed += nlen;
+    }
+    else if (AP_MODE_SPECULATIVE == mode) {
+        ap_assert(readbytes > 0);
+        rv = tls_util_brigade_copy(bb, fctx->fin_plain_bb, readbytes, &nlen);
+        if (APR_SUCCESS != rv) goto cleanup;
+        passed += nlen;
+    }
+    else if (AP_MODE_EXHAUSTIVE == mode) {
+        /* return all we have */
+        APR_BRIGADE_CONCAT(bb, fctx->fin_plain_bb);
+    }
+    else {
+        /* We do support any other mode */
+        rv = APR_ENOTIMPL; goto cleanup;
+    }
+
+    fout_pass_all_to_net(fctx, 0);
+
+cleanup:
+    if (NULL != in_buf) free(in_buf);
+
+    if (APLOGctrace3(fctx->c)) {
+        tls_util_bb_log(fctx->c, APLOG_TRACE3, "tls_input, fctx->fin_plain_bb", fctx->fin_plain_bb);
+        tls_util_bb_log(fctx->c, APLOG_TRACE3, "tls_input, bb", bb);
+    }
+    if (rr != RUSTLS_RESULT_OK) {
+        const char *err_descr = "";
+
+        rv = tls_core_error(fctx->c, rr, &err_descr);
+        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, rv, fctx->c, APLOGNO(10355)
+                     "tls_filter_conn_input: [%d] %s", (int)rr, err_descr);
+    }
+    else if (APR_STATUS_IS_EAGAIN(rv)) {
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE4, rv, fctx->c,
+                     "tls_filter_conn_input: no data available");
+    }
+    else if (APR_SUCCESS != rv) {
+        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, rv, fctx->c, APLOGNO(10356)
+                     "tls_filter_conn_input");
+    }
+    else {
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE2, rv, fctx->c,
+                     "tls_filter_conn_input: passed %ld bytes", (long)passed);
+    }
+
+#if AP_MODULE_MAGIC_AT_LEAST(20200420, 1)
+    if (APR_SUCCESS == rv || APR_STATUS_IS_EAGAIN(rv)) {
+        ap_filter_setaside_brigade(f, fctx->fin_plain_bb);
+    }
+#endif
+    return rv;
+}
+
+static rustls_io_result tls_write_callback(
+    void *userdata, const unsigned char *buf, size_t n, size_t *out_n)
+{
+    tls_filter_ctx_t *fctx = userdata;
+    apr_status_t rv;
+
+    if ((apr_off_t)n + fctx->fout_bytes_in_tls_bb >= (apr_off_t)fctx->fout_auto_flush_size) {
+        apr_bucket *b = apr_bucket_transient_create((const char*)buf, n, fctx->fout_tls_bb->bucket_alloc);
+        APR_BRIGADE_INSERT_TAIL(fctx->fout_tls_bb, b);
+        fctx->fout_bytes_in_tls_bb += (apr_off_t)n;
+        rv = fout_pass_tls_to_net(fctx);
+        *out_n = n;
+    }
+    else {
+        rv = apr_brigade_write(fctx->fout_tls_bb, NULL, NULL, (const char*)buf, n);
+        if (APR_SUCCESS != rv) goto cleanup;
+        fctx->fout_bytes_in_tls_bb += (apr_off_t)n;
+        *out_n = n;
+    }
+cleanup:
+    ap_log_error(APLOG_MARK, APLOG_TRACE5, rv, fctx->cc->server,
+        "tls_write_callback: %ld bytes", (long)n);
+    return APR_TO_OS_ERROR(rv);
+}
+
+static rustls_io_result tls_write_vectored_callback(
+    void *userdata, const rustls_iovec *riov, size_t count, size_t *out_n)
+{
+    tls_filter_ctx_t *fctx = userdata;
+    const struct iovec *iov = (const struct iovec*)riov;
+    apr_status_t rv;
+    size_t i, n = 0;
+    apr_bucket *b;
+
+    for (i = 0; i < count; ++i, ++iov) {
+        b = apr_bucket_transient_create((const char*)iov->iov_base, iov->iov_len, fctx->fout_tls_bb->bucket_alloc);
+        APR_BRIGADE_INSERT_TAIL(fctx->fout_tls_bb, b);
+        n += iov->iov_len;
+    }
+    fctx->fout_bytes_in_tls_bb += (apr_off_t)n;
+    rv = fout_pass_tls_to_net(fctx);
+    *out_n = n;
+    ap_log_error(APLOG_MARK, APLOG_TRACE5, rv, fctx->cc->server,
+        "tls_write_vectored_callback: %ld bytes in %d slices", (long)n, (int)count);
+    return APR_TO_OS_ERROR(rv);
+}
+
+#define TLS_WRITE_VECTORED      1
+/**
+ * Read TLS encrypted data from <fctx->cc->rustls_connection> and pass it down
+ * Apache's filter chain to the network.
+ *
+ * For now, we always FLUSH the data, since that is what we need during handshakes.
+ */
+static apr_status_t fout_pass_rustls_to_tls(tls_filter_ctx_t *fctx)
+{
+    apr_status_t rv = APR_SUCCESS;
+
+    if (rustls_connection_wants_write(fctx->cc->rustls_connection)) {
+        size_t dlen;
+        int os_err;
+
+        if (TLS_WRITE_VECTORED) {
+            do {
+                os_err = rustls_connection_write_tls_vectored(
+                    fctx->cc->rustls_connection, tls_write_vectored_callback, fctx, &dlen);
+                if (os_err) {
+                    rv = APR_FROM_OS_ERROR(os_err);
+                    goto cleanup;
+                }
+            }
+            while (rustls_connection_wants_write(fctx->cc->rustls_connection));
+        }
+        else {
+            do {
+                os_err = rustls_connection_write_tls(
+                    fctx->cc->rustls_connection, tls_write_callback, fctx, &dlen);
+                if (os_err) {
+                    rv = APR_FROM_OS_ERROR(os_err);
+                    goto cleanup;
+                }
+            }
+            while (rustls_connection_wants_write(fctx->cc->rustls_connection));
+            ap_log_cerror(APLOG_MARK, APLOG_TRACE3, rv, fctx->c,
+                "fout_pass_rustls_to_tls, %ld bytes ready for network", (long)fctx->fout_bytes_in_tls_bb);
+            fctx->fout_bytes_in_rustls = 0;
+        }
+    }
+cleanup:
+    return rv;
+}
+
+static apr_status_t fout_pass_buf_to_rustls(
+    tls_filter_ctx_t *fctx, const char *buf, apr_size_t len)
+{
+    apr_status_t rv = APR_SUCCESS;
+    rustls_result rr = RUSTLS_RESULT_OK;
+    apr_size_t written;
+
+    while (len) {
+        /* check if we will exceed the limit of data in rustls.
+         * rustls does not guarantuee that it will accept all data, so we
+         * iterate and flush when needed. */
+        if (fctx->fout_bytes_in_rustls + (apr_off_t)len > (apr_off_t)fctx->fout_max_in_rustls) {
+            rv = fout_pass_rustls_to_tls(fctx);
+            if (APR_SUCCESS != rv) goto cleanup;
+        }
+
+        rr = rustls_connection_write(fctx->cc->rustls_connection,
+                                     (const unsigned char*)buf, len, &written);
+        if (rr != RUSTLS_RESULT_OK) goto cleanup;
+        ap_assert(written <= len);
+        fctx->fout_bytes_in_rustls += (apr_off_t)written;
+        buf += written;
+        len -= written;
+        if (written == 0) {
+            rv = APR_EAGAIN;
+            ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, fctx->c, APLOGNO(10357)
+                         "fout_pass_buf_to_rustls: not read by rustls at all");
+            goto cleanup;
+        }
+    }
+cleanup:
+    if (rr != RUSTLS_RESULT_OK) {
+        const char *err_descr = "";
+        rv = tls_core_error(fctx->c, rr, &err_descr);
+        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, rv, fctx->c, APLOGNO(10358)
+                     "fout_pass_buf_to_tls to rustls: [%d] %s", (int)rr, err_descr);
+    }
+    return rv;
+}
+
+static apr_status_t fout_pass_all_to_tls(tls_filter_ctx_t *fctx)
+{
+    apr_status_t rv = APR_SUCCESS;
+
+    if (fctx->fout_buf_plain_len) {
+        rv = fout_pass_buf_to_rustls(fctx, fctx->fout_buf_plain, fctx->fout_buf_plain_len);
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE2, rv, fctx->c,
+                     "fout_pass_all_to_tls: %ld plain bytes written to rustls",
+                     (long)fctx->fout_buf_plain_len);
+        if (APR_SUCCESS != rv) goto cleanup;
+        fctx->fout_buf_plain_len = 0;
+    }
+
+    rv = fout_pass_rustls_to_tls(fctx);
+cleanup:
+    return rv;
+}
+
+static apr_status_t fout_pass_all_to_net(tls_filter_ctx_t *fctx, int flush)
+{
+    apr_status_t rv;
+
+    rv = fout_pass_all_to_tls(fctx);
+    if (APR_SUCCESS != rv) goto cleanup;
+    if (flush) {
+        apr_bucket *b = apr_bucket_flush_create(fctx->fout_tls_bb->bucket_alloc);
+        APR_BRIGADE_INSERT_TAIL(fctx->fout_tls_bb, b);
+    }
+    rv = fout_pass_tls_to_net(fctx);
+cleanup:
+    return rv;
+}
+
+static apr_status_t fout_add_bucket_to_plain(tls_filter_ctx_t *fctx, apr_bucket *b)
+{
+    const char *data;
+    apr_size_t dlen, buf_remain;
+    apr_status_t rv = APR_SUCCESS;
+
+    ap_assert((apr_size_t)-1 != b->length);
+    if (b->length == 0) {
+        apr_bucket_delete(b);
+        goto cleanup;
+    }
+
+    buf_remain = fctx->fout_buf_plain_size - fctx->fout_buf_plain_len;
+    if (buf_remain == 0) {
+        rv = fout_pass_all_to_tls(fctx);
+        if (APR_SUCCESS != rv) goto cleanup;
+        buf_remain = fctx->fout_buf_plain_size - fctx->fout_buf_plain_len;
+        ap_assert(buf_remain > 0);
+    }
+    if (b->length > buf_remain) {
+        apr_bucket_split(b, buf_remain);
+    }
+    rv = apr_bucket_read(b, &data, &dlen, APR_BLOCK_READ);
+    if (APR_SUCCESS != rv) goto cleanup;
+    /*if (dlen > TLS_PREF_PLAIN_CHUNK_SIZE)*/
+    ap_assert(dlen <= buf_remain);
+    memcpy(fctx->fout_buf_plain + fctx->fout_buf_plain_len, data, dlen);
+    fctx->fout_buf_plain_len += dlen;
+    apr_bucket_delete(b);
+cleanup:
+    return rv;
+}
+
+static apr_status_t fout_add_bucket_to_tls(tls_filter_ctx_t *fctx, apr_bucket *b)
+{
+    apr_status_t rv;
+
+    rv = fout_pass_all_to_tls(fctx);
+    if (APR_SUCCESS != rv) goto cleanup;
+    APR_BUCKET_REMOVE(b);
+    APR_BRIGADE_INSERT_TAIL(fctx->fout_tls_bb, b);
+    if (AP_BUCKET_IS_EOC(b)) {
+        rustls_connection_send_close_notify(fctx->cc->rustls_connection);
+        fctx->cc->state = TLS_CONN_ST_NOTIFIED;
+        rv = fout_pass_rustls_to_tls(fctx);
+        if (APR_SUCCESS != rv) goto cleanup;
+    }
+cleanup:
+    return rv;
+}
+
+static apr_status_t fout_append_plain(tls_filter_ctx_t *fctx, apr_bucket *b)
+{
+    const char *data;
+    apr_size_t dlen, buf_remain;
+    rustls_result rr = RUSTLS_RESULT_OK;
+    apr_status_t rv = APR_SUCCESS;
+    const char *lbuf = NULL;
+    int flush = 0;
+
+    if (b) {
+        /* if our plain buffer is full, now is a good time to flush it. */
+        buf_remain = fctx->fout_buf_plain_size - fctx->fout_buf_plain_len;
+        if (buf_remain == 0) {
+            rv = fout_pass_all_to_tls(fctx);
+            if (APR_SUCCESS != rv) goto cleanup;
+            buf_remain = fctx->fout_buf_plain_size - fctx->fout_buf_plain_len;
+            ap_assert(buf_remain > 0);
+        }
+
+        /* Resolve any indeterminate bucket to a "real" one by reading it. */
+        if ((apr_size_t)-1 == b->length) {
+            rv = apr_bucket_read(b, &data, &dlen, APR_BLOCK_READ);
+            if (APR_STATUS_IS_EOF(rv)) {
+                apr_bucket_delete(b);
+                goto maybe_flush;
+            }
+            else if (APR_SUCCESS != rv) goto cleanup;
+        }
+        /* Now `b` is the bucket that we need to append and consume */
+        if (APR_BUCKET_IS_METADATA(b)) {
+            /* outgoing buckets:
+             *   [PLAINDATA META PLAINDATA META META]
+             * need to become:
+             *   [TLSDATA META TLSDATA META META]
+             * because we need to send the meta buckets down the
+             * network filters. */
+            rv = fout_add_bucket_to_tls(fctx, b);
+            flush = 1;
+        }
+        else if (b->length == 0) {
+            apr_bucket_delete(b);
+        }
+        else if (b->length < 1024 || fctx->fout_buf_plain_len > 0) {
+            /* we want to buffer small chunks to create larger TLS records and
+             * not leak security relevant information. So, we buffer small
+             * chunks and add (parts of) later, larger chunks if the plain
+             * buffer contains data. */
+            rv = fout_add_bucket_to_plain(fctx, b);
+            if (APR_SUCCESS != rv) goto cleanup;
+        }
+        else {
+            /* we have a large chunk and our plain buffer is empty, write it
+             * directly into rustls. */
+#define TLS_FILE_CHUNK_SIZE  4 * TLS_PREF_PLAIN_CHUNK_SIZE
+            if (b->length > TLS_FILE_CHUNK_SIZE) {
+                apr_bucket_split(b, TLS_FILE_CHUNK_SIZE);
+            }
+
+            if (APR_BUCKET_IS_FILE(b)
+                && (lbuf = malloc(b->length))) {
+                /* A file bucket is a most wonderous thing. Since the dawn of time,
+                 * it has been subject to many optimizations for efficient handling
+                 * of large data in the server:
+                 * - unless one reads from it, it will just consist of a file handle
+                 *   and the offset+length information.
+                 * - a apr_bucket_read() will transform itself to a bucket holding
+                 *   some 8000 bytes of data (APR_BUCKET_BUFF_SIZE), plus a following
+                 *   bucket that continues to hold the file handle and updated offsets/length
+                 *   information.
+                 *   Using standard bucket brigade handling, one would send 8000 bytes
+                 *   chunks to the network and that is fine for many occasions.
+                 * - to have improved performance, the http: network handler takes
+                 *   the file handle directly and uses sendfile() when the OS supports it.
+                 * - But there is not sendfile() for TLS (netflix did some experiments).
+                 * So.
+                 * rustls will try to collect max length traffic data into ont TLS
+                 * message, but it can only work with what we gave it. If we give it buffers
+                 * that fit what it wants to assemble already, its work is much easier.
+                 *
+                 * We can read file buckets in large chunks than APR_BUCKET_BUFF_SIZE,
+                 * with a bit of knowledge about how they work.
+                 */
+                apr_bucket_file *f = (apr_bucket_file *)b->data;
+                apr_file_t *fd = f->fd;
+                apr_off_t offset = b->start;
+
+                dlen = b->length;
+                rv = apr_file_seek(fd, APR_SET, &offset);
+                if (APR_SUCCESS != rv) goto cleanup;
+                rv = apr_file_read(fd, (void*)lbuf, &dlen);
+                if (APR_SUCCESS != rv && !APR_STATUS_IS_EOF(rv)) goto cleanup;
+                rv = fout_pass_buf_to_rustls(fctx, lbuf, dlen);
+                if (APR_SUCCESS != rv) goto cleanup;
+                apr_bucket_delete(b);
+            }
+            else {
+                rv = apr_bucket_read(b, &data, &dlen, APR_BLOCK_READ);
+                if (APR_SUCCESS != rv) goto cleanup;
+                rv = fout_pass_buf_to_rustls(fctx, data, dlen);
+                if (APR_SUCCESS != rv) goto cleanup;
+                apr_bucket_delete(b);
+            }
+        }
+    }
+
+maybe_flush:
+    if (flush) {
+        rv = fout_pass_all_to_net(fctx, 1);
+        if (APR_SUCCESS != rv) goto cleanup;
+    }
+
+cleanup:
+    if (lbuf) free((void*)lbuf);
+    if (rr != RUSTLS_RESULT_OK) {
+        const char *err_descr = "";
+        rv = tls_core_error(fctx->c, rr, &err_descr);
+        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, rv, fctx->c, APLOGNO(10359)
+                     "write_bucket_to_rustls: [%d] %s", (int)rr, err_descr);
+    }
+    return rv;
+}
+
+/**
+ * The connection filter converting plain, unencrypted traffic data into TLS
+ * encrypted bytes and send the down the Apache filter chain out to the network.
+ *
+ * <bb>    the data to send, including "meta data" such as FLUSH indicators
+ *         to force filters to write any data set aside (an apache term for
+ *         'buffering').
+ *         The buckets in <bb> need to be completely consumed, e.g. <bb> will be
+ *         empty on a successful return. but unless FLUSHed, filters may hold
+ *         buckets back internally, for various reasons. However they always
+ *         need to be processed in the order they arrive.
+ */
+static apr_status_t filter_conn_output(
+    ap_filter_t *f, apr_bucket_brigade *bb)
+{
+    tls_filter_ctx_t *fctx = f->ctx;
+    apr_status_t rv = APR_SUCCESS;
+    rustls_result rr = RUSTLS_RESULT_OK;
+
+    if (f->c->aborted) {
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE4, 0, fctx->c,
+            "tls_filter_conn_output: aborted conn");
+        apr_brigade_cleanup(bb);
+        rv = APR_ECONNABORTED; goto  cleanup;
+    }
+
+    rv = progress_tls_atleast_to(fctx, TLS_CONN_ST_TRAFFIC);
+    if (APR_SUCCESS != rv) goto cleanup; /* this also leaves on APR_EAGAIN */
+
+    if (fctx->cc->state == TLS_CONN_ST_DONE) {
+        /* have done everything, just pass through */
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE4, 0, fctx->c,
+            "tls_filter_conn_output: tls session is already done");
+        rv = ap_pass_brigade(f->next, bb);
+        goto cleanup;
+    }
+
+    ap_log_error(APLOG_MARK, APLOG_TRACE2, 0, fctx->cc->server,
+        "tls_filter_conn_output, server=%s", fctx->cc->server->server_hostname);
+    if (APLOGctrace5(fctx->c)) {
+        tls_util_bb_log(fctx->c, APLOG_TRACE5, "filter_conn_output", bb);
+    }
+
+    while (!APR_BRIGADE_EMPTY(bb)) {
+        rv = fout_append_plain(fctx, APR_BRIGADE_FIRST(bb));
+        if (APR_SUCCESS != rv) goto cleanup;
+    }
+
+    if (APLOGctrace5(fctx->c)) {
+        tls_util_bb_log(fctx->c, APLOG_TRACE5, "filter_conn_output, processed plain", bb);
+        tls_util_bb_log(fctx->c, APLOG_TRACE5, "filter_conn_output, tls", fctx->fout_tls_bb);
+    }
+
+cleanup:
+    if (rr != RUSTLS_RESULT_OK) {
+        const char *err_descr = "";
+        rv = tls_core_error(fctx->c, rr, &err_descr);
+        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, rv, fctx->c, APLOGNO(10360)
+                     "tls_filter_conn_output: [%d] %s", (int)rr, err_descr);
+    }
+    else {
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE2, rv, fctx->c,
+                     "tls_filter_conn_output: done");
+    }
+    return rv;
+}
+
+int tls_filter_pre_conn_init(conn_rec *c)
+{
+    tls_conf_conn_t *cc;
+    tls_filter_ctx_t *fctx;
+
+    if (OK != tls_core_pre_conn_init(c)) {
+        return DECLINED;
+    }
+
+    ap_log_error(APLOG_MARK, APLOG_TRACE2, 0, c->base_server,
+        "tls_filter_pre_conn_init on %s", c->base_server->server_hostname);
+
+    cc = tls_conf_conn_get(c);
+    ap_assert(cc);
+
+    fctx = apr_pcalloc(c->pool, sizeof(*fctx));
+    fctx->c = c;
+    fctx->cc = cc;
+    cc->filter_ctx = fctx;
+
+    /* a bit tricky: registering out filters returns the ap_filter_t*
+     * that it created for it. The ->next field points always
+     * to the filter "below" our filter. That will be other registered
+     * filters and last, but not least, the network filter on the socket.
+     *
+     * Therefore, wenn we need to read/write TLS data during handshake, we can
+     * pass the data to/call on ->next- Since ->next can change during the setup of
+     * a connections (other modules register also sth.), we keep the ap_filter_t*
+     * returned here, since httpd core will update the ->next whenever someone
+     * adds a filter or removes one. This can potentially happen all the time.
+     */
+    fctx->fin_ctx = ap_add_input_filter(TLS_FILTER_RAW, fctx, NULL, c);
+    fctx->fin_tls_bb = apr_brigade_create(c->pool, c->bucket_alloc);
+    fctx->fin_tls_buffer_bb = NULL;
+    fctx->fin_plain_bb = apr_brigade_create(c->pool, c->bucket_alloc);
+    fctx->fout_ctx = ap_add_output_filter(TLS_FILTER_RAW, fctx, NULL, c);
+    fctx->fout_tls_bb = apr_brigade_create(c->pool, c->bucket_alloc);
+    fctx->fout_buf_plain_size = APR_BUCKET_BUFF_SIZE;
+    fctx->fout_buf_plain = apr_pcalloc(c->pool, fctx->fout_buf_plain_size);
+    fctx->fout_buf_plain_len = 0;
+
+    /* Let the filters have 2 max-length TLS Messages in the rustls buffers.
+     * The effects we would like to achieve here are:
+     * 1. pass data out, so that every bucket becomes its own TLS message.
+     *    This hides, if possible, the length of response parts.
+     *    If we give rustls enough plain data, it will use the max TLS message
+     *    size and things are more hidden. But we can only write what the application
+     *    or protocol gives us.
+     * 2. max length records result in less overhead for all layers involved.
+     * 3. a TLS message from the client can only be decrypted when it has
+     *    completely arrived. If we provide rustls with enough data (if the
+     *    network has it for us), it should always be able to decrypt at least
+     *    one TLS message and we have plain bytes to forward to the protocol
+     *    handler.
+     */
+    fctx->fin_max_in_rustls = 4 * TLS_REC_MAX_SIZE;
+    fctx->fout_max_in_rustls = 4 * TLS_PREF_PLAIN_CHUNK_SIZE;
+    fctx->fout_auto_flush_size = 2 * TLS_REC_MAX_SIZE;
+
+    return OK;
+}
+
+void tls_filter_conn_init(conn_rec *c)
+{
+    tls_conf_conn_t *cc = tls_conf_conn_get(c);
+
+    if (cc && cc->filter_ctx && !cc->outgoing) {
+        /* We are one in a row of hooks that - possibly - want to process this
+         * connection, the (HTTP) protocol handlers among them.
+         *
+         * For incoming connections, we need to select the protocol to use NOW,
+         * so that the later protocol handlers do the right thing.
+         * Send an INIT down the input filter chain to trigger the TLS handshake,
+         * which will select a protocol via ALPN. */
+        apr_bucket_brigade* temp;
+
+        ap_log_error(APLOG_MARK, APLOG_TRACE2, 0, c->base_server,
+            "tls_filter_conn_init on %s, triggering handshake", c->base_server->server_hostname);
+        temp = apr_brigade_create(c->pool, c->bucket_alloc);
+        ap_get_brigade(c->input_filters, temp, AP_MODE_INIT, APR_BLOCK_READ, 0);
+        apr_brigade_destroy(temp);
+    }
+}
+
+void tls_filter_register(
+    apr_pool_t *pool)
+{
+    (void)pool;
+    ap_register_input_filter(TLS_FILTER_RAW, filter_conn_input,  NULL, AP_FTYPE_CONNECTION + 5);
+    ap_register_output_filter(TLS_FILTER_RAW, filter_conn_output, NULL, AP_FTYPE_CONNECTION + 5);
+}
\ No newline at end of file

Added: httpd/httpd/trunk/modules/tls/tls_filter.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/tls/tls_filter.h?rev=1895432&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/tls/tls_filter.h (added)
+++ httpd/httpd/trunk/modules/tls/tls_filter.h Tue Nov 30 16:29:20 2021
@@ -0,0 +1,90 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef tls_filter_h
+#define tls_filter_h
+
+#define TLS_FILTER_RAW    "TLS raw"
+
+typedef struct tls_filter_ctx_t tls_filter_ctx_t;
+
+struct tls_filter_ctx_t {
+    conn_rec *c;                         /* connection this context is for */
+    tls_conf_conn_t *cc;                 /* tls module configuration of connection */
+
+    ap_filter_t *fin_ctx;                /* Apache's entry into the input filter chain */
+    apr_bucket_brigade *fin_tls_bb;      /* TLS encrypted, incoming network data */
+    apr_bucket_brigade *fin_tls_buffer_bb; /* TLS encrypted, incoming network data buffering */
+    apr_bucket_brigade *fin_plain_bb;    /* decrypted, incoming traffic data */
+    apr_off_t fin_bytes_in_rustls;       /* # of input TLS bytes in rustls_connection */
+    apr_read_type_e fin_block;           /* Do we block on input reads or not? */
+
+    ap_filter_t *fout_ctx;               /* Apache's entry into the output filter chain */
+    char *fout_buf_plain;                /* a buffer to collect plain bytes for output */
+    apr_size_t fout_buf_plain_len;       /* the amount of bytes in the buffer */
+    apr_size_t fout_buf_plain_size;      /* the total size of the buffer */
+    apr_bucket_brigade *fout_tls_bb;     /* TLS encrypted, outgoing network data */
+    apr_off_t fout_bytes_in_rustls;      /* # of output plain bytes in rustls_connection */
+    apr_off_t fout_bytes_in_tls_bb;      /* # of output tls bytes in our brigade */
+
+    apr_size_t fin_max_in_rustls;         /* how much tls we like to read into rustls */
+    apr_size_t fout_max_in_rustls;        /* how much plain bytes we like in rustls */
+    apr_size_t fout_max_bucket_size;      /* how large bucket chunks we handle before splitting */
+    apr_size_t fout_auto_flush_size;      /* on much outoing TLS data we flush to network */
+};
+
+/**
+ * Register the in-/output filters for converting TLS to application data and vice versa.
+ */
+void tls_filter_register(apr_pool_t *pool);
+
+/**
+ * Initialize the pre_connection state. Install all filters.
+ *
+ * @return OK if TLS on connection is enabled, DECLINED otherwise
+ */
+int tls_filter_pre_conn_init(conn_rec *c);
+
+/**
+ * Initialize the connection for use, perform the TLS handshake.
+ *
+ * Any failure will lead to the connection becoming aborted.
+ */
+void tls_filter_conn_init(conn_rec *c);
+
+/*
+ * <https://tools.ietf.org/html/rfc8449> says:
+ * "For large data transfers, small record sizes can materially affect performance."
+ * and
+ * "For TLS 1.2 and earlier, that limit is 2^14 octets. TLS 1.3 uses a limit of
+ * 2^14+1 octets."
+ * Maybe future TLS versions will raise that value, but for now these limits stand.
+ * Given the choice, we would like rustls to provide traffic data in those chunks.
+ */
+#define TLS_PREF_PLAIN_CHUNK_SIZE       (16384)
+
+/*
+ * When retrieving TLS chunks for rustls, or providing it a buffer
+ * to pass out TLS chunks (which are then bucketed and written to the
+ * network filters), we ideally would do that in multiples of TLS
+ * messages sizes.
+ * That would be TLS_PREF_WRITE_SIZE + TLS Message Overhead, such as
+ * MAC and padding. But these vary with protocol and ciphers chosen, so
+ * we define something which should be "large enough", but not overly so.
+ */
+#define TLS_REC_EXTRA             (1024)
+#define TLS_REC_MAX_SIZE   (TLS_PREF_PLAIN_CHUNK_SIZE + TLS_REC_EXTRA)
+
+#endif /* tls_filter_h */
\ No newline at end of file

Added: httpd/httpd/trunk/modules/tls/tls_ocsp.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/tls/tls_ocsp.c?rev=1895432&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/tls/tls_ocsp.c (added)
+++ httpd/httpd/trunk/modules/tls/tls_ocsp.c Tue Nov 30 16:29:20 2021
@@ -0,0 +1,120 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <assert.h>
+#include <apr_lib.h>
+#include <apr_strings.h>
+
+#include <httpd.h>
+#include <http_connection.h>
+#include <http_core.h>
+#include <http_log.h>
+#include <http_ssl.h>
+
+#include <rustls.h>
+
+#include "tls_cert.h"
+#include "tls_conf.h"
+#include "tls_core.h"
+#include "tls_proto.h"
+#include "tls_ocsp.h"
+
+extern module AP_MODULE_DECLARE_DATA tls_module;
+APLOG_USE_MODULE(tls);
+
+
+static int prime_cert(
+    void *userdata, server_rec *s, const char *cert_id, const char *cert_pem,
+    const rustls_certified_key *certified_key)
+{
+    apr_pool_t *p = userdata;
+    apr_status_t rv;
+
+    (void)certified_key;
+    rv = ap_ssl_ocsp_prime(s, p, cert_id, strlen(cert_id), cert_pem);
+    ap_log_error(APLOG_MARK, APLOG_TRACE1, rv, s, "ocsp prime of cert [%s] from %s",
+                 cert_id, s->server_hostname);
+    return 1;
+}
+
+apr_status_t tls_ocsp_prime_certs(tls_conf_global_t *gc, apr_pool_t *p, server_rec *s)
+{
+    ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, s, "ocsp priming of %d certs",
+                 (int)tls_cert_reg_count(gc->cert_reg));
+    tls_cert_reg_do(prime_cert, p, gc->cert_reg);
+    return APR_SUCCESS;
+}
+
+typedef struct {
+    conn_rec *c;
+    const rustls_certified_key *key_in;
+    const rustls_certified_key *key_out;
+} ocsp_copy_ctx_t;
+
+static void ocsp_clone_key(const unsigned char *der, apr_size_t der_len, void *userdata)
+{
+    ocsp_copy_ctx_t *ctx = userdata;
+    rustls_slice_bytes rslice;
+    rustls_result rr;
+
+    rslice.data = der;
+    rslice.len = der_len;
+
+    rr = rustls_certified_key_clone_with_ocsp(ctx->key_in, der_len? &rslice : NULL, &ctx->key_out);
+    if (RUSTLS_RESULT_OK != rr) {
+        const char *err_descr = NULL;
+        apr_status_t rv = tls_util_rustls_error(ctx->c->pool, rr, &err_descr);
+        ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, ctx->c, APLOGNO(10362)
+                     "Failed add OCSP data to certificate: [%d] %s", (int)rr, err_descr);
+    }
+    else {
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, ctx->c,
+            "provided %ld bytes of ocsp response DER data to key.", (long)der_len);
+    }
+}
+
+apr_status_t tls_ocsp_update_key(
+    conn_rec *c, const rustls_certified_key *certified_key,
+    const rustls_certified_key **pkey_out)
+{
+    tls_conf_conn_t *cc = tls_conf_conn_get(c);
+    tls_conf_server_t *sc;
+    const char *key_id;
+    apr_status_t rv = APR_SUCCESS;
+    ocsp_copy_ctx_t ctx;
+
+    assert(cc);
+    assert(cc->server);
+    sc = tls_conf_server_get(cc->server);
+    key_id = tls_cert_reg_get_id(sc->global->cert_reg, certified_key);
+    if (!key_id) {
+        rv = APR_ENOENT;
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, rv, c, "certified key not registered");
+        goto cleanup;
+    }
+
+    ctx.c = c;
+    ctx.key_in = certified_key;
+    ctx.key_out = NULL;
+    rv = ap_ssl_ocsp_get_resp(cc->server, c, key_id, strlen(key_id), ocsp_clone_key, &ctx);
+    if (APR_SUCCESS != rv) {
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, rv, c,
+            "ocsp response not available for cert %s", key_id);
+    }
+
+cleanup:
+    *pkey_out = (APR_SUCCESS == rv)? ctx.key_out : NULL;
+    return rv;
+}

Added: httpd/httpd/trunk/modules/tls/tls_ocsp.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/tls/tls_ocsp.h?rev=1895432&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/tls/tls_ocsp.h (added)
+++ httpd/httpd/trunk/modules/tls/tls_ocsp.h Tue Nov 30 16:29:20 2021
@@ -0,0 +1,47 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef tls_ocsp_h
+#define tls_ocsp_h
+
+/**
+ * Prime the collected certified keys for OCSP response provisioning (aka. Stapling).
+ *
+ * To be called in the post-config phase of the server before connections are handled.
+ * @param gc the global module configuration with the certified_key registry
+ * @param p the pool to use for allocations
+ * @param s the base server record
+ */
+apr_status_t tls_ocsp_prime_certs(tls_conf_global_t *gc, apr_pool_t *p, server_rec *s);
+
+/**
+ * Provide the OCSP response data for the certified_key into the offered buffer,
+ * so available.
+ * If not data is available `out_n` is set to 0. Same, if the offered buffer
+ * is not large enough to hold the complete response.
+ * If OCSP response DER data is copied, the number of copied bytes is given in `out_n`.
+ *
+ * Note that only keys that have been primed initially will have OCSP data available.
+ * @param c the current connection
+ * @param certified_key the key to get the OCSP response data for
+ * @param buf a buffer which can hold up to `buf_len` bytes
+ * @param buf_len the length of `buf`
+ * @param out_n the number of OCSP response DER bytes copied or 0.
+ */
+apr_status_t tls_ocsp_update_key(
+    conn_rec *c, const rustls_certified_key *certified_key,
+    const rustls_certified_key **key_out);
+
+#endif /* tls_ocsp_h */

Added: httpd/httpd/trunk/modules/tls/tls_proto.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/tls/tls_proto.c?rev=1895432&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/tls/tls_proto.c (added)
+++ httpd/httpd/trunk/modules/tls/tls_proto.c Tue Nov 30 16:29:20 2021
@@ -0,0 +1,603 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <assert.h>
+#include <apr_lib.h>
+#include <apr_strings.h>
+
+#include <httpd.h>
+#include <http_connection.h>
+#include <http_core.h>
+#include <http_log.h>
+
+#include <rustls.h>
+
+#include "tls_proto.h"
+#include "tls_conf.h"
+#include "tls_util.h"
+
+extern module AP_MODULE_DECLARE_DATA tls_module;
+APLOG_USE_MODULE(tls);
+
+
+
+/**
+ * Known cipher as registered in
+ * <https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-4>
+ */
+static tls_cipher_t KNOWN_CIPHERS[] = {
+    { 0x0000, "TLS_NULL_WITH_NULL_NULL", NULL },
+    { 0x0001, "TLS_RSA_WITH_NULL_MD5", NULL },
+    { 0x0002, "TLS_RSA_WITH_NULL_SHA", NULL },
+    { 0x0003, "TLS_RSA_EXPORT_WITH_RC4_40_MD5", NULL },
+    { 0x0004, "TLS_RSA_WITH_RC4_128_MD5", NULL },
+    { 0x0005, "TLS_RSA_WITH_RC4_128_SHA", NULL },
+    { 0x0006, "TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5", NULL },
+    { 0x0007, "TLS_RSA_WITH_IDEA_CBC_SHA", NULL },
+    { 0x0008, "TLS_RSA_EXPORT_WITH_DES40_CBC_SHA", NULL },
+    { 0x0009, "TLS_RSA_WITH_DES_CBC_SHA", NULL },
+    { 0x000a, "TLS_RSA_WITH_3DES_EDE_CBC_SHA", NULL },
+    { 0x000b, "TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA", NULL },
+    { 0x000c, "TLS_DH_DSS_WITH_DES_CBC_SHA", NULL },
+    { 0x000d, "TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA", NULL },
+    { 0x000e, "TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA", NULL },
+    { 0x000f, "TLS_DH_RSA_WITH_DES_CBC_SHA", NULL },
+    { 0x0010, "TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA", NULL },
+    { 0x0011, "TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA", NULL },
+    { 0x0012, "TLS_DHE_DSS_WITH_DES_CBC_SHA", NULL },
+    { 0x0013, "TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA", NULL },
+    { 0x0014, "TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA", NULL },
+    { 0x0015, "TLS_DHE_RSA_WITH_DES_CBC_SHA", NULL },
+    { 0x0016, "TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA", NULL },
+    { 0x0017, "TLS_DH_anon_EXPORT_WITH_RC4_40_MD5", NULL },
+    { 0x0018, "TLS_DH_anon_WITH_RC4_128_MD5", NULL },
+    { 0x0019, "TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA", NULL },
+    { 0x001a, "TLS_DH_anon_WITH_DES_CBC_SHA", NULL },
+    { 0x001b, "TLS_DH_anon_WITH_3DES_EDE_CBC_SHA", NULL },
+    { 0x001c, "SSL_FORTEZZA_KEA_WITH_NULL_SHA", NULL },
+    { 0x001d, "SSL_FORTEZZA_KEA_WITH_FORTEZZA_CBC_SHA", NULL },
+    { 0x001e, "TLS_KRB5_WITH_DES_CBC_SHA_or_SSL_FORTEZZA_KEA_WITH_RC4_128_SHA", NULL },
+    { 0x001f, "TLS_KRB5_WITH_3DES_EDE_CBC_SHA", NULL },
+    { 0x0020, "TLS_KRB5_WITH_RC4_128_SHA", NULL },
+    { 0x0021, "TLS_KRB5_WITH_IDEA_CBC_SHA", NULL },
+    { 0x0022, "TLS_KRB5_WITH_DES_CBC_MD5", NULL },
+    { 0x0023, "TLS_KRB5_WITH_3DES_EDE_CBC_MD5", NULL },
+    { 0x0024, "TLS_KRB5_WITH_RC4_128_MD5", NULL },
+    { 0x0025, "TLS_KRB5_WITH_IDEA_CBC_MD5", NULL },
+    { 0x0026, "TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA", NULL },
+    { 0x0027, "TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA", NULL },
+    { 0x0028, "TLS_KRB5_EXPORT_WITH_RC4_40_SHA", NULL },
+    { 0x0029, "TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5", NULL },
+    { 0x002a, "TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5", NULL },
+    { 0x002b, "TLS_KRB5_EXPORT_WITH_RC4_40_MD5", NULL },
+    { 0x002c, "TLS_PSK_WITH_NULL_SHA", NULL },
+    { 0x002d, "TLS_DHE_PSK_WITH_NULL_SHA", NULL },
+    { 0x002e, "TLS_RSA_PSK_WITH_NULL_SHA", NULL },
+    { 0x002f, "TLS_RSA_WITH_AES_128_CBC_SHA", NULL },
+    { 0x0030, "TLS_DH_DSS_WITH_AES_128_CBC_SHA", NULL },
+    { 0x0031, "TLS_DH_RSA_WITH_AES_128_CBC_SHA", NULL },
+    { 0x0032, "TLS_DHE_DSS_WITH_AES_128_CBC_SHA", NULL },
+    { 0x0033, "TLS_DHE_RSA_WITH_AES_128_CBC_SHA", NULL },
+    { 0x0034, "TLS_DH_anon_WITH_AES_128_CBC_SHA", NULL },
+    { 0x0035, "TLS_RSA_WITH_AES_256_CBC_SHA", NULL },
+    { 0x0036, "TLS_DH_DSS_WITH_AES_256_CBC_SHA", NULL },
+    { 0x0037, "TLS_DH_RSA_WITH_AES_256_CBC_SHA", NULL },
+    { 0x0038, "TLS_DHE_DSS_WITH_AES_256_CBC_SHA", NULL },
+    { 0x0039, "TLS_DHE_RSA_WITH_AES_256_CBC_SHA", NULL },
+    { 0x003a, "TLS_DH_anon_WITH_AES_256_CBC_SHA", NULL },
+    { 0x003b, "TLS_RSA_WITH_NULL_SHA256", "NULL-SHA256" },
+    { 0x003c, "TLS_RSA_WITH_AES_128_CBC_SHA256", "AES128-SHA256" },
+    { 0x003d, "TLS_RSA_WITH_AES_256_CBC_SHA256", "AES256-SHA256" },
+    { 0x003e, "TLS_DH_DSS_WITH_AES_128_CBC_SHA256", "DH-DSS-AES128-SHA256" },
+    { 0x003f, "TLS_DH_RSA_WITH_AES_128_CBC_SHA256", "DH-RSA-AES128-SHA256" },
+    { 0x0040, "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256", "DHE-DSS-AES128-SHA256" },
+    { 0x0041, "TLS_RSA_WITH_CAMELLIA_128_CBC_SHA", NULL },
+    { 0x0042, "TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA", NULL },
+    { 0x0043, "TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA", NULL },
+    { 0x0044, "TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA", NULL },
+    { 0x0045, "TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA", NULL },
+    { 0x0046, "TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA", NULL },
+    { 0x0047, "TLS_ECDH_ECDSA_WITH_NULL_SHA_draft", NULL },
+    { 0x0048, "TLS_ECDH_ECDSA_WITH_RC4_128_SHA_draft", NULL },
+    { 0x0049, "TLS_ECDH_ECDSA_WITH_DES_CBC_SHA_draft", NULL },
+    { 0x004a, "TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA_draft", NULL },
+    { 0x004b, "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA_draft", NULL },
+    { 0x004c, "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA_draft", NULL },
+    { 0x004d, "TLS_ECDH_ECNRA_WITH_DES_CBC_SHA_draft", NULL },
+    { 0x004e, "TLS_ECDH_ECNRA_WITH_3DES_EDE_CBC_SHA_draft", NULL },
+    { 0x004f, "TLS_ECMQV_ECDSA_NULL_SHA_draft", NULL },
+    { 0x0050, "TLS_ECMQV_ECDSA_WITH_RC4_128_SHA_draft", NULL },
+    { 0x0051, "TLS_ECMQV_ECDSA_WITH_DES_CBC_SHA_draft", NULL },
+    { 0x0052, "TLS_ECMQV_ECDSA_WITH_3DES_EDE_CBC_SHA_draft", NULL },
+    { 0x0053, "TLS_ECMQV_ECNRA_NULL_SHA_draft", NULL },
+    { 0x0054, "TLS_ECMQV_ECNRA_WITH_RC4_128_SHA_draft", NULL },
+    { 0x0055, "TLS_ECMQV_ECNRA_WITH_DES_CBC_SHA_draft", NULL },
+    { 0x0056, "TLS_ECMQV_ECNRA_WITH_3DES_EDE_CBC_SHA_draft", NULL },
+    { 0x0057, "TLS_ECDH_anon_NULL_WITH_SHA_draft", NULL },
+    { 0x0058, "TLS_ECDH_anon_WITH_RC4_128_SHA_draft", NULL },
+    { 0x0059, "TLS_ECDH_anon_WITH_DES_CBC_SHA_draft", NULL },
+    { 0x005a, "TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA_draft", NULL },
+    { 0x005b, "TLS_ECDH_anon_EXPORT_WITH_DES40_CBC_SHA_draft", NULL },
+    { 0x005c, "TLS_ECDH_anon_EXPORT_WITH_RC4_40_SHA_draft", NULL },
+    { 0x0060, "TLS_RSA_EXPORT1024_WITH_RC4_56_MD5", NULL },
+    { 0x0061, "TLS_RSA_EXPORT1024_WITH_RC2_CBC_56_MD5", NULL },
+    { 0x0062, "TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA", NULL },
+    { 0x0063, "TLS_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA", NULL },
+    { 0x0064, "TLS_RSA_EXPORT1024_WITH_RC4_56_SHA", NULL },
+    { 0x0065, "TLS_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA", NULL },
+    { 0x0066, "TLS_DHE_DSS_WITH_RC4_128_SHA", NULL },
+    { 0x0067, "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256", "DHE-RSA-AES128-SHA256" },
+    { 0x0068, "TLS_DH_DSS_WITH_AES_256_CBC_SHA256", "DH-DSS-AES256-SHA256" },
+    { 0x0069, "TLS_DH_RSA_WITH_AES_256_CBC_SHA256", "DH-RSA-AES256-SHA256" },
+    { 0x006a, "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256", "DHE-DSS-AES256-SHA256" },
+    { 0x006b, "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256", "DHE-RSA-AES256-SHA256" },
+    { 0x006c, "TLS_DH_anon_WITH_AES_128_CBC_SHA256", "ADH-AES128-SHA256" },
+    { 0x006d, "TLS_DH_anon_WITH_AES_256_CBC_SHA256", "ADH-AES256-SHA256" },
+    { 0x0072, "TLS_DHE_DSS_WITH_3DES_EDE_CBC_RMD", NULL },
+    { 0x0073, "TLS_DHE_DSS_WITH_AES_128_CBC_RMD", NULL },
+    { 0x0074, "TLS_DHE_DSS_WITH_AES_256_CBC_RMD", NULL },
+    { 0x0077, "TLS_DHE_RSA_WITH_3DES_EDE_CBC_RMD", NULL },
+    { 0x0078, "TLS_DHE_RSA_WITH_AES_128_CBC_RMD", NULL },
+    { 0x0079, "TLS_DHE_RSA_WITH_AES_256_CBC_RMD", NULL },
+    { 0x007c, "TLS_RSA_WITH_3DES_EDE_CBC_RMD", NULL },
+    { 0x007d, "TLS_RSA_WITH_AES_128_CBC_RMD", NULL },
+    { 0x007e, "TLS_RSA_WITH_AES_256_CBC_RMD", NULL },
+    { 0x0080, "TLS_GOSTR341094_WITH_28147_CNT_IMIT", NULL },
+    { 0x0081, "TLS_GOSTR341001_WITH_28147_CNT_IMIT", NULL },
+    { 0x0082, "TLS_GOSTR341094_WITH_NULL_GOSTR3411", NULL },
+    { 0x0083, "TLS_GOSTR341001_WITH_NULL_GOSTR3411", NULL },
+    { 0x0084, "TLS_RSA_WITH_CAMELLIA_256_CBC_SHA", NULL },
+    { 0x0085, "TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA", NULL },
+    { 0x0086, "TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA", NULL },
+    { 0x0087, "TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA", NULL },
+    { 0x0088, "TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA", NULL },
+    { 0x0089, "TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA", NULL },
+    { 0x008a, "TLS_PSK_WITH_RC4_128_SHA", "PSK-RC4-SHA" },
+    { 0x008b, "TLS_PSK_WITH_3DES_EDE_CBC_SHA", "PSK-3DES-EDE-CBC-SHA" },
+    { 0x008c, "TLS_PSK_WITH_AES_128_CBC_SHA", NULL },
+    { 0x008d, "TLS_PSK_WITH_AES_256_CBC_SHA", NULL },
+    { 0x008e, "TLS_DHE_PSK_WITH_RC4_128_SHA", NULL },
+    { 0x008f, "TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA", NULL },
+    { 0x0090, "TLS_DHE_PSK_WITH_AES_128_CBC_SHA", NULL },
+    { 0x0091, "TLS_DHE_PSK_WITH_AES_256_CBC_SHA", NULL },
+    { 0x0092, "TLS_RSA_PSK_WITH_RC4_128_SHA", NULL },
+    { 0x0093, "TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA", NULL },
+    { 0x0094, "TLS_RSA_PSK_WITH_AES_128_CBC_SHA", NULL },
+    { 0x0095, "TLS_RSA_PSK_WITH_AES_256_CBC_SHA", NULL },
+    { 0x0096, "TLS_RSA_WITH_SEED_CBC_SHA", NULL },
+    { 0x0097, "TLS_DH_DSS_WITH_SEED_CBC_SHA", NULL },
+    { 0x0098, "TLS_DH_RSA_WITH_SEED_CBC_SHA", NULL },
+    { 0x0099, "TLS_DHE_DSS_WITH_SEED_CBC_SHA", NULL },
+    { 0x009a, "TLS_DHE_RSA_WITH_SEED_CBC_SHA", NULL },
+    { 0x009b, "TLS_DH_anon_WITH_SEED_CBC_SHA", NULL },
+    { 0x009c, "TLS_RSA_WITH_AES_128_GCM_SHA256", "AES128-GCM-SHA256" },
+    { 0x009d, "TLS_RSA_WITH_AES_256_GCM_SHA384", "AES256-GCM-SHA384" },
+    { 0x009e, "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256", "DHE-RSA-AES128-GCM-SHA256" },
+    { 0x009f, "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384", "DHE-RSA-AES256-GCM-SHA384" },
+    { 0x00a0, "TLS_DH_RSA_WITH_AES_128_GCM_SHA256", "DH-RSA-AES128-GCM-SHA256" },
+    { 0x00a1, "TLS_DH_RSA_WITH_AES_256_GCM_SHA384", "DH-RSA-AES256-GCM-SHA384" },
+    { 0x00a2, "TLS_DHE_DSS_WITH_AES_128_GCM_SHA256", "DHE-DSS-AES128-GCM-SHA256" },
+    { 0x00a3, "TLS_DHE_DSS_WITH_AES_256_GCM_SHA384", "DHE-DSS-AES256-GCM-SHA384" },
+    { 0x00a4, "TLS_DH_DSS_WITH_AES_128_GCM_SHA256", "DH-DSS-AES128-GCM-SHA256" },
+    { 0x00a5, "TLS_DH_DSS_WITH_AES_256_GCM_SHA384", "DH-DSS-AES256-GCM-SHA384" },
+    { 0x00a6, "TLS_DH_anon_WITH_AES_128_GCM_SHA256", "ADH-AES128-GCM-SHA256" },
+    { 0x00a7, "TLS_DH_anon_WITH_AES_256_GCM_SHA384", "ADH-AES256-GCM-SHA384" },
+    { 0x00a8, "TLS_PSK_WITH_AES_128_GCM_SHA256", NULL },
+    { 0x00a9, "TLS_PSK_WITH_AES_256_GCM_SHA384", NULL },
+    { 0x00aa, "TLS_DHE_PSK_WITH_AES_128_GCM_SHA256", NULL },
+    { 0x00ab, "TLS_DHE_PSK_WITH_AES_256_GCM_SHA384", NULL },
+    { 0x00ac, "TLS_RSA_PSK_WITH_AES_128_GCM_SHA256", NULL },
+    { 0x00ad, "TLS_RSA_PSK_WITH_AES_256_GCM_SHA384", NULL },
+    { 0x00ae, "TLS_PSK_WITH_AES_128_CBC_SHA256", "PSK-AES128-CBC-SHA" },
+    { 0x00af, "TLS_PSK_WITH_AES_256_CBC_SHA384", "PSK-AES256-CBC-SHA" },
+    { 0x00b0, "TLS_PSK_WITH_NULL_SHA256", NULL },
+    { 0x00b1, "TLS_PSK_WITH_NULL_SHA384", NULL },
+    { 0x00b2, "TLS_DHE_PSK_WITH_AES_128_CBC_SHA256", NULL },
+    { 0x00b3, "TLS_DHE_PSK_WITH_AES_256_CBC_SHA384", NULL },
+    { 0x00b4, "TLS_DHE_PSK_WITH_NULL_SHA256", NULL },
+    { 0x00b5, "TLS_DHE_PSK_WITH_NULL_SHA384", NULL },
+    { 0x00b6, "TLS_RSA_PSK_WITH_AES_128_CBC_SHA256", NULL },
+    { 0x00b7, "TLS_RSA_PSK_WITH_AES_256_CBC_SHA384", NULL },
+    { 0x00b8, "TLS_RSA_PSK_WITH_NULL_SHA256", NULL },
+    { 0x00b9, "TLS_RSA_PSK_WITH_NULL_SHA384", NULL },
+    { 0x00ba, "TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256", NULL },
+    { 0x00bb, "TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256", NULL },
+    { 0x00bc, "TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256", NULL },
+    { 0x00bd, "TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256", NULL },
+    { 0x00be, "TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256", NULL },
+    { 0x00bf, "TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256", NULL },
+    { 0x00c0, "TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256", NULL },
+    { 0x00c1, "TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256", NULL },
+    { 0x00c2, "TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256", NULL },
+    { 0x00c3, "TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256", NULL },
+    { 0x00c4, "TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256", NULL },
+    { 0x00c5, "TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256", NULL },
+    { 0x00ff, "TLS_EMPTY_RENEGOTIATION_INFO_SCSV", NULL },
+    { 0x1301, "TLS_AES_128_GCM_SHA256", "TLS13_AES_128_GCM_SHA256" },
+    { 0x1302, "TLS_AES_256_GCM_SHA384", "TLS13_AES_256_GCM_SHA384" },
+    { 0x1303, "TLS_CHACHA20_POLY1305_SHA256", "TLS13_CHACHA20_POLY1305_SHA256" },
+    { 0x1304, "TLS_AES_128_CCM_SHA256", "TLS13_AES_128_CCM_SHA256" },
+    { 0x1305, "TLS_AES_128_CCM_8_SHA256", "TLS13_AES_128_CCM_8_SHA256" },
+    { 0xc001, "TLS_ECDH_ECDSA_WITH_NULL_SHA", NULL },
+    { 0xc002, "TLS_ECDH_ECDSA_WITH_RC4_128_SHA", NULL },
+    { 0xc003, "TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA", NULL },
+    { 0xc004, "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA", NULL },
+    { 0xc005, "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA", NULL },
+    { 0xc006, "TLS_ECDHE_ECDSA_WITH_NULL_SHA", NULL },
+    { 0xc007, "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA", NULL },
+    { 0xc008, "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA", NULL },
+    { 0xc009, "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", NULL },
+    { 0xc00a, "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", NULL },
+    { 0xc00b, "TLS_ECDH_RSA_WITH_NULL_SHA", NULL },
+    { 0xc00c, "TLS_ECDH_RSA_WITH_RC4_128_SHA", NULL },
+    { 0xc00d, "TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA", NULL },
+    { 0xc00e, "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA", NULL },
+    { 0xc00f, "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA", NULL },
+    { 0xc010, "TLS_ECDHE_RSA_WITH_NULL_SHA", NULL },
+    { 0xc011, "TLS_ECDHE_RSA_WITH_RC4_128_SHA", NULL },
+    { 0xc012, "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA", NULL },
+    { 0xc013, "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", NULL },
+    { 0xc014, "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", NULL },
+    { 0xc015, "TLS_ECDH_anon_WITH_NULL_SHA", NULL },
+    { 0xc016, "TLS_ECDH_anon_WITH_RC4_128_SHA", NULL },
+    { 0xc017, "TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA", NULL },
+    { 0xc018, "TLS_ECDH_anon_WITH_AES_128_CBC_SHA", NULL },
+    { 0xc019, "TLS_ECDH_anon_WITH_AES_256_CBC_SHA", NULL },
+    { 0xc01a, "TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA", NULL },
+    { 0xc01b, "TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA", NULL },
+    { 0xc01c, "TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA", NULL },
+    { 0xc01d, "TLS_SRP_SHA_WITH_AES_128_CBC_SHA", NULL },
+    { 0xc01e, "TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA", NULL },
+    { 0xc01f, "TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA", NULL },
+    { 0xc020, "TLS_SRP_SHA_WITH_AES_256_CBC_SHA", NULL },
+    { 0xc021, "TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA", NULL },
+    { 0xc022, "TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA", NULL },
+    { 0xc023, "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", "ECDHE-ECDSA-AES128-SHA256" },
+    { 0xc024, "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", "ECDHE-ECDSA-AES256-SHA384" },
+    { 0xc025, "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256", "ECDH-ECDSA-AES128-SHA256" },
+    { 0xc026, "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384", "ECDH-ECDSA-AES256-SHA384" },
+    { 0xc027, "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", "ECDHE-RSA-AES128-SHA256" },
+    { 0xc028, "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384", "ECDHE-RSA-AES256-SHA384" },
+    { 0xc029, "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256", "ECDH-RSA-AES128-SHA256" },
+    { 0xc02a, "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384", "ECDH-RSA-AES256-SHA384" },
+    { 0xc02b, "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", "ECDHE-ECDSA-AES128-GCM-SHA256" },
+    { 0xc02c, "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", "ECDHE-ECDSA-AES256-GCM-SHA384" },
+    { 0xc02d, "TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256", "ECDH-ECDSA-AES128-GCM-SHA256" },
+    { 0xc02e, "TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384", "ECDH-ECDSA-AES256-GCM-SHA384" },
+    { 0xc02f, "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", "ECDHE-RSA-AES128-GCM-SHA256" },
+    { 0xc030, "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", "ECDHE-RSA-AES256-GCM-SHA384" },
+    { 0xc031, "TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256", "ECDH-RSA-AES128-GCM-SHA256" },
+    { 0xc032, "TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384", "ECDH-RSA-AES256-GCM-SHA384" },
+    { 0xc033, "TLS_ECDHE_PSK_WITH_RC4_128_SHA", NULL },
+    { 0xc034, "TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA", NULL },
+    { 0xc035, "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA", NULL },
+    { 0xc036, "TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA", NULL },
+    { 0xc037, "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256", NULL },
+    { 0xc038, "TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384", NULL },
+    { 0xc039, "TLS_ECDHE_PSK_WITH_NULL_SHA", NULL },
+    { 0xc03a, "TLS_ECDHE_PSK_WITH_NULL_SHA256", NULL },
+    { 0xc03b, "TLS_ECDHE_PSK_WITH_NULL_SHA384", NULL },
+    { 0xc03c, "TLS_RSA_WITH_ARIA_128_CBC_SHA256", NULL },
+    { 0xc03d, "TLS_RSA_WITH_ARIA_256_CBC_SHA384", NULL },
+    { 0xc03e, "TLS_DH_DSS_WITH_ARIA_128_CBC_SHA256", NULL },
+    { 0xc03f, "TLS_DH_DSS_WITH_ARIA_256_CBC_SHA384", NULL },
+    { 0xc040, "TLS_DH_RSA_WITH_ARIA_128_CBC_SHA256", NULL },
+    { 0xc041, "TLS_DH_RSA_WITH_ARIA_256_CBC_SHA384", NULL },
+    { 0xc042, "TLS_DHE_DSS_WITH_ARIA_128_CBC_SHA256", NULL },
+    { 0xc043, "TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384", NULL },
+    { 0xc044, "TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256", NULL },
+    { 0xc045, "TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384", NULL },
+    { 0xc046, "TLS_DH_anon_WITH_ARIA_128_CBC_SHA256", NULL },
+    { 0xc047, "TLS_DH_anon_WITH_ARIA_256_CBC_SHA384", NULL },
+    { 0xc048, "TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256", NULL },
+    { 0xc049, "TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384", NULL },
+    { 0xc04a, "TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256", NULL },
+    { 0xc04b, "TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384", NULL },
+    { 0xc04c, "TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256", NULL },
+    { 0xc04d, "TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384", NULL },
+    { 0xc04e, "TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256", NULL },
+    { 0xc04f, "TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384", NULL },
+    { 0xc050, "TLS_RSA_WITH_ARIA_128_GCM_SHA256", NULL },
+    { 0xc051, "TLS_RSA_WITH_ARIA_256_GCM_SHA384", NULL },
+    { 0xc052, "TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256", NULL },
+    { 0xc053, "TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384", NULL },
+    { 0xc054, "TLS_DH_RSA_WITH_ARIA_128_GCM_SHA256", NULL },
+    { 0xc055, "TLS_DH_RSA_WITH_ARIA_256_GCM_SHA384", NULL },
+    { 0xc056, "TLS_DHE_DSS_WITH_ARIA_128_GCM_SHA256", NULL },
+    { 0xc057, "TLS_DHE_DSS_WITH_ARIA_256_GCM_SHA384", NULL },
+    { 0xc058, "TLS_DH_DSS_WITH_ARIA_128_GCM_SHA256", NULL },
+    { 0xc059, "TLS_DH_DSS_WITH_ARIA_256_GCM_SHA384", NULL },
+    { 0xc05a, "TLS_DH_anon_WITH_ARIA_128_GCM_SHA256", NULL },
+    { 0xc05b, "TLS_DH_anon_WITH_ARIA_256_GCM_SHA384", NULL },
+    { 0xc05c, "TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256", NULL },
+    { 0xc05d, "TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384", NULL },
+    { 0xc05e, "TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256", NULL },
+    { 0xc05f, "TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384", NULL },
+    { 0xc060, "TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256", NULL },
+    { 0xc061, "TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384", NULL },
+    { 0xc062, "TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256", NULL },
+    { 0xc063, "TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384", NULL },
+    { 0xc064, "TLS_PSK_WITH_ARIA_128_CBC_SHA256", NULL },
+    { 0xc065, "TLS_PSK_WITH_ARIA_256_CBC_SHA384", NULL },
+    { 0xc066, "TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256", NULL },
+    { 0xc067, "TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384", NULL },
+    { 0xc068, "TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256", NULL },
+    { 0xc069, "TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384", NULL },
+    { 0xc06a, "TLS_PSK_WITH_ARIA_128_GCM_SHA256", NULL },
+    { 0xc06b, "TLS_PSK_WITH_ARIA_256_GCM_SHA384", NULL },
+    { 0xc06c, "TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256", NULL },
+    { 0xc06d, "TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384", NULL },
+    { 0xc06e, "TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256", NULL },
+    { 0xc06f, "TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384", NULL },
+    { 0xc070, "TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256", NULL },
+    { 0xc071, "TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384", NULL },
+    { 0xc072, "TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256", NULL },
+    { 0xc073, "TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384", NULL },
+    { 0xc074, "TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256", NULL },
+    { 0xc075, "TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384", NULL },
+    { 0xc076, "TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256", NULL },
+    { 0xc077, "TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384", NULL },
+    { 0xc078, "TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256", NULL },
+    { 0xc079, "TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384", NULL },
+    { 0xc07a, "TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256", NULL },
+    { 0xc07b, "TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384", NULL },
+    { 0xc07c, "TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256", NULL },
+    { 0xc07d, "TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384", NULL },
+    { 0xc07e, "TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256", NULL },
+    { 0xc07f, "TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384", NULL },
+    { 0xc080, "TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256", NULL },
+    { 0xc081, "TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384", NULL },
+    { 0xc082, "TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256", NULL },
+    { 0xc083, "TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384", NULL },
+    { 0xc084, "TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256", NULL },
+    { 0xc085, "TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384", NULL },
+    { 0xc086, "TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256", NULL },
+    { 0xc087, "TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384", NULL },
+    { 0xc088, "TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256", NULL },
+    { 0xc089, "TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384", NULL },
+    { 0xc08a, "TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256", NULL },
+    { 0xc08b, "TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384", NULL },
+    { 0xc08c, "TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256", NULL },
+    { 0xc08d, "TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384", NULL },
+    { 0xc08e, "TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256", NULL },
+    { 0xc08f, "TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384", NULL },
+    { 0xc090, "TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256", NULL },
+    { 0xc091, "TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384", NULL },
+    { 0xc092, "TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256", NULL },
+    { 0xc093, "TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384", NULL },
+    { 0xc094, "TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256", NULL },
+    { 0xc095, "TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384", NULL },
+    { 0xc096, "TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256", NULL },
+    { 0xc097, "TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384", NULL },
+    { 0xc098, "TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256", NULL },
+    { 0xc099, "TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384", NULL },
+    { 0xc09a, "TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256", NULL },
+    { 0xc09b, "TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384", NULL },
+    { 0xc09c, "TLS_RSA_WITH_AES_128_CCM", NULL },
+    { 0xc09d, "TLS_RSA_WITH_AES_256_CCM", NULL },
+    { 0xc09e, "TLS_DHE_RSA_WITH_AES_128_CCM", NULL },
+    { 0xc09f, "TLS_DHE_RSA_WITH_AES_256_CCM", NULL },
+    { 0xc0a0, "TLS_RSA_WITH_AES_128_CCM_8", NULL },
+    { 0xc0a1, "TLS_RSA_WITH_AES_256_CCM_8", NULL },
+    { 0xc0a2, "TLS_DHE_RSA_WITH_AES_128_CCM_8", NULL },
+    { 0xc0a3, "TLS_DHE_RSA_WITH_AES_256_CCM_8", NULL },
+    { 0xc0a4, "TLS_PSK_WITH_AES_128_CCM", NULL },
+    { 0xc0a5, "TLS_PSK_WITH_AES_256_CCM", NULL },
+    { 0xc0a6, "TLS_DHE_PSK_WITH_AES_128_CCM", NULL },
+    { 0xc0a7, "TLS_DHE_PSK_WITH_AES_256_CCM", NULL },
+    { 0xc0a8, "TLS_PSK_WITH_AES_128_CCM_8", NULL },
+    { 0xc0a9, "TLS_PSK_WITH_AES_256_CCM_8", NULL },
+    { 0xc0aa, "TLS_PSK_DHE_WITH_AES_128_CCM_8", NULL },
+    { 0xc0ab, "TLS_PSK_DHE_WITH_AES_256_CCM_8", NULL },
+    { 0xcca8, "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", "ECDHE-RSA-CHACHA20-POLY1305" },
+    { 0xcca9, "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", "ECDHE-ECDSA-CHACHA20-POLY1305" },
+    { 0xccaa, "TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256", "DHE-RSA-CHACHA20-POLY1305" },
+    { 0xccab, "TLS_PSK_WITH_CHACHA20_POLY1305_SHA256", "PSK-CHACHA20-POLY1305" },
+    { 0xccac, "TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256", "ECDHE-PSK-CHACHA20-POLY1305" },
+    { 0xccad, "TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256", "DHE-PSK-CHACHA20-POLY1305" },
+    { 0xccae, "TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256", "RSA-PSK-CHACHA20-POLY1305" },
+    { 0xfefe, "SSL_RSA_FIPS_WITH_DES_CBC_SHA", NULL },
+    { 0xfeff, "SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA", NULL },
+};
+
+typedef struct {
+    apr_uint16_t id;
+    const rustls_supported_ciphersuite *rustls_suite;
+} rustls_cipher_t;
+
+tls_proto_conf_t *tls_proto_init(apr_pool_t *pool, server_rec *s)
+{
+    tls_proto_conf_t *conf;
+    tls_cipher_t *cipher;
+    const rustls_supported_ciphersuite *rustls_suite;
+    rustls_cipher_t *rcipher;
+    apr_uint16_t id;
+    apr_size_t i;
+
+    (void)s;
+    conf = apr_pcalloc(pool, sizeof(*conf));
+
+    conf->supported_versions = apr_array_make(pool, 3, sizeof(apr_uint16_t));
+    /* Until we can look that up at crustls, we assume what we currently know */
+    APR_ARRAY_PUSH(conf->supported_versions, apr_uint16_t) = TLS_VERSION_1_2;
+    APR_ARRAY_PUSH(conf->supported_versions, apr_uint16_t) = TLS_VERSION_1_3;
+
+    conf->known_ciphers_by_name = apr_hash_make(pool);
+    conf->known_ciphers_by_id = apr_hash_make(pool);
+    for (i = 0; i < TLS_DIM(KNOWN_CIPHERS); ++i) {
+        cipher = &KNOWN_CIPHERS[i];
+        apr_hash_set(conf->known_ciphers_by_id, &cipher->id, sizeof(apr_uint16_t), cipher);
+        apr_hash_set(conf->known_ciphers_by_name, cipher->name, APR_HASH_KEY_STRING, cipher);
+        if (cipher->alias) {
+            apr_hash_set(conf->known_ciphers_by_name, cipher->alias, APR_HASH_KEY_STRING, cipher);
+        }
+    }
+
+    conf->supported_cipher_ids = apr_array_make(pool, 10, sizeof(apr_uint16_t));
+    conf->rustls_ciphers_by_id = apr_hash_make(pool);
+    i = 0;
+    while ((rustls_suite = rustls_all_ciphersuites_get_entry(i++))) {
+        id = rustls_supported_ciphersuite_get_suite(rustls_suite);
+        rcipher = apr_pcalloc(pool, sizeof(*rcipher));
+        rcipher->id = id;
+        rcipher->rustls_suite = rustls_suite;
+        APR_ARRAY_PUSH(conf->supported_cipher_ids, apr_uint16_t) = id;
+        apr_hash_set(conf->rustls_ciphers_by_id, &rcipher->id, sizeof(apr_uint16_t), rcipher);
+
+    }
+
+    return conf;
+}
+
+const char *tls_proto_get_cipher_names(
+    tls_proto_conf_t *conf, const apr_array_header_t *ciphers, apr_pool_t *pool)
+{
+    apr_array_header_t *names;
+    int n;
+
+    names = apr_array_make(pool, ciphers->nelts, sizeof(const char*));
+    for (n = 0; n < ciphers->nelts; ++n) {
+        apr_uint16_t id = APR_ARRAY_IDX(ciphers, n, apr_uint16_t);
+        APR_ARRAY_PUSH(names, const char *) = tls_proto_get_cipher_name(conf, id, pool);
+    }
+    return apr_array_pstrcat(pool, names, ':');
+}
+
+apr_status_t tls_proto_pre_config(apr_pool_t *pool, apr_pool_t *ptemp)
+{
+    (void)pool;
+    (void)ptemp;
+    return APR_SUCCESS;
+}
+
+apr_status_t tls_proto_post_config(apr_pool_t *pool, apr_pool_t *ptemp, server_rec *s)
+{
+    tls_conf_server_t *sc = tls_conf_server_get(s);
+    tls_proto_conf_t *conf = sc->global->proto;
+
+    (void)pool;
+    if (APLOGdebug(s)) {
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(10314)
+                     "tls ciphers supported: %s",
+                     tls_proto_get_cipher_names(conf, conf->supported_cipher_ids, ptemp));
+    }
+    return APR_SUCCESS;
+}
+
+static apr_status_t get_uint16_from(const char *name, const char *prefix, apr_uint16_t *pint)
+{
+    apr_size_t plen = strlen(prefix);
+    if (strlen(name) == plen+4 && !strncmp(name, prefix, plen)) {
+        /* may be a hex notation cipher id */
+        char *end = NULL;
+        apr_int64_t code = apr_strtoi64(name + plen, &end, 16);
+        if ((!end || !*end) && code && code <= APR_UINT16_MAX) {
+            *pint = (apr_uint16_t)code;
+            return APR_SUCCESS;
+        }
+    }
+    return APR_ENOENT;
+}
+
+apr_uint16_t tls_proto_get_version_by_name(tls_proto_conf_t *conf, const char *name)
+{
+    apr_uint16_t version;
+    (void)conf;
+    if (!apr_strnatcasecmp(name, "TLSv1.2")) {
+        return TLS_VERSION_1_2;
+    }
+    else if (!apr_strnatcasecmp(name, "TLSv1.3")) {
+        return TLS_VERSION_1_3;
+    }
+    if (APR_SUCCESS == get_uint16_from(name, "TLSv0x", &version)) {
+        return version;
+    }
+    return 0;
+}
+
+const char *tls_proto_get_version_name(
+    tls_proto_conf_t *conf, apr_uint16_t id, apr_pool_t *pool)
+{
+    (void)conf;
+    switch (id) {
+    case TLS_VERSION_1_2:
+        return "TLSv1.2";
+    case TLS_VERSION_1_3:
+        return "TLSv1.3";
+    default:
+        return apr_psprintf(pool, "TLSv0x%04x", id);
+    }
+}
+
+apr_array_header_t *tls_proto_create_versions_plus(
+    tls_proto_conf_t *conf, apr_uint16_t min_version, apr_pool_t *pool)
+{
+    apr_array_header_t *versions = apr_array_make(pool, 3, sizeof(apr_uint16_t));
+    apr_uint16_t version;
+    int i;
+
+    for (i = 0; i < conf->supported_versions->nelts; ++i) {
+        version = APR_ARRAY_IDX(conf->supported_versions, i, apr_uint16_t);
+        if (version >= min_version) {
+            APR_ARRAY_PUSH(versions, apr_uint16_t) = version;
+        }
+    }
+    return versions;
+}
+
+int tls_proto_is_cipher_supported(tls_proto_conf_t *conf, apr_uint16_t cipher)
+{
+    return tls_util_array_uint16_contains(conf->supported_cipher_ids, cipher);
+}
+
+apr_status_t tls_proto_get_cipher_by_name(
+    tls_proto_conf_t *conf, const char *name, apr_uint16_t *pcipher)
+{
+    tls_cipher_t *cipher = apr_hash_get(conf->known_ciphers_by_name, name, APR_HASH_KEY_STRING);
+    if (cipher) {
+        *pcipher = cipher->id;
+        return APR_SUCCESS;
+    }
+    return get_uint16_from(name, "TLS_CIPHER_0x", pcipher);
+}
+
+const char *tls_proto_get_cipher_name(
+    tls_proto_conf_t *conf, apr_uint16_t id, apr_pool_t *pool)
+{
+    tls_cipher_t *cipher = apr_hash_get(conf->known_ciphers_by_id, &id, sizeof(apr_uint16_t));
+    if (cipher) {
+        return cipher->name;
+    }
+    return apr_psprintf(pool, "TLS_CIPHER_0x%04x", id);
+}
+
+apr_array_header_t *tls_proto_get_rustls_suites(
+    tls_proto_conf_t *conf, const apr_array_header_t *ids, apr_pool_t *pool)
+{
+    apr_array_header_t *suites;
+    rustls_cipher_t *rcipher;
+    apr_uint16_t id;
+    int i;
+
+    suites = apr_array_make(pool, ids->nelts, sizeof(const rustls_supported_ciphersuite*));
+    for (i = 0; i < ids->nelts; ++i) {
+        id = APR_ARRAY_IDX(ids, i, apr_uint16_t);
+        rcipher = apr_hash_get(conf->rustls_ciphers_by_id, &id, sizeof(apr_uint16_t));
+        if (rcipher) {
+            APR_ARRAY_PUSH(suites, const rustls_supported_ciphersuite *) = rcipher->rustls_suite;
+        }
+    }
+    return suites;
+}

Added: httpd/httpd/trunk/modules/tls/tls_proto.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/tls/tls_proto.h?rev=1895432&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/tls/tls_proto.h (added)
+++ httpd/httpd/trunk/modules/tls/tls_proto.h Tue Nov 30 16:29:20 2021
@@ -0,0 +1,124 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef tls_proto_h
+#define tls_proto_h
+
+#include "tls_util.h"
+
+
+#define TLS_VERSION_1_2   0x0303
+#define TLS_VERSION_1_3   0x0304
+
+/**
+ * Specification of a TLS cipher by name, possible alias and its 16 bit value
+ * as assigned by IANA.
+ */
+typedef struct {
+    apr_uint16_t id;      /* IANA 16-bit assigned value as used on the wire */
+    const char *name;     /* IANA given name of hte cipher */
+    const char *alias;    /* Optional, commonly known alternate name */
+} tls_cipher_t;
+
+/**
+ * TLS protocol related definitions constructed
+ * by querying crustls lib.
+ */
+typedef struct tls_proto_conf_t tls_proto_conf_t;
+struct tls_proto_conf_t {
+    apr_array_header_t *supported_versions; /* supported protocol versions (apr_uint16_t) */
+    apr_hash_t *known_ciphers_by_name; /* hash by name of known tls_cipher_t* */
+    apr_hash_t *known_ciphers_by_id; /* hash by id of known tls_cipher_t* */
+    apr_hash_t *rustls_ciphers_by_id; /* hash by id of rustls rustls_supported_ciphersuite* */
+    apr_array_header_t *supported_cipher_ids; /* cipher ids (apr_uint16_t) supported by rustls */
+    const rustls_root_cert_store *native_roots;
+};
+
+/**
+ * Create and populate the protocol configuration.
+ */
+tls_proto_conf_t *tls_proto_init(apr_pool_t *p, server_rec *s);
+
+/**
+ * Called during pre-config phase to start intialization
+ * of the tls protocol configuration.
+ */
+apr_status_t tls_proto_pre_config(apr_pool_t *pool, apr_pool_t *ptemp);
+
+/**
+ * Called during post-config phase to conclude the intialization
+ * of the tls protocol configuration.
+ */
+apr_status_t tls_proto_post_config(apr_pool_t *p, apr_pool_t *ptemp, server_rec *s);
+
+/**
+ * Get the TLS protocol identifer (as used on the wire) for the TLS
+ * protocol of the given name. Returns 0 if protocol is unknown.
+ */
+apr_uint16_t tls_proto_get_version_by_name(tls_proto_conf_t *conf, const char *name);
+
+/**
+ * Get the name of the protocol version identified by its identifier. This
+ * will return the name from the protocol configuration or, if unknown, create
+ * the string `TLSv0x%04x` from the 16bit identifier.
+ */
+const char *tls_proto_get_version_name(
+    tls_proto_conf_t *conf, apr_uint16_t id, apr_pool_t *pool);
+
+/**
+ * Create an array of the given TLS protocol version identifier `min_version`
+ * and all supported new ones. The array carries apr_uint16_t values.
+ */
+apr_array_header_t *tls_proto_create_versions_plus(
+    tls_proto_conf_t *conf, apr_uint16_t min_version, apr_pool_t *pool);
+
+/**
+ * Get a TLS cipher spec by name/alias.
+ */
+apr_status_t tls_proto_get_cipher_by_name(
+    tls_proto_conf_t *conf, const char *name, apr_uint16_t *pcipher);
+
+/**
+ * Return != 0 iff the cipher is supported by the rustls library.
+ */
+int tls_proto_is_cipher_supported(tls_proto_conf_t *conf, apr_uint16_t cipher);
+
+/**
+ * Get the name of a TLS cipher for the IANA assigned 16bit value. This will
+ * return the name in the protocol configuation, if the cipher is known, and
+ * create the string `TLS_CIPHER_0x%04x` for the 16bit cipher value.
+ */
+const char *tls_proto_get_cipher_name(
+    tls_proto_conf_t *conf, apr_uint16_t cipher, apr_pool_t *pool);
+
+/**
+ * Get the concatenated names with ':' as separator of all TLS cipher identifiers
+ * as given in `ciphers`.
+ * @param conf the TLS protocol configuration
+ * @param ciphers the 16bit values of the TLS ciphers
+ * @param pool to use for allocation the string.
+ */
+const char *tls_proto_get_cipher_names(
+    tls_proto_conf_t *conf, const apr_array_header_t *ciphers, apr_pool_t *pool);
+
+/**
+ * Convert an array of TLS cipher 16bit identifiers into the `rustls_supported_ciphersuite`
+ * instances that can be passed to crustls in session configurations.
+ * Any cipher identifier not supported by rustls we be silently omitted.
+ */
+apr_array_header_t *tls_proto_get_rustls_suites(
+    tls_proto_conf_t *conf, const apr_array_header_t *ids, apr_pool_t *pool);
+
+#endif /* tls_proto_h */
\ No newline at end of file