Implementation of parsing URL to IPv6

speedfirst nginx-forum at nginx.us
Mon Feb 28 14:39:00 MSK 2011


Hey, guys. I've implement a parse URL to IPv6 function which is
discussed in "http://forum.nginx.org/read.php?2,177146,177190". I change
"ngx_parse_url" and add an function "ngx_parse_host" to resolve a host
(IP or text) to either IPv4 or IPv6 functions. And I change
"ngx_inet_resolve_host" to let it accept IPv4/IPv6. Besides I add
another function "ngx_inet_sock_addr" to parse string with format
"ipv4:port" and "[ipv6]:port" to the addr structure. Seems I can't
upload the attachment so I past them here. I've do the unit test to
them. Hope you can take regression test and merge them to the nginx main
branch.

If u wish, I can also past the unit test I write. (I think send via
email would be more convenient)

ngx_int_t
ngx_inet_sock_addr (u_char * p, size_t len, struct sockaddr * sockaddr)
{
    u_char               *port, *last;
    ngx_int_t             n;
    struct sockaddr_in   *sin;

#if (NGX_HAVE_INET6)
    struct sockaddr_in6  *sin6;
    u_char               *q;
#endif

    if (len == 0) {
        return NGX_ERROR;
    }

    last = p + len;

    port = NULL;

#if (NGX_HAVE_INET6)

    if (*p == '[') {

        p++;

        q = ngx_strlchr(p, last, ']');

        if (q == NULL) {
            return NGX_ERROR;
        }

        if (q < last - 2 && *(q + 1) == ':') {
            port = q + 2;
        } else {
            return NGX_ERROR;
        }

        sin6 = (struct sockaddr_in6 *)sockaddr;

        sin6->sin6_family = AF_INET6;

        if (ngx_inet6_addr(p, q - p, sin6->sin6_addr.s6_addr) ==
NGX_ERROR) {
            return NGX_ERROR;
        }

        n = ngx_atoi(port, last - port);

        if (n == NGX_ERROR || n < 1 || n > 65535) {
            return NGX_ERROR;
        }

        sin6->sin6_port = htons(n);

    }
    else
#endif
    {
        port = ngx_strlchr(p, last, ':');

        if (port == NULL) {
            return NGX_ERROR;
        }

        sin = (struct sockaddr_in *)sockaddr;

        sin->sin_family = AF_INET;

        sin->sin_addr.s_addr = ngx_inet_addr (p, port - p);

        if (sin->sin_addr.s_addr == INADDR_NONE) {
            return NGX_ERROR;
        }

        port++;

        n = ngx_atoi(port, last - port);

        if (n == NGX_ERROR || n < 1 || n > 65535) {
            return NGX_ERROR;
        }

        sin->sin_port = htons(n);

    }

    return NGX_OK;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////
ngx_int_t
ngx_parse_url(ngx_pool_t *pool, ngx_url_t *u)
{
    u_char   *p, *host, *port, *last, *uri, *args;
    size_t    len;
    ngx_int_t n;
    struct sockaddr_in  *sin;

#if (NGX_HAVE_INET6)
    struct sockaddr_in6 *sin6;
    ngx_flag_t           ipv6 = 0;
#endif

    p = u->url.data;

    if (ngx_strncasecmp(p, (u_char *) "unix:", 5) == 0) {
        return ngx_parse_unix_domain_url(pool, u);
    }

    if ((p[0] == ':' || p[0] == '/') && !u->listen) {
        u->err = "invalid host";
        return NGX_ERROR;
    }

    host = u->url.data;

    last = host + u->url.len;

    len = 0;

#if (NGX_HAVE_INET6)
    if (host[0] == '[') {

        ipv6 = 1;

        host = u->url.data + 1;

        p = ngx_strlchr(host, last, ']');

        if (p == NULL) {
            u->err = "invalid host";
            return NGX_ERROR;
        }

        u->family = AF_INET6;

    }
#endif

    port = ngx_strlchr(p, last, ':');

    uri = ngx_strlchr(p, last, '/');

    args = ngx_strlchr(p, last, '?');

    if (args && (uri == NULL || args < uri)) {
        uri = args;
    }

    if (uri) {
        if (u->listen || !u->uri_part) {
            u->err = "invalid url to listen";
            return NGX_ERROR;
        }

        u->uri.len = last - uri;
        u->uri.data = uri;

        last = uri;

        if (uri < port) {
            port = NULL;
        }
    }

    if (port) {
        port++;

        len = last - port;

        if (len == 0) {
            u->err = "invalid port";
            return NGX_ERROR;
        }

        n = ngx_atoi(port, len);

        if (n < 1 || n > 65536) {
            u->err = "invalid port";
            return NGX_ERROR;
        }

        u->port = (in_port_t) n;
        u->port_text.len = len;
        u->port_text.data = port;

        last = port - 1;

    } else {
        if (uri == NULL && u->listen) {
            /* test value as port only */

            n = ngx_atoi(u->url.data, u->url.len);

            if (n < 1 || n > 65536) {
                u->err = "invalid port";
                return NGX_ERROR;
            }

            u->family = AF_INET;
            u->port = (in_port_t) n;
            sin = (struct sockaddr_in *)u->sockaddr;
            sin->sin_family = AF_INET;
            sin->sin_addr.s_addr = INADDR_ANY;
            sin->sin_port = htons((in_port_t) n);
            u->port_text.len = len;
            u->port_text.data = port;
            u->socklen = sizeof (struct sockaddr_in);
            u->wildcard = 1;

            return NGX_OK;

        } else {
            u->no_port = 1;
        }
    }

#if (NGX_HAVE_INET6)
    if (ipv6) {
        if (*(last - 1) == ']' && last > host) {
            last--;
        } else {
            u->err = "invalid host";
            return NGX_ERROR;
        }
    }
#endif

    len = last - host;

    if (len == 1 && *host == '*') {
        len = 0;
        u->family = AF_INET;
        u->socklen = sizeof (struct sockaddr_in);
        u->wildcard = 1;
        sin = (struct sockaddr_in *)u->sockaddr;
        sin->sin_family = AF_INET;
        sin->sin_addr.s_addr = INADDR_ANY;
    }

    u->host.len = len;
    u->host.data = host;

    if (u->no_resolve) {
        return NGX_OK;
    }

    if(u->host.len > 0 && ngx_parse_host(pool, u) == NGX_ERROR) {
        u->err = "invalid host";
        return NGX_ERROR;
    }

    if (u->no_port) {
        u->port = u->default_port;
    }

#if (NGX_HAVE_INET6)
    if (u->family == AF_INET6) {
        sin6 = (struct sockaddr_in6 *)u->sockaddr;
        sin6->sin6_port = htons (u->port);

    }
    else
#endif
    {
        sin = (struct sockaddr_in *)u->sockaddr;
        sin->sin_port = htons (u->port);
    }

    if (u->listen) {
        return NGX_OK;
    }

    if (ngx_inet_resolve_host(pool, u) != NGX_OK) {
        return NGX_ERROR;
    }

    return NGX_OK;
}


///////////////////////////////////////////////////////////////////////////////////////////////////////
static ngx_int_t
ngx_parse_host(ngx_pool_t *pool, ngx_url_t *u) {
    u_char                *p;
    ngx_uint_t            family, n;
    in_addr_t             inaddr;
    struct sockaddr_in   *sin;
    struct addrinfo       hints, *addrinfo;

#if (NGX_HAVE_INET6)
    struct in6_addr       inaddr6;
    struct sockaddr_in6  *sin6;

   if (u->family == AF_INET6) {
       /* u->family has been set to AF_INET6 means the host
        * to be parsed should be IPv6 address so no need to parse
        * it as IPv4 or resolve host
        */
       ngx_memzero(inaddr6.s6_addr, sizeof(struct in6_addr));
       if (ngx_inet6_addr(u->host.data, u->host.len, inaddr6.s6_addr) ==
NGX_OK) {
           family = AF_INET6;
           goto done;
       } else {
           u->err = "invalid host";
           return NGX_ERROR;
       }
   }
#endif

   inaddr = ngx_inet_addr(u->host.data, u->host.len);

   if (inaddr != INADDR_NONE) {
       family = AF_INET;

#if (NGX_HAVE_INET6)
   } else if (ngx_inet6_addr(u->host.data, u->host.len, inaddr6.s6_addr)
== NGX_OK) {
       family = AF_INET6;

#endif
   } else {
       /* resolve the IP address through host name
          only the first IP address will be used   */
       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);

       ngx_memzero (&hints, sizeof (struct addrinfo));

       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);

       ngx_free (p);

       if (n != NGX_OK) {
           u->err = "error in host resolve";
           return NGX_ERROR;
       }

       if (addrinfo->ai_family == AF_INET) {
           family = AF_INET;
           inaddr = ((struct sockaddr_in *)
addrinfo->ai_addr)->sin_addr.s_addr;

#if (NGX_HAVE_INET6)
       } else if (addrinfo->ai_family == AF_INET6) {
           family = AF_INET6;
           inaddr6 = ((struct sockaddr_in6 *)
addrinfo->ai_addr)->sin6_addr;

#endif
       } else {
           u->err = "unknown address family";
           return NGX_ERROR;
       }
   }

