changeset 448:76a79816b771 NGINX_0_7_36

nginx 0.7.36 *) Feature: a preliminary IPv6 support; the "listen" directive of the HTTP module supports IPv6. *) Bugfix: the $ancient_browser variable did not work for browsers preset by a "modern_browser" directives.
author Igor Sysoev <http://sysoev.ru>
date Sat, 21 Feb 2009 00:00:00 +0300
parents 40964c811e59
children 32d6e4e48a3a
files CHANGES CHANGES.ru auto/options auto/unix src/core/nginx.h src/core/ngx_connection.c src/core/ngx_connection.h src/core/ngx_cycle.c src/core/ngx_inet.c src/core/ngx_inet.h src/event/ngx_event_accept.c src/http/modules/ngx_http_access_module.c src/http/modules/ngx_http_browser_module.c src/http/modules/ngx_http_empty_gif_module.c src/http/modules/ngx_http_geo_module.c src/http/modules/ngx_http_realip_module.c src/http/modules/ngx_http_upstream_ip_hash_module.c src/http/modules/perl/nginx.pm src/http/ngx_http.c src/http/ngx_http_core_module.c src/http/ngx_http_core_module.h src/http/ngx_http_header_filter_module.c src/http/ngx_http_request.c src/http/ngx_http_variables.c src/mail/ngx_mail_core_module.c
diffstat 25 files changed, 1285 insertions(+), 601 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGES
+++ b/CHANGES
@@ -1,4 +1,13 @@
 
+Changes with nginx 0.7.36                                        21 Feb 2009
+
+    *) Feature: a preliminary IPv6 support; the "listen" directive of the 
+       HTTP module supports IPv6.
+
+    *) Bugfix: the $ancient_browser variable did not work for browsers 
+       preset by a "modern_browser" directives.
+
+
 Changes with nginx 0.7.35                                        16 Feb 2009
 
     *) Bugfix: a "ssl_engine" directive did not use a SSL-accelerator for 
--- a/CHANGES.ru
+++ b/CHANGES.ru
@@ -1,4 +1,13 @@
 
+Изменения в nginx 0.7.36                                          21.02.2009
+
+    *) Добавление: предварительная поддержка IPv6; директива listen модуля 
+       HTTP поддерживает IPv6.
+
+    *) Исправление: переменная $ancient_browser не работала для браузеров, 
+       заданных директивами modern_browser.
+
+
 Изменения в nginx 0.7.35                                          16.02.2009
 
     *) Исправление: директива ssl_engine не использовала SSL-акселератор 
--- a/auto/options
+++ b/auto/options
@@ -43,6 +43,8 @@ EVENT_AIO=NO
 
 USE_THREADS=NO
 
+NGX_IPV6=NO
+
 HTTP=YES
 
 NGX_HTTP_LOG_PATH=
@@ -160,6 +162,8 @@ do
         #--with-threads=*)                USE_THREADS="$value"       ;;
         #--with-threads)                  USE_THREADS="pthreads"     ;;
 
+        --with-ipv6)                     NGX_IPV6=YES               ;;
+
         --without-http)                  HTTP=NO                    ;;
         --http-log-path=*)               NGX_HTTP_LOG_PATH="$value" ;;
         --http-client-body-temp-path=*)  NGX_HTTP_CLIENT_TEMP_PATH="$value" ;;
@@ -285,6 +289,8 @@ cat << END
   --with-poll_module                 enable poll module
   --without-poll_module              disable poll module
 
+  --with-ipv6                        enable ipv6 support
+
   --with-http_ssl_module             enable ngx_http_ssl_module
   --with-http_realip_module          enable ngx_http_realip_module
   --with-http_addition_module        enable ngx_http_addition_module
--- a/auto/unix
+++ b/auto/unix
@@ -64,6 +64,21 @@ ngx_param=NGX_TIME_T_LEN; ngx_value=$ngx
 # syscalls, libc calls and some features
 
 
+if [ $NGX_IPV6 = YES ]; then
+    ngx_feature="AF_INET6"
+    ngx_feature_name="NGX_HAVE_INET6"
+    ngx_feature_run=no
+    ngx_feature_incs="#include <sys/socket.h>
+                      #include <netinet/in.h>
+                      #include <arpa/inet.h>"
+    ngx_feature_path=
+    ngx_feature_libs=
+    ngx_feature_test="struct sockaddr_in6  sin6;
+                      sin6.sin6_family = AF_INET6;"
+    . auto/feature
+fi
+
+
 ngx_feature="setproctitle()"
 ngx_feature_name="NGX_HAVE_SETPROCTITLE"
 ngx_feature_run=no
--- a/src/core/nginx.h
+++ b/src/core/nginx.h
@@ -8,7 +8,7 @@
 #define _NGINX_H_INCLUDED_
 
 
-#define NGINX_VERSION      "0.7.35"
+#define NGINX_VERSION      "0.7.36"
 #define NGINX_VER          "nginx/" NGINX_VERSION
 
 #define NGINX_VAR          "NGINX"
--- a/src/core/ngx_connection.c
+++ b/src/core/ngx_connection.c
@@ -52,7 +52,6 @@ ngx_listening_inet_stream_socket(ngx_con
     ls->type = SOCK_STREAM;
     ls->sockaddr = (struct sockaddr *) sin;
     ls->socklen = sizeof(struct sockaddr_in);
-    ls->addr = offsetof(struct sockaddr_in, sin_addr);
     ls->addr_text_max_len = NGX_INET_ADDRSTRLEN;
 
     return ls;
@@ -65,7 +64,6 @@ ngx_set_inherited_sockets(ngx_cycle_t *c
     size_t                     len;
     ngx_uint_t                 i;
     ngx_listening_t           *ls;
-    struct sockaddr_in        *sin;
     socklen_t                  olen;
 #if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER)
     ngx_err_t                  err;
@@ -94,33 +92,39 @@ ngx_set_inherited_sockets(ngx_cycle_t *c
             continue;
         }
 
-        sin = (struct sockaddr_in *) ls[i].sockaddr;
+        switch (ls[i].sockaddr->sa_family) {
 
-        if (sin->sin_family != AF_INET) {
+#if (NGX_HAVE_INET6)
+        case AF_INET6:
+             ls[i].addr_text_max_len = NGX_INET6_ADDRSTRLEN;
+             break;
+#endif
+
+        case AF_INET:
+             ls[i].addr_text_max_len = NGX_INET_ADDRSTRLEN;
+             break;
+
+        default:
             ngx_log_error(NGX_LOG_CRIT, cycle->log, ngx_socket_errno,
                           "the inherited socket #%d has "
-                          "unsupported family", ls[i].fd);
+                          "an unsupported protocol family", ls[i].fd);
             ls[i].ignore = 1;
             continue;
         }
 
-        ls[i].addr_text_max_len = NGX_INET_ADDRSTRLEN;
+        len = ls[i].addr_text_max_len + sizeof(":65535") - 1;
 
-        ls[i].addr_text.data = ngx_pnalloc(cycle->pool,
-                                   NGX_INET_ADDRSTRLEN + sizeof(":65535") - 1);
+        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].addr_text.data,
-                            NGX_INET_ADDRSTRLEN);
+        len = ngx_sock_ntop(ls[i].sockaddr, ls[i].addr_text.data, len, 1);
         if (len == 0) {
             return NGX_ERROR;
         }
 
-        ls[i].addr_text.len = ngx_sprintf(ls[i].addr_text.data + len, ":%d",
-                                          ntohs(sin->sin_port))
-                              - ls[i].addr_text.data;
+        ls[i].addr_text.len = len;
 
         ls[i].backlog = NGX_LISTEN_BACKLOG;
 
