changeset 626:a7a5fa2e395b NGINX_1_0_3

nginx 1.0.3 *) Feature: the "auth_basic_user_file" directive supports "$apr1", "{PLAIN}", and "{SSHA}" password encryption methods. Thanks to Maxim Dounin. *) Feature: the "geoip_org" directive and $geoip_org variable. Thanks to Alexander Uskov, Arnaud Granal, and Denis F. Latypoff. *) Feature: ngx_http_geo_module and ngx_http_geoip_module support IPv4 addresses mapped to IPv6 addresses. *) Bugfix: a segmentation fault occurred in a worker process during testing IPv4 address mapped to IPv6 address, if access or deny rules were defined only for IPv6; the bug had appeared in 0.8.22. *) Bugfix: a cached reponse may be broken if proxy/fastcgi/scgi/ uwsgi_cache_bypass and proxy/fastcgi/scgi/uwsgi_no_cache directive values were different; the bug had appeared in 0.8.46.
author Igor Sysoev <http://sysoev.ru>
date Wed, 25 May 2011 00:00:00 +0400
parents 30f948276abe
children a63a292c61af
files CHANGES CHANGES.ru auto/feature auto/lib/conf auto/lib/sha1/conf auto/modules auto/sources src/core/nginx.h src/core/ngx_crypt.c src/core/ngx_crypt.h src/http/modules/ngx_http_access_module.c src/http/modules/ngx_http_auth_basic_module.c src/http/modules/ngx_http_geo_module.c src/http/modules/ngx_http_geoip_module.c src/http/modules/ngx_http_split_clients_module.c src/http/modules/perl/nginx.pm src/http/ngx_http_file_cache.c src/http/ngx_http_upstream.c src/os/unix/ngx_user.c src/os/unix/ngx_user.h
diffstat 20 files changed, 525 insertions(+), 73 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGES
+++ b/CHANGES
@@ -1,4 +1,25 @@
 
+Changes with nginx 1.0.3                                         25 May 2011
+
+    *) Feature: the "auth_basic_user_file" directive supports "$apr1", 
+       "{PLAIN}", and "{SSHA}" password encryption methods.
+       Thanks to Maxim Dounin.
+
+    *) Feature: the "geoip_org" directive and $geoip_org variable.
+       Thanks to Alexander Uskov, Arnaud Granal, and Denis F. Latypoff.
+
+    *) Feature: ngx_http_geo_module and ngx_http_geoip_module support IPv4 
+       addresses mapped to IPv6 addresses.
+
+    *) Bugfix: a segmentation fault occurred in a worker process during 
+       testing IPv4 address mapped to IPv6 address, if access or deny rules 
+       were defined only for IPv6; the bug had appeared in 0.8.22.
+
+    *) Bugfix: a cached reponse may be broken if proxy/fastcgi/scgi/ 
+       uwsgi_cache_bypass and proxy/fastcgi/scgi/uwsgi_no_cache directive 
+       values were different; the bug had appeared in 0.8.46.
+
+
 Changes with nginx 1.0.2                                         10 May 2011
 
     *) Feature: now shared zones and caches use POSIX semaphores.
--- a/CHANGES.ru
+++ b/CHANGES.ru
@@ -1,4 +1,26 @@
 
+Изменения в nginx 1.0.3                                           25.05.2011
+
+    *) Добавление: директива auth_basic_user_file поддерживает шифрование 
+       пароля методами "$apr1", "{PLAIN}" и "{SSHA}".
+       Спасибо Максиму Дунину.
+
+    *) Добавление: директива geoip_org и переменная $geoip_org.
+       Спасибо Александру Ускову, Arnaud Granal и Денису Латыпову.
+
+    *) Добавление: модули ngx_http_geo_module и ngx_http_geoip_module 
+       поддерживают адреса IPv4, отображённые на IPv6 адреса.
+
+    *) Исправление: при проверке адреса IPv4, отображённого на адрес IPv6, 
+       в рабочем процессе происходил segmentation fault, если директивы 
+       access или deny были определены только для адресов IPv6; ошибка 
+       появилась в 0.8.22.
+
+    *) Исправление: закэшированный ответ мог быть испорчен, если значения 
+       директив proxy/fastcgi/scgi/uwsgi_cache_bypass и proxy/fastcgi/scgi/ 
+       uwsgi_no_cache были разными; ошибка появилась в 0.8.46.
+
+
 Изменения в nginx 1.0.2                                           10.05.2011
 
     *) Добавление: теперь разделяемые зоны и кэши используют семафоры POSIX.