#if (NGX_HAVE_INET6)
   done:
#endif

   switch (family) {

#if (NGX_HAVE_INET6)
   case AF_INET6:
       sin6 = (struct sockaddr_in6 *) u->sockaddr;
       sin6->sin6_family = AF_INET6;
       u->family = AF_INET6;
       u->socklen = sizeof (struct sockaddr_in6);
       ngx_memcpy(sin6->sin6_addr.s6_addr, inaddr6.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;
       u->family = AF_INET;
       u->socklen = sizeof (struct sockaddr_in);
       sin->sin_addr.s_addr = inaddr;
       if (sin->sin_addr.s_addr == INADDR_ANY) {
           u->wildcard = 1;
       }
       break;
   }

   return NGX_OK;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////
ngx_int_t
ngx_inet_resolve_host(ngx_pool_t *pool, ngx_url_t *u)
{
    u_char              *p, *host;
    size_t               len;
    in_port_t            port;
    in_addr_t            inaddr;
    ngx_uint_t           i, n;
    struct addrinfo      hints, *addrinfo, *item;
    struct sockaddr_in  *sin;

#if (NGX_HAVE_INET6)
    struct in6_addr      inaddr6;
    struct sockaddr_in6 *sin6;

    /*
     * prevent MSVC8 waring:
     *    potentially uninitialized local variable 'inaddr6' used
     */
    ngx_memzero(inaddr6.s6_addr, sizeof(struct in6_addr));
#endif

    port = htons(u->port);

    inaddr = ngx_inet_addr(u->host.data, u->host.len);

    if (inaddr != INADDR_NONE) {
        /* MP: ngx_shared_palloc() */

        u->addrs = ngx_pcalloc(pool, sizeof(ngx_addr_t));
        if (u->addrs == NULL) {
            return NGX_ERROR;
        }

        sin = ngx_pcalloc(pool, sizeof(struct sockaddr_in));
        if (sin == NULL) {
            return NGX_ERROR;
        }

        u->naddrs = 1;

        sin->sin_family = AF_INET;
        sin->sin_port = port;
        sin->sin_addr.s_addr = inaddr;

        u->addrs[0].sockaddr = (struct sockaddr *) sin;
        u->addrs[0].socklen = sizeof(struct sockaddr_in);

        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, ntohs(port)) - p;
        u->addrs[0].name.data = p;

        return NGX_OK;
    }

#if (NGX_HAVE_INET6)
    if(ngx_inet6_addr(u->host.data, u->host.len, inaddr6.s6_addr) ==
NGX_OK) {
        u->addrs = ngx_pcalloc(pool, sizeof(ngx_addr_t));
        if (u->addrs == NULL) {
            return NGX_ERROR;
        }

        sin6 = ngx_pcalloc(pool, sizeof(struct sockaddr_in6));
        if (sin6 == NULL) {
            return NGX_ERROR;
        }

        u->naddrs = 1;

        sin6->sin6_family = AF_INET6;
        sin6->sin6_port = port;
        ngx_memcpy(sin6->sin6_addr.s6_addr, inaddr6.s6_addr, 16);
        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, ntohs(port)) - p;
        u->addrs[0].name.data = p;

        return NGX_OK;
    }
