From e18a033b921d0d79fa8278f853548e6125b93e0c Mon Sep 17 00:00:00 2001 From: Konstantin Ananyev Date: Tue, 31 Oct 2017 12:40:17 +0000 Subject: Integrate TLDK with NGINX Created a clone of nginx (from https://github.com/nginx/nginx) to demonstrate and benchmark TLDK library integrated with real world application. A new nginx module is created and and BSD socket-like API is implemented on top of native TLDK API. Note, that right now only minimalistic subset of socket-like API is provided: - accept - close - readv - recv - writev so only limited nginx functionality is available for a moment. Change-Id: Ie1efe9349a0538da4348a48fb8306cbf636b5a92 Signed-off-by: Mohammad Abdul Awal Signed-off-by: Reshma Pattan Signed-off-by: Remy Horton Signed-off-by: Konstantin Ananyev --- app/nginx/src/core/nginx.c | 1583 ++++++++++ app/nginx/src/core/nginx.h | 26 + app/nginx/src/core/ngx_array.c | 141 + app/nginx/src/core/ngx_array.h | 53 + app/nginx/src/core/ngx_buf.c | 313 ++ app/nginx/src/core/ngx_buf.h | 170 ++ app/nginx/src/core/ngx_conf_file.c | 1479 ++++++++++ app/nginx/src/core/ngx_conf_file.h | 295 ++ app/nginx/src/core/ngx_config.h | 145 + app/nginx/src/core/ngx_connection.c | 1404 +++++++++ app/nginx/src/core/ngx_connection.h | 224 ++ app/nginx/src/core/ngx_core.h | 111 + app/nginx/src/core/ngx_cpuinfo.c | 139 + app/nginx/src/core/ngx_crc.h | 39 + app/nginx/src/core/ngx_crc32.c | 129 + app/nginx/src/core/ngx_crc32.h | 79 + app/nginx/src/core/ngx_crypt.c | 270 ++ app/nginx/src/core/ngx_crypt.h | 20 + app/nginx/src/core/ngx_cycle.c | 1384 +++++++++ app/nginx/src/core/ngx_cycle.h | 144 + app/nginx/src/core/ngx_file.c | 1127 ++++++++ app/nginx/src/core/ngx_file.h | 164 ++ app/nginx/src/core/ngx_hash.c | 988 +++++++ app/nginx/src/core/ngx_hash.h | 122 + app/nginx/src/core/ngx_inet.c | 1493 ++++++++++ app/nginx/src/core/ngx_inet.h | 129 + app/nginx/src/core/ngx_list.c | 63 + app/nginx/src/core/ngx_list.h | 83 + app/nginx/src/core/ngx_log.c | 755 +++++ app/nginx/src/core/ngx_log.h | 268 ++ app/nginx/src/core/ngx_md5.c | 283 ++ app/nginx/src/core/ngx_md5.h | 28 + app/nginx/src/core/ngx_module.c | 360 +++ app/nginx/src/core/ngx_module.h | 283 ++ app/nginx/src/core/ngx_murmurhash.c | 50 + app/nginx/src/core/ngx_murmurhash.h | 19 + app/nginx/src/core/ngx_open_file_cache.c | 1253 ++++++++ app/nginx/src/core/ngx_open_file_cache.h | 129 + app/nginx/src/core/ngx_output_chain.c | 767 +++++ app/nginx/src/core/ngx_palloc.c | 430 +++ app/nginx/src/core/ngx_palloc.h | 95 + app/nginx/src/core/ngx_parse.c | 283 ++ app/nginx/src/core/ngx_parse.h | 21 + app/nginx/src/core/ngx_parse_time.c | 276 ++ app/nginx/src/core/ngx_parse_time.h | 22 + app/nginx/src/core/ngx_proxy_protocol.c | 168 ++ app/nginx/src/core/ngx_proxy_protocol.h | 25 + app/nginx/src/core/ngx_queue.c | 80 + app/nginx/src/core/ngx_queue.h | 112 + app/nginx/src/core/ngx_radix_tree.c | 488 ++++ app/nginx/src/core/ngx_radix_tree.h | 55 + app/nginx/src/core/ngx_rbtree.c | 409 +++ app/nginx/src/core/ngx_rbtree.h | 84 + app/nginx/src/core/ngx_regex.c | 435 +++ app/nginx/src/core/ngx_regex.h | 60 + app/nginx/src/core/ngx_resolver.c | 4662 ++++++++++++++++++++++++++++++ app/nginx/src/core/ngx_resolver.h | 238 ++ app/nginx/src/core/ngx_rwlock.c | 120 + app/nginx/src/core/ngx_rwlock.h | 21 + app/nginx/src/core/ngx_sha1.c | 294 ++ app/nginx/src/core/ngx_sha1.h | 28 + app/nginx/src/core/ngx_shmtx.c | 310 ++ app/nginx/src/core/ngx_shmtx.h | 49 + app/nginx/src/core/ngx_slab.c | 806 ++++++ app/nginx/src/core/ngx_slab.h | 71 + app/nginx/src/core/ngx_spinlock.c | 53 + app/nginx/src/core/ngx_string.c | 1976 +++++++++++++ app/nginx/src/core/ngx_string.h | 234 ++ app/nginx/src/core/ngx_syslog.c | 382 +++ app/nginx/src/core/ngx_syslog.h | 31 + app/nginx/src/core/ngx_thread_pool.c | 641 ++++ app/nginx/src/core/ngx_thread_pool.h | 36 + app/nginx/src/core/ngx_times.c | 428 +++ app/nginx/src/core/ngx_times.h | 52 + 74 files changed, 29987 insertions(+) create mode 100644 app/nginx/src/core/nginx.c create mode 100644 app/nginx/src/core/nginx.h create mode 100644 app/nginx/src/core/ngx_array.c create mode 100644 app/nginx/src/core/ngx_array.h create mode 100644 app/nginx/src/core/ngx_buf.c create mode 100644 app/nginx/src/core/ngx_buf.h create mode 100644 app/nginx/src/core/ngx_conf_file.c create mode 100644 app/nginx/src/core/ngx_conf_file.h create mode 100644 app/nginx/src/core/ngx_config.h create mode 100644 app/nginx/src/core/ngx_connection.c create mode 100644 app/nginx/src/core/ngx_connection.h create mode 100644 app/nginx/src/core/ngx_core.h create mode 100644 app/nginx/src/core/ngx_cpuinfo.c create mode 100644 app/nginx/src/core/ngx_crc.h create mode 100644 app/nginx/src/core/ngx_crc32.c create mode 100644 app/nginx/src/core/ngx_crc32.h create mode 100644 app/nginx/src/core/ngx_crypt.c create mode 100644 app/nginx/src/core/ngx_crypt.h create mode 100644 app/nginx/src/core/ngx_cycle.c create mode 100644 app/nginx/src/core/ngx_cycle.h create mode 100644 app/nginx/src/core/ngx_file.c create mode 100644 app/nginx/src/core/ngx_file.h create mode 100644 app/nginx/src/core/ngx_hash.c create mode 100644 app/nginx/src/core/ngx_hash.h create mode 100644 app/nginx/src/core/ngx_inet.c create mode 100644 app/nginx/src/core/ngx_inet.h create mode 100644 app/nginx/src/core/ngx_list.c create mode 100644 app/nginx/src/core/ngx_list.h create mode 100644 app/nginx/src/core/ngx_log.c create mode 100644 app/nginx/src/core/ngx_log.h create mode 100644 app/nginx/src/core/ngx_md5.c create mode 100644 app/nginx/src/core/ngx_md5.h create mode 100644 app/nginx/src/core/ngx_module.c create mode 100644 app/nginx/src/core/ngx_module.h create mode 100644 app/nginx/src/core/ngx_murmurhash.c create mode 100644 app/nginx/src/core/ngx_murmurhash.h create mode 100644 app/nginx/src/core/ngx_open_file_cache.c create mode 100644 app/nginx/src/core/ngx_open_file_cache.h create mode 100644 app/nginx/src/core/ngx_output_chain.c create mode 100644 app/nginx/src/core/ngx_palloc.c create mode 100644 app/nginx/src/core/ngx_palloc.h create mode 100644 app/nginx/src/core/ngx_parse.c create mode 100644 app/nginx/src/core/ngx_parse.h create mode 100644 app/nginx/src/core/ngx_parse_time.c create mode 100644 app/nginx/src/core/ngx_parse_time.h create mode 100644 app/nginx/src/core/ngx_proxy_protocol.c create mode 100644 app/nginx/src/core/ngx_proxy_protocol.h create mode 100644 app/nginx/src/core/ngx_queue.c create mode 100644 app/nginx/src/core/ngx_queue.h create mode 100644 app/nginx/src/core/ngx_radix_tree.c create mode 100644 app/nginx/src/core/ngx_radix_tree.h create mode 100644 app/nginx/src/core/ngx_rbtree.c create mode 100644 app/nginx/src/core/ngx_rbtree.h create mode 100644 app/nginx/src/core/ngx_regex.c create mode 100644 app/nginx/src/core/ngx_regex.h create mode 100644 app/nginx/src/core/ngx_resolver.c create mode 100644 app/nginx/src/core/ngx_resolver.h create mode 100644 app/nginx/src/core/ngx_rwlock.c create mode 100644 app/nginx/src/core/ngx_rwlock.h create mode 100644 app/nginx/src/core/ngx_sha1.c create mode 100644 app/nginx/src/core/ngx_sha1.h create mode 100644 app/nginx/src/core/ngx_shmtx.c create mode 100644 app/nginx/src/core/ngx_shmtx.h create mode 100644 app/nginx/src/core/ngx_slab.c create mode 100644 app/nginx/src/core/ngx_slab.h create mode 100644 app/nginx/src/core/ngx_spinlock.c create mode 100644 app/nginx/src/core/ngx_string.c create mode 100644 app/nginx/src/core/ngx_string.h create mode 100644 app/nginx/src/core/ngx_syslog.c create mode 100644 app/nginx/src/core/ngx_syslog.h create mode 100644 app/nginx/src/core/ngx_thread_pool.c create mode 100644 app/nginx/src/core/ngx_thread_pool.h create mode 100644 app/nginx/src/core/ngx_times.c create mode 100644 app/nginx/src/core/ngx_times.h (limited to 'app/nginx/src/core') diff --git a/app/nginx/src/core/nginx.c b/app/nginx/src/core/nginx.c new file mode 100644 index 0000000..abaa50d --- /dev/null +++ b/app/nginx/src/core/nginx.c @@ -0,0 +1,1583 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +static void ngx_show_version_info(void); +static ngx_int_t ngx_add_inherited_sockets(ngx_cycle_t *cycle); +static void ngx_cleanup_environment(void *data); +static ngx_int_t ngx_get_options(int argc, char *const *argv); +static ngx_int_t ngx_process_options(ngx_cycle_t *cycle); +static ngx_int_t ngx_save_argv(ngx_cycle_t *cycle, int argc, char *const *argv); +static void *ngx_core_module_create_conf(ngx_cycle_t *cycle); +static char *ngx_core_module_init_conf(ngx_cycle_t *cycle, void *conf); +static char *ngx_set_user(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static char *ngx_set_env(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static char *ngx_set_priority(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static char *ngx_set_cpu_affinity(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static char *ngx_set_worker_processes(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static char *ngx_load_module(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +#if (NGX_HAVE_DLOPEN) +static void ngx_unload_module(void *data); +#endif + + +static ngx_conf_enum_t ngx_debug_points[] = { + { ngx_string("stop"), NGX_DEBUG_POINTS_STOP }, + { ngx_string("abort"), NGX_DEBUG_POINTS_ABORT }, + { ngx_null_string, 0 } +}; + + +static ngx_command_t ngx_core_commands[] = { + + { ngx_string("daemon"), + NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + 0, + offsetof(ngx_core_conf_t, daemon), + NULL }, + + { ngx_string("master_process"), + NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + 0, + offsetof(ngx_core_conf_t, master), + NULL }, + + { ngx_string("timer_resolution"), + NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1, + ngx_conf_set_msec_slot, + 0, + offsetof(ngx_core_conf_t, timer_resolution), + NULL }, + + { ngx_string("pid"), + NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1, + ngx_conf_set_str_slot, + 0, + offsetof(ngx_core_conf_t, pid), + NULL }, + + { ngx_string("lock_file"), + NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1, + ngx_conf_set_str_slot, + 0, + offsetof(ngx_core_conf_t, lock_file), + NULL }, + + { ngx_string("worker_processes"), + NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1, + ngx_set_worker_processes, + 0, + 0, + NULL }, + + { ngx_string("debug_points"), + NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1, + ngx_conf_set_enum_slot, + 0, + offsetof(ngx_core_conf_t, debug_points), + &ngx_debug_points }, + + { ngx_string("user"), + NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE12, + ngx_set_user, + 0, + 0, + NULL }, + + { ngx_string("worker_priority"), + NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1, + ngx_set_priority, + 0, + 0, + NULL }, + + { ngx_string("worker_cpu_affinity"), + NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_1MORE, + ngx_set_cpu_affinity, + 0, + 0, + NULL }, + + { ngx_string("worker_rlimit_nofile"), + NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + 0, + offsetof(ngx_core_conf_t, rlimit_nofile), + NULL }, + + { ngx_string("worker_rlimit_core"), + NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1, + ngx_conf_set_off_slot, + 0, + offsetof(ngx_core_conf_t, rlimit_core), + NULL }, + + { ngx_string("worker_shutdown_timeout"), + NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1, + ngx_conf_set_msec_slot, + 0, + offsetof(ngx_core_conf_t, shutdown_timeout), + NULL }, + + { ngx_string("working_directory"), + NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1, + ngx_conf_set_str_slot, + 0, + offsetof(ngx_core_conf_t, working_directory), + NULL }, + + { ngx_string("env"), + NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1, + ngx_set_env, + 0, + 0, + NULL }, + + { ngx_string("load_module"), + NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1, + ngx_load_module, + 0, + 0, + NULL }, + + ngx_null_command +}; + + +static ngx_core_module_t ngx_core_module_ctx = { + ngx_string("core"), + ngx_core_module_create_conf, + ngx_core_module_init_conf +}; + + +ngx_module_t ngx_core_module = { + NGX_MODULE_V1, + &ngx_core_module_ctx, /* module context */ + ngx_core_commands, /* 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_uint_t ngx_show_help; +static ngx_uint_t ngx_show_version; +static ngx_uint_t ngx_show_configure; +static u_char *ngx_prefix; +static u_char *ngx_conf_file; +static u_char *ngx_conf_params; +static char *ngx_signal; + + +static char **ngx_os_environ; + + +int ngx_cdecl +main(int argc, char *const *argv) +{ + ngx_buf_t *b; + ngx_log_t *log; + ngx_uint_t i; + ngx_cycle_t *cycle, init_cycle; + ngx_conf_dump_t *cd; + ngx_core_conf_t *ccf; + + ngx_debug_init(); + + if (ngx_strerror_init() != NGX_OK) { + return 1; + } + + if (ngx_get_options(argc, argv) != NGX_OK) { + return 1; + } + + if (ngx_show_version) { + ngx_show_version_info(); + + if (!ngx_test_config) { + return 0; + } + } + + /* TODO */ ngx_max_sockets = -1; + + ngx_time_init(); + +#if (NGX_PCRE) + ngx_regex_init(); +#endif + + ngx_pid = ngx_getpid(); + + log = ngx_log_init(ngx_prefix); + if (log == NULL) { + return 1; + } + + /* STUB */ +#if (NGX_OPENSSL) + ngx_ssl_init(log); +#endif + + /* + * init_cycle->log is required for signal handlers and + * ngx_process_options() + */ + + ngx_memzero(&init_cycle, sizeof(ngx_cycle_t)); + init_cycle.log = log; + ngx_cycle = &init_cycle; + + init_cycle.pool = ngx_create_pool(1024, log); + if (init_cycle.pool == NULL) { + return 1; + } + + if (ngx_save_argv(&init_cycle, argc, argv) != NGX_OK) { + return 1; + } + + if (ngx_process_options(&init_cycle) != NGX_OK) { + return 1; + } + + if (ngx_os_init(log) != NGX_OK) { + return 1; + } + + /* + * ngx_crc32_table_init() requires ngx_cacheline_size set in ngx_os_init() + */ + + if (ngx_crc32_table_init() != NGX_OK) { + return 1; + } + + if (ngx_add_inherited_sockets(&init_cycle) != NGX_OK) { + return 1; + } + + if (ngx_preinit_modules() != NGX_OK) { + return 1; + } + + cycle = ngx_init_cycle(&init_cycle); + if (cycle == NULL) { + if (ngx_test_config) { + ngx_log_stderr(0, "configuration file %s test failed", + init_cycle.conf_file.data); + } + + return 1; + } + + if (ngx_test_config) { + if (!ngx_quiet_mode) { + ngx_log_stderr(0, "configuration file %s test is successful", + cycle->conf_file.data); + } + + if (ngx_dump_config) { + cd = cycle->config_dump.elts; + + for (i = 0; i < cycle->config_dump.nelts; i++) { + + ngx_write_stdout("# configuration file "); + (void) ngx_write_fd(ngx_stdout, cd[i].name.data, + cd[i].name.len); + ngx_write_stdout(":" NGX_LINEFEED); + + b = cd[i].buffer; + + (void) ngx_write_fd(ngx_stdout, b->pos, b->last - b->pos); + ngx_write_stdout(NGX_LINEFEED); + } + } + + return 0; + } + + if (ngx_signal) { + return ngx_signal_process(cycle, ngx_signal); + } + + ngx_os_status(cycle->log); + + ngx_cycle = cycle; + + ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module); + + if (ccf->master && ngx_process == NGX_PROCESS_SINGLE) { + ngx_process = NGX_PROCESS_MASTER; + } + +#if !(NGX_WIN32) + + if (ngx_init_signals(cycle->log) != NGX_OK) { + return 1; + } + + if (!ngx_inherited && ccf->daemon) { + if (ngx_daemon(cycle->log) != NGX_OK) { + return 1; + } + + ngx_daemonized = 1; + } + + if (ngx_inherited) { + ngx_daemonized = 1; + } + +#endif + + if (ngx_create_pidfile(&ccf->pid, cycle->log) != NGX_OK) { + return 1; + } + + if (ngx_log_redirect_stderr(cycle) != NGX_OK) { + return 1; + } + + if (log->file->fd != ngx_stderr) { + if (ngx_close_file(log->file->fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + ngx_close_file_n " built-in log failed"); + } + } + + ngx_use_stderr = 0; + + if (ngx_process == NGX_PROCESS_SINGLE) { + ngx_single_process_cycle(cycle); + + } else { + ngx_master_process_cycle(cycle); + } + + return 0; +} + + +static void +ngx_show_version_info(void) +{ + ngx_write_stderr("nginx version: " NGINX_VER_BUILD NGX_LINEFEED); + + if (ngx_show_help) { + ngx_write_stderr( + "Usage: nginx [-?hvVtTq] [-s signal] [-c filename] " + "[-p prefix] [-g directives]" NGX_LINEFEED + NGX_LINEFEED + "Options:" NGX_LINEFEED + " -?,-h : this help" NGX_LINEFEED + " -v : show version and exit" NGX_LINEFEED + " -V : show version and configure options then exit" + NGX_LINEFEED + " -t : test configuration and exit" NGX_LINEFEED + " -T : test configuration, dump it and exit" + NGX_LINEFEED + " -q : suppress non-error messages " + "during configuration testing" NGX_LINEFEED + " -s signal : send signal to a master process: " + "stop, quit, reopen, reload" NGX_LINEFEED +#ifdef NGX_PREFIX + " -p prefix : set prefix path (default: " NGX_PREFIX ")" + NGX_LINEFEED +#else + " -p prefix : set prefix path (default: NONE)" NGX_LINEFEED +#endif + " -c filename : set configuration file (default: " NGX_CONF_PATH + ")" NGX_LINEFEED + " -g directives : set global directives out of configuration " + "file" NGX_LINEFEED NGX_LINEFEED + ); + } + + if (ngx_show_configure) { + +#ifdef NGX_COMPILER + ngx_write_stderr("built by " NGX_COMPILER NGX_LINEFEED); +#endif + +#if (NGX_SSL) + if (ngx_strcmp(ngx_ssl_version(), OPENSSL_VERSION_TEXT) == 0) { + ngx_write_stderr("built with " OPENSSL_VERSION_TEXT NGX_LINEFEED); + } else { + ngx_write_stderr("built with " OPENSSL_VERSION_TEXT + " (running with "); + ngx_write_stderr((char *) (uintptr_t) ngx_ssl_version()); + ngx_write_stderr(")" NGX_LINEFEED); + } +#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME + ngx_write_stderr("TLS SNI support enabled" NGX_LINEFEED); +#else + ngx_write_stderr("TLS SNI support disabled" NGX_LINEFEED); +#endif +#endif + + ngx_write_stderr("configure arguments:" NGX_CONFIGURE NGX_LINEFEED); + } +} + + +static ngx_int_t +ngx_add_inherited_sockets(ngx_cycle_t *cycle) +{ + u_char *p, *v, *inherited; + ngx_int_t s; + ngx_listening_t *ls; + + inherited = (u_char *) getenv(NGINX_VAR); + + if (inherited == NULL) { + return NGX_OK; + } + + ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, + "using inherited sockets from \"%s\"", inherited); + + if (ngx_array_init(&cycle->listening, cycle->pool, 10, + sizeof(ngx_listening_t)) + != NGX_OK) + { + return NGX_ERROR; + } + + for (p = inherited, v = p; *p; p++) { + if (*p == ':' || *p == ';') { + s = ngx_atoi(v, p - v); + if (s == NGX_ERROR) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, + "invalid socket number \"%s\" in " NGINX_VAR + " environment variable, ignoring the rest" + " of the variable", v); + break; + } + + v = p + 1; + + ls = ngx_array_push(&cycle->listening); + if (ls == NULL) { + return NGX_ERROR; + } + + ngx_memzero(ls, sizeof(ngx_listening_t)); + + ls->fd = (ngx_socket_t) s; + } + } + + if (v != p) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, + "invalid socket number \"%s\" in " NGINX_VAR + " environment variable, ignoring", v); + } + + ngx_inherited = 1; + + return ngx_set_inherited_sockets(cycle); +} + + +char ** +ngx_set_environment(ngx_cycle_t *cycle, ngx_uint_t *last) +{ + char **p, **env; + ngx_str_t *var; + ngx_uint_t i, n; + ngx_core_conf_t *ccf; + ngx_pool_cleanup_t *cln; + + ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module); + + if (last == NULL && ccf->environment) { + return ccf->environment; + } + + var = ccf->env.elts; + + for (i = 0; i < ccf->env.nelts; i++) { + if (ngx_strcmp(var[i].data, "TZ") == 0 + || ngx_strncmp(var[i].data, "TZ=", 3) == 0) + { + goto tz_found; + } + } + + var = ngx_array_push(&ccf->env); + if (var == NULL) { + return NULL; + } + + var->len = 2; + var->data = (u_char *) "TZ"; + + var = ccf->env.elts; + +tz_found: + + n = 0; + + for (i = 0; i < ccf->env.nelts; i++) { + + if (var[i].data[var[i].len] == '=') { + n++; + continue; + } + + for (p = ngx_os_environ; *p; p++) { + + if (ngx_strncmp(*p, var[i].data, var[i].len) == 0 + && (*p)[var[i].len] == '=') + { + n++; + break; + } + } + } + + if (last) { + env = ngx_alloc((*last + n + 1) * sizeof(char *), cycle->log); + if (env == NULL) { + return NULL; + } + + *last = n; + + } else { + cln = ngx_pool_cleanup_add(cycle->pool, 0); + if (cln == NULL) { + return NULL; + } + + env = ngx_alloc((n + 1) * sizeof(char *), cycle->log); + if (env == NULL) { + return NULL; + } + + cln->handler = ngx_cleanup_environment; + cln->data = env; + } + + n = 0; + + for (i = 0; i < ccf->env.nelts; i++) { + + if (var[i].data[var[i].len] == '=') { + env[n++] = (char *) var[i].data; + continue; + } + + for (p = ngx_os_environ; *p; p++) { + + if (ngx_strncmp(*p, var[i].data, var[i].len) == 0 + && (*p)[var[i].len] == '=') + { + env[n++] = *p; + break; + } + } + } + + env[n] = NULL; + + if (last == NULL) { + ccf->environment = env; + environ = env; + } + + return env; +} + + +static void +ngx_cleanup_environment(void *data) +{ + char **env = data; + + if (environ == env) { + + /* + * if the environment is still used, as it happens on exit, + * the only option is to leak it + */ + + return; + } + + ngx_free(env); +} + + +ngx_pid_t +ngx_exec_new_binary(ngx_cycle_t *cycle, char *const *argv) +{ + char **env, *var; + u_char *p; + ngx_uint_t i, n; + ngx_pid_t pid; + ngx_exec_ctx_t ctx; + ngx_core_conf_t *ccf; + ngx_listening_t *ls; + + ngx_memzero(&ctx, sizeof(ngx_exec_ctx_t)); + + ctx.path = argv[0]; + ctx.name = "new binary process"; + ctx.argv = argv; + + n = 2; + env = ngx_set_environment(cycle, &n); + if (env == NULL) { + return NGX_INVALID_PID; + } + + var = ngx_alloc(sizeof(NGINX_VAR) + + cycle->listening.nelts * (NGX_INT32_LEN + 1) + 2, + cycle->log); + if (var == NULL) { + ngx_free(env); + return NGX_INVALID_PID; + } + + p = ngx_cpymem(var, NGINX_VAR "=", sizeof(NGINX_VAR)); + + ls = cycle->listening.elts; + for (i = 0; i < cycle->listening.nelts; i++) { + p = ngx_sprintf(p, "%ud;", ls[i].fd); + } + + *p = '\0'; + + env[n++] = var; + +#if (NGX_SETPROCTITLE_USES_ENV) + + /* allocate the spare 300 bytes for the new binary process title */ + + env[n++] = "SPARE=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"; + +#endif + + env[n] = NULL; + +#if (NGX_DEBUG) + { + char **e; + for (e = env; *e; e++) { + ngx_log_debug1(NGX_LOG_DEBUG_CORE, cycle->log, 0, "env: %s", *e); + } + } +#endif + + ctx.envp = (char *const *) env; + + ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module); + + if (ngx_rename_file(ccf->pid.data, ccf->oldpid.data) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + ngx_rename_file_n " %s to %s failed " + "before executing new binary process \"%s\"", + ccf->pid.data, ccf->oldpid.data, argv[0]); + + ngx_free(env); + ngx_free(var); + + return NGX_INVALID_PID; + } + + pid = ngx_execute(cycle, &ctx); + + if (pid == NGX_INVALID_PID) { + if (ngx_rename_file(ccf->oldpid.data, ccf->pid.data) + == NGX_FILE_ERROR) + { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + ngx_rename_file_n " %s back to %s failed after " + "an attempt to execute new binary process \"%s\"", + ccf->oldpid.data, ccf->pid.data, argv[0]); + } + } + + ngx_free(env); + ngx_free(var); + + return pid; +} + + +static ngx_int_t +ngx_get_options(int argc, char *const *argv) +{ + u_char *p; + ngx_int_t i; + + for (i = 1; i < argc; i++) { + + p = (u_char *) argv[i]; + + if (*p++ != '-') { + ngx_log_stderr(0, "invalid option: \"%s\"", argv[i]); + return NGX_ERROR; + } + + while (*p) { + + switch (*p++) { + + case '?': + case 'h': + ngx_show_version = 1; + ngx_show_help = 1; + break; + + case 'v': + ngx_show_version = 1; + break; + + case 'V': + ngx_show_version = 1; + ngx_show_configure = 1; + break; + + case 't': + ngx_test_config = 1; + break; + + case 'T': + ngx_test_config = 1; + ngx_dump_config = 1; + break; + + case 'q': + ngx_quiet_mode = 1; + break; + + case 'p': + if (*p) { + ngx_prefix = p; + goto next; + } + + if (argv[++i]) { + ngx_prefix = (u_char *) argv[i]; + goto next; + } + + ngx_log_stderr(0, "option \"-p\" requires directory name"); + return NGX_ERROR; + + case 'c': + if (*p) { + ngx_conf_file = p; + goto next; + } + + if (argv[++i]) { + ngx_conf_file = (u_char *) argv[i]; + goto next; + } + + ngx_log_stderr(0, "option \"-c\" requires file name"); + return NGX_ERROR; + + case 'g': + if (*p) { + ngx_conf_params = p; + goto next; + } + + if (argv[++i]) { + ngx_conf_params = (u_char *) argv[i]; + goto next; + } + + ngx_log_stderr(0, "option \"-g\" requires parameter"); + return NGX_ERROR; + + case 's': + if (*p) { + ngx_signal = (char *) p; + + } else if (argv[++i]) { + ngx_signal = argv[i]; + + } else { + ngx_log_stderr(0, "option \"-s\" requires parameter"); + return NGX_ERROR; + } + + if (ngx_strcmp(ngx_signal, "stop") == 0 + || ngx_strcmp(ngx_signal, "quit") == 0 + || ngx_strcmp(ngx_signal, "reopen") == 0 + || ngx_strcmp(ngx_signal, "reload") == 0) + { + ngx_process = NGX_PROCESS_SIGNALLER; + goto next; + } + + ngx_log_stderr(0, "invalid option: \"-s %s\"", ngx_signal); + return NGX_ERROR; + + default: + ngx_log_stderr(0, "invalid option: \"%c\"", *(p - 1)); + return NGX_ERROR; + } + } + + next: + + continue; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_save_argv(ngx_cycle_t *cycle, int argc, char *const *argv) +{ +#if (NGX_FREEBSD) + + ngx_os_argv = (char **) argv; + ngx_argc = argc; + ngx_argv = (char **) argv; + +#else + size_t len; + ngx_int_t i; + + ngx_os_argv = (char **) argv; + ngx_argc = argc; + + ngx_argv = ngx_alloc((argc + 1) * sizeof(char *), cycle->log); + if (ngx_argv == NULL) { + return NGX_ERROR; + } + + for (i = 0; i < argc; i++) { + len = ngx_strlen(argv[i]) + 1; + + ngx_argv[i] = ngx_alloc(len, cycle->log); + if (ngx_argv[i] == NULL) { + return NGX_ERROR; + } + + (void) ngx_cpystrn((u_char *) ngx_argv[i], (u_char *) argv[i], len); + } + + ngx_argv[i] = NULL; + +#endif + + ngx_os_environ = environ; + + return NGX_OK; +} + + +static ngx_int_t +ngx_process_options(ngx_cycle_t *cycle) +{ + u_char *p; + size_t len; + + if (ngx_prefix) { + len = ngx_strlen(ngx_prefix); + p = ngx_prefix; + + if (len && !ngx_path_separator(p[len - 1])) { + p = ngx_pnalloc(cycle->pool, len + 1); + if (p == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(p, ngx_prefix, len); + p[len++] = '/'; + } + + cycle->conf_prefix.len = len; + cycle->conf_prefix.data = p; + cycle->prefix.len = len; + cycle->prefix.data = p; + + } else { + +#ifndef NGX_PREFIX + + p = ngx_pnalloc(cycle->pool, NGX_MAX_PATH); + if (p == NULL) { + return NGX_ERROR; + } + + if (ngx_getcwd(p, NGX_MAX_PATH) == 0) { + ngx_log_stderr(ngx_errno, "[emerg]: " ngx_getcwd_n " failed"); + return NGX_ERROR; + } + + len = ngx_strlen(p); + + p[len++] = '/'; + + cycle->conf_prefix.len = len; + cycle->conf_prefix.data = p; + cycle->prefix.len = len; + cycle->prefix.data = p; + +#else + +#ifdef NGX_CONF_PREFIX + ngx_str_set(&cycle->conf_prefix, NGX_CONF_PREFIX); +#else + ngx_str_set(&cycle->conf_prefix, NGX_PREFIX); +#endif + ngx_str_set(&cycle->prefix, NGX_PREFIX); + +#endif + } + + if (ngx_conf_file) { + cycle->conf_file.len = ngx_strlen(ngx_conf_file); + cycle->conf_file.data = ngx_conf_file; + + } else { + ngx_str_set(&cycle->conf_file, NGX_CONF_PATH); + } + + if (ngx_conf_full_name(cycle, &cycle->conf_file, 0) != NGX_OK) { + return NGX_ERROR; + } + + for (p = cycle->conf_file.data + cycle->conf_file.len - 1; + p > cycle->conf_file.data; + p--) + { + if (ngx_path_separator(*p)) { + cycle->conf_prefix.len = p - ngx_cycle->conf_file.data + 1; + cycle->conf_prefix.data = ngx_cycle->conf_file.data; + break; + } + } + + if (ngx_conf_params) { + cycle->conf_param.len = ngx_strlen(ngx_conf_params); + cycle->conf_param.data = ngx_conf_params; + } + + if (ngx_test_config) { + cycle->log->log_level = NGX_LOG_INFO; + } + + return NGX_OK; +} + + +static void * +ngx_core_module_create_conf(ngx_cycle_t *cycle) +{ + ngx_core_conf_t *ccf; + + ccf = ngx_pcalloc(cycle->pool, sizeof(ngx_core_conf_t)); + if (ccf == NULL) { + return NULL; + } + + /* + * set by ngx_pcalloc() + * + * ccf->pid = NULL; + * ccf->oldpid = NULL; + * ccf->priority = 0; + * ccf->cpu_affinity_auto = 0; + * ccf->cpu_affinity_n = 0; + * ccf->cpu_affinity = NULL; + */ + + ccf->daemon = NGX_CONF_UNSET; + ccf->master = NGX_CONF_UNSET; + ccf->timer_resolution = NGX_CONF_UNSET_MSEC; + ccf->shutdown_timeout = NGX_CONF_UNSET_MSEC; + + ccf->worker_processes = NGX_CONF_UNSET; + ccf->debug_points = NGX_CONF_UNSET; + + ccf->rlimit_nofile = NGX_CONF_UNSET; + ccf->rlimit_core = NGX_CONF_UNSET; + + ccf->user = (ngx_uid_t) NGX_CONF_UNSET_UINT; + ccf->group = (ngx_gid_t) NGX_CONF_UNSET_UINT; + + if (ngx_array_init(&ccf->env, cycle->pool, 1, sizeof(ngx_str_t)) + != NGX_OK) + { + return NULL; + } + + return ccf; +} + + +static char * +ngx_core_module_init_conf(ngx_cycle_t *cycle, void *conf) +{ + ngx_core_conf_t *ccf = conf; + + ngx_conf_init_value(ccf->daemon, 1); + ngx_conf_init_value(ccf->master, 1); + ngx_conf_init_msec_value(ccf->timer_resolution, 0); + ngx_conf_init_msec_value(ccf->shutdown_timeout, 0); + + ngx_conf_init_value(ccf->worker_processes, 1); + ngx_conf_init_value(ccf->debug_points, 0); + +#if (NGX_HAVE_CPU_AFFINITY) + + if (!ccf->cpu_affinity_auto + && ccf->cpu_affinity_n + && ccf->cpu_affinity_n != 1 + && ccf->cpu_affinity_n != (ngx_uint_t) ccf->worker_processes) + { + ngx_log_error(NGX_LOG_WARN, cycle->log, 0, + "the number of \"worker_processes\" is not equal to " + "the number of \"worker_cpu_affinity\" masks, " + "using last mask for remaining worker processes"); + } + +#endif + + + if (ccf->pid.len == 0) { + ngx_str_set(&ccf->pid, NGX_PID_PATH); + } + + if (ngx_conf_full_name(cycle, &ccf->pid, 0) != NGX_OK) { + return NGX_CONF_ERROR; + } + + ccf->oldpid.len = ccf->pid.len + sizeof(NGX_OLDPID_EXT); + + ccf->oldpid.data = ngx_pnalloc(cycle->pool, ccf->oldpid.len); + if (ccf->oldpid.data == NULL) { + return NGX_CONF_ERROR; + } + + ngx_memcpy(ngx_cpymem(ccf->oldpid.data, ccf->pid.data, ccf->pid.len), + NGX_OLDPID_EXT, sizeof(NGX_OLDPID_EXT)); + + +#if !(NGX_WIN32) + + if (ccf->user == (uid_t) NGX_CONF_UNSET_UINT && geteuid() == 0) { + struct group *grp; + struct passwd *pwd; + + ngx_set_errno(0); + pwd = getpwnam(NGX_USER); + if (pwd == NULL) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, + "getpwnam(\"" NGX_USER "\") failed"); + return NGX_CONF_ERROR; + } + + ccf->username = NGX_USER; + ccf->user = pwd->pw_uid; + + ngx_set_errno(0); + grp = getgrnam(NGX_GROUP); + if (grp == NULL) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, + "getgrnam(\"" NGX_GROUP "\") failed"); + return NGX_CONF_ERROR; + } + + ccf->group = grp->gr_gid; + } + + + if (ccf->lock_file.len == 0) { + ngx_str_set(&ccf->lock_file, NGX_LOCK_PATH); + } + + if (ngx_conf_full_name(cycle, &ccf->lock_file, 0) != NGX_OK) { + return NGX_CONF_ERROR; + } + + { + ngx_str_t lock_file; + + lock_file = cycle->old_cycle->lock_file; + + if (lock_file.len) { + lock_file.len--; + + if (ccf->lock_file.len != lock_file.len + || ngx_strncmp(ccf->lock_file.data, lock_file.data, lock_file.len) + != 0) + { + ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, + "\"lock_file\" could not be changed, ignored"); + } + + cycle->lock_file.len = lock_file.len + 1; + lock_file.len += sizeof(".accept"); + + cycle->lock_file.data = ngx_pstrdup(cycle->pool, &lock_file); + if (cycle->lock_file.data == NULL) { + return NGX_CONF_ERROR; + } + + } else { + cycle->lock_file.len = ccf->lock_file.len + 1; + cycle->lock_file.data = ngx_pnalloc(cycle->pool, + ccf->lock_file.len + sizeof(".accept")); + if (cycle->lock_file.data == NULL) { + return NGX_CONF_ERROR; + } + + ngx_memcpy(ngx_cpymem(cycle->lock_file.data, ccf->lock_file.data, + ccf->lock_file.len), + ".accept", sizeof(".accept")); + } + } + +#endif + + return NGX_CONF_OK; +} + + +static char * +ngx_set_user(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ +#if (NGX_WIN32) + + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "\"user\" is not supported, ignored"); + + return NGX_CONF_OK; + +#else + + ngx_core_conf_t *ccf = conf; + + char *group; + struct passwd *pwd; + struct group *grp; + ngx_str_t *value; + + if (ccf->user != (uid_t) NGX_CONF_UNSET_UINT) { + return "is duplicate"; + } + + if (geteuid() != 0) { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "the \"user\" directive makes sense only " + "if the master process runs " + "with super-user privileges, ignored"); + return NGX_CONF_OK; + } + + value = cf->args->elts; + + ccf->username = (char *) value[1].data; + + ngx_set_errno(0); + pwd = getpwnam((const char *) value[1].data); + if (pwd == NULL) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno, + "getpwnam(\"%s\") failed", value[1].data); + return NGX_CONF_ERROR; + } + + ccf->user = pwd->pw_uid; + + group = (char *) ((cf->args->nelts == 2) ? value[1].data : value[2].data); + + ngx_set_errno(0); + grp = getgrnam(group); + if (grp == NULL) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno, + "getgrnam(\"%s\") failed", group); + return NGX_CONF_ERROR; + } + + ccf->group = grp->gr_gid; + + return NGX_CONF_OK; + +#endif +} + + +static char * +ngx_set_env(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_core_conf_t *ccf = conf; + + ngx_str_t *value, *var; + ngx_uint_t i; + + var = ngx_array_push(&ccf->env); + if (var == NULL) { + return NGX_CONF_ERROR; + } + + value = cf->args->elts; + *var = value[1]; + + for (i = 0; i < value[1].len; i++) { + + if (value[1].data[i] == '=') { + + var->len = i; + + return NGX_CONF_OK; + } + } + + return NGX_CONF_OK; +} + + +static char * +ngx_set_priority(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_core_conf_t *ccf = conf; + + ngx_str_t *value; + ngx_uint_t n, minus; + + if (ccf->priority != 0) { + return "is duplicate"; + } + + value = cf->args->elts; + + if (value[1].data[0] == '-') { + n = 1; + minus = 1; + + } else if (value[1].data[0] == '+') { + n = 1; + minus = 0; + + } else { + n = 0; + minus = 0; + } + + ccf->priority = ngx_atoi(&value[1].data[n], value[1].len - n); + if (ccf->priority == NGX_ERROR) { + return "invalid number"; + } + + if (minus) { + ccf->priority = -ccf->priority; + } + + return NGX_CONF_OK; +} + + +static char * +ngx_set_cpu_affinity(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ +#if (NGX_HAVE_CPU_AFFINITY) + ngx_core_conf_t *ccf = conf; + + u_char ch, *p; + ngx_str_t *value; + ngx_uint_t i, n; + ngx_cpuset_t *mask; + + if (ccf->cpu_affinity) { + return "is duplicate"; + } + + mask = ngx_palloc(cf->pool, (cf->args->nelts - 1) * sizeof(ngx_cpuset_t)); + if (mask == NULL) { + return NGX_CONF_ERROR; + } + + ccf->cpu_affinity_n = cf->args->nelts - 1; + ccf->cpu_affinity = mask; + + value = cf->args->elts; + + if (ngx_strcmp(value[1].data, "auto") == 0) { + + if (cf->args->nelts > 3) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid number of arguments in " + "\"worker_cpu_affinity\" directive"); + return NGX_CONF_ERROR; + } + + ccf->cpu_affinity_auto = 1; + + CPU_ZERO(&mask[0]); + for (i = 0; i < (ngx_uint_t) ngx_min(ngx_ncpu, CPU_SETSIZE); i++) { + CPU_SET(i, &mask[0]); + } + + n = 2; + + } else { + n = 1; + } + + for ( /* void */ ; n < cf->args->nelts; n++) { + + if (value[n].len > CPU_SETSIZE) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "\"worker_cpu_affinity\" supports up to %d CPUs only", + CPU_SETSIZE); + return NGX_CONF_ERROR; + } + + i = 0; + CPU_ZERO(&mask[n - 1]); + + for (p = value[n].data + value[n].len - 1; + p >= value[n].data; + p--) + { + ch = *p; + + if (ch == ' ') { + continue; + } + + i++; + + if (ch == '0') { + continue; + } + + if (ch == '1') { + CPU_SET(i - 1, &mask[n - 1]); + continue; + } + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid character \"%c\" in \"worker_cpu_affinity\"", + ch); + return NGX_CONF_ERROR; + } + } + +#else + + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "\"worker_cpu_affinity\" is not supported " + "on this platform, ignored"); +#endif + + return NGX_CONF_OK; +} + + +ngx_cpuset_t * +ngx_get_cpu_affinity(ngx_uint_t n) +{ +#if (NGX_HAVE_CPU_AFFINITY) + ngx_uint_t i, j; + ngx_cpuset_t *mask; + ngx_core_conf_t *ccf; + + static ngx_cpuset_t result; + + ccf = (ngx_core_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx, + ngx_core_module); + + if (ccf->cpu_affinity == NULL) { + return NULL; + } + + if (ccf->cpu_affinity_auto) { + mask = &ccf->cpu_affinity[ccf->cpu_affinity_n - 1]; + + for (i = 0, j = n; /* void */ ; i++) { + + if (CPU_ISSET(i % CPU_SETSIZE, mask) && j-- == 0) { + break; + } + + if (i == CPU_SETSIZE && j == n) { + /* empty mask */ + return NULL; + } + + /* void */ + } + + CPU_ZERO(&result); + CPU_SET(i % CPU_SETSIZE, &result); + + return &result; + } + + if (ccf->cpu_affinity_n > n) { + return &ccf->cpu_affinity[n]; + } + + return &ccf->cpu_affinity[ccf->cpu_affinity_n - 1]; + +#else + + return NULL; + +#endif +} + + +static char * +ngx_set_worker_processes(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_str_t *value; + ngx_core_conf_t *ccf; + + ccf = (ngx_core_conf_t *) conf; + + if (ccf->worker_processes != NGX_CONF_UNSET) { + return "is duplicate"; + } + + value = cf->args->elts; + + if (ngx_strcmp(value[1].data, "auto") == 0) { + ccf->worker_processes = ngx_ncpu; + return NGX_CONF_OK; + } + + ccf->worker_processes = ngx_atoi(value[1].data, value[1].len); + + if (ccf->worker_processes == NGX_ERROR) { + return "invalid value"; + } + + return NGX_CONF_OK; +} + + +static char * +ngx_load_module(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ +#if (NGX_HAVE_DLOPEN) + void *handle; + char **names, **order; + ngx_str_t *value, file; + ngx_uint_t i; + ngx_module_t *module, **modules; + ngx_pool_cleanup_t *cln; + + if (cf->cycle->modules_used) { + return "is specified too late"; + } + + value = cf->args->elts; + + file = value[1]; + + if (ngx_conf_full_name(cf->cycle, &file, 0) != NGX_OK) { + return NGX_CONF_ERROR; + } + + cln = ngx_pool_cleanup_add(cf->cycle->pool, 0); + if (cln == NULL) { + return NGX_CONF_ERROR; + } + + handle = ngx_dlopen(file.data); + if (handle == NULL) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + ngx_dlopen_n " \"%s\" failed (%s)", + file.data, ngx_dlerror()); + return NGX_CONF_ERROR; + } + + cln->handler = ngx_unload_module; + cln->data = handle; + + modules = ngx_dlsym(handle, "ngx_modules"); + if (modules == NULL) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + ngx_dlsym_n " \"%V\", \"%s\" failed (%s)", + &value[1], "ngx_modules", ngx_dlerror()); + return NGX_CONF_ERROR; + } + + names = ngx_dlsym(handle, "ngx_module_names"); + if (names == NULL) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + ngx_dlsym_n " \"%V\", \"%s\" failed (%s)", + &value[1], "ngx_module_names", ngx_dlerror()); + return NGX_CONF_ERROR; + } + + order = ngx_dlsym(handle, "ngx_module_order"); + + for (i = 0; modules[i]; i++) { + module = modules[i]; + module->name = names[i]; + + if (ngx_add_module(cf, &file, module, order) != NGX_OK) { + return NGX_CONF_ERROR; + } + + ngx_log_debug2(NGX_LOG_DEBUG_CORE, cf->log, 0, "module: %s i:%ui", + module->name, module->index); + } + + return NGX_CONF_OK; + +#else + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "\"load_module\" is not supported " + "on this platform"); + return NGX_CONF_ERROR; + +#endif +} + + +#if (NGX_HAVE_DLOPEN) + +static void +ngx_unload_module(void *data) +{ + void *handle = data; + + if (ngx_dlclose(handle) != 0) { + ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, + ngx_dlclose_n " failed (%s)", ngx_dlerror()); + } +} + +#endif diff --git a/app/nginx/src/core/nginx.h b/app/nginx/src/core/nginx.h new file mode 100644 index 0000000..5d3112f --- /dev/null +++ b/app/nginx/src/core/nginx.h @@ -0,0 +1,26 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGINX_H_INCLUDED_ +#define _NGINX_H_INCLUDED_ + + +#define nginx_version 1011013 +#define NGINX_VERSION "1.11.13" +#define NGINX_VER "nginx/" NGINX_VERSION + +#ifdef NGX_BUILD +#define NGINX_VER_BUILD NGINX_VER " (" NGX_BUILD ")" +#else +#define NGINX_VER_BUILD NGINX_VER +#endif + +#define NGINX_VAR "NGINX" +#define NGX_OLDPID_EXT ".oldbin" + + +#endif /* _NGINX_H_INCLUDED_ */ diff --git a/app/nginx/src/core/ngx_array.c b/app/nginx/src/core/ngx_array.c new file mode 100644 index 0000000..4ea226f --- /dev/null +++ b/app/nginx/src/core/ngx_array.c @@ -0,0 +1,141 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include + + +ngx_array_t * +ngx_array_create(ngx_pool_t *p, ngx_uint_t n, size_t size) +{ + ngx_array_t *a; + + a = ngx_palloc(p, sizeof(ngx_array_t)); + if (a == NULL) { + return NULL; + } + + if (ngx_array_init(a, p, n, size) != NGX_OK) { + return NULL; + } + + return a; +} + + +void +ngx_array_destroy(ngx_array_t *a) +{ + ngx_pool_t *p; + + p = a->pool; + + if ((u_char *) a->elts + a->size * a->nalloc == p->d.last) { + p->d.last -= a->size * a->nalloc; + } + + if ((u_char *) a + sizeof(ngx_array_t) == p->d.last) { + p->d.last = (u_char *) a; + } +} + + +void * +ngx_array_push(ngx_array_t *a) +{ + void *elt, *new; + size_t size; + ngx_pool_t *p; + + if (a->nelts == a->nalloc) { + + /* the array is full */ + + size = a->size * a->nalloc; + + p = a->pool; + + if ((u_char *) a->elts + size == p->d.last + && p->d.last + a->size <= p->d.end) + { + /* + * the array allocation is the last in the pool + * and there is space for new allocation + */ + + p->d.last += a->size; + a->nalloc++; + + } else { + /* allocate a new array */ + + new = ngx_palloc(p, 2 * size); + if (new == NULL) { + return NULL; + } + + ngx_memcpy(new, a->elts, size); + a->elts = new; + a->nalloc *= 2; + } + } + + elt = (u_char *) a->elts + a->size * a->nelts; + a->nelts++; + + return elt; +} + + +void * +ngx_array_push_n(ngx_array_t *a, ngx_uint_t n) +{ + void *elt, *new; + size_t size; + ngx_uint_t nalloc; + ngx_pool_t *p; + + size = n * a->size; + + if (a->nelts + n > a->nalloc) { + + /* the array is full */ + + p = a->pool; + + if ((u_char *) a->elts + a->size * a->nalloc == p->d.last + && p->d.last + size <= p->d.end) + { + /* + * the array allocation is the last in the pool + * and there is space for new allocation + */ + + p->d.last += size; + a->nalloc += n; + + } else { + /* allocate a new array */ + + nalloc = 2 * ((n >= a->nalloc) ? n : a->nalloc); + + new = ngx_palloc(p, nalloc * a->size); + if (new == NULL) { + return NULL; + } + + ngx_memcpy(new, a->elts, a->nelts * a->size); + a->elts = new; + a->nalloc = nalloc; + } + } + + elt = (u_char *) a->elts + a->size * a->nelts; + a->nelts += n; + + return elt; +} diff --git a/app/nginx/src/core/ngx_array.h b/app/nginx/src/core/ngx_array.h new file mode 100644 index 0000000..a0f2a74 --- /dev/null +++ b/app/nginx/src/core/ngx_array.h @@ -0,0 +1,53 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_ARRAY_H_INCLUDED_ +#define _NGX_ARRAY_H_INCLUDED_ + + +#include +#include + + +typedef struct { + void *elts; + ngx_uint_t nelts; + size_t size; + ngx_uint_t nalloc; + ngx_pool_t *pool; +} ngx_array_t; + + +ngx_array_t *ngx_array_create(ngx_pool_t *p, ngx_uint_t n, size_t size); +void ngx_array_destroy(ngx_array_t *a); +void *ngx_array_push(ngx_array_t *a); +void *ngx_array_push_n(ngx_array_t *a, ngx_uint_t n); + + +static ngx_inline ngx_int_t +ngx_array_init(ngx_array_t *array, ngx_pool_t *pool, ngx_uint_t n, size_t size) +{ + /* + * set "array->nelts" before "array->elts", otherwise MSVC thinks + * that "array->nelts" may be used without having been initialized + */ + + array->nelts = 0; + array->size = size; + array->nalloc = n; + array->pool = pool; + + array->elts = ngx_palloc(pool, n * size); + if (array->elts == NULL) { + return NGX_ERROR; + } + + return NGX_OK; +} + + +#endif /* _NGX_ARRAY_H_INCLUDED_ */ diff --git a/app/nginx/src/core/ngx_buf.c b/app/nginx/src/core/ngx_buf.c new file mode 100644 index 0000000..1862a06 --- /dev/null +++ b/app/nginx/src/core/ngx_buf.c @@ -0,0 +1,313 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include + + +ngx_buf_t * +ngx_create_temp_buf(ngx_pool_t *pool, size_t size) +{ + ngx_buf_t *b; + + b = ngx_calloc_buf(pool); + if (b == NULL) { + return NULL; + } + + b->start = ngx_palloc(pool, size); + if (b->start == NULL) { + return NULL; + } + + /* + * set by ngx_calloc_buf(): + * + * b->file_pos = 0; + * b->file_last = 0; + * b->file = NULL; + * b->shadow = NULL; + * b->tag = 0; + * and flags + */ + + b->pos = b->start; + b->last = b->start; + b->end = b->last + size; + b->temporary = 1; + + return b; +} + + +ngx_chain_t * +ngx_alloc_chain_link(ngx_pool_t *pool) +{ + ngx_chain_t *cl; + + cl = pool->chain; + + if (cl) { + pool->chain = cl->next; + return cl; + } + + cl = ngx_palloc(pool, sizeof(ngx_chain_t)); + if (cl == NULL) { + return NULL; + } + + return cl; +} + + +ngx_chain_t * +ngx_create_chain_of_bufs(ngx_pool_t *pool, ngx_bufs_t *bufs) +{ + u_char *p; + ngx_int_t i; + ngx_buf_t *b; + ngx_chain_t *chain, *cl, **ll; + + p = ngx_palloc(pool, bufs->num * bufs->size); + if (p == NULL) { + return NULL; + } + + ll = &chain; + + for (i = 0; i < bufs->num; i++) { + + b = ngx_calloc_buf(pool); + if (b == NULL) { + return NULL; + } + + /* + * set by ngx_calloc_buf(): + * + * b->file_pos = 0; + * b->file_last = 0; + * b->file = NULL; + * b->shadow = NULL; + * b->tag = 0; + * and flags + * + */ + + b->pos = p; + b->last = p; + b->temporary = 1; + + b->start = p; + p += bufs->size; + b->end = p; + + cl = ngx_alloc_chain_link(pool); + if (cl == NULL) { + return NULL; + } + + cl->buf = b; + *ll = cl; + ll = &cl->next; + } + + *ll = NULL; + + return chain; +} + + +ngx_int_t +ngx_chain_add_copy(ngx_pool_t *pool, ngx_chain_t **chain, ngx_chain_t *in) +{ + ngx_chain_t *cl, **ll; + + ll = chain; + + for (cl = *chain; cl; cl = cl->next) { + ll = &cl->next; + } + + while (in) { + cl = ngx_alloc_chain_link(pool); + if (cl == NULL) { + return NGX_ERROR; + } + + cl->buf = in->buf; + *ll = cl; + ll = &cl->next; + in = in->next; + } + + *ll = NULL; + + return NGX_OK; +} + + +ngx_chain_t * +ngx_chain_get_free_buf(ngx_pool_t *p, ngx_chain_t **free) +{ + ngx_chain_t *cl; + + if (*free) { + cl = *free; + *free = cl->next; + cl->next = NULL; + return cl; + } + + cl = ngx_alloc_chain_link(p); + if (cl == NULL) { + return NULL; + } + + cl->buf = ngx_calloc_buf(p); + if (cl->buf == NULL) { + return NULL; + } + + cl->next = NULL; + + return cl; +} + + +void +ngx_chain_update_chains(ngx_pool_t *p, ngx_chain_t **free, ngx_chain_t **busy, + ngx_chain_t **out, ngx_buf_tag_t tag) +{ + ngx_chain_t *cl; + + if (*out) { + if (*busy == NULL) { + *busy = *out; + + } else { + for (cl = *busy; cl->next; cl = cl->next) { /* void */ } + + cl->next = *out; + } + + *out = NULL; + } + + while (*busy) { + cl = *busy; + + if (ngx_buf_size(cl->buf) != 0) { + break; + } + + if (cl->buf->tag != tag) { + *busy = cl->next; + ngx_free_chain(p, cl); + continue; + } + + cl->buf->pos = cl->buf->start; + cl->buf->last = cl->buf->start; + + *busy = cl->next; + cl->next = *free; + *free = cl; + } +} + + +off_t +ngx_chain_coalesce_file(ngx_chain_t **in, off_t limit) +{ + off_t total, size, aligned, fprev; + ngx_fd_t fd; + ngx_chain_t *cl; + + total = 0; + + cl = *in; + fd = cl->buf->file->fd; + + do { + size = cl->buf->file_last - cl->buf->file_pos; + + if (size > limit - total) { + size = limit - total; + + aligned = (cl->buf->file_pos + size + ngx_pagesize - 1) + & ~((off_t) ngx_pagesize - 1); + + if (aligned <= cl->buf->file_last) { + size = aligned - cl->buf->file_pos; + } + + total += size; + break; + } + + total += size; + fprev = cl->buf->file_pos + size; + cl = cl->next; + + } while (cl + && cl->buf->in_file + && total < limit + && fd == cl->buf->file->fd + && fprev == cl->buf->file_pos); + + *in = cl; + + return total; +} + + +ngx_chain_t * +ngx_chain_update_sent(ngx_chain_t *in, off_t sent) +{ + off_t size; + + for ( /* void */ ; in; in = in->next) { + + if (ngx_buf_special(in->buf)) { + continue; + } + + if (sent == 0) { + break; + } + + size = ngx_buf_size(in->buf); + + if (sent >= size) { + sent -= size; + + if (ngx_buf_in_memory(in->buf)) { + in->buf->pos = in->buf->last; + } + + if (in->buf->in_file) { + in->buf->file_pos = in->buf->file_last; + } + + continue; + } + + if (ngx_buf_in_memory(in->buf)) { + in->buf->pos += (size_t) sent; + } + + if (in->buf->in_file) { + in->buf->file_pos += sent; + } + + break; + } + + return in; +} diff --git a/app/nginx/src/core/ngx_buf.h b/app/nginx/src/core/ngx_buf.h new file mode 100644 index 0000000..12781a7 --- /dev/null +++ b/app/nginx/src/core/ngx_buf.h @@ -0,0 +1,170 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_BUF_H_INCLUDED_ +#define _NGX_BUF_H_INCLUDED_ + + +#include +#include + + +typedef void * ngx_buf_tag_t; + +typedef struct ngx_buf_s ngx_buf_t; + +struct ngx_buf_s { + u_char *pos; + u_char *last; + off_t file_pos; + off_t file_last; + + u_char *start; /* start of buffer */ + u_char *end; /* end of buffer */ + ngx_buf_tag_t tag; + ngx_file_t *file; + ngx_buf_t *shadow; + + + /* the buf's content could be changed */ + unsigned temporary:1; + + /* + * the buf's content is in a memory cache or in a read only memory + * and must not be changed + */ + unsigned memory:1; + + /* the buf's content is mmap()ed and must not be changed */ + unsigned mmap:1; + + unsigned recycled:1; + unsigned in_file:1; + unsigned flush:1; + unsigned sync:1; + unsigned last_buf:1; + unsigned last_in_chain:1; + + unsigned last_shadow:1; + unsigned temp_file:1; + + /* STUB */ int num; +}; + + +struct ngx_chain_s { + ngx_buf_t *buf; + ngx_chain_t *next; +}; + + +typedef struct { + ngx_int_t num; + size_t size; +} ngx_bufs_t; + + +typedef struct ngx_output_chain_ctx_s ngx_output_chain_ctx_t; + +typedef ngx_int_t (*ngx_output_chain_filter_pt)(void *ctx, ngx_chain_t *in); + +typedef void (*ngx_output_chain_aio_pt)(ngx_output_chain_ctx_t *ctx, + ngx_file_t *file); + +struct ngx_output_chain_ctx_s { + ngx_buf_t *buf; + ngx_chain_t *in; + ngx_chain_t *free; + ngx_chain_t *busy; + + unsigned sendfile:1; + unsigned directio:1; + unsigned unaligned:1; + unsigned need_in_memory:1; + unsigned need_in_temp:1; + unsigned aio:1; + +#if (NGX_HAVE_FILE_AIO || NGX_COMPAT) + ngx_output_chain_aio_pt aio_handler; +#if (NGX_HAVE_AIO_SENDFILE || NGX_COMPAT) + ssize_t (*aio_preload)(ngx_buf_t *file); +#endif +#endif + +#if (NGX_THREADS || NGX_COMPAT) + ngx_int_t (*thread_handler)(ngx_thread_task_t *task, + ngx_file_t *file); + ngx_thread_task_t *thread_task; +#endif + + off_t alignment; + + ngx_pool_t *pool; + ngx_int_t allocated; + ngx_bufs_t bufs; + ngx_buf_tag_t tag; + + ngx_output_chain_filter_pt output_filter; + void *filter_ctx; +}; + + +typedef struct { + ngx_chain_t *out; + ngx_chain_t **last; + ngx_connection_t *connection; + ngx_pool_t *pool; + off_t limit; +} ngx_chain_writer_ctx_t; + + +#define NGX_CHAIN_ERROR (ngx_chain_t *) NGX_ERROR + + +#define ngx_buf_in_memory(b) (b->temporary || b->memory || b->mmap) +#define ngx_buf_in_memory_only(b) (ngx_buf_in_memory(b) && !b->in_file) + +#define ngx_buf_special(b) \ + ((b->flush || b->last_buf || b->sync) \ + && !ngx_buf_in_memory(b) && !b->in_file) + +#define ngx_buf_sync_only(b) \ + (b->sync \ + && !ngx_buf_in_memory(b) && !b->in_file && !b->flush && !b->last_buf) + +#define ngx_buf_size(b) \ + (ngx_buf_in_memory(b) ? (off_t) (b->last - b->pos): \ + (b->file_last - b->file_pos)) + +ngx_buf_t *ngx_create_temp_buf(ngx_pool_t *pool, size_t size); +ngx_chain_t *ngx_create_chain_of_bufs(ngx_pool_t *pool, ngx_bufs_t *bufs); + + +#define ngx_alloc_buf(pool) ngx_palloc(pool, sizeof(ngx_buf_t)) +#define ngx_calloc_buf(pool) ngx_pcalloc(pool, sizeof(ngx_buf_t)) + +ngx_chain_t *ngx_alloc_chain_link(ngx_pool_t *pool); +#define ngx_free_chain(pool, cl) \ + cl->next = pool->chain; \ + pool->chain = cl + + + +ngx_int_t ngx_output_chain(ngx_output_chain_ctx_t *ctx, ngx_chain_t *in); +ngx_int_t ngx_chain_writer(void *ctx, ngx_chain_t *in); + +ngx_int_t ngx_chain_add_copy(ngx_pool_t *pool, ngx_chain_t **chain, + ngx_chain_t *in); +ngx_chain_t *ngx_chain_get_free_buf(ngx_pool_t *p, ngx_chain_t **free); +void ngx_chain_update_chains(ngx_pool_t *p, ngx_chain_t **free, + ngx_chain_t **busy, ngx_chain_t **out, ngx_buf_tag_t tag); + +off_t ngx_chain_coalesce_file(ngx_chain_t **in, off_t limit); + +ngx_chain_t *ngx_chain_update_sent(ngx_chain_t *in, off_t sent); + +#endif /* _NGX_BUF_H_INCLUDED_ */ diff --git a/app/nginx/src/core/ngx_conf_file.c b/app/nginx/src/core/ngx_conf_file.c new file mode 100644 index 0000000..ce8c602 --- /dev/null +++ b/app/nginx/src/core/ngx_conf_file.c @@ -0,0 +1,1479 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include + +#define NGX_CONF_BUFFER 4096 + +static ngx_int_t ngx_conf_add_dump(ngx_conf_t *cf, ngx_str_t *filename); +static ngx_int_t ngx_conf_handler(ngx_conf_t *cf, ngx_int_t last); +static ngx_int_t ngx_conf_read_token(ngx_conf_t *cf); +static void ngx_conf_flush_files(ngx_cycle_t *cycle); + + +static ngx_command_t ngx_conf_commands[] = { + + { ngx_string("include"), + NGX_ANY_CONF|NGX_CONF_TAKE1, + ngx_conf_include, + 0, + 0, + NULL }, + + ngx_null_command +}; + + +ngx_module_t ngx_conf_module = { + NGX_MODULE_V1, + NULL, /* module context */ + ngx_conf_commands, /* module directives */ + NGX_CONF_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + ngx_conf_flush_files, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +/* The eight fixed arguments */ + +static ngx_uint_t argument_number[] = { + NGX_CONF_NOARGS, + NGX_CONF_TAKE1, + NGX_CONF_TAKE2, + NGX_CONF_TAKE3, + NGX_CONF_TAKE4, + NGX_CONF_TAKE5, + NGX_CONF_TAKE6, + NGX_CONF_TAKE7 +}; + + +char * +ngx_conf_param(ngx_conf_t *cf) +{ + char *rv; + ngx_str_t *param; + ngx_buf_t b; + ngx_conf_file_t conf_file; + + param = &cf->cycle->conf_param; + + if (param->len == 0) { + return NGX_CONF_OK; + } + + ngx_memzero(&conf_file, sizeof(ngx_conf_file_t)); + + ngx_memzero(&b, sizeof(ngx_buf_t)); + + b.start = param->data; + b.pos = param->data; + b.last = param->data + param->len; + b.end = b.last; + b.temporary = 1; + + conf_file.file.fd = NGX_INVALID_FILE; + conf_file.file.name.data = NULL; + conf_file.line = 0; + + cf->conf_file = &conf_file; + cf->conf_file->buffer = &b; + + rv = ngx_conf_parse(cf, NULL); + + cf->conf_file = NULL; + + return rv; +} + + +static ngx_int_t +ngx_conf_add_dump(ngx_conf_t *cf, ngx_str_t *filename) +{ + off_t size; + u_char *p; + uint32_t hash; + ngx_buf_t *buf; + ngx_str_node_t *sn; + ngx_conf_dump_t *cd; + + hash = ngx_crc32_long(filename->data, filename->len); + + sn = ngx_str_rbtree_lookup(&cf->cycle->config_dump_rbtree, filename, hash); + + if (sn) { + cf->conf_file->dump = NULL; + return NGX_OK; + } + + p = ngx_pstrdup(cf->cycle->pool, filename); + if (p == NULL) { + return NGX_ERROR; + } + + cd = ngx_array_push(&cf->cycle->config_dump); + if (cd == NULL) { + return NGX_ERROR; + } + + size = ngx_file_size(&cf->conf_file->file.info); + + buf = ngx_create_temp_buf(cf->cycle->pool, (size_t) size); + if (buf == NULL) { + return NGX_ERROR; + } + + cd->name.data = p; + cd->name.len = filename->len; + cd->buffer = buf; + + cf->conf_file->dump = buf; + + sn = ngx_palloc(cf->temp_pool, sizeof(ngx_str_node_t)); + if (sn == NULL) { + return NGX_ERROR; + } + + sn->node.key = hash; + sn->str = cd->name; + + ngx_rbtree_insert(&cf->cycle->config_dump_rbtree, &sn->node); + + return NGX_OK; +} + + +char * +ngx_conf_parse(ngx_conf_t *cf, ngx_str_t *filename) +{ + char *rv; + ngx_fd_t fd; + ngx_int_t rc; + ngx_buf_t buf; + ngx_conf_file_t *prev, conf_file; + enum { + parse_file = 0, + parse_block, + parse_param + } type; + +#if (NGX_SUPPRESS_WARN) + fd = NGX_INVALID_FILE; + prev = NULL; +#endif + + if (filename) { + + /* open configuration file */ + + fd = ngx_open_file(filename->data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0); + if (fd == NGX_INVALID_FILE) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno, + ngx_open_file_n " \"%s\" failed", + filename->data); + return NGX_CONF_ERROR; + } + + prev = cf->conf_file; + + cf->conf_file = &conf_file; + + if (ngx_fd_info(fd, &cf->conf_file->file.info) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_EMERG, cf->log, ngx_errno, + ngx_fd_info_n " \"%s\" failed", filename->data); + } + + cf->conf_file->buffer = &buf; + + buf.start = ngx_alloc(NGX_CONF_BUFFER, cf->log); + if (buf.start == NULL) { + goto failed; + } + + buf.pos = buf.start; + buf.last = buf.start; + buf.end = buf.last + NGX_CONF_BUFFER; + buf.temporary = 1; + + cf->conf_file->file.fd = fd; + cf->conf_file->file.name.len = filename->len; + cf->conf_file->file.name.data = filename->data; + cf->conf_file->file.offset = 0; + cf->conf_file->file.log = cf->log; + cf->conf_file->line = 1; + + type = parse_file; + + if (ngx_dump_config +#if (NGX_DEBUG) + || 1 +#endif + ) + { + if (ngx_conf_add_dump(cf, filename) != NGX_OK) { + goto failed; + } + + } else { + cf->conf_file->dump = NULL; + } + + } else if (cf->conf_file->file.fd != NGX_INVALID_FILE) { + + type = parse_block; + + } else { + type = parse_param; + } + + + for ( ;; ) { + rc = ngx_conf_read_token(cf); + + /* + * ngx_conf_read_token() may return + * + * NGX_ERROR there is error + * NGX_OK the token terminated by ";" was found + * NGX_CONF_BLOCK_START the token terminated by "{" was found + * NGX_CONF_BLOCK_DONE the "}" was found + * NGX_CONF_FILE_DONE the configuration file is done + */ + + if (rc == NGX_ERROR) { + goto done; + } + + if (rc == NGX_CONF_BLOCK_DONE) { + + if (type != parse_block) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "unexpected \"}\""); + goto failed; + } + + goto done; + } + + if (rc == NGX_CONF_FILE_DONE) { + + if (type == parse_block) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "unexpected end of file, expecting \"}\""); + goto failed; + } + + goto done; + } + + if (rc == NGX_CONF_BLOCK_START) { + + if (type == parse_param) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "block directives are not supported " + "in -g option"); + goto failed; + } + } + + /* rc == NGX_OK || rc == NGX_CONF_BLOCK_START */ + + if (cf->handler) { + + /* + * the custom handler, i.e., that is used in the http's + * "types { ... }" directive + */ + + if (rc == NGX_CONF_BLOCK_START) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "unexpected \"{\""); + goto failed; + } + + rv = (*cf->handler)(cf, NULL, cf->handler_conf); + if (rv == NGX_CONF_OK) { + continue; + } + + if (rv == NGX_CONF_ERROR) { + goto failed; + } + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, rv); + + goto failed; + } + + + rc = ngx_conf_handler(cf, rc); + + if (rc == NGX_ERROR) { + goto failed; + } + } + +failed: + + rc = NGX_ERROR; + +done: + + if (filename) { + if (cf->conf_file->buffer->start) { + ngx_free(cf->conf_file->buffer->start); + } + + if (ngx_close_file(fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno, + ngx_close_file_n " %s failed", + filename->data); + rc = NGX_ERROR; + } + + cf->conf_file = prev; + } + + if (rc == NGX_ERROR) { + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} + + +static ngx_int_t +ngx_conf_handler(ngx_conf_t *cf, ngx_int_t last) +{ + char *rv; + void *conf, **confp; + ngx_uint_t i, found; + ngx_str_t *name; + ngx_command_t *cmd; + + name = cf->args->elts; + + found = 0; + + for (i = 0; cf->cycle->modules[i]; i++) { + + cmd = cf->cycle->modules[i]->commands; + if (cmd == NULL) { + continue; + } + + for ( /* void */ ; cmd->name.len; cmd++) { + + if (name->len != cmd->name.len) { + continue; + } + + if (ngx_strcmp(name->data, cmd->name.data) != 0) { + continue; + } + + found = 1; + + if (cf->cycle->modules[i]->type != NGX_CONF_MODULE + && cf->cycle->modules[i]->type != cf->module_type) + { + continue; + } + + /* is the directive's location right ? */ + + if (!(cmd->type & cf->cmd_type)) { + continue; + } + + if (!(cmd->type & NGX_CONF_BLOCK) && last != NGX_OK) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "directive \"%s\" is not terminated by \";\"", + name->data); + return NGX_ERROR; + } + + if ((cmd->type & NGX_CONF_BLOCK) && last != NGX_CONF_BLOCK_START) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "directive \"%s\" has no opening \"{\"", + name->data); + return NGX_ERROR; + } + + /* is the directive's argument count right ? */ + + if (!(cmd->type & NGX_CONF_ANY)) { + + if (cmd->type & NGX_CONF_FLAG) { + + if (cf->args->nelts != 2) { + goto invalid; + } + + } else if (cmd->type & NGX_CONF_1MORE) { + + if (cf->args->nelts < 2) { + goto invalid; + } + + } else if (cmd->type & NGX_CONF_2MORE) { + + if (cf->args->nelts < 3) { + goto invalid; + } + + } else if (cf->args->nelts > NGX_CONF_MAX_ARGS) { + + goto invalid; + + } else if (!(cmd->type & argument_number[cf->args->nelts - 1])) + { + goto invalid; + } + } + + /* set up the directive's configuration context */ + + conf = NULL; + + if (cmd->type & NGX_DIRECT_CONF) { + conf = ((void **) cf->ctx)[cf->cycle->modules[i]->index]; + + } else if (cmd->type & NGX_MAIN_CONF) { + conf = &(((void **) cf->ctx)[cf->cycle->modules[i]->index]); + + } else if (cf->ctx) { + confp = *(void **) ((char *) cf->ctx + cmd->conf); + + if (confp) { + conf = confp[cf->cycle->modules[i]->ctx_index]; + } + } + + rv = cmd->set(cf, cmd, conf); + + if (rv == NGX_CONF_OK) { + return NGX_OK; + } + + if (rv == NGX_CONF_ERROR) { + return NGX_ERROR; + } + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "\"%s\" directive %s", name->data, rv); + + return NGX_ERROR; + } + } + + if (found) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "\"%s\" directive is not allowed here", name->data); + + return NGX_ERROR; + } + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "unknown directive \"%s\"", name->data); + + return NGX_ERROR; + +invalid: + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid number of arguments in \"%s\" directive", + name->data); + + return NGX_ERROR; +} + + +static ngx_int_t +ngx_conf_read_token(ngx_conf_t *cf) +{ + u_char *start, ch, *src, *dst; + off_t file_size; + size_t len; + ssize_t n, size; + ngx_uint_t found, need_space, last_space, sharp_comment, variable; + ngx_uint_t quoted, s_quoted, d_quoted, start_line; + ngx_str_t *word; + ngx_buf_t *b, *dump; + + found = 0; + need_space = 0; + last_space = 1; + sharp_comment = 0; + variable = 0; + quoted = 0; + s_quoted = 0; + d_quoted = 0; + + cf->args->nelts = 0; + b = cf->conf_file->buffer; + dump = cf->conf_file->dump; + start = b->pos; + start_line = cf->conf_file->line; + + file_size = ngx_file_size(&cf->conf_file->file.info); + + for ( ;; ) { + + if (b->pos >= b->last) { + + if (cf->conf_file->file.offset >= file_size) { + + if (cf->args->nelts > 0 || !last_space) { + + if (cf->conf_file->file.fd == NGX_INVALID_FILE) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "unexpected end of parameter, " + "expecting \";\""); + return NGX_ERROR; + } + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "unexpected end of file, " + "expecting \";\" or \"}\""); + return NGX_ERROR; + } + + return NGX_CONF_FILE_DONE; + } + + len = b->pos - start; + + if (len == NGX_CONF_BUFFER) { + cf->conf_file->line = start_line; + + if (d_quoted) { + ch = '"'; + + } else if (s_quoted) { + ch = '\''; + + } else { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "too long parameter \"%*s...\" started", + 10, start); + return NGX_ERROR; + } + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "too long parameter, probably " + "missing terminating \"%c\" character", ch); + return NGX_ERROR; + } + + if (len) { + ngx_memmove(b->start, start, len); + } + + size = (ssize_t) (file_size - cf->conf_file->file.offset); + + if (size > b->end - (b->start + len)) { + size = b->end - (b->start + len); + } + + n = ngx_read_file(&cf->conf_file->file, b->start + len, size, + cf->conf_file->file.offset); + + if (n == NGX_ERROR) { + return NGX_ERROR; + } + + if (n != size) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + ngx_read_file_n " returned " + "only %z bytes instead of %z", + n, size); + return NGX_ERROR; + } + + b->pos = b->start + len; + b->last = b->pos + n; + start = b->start; + + if (dump) { + dump->last = ngx_cpymem(dump->last, b->pos, size); + } + } + + ch = *b->pos++; + + if (ch == LF) { + cf->conf_file->line++; + + if (sharp_comment) { + sharp_comment = 0; + } + } + + if (sharp_comment) { + continue; + } + + if (quoted) { + quoted = 0; + continue; + } + + if (need_space) { + if (ch == ' ' || ch == '\t' || ch == CR || ch == LF) { + last_space = 1; + need_space = 0; + continue; + } + + if (ch == ';') { + return NGX_OK; + } + + if (ch == '{') { + return NGX_CONF_BLOCK_START; + } + + if (ch == ')') { + last_space = 1; + need_space = 0; + + } else { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "unexpected \"%c\"", ch); + return NGX_ERROR; + } + } + + if (last_space) { + if (ch == ' ' || ch == '\t' || ch == CR || ch == LF) { + continue; + } + + start = b->pos - 1; + start_line = cf->conf_file->line; + + switch (ch) { + + case ';': + case '{': + if (cf->args->nelts == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "unexpected \"%c\"", ch); + return NGX_ERROR; + } + + if (ch == '{') { + return NGX_CONF_BLOCK_START; + } + + return NGX_OK; + + case '}': + if (cf->args->nelts != 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "unexpected \"}\""); + return NGX_ERROR; + } + + return NGX_CONF_BLOCK_DONE; + + case '#': + sharp_comment = 1; + continue; + + case '\\': + quoted = 1; + last_space = 0; + continue; + + case '"': + start++; + d_quoted = 1; + last_space = 0; + continue; + + case '\'': + start++; + s_quoted = 1; + last_space = 0; + continue; + + default: + last_space = 0; + } + + } else { + if (ch == '{' && variable) { + continue; + } + + variable = 0; + + if (ch == '\\') { + quoted = 1; + continue; + } + + if (ch == '$') { + variable = 1; + continue; + } + + if (d_quoted) { + if (ch == '"') { + d_quoted = 0; + need_space = 1; + found = 1; + } + + } else if (s_quoted) { + if (ch == '\'') { + s_quoted = 0; + need_space = 1; + found = 1; + } + + } else if (ch == ' ' || ch == '\t' || ch == CR || ch == LF + || ch == ';' || ch == '{') + { + last_space = 1; + found = 1; + } + + if (found) { + word = ngx_array_push(cf->args); + if (word == NULL) { + return NGX_ERROR; + } + + word->data = ngx_pnalloc(cf->pool, b->pos - 1 - start + 1); + if (word->data == NULL) { + return NGX_ERROR; + } + + for (dst = word->data, src = start, len = 0; + src < b->pos - 1; + len++) + { + if (*src == '\\') { + switch (src[1]) { + case '"': + case '\'': + case '\\': + src++; + break; + + case 't': + *dst++ = '\t'; + src += 2; + continue; + + case 'r': + *dst++ = '\r'; + src += 2; + continue; + + case 'n': + *dst++ = '\n'; + src += 2; + continue; + } + + } + *dst++ = *src++; + } + *dst = '\0'; + word->len = len; + + if (ch == ';') { + return NGX_OK; + } + + if (ch == '{') { + return NGX_CONF_BLOCK_START; + } + + found = 0; + } + } + } +} + + +char * +ngx_conf_include(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + char *rv; + ngx_int_t n; + ngx_str_t *value, file, name; + ngx_glob_t gl; + + value = cf->args->elts; + file = value[1]; + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data); + + if (ngx_conf_full_name(cf->cycle, &file, 1) != NGX_OK) { + return NGX_CONF_ERROR; + } + + if (strpbrk((char *) file.data, "*?[") == NULL) { + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data); + + return ngx_conf_parse(cf, &file); + } + + ngx_memzero(&gl, sizeof(ngx_glob_t)); + + gl.pattern = file.data; + gl.log = cf->log; + gl.test = 1; + + if (ngx_open_glob(&gl) != NGX_OK) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno, + ngx_open_glob_n " \"%s\" failed", file.data); + return NGX_CONF_ERROR; + } + + rv = NGX_CONF_OK; + + for ( ;; ) { + n = ngx_read_glob(&gl, &name); + + if (n != NGX_OK) { + break; + } + + file.len = name.len++; + file.data = ngx_pstrdup(cf->pool, &name); + if (file.data == NULL) { + return NGX_CONF_ERROR; + } + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data); + + rv = ngx_conf_parse(cf, &file); + + if (rv != NGX_CONF_OK) { + break; + } + } + + ngx_close_glob(&gl); + + return rv; +} + + +ngx_int_t +ngx_conf_full_name(ngx_cycle_t *cycle, ngx_str_t *name, ngx_uint_t conf_prefix) +{ + ngx_str_t *prefix; + + prefix = conf_prefix ? &cycle->conf_prefix : &cycle->prefix; + + return ngx_get_full_name(cycle->pool, prefix, name); +} + + +ngx_open_file_t * +ngx_conf_open_file(ngx_cycle_t *cycle, ngx_str_t *name) +{ + ngx_str_t full; + ngx_uint_t i; + ngx_list_part_t *part; + ngx_open_file_t *file; + +#if (NGX_SUPPRESS_WARN) + ngx_str_null(&full); +#endif + + if (name->len) { + full = *name; + + if (ngx_conf_full_name(cycle, &full, 0) != NGX_OK) { + return NULL; + } + + part = &cycle->open_files.part; + file = part->elts; + + for (i = 0; /* void */ ; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + part = part->next; + file = part->elts; + i = 0; + } + + if (full.len != file[i].name.len) { + continue; + } + + if (ngx_strcmp(full.data, file[i].name.data) == 0) { + return &file[i]; + } + } + } + + file = ngx_list_push(&cycle->open_files); + if (file == NULL) { + return NULL; + } + + if (name->len) { + file->fd = NGX_INVALID_FILE; + file->name = full; + + } else { + file->fd = ngx_stderr; + file->name = *name; + } + + file->flush = NULL; + file->data = NULL; + + return file; +} + + +static void +ngx_conf_flush_files(ngx_cycle_t *cycle) +{ + ngx_uint_t i; + ngx_list_part_t *part; + ngx_open_file_t *file; + + ngx_log_debug0(NGX_LOG_DEBUG_CORE, cycle->log, 0, "flush files"); + + part = &cycle->open_files.part; + file = part->elts; + + for (i = 0; /* void */ ; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + part = part->next; + file = part->elts; + i = 0; + } + + if (file[i].flush) { + file[i].flush(&file[i], cycle->log); + } + } +} + + +void ngx_cdecl +ngx_conf_log_error(ngx_uint_t level, ngx_conf_t *cf, ngx_err_t err, + const char *fmt, ...) +{ + u_char errstr[NGX_MAX_CONF_ERRSTR], *p, *last; + va_list args; + + last = errstr + NGX_MAX_CONF_ERRSTR; + + va_start(args, fmt); + p = ngx_vslprintf(errstr, last, fmt, args); + va_end(args); + + if (err) { + p = ngx_log_errno(p, last, err); + } + + if (cf->conf_file == NULL) { + ngx_log_error(level, cf->log, 0, "%*s", p - errstr, errstr); + return; + } + + if (cf->conf_file->file.fd == NGX_INVALID_FILE) { + ngx_log_error(level, cf->log, 0, "%*s in command line", + p - errstr, errstr); + return; + } + + ngx_log_error(level, cf->log, 0, "%*s in %s:%ui", + p - errstr, errstr, + cf->conf_file->file.name.data, cf->conf_file->line); +} + + +char * +ngx_conf_set_flag_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + char *p = conf; + + ngx_str_t *value; + ngx_flag_t *fp; + ngx_conf_post_t *post; + + fp = (ngx_flag_t *) (p + cmd->offset); + + if (*fp != NGX_CONF_UNSET) { + return "is duplicate"; + } + + value = cf->args->elts; + + if (ngx_strcasecmp(value[1].data, (u_char *) "on") == 0) { + *fp = 1; + + } else if (ngx_strcasecmp(value[1].data, (u_char *) "off") == 0) { + *fp = 0; + + } else { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid value \"%s\" in \"%s\" directive, " + "it must be \"on\" or \"off\"", + value[1].data, cmd->name.data); + return NGX_CONF_ERROR; + } + + if (cmd->post) { + post = cmd->post; + return post->post_handler(cf, post, fp); + } + + return NGX_CONF_OK; +} + + +char * +ngx_conf_set_str_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + char *p = conf; + + ngx_str_t *field, *value; + ngx_conf_post_t *post; + + field = (ngx_str_t *) (p + cmd->offset); + + if (field->data) { + return "is duplicate"; + } + + value = cf->args->elts; + + *field = value[1]; + + if (cmd->post) { + post = cmd->post; + return post->post_handler(cf, post, field); + } + + return NGX_CONF_OK; +} + + +char * +ngx_conf_set_str_array_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + char *p = conf; + + ngx_str_t *value, *s; + ngx_array_t **a; + ngx_conf_post_t *post; + + a = (ngx_array_t **) (p + cmd->offset); + + if (*a == NGX_CONF_UNSET_PTR) { + *a = ngx_array_create(cf->pool, 4, sizeof(ngx_str_t)); + if (*a == NULL) { + return NGX_CONF_ERROR; + } + } + + s = ngx_array_push(*a); + if (s == NULL) { + return NGX_CONF_ERROR; + } + + value = cf->args->elts; + + *s = value[1]; + + if (cmd->post) { + post = cmd->post; + return post->post_handler(cf, post, s); + } + + return NGX_CONF_OK; +} + + +char * +ngx_conf_set_keyval_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + char *p = conf; + + ngx_str_t *value; + ngx_array_t **a; + ngx_keyval_t *kv; + ngx_conf_post_t *post; + + a = (ngx_array_t **) (p + cmd->offset); + + if (*a == NULL) { + *a = ngx_array_create(cf->pool, 4, sizeof(ngx_keyval_t)); + if (*a == NULL) { + return NGX_CONF_ERROR; + } + } + + kv = ngx_array_push(*a); + if (kv == NULL) { + return NGX_CONF_ERROR; + } + + value = cf->args->elts; + + kv->key = value[1]; + kv->value = value[2]; + + if (cmd->post) { + post = cmd->post; + return post->post_handler(cf, post, kv); + } + + return NGX_CONF_OK; +} + + +char * +ngx_conf_set_num_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + char *p = conf; + + ngx_int_t *np; + ngx_str_t *value; + ngx_conf_post_t *post; + + + np = (ngx_int_t *) (p + cmd->offset); + + if (*np != NGX_CONF_UNSET) { + return "is duplicate"; + } + + value = cf->args->elts; + *np = ngx_atoi(value[1].data, value[1].len); + if (*np == NGX_ERROR) { + return "invalid number"; + } + + if (cmd->post) { + post = cmd->post; + return post->post_handler(cf, post, np); + } + + return NGX_CONF_OK; +} + + +char * +ngx_conf_set_size_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + char *p = conf; + + size_t *sp; + ngx_str_t *value; + ngx_conf_post_t *post; + + + sp = (size_t *) (p + cmd->offset); + if (*sp != NGX_CONF_UNSET_SIZE) { + return "is duplicate"; + } + + value = cf->args->elts; + + *sp = ngx_parse_size(&value[1]); + if (*sp == (size_t) NGX_ERROR) { + return "invalid value"; + } + + if (cmd->post) { + post = cmd->post; + return post->post_handler(cf, post, sp); + } + + return NGX_CONF_OK; +} + + +char * +ngx_conf_set_off_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + char *p = conf; + + off_t *op; + ngx_str_t *value; + ngx_conf_post_t *post; + + + op = (off_t *) (p + cmd->offset); + if (*op != NGX_CONF_UNSET) { + return "is duplicate"; + } + + value = cf->args->elts; + + *op = ngx_parse_offset(&value[1]); + if (*op == (off_t) NGX_ERROR) { + return "invalid value"; + } + + if (cmd->post) { + post = cmd->post; + return post->post_handler(cf, post, op); + } + + return NGX_CONF_OK; +} + + +char * +ngx_conf_set_msec_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + char *p = conf; + + ngx_msec_t *msp; + ngx_str_t *value; + ngx_conf_post_t *post; + + + msp = (ngx_msec_t *) (p + cmd->offset); + if (*msp != NGX_CONF_UNSET_MSEC) { + return "is duplicate"; + } + + value = cf->args->elts; + + *msp = ngx_parse_time(&value[1], 0); + if (*msp == (ngx_msec_t) NGX_ERROR) { + return "invalid value"; + } + + if (cmd->post) { + post = cmd->post; + return post->post_handler(cf, post, msp); + } + + return NGX_CONF_OK; +} + + +char * +ngx_conf_set_sec_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + char *p = conf; + + time_t *sp; + ngx_str_t *value; + ngx_conf_post_t *post; + + + sp = (time_t *) (p + cmd->offset); + if (*sp != NGX_CONF_UNSET) { + return "is duplicate"; + } + + value = cf->args->elts; + + *sp = ngx_parse_time(&value[1], 1); + if (*sp == (time_t) NGX_ERROR) { + return "invalid value"; + } + + if (cmd->post) { + post = cmd->post; + return post->post_handler(cf, post, sp); + } + + return NGX_CONF_OK; +} + + +char * +ngx_conf_set_bufs_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + char *p = conf; + + ngx_str_t *value; + ngx_bufs_t *bufs; + + + bufs = (ngx_bufs_t *) (p + cmd->offset); + if (bufs->num) { + return "is duplicate"; + } + + value = cf->args->elts; + + bufs->num = ngx_atoi(value[1].data, value[1].len); + if (bufs->num == NGX_ERROR || bufs->num == 0) { + return "invalid value"; + } + + bufs->size = ngx_parse_size(&value[2]); + if (bufs->size == (size_t) NGX_ERROR || bufs->size == 0) { + return "invalid value"; + } + + return NGX_CONF_OK; +} + + +char * +ngx_conf_set_enum_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + char *p = conf; + + ngx_uint_t *np, i; + ngx_str_t *value; + ngx_conf_enum_t *e; + + np = (ngx_uint_t *) (p + cmd->offset); + + if (*np != NGX_CONF_UNSET_UINT) { + return "is duplicate"; + } + + value = cf->args->elts; + e = cmd->post; + + for (i = 0; e[i].name.len != 0; i++) { + if (e[i].name.len != value[1].len + || ngx_strcasecmp(e[i].name.data, value[1].data) != 0) + { + continue; + } + + *np = e[i].value; + + return NGX_CONF_OK; + } + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid value \"%s\"", value[1].data); + + return NGX_CONF_ERROR; +} + + +char * +ngx_conf_set_bitmask_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + char *p = conf; + + ngx_uint_t *np, i, m; + ngx_str_t *value; + ngx_conf_bitmask_t *mask; + + + np = (ngx_uint_t *) (p + cmd->offset); + value = cf->args->elts; + mask = cmd->post; + + for (i = 1; i < cf->args->nelts; i++) { + for (m = 0; mask[m].name.len != 0; m++) { + + if (mask[m].name.len != value[i].len + || ngx_strcasecmp(mask[m].name.data, value[i].data) != 0) + { + continue; + } + + if (*np & mask[m].mask) { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "duplicate value \"%s\"", value[i].data); + + } else { + *np |= mask[m].mask; + } + + break; + } + + if (mask[m].name.len == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid value \"%s\"", value[i].data); + + return NGX_CONF_ERROR; + } + } + + return NGX_CONF_OK; +} + + +#if 0 + +char * +ngx_conf_unsupported(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + return "unsupported on this platform"; +} + +#endif + + +char * +ngx_conf_deprecated(ngx_conf_t *cf, void *post, void *data) +{ + ngx_conf_deprecated_t *d = post; + + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "the \"%s\" directive is deprecated, " + "use the \"%s\" directive instead", + d->old_name, d->new_name); + + return NGX_CONF_OK; +} + + +char * +ngx_conf_check_num_bounds(ngx_conf_t *cf, void *post, void *data) +{ + ngx_conf_num_bounds_t *bounds = post; + ngx_int_t *np = data; + + if (bounds->high == -1) { + if (*np >= bounds->low) { + return NGX_CONF_OK; + } + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "value must be equal to or greater than %i", + bounds->low); + + return NGX_CONF_ERROR; + } + + if (*np >= bounds->low && *np <= bounds->high) { + return NGX_CONF_OK; + } + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "value must be between %i and %i", + bounds->low, bounds->high); + + return NGX_CONF_ERROR; +} diff --git a/app/nginx/src/core/ngx_conf_file.h b/app/nginx/src/core/ngx_conf_file.h new file mode 100644 index 0000000..213611f --- /dev/null +++ b/app/nginx/src/core/ngx_conf_file.h @@ -0,0 +1,295 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_CONF_FILE_H_INCLUDED_ +#define _NGX_CONF_FILE_H_INCLUDED_ + + +#include +#include + + +/* + * AAAA number of arguments + * FF command flags + * TT command type, i.e. HTTP "location" or "server" command + */ + +#define NGX_CONF_NOARGS 0x00000001 +#define NGX_CONF_TAKE1 0x00000002 +#define NGX_CONF_TAKE2 0x00000004 +#define NGX_CONF_TAKE3 0x00000008 +#define NGX_CONF_TAKE4 0x00000010 +#define NGX_CONF_TAKE5 0x00000020 +#define NGX_CONF_TAKE6 0x00000040 +#define NGX_CONF_TAKE7 0x00000080 + +#define NGX_CONF_MAX_ARGS 8 + +#define NGX_CONF_TAKE12 (NGX_CONF_TAKE1|NGX_CONF_TAKE2) +#define NGX_CONF_TAKE13 (NGX_CONF_TAKE1|NGX_CONF_TAKE3) + +#define NGX_CONF_TAKE23 (NGX_CONF_TAKE2|NGX_CONF_TAKE3) + +#define NGX_CONF_TAKE123 (NGX_CONF_TAKE1|NGX_CONF_TAKE2|NGX_CONF_TAKE3) +#define NGX_CONF_TAKE1234 (NGX_CONF_TAKE1|NGX_CONF_TAKE2|NGX_CONF_TAKE3 \ + |NGX_CONF_TAKE4) + +#define NGX_CONF_ARGS_NUMBER 0x000000ff +#define NGX_CONF_BLOCK 0x00000100 +#define NGX_CONF_FLAG 0x00000200 +#define NGX_CONF_ANY 0x00000400 +#define NGX_CONF_1MORE 0x00000800 +#define NGX_CONF_2MORE 0x00001000 + +#define NGX_DIRECT_CONF 0x00010000 + +#define NGX_MAIN_CONF 0x01000000 +#define NGX_ANY_CONF 0x1F000000 + + + +#define NGX_CONF_UNSET -1 +#define NGX_CONF_UNSET_UINT (ngx_uint_t) -1 +#define NGX_CONF_UNSET_PTR (void *) -1 +#define NGX_CONF_UNSET_SIZE (size_t) -1 +#define NGX_CONF_UNSET_MSEC (ngx_msec_t) -1 + + +#define NGX_CONF_OK NULL +#define NGX_CONF_ERROR (void *) -1 + +#define NGX_CONF_BLOCK_START 1 +#define NGX_CONF_BLOCK_DONE 2 +#define NGX_CONF_FILE_DONE 3 + +#define NGX_CORE_MODULE 0x45524F43 /* "CORE" */ +#define NGX_CONF_MODULE 0x464E4F43 /* "CONF" */ + + +#define NGX_MAX_CONF_ERRSTR 1024 + + +struct ngx_command_s { + ngx_str_t name; + ngx_uint_t type; + char *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); + ngx_uint_t conf; + ngx_uint_t offset; + void *post; +}; + +#define ngx_null_command { ngx_null_string, 0, NULL, 0, 0, NULL } + + +struct ngx_open_file_s { + ngx_fd_t fd; + ngx_str_t name; + + void (*flush)(ngx_open_file_t *file, ngx_log_t *log); + void *data; +}; + + +typedef struct { + ngx_file_t file; + ngx_buf_t *buffer; + ngx_buf_t *dump; + ngx_uint_t line; +} ngx_conf_file_t; + + +typedef struct { + ngx_str_t name; + ngx_buf_t *buffer; +} ngx_conf_dump_t; + + +typedef char *(*ngx_conf_handler_pt)(ngx_conf_t *cf, + ngx_command_t *dummy, void *conf); + + +struct ngx_conf_s { + char *name; + ngx_array_t *args; + + ngx_cycle_t *cycle; + ngx_pool_t *pool; + ngx_pool_t *temp_pool; + ngx_conf_file_t *conf_file; + ngx_log_t *log; + + void *ctx; + ngx_uint_t module_type; + ngx_uint_t cmd_type; + + ngx_conf_handler_pt handler; + char *handler_conf; +}; + + +typedef char *(*ngx_conf_post_handler_pt) (ngx_conf_t *cf, + void *data, void *conf); + +typedef struct { + ngx_conf_post_handler_pt post_handler; +} ngx_conf_post_t; + + +typedef struct { + ngx_conf_post_handler_pt post_handler; + char *old_name; + char *new_name; +} ngx_conf_deprecated_t; + + +typedef struct { + ngx_conf_post_handler_pt post_handler; + ngx_int_t low; + ngx_int_t high; +} ngx_conf_num_bounds_t; + + +typedef struct { + ngx_str_t name; + ngx_uint_t value; +} ngx_conf_enum_t; + + +#define NGX_CONF_BITMASK_SET 1 + +typedef struct { + ngx_str_t name; + ngx_uint_t mask; +} ngx_conf_bitmask_t; + + + +char * ngx_conf_deprecated(ngx_conf_t *cf, void *post, void *data); +char *ngx_conf_check_num_bounds(ngx_conf_t *cf, void *post, void *data); + + +#define ngx_get_conf(conf_ctx, module) conf_ctx[module.index] + + + +#define ngx_conf_init_value(conf, default) \ + if (conf == NGX_CONF_UNSET) { \ + conf = default; \ + } + +#define ngx_conf_init_ptr_value(conf, default) \ + if (conf == NGX_CONF_UNSET_PTR) { \ + conf = default; \ + } + +#define ngx_conf_init_uint_value(conf, default) \ + if (conf == NGX_CONF_UNSET_UINT) { \ + conf = default; \ + } + +#define ngx_conf_init_size_value(conf, default) \ + if (conf == NGX_CONF_UNSET_SIZE) { \ + conf = default; \ + } + +#define ngx_conf_init_msec_value(conf, default) \ + if (conf == NGX_CONF_UNSET_MSEC) { \ + conf = default; \ + } + +#define ngx_conf_merge_value(conf, prev, default) \ + if (conf == NGX_CONF_UNSET) { \ + conf = (prev == NGX_CONF_UNSET) ? default : prev; \ + } + +#define ngx_conf_merge_ptr_value(conf, prev, default) \ + if (conf == NGX_CONF_UNSET_PTR) { \ + conf = (prev == NGX_CONF_UNSET_PTR) ? default : prev; \ + } + +#define ngx_conf_merge_uint_value(conf, prev, default) \ + if (conf == NGX_CONF_UNSET_UINT) { \ + conf = (prev == NGX_CONF_UNSET_UINT) ? default : prev; \ + } + +#define ngx_conf_merge_msec_value(conf, prev, default) \ + if (conf == NGX_CONF_UNSET_MSEC) { \ + conf = (prev == NGX_CONF_UNSET_MSEC) ? default : prev; \ + } + +#define ngx_conf_merge_sec_value(conf, prev, default) \ + if (conf == NGX_CONF_UNSET) { \ + conf = (prev == NGX_CONF_UNSET) ? default : prev; \ + } + +#define ngx_conf_merge_size_value(conf, prev, default) \ + if (conf == NGX_CONF_UNSET_SIZE) { \ + conf = (prev == NGX_CONF_UNSET_SIZE) ? default : prev; \ + } + +#define ngx_conf_merge_off_value(conf, prev, default) \ + if (conf == NGX_CONF_UNSET) { \ + conf = (prev == NGX_CONF_UNSET) ? default : prev; \ + } + +#define ngx_conf_merge_str_value(conf, prev, default) \ + if (conf.data == NULL) { \ + if (prev.data) { \ + conf.len = prev.len; \ + conf.data = prev.data; \ + } else { \ + conf.len = sizeof(default) - 1; \ + conf.data = (u_char *) default; \ + } \ + } + +#define ngx_conf_merge_bufs_value(conf, prev, default_num, default_size) \ + if (conf.num == 0) { \ + if (prev.num) { \ + conf.num = prev.num; \ + conf.size = prev.size; \ + } else { \ + conf.num = default_num; \ + conf.size = default_size; \ + } \ + } + +#define ngx_conf_merge_bitmask_value(conf, prev, default) \ + if (conf == 0) { \ + conf = (prev == 0) ? default : prev; \ + } + + +char *ngx_conf_param(ngx_conf_t *cf); +char *ngx_conf_parse(ngx_conf_t *cf, ngx_str_t *filename); +char *ngx_conf_include(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); + + +ngx_int_t ngx_conf_full_name(ngx_cycle_t *cycle, ngx_str_t *name, + ngx_uint_t conf_prefix); +ngx_open_file_t *ngx_conf_open_file(ngx_cycle_t *cycle, ngx_str_t *name); +void ngx_cdecl ngx_conf_log_error(ngx_uint_t level, ngx_conf_t *cf, + ngx_err_t err, const char *fmt, ...); + + +char *ngx_conf_set_flag_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +char *ngx_conf_set_str_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +char *ngx_conf_set_str_array_slot(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +char *ngx_conf_set_keyval_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +char *ngx_conf_set_num_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +char *ngx_conf_set_size_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +char *ngx_conf_set_off_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +char *ngx_conf_set_msec_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +char *ngx_conf_set_sec_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +char *ngx_conf_set_bufs_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +char *ngx_conf_set_enum_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +char *ngx_conf_set_bitmask_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); + + +#endif /* _NGX_CONF_FILE_H_INCLUDED_ */ diff --git a/app/nginx/src/core/ngx_config.h b/app/nginx/src/core/ngx_config.h new file mode 100644 index 0000000..1861be6 --- /dev/null +++ b/app/nginx/src/core/ngx_config.h @@ -0,0 +1,145 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_CONFIG_H_INCLUDED_ +#define _NGX_CONFIG_H_INCLUDED_ + + +#include + + +#if defined __DragonFly__ && !defined __FreeBSD__ +#define __FreeBSD__ 4 +#define __FreeBSD_version 480101 +#endif + + +#if (NGX_FREEBSD) +#include + + +#elif (NGX_LINUX) +#include + + +#elif (NGX_SOLARIS) +#include + + +#elif (NGX_DARWIN) +#include + + +#elif (NGX_WIN32) +#include + + +#else /* POSIX */ +#include + +#endif + + +#ifndef NGX_HAVE_SO_SNDLOWAT +#define NGX_HAVE_SO_SNDLOWAT 1 +#endif + + +#if !(NGX_WIN32) + +#define ngx_signal_helper(n) SIG##n +#define ngx_signal_value(n) ngx_signal_helper(n) + +#define ngx_random random + +/* TODO: #ifndef */ +#define NGX_SHUTDOWN_SIGNAL QUIT +#define NGX_TERMINATE_SIGNAL TERM +#define NGX_NOACCEPT_SIGNAL WINCH +#define NGX_RECONFIGURE_SIGNAL HUP + +#if (NGX_LINUXTHREADS) +#define NGX_REOPEN_SIGNAL INFO +#define NGX_CHANGEBIN_SIGNAL XCPU +#else +#define NGX_REOPEN_SIGNAL USR1 +#define NGX_CHANGEBIN_SIGNAL USR2 +#endif + +#define ngx_cdecl +#define ngx_libc_cdecl + +#endif + +typedef intptr_t ngx_int_t; +typedef uintptr_t ngx_uint_t; +typedef intptr_t ngx_flag_t; + + +#define NGX_INT32_LEN (sizeof("-2147483648") - 1) +#define NGX_INT64_LEN (sizeof("-9223372036854775808") - 1) + +#if (NGX_PTR_SIZE == 4) +#define NGX_INT_T_LEN NGX_INT32_LEN +#define NGX_MAX_INT_T_VALUE 2147483647 + +#else +#define NGX_INT_T_LEN NGX_INT64_LEN +#define NGX_MAX_INT_T_VALUE 9223372036854775807 +#endif + + +#ifndef NGX_ALIGNMENT +#define NGX_ALIGNMENT sizeof(unsigned long) /* platform word */ +#endif + +#define ngx_align(d, a) (((d) + (a - 1)) & ~(a - 1)) +#define ngx_align_ptr(p, a) \ + (u_char *) (((uintptr_t) (p) + ((uintptr_t) a - 1)) & ~((uintptr_t) a - 1)) + + +#define ngx_abort abort + + +/* TODO: platform specific: array[NGX_INVALID_ARRAY_INDEX] must cause SIGSEGV */ +#define NGX_INVALID_ARRAY_INDEX 0x80000000 + + +/* TODO: auto_conf: ngx_inline inline __inline __inline__ */ +#ifndef ngx_inline +#define ngx_inline inline +#endif + +#ifndef INADDR_NONE /* Solaris */ +#define INADDR_NONE ((unsigned int) -1) +#endif + +#ifdef MAXHOSTNAMELEN +#define NGX_MAXHOSTNAMELEN MAXHOSTNAMELEN +#else +#define NGX_MAXHOSTNAMELEN 256 +#endif + + +#define NGX_MAX_UINT32_VALUE (uint32_t) 0xffffffff +#define NGX_MAX_INT32_VALUE (uint32_t) 0x7fffffff + + +#if (NGX_COMPAT) + +#define NGX_COMPAT_BEGIN(slots) uint64_t spare[slots]; +#define NGX_COMPAT_END + +#else + +#define NGX_COMPAT_BEGIN(slots) +#define NGX_COMPAT_END + +#endif + + +#endif /* _NGX_CONFIG_H_INCLUDED_ */ diff --git a/app/nginx/src/core/ngx_connection.c b/app/nginx/src/core/ngx_connection.c new file mode 100644 index 0000000..2af2876 --- /dev/null +++ b/app/nginx/src/core/ngx_connection.c @@ -0,0 +1,1404 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +ngx_os_io_t ngx_io; + + +static void ngx_drain_connections(ngx_cycle_t *cycle); + + +ngx_listening_t * +ngx_create_listening(ngx_conf_t *cf, struct sockaddr *sockaddr, + socklen_t socklen) +{ + size_t len; + ngx_listening_t *ls; + struct sockaddr *sa; + u_char text[NGX_SOCKADDR_STRLEN]; + + ls = ngx_array_push(&cf->cycle->listening); + if (ls == NULL) { + return NULL; + } + + ngx_memzero(ls, sizeof(ngx_listening_t)); + + sa = ngx_palloc(cf->pool, socklen); + if (sa == NULL) { + return NULL; + } + + ngx_memcpy(sa, sockaddr, socklen); + + ls->sockaddr = sa; + ls->socklen = socklen; + + len = ngx_sock_ntop(sa, socklen, text, NGX_SOCKADDR_STRLEN, 1); + ls->addr_text.len = len; + + switch (ls->sockaddr->sa_family) { +#if (NGX_HAVE_INET6) + case AF_INET6: + ls->addr_text_max_len = NGX_INET6_ADDRSTRLEN; + break; +#endif +#if (NGX_HAVE_UNIX_DOMAIN) + case AF_UNIX: + ls->addr_text_max_len = NGX_UNIX_ADDRSTRLEN; + len++; + break; +#endif + case AF_INET: + ls->addr_text_max_len = NGX_INET_ADDRSTRLEN; + break; + default: + ls->addr_text_max_len = NGX_SOCKADDR_STRLEN; + break; + } + + ls->addr_text.data = ngx_pnalloc(cf->pool, len); + if (ls->addr_text.data == NULL) { + return NULL; + } + + ngx_memcpy(ls->addr_text.data, text, len); + + ls->fd = (ngx_socket_t) -1; + ls->type = SOCK_STREAM; + + ls->backlog = NGX_LISTEN_BACKLOG; + ls->rcvbuf = -1; + ls->sndbuf = -1; + +#if (NGX_HAVE_SETFIB) + ls->setfib = -1; +#endif + +#if (NGX_HAVE_TCP_FASTOPEN) + ls->fastopen = -1; +#endif + + return ls; +} + + +ngx_int_t +ngx_clone_listening(ngx_conf_t *cf, ngx_listening_t *ls) +{ +#if (NGX_HAVE_REUSEPORT) + + ngx_int_t n; + ngx_core_conf_t *ccf; + ngx_listening_t ols; + + if (!ls->reuseport) { + return NGX_OK; + } + + ols = *ls; + + ccf = (ngx_core_conf_t *) ngx_get_conf(cf->cycle->conf_ctx, + ngx_core_module); + + for (n = 1; n < ccf->worker_processes; n++) { + + /* create a socket for each worker process */ + + ls = ngx_array_push(&cf->cycle->listening); + if (ls == NULL) { + return NGX_ERROR; + } + + *ls = ols; + ls->worker = n; + } + +#endif + + return NGX_OK; +} + + +ngx_int_t +ngx_set_inherited_sockets(ngx_cycle_t *cycle) +{ + size_t len; + ngx_uint_t i; + ngx_listening_t *ls; + socklen_t olen; +#if (NGX_HAVE_DEFERRED_ACCEPT || NGX_HAVE_TCP_FASTOPEN) + ngx_err_t err; +#endif +#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER) + struct accept_filter_arg af; +#endif +#if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT) + int timeout; +#endif +#if (NGX_HAVE_REUSEPORT) + int reuseport; +#endif + + ls = cycle->listening.elts; + for (i = 0; i < cycle->listening.nelts; i++) { + + ls[i].sockaddr = ngx_palloc(cycle->pool, sizeof(ngx_sockaddr_t)); + if (ls[i].sockaddr == NULL) { + return NGX_ERROR; + } + + ls[i].socklen = sizeof(ngx_sockaddr_t); + if (getsockname(ls[i].fd, ls[i].sockaddr, &ls[i].socklen) == -1) { + ngx_log_error(NGX_LOG_CRIT, cycle->log, ngx_socket_errno, + "getsockname() of the inherited " + "socket #%d failed", ls[i].fd); + ls[i].ignore = 1; + continue; + } + + switch (ls[i].sockaddr->sa_family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + ls[i].addr_text_max_len = NGX_INET6_ADDRSTRLEN; + len = NGX_INET6_ADDRSTRLEN + sizeof("[]:65535") - 1; + break; +#endif + +#if (NGX_HAVE_UNIX_DOMAIN) + case AF_UNIX: + ls[i].addr_text_max_len = NGX_UNIX_ADDRSTRLEN; + len = NGX_UNIX_ADDRSTRLEN; + break; +#endif + + case AF_INET: + ls[i].addr_text_max_len = NGX_INET_ADDRSTRLEN; + len = NGX_INET_ADDRSTRLEN + sizeof(":65535") - 1; + break; + + default: + ngx_log_error(NGX_LOG_CRIT, cycle->log, ngx_socket_errno, + "the inherited socket #%d has " + "an unsupported protocol family", ls[i].fd); + ls[i].ignore = 1; + continue; + } + + ls[i].addr_text.data = ngx_pnalloc(cycle->pool, len); + if (ls[i].addr_text.data == NULL) { + return NGX_ERROR; + } + + len = ngx_sock_ntop(ls[i].sockaddr, ls[i].socklen, + ls[i].addr_text.data, len, 1); + if (len == 0) { + return NGX_ERROR; + } + + ls[i].addr_text.len = len; + + ls[i].backlog = NGX_LISTEN_BACKLOG; + + olen = sizeof(int); + + if (getsockopt(ls[i].fd, SOL_SOCKET, SO_TYPE, (void *) &ls[i].type, + &olen) + == -1) + { + ngx_log_error(NGX_LOG_CRIT, cycle->log, ngx_socket_errno, + "getsockopt(SO_TYPE) %V failed", &ls[i].addr_text); + ls[i].ignore = 1; + continue; + } + + olen = sizeof(int); + + if (getsockopt(ls[i].fd, SOL_SOCKET, SO_RCVBUF, (void *) &ls[i].rcvbuf, + &olen) + == -1) + { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno, + "getsockopt(SO_RCVBUF) %V failed, ignored", + &ls[i].addr_text); + + ls[i].rcvbuf = -1; + } + + olen = sizeof(int); + + if (getsockopt(ls[i].fd, SOL_SOCKET, SO_SNDBUF, (void *) &ls[i].sndbuf, + &olen) + == -1) + { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno, + "getsockopt(SO_SNDBUF) %V failed, ignored", + &ls[i].addr_text); + + ls[i].sndbuf = -1; + } + +#if 0 + /* SO_SETFIB is currently a set only option */ + +#if (NGX_HAVE_SETFIB) + + olen = sizeof(int); + + if (getsockopt(ls[i].fd, SOL_SOCKET, SO_SETFIB, + (void *) &ls[i].setfib, &olen) + == -1) + { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno, + "getsockopt(SO_SETFIB) %V failed, ignored", + &ls[i].addr_text); + + ls[i].setfib = -1; + } + +#endif +#endif + +#if (NGX_HAVE_REUSEPORT) + + reuseport = 0; + olen = sizeof(int); + + if (getsockopt(ls[i].fd, SOL_SOCKET, SO_REUSEPORT, + (void *) &reuseport, &olen) + == -1) + { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno, + "getsockopt(SO_REUSEPORT) %V failed, ignored", + &ls[i].addr_text); + + } else { + ls[i].reuseport = reuseport ? 1 : 0; + } + +#endif + + if (ls[i].type != SOCK_STREAM) { + continue; + } + +#if (NGX_HAVE_TCP_FASTOPEN) + + olen = sizeof(int); + + if (getsockopt(ls[i].fd, IPPROTO_TCP, TCP_FASTOPEN, + (void *) &ls[i].fastopen, &olen) + == -1) + { + err = ngx_socket_errno; + + if (err != NGX_EOPNOTSUPP && err != NGX_ENOPROTOOPT) { + ngx_log_error(NGX_LOG_NOTICE, cycle->log, err, + "getsockopt(TCP_FASTOPEN) %V failed, ignored", + &ls[i].addr_text); + } + + ls[i].fastopen = -1; + } + +#endif + +#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER) + + ngx_memzero(&af, sizeof(struct accept_filter_arg)); + olen = sizeof(struct accept_filter_arg); + + if (getsockopt(ls[i].fd, SOL_SOCKET, SO_ACCEPTFILTER, &af, &olen) + == -1) + { + err = ngx_socket_errno; + + if (err == NGX_EINVAL) { + continue; + } + + ngx_log_error(NGX_LOG_NOTICE, cycle->log, err, + "getsockopt(SO_ACCEPTFILTER) for %V failed, ignored", + &ls[i].addr_text); + continue; + } + + if (olen < sizeof(struct accept_filter_arg) || af.af_name[0] == '\0') { + continue; + } + + ls[i].accept_filter = ngx_palloc(cycle->pool, 16); + if (ls[i].accept_filter == NULL) { + return NGX_ERROR; + } + + (void) ngx_cpystrn((u_char *) ls[i].accept_filter, + (u_char *) af.af_name, 16); +#endif + +#if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT) + + timeout = 0; + olen = sizeof(int); + + if (getsockopt(ls[i].fd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &timeout, &olen) + == -1) + { + err = ngx_socket_errno; + + if (err == NGX_EOPNOTSUPP) { + continue; + } + + ngx_log_error(NGX_LOG_NOTICE, cycle->log, err, + "getsockopt(TCP_DEFER_ACCEPT) for %V failed, ignored", + &ls[i].addr_text); + continue; + } + + if (olen < sizeof(int) || timeout == 0) { + continue; + } + + ls[i].deferred_accept = 1; +#endif + } + + return NGX_OK; +} + + +ngx_int_t +ngx_open_listening_sockets(ngx_cycle_t *cycle) +{ + int reuseaddr; + ngx_uint_t i, tries, failed; + ngx_err_t err; + ngx_log_t *log; + ngx_socket_t s; + ngx_listening_t *ls; + + reuseaddr = 1; +#if (NGX_SUPPRESS_WARN) + failed = 0; +#endif + + log = cycle->log; + + /* TODO: configurable try number */ + + for (tries = 5; tries; tries--) { + failed = 0; + + /* for each listening socket */ + + ls = cycle->listening.elts; + for (i = 0; i < cycle->listening.nelts; i++) { + + if (ls[i].ignore) { + continue; + } + +#if (NGX_HAVE_REUSEPORT) + + if (ls[i].add_reuseport) { + + /* + * to allow transition from a socket without SO_REUSEPORT + * to multiple sockets with SO_REUSEPORT, we have to set + * SO_REUSEPORT on the old socket before opening new ones + */ + + int reuseport = 1; + + if (setsockopt(ls[i].fd, SOL_SOCKET, SO_REUSEPORT, + (const void *) &reuseport, sizeof(int)) + == -1) + { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno, + "setsockopt(SO_REUSEPORT) %V failed, ignored", + &ls[i].addr_text); + } + + ls[i].add_reuseport = 0; + } +#endif + + if (ls[i].fd != (ngx_socket_t) -1) { + continue; + } + + if (ls[i].inherited) { + + /* TODO: close on exit */ + /* TODO: nonblocking */ + /* TODO: deferred accept */ + + continue; + } + + s = ngx_socket(ls[i].sockaddr->sa_family, ls[i].type, 0); + + if (s == (ngx_socket_t) -1) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno, + ngx_socket_n " %V failed", &ls[i].addr_text); + return NGX_ERROR; + } + + if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, + (const void *) &reuseaddr, sizeof(int)) + == -1) + { + ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno, + "setsockopt(SO_REUSEADDR) %V failed", + &ls[i].addr_text); + + if (ngx_close_socket(s) == -1) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno, + ngx_close_socket_n " %V failed", + &ls[i].addr_text); + } + + return NGX_ERROR; + } + +#if (NGX_HAVE_REUSEPORT) + + if (ls[i].reuseport) { + int reuseport; + + reuseport = 1; + + if (setsockopt(s, SOL_SOCKET, SO_REUSEPORT, + (const void *) &reuseport, sizeof(int)) + == -1) + { + ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno, + "setsockopt(SO_REUSEPORT) %V failed, ignored", + &ls[i].addr_text); + + if (ngx_close_socket(s) == -1) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno, + ngx_close_socket_n " %V failed", + &ls[i].addr_text); + } + + return NGX_ERROR; + } + } +#endif + +#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY) + + if (ls[i].sockaddr->sa_family == AF_INET6) { + int ipv6only; + + ipv6only = ls[i].ipv6only; + + if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, + (const void *) &ipv6only, sizeof(int)) + == -1) + { + ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno, + "setsockopt(IPV6_V6ONLY) %V failed, ignored", + &ls[i].addr_text); + } + } +#endif + /* TODO: close on exit */ + + if (!(ngx_event_flags & NGX_USE_IOCP_EVENT)) { + if (ngx_nonblocking(s) == -1) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno, + ngx_nonblocking_n " %V failed", + &ls[i].addr_text); + + if (ngx_close_socket(s) == -1) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno, + ngx_close_socket_n " %V failed", + &ls[i].addr_text); + } + + return NGX_ERROR; + } + } + + ngx_log_debug2(NGX_LOG_DEBUG_CORE, log, 0, + "bind() %V #%d ", &ls[i].addr_text, s); + + if (bind(s, ls[i].sockaddr, ls[i].socklen) == -1) { + err = ngx_socket_errno; + + if (err != NGX_EADDRINUSE || !ngx_test_config) { + ngx_log_error(NGX_LOG_EMERG, log, err, + "bind() to %V failed", &ls[i].addr_text); + } + + if (ngx_close_socket(s) == -1) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno, + ngx_close_socket_n " %V failed", + &ls[i].addr_text); + } + + if (err != NGX_EADDRINUSE) { + return NGX_ERROR; + } + + if (!ngx_test_config) { + failed = 1; + } + + continue; + } + +#if (NGX_HAVE_UNIX_DOMAIN) + + if (ls[i].sockaddr->sa_family == AF_UNIX) { + mode_t mode; + u_char *name; + + name = ls[i].addr_text.data + sizeof("unix:") - 1; + mode = (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH); + + if (chmod((char *) name, mode) == -1) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, + "chmod() \"%s\" failed", name); + } + + if (ngx_test_config) { + if (ngx_delete_file(name) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, + ngx_delete_file_n " %s failed", name); + } + } + } +#endif + + if (ls[i].type != SOCK_STREAM) { + ls[i].fd = s; + continue; + } + + if (listen(s, ls[i].backlog) == -1) { + err = ngx_socket_errno; + + /* + * on OpenVZ after suspend/resume EADDRINUSE + * may be returned by listen() instead of bind(), see + * https://bugzilla.openvz.org/show_bug.cgi?id=2470 + */ + + if (err != NGX_EADDRINUSE || !ngx_test_config) { + ngx_log_error(NGX_LOG_EMERG, log, err, + "listen() to %V, backlog %d failed", + &ls[i].addr_text, ls[i].backlog); + } + + if (ngx_close_socket(s) == -1) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno, + ngx_close_socket_n " %V failed", + &ls[i].addr_text); + } + + if (err != NGX_EADDRINUSE) { + return NGX_ERROR; + } + + if (!ngx_test_config) { + failed = 1; + } + + continue; + } + + ls[i].listen = 1; + + ls[i].fd = s; + } + + if (!failed) { + break; + } + + /* TODO: delay configurable */ + + ngx_log_error(NGX_LOG_NOTICE, log, 0, + "try again to bind() after 500ms"); + + ngx_msleep(500); + } + + if (failed) { + ngx_log_error(NGX_LOG_EMERG, log, 0, "still could not bind()"); + return NGX_ERROR; + } + + return NGX_OK; +} + + +void +ngx_configure_listening_sockets(ngx_cycle_t *cycle) +{ + int value; + ngx_uint_t i; + ngx_listening_t *ls; + +#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER) + struct accept_filter_arg af; +#endif + + ls = cycle->listening.elts; + for (i = 0; i < cycle->listening.nelts; i++) { + + ls[i].log = *ls[i].logp; + + if (ls[i].rcvbuf != -1) { + if (setsockopt(ls[i].fd, SOL_SOCKET, SO_RCVBUF, + (const void *) &ls[i].rcvbuf, sizeof(int)) + == -1) + { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno, + "setsockopt(SO_RCVBUF, %d) %V failed, ignored", + ls[i].rcvbuf, &ls[i].addr_text); + } + } + + if (ls[i].sndbuf != -1) { + if (setsockopt(ls[i].fd, SOL_SOCKET, SO_SNDBUF, + (const void *) &ls[i].sndbuf, sizeof(int)) + == -1) + { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno, + "setsockopt(SO_SNDBUF, %d) %V failed, ignored", + ls[i].sndbuf, &ls[i].addr_text); + } + } + + if (ls[i].keepalive) { + value = (ls[i].keepalive == 1) ? 1 : 0; + + if (setsockopt(ls[i].fd, SOL_SOCKET, SO_KEEPALIVE, + (const void *) &value, sizeof(int)) + == -1) + { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno, + "setsockopt(SO_KEEPALIVE, %d) %V failed, ignored", + value, &ls[i].addr_text); + } + } + +#if (NGX_HAVE_KEEPALIVE_TUNABLE) + + if (ls[i].keepidle) { + value = ls[i].keepidle; + +#if (NGX_KEEPALIVE_FACTOR) + value *= NGX_KEEPALIVE_FACTOR; +#endif + + if (setsockopt(ls[i].fd, IPPROTO_TCP, TCP_KEEPIDLE, + (const void *) &value, sizeof(int)) + == -1) + { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno, + "setsockopt(TCP_KEEPIDLE, %d) %V failed, ignored", + value, &ls[i].addr_text); + } + } + + if (ls[i].keepintvl) { + value = ls[i].keepintvl; + +#if (NGX_KEEPALIVE_FACTOR) + value *= NGX_KEEPALIVE_FACTOR; +#endif + + if (setsockopt(ls[i].fd, IPPROTO_TCP, TCP_KEEPINTVL, + (const void *) &value, sizeof(int)) + == -1) + { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno, + "setsockopt(TCP_KEEPINTVL, %d) %V failed, ignored", + value, &ls[i].addr_text); + } + } + + if (ls[i].keepcnt) { + if (setsockopt(ls[i].fd, IPPROTO_TCP, TCP_KEEPCNT, + (const void *) &ls[i].keepcnt, sizeof(int)) + == -1) + { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno, + "setsockopt(TCP_KEEPCNT, %d) %V failed, ignored", + ls[i].keepcnt, &ls[i].addr_text); + } + } + +#endif + +#if (NGX_HAVE_SETFIB) + if (ls[i].setfib != -1) { + if (setsockopt(ls[i].fd, SOL_SOCKET, SO_SETFIB, + (const void *) &ls[i].setfib, sizeof(int)) + == -1) + { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno, + "setsockopt(SO_SETFIB, %d) %V failed, ignored", + ls[i].setfib, &ls[i].addr_text); + } + } +#endif + +#if (NGX_HAVE_TCP_FASTOPEN) + if (ls[i].fastopen != -1) { + if (setsockopt(ls[i].fd, IPPROTO_TCP, TCP_FASTOPEN, + (const void *) &ls[i].fastopen, sizeof(int)) + == -1) + { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno, + "setsockopt(TCP_FASTOPEN, %d) %V failed, ignored", + ls[i].fastopen, &ls[i].addr_text); + } + } +#endif + +#if 0 + if (1) { + int tcp_nodelay = 1; + + if (setsockopt(ls[i].fd, IPPROTO_TCP, TCP_NODELAY, + (const void *) &tcp_nodelay, sizeof(int)) + == -1) + { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno, + "setsockopt(TCP_NODELAY) %V failed, ignored", + &ls[i].addr_text); + } + } +#endif + + if (ls[i].listen) { + + /* change backlog via listen() */ + + if (listen(ls[i].fd, ls[i].backlog) == -1) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno, + "listen() to %V, backlog %d failed, ignored", + &ls[i].addr_text, ls[i].backlog); + } + } + + /* + * setting deferred mode should be last operation on socket, + * because code may prematurely continue cycle on failure + */ + +#if (NGX_HAVE_DEFERRED_ACCEPT) + +#ifdef SO_ACCEPTFILTER + + if (ls[i].delete_deferred) { + if (setsockopt(ls[i].fd, SOL_SOCKET, SO_ACCEPTFILTER, NULL, 0) + == -1) + { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno, + "setsockopt(SO_ACCEPTFILTER, NULL) " + "for %V failed, ignored", + &ls[i].addr_text); + + if (ls[i].accept_filter) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, + "could not change the accept filter " + "to \"%s\" for %V, ignored", + ls[i].accept_filter, &ls[i].addr_text); + } + + continue; + } + + ls[i].deferred_accept = 0; + } + + if (ls[i].add_deferred) { + ngx_memzero(&af, sizeof(struct accept_filter_arg)); + (void) ngx_cpystrn((u_char *) af.af_name, + (u_char *) ls[i].accept_filter, 16); + + if (setsockopt(ls[i].fd, SOL_SOCKET, SO_ACCEPTFILTER, + &af, sizeof(struct accept_filter_arg)) + == -1) + { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno, + "setsockopt(SO_ACCEPTFILTER, \"%s\") " + "for %V failed, ignored", + ls[i].accept_filter, &ls[i].addr_text); + continue; + } + + ls[i].deferred_accept = 1; + } + +#endif + +#ifdef TCP_DEFER_ACCEPT + + if (ls[i].add_deferred || ls[i].delete_deferred) { + + if (ls[i].add_deferred) { + /* + * There is no way to find out how long a connection was + * in queue (and a connection may bypass deferred queue at all + * if syncookies were used), hence we use 1 second timeout + * here. + */ + value = 1; + + } else { + value = 0; + } + + if (setsockopt(ls[i].fd, IPPROTO_TCP, TCP_DEFER_ACCEPT, + &value, sizeof(int)) + == -1) + { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno, + "setsockopt(TCP_DEFER_ACCEPT, %d) for %V failed, " + "ignored", + value, &ls[i].addr_text); + + continue; + } + } + + if (ls[i].add_deferred) { + ls[i].deferred_accept = 1; + } + +#endif + +#endif /* NGX_HAVE_DEFERRED_ACCEPT */ + +#if (NGX_HAVE_IP_RECVDSTADDR) + + if (ls[i].wildcard + && ls[i].type == SOCK_DGRAM + && ls[i].sockaddr->sa_family == AF_INET) + { + value = 1; + + if (setsockopt(ls[i].fd, IPPROTO_IP, IP_RECVDSTADDR, + (const void *) &value, sizeof(int)) + == -1) + { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno, + "setsockopt(IP_RECVDSTADDR) " + "for %V failed, ignored", + &ls[i].addr_text); + } + } + +#elif (NGX_HAVE_IP_PKTINFO) + + if (ls[i].wildcard + && ls[i].type == SOCK_DGRAM + && ls[i].sockaddr->sa_family == AF_INET) + { + value = 1; + + if (setsockopt(ls[i].fd, IPPROTO_IP, IP_PKTINFO, + (const void *) &value, sizeof(int)) + == -1) + { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno, + "setsockopt(IP_PKTINFO) " + "for %V failed, ignored", + &ls[i].addr_text); + } + } + +#endif + +#if (NGX_HAVE_INET6 && NGX_HAVE_IPV6_RECVPKTINFO) + + if (ls[i].wildcard + && ls[i].type == SOCK_DGRAM + && ls[i].sockaddr->sa_family == AF_INET6) + { + value = 1; + + if (setsockopt(ls[i].fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, + (const void *) &value, sizeof(int)) + == -1) + { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno, + "setsockopt(IPV6_RECVPKTINFO) " + "for %V failed, ignored", + &ls[i].addr_text); + } + } + +#endif + } + + return; +} + + +void +ngx_close_listening_sockets(ngx_cycle_t *cycle) +{ + ngx_uint_t i; + ngx_listening_t *ls; + ngx_connection_t *c; + + if (ngx_event_flags & NGX_USE_IOCP_EVENT) { + return; + } + + ngx_accept_mutex_held = 0; + ngx_use_accept_mutex = 0; + + ls = cycle->listening.elts; + for (i = 0; i < cycle->listening.nelts; i++) { + + c = ls[i].connection; + + if (c) { + if (c->read->active) { + if (ngx_event_flags & NGX_USE_EPOLL_EVENT) { + + /* + * it seems that Linux-2.6.x OpenVZ sends events + * for closed shared listening sockets unless + * the events was explicitly deleted + */ + + ngx_del_event(c->read, NGX_READ_EVENT, 0); + + } else { + ngx_del_event(c->read, NGX_READ_EVENT, NGX_CLOSE_EVENT); + } + } + + ngx_free_connection(c); + + c->fd = (ngx_socket_t) -1; + } + + ngx_log_debug2(NGX_LOG_DEBUG_CORE, cycle->log, 0, + "close listening %V #%d ", &ls[i].addr_text, ls[i].fd); + + if (ngx_close_socket(ls[i].fd) == -1) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_socket_errno, + ngx_close_socket_n " %V failed", &ls[i].addr_text); + } + +#if (NGX_HAVE_UNIX_DOMAIN) + + if (ls[i].sockaddr->sa_family == AF_UNIX + && ngx_process <= NGX_PROCESS_MASTER + && ngx_new_binary == 0) + { + u_char *name = ls[i].addr_text.data + sizeof("unix:") - 1; + + if (ngx_delete_file(name) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_socket_errno, + ngx_delete_file_n " %s failed", name); + } + } + +#endif + + ls[i].fd = (ngx_socket_t) -1; + } + + cycle->listening.nelts = 0; +} + + +ngx_connection_t * +ngx_get_connection(ngx_socket_t s, ngx_log_t *log) +{ + ngx_uint_t instance; + ngx_event_t *rev, *wev; + ngx_connection_t *c; + + /* disable warning: Win32 SOCKET is u_int while UNIX socket is int */ + + if (ngx_cycle->files && (ngx_uint_t) s >= ngx_cycle->files_n) { + ngx_log_error(NGX_LOG_ALERT, log, 0, + "the new socket has number %d, " + "but only %ui files are available", + s, ngx_cycle->files_n); + return NULL; + } + + c = ngx_cycle->free_connections; + + if (c == NULL) { + ngx_drain_connections((ngx_cycle_t *) ngx_cycle); + c = ngx_cycle->free_connections; + } + + if (c == NULL) { + ngx_log_error(NGX_LOG_ALERT, log, 0, + "%ui worker_connections are not enough", + ngx_cycle->connection_n); + + return NULL; + } + + ngx_cycle->free_connections = c->data; + ngx_cycle->free_connection_n--; + + if (ngx_cycle->files && ngx_cycle->files[s] == NULL) { + ngx_cycle->files[s] = c; + } + + rev = c->read; + wev = c->write; + + ngx_memzero(c, sizeof(ngx_connection_t)); + + c->read = rev; + c->write = wev; + c->fd = s; + c->log = log; + + instance = rev->instance; + + ngx_memzero(rev, sizeof(ngx_event_t)); + ngx_memzero(wev, sizeof(ngx_event_t)); + + rev->instance = !instance; + wev->instance = !instance; + + rev->index = NGX_INVALID_INDEX; + wev->index = NGX_INVALID_INDEX; + + rev->data = c; + wev->data = c; + + wev->write = 1; + + return c; +} + + +void +ngx_free_connection(ngx_connection_t *c) +{ + c->data = ngx_cycle->free_connections; + ngx_cycle->free_connections = c; + ngx_cycle->free_connection_n++; + + if (ngx_cycle->files && ngx_cycle->files[c->fd] == c) { + ngx_cycle->files[c->fd] = NULL; + } +} + + +void +ngx_close_connection(ngx_connection_t *c) +{ + ngx_err_t err; + ngx_uint_t log_error, level; + ngx_socket_t fd; + + if (c->fd == (ngx_socket_t) -1) { + ngx_log_error(NGX_LOG_ALERT, c->log, 0, "connection already closed"); + return; + } + + if (c->read->timer_set) { + ngx_del_timer(c->read); + } + + if (c->write->timer_set) { + ngx_del_timer(c->write); + } + + if (!c->shared) { + if (ngx_del_conn) { + ngx_del_conn(c, NGX_CLOSE_EVENT); + + } else { + if (c->read->active || c->read->disabled) { + ngx_del_event(c->read, NGX_READ_EVENT, NGX_CLOSE_EVENT); + } + + if (c->write->active || c->write->disabled) { + ngx_del_event(c->write, NGX_WRITE_EVENT, NGX_CLOSE_EVENT); + } + } + } + + if (c->read->posted) { + ngx_delete_posted_event(c->read); + } + + if (c->write->posted) { + ngx_delete_posted_event(c->write); + } + + c->read->closed = 1; + c->write->closed = 1; + + ngx_reusable_connection(c, 0); + + log_error = c->log_error; + + ngx_free_connection(c); + + fd = c->fd; + c->fd = (ngx_socket_t) -1; + + if (c->shared) { + return; + } + + if (ngx_close_socket(fd) == -1) { + + err = ngx_socket_errno; + + if (err == NGX_ECONNRESET || err == NGX_ENOTCONN) { + + switch (log_error) { + + case NGX_ERROR_INFO: + level = NGX_LOG_INFO; + break; + + case NGX_ERROR_ERR: + level = NGX_LOG_ERR; + break; + + default: + level = NGX_LOG_CRIT; + } + + } else { + level = NGX_LOG_CRIT; + } + + ngx_log_error(level, c->log, err, ngx_close_socket_n " %d failed", fd); + } +} + + +void +ngx_reusable_connection(ngx_connection_t *c, ngx_uint_t reusable) +{ + ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0, + "reusable connection: %ui", reusable); + + if (c->reusable) { + ngx_queue_remove(&c->queue); + ngx_cycle->reusable_connections_n--; + +#if (NGX_STAT_STUB) + (void) ngx_atomic_fetch_add(ngx_stat_waiting, -1); +#endif + } + + c->reusable = reusable; + + if (reusable) { + /* need cast as ngx_cycle is volatile */ + + ngx_queue_insert_head( + (ngx_queue_t *) &ngx_cycle->reusable_connections_queue, &c->queue); + ngx_cycle->reusable_connections_n++; + +#if (NGX_STAT_STUB) + (void) ngx_atomic_fetch_add(ngx_stat_waiting, 1); +#endif + } +} + + +static void +ngx_drain_connections(ngx_cycle_t *cycle) +{ + ngx_uint_t i, n; + ngx_queue_t *q; + ngx_connection_t *c; + + n = ngx_max(ngx_min(32, cycle->reusable_connections_n / 8), 1); + + for (i = 0; i < n; i++) { + if (ngx_queue_empty(&cycle->reusable_connections_queue)) { + break; + } + + q = ngx_queue_last(&cycle->reusable_connections_queue); + c = ngx_queue_data(q, ngx_connection_t, queue); + + ngx_log_debug0(NGX_LOG_DEBUG_CORE, c->log, 0, + "reusing connection"); + + c->close = 1; + c->read->handler(c->read); + } +} + + +void +ngx_close_idle_connections(ngx_cycle_t *cycle) +{ + ngx_uint_t i; + ngx_connection_t *c; + + c = cycle->connections; + + for (i = 0; i < cycle->connection_n; i++) { + + /* THREAD: lock */ + + if (c[i].fd != (ngx_socket_t) -1 && c[i].idle) { + c[i].close = 1; + c[i].read->handler(c[i].read); + } + } +} + + +ngx_int_t +ngx_connection_local_sockaddr(ngx_connection_t *c, ngx_str_t *s, + ngx_uint_t port) +{ + socklen_t len; + ngx_uint_t addr; + ngx_sockaddr_t sa; + struct sockaddr_in *sin; +#if (NGX_HAVE_INET6) + ngx_uint_t i; + struct sockaddr_in6 *sin6; +#endif + + addr = 0; + + if (c->local_socklen) { + switch (c->local_sockaddr->sa_family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + sin6 = (struct sockaddr_in6 *) c->local_sockaddr; + + for (i = 0; addr == 0 && i < 16; i++) { + addr |= sin6->sin6_addr.s6_addr[i]; + } + + break; +#endif + +#if (NGX_HAVE_UNIX_DOMAIN) + case AF_UNIX: + addr = 1; + break; +#endif + + default: /* AF_INET */ + sin = (struct sockaddr_in *) c->local_sockaddr; + addr = sin->sin_addr.s_addr; + break; + } + } + + if (addr == 0) { + + len = sizeof(ngx_sockaddr_t); + + if (getsockname(c->fd, &sa.sockaddr, &len) == -1) { + ngx_connection_error(c, ngx_socket_errno, "getsockname() failed"); + return NGX_ERROR; + } + + c->local_sockaddr = ngx_palloc(c->pool, len); + if (c->local_sockaddr == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(c->local_sockaddr, &sa, len); + + c->local_socklen = len; + } + + if (s == NULL) { + return NGX_OK; + } + + s->len = ngx_sock_ntop(c->local_sockaddr, c->local_socklen, + s->data, s->len, port); + + return NGX_OK; +} + + +ngx_int_t +ngx_connection_error(ngx_connection_t *c, ngx_err_t err, char *text) +{ + ngx_uint_t level; + + /* Winsock may return NGX_ECONNABORTED instead of NGX_ECONNRESET */ + + if ((err == NGX_ECONNRESET +#if (NGX_WIN32) + || err == NGX_ECONNABORTED +#endif + ) && c->log_error == NGX_ERROR_IGNORE_ECONNRESET) + { + return 0; + } + +#if (NGX_SOLARIS) + if (err == NGX_EINVAL && c->log_error == NGX_ERROR_IGNORE_EINVAL) { + return 0; + } +#endif + + if (err == 0 + || err == NGX_ECONNRESET +#if (NGX_WIN32) + || err == NGX_ECONNABORTED +#else + || err == NGX_EPIPE +#endif + || err == NGX_ENOTCONN + || err == NGX_ETIMEDOUT + || err == NGX_ECONNREFUSED + || err == NGX_ENETDOWN + || err == NGX_ENETUNREACH + || err == NGX_EHOSTDOWN + || err == NGX_EHOSTUNREACH) + { + switch (c->log_error) { + + case NGX_ERROR_IGNORE_EINVAL: + case NGX_ERROR_IGNORE_ECONNRESET: + case NGX_ERROR_INFO: + level = NGX_LOG_INFO; + break; + + default: + level = NGX_LOG_ERR; + } + + } else { + level = NGX_LOG_ALERT; + } + + ngx_log_error(level, c->log, err, text); + + return NGX_ERROR; +} diff --git a/app/nginx/src/core/ngx_connection.h b/app/nginx/src/core/ngx_connection.h new file mode 100644 index 0000000..1d3e3a3 --- /dev/null +++ b/app/nginx/src/core/ngx_connection.h @@ -0,0 +1,224 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_CONNECTION_H_INCLUDED_ +#define _NGX_CONNECTION_H_INCLUDED_ + + +#include +#include + + +typedef struct ngx_listening_s ngx_listening_t; + +struct ngx_listening_s { + ngx_socket_t fd; + + struct sockaddr *sockaddr; + socklen_t socklen; /* size of sockaddr */ + size_t addr_text_max_len; + ngx_str_t addr_text; + + int type; + + int backlog; + int rcvbuf; + int sndbuf; +#if (NGX_HAVE_KEEPALIVE_TUNABLE) + int keepidle; + int keepintvl; + int keepcnt; +#endif + + /* handler of accepted connection */ + ngx_connection_handler_pt handler; + + void *servers; /* array of ngx_http_in_addr_t, for example */ + + ngx_log_t log; + ngx_log_t *logp; + + size_t pool_size; + /* should be here because of the AcceptEx() preread */ + size_t post_accept_buffer_size; + /* should be here because of the deferred accept */ + ngx_msec_t post_accept_timeout; + + ngx_listening_t *previous; + ngx_connection_t *connection; + + ngx_uint_t worker; + + unsigned open:1; + unsigned remain:1; + unsigned ignore:1; + + unsigned bound:1; /* already bound */ + unsigned inherited:1; /* inherited from previous process */ + unsigned nonblocking_accept:1; + unsigned listen:1; + unsigned nonblocking:1; + unsigned shared:1; /* shared between threads or processes */ + unsigned addr_ntop:1; + unsigned wildcard:1; + +#if (NGX_HAVE_INET6) + unsigned ipv6only:1; +#endif + unsigned reuseport:1; + unsigned add_reuseport:1; + unsigned keepalive:2; + + unsigned deferred_accept:1; + unsigned delete_deferred:1; + unsigned add_deferred:1; +#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER) + char *accept_filter; +#endif +#if (NGX_HAVE_SETFIB) + int setfib; +#endif + +#if (NGX_HAVE_TCP_FASTOPEN) + int fastopen; +#endif + +}; + + +typedef enum { + NGX_ERROR_ALERT = 0, + NGX_ERROR_ERR, + NGX_ERROR_INFO, + NGX_ERROR_IGNORE_ECONNRESET, + NGX_ERROR_IGNORE_EINVAL +} ngx_connection_log_error_e; + + +typedef enum { + NGX_TCP_NODELAY_UNSET = 0, + NGX_TCP_NODELAY_SET, + NGX_TCP_NODELAY_DISABLED +} ngx_connection_tcp_nodelay_e; + + +typedef enum { + NGX_TCP_NOPUSH_UNSET = 0, + NGX_TCP_NOPUSH_SET, + NGX_TCP_NOPUSH_DISABLED +} ngx_connection_tcp_nopush_e; + + +#define NGX_LOWLEVEL_BUFFERED 0x0f +#define NGX_SSL_BUFFERED 0x01 +#define NGX_HTTP_V2_BUFFERED 0x02 + + +struct ngx_connection_s { + void *data; + ngx_event_t *read; + ngx_event_t *write; + + ngx_socket_t fd; + + ngx_recv_pt recv; + ngx_send_pt send; + ngx_recv_chain_pt recv_chain; + ngx_send_chain_pt send_chain; + + ngx_listening_t *listening; + + off_t sent; + + ngx_log_t *log; + + ngx_pool_t *pool; + + int type; + + struct sockaddr *sockaddr; + socklen_t socklen; + ngx_str_t addr_text; + + ngx_str_t proxy_protocol_addr; + in_port_t proxy_protocol_port; + +#if (NGX_SSL || NGX_COMPAT) + ngx_ssl_connection_t *ssl; +#endif + + struct sockaddr *local_sockaddr; + socklen_t local_socklen; + + ngx_buf_t *buffer; + + ngx_queue_t queue; + + ngx_atomic_uint_t number; + + ngx_uint_t requests; + + unsigned buffered:8; + + unsigned log_error:3; /* ngx_connection_log_error_e */ + + unsigned timedout:1; + unsigned error:1; + unsigned destroyed:1; + + unsigned idle:1; + unsigned reusable:1; + unsigned close:1; + unsigned shared:1; + + unsigned sendfile:1; + unsigned sndlowat:1; + unsigned tcp_nodelay:2; /* ngx_connection_tcp_nodelay_e */ + unsigned tcp_nopush:2; /* ngx_connection_tcp_nopush_e */ + + unsigned need_last_buf:1; + +#if (NGX_HAVE_AIO_SENDFILE || NGX_COMPAT) + unsigned busy_count:2; +#endif + +#if (NGX_THREADS || NGX_COMPAT) + ngx_thread_task_t *sendfile_task; +#endif +}; + + +#define ngx_set_connection_log(c, l) \ + \ + c->log->file = l->file; \ + c->log->next = l->next; \ + c->log->writer = l->writer; \ + c->log->wdata = l->wdata; \ + if (!(c->log->log_level & NGX_LOG_DEBUG_CONNECTION)) { \ + c->log->log_level = l->log_level; \ + } + + +ngx_listening_t *ngx_create_listening(ngx_conf_t *cf, struct sockaddr *sockaddr, + socklen_t socklen); +ngx_int_t ngx_clone_listening(ngx_conf_t *cf, ngx_listening_t *ls); +ngx_int_t ngx_set_inherited_sockets(ngx_cycle_t *cycle); +ngx_int_t ngx_open_listening_sockets(ngx_cycle_t *cycle); +void ngx_configure_listening_sockets(ngx_cycle_t *cycle); +void ngx_close_listening_sockets(ngx_cycle_t *cycle); +void ngx_close_connection(ngx_connection_t *c); +void ngx_close_idle_connections(ngx_cycle_t *cycle); +ngx_int_t ngx_connection_local_sockaddr(ngx_connection_t *c, ngx_str_t *s, + ngx_uint_t port); +ngx_int_t ngx_connection_error(ngx_connection_t *c, ngx_err_t err, char *text); + +ngx_connection_t *ngx_get_connection(ngx_socket_t s, ngx_log_t *log); +void ngx_free_connection(ngx_connection_t *c); + +void ngx_reusable_connection(ngx_connection_t *c, ngx_uint_t reusable); + +#endif /* _NGX_CONNECTION_H_INCLUDED_ */ diff --git a/app/nginx/src/core/ngx_core.h b/app/nginx/src/core/ngx_core.h new file mode 100644 index 0000000..2069373 --- /dev/null +++ b/app/nginx/src/core/ngx_core.h @@ -0,0 +1,111 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_CORE_H_INCLUDED_ +#define _NGX_CORE_H_INCLUDED_ + + +#include + + +typedef struct ngx_module_s ngx_module_t; +typedef struct ngx_conf_s ngx_conf_t; +typedef struct ngx_cycle_s ngx_cycle_t; +typedef struct ngx_pool_s ngx_pool_t; +typedef struct ngx_chain_s ngx_chain_t; +typedef struct ngx_log_s ngx_log_t; +typedef struct ngx_open_file_s ngx_open_file_t; +typedef struct ngx_command_s ngx_command_t; +typedef struct ngx_file_s ngx_file_t; +typedef struct ngx_event_s ngx_event_t; +typedef struct ngx_event_aio_s ngx_event_aio_t; +typedef struct ngx_connection_s ngx_connection_t; +typedef struct ngx_thread_task_s ngx_thread_task_t; +typedef struct ngx_ssl_s ngx_ssl_t; +typedef struct ngx_ssl_connection_s ngx_ssl_connection_t; + +typedef void (*ngx_event_handler_pt)(ngx_event_t *ev); +typedef void (*ngx_connection_handler_pt)(ngx_connection_t *c); + + +#define NGX_OK 0 +#define NGX_ERROR -1 +#define NGX_AGAIN -2 +#define NGX_BUSY -3 +#define NGX_DONE -4 +#define NGX_DECLINED -5 +#define NGX_ABORT -6 + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if (NGX_PCRE) +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#if (NGX_OPENSSL) +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include + + +#define LF (u_char) '\n' +#define CR (u_char) '\r' +#define CRLF "\r\n" + + +#define ngx_abs(value) (((value) >= 0) ? (value) : - (value)) +#define ngx_max(val1, val2) ((val1 < val2) ? (val2) : (val1)) +#define ngx_min(val1, val2) ((val1 > val2) ? (val2) : (val1)) + +void ngx_cpuinfo(void); + +#if (NGX_HAVE_OPENAT) +#define NGX_DISABLE_SYMLINKS_OFF 0 +#define NGX_DISABLE_SYMLINKS_ON 1 +#define NGX_DISABLE_SYMLINKS_NOTOWNER 2 +#endif + +#endif /* _NGX_CORE_H_INCLUDED_ */ diff --git a/app/nginx/src/core/ngx_cpuinfo.c b/app/nginx/src/core/ngx_cpuinfo.c new file mode 100644 index 0000000..7205319 --- /dev/null +++ b/app/nginx/src/core/ngx_cpuinfo.c @@ -0,0 +1,139 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include + + +#if (( __i386__ || __amd64__ ) && ( __GNUC__ || __INTEL_COMPILER )) + + +static ngx_inline void ngx_cpuid(uint32_t i, uint32_t *buf); + + +#if ( __i386__ ) + +static ngx_inline void +ngx_cpuid(uint32_t i, uint32_t *buf) +{ + + /* + * we could not use %ebx as output parameter if gcc builds PIC, + * and we could not save %ebx on stack, because %esp is used, + * when the -fomit-frame-pointer optimization is specified. + */ + + __asm__ ( + + " mov %%ebx, %%esi; " + + " cpuid; " + " mov %%eax, (%1); " + " mov %%ebx, 4(%1); " + " mov %%edx, 8(%1); " + " mov %%ecx, 12(%1); " + + " mov %%esi, %%ebx; " + + : : "a" (i), "D" (buf) : "ecx", "edx", "esi", "memory" ); +} + + +#else /* __amd64__ */ + + +static ngx_inline void +ngx_cpuid(uint32_t i, uint32_t *buf) +{ + uint32_t eax, ebx, ecx, edx; + + __asm__ ( + + "cpuid" + + : "=a" (eax), "=b" (ebx), "=c" (ecx), "=d" (edx) : "a" (i) ); + + buf[0] = eax; + buf[1] = ebx; + buf[2] = edx; + buf[3] = ecx; +} + + +#endif + + +/* auto detect the L2 cache line size of modern and widespread CPUs */ + +void +ngx_cpuinfo(void) +{ + u_char *vendor; + uint32_t vbuf[5], cpu[4], model; + + vbuf[0] = 0; + vbuf[1] = 0; + vbuf[2] = 0; + vbuf[3] = 0; + vbuf[4] = 0; + + ngx_cpuid(0, vbuf); + + vendor = (u_char *) &vbuf[1]; + + if (vbuf[0] == 0) { + return; + } + + ngx_cpuid(1, cpu); + + if (ngx_strcmp(vendor, "GenuineIntel") == 0) { + + switch ((cpu[0] & 0xf00) >> 8) { + + /* Pentium */ + case 5: + ngx_cacheline_size = 32; + break; + + /* Pentium Pro, II, III */ + case 6: + ngx_cacheline_size = 32; + + model = ((cpu[0] & 0xf0000) >> 8) | (cpu[0] & 0xf0); + + if (model >= 0xd0) { + /* Intel Core, Core 2, Atom */ + ngx_cacheline_size = 64; + } + + break; + + /* + * Pentium 4, although its cache line size is 64 bytes, + * it prefetches up to two cache lines during memory read + */ + case 15: + ngx_cacheline_size = 128; + break; + } + + } else if (ngx_strcmp(vendor, "AuthenticAMD") == 0) { + ngx_cacheline_size = 64; + } +} + +#else + + +void +ngx_cpuinfo(void) +{ +} + + +#endif diff --git a/app/nginx/src/core/ngx_crc.h b/app/nginx/src/core/ngx_crc.h new file mode 100644 index 0000000..35981bc --- /dev/null +++ b/app/nginx/src/core/ngx_crc.h @@ -0,0 +1,39 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_CRC_H_INCLUDED_ +#define _NGX_CRC_H_INCLUDED_ + + +#include +#include + + +/* 32-bit crc16 */ + +static ngx_inline uint32_t +ngx_crc(u_char *data, size_t len) +{ + uint32_t sum; + + for (sum = 0; len; len--) { + + /* + * gcc 2.95.2 x86 and icc 7.1.006 compile + * that operator into the single "rol" opcode, + * msvc 6.0sp2 compiles it into four opcodes. + */ + sum = sum >> 1 | sum << 31; + + sum += *data++; + } + + return sum; +} + + +#endif /* _NGX_CRC_H_INCLUDED_ */ diff --git a/app/nginx/src/core/ngx_crc32.c b/app/nginx/src/core/ngx_crc32.c new file mode 100644 index 0000000..a5b4017 --- /dev/null +++ b/app/nginx/src/core/ngx_crc32.c @@ -0,0 +1,129 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include + + +/* + * The code and lookup tables are based on the algorithm + * described at http://www.w3.org/TR/PNG/ + * + * The 256 element lookup table takes 1024 bytes, and it may be completely + * cached after processing about 30-60 bytes of data. So for short data + * we use the 16 element lookup table that takes only 64 bytes and align it + * to CPU cache line size. Of course, the small table adds code inside + * CRC32 loop, but the cache misses overhead is bigger than overhead of + * the additional code. For example, ngx_crc32_short() of 16 bytes of data + * takes half as much CPU clocks than ngx_crc32_long(). + */ + + +static uint32_t ngx_crc32_table16[] = { + 0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac, + 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, + 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, + 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c +}; + + +uint32_t ngx_crc32_table256[] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, + 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, + 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, + 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, + 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, + 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, + 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, + 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, + 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, + 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, + 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, + 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, + 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, + 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, + 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, + 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, + 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, + 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, + 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, + 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, + 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, + 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, + 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, + 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, + 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, + 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, + 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, + 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, + 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, + 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, + 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, + 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, + 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, + 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, + 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, + 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, + 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, + 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, + 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, + 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, + 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, + 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, + 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d +}; + + +uint32_t *ngx_crc32_table_short = ngx_crc32_table16; + + +ngx_int_t +ngx_crc32_table_init(void) +{ + void *p; + + if (((uintptr_t) ngx_crc32_table_short + & ~((uintptr_t) ngx_cacheline_size - 1)) + == (uintptr_t) ngx_crc32_table_short) + { + return NGX_OK; + } + + p = ngx_alloc(16 * sizeof(uint32_t) + ngx_cacheline_size, ngx_cycle->log); + if (p == NULL) { + return NGX_ERROR; + } + + p = ngx_align_ptr(p, ngx_cacheline_size); + + ngx_memcpy(p, ngx_crc32_table16, 16 * sizeof(uint32_t)); + + ngx_crc32_table_short = p; + + return NGX_OK; +} diff --git a/app/nginx/src/core/ngx_crc32.h b/app/nginx/src/core/ngx_crc32.h new file mode 100644 index 0000000..f6d6865 --- /dev/null +++ b/app/nginx/src/core/ngx_crc32.h @@ -0,0 +1,79 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_CRC32_H_INCLUDED_ +#define _NGX_CRC32_H_INCLUDED_ + + +#include +#include + + +extern uint32_t *ngx_crc32_table_short; +extern uint32_t ngx_crc32_table256[]; + + +static ngx_inline uint32_t +ngx_crc32_short(u_char *p, size_t len) +{ + u_char c; + uint32_t crc; + + crc = 0xffffffff; + + while (len--) { + c = *p++; + crc = ngx_crc32_table_short[(crc ^ (c & 0xf)) & 0xf] ^ (crc >> 4); + crc = ngx_crc32_table_short[(crc ^ (c >> 4)) & 0xf] ^ (crc >> 4); + } + + return crc ^ 0xffffffff; +} + + +static ngx_inline uint32_t +ngx_crc32_long(u_char *p, size_t len) +{ + uint32_t crc; + + crc = 0xffffffff; + + while (len--) { + crc = ngx_crc32_table256[(crc ^ *p++) & 0xff] ^ (crc >> 8); + } + + return crc ^ 0xffffffff; +} + + +#define ngx_crc32_init(crc) \ + crc = 0xffffffff + + +static ngx_inline void +ngx_crc32_update(uint32_t *crc, u_char *p, size_t len) +{ + uint32_t c; + + c = *crc; + + while (len--) { + c = ngx_crc32_table256[(c ^ *p++) & 0xff] ^ (c >> 8); + } + + *crc = c; +} + + +#define ngx_crc32_final(crc) \ + crc ^= 0xffffffff + + +ngx_int_t ngx_crc32_table_init(void); + + +#endif /* _NGX_CRC32_H_INCLUDED_ */ diff --git a/app/nginx/src/core/ngx_crypt.c b/app/nginx/src/core/ngx_crypt.c new file mode 100644 index 0000000..868dc5d --- /dev/null +++ b/app/nginx/src/core/ngx_crypt.c @@ -0,0 +1,270 @@ + +/* + * Copyright (C) Maxim Dounin + */ + + +#include +#include +#include +#include +#include + + +#if (NGX_CRYPT) + +static ngx_int_t ngx_crypt_apr1(ngx_pool_t *pool, u_char *key, u_char *salt, + u_char **encrypted); +static ngx_int_t ngx_crypt_plain(ngx_pool_t *pool, u_char *key, u_char *salt, + u_char **encrypted); +static ngx_int_t ngx_crypt_ssha(ngx_pool_t *pool, u_char *key, u_char *salt, + u_char **encrypted); +static ngx_int_t ngx_crypt_sha(ngx_pool_t *pool, u_char *key, u_char *salt, + u_char **encrypted); + + +static u_char *ngx_crypt_to64(u_char *p, uint32_t v, size_t n); + + +ngx_int_t +ngx_crypt(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted) +{ + if (ngx_strncmp(salt, "$apr1$", sizeof("$apr1$") - 1) == 0) { + return ngx_crypt_apr1(pool, key, salt, encrypted); + + } else if (ngx_strncmp(salt, "{PLAIN}", sizeof("{PLAIN}") - 1) == 0) { + return ngx_crypt_plain(pool, key, salt, encrypted); + + } else if (ngx_strncmp(salt, "{SSHA}", sizeof("{SSHA}") - 1) == 0) { + return ngx_crypt_ssha(pool, key, salt, encrypted); + + } else if (ngx_strncmp(salt, "{SHA}", sizeof("{SHA}") - 1) == 0) { + return ngx_crypt_sha(pool, key, salt, encrypted); + } + + /* fallback to libc crypt() */ + + return ngx_libc_crypt(pool, key, salt, encrypted); +} + + +static ngx_int_t +ngx_crypt_apr1(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted) +{ + ngx_int_t n; + ngx_uint_t i; + u_char *p, *last, final[16]; + size_t saltlen, keylen; + ngx_md5_t md5, ctx1; + + /* Apache's apr1 crypt is Poul-Henning Kamp's md5 crypt with $apr1$ magic */ + + keylen = ngx_strlen(key); + + /* true salt: no magic, max 8 chars, stop at first $ */ + + salt += sizeof("$apr1$") - 1; + last = salt + 8; + for (p = salt; *p && *p != '$' && p < last; p++) { /* void */ } + saltlen = p - salt; + + /* hash key and salt */ + + ngx_md5_init(&md5); + ngx_md5_update(&md5, key, keylen); + ngx_md5_update(&md5, (u_char *) "$apr1$", sizeof("$apr1$") - 1); + ngx_md5_update(&md5, salt, saltlen); + + ngx_md5_init(&ctx1); + ngx_md5_update(&ctx1, key, keylen); + ngx_md5_update(&ctx1, salt, saltlen); + ngx_md5_update(&ctx1, key, keylen); + ngx_md5_final(final, &ctx1); + + for (n = keylen; n > 0; n -= 16) { + ngx_md5_update(&md5, final, n > 16 ? 16 : n); + } + + ngx_memzero(final, sizeof(final)); + + for (i = keylen; i; i >>= 1) { + if (i & 1) { + ngx_md5_update(&md5, final, 1); + + } else { + ngx_md5_update(&md5, key, 1); + } + } + + ngx_md5_final(final, &md5); + + for (i = 0; i < 1000; i++) { + ngx_md5_init(&ctx1); + + if (i & 1) { + ngx_md5_update(&ctx1, key, keylen); + + } else { + ngx_md5_update(&ctx1, final, 16); + } + + if (i % 3) { + ngx_md5_update(&ctx1, salt, saltlen); + } + + if (i % 7) { + ngx_md5_update(&ctx1, key, keylen); + } + + if (i & 1) { + ngx_md5_update(&ctx1, final, 16); + + } else { + ngx_md5_update(&ctx1, key, keylen); + } + + ngx_md5_final(final, &ctx1); + } + + /* output */ + + *encrypted = ngx_pnalloc(pool, sizeof("$apr1$") - 1 + saltlen + 1 + 22 + 1); + if (*encrypted == NULL) { + return NGX_ERROR; + } + + p = ngx_cpymem(*encrypted, "$apr1$", sizeof("$apr1$") - 1); + p = ngx_copy(p, salt, saltlen); + *p++ = '$'; + + p = ngx_crypt_to64(p, (final[ 0]<<16) | (final[ 6]<<8) | final[12], 4); + p = ngx_crypt_to64(p, (final[ 1]<<16) | (final[ 7]<<8) | final[13], 4); + p = ngx_crypt_to64(p, (final[ 2]<<16) | (final[ 8]<<8) | final[14], 4); + p = ngx_crypt_to64(p, (final[ 3]<<16) | (final[ 9]<<8) | final[15], 4); + p = ngx_crypt_to64(p, (final[ 4]<<16) | (final[10]<<8) | final[ 5], 4); + p = ngx_crypt_to64(p, final[11], 2); + *p = '\0'; + + return NGX_OK; +} + + +static u_char * +ngx_crypt_to64(u_char *p, uint32_t v, size_t n) +{ + static u_char itoa64[] = + "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + + while (n--) { + *p++ = itoa64[v & 0x3f]; + v >>= 6; + } + + return p; +} + + +static ngx_int_t +ngx_crypt_plain(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted) +{ + size_t len; + u_char *p; + + len = ngx_strlen(key); + + *encrypted = ngx_pnalloc(pool, sizeof("{PLAIN}") - 1 + len + 1); + if (*encrypted == NULL) { + return NGX_ERROR; + } + + p = ngx_cpymem(*encrypted, "{PLAIN}", sizeof("{PLAIN}") - 1); + ngx_memcpy(p, key, len + 1); + + return NGX_OK; +} + + +static ngx_int_t +ngx_crypt_ssha(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted) +{ + size_t len; + ngx_int_t rc; + ngx_str_t encoded, decoded; + ngx_sha1_t sha1; + + /* "{SSHA}" base64(SHA1(key salt) salt) */ + + /* decode base64 salt to find out true salt */ + + encoded.data = salt + sizeof("{SSHA}") - 1; + encoded.len = ngx_strlen(encoded.data); + + len = ngx_max(ngx_base64_decoded_length(encoded.len), 20); + + decoded.data = ngx_pnalloc(pool, len); + if (decoded.data == NULL) { + return NGX_ERROR; + } + + rc = ngx_decode_base64(&decoded, &encoded); + + if (rc != NGX_OK || decoded.len < 20) { + decoded.len = 20; + } + + /* update SHA1 from key and salt */ + + ngx_sha1_init(&sha1); + ngx_sha1_update(&sha1, key, ngx_strlen(key)); + ngx_sha1_update(&sha1, decoded.data + 20, decoded.len - 20); + ngx_sha1_final(decoded.data, &sha1); + + /* encode it back to base64 */ + + len = sizeof("{SSHA}") - 1 + ngx_base64_encoded_length(decoded.len) + 1; + + *encrypted = ngx_pnalloc(pool, len); + if (*encrypted == NULL) { + return NGX_ERROR; + } + + encoded.data = ngx_cpymem(*encrypted, "{SSHA}", sizeof("{SSHA}") - 1); + ngx_encode_base64(&encoded, &decoded); + encoded.data[encoded.len] = '\0'; + + return NGX_OK; +} + + +static ngx_int_t +ngx_crypt_sha(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted) +{ + size_t len; + ngx_str_t encoded, decoded; + ngx_sha1_t sha1; + u_char digest[20]; + + /* "{SHA}" base64(SHA1(key)) */ + + decoded.len = sizeof(digest); + decoded.data = digest; + + ngx_sha1_init(&sha1); + ngx_sha1_update(&sha1, key, ngx_strlen(key)); + ngx_sha1_final(digest, &sha1); + + len = sizeof("{SHA}") - 1 + ngx_base64_encoded_length(decoded.len) + 1; + + *encrypted = ngx_pnalloc(pool, len); + if (*encrypted == NULL) { + return NGX_ERROR; + } + + encoded.data = ngx_cpymem(*encrypted, "{SHA}", sizeof("{SHA}") - 1); + ngx_encode_base64(&encoded, &decoded); + encoded.data[encoded.len] = '\0'; + + return NGX_OK; +} + +#endif /* NGX_CRYPT */ diff --git a/app/nginx/src/core/ngx_crypt.h b/app/nginx/src/core/ngx_crypt.h new file mode 100644 index 0000000..3869114 --- /dev/null +++ b/app/nginx/src/core/ngx_crypt.h @@ -0,0 +1,20 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_CRYPT_H_INCLUDED_ +#define _NGX_CRYPT_H_INCLUDED_ + + +#include +#include + + +ngx_int_t ngx_crypt(ngx_pool_t *pool, u_char *key, u_char *salt, + u_char **encrypted); + + +#endif /* _NGX_CRYPT_H_INCLUDED_ */ diff --git a/app/nginx/src/core/ngx_cycle.c b/app/nginx/src/core/ngx_cycle.c new file mode 100644 index 0000000..aee7a58 --- /dev/null +++ b/app/nginx/src/core/ngx_cycle.c @@ -0,0 +1,1384 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +static void ngx_destroy_cycle_pools(ngx_conf_t *conf); +static ngx_int_t ngx_init_zone_pool(ngx_cycle_t *cycle, + ngx_shm_zone_t *shm_zone); +static ngx_int_t ngx_test_lockfile(u_char *file, ngx_log_t *log); +static void ngx_clean_old_cycles(ngx_event_t *ev); +static void ngx_shutdown_timer_handler(ngx_event_t *ev); + + +volatile ngx_cycle_t *ngx_cycle; +ngx_array_t ngx_old_cycles; + +static ngx_pool_t *ngx_temp_pool; +static ngx_event_t ngx_cleaner_event; +static ngx_event_t ngx_shutdown_event; + +ngx_uint_t ngx_test_config; +ngx_uint_t ngx_dump_config; +ngx_uint_t ngx_quiet_mode; + + +/* STUB NAME */ +static ngx_connection_t dumb; +/* STUB */ + + +ngx_cycle_t * +ngx_init_cycle(ngx_cycle_t *old_cycle) +{ + void *rv; + char **senv; + ngx_uint_t i, n; + ngx_log_t *log; + ngx_time_t *tp; + ngx_conf_t conf; + ngx_pool_t *pool; + ngx_cycle_t *cycle, **old; + ngx_shm_zone_t *shm_zone, *oshm_zone; + ngx_list_part_t *part, *opart; + ngx_open_file_t *file; + ngx_listening_t *ls, *nls; + ngx_core_conf_t *ccf, *old_ccf; + ngx_core_module_t *module; + char hostname[NGX_MAXHOSTNAMELEN]; + + ngx_timezone_update(); + + /* force localtime update with a new timezone */ + + tp = ngx_timeofday(); + tp->sec = 0; + + ngx_time_update(); + + + log = old_cycle->log; + + pool = ngx_create_pool(NGX_CYCLE_POOL_SIZE, log); + if (pool == NULL) { + return NULL; + } + pool->log = log; + + cycle = ngx_pcalloc(pool, sizeof(ngx_cycle_t)); + if (cycle == NULL) { + ngx_destroy_pool(pool); + return NULL; + } + + cycle->pool = pool; + cycle->log = log; + cycle->old_cycle = old_cycle; + + cycle->conf_prefix.len = old_cycle->conf_prefix.len; + cycle->conf_prefix.data = ngx_pstrdup(pool, &old_cycle->conf_prefix); + if (cycle->conf_prefix.data == NULL) { + ngx_destroy_pool(pool); + return NULL; + } + + cycle->prefix.len = old_cycle->prefix.len; + cycle->prefix.data = ngx_pstrdup(pool, &old_cycle->prefix); + if (cycle->prefix.data == NULL) { + ngx_destroy_pool(pool); + return NULL; + } + + cycle->conf_file.len = old_cycle->conf_file.len; + cycle->conf_file.data = ngx_pnalloc(pool, old_cycle->conf_file.len + 1); + if (cycle->conf_file.data == NULL) { + ngx_destroy_pool(pool); + return NULL; + } + ngx_cpystrn(cycle->conf_file.data, old_cycle->conf_file.data, + old_cycle->conf_file.len + 1); + + cycle->conf_param.len = old_cycle->conf_param.len; + cycle->conf_param.data = ngx_pstrdup(pool, &old_cycle->conf_param); + if (cycle->conf_param.data == NULL) { + ngx_destroy_pool(pool); + return NULL; + } + + + n = old_cycle->paths.nelts ? old_cycle->paths.nelts : 10; + + if (ngx_array_init(&cycle->paths, pool, n, sizeof(ngx_path_t *)) + != NGX_OK) + { + ngx_destroy_pool(pool); + return NULL; + } + + ngx_memzero(cycle->paths.elts, n * sizeof(ngx_path_t *)); + + + if (ngx_array_init(&cycle->config_dump, pool, 1, sizeof(ngx_conf_dump_t)) + != NGX_OK) + { + ngx_destroy_pool(pool); + return NULL; + } + + ngx_rbtree_init(&cycle->config_dump_rbtree, &cycle->config_dump_sentinel, + ngx_str_rbtree_insert_value); + + if (old_cycle->open_files.part.nelts) { + n = old_cycle->open_files.part.nelts; + for (part = old_cycle->open_files.part.next; part; part = part->next) { + n += part->nelts; + } + + } else { + n = 20; + } + + if (ngx_list_init(&cycle->open_files, pool, n, sizeof(ngx_open_file_t)) + != NGX_OK) + { + ngx_destroy_pool(pool); + return NULL; + } + + + if (old_cycle->shared_memory.part.nelts) { + n = old_cycle->shared_memory.part.nelts; + for (part = old_cycle->shared_memory.part.next; part; part = part->next) + { + n += part->nelts; + } + + } else { + n = 1; + } + + if (ngx_list_init(&cycle->shared_memory, pool, n, sizeof(ngx_shm_zone_t)) + != NGX_OK) + { + ngx_destroy_pool(pool); + return NULL; + } + + n = old_cycle->listening.nelts ? old_cycle->listening.nelts : 10; + + if (ngx_array_init(&cycle->listening, pool, n, sizeof(ngx_listening_t)) + != NGX_OK) + { + ngx_destroy_pool(pool); + return NULL; + } + + ngx_memzero(cycle->listening.elts, n * sizeof(ngx_listening_t)); + + + ngx_queue_init(&cycle->reusable_connections_queue); + + + cycle->conf_ctx = ngx_pcalloc(pool, ngx_max_module * sizeof(void *)); + if (cycle->conf_ctx == NULL) { + ngx_destroy_pool(pool); + return NULL; + } + + + if (gethostname(hostname, NGX_MAXHOSTNAMELEN) == -1) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "gethostname() failed"); + ngx_destroy_pool(pool); + return NULL; + } + + /* on Linux gethostname() silently truncates name that does not fit */ + + hostname[NGX_MAXHOSTNAMELEN - 1] = '\0'; + cycle->hostname.len = ngx_strlen(hostname); + + cycle->hostname.data = ngx_pnalloc(pool, cycle->hostname.len); + if (cycle->hostname.data == NULL) { + ngx_destroy_pool(pool); + return NULL; + } + + ngx_strlow(cycle->hostname.data, (u_char *) hostname, cycle->hostname.len); + + + if (ngx_cycle_modules(cycle) != NGX_OK) { + ngx_destroy_pool(pool); + return NULL; + } + + + for (i = 0; cycle->modules[i]; i++) { + if (cycle->modules[i]->type != NGX_CORE_MODULE) { + continue; + } + + module = cycle->modules[i]->ctx; + + if (module->create_conf) { + rv = module->create_conf(cycle); + if (rv == NULL) { + ngx_destroy_pool(pool); + return NULL; + } + cycle->conf_ctx[cycle->modules[i]->index] = rv; + } + } + + + senv = environ; + + + ngx_memzero(&conf, sizeof(ngx_conf_t)); + /* STUB: init array ? */ + conf.args = ngx_array_create(pool, 10, sizeof(ngx_str_t)); + if (conf.args == NULL) { + ngx_destroy_pool(pool); + return NULL; + } + + conf.temp_pool = ngx_create_pool(NGX_CYCLE_POOL_SIZE, log); + if (conf.temp_pool == NULL) { + ngx_destroy_pool(pool); + return NULL; + } + + + conf.ctx = cycle->conf_ctx; + conf.cycle = cycle; + conf.pool = pool; + conf.log = log; + conf.module_type = NGX_CORE_MODULE; + conf.cmd_type = NGX_MAIN_CONF; + +#if 0 + log->log_level = NGX_LOG_DEBUG_ALL; +#endif + + if (ngx_conf_param(&conf) != NGX_CONF_OK) { + environ = senv; + ngx_destroy_cycle_pools(&conf); + return NULL; + } + + if (ngx_conf_parse(&conf, &cycle->conf_file) != NGX_CONF_OK) { + environ = senv; + ngx_destroy_cycle_pools(&conf); + return NULL; + } + + if (ngx_test_config && !ngx_quiet_mode) { + ngx_log_stderr(0, "the configuration file %s syntax is ok", + cycle->conf_file.data); + } + + for (i = 0; cycle->modules[i]; i++) { + if (cycle->modules[i]->type != NGX_CORE_MODULE) { + continue; + } + + module = cycle->modules[i]->ctx; + + if (module->init_conf) { + if (module->init_conf(cycle, + cycle->conf_ctx[cycle->modules[i]->index]) + == NGX_CONF_ERROR) + { + environ = senv; + ngx_destroy_cycle_pools(&conf); + return NULL; + } + } + } + + if (ngx_process == NGX_PROCESS_SIGNALLER) { + return cycle; + } + + ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module); + + if (ngx_test_config) { + + if (ngx_create_pidfile(&ccf->pid, log) != NGX_OK) { + goto failed; + } + + } else if (!ngx_is_init_cycle(old_cycle)) { + + /* + * we do not create the pid file in the first ngx_init_cycle() call + * because we need to write the demonized process pid + */ + + old_ccf = (ngx_core_conf_t *) ngx_get_conf(old_cycle->conf_ctx, + ngx_core_module); + if (ccf->pid.len != old_ccf->pid.len + || ngx_strcmp(ccf->pid.data, old_ccf->pid.data) != 0) + { + /* new pid file name */ + + if (ngx_create_pidfile(&ccf->pid, log) != NGX_OK) { + goto failed; + } + + ngx_delete_pidfile(old_cycle); + } + } + + + if (ngx_test_lockfile(cycle->lock_file.data, log) != NGX_OK) { + goto failed; + } + + + if (ngx_create_paths(cycle, ccf->user) != NGX_OK) { + goto failed; + } + + + if (ngx_log_open_default(cycle) != NGX_OK) { + goto failed; + } + + /* open the new files */ + + part = &cycle->open_files.part; + file = part->elts; + + for (i = 0; /* void */ ; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + part = part->next; + file = part->elts; + i = 0; + } + + if (file[i].name.len == 0) { + continue; + } + + file[i].fd = ngx_open_file(file[i].name.data, + NGX_FILE_APPEND, + NGX_FILE_CREATE_OR_OPEN, + NGX_FILE_DEFAULT_ACCESS); + + ngx_log_debug3(NGX_LOG_DEBUG_CORE, log, 0, + "log: %p %d \"%s\"", + &file[i], file[i].fd, file[i].name.data); + + if (file[i].fd == NGX_INVALID_FILE) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, + ngx_open_file_n " \"%s\" failed", + file[i].name.data); + goto failed; + } + +#if !(NGX_WIN32) + if (fcntl(file[i].fd, F_SETFD, FD_CLOEXEC) == -1) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, + "fcntl(FD_CLOEXEC) \"%s\" failed", + file[i].name.data); + goto failed; + } +#endif + } + + cycle->log = &cycle->new_log; + pool->log = &cycle->new_log; + + + /* create shared memory */ + + part = &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 (shm_zone[i].shm.size == 0) { + ngx_log_error(NGX_LOG_EMERG, log, 0, + "zero size shared memory zone \"%V\"", + &shm_zone[i].shm.name); + goto failed; + } + + shm_zone[i].shm.log = cycle->log; + + opart = &old_cycle->shared_memory.part; + oshm_zone = opart->elts; + + for (n = 0; /* void */ ; n++) { + + if (n >= opart->nelts) { + if (opart->next == NULL) { + break; + } + opart = opart->next; + oshm_zone = opart->elts; + n = 0; + } + + if (shm_zone[i].shm.name.len != oshm_zone[n].shm.name.len) { + continue; + } + + if (ngx_strncmp(shm_zone[i].shm.name.data, + oshm_zone[n].shm.name.data, + shm_zone[i].shm.name.len) + != 0) + { + continue; + } + + if (shm_zone[i].tag == oshm_zone[n].tag + && shm_zone[i].shm.size == oshm_zone[n].shm.size + && !shm_zone[i].noreuse) + { + shm_zone[i].shm.addr = oshm_zone[n].shm.addr; +#if (NGX_WIN32) + shm_zone[i].shm.handle = oshm_zone[n].shm.handle; +#endif + + if (shm_zone[i].init(&shm_zone[i], oshm_zone[n].data) + != NGX_OK) + { + goto failed; + } + + goto shm_zone_found; + } + + ngx_shm_free(&oshm_zone[n].shm); + + break; + } + + if (ngx_shm_alloc(&shm_zone[i].shm) != NGX_OK) { + goto failed; + } + + if (ngx_init_zone_pool(cycle, &shm_zone[i]) != NGX_OK) { + goto failed; + } + + if (shm_zone[i].init(&shm_zone[i], NULL) != NGX_OK) { + goto failed; + } + + shm_zone_found: + + continue; + } + + + /* handle the listening sockets */ + + if (old_cycle->listening.nelts) { + ls = old_cycle->listening.elts; + for (i = 0; i < old_cycle->listening.nelts; i++) { + ls[i].remain = 0; + } + + nls = cycle->listening.elts; + for (n = 0; n < cycle->listening.nelts; n++) { + + for (i = 0; i < old_cycle->listening.nelts; i++) { + if (ls[i].ignore) { + continue; + } + + if (ls[i].remain) { + continue; + } + + if (ls[i].type != nls[n].type) { + continue; + } + + if (ngx_cmp_sockaddr(nls[n].sockaddr, nls[n].socklen, + ls[i].sockaddr, ls[i].socklen, 1) + == NGX_OK) + { + nls[n].fd = ls[i].fd; + nls[n].previous = &ls[i]; + ls[i].remain = 1; + + if (ls[i].backlog != nls[n].backlog) { + nls[n].listen = 1; + } + +#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER) + + /* + * FreeBSD, except the most recent versions, + * could not remove accept filter + */ + nls[n].deferred_accept = ls[i].deferred_accept; + + if (ls[i].accept_filter && nls[n].accept_filter) { + if (ngx_strcmp(ls[i].accept_filter, + nls[n].accept_filter) + != 0) + { + nls[n].delete_deferred = 1; + nls[n].add_deferred = 1; + } + + } else if (ls[i].accept_filter) { + nls[n].delete_deferred = 1; + + } else if (nls[n].accept_filter) { + nls[n].add_deferred = 1; + } +#endif + +#if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT) + + if (ls[i].deferred_accept && !nls[n].deferred_accept) { + nls[n].delete_deferred = 1; + + } else if (ls[i].deferred_accept != nls[n].deferred_accept) + { + nls[n].add_deferred = 1; + } +#endif + +#if (NGX_HAVE_REUSEPORT) + if (nls[n].reuseport && !ls[i].reuseport) { + nls[n].add_reuseport = 1; + } +#endif + + break; + } + } + + if (nls[n].fd == (ngx_socket_t) -1) { + nls[n].open = 1; +#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER) + if (nls[n].accept_filter) { + nls[n].add_deferred = 1; + } +#endif +#if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT) + if (nls[n].deferred_accept) { + nls[n].add_deferred = 1; + } +#endif + } + } + + } else { + ls = cycle->listening.elts; + for (i = 0; i < cycle->listening.nelts; i++) { + ls[i].open = 1; +#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER) + if (ls[i].accept_filter) { + ls[i].add_deferred = 1; + } +#endif +#if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT) + if (ls[i].deferred_accept) { + ls[i].add_deferred = 1; + } +#endif + } + } + + if (ngx_open_listening_sockets(cycle) != NGX_OK) { + goto failed; + } + + if (!ngx_test_config) { + ngx_configure_listening_sockets(cycle); + } + + + /* commit the new cycle configuration */ + + if (!ngx_use_stderr) { + (void) ngx_log_redirect_stderr(cycle); + } + + pool->log = cycle->log; + + if (ngx_init_modules(cycle) != NGX_OK) { + /* fatal */ + exit(1); + } + + + /* close and delete stuff that lefts from an old cycle */ + + /* 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].shm.name.len == shm_zone[n].shm.name.len + && ngx_strncmp(oshm_zone[i].shm.name.data, + shm_zone[n].shm.name.data, + oshm_zone[i].shm.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++) { + + if (ls[i].remain || ls[i].fd == (ngx_socket_t) -1) { + continue; + } + + if (ngx_close_socket(ls[i].fd) == -1) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno, + ngx_close_socket_n " listening socket on %V failed", + &ls[i].addr_text); + } + +#if (NGX_HAVE_UNIX_DOMAIN) + + if (ls[i].sockaddr->sa_family == AF_UNIX) { + u_char *name; + + name = ls[i].addr_text.data + sizeof("unix:") - 1; + + ngx_log_error(NGX_LOG_WARN, cycle->log, 0, + "deleting socket %s", name); + + if (ngx_delete_file(name) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_socket_errno, + ngx_delete_file_n " %s failed", name); + } + } + +#endif + } + + + /* close the unnecessary open files */ + + part = &old_cycle->open_files.part; + file = part->elts; + + for (i = 0; /* void */ ; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + part = part->next; + file = part->elts; + i = 0; + } + + if (file[i].fd == NGX_INVALID_FILE || file[i].fd == ngx_stderr) { + continue; + } + + if (ngx_close_file(file[i].fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, + ngx_close_file_n " \"%s\" failed", + file[i].name.data); + } + } + + ngx_destroy_pool(conf.temp_pool); + + if (ngx_process == NGX_PROCESS_MASTER || ngx_is_init_cycle(old_cycle)) { + + ngx_destroy_pool(old_cycle->pool); + cycle->old_cycle = NULL; + + return cycle; + } + + + if (ngx_temp_pool == NULL) { + ngx_temp_pool = ngx_create_pool(128, cycle->log); + if (ngx_temp_pool == NULL) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, + "could not create ngx_temp_pool"); + exit(1); + } + + n = 10; + + if (ngx_array_init(&ngx_old_cycles, ngx_temp_pool, n, + sizeof(ngx_cycle_t *)) + != NGX_OK) + { + exit(1); + } + + ngx_memzero(ngx_old_cycles.elts, n * sizeof(ngx_cycle_t *)); + + ngx_cleaner_event.handler = ngx_clean_old_cycles; + ngx_cleaner_event.log = cycle->log; + ngx_cleaner_event.data = &dumb; + dumb.fd = (ngx_socket_t) -1; + } + + ngx_temp_pool->log = cycle->log; + + old = ngx_array_push(&ngx_old_cycles); + if (old == NULL) { + exit(1); + } + *old = old_cycle; + + if (!ngx_cleaner_event.timer_set) { + ngx_add_timer(&ngx_cleaner_event, 30000); + ngx_cleaner_event.timer_set = 1; + } + + return cycle; + + +failed: + + if (!ngx_is_init_cycle(old_cycle)) { + old_ccf = (ngx_core_conf_t *) ngx_get_conf(old_cycle->conf_ctx, + ngx_core_module); + if (old_ccf->environment) { + environ = old_ccf->environment; + } + } + + /* rollback the new cycle configuration */ + + part = &cycle->open_files.part; + file = part->elts; + + for (i = 0; /* void */ ; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + part = part->next; + file = part->elts; + i = 0; + } + + if (file[i].fd == NGX_INVALID_FILE || file[i].fd == ngx_stderr) { + continue; + } + + if (ngx_close_file(file[i].fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, + ngx_close_file_n " \"%s\" failed", + file[i].name.data); + } + } + + if (ngx_test_config) { + ngx_destroy_cycle_pools(&conf); + return NULL; + } + + ls = cycle->listening.elts; + for (i = 0; i < cycle->listening.nelts; i++) { + if (ls[i].fd == (ngx_socket_t) -1 || !ls[i].open) { + continue; + } + + if (ngx_close_socket(ls[i].fd) == -1) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno, + ngx_close_socket_n " %V failed", + &ls[i].addr_text); + } + } + + ngx_destroy_cycle_pools(&conf); + + return NULL; +} + + +static void +ngx_destroy_cycle_pools(ngx_conf_t *conf) +{ + ngx_destroy_pool(conf->temp_pool); + ngx_destroy_pool(conf->pool); +} + + +static ngx_int_t +ngx_init_zone_pool(ngx_cycle_t *cycle, ngx_shm_zone_t *zn) +{ + u_char *file; + ngx_slab_pool_t *sp; + + sp = (ngx_slab_pool_t *) zn->shm.addr; + + if (zn->shm.exists) { + + if (sp == sp->addr) { + return NGX_OK; + } + +#if (NGX_WIN32) + + /* remap at the required address */ + + if (ngx_shm_remap(&zn->shm, sp->addr) != NGX_OK) { + return NGX_ERROR; + } + + sp = (ngx_slab_pool_t *) zn->shm.addr; + + if (sp == sp->addr) { + return NGX_OK; + } + +#endif + + ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, + "shared zone \"%V\" has no equal addresses: %p vs %p", + &zn->shm.name, sp->addr, sp); + return NGX_ERROR; + } + + sp->end = zn->shm.addr + zn->shm.size; + sp->min_shift = 3; + sp->addr = zn->shm.addr; + +#if (NGX_HAVE_ATOMIC_OPS) + + file = NULL; + +#else + + file = ngx_pnalloc(cycle->pool, cycle->lock_file.len + zn->shm.name.len); + if (file == NULL) { + return NGX_ERROR; + } + + (void) ngx_sprintf(file, "%V%V%Z", &cycle->lock_file, &zn->shm.name); + +#endif + + if (ngx_shmtx_create(&sp->mutex, &sp->lock, file) != NGX_OK) { + return NGX_ERROR; + } + + ngx_slab_init(sp); + + return NGX_OK; +} + + +ngx_int_t +ngx_create_pidfile(ngx_str_t *name, ngx_log_t *log) +{ + size_t len; + ngx_uint_t create; + ngx_file_t file; + u_char pid[NGX_INT64_LEN + 2]; + + if (ngx_process > NGX_PROCESS_MASTER) { + return NGX_OK; + } + + ngx_memzero(&file, sizeof(ngx_file_t)); + + file.name = *name; + file.log = log; + + create = ngx_test_config ? NGX_FILE_CREATE_OR_OPEN : NGX_FILE_TRUNCATE; + + file.fd = ngx_open_file(file.name.data, NGX_FILE_RDWR, + create, NGX_FILE_DEFAULT_ACCESS); + + if (file.fd == NGX_INVALID_FILE) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, + ngx_open_file_n " \"%s\" failed", file.name.data); + return NGX_ERROR; + } + + if (!ngx_test_config) { + len = ngx_snprintf(pid, NGX_INT64_LEN + 2, "%P%N", ngx_pid) - pid; + + if (ngx_write_file(&file, pid, len, 0) == NGX_ERROR) { + return NGX_ERROR; + } + } + + if (ngx_close_file(file.fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + ngx_close_file_n " \"%s\" failed", file.name.data); + } + + return NGX_OK; +} + + +void +ngx_delete_pidfile(ngx_cycle_t *cycle) +{ + u_char *name; + ngx_core_conf_t *ccf; + + ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module); + + name = ngx_new_binary ? ccf->oldpid.data : ccf->pid.data; + + if (ngx_delete_file(name) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + ngx_delete_file_n " \"%s\" failed", name); + } +} + + +ngx_int_t +ngx_signal_process(ngx_cycle_t *cycle, char *sig) +{ + ssize_t n; + ngx_pid_t pid; + ngx_file_t file; + ngx_core_conf_t *ccf; + u_char buf[NGX_INT64_LEN + 2]; + + ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "signal process started"); + + ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module); + + ngx_memzero(&file, sizeof(ngx_file_t)); + + file.name = ccf->pid; + file.log = cycle->log; + + file.fd = ngx_open_file(file.name.data, NGX_FILE_RDONLY, + NGX_FILE_OPEN, NGX_FILE_DEFAULT_ACCESS); + + if (file.fd == NGX_INVALID_FILE) { + ngx_log_error(NGX_LOG_ERR, cycle->log, ngx_errno, + ngx_open_file_n " \"%s\" failed", file.name.data); + return 1; + } + + n = ngx_read_file(&file, buf, NGX_INT64_LEN + 2, 0); + + if (ngx_close_file(file.fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + ngx_close_file_n " \"%s\" failed", file.name.data); + } + + if (n == NGX_ERROR) { + return 1; + } + + while (n-- && (buf[n] == CR || buf[n] == LF)) { /* void */ } + + pid = ngx_atoi(buf, ++n); + + if (pid == (ngx_pid_t) NGX_ERROR) { + ngx_log_error(NGX_LOG_ERR, cycle->log, 0, + "invalid PID number \"%*s\" in \"%s\"", + n, buf, file.name.data); + return 1; + } + + return ngx_os_signal_process(cycle, sig, pid); + +} + + +static ngx_int_t +ngx_test_lockfile(u_char *file, ngx_log_t *log) +{ +#if !(NGX_HAVE_ATOMIC_OPS) + ngx_fd_t fd; + + fd = ngx_open_file(file, NGX_FILE_RDWR, NGX_FILE_CREATE_OR_OPEN, + NGX_FILE_DEFAULT_ACCESS); + + if (fd == NGX_INVALID_FILE) { + ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, + ngx_open_file_n " \"%s\" failed", file); + return NGX_ERROR; + } + + if (ngx_close_file(fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + ngx_close_file_n " \"%s\" failed", file); + } + + if (ngx_delete_file(file) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + ngx_delete_file_n " \"%s\" failed", file); + } + +#endif + + return NGX_OK; +} + + +void +ngx_reopen_files(ngx_cycle_t *cycle, ngx_uid_t user) +{ + ngx_fd_t fd; + ngx_uint_t i; + ngx_list_part_t *part; + ngx_open_file_t *file; + + part = &cycle->open_files.part; + file = part->elts; + + for (i = 0; /* void */ ; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + part = part->next; + file = part->elts; + i = 0; + } + + if (file[i].name.len == 0) { + continue; + } + + if (file[i].flush) { + file[i].flush(&file[i], cycle->log); + } + + fd = ngx_open_file(file[i].name.data, NGX_FILE_APPEND, + NGX_FILE_CREATE_OR_OPEN, NGX_FILE_DEFAULT_ACCESS); + + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, cycle->log, 0, + "reopen file \"%s\", old:%d new:%d", + file[i].name.data, file[i].fd, fd); + + if (fd == NGX_INVALID_FILE) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, + ngx_open_file_n " \"%s\" failed", file[i].name.data); + continue; + } + +#if !(NGX_WIN32) + if (user != (ngx_uid_t) NGX_CONF_UNSET_UINT) { + ngx_file_info_t fi; + + if (ngx_file_info((const char *) file[i].name.data, &fi) + == NGX_FILE_ERROR) + { + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, + ngx_file_info_n " \"%s\" failed", + file[i].name.data); + + if (ngx_close_file(fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, + ngx_close_file_n " \"%s\" failed", + file[i].name.data); + } + + continue; + } + + if (fi.st_uid != user) { + if (chown((const char *) file[i].name.data, user, -1) == -1) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, + "chown(\"%s\", %d) failed", + file[i].name.data, user); + + if (ngx_close_file(fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, + ngx_close_file_n " \"%s\" failed", + file[i].name.data); + } + + continue; + } + } + + if ((fi.st_mode & (S_IRUSR|S_IWUSR)) != (S_IRUSR|S_IWUSR)) { + + fi.st_mode |= (S_IRUSR|S_IWUSR); + + if (chmod((const char *) file[i].name.data, fi.st_mode) == -1) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, + "chmod() \"%s\" failed", file[i].name.data); + + if (ngx_close_file(fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, + ngx_close_file_n " \"%s\" failed", + file[i].name.data); + } + + continue; + } + } + } + + if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, + "fcntl(FD_CLOEXEC) \"%s\" failed", + file[i].name.data); + + if (ngx_close_file(fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, + ngx_close_file_n " \"%s\" failed", + file[i].name.data); + } + + continue; + } +#endif + + if (ngx_close_file(file[i].fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, + ngx_close_file_n " \"%s\" failed", + file[i].name.data); + } + + file[i].fd = fd; + } + + (void) ngx_log_redirect_stderr(cycle); +} + + +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].shm.name.len) { + continue; + } + + if (ngx_strncmp(name->data, shm_zone[i].shm.name.data, name->len) + != 0) + { + continue; + } + + 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].shm.name); + return NULL; + } + + if (shm_zone[i].shm.size == 0) { + shm_zone[i].shm.size = size; + } + + 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].shm.name, shm_zone[i].shm.size); + 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->shm.name = *name; + shm_zone->shm.exists = 0; + shm_zone->init = NULL; + shm_zone->tag = tag; + shm_zone->noreuse = 0; + + return shm_zone; +} + + +static void +ngx_clean_old_cycles(ngx_event_t *ev) +{ + ngx_uint_t i, n, found, live; + ngx_log_t *log; + ngx_cycle_t **cycle; + + log = ngx_cycle->log; + ngx_temp_pool->log = log; + + ngx_log_debug0(NGX_LOG_DEBUG_CORE, log, 0, "clean old cycles"); + + live = 0; + + cycle = ngx_old_cycles.elts; + for (i = 0; i < ngx_old_cycles.nelts; i++) { + + if (cycle[i] == NULL) { + continue; + } + + found = 0; + + for (n = 0; n < cycle[i]->connection_n; n++) { + if (cycle[i]->connections[n].fd != (ngx_socket_t) -1) { + found = 1; + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, log, 0, "live fd:%ui", n); + + break; + } + } + + if (found) { + live = 1; + continue; + } + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, log, 0, "clean old cycle: %ui", i); + + ngx_destroy_pool(cycle[i]->pool); + cycle[i] = NULL; + } + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, log, 0, "old cycles status: %ui", live); + + if (live) { + ngx_add_timer(ev, 30000); + + } else { + ngx_destroy_pool(ngx_temp_pool); + ngx_temp_pool = NULL; + ngx_old_cycles.nelts = 0; + } +} + + +void +ngx_set_shutdown_timer(ngx_cycle_t *cycle) +{ + ngx_core_conf_t *ccf; + + ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module); + + if (ccf->shutdown_timeout) { + ngx_shutdown_event.handler = ngx_shutdown_timer_handler; + ngx_shutdown_event.data = cycle; + ngx_shutdown_event.log = cycle->log; + ngx_shutdown_event.cancelable = 1; + + ngx_add_timer(&ngx_shutdown_event, ccf->shutdown_timeout); + } +} + + +static void +ngx_shutdown_timer_handler(ngx_event_t *ev) +{ + ngx_uint_t i; + ngx_cycle_t *cycle; + ngx_connection_t *c; + + cycle = ev->data; + + c = cycle->connections; + + for (i = 0; i < cycle->connection_n; i++) { + + if (c[i].fd == (ngx_socket_t) -1 + || c[i].read == NULL + || c[i].read->accept + || c[i].read->channel + || c[i].read->resolver) + { + continue; + } + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, ev->log, 0, + "*%uA shutdown timeout", c[i].number); + + c[i].close = 1; + c[i].error = 1; + + c[i].read->handler(c[i].read); + } +} diff --git a/app/nginx/src/core/ngx_cycle.h b/app/nginx/src/core/ngx_cycle.h new file mode 100644 index 0000000..2b48ccb --- /dev/null +++ b/app/nginx/src/core/ngx_cycle.h @@ -0,0 +1,144 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_CYCLE_H_INCLUDED_ +#define _NGX_CYCLE_H_INCLUDED_ + + +#include +#include + + +#ifndef NGX_CYCLE_POOL_SIZE +#define NGX_CYCLE_POOL_SIZE NGX_DEFAULT_POOL_SIZE +#endif + + +#define NGX_DEBUG_POINTS_STOP 1 +#define NGX_DEBUG_POINTS_ABORT 2 + + +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; + void *tag; + ngx_uint_t noreuse; /* unsigned noreuse:1; */ +}; + + +struct ngx_cycle_s { + void ****conf_ctx; + ngx_pool_t *pool; + + ngx_log_t *log; + ngx_log_t new_log; + + ngx_uint_t log_use_stderr; /* unsigned log_use_stderr:1; */ + + ngx_connection_t **files; + ngx_connection_t *free_connections; + ngx_uint_t free_connection_n; + + ngx_module_t **modules; + ngx_uint_t modules_n; + ngx_uint_t modules_used; /* unsigned modules_used:1; */ + + ngx_queue_t reusable_connections_queue; + ngx_uint_t reusable_connections_n; + + ngx_array_t listening; + ngx_array_t paths; + + ngx_array_t config_dump; + ngx_rbtree_t config_dump_rbtree; + ngx_rbtree_node_t config_dump_sentinel; + + ngx_list_t open_files; + ngx_list_t shared_memory; + + ngx_uint_t connection_n; + ngx_uint_t files_n; + + ngx_connection_t *connections; + ngx_event_t *read_events; + ngx_event_t *write_events; + + ngx_cycle_t *old_cycle; + + ngx_str_t conf_file; + ngx_str_t conf_param; + ngx_str_t conf_prefix; + ngx_str_t prefix; + ngx_str_t lock_file; + ngx_str_t hostname; +}; + + +typedef struct { + ngx_flag_t daemon; + ngx_flag_t master; + + ngx_msec_t timer_resolution; + ngx_msec_t shutdown_timeout; + + ngx_int_t worker_processes; + ngx_int_t debug_points; + + ngx_int_t rlimit_nofile; + off_t rlimit_core; + + int priority; + + ngx_uint_t cpu_affinity_auto; + ngx_uint_t cpu_affinity_n; + ngx_cpuset_t *cpu_affinity; + + char *username; + ngx_uid_t user; + ngx_gid_t group; + + ngx_str_t working_directory; + ngx_str_t lock_file; + + ngx_str_t pid; + ngx_str_t oldpid; + + ngx_array_t env; + char **environment; +} ngx_core_conf_t; + + +#define ngx_is_init_cycle(cycle) (cycle->conf_ctx == NULL) + + +ngx_cycle_t *ngx_init_cycle(ngx_cycle_t *old_cycle); +ngx_int_t ngx_create_pidfile(ngx_str_t *name, ngx_log_t *log); +void ngx_delete_pidfile(ngx_cycle_t *cycle); +ngx_int_t ngx_signal_process(ngx_cycle_t *cycle, char *sig); +void ngx_reopen_files(ngx_cycle_t *cycle, ngx_uid_t user); +char **ngx_set_environment(ngx_cycle_t *cycle, ngx_uint_t *last); +ngx_pid_t ngx_exec_new_binary(ngx_cycle_t *cycle, char *const *argv); +ngx_cpuset_t *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); +void ngx_set_shutdown_timer(ngx_cycle_t *cycle); + + +extern volatile ngx_cycle_t *ngx_cycle; +extern ngx_array_t ngx_old_cycles; +extern ngx_module_t ngx_core_module; +extern ngx_uint_t ngx_test_config; +extern ngx_uint_t ngx_dump_config; +extern ngx_uint_t ngx_quiet_mode; + + +#endif /* _NGX_CYCLE_H_INCLUDED_ */ diff --git a/app/nginx/src/core/ngx_file.c b/app/nginx/src/core/ngx_file.c new file mode 100644 index 0000000..b7dd4bc --- /dev/null +++ b/app/nginx/src/core/ngx_file.c @@ -0,0 +1,1127 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include + + +static ngx_int_t ngx_test_full_name(ngx_str_t *name); + + +static ngx_atomic_t temp_number = 0; +ngx_atomic_t *ngx_temp_number = &temp_number; +ngx_atomic_int_t ngx_random_number = 123456; + + +ngx_int_t +ngx_get_full_name(ngx_pool_t *pool, ngx_str_t *prefix, ngx_str_t *name) +{ + size_t len; + u_char *p, *n; + ngx_int_t rc; + + rc = ngx_test_full_name(name); + + if (rc == NGX_OK) { + return rc; + } + + len = prefix->len; + +#if (NGX_WIN32) + + if (rc == 2) { + len = rc; + } + +#endif + + n = ngx_pnalloc(pool, len + name->len + 1); + if (n == NULL) { + return NGX_ERROR; + } + + p = ngx_cpymem(n, prefix->data, len); + ngx_cpystrn(p, name->data, name->len + 1); + + name->len += len; + name->data = n; + + return NGX_OK; +} + + +static ngx_int_t +ngx_test_full_name(ngx_str_t *name) +{ +#if (NGX_WIN32) + u_char c0, c1; + + c0 = name->data[0]; + + if (name->len < 2) { + if (c0 == '/') { + return 2; + } + + return NGX_DECLINED; + } + + c1 = name->data[1]; + + if (c1 == ':') { + c0 |= 0x20; + + if ((c0 >= 'a' && c0 <= 'z')) { + return NGX_OK; + } + + return NGX_DECLINED; + } + + if (c1 == '/') { + return NGX_OK; + } + + if (c0 == '/') { + return 2; + } + + return NGX_DECLINED; + +#else + + if (name->data[0] == '/') { + return NGX_OK; + } + + return NGX_DECLINED; + +#endif +} + + +ssize_t +ngx_write_chain_to_temp_file(ngx_temp_file_t *tf, ngx_chain_t *chain) +{ + ngx_int_t rc; + + if (tf->file.fd == NGX_INVALID_FILE) { + rc = ngx_create_temp_file(&tf->file, tf->path, tf->pool, + tf->persistent, tf->clean, tf->access); + + if (rc != NGX_OK) { + return rc; + } + + if (tf->log_level) { + ngx_log_error(tf->log_level, tf->file.log, 0, "%s %V", + tf->warn, &tf->file.name); + } + } + +#if (NGX_THREADS && NGX_HAVE_PWRITEV) + + if (tf->thread_write) { + return ngx_thread_write_chain_to_file(&tf->file, chain, tf->offset, + tf->pool); + } + +#endif + + return ngx_write_chain_to_file(&tf->file, chain, tf->offset, tf->pool); +} + + +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 clean, ngx_uint_t access) +{ + size_t levels; + u_char *p; + uint32_t n; + ngx_err_t err; + ngx_str_t name; + ngx_uint_t prefix; + ngx_pool_cleanup_t *cln; + ngx_pool_cleanup_file_t *clnf; + + if (file->name.len) { + name = file->name; + levels = 0; + prefix = 1; + + } else { + name = path->name; + levels = path->len; + prefix = 0; + } + + file->name.len = name.len + 1 + levels + 10; + + file->name.data = ngx_pnalloc(pool, file->name.len + 1); + if (file->name.data == NULL) { + return NGX_ERROR; + } + +#if 0 + for (i = 0; i < file->name.len; i++) { + file->name.data[i] = 'X'; + } +#endif + + p = ngx_cpymem(file->name.data, name.data, name.len); + + if (prefix) { + *p = '.'; + } + + p += 1 + levels; + + n = (uint32_t) ngx_next_temp_number(0); + + cln = ngx_pool_cleanup_add(pool, sizeof(ngx_pool_cleanup_file_t)); + if (cln == NULL) { + return NGX_ERROR; + } + + for ( ;; ) { + (void) ngx_sprintf(p, "%010uD%Z", n); + + if (!prefix) { + ngx_create_hashed_filename(path, file->name.data, file->name.len); + } + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, file->log, 0, + "hashed path: %s", file->name.data); + + file->fd = ngx_open_tempfile(file->name.data, persistent, access); + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, file->log, 0, + "temp fd:%d", file->fd); + + if (file->fd != NGX_INVALID_FILE) { + + cln->handler = clean ? ngx_pool_delete_file : ngx_pool_cleanup_file; + clnf = cln->data; + + clnf->fd = file->fd; + clnf->name = file->name.data; + clnf->log = pool->log; + + return NGX_OK; + } + + err = ngx_errno; + + if (err == NGX_EEXIST_FILE) { + n = (uint32_t) ngx_next_temp_number(1); + continue; + } + + if ((path->level[0] == 0) || (err != NGX_ENOPATH)) { + ngx_log_error(NGX_LOG_CRIT, file->log, err, + ngx_open_tempfile_n " \"%s\" failed", + file->name.data); + return NGX_ERROR; + } + + if (ngx_create_path(file, path) == NGX_ERROR) { + return NGX_ERROR; + } + } +} + + +void +ngx_create_hashed_filename(ngx_path_t *path, u_char *file, size_t len) +{ + size_t i, level; + ngx_uint_t n; + + i = path->name.len + 1; + + file[path->name.len + path->len] = '/'; + + for (n = 0; n < NGX_MAX_PATH_LEVEL; n++) { + level = path->level[n]; + + if (level == 0) { + break; + } + + len -= level; + file[i - 1] = '/'; + ngx_memcpy(&file[i], &file[len], level); + i += level + 1; + } +} + + +ngx_int_t +ngx_create_path(ngx_file_t *file, ngx_path_t *path) +{ + size_t pos; + ngx_err_t err; + ngx_uint_t i; + + pos = path->name.len; + + for (i = 0; i < NGX_MAX_PATH_LEVEL; i++) { + if (path->level[i] == 0) { + break; + } + + pos += path->level[i] + 1; + + file->name.data[pos] = '\0'; + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, file->log, 0, + "temp file: \"%s\"", file->name.data); + + if (ngx_create_dir(file->name.data, 0700) == NGX_FILE_ERROR) { + err = ngx_errno; + if (err != NGX_EEXIST) { + ngx_log_error(NGX_LOG_CRIT, file->log, err, + ngx_create_dir_n " \"%s\" failed", + file->name.data); + return NGX_ERROR; + } + } + + file->name.data[pos] = '/'; + } + + return NGX_OK; +} + + +ngx_err_t +ngx_create_full_path(u_char *dir, ngx_uint_t access) +{ + u_char *p, ch; + ngx_err_t err; + + err = 0; + +#if (NGX_WIN32) + p = dir + 3; +#else + p = dir + 1; +#endif + + for ( /* void */ ; *p; p++) { + ch = *p; + + if (ch != '/') { + continue; + } + + *p = '\0'; + + if (ngx_create_dir(dir, access) == NGX_FILE_ERROR) { + err = ngx_errno; + + switch (err) { + case NGX_EEXIST: + err = 0; + case NGX_EACCES: + break; + + default: + return err; + } + } + + *p = '/'; + } + + return err; +} + + +ngx_atomic_uint_t +ngx_next_temp_number(ngx_uint_t collision) +{ + ngx_atomic_uint_t n, add; + + add = collision ? ngx_random_number : 1; + + n = ngx_atomic_fetch_add(ngx_temp_number, add); + + return n + add; +} + + +char * +ngx_conf_set_path_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + char *p = conf; + + ssize_t level; + ngx_str_t *value; + ngx_uint_t i, n; + ngx_path_t *path, **slot; + + slot = (ngx_path_t **) (p + cmd->offset); + + if (*slot) { + return "is duplicate"; + } + + path = ngx_pcalloc(cf->pool, sizeof(ngx_path_t)); + if (path == NULL) { + return NGX_CONF_ERROR; + } + + value = cf->args->elts; + + path->name = value[1]; + + if (path->name.data[path->name.len - 1] == '/') { + path->name.len--; + } + + if (ngx_conf_full_name(cf->cycle, &path->name, 0) != NGX_OK) { + return NGX_CONF_ERROR; + } + + path->conf_file = cf->conf_file->file.name.data; + path->line = cf->conf_file->line; + + for (i = 0, n = 2; n < cf->args->nelts; i++, n++) { + level = ngx_atoi(value[n].data, value[n].len); + if (level == NGX_ERROR || level == 0) { + return "invalid value"; + } + + path->level[i] = level; + path->len += level + 1; + } + + if (path->len > 10 + i) { + return "invalid value"; + } + + *slot = path; + + if (ngx_add_path(cf, slot) == NGX_ERROR) { + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} + + +char * +ngx_conf_merge_path_value(ngx_conf_t *cf, ngx_path_t **path, ngx_path_t *prev, + ngx_path_init_t *init) +{ + ngx_uint_t i; + + if (*path) { + return NGX_CONF_OK; + } + + if (prev) { + *path = prev; + return NGX_CONF_OK; + } + + *path = ngx_pcalloc(cf->pool, sizeof(ngx_path_t)); + if (*path == NULL) { + return NGX_CONF_ERROR; + } + + (*path)->name = init->name; + + if (ngx_conf_full_name(cf->cycle, &(*path)->name, 0) != NGX_OK) { + return NGX_CONF_ERROR; + } + + for (i = 0; i < NGX_MAX_PATH_LEVEL; i++) { + (*path)->level[i] = init->level[i]; + (*path)->len += init->level[i] + (init->level[i] ? 1 : 0); + } + + if (ngx_add_path(cf, path) != NGX_OK) { + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} + + +char * +ngx_conf_set_access_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + char *confp = conf; + + u_char *p; + ngx_str_t *value; + ngx_uint_t i, right, shift, *access, user; + + access = (ngx_uint_t *) (confp + cmd->offset); + + if (*access != NGX_CONF_UNSET_UINT) { + return "is duplicate"; + } + + value = cf->args->elts; + + *access = 0; + user = 0600; + + for (i = 1; i < cf->args->nelts; i++) { + + p = value[i].data; + + if (ngx_strncmp(p, "user:", sizeof("user:") - 1) == 0) { + shift = 6; + p += sizeof("user:") - 1; + user = 0; + + } else if (ngx_strncmp(p, "group:", sizeof("group:") - 1) == 0) { + shift = 3; + p += sizeof("group:") - 1; + + } else if (ngx_strncmp(p, "all:", sizeof("all:") - 1) == 0) { + shift = 0; + p += sizeof("all:") - 1; + + } else { + goto invalid; + } + + if (ngx_strcmp(p, "rw") == 0) { + right = 6; + + } else if (ngx_strcmp(p, "r") == 0) { + right = 4; + + } else { + goto invalid; + } + + *access |= right << shift; + } + + *access |= user; + + return NGX_CONF_OK; + +invalid: + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid value \"%V\"", &value[i]); + + return NGX_CONF_ERROR; +} + + +ngx_int_t +ngx_add_path(ngx_conf_t *cf, ngx_path_t **slot) +{ + ngx_uint_t i, n; + ngx_path_t *path, **p; + + path = *slot; + + p = cf->cycle->paths.elts; + for (i = 0; i < cf->cycle->paths.nelts; i++) { + if (p[i]->name.len == path->name.len + && ngx_strcmp(p[i]->name.data, path->name.data) == 0) + { + if (p[i]->data != path->data) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "the same path name \"%V\" " + "used in %s:%ui and", + &p[i]->name, p[i]->conf_file, p[i]->line); + return NGX_ERROR; + } + + for (n = 0; n < NGX_MAX_PATH_LEVEL; n++) { + if (p[i]->level[n] != path->level[n]) { + if (path->conf_file == NULL) { + if (p[i]->conf_file == NULL) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "the default path name \"%V\" has " + "the same name as another default path, " + "but the different levels, you need to " + "redefine one of them in http section", + &p[i]->name); + return NGX_ERROR; + } + + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "the path name \"%V\" in %s:%ui has " + "the same name as default path, but " + "the different levels, you need to " + "define default path in http section", + &p[i]->name, p[i]->conf_file, p[i]->line); + return NGX_ERROR; + } + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "the same path name \"%V\" in %s:%ui " + "has the different levels than", + &p[i]->name, p[i]->conf_file, p[i]->line); + return NGX_ERROR; + } + + if (p[i]->level[n] == 0) { + break; + } + } + + *slot = p[i]; + + return NGX_OK; + } + } + + p = ngx_array_push(&cf->cycle->paths); + if (p == NULL) { + return NGX_ERROR; + } + + *p = path; + + return NGX_OK; +} + + +ngx_int_t +ngx_create_paths(ngx_cycle_t *cycle, ngx_uid_t user) +{ + ngx_err_t err; + ngx_uint_t i; + ngx_path_t **path; + + path = cycle->paths.elts; + for (i = 0; i < cycle->paths.nelts; i++) { + + if (ngx_create_dir(path[i]->name.data, 0700) == NGX_FILE_ERROR) { + err = ngx_errno; + if (err != NGX_EEXIST) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, err, + ngx_create_dir_n " \"%s\" failed", + path[i]->name.data); + return NGX_ERROR; + } + } + + if (user == (ngx_uid_t) NGX_CONF_UNSET_UINT) { + continue; + } + +#if !(NGX_WIN32) + { + ngx_file_info_t fi; + + if (ngx_file_info((const char *) path[i]->name.data, &fi) + == NGX_FILE_ERROR) + { + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, + ngx_file_info_n " \"%s\" failed", path[i]->name.data); + return NGX_ERROR; + } + + if (fi.st_uid != user) { + if (chown((const char *) path[i]->name.data, user, -1) == -1) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, + "chown(\"%s\", %d) failed", + path[i]->name.data, user); + return NGX_ERROR; + } + } + + if ((fi.st_mode & (S_IRUSR|S_IWUSR|S_IXUSR)) + != (S_IRUSR|S_IWUSR|S_IXUSR)) + { + fi.st_mode |= (S_IRUSR|S_IWUSR|S_IXUSR); + + if (chmod((const char *) path[i]->name.data, fi.st_mode) == -1) { + ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, + "chmod() \"%s\" failed", path[i]->name.data); + return NGX_ERROR; + } + } + } +#endif + } + + return NGX_OK; +} + + +ngx_int_t +ngx_ext_rename_file(ngx_str_t *src, ngx_str_t *to, ngx_ext_rename_file_t *ext) +{ + u_char *name; + ngx_err_t err; + ngx_copy_file_t cf; + +#if !(NGX_WIN32) + + if (ext->access) { + if (ngx_change_file_access(src->data, ext->access) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_CRIT, ext->log, ngx_errno, + ngx_change_file_access_n " \"%s\" failed", src->data); + err = 0; + goto failed; + } + } + +#endif + + if (ext->time != -1) { + if (ngx_set_file_time(src->data, ext->fd, ext->time) != NGX_OK) { + ngx_log_error(NGX_LOG_CRIT, ext->log, ngx_errno, + ngx_set_file_time_n " \"%s\" failed", src->data); + err = 0; + goto failed; + } + } + + if (ngx_rename_file(src->data, to->data) != NGX_FILE_ERROR) { + return NGX_OK; + } + + err = ngx_errno; + + if (err == NGX_ENOPATH) { + + if (!ext->create_path) { + goto failed; + } + + err = ngx_create_full_path(to->data, ngx_dir_access(ext->path_access)); + + if (err) { + ngx_log_error(NGX_LOG_CRIT, ext->log, err, + ngx_create_dir_n " \"%s\" failed", to->data); + err = 0; + goto failed; + } + + if (ngx_rename_file(src->data, to->data) != NGX_FILE_ERROR) { + return NGX_OK; + } + + err = ngx_errno; + } + +#if (NGX_WIN32) + + if (err == NGX_EEXIST || err == NGX_EEXIST_FILE) { + err = ngx_win32_rename_file(src, to, ext->log); + + if (err == 0) { + return NGX_OK; + } + } + +#endif + + if (err == NGX_EXDEV) { + + cf.size = -1; + cf.buf_size = 0; + cf.access = ext->access; + cf.time = ext->time; + cf.log = ext->log; + + name = ngx_alloc(to->len + 1 + 10 + 1, ext->log); + if (name == NULL) { + return NGX_ERROR; + } + + (void) ngx_sprintf(name, "%*s.%010uD%Z", to->len, to->data, + (uint32_t) ngx_next_temp_number(0)); + + if (ngx_copy_file(src->data, name, &cf) == NGX_OK) { + + if (ngx_rename_file(name, to->data) != NGX_FILE_ERROR) { + ngx_free(name); + + if (ngx_delete_file(src->data) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_CRIT, ext->log, ngx_errno, + ngx_delete_file_n " \"%s\" failed", + src->data); + return NGX_ERROR; + } + + return NGX_OK; + } + + ngx_log_error(NGX_LOG_CRIT, ext->log, ngx_errno, + ngx_rename_file_n " \"%s\" to \"%s\" failed", + name, to->data); + + if (ngx_delete_file(name) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_CRIT, ext->log, ngx_errno, + ngx_delete_file_n " \"%s\" failed", name); + + } + } + + ngx_free(name); + + err = 0; + } + +failed: + + if (ext->delete_file) { + if (ngx_delete_file(src->data) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_CRIT, ext->log, ngx_errno, + ngx_delete_file_n " \"%s\" failed", src->data); + } + } + + if (err) { + ngx_log_error(NGX_LOG_CRIT, ext->log, err, + ngx_rename_file_n " \"%s\" to \"%s\" failed", + src->data, to->data); + } + + return NGX_ERROR; +} + + +ngx_int_t +ngx_copy_file(u_char *from, u_char *to, ngx_copy_file_t *cf) +{ + char *buf; + off_t size; + size_t len; + ssize_t n; + ngx_fd_t fd, nfd; + ngx_int_t rc; + ngx_file_info_t fi; + + rc = NGX_ERROR; + buf = NULL; + nfd = NGX_INVALID_FILE; + + fd = ngx_open_file(from, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0); + + if (fd == NGX_INVALID_FILE) { + ngx_log_error(NGX_LOG_CRIT, cf->log, ngx_errno, + ngx_open_file_n " \"%s\" failed", from); + goto failed; + } + + if (cf->size != -1) { + size = cf->size; + + } else { + if (ngx_fd_info(fd, &fi) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno, + ngx_fd_info_n " \"%s\" failed", from); + + goto failed; + } + + size = ngx_file_size(&fi); + } + + len = cf->buf_size ? cf->buf_size : 65536; + + if ((off_t) len > size) { + len = (size_t) size; + } + + buf = ngx_alloc(len, cf->log); + if (buf == NULL) { + goto failed; + } + + nfd = ngx_open_file(to, NGX_FILE_WRONLY, NGX_FILE_CREATE_OR_OPEN, + cf->access); + + if (nfd == NGX_INVALID_FILE) { + ngx_log_error(NGX_LOG_CRIT, cf->log, ngx_errno, + ngx_open_file_n " \"%s\" failed", to); + goto failed; + } + + while (size > 0) { + + if ((off_t) len > size) { + len = (size_t) size; + } + + n = ngx_read_fd(fd, buf, len); + + if (n == -1) { + ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno, + ngx_read_fd_n " \"%s\" failed", from); + goto failed; + } + + if ((size_t) n != len) { + ngx_log_error(NGX_LOG_ALERT, cf->log, 0, + ngx_read_fd_n " has read only %z of %O from %s", + n, size, from); + goto failed; + } + + n = ngx_write_fd(nfd, buf, len); + + if (n == -1) { + ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno, + ngx_write_fd_n " \"%s\" failed", to); + goto failed; + } + + if ((size_t) n != len) { + ngx_log_error(NGX_LOG_ALERT, cf->log, 0, + ngx_write_fd_n " has written only %z of %O to %s", + n, size, to); + goto failed; + } + + size -= n; + } + + if (cf->time != -1) { + if (ngx_set_file_time(to, nfd, cf->time) != NGX_OK) { + ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno, + ngx_set_file_time_n " \"%s\" failed", to); + goto failed; + } + } + + rc = NGX_OK; + +failed: + + if (nfd != NGX_INVALID_FILE) { + if (ngx_close_file(nfd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno, + ngx_close_file_n " \"%s\" failed", to); + } + } + + if (fd != NGX_INVALID_FILE) { + if (ngx_close_file(fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno, + ngx_close_file_n " \"%s\" failed", from); + } + } + + if (buf) { + ngx_free(buf); + } + + return rc; +} + + +/* + * ctx->init_handler() - see ctx->alloc + * ctx->file_handler() - file handler + * ctx->pre_tree_handler() - handler is called before entering directory + * ctx->post_tree_handler() - handler is called after leaving directory + * ctx->spec_handler() - special (socket, FIFO, etc.) file handler + * + * ctx->data - some data structure, it may be the same on all levels, or + * reallocated if ctx->alloc is nonzero + * + * ctx->alloc - a size of data structure that is allocated at every level + * and is initialized by ctx->init_handler() + * + * ctx->log - a log + * + * on fatal (memory) error handler must return NGX_ABORT to stop walking tree + */ + +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; + + ngx_str_null(&buf); + + 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->alloc) { + data = ngx_alloc(ctx->alloc, ctx->log); + if (data == NULL) { + goto failed; + } + + if (ctx->init_handler(data, prev) == NGX_ABORT) { + goto failed; + } + + ctx->data = data; + + } else { + data = NULL; + } + + 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); + + ctx->size = ngx_de_size(&dir); + ctx->fs_size = ngx_de_fs_size(&dir); + ctx->access = ngx_de_access(&dir); + ctx->mtime = ngx_de_mtime(&dir); + + 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); + + ctx->access = ngx_de_access(&dir); + ctx->mtime = ngx_de_mtime(&dir); + + rc = ctx->pre_tree_handler(ctx, &file); + + if (rc == NGX_ABORT) { + goto failed; + } + + if (rc == NGX_DECLINED) { + ngx_log_debug1(NGX_LOG_DEBUG_CORE, ctx->log, 0, + "tree skip dir \"%s\"", file.data); + continue; + } + + if (ngx_walk_tree(ctx, &file) == NGX_ABORT) { + goto failed; + } + + ctx->access = ngx_de_access(&dir); + ctx->mtime = ngx_de_mtime(&dir); + + 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 (data) { + ngx_free(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/app/nginx/src/core/ngx_file.h b/app/nginx/src/core/ngx_file.h new file mode 100644 index 0000000..320adc2 --- /dev/null +++ b/app/nginx/src/core/ngx_file.h @@ -0,0 +1,164 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_FILE_H_INCLUDED_ +#define _NGX_FILE_H_INCLUDED_ + + +#include +#include + + +struct ngx_file_s { + ngx_fd_t fd; + ngx_str_t name; + ngx_file_info_t info; + + off_t offset; + off_t sys_offset; + + ngx_log_t *log; + +#if (NGX_THREADS || NGX_COMPAT) + ngx_int_t (*thread_handler)(ngx_thread_task_t *task, + ngx_file_t *file); + void *thread_ctx; + ngx_thread_task_t *thread_task; +#endif + +#if (NGX_HAVE_FILE_AIO || NGX_COMPAT) + ngx_event_aio_t *aio; +#endif + + unsigned valid_info:1; + unsigned directio:1; +}; + + +#define NGX_MAX_PATH_LEVEL 3 + + +typedef ngx_msec_t (*ngx_path_manager_pt) (void *data); +typedef ngx_msec_t (*ngx_path_purger_pt) (void *data); +typedef void (*ngx_path_loader_pt) (void *data); + + +typedef struct { + ngx_str_t name; + size_t len; + size_t level[NGX_MAX_PATH_LEVEL]; + + ngx_path_manager_pt manager; + ngx_path_purger_pt purger; + ngx_path_loader_pt loader; + void *data; + + u_char *conf_file; + ngx_uint_t line; +} ngx_path_t; + + +typedef struct { + ngx_str_t name; + size_t level[NGX_MAX_PATH_LEVEL]; +} ngx_path_init_t; + + +typedef struct { + ngx_file_t file; + off_t offset; + ngx_path_t *path; + ngx_pool_t *pool; + char *warn; + + ngx_uint_t access; + + unsigned log_level:8; + unsigned persistent:1; + unsigned clean:1; + unsigned thread_write:1; +} ngx_temp_file_t; + + +typedef struct { + ngx_uint_t access; + ngx_uint_t path_access; + time_t time; + ngx_fd_t fd; + + unsigned create_path:1; + unsigned delete_file:1; + + ngx_log_t *log; +} ngx_ext_rename_file_t; + + +typedef struct { + off_t size; + size_t buf_size; + + ngx_uint_t access; + time_t time; + + ngx_log_t *log; +} ngx_copy_file_t; + + +typedef struct ngx_tree_ctx_s ngx_tree_ctx_t; + +typedef ngx_int_t (*ngx_tree_init_handler_pt) (void *ctx, void *prev); +typedef ngx_int_t (*ngx_tree_handler_pt) (ngx_tree_ctx_t *ctx, ngx_str_t *name); + +struct ngx_tree_ctx_s { + off_t size; + off_t fs_size; + ngx_uint_t access; + time_t mtime; + + 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 alloc; + + ngx_log_t *log; +}; + + +ngx_int_t ngx_get_full_name(ngx_pool_t *pool, ngx_str_t *prefix, + ngx_str_t *name); + +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 clean, + ngx_uint_t access); +void ngx_create_hashed_filename(ngx_path_t *path, u_char *file, size_t len); +ngx_int_t ngx_create_path(ngx_file_t *file, ngx_path_t *path); +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_paths(ngx_cycle_t *cycle, ngx_uid_t user); +ngx_int_t ngx_ext_rename_file(ngx_str_t *src, ngx_str_t *to, + ngx_ext_rename_file_t *ext); +ngx_int_t ngx_copy_file(u_char *from, u_char *to, ngx_copy_file_t *cf); +ngx_int_t ngx_walk_tree(ngx_tree_ctx_t *ctx, ngx_str_t *tree); + +ngx_atomic_uint_t ngx_next_temp_number(ngx_uint_t collision); + +char *ngx_conf_set_path_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +char *ngx_conf_merge_path_value(ngx_conf_t *cf, ngx_path_t **path, + ngx_path_t *prev, ngx_path_init_t *init); +char *ngx_conf_set_access_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); + + +extern ngx_atomic_t *ngx_temp_number; +extern ngx_atomic_int_t ngx_random_number; + + +#endif /* _NGX_FILE_H_INCLUDED_ */ diff --git a/app/nginx/src/core/ngx_hash.c b/app/nginx/src/core/ngx_hash.c new file mode 100644 index 0000000..1944c7a --- /dev/null +++ b/app/nginx/src/core/ngx_hash.c @@ -0,0 +1,988 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include + + +void * +ngx_hash_find(ngx_hash_t *hash, ngx_uint_t key, u_char *name, size_t len) +{ + ngx_uint_t i; + ngx_hash_elt_t *elt; + +#if 0 + ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "hf:\"%*s\"", len, name); +#endif + + elt = hash->buckets[key % hash->size]; + + if (elt == NULL) { + return NULL; + } + + while (elt->value) { + if (len != (size_t) elt->len) { + goto next; + } + + for (i = 0; i < len; i++) { + if (name[i] != elt->name[i]) { + goto next; + } + } + + return elt->value; + + next: + + elt = (ngx_hash_elt_t *) ngx_align_ptr(&elt->name[0] + elt->len, + sizeof(void *)); + continue; + } + + return NULL; +} + + +void * +ngx_hash_find_wc_head(ngx_hash_wildcard_t *hwc, u_char *name, size_t len) +{ + void *value; + ngx_uint_t i, n, key; + +#if 0 + ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "wch:\"%*s\"", len, name); +#endif + + n = len; + + while (n) { + if (name[n - 1] == '.') { + break; + } + + n--; + } + + key = 0; + + for (i = n; i < len; i++) { + key = ngx_hash(key, name[i]); + } + +#if 0 + ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "key:\"%ui\"", key); +#endif + + value = ngx_hash_find(&hwc->hash, key, &name[n], len - n); + +#if 0 + ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "value:\"%p\"", value); +#endif + + if (value) { + + /* + * the 2 low bits of value have the special meaning: + * 00 - value is data pointer for both "example.com" + * and "*.example.com"; + * 01 - value is data pointer for "*.example.com" only; + * 10 - value is pointer to wildcard hash allowing + * both "example.com" and "*.example.com"; + * 11 - value is pointer to wildcard hash allowing + * "*.example.com" only. + */ + + if ((uintptr_t) value & 2) { + + if (n == 0) { + + /* "example.com" */ + + if ((uintptr_t) value & 1) { + return NULL; + } + + hwc = (ngx_hash_wildcard_t *) + ((uintptr_t) value & (uintptr_t) ~3); + return hwc->value; + } + + hwc = (ngx_hash_wildcard_t *) ((uintptr_t) value & (uintptr_t) ~3); + + value = ngx_hash_find_wc_head(hwc, name, n - 1); + + if (value) { + return value; + } + + return hwc->value; + } + + if ((uintptr_t) value & 1) { + + if (n == 0) { + + /* "example.com" */ + + return NULL; + } + + return (void *) ((uintptr_t) value & (uintptr_t) ~3); + } + + return value; + } + + return hwc->value; +} + + +void * +ngx_hash_find_wc_tail(ngx_hash_wildcard_t *hwc, u_char *name, size_t len) +{ + void *value; + ngx_uint_t i, key; + +#if 0 + ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "wct:\"%*s\"", len, name); +#endif + + key = 0; + + for (i = 0; i < len; i++) { + if (name[i] == '.') { + break; + } + + key = ngx_hash(key, name[i]); + } + + if (i == len) { + return NULL; + } + +#if 0 + ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "key:\"%ui\"", key); +#endif + + value = ngx_hash_find(&hwc->hash, key, name, i); + +#if 0 + ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "value:\"%p\"", value); +#endif + + if (value) { + + /* + * the 2 low bits of value have the special meaning: + * 00 - value is data pointer; + * 11 - value is pointer to wildcard hash allowing "example.*". + */ + + if ((uintptr_t) value & 2) { + + i++; + + hwc = (ngx_hash_wildcard_t *) ((uintptr_t) value & (uintptr_t) ~3); + + value = ngx_hash_find_wc_tail(hwc, &name[i], len - i); + + if (value) { + return value; + } + + return hwc->value; + } + + return value; + } + + return hwc->value; +} + + +void * +ngx_hash_find_combined(ngx_hash_combined_t *hash, ngx_uint_t key, u_char *name, + size_t len) +{ + void *value; + + if (hash->hash.buckets) { + value = ngx_hash_find(&hash->hash, key, name, len); + + if (value) { + return value; + } + } + + if (len == 0) { + return NULL; + } + + if (hash->wc_head && hash->wc_head->hash.buckets) { + value = ngx_hash_find_wc_head(hash->wc_head, name, len); + + if (value) { + return value; + } + } + + if (hash->wc_tail && hash->wc_tail->hash.buckets) { + value = ngx_hash_find_wc_tail(hash->wc_tail, name, len); + + if (value) { + return value; + } + } + + return NULL; +} + + +#define NGX_HASH_ELT_SIZE(name) \ + (sizeof(void *) + ngx_align((name)->key.len + 2, sizeof(void *))) + +ngx_int_t +ngx_hash_init(ngx_hash_init_t *hinit, ngx_hash_key_t *names, ngx_uint_t nelts) +{ + u_char *elts; + size_t len; + u_short *test; + ngx_uint_t i, n, key, size, start, bucket_size; + ngx_hash_elt_t *elt, **buckets; + + if (hinit->max_size == 0) { + ngx_log_error(NGX_LOG_EMERG, hinit->pool->log, 0, + "could not build %s, you should " + "increase %s_max_size: %i", + hinit->name, hinit->name, hinit->max_size); + return NGX_ERROR; + } + + for (n = 0; n < nelts; n++) { + if (hinit->bucket_size < NGX_HASH_ELT_SIZE(&names[n]) + sizeof(void *)) + { + ngx_log_error(NGX_LOG_EMERG, hinit->pool->log, 0, + "could not build %s, you should " + "increase %s_bucket_size: %i", + hinit->name, hinit->name, hinit->bucket_size); + return NGX_ERROR; + } + } + + test = ngx_alloc(hinit->max_size * sizeof(u_short), hinit->pool->log); + if (test == NULL) { + return NGX_ERROR; + } + + bucket_size = hinit->bucket_size - sizeof(void *); + + start = nelts / (bucket_size / (2 * sizeof(void *))); + start = start ? start : 1; + + if (hinit->max_size > 10000 && nelts && hinit->max_size / nelts < 100) { + start = hinit->max_size - 1000; + } + + for (size = start; size <= hinit->max_size; size++) { + + ngx_memzero(test, size * sizeof(u_short)); + + for (n = 0; n < nelts; n++) { + if (names[n].key.data == NULL) { + continue; + } + + key = names[n].key_hash % size; + test[key] = (u_short) (test[key] + NGX_HASH_ELT_SIZE(&names[n])); + +#if 0 + ngx_log_error(NGX_LOG_ALERT, hinit->pool->log, 0, + "%ui: %ui %ui \"%V\"", + size, key, test[key], &names[n].key); +#endif + + if (test[key] > (u_short) bucket_size) { + goto next; + } + } + + goto found; + + next: + + continue; + } + + size = hinit->max_size; + + ngx_log_error(NGX_LOG_WARN, hinit->pool->log, 0, + "could not build optimal %s, you should increase " + "either %s_max_size: %i or %s_bucket_size: %i; " + "ignoring %s_bucket_size", + hinit->name, hinit->name, hinit->max_size, + hinit->name, hinit->bucket_size, hinit->name); + +found: + + for (i = 0; i < size; i++) { + test[i] = sizeof(void *); + } + + for (n = 0; n < nelts; n++) { + if (names[n].key.data == NULL) { + continue; + } + + key = names[n].key_hash % size; + test[key] = (u_short) (test[key] + NGX_HASH_ELT_SIZE(&names[n])); + } + + len = 0; + + for (i = 0; i < size; i++) { + if (test[i] == sizeof(void *)) { + continue; + } + + test[i] = (u_short) (ngx_align(test[i], ngx_cacheline_size)); + + len += test[i]; + } + + if (hinit->hash == NULL) { + hinit->hash = ngx_pcalloc(hinit->pool, sizeof(ngx_hash_wildcard_t) + + size * sizeof(ngx_hash_elt_t *)); + if (hinit->hash == NULL) { + ngx_free(test); + return NGX_ERROR; + } + + buckets = (ngx_hash_elt_t **) + ((u_char *) hinit->hash + sizeof(ngx_hash_wildcard_t)); + + } else { + buckets = ngx_pcalloc(hinit->pool, size * sizeof(ngx_hash_elt_t *)); + if (buckets == NULL) { + ngx_free(test); + return NGX_ERROR; + } + } + + elts = ngx_palloc(hinit->pool, len + ngx_cacheline_size); + if (elts == NULL) { + ngx_free(test); + return NGX_ERROR; + } + + elts = ngx_align_ptr(elts, ngx_cacheline_size); + + for (i = 0; i < size; i++) { + if (test[i] == sizeof(void *)) { + continue; + } + + buckets[i] = (ngx_hash_elt_t *) elts; + elts += test[i]; + } + + for (i = 0; i < size; i++) { + test[i] = 0; + } + + for (n = 0; n < nelts; n++) { + if (names[n].key.data == NULL) { + continue; + } + + key = names[n].key_hash % size; + elt = (ngx_hash_elt_t *) ((u_char *) buckets[key] + test[key]); + + elt->value = names[n].value; + elt->len = (u_short) names[n].key.len; + + ngx_strlow(elt->name, names[n].key.data, names[n].key.len); + + test[key] = (u_short) (test[key] + NGX_HASH_ELT_SIZE(&names[n])); + } + + for (i = 0; i < size; i++) { + if (buckets[i] == NULL) { + continue; + } + + elt = (ngx_hash_elt_t *) ((u_char *) buckets[i] + test[i]); + + elt->value = NULL; + } + + ngx_free(test); + + hinit->hash->buckets = buckets; + hinit->hash->size = size; + +#if 0 + + for (i = 0; i < size; i++) { + ngx_str_t val; + ngx_uint_t key; + + elt = buckets[i]; + + if (elt == NULL) { + ngx_log_error(NGX_LOG_ALERT, hinit->pool->log, 0, + "%ui: NULL", i); + continue; + } + + while (elt->value) { + val.len = elt->len; + val.data = &elt->name[0]; + + key = hinit->key(val.data, val.len); + + ngx_log_error(NGX_LOG_ALERT, hinit->pool->log, 0, + "%ui: %p \"%V\" %ui", i, elt, &val, key); + + elt = (ngx_hash_elt_t *) ngx_align_ptr(&elt->name[0] + elt->len, + sizeof(void *)); + } + } + +#endif + + return NGX_OK; +} + + +ngx_int_t +ngx_hash_wildcard_init(ngx_hash_init_t *hinit, ngx_hash_key_t *names, + ngx_uint_t nelts) +{ + size_t len, dot_len; + ngx_uint_t i, n, dot; + ngx_array_t curr_names, next_names; + ngx_hash_key_t *name, *next_name; + ngx_hash_init_t h; + ngx_hash_wildcard_t *wdc; + + if (ngx_array_init(&curr_names, hinit->temp_pool, nelts, + sizeof(ngx_hash_key_t)) + != NGX_OK) + { + return NGX_ERROR; + } + + if (ngx_array_init(&next_names, hinit->temp_pool, nelts, + sizeof(ngx_hash_key_t)) + != NGX_OK) + { + return NGX_ERROR; + } + + for (n = 0; n < nelts; n = i) { + +#if 0 + ngx_log_error(NGX_LOG_ALERT, hinit->pool->log, 0, + "wc0: \"%V\"", &names[n].key); +#endif + + dot = 0; + + for (len = 0; len < names[n].key.len; len++) { + if (names[n].key.data[len] == '.') { + dot = 1; + break; + } + } + + name = ngx_array_push(&curr_names); + if (name == NULL) { + return NGX_ERROR; + } + + name->key.len = len; + name->key.data = names[n].key.data; + name->key_hash = hinit->key(name->key.data, name->key.len); + name->value = names[n].value; + +#if 0 + ngx_log_error(NGX_LOG_ALERT, hinit->pool->log, 0, + "wc1: \"%V\" %ui", &name->key, dot); +#endif + + dot_len = len + 1; + + if (dot) { + len++; + } + + next_names.nelts = 0; + + if (names[n].key.len != len) { + next_name = ngx_array_push(&next_names); + if (next_name == NULL) { + return NGX_ERROR; + } + + next_name->key.len = names[n].key.len - len; + next_name->key.data = names[n].key.data + len; + next_name->key_hash = 0; + next_name->value = names[n].value; + +#if 0 + ngx_log_error(NGX_LOG_ALERT, hinit->pool->log, 0, + "wc2: \"%V\"", &next_name->key); +#endif + } + + for (i = n + 1; i < nelts; i++) { + if (ngx_strncmp(names[n].key.data, names[i].key.data, len) != 0) { + break; + } + + if (!dot + && names[i].key.len > len + && names[i].key.data[len] != '.') + { + break; + } + + next_name = ngx_array_push(&next_names); + if (next_name == NULL) { + return NGX_ERROR; + } + + next_name->key.len = names[i].key.len - dot_len; + next_name->key.data = names[i].key.data + dot_len; + next_name->key_hash = 0; + next_name->value = names[i].value; + +#if 0 + ngx_log_error(NGX_LOG_ALERT, hinit->pool->log, 0, + "wc3: \"%V\"", &next_name->key); +#endif + } + + if (next_names.nelts) { + + h = *hinit; + h.hash = NULL; + + if (ngx_hash_wildcard_init(&h, (ngx_hash_key_t *) next_names.elts, + next_names.nelts) + != NGX_OK) + { + return NGX_ERROR; + } + + wdc = (ngx_hash_wildcard_t *) h.hash; + + if (names[n].key.len == len) { + wdc->value = names[n].value; + } + + name->value = (void *) ((uintptr_t) wdc | (dot ? 3 : 2)); + + } else if (dot) { + name->value = (void *) ((uintptr_t) name->value | 1); + } + } + + if (ngx_hash_init(hinit, (ngx_hash_key_t *) curr_names.elts, + curr_names.nelts) + != NGX_OK) + { + return NGX_ERROR; + } + + return NGX_OK; +} + + +ngx_uint_t +ngx_hash_key(u_char *data, size_t len) +{ + ngx_uint_t i, key; + + key = 0; + + for (i = 0; i < len; i++) { + key = ngx_hash(key, data[i]); + } + + return key; +} + + +ngx_uint_t +ngx_hash_key_lc(u_char *data, size_t len) +{ + ngx_uint_t i, key; + + key = 0; + + for (i = 0; i < len; i++) { + key = ngx_hash(key, ngx_tolower(data[i])); + } + + return key; +} + + +ngx_uint_t +ngx_hash_strlow(u_char *dst, u_char *src, size_t n) +{ + ngx_uint_t key; + + key = 0; + + while (n--) { + *dst = ngx_tolower(*src); + key = ngx_hash(key, *dst); + dst++; + src++; + } + + return key; +} + + +ngx_int_t +ngx_hash_keys_array_init(ngx_hash_keys_arrays_t *ha, ngx_uint_t type) +{ + ngx_uint_t asize; + + if (type == NGX_HASH_SMALL) { + asize = 4; + ha->hsize = 107; + + } else { + asize = NGX_HASH_LARGE_ASIZE; + ha->hsize = NGX_HASH_LARGE_HSIZE; + } + + if (ngx_array_init(&ha->keys, ha->temp_pool, asize, sizeof(ngx_hash_key_t)) + != NGX_OK) + { + return NGX_ERROR; + } + + if (ngx_array_init(&ha->dns_wc_head, ha->temp_pool, asize, + sizeof(ngx_hash_key_t)) + != NGX_OK) + { + return NGX_ERROR; + } + + if (ngx_array_init(&ha->dns_wc_tail, ha->temp_pool, asize, + sizeof(ngx_hash_key_t)) + != NGX_OK) + { + return NGX_ERROR; + } + + ha->keys_hash = ngx_pcalloc(ha->temp_pool, sizeof(ngx_array_t) * ha->hsize); + if (ha->keys_hash == NULL) { + return NGX_ERROR; + } + + ha->dns_wc_head_hash = ngx_pcalloc(ha->temp_pool, + sizeof(ngx_array_t) * ha->hsize); + if (ha->dns_wc_head_hash == NULL) { + return NGX_ERROR; + } + + ha->dns_wc_tail_hash = ngx_pcalloc(ha->temp_pool, + sizeof(ngx_array_t) * ha->hsize); + if (ha->dns_wc_tail_hash == NULL) { + return NGX_ERROR; + } + + return NGX_OK; +} + + +ngx_int_t +ngx_hash_add_key(ngx_hash_keys_arrays_t *ha, ngx_str_t *key, void *value, + ngx_uint_t flags) +{ + size_t len; + u_char *p; + ngx_str_t *name; + ngx_uint_t i, k, n, skip, last; + ngx_array_t *keys, *hwc; + ngx_hash_key_t *hk; + + last = key->len; + + if (flags & NGX_HASH_WILDCARD_KEY) { + + /* + * supported wildcards: + * "*.example.com", ".example.com", and "www.example.*" + */ + + n = 0; + + for (i = 0; i < key->len; i++) { + + if (key->data[i] == '*') { + if (++n > 1) { + return NGX_DECLINED; + } + } + + if (key->data[i] == '.' && key->data[i + 1] == '.') { + return NGX_DECLINED; + } + + if (key->data[i] == '\0') { + return NGX_DECLINED; + } + } + + if (key->len > 1 && key->data[0] == '.') { + skip = 1; + goto wildcard; + } + + if (key->len > 2) { + + if (key->data[0] == '*' && key->data[1] == '.') { + skip = 2; + goto wildcard; + } + + if (key->data[i - 2] == '.' && key->data[i - 1] == '*') { + skip = 0; + last -= 2; + goto wildcard; + } + } + + if (n) { + return NGX_DECLINED; + } + } + + /* exact hash */ + + k = 0; + + for (i = 0; i < last; i++) { + if (!(flags & NGX_HASH_READONLY_KEY)) { + key->data[i] = ngx_tolower(key->data[i]); + } + k = ngx_hash(k, key->data[i]); + } + + k %= ha->hsize; + + /* check conflicts in exact hash */ + + name = ha->keys_hash[k].elts; + + if (name) { + for (i = 0; i < ha->keys_hash[k].nelts; i++) { + if (last != name[i].len) { + continue; + } + + if (ngx_strncmp(key->data, name[i].data, last) == 0) { + return NGX_BUSY; + } + } + + } else { + if (ngx_array_init(&ha->keys_hash[k], ha->temp_pool, 4, + sizeof(ngx_str_t)) + != NGX_OK) + { + return NGX_ERROR; + } + } + + name = ngx_array_push(&ha->keys_hash[k]); + if (name == NULL) { + return NGX_ERROR; + } + + *name = *key; + + hk = ngx_array_push(&ha->keys); + if (hk == NULL) { + return NGX_ERROR; + } + + hk->key = *key; + hk->key_hash = ngx_hash_key(key->data, last); + hk->value = value; + + return NGX_OK; + + +wildcard: + + /* wildcard hash */ + + k = ngx_hash_strlow(&key->data[skip], &key->data[skip], last - skip); + + k %= ha->hsize; + + if (skip == 1) { + + /* check conflicts in exact hash for ".example.com" */ + + name = ha->keys_hash[k].elts; + + if (name) { + len = last - skip; + + for (i = 0; i < ha->keys_hash[k].nelts; i++) { + if (len != name[i].len) { + continue; + } + + if (ngx_strncmp(&key->data[1], name[i].data, len) == 0) { + return NGX_BUSY; + } + } + + } else { + if (ngx_array_init(&ha->keys_hash[k], ha->temp_pool, 4, + sizeof(ngx_str_t)) + != NGX_OK) + { + return NGX_ERROR; + } + } + + name = ngx_array_push(&ha->keys_hash[k]); + if (name == NULL) { + return NGX_ERROR; + } + + name->len = last - 1; + name->data = ngx_pnalloc(ha->temp_pool, name->len); + if (name->data == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(name->data, &key->data[1], name->len); + } + + + if (skip) { + + /* + * convert "*.example.com" to "com.example.\0" + * and ".example.com" to "com.example\0" + */ + + p = ngx_pnalloc(ha->temp_pool, last); + if (p == NULL) { + return NGX_ERROR; + } + + len = 0; + n = 0; + + for (i = last - 1; i; i--) { + if (key->data[i] == '.') { + ngx_memcpy(&p[n], &key->data[i + 1], len); + n += len; + p[n++] = '.'; + len = 0; + continue; + } + + len++; + } + + if (len) { + ngx_memcpy(&p[n], &key->data[1], len); + n += len; + } + + p[n] = '\0'; + + hwc = &ha->dns_wc_head; + keys = &ha->dns_wc_head_hash[k]; + + } else { + + /* convert "www.example.*" to "www.example\0" */ + + last++; + + p = ngx_pnalloc(ha->temp_pool, last); + if (p == NULL) { + return NGX_ERROR; + } + + ngx_cpystrn(p, key->data, last); + + hwc = &ha->dns_wc_tail; + keys = &ha->dns_wc_tail_hash[k]; + } + + + /* check conflicts in wildcard hash */ + + name = keys->elts; + + if (name) { + len = last - skip; + + for (i = 0; i < keys->nelts; i++) { + if (len != name[i].len) { + continue; + } + + if (ngx_strncmp(key->data + skip, name[i].data, len) == 0) { + return NGX_BUSY; + } + } + + } else { + if (ngx_array_init(keys, ha->temp_pool, 4, sizeof(ngx_str_t)) != NGX_OK) + { + return NGX_ERROR; + } + } + + name = ngx_array_push(keys); + if (name == NULL) { + return NGX_ERROR; + } + + name->len = last - skip; + name->data = ngx_pnalloc(ha->temp_pool, name->len); + if (name->data == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(name->data, key->data + skip, name->len); + + + /* add to wildcard hash */ + + hk = ngx_array_push(hwc); + if (hk == NULL) { + return NGX_ERROR; + } + + hk->key.len = last - 1; + hk->key.data = p; + hk->key_hash = 0; + hk->value = value; + + return NGX_OK; +} diff --git a/app/nginx/src/core/ngx_hash.h b/app/nginx/src/core/ngx_hash.h new file mode 100644 index 0000000..abc3cbe --- /dev/null +++ b/app/nginx/src/core/ngx_hash.h @@ -0,0 +1,122 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_HASH_H_INCLUDED_ +#define _NGX_HASH_H_INCLUDED_ + + +#include +#include + + +typedef struct { + void *value; + u_short len; + u_char name[1]; +} ngx_hash_elt_t; + + +typedef struct { + ngx_hash_elt_t **buckets; + ngx_uint_t size; +} ngx_hash_t; + + +typedef struct { + ngx_hash_t hash; + void *value; +} ngx_hash_wildcard_t; + + +typedef struct { + ngx_str_t key; + ngx_uint_t key_hash; + void *value; +} ngx_hash_key_t; + + +typedef ngx_uint_t (*ngx_hash_key_pt) (u_char *data, size_t len); + + +typedef struct { + ngx_hash_t hash; + ngx_hash_wildcard_t *wc_head; + ngx_hash_wildcard_t *wc_tail; +} ngx_hash_combined_t; + + +typedef struct { + ngx_hash_t *hash; + ngx_hash_key_pt key; + + ngx_uint_t max_size; + ngx_uint_t bucket_size; + + char *name; + ngx_pool_t *pool; + ngx_pool_t *temp_pool; +} ngx_hash_init_t; + + +#define NGX_HASH_SMALL 1 +#define NGX_HASH_LARGE 2 + +#define NGX_HASH_LARGE_ASIZE 16384 +#define NGX_HASH_LARGE_HSIZE 10007 + +#define NGX_HASH_WILDCARD_KEY 1 +#define NGX_HASH_READONLY_KEY 2 + + +typedef struct { + ngx_uint_t hsize; + + ngx_pool_t *pool; + ngx_pool_t *temp_pool; + + ngx_array_t keys; + ngx_array_t *keys_hash; + + ngx_array_t dns_wc_head; + ngx_array_t *dns_wc_head_hash; + + ngx_array_t dns_wc_tail; + ngx_array_t *dns_wc_tail_hash; +} ngx_hash_keys_arrays_t; + + +typedef struct { + ngx_uint_t hash; + ngx_str_t key; + ngx_str_t value; + u_char *lowcase_key; +} ngx_table_elt_t; + + +void *ngx_hash_find(ngx_hash_t *hash, ngx_uint_t key, u_char *name, size_t len); +void *ngx_hash_find_wc_head(ngx_hash_wildcard_t *hwc, u_char *name, size_t len); +void *ngx_hash_find_wc_tail(ngx_hash_wildcard_t *hwc, u_char *name, size_t len); +void *ngx_hash_find_combined(ngx_hash_combined_t *hash, ngx_uint_t key, + u_char *name, size_t len); + +ngx_int_t ngx_hash_init(ngx_hash_init_t *hinit, ngx_hash_key_t *names, + ngx_uint_t nelts); +ngx_int_t ngx_hash_wildcard_init(ngx_hash_init_t *hinit, ngx_hash_key_t *names, + ngx_uint_t nelts); + +#define ngx_hash(key, c) ((ngx_uint_t) key * 31 + c) +ngx_uint_t ngx_hash_key(u_char *data, size_t len); +ngx_uint_t ngx_hash_key_lc(u_char *data, size_t len); +ngx_uint_t ngx_hash_strlow(u_char *dst, u_char *src, size_t n); + + +ngx_int_t ngx_hash_keys_array_init(ngx_hash_keys_arrays_t *ha, ngx_uint_t type); +ngx_int_t ngx_hash_add_key(ngx_hash_keys_arrays_t *ha, ngx_str_t *key, + void *value, ngx_uint_t flags); + + +#endif /* _NGX_HASH_H_INCLUDED_ */ diff --git a/app/nginx/src/core/ngx_inet.c b/app/nginx/src/core/ngx_inet.c new file mode 100644 index 0000000..3bcd3e7 --- /dev/null +++ b/app/nginx/src/core/ngx_inet.c @@ -0,0 +1,1493 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include + + +static ngx_int_t ngx_parse_unix_domain_url(ngx_pool_t *pool, ngx_url_t *u); +static ngx_int_t ngx_parse_inet_url(ngx_pool_t *pool, ngx_url_t *u); +static ngx_int_t ngx_parse_inet6_url(ngx_pool_t *pool, ngx_url_t *u); + + +in_addr_t +ngx_inet_addr(u_char *text, size_t len) +{ + u_char *p, c; + in_addr_t addr; + ngx_uint_t octet, n; + + addr = 0; + octet = 0; + n = 0; + + for (p = text; p < text + len; p++) { + c = *p; + + if (c >= '0' && c <= '9') { + octet = octet * 10 + (c - '0'); + + if (octet > 255) { + return INADDR_NONE; + } + + continue; + } + + if (c == '.') { + addr = (addr << 8) + octet; + octet = 0; + n++; + continue; + } + + return INADDR_NONE; + } + + if (n == 3) { + addr = (addr << 8) + octet; + return htonl(addr); + } + + return INADDR_NONE; +} + + +#if (NGX_HAVE_INET6) + +ngx_int_t +ngx_inet6_addr(u_char *p, size_t len, u_char *addr) +{ + u_char c, *zero, *digit, *s, *d; + size_t len4; + ngx_uint_t n, nibbles, word; + + if (len == 0) { + return NGX_ERROR; + } + + zero = NULL; + digit = NULL; + len4 = 0; + nibbles = 0; + word = 0; + n = 8; + + if (p[0] == ':') { + p++; + len--; + } + + for (/* void */; len; len--) { + c = *p++; + + if (c == ':') { + if (nibbles) { + digit = p; + len4 = len; + *addr++ = (u_char) (word >> 8); + *addr++ = (u_char) (word & 0xff); + + if (--n) { + nibbles = 0; + word = 0; + continue; + } + + } else { + if (zero == NULL) { + digit = p; + len4 = len; + zero = addr; + continue; + } + } + + return NGX_ERROR; + } + + if (c == '.' && nibbles) { + if (n < 2 || digit == NULL) { + return NGX_ERROR; + } + + word = ngx_inet_addr(digit, len4 - 1); + if (word == INADDR_NONE) { + return NGX_ERROR; + } + + word = ntohl(word); + *addr++ = (u_char) ((word >> 24) & 0xff); + *addr++ = (u_char) ((word >> 16) & 0xff); + n--; + break; + } + + if (++nibbles > 4) { + return NGX_ERROR; + } + + if (c >= '0' && c <= '9') { + word = word * 16 + (c - '0'); + continue; + } + + c |= 0x20; + + if (c >= 'a' && c <= 'f') { + word = word * 16 + (c - 'a') + 10; + continue; + } + + return NGX_ERROR; + } + + if (nibbles == 0 && zero == NULL) { + return NGX_ERROR; + } + + *addr++ = (u_char) (word >> 8); + *addr++ = (u_char) (word & 0xff); + + if (--n) { + if (zero) { + n *= 2; + s = addr - 1; + d = s + n; + while (s >= zero) { + *d-- = *s--; + } + ngx_memzero(zero, n); + return NGX_OK; + } + + } else { + if (zero == NULL) { + return NGX_OK; + } + } + + return NGX_ERROR; +} + +#endif + + +size_t +ngx_sock_ntop(struct sockaddr *sa, socklen_t socklen, u_char *text, size_t len, + ngx_uint_t port) +{ + u_char *p; + struct sockaddr_in *sin; +#if (NGX_HAVE_INET6) + size_t n; + struct sockaddr_in6 *sin6; +#endif +#if (NGX_HAVE_UNIX_DOMAIN) + struct sockaddr_un *saun; +#endif + + switch (sa->sa_family) { + + case AF_INET: + + sin = (struct sockaddr_in *) sa; + p = (u_char *) &sin->sin_addr; + + if (port) { + p = ngx_snprintf(text, len, "%ud.%ud.%ud.%ud:%d", + p[0], p[1], p[2], p[3], ntohs(sin->sin_port)); + } else { + p = ngx_snprintf(text, len, "%ud.%ud.%ud.%ud", + p[0], p[1], p[2], p[3]); + } + + return (p - text); + +#if (NGX_HAVE_INET6) + + case AF_INET6: + + sin6 = (struct sockaddr_in6 *) sa; + + n = 0; + + if (port) { + text[n++] = '['; + } + + n = ngx_inet6_ntop(sin6->sin6_addr.s6_addr, &text[n], len); + + if (port) { + n = ngx_sprintf(&text[1 + n], "]:%d", + ntohs(sin6->sin6_port)) - text; + } + + return n; +#endif + +#if (NGX_HAVE_UNIX_DOMAIN) + + case AF_UNIX: + saun = (struct sockaddr_un *) sa; + + /* on Linux sockaddr might not include sun_path at all */ + + if (socklen <= (socklen_t) offsetof(struct sockaddr_un, sun_path)) { + p = ngx_snprintf(text, len, "unix:%Z"); + + } else { + p = ngx_snprintf(text, len, "unix:%s%Z", saun->sun_path); + } + + /* we do not include trailing zero in address length */ + + return (p - text - 1); + +#endif + + default: + return 0; + } +} + + +size_t +ngx_inet_ntop(int family, void *addr, u_char *text, size_t len) +{ + u_char *p; + + switch (family) { + + case AF_INET: + + p = addr; + + return ngx_snprintf(text, len, "%ud.%ud.%ud.%ud", + p[0], p[1], p[2], p[3]) + - text; + +#if (NGX_HAVE_INET6) + + case AF_INET6: + return ngx_inet6_ntop(addr, text, len); + +#endif + + default: + return 0; + } +} + + +#if (NGX_HAVE_INET6) + +size_t +ngx_inet6_ntop(u_char *p, u_char *text, size_t len) +{ + u_char *dst; + size_t max, n; + ngx_uint_t i, zero, last; + + if (len < NGX_INET6_ADDRSTRLEN) { + return 0; + } + + zero = (ngx_uint_t) -1; + last = (ngx_uint_t) -1; + max = 1; + n = 0; + + for (i = 0; i < 16; i += 2) { + + if (p[i] || p[i + 1]) { + + if (max < n) { + zero = last; + max = n; + } + + n = 0; + continue; + } + + if (n++ == 0) { + last = i; + } + } + + if (max < n) { + zero = last; + max = n; + } + + dst = text; + n = 16; + + if (zero == 0) { + + if ((max == 5 && p[10] == 0xff && p[11] == 0xff) + || (max == 6) + || (max == 7 && p[14] != 0 && p[15] != 1)) + { + n = 12; + } + + *dst++ = ':'; + } + + for (i = 0; i < n; i += 2) { + + if (i == zero) { + *dst++ = ':'; + i += (max - 1) * 2; + continue; + } + + dst = ngx_sprintf(dst, "%xd", p[i] * 256 + p[i + 1]); + + if (i < 14) { + *dst++ = ':'; + } + } + + if (n == 12) { + dst = ngx_sprintf(dst, "%ud.%ud.%ud.%ud", p[12], p[13], p[14], p[15]); + } + + return dst - text; +} + +#endif + + +ngx_int_t +ngx_ptocidr(ngx_str_t *text, ngx_cidr_t *cidr) +{ + u_char *addr, *mask, *last; + size_t len; + ngx_int_t shift; +#if (NGX_HAVE_INET6) + ngx_int_t rc; + ngx_uint_t s, i; +#endif + + addr = text->data; + last = addr + text->len; + + mask = ngx_strlchr(addr, last, '/'); + len = (mask ? mask : last) - addr; + + cidr->u.in.addr = ngx_inet_addr(addr, len); + + if (cidr->u.in.addr != INADDR_NONE) { + cidr->family = AF_INET; + + if (mask == NULL) { + cidr->u.in.mask = 0xffffffff; + return NGX_OK; + } + +#if (NGX_HAVE_INET6) + } else if (ngx_inet6_addr(addr, len, cidr->u.in6.addr.s6_addr) == NGX_OK) { + cidr->family = AF_INET6; + + if (mask == NULL) { + ngx_memset(cidr->u.in6.mask.s6_addr, 0xff, 16); + return NGX_OK; + } + +#endif + } else { + return NGX_ERROR; + } + + mask++; + + shift = ngx_atoi(mask, last - mask); + if (shift == NGX_ERROR) { + return NGX_ERROR; + } + + switch (cidr->family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + if (shift > 128) { + return NGX_ERROR; + } + + addr = cidr->u.in6.addr.s6_addr; + mask = cidr->u.in6.mask.s6_addr; + rc = NGX_OK; + + for (i = 0; i < 16; i++) { + + s = (shift > 8) ? 8 : shift; + shift -= s; + + mask[i] = (u_char) (0xffu << (8 - s)); + + if (addr[i] != (addr[i] & mask[i])) { + rc = NGX_DONE; + addr[i] &= mask[i]; + } + } + + return rc; +#endif + + default: /* AF_INET */ + if (shift > 32) { + return NGX_ERROR; + } + + if (shift) { + cidr->u.in.mask = htonl((uint32_t) (0xffffffffu << (32 - shift))); + + } else { + /* x86 compilers use a shl instruction that shifts by modulo 32 */ + cidr->u.in.mask = 0; + } + + if (cidr->u.in.addr == (cidr->u.in.addr & cidr->u.in.mask)) { + return NGX_OK; + } + + cidr->u.in.addr &= cidr->u.in.mask; + + return NGX_DONE; + } +} + + +ngx_int_t +ngx_cidr_match(struct sockaddr *sa, ngx_array_t *cidrs) +{ +#if (NGX_HAVE_INET6) + u_char *p; +#endif + in_addr_t inaddr; + ngx_cidr_t *cidr; + ngx_uint_t family, i; +#if (NGX_HAVE_INET6) + ngx_uint_t n; + struct in6_addr *inaddr6; +#endif + +#if (NGX_SUPPRESS_WARN) + inaddr = 0; +#if (NGX_HAVE_INET6) + inaddr6 = NULL; +#endif +#endif + + family = sa->sa_family; + + if (family == AF_INET) { + inaddr = ((struct sockaddr_in *) sa)->sin_addr.s_addr; + } + +#if (NGX_HAVE_INET6) + else if (family == AF_INET6) { + inaddr6 = &((struct sockaddr_in6 *) sa)->sin6_addr; + + if (IN6_IS_ADDR_V4MAPPED(inaddr6)) { + family = AF_INET; + + p = inaddr6->s6_addr; + + inaddr = p[12] << 24; + inaddr += p[13] << 16; + inaddr += p[14] << 8; + inaddr += p[15]; + + inaddr = htonl(inaddr); + } + } +#endif + + for (cidr = cidrs->elts, i = 0; i < cidrs->nelts; i++) { + if (cidr[i].family != family) { + goto next; + } + + switch (family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + for (n = 0; n < 16; n++) { + if ((inaddr6->s6_addr[n] & cidr[i].u.in6.mask.s6_addr[n]) + != cidr[i].u.in6.addr.s6_addr[n]) + { + goto next; + } + } + break; +#endif + +#if (NGX_HAVE_UNIX_DOMAIN) + case AF_UNIX: + break; +#endif + + default: /* AF_INET */ + if ((inaddr & cidr[i].u.in.mask) != cidr[i].u.in.addr) { + goto next; + } + break; + } + + return NGX_OK; + + next: + continue; + } + + return NGX_DECLINED; +} + + +ngx_int_t +ngx_parse_addr(ngx_pool_t *pool, ngx_addr_t *addr, u_char *text, size_t len) +{ + in_addr_t inaddr; + ngx_uint_t family; + struct sockaddr_in *sin; +#if (NGX_HAVE_INET6) + struct in6_addr inaddr6; + struct sockaddr_in6 *sin6; + + /* + * prevent MSVC8 warning: + * potentially uninitialized local variable 'inaddr6' used + */ + ngx_memzero(&inaddr6, sizeof(struct in6_addr)); +#endif + + inaddr = ngx_inet_addr(text, len); + + if (inaddr != INADDR_NONE) { + family = AF_INET; + len = sizeof(struct sockaddr_in); + +#if (NGX_HAVE_INET6) + } else if (ngx_inet6_addr(text, len, inaddr6.s6_addr) == NGX_OK) { + family = AF_INET6; + len = sizeof(struct sockaddr_in6); + +#endif + } else { + return NGX_DECLINED; + } + + addr->sockaddr = ngx_pcalloc(pool, len); + if (addr->sockaddr == NULL) { + return NGX_ERROR; + } + + addr->sockaddr->sa_family = (u_char) family; + addr->socklen = len; + + switch (family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + sin6 = (struct sockaddr_in6 *) addr->sockaddr; + ngx_memcpy(sin6->sin6_addr.s6_addr, inaddr6.s6_addr, 16); + break; +#endif + + default: /* AF_INET */ + sin = (struct sockaddr_in *) addr->sockaddr; + sin->sin_addr.s_addr = inaddr; + break; + } + + return NGX_OK; +} + + +ngx_int_t +ngx_parse_addr_port(ngx_pool_t *pool, ngx_addr_t *addr, u_char *text, + size_t len) +{ + u_char *p, *last; + size_t plen; + ngx_int_t rc, port; + + rc = ngx_parse_addr(pool, addr, text, len); + + if (rc != NGX_DECLINED) { + return rc; + } + + last = text + len; + +#if (NGX_HAVE_INET6) + if (len && text[0] == '[') { + + p = ngx_strlchr(text, last, ']'); + + if (p == NULL || p == last - 1 || *++p != ':') { + return NGX_DECLINED; + } + + text++; + len -= 2; + + } else +#endif + + { + p = ngx_strlchr(text, last, ':'); + + if (p == NULL) { + return NGX_DECLINED; + } + } + + p++; + plen = last - p; + + port = ngx_atoi(p, plen); + + if (port < 1 || port > 65535) { + return NGX_DECLINED; + } + + len -= plen + 1; + + rc = ngx_parse_addr(pool, addr, text, len); + + if (rc != NGX_OK) { + return rc; + } + + ngx_inet_set_port(addr->sockaddr, (in_port_t) port); + + return NGX_OK; +} + + +ngx_int_t +ngx_parse_url(ngx_pool_t *pool, ngx_url_t *u) +{ + u_char *p; + size_t len; + + p = u->url.data; + len = u->url.len; + + if (len >= 5 && ngx_strncasecmp(p, (u_char *) "unix:", 5) == 0) { + return ngx_parse_unix_domain_url(pool, u); + } + + if (len && p[0] == '[') { + return ngx_parse_inet6_url(pool, u); + } + + return ngx_parse_inet_url(pool, u); +} + + +static ngx_int_t +ngx_parse_unix_domain_url(ngx_pool_t *pool, ngx_url_t *u) +{ +#if (NGX_HAVE_UNIX_DOMAIN) + u_char *path, *uri, *last; + size_t len; + struct sockaddr_un *saun; + + len = u->url.len; + path = u->url.data; + + path += 5; + len -= 5; + + if (u->uri_part) { + + last = path + len; + uri = ngx_strlchr(path, last, ':'); + + if (uri) { + len = uri - path; + uri++; + u->uri.len = last - uri; + u->uri.data = uri; + } + } + + if (len == 0) { + u->err = "no path in the unix domain socket"; + return NGX_ERROR; + } + + u->host.len = len++; + u->host.data = path; + + if (len > sizeof(saun->sun_path)) { + u->err = "too long path in the unix domain socket"; + return NGX_ERROR; + } + + u->socklen = sizeof(struct sockaddr_un); + saun = (struct sockaddr_un *) &u->sockaddr; + saun->sun_family = AF_UNIX; + (void) ngx_cpystrn((u_char *) saun->sun_path, path, len); + + u->addrs = ngx_pcalloc(pool, sizeof(ngx_addr_t)); + if (u->addrs == NULL) { + return NGX_ERROR; + } + + saun = ngx_pcalloc(pool, sizeof(struct sockaddr_un)); + if (saun == NULL) { + return NGX_ERROR; + } + + u->family = AF_UNIX; + u->naddrs = 1; + + saun->sun_family = AF_UNIX; + (void) ngx_cpystrn((u_char *) saun->sun_path, path, len); + + u->addrs[0].sockaddr = (struct sockaddr *) saun; + u->addrs[0].socklen = sizeof(struct sockaddr_un); + u->addrs[0].name.len = len + 4; + u->addrs[0].name.data = u->url.data; + + return NGX_OK; + +#else + + u->err = "the unix domain sockets are not supported on this platform"; + + return NGX_ERROR; + +#endif +} + + +static ngx_int_t +ngx_parse_inet_url(ngx_pool_t *pool, ngx_url_t *u) +{ + u_char *p, *host, *port, *last, *uri, *args; + size_t len; + ngx_int_t n; + struct sockaddr_in *sin; +#if (NGX_HAVE_INET6) + struct sockaddr_in6 *sin6; +#endif + + u->socklen = sizeof(struct sockaddr_in); + sin = (struct sockaddr_in *) &u->sockaddr; + sin->sin_family = AF_INET; + + u->family = AF_INET; + + host = u->url.data; + + last = host + u->url.len; + + port = ngx_strlchr(host, last, ':'); + + uri = ngx_strlchr(host, last, '/'); + + args = ngx_strlchr(host, last, '?'); + + if (args) { + if (uri == NULL || args < uri) { + uri = args; + } + } + + if (uri) { + if (u->listen || !u->uri_part) { + u->err = "invalid host"; + return NGX_ERROR; + } + + u->uri.len = last - uri; + u->uri.data = uri; + + last = uri; + + if (uri < port) { + port = NULL; + } + } + + if (port) { + port++; + + len = last - port; + + n = ngx_atoi(port, len); + + if (n < 1 || n > 65535) { + u->err = "invalid port"; + return NGX_ERROR; + } + + u->port = (in_port_t) n; + sin->sin_port = htons((in_port_t) n); + + u->port_text.len = len; + u->port_text.data = port; + + last = port - 1; + + } else { + if (uri == NULL) { + + if (u->listen) { + + /* test value as port only */ + + n = ngx_atoi(host, last - host); + + if (n != NGX_ERROR) { + + if (n < 1 || n > 65535) { + u->err = "invalid port"; + return NGX_ERROR; + } + + u->port = (in_port_t) n; + sin->sin_port = htons((in_port_t) n); + + u->port_text.len = last - host; + u->port_text.data = host; + + u->wildcard = 1; + + return NGX_OK; + } + } + } + + u->no_port = 1; + u->port = u->default_port; + sin->sin_port = htons(u->default_port); + } + + len = last - host; + + if (len == 0) { + u->err = "no host"; + return NGX_ERROR; + } + + u->host.len = len; + u->host.data = host; + + if (u->listen && len == 1 && *host == '*') { + sin->sin_addr.s_addr = INADDR_ANY; + u->wildcard = 1; + return NGX_OK; + } + + sin->sin_addr.s_addr = ngx_inet_addr(host, len); + + if (sin->sin_addr.s_addr != INADDR_NONE) { + + if (sin->sin_addr.s_addr == INADDR_ANY) { + u->wildcard = 1; + } + + u->naddrs = 1; + + u->addrs = ngx_pcalloc(pool, sizeof(ngx_addr_t)); + if (u->addrs == NULL) { + return NGX_ERROR; + } + + sin = ngx_pcalloc(pool, sizeof(struct sockaddr_in)); + if (sin == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(sin, &u->sockaddr, sizeof(struct sockaddr_in)); + + u->addrs[0].sockaddr = (struct sockaddr *) sin; + u->addrs[0].socklen = sizeof(struct sockaddr_in); + + p = ngx_pnalloc(pool, u->host.len + sizeof(":65535") - 1); + if (p == NULL) { + return NGX_ERROR; + } + + u->addrs[0].name.len = ngx_sprintf(p, "%V:%d", + &u->host, u->port) - p; + u->addrs[0].name.data = p; + + return NGX_OK; + } + + if (u->no_resolve) { + return NGX_OK; + } + + if (ngx_inet_resolve_host(pool, u) != NGX_OK) { + return NGX_ERROR; + } + + u->family = u->addrs[0].sockaddr->sa_family; + u->socklen = u->addrs[0].socklen; + ngx_memcpy(&u->sockaddr, u->addrs[0].sockaddr, u->addrs[0].socklen); + + switch (u->family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + sin6 = (struct sockaddr_in6 *) &u->sockaddr; + + if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { + u->wildcard = 1; + } + + break; +#endif + + default: /* AF_INET */ + sin = (struct sockaddr_in *) &u->sockaddr; + + if (sin->sin_addr.s_addr == INADDR_ANY) { + u->wildcard = 1; + } + + break; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_parse_inet6_url(ngx_pool_t *pool, ngx_url_t *u) +{ +#if (NGX_HAVE_INET6) + u_char *p, *host, *port, *last, *uri; + size_t len; + ngx_int_t n; + struct sockaddr_in6 *sin6; + + u->socklen = sizeof(struct sockaddr_in6); + sin6 = (struct sockaddr_in6 *) &u->sockaddr; + sin6->sin6_family = AF_INET6; + + host = u->url.data + 1; + + last = u->url.data + u->url.len; + + p = ngx_strlchr(host, last, ']'); + + if (p == NULL) { + u->err = "invalid host"; + return NGX_ERROR; + } + + port = p + 1; + + uri = ngx_strlchr(port, last, '/'); + + if (uri) { + if (u->listen || !u->uri_part) { + u->err = "invalid host"; + return NGX_ERROR; + } + + u->uri.len = last - uri; + u->uri.data = uri; + + last = uri; + } + + if (port < last) { + if (*port != ':') { + u->err = "invalid host"; + return NGX_ERROR; + } + + port++; + + len = last - port; + + n = ngx_atoi(port, len); + + if (n < 1 || n > 65535) { + u->err = "invalid port"; + return NGX_ERROR; + } + + u->port = (in_port_t) n; + sin6->sin6_port = htons((in_port_t) n); + + u->port_text.len = len; + u->port_text.data = port; + + } else { + u->no_port = 1; + u->port = u->default_port; + sin6->sin6_port = htons(u->default_port); + } + + len = p - host; + + if (len == 0) { + u->err = "no host"; + return NGX_ERROR; + } + + u->host.len = len + 2; + u->host.data = host - 1; + + if (ngx_inet6_addr(host, len, sin6->sin6_addr.s6_addr) != NGX_OK) { + u->err = "invalid IPv6 address"; + return NGX_ERROR; + } + + if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { + u->wildcard = 1; + } + + u->family = AF_INET6; + u->naddrs = 1; + + u->addrs = ngx_pcalloc(pool, sizeof(ngx_addr_t)); + if (u->addrs == NULL) { + return NGX_ERROR; + } + + sin6 = ngx_pcalloc(pool, sizeof(struct sockaddr_in6)); + if (sin6 == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(sin6, &u->sockaddr, sizeof(struct sockaddr_in6)); + + u->addrs[0].sockaddr = (struct sockaddr *) sin6; + u->addrs[0].socklen = sizeof(struct sockaddr_in6); + + p = ngx_pnalloc(pool, u->host.len + sizeof(":65535") - 1); + if (p == NULL) { + return NGX_ERROR; + } + + u->addrs[0].name.len = ngx_sprintf(p, "%V:%d", + &u->host, u->port) - p; + u->addrs[0].name.data = p; + + return NGX_OK; + +#else + + u->err = "the INET6 sockets are not supported on this platform"; + + return NGX_ERROR; + +#endif +} + + +#if (NGX_HAVE_GETADDRINFO && NGX_HAVE_INET6) + +ngx_int_t +ngx_inet_resolve_host(ngx_pool_t *pool, ngx_url_t *u) +{ + u_char *p, *host; + size_t len; + in_port_t port; + ngx_uint_t i; + struct addrinfo hints, *res, *rp; + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; + + port = htons(u->port); + + host = ngx_alloc(u->host.len + 1, pool->log); + if (host == NULL) { + return NGX_ERROR; + } + + (void) ngx_cpystrn(host, u->host.data, u->host.len + 1); + + ngx_memzero(&hints, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; +#ifdef AI_ADDRCONFIG + hints.ai_flags = AI_ADDRCONFIG; +#endif + + if (getaddrinfo((char *) host, NULL, &hints, &res) != 0) { + u->err = "host not found"; + ngx_free(host); + return NGX_ERROR; + } + + ngx_free(host); + + for (i = 0, rp = res; rp != NULL; rp = rp->ai_next) { + + switch (rp->ai_family) { + + case AF_INET: + case AF_INET6: + break; + + default: + continue; + } + + i++; + } + + if (i == 0) { + u->err = "host not found"; + goto failed; + } + + /* MP: ngx_shared_palloc() */ + + u->addrs = ngx_pcalloc(pool, i * sizeof(ngx_addr_t)); + if (u->addrs == NULL) { + goto failed; + } + + u->naddrs = i; + + i = 0; + + /* AF_INET addresses first */ + + for (rp = res; rp != NULL; rp = rp->ai_next) { + + if (rp->ai_family != AF_INET) { + continue; + } + + sin = ngx_pcalloc(pool, rp->ai_addrlen); + if (sin == NULL) { + goto failed; + } + + ngx_memcpy(sin, rp->ai_addr, rp->ai_addrlen); + + sin->sin_port = port; + + u->addrs[i].sockaddr = (struct sockaddr *) sin; + u->addrs[i].socklen = rp->ai_addrlen; + + len = NGX_INET_ADDRSTRLEN + sizeof(":65535") - 1; + + p = ngx_pnalloc(pool, len); + if (p == NULL) { + goto failed; + } + + len = ngx_sock_ntop((struct sockaddr *) sin, rp->ai_addrlen, p, len, 1); + + u->addrs[i].name.len = len; + u->addrs[i].name.data = p; + + i++; + } + + for (rp = res; rp != NULL; rp = rp->ai_next) { + + if (rp->ai_family != AF_INET6) { + continue; + } + + sin6 = ngx_pcalloc(pool, rp->ai_addrlen); + if (sin6 == NULL) { + goto failed; + } + + ngx_memcpy(sin6, rp->ai_addr, rp->ai_addrlen); + + sin6->sin6_port = port; + + u->addrs[i].sockaddr = (struct sockaddr *) sin6; + u->addrs[i].socklen = rp->ai_addrlen; + + len = NGX_INET6_ADDRSTRLEN + sizeof("[]:65535") - 1; + + p = ngx_pnalloc(pool, len); + if (p == NULL) { + goto failed; + } + + len = ngx_sock_ntop((struct sockaddr *) sin6, rp->ai_addrlen, p, + len, 1); + + u->addrs[i].name.len = len; + u->addrs[i].name.data = p; + + i++; + } + + freeaddrinfo(res); + return NGX_OK; + +failed: + + freeaddrinfo(res); + return NGX_ERROR; +} + +#else /* !NGX_HAVE_GETADDRINFO || !NGX_HAVE_INET6 */ + +ngx_int_t +ngx_inet_resolve_host(ngx_pool_t *pool, ngx_url_t *u) +{ + u_char *p, *host; + size_t len; + in_port_t port; + in_addr_t in_addr; + ngx_uint_t i; + struct hostent *h; + struct sockaddr_in *sin; + + /* AF_INET only */ + + port = htons(u->port); + + in_addr = ngx_inet_addr(u->host.data, u->host.len); + + if (in_addr == INADDR_NONE) { + host = ngx_alloc(u->host.len + 1, pool->log); + if (host == NULL) { + return NGX_ERROR; + } + + (void) ngx_cpystrn(host, u->host.data, u->host.len + 1); + + h = gethostbyname((char *) host); + + ngx_free(host); + + if (h == NULL || h->h_addr_list[0] == NULL) { + u->err = "host not found"; + return NGX_ERROR; + } + + for (i = 0; h->h_addr_list[i] != NULL; i++) { /* void */ } + + /* MP: ngx_shared_palloc() */ + + u->addrs = ngx_pcalloc(pool, i * sizeof(ngx_addr_t)); + if (u->addrs == NULL) { + return NGX_ERROR; + } + + u->naddrs = i; + + for (i = 0; i < u->naddrs; i++) { + + sin = ngx_pcalloc(pool, sizeof(struct sockaddr_in)); + if (sin == NULL) { + return NGX_ERROR; + } + + sin->sin_family = AF_INET; + sin->sin_port = port; + sin->sin_addr.s_addr = *(in_addr_t *) (h->h_addr_list[i]); + + u->addrs[i].sockaddr = (struct sockaddr *) sin; + u->addrs[i].socklen = sizeof(struct sockaddr_in); + + len = NGX_INET_ADDRSTRLEN + sizeof(":65535") - 1; + + p = ngx_pnalloc(pool, len); + if (p == NULL) { + return NGX_ERROR; + } + + len = ngx_sock_ntop((struct sockaddr *) sin, + sizeof(struct sockaddr_in), p, len, 1); + + u->addrs[i].name.len = len; + u->addrs[i].name.data = p; + } + + } else { + + /* MP: ngx_shared_palloc() */ + + u->addrs = ngx_pcalloc(pool, sizeof(ngx_addr_t)); + if (u->addrs == NULL) { + return NGX_ERROR; + } + + sin = ngx_pcalloc(pool, sizeof(struct sockaddr_in)); + if (sin == NULL) { + return NGX_ERROR; + } + + u->naddrs = 1; + + sin->sin_family = AF_INET; + sin->sin_port = port; + sin->sin_addr.s_addr = in_addr; + + u->addrs[0].sockaddr = (struct sockaddr *) sin; + u->addrs[0].socklen = sizeof(struct sockaddr_in); + + p = ngx_pnalloc(pool, u->host.len + sizeof(":65535") - 1); + if (p == NULL) { + return NGX_ERROR; + } + + u->addrs[0].name.len = ngx_sprintf(p, "%V:%d", + &u->host, ntohs(port)) - p; + u->addrs[0].name.data = p; + } + + return NGX_OK; +} + +#endif /* NGX_HAVE_GETADDRINFO && NGX_HAVE_INET6 */ + + +ngx_int_t +ngx_cmp_sockaddr(struct sockaddr *sa1, socklen_t slen1, + struct sockaddr *sa2, socklen_t slen2, ngx_uint_t cmp_port) +{ + struct sockaddr_in *sin1, *sin2; +#if (NGX_HAVE_INET6) + struct sockaddr_in6 *sin61, *sin62; +#endif +#if (NGX_HAVE_UNIX_DOMAIN) + size_t len; + struct sockaddr_un *saun1, *saun2; +#endif + + if (sa1->sa_family != sa2->sa_family) { + return NGX_DECLINED; + } + + switch (sa1->sa_family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + + sin61 = (struct sockaddr_in6 *) sa1; + sin62 = (struct sockaddr_in6 *) sa2; + + if (cmp_port && sin61->sin6_port != sin62->sin6_port) { + return NGX_DECLINED; + } + + if (ngx_memcmp(&sin61->sin6_addr, &sin62->sin6_addr, 16) != 0) { + return NGX_DECLINED; + } + + break; +#endif + +#if (NGX_HAVE_UNIX_DOMAIN) + case AF_UNIX: + + saun1 = (struct sockaddr_un *) sa1; + saun2 = (struct sockaddr_un *) sa2; + + if (slen1 < slen2) { + len = slen1 - offsetof(struct sockaddr_un, sun_path); + + } else { + len = slen2 - offsetof(struct sockaddr_un, sun_path); + } + + if (len > sizeof(saun1->sun_path)) { + len = sizeof(saun1->sun_path); + } + + if (ngx_memcmp(&saun1->sun_path, &saun2->sun_path, len) != 0) { + return NGX_DECLINED; + } + + break; +#endif + + default: /* AF_INET */ + + sin1 = (struct sockaddr_in *) sa1; + sin2 = (struct sockaddr_in *) sa2; + + if (cmp_port && sin1->sin_port != sin2->sin_port) { + return NGX_DECLINED; + } + + if (sin1->sin_addr.s_addr != sin2->sin_addr.s_addr) { + return NGX_DECLINED; + } + + break; + } + + return NGX_OK; +} + + +in_port_t +ngx_inet_get_port(struct sockaddr *sa) +{ + struct sockaddr_in *sin; +#if (NGX_HAVE_INET6) + struct sockaddr_in6 *sin6; +#endif + + switch (sa->sa_family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + sin6 = (struct sockaddr_in6 *) sa; + return ntohs(sin6->sin6_port); +#endif + +#if (NGX_HAVE_UNIX_DOMAIN) + case AF_UNIX: + return 0; +#endif + + default: /* AF_INET */ + sin = (struct sockaddr_in *) sa; + return ntohs(sin->sin_port); + } +} + + +void +ngx_inet_set_port(struct sockaddr *sa, in_port_t port) +{ + struct sockaddr_in *sin; +#if (NGX_HAVE_INET6) + struct sockaddr_in6 *sin6; +#endif + + switch (sa->sa_family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + sin6 = (struct sockaddr_in6 *) sa; + sin6->sin6_port = htons(port); + break; +#endif + +#if (NGX_HAVE_UNIX_DOMAIN) + case AF_UNIX: + break; +#endif + + default: /* AF_INET */ + sin = (struct sockaddr_in *) sa; + sin->sin_port = htons(port); + break; + } +} diff --git a/app/nginx/src/core/ngx_inet.h b/app/nginx/src/core/ngx_inet.h new file mode 100644 index 0000000..538771e --- /dev/null +++ b/app/nginx/src/core/ngx_inet.h @@ -0,0 +1,129 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_INET_H_INCLUDED_ +#define _NGX_INET_H_INCLUDED_ + + +#include +#include + + +#define NGX_INET_ADDRSTRLEN (sizeof("255.255.255.255") - 1) +#define NGX_INET6_ADDRSTRLEN \ + (sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255") - 1) +#define NGX_UNIX_ADDRSTRLEN \ + (sizeof(struct sockaddr_un) - offsetof(struct sockaddr_un, sun_path)) + +#if (NGX_HAVE_UNIX_DOMAIN) +#define NGX_SOCKADDR_STRLEN (sizeof("unix:") - 1 + NGX_UNIX_ADDRSTRLEN) +#elif (NGX_HAVE_INET6) +#define NGX_SOCKADDR_STRLEN (NGX_INET6_ADDRSTRLEN + sizeof("[]:65535") - 1) +#else +#define NGX_SOCKADDR_STRLEN (NGX_INET_ADDRSTRLEN + sizeof(":65535") - 1) +#endif + +/* compatibility */ +#define NGX_SOCKADDRLEN sizeof(ngx_sockaddr_t) + + +typedef union { + struct sockaddr sockaddr; + struct sockaddr_in sockaddr_in; +#if (NGX_HAVE_INET6) + struct sockaddr_in6 sockaddr_in6; +#endif +#if (NGX_HAVE_UNIX_DOMAIN) + struct sockaddr_un sockaddr_un; +#endif +} ngx_sockaddr_t; + + +typedef struct { + in_addr_t addr; + in_addr_t mask; +} ngx_in_cidr_t; + + +#if (NGX_HAVE_INET6) + +typedef struct { + struct in6_addr addr; + struct in6_addr mask; +} ngx_in6_cidr_t; + +#endif + + +typedef struct { + ngx_uint_t family; + union { + ngx_in_cidr_t in; +#if (NGX_HAVE_INET6) + ngx_in6_cidr_t in6; +#endif + } u; +} ngx_cidr_t; + + +typedef struct { + struct sockaddr *sockaddr; + socklen_t socklen; + ngx_str_t name; +} ngx_addr_t; + + +typedef struct { + ngx_str_t url; + ngx_str_t host; + ngx_str_t port_text; + ngx_str_t uri; + + in_port_t port; + in_port_t default_port; + int family; + + unsigned listen:1; + unsigned uri_part:1; + unsigned no_resolve:1; + + unsigned no_port:1; + unsigned wildcard:1; + + socklen_t socklen; + ngx_sockaddr_t sockaddr; + + ngx_addr_t *addrs; + ngx_uint_t naddrs; + + char *err; +} ngx_url_t; + + +in_addr_t ngx_inet_addr(u_char *text, size_t len); +#if (NGX_HAVE_INET6) +ngx_int_t ngx_inet6_addr(u_char *p, size_t len, u_char *addr); +size_t ngx_inet6_ntop(u_char *p, u_char *text, size_t len); +#endif +size_t ngx_sock_ntop(struct sockaddr *sa, socklen_t socklen, u_char *text, + size_t len, ngx_uint_t port); +size_t ngx_inet_ntop(int family, void *addr, u_char *text, size_t len); +ngx_int_t ngx_ptocidr(ngx_str_t *text, ngx_cidr_t *cidr); +ngx_int_t ngx_cidr_match(struct sockaddr *sa, ngx_array_t *cidrs); +ngx_int_t ngx_parse_addr(ngx_pool_t *pool, ngx_addr_t *addr, u_char *text, + size_t len); +ngx_int_t ngx_parse_addr_port(ngx_pool_t *pool, ngx_addr_t *addr, + u_char *text, size_t len); +ngx_int_t ngx_parse_url(ngx_pool_t *pool, ngx_url_t *u); +ngx_int_t ngx_inet_resolve_host(ngx_pool_t *pool, ngx_url_t *u); +ngx_int_t ngx_cmp_sockaddr(struct sockaddr *sa1, socklen_t slen1, + struct sockaddr *sa2, socklen_t slen2, ngx_uint_t cmp_port); +in_port_t ngx_inet_get_port(struct sockaddr *sa); +void ngx_inet_set_port(struct sockaddr *sa, in_port_t port); + + +#endif /* _NGX_INET_H_INCLUDED_ */ diff --git a/app/nginx/src/core/ngx_list.c b/app/nginx/src/core/ngx_list.c new file mode 100644 index 0000000..d0eb159 --- /dev/null +++ b/app/nginx/src/core/ngx_list.c @@ -0,0 +1,63 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include + + +ngx_list_t * +ngx_list_create(ngx_pool_t *pool, ngx_uint_t n, size_t size) +{ + ngx_list_t *list; + + list = ngx_palloc(pool, sizeof(ngx_list_t)); + if (list == NULL) { + return NULL; + } + + if (ngx_list_init(list, pool, n, size) != NGX_OK) { + return NULL; + } + + return list; +} + + +void * +ngx_list_push(ngx_list_t *l) +{ + void *elt; + ngx_list_part_t *last; + + last = l->last; + + if (last->nelts == l->nalloc) { + + /* the last part is full, allocate a new list part */ + + last = ngx_palloc(l->pool, sizeof(ngx_list_part_t)); + if (last == NULL) { + return NULL; + } + + last->elts = ngx_palloc(l->pool, l->nalloc * l->size); + if (last->elts == NULL) { + return NULL; + } + + last->nelts = 0; + last->next = NULL; + + l->last->next = last; + l->last = last; + } + + elt = (char *) last->elts + l->size * last->nelts; + last->nelts++; + + return elt; +} diff --git a/app/nginx/src/core/ngx_list.h b/app/nginx/src/core/ngx_list.h new file mode 100644 index 0000000..e0fe643 --- /dev/null +++ b/app/nginx/src/core/ngx_list.h @@ -0,0 +1,83 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_LIST_H_INCLUDED_ +#define _NGX_LIST_H_INCLUDED_ + + +#include +#include + + +typedef struct ngx_list_part_s ngx_list_part_t; + +struct ngx_list_part_s { + void *elts; + ngx_uint_t nelts; + ngx_list_part_t *next; +}; + + +typedef struct { + ngx_list_part_t *last; + ngx_list_part_t part; + size_t size; + ngx_uint_t nalloc; + ngx_pool_t *pool; +} ngx_list_t; + + +ngx_list_t *ngx_list_create(ngx_pool_t *pool, ngx_uint_t n, size_t size); + +static ngx_inline ngx_int_t +ngx_list_init(ngx_list_t *list, ngx_pool_t *pool, ngx_uint_t n, size_t size) +{ + list->part.elts = ngx_palloc(pool, n * size); + if (list->part.elts == NULL) { + return NGX_ERROR; + } + + list->part.nelts = 0; + list->part.next = NULL; + list->last = &list->part; + list->size = size; + list->nalloc = n; + list->pool = pool; + + return NGX_OK; +} + + +/* + * + * the iteration through the list: + * + * part = &list.part; + * data = part->elts; + * + * for (i = 0 ;; i++) { + * + * if (i >= part->nelts) { + * if (part->next == NULL) { + * break; + * } + * + * part = part->next; + * data = part->elts; + * i = 0; + * } + * + * ... data[i] ... + * + * } + */ + + +void *ngx_list_push(ngx_list_t *list); + + +#endif /* _NGX_LIST_H_INCLUDED_ */ diff --git a/app/nginx/src/core/ngx_log.c b/app/nginx/src/core/ngx_log.c new file mode 100644 index 0000000..8e9408d --- /dev/null +++ b/app/nginx/src/core/ngx_log.c @@ -0,0 +1,755 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include + + +static char *ngx_error_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static char *ngx_log_set_levels(ngx_conf_t *cf, ngx_log_t *log); +static void ngx_log_insert(ngx_log_t *log, ngx_log_t *new_log); + + +#if (NGX_DEBUG) + +static void ngx_log_memory_writer(ngx_log_t *log, ngx_uint_t level, + u_char *buf, size_t len); +static void ngx_log_memory_cleanup(void *data); + + +typedef struct { + u_char *start; + u_char *end; + u_char *pos; + ngx_atomic_t written; +} ngx_log_memory_buf_t; + +#endif + + +static ngx_command_t ngx_errlog_commands[] = { + + { ngx_string("error_log"), + NGX_MAIN_CONF|NGX_CONF_1MORE, + ngx_error_log, + 0, + 0, + NULL }, + + ngx_null_command +}; + + +static ngx_core_module_t ngx_errlog_module_ctx = { + ngx_string("errlog"), + NULL, + NULL +}; + + +ngx_module_t ngx_errlog_module = { + NGX_MODULE_V1, + &ngx_errlog_module_ctx, /* module context */ + ngx_errlog_commands, /* 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_log_t ngx_log; +static ngx_open_file_t ngx_log_file; +ngx_uint_t ngx_use_stderr = 1; + + +static ngx_str_t err_levels[] = { + ngx_null_string, + ngx_string("emerg"), + ngx_string("alert"), + ngx_string("crit"), + ngx_string("error"), + ngx_string("warn"), + ngx_string("notice"), + ngx_string("info"), + ngx_string("debug") +}; + +static const char *debug_levels[] = { + "debug_core", "debug_alloc", "debug_mutex", "debug_event", + "debug_http", "debug_mail", "debug_stream" +}; + + +#if (NGX_HAVE_VARIADIC_MACROS) + +void +ngx_log_error_core(ngx_uint_t level, ngx_log_t *log, ngx_err_t err, + const char *fmt, ...) + +#else + +void +ngx_log_error_core(ngx_uint_t level, ngx_log_t *log, ngx_err_t err, + const char *fmt, va_list args) + +#endif +{ +#if (NGX_HAVE_VARIADIC_MACROS) + va_list args; +#endif + u_char *p, *last, *msg; + ssize_t n; + ngx_uint_t wrote_stderr, debug_connection; + u_char errstr[NGX_MAX_ERROR_STR]; + + last = errstr + NGX_MAX_ERROR_STR; + + p = ngx_cpymem(errstr, ngx_cached_err_log_time.data, + ngx_cached_err_log_time.len); + + p = ngx_slprintf(p, last, " [%V] ", &err_levels[level]); + + /* pid#tid */ + p = ngx_slprintf(p, last, "%P#" NGX_TID_T_FMT ": ", + ngx_log_pid, ngx_log_tid); + + if (log->connection) { + p = ngx_slprintf(p, last, "*%uA ", log->connection); + } + + msg = p; + +#if (NGX_HAVE_VARIADIC_MACROS) + + va_start(args, fmt); + p = ngx_vslprintf(p, last, fmt, args); + va_end(args); + +#else + + p = ngx_vslprintf(p, last, fmt, args); + +#endif + + if (err) { + p = ngx_log_errno(p, last, err); + } + + if (level != NGX_LOG_DEBUG && log->handler) { + p = log->handler(log, p, last - p); + } + + if (p > last - NGX_LINEFEED_SIZE) { + p = last - NGX_LINEFEED_SIZE; + } + + ngx_linefeed(p); + + wrote_stderr = 0; + debug_connection = (log->log_level & NGX_LOG_DEBUG_CONNECTION) != 0; + + while (log) { + + if (log->log_level < level && !debug_connection) { + break; + } + + if (log->writer) { + log->writer(log, level, errstr, p - errstr); + goto next; + } + + if (ngx_time() == log->disk_full_time) { + + /* + * on FreeBSD writing to a full filesystem with enabled softupdates + * may block process for much longer time than writing to non-full + * filesystem, so we skip writing to a log for one second + */ + + goto next; + } + + n = ngx_write_fd(log->file->fd, errstr, p - errstr); + + if (n == -1 && ngx_errno == NGX_ENOSPC) { + log->disk_full_time = ngx_time(); + } + + if (log->file->fd == ngx_stderr) { + wrote_stderr = 1; + } + + next: + + log = log->next; + } + + if (!ngx_use_stderr + || level > NGX_LOG_WARN + || wrote_stderr) + { + return; + } + + msg -= (7 + err_levels[level].len + 3); + + (void) ngx_sprintf(msg, "nginx: [%V] ", &err_levels[level]); + + (void) ngx_write_console(ngx_stderr, msg, p - msg); +} + + +#if !(NGX_HAVE_VARIADIC_MACROS) + +void ngx_cdecl +ngx_log_error(ngx_uint_t level, ngx_log_t *log, ngx_err_t err, + const char *fmt, ...) +{ + va_list args; + + if (log->log_level >= level) { + va_start(args, fmt); + ngx_log_error_core(level, log, err, fmt, args); + va_end(args); + } +} + + +void ngx_cdecl +ngx_log_debug_core(ngx_log_t *log, ngx_err_t err, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + ngx_log_error_core(NGX_LOG_DEBUG, log, err, fmt, args); + va_end(args); +} + +#endif + + +void ngx_cdecl +ngx_log_abort(ngx_err_t err, const char *fmt, ...) +{ + u_char *p; + va_list args; + u_char errstr[NGX_MAX_CONF_ERRSTR]; + + va_start(args, fmt); + p = ngx_vsnprintf(errstr, sizeof(errstr) - 1, fmt, args); + va_end(args); + + ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, err, + "%*s", p - errstr, errstr); +} + + +void ngx_cdecl +ngx_log_stderr(ngx_err_t err, const char *fmt, ...) +{ + u_char *p, *last; + va_list args; + u_char errstr[NGX_MAX_ERROR_STR]; + + last = errstr + NGX_MAX_ERROR_STR; + + p = ngx_cpymem(errstr, "nginx: ", 7); + + va_start(args, fmt); + p = ngx_vslprintf(p, last, fmt, args); + va_end(args); + + if (err) { + p = ngx_log_errno(p, last, err); + } + + if (p > last - NGX_LINEFEED_SIZE) { + p = last - NGX_LINEFEED_SIZE; + } + + ngx_linefeed(p); + + (void) ngx_write_console(ngx_stderr, errstr, p - errstr); +} + + +u_char * +ngx_log_errno(u_char *buf, u_char *last, ngx_err_t err) +{ + if (buf > last - 50) { + + /* leave a space for an error code */ + + buf = last - 50; + *buf++ = '.'; + *buf++ = '.'; + *buf++ = '.'; + } + +#if (NGX_WIN32) + buf = ngx_slprintf(buf, last, ((unsigned) err < 0x80000000) + ? " (%d: " : " (%Xd: ", err); +#else + buf = ngx_slprintf(buf, last, " (%d: ", err); +#endif + + buf = ngx_strerror(err, buf, last - buf); + + if (buf < last) { + *buf++ = ')'; + } + + return buf; +} + + +ngx_log_t * +ngx_log_init(u_char *prefix) +{ + u_char *p, *name; + size_t nlen, plen; + + ngx_log.file = &ngx_log_file; + ngx_log.log_level = NGX_LOG_NOTICE; + + name = (u_char *) NGX_ERROR_LOG_PATH; + + /* + * we use ngx_strlen() here since BCC warns about + * condition is always false and unreachable code + */ + + nlen = ngx_strlen(name); + + if (nlen == 0) { + ngx_log_file.fd = ngx_stderr; + return &ngx_log; + } + + p = NULL; + +#if (NGX_WIN32) + if (name[1] != ':') { +#else + if (name[0] != '/') { +#endif + + if (prefix) { + plen = ngx_strlen(prefix); + + } else { +#ifdef NGX_PREFIX + prefix = (u_char *) NGX_PREFIX; + plen = ngx_strlen(prefix); +#else + plen = 0; +#endif + } + + if (plen) { + name = malloc(plen + nlen + 2); + if (name == NULL) { + return NULL; + } + + p = ngx_cpymem(name, prefix, plen); + + if (!ngx_path_separator(*(p - 1))) { + *p++ = '/'; + } + + ngx_cpystrn(p, (u_char *) NGX_ERROR_LOG_PATH, nlen + 1); + + p = name; + } + } + + ngx_log_file.fd = ngx_open_file(name, NGX_FILE_APPEND, + NGX_FILE_CREATE_OR_OPEN, + NGX_FILE_DEFAULT_ACCESS); + + if (ngx_log_file.fd == NGX_INVALID_FILE) { + ngx_log_stderr(ngx_errno, + "[alert] could not open error log file: " + ngx_open_file_n " \"%s\" failed", name); +#if (NGX_WIN32) + ngx_event_log(ngx_errno, + "could not open error log file: " + ngx_open_file_n " \"%s\" failed", name); +#endif + + ngx_log_file.fd = ngx_stderr; + } + + if (p) { + ngx_free(p); + } + + return &ngx_log; +} + + +ngx_int_t +ngx_log_open_default(ngx_cycle_t *cycle) +{ + ngx_log_t *log; + static ngx_str_t error_log = ngx_string(NGX_ERROR_LOG_PATH); + + if (ngx_log_get_file_log(&cycle->new_log) != NULL) { + return NGX_OK; + } + + if (cycle->new_log.log_level != 0) { + /* there are some error logs, but no files */ + + log = ngx_pcalloc(cycle->pool, sizeof(ngx_log_t)); + if (log == NULL) { + return NGX_ERROR; + } + + } else { + /* no error logs at all */ + log = &cycle->new_log; + } + + log->log_level = NGX_LOG_ERR; + + log->file = ngx_conf_open_file(cycle, &error_log); + if (log->file == NULL) { + return NGX_ERROR; + } + + if (log != &cycle->new_log) { + ngx_log_insert(&cycle->new_log, log); + } + + return NGX_OK; +} + + +ngx_int_t +ngx_log_redirect_stderr(ngx_cycle_t *cycle) +{ + ngx_fd_t fd; + + if (cycle->log_use_stderr) { + return NGX_OK; + } + + /* file log always exists when we are called */ + fd = ngx_log_get_file_log(cycle->log)->file->fd; + + if (fd != ngx_stderr) { + if (ngx_set_stderr(fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, + ngx_set_stderr_n " failed"); + + return NGX_ERROR; + } + } + + return NGX_OK; +} + + +ngx_log_t * +ngx_log_get_file_log(ngx_log_t *head) +{ + ngx_log_t *log; + + for (log = head; log; log = log->next) { + if (log->file != NULL) { + return log; + } + } + + return NULL; +} + + +static char * +ngx_log_set_levels(ngx_conf_t *cf, ngx_log_t *log) +{ + ngx_uint_t i, n, d, found; + ngx_str_t *value; + + if (cf->args->nelts == 2) { + log->log_level = NGX_LOG_ERR; + return NGX_CONF_OK; + } + + value = cf->args->elts; + + for (i = 2; i < cf->args->nelts; i++) { + found = 0; + + for (n = 1; n <= NGX_LOG_DEBUG; n++) { + if (ngx_strcmp(value[i].data, err_levels[n].data) == 0) { + + if (log->log_level != 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "duplicate log level \"%V\"", + &value[i]); + return NGX_CONF_ERROR; + } + + log->log_level = n; + found = 1; + break; + } + } + + for (n = 0, d = NGX_LOG_DEBUG_FIRST; d <= NGX_LOG_DEBUG_LAST; d <<= 1) { + if (ngx_strcmp(value[i].data, debug_levels[n++]) == 0) { + if (log->log_level & ~NGX_LOG_DEBUG_ALL) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid log level \"%V\"", + &value[i]); + return NGX_CONF_ERROR; + } + + log->log_level |= d; + found = 1; + break; + } + } + + + if (!found) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid log level \"%V\"", &value[i]); + return NGX_CONF_ERROR; + } + } + + if (log->log_level == NGX_LOG_DEBUG) { + log->log_level = NGX_LOG_DEBUG_ALL; + } + + return NGX_CONF_OK; +} + + +static char * +ngx_error_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_log_t *dummy; + + dummy = &cf->cycle->new_log; + + return ngx_log_set_log(cf, &dummy); +} + + +char * +ngx_log_set_log(ngx_conf_t *cf, ngx_log_t **head) +{ + ngx_log_t *new_log; + ngx_str_t *value, name; + ngx_syslog_peer_t *peer; + + if (*head != NULL && (*head)->log_level == 0) { + new_log = *head; + + } else { + + new_log = ngx_pcalloc(cf->pool, sizeof(ngx_log_t)); + if (new_log == NULL) { + return NGX_CONF_ERROR; + } + + if (*head == NULL) { + *head = new_log; + } + } + + value = cf->args->elts; + + if (ngx_strcmp(value[1].data, "stderr") == 0) { + ngx_str_null(&name); + cf->cycle->log_use_stderr = 1; + + new_log->file = ngx_conf_open_file(cf->cycle, &name); + if (new_log->file == NULL) { + return NGX_CONF_ERROR; + } + + } else if (ngx_strncmp(value[1].data, "memory:", 7) == 0) { + +#if (NGX_DEBUG) + size_t size, needed; + ngx_pool_cleanup_t *cln; + ngx_log_memory_buf_t *buf; + + value[1].len -= 7; + value[1].data += 7; + + needed = sizeof("MEMLOG :" NGX_LINEFEED) + + cf->conf_file->file.name.len + + NGX_SIZE_T_LEN + + NGX_INT_T_LEN + + NGX_MAX_ERROR_STR; + + size = ngx_parse_size(&value[1]); + + if (size == (size_t) NGX_ERROR || size < needed) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid buffer size \"%V\"", &value[1]); + return NGX_CONF_ERROR; + } + + buf = ngx_pcalloc(cf->pool, sizeof(ngx_log_memory_buf_t)); + if (buf == NULL) { + return NGX_CONF_ERROR; + } + + buf->start = ngx_pnalloc(cf->pool, size); + if (buf->start == NULL) { + return NGX_CONF_ERROR; + } + + buf->end = buf->start + size; + + buf->pos = ngx_slprintf(buf->start, buf->end, "MEMLOG %uz %V:%ui%N", + size, &cf->conf_file->file.name, + cf->conf_file->line); + + ngx_memset(buf->pos, ' ', buf->end - buf->pos); + + cln = ngx_pool_cleanup_add(cf->pool, 0); + if (cln == NULL) { + return NGX_CONF_ERROR; + } + + cln->data = new_log; + cln->handler = ngx_log_memory_cleanup; + + new_log->writer = ngx_log_memory_writer; + new_log->wdata = buf; + +#else + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "nginx was built without debug support"); + return NGX_CONF_ERROR; +#endif + + } else if (ngx_strncmp(value[1].data, "syslog:", 7) == 0) { + peer = ngx_pcalloc(cf->pool, sizeof(ngx_syslog_peer_t)); + if (peer == NULL) { + return NGX_CONF_ERROR; + } + + if (ngx_syslog_process_conf(cf, peer) != NGX_CONF_OK) { + return NGX_CONF_ERROR; + } + + new_log->writer = ngx_syslog_writer; + new_log->wdata = peer; + + } else { + new_log->file = ngx_conf_open_file(cf->cycle, &value[1]); + if (new_log->file == NULL) { + return NGX_CONF_ERROR; + } + } + + if (ngx_log_set_levels(cf, new_log) != NGX_CONF_OK) { + return NGX_CONF_ERROR; + } + + if (*head != new_log) { + ngx_log_insert(*head, new_log); + } + + return NGX_CONF_OK; +} + + +static void +ngx_log_insert(ngx_log_t *log, ngx_log_t *new_log) +{ + ngx_log_t tmp; + + if (new_log->log_level > log->log_level) { + + /* + * list head address is permanent, insert new log after + * head and swap its contents with head + */ + + tmp = *log; + *log = *new_log; + *new_log = tmp; + + log->next = new_log; + return; + } + + while (log->next) { + if (new_log->log_level > log->next->log_level) { + new_log->next = log->next; + log->next = new_log; + return; + } + + log = log->next; + } + + log->next = new_log; +} + + +#if (NGX_DEBUG) + +static void +ngx_log_memory_writer(ngx_log_t *log, ngx_uint_t level, u_char *buf, + size_t len) +{ + u_char *p; + size_t avail, written; + ngx_log_memory_buf_t *mem; + + mem = log->wdata; + + if (mem == NULL) { + return; + } + + written = ngx_atomic_fetch_add(&mem->written, len); + + p = mem->pos + written % (mem->end - mem->pos); + + avail = mem->end - p; + + if (avail >= len) { + ngx_memcpy(p, buf, len); + + } else { + ngx_memcpy(p, buf, avail); + ngx_memcpy(mem->pos, buf + avail, len - avail); + } +} + + +static void +ngx_log_memory_cleanup(void *data) +{ + ngx_log_t *log = data; + + ngx_log_debug0(NGX_LOG_DEBUG_CORE, log, 0, "destroy memory log buffer"); + + log->wdata = NULL; +} + +#endif diff --git a/app/nginx/src/core/ngx_log.h b/app/nginx/src/core/ngx_log.h new file mode 100644 index 0000000..afb73bf --- /dev/null +++ b/app/nginx/src/core/ngx_log.h @@ -0,0 +1,268 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_LOG_H_INCLUDED_ +#define _NGX_LOG_H_INCLUDED_ + + +#include +#include + + +#define NGX_LOG_STDERR 0 +#define NGX_LOG_EMERG 1 +#define NGX_LOG_ALERT 2 +#define NGX_LOG_CRIT 3 +#define NGX_LOG_ERR 4 +#define NGX_LOG_WARN 5 +#define NGX_LOG_NOTICE 6 +#define NGX_LOG_INFO 7 +#define NGX_LOG_DEBUG 8 + +#define NGX_LOG_DEBUG_CORE 0x010 +#define NGX_LOG_DEBUG_ALLOC 0x020 +#define NGX_LOG_DEBUG_MUTEX 0x040 +#define NGX_LOG_DEBUG_EVENT 0x080 +#define NGX_LOG_DEBUG_HTTP 0x100 +#define NGX_LOG_DEBUG_MAIL 0x200 +#define NGX_LOG_DEBUG_STREAM 0x400 + +/* + * do not forget to update debug_levels[] in src/core/ngx_log.c + * after the adding a new debug level + */ + +#define NGX_LOG_DEBUG_FIRST NGX_LOG_DEBUG_CORE +#define NGX_LOG_DEBUG_LAST NGX_LOG_DEBUG_STREAM +#define NGX_LOG_DEBUG_CONNECTION 0x80000000 +#define NGX_LOG_DEBUG_ALL 0x7ffffff0 + + +typedef u_char *(*ngx_log_handler_pt) (ngx_log_t *log, u_char *buf, size_t len); +typedef void (*ngx_log_writer_pt) (ngx_log_t *log, ngx_uint_t level, + u_char *buf, size_t len); + + +struct ngx_log_s { + ngx_uint_t log_level; + ngx_open_file_t *file; + + ngx_atomic_uint_t connection; + + time_t disk_full_time; + + ngx_log_handler_pt handler; + void *data; + + ngx_log_writer_pt writer; + void *wdata; + + /* + * we declare "action" as "char *" because the actions are usually + * the static strings and in the "u_char *" case we have to override + * their types all the time + */ + + char *action; + + ngx_log_t *next; +}; + + +#define NGX_MAX_ERROR_STR 2048 + + +/*********************************/ + +#if (NGX_HAVE_C99_VARIADIC_MACROS) + +#define NGX_HAVE_VARIADIC_MACROS 1 + +#define ngx_log_error(level, log, ...) \ + if ((log)->log_level >= level) ngx_log_error_core(level, log, __VA_ARGS__) + +void ngx_log_error_core(ngx_uint_t level, ngx_log_t *log, ngx_err_t err, + const char *fmt, ...); + +#define ngx_log_debug(level, log, ...) \ + if ((log)->log_level & level) \ + ngx_log_error_core(NGX_LOG_DEBUG, log, __VA_ARGS__) + +/*********************************/ + +#elif (NGX_HAVE_GCC_VARIADIC_MACROS) + +#define NGX_HAVE_VARIADIC_MACROS 1 + +#define ngx_log_error(level, log, args...) \ + if ((log)->log_level >= level) ngx_log_error_core(level, log, args) + +void ngx_log_error_core(ngx_uint_t level, ngx_log_t *log, ngx_err_t err, + const char *fmt, ...); + +#define ngx_log_debug(level, log, args...) \ + if ((log)->log_level & level) \ + ngx_log_error_core(NGX_LOG_DEBUG, log, args) + +/*********************************/ + +#else /* no variadic macros */ + +#define NGX_HAVE_VARIADIC_MACROS 0 + +void ngx_cdecl ngx_log_error(ngx_uint_t level, ngx_log_t *log, ngx_err_t err, + const char *fmt, ...); +void ngx_log_error_core(ngx_uint_t level, ngx_log_t *log, ngx_err_t err, + const char *fmt, va_list args); +void ngx_cdecl ngx_log_debug_core(ngx_log_t *log, ngx_err_t err, + const char *fmt, ...); + + +#endif /* variadic macros */ + + +/*********************************/ + +#if (NGX_DEBUG) + +#if (NGX_HAVE_VARIADIC_MACROS) + +#define ngx_log_debug0(level, log, err, fmt) \ + ngx_log_debug(level, log, err, fmt) + +#define ngx_log_debug1(level, log, err, fmt, arg1) \ + ngx_log_debug(level, log, err, fmt, arg1) + +#define ngx_log_debug2(level, log, err, fmt, arg1, arg2) \ + ngx_log_debug(level, log, err, fmt, arg1, arg2) + +#define ngx_log_debug3(level, log, err, fmt, arg1, arg2, arg3) \ + ngx_log_debug(level, log, err, fmt, arg1, arg2, arg3) + +#define ngx_log_debug4(level, log, err, fmt, arg1, arg2, arg3, arg4) \ + ngx_log_debug(level, log, err, fmt, arg1, arg2, arg3, arg4) + +#define ngx_log_debug5(level, log, err, fmt, arg1, arg2, arg3, arg4, arg5) \ + ngx_log_debug(level, log, err, fmt, arg1, arg2, arg3, arg4, arg5) + +#define ngx_log_debug6(level, log, err, fmt, \ + arg1, arg2, arg3, arg4, arg5, arg6) \ + ngx_log_debug(level, log, err, fmt, \ + arg1, arg2, arg3, arg4, arg5, arg6) + +#define ngx_log_debug7(level, log, err, fmt, \ + arg1, arg2, arg3, arg4, arg5, arg6, arg7) \ + ngx_log_debug(level, log, err, fmt, \ + arg1, arg2, arg3, arg4, arg5, arg6, arg7) + +#define ngx_log_debug8(level, log, err, fmt, \ + arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) \ + ngx_log_debug(level, log, err, fmt, \ + arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) + + +#else /* no variadic macros */ + +#define ngx_log_debug0(level, log, err, fmt) \ + if ((log)->log_level & level) \ + ngx_log_debug_core(log, err, fmt) + +#define ngx_log_debug1(level, log, err, fmt, arg1) \ + if ((log)->log_level & level) \ + ngx_log_debug_core(log, err, fmt, arg1) + +#define ngx_log_debug2(level, log, err, fmt, arg1, arg2) \ + if ((log)->log_level & level) \ + ngx_log_debug_core(log, err, fmt, arg1, arg2) + +#define ngx_log_debug3(level, log, err, fmt, arg1, arg2, arg3) \ + if ((log)->log_level & level) \ + ngx_log_debug_core(log, err, fmt, arg1, arg2, arg3) + +#define ngx_log_debug4(level, log, err, fmt, arg1, arg2, arg3, arg4) \ + if ((log)->log_level & level) \ + ngx_log_debug_core(log, err, fmt, arg1, arg2, arg3, arg4) + +#define ngx_log_debug5(level, log, err, fmt, arg1, arg2, arg3, arg4, arg5) \ + if ((log)->log_level & level) \ + ngx_log_debug_core(log, err, fmt, arg1, arg2, arg3, arg4, arg5) + +#define ngx_log_debug6(level, log, err, fmt, \ + arg1, arg2, arg3, arg4, arg5, arg6) \ + if ((log)->log_level & level) \ + ngx_log_debug_core(log, err, fmt, arg1, arg2, arg3, arg4, arg5, arg6) + +#define ngx_log_debug7(level, log, err, fmt, \ + arg1, arg2, arg3, arg4, arg5, arg6, arg7) \ + if ((log)->log_level & level) \ + ngx_log_debug_core(log, err, fmt, \ + arg1, arg2, arg3, arg4, arg5, arg6, arg7) + +#define ngx_log_debug8(level, log, err, fmt, \ + arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) \ + if ((log)->log_level & level) \ + ngx_log_debug_core(log, err, fmt, \ + arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) + +#endif + +#else /* !NGX_DEBUG */ + +#define ngx_log_debug0(level, log, err, fmt) +#define ngx_log_debug1(level, log, err, fmt, arg1) +#define ngx_log_debug2(level, log, err, fmt, arg1, arg2) +#define ngx_log_debug3(level, log, err, fmt, arg1, arg2, arg3) +#define ngx_log_debug4(level, log, err, fmt, arg1, arg2, arg3, arg4) +#define ngx_log_debug5(level, log, err, fmt, arg1, arg2, arg3, arg4, arg5) +#define ngx_log_debug6(level, log, err, fmt, arg1, arg2, arg3, arg4, arg5, arg6) +#define ngx_log_debug7(level, log, err, fmt, arg1, arg2, arg3, arg4, arg5, \ + arg6, arg7) +#define ngx_log_debug8(level, log, err, fmt, arg1, arg2, arg3, arg4, arg5, \ + arg6, arg7, arg8) + +#endif + +/*********************************/ + +ngx_log_t *ngx_log_init(u_char *prefix); +void ngx_cdecl ngx_log_abort(ngx_err_t err, const char *fmt, ...); +void ngx_cdecl ngx_log_stderr(ngx_err_t err, const char *fmt, ...); +u_char *ngx_log_errno(u_char *buf, u_char *last, ngx_err_t err); +ngx_int_t ngx_log_open_default(ngx_cycle_t *cycle); +ngx_int_t ngx_log_redirect_stderr(ngx_cycle_t *cycle); +ngx_log_t *ngx_log_get_file_log(ngx_log_t *head); +char *ngx_log_set_log(ngx_conf_t *cf, ngx_log_t **head); + + +/* + * ngx_write_stderr() cannot be implemented as macro, since + * MSVC does not allow to use #ifdef inside macro parameters. + * + * ngx_write_fd() is used instead of ngx_write_console(), since + * CharToOemBuff() inside ngx_write_console() cannot be used with + * read only buffer as destination and CharToOemBuff() is not needed + * for ngx_write_stderr() anyway. + */ +static ngx_inline void +ngx_write_stderr(char *text) +{ + (void) ngx_write_fd(ngx_stderr, text, ngx_strlen(text)); +} + + +static ngx_inline void +ngx_write_stdout(char *text) +{ + (void) ngx_write_fd(ngx_stdout, text, ngx_strlen(text)); +} + + +extern ngx_module_t ngx_errlog_module; +extern ngx_uint_t ngx_use_stderr; + + +#endif /* _NGX_LOG_H_INCLUDED_ */ diff --git a/app/nginx/src/core/ngx_md5.c b/app/nginx/src/core/ngx_md5.c new file mode 100644 index 0000000..c25d002 --- /dev/null +++ b/app/nginx/src/core/ngx_md5.c @@ -0,0 +1,283 @@ + +/* + * An internal implementation, based on Alexander Peslyak's + * public domain implementation: + * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5 + */ + + +#include +#include +#include + + +static const u_char *ngx_md5_body(ngx_md5_t *ctx, const u_char *data, + size_t size); + + +void +ngx_md5_init(ngx_md5_t *ctx) +{ + ctx->a = 0x67452301; + ctx->b = 0xefcdab89; + ctx->c = 0x98badcfe; + ctx->d = 0x10325476; + + ctx->bytes = 0; +} + + +void +ngx_md5_update(ngx_md5_t *ctx, const void *data, size_t size) +{ + size_t used, free; + + used = (size_t) (ctx->bytes & 0x3f); + ctx->bytes += size; + + if (used) { + free = 64 - used; + + if (size < free) { + ngx_memcpy(&ctx->buffer[used], data, size); + return; + } + + ngx_memcpy(&ctx->buffer[used], data, free); + data = (u_char *) data + free; + size -= free; + (void) ngx_md5_body(ctx, ctx->buffer, 64); + } + + if (size >= 64) { + data = ngx_md5_body(ctx, data, size & ~(size_t) 0x3f); + size &= 0x3f; + } + + ngx_memcpy(ctx->buffer, data, size); +} + + +void +ngx_md5_final(u_char result[16], ngx_md5_t *ctx) +{ + size_t used, free; + + used = (size_t) (ctx->bytes & 0x3f); + + ctx->buffer[used++] = 0x80; + + free = 64 - used; + + if (free < 8) { + ngx_memzero(&ctx->buffer[used], free); + (void) ngx_md5_body(ctx, ctx->buffer, 64); + used = 0; + free = 64; + } + + ngx_memzero(&ctx->buffer[used], free - 8); + + ctx->bytes <<= 3; + ctx->buffer[56] = (u_char) ctx->bytes; + ctx->buffer[57] = (u_char) (ctx->bytes >> 8); + ctx->buffer[58] = (u_char) (ctx->bytes >> 16); + ctx->buffer[59] = (u_char) (ctx->bytes >> 24); + ctx->buffer[60] = (u_char) (ctx->bytes >> 32); + ctx->buffer[61] = (u_char) (ctx->bytes >> 40); + ctx->buffer[62] = (u_char) (ctx->bytes >> 48); + ctx->buffer[63] = (u_char) (ctx->bytes >> 56); + + (void) ngx_md5_body(ctx, ctx->buffer, 64); + + result[0] = (u_char) ctx->a; + result[1] = (u_char) (ctx->a >> 8); + result[2] = (u_char) (ctx->a >> 16); + result[3] = (u_char) (ctx->a >> 24); + result[4] = (u_char) ctx->b; + result[5] = (u_char) (ctx->b >> 8); + result[6] = (u_char) (ctx->b >> 16); + result[7] = (u_char) (ctx->b >> 24); + result[8] = (u_char) ctx->c; + result[9] = (u_char) (ctx->c >> 8); + result[10] = (u_char) (ctx->c >> 16); + result[11] = (u_char) (ctx->c >> 24); + result[12] = (u_char) ctx->d; + result[13] = (u_char) (ctx->d >> 8); + result[14] = (u_char) (ctx->d >> 16); + result[15] = (u_char) (ctx->d >> 24); + + ngx_memzero(ctx, sizeof(*ctx)); +} + + +/* + * The basic MD5 functions. + * + * F and G are optimized compared to their RFC 1321 definitions for + * architectures that lack an AND-NOT instruction, just like in + * Colin Plumb's implementation. + */ + +#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) +#define G(x, y, z) ((y) ^ ((z) & ((x) ^ (y)))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | ~(z))) + +/* + * The MD5 transformation for all four rounds. + */ + +#define STEP(f, a, b, c, d, x, t, s) \ + (a) += f((b), (c), (d)) + (x) + (t); \ + (a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); \ + (a) += (b) + +/* + * SET() reads 4 input bytes in little-endian byte order and stores them + * in a properly aligned word in host byte order. + * + * The check for little-endian architectures that tolerate unaligned + * memory accesses is just an optimization. Nothing will break if it + * does not work. + */ + +#if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED) + +#define SET(n) (*(uint32_t *) &p[n * 4]) +#define GET(n) (*(uint32_t *) &p[n * 4]) + +#else + +#define SET(n) \ + (block[n] = \ + (uint32_t) p[n * 4] | \ + ((uint32_t) p[n * 4 + 1] << 8) | \ + ((uint32_t) p[n * 4 + 2] << 16) | \ + ((uint32_t) p[n * 4 + 3] << 24)) + +#define GET(n) block[n] + +#endif + + +/* + * This processes one or more 64-byte data blocks, but does not update + * the bit counters. There are no alignment requirements. + */ + +static const u_char * +ngx_md5_body(ngx_md5_t *ctx, const u_char *data, size_t size) +{ + uint32_t a, b, c, d; + uint32_t saved_a, saved_b, saved_c, saved_d; + const u_char *p; +#if !(NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED) + uint32_t block[16]; +#endif + + p = data; + + a = ctx->a; + b = ctx->b; + c = ctx->c; + d = ctx->d; + + do { + saved_a = a; + saved_b = b; + saved_c = c; + saved_d = d; + + /* Round 1 */ + + STEP(F, a, b, c, d, SET(0), 0xd76aa478, 7); + STEP(F, d, a, b, c, SET(1), 0xe8c7b756, 12); + STEP(F, c, d, a, b, SET(2), 0x242070db, 17); + STEP(F, b, c, d, a, SET(3), 0xc1bdceee, 22); + STEP(F, a, b, c, d, SET(4), 0xf57c0faf, 7); + STEP(F, d, a, b, c, SET(5), 0x4787c62a, 12); + STEP(F, c, d, a, b, SET(6), 0xa8304613, 17); + STEP(F, b, c, d, a, SET(7), 0xfd469501, 22); + STEP(F, a, b, c, d, SET(8), 0x698098d8, 7); + STEP(F, d, a, b, c, SET(9), 0x8b44f7af, 12); + STEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17); + STEP(F, b, c, d, a, SET(11), 0x895cd7be, 22); + STEP(F, a, b, c, d, SET(12), 0x6b901122, 7); + STEP(F, d, a, b, c, SET(13), 0xfd987193, 12); + STEP(F, c, d, a, b, SET(14), 0xa679438e, 17); + STEP(F, b, c, d, a, SET(15), 0x49b40821, 22); + + /* Round 2 */ + + STEP(G, a, b, c, d, GET(1), 0xf61e2562, 5); + STEP(G, d, a, b, c, GET(6), 0xc040b340, 9); + STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14); + STEP(G, b, c, d, a, GET(0), 0xe9b6c7aa, 20); + STEP(G, a, b, c, d, GET(5), 0xd62f105d, 5); + STEP(G, d, a, b, c, GET(10), 0x02441453, 9); + STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14); + STEP(G, b, c, d, a, GET(4), 0xe7d3fbc8, 20); + STEP(G, a, b, c, d, GET(9), 0x21e1cde6, 5); + STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9); + STEP(G, c, d, a, b, GET(3), 0xf4d50d87, 14); + STEP(G, b, c, d, a, GET(8), 0x455a14ed, 20); + STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5); + STEP(G, d, a, b, c, GET(2), 0xfcefa3f8, 9); + STEP(G, c, d, a, b, GET(7), 0x676f02d9, 14); + STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20); + + /* Round 3 */ + + STEP(H, a, b, c, d, GET(5), 0xfffa3942, 4); + STEP(H, d, a, b, c, GET(8), 0x8771f681, 11); + STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16); + STEP(H, b, c, d, a, GET(14), 0xfde5380c, 23); + STEP(H, a, b, c, d, GET(1), 0xa4beea44, 4); + STEP(H, d, a, b, c, GET(4), 0x4bdecfa9, 11); + STEP(H, c, d, a, b, GET(7), 0xf6bb4b60, 16); + STEP(H, b, c, d, a, GET(10), 0xbebfbc70, 23); + STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4); + STEP(H, d, a, b, c, GET(0), 0xeaa127fa, 11); + STEP(H, c, d, a, b, GET(3), 0xd4ef3085, 16); + STEP(H, b, c, d, a, GET(6), 0x04881d05, 23); + STEP(H, a, b, c, d, GET(9), 0xd9d4d039, 4); + STEP(H, d, a, b, c, GET(12), 0xe6db99e5, 11); + STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16); + STEP(H, b, c, d, a, GET(2), 0xc4ac5665, 23); + + /* Round 4 */ + + STEP(I, a, b, c, d, GET(0), 0xf4292244, 6); + STEP(I, d, a, b, c, GET(7), 0x432aff97, 10); + STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15); + STEP(I, b, c, d, a, GET(5), 0xfc93a039, 21); + STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6); + STEP(I, d, a, b, c, GET(3), 0x8f0ccc92, 10); + STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15); + STEP(I, b, c, d, a, GET(1), 0x85845dd1, 21); + STEP(I, a, b, c, d, GET(8), 0x6fa87e4f, 6); + STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10); + STEP(I, c, d, a, b, GET(6), 0xa3014314, 15); + STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21); + STEP(I, a, b, c, d, GET(4), 0xf7537e82, 6); + STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10); + STEP(I, c, d, a, b, GET(2), 0x2ad7d2bb, 15); + STEP(I, b, c, d, a, GET(9), 0xeb86d391, 21); + + a += saved_a; + b += saved_b; + c += saved_c; + d += saved_d; + + p += 64; + + } while (size -= 64); + + ctx->a = a; + ctx->b = b; + ctx->c = c; + ctx->d = d; + + return p; +} diff --git a/app/nginx/src/core/ngx_md5.h b/app/nginx/src/core/ngx_md5.h new file mode 100644 index 0000000..713b614 --- /dev/null +++ b/app/nginx/src/core/ngx_md5.h @@ -0,0 +1,28 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_MD5_H_INCLUDED_ +#define _NGX_MD5_H_INCLUDED_ + + +#include +#include + + +typedef struct { + uint64_t bytes; + uint32_t a, b, c, d; + u_char buffer[64]; +} ngx_md5_t; + + +void ngx_md5_init(ngx_md5_t *ctx); +void ngx_md5_update(ngx_md5_t *ctx, const void *data, size_t size); +void ngx_md5_final(u_char result[16], ngx_md5_t *ctx); + + +#endif /* _NGX_MD5_H_INCLUDED_ */ diff --git a/app/nginx/src/core/ngx_module.c b/app/nginx/src/core/ngx_module.c new file mode 100644 index 0000000..3e3c506 --- /dev/null +++ b/app/nginx/src/core/ngx_module.c @@ -0,0 +1,360 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Maxim Dounin + * Copyright (C) Nginx, Inc. + */ + + +#include +#include + + +#define NGX_MAX_DYNAMIC_MODULES 128 + + +static ngx_uint_t ngx_module_index(ngx_cycle_t *cycle); +static ngx_uint_t ngx_module_ctx_index(ngx_cycle_t *cycle, ngx_uint_t type, + ngx_uint_t index); + + +ngx_uint_t ngx_max_module; +static ngx_uint_t ngx_modules_n; + + +ngx_int_t +ngx_preinit_modules(void) +{ + ngx_uint_t i; + + for (i = 0; ngx_modules[i]; i++) { + ngx_modules[i]->index = i; + ngx_modules[i]->name = ngx_module_names[i]; + } + + ngx_modules_n = i; + ngx_max_module = ngx_modules_n + NGX_MAX_DYNAMIC_MODULES; + + return NGX_OK; +} + + +ngx_int_t +ngx_cycle_modules(ngx_cycle_t *cycle) +{ + /* + * create a list of modules to be used for this cycle, + * copy static modules to it + */ + + cycle->modules = ngx_pcalloc(cycle->pool, (ngx_max_module + 1) + * sizeof(ngx_module_t *)); + if (cycle->modules == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(cycle->modules, ngx_modules, + ngx_modules_n * sizeof(ngx_module_t *)); + + cycle->modules_n = ngx_modules_n; + + return NGX_OK; +} + + +ngx_int_t +ngx_init_modules(ngx_cycle_t *cycle) +{ + ngx_uint_t i; + + for (i = 0; cycle->modules[i]; i++) { + if (cycle->modules[i]->init_module) { + if (cycle->modules[i]->init_module(cycle) != NGX_OK) { + return NGX_ERROR; + } + } + } + + return NGX_OK; +} + + +ngx_int_t +ngx_count_modules(ngx_cycle_t *cycle, ngx_uint_t type) +{ + ngx_uint_t i, next, max; + ngx_module_t *module; + + next = 0; + max = 0; + + /* count appropriate modules, set up their indices */ + + for (i = 0; cycle->modules[i]; i++) { + module = cycle->modules[i]; + + if (module->type != type) { + continue; + } + + if (module->ctx_index != NGX_MODULE_UNSET_INDEX) { + + /* if ctx_index was assigned, preserve it */ + + if (module->ctx_index > max) { + max = module->ctx_index; + } + + if (module->ctx_index == next) { + next++; + } + + continue; + } + + /* search for some free index */ + + module->ctx_index = ngx_module_ctx_index(cycle, type, next); + + if (module->ctx_index > max) { + max = module->ctx_index; + } + + next = module->ctx_index + 1; + } + + /* + * make sure the number returned is big enough for previous + * cycle as well, else there will be problems if the number + * will be stored in a global variable (as it's used to be) + * and we'll have to roll back to the previous cycle + */ + + if (cycle->old_cycle && cycle->old_cycle->modules) { + + for (i = 0; cycle->old_cycle->modules[i]; i++) { + module = cycle->old_cycle->modules[i]; + + if (module->type != type) { + continue; + } + + if (module->ctx_index > max) { + max = module->ctx_index; + } + } + } + + /* prevent loading of additional modules */ + + cycle->modules_used = 1; + + return max + 1; +} + + +ngx_int_t +ngx_add_module(ngx_conf_t *cf, ngx_str_t *file, ngx_module_t *module, + char **order) +{ + void *rv; + ngx_uint_t i, m, before; + ngx_core_module_t *core_module; + + if (cf->cycle->modules_n >= ngx_max_module) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "too many modules loaded"); + return NGX_ERROR; + } + + if (module->version != nginx_version) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "module \"%V\" version %ui instead of %ui", + file, module->version, (ngx_uint_t) nginx_version); + return NGX_ERROR; + } + + if (ngx_strcmp(module->signature, NGX_MODULE_SIGNATURE) != 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "module \"%V\" is not binary compatible", + file); + return NGX_ERROR; + } + + for (m = 0; cf->cycle->modules[m]; m++) { + if (ngx_strcmp(cf->cycle->modules[m]->name, module->name) == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "module \"%s\" is already loaded", + module->name); + return NGX_ERROR; + } + } + + /* + * if the module wasn't previously loaded, assign an index + */ + + if (module->index == NGX_MODULE_UNSET_INDEX) { + module->index = ngx_module_index(cf->cycle); + + if (module->index >= ngx_max_module) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "too many modules loaded"); + return NGX_ERROR; + } + } + + /* + * put the module into the cycle->modules array + */ + + before = cf->cycle->modules_n; + + if (order) { + for (i = 0; order[i]; i++) { + if (ngx_strcmp(order[i], module->name) == 0) { + i++; + break; + } + } + + for ( /* void */ ; order[i]; i++) { + +#if 0 + ngx_log_debug2(NGX_LOG_DEBUG_CORE, cf->log, 0, + "module: %s before %s", + module->name, order[i]); +#endif + + for (m = 0; m < before; m++) { + if (ngx_strcmp(cf->cycle->modules[m]->name, order[i]) == 0) { + + ngx_log_debug3(NGX_LOG_DEBUG_CORE, cf->log, 0, + "module: %s before %s:%i", + module->name, order[i], m); + + before = m; + break; + } + } + } + } + + /* put the module before modules[before] */ + + if (before != cf->cycle->modules_n) { + ngx_memmove(&cf->cycle->modules[before + 1], + &cf->cycle->modules[before], + (cf->cycle->modules_n - before) * sizeof(ngx_module_t *)); + } + + cf->cycle->modules[before] = module; + cf->cycle->modules_n++; + + if (module->type == NGX_CORE_MODULE) { + + /* + * we are smart enough to initialize core modules; + * other modules are expected to be loaded before + * initialization - e.g., http modules must be loaded + * before http{} block + */ + + core_module = module->ctx; + + if (core_module->create_conf) { + rv = core_module->create_conf(cf->cycle); + if (rv == NULL) { + return NGX_ERROR; + } + + cf->cycle->conf_ctx[module->index] = rv; + } + } + + return NGX_OK; +} + + +static ngx_uint_t +ngx_module_index(ngx_cycle_t *cycle) +{ + ngx_uint_t i, index; + ngx_module_t *module; + + index = 0; + +again: + + /* find an unused index */ + + for (i = 0; cycle->modules[i]; i++) { + module = cycle->modules[i]; + + if (module->index == index) { + index++; + goto again; + } + } + + /* check previous cycle */ + + if (cycle->old_cycle && cycle->old_cycle->modules) { + + for (i = 0; cycle->old_cycle->modules[i]; i++) { + module = cycle->old_cycle->modules[i]; + + if (module->index == index) { + index++; + goto again; + } + } + } + + return index; +} + + +static ngx_uint_t +ngx_module_ctx_index(ngx_cycle_t *cycle, ngx_uint_t type, ngx_uint_t index) +{ + ngx_uint_t i; + ngx_module_t *module; + +again: + + /* find an unused ctx_index */ + + for (i = 0; cycle->modules[i]; i++) { + module = cycle->modules[i]; + + if (module->type != type) { + continue; + } + + if (module->ctx_index == index) { + index++; + goto again; + } + } + + /* check previous cycle */ + + if (cycle->old_cycle && cycle->old_cycle->modules) { + + for (i = 0; cycle->old_cycle->modules[i]; i++) { + module = cycle->old_cycle->modules[i]; + + if (module->type != type) { + continue; + } + + if (module->ctx_index == index) { + index++; + goto again; + } + } + } + + return index; +} diff --git a/app/nginx/src/core/ngx_module.h b/app/nginx/src/core/ngx_module.h new file mode 100644 index 0000000..8cf3210 --- /dev/null +++ b/app/nginx/src/core/ngx_module.h @@ -0,0 +1,283 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Maxim Dounin + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_MODULE_H_INCLUDED_ +#define _NGX_MODULE_H_INCLUDED_ + + +#include +#include +#include + + +#define NGX_MODULE_UNSET_INDEX (ngx_uint_t) -1 + + +#define NGX_MODULE_SIGNATURE_0 \ + ngx_value(NGX_PTR_SIZE) "," \ + ngx_value(NGX_SIG_ATOMIC_T_SIZE) "," \ + ngx_value(NGX_TIME_T_SIZE) "," + +#if (NGX_HAVE_KQUEUE) +#define NGX_MODULE_SIGNATURE_1 "1" +#else +#define NGX_MODULE_SIGNATURE_1 "0" +#endif + +#if (NGX_HAVE_IOCP) +#define NGX_MODULE_SIGNATURE_2 "1" +#else +#define NGX_MODULE_SIGNATURE_2 "0" +#endif + +#if (NGX_HAVE_FILE_AIO || NGX_COMPAT) +#define NGX_MODULE_SIGNATURE_3 "1" +#else +#define NGX_MODULE_SIGNATURE_3 "0" +#endif + +#if (NGX_HAVE_AIO_SENDFILE || NGX_COMPAT) +#define NGX_MODULE_SIGNATURE_4 "1" +#else +#define NGX_MODULE_SIGNATURE_4 "0" +#endif + +#if (NGX_HAVE_EVENTFD) +#define NGX_MODULE_SIGNATURE_5 "1" +#else +#define NGX_MODULE_SIGNATURE_5 "0" +#endif + +#if (NGX_HAVE_EPOLL) +#define NGX_MODULE_SIGNATURE_6 "1" +#else +#define NGX_MODULE_SIGNATURE_6 "0" +#endif + +#if (NGX_HAVE_KEEPALIVE_TUNABLE) +#define NGX_MODULE_SIGNATURE_7 "1" +#else +#define NGX_MODULE_SIGNATURE_7 "0" +#endif + +#if (NGX_HAVE_INET6) +#define NGX_MODULE_SIGNATURE_8 "1" +#else +#define NGX_MODULE_SIGNATURE_8 "0" +#endif + +#define NGX_MODULE_SIGNATURE_9 "1" +#define NGX_MODULE_SIGNATURE_10 "1" + +#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER) +#define NGX_MODULE_SIGNATURE_11 "1" +#else +#define NGX_MODULE_SIGNATURE_11 "0" +#endif + +#define NGX_MODULE_SIGNATURE_12 "1" + +#if (NGX_HAVE_SETFIB) +#define NGX_MODULE_SIGNATURE_13 "1" +#else +#define NGX_MODULE_SIGNATURE_13 "0" +#endif + +#if (NGX_HAVE_TCP_FASTOPEN) +#define NGX_MODULE_SIGNATURE_14 "1" +#else +#define NGX_MODULE_SIGNATURE_14 "0" +#endif + +#if (NGX_HAVE_UNIX_DOMAIN) +#define NGX_MODULE_SIGNATURE_15 "1" +#else +#define NGX_MODULE_SIGNATURE_15 "0" +#endif + +#if (NGX_HAVE_VARIADIC_MACROS) +#define NGX_MODULE_SIGNATURE_16 "1" +#else +#define NGX_MODULE_SIGNATURE_16 "0" +#endif + +#define NGX_MODULE_SIGNATURE_17 "0" +#define NGX_MODULE_SIGNATURE_18 "0" + +#if (NGX_HAVE_OPENAT) +#define NGX_MODULE_SIGNATURE_19 "1" +#else +#define NGX_MODULE_SIGNATURE_19 "0" +#endif + +#if (NGX_HAVE_ATOMIC_OPS) +#define NGX_MODULE_SIGNATURE_20 "1" +#else +#define NGX_MODULE_SIGNATURE_20 "0" +#endif + +#if (NGX_HAVE_POSIX_SEM) +#define NGX_MODULE_SIGNATURE_21 "1" +#else +#define NGX_MODULE_SIGNATURE_21 "0" +#endif + +#if (NGX_THREADS || NGX_COMPAT) +#define NGX_MODULE_SIGNATURE_22 "1" +#else +#define NGX_MODULE_SIGNATURE_22 "0" +#endif + +#if (NGX_PCRE) +#define NGX_MODULE_SIGNATURE_23 "1" +#else +#define NGX_MODULE_SIGNATURE_23 "0" +#endif + +#if (NGX_HTTP_SSL || NGX_COMPAT) +#define NGX_MODULE_SIGNATURE_24 "1" +#else +#define NGX_MODULE_SIGNATURE_24 "0" +#endif + +#define NGX_MODULE_SIGNATURE_25 "1" + +#if (NGX_HTTP_GZIP) +#define NGX_MODULE_SIGNATURE_26 "1" +#else +#define NGX_MODULE_SIGNATURE_26 "0" +#endif + +#define NGX_MODULE_SIGNATURE_27 "1" + +#if (NGX_HTTP_X_FORWARDED_FOR) +#define NGX_MODULE_SIGNATURE_28 "1" +#else +#define NGX_MODULE_SIGNATURE_28 "0" +#endif + +#if (NGX_HTTP_REALIP) +#define NGX_MODULE_SIGNATURE_29 "1" +#else +#define NGX_MODULE_SIGNATURE_29 "0" +#endif + +#if (NGX_HTTP_HEADERS) +#define NGX_MODULE_SIGNATURE_30 "1" +#else +#define NGX_MODULE_SIGNATURE_30 "0" +#endif + +#if (NGX_HTTP_DAV) +#define NGX_MODULE_SIGNATURE_31 "1" +#else +#define NGX_MODULE_SIGNATURE_31 "0" +#endif + +#if (NGX_HTTP_CACHE) +#define NGX_MODULE_SIGNATURE_32 "1" +#else +#define NGX_MODULE_SIGNATURE_32 "0" +#endif + +#if (NGX_HTTP_UPSTREAM_ZONE) +#define NGX_MODULE_SIGNATURE_33 "1" +#else +#define NGX_MODULE_SIGNATURE_33 "0" +#endif + +#if (NGX_COMPAT) +#define NGX_MODULE_SIGNATURE_34 "1" +#else +#define NGX_MODULE_SIGNATURE_34 "0" +#endif + +#define NGX_MODULE_SIGNATURE \ + NGX_MODULE_SIGNATURE_0 NGX_MODULE_SIGNATURE_1 NGX_MODULE_SIGNATURE_2 \ + NGX_MODULE_SIGNATURE_3 NGX_MODULE_SIGNATURE_4 NGX_MODULE_SIGNATURE_5 \ + NGX_MODULE_SIGNATURE_6 NGX_MODULE_SIGNATURE_7 NGX_MODULE_SIGNATURE_8 \ + NGX_MODULE_SIGNATURE_9 NGX_MODULE_SIGNATURE_10 NGX_MODULE_SIGNATURE_11 \ + NGX_MODULE_SIGNATURE_12 NGX_MODULE_SIGNATURE_13 NGX_MODULE_SIGNATURE_14 \ + NGX_MODULE_SIGNATURE_15 NGX_MODULE_SIGNATURE_16 NGX_MODULE_SIGNATURE_17 \ + NGX_MODULE_SIGNATURE_18 NGX_MODULE_SIGNATURE_19 NGX_MODULE_SIGNATURE_20 \ + NGX_MODULE_SIGNATURE_21 NGX_MODULE_SIGNATURE_22 NGX_MODULE_SIGNATURE_23 \ + NGX_MODULE_SIGNATURE_24 NGX_MODULE_SIGNATURE_25 NGX_MODULE_SIGNATURE_26 \ + NGX_MODULE_SIGNATURE_27 NGX_MODULE_SIGNATURE_28 NGX_MODULE_SIGNATURE_29 \ + NGX_MODULE_SIGNATURE_30 NGX_MODULE_SIGNATURE_31 NGX_MODULE_SIGNATURE_32 \ + NGX_MODULE_SIGNATURE_33 NGX_MODULE_SIGNATURE_34 + + +#define NGX_MODULE_V1 \ + NGX_MODULE_UNSET_INDEX, NGX_MODULE_UNSET_INDEX, \ + NULL, 0, 0, nginx_version, NGX_MODULE_SIGNATURE + +#define NGX_MODULE_V1_PADDING 0, 0, 0, 0, 0, 0, 0, 0 + + +struct ngx_module_s { + ngx_uint_t ctx_index; + ngx_uint_t index; + + char *name; + + ngx_uint_t spare0; + ngx_uint_t spare1; + + ngx_uint_t version; + const char *signature; + + void *ctx; + ngx_command_t *commands; + ngx_uint_t type; + + ngx_int_t (*init_master)(ngx_log_t *log); + + ngx_int_t (*init_module)(ngx_cycle_t *cycle); + + ngx_int_t (*init_process)(ngx_cycle_t *cycle); + ngx_int_t (*init_thread)(ngx_cycle_t *cycle); + void (*exit_thread)(ngx_cycle_t *cycle); + void (*exit_process)(ngx_cycle_t *cycle); + + void (*exit_master)(ngx_cycle_t *cycle); + + uintptr_t spare_hook0; + uintptr_t spare_hook1; + uintptr_t spare_hook2; + uintptr_t spare_hook3; + uintptr_t spare_hook4; + uintptr_t spare_hook5; + uintptr_t spare_hook6; + uintptr_t spare_hook7; +}; + + +typedef struct { + ngx_str_t name; + void *(*create_conf)(ngx_cycle_t *cycle); + char *(*init_conf)(ngx_cycle_t *cycle, void *conf); +} ngx_core_module_t; + + +ngx_int_t ngx_preinit_modules(void); +ngx_int_t ngx_cycle_modules(ngx_cycle_t *cycle); +ngx_int_t ngx_init_modules(ngx_cycle_t *cycle); +ngx_int_t ngx_count_modules(ngx_cycle_t *cycle, ngx_uint_t type); + + +ngx_int_t ngx_add_module(ngx_conf_t *cf, ngx_str_t *file, + ngx_module_t *module, char **order); + + +extern ngx_module_t *ngx_modules[]; +extern ngx_uint_t ngx_max_module; + +extern char *ngx_module_names[]; + + +#endif /* _NGX_MODULE_H_INCLUDED_ */ diff --git a/app/nginx/src/core/ngx_murmurhash.c b/app/nginx/src/core/ngx_murmurhash.c new file mode 100644 index 0000000..c31e0e0 --- /dev/null +++ b/app/nginx/src/core/ngx_murmurhash.c @@ -0,0 +1,50 @@ + +/* + * Copyright (C) Austin Appleby + */ + + +#include +#include + + +uint32_t +ngx_murmur_hash2(u_char *data, size_t len) +{ + uint32_t h, k; + + h = 0 ^ len; + + while (len >= 4) { + k = data[0]; + k |= data[1] << 8; + k |= data[2] << 16; + k |= data[3] << 24; + + k *= 0x5bd1e995; + k ^= k >> 24; + k *= 0x5bd1e995; + + h *= 0x5bd1e995; + h ^= k; + + data += 4; + len -= 4; + } + + switch (len) { + case 3: + h ^= data[2] << 16; + case 2: + h ^= data[1] << 8; + case 1: + h ^= data[0]; + h *= 0x5bd1e995; + } + + h ^= h >> 13; + h *= 0x5bd1e995; + h ^= h >> 15; + + return h; +} diff --git a/app/nginx/src/core/ngx_murmurhash.h b/app/nginx/src/core/ngx_murmurhash.h new file mode 100644 index 0000000..54e867d --- /dev/null +++ b/app/nginx/src/core/ngx_murmurhash.h @@ -0,0 +1,19 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_MURMURHASH_H_INCLUDED_ +#define _NGX_MURMURHASH_H_INCLUDED_ + + +#include +#include + + +uint32_t ngx_murmur_hash2(u_char *data, size_t len); + + +#endif /* _NGX_MURMURHASH_H_INCLUDED_ */ diff --git a/app/nginx/src/core/ngx_open_file_cache.c b/app/nginx/src/core/ngx_open_file_cache.c new file mode 100644 index 0000000..b23ee78 --- /dev/null +++ b/app/nginx/src/core/ngx_open_file_cache.c @@ -0,0 +1,1253 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +/* + * open file cache caches + * open file handles with stat() info; + * directories stat() info; + * files and directories errors: not found, access denied, etc. + */ + + +#define NGX_MIN_READ_AHEAD (128 * 1024) + + +static void ngx_open_file_cache_cleanup(void *data); +#if (NGX_HAVE_OPENAT) +static ngx_fd_t ngx_openat_file_owner(ngx_fd_t at_fd, const u_char *name, + ngx_int_t mode, ngx_int_t create, ngx_int_t access, ngx_log_t *log); +#if (NGX_HAVE_O_PATH) +static ngx_int_t ngx_file_o_path_info(ngx_fd_t fd, ngx_file_info_t *fi, + ngx_log_t *log); +#endif +#endif +static ngx_fd_t ngx_open_file_wrapper(ngx_str_t *name, + ngx_open_file_info_t *of, ngx_int_t mode, ngx_int_t create, + ngx_int_t access, ngx_log_t *log); +static ngx_int_t ngx_file_info_wrapper(ngx_str_t *name, + ngx_open_file_info_t *of, ngx_file_info_t *fi, ngx_log_t *log); +static ngx_int_t ngx_open_and_stat_file(ngx_str_t *name, + ngx_open_file_info_t *of, ngx_log_t *log); +static void ngx_open_file_add_event(ngx_open_file_cache_t *cache, + ngx_cached_open_file_t *file, ngx_open_file_info_t *of, ngx_log_t *log); +static void ngx_open_file_cleanup(void *data); +static void ngx_close_cached_file(ngx_open_file_cache_t *cache, + ngx_cached_open_file_t *file, ngx_uint_t min_uses, ngx_log_t *log); +static void ngx_open_file_del_event(ngx_cached_open_file_t *file); +static void ngx_expire_old_cached_files(ngx_open_file_cache_t *cache, + ngx_uint_t n, ngx_log_t *log); +static void ngx_open_file_cache_rbtree_insert_value(ngx_rbtree_node_t *temp, + ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel); +static ngx_cached_open_file_t * + ngx_open_file_lookup(ngx_open_file_cache_t *cache, ngx_str_t *name, + uint32_t hash); +static void ngx_open_file_cache_remove(ngx_event_t *ev); + + +ngx_open_file_cache_t * +ngx_open_file_cache_init(ngx_pool_t *pool, ngx_uint_t max, time_t inactive) +{ + ngx_pool_cleanup_t *cln; + ngx_open_file_cache_t *cache; + + cache = ngx_palloc(pool, sizeof(ngx_open_file_cache_t)); + if (cache == NULL) { + return NULL; + } + + ngx_rbtree_init(&cache->rbtree, &cache->sentinel, + ngx_open_file_cache_rbtree_insert_value); + + ngx_queue_init(&cache->expire_queue); + + cache->current = 0; + cache->max = max; + cache->inactive = inactive; + + cln = ngx_pool_cleanup_add(pool, 0); + if (cln == NULL) { + return NULL; + } + + cln->handler = ngx_open_file_cache_cleanup; + cln->data = cache; + + return cache; +} + + +static void +ngx_open_file_cache_cleanup(void *data) +{ + ngx_open_file_cache_t *cache = data; + + ngx_queue_t *q; + ngx_cached_open_file_t *file; + + ngx_log_debug0(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0, + "open file cache cleanup"); + + for ( ;; ) { + + if (ngx_queue_empty(&cache->expire_queue)) { + break; + } + + q = ngx_queue_last(&cache->expire_queue); + + file = ngx_queue_data(q, ngx_cached_open_file_t, queue); + + ngx_queue_remove(q); + + ngx_rbtree_delete(&cache->rbtree, &file->node); + + cache->current--; + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0, + "delete cached open file: %s", file->name); + + if (!file->err && !file->is_dir) { + file->close = 1; + file->count = 0; + ngx_close_cached_file(cache, file, 0, ngx_cycle->log); + + } else { + ngx_free(file->name); + ngx_free(file); + } + } + + if (cache->current) { + ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, + "%ui items still left in open file cache", + cache->current); + } + + if (cache->rbtree.root != cache->rbtree.sentinel) { + ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, + "rbtree still is not empty in open file cache"); + + } +} + + +ngx_int_t +ngx_open_cached_file(ngx_open_file_cache_t *cache, ngx_str_t *name, + ngx_open_file_info_t *of, ngx_pool_t *pool) +{ + time_t now; + uint32_t hash; + ngx_int_t rc; + ngx_file_info_t fi; + ngx_pool_cleanup_t *cln; + ngx_cached_open_file_t *file; + ngx_pool_cleanup_file_t *clnf; + ngx_open_file_cache_cleanup_t *ofcln; + + of->fd = NGX_INVALID_FILE; + of->err = 0; + + if (cache == NULL) { + + if (of->test_only) { + + if (ngx_file_info_wrapper(name, of, &fi, pool->log) + == NGX_FILE_ERROR) + { + return NGX_ERROR; + } + + of->uniq = ngx_file_uniq(&fi); + of->mtime = ngx_file_mtime(&fi); + of->size = ngx_file_size(&fi); + of->fs_size = ngx_file_fs_size(&fi); + of->is_dir = ngx_is_dir(&fi); + of->is_file = ngx_is_file(&fi); + of->is_link = ngx_is_link(&fi); + of->is_exec = ngx_is_exec(&fi); + + return NGX_OK; + } + + cln = ngx_pool_cleanup_add(pool, sizeof(ngx_pool_cleanup_file_t)); + if (cln == NULL) { + return NGX_ERROR; + } + + rc = ngx_open_and_stat_file(name, of, pool->log); + + if (rc == NGX_OK && !of->is_dir) { + cln->handler = ngx_pool_cleanup_file; + clnf = cln->data; + + clnf->fd = of->fd; + clnf->name = name->data; + clnf->log = pool->log; + } + + return rc; + } + + cln = ngx_pool_cleanup_add(pool, sizeof(ngx_open_file_cache_cleanup_t)); + if (cln == NULL) { + return NGX_ERROR; + } + + now = ngx_time(); + + hash = ngx_crc32_long(name->data, name->len); + + file = ngx_open_file_lookup(cache, name, hash); + + if (file) { + + file->uses++; + + ngx_queue_remove(&file->queue); + + if (file->fd == NGX_INVALID_FILE && file->err == 0 && !file->is_dir) { + + /* file was not used often enough to keep open */ + + rc = ngx_open_and_stat_file(name, of, pool->log); + + if (rc != NGX_OK && (of->err == 0 || !of->errors)) { + goto failed; + } + + goto add_event; + } + + if (file->use_event + || (file->event == NULL + && (of->uniq == 0 || of->uniq == file->uniq) + && now - file->created < of->valid +#if (NGX_HAVE_OPENAT) + && of->disable_symlinks == file->disable_symlinks + && of->disable_symlinks_from == file->disable_symlinks_from +#endif + )) + { + if (file->err == 0) { + + of->fd = file->fd; + of->uniq = file->uniq; + of->mtime = file->mtime; + of->size = file->size; + + of->is_dir = file->is_dir; + of->is_file = file->is_file; + of->is_link = file->is_link; + of->is_exec = file->is_exec; + of->is_directio = file->is_directio; + + if (!file->is_dir) { + file->count++; + ngx_open_file_add_event(cache, file, of, pool->log); + } + + } else { + of->err = file->err; +#if (NGX_HAVE_OPENAT) + of->failed = file->disable_symlinks ? ngx_openat_file_n + : ngx_open_file_n; +#else + of->failed = ngx_open_file_n; +#endif + } + + goto found; + } + + ngx_log_debug4(NGX_LOG_DEBUG_CORE, pool->log, 0, + "retest open file: %s, fd:%d, c:%d, e:%d", + file->name, file->fd, file->count, file->err); + + if (file->is_dir) { + + /* + * chances that directory became file are very small + * so test_dir flag allows to use a single syscall + * in ngx_file_info() instead of three syscalls + */ + + of->test_dir = 1; + } + + of->fd = file->fd; + of->uniq = file->uniq; + + rc = ngx_open_and_stat_file(name, of, pool->log); + + if (rc != NGX_OK && (of->err == 0 || !of->errors)) { + goto failed; + } + + if (of->is_dir) { + + if (file->is_dir || file->err) { + goto update; + } + + /* file became directory */ + + } else if (of->err == 0) { /* file */ + + if (file->is_dir || file->err) { + goto add_event; + } + + if (of->uniq == file->uniq) { + + if (file->event) { + file->use_event = 1; + } + + of->is_directio = file->is_directio; + + goto update; + } + + /* file was changed */ + + } else { /* error to cache */ + + if (file->err || file->is_dir) { + goto update; + } + + /* file was removed, etc. */ + } + + if (file->count == 0) { + + ngx_open_file_del_event(file); + + if (ngx_close_file(file->fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno, + ngx_close_file_n " \"%V\" failed", name); + } + + goto add_event; + } + + ngx_rbtree_delete(&cache->rbtree, &file->node); + + cache->current--; + + file->close = 1; + + goto create; + } + + /* not found */ + + rc = ngx_open_and_stat_file(name, of, pool->log); + + if (rc != NGX_OK && (of->err == 0 || !of->errors)) { + goto failed; + } + +create: + + if (cache->current >= cache->max) { + ngx_expire_old_cached_files(cache, 0, pool->log); + } + + file = ngx_alloc(sizeof(ngx_cached_open_file_t), pool->log); + + if (file == NULL) { + goto failed; + } + + file->name = ngx_alloc(name->len + 1, pool->log); + + if (file->name == NULL) { + ngx_free(file); + file = NULL; + goto failed; + } + + ngx_cpystrn(file->name, name->data, name->len + 1); + + file->node.key = hash; + + ngx_rbtree_insert(&cache->rbtree, &file->node); + + cache->current++; + + file->uses = 1; + file->count = 0; + file->use_event = 0; + file->event = NULL; + +add_event: + + ngx_open_file_add_event(cache, file, of, pool->log); + +update: + + file->fd = of->fd; + file->err = of->err; +#if (NGX_HAVE_OPENAT) + file->disable_symlinks = of->disable_symlinks; + file->disable_symlinks_from = of->disable_symlinks_from; +#endif + + if (of->err == 0) { + file->uniq = of->uniq; + file->mtime = of->mtime; + file->size = of->size; + + file->close = 0; + + file->is_dir = of->is_dir; + file->is_file = of->is_file; + file->is_link = of->is_link; + file->is_exec = of->is_exec; + file->is_directio = of->is_directio; + + if (!of->is_dir) { + file->count++; + } + } + + file->created = now; + +found: + + file->accessed = now; + + ngx_queue_insert_head(&cache->expire_queue, &file->queue); + + ngx_log_debug5(NGX_LOG_DEBUG_CORE, pool->log, 0, + "cached open file: %s, fd:%d, c:%d, e:%d, u:%d", + file->name, file->fd, file->count, file->err, file->uses); + + if (of->err == 0) { + + if (!of->is_dir) { + cln->handler = ngx_open_file_cleanup; + ofcln = cln->data; + + ofcln->cache = cache; + ofcln->file = file; + ofcln->min_uses = of->min_uses; + ofcln->log = pool->log; + } + + return NGX_OK; + } + + return NGX_ERROR; + +failed: + + if (file) { + ngx_rbtree_delete(&cache->rbtree, &file->node); + + cache->current--; + + if (file->count == 0) { + + if (file->fd != NGX_INVALID_FILE) { + if (ngx_close_file(file->fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno, + ngx_close_file_n " \"%s\" failed", + file->name); + } + } + + ngx_free(file->name); + ngx_free(file); + + } else { + file->close = 1; + } + } + + if (of->fd != NGX_INVALID_FILE) { + if (ngx_close_file(of->fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno, + ngx_close_file_n " \"%V\" failed", name); + } + } + + return NGX_ERROR; +} + + +#if (NGX_HAVE_OPENAT) + +static ngx_fd_t +ngx_openat_file_owner(ngx_fd_t at_fd, const u_char *name, + ngx_int_t mode, ngx_int_t create, ngx_int_t access, ngx_log_t *log) +{ + ngx_fd_t fd; + ngx_err_t err; + ngx_file_info_t fi, atfi; + + /* + * To allow symlinks with the same owner, use openat() (followed + * by fstat()) and fstatat(AT_SYMLINK_NOFOLLOW), and then compare + * uids between fstat() and fstatat(). + * + * As there is a race between openat() and fstatat() we don't + * know if openat() in fact opened symlink or not. Therefore, + * we have to compare uids even if fstatat() reports the opened + * component isn't a symlink (as we don't know whether it was + * symlink during openat() or not). + */ + + fd = ngx_openat_file(at_fd, name, mode, create, access); + + if (fd == NGX_INVALID_FILE) { + return NGX_INVALID_FILE; + } + + if (ngx_file_at_info(at_fd, name, &atfi, AT_SYMLINK_NOFOLLOW) + == NGX_FILE_ERROR) + { + err = ngx_errno; + goto failed; + } + +#if (NGX_HAVE_O_PATH) + if (ngx_file_o_path_info(fd, &fi, log) == NGX_ERROR) { + err = ngx_errno; + goto failed; + } +#else + if (ngx_fd_info(fd, &fi) == NGX_FILE_ERROR) { + err = ngx_errno; + goto failed; + } +#endif + + if (fi.st_uid != atfi.st_uid) { + err = NGX_ELOOP; + goto failed; + } + + return fd; + +failed: + + if (ngx_close_file(fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + ngx_close_file_n " \"%s\" failed", name); + } + + ngx_set_errno(err); + + return NGX_INVALID_FILE; +} + + +#if (NGX_HAVE_O_PATH) + +static ngx_int_t +ngx_file_o_path_info(ngx_fd_t fd, ngx_file_info_t *fi, ngx_log_t *log) +{ + static ngx_uint_t use_fstat = 1; + + /* + * In Linux 2.6.39 the O_PATH flag was introduced that allows to obtain + * a descriptor without actually opening file or directory. It requires + * less permissions for path components, but till Linux 3.6 fstat() returns + * EBADF on such descriptors, and fstatat() with the AT_EMPTY_PATH flag + * should be used instead. + * + * Three scenarios are handled in this function: + * + * 1) The kernel is newer than 3.6 or fstat() with O_PATH support was + * backported by vendor. Then fstat() is used. + * + * 2) The kernel is newer than 2.6.39 but older than 3.6. In this case + * the first call of fstat() returns EBADF and we fallback to fstatat() + * with AT_EMPTY_PATH which was introduced at the same time as O_PATH. + * + * 3) The kernel is older than 2.6.39 but nginx was build with O_PATH + * support. Since descriptors are opened with O_PATH|O_RDONLY flags + * and O_PATH is ignored by the kernel then the O_RDONLY flag is + * actually used. In this case fstat() just works. + */ + + if (use_fstat) { + if (ngx_fd_info(fd, fi) != NGX_FILE_ERROR) { + return NGX_OK; + } + + if (ngx_errno != NGX_EBADF) { + return NGX_ERROR; + } + + ngx_log_error(NGX_LOG_NOTICE, log, 0, + "fstat(O_PATH) failed with EBADF, " + "switching to fstatat(AT_EMPTY_PATH)"); + + use_fstat = 0; + } + + if (ngx_file_at_info(fd, "", fi, AT_EMPTY_PATH) != NGX_FILE_ERROR) { + return NGX_OK; + } + + return NGX_ERROR; +} + +#endif + +#endif /* NGX_HAVE_OPENAT */ + + +static ngx_fd_t +ngx_open_file_wrapper(ngx_str_t *name, ngx_open_file_info_t *of, + ngx_int_t mode, ngx_int_t create, ngx_int_t access, ngx_log_t *log) +{ + ngx_fd_t fd; + +#if !(NGX_HAVE_OPENAT) + + fd = ngx_open_file(name->data, mode, create, access); + + if (fd == NGX_INVALID_FILE) { + of->err = ngx_errno; + of->failed = ngx_open_file_n; + return NGX_INVALID_FILE; + } + + return fd; + +#else + + u_char *p, *cp, *end; + ngx_fd_t at_fd; + ngx_str_t at_name; + + if (of->disable_symlinks == NGX_DISABLE_SYMLINKS_OFF) { + fd = ngx_open_file(name->data, mode, create, access); + + if (fd == NGX_INVALID_FILE) { + of->err = ngx_errno; + of->failed = ngx_open_file_n; + return NGX_INVALID_FILE; + } + + return fd; + } + + p = name->data; + end = p + name->len; + + at_name = *name; + + if (of->disable_symlinks_from) { + + cp = p + of->disable_symlinks_from; + + *cp = '\0'; + + at_fd = ngx_open_file(p, NGX_FILE_SEARCH|NGX_FILE_NONBLOCK, + NGX_FILE_OPEN, 0); + + *cp = '/'; + + if (at_fd == NGX_INVALID_FILE) { + of->err = ngx_errno; + of->failed = ngx_open_file_n; + return NGX_INVALID_FILE; + } + + at_name.len = of->disable_symlinks_from; + p = cp + 1; + + } else if (*p == '/') { + + at_fd = ngx_open_file("/", + NGX_FILE_SEARCH|NGX_FILE_NONBLOCK, + NGX_FILE_OPEN, 0); + + if (at_fd == NGX_INVALID_FILE) { + of->err = ngx_errno; + of->failed = ngx_openat_file_n; + return NGX_INVALID_FILE; + } + + at_name.len = 1; + p++; + + } else { + at_fd = NGX_AT_FDCWD; + } + + for ( ;; ) { + cp = ngx_strlchr(p, end, '/'); + if (cp == NULL) { + break; + } + + if (cp == p) { + p++; + continue; + } + + *cp = '\0'; + + if (of->disable_symlinks == NGX_DISABLE_SYMLINKS_NOTOWNER) { + fd = ngx_openat_file_owner(at_fd, p, + NGX_FILE_SEARCH|NGX_FILE_NONBLOCK, + NGX_FILE_OPEN, 0, log); + + } else { + fd = ngx_openat_file(at_fd, p, + NGX_FILE_SEARCH|NGX_FILE_NONBLOCK|NGX_FILE_NOFOLLOW, + NGX_FILE_OPEN, 0); + } + + *cp = '/'; + + if (fd == NGX_INVALID_FILE) { + of->err = ngx_errno; + of->failed = ngx_openat_file_n; + goto failed; + } + + if (at_fd != NGX_AT_FDCWD && ngx_close_file(at_fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + ngx_close_file_n " \"%V\" failed", &at_name); + } + + p = cp + 1; + at_fd = fd; + at_name.len = cp - at_name.data; + } + + if (p == end) { + + /* + * If pathname ends with a trailing slash, assume the last path + * component is a directory and reopen it with requested flags; + * if not, fail with ENOTDIR as per POSIX. + * + * We cannot rely on O_DIRECTORY in the loop above to check + * that the last path component is a directory because + * O_DIRECTORY doesn't work on FreeBSD 8. Fortunately, by + * reopening a directory, we don't depend on it at all. + */ + + fd = ngx_openat_file(at_fd, ".", mode, create, access); + goto done; + } + + if (of->disable_symlinks == NGX_DISABLE_SYMLINKS_NOTOWNER + && !(create & (NGX_FILE_CREATE_OR_OPEN|NGX_FILE_TRUNCATE))) + { + fd = ngx_openat_file_owner(at_fd, p, mode, create, access, log); + + } else { + fd = ngx_openat_file(at_fd, p, mode|NGX_FILE_NOFOLLOW, create, access); + } + +done: + + if (fd == NGX_INVALID_FILE) { + of->err = ngx_errno; + of->failed = ngx_openat_file_n; + } + +failed: + + if (at_fd != NGX_AT_FDCWD && ngx_close_file(at_fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + ngx_close_file_n " \"%V\" failed", &at_name); + } + + return fd; +#endif +} + + +static ngx_int_t +ngx_file_info_wrapper(ngx_str_t *name, ngx_open_file_info_t *of, + ngx_file_info_t *fi, ngx_log_t *log) +{ + ngx_int_t rc; + +#if !(NGX_HAVE_OPENAT) + + rc = ngx_file_info(name->data, fi); + + if (rc == NGX_FILE_ERROR) { + of->err = ngx_errno; + of->failed = ngx_file_info_n; + return NGX_FILE_ERROR; + } + + return rc; + +#else + + ngx_fd_t fd; + + if (of->disable_symlinks == NGX_DISABLE_SYMLINKS_OFF) { + + rc = ngx_file_info(name->data, fi); + + if (rc == NGX_FILE_ERROR) { + of->err = ngx_errno; + of->failed = ngx_file_info_n; + return NGX_FILE_ERROR; + } + + return rc; + } + + fd = ngx_open_file_wrapper(name, of, NGX_FILE_RDONLY|NGX_FILE_NONBLOCK, + NGX_FILE_OPEN, 0, log); + + if (fd == NGX_INVALID_FILE) { + return NGX_FILE_ERROR; + } + + rc = ngx_fd_info(fd, fi); + + if (rc == NGX_FILE_ERROR) { + of->err = ngx_errno; + of->failed = ngx_fd_info_n; + } + + if (ngx_close_file(fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + ngx_close_file_n " \"%V\" failed", name); + } + + return rc; +#endif +} + + +static ngx_int_t +ngx_open_and_stat_file(ngx_str_t *name, ngx_open_file_info_t *of, + ngx_log_t *log) +{ + ngx_fd_t fd; + ngx_file_info_t fi; + + if (of->fd != NGX_INVALID_FILE) { + + if (ngx_file_info_wrapper(name, of, &fi, log) == NGX_FILE_ERROR) { + of->fd = NGX_INVALID_FILE; + return NGX_ERROR; + } + + if (of->uniq == ngx_file_uniq(&fi)) { + goto done; + } + + } else if (of->test_dir) { + + if (ngx_file_info_wrapper(name, of, &fi, log) == NGX_FILE_ERROR) { + of->fd = NGX_INVALID_FILE; + return NGX_ERROR; + } + + if (ngx_is_dir(&fi)) { + goto done; + } + } + + if (!of->log) { + + /* + * Use non-blocking open() not to hang on FIFO files, etc. + * This flag has no effect on a regular files. + */ + + fd = ngx_open_file_wrapper(name, of, NGX_FILE_RDONLY|NGX_FILE_NONBLOCK, + NGX_FILE_OPEN, 0, log); + + } else { + fd = ngx_open_file_wrapper(name, of, NGX_FILE_APPEND, + NGX_FILE_CREATE_OR_OPEN, + NGX_FILE_DEFAULT_ACCESS, log); + } + + if (fd == NGX_INVALID_FILE) { + of->fd = NGX_INVALID_FILE; + return NGX_ERROR; + } + + if (ngx_fd_info(fd, &fi) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_CRIT, log, ngx_errno, + ngx_fd_info_n " \"%V\" failed", name); + + if (ngx_close_file(fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + ngx_close_file_n " \"%V\" failed", name); + } + + of->fd = NGX_INVALID_FILE; + + return NGX_ERROR; + } + + if (ngx_is_dir(&fi)) { + if (ngx_close_file(fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + ngx_close_file_n " \"%V\" failed", name); + } + + of->fd = NGX_INVALID_FILE; + + } else { + of->fd = fd; + + if (of->read_ahead && ngx_file_size(&fi) > NGX_MIN_READ_AHEAD) { + if (ngx_read_ahead(fd, of->read_ahead) == NGX_ERROR) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + ngx_read_ahead_n " \"%V\" failed", name); + } + } + + if (of->directio <= ngx_file_size(&fi)) { + if (ngx_directio_on(fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + ngx_directio_on_n " \"%V\" failed", name); + + } else { + of->is_directio = 1; + } + } + } + +done: + + of->uniq = ngx_file_uniq(&fi); + of->mtime = ngx_file_mtime(&fi); + of->size = ngx_file_size(&fi); + of->fs_size = ngx_file_fs_size(&fi); + of->is_dir = ngx_is_dir(&fi); + of->is_file = ngx_is_file(&fi); + of->is_link = ngx_is_link(&fi); + of->is_exec = ngx_is_exec(&fi); + + return NGX_OK; +} + + +/* + * we ignore any possible event setting error and + * fallback to usual periodic file retests + */ + +static void +ngx_open_file_add_event(ngx_open_file_cache_t *cache, + ngx_cached_open_file_t *file, ngx_open_file_info_t *of, ngx_log_t *log) +{ + ngx_open_file_cache_event_t *fev; + + if (!(ngx_event_flags & NGX_USE_VNODE_EVENT) + || !of->events + || file->event + || of->fd == NGX_INVALID_FILE + || file->uses < of->min_uses) + { + return; + } + + file->use_event = 0; + + file->event = ngx_calloc(sizeof(ngx_event_t), log); + if (file->event== NULL) { + return; + } + + fev = ngx_alloc(sizeof(ngx_open_file_cache_event_t), log); + if (fev == NULL) { + ngx_free(file->event); + file->event = NULL; + return; + } + + fev->fd = of->fd; + fev->file = file; + fev->cache = cache; + + file->event->handler = ngx_open_file_cache_remove; + file->event->data = fev; + + /* + * although vnode event may be called while ngx_cycle->poll + * destruction, however, cleanup procedures are run before any + * memory freeing and events will be canceled. + */ + + file->event->log = ngx_cycle->log; + + if (ngx_add_event(file->event, NGX_VNODE_EVENT, NGX_ONESHOT_EVENT) + != NGX_OK) + { + ngx_free(file->event->data); + ngx_free(file->event); + file->event = NULL; + return; + } + + /* + * we do not set file->use_event here because there may be a race + * condition: a file may be deleted between opening the file and + * adding event, so we rely upon event notification only after + * one file revalidation on next file access + */ + + return; +} + + +static void +ngx_open_file_cleanup(void *data) +{ + ngx_open_file_cache_cleanup_t *c = data; + + c->file->count--; + + ngx_close_cached_file(c->cache, c->file, c->min_uses, c->log); + + /* drop one or two expired open files */ + ngx_expire_old_cached_files(c->cache, 1, c->log); +} + + +static void +ngx_close_cached_file(ngx_open_file_cache_t *cache, + ngx_cached_open_file_t *file, ngx_uint_t min_uses, ngx_log_t *log) +{ + ngx_log_debug5(NGX_LOG_DEBUG_CORE, log, 0, + "close cached open file: %s, fd:%d, c:%d, u:%d, %d", + file->name, file->fd, file->count, file->uses, file->close); + + if (!file->close) { + + file->accessed = ngx_time(); + + ngx_queue_remove(&file->queue); + + ngx_queue_insert_head(&cache->expire_queue, &file->queue); + + if (file->uses >= min_uses || file->count) { + return; + } + } + + ngx_open_file_del_event(file); + + if (file->count) { + return; + } + + if (file->fd != NGX_INVALID_FILE) { + + if (ngx_close_file(file->fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + ngx_close_file_n " \"%s\" failed", file->name); + } + + file->fd = NGX_INVALID_FILE; + } + + if (!file->close) { + return; + } + + ngx_free(file->name); + ngx_free(file); +} + + +static void +ngx_open_file_del_event(ngx_cached_open_file_t *file) +{ + if (file->event == NULL) { + return; + } + + (void) ngx_del_event(file->event, NGX_VNODE_EVENT, + file->count ? NGX_FLUSH_EVENT : NGX_CLOSE_EVENT); + + ngx_free(file->event->data); + ngx_free(file->event); + file->event = NULL; + file->use_event = 0; +} + + +static void +ngx_expire_old_cached_files(ngx_open_file_cache_t *cache, ngx_uint_t n, + ngx_log_t *log) +{ + time_t now; + ngx_queue_t *q; + ngx_cached_open_file_t *file; + + now = ngx_time(); + + /* + * n == 1 deletes one or two inactive files + * n == 0 deletes least recently used file by force + * and one or two inactive files + */ + + while (n < 3) { + + if (ngx_queue_empty(&cache->expire_queue)) { + return; + } + + q = ngx_queue_last(&cache->expire_queue); + + file = ngx_queue_data(q, ngx_cached_open_file_t, queue); + + if (n++ != 0 && now - file->accessed <= cache->inactive) { + return; + } + + ngx_queue_remove(q); + + ngx_rbtree_delete(&cache->rbtree, &file->node); + + cache->current--; + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, log, 0, + "expire cached open file: %s", file->name); + + if (!file->err && !file->is_dir) { + file->close = 1; + ngx_close_cached_file(cache, file, 0, log); + + } else { + ngx_free(file->name); + ngx_free(file); + } + } +} + + +static void +ngx_open_file_cache_rbtree_insert_value(ngx_rbtree_node_t *temp, + ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel) +{ + ngx_rbtree_node_t **p; + ngx_cached_open_file_t *file, *file_temp; + + for ( ;; ) { + + if (node->key < temp->key) { + + p = &temp->left; + + } else if (node->key > temp->key) { + + p = &temp->right; + + } else { /* node->key == temp->key */ + + file = (ngx_cached_open_file_t *) node; + file_temp = (ngx_cached_open_file_t *) temp; + + p = (ngx_strcmp(file->name, file_temp->name) < 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); +} + + +static ngx_cached_open_file_t * +ngx_open_file_lookup(ngx_open_file_cache_t *cache, ngx_str_t *name, + uint32_t hash) +{ + ngx_int_t rc; + ngx_rbtree_node_t *node, *sentinel; + ngx_cached_open_file_t *file; + + 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 */ + + file = (ngx_cached_open_file_t *) node; + + rc = ngx_strcmp(name->data, file->name); + + if (rc == 0) { + return file; + } + + node = (rc < 0) ? node->left : node->right; + } + + return NULL; +} + + +static void +ngx_open_file_cache_remove(ngx_event_t *ev) +{ + ngx_cached_open_file_t *file; + ngx_open_file_cache_event_t *fev; + + fev = ev->data; + file = fev->file; + + ngx_queue_remove(&file->queue); + + ngx_rbtree_delete(&fev->cache->rbtree, &file->node); + + fev->cache->current--; + + /* NGX_ONESHOT_EVENT was already deleted */ + file->event = NULL; + file->use_event = 0; + + file->close = 1; + + ngx_close_cached_file(fev->cache, file, 0, ev->log); + + /* free memory only when fev->cache and fev->file are already not needed */ + + ngx_free(ev->data); + ngx_free(ev); +} diff --git a/app/nginx/src/core/ngx_open_file_cache.h b/app/nginx/src/core/ngx_open_file_cache.h new file mode 100644 index 0000000..d119c12 --- /dev/null +++ b/app/nginx/src/core/ngx_open_file_cache.h @@ -0,0 +1,129 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include + + +#ifndef _NGX_OPEN_FILE_CACHE_H_INCLUDED_ +#define _NGX_OPEN_FILE_CACHE_H_INCLUDED_ + + +#define NGX_OPEN_FILE_DIRECTIO_OFF NGX_MAX_OFF_T_VALUE + + +typedef struct { + ngx_fd_t fd; + ngx_file_uniq_t uniq; + time_t mtime; + off_t size; + off_t fs_size; + off_t directio; + size_t read_ahead; + + ngx_err_t err; + char *failed; + + time_t valid; + + ngx_uint_t min_uses; + +#if (NGX_HAVE_OPENAT) + size_t disable_symlinks_from; + unsigned disable_symlinks:2; +#endif + + unsigned test_dir:1; + unsigned test_only:1; + unsigned log:1; + unsigned errors:1; + unsigned events:1; + + unsigned is_dir:1; + unsigned is_file:1; + unsigned is_link:1; + unsigned is_exec:1; + unsigned is_directio:1; +} ngx_open_file_info_t; + + +typedef struct ngx_cached_open_file_s ngx_cached_open_file_t; + +struct ngx_cached_open_file_s { + ngx_rbtree_node_t node; + ngx_queue_t queue; + + u_char *name; + time_t created; + time_t accessed; + + ngx_fd_t fd; + ngx_file_uniq_t uniq; + time_t mtime; + off_t size; + ngx_err_t err; + + uint32_t uses; + +#if (NGX_HAVE_OPENAT) + size_t disable_symlinks_from; + unsigned disable_symlinks:2; +#endif + + unsigned count:24; + unsigned close:1; + unsigned use_event:1; + + unsigned is_dir:1; + unsigned is_file:1; + unsigned is_link:1; + unsigned is_exec:1; + unsigned is_directio:1; + + ngx_event_t *event; +}; + + +typedef struct { + ngx_rbtree_t rbtree; + ngx_rbtree_node_t sentinel; + ngx_queue_t expire_queue; + + ngx_uint_t current; + ngx_uint_t max; + time_t inactive; +} ngx_open_file_cache_t; + + +typedef struct { + ngx_open_file_cache_t *cache; + ngx_cached_open_file_t *file; + ngx_uint_t min_uses; + ngx_log_t *log; +} ngx_open_file_cache_cleanup_t; + + +typedef struct { + + /* ngx_connection_t stub to allow use c->fd as event ident */ + void *data; + ngx_event_t *read; + ngx_event_t *write; + ngx_fd_t fd; + + ngx_cached_open_file_t *file; + ngx_open_file_cache_t *cache; +} ngx_open_file_cache_event_t; + + +ngx_open_file_cache_t *ngx_open_file_cache_init(ngx_pool_t *pool, + ngx_uint_t max, time_t inactive); +ngx_int_t ngx_open_cached_file(ngx_open_file_cache_t *cache, ngx_str_t *name, + ngx_open_file_info_t *of, ngx_pool_t *pool); + + +#endif /* _NGX_OPEN_FILE_CACHE_H_INCLUDED_ */ diff --git a/app/nginx/src/core/ngx_output_chain.c b/app/nginx/src/core/ngx_output_chain.c new file mode 100644 index 0000000..7f5dc78 --- /dev/null +++ b/app/nginx/src/core/ngx_output_chain.c @@ -0,0 +1,767 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +#if 0 +#define NGX_SENDFILE_LIMIT 4096 +#endif + +/* + * When DIRECTIO is enabled FreeBSD, Solaris, and MacOSX read directly + * to an application memory from a device if parameters are aligned + * to device sector boundary (512 bytes). They fallback to usual read + * operation if the parameters are not aligned. + * Linux allows DIRECTIO only if the parameters are aligned to a filesystem + * sector boundary, otherwise it returns EINVAL. The sector size is + * usually 512 bytes, however, on XFS it may be 4096 bytes. + */ + +#define NGX_NONE 1 + + +static ngx_inline ngx_int_t + ngx_output_chain_as_is(ngx_output_chain_ctx_t *ctx, ngx_buf_t *buf); +#if (NGX_HAVE_AIO_SENDFILE) +static ngx_int_t ngx_output_chain_aio_setup(ngx_output_chain_ctx_t *ctx, + ngx_file_t *file); +#endif +static ngx_int_t ngx_output_chain_add_copy(ngx_pool_t *pool, + ngx_chain_t **chain, ngx_chain_t *in); +static ngx_int_t ngx_output_chain_align_file_buf(ngx_output_chain_ctx_t *ctx, + off_t bsize); +static ngx_int_t ngx_output_chain_get_buf(ngx_output_chain_ctx_t *ctx, + off_t bsize); +static ngx_int_t ngx_output_chain_copy_buf(ngx_output_chain_ctx_t *ctx); + + +ngx_int_t +ngx_output_chain(ngx_output_chain_ctx_t *ctx, ngx_chain_t *in) +{ + off_t bsize; + ngx_int_t rc, last; + ngx_chain_t *cl, *out, **last_out; + + if (ctx->in == NULL && ctx->busy == NULL +#if (NGX_HAVE_FILE_AIO || NGX_THREADS) + && !ctx->aio +#endif + ) + { + /* + * the short path for the case when the ctx->in and ctx->busy chains + * are empty, the incoming chain is empty too or has the single buf + * that does not require the copy + */ + + if (in == NULL) { + return ctx->output_filter(ctx->filter_ctx, in); + } + + if (in->next == NULL +#if (NGX_SENDFILE_LIMIT) + && !(in->buf->in_file && in->buf->file_last > NGX_SENDFILE_LIMIT) +#endif + && ngx_output_chain_as_is(ctx, in->buf)) + { + return ctx->output_filter(ctx->filter_ctx, in); + } + } + + /* add the incoming buf to the chain ctx->in */ + + if (in) { + if (ngx_output_chain_add_copy(ctx->pool, &ctx->in, in) == NGX_ERROR) { + return NGX_ERROR; + } + } + + out = NULL; + last_out = &out; + last = NGX_NONE; + + for ( ;; ) { + +#if (NGX_HAVE_FILE_AIO || NGX_THREADS) + if (ctx->aio) { + return NGX_AGAIN; + } +#endif + + while (ctx->in) { + + /* + * cycle while there are the ctx->in bufs + * and there are the free output bufs to copy in + */ + + bsize = ngx_buf_size(ctx->in->buf); + + if (bsize == 0 && !ngx_buf_special(ctx->in->buf)) { + + ngx_log_error(NGX_LOG_ALERT, ctx->pool->log, 0, + "zero size buf in output " + "t:%d r:%d f:%d %p %p-%p %p %O-%O", + ctx->in->buf->temporary, + ctx->in->buf->recycled, + ctx->in->buf->in_file, + ctx->in->buf->start, + ctx->in->buf->pos, + ctx->in->buf->last, + ctx->in->buf->file, + ctx->in->buf->file_pos, + ctx->in->buf->file_last); + + ngx_debug_point(); + + ctx->in = ctx->in->next; + + continue; + } + + if (ngx_output_chain_as_is(ctx, ctx->in->buf)) { + + /* move the chain link to the output chain */ + + cl = ctx->in; + ctx->in = cl->next; + + *last_out = cl; + last_out = &cl->next; + cl->next = NULL; + + continue; + } + + if (ctx->buf == NULL) { + + rc = ngx_output_chain_align_file_buf(ctx, bsize); + + if (rc == NGX_ERROR) { + return NGX_ERROR; + } + + if (rc != NGX_OK) { + + if (ctx->free) { + + /* get the free buf */ + + cl = ctx->free; + ctx->buf = cl->buf; + ctx->free = cl->next; + + ngx_free_chain(ctx->pool, cl); + + } else if (out || ctx->allocated == ctx->bufs.num) { + + break; + + } else if (ngx_output_chain_get_buf(ctx, bsize) != NGX_OK) { + return NGX_ERROR; + } + } + } + + rc = ngx_output_chain_copy_buf(ctx); + + if (rc == NGX_ERROR) { + return rc; + } + + if (rc == NGX_AGAIN) { + if (out) { + break; + } + + return rc; + } + + /* delete the completed buf from the ctx->in chain */ + + if (ngx_buf_size(ctx->in->buf) == 0) { + ctx->in = ctx->in->next; + } + + cl = ngx_alloc_chain_link(ctx->pool); + if (cl == NULL) { + return NGX_ERROR; + } + + cl->buf = ctx->buf; + cl->next = NULL; + *last_out = cl; + last_out = &cl->next; + ctx->buf = NULL; + } + + if (out == NULL && last != NGX_NONE) { + + if (ctx->in) { + return NGX_AGAIN; + } + + return last; + } + + last = ctx->output_filter(ctx->filter_ctx, out); + + if (last == NGX_ERROR || last == NGX_DONE) { + return last; + } + + ngx_chain_update_chains(ctx->pool, &ctx->free, &ctx->busy, &out, + ctx->tag); + last_out = &out; + } +} + + +static ngx_inline ngx_int_t +ngx_output_chain_as_is(ngx_output_chain_ctx_t *ctx, ngx_buf_t *buf) +{ + ngx_uint_t sendfile; + + if (ngx_buf_special(buf)) { + return 1; + } + +#if (NGX_THREADS) + if (buf->in_file) { + buf->file->thread_handler = ctx->thread_handler; + buf->file->thread_ctx = ctx->filter_ctx; + } +#endif + + if (buf->in_file && buf->file->directio) { + return 0; + } + + sendfile = ctx->sendfile; + +#if (NGX_SENDFILE_LIMIT) + + if (buf->in_file && buf->file_pos >= NGX_SENDFILE_LIMIT) { + sendfile = 0; + } + +#endif + + if (!sendfile) { + + if (!ngx_buf_in_memory(buf)) { + return 0; + } + + buf->in_file = 0; + } + +#if (NGX_HAVE_AIO_SENDFILE) + if (ctx->aio_preload && buf->in_file) { + (void) ngx_output_chain_aio_setup(ctx, buf->file); + } +#endif + + if (ctx->need_in_memory && !ngx_buf_in_memory(buf)) { + return 0; + } + + if (ctx->need_in_temp && (buf->memory || buf->mmap)) { + return 0; + } + + return 1; +} + + +#if (NGX_HAVE_AIO_SENDFILE) + +static ngx_int_t +ngx_output_chain_aio_setup(ngx_output_chain_ctx_t *ctx, ngx_file_t *file) +{ + ngx_event_aio_t *aio; + + if (file->aio == NULL && ngx_file_aio_init(file, ctx->pool) != NGX_OK) { + return NGX_ERROR; + } + + aio = file->aio; + + aio->data = ctx->filter_ctx; + aio->preload_handler = ctx->aio_preload; + + return NGX_OK; +} + +#endif + + +static ngx_int_t +ngx_output_chain_add_copy(ngx_pool_t *pool, ngx_chain_t **chain, + ngx_chain_t *in) +{ + ngx_chain_t *cl, **ll; +#if (NGX_SENDFILE_LIMIT) + ngx_buf_t *b, *buf; +#endif + + ll = chain; + + for (cl = *chain; cl; cl = cl->next) { + ll = &cl->next; + } + + while (in) { + + cl = ngx_alloc_chain_link(pool); + if (cl == NULL) { + return NGX_ERROR; + } + +#if (NGX_SENDFILE_LIMIT) + + buf = in->buf; + + if (buf->in_file + && buf->file_pos < NGX_SENDFILE_LIMIT + && buf->file_last > NGX_SENDFILE_LIMIT) + { + /* split a file buf on two bufs by the sendfile limit */ + + b = ngx_calloc_buf(pool); + if (b == NULL) { + return NGX_ERROR; + } + + ngx_memcpy(b, buf, sizeof(ngx_buf_t)); + + if (ngx_buf_in_memory(buf)) { + buf->pos += (ssize_t) (NGX_SENDFILE_LIMIT - buf->file_pos); + b->last = buf->pos; + } + + buf->file_pos = NGX_SENDFILE_LIMIT; + b->file_last = NGX_SENDFILE_LIMIT; + + cl->buf = b; + + } else { + cl->buf = buf; + in = in->next; + } + +#else + cl->buf = in->buf; + in = in->next; + +#endif + + cl->next = NULL; + *ll = cl; + ll = &cl->next; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_output_chain_align_file_buf(ngx_output_chain_ctx_t *ctx, off_t bsize) +{ + size_t size; + ngx_buf_t *in; + + in = ctx->in->buf; + + if (in->file == NULL || !in->file->directio) { + return NGX_DECLINED; + } + + ctx->directio = 1; + + size = (size_t) (in->file_pos - (in->file_pos & ~(ctx->alignment - 1))); + + if (size == 0) { + + if (bsize >= (off_t) ctx->bufs.size) { + return NGX_DECLINED; + } + + size = (size_t) bsize; + + } else { + size = (size_t) ctx->alignment - size; + + if ((off_t) size > bsize) { + size = (size_t) bsize; + } + } + + ctx->buf = ngx_create_temp_buf(ctx->pool, size); + if (ctx->buf == NULL) { + return NGX_ERROR; + } + + /* + * we do not set ctx->buf->tag, because we do not want + * to reuse the buf via ctx->free list + */ + +#if (NGX_HAVE_ALIGNED_DIRECTIO) + ctx->unaligned = 1; +#endif + + return NGX_OK; +} + + +static ngx_int_t +ngx_output_chain_get_buf(ngx_output_chain_ctx_t *ctx, off_t bsize) +{ + size_t size; + ngx_buf_t *b, *in; + ngx_uint_t recycled; + + in = ctx->in->buf; + size = ctx->bufs.size; + recycled = 1; + + if (in->last_in_chain) { + + if (bsize < (off_t) size) { + + /* + * allocate a small temp buf for a small last buf + * or its small last part + */ + + size = (size_t) bsize; + recycled = 0; + + } else if (!ctx->directio + && ctx->bufs.num == 1 + && (bsize < (off_t) (size + size / 4))) + { + /* + * allocate a temp buf that equals to a last buf, + * if there is no directio, the last buf size is lesser + * than 1.25 of bufs.size and the temp buf is single + */ + + size = (size_t) bsize; + recycled = 0; + } + } + + b = ngx_calloc_buf(ctx->pool); + if (b == NULL) { + return NGX_ERROR; + } + + if (ctx->directio) { + + /* + * allocate block aligned to a disk sector size to enable + * userland buffer direct usage conjunctly with directio + */ + + b->start = ngx_pmemalign(ctx->pool, size, (size_t) ctx->alignment); + if (b->start == NULL) { + return NGX_ERROR; + } + + } else { + b->start = ngx_palloc(ctx->pool, size); + if (b->start == NULL) { + return NGX_ERROR; + } + } + + b->pos = b->start; + b->last = b->start; + b->end = b->last + size; + b->temporary = 1; + b->tag = ctx->tag; + b->recycled = recycled; + + ctx->buf = b; + ctx->allocated++; + + return NGX_OK; +} + + +static ngx_int_t +ngx_output_chain_copy_buf(ngx_output_chain_ctx_t *ctx) +{ + off_t size; + ssize_t n; + ngx_buf_t *src, *dst; + ngx_uint_t sendfile; + + src = ctx->in->buf; + dst = ctx->buf; + + size = ngx_buf_size(src); + size = ngx_min(size, dst->end - dst->pos); + + sendfile = ctx->sendfile && !ctx->directio; + +#if (NGX_SENDFILE_LIMIT) + + if (src->in_file && src->file_pos >= NGX_SENDFILE_LIMIT) { + sendfile = 0; + } + +#endif + + if (ngx_buf_in_memory(src)) { + ngx_memcpy(dst->pos, src->pos, (size_t) size); + src->pos += (size_t) size; + dst->last += (size_t) size; + + if (src->in_file) { + + if (sendfile) { + dst->in_file = 1; + dst->file = src->file; + dst->file_pos = src->file_pos; + dst->file_last = src->file_pos + size; + + } else { + dst->in_file = 0; + } + + src->file_pos += size; + + } else { + dst->in_file = 0; + } + + if (src->pos == src->last) { + dst->flush = src->flush; + dst->last_buf = src->last_buf; + dst->last_in_chain = src->last_in_chain; + } + + } else { + +#if (NGX_HAVE_ALIGNED_DIRECTIO) + + if (ctx->unaligned) { + if (ngx_directio_off(src->file->fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, ctx->pool->log, ngx_errno, + ngx_directio_off_n " \"%s\" failed", + src->file->name.data); + } + } + +#endif + +#if (NGX_HAVE_FILE_AIO) + if (ctx->aio_handler) { + n = ngx_file_aio_read(src->file, dst->pos, (size_t) size, + src->file_pos, ctx->pool); + if (n == NGX_AGAIN) { + ctx->aio_handler(ctx, src->file); + return NGX_AGAIN; + } + + } else +#endif +#if (NGX_THREADS) + if (ctx->thread_handler) { + src->file->thread_task = ctx->thread_task; + src->file->thread_handler = ctx->thread_handler; + src->file->thread_ctx = ctx->filter_ctx; + + n = ngx_thread_read(src->file, dst->pos, (size_t) size, + src->file_pos, ctx->pool); + if (n == NGX_AGAIN) { + ctx->thread_task = src->file->thread_task; + return NGX_AGAIN; + } + + } else +#endif + { + n = ngx_read_file(src->file, dst->pos, (size_t) size, + src->file_pos); + } + +#if (NGX_HAVE_ALIGNED_DIRECTIO) + + if (ctx->unaligned) { + ngx_err_t err; + + err = ngx_errno; + + if (ngx_directio_on(src->file->fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, ctx->pool->log, ngx_errno, + ngx_directio_on_n " \"%s\" failed", + src->file->name.data); + } + + ngx_set_errno(err); + + ctx->unaligned = 0; + } + +#endif + + if (n == NGX_ERROR) { + return (ngx_int_t) n; + } + + if (n != size) { + ngx_log_error(NGX_LOG_ALERT, ctx->pool->log, 0, + ngx_read_file_n " read only %z of %O from \"%s\"", + n, size, src->file->name.data); + return NGX_ERROR; + } + + dst->last += n; + + if (sendfile) { + dst->in_file = 1; + dst->file = src->file; + dst->file_pos = src->file_pos; + dst->file_last = src->file_pos + n; + + } else { + dst->in_file = 0; + } + + src->file_pos += n; + + if (src->file_pos == src->file_last) { + dst->flush = src->flush; + dst->last_buf = src->last_buf; + dst->last_in_chain = src->last_in_chain; + } + } + + return NGX_OK; +} + + +ngx_int_t +ngx_chain_writer(void *data, ngx_chain_t *in) +{ + ngx_chain_writer_ctx_t *ctx = data; + + off_t size; + ngx_chain_t *cl, *ln, *chain; + ngx_connection_t *c; + + c = ctx->connection; + + for (size = 0; in; in = in->next) { + +#if 1 + if (ngx_buf_size(in->buf) == 0 && !ngx_buf_special(in->buf)) { + + ngx_log_error(NGX_LOG_ALERT, ctx->pool->log, 0, + "zero size buf in chain writer " + "t:%d r:%d f:%d %p %p-%p %p %O-%O", + in->buf->temporary, + in->buf->recycled, + in->buf->in_file, + in->buf->start, + in->buf->pos, + in->buf->last, + in->buf->file, + in->buf->file_pos, + in->buf->file_last); + + ngx_debug_point(); + + continue; + } +#endif + + size += ngx_buf_size(in->buf); + + ngx_log_debug2(NGX_LOG_DEBUG_CORE, c->log, 0, + "chain writer buf fl:%d s:%uO", + in->buf->flush, ngx_buf_size(in->buf)); + + cl = ngx_alloc_chain_link(ctx->pool); + if (cl == NULL) { + return NGX_ERROR; + } + + cl->buf = in->buf; + cl->next = NULL; + *ctx->last = cl; + ctx->last = &cl->next; + } + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0, + "chain writer in: %p", ctx->out); + + for (cl = ctx->out; cl; cl = cl->next) { + +#if 1 + if (ngx_buf_size(cl->buf) == 0 && !ngx_buf_special(cl->buf)) { + + ngx_log_error(NGX_LOG_ALERT, ctx->pool->log, 0, + "zero size buf in chain writer " + "t:%d r:%d f:%d %p %p-%p %p %O-%O", + cl->buf->temporary, + cl->buf->recycled, + cl->buf->in_file, + cl->buf->start, + cl->buf->pos, + cl->buf->last, + cl->buf->file, + cl->buf->file_pos, + cl->buf->file_last); + + ngx_debug_point(); + + continue; + } +#endif + + size += ngx_buf_size(cl->buf); + } + + if (size == 0 && !c->buffered) { + return NGX_OK; + } + + chain = c->send_chain(c, ctx->out, ctx->limit); + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0, + "chain writer out: %p", chain); + + if (chain == NGX_CHAIN_ERROR) { + return NGX_ERROR; + } + + for (cl = ctx->out; cl && cl != chain; /* void */) { + ln = cl; + cl = cl->next; + ngx_free_chain(ctx->pool, ln); + } + + ctx->out = chain; + + if (ctx->out == NULL) { + ctx->last = &ctx->out; + + if (!c->buffered) { + return NGX_OK; + } + } + + return NGX_AGAIN; +} diff --git a/app/nginx/src/core/ngx_palloc.c b/app/nginx/src/core/ngx_palloc.c new file mode 100644 index 0000000..d3044ac --- /dev/null +++ b/app/nginx/src/core/ngx_palloc.c @@ -0,0 +1,430 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include + + +static ngx_inline void *ngx_palloc_small(ngx_pool_t *pool, size_t size, + ngx_uint_t align); +static void *ngx_palloc_block(ngx_pool_t *pool, size_t size); +static void *ngx_palloc_large(ngx_pool_t *pool, size_t size); + + +ngx_pool_t * +ngx_create_pool(size_t size, ngx_log_t *log) +{ + ngx_pool_t *p; + + p = ngx_memalign(NGX_POOL_ALIGNMENT, size, log); + if (p == NULL) { + return NULL; + } + + p->d.last = (u_char *) p + sizeof(ngx_pool_t); + p->d.end = (u_char *) p + size; + p->d.next = NULL; + p->d.failed = 0; + + size = size - sizeof(ngx_pool_t); + p->max = (size < NGX_MAX_ALLOC_FROM_POOL) ? size : NGX_MAX_ALLOC_FROM_POOL; + + p->current = p; + p->chain = NULL; + p->large = NULL; + p->cleanup = NULL; + p->log = log; + + return p; +} + + +void +ngx_destroy_pool(ngx_pool_t *pool) +{ + ngx_pool_t *p, *n; + ngx_pool_large_t *l; + ngx_pool_cleanup_t *c; + + for (c = pool->cleanup; c; c = c->next) { + if (c->handler) { + ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0, + "run cleanup: %p", c); + c->handler(c->data); + } + } + +#if (NGX_DEBUG) + + /* + * we could allocate the pool->log from this pool + * so we cannot use this log while free()ing the pool + */ + + for (l = pool->large; l; l = l->next) { + ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0, "free: %p", l->alloc); + } + + for (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next) { + ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, pool->log, 0, + "free: %p, unused: %uz", p, p->d.end - p->d.last); + + if (n == NULL) { + break; + } + } + +#endif + + for (l = pool->large; l; l = l->next) { + if (l->alloc) { + ngx_free(l->alloc); + } + } + + for (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next) { + ngx_free(p); + + if (n == NULL) { + break; + } + } +} + + +void +ngx_reset_pool(ngx_pool_t *pool) +{ + ngx_pool_t *p; + ngx_pool_large_t *l; + + for (l = pool->large; l; l = l->next) { + if (l->alloc) { + ngx_free(l->alloc); + } + } + + for (p = pool; p; p = p->d.next) { + p->d.last = (u_char *) p + sizeof(ngx_pool_t); + p->d.failed = 0; + } + + pool->current = pool; + pool->chain = NULL; + pool->large = NULL; +} + + +void * +ngx_palloc(ngx_pool_t *pool, size_t size) +{ +#if !(NGX_DEBUG_PALLOC) + if (size <= pool->max) { + return ngx_palloc_small(pool, size, 1); + } +#endif + + return ngx_palloc_large(pool, size); +} + + +void * +ngx_pnalloc(ngx_pool_t *pool, size_t size) +{ +#if !(NGX_DEBUG_PALLOC) + if (size <= pool->max) { + return ngx_palloc_small(pool, size, 0); + } +#endif + + return ngx_palloc_large(pool, size); +} + + +static ngx_inline void * +ngx_palloc_small(ngx_pool_t *pool, size_t size, ngx_uint_t align) +{ + u_char *m; + ngx_pool_t *p; + + p = pool->current; + + do { + m = p->d.last; + + if (align) { + m = ngx_align_ptr(m, NGX_ALIGNMENT); + } + + if ((size_t) (p->d.end - m) >= size) { + p->d.last = m + size; + + return m; + } + + p = p->d.next; + + } while (p); + + return ngx_palloc_block(pool, size); +} + + +static void * +ngx_palloc_block(ngx_pool_t *pool, size_t size) +{ + u_char *m; + size_t psize; + ngx_pool_t *p, *new; + + psize = (size_t) (pool->d.end - (u_char *) pool); + + m = ngx_memalign(NGX_POOL_ALIGNMENT, psize, pool->log); + if (m == NULL) { + return NULL; + } + + new = (ngx_pool_t *) m; + + new->d.end = m + psize; + new->d.next = NULL; + new->d.failed = 0; + + m += sizeof(ngx_pool_data_t); + m = ngx_align_ptr(m, NGX_ALIGNMENT); + new->d.last = m + size; + + for (p = pool->current; p->d.next; p = p->d.next) { + if (p->d.failed++ > 4) { + pool->current = p->d.next; + } + } + + p->d.next = new; + + return m; +} + + +static void * +ngx_palloc_large(ngx_pool_t *pool, size_t size) +{ + void *p; + ngx_uint_t n; + ngx_pool_large_t *large; + + p = ngx_alloc(size, pool->log); + if (p == NULL) { + return NULL; + } + + n = 0; + + for (large = pool->large; large; large = large->next) { + if (large->alloc == NULL) { + large->alloc = p; + return p; + } + + if (n++ > 3) { + break; + } + } + + large = ngx_palloc_small(pool, sizeof(ngx_pool_large_t), 1); + if (large == NULL) { + ngx_free(p); + return NULL; + } + + large->alloc = p; + large->next = pool->large; + pool->large = large; + + return p; +} + + +void * +ngx_pmemalign(ngx_pool_t *pool, size_t size, size_t alignment) +{ + void *p; + ngx_pool_large_t *large; + + p = ngx_memalign(alignment, size, pool->log); + if (p == NULL) { + return NULL; + } + + large = ngx_palloc_small(pool, sizeof(ngx_pool_large_t), 1); + if (large == NULL) { + ngx_free(p); + return NULL; + } + + large->alloc = p; + large->next = pool->large; + pool->large = large; + + return p; +} + + +ngx_int_t +ngx_pfree(ngx_pool_t *pool, void *p) +{ + ngx_pool_large_t *l; + + for (l = pool->large; l; l = l->next) { + if (p == l->alloc) { + ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0, + "free: %p", l->alloc); + ngx_free(l->alloc); + l->alloc = NULL; + + return NGX_OK; + } + } + + return NGX_DECLINED; +} + + +void * +ngx_pcalloc(ngx_pool_t *pool, size_t size) +{ + void *p; + + p = ngx_palloc(pool, size); + if (p) { + ngx_memzero(p, size); + } + + return p; +} + + +ngx_pool_cleanup_t * +ngx_pool_cleanup_add(ngx_pool_t *p, size_t size) +{ + ngx_pool_cleanup_t *c; + + c = ngx_palloc(p, sizeof(ngx_pool_cleanup_t)); + if (c == NULL) { + return NULL; + } + + if (size) { + c->data = ngx_palloc(p, size); + if (c->data == NULL) { + return NULL; + } + + } else { + c->data = NULL; + } + + c->handler = NULL; + c->next = p->cleanup; + + p->cleanup = c; + + ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, p->log, 0, "add cleanup: %p", c); + + return c; +} + + +void +ngx_pool_run_cleanup_file(ngx_pool_t *p, ngx_fd_t fd) +{ + ngx_pool_cleanup_t *c; + ngx_pool_cleanup_file_t *cf; + + for (c = p->cleanup; c; c = c->next) { + if (c->handler == ngx_pool_cleanup_file) { + + cf = c->data; + + if (cf->fd == fd) { + c->handler(cf); + c->handler = NULL; + return; + } + } + } +} + + +void +ngx_pool_cleanup_file(void *data) +{ + ngx_pool_cleanup_file_t *c = data; + + ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, c->log, 0, "file cleanup: fd:%d", + c->fd); + + if (ngx_close_file(c->fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno, + ngx_close_file_n " \"%s\" failed", c->name); + } +} + + +void +ngx_pool_delete_file(void *data) +{ + ngx_pool_cleanup_file_t *c = data; + + ngx_err_t err; + + ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, c->log, 0, "file cleanup: fd:%d %s", + c->fd, c->name); + + if (ngx_delete_file(c->name) == NGX_FILE_ERROR) { + err = ngx_errno; + + if (err != NGX_ENOENT) { + ngx_log_error(NGX_LOG_CRIT, c->log, err, + ngx_delete_file_n " \"%s\" failed", c->name); + } + } + + if (ngx_close_file(c->fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno, + ngx_close_file_n " \"%s\" failed", c->name); + } +} + + +#if 0 + +static void * +ngx_get_cached_block(size_t size) +{ + void *p; + ngx_cached_block_slot_t *slot; + + if (ngx_cycle->cache == NULL) { + return NULL; + } + + slot = &ngx_cycle->cache[(size + ngx_pagesize - 1) / ngx_pagesize]; + + slot->tries++; + + if (slot->number) { + p = slot->block; + slot->block = slot->block->next; + slot->number--; + return p; + } + + return NULL; +} + +#endif diff --git a/app/nginx/src/core/ngx_palloc.h b/app/nginx/src/core/ngx_palloc.h new file mode 100644 index 0000000..d652829 --- /dev/null +++ b/app/nginx/src/core/ngx_palloc.h @@ -0,0 +1,95 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_PALLOC_H_INCLUDED_ +#define _NGX_PALLOC_H_INCLUDED_ + + +#include +#include + + +/* + * NGX_MAX_ALLOC_FROM_POOL should be (ngx_pagesize - 1), i.e. 4095 on x86. + * On Windows NT it decreases a number of locked pages in a kernel. + */ +#define NGX_MAX_ALLOC_FROM_POOL (ngx_pagesize - 1) + +#define NGX_DEFAULT_POOL_SIZE (16 * 1024) + +#define NGX_POOL_ALIGNMENT 16 +#define NGX_MIN_POOL_SIZE \ + ngx_align((sizeof(ngx_pool_t) + 2 * sizeof(ngx_pool_large_t)), \ + NGX_POOL_ALIGNMENT) + + +typedef void (*ngx_pool_cleanup_pt)(void *data); + +typedef struct ngx_pool_cleanup_s ngx_pool_cleanup_t; + +struct ngx_pool_cleanup_s { + ngx_pool_cleanup_pt handler; + void *data; + ngx_pool_cleanup_t *next; +}; + + +typedef struct ngx_pool_large_s ngx_pool_large_t; + +struct ngx_pool_large_s { + ngx_pool_large_t *next; + void *alloc; +}; + + +typedef struct { + u_char *last; + u_char *end; + ngx_pool_t *next; + ngx_uint_t failed; +} ngx_pool_data_t; + + +struct ngx_pool_s { + ngx_pool_data_t d; + size_t max; + ngx_pool_t *current; + ngx_chain_t *chain; + ngx_pool_large_t *large; + ngx_pool_cleanup_t *cleanup; + ngx_log_t *log; +}; + + +typedef struct { + ngx_fd_t fd; + u_char *name; + ngx_log_t *log; +} ngx_pool_cleanup_file_t; + + +void *ngx_alloc(size_t size, ngx_log_t *log); +void *ngx_calloc(size_t size, ngx_log_t *log); + +ngx_pool_t *ngx_create_pool(size_t size, ngx_log_t *log); +void ngx_destroy_pool(ngx_pool_t *pool); +void ngx_reset_pool(ngx_pool_t *pool); + +void *ngx_palloc(ngx_pool_t *pool, size_t size); +void *ngx_pnalloc(ngx_pool_t *pool, size_t size); +void *ngx_pcalloc(ngx_pool_t *pool, size_t size); +void *ngx_pmemalign(ngx_pool_t *pool, size_t size, size_t alignment); +ngx_int_t ngx_pfree(ngx_pool_t *pool, void *p); + + +ngx_pool_cleanup_t *ngx_pool_cleanup_add(ngx_pool_t *p, size_t size); +void ngx_pool_run_cleanup_file(ngx_pool_t *p, ngx_fd_t fd); +void ngx_pool_cleanup_file(void *data); +void ngx_pool_delete_file(void *data); + + +#endif /* _NGX_PALLOC_H_INCLUDED_ */ diff --git a/app/nginx/src/core/ngx_parse.c b/app/nginx/src/core/ngx_parse.c new file mode 100644 index 0000000..d35e60f --- /dev/null +++ b/app/nginx/src/core/ngx_parse.c @@ -0,0 +1,283 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include + + +ssize_t +ngx_parse_size(ngx_str_t *line) +{ + u_char unit; + size_t len; + ssize_t size, scale, max; + + len = line->len; + + if (len == 0) { + return NGX_ERROR; + } + + unit = line->data[len - 1]; + + switch (unit) { + case 'K': + case 'k': + len--; + max = NGX_MAX_SIZE_T_VALUE / 1024; + scale = 1024; + break; + + case 'M': + case 'm': + len--; + max = NGX_MAX_SIZE_T_VALUE / (1024 * 1024); + scale = 1024 * 1024; + break; + + default: + max = NGX_MAX_SIZE_T_VALUE; + scale = 1; + } + + size = ngx_atosz(line->data, len); + if (size == NGX_ERROR || size > max) { + return NGX_ERROR; + } + + size *= scale; + + return size; +} + + +off_t +ngx_parse_offset(ngx_str_t *line) +{ + u_char unit; + off_t offset, scale, max; + size_t len; + + len = line->len; + + if (len == 0) { + return NGX_ERROR; + } + + unit = line->data[len - 1]; + + switch (unit) { + case 'K': + case 'k': + len--; + max = NGX_MAX_OFF_T_VALUE / 1024; + scale = 1024; + break; + + case 'M': + case 'm': + len--; + max = NGX_MAX_OFF_T_VALUE / (1024 * 1024); + scale = 1024 * 1024; + break; + + case 'G': + case 'g': + len--; + max = NGX_MAX_OFF_T_VALUE / (1024 * 1024 * 1024); + scale = 1024 * 1024 * 1024; + break; + + default: + max = NGX_MAX_OFF_T_VALUE; + scale = 1; + } + + offset = ngx_atoof(line->data, len); + if (offset == NGX_ERROR || offset > max) { + return NGX_ERROR; + } + + offset *= scale; + + return offset; +} + + +ngx_int_t +ngx_parse_time(ngx_str_t *line, ngx_uint_t is_sec) +{ + u_char *p, *last; + ngx_int_t value, total, scale; + ngx_int_t max, cutoff, cutlim; + ngx_uint_t valid; + enum { + st_start = 0, + st_year, + st_month, + st_week, + st_day, + st_hour, + st_min, + st_sec, + st_msec, + st_last + } step; + + valid = 0; + value = 0; + total = 0; + cutoff = NGX_MAX_INT_T_VALUE / 10; + cutlim = NGX_MAX_INT_T_VALUE % 10; + step = is_sec ? st_start : st_month; + + p = line->data; + last = p + line->len; + + while (p < last) { + + if (*p >= '0' && *p <= '9') { + if (value >= cutoff && (value > cutoff || *p - '0' > cutlim)) { + return NGX_ERROR; + } + + value = value * 10 + (*p++ - '0'); + valid = 1; + continue; + } + + switch (*p++) { + + case 'y': + if (step > st_start) { + return NGX_ERROR; + } + step = st_year; + max = NGX_MAX_INT_T_VALUE / (60 * 60 * 24 * 365); + scale = 60 * 60 * 24 * 365; + break; + + case 'M': + if (step >= st_month) { + return NGX_ERROR; + } + step = st_month; + max = NGX_MAX_INT_T_VALUE / (60 * 60 * 24 * 30); + scale = 60 * 60 * 24 * 30; + break; + + case 'w': + if (step >= st_week) { + return NGX_ERROR; + } + step = st_week; + max = NGX_MAX_INT_T_VALUE / (60 * 60 * 24 * 7); + scale = 60 * 60 * 24 * 7; + break; + + case 'd': + if (step >= st_day) { + return NGX_ERROR; + } + step = st_day; + max = NGX_MAX_INT_T_VALUE / (60 * 60 * 24); + scale = 60 * 60 * 24; + break; + + case 'h': + if (step >= st_hour) { + return NGX_ERROR; + } + step = st_hour; + max = NGX_MAX_INT_T_VALUE / (60 * 60); + scale = 60 * 60; + break; + + case 'm': + if (p < last && *p == 's') { + if (is_sec || step >= st_msec) { + return NGX_ERROR; + } + p++; + step = st_msec; + max = NGX_MAX_INT_T_VALUE; + scale = 1; + break; + } + + if (step >= st_min) { + return NGX_ERROR; + } + step = st_min; + max = NGX_MAX_INT_T_VALUE / 60; + scale = 60; + break; + + case 's': + if (step >= st_sec) { + return NGX_ERROR; + } + step = st_sec; + max = NGX_MAX_INT_T_VALUE; + scale = 1; + break; + + case ' ': + if (step >= st_sec) { + return NGX_ERROR; + } + step = st_last; + max = NGX_MAX_INT_T_VALUE; + scale = 1; + break; + + default: + return NGX_ERROR; + } + + if (step != st_msec && !is_sec) { + scale *= 1000; + max /= 1000; + } + + if (value > max) { + return NGX_ERROR; + } + + value *= scale; + + if (total > NGX_MAX_INT_T_VALUE - value) { + return NGX_ERROR; + } + + total += value; + + value = 0; + + while (p < last && *p == ' ') { + p++; + } + } + + if (!valid) { + return NGX_ERROR; + } + + if (!is_sec) { + if (value > NGX_MAX_INT_T_VALUE / 1000) { + return NGX_ERROR; + } + + value *= 1000; + } + + if (total > NGX_MAX_INT_T_VALUE - value) { + return NGX_ERROR; + } + + return total + value; +} diff --git a/app/nginx/src/core/ngx_parse.h b/app/nginx/src/core/ngx_parse.h new file mode 100644 index 0000000..ec093b5 --- /dev/null +++ b/app/nginx/src/core/ngx_parse.h @@ -0,0 +1,21 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_PARSE_H_INCLUDED_ +#define _NGX_PARSE_H_INCLUDED_ + + +#include +#include + + +ssize_t ngx_parse_size(ngx_str_t *line); +off_t ngx_parse_offset(ngx_str_t *line); +ngx_int_t ngx_parse_time(ngx_str_t *line, ngx_uint_t is_sec); + + +#endif /* _NGX_PARSE_H_INCLUDED_ */ diff --git a/app/nginx/src/core/ngx_parse_time.c b/app/nginx/src/core/ngx_parse_time.c new file mode 100644 index 0000000..13afde3 --- /dev/null +++ b/app/nginx/src/core/ngx_parse_time.c @@ -0,0 +1,276 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include + + +static ngx_uint_t mday[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + +time_t +ngx_parse_http_time(u_char *value, size_t len) +{ + u_char *p, *end; + ngx_int_t month; + ngx_uint_t day, year, hour, min, sec; + uint64_t time; + enum { + no = 0, + rfc822, /* Tue, 10 Nov 2002 23:50:13 */ + rfc850, /* Tuesday, 10-Dec-02 23:50:13 */ + isoc /* Tue Dec 10 23:50:13 2002 */ + } fmt; + + fmt = 0; + end = value + len; + +#if (NGX_SUPPRESS_WARN) + day = 32; + year = 2038; +#endif + + for (p = value; p < end; p++) { + if (*p == ',') { + break; + } + + if (*p == ' ') { + fmt = isoc; + break; + } + } + + for (p++; p < end; p++) + if (*p != ' ') { + break; + } + + if (end - p < 18) { + return NGX_ERROR; + } + + if (fmt != isoc) { + if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') { + return NGX_ERROR; + } + + day = (*p - '0') * 10 + *(p + 1) - '0'; + p += 2; + + if (*p == ' ') { + if (end - p < 18) { + return NGX_ERROR; + } + fmt = rfc822; + + } else if (*p == '-') { + fmt = rfc850; + + } else { + return NGX_ERROR; + } + + p++; + } + + switch (*p) { + + case 'J': + month = *(p + 1) == 'a' ? 0 : *(p + 2) == 'n' ? 5 : 6; + break; + + case 'F': + month = 1; + break; + + case 'M': + month = *(p + 2) == 'r' ? 2 : 4; + break; + + case 'A': + month = *(p + 1) == 'p' ? 3 : 7; + break; + + case 'S': + month = 8; + break; + + case 'O': + month = 9; + break; + + case 'N': + month = 10; + break; + + case 'D': + month = 11; + break; + + default: + return NGX_ERROR; + } + + p += 3; + + if ((fmt == rfc822 && *p != ' ') || (fmt == rfc850 && *p != '-')) { + return NGX_ERROR; + } + + p++; + + if (fmt == rfc822) { + if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9' + || *(p + 2) < '0' || *(p + 2) > '9' + || *(p + 3) < '0' || *(p + 3) > '9') + { + return NGX_ERROR; + } + + year = (*p - '0') * 1000 + (*(p + 1) - '0') * 100 + + (*(p + 2) - '0') * 10 + *(p + 3) - '0'; + p += 4; + + } else if (fmt == rfc850) { + if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') { + return NGX_ERROR; + } + + year = (*p - '0') * 10 + *(p + 1) - '0'; + year += (year < 70) ? 2000 : 1900; + p += 2; + } + + if (fmt == isoc) { + if (*p == ' ') { + p++; + } + + if (*p < '0' || *p > '9') { + return NGX_ERROR; + } + + day = *p++ - '0'; + + if (*p != ' ') { + if (*p < '0' || *p > '9') { + return NGX_ERROR; + } + + day = day * 10 + *p++ - '0'; + } + + if (end - p < 14) { + return NGX_ERROR; + } + } + + if (*p++ != ' ') { + return NGX_ERROR; + } + + if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') { + return NGX_ERROR; + } + + hour = (*p - '0') * 10 + *(p + 1) - '0'; + p += 2; + + if (*p++ != ':') { + return NGX_ERROR; + } + + if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') { + return NGX_ERROR; + } + + min = (*p - '0') * 10 + *(p + 1) - '0'; + p += 2; + + if (*p++ != ':') { + return NGX_ERROR; + } + + if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') { + return NGX_ERROR; + } + + sec = (*p - '0') * 10 + *(p + 1) - '0'; + + if (fmt == isoc) { + p += 2; + + if (*p++ != ' ') { + return NGX_ERROR; + } + + if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9' + || *(p + 2) < '0' || *(p + 2) > '9' + || *(p + 3) < '0' || *(p + 3) > '9') + { + return NGX_ERROR; + } + + year = (*p - '0') * 1000 + (*(p + 1) - '0') * 100 + + (*(p + 2) - '0') * 10 + *(p + 3) - '0'; + } + + if (hour > 23 || min > 59 || sec > 59) { + return NGX_ERROR; + } + + if (day == 29 && month == 1) { + if ((year & 3) || ((year % 100 == 0) && (year % 400) != 0)) { + return NGX_ERROR; + } + + } else if (day > mday[month]) { + return NGX_ERROR; + } + + /* + * shift new year to March 1 and start months from 1 (not 0), + * it is needed for Gauss' formula + */ + + if (--month <= 0) { + month += 12; + year -= 1; + } + + /* Gauss' formula for Gregorian days since March 1, 1 BC */ + + time = (uint64_t) ( + /* days in years including leap years since March 1, 1 BC */ + + 365 * year + year / 4 - year / 100 + year / 400 + + /* days before the month */ + + + 367 * month / 12 - 30 + + /* days before the day */ + + + day - 1 + + /* + * 719527 days were between March 1, 1 BC and March 1, 1970, + * 31 and 28 days were in January and February 1970 + */ + + - 719527 + 31 + 28) * 86400 + hour * 3600 + min * 60 + sec; + +#if (NGX_TIME_T_SIZE <= 4) + + if (time > 0x7fffffff) { + return NGX_ERROR; + } + +#endif + + return (time_t) time; +} diff --git a/app/nginx/src/core/ngx_parse_time.h b/app/nginx/src/core/ngx_parse_time.h new file mode 100644 index 0000000..aa542eb --- /dev/null +++ b/app/nginx/src/core/ngx_parse_time.h @@ -0,0 +1,22 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_PARSE_TIME_H_INCLUDED_ +#define _NGX_PARSE_TIME_H_INCLUDED_ + + +#include +#include + + +time_t ngx_parse_http_time(u_char *value, size_t len); + +/* compatibility */ +#define ngx_http_parse_time(value, len) ngx_parse_http_time(value, len) + + +#endif /* _NGX_PARSE_TIME_H_INCLUDED_ */ diff --git a/app/nginx/src/core/ngx_proxy_protocol.c b/app/nginx/src/core/ngx_proxy_protocol.c new file mode 100644 index 0000000..523ec35 --- /dev/null +++ b/app/nginx/src/core/ngx_proxy_protocol.c @@ -0,0 +1,168 @@ + +/* + * Copyright (C) Roman Arutyunyan + * Copyright (C) Nginx, Inc. + */ + + +#include +#include + + +u_char * +ngx_proxy_protocol_read(ngx_connection_t *c, u_char *buf, u_char *last) +{ + size_t len; + u_char ch, *p, *addr, *port; + ngx_int_t n; + + p = buf; + len = last - buf; + + if (len < 8 || ngx_strncmp(p, "PROXY ", 6) != 0) { + goto invalid; + } + + p += 6; + len -= 6; + + if (len >= 7 && ngx_strncmp(p, "UNKNOWN", 7) == 0) { + ngx_log_debug0(NGX_LOG_DEBUG_CORE, c->log, 0, + "PROXY protocol unknown protocol"); + p += 7; + goto skip; + } + + if (len < 5 || ngx_strncmp(p, "TCP", 3) != 0 + || (p[3] != '4' && p[3] != '6') || p[4] != ' ') + { + goto invalid; + } + + p += 5; + addr = p; + + for ( ;; ) { + if (p == last) { + goto invalid; + } + + ch = *p++; + + if (ch == ' ') { + break; + } + + if (ch != ':' && ch != '.' + && (ch < 'a' || ch > 'f') + && (ch < 'A' || ch > 'F') + && (ch < '0' || ch > '9')) + { + goto invalid; + } + } + + len = p - addr - 1; + c->proxy_protocol_addr.data = ngx_pnalloc(c->pool, len); + + if (c->proxy_protocol_addr.data == NULL) { + return NULL; + } + + ngx_memcpy(c->proxy_protocol_addr.data, addr, len); + c->proxy_protocol_addr.len = len; + + for ( ;; ) { + if (p == last) { + goto invalid; + } + + if (*p++ == ' ') { + break; + } + } + + port = p; + + for ( ;; ) { + if (p == last) { + goto invalid; + } + + if (*p++ == ' ') { + break; + } + } + + len = p - port - 1; + + n = ngx_atoi(port, len); + + if (n < 0 || n > 65535) { + goto invalid; + } + + c->proxy_protocol_port = (in_port_t) n; + + ngx_log_debug2(NGX_LOG_DEBUG_CORE, c->log, 0, + "PROXY protocol address: %V %i", &c->proxy_protocol_addr, n); + +skip: + + for ( /* void */ ; p < last - 1; p++) { + if (p[0] == CR && p[1] == LF) { + return p + 2; + } + } + +invalid: + + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "broken header: \"%*s\"", (size_t) (last - buf), buf); + + return NULL; +} + + +u_char * +ngx_proxy_protocol_write(ngx_connection_t *c, u_char *buf, u_char *last) +{ + ngx_uint_t port, lport; + + if (last - buf < NGX_PROXY_PROTOCOL_MAX_HEADER) { + return NULL; + } + + if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) { + return NULL; + } + + switch (c->sockaddr->sa_family) { + + case AF_INET: + buf = ngx_cpymem(buf, "PROXY TCP4 ", sizeof("PROXY TCP4 ") - 1); + break; + +#if (NGX_HAVE_INET6) + case AF_INET6: + buf = ngx_cpymem(buf, "PROXY TCP6 ", sizeof("PROXY TCP6 ") - 1); + break; +#endif + + default: + return ngx_cpymem(buf, "PROXY UNKNOWN" CRLF, + sizeof("PROXY UNKNOWN" CRLF) - 1); + } + + buf += ngx_sock_ntop(c->sockaddr, c->socklen, buf, last - buf, 0); + + *buf++ = ' '; + + buf += ngx_sock_ntop(c->local_sockaddr, c->local_socklen, buf, last - buf, + 0); + + port = ngx_inet_get_port(c->sockaddr); + lport = ngx_inet_get_port(c->local_sockaddr); + + return ngx_slprintf(buf, last, " %ui %ui" CRLF, port, lport); +} diff --git a/app/nginx/src/core/ngx_proxy_protocol.h b/app/nginx/src/core/ngx_proxy_protocol.h new file mode 100644 index 0000000..fb848f6 --- /dev/null +++ b/app/nginx/src/core/ngx_proxy_protocol.h @@ -0,0 +1,25 @@ + +/* + * Copyright (C) Roman Arutyunyan + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_PROXY_PROTOCOL_H_INCLUDED_ +#define _NGX_PROXY_PROTOCOL_H_INCLUDED_ + + +#include +#include + + +#define NGX_PROXY_PROTOCOL_MAX_HEADER 107 + + +u_char *ngx_proxy_protocol_read(ngx_connection_t *c, u_char *buf, + u_char *last); +u_char *ngx_proxy_protocol_write(ngx_connection_t *c, u_char *buf, + u_char *last); + + +#endif /* _NGX_PROXY_PROTOCOL_H_INCLUDED_ */ diff --git a/app/nginx/src/core/ngx_queue.c b/app/nginx/src/core/ngx_queue.c new file mode 100644 index 0000000..3cacaf3 --- /dev/null +++ b/app/nginx/src/core/ngx_queue.c @@ -0,0 +1,80 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include + + +/* + * find the middle queue element if the queue has odd number of elements + * or the first element of the queue's second part otherwise + */ + +ngx_queue_t * +ngx_queue_middle(ngx_queue_t *queue) +{ + ngx_queue_t *middle, *next; + + middle = ngx_queue_head(queue); + + if (middle == ngx_queue_last(queue)) { + return middle; + } + + next = ngx_queue_head(queue); + + for ( ;; ) { + middle = ngx_queue_next(middle); + + next = ngx_queue_next(next); + + if (next == ngx_queue_last(queue)) { + return middle; + } + + next = ngx_queue_next(next); + + if (next == ngx_queue_last(queue)) { + return middle; + } + } +} + + +/* the stable insertion sort */ + +void +ngx_queue_sort(ngx_queue_t *queue, + ngx_int_t (*cmp)(const ngx_queue_t *, const ngx_queue_t *)) +{ + ngx_queue_t *q, *prev, *next; + + q = ngx_queue_head(queue); + + if (q == ngx_queue_last(queue)) { + return; + } + + for (q = ngx_queue_next(q); q != ngx_queue_sentinel(queue); q = next) { + + prev = ngx_queue_prev(q); + next = ngx_queue_next(q); + + ngx_queue_remove(q); + + do { + if (cmp(prev, q) <= 0) { + break; + } + + prev = ngx_queue_prev(prev); + + } while (prev != ngx_queue_sentinel(queue)); + + ngx_queue_insert_after(prev, q); + } +} diff --git a/app/nginx/src/core/ngx_queue.h b/app/nginx/src/core/ngx_queue.h new file mode 100644 index 0000000..038bf12 --- /dev/null +++ b/app/nginx/src/core/ngx_queue.h @@ -0,0 +1,112 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include + + +#ifndef _NGX_QUEUE_H_INCLUDED_ +#define _NGX_QUEUE_H_INCLUDED_ + + +typedef struct ngx_queue_s ngx_queue_t; + +struct ngx_queue_s { + ngx_queue_t *prev; + ngx_queue_t *next; +}; + + +#define ngx_queue_init(q) \ + (q)->prev = q; \ + (q)->next = q + + +#define ngx_queue_empty(h) \ + (h == (h)->prev) + + +#define ngx_queue_insert_head(h, x) \ + (x)->next = (h)->next; \ + (x)->next->prev = x; \ + (x)->prev = h; \ + (h)->next = x + + +#define ngx_queue_insert_after ngx_queue_insert_head + + +#define ngx_queue_insert_tail(h, x) \ + (x)->prev = (h)->prev; \ + (x)->prev->next = x; \ + (x)->next = h; \ + (h)->prev = x + + +#define ngx_queue_head(h) \ + (h)->next + + +#define ngx_queue_last(h) \ + (h)->prev + + +#define ngx_queue_sentinel(h) \ + (h) + + +#define ngx_queue_next(q) \ + (q)->next + + +#define ngx_queue_prev(q) \ + (q)->prev + + +#if (NGX_DEBUG) + +#define ngx_queue_remove(x) \ + (x)->next->prev = (x)->prev; \ + (x)->prev->next = (x)->next; \ + (x)->prev = NULL; \ + (x)->next = NULL + +#else + +#define ngx_queue_remove(x) \ + (x)->next->prev = (x)->prev; \ + (x)->prev->next = (x)->next + +#endif + + +#define ngx_queue_split(h, q, n) \ + (n)->prev = (h)->prev; \ + (n)->prev->next = n; \ + (n)->next = q; \ + (h)->prev = (q)->prev; \ + (h)->prev->next = h; \ + (q)->prev = n; + + +#define ngx_queue_add(h, n) \ + (h)->prev->next = (n)->next; \ + (n)->next->prev = (h)->prev; \ + (h)->prev = (n)->prev; \ + (h)->prev->next = h; + + +#define ngx_queue_data(q, type, link) \ + (type *) ((u_char *) q - offsetof(type, link)) + + +ngx_queue_t *ngx_queue_middle(ngx_queue_t *queue); +void ngx_queue_sort(ngx_queue_t *queue, + ngx_int_t (*cmp)(const ngx_queue_t *, const ngx_queue_t *)); + + +#endif /* _NGX_QUEUE_H_INCLUDED_ */ diff --git a/app/nginx/src/core/ngx_radix_tree.c b/app/nginx/src/core/ngx_radix_tree.c new file mode 100644 index 0000000..c1d8737 --- /dev/null +++ b/app/nginx/src/core/ngx_radix_tree.c @@ -0,0 +1,488 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include + + +static ngx_radix_node_t *ngx_radix_alloc(ngx_radix_tree_t *tree); + + +ngx_radix_tree_t * +ngx_radix_tree_create(ngx_pool_t *pool, ngx_int_t preallocate) +{ + uint32_t key, mask, inc; + ngx_radix_tree_t *tree; + + tree = ngx_palloc(pool, sizeof(ngx_radix_tree_t)); + if (tree == NULL) { + return NULL; + } + + tree->pool = pool; + tree->free = NULL; + tree->start = NULL; + tree->size = 0; + + tree->root = ngx_radix_alloc(tree); + if (tree->root == NULL) { + return NULL; + } + + tree->root->right = NULL; + tree->root->left = NULL; + tree->root->parent = NULL; + tree->root->value = NGX_RADIX_NO_VALUE; + + if (preallocate == 0) { + return tree; + } + + /* + * Preallocation of first nodes : 0, 1, 00, 01, 10, 11, 000, 001, etc. + * increases TLB hits even if for first lookup iterations. + * On 32-bit platforms the 7 preallocated bits takes continuous 4K, + * 8 - 8K, 9 - 16K, etc. On 64-bit platforms the 6 preallocated bits + * takes continuous 4K, 7 - 8K, 8 - 16K, etc. There is no sense to + * to preallocate more than one page, because further preallocation + * distributes the only bit per page. Instead, a random insertion + * may distribute several bits per page. + * + * Thus, by default we preallocate maximum + * 6 bits on amd64 (64-bit platform and 4K pages) + * 7 bits on i386 (32-bit platform and 4K pages) + * 7 bits on sparc64 in 64-bit mode (8K pages) + * 8 bits on sparc64 in 32-bit mode (8K pages) + */ + + if (preallocate == -1) { + switch (ngx_pagesize / sizeof(ngx_radix_node_t)) { + + /* amd64 */ + case 128: + preallocate = 6; + break; + + /* i386, sparc64 */ + case 256: + preallocate = 7; + break; + + /* sparc64 in 32-bit mode */ + default: + preallocate = 8; + } + } + + mask = 0; + inc = 0x80000000; + + while (preallocate--) { + + key = 0; + mask >>= 1; + mask |= 0x80000000; + + do { + if (ngx_radix32tree_insert(tree, key, mask, NGX_RADIX_NO_VALUE) + != NGX_OK) + { + return NULL; + } + + key += inc; + + } while (key); + + inc >>= 1; + } + + return tree; +} + + +ngx_int_t +ngx_radix32tree_insert(ngx_radix_tree_t *tree, uint32_t key, uint32_t mask, + uintptr_t value) +{ + uint32_t bit; + ngx_radix_node_t *node, *next; + + bit = 0x80000000; + + node = tree->root; + next = tree->root; + + while (bit & mask) { + if (key & bit) { + next = node->right; + + } else { + next = node->left; + } + + if (next == NULL) { + break; + } + + bit >>= 1; + node = next; + } + + if (next) { + if (node->value != NGX_RADIX_NO_VALUE) { + return NGX_BUSY; + } + + node->value = value; + return NGX_OK; + } + + while (bit & mask) { + next = ngx_radix_alloc(tree); + if (next == NULL) { + return NGX_ERROR; + } + + next->right = NULL; + next->left = NULL; + next->parent = node; + next->value = NGX_RADIX_NO_VALUE; + + if (key & bit) { + node->right = next; + + } else { + node->left = next; + } + + bit >>= 1; + node = next; + } + + node->value = value; + + return NGX_OK; +} + + +ngx_int_t +ngx_radix32tree_delete(ngx_radix_tree_t *tree, uint32_t key, uint32_t mask) +{ + uint32_t bit; + ngx_radix_node_t *node; + + bit = 0x80000000; + node = tree->root; + + while (node && (bit & mask)) { + if (key & bit) { + node = node->right; + + } else { + node = node->left; + } + + bit >>= 1; + } + + if (node == NULL) { + return NGX_ERROR; + } + + if (node->right || node->left) { + if (node->value != NGX_RADIX_NO_VALUE) { + node->value = NGX_RADIX_NO_VALUE; + return NGX_OK; + } + + return NGX_ERROR; + } + + for ( ;; ) { + if (node->parent->right == node) { + node->parent->right = NULL; + + } else { + node->parent->left = NULL; + } + + node->right = tree->free; + tree->free = node; + + node = node->parent; + + if (node->right || node->left) { + break; + } + + if (node->value != NGX_RADIX_NO_VALUE) { + break; + } + + if (node->parent == NULL) { + break; + } + } + + return NGX_OK; +} + + +uintptr_t +ngx_radix32tree_find(ngx_radix_tree_t *tree, uint32_t key) +{ + uint32_t bit; + uintptr_t value; + ngx_radix_node_t *node; + + bit = 0x80000000; + value = NGX_RADIX_NO_VALUE; + node = tree->root; + + while (node) { + if (node->value != NGX_RADIX_NO_VALUE) { + value = node->value; + } + + if (key & bit) { + node = node->right; + + } else { + node = node->left; + } + + bit >>= 1; + } + + return value; +} + + +#if (NGX_HAVE_INET6) + +ngx_int_t +ngx_radix128tree_insert(ngx_radix_tree_t *tree, u_char *key, u_char *mask, + uintptr_t value) +{ + u_char bit; + ngx_uint_t i; + ngx_radix_node_t *node, *next; + + i = 0; + bit = 0x80; + + node = tree->root; + next = tree->root; + + while (bit & mask[i]) { + if (key[i] & bit) { + next = node->right; + + } else { + next = node->left; + } + + if (next == NULL) { + break; + } + + bit >>= 1; + node = next; + + if (bit == 0) { + if (++i == 16) { + break; + } + + bit = 0x80; + } + } + + if (next) { + if (node->value != NGX_RADIX_NO_VALUE) { + return NGX_BUSY; + } + + node->value = value; + return NGX_OK; + } + + while (bit & mask[i]) { + next = ngx_radix_alloc(tree); + if (next == NULL) { + return NGX_ERROR; + } + + next->right = NULL; + next->left = NULL; + next->parent = node; + next->value = NGX_RADIX_NO_VALUE; + + if (key[i] & bit) { + node->right = next; + + } else { + node->left = next; + } + + bit >>= 1; + node = next; + + if (bit == 0) { + if (++i == 16) { + break; + } + + bit = 0x80; + } + } + + node->value = value; + + return NGX_OK; +} + + +ngx_int_t +ngx_radix128tree_delete(ngx_radix_tree_t *tree, u_char *key, u_char *mask) +{ + u_char bit; + ngx_uint_t i; + ngx_radix_node_t *node; + + i = 0; + bit = 0x80; + node = tree->root; + + while (node && (bit & mask[i])) { + if (key[i] & bit) { + node = node->right; + + } else { + node = node->left; + } + + bit >>= 1; + + if (bit == 0) { + if (++i == 16) { + break; + } + + bit = 0x80; + } + } + + if (node == NULL) { + return NGX_ERROR; + } + + if (node->right || node->left) { + if (node->value != NGX_RADIX_NO_VALUE) { + node->value = NGX_RADIX_NO_VALUE; + return NGX_OK; + } + + return NGX_ERROR; + } + + for ( ;; ) { + if (node->parent->right == node) { + node->parent->right = NULL; + + } else { + node->parent->left = NULL; + } + + node->right = tree->free; + tree->free = node; + + node = node->parent; + + if (node->right || node->left) { + break; + } + + if (node->value != NGX_RADIX_NO_VALUE) { + break; + } + + if (node->parent == NULL) { + break; + } + } + + return NGX_OK; +} + + +uintptr_t +ngx_radix128tree_find(ngx_radix_tree_t *tree, u_char *key) +{ + u_char bit; + uintptr_t value; + ngx_uint_t i; + ngx_radix_node_t *node; + + i = 0; + bit = 0x80; + value = NGX_RADIX_NO_VALUE; + node = tree->root; + + while (node) { + if (node->value != NGX_RADIX_NO_VALUE) { + value = node->value; + } + + if (key[i] & bit) { + node = node->right; + + } else { + node = node->left; + } + + bit >>= 1; + + if (bit == 0) { + i++; + bit = 0x80; + } + } + + return value; +} + +#endif + + +static ngx_radix_node_t * +ngx_radix_alloc(ngx_radix_tree_t *tree) +{ + ngx_radix_node_t *p; + + if (tree->free) { + p = tree->free; + tree->free = tree->free->right; + return p; + } + + if (tree->size < sizeof(ngx_radix_node_t)) { + tree->start = ngx_pmemalign(tree->pool, ngx_pagesize, ngx_pagesize); + if (tree->start == NULL) { + return NULL; + } + + tree->size = ngx_pagesize; + } + + p = (ngx_radix_node_t *) tree->start; + tree->start += sizeof(ngx_radix_node_t); + tree->size -= sizeof(ngx_radix_node_t); + + return p; +} diff --git a/app/nginx/src/core/ngx_radix_tree.h b/app/nginx/src/core/ngx_radix_tree.h new file mode 100644 index 0000000..4fe06e0 --- /dev/null +++ b/app/nginx/src/core/ngx_radix_tree.h @@ -0,0 +1,55 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_RADIX_TREE_H_INCLUDED_ +#define _NGX_RADIX_TREE_H_INCLUDED_ + + +#include +#include + + +#define NGX_RADIX_NO_VALUE (uintptr_t) -1 + +typedef struct ngx_radix_node_s ngx_radix_node_t; + +struct ngx_radix_node_s { + ngx_radix_node_t *right; + ngx_radix_node_t *left; + ngx_radix_node_t *parent; + uintptr_t value; +}; + + +typedef struct { + ngx_radix_node_t *root; + ngx_pool_t *pool; + ngx_radix_node_t *free; + char *start; + size_t size; +} ngx_radix_tree_t; + + +ngx_radix_tree_t *ngx_radix_tree_create(ngx_pool_t *pool, + ngx_int_t preallocate); + +ngx_int_t ngx_radix32tree_insert(ngx_radix_tree_t *tree, + uint32_t key, uint32_t mask, uintptr_t value); +ngx_int_t ngx_radix32tree_delete(ngx_radix_tree_t *tree, + uint32_t key, uint32_t mask); +uintptr_t ngx_radix32tree_find(ngx_radix_tree_t *tree, uint32_t key); + +#if (NGX_HAVE_INET6) +ngx_int_t ngx_radix128tree_insert(ngx_radix_tree_t *tree, + u_char *key, u_char *mask, uintptr_t value); +ngx_int_t ngx_radix128tree_delete(ngx_radix_tree_t *tree, + u_char *key, u_char *mask); +uintptr_t ngx_radix128tree_find(ngx_radix_tree_t *tree, u_char *key); +#endif + + +#endif /* _NGX_RADIX_TREE_H_INCLUDED_ */ diff --git a/app/nginx/src/core/ngx_rbtree.c b/app/nginx/src/core/ngx_rbtree.c new file mode 100644 index 0000000..969d549 --- /dev/null +++ b/app/nginx/src/core/ngx_rbtree.c @@ -0,0 +1,409 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include + + +/* + * The red-black tree code is based on the algorithm described in + * the "Introduction to Algorithms" by Cormen, Leiserson and Rivest. + */ + + +static ngx_inline void ngx_rbtree_left_rotate(ngx_rbtree_node_t **root, + ngx_rbtree_node_t *sentinel, ngx_rbtree_node_t *node); +static ngx_inline void ngx_rbtree_right_rotate(ngx_rbtree_node_t **root, + ngx_rbtree_node_t *sentinel, ngx_rbtree_node_t *node); + + +void +ngx_rbtree_insert(ngx_rbtree_t *tree, ngx_rbtree_node_t *node) +{ + ngx_rbtree_node_t **root, *temp, *sentinel; + + /* a binary tree insert */ + + root = &tree->root; + sentinel = tree->sentinel; + + if (*root == sentinel) { + node->parent = NULL; + node->left = sentinel; + node->right = sentinel; + ngx_rbt_black(node); + *root = node; + + return; + } + + tree->insert(*root, node, sentinel); + + /* re-balance tree */ + + while (node != *root && ngx_rbt_is_red(node->parent)) { + + if (node->parent == node->parent->parent->left) { + temp = node->parent->parent->right; + + if (ngx_rbt_is_red(temp)) { + ngx_rbt_black(node->parent); + ngx_rbt_black(temp); + ngx_rbt_red(node->parent->parent); + node = node->parent->parent; + + } else { + if (node == node->parent->right) { + node = node->parent; + ngx_rbtree_left_rotate(root, sentinel, node); + } + + ngx_rbt_black(node->parent); + ngx_rbt_red(node->parent->parent); + ngx_rbtree_right_rotate(root, sentinel, node->parent->parent); + } + + } else { + temp = node->parent->parent->left; + + if (ngx_rbt_is_red(temp)) { + ngx_rbt_black(node->parent); + ngx_rbt_black(temp); + ngx_rbt_red(node->parent->parent); + node = node->parent->parent; + + } else { + if (node == node->parent->left) { + node = node->parent; + ngx_rbtree_right_rotate(root, sentinel, node); + } + + ngx_rbt_black(node->parent); + ngx_rbt_red(node->parent->parent); + ngx_rbtree_left_rotate(root, sentinel, node->parent->parent); + } + } + } + + ngx_rbt_black(*root); +} + + +void +ngx_rbtree_insert_value(ngx_rbtree_node_t *temp, ngx_rbtree_node_t *node, + ngx_rbtree_node_t *sentinel) +{ + ngx_rbtree_node_t **p; + + for ( ;; ) { + + p = (node->key < temp->key) ? &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); +} + + +void +ngx_rbtree_insert_timer_value(ngx_rbtree_node_t *temp, ngx_rbtree_node_t *node, + ngx_rbtree_node_t *sentinel) +{ + ngx_rbtree_node_t **p; + + for ( ;; ) { + + /* + * Timer values + * 1) are spread in small range, usually several minutes, + * 2) and overflow each 49 days, if milliseconds are stored in 32 bits. + * The comparison takes into account that overflow. + */ + + /* node->key < temp->key */ + + p = ((ngx_rbtree_key_int_t) (node->key - temp->key) < 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); +} + + +void +ngx_rbtree_delete(ngx_rbtree_t *tree, ngx_rbtree_node_t *node) +{ + ngx_uint_t red; + ngx_rbtree_node_t **root, *sentinel, *subst, *temp, *w; + + /* a binary tree delete */ + + root = &tree->root; + sentinel = tree->sentinel; + + if (node->left == sentinel) { + temp = node->right; + subst = node; + + } else if (node->right == sentinel) { + temp = node->left; + subst = node; + + } else { + subst = ngx_rbtree_min(node->right, sentinel); + + if (subst->left != sentinel) { + temp = subst->left; + } else { + temp = subst->right; + } + } + + if (subst == *root) { + *root = temp; + ngx_rbt_black(temp); + + /* DEBUG stuff */ + node->left = NULL; + node->right = NULL; + node->parent = NULL; + node->key = 0; + + return; + } + + red = ngx_rbt_is_red(subst); + + if (subst == subst->parent->left) { + subst->parent->left = temp; + + } else { + subst->parent->right = temp; + } + + if (subst == node) { + + temp->parent = subst->parent; + + } else { + + if (subst->parent == node) { + temp->parent = subst; + + } else { + temp->parent = subst->parent; + } + + subst->left = node->left; + subst->right = node->right; + subst->parent = node->parent; + ngx_rbt_copy_color(subst, node); + + if (node == *root) { + *root = subst; + + } else { + if (node == node->parent->left) { + node->parent->left = subst; + } else { + node->parent->right = subst; + } + } + + if (subst->left != sentinel) { + subst->left->parent = subst; + } + + if (subst->right != sentinel) { + subst->right->parent = subst; + } + } + + /* DEBUG stuff */ + node->left = NULL; + node->right = NULL; + node->parent = NULL; + node->key = 0; + + if (red) { + return; + } + + /* a delete fixup */ + + while (temp != *root && ngx_rbt_is_black(temp)) { + + if (temp == temp->parent->left) { + w = temp->parent->right; + + if (ngx_rbt_is_red(w)) { + ngx_rbt_black(w); + ngx_rbt_red(temp->parent); + ngx_rbtree_left_rotate(root, sentinel, temp->parent); + w = temp->parent->right; + } + + if (ngx_rbt_is_black(w->left) && ngx_rbt_is_black(w->right)) { + ngx_rbt_red(w); + temp = temp->parent; + + } else { + if (ngx_rbt_is_black(w->right)) { + ngx_rbt_black(w->left); + ngx_rbt_red(w); + ngx_rbtree_right_rotate(root, sentinel, w); + w = temp->parent->right; + } + + ngx_rbt_copy_color(w, temp->parent); + ngx_rbt_black(temp->parent); + ngx_rbt_black(w->right); + ngx_rbtree_left_rotate(root, sentinel, temp->parent); + temp = *root; + } + + } else { + w = temp->parent->left; + + if (ngx_rbt_is_red(w)) { + ngx_rbt_black(w); + ngx_rbt_red(temp->parent); + ngx_rbtree_right_rotate(root, sentinel, temp->parent); + w = temp->parent->left; + } + + if (ngx_rbt_is_black(w->left) && ngx_rbt_is_black(w->right)) { + ngx_rbt_red(w); + temp = temp->parent; + + } else { + if (ngx_rbt_is_black(w->left)) { + ngx_rbt_black(w->right); + ngx_rbt_red(w); + ngx_rbtree_left_rotate(root, sentinel, w); + w = temp->parent->left; + } + + ngx_rbt_copy_color(w, temp->parent); + ngx_rbt_black(temp->parent); + ngx_rbt_black(w->left); + ngx_rbtree_right_rotate(root, sentinel, temp->parent); + temp = *root; + } + } + } + + ngx_rbt_black(temp); +} + + +static ngx_inline void +ngx_rbtree_left_rotate(ngx_rbtree_node_t **root, ngx_rbtree_node_t *sentinel, + ngx_rbtree_node_t *node) +{ + ngx_rbtree_node_t *temp; + + temp = node->right; + node->right = temp->left; + + if (temp->left != sentinel) { + temp->left->parent = node; + } + + temp->parent = node->parent; + + if (node == *root) { + *root = temp; + + } else if (node == node->parent->left) { + node->parent->left = temp; + + } else { + node->parent->right = temp; + } + + temp->left = node; + node->parent = temp; +} + + +static ngx_inline void +ngx_rbtree_right_rotate(ngx_rbtree_node_t **root, ngx_rbtree_node_t *sentinel, + ngx_rbtree_node_t *node) +{ + ngx_rbtree_node_t *temp; + + temp = node->left; + node->left = temp->right; + + if (temp->right != sentinel) { + temp->right->parent = node; + } + + temp->parent = node->parent; + + if (node == *root) { + *root = temp; + + } else if (node == node->parent->right) { + node->parent->right = temp; + + } else { + node->parent->left = temp; + } + + temp->right = node; + node->parent = temp; +} + + +ngx_rbtree_node_t * +ngx_rbtree_next(ngx_rbtree_t *tree, ngx_rbtree_node_t *node) +{ + ngx_rbtree_node_t *root, *sentinel, *parent; + + sentinel = tree->sentinel; + + if (node->right != sentinel) { + return ngx_rbtree_min(node->right, sentinel); + } + + root = tree->root; + + for ( ;; ) { + parent = node->parent; + + if (node == root) { + return NULL; + } + + if (node == parent->left) { + return parent; + } + + node = parent; + } +} diff --git a/app/nginx/src/core/ngx_rbtree.h b/app/nginx/src/core/ngx_rbtree.h new file mode 100644 index 0000000..97f0e3e --- /dev/null +++ b/app/nginx/src/core/ngx_rbtree.h @@ -0,0 +1,84 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_RBTREE_H_INCLUDED_ +#define _NGX_RBTREE_H_INCLUDED_ + + +#include +#include + + +typedef ngx_uint_t ngx_rbtree_key_t; +typedef ngx_int_t ngx_rbtree_key_int_t; + + +typedef struct ngx_rbtree_node_s ngx_rbtree_node_t; + +struct ngx_rbtree_node_s { + ngx_rbtree_key_t key; + ngx_rbtree_node_t *left; + ngx_rbtree_node_t *right; + ngx_rbtree_node_t *parent; + u_char color; + u_char data; +}; + + +typedef struct ngx_rbtree_s ngx_rbtree_t; + +typedef void (*ngx_rbtree_insert_pt) (ngx_rbtree_node_t *root, + ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel); + +struct ngx_rbtree_s { + ngx_rbtree_node_t *root; + ngx_rbtree_node_t *sentinel; + ngx_rbtree_insert_pt insert; +}; + + +#define ngx_rbtree_init(tree, s, i) \ + ngx_rbtree_sentinel_init(s); \ + (tree)->root = s; \ + (tree)->sentinel = s; \ + (tree)->insert = i + + +void ngx_rbtree_insert(ngx_rbtree_t *tree, ngx_rbtree_node_t *node); +void ngx_rbtree_delete(ngx_rbtree_t *tree, ngx_rbtree_node_t *node); +void ngx_rbtree_insert_value(ngx_rbtree_node_t *root, ngx_rbtree_node_t *node, + ngx_rbtree_node_t *sentinel); +void ngx_rbtree_insert_timer_value(ngx_rbtree_node_t *root, + ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel); +ngx_rbtree_node_t *ngx_rbtree_next(ngx_rbtree_t *tree, + ngx_rbtree_node_t *node); + + +#define ngx_rbt_red(node) ((node)->color = 1) +#define ngx_rbt_black(node) ((node)->color = 0) +#define ngx_rbt_is_red(node) ((node)->color) +#define ngx_rbt_is_black(node) (!ngx_rbt_is_red(node)) +#define ngx_rbt_copy_color(n1, n2) (n1->color = n2->color) + + +/* a sentinel must be black */ + +#define ngx_rbtree_sentinel_init(node) ngx_rbt_black(node) + + +static ngx_inline ngx_rbtree_node_t * +ngx_rbtree_min(ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel) +{ + while (node->left != sentinel) { + node = node->left; + } + + return node; +} + + +#endif /* _NGX_RBTREE_H_INCLUDED_ */ diff --git a/app/nginx/src/core/ngx_regex.c b/app/nginx/src/core/ngx_regex.c new file mode 100644 index 0000000..9939dce --- /dev/null +++ b/app/nginx/src/core/ngx_regex.c @@ -0,0 +1,435 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include + + +typedef struct { + ngx_flag_t pcre_jit; +} ngx_regex_conf_t; + + +static void * ngx_libc_cdecl ngx_regex_malloc(size_t size); +static void ngx_libc_cdecl ngx_regex_free(void *p); +#if (NGX_HAVE_PCRE_JIT) +static void ngx_pcre_free_studies(void *data); +#endif + +static ngx_int_t ngx_regex_module_init(ngx_cycle_t *cycle); + +static void *ngx_regex_create_conf(ngx_cycle_t *cycle); +static char *ngx_regex_init_conf(ngx_cycle_t *cycle, void *conf); + +static char *ngx_regex_pcre_jit(ngx_conf_t *cf, void *post, void *data); +static ngx_conf_post_t ngx_regex_pcre_jit_post = { ngx_regex_pcre_jit }; + + +static ngx_command_t ngx_regex_commands[] = { + + { ngx_string("pcre_jit"), + NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_FLAG, + ngx_conf_set_flag_slot, + 0, + offsetof(ngx_regex_conf_t, pcre_jit), + &ngx_regex_pcre_jit_post }, + + ngx_null_command +}; + + +static ngx_core_module_t ngx_regex_module_ctx = { + ngx_string("regex"), + ngx_regex_create_conf, + ngx_regex_init_conf +}; + + +ngx_module_t ngx_regex_module = { + NGX_MODULE_V1, + &ngx_regex_module_ctx, /* module context */ + ngx_regex_commands, /* module directives */ + NGX_CORE_MODULE, /* module type */ + NULL, /* init master */ + ngx_regex_module_init, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +static ngx_pool_t *ngx_pcre_pool; +static ngx_list_t *ngx_pcre_studies; + + +void +ngx_regex_init(void) +{ + pcre_malloc = ngx_regex_malloc; + pcre_free = ngx_regex_free; +} + + +static ngx_inline void +ngx_regex_malloc_init(ngx_pool_t *pool) +{ + ngx_pcre_pool = pool; +} + + +static ngx_inline void +ngx_regex_malloc_done(void) +{ + ngx_pcre_pool = NULL; +} + + +ngx_int_t +ngx_regex_compile(ngx_regex_compile_t *rc) +{ + int n, erroff; + char *p; + pcre *re; + const char *errstr; + ngx_regex_elt_t *elt; + + ngx_regex_malloc_init(rc->pool); + + re = pcre_compile((const char *) rc->pattern.data, (int) rc->options, + &errstr, &erroff, NULL); + + /* ensure that there is no current pool */ + ngx_regex_malloc_done(); + + if (re == NULL) { + if ((size_t) erroff == rc->pattern.len) { + rc->err.len = ngx_snprintf(rc->err.data, rc->err.len, + "pcre_compile() failed: %s in \"%V\"", + errstr, &rc->pattern) + - rc->err.data; + + } else { + rc->err.len = ngx_snprintf(rc->err.data, rc->err.len, + "pcre_compile() failed: %s in \"%V\" at \"%s\"", + errstr, &rc->pattern, rc->pattern.data + erroff) + - rc->err.data; + } + + return NGX_ERROR; + } + + rc->regex = ngx_pcalloc(rc->pool, sizeof(ngx_regex_t)); + if (rc->regex == NULL) { + goto nomem; + } + + rc->regex->code = re; + + /* do not study at runtime */ + + if (ngx_pcre_studies != NULL) { + elt = ngx_list_push(ngx_pcre_studies); + if (elt == NULL) { + goto nomem; + } + + elt->regex = rc->regex; + elt->name = rc->pattern.data; + } + + n = pcre_fullinfo(re, NULL, PCRE_INFO_CAPTURECOUNT, &rc->captures); + if (n < 0) { + p = "pcre_fullinfo(\"%V\", PCRE_INFO_CAPTURECOUNT) failed: %d"; + goto failed; + } + + if (rc->captures == 0) { + return NGX_OK; + } + + n = pcre_fullinfo(re, NULL, PCRE_INFO_NAMECOUNT, &rc->named_captures); + if (n < 0) { + p = "pcre_fullinfo(\"%V\", PCRE_INFO_NAMECOUNT) failed: %d"; + goto failed; + } + + if (rc->named_captures == 0) { + return NGX_OK; + } + + n = pcre_fullinfo(re, NULL, PCRE_INFO_NAMEENTRYSIZE, &rc->name_size); + if (n < 0) { + p = "pcre_fullinfo(\"%V\", PCRE_INFO_NAMEENTRYSIZE) failed: %d"; + goto failed; + } + + n = pcre_fullinfo(re, NULL, PCRE_INFO_NAMETABLE, &rc->names); + if (n < 0) { + p = "pcre_fullinfo(\"%V\", PCRE_INFO_NAMETABLE) failed: %d"; + goto failed; + } + + return NGX_OK; + +failed: + + rc->err.len = ngx_snprintf(rc->err.data, rc->err.len, p, &rc->pattern, n) + - rc->err.data; + return NGX_ERROR; + +nomem: + + rc->err.len = ngx_snprintf(rc->err.data, rc->err.len, + "regex \"%V\" compilation failed: no memory", + &rc->pattern) + - rc->err.data; + return NGX_ERROR; +} + + +ngx_int_t +ngx_regex_exec_array(ngx_array_t *a, ngx_str_t *s, ngx_log_t *log) +{ + ngx_int_t n; + ngx_uint_t i; + ngx_regex_elt_t *re; + + re = a->elts; + + for (i = 0; i < a->nelts; i++) { + + n = ngx_regex_exec(re[i].regex, s, NULL, 0); + + if (n == NGX_REGEX_NO_MATCHED) { + continue; + } + + if (n < 0) { + ngx_log_error(NGX_LOG_ALERT, log, 0, + ngx_regex_exec_n " failed: %i on \"%V\" using \"%s\"", + n, s, re[i].name); + return NGX_ERROR; + } + + /* match */ + + return NGX_OK; + } + + return NGX_DECLINED; +} + + +static void * ngx_libc_cdecl +ngx_regex_malloc(size_t size) +{ + ngx_pool_t *pool; + pool = ngx_pcre_pool; + + if (pool) { + return ngx_palloc(pool, size); + } + + return NULL; +} + + +static void ngx_libc_cdecl +ngx_regex_free(void *p) +{ + return; +} + + +#if (NGX_HAVE_PCRE_JIT) + +static void +ngx_pcre_free_studies(void *data) +{ + ngx_list_t *studies = data; + + ngx_uint_t i; + ngx_list_part_t *part; + ngx_regex_elt_t *elts; + + part = &studies->part; + elts = part->elts; + + for (i = 0 ; /* void */ ; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + elts = part->elts; + i = 0; + } + + if (elts[i].regex->extra != NULL) { + pcre_free_study(elts[i].regex->extra); + } + } +} + +#endif + + +static ngx_int_t +ngx_regex_module_init(ngx_cycle_t *cycle) +{ + int opt; + const char *errstr; + ngx_uint_t i; + ngx_list_part_t *part; + ngx_regex_elt_t *elts; + + opt = 0; + +#if (NGX_HAVE_PCRE_JIT) + { + ngx_regex_conf_t *rcf; + ngx_pool_cleanup_t *cln; + + rcf = (ngx_regex_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_regex_module); + + if (rcf->pcre_jit) { + opt = PCRE_STUDY_JIT_COMPILE; + + /* + * The PCRE JIT compiler uses mmap for its executable codes, so we + * have to explicitly call the pcre_free_study() function to free + * this memory. + */ + + cln = ngx_pool_cleanup_add(cycle->pool, 0); + if (cln == NULL) { + return NGX_ERROR; + } + + cln->handler = ngx_pcre_free_studies; + cln->data = ngx_pcre_studies; + } + } +#endif + + ngx_regex_malloc_init(cycle->pool); + + part = &ngx_pcre_studies->part; + elts = part->elts; + + for (i = 0 ; /* void */ ; i++) { + + if (i >= part->nelts) { + if (part->next == NULL) { + break; + } + + part = part->next; + elts = part->elts; + i = 0; + } + + elts[i].regex->extra = pcre_study(elts[i].regex->code, opt, &errstr); + + if (errstr != NULL) { + ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, + "pcre_study() failed: %s in \"%s\"", + errstr, elts[i].name); + } + +#if (NGX_HAVE_PCRE_JIT) + if (opt & PCRE_STUDY_JIT_COMPILE) { + int jit, n; + + jit = 0; + n = pcre_fullinfo(elts[i].regex->code, elts[i].regex->extra, + PCRE_INFO_JIT, &jit); + + if (n != 0 || jit != 1) { + ngx_log_error(NGX_LOG_INFO, cycle->log, 0, + "JIT compiler does not support pattern: \"%s\"", + elts[i].name); + } + } +#endif + } + + ngx_regex_malloc_done(); + + ngx_pcre_studies = NULL; + + return NGX_OK; +} + + +static void * +ngx_regex_create_conf(ngx_cycle_t *cycle) +{ + ngx_regex_conf_t *rcf; + + rcf = ngx_pcalloc(cycle->pool, sizeof(ngx_regex_conf_t)); + if (rcf == NULL) { + return NULL; + } + + rcf->pcre_jit = NGX_CONF_UNSET; + + ngx_pcre_studies = ngx_list_create(cycle->pool, 8, sizeof(ngx_regex_elt_t)); + if (ngx_pcre_studies == NULL) { + return NULL; + } + + return rcf; +} + + +static char * +ngx_regex_init_conf(ngx_cycle_t *cycle, void *conf) +{ + ngx_regex_conf_t *rcf = conf; + + ngx_conf_init_value(rcf->pcre_jit, 0); + + return NGX_CONF_OK; +} + + +static char * +ngx_regex_pcre_jit(ngx_conf_t *cf, void *post, void *data) +{ + ngx_flag_t *fp = data; + + if (*fp == 0) { + return NGX_CONF_OK; + } + +#if (NGX_HAVE_PCRE_JIT) + { + int jit, r; + + jit = 0; + r = pcre_config(PCRE_CONFIG_JIT, &jit); + + if (r != 0 || jit != 1) { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "PCRE library does not support JIT"); + *fp = 0; + } + } +#else + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "nginx was built without PCRE JIT support"); + *fp = 0; +#endif + + return NGX_CONF_OK; +} diff --git a/app/nginx/src/core/ngx_regex.h b/app/nginx/src/core/ngx_regex.h new file mode 100644 index 0000000..680486c --- /dev/null +++ b/app/nginx/src/core/ngx_regex.h @@ -0,0 +1,60 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_REGEX_H_INCLUDED_ +#define _NGX_REGEX_H_INCLUDED_ + + +#include +#include + +#include + + +#define NGX_REGEX_NO_MATCHED PCRE_ERROR_NOMATCH /* -1 */ + +#define NGX_REGEX_CASELESS PCRE_CASELESS + + +typedef struct { + pcre *code; + pcre_extra *extra; +} ngx_regex_t; + + +typedef struct { + ngx_str_t pattern; + ngx_pool_t *pool; + ngx_int_t options; + + ngx_regex_t *regex; + int captures; + int named_captures; + int name_size; + u_char *names; + ngx_str_t err; +} ngx_regex_compile_t; + + +typedef struct { + ngx_regex_t *regex; + u_char *name; +} ngx_regex_elt_t; + + +void ngx_regex_init(void); +ngx_int_t ngx_regex_compile(ngx_regex_compile_t *rc); + +#define ngx_regex_exec(re, s, captures, size) \ + pcre_exec(re->code, re->extra, (const char *) (s)->data, (s)->len, 0, 0, \ + captures, size) +#define ngx_regex_exec_n "pcre_exec()" + +ngx_int_t ngx_regex_exec_array(ngx_array_t *a, ngx_str_t *s, ngx_log_t *log); + + +#endif /* _NGX_REGEX_H_INCLUDED_ */ diff --git a/app/nginx/src/core/ngx_resolver.c b/app/nginx/src/core/ngx_resolver.c new file mode 100644 index 0000000..e140ab6 --- /dev/null +++ b/app/nginx/src/core/ngx_resolver.c @@ -0,0 +1,4662 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +#define NGX_RESOLVER_UDP_SIZE 4096 + +#define NGX_RESOLVER_TCP_RSIZE (2 + 65535) +#define NGX_RESOLVER_TCP_WSIZE 8192 + + +typedef struct { + u_char ident_hi; + u_char ident_lo; + u_char flags_hi; + u_char flags_lo; + u_char nqs_hi; + u_char nqs_lo; + u_char nan_hi; + u_char nan_lo; + u_char nns_hi; + u_char nns_lo; + u_char nar_hi; + u_char nar_lo; +} ngx_resolver_hdr_t; + + +typedef struct { + u_char type_hi; + u_char type_lo; + u_char class_hi; + u_char class_lo; +} ngx_resolver_qs_t; + + +typedef struct { + u_char type_hi; + u_char type_lo; + u_char class_hi; + u_char class_lo; + u_char ttl[4]; + u_char len_hi; + u_char len_lo; +} ngx_resolver_an_t; + + +#define ngx_resolver_node(n) \ + (ngx_resolver_node_t *) \ + ((u_char *) (n) - offsetof(ngx_resolver_node_t, node)) + + +static ngx_int_t ngx_udp_connect(ngx_resolver_connection_t *rec); +static ngx_int_t ngx_tcp_connect(ngx_resolver_connection_t *rec); + + +static void ngx_resolver_cleanup(void *data); +static void ngx_resolver_cleanup_tree(ngx_resolver_t *r, ngx_rbtree_t *tree); +static ngx_int_t ngx_resolve_name_locked(ngx_resolver_t *r, + ngx_resolver_ctx_t *ctx, ngx_str_t *name); +static void ngx_resolver_expire(ngx_resolver_t *r, ngx_rbtree_t *tree, + ngx_queue_t *queue); +static ngx_int_t ngx_resolver_send_query(ngx_resolver_t *r, + ngx_resolver_node_t *rn); +static ngx_int_t ngx_resolver_send_udp_query(ngx_resolver_t *r, + ngx_resolver_connection_t *rec, u_char *query, u_short qlen); +static ngx_int_t ngx_resolver_send_tcp_query(ngx_resolver_t *r, + ngx_resolver_connection_t *rec, u_char *query, u_short qlen); +static ngx_int_t ngx_resolver_create_name_query(ngx_resolver_t *r, + ngx_resolver_node_t *rn, ngx_str_t *name); +static ngx_int_t ngx_resolver_create_srv_query(ngx_resolver_t *r, + ngx_resolver_node_t *rn, ngx_str_t *name); +static ngx_int_t ngx_resolver_create_addr_query(ngx_resolver_t *r, + ngx_resolver_node_t *rn, ngx_resolver_addr_t *addr); +static void ngx_resolver_resend_handler(ngx_event_t *ev); +static time_t ngx_resolver_resend(ngx_resolver_t *r, ngx_rbtree_t *tree, + ngx_queue_t *queue); +static ngx_uint_t ngx_resolver_resend_empty(ngx_resolver_t *r); +static void ngx_resolver_udp_read(ngx_event_t *rev); +static void ngx_resolver_tcp_write(ngx_event_t *wev); +static void ngx_resolver_tcp_read(ngx_event_t *rev); +static void ngx_resolver_process_response(ngx_resolver_t *r, u_char *buf, + size_t n, ngx_uint_t tcp); +static void ngx_resolver_process_a(ngx_resolver_t *r, u_char *buf, size_t n, + ngx_uint_t ident, ngx_uint_t code, ngx_uint_t qtype, + ngx_uint_t nan, ngx_uint_t trunc, ngx_uint_t ans); +static void ngx_resolver_process_srv(ngx_resolver_t *r, u_char *buf, size_t n, + ngx_uint_t ident, ngx_uint_t code, ngx_uint_t nan, + ngx_uint_t trunc, ngx_uint_t ans); +static void ngx_resolver_process_ptr(ngx_resolver_t *r, u_char *buf, size_t n, + ngx_uint_t ident, ngx_uint_t code, ngx_uint_t nan); +static ngx_resolver_node_t *ngx_resolver_lookup_name(ngx_resolver_t *r, + ngx_str_t *name, uint32_t hash); +static ngx_resolver_node_t *ngx_resolver_lookup_srv(ngx_resolver_t *r, + ngx_str_t *name, uint32_t hash); +static ngx_resolver_node_t *ngx_resolver_lookup_addr(ngx_resolver_t *r, + in_addr_t addr); +static void ngx_resolver_rbtree_insert_value(ngx_rbtree_node_t *temp, + ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel); +static ngx_int_t ngx_resolver_copy(ngx_resolver_t *r, ngx_str_t *name, + u_char *buf, u_char *src, u_char *last); +static void ngx_resolver_timeout_handler(ngx_event_t *ev); +static void ngx_resolver_free_node(ngx_resolver_t *r, ngx_resolver_node_t *rn); +static void *ngx_resolver_alloc(ngx_resolver_t *r, size_t size); +static void *ngx_resolver_calloc(ngx_resolver_t *r, size_t size); +static void ngx_resolver_free(ngx_resolver_t *r, void *p); +static void ngx_resolver_free_locked(ngx_resolver_t *r, void *p); +static void *ngx_resolver_dup(ngx_resolver_t *r, void *src, size_t size); +static ngx_resolver_addr_t *ngx_resolver_export(ngx_resolver_t *r, + ngx_resolver_node_t *rn, ngx_uint_t rotate); +static void ngx_resolver_report_srv(ngx_resolver_t *r, ngx_resolver_ctx_t *ctx); +static u_char *ngx_resolver_log_error(ngx_log_t *log, u_char *buf, size_t len); +static void ngx_resolver_resolve_srv_names(ngx_resolver_ctx_t *ctx, + ngx_resolver_node_t *rn); +static void ngx_resolver_srv_names_handler(ngx_resolver_ctx_t *ctx); +static ngx_int_t ngx_resolver_cmp_srvs(const void *one, const void *two); + +#if (NGX_HAVE_INET6) +static void ngx_resolver_rbtree_insert_addr6_value(ngx_rbtree_node_t *temp, + ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel); +static ngx_resolver_node_t *ngx_resolver_lookup_addr6(ngx_resolver_t *r, + struct in6_addr *addr, uint32_t hash); +#endif + + +ngx_resolver_t * +ngx_resolver_create(ngx_conf_t *cf, ngx_str_t *names, ngx_uint_t n) +{ + ngx_str_t s; + ngx_url_t u; + ngx_uint_t i, j; + ngx_resolver_t *r; + ngx_pool_cleanup_t *cln; + ngx_resolver_connection_t *rec; + + cln = ngx_pool_cleanup_add(cf->pool, 0); + if (cln == NULL) { + return NULL; + } + + cln->handler = ngx_resolver_cleanup; + + r = ngx_calloc(sizeof(ngx_resolver_t), cf->log); + if (r == NULL) { + return NULL; + } + + cln->data = r; + + r->event = ngx_calloc(sizeof(ngx_event_t), cf->log); + if (r->event == NULL) { + return NULL; + } + + ngx_rbtree_init(&r->name_rbtree, &r->name_sentinel, + ngx_resolver_rbtree_insert_value); + + ngx_rbtree_init(&r->srv_rbtree, &r->srv_sentinel, + ngx_resolver_rbtree_insert_value); + + ngx_rbtree_init(&r->addr_rbtree, &r->addr_sentinel, + ngx_rbtree_insert_value); + + ngx_queue_init(&r->name_resend_queue); + ngx_queue_init(&r->srv_resend_queue); + ngx_queue_init(&r->addr_resend_queue); + + ngx_queue_init(&r->name_expire_queue); + ngx_queue_init(&r->srv_expire_queue); + ngx_queue_init(&r->addr_expire_queue); + +#if (NGX_HAVE_INET6) + r->ipv6 = 1; + + ngx_rbtree_init(&r->addr6_rbtree, &r->addr6_sentinel, + ngx_resolver_rbtree_insert_addr6_value); + + ngx_queue_init(&r->addr6_resend_queue); + + ngx_queue_init(&r->addr6_expire_queue); +#endif + + r->event->handler = ngx_resolver_resend_handler; + r->event->data = r; + r->event->log = &cf->cycle->new_log; + r->ident = -1; + + r->resend_timeout = 5; + r->tcp_timeout = 5; + r->expire = 30; + r->valid = 0; + + r->log = &cf->cycle->new_log; + r->log_level = NGX_LOG_ERR; + + if (n) { + if (ngx_array_init(&r->connections, cf->pool, n, + sizeof(ngx_resolver_connection_t)) + != NGX_OK) + { + return NULL; + } + } + + for (i = 0; i < n; i++) { + if (ngx_strncmp(names[i].data, "valid=", 6) == 0) { + s.len = names[i].len - 6; + s.data = names[i].data + 6; + + r->valid = ngx_parse_time(&s, 1); + + if (r->valid == (time_t) NGX_ERROR) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid parameter: %V", &names[i]); + return NULL; + } + + continue; + } + +#if (NGX_HAVE_INET6) + if (ngx_strncmp(names[i].data, "ipv6=", 5) == 0) { + + if (ngx_strcmp(&names[i].data[5], "on") == 0) { + r->ipv6 = 1; + + } else if (ngx_strcmp(&names[i].data[5], "off") == 0) { + r->ipv6 = 0; + + } else { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid parameter: %V", &names[i]); + return NULL; + } + + continue; + } +#endif + + ngx_memzero(&u, sizeof(ngx_url_t)); + + u.url = names[i]; + u.default_port = 53; + + if (ngx_parse_url(cf->pool, &u) != NGX_OK) { + if (u.err) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "%s in resolver \"%V\"", + u.err, &u.url); + } + + return NULL; + } + + rec = ngx_array_push_n(&r->connections, u.naddrs); + if (rec == NULL) { + return NULL; + } + + ngx_memzero(rec, u.naddrs * sizeof(ngx_resolver_connection_t)); + + for (j = 0; j < u.naddrs; j++) { + rec[j].sockaddr = u.addrs[j].sockaddr; + rec[j].socklen = u.addrs[j].socklen; + rec[j].server = u.addrs[j].name; + rec[j].resolver = r; + } + } + + return r; +} + + +static void +ngx_resolver_cleanup(void *data) +{ + ngx_resolver_t *r = data; + + ngx_uint_t i; + ngx_resolver_connection_t *rec; + + if (r) { + ngx_log_debug0(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0, + "cleanup resolver"); + + ngx_resolver_cleanup_tree(r, &r->name_rbtree); + + ngx_resolver_cleanup_tree(r, &r->srv_rbtree); + + ngx_resolver_cleanup_tree(r, &r->addr_rbtree); + +#if (NGX_HAVE_INET6) + ngx_resolver_cleanup_tree(r, &r->addr6_rbtree); +#endif + + if (r->event) { + if (r->event->timer_set) { + ngx_del_timer(r->event); + } + + ngx_free(r->event); + } + + + rec = r->connections.elts; + + for (i = 0; i < r->connections.nelts; i++) { + if (rec[i].udp) { + ngx_close_connection(rec[i].udp); + } + + if (rec[i].tcp) { + ngx_close_connection(rec[i].tcp); + } + + if (rec[i].read_buf) { + ngx_resolver_free(r, rec[i].read_buf->start); + ngx_resolver_free(r, rec[i].read_buf); + } + + if (rec[i].write_buf) { + ngx_resolver_free(r, rec[i].write_buf->start); + ngx_resolver_free(r, rec[i].write_buf); + } + } + + ngx_free(r); + } +} + + +static void +ngx_resolver_cleanup_tree(ngx_resolver_t *r, ngx_rbtree_t *tree) +{ + ngx_resolver_ctx_t *ctx, *next; + ngx_resolver_node_t *rn; + + while (tree->root != tree->sentinel) { + + rn = ngx_resolver_node(ngx_rbtree_min(tree->root, tree->sentinel)); + + ngx_queue_remove(&rn->queue); + + for (ctx = rn->waiting; ctx; ctx = next) { + next = ctx->next; + + if (ctx->event) { + if (ctx->event->timer_set) { + ngx_del_timer(ctx->event); + } + + ngx_resolver_free(r, ctx->event); + } + + ngx_resolver_free(r, ctx); + } + + ngx_rbtree_delete(tree, &rn->node); + + ngx_resolver_free_node(r, rn); + } +} + + +ngx_resolver_ctx_t * +ngx_resolve_start(ngx_resolver_t *r, ngx_resolver_ctx_t *temp) +{ + in_addr_t addr; + ngx_resolver_ctx_t *ctx; + + if (temp) { + addr = ngx_inet_addr(temp->name.data, temp->name.len); + + if (addr != INADDR_NONE) { + temp->resolver = r; + temp->state = NGX_OK; + temp->naddrs = 1; + temp->addrs = &temp->addr; + temp->addr.sockaddr = (struct sockaddr *) &temp->sin; + temp->addr.socklen = sizeof(struct sockaddr_in); + ngx_memzero(&temp->sin, sizeof(struct sockaddr_in)); + temp->sin.sin_family = AF_INET; + temp->sin.sin_addr.s_addr = addr; + temp->quick = 1; + + return temp; + } + } + + if (r->connections.nelts == 0) { + return NGX_NO_RESOLVER; + } + + ctx = ngx_resolver_calloc(r, sizeof(ngx_resolver_ctx_t)); + + if (ctx) { + ctx->resolver = r; + } + + return ctx; +} + + +ngx_int_t +ngx_resolve_name(ngx_resolver_ctx_t *ctx) +{ + size_t slen; + ngx_int_t rc; + ngx_str_t name; + ngx_resolver_t *r; + + r = ctx->resolver; + + if (ctx->name.len > 0 && ctx->name.data[ctx->name.len - 1] == '.') { + ctx->name.len--; + } + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, r->log, 0, + "resolve: \"%V\"", &ctx->name); + + if (ctx->quick) { + ctx->handler(ctx); + return NGX_OK; + } + + if (ctx->service.len) { + slen = ctx->service.len; + + if (ngx_strlchr(ctx->service.data, + ctx->service.data + ctx->service.len, '.') + == NULL) + { + slen += sizeof("_._tcp") - 1; + } + + name.len = slen + 1 + ctx->name.len; + + name.data = ngx_resolver_alloc(r, name.len); + if (name.data == NULL) { + return NGX_ERROR; + } + + if (slen == ctx->service.len) { + ngx_sprintf(name.data, "%V.%V", &ctx->service, &ctx->name); + + } else { + ngx_sprintf(name.data, "_%V._tcp.%V", &ctx->service, &ctx->name); + } + + /* lock name mutex */ + + rc = ngx_resolve_name_locked(r, ctx, &name); + + ngx_resolver_free(r, name.data); + + } else { + /* lock name mutex */ + + rc = ngx_resolve_name_locked(r, ctx, &ctx->name); + } + + if (rc == NGX_OK) { + return NGX_OK; + } + + /* unlock name mutex */ + + if (rc == NGX_AGAIN) { + return NGX_OK; + } + + /* NGX_ERROR */ + + if (ctx->event) { + ngx_resolver_free(r, ctx->event); + } + + ngx_resolver_free(r, ctx); + + return NGX_ERROR; +} + + +void +ngx_resolve_name_done(ngx_resolver_ctx_t *ctx) +{ + ngx_uint_t i; + ngx_resolver_t *r; + ngx_resolver_ctx_t *w, **p; + ngx_resolver_node_t *rn; + + r = ctx->resolver; + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, r->log, 0, + "resolve name done: %i", ctx->state); + + if (ctx->quick) { + return; + } + + if (ctx->event && ctx->event->timer_set) { + ngx_del_timer(ctx->event); + } + + /* lock name mutex */ + + if (ctx->nsrvs) { + for (i = 0; i < ctx->nsrvs; i++) { + if (ctx->srvs[i].ctx) { + ngx_resolve_name_done(ctx->srvs[i].ctx); + } + + if (ctx->srvs[i].addrs) { + ngx_resolver_free(r, ctx->srvs[i].addrs->sockaddr); + ngx_resolver_free(r, ctx->srvs[i].addrs); + } + + ngx_resolver_free(r, ctx->srvs[i].name.data); + } + + ngx_resolver_free(r, ctx->srvs); + } + + if (ctx->state == NGX_AGAIN || ctx->state == NGX_RESOLVE_TIMEDOUT) { + + rn = ctx->node; + + if (rn) { + p = &rn->waiting; + w = rn->waiting; + + while (w) { + if (w == ctx) { + *p = w->next; + + goto done; + } + + p = &w->next; + w = w->next; + } + + ngx_log_error(NGX_LOG_ALERT, r->log, 0, + "could not cancel %V resolving", &ctx->name); + } + } + +done: + + if (ctx->service.len) { + ngx_resolver_expire(r, &r->srv_rbtree, &r->srv_expire_queue); + + } else { + ngx_resolver_expire(r, &r->name_rbtree, &r->name_expire_queue); + } + + /* unlock name mutex */ + + /* lock alloc mutex */ + + if (ctx->event) { + ngx_resolver_free_locked(r, ctx->event); + } + + ngx_resolver_free_locked(r, ctx); + + /* unlock alloc mutex */ + + if (r->event->timer_set && ngx_resolver_resend_empty(r)) { + ngx_del_timer(r->event); + } +} + + +static ngx_int_t +ngx_resolve_name_locked(ngx_resolver_t *r, ngx_resolver_ctx_t *ctx, + ngx_str_t *name) +{ + uint32_t hash; + ngx_int_t rc; + ngx_str_t cname; + ngx_uint_t i, naddrs; + ngx_queue_t *resend_queue, *expire_queue; + ngx_rbtree_t *tree; + ngx_resolver_ctx_t *next, *last; + ngx_resolver_addr_t *addrs; + ngx_resolver_node_t *rn; + + ngx_strlow(name->data, name->data, name->len); + + hash = ngx_crc32_short(name->data, name->len); + + if (ctx->service.len) { + rn = ngx_resolver_lookup_srv(r, name, hash); + + tree = &r->srv_rbtree; + resend_queue = &r->srv_resend_queue; + expire_queue = &r->srv_expire_queue; + + } else { + rn = ngx_resolver_lookup_name(r, name, hash); + + tree = &r->name_rbtree; + resend_queue = &r->name_resend_queue; + expire_queue = &r->name_expire_queue; + } + + if (rn) { + + /* ctx can be a list after NGX_RESOLVE_CNAME */ + for (last = ctx; last->next; last = last->next); + + if (rn->valid >= ngx_time()) { + + ngx_log_debug0(NGX_LOG_DEBUG_CORE, r->log, 0, "resolve cached"); + + ngx_queue_remove(&rn->queue); + + rn->expire = ngx_time() + r->expire; + + ngx_queue_insert_head(expire_queue, &rn->queue); + + naddrs = (rn->naddrs == (u_short) -1) ? 0 : rn->naddrs; +#if (NGX_HAVE_INET6) + naddrs += (rn->naddrs6 == (u_short) -1) ? 0 : rn->naddrs6; +#endif + + if (naddrs) { + + if (naddrs == 1 && rn->naddrs == 1) { + addrs = NULL; + + } else { + addrs = ngx_resolver_export(r, rn, 1); + if (addrs == NULL) { + return NGX_ERROR; + } + } + + last->next = rn->waiting; + rn->waiting = NULL; + + /* unlock name mutex */ + + do { + ctx->state = NGX_OK; + ctx->valid = rn->valid; + ctx->naddrs = naddrs; + + if (addrs == NULL) { + ctx->addrs = &ctx->addr; + ctx->addr.sockaddr = (struct sockaddr *) &ctx->sin; + ctx->addr.socklen = sizeof(struct sockaddr_in); + ngx_memzero(&ctx->sin, sizeof(struct sockaddr_in)); + ctx->sin.sin_family = AF_INET; + ctx->sin.sin_addr.s_addr = rn->u.addr; + + } else { + ctx->addrs = addrs; + } + + next = ctx->next; + + ctx->handler(ctx); + + ctx = next; + } while (ctx); + + if (addrs != NULL) { + ngx_resolver_free(r, addrs->sockaddr); + ngx_resolver_free(r, addrs); + } + + return NGX_OK; + } + + if (rn->nsrvs) { + last->next = rn->waiting; + rn->waiting = NULL; + + /* unlock name mutex */ + + do { + next = ctx->next; + + ngx_resolver_resolve_srv_names(ctx, rn); + + ctx = next; + } while (ctx); + + return NGX_OK; + } + + /* NGX_RESOLVE_CNAME */ + + if (ctx->recursion++ < NGX_RESOLVER_MAX_RECURSION) { + + cname.len = rn->cnlen; + cname.data = rn->u.cname; + + return ngx_resolve_name_locked(r, ctx, &cname); + } + + last->next = rn->waiting; + rn->waiting = NULL; + + /* unlock name mutex */ + + do { + ctx->state = NGX_RESOLVE_NXDOMAIN; + ctx->valid = ngx_time() + (r->valid ? r->valid : 10); + next = ctx->next; + + ctx->handler(ctx); + + ctx = next; + } while (ctx); + + return NGX_OK; + } + + if (rn->waiting) { + + if (ctx->event == NULL && ctx->timeout) { + ctx->event = ngx_resolver_calloc(r, sizeof(ngx_event_t)); + if (ctx->event == NULL) { + return NGX_ERROR; + } + + ctx->event->handler = ngx_resolver_timeout_handler; + ctx->event->data = ctx; + ctx->event->log = r->log; + ctx->ident = -1; + + ngx_add_timer(ctx->event, ctx->timeout); + } + + last->next = rn->waiting; + rn->waiting = ctx; + ctx->state = NGX_AGAIN; + + do { + ctx->node = rn; + ctx = ctx->next; + } while (ctx); + + return NGX_AGAIN; + } + + ngx_queue_remove(&rn->queue); + + /* lock alloc mutex */ + + if (rn->query) { + ngx_resolver_free_locked(r, rn->query); + rn->query = NULL; +#if (NGX_HAVE_INET6) + rn->query6 = NULL; +#endif + } + + if (rn->cnlen) { + ngx_resolver_free_locked(r, rn->u.cname); + } + + if (rn->naddrs > 1 && rn->naddrs != (u_short) -1) { + ngx_resolver_free_locked(r, rn->u.addrs); + } + +#if (NGX_HAVE_INET6) + if (rn->naddrs6 > 1 && rn->naddrs6 != (u_short) -1) { + ngx_resolver_free_locked(r, rn->u6.addrs6); + } +#endif + + if (rn->nsrvs) { + for (i = 0; i < (ngx_uint_t) rn->nsrvs; i++) { + if (rn->u.srvs[i].name.data) { + ngx_resolver_free_locked(r, rn->u.srvs[i].name.data); + } + } + + ngx_resolver_free_locked(r, rn->u.srvs); + } + + /* unlock alloc mutex */ + + } else { + + rn = ngx_resolver_alloc(r, sizeof(ngx_resolver_node_t)); + if (rn == NULL) { + return NGX_ERROR; + } + + rn->name = ngx_resolver_dup(r, name->data, name->len); + if (rn->name == NULL) { + ngx_resolver_free(r, rn); + return NGX_ERROR; + } + + rn->node.key = hash; + rn->nlen = (u_short) name->len; + rn->query = NULL; +#if (NGX_HAVE_INET6) + rn->query6 = NULL; +#endif + + ngx_rbtree_insert(tree, &rn->node); + } + + if (ctx->service.len) { + rc = ngx_resolver_create_srv_query(r, rn, name); + + } else { + rc = ngx_resolver_create_name_query(r, rn, name); + } + + if (rc == NGX_ERROR) { + goto failed; + } + + if (rc == NGX_DECLINED) { + ngx_rbtree_delete(tree, &rn->node); + + ngx_resolver_free(r, rn->query); + ngx_resolver_free(r, rn->name); + ngx_resolver_free(r, rn); + + do { + ctx->state = NGX_RESOLVE_NXDOMAIN; + next = ctx->next; + + ctx->handler(ctx); + + ctx = next; + } while (ctx); + + return NGX_OK; + } + + rn->last_connection = r->last_connection++; + if (r->last_connection == r->connections.nelts) { + r->last_connection = 0; + } + + rn->naddrs = (u_short) -1; + rn->tcp = 0; +#if (NGX_HAVE_INET6) + rn->naddrs6 = r->ipv6 ? (u_short) -1 : 0; + rn->tcp6 = 0; +#endif + rn->nsrvs = 0; + + if (ngx_resolver_send_query(r, rn) != NGX_OK) { + goto failed; + } + + if (ctx->event == NULL && ctx->timeout) { + ctx->event = ngx_resolver_calloc(r, sizeof(ngx_event_t)); + if (ctx->event == NULL) { + goto failed; + } + + ctx->event->handler = ngx_resolver_timeout_handler; + ctx->event->data = ctx; + ctx->event->log = r->log; + ctx->ident = -1; + + ngx_add_timer(ctx->event, ctx->timeout); + } + + if (ngx_resolver_resend_empty(r)) { + ngx_add_timer(r->event, (ngx_msec_t) (r->resend_timeout * 1000)); + } + + rn->expire = ngx_time() + r->resend_timeout; + + ngx_queue_insert_head(resend_queue, &rn->queue); + + rn->code = 0; + rn->cnlen = 0; + rn->valid = 0; + rn->ttl = NGX_MAX_UINT32_VALUE; + rn->waiting = ctx; + + ctx->state = NGX_AGAIN; + + do { + ctx->node = rn; + ctx = ctx->next; + } while (ctx); + + return NGX_AGAIN; + +failed: + + ngx_rbtree_delete(tree, &rn->node); + + if (rn->query) { + ngx_resolver_free(r, rn->query); + } + + ngx_resolver_free(r, rn->name); + + ngx_resolver_free(r, rn); + + return NGX_ERROR; +} + + +ngx_int_t +ngx_resolve_addr(ngx_resolver_ctx_t *ctx) +{ + u_char *name; + in_addr_t addr; + ngx_queue_t *resend_queue, *expire_queue; + ngx_rbtree_t *tree; + ngx_resolver_t *r; + struct sockaddr_in *sin; + ngx_resolver_node_t *rn; +#if (NGX_HAVE_INET6) + uint32_t hash; + struct sockaddr_in6 *sin6; +#endif + +#if (NGX_SUPPRESS_WARN) + addr = 0; +#if (NGX_HAVE_INET6) + hash = 0; + sin6 = NULL; +#endif +#endif + + r = ctx->resolver; + + switch (ctx->addr.sockaddr->sa_family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + sin6 = (struct sockaddr_in6 *) ctx->addr.sockaddr; + hash = ngx_crc32_short(sin6->sin6_addr.s6_addr, 16); + + /* lock addr mutex */ + + rn = ngx_resolver_lookup_addr6(r, &sin6->sin6_addr, hash); + + tree = &r->addr6_rbtree; + resend_queue = &r->addr6_resend_queue; + expire_queue = &r->addr6_expire_queue; + + break; +#endif + + default: /* AF_INET */ + sin = (struct sockaddr_in *) ctx->addr.sockaddr; + addr = ntohl(sin->sin_addr.s_addr); + + /* lock addr mutex */ + + rn = ngx_resolver_lookup_addr(r, addr); + + tree = &r->addr_rbtree; + resend_queue = &r->addr_resend_queue; + expire_queue = &r->addr_expire_queue; + } + + if (rn) { + + if (rn->valid >= ngx_time()) { + + ngx_log_debug0(NGX_LOG_DEBUG_CORE, r->log, 0, "resolve cached"); + + ngx_queue_remove(&rn->queue); + + rn->expire = ngx_time() + r->expire; + + ngx_queue_insert_head(expire_queue, &rn->queue); + + name = ngx_resolver_dup(r, rn->name, rn->nlen); + if (name == NULL) { + goto failed; + } + + ctx->name.len = rn->nlen; + ctx->name.data = name; + + /* unlock addr mutex */ + + ctx->state = NGX_OK; + ctx->valid = rn->valid; + + ctx->handler(ctx); + + ngx_resolver_free(r, name); + + return NGX_OK; + } + + if (rn->waiting) { + + if (ctx->event == NULL && ctx->timeout) { + ctx->event = ngx_resolver_calloc(r, sizeof(ngx_event_t)); + if (ctx->event == NULL) { + return NGX_ERROR; + } + + ctx->event->handler = ngx_resolver_timeout_handler; + ctx->event->data = ctx; + ctx->event->log = r->log; + ctx->ident = -1; + + ngx_add_timer(ctx->event, ctx->timeout); + } + + ctx->next = rn->waiting; + rn->waiting = ctx; + ctx->state = NGX_AGAIN; + ctx->node = rn; + + /* unlock addr mutex */ + + return NGX_OK; + } + + ngx_queue_remove(&rn->queue); + + ngx_resolver_free(r, rn->query); + rn->query = NULL; +#if (NGX_HAVE_INET6) + rn->query6 = NULL; +#endif + + } else { + rn = ngx_resolver_alloc(r, sizeof(ngx_resolver_node_t)); + if (rn == NULL) { + goto failed; + } + + switch (ctx->addr.sockaddr->sa_family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + rn->addr6 = sin6->sin6_addr; + rn->node.key = hash; + break; +#endif + + default: /* AF_INET */ + rn->node.key = addr; + } + + rn->query = NULL; +#if (NGX_HAVE_INET6) + rn->query6 = NULL; +#endif + + ngx_rbtree_insert(tree, &rn->node); + } + + if (ngx_resolver_create_addr_query(r, rn, &ctx->addr) != NGX_OK) { + goto failed; + } + + rn->last_connection = r->last_connection++; + if (r->last_connection == r->connections.nelts) { + r->last_connection = 0; + } + + rn->naddrs = (u_short) -1; + rn->tcp = 0; +#if (NGX_HAVE_INET6) + rn->naddrs6 = (u_short) -1; + rn->tcp6 = 0; +#endif + rn->nsrvs = 0; + + if (ngx_resolver_send_query(r, rn) != NGX_OK) { + goto failed; + } + + if (ctx->event == NULL && ctx->timeout) { + ctx->event = ngx_resolver_calloc(r, sizeof(ngx_event_t)); + if (ctx->event == NULL) { + goto failed; + } + + ctx->event->handler = ngx_resolver_timeout_handler; + ctx->event->data = ctx; + ctx->event->log = r->log; + ctx->ident = -1; + + ngx_add_timer(ctx->event, ctx->timeout); + } + + if (ngx_resolver_resend_empty(r)) { + ngx_add_timer(r->event, (ngx_msec_t) (r->resend_timeout * 1000)); + } + + rn->expire = ngx_time() + r->resend_timeout; + + ngx_queue_insert_head(resend_queue, &rn->queue); + + rn->code = 0; + rn->cnlen = 0; + rn->name = NULL; + rn->nlen = 0; + rn->valid = 0; + rn->ttl = NGX_MAX_UINT32_VALUE; + rn->waiting = ctx; + + /* unlock addr mutex */ + + ctx->state = NGX_AGAIN; + ctx->node = rn; + + return NGX_OK; + +failed: + + if (rn) { + ngx_rbtree_delete(tree, &rn->node); + + if (rn->query) { + ngx_resolver_free(r, rn->query); + } + + ngx_resolver_free(r, rn); + } + + /* unlock addr mutex */ + + if (ctx->event) { + ngx_resolver_free(r, ctx->event); + } + + ngx_resolver_free(r, ctx); + + return NGX_ERROR; +} + + +void +ngx_resolve_addr_done(ngx_resolver_ctx_t *ctx) +{ + ngx_queue_t *expire_queue; + ngx_rbtree_t *tree; + ngx_resolver_t *r; + ngx_resolver_ctx_t *w, **p; + ngx_resolver_node_t *rn; + + r = ctx->resolver; + + switch (ctx->addr.sockaddr->sa_family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + tree = &r->addr6_rbtree; + expire_queue = &r->addr6_expire_queue; + break; +#endif + + default: /* AF_INET */ + tree = &r->addr_rbtree; + expire_queue = &r->addr_expire_queue; + } + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, r->log, 0, + "resolve addr done: %i", ctx->state); + + if (ctx->event && ctx->event->timer_set) { + ngx_del_timer(ctx->event); + } + + /* lock addr mutex */ + + if (ctx->state == NGX_AGAIN || ctx->state == NGX_RESOLVE_TIMEDOUT) { + + rn = ctx->node; + + if (rn) { + p = &rn->waiting; + w = rn->waiting; + + while (w) { + if (w == ctx) { + *p = w->next; + + goto done; + } + + p = &w->next; + w = w->next; + } + } + + { + u_char text[NGX_SOCKADDR_STRLEN]; + ngx_str_t addrtext; + + addrtext.data = text; + addrtext.len = ngx_sock_ntop(ctx->addr.sockaddr, ctx->addr.socklen, + text, NGX_SOCKADDR_STRLEN, 0); + + ngx_log_error(NGX_LOG_ALERT, r->log, 0, + "could not cancel %V resolving", &addrtext); + } + } + +done: + + ngx_resolver_expire(r, tree, expire_queue); + + /* unlock addr mutex */ + + /* lock alloc mutex */ + + if (ctx->event) { + ngx_resolver_free_locked(r, ctx->event); + } + + ngx_resolver_free_locked(r, ctx); + + /* unlock alloc mutex */ + + if (r->event->timer_set && ngx_resolver_resend_empty(r)) { + ngx_del_timer(r->event); + } +} + + +static void +ngx_resolver_expire(ngx_resolver_t *r, ngx_rbtree_t *tree, ngx_queue_t *queue) +{ + time_t now; + ngx_uint_t i; + ngx_queue_t *q; + ngx_resolver_node_t *rn; + + ngx_log_debug0(NGX_LOG_DEBUG_CORE, r->log, 0, "resolver expire"); + + now = ngx_time(); + + for (i = 0; i < 2; i++) { + if (ngx_queue_empty(queue)) { + return; + } + + q = ngx_queue_last(queue); + + rn = ngx_queue_data(q, ngx_resolver_node_t, queue); + + if (now <= rn->expire) { + return; + } + + ngx_log_debug2(NGX_LOG_DEBUG_CORE, r->log, 0, + "resolver expire \"%*s\"", (size_t) rn->nlen, rn->name); + + ngx_queue_remove(q); + + ngx_rbtree_delete(tree, &rn->node); + + ngx_resolver_free_node(r, rn); + } +} + + +static ngx_int_t +ngx_resolver_send_query(ngx_resolver_t *r, ngx_resolver_node_t *rn) +{ + ngx_int_t rc; + ngx_resolver_connection_t *rec; + + rec = r->connections.elts; + rec = &rec[rn->last_connection]; + + if (rec->log.handler == NULL) { + rec->log = *r->log; + rec->log.handler = ngx_resolver_log_error; + rec->log.data = rec; + rec->log.action = "resolving"; + } + + if (rn->naddrs == (u_short) -1) { + rc = rn->tcp ? ngx_resolver_send_tcp_query(r, rec, rn->query, rn->qlen) + : ngx_resolver_send_udp_query(r, rec, rn->query, rn->qlen); + + if (rc != NGX_OK) { + return rc; + } + } + +#if (NGX_HAVE_INET6) + + if (rn->query6 && rn->naddrs6 == (u_short) -1) { + rc = rn->tcp6 + ? ngx_resolver_send_tcp_query(r, rec, rn->query6, rn->qlen) + : ngx_resolver_send_udp_query(r, rec, rn->query6, rn->qlen); + + if (rc != NGX_OK) { + return rc; + } + } + +#endif + + return NGX_OK; +} + + +static ngx_int_t +ngx_resolver_send_udp_query(ngx_resolver_t *r, ngx_resolver_connection_t *rec, + u_char *query, u_short qlen) +{ + ssize_t n; + + if (rec->udp == NULL) { + if (ngx_udp_connect(rec) != NGX_OK) { + return NGX_ERROR; + } + + rec->udp->data = rec; + rec->udp->read->handler = ngx_resolver_udp_read; + rec->udp->read->resolver = 1; + } + + n = ngx_send(rec->udp, query, qlen); + + if (n == -1) { + return NGX_ERROR; + } + + if ((size_t) n != (size_t) qlen) { + ngx_log_error(NGX_LOG_CRIT, &rec->log, 0, "send() incomplete"); + return NGX_ERROR; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_resolver_send_tcp_query(ngx_resolver_t *r, ngx_resolver_connection_t *rec, + u_char *query, u_short qlen) +{ + ngx_buf_t *b; + ngx_int_t rc; + + rc = NGX_OK; + + if (rec->tcp == NULL) { + b = rec->read_buf; + + if (b == NULL) { + b = ngx_resolver_calloc(r, sizeof(ngx_buf_t)); + if (b == NULL) { + return NGX_ERROR; + } + + b->start = ngx_resolver_alloc(r, NGX_RESOLVER_TCP_RSIZE); + if (b->start == NULL) { + ngx_resolver_free(r, b); + return NGX_ERROR; + } + + b->end = b->start + NGX_RESOLVER_TCP_RSIZE; + + rec->read_buf = b; + } + + b->pos = b->start; + b->last = b->start; + + b = rec->write_buf; + + if (b == NULL) { + b = ngx_resolver_calloc(r, sizeof(ngx_buf_t)); + if (b == NULL) { + return NGX_ERROR; + } + + b->start = ngx_resolver_alloc(r, NGX_RESOLVER_TCP_WSIZE); + if (b->start == NULL) { + ngx_resolver_free(r, b); + return NGX_ERROR; + } + + b->end = b->start + NGX_RESOLVER_TCP_WSIZE; + + rec->write_buf = b; + } + + b->pos = b->start; + b->last = b->start; + + rc = ngx_tcp_connect(rec); + if (rc == NGX_ERROR) { + return NGX_ERROR; + } + + rec->tcp->data = rec; + rec->tcp->write->handler = ngx_resolver_tcp_write; + rec->tcp->read->handler = ngx_resolver_tcp_read; + rec->tcp->read->resolver = 1; + + ngx_add_timer(rec->tcp->write, (ngx_msec_t) (r->tcp_timeout * 1000)); + } + + b = rec->write_buf; + + if (b->end - b->last < 2 + qlen) { + ngx_log_error(NGX_LOG_CRIT, &rec->log, 0, "buffer overflow"); + return NGX_ERROR; + } + + *b->last++ = (u_char) (qlen >> 8); + *b->last++ = (u_char) qlen; + b->last = ngx_cpymem(b->last, query, qlen); + + if (rc == NGX_OK) { + ngx_resolver_tcp_write(rec->tcp->write); + } + + return NGX_OK; +} + + +static void +ngx_resolver_resend_handler(ngx_event_t *ev) +{ + time_t timer, atimer, stimer, ntimer; +#if (NGX_HAVE_INET6) + time_t a6timer; +#endif + ngx_resolver_t *r; + + r = ev->data; + + ngx_log_debug0(NGX_LOG_DEBUG_CORE, r->log, 0, + "resolver resend handler"); + + /* lock name mutex */ + + ntimer = ngx_resolver_resend(r, &r->name_rbtree, &r->name_resend_queue); + + stimer = ngx_resolver_resend(r, &r->srv_rbtree, &r->srv_resend_queue); + + /* unlock name mutex */ + + /* lock addr mutex */ + + atimer = ngx_resolver_resend(r, &r->addr_rbtree, &r->addr_resend_queue); + + /* unlock addr mutex */ + +#if (NGX_HAVE_INET6) + + /* lock addr6 mutex */ + + a6timer = ngx_resolver_resend(r, &r->addr6_rbtree, &r->addr6_resend_queue); + + /* unlock addr6 mutex */ + +#endif + + timer = ntimer; + + if (timer == 0) { + timer = atimer; + + } else if (atimer) { + timer = ngx_min(timer, atimer); + } + + if (timer == 0) { + timer = stimer; + + } else if (stimer) { + timer = ngx_min(timer, stimer); + } + +#if (NGX_HAVE_INET6) + + if (timer == 0) { + timer = a6timer; + + } else if (a6timer) { + timer = ngx_min(timer, a6timer); + } + +#endif + + if (timer) { + ngx_add_timer(r->event, (ngx_msec_t) (timer * 1000)); + } +} + + +static time_t +ngx_resolver_resend(ngx_resolver_t *r, ngx_rbtree_t *tree, ngx_queue_t *queue) +{ + time_t now; + ngx_queue_t *q; + ngx_resolver_node_t *rn; + + now = ngx_time(); + + for ( ;; ) { + if (ngx_queue_empty(queue)) { + return 0; + } + + q = ngx_queue_last(queue); + + rn = ngx_queue_data(q, ngx_resolver_node_t, queue); + + if (now < rn->expire) { + return rn->expire - now; + } + + ngx_log_debug3(NGX_LOG_DEBUG_CORE, r->log, 0, + "resolver resend \"%*s\" %p", + (size_t) rn->nlen, rn->name, rn->waiting); + + ngx_queue_remove(q); + + if (rn->waiting) { + + if (++rn->last_connection == r->connections.nelts) { + rn->last_connection = 0; + } + + (void) ngx_resolver_send_query(r, rn); + + rn->expire = now + r->resend_timeout; + + ngx_queue_insert_head(queue, q); + + continue; + } + + ngx_rbtree_delete(tree, &rn->node); + + ngx_resolver_free_node(r, rn); + } +} + + +static ngx_uint_t +ngx_resolver_resend_empty(ngx_resolver_t *r) +{ + return ngx_queue_empty(&r->name_resend_queue) + && ngx_queue_empty(&r->srv_resend_queue) +#if (NGX_HAVE_INET6) + && ngx_queue_empty(&r->addr6_resend_queue) +#endif + && ngx_queue_empty(&r->addr_resend_queue); +} + + +static void +ngx_resolver_udp_read(ngx_event_t *rev) +{ + ssize_t n; + ngx_connection_t *c; + ngx_resolver_connection_t *rec; + u_char buf[NGX_RESOLVER_UDP_SIZE]; + + c = rev->data; + rec = c->data; + + do { + n = ngx_udp_recv(c, buf, NGX_RESOLVER_UDP_SIZE); + + if (n < 0) { + return; + } + + ngx_resolver_process_response(rec->resolver, buf, n, 0); + + } while (rev->ready); +} + + +static void +ngx_resolver_tcp_write(ngx_event_t *wev) +{ + off_t sent; + ssize_t n; + ngx_buf_t *b; + ngx_resolver_t *r; + ngx_connection_t *c; + ngx_resolver_connection_t *rec; + + c = wev->data; + rec = c->data; + b = rec->write_buf; + r = rec->resolver; + + if (wev->timedout) { + goto failed; + } + + sent = c->sent; + + while (wev->ready && b->pos < b->last) { + n = ngx_send(c, b->pos, b->last - b->pos); + + if (n == NGX_AGAIN) { + break; + } + + if (n == NGX_ERROR) { + goto failed; + } + + b->pos += n; + } + + if (b->pos != b->start) { + b->last = ngx_movemem(b->start, b->pos, b->last - b->pos); + b->pos = b->start; + } + + if (c->sent != sent) { + ngx_add_timer(wev, (ngx_msec_t) (r->tcp_timeout * 1000)); + } + + if (ngx_handle_write_event(wev, 0) != NGX_OK) { + goto failed; + } + + return; + +failed: + + ngx_close_connection(c); + rec->tcp = NULL; +} + + +static void +ngx_resolver_tcp_read(ngx_event_t *rev) +{ + u_char *p; + size_t size; + ssize_t n; + u_short qlen; + ngx_buf_t *b; + ngx_resolver_t *r; + ngx_connection_t *c; + ngx_resolver_connection_t *rec; + + c = rev->data; + rec = c->data; + b = rec->read_buf; + r = rec->resolver; + + while (rev->ready) { + n = ngx_recv(c, b->last, b->end - b->last); + + if (n == NGX_AGAIN) { + break; + } + + if (n == NGX_ERROR || n == 0) { + goto failed; + } + + b->last += n; + + for ( ;; ) { + p = b->pos; + size = b->last - p; + + if (size < 2) { + break; + } + + qlen = (u_short) *p++ << 8; + qlen += *p++; + + if (size < (size_t) (2 + qlen)) { + break; + } + + ngx_resolver_process_response(r, p, qlen, 1); + + b->pos += 2 + qlen; + } + + if (b->pos != b->start) { + b->last = ngx_movemem(b->start, b->pos, b->last - b->pos); + b->pos = b->start; + } + } + + if (ngx_handle_read_event(rev, 0) != NGX_OK) { + goto failed; + } + + return; + +failed: + + ngx_close_connection(c); + rec->tcp = NULL; +} + + +static void +ngx_resolver_process_response(ngx_resolver_t *r, u_char *buf, size_t n, + ngx_uint_t tcp) +{ + char *err; + ngx_uint_t i, times, ident, qident, flags, code, nqs, nan, trunc, + qtype, qclass; +#if (NGX_HAVE_INET6) + ngx_uint_t qident6; +#endif + ngx_queue_t *q; + ngx_resolver_qs_t *qs; + ngx_resolver_hdr_t *response; + ngx_resolver_node_t *rn; + + if (n < sizeof(ngx_resolver_hdr_t)) { + goto short_response; + } + + response = (ngx_resolver_hdr_t *) buf; + + ident = (response->ident_hi << 8) + response->ident_lo; + flags = (response->flags_hi << 8) + response->flags_lo; + nqs = (response->nqs_hi << 8) + response->nqs_lo; + nan = (response->nan_hi << 8) + response->nan_lo; + trunc = flags & 0x0200; + + ngx_log_debug6(NGX_LOG_DEBUG_CORE, r->log, 0, + "resolver DNS response %ui fl:%04Xi %ui/%ui/%ud/%ud", + ident, flags, nqs, nan, + (response->nns_hi << 8) + response->nns_lo, + (response->nar_hi << 8) + response->nar_lo); + + /* response to a standard query */ + if ((flags & 0xf870) != 0x8000 || (trunc && tcp)) { + ngx_log_error(r->log_level, r->log, 0, + "invalid %s DNS response %ui fl:%04Xi", + tcp ? "TCP" : "UDP", ident, flags); + return; + } + + code = flags & 0xf; + + if (code == NGX_RESOLVE_FORMERR) { + + times = 0; + + for (q = ngx_queue_head(&r->name_resend_queue); + q != ngx_queue_sentinel(&r->name_resend_queue) && times++ < 100; + q = ngx_queue_next(q)) + { + rn = ngx_queue_data(q, ngx_resolver_node_t, queue); + qident = (rn->query[0] << 8) + rn->query[1]; + + if (qident == ident) { + goto dns_error_name; + } + +#if (NGX_HAVE_INET6) + if (rn->query6) { + qident6 = (rn->query6[0] << 8) + rn->query6[1]; + + if (qident6 == ident) { + goto dns_error_name; + } + } +#endif + } + + goto dns_error; + } + + if (code > NGX_RESOLVE_REFUSED) { + goto dns_error; + } + + if (nqs != 1) { + err = "invalid number of questions in DNS response"; + goto done; + } + + i = sizeof(ngx_resolver_hdr_t); + + while (i < (ngx_uint_t) n) { + if (buf[i] == '\0') { + goto found; + } + + i += 1 + buf[i]; + } + + goto short_response; + +found: + + if (i++ == sizeof(ngx_resolver_hdr_t)) { + err = "zero-length domain name in DNS response"; + goto done; + } + + if (i + sizeof(ngx_resolver_qs_t) + nan * (2 + sizeof(ngx_resolver_an_t)) + > (ngx_uint_t) n) + { + goto short_response; + } + + qs = (ngx_resolver_qs_t *) &buf[i]; + + qtype = (qs->type_hi << 8) + qs->type_lo; + qclass = (qs->class_hi << 8) + qs->class_lo; + + ngx_log_debug2(NGX_LOG_DEBUG_CORE, r->log, 0, + "resolver DNS response qt:%ui cl:%ui", qtype, qclass); + + if (qclass != 1) { + ngx_log_error(r->log_level, r->log, 0, + "unknown query class %ui in DNS response", qclass); + return; + } + + switch (qtype) { + + case NGX_RESOLVE_A: +#if (NGX_HAVE_INET6) + case NGX_RESOLVE_AAAA: +#endif + + ngx_resolver_process_a(r, buf, n, ident, code, qtype, nan, trunc, + i + sizeof(ngx_resolver_qs_t)); + + break; + + case NGX_RESOLVE_SRV: + + ngx_resolver_process_srv(r, buf, n, ident, code, nan, trunc, + i + sizeof(ngx_resolver_qs_t)); + + break; + + case NGX_RESOLVE_PTR: + + ngx_resolver_process_ptr(r, buf, n, ident, code, nan); + + break; + + default: + ngx_log_error(r->log_level, r->log, 0, + "unknown query type %ui in DNS response", qtype); + return; + } + + return; + +short_response: + + err = "short DNS response"; + +done: + + ngx_log_error(r->log_level, r->log, 0, err); + + return; + +dns_error_name: + + ngx_log_error(r->log_level, r->log, 0, + "DNS error (%ui: %s), query id:%ui, name:\"%*s\"", + code, ngx_resolver_strerror(code), ident, + (size_t) rn->nlen, rn->name); + return; + +dns_error: + + ngx_log_error(r->log_level, r->log, 0, + "DNS error (%ui: %s), query id:%ui", + code, ngx_resolver_strerror(code), ident); + return; +} + + +static void +ngx_resolver_process_a(ngx_resolver_t *r, u_char *buf, size_t n, + ngx_uint_t ident, ngx_uint_t code, ngx_uint_t qtype, + ngx_uint_t nan, ngx_uint_t trunc, ngx_uint_t ans) +{ + char *err; + u_char *cname; + size_t len; + int32_t ttl; + uint32_t hash; + in_addr_t *addr; + ngx_str_t name; + ngx_uint_t type, class, qident, naddrs, a, i, j, start; +#if (NGX_HAVE_INET6) + struct in6_addr *addr6; +#endif + ngx_resolver_an_t *an; + ngx_resolver_ctx_t *ctx, *next; + ngx_resolver_node_t *rn; + ngx_resolver_addr_t *addrs; + ngx_resolver_connection_t *rec; + + if (ngx_resolver_copy(r, &name, buf, + buf + sizeof(ngx_resolver_hdr_t), buf + n) + != NGX_OK) + { + return; + } + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, r->log, 0, "resolver qs:%V", &name); + + hash = ngx_crc32_short(name.data, name.len); + + /* lock name mutex */ + + rn = ngx_resolver_lookup_name(r, &name, hash); + + if (rn == NULL) { + ngx_log_error(r->log_level, r->log, 0, + "unexpected response for %V", &name); + ngx_resolver_free(r, name.data); + goto failed; + } + + switch (qtype) { + +#if (NGX_HAVE_INET6) + case NGX_RESOLVE_AAAA: + + if (rn->query6 == NULL || rn->naddrs6 != (u_short) -1) { + ngx_log_error(r->log_level, r->log, 0, + "unexpected response for %V", &name); + ngx_resolver_free(r, name.data); + goto failed; + } + + if (trunc && rn->tcp6) { + ngx_resolver_free(r, name.data); + goto failed; + } + + qident = (rn->query6[0] << 8) + rn->query6[1]; + + break; +#endif + + default: /* NGX_RESOLVE_A */ + + if (rn->query == NULL || rn->naddrs != (u_short) -1) { + ngx_log_error(r->log_level, r->log, 0, + "unexpected response for %V", &name); + ngx_resolver_free(r, name.data); + goto failed; + } + + if (trunc && rn->tcp) { + ngx_resolver_free(r, name.data); + goto failed; + } + + qident = (rn->query[0] << 8) + rn->query[1]; + } + + if (ident != qident) { + ngx_log_error(r->log_level, r->log, 0, + "wrong ident %ui response for %V, expect %ui", + ident, &name, qident); + ngx_resolver_free(r, name.data); + goto failed; + } + + ngx_resolver_free(r, name.data); + + if (trunc) { + + ngx_queue_remove(&rn->queue); + + if (rn->waiting == NULL) { + ngx_rbtree_delete(&r->name_rbtree, &rn->node); + ngx_resolver_free_node(r, rn); + goto next; + } + + rec = r->connections.elts; + rec = &rec[rn->last_connection]; + + switch (qtype) { + +#if (NGX_HAVE_INET6) + case NGX_RESOLVE_AAAA: + + rn->tcp6 = 1; + + (void) ngx_resolver_send_tcp_query(r, rec, rn->query6, rn->qlen); + + break; +#endif + + default: /* NGX_RESOLVE_A */ + + rn->tcp = 1; + + (void) ngx_resolver_send_tcp_query(r, rec, rn->query, rn->qlen); + } + + rn->expire = ngx_time() + r->resend_timeout; + + ngx_queue_insert_head(&r->name_resend_queue, &rn->queue); + + goto next; + } + + if (code == 0 && rn->code) { + code = rn->code; + } + + if (code == 0 && nan == 0) { + +#if (NGX_HAVE_INET6) + switch (qtype) { + + case NGX_RESOLVE_AAAA: + + rn->naddrs6 = 0; + + if (rn->naddrs == (u_short) -1) { + goto next; + } + + if (rn->naddrs) { + goto export; + } + + break; + + default: /* NGX_RESOLVE_A */ + + rn->naddrs = 0; + + if (rn->naddrs6 == (u_short) -1) { + goto next; + } + + if (rn->naddrs6) { + goto export; + } + } +#endif + + code = NGX_RESOLVE_NXDOMAIN; + } + + if (code) { + +#if (NGX_HAVE_INET6) + switch (qtype) { + + case NGX_RESOLVE_AAAA: + + rn->naddrs6 = 0; + + if (rn->naddrs == (u_short) -1) { + rn->code = (u_char) code; + goto next; + } + + break; + + default: /* NGX_RESOLVE_A */ + + rn->naddrs = 0; + + if (rn->naddrs6 == (u_short) -1) { + rn->code = (u_char) code; + goto next; + } + } +#endif + + next = rn->waiting; + rn->waiting = NULL; + + ngx_queue_remove(&rn->queue); + + ngx_rbtree_delete(&r->name_rbtree, &rn->node); + + /* unlock name mutex */ + + while (next) { + ctx = next; + ctx->state = code; + ctx->valid = ngx_time() + (r->valid ? r->valid : 10); + next = ctx->next; + + ctx->handler(ctx); + } + + ngx_resolver_free_node(r, rn); + + return; + } + + i = ans; + naddrs = 0; + cname = NULL; + + for (a = 0; a < nan; a++) { + + start = i; + + while (i < n) { + + if (buf[i] & 0xc0) { + i += 2; + goto found; + } + + if (buf[i] == 0) { + i++; + goto test_length; + } + + i += 1 + buf[i]; + } + + goto short_response; + + test_length: + + if (i - start < 2) { + err = "invalid name in DNS response"; + goto invalid; + } + + found: + + if (i + sizeof(ngx_resolver_an_t) >= n) { + goto short_response; + } + + an = (ngx_resolver_an_t *) &buf[i]; + + type = (an->type_hi << 8) + an->type_lo; + class = (an->class_hi << 8) + an->class_lo; + len = (an->len_hi << 8) + an->len_lo; + ttl = (an->ttl[0] << 24) + (an->ttl[1] << 16) + + (an->ttl[2] << 8) + (an->ttl[3]); + + if (class != 1) { + ngx_log_error(r->log_level, r->log, 0, + "unexpected RR class %ui", class); + goto failed; + } + + if (ttl < 0) { + ttl = 0; + } + + rn->ttl = ngx_min(rn->ttl, (uint32_t) ttl); + + i += sizeof(ngx_resolver_an_t); + + switch (type) { + + case NGX_RESOLVE_A: + + if (qtype != NGX_RESOLVE_A) { + err = "unexpected A record in DNS response"; + goto invalid; + } + + if (len != 4) { + err = "invalid A record in DNS response"; + goto invalid; + } + + if (i + 4 > n) { + goto short_response; + } + + naddrs++; + + break; + +#if (NGX_HAVE_INET6) + case NGX_RESOLVE_AAAA: + + if (qtype != NGX_RESOLVE_AAAA) { + err = "unexpected AAAA record in DNS response"; + goto invalid; + } + + if (len != 16) { + err = "invalid AAAA record in DNS response"; + goto invalid; + } + + if (i + 16 > n) { + goto short_response; + } + + naddrs++; + + break; +#endif + + case NGX_RESOLVE_CNAME: + + cname = &buf[i]; + + break; + + case NGX_RESOLVE_DNAME: + + break; + + default: + + ngx_log_error(r->log_level, r->log, 0, + "unexpected RR type %ui", type); + } + + i += len; + } + + ngx_log_debug3(NGX_LOG_DEBUG_CORE, r->log, 0, + "resolver naddrs:%ui cname:%p ttl:%uD", + naddrs, cname, rn->ttl); + + if (naddrs) { + + switch (qtype) { + +#if (NGX_HAVE_INET6) + case NGX_RESOLVE_AAAA: + + if (naddrs == 1) { + addr6 = &rn->u6.addr6; + rn->naddrs6 = 1; + + } else { + addr6 = ngx_resolver_alloc(r, naddrs * sizeof(struct in6_addr)); + if (addr6 == NULL) { + goto failed; + } + + rn->u6.addrs6 = addr6; + rn->naddrs6 = (u_short) naddrs; + } + +#if (NGX_SUPPRESS_WARN) + addr = NULL; +#endif + + break; +#endif + + default: /* NGX_RESOLVE_A */ + + if (naddrs == 1) { + addr = &rn->u.addr; + rn->naddrs = 1; + + } else { + addr = ngx_resolver_alloc(r, naddrs * sizeof(in_addr_t)); + if (addr == NULL) { + goto failed; + } + + rn->u.addrs = addr; + rn->naddrs = (u_short) naddrs; + } + +#if (NGX_HAVE_INET6 && NGX_SUPPRESS_WARN) + addr6 = NULL; +#endif + } + + j = 0; + i = ans; + + for (a = 0; a < nan; a++) { + + for ( ;; ) { + + if (buf[i] & 0xc0) { + i += 2; + break; + } + + if (buf[i] == 0) { + i++; + break; + } + + i += 1 + buf[i]; + } + + an = (ngx_resolver_an_t *) &buf[i]; + + type = (an->type_hi << 8) + an->type_lo; + len = (an->len_hi << 8) + an->len_lo; + + i += sizeof(ngx_resolver_an_t); + + if (type == NGX_RESOLVE_A) { + + addr[j] = htonl((buf[i] << 24) + (buf[i + 1] << 16) + + (buf[i + 2] << 8) + (buf[i + 3])); + + if (++j == naddrs) { + +#if (NGX_HAVE_INET6) + if (rn->naddrs6 == (u_short) -1) { + goto next; + } +#endif + + break; + } + } + +#if (NGX_HAVE_INET6) + else if (type == NGX_RESOLVE_AAAA) { + + ngx_memcpy(addr6[j].s6_addr, &buf[i], 16); + + if (++j == naddrs) { + + if (rn->naddrs == (u_short) -1) { + goto next; + } + + break; + } + } +#endif + + i += len; + } + } + + switch (qtype) { + +#if (NGX_HAVE_INET6) + case NGX_RESOLVE_AAAA: + + if (rn->naddrs6 == (u_short) -1) { + rn->naddrs6 = 0; + } + + break; +#endif + + default: /* NGX_RESOLVE_A */ + + if (rn->naddrs == (u_short) -1) { + rn->naddrs = 0; + } + } + + if (rn->naddrs != (u_short) -1 +#if (NGX_HAVE_INET6) + && rn->naddrs6 != (u_short) -1 +#endif + && rn->naddrs +#if (NGX_HAVE_INET6) + + rn->naddrs6 +#endif + > 0) + { + +#if (NGX_HAVE_INET6) + export: +#endif + + naddrs = rn->naddrs; +#if (NGX_HAVE_INET6) + naddrs += rn->naddrs6; +#endif + + if (naddrs == 1 && rn->naddrs == 1) { + addrs = NULL; + + } else { + addrs = ngx_resolver_export(r, rn, 0); + if (addrs == NULL) { + goto failed; + } + } + + ngx_queue_remove(&rn->queue); + + rn->valid = ngx_time() + (r->valid ? r->valid : (time_t) rn->ttl); + rn->expire = ngx_time() + r->expire; + + ngx_queue_insert_head(&r->name_expire_queue, &rn->queue); + + next = rn->waiting; + rn->waiting = NULL; + + /* unlock name mutex */ + + while (next) { + ctx = next; + ctx->state = NGX_OK; + ctx->valid = rn->valid; + ctx->naddrs = naddrs; + + if (addrs == NULL) { + ctx->addrs = &ctx->addr; + ctx->addr.sockaddr = (struct sockaddr *) &ctx->sin; + ctx->addr.socklen = sizeof(struct sockaddr_in); + ngx_memzero(&ctx->sin, sizeof(struct sockaddr_in)); + ctx->sin.sin_family = AF_INET; + ctx->sin.sin_addr.s_addr = rn->u.addr; + + } else { + ctx->addrs = addrs; + } + + next = ctx->next; + + ctx->handler(ctx); + } + + if (addrs != NULL) { + ngx_resolver_free(r, addrs->sockaddr); + ngx_resolver_free(r, addrs); + } + + ngx_resolver_free(r, rn->query); + rn->query = NULL; +#if (NGX_HAVE_INET6) + rn->query6 = NULL; +#endif + + return; + } + + if (cname) { + + /* CNAME only */ + + if (rn->naddrs == (u_short) -1 +#if (NGX_HAVE_INET6) + || rn->naddrs6 == (u_short) -1 +#endif + ) + { + goto next; + } + + if (ngx_resolver_copy(r, &name, buf, cname, buf + n) != NGX_OK) { + goto failed; + } + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, r->log, 0, + "resolver cname:\"%V\"", &name); + + ngx_queue_remove(&rn->queue); + + rn->cnlen = (u_short) name.len; + rn->u.cname = name.data; + + rn->valid = ngx_time() + (r->valid ? r->valid : (time_t) rn->ttl); + rn->expire = ngx_time() + r->expire; + + ngx_queue_insert_head(&r->name_expire_queue, &rn->queue); + + ngx_resolver_free(r, rn->query); + rn->query = NULL; +#if (NGX_HAVE_INET6) + rn->query6 = NULL; +#endif + + ctx = rn->waiting; + rn->waiting = NULL; + + if (ctx) { + + if (ctx->recursion++ >= NGX_RESOLVER_MAX_RECURSION) { + + /* unlock name mutex */ + + do { + ctx->state = NGX_RESOLVE_NXDOMAIN; + next = ctx->next; + + ctx->handler(ctx); + + ctx = next; + } while (ctx); + + return; + } + + for (next = ctx; next; next = next->next) { + next->node = NULL; + } + + (void) ngx_resolve_name_locked(r, ctx, &name); + } + + /* unlock name mutex */ + + return; + } + + ngx_log_error(r->log_level, r->log, 0, + "no A or CNAME types in DNS response"); + return; + +short_response: + + err = "short DNS response"; + +invalid: + + /* unlock name mutex */ + + ngx_log_error(r->log_level, r->log, 0, err); + + return; + +failed: + +next: + + /* unlock name mutex */ + + return; +} + + +static void +ngx_resolver_process_srv(ngx_resolver_t *r, u_char *buf, size_t n, + ngx_uint_t ident, ngx_uint_t code, ngx_uint_t nan, + ngx_uint_t trunc, ngx_uint_t ans) +{ + char *err; + u_char *cname; + size_t len; + int32_t ttl; + uint32_t hash; + ngx_str_t name; + ngx_uint_t type, qident, class, start, nsrvs, a, i, j; + ngx_resolver_an_t *an; + ngx_resolver_ctx_t *ctx, *next; + ngx_resolver_srv_t *srvs; + ngx_resolver_node_t *rn; + ngx_resolver_connection_t *rec; + + if (ngx_resolver_copy(r, &name, buf, + buf + sizeof(ngx_resolver_hdr_t), buf + n) + != NGX_OK) + { + return; + } + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, r->log, 0, "resolver qs:%V", &name); + + hash = ngx_crc32_short(name.data, name.len); + + rn = ngx_resolver_lookup_srv(r, &name, hash); + + if (rn == NULL || rn->query == NULL) { + ngx_log_error(r->log_level, r->log, 0, + "unexpected response for %V", &name); + ngx_resolver_free(r, name.data); + goto failed; + } + + if (trunc && rn->tcp) { + ngx_resolver_free(r, name.data); + goto failed; + } + + qident = (rn->query[0] << 8) + rn->query[1]; + + if (ident != qident) { + ngx_log_error(r->log_level, r->log, 0, + "wrong ident %ui response for %V, expect %ui", + ident, &name, qident); + ngx_resolver_free(r, name.data); + goto failed; + } + + ngx_resolver_free(r, name.data); + + if (trunc) { + + ngx_queue_remove(&rn->queue); + + if (rn->waiting == NULL) { + ngx_rbtree_delete(&r->srv_rbtree, &rn->node); + ngx_resolver_free_node(r, rn); + return; + } + + rec = r->connections.elts; + rec = &rec[rn->last_connection]; + + rn->tcp = 1; + + (void) ngx_resolver_send_tcp_query(r, rec, rn->query, rn->qlen); + + rn->expire = ngx_time() + r->resend_timeout; + + ngx_queue_insert_head(&r->srv_resend_queue, &rn->queue); + + return; + } + + if (code == 0 && rn->code) { + code = rn->code; + } + + if (code == 0 && nan == 0) { + code = NGX_RESOLVE_NXDOMAIN; + } + + if (code) { + next = rn->waiting; + rn->waiting = NULL; + + ngx_queue_remove(&rn->queue); + + ngx_rbtree_delete(&r->srv_rbtree, &rn->node); + + while (next) { + ctx = next; + ctx->state = code; + ctx->valid = ngx_time() + (r->valid ? r->valid : 10); + next = ctx->next; + + ctx->handler(ctx); + } + + ngx_resolver_free_node(r, rn); + + return; + } + + i = ans; + nsrvs = 0; + cname = NULL; + + for (a = 0; a < nan; a++) { + + start = i; + + while (i < n) { + + if (buf[i] & 0xc0) { + i += 2; + goto found; + } + + if (buf[i] == 0) { + i++; + goto test_length; + } + + i += 1 + buf[i]; + } + + goto short_response; + + test_length: + + if (i - start < 2) { + err = "invalid name DNS response"; + goto invalid; + } + + found: + + if (i + sizeof(ngx_resolver_an_t) >= n) { + goto short_response; + } + + an = (ngx_resolver_an_t *) &buf[i]; + + type = (an->type_hi << 8) + an->type_lo; + class = (an->class_hi << 8) + an->class_lo; + len = (an->len_hi << 8) + an->len_lo; + ttl = (an->ttl[0] << 24) + (an->ttl[1] << 16) + + (an->ttl[2] << 8) + (an->ttl[3]); + + if (class != 1) { + ngx_log_error(r->log_level, r->log, 0, + "unexpected RR class %ui", class); + goto failed; + } + + if (ttl < 0) { + ttl = 0; + } + + rn->ttl = ngx_min(rn->ttl, (uint32_t) ttl); + + i += sizeof(ngx_resolver_an_t); + + switch (type) { + + case NGX_RESOLVE_SRV: + + if (i + 6 > n) { + goto short_response; + } + + if (ngx_resolver_copy(r, NULL, buf, &buf[i + 6], buf + n) + != NGX_OK) + { + goto failed; + } + + nsrvs++; + + break; + + case NGX_RESOLVE_CNAME: + + cname = &buf[i]; + + break; + + case NGX_RESOLVE_DNAME: + + break; + + default: + + ngx_log_error(r->log_level, r->log, 0, + "unexpected RR type %ui", type); + } + + i += len; + } + + ngx_log_debug3(NGX_LOG_DEBUG_CORE, r->log, 0, + "resolver nsrvs:%ui cname:%p ttl:%uD", + nsrvs, cname, rn->ttl); + + if (nsrvs) { + + srvs = ngx_resolver_calloc(r, nsrvs * sizeof(ngx_resolver_srv_t)); + if (srvs == NULL) { + goto failed; + } + + rn->u.srvs = srvs; + rn->nsrvs = (u_short) nsrvs; + + j = 0; + i = ans; + + for (a = 0; a < nan; a++) { + + for ( ;; ) { + + if (buf[i] & 0xc0) { + i += 2; + break; + } + + if (buf[i] == 0) { + i++; + break; + } + + i += 1 + buf[i]; + } + + an = (ngx_resolver_an_t *) &buf[i]; + + type = (an->type_hi << 8) + an->type_lo; + len = (an->len_hi << 8) + an->len_lo; + + i += sizeof(ngx_resolver_an_t); + + if (type == NGX_RESOLVE_SRV) { + + srvs[j].priority = (buf[i] << 8) + buf[i + 1]; + srvs[j].weight = (buf[i + 2] << 8) + buf[i + 3]; + + if (srvs[j].weight == 0) { + srvs[j].weight = 1; + } + + srvs[j].port = (buf[i + 4] << 8) + buf[i + 5]; + + if (ngx_resolver_copy(r, &srvs[j].name, buf, &buf[i + 6], + buf + n) + != NGX_OK) + { + goto failed; + } + + j++; + } + + i += len; + } + + ngx_sort(srvs, nsrvs, sizeof(ngx_resolver_srv_t), + ngx_resolver_cmp_srvs); + + ngx_resolver_free(r, rn->query); + rn->query = NULL; + + ngx_queue_remove(&rn->queue); + + rn->valid = ngx_time() + (r->valid ? r->valid : (time_t) rn->ttl); + rn->expire = ngx_time() + r->expire; + + ngx_queue_insert_head(&r->srv_expire_queue, &rn->queue); + + next = rn->waiting; + rn->waiting = NULL; + + while (next) { + ctx = next; + next = ctx->next; + + ngx_resolver_resolve_srv_names(ctx, rn); + } + + return; + } + + rn->nsrvs = 0; + + if (cname) { + + /* CNAME only */ + + if (ngx_resolver_copy(r, &name, buf, cname, buf + n) != NGX_OK) { + goto failed; + } + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, r->log, 0, + "resolver cname:\"%V\"", &name); + + ngx_queue_remove(&rn->queue); + + rn->cnlen = (u_short) name.len; + rn->u.cname = name.data; + + rn->valid = ngx_time() + (r->valid ? r->valid : (time_t) rn->ttl); + rn->expire = ngx_time() + r->expire; + + ngx_queue_insert_head(&r->srv_expire_queue, &rn->queue); + + ngx_resolver_free(r, rn->query); + rn->query = NULL; +#if (NGX_HAVE_INET6) + rn->query6 = NULL; +#endif + + ctx = rn->waiting; + rn->waiting = NULL; + + if (ctx) { + + if (ctx->recursion++ >= NGX_RESOLVER_MAX_RECURSION) { + + /* unlock name mutex */ + + do { + ctx->state = NGX_RESOLVE_NXDOMAIN; + next = ctx->next; + + ctx->handler(ctx); + + ctx = next; + } while (ctx); + + return; + } + + for (next = ctx; next; next = next->next) { + next->node = NULL; + } + + (void) ngx_resolve_name_locked(r, ctx, &name); + } + + /* unlock name mutex */ + + return; + } + + ngx_log_error(r->log_level, r->log, 0, "no SRV type in DNS response"); + + return; + +short_response: + + err = "short DNS response"; + +invalid: + + /* unlock name mutex */ + + ngx_log_error(r->log_level, r->log, 0, err); + + return; + +failed: + + /* unlock name mutex */ + + return; +} + + +static void +ngx_resolver_resolve_srv_names(ngx_resolver_ctx_t *ctx, ngx_resolver_node_t *rn) +{ + ngx_uint_t i; + ngx_resolver_t *r; + ngx_resolver_ctx_t *cctx; + ngx_resolver_srv_name_t *srvs; + + r = ctx->resolver; + + ctx->node = NULL; + ctx->state = NGX_OK; + ctx->valid = rn->valid; + ctx->count = rn->nsrvs; + + srvs = ngx_resolver_calloc(r, rn->nsrvs * sizeof(ngx_resolver_srv_name_t)); + if (srvs == NULL) { + goto failed; + } + + ctx->srvs = srvs; + ctx->nsrvs = rn->nsrvs; + + if (ctx->event && ctx->event->timer_set) { + ngx_del_timer(ctx->event); + } + + for (i = 0; i < (ngx_uint_t) rn->nsrvs; i++) { + srvs[i].name.data = ngx_resolver_alloc(r, rn->u.srvs[i].name.len); + if (srvs[i].name.data == NULL) { + goto failed; + } + + srvs[i].name.len = rn->u.srvs[i].name.len; + ngx_memcpy(srvs[i].name.data, rn->u.srvs[i].name.data, + srvs[i].name.len); + + cctx = ngx_resolve_start(r, NULL); + if (cctx == NULL) { + goto failed; + } + + cctx->name = srvs[i].name; + cctx->handler = ngx_resolver_srv_names_handler; + cctx->data = ctx; + cctx->srvs = &srvs[i]; + cctx->timeout = ctx->timeout; + + srvs[i].priority = rn->u.srvs[i].priority; + srvs[i].weight = rn->u.srvs[i].weight; + srvs[i].port = rn->u.srvs[i].port; + srvs[i].ctx = cctx; + + if (ngx_resolve_name(cctx) == NGX_ERROR) { + srvs[i].ctx = NULL; + goto failed; + } + } + + return; + +failed: + + ctx->state = NGX_ERROR; + ctx->valid = ngx_time() + (r->valid ? r->valid : 10); + + ctx->handler(ctx); +} + + +static void +ngx_resolver_srv_names_handler(ngx_resolver_ctx_t *cctx) +{ + ngx_uint_t i; + ngx_addr_t *addrs; + ngx_resolver_t *r; + ngx_sockaddr_t *sockaddr; + ngx_resolver_ctx_t *ctx; + ngx_resolver_srv_name_t *srv; + + r = cctx->resolver; + ctx = cctx->data; + srv = cctx->srvs; + + ctx->count--; + + srv->ctx = NULL; + srv->state = cctx->state; + + if (cctx->naddrs) { + + ctx->valid = ngx_min(ctx->valid, cctx->valid); + + addrs = ngx_resolver_calloc(r, cctx->naddrs * sizeof(ngx_addr_t)); + if (addrs == NULL) { + ngx_resolve_name_done(cctx); + + ctx->state = NGX_ERROR; + ctx->valid = ngx_time() + (r->valid ? r->valid : 10); + + ctx->handler(ctx); + return; + } + + sockaddr = ngx_resolver_alloc(r, cctx->naddrs * sizeof(ngx_sockaddr_t)); + if (sockaddr == NULL) { + ngx_resolver_free(r, addrs); + ngx_resolve_name_done(cctx); + + ctx->state = NGX_ERROR; + ctx->valid = ngx_time() + (r->valid ? r->valid : 10); + + ctx->handler(ctx); + return; + } + + for (i = 0; i < cctx->naddrs; i++) { + addrs[i].sockaddr = &sockaddr[i].sockaddr; + addrs[i].socklen = cctx->addrs[i].socklen; + + ngx_memcpy(&sockaddr[i], cctx->addrs[i].sockaddr, + addrs[i].socklen); + + ngx_inet_set_port(addrs[i].sockaddr, srv->port); + } + + srv->addrs = addrs; + srv->naddrs = cctx->naddrs; + } + + ngx_resolve_name_done(cctx); + + if (ctx->count == 0) { + ngx_resolver_report_srv(r, ctx); + } +} + + +static void +ngx_resolver_process_ptr(ngx_resolver_t *r, u_char *buf, size_t n, + ngx_uint_t ident, ngx_uint_t code, ngx_uint_t nan) +{ + char *err; + size_t len; + in_addr_t addr; + int32_t ttl; + ngx_int_t octet; + ngx_str_t name; + ngx_uint_t mask, type, class, qident, a, i, start; + ngx_queue_t *expire_queue; + ngx_rbtree_t *tree; + ngx_resolver_an_t *an; + ngx_resolver_ctx_t *ctx, *next; + ngx_resolver_node_t *rn; +#if (NGX_HAVE_INET6) + uint32_t hash; + ngx_int_t digit; + struct in6_addr addr6; +#endif + + if (ngx_resolver_copy(r, &name, buf, + buf + sizeof(ngx_resolver_hdr_t), buf + n) + != NGX_OK) + { + return; + } + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, r->log, 0, "resolver qs:%V", &name); + + /* AF_INET */ + + addr = 0; + i = sizeof(ngx_resolver_hdr_t); + + for (mask = 0; mask < 32; mask += 8) { + len = buf[i++]; + + octet = ngx_atoi(&buf[i], len); + if (octet == NGX_ERROR || octet > 255) { + goto invalid_in_addr_arpa; + } + + addr += octet << mask; + i += len; + } + + if (ngx_strcasecmp(&buf[i], (u_char *) "\7in-addr\4arpa") == 0) { + i += sizeof("\7in-addr\4arpa"); + + /* lock addr mutex */ + + rn = ngx_resolver_lookup_addr(r, addr); + + tree = &r->addr_rbtree; + expire_queue = &r->addr_expire_queue; + + goto valid; + } + +invalid_in_addr_arpa: + +#if (NGX_HAVE_INET6) + + i = sizeof(ngx_resolver_hdr_t); + + for (octet = 15; octet >= 0; octet--) { + if (buf[i++] != '\1') { + goto invalid_ip6_arpa; + } + + digit = ngx_hextoi(&buf[i++], 1); + if (digit == NGX_ERROR) { + goto invalid_ip6_arpa; + } + + addr6.s6_addr[octet] = (u_char) digit; + + if (buf[i++] != '\1') { + goto invalid_ip6_arpa; + } + + digit = ngx_hextoi(&buf[i++], 1); + if (digit == NGX_ERROR) { + goto invalid_ip6_arpa; + } + + addr6.s6_addr[octet] += (u_char) (digit * 16); + } + + if (ngx_strcasecmp(&buf[i], (u_char *) "\3ip6\4arpa") == 0) { + i += sizeof("\3ip6\4arpa"); + + /* lock addr mutex */ + + hash = ngx_crc32_short(addr6.s6_addr, 16); + rn = ngx_resolver_lookup_addr6(r, &addr6, hash); + + tree = &r->addr6_rbtree; + expire_queue = &r->addr6_expire_queue; + + goto valid; + } + +invalid_ip6_arpa: +#endif + + ngx_log_error(r->log_level, r->log, 0, + "invalid in-addr.arpa or ip6.arpa name in DNS response"); + ngx_resolver_free(r, name.data); + return; + +valid: + + if (rn == NULL || rn->query == NULL) { + ngx_log_error(r->log_level, r->log, 0, + "unexpected response for %V", &name); + ngx_resolver_free(r, name.data); + goto failed; + } + + qident = (rn->query[0] << 8) + rn->query[1]; + + if (ident != qident) { + ngx_log_error(r->log_level, r->log, 0, + "wrong ident %ui response for %V, expect %ui", + ident, &name, qident); + ngx_resolver_free(r, name.data); + goto failed; + } + + ngx_resolver_free(r, name.data); + + if (code == 0 && nan == 0) { + code = NGX_RESOLVE_NXDOMAIN; + } + + if (code) { + next = rn->waiting; + rn->waiting = NULL; + + ngx_queue_remove(&rn->queue); + + ngx_rbtree_delete(tree, &rn->node); + + /* unlock addr mutex */ + + while (next) { + ctx = next; + ctx->state = code; + ctx->valid = ngx_time() + (r->valid ? r->valid : 10); + next = ctx->next; + + ctx->handler(ctx); + } + + ngx_resolver_free_node(r, rn); + + return; + } + + i += sizeof(ngx_resolver_qs_t); + + for (a = 0; a < nan; a++) { + + start = i; + + while (i < n) { + + if (buf[i] & 0xc0) { + i += 2; + goto found; + } + + if (buf[i] == 0) { + i++; + goto test_length; + } + + i += 1 + buf[i]; + } + + goto short_response; + + test_length: + + if (i - start < 2) { + err = "invalid name in DNS response"; + goto invalid; + } + + found: + + if (i + sizeof(ngx_resolver_an_t) >= n) { + goto short_response; + } + + an = (ngx_resolver_an_t *) &buf[i]; + + type = (an->type_hi << 8) + an->type_lo; + class = (an->class_hi << 8) + an->class_lo; + len = (an->len_hi << 8) + an->len_lo; + ttl = (an->ttl[0] << 24) + (an->ttl[1] << 16) + + (an->ttl[2] << 8) + (an->ttl[3]); + + if (class != 1) { + ngx_log_error(r->log_level, r->log, 0, + "unexpected RR class %ui", class); + goto failed; + } + + if (ttl < 0) { + ttl = 0; + } + + ngx_log_debug3(NGX_LOG_DEBUG_CORE, r->log, 0, + "resolver qt:%ui cl:%ui len:%uz", + type, class, len); + + i += sizeof(ngx_resolver_an_t); + + switch (type) { + + case NGX_RESOLVE_PTR: + + goto ptr; + + case NGX_RESOLVE_CNAME: + + break; + + default: + + ngx_log_error(r->log_level, r->log, 0, + "unexpected RR type %ui", type); + } + + i += len; + } + + /* unlock addr mutex */ + + ngx_log_error(r->log_level, r->log, 0, + "no PTR type in DNS response"); + return; + +ptr: + + if (ngx_resolver_copy(r, &name, buf, buf + i, buf + n) != NGX_OK) { + goto failed; + } + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, r->log, 0, "resolver an:%V", &name); + + if (name.len != (size_t) rn->nlen + || ngx_strncmp(name.data, rn->name, name.len) != 0) + { + if (rn->nlen) { + ngx_resolver_free(r, rn->name); + } + + rn->nlen = (u_short) name.len; + rn->name = name.data; + + name.data = ngx_resolver_dup(r, rn->name, name.len); + if (name.data == NULL) { + goto failed; + } + } + + ngx_queue_remove(&rn->queue); + + rn->valid = ngx_time() + (r->valid ? r->valid : ttl); + rn->expire = ngx_time() + r->expire; + + ngx_queue_insert_head(expire_queue, &rn->queue); + + next = rn->waiting; + rn->waiting = NULL; + + /* unlock addr mutex */ + + while (next) { + ctx = next; + ctx->state = NGX_OK; + ctx->valid = rn->valid; + ctx->name = name; + next = ctx->next; + + ctx->handler(ctx); + } + + ngx_resolver_free(r, name.data); + + return; + +short_response: + + err = "short DNS response"; + +invalid: + + /* unlock addr mutex */ + + ngx_log_error(r->log_level, r->log, 0, err); + + return; + +failed: + + /* unlock addr mutex */ + + return; +} + + +static ngx_resolver_node_t * +ngx_resolver_lookup_name(ngx_resolver_t *r, ngx_str_t *name, uint32_t hash) +{ + ngx_int_t rc; + ngx_rbtree_node_t *node, *sentinel; + ngx_resolver_node_t *rn; + + node = r->name_rbtree.root; + sentinel = r->name_rbtree.sentinel; + + while (node != sentinel) { + + if (hash < node->key) { + node = node->left; + continue; + } + + if (hash > node->key) { + node = node->right; + continue; + } + + /* hash == node->key */ + + rn = ngx_resolver_node(node); + + rc = ngx_memn2cmp(name->data, rn->name, name->len, rn->nlen); + + if (rc == 0) { + return rn; + } + + node = (rc < 0) ? node->left : node->right; + } + + /* not found */ + + return NULL; +} + + +static ngx_resolver_node_t * +ngx_resolver_lookup_srv(ngx_resolver_t *r, ngx_str_t *name, uint32_t hash) +{ + ngx_int_t rc; + ngx_rbtree_node_t *node, *sentinel; + ngx_resolver_node_t *rn; + + node = r->srv_rbtree.root; + sentinel = r->srv_rbtree.sentinel; + + while (node != sentinel) { + + if (hash < node->key) { + node = node->left; + continue; + } + + if (hash > node->key) { + node = node->right; + continue; + } + + /* hash == node->key */ + + rn = ngx_resolver_node(node); + + rc = ngx_memn2cmp(name->data, rn->name, name->len, rn->nlen); + + if (rc == 0) { + return rn; + } + + node = (rc < 0) ? node->left : node->right; + } + + /* not found */ + + return NULL; +} + + +static ngx_resolver_node_t * +ngx_resolver_lookup_addr(ngx_resolver_t *r, in_addr_t addr) +{ + ngx_rbtree_node_t *node, *sentinel; + + node = r->addr_rbtree.root; + sentinel = r->addr_rbtree.sentinel; + + while (node != sentinel) { + + if (addr < node->key) { + node = node->left; + continue; + } + + if (addr > node->key) { + node = node->right; + continue; + } + + /* addr == node->key */ + + return ngx_resolver_node(node); + } + + /* not found */ + + return NULL; +} + + +#if (NGX_HAVE_INET6) + +static ngx_resolver_node_t * +ngx_resolver_lookup_addr6(ngx_resolver_t *r, struct in6_addr *addr, + uint32_t hash) +{ + ngx_int_t rc; + ngx_rbtree_node_t *node, *sentinel; + ngx_resolver_node_t *rn; + + node = r->addr6_rbtree.root; + sentinel = r->addr6_rbtree.sentinel; + + while (node != sentinel) { + + if (hash < node->key) { + node = node->left; + continue; + } + + if (hash > node->key) { + node = node->right; + continue; + } + + /* hash == node->key */ + + rn = ngx_resolver_node(node); + + rc = ngx_memcmp(addr, &rn->addr6, 16); + + if (rc == 0) { + return rn; + } + + node = (rc < 0) ? node->left : node->right; + } + + /* not found */ + + return NULL; +} + +#endif + + +static void +ngx_resolver_rbtree_insert_value(ngx_rbtree_node_t *temp, + ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel) +{ + ngx_rbtree_node_t **p; + ngx_resolver_node_t *rn, *rn_temp; + + for ( ;; ) { + + if (node->key < temp->key) { + + p = &temp->left; + + } else if (node->key > temp->key) { + + p = &temp->right; + + } else { /* node->key == temp->key */ + + rn = ngx_resolver_node(node); + rn_temp = ngx_resolver_node(temp); + + p = (ngx_memn2cmp(rn->name, rn_temp->name, rn->nlen, rn_temp->nlen) + < 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); +} + + +#if (NGX_HAVE_INET6) + +static void +ngx_resolver_rbtree_insert_addr6_value(ngx_rbtree_node_t *temp, + ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel) +{ + ngx_rbtree_node_t **p; + ngx_resolver_node_t *rn, *rn_temp; + + for ( ;; ) { + + if (node->key < temp->key) { + + p = &temp->left; + + } else if (node->key > temp->key) { + + p = &temp->right; + + } else { /* node->key == temp->key */ + + rn = ngx_resolver_node(node); + rn_temp = ngx_resolver_node(temp); + + p = (ngx_memcmp(&rn->addr6, &rn_temp->addr6, 16) + < 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); +} + +#endif + + +static ngx_int_t +ngx_resolver_create_name_query(ngx_resolver_t *r, ngx_resolver_node_t *rn, + ngx_str_t *name) +{ + u_char *p, *s; + size_t len, nlen; + ngx_uint_t ident; + ngx_resolver_qs_t *qs; + ngx_resolver_hdr_t *query; + + nlen = name->len ? (1 + name->len + 1) : 1; + + len = sizeof(ngx_resolver_hdr_t) + nlen + sizeof(ngx_resolver_qs_t); + +#if (NGX_HAVE_INET6) + p = ngx_resolver_alloc(r, r->ipv6 ? len * 2 : len); +#else + p = ngx_resolver_alloc(r, len); +#endif + if (p == NULL) { + return NGX_ERROR; + } + + rn->qlen = (u_short) len; + rn->query = p; + +#if (NGX_HAVE_INET6) + if (r->ipv6) { + rn->query6 = p + len; + } +#endif + + query = (ngx_resolver_hdr_t *) p; + + ident = ngx_random(); + + ngx_log_debug2(NGX_LOG_DEBUG_CORE, r->log, 0, + "resolve: \"%V\" A %i", name, ident & 0xffff); + + query->ident_hi = (u_char) ((ident >> 8) & 0xff); + query->ident_lo = (u_char) (ident & 0xff); + + /* recursion query */ + query->flags_hi = 1; query->flags_lo = 0; + + /* one question */ + query->nqs_hi = 0; query->nqs_lo = 1; + query->nan_hi = 0; query->nan_lo = 0; + query->nns_hi = 0; query->nns_lo = 0; + query->nar_hi = 0; query->nar_lo = 0; + + p += sizeof(ngx_resolver_hdr_t) + nlen; + + qs = (ngx_resolver_qs_t *) p; + + /* query type */ + qs->type_hi = 0; qs->type_lo = NGX_RESOLVE_A; + + /* IN query class */ + qs->class_hi = 0; qs->class_lo = 1; + + /* convert "www.example.com" to "\3www\7example\3com\0" */ + + len = 0; + p--; + *p-- = '\0'; + + if (name->len == 0) { + return NGX_DECLINED; + } + + for (s = name->data + name->len - 1; s >= name->data; s--) { + if (*s != '.') { + *p = *s; + len++; + + } else { + if (len == 0 || len > 255) { + return NGX_DECLINED; + } + + *p = (u_char) len; + len = 0; + } + + p--; + } + + if (len == 0 || len > 255) { + return NGX_DECLINED; + } + + *p = (u_char) len; + +#if (NGX_HAVE_INET6) + if (!r->ipv6) { + return NGX_OK; + } + + p = rn->query6; + + ngx_memcpy(p, rn->query, rn->qlen); + + query = (ngx_resolver_hdr_t *) p; + + ident = ngx_random(); + + ngx_log_debug2(NGX_LOG_DEBUG_CORE, r->log, 0, + "resolve: \"%V\" AAAA %i", name, ident & 0xffff); + + query->ident_hi = (u_char) ((ident >> 8) & 0xff); + query->ident_lo = (u_char) (ident & 0xff); + + p += sizeof(ngx_resolver_hdr_t) + nlen; + + qs = (ngx_resolver_qs_t *) p; + + qs->type_lo = NGX_RESOLVE_AAAA; +#endif + + return NGX_OK; +} + + +static ngx_int_t +ngx_resolver_create_srv_query(ngx_resolver_t *r, ngx_resolver_node_t *rn, + ngx_str_t *name) +{ + u_char *p, *s; + size_t len, nlen; + ngx_uint_t ident; + ngx_resolver_qs_t *qs; + ngx_resolver_hdr_t *query; + + nlen = name->len ? (1 + name->len + 1) : 1; + + len = sizeof(ngx_resolver_hdr_t) + nlen + sizeof(ngx_resolver_qs_t); + + p = ngx_resolver_alloc(r, len); + if (p == NULL) { + return NGX_ERROR; + } + + rn->qlen = (u_short) len; + rn->query = p; + + query = (ngx_resolver_hdr_t *) p; + + ident = ngx_random(); + + ngx_log_debug2(NGX_LOG_DEBUG_CORE, r->log, 0, + "resolve: \"%V\" SRV %i", name, ident & 0xffff); + + query->ident_hi = (u_char) ((ident >> 8) & 0xff); + query->ident_lo = (u_char) (ident & 0xff); + + /* recursion query */ + query->flags_hi = 1; query->flags_lo = 0; + + /* one question */ + query->nqs_hi = 0; query->nqs_lo = 1; + query->nan_hi = 0; query->nan_lo = 0; + query->nns_hi = 0; query->nns_lo = 0; + query->nar_hi = 0; query->nar_lo = 0; + + p += sizeof(ngx_resolver_hdr_t) + nlen; + + qs = (ngx_resolver_qs_t *) p; + + /* query type */ + qs->type_hi = 0; qs->type_lo = NGX_RESOLVE_SRV; + + /* IN query class */ + qs->class_hi = 0; qs->class_lo = 1; + + /* converts "www.example.com" to "\3www\7example\3com\0" */ + + len = 0; + p--; + *p-- = '\0'; + + if (name->len == 0) { + return NGX_DECLINED; + } + + for (s = name->data + name->len - 1; s >= name->data; s--) { + if (*s != '.') { + *p = *s; + len++; + + } else { + if (len == 0 || len > 255) { + return NGX_DECLINED; + } + + *p = (u_char) len; + len = 0; + } + + p--; + } + + if (len == 0 || len > 255) { + return NGX_DECLINED; + } + + *p = (u_char) len; + + return NGX_OK; +} + + +static ngx_int_t +ngx_resolver_create_addr_query(ngx_resolver_t *r, ngx_resolver_node_t *rn, + ngx_resolver_addr_t *addr) +{ + u_char *p, *d; + size_t len; + in_addr_t inaddr; + ngx_int_t n; + ngx_uint_t ident; + ngx_resolver_hdr_t *query; + struct sockaddr_in *sin; +#if (NGX_HAVE_INET6) + struct sockaddr_in6 *sin6; +#endif + + switch (addr->sockaddr->sa_family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + len = sizeof(ngx_resolver_hdr_t) + + 64 + sizeof(".ip6.arpa.") - 1 + + sizeof(ngx_resolver_qs_t); + + break; +#endif + + default: /* AF_INET */ + len = sizeof(ngx_resolver_hdr_t) + + sizeof(".255.255.255.255.in-addr.arpa.") - 1 + + sizeof(ngx_resolver_qs_t); + } + + p = ngx_resolver_alloc(r, len); + if (p == NULL) { + return NGX_ERROR; + } + + rn->query = p; + query = (ngx_resolver_hdr_t *) p; + + ident = ngx_random(); + + query->ident_hi = (u_char) ((ident >> 8) & 0xff); + query->ident_lo = (u_char) (ident & 0xff); + + /* recursion query */ + query->flags_hi = 1; query->flags_lo = 0; + + /* one question */ + query->nqs_hi = 0; query->nqs_lo = 1; + query->nan_hi = 0; query->nan_lo = 0; + query->nns_hi = 0; query->nns_lo = 0; + query->nar_hi = 0; query->nar_lo = 0; + + p += sizeof(ngx_resolver_hdr_t); + + switch (addr->sockaddr->sa_family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + sin6 = (struct sockaddr_in6 *) addr->sockaddr; + + for (n = 15; n >= 0; n--) { + p = ngx_sprintf(p, "\1%xd\1%xd", + sin6->sin6_addr.s6_addr[n] & 0xf, + (sin6->sin6_addr.s6_addr[n] >> 4) & 0xf); + } + + p = ngx_cpymem(p, "\3ip6\4arpa\0", 10); + + break; +#endif + + default: /* AF_INET */ + + sin = (struct sockaddr_in *) addr->sockaddr; + inaddr = ntohl(sin->sin_addr.s_addr); + + for (n = 0; n < 32; n += 8) { + d = ngx_sprintf(&p[1], "%ud", (inaddr >> n) & 0xff); + *p = (u_char) (d - &p[1]); + p = d; + } + + p = ngx_cpymem(p, "\7in-addr\4arpa\0", 14); + } + + /* query type "PTR", IN query class */ + p = ngx_cpymem(p, "\0\14\0\1", 4); + + rn->qlen = (u_short) (p - rn->query); + + return NGX_OK; +} + + +static ngx_int_t +ngx_resolver_copy(ngx_resolver_t *r, ngx_str_t *name, u_char *buf, u_char *src, + u_char *last) +{ + char *err; + u_char *p, *dst; + ssize_t len; + ngx_uint_t i, n; + + p = src; + len = -1; + + /* + * compression pointers allow to create endless loop, so we set limit; + * 128 pointers should be enough to store 255-byte name + */ + + for (i = 0; i < 128; i++) { + n = *p++; + + if (n == 0) { + goto done; + } + + if (n & 0xc0) { + n = ((n & 0x3f) << 8) + *p; + p = &buf[n]; + + } else { + len += 1 + n; + p = &p[n]; + } + + if (p >= last) { + err = "name is out of response"; + goto invalid; + } + } + + err = "compression pointers loop"; + +invalid: + + ngx_log_error(r->log_level, r->log, 0, err); + + return NGX_ERROR; + +done: + + if (name == NULL) { + return NGX_OK; + } + + if (len == -1) { + ngx_str_null(name); + return NGX_OK; + } + + dst = ngx_resolver_alloc(r, len); + if (dst == NULL) { + return NGX_ERROR; + } + + name->data = dst; + + n = *src++; + + for ( ;; ) { + if (n & 0xc0) { + n = ((n & 0x3f) << 8) + *src; + src = &buf[n]; + + n = *src++; + + } else { + ngx_strlow(dst, src, n); + dst += n; + src += n; + + n = *src++; + + if (n != 0) { + *dst++ = '.'; + } + } + + if (n == 0) { + name->len = dst - name->data; + return NGX_OK; + } + } +} + + +static void +ngx_resolver_timeout_handler(ngx_event_t *ev) +{ + ngx_resolver_ctx_t *ctx; + + ctx = ev->data; + + ctx->state = NGX_RESOLVE_TIMEDOUT; + + ctx->handler(ctx); +} + + +static void +ngx_resolver_free_node(ngx_resolver_t *r, ngx_resolver_node_t *rn) +{ + ngx_uint_t i; + + /* lock alloc mutex */ + + if (rn->query) { + ngx_resolver_free_locked(r, rn->query); + } + + if (rn->name) { + ngx_resolver_free_locked(r, rn->name); + } + + if (rn->cnlen) { + ngx_resolver_free_locked(r, rn->u.cname); + } + + if (rn->naddrs > 1 && rn->naddrs != (u_short) -1) { + ngx_resolver_free_locked(r, rn->u.addrs); + } + +#if (NGX_HAVE_INET6) + if (rn->naddrs6 > 1 && rn->naddrs6 != (u_short) -1) { + ngx_resolver_free_locked(r, rn->u6.addrs6); + } +#endif + + if (rn->nsrvs) { + for (i = 0; i < (ngx_uint_t) rn->nsrvs; i++) { + if (rn->u.srvs[i].name.data) { + ngx_resolver_free_locked(r, rn->u.srvs[i].name.data); + } + } + + ngx_resolver_free_locked(r, rn->u.srvs); + } + + ngx_resolver_free_locked(r, rn); + + /* unlock alloc mutex */ +} + + +static void * +ngx_resolver_alloc(ngx_resolver_t *r, size_t size) +{ + u_char *p; + + /* lock alloc mutex */ + + p = ngx_alloc(size, r->log); + + /* unlock alloc mutex */ + + return p; +} + + +static void * +ngx_resolver_calloc(ngx_resolver_t *r, size_t size) +{ + u_char *p; + + p = ngx_resolver_alloc(r, size); + + if (p) { + ngx_memzero(p, size); + } + + return p; +} + + +static void +ngx_resolver_free(ngx_resolver_t *r, void *p) +{ + /* lock alloc mutex */ + + ngx_free(p); + + /* unlock alloc mutex */ +} + + +static void +ngx_resolver_free_locked(ngx_resolver_t *r, void *p) +{ + ngx_free(p); +} + + +static void * +ngx_resolver_dup(ngx_resolver_t *r, void *src, size_t size) +{ + void *dst; + + dst = ngx_resolver_alloc(r, size); + + if (dst == NULL) { + return dst; + } + + ngx_memcpy(dst, src, size); + + return dst; +} + + +static ngx_resolver_addr_t * +ngx_resolver_export(ngx_resolver_t *r, ngx_resolver_node_t *rn, + ngx_uint_t rotate) +{ + ngx_uint_t d, i, j, n; + in_addr_t *addr; + ngx_sockaddr_t *sockaddr; + struct sockaddr_in *sin; + ngx_resolver_addr_t *dst; +#if (NGX_HAVE_INET6) + struct in6_addr *addr6; + struct sockaddr_in6 *sin6; +#endif + + n = rn->naddrs; +#if (NGX_HAVE_INET6) + n += rn->naddrs6; +#endif + + dst = ngx_resolver_calloc(r, n * sizeof(ngx_resolver_addr_t)); + if (dst == NULL) { + return NULL; + } + + sockaddr = ngx_resolver_calloc(r, n * sizeof(ngx_sockaddr_t)); + if (sockaddr == NULL) { + ngx_resolver_free(r, dst); + return NULL; + } + + i = 0; + d = rotate ? ngx_random() % n : 0; + + if (rn->naddrs) { + j = rotate ? ngx_random() % rn->naddrs : 0; + + addr = (rn->naddrs == 1) ? &rn->u.addr : rn->u.addrs; + + do { + sin = &sockaddr[d].sockaddr_in; + sin->sin_family = AF_INET; + sin->sin_addr.s_addr = addr[j++]; + dst[d].sockaddr = (struct sockaddr *) sin; + dst[d++].socklen = sizeof(struct sockaddr_in); + + if (d == n) { + d = 0; + } + + if (j == (ngx_uint_t) rn->naddrs) { + j = 0; + } + } while (++i < (ngx_uint_t) rn->naddrs); + } + +#if (NGX_HAVE_INET6) + if (rn->naddrs6) { + j = rotate ? ngx_random() % rn->naddrs6 : 0; + + addr6 = (rn->naddrs6 == 1) ? &rn->u6.addr6 : rn->u6.addrs6; + + do { + sin6 = &sockaddr[d].sockaddr_in6; + sin6->sin6_family = AF_INET6; + ngx_memcpy(sin6->sin6_addr.s6_addr, addr6[j++].s6_addr, 16); + dst[d].sockaddr = (struct sockaddr *) sin6; + dst[d++].socklen = sizeof(struct sockaddr_in6); + + if (d == n) { + d = 0; + } + + if (j == rn->naddrs6) { + j = 0; + } + } while (++i < n); + } +#endif + + return dst; +} + + +static void +ngx_resolver_report_srv(ngx_resolver_t *r, ngx_resolver_ctx_t *ctx) +{ + ngx_uint_t naddrs, nsrvs, nw, i, j, k, l, m, n, w; + ngx_resolver_addr_t *addrs; + ngx_resolver_srv_name_t *srvs; + + naddrs = 0; + + for (i = 0; i < ctx->nsrvs; i++) { + naddrs += ctx->srvs[i].naddrs; + } + + if (naddrs == 0) { + ctx->state = NGX_RESOLVE_NXDOMAIN; + ctx->valid = ngx_time() + (r->valid ? r->valid : 10); + + ctx->handler(ctx); + return; + } + + addrs = ngx_resolver_calloc(r, naddrs * sizeof(ngx_resolver_addr_t)); + if (addrs == NULL) { + ctx->state = NGX_ERROR; + ctx->valid = ngx_time() + (r->valid ? r->valid : 10); + + ctx->handler(ctx); + return; + } + + srvs = ctx->srvs; + nsrvs = ctx->nsrvs; + + i = 0; + n = 0; + + do { + nw = 0; + + for (j = i; j < nsrvs; j++) { + if (srvs[j].priority != srvs[i].priority) { + break; + } + + nw += srvs[j].naddrs * srvs[j].weight; + } + + if (nw == 0) { + goto next_srv; + } + + w = ngx_random() % nw; + + for (k = i; k < j; k++) { + if (w < srvs[k].naddrs * srvs[k].weight) { + break; + } + + w -= srvs[k].naddrs * srvs[k].weight; + } + + for (l = i; l < j; l++) { + + for (m = 0; m < srvs[k].naddrs; m++) { + addrs[n].socklen = srvs[k].addrs[m].socklen; + addrs[n].sockaddr = srvs[k].addrs[m].sockaddr; + addrs[n].name = srvs[k].name; + addrs[n].priority = srvs[k].priority; + addrs[n].weight = srvs[k].weight; + n++; + } + + if (++k == j) { + k = i; + } + } + +next_srv: + + i = j; + + } while (i < ctx->nsrvs); + + ctx->state = NGX_OK; + ctx->addrs = addrs; + ctx->naddrs = naddrs; + + ctx->handler(ctx); + + ngx_resolver_free(r, addrs); +} + + +char * +ngx_resolver_strerror(ngx_int_t err) +{ + static char *errors[] = { + "Format error", /* FORMERR */ + "Server failure", /* SERVFAIL */ + "Host not found", /* NXDOMAIN */ + "Unimplemented", /* NOTIMP */ + "Operation refused" /* REFUSED */ + }; + + if (err > 0 && err < 6) { + return errors[err - 1]; + } + + if (err == NGX_RESOLVE_TIMEDOUT) { + return "Operation timed out"; + } + + return "Unknown error"; +} + + +static u_char * +ngx_resolver_log_error(ngx_log_t *log, u_char *buf, size_t len) +{ + u_char *p; + ngx_resolver_connection_t *rec; + + p = buf; + + if (log->action) { + p = ngx_snprintf(buf, len, " while %s", log->action); + len -= p - buf; + } + + rec = log->data; + + if (rec) { + p = ngx_snprintf(p, len, ", resolver: %V", &rec->server); + } + + return p; +} + + +static ngx_int_t +ngx_udp_connect(ngx_resolver_connection_t *rec) +{ + int rc; + ngx_int_t event; + ngx_event_t *rev, *wev; + ngx_socket_t s; + ngx_connection_t *c; + + s = ngx_socket(rec->sockaddr->sa_family, SOCK_DGRAM, 0); + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, &rec->log, 0, "UDP socket %d", s); + + if (s == (ngx_socket_t) -1) { + ngx_log_error(NGX_LOG_ALERT, &rec->log, ngx_socket_errno, + ngx_socket_n " failed"); + return NGX_ERROR; + } + + c = ngx_get_connection(s, &rec->log); + + if (c == NULL) { + if (ngx_close_socket(s) == -1) { + ngx_log_error(NGX_LOG_ALERT, &rec->log, ngx_socket_errno, + ngx_close_socket_n "failed"); + } + + return NGX_ERROR; + } + + if (ngx_nonblocking(s) == -1) { + ngx_log_error(NGX_LOG_ALERT, &rec->log, ngx_socket_errno, + ngx_nonblocking_n " failed"); + + goto failed; + } + + rev = c->read; + wev = c->write; + + rev->log = &rec->log; + wev->log = &rec->log; + + rec->udp = c; + + c->number = ngx_atomic_fetch_add(ngx_connection_counter, 1); + + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, &rec->log, 0, + "connect to %V, fd:%d #%uA", &rec->server, s, c->number); + + rc = connect(s, rec->sockaddr, rec->socklen); + + /* TODO: iocp */ + + if (rc == -1) { + ngx_log_error(NGX_LOG_CRIT, &rec->log, ngx_socket_errno, + "connect() failed"); + + goto failed; + } + + /* UDP sockets are always ready to write */ + wev->ready = 1; + + event = (ngx_event_flags & NGX_USE_CLEAR_EVENT) ? + /* kqueue, epoll */ NGX_CLEAR_EVENT: + /* select, poll, /dev/poll */ NGX_LEVEL_EVENT; + /* eventport event type has no meaning: oneshot only */ + + if (ngx_add_event(rev, NGX_READ_EVENT, event) != NGX_OK) { + goto failed; + } + + return NGX_OK; + +failed: + + ngx_close_connection(c); + rec->udp = NULL; + + return NGX_ERROR; +} + + +static ngx_int_t +ngx_tcp_connect(ngx_resolver_connection_t *rec) +{ + int rc; + ngx_int_t event; + ngx_err_t err; + ngx_uint_t level; + ngx_socket_t s; + ngx_event_t *rev, *wev; + ngx_connection_t *c; + + s = ngx_socket(rec->sockaddr->sa_family, SOCK_STREAM, 0); + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, &rec->log, 0, "TCP socket %d", s); + + if (s == (ngx_socket_t) -1) { + ngx_log_error(NGX_LOG_ALERT, &rec->log, ngx_socket_errno, + ngx_socket_n " failed"); + return NGX_ERROR; + } + + c = ngx_get_connection(s, &rec->log); + + if (c == NULL) { + if (ngx_close_socket(s) == -1) { + ngx_log_error(NGX_LOG_ALERT, &rec->log, ngx_socket_errno, + ngx_close_socket_n "failed"); + } + + return NGX_ERROR; + } + + if (ngx_nonblocking(s) == -1) { + ngx_log_error(NGX_LOG_ALERT, &rec->log, ngx_socket_errno, + ngx_nonblocking_n " failed"); + + goto failed; + } + + rev = c->read; + wev = c->write; + + rev->log = &rec->log; + wev->log = &rec->log; + + rec->tcp = c; + + c->number = ngx_atomic_fetch_add(ngx_connection_counter, 1); + + if (ngx_add_conn) { + if (ngx_add_conn(c) == NGX_ERROR) { + goto failed; + } + } + + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, &rec->log, 0, + "connect to %V, fd:%d #%uA", &rec->server, s, c->number); + + rc = connect(s, rec->sockaddr, rec->socklen); + + if (rc == -1) { + err = ngx_socket_errno; + + + if (err != NGX_EINPROGRESS +#if (NGX_WIN32) + /* Winsock returns WSAEWOULDBLOCK (NGX_EAGAIN) */ + && err != NGX_EAGAIN +#endif + ) + { + if (err == NGX_ECONNREFUSED +#if (NGX_LINUX) + /* + * Linux returns EAGAIN instead of ECONNREFUSED + * for unix sockets if listen queue is full + */ + || err == NGX_EAGAIN +#endif + || err == NGX_ECONNRESET + || err == NGX_ENETDOWN + || err == NGX_ENETUNREACH + || err == NGX_EHOSTDOWN + || err == NGX_EHOSTUNREACH) + { + level = NGX_LOG_ERR; + + } else { + level = NGX_LOG_CRIT; + } + + ngx_log_error(level, c->log, err, "connect() to %V failed", + &rec->server); + + ngx_close_connection(c); + rec->tcp = NULL; + + return NGX_ERROR; + } + } + + if (ngx_add_conn) { + if (rc == -1) { + + /* NGX_EINPROGRESS */ + + return NGX_AGAIN; + } + + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, &rec->log, 0, "connected"); + + wev->ready = 1; + + return NGX_OK; + } + + if (ngx_event_flags & NGX_USE_IOCP_EVENT) { + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, &rec->log, ngx_socket_errno, + "connect(): %d", rc); + + if (ngx_blocking(s) == -1) { + ngx_log_error(NGX_LOG_ALERT, &rec->log, ngx_socket_errno, + ngx_blocking_n " failed"); + goto failed; + } + + /* + * FreeBSD's aio allows to post an operation on non-connected socket. + * NT does not support it. + * + * TODO: check in Win32, etc. As workaround we can use NGX_ONESHOT_EVENT + */ + + rev->ready = 1; + wev->ready = 1; + + return NGX_OK; + } + + if (ngx_event_flags & NGX_USE_CLEAR_EVENT) { + + /* kqueue */ + + event = NGX_CLEAR_EVENT; + + } else { + + /* select, poll, /dev/poll */ + + event = NGX_LEVEL_EVENT; + } + + if (ngx_add_event(rev, NGX_READ_EVENT, event) != NGX_OK) { + goto failed; + } + + if (rc == -1) { + + /* NGX_EINPROGRESS */ + + if (ngx_add_event(wev, NGX_WRITE_EVENT, event) != NGX_OK) { + goto failed; + } + + return NGX_AGAIN; + } + + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, &rec->log, 0, "connected"); + + wev->ready = 1; + + return NGX_OK; + +failed: + + ngx_close_connection(c); + rec->tcp = NULL; + + return NGX_ERROR; +} + + +static ngx_int_t +ngx_resolver_cmp_srvs(const void *one, const void *two) +{ + ngx_int_t p1, p2; + ngx_resolver_srv_t *first, *second; + + first = (ngx_resolver_srv_t *) one; + second = (ngx_resolver_srv_t *) two; + + p1 = first->priority; + p2 = second->priority; + + return p1 - p2; +} diff --git a/app/nginx/src/core/ngx_resolver.h b/app/nginx/src/core/ngx_resolver.h new file mode 100644 index 0000000..a0d6fc3 --- /dev/null +++ b/app/nginx/src/core/ngx_resolver.h @@ -0,0 +1,238 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include + + +#ifndef _NGX_RESOLVER_H_INCLUDED_ +#define _NGX_RESOLVER_H_INCLUDED_ + + +#define NGX_RESOLVE_A 1 +#define NGX_RESOLVE_CNAME 5 +#define NGX_RESOLVE_PTR 12 +#define NGX_RESOLVE_MX 15 +#define NGX_RESOLVE_TXT 16 +#if (NGX_HAVE_INET6) +#define NGX_RESOLVE_AAAA 28 +#endif +#define NGX_RESOLVE_SRV 33 +#define NGX_RESOLVE_DNAME 39 + +#define NGX_RESOLVE_FORMERR 1 +#define NGX_RESOLVE_SERVFAIL 2 +#define NGX_RESOLVE_NXDOMAIN 3 +#define NGX_RESOLVE_NOTIMP 4 +#define NGX_RESOLVE_REFUSED 5 +#define NGX_RESOLVE_TIMEDOUT NGX_ETIMEDOUT + + +#define NGX_NO_RESOLVER (void *) -1 + +#define NGX_RESOLVER_MAX_RECURSION 50 + + +typedef struct ngx_resolver_s ngx_resolver_t; + + +typedef struct { + ngx_connection_t *udp; + ngx_connection_t *tcp; + struct sockaddr *sockaddr; + socklen_t socklen; + ngx_str_t server; + ngx_log_t log; + ngx_buf_t *read_buf; + ngx_buf_t *write_buf; + ngx_resolver_t *resolver; +} ngx_resolver_connection_t; + + +typedef struct ngx_resolver_ctx_s ngx_resolver_ctx_t; + +typedef void (*ngx_resolver_handler_pt)(ngx_resolver_ctx_t *ctx); + + +typedef struct { + struct sockaddr *sockaddr; + socklen_t socklen; + ngx_str_t name; + u_short priority; + u_short weight; +} ngx_resolver_addr_t; + + +typedef struct { + ngx_str_t name; + u_short priority; + u_short weight; + u_short port; +} ngx_resolver_srv_t; + + +typedef struct { + ngx_str_t name; + u_short priority; + u_short weight; + u_short port; + + ngx_resolver_ctx_t *ctx; + ngx_int_t state; + + ngx_uint_t naddrs; + ngx_addr_t *addrs; +} ngx_resolver_srv_name_t; + + +typedef struct { + ngx_rbtree_node_t node; + ngx_queue_t queue; + + /* PTR: resolved name, A: name to resolve */ + u_char *name; + +#if (NGX_HAVE_INET6) + /* PTR: IPv6 address to resolve (IPv4 address is in rbtree node key) */ + struct in6_addr addr6; +#endif + + u_short nlen; + u_short qlen; + + u_char *query; +#if (NGX_HAVE_INET6) + u_char *query6; +#endif + + union { + in_addr_t addr; + in_addr_t *addrs; + u_char *cname; + ngx_resolver_srv_t *srvs; + } u; + + u_char code; + u_short naddrs; + u_short nsrvs; + u_short cnlen; + +#if (NGX_HAVE_INET6) + union { + struct in6_addr addr6; + struct in6_addr *addrs6; + } u6; + + u_short naddrs6; +#endif + + time_t expire; + time_t valid; + uint32_t ttl; + + unsigned tcp:1; +#if (NGX_HAVE_INET6) + unsigned tcp6:1; +#endif + + ngx_uint_t last_connection; + + ngx_resolver_ctx_t *waiting; +} ngx_resolver_node_t; + + +struct ngx_resolver_s { + /* has to be pointer because of "incomplete type" */ + ngx_event_t *event; + void *dummy; + ngx_log_t *log; + + /* event ident must be after 3 pointers as in ngx_connection_t */ + ngx_int_t ident; + + /* simple round robin DNS peers balancer */ + ngx_array_t connections; + ngx_uint_t last_connection; + + ngx_rbtree_t name_rbtree; + ngx_rbtree_node_t name_sentinel; + + ngx_rbtree_t srv_rbtree; + ngx_rbtree_node_t srv_sentinel; + + ngx_rbtree_t addr_rbtree; + ngx_rbtree_node_t addr_sentinel; + + ngx_queue_t name_resend_queue; + ngx_queue_t srv_resend_queue; + ngx_queue_t addr_resend_queue; + + ngx_queue_t name_expire_queue; + ngx_queue_t srv_expire_queue; + ngx_queue_t addr_expire_queue; + +#if (NGX_HAVE_INET6) + ngx_uint_t ipv6; /* unsigned ipv6:1; */ + ngx_rbtree_t addr6_rbtree; + ngx_rbtree_node_t addr6_sentinel; + ngx_queue_t addr6_resend_queue; + ngx_queue_t addr6_expire_queue; +#endif + + time_t resend_timeout; + time_t tcp_timeout; + time_t expire; + time_t valid; + + ngx_uint_t log_level; +}; + + +struct ngx_resolver_ctx_s { + ngx_resolver_ctx_t *next; + ngx_resolver_t *resolver; + ngx_resolver_node_t *node; + + /* event ident must be after 3 pointers as in ngx_connection_t */ + ngx_int_t ident; + + ngx_int_t state; + ngx_str_t name; + ngx_str_t service; + + time_t valid; + ngx_uint_t naddrs; + ngx_resolver_addr_t *addrs; + ngx_resolver_addr_t addr; + struct sockaddr_in sin; + + ngx_uint_t count; + ngx_uint_t nsrvs; + ngx_resolver_srv_name_t *srvs; + + ngx_resolver_handler_pt handler; + void *data; + ngx_msec_t timeout; + + ngx_uint_t quick; /* unsigned quick:1; */ + ngx_uint_t recursion; + ngx_event_t *event; +}; + + +ngx_resolver_t *ngx_resolver_create(ngx_conf_t *cf, ngx_str_t *names, + ngx_uint_t n); +ngx_resolver_ctx_t *ngx_resolve_start(ngx_resolver_t *r, + ngx_resolver_ctx_t *temp); +ngx_int_t ngx_resolve_name(ngx_resolver_ctx_t *ctx); +void ngx_resolve_name_done(ngx_resolver_ctx_t *ctx); +ngx_int_t ngx_resolve_addr(ngx_resolver_ctx_t *ctx); +void ngx_resolve_addr_done(ngx_resolver_ctx_t *ctx); +char *ngx_resolver_strerror(ngx_int_t err); + + +#endif /* _NGX_RESOLVER_H_INCLUDED_ */ diff --git a/app/nginx/src/core/ngx_rwlock.c b/app/nginx/src/core/ngx_rwlock.c new file mode 100644 index 0000000..905de78 --- /dev/null +++ b/app/nginx/src/core/ngx_rwlock.c @@ -0,0 +1,120 @@ + +/* + * Copyright (C) Ruslan Ermilov + * Copyright (C) Nginx, Inc. + */ + + +#include +#include + + +#if (NGX_HAVE_ATOMIC_OPS) + + +#define NGX_RWLOCK_SPIN 2048 +#define NGX_RWLOCK_WLOCK ((ngx_atomic_uint_t) -1) + + +void +ngx_rwlock_wlock(ngx_atomic_t *lock) +{ + ngx_uint_t i, n; + + for ( ;; ) { + + if (*lock == 0 && ngx_atomic_cmp_set(lock, 0, NGX_RWLOCK_WLOCK)) { + return; + } + + if (ngx_ncpu > 1) { + + for (n = 1; n < NGX_RWLOCK_SPIN; n <<= 1) { + + for (i = 0; i < n; i++) { + ngx_cpu_pause(); + } + + if (*lock == 0 + && ngx_atomic_cmp_set(lock, 0, NGX_RWLOCK_WLOCK)) + { + return; + } + } + } + + ngx_sched_yield(); + } +} + + +void +ngx_rwlock_rlock(ngx_atomic_t *lock) +{ + ngx_uint_t i, n; + ngx_atomic_uint_t readers; + + for ( ;; ) { + readers = *lock; + + if (readers != NGX_RWLOCK_WLOCK + && ngx_atomic_cmp_set(lock, readers, readers + 1)) + { + return; + } + + if (ngx_ncpu > 1) { + + for (n = 1; n < NGX_RWLOCK_SPIN; n <<= 1) { + + for (i = 0; i < n; i++) { + ngx_cpu_pause(); + } + + readers = *lock; + + if (readers != NGX_RWLOCK_WLOCK + && ngx_atomic_cmp_set(lock, readers, readers + 1)) + { + return; + } + } + } + + ngx_sched_yield(); + } +} + + +void +ngx_rwlock_unlock(ngx_atomic_t *lock) +{ + ngx_atomic_uint_t readers; + + readers = *lock; + + if (readers == NGX_RWLOCK_WLOCK) { + *lock = 0; + return; + } + + for ( ;; ) { + + if (ngx_atomic_cmp_set(lock, readers, readers - 1)) { + return; + } + + readers = *lock; + } +} + + +#else + +#if (NGX_HTTP_UPSTREAM_ZONE || NGX_STREAM_UPSTREAM_ZONE) + +#error ngx_atomic_cmp_set() is not defined! + +#endif + +#endif diff --git a/app/nginx/src/core/ngx_rwlock.h b/app/nginx/src/core/ngx_rwlock.h new file mode 100644 index 0000000..8b16eca --- /dev/null +++ b/app/nginx/src/core/ngx_rwlock.h @@ -0,0 +1,21 @@ + +/* + * Copyright (C) Ruslan Ermilov + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_RWLOCK_H_INCLUDED_ +#define _NGX_RWLOCK_H_INCLUDED_ + + +#include +#include + + +void ngx_rwlock_wlock(ngx_atomic_t *lock); +void ngx_rwlock_rlock(ngx_atomic_t *lock); +void ngx_rwlock_unlock(ngx_atomic_t *lock); + + +#endif /* _NGX_RWLOCK_H_INCLUDED_ */ diff --git a/app/nginx/src/core/ngx_sha1.c b/app/nginx/src/core/ngx_sha1.c new file mode 100644 index 0000000..f00dc52 --- /dev/null +++ b/app/nginx/src/core/ngx_sha1.c @@ -0,0 +1,294 @@ + +/* + * Copyright (C) Maxim Dounin + * Copyright (C) Nginx, Inc. + * + * An internal SHA1 implementation. + */ + + +#include +#include +#include + + +static const u_char *ngx_sha1_body(ngx_sha1_t *ctx, const u_char *data, + size_t size); + + +void +ngx_sha1_init(ngx_sha1_t *ctx) +{ + ctx->a = 0x67452301; + ctx->b = 0xefcdab89; + ctx->c = 0x98badcfe; + ctx->d = 0x10325476; + ctx->e = 0xc3d2e1f0; + + ctx->bytes = 0; +} + + +void +ngx_sha1_update(ngx_sha1_t *ctx, const void *data, size_t size) +{ + size_t used, free; + + used = (size_t) (ctx->bytes & 0x3f); + ctx->bytes += size; + + if (used) { + free = 64 - used; + + if (size < free) { + ngx_memcpy(&ctx->buffer[used], data, size); + return; + } + + ngx_memcpy(&ctx->buffer[used], data, free); + data = (u_char *) data + free; + size -= free; + (void) ngx_sha1_body(ctx, ctx->buffer, 64); + } + + if (size >= 64) { + data = ngx_sha1_body(ctx, data, size & ~(size_t) 0x3f); + size &= 0x3f; + } + + ngx_memcpy(ctx->buffer, data, size); +} + + +void +ngx_sha1_final(u_char result[20], ngx_sha1_t *ctx) +{ + size_t used, free; + + used = (size_t) (ctx->bytes & 0x3f); + + ctx->buffer[used++] = 0x80; + + free = 64 - used; + + if (free < 8) { + ngx_memzero(&ctx->buffer[used], free); + (void) ngx_sha1_body(ctx, ctx->buffer, 64); + used = 0; + free = 64; + } + + ngx_memzero(&ctx->buffer[used], free - 8); + + ctx->bytes <<= 3; + ctx->buffer[56] = (u_char) (ctx->bytes >> 56); + ctx->buffer[57] = (u_char) (ctx->bytes >> 48); + ctx->buffer[58] = (u_char) (ctx->bytes >> 40); + ctx->buffer[59] = (u_char) (ctx->bytes >> 32); + ctx->buffer[60] = (u_char) (ctx->bytes >> 24); + ctx->buffer[61] = (u_char) (ctx->bytes >> 16); + ctx->buffer[62] = (u_char) (ctx->bytes >> 8); + ctx->buffer[63] = (u_char) ctx->bytes; + + (void) ngx_sha1_body(ctx, ctx->buffer, 64); + + result[0] = (u_char) (ctx->a >> 24); + result[1] = (u_char) (ctx->a >> 16); + result[2] = (u_char) (ctx->a >> 8); + result[3] = (u_char) ctx->a; + result[4] = (u_char) (ctx->b >> 24); + result[5] = (u_char) (ctx->b >> 16); + result[6] = (u_char) (ctx->b >> 8); + result[7] = (u_char) ctx->b; + result[8] = (u_char) (ctx->c >> 24); + result[9] = (u_char) (ctx->c >> 16); + result[10] = (u_char) (ctx->c >> 8); + result[11] = (u_char) ctx->c; + result[12] = (u_char) (ctx->d >> 24); + result[13] = (u_char) (ctx->d >> 16); + result[14] = (u_char) (ctx->d >> 8); + result[15] = (u_char) ctx->d; + result[16] = (u_char) (ctx->e >> 24); + result[17] = (u_char) (ctx->e >> 16); + result[18] = (u_char) (ctx->e >> 8); + result[19] = (u_char) ctx->e; + + ngx_memzero(ctx, sizeof(*ctx)); +} + + +/* + * Helper functions. + */ + +#define ROTATE(bits, word) (((word) << (bits)) | ((word) >> (32 - (bits)))) + +#define F1(b, c, d) (((b) & (c)) | ((~(b)) & (d))) +#define F2(b, c, d) ((b) ^ (c) ^ (d)) +#define F3(b, c, d) (((b) & (c)) | ((b) & (d)) | ((c) & (d))) + +#define STEP(f, a, b, c, d, e, w, t) \ + temp = ROTATE(5, (a)) + f((b), (c), (d)) + (e) + (w) + (t); \ + (e) = (d); \ + (d) = (c); \ + (c) = ROTATE(30, (b)); \ + (b) = (a); \ + (a) = temp; + + +/* + * GET() reads 4 input bytes in big-endian byte order and returns + * them as uint32_t. + */ + +#define GET(n) \ + ((uint32_t) p[n * 4 + 3] | \ + ((uint32_t) p[n * 4 + 2] << 8) | \ + ((uint32_t) p[n * 4 + 1] << 16) | \ + ((uint32_t) p[n * 4] << 24)) + + +/* + * This processes one or more 64-byte data blocks, but does not update + * the bit counters. There are no alignment requirements. + */ + +static const u_char * +ngx_sha1_body(ngx_sha1_t *ctx, const u_char *data, size_t size) +{ + uint32_t a, b, c, d, e, temp; + uint32_t saved_a, saved_b, saved_c, saved_d, saved_e; + uint32_t words[80]; + ngx_uint_t i; + const u_char *p; + + p = data; + + a = ctx->a; + b = ctx->b; + c = ctx->c; + d = ctx->d; + e = ctx->e; + + do { + saved_a = a; + saved_b = b; + saved_c = c; + saved_d = d; + saved_e = e; + + /* Load data block into the words array */ + + for (i = 0; i < 16; i++) { + words[i] = GET(i); + } + + for (i = 16; i < 80; i++) { + words[i] = ROTATE(1, words[i - 3] ^ words[i - 8] ^ words[i - 14] + ^ words[i - 16]); + } + + /* Transformations */ + + STEP(F1, a, b, c, d, e, words[0], 0x5a827999); + STEP(F1, a, b, c, d, e, words[1], 0x5a827999); + STEP(F1, a, b, c, d, e, words[2], 0x5a827999); + STEP(F1, a, b, c, d, e, words[3], 0x5a827999); + STEP(F1, a, b, c, d, e, words[4], 0x5a827999); + STEP(F1, a, b, c, d, e, words[5], 0x5a827999); + STEP(F1, a, b, c, d, e, words[6], 0x5a827999); + STEP(F1, a, b, c, d, e, words[7], 0x5a827999); + STEP(F1, a, b, c, d, e, words[8], 0x5a827999); + STEP(F1, a, b, c, d, e, words[9], 0x5a827999); + STEP(F1, a, b, c, d, e, words[10], 0x5a827999); + STEP(F1, a, b, c, d, e, words[11], 0x5a827999); + STEP(F1, a, b, c, d, e, words[12], 0x5a827999); + STEP(F1, a, b, c, d, e, words[13], 0x5a827999); + STEP(F1, a, b, c, d, e, words[14], 0x5a827999); + STEP(F1, a, b, c, d, e, words[15], 0x5a827999); + STEP(F1, a, b, c, d, e, words[16], 0x5a827999); + STEP(F1, a, b, c, d, e, words[17], 0x5a827999); + STEP(F1, a, b, c, d, e, words[18], 0x5a827999); + STEP(F1, a, b, c, d, e, words[19], 0x5a827999); + + STEP(F2, a, b, c, d, e, words[20], 0x6ed9eba1); + STEP(F2, a, b, c, d, e, words[21], 0x6ed9eba1); + STEP(F2, a, b, c, d, e, words[22], 0x6ed9eba1); + STEP(F2, a, b, c, d, e, words[23], 0x6ed9eba1); + STEP(F2, a, b, c, d, e, words[24], 0x6ed9eba1); + STEP(F2, a, b, c, d, e, words[25], 0x6ed9eba1); + STEP(F2, a, b, c, d, e, words[26], 0x6ed9eba1); + STEP(F2, a, b, c, d, e, words[27], 0x6ed9eba1); + STEP(F2, a, b, c, d, e, words[28], 0x6ed9eba1); + STEP(F2, a, b, c, d, e, words[29], 0x6ed9eba1); + STEP(F2, a, b, c, d, e, words[30], 0x6ed9eba1); + STEP(F2, a, b, c, d, e, words[31], 0x6ed9eba1); + STEP(F2, a, b, c, d, e, words[32], 0x6ed9eba1); + STEP(F2, a, b, c, d, e, words[33], 0x6ed9eba1); + STEP(F2, a, b, c, d, e, words[34], 0x6ed9eba1); + STEP(F2, a, b, c, d, e, words[35], 0x6ed9eba1); + STEP(F2, a, b, c, d, e, words[36], 0x6ed9eba1); + STEP(F2, a, b, c, d, e, words[37], 0x6ed9eba1); + STEP(F2, a, b, c, d, e, words[38], 0x6ed9eba1); + STEP(F2, a, b, c, d, e, words[39], 0x6ed9eba1); + + STEP(F3, a, b, c, d, e, words[40], 0x8f1bbcdc); + STEP(F3, a, b, c, d, e, words[41], 0x8f1bbcdc); + STEP(F3, a, b, c, d, e, words[42], 0x8f1bbcdc); + STEP(F3, a, b, c, d, e, words[43], 0x8f1bbcdc); + STEP(F3, a, b, c, d, e, words[44], 0x8f1bbcdc); + STEP(F3, a, b, c, d, e, words[45], 0x8f1bbcdc); + STEP(F3, a, b, c, d, e, words[46], 0x8f1bbcdc); + STEP(F3, a, b, c, d, e, words[47], 0x8f1bbcdc); + STEP(F3, a, b, c, d, e, words[48], 0x8f1bbcdc); + STEP(F3, a, b, c, d, e, words[49], 0x8f1bbcdc); + STEP(F3, a, b, c, d, e, words[50], 0x8f1bbcdc); + STEP(F3, a, b, c, d, e, words[51], 0x8f1bbcdc); + STEP(F3, a, b, c, d, e, words[52], 0x8f1bbcdc); + STEP(F3, a, b, c, d, e, words[53], 0x8f1bbcdc); + STEP(F3, a, b, c, d, e, words[54], 0x8f1bbcdc); + STEP(F3, a, b, c, d, e, words[55], 0x8f1bbcdc); + STEP(F3, a, b, c, d, e, words[56], 0x8f1bbcdc); + STEP(F3, a, b, c, d, e, words[57], 0x8f1bbcdc); + STEP(F3, a, b, c, d, e, words[58], 0x8f1bbcdc); + STEP(F3, a, b, c, d, e, words[59], 0x8f1bbcdc); + + STEP(F2, a, b, c, d, e, words[60], 0xca62c1d6); + STEP(F2, a, b, c, d, e, words[61], 0xca62c1d6); + STEP(F2, a, b, c, d, e, words[62], 0xca62c1d6); + STEP(F2, a, b, c, d, e, words[63], 0xca62c1d6); + STEP(F2, a, b, c, d, e, words[64], 0xca62c1d6); + STEP(F2, a, b, c, d, e, words[65], 0xca62c1d6); + STEP(F2, a, b, c, d, e, words[66], 0xca62c1d6); + STEP(F2, a, b, c, d, e, words[67], 0xca62c1d6); + STEP(F2, a, b, c, d, e, words[68], 0xca62c1d6); + STEP(F2, a, b, c, d, e, words[69], 0xca62c1d6); + STEP(F2, a, b, c, d, e, words[70], 0xca62c1d6); + STEP(F2, a, b, c, d, e, words[71], 0xca62c1d6); + STEP(F2, a, b, c, d, e, words[72], 0xca62c1d6); + STEP(F2, a, b, c, d, e, words[73], 0xca62c1d6); + STEP(F2, a, b, c, d, e, words[74], 0xca62c1d6); + STEP(F2, a, b, c, d, e, words[75], 0xca62c1d6); + STEP(F2, a, b, c, d, e, words[76], 0xca62c1d6); + STEP(F2, a, b, c, d, e, words[77], 0xca62c1d6); + STEP(F2, a, b, c, d, e, words[78], 0xca62c1d6); + STEP(F2, a, b, c, d, e, words[79], 0xca62c1d6); + + a += saved_a; + b += saved_b; + c += saved_c; + d += saved_d; + e += saved_e; + + p += 64; + + } while (size -= 64); + + ctx->a = a; + ctx->b = b; + ctx->c = c; + ctx->d = d; + ctx->e = e; + + return p; +} diff --git a/app/nginx/src/core/ngx_sha1.h b/app/nginx/src/core/ngx_sha1.h new file mode 100644 index 0000000..4a98f71 --- /dev/null +++ b/app/nginx/src/core/ngx_sha1.h @@ -0,0 +1,28 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_SHA1_H_INCLUDED_ +#define _NGX_SHA1_H_INCLUDED_ + + +#include +#include + + +typedef struct { + uint64_t bytes; + uint32_t a, b, c, d, e, f; + u_char buffer[64]; +} ngx_sha1_t; + + +void ngx_sha1_init(ngx_sha1_t *ctx); +void ngx_sha1_update(ngx_sha1_t *ctx, const void *data, size_t size); +void ngx_sha1_final(u_char result[20], ngx_sha1_t *ctx); + + +#endif /* _NGX_SHA1_H_INCLUDED_ */ diff --git a/app/nginx/src/core/ngx_shmtx.c b/app/nginx/src/core/ngx_shmtx.c new file mode 100644 index 0000000..a255903 --- /dev/null +++ b/app/nginx/src/core/ngx_shmtx.c @@ -0,0 +1,310 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include + + +#if (NGX_HAVE_ATOMIC_OPS) + + +static void ngx_shmtx_wakeup(ngx_shmtx_t *mtx); + + +ngx_int_t +ngx_shmtx_create(ngx_shmtx_t *mtx, ngx_shmtx_sh_t *addr, u_char *name) +{ + mtx->lock = &addr->lock; + + if (mtx->spin == (ngx_uint_t) -1) { + return NGX_OK; + } + + mtx->spin = 2048; + +#if (NGX_HAVE_POSIX_SEM) + + mtx->wait = &addr->wait; + + if (sem_init(&mtx->sem, 1, 0) == -1) { + ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_errno, + "sem_init() failed"); + } else { + mtx->semaphore = 1; + } + +#endif + + return NGX_OK; +} + + +void +ngx_shmtx_destroy(ngx_shmtx_t *mtx) +{ +#if (NGX_HAVE_POSIX_SEM) + + if (mtx->semaphore) { + if (sem_destroy(&mtx->sem) == -1) { + ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_errno, + "sem_destroy() failed"); + } + } + +#endif +} + + +ngx_uint_t +ngx_shmtx_trylock(ngx_shmtx_t *mtx) +{ + return (*mtx->lock == 0 && ngx_atomic_cmp_set(mtx->lock, 0, ngx_pid)); +} + + +void +ngx_shmtx_lock(ngx_shmtx_t *mtx) +{ + ngx_uint_t i, n; + + ngx_log_debug0(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0, "shmtx lock"); + + for ( ;; ) { + + if (*mtx->lock == 0 && ngx_atomic_cmp_set(mtx->lock, 0, ngx_pid)) { + return; + } + + if (ngx_ncpu > 1) { + + for (n = 1; n < mtx->spin; n <<= 1) { + + for (i = 0; i < n; i++) { + ngx_cpu_pause(); + } + + if (*mtx->lock == 0 + && ngx_atomic_cmp_set(mtx->lock, 0, ngx_pid)) + { + return; + } + } + } + +#if (NGX_HAVE_POSIX_SEM) + + if (mtx->semaphore) { + (void) ngx_atomic_fetch_add(mtx->wait, 1); + + if (*mtx->lock == 0 && ngx_atomic_cmp_set(mtx->lock, 0, ngx_pid)) { + (void) ngx_atomic_fetch_add(mtx->wait, -1); + return; + } + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0, + "shmtx wait %uA", *mtx->wait); + + while (sem_wait(&mtx->sem) == -1) { + ngx_err_t err; + + err = ngx_errno; + + if (err != NGX_EINTR) { + ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, err, + "sem_wait() failed while waiting on shmtx"); + break; + } + } + + ngx_log_debug0(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0, + "shmtx awoke"); + + continue; + } + +#endif + + ngx_sched_yield(); + } +} + + +void +ngx_shmtx_unlock(ngx_shmtx_t *mtx) +{ + if (mtx->spin != (ngx_uint_t) -1) { + ngx_log_debug0(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0, "shmtx unlock"); + } + + if (ngx_atomic_cmp_set(mtx->lock, ngx_pid, 0)) { + ngx_shmtx_wakeup(mtx); + } +} + + +ngx_uint_t +ngx_shmtx_force_unlock(ngx_shmtx_t *mtx, ngx_pid_t pid) +{ + ngx_log_debug0(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0, + "shmtx forced unlock"); + + if (ngx_atomic_cmp_set(mtx->lock, pid, 0)) { + ngx_shmtx_wakeup(mtx); + return 1; + } + + return 0; +} + + +static void +ngx_shmtx_wakeup(ngx_shmtx_t *mtx) +{ +#if (NGX_HAVE_POSIX_SEM) + ngx_atomic_uint_t wait; + + if (!mtx->semaphore) { + return; + } + + for ( ;; ) { + + wait = *mtx->wait; + + if ((ngx_atomic_int_t) wait <= 0) { + return; + } + + if (ngx_atomic_cmp_set(mtx->wait, wait, wait - 1)) { + break; + } + } + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0, + "shmtx wake %uA", wait); + + if (sem_post(&mtx->sem) == -1) { + ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_errno, + "sem_post() failed while wake shmtx"); + } + +#endif +} + + +#else + + +ngx_int_t +ngx_shmtx_create(ngx_shmtx_t *mtx, ngx_shmtx_sh_t *addr, u_char *name) +{ + if (mtx->name) { + + if (ngx_strcmp(name, mtx->name) == 0) { + mtx->name = name; + return NGX_OK; + } + + ngx_shmtx_destroy(mtx); + } + + mtx->fd = ngx_open_file(name, NGX_FILE_RDWR, NGX_FILE_CREATE_OR_OPEN, + NGX_FILE_DEFAULT_ACCESS); + + if (mtx->fd == NGX_INVALID_FILE) { + ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno, + ngx_open_file_n " \"%s\" failed", name); + return NGX_ERROR; + } + + if (ngx_delete_file(name) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_errno, + ngx_delete_file_n " \"%s\" failed", name); + } + + mtx->name = name; + + return NGX_OK; +} + + +void +ngx_shmtx_destroy(ngx_shmtx_t *mtx) +{ + if (ngx_close_file(mtx->fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_errno, + ngx_close_file_n " \"%s\" failed", mtx->name); + } +} + + +ngx_uint_t +ngx_shmtx_trylock(ngx_shmtx_t *mtx) +{ + ngx_err_t err; + + err = ngx_trylock_fd(mtx->fd); + + if (err == 0) { + return 1; + } + + if (err == NGX_EAGAIN) { + return 0; + } + +#if __osf__ /* Tru64 UNIX */ + + if (err == NGX_EACCES) { + return 0; + } + +#endif + + ngx_log_abort(err, ngx_trylock_fd_n " %s failed", mtx->name); + + return 0; +} + + +void +ngx_shmtx_lock(ngx_shmtx_t *mtx) +{ + ngx_err_t err; + + err = ngx_lock_fd(mtx->fd); + + if (err == 0) { + return; + } + + ngx_log_abort(err, ngx_lock_fd_n " %s failed", mtx->name); +} + + +void +ngx_shmtx_unlock(ngx_shmtx_t *mtx) +{ + ngx_err_t err; + + err = ngx_unlock_fd(mtx->fd); + + if (err == 0) { + return; + } + + ngx_log_abort(err, ngx_unlock_fd_n " %s failed", mtx->name); +} + + +ngx_uint_t +ngx_shmtx_force_unlock(ngx_shmtx_t *mtx, ngx_pid_t pid) +{ + return 0; +} + +#endif diff --git a/app/nginx/src/core/ngx_shmtx.h b/app/nginx/src/core/ngx_shmtx.h new file mode 100644 index 0000000..91e11be --- /dev/null +++ b/app/nginx/src/core/ngx_shmtx.h @@ -0,0 +1,49 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_SHMTX_H_INCLUDED_ +#define _NGX_SHMTX_H_INCLUDED_ + + +#include +#include + + +typedef struct { + ngx_atomic_t lock; +#if (NGX_HAVE_POSIX_SEM) + ngx_atomic_t wait; +#endif +} ngx_shmtx_sh_t; + + +typedef struct { +#if (NGX_HAVE_ATOMIC_OPS) + ngx_atomic_t *lock; +#if (NGX_HAVE_POSIX_SEM) + ngx_atomic_t *wait; + ngx_uint_t semaphore; + sem_t sem; +#endif +#else + ngx_fd_t fd; + u_char *name; +#endif + ngx_uint_t spin; +} ngx_shmtx_t; + + +ngx_int_t ngx_shmtx_create(ngx_shmtx_t *mtx, ngx_shmtx_sh_t *addr, + u_char *name); +void ngx_shmtx_destroy(ngx_shmtx_t *mtx); +ngx_uint_t ngx_shmtx_trylock(ngx_shmtx_t *mtx); +void ngx_shmtx_lock(ngx_shmtx_t *mtx); +void ngx_shmtx_unlock(ngx_shmtx_t *mtx); +ngx_uint_t ngx_shmtx_force_unlock(ngx_shmtx_t *mtx, ngx_pid_t pid); + + +#endif /* _NGX_SHMTX_H_INCLUDED_ */ diff --git a/app/nginx/src/core/ngx_slab.c b/app/nginx/src/core/ngx_slab.c new file mode 100644 index 0000000..1d4ce2b --- /dev/null +++ b/app/nginx/src/core/ngx_slab.c @@ -0,0 +1,806 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + +#include +#include + + +#define NGX_SLAB_PAGE_MASK 3 +#define NGX_SLAB_PAGE 0 +#define NGX_SLAB_BIG 1 +#define NGX_SLAB_EXACT 2 +#define NGX_SLAB_SMALL 3 + +#if (NGX_PTR_SIZE == 4) + +#define NGX_SLAB_PAGE_FREE 0 +#define NGX_SLAB_PAGE_BUSY 0xffffffff +#define NGX_SLAB_PAGE_START 0x80000000 + +#define NGX_SLAB_SHIFT_MASK 0x0000000f +#define NGX_SLAB_MAP_MASK 0xffff0000 +#define NGX_SLAB_MAP_SHIFT 16 + +#define NGX_SLAB_BUSY 0xffffffff + +#else /* (NGX_PTR_SIZE == 8) */ + +#define NGX_SLAB_PAGE_FREE 0 +#define NGX_SLAB_PAGE_BUSY 0xffffffffffffffff +#define NGX_SLAB_PAGE_START 0x8000000000000000 + +#define NGX_SLAB_SHIFT_MASK 0x000000000000000f +#define NGX_SLAB_MAP_MASK 0xffffffff00000000 +#define NGX_SLAB_MAP_SHIFT 32 + +#define NGX_SLAB_BUSY 0xffffffffffffffff + +#endif + + +#define ngx_slab_slots(pool) \ + (ngx_slab_page_t *) ((u_char *) (pool) + sizeof(ngx_slab_pool_t)) + +#define ngx_slab_page_type(page) ((page)->prev & NGX_SLAB_PAGE_MASK) + +#define ngx_slab_page_prev(page) \ + (ngx_slab_page_t *) ((page)->prev & ~NGX_SLAB_PAGE_MASK) + +#define ngx_slab_page_addr(pool, page) \ + ((((page) - (pool)->pages) << ngx_pagesize_shift) \ + + (uintptr_t) (pool)->start) + + +#if (NGX_DEBUG_MALLOC) + +#define ngx_slab_junk(p, size) ngx_memset(p, 0xA5, size) + +#elif (NGX_HAVE_DEBUG_MALLOC) + +#define ngx_slab_junk(p, size) \ + if (ngx_debug_malloc) ngx_memset(p, 0xA5, size) + +#else + +#define ngx_slab_junk(p, size) + +#endif + +static ngx_slab_page_t *ngx_slab_alloc_pages(ngx_slab_pool_t *pool, + ngx_uint_t pages); +static void ngx_slab_free_pages(ngx_slab_pool_t *pool, ngx_slab_page_t *page, + ngx_uint_t pages); +static void ngx_slab_error(ngx_slab_pool_t *pool, ngx_uint_t level, + char *text); + + +static ngx_uint_t ngx_slab_max_size; +static ngx_uint_t ngx_slab_exact_size; +static ngx_uint_t ngx_slab_exact_shift; + + +void +ngx_slab_init(ngx_slab_pool_t *pool) +{ + u_char *p; + size_t size; + ngx_int_t m; + ngx_uint_t i, n, pages; + ngx_slab_page_t *slots, *page; + + /* STUB */ + if (ngx_slab_max_size == 0) { + ngx_slab_max_size = ngx_pagesize / 2; + ngx_slab_exact_size = ngx_pagesize / (8 * sizeof(uintptr_t)); + for (n = ngx_slab_exact_size; n >>= 1; ngx_slab_exact_shift++) { + /* void */ + } + } + /**/ + + pool->min_size = (size_t) 1 << pool->min_shift; + + slots = ngx_slab_slots(pool); + + p = (u_char *) slots; + size = pool->end - p; + + ngx_slab_junk(p, size); + + n = ngx_pagesize_shift - pool->min_shift; + + for (i = 0; i < n; i++) { + /* only "next" is used in list head */ + slots[i].slab = 0; + slots[i].next = &slots[i]; + slots[i].prev = 0; + } + + p += n * sizeof(ngx_slab_page_t); + + pool->stats = (ngx_slab_stat_t *) p; + ngx_memzero(pool->stats, n * sizeof(ngx_slab_stat_t)); + + p += n * sizeof(ngx_slab_stat_t); + + size -= n * (sizeof(ngx_slab_page_t) + sizeof(ngx_slab_stat_t)); + + pages = (ngx_uint_t) (size / (ngx_pagesize + sizeof(ngx_slab_page_t))); + + pool->pages = (ngx_slab_page_t *) p; + ngx_memzero(pool->pages, pages * sizeof(ngx_slab_page_t)); + + page = pool->pages; + + /* only "next" is used in list head */ + pool->free.slab = 0; + pool->free.next = page; + pool->free.prev = 0; + + page->slab = pages; + page->next = &pool->free; + page->prev = (uintptr_t) &pool->free; + + pool->start = ngx_align_ptr(p + pages * sizeof(ngx_slab_page_t), + ngx_pagesize); + + m = pages - (pool->end - pool->start) / ngx_pagesize; + if (m > 0) { + pages -= m; + page->slab = pages; + } + + pool->last = pool->pages + pages; + pool->pfree = pages; + + pool->log_nomem = 1; + pool->log_ctx = &pool->zero; + pool->zero = '\0'; +} + + +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; + + 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_shift) + + ((size % ngx_pagesize) ? 1 : 0)); + if (page) { + p = ngx_slab_page_addr(pool, page); + + } else { + p = 0; + } + + goto done; + } + + if (size > pool->min_size) { + shift = 1; + for (s = size - 1; s >>= 1; shift++) { /* void */ } + slot = shift - pool->min_shift; + + } else { + shift = pool->min_shift; + slot = 0; + } + + pool->stats[slot].reqs++; + + ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, ngx_cycle->log, 0, + "slab alloc: %uz slot: %ui", size, slot); + + slots = ngx_slab_slots(pool); + page = slots[slot].next; + + if (page->next != page) { + + if (shift < ngx_slab_exact_shift) { + + bitmap = (uintptr_t *) ngx_slab_page_addr(pool, page); + + map = (ngx_pagesize >> shift) / (sizeof(uintptr_t) * 8); + + for (n = 0; n < map; n++) { + + if (bitmap[n] != NGX_SLAB_BUSY) { + + for (m = 1, i = 0; m; m <<= 1, i++) { + if (bitmap[n] & m) { + continue; + } + + bitmap[n] |= m; + + i = (n * sizeof(uintptr_t) * 8 + i) << shift; + + p = (uintptr_t) bitmap + i; + + pool->stats[slot].used++; + + if (bitmap[n] == NGX_SLAB_BUSY) { + for (n = n + 1; n < map; n++) { + if (bitmap[n] != NGX_SLAB_BUSY) { + goto done; + } + } + + prev = ngx_slab_page_prev(page); + prev->next = page->next; + page->next->prev = page->prev; + + page->next = NULL; + page->prev = NGX_SLAB_SMALL; + } + + goto done; + } + } + } + + } else if (shift == ngx_slab_exact_shift) { + + for (m = 1, i = 0; m; m <<= 1, i++) { + if (page->slab & m) { + continue; + } + + page->slab |= m; + + if (page->slab == NGX_SLAB_BUSY) { + prev = ngx_slab_page_prev(page); + prev->next = page->next; + page->next->prev = page->prev; + + page->next = NULL; + page->prev = NGX_SLAB_EXACT; + } + + p = ngx_slab_page_addr(pool, page) + (i << shift); + + pool->stats[slot].used++; + + goto done; + } + + } else { /* shift > ngx_slab_exact_shift */ + + mask = ((uintptr_t) 1 << (ngx_pagesize >> shift)) - 1; + mask <<= NGX_SLAB_MAP_SHIFT; + + for (m = (uintptr_t) 1 << NGX_SLAB_MAP_SHIFT, i = 0; + m & mask; + m <<= 1, i++) + { + if (page->slab & m) { + continue; + } + + page->slab |= m; + + if ((page->slab & NGX_SLAB_MAP_MASK) == mask) { + prev = ngx_slab_page_prev(page); + prev->next = page->next; + page->next->prev = page->prev; + + page->next = NULL; + page->prev = NGX_SLAB_BIG; + } + + p = ngx_slab_page_addr(pool, page) + (i << shift); + + pool->stats[slot].used++; + + goto done; + } + } + + ngx_slab_error(pool, NGX_LOG_ALERT, "ngx_slab_alloc(): page is busy"); + ngx_debug_point(); + } + + page = ngx_slab_alloc_pages(pool, 1); + + if (page) { + if (shift < ngx_slab_exact_shift) { + bitmap = (uintptr_t *) ngx_slab_page_addr(pool, page); + + n = (ngx_pagesize >> shift) / ((1 << shift) * 8); + + if (n == 0) { + n = 1; + } + + /* "n" elements for bitmap, plus one requested */ + bitmap[0] = ((uintptr_t) 2 << n) - 1; + + map = (ngx_pagesize >> shift) / (sizeof(uintptr_t) * 8); + + for (i = 1; i < map; i++) { + bitmap[i] = 0; + } + + page->slab = shift; + page->next = &slots[slot]; + page->prev = (uintptr_t) &slots[slot] | NGX_SLAB_SMALL; + + slots[slot].next = page; + + pool->stats[slot].total += (ngx_pagesize >> shift) - n; + + p = ngx_slab_page_addr(pool, page) + (n << shift); + + pool->stats[slot].used++; + + goto done; + + } else if (shift == ngx_slab_exact_shift) { + + page->slab = 1; + page->next = &slots[slot]; + page->prev = (uintptr_t) &slots[slot] | NGX_SLAB_EXACT; + + slots[slot].next = page; + + pool->stats[slot].total += sizeof(uintptr_t) * 8; + + p = ngx_slab_page_addr(pool, page); + + pool->stats[slot].used++; + + goto done; + + } else { /* shift > ngx_slab_exact_shift */ + + page->slab = ((uintptr_t) 1 << NGX_SLAB_MAP_SHIFT) | shift; + page->next = &slots[slot]; + page->prev = (uintptr_t) &slots[slot] | NGX_SLAB_BIG; + + slots[slot].next = page; + + pool->stats[slot].total += ngx_pagesize >> shift; + + p = ngx_slab_page_addr(pool, page); + + pool->stats[slot].used++; + + goto done; + } + } + + p = 0; + + pool->stats[slot].fails++; + +done: + + ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, ngx_cycle->log, 0, + "slab alloc: %p", (void *) p); + + return (void *) p; +} + + +void * +ngx_slab_calloc(ngx_slab_pool_t *pool, size_t size) +{ + void *p; + + ngx_shmtx_lock(&pool->mutex); + + p = ngx_slab_calloc_locked(pool, size); + + ngx_shmtx_unlock(&pool->mutex); + + return p; +} + + +void * +ngx_slab_calloc_locked(ngx_slab_pool_t *pool, size_t size) +{ + void *p; + + p = ngx_slab_alloc_locked(pool, size); + if (p) { + ngx_memzero(p, size); + } + + return p; +} + + +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, m, *bitmap; + ngx_uint_t i, n, type, slot, shift, map; + ngx_slab_page_t *slots, *page; + + ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, ngx_cycle->log, 0, "slab free: %p", p); + + if ((u_char *) p < pool->start || (u_char *) p > pool->end) { + ngx_slab_error(pool, NGX_LOG_ALERT, "ngx_slab_free(): outside of pool"); + goto fail; + } + + n = ((u_char *) p - pool->start) >> ngx_pagesize_shift; + page = &pool->pages[n]; + slab = page->slab; + type = ngx_slab_page_type(page); + + switch (type) { + + case NGX_SLAB_SMALL: + + shift = slab & NGX_SLAB_SHIFT_MASK; + size = (size_t) 1 << shift; + + if ((uintptr_t) p & (size - 1)) { + goto wrong_chunk; + } + + n = ((uintptr_t) p & (ngx_pagesize - 1)) >> shift; + m = (uintptr_t) 1 << (n % (sizeof(uintptr_t) * 8)); + n /= sizeof(uintptr_t) * 8; + bitmap = (uintptr_t *) + ((uintptr_t) p & ~((uintptr_t) ngx_pagesize - 1)); + + if (bitmap[n] & m) { + slot = shift - pool->min_shift; + + if (page->next == NULL) { + slots = ngx_slab_slots(pool); + + page->next = slots[slot].next; + slots[slot].next = page; + + page->prev = (uintptr_t) &slots[slot] | NGX_SLAB_SMALL; + page->next->prev = (uintptr_t) page | NGX_SLAB_SMALL; + } + + bitmap[n] &= ~m; + + n = (ngx_pagesize >> shift) / ((1 << shift) * 8); + + if (n == 0) { + n = 1; + } + + if (bitmap[0] & ~(((uintptr_t) 1 << n) - 1)) { + goto done; + } + + map = (ngx_pagesize >> shift) / (sizeof(uintptr_t) * 8); + + for (i = 1; i < map; i++) { + if (bitmap[i]) { + goto done; + } + } + + ngx_slab_free_pages(pool, page, 1); + + pool->stats[slot].total -= (ngx_pagesize >> shift) - n; + + goto done; + } + + goto chunk_already_free; + + case NGX_SLAB_EXACT: + + m = (uintptr_t) 1 << + (((uintptr_t) p & (ngx_pagesize - 1)) >> ngx_slab_exact_shift); + size = ngx_slab_exact_size; + + if ((uintptr_t) p & (size - 1)) { + goto wrong_chunk; + } + + if (slab & m) { + slot = ngx_slab_exact_shift - pool->min_shift; + + if (slab == NGX_SLAB_BUSY) { + slots = ngx_slab_slots(pool); + + page->next = slots[slot].next; + slots[slot].next = page; + + page->prev = (uintptr_t) &slots[slot] | NGX_SLAB_EXACT; + page->next->prev = (uintptr_t) page | NGX_SLAB_EXACT; + } + + page->slab &= ~m; + + if (page->slab) { + goto done; + } + + ngx_slab_free_pages(pool, page, 1); + + pool->stats[slot].total -= sizeof(uintptr_t) * 8; + + goto done; + } + + goto chunk_already_free; + + case NGX_SLAB_BIG: + + shift = slab & NGX_SLAB_SHIFT_MASK; + size = (size_t) 1 << shift; + + if ((uintptr_t) p & (size - 1)) { + goto wrong_chunk; + } + + m = (uintptr_t) 1 << ((((uintptr_t) p & (ngx_pagesize - 1)) >> shift) + + NGX_SLAB_MAP_SHIFT); + + if (slab & m) { + slot = shift - pool->min_shift; + + if (page->next == NULL) { + slots = ngx_slab_slots(pool); + + page->next = slots[slot].next; + slots[slot].next = page; + + page->prev = (uintptr_t) &slots[slot] | NGX_SLAB_BIG; + page->next->prev = (uintptr_t) page | NGX_SLAB_BIG; + } + + page->slab &= ~m; + + if (page->slab & NGX_SLAB_MAP_MASK) { + goto done; + } + + ngx_slab_free_pages(pool, page, 1); + + pool->stats[slot].total -= ngx_pagesize >> shift; + + goto done; + } + + goto chunk_already_free; + + case NGX_SLAB_PAGE: + + if ((uintptr_t) p & (ngx_pagesize - 1)) { + goto wrong_chunk; + } + + if (!(slab & NGX_SLAB_PAGE_START)) { + ngx_slab_error(pool, NGX_LOG_ALERT, + "ngx_slab_free(): page is already free"); + goto fail; + } + + if (slab == NGX_SLAB_PAGE_BUSY) { + ngx_slab_error(pool, NGX_LOG_ALERT, + "ngx_slab_free(): pointer to wrong page"); + goto fail; + } + + n = ((u_char *) p - pool->start) >> ngx_pagesize_shift; + size = slab & ~NGX_SLAB_PAGE_START; + + ngx_slab_free_pages(pool, &pool->pages[n], size); + + ngx_slab_junk(p, size << ngx_pagesize_shift); + + return; + } + + /* not reached */ + + return; + +done: + + pool->stats[slot].used--; + + ngx_slab_junk(p, size); + + return; + +wrong_chunk: + + ngx_slab_error(pool, NGX_LOG_ALERT, + "ngx_slab_free(): pointer to wrong chunk"); + + goto fail; + +chunk_already_free: + + ngx_slab_error(pool, NGX_LOG_ALERT, + "ngx_slab_free(): chunk is already free"); + +fail: + + return; +} + + +static ngx_slab_page_t * +ngx_slab_alloc_pages(ngx_slab_pool_t *pool, ngx_uint_t pages) +{ + ngx_slab_page_t *page, *p; + + for (page = pool->free.next; page != &pool->free; page = page->next) { + + if (page->slab >= pages) { + + if (page->slab > pages) { + page[page->slab - 1].prev = (uintptr_t) &page[pages]; + + page[pages].slab = page->slab - pages; + page[pages].next = page->next; + page[pages].prev = page->prev; + + p = (ngx_slab_page_t *) page->prev; + p->next = &page[pages]; + page->next->prev = (uintptr_t) &page[pages]; + + } else { + p = (ngx_slab_page_t *) page->prev; + p->next = page->next; + page->next->prev = page->prev; + } + + page->slab = pages | NGX_SLAB_PAGE_START; + page->next = NULL; + page->prev = NGX_SLAB_PAGE; + + pool->pfree -= pages; + + if (--pages == 0) { + return page; + } + + for (p = page + 1; pages; pages--) { + p->slab = NGX_SLAB_PAGE_BUSY; + p->next = NULL; + p->prev = NGX_SLAB_PAGE; + p++; + } + + return page; + } + } + + if (pool->log_nomem) { + ngx_slab_error(pool, NGX_LOG_CRIT, + "ngx_slab_alloc() failed: no memory"); + } + + return NULL; +} + + +static void +ngx_slab_free_pages(ngx_slab_pool_t *pool, ngx_slab_page_t *page, + ngx_uint_t pages) +{ + ngx_slab_page_t *prev, *join; + + pool->pfree += pages; + + page->slab = pages--; + + if (pages) { + ngx_memzero(&page[1], pages * sizeof(ngx_slab_page_t)); + } + + if (page->next) { + prev = ngx_slab_page_prev(page); + prev->next = page->next; + page->next->prev = page->prev; + } + + join = page + page->slab; + + if (join < pool->last) { + + if (ngx_slab_page_type(join) == NGX_SLAB_PAGE) { + + if (join->next != NULL) { + pages += join->slab; + page->slab += join->slab; + + prev = ngx_slab_page_prev(join); + prev->next = join->next; + join->next->prev = join->prev; + + join->slab = NGX_SLAB_PAGE_FREE; + join->next = NULL; + join->prev = NGX_SLAB_PAGE; + } + } + } + + if (page > pool->pages) { + join = page - 1; + + if (ngx_slab_page_type(join) == NGX_SLAB_PAGE) { + + if (join->slab == NGX_SLAB_PAGE_FREE) { + join = ngx_slab_page_prev(join); + } + + if (join->next != NULL) { + pages += join->slab; + join->slab += page->slab; + + prev = ngx_slab_page_prev(join); + prev->next = join->next; + join->next->prev = join->prev; + + page->slab = NGX_SLAB_PAGE_FREE; + page->next = NULL; + page->prev = NGX_SLAB_PAGE; + + page = join; + } + } + } + + if (pages) { + page[pages].prev = (uintptr_t) page; + } + + page->prev = (uintptr_t) &pool->free; + page->next = pool->free.next; + + page->next->prev = (uintptr_t) page; + + pool->free.next = page; +} + + +static void +ngx_slab_error(ngx_slab_pool_t *pool, ngx_uint_t level, char *text) +{ + ngx_log_error(level, ngx_cycle->log, 0, "%s%s", text, pool->log_ctx); +} diff --git a/app/nginx/src/core/ngx_slab.h b/app/nginx/src/core/ngx_slab.h new file mode 100644 index 0000000..eff893c --- /dev/null +++ b/app/nginx/src/core/ngx_slab.h @@ -0,0 +1,71 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_SLAB_H_INCLUDED_ +#define _NGX_SLAB_H_INCLUDED_ + + +#include +#include + + +typedef struct ngx_slab_page_s ngx_slab_page_t; + +struct ngx_slab_page_s { + uintptr_t slab; + ngx_slab_page_t *next; + uintptr_t prev; +}; + + +typedef struct { + ngx_uint_t total; + ngx_uint_t used; + + ngx_uint_t reqs; + ngx_uint_t fails; +} ngx_slab_stat_t; + + +typedef struct { + ngx_shmtx_sh_t lock; + + size_t min_size; + size_t min_shift; + + ngx_slab_page_t *pages; + ngx_slab_page_t *last; + ngx_slab_page_t free; + + ngx_slab_stat_t *stats; + ngx_uint_t pfree; + + u_char *start; + u_char *end; + + ngx_shmtx_t mutex; + + u_char *log_ctx; + u_char zero; + + unsigned log_nomem:1; + + void *data; + void *addr; +} ngx_slab_pool_t; + + +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_calloc(ngx_slab_pool_t *pool, size_t size); +void *ngx_slab_calloc_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/app/nginx/src/core/ngx_spinlock.c b/app/nginx/src/core/ngx_spinlock.c new file mode 100644 index 0000000..9c93afa --- /dev/null +++ b/app/nginx/src/core/ngx_spinlock.c @@ -0,0 +1,53 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include + + +void +ngx_spinlock(ngx_atomic_t *lock, ngx_atomic_int_t value, ngx_uint_t spin) +{ + +#if (NGX_HAVE_ATOMIC_OPS) + + ngx_uint_t i, n; + + for ( ;; ) { + + if (*lock == 0 && ngx_atomic_cmp_set(lock, 0, value)) { + return; + } + + if (ngx_ncpu > 1) { + + for (n = 1; n < spin; n <<= 1) { + + for (i = 0; i < n; i++) { + ngx_cpu_pause(); + } + + if (*lock == 0 && ngx_atomic_cmp_set(lock, 0, value)) { + return; + } + } + } + + ngx_sched_yield(); + } + +#else + +#if (NGX_THREADS) + +#error ngx_spinlock() or ngx_atomic_cmp_set() are not defined ! + +#endif + +#endif + +} diff --git a/app/nginx/src/core/ngx_string.c b/app/nginx/src/core/ngx_string.c new file mode 100644 index 0000000..7a73ef5 --- /dev/null +++ b/app/nginx/src/core/ngx_string.c @@ -0,0 +1,1976 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include + + +static u_char *ngx_sprintf_num(u_char *buf, u_char *last, uint64_t ui64, + u_char zero, ngx_uint_t hexadecimal, ngx_uint_t width); +static void ngx_encode_base64_internal(ngx_str_t *dst, ngx_str_t *src, + const u_char *basis, ngx_uint_t padding); +static ngx_int_t ngx_decode_base64_internal(ngx_str_t *dst, ngx_str_t *src, + const u_char *basis); + + +void +ngx_strlow(u_char *dst, u_char *src, size_t n) +{ + while (n) { + *dst = ngx_tolower(*src); + dst++; + src++; + n--; + } +} + + +u_char * +ngx_cpystrn(u_char *dst, u_char *src, size_t n) +{ + if (n == 0) { + return dst; + } + + while (--n) { + *dst = *src; + + if (*dst == '\0') { + return dst; + } + + dst++; + src++; + } + + *dst = '\0'; + + return dst; +} + + +u_char * +ngx_pstrdup(ngx_pool_t *pool, ngx_str_t *src) +{ + u_char *dst; + + dst = ngx_pnalloc(pool, src->len); + if (dst == NULL) { + return NULL; + } + + ngx_memcpy(dst, src->data, src->len); + + return dst; +} + + +/* + * supported formats: + * %[0][width][x][X]O off_t + * %[0][width]T time_t + * %[0][width][u][x|X]z ssize_t/size_t + * %[0][width][u][x|X]d int/u_int + * %[0][width][u][x|X]l long + * %[0][width|m][u][x|X]i ngx_int_t/ngx_uint_t + * %[0][width][u][x|X]D int32_t/uint32_t + * %[0][width][u][x|X]L int64_t/uint64_t + * %[0][width|m][u][x|X]A ngx_atomic_int_t/ngx_atomic_uint_t + * %[0][width][.width]f double, max valid number fits to %18.15f + * %P ngx_pid_t + * %M ngx_msec_t + * %r rlim_t + * %p void * + * %V ngx_str_t * + * %v ngx_variable_value_t * + * %s null-terminated string + * %*s length and string + * %Z '\0' + * %N '\n' + * %c char + * %% % + * + * reserved: + * %t ptrdiff_t + * %S null-terminated wchar string + * %C wchar + */ + + +u_char * ngx_cdecl +ngx_sprintf(u_char *buf, const char *fmt, ...) +{ + u_char *p; + va_list args; + + va_start(args, fmt); + p = ngx_vslprintf(buf, (void *) -1, fmt, args); + va_end(args); + + return p; +} + + +u_char * ngx_cdecl +ngx_snprintf(u_char *buf, size_t max, const char *fmt, ...) +{ + u_char *p; + va_list args; + + va_start(args, fmt); + p = ngx_vslprintf(buf, buf + max, fmt, args); + va_end(args); + + return p; +} + + +u_char * ngx_cdecl +ngx_slprintf(u_char *buf, u_char *last, const char *fmt, ...) +{ + u_char *p; + va_list args; + + va_start(args, fmt); + p = ngx_vslprintf(buf, last, fmt, args); + va_end(args); + + return p; +} + + +u_char * +ngx_vslprintf(u_char *buf, u_char *last, const char *fmt, va_list args) +{ + u_char *p, zero; + int d; + double f; + size_t len, slen; + int64_t i64; + uint64_t ui64, frac; + ngx_msec_t ms; + ngx_uint_t width, sign, hex, max_width, frac_width, scale, n; + ngx_str_t *v; + ngx_variable_value_t *vv; + + while (*fmt && buf < last) { + + /* + * "buf < last" means that we could copy at least one character: + * the plain character, "%%", "%c", and minus without the checking + */ + + if (*fmt == '%') { + + i64 = 0; + ui64 = 0; + + zero = (u_char) ((*++fmt == '0') ? '0' : ' '); + width = 0; + sign = 1; + hex = 0; + max_width = 0; + frac_width = 0; + slen = (size_t) -1; + + while (*fmt >= '0' && *fmt <= '9') { + width = width * 10 + *fmt++ - '0'; + } + + + for ( ;; ) { + switch (*fmt) { + + case 'u': + sign = 0; + fmt++; + continue; + + case 'm': + max_width = 1; + fmt++; + continue; + + case 'X': + hex = 2; + sign = 0; + fmt++; + continue; + + case 'x': + hex = 1; + sign = 0; + fmt++; + continue; + + case '.': + fmt++; + + while (*fmt >= '0' && *fmt <= '9') { + frac_width = frac_width * 10 + *fmt++ - '0'; + } + + break; + + case '*': + slen = va_arg(args, size_t); + fmt++; + continue; + + default: + break; + } + + break; + } + + + switch (*fmt) { + + case 'V': + v = va_arg(args, ngx_str_t *); + + len = ngx_min(((size_t) (last - buf)), v->len); + buf = ngx_cpymem(buf, v->data, len); + fmt++; + + continue; + + case 'v': + vv = va_arg(args, ngx_variable_value_t *); + + len = ngx_min(((size_t) (last - buf)), vv->len); + buf = ngx_cpymem(buf, vv->data, len); + fmt++; + + continue; + + case 's': + p = va_arg(args, u_char *); + + if (slen == (size_t) -1) { + while (*p && buf < last) { + *buf++ = *p++; + } + + } else { + len = ngx_min(((size_t) (last - buf)), slen); + buf = ngx_cpymem(buf, p, len); + } + + fmt++; + + continue; + + case 'O': + i64 = (int64_t) va_arg(args, off_t); + sign = 1; + break; + + case 'P': + i64 = (int64_t) va_arg(args, ngx_pid_t); + sign = 1; + break; + + case 'T': + i64 = (int64_t) va_arg(args, time_t); + sign = 1; + break; + + case 'M': + ms = (ngx_msec_t) va_arg(args, ngx_msec_t); + if ((ngx_msec_int_t) ms == -1) { + sign = 1; + i64 = -1; + } else { + sign = 0; + ui64 = (uint64_t) ms; + } + break; + + case 'z': + if (sign) { + i64 = (int64_t) va_arg(args, ssize_t); + } else { + ui64 = (uint64_t) va_arg(args, size_t); + } + break; + + case 'i': + if (sign) { + i64 = (int64_t) va_arg(args, ngx_int_t); + } else { + ui64 = (uint64_t) va_arg(args, ngx_uint_t); + } + + if (max_width) { + width = NGX_INT_T_LEN; + } + + break; + + case 'd': + if (sign) { + i64 = (int64_t) va_arg(args, int); + } else { + ui64 = (uint64_t) va_arg(args, u_int); + } + break; + + case 'l': + if (sign) { + i64 = (int64_t) va_arg(args, long); + } else { + ui64 = (uint64_t) va_arg(args, u_long); + } + break; + + case 'D': + if (sign) { + i64 = (int64_t) va_arg(args, int32_t); + } else { + ui64 = (uint64_t) va_arg(args, uint32_t); + } + break; + + case 'L': + if (sign) { + i64 = va_arg(args, int64_t); + } else { + ui64 = va_arg(args, uint64_t); + } + break; + + case 'A': + if (sign) { + i64 = (int64_t) va_arg(args, ngx_atomic_int_t); + } else { + ui64 = (uint64_t) va_arg(args, ngx_atomic_uint_t); + } + + if (max_width) { + width = NGX_ATOMIC_T_LEN; + } + + break; + + case 'f': + f = va_arg(args, double); + + if (f < 0) { + *buf++ = '-'; + f = -f; + } + + ui64 = (int64_t) f; + frac = 0; + + if (frac_width) { + + scale = 1; + for (n = frac_width; n; n--) { + scale *= 10; + } + + frac = (uint64_t) ((f - (double) ui64) * scale + 0.5); + + if (frac == scale) { + ui64++; + frac = 0; + } + } + + buf = ngx_sprintf_num(buf, last, ui64, zero, 0, width); + + if (frac_width) { + if (buf < last) { + *buf++ = '.'; + } + + buf = ngx_sprintf_num(buf, last, frac, '0', 0, frac_width); + } + + fmt++; + + continue; + +#if !(NGX_WIN32) + case 'r': + i64 = (int64_t) va_arg(args, rlim_t); + sign = 1; + break; +#endif + + case 'p': + ui64 = (uintptr_t) va_arg(args, void *); + hex = 2; + sign = 0; + zero = '0'; + width = 2 * sizeof(void *); + break; + + case 'c': + d = va_arg(args, int); + *buf++ = (u_char) (d & 0xff); + fmt++; + + continue; + + case 'Z': + *buf++ = '\0'; + fmt++; + + continue; + + case 'N': +#if (NGX_WIN32) + *buf++ = CR; + if (buf < last) { + *buf++ = LF; + } +#else + *buf++ = LF; +#endif + fmt++; + + continue; + + case '%': + *buf++ = '%'; + fmt++; + + continue; + + default: + *buf++ = *fmt++; + + continue; + } + + if (sign) { + if (i64 < 0) { + *buf++ = '-'; + ui64 = (uint64_t) -i64; + + } else { + ui64 = (uint64_t) i64; + } + } + + buf = ngx_sprintf_num(buf, last, ui64, zero, hex, width); + + fmt++; + + } else { + *buf++ = *fmt++; + } + } + + return buf; +} + + +static u_char * +ngx_sprintf_num(u_char *buf, u_char *last, uint64_t ui64, u_char zero, + ngx_uint_t hexadecimal, ngx_uint_t width) +{ + u_char *p, temp[NGX_INT64_LEN + 1]; + /* + * we need temp[NGX_INT64_LEN] only, + * but icc issues the warning + */ + size_t len; + uint32_t ui32; + static u_char hex[] = "0123456789abcdef"; + static u_char HEX[] = "0123456789ABCDEF"; + + p = temp + NGX_INT64_LEN; + + if (hexadecimal == 0) { + + if (ui64 <= (uint64_t) NGX_MAX_UINT32_VALUE) { + + /* + * To divide 64-bit numbers and to find remainders + * on the x86 platform gcc and icc call the libc functions + * [u]divdi3() and [u]moddi3(), they call another function + * in its turn. On FreeBSD it is the qdivrem() function, + * its source code is about 170 lines of the code. + * The glibc counterpart is about 150 lines of the code. + * + * For 32-bit numbers and some divisors gcc and icc use + * a inlined multiplication and shifts. For example, + * unsigned "i32 / 10" is compiled to + * + * (i32 * 0xCCCCCCCD) >> 35 + */ + + ui32 = (uint32_t) ui64; + + do { + *--p = (u_char) (ui32 % 10 + '0'); + } while (ui32 /= 10); + + } else { + do { + *--p = (u_char) (ui64 % 10 + '0'); + } while (ui64 /= 10); + } + + } else if (hexadecimal == 1) { + + do { + + /* the "(uint32_t)" cast disables the BCC's warning */ + *--p = hex[(uint32_t) (ui64 & 0xf)]; + + } while (ui64 >>= 4); + + } else { /* hexadecimal == 2 */ + + do { + + /* the "(uint32_t)" cast disables the BCC's warning */ + *--p = HEX[(uint32_t) (ui64 & 0xf)]; + + } while (ui64 >>= 4); + } + + /* zero or space padding */ + + len = (temp + NGX_INT64_LEN) - p; + + while (len++ < width && buf < last) { + *buf++ = zero; + } + + /* number safe copy */ + + len = (temp + NGX_INT64_LEN) - p; + + if (buf + len > last) { + len = last - buf; + } + + return ngx_cpymem(buf, p, len); +} + + +/* + * We use ngx_strcasecmp()/ngx_strncasecmp() for 7-bit ASCII strings only, + * and implement our own ngx_strcasecmp()/ngx_strncasecmp() + * to avoid libc locale overhead. Besides, we use the ngx_uint_t's + * instead of the u_char's, because they are slightly faster. + */ + +ngx_int_t +ngx_strcasecmp(u_char *s1, u_char *s2) +{ + ngx_uint_t c1, c2; + + for ( ;; ) { + c1 = (ngx_uint_t) *s1++; + c2 = (ngx_uint_t) *s2++; + + c1 = (c1 >= 'A' && c1 <= 'Z') ? (c1 | 0x20) : c1; + c2 = (c2 >= 'A' && c2 <= 'Z') ? (c2 | 0x20) : c2; + + if (c1 == c2) { + + if (c1) { + continue; + } + + return 0; + } + + return c1 - c2; + } +} + + +ngx_int_t +ngx_strncasecmp(u_char *s1, u_char *s2, size_t n) +{ + ngx_uint_t c1, c2; + + while (n) { + c1 = (ngx_uint_t) *s1++; + c2 = (ngx_uint_t) *s2++; + + c1 = (c1 >= 'A' && c1 <= 'Z') ? (c1 | 0x20) : c1; + c2 = (c2 >= 'A' && c2 <= 'Z') ? (c2 | 0x20) : c2; + + if (c1 == c2) { + + if (c1) { + n--; + continue; + } + + return 0; + } + + return c1 - c2; + } + + return 0; +} + + +u_char * +ngx_strnstr(u_char *s1, char *s2, size_t len) +{ + u_char c1, c2; + size_t n; + + c2 = *(u_char *) s2++; + + n = ngx_strlen(s2); + + do { + do { + if (len-- == 0) { + return NULL; + } + + c1 = *s1++; + + if (c1 == 0) { + return NULL; + } + + } while (c1 != c2); + + if (n > len) { + return NULL; + } + + } while (ngx_strncmp(s1, (u_char *) s2, n) != 0); + + return --s1; +} + + +/* + * ngx_strstrn() and ngx_strcasestrn() are intended to search for static + * substring with known length in null-terminated string. The argument n + * must be length of the second substring - 1. + */ + +u_char * +ngx_strstrn(u_char *s1, char *s2, size_t n) +{ + u_char c1, c2; + + c2 = *(u_char *) s2++; + + do { + do { + c1 = *s1++; + + if (c1 == 0) { + return NULL; + } + + } while (c1 != c2); + + } while (ngx_strncmp(s1, (u_char *) s2, n) != 0); + + return --s1; +} + + +u_char * +ngx_strcasestrn(u_char *s1, char *s2, size_t n) +{ + ngx_uint_t c1, c2; + + c2 = (ngx_uint_t) *s2++; + c2 = (c2 >= 'A' && c2 <= 'Z') ? (c2 | 0x20) : c2; + + do { + do { + c1 = (ngx_uint_t) *s1++; + + if (c1 == 0) { + return NULL; + } + + c1 = (c1 >= 'A' && c1 <= 'Z') ? (c1 | 0x20) : c1; + + } while (c1 != c2); + + } while (ngx_strncasecmp(s1, (u_char *) s2, n) != 0); + + return --s1; +} + + +/* + * ngx_strlcasestrn() is intended to search for static substring + * with known length in string until the argument last. The argument n + * must be length of the second substring - 1. + */ + +u_char * +ngx_strlcasestrn(u_char *s1, u_char *last, u_char *s2, size_t n) +{ + ngx_uint_t c1, c2; + + c2 = (ngx_uint_t) *s2++; + c2 = (c2 >= 'A' && c2 <= 'Z') ? (c2 | 0x20) : c2; + last -= n; + + do { + do { + if (s1 >= last) { + return NULL; + } + + c1 = (ngx_uint_t) *s1++; + + c1 = (c1 >= 'A' && c1 <= 'Z') ? (c1 | 0x20) : c1; + + } while (c1 != c2); + + } while (ngx_strncasecmp(s1, s2, n) != 0); + + return --s1; +} + + +ngx_int_t +ngx_rstrncmp(u_char *s1, u_char *s2, size_t n) +{ + if (n == 0) { + return 0; + } + + n--; + + for ( ;; ) { + if (s1[n] != s2[n]) { + return s1[n] - s2[n]; + } + + if (n == 0) { + return 0; + } + + n--; + } +} + + +ngx_int_t +ngx_rstrncasecmp(u_char *s1, u_char *s2, size_t n) +{ + u_char c1, c2; + + if (n == 0) { + return 0; + } + + n--; + + for ( ;; ) { + c1 = s1[n]; + if (c1 >= 'a' && c1 <= 'z') { + c1 -= 'a' - 'A'; + } + + c2 = s2[n]; + if (c2 >= 'a' && c2 <= 'z') { + c2 -= 'a' - 'A'; + } + + if (c1 != c2) { + return c1 - c2; + } + + if (n == 0) { + return 0; + } + + n--; + } +} + + +ngx_int_t +ngx_memn2cmp(u_char *s1, u_char *s2, size_t n1, size_t n2) +{ + size_t n; + ngx_int_t m, z; + + if (n1 <= n2) { + n = n1; + z = -1; + + } else { + n = n2; + z = 1; + } + + m = ngx_memcmp(s1, s2, n); + + if (m || n1 == n2) { + return m; + } + + return z; +} + + +ngx_int_t +ngx_dns_strcmp(u_char *s1, u_char *s2) +{ + ngx_uint_t c1, c2; + + for ( ;; ) { + c1 = (ngx_uint_t) *s1++; + c2 = (ngx_uint_t) *s2++; + + c1 = (c1 >= 'A' && c1 <= 'Z') ? (c1 | 0x20) : c1; + c2 = (c2 >= 'A' && c2 <= 'Z') ? (c2 | 0x20) : c2; + + if (c1 == c2) { + + if (c1) { + continue; + } + + return 0; + } + + /* in ASCII '.' > '-', but we need '.' to be the lowest character */ + + c1 = (c1 == '.') ? ' ' : c1; + c2 = (c2 == '.') ? ' ' : c2; + + return c1 - c2; + } +} + + +ngx_int_t +ngx_filename_cmp(u_char *s1, u_char *s2, size_t n) +{ + ngx_uint_t c1, c2; + + while (n) { + c1 = (ngx_uint_t) *s1++; + c2 = (ngx_uint_t) *s2++; + +#if (NGX_HAVE_CASELESS_FILESYSTEM) + c1 = tolower(c1); + c2 = tolower(c2); +#endif + + if (c1 == c2) { + + if (c1) { + n--; + continue; + } + + return 0; + } + + /* we need '/' to be the lowest character */ + + if (c1 == 0 || c2 == 0) { + return c1 - c2; + } + + c1 = (c1 == '/') ? 0 : c1; + c2 = (c2 == '/') ? 0 : c2; + + return c1 - c2; + } + + return 0; +} + + +ngx_int_t +ngx_atoi(u_char *line, size_t n) +{ + ngx_int_t value, cutoff, cutlim; + + if (n == 0) { + return NGX_ERROR; + } + + cutoff = NGX_MAX_INT_T_VALUE / 10; + cutlim = NGX_MAX_INT_T_VALUE % 10; + + for (value = 0; n--; line++) { + if (*line < '0' || *line > '9') { + return NGX_ERROR; + } + + if (value >= cutoff && (value > cutoff || *line - '0' > cutlim)) { + return NGX_ERROR; + } + + value = value * 10 + (*line - '0'); + } + + return value; +} + + +/* parse a fixed point number, e.g., ngx_atofp("10.5", 4, 2) returns 1050 */ + +ngx_int_t +ngx_atofp(u_char *line, size_t n, size_t point) +{ + ngx_int_t value, cutoff, cutlim; + ngx_uint_t dot; + + if (n == 0) { + return NGX_ERROR; + } + + cutoff = NGX_MAX_INT_T_VALUE / 10; + cutlim = NGX_MAX_INT_T_VALUE % 10; + + dot = 0; + + for (value = 0; n--; line++) { + + if (point == 0) { + return NGX_ERROR; + } + + if (*line == '.') { + if (dot) { + return NGX_ERROR; + } + + dot = 1; + continue; + } + + if (*line < '0' || *line > '9') { + return NGX_ERROR; + } + + if (value >= cutoff && (value > cutoff || *line - '0' > cutlim)) { + return NGX_ERROR; + } + + value = value * 10 + (*line - '0'); + point -= dot; + } + + while (point--) { + if (value > cutoff) { + return NGX_ERROR; + } + + value = value * 10; + } + + return value; +} + + +ssize_t +ngx_atosz(u_char *line, size_t n) +{ + ssize_t value, cutoff, cutlim; + + if (n == 0) { + return NGX_ERROR; + } + + cutoff = NGX_MAX_SIZE_T_VALUE / 10; + cutlim = NGX_MAX_SIZE_T_VALUE % 10; + + for (value = 0; n--; line++) { + if (*line < '0' || *line > '9') { + return NGX_ERROR; + } + + if (value >= cutoff && (value > cutoff || *line - '0' > cutlim)) { + return NGX_ERROR; + } + + value = value * 10 + (*line - '0'); + } + + return value; +} + + +off_t +ngx_atoof(u_char *line, size_t n) +{ + off_t value, cutoff, cutlim; + + if (n == 0) { + return NGX_ERROR; + } + + cutoff = NGX_MAX_OFF_T_VALUE / 10; + cutlim = NGX_MAX_OFF_T_VALUE % 10; + + for (value = 0; n--; line++) { + if (*line < '0' || *line > '9') { + return NGX_ERROR; + } + + if (value >= cutoff && (value > cutoff || *line - '0' > cutlim)) { + return NGX_ERROR; + } + + value = value * 10 + (*line - '0'); + } + + return value; +} + + +time_t +ngx_atotm(u_char *line, size_t n) +{ + time_t value, cutoff, cutlim; + + if (n == 0) { + return NGX_ERROR; + } + + cutoff = NGX_MAX_TIME_T_VALUE / 10; + cutlim = NGX_MAX_TIME_T_VALUE % 10; + + for (value = 0; n--; line++) { + if (*line < '0' || *line > '9') { + return NGX_ERROR; + } + + if (value >= cutoff && (value > cutoff || *line - '0' > cutlim)) { + return NGX_ERROR; + } + + value = value * 10 + (*line - '0'); + } + + return value; +} + + +ngx_int_t +ngx_hextoi(u_char *line, size_t n) +{ + u_char c, ch; + ngx_int_t value, cutoff; + + if (n == 0) { + return NGX_ERROR; + } + + cutoff = NGX_MAX_INT_T_VALUE / 16; + + for (value = 0; n--; line++) { + if (value > cutoff) { + return NGX_ERROR; + } + + ch = *line; + + if (ch >= '0' && ch <= '9') { + value = value * 16 + (ch - '0'); + continue; + } + + c = (u_char) (ch | 0x20); + + if (c >= 'a' && c <= 'f') { + value = value * 16 + (c - 'a' + 10); + continue; + } + + return NGX_ERROR; + } + + return value; +} + + +u_char * +ngx_hex_dump(u_char *dst, u_char *src, size_t len) +{ + static u_char hex[] = "0123456789abcdef"; + + while (len--) { + *dst++ = hex[*src >> 4]; + *dst++ = hex[*src++ & 0xf]; + } + + return dst; +} + + +void +ngx_encode_base64(ngx_str_t *dst, ngx_str_t *src) +{ + static u_char basis64[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + ngx_encode_base64_internal(dst, src, basis64, 1); +} + + +void +ngx_encode_base64url(ngx_str_t *dst, ngx_str_t *src) +{ + static u_char basis64[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; + + ngx_encode_base64_internal(dst, src, basis64, 0); +} + + +static void +ngx_encode_base64_internal(ngx_str_t *dst, ngx_str_t *src, const u_char *basis, + ngx_uint_t padding) +{ + u_char *d, *s; + size_t len; + + len = src->len; + s = src->data; + d = dst->data; + + while (len > 2) { + *d++ = basis[(s[0] >> 2) & 0x3f]; + *d++ = basis[((s[0] & 3) << 4) | (s[1] >> 4)]; + *d++ = basis[((s[1] & 0x0f) << 2) | (s[2] >> 6)]; + *d++ = basis[s[2] & 0x3f]; + + s += 3; + len -= 3; + } + + if (len) { + *d++ = basis[(s[0] >> 2) & 0x3f]; + + if (len == 1) { + *d++ = basis[(s[0] & 3) << 4]; + if (padding) { + *d++ = '='; + } + + } else { + *d++ = basis[((s[0] & 3) << 4) | (s[1] >> 4)]; + *d++ = basis[(s[1] & 0x0f) << 2]; + } + + if (padding) { + *d++ = '='; + } + } + + dst->len = d - dst->data; +} + + +ngx_int_t +ngx_decode_base64(ngx_str_t *dst, ngx_str_t *src) +{ + static u_char basis64[] = { + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 62, 77, 77, 77, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 77, 77, 77, 77, 77, 77, + 77, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 77, 77, 77, 77, 77, + 77, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 77, 77, 77, 77, 77, + + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77 + }; + + return ngx_decode_base64_internal(dst, src, basis64); +} + + +ngx_int_t +ngx_decode_base64url(ngx_str_t *dst, ngx_str_t *src) +{ + static u_char basis64[] = { + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 62, 77, 77, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 77, 77, 77, 77, 77, 77, + 77, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 77, 77, 77, 77, 63, + 77, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 77, 77, 77, 77, 77, + + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77 + }; + + return ngx_decode_base64_internal(dst, src, basis64); +} + + +static ngx_int_t +ngx_decode_base64_internal(ngx_str_t *dst, ngx_str_t *src, const u_char *basis) +{ + size_t len; + u_char *d, *s; + + for (len = 0; len < src->len; len++) { + if (src->data[len] == '=') { + break; + } + + if (basis[src->data[len]] == 77) { + return NGX_ERROR; + } + } + + if (len % 4 == 1) { + return NGX_ERROR; + } + + s = src->data; + d = dst->data; + + while (len > 3) { + *d++ = (u_char) (basis[s[0]] << 2 | basis[s[1]] >> 4); + *d++ = (u_char) (basis[s[1]] << 4 | basis[s[2]] >> 2); + *d++ = (u_char) (basis[s[2]] << 6 | basis[s[3]]); + + s += 4; + len -= 4; + } + + if (len > 1) { + *d++ = (u_char) (basis[s[0]] << 2 | basis[s[1]] >> 4); + } + + if (len > 2) { + *d++ = (u_char) (basis[s[1]] << 4 | basis[s[2]] >> 2); + } + + dst->len = d - dst->data; + + return NGX_OK; +} + + +/* + * ngx_utf8_decode() decodes two and more bytes UTF sequences only + * the return values: + * 0x80 - 0x10ffff valid character + * 0x110000 - 0xfffffffd invalid sequence + * 0xfffffffe incomplete sequence + * 0xffffffff error + */ + +uint32_t +ngx_utf8_decode(u_char **p, size_t n) +{ + size_t len; + uint32_t u, i, valid; + + u = **p; + + if (u >= 0xf0) { + + u &= 0x07; + valid = 0xffff; + len = 3; + + } else if (u >= 0xe0) { + + u &= 0x0f; + valid = 0x7ff; + len = 2; + + } else if (u >= 0xc2) { + + u &= 0x1f; + valid = 0x7f; + len = 1; + + } else { + (*p)++; + return 0xffffffff; + } + + if (n - 1 < len) { + return 0xfffffffe; + } + + (*p)++; + + while (len) { + i = *(*p)++; + + if (i < 0x80) { + return 0xffffffff; + } + + u = (u << 6) | (i & 0x3f); + + len--; + } + + if (u > valid) { + return u; + } + + return 0xffffffff; +} + + +size_t +ngx_utf8_length(u_char *p, size_t n) +{ + u_char c, *last; + size_t len; + + last = p + n; + + for (len = 0; p < last; len++) { + + c = *p; + + if (c < 0x80) { + p++; + continue; + } + + if (ngx_utf8_decode(&p, n) > 0x10ffff) { + /* invalid UTF-8 */ + return n; + } + } + + return len; +} + + +u_char * +ngx_utf8_cpystrn(u_char *dst, u_char *src, size_t n, size_t len) +{ + u_char c, *next; + + if (n == 0) { + return dst; + } + + while (--n) { + + c = *src; + *dst = c; + + if (c < 0x80) { + + if (c != '\0') { + dst++; + src++; + len--; + + continue; + } + + return dst; + } + + next = src; + + if (ngx_utf8_decode(&next, len) > 0x10ffff) { + /* invalid UTF-8 */ + break; + } + + while (src < next) { + *dst++ = *src++; + len--; + } + } + + *dst = '\0'; + + return dst; +} + + +uintptr_t +ngx_escape_uri(u_char *dst, u_char *src, size_t size, ngx_uint_t type) +{ + ngx_uint_t n; + uint32_t *escape; + static u_char hex[] = "0123456789ABCDEF"; + + /* " ", "#", "%", "?", %00-%1F, %7F-%FF */ + + static uint32_t uri[] = { + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + + /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */ + 0x80000029, /* 1000 0000 0000 0000 0000 0000 0010 1001 */ + + /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */ + 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ + + /* ~}| {zyx wvut srqp onml kjih gfed cba` */ + 0x80000000, /* 1000 0000 0000 0000 0000 0000 0000 0000 */ + + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + }; + + /* " ", "#", "%", "&", "+", "?", %00-%1F, %7F-%FF */ + + static uint32_t args[] = { + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + + /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */ + 0x88000869, /* 1000 1000 0000 0000 0000 1000 0110 1001 */ + + /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */ + 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ + + /* ~}| {zyx wvut srqp onml kjih gfed cba` */ + 0x80000000, /* 1000 0000 0000 0000 0000 0000 0000 0000 */ + + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + }; + + /* not ALPHA, DIGIT, "-", ".", "_", "~" */ + + static uint32_t uri_component[] = { + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + + /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */ + 0xfc009fff, /* 1111 1100 0000 0000 1001 1111 1111 1111 */ + + /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */ + 0x78000001, /* 0111 1000 0000 0000 0000 0000 0000 0001 */ + + /* ~}| {zyx wvut srqp onml kjih gfed cba` */ + 0xb8000001, /* 1011 1000 0000 0000 0000 0000 0000 0001 */ + + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + }; + + /* " ", "#", """, "%", "'", %00-%1F, %7F-%FF */ + + static uint32_t html[] = { + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + + /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */ + 0x000000ad, /* 0000 0000 0000 0000 0000 0000 1010 1101 */ + + /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */ + 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ + + /* ~}| {zyx wvut srqp onml kjih gfed cba` */ + 0x80000000, /* 1000 0000 0000 0000 0000 0000 0000 0000 */ + + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + }; + + /* " ", """, "%", "'", %00-%1F, %7F-%FF */ + + static uint32_t refresh[] = { + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + + /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */ + 0x00000085, /* 0000 0000 0000 0000 0000 0000 1000 0101 */ + + /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */ + 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ + + /* ~}| {zyx wvut srqp onml kjih gfed cba` */ + 0x80000000, /* 1000 0000 0000 0000 0000 0000 0000 0000 */ + + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + }; + + /* " ", "%", %00-%1F */ + + static uint32_t memcached[] = { + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + + /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */ + 0x00000021, /* 0000 0000 0000 0000 0000 0000 0010 0001 */ + + /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */ + 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ + + /* ~}| {zyx wvut srqp onml kjih gfed cba` */ + 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ + + 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ + 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ + 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ + 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ + }; + + /* mail_auth is the same as memcached */ + + static uint32_t *map[] = + { uri, args, uri_component, html, refresh, memcached, memcached }; + + + escape = map[type]; + + if (dst == NULL) { + + /* find the number of the characters to be escaped */ + + n = 0; + + while (size) { + if (escape[*src >> 5] & (1U << (*src & 0x1f))) { + n++; + } + src++; + size--; + } + + return (uintptr_t) n; + } + + while (size) { + if (escape[*src >> 5] & (1U << (*src & 0x1f))) { + *dst++ = '%'; + *dst++ = hex[*src >> 4]; + *dst++ = hex[*src & 0xf]; + src++; + + } else { + *dst++ = *src++; + } + size--; + } + + return (uintptr_t) dst; +} + + +void +ngx_unescape_uri(u_char **dst, u_char **src, size_t size, ngx_uint_t type) +{ + u_char *d, *s, ch, c, decoded; + enum { + sw_usual = 0, + sw_quoted, + sw_quoted_second + } state; + + d = *dst; + s = *src; + + state = 0; + decoded = 0; + + while (size--) { + + ch = *s++; + + switch (state) { + case sw_usual: + if (ch == '?' + && (type & (NGX_UNESCAPE_URI|NGX_UNESCAPE_REDIRECT))) + { + *d++ = ch; + goto done; + } + + if (ch == '%') { + state = sw_quoted; + break; + } + + *d++ = ch; + break; + + case sw_quoted: + + if (ch >= '0' && ch <= '9') { + decoded = (u_char) (ch - '0'); + state = sw_quoted_second; + break; + } + + c = (u_char) (ch | 0x20); + if (c >= 'a' && c <= 'f') { + decoded = (u_char) (c - 'a' + 10); + state = sw_quoted_second; + break; + } + + /* the invalid quoted character */ + + state = sw_usual; + + *d++ = ch; + + break; + + case sw_quoted_second: + + state = sw_usual; + + if (ch >= '0' && ch <= '9') { + ch = (u_char) ((decoded << 4) + ch - '0'); + + if (type & NGX_UNESCAPE_REDIRECT) { + if (ch > '%' && ch < 0x7f) { + *d++ = ch; + break; + } + + *d++ = '%'; *d++ = *(s - 2); *d++ = *(s - 1); + + break; + } + + *d++ = ch; + + break; + } + + c = (u_char) (ch | 0x20); + if (c >= 'a' && c <= 'f') { + ch = (u_char) ((decoded << 4) + c - 'a' + 10); + + if (type & NGX_UNESCAPE_URI) { + if (ch == '?') { + *d++ = ch; + goto done; + } + + *d++ = ch; + break; + } + + if (type & NGX_UNESCAPE_REDIRECT) { + if (ch == '?') { + *d++ = ch; + goto done; + } + + if (ch > '%' && ch < 0x7f) { + *d++ = ch; + break; + } + + *d++ = '%'; *d++ = *(s - 2); *d++ = *(s - 1); + break; + } + + *d++ = ch; + + break; + } + + /* the invalid quoted character */ + + break; + } + } + +done: + + *dst = d; + *src = s; +} + + +uintptr_t +ngx_escape_html(u_char *dst, u_char *src, size_t size) +{ + u_char ch; + ngx_uint_t len; + + if (dst == NULL) { + + len = 0; + + while (size) { + switch (*src++) { + + case '<': + len += sizeof("<") - 2; + break; + + case '>': + len += sizeof(">") - 2; + break; + + case '&': + len += sizeof("&") - 2; + break; + + case '"': + len += sizeof(""") - 2; + break; + + default: + break; + } + size--; + } + + return (uintptr_t) len; + } + + while (size) { + ch = *src++; + + switch (ch) { + + case '<': + *dst++ = '&'; *dst++ = 'l'; *dst++ = 't'; *dst++ = ';'; + break; + + case '>': + *dst++ = '&'; *dst++ = 'g'; *dst++ = 't'; *dst++ = ';'; + break; + + case '&': + *dst++ = '&'; *dst++ = 'a'; *dst++ = 'm'; *dst++ = 'p'; + *dst++ = ';'; + break; + + case '"': + *dst++ = '&'; *dst++ = 'q'; *dst++ = 'u'; *dst++ = 'o'; + *dst++ = 't'; *dst++ = ';'; + break; + + default: + *dst++ = ch; + break; + } + size--; + } + + return (uintptr_t) dst; +} + + +uintptr_t +ngx_escape_json(u_char *dst, u_char *src, size_t size) +{ + u_char ch; + ngx_uint_t len; + + if (dst == NULL) { + len = 0; + + while (size) { + ch = *src++; + + if (ch == '\\' || ch == '"') { + len++; + + } else if (ch <= 0x1f) { + len += sizeof("\\u001F") - 2; + } + + size--; + } + + return (uintptr_t) len; + } + + while (size) { + ch = *src++; + + if (ch > 0x1f) { + + if (ch == '\\' || ch == '"') { + *dst++ = '\\'; + } + + *dst++ = ch; + + } else { + *dst++ = '\\'; *dst++ = 'u'; *dst++ = '0'; *dst++ = '0'; + *dst++ = '0' + (ch >> 4); + + ch &= 0xf; + + *dst++ = (ch < 10) ? ('0' + ch) : ('A' + ch - 10); + } + + size--; + } + + return (uintptr_t) dst; +} + + +void +ngx_str_rbtree_insert_value(ngx_rbtree_node_t *temp, + ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel) +{ + ngx_str_node_t *n, *t; + ngx_rbtree_node_t **p; + + for ( ;; ) { + + n = (ngx_str_node_t *) node; + t = (ngx_str_node_t *) temp; + + if (node->key != temp->key) { + + p = (node->key < temp->key) ? &temp->left : &temp->right; + + } else if (n->str.len != t->str.len) { + + p = (n->str.len < t->str.len) ? &temp->left : &temp->right; + + } else { + p = (ngx_memcmp(n->str.data, t->str.data, n->str.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); +} + + +ngx_str_node_t * +ngx_str_rbtree_lookup(ngx_rbtree_t *rbtree, ngx_str_t *val, uint32_t hash) +{ + ngx_int_t rc; + ngx_str_node_t *n; + ngx_rbtree_node_t *node, *sentinel; + + node = rbtree->root; + sentinel = rbtree->sentinel; + + while (node != sentinel) { + + n = (ngx_str_node_t *) node; + + if (hash != node->key) { + node = (hash < node->key) ? node->left : node->right; + continue; + } + + if (val->len != n->str.len) { + node = (val->len < n->str.len) ? node->left : node->right; + continue; + } + + rc = ngx_memcmp(val->data, n->str.data, val->len); + + if (rc < 0) { + node = node->left; + continue; + } + + if (rc > 0) { + node = node->right; + continue; + } + + return n; + } + + return NULL; +} + + +/* ngx_sort() is implemented as insertion sort because we need stable sort */ + +void +ngx_sort(void *base, size_t n, size_t size, + ngx_int_t (*cmp)(const void *, const void *)) +{ + u_char *p1, *p2, *p; + + p = ngx_alloc(size, ngx_cycle->log); + if (p == NULL) { + return; + } + + for (p1 = (u_char *) base + size; + p1 < (u_char *) base + n * size; + p1 += size) + { + ngx_memcpy(p, p1, size); + + for (p2 = p1; + p2 > (u_char *) base && cmp(p2 - size, p) > 0; + p2 -= size) + { + ngx_memcpy(p2, p2 - size, size); + } + + ngx_memcpy(p2, p, size); + } + + ngx_free(p); +} + + +#if (NGX_MEMCPY_LIMIT) + +void * +ngx_memcpy(void *dst, const void *src, size_t n) +{ + if (n > NGX_MEMCPY_LIMIT) { + ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "memcpy %uz bytes", n); + ngx_debug_point(); + } + + return memcpy(dst, src, n); +} + +#endif diff --git a/app/nginx/src/core/ngx_string.h b/app/nginx/src/core/ngx_string.h new file mode 100644 index 0000000..7363bd2 --- /dev/null +++ b/app/nginx/src/core/ngx_string.h @@ -0,0 +1,234 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_STRING_H_INCLUDED_ +#define _NGX_STRING_H_INCLUDED_ + + +#include +#include + + +typedef struct { + size_t len; + u_char *data; +} ngx_str_t; + + +typedef struct { + ngx_str_t key; + ngx_str_t value; +} ngx_keyval_t; + + +typedef struct { + unsigned len:28; + + unsigned valid:1; + unsigned no_cacheable:1; + unsigned not_found:1; + unsigned escape:1; + + u_char *data; +} ngx_variable_value_t; + + +#define ngx_string(str) { sizeof(str) - 1, (u_char *) str } +#define ngx_null_string { 0, NULL } +#define ngx_str_set(str, text) \ + (str)->len = sizeof(text) - 1; (str)->data = (u_char *) text +#define ngx_str_null(str) (str)->len = 0; (str)->data = NULL + + +#define ngx_tolower(c) (u_char) ((c >= 'A' && c <= 'Z') ? (c | 0x20) : c) +#define ngx_toupper(c) (u_char) ((c >= 'a' && c <= 'z') ? (c & ~0x20) : c) + +void ngx_strlow(u_char *dst, u_char *src, size_t n); + + +#define ngx_strncmp(s1, s2, n) strncmp((const char *) s1, (const char *) s2, n) + + +/* msvc and icc7 compile strcmp() to inline loop */ +#define ngx_strcmp(s1, s2) strcmp((const char *) s1, (const char *) s2) + + +#define ngx_strstr(s1, s2) strstr((const char *) s1, (const char *) s2) +#define ngx_strlen(s) strlen((const char *) s) + +#define ngx_strchr(s1, c) strchr((const char *) s1, (int) c) + +static ngx_inline u_char * +ngx_strlchr(u_char *p, u_char *last, u_char c) +{ + while (p < last) { + + if (*p == c) { + return p; + } + + p++; + } + + return NULL; +} + + +/* + * msvc and icc7 compile memset() to the inline "rep stos" + * while ZeroMemory() and bzero() are the calls. + * icc7 may also inline several mov's of a zeroed register for small blocks. + */ +#define ngx_memzero(buf, n) (void) memset(buf, 0, n) +#define ngx_memset(buf, c, n) (void) memset(buf, c, n) + + +#if (NGX_MEMCPY_LIMIT) + +void *ngx_memcpy(void *dst, const void *src, size_t n); +#define ngx_cpymem(dst, src, n) (((u_char *) ngx_memcpy(dst, src, n)) + (n)) + +#else + +/* + * gcc3, msvc, and icc7 compile memcpy() to the inline "rep movs". + * gcc3 compiles memcpy(d, s, 4) to the inline "mov"es. + * icc8 compile memcpy(d, s, 4) to the inline "mov"es or XMM moves. + */ +#define ngx_memcpy(dst, src, n) (void) memcpy(dst, src, n) +#define ngx_cpymem(dst, src, n) (((u_char *) memcpy(dst, src, n)) + (n)) + +#endif + + +#if ( __INTEL_COMPILER >= 800 ) + +/* + * the simple inline cycle copies the variable length strings up to 16 + * bytes faster than icc8 autodetecting _intel_fast_memcpy() + */ + +static ngx_inline u_char * +ngx_copy(u_char *dst, u_char *src, size_t len) +{ + if (len < 17) { + + while (len) { + *dst++ = *src++; + len--; + } + + return dst; + + } else { + return ngx_cpymem(dst, src, len); + } +} + +#else + +#define ngx_copy ngx_cpymem + +#endif + + +#define ngx_memmove(dst, src, n) (void) memmove(dst, src, n) +#define ngx_movemem(dst, src, n) (((u_char *) memmove(dst, src, n)) + (n)) + + +/* msvc and icc7 compile memcmp() to the inline loop */ +#define ngx_memcmp(s1, s2, n) memcmp((const char *) s1, (const char *) s2, n) + + +u_char *ngx_cpystrn(u_char *dst, u_char *src, size_t n); +u_char *ngx_pstrdup(ngx_pool_t *pool, ngx_str_t *src); +u_char * ngx_cdecl ngx_sprintf(u_char *buf, const char *fmt, ...); +u_char * ngx_cdecl ngx_snprintf(u_char *buf, size_t max, const char *fmt, ...); +u_char * ngx_cdecl ngx_slprintf(u_char *buf, u_char *last, const char *fmt, + ...); +u_char *ngx_vslprintf(u_char *buf, u_char *last, const char *fmt, va_list args); +#define ngx_vsnprintf(buf, max, fmt, args) \ + ngx_vslprintf(buf, buf + (max), fmt, args) + +ngx_int_t ngx_strcasecmp(u_char *s1, u_char *s2); +ngx_int_t ngx_strncasecmp(u_char *s1, u_char *s2, size_t n); + +u_char *ngx_strnstr(u_char *s1, char *s2, size_t n); + +u_char *ngx_strstrn(u_char *s1, char *s2, size_t n); +u_char *ngx_strcasestrn(u_char *s1, char *s2, size_t n); +u_char *ngx_strlcasestrn(u_char *s1, u_char *last, u_char *s2, size_t n); + +ngx_int_t ngx_rstrncmp(u_char *s1, u_char *s2, size_t n); +ngx_int_t ngx_rstrncasecmp(u_char *s1, u_char *s2, size_t n); +ngx_int_t ngx_memn2cmp(u_char *s1, u_char *s2, size_t n1, size_t n2); +ngx_int_t ngx_dns_strcmp(u_char *s1, u_char *s2); +ngx_int_t ngx_filename_cmp(u_char *s1, u_char *s2, size_t n); + +ngx_int_t ngx_atoi(u_char *line, size_t n); +ngx_int_t ngx_atofp(u_char *line, size_t n, size_t point); +ssize_t ngx_atosz(u_char *line, size_t n); +off_t ngx_atoof(u_char *line, size_t n); +time_t ngx_atotm(u_char *line, size_t n); +ngx_int_t ngx_hextoi(u_char *line, size_t n); + +u_char *ngx_hex_dump(u_char *dst, u_char *src, size_t len); + + +#define ngx_base64_encoded_length(len) (((len + 2) / 3) * 4) +#define ngx_base64_decoded_length(len) (((len + 3) / 4) * 3) + +void ngx_encode_base64(ngx_str_t *dst, ngx_str_t *src); +void ngx_encode_base64url(ngx_str_t *dst, ngx_str_t *src); +ngx_int_t ngx_decode_base64(ngx_str_t *dst, ngx_str_t *src); +ngx_int_t ngx_decode_base64url(ngx_str_t *dst, ngx_str_t *src); + +uint32_t ngx_utf8_decode(u_char **p, size_t n); +size_t ngx_utf8_length(u_char *p, size_t n); +u_char *ngx_utf8_cpystrn(u_char *dst, u_char *src, size_t n, size_t len); + + +#define NGX_ESCAPE_URI 0 +#define NGX_ESCAPE_ARGS 1 +#define NGX_ESCAPE_URI_COMPONENT 2 +#define NGX_ESCAPE_HTML 3 +#define NGX_ESCAPE_REFRESH 4 +#define NGX_ESCAPE_MEMCACHED 5 +#define NGX_ESCAPE_MAIL_AUTH 6 + +#define NGX_UNESCAPE_URI 1 +#define NGX_UNESCAPE_REDIRECT 2 + +uintptr_t ngx_escape_uri(u_char *dst, u_char *src, size_t size, + ngx_uint_t type); +void ngx_unescape_uri(u_char **dst, u_char **src, size_t size, ngx_uint_t type); +uintptr_t ngx_escape_html(u_char *dst, u_char *src, size_t size); +uintptr_t ngx_escape_json(u_char *dst, u_char *src, size_t size); + + +typedef struct { + ngx_rbtree_node_t node; + ngx_str_t str; +} ngx_str_node_t; + + +void ngx_str_rbtree_insert_value(ngx_rbtree_node_t *temp, + ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel); +ngx_str_node_t *ngx_str_rbtree_lookup(ngx_rbtree_t *rbtree, ngx_str_t *name, + uint32_t hash); + + +void ngx_sort(void *base, size_t n, size_t size, + ngx_int_t (*cmp)(const void *, const void *)); +#define ngx_qsort qsort + + +#define ngx_value_helper(n) #n +#define ngx_value(n) ngx_value_helper(n) + + +#endif /* _NGX_STRING_H_INCLUDED_ */ diff --git a/app/nginx/src/core/ngx_syslog.c b/app/nginx/src/core/ngx_syslog.c new file mode 100644 index 0000000..0a67928 --- /dev/null +++ b/app/nginx/src/core/ngx_syslog.c @@ -0,0 +1,382 @@ + +/* + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include + + +#define NGX_SYSLOG_MAX_STR \ + NGX_MAX_ERROR_STR + sizeof("<255>Jan 01 00:00:00 ") - 1 \ + + (NGX_MAXHOSTNAMELEN - 1) + 1 /* space */ \ + + 32 /* tag */ + 2 /* colon, space */ + + +static char *ngx_syslog_parse_args(ngx_conf_t *cf, ngx_syslog_peer_t *peer); +static ngx_int_t ngx_syslog_init_peer(ngx_syslog_peer_t *peer); +static void ngx_syslog_cleanup(void *data); + + +static char *facilities[] = { + "kern", "user", "mail", "daemon", "auth", "intern", "lpr", "news", "uucp", + "clock", "authpriv", "ftp", "ntp", "audit", "alert", "cron", "local0", + "local1", "local2", "local3", "local4", "local5", "local6", "local7", + NULL +}; + +/* note 'error/warn' like in nginx.conf, not 'err/warning' */ +static char *severities[] = { + "emerg", "alert", "crit", "error", "warn", "notice", "info", "debug", NULL +}; + +static ngx_log_t ngx_syslog_dummy_log; +static ngx_event_t ngx_syslog_dummy_event; + + +char * +ngx_syslog_process_conf(ngx_conf_t *cf, ngx_syslog_peer_t *peer) +{ + peer->pool = cf->pool; + peer->facility = NGX_CONF_UNSET_UINT; + peer->severity = NGX_CONF_UNSET_UINT; + + if (ngx_syslog_parse_args(cf, peer) != NGX_CONF_OK) { + return NGX_CONF_ERROR; + } + + if (peer->server.sockaddr == NULL) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "no syslog server specified"); + return NGX_CONF_ERROR; + } + + if (peer->facility == NGX_CONF_UNSET_UINT) { + peer->facility = 23; /* local7 */ + } + + if (peer->severity == NGX_CONF_UNSET_UINT) { + peer->severity = 6; /* info */ + } + + if (peer->tag.data == NULL) { + ngx_str_set(&peer->tag, "nginx"); + } + + peer->conn.fd = (ngx_socket_t) -1; + + return NGX_CONF_OK; +} + + +static char * +ngx_syslog_parse_args(ngx_conf_t *cf, ngx_syslog_peer_t *peer) +{ + u_char *p, *comma, c; + size_t len; + ngx_str_t *value; + ngx_url_t u; + ngx_uint_t i; + + value = cf->args->elts; + + p = value[1].data + sizeof("syslog:") - 1; + + for ( ;; ) { + comma = (u_char *) ngx_strchr(p, ','); + + if (comma != NULL) { + len = comma - p; + *comma = '\0'; + + } else { + len = value[1].data + value[1].len - p; + } + + if (ngx_strncmp(p, "server=", 7) == 0) { + + if (peer->server.sockaddr != NULL) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "duplicate syslog \"server\""); + return NGX_CONF_ERROR; + } + + ngx_memzero(&u, sizeof(ngx_url_t)); + + u.url.data = p + 7; + u.url.len = len - 7; + u.default_port = 514; + + if (ngx_parse_url(cf->pool, &u) != NGX_OK) { + if (u.err) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "%s in syslog server \"%V\"", + u.err, &u.url); + } + + return NGX_CONF_ERROR; + } + + peer->server = u.addrs[0]; + + } else if (ngx_strncmp(p, "facility=", 9) == 0) { + + if (peer->facility != NGX_CONF_UNSET_UINT) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "duplicate syslog \"facility\""); + return NGX_CONF_ERROR; + } + + for (i = 0; facilities[i] != NULL; i++) { + + if (ngx_strcmp(p + 9, facilities[i]) == 0) { + peer->facility = i; + goto next; + } + } + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "unknown syslog facility \"%s\"", p + 9); + return NGX_CONF_ERROR; + + } else if (ngx_strncmp(p, "severity=", 9) == 0) { + + if (peer->severity != NGX_CONF_UNSET_UINT) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "duplicate syslog \"severity\""); + return NGX_CONF_ERROR; + } + + for (i = 0; severities[i] != NULL; i++) { + + if (ngx_strcmp(p + 9, severities[i]) == 0) { + peer->severity = i; + goto next; + } + } + + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "unknown syslog severity \"%s\"", p + 9); + return NGX_CONF_ERROR; + + } else if (ngx_strncmp(p, "tag=", 4) == 0) { + + if (peer->tag.data != NULL) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "duplicate syslog \"tag\""); + return NGX_CONF_ERROR; + } + + /* + * RFC 3164: the TAG is a string of ABNF alphanumeric characters + * that MUST NOT exceed 32 characters. + */ + if (len - 4 > 32) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "syslog tag length exceeds 32"); + return NGX_CONF_ERROR; + } + + for (i = 4; i < len; i++) { + c = ngx_tolower(p[i]); + + if (c < '0' || (c > '9' && c < 'a' && c != '_') || c > 'z') { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "syslog \"tag\" only allows " + "alphanumeric characters " + "and underscore"); + return NGX_CONF_ERROR; + } + } + + peer->tag.data = p + 4; + peer->tag.len = len - 4; + + } else if (len == 10 && ngx_strncmp(p, "nohostname", 10) == 0) { + peer->nohostname = 1; + + } else { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "unknown syslog parameter \"%s\"", p); + return NGX_CONF_ERROR; + } + + next: + + if (comma == NULL) { + break; + } + + p = comma + 1; + } + + return NGX_CONF_OK; +} + + +u_char * +ngx_syslog_add_header(ngx_syslog_peer_t *peer, u_char *buf) +{ + ngx_uint_t pri; + + pri = peer->facility * 8 + peer->severity; + + if (peer->nohostname) { + return ngx_sprintf(buf, "<%ui>%V %V: ", pri, &ngx_cached_syslog_time, + &peer->tag); + } + + return ngx_sprintf(buf, "<%ui>%V %V %V: ", pri, &ngx_cached_syslog_time, + &ngx_cycle->hostname, &peer->tag); +} + + +void +ngx_syslog_writer(ngx_log_t *log, ngx_uint_t level, u_char *buf, + size_t len) +{ + u_char *p, msg[NGX_SYSLOG_MAX_STR]; + ngx_uint_t head_len; + ngx_syslog_peer_t *peer; + + peer = log->wdata; + + if (peer->busy) { + return; + } + + peer->busy = 1; + peer->severity = level - 1; + + p = ngx_syslog_add_header(peer, msg); + head_len = p - msg; + + len -= NGX_LINEFEED_SIZE; + + if (len > NGX_SYSLOG_MAX_STR - head_len) { + len = NGX_SYSLOG_MAX_STR - head_len; + } + + p = ngx_snprintf(p, len, "%s", buf); + + (void) ngx_syslog_send(peer, msg, p - msg); + + peer->busy = 0; +} + + +ssize_t +ngx_syslog_send(ngx_syslog_peer_t *peer, u_char *buf, size_t len) +{ + ssize_t n; + + if (peer->conn.fd == (ngx_socket_t) -1) { + if (ngx_syslog_init_peer(peer) != NGX_OK) { + return NGX_ERROR; + } + } + + /* log syslog socket events with valid log */ + peer->conn.log = ngx_cycle->log; + + if (ngx_send) { + n = ngx_send(&peer->conn, buf, len); + + } else { + /* event module has not yet set ngx_io */ + n = ngx_os_io.send(&peer->conn, buf, len); + } + +#if (NGX_HAVE_UNIX_DOMAIN) + + if (n == NGX_ERROR && peer->server.sockaddr->sa_family == AF_UNIX) { + + if (ngx_close_socket(peer->conn.fd) == -1) { + ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_socket_errno, + ngx_close_socket_n " failed"); + } + + peer->conn.fd = (ngx_socket_t) -1; + } + +#endif + + return n; +} + + +static ngx_int_t +ngx_syslog_init_peer(ngx_syslog_peer_t *peer) +{ + ngx_socket_t fd; + ngx_pool_cleanup_t *cln; + + peer->conn.read = &ngx_syslog_dummy_event; + peer->conn.write = &ngx_syslog_dummy_event; + + ngx_syslog_dummy_event.log = &ngx_syslog_dummy_log; + + fd = ngx_socket(peer->server.sockaddr->sa_family, SOCK_DGRAM, 0); + if (fd == (ngx_socket_t) -1) { + ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_socket_errno, + ngx_socket_n " failed"); + return NGX_ERROR; + } + + if (ngx_nonblocking(fd) == -1) { + ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_socket_errno, + ngx_nonblocking_n " failed"); + goto failed; + } + + if (connect(fd, peer->server.sockaddr, peer->server.socklen) == -1) { + ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_socket_errno, + "connect() failed"); + goto failed; + } + + cln = ngx_pool_cleanup_add(peer->pool, 0); + if (cln == NULL) { + goto failed; + } + + cln->data = peer; + cln->handler = ngx_syslog_cleanup; + + peer->conn.fd = fd; + + /* UDP sockets are always ready to write */ + peer->conn.write->ready = 1; + + return NGX_OK; + +failed: + + if (ngx_close_socket(fd) == -1) { + ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_socket_errno, + ngx_close_socket_n " failed"); + } + + return NGX_ERROR; +} + + +static void +ngx_syslog_cleanup(void *data) +{ + ngx_syslog_peer_t *peer = data; + + /* prevents further use of this peer */ + peer->busy = 1; + + if (peer->conn.fd == (ngx_socket_t) -1) { + return; + } + + if (ngx_close_socket(peer->conn.fd) == -1) { + ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_socket_errno, + ngx_close_socket_n " failed"); + } +} diff --git a/app/nginx/src/core/ngx_syslog.h b/app/nginx/src/core/ngx_syslog.h new file mode 100644 index 0000000..cc4c842 --- /dev/null +++ b/app/nginx/src/core/ngx_syslog.h @@ -0,0 +1,31 @@ + +/* + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_SYSLOG_H_INCLUDED_ +#define _NGX_SYSLOG_H_INCLUDED_ + + +typedef struct { + ngx_pool_t *pool; + ngx_uint_t facility; + ngx_uint_t severity; + ngx_str_t tag; + + ngx_addr_t server; + ngx_connection_t conn; + unsigned busy:1; + unsigned nohostname:1; +} ngx_syslog_peer_t; + + +char *ngx_syslog_process_conf(ngx_conf_t *cf, ngx_syslog_peer_t *peer); +u_char *ngx_syslog_add_header(ngx_syslog_peer_t *peer, u_char *buf); +void ngx_syslog_writer(ngx_log_t *log, ngx_uint_t level, u_char *buf, + size_t len); +ssize_t ngx_syslog_send(ngx_syslog_peer_t *peer, u_char *buf, size_t len); + + +#endif /* _NGX_SYSLOG_H_INCLUDED_ */ diff --git a/app/nginx/src/core/ngx_thread_pool.c b/app/nginx/src/core/ngx_thread_pool.c new file mode 100644 index 0000000..7fb0f7f --- /dev/null +++ b/app/nginx/src/core/ngx_thread_pool.c @@ -0,0 +1,641 @@ + +/* + * Copyright (C) Nginx, Inc. + * Copyright (C) Valentin V. Bartenev + * Copyright (C) Ruslan Ermilov + */ + + +#include +#include +#include + + +typedef struct { + ngx_array_t pools; +} ngx_thread_pool_conf_t; + + +typedef struct { + ngx_thread_task_t *first; + ngx_thread_task_t **last; +} ngx_thread_pool_queue_t; + +#define ngx_thread_pool_queue_init(q) \ + (q)->first = NULL; \ + (q)->last = &(q)->first + + +struct ngx_thread_pool_s { + ngx_thread_mutex_t mtx; + ngx_thread_pool_queue_t queue; + ngx_int_t waiting; + ngx_thread_cond_t cond; + + ngx_log_t *log; + + ngx_str_t name; + ngx_uint_t threads; + ngx_int_t max_queue; + + u_char *file; + ngx_uint_t line; +}; + + +static ngx_int_t ngx_thread_pool_init(ngx_thread_pool_t *tp, ngx_log_t *log, + ngx_pool_t *pool); +static void ngx_thread_pool_destroy(ngx_thread_pool_t *tp); +static void ngx_thread_pool_exit_handler(void *data, ngx_log_t *log); + +static void *ngx_thread_pool_cycle(void *data); +static void ngx_thread_pool_handler(ngx_event_t *ev); + +static char *ngx_thread_pool(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); + +static void *ngx_thread_pool_create_conf(ngx_cycle_t *cycle); +static char *ngx_thread_pool_init_conf(ngx_cycle_t *cycle, void *conf); + +static ngx_int_t ngx_thread_pool_init_worker(ngx_cycle_t *cycle); +static void ngx_thread_pool_exit_worker(ngx_cycle_t *cycle); + + +static ngx_command_t ngx_thread_pool_commands[] = { + + { ngx_string("thread_pool"), + NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE23, + ngx_thread_pool, + 0, + 0, + NULL }, + + ngx_null_command +}; + + +static ngx_core_module_t ngx_thread_pool_module_ctx = { + ngx_string("thread_pool"), + ngx_thread_pool_create_conf, + ngx_thread_pool_init_conf +}; + + +ngx_module_t ngx_thread_pool_module = { + NGX_MODULE_V1, + &ngx_thread_pool_module_ctx, /* module context */ + ngx_thread_pool_commands, /* module directives */ + NGX_CORE_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + ngx_thread_pool_init_worker, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + ngx_thread_pool_exit_worker, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +static ngx_str_t ngx_thread_pool_default = ngx_string("default"); + +static ngx_uint_t ngx_thread_pool_task_id; +static ngx_atomic_t ngx_thread_pool_done_lock; +static ngx_thread_pool_queue_t ngx_thread_pool_done; + + +static ngx_int_t +ngx_thread_pool_init(ngx_thread_pool_t *tp, ngx_log_t *log, ngx_pool_t *pool) +{ + int err; + pthread_t tid; + ngx_uint_t n; + pthread_attr_t attr; + + if (ngx_notify == NULL) { + ngx_log_error(NGX_LOG_ALERT, log, 0, + "the configured event method cannot be used with thread pools"); + return NGX_ERROR; + } + + ngx_thread_pool_queue_init(&tp->queue); + + if (ngx_thread_mutex_create(&tp->mtx, log) != NGX_OK) { + return NGX_ERROR; + } + + if (ngx_thread_cond_create(&tp->cond, log) != NGX_OK) { + (void) ngx_thread_mutex_destroy(&tp->mtx, log); + return NGX_ERROR; + } + + tp->log = log; + + err = pthread_attr_init(&attr); + if (err) { + ngx_log_error(NGX_LOG_ALERT, log, err, + "pthread_attr_init() failed"); + return NGX_ERROR; + } + + err = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + if (err) { + ngx_log_error(NGX_LOG_ALERT, log, err, + "pthread_attr_setdetachstate() failed"); + return NGX_ERROR; + } + +#if 0 + err = pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN); + if (err) { + ngx_log_error(NGX_LOG_ALERT, log, err, + "pthread_attr_setstacksize() failed"); + return NGX_ERROR; + } +#endif + + for (n = 0; n < tp->threads; n++) { + err = pthread_create(&tid, &attr, ngx_thread_pool_cycle, tp); + if (err) { + ngx_log_error(NGX_LOG_ALERT, log, err, + "pthread_create() failed"); + return NGX_ERROR; + } + } + + (void) pthread_attr_destroy(&attr); + + return NGX_OK; +} + + +static void +ngx_thread_pool_destroy(ngx_thread_pool_t *tp) +{ + ngx_uint_t n; + ngx_thread_task_t task; + volatile ngx_uint_t lock; + + ngx_memzero(&task, sizeof(ngx_thread_task_t)); + + task.handler = ngx_thread_pool_exit_handler; + task.ctx = (void *) &lock; + + for (n = 0; n < tp->threads; n++) { + lock = 1; + + if (ngx_thread_task_post(tp, &task) != NGX_OK) { + return; + } + + while (lock) { + ngx_sched_yield(); + } + + task.event.active = 0; + } + + (void) ngx_thread_cond_destroy(&tp->cond, tp->log); + + (void) ngx_thread_mutex_destroy(&tp->mtx, tp->log); +} + + +static void +ngx_thread_pool_exit_handler(void *data, ngx_log_t *log) +{ + ngx_uint_t *lock = data; + + *lock = 0; + + pthread_exit(0); +} + + +ngx_thread_task_t * +ngx_thread_task_alloc(ngx_pool_t *pool, size_t size) +{ + ngx_thread_task_t *task; + + task = ngx_pcalloc(pool, sizeof(ngx_thread_task_t) + size); + if (task == NULL) { + return NULL; + } + + task->ctx = task + 1; + + return task; +} + + +ngx_int_t +ngx_thread_task_post(ngx_thread_pool_t *tp, ngx_thread_task_t *task) +{ + if (task->event.active) { + ngx_log_error(NGX_LOG_ALERT, tp->log, 0, + "task #%ui already active", task->id); + return NGX_ERROR; + } + + if (ngx_thread_mutex_lock(&tp->mtx, tp->log) != NGX_OK) { + return NGX_ERROR; + } + + if (tp->waiting >= tp->max_queue) { + (void) ngx_thread_mutex_unlock(&tp->mtx, tp->log); + + ngx_log_error(NGX_LOG_ERR, tp->log, 0, + "thread pool \"%V\" queue overflow: %i tasks waiting", + &tp->name, tp->waiting); + return NGX_ERROR; + } + + task->event.active = 1; + + task->id = ngx_thread_pool_task_id++; + task->next = NULL; + + if (ngx_thread_cond_signal(&tp->cond, tp->log) != NGX_OK) { + (void) ngx_thread_mutex_unlock(&tp->mtx, tp->log); + return NGX_ERROR; + } + + *tp->queue.last = task; + tp->queue.last = &task->next; + + tp->waiting++; + + (void) ngx_thread_mutex_unlock(&tp->mtx, tp->log); + + ngx_log_debug2(NGX_LOG_DEBUG_CORE, tp->log, 0, + "task #%ui added to thread pool \"%V\"", + task->id, &tp->name); + + return NGX_OK; +} + + +static void * +ngx_thread_pool_cycle(void *data) +{ + ngx_thread_pool_t *tp = data; + + int err; + sigset_t set; + ngx_thread_task_t *task; + +#if 0 + ngx_time_update(); +#endif + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, tp->log, 0, + "thread in pool \"%V\" started", &tp->name); + + sigfillset(&set); + + sigdelset(&set, SIGILL); + sigdelset(&set, SIGFPE); + sigdelset(&set, SIGSEGV); + sigdelset(&set, SIGBUS); + + err = pthread_sigmask(SIG_BLOCK, &set, NULL); + if (err) { + ngx_log_error(NGX_LOG_ALERT, tp->log, err, "pthread_sigmask() failed"); + return NULL; + } + + for ( ;; ) { + if (ngx_thread_mutex_lock(&tp->mtx, tp->log) != NGX_OK) { + return NULL; + } + + /* the number may become negative */ + tp->waiting--; + + while (tp->queue.first == NULL) { + if (ngx_thread_cond_wait(&tp->cond, &tp->mtx, tp->log) + != NGX_OK) + { + (void) ngx_thread_mutex_unlock(&tp->mtx, tp->log); + return NULL; + } + } + + task = tp->queue.first; + tp->queue.first = task->next; + + if (tp->queue.first == NULL) { + tp->queue.last = &tp->queue.first; + } + + if (ngx_thread_mutex_unlock(&tp->mtx, tp->log) != NGX_OK) { + return NULL; + } + +#if 0 + ngx_time_update(); +#endif + + ngx_log_debug2(NGX_LOG_DEBUG_CORE, tp->log, 0, + "run task #%ui in thread pool \"%V\"", + task->id, &tp->name); + + task->handler(task->ctx, tp->log); + + ngx_log_debug2(NGX_LOG_DEBUG_CORE, tp->log, 0, + "complete task #%ui in thread pool \"%V\"", + task->id, &tp->name); + + task->next = NULL; + + ngx_spinlock(&ngx_thread_pool_done_lock, 1, 2048); + + *ngx_thread_pool_done.last = task; + ngx_thread_pool_done.last = &task->next; + + ngx_memory_barrier(); + + ngx_unlock(&ngx_thread_pool_done_lock); + + (void) ngx_notify(ngx_thread_pool_handler); + } +} + + +static void +ngx_thread_pool_handler(ngx_event_t *ev) +{ + ngx_event_t *event; + ngx_thread_task_t *task; + + ngx_log_debug0(NGX_LOG_DEBUG_CORE, ev->log, 0, "thread pool handler"); + + ngx_spinlock(&ngx_thread_pool_done_lock, 1, 2048); + + task = ngx_thread_pool_done.first; + ngx_thread_pool_done.first = NULL; + ngx_thread_pool_done.last = &ngx_thread_pool_done.first; + + ngx_memory_barrier(); + + ngx_unlock(&ngx_thread_pool_done_lock); + + while (task) { + ngx_log_debug1(NGX_LOG_DEBUG_CORE, ev->log, 0, + "run completion handler for task #%ui", task->id); + + event = &task->event; + task = task->next; + + event->complete = 1; + event->active = 0; + + event->handler(event); + } +} + + +static void * +ngx_thread_pool_create_conf(ngx_cycle_t *cycle) +{ + ngx_thread_pool_conf_t *tcf; + + tcf = ngx_pcalloc(cycle->pool, sizeof(ngx_thread_pool_conf_t)); + if (tcf == NULL) { + return NULL; + } + + if (ngx_array_init(&tcf->pools, cycle->pool, 4, + sizeof(ngx_thread_pool_t *)) + != NGX_OK) + { + return NULL; + } + + return tcf; +} + + +static char * +ngx_thread_pool_init_conf(ngx_cycle_t *cycle, void *conf) +{ + ngx_thread_pool_conf_t *tcf = conf; + + ngx_uint_t i; + ngx_thread_pool_t **tpp; + + tpp = tcf->pools.elts; + + for (i = 0; i < tcf->pools.nelts; i++) { + + if (tpp[i]->threads) { + continue; + } + + if (tpp[i]->name.len == ngx_thread_pool_default.len + && ngx_strncmp(tpp[i]->name.data, ngx_thread_pool_default.data, + ngx_thread_pool_default.len) + == 0) + { + tpp[i]->threads = 32; + tpp[i]->max_queue = 65536; + continue; + } + + ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, + "unknown thread pool \"%V\" in %s:%ui", + &tpp[i]->name, tpp[i]->file, tpp[i]->line); + + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} + + +static char * +ngx_thread_pool(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_str_t *value; + ngx_uint_t i; + ngx_thread_pool_t *tp; + + value = cf->args->elts; + + tp = ngx_thread_pool_add(cf, &value[1]); + + if (tp == NULL) { + return NGX_CONF_ERROR; + } + + if (tp->threads) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "duplicate thread pool \"%V\"", &tp->name); + return NGX_CONF_ERROR; + } + + tp->max_queue = 65536; + + for (i = 2; i < cf->args->nelts; i++) { + + if (ngx_strncmp(value[i].data, "threads=", 8) == 0) { + + tp->threads = ngx_atoi(value[i].data + 8, value[i].len - 8); + + if (tp->threads == (ngx_uint_t) NGX_ERROR || tp->threads == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid threads value \"%V\"", &value[i]); + return NGX_CONF_ERROR; + } + + continue; + } + + if (ngx_strncmp(value[i].data, "max_queue=", 10) == 0) { + + tp->max_queue = ngx_atoi(value[i].data + 10, value[i].len - 10); + + if (tp->max_queue == NGX_ERROR) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid max_queue value \"%V\"", &value[i]); + return NGX_CONF_ERROR; + } + + continue; + } + } + + if (tp->threads == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "\"%V\" must have \"threads\" parameter", + &cmd->name); + return NGX_CONF_ERROR; + } + + return NGX_CONF_OK; +} + + +ngx_thread_pool_t * +ngx_thread_pool_add(ngx_conf_t *cf, ngx_str_t *name) +{ + ngx_thread_pool_t *tp, **tpp; + ngx_thread_pool_conf_t *tcf; + + if (name == NULL) { + name = &ngx_thread_pool_default; + } + + tp = ngx_thread_pool_get(cf->cycle, name); + + if (tp) { + return tp; + } + + tp = ngx_pcalloc(cf->pool, sizeof(ngx_thread_pool_t)); + if (tp == NULL) { + return NULL; + } + + tp->name = *name; + tp->file = cf->conf_file->file.name.data; + tp->line = cf->conf_file->line; + + tcf = (ngx_thread_pool_conf_t *) ngx_get_conf(cf->cycle->conf_ctx, + ngx_thread_pool_module); + + tpp = ngx_array_push(&tcf->pools); + if (tpp == NULL) { + return NULL; + } + + *tpp = tp; + + return tp; +} + + +ngx_thread_pool_t * +ngx_thread_pool_get(ngx_cycle_t *cycle, ngx_str_t *name) +{ + ngx_uint_t i; + ngx_thread_pool_t **tpp; + ngx_thread_pool_conf_t *tcf; + + tcf = (ngx_thread_pool_conf_t *) ngx_get_conf(cycle->conf_ctx, + ngx_thread_pool_module); + + tpp = tcf->pools.elts; + + for (i = 0; i < tcf->pools.nelts; i++) { + + if (tpp[i]->name.len == name->len + && ngx_strncmp(tpp[i]->name.data, name->data, name->len) == 0) + { + return tpp[i]; + } + } + + return NULL; +} + + +static ngx_int_t +ngx_thread_pool_init_worker(ngx_cycle_t *cycle) +{ + ngx_uint_t i; + ngx_thread_pool_t **tpp; + ngx_thread_pool_conf_t *tcf; + + if (ngx_process != NGX_PROCESS_WORKER + && ngx_process != NGX_PROCESS_SINGLE) + { + return NGX_OK; + } + + tcf = (ngx_thread_pool_conf_t *) ngx_get_conf(cycle->conf_ctx, + ngx_thread_pool_module); + + if (tcf == NULL) { + return NGX_OK; + } + + ngx_thread_pool_queue_init(&ngx_thread_pool_done); + + tpp = tcf->pools.elts; + + for (i = 0; i < tcf->pools.nelts; i++) { + if (ngx_thread_pool_init(tpp[i], cycle->log, cycle->pool) != NGX_OK) { + return NGX_ERROR; + } + } + + return NGX_OK; +} + + +static void +ngx_thread_pool_exit_worker(ngx_cycle_t *cycle) +{ + ngx_uint_t i; + ngx_thread_pool_t **tpp; + ngx_thread_pool_conf_t *tcf; + + if (ngx_process != NGX_PROCESS_WORKER + && ngx_process != NGX_PROCESS_SINGLE) + { + return; + } + + tcf = (ngx_thread_pool_conf_t *) ngx_get_conf(cycle->conf_ctx, + ngx_thread_pool_module); + + if (tcf == NULL) { + return; + } + + tpp = tcf->pools.elts; + + for (i = 0; i < tcf->pools.nelts; i++) { + ngx_thread_pool_destroy(tpp[i]); + } +} diff --git a/app/nginx/src/core/ngx_thread_pool.h b/app/nginx/src/core/ngx_thread_pool.h new file mode 100644 index 0000000..5e5adf6 --- /dev/null +++ b/app/nginx/src/core/ngx_thread_pool.h @@ -0,0 +1,36 @@ + +/* + * Copyright (C) Nginx, Inc. + * Copyright (C) Valentin V. Bartenev + */ + + +#ifndef _NGX_THREAD_POOL_H_INCLUDED_ +#define _NGX_THREAD_POOL_H_INCLUDED_ + + +#include +#include +#include + + +struct ngx_thread_task_s { + ngx_thread_task_t *next; + ngx_uint_t id; + void *ctx; + void (*handler)(void *data, ngx_log_t *log); + ngx_event_t event; +}; + + +typedef struct ngx_thread_pool_s ngx_thread_pool_t; + + +ngx_thread_pool_t *ngx_thread_pool_add(ngx_conf_t *cf, ngx_str_t *name); +ngx_thread_pool_t *ngx_thread_pool_get(ngx_cycle_t *cycle, ngx_str_t *name); + +ngx_thread_task_t *ngx_thread_task_alloc(ngx_pool_t *pool, size_t size); +ngx_int_t ngx_thread_task_post(ngx_thread_pool_t *tp, ngx_thread_task_t *task); + + +#endif /* _NGX_THREAD_POOL_H_INCLUDED_ */ diff --git a/app/nginx/src/core/ngx_times.c b/app/nginx/src/core/ngx_times.c new file mode 100644 index 0000000..843314a --- /dev/null +++ b/app/nginx/src/core/ngx_times.c @@ -0,0 +1,428 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include +#include + + +/* + * The time may be updated by signal handler or by several threads. + * The time update operations are rare and require to hold the ngx_time_lock. + * The time read operations are frequent, so they are lock-free and get time + * values and strings from the current slot. Thus thread may get the corrupted + * values only if it is preempted while copying and then it is not scheduled + * to run more than NGX_TIME_SLOTS seconds. + */ + +#define NGX_TIME_SLOTS 64 + +static ngx_uint_t slot; +static ngx_atomic_t ngx_time_lock; + +volatile ngx_msec_t ngx_current_msec; +volatile ngx_time_t *ngx_cached_time; +volatile ngx_str_t ngx_cached_err_log_time; +volatile ngx_str_t ngx_cached_http_time; +volatile ngx_str_t ngx_cached_http_log_time; +volatile ngx_str_t ngx_cached_http_log_iso8601; +volatile ngx_str_t ngx_cached_syslog_time; + +#if !(NGX_WIN32) + +/* + * localtime() and localtime_r() are not Async-Signal-Safe functions, therefore, + * they must not be called by a signal handler, so we use the cached + * GMT offset value. Fortunately the value is changed only two times a year. + */ + +static ngx_int_t cached_gmtoff; +#endif + +static ngx_time_t cached_time[NGX_TIME_SLOTS]; +static u_char cached_err_log_time[NGX_TIME_SLOTS] + [sizeof("1970/09/28 12:00:00")]; +static u_char cached_http_time[NGX_TIME_SLOTS] + [sizeof("Mon, 28 Sep 1970 06:00:00 GMT")]; +static u_char cached_http_log_time[NGX_TIME_SLOTS] + [sizeof("28/Sep/1970:12:00:00 +0600")]; +static u_char cached_http_log_iso8601[NGX_TIME_SLOTS] + [sizeof("1970-09-28T12:00:00+06:00")]; +static u_char cached_syslog_time[NGX_TIME_SLOTS] + [sizeof("Sep 28 12:00:00")]; + + +static char *week[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; +static char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; + +void +ngx_time_init(void) +{ + ngx_cached_err_log_time.len = sizeof("1970/09/28 12:00:00") - 1; + ngx_cached_http_time.len = sizeof("Mon, 28 Sep 1970 06:00:00 GMT") - 1; + ngx_cached_http_log_time.len = sizeof("28/Sep/1970:12:00:00 +0600") - 1; + ngx_cached_http_log_iso8601.len = sizeof("1970-09-28T12:00:00+06:00") - 1; + ngx_cached_syslog_time.len = sizeof("Sep 28 12:00:00") - 1; + + ngx_cached_time = &cached_time[0]; + + ngx_time_update(); +} + + +void +ngx_time_update(void) +{ + u_char *p0, *p1, *p2, *p3, *p4; + ngx_tm_t tm, gmt; + time_t sec; + ngx_uint_t msec; + ngx_time_t *tp; + struct timeval tv; + + if (!ngx_trylock(&ngx_time_lock)) { + return; + } + + ngx_gettimeofday(&tv); + + sec = tv.tv_sec; + msec = tv.tv_usec / 1000; + + ngx_current_msec = (ngx_msec_t) sec * 1000 + msec; + + tp = &cached_time[slot]; + + if (tp->sec == sec) { + tp->msec = msec; + ngx_unlock(&ngx_time_lock); + return; + } + + if (slot == NGX_TIME_SLOTS - 1) { + slot = 0; + } else { + slot++; + } + + tp = &cached_time[slot]; + + tp->sec = sec; + tp->msec = msec; + + ngx_gmtime(sec, &gmt); + + + p0 = &cached_http_time[slot][0]; + + (void) ngx_sprintf(p0, "%s, %02d %s %4d %02d:%02d:%02d GMT", + week[gmt.ngx_tm_wday], gmt.ngx_tm_mday, + months[gmt.ngx_tm_mon - 1], gmt.ngx_tm_year, + gmt.ngx_tm_hour, gmt.ngx_tm_min, gmt.ngx_tm_sec); + +#if (NGX_HAVE_GETTIMEZONE) + + tp->gmtoff = ngx_gettimezone(); + ngx_gmtime(sec + tp->gmtoff * 60, &tm); + +#elif (NGX_HAVE_GMTOFF) + + ngx_localtime(sec, &tm); + cached_gmtoff = (ngx_int_t) (tm.ngx_tm_gmtoff / 60); + tp->gmtoff = cached_gmtoff; + +#else + + ngx_localtime(sec, &tm); + cached_gmtoff = ngx_timezone(tm.ngx_tm_isdst); + tp->gmtoff = cached_gmtoff; + +#endif + + + p1 = &cached_err_log_time[slot][0]; + + (void) ngx_sprintf(p1, "%4d/%02d/%02d %02d:%02d:%02d", + tm.ngx_tm_year, tm.ngx_tm_mon, + tm.ngx_tm_mday, tm.ngx_tm_hour, + tm.ngx_tm_min, tm.ngx_tm_sec); + + + p2 = &cached_http_log_time[slot][0]; + + (void) ngx_sprintf(p2, "%02d/%s/%d:%02d:%02d:%02d %c%02i%02i", + tm.ngx_tm_mday, months[tm.ngx_tm_mon - 1], + tm.ngx_tm_year, tm.ngx_tm_hour, + tm.ngx_tm_min, tm.ngx_tm_sec, + tp->gmtoff < 0 ? '-' : '+', + ngx_abs(tp->gmtoff / 60), ngx_abs(tp->gmtoff % 60)); + + p3 = &cached_http_log_iso8601[slot][0]; + + (void) ngx_sprintf(p3, "%4d-%02d-%02dT%02d:%02d:%02d%c%02i:%02i", + tm.ngx_tm_year, tm.ngx_tm_mon, + tm.ngx_tm_mday, tm.ngx_tm_hour, + tm.ngx_tm_min, tm.ngx_tm_sec, + tp->gmtoff < 0 ? '-' : '+', + ngx_abs(tp->gmtoff / 60), ngx_abs(tp->gmtoff % 60)); + + p4 = &cached_syslog_time[slot][0]; + + (void) ngx_sprintf(p4, "%s %2d %02d:%02d:%02d", + months[tm.ngx_tm_mon - 1], tm.ngx_tm_mday, + tm.ngx_tm_hour, tm.ngx_tm_min, tm.ngx_tm_sec); + + ngx_memory_barrier(); + + ngx_cached_time = tp; + ngx_cached_http_time.data = p0; + ngx_cached_err_log_time.data = p1; + ngx_cached_http_log_time.data = p2; + ngx_cached_http_log_iso8601.data = p3; + ngx_cached_syslog_time.data = p4; + + ngx_unlock(&ngx_time_lock); +} + + +#if !(NGX_WIN32) + +void +ngx_time_sigsafe_update(void) +{ + u_char *p, *p2; + ngx_tm_t tm; + time_t sec; + ngx_time_t *tp; + struct timeval tv; + + if (!ngx_trylock(&ngx_time_lock)) { + return; + } + + ngx_gettimeofday(&tv); + + sec = tv.tv_sec; + + tp = &cached_time[slot]; + + if (tp->sec == sec) { + ngx_unlock(&ngx_time_lock); + return; + } + + if (slot == NGX_TIME_SLOTS - 1) { + slot = 0; + } else { + slot++; + } + + tp = &cached_time[slot]; + + tp->sec = 0; + + ngx_gmtime(sec + cached_gmtoff * 60, &tm); + + p = &cached_err_log_time[slot][0]; + + (void) ngx_sprintf(p, "%4d/%02d/%02d %02d:%02d:%02d", + tm.ngx_tm_year, tm.ngx_tm_mon, + tm.ngx_tm_mday, tm.ngx_tm_hour, + tm.ngx_tm_min, tm.ngx_tm_sec); + + p2 = &cached_syslog_time[slot][0]; + + (void) ngx_sprintf(p2, "%s %2d %02d:%02d:%02d", + months[tm.ngx_tm_mon - 1], tm.ngx_tm_mday, + tm.ngx_tm_hour, tm.ngx_tm_min, tm.ngx_tm_sec); + + ngx_memory_barrier(); + + ngx_cached_err_log_time.data = p; + ngx_cached_syslog_time.data = p2; + + ngx_unlock(&ngx_time_lock); +} + +#endif + + +u_char * +ngx_http_time(u_char *buf, time_t t) +{ + ngx_tm_t tm; + + ngx_gmtime(t, &tm); + + return ngx_sprintf(buf, "%s, %02d %s %4d %02d:%02d:%02d GMT", + week[tm.ngx_tm_wday], + tm.ngx_tm_mday, + months[tm.ngx_tm_mon - 1], + tm.ngx_tm_year, + tm.ngx_tm_hour, + tm.ngx_tm_min, + tm.ngx_tm_sec); +} + + +u_char * +ngx_http_cookie_time(u_char *buf, time_t t) +{ + ngx_tm_t tm; + + ngx_gmtime(t, &tm); + + /* + * Netscape 3.x does not understand 4-digit years at all and + * 2-digit years more than "37" + */ + + return ngx_sprintf(buf, + (tm.ngx_tm_year > 2037) ? + "%s, %02d-%s-%d %02d:%02d:%02d GMT": + "%s, %02d-%s-%02d %02d:%02d:%02d GMT", + week[tm.ngx_tm_wday], + tm.ngx_tm_mday, + months[tm.ngx_tm_mon - 1], + (tm.ngx_tm_year > 2037) ? tm.ngx_tm_year: + tm.ngx_tm_year % 100, + tm.ngx_tm_hour, + tm.ngx_tm_min, + tm.ngx_tm_sec); +} + + +void +ngx_gmtime(time_t t, ngx_tm_t *tp) +{ + ngx_int_t yday; + ngx_uint_t n, sec, min, hour, mday, mon, year, wday, days, leap; + + /* the calculation is valid for positive time_t only */ + + n = (ngx_uint_t) t; + + days = n / 86400; + + /* January 1, 1970 was Thursday */ + + wday = (4 + days) % 7; + + n %= 86400; + hour = n / 3600; + n %= 3600; + min = n / 60; + sec = n % 60; + + /* + * the algorithm based on Gauss' formula, + * see src/http/ngx_http_parse_time.c + */ + + /* days since March 1, 1 BC */ + days = days - (31 + 28) + 719527; + + /* + * The "days" should be adjusted to 1 only, however, some March 1st's go + * to previous year, so we adjust them to 2. This causes also shift of the + * last February days to next year, but we catch the case when "yday" + * becomes negative. + */ + + year = (days + 2) * 400 / (365 * 400 + 100 - 4 + 1); + + yday = days - (365 * year + year / 4 - year / 100 + year / 400); + + if (yday < 0) { + leap = (year % 4 == 0) && (year % 100 || (year % 400 == 0)); + yday = 365 + leap + yday; + year--; + } + + /* + * The empirical formula that maps "yday" to month. + * There are at least 10 variants, some of them are: + * mon = (yday + 31) * 15 / 459 + * mon = (yday + 31) * 17 / 520 + * mon = (yday + 31) * 20 / 612 + */ + + mon = (yday + 31) * 10 / 306; + + /* the Gauss' formula that evaluates days before the month */ + + mday = yday - (367 * mon / 12 - 30) + 1; + + if (yday >= 306) { + + year++; + mon -= 10; + + /* + * there is no "yday" in Win32 SYSTEMTIME + * + * yday -= 306; + */ + + } else { + + mon += 2; + + /* + * there is no "yday" in Win32 SYSTEMTIME + * + * yday += 31 + 28 + leap; + */ + } + + tp->ngx_tm_sec = (ngx_tm_sec_t) sec; + tp->ngx_tm_min = (ngx_tm_min_t) min; + tp->ngx_tm_hour = (ngx_tm_hour_t) hour; + tp->ngx_tm_mday = (ngx_tm_mday_t) mday; + tp->ngx_tm_mon = (ngx_tm_mon_t) mon; + tp->ngx_tm_year = (ngx_tm_year_t) year; + tp->ngx_tm_wday = (ngx_tm_wday_t) wday; +} + + +time_t +ngx_next_time(time_t when) +{ + time_t now, next; + struct tm tm; + + now = ngx_time(); + + ngx_libc_localtime(now, &tm); + + tm.tm_hour = (int) (when / 3600); + when %= 3600; + tm.tm_min = (int) (when / 60); + tm.tm_sec = (int) (when % 60); + + next = mktime(&tm); + + if (next == -1) { + return -1; + } + + if (next - now > 0) { + return next; + } + + tm.tm_mday++; + + /* mktime() should normalize a date (Jan 32, etc) */ + + next = mktime(&tm); + + if (next != -1) { + return next; + } + + return -1; +} diff --git a/app/nginx/src/core/ngx_times.h b/app/nginx/src/core/ngx_times.h new file mode 100644 index 0000000..94aedcd --- /dev/null +++ b/app/nginx/src/core/ngx_times.h @@ -0,0 +1,52 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_TIMES_H_INCLUDED_ +#define _NGX_TIMES_H_INCLUDED_ + + +#include +#include + + +typedef struct { + time_t sec; + ngx_uint_t msec; + ngx_int_t gmtoff; +} ngx_time_t; + + +void ngx_time_init(void); +void ngx_time_update(void); +void ngx_time_sigsafe_update(void); +u_char *ngx_http_time(u_char *buf, time_t t); +u_char *ngx_http_cookie_time(u_char *buf, time_t t); +void ngx_gmtime(time_t t, ngx_tm_t *tp); + +time_t ngx_next_time(time_t when); +#define ngx_next_time_n "mktime()" + + +extern volatile ngx_time_t *ngx_cached_time; + +#define ngx_time() ngx_cached_time->sec +#define ngx_timeofday() (ngx_time_t *) ngx_cached_time + +extern volatile ngx_str_t ngx_cached_err_log_time; +extern volatile ngx_str_t ngx_cached_http_time; +extern volatile ngx_str_t ngx_cached_http_log_time; +extern volatile ngx_str_t ngx_cached_http_log_iso8601; +extern volatile ngx_str_t ngx_cached_syslog_time; + +/* + * milliseconds elapsed since epoch and truncated to ngx_msec_t, + * used in event timers + */ +extern volatile ngx_msec_t ngx_current_msec; + + +#endif /* _NGX_TIMES_H_INCLUDED_ */ -- cgit 1.2.3-korg