--- a/auto/feature
+++ b/auto/feature
@@ -52,7 +52,7 @@ if [ -x $NGX_AUTOTEST ]; then
 
         yes)
             # /bin/sh is used to intercept "Killed" or "Abort trap" messages
-            if /bin/sh -c $NGX_AUTOTEST >/dev/null 2>&1; then
+            if /bin/sh -c $NGX_AUTOTEST >> $NGX_AUTOCONF_ERR 2>&1; then
                 echo " found"
                 ngx_found=yes
 
@@ -67,7 +67,7 @@ if [ -x $NGX_AUTOTEST ]; then
 
         value)
             # /bin/sh is used to intercept "Killed" or "Abort trap" messages
-            if /bin/sh -c $NGX_AUTOTEST >/dev/null 2>&1; then
+            if /bin/sh -c $NGX_AUTOTEST >> $NGX_AUTOCONF_ERR 2>&1; then
                 echo " found"
                 ngx_found=yes
 
@@ -85,7 +85,7 @@ END
 
         bug)
             # /bin/sh is used to intercept "Killed" or "Abort trap" messages
-            if /bin/sh -c $NGX_AUTOTEST >/dev/null 2>&1; then
+            if /bin/sh -c $NGX_AUTOTEST >> $NGX_AUTOCONF_ERR 2>&1; then
                 echo " not found"
 
             else
--- a/auto/lib/conf
+++ b/auto/lib/conf
@@ -43,6 +43,7 @@ if [ $USE_SHA1 = YES ]; then
 
     if [ $USE_OPENSSL = YES ]; then
         have=NGX_HAVE_OPENSSL_SHA1_H . auto/have
+        have=NGX_HAVE_SHA1 . auto/have
         SHA1=YES
         SHA1_LIB=OpenSSL
 
--- a/auto/lib/sha1/conf
+++ b/auto/lib/sha1/conf
@@ -4,6 +4,7 @@
 
 if [ $SHA1 != NONE ]; then
 
+    have=NGX_HAVE_SHA1 . auto/have
     CORE_INCS="$CORE_INCS $SHA1"
 
     case "$NGX_CC_NAME" in
@@ -41,7 +42,7 @@ else
         # FreeBSD
 
         ngx_feature="sha1 in system md library"
-        ngx_feature_name=
+        ngx_feature_name=NGX_HAVE_SHA1
         ngx_feature_run=no
         ngx_feature_incs="#include <sha.h>"
         ngx_feature_path=
--- a/auto/modules
+++ b/auto/modules
@@ -197,6 +197,8 @@ if [ $HTTP_RANDOM_INDEX = YES ]; then
 fi
 
 if [ $HTTP_AUTH_BASIC = YES ]; then
+    USE_MD5=YES
+    USE_SHA1=YES
     have=NGX_CRYPT . auto/have
     HTTP_MODULES="$HTTP_MODULES $HTTP_AUTH_BASIC_MODULE"
     HTTP_SRCS="$HTTP_SRCS $HTTP_AUTH_BASIC_SRCS"
--- a/auto/sources
+++ b/auto/sources
@@ -34,7 +34,8 @@ CORE_DEPS="src/core/nginx.h \
            src/core/ngx_cycle.h \
            src/core/ngx_conf_file.h \
            src/core/ngx_resolver.h \
-           src/core/ngx_open_file_cache.h"
+           src/core/ngx_open_file_cache.h \
+           src/core/ngx_crypt.h"
 
 
 CORE_SRCS="src/core/nginx.c \
@@ -64,7 +65,8 @@ CORE_SRCS="src/core/nginx.c \
            src/core/ngx_cpuinfo.c \
            src/core/ngx_conf_file.c \
            src/core/ngx_resolver.c \
