# HG changeset patch # User Igor Sysoev # Date 1168290000 -10800 # Node ID 29a6403156b0d9baa641a033bf650c29237fa512 # Parent fcbee7dacf2bd2dcfa09bb977c08aa20ab852318 nginx 0.5.6 *) Change: now the ngx_http_index_module ignores all methods except the GET, HEAD, and POST methods. *) Feature: the ngx_http_limit_zone_module. *) Feature: the $binary_remote_addr variable. *) Feature: the "ssl_session_cache" directives of the ngx_http_ssl_module and ngx_imap_ssl_module. *) Feature: the DELETE method supports recursive removal. *) Bugfix: the byte-ranges were transferred incorrectly if the $r->sendfile() was used. diff --git a/CHANGES b/CHANGES --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,22 @@ +Changes with nginx 0.5.6 09 Jan 2007 + + *) Change: now the ngx_http_index_module ignores all methods except the + GET, HEAD, and POST methods. + + *) Feature: the ngx_http_limit_zone_module. + + *) Feature: the $binary_remote_addr variable. + + *) Feature: the "ssl_session_cache" directives of the + ngx_http_ssl_module and ngx_imap_ssl_module. + + *) Feature: the DELETE method supports recursive removal. + + *) Bugfix: the byte-ranges were transferred incorrectly if the + $r->sendfile() was used. + + Changes with nginx 0.5.5 24 Dec 2006 *) Change: the -v switch does not show compiler information any more. diff --git a/CHANGES.ru b/CHANGES.ru --- a/CHANGES.ru +++ b/CHANGES.ru @@ -1,4 +1,22 @@ +Изменения в nginx 0.5.6 09.01.2007 + + *) Изменение: теперь модуль ngx_http_index_module игнорирует все + методы, кроме GET, HEAD и POST. + + *) Добавление: модуль ngx_http_limit_zone_module. + + *) Добавление: переменная $binary_remote_addr. + + *) Добавление: директивы ssl_session_cache модулей ngx_http_ssl_module + и ngx_imap_ssl_module. + + *) Добавление: метод DELETE поддерживает рекурсивное удаление. + + *) Исправление: при использовании $r->sendfile() byte-ranges + передавались неверно. + + Изменения в nginx 0.5.5 24.12.2006 *) Изменение: ключ -v больше не выводит информацию о компиляторе. diff --git a/LICENSE b/LICENSE --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2006 Igor Sysoev + * Copyright (C) 2002-2007 Igor Sysoev * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/auto/modules b/auto/modules --- a/auto/modules +++ b/auto/modules @@ -169,6 +169,11 @@ if [ $HTTP_ACCESS = YES ]; then HTTP_SRCS="$HTTP_SRCS $HTTP_ACCESS_SRCS" fi +if [ $HTTP_LIMIT_ZONE = YES ]; then + HTTP_MODULES="$HTTP_MODULES $HTTP_LIMIT_ZONE_MODULE" + HTTP_SRCS="$HTTP_SRCS $HTTP_LIMIT_ZONE_SRCS" +fi + if [ $HTTP_REALIP = YES ]; then have=NGX_HTTP_REALIP . auto/have HTTP_MODULES="$HTTP_MODULES $HTTP_REALIP_MODULE" diff --git a/auto/options b/auto/options --- a/auto/options +++ b/auto/options @@ -68,6 +68,7 @@ HTTP_PROXY=YES HTTP_FASTCGI=YES HTTP_PERL=NO HTTP_MEMCACHED=YES +HTTP_LIMIT_ZONE=YES HTTP_EMPTY_GIF=YES HTTP_BROWSER=YES HTTP_FLV=NO @@ -169,6 +170,7 @@ do --without-http_proxy_module) HTTP_PROXY=NO ;; --without-http_fastcgi_module) HTTP_FASTCGI=NO ;; --without-http_memcached_module) HTTP_MEMCACHED=NO ;; + --without-http_limit_zone_module) HTTP_LIMIT_ZONE=NO ;; --without-http_empty_gif_module) HTTP_EMPTY_GIF=NO ;; --without-http_browser_module) HTTP_BROWSER=NO ;; --without-http_upstream_ip_hash_module) HTTP_UPSTREAM_IP_HASH=NO ;; @@ -271,6 +273,7 @@ cat << END --without-http_proxy_module disable ngx_http_proxy_module --without-http_fastcgi_module disable ngx_http_fastcgi_module --without-http_memcached_module disable ngx_http_memcached_module + --without-http_limit_zone_module disable ngx_http_limit_zone_module --without-http_empty_gif_module disable ngx_http_empty_gif_module --without-http_browser_module disable ngx_http_browser_module --without-http_upstream_ip_hash_module diff --git a/auto/sources b/auto/sources --- a/auto/sources +++ b/auto/sources @@ -388,6 +388,10 @@ HTTP_MEMCACHED_MODULE=ngx_http_memcached HTTP_MEMCACHED_SRCS=src/http/modules/ngx_http_memcached_module.c +HTTP_LIMIT_ZONE_MODULE=ngx_http_limit_zone_module +HTTP_LIMIT_ZONE_SRCS=src/http/modules/ngx_http_limit_zone_module.c + + HTTP_EMPTY_GIF_MODULE=ngx_http_empty_gif_module HTTP_EMPTY_GIF_SRCS=src/http/modules/ngx_http_empty_gif_module.c diff --git a/src/core/nginx.h b/src/core/nginx.h --- a/src/core/nginx.h +++ b/src/core/nginx.h @@ -8,7 +8,7 @@ #define _NGINX_H_INCLUDED_ -#define NGINX_VERSION "0.5.5" +#define NGINX_VERSION "0.5.6" #define NGINX_VER "nginx/" NGINX_VERSION #define NGINX_VAR "NGINX" diff --git a/src/core/ngx_conf_file.c b/src/core/ngx_conf_file.c --- a/src/core/ngx_conf_file.c +++ b/src/core/ngx_conf_file.c @@ -150,9 +150,9 @@ ngx_conf_parse(ngx_conf_t *cf, ngx_str_t if (rc == NGX_CONF_FILE_DONE && block) { ngx_log_error(NGX_LOG_EMERG, cf->log, 0, - "unexpected end of file in %s:%ui, expecting \"}\"", - cf->conf_file->file.name.data, - cf->conf_file->line); + "unexpected end of file in %s:%ui, expecting \"}\"", + cf->conf_file->file.name.data, + cf->conf_file->line); rc = NGX_ERROR; break; } @@ -179,9 +179,9 @@ ngx_conf_parse(ngx_conf_t *cf, ngx_str_t } ngx_log_error(NGX_LOG_EMERG, cf->log, 0, - "%s in %s:%ui", - rv, cf->conf_file->file.name.data, - cf->conf_file->line); + "%s in %s:%ui", + rv, cf->conf_file->file.name.data, + cf->conf_file->line); rc = NGX_ERROR; break; } diff --git a/src/core/ngx_core.h b/src/core/ngx_core.h --- a/src/core/ngx_core.h +++ b/src/core/ngx_core.h @@ -64,11 +64,11 @@ typedef void (*ngx_connection_handler_pt #include #include #include +#include +#include #if (NGX_OPENSSL) #include #endif -#include -#include #include #include #include diff --git a/src/core/ngx_cycle.c b/src/core/ngx_cycle.c --- a/src/core/ngx_cycle.c +++ b/src/core/ngx_cycle.c @@ -49,7 +49,7 @@ ngx_init_cycle(ngx_cycle_t *old_cycle) ngx_conf_t conf; ngx_pool_t *pool; ngx_cycle_t *cycle, **old; - ngx_shm_zone_t *shm, *oshm; + ngx_shm_zone_t *shm_zone, *oshm_zone; ngx_slab_pool_t *shpool; ngx_list_part_t *part, *opart; ngx_open_file_t *file; @@ -353,7 +353,7 @@ ngx_init_cycle(ngx_cycle_t *old_cycle) /* create shared memory */ part = &cycle->shared_memory.part; - shm = part->elts; + shm_zone = part->elts; for (i = 0; /* void */ ; i++) { @@ -362,14 +362,21 @@ ngx_init_cycle(ngx_cycle_t *old_cycle) break; } part = part->next; - shm = part->elts; + shm_zone = part->elts; i = 0; } - shm[i].shm.log = cycle->log; + if (shm_zone[i].shm.size == 0) { + ngx_log_error(NGX_LOG_EMERG, log, 0, + "zero size shared memory zone \"%V\"", + &shm_zone[i].name); + goto failed; + } + + shm_zone[i].shm.log = cycle->log; opart = &old_cycle->shared_memory.part; - oshm = opart->elts; + oshm_zone = opart->elts; for (n = 0; /* void */ ; n++) { @@ -378,31 +385,45 @@ ngx_init_cycle(ngx_cycle_t *old_cycle) break; } opart = opart->next; - oshm = opart->elts; + oshm_zone = opart->elts; n = 0; } - if (ngx_strcmp(shm[i].name.data, oshm[n].name.data) != 0) { + if (shm_zone[i].name.len != oshm_zone[n].name.len) { + continue; + } + + if (ngx_strncmp(shm_zone[i].name.data, oshm_zone[n].name.data, + shm_zone[i].name.len) + != 0) + { continue; } - if (shm[i].shm.size == oshm[n].shm.size) { - shm[i].shm.addr = oshm[n].shm.addr; - goto found; + if (shm_zone[i].shm.size == oshm_zone[n].shm.size) { + shm_zone[i].shm.addr = oshm_zone[n].shm.addr; + + if (shm_zone[i].init(&shm_zone[i], oshm_zone[n].data) + != NGX_OK) + { + goto failed; + } + + goto shm_zone_found; } - ngx_shm_free(&oshm[n].shm); + ngx_shm_free(&oshm_zone[n].shm); break; } - if (ngx_shm_alloc(&shm[i].shm) != NGX_OK) { + if (ngx_shm_alloc(&shm_zone[i].shm) != NGX_OK) { goto failed; } - shpool = (ngx_slab_pool_t *) shm[i].shm.addr; + shpool = (ngx_slab_pool_t *) shm_zone[i].shm.addr; - shpool->end = shm[i].shm.addr + shm[i].shm.size; + shpool->end = shm_zone[i].shm.addr + shm_zone[i].shm.size; shpool->min_shift = 3; #if (NGX_HAVE_ATOMIC_OPS) @@ -412,7 +433,7 @@ ngx_init_cycle(ngx_cycle_t *old_cycle) #else lock_file = ngx_palloc(cycle->pool, - cycle->lock_file.len + shm[i].name.len); + cycle->lock_file.len + shm_zone[i].name.len); if (lock_file == NULL) { goto failed; @@ -420,7 +441,7 @@ ngx_init_cycle(ngx_cycle_t *old_cycle) (void) ngx_cpystrn(ngx_cpymem(lock_file, cycle->lock_file.data, cycle->lock_file.len), - shm[i].name.data, shm[i].name.len + 1); + shm_zone[i].name.data, shm_zone[i].name.len + 1); #endif @@ -432,7 +453,11 @@ ngx_init_cycle(ngx_cycle_t *old_cycle) ngx_slab_init(shpool); - found: + if (shm_zone[i].init(&shm_zone[i], NULL) != NGX_OK) { + goto failed; + } + + shm_zone_found: continue; } @@ -569,7 +594,57 @@ ngx_init_cycle(ngx_cycle_t *old_cycle) /* close and delete stuff that lefts from an old cycle */ - /* close the unneeded listening sockets */ + /* free the unnecessary shared memory */ + + opart = &old_cycle->shared_memory.part; + oshm_zone = opart->elts; + + for (i = 0; /* void */ ; i++) { + + if (i >= opart->nelts) { + if (opart->next == NULL) { + goto old_shm_zone_done; + } + opart = opart->next; + oshm_zone = opart->elts; + i = 0; + } + + part = &cycle->shared_memory.part; + shm_zone = part->elts; + + for (n = 0; /* void */ ; n++) { + + if (n >= part->nelts) { + if (part->next == NULL) { + break; + } + part = part->next; + shm_zone = part->elts; + n = 0; + } + + if (oshm_zone[i].name.len == shm_zone[n].name.len + && ngx_strncmp(oshm_zone[i].name.data, + shm_zone[n].name.data, + oshm_zone[i].name.len) + == 0) + { + goto live_shm_zone; + } + } + + ngx_shm_free(&oshm_zone[i].shm); + + live_shm_zone: + + continue; + } + +old_shm_zone_done: + + + /* close the unnecessary listening sockets */ ls = old_cycle->listening.elts; for (i = 0; i < old_cycle->listening.nelts; i++) { @@ -585,7 +660,7 @@ ngx_init_cycle(ngx_cycle_t *old_cycle) } - /* close the unneeded open files */ + /* close the unnecessary open files */ part = &old_cycle->open_files.part; file = part->elts; @@ -988,6 +1063,71 @@ ngx_reopen_files(ngx_cycle_t *cycle, ngx } +ngx_shm_zone_t * +ngx_shared_memory_add(ngx_conf_t *cf, ngx_str_t *name, size_t size, void *tag) +{ + ngx_uint_t i; + ngx_shm_zone_t *shm_zone; + ngx_list_part_t *part; + + part = &cf->cycle->shared_memory.part; + shm_zone = part->elts; + + for (i = 0; /* void */ ; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + part = part->next; + shm_zone = part->elts; + i = 0; + } + + if (name->len != shm_zone[i].name.len) { + continue; + } + + if (ngx_strncmp(name->data, shm_zone[i].name.data, name->len) != 0) { + continue; + } + + if (size && size != shm_zone[i].shm.size) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "the size %uz of shared memory zone \"%V\" " + "conflicts with already declared size %uz", + size, &shm_zone[i].name, shm_zone[i].shm.size); + return NULL; + } + + if (tag != shm_zone[i].tag) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "the shared memory zone \"%V\" is " + "already declared for a different use", + &shm_zone[i].name); + return NULL; + } + + return &shm_zone[i]; + } + + shm_zone = ngx_list_push(&cf->cycle->shared_memory); + + if (shm_zone == NULL) { + return NULL; + } + + shm_zone->data = NULL; + shm_zone->shm.log = cf->cycle->log; + shm_zone->shm.size = size; + shm_zone->init = NULL; + shm_zone->name = *name; + shm_zone->tag = tag; + + return shm_zone; +} + + static void ngx_clean_old_cycles(ngx_event_t *ev) { diff --git a/src/core/ngx_cycle.h b/src/core/ngx_cycle.h --- a/src/core/ngx_cycle.h +++ b/src/core/ngx_cycle.h @@ -21,10 +21,17 @@ #define NGX_DEBUG_POINTS_ABORT 2 -typedef struct { +typedef struct ngx_shm_zone_s ngx_shm_zone_t; + +typedef ngx_int_t (*ngx_shm_zone_init_pt) (ngx_shm_zone_t *zone, void *data); + +struct ngx_shm_zone_s { + void *data; ngx_shm_t shm; + ngx_shm_zone_init_pt init; ngx_str_t name; -} ngx_shm_zone_t; + void *tag; +}; struct ngx_cycle_s { @@ -108,6 +115,8 @@ void ngx_delete_pidfile(ngx_cycle_t *cyc void ngx_reopen_files(ngx_cycle_t *cycle, ngx_uid_t user); ngx_pid_t ngx_exec_new_binary(ngx_cycle_t *cycle, char *const *argv); u_long ngx_get_cpu_affinity(ngx_uint_t n); +ngx_shm_zone_t *ngx_shared_memory_add(ngx_conf_t *cf, ngx_str_t *name, + size_t size, void *tag); extern volatile ngx_cycle_t *ngx_cycle; diff --git a/src/core/ngx_file.c b/src/core/ngx_file.c --- a/src/core/ngx_file.c +++ b/src/core/ngx_file.c @@ -418,3 +418,168 @@ ngx_create_pathes(ngx_cycle_t *cycle, ng return NGX_OK; } + + +ngx_int_t +ngx_walk_tree(ngx_tree_ctx_t *ctx, ngx_str_t *tree) +{ + void *data, *prev; + u_char *p, *name; + size_t len; + ngx_int_t rc; + ngx_err_t err; + ngx_str_t file, buf; + ngx_dir_t dir; + + buf.len = 0; + buf.data = NULL; + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, ctx->log, 0, + "walk tree \"%V\"", tree); + + if (ngx_open_dir(tree, &dir) == NGX_ERROR) { + ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno, + ngx_open_dir_n " \"%s\" failed", tree->data); + return NGX_ERROR; + } + + prev = ctx->data; + + if (ctx->size) { + data = ngx_alloc(ctx->size, ctx->log); + if (data == NULL) { + goto failed; + } + + if (ctx->init_handler(data, prev) == NGX_ABORT) { + goto failed; + } + + ctx->data = data; + } + + for ( ;; ) { + + ngx_set_errno(0); + + if (ngx_read_dir(&dir) == NGX_ERROR) { + err = ngx_errno; + + if (err == NGX_ENOMOREFILES) { + rc = NGX_OK; + + } else { + ngx_log_error(NGX_LOG_CRIT, ctx->log, err, + ngx_read_dir_n " \"%s\" failed", tree->data); + rc = NGX_ERROR; + } + + goto done; + } + + len = ngx_de_namelen(&dir); + name = ngx_de_name(&dir); + + ngx_log_debug2(NGX_LOG_DEBUG_CORE, ctx->log, 0, + "tree name %uz:\"%s\"", len, name); + + if (len == 1 && name[0] == '.') { + continue; + } + + if (len == 2 && name[0] == '.' && name[1] == '.') { + continue; + } + + file.len = tree->len + 1 + len; + + if (file.len + NGX_DIR_MASK_LEN > buf.len) { + + if (buf.len) { + ngx_free(buf.data); + } + + buf.len = tree->len + 1 + len + NGX_DIR_MASK_LEN; + + buf.data = ngx_alloc(buf.len + 1, ctx->log); + if (buf.data == NULL) { + goto failed; + } + } + + p = ngx_cpymem(buf.data, tree->data, tree->len); + *p++ = '/'; + ngx_memcpy(p, name, len + 1); + + file.data = buf.data; + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, ctx->log, 0, + "tree path \"%s\"", file.data); + + if (!dir.valid_info) { + if (ngx_de_info(file.data, &dir) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno, + ngx_de_info_n " \"%s\" failed", file.data); + continue; + } + } + + if (ngx_de_is_file(&dir)) { + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, ctx->log, 0, + "tree file \"%s\"", file.data); + + if (ctx->file_handler(ctx, &file) == NGX_ABORT) { + goto failed; + } + + } else if (ngx_de_is_dir(&dir)) { + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, ctx->log, 0, + "tree enter dir \"%s\"", file.data); + + if (ctx->pre_tree_handler(ctx, &file) == NGX_ABORT) { + goto failed; + } + + if (ngx_walk_tree(ctx, &file) == NGX_ABORT) { + goto failed; + } + + if (ctx->post_tree_handler(ctx, &file) == NGX_ABORT) { + goto failed; + } + + } else { + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, ctx->log, 0, + "tree special \"%s\"", file.data); + + if (ctx->spec_handler(ctx, &file) == NGX_ABORT) { + goto failed; + } + } + } + +failed: + + rc = NGX_ABORT; + +done: + + if (buf.len) { + ngx_free(buf.data); + } + + if (ctx->data) { + ngx_free(ctx->data); + ctx->data = prev; + } + + if (ngx_close_dir(&dir) == NGX_ERROR) { + ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno, + ngx_close_dir_n " \"%s\" failed", tree->data); + } + + return rc; +} diff --git a/src/core/ngx_file.h b/src/core/ngx_file.h --- a/src/core/ngx_file.h +++ b/src/core/ngx_file.h @@ -56,6 +56,24 @@ typedef struct { } ngx_temp_file_t; +typedef struct ngx_tree_ctx_s ngx_tree_ctx_t; + +typedef ngx_int_t (*ngx_tree_init_handler_pt) (ngx_tree_ctx_t *ctx, + ngx_tree_ctx_t *prev); +typedef ngx_int_t (*ngx_tree_handler_pt) (ngx_tree_ctx_t *ctx, ngx_str_t *name); + +struct ngx_tree_ctx_s { + ngx_tree_init_handler_pt init_handler; + ngx_tree_handler_pt file_handler; + ngx_tree_handler_pt pre_tree_handler; + ngx_tree_handler_pt post_tree_handler; + ngx_tree_handler_pt spec_handler; + void *data; + size_t size; + ngx_log_t *log; +}; + + ssize_t ngx_write_chain_to_temp_file(ngx_temp_file_t *tf, ngx_chain_t *chain); ngx_int_t ngx_create_temp_file(ngx_file_t *file, ngx_path_t *path, ngx_pool_t *pool, ngx_uint_t persistent,ngx_uint_t mode); @@ -64,6 +82,7 @@ ngx_int_t ngx_create_path(ngx_file_t *fi ngx_err_t ngx_create_full_path(u_char *dir, ngx_uint_t access); ngx_int_t ngx_add_path(ngx_conf_t *cf, ngx_path_t **slot); ngx_int_t ngx_create_pathes(ngx_cycle_t *cycle, ngx_uid_t user); +ngx_int_t ngx_walk_tree(ngx_tree_ctx_t *ctx, ngx_str_t *tree); void ngx_init_temp_number(void); ngx_atomic_uint_t ngx_next_temp_number(ngx_uint_t collision); diff --git a/src/core/ngx_rbtree.h b/src/core/ngx_rbtree.h --- a/src/core/ngx_rbtree.h +++ b/src/core/ngx_rbtree.h @@ -50,6 +50,11 @@ void ngx_rbtree_insert_timer_value(ngx_r ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel); +/* a sentinel must be black */ + +#define ngx_rbtree_sentinel_init(node) node->color = 0 + + static ngx_inline ngx_rbtree_node_t * ngx_rbtree_min(ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel) { diff --git a/src/core/ngx_slab.c b/src/core/ngx_slab.c --- a/src/core/ngx_slab.c +++ b/src/core/ngx_slab.c @@ -148,14 +148,31 @@ ngx_slab_init(ngx_slab_pool_t *pool) void * ngx_slab_alloc(ngx_slab_pool_t *pool, size_t size) { + void *p; + + ngx_shmtx_lock(&pool->mutex); + + p = ngx_slab_alloc_locked(pool, size); + + ngx_shmtx_unlock(&pool->mutex); + + return p; +} + + +void * +ngx_slab_alloc_locked(ngx_slab_pool_t *pool, size_t size) +{ size_t s; uintptr_t p, n, m, mask, *bitmap; ngx_uint_t i, slot, shift, map; ngx_slab_page_t *page, *prev, *slots; - ngx_shmtx_lock(&pool->mutex); + if (size >= ngx_slab_max_size) { - if (size >= ngx_slab_max_size) { + ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, ngx_cycle->log, 0, + "slab alloc: %uz", size); + page = ngx_slab_alloc_pages(pool, (size + ngx_pagesize - 1) >> ngx_pagesize_shift); if (page) { @@ -186,14 +203,9 @@ ngx_slab_alloc(ngx_slab_pool_t *pool, si slots = (ngx_slab_page_t *) ((u_char *) pool + sizeof(ngx_slab_pool_t)); page = slots[slot].next; -#if 0 - ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, ngx_cycle->log, 0, - "slab alloc: page %p next: %p", page, page->next); -#endif - if (page->next != page) { - if (size < ngx_slab_exact_size) { + if (shift < ngx_slab_exact_shift) { do { p = (page - pool->pages) << ngx_pagesize_shift; @@ -212,7 +224,9 @@ ngx_slab_alloc(ngx_slab_pool_t *pool, si } bitmap[n] |= m; - i <<= shift; + + i = ((n * sizeof(uintptr_t) * 8) << shift) + + (i << shift); if (bitmap[n] == NGX_SLAB_BUSY) { for (n = n + 1; n < map; n++) { @@ -243,7 +257,7 @@ ngx_slab_alloc(ngx_slab_pool_t *pool, si } while (page); - } else if (size == ngx_slab_exact_size) { + } else if (shift == ngx_slab_exact_shift) { do { if (page->slab != NGX_SLAB_BUSY) { @@ -277,7 +291,7 @@ ngx_slab_alloc(ngx_slab_pool_t *pool, si } while (page); - } else { /* size < ngx_pagesize */ + } else { /* shift > ngx_slab_exact_shift */ n = ngx_pagesize_shift - (page->slab & NGX_SLAB_SHIFT_MASK); n = 1 << n; @@ -324,7 +338,7 @@ ngx_slab_alloc(ngx_slab_pool_t *pool, si page = ngx_slab_alloc_pages(pool, 1); if (page) { - if (size < ngx_slab_exact_size) { + if (shift < ngx_slab_exact_shift) { p = (page - pool->pages) << ngx_pagesize_shift; bitmap = (uintptr_t *) (pool->start + p); @@ -354,7 +368,7 @@ ngx_slab_alloc(ngx_slab_pool_t *pool, si goto done; - } else if (size == ngx_slab_exact_size) { + } else if (shift == ngx_slab_exact_shift) { page->slab = 1; page->next = &slots[slot]; @@ -367,7 +381,7 @@ ngx_slab_alloc(ngx_slab_pool_t *pool, si goto done; - } else { /* size < ngx_pagesize */ + } else { /* shift > ngx_slab_exact_shift */ page->slab = ((uintptr_t) 1 << NGX_SLAB_MAP_SHIFT) | shift; page->next = &slots[slot]; @@ -386,8 +400,6 @@ ngx_slab_alloc(ngx_slab_pool_t *pool, si done: - ngx_shmtx_unlock(&pool->mutex); - ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, ngx_cycle->log, 0, "slab alloc: %p", p); return (void *) p; @@ -397,6 +409,17 @@ done: void ngx_slab_free(ngx_slab_pool_t *pool, void *p) { + ngx_shmtx_lock(&pool->mutex); + + ngx_slab_free_locked(pool, p); + + ngx_shmtx_unlock(&pool->mutex); +} + + +void +ngx_slab_free_locked(ngx_slab_pool_t *pool, void *p) +{ size_t size; uintptr_t slab, *bitmap; ngx_uint_t n, m, type, slot, shift, map; @@ -404,8 +427,6 @@ ngx_slab_free(ngx_slab_pool_t *pool, voi ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, ngx_cycle->log, 0, "slab free: %p", p); - ngx_shmtx_lock(&pool->mutex); - if ((u_char *) p < pool->start || (u_char *) p > pool->end) { ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "ngx_slab_free(): outside of pool"); @@ -584,8 +605,6 @@ done: ngx_slab_junk(p, size); - ngx_shmtx_unlock(&pool->mutex); - return; wrong_chunk: @@ -602,8 +621,6 @@ chunk_already_free: fail: - ngx_shmtx_unlock(&pool->mutex); - return; } @@ -658,6 +675,7 @@ ngx_slab_alloc_pages(ngx_slab_pool_t *po ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, NGX_ENOMEM, "ngx_slab_alloc(): failed"); + return NULL; } @@ -674,12 +692,16 @@ ngx_slab_free_pages(ngx_slab_pool_t *poo ngx_memzero(&page[1], pages * sizeof(ngx_slab_page_t)); } - prev = (ngx_slab_page_t *) (page->prev & ~NGX_SLAB_PAGE_MASK); - prev->next = page->next; + if (page->next) { + prev = (ngx_slab_page_t *) (page->prev & ~NGX_SLAB_PAGE_MASK); + prev->next = page->next; + page->next->prev = page->prev; + } + page->prev = (uintptr_t) &pool->free; page->next = pool->free.next; + + page->next->prev = (uintptr_t) page; + pool->free.next = page; - - page->prev = page->next->prev; - page->next->prev = (uintptr_t) page; } diff --git a/src/core/ngx_slab.h b/src/core/ngx_slab.h --- a/src/core/ngx_slab.h +++ b/src/core/ngx_slab.h @@ -39,7 +39,9 @@ typedef struct { void ngx_slab_init(ngx_slab_pool_t *pool); void *ngx_slab_alloc(ngx_slab_pool_t *pool, size_t size); +void *ngx_slab_alloc_locked(ngx_slab_pool_t *pool, size_t size); void ngx_slab_free(ngx_slab_pool_t *pool, void *p); +void ngx_slab_free_locked(ngx_slab_pool_t *pool, void *p); #endif /* _NGX_SLAB_H_INCLUDED_ */ diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c --- a/src/event/ngx_event_openssl.c +++ b/src/event/ngx_event_openssl.c @@ -22,6 +22,17 @@ static void ngx_ssl_read_handler(ngx_eve static void ngx_ssl_shutdown_handler(ngx_event_t *ev); static void ngx_ssl_connection_error(ngx_connection_t *c, int sslerr, ngx_err_t err, char *text); + +static ngx_int_t ngx_ssl_session_cache_init(ngx_shm_zone_t *shm_zone, + void *data); +static int ngx_ssl_new_session(ngx_ssl_conn_t *ssl_conn, + ngx_ssl_session_t *sess); +static ngx_ssl_session_t *ngx_ssl_get_cached_session(ngx_ssl_conn_t *ssl_conn, + u_char *id, int len, int *copy); +static void ngx_ssl_remove_session(SSL_CTX *ssl, ngx_ssl_session_t *sess); +static void ngx_ssl_expire_sessions(ngx_ssl_session_cache_t *cache, + ngx_slab_pool_t *shpool, ngx_uint_t n); + static void *ngx_openssl_create_conf(ngx_cycle_t *cycle); static char *ngx_openssl_init_conf(ngx_cycle_t *cycle, void *conf); static void ngx_openssl_exit(ngx_cycle_t *cycle); @@ -84,12 +95,18 @@ static long ngx_ssl_protocols[] = { }; -int ngx_connection_index; +int ngx_ssl_connection_index; +int ngx_ssl_server_conf_index; +int ngx_ssl_session_cache_index; ngx_int_t ngx_ssl_init(ngx_log_t *log) { +#if OPENSSL_VERSION_NUMBER >= 0x00907000 + OPENSSL_config(NULL); +#endif + SSL_library_init(); SSL_load_error_strings(); @@ -97,10 +114,26 @@ ngx_ssl_init(ngx_log_t *log) ENGINE_load_builtin_engines(); #endif - ngx_connection_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL); + ngx_ssl_connection_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL); + + if (ngx_ssl_connection_index == -1) { + ngx_ssl_error(NGX_LOG_ALERT, log, 0, "SSL_get_ex_new_index() failed"); + return NGX_ERROR; + } - if (ngx_connection_index == -1) { - ngx_ssl_error(NGX_LOG_ALERT, log, 0, "SSL_get_ex_new_index() failed"); + ngx_ssl_server_conf_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL, + NULL); + if (ngx_ssl_server_conf_index == -1) { + ngx_ssl_error(NGX_LOG_ALERT, log, 0, + "SSL_CTX_get_ex_new_index() failed"); + return NGX_ERROR; + } + + ngx_ssl_session_cache_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL, + NULL); + if (ngx_ssl_session_cache_index == -1) { + ngx_ssl_error(NGX_LOG_ALERT, log, 0, + "SSL_CTX_get_ex_new_index() failed"); return NGX_ERROR; } @@ -109,7 +142,7 @@ ngx_ssl_init(ngx_log_t *log) ngx_int_t -ngx_ssl_create(ngx_ssl_t *ssl, ngx_uint_t protocols) +ngx_ssl_create(ngx_ssl_t *ssl, ngx_uint_t protocols, void *data) { ssl->ctx = SSL_CTX_new(SSLv23_method()); @@ -118,6 +151,12 @@ ngx_ssl_create(ngx_ssl_t *ssl, ngx_uint_ return NGX_ERROR; } + if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_server_conf_index, data) == 0) { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "SSL_CTX_set_ex_data() failed"); + return NGX_ERROR; + } + /* client side options */ SSL_CTX_set_options(ssl->ctx, SSL_OP_MICROSOFT_SESS_ID_BUG); @@ -260,7 +299,7 @@ ngx_http_ssl_verify_callback(int ok, X50 name = X509_get_issuer_name(cert); issuer = name ? X509_NAME_oneline(name, NULL, 0) : "(none)"; - ngx_log_debug5(NGX_LOG_DEBUG_HTTP, c->log, 0, + ngx_log_debug5(NGX_LOG_DEBUG_EVENT, c->log, 0, "verify:%d, error:%d, depth:%d, " "subject:\"%s\",issuer: \"%s\"", ok, err, depth, subject, issuer); @@ -332,7 +371,7 @@ ngx_ssl_create_connection(ngx_ssl_t *ssl SSL_set_accept_state(sc->connection); } - if (SSL_set_ex_data(sc->connection, ngx_connection_index, c) == 0) { + if (SSL_set_ex_data(sc->connection, ngx_ssl_connection_index, c) == 0) { ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "SSL_set_ex_data() failed"); return NGX_ERROR; } @@ -1014,7 +1053,7 @@ ngx_ssl_shutdown_handler(ngx_event_t *ev c->timedout = 1; } - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ev->log, 0, "SSL shutdown handler"); + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0, "SSL shutdown handler"); if (ngx_ssl_shutdown(c) == NGX_AGAIN) { return; @@ -1100,6 +1139,433 @@ ngx_ssl_error(ngx_uint_t level, ngx_log_ } +ngx_int_t +ngx_ssl_session_cache(ngx_ssl_t *ssl, ngx_str_t *sess_ctx, + ssize_t builtin_session_cache, ngx_shm_zone_t *shm_zone, time_t timeout) +{ + long cache_mode; + + cache_mode = SSL_SESS_CACHE_SERVER; + + if (shm_zone && builtin_session_cache == NGX_SSL_NO_BUILTIN_SCACHE) { + cache_mode |= SSL_SESS_CACHE_NO_INTERNAL; + } + + SSL_CTX_set_session_cache_mode(ssl->ctx, cache_mode); + + SSL_CTX_set_session_id_context(ssl->ctx, sess_ctx->data, sess_ctx->len); + + if (builtin_session_cache != NGX_SSL_NO_BUILTIN_SCACHE) { + + if (builtin_session_cache != NGX_SSL_DFLT_BUILTIN_SCACHE) { + SSL_CTX_sess_set_cache_size(ssl->ctx, builtin_session_cache); + } + + SSL_CTX_set_timeout(ssl->ctx, timeout); + } + + if (shm_zone) { + shm_zone->init = ngx_ssl_session_cache_init; + + SSL_CTX_sess_set_new_cb(ssl->ctx, ngx_ssl_new_session); + SSL_CTX_sess_set_get_cb(ssl->ctx, ngx_ssl_get_cached_session); + SSL_CTX_sess_set_remove_cb(ssl->ctx, ngx_ssl_remove_session); + + if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_session_cache_index, shm_zone) + == 0) + { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "SSL_CTX_set_ex_data() failed"); + return NGX_ERROR; + } + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_ssl_session_cache_init(ngx_shm_zone_t *shm_zone, void *data) +{ + ngx_slab_pool_t *shpool; + ngx_rbtree_node_t *sentinel; + ngx_ssl_session_cache_t *cache; + + if (data) { + shm_zone->data = data; + return NGX_OK; + } + + shpool = (ngx_slab_pool_t *) shm_zone->shm.addr; + + cache = ngx_slab_alloc(shpool, sizeof(ngx_ssl_session_cache_t)); + if (cache == NULL) { + return NGX_ERROR; + } + + cache->session_cache_head.prev = NULL; + cache->session_cache_head.next = &cache->session_cache_tail; + + cache->session_cache_tail.prev = &cache->session_cache_head; + cache->session_cache_tail.next = NULL; + + cache->session_rbtree = ngx_slab_alloc(shpool, sizeof(ngx_rbtree_t)); + if (cache->session_rbtree == NULL) { + return NGX_ERROR; + } + + sentinel = ngx_slab_alloc(shpool, sizeof(ngx_rbtree_node_t)); + if (sentinel == NULL) { + return NGX_ERROR; + } + + ngx_rbtree_sentinel_init(sentinel); + + cache->session_rbtree->root = sentinel; + cache->session_rbtree->sentinel = sentinel; + cache->session_rbtree->insert = ngx_rbtree_insert_value; + + shm_zone->data = cache; + + return NGX_OK; +} + + +/* + * OpenSSL's i2d_SSL_SESSION() and d2i_SSL_SESSION are slow, + * so they are outside the code locked by shared pool mutex + */ + +static int +ngx_ssl_new_session(ngx_ssl_conn_t *ssl_conn, ngx_ssl_session_t *sess) +{ + int len; + u_char *p, *id; + uint32_t hash; + SSL_CTX *ssl_ctx; + ngx_time_t *tp; + ngx_shm_zone_t *shm_zone; + ngx_connection_t *c; + ngx_slab_pool_t *shpool; + ngx_ssl_sess_id_t *sess_id; + ngx_ssl_cached_sess_t *cached_sess; + ngx_ssl_session_cache_t *cache; + u_char buf[NGX_SSL_MAX_SESSION_SIZE]; + + len = i2d_SSL_SESSION(sess, NULL); + + /* do not cache too big session */ + + if (len > (int) NGX_SSL_MAX_SESSION_SIZE) { + return 0; + } + + p = buf; + i2d_SSL_SESSION(sess, &p); + + c = ngx_ssl_get_connection(ssl_conn); + + ssl_ctx = SSL_get_SSL_CTX(ssl_conn); + shm_zone = SSL_CTX_get_ex_data(ssl_ctx, ngx_ssl_session_cache_index); + + cache = shm_zone->data; + shpool = (ngx_slab_pool_t *) shm_zone->shm.addr; + + ngx_shmtx_lock(&shpool->mutex); + + /* drop one or two expired sessions */ + ngx_ssl_expire_sessions(cache, shpool, 1); + + cached_sess = ngx_slab_alloc_locked(shpool, + offsetof(ngx_ssl_cached_sess_t, asn1) + len); + + if (cached_sess == NULL) { + + /* drop the oldest non-expired session and try once more */ + + ngx_ssl_expire_sessions(cache, shpool, 0); + + cached_sess = ngx_slab_alloc_locked(shpool, + offsetof(ngx_ssl_cached_sess_t, asn1) + len); + + if (cached_sess == NULL) { + id = NULL; + goto failed; + } + } + + id = ngx_slab_alloc_locked(shpool, sess->session_id_length); + if (id == NULL) { + goto failed; + } + + sess_id = ngx_slab_alloc_locked(shpool, sizeof(ngx_ssl_sess_id_t)); + if (sess_id == NULL) { + goto failed; + } + + ngx_memcpy(&cached_sess->asn1[0], buf, len); + + ngx_memcpy(id, sess->session_id, sess->session_id_length); + + hash = ngx_crc32_short(sess->session_id, sess->session_id_length); + + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, + "http ssl new session: %08XD:%d:%d", + hash, sess->session_id_length, len); + + sess_id->node.key = hash; + sess_id->node.data = (u_char) sess->session_id_length; + sess_id->id = id; + sess_id->len = len; + sess_id->session = cached_sess; + + tp = ngx_timeofday(); + + cached_sess->expire = tp->sec + SSL_CTX_get_timeout(ssl_ctx); + cached_sess->sess_id = sess_id; + + cached_sess->next = cache->session_cache_head.next; + cached_sess->next->prev = cached_sess; + cached_sess->prev = &cache->session_cache_head; + cache->session_cache_head.next = cached_sess; + + ngx_rbtree_insert(cache->session_rbtree, &sess_id->node); + + ngx_shmtx_unlock(&shpool->mutex); + + return 0; + +failed: + + if (cached_sess) { + ngx_slab_free_locked(shpool, cached_sess); + } + + if (id) { + ngx_slab_free_locked(shpool, id); + } + + ngx_shmtx_unlock(&shpool->mutex); + + ngx_log_error(NGX_LOG_ALERT, c->log, 0, + "could not add new SSL session to the session cache"); + + return 0; +} + + +static ngx_ssl_session_t * +ngx_ssl_get_cached_session(ngx_ssl_conn_t *ssl_conn, u_char *id, int len, + int *copy) +{ +#if OPENSSL_VERSION_NUMBER >= 0x0090707fL + const +#endif + u_char *p; + uint32_t hash; + ngx_time_t *tp; + ngx_shm_zone_t *shm_zone; + ngx_slab_pool_t *shpool; + ngx_connection_t *c; + ngx_rbtree_node_t *node, *sentinel; + ngx_ssl_session_t *sess; + ngx_ssl_sess_id_t *sess_id; + ngx_ssl_cached_sess_t *cached_sess; + ngx_ssl_session_cache_t *cache; + u_char buf[NGX_SSL_MAX_SESSION_SIZE]; + + c = ngx_ssl_get_connection(ssl_conn); + + hash = ngx_crc32_short(id, len); + *copy = 0; + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, + "http ssl get session: %08XD:%d", hash, len); + + shm_zone = SSL_CTX_get_ex_data(SSL_get_SSL_CTX(ssl_conn), + ngx_ssl_session_cache_index); + + cache = shm_zone->data; + + if (cache->session_rbtree == NULL) { + return NULL; + } + + sess = NULL; + + shpool = (ngx_slab_pool_t *) shm_zone->shm.addr; + + ngx_shmtx_lock(&shpool->mutex); + + node = cache->session_rbtree->root; + sentinel = cache->session_rbtree->sentinel; + + while (node != sentinel) { + + if (hash < node->key) { + node = node->left; + continue; + } + + if (hash > node->key) { + node = node->right; + continue; + } + + if (hash == node->key && (u_char) len == node->data) { + sess_id = (ngx_ssl_sess_id_t *) node; + + if (ngx_strncmp(id, sess_id->id, len) == 0) { + + cached_sess = sess_id->session; + + tp = ngx_timeofday(); + + if (cached_sess->expire > tp->sec) { + ngx_memcpy(buf, &cached_sess->asn1[0], sess_id->len); + + ngx_shmtx_unlock(&shpool->mutex); + + p = buf; + sess = d2i_SSL_SESSION(NULL, &p, sess_id->len); + + return sess; + } + + cached_sess->next->prev = cached_sess->prev; + cached_sess->prev->next = cached_sess->next; + + ngx_rbtree_delete(cache->session_rbtree, node); + + ngx_slab_free_locked(shpool, cached_sess); + ngx_slab_free_locked(shpool, sess_id->id); + ngx_slab_free_locked(shpool, sess_id); + + sess = NULL; + + break; + } + } + + node = node->right; + } + + ngx_shmtx_unlock(&shpool->mutex); + + return sess; +} + + +static void +ngx_ssl_remove_session(SSL_CTX *ssl, ngx_ssl_session_t *sess) +{ + u_char *id, len; + uint32_t hash; + ngx_shm_zone_t *shm_zone; + ngx_slab_pool_t *shpool; + ngx_rbtree_node_t *node, *sentinel; + ngx_ssl_sess_id_t *sess_id; + ngx_ssl_cached_sess_t *cached_sess; + ngx_ssl_session_cache_t *cache; + + shm_zone = SSL_CTX_get_ex_data(ssl, ngx_ssl_session_cache_index); + + cache = shm_zone->data; + + id = sess->session_id; + len = (u_char) sess->session_id_length; + + hash = ngx_crc32_short(id, (size_t) len); + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ngx_cycle->log, 0, + "http ssl remove session: %08XD:%d", hash, len); + + shpool = (ngx_slab_pool_t *) shm_zone->shm.addr; + + ngx_shmtx_lock(&shpool->mutex); + + node = cache->session_rbtree->root; + sentinel = cache->session_rbtree->sentinel; + + while (node != sentinel) { + + if (hash < node->key) { + node = node->left; + continue; + } + + if (hash > node->key) { + node = node->right; + continue; + } + + if (hash == node->key && len == node->data) { + sess_id = (ngx_ssl_sess_id_t *) node; + + if (ngx_strncmp(id, sess_id->id, (size_t) len) == 0) { + + cached_sess = sess_id->session; + + cached_sess->next->prev = cached_sess->prev; + cached_sess->prev->next = cached_sess->next; + + ngx_rbtree_delete(cache->session_rbtree, node); + + ngx_slab_free_locked(shpool, cached_sess); + ngx_slab_free_locked(shpool, sess_id->id); + ngx_slab_free_locked(shpool, sess_id); + + break; + } + } + + node = node->right; + } + + ngx_shmtx_unlock(&shpool->mutex); +} + + +static void +ngx_ssl_expire_sessions(ngx_ssl_session_cache_t *cache, + ngx_slab_pool_t *shpool, ngx_uint_t n) +{ + ngx_time_t *tp; + ngx_ssl_sess_id_t *sess_id; + ngx_ssl_cached_sess_t *sess; + + tp = ngx_timeofday(); + + while (n < 3) { + + sess = cache->session_cache_tail.prev; + + if (sess == &cache->session_cache_head) { + return; + } + + if (n++ != 0 && sess->expire > tp->sec) { + break; + } + + sess->next->prev = sess->prev; + sess->prev->next = sess->next; + + sess_id = sess->sess_id; + + ngx_rbtree_delete(cache->session_rbtree, &sess_id->node); + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ngx_cycle->log, 0, + "expire session: %08Xi", sess_id->node.key); + + ngx_slab_free_locked(shpool, sess); + ngx_slab_free_locked(shpool, sess_id->id); + ngx_slab_free_locked(shpool, sess_id); + } +} + + void ngx_ssl_cleanup_ctx(void *data) { 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 @@ -15,6 +15,7 @@ #include #if OPENSSL_VERSION_NUMBER >= 0x00907000 +#include #include #define NGX_SSL_ENGINE 1 #endif @@ -50,6 +51,41 @@ typedef struct { } ngx_ssl_connection_t; +#define NGX_SSL_DFLT_BUILTIN_SCACHE -2 +#define NGX_SSL_NO_BUILTIN_SCACHE -3 + + +typedef struct ngx_ssl_cached_sess_s ngx_ssl_cached_sess_t; + + +#define NGX_SSL_MAX_SESSION_SIZE (4096 - offsetof(ngx_ssl_cached_sess_t, asn1)) + + +typedef struct { + ngx_rbtree_node_t node; + u_char *id; + size_t len; + ngx_ssl_cached_sess_t *session; +} ngx_ssl_sess_id_t; + + +struct ngx_ssl_cached_sess_s { + ngx_ssl_cached_sess_t *prev; + ngx_ssl_cached_sess_t *next; + time_t expire; + ngx_ssl_sess_id_t *sess_id; + u_char asn1[1]; +}; + + +typedef struct { + ngx_rbtree_t *session_rbtree; + ngx_ssl_cached_sess_t session_cache_head; + ngx_ssl_cached_sess_t session_cache_tail; +} ngx_ssl_session_cache_t; + + + #define NGX_SSL_SSLv2 2 #define NGX_SSL_SSLv3 4 #define NGX_SSL_TLSv1 8 @@ -62,19 +98,24 @@ typedef struct { ngx_int_t ngx_ssl_init(ngx_log_t *log); -ngx_int_t ngx_ssl_create(ngx_ssl_t *ssl, ngx_uint_t protocols); +ngx_int_t ngx_ssl_create(ngx_ssl_t *ssl, ngx_uint_t protocols, void *data); ngx_int_t ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert, ngx_str_t *key); ngx_int_t ngx_ssl_client_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert, ngx_int_t depth); ngx_int_t ngx_ssl_generate_rsa512_key(ngx_ssl_t *ssl); +ngx_int_t ngx_ssl_session_cache(ngx_ssl_t *ssl, ngx_str_t *sess_ctx, + ssize_t builtin_session_cache, ngx_shm_zone_t *shm_zone, time_t timeout); ngx_int_t ngx_ssl_create_connection(ngx_ssl_t *ssl, ngx_connection_t *c, ngx_uint_t flags); ngx_int_t ngx_ssl_set_session(ngx_connection_t *c, ngx_ssl_session_t *session); #define ngx_ssl_get_session(c) SSL_get1_session(c->ssl->connection) #define ngx_ssl_free_session SSL_SESSION_free -#define ngx_ssl_get_connection(sc) SSL_get_ex_data(sc, ngx_connection_index) +#define ngx_ssl_get_connection(ssl_conn) \ + SSL_get_ex_data(ssl_conn, ngx_ssl_connection_index) +#define ngx_ssl_get_server_conf(ssl_ctx) \ + SSL_CTX_get_ex_data(ssl_ctx, ngx_ssl_server_conf_index) ngx_int_t ngx_ssl_get_protocol(ngx_connection_t *c, ngx_pool_t *pool, @@ -89,8 +130,6 @@ ngx_int_t ngx_ssl_get_serial_number(ngx_ ngx_str_t *s); - - ngx_int_t ngx_ssl_handshake(ngx_connection_t *c); ssize_t ngx_ssl_recv(ngx_connection_t *c, u_char *buf, size_t size); ssize_t ngx_ssl_write(ngx_connection_t *c, u_char *data, size_t size); @@ -103,7 +142,9 @@ void ngx_cdecl ngx_ssl_error(ngx_uint_t void ngx_ssl_cleanup_ctx(void *data); -extern int ngx_connection_index; +extern int ngx_ssl_connection_index; +extern int ngx_ssl_server_conf_index; +extern int ngx_ssl_session_cache_index; #endif /* _NGX_EVENT_OPENSSL_H_INCLUDED_ */ diff --git a/src/http/modules/ngx_http_dav_module.c b/src/http/modules/ngx_http_dav_module.c --- a/src/http/modules/ngx_http_dav_module.c +++ b/src/http/modules/ngx_http_dav_module.c @@ -19,6 +19,11 @@ typedef struct { static ngx_int_t ngx_http_dav_handler(ngx_http_request_t *r); +static ngx_int_t ngx_http_dav_no_init(ngx_tree_ctx_t *ctx, + ngx_tree_ctx_t *prev); +static ngx_int_t ngx_http_dav_noop(ngx_tree_ctx_t *ctx, ngx_str_t *path); +static ngx_int_t ngx_http_dav_delete_dir(ngx_tree_ctx_t *ctx, ngx_str_t *path); +static ngx_int_t ngx_http_dav_delete_file(ngx_tree_ctx_t *ctx, ngx_str_t *path); static void ngx_http_dav_put_handler(ngx_http_request_t *r); static ngx_int_t ngx_http_dav_error(ngx_http_request_t *, ngx_err_t err, ngx_int_t not_found, char *failed, u_char *path); @@ -105,6 +110,7 @@ ngx_http_dav_handler(ngx_http_request_t size_t root; ngx_int_t rc; ngx_str_t path; + ngx_tree_ctx_t tree; ngx_file_info_t fi; ngx_table_elt_t *depth; ngx_http_dav_loc_conf_t *dlcf; @@ -176,8 +182,23 @@ ngx_http_dav_handler(ngx_http_request_t return NGX_HTTP_BAD_REQUEST; } - if (ngx_delete_dir(path.data) != NGX_FILE_ERROR) { - return NGX_HTTP_NO_CONTENT; + path.len -= 2; + + tree.init_handler = ngx_http_dav_no_init; + tree.file_handler = ngx_http_dav_delete_file; + tree.pre_tree_handler = ngx_http_dav_noop; + tree.post_tree_handler = ngx_http_dav_delete_dir; + tree.spec_handler = ngx_http_dav_delete_file; + tree.data = NULL; + tree.size = 0; + tree.log = r->connection->log; + + if (ngx_walk_tree(&tree, &path) == NGX_OK) { + + if (ngx_delete_dir(path.data) != NGX_FILE_ERROR) { + return NGX_HTTP_NO_CONTENT; + } + } failed = ngx_delete_dir_n; @@ -248,6 +269,44 @@ ngx_http_dav_handler(ngx_http_request_t } +static ngx_int_t +ngx_http_dav_no_init(ngx_tree_ctx_t *ctx, ngx_tree_ctx_t *prev) +{ + return NGX_OK; +} + + +static ngx_int_t +ngx_http_dav_noop(ngx_tree_ctx_t *ctx, ngx_str_t *path) +{ + return NGX_OK; +} + + +static ngx_int_t +ngx_http_dav_delete_dir(ngx_tree_ctx_t *ctx, ngx_str_t *path) +{ + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0, + "http delete dir: \"%s\"", path->data); + + ngx_delete_dir(path->data); + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_dav_delete_file(ngx_tree_ctx_t *ctx, ngx_str_t *path) +{ + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0, + "http delete file: \"%s\"", path->data); + + ngx_delete_file(path->data); + + return NGX_OK; +} + + static void ngx_http_dav_put_handler(ngx_http_request_t *r) { diff --git a/src/http/modules/ngx_http_index_module.c b/src/http/modules/ngx_http_index_module.c --- a/src/http/modules/ngx_http_index_module.c +++ b/src/http/modules/ngx_http_index_module.c @@ -140,6 +140,10 @@ ngx_http_index_handler(ngx_http_request_ return NGX_DECLINED; } + if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD|NGX_HTTP_POST))) { + return NGX_DECLINED; + } + /* TODO: Win32 */ if (r->zero_in_uri) { return NGX_DECLINED; diff --git a/src/http/modules/ngx_http_limit_zone_module.c b/src/http/modules/ngx_http_limit_zone_module.c new file mode 100644 --- /dev/null +++ b/src/http/modules/ngx_http_limit_zone_module.c @@ -0,0 +1,444 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include +#include +#include + + +typedef struct { + u_char color; + u_char len; + u_short conn; + u_char data[1]; +} ngx_http_limit_zone_node_t; + + +typedef struct { + ngx_shm_zone_t *shm_zone; + ngx_rbtree_node_t *node; +} ngx_http_limit_zone_cleanup_t; + + +typedef struct { + ngx_rbtree_t *rbtree; + ngx_int_t index; + ngx_str_t var; +} ngx_http_limit_zone_ctx_t; + + +typedef struct { + ngx_shm_zone_t *shm_zone; + ngx_uint_t conn; +} ngx_http_limit_zone_conf_t; + + +static void ngx_http_limit_zone_cleanup(void *data); + +static void *ngx_http_limit_zone_create_conf(ngx_conf_t *cf); +static char *ngx_http_limit_zone_merge_conf(ngx_conf_t *cf, void *parent, + void *child); +static char *ngx_http_limit_zone(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static char *ngx_http_limit_conn(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static ngx_int_t ngx_http_limit_zone_init(ngx_conf_t *cf); + + +static ngx_command_t ngx_http_limit_zone_commands[] = { + + { ngx_string("limit_zone"), + NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE3, + ngx_http_limit_zone, + 0, + 0, + NULL }, + + { ngx_string("limit_conn"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2, + ngx_http_limit_conn, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL }, + + ngx_null_command +}; + + +static ngx_http_module_t ngx_http_limit_zone_module_ctx = { + NULL, /* preconfiguration */ + ngx_http_limit_zone_init, /* postconfiguration */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + NULL, /* create server configuration */ + NULL, /* merge server configuration */ + + ngx_http_limit_zone_create_conf, /* create location configration */ + ngx_http_limit_zone_merge_conf /* merge location configration */ +}; + + +ngx_module_t ngx_http_limit_zone_module = { + NGX_MODULE_V1, + &ngx_http_limit_zone_module_ctx, /* module context */ + ngx_http_limit_zone_commands, /* module directives */ + NGX_HTTP_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_int_t +ngx_http_limit_zone_handler(ngx_http_request_t *r) +{ + size_t len, n; + uint32_t hash; + ngx_slab_pool_t *shpool; + ngx_rbtree_node_t *node, *sentinel; + ngx_pool_cleanup_t *cln; + ngx_http_variable_value_t *vv; + ngx_http_limit_zone_ctx_t *ctx; + ngx_http_limit_zone_node_t *lz; + ngx_http_limit_zone_conf_t *lzcf; + ngx_http_limit_zone_cleanup_t *lzcln; + + if (r->main->limit_zone_set) { + return NGX_DECLINED; + } + + lzcf = ngx_http_get_module_loc_conf(r, ngx_http_limit_zone_module); + + if (lzcf->shm_zone == NULL) { + return NGX_DECLINED; + } + + ctx = lzcf->shm_zone->data; + + vv = ngx_http_get_indexed_variable(r, ctx->index); + + if (vv == NULL || vv->not_found) { + return NGX_DECLINED; + } + + r->limit_zone_set = 1; + + len = vv->len; + + hash = ngx_crc32_short(vv->data, len); + + cln = ngx_pool_cleanup_add(r->pool, sizeof(ngx_http_limit_zone_cleanup_t)); + if (cln == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + shpool = (ngx_slab_pool_t *) lzcf->shm_zone->shm.addr; + + ngx_shmtx_lock(&shpool->mutex); + + node = ctx->rbtree->root; + sentinel = ctx->rbtree->sentinel; + + while (node != sentinel) { + + if (hash < node->key) { + node = node->left; + continue; + } + + if (hash > node->key) { + node = node->right; + continue; + } + + if (hash == node->key ){ + lz = (ngx_http_limit_zone_node_t *) &node->color; + + if (len == (size_t) lz->len + && ngx_strncmp(lz->data, vv->data, len) == 0) + { + if ((ngx_uint_t) lz->conn < lzcf->conn) { + lz->conn++; + goto done; + } + + ngx_shmtx_unlock(&shpool->mutex); + + return NGX_HTTP_SERVICE_UNAVAILABLE; + } + } + } + + n = offsetof(ngx_rbtree_node_t, color) + + offsetof(ngx_http_limit_zone_node_t, data) + + len; + + node = ngx_slab_alloc_locked(shpool, n); + if (node == NULL) { + ngx_shmtx_unlock(&shpool->mutex); + return NGX_HTTP_SERVICE_UNAVAILABLE; + } + + lz = (ngx_http_limit_zone_node_t *) &node->color; + + node->key = hash; + lz->len = (u_char) len; + lz->conn = 1; + ngx_memcpy(lz->data, vv->data, len); + + ngx_rbtree_insert(ctx->rbtree, node); + +done: + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "limit zone: %08XD %d", node->key, lz->conn); + + ngx_shmtx_unlock(&shpool->mutex); + + cln->handler = ngx_http_limit_zone_cleanup; + lzcln = cln->data; + + lzcln->shm_zone = lzcf->shm_zone; + lzcln->node = node; + + return NGX_DECLINED; +} + + +static void +ngx_http_limit_zone_cleanup(void *data) +{ + ngx_http_limit_zone_cleanup_t *lzcln = data; + + ngx_slab_pool_t *shpool; + ngx_rbtree_node_t *node; + ngx_http_limit_zone_ctx_t *ctx; + ngx_http_limit_zone_node_t *lz; + + ctx = lzcln->shm_zone->data; + shpool = (ngx_slab_pool_t *) lzcln->shm_zone->shm.addr; + node = lzcln->node; + lz = (ngx_http_limit_zone_node_t *) &node->color; + + ngx_shmtx_lock(&shpool->mutex); + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, lzcln->shm_zone->shm.log, 0, + "limit zone cleanup: %08XD %d", node->key, lz->conn); + + lz->conn--; + + if (lz->conn == 0) { + ngx_rbtree_delete(ctx->rbtree, node); + ngx_slab_free_locked(shpool, node); + } + + ngx_shmtx_unlock(&shpool->mutex); +} + + +static ngx_int_t +ngx_http_limit_zone_init_zone(ngx_shm_zone_t *shm_zone, void *data) +{ + ngx_http_limit_zone_ctx_t *octx = data; + + ngx_slab_pool_t *shpool; + ngx_rbtree_node_t *sentinel; + ngx_http_limit_zone_ctx_t *ctx; + + ctx = shm_zone->data; + + if (octx) { + if (ngx_strcmp(ctx->var.data, octx->var.data) != 0) { + ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0, + "limit_zone \"%V\" use the \"%V\" variable " + "while previously it used the \"%V\" variable", + &shm_zone->name, &ctx->var, &octx->var); + return NGX_ERROR; + } + + ctx->rbtree = octx->rbtree; + + return NGX_OK; + } + + shpool = (ngx_slab_pool_t *) shm_zone->shm.addr; + + ctx->rbtree = ngx_slab_alloc(shpool, sizeof(ngx_rbtree_t)); + if (ctx->rbtree == NULL) { + return NGX_ERROR; + } + + sentinel = ngx_slab_alloc(shpool, sizeof(ngx_rbtree_node_t)); + if (sentinel == NULL) { + return NGX_ERROR; + } + + ngx_rbtree_sentinel_init(sentinel); + + ctx->rbtree->root = sentinel; + ctx->rbtree->sentinel = sentinel; + ctx->rbtree->insert = ngx_rbtree_insert_value; + + return NGX_OK; +} + + +static void * +ngx_http_limit_zone_create_conf(ngx_conf_t *cf) +{ + ngx_http_limit_zone_conf_t *conf; + + conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_limit_zone_conf_t)); + if (conf == NULL) { + return NGX_CONF_ERROR; + } + + /* + * set by ngx_pcalloc(): + * + * conf->shm_zone = NULL; + * conf->conn = 0; + */ + + return conf; +} + + +static char * +ngx_http_limit_zone_merge_conf(ngx_conf_t *cf, void *parent, void *child) +{ + ngx_http_limit_zone_conf_t *prev = parent; + ngx_http_limit_zone_conf_t *conf = child; + + if (conf->shm_zone == NULL) { + *conf = *prev; + } + + return NGX_CONF_OK; +} + + +static char * +ngx_http_limit_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ssize_t n; + ngx_str_t *value; + ngx_shm_zone_t *shm_zone; + ngx_http_limit_zone_ctx_t *ctx; + + value = cf->args->elts; + + if (value[2].data[0] != '$') { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid variable name \"%V\"", &value[2]); + return NGX_CONF_ERROR; + } + + value[2].len--; + value[2].data++; + + ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_limit_zone_ctx_t)); + if (ctx == NULL) { + return NGX_CONF_ERROR; + } + + ctx->index = ngx_http_get_variable_index(cf, &value[2]); + if (ctx->index == NGX_ERROR) { + return NGX_CONF_ERROR; + } + + ctx->var = value[2]; + + n = ngx_parse_size(&value[3]); + + if (n == NGX_ERROR) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid size of limit_zone \"%V\"", &value[3]); + return NGX_CONF_ERROR; + } + + if (n < (ngx_int_t) (8 * ngx_pagesize)) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "limit_zone \"%V\" is too small", &value[1]); + return NGX_CONF_ERROR; + } + + + shm_zone = ngx_shared_memory_add(cf, &value[1], n, + &ngx_http_limit_zone_module); + if (shm_zone == NULL) { + return NGX_CONF_ERROR; + } + + if (shm_zone->data) { + ctx = shm_zone->data; + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "limit_zone \"%V\" is already bound to variable \"%V\"", + &value[1], &ctx->var); + return NGX_CONF_ERROR; + } + + shm_zone->init = ngx_http_limit_zone_init_zone; + shm_zone->data = ctx; + + return NGX_CONF_OK; +} + + +static char * +ngx_http_limit_conn(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_limit_zone_conf_t *lzcf = conf; + + ngx_int_t n; + ngx_str_t *value; + + value = cf->args->elts; + + lzcf->shm_zone = ngx_shared_memory_add(cf, &value[1], 0, + &ngx_http_limit_zone_module); + if (lzcf->shm_zone == NULL) { + return NGX_CONF_ERROR; + } + + n = ngx_atoi(value[2].data, value[2].len); + if (n <= 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid number of connections \"%V\"", &value[2]); + return NGX_CONF_ERROR; + } + + lzcf->conn = n; + + return NGX_CONF_OK; +} + + +static ngx_int_t +ngx_http_limit_zone_init(ngx_conf_t *cf) +{ + ngx_http_handler_pt *h; + ngx_http_core_main_conf_t *cmcf; + + cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); + + h = ngx_array_push(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers); + if (h == NULL) { + return NGX_ERROR; + } + + *h = ngx_http_limit_zone_handler; + + return NGX_OK; +} diff --git a/src/http/modules/ngx_http_proxy_module.c b/src/http/modules/ngx_http_proxy_module.c --- a/src/http/modules/ngx_http_proxy_module.c +++ b/src/http/modules/ngx_http_proxy_module.c @@ -2130,7 +2130,7 @@ ngx_http_proxy_pass(ngx_conf_t *cf, ngx_ plcf->upstream.ssl->log = cf->log; if (ngx_ssl_create(plcf->upstream.ssl, - NGX_SSL_SSLv2|NGX_SSL_SSLv3|NGX_SSL_TLSv1) + NGX_SSL_SSLv2|NGX_SSL_SSLv3|NGX_SSL_TLSv1, NULL) != NGX_OK) { return NGX_CONF_ERROR; diff --git a/src/http/modules/ngx_http_range_filter_module.c b/src/http/modules/ngx_http_range_filter_module.c --- a/src/http/modules/ngx_http_range_filter_module.c +++ b/src/http/modules/ngx_http_range_filter_module.c @@ -45,7 +45,16 @@ typedef struct { - ngx_str_t boundary_header; + off_t start; + off_t end; + ngx_str_t content_range; +} ngx_http_range_t; + + +typedef struct { + off_t offset; + ngx_str_t boundary_header; + ngx_array_t ranges; } ngx_http_range_filter_ctx_t; @@ -159,8 +168,13 @@ ngx_http_range_header_filter(ngx_http_re return ngx_http_next_header_filter(r); } - if (ngx_array_init(&r->headers_out.ranges, r->pool, 2, - sizeof(ngx_http_range_t)) == NGX_ERROR) + ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_range_filter_ctx_t)); + if (ctx == NULL) { + return NGX_ERROR; + } + + if (ngx_array_init(&ctx->ranges, r->pool, 1, sizeof(ngx_http_range_t)) + == NGX_ERROR) { return NGX_ERROR; } @@ -201,7 +215,7 @@ ngx_http_range_header_filter(ngx_http_re while (*p == ' ') { p++; } if (*p == ',' || *p == '\0') { - range = ngx_array_push(&r->headers_out.ranges); + range = ngx_array_push(&ctx->ranges); if (range == NULL) { return NGX_ERROR; } @@ -247,7 +261,7 @@ ngx_http_range_header_filter(ngx_http_re break; } - range = ngx_array_push(&r->headers_out.ranges); + range = ngx_array_push(&ctx->ranges); if (range == NULL) { return NGX_ERROR; } @@ -275,7 +289,6 @@ ngx_http_range_header_filter(ngx_http_re /* rc == NGX_HTTP_RANGE_NOT_SATISFIABLE */ r->headers_out.status = rc; - r->headers_out.ranges.nelts = 0; content_range = ngx_list_push(&r->headers_out.headers); if (content_range == NULL) { @@ -304,9 +317,11 @@ ngx_http_range_header_filter(ngx_http_re return rc; } + ngx_http_set_ctx(r, ctx, ngx_http_range_body_filter_module); + r->headers_out.status = NGX_HTTP_PARTIAL_CONTENT; - if (r->headers_out.ranges.nelts == 1) { + if (ctx->ranges.nelts == 1) { content_range = ngx_list_push(&r->headers_out.headers); if (content_range == NULL) { @@ -335,20 +350,17 @@ ngx_http_range_header_filter(ngx_http_re r->headers_out.content_length_n = range->end - range->start; + if (r->headers_out.content_length) { + r->headers_out.content_length->hash = 0; + r->headers_out.content_length = NULL; + } + return ngx_http_next_header_filter(r); } /* TODO: what if no content_type ?? */ - ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_range_filter_ctx_t)); - if (ctx == NULL) { - return NGX_ERROR; - } - - ngx_http_set_ctx(r, ctx, ngx_http_range_body_filter_module); - - len = sizeof(CRLF "--") - 1 + NGX_ATOMIC_T_LEN + sizeof(CRLF "Content-Type: ") - 1 + r->headers_out.content_type.len @@ -417,8 +429,8 @@ ngx_http_range_header_filter(ngx_http_re len = sizeof(CRLF "--") - 1 + NGX_ATOMIC_T_LEN + sizeof("--" CRLF) - 1; - range = r->headers_out.ranges.elts; - for (i = 0; i < r->headers_out.ranges.nelts; i++) { + range = ctx->ranges.elts; + for (i = 0; i < ctx->ranges.nelts; i++) { /* the size of the range: "SSSS-EEEE/TTTT" CRLF CRLF */ @@ -440,7 +452,11 @@ ngx_http_range_header_filter(ngx_http_re } r->headers_out.content_length_n = len; - r->headers_out.content_length = NULL; + + if (r->headers_out.content_length) { + r->headers_out.content_length->hash = 0; + r->headers_out.content_length = NULL; + } return ngx_http_next_header_filter(r); } @@ -449,106 +465,105 @@ ngx_http_range_header_filter(ngx_http_re static ngx_int_t ngx_http_range_body_filter(ngx_http_request_t *r, ngx_chain_t *in) { + off_t start, last; + ngx_buf_t *b, *buf; ngx_uint_t i; - ngx_buf_t *b; ngx_chain_t *out, *hcl, *rcl, *dcl, **ll; ngx_http_range_t *range; ngx_http_range_filter_ctx_t *ctx; - if (r->headers_out.ranges.nelts == 0) { + if (in == NULL) { + return ngx_http_next_body_filter(r, in); + } + + ctx = ngx_http_get_module_ctx(r, ngx_http_range_body_filter_module); + + if (ctx == NULL) { + return ngx_http_next_body_filter(r, in); + } + + buf = in->buf; + + if (ngx_buf_special(in->buf)) { return ngx_http_next_body_filter(r, in); } + if (ctx->offset) { + goto overlapped; + } + + range = ctx->ranges.elts; + + if (!buf->last_buf) { + + if (buf->in_file) { + start = buf->file_pos + ctx->offset; + last = buf->file_last + ctx->offset; + + } else { + start = buf->pos - buf->start + ctx->offset; + last = buf->last - buf->start + ctx->offset; + } + + for (i = 0; i < ctx->ranges.nelts; i++) { + if (start > range[i].start || last < range[i].end) { + goto overlapped; + } + } + } + /* - * the optimized version for the static files only - * that are passed in the single file buf + * the optimized version for the responses + * that are passed in the single buffer */ - if (in && in->buf->in_file && in->buf->last_buf) { - range = r->headers_out.ranges.elts; + ctx->offset = ngx_buf_size(buf); + + if (ctx->ranges.nelts == 1) { - if (r->headers_out.ranges.nelts == 1) { - in->buf->file_pos = range->start; - in->buf->file_last = range->end; + if (buf->in_file) { + buf->file_pos = range->start; + buf->file_last = range->end; + } - return ngx_http_next_body_filter(r, in); + if (ngx_buf_in_memory(buf)) { + buf->pos = buf->start + (size_t) range->start; + buf->last = buf->start + (size_t) range->end; } - ctx = ngx_http_get_module_ctx(r, ngx_http_range_body_filter_module); - ll = &out; + return ngx_http_next_body_filter(r, in); + } - for (i = 0; i < r->headers_out.ranges.nelts; i++) { + ll = &out; + + for (i = 0; i < ctx->ranges.nelts; i++) { - /* - * The boundary header of the range: - * CRLF - * "--0123456789" CRLF - * "Content-Type: image/jpeg" CRLF - * "Content-Range: bytes " - */ + /* + * The boundary header of the range: + * CRLF + * "--0123456789" CRLF + * "Content-Type: image/jpeg" CRLF + * "Content-Range: bytes " + */ - b = ngx_calloc_buf(r->pool); - if (b == NULL) { - return NGX_ERROR; - } + b = ngx_calloc_buf(r->pool); + if (b == NULL) { + return NGX_ERROR; + } - b->memory = 1; - b->pos = ctx->boundary_header.data; - b->last = ctx->boundary_header.data + ctx->boundary_header.len; + b->memory = 1; + b->pos = ctx->boundary_header.data; + b->last = ctx->boundary_header.data + ctx->boundary_header.len; - hcl = ngx_alloc_chain_link(r->pool); - if (hcl == NULL) { - return NGX_ERROR; - } + hcl = ngx_alloc_chain_link(r->pool); + if (hcl == NULL) { + return NGX_ERROR; + } - hcl->buf = b; + hcl->buf = b; - /* "SSSS-EEEE/TTTT" CRLF CRLF */ - - b = ngx_calloc_buf(r->pool); - if (b == NULL) { - return NGX_ERROR; - } - - b->temporary = 1; - b->pos = range[i].content_range.data; - b->last = range[i].content_range.data + range[i].content_range.len; - - rcl = ngx_alloc_chain_link(r->pool); - if (rcl == NULL) { - return NGX_ERROR; - } - - rcl->buf = b; - - - /* the range data */ - - b = ngx_calloc_buf(r->pool); - if (b == NULL) { - return NGX_ERROR; - } - - b->in_file = 1; - b->file_pos = range[i].start; - b->file_last = range[i].end; - b->file = in->buf->file; - - dcl = ngx_alloc_chain_link(r->pool); - if (dcl == NULL) { - return NGX_ERROR; - } - - dcl->buf = b; - - *ll = hcl; - hcl->next = rcl; - rcl->next = dcl; - ll = &dcl->next; - } - - /* the last boundary CRLF "--0123456789--" CRLF */ + /* "SSSS-EEEE/TTTT" CRLF CRLF */ b = ngx_calloc_buf(r->pool); if (b == NULL) { @@ -556,34 +571,91 @@ ngx_http_range_body_filter(ngx_http_requ } b->temporary = 1; - b->last_buf = 1; + b->pos = range[i].content_range.data; + b->last = range[i].content_range.data + range[i].content_range.len; + + rcl = ngx_alloc_chain_link(r->pool); + if (rcl == NULL) { + return NGX_ERROR; + } + + rcl->buf = b; + + + /* the range data */ + + b = ngx_calloc_buf(r->pool); + if (b == NULL) { + return NGX_ERROR; + } - b->pos = ngx_palloc(r->pool, sizeof(CRLF "--") - 1 + NGX_ATOMIC_T_LEN - + sizeof("--" CRLF) - 1); - if (b->pos == NULL) { + b->in_file = buf->in_file; + b->temporary = buf->temporary; + b->memory = buf->memory; + b->mmap = buf->mmap; + b->file = buf->file; + + if (buf->in_file) { + buf->file_pos = range[i].start; + buf->file_last = range[i].end; + } + + if (ngx_buf_in_memory(buf)) { + buf->pos = buf->start + (size_t) range[i].start; + buf->last = buf->start + (size_t) range[i].end; + } + + dcl = ngx_alloc_chain_link(r->pool); + if (dcl == NULL) { return NGX_ERROR; } - b->last = ngx_cpymem(b->pos, ctx->boundary_header.data, 4 + 10); - *b->last++ = '-'; *b->last++ = '-'; - *b->last++ = CR; *b->last++ = LF; - - hcl = ngx_alloc_chain_link(r->pool); - if (hcl == NULL) { - return NGX_ERROR; - } - - hcl->buf = b; - hcl->next = NULL; + dcl->buf = b; *ll = hcl; + hcl->next = rcl; + rcl->next = dcl; + ll = &dcl->next; + } - return ngx_http_next_body_filter(r, out); + /* the last boundary CRLF "--0123456789--" CRLF */ + + b = ngx_calloc_buf(r->pool); + if (b == NULL) { + return NGX_ERROR; + } + + b->temporary = 1; + b->last_buf = 1; + + b->pos = ngx_palloc(r->pool, sizeof(CRLF "--") - 1 + NGX_ATOMIC_T_LEN + + sizeof("--" CRLF) - 1); + if (b->pos == NULL) { + return NGX_ERROR; } - /* TODO: alert */ + b->last = ngx_cpymem(b->pos, ctx->boundary_header.data, 4 + 10); + *b->last++ = '-'; *b->last++ = '-'; + *b->last++ = CR; *b->last++ = LF; + + hcl = ngx_alloc_chain_link(r->pool); + if (hcl == NULL) { + return NGX_ERROR; + } - return ngx_http_next_body_filter(r, in); + hcl->buf = b; + hcl->next = NULL; + + *ll = hcl; + + return ngx_http_next_body_filter(r, out); + +overlapped: + + ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, + "range in overlapped buffers"); + + return NGX_ERROR; } diff --git a/src/http/modules/ngx_http_realip_module.c b/src/http/modules/ngx_http_realip_module.c --- a/src/http/modules/ngx_http_realip_module.c +++ b/src/http/modules/ngx_http_realip_module.c @@ -103,18 +103,18 @@ ngx_http_realip_handler(ngx_http_request ngx_http_realip_loc_conf_t *rlcf; if (r->realip_set) { - return NGX_OK; + return NGX_DECLINED; } rlcf = ngx_http_get_module_loc_conf(r, ngx_http_realip_module); if (rlcf->from == NULL) { - return NGX_OK; + return NGX_DECLINED; } if (rlcf->xfwd == 0) { if (r->headers_in.x_real_ip == NULL) { - return NGX_OK; + return NGX_DECLINED; } len = r->headers_in.x_real_ip->value.len; @@ -122,7 +122,7 @@ ngx_http_realip_handler(ngx_http_request } else { if (r->headers_in.x_forwarded_for == NULL) { - return NGX_OK; + return NGX_DECLINED; } len = r->headers_in.x_forwarded_for->value.len; @@ -158,11 +158,11 @@ ngx_http_realip_handler(ngx_http_request r->realip_set = 1; - return NGX_OK; + return NGX_DECLINED; } } - return NGX_OK; + return NGX_DECLINED; } diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c --- a/src/http/modules/ngx_http_ssl_module.c +++ b/src/http/modules/ngx_http_ssl_module.c @@ -28,6 +28,9 @@ static void *ngx_http_ssl_create_srv_con static char *ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child); +static char *ngx_http_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); + #if !defined (SSL_OP_CIPHER_SERVER_PREFERENCE) static char *ngx_http_ssl_nosupported(ngx_conf_t *cf, ngx_command_t *cmd, @@ -115,6 +118,13 @@ static ngx_command_t ngx_http_ssl_comma ngx_http_ssl_nosupported, 0, 0, ngx_http_ssl_openssl097 }, #endif + { ngx_string("ssl_session_cache"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE12, + ngx_http_ssl_session_cache, + NGX_HTTP_SRV_CONF_OFFSET, + 0, + NULL }, + { ngx_string("ssl_session_timeout"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, ngx_conf_set_sec_slot, @@ -178,7 +188,7 @@ static ngx_http_variable_t ngx_http_ssl }; -static u_char ngx_http_session_id_ctx[] = "HTTP"; +static ngx_str_t ngx_http_ssl_sess_id_ctx = ngx_string("HTTP"); static ngx_int_t @@ -257,35 +267,36 @@ ngx_http_ssl_add_variables(ngx_conf_t *c static void * ngx_http_ssl_create_srv_conf(ngx_conf_t *cf) { - ngx_http_ssl_srv_conf_t *scf; + ngx_http_ssl_srv_conf_t *sscf; - scf = ngx_pcalloc(cf->pool, sizeof(ngx_http_ssl_srv_conf_t)); - if (scf == NULL) { + sscf = ngx_pcalloc(cf->pool, sizeof(ngx_http_ssl_srv_conf_t)); + if (sscf == NULL) { return NGX_CONF_ERROR; } /* * set by ngx_pcalloc(): * - * scf->protocols = 0; - - * scf->certificate.len = 0; - * scf->certificate.data = NULL; - * scf->certificate_key.len = 0; - * scf->certificate_key.data = NULL; - * scf->client_certificate.len = 0; - * scf->client_certificate.data = NULL; - * scf->ciphers.len = 0; - * scf->ciphers.data = NULL; + * sscf->protocols = 0; + * sscf->certificate.len = 0; + * sscf->certificate.data = NULL; + * sscf->certificate_key.len = 0; + * sscf->certificate_key.data = NULL; + * sscf->client_certificate.len = 0; + * sscf->client_certificate.data = NULL; + * sscf->ciphers.len = 0; + * sscf->ciphers.data = NULL; + * sscf->shm_zone = NULL; */ - scf->enable = NGX_CONF_UNSET; - scf->session_timeout = NGX_CONF_UNSET; - scf->verify = NGX_CONF_UNSET; - scf->verify_depth = NGX_CONF_UNSET; - scf->prefer_server_ciphers = NGX_CONF_UNSET; + sscf->enable = NGX_CONF_UNSET; + sscf->verify = NGX_CONF_UNSET; + sscf->verify_depth = NGX_CONF_UNSET; + sscf->prefer_server_ciphers = NGX_CONF_UNSET; + sscf->builtin_session_cache = NGX_CONF_UNSET; + sscf->session_timeout = NGX_CONF_UNSET; - return scf; + return sscf; } @@ -330,7 +341,7 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t * conf->ssl.log = cf->log; - if (ngx_ssl_create(&conf->ssl, conf->protocols) != NGX_OK) { + if (ngx_ssl_create(&conf->ssl, conf->protocols, conf) != NGX_OK) { return NGX_CONF_ERROR; } @@ -343,7 +354,8 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t * cln->data = &conf->ssl; if (ngx_ssl_certificate(cf, &conf->ssl, &conf->certificate, - &conf->certificate_key) != NGX_OK) + &conf->certificate_key) + != NGX_OK) { return NGX_CONF_ERROR; } @@ -359,7 +371,8 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t * if (conf->verify) { if (ngx_ssl_client_certificate(cf, &conf->ssl, - &conf->client_certificate, conf->verify_depth) + &conf->client_certificate, + conf->verify_depth) != NGX_OK) { return NGX_CONF_ERROR; @@ -379,17 +392,126 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t * return NGX_CONF_ERROR; } - SSL_CTX_set_session_cache_mode(conf->ssl.ctx, SSL_SESS_CACHE_SERVER); + ngx_conf_merge_value(conf->builtin_session_cache, + prev->builtin_session_cache, + NGX_SSL_DFLT_BUILTIN_SCACHE); + + if (conf->shm_zone == NULL) { + conf->shm_zone = prev->shm_zone; + } - SSL_CTX_set_session_id_context(conf->ssl.ctx, ngx_http_session_id_ctx, - sizeof(ngx_http_session_id_ctx) - 1); - - SSL_CTX_set_timeout(conf->ssl.ctx, conf->session_timeout); + if (ngx_ssl_session_cache(&conf->ssl, &ngx_http_ssl_sess_id_ctx, + conf->builtin_session_cache, + conf->shm_zone, conf->session_timeout) + != NGX_OK) + { + return NGX_CONF_ERROR; + } return NGX_CONF_OK; } +static char * +ngx_http_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_ssl_srv_conf_t *sscf = conf; + + size_t len; + ngx_str_t *value, name, size; + ngx_int_t n; + ngx_uint_t i, j; + + value = cf->args->elts; + + for (i = 1; i < cf->args->nelts; i++) { + + if (ngx_strcmp(value[i].data, "builtin") == 0) { + sscf->builtin_session_cache = NGX_SSL_DFLT_BUILTIN_SCACHE; + continue; + } + + if (value[i].len > sizeof("builtin:") - 1 + && ngx_strncmp(value[i].data, "builtin:", sizeof("builtin:") - 1) + == 0) + { + n = ngx_atoi(value[i].data + sizeof("builtin:") - 1, + value[i].len - (sizeof("builtin:") - 1)); + + if (n == NGX_ERROR) { + goto invalid; + } + + sscf->builtin_session_cache = n; + + continue; + } + + if (value[i].len > sizeof("shared:") - 1 + && ngx_strncmp(value[i].data, "shared:", sizeof("shared:") - 1) + == 0) + { + len = 0; + + for (j = sizeof("shared:") - 1; j < value[i].len; j++) { + if (value[i].data[j] == ':') { + break; + } + + len++; + } + + if (len == 0) { + goto invalid; + } + + name.len = len; + name.data = value[i].data + sizeof("shared:") - 1; + + size.len = value[i].len - j - 1; + size.data = name.data + len + 1; + + n = ngx_parse_size(&size); + + if (n == NGX_ERROR) { + goto invalid; + } + + if (n < (ngx_int_t) (8 * ngx_pagesize)) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "session cache \"%V\" is too small", + &value[i]); + + return NGX_CONF_ERROR; + } + + sscf->shm_zone = ngx_shared_memory_add(cf, &name, n, + &ngx_http_ssl_module); + if (sscf->shm_zone == NULL) { + return NGX_CONF_ERROR; + } + + continue; + } + + goto invalid; + } + + if (sscf->shm_zone && sscf->builtin_session_cache == NGX_CONF_UNSET) { + sscf->builtin_session_cache = NGX_SSL_NO_BUILTIN_SCACHE; + } + + return NGX_CONF_OK; + +invalid: + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid session cache \"%V\"", &value[i]); + + return NGX_CONF_ERROR; +} + + #if !defined (SSL_OP_CIPHER_SERVER_PREFERENCE) static char * diff --git a/src/http/modules/ngx_http_ssl_module.h b/src/http/modules/ngx_http_ssl_module.h --- a/src/http/modules/ngx_http_ssl_module.h +++ b/src/http/modules/ngx_http_ssl_module.h @@ -14,24 +14,28 @@ typedef struct { - ngx_flag_t enable; + ngx_flag_t enable; - ngx_ssl_t ssl; + ngx_ssl_t ssl; - ngx_flag_t prefer_server_ciphers; + ngx_flag_t prefer_server_ciphers; - ngx_uint_t protocols; + ngx_uint_t protocols; + + ngx_int_t verify; + ngx_int_t verify_depth; - ngx_int_t verify; - ngx_int_t verify_depth; + ssize_t builtin_session_cache; - time_t session_timeout; + time_t session_timeout; - ngx_str_t certificate; - ngx_str_t certificate_key; - ngx_str_t client_certificate; + ngx_str_t certificate; + ngx_str_t certificate_key; + ngx_str_t client_certificate; - ngx_str_t ciphers; + ngx_str_t ciphers; + + ngx_shm_zone_t *shm_zone; } ngx_http_ssl_srv_conf_t; diff --git a/src/http/modules/perl/nginx.pm b/src/http/modules/perl/nginx.pm --- a/src/http/modules/perl/nginx.pm +++ b/src/http/modules/perl/nginx.pm @@ -47,7 +47,7 @@ our @EXPORT = qw( HTTP_INSUFFICIENT_STORAGE ); -our $VERSION = '0.5.5'; +our $VERSION = '0.5.6'; require XSLoader; XSLoader::load('nginx', $VERSION); diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -976,10 +976,10 @@ ngx_http_read_request_header(ngx_http_re } if (n == 0 || n == NGX_ERROR) { - c->error = rev->error; - c->log->action = "sending response to client"; - - ngx_http_finalize_request(r, NGX_HTTP_CLIENT_CLOSED_REQUEST); + c->error = 1; + c->log->action = "reading client request headers"; + + ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); return NGX_ERROR; } diff --git a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h --- a/src/http/ngx_http_request.h +++ b/src/http/ngx_http_request.h @@ -215,13 +215,6 @@ typedef struct { typedef struct { - off_t start; - off_t end; - ngx_str_t content_range; -} ngx_http_range_t; - - -typedef struct { ngx_list_t headers; ngx_uint_t status; @@ -245,7 +238,6 @@ typedef struct { ngx_str_t content_type; ngx_str_t charset; - ngx_array_t ranges; ngx_array_t cache_control; off_t content_length_n; @@ -438,6 +430,12 @@ struct ngx_http_request_s { #endif + /* + * instead of using the request context data in ngx_http_limit_zone_module + * we use the single bit in the request structure + */ + unsigned limit_zone_set:1; + #if 0 unsigned cachable:1; #endif diff --git a/src/http/ngx_http_upstream_round_robin.h b/src/http/ngx_http_upstream_round_robin.h --- a/src/http/ngx_http_upstream_round_robin.h +++ b/src/http/ngx_http_upstream_round_robin.h @@ -53,7 +53,7 @@ typedef struct { typedef struct { ngx_http_upstream_rr_peers_t *peers; ngx_uint_t current; - uintptr_t *tried; + uintptr_t *tried; uintptr_t data; } ngx_http_upstream_rr_peer_data_t; diff --git a/src/http/ngx_http_variables.c b/src/http/ngx_http_variables.c --- a/src/http/ngx_http_variables.c +++ b/src/http/ngx_http_variables.c @@ -28,6 +28,8 @@ static ngx_int_t ngx_http_variable_unkno static ngx_int_t ngx_http_variable_host(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_binary_remote_addr(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_variable_remote_addr(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_variable_remote_port(ngx_http_request_t *r, @@ -115,6 +117,9 @@ static ngx_http_variable_t ngx_http_cor { ngx_string("host"), NULL, ngx_http_variable_host, 0, 0, 0 }, + { ngx_string("binary_remote_addr"), NULL, + ngx_http_variable_binary_remote_addr, 0, 0, 0 }, + { ngx_string("remote_addr"), NULL, ngx_http_variable_remote_addr, 0, 0, 0 }, { ngx_string("remote_port"), NULL, ngx_http_variable_remote_port, 0, 0, 0 }, @@ -696,6 +701,26 @@ ngx_http_variable_host(ngx_http_request_ static ngx_int_t +ngx_http_variable_binary_remote_addr(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + struct sockaddr_in *sin; + + /* AF_INET only */ + + sin = (struct sockaddr_in *) r->connection->sockaddr; + + v->len = sizeof(in_addr_t); + v->valid = 1; + v->no_cachable = 0; + v->not_found = 0; + v->data = (u_char *) &sin->sin_addr.s_addr; + + return NGX_OK; +} + + +static ngx_int_t ngx_http_variable_remote_addr(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { diff --git a/src/imap/ngx_imap_ssl_module.c b/src/imap/ngx_imap_ssl_module.c --- a/src/imap/ngx_imap_ssl_module.c +++ b/src/imap/ngx_imap_ssl_module.c @@ -16,6 +16,8 @@ static void *ngx_imap_ssl_create_conf(ngx_conf_t *cf); static char *ngx_imap_ssl_merge_conf(ngx_conf_t *cf, void *parent, void *child); +static char *ngx_imap_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); #if !defined (SSL_OP_CIPHER_SERVER_PREFERENCE) @@ -99,6 +101,13 @@ static ngx_command_t ngx_imap_ssl_comma ngx_imap_ssl_nosupported, 0, 0, ngx_imap_ssl_openssl097 }, #endif + { ngx_string("ssl_session_cache"), + NGX_IMAP_MAIN_CONF|NGX_IMAP_SRV_CONF|NGX_CONF_TAKE12, + ngx_imap_ssl_session_cache, + NGX_IMAP_SRV_CONF_OFFSET, + 0, + NULL }, + { ngx_string("ssl_session_timeout"), NGX_IMAP_MAIN_CONF|NGX_IMAP_SRV_CONF|NGX_CONF_TAKE1, ngx_conf_set_sec_slot, @@ -135,7 +144,7 @@ ngx_module_t ngx_imap_ssl_module = { }; -static u_char ngx_imap_session_id_ctx[] = "IMAP"; +static ngx_str_t ngx_imap_ssl_sess_id_ctx = ngx_string("IMAP"); static void * @@ -152,19 +161,20 @@ ngx_imap_ssl_create_conf(ngx_conf_t *cf) * set by ngx_pcalloc(): * * scf->protocols = 0; - * * scf->certificate.len = 0; * scf->certificate.data = NULL; * scf->certificate_key.len = 0; * scf->certificate_key.data = NULL; * scf->ciphers.len = 0; * scf->ciphers.data = NULL; + * scf->shm_zone = NULL; */ scf->enable = NGX_CONF_UNSET; scf->starttls = NGX_CONF_UNSET; + scf->prefer_server_ciphers = NGX_CONF_UNSET; + scf->builtin_session_cache = NGX_CONF_UNSET; scf->session_timeout = NGX_CONF_UNSET; - scf->prefer_server_ciphers = NGX_CONF_UNSET; return scf; } @@ -206,7 +216,7 @@ ngx_imap_ssl_merge_conf(ngx_conf_t *cf, conf->ssl.log = cf->log; - if (ngx_ssl_create(&conf->ssl, conf->protocols) != NGX_OK) { + if (ngx_ssl_create(&conf->ssl, conf->protocols, NULL) != NGX_OK) { return NGX_CONF_ERROR; } @@ -248,17 +258,126 @@ ngx_imap_ssl_merge_conf(ngx_conf_t *cf, return NGX_CONF_ERROR; } - SSL_CTX_set_session_cache_mode(conf->ssl.ctx, SSL_SESS_CACHE_SERVER); + ngx_conf_merge_value(conf->builtin_session_cache, + prev->builtin_session_cache, + NGX_SSL_DFLT_BUILTIN_SCACHE); + + if (conf->shm_zone == NULL) { + conf->shm_zone = prev->shm_zone; + } - SSL_CTX_set_session_id_context(conf->ssl.ctx, ngx_imap_session_id_ctx, - sizeof(ngx_imap_session_id_ctx) - 1); - - SSL_CTX_set_timeout(conf->ssl.ctx, conf->session_timeout); + if (ngx_ssl_session_cache(&conf->ssl, &ngx_imap_ssl_sess_id_ctx, + conf->builtin_session_cache, + conf->shm_zone, conf->session_timeout) + != NGX_OK) + { + return NGX_CONF_ERROR; + } return NGX_CONF_OK; } +static char * +ngx_imap_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_imap_ssl_conf_t *scf = conf; + + size_t len; + ngx_str_t *value, name, size; + ngx_int_t n; + ngx_uint_t i, j; + + value = cf->args->elts; + + for (i = 1; i < cf->args->nelts; i++) { + + if (ngx_strcmp(value[i].data, "builtin") == 0) { + scf->builtin_session_cache = NGX_SSL_DFLT_BUILTIN_SCACHE; + continue; + } + + if (value[i].len > sizeof("builtin:") - 1 + && ngx_strncmp(value[i].data, "builtin:", sizeof("builtin:") - 1) + == 0) + { + n = ngx_atoi(value[i].data + sizeof("builtin:") - 1, + value[i].len - (sizeof("builtin:") - 1)); + + if (n == NGX_ERROR) { + goto invalid; + } + + scf->builtin_session_cache = n; + + continue; + } + + if (value[i].len > sizeof("shared:") - 1 + && ngx_strncmp(value[i].data, "shared:", sizeof("shared:") - 1) + == 0) + { + len = 0; + + for (j = sizeof("shared:") - 1; j < value[i].len; j++) { + if (value[i].data[j] == ':') { + break; + } + + len++; + } + + if (len == 0) { + goto invalid; + } + + name.len = len; + name.data = value[i].data + sizeof("shared:") - 1; + + size.len = value[i].len - j - 1; + size.data = name.data + len + 1; + + n = ngx_parse_size(&size); + + if (n == NGX_ERROR) { + goto invalid; + } + + if (n < (ngx_int_t) (8 * ngx_pagesize)) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "session cache \"%V\" is too small", + &value[i]); + + return NGX_CONF_ERROR; + } + + scf->shm_zone = ngx_shared_memory_add(cf, &name, n, + &ngx_imap_ssl_module); + if (scf->shm_zone == NULL) { + return NGX_CONF_ERROR; + } + + continue; + } + + goto invalid; + } + + if (scf->shm_zone && scf->builtin_session_cache == NGX_CONF_UNSET) { + scf->builtin_session_cache = NGX_SSL_NO_BUILTIN_SCACHE; + } + + return NGX_CONF_OK; + +invalid: + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid session cache \"%V\"", &value[i]); + + return NGX_CONF_ERROR; +} + + #if !defined (SSL_OP_CIPHER_SERVER_PREFERENCE) static char * diff --git a/src/imap/ngx_imap_ssl_module.h b/src/imap/ngx_imap_ssl_module.h --- a/src/imap/ngx_imap_ssl_module.h +++ b/src/imap/ngx_imap_ssl_module.h @@ -19,22 +19,25 @@ typedef struct { - ngx_flag_t enable; + ngx_flag_t enable; + + ngx_ssl_t ssl; - ngx_ssl_t ssl; + ngx_flag_t prefer_server_ciphers; + ngx_flag_t starttls; - ngx_flag_t prefer_server_ciphers; - ngx_flag_t starttls; + ngx_uint_t protocols; - ngx_uint_t protocols; + ssize_t builtin_session_cache; - time_t session_timeout; + time_t session_timeout; - ngx_str_t certificate; - ngx_str_t certificate_key; + ngx_str_t certificate; + ngx_str_t certificate_key; - ngx_str_t ciphers; + ngx_str_t ciphers; + ngx_shm_zone_t *shm_zone; } ngx_imap_ssl_conf_t; diff --git a/src/os/unix/ngx_linux_init.c b/src/os/unix/ngx_linux_init.c --- a/src/os/unix/ngx_linux_init.c +++ b/src/os/unix/ngx_linux_init.c @@ -13,7 +13,7 @@ static ngx_int_t ngx_linux_procfs(char * char ngx_linux_kern_ostype[50]; -char ngx_linux_kern_osrelease[20]; +char ngx_linux_kern_osrelease[50]; int ngx_linux_rtsig_max;