--- a/src/core/ngx_connection.h
+++ b/src/core/ngx_connection.h
@@ -19,7 +19,6 @@ struct ngx_listening_s {
 
     struct sockaddr    *sockaddr;
     socklen_t           socklen;    /* size of sockaddr */
-    size_t              addr;       /* offset to address in sockaddr */
     size_t              addr_text_max_len;
     ngx_str_t           addr_text;
 
@@ -123,10 +122,8 @@ struct ngx_connection_s {
     ngx_ssl_connection_t  *ssl;
 #endif
 
-#if (NGX_HAVE_IOCP)
     struct sockaddr    *local_sockaddr;
     socklen_t           local_socklen;
-#endif
 
     ngx_buf_t          *buffer;
 
--- a/src/core/ngx_cycle.c
+++ b/src/core/ngx_cycle.c
@@ -876,23 +876,47 @@ ngx_destroy_cycle_pools(ngx_conf_t *conf
 static ngx_int_t
 ngx_cmp_sockaddr(struct sockaddr *sa1, struct sockaddr *sa2)
 {
-    struct sockaddr_in  *sin1, *sin2;
+    struct sockaddr_in   *sin1, *sin2;
+#if (NGX_HAVE_INET6)
+    struct sockaddr_in6  *sin61, *sin62;
+#endif
 
-    /* AF_INET only */
-
-    if (sa1->sa_family != AF_INET || sa2->sa_family != AF_INET) {
+    if (sa1->sa_family != sa2->sa_family) {
         return NGX_DECLINED;
     }
 
-    sin1 = (struct sockaddr_in *) sa1;
-    sin2 = (struct sockaddr_in *) sa2;
+    switch (sa1->sa_family) {
+
+#if (NGX_HAVE_INET6)
+    case AF_INET6:
+        sin61 = (struct sockaddr_in6 *) sa1;
+        sin62 = (struct sockaddr_in6 *) sa2;
+
+        if (sin61->sin6_port != sin61->sin6_port) {
+            return NGX_DECLINED;
+        }
+
+        if (ngx_memcmp(&sin61->sin6_addr, &sin62->sin6_addr, 16) != 0) {
+            return NGX_DECLINED;
+        }
 
-    if (sin1->sin_addr.s_addr != sin2->sin_addr.s_addr) {
-        return NGX_DECLINED;
-    }
+        break;
+#endif
+
+    default: /* AF_INET */
+
+        sin1 = (struct sockaddr_in *) sa1;
+        sin2 = (struct sockaddr_in *) sa2;
 
-    if (sin1->sin_port != sin2->sin_port) {
-        return NGX_DECLINED;
+        if (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;
--- a/src/core/ngx_inet.c
+++ b/src/core/ngx_inet.c
@@ -8,11 +8,13 @@
 #include <ngx_core.h>
 
 
+#if (NGX_HAVE_INET6)
+static size_t ngx_inet6_ntop(u_char *p, u_char *text, size_t len);
+#endif
 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);
 
-/* AF_INET only */
 
 in_addr_t
 ngx_inet_addr(u_char *text, size_t len)
@@ -57,25 +59,58 @@ ngx_inet_addr(u_char *text, size_t len)
 }
 
 
-/* AF_INET only */
-
 size_t
-ngx_sock_ntop(struct sockaddr *sa, u_char *text, size_t len)
+ngx_sock_ntop(struct sockaddr *sa, u_char *text, size_t len, ngx_uint_t port)
 {
-    u_char              *p;
-    struct sockaddr_in  *sin;
+    u_char               *p;
+    struct sockaddr_in   *sin;
+#if (NGX_HAVE_INET6)
+    size_t                n;
+    struct sockaddr_in6  *sin6;
+#endif
 
-    if (sa->sa_family == AF_INET) {
+    switch (sa->sa_family) {
+
+    case AF_INET:
 
         sin = (struct sockaddr_in *) sa;
         p = (u_char *) &sin->sin_addr;
 
-        return ngx_snprintf(text, len, "%ud.%ud.%ud.%ud",
-                            p[0], p[1], p[2], p[3])
-               - text;
+        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((u_char *) &sin6->sin6_addr, &text[n], len);
+
+        if (port) {
+            n = ngx_sprintf(&text[1 + n], "]:%d",
+                            ntohs(sin6->sin6_port)) - text;
+        }
+
+        return n;
+#endif
+
+    default:
+        return 0;
     }
-
-    return 0;
 }
 
 
@@ -84,18 +119,109 @@ ngx_inet_ntop(int family, void *addr, u_
 {
     u_char  *p;
 
-    if (family == AF_INET) {
+    switch (family) {
 
-        p = (u_char *) addr;
+    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)
+
+static 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;
     }
 
-    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, "%uxi", 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
+
 
 /* AF_INET only */
 
@@ -171,6 +297,10 @@ ngx_parse_url(ngx_pool_t *pool, ngx_url_
         return NGX_ERROR;
     }
 
+    if (p[0] == '[') {
+        return ngx_parse_inet6_url(pool, u);
+    }
+
     return ngx_parse_inet_url(pool, u);
 }
 
@@ -209,13 +339,17 @@ ngx_parse_unix_domain_url(ngx_pool_t *po
 
     u->host.len = len++;
     u->host.data = path;
-    u->family = AF_UNIX;
 
     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_peer_addr_t));
     if (u->addrs == NULL) {
         return NGX_ERROR;
@@ -226,6 +360,7 @@ ngx_parse_unix_domain_url(ngx_pool_t *po
         return NGX_ERROR;
     }
 
+    u->family = AF_UNIX;
     u->naddrs = 1;
 
     saun->sun_family = AF_UNIX;
@@ -251,10 +386,15 @@ ngx_parse_unix_domain_url(ngx_pool_t *po
 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 hostent  *h;
+    u_char              *p, *host, *port, *last, *uri, *args;
+    size_t               len;
+    ngx_int_t            n;
+    struct hostent      *h;
+    struct sockaddr_in  *sin;
+
+    u->socklen = sizeof(struct sockaddr_in);
+    sin = (struct sockaddr_in *) &u->sockaddr;
+    sin->sin_family = AF_INET;
 
     u->family = AF_INET;
 
@@ -311,6 +451,7 @@ ngx_parse_inet_url(ngx_pool_t *pool, ngx
         }
 
         u->port = (in_port_t) n;
+        sin->sin_port = htons((in_port_t) n);
 
         u->port_text.len = len;
         u->port_text.data = port;
@@ -334,10 +475,13 @@ ngx_parse_inet_url(ngx_pool_t *pool, ngx
                     }
 
                     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;
                 }
             }
@@ -374,8 +518,9 @@ ngx_parse_inet_url(ngx_pool_t *pool, ngx
         (void) ngx_cpystrn(p, host, len);
 
         u->addr.in_addr = inet_addr((const char *) p);
+        sin->sin_addr.s_addr = inet_addr((const char *) p);
 
-        if (u->addr.in_addr == INADDR_NONE) {
+        if (sin->sin_addr.s_addr == INADDR_NONE) {
             h = gethostbyname((const char *) p);
 
             if (h == NULL || h->h_addr_list[0] == NULL) {
@@ -385,16 +530,24 @@ ngx_parse_inet_url(ngx_pool_t *pool, ngx
             }
 
             u->addr.in_addr = *(in_addr_t *) (h->h_addr_list[0]);
+            sin->sin_addr.s_addr = *(in_addr_t *) (h->h_addr_list[0]);
+        }
+
+        if (sin->sin_addr.s_addr == INADDR_ANY) {
+            u->wildcard = 1;
         }
 
         ngx_free(p);
 
     } else {
         u->addr.in_addr = INADDR_ANY;
+        sin->sin_addr.s_addr = INADDR_ANY;
+        u->wildcard = 1;
     }
 
     if (u->no_port) {
         u->port = u->default_port;
+        sin->sin_port = htons(u->default_port);
     }
 
     if (u->listen) {
@@ -409,11 +562,134 @@ ngx_parse_inet_url(ngx_pool_t *pool, ngx
 }
 
 
+static ngx_int_t
+ngx_parse_inet6_url(ngx_pool_t *pool, ngx_url_t *u)
+{
+#if (NGX_HAVE_INET6)
+    int                   rc;
+    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;
+    }
+
+    if (last - p) {
+
+        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;
+        }
+
+        if (*port == ':') {
+            port++;
+
+            len = last - port;
+
+            if (len == 0) {
+                u->err = "invalid port";
+                return NGX_ERROR;
+            }
+
+            n = ngx_atoi(port, len);
+
+            if (n < 1 || n > 65536) {
+                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;
+        }
+    }
+
+    len = p - host;
+
+    if (len == 0) {
+        u->err = "no host";
+        return NGX_ERROR;
+    }
+
+    u->host.len = len++;
+    u->host.data = host;
+
+    p = ngx_alloc(len, pool->log);
+    if (p == NULL) {
+        return NGX_ERROR;
+    }
+
+    (void) ngx_cpystrn(p, host, len);
+
+    rc = inet_pton(AF_INET6, (const char *) p, &sin6->sin6_addr);
+
+    ngx_free(p);
+
+    if (rc == 0) {
+        u->err = "invalid IPv6 address";
+        return NGX_ERROR;
+    }
+
+    if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
+        u->wildcard = 1;
+    }
+
+    u->family = AF_INET6;
+
+    if (u->no_resolve) {
+        return NGX_OK;
+    }
+
+    if (u->no_port) {
+        u->port = u->default_port;
+        sin6->sin6_port = htons(u->default_port);
+    }
+
+    return NGX_OK;
+
+#else
+
+    u->err = "the INET6 sockets are not supported on this platform";
+
+    return NGX_ERROR;
+
+#endif
+}
+
+
 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;
@@ -428,6 +704,9 @@ ngx_inet_resolve_host(ngx_pool_t *pool, 
 
     /* AF_INET only */
 
+    sin = (struct sockaddr_in *) &u->sockaddr;
+    port = sin->sin_port;
+
     in_addr = inet_addr((char *) host);
 
     if (in_addr == INADDR_NONE) {
@@ -464,22 +743,22 @@ ngx_inet_resolve_host(ngx_pool_t *pool, 
             }
 
             sin->sin_family = AF_INET;
-            sin->sin_port = htons(u->port);
+            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(":65536") - 1;
+            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, p, len);
+            len = ngx_sock_ntop((struct sockaddr *) sin, p, len, 1);
 
-            u->addrs[i].name.len = ngx_sprintf(&p[len], ":%d", u->port) - p;
+            u->addrs[i].name.len = len;
             u->addrs[i].name.data = p;
         }
 
@@ -502,18 +781,19 @@ ngx_inet_resolve_host(ngx_pool_t *pool, 
         u->naddrs = 1;
 
         sin->sin_family = AF_INET;
-        sin->sin_port = htons(u->port);
+        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(":65536") - 1);
+        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.len = ngx_sprintf(p, "%V:%d",
+                                           &u->host, ntohs(port)) - p;
         u->addrs[0].name.data = p;
     }
 
--- a/src/core/ngx_inet.h
+++ b/src/core/ngx_inet.h
@@ -12,7 +12,25 @@
 #include <ngx_core.h>
 
 
-#define NGX_INET_ADDRSTRLEN  (sizeof("255.255.255.255") - 1)
+#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_SOCKADDR_STRLEN   (NGX_INET6_ADDRSTRLEN + sizeof(":65535") - 1)
+
+
+/*
+ * TODO: autoconfigure NGX_SOCKADDRLEN as
+ *       sizeof(struct sockaddr_storage)
+ *       sizeof(struct sockaddr_in6)
+ *       sizeof(struct sockaddr_in)
+ */
+
+#if (NGX_HAVE_INET6)
+#define NGX_SOCKADDRLEN       sizeof(struct sockaddr_in6)
+#else
+#define NGX_SOCKADDRLEN       sizeof(struct sockaddr_in)
+#endif
 
 
 typedef struct {
@@ -49,9 +67,13 @@ typedef struct {
     unsigned          one_addr:1;
 
     unsigned          no_port:1;
+    unsigned          wildcard:1;
 
     ngx_url_addr_t    addr;
 
+    socklen_t         socklen;
+    u_char            sockaddr[NGX_SOCKADDRLEN];
+
     ngx_peer_addr_t  *addrs;
     ngx_uint_t        naddrs;
 
@@ -60,7 +82,8 @@ typedef struct {
 
 
 in_addr_t ngx_inet_addr(u_char *text, size_t len);
-size_t ngx_sock_ntop(struct sockaddr *sa, u_char *text, size_t len);
+size_t ngx_sock_ntop(struct sockaddr *sa, 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, void *cidr);
 ngx_int_t ngx_parse_url(ngx_pool_t *pool, ngx_url_t *u);
--- a/src/event/ngx_event_accept.c
+++ b/src/event/ngx_event_accept.c
@@ -9,10 +9,6 @@
 #include <ngx_event.h>
 
 
-/* the buffer size is enough to hold "struct sockaddr_un" */
-#define NGX_SOCKLEN  512
-
-
 static ngx_int_t ngx_enable_accept_events(ngx_cycle_t *cycle);
 static ngx_int_t ngx_disable_accept_events(ngx_cycle_t *cycle);
 static void ngx_close_accepted_connection(ngx_connection_t *c);
@@ -29,7 +25,7 @@ ngx_event_accept(ngx_event_t *ev)
     ngx_listening_t   *ls;
     ngx_connection_t  *c, *lc;
     ngx_event_conf_t  *ecf;
-    char               sa[NGX_SOCKLEN];
+    u_char             sa[NGX_SOCKADDRLEN];
 
     ecf = ngx_event_get_conf(ngx_cycle->conf_ctx, ngx_event_core_module);
 
@@ -48,7 +44,7 @@ ngx_event_accept(ngx_event_t *ev)
                    "accept on %V, ready: %d", &ls->addr_text, ev->available);
 
     do {
-        socklen = NGX_SOCKLEN;
+        socklen = NGX_SOCKADDRLEN;
 
         s = accept(lc->fd, (struct sockaddr *) sa, &socklen);
 
@@ -153,8 +149,10 @@ ngx_event_accept(ngx_event_t *ev)
         c->log = log;
         c->pool->log = log;
 
+        c->socklen = socklen;
         c->listening = ls;
-        c->socklen = socklen;
+        c->local_sockaddr = ls->sockaddr;
+        c->local_socklen = ls->socklen;
 
         c->unexpected_eof = 1;
 
@@ -208,7 +206,7 @@ ngx_event_accept(ngx_event_t *ev)
             }
 
             c->addr_text.len = ngx_sock_ntop(c->sockaddr, c->addr_text.data,
-                                             ls->addr_text_max_len);
+                                             ls->addr_text_max_len, 0);
             if (c->addr_text.len == 0) {
                 ngx_close_accepted_connection(c);
                 return;
--- a/src/http/modules/ngx_http_access_module.c
+++ b/src/http/modules/ngx_http_access_module.c
@@ -9,8 +9,6 @@
 #include <ngx_http.h>
 
 
-/* AF_INET only */
-
 typedef struct {
     in_addr_t     mask;
     in_addr_t     addr;
@@ -103,6 +101,10 @@ ngx_http_access_handler(ngx_http_request
 
     /* AF_INET only */
 
+    if (r->connection->sockaddr->sa_family != AF_INET) {
+        return NGX_DECLINED;
+    }
+
     sin = (struct sockaddr_in *) r->connection->sockaddr;
 
     rule = alcf->rules->elts;
--- a/src/http/modules/ngx_http_browser_module.c
+++ b/src/http/modules/ngx_http_browser_module.c
@@ -318,6 +318,10 @@ ngx_http_browser(ngx_http_request_t *r, 
                 if (c == '.') {
                     version += ver * scale;
 
+                    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                                   "version: \"%ui\" \"%ui\"",
+                                   modern[i].version, version);
+
                     if (version > modern[i].version) {
                         return NGX_HTTP_MODERN_BROWSER;
                     }
@@ -339,6 +343,8 @@ ngx_http_browser(ngx_http_request_t *r, 
             if (version >= modern[i].version) {
                 return NGX_HTTP_MODERN_BROWSER;
             }
+
+            return NGX_HTTP_ANCIENT_BROWSER;
         }
 
         if (!cf->modern_unlisted_browsers) {
--- a/src/http/modules/ngx_http_empty_gif_module.c
+++ b/src/http/modules/ngx_http_empty_gif_module.c
@@ -122,6 +122,7 @@ ngx_http_empty_gif_handler(ngx_http_requ
         return rc;
     }
 
+    r->headers_out.content_type_len = sizeof("image/gif") - 1;
     r->headers_out.content_type.len = sizeof("image/gif") - 1;
     r->headers_out.content_type.data = (u_char *) "image/gif";
 
--- a/src/http/modules/ngx_http_geo_module.c
+++ b/src/http/modules/ngx_http_geo_module.c
@@ -175,6 +175,10 @@ ngx_http_geo_addr(ngx_http_request_t *r,
         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;
+        }
+
         sin = (struct sockaddr_in *) r->connection->sockaddr;
         return ntohl(sin->sin_addr.s_addr);
     }
--- a/src/http/modules/ngx_http_realip_module.c
+++ b/src/http/modules/ngx_http_realip_module.c
@@ -14,8 +14,6 @@
 #define NGX_HTTP_REALIP_HEADER   2
 
 
-/* AF_INET only */
-
 typedef struct {
     in_addr_t          mask;
     in_addr_t          addr;
@@ -209,6 +207,10 @@ found:
 
     /* AF_INET only */
 
+    if (r->connection->sockaddr->sa_family != AF_INET) {
+        return NGX_DECLINED;
+    }
+
     sin = (struct sockaddr_in *) c->sockaddr;
 
     from = rlcf->from->elts;
--- a/src/http/modules/ngx_http_upstream_ip_hash_module.c
+++ b/src/http/modules/ngx_http_upstream_ip_hash_module.c
@@ -15,7 +15,6 @@ typedef struct {
 
     ngx_uint_t                         hash;
 
-    /* AF_INET only */
     u_char                             addr[3];
 
     u_char                             tries;
@@ -111,11 +110,20 @@ ngx_http_upstream_init_ip_hash_peer(ngx_
     r->upstream->peer.get = ngx_http_upstream_get_ip_hash_peer;
 
     /* AF_INET only */
-    sin = (struct sockaddr_in *) r->connection->sockaddr;
-    p = (u_char *) &sin->sin_addr.s_addr;
-    iphp->addr[0] = p[0];
-    iphp->addr[1] = p[1];
-    iphp->addr[2] = p[2];
+
+    if (r->connection->sockaddr->sa_family == AF_INET) {
+
+        sin = (struct sockaddr_in *) r->connection->sockaddr;
+        p = (u_char *) &sin->sin_addr.s_addr;
+        iphp->addr[0] = p[0];
+        iphp->addr[1] = p[1];
+        iphp->addr[2] = p[2];
+
+    } else {
+        iphp->addr[0] = 0;
+        iphp->addr[1] = 0;
+        iphp->addr[2] = 0;
+    }
 
     iphp->hash = 89;
     iphp->tries = 0;
--- a/src/http/modules/perl/nginx.pm
+++ b/src/http/modules/perl/nginx.pm
@@ -47,7 +47,7 @@ our @EXPORT = qw(
     HTTP_INSUFFICIENT_STORAGE
 );
 
-our $VERSION = '0.7.35';
+our $VERSION = '0.7.36';
 
 require XSLoader;
 XSLoader::load('nginx', $VERSION);
--- a/src/http/ngx_http.c
+++ b/src/http/ngx_http.c
@@ -19,11 +19,17 @@ static ngx_int_t ngx_http_init_phase_han
 
 static ngx_int_t ngx_http_init_server_lists(ngx_conf_t *cf,
     ngx_array_t *servers, ngx_array_t *in_ports);
+static ngx_int_t ngx_http_add_ports(ngx_conf_t *cf,
+    ngx_http_core_srv_conf_t *cscf, ngx_array_t *ports,
+    ngx_http_listen_t *listen);
+static ngx_int_t ngx_http_add_addresses(ngx_conf_t *cf,
+    ngx_http_core_srv_conf_t *cscf, ngx_http_conf_port_t *port,
+    ngx_http_listen_t *listen);
 static ngx_int_t ngx_http_add_address(ngx_conf_t *cf,
-    ngx_http_core_srv_conf_t *cscf, ngx_http_conf_in_port_t *in_port,
+    ngx_http_core_srv_conf_t *cscf, ngx_http_conf_port_t *port,
     ngx_http_listen_t *listen);
 static ngx_int_t ngx_http_add_names(ngx_conf_t *cf,
-    ngx_http_core_srv_conf_t *cscf, ngx_http_conf_in_addr_t *in_addr);
+    ngx_http_core_srv_conf_t *cscf, ngx_http_conf_addr_t *addr);
 
 static char *ngx_http_merge_locations(ngx_conf_t *cf,
     ngx_queue_t *locations, void **loc_conf, ngx_http_module_t *module,
@@ -43,13 +49,23 @@ static ngx_http_location_tree_node_t *
     size_t prefix);
 
 static ngx_int_t ngx_http_optimize_servers(ngx_conf_t *cf,
-    ngx_http_core_main_conf_t *cmcf, ngx_array_t *in_ports);
-static ngx_int_t ngx_http_cmp_conf_in_addrs(const void *one, const void *two);
+    ngx_http_core_main_conf_t *cmcf, ngx_array_t *ports);
+static ngx_int_t ngx_http_server_names(ngx_conf_t *cf,
+    ngx_http_core_main_conf_t *cmcf, ngx_http_conf_addr_t *addr);
+static ngx_int_t ngx_http_cmp_conf_addrs(const void *one, const void *two);
 static int ngx_libc_cdecl ngx_http_cmp_dns_wildcards(const void *one,
     const void *two);
 
 static ngx_int_t ngx_http_init_listening(ngx_conf_t *cf,
-    ngx_http_conf_in_port_t *in_port);
+    ngx_http_conf_port_t *port);
+static ngx_listening_t *ngx_http_add_listening(ngx_conf_t *cf,
+    ngx_http_conf_addr_t *addr);
+static ngx_int_t ngx_http_add_addrs(ngx_conf_t *cf, ngx_http_port_t *hport,
+    ngx_http_conf_addr_t *addr);
+#if (NGX_HAVE_INET6)
+static ngx_int_t ngx_http_add_addrs6(ngx_conf_t *cf, ngx_http_port_t *hport,
+    ngx_http_conf_addr_t *addr);
+#endif
 
 ngx_uint_t   ngx_http_max_module;
 
@@ -351,8 +367,6 @@ ngx_http_block(ngx_conf_t *cf, ngx_comma
      * to find quickly the server core module configuration at run-time
      */
 
-    /* AF_INET only */
-
     if (ngx_http_init_server_lists(cf, &cmcf->servers, &in_ports) != NGX_OK) {
         return NGX_CONF_ERROR;
     }
@@ -360,8 +374,6 @@ ngx_http_block(ngx_conf_t *cf, ngx_comma
 
     /* optimize the lists of ports, addresses and server names */
 
-    /* AF_INET only */
-
     if (ngx_http_optimize_servers(cf, cmcf, &in_ports) != NGX_OK) {
         return NGX_CONF_ERROR;
     }
@@ -1099,16 +1111,13 @@ inclusive:
 
 static ngx_int_t
 ngx_http_init_server_lists(ngx_conf_t *cf, ngx_array_t *servers,
-    ngx_array_t *in_ports)
+    ngx_array_t *ports)
 {
-    ngx_uint_t                  s, l, p, a;
+    ngx_uint_t                  s, i;
     ngx_http_listen_t          *listen;
-    ngx_http_conf_in_port_t    *in_port;
-    ngx_http_conf_in_addr_t    *in_addr;
     ngx_http_core_srv_conf_t  **cscfp;
 
-    if (ngx_array_init(in_ports, cf->temp_pool, 2,
-                       sizeof(ngx_http_conf_in_port_t))
+    if (ngx_array_init(ports, cf->temp_pool, 2, sizeof(ngx_http_conf_port_t))
         != NGX_OK)
     {
         return NGX_ERROR;
@@ -1122,92 +1131,11 @@ ngx_http_init_server_lists(ngx_conf_t *c
         /* "listen" directives */
 
         listen = cscfp[s]->listen.elts;
-        for (l = 0; l < cscfp[s]->listen.nelts; l++) {
-
-            /* AF_INET only */
-
-            in_port = in_ports->elts;
-            for (p = 0; p < in_ports->nelts; p++) {
-
-                if (listen[l].port != in_port[p].port) {
-                    continue;
-                }
-
-                /* the port is already in the port list */
-
-                in_addr = in_port[p].addrs.elts;
-                for (a = 0; a < in_port[p].addrs.nelts; a++) {
-
-                    if (listen[l].addr != in_addr[a].addr) {
-                        continue;
-                    }
-
-                    /* the address is already in the address list */
-
-                    if (ngx_http_add_names(cf, cscfp[s], &in_addr[a]) != NGX_OK)
-                    {
-                        return NGX_ERROR;
-                    }
-
-                    /*
-                     * check the duplicate "default" server
-                     * for this address:port
-                     */
-
-                    if (listen[l].conf.default_server) {
-
-                        if (in_addr[a].default_server) {
-                            ngx_log_error(NGX_LOG_ERR, cf->log, 0,
-                                      "the duplicate default server in %s:%ui",
-                                       listen[l].file_name, listen[l].line);
-
-                            return NGX_ERROR;
-                        }
-
-                        in_addr[a].core_srv_conf = cscfp[s];
-                        in_addr[a].default_server = 1;
-#if (NGX_HTTP_SSL)
-                        in_addr[a].ssl = listen[l].conf.ssl;
-#endif
-                        in_addr[a].listen_conf = &listen[l].conf;
-                    }
-
-                    goto found;
-                }
-
-                /*
-                 * add the address to the addresses list that
-                 * bound to this port
-                 */
-
-                if (ngx_http_add_address(cf, cscfp[s], &in_port[p], &listen[l])
-                    != NGX_OK)
-                {
-                    return NGX_ERROR;
-                }
-
-                goto found;
-            }
-
-            /* add the port to the in_port list */
-
-            in_port = ngx_array_push(in_ports);
-            if (in_port == NULL) {
+        for (i = 0; i < cscfp[s]->listen.nelts; i++) {
+
+            if (ngx_http_add_ports(cf, cscfp[s], ports, &listen[i]) != NGX_OK) {
                 return NGX_ERROR;
             }
-
-            in_port->port = listen[l].port;
-            in_port->addrs.elts = NULL;
-
-            if (ngx_http_add_address(cf, cscfp[s], in_port, &listen[l])
-                != NGX_OK)
-            {
-                return NGX_ERROR;
-            }
-
-        found:
-
-            continue;
         }
     }
 
@@ -1215,6 +1143,140 @@ ngx_http_init_server_lists(ngx_conf_t *c
 }
 
 
+static ngx_int_t
+ngx_http_add_ports(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,
+    ngx_array_t *ports, ngx_http_listen_t *listen)
+{
+    in_port_t                 p;
+    ngx_uint_t                i;
+    struct sockaddr          *sa;
+    struct sockaddr_in       *sin;
+    ngx_http_conf_port_t     *port;
+#if (NGX_HAVE_INET6)
+    struct sockaddr_in6      *sin6;
+#endif
+
+    sa = (struct sockaddr *) &listen->sockaddr;
+
+    switch (sa->sa_family) {
+
+#if (NGX_HAVE_INET6)
+    case AF_INET6:
+        sin6 = (struct sockaddr_in6 *) sa;
+        p = sin6->sin6_port;
+        break;
+#endif
+
+    default: /* AF_INET */
+        sin = (struct sockaddr_in *) sa;
+        p = sin->sin_port;
+        break;
+    }
+
+    port = ports->elts;
+    for (i = 0; i < ports->nelts; i++) {
+
+        if (p != port[i].port || sa->sa_family != port[i].family) {
+            continue;
+        }
+
+        /* a port is already in the in_port list */
+
+        return ngx_http_add_addresses(cf, cscf, &port[i], listen);
+    }
+
+    /* add a port to the in_port list */
+
+    port = ngx_array_push(ports);
+    if (port == NULL) {
+        return NGX_ERROR;
+    }
+
+    port->family = sa->sa_family;
+    port->port = p;
+    port->addrs.elts = NULL;
+
+    return ngx_http_add_address(cf, cscf, port, listen);
+}
+
+
+static ngx_int_t
+ngx_http_add_addresses(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,
+    ngx_http_conf_port_t *port, ngx_http_listen_t *listen)
+{
+    u_char                *p;
+    size_t                 len, off;
+    ngx_uint_t             i;
+    struct sockaddr       *sa;
+    ngx_http_conf_addr_t  *addr;
+
+    /*
+     * we can not compare whole sockaddr struct's as kernel
+     * may fill some fields in inherited sockaddr struct's
+     */
+
+    sa = (struct sockaddr *) &listen->sockaddr;
+
+    switch (sa->sa_family) {
+
+#if (NGX_HAVE_INET6)
+    case AF_INET6:
+        off = offsetof(struct sockaddr_in6, sin6_addr);
+        len = 16;
+        break;
+#endif
+
+    default: /* AF_INET */
+        off = offsetof(struct sockaddr_in, sin_addr);
+        len = 4;
+        break;
+    }
+
+    p = listen->sockaddr + off;
+
+    addr = port->addrs.elts;
+
+    for (i = 0; i < port->addrs.nelts; i++) {
+
+        if (ngx_memcmp(p, (u_char *) addr[i].sockaddr + off, len) != 0) {
+            continue;
+        }
+
+        /* the address is already in the address list */
+
+        if (ngx_http_add_names(cf, cscf, &addr[i]) != NGX_OK) {
+            return NGX_ERROR;
+        }
+
+        /* check the duplicate "default" server for this address:port */
+
+        if (listen->conf.default_server) {
+
+            if (addr[i].default_server) {
+                ngx_log_error(NGX_LOG_ERR, cf->log, 0,
+                              "the duplicate default server in %s:%ui",
+                               listen->file_name, listen->line);
+
+                return NGX_ERROR;
+            }
+
+            addr[i].core_srv_conf = cscf;
+            addr[i].default_server = 1;
+#if (NGX_HTTP_SSL)
+            addr[i].ssl = listen->conf.ssl;
+#endif
+            addr[i].listen_conf = &listen->conf;
+        }
+
+        return NGX_OK;
+    }
+
+    /* add the address to the addresses list that bound to this port */
+
+    return ngx_http_add_address(cf, cscf, port, listen);
+}
+
+
 /*
  * add the server address, the server names and the server core module
  * configurations to the port (in_port)
@@ -1222,60 +1284,62 @@ ngx_http_init_server_lists(ngx_conf_t *c
 
 static ngx_int_t
 ngx_http_add_address(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,
-     ngx_http_conf_in_port_t *in_port, ngx_http_listen_t *listen)
+    ngx_http_conf_port_t *port, ngx_http_listen_t *listen)
 {
-    ngx_http_conf_in_addr_t  *in_addr;
-
-    if (in_port->addrs.elts == NULL) {
-        if (ngx_array_init(&in_port->addrs, cf->temp_pool, 4,
-                           sizeof(ngx_http_conf_in_addr_t))
+    ngx_http_conf_addr_t  *addr;
+
+    if (port->addrs.elts == NULL) {
+        if (ngx_array_init(&port->addrs, cf->temp_pool, 4,
+                           sizeof(ngx_http_conf_addr_t))
             != NGX_OK)
         {
             return NGX_ERROR;
         }
     }
 
-    in_addr = ngx_array_push(&in_port->addrs);
-    if (in_addr == NULL) {
+    addr = ngx_array_push(&port->addrs);
+    if (addr == NULL) {
         return NGX_ERROR;
     }
 
-    in_addr->addr = listen->addr;
-    in_addr->hash.buckets = NULL;
-    in_addr->hash.size = 0;
-    in_addr->wc_head = NULL;
-    in_addr->wc_tail = NULL;
-    in_addr->names.elts = NULL;
+    addr->sockaddr = (struct sockaddr *) &listen->sockaddr;
+    addr->socklen = listen->socklen;
+    addr->hash.buckets = NULL;
+    addr->hash.size = 0;
+    addr->wc_head = NULL;
+    addr->wc_tail = NULL;
+    addr->names.elts = NULL;
 #if (NGX_PCRE)
-    in_addr->nregex = 0;
-    in_addr->regex = NULL;
+    addr->nregex = 0;
+    addr->regex = NULL;
 #endif
-    in_addr->core_srv_conf = cscf;
-    in_addr->default_server = listen->conf.default_server;
-    in_addr->bind = listen->conf.bind;
+    addr->core_srv_conf = cscf;
+    addr->default_server = listen->conf.default_server;
+    addr->bind = listen->conf.bind;
+    addr->wildcard = listen->conf.wildcard;
 #if (NGX_HTTP_SSL)
-    in_addr->ssl = listen->conf.ssl;
+    addr->ssl = listen->conf.ssl;
 #endif
-    in_addr->listen_conf = &listen->conf;
-
-    return ngx_http_add_names(cf, cscf, in_addr);
+    addr->listen_conf = &listen->conf;
+
+    return ngx_http_add_names(cf, cscf, addr);
 }
 
 
 /*
  * add the server names and the server core module
- * configurations to the address:port (in_addr)
+ * configurations to the address:port
  */
 
 static ngx_int_t
 ngx_http_add_names(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,
-    ngx_http_conf_in_addr_t *in_addr)
+    ngx_http_conf_addr_t *addr)
 {
     ngx_uint_t               i;
     ngx_http_server_name_t  *server_names, *name;
 
-    if (in_addr->names.elts == NULL) {
-        if (ngx_array_init(&in_addr->names, cf->temp_pool, 4,
+    if (addr->names.elts == NULL) {
+        if (ngx_array_init(&addr->names, cf->temp_pool, 4,
                            sizeof(ngx_http_server_name_t))
             != NGX_OK)
         {
@@ -1293,7 +1357,7 @@ ngx_http_add_names(ngx_conf_t *cf, ngx_h
         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, cf->log, 0,
                        "name: %V", &server_names[i].name);
 
-        name = ngx_array_push(&in_addr->names);
+        name = ngx_array_push(&addr->names);
         if (name == NULL) {
             return NGX_ERROR;
         }
@@ -1307,187 +1371,184 @@ ngx_http_add_names(ngx_conf_t *cf, ngx_h
 
 static ngx_int_t
 ngx_http_optimize_servers(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf,
-    ngx_array_t *in_ports)
+    ngx_array_t *ports)
+{
+    ngx_uint_t                s, p, a;
+    ngx_http_conf_port_t     *port;
+    ngx_http_conf_addr_t     *addr;
+    ngx_http_server_name_t   *name;
+
+    port = ports->elts;
+    for (p = 0; p < ports->nelts; p++) {
+
+        ngx_sort(port[p].addrs.elts, (size_t) port[p].addrs.nelts,
+                 sizeof(ngx_http_conf_addr_t), ngx_http_cmp_conf_addrs);
+
+        /*
+         * check whether all name-based servers have the same
+         * configuraiton as a default server for given address:port
+         */
+
+        addr = port[p].addrs.elts;
+        for (a = 0; a < port[p].addrs.nelts; a++) {
+
+            name = addr[a].names.elts;
+            for (s = 0; s < addr[a].names.nelts; s++) {
+
+                if (addr[a].core_srv_conf == name[s].core_srv_conf) {
+                    continue;
+                }
+
+                if (ngx_http_server_names(cf, cmcf, &addr[a]) != NGX_OK) {
+                    return NGX_ERROR;
+                }
+
+                break;
+            }
+        }
+
+        if (ngx_http_init_listening(cf, &port[p]) != NGX_OK) {
+            return NGX_ERROR;
+        }
+    }
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_server_names(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf,
+    ngx_http_conf_addr_t *addr)
 {
     ngx_int_t                  rc;
-    ngx_uint_t                 s, p, a;
+    ngx_uint_t                 s;
     ngx_hash_init_t            hash;
     ngx_http_server_name_t    *name;
     ngx_hash_keys_arrays_t     ha;
-    ngx_http_conf_in_port_t   *in_port;
-    ngx_http_conf_in_addr_t   *in_addr;
 #if (NGX_PCRE)
     ngx_uint_t                 regex, i;
+
+    regex = 0;
 #endif
 
-    in_port = in_ports->elts;
-    for (p = 0; p < in_ports->nelts; p++) {
-
-        ngx_sort(in_port[p].addrs.elts, (size_t) in_port[p].addrs.nelts,
-                 sizeof(ngx_http_conf_in_addr_t), ngx_http_cmp_conf_in_addrs);
-
-        /*
-         * check whether all name-based servers have
-         * the same configuraiton as the default server
-         */
-
-        in_addr = in_port[p].addrs.elts;
-        for (a = 0; a < in_port[p].addrs.nelts; a++) {
-
-            name = in_addr[a].names.elts;
-            for (s = 0; s < in_addr[a].names.nelts; s++) {
-
-                if (in_addr[a].core_srv_conf != name[s].core_srv_conf) {
-                    goto virtual_names;
-                }
-            }
-
-            /*
-             * if all name-based servers have the same configuration
-             * as the default server, then we do not need to check
-             * them at run-time at all
-             */
-
-            in_addr[a].names.nelts = 0;
-
-            continue;
-
-        virtual_names:
-
-            ngx_memzero(&ha, sizeof(ngx_hash_keys_arrays_t));
-
-            ha.temp_pool = ngx_create_pool(16384, cf->log);
-            if (ha.temp_pool == NULL) {
-                return NGX_ERROR;
-            }
-
-            ha.pool = cf->pool;
-
-            if (ngx_hash_keys_array_init(&ha, NGX_HASH_LARGE) != NGX_OK) {
-                goto failed;
-            }
-
-#if (NGX_PCRE)
-            regex = 0;
-#endif
-
-            name = in_addr[a].names.elts;
-
-            for (s = 0; s < in_addr[a].names.nelts; s++) {
+    ngx_memzero(&ha, sizeof(ngx_hash_keys_arrays_t));
+
+    ha.temp_pool = ngx_create_pool(16384, cf->log);
+    if (ha.temp_pool == NULL) {
+        return NGX_ERROR;
+    }
+
+    ha.pool = cf->pool;
+
+    if (ngx_hash_keys_array_init(&ha, NGX_HASH_LARGE) != NGX_OK) {
+        goto failed;
+    }
+
+    name = addr->names.elts;
+
+    for (s = 0; s < addr->names.nelts; s++) {
 
 #if (NGX_PCRE)
-                if (name[s].regex) {
-                    regex++;
-                    continue;
-                }
+        if (name[s].regex) {
+            regex++;
+            continue;
+        }
 #endif
 
-                rc = ngx_hash_add_key(&ha, &name[s].name, name[s].core_srv_conf,
-                                      NGX_HASH_WILDCARD_KEY);
-
-                if (rc == NGX_ERROR) {
-                    return NGX_ERROR;
-                }
-
-                if (rc == NGX_DECLINED) {
-                    ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
-                                "invalid server name or wildcard \"%V\" on %s",
-                                &name[s].name, in_addr[a].listen_conf->addr);
-                    return NGX_ERROR;
-                }
-
-                if (rc == NGX_BUSY) {
-                    ngx_log_error(NGX_LOG_WARN, cf->log, 0,
-                                "conflicting server name \"%V\" on %s, ignored",
-                                &name[s].name, in_addr[a].listen_conf->addr);
-                }
-            }
-
-            hash.key = ngx_hash_key_lc;
-            hash.max_size = cmcf->server_names_hash_max_size;
-            hash.bucket_size = cmcf->server_names_hash_bucket_size;
-            hash.name = "server_names_hash";
-            hash.pool = cf->pool;
-
-            if (ha.keys.nelts) {
-                hash.hash = &in_addr[a].hash;
-                hash.temp_pool = NULL;
-
-                if (ngx_hash_init(&hash, ha.keys.elts, ha.keys.nelts) != NGX_OK)
-                {
-                    goto failed;
-                }
-            }
-
-            if (ha.dns_wc_head.nelts) {
-
-                ngx_qsort(ha.dns_wc_head.elts,
-                          (size_t) ha.dns_wc_head.nelts,
-                          sizeof(ngx_hash_key_t),
-                          ngx_http_cmp_dns_wildcards);
-
-                hash.hash = NULL;
-                hash.temp_pool = ha.temp_pool;
-
-                if (ngx_hash_wildcard_init(&hash, ha.dns_wc_head.elts,
-                                           ha.dns_wc_head.nelts)
-                    != NGX_OK)
-                {
-                    goto failed;
-                }
-
-                in_addr[a].wc_head = (ngx_hash_wildcard_t *) hash.hash;
-            }
-
-            if (ha.dns_wc_tail.nelts) {
-
-                ngx_qsort(ha.dns_wc_tail.elts,
-                          (size_t) ha.dns_wc_tail.nelts,
-                          sizeof(ngx_hash_key_t),
-                          ngx_http_cmp_dns_wildcards);
-
-                hash.hash = NULL;
-                hash.temp_pool = ha.temp_pool;
-
-                if (ngx_hash_wildcard_init(&hash, ha.dns_wc_tail.elts,
-                                           ha.dns_wc_tail.nelts)
-                    != NGX_OK)
-                {
-                    goto failed;
-                }
-
-                in_addr[a].wc_tail = (ngx_hash_wildcard_t *) hash.hash;
-            }
-
-            ngx_destroy_pool(ha.temp_pool);
+        rc = ngx_hash_add_key(&ha, &name[s].name, name[s].core_srv_conf,
+                              NGX_HASH_WILDCARD_KEY);
+
+        if (rc == NGX_ERROR) {
+            return NGX_ERROR;
+        }
+
+        if (rc == NGX_DECLINED) {
+            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+                          "invalid server name or wildcard \"%V\" on %s",
+                          &name[s].name, addr->listen_conf->addr);
+            return NGX_ERROR;
+        }
+
+        if (rc == NGX_BUSY) {
+            ngx_log_error(NGX_LOG_WARN, cf->log, 0,
+                          "conflicting server name \"%V\" on %s, ignored",
+                          &name[s].name, addr->listen_conf->addr);
+        }
+    }
+
+    hash.key = ngx_hash_key_lc;
+    hash.max_size = cmcf->server_names_hash_max_size;
+    hash.bucket_size = cmcf->server_names_hash_bucket_size;
+    hash.name = "server_names_hash";
+    hash.pool = cf->pool;
+
+    if (ha.keys.nelts) {
+        hash.hash = &addr->hash;
+        hash.temp_pool = NULL;
+
+        if (ngx_hash_init(&hash, ha.keys.elts, ha.keys.nelts) != NGX_OK) {
+            goto failed;
+        }
+    }
+
+    if (ha.dns_wc_head.nelts) {
+
+        ngx_qsort(ha.dns_wc_head.elts, (size_t) ha.dns_wc_head.nelts,
+                  sizeof(ngx_hash_key_t), ngx_http_cmp_dns_wildcards);
+
+        hash.hash = NULL;
+        hash.temp_pool = ha.temp_pool;
+
+        if (ngx_hash_wildcard_init(&hash, ha.dns_wc_head.elts,
+                                   ha.dns_wc_head.nelts)
+            != NGX_OK)
+        {
+            goto failed;
+        }
+
+        addr->wc_head = (ngx_hash_wildcard_t *) hash.hash;
+    }
+
+    if (ha.dns_wc_tail.nelts) {
+
+        ngx_qsort(ha.dns_wc_tail.elts, (size_t) ha.dns_wc_tail.nelts,
+                  sizeof(ngx_hash_key_t), ngx_http_cmp_dns_wildcards);
+
+        hash.hash = NULL;
+        hash.temp_pool = ha.temp_pool;
+
+        if (ngx_hash_wildcard_init(&hash, ha.dns_wc_tail.elts,
+                                   ha.dns_wc_tail.nelts)
+            != NGX_OK)
+        {
+            goto failed;
+        }
+
+        addr->wc_tail = (ngx_hash_wildcard_t *) hash.hash;
+    }
+
+    ngx_destroy_pool(ha.temp_pool);
 
 #if (NGX_PCRE)
 
-            if (regex == 0) {
-                continue;
-            }
-
-            in_addr[a].nregex = regex;
-            in_addr[a].regex = ngx_palloc(cf->pool,
-                                       regex * sizeof(ngx_http_server_name_t));
-
-            if (in_addr[a].regex == NULL) {
-                return NGX_ERROR;
-            }
-
-            for (i = 0, s = 0; s < in_addr[a].names.nelts; s++) {
-                if (name[s].regex) {
-                    in_addr[a].regex[i++] = name[s];
-                }
-            }
-#endif
-        }
-
-        if (ngx_http_init_listening(cf, &in_port[p]) != NGX_OK) {
-            return NGX_ERROR;
+    if (regex == 0) {
+        return NGX_OK;
+    }
+
+    addr->nregex = regex;
+    addr->regex = ngx_palloc(cf->pool, regex * sizeof(ngx_http_server_name_t));
+    if (addr->regex == NULL) {
+        return NGX_ERROR;
+    }
+
+    for (i = 0, s = 0; s < addr->names.nelts; s++) {
+        if (name[s].regex) {
+            addr->regex[i++] = name[s];
         }
     }
 
+#endif
+
     return NGX_OK;
 
 failed:
@@ -1499,15 +1560,15 @@ failed:
 
 
 static ngx_int_t
-ngx_http_cmp_conf_in_addrs(const void *one, const void *two)
+ngx_http_cmp_conf_addrs(const void *one, const void *two)
 {
-    ngx_http_conf_in_addr_t  *first, *second;
-
-    first = (ngx_http_conf_in_addr_t *) one;
-    second = (ngx_http_conf_in_addr_t *) two;
-
-    if (first->addr == INADDR_ANY) {
-        /* the INADDR_ANY must be the last resort, shift it to the end */
+    ngx_http_conf_addr_t  *first, *second;
+
+    first = (ngx_http_conf_addr_t *) one;
+    second = (ngx_http_conf_addr_t *) two;
+
+    if (first->wildcard) {
+        /* a wildcard address must be the last resort, shift it to the end */
         return 1;
     }
 
@@ -1540,171 +1601,302 @@ ngx_http_cmp_dns_wildcards(const void *o
 
 
 static ngx_int_t
-ngx_http_init_listening(ngx_conf_t *cf, ngx_http_conf_in_port_t *in_port)
+ngx_http_init_listening(ngx_conf_t *cf, ngx_http_conf_port_t *port)
 {
-    ngx_uint_t                 i, a, last, bind_all, done;
+    ngx_uint_t                 i, a, last, bind_wildcard;
     ngx_listening_t           *ls;
-    ngx_http_in_port_t        *hip;
-    ngx_http_conf_in_addr_t   *in_addr;
-    ngx_http_virtual_names_t  *vn;
-    ngx_http_core_loc_conf_t  *clcf;
-    ngx_http_core_srv_conf_t  *cscf;
-
-    in_addr = in_port->addrs.elts;
-    last = in_port->addrs.nelts;
+    ngx_http_port_t           *hport;
+    ngx_http_conf_addr_t      *addr;
+
+    addr = port->addrs.elts;
+    last = port->addrs.nelts;
 
     /*
-     * if there is a binding to a "*:port" then we need to bind()
-     * to the "*:port" only and ignore other bindings
+     * If there is a binding to an "*:port" then we need to bind() to
+     * the "*:port" only and ignore other implicit bindings.  The bindings
+     * have been already sorted: explicit bindings are on the start, then
+     * implicit bindings go, and wildcard binding is in the end.
      */
 
-    if (in_addr[last - 1].addr == INADDR_ANY) {
-        in_addr[last - 1].bind = 1;
-        bind_all = 0;
+    if (addr[last - 1].wildcard) {
+        addr[last - 1].bind = 1;
+        bind_wildcard = 1;
 
     } else {
-        bind_all = 1;
+        bind_wildcard = 0;
     }
 
     a = 0;
 
     while (a < last) {
 
-        if (!bind_all && !in_addr[a].bind) {
+        if (bind_wildcard && !addr[a].bind) {
             a++;
             continue;
         }
 
-        ls = ngx_listening_inet_stream_socket(cf, in_addr[a].addr,
-                                              in_port->port);
+        ls = ngx_http_add_listening(cf, &addr[a]);
         if (ls == NULL) {
             return NGX_ERROR;
         }
 
-        ls->addr_ntop = 1;
-
-        ls->handler = ngx_http_init_connection;
-
-        cscf = in_addr[a].core_srv_conf;
-        ls->pool_size = cscf->connection_pool_size;
-        ls->post_accept_timeout = cscf->client_header_timeout;
-
-        clcf = cscf->ctx->loc_conf[ngx_http_core_module.ctx_index];
-
-        ls->log = *clcf->err_log;
-        ls->log.data = &ls->addr_text;
-        ls->log.handler = ngx_accept_log_error;
-
-#if (NGX_WIN32)
-        {
-        ngx_iocp_conf_t  *iocpcf;
-
-        iocpcf = ngx_event_get_conf(cf->cycle->conf_ctx, ngx_iocp_module);
-        if (iocpcf->acceptex_read) {
-            ls->post_accept_buffer_size = cscf->client_header_buffer_size;
-        }
-        }
-#endif
-
-        ls->backlog = in_addr[a].listen_conf->backlog;
-        ls->rcvbuf = in_addr[a].listen_conf->rcvbuf;
-        ls->sndbuf = in_addr[a].listen_conf->sndbuf;
-
-#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER)
-        ls->accept_filter = in_addr[a].listen_conf->accept_filter;
-#endif
-
-#if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT)
-        ls->deferred_accept = in_addr[a].listen_conf->deferred_accept;
-#endif
-
-        hip = ngx_palloc(cf->pool, sizeof(ngx_http_in_port_t));
-        if (hip == NULL) {
-            return NGX_ERROR;
-        }
-
-        hip->port = in_port->port;
-
-        hip->port_text.data = ngx_pnalloc(cf->pool, 7);
-        if (hip->port_text.data == NULL) {
+        hport = ngx_pcalloc(cf->pool, sizeof(ngx_http_port_t));
+        if (hport == NULL) {
             return NGX_ERROR;
         }
 
-        ls->servers = hip;
-
-        hip->port_text.len = ngx_sprintf(hip->port_text.data, ":%d", hip->port)
-                             - hip->port_text.data;
-
-        in_addr = in_port->addrs.elts;
-
-        if (in_addr[a].bind && in_addr[a].addr != INADDR_ANY) {
-            hip->naddrs = 1;
-            done = 0;
-
-        } else if (in_port->addrs.nelts > 1
-                   && in_addr[last - 1].addr == INADDR_ANY)
-        {
-            hip->naddrs = last;
-            done = 1;
+        ls->servers = hport;
+
+        hport->port = ntohs(port->port);
+
+        for (i = ls->addr_text.len - 1; i; i--) {
+
+            if (ls->addr_text.data[i] == ':') {
+                hport->port_text.len = ls->addr_text.len - i;
+                hport->port_text.data = &ls->addr_text.data[i];
+                break;
+            }
+        }
+
+        if (a == last - 1) {
+            hport->naddrs = last;
 
         } else {
-            hip->naddrs = 1;
-            done = 0;
-        }
-
-        hip->addrs = ngx_pcalloc(cf->pool,
-                                 hip->naddrs * sizeof(ngx_http_in_addr_t));
-        if (hip->addrs == NULL) {
-            return NGX_ERROR;
+            hport->naddrs = 1;
+            a = 0;
         }
 
-        for (i = 0; i < hip->naddrs; i++) {
-            hip->addrs[i].addr = in_addr[i].addr;
-            hip->addrs[i].core_srv_conf = in_addr[i].core_srv_conf;
-
-#if (NGX_HTTP_SSL)
-            hip->addrs[i].ssl = in_addr[i].ssl;
-#endif
-
-            if (in_addr[i].hash.buckets == NULL
-                && (in_addr[i].wc_head == NULL
-                    || in_addr[i].wc_head->hash.buckets == NULL)
-                && (in_addr[i].wc_head == NULL
-                    || in_addr[i].wc_head->hash.buckets == NULL))
-            {
-                continue;
-            }
-
-            vn = ngx_palloc(cf->pool, sizeof(ngx_http_virtual_names_t));
-            if (vn == NULL) {
+        switch (ls->sockaddr->sa_family) {
+
+#if (NGX_HAVE_INET6)
+        case AF_INET6:
+            if (ngx_http_add_addrs6(cf, hport, addr) != NGX_OK) {
                 return NGX_ERROR;
             }
-            hip->addrs[i].virtual_names = vn;
-
-            vn->names.hash = in_addr[i].hash;
-            vn->names.wc_head = in_addr[i].wc_head;
-            vn->names.wc_tail = in_addr[i].wc_tail;
-#if (NGX_PCRE)
-            vn->nregex = in_addr[i].nregex;
-            vn->regex = in_addr[i].regex;
+            break;
 #endif
+        default: /* AF_INET */
+            if (ngx_http_add_addrs(cf, hport, addr) != NGX_OK) {
+                return NGX_ERROR;
+            }
+            break;
         }
 
-        if (done) {
-            return NGX_OK;
-        }
-
-        in_addr++;
-        in_port->addrs.elts = in_addr;
+        addr++;
         last--;
-
-        a = 0;
     }
 
     return NGX_OK;
 }
 
 
+static ngx_listening_t *
+ngx_http_add_listening(ngx_conf_t *cf, ngx_http_conf_addr_t *addr)
+{
+    ngx_listening_t           *ls;
+    struct sockaddr           *sa;
+    ngx_http_core_loc_conf_t  *clcf;
+    ngx_http_core_srv_conf_t  *cscf;
+    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, addr->socklen);
+    if (sa == NULL) {
+        return NULL;
+    }
+
+    ngx_memcpy(sa, addr->sockaddr, addr->socklen);
+
+    ls->sockaddr = sa;
+    ls->socklen = addr->socklen;
+
+    ls->addr_text.len = ngx_sock_ntop(sa, text, NGX_SOCKADDR_STRLEN, 1);
+
+    ls->addr_text.data = ngx_pnalloc(cf->pool, ls->addr_text.len);
+    if (ls->addr_text.data == NULL) {
+        return NULL;
+    }
+
+    ngx_memcpy(ls->addr_text.data, text, ls->addr_text.len);
+
+    ls->fd = (ngx_socket_t) -1;
+    ls->type = SOCK_STREAM;
+
+    switch (ls->sockaddr->sa_family) {
+#if (NGX_HAVE_INET6)
+    case AF_INET6:
+         ls->addr_text_max_len = NGX_INET6_ADDRSTRLEN;
+         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_ntop = 1;
+
+    ls->handler = ngx_http_init_connection;
+
+    cscf = addr->core_srv_conf;
+    ls->pool_size = cscf->connection_pool_size;
+    ls->post_accept_timeout = cscf->client_header_timeout;
+
+    clcf = cscf->ctx->loc_conf[ngx_http_core_module.ctx_index];
+
+    ls->log = *clcf->err_log;
+    ls->log.data = &ls->addr_text;
+    ls->log.handler = ngx_accept_log_error;
+
+#if (NGX_WIN32)
+    {
+    ngx_iocp_conf_t  *iocpcf;
+
+    iocpcf = ngx_event_get_conf(cf->cycle->conf_ctx, ngx_iocp_module);
+    if (iocpcf->acceptex_read) {
+        ls->post_accept_buffer_size = cscf->client_header_buffer_size;
+    }
+    }
+#endif
+
+    ls->backlog = addr->listen_conf->backlog;
+    ls->rcvbuf = addr->listen_conf->rcvbuf;
+    ls->sndbuf = addr->listen_conf->sndbuf;
+
+#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER)
+    ls->accept_filter = addr->listen_conf->accept_filter;
+#endif
+
+#if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT)
+    ls->deferred_accept = addr->listen_conf->deferred_accept;
+#endif
+
+    return ls;
+}
+
+
+static ngx_int_t
+ngx_http_add_addrs(ngx_conf_t *cf, ngx_http_port_t *hport,
+    ngx_http_conf_addr_t *addr)
+{
+    ngx_uint_t                 i;
+    ngx_http_in_addr_t        *addrs;
+    struct sockaddr_in        *sin;
+    ngx_http_virtual_names_t  *vn;
+
+    hport->addrs = ngx_pcalloc(cf->pool,
+                               hport->naddrs * sizeof(ngx_http_in_addr_t));
+    if (hport->addrs == NULL) {
+        return NGX_ERROR;
+    }
+
+    addrs = hport->addrs;
+
+    for (i = 0; i < hport->naddrs; i++) {
+
+        sin = (struct sockaddr_in *) addr[i].sockaddr;
+        addrs[i].addr = sin->sin_addr.s_addr;
+        addrs[i].conf.core_srv_conf = addr[i].core_srv_conf;
+#if (NGX_HTTP_SSL)
+        addrs[i].conf.ssl = addr[i].ssl;
+#endif
+
+        if (addr[i].hash.buckets == NULL
+            && (addr[i].wc_head == NULL
+                || addr[i].wc_head->hash.buckets == NULL)
+            && (addr[i].wc_head == NULL
+                || addr[i].wc_head->hash.buckets == NULL))
+        {
+            continue;
+        }
+
+        vn = ngx_palloc(cf->pool, sizeof(ngx_http_virtual_names_t));
+        if (vn == NULL) {
+            return NGX_ERROR;
+        }
+
+        addrs[i].conf.virtual_names = vn;
+
+        vn->names.hash = addr[i].hash;
+        vn->names.wc_head = addr[i].wc_head;
+        vn->names.wc_tail = addr[i].wc_tail;
+#if (NGX_PCRE)
+        vn->nregex = addr[i].nregex;
+        vn->regex = addr[i].regex;
+#endif
+    }
+
+    return NGX_OK;
+}
+
+
+#if (NGX_HAVE_INET6)
+
+static ngx_int_t
+ngx_http_add_addrs6(ngx_conf_t *cf, ngx_http_port_t *hport,
+    ngx_http_conf_addr_t *addr)
+{
+    ngx_uint_t                 i;
+    ngx_http_in6_addr_t       *addrs6;
+    struct sockaddr_in6       *sin6;
+    ngx_http_virtual_names_t  *vn;
+
+    hport->addrs = ngx_pcalloc(cf->pool,
+                               hport->naddrs * sizeof(ngx_http_in6_addr_t));
+    if (hport->addrs == NULL) {
+        return NGX_ERROR;
+    }
+
+    addrs6 = hport->addrs;
+
+    for (i = 0; i < hport->naddrs; i++) {
+
+        sin6 = (struct sockaddr_in6 *) addr[i].sockaddr;
+        addrs6[i].addr6 = sin6->sin6_addr;
+        addrs6[i].conf.core_srv_conf = addr[i].core_srv_conf;
+#if (NGX_HTTP_SSL)
+        addrs6[i].conf.ssl = addr[i].ssl;
+#endif
+
+        if (addr[i].hash.buckets == NULL
+            && (addr[i].wc_head == NULL
+                || addr[i].wc_head->hash.buckets == NULL)
+            && (addr[i].wc_head == NULL
+                || addr[i].wc_head->hash.buckets == NULL))
+        {
+            continue;
+        }
+
+        vn = ngx_palloc(cf->pool, sizeof(ngx_http_virtual_names_t));
+        if (vn == NULL) {
+            return NGX_ERROR;
+        }
+
+        addrs6[i].conf.virtual_names = vn;
+
+        vn->names.hash = addr[i].hash;
+        vn->names.wc_head = addr[i].wc_head;
+        vn->names.wc_tail = addr[i].wc_tail;
+#if (NGX_PCRE)
+        vn->nregex = addr[i].nregex;
+        vn->regex = addr[i].regex;
+#endif
+    }
+
+    return NGX_OK;
+}
+
+#endif
+
+
 char *
 ngx_http_types_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 {
--- a/src/http/ngx_http_core_module.c
+++ b/src/http/ngx_http_core_module.c
@@ -1776,32 +1776,37 @@ ngx_http_server_addr(ngx_http_request_t 
 {
     socklen_t            len;
     ngx_connection_t    *c;
-    struct sockaddr_in   sin;
-
-    /* AF_INET only */
+    struct sockaddr_in  *sin;
+    u_char               sa[NGX_SOCKADDRLEN];
 
     c = r->connection;
 
-    if (r->in_addr == 0) {
-        len = sizeof(struct sockaddr_in);
-        if (getsockname(c->fd, (struct sockaddr *) &sin, &len) == -1) {
+    if (c->local_sockaddr == NULL) {
+
+        len = NGX_SOCKADDRLEN;
+
+        if (getsockname(c->fd, (struct sockaddr *) &sa, &len) == -1) {
             ngx_connection_error(c, ngx_socket_errno, "getsockname() failed");
             return NGX_ERROR;
         }
 
-        r->in_addr = sin.sin_addr.s_addr;
-
-    } else {
-        sin.sin_family = c->sockaddr->sa_family;
-        sin.sin_addr.s_addr = r->in_addr;
+        c->local_sockaddr = ngx_palloc(r->connection->pool, len);
+        if (c->local_sockaddr == NULL) {
+            return NGX_ERROR;
+        }
+
+        c->local_socklen = len;
+        ngx_memcpy(c->local_sockaddr, &sa, len);
     }
 
+    sin = (struct sockaddr_in *) c->local_sockaddr;
+    r->in_addr = sin->sin_addr.s_addr;
+
     if (s == NULL) {
         return NGX_OK;
     }
 
-    s->len = ngx_sock_ntop((struct sockaddr *) &sin, s->data,
-                           NGX_INET_ADDRSTRLEN);
+    s->len = ngx_sock_ntop(c->local_sockaddr, s->data, s->len, 0);
 
     return NGX_OK;
 }
@@ -2729,7 +2734,8 @@ ngx_http_core_create_srv_conf(ngx_conf_t
      *     conf->client_large_buffers.num = 0;
      */
 
-    if (ngx_array_init(&cscf->listen, cf->pool, 4, sizeof(ngx_http_listen_t))
+    if (ngx_array_init(&cscf->listen, cf->temp_pool, 4,
+                       sizeof(ngx_http_listen_t))
         == NGX_ERROR)
     {
         return NGX_CONF_ERROR;
@@ -2761,6 +2767,7 @@ ngx_http_core_merge_srv_conf(ngx_conf_t 
     ngx_http_core_srv_conf_t *conf = child;
 
     ngx_http_listen_t       *ls;
+    struct sockaddr_in      *sin;
     ngx_http_server_name_t  *sn;
 
     /* TODO: it does not merge, it inits only */
@@ -2773,14 +2780,15 @@ ngx_http_core_merge_srv_conf(ngx_conf_t 
 
         ngx_memzero(ls, sizeof(ngx_http_listen_t));
 
-        ls->addr = INADDR_ANY;
+        sin = (struct sockaddr_in *) &ls->sockaddr;
+
+        sin->sin_family = AF_INET;
 #if (NGX_WIN32)
-        ls->port = 80;
+        sin->sin_port = htons(80);
 #else
-        /* STUB: getuid() should be cached */
-        ls->port = (getuid() == 0) ? 80 : 8000;
+        sin->sin_port = htons((getuid() == 0) ? 80 : 8000);
 #endif
-        ls->family = AF_INET;
+        sin->sin_addr.s_addr = INADDR_ANY;
 
         ls->conf.backlog = NGX_LISTEN_BACKLOG;
         ls->conf.rcvbuf = -1;
@@ -3159,8 +3167,6 @@ ngx_http_core_merge_loc_conf(ngx_conf_t 
 }
 
 
-/* AF_INET only */
-
 static char *
 ngx_http_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 {
@@ -3201,17 +3207,18 @@ ngx_http_core_listen(ngx_conf_t *cf, ngx
 
     ngx_memzero(ls, sizeof(ngx_http_listen_t));
 
-    ls->family = u.family;
-    ls->addr = u.addr.in_addr;
-    ls->port = u.port;
+    ngx_memcpy(ls->sockaddr, u.sockaddr, u.socklen);
+
+    ls->socklen = u.socklen;
     ls->file_name = cf->conf_file->file.name.data;
     ls->line = cf->conf_file->line;
     ls->conf.backlog = NGX_LISTEN_BACKLOG;
     ls->conf.rcvbuf = -1;
     ls->conf.sndbuf = -1;
-
-    n = ngx_inet_ntop(AF_INET, &ls->addr, ls->conf.addr, NGX_INET_ADDRSTRLEN);
-    ngx_sprintf(&ls->conf.addr[n], ":%ui", ls->port);
+    ls->conf.wildcard = u.wildcard;
+
+    (void) ngx_sock_ntop((struct sockaddr *) &ls->sockaddr, ls->conf.addr,
+                         NGX_SOCKADDR_STRLEN, 1);
 
     if (cf->args->nelts == 2) {
         return NGX_CONF_OK;
--- a/src/http/ngx_http_core_module.h
+++ b/src/http/ngx_http_core_module.h
@@ -40,6 +40,7 @@ typedef struct ngx_http_core_loc_conf_s 
 typedef struct {
     unsigned                   default_server:1;
     unsigned                   bind:1;
+    unsigned                   wildcard:1;
 #if (NGX_HTTP_SSL)
     unsigned                   ssl:1;
 #endif
@@ -55,15 +56,13 @@ typedef struct {
     ngx_uint_t                 deferred_accept;
 #endif
 
-    u_char                     addr[NGX_INET_ADDRSTRLEN + sizeof(":65535")];
-
+    u_char                     addr[NGX_SOCKADDR_STRLEN + 1];
 } ngx_http_listen_conf_t;
 
 
 typedef struct {
-    in_addr_t                  addr;
-    in_port_t                  port;
-    int                        family;
+    u_char                     sockaddr[NGX_SOCKADDRLEN];
+    socklen_t                  socklen;
 
     u_char                    *file_name;
     ngx_uint_t                 line;
@@ -173,8 +172,6 @@ typedef struct {
 
 
 typedef struct {
-    in_addr_t                  addr;
-
     /* the default server configuration for this address:port */
     ngx_http_core_srv_conf_t  *core_srv_conf;
 
@@ -183,25 +180,45 @@ typedef struct {
 #if (NGX_HTTP_SSL)
     ngx_uint_t                 ssl;   /* unsigned  ssl:1; */
 #endif
+} ngx_http_addr_conf_t;
+
+
+typedef struct {
+    in_addr_t                  addr;
+    ngx_http_addr_conf_t       conf;
 } ngx_http_in_addr_t;
 
 
+#if (NGX_HAVE_INET6)
+
+typedef struct {
+    struct in6_addr            addr6;
+    ngx_http_addr_conf_t       conf;
+} ngx_http_in6_addr_t;
+
+#endif
+
+
 typedef struct {
     in_port_t                  port;
     ngx_str_t                  port_text;
-    ngx_http_in_addr_t        *addrs;
+
+    /* ngx_http_in_addr_t or ngx_http_in6_addr_t */
+    void                      *addrs;
     ngx_uint_t                 naddrs;
-} ngx_http_in_port_t;
+} ngx_http_port_t;
 
 
 typedef struct {
+    ngx_int_t                  family;
     in_port_t                  port;
-    ngx_array_t                addrs;     /* array of ngx_http_conf_in_addr_t */
-} ngx_http_conf_in_port_t;
+    ngx_array_t                addrs;     /* array of ngx_http_conf_addr_t */
+} ngx_http_conf_port_t;
 
 
 typedef struct {
-    in_addr_t                  addr;
+    struct sockaddr           *sockaddr;
+    socklen_t                  socklen;
 
     ngx_hash_t                 hash;
     ngx_hash_wildcard_t       *wc_head;
@@ -219,12 +236,13 @@ typedef struct {
 
     unsigned                   default_server:1;
     unsigned                   bind:1;
+    unsigned                   wildcard:1;
 #if (NGX_HTTP_SSL)
     unsigned                   ssl:1;
 #endif
 
     ngx_http_listen_conf_t    *listen_conf;
-} ngx_http_conf_in_addr_t;
+} ngx_http_conf_addr_t;
 
 
 struct ngx_http_server_name_s {
--- a/src/http/ngx_http_header_filter_module.c
+++ b/src/http/ngx_http_header_filter_module.c
@@ -161,8 +161,7 @@ ngx_http_header_filter(ngx_http_request_
     ngx_table_elt_t           *header;
     ngx_http_core_loc_conf_t  *clcf;
     ngx_http_core_srv_conf_t  *cscf;
-    /* AF_INET only */
-    u_char                     addr[NGX_INET_ADDRSTRLEN];
+    u_char                     addr[NGX_SOCKADDR_STRLEN];
 
     r->header_sent = 1;
 
@@ -290,6 +289,7 @@ ngx_http_header_filter(ngx_http_request_
             host = r->headers_in.server;
 
         } else {
+            host.len = NGX_SOCKADDR_STRLEN;
             host.data = addr;
 
             if (ngx_http_server_addr(r, &host) != NGX_OK) {
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -232,13 +232,19 @@ ngx_http_init_request(ngx_event_t *rev)
     ngx_uint_t                  i;
     ngx_connection_t           *c;
     ngx_http_request_t         *r;
-    ngx_http_in_port_t         *hip;
-    ngx_http_in_addr_t         *hia;
+    struct sockaddr_in         *sin;
+    ngx_http_port_t            *port;
+    ngx_http_in_addr_t         *addr;
     ngx_http_log_ctx_t         *ctx;
+    ngx_http_addr_conf_t       *addr_conf;
     ngx_http_connection_t      *hc;
     ngx_http_core_srv_conf_t   *cscf;
     ngx_http_core_loc_conf_t   *clcf;
     ngx_http_core_main_conf_t  *cmcf;
+#if (NGX_HAVE_INET6)
+    struct sockaddr_in6        *sin6;
+    ngx_http_in6_addr_t        *addr6;
+#endif
 
 #if (NGX_STAT_STUB)
     ngx_atomic_fetch_add(ngx_stat_reading, -1);
@@ -292,58 +298,90 @@ ngx_http_init_request(ngx_event_t *rev)
 
     /* find the server configuration for the address:port */
 
-    /* AF_INET only */
-
-    hip = c->listening->servers;
-    hia = hip->addrs;
-
-    r->port = hip->port;
-    r->port_text = &hip->port_text;
-
-    i = 0;
+    port = c->listening->servers;
+
+    r->port = port->port;
+    r->port_text = &port->port_text;
 
     r->connection = c;
 
-    if (hip->naddrs > 1) {
+    if (port->naddrs > 1) {
 
         /*
-         * There are several addresses on this port and one of them
-         * is the "*:port" wildcard so getsockname() is needed to determine
-         * the server address.
-         *
-         * AcceptEx() already has given this address.
+         * there are several addresses on this port and one of them
+         * is an "*:port" wildcard so getsockname() in ngx_http_server_addr()
+         * is required to determine a server address
          */
 
-#if (NGX_WIN32)
-        if (c->local_sockaddr) {
-            r->in_addr =
-                   ((struct sockaddr_in *) c->local_sockaddr)->sin_addr.s_addr;
-
-        } else
+        c->local_sockaddr = NULL;
+
+        if (ngx_http_server_addr(r, NULL) != NGX_OK) {
+            ngx_http_close_connection(c);
+            return;
+        }
+
+        switch (c->local_sockaddr->sa_family) {
+
+#if (NGX_HAVE_INET6)
+        case AF_INET6:
+            sin6 = (struct sockaddr_in6 *) c->local_sockaddr;
+
+            addr6 = (ngx_http_in6_addr_t *) port->addrs;
+
+            /* the last address is "*" */
+
+            for (i = 0; i < port->naddrs - 1; i++) {
+                if (ngx_memcmp(&addr6[i].addr6, &sin6->sin6_addr, 16) == 0) {
+                    break;
+                }
+            }
+
+            addr_conf = &addr6[i].conf;
+
+            break;
 #endif
-        {
-            if (ngx_http_server_addr(r, NULL) != NGX_OK) {
-                ngx_http_close_connection(c);
-                return;
+
+        default: /* AF_INET */
+            sin = (struct sockaddr_in *) c->local_sockaddr;
+
+            addr = port->addrs;
+
+            /* the last address is "*" */
+
+            for (i = 0; i < port->naddrs - 1; i++) {
+                if (addr[i].addr == sin->sin_addr.s_addr) {
+                    break;
+                }
             }
-        }
-
-        /* the last address is "*" */
-
-        for ( /* void */ ; i < hip->naddrs - 1; i++) {
-            if (hia[i].addr == r->in_addr) {
-                break;
-            }
+
+            addr_conf = &addr[i].conf;
+
+            break;
         }
 
     } else {
-        r->in_addr = hia[0].addr;
+
+        switch (c->local_sockaddr->sa_family) {
+
+#if (NGX_HAVE_INET6)
+        case AF_INET6:
+            addr6 = (ngx_http_in6_addr_t *) port->addrs;
+            addr_conf = &addr6[0].conf;
+            break;
+#endif
+
+        default: /* AF_INET */
+            addr = port->addrs;
+            addr_conf = &addr[0].conf;
+            r->in_addr = addr[0].addr;
+            break;
+        }
     }
 
-    r->virtual_names = hia[i].virtual_names;
+    r->virtual_names = addr_conf->virtual_names;
 
     /* the default server configuration for the address:port */
-    cscf = hia[i].core_srv_conf;
+    cscf = addr_conf->core_srv_conf;
 
     r->main_conf = cscf->ctx->main_conf;
     r->srv_conf = cscf->ctx->srv_conf;
@@ -357,13 +395,13 @@ ngx_http_init_request(ngx_event_t *rev)
     ngx_http_ssl_srv_conf_t  *sscf;
 
     sscf = ngx_http_get_module_srv_conf(r, ngx_http_ssl_module);
-    if (sscf->enable || hia[i].ssl) {
+    if (sscf->enable || addr_conf->ssl) {
 
         if (c->ssl == NULL) {
 
             c->log->action = "SSL handshaking";
 
-            if (hia[i].ssl && sscf->ssl.ctx == NULL) {
+            if (addr_conf->ssl && sscf->ssl.ctx == NULL) {
                 ngx_log_error(NGX_LOG_ERR, c->log, 0,
                               "no \"ssl_certificate\" is defined "
                               "in server listening on SSL port");
--- a/src/http/ngx_http_variables.c
+++ b/src/http/ngx_http_variables.c
@@ -828,17 +828,37 @@ static ngx_int_t
 ngx_http_variable_binary_remote_addr(ngx_http_request_t *r,
     ngx_http_variable_value_t *v, uintptr_t data)
 {
-    struct sockaddr_in  *sin;
+    struct sockaddr_in   *sin;
+#if (NGX_HAVE_INET6)
+    struct sockaddr_in6  *sin6;
+#endif
 
-    /* AF_INET only */
+    switch (r->connection->sockaddr->sa_family) {
 
-    sin = (struct sockaddr_in *) r->connection->sockaddr;
+#if (NGX_HAVE_INET6)
+    case AF_INET6:
+        sin6 = (struct sockaddr_in6 *) r->connection->sockaddr;
 
-    v->len = sizeof(in_addr_t);
-    v->valid = 1;
-    v->no_cacheable = 0;
-    v->not_found = 0;
-    v->data = (u_char *) &sin->sin_addr.s_addr;
+        v->len = sizeof(struct in6_addr);
+        v->valid = 1;
+        v->no_cacheable = 0;
+        v->not_found = 0;
+        v->data = (u_char *) &sin6->sin6_addr;
+
+        break;
+#endif
+
+    default: /* AF_INET */
+        sin = (struct sockaddr_in *) r->connection->sockaddr;
+
+        v->len = sizeof(in_addr_t);
+        v->valid = 1;
+        v->no_cacheable = 0;
+        v->not_found = 0;
+        v->data = (u_char *) &sin->sin_addr;
+
+        break;
+    }
 
     return NGX_OK;
 }
@@ -862,8 +882,11 @@ static ngx_int_t
 ngx_http_variable_remote_port(ngx_http_request_t *r,
     ngx_http_variable_value_t *v, uintptr_t data)
 {
-    ngx_uint_t           port;
-    struct sockaddr_in  *sin;
+    ngx_uint_t            port;
+    struct sockaddr_in   *sin;
+#if (NGX_HAVE_INET6)
+    struct sockaddr_in6  *sin6;
+#endif
 
     v->len = 0;
     v->valid = 1;
@@ -875,16 +898,23 @@ ngx_http_variable_remote_port(ngx_http_r
         return NGX_ERROR;
     }
 
-    /* AF_INET only */
+    switch (r->connection->sockaddr->sa_family) {
 
-    if (r->connection->sockaddr->sa_family == AF_INET) {
-        sin = (struct sockaddr_in *) r->connection->sockaddr;
+#if (NGX_HAVE_INET6)
+    case AF_INET6:
+        sin6 = (struct sockaddr_in6 *) r->connection->sockaddr;
+        port = ntohs(sin6->sin6_port);
+        break;
+#endif
 
+    default: /* AF_INET */
+        sin = (struct sockaddr_in *) r->connection->sockaddr;
         port = ntohs(sin->sin_port);
+        break;
+    }
 
-        if (port > 0 && port < 65536) {
-            v->len = ngx_sprintf(v->data, "%ui", port) - v->data;
-        }
+    if (port > 0 && port < 65536) {
+        v->len = ngx_sprintf(v->data, "%ui", port) - v->data;
     }
 
     return NGX_OK;
@@ -896,15 +926,21 @@ ngx_http_variable_server_addr(ngx_http_r
     ngx_http_variable_value_t *v, uintptr_t data)
 {
     ngx_str_t  s;
+    u_char     addr[NGX_SOCKADDR_STRLEN];
 
-    s.data = ngx_pnalloc(r->pool, NGX_INET_ADDRSTRLEN);
+    s.len = NGX_SOCKADDR_STRLEN;
+    s.data = addr;
+
+    if (ngx_http_server_addr(r, &s) != NGX_OK) {
+        return NGX_ERROR;
+    }
+
+    s.data = ngx_pnalloc(r->pool, s.len);
     if (s.data == NULL) {
         return NGX_ERROR;
     }
 
-    if (ngx_http_server_addr(r, &s) != NGX_OK) {
-        return NGX_ERROR;
-    }
+    ngx_memcpy(s.data, addr, s.len);
 
     v->len = s.len;
     v->valid = 1;
--- a/src/mail/ngx_mail_core_module.c
+++ b/src/mail/ngx_mail_core_module.c
@@ -305,6 +305,11 @@ ngx_mail_core_listen(ngx_conf_t *cf, ngx
         return NGX_CONF_ERROR;
     }
 
+    if (u.family != AF_INET) {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "listen supports IPv4 only");
+        return NGX_CONF_ERROR;
+    }
+
     cmcf = ngx_mail_conf_get_module_main_conf(cf, ngx_mail_core_module);
 
     imls = cmcf->listen.elts;