IPv6 support in resolver

ToSHiC toshic.toshic at gmail.com
Wed Jul 10 17:30:58 UTC 2013


commit 2bf37859004e3ff2b5dd9a11e1725153ca43ff32
Author: Anton Kortunov <toshic.toshic at gmail.com>
Date:   Wed Jul 10 20:49:28 2013 +0400

    IPv6 support in http server upstreams

    Try to resolve upstream server name to IPv4 address first, then to IPv6.

diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c
index 16e6602..df522f7 100644
--- a/src/http/ngx_http_upstream.c
+++ b/src/http/ngx_http_upstream.c
@@ -638,7 +638,11 @@ ngx_http_upstream_init_request(ngx_http_request_t *r)
         }

         ctx->name = *host;
+#if (NGX_HAVE_INET6)
+        ctx->type = NGX_RESOLVE_A_AAAA;
+#else
         ctx->type = NGX_RESOLVE_A;
+#endif
         ctx->handler = ngx_http_upstream_resolve_handler;
         ctx->data = r;
         ctx->timeout = clcf->resolver_timeout;
@@ -912,16 +916,14 @@ ngx_http_upstream_resolve_handler(ngx_resolver_ctx_t
*ctx)

 #if (NGX_DEBUG)
     {
-    in_addr_t   addr;
+    u_char      text[NGX_SOCKADDR_STRLEN];
     ngx_uint_t  i;

-    for (i = 0; i < ctx->naddrs; i++) {
-        addr = ntohl(ur->addrs[i]);
+    for (i = 0; i < ur->naddrs; i++) {
+        ngx_inet_ntop(ur->addrs[i].family, &ur->addrs[i].u, text,
NGX_SOCKADDR_STRLEN);

-        ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                       "name was resolved to %ud.%ud.%ud.%ud",
-                       (addr >> 24) & 0xff, (addr >> 16) & 0xff,
-                       (addr >> 8) & 0xff, addr & 0xff);
+        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                       "name was resolved to %s", text);
     }
     }
 #endif
