[PATCH 2 of 6] SSL: object caching

Sergey Kandaurov pluknet at nginx.com
Wed Aug 21 22:04:53 UTC 2024


# HG changeset patch
# User Sergey Kandaurov <pluknet at nginx.com>
# Date 1721762842 0
#      Tue Jul 23 19:27:22 2024 +0000
# Node ID 6fbe0bcb81696bba12d186e5c15323046bcac2d9
# Parent  6baaa6efe6f0a2e8b95374717cd5f73db8a3a862
SSL: object caching.

Added ngx_openssl_cache_module, which indexes a type-aware object cache.
It maps an id to a unique instance, and provides references to it, which
are dropped when the cycle's pool is destroyed.

The cache will be used in subsequent patches.

Based on previous work by Mini Hawthorne.

diff --git a/auto/modules b/auto/modules
--- a/auto/modules
+++ b/auto/modules
@@ -1307,10 +1307,11 @@ fi
 
 if [ $USE_OPENSSL = YES ]; then
     ngx_module_type=CORE
-    ngx_module_name=ngx_openssl_module
+    ngx_module_name="ngx_openssl_module ngx_openssl_cache_module"
     ngx_module_incs=
     ngx_module_deps=src/event/ngx_event_openssl.h
     ngx_module_srcs="src/event/ngx_event_openssl.c
+                     src/event/ngx_event_openssl_cache.c
                      src/event/ngx_event_openssl_stapling.c"
     ngx_module_libs=
     ngx_module_link=YES
diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h
--- a/src/event/ngx_event_openssl.h
+++ b/src/event/ngx_event_openssl.h
@@ -233,6 +233,11 @@ ngx_int_t ngx_ssl_ocsp_get_status(ngx_co
 void ngx_ssl_ocsp_cleanup(ngx_connection_t *c);
 ngx_int_t ngx_ssl_ocsp_cache_init(ngx_shm_zone_t *shm_zone, void *data);
 
+void *ngx_ssl_cache_fetch(ngx_conf_t *cf, ngx_uint_t index, char **err,
+    ngx_str_t *id, void *data);
+void *ngx_ssl_cache_connection_fetch(ngx_uint_t index, char **err,
+    ngx_str_t *id, void *data);
+
 ngx_array_t *ngx_ssl_read_password_file(ngx_conf_t *cf, ngx_str_t *file);
 ngx_array_t *ngx_ssl_preserve_passwords(ngx_conf_t *cf,
     ngx_array_t *passwords);
diff --git a/src/event/ngx_event_openssl_cache.c b/src/event/ngx_event_openssl_cache.c
new file mode 100644
--- /dev/null
+++ b/src/event/ngx_event_openssl_cache.c
@@ -0,0 +1,285 @@
+
+/*
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+typedef void *(*ngx_ssl_cache_create_pt)(ngx_str_t *id, char **err, void *data);
+typedef void (*ngx_ssl_cache_free_pt)(void *data);
+typedef void *(*ngx_ssl_cache_ref_pt)(char **err, void *data);
+
+
+typedef struct {
+    ngx_ssl_cache_create_pt     create;
+    ngx_ssl_cache_free_pt       free;
+    ngx_ssl_cache_ref_pt        ref;
+} ngx_ssl_cache_type_t;
+
+
+typedef struct {
+    ngx_rbtree_node_t           node;
+    ngx_str_t                   id;
+
+    ngx_ssl_cache_type_t       *type;
+    void                       *value;
+} ngx_ssl_cache_node_t;
+
+
+typedef struct {
+    ngx_rbtree_t                rbtree;
+    ngx_rbtree_node_t           sentinel;
+} ngx_ssl_cache_t;
+
+
+static ngx_ssl_cache_node_t *ngx_ssl_cache_lookup(ngx_ssl_cache_t *cache,
+    ngx_ssl_cache_type_t *type, ngx_str_t *id, uint32_t hash);
+
+static void *ngx_openssl_cache_create_conf(ngx_cycle_t *cycle);
+static void ngx_ssl_cache_cleanup(void *data);
+static void ngx_ssl_cache_node_insert(ngx_rbtree_node_t *temp,
+    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);
+
+
+static ngx_core_module_t  ngx_openssl_cache_module_ctx = {
+    ngx_string("openssl_cache"),
+    ngx_openssl_cache_create_conf,
+    NULL
+};
+
+
+ngx_module_t  ngx_openssl_cache_module = {
+    NGX_MODULE_V1,
+    &ngx_openssl_cache_module_ctx,         /* module context */
+    NULL,                                  /* module directives */
+    NGX_CORE_MODULE,                       /* module type */
+    NULL,                                  /* init master */
+    NULL,                                  /* init module */
+    NULL,                                  /* init process */
+    NULL,                                  /* init thread */
+    NULL,                                  /* exit thread */
+    NULL,                                  /* exit process */
+    NULL,                                  /* exit master */
+    NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_ssl_cache_type_t  ngx_ssl_cache_types[] = {
+
+};
+
+
+void *
+ngx_ssl_cache_fetch(ngx_conf_t *cf, ngx_uint_t index, char **err,
+    ngx_str_t *id, void *data)
+{
+    void                  *value;
+    uint32_t               hash;
+    ngx_ssl_cache_t       *cache;
+    ngx_ssl_cache_type_t  *type;
+    ngx_ssl_cache_node_t  *cn;
+
+    value = NULL;
+
+    hash = ngx_murmur_hash2(id->data, id->len);
+
+    cache = (ngx_ssl_cache_t *) ngx_get_conf(cf->cycle->conf_ctx,
+                                             ngx_openssl_cache_module);
+
+    type = &ngx_ssl_cache_types[index];
+
+    cn = ngx_ssl_cache_lookup(cache, type, id, hash);
+
+    if (cn == NULL) {
+        cn = ngx_palloc(cf->pool, sizeof(ngx_ssl_cache_node_t) + id->len + 1);
+        if (cn == NULL) {
+            return NULL;
+        }
+
+        cn->node.key = hash;
+        cn->id.data = (u_char *)(cn + 1);
+        cn->id.len = id->len;
+        cn->type = type;
+        cn->value = NULL;
+
+        ngx_cpystrn(cn->id.data, id->data, id->len + 1);
+
+        ngx_rbtree_insert(&cache->rbtree, &cn->node);
+    }
+
+    /* try to use a reference from the cache */
+
+    if (cn->value != NULL) {
+        value = type->ref(err, cn->value);
+    }
+
+    if (value == NULL) {
+        value = type->create(id, err, data);
+    }
+
+    if (value != NULL && cn->value == NULL) {
+        /* we have a value and the node needs one; try to reference it */
+        cn->value = type->ref(err, value);
+    }
+
+    return value;
+}
+
+
+void *
+ngx_ssl_cache_connection_fetch(ngx_uint_t index, char **err,
+    ngx_str_t *id, void *data)
+{
+    return ngx_ssl_cache_types[index].create(id, err, data);
+}
+
+
+static ngx_ssl_cache_node_t *
+ngx_ssl_cache_lookup(ngx_ssl_cache_t *cache, ngx_ssl_cache_type_t *type,
+    ngx_str_t *id, uint32_t hash)
+{
+    ngx_int_t              rc;
+    ngx_rbtree_node_t     *node, *sentinel;
+    ngx_ssl_cache_node_t  *cn;
+
+    node = cache->rbtree.root;
+    sentinel = cache->rbtree.sentinel;
+
+    while (node != sentinel) {
+
+        if (hash < node->key) {
+            node = node->left;
+            continue;
+        }
+
+        if (hash > node->key) {
+            node = node->right;
+            continue;
+        }
+
+        /* hash == node->key */
+
+        cn = (ngx_ssl_cache_node_t *) node;
+
+        if ((ngx_uint_t) type < (ngx_uint_t) cn->type) {
+            node = node->left;
+            continue;
+        }
+
+        if ((ngx_uint_t) type > (ngx_uint_t) cn->type) {
+            node = node->right;
+            continue;
+        }
+
+        /* type == cn->type */
+
+        rc = ngx_memn2cmp(id->data, cn->id.data, id->len, cn->id.len);
+
+        if (rc == 0) {
+            return cn;
+        }
+
+        node = (rc < 0) ? node->left : node->right;
+    }
+
+    return NULL;
+}
+
+
+static void *
+ngx_openssl_cache_create_conf(ngx_cycle_t *cycle)
+{
+    ngx_ssl_cache_t     *cache;
+    ngx_pool_cleanup_t  *cln;
+
+    cache = ngx_pcalloc(cycle->pool, sizeof(ngx_ssl_cache_t));
+    if (cache == NULL) {
+        return NULL;
+    }
+
+    cln = ngx_pool_cleanup_add(cycle->pool, 0);
+    if (cln == NULL) {
+        return NULL;
+    }
+
+    cln->handler = ngx_ssl_cache_cleanup;
+    cln->data = cache;
+
+    ngx_rbtree_init(&cache->rbtree, &cache->sentinel,
+                    ngx_ssl_cache_node_insert);
+
+    return cache;
+}
+
+
+static void
+ngx_ssl_cache_cleanup(void *data)
+{
+    ngx_ssl_cache_t  *cache = data;
+
+    ngx_rbtree_t          *tree;
+    ngx_rbtree_node_t     *node;
+    ngx_ssl_cache_node_t  *cn;
+
+    tree = &cache->rbtree;
+
+    if (tree->root == tree->sentinel) {
+        return;
+    }
+
+    for (node = ngx_rbtree_min(tree->root, tree->sentinel);
+         node;
+         node = ngx_rbtree_next(tree, node))
+    {
+        cn = ngx_rbtree_data(node, ngx_ssl_cache_node_t, node);
+
+        if (cn->type != NULL && cn->value != NULL) {
+            cn->type->free(cn->value);
+        }
+    }
+}
+
+
+static void
+ngx_ssl_cache_node_insert(ngx_rbtree_node_t *temp,
+    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
+{
+    ngx_rbtree_node_t     **p;
+    ngx_ssl_cache_node_t   *n, *t;
+
+    for ( ;; ) {
+
+        n = ngx_rbtree_data(node, ngx_ssl_cache_node_t, node);
+        t = ngx_rbtree_data(temp, ngx_ssl_cache_node_t, node);
+
+        if (node->key != temp->key) {
+
+            p = (node->key < temp->key) ? &temp->left : &temp->right;
+
+        } else if (n->type != t->type) {
+
+            p = (n->type < t->type) ? &temp->left : &temp->right;
+
+        } else {
+
+            p = (ngx_memn2cmp(n->id.data, t->id.data, n->id.len, t->id.len)
+                 < 0) ? &temp->left : &temp->right;
+        }
+
+        if (*p == sentinel) {
+            break;
+        }
+
+        temp = *p;
+    }
+
+    *p = node;
+    node->parent = temp;
+    node->left = sentinel;
+    node->right = sentinel;
+    ngx_rbt_red(node);
+}


More information about the nginx-devel mailing list