Initial version.
This commit is contained in:
973
src/nxt_sockaddr.c
Normal file
973
src/nxt_sockaddr.c
Normal file
@@ -0,0 +1,973 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) NGINX, Inc.
|
||||
*/
|
||||
|
||||
#include <nxt_main.h>
|
||||
|
||||
|
||||
#if (NXT_INET6)
|
||||
static u_char *nxt_inet6_ntop(u_char *addr, u_char *buf, u_char *end);
|
||||
#endif
|
||||
|
||||
static nxt_int_t nxt_job_sockaddr_unix_parse(nxt_job_sockaddr_parse_t *jbs);
|
||||
static nxt_int_t nxt_job_sockaddr_inet6_parse(nxt_job_sockaddr_parse_t *jbs);
|
||||
static nxt_int_t nxt_job_sockaddr_inet_parse(nxt_job_sockaddr_parse_t *jbs);
|
||||
|
||||
|
||||
nxt_sockaddr_t *
|
||||
nxt_sockaddr_alloc(nxt_mem_pool_t *mp, socklen_t len)
|
||||
{
|
||||
nxt_sockaddr_t *sa;
|
||||
|
||||
/*
|
||||
* The current struct sockaddr's define 32-bit fields at maximum
|
||||
* and may define 64-bit AF_INET6 fields in the future. Alignment
|
||||
* of memory allocated by nxt_mem_zalloc() is enough for these fields.
|
||||
* If 128-bit alignment will be required then nxt_mem_malloc() and
|
||||
* nxt_memzero() should be used instead.
|
||||
*/
|
||||
sa = nxt_mem_zalloc(mp, offsetof(nxt_sockaddr_t, u) + len);
|
||||
|
||||
if (nxt_fast_path(sa != NULL)) {
|
||||
nxt_socklen_set(sa, len);
|
||||
}
|
||||
|
||||
return sa;
|
||||
}
|
||||
|
||||
|
||||
nxt_sockaddr_t *
|
||||
nxt_sockaddr_create(nxt_mem_pool_t *mp, struct sockaddr *sockaddr,
|
||||
socklen_t len)
|
||||
{
|
||||
size_t size, copy;
|
||||
nxt_sockaddr_t *sa;
|
||||
|
||||
size = len;
|
||||
copy = len;
|
||||
|
||||
#if (NXT_HAVE_UNIX_DOMAIN)
|
||||
|
||||
/*
|
||||
* Unspecified Unix domain sockaddr_un form and length are very
|
||||
* platform depended (see comment in unix/socket.h). Here they are
|
||||
* normalized to the sockaddr_un with single zero byte sun_path[].
|
||||
*/
|
||||
|
||||
if (size <= offsetof(struct sockaddr_un, sun_path)) {
|
||||
/*
|
||||
* Small socket length means a short unspecified Unix domain
|
||||
* socket address:
|
||||
*
|
||||
* getsockname() and getpeername() on OpenBSD prior to 5.3
|
||||
* return zero length and does not update a passed sockaddr
|
||||
* buffer at all.
|
||||
*
|
||||
* Linux returns length equal to 2, i.e. sockaddr_un without
|
||||
* sun_path[], unix(7):
|
||||
*
|
||||
* unnamed: A stream socket that has not been bound
|
||||
* to a pathname using bind(2) has no name. Likewise,
|
||||
* the two sockets created by socketpair(2) are unnamed.
|
||||
* When the address of an unnamed socket is returned by
|
||||
* getsockname(2), getpeername(2), and accept(2), its
|
||||
* length is sizeof(sa_family_t), and sun_path should
|
||||
* not be inspected.
|
||||
*/
|
||||
size = offsetof(struct sockaddr_un, sun_path) + 1;
|
||||
|
||||
#if !(NXT_LINUX)
|
||||
|
||||
} else if (sockaddr->sa_family == AF_UNIX && sockaddr->sa_data[0] == '\0') {
|
||||
/*
|
||||
* Omit nonsignificant zeros of the unspecified Unix domain socket
|
||||
* address. This test is disabled for Linux since Linux abstract
|
||||
* socket address also starts with zero. However Linux unspecified
|
||||
* Unix domain socket address is short and is handled above.
|
||||
*/
|
||||
size = offsetof(struct sockaddr_un, sun_path) + 1;
|
||||
copy = size;
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* NXT_HAVE_UNIX_DOMAIN */
|
||||
|
||||
sa = nxt_sockaddr_alloc(mp, size);
|
||||
|
||||
if (nxt_fast_path(sa != NULL)) {
|
||||
|
||||
nxt_memcpy(&sa->u.sockaddr, sockaddr, copy);
|
||||
|
||||
#if (NXT_SOCKADDR_SA_LEN)
|
||||
|
||||
/* Update shortcut sockaddr length overwritten by nxt_memcpy(). */
|
||||
nxt_socklen_set(sa, size);
|
||||
|
||||
#endif
|
||||
|
||||
#if (NXT_HAVE_UNIX_DOMAIN && NXT_OPENBSD)
|
||||
|
||||
if (len == 0) {
|
||||
sa->u.sockaddr.sa_family = AF_UNIX;
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
return sa;
|
||||
}
|
||||
|
||||
|
||||
nxt_sockaddr_t *
|
||||
nxt_sockaddr_copy(nxt_mem_pool_t *mp, nxt_sockaddr_t *src)
|
||||
{
|
||||
size_t len;
|
||||
nxt_sockaddr_t *dst;
|
||||
|
||||
len = offsetof(nxt_sockaddr_t, u) + nxt_socklen(src);
|
||||
|
||||
dst = nxt_mem_alloc(mp, len);
|
||||
|
||||
if (nxt_fast_path(dst != NULL)) {
|
||||
nxt_memcpy(dst, src, len);
|
||||
}
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
|
||||
nxt_sockaddr_t *
|
||||
nxt_getsockname(nxt_mem_pool_t *mp, nxt_socket_t s)
|
||||
{
|
||||
int ret;
|
||||
socklen_t socklen;
|
||||
nxt_sockaddr_buf_t sockaddr;
|
||||
|
||||
socklen = NXT_SOCKADDR_LEN;
|
||||
|
||||
ret = getsockname(s, &sockaddr.buf, &socklen);
|
||||
|
||||
if (nxt_fast_path(ret == 0)) {
|
||||
return nxt_sockaddr_create(mp, &sockaddr.buf, socklen);
|
||||
}
|
||||
|
||||
nxt_thread_log_error(NXT_LOG_ERR, "getsockname(%d) failed %E",
|
||||
s, nxt_errno);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
nxt_int_t
|
||||
nxt_sockaddr_text(nxt_mem_pool_t *mp, nxt_sockaddr_t *sa, nxt_bool_t port)
|
||||
{
|
||||
size_t len;
|
||||
u_char *p;
|
||||
u_char buf[NXT_SOCKADDR_STR_LEN + NXT_SOCKPORT_STR_LEN];
|
||||
|
||||
len = NXT_SOCKADDR_STR_LEN + NXT_SOCKPORT_STR_LEN;
|
||||
|
||||
len = nxt_sockaddr_ntop(sa, buf, buf + len, port);
|
||||
|
||||
p = nxt_mem_alloc(mp, len);
|
||||
|
||||
if (nxt_fast_path(p != NULL)) {
|
||||
|
||||
sa->text = p;
|
||||
sa->text_len = len;
|
||||
nxt_memcpy(p, buf, len);
|
||||
|
||||
return NXT_OK;
|
||||
}
|
||||
|
||||
return NXT_ERROR;
|
||||
}
|
||||
|
||||
|
||||
uint32_t
|
||||
nxt_sockaddr_port(nxt_sockaddr_t *sa)
|
||||
{
|
||||
uint32_t port;
|
||||
|
||||
switch (sa->u.sockaddr.sa_family) {
|
||||
|
||||
#if (NXT_INET6)
|
||||
|
||||
case AF_INET6:
|
||||
port = sa->u.sockaddr_in6.sin6_port;
|
||||
break;
|
||||
|
||||
#endif
|
||||
|
||||
#if (NXT_HAVE_UNIX_DOMAIN)
|
||||
|
||||
case AF_UNIX:
|
||||
return 0;
|
||||
|
||||
#endif
|
||||
|
||||
default:
|
||||
port = sa->u.sockaddr_in.sin_port;
|
||||
break;
|
||||
}
|
||||
|
||||
return ntohs((uint16_t) port);
|
||||
}
|
||||
|
||||
|
||||
nxt_bool_t
|
||||
nxt_sockaddr_cmp(nxt_sockaddr_t *sa1, nxt_sockaddr_t *sa2)
|
||||
{
|
||||
if (nxt_socklen(sa1) != nxt_socklen(sa2)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (sa1->type != sa2->type) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (sa1->u.sockaddr.sa_family != sa2->u.sockaddr.sa_family) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* sockaddr struct's cannot be compared in whole since kernel
|
||||
* may fill some fields in inherited sockaddr struct's.
|
||||
*/
|
||||
|
||||
switch (sa1->u.sockaddr.sa_family) {
|
||||
|
||||
#if (NXT_INET6)
|
||||
|
||||
case AF_INET6:
|
||||
if (sa1->u.sockaddr_in6.sin6_port != sa2->u.sockaddr_in6.sin6_port) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (nxt_memcmp(&sa1->u.sockaddr_in6.sin6_addr,
|
||||
&sa2->u.sockaddr_in6.sin6_addr, 16)
|
||||
!= 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
||||
#endif
|
||||
|
||||
#if (NXT_HAVE_UNIX_DOMAIN)
|
||||
|
||||
case AF_UNIX:
|
||||
{
|
||||
size_t len;
|
||||
|
||||
len = nxt_socklen(sa1) - offsetof(struct sockaddr_un, sun_path);
|
||||
|
||||
if (nxt_memcmp(&sa1->u.sockaddr_un.sun_path,
|
||||
&sa2->u.sockaddr_un.sun_path, len)
|
||||
!= 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
default: /* AF_INET */
|
||||
if (sa1->u.sockaddr_in.sin_port != sa2->u.sockaddr_in.sin_port) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (sa1->u.sockaddr_in.sin_addr.s_addr
|
||||
!= sa2->u.sockaddr_in.sin_addr.s_addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
size_t
|
||||
nxt_sockaddr_ntop(nxt_sockaddr_t *sa, u_char *buf, u_char *end, nxt_bool_t port)
|
||||
{
|
||||
u_char *p;
|
||||
|
||||
switch (sa->u.sockaddr.sa_family) {
|
||||
|
||||
case AF_INET:
|
||||
p = (u_char *) &sa->u.sockaddr_in.sin_addr;
|
||||
|
||||
if (port) {
|
||||
p = nxt_sprintf(buf, end, "%ud.%ud.%ud.%ud:%d",
|
||||
p[0], p[1], p[2], p[3],
|
||||
ntohs(sa->u.sockaddr_in.sin_port));
|
||||
} else {
|
||||
p = nxt_sprintf(buf, end, "%ud.%ud.%ud.%ud",
|
||||
p[0], p[1], p[2], p[3]);
|
||||
}
|
||||
|
||||
return p - buf;
|
||||
|
||||
#if (NXT_INET6)
|
||||
|
||||
case AF_INET6:
|
||||
p = buf;
|
||||
|
||||
if (port) {
|
||||
*p++ = '[';
|
||||
}
|
||||
|
||||
p = nxt_inet6_ntop(sa->u.sockaddr_in6.sin6_addr.s6_addr, p, end);
|
||||
|
||||
if (port) {
|
||||
p = nxt_sprintf(p, end, "]:%d",
|
||||
ntohs(sa->u.sockaddr_in6.sin6_port));
|
||||
}
|
||||
|
||||
return p - buf;
|
||||
#endif
|
||||
|
||||
#if (NXT_HAVE_UNIX_DOMAIN)
|
||||
|
||||
case AF_UNIX:
|
||||
|
||||
#if (NXT_LINUX)
|
||||
|
||||
p = (u_char *) sa->u.sockaddr_un.sun_path;
|
||||
|
||||
if (p[0] == '\0') {
|
||||
int len;
|
||||
|
||||
/* Linux abstract socket address has no trailing zero. */
|
||||
|
||||
len = nxt_socklen(sa) - offsetof(struct sockaddr_un, sun_path) - 1;
|
||||
p = nxt_sprintf(buf, end, "unix:\\0%*s", len, p + 1);
|
||||
|
||||
} else {
|
||||
p = nxt_sprintf(buf, end, "unix:%s", p);
|
||||
}
|
||||
|
||||
#else /* !(NXT_LINUX) */
|
||||
|
||||
p = nxt_sprintf(buf, end, "unix:%s", sa->u.sockaddr_un.sun_path);
|
||||
|
||||
#endif
|
||||
|
||||
return p - buf;
|
||||
|
||||
#endif /* NXT_HAVE_UNIX_DOMAIN */
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if (NXT_INET6)
|
||||
|
||||
static u_char *
|
||||
nxt_inet6_ntop(u_char *addr, u_char *buf, u_char *end)
|
||||
{
|
||||
u_char *p;
|
||||
size_t zero_groups, last_zero_groups, ipv6_bytes;
|
||||
nxt_uint_t i, zero_start, last_zero_start;
|
||||
|
||||
if (buf + NXT_INET6_ADDR_STR_LEN > end) {
|
||||
return buf;
|
||||
}
|
||||
|
||||
zero_start = 8;
|
||||
zero_groups = 0;
|
||||
last_zero_start = 8;
|
||||
last_zero_groups = 0;
|
||||
|
||||
for (i = 0; i < 16; i += 2) {
|
||||
|
||||
if (addr[i] == 0 && addr[i + 1] == 0) {
|
||||
|
||||
if (last_zero_groups == 0) {
|
||||
last_zero_start = i;
|
||||
}
|
||||
|
||||
last_zero_groups++;
|
||||
|
||||
} else {
|
||||
if (zero_groups < last_zero_groups) {
|
||||
zero_groups = last_zero_groups;
|
||||
zero_start = last_zero_start;
|
||||
}
|
||||
|
||||
last_zero_groups = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (zero_groups < last_zero_groups) {
|
||||
zero_groups = last_zero_groups;
|
||||
zero_start = last_zero_start;
|
||||
}
|
||||
|
||||
ipv6_bytes = 16;
|
||||
p = buf;
|
||||
|
||||
if (zero_start == 0) {
|
||||
|
||||
/* IPv4-mapped address */
|
||||
if ((zero_groups == 5 && addr[10] == 0xff && addr[11] == 0xff)
|
||||
/* IPv4-compatible address */
|
||||
|| (zero_groups == 6)
|
||||
/* not IPv6 loopback address */
|
||||
|| (zero_groups == 7 && addr[14] != 0 && addr[15] != 1))
|
||||
{
|
||||
ipv6_bytes = 12;
|
||||
}
|
||||
|
||||
*p++ = ':';
|
||||
}
|
||||
|
||||
for (i = 0; i < ipv6_bytes; i += 2) {
|
||||
|
||||
if (i == zero_start) {
|
||||
/* Output maximum number of consecutive zero groups as "::". */
|
||||
i += (zero_groups - 1) * 2;
|
||||
*p++ = ':';
|
||||
continue;
|
||||
}
|
||||
|
||||
p = nxt_sprintf(p, end, "%uxd", (addr[i] << 8) + addr[i + 1]);
|
||||
|
||||
if (i < 14) {
|
||||
*p++ = ':';
|
||||
}
|
||||
}
|
||||
|
||||
if (ipv6_bytes == 12) {
|
||||
p = nxt_sprintf(p, end, "%ud.%ud.%ud.%ud",
|
||||
addr[12], addr[13], addr[14], addr[15]);
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
void
|
||||
nxt_job_sockaddr_parse(nxt_job_sockaddr_parse_t *jbs)
|
||||
{
|
||||
u_char *p;
|
||||
size_t len;
|
||||
nxt_int_t ret;
|
||||
nxt_work_handler_t handler;
|
||||
|
||||
nxt_job_set_name(&jbs->resolve.job, "job sockaddr parse");
|
||||
|
||||
len = jbs->addr.len;
|
||||
p = jbs->addr.data;
|
||||
|
||||
if (len > 6 && nxt_memcmp(p, (u_char *) "unix:", 5) == 0) {
|
||||
ret = nxt_job_sockaddr_unix_parse(jbs);
|
||||
|
||||
} else if (len != 0 && *p == '[') {
|
||||
ret = nxt_job_sockaddr_inet6_parse(jbs);
|
||||
|
||||
} else {
|
||||
ret = nxt_job_sockaddr_inet_parse(jbs);
|
||||
}
|
||||
|
||||
switch (ret) {
|
||||
|
||||
case NXT_OK:
|
||||
handler = jbs->resolve.ready_handler;
|
||||
break;
|
||||
|
||||
case NXT_ERROR:
|
||||
handler = jbs->resolve.error_handler;
|
||||
break;
|
||||
|
||||
default: /* NXT_AGAIN */
|
||||
return;
|
||||
}
|
||||
|
||||
nxt_job_return(nxt_thread(), &jbs->resolve.job, handler);
|
||||
}
|
||||
|
||||
|
||||
static nxt_int_t
|
||||
nxt_job_sockaddr_unix_parse(nxt_job_sockaddr_parse_t *jbs)
|
||||
{
|
||||
#if (NXT_HAVE_UNIX_DOMAIN)
|
||||
size_t len, socklen;
|
||||
u_char *path;
|
||||
nxt_mem_pool_t *mp;
|
||||
nxt_sockaddr_t *sa;
|
||||
|
||||
/*
|
||||
* Actual sockaddr_un length can be lesser or even larger than defined
|
||||
* struct sockaddr_un length (see comment in unix/nxt_socket.h). So
|
||||
* limit maximum Unix domain socket address length by defined sun_path[]
|
||||
* length because some OSes accept addresses twice larger than defined
|
||||
* struct sockaddr_un. Also reserve space for a trailing zero to avoid
|
||||
* ambiguity, since many OSes accept Unix domain socket addresses
|
||||
* without a trailing zero.
|
||||
*/
|
||||
const size_t max_len = sizeof(struct sockaddr_un)
|
||||
- offsetof(struct sockaddr_un, sun_path) - 1;
|
||||
|
||||
/* cutting "unix:" */
|
||||
len = jbs->addr.len - 5;
|
||||
path = jbs->addr.data + 5;
|
||||
|
||||
if (len > max_len) {
|
||||
nxt_thread_log_error(jbs->resolve.log_level,
|
||||
"unix domain socket \"%V\" name is too long",
|
||||
&jbs->addr);
|
||||
return NXT_ERROR;
|
||||
}
|
||||
|
||||
socklen = offsetof(struct sockaddr_un, sun_path) + len + 1;
|
||||
|
||||
#if (NXT_LINUX)
|
||||
|
||||
/*
|
||||
* Linux unix(7):
|
||||
*
|
||||
* abstract: an abstract socket address is distinguished by the fact
|
||||
* that sun_path[0] is a null byte ('\0'). The socket's address in
|
||||
* this namespace is given by the additional bytes in sun_path that
|
||||
* are covered by the specified length of the address structure.
|
||||
* (Null bytes in the name have no special significance.)
|
||||
*/
|
||||
if (path[0] == '\0') {
|
||||
socklen--;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
mp = jbs->resolve.job.mem_pool;
|
||||
|
||||
jbs->resolve.sockaddrs = nxt_mem_alloc(mp, sizeof(void *));
|
||||
|
||||
if (nxt_fast_path(jbs->resolve.sockaddrs != NULL)) {
|
||||
sa = nxt_sockaddr_alloc(mp, socklen);
|
||||
|
||||
if (nxt_fast_path(sa != NULL)) {
|
||||
jbs->resolve.count = 1;
|
||||
jbs->resolve.sockaddrs[0] = sa;
|
||||
|
||||
sa->u.sockaddr_un.sun_family = AF_UNIX;
|
||||
nxt_memcpy(sa->u.sockaddr_un.sun_path, path, len);
|
||||
|
||||
return NXT_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return NXT_ERROR;
|
||||
|
||||
#else /* !(NXT_HAVE_UNIX_DOMAIN) */
|
||||
|
||||
nxt_thread_log_error(jbs->resolve.log_level,
|
||||
"unix domain socket \"%V\" is not supported",
|
||||
&jbs->addr);
|
||||
return NXT_ERROR;
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
static nxt_int_t
|
||||
nxt_job_sockaddr_inet6_parse(nxt_job_sockaddr_parse_t *jbs)
|
||||
{
|
||||
#if (NXT_INET6)
|
||||
u_char *p, *addr, *addr_end;
|
||||
size_t len;
|
||||
nxt_int_t port;
|
||||
nxt_mem_pool_t *mp;
|
||||
nxt_sockaddr_t *sa;
|
||||
struct in6_addr *in6_addr;
|
||||
|
||||
len = jbs->addr.len - 1;
|
||||
addr = jbs->addr.data + 1;
|
||||
|
||||
addr_end = nxt_memchr(addr, ']', len);
|
||||
|
||||
if (addr_end == NULL) {
|
||||
goto invalid_address;
|
||||
}
|
||||
|
||||
mp = jbs->resolve.job.mem_pool;
|
||||
|
||||
jbs->resolve.sockaddrs = nxt_mem_alloc(mp, sizeof(void *));
|
||||
|
||||
if (nxt_slow_path(jbs->resolve.sockaddrs == NULL)) {
|
||||
return NXT_ERROR;
|
||||
}
|
||||
|
||||
sa = nxt_sockaddr_alloc(mp, sizeof(struct sockaddr_in6));
|
||||
|
||||
if (nxt_slow_path(sa == NULL)) {
|
||||
return NXT_ERROR;
|
||||
}
|
||||
|
||||
jbs->resolve.count = 1;
|
||||
jbs->resolve.sockaddrs[0] = sa;
|
||||
|
||||
in6_addr = &sa->u.sockaddr_in6.sin6_addr;
|
||||
|
||||
if (nxt_inet6_addr(in6_addr, addr, addr_end - addr) != NXT_OK) {
|
||||
goto invalid_address;
|
||||
}
|
||||
|
||||
p = addr_end + 1;
|
||||
len = (addr + len) - p;
|
||||
|
||||
if (len == 0) {
|
||||
jbs->no_port = 1;
|
||||
port = jbs->resolve.port;
|
||||
goto found;
|
||||
}
|
||||
|
||||
if (*p == ':') {
|
||||
port = nxt_int_parse(p + 1, len - 1);
|
||||
|
||||
if (port >= 1 && port <= 65535) {
|
||||
port = htons((in_port_t) port);
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
|
||||
nxt_thread_log_error(jbs->resolve.log_level,
|
||||
"invalid port in \"%V\"", &jbs->addr);
|
||||
|
||||
return NXT_ERROR;
|
||||
|
||||
found:
|
||||
|
||||
sa->u.sockaddr_in6.sin6_family = AF_INET6;
|
||||
sa->u.sockaddr_in6.sin6_port = (in_port_t) port;
|
||||
|
||||
if (IN6_IS_ADDR_UNSPECIFIED(in6_addr)) {
|
||||
jbs->wildcard = 1;
|
||||
}
|
||||
|
||||
return NXT_OK;
|
||||
|
||||
invalid_address:
|
||||
|
||||
nxt_thread_log_error(jbs->resolve.log_level,
|
||||
"invalid IPv6 address in \"%V\"", &jbs->addr);
|
||||
return NXT_ERROR;
|
||||
|
||||
#else
|
||||
|
||||
nxt_thread_log_error(jbs->resolve.log_level,
|
||||
"IPv6 socket \"%V\" is not supported", &jbs->addr);
|
||||
return NXT_ERROR;
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
static nxt_int_t
|
||||
nxt_job_sockaddr_inet_parse(nxt_job_sockaddr_parse_t *jbs)
|
||||
{
|
||||
u_char *p, *host;
|
||||
size_t len;
|
||||
in_addr_t addr;
|
||||
nxt_int_t port;
|
||||
nxt_mem_pool_t *mp;
|
||||
nxt_sockaddr_t *sa;
|
||||
|
||||
addr = INADDR_ANY;
|
||||
|
||||
len = jbs->addr.len;
|
||||
host = jbs->addr.data;
|
||||
|
||||
p = nxt_memchr(host, ':', len);
|
||||
|
||||
if (p == NULL) {
|
||||
|
||||
/* single value port, address, or host name */
|
||||
|
||||
port = nxt_int_parse(host, len);
|
||||
|
||||
if (port > 0) {
|
||||
if (port < 1 || port > 65535) {
|
||||
goto invalid_port;
|
||||
}
|
||||
|
||||
/* "*:XX" */
|
||||
port = htons((in_port_t) port);
|
||||
jbs->resolve.port = (in_port_t) port;
|
||||
|
||||
} else {
|
||||
jbs->no_port = 1;
|
||||
|
||||
addr = nxt_inet_addr(host, len);
|
||||
|
||||
if (addr == INADDR_NONE) {
|
||||
jbs->resolve.name.len = len;
|
||||
jbs->resolve.name.data = host;
|
||||
|
||||
nxt_job_resolve(&jbs->resolve);
|
||||
return NXT_AGAIN;
|
||||
}
|
||||
|
||||
/* "x.x.x.x" */
|
||||
port = jbs->resolve.port;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
/* x.x.x.x:XX or host:XX */
|
||||
|
||||
p++;
|
||||
len = (host + len) - p;
|
||||
port = nxt_int_parse(p, len);
|
||||
|
||||
if (port < 1 || port > 65535) {
|
||||
goto invalid_port;
|
||||
}
|
||||
|
||||
port = htons((in_port_t) port);
|
||||
|
||||
len = (p - 1) - host;
|
||||
|
||||
if (len != 1 || host[0] != '*') {
|
||||
addr = nxt_inet_addr(host, len);
|
||||
|
||||
if (addr == INADDR_NONE) {
|
||||
jbs->resolve.name.len = len;
|
||||
jbs->resolve.name.data = host;
|
||||
jbs->resolve.port = (in_port_t) port;
|
||||
|
||||
nxt_job_resolve(&jbs->resolve);
|
||||
return NXT_AGAIN;
|
||||
}
|
||||
|
||||
/* "x.x.x.x:XX" */
|
||||
}
|
||||
}
|
||||
|
||||
mp = jbs->resolve.job.mem_pool;
|
||||
|
||||
jbs->resolve.sockaddrs = nxt_mem_alloc(mp, sizeof(void *));
|
||||
if (nxt_slow_path(jbs->resolve.sockaddrs == NULL)) {
|
||||
return NXT_ERROR;
|
||||
}
|
||||
|
||||
sa = nxt_sockaddr_alloc(mp, sizeof(struct sockaddr_in));
|
||||
|
||||
if (nxt_fast_path(sa != NULL)) {
|
||||
jbs->resolve.count = 1;
|
||||
jbs->resolve.sockaddrs[0] = sa;
|
||||
|
||||
jbs->wildcard = (addr == INADDR_ANY);
|
||||
|
||||
sa->u.sockaddr_in.sin_family = AF_INET;
|
||||
sa->u.sockaddr_in.sin_port = (in_port_t) port;
|
||||
sa->u.sockaddr_in.sin_addr.s_addr = addr;
|
||||
|
||||
return NXT_OK;
|
||||
}
|
||||
|
||||
return NXT_ERROR;
|
||||
|
||||
invalid_port:
|
||||
|
||||
nxt_thread_log_error(jbs->resolve.log_level,
|
||||
"invalid port in \"%V\"", &jbs->addr);
|
||||
|
||||
return NXT_ERROR;
|
||||
}
|
||||
|
||||
|
||||
in_addr_t
|
||||
nxt_inet_addr(u_char *buf, size_t len)
|
||||
{
|
||||
u_char c, *end;
|
||||
in_addr_t addr;
|
||||
nxt_uint_t digit, octet, dots;
|
||||
|
||||
addr = 0;
|
||||
octet = 0;
|
||||
dots = 0;
|
||||
|
||||
end = buf + len;
|
||||
|
||||
while (buf < end) {
|
||||
|
||||
c = *buf++;
|
||||
|
||||
digit = c - '0';
|
||||
/* values below '0' become large unsigned integers */
|
||||
|
||||
if (digit < 10) {
|
||||
octet = octet * 10 + digit;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c == '.' && octet < 256) {
|
||||
addr = (addr << 8) + octet;
|
||||
octet = 0;
|
||||
dots++;
|
||||
continue;
|
||||
}
|
||||
|
||||
return INADDR_NONE;
|
||||
}
|
||||
|
||||
if (dots == 3 && octet < 256) {
|
||||
addr = (addr << 8) + octet;
|
||||
return htonl(addr);
|
||||
}
|
||||
|
||||
return INADDR_NONE;
|
||||
}
|
||||
|
||||
|
||||
#if (NXT_INET6)
|
||||
|
||||
nxt_int_t
|
||||
nxt_inet6_addr(struct in6_addr *in6_addr, u_char *buf, size_t len)
|
||||
{
|
||||
u_char c, *addr, *zero_start, *ipv4, *dst, *src, *end;
|
||||
nxt_uint_t digit, group, nibbles, groups_left;
|
||||
|
||||
if (len == 0) {
|
||||
return NXT_ERROR;
|
||||
}
|
||||
|
||||
end = buf + len;
|
||||
|
||||
if (buf[0] == ':') {
|
||||
buf++;
|
||||
}
|
||||
|
||||
addr = in6_addr->s6_addr;
|
||||
zero_start = NULL;
|
||||
groups_left = 8;
|
||||
nibbles = 0;
|
||||
group = 0;
|
||||
ipv4 = NULL;
|
||||
|
||||
while (buf < end) {
|
||||
c = *buf++;
|
||||
|
||||
if (c == ':') {
|
||||
if (nibbles != 0) {
|
||||
ipv4 = buf;
|
||||
|
||||
*addr++ = (u_char) (group >> 8);
|
||||
*addr++ = (u_char) (group & 0xff);
|
||||
groups_left--;
|
||||
|
||||
if (groups_left != 0) {
|
||||
nibbles = 0;
|
||||
group = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
} else {
|
||||
if (zero_start == NULL) {
|
||||
ipv4 = buf;
|
||||
zero_start = addr;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return NXT_ERROR;
|
||||
}
|
||||
|
||||
if (c == '.' && nibbles != 0) {
|
||||
|
||||
if (groups_left < 2 || ipv4 == NULL) {
|
||||
return NXT_ERROR;
|
||||
}
|
||||
|
||||
group = nxt_inet_addr(ipv4, end - ipv4);
|
||||
if (group == INADDR_NONE) {
|
||||
return NXT_ERROR;
|
||||
}
|
||||
|
||||
group = ntohl(group);
|
||||
|
||||
*addr++ = (u_char) ((group >> 24) & 0xff);
|
||||
*addr++ = (u_char) ((group >> 16) & 0xff);
|
||||
groups_left--;
|
||||
|
||||
/* the low 16-bit are copied below */
|
||||
break;
|
||||
}
|
||||
|
||||
nibbles++;
|
||||
|
||||
if (nibbles > 4) {
|
||||
return NXT_ERROR;
|
||||
}
|
||||
|
||||
group <<= 4;
|
||||
|
||||
digit = c - '0';
|
||||
/* values below '0' become large unsigned integers */
|
||||
|
||||
if (digit < 10) {
|
||||
group += digit;
|
||||
continue;
|
||||
}
|
||||
|
||||
c |= 0x20;
|
||||
digit = c - 'a';
|
||||
/* values below 'a' become large unsigned integers */
|
||||
|
||||
if (digit < 6) {
|
||||
group += 10 + digit;
|
||||
continue;
|
||||
}
|
||||
|
||||
return NXT_ERROR;
|
||||
}
|
||||
|
||||
if (nibbles == 0 && zero_start == NULL) {
|
||||
return NXT_ERROR;
|
||||
}
|
||||
|
||||
*addr++ = (u_char) (group >> 8);
|
||||
*addr++ = (u_char) (group & 0xff);
|
||||
groups_left--;
|
||||
|
||||
if (groups_left != 0) {
|
||||
|
||||
if (zero_start != NULL) {
|
||||
|
||||
/* moving part before consecutive zero groups to the end */
|
||||
|
||||
groups_left *= 2;
|
||||
src = addr - 1;
|
||||
dst = src + groups_left;
|
||||
|
||||
while (src >= zero_start) {
|
||||
*dst-- = *src--;
|
||||
}
|
||||
|
||||
nxt_memzero(zero_start, groups_left);
|
||||
|
||||
return NXT_OK;
|
||||
}
|
||||
|
||||
} else {
|
||||
if (zero_start == NULL) {
|
||||
return NXT_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return NXT_ERROR;
|
||||
}
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user