<div dir="ltr">Hello,<div><br></div><div style>We needed this feature in our company, I found that it is in milestones of version 1.5 but doesn't exist yet. So I've implemented it based in 1.3 code and merged in current 1.5 code. When I wrote this code I mostly cared about minimum intrusion into other parts of nginx.</div>
<div style><br></div><div style>IPv6 fallback logic is not a straightforward implementation of suggested by RFC. RFC states that IPv6 resolving have priority over IPv4, and it's not very good for Internet we have currently. With this patch you can specify priority, and in upstream and mail modules I've set IPv4 as preferred address family.</div>
<div style><br></div><div style>Patch is pretty big and I hope it'll not break mailing list or mail clients.</div><div style><br></div><div style><div>From b98c8cd3bd0bca9df88a8d6d660015a502b9727c Mon Sep 17 00:00:00 2001</div>
<div>From: Anton Kortunov <<a href="mailto:toshic.toshic@gmail.com">toshic.toshic@gmail.com</a>></div><div>Date: Fri, 14 Jun 2013 20:38:41 +0400</div><div>Subject: [PATCH] IPv6 resolver</div><div><br></div><div>---</div>
<div> src/core/ngx_inet.h                      |   10 +</div><div> src/core/ngx_resolver.c                  |  436 +++++++++++++++++++++++++-----</div><div> src/core/ngx_resolver.h                  |   23 ++-</div><div> src/http/ngx_http_upstream.c             |   16 +-</div>
<div> src/http/ngx_http_upstream.h             |    2 +-</div><div> src/http/ngx_http_upstream_round_robin.c |   49 +++-</div><div> src/mail/ngx_mail_smtp_handler.c         |   95 ++++++--</div><div> 7 files changed, 524 insertions(+), 107 deletions(-)</div>
<div><br></div><div>diff --git a/src/core/ngx_inet.h b/src/core/ngx_inet.h</div><div>index 6a5a368..077ed34 100644</div><div>--- a/src/core/ngx_inet.h</div><div>+++ b/src/core/ngx_inet.h</div><div>@@ -68,6 +68,16 @@ typedef struct {</div>
<div> </div><div> </div><div> typedef struct {</div><div>+    ngx_uint_t                family;</div><div>+    union {</div><div>+        in_addr_t             v4;</div><div>+#if (NGX_HAVE_INET6)</div><div>+        struct in6_addr       v6;</div>
<div>+#endif</div><div>+    } u;</div><div>+} ngx_ipaddr_t;</div><div>+</div><div>+typedef struct {</div><div>     struct sockaddr          *sockaddr;</div><div>     socklen_t                 socklen;</div><div>     ngx_str_t                 name;</div>
<div>diff --git a/src/core/ngx_resolver.c b/src/core/ngx_resolver.c</div><div>index d59d0c4..5953b9c 100644</div><div>--- a/src/core/ngx_resolver.c</div><div>+++ b/src/core/ngx_resolver.c</div><div>@@ -71,12 +71,12 @@ static void ngx_resolver_process_response(ngx_resolver_t *r, u_char *buf,</div>
<div>     size_t n);</div><div> static void ngx_resolver_process_a(ngx_resolver_t *r, u_char *buf, size_t n,</div><div>     ngx_uint_t ident, ngx_uint_t code, ngx_uint_t nan, ngx_uint_t ans);</div><div>-static void ngx_resolver_process_ptr(ngx_resolver_t *r, u_char *buf, size_t n,</div>
<div>+void ngx_resolver_process_ptr(ngx_resolver_t *r, u_char *buf, size_t n,</div><div>     ngx_uint_t ident, ngx_uint_t code, ngx_uint_t nan);</div><div> static ngx_resolver_node_t *ngx_resolver_lookup_name(ngx_resolver_t *r,</div>
<div>     ngx_str_t *name, uint32_t hash);</div><div> static ngx_resolver_node_t *ngx_resolver_lookup_addr(ngx_resolver_t *r,</div><div>-    in_addr_t addr);</div><div>+    ngx_ipaddr_t addr, uint32_t hash);</div><div> static void ngx_resolver_rbtree_insert_value(ngx_rbtree_node_t *temp,</div>
<div>     ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);</div><div> static ngx_int_t ngx_resolver_copy(ngx_resolver_t *r, ngx_str_t *name,</div><div>@@ -88,7 +88,7 @@ static void *ngx_resolver_calloc(ngx_resolver_t *r, size_t size);</div>
<div> static void ngx_resolver_free(ngx_resolver_t *r, void *p);</div><div> static void ngx_resolver_free_locked(ngx_resolver_t *r, void *p);</div><div> static void *ngx_resolver_dup(ngx_resolver_t *r, void *src, size_t size);</div>
<div>-static in_addr_t *ngx_resolver_rotate(ngx_resolver_t *r, in_addr_t *src,</div><div>+static ngx_ipaddr_t *ngx_resolver_rotate(ngx_resolver_t *r, ngx_ipaddr_t *src,</div><div>     ngx_uint_t n);</div><div> static u_char *ngx_resolver_log_error(ngx_log_t *log, u_char *buf, size_t len);</div>
<div> </div><div>@@ -270,13 +270,27 @@ ngx_resolver_cleanup_tree(ngx_resolver_t *r, ngx_rbtree_t *tree)</div><div> ngx_resolver_ctx_t *</div><div> ngx_resolve_start(ngx_resolver_t *r, ngx_resolver_ctx_t *temp)</div><div> {</div>
<div>-    in_addr_t            addr;</div><div>+    ngx_ipaddr_t         addr;</div><div>     ngx_resolver_ctx_t  *ctx;</div><div> </div><div>     if (temp) {</div><div>-        addr = ngx_inet_addr(temp->name.data, temp->name.len);</div>
<div>+        addr.family = 0;</div><div> </div><div>-        if (addr != INADDR_NONE) {</div><div>+</div><div>+        addr.u.v4 = ngx_inet_addr(temp->name.data, temp->name.len);</div><div>+</div><div>+        if (addr.u.v4 != INADDR_NONE) {</div>
<div>+</div><div>+            addr.family = AF_INET;</div><div>+</div><div>+#if (NGX_HAVE_INET6)</div><div>+        } else if (ngx_inet6_addr(temp->name.data, temp->name.len, addr.u.v6.s6_addr) == NGX_OK) {</div><div>
+</div><div>+            addr.family = AF_INET6;</div><div>+#endif</div><div>+        }</div><div>+        </div><div>+        if (addr.family) {</div><div>             temp->resolver = r;</div><div>             temp->state = NGX_OK;</div>
<div>             temp->naddrs = 1;</div><div>@@ -417,7 +431,7 @@ static ngx_int_t</div><div> ngx_resolve_name_locked(ngx_resolver_t *r, ngx_resolver_ctx_t *ctx)</div><div> {</div><div>     uint32_t              hash;</div>
<div>-    in_addr_t             addr, *addrs;</div><div>+    ngx_ipaddr_t          addr, *addrs;</div><div>     ngx_int_t             rc;</div><div>     ngx_uint_t            naddrs;</div><div>     ngx_resolver_ctx_t   *next;</div>
<div>@@ -429,7 +443,11 @@ ngx_resolve_name_locked(ngx_resolver_t *r, ngx_resolver_ctx_t *ctx)</div><div> </div><div>     if (rn) {</div><div> </div><div>-        if (rn->valid >= ngx_time()) {</div><div>+        if (rn->valid >= ngx_time()</div>
<div>+#if (NGX_HAVE_INET6)</div><div>+ && rn->qtype != NGX_RESOLVE_RETRY</div><div>+#endif</div><div>+                                    ) {</div><div> </div><div>             ngx_log_debug0(NGX_LOG_DEBUG_CORE, r->log, 0, "resolve cached");</div>
<div> </div><div>@@ -446,7 +464,6 @@ ngx_resolve_name_locked(ngx_resolver_t *r, ngx_resolver_ctx_t *ctx)</div><div>                 /* NGX_RESOLVE_A answer */</div><div> </div><div>                 if (naddrs != 1) {</div>
<div>-                    addr = 0;</div><div>                     addrs = ngx_resolver_rotate(r, rn->u.addrs, naddrs);</div><div>                     if (addrs == NULL) {</div><div>                         return NGX_ERROR;</div>
<div>@@ -506,6 +523,8 @@ ngx_resolve_name_locked(ngx_resolver_t *r, ngx_resolver_ctx_t *ctx)</div><div>             } while (ctx);</div><div> </div><div>             return NGX_OK;</div><div>+        } else {</div><div>+            rn->qtype = ctx->type;</div>
<div>         }</div><div> </div><div>         if (rn->waiting) {</div><div>@@ -552,6 +571,7 @@ ngx_resolve_name_locked(ngx_resolver_t *r, ngx_resolver_ctx_t *ctx)</div><div>         rn->node.key = hash;</div><div>         rn->nlen = (u_short) ctx->name.len;</div>
<div>         rn->query = NULL;</div><div>+        rn->qtype = ctx->type;</div><div> </div><div>         ngx_rbtree_insert(&r->name_rbtree, &rn->node);</div><div>     }</div><div>@@ -629,17 +649,40 @@ failed:</div>
<div> ngx_int_t</div><div> ngx_resolve_addr(ngx_resolver_ctx_t *ctx)</div><div> {</div><div>+    uint32_t              hash;</div><div>     u_char               *name;</div><div>     ngx_resolver_t       *r;</div><div>     ngx_resolver_node_t  *rn;</div>
<div> </div><div>     r = ctx->resolver;</div><div>+    rn = NULL;</div><div>+</div><div>+    hash = ctx->addr.family;</div><div> </div><div>-    ctx->addr = ntohl(ctx->addr);</div><div>+    switch(ctx->addr.family) {</div>
<div>+</div><div>+    case AF_INET:</div><div>+        ctx->addr.u.v4 = ntohl(ctx->addr.u.v4);</div><div>+        ngx_crc32_update(&hash, (u_char *)&ctx->addr.u.v4, sizeof(in_addr_t));</div><div>+ngx_log_debug3(NGX_LOG_DEBUG_CORE, r->log, 0,</div>
<div>+               "resolve addr hash: %xd, addr:%xd, family: %d", hash, ctx->addr.u.v4, ctx->addr.family);</div><div>+        break;</div><div>+</div><div>+#if (NGX_HAVE_INET6)</div><div>+    case AF_INET6:</div>
<div>+        ngx_crc32_update(&hash, (u_char *)&ctx->addr.u.v6, sizeof(struct in6_addr));</div><div>+        break;</div><div>+#endif</div><div>+</div><div>+    default:</div><div>+        goto failed;</div><div>
+    }</div><div> </div><div>     /* lock addr mutex */</div><div> </div><div>-    rn = ngx_resolver_lookup_addr(r, ctx->addr);</div><div>+    rn = ngx_resolver_lookup_addr(r, ctx->addr, hash);</div><div>+    ngx_log_error(r->log_level, r->log, 0,</div>
<div>+                  "resolve: in resolve_addr searching, hash = %xd, rn = %p", hash, rn);</div><div> </div><div>     if (rn) {</div><div> </div><div>@@ -694,8 +737,10 @@ ngx_resolve_addr(ngx_resolver_ctx_t *ctx)</div>
<div>             goto failed;</div><div>         }</div><div> </div><div>-        rn->node.key = ctx->addr;</div><div>+        rn->node.key = hash;</div><div>         rn->query = NULL;</div><div>+        rn->qtype = ctx->type;</div>
<div>+        rn->u.addr = ctx->addr;</div><div> </div><div>         ngx_rbtree_insert(&r->addr_rbtree, &rn->node);</div><div>     }</div><div>@@ -768,10 +813,11 @@ failed:</div><div> void</div><div> ngx_resolve_addr_done(ngx_resolver_ctx_t *ctx)</div>
<div> {</div><div>-    in_addr_t             addr;</div><div>+    uint32_t              hash;</div><div>     ngx_resolver_t       *r;</div><div>     ngx_resolver_ctx_t   *w, **p;</div><div>     ngx_resolver_node_t  *rn;</div>
<div>+    char                  text[NGX_SOCKADDR_STRLEN];</div><div> </div><div>     r = ctx->resolver;</div><div> </div><div>@@ -786,7 +832,25 @@ ngx_resolve_addr_done(ngx_resolver_ctx_t *ctx)</div><div> </div><div>     if (ctx->state == NGX_AGAIN || ctx->state == NGX_RESOLVE_TIMEDOUT) {</div>
<div> </div><div>-        rn = ngx_resolver_lookup_addr(r, ctx->addr);</div><div>+        hash = ctx->addr.family;</div><div>+</div><div>+        switch(ctx->addr.family) {</div><div>+</div><div>+        case AF_INET:</div>
<div>+            ngx_crc32_update(&hash, (u_char *)&ctx->addr.u.v4, sizeof(in_addr_t));</div><div>+            break;</div><div>+</div><div>+#if (NGX_HAVE_INET6)</div><div>+        case AF_INET6:</div><div>+            ngx_crc32_update(&hash, (u_char *)&ctx->addr.u.v6, sizeof(struct in6_addr));</div>
<div>+            break;</div><div>+#endif</div><div>+</div><div>+        default:</div><div>+            goto failed;</div><div>+        }</div><div>+</div><div>+        rn = ngx_resolver_lookup_addr(r, ctx->addr, hash);</div>
<div> </div><div>         if (rn) {</div><div>             p = &rn->waiting;</div><div>@@ -804,12 +868,13 @@ ngx_resolve_addr_done(ngx_resolver_ctx_t *ctx)</div><div>             }</div><div>         }</div><div> </div>
<div>-        addr = ntohl(ctx->addr);</div><div>+failed:</div><div>+</div><div>+        //addr = ntohl(ctx->addr);</div><div>+        inet_ntop(ctx->addr.family, &ctx->addr.u, text, NGX_SOCKADDR_STRLEN);</div>
<div> </div><div>         ngx_log_error(NGX_LOG_ALERT, r->log, 0,</div><div>-                      "could not cancel %ud.%ud.%ud.%ud resolving",</div><div>-                      (addr >> 24) & 0xff, (addr >> 16) & 0xff,</div>
<div>-                      (addr >> 8) & 0xff, addr & 0xff);</div><div>+                      "could not cancel %s resolving", text);</div><div>     }</div><div> </div><div> done:</div><div>@@ -1130,6 +1195,9 @@ found:</div>
<div>     switch (qtype) {</div><div> </div><div>     case NGX_RESOLVE_A:</div><div>+#if (NGX_HAVE_INET6)</div><div>+    case NGX_RESOLVE_AAAA:</div><div>+#endif</div><div> </div><div>         ngx_resolver_process_a(r, buf, n, ident, code, nan,</div>
<div>                                i + sizeof(ngx_resolver_qs_t));</div><div>@@ -1178,7 +1246,7 @@ ngx_resolver_process_a(ngx_resolver_t *r, u_char *buf, size_t last,</div><div>     size_t                len;</div><div>
     int32_t               ttl;</div><div>     uint32_t              hash;</div><div>-    in_addr_t             addr, *addrs;</div><div>+    ngx_ipaddr_t          addr, *addrs;</div><div>     ngx_str_t             name;</div>
<div>     ngx_uint_t            qtype, qident, naddrs, a, i, n, start;</div><div>     ngx_resolver_an_t    *an;</div><div>@@ -1212,12 +1280,55 @@ ngx_resolver_process_a(ngx_resolver_t *r, u_char *buf, size_t last,</div><div>
         goto failed;</div><div>     }</div><div> </div><div>-    ngx_resolver_free(r, name.data);</div><div>-</div><div>     if (code == 0 && nan == 0) {</div><div>+</div><div>+#if (NGX_HAVE_INET6)</div><div>+<span class="" style="white-space:pre">     </span>// If it was required dual type v4|v6 resolv create one more request</div>
<div>+<span class="" style="white-space:pre">   </span>if (rn->qtype == NGX_RESOLVE_A_AAAA</div><div>+<span class="" style="white-space:pre">            </span>|| rn->qtype == NGX_RESOLVE_AAAA_A) {</div><div>+</div><div>+            ngx_queue_remove(&rn->queue);</div>
<div>+</div><div>+            rn->valid = ngx_time() + (r->valid ? r->valid : ttl);</div><div>+            rn->expire = ngx_time() + r->expire;</div><div>+</div><div>+            ngx_queue_insert_head(&r->name_expire_queue, &rn->queue);</div>
<div>+</div><div>+            ctx = rn->waiting;</div><div>+            rn->waiting = NULL;</div><div>+</div><div>+            if (ctx) {</div><div>+                ctx->name = name;</div><div>+</div><div>+                switch (rn->qtype) {</div>
<div>+</div><div>+                case NGX_RESOLVE_A_AAAA:</div><div>+                    ctx->type = NGX_RESOLVE_AAAA;</div><div>+                    break;</div><div>+</div><div>+                case NGX_RESOLVE_AAAA_A:</div>
<div>+                    ctx->type = NGX_RESOLVE_A;</div><div>+                    break;</div><div>+                }</div><div>+</div><div>+                ngx_log_debug2(NGX_LOG_DEBUG_CORE, r->log, 0,</div><div>
+                              "restarting request for name %V, with type %ud",</div><div>+                              &name, ctx->type);</div><div>+</div><div>+                rn->qtype = NGX_RESOLVE_RETRY;</div>
<div>+</div><div>+                (void) ngx_resolve_name_locked(r, ctx);</div><div>+            }</div><div>+</div><div>+            return;</div><div>+        }</div><div>+#endif</div><div>+</div><div>         code = 3; /* NXDOMAIN */</div>
<div>     }</div><div> </div><div>+    ngx_resolver_free(r, name.data);</div><div>+</div><div>     if (code) {</div><div>         next = rn->waiting;</div><div>         rn->waiting = NULL;</div><div>@@ -1243,7 +1354,7 @@ ngx_resolver_process_a(ngx_resolver_t *r, u_char *buf, size_t last,</div>
<div> </div><div>     i = ans;</div><div>     naddrs = 0;</div><div>-    addr = 0;</div><div>+    addr.family = 0;</div><div>     addrs = NULL;</div><div>     cname = NULL;</div><div>     qtype = 0;</div><div>@@ -1302,13 +1413,30 @@ ngx_resolver_process_a(ngx_resolver_t *r, u_char *buf, size_t last,</div>
<div>                 goto short_response;</div><div>             }</div><div> </div><div>-            addr = htonl((buf[i] << 24) + (buf[i + 1] << 16)</div><div>+            addr.family = AF_INET;</div><div>+            addr.u.v4 = htonl((buf[i] << 24) + (buf[i + 1] << 16)</div>
<div>                          + (buf[i + 2] << 8) + (buf[i + 3]));</div><div> </div><div>             naddrs++;</div><div> </div><div>             i += len;</div><div> </div><div>+#if (NGX_HAVE_INET6)</div><div>+        } else if (qtype == NGX_RESOLVE_AAAA) {</div>
<div>+</div><div>+            i += sizeof(ngx_resolver_an_t);</div><div>+</div><div>+            if (i + len > last) {</div><div>+                goto short_response;</div><div>+            }</div><div>+</div><div>+            addr.family = AF_INET6;</div>
<div>+            ngx_memcpy(&addr.u.v6.s6_addr, &buf[i], 16);</div><div>+</div><div>+            naddrs++;</div><div>+</div><div>+            i += len;</div><div>+#endif</div><div>         } else if (qtype == NGX_RESOLVE_CNAME) {</div>
<div>             cname = &buf[i] + sizeof(ngx_resolver_an_t);</div><div>             i += sizeof(ngx_resolver_an_t) + len;</div><div>@@ -1333,7 +1461,7 @@ ngx_resolver_process_a(ngx_resolver_t *r, u_char *buf, size_t last,</div>
<div> </div><div>         } else {</div><div> </div><div>-            addrs = ngx_resolver_alloc(r, naddrs * sizeof(in_addr_t));</div><div>+            addrs = ngx_resolver_alloc(r, naddrs * sizeof(ngx_ipaddr_t));</div><div>
             if (addrs == NULL) {</div><div>                 return;</div><div>             }</div><div>@@ -1369,12 +1497,23 @@ ngx_resolver_process_a(ngx_resolver_t *r, u_char *buf, size_t last,</div><div> </div><div>                 if (qtype == NGX_RESOLVE_A) {</div>
<div> </div><div>-                    addrs[n++] = htonl((buf[i] << 24) + (buf[i + 1] << 16)</div><div>+                    addrs[n].family = AF_INET;</div><div>+                    addrs[n++].u.v4 = htonl((buf[i] << 24) + (buf[i + 1] << 16)</div>
<div>                                        + (buf[i + 2] << 8) + (buf[i + 3]));</div><div> </div><div>                     if (n == naddrs) {</div><div>                         break;</div><div>                     }</div>
<div>+#if (NGX_HAVE_INET6)</div><div>+                } else if (qtype == NGX_RESOLVE_AAAA) {</div><div>+</div><div>+                    addrs[n].family = AF_INET6;</div><div>+                    ngx_memcpy(&addrs[n++].u.v6.s6_addr, &buf[i], 16);</div>
<div>+</div><div>+                    if (n == naddrs) {</div><div>+                        break;</div><div>+                    }</div><div>+#endif</div><div>                 }</div><div> </div><div>                 i += len;</div>
<div>@@ -1383,7 +1522,7 @@ ngx_resolver_process_a(ngx_resolver_t *r, u_char *buf, size_t last,</div><div>             rn->u.addrs = addrs;</div><div> </div><div>             addrs = ngx_resolver_dup(r, rn->u.addrs,</div>
<div>-                                     naddrs * sizeof(in_addr_t));</div><div>+                                     naddrs * sizeof(ngx_ipaddr_t));</div><div>             if (addrs == NULL) {</div><div>                 return;</div>
<div>             }</div><div>@@ -1486,13 +1625,14 @@ failed:</div><div> }</div><div> </div><div> </div><div>-static void</div><div>+void</div><div> ngx_resolver_process_ptr(ngx_resolver_t *r, u_char *buf, size_t n,</div>
<div>     ngx_uint_t ident, ngx_uint_t code, ngx_uint_t nan)</div><div> {</div><div>-    char                 *err;</div><div>+    char                 *err = NULL;</div><div>+    uint32_t              hash = 0;</div><div>
     size_t                len;</div><div>-    in_addr_t             addr;</div><div>+    ngx_ipaddr_t          addr;</div><div>     int32_t               ttl;</div><div>     ngx_int_t             digit;</div><div>     ngx_str_t             name;</div>
<div>@@ -1500,12 +1640,16 @@ ngx_resolver_process_ptr(ngx_resolver_t *r, u_char *buf, size_t n,</div><div>     ngx_resolver_an_t    *an;</div><div>     ngx_resolver_ctx_t   *ctx, *next;</div><div>     ngx_resolver_node_t  *rn;</div>
<div>+    char                  text[NGX_SOCKADDR_STRLEN];</div><div> </div><div>     if (ngx_resolver_copy(r, NULL, buf, &buf[12], &buf[n]) != NGX_OK) {</div><div>         goto invalid_in_addr_arpa;</div><div>     }</div>
<div> </div><div>-    addr = 0;</div><div>+    ngx_memzero(&addr, sizeof(ngx_ipaddr_t));</div><div>+</div><div>+    /* Try to parse request as in-addr.arpa */</div><div>+    addr.family = AF_INET;</div><div>     i = 12;</div>
<div> </div><div>     for (mask = 0; mask < 32; mask += 8) {</div><div>@@ -1516,7 +1660,7 @@ ngx_resolver_process_ptr(ngx_resolver_t *r, u_char *buf, size_t n,</div><div>             goto invalid_in_addr_arpa;</div><div>
         }</div><div> </div><div>-        addr += digit << mask;</div><div>+        addr.u.v4 += digit << mask;</div><div>         i += len;</div><div>     }</div><div> </div><div>@@ -1524,15 +1668,79 @@ ngx_resolver_process_ptr(ngx_resolver_t *r, u_char *buf, size_t n,</div>
<div>         goto invalid_in_addr_arpa;</div><div>     }</div><div> </div><div>+    i += sizeof("\7in-addr\4arpa") + sizeof(ngx_resolver_qs_t);</div><div>+</div><div>+    goto found;</div><div>+</div><div>+invalid_in_addr_arpa:</div>
<div>+</div><div>+#if (NGX_HAVE_INET6)</div><div>+    /* Try to parse request as ip6.arpa */</div><div>+    addr.family = AF_INET6;</div><div>+    i = 12;</div><div>+</div><div>+    for (len = 15; len < 16; len--) {</div>
<div>+        if (buf[i++] != 1)</div><div>+            goto invalid_arpa;</div><div>+</div><div>+        digit = ngx_hextoi(&buf[i++], 1);</div><div>+        if (digit == NGX_ERROR || digit > 16) {</div><div>+            goto invalid_arpa;</div>
<div>+        }</div><div>+</div><div>+        addr.u.v6.s6_addr[len] = digit;</div><div>+</div><div>+        if (buf[i++] != 1)</div><div>+            goto invalid_arpa;</div><div>+</div><div>+</div><div>+        digit = ngx_hextoi(&buf[i++], 1);</div>
<div>+        if (digit == NGX_ERROR || digit > 16) {</div><div>+            goto invalid_arpa;</div><div>+        }</div><div>+</div><div>+        addr.u.v6.s6_addr[len] += digit << 4;</div><div>+    }</div><div>
+</div><div>+    if (ngx_strcmp(&buf[i], "\3ip6\4arpa") != 0) {</div><div>+        goto invalid_arpa;</div><div>+    }</div><div>+</div><div>+    i += sizeof("\3ip6\4arpa") + sizeof(ngx_resolver_qs_t);</div>
<div>+</div><div>+#else /* NGX_HAVE_INET6 */</div><div>+    goto invalid_arpa;</div><div>+#endif</div><div>+</div><div>+found:</div><div>+</div><div>     /* lock addr mutex */</div><div> </div><div>-    rn = ngx_resolver_lookup_addr(r, addr);</div>
<div>+    hash = addr.family;</div><div>+</div><div>+    switch(addr.family) {</div><div>+</div><div>+    case AF_INET:</div><div>+        ngx_crc32_update(&hash, (u_char *)&addr.u.v4, sizeof(in_addr_t));</div><div>
+        break;</div><div>+</div><div>+#if (NGX_HAVE_INET6)</div><div>+    case AF_INET6:</div><div>+        ngx_crc32_update(&hash, (u_char *)&addr.u.v6, sizeof(struct in6_addr));</div><div>+        break;</div><div>
+#endif</div><div>+</div><div>+    default:</div><div>+        goto invalid;</div><div>+    }</div><div>+</div><div>+    rn = ngx_resolver_lookup_addr(r, addr, hash);</div><div>+</div><div>+    inet_ntop(addr.family, &addr.u, text, NGX_SOCKADDR_STRLEN);</div>
<div> </div><div>     if (rn == NULL || rn->query == NULL) {</div><div>         ngx_log_error(r->log_level, r->log, 0,</div><div>-                      "unexpected response for %ud.%ud.%ud.%ud",</div><div>
-                      (addr >> 24) & 0xff, (addr >> 16) & 0xff,</div><div>-                      (addr >> 8) & 0xff, addr & 0xff);</div><div>+                      "unexpected response for %s", text);</div>
<div>         goto failed;</div><div>     }</div><div> </div><div>@@ -1540,12 +1748,15 @@ ngx_resolver_process_ptr(ngx_resolver_t *r, u_char *buf, size_t n,</div><div> </div><div>     if (ident != qident) {</div><div>         ngx_log_error(r->log_level, r->log, 0,</div>
<div>-                    "wrong ident %ui response for %ud.%ud.%ud.%ud, expect %ui",</div><div>-                    ident, (addr >> 24) & 0xff, (addr >> 16) & 0xff,</div><div>-                    (addr >> 8) & 0xff, addr & 0xff, qident);</div>
<div>+                    "wrong ident %ui response for %s, expect %ui",</div><div>+                    ident, text, qident);</div><div>         goto failed;</div><div>     }</div><div> </div><div>+    ngx_log_error(r->log_level, r->log, 0,</div>
<div>+                "code: %d, nan: %d",</div><div>+                code, nan);</div><div>+</div><div>     if (code == 0 && nan == 0) {</div><div>         code = 3; /* NXDOMAIN */</div><div>     }</div>
<div>@@ -1573,8 +1784,6 @@ ngx_resolver_process_ptr(ngx_resolver_t *r, u_char *buf, size_t n,</div><div>         return;</div><div>     }</div><div> </div><div>-    i += sizeof("\7in-addr\4arpa") + sizeof(ngx_resolver_qs_t);</div>
<div>-</div><div>     if (i + 2 + sizeof(ngx_resolver_an_t) > (ngx_uint_t) n) {</div><div>         goto short_response;</div><div>     }</div><div>@@ -1654,10 +1863,10 @@ ngx_resolver_process_ptr(ngx_resolver_t *r, u_char *buf, size_t n,</div>
<div> </div><div>     return;</div><div> </div><div>-invalid_in_addr_arpa:</div><div>+invalid_arpa:</div><div> </div><div>     ngx_log_error(r->log_level, r->log, 0,</div><div>-                  "invalid in-addr.arpa name in DNS response");</div>
<div>+                  "invalid in-addr.arpa or ip6.arpa name in DNS response");</div><div>     return;</div><div> </div><div> short_response:</div><div>@@ -1722,28 +1931,54 @@ ngx_resolver_lookup_name(ngx_resolver_t *r, ngx_str_t *name, uint32_t hash)</div>
<div> </div><div> </div><div> static ngx_resolver_node_t *</div><div>-ngx_resolver_lookup_addr(ngx_resolver_t *r, in_addr_t addr)</div><div>+ngx_resolver_lookup_addr(ngx_resolver_t *r, ngx_ipaddr_t addr, uint32_t hash)</div>
<div> {</div><div>+    ngx_int_t             rc;</div><div>     ngx_rbtree_node_t  *node, *sentinel;</div><div>+    ngx_resolver_node_t  *rn;</div><div> </div><div>     node = r->addr_rbtree.root;</div><div>     sentinel = r->addr_rbtree.sentinel;</div>
<div> </div><div>     while (node != sentinel) {</div><div> </div><div>-        if (addr < node->key) {</div><div>+        if (hash < node->key) {</div><div>             node = node->left;</div><div>             continue;</div>
<div>         }</div><div> </div><div>-        if (addr > node->key) {</div><div>+        if (hash > node->key) {</div><div>             node = node->right;</div><div>             continue;</div><div>         }</div>
<div> </div><div>-        /* addr == node->key */</div><div>+        /* hash == node->key */</div><div>+</div><div>+        rn = (ngx_resolver_node_t *) node;</div><div>+</div><div>+        rc = addr.family - rn->u.addr.family;</div>
<div>+</div><div>+        if (rc == 0) {</div><div>+</div><div>+            switch (addr.family) {</div><div>+            case AF_INET:</div><div>+                rc = ngx_memn2cmp((u_char *)&addr.u.v4, (u_char *)&rn->u.addr.u.v4, sizeof(in_addr_t), sizeof(in_addr_t));</div>
<div>+                break;</div><div>+</div><div>+#if (NGX_HAVE_INET6)</div><div>+            case AF_INET6:</div><div>+                rc = ngx_memn2cmp((u_char *)&addr.u.v6, (u_char *)&rn->u.addr.u.v6, sizeof(struct in6_addr), sizeof(struct in6_addr));</div>
<div>+                break;</div><div>+#endif</div><div>+            }</div><div>+</div><div>+            if (rc == 0) {</div><div>+                return rn;</div><div>+            }</div><div>+</div><div>+        }</div>
<div> </div><div>-        return (ngx_resolver_node_t *) node;</div><div>+        node = (rc < 0) ? node->left : node->right;</div><div>     }</div><div> </div><div>     /* not found */</div><div>@@ -1758,6 +1993,7 @@ ngx_resolver_rbtree_insert_value(ngx_rbtree_node_t *temp,</div>
<div> {</div><div>     ngx_rbtree_node_t    **p;</div><div>     ngx_resolver_node_t   *rn, *rn_temp;</div><div>+    ngx_int_t              rc;</div><div> </div><div>     for ( ;; ) {</div><div> </div><div>@@ -1774,8 +2010,29 @@ ngx_resolver_rbtree_insert_value(ngx_rbtree_node_t *temp,</div>
<div>             rn = (ngx_resolver_node_t *) node;</div><div>             rn_temp = (ngx_resolver_node_t *) temp;</div><div> </div><div>-            p = (ngx_memn2cmp(rn->name, rn_temp->name, rn->nlen, rn_temp->nlen)</div>
<div>-                 < 0) ? &temp->left : &temp->right;</div><div>+            if (rn->qtype == NGX_RESOLVE_PTR) {</div><div>+                rc = rn->u.addr.family - rn_temp->u.addr.family;</div>
<div>+</div><div>+                if (rc == 0) {</div><div>+</div><div>+                    switch (rn->u.addr.family) {</div><div>+                    case AF_INET:</div><div>+                        rc = ngx_memn2cmp((u_char *)&rn->u.addr.u.v4, (u_char *)&rn_temp->u.addr.u.v4, sizeof(in_addr_t), sizeof(in_addr_t));</div>
<div>+                        break;</div><div>+</div><div>+        #if (NGX_HAVE_INET6)</div><div>+                    case AF_INET6:</div><div>+                        rc = ngx_memn2cmp((u_char *)&rn->u.addr.u.v6, (u_char *)&rn_temp->u.addr.u.v6, sizeof(struct in6_addr), sizeof(struct in6_addr));</div>
<div>+                        break;</div><div>+        #endif</div><div>+                    }</div><div>+                }</div><div>+</div><div>+            } else {</div><div>+                rc = ngx_memn2cmp(rn->name, rn_temp->name, rn->nlen, rn_temp->nlen);</div>
<div>+            }</div><div>+</div><div>+            p = (rc < 0) ? &temp->left : &temp->right;</div><div>         }</div><div> </div><div>         if (*p == sentinel) {</div><div>@@ -1838,7 +2095,20 @@ ngx_resolver_create_name_query(ngx_resolver_node_t *rn, ngx_resolver_ctx_t *ctx)</div>
<div>     qs = (ngx_resolver_qs_t *) p;</div><div> </div><div>     /* query type */</div><div>-    qs->type_hi = 0; qs->type_lo = (u_char) ctx->type;</div><div>+    qs->type_hi = 0; qs->type_lo = (u_char) rn->qtype;</div>
<div>+</div><div>+#if (NGX_HAVE_INET6)</div><div>+    switch (rn->qtype) {</div><div>+</div><div>+    case NGX_RESOLVE_A_AAAA:</div><div>+        qs->type_lo = NGX_RESOLVE_A;</div><div>+        break;</div><div>+</div>
<div>+    case NGX_RESOLVE_AAAA_A:</div><div>+        qs->type_lo = NGX_RESOLVE_AAAA;</div><div>+        break;</div><div>+    }</div><div>+#endif</div><div> </div><div>     /* IP query class */</div><div>     qs->class_hi = 0; qs->class_lo = 1;</div>
<div>@@ -1880,8 +2150,6 @@ ngx_resolver_create_name_query(ngx_resolver_node_t *rn, ngx_resolver_ctx_t *ctx)</div><div> }</div><div> </div><div> </div><div>-/* AF_INET only */</div><div>-</div><div> static ngx_int_t</div><div>
 ngx_resolver_create_addr_query(ngx_resolver_node_t *rn, ngx_resolver_ctx_t *ctx)</div><div> {</div><div>@@ -1892,7 +2160,7 @@ ngx_resolver_create_addr_query(ngx_resolver_node_t *rn, ngx_resolver_ctx_t *ctx)</div><div>     ngx_resolver_query_t  *query;</div>
<div> </div><div>     len = sizeof(ngx_resolver_query_t)</div><div>-          + sizeof(".255.255.255.255.in-addr.arpa.") - 1</div><div>+          + NGX_PTR_QUERY_LEN</div><div>           + sizeof(ngx_resolver_qs_t);</div>
<div> </div><div>     p = ngx_resolver_alloc(ctx->resolver, len);</div><div>@@ -1919,18 +2187,50 @@ ngx_resolver_create_addr_query(ngx_resolver_node_t *rn, ngx_resolver_ctx_t *ctx)</div><div> </div><div>     p += sizeof(ngx_resolver_query_t);</div>
<div> </div><div>-    for (n = 0; n < 32; n += 8) {</div><div>-        d = ngx_sprintf(&p[1], "%ud", (ctx->addr >> n) & 0xff);</div><div>-        *p = (u_char) (d - &p[1]);</div><div>-        p = d;</div>
<div>+    switch (ctx->addr.family) {</div><div>+</div><div>+    case AF_INET:</div><div>+        for (n = 0; n < 32; n += 8) {</div><div>+            d = ngx_sprintf(&p[1], "%ud", (ctx->addr.u.v4 >> n) & 0xff);</div>
<div>+            *p = (u_char) (d - &p[1]);</div><div>+            p = d;</div><div>+        }</div><div>+</div><div>+        /* query type "PTR", IP query class */</div><div>+        ngx_memcpy(p, "\7in-addr\4arpa\0\0\14\0\1", 18);</div>
<div>+</div><div>+        rn->qlen = (u_short)</div><div>+                      (p + sizeof("\7in-addr\4arpa") + sizeof(ngx_resolver_qs_t)</div><div>+                       - rn->query);</div><div>+</div><div>
+        break;</div><div>+</div><div>+#if (NGX_HAVE_INET6)</div><div>+    case AF_INET6:</div><div>+        for (n = 15; n >= 0; n--) {</div><div>+            p = ngx_sprintf(p, "\1%xd\1%xd", </div><div>+                            (ctx->addr.u.v6.s6_addr[n]) & 0xf,</div>
<div>+                            (ctx->addr.u.v6.s6_addr[n] >> 4) & 0xf);</div><div>+</div><div>+        }</div><div>+</div><div>+        /* query type "PTR", IP query class */</div><div>+        ngx_memcpy(p, "\3ip6\4arpa\0\0\14\0\1", 18);</div>
<div>+</div><div>+        rn->qlen = (u_short)</div><div>+                      (p + sizeof("\3ip6\4arpa") + sizeof(ngx_resolver_qs_t)</div><div>+                       - rn->query);</div><div>+</div><div>
+        break;</div><div>+#endif</div><div>+</div><div>+    default:</div><div>+        return NGX_ERROR;</div><div>     }</div><div> </div><div>-    /* query type "PTR", IP query class */</div><div>-    ngx_memcpy(p, "\7in-addr\4arpa\0\0\14\0\1", 18);</div>
<div>+ngx_log_debug2(NGX_LOG_DEBUG_CORE, ctx->resolver->log, 0,</div><div>+               "resolve: query %s, ident %i", (rn->query+12), ident & 0xffff);</div><div> </div><div>-    rn->qlen = (u_short)</div>
<div>-                  (p + sizeof("\7in-addr\4arpa") + sizeof(ngx_resolver_qs_t)</div><div>-                   - rn->query);</div><div> </div><div>     return NGX_OK;</div><div> }</div><div>@@ -2136,13 +2436,13 @@ ngx_resolver_dup(ngx_resolver_t *r, void *src, size_t size)</div>
<div> }</div><div> </div><div> </div><div>-static in_addr_t *</div><div>-ngx_resolver_rotate(ngx_resolver_t *r, in_addr_t *src, ngx_uint_t n)</div><div>+static ngx_ipaddr_t *</div><div>+ngx_resolver_rotate(ngx_resolver_t *r, ngx_ipaddr_t *src, ngx_uint_t n)</div>
<div> {</div><div>     void        *dst, *p;</div><div>     ngx_uint_t   j;</div><div> </div><div>-    dst = ngx_resolver_alloc(r, n * sizeof(in_addr_t));</div><div>+    dst = ngx_resolver_alloc(r, n * sizeof(ngx_ipaddr_t));</div>
<div> </div><div>     if (dst == NULL) {</div><div>         return dst;</div><div>@@ -2151,12 +2451,12 @@ ngx_resolver_rotate(ngx_resolver_t *r, in_addr_t *src, ngx_uint_t n)</div><div>     j = ngx_random() % n;</div><div>
 </div><div>     if (j == 0) {</div><div>-        ngx_memcpy(dst, src, n * sizeof(in_addr_t));</div><div>+        ngx_memcpy(dst, src, n * sizeof(ngx_ipaddr_t));</div><div>         return dst;</div><div>     }</div><div> </div>
<div>-    p = ngx_cpymem(dst, &src[j], (n - j) * sizeof(in_addr_t));</div><div>-    ngx_memcpy(p, src, j * sizeof(in_addr_t));</div><div>+    p = ngx_cpymem(dst, &src[j], (n - j) * sizeof(ngx_ipaddr_t));</div><div>
+    ngx_memcpy(p, src, j * sizeof(ngx_ipaddr_t));</div><div> </div><div>     return dst;</div><div> }</div><div>diff --git a/src/core/ngx_resolver.h b/src/core/ngx_resolver.h</div><div>index ae34ca5..a45b244 100644</div>
<div>--- a/src/core/ngx_resolver.h</div><div>+++ b/src/core/ngx_resolver.h</div><div>@@ -20,6 +20,15 @@</div><div> #define NGX_RESOLVE_TXT       16</div><div> #define NGX_RESOLVE_DNAME     39</div><div> </div><div>+#if (NGX_HAVE_INET6)</div>
<div>+</div><div>+#define NGX_RESOLVE_AAAA      28</div><div>+#define NGX_RESOLVE_A_AAAA    1000</div><div>+#define NGX_RESOLVE_AAAA_A    1001</div><div>+#define NGX_RESOLVE_RETRY     1002</div><div>+</div><div>+#endif</div>
<div>+</div><div> #define NGX_RESOLVE_FORMERR   1</div><div> #define NGX_RESOLVE_SERVFAIL  2</div><div> #define NGX_RESOLVE_NXDOMAIN  3</div><div>@@ -32,6 +41,11 @@</div><div> </div><div> #define NGX_RESOLVER_MAX_RECURSION    50</div>
<div> </div><div>+#if (NGX_HAVE_INET6)</div><div>+#define NGX_PTR_QUERY_LEN   (sizeof(".f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.ip6.arpa.") - 1)</div><div>+#else</div><div>+#define NGX_PTR_QUERY_LEN   (sizeof(".255.255.255.255.in-addr.arpa.") - 1)</div>
<div>+#endif</div><div> </div><div> typedef struct {</div><div>     ngx_connection_t         *connection;</div><div>@@ -58,10 +72,11 @@ typedef struct {</div><div>     u_short                   qlen;</div><div> </div><div>
     u_char                   *query;</div><div>+    ngx_int_t                 qtype;</div><div> </div><div>     union {</div><div>-        in_addr_t             addr;</div><div>-        in_addr_t            *addrs;</div>
<div>+        ngx_ipaddr_t          addr;</div><div>+        ngx_ipaddr_t         *addrs;</div><div>         u_char               *cname;</div><div>     } u;</div><div> </div><div>@@ -121,8 +136,8 @@ struct ngx_resolver_ctx_s {</div>
<div>     ngx_str_t                 name;</div><div> </div><div>     ngx_uint_t                naddrs;</div><div>-    in_addr_t                *addrs;</div><div>-    in_addr_t                 addr;</div><div>+    ngx_ipaddr_t             *addrs;</div>
<div>+    ngx_ipaddr_t              addr;</div><div> </div><div>     ngx_resolver_handler_pt   handler;</div><div>     void                     *data;</div><div>diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c</div>
<div>index 16e6602..7a8035c 100644</div><div>--- a/src/http/ngx_http_upstream.c</div><div>+++ b/src/http/ngx_http_upstream.c</div><div>@@ -638,7 +638,11 @@ ngx_http_upstream_init_request(ngx_http_request_t *r)</div><div>         }</div>
<div> </div><div>         ctx->name = *host;</div><div>+#if (NGX_HAVE_INET6)</div><div>+        ctx->type = NGX_RESOLVE_A_AAAA;</div><div>+#else</div><div>         ctx->type = NGX_RESOLVE_A;</div><div>+#endif</div>
<div>         ctx->handler = ngx_http_upstream_resolve_handler;</div><div>         ctx->data = r;</div><div>         ctx->timeout = clcf->resolver_timeout;</div><div>@@ -912,16 +916,14 @@ ngx_http_upstream_resolve_handler(ngx_resolver_ctx_t *ctx)</div>
<div> </div><div> #if (NGX_DEBUG)</div><div>     {</div><div>-    in_addr_t   addr;</div><div>+    char        text[NGX_SOCKADDR_STRLEN];</div><div>     ngx_uint_t  i;</div><div> </div><div>-    for (i = 0; i < ctx->naddrs; i++) {</div>
<div>-        addr = ntohl(ur->addrs[i]);</div><div>+    for (i = 0; i < ur->naddrs; i++) {</div><div>+        inet_ntop(ur->addrs[i].family, &ur->addrs[i].u, text, NGX_SOCKADDR_STRLEN);</div><div> </div>
<div>-        ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,</div><div>-                       "name was resolved to %ud.%ud.%ud.%ud",</div><div>-                       (addr >> 24) & 0xff, (addr >> 16) & 0xff,</div>
<div>-                       (addr >> 8) & 0xff, addr & 0xff);</div><div>+        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,</div><div>+                       "name was resolved to %s", text);</div>
<div>     }</div><div>     }</div><div> #endif</div><div>diff --git a/src/http/ngx_http_upstream.h b/src/http/ngx_http_upstream.h</div><div>index fd4e36b..9e88a9a 100644</div><div>--- a/src/http/ngx_http_upstream.h</div><div>
+++ b/src/http/ngx_http_upstream.h</div><div>@@ -254,7 +254,7 @@ typedef struct {</div><div>     ngx_uint_t                       no_port; /* unsigned no_port:1 */</div><div> </div><div>     ngx_uint_t                       naddrs;</div>
<div>-    in_addr_t                       *addrs;</div><div>+    ngx_ipaddr_t                    *addrs;</div><div> </div><div>     struct sockaddr                 *sockaddr;</div><div>     socklen_t                        socklen;</div>
<div>diff --git a/src/http/ngx_http_upstream_round_robin.c b/src/http/ngx_http_upstream_round_robin.c</div><div>index e0c6c58..92fa825 100644</div><div>--- a/src/http/ngx_http_upstream_round_robin.c</div><div>+++ b/src/http/ngx_http_upstream_round_robin.c</div>
<div>@@ -268,6 +268,9 @@ ngx_http_upstream_create_round_robin_peer(ngx_http_request_t *r,</div><div>     size_t                             len;</div><div>     ngx_uint_t                         i, n;</div><div>     struct sockaddr_in                *sin;</div>
<div>+#if (NGX_HAVE_INET6)</div><div>+    struct sockaddr_in6               *sin6;</div><div>+#endif</div><div>     ngx_http_upstream_rr_peers_t      *peers;</div><div>     ngx_http_upstream_rr_peer_data_t  *rrp;</div><div>
 </div><div>@@ -306,27 +309,52 @@ ngx_http_upstream_create_round_robin_peer(ngx_http_request_t *r,</div><div> </div><div>         for (i = 0; i < ur->naddrs; i++) {</div><div> </div><div>-            len = NGX_INET_ADDRSTRLEN + sizeof(":65536") - 1;</div>
<div>+            len = NGX_SOCKADDR_STRLEN;</div><div> </div><div>             p = ngx_pnalloc(r->pool, len);</div><div>             if (p == NULL) {</div><div>                 return NGX_ERROR;</div><div>             }</div>
<div> </div><div>-            len = ngx_inet_ntop(AF_INET, &ur->addrs[i], p, NGX_INET_ADDRSTRLEN);</div><div>+            len = ngx_inet_ntop(ur->addrs[i].family, &ur->addrs[i].u, p, NGX_SOCKADDR_STRLEN - sizeof(":65535") + 1);</div>
<div>             len = ngx_sprintf(&p[len], ":%d", ur->port) - p;</div><div> </div><div>-            sin = ngx_pcalloc(r->pool, sizeof(struct sockaddr_in));</div><div>-            if (sin == NULL) {</div>
<div>+            switch (ur->addrs[i].family) {</div><div>+</div><div>+            case AF_INET:</div><div>+                sin = ngx_pcalloc(r->pool, sizeof(struct sockaddr_in));</div><div>+                if (sin == NULL) {</div>
<div>+                    return NGX_ERROR;</div><div>+                }</div><div>+</div><div>+                sin->sin_family = AF_INET;</div><div>+                sin->sin_port = htons(ur->port);</div><div>+                sin->sin_addr.s_addr = ur->addrs[i].u.v4;</div>
<div>+</div><div>+                peers->peer[i].sockaddr = (struct sockaddr *) sin;</div><div>+                peers->peer[i].socklen = sizeof(struct sockaddr_in);</div><div>+                break;</div><div>+</div>
<div>+#if (NGX_HAVE_INET6)</div><div>+            case AF_INET6:</div><div>+                sin6 = ngx_pcalloc(r->pool, sizeof(struct sockaddr_in6));</div><div>+                if (sin6 == NULL) {</div><div>+                    return NGX_ERROR;</div>
<div>+                }</div><div>+</div><div>+                sin6->sin6_family = AF_INET6;</div><div>+                sin6->sin6_port = htons(ur->port);</div><div>+                sin6->sin6_addr = ur->addrs[i].u.v6;</div>
<div>+</div><div>+                peers->peer[i].sockaddr = (struct sockaddr *) sin6;</div><div>+                peers->peer[i].socklen = sizeof(struct sockaddr_in6);</div><div>+                break;</div><div>+#endif</div>
<div>+</div><div>+            default:</div><div>                 return NGX_ERROR;</div><div>             }</div><div> </div><div>-            sin->sin_family = AF_INET;</div><div>-            sin->sin_port = htons(ur->port);</div>
<div>-            sin->sin_addr.s_addr = ur->addrs[i];</div><div>-</div><div>-            peers->peer[i].sockaddr = (struct sockaddr *) sin;</div><div>-            peers->peer[i].socklen = sizeof(struct sockaddr_in);</div>
<div>             peers->peer[i].name.len = len;</div><div>             peers->peer[i].name.data = p;</div><div>             peers->peer[i].weight = 1;</div><div>@@ -334,6 +362,7 @@ ngx_http_upstream_create_round_robin_peer(ngx_http_request_t *r,</div>
<div>             peers->peer[i].current_weight = 0;</div><div>             peers->peer[i].max_fails = 1;</div><div>             peers->peer[i].fail_timeout = 10;</div><div>+</div><div>         }</div><div>     }</div>
<div> </div><div>diff --git a/src/mail/ngx_mail_smtp_handler.c b/src/mail/ngx_mail_smtp_handler.c</div><div>index 2171423..668424e 100644</div><div>--- a/src/mail/ngx_mail_smtp_handler.c</div><div>+++ b/src/mail/ngx_mail_smtp_handler.c</div>
<div>@@ -56,6 +56,9 @@ void</div><div> ngx_mail_smtp_init_session(ngx_mail_session_t *s, ngx_connection_t *c)</div><div> {</div><div>     struct sockaddr_in        *sin;</div><div>+#if (NGX_HAVE_INET6)</div><div>+    struct sockaddr_in6       *sin6;</div>
<div>+#endif</div><div>     ngx_resolver_ctx_t        *ctx;</div><div>     ngx_mail_core_srv_conf_t  *cscf;</div><div> </div><div>@@ -67,7 +70,11 @@ ngx_mail_smtp_init_session(ngx_mail_session_t *s, ngx_connection_t *c)</div>
<div>         return;</div><div>     }</div><div> </div><div>-    if (c->sockaddr->sa_family != AF_INET) {</div><div>+    if (c->sockaddr->sa_family != AF_INET</div><div>+#if (NGX_HAVE_INET6)</div><div>+        && c->sockaddr->sa_family != AF_INET6</div>
<div>+#endif</div><div>+                                        ) {</div><div>         s->host = smtp_tempunavail;</div><div>         ngx_mail_smtp_greeting(s, c);</div><div>         return;</div><div>@@ -81,11 +88,23 @@ ngx_mail_smtp_init_session(ngx_mail_session_t *s, ngx_connection_t *c)</div>
<div>         return;</div><div>     }</div><div> </div><div>-    /* AF_INET only */</div><div>+    ctx->addr.family = c->sockaddr->sa_family;</div><div> </div><div>-    sin = (struct sockaddr_in *) c->sockaddr;</div>
<div>+    switch (c->sockaddr->sa_family) {</div><div>+</div><div>+    case AF_INET:</div><div>+        sin = (struct sockaddr_in *) c->sockaddr;</div><div>+        ctx->addr.u.v4 = sin->sin_addr.s_addr;</div>
<div>+        break;</div><div>+</div><div>+#if (NGX_HAVE_INET6)</div><div>+    case AF_INET6:</div><div>+        sin6 = (struct sockaddr_in6 *) c->sockaddr;</div><div>+        ctx->addr.u.v6 = sin6->sin6_addr;</div>
<div>+        break;</div><div>+#endif</div><div>+    }</div><div> </div><div>-    ctx->addr = sin->sin_addr.s_addr;</div><div>     ctx->handler = ngx_mail_smtp_resolve_addr_handler;</div><div>     ctx->data = s;</div>
<div>     ctx->timeout = cscf->resolver_timeout;</div><div>@@ -167,11 +186,23 @@ ngx_mail_smtp_resolve_name(ngx_event_t *rev)</div><div>     }</div><div> </div><div>     ctx->name = s->host;</div><div>-    ctx->type = NGX_RESOLVE_A;</div>
<div>     ctx->handler = ngx_mail_smtp_resolve_name_handler;</div><div>     ctx->data = s;</div><div>     ctx->timeout = cscf->resolver_timeout;</div><div> </div><div>+    switch (c->sockaddr->sa_family) {</div>
<div>+</div><div>+    case AF_INET:</div><div>+        ctx->type = NGX_RESOLVE_A;</div><div>+        break;</div><div>+</div><div>+#if (NGX_HAVE_INET6)</div><div>+    case AF_INET6:</div><div>+        ctx->type = NGX_RESOLVE_AAAA_A;</div>
<div>+        break;</div><div>+#endif</div><div>+    }</div><div>+</div><div>     if (ngx_resolve_name(ctx) != NGX_OK) {</div><div>         ngx_mail_close_connection(c);</div><div>     }</div><div>@@ -181,11 +212,15 @@ ngx_mail_smtp_resolve_name(ngx_event_t *rev)</div>
<div> static void</div><div> ngx_mail_smtp_resolve_name_handler(ngx_resolver_ctx_t *ctx)</div><div> {</div><div>-    in_addr_t            addr;</div><div>+    ngx_ipaddr_t         addr;</div><div>     ngx_uint_t           i;</div>
<div>     ngx_connection_t    *c;</div><div>     struct sockaddr_in  *sin;</div><div>+#if (NGX_HAVE_INET6)</div><div>+    struct sockaddr_in6 *sin6;</div><div>+#endif</div><div>     ngx_mail_session_t  *s;</div><div>+    char                 text[NGX_SOCKADDR_STRLEN];</div>
<div> </div><div>     s = ctx->data;</div><div>     c = s->connection;</div><div>@@ -205,23 +240,49 @@ ngx_mail_smtp_resolve_name_handler(ngx_resolver_ctx_t *ctx)</div><div> </div><div>     } else {</div><div> </div>
<div>-        /* AF_INET only */</div><div>+        addr.family = c->sockaddr->sa_family;</div><div> </div><div>-        sin = (struct sockaddr_in *) c->sockaddr;</div><div>+        switch (c->sockaddr->sa_family) {</div>
<div>+</div><div>+        case AF_INET:</div><div>+            sin = (struct sockaddr_in *) c->sockaddr;</div><div>+            addr.u.v4 = sin->sin_addr.s_addr;</div><div>+            break;</div><div>+</div><div>+#if (NGX_HAVE_INET6)</div>
<div>+        case AF_INET6:</div><div>+            sin6 = (struct sockaddr_in6 *) c->sockaddr;</div><div>+            addr.u.v6 = sin6->sin6_addr;</div><div>+            break;</div><div>+#endif</div><div>+        }</div>
<div> </div><div>         for (i = 0; i < ctx->naddrs; i++) {</div><div> </div><div>-            addr = ctx->addrs[i];</div><div>+            inet_ntop(ctx->addrs[i].family, &ctx->addrs[i].u, text, NGX_SOCKADDR_STRLEN);</div>
<div> </div><div>-            ngx_log_debug4(NGX_LOG_DEBUG_MAIL, c->log, 0,</div><div>-                           "name was resolved to %ud.%ud.%ud.%ud",</div><div>-                           (ntohl(addr) >> 24) & 0xff,</div>
<div>-                           (ntohl(addr) >> 16) & 0xff,</div><div>-                           (ntohl(addr) >> 8) & 0xff,</div><div>-                           ntohl(addr) & 0xff);</div><div>+            ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,</div>
<div>+                           "name was resolved to %s", text);</div><div> </div><div>-            if (addr == sin->sin_addr.s_addr) {</div><div>-                goto found;</div><div>+            if (addr.family != ctx->addrs[i].family) {</div>
<div>+                continue;</div><div>+            }</div><div>+</div><div>+            switch (addr.family) {</div><div>+</div><div>+            case AF_INET:</div><div>+                if (addr.u.v4 == ctx->addrs[i].u.v4) {</div>
<div>+                    goto found;</div><div>+                }</div><div>+                break;</div><div>+</div><div>+#if (NGX_HAVE_INET6)</div><div>+            case AF_INET6:</div><div>+                if (!ngx_memcmp(&addr.u.v6, &ctx->addrs[i].u.v6, sizeof(addr.u.v6))) {</div>
<div>+                    goto found;</div><div>+                }</div><div>+                break;</div><div>+#endif</div><div>             }</div><div>         }</div><div> </div><div>-- </div><div>1.7.0.4</div><div><br>
</div></div></div>