-           src/core/ngx_open_file_cache.c"
+           src/core/ngx_open_file_cache.c \
+           src/core/ngx_crypt.c"
 
 
 REGEX_DEPS=src/core/ngx_regex.h
--- a/src/core/nginx.h
+++ b/src/core/nginx.h
@@ -8,8 +8,8 @@
 #define _NGINX_H_INCLUDED_
 
 
-#define nginx_version      1000002
-#define NGINX_VERSION      "1.0.2"
+#define nginx_version      1000003
+#define NGINX_VERSION      "1.0.3"
 #define NGINX_VER          "nginx/" NGINX_VERSION
 
 #define NGINX_VAR          "NGINX"
new file mode 100644
--- /dev/null
+++ b/src/core/ngx_crypt.c
@@ -0,0 +1,234 @@
+
+/*
+ * Copyright (C) Maxim Dounin
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_md5.h>
+#if (NGX_HAVE_SHA1)
+#include <ngx_sha1.h>
+#endif
+
+
+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);
+
+#if (NGX_HAVE_SHA1)
+
+static ngx_int_t ngx_crypt_ssha(ngx_pool_t *pool, u_char *key, u_char *salt,
+    u_char **encrypted);
+
+#endif
+
+
+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);
+
+#if (NGX_HAVE_SHA1)
+    } else if (ngx_strncmp(salt, "{SSHA}", sizeof("{SSHA}") - 1) == 0) {
+        return ngx_crypt_ssha(pool, key, salt, encrypted);
+#endif
+    }
+
+    /* 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 Paul-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, "$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 + 16 + 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;
+}
+
+
+#if (NGX_HAVE_SHA1)
+
+static ngx_int_t
+ngx_crypt_ssha(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;
+
+    /* "{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);
+
+    decoded.data = ngx_pnalloc(pool, ngx_base64_decoded_length(encoded.len));
+    if (decoded.data == NULL) {
+        return NGX_ERROR;
+    }
+
+    ngx_decode_base64(&decoded, &encoded);
+
+    /* 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;
+}
+
+#endif /* NGX_HAVE_SHA1 */
new file mode 100644
--- /dev/null
+++ b/src/core/ngx_crypt.h
@@ -0,0 +1,19 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#ifndef _NGX_CRYPT_H_INCLUDED_
+#define _NGX_CRYPT_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+ngx_int_t ngx_crypt(ngx_pool_t *pool, u_char *key, u_char *salt,
+    u_char **encrypted);
+
+
+#endif /* _NGX_CRYPT_H_INCLUDED_ */
--- a/src/http/modules/ngx_http_access_module.c
+++ b/src/http/modules/ngx_http_access_module.c
@@ -108,20 +108,30 @@ ngx_http_access_handler(ngx_http_request
 {
     struct sockaddr_in          *sin;
     ngx_http_access_loc_conf_t  *alcf;
+#if (NGX_HAVE_INET6)
+    u_char                      *p;
+    in_addr_t                    addr;
+    struct sockaddr_in6         *sin6;
+#endif
 
     alcf = ngx_http_get_module_loc_conf(r, ngx_http_access_module);
 
+    switch (r->connection->sockaddr->sa_family) {
+
+    case AF_INET:
+        if (alcf->rules) {
+            sin = (struct sockaddr_in *) r->connection->sockaddr;
+            return ngx_http_access_inet(r, alcf, sin->sin_addr.s_addr);
+        }
+        break;
+
 #if (NGX_HAVE_INET6)
 
-    if (alcf->rules6 && r->connection->sockaddr->sa_family == AF_INET6) {
-        u_char               *p;
-        in_addr_t             addr;
-        struct sockaddr_in6  *sin6;
-
+    case AF_INET6:
         sin6 = (struct sockaddr_in6 *) r->connection->sockaddr;
         p = sin6->sin6_addr.s6_addr;
 
-        if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
+        if (alcf->rules && IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
             addr = p[12] << 24;
             addr += p[13] << 16;
             addr += p[14] << 8;
@@ -129,14 +139,11 @@ ngx_http_access_handler(ngx_http_request
             return ngx_http_access_inet(r, alcf, htonl(addr));
         }
 
-        return ngx_http_access_inet6(r, alcf, p);
-    }
+        if (alcf->rules6) {
+            return ngx_http_access_inet6(r, alcf, p);
+        }
 
 #endif
-
-    if (alcf->rules && r->connection->sockaddr->sa_family == AF_INET) {
-        sin = (struct sockaddr_in *) r->connection->sockaddr;
-        return ngx_http_access_inet(r, alcf, sin->sin_addr.s_addr);
     }
 
     return NGX_DECLINED;
--- a/src/http/modules/ngx_http_auth_basic_module.c
+++ b/src/http/modules/ngx_http_auth_basic_module.c
@@ -7,6 +7,7 @@
 #include <ngx_config.h>
 #include <ngx_core.h>
 #include <ngx_http.h>
+#include <ngx_crypt.h>
 
 
 #define NGX_HTTP_AUTH_BUF_SIZE  2048
--- a/src/http/modules/ngx_http_geo_module.c
+++ b/src/http/modules/ngx_http_geo_module.c
@@ -257,17 +257,41 @@ ngx_http_geo_real_addr(ngx_http_request_
 {
     struct sockaddr_in         *sin;
     ngx_http_variable_value_t  *v;
+#if (NGX_HAVE_INET6)
+    u_char                     *p;
+    in_addr_t                   addr;
+    struct sockaddr_in6        *sin6;
+#endif
 
     if (ctx->index == -1) {
         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                        "http geo started: %V", &r->connection->addr_text);
 
-        if (r->connection->sockaddr->sa_family != AF_INET) {
-            return 0;
+        switch (r->connection->sockaddr->sa_family) {
+
+        case AF_INET:
+            sin = (struct sockaddr_in *) r->connection->sockaddr;
+            return ntohl(sin->sin_addr.s_addr);
+
+#if (NGX_HAVE_INET6)
+
+        case AF_INET6:
+            sin6 = (struct sockaddr_in6 *) r->connection->sockaddr;
+
+            if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
+                p = sin6->sin6_addr.s6_addr;
+                addr = p[12] << 24;
+                addr += p[13] << 16;
+                addr += p[14] << 8;
+                addr += p[15];
+
+                return addr;
+            }
+
+#endif
         }
 
-        sin = (struct sockaddr_in *) r->connection->sockaddr;
-        return ntohl(sin->sin_addr.s_addr);
+        return INADDR_NONE;
     }
 
     v = ngx_http_get_flushed_variable(r, ctx->index);
--- a/src/http/modules/ngx_http_geoip_module.c
+++ b/src/http/modules/ngx_http_geoip_module.c
@@ -14,6 +14,7 @@
 
 typedef struct {
     GeoIP      *country;
+    GeoIP      *org;
     GeoIP      *city;
 } ngx_http_geoip_conf_t;
 
@@ -28,6 +29,8 @@ typedef const char *(*ngx_http_geoip_var
 
 static ngx_int_t ngx_http_geoip_country_variable(ngx_http_request_t *r,
     ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_geoip_org_variable(ngx_http_request_t *r,
+    ngx_http_variable_value_t *v, uintptr_t data);
 static ngx_int_t ngx_http_geoip_city_variable(ngx_http_request_t *r,
     ngx_http_variable_value_t *v, uintptr_t data);
 static ngx_int_t ngx_http_geoip_region_name_variable(ngx_http_request_t *r,
@@ -42,6 +45,8 @@ static ngx_int_t ngx_http_geoip_add_vari
 static void *ngx_http_geoip_create_conf(ngx_conf_t *cf);
 static char *ngx_http_geoip_country(ngx_conf_t *cf, ngx_command_t *cmd,
     void *conf);
+static char *ngx_http_geoip_org(ngx_conf_t *cf, ngx_command_t *cmd,
+    void *conf);
 static char *ngx_http_geoip_city(ngx_conf_t *cf, ngx_command_t *cmd,
     void *conf);
 static void ngx_http_geoip_cleanup(void *data);
@@ -56,6 +61,13 @@ static ngx_command_t  ngx_http_geoip_com
       0,
       NULL },
 
+    { ngx_string("geoip_org"),
+      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE12,
+      ngx_http_geoip_org,
+      NGX_HTTP_MAIN_CONF_OFFSET,
+      0,
+      NULL },
+
     { ngx_string("geoip_city"),
       NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE12,
       ngx_http_geoip_city,
@@ -112,6 +124,10 @@ static ngx_http_variable_t  ngx_http_geo
       ngx_http_geoip_country_variable,
       (uintptr_t) GeoIP_country_name_by_ipnum, 0, 0 },
 
+    { ngx_string("geoip_org"), NULL,
+      ngx_http_geoip_org_variable,
+      (uintptr_t) GeoIP_name_by_ipnum, 0, 0 },
+
     { ngx_string("geoip_city_continent_code"), NULL,
       ngx_http_geoip_city_variable,
       offsetof(GeoIPRecord, continent_code), 0, 0 },
@@ -164,6 +180,44 @@ static ngx_http_variable_t  ngx_http_geo
 };
 
 
+static u_long
+ngx_http_geoip_addr(ngx_http_request_t *r)
+{
+    struct sockaddr_in   *sin;
+#if (NGX_HAVE_INET6)
+    u_char               *p;
+    u_long                addr;
+    struct sockaddr_in6  *sin6;
+#endif
+
+    switch (r->connection->sockaddr->sa_family) {
+
+    case AF_INET:
+        sin = (struct sockaddr_in *) r->connection->sockaddr;
+        return ntohl(sin->sin_addr.s_addr);
+
+#if (NGX_HAVE_INET6)
+
+    case AF_INET6:
+        sin6 = (struct sockaddr_in6 *) r->connection->sockaddr;
+
+        if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
+            p = sin6->sin6_addr.s6_addr;
+            addr = p[12] << 24;
+            addr += p[13] << 16;
+            addr += p[14] << 8;
+            addr += p[15];
+
+            return addr;
+        }
+
+#endif
+    }
+
+    return INADDR_NONE;
+}
+
+
 static ngx_int_t
 ngx_http_geoip_country_variable(ngx_http_request_t *r,
     ngx_http_variable_value_t *v, uintptr_t data)
@@ -171,9 +225,7 @@ ngx_http_geoip_country_variable(ngx_http
     ngx_http_geoip_variable_handler_pt  handler =
         (ngx_http_geoip_variable_handler_pt) data;
 
-    u_long                  addr;
     const char             *val;
-    struct sockaddr_in     *sin;
     ngx_http_geoip_conf_t  *gcf;
 
     gcf = ngx_http_get_module_main_conf(r, ngx_http_geoip_module);
@@ -182,14 +234,45 @@ ngx_http_geoip_country_variable(ngx_http
         goto not_found;
     }
 
-    if (r->connection->sockaddr->sa_family != AF_INET) {
+    val = handler(gcf->country, ngx_http_geoip_addr(r));
+
+    if (val == NULL) {
         goto not_found;
     }
 
-    sin = (struct sockaddr_in *) r->connection->sockaddr;
-    addr = ntohl(sin->sin_addr.s_addr);
+    v->len = ngx_strlen(val);
+    v->valid = 1;
+    v->no_cacheable = 0;
+    v->not_found = 0;
+    v->data = (u_char *) val;
+
+    return NGX_OK;
+
+not_found:
+
+    v->not_found = 1;
+
+    return NGX_OK;
+}
+
 
-    val = handler(gcf->country, addr);
+static ngx_int_t
+ngx_http_geoip_org_variable(ngx_http_request_t *r,
+    ngx_http_variable_value_t *v, uintptr_t data)
+{
+    ngx_http_geoip_variable_handler_pt  handler =
+        (ngx_http_geoip_variable_handler_pt) data;
+
+    const char             *val;
+    ngx_http_geoip_conf_t  *gcf;
+
+    gcf = ngx_http_get_module_main_conf(r, ngx_http_geoip_module);
+
+    if (gcf->org == NULL) {
+        goto not_found;
+    }
+
+    val = handler(gcf->org, ngx_http_geoip_addr(r));
 
     if (val == NULL) {
         goto not_found;
@@ -364,18 +447,12 @@ ngx_http_geoip_city_int_variable(ngx_htt
 static GeoIPRecord *
 ngx_http_geoip_get_city_record(ngx_http_request_t *r)
 {
-    u_long                  addr;
-    struct sockaddr_in     *sin;
     ngx_http_geoip_conf_t  *gcf;
 
     gcf = ngx_http_get_module_main_conf(r, ngx_http_geoip_module);
 
-    if (gcf->city && r->connection->sockaddr->sa_family == AF_INET) {
-
-        sin = (struct sockaddr_in *) r->connection->sockaddr;
-        addr = ntohl(sin->sin_addr.s_addr);
-
-        return GeoIP_record_by_ipnum(gcf->city, addr);
+    if (gcf->city) {
+        return GeoIP_record_by_ipnum(gcf->city, ngx_http_geoip_addr(r));
     }
 
     return NULL;
@@ -441,7 +518,7 @@ ngx_http_geoip_country(ngx_conf_t *cf, n
 
     if (gcf->country == NULL) {
         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
-                           "GeoIO_open(\"%V\") failed", &value[1]);
+                           "GeoIP_open(\"%V\") failed", &value[1]);
 
         return NGX_CONF_ERROR;
     }
@@ -475,6 +552,57 @@ ngx_http_geoip_country(ngx_conf_t *cf, n
 
 
 static char *
+ngx_http_geoip_org(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    ngx_http_geoip_conf_t  *gcf = conf;
+
+    ngx_str_t  *value;
+
+    if (gcf->org) {
+        return "is duplicate";
+    }
+
+    value = cf->args->elts;
+
+    gcf->org = GeoIP_open((char *) value[1].data, GEOIP_MEMORY_CACHE);
+
+    if (gcf->org == NULL) {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "GeoIP_open(\"%V\") failed", &value[1]);
+
+        return NGX_CONF_ERROR;
+    }
+
+    if (cf->args->nelts == 3) {
+        if (ngx_strcmp(value[2].data, "utf8") == 0) {
+            GeoIP_set_charset (gcf->org, GEOIP_CHARSET_UTF8);
+
+        } else {
+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                               "invalid parameter \"%V\"", &value[2]);
+            return NGX_CONF_ERROR;
+        }
+    }
+
+    switch (gcf->org->databaseType) {
+
+    case GEOIP_ISP_EDITION:
+    case GEOIP_ORG_EDITION:
+    case GEOIP_DOMAIN_EDITION:
+    case GEOIP_ASNUM_EDITION:
+
+        return NGX_CONF_OK;
+
+    default:
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "invalid GeoIP database \"%V\" type:%d",
+                           &value[1], gcf->org->databaseType);
+        return NGX_CONF_ERROR;
+    }
+}
+
+
+static char *
 ngx_http_geoip_city(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 {
     ngx_http_geoip_conf_t  *gcf = conf;
@@ -491,7 +619,7 @@ ngx_http_geoip_city(ngx_conf_t *cf, ngx_
 
     if (gcf->city == NULL) {
         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
-                           "GeoIO_open(\"%V\") failed", &value[1]);
+                           "GeoIP_open(\"%V\") failed", &value[1]);
 
         return NGX_CONF_ERROR;
     }
@@ -532,6 +660,10 @@ ngx_http_geoip_cleanup(void *data)
         GeoIP_delete(gcf->country);
     }
 
+    if (gcf->org) {
+        GeoIP_delete(gcf->org);
+    }
+
     if (gcf->city) {
         GeoIP_delete(gcf->city);
     }
--- a/src/http/modules/ngx_http_split_clients_module.c
+++ b/src/http/modules/ngx_http_split_clients_module.c
@@ -94,7 +94,7 @@ ngx_http_split_clients_variable(ngx_http
     for (i = 0; i < ctx->parts.nelts; i++) {
 
         ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                       "%D %D", hash, part[i].percent);
+                       "http split: %uD %uD", hash, part[i].percent);
 
         if (hash < part[i].percent) {
             *v = part[i].value;
--- a/src/http/modules/perl/nginx.pm
+++ b/src/http/modules/perl/nginx.pm
@@ -48,7 +48,7 @@ our @EXPORT = qw(
     HTTP_INSUFFICIENT_STORAGE
 );
 
-our $VERSION = '1.0.2';
+our $VERSION = '1.0.3';
 
 require XSLoader;
 XSLoader::load('nginx', $VERSION);
--- a/src/http/ngx_http_file_cache.c
+++ b/src/http/ngx_http_file_cache.c
@@ -174,8 +174,6 @@ ngx_http_file_cache_create(ngx_http_requ
     ngx_pool_cleanup_t     *cln;
     ngx_http_file_cache_t  *cache;
 
-    ngx_http_file_cache_create_key(r);
-
     c = r->cache;
     cache = c->file_cache;
 
@@ -906,7 +904,7 @@ ngx_http_file_cache_free(ngx_http_cache_
     ngx_http_file_cache_t       *cache;
     ngx_http_file_cache_node_t  *fcn;
 
-    if (c->updated) {
+    if (c->updated || c->node == NULL) {
         return;
     }
 
--- a/src/http/ngx_http_upstream.c
+++ b/src/http/ngx_http_upstream.c
@@ -641,19 +641,6 @@ ngx_http_upstream_cache(ngx_http_request
 
     if (c == NULL) {
 
-        switch (ngx_http_test_predicates(r, u->conf->cache_bypass)) {
-
-        case NGX_ERROR:
-            return NGX_ERROR;
-
-        case NGX_DECLINED:
-            u->cache_status = NGX_HTTP_CACHE_BYPASS;
-            return NGX_DECLINED;
-
-        default: /* NGX_OK */
-            break;
-        }
-
         if (!(r->method & u->conf->cache_methods)) {
             return NGX_DECLINED;
         }
@@ -674,6 +661,19 @@ ngx_http_upstream_cache(ngx_http_request
 
         ngx_http_file_cache_create_key(r);
 
+        switch (ngx_http_test_predicates(r, u->conf->cache_bypass)) {
+
+        case NGX_ERROR:
+            return NGX_ERROR;
+
+        case NGX_DECLINED:
+            u->cache_status = NGX_HTTP_CACHE_BYPASS;
+            return NGX_DECLINED;
+
+        default: /* NGX_OK */
+            break;
+        }
+
         u->cacheable = 1;
 
         c = r->cache;
@@ -2135,18 +2135,6 @@ ngx_http_upstream_send_response(ngx_http
 
         if (u->cache_status == NGX_HTTP_CACHE_BYPASS) {
 
-            if (ngx_http_file_cache_new(r) != NGX_OK) {
-                ngx_http_upstream_finalize_request(r, u, 0);
-                return;
-            }
-
-            if (u->create_key(r) != NGX_OK) {
-                ngx_http_upstream_finalize_request(r, u, 0);
-                return;
-            }
-
-            /* TODO: add keys */
-
             r->cache->min_uses = u->conf->cache_min_uses;
             r->cache->body_start = u->conf->buffer_size;
             r->cache->file_cache = u->conf->cache->data;
--- a/src/os/unix/ngx_user.c
+++ b/src/os/unix/ngx_user.c
@@ -23,7 +23,7 @@
 #if (NGX_HAVE_GNU_CRYPT_R)
 
 ngx_int_t
-ngx_crypt(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted)
+ngx_libc_crypt(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted)
 {
     char               *value;
     size_t              len;
@@ -58,7 +58,7 @@ ngx_crypt(ngx_pool_t *pool, u_char *key,
 #else
 
 ngx_int_t
-ngx_crypt(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted)
+ngx_libc_crypt(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted)
 {
     char       *value;
     size_t      len;
--- a/src/os/unix/ngx_user.h
+++ b/src/os/unix/ngx_user.h
@@ -16,7 +16,7 @@ typedef uid_t  ngx_uid_t;
 typedef gid_t  ngx_gid_t;
 
 
-ngx_int_t ngx_crypt(ngx_pool_t *pool, u_char *key, u_char *salt,
+ngx_int_t ngx_libc_crypt(ngx_pool_t *pool, u_char *key, u_char *salt,
     u_char **encrypted);