]> git.kaiwu.me - nginx.git/commitdiff
SSL: object caching.
authorSergey Kandaurov <pluknet@nginx.com>
Mon, 9 Sep 2024 15:03:20 +0000 (19:03 +0400)
committerpluknet <pluknet@nginx.com>
Tue, 1 Oct 2024 13:59:24 +0000 (17:59 +0400)
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.

auto/modules
src/event/ngx_event_openssl.h
src/event/ngx_event_openssl_cache.c [new file with mode: 0644]

index 1a5e4212d0b38f0f2be6e2de3ff8775fc772130a..38b3aba78b5b2ee655df2ed43d19ce24a8cb78fc 100644 (file)
@@ -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
index 41566422a8a819d819d9ff3846bf28504d416a3f..fa2282ce83abe322924dac7c28dd7a467959afab 100644 (file)
@@ -224,6 +224,11 @@ ngx_int_t ngx_ssl_ocsp_get_status(ngx_connection_t *c, const char **s);
 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 *path, void *data);
+void *ngx_ssl_cache_connection_fetch(ngx_pool_t *pool, ngx_uint_t index,
+    char **err, ngx_str_t *path, 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 (file)
index 0000000..98867de
--- /dev/null
@@ -0,0 +1,311 @@
+
+/*
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+#define NGX_SSL_CACHE_PATH    0
+
+
+typedef struct {
+    unsigned                    type:2;
+    unsigned                    len:30;
+    u_char                     *data;
+} ngx_ssl_cache_key_t;
+
+
+typedef void *(*ngx_ssl_cache_create_pt)(ngx_ssl_cache_key_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_ssl_cache_key_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_int_t ngx_ssl_cache_init_key(ngx_pool_t *pool, ngx_uint_t index,
+    ngx_str_t *path, ngx_ssl_cache_key_t *id);
+static ngx_ssl_cache_node_t *ngx_ssl_cache_lookup(ngx_ssl_cache_t *cache,
+    ngx_ssl_cache_type_t *type, ngx_ssl_cache_key_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 *path, void *data)
+{
+    uint32_t               hash;
+    ngx_ssl_cache_t       *cache;
+    ngx_ssl_cache_key_t    id;
+    ngx_ssl_cache_type_t  *type;
+    ngx_ssl_cache_node_t  *cn;
+
+    if (ngx_ssl_cache_init_key(cf->pool, index, path, &id) != NGX_OK) {
+        return NULL;
+    }
+
+    cache = (ngx_ssl_cache_t *) ngx_get_conf(cf->cycle->conf_ctx,
+                                             ngx_openssl_cache_module);
+
+    type = &ngx_ssl_cache_types[index];
+    hash = ngx_murmur_hash2(id.data, id.len);
+
+    cn = ngx_ssl_cache_lookup(cache, type, &id, hash);
+    if (cn != NULL) {
+        return type->ref(err, cn->value);
+    }
+
+    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->id.type = id.type;
+    cn->type = type;
+
+    ngx_cpystrn(cn->id.data, id.data, id.len + 1);
+
+    cn->value = type->create(&id, err, data);
+    if (cn->value == NULL) {
+        return NULL;
+    }
+
+    ngx_rbtree_insert(&cache->rbtree, &cn->node);
+
+    return type->ref(err, cn->value);
+}
+
+
+void *
+ngx_ssl_cache_connection_fetch(ngx_pool_t *pool, ngx_uint_t index, char **err,
+    ngx_str_t *path, void *data)
+{
+    ngx_ssl_cache_key_t  id;
+
+    if (ngx_ssl_cache_init_key(pool, index, path, &id) != NGX_OK) {
+        return NULL;
+    }
+
+    return ngx_ssl_cache_types[index].create(&id, err, data);
+}
+
+
+static ngx_int_t
+ngx_ssl_cache_init_key(ngx_pool_t *pool, ngx_uint_t index, ngx_str_t *path,
+    ngx_ssl_cache_key_t *id)
+{
+    if (ngx_get_full_name(pool, (ngx_str_t *) &ngx_cycle->conf_prefix, path)
+        != NGX_OK)
+    {
+        return NGX_ERROR;
+    }
+
+    id->type = NGX_SSL_CACHE_PATH;
+
+    id->len = path->len;
+    id->data = path->data;
+
+    return NGX_OK;
+}
+
+
+static ngx_ssl_cache_node_t *
+ngx_ssl_cache_lookup(ngx_ssl_cache_t *cache, ngx_ssl_cache_type_t *type,
+    ngx_ssl_cache_key_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 (type < cn->type) {
+            node = node->left;
+            continue;
+        }
+
+        if (type > 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);
+        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);
+}