[PATCH] Make Nginx Parse URL to IPv6

Maxim Dounin mdounin at mdounin.ru
Thu Mar 3 14:04:09 MSK 2011


Hello!

On Wed, Mar 02, 2011 at 12:55:53AM -0800, Speed First wrote:

Just a side note: you may want to avoid starting new thread with 
each new message.  That is, try hitting "reply" button.

[...]

> 4. seems there is a bug in old file's line 829. Only when uri is NULL, len =
> last - port.

You are wrong assuming anybody remember line numbers (and this is 
why asked for "-p" switch to diff, btw).

And yes, it's bug (and was recently discussed here).  Consider 
making fix a separate patch.

> 5. now "ngx_inet6_parse_url" will finally invoke new added
> "ngx_inet6_resolve_host", just like what "ngx_inet_parse_url" does. I think
> it should be done but the old code doesn't do that. Please confirm this is
> valid.

Resolve shouldn't be needed here as ngx_inet6_parse_url() is only 
invoked for ipv6-literals.  That is, only parse is required, not 
resolve.

> 6. I try to remove duplicated code as much as possible, so the
> "ngx_inet_resolve_host" is almost rewritten. Now, it will first handle the
> u->host as it's an IP; if not, it will try to resolve them.
> 
> 7. In "ngx_inet_parse_url", it firstly assume it's AF_INET and allocate the
> memory. I think it's not suitable in the new implementation, but I didn't
> touch this part of code.
> 
> 8. All the fix are tested as before (with and without "--with-ipv6"), all
> test are passed.
> 
> 9. I make this implementation based on 0.9.3. I compare the ngx_inet.c in
> official 0.9.3 and 0.9.5, and find there is a difference:
> 
> -        for (i = 0; i < u->naddrs; i++) {
> +        for (i = 0; h->h_addr_list[i] != NULL; i++) {
> 
> Since my implementation use "getaddrinfo", h_addr_list[i] will be invalid
> when "--with-ipv6" is set.

In 0.9.5 code is "for (i = 0; i < u->naddrs; i++) {", which is 
correct.  You may want to merge with 0.9.5 anyway though.

Some generic note: You may consider splitting your patch into 
series of smaller ones to make them easier for review.

Some notes are below (mostly random ones, as I have no time for 
more detailed review).

> 
> Thanks.

> --- nginx-0.9.3/src/core/ngx_inet.c	2009-12-07 07:13:46.000000000 -0800
> +++ /home/speedfirst/p4/zimbra/main/ThirdParty/nginx/nginx-0.9-zimbra/src/core/ngx_inet.c	2011-03-02 00:22:29.958260165 -0800
> @@ -11,6 +11,8 @@
>  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);
> +static ngx_int_t ngx_resolve_hostname(ngx_pool_t *pool, ngx_url_t *u);
> +static ngx_int_t ngx_inet6_resolve_host(ngx_pool_t *pool, ngx_url_t *u);
>  
>  
>  in_addr_t
> @@ -612,10 +614,9 @@
>  static ngx_int_t
>  ngx_parse_inet_url(ngx_pool_t *pool, ngx_url_t *u)
>  {
> -    u_char              *p, *host, *port, *last, *uri, *args;
> +    u_char              *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);
> @@ -677,7 +678,6 @@
>          }
>  
>          u->port = (in_port_t) n;
> -        sin->sin_port = htons((in_port_t) n);
>  
>          u->port_text.len = len;
>          u->port_text.data = port;
> @@ -734,27 +734,15 @@
>          return NGX_OK;
>      }
>  
> +    if (u->no_port) {
> +        u->port = u->default_port;
> +    }
> +
>      if (len) {
>          sin->sin_addr.s_addr = ngx_inet_addr(host, len);
>  
>          if (sin->sin_addr.s_addr == INADDR_NONE) {
> -            p = ngx_alloc(++len, pool->log);
> -            if (p == NULL) {
> -                return NGX_ERROR;
> -            }
> -
> -            (void) ngx_cpystrn(p, host, len);
> -
> -            h = gethostbyname((const char *) p);
> -
> -            ngx_free(p);
> -
> -            if (h == NULL || h->h_addr_list[0] == NULL) {
> -                u->err = "host not found";
> -                return NGX_ERROR;
> -            }
> -
> -            sin->sin_addr.s_addr = *(in_addr_t *) (h->h_addr_list[0]);
> +            return ngx_resolve_hostname(pool, u);
>          }
>  
>          if (sin->sin_addr.s_addr == INADDR_ANY) {
> @@ -766,10 +754,7 @@
>          u->wildcard = 1;
>      }
>  
> -    if (u->no_port) {
> -        u->port = u->default_port;
> -        sin->sin_port = htons(u->default_port);
> -    }
> +    sin->sin_port = htons(u->port);

This change is completely unrelated and not really needed.  You 
may want to avoid cluttering patch with such changes.

>  
>      if (u->listen) {
>          return NGX_OK;
> @@ -782,6 +767,111 @@
>      return NGX_OK;
>  }
>  
> +static ngx_int_t
> +ngx_resolve_hostname(ngx_pool_t *pool, ngx_url_t *u) {
> +    u_char               *p;
> +    ngx_uint_t            family;
> +    in_addr_t             in_addr;
> +    struct sockaddr_in   *sin;
> +
> +#if (NGX_HAVE_INET6)
> +    struct addrinfo       hints, *addrinfo;
> +    struct in6_addr       in6_addr;
> +    struct sockaddr_in6  *sin6;
> +    int                   n;
> +#else
> +    struct hostent       *h;
> +#endif
> +
> +    /* resolve the IP address through host name.
> +     * only the first IP address will be used.   */

Style problem here.

> +    p = ngx_alloc(u->host.len + 1, pool->log);
> +
> +    if (p == NULL) {
> +      return NGX_ERROR;
> +    }
> +
> +    ngx_cpystrn(p, u->host.data, u->host.len + 1);
> +
> +#if (NGX_HAVE_INET6)
> +
> +    ngx_memzero (&hints, sizeof (struct addrinfo));

Style problem here.

> +
> +    if (u->listen) {
> +       hints.ai_flags = AI_PASSIVE;
> +    } else {
> +       hints.ai_flags = AI_CANONNAME;
> +    }
> +
> +    hints.ai_protocol = IPPROTO_TCP;
> +
> +    n = getaddrinfo((const char *) p,
> +           NULL, &hints, &addrinfo);
> +
> +    if (n != NGX_OK) {
> +       u->err = "host not found";
> +       return NGX_ERROR;
> +    }
> +
> +    if (addrinfo->ai_family == AF_INET) {
> +       family = AF_INET;
> +       in_addr = ((struct sockaddr_in *) addrinfo->ai_addr)->sin_addr.s_addr;
> +
> +    } else { /* AF_INET6 */
> +       family = AF_INET6;
> +       in6_addr = ((struct sockaddr_in6 *) addrinfo->ai_addr)->sin6_addr;
> +
> +    }
> +#else
> +    h = gethostbyname((const char *) p);
> +
> +    if (h == NULL || h->h_addr_list[0] == NULL) {
> +        u->err = "host not found";
> +        return NGX_ERROR;
> +    }
> +
> +    in_addr = *(in_addr_t *) (h->h_addr_list[0]);
> +#endif
> +
> +    ngx_free(p);
> +
> +    switch (family) {
> +
> +#if (NGX_HAVE_INET6)
> +    case AF_INET6:
> +       sin6 = (struct sockaddr_in6 *) u->sockaddr;
> +       sin6->sin6_family = AF_INET6;
> +       sin6->sin6_port = htons(u->port);
> +       u->family = AF_INET6;
> +       u->socklen = sizeof (struct sockaddr_in6);
> +       ngx_memcpy(sin6->sin6_addr.s6_addr, in6_addr.s6_addr, 16);
> +
> +       if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
> +           u->wildcard = 1;
> +       }
> +       break;
> +#endif
> +
> +    default: /* AF_INET */
> +       sin = (struct sockaddr_in *) u->sockaddr;
> +       sin->sin_family = AF_INET;
> +       sin->sin_port = htons(u->port);
> +       u->family = AF_INET;
> +       u->socklen = sizeof (struct sockaddr_in);
> +       sin->sin_addr.s_addr = in_addr;
> +       if (sin->sin_addr.s_addr == INADDR_ANY) {
> +           u->wildcard = 1;
> +       }
> +       break;
> +    }
> +
> +    if (u->listen) {
> +        return NGX_OK;
> +    }
> +
> +    return ngx_inet_resolve_host(pool, u);

It's not clear why you are trying to do two different "resolve" in 
a raw, each of them calling gethostbyname/getaddrinfo.  Looks silly.

> +}
> +
>  
>  static ngx_int_t
>  ngx_parse_inet6_url(ngx_pool_t *pool, ngx_url_t *u)
> @@ -826,7 +916,11 @@
>          if (*port == ':') {
>              port++;
>  
> -            len = last - port;
> +            if (uri) {
> +                len = uri - port;
> +            } else {
> +                len = last - port;
> +            }
>  
>              if (len == 0) {
>                  u->err = "invalid port";

This hunk is good one.  Please submit as a separate patch.

> @@ -881,7 +975,11 @@
>          sin6->sin6_port = htons(u->default_port);
>      }
>  
> -    return NGX_OK;
> +    if (u->listen) {
> +        return NGX_OK;
> +    }
> +
> +    return ngx_inet6_resolve_host(pool, u);

As already noted above - shouldn't be needed.

>  
>  #else
>  
> @@ -901,107 +999,245 @@
>      in_port_t            port;
>      in_addr_t            in_addr;
>      ngx_uint_t           i;
> -    struct hostent      *h;
>      struct sockaddr_in  *sin;
> +    struct sockaddr     *sa;
>  
> -    /* AF_INET only */
> +#if (NGX_HAVE_INET6)
> +    int                  family = -1;
> +    struct addrinfo      hints, *addrinfo, *item;
> +    struct in6_addr      in6_addr;
> +    struct sockaddr_in6 *sin6;
> +    int                  n;
> +
> +#else
> +    struct hostent      *h;
> +#endif
> +
> +    if (*u->host.data == '[') {
> +        return ngx_inet6_resolve_host(pool, u);
> +    }
>  
>      port = htons(u->port);
>  
>      in_addr = ngx_inet_addr(u->host.data, u->host.len);
>  
> -    if (in_addr == INADDR_NONE) {
> -        host = ngx_alloc(u->host.len + 1, pool->log);
> -        if (host == NULL) {
> -            return NGX_ERROR;
> +    if (in_addr != INADDR_NONE) {
> +
> +        u->addrs = ngx_pcalloc(pool, sizeof(ngx_addr_t));
> +        if (u->addrs == NULL) {
> +           return NGX_ERROR;
>          }
>  
> -        (void) ngx_cpystrn(host, u->host.data, u->host.len + 1);
> +        u->naddrs = 1;
>  
> -        h = gethostbyname((char *) host);
> +        sin = ngx_pcalloc(pool, sizeof(struct sockaddr_in));
> +        if (sin == NULL) {
> +           return NGX_ERROR;
> +        }
> +        sin->sin_family = AF_INET;
> +        sin->sin_port = port;
> +        sin->sin_addr.s_addr = in_addr;
>  
> -        ngx_free(host);
> +        u->addrs[0].sockaddr = (struct sockaddr *) sin;
> +        u->addrs[0].socklen = sizeof(struct sockaddr_in);
>  
> -        if (h == NULL || h->h_addr_list[0] == NULL) {
> -            u->err = "host not found";
> +        p = ngx_pnalloc(pool, u->host.len + sizeof(":65535") - 1);
> +        if (p == NULL) {
>              return NGX_ERROR;
>          }
>  
> -        if (u->one_addr == 0) {
> -            for (i = 0; h->h_addr_list[i] != NULL; i++) { /* void */ }
> +        u->addrs[0].name.len = ngx_sprintf(p, "%V:%d",
> +                                       &u->host, ntohs(port)) - p;
> +        u->addrs[0].name.data = p;
>  
> -        } else {
> -            i = 1;
> -        }
> +        return NGX_OK;
> +    }
> +
> +    /* need to resolve host name */
> +    host = ngx_alloc(u->host.len + 1, pool->log);
> +    if (host == NULL) {
> +        return NGX_ERROR;
> +    }
>  
> -        /* MP: ngx_shared_palloc() */
> +    (void) ngx_cpystrn(host, u->host.data, u->host.len + 1);
>  
> -        u->addrs = ngx_pcalloc(pool, i * sizeof(ngx_addr_t));
> -        if (u->addrs == NULL) {
> -            return NGX_ERROR;
> -        }
> +#if (NGX_HAVE_INET6)
>  
> -        u->naddrs = i;
> +    ngx_memzero (&hints, sizeof (struct addrinfo));
>  
> -        for (i = 0; h->h_addr_list[i] != NULL; i++) {
> +    /* if the address is for listen, it won't enter this reslove function */
> +    hints.ai_flags = AI_CANONNAME;
> +    hints.ai_protocol = IPPROTO_TCP;
>  
> -            sin = ngx_pcalloc(pool, sizeof(struct sockaddr_in));
> -            if (sin == NULL) {
> -                return NGX_ERROR;
> -            }
> +    n = getaddrinfo((const char *) host,
> +          NULL, &hints, &addrinfo);
>  
> -            sin->sin_family = AF_INET;
> -            sin->sin_port = port;
> -            sin->sin_addr.s_addr = *(in_addr_t *) (h->h_addr_list[i]);
> +    if (n != NGX_OK) {
> +        u->err = "host not found";
> +        return NGX_ERROR;
> +    }
>  
> -            u->addrs[i].sockaddr = (struct sockaddr *) sin;
> -            u->addrs[i].socklen = sizeof(struct sockaddr_in);
> +    if (u->one_addr == 0) {
> +        item = addrinfo;
> +        for (i = 0; item != NULL; i++, item = item->ai_next) { /* void */ }
>  
> -            len = NGX_INET_ADDRSTRLEN + sizeof(":65535") - 1;
> +    } else {
> +        i = 1;
> +    }
>  
> -            p = ngx_pnalloc(pool, len);
> -            if (p == NULL) {
> -                return NGX_ERROR;
> -            }
> +#else
>  
> -            len = ngx_sock_ntop((struct sockaddr *) sin, p, len, 1);
> +    h = gethostbyname((char *) host);
>  
> -            u->addrs[i].name.len = len;
> -            u->addrs[i].name.data = p;
> -        }
> +    if (h == NULL || h->h_addr_list[0] == NULL) {
> +        u->err = "host not found";
> +        return NGX_ERROR;
> +    }
> +
> +    if (u->one_addr == 0) {
> +        for (i = 0; h->h_addr_list[i] != NULL; i++) { /* void */ }
>  
>      } else {
> +        i = 1;
> +    }
> +#endif
>  
> -        /* MP: ngx_shared_palloc() */
> +    ngx_free (host);
>  
> -        u->addrs = ngx_pcalloc(pool, sizeof(ngx_addr_t));
> -        if (u->addrs == NULL) {
> -            return NGX_ERROR;
> +    /* MP: ngx_shared_palloc() */
> +
> +    u->addrs = ngx_pcalloc(pool, i * sizeof(ngx_addr_t));
> +    if (u->addrs == NULL) {
> +        return NGX_ERROR;
> +    }
> +
> +    u->naddrs = i;
> +
> +    for (i = 0; i < u->naddrs; i++) {
> +
> +#if (NGX_HAVE_INET6)
> +        if (addrinfo->ai_family == AF_INET) {
> +            family = AF_INET;
> +            sin = ngx_pcalloc(pool, sizeof(struct sockaddr_in));
> +            if (sin == NULL) {
> +                return NGX_ERROR;
> +            }
> +            in_addr = ((struct sockaddr_in *) addrinfo->ai_addr)->sin_addr.s_addr;
> +            sin->sin_addr.s_addr = in_addr;
> +        } else {
> +            family = AF_INET6;
> +            sin6 = ngx_pcalloc(pool, sizeof(struct sockaddr_in6));
> +            if (sin6 == NULL) {
> +                return NGX_ERROR;
> +            }
> +            in6_addr = ((struct sockaddr_in6 *) addrinfo->ai_addr)->sin6_addr;
> +            ngx_memcpy(sin6->sin6_addr.s6_addr, in6_addr.s6_addr, 16);
>          }
>  
> +        addrinfo = addrinfo->ai_next;
> +
> +#else
>          sin = ngx_pcalloc(pool, sizeof(struct sockaddr_in));
> -        if (sin == NULL) {
> +        sin->sin_addr.s_addr = *(in_addr_t *) (h->h_addr_list[i]);
> +#endif
> +
> +#if (NGX_HAVE_INET6)
> +        if (family == AF_INET6) {
> +
> +            sin6->sin6_family = AF_INET6;
> +            sin6->sin6_port = port;
> +            sa = (struct sockaddr *)sin6;
> +            u->addrs[i].sockaddr = sa;
> +            u->addrs[i].socklen = sizeof(struct sockaddr_in6);
> +            len = NGX_INET6_ADDRSTRLEN + sizeof(":65535") - 1;
> +        }
> +        else
> +#endif
> +        {
> +            sin->sin_family = AF_INET;
> +            sin->sin_port = port;
> +            sa = (struct sockaddr *)sin;
> +            u->addrs[i].sockaddr = sa;
> +            u->addrs[i].socklen = sizeof(struct sockaddr_in);
> +            len = NGX_INET_ADDRSTRLEN + sizeof(":65535") - 1;
> +        }
> +
> +        p = ngx_pnalloc(pool, len);
> +        if (p == NULL) {
>              return NGX_ERROR;
>          }
>  
> -        u->naddrs = 1;
> +        len = ngx_sock_ntop(sa, p, len, port);
>  
> -        sin->sin_family = AF_INET;
> -        sin->sin_port = port;
> -        sin->sin_addr.s_addr = in_addr;
> +        u->addrs[i].name.len = len;
> +        u->addrs[i].name.data = p;
> +    }
>  
> -        u->addrs[0].sockaddr = (struct sockaddr *) sin;
> -        u->addrs[0].socklen = sizeof(struct sockaddr_in);
> +    return NGX_OK;
> +}
>  
> -        p = ngx_pnalloc(pool, u->host.len + sizeof(":65535") - 1);
> -        if (p == NULL) {
> +ngx_int_t
> +ngx_inet6_resolve_host(ngx_pool_t * pool, ngx_url_t * u)
> +{

"static" is missing here (on the other hand, the whole function 
looks strange, see above)

> +#if (NGX_HAVE_INET6)
> +    u_char              *p;
> +    struct in6_addr      in6addr;
> +    struct sockaddr_in6 *sin6;
> +    ngx_str_t           *host;
> +
> +    host = &u->host;
> +
> +    if (*(host->data) == '[') {
> +        if(*(host->data + host->len - 1) == ']') {
> +            host->data++;
> +            host->len -= 2;
> +        } else {
> +            u->err = "invalid host";
>              return NGX_ERROR;
>          }
> +    }
>  
> -        u->addrs[0].name.len = ngx_sprintf(p, "%V:%d",
> -                                           &u->host, ntohs(port)) - p;
> -        u->addrs[0].name.data = p;
> +    if (ngx_inet6_addr(u->host.data,
> +            u->host.len, (u_char *)&in6addr) == NGX_ERROR) {
> +        u->err = "invalid host";
> +        return NGX_ERROR;
> +    }
> +
> +    sin6 = ngx_pcalloc(pool, sizeof(struct sockaddr_in6));
> +    if (sin6 == NULL) {
> +        return NGX_ERROR;
> +    }
> +    sin6->sin6_family = AF_INET6;
> +    sin6->sin6_port = htons(u->port);
> +    ngx_memcpy(sin6->sin6_addr.s6_addr, in6addr.s6_addr, 16);
> +
> +    u->addrs = ngx_pcalloc(pool, sizeof(ngx_addr_t));
> +
> +    if(u->addrs == NULL) {
> +        return NGX_ERROR;
> +    }
> +
> +    u->naddrs = 1;
> +
> +    u->addrs[0].sockaddr = (struct sockaddr *) sin6;
> +    u->addrs[0].socklen = sizeof(struct sockaddr_in6);
> +
> +    p = ngx_pnalloc(pool, u->host.len + sizeof(":65535") - 1);
> +    if (p == NULL) {
> +        return NGX_ERROR;
>      }
>  
> +    u->addrs[0].name.len = ngx_sprintf(p, "[%V]:%d",
> +                                   &u->host, u->port) - p;
> +    u->addrs[0].name.data = p;
> +
>      return NGX_OK;
> +
> +#else
> +
> +    u->err = "the INET6 sockets are not supported on this platform";
> +
> +    return NGX_ERROR;
> +
> +#endif
>  }

Maxim Dounin



More information about the nginx-devel mailing list