diff --git a/src/http/ngx_http_upstream.h b/src/http/ngx_http_upstream.h
index fd4e36b..9e88a9a 100644
--- a/src/http/ngx_http_upstream.h
+++ b/src/http/ngx_http_upstream.h
@@ -254,7 +254,7 @@ typedef struct {
     ngx_uint_t                       no_port; /* unsigned no_port:1 */

     ngx_uint_t                       naddrs;
-    in_addr_t                       *addrs;
+    ngx_ipaddr_t                    *addrs;

     struct sockaddr                 *sockaddr;
     socklen_t                        socklen;
diff --git a/src/http/ngx_http_upstream_round_robin.c
b/src/http/ngx_http_upstream_round_robin.c
index e0c6c58..cf9d6a0 100644
--- a/src/http/ngx_http_upstream_round_robin.c
+++ b/src/http/ngx_http_upstream_round_robin.c
@@ -268,6 +268,9 @@
ngx_http_upstream_create_round_robin_peer(ngx_http_request_t *r,
     size_t                             len;
     ngx_uint_t                         i, n;
     struct sockaddr_in                *sin;
+#if (NGX_HAVE_INET6)
+    struct sockaddr_in6               *sin6;
+#endif
     ngx_http_upstream_rr_peers_t      *peers;
     ngx_http_upstream_rr_peer_data_t  *rrp;

@@ -306,27 +309,52 @@
ngx_http_upstream_create_round_robin_peer(ngx_http_request_t *r,

         for (i = 0; i < ur->naddrs; i++) {

-            len = NGX_INET_ADDRSTRLEN + sizeof(":65536") - 1;
+            len = NGX_SOCKADDR_STRLEN;

             p = ngx_pnalloc(r->pool, len);
             if (p == NULL) {
                 return NGX_ERROR;
             }

-            len = ngx_inet_ntop(AF_INET, &ur->addrs[i], p,
NGX_INET_ADDRSTRLEN);
+            len = ngx_inet_ntop(ur->addrs[i].family, &ur->addrs[i].u, p,
NGX_SOCKADDR_STRLEN - sizeof(":65535") + 1);
             len = ngx_sprintf(&p[len], ":%d", ur->port) - p;

-            sin = ngx_pcalloc(r->pool, sizeof(struct sockaddr_in));
-            if (sin == NULL) {
+            switch (ur->addrs[i].family) {
+
+            case AF_INET:
+                sin = ngx_pcalloc(r->pool, sizeof(struct sockaddr_in));
+                if (sin == NULL) {
+                    return NGX_ERROR;
+                }
+
+                sin->sin_family = AF_INET;
+                sin->sin_port = htons(ur->port);
+                sin->sin_addr.s_addr = ur->addrs[i].u.v4;
+
+                peers->peer[i].sockaddr = (struct sockaddr *) sin;
+                peers->peer[i].socklen = sizeof(struct sockaddr_in);
+                break;
+
+#if (NGX_HAVE_INET6)
+            case AF_INET6:
+                sin6 = ngx_pcalloc(r->pool, sizeof(struct sockaddr_in6));
+                if (sin6 == NULL) {
+                    return NGX_ERROR;
+                }
+
+                sin6->sin6_family = AF_INET6;
+                sin6->sin6_port = htons(ur->port);
+                sin6->sin6_addr = ur->addrs[i].u.v6;
+
+                peers->peer[i].sockaddr = (struct sockaddr *) sin6;
+                peers->peer[i].socklen = sizeof(struct sockaddr_in6);
+                break;
+#endif
+
+            default:
                 return NGX_ERROR;
             }

-            sin->sin_family = AF_INET;
-            sin->sin_port = htons(ur->port);
-            sin->sin_addr.s_addr = ur->addrs[i];
-
-            peers->peer[i].sockaddr = (struct sockaddr *) sin;
-            peers->peer[i].socklen = sizeof(struct sockaddr_in);
             peers->peer[i].name.len = len;
             peers->peer[i].name.data = p;
             peers->peer[i].weight = 1;



On Wed, Jul 10, 2013 at 9:29 PM, ToSHiC <toshic.toshic at gmail.com> wrote:

> commit 524dd02549575cb9ad5e95444093f6b494dc59bc
> Author: Anton Kortunov <toshic.toshic at gmail.com>
> Date:   Wed Jul 10 20:43:59 2013 +0400
>
>     IPv6 reverse resolve support
>
> diff --git a/src/core/ngx_resolver.c b/src/core/ngx_resolver.c
> index 567368b..06d46c1 100644
> --- a/src/core/ngx_resolver.c
> +++ b/src/core/ngx_resolver.c
> @@ -71,7 +71,7 @@ static void ngx_resolver_process_response(ngx_resolver_t
> *r, u_char *buf,
>      size_t n);
>  static void ngx_resolver_process_a(ngx_resolver_t *r, u_char *buf, size_t
> n,
>      ngx_uint_t ident, ngx_uint_t code, ngx_uint_t nan, ngx_uint_t ans);
> -static void ngx_resolver_process_ptr(ngx_resolver_t *r, u_char *buf,
> size_t n,
> +void ngx_resolver_process_ptr(ngx_resolver_t *r, u_char *buf, size_t n,
>      ngx_uint_t ident, ngx_uint_t code, ngx_uint_t nan);
>  static ngx_resolver_node_t *ngx_resolver_lookup_name(ngx_resolver_t *r,
>      ngx_str_t *name, uint32_t hash);
> @@ -126,7 +126,7 @@ ngx_resolver_create(ngx_conf_t *cf, ngx_str_t *names,
> ngx_uint_t n)
>                      ngx_resolver_rbtree_insert_value);
>
>      ngx_rbtree_init(&r->addr_rbtree, &r->addr_sentinel,
> -                    ngx_rbtree_insert_value);
> +                    ngx_resolver_rbtree_insert_value);
>
>      ngx_queue_init(&r->name_resend_queue);
>      ngx_queue_init(&r->addr_resend_queue);
> @@ -649,17 +649,40 @@ failed:
>  ngx_int_t
>  ngx_resolve_addr(ngx_resolver_ctx_t *ctx)
>  {
> +    uint32_t              hash;
>      u_char               *name;
>      ngx_resolver_t       *r;
>      ngx_resolver_node_t  *rn;
>
>      r = ctx->resolver;
> +    rn = NULL;
> +
> +    hash = ctx->addr.family;
> +
> +    switch(ctx->addr.family) {
> +
> +    case AF_INET:
> +        ctx->addr.u.v4 = ntohl(ctx->addr.u.v4);
> +        ngx_crc32_update(&hash, (u_char *)&ctx->addr.u.v4,
> sizeof(in_addr_t));
> +ngx_log_debug3(NGX_LOG_DEBUG_CORE, r->log, 0,
> +               "resolve addr hash: %xd, addr:%xd, family: %d", hash,
> ctx->addr.u.v4, ctx->addr.family);
> +        break;
> +
> +#if (NGX_HAVE_INET6)
> +    case AF_INET6:
> +        ngx_crc32_update(&hash, (u_char *)&ctx->addr.u.v6, sizeof(struct
> in6_addr));
> +        break;
> +#endif
>
> -    ctx->addr = ntohl(ctx->addr);
> +    default:
> +        goto failed;
> +    }
>
>      /* lock addr mutex */
>
> -    rn = ngx_resolver_lookup_addr(r, ctx->addr);
> +    rn = ngx_resolver_lookup_addr(r, ctx->addr, hash);
> +    ngx_log_error(r->log_level, r->log, 0,
> +                  "resolve: in resolve_addr searching, hash = %xd, rn =
> %p", hash, rn);
>
>      if (rn) {
>
> @@ -714,8 +737,10 @@ ngx_resolve_addr(ngx_resolver_ctx_t *ctx)
>              goto failed;
>          }
>
> -        rn->node.key = ctx->addr;
> +        rn->node.key = hash;
>          rn->query = NULL;
> +        rn->qtype = ctx->type;
> +        rn->u.addr = ctx->addr;
>
>          ngx_rbtree_insert(&r->addr_rbtree, &rn->node);
>      }
> @@ -788,10 +813,11 @@ failed:
>  void
>  ngx_resolve_addr_done(ngx_resolver_ctx_t *ctx)
>  {
> -    in_addr_t             addr;
> +    uint32_t              hash;
>      ngx_resolver_t       *r;
>      ngx_resolver_ctx_t   *w, **p;
>      ngx_resolver_node_t  *rn;
> +    u_char                text[NGX_SOCKADDR_STRLEN];
>
>      r = ctx->resolver;
>
> @@ -806,7 +832,25 @@ ngx_resolve_addr_done(ngx_resolver_ctx_t *ctx)
>
>      if (ctx->state == NGX_AGAIN || ctx->state == NGX_RESOLVE_TIMEDOUT) {
>
> -        rn = ngx_resolver_lookup_addr(r, ctx->addr);
> +        hash = ctx->addr.family;
> +
> +        switch(ctx->addr.family) {
> +
> +        case AF_INET:
> +            ngx_crc32_update(&hash, (u_char *)&ctx->addr.u.v4,
> sizeof(in_addr_t));
> +            break;
> +
> +#if (NGX_HAVE_INET6)
> +        case AF_INET6:
> +            ngx_crc32_update(&hash, (u_char *)&ctx->addr.u.v6,
> sizeof(struct in6_addr));
> +            break;
> +#endif
> +
> +        default:
> +            goto failed;
> +        }
> +
> +        rn = ngx_resolver_lookup_addr(r, ctx->addr, hash);
>
>          if (rn) {
>              p = &rn->waiting;
> @@ -824,12 +868,12 @@ ngx_resolve_addr_done(ngx_resolver_ctx_t *ctx)
>              }
>          }
>
> -        addr = ntohl(ctx->addr);
> +failed:
> +
> +        ngx_inet_ntop(ctx->addr.family, &ctx->addr.u, text,
> NGX_SOCKADDR_STRLEN);
>
>          ngx_log_error(NGX_LOG_ALERT, r->log, 0,
> -                      "could not cancel %ud.%ud.%ud.%ud resolving",
> -                      (addr >> 24) & 0xff, (addr >> 16) & 0xff,
> -                      (addr >> 8) & 0xff, addr & 0xff);
> +                      "could not cancel %s resolving", text);
>      }
>
>  done:
> @@ -1582,13 +1626,14 @@ failed:
>  }
>
>
> -static void
> +void
>  ngx_resolver_process_ptr(ngx_resolver_t *r, u_char *buf, size_t n,
>      ngx_uint_t ident, ngx_uint_t code, ngx_uint_t nan)
>  {
> -    char                 *err;
> +    char                 *err = NULL;
> +    uint32_t              hash = 0;
>      size_t                len;
> -    in_addr_t             addr;
> +    ngx_ipaddr_t          addr;
>      int32_t               ttl;
>      ngx_int_t             digit;
>      ngx_str_t             name;
> @@ -1596,12 +1641,16 @@ ngx_resolver_process_ptr(ngx_resolver_t *r, u_char
> *buf, size_t n,
>      ngx_resolver_an_t    *an;
>      ngx_resolver_ctx_t   *ctx, *next;
>      ngx_resolver_node_t  *rn;
> +    u_char                text[NGX_SOCKADDR_STRLEN];
>
>      if (ngx_resolver_copy(r, NULL, buf, &buf[12], &buf[n]) != NGX_OK) {
>          goto invalid_in_addr_arpa;
>      }
>
> -    addr = 0;
> +    ngx_memzero(&addr, sizeof(ngx_ipaddr_t));
> +
> +    /* Try to parse request as in-addr.arpa */
> +    addr.family = AF_INET;
>      i = 12;
>
>      for (mask = 0; mask < 32; mask += 8) {
> @@ -1612,7 +1661,7 @@ ngx_resolver_process_ptr(ngx_resolver_t *r, u_char
> *buf, size_t n,
>              goto invalid_in_addr_arpa;
>          }
>
> -        addr += digit << mask;
> +        addr.u.v4 += digit << mask;
>          i += len;
>      }
>
> @@ -1620,15 +1669,79 @@ ngx_resolver_process_ptr(ngx_resolver_t *r, u_char
> *buf, size_t n,
>          goto invalid_in_addr_arpa;
>      }
>
> +    i += sizeof("\7in-addr\4arpa") + sizeof(ngx_resolver_qs_t);
> +
> +    goto found;
> +
> +invalid_in_addr_arpa:
> +
> +#if (NGX_HAVE_INET6)
> +    /* Try to parse request as ip6.arpa */
> +    addr.family = AF_INET6;
> +    i = 12;
> +
> +    for (len = 15; len < 16; len--) {
> +        if (buf[i++] != 1)
> +            goto invalid_arpa;
> +
> +        digit = ngx_hextoi(&buf[i++], 1);
> +        if (digit == NGX_ERROR || digit > 16) {
> +            goto invalid_arpa;
> +        }
> +
> +        addr.u.v6.s6_addr[len] = digit;
> +
> +        if (buf[i++] != 1)
> +            goto invalid_arpa;
> +
> +
> +        digit = ngx_hextoi(&buf[i++], 1);
> +        if (digit == NGX_ERROR || digit > 16) {
> +            goto invalid_arpa;
> +        }
> +
> +        addr.u.v6.s6_addr[len] += digit << 4;
> +    }
> +
> +    if (ngx_strcmp(&buf[i], "\3ip6\4arpa") != 0) {
> +        goto invalid_arpa;
> +    }
> +
> +    i += sizeof("\3ip6\4arpa") + sizeof(ngx_resolver_qs_t);
> +
> +#else /* NGX_HAVE_INET6 */
> +    goto invalid_arpa;
> +#endif
> +
> +found:
> +
>      /* lock addr mutex */
>
> -    rn = ngx_resolver_lookup_addr(r, addr);
> +    hash = addr.family;
> +
> +    switch(addr.family) {
> +
> +    case AF_INET:
> +        ngx_crc32_update(&hash, (u_char *)&addr.u.v4, sizeof(in_addr_t));
> +        break;
> +
> +#if (NGX_HAVE_INET6)
> +    case AF_INET6:
> +        ngx_crc32_update(&hash, (u_char *)&addr.u.v6, sizeof(struct
> in6_addr));
> +        break;
> +#endif
> +
> +    default:
> +        goto invalid;
> +    }
> +
> +    rn = ngx_resolver_lookup_addr(r, addr, hash);
> +
> +    ngx_inet_ntop(addr.family, &addr.u, text, NGX_SOCKADDR_STRLEN);
>
>      if (rn == NULL || rn->query == NULL) {
>          ngx_log_error(r->log_level, r->log, 0,
> -                      "unexpected response for %ud.%ud.%ud.%ud",
> -                      (addr >> 24) & 0xff, (addr >> 16) & 0xff,
> -                      (addr >> 8) & 0xff, addr & 0xff);
> +                      "unexpected response for %s", text);
>          goto failed;
>      }
>
> @@ -1636,12 +1749,15 @@ ngx_resolver_process_ptr(ngx_resolver_t *r, u_char
> *buf, size_t n,
>
>      if (ident != qident) {
>          ngx_log_error(r->log_level, r->log, 0,
> -                    "wrong ident %ui response for %ud.%ud.%ud.%ud, expect
> %ui",
> -                    ident, (addr >> 24) & 0xff, (addr >> 16) & 0xff,
> -                    (addr >> 8) & 0xff, addr & 0xff, qident);
> +                    "wrong ident %ui response for %s, expect %ui",
> +                    ident, text, qident);
>          goto failed;
>      }
>
> +    ngx_log_error(r->log_level, r->log, 0,
> +                "code: %d, nan: %d",
> +                code, nan);
> +
>      if (code == 0 && nan == 0) {
>          code = 3; /* NXDOMAIN */
>      }
> @@ -1669,8 +1785,6 @@ ngx_resolver_process_ptr(ngx_resolver_t *r, u_char
> *buf, size_t n,
>          return;
>      }
>
> -    i += sizeof("\7in-addr\4arpa") + sizeof(ngx_resolver_qs_t);
> -
>      if (i + 2 + sizeof(ngx_resolver_an_t) > (ngx_uint_t) n) {
>          goto short_response;
>      }
> @@ -1750,10 +1864,10 @@ ngx_resolver_process_ptr(ngx_resolver_t *r, u_char
> *buf, size_t n,
>
>      return;
> -invalid_in_addr_arpa:
> +invalid_arpa:
>
>      ngx_log_error(r->log_level, r->log, 0,
> -                  "invalid in-addr.arpa name in DNS response");
> +                  "invalid in-addr.arpa or ip6.arpa name in DNS
> response");
>      return;
>
>  short_response:
> @@ -1818,28 +1932,54 @@ ngx_resolver_lookup_name(ngx_resolver_t *r,
> ngx_str_t *name, uint32_t hash)
>
>
>  static ngx_resolver_node_t *
> -ngx_resolver_lookup_addr(ngx_resolver_t *r, in_addr_t addr)
> +ngx_resolver_lookup_addr(ngx_resolver_t *r, ngx_ipaddr_t addr, uint32_t
> hash)
>  {
> +    ngx_int_t             rc;
>      ngx_rbtree_node_t  *node, *sentinel;
> +    ngx_resolver_node_t  *rn;
>
>      node = r->addr_rbtree.root;
>      sentinel = r->addr_rbtree.sentinel;
>
>      while (node != sentinel) {
>
> -        if (addr < node->key) {
> +        if (hash < node->key) {
>              node = node->left;
>              continue;
>          }
>
> -        if (addr > node->key) {
> +        if (hash > node->key) {
>              node = node->right;
>              continue;
>          }
>
> -        /* addr == node->key */
> +        /* hash == node->key */
> +
> +        rn = (ngx_resolver_node_t *) node;
> +
> +        rc = addr.family - rn->u.addr.family;
> +
> +        if (rc == 0) {
> +
> +            switch (addr.family) {
> +            case AF_INET:
> +                rc = ngx_memn2cmp((u_char *)&addr.u.v4, (u_char
> *)&rn->u.addr.u.v4, sizeof(in_addr_t), sizeof(in_addr_t));
> +                break;
> +
> +#if (NGX_HAVE_INET6)
> +            case AF_INET6:
> +                rc = ngx_memn2cmp((u_char *)&addr.u.v6, (u_char
> *)&rn->u.addr.u.v6, sizeof(struct in6_addr), sizeof(struct in6_addr));
> +                break;
> +#endif
> +            }
> +
> +            if (rc == 0) {
> +                return rn;
> +            }
>
> -        return (ngx_resolver_node_t *) node;
> +        }
> +
> +        node = (rc < 0) ? node->left : node->right;
>      }
>
>      /* not found */
> @@ -1854,6 +1994,7 @@ ngx_resolver_rbtree_insert_value(ngx_rbtree_node_t
> *temp,
>  {
>      ngx_rbtree_node_t    **p;
>      ngx_resolver_node_t   *rn, *rn_temp;
> +    ngx_int_t              rc;
>
>      for ( ;; ) {
>
> @@ -1870,8 +2011,29 @@ ngx_resolver_rbtree_insert_value(ngx_rbtree_node_t
> *temp,
>              rn = (ngx_resolver_node_t *) node;
>              rn_temp = (ngx_resolver_node_t *) temp;
>
> -            p = (ngx_memn2cmp(rn->name, rn_temp->name, rn->nlen,
> rn_temp->nlen)
> -                 < 0) ? &temp->left : &temp->right;
> +            if (rn->qtype == NGX_RESOLVE_PTR) {
> +                rc = rn->u.addr.family - rn_temp->u.addr.family;
> +
> +                if (rc == 0) {
> +
> +                    switch (rn->u.addr.family) {
> +                    case AF_INET:
> +                        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));
> +                        break;
> +
> +        #if (NGX_HAVE_INET6)
> +                    case AF_INET6:
> +                        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));
> +                        break;
> +        #endif
> +                    }
> +                }
> +
> +            } else {
> +                rc = ngx_memn2cmp(rn->name, rn_temp->name, rn->nlen,
> rn_temp->nlen);
> +            }
> +
> +            p = (rc < 0) ? &temp->left : &temp->right;
>          }
>
>          if (*p == sentinel) {
> @@ -1989,8 +2151,6 @@ ngx_resolver_create_name_query(ngx_resolver_node_t
> *rn, ngx_resolver_ctx_t *ctx)
>  }
>
>
> -/* AF_INET only */
> -
>  static ngx_int_t
>  ngx_resolver_create_addr_query(ngx_resolver_node_t *rn,
> ngx_resolver_ctx_t *ctx)
>  {
> @@ -2001,7 +2161,7 @@ ngx_resolver_create_addr_query(ngx_resolver_node_t
> *rn, ngx_resolver_ctx_t *ctx)
>      ngx_resolver_query_t  *query;
>
>      len = sizeof(ngx_resolver_query_t)
> -          + sizeof(".255.255.255.255.in-addr.arpa.") - 1
> +          + NGX_PTR_QUERY_LEN
>            + sizeof(ngx_resolver_qs_t);
>
>      p = ngx_resolver_alloc(ctx->resolver, len);
> @@ -2028,18 +2188,50 @@ ngx_resolver_create_addr_query(ngx_resolver_node_t
> *rn, ngx_resolver_ctx_t *ctx)
>      p += sizeof(ngx_resolver_query_t);
>
> -    for (n = 0; n < 32; n += 8) {
> -        d = ngx_sprintf(&p[1], "%ud", (ctx->addr >> n) & 0xff);
> -        *p = (u_char) (d - &p[1]);
> -        p = d;
> +    switch (ctx->addr.family) {
> +
> +    case AF_INET:
> +        for (n = 0; n < 32; n += 8) {
> +            d = ngx_sprintf(&p[1], "%ud", (ctx->addr.u.v4 >> n) & 0xff);
> +            *p = (u_char) (d - &p[1]);
> +            p = d;
> +        }
> +
> +        /* query type "PTR", IP query class */
> +        ngx_memcpy(p, "\7in-addr\4arpa\0\0\14\0\1", 18);
> +
> +        rn->qlen = (u_short)
> +                      (p + sizeof("\7in-addr\4arpa") +
> sizeof(ngx_resolver_qs_t)
> +                       - rn->query);
> +
> +        break;
> +
> +#if (NGX_HAVE_INET6)
> +    case AF_INET6:
> +        for (n = 15; n >= 0; n--) {
> +            p = ngx_sprintf(p, "\1%xd\1%xd",
> +                            (ctx->addr.u.v6.s6_addr[n]) & 0xf,
> +                            (ctx->addr.u.v6.s6_addr[n] >> 4) & 0xf);
> +
> +        }
> +
> +        /* query type "PTR", IP query class */
> +        ngx_memcpy(p, "\3ip6\4arpa\0\0\14\0\1", 18);
> +
> +        rn->qlen = (u_short)
> +                      (p + sizeof("\3ip6\4arpa") +
> sizeof(ngx_resolver_qs_t)
> +                       - rn->query);
> +
> +        break;
> +#endif
> +
> +    default:
> +        return NGX_ERROR;
>      }
>
> -    /* query type "PTR", IP query class */
> -    ngx_memcpy(p, "\7in-addr\4arpa\0\0\14\0\1", 18);
> +ngx_log_debug2(NGX_LOG_DEBUG_CORE, ctx->resolver->log, 0,
> +               "resolve: query %s, ident %i", (rn->query+12), ident &
> 0xffff);
>
> -    rn->qlen = (u_short)
> -                  (p + sizeof("\7in-addr\4arpa") +
> sizeof(ngx_resolver_qs_t)
> -                   - rn->query);
>
>      return NGX_OK;
>  }
> diff --git a/src/core/ngx_resolver.h b/src/core/ngx_resolver.h
> index d2a4606..a45b244 100644
> --- a/src/core/ngx_resolver.h
> +++ b/src/core/ngx_resolver.h
> @@ -41,6 +41,11 @@
>
>  #define NGX_RESOLVER_MAX_RECURSION    50
>
> +#if (NGX_HAVE_INET6)
> +#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)
> +#else
> +#define NGX_PTR_QUERY_LEN   (sizeof(".255.255.255.255.in-addr.arpa.") - 1)
> +#endif
>
>  typedef struct {
>      ngx_connection_t         *connection;
>
>
>
> On Wed, Jul 10, 2013 at 9:24 PM, ToSHiC <toshic.toshic at gmail.com> wrote:
>
>> commit 8670b164784032b2911b3c34ac31ef52ddba5b60
>> Author: Anton Kortunov <toshic.toshic at gmail.com>
>> Date:   Wed Jul 10 19:53:06 2013 +0400
>>
>>     IPv6 support in resolver for forward requests
>>
>>     To resolve name into IPv6 address use NGX_RESOLVE_AAAA,
>>     NGX_RESOLVE_A_AAAA or NGX_RESOLVE_AAAA_A record type instead of
>>     NGX_RESOLVE_A
>>
>> diff --git a/src/core/ngx_resolver.c b/src/core/ngx_resolver.c
>> index d59d0c4..567368b 100644
>> --- a/src/core/ngx_resolver.c
>> +++ b/src/core/ngx_resolver.c
>> @@ -76,7 +76,7 @@ static void ngx_resolver_process_ptr(ngx_resolver_t *r,
>> u_char *buf, size_t n,
>>  static ngx_resolver_node_t *ngx_resolver_lookup_name(ngx_resolver_t *r,
>>      ngx_str_t *name, uint32_t hash);
>>  static ngx_resolver_node_t *ngx_resolver_lookup_addr(ngx_resolver_t *r,
>> -    in_addr_t addr);
>> +    ngx_ipaddr_t addr, uint32_t hash);
>>  static void ngx_resolver_rbtree_insert_value(ngx_rbtree_node_t *temp,
>>      ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);
>>  static ngx_int_t ngx_resolver_copy(ngx_resolver_t *r, ngx_str_t *name,
>> @@ -88,7 +88,7 @@ static void *ngx_resolver_calloc(ngx_resolver_t *r,
>> size_t size);
>>  static void ngx_resolver_free(ngx_resolver_t *r, void *p);
>>  static void ngx_resolver_free_locked(ngx_resolver_t *r, void *p);
>>  static void *ngx_resolver_dup(ngx_resolver_t *r, void *src, size_t size);
>> -static in_addr_t *ngx_resolver_rotate(ngx_resolver_t *r, in_addr_t *src,
>> +static ngx_ipaddr_t *ngx_resolver_rotate(ngx_resolver_t *r, ngx_ipaddr_t
>> *src,
>>      ngx_uint_t n);
>>  static u_char *ngx_resolver_log_error(ngx_log_t *log, u_char *buf,
>> size_t len);
>>
>> @@ -270,13 +270,27 @@ ngx_resolver_cleanup_tree(ngx_resolver_t *r,
>> ngx_rbtree_t *tree)
>>  ngx_resolver_ctx_t *
>>  ngx_resolve_start(ngx_resolver_t *r, ngx_resolver_ctx_t *temp)
>>  {
>> -    in_addr_t            addr;
>> +    ngx_ipaddr_t         addr;
>>      ngx_resolver_ctx_t  *ctx;
>>
>>      if (temp) {
>> -        addr = ngx_inet_addr(temp->name.data, temp->name.len);
>> +        addr.family = 0;
>>
>> -        if (addr != INADDR_NONE) {
>> +
>> +        addr.u.v4 = ngx_inet_addr(temp->name.data, temp->name.len);
>> +
>> +        if (addr.u.v4 != INADDR_NONE) {
>> +
>> +            addr.family = AF_INET;
>> +
>> +#if (NGX_HAVE_INET6)
>> +        } else if (ngx_inet6_addr(temp->name.data, temp->name.len,
>> addr.u.v6.s6_addr) == NGX_OK) {
>> +
>> +            addr.family = AF_INET6;
>> +#endif
>> +        }
>> +
>> +        if (addr.family) {
>>              temp->resolver = r;
>>              temp->state = NGX_OK;
>>              temp->naddrs = 1;
>> @@ -417,7 +431,7 @@ static ngx_int_t
>>  ngx_resolve_name_locked(ngx_resolver_t *r, ngx_resolver_ctx_t *ctx)
>>  {
>>      uint32_t              hash;
>> -    in_addr_t             addr, *addrs;
>> +    ngx_ipaddr_t          addr, *addrs;
>>      ngx_int_t             rc;
>>      ngx_uint_t            naddrs;
>>      ngx_resolver_ctx_t   *next;
>> @@ -429,7 +443,11 @@ ngx_resolve_name_locked(ngx_resolver_t *r,
>> ngx_resolver_ctx_t *ctx)
>>
>>      if (rn) {
>>
>> -        if (rn->valid >= ngx_time()) {
>> +        if (rn->valid >= ngx_time()
>> +#if (NGX_HAVE_INET6)
>> + && rn->qtype != NGX_RESOLVE_RETRY
>> +#endif
>> +                                    ) {
>>
>>              ngx_log_debug0(NGX_LOG_DEBUG_CORE, r->log, 0, "resolve
>> cached");
>>
>> @@ -446,7 +464,6 @@ ngx_resolve_name_locked(ngx_resolver_t *r,
>> ngx_resolver_ctx_t *ctx)
>>                  /* NGX_RESOLVE_A answer */
>>
>>                  if (naddrs != 1) {
>> -                    addr = 0;
>>                      addrs = ngx_resolver_rotate(r, rn->u.addrs, naddrs);
>>                      if (addrs == NULL) {
>>                          return NGX_ERROR;
>> @@ -506,6 +523,8 @@ ngx_resolve_name_locked(ngx_resolver_t *r,
>> ngx_resolver_ctx_t *ctx)
>>              } while (ctx);
>>
>>              return NGX_OK;
>> +        } else {
>> +            rn->qtype = ctx->type;
>>          }
>>
>>          if (rn->waiting) {
>> @@ -552,6 +571,7 @@ ngx_resolve_name_locked(ngx_resolver_t *r,
>> ngx_resolver_ctx_t *ctx)
>>          rn->node.key = hash;
>>          rn->nlen = (u_short) ctx->name.len;
>>          rn->query = NULL;
>> +        rn->qtype = ctx->type;
>>
>>          ngx_rbtree_insert(&r->name_rbtree, &rn->node);
>>      }
>> @@ -1130,6 +1150,9 @@ found:
>>      switch (qtype) {
>>
>>      case NGX_RESOLVE_A:
>> +#if (NGX_HAVE_INET6)
>> +    case NGX_RESOLVE_AAAA:
>> +#endif
>>
>>          ngx_resolver_process_a(r, buf, n, ident, code, nan,
>>                                 i + sizeof(ngx_resolver_qs_t));
>> @@ -1178,7 +1201,7 @@ ngx_resolver_process_a(ngx_resolver_t *r, u_char
>> *buf, size_t last,
>>       size_t                len;
>>      int32_t               ttl;
>>      uint32_t              hash;
>> -    in_addr_t             addr, *addrs;
>> +    ngx_ipaddr_t          addr, *addrs;
>>      ngx_str_t             name;
>>      ngx_uint_t            qtype, qident, naddrs, a, i, n, start;
>>      ngx_resolver_an_t    *an;
>> @@ -1212,12 +1235,57 @@ ngx_resolver_process_a(ngx_resolver_t *r, u_char
>> *buf, size_t last,
>>           goto failed;
>>      }
>>
>> -    ngx_resolver_free(r, name.data);
>> -
>>      if (code == 0 && nan == 0) {
>> +
>> +#if (NGX_HAVE_INET6)
>>  +       /*
>> +     * If it was required dual type v4|v6 resolv create one more request
>> +     */
>> +       if (rn->qtype == NGX_RESOLVE_A_AAAA
>> +               || rn->qtype == NGX_RESOLVE_AAAA_A) {
>> +
>> +            ngx_queue_remove(&rn->queue);
>> +
>> +            rn->valid = ngx_time() + (r->valid ? r->valid : ttl);
>> +            rn->expire = ngx_time() + r->expire;
>> +
>> +            ngx_queue_insert_head(&r->name_expire_queue, &rn->queue);
>> +
>> +            ctx = rn->waiting;
>> +            rn->waiting = NULL;
>> +
>> +            if (ctx) {
>> +                ctx->name = name;
>> +
>> +                switch (rn->qtype) {
>> +
>> +                case NGX_RESOLVE_A_AAAA:
>> +                    ctx->type = NGX_RESOLVE_AAAA;
>> +                    break;
>> +
>> +                case NGX_RESOLVE_AAAA_A:
>> +                    ctx->type = NGX_RESOLVE_A;
>> +                    break;
>> +                }
>> +
>> +                ngx_log_debug2(NGX_LOG_DEBUG_CORE, r->log, 0,
>> +                              "restarting request for name %V, with type
>> %ud",
>> +                              &name, ctx->type);
>> +
>> +                rn->qtype = NGX_RESOLVE_RETRY;
>> +
>> +                (void) ngx_resolve_name_locked(r, ctx);
>> +            }
>> +
>> +            return;
>> +        }
>> +#endif
>> +
>>          code = 3; /* NXDOMAIN */
>>      }
>>
>> +    ngx_resolver_free(r, name.data);
>> +
>>      if (code) {
>>          next = rn->waiting;
>>          rn->waiting = NULL;
>> @@ -1243,7 +1311,7 @@ ngx_resolver_process_a(ngx_resolver_t *r, u_char
>> *buf, size_t last,
>>
>>      i = ans;
>>      naddrs = 0;
>> -    addr = 0;
>> +    addr.family = 0;
>>      addrs = NULL;
>>      cname = NULL;
>>      qtype = 0;
>> @@ -1302,13 +1370,30 @@ ngx_resolver_process_a(ngx_resolver_t *r, u_char
>> *buf, size_t last,
>>                   goto short_response;
>>              }
>>
>> -            addr = htonl((buf[i] << 24) + (buf[i + 1] << 16)
>> +            addr.family = AF_INET;
>> +            addr.u.v4 = htonl((buf[i] << 24) + (buf[i + 1] << 16)
>>                           + (buf[i + 2] << 8) + (buf[i + 3]));
>>
>>              naddrs++;
>>
>>              i += len;
>>
>> +#if (NGX_HAVE_INET6)
>> +        } else if (qtype == NGX_RESOLVE_AAAA) {
>> +
>> +            i += sizeof(ngx_resolver_an_t);
>> +
>> +            if (i + len > last) {
>> +                goto short_response;
>> +            }
>> +
>> +            addr.family = AF_INET6;
>> +            ngx_memcpy(&addr.u.v6.s6_addr, &buf[i], 16);
>> +
>> +            naddrs++;
>> +
>> +            i += len;
>> +#endif
>>          } else if (qtype == NGX_RESOLVE_CNAME) {
>>              cname = &buf[i] + sizeof(ngx_resolver_an_t);
>>              i += sizeof(ngx_resolver_an_t) + len;
>> @@ -1333,7 +1418,7 @@ ngx_resolver_process_a(ngx_resolver_t *r, u_char
>> *buf, size_t last,
>>
>>          } else {
>>
>> -            addrs = ngx_resolver_alloc(r, naddrs * sizeof(in_addr_t));
>> +            addrs = ngx_resolver_alloc(r, naddrs * sizeof(ngx_ipaddr_t));
>>              if (addrs == NULL) {
>>                  return;
>>              }
>> @@ -1369,12 +1454,23 @@ ngx_resolver_process_a(ngx_resolver_t *r, u_char
>> *buf, size_t last,
>>
>>                  if (qtype == NGX_RESOLVE_A) {
>>
>> -                    addrs[n++] = htonl((buf[i] << 24) + (buf[i + 1] <<
>> 16)
>> +                    addrs[n].family = AF_INET;
>> +                    addrs[n++].u.v4 = htonl((buf[i] << 24) + (buf[i + 1]
>> << 16)
>>                                         + (buf[i + 2] << 8) + (buf[i +
>> 3]));
>>
>>                      if (n == naddrs) {
>>                          break;
>>                      }
>> +#if (NGX_HAVE_INET6)
>> +                } else if (qtype == NGX_RESOLVE_AAAA) {
>> +
>> +                    addrs[n].family = AF_INET6;
>> +                    ngx_memcpy(&addrs[n++].u.v6.s6_addr, &buf[i], 16);
>> +
>> +                    if (n == naddrs) {
>> +                        break;
>> +                    }
>> +#endif
>>                  }
>>
>>                  i += len;
>> @@ -1383,7 +1479,7 @@ ngx_resolver_process_a(ngx_resolver_t *r, u_char
>> *buf, size_t last,
>>              rn->u.addrs = addrs;
>>
>>              addrs = ngx_resolver_dup(r, rn->u.addrs,
>> -                                     naddrs * sizeof(in_addr_t));
>> +                                     naddrs * sizeof(ngx_ipaddr_t));
>>              if (addrs == NULL) {
>>                  return;
>>              }
>> @@ -1838,7 +1934,20 @@ ngx_resolver_create_name_query(ngx_resolver_node_t
>> *rn, ngx_resolver_ctx_t *ctx)
>>       qs = (ngx_resolver_qs_t *) p;
>>
>>      /* query type */
>> -    qs->type_hi = 0; qs->type_lo = (u_char) ctx->type;
>> +    qs->type_hi = 0; qs->type_lo = (u_char) rn->qtype;
>> +
>> +#if (NGX_HAVE_INET6)
>> +    switch (rn->qtype) {
>> +
>> +    case NGX_RESOLVE_A_AAAA:
>> +        qs->type_lo = NGX_RESOLVE_A;
>> +        break;
>> +
>> +    case NGX_RESOLVE_AAAA_A:
>> +        qs->type_lo = NGX_RESOLVE_AAAA;
>> +        break;
>> +    }
>> +#endif
>>
>>      /* IP query class */
>>      qs->class_hi = 0; qs->class_lo = 1;
>> @@ -2136,13 +2245,13 @@ ngx_resolver_dup(ngx_resolver_t *r, void *src,
>> size_t size)
>>  }
>>
>>
>> -static in_addr_t *
>> -ngx_resolver_rotate(ngx_resolver_t *r, in_addr_t *src, ngx_uint_t n)
>> +static ngx_ipaddr_t *
>> +ngx_resolver_rotate(ngx_resolver_t *r, ngx_ipaddr_t *src, ngx_uint_t n)
>>  {
>>      void        *dst, *p;
>>      ngx_uint_t   j;
>>
>> -    dst = ngx_resolver_alloc(r, n * sizeof(in_addr_t));
>> +    dst = ngx_resolver_alloc(r, n * sizeof(ngx_ipaddr_t));
>>
>>      if (dst == NULL) {
>>          return dst;
>>  @@ -2151,12 +2260,12 @@ ngx_resolver_rotate(ngx_resolver_t *r, in_addr_t
>> *src, ngx_uint_t n)
>>      j = ngx_random() % n;
>>
>>      if (j == 0) {
>> -        ngx_memcpy(dst, src, n * sizeof(in_addr_t));
>> +        ngx_memcpy(dst, src, n * sizeof(ngx_ipaddr_t));
>>          return dst;
>>      }
>>
>> -    p = ngx_cpymem(dst, &src[j], (n - j) * sizeof(in_addr_t));
>> -    ngx_memcpy(p, src, j * sizeof(in_addr_t));
>> +    p = ngx_cpymem(dst, &src[j], (n - j) * sizeof(ngx_ipaddr_t));
>> +    ngx_memcpy(p, src, j * sizeof(ngx_ipaddr_t));
>>
>>      return dst;
>>  }
>> diff --git a/src/core/ngx_resolver.h b/src/core/ngx_resolver.h
>> index 6fd81fe..d2a4606 100644
>> --- a/src/core/ngx_resolver.h
>> +++ b/src/core/ngx_resolver.h
>> @@ -67,10 +67,11 @@ typedef struct {
>>      u_short                   qlen;
>>
>>      u_char                   *query;
>> +    ngx_int_t                 qtype;
>>
>>      union {
>> -        in_addr_t             addr;
>> -        in_addr_t            *addrs;
>> +        ngx_ipaddr_t          addr;
>> +        ngx_ipaddr_t         *addrs;
>>          u_char               *cname;
>>      } u;
>>
>> @@ -130,8 +131,8 @@ struct ngx_resolver_ctx_s {
>>       ngx_str_t                 name;
>>
>>      ngx_uint_t                naddrs;
>> -    in_addr_t                *addrs;
>> -    in_addr_t                 addr;
>> +    ngx_ipaddr_t             *addrs;
>> +    ngx_ipaddr_t              addr;
>>
>>      ngx_resolver_handler_pt   handler;
>>      void                     *data;
>>
>>
>>
>> On Wed, Jul 10, 2013 at 9:17 PM, ToSHiC <toshic.toshic at gmail.com> wrote:
>>
>>> commit 482bd2a0b6240a2b26409b9c7924ad01c814f293
>>> Author: Anton Kortunov <toshic.toshic at gmail.com>
>>> Date:   Wed Jul 10 13:21:27 2013 +0400
>>>
>>>     Added NGX_RESOLVE_* constants
>>>
>>>     Module developers can decide how to resolve hosts relating to IPv6:
>>>
>>>     NGX_RESOLVE_AAAA - try to resolve only to IPv6 address
>>>     NGX_RESOLVE_AAAA_A - IPv6 is preferred (recommended by standards)
>>>     NGX_RESOLVE_A_AAAA - IPv4 is preferred (better strategy nowadays)
>>>
>>> diff --git a/src/core/ngx_resolver.h b/src/core/ngx_resolver.h
>>> index ae34ca5..6fd81fe 100644
>>> --- a/src/core/ngx_resolver.h
>>> +++ b/src/core/ngx_resolver.h
>>> @@ -20,6 +20,15 @@
>>>  #define NGX_RESOLVE_TXT       16
>>>  #define NGX_RESOLVE_DNAME     39
>>>
>>> +#if (NGX_HAVE_INET6)
>>> +
>>> +#define NGX_RESOLVE_AAAA      28
>>> +#define NGX_RESOLVE_A_AAAA    1000
>>> +#define NGX_RESOLVE_AAAA_A    1001
>>> +#define NGX_RESOLVE_RETRY     1002
>>> +
>>> +#endif
>>> +
>>>  #define NGX_RESOLVE_FORMERR   1
>>>  #define NGX_RESOLVE_SERVFAIL  2
>>>  #define NGX_RESOLVE_NXDOMAIN  3
>>>
>>>
>>>
>>> On Wed, Jul 10, 2013 at 9:17 PM, ToSHiC <toshic.toshic at gmail.com> wrote:
>>>
>>>> Hello,
>>>>
>>>> I've split this big patch into several small patches, taking into
>>>> account your comments. I'll send each part in separate email. Here is the
>>>> first one.
>>>>
>>>> commit 597d09e7ae9247c5466b18aa2ef3f5892e61b708
>>>> Author: Anton Kortunov <toshic.toshic at gmail.com>
>>>> Date:   Wed Jul 10 13:14:52 2013 +0400
>>>>
>>>>     Added new structure ngx_ipaddr_t
>>>>
>>>>     This structure contains family field
>>>>     and the union of ipv4/ipv6 structures in_addr_t and in6_addr.
>>>>
>>>> diff --git a/src/core/ngx_inet.h b/src/core/ngx_inet.h
>>>> index 6a5a368..077ed34 100644
>>>> --- a/src/core/ngx_inet.h
>>>> +++ b/src/core/ngx_inet.h
>>>> @@ -68,6 +68,16 @@ typedef struct {
>>>>
>>>>
>>>>  typedef struct {
>>>> +    ngx_uint_t                family;
>>>> +    union {
>>>> +        in_addr_t             v4;
>>>> +#if (NGX_HAVE_INET6)
>>>> +        struct in6_addr       v6;
>>>> +#endif
>>>> +    } u;
>>>> +} ngx_ipaddr_t;
>>>> +
>>>> +typedef struct {
>>>>      struct sockaddr          *sockaddr;
>>>>      socklen_t                 socklen;
>>>>      ngx_str_t                 name;
>>>>
>>>>
>>>>
>>>> On Mon, Jun 17, 2013 at 7:30 PM, Maxim Dounin <mdounin at mdounin.ru>wrote:
>>>>
>>>>> Hello!
>>>>>
>>>>> On Fri, Jun 14, 2013 at 09:44:46PM +0400, ToSHiC wrote:
>>>>>
>>>>> > Hello,
>>>>> >
>>>>> > 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.
>>>>> >
>>>>> > 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.
>>>>> >
>>>>> > Patch is pretty big and I hope it'll not break mailing list or mail
>>>>> clients.
>>>>>
>>>>> You may want to try to split the patch into smaller patches to
>>>>> simplify review.  See also some hints here:
>>>>>
>>>>> http://nginx.org/en/docs/contributing_changes.html
>>>>>
>>>>> Some quick comments below.
>>>>>
>>>>> [...]
>>>>>
>>>>> > -        addr = ntohl(ctx->addr);
>>>>> > +failed:
>>>>> > +
>>>>> > +        //addr = ntohl(ctx->addr);
>>>>> > +        inet_ntop(ctx->addr.family, &ctx->addr.u, text,
>>>>> > NGX_SOCKADDR_STRLEN);
>>>>> >
>>>>> >          ngx_log_error(NGX_LOG_ALERT, r->log, 0,
>>>>> > -                      "could not cancel %ud.%ud.%ud.%ud resolving",
>>>>> > -                      (addr >> 24) & 0xff, (addr >> 16) & 0xff,
>>>>> > -                      (addr >> 8) & 0xff, addr & 0xff);
>>>>> > +                      "could not cancel %s resolving", text);
>>>>>
>>>>> 1. Don't use inet_ntop(), there is ngx_sock_ntop() instead.
>>>>>
>>>>> 2. Don't use C++ style ("//") comments.
>>>>>
>>>>> 3. If some data is only needed for debug logging, keep relevant
>>>>> calculations under #if (NGX_DEBUG).
>>>>>
>>>>> [...]
>>>>>
>>>>> > @@ -334,6 +362,7 @@
>>>>> > ngx_http_upstream_create_round_robin_peer(ngx_http_request_t *r,
>>>>> >              peers->peer[i].current_weight = 0;
>>>>> >              peers->peer[i].max_fails = 1;
>>>>> >              peers->peer[i].fail_timeout = 10;
>>>>> > +
>>>>> >          }
>>>>> >      }
>>>>> >
>>>>>
>>>>> Please avoid unrelated changes.
>>>>>
>>>>> [...]
>>>>>
>>>>> --
>>>>> Maxim Dounin
>>>>> http://nginx.org/en/donation.html
>>>>>
>>>>> _______________________________________________
>>>>> nginx-devel mailing list
>>>>> nginx-devel at nginx.org
>>>>> http://mailman.nginx.org/mailman/listinfo/nginx-devel
>>>>>
>>>>
>>>>
>>>
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mailman.nginx.org/pipermail/nginx-devel/attachments/20130710/db48e554/attachment-0001.html>


More information about the nginx-devel mailing list