diff options
-rw-r--r-- | src/nxt_sockaddr.c | 208 | ||||
-rw-r--r-- | src/nxt_sockaddr.h | 1 |
2 files changed, 208 insertions, 1 deletions
diff --git a/src/nxt_sockaddr.c b/src/nxt_sockaddr.c index 3ec4abb6..4bfff2b0 100644 --- a/src/nxt_sockaddr.c +++ b/src/nxt_sockaddr.c @@ -11,6 +11,10 @@ static u_char *nxt_inet6_ntop(u_char *addr, u_char *buf, u_char *end); #endif +static nxt_sockaddr_t *nxt_sockaddr_unix_parse(nxt_mp_t *mp, nxt_str_t *addr); +static nxt_sockaddr_t *nxt_sockaddr_inet6_parse(nxt_mp_t *mp, nxt_str_t *addr); +static nxt_sockaddr_t *nxt_sockaddr_inet_parse(nxt_mp_t *mp, nxt_str_t *addr); + 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); @@ -561,6 +565,207 @@ nxt_inet6_ntop(u_char *addr, u_char *buf, u_char *end) #endif +nxt_sockaddr_t * +nxt_sockaddr_parse(nxt_mp_t *mp, nxt_str_t *addr) +{ + nxt_sockaddr_t *sa; + + if (addr->length > 6 && nxt_memcmp(addr->start, "unix:", 5) == 0) { + sa = nxt_sockaddr_unix_parse(mp, addr); + + } else if (addr->length != 0 && addr->start[0] == '[') { + sa = nxt_sockaddr_inet6_parse(mp, addr); + + } else { + sa = nxt_sockaddr_inet_parse(mp, addr); + } + + if (nxt_fast_path(sa != NULL)) { + nxt_sockaddr_text(sa); + } + + return sa; +} + + +static nxt_sockaddr_t * +nxt_sockaddr_unix_parse(nxt_mp_t *mp, nxt_str_t *addr) +{ +#if (NXT_HAVE_UNIX_DOMAIN) + size_t length, socklen; + u_char *path; + 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:". */ + length = addr->length - 5; + path = addr->start + 5; + + if (length > max_len) { + nxt_thread_log_error(NXT_LOG_ERR, + "unix domain socket \"%V\" name is too long", + addr); + return NULL; + } + + socklen = offsetof(struct sockaddr_un, sun_path) + length + 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] == '@') { + path[0] = '\0'; + socklen--; + } + +#endif + + sa = nxt_sockaddr_alloc(mp, socklen, addr->length); + + if (nxt_fast_path(sa != NULL)) { + sa->u.sockaddr_un.sun_family = AF_UNIX; + nxt_memcpy(sa->u.sockaddr_un.sun_path, path, length); + } + + return sa; + +#else /* !(NXT_HAVE_UNIX_DOMAIN) */ + + nxt_thread_log_error(NXT_LOG_ERR, + "unix domain socket \"%V\" is not supported", addr); + + return NULL; + +#endif +} + + +static nxt_sockaddr_t * +nxt_sockaddr_inet6_parse(nxt_mp_t *mp, nxt_str_t *addr) +{ +#if (NXT_INET6) + u_char *p, *start, *end; + size_t length; + nxt_int_t ret, port; + nxt_sockaddr_t *sa; + + length = addr->length - 1; + start = addr->start + 1; + + end = nxt_memchr(start, ']', length); + + if (end != NULL) { + sa = nxt_sockaddr_alloc(mp, sizeof(struct sockaddr_in6), + NXT_INET6_ADDR_STR_LEN); + if (nxt_slow_path(sa == NULL)) { + return NULL; + } + + ret = nxt_inet6_addr(&sa->u.sockaddr_in6.sin6_addr, start, end - start); + + if (nxt_fast_path(ret == NXT_OK)) { + p = end + 1; + length = (start + length) - p; + + if (length > 2 && *p == ':') { + port = nxt_int_parse(p + 1, length - 1); + + if (port > 0 && port < 65536) { + sa->u.sockaddr_in6.sin6_port = htons((in_port_t) port); + sa->u.sockaddr_in6.sin6_family = AF_INET6; + + return sa; + } + } + + nxt_thread_log_error(NXT_LOG_ERR, "invalid port in \"%V\"", addr); + + return NULL; + } + } + + nxt_thread_log_error(NXT_LOG_ERR, "invalid IPv6 address in \"%V\"", addr); + + return NULL; + +#else /* !(NXT_INET6) */ + + nxt_thread_log_error(NXT_LOG_ERR, "IPv6 socket \"%V\" is not supported", + addr); + return NULL; + +#endif +} + + +static nxt_sockaddr_t * +nxt_sockaddr_inet_parse(nxt_mp_t *mp, nxt_str_t *addr) +{ + u_char *p; + size_t length; + nxt_int_t port; + in_addr_t inaddr; + nxt_sockaddr_t *sa; + + p = nxt_memchr(addr->start, ':', addr->length); + + if (nxt_fast_path(p != NULL)) { + inaddr = INADDR_ANY; + length = p - addr->start; + + if (length != 1 || addr->start[0] != '*') { + inaddr = nxt_inet_addr(addr->start, length); + + if (nxt_slow_path(inaddr == INADDR_NONE)) { + nxt_thread_log_error(NXT_LOG_ERR, "invalid address \"%V\"", + addr); + return NULL; + } + } + + p++; + length = (addr->start + addr->length) - p; + port = nxt_int_parse(p, length); + + if (port > 0 && port < 65536) { + sa = nxt_sockaddr_alloc(mp, sizeof(struct sockaddr_in), + NXT_INET_ADDR_STR_LEN); + + if (nxt_slow_path(sa != NULL)) { + sa->u.sockaddr_in.sin_family = AF_INET; + sa->u.sockaddr_in.sin_port = htons((in_port_t) port); + sa->u.sockaddr_in.sin_addr.s_addr = inaddr; + } + + return sa; + } + } + + nxt_thread_log_error(NXT_LOG_ERR, "invalid port in \"%V\"", addr); + + return NULL; +} + + void nxt_job_sockaddr_parse(nxt_job_sockaddr_parse_t *jbs) { @@ -712,7 +917,8 @@ nxt_job_sockaddr_inet6_parse(nxt_job_sockaddr_parse_t *jbs) return NXT_ERROR; } - sa = nxt_sockaddr_alloc(mp, sizeof(struct sockaddr_in6)); + sa = nxt_sockaddr_alloc(mp, sizeof(struct sockaddr_in6), + NXT_INET6_ADDR_STR_LEN); if (nxt_slow_path(sa == NULL)) { return NXT_ERROR; diff --git a/src/nxt_sockaddr.h b/src/nxt_sockaddr.h index dc4477c9..007d42e6 100644 --- a/src/nxt_sockaddr.h +++ b/src/nxt_sockaddr.h @@ -88,6 +88,7 @@ NXT_EXPORT nxt_bool_t nxt_sockaddr_cmp(nxt_sockaddr_t *sa1, nxt_sockaddr_t *sa2); NXT_EXPORT size_t nxt_sockaddr_ntop(nxt_sockaddr_t *sa, u_char *buf, u_char *end, nxt_bool_t port); +NXT_EXPORT nxt_sockaddr_t *nxt_sockaddr_parse(nxt_mp_t *mp, nxt_str_t *addr); NXT_EXPORT void nxt_job_sockaddr_parse(nxt_job_sockaddr_parse_t *jbs); NXT_EXPORT in_addr_t nxt_inet_addr(u_char *buf, size_t len); #if (NXT_INET6) |