#endif

    /* resolve all the IP address for this host */
    host = ngx_alloc(u->host.len + 1, pool->log);
    if (host == NULL) {
        return NGX_ERROR;
    }
    ngx_cpystrn(host, u->host.data, u->host.len + 1);

    ngx_memzero (&hints, sizeof (struct addrinfo));

    /* if the address is for listen, it won't enter this reslove
function */
    hints.ai_flags = AI_CANONNAME;
    hints.ai_protocol = IPPROTO_TCP;

    n = getaddrinfo((const char *) host,
          NULL, &hints, &addrinfo);

    ngx_free (host);

    if (n != NGX_OK) {
      u->err = "error in host resolve";
      return NGX_ERROR;
    }

    i = 0;

    if (u->one_addr == 0) {
        item = addrinfo;
        for (i = 0; item != NULL; i++, item = item->ai_next) { /* void
*/ }

    } else {
        i = 1;
    }

    /* 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++, addrinfo = addrinfo->ai_next) {

        if (addrinfo->ai_family == AF_INET) {
            sin = ngx_pcalloc(pool, sizeof(struct sockaddr_in));
            if (sin == NULL) {
                return NGX_ERROR;
            }

            sin->sin_family = AF_INET;
            sin->sin_port = port;
            inaddr = ((struct sockaddr_in *)
addrinfo->ai_addr)->sin_addr.s_addr;
            sin->sin_addr.s_addr = inaddr;
            u->addrs[i].sockaddr = (struct sockaddr *) sin;
            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;
            }

            len = ngx_sock_ntop((struct sockaddr *) sin, p, len,
sin->sin_port);

            u->addrs[i].name.len = len;
            u->addrs[i].name.data = p;

#if (NGX_HAVE_INET6)
        } else if (addrinfo->ai_family == AF_INET6) {
            sin6 = ngx_pcalloc(pool, sizeof(struct sockaddr_in6));
            if (sin6 == NULL) {
                return NGX_ERROR;
            }

            sin6->sin6_family = AF_INET6;
            sin6->sin6_port = port;
            inaddr6 = ((struct sockaddr_in6 *)
addrinfo->ai_addr)->sin6_addr;
            ngx_memcpy(sin6->sin6_addr.s6_addr, inaddr6.s6_addr, 16);
            u->addrs[i].sockaddr = (struct sockaddr *) sin6;
            u->addrs[i].socklen = sizeof(struct sockaddr_in6);

            len = NGX_INET6_ADDRSTRLEN + sizeof(":65535") - 1;
            p = ngx_pnalloc(pool, len);
            if (p == NULL) {
                return NGX_ERROR;
            }

            len = ngx_sock_ntop((struct sockaddr *) sin6, p, len,
sin6->sin6_port);

            u->addrs[i].name.len = len;
            u->addrs[i].name.data = p;
#endif
        } else {
            u->err = "unknown address family";
            return NGX_ERROR;
        }
    }

    return NGX_OK;
}

Posted at Nginx Forum: http://forum.nginx.org/read.php?2,179085,179085#msg-179085




More information about the nginx mailing list