xref: /unit/src/nxt_sockaddr.c (revision 4)
10Sigor@sysoev.ru 
20Sigor@sysoev.ru /*
30Sigor@sysoev.ru  * Copyright (C) Igor Sysoev
40Sigor@sysoev.ru  * Copyright (C) NGINX, Inc.
50Sigor@sysoev.ru  */
60Sigor@sysoev.ru 
70Sigor@sysoev.ru #include <nxt_main.h>
80Sigor@sysoev.ru 
90Sigor@sysoev.ru 
100Sigor@sysoev.ru #if (NXT_INET6)
110Sigor@sysoev.ru static u_char *nxt_inet6_ntop(u_char *addr, u_char *buf, u_char *end);
120Sigor@sysoev.ru #endif
130Sigor@sysoev.ru 
140Sigor@sysoev.ru static nxt_int_t nxt_job_sockaddr_unix_parse(nxt_job_sockaddr_parse_t *jbs);
150Sigor@sysoev.ru static nxt_int_t nxt_job_sockaddr_inet6_parse(nxt_job_sockaddr_parse_t *jbs);
160Sigor@sysoev.ru static nxt_int_t nxt_job_sockaddr_inet_parse(nxt_job_sockaddr_parse_t *jbs);
170Sigor@sysoev.ru 
180Sigor@sysoev.ru 
190Sigor@sysoev.ru nxt_sockaddr_t *
200Sigor@sysoev.ru nxt_sockaddr_alloc(nxt_mem_pool_t *mp, socklen_t len)
210Sigor@sysoev.ru {
220Sigor@sysoev.ru     nxt_sockaddr_t  *sa;
230Sigor@sysoev.ru 
240Sigor@sysoev.ru     /*
250Sigor@sysoev.ru      * The current struct sockaddr's define 32-bit fields at maximum
260Sigor@sysoev.ru      * and may define 64-bit AF_INET6 fields in the future.  Alignment
270Sigor@sysoev.ru      * of memory allocated by nxt_mem_zalloc() is enough for these fields.
280Sigor@sysoev.ru      * If 128-bit alignment will be required then nxt_mem_malloc() and
290Sigor@sysoev.ru      * nxt_memzero() should be used instead.
300Sigor@sysoev.ru      */
310Sigor@sysoev.ru     sa = nxt_mem_zalloc(mp, offsetof(nxt_sockaddr_t, u) + len);
320Sigor@sysoev.ru 
330Sigor@sysoev.ru     if (nxt_fast_path(sa != NULL)) {
340Sigor@sysoev.ru         nxt_socklen_set(sa, len);
350Sigor@sysoev.ru     }
360Sigor@sysoev.ru 
370Sigor@sysoev.ru     return sa;
380Sigor@sysoev.ru }
390Sigor@sysoev.ru 
400Sigor@sysoev.ru 
410Sigor@sysoev.ru nxt_sockaddr_t *
420Sigor@sysoev.ru nxt_sockaddr_create(nxt_mem_pool_t *mp, struct sockaddr *sockaddr,
430Sigor@sysoev.ru     socklen_t len)
440Sigor@sysoev.ru {
450Sigor@sysoev.ru     size_t          size, copy;
460Sigor@sysoev.ru     nxt_sockaddr_t  *sa;
470Sigor@sysoev.ru 
480Sigor@sysoev.ru     size = len;
490Sigor@sysoev.ru     copy = len;
500Sigor@sysoev.ru 
510Sigor@sysoev.ru #if (NXT_HAVE_UNIX_DOMAIN)
520Sigor@sysoev.ru 
530Sigor@sysoev.ru     /*
540Sigor@sysoev.ru      * Unspecified Unix domain sockaddr_un form and length are very
550Sigor@sysoev.ru      * platform depended (see comment in unix/socket.h).  Here they are
560Sigor@sysoev.ru      * normalized to the sockaddr_un with single zero byte sun_path[].
570Sigor@sysoev.ru      */
580Sigor@sysoev.ru 
590Sigor@sysoev.ru     if (size <= offsetof(struct sockaddr_un, sun_path)) {
600Sigor@sysoev.ru         /*
610Sigor@sysoev.ru          * Small socket length means a short unspecified Unix domain
620Sigor@sysoev.ru          * socket address:
630Sigor@sysoev.ru          *
640Sigor@sysoev.ru          *   getsockname() and getpeername() on OpenBSD prior to 5.3
650Sigor@sysoev.ru          *   return zero length and does not update a passed sockaddr
660Sigor@sysoev.ru          *   buffer at all.
670Sigor@sysoev.ru          *
680Sigor@sysoev.ru          *   Linux returns length equal to 2, i.e. sockaddr_un without
690Sigor@sysoev.ru          *   sun_path[], unix(7):
700Sigor@sysoev.ru          *
710Sigor@sysoev.ru          *     unnamed: A stream socket that has not been bound
720Sigor@sysoev.ru          *     to a pathname using bind(2) has no name.  Likewise,
730Sigor@sysoev.ru          *     the two sockets created by socketpair(2) are unnamed.
740Sigor@sysoev.ru          *     When the address of an unnamed socket is returned by
750Sigor@sysoev.ru          *     getsockname(2), getpeername(2), and accept(2), its
760Sigor@sysoev.ru          *     length is sizeof(sa_family_t), and sun_path should
770Sigor@sysoev.ru          *     not be inspected.
780Sigor@sysoev.ru          */
790Sigor@sysoev.ru         size = offsetof(struct sockaddr_un, sun_path) + 1;
800Sigor@sysoev.ru 
810Sigor@sysoev.ru #if !(NXT_LINUX)
820Sigor@sysoev.ru 
830Sigor@sysoev.ru     } else if (sockaddr->sa_family == AF_UNIX && sockaddr->sa_data[0] == '\0') {
840Sigor@sysoev.ru         /*
850Sigor@sysoev.ru          * Omit nonsignificant zeros of the unspecified Unix domain socket
860Sigor@sysoev.ru          * address.  This test is disabled for Linux since Linux abstract
870Sigor@sysoev.ru          * socket address also starts with zero.  However Linux unspecified
880Sigor@sysoev.ru          * Unix domain socket address is short and is handled above.
890Sigor@sysoev.ru          */
900Sigor@sysoev.ru         size = offsetof(struct sockaddr_un, sun_path) + 1;
910Sigor@sysoev.ru         copy = size;
920Sigor@sysoev.ru 
930Sigor@sysoev.ru #endif
940Sigor@sysoev.ru     }
950Sigor@sysoev.ru 
960Sigor@sysoev.ru #endif  /* NXT_HAVE_UNIX_DOMAIN */
970Sigor@sysoev.ru 
980Sigor@sysoev.ru     sa = nxt_sockaddr_alloc(mp, size);
990Sigor@sysoev.ru 
1000Sigor@sysoev.ru     if (nxt_fast_path(sa != NULL)) {
1010Sigor@sysoev.ru 
1020Sigor@sysoev.ru         nxt_memcpy(&sa->u.sockaddr, sockaddr, copy);
1030Sigor@sysoev.ru 
1040Sigor@sysoev.ru #if (NXT_SOCKADDR_SA_LEN)
1050Sigor@sysoev.ru 
1060Sigor@sysoev.ru         /* Update shortcut sockaddr length overwritten by nxt_memcpy(). */
1070Sigor@sysoev.ru         nxt_socklen_set(sa, size);
1080Sigor@sysoev.ru 
1090Sigor@sysoev.ru #endif
1100Sigor@sysoev.ru 
1110Sigor@sysoev.ru #if (NXT_HAVE_UNIX_DOMAIN && NXT_OPENBSD)
1120Sigor@sysoev.ru 
1130Sigor@sysoev.ru         if (len == 0) {
1140Sigor@sysoev.ru             sa->u.sockaddr.sa_family = AF_UNIX;
1150Sigor@sysoev.ru         }
1160Sigor@sysoev.ru 
1170Sigor@sysoev.ru #endif
1180Sigor@sysoev.ru     }
1190Sigor@sysoev.ru 
1200Sigor@sysoev.ru     return sa;
1210Sigor@sysoev.ru }
1220Sigor@sysoev.ru 
1230Sigor@sysoev.ru 
1240Sigor@sysoev.ru nxt_sockaddr_t *
1250Sigor@sysoev.ru nxt_sockaddr_copy(nxt_mem_pool_t *mp, nxt_sockaddr_t *src)
1260Sigor@sysoev.ru {
1270Sigor@sysoev.ru     size_t          len;
1280Sigor@sysoev.ru     nxt_sockaddr_t  *dst;
1290Sigor@sysoev.ru 
1300Sigor@sysoev.ru     len = offsetof(nxt_sockaddr_t, u) + nxt_socklen(src);
1310Sigor@sysoev.ru 
1320Sigor@sysoev.ru     dst = nxt_mem_alloc(mp, len);
1330Sigor@sysoev.ru 
1340Sigor@sysoev.ru     if (nxt_fast_path(dst != NULL)) {
1350Sigor@sysoev.ru         nxt_memcpy(dst, src, len);
1360Sigor@sysoev.ru     }
1370Sigor@sysoev.ru 
1380Sigor@sysoev.ru     return dst;
1390Sigor@sysoev.ru }
1400Sigor@sysoev.ru 
1410Sigor@sysoev.ru 
1420Sigor@sysoev.ru nxt_sockaddr_t *
1430Sigor@sysoev.ru nxt_getsockname(nxt_mem_pool_t *mp, nxt_socket_t s)
1440Sigor@sysoev.ru {
1450Sigor@sysoev.ru     int                 ret;
1460Sigor@sysoev.ru     socklen_t           socklen;
1470Sigor@sysoev.ru     nxt_sockaddr_buf_t  sockaddr;
1480Sigor@sysoev.ru 
1490Sigor@sysoev.ru     socklen = NXT_SOCKADDR_LEN;
1500Sigor@sysoev.ru 
1510Sigor@sysoev.ru     ret = getsockname(s, &sockaddr.buf, &socklen);
1520Sigor@sysoev.ru 
1530Sigor@sysoev.ru     if (nxt_fast_path(ret == 0)) {
1540Sigor@sysoev.ru         return nxt_sockaddr_create(mp, &sockaddr.buf, socklen);
1550Sigor@sysoev.ru     }
1560Sigor@sysoev.ru 
1570Sigor@sysoev.ru     nxt_thread_log_error(NXT_LOG_ERR, "getsockname(%d) failed %E",
1580Sigor@sysoev.ru                          s, nxt_errno);
1590Sigor@sysoev.ru 
1600Sigor@sysoev.ru     return NULL;
1610Sigor@sysoev.ru }
1620Sigor@sysoev.ru 
1630Sigor@sysoev.ru 
1640Sigor@sysoev.ru nxt_int_t
1650Sigor@sysoev.ru nxt_sockaddr_text(nxt_mem_pool_t *mp, nxt_sockaddr_t *sa, nxt_bool_t port)
1660Sigor@sysoev.ru {
1670Sigor@sysoev.ru     size_t  len;
1680Sigor@sysoev.ru     u_char  *p;
1690Sigor@sysoev.ru     u_char  buf[NXT_SOCKADDR_STR_LEN + NXT_SOCKPORT_STR_LEN];
1700Sigor@sysoev.ru 
1710Sigor@sysoev.ru     len = NXT_SOCKADDR_STR_LEN + NXT_SOCKPORT_STR_LEN;
1720Sigor@sysoev.ru 
1730Sigor@sysoev.ru     len = nxt_sockaddr_ntop(sa, buf, buf + len, port);
1740Sigor@sysoev.ru 
1750Sigor@sysoev.ru     p = nxt_mem_alloc(mp, len);
1760Sigor@sysoev.ru 
1770Sigor@sysoev.ru     if (nxt_fast_path(p != NULL)) {
1780Sigor@sysoev.ru 
1790Sigor@sysoev.ru         sa->text = p;
1800Sigor@sysoev.ru         sa->text_len = len;
1810Sigor@sysoev.ru         nxt_memcpy(p, buf, len);
1820Sigor@sysoev.ru 
1830Sigor@sysoev.ru         return NXT_OK;
1840Sigor@sysoev.ru     }
1850Sigor@sysoev.ru 
1860Sigor@sysoev.ru     return NXT_ERROR;
1870Sigor@sysoev.ru }
1880Sigor@sysoev.ru 
1890Sigor@sysoev.ru 
1900Sigor@sysoev.ru uint32_t
1910Sigor@sysoev.ru nxt_sockaddr_port(nxt_sockaddr_t *sa)
1920Sigor@sysoev.ru {
1930Sigor@sysoev.ru     uint32_t  port;
1940Sigor@sysoev.ru 
1950Sigor@sysoev.ru     switch (sa->u.sockaddr.sa_family) {
1960Sigor@sysoev.ru 
1970Sigor@sysoev.ru #if (NXT_INET6)
1980Sigor@sysoev.ru 
1990Sigor@sysoev.ru     case AF_INET6:
2000Sigor@sysoev.ru         port = sa->u.sockaddr_in6.sin6_port;
2010Sigor@sysoev.ru         break;
2020Sigor@sysoev.ru 
2030Sigor@sysoev.ru #endif
2040Sigor@sysoev.ru 
2050Sigor@sysoev.ru #if (NXT_HAVE_UNIX_DOMAIN)
2060Sigor@sysoev.ru 
2070Sigor@sysoev.ru     case AF_UNIX:
2080Sigor@sysoev.ru         return 0;
2090Sigor@sysoev.ru 
2100Sigor@sysoev.ru #endif
2110Sigor@sysoev.ru 
2120Sigor@sysoev.ru     default:
2130Sigor@sysoev.ru         port = sa->u.sockaddr_in.sin_port;
2140Sigor@sysoev.ru         break;
2150Sigor@sysoev.ru     }
2160Sigor@sysoev.ru 
2170Sigor@sysoev.ru     return ntohs((uint16_t) port);
2180Sigor@sysoev.ru }
2190Sigor@sysoev.ru 
2200Sigor@sysoev.ru 
2210Sigor@sysoev.ru nxt_bool_t
2220Sigor@sysoev.ru nxt_sockaddr_cmp(nxt_sockaddr_t *sa1, nxt_sockaddr_t *sa2)
2230Sigor@sysoev.ru {
2240Sigor@sysoev.ru     if (nxt_socklen(sa1) != nxt_socklen(sa2)) {
2250Sigor@sysoev.ru         return 0;
2260Sigor@sysoev.ru     }
2270Sigor@sysoev.ru 
2280Sigor@sysoev.ru     if (sa1->type != sa2->type) {
2290Sigor@sysoev.ru         return 0;
2300Sigor@sysoev.ru     }
2310Sigor@sysoev.ru 
2320Sigor@sysoev.ru     if (sa1->u.sockaddr.sa_family != sa2->u.sockaddr.sa_family) {
2330Sigor@sysoev.ru         return 0;
2340Sigor@sysoev.ru     }
2350Sigor@sysoev.ru 
2360Sigor@sysoev.ru     /*
2370Sigor@sysoev.ru      * sockaddr struct's cannot be compared in whole since kernel
2380Sigor@sysoev.ru      * may fill some fields in inherited sockaddr struct's.
2390Sigor@sysoev.ru      */
2400Sigor@sysoev.ru 
2410Sigor@sysoev.ru     switch (sa1->u.sockaddr.sa_family) {
2420Sigor@sysoev.ru 
2430Sigor@sysoev.ru #if (NXT_INET6)
2440Sigor@sysoev.ru 
2450Sigor@sysoev.ru     case AF_INET6:
2460Sigor@sysoev.ru         if (sa1->u.sockaddr_in6.sin6_port != sa2->u.sockaddr_in6.sin6_port) {
2470Sigor@sysoev.ru             return 0;
2480Sigor@sysoev.ru         }
2490Sigor@sysoev.ru 
2500Sigor@sysoev.ru         if (nxt_memcmp(&sa1->u.sockaddr_in6.sin6_addr,
2510Sigor@sysoev.ru                        &sa2->u.sockaddr_in6.sin6_addr, 16)
2520Sigor@sysoev.ru             != 0)
2530Sigor@sysoev.ru         {
2540Sigor@sysoev.ru             return 0;
2550Sigor@sysoev.ru         }
2560Sigor@sysoev.ru 
2570Sigor@sysoev.ru         return 1;
2580Sigor@sysoev.ru 
2590Sigor@sysoev.ru #endif
2600Sigor@sysoev.ru 
2610Sigor@sysoev.ru #if (NXT_HAVE_UNIX_DOMAIN)
2620Sigor@sysoev.ru 
2630Sigor@sysoev.ru     case AF_UNIX:
2640Sigor@sysoev.ru         {
2650Sigor@sysoev.ru             size_t  len;
2660Sigor@sysoev.ru 
2670Sigor@sysoev.ru             len = nxt_socklen(sa1) - offsetof(struct sockaddr_un, sun_path);
2680Sigor@sysoev.ru 
2690Sigor@sysoev.ru             if (nxt_memcmp(&sa1->u.sockaddr_un.sun_path,
2700Sigor@sysoev.ru                            &sa2->u.sockaddr_un.sun_path, len)
2710Sigor@sysoev.ru                 != 0)
2720Sigor@sysoev.ru             {
2730Sigor@sysoev.ru                 return 0;
2740Sigor@sysoev.ru             }
2750Sigor@sysoev.ru 
2760Sigor@sysoev.ru             return 1;
2770Sigor@sysoev.ru         }
2780Sigor@sysoev.ru 
2790Sigor@sysoev.ru #endif
2800Sigor@sysoev.ru 
2810Sigor@sysoev.ru     default: /* AF_INET */
2820Sigor@sysoev.ru         if (sa1->u.sockaddr_in.sin_port != sa2->u.sockaddr_in.sin_port) {
2830Sigor@sysoev.ru             return 0;
2840Sigor@sysoev.ru         }
2850Sigor@sysoev.ru 
2860Sigor@sysoev.ru         if (sa1->u.sockaddr_in.sin_addr.s_addr
2870Sigor@sysoev.ru             != sa2->u.sockaddr_in.sin_addr.s_addr)
2880Sigor@sysoev.ru         {
2890Sigor@sysoev.ru             return 0;
2900Sigor@sysoev.ru         }
2910Sigor@sysoev.ru 
2920Sigor@sysoev.ru         return 1;
2930Sigor@sysoev.ru     }
2940Sigor@sysoev.ru }
2950Sigor@sysoev.ru 
2960Sigor@sysoev.ru 
2970Sigor@sysoev.ru size_t
2980Sigor@sysoev.ru nxt_sockaddr_ntop(nxt_sockaddr_t *sa, u_char *buf, u_char *end, nxt_bool_t port)
2990Sigor@sysoev.ru {
3000Sigor@sysoev.ru     u_char  *p;
3010Sigor@sysoev.ru 
3020Sigor@sysoev.ru     switch (sa->u.sockaddr.sa_family) {
3030Sigor@sysoev.ru 
3040Sigor@sysoev.ru     case AF_INET:
3050Sigor@sysoev.ru         p = (u_char *) &sa->u.sockaddr_in.sin_addr;
3060Sigor@sysoev.ru 
3070Sigor@sysoev.ru         if (port) {
3080Sigor@sysoev.ru             p = nxt_sprintf(buf, end, "%ud.%ud.%ud.%ud:%d",
3090Sigor@sysoev.ru                             p[0], p[1], p[2], p[3],
3100Sigor@sysoev.ru                             ntohs(sa->u.sockaddr_in.sin_port));
3110Sigor@sysoev.ru         } else {
3120Sigor@sysoev.ru             p = nxt_sprintf(buf, end, "%ud.%ud.%ud.%ud",
3130Sigor@sysoev.ru                             p[0], p[1], p[2], p[3]);
3140Sigor@sysoev.ru         }
3150Sigor@sysoev.ru 
3160Sigor@sysoev.ru         return p - buf;
3170Sigor@sysoev.ru 
3180Sigor@sysoev.ru #if (NXT_INET6)
3190Sigor@sysoev.ru 
3200Sigor@sysoev.ru     case AF_INET6:
3210Sigor@sysoev.ru         p = buf;
3220Sigor@sysoev.ru 
3230Sigor@sysoev.ru         if (port) {
3240Sigor@sysoev.ru             *p++ = '[';
3250Sigor@sysoev.ru         }
3260Sigor@sysoev.ru 
3270Sigor@sysoev.ru         p = nxt_inet6_ntop(sa->u.sockaddr_in6.sin6_addr.s6_addr, p, end);
3280Sigor@sysoev.ru 
3290Sigor@sysoev.ru         if (port) {
3300Sigor@sysoev.ru             p = nxt_sprintf(p, end, "]:%d",
3310Sigor@sysoev.ru                             ntohs(sa->u.sockaddr_in6.sin6_port));
3320Sigor@sysoev.ru         }
3330Sigor@sysoev.ru 
3340Sigor@sysoev.ru         return p - buf;
3350Sigor@sysoev.ru #endif
3360Sigor@sysoev.ru 
3370Sigor@sysoev.ru #if (NXT_HAVE_UNIX_DOMAIN)
3380Sigor@sysoev.ru 
3390Sigor@sysoev.ru     case AF_UNIX:
3400Sigor@sysoev.ru 
3410Sigor@sysoev.ru #if (NXT_LINUX)
3420Sigor@sysoev.ru 
3430Sigor@sysoev.ru         p = (u_char *) sa->u.sockaddr_un.sun_path;
3440Sigor@sysoev.ru 
3450Sigor@sysoev.ru         if (p[0] == '\0') {
3460Sigor@sysoev.ru             int  len;
3470Sigor@sysoev.ru 
3480Sigor@sysoev.ru             /* Linux abstract socket address has no trailing zero. */
3490Sigor@sysoev.ru 
3500Sigor@sysoev.ru             len = nxt_socklen(sa) - offsetof(struct sockaddr_un, sun_path) - 1;
3510Sigor@sysoev.ru             p = nxt_sprintf(buf, end, "unix:\\0%*s", len, p + 1);
3520Sigor@sysoev.ru 
3530Sigor@sysoev.ru         } else {
3540Sigor@sysoev.ru             p = nxt_sprintf(buf, end, "unix:%s", p);
3550Sigor@sysoev.ru         }
3560Sigor@sysoev.ru 
3570Sigor@sysoev.ru #else  /* !(NXT_LINUX) */
3580Sigor@sysoev.ru 
3590Sigor@sysoev.ru         p = nxt_sprintf(buf, end, "unix:%s", sa->u.sockaddr_un.sun_path);
3600Sigor@sysoev.ru 
3610Sigor@sysoev.ru #endif
3620Sigor@sysoev.ru 
3630Sigor@sysoev.ru         return p - buf;
3640Sigor@sysoev.ru 
3650Sigor@sysoev.ru #endif  /* NXT_HAVE_UNIX_DOMAIN */
3660Sigor@sysoev.ru 
3670Sigor@sysoev.ru     default:
3680Sigor@sysoev.ru         return 0;
3690Sigor@sysoev.ru     }
3700Sigor@sysoev.ru }
3710Sigor@sysoev.ru 
3720Sigor@sysoev.ru 
3730Sigor@sysoev.ru #if (NXT_INET6)
3740Sigor@sysoev.ru 
3750Sigor@sysoev.ru static u_char *
3760Sigor@sysoev.ru nxt_inet6_ntop(u_char *addr, u_char *buf, u_char *end)
3770Sigor@sysoev.ru {
3780Sigor@sysoev.ru     u_char      *p;
3790Sigor@sysoev.ru     size_t      zero_groups, last_zero_groups, ipv6_bytes;
3800Sigor@sysoev.ru     nxt_uint_t  i, zero_start, last_zero_start;
3810Sigor@sysoev.ru 
3820Sigor@sysoev.ru     if (buf + NXT_INET6_ADDR_STR_LEN > end) {
3830Sigor@sysoev.ru         return buf;
3840Sigor@sysoev.ru     }
3850Sigor@sysoev.ru 
3860Sigor@sysoev.ru     zero_start = 8;
3870Sigor@sysoev.ru     zero_groups = 0;
3880Sigor@sysoev.ru     last_zero_start = 8;
3890Sigor@sysoev.ru     last_zero_groups = 0;
3900Sigor@sysoev.ru 
3910Sigor@sysoev.ru     for (i = 0; i < 16; i += 2) {
3920Sigor@sysoev.ru 
3930Sigor@sysoev.ru         if (addr[i] == 0 && addr[i + 1] == 0) {
3940Sigor@sysoev.ru 
3950Sigor@sysoev.ru             if (last_zero_groups == 0) {
3960Sigor@sysoev.ru                 last_zero_start = i;
3970Sigor@sysoev.ru             }
3980Sigor@sysoev.ru 
3990Sigor@sysoev.ru             last_zero_groups++;
4000Sigor@sysoev.ru 
4010Sigor@sysoev.ru         } else {
4020Sigor@sysoev.ru             if (zero_groups < last_zero_groups) {
4030Sigor@sysoev.ru                 zero_groups = last_zero_groups;
4040Sigor@sysoev.ru                 zero_start = last_zero_start;
4050Sigor@sysoev.ru             }
4060Sigor@sysoev.ru 
4070Sigor@sysoev.ru             last_zero_groups = 0;
4080Sigor@sysoev.ru         }
4090Sigor@sysoev.ru     }
4100Sigor@sysoev.ru 
4110Sigor@sysoev.ru     if (zero_groups < last_zero_groups) {
4120Sigor@sysoev.ru         zero_groups = last_zero_groups;
4130Sigor@sysoev.ru         zero_start = last_zero_start;
4140Sigor@sysoev.ru     }
4150Sigor@sysoev.ru 
4160Sigor@sysoev.ru     ipv6_bytes = 16;
4170Sigor@sysoev.ru     p = buf;
4180Sigor@sysoev.ru 
4190Sigor@sysoev.ru     if (zero_start == 0) {
4200Sigor@sysoev.ru 
4210Sigor@sysoev.ru                /* IPv4-mapped address */
4220Sigor@sysoev.ru         if ((zero_groups == 5 && addr[10] == 0xff && addr[11] == 0xff)
4230Sigor@sysoev.ru                /* IPv4-compatible address */
4240Sigor@sysoev.ru             || (zero_groups == 6)
4250Sigor@sysoev.ru                /* not IPv6 loopback address */
4260Sigor@sysoev.ru             || (zero_groups == 7 && addr[14] != 0 && addr[15] != 1))
4270Sigor@sysoev.ru         {
4280Sigor@sysoev.ru             ipv6_bytes = 12;
4290Sigor@sysoev.ru         }
4300Sigor@sysoev.ru 
4310Sigor@sysoev.ru         *p++ = ':';
4320Sigor@sysoev.ru     }
4330Sigor@sysoev.ru 
4340Sigor@sysoev.ru     for (i = 0; i < ipv6_bytes; i += 2) {
4350Sigor@sysoev.ru 
4360Sigor@sysoev.ru         if (i == zero_start) {
4370Sigor@sysoev.ru             /* Output maximum number of consecutive zero groups as "::". */
4380Sigor@sysoev.ru             i += (zero_groups - 1) * 2;
4390Sigor@sysoev.ru             *p++ = ':';
4400Sigor@sysoev.ru             continue;
4410Sigor@sysoev.ru         }
4420Sigor@sysoev.ru 
4430Sigor@sysoev.ru         p = nxt_sprintf(p, end, "%uxd", (addr[i] << 8) + addr[i + 1]);
4440Sigor@sysoev.ru 
4450Sigor@sysoev.ru         if (i < 14) {
4460Sigor@sysoev.ru             *p++ = ':';
4470Sigor@sysoev.ru         }
4480Sigor@sysoev.ru     }
4490Sigor@sysoev.ru 
4500Sigor@sysoev.ru     if (ipv6_bytes == 12) {
4510Sigor@sysoev.ru         p = nxt_sprintf(p, end, "%ud.%ud.%ud.%ud",
4520Sigor@sysoev.ru                         addr[12], addr[13], addr[14], addr[15]);
4530Sigor@sysoev.ru     }
4540Sigor@sysoev.ru 
4550Sigor@sysoev.ru     return p;
4560Sigor@sysoev.ru }
4570Sigor@sysoev.ru 
4580Sigor@sysoev.ru #endif
4590Sigor@sysoev.ru 
4600Sigor@sysoev.ru 
4610Sigor@sysoev.ru void
4620Sigor@sysoev.ru nxt_job_sockaddr_parse(nxt_job_sockaddr_parse_t *jbs)
4630Sigor@sysoev.ru {
4640Sigor@sysoev.ru     u_char              *p;
4650Sigor@sysoev.ru     size_t              len;
4660Sigor@sysoev.ru     nxt_int_t           ret;
4670Sigor@sysoev.ru     nxt_work_handler_t  handler;
4680Sigor@sysoev.ru 
4690Sigor@sysoev.ru     nxt_job_set_name(&jbs->resolve.job, "job sockaddr parse");
4700Sigor@sysoev.ru 
4710Sigor@sysoev.ru     len = jbs->addr.len;
4720Sigor@sysoev.ru     p = jbs->addr.data;
4730Sigor@sysoev.ru 
4740Sigor@sysoev.ru     if (len > 6 && nxt_memcmp(p, (u_char *) "unix:", 5) == 0) {
4750Sigor@sysoev.ru         ret = nxt_job_sockaddr_unix_parse(jbs);
4760Sigor@sysoev.ru 
4770Sigor@sysoev.ru     } else if (len != 0 && *p == '[') {
4780Sigor@sysoev.ru         ret = nxt_job_sockaddr_inet6_parse(jbs);
4790Sigor@sysoev.ru 
4800Sigor@sysoev.ru     } else {
4810Sigor@sysoev.ru         ret = nxt_job_sockaddr_inet_parse(jbs);
4820Sigor@sysoev.ru     }
4830Sigor@sysoev.ru 
4840Sigor@sysoev.ru     switch (ret) {
4850Sigor@sysoev.ru 
4860Sigor@sysoev.ru     case NXT_OK:
4870Sigor@sysoev.ru         handler = jbs->resolve.ready_handler;
4880Sigor@sysoev.ru         break;
4890Sigor@sysoev.ru 
4900Sigor@sysoev.ru     case NXT_ERROR:
4910Sigor@sysoev.ru         handler = jbs->resolve.error_handler;
4920Sigor@sysoev.ru         break;
4930Sigor@sysoev.ru 
4940Sigor@sysoev.ru     default: /* NXT_AGAIN */
4950Sigor@sysoev.ru         return;
4960Sigor@sysoev.ru     }
4970Sigor@sysoev.ru 
498*4Sigor@sysoev.ru     nxt_job_return(jbs->resolve.job.task, &jbs->resolve.job, handler);
4990Sigor@sysoev.ru }
5000Sigor@sysoev.ru 
5010Sigor@sysoev.ru 
5020Sigor@sysoev.ru static nxt_int_t
5030Sigor@sysoev.ru nxt_job_sockaddr_unix_parse(nxt_job_sockaddr_parse_t *jbs)
5040Sigor@sysoev.ru {
5050Sigor@sysoev.ru #if (NXT_HAVE_UNIX_DOMAIN)
5060Sigor@sysoev.ru     size_t          len, socklen;
5070Sigor@sysoev.ru     u_char          *path;
5080Sigor@sysoev.ru     nxt_mem_pool_t  *mp;
5090Sigor@sysoev.ru     nxt_sockaddr_t  *sa;
5100Sigor@sysoev.ru 
5110Sigor@sysoev.ru     /*
5120Sigor@sysoev.ru      * Actual sockaddr_un length can be lesser or even larger than defined
5130Sigor@sysoev.ru      * struct sockaddr_un length (see comment in unix/nxt_socket.h).  So
5140Sigor@sysoev.ru      * limit maximum Unix domain socket address length by defined sun_path[]
5150Sigor@sysoev.ru      * length because some OSes accept addresses twice larger than defined
5160Sigor@sysoev.ru      * struct sockaddr_un.  Also reserve space for a trailing zero to avoid
5170Sigor@sysoev.ru      * ambiguity, since many OSes accept Unix domain socket addresses
5180Sigor@sysoev.ru      * without a trailing zero.
5190Sigor@sysoev.ru      */
5200Sigor@sysoev.ru     const size_t max_len = sizeof(struct sockaddr_un)
5210Sigor@sysoev.ru                            - offsetof(struct sockaddr_un, sun_path) - 1;
5220Sigor@sysoev.ru 
5230Sigor@sysoev.ru     /* cutting "unix:" */
5240Sigor@sysoev.ru     len = jbs->addr.len - 5;
5250Sigor@sysoev.ru     path = jbs->addr.data + 5;
5260Sigor@sysoev.ru 
5270Sigor@sysoev.ru     if (len > max_len) {
5280Sigor@sysoev.ru         nxt_thread_log_error(jbs->resolve.log_level,
5290Sigor@sysoev.ru                              "unix domain socket \"%V\" name is too long",
5300Sigor@sysoev.ru                              &jbs->addr);
5310Sigor@sysoev.ru         return NXT_ERROR;
5320Sigor@sysoev.ru     }
5330Sigor@sysoev.ru 
5340Sigor@sysoev.ru     socklen = offsetof(struct sockaddr_un, sun_path) + len + 1;
5350Sigor@sysoev.ru 
5360Sigor@sysoev.ru #if (NXT_LINUX)
5370Sigor@sysoev.ru 
5380Sigor@sysoev.ru     /*
5390Sigor@sysoev.ru      * Linux unix(7):
5400Sigor@sysoev.ru      *
5410Sigor@sysoev.ru      *   abstract: an abstract socket address is distinguished by the fact
5420Sigor@sysoev.ru      *   that sun_path[0] is a null byte ('\0').  The socket's address in
5430Sigor@sysoev.ru      *   this namespace is given by the additional bytes in sun_path that
5440Sigor@sysoev.ru      *   are covered by the specified length of the address structure.
5450Sigor@sysoev.ru      *   (Null bytes in the name have no special significance.)
5460Sigor@sysoev.ru      */
5470Sigor@sysoev.ru     if (path[0] == '\0') {
5480Sigor@sysoev.ru         socklen--;
5490Sigor@sysoev.ru     }
5500Sigor@sysoev.ru 
5510Sigor@sysoev.ru #endif
5520Sigor@sysoev.ru 
5530Sigor@sysoev.ru     mp = jbs->resolve.job.mem_pool;
5540Sigor@sysoev.ru 
5550Sigor@sysoev.ru     jbs->resolve.sockaddrs = nxt_mem_alloc(mp, sizeof(void *));
5560Sigor@sysoev.ru 
5570Sigor@sysoev.ru     if (nxt_fast_path(jbs->resolve.sockaddrs != NULL)) {
5580Sigor@sysoev.ru         sa = nxt_sockaddr_alloc(mp, socklen);
5590Sigor@sysoev.ru 
5600Sigor@sysoev.ru         if (nxt_fast_path(sa != NULL)) {
5610Sigor@sysoev.ru             jbs->resolve.count = 1;
5620Sigor@sysoev.ru             jbs->resolve.sockaddrs[0] = sa;
5630Sigor@sysoev.ru 
5640Sigor@sysoev.ru             sa->u.sockaddr_un.sun_family = AF_UNIX;
5650Sigor@sysoev.ru             nxt_memcpy(sa->u.sockaddr_un.sun_path, path, len);
5660Sigor@sysoev.ru 
5670Sigor@sysoev.ru             return NXT_OK;
5680Sigor@sysoev.ru         }
5690Sigor@sysoev.ru     }
5700Sigor@sysoev.ru 
5710Sigor@sysoev.ru     return NXT_ERROR;
5720Sigor@sysoev.ru 
5730Sigor@sysoev.ru #else  /* !(NXT_HAVE_UNIX_DOMAIN) */
5740Sigor@sysoev.ru 
5750Sigor@sysoev.ru     nxt_thread_log_error(jbs->resolve.log_level,
5760Sigor@sysoev.ru                          "unix domain socket \"%V\" is not supported",
5770Sigor@sysoev.ru                          &jbs->addr);
5780Sigor@sysoev.ru     return NXT_ERROR;
5790Sigor@sysoev.ru 
5800Sigor@sysoev.ru #endif
5810Sigor@sysoev.ru }
5820Sigor@sysoev.ru 
5830Sigor@sysoev.ru 
5840Sigor@sysoev.ru static nxt_int_t
5850Sigor@sysoev.ru nxt_job_sockaddr_inet6_parse(nxt_job_sockaddr_parse_t *jbs)
5860Sigor@sysoev.ru {
5870Sigor@sysoev.ru #if (NXT_INET6)
5880Sigor@sysoev.ru     u_char           *p, *addr, *addr_end;
5890Sigor@sysoev.ru     size_t           len;
5900Sigor@sysoev.ru     nxt_int_t        port;
5910Sigor@sysoev.ru     nxt_mem_pool_t   *mp;
5920Sigor@sysoev.ru     nxt_sockaddr_t   *sa;
5930Sigor@sysoev.ru     struct in6_addr  *in6_addr;
5940Sigor@sysoev.ru 
5950Sigor@sysoev.ru     len = jbs->addr.len - 1;
5960Sigor@sysoev.ru     addr = jbs->addr.data + 1;
5970Sigor@sysoev.ru 
5980Sigor@sysoev.ru     addr_end = nxt_memchr(addr, ']', len);
5990Sigor@sysoev.ru 
6000Sigor@sysoev.ru     if (addr_end == NULL) {
6010Sigor@sysoev.ru         goto invalid_address;
6020Sigor@sysoev.ru     }
6030Sigor@sysoev.ru 
6040Sigor@sysoev.ru     mp = jbs->resolve.job.mem_pool;
6050Sigor@sysoev.ru 
6060Sigor@sysoev.ru     jbs->resolve.sockaddrs = nxt_mem_alloc(mp, sizeof(void *));
6070Sigor@sysoev.ru 
6080Sigor@sysoev.ru     if (nxt_slow_path(jbs->resolve.sockaddrs == NULL)) {
6090Sigor@sysoev.ru         return NXT_ERROR;
6100Sigor@sysoev.ru     }
6110Sigor@sysoev.ru 
6120Sigor@sysoev.ru     sa = nxt_sockaddr_alloc(mp, sizeof(struct sockaddr_in6));
6130Sigor@sysoev.ru 
6140Sigor@sysoev.ru     if (nxt_slow_path(sa == NULL)) {
6150Sigor@sysoev.ru         return NXT_ERROR;
6160Sigor@sysoev.ru     }
6170Sigor@sysoev.ru 
6180Sigor@sysoev.ru     jbs->resolve.count = 1;
6190Sigor@sysoev.ru     jbs->resolve.sockaddrs[0] = sa;
6200Sigor@sysoev.ru 
6210Sigor@sysoev.ru     in6_addr = &sa->u.sockaddr_in6.sin6_addr;
6220Sigor@sysoev.ru 
6230Sigor@sysoev.ru     if (nxt_inet6_addr(in6_addr, addr, addr_end - addr) != NXT_OK) {
6240Sigor@sysoev.ru         goto invalid_address;
6250Sigor@sysoev.ru     }
6260Sigor@sysoev.ru 
6270Sigor@sysoev.ru     p = addr_end + 1;
6280Sigor@sysoev.ru     len = (addr + len) - p;
6290Sigor@sysoev.ru 
6300Sigor@sysoev.ru     if (len == 0) {
6310Sigor@sysoev.ru         jbs->no_port = 1;
6320Sigor@sysoev.ru         port = jbs->resolve.port;
6330Sigor@sysoev.ru         goto found;
6340Sigor@sysoev.ru     }
6350Sigor@sysoev.ru 
6360Sigor@sysoev.ru     if (*p == ':') {
6370Sigor@sysoev.ru         port = nxt_int_parse(p + 1, len - 1);
6380Sigor@sysoev.ru 
6390Sigor@sysoev.ru         if (port >= 1 && port <= 65535) {
6400Sigor@sysoev.ru             port = htons((in_port_t) port);
6410Sigor@sysoev.ru             goto found;
6420Sigor@sysoev.ru         }
6430Sigor@sysoev.ru     }
6440Sigor@sysoev.ru 
6450Sigor@sysoev.ru     nxt_thread_log_error(jbs->resolve.log_level,
6460Sigor@sysoev.ru                          "invalid port in \"%V\"", &jbs->addr);
6470Sigor@sysoev.ru 
6480Sigor@sysoev.ru     return NXT_ERROR;
6490Sigor@sysoev.ru 
6500Sigor@sysoev.ru found:
6510Sigor@sysoev.ru 
6520Sigor@sysoev.ru     sa->u.sockaddr_in6.sin6_family = AF_INET6;
6530Sigor@sysoev.ru     sa->u.sockaddr_in6.sin6_port = (in_port_t) port;
6540Sigor@sysoev.ru 
6550Sigor@sysoev.ru     if (IN6_IS_ADDR_UNSPECIFIED(in6_addr)) {
6560Sigor@sysoev.ru         jbs->wildcard = 1;
6570Sigor@sysoev.ru     }
6580Sigor@sysoev.ru 
6590Sigor@sysoev.ru     return NXT_OK;
6600Sigor@sysoev.ru 
6610Sigor@sysoev.ru invalid_address:
6620Sigor@sysoev.ru 
6630Sigor@sysoev.ru     nxt_thread_log_error(jbs->resolve.log_level,
6640Sigor@sysoev.ru                          "invalid IPv6 address in \"%V\"", &jbs->addr);
6650Sigor@sysoev.ru     return NXT_ERROR;
6660Sigor@sysoev.ru 
6670Sigor@sysoev.ru #else
6680Sigor@sysoev.ru 
6690Sigor@sysoev.ru     nxt_thread_log_error(jbs->resolve.log_level,
6700Sigor@sysoev.ru                          "IPv6 socket \"%V\" is not supported", &jbs->addr);
6710Sigor@sysoev.ru     return NXT_ERROR;
6720Sigor@sysoev.ru 
6730Sigor@sysoev.ru #endif
6740Sigor@sysoev.ru }
6750Sigor@sysoev.ru 
6760Sigor@sysoev.ru 
6770Sigor@sysoev.ru static nxt_int_t
6780Sigor@sysoev.ru nxt_job_sockaddr_inet_parse(nxt_job_sockaddr_parse_t *jbs)
6790Sigor@sysoev.ru {
6800Sigor@sysoev.ru     u_char          *p, *host;
6810Sigor@sysoev.ru     size_t          len;
6820Sigor@sysoev.ru     in_addr_t       addr;
6830Sigor@sysoev.ru     nxt_int_t       port;
6840Sigor@sysoev.ru     nxt_mem_pool_t  *mp;
6850Sigor@sysoev.ru     nxt_sockaddr_t  *sa;
6860Sigor@sysoev.ru 
6870Sigor@sysoev.ru     addr = INADDR_ANY;
6880Sigor@sysoev.ru 
6890Sigor@sysoev.ru     len = jbs->addr.len;
6900Sigor@sysoev.ru     host = jbs->addr.data;
6910Sigor@sysoev.ru 
6920Sigor@sysoev.ru     p = nxt_memchr(host, ':', len);
6930Sigor@sysoev.ru 
6940Sigor@sysoev.ru     if (p == NULL) {
6950Sigor@sysoev.ru 
6960Sigor@sysoev.ru         /* single value port, address, or host name */
6970Sigor@sysoev.ru 
6980Sigor@sysoev.ru         port = nxt_int_parse(host, len);
6990Sigor@sysoev.ru 
7000Sigor@sysoev.ru         if (port > 0) {
7010Sigor@sysoev.ru             if (port < 1 || port > 65535) {
7020Sigor@sysoev.ru                 goto invalid_port;
7030Sigor@sysoev.ru             }
7040Sigor@sysoev.ru 
7050Sigor@sysoev.ru             /* "*:XX" */
7060Sigor@sysoev.ru             port = htons((in_port_t) port);
7070Sigor@sysoev.ru             jbs->resolve.port = (in_port_t) port;
7080Sigor@sysoev.ru 
7090Sigor@sysoev.ru         } else {
7100Sigor@sysoev.ru             jbs->no_port = 1;
7110Sigor@sysoev.ru 
7120Sigor@sysoev.ru             addr = nxt_inet_addr(host, len);
7130Sigor@sysoev.ru 
7140Sigor@sysoev.ru             if (addr == INADDR_NONE) {
7150Sigor@sysoev.ru                 jbs->resolve.name.len = len;
7160Sigor@sysoev.ru                 jbs->resolve.name.data = host;
7170Sigor@sysoev.ru 
7180Sigor@sysoev.ru                 nxt_job_resolve(&jbs->resolve);
7190Sigor@sysoev.ru                 return NXT_AGAIN;
7200Sigor@sysoev.ru             }
7210Sigor@sysoev.ru 
7220Sigor@sysoev.ru             /* "x.x.x.x" */
7230Sigor@sysoev.ru             port = jbs->resolve.port;
7240Sigor@sysoev.ru         }
7250Sigor@sysoev.ru 
7260Sigor@sysoev.ru     } else {
7270Sigor@sysoev.ru 
7280Sigor@sysoev.ru         /* x.x.x.x:XX or host:XX */
7290Sigor@sysoev.ru 
7300Sigor@sysoev.ru         p++;
7310Sigor@sysoev.ru         len = (host + len) - p;
7320Sigor@sysoev.ru         port = nxt_int_parse(p, len);
7330Sigor@sysoev.ru 
7340Sigor@sysoev.ru         if (port < 1 || port > 65535) {
7350Sigor@sysoev.ru             goto invalid_port;
7360Sigor@sysoev.ru         }
7370Sigor@sysoev.ru 
7380Sigor@sysoev.ru         port = htons((in_port_t) port);
7390Sigor@sysoev.ru 
7400Sigor@sysoev.ru         len = (p - 1) - host;
7410Sigor@sysoev.ru 
7420Sigor@sysoev.ru         if (len != 1 || host[0] != '*') {
7430Sigor@sysoev.ru             addr = nxt_inet_addr(host, len);
7440Sigor@sysoev.ru 
7450Sigor@sysoev.ru             if (addr == INADDR_NONE) {
7460Sigor@sysoev.ru                 jbs->resolve.name.len = len;
7470Sigor@sysoev.ru                 jbs->resolve.name.data = host;
7480Sigor@sysoev.ru                 jbs->resolve.port = (in_port_t) port;
7490Sigor@sysoev.ru 
7500Sigor@sysoev.ru                 nxt_job_resolve(&jbs->resolve);
7510Sigor@sysoev.ru                 return NXT_AGAIN;
7520Sigor@sysoev.ru             }
7530Sigor@sysoev.ru 
7540Sigor@sysoev.ru             /* "x.x.x.x:XX" */
7550Sigor@sysoev.ru         }
7560Sigor@sysoev.ru     }
7570Sigor@sysoev.ru 
7580Sigor@sysoev.ru     mp = jbs->resolve.job.mem_pool;
7590Sigor@sysoev.ru 
7600Sigor@sysoev.ru     jbs->resolve.sockaddrs = nxt_mem_alloc(mp, sizeof(void *));
7610Sigor@sysoev.ru     if (nxt_slow_path(jbs->resolve.sockaddrs == NULL)) {
7620Sigor@sysoev.ru         return NXT_ERROR;
7630Sigor@sysoev.ru     }
7640Sigor@sysoev.ru 
7650Sigor@sysoev.ru     sa = nxt_sockaddr_alloc(mp, sizeof(struct sockaddr_in));
7660Sigor@sysoev.ru 
7670Sigor@sysoev.ru     if (nxt_fast_path(sa != NULL)) {
7680Sigor@sysoev.ru         jbs->resolve.count = 1;
7690Sigor@sysoev.ru         jbs->resolve.sockaddrs[0] = sa;
7700Sigor@sysoev.ru 
7710Sigor@sysoev.ru         jbs->wildcard = (addr == INADDR_ANY);
7720Sigor@sysoev.ru 
7730Sigor@sysoev.ru         sa->u.sockaddr_in.sin_family = AF_INET;
7740Sigor@sysoev.ru         sa->u.sockaddr_in.sin_port = (in_port_t) port;
7750Sigor@sysoev.ru         sa->u.sockaddr_in.sin_addr.s_addr = addr;
7760Sigor@sysoev.ru 
7770Sigor@sysoev.ru         return NXT_OK;
7780Sigor@sysoev.ru     }
7790Sigor@sysoev.ru 
7800Sigor@sysoev.ru     return NXT_ERROR;
7810Sigor@sysoev.ru 
7820Sigor@sysoev.ru invalid_port:
7830Sigor@sysoev.ru 
7840Sigor@sysoev.ru     nxt_thread_log_error(jbs->resolve.log_level,
7850Sigor@sysoev.ru                          "invalid port in \"%V\"", &jbs->addr);
7860Sigor@sysoev.ru 
7870Sigor@sysoev.ru     return NXT_ERROR;
7880Sigor@sysoev.ru }
7890Sigor@sysoev.ru 
7900Sigor@sysoev.ru 
7910Sigor@sysoev.ru in_addr_t
7920Sigor@sysoev.ru nxt_inet_addr(u_char *buf, size_t len)
7930Sigor@sysoev.ru {
7940Sigor@sysoev.ru     u_char      c, *end;
7950Sigor@sysoev.ru     in_addr_t   addr;
7960Sigor@sysoev.ru     nxt_uint_t  digit, octet, dots;
7970Sigor@sysoev.ru 
7980Sigor@sysoev.ru     addr = 0;
7990Sigor@sysoev.ru     octet = 0;
8000Sigor@sysoev.ru     dots = 0;
8010Sigor@sysoev.ru 
8020Sigor@sysoev.ru     end = buf + len;
8030Sigor@sysoev.ru 
8040Sigor@sysoev.ru     while (buf < end) {
8050Sigor@sysoev.ru 
8060Sigor@sysoev.ru         c = *buf++;
8070Sigor@sysoev.ru 
8080Sigor@sysoev.ru         digit = c - '0';
8090Sigor@sysoev.ru         /* values below '0' become large unsigned integers */
8100Sigor@sysoev.ru 
8110Sigor@sysoev.ru         if (digit < 10) {
8120Sigor@sysoev.ru             octet = octet * 10 + digit;
8130Sigor@sysoev.ru             continue;
8140Sigor@sysoev.ru         }
8150Sigor@sysoev.ru 
8160Sigor@sysoev.ru         if (c == '.' && octet < 256) {
8170Sigor@sysoev.ru             addr = (addr << 8) + octet;
8180Sigor@sysoev.ru             octet = 0;
8190Sigor@sysoev.ru             dots++;
8200Sigor@sysoev.ru             continue;
8210Sigor@sysoev.ru         }
8220Sigor@sysoev.ru 
8230Sigor@sysoev.ru         return INADDR_NONE;
8240Sigor@sysoev.ru     }
8250Sigor@sysoev.ru 
8260Sigor@sysoev.ru     if (dots == 3 && octet < 256) {
8270Sigor@sysoev.ru         addr = (addr << 8) + octet;
8280Sigor@sysoev.ru         return htonl(addr);
8290Sigor@sysoev.ru     }
8300Sigor@sysoev.ru 
8310Sigor@sysoev.ru     return INADDR_NONE;
8320Sigor@sysoev.ru }
8330Sigor@sysoev.ru 
8340Sigor@sysoev.ru 
8350Sigor@sysoev.ru #if (NXT_INET6)
8360Sigor@sysoev.ru 
8370Sigor@sysoev.ru nxt_int_t
8380Sigor@sysoev.ru nxt_inet6_addr(struct in6_addr *in6_addr, u_char *buf, size_t len)
8390Sigor@sysoev.ru {
8400Sigor@sysoev.ru     u_char      c, *addr, *zero_start, *ipv4, *dst, *src, *end;
8410Sigor@sysoev.ru     nxt_uint_t  digit, group, nibbles, groups_left;
8420Sigor@sysoev.ru 
8430Sigor@sysoev.ru     if (len == 0) {
8440Sigor@sysoev.ru         return NXT_ERROR;
8450Sigor@sysoev.ru     }
8460Sigor@sysoev.ru 
8470Sigor@sysoev.ru     end = buf + len;
8480Sigor@sysoev.ru 
8490Sigor@sysoev.ru     if (buf[0] == ':') {
8500Sigor@sysoev.ru         buf++;
8510Sigor@sysoev.ru     }
8520Sigor@sysoev.ru 
8530Sigor@sysoev.ru     addr = in6_addr->s6_addr;
8540Sigor@sysoev.ru     zero_start = NULL;
8550Sigor@sysoev.ru     groups_left = 8;
8560Sigor@sysoev.ru     nibbles = 0;
8570Sigor@sysoev.ru     group = 0;
8580Sigor@sysoev.ru     ipv4 = NULL;
8590Sigor@sysoev.ru 
8600Sigor@sysoev.ru     while (buf < end) {
8610Sigor@sysoev.ru         c = *buf++;
8620Sigor@sysoev.ru 
8630Sigor@sysoev.ru         if (c == ':') {
8640Sigor@sysoev.ru             if (nibbles != 0) {
8650Sigor@sysoev.ru                 ipv4 = buf;
8660Sigor@sysoev.ru 
8670Sigor@sysoev.ru                 *addr++ = (u_char) (group >> 8);
8680Sigor@sysoev.ru                 *addr++ = (u_char) (group & 0xff);
8690Sigor@sysoev.ru                 groups_left--;
8700Sigor@sysoev.ru 
8710Sigor@sysoev.ru                 if (groups_left != 0) {
8720Sigor@sysoev.ru                     nibbles = 0;
8730Sigor@sysoev.ru                     group = 0;
8740Sigor@sysoev.ru                     continue;
8750Sigor@sysoev.ru                 }
8760Sigor@sysoev.ru 
8770Sigor@sysoev.ru             } else {
8780Sigor@sysoev.ru                 if (zero_start == NULL) {
8790Sigor@sysoev.ru                     ipv4 = buf;
8800Sigor@sysoev.ru                     zero_start = addr;
8810Sigor@sysoev.ru                     continue;
8820Sigor@sysoev.ru                 }
8830Sigor@sysoev.ru             }
8840Sigor@sysoev.ru 
8850Sigor@sysoev.ru             return NXT_ERROR;
8860Sigor@sysoev.ru         }
8870Sigor@sysoev.ru 
8880Sigor@sysoev.ru         if (c == '.' && nibbles != 0) {
8890Sigor@sysoev.ru 
8900Sigor@sysoev.ru             if (groups_left < 2 || ipv4 == NULL) {
8910Sigor@sysoev.ru                 return NXT_ERROR;
8920Sigor@sysoev.ru             }
8930Sigor@sysoev.ru 
8940Sigor@sysoev.ru             group = nxt_inet_addr(ipv4, end - ipv4);
8950Sigor@sysoev.ru             if (group == INADDR_NONE) {
8960Sigor@sysoev.ru                 return NXT_ERROR;
8970Sigor@sysoev.ru             }
8980Sigor@sysoev.ru 
8990Sigor@sysoev.ru             group = ntohl(group);
9000Sigor@sysoev.ru 
9010Sigor@sysoev.ru             *addr++ = (u_char) ((group >> 24) & 0xff);
9020Sigor@sysoev.ru             *addr++ = (u_char) ((group >> 16) & 0xff);
9030Sigor@sysoev.ru             groups_left--;
9040Sigor@sysoev.ru 
9050Sigor@sysoev.ru             /* the low 16-bit are copied below */
9060Sigor@sysoev.ru             break;
9070Sigor@sysoev.ru         }
9080Sigor@sysoev.ru 
9090Sigor@sysoev.ru         nibbles++;
9100Sigor@sysoev.ru 
9110Sigor@sysoev.ru         if (nibbles > 4) {
9120Sigor@sysoev.ru             return NXT_ERROR;
9130Sigor@sysoev.ru         }
9140Sigor@sysoev.ru 
9150Sigor@sysoev.ru         group <<= 4;
9160Sigor@sysoev.ru 
9170Sigor@sysoev.ru         digit = c - '0';
9180Sigor@sysoev.ru         /* values below '0' become large unsigned integers */
9190Sigor@sysoev.ru 
9200Sigor@sysoev.ru         if (digit < 10) {
9210Sigor@sysoev.ru             group += digit;
9220Sigor@sysoev.ru             continue;
9230Sigor@sysoev.ru         }
9240Sigor@sysoev.ru 
9250Sigor@sysoev.ru         c |= 0x20;
9260Sigor@sysoev.ru         digit = c - 'a';
9270Sigor@sysoev.ru         /* values below 'a' become large unsigned integers */
9280Sigor@sysoev.ru 
9290Sigor@sysoev.ru         if (digit < 6) {
9300Sigor@sysoev.ru             group += 10 + digit;
9310Sigor@sysoev.ru             continue;
9320Sigor@sysoev.ru         }
9330Sigor@sysoev.ru 
9340Sigor@sysoev.ru         return NXT_ERROR;
9350Sigor@sysoev.ru     }
9360Sigor@sysoev.ru 
9370Sigor@sysoev.ru     if (nibbles == 0 && zero_start == NULL) {
9380Sigor@sysoev.ru         return NXT_ERROR;
9390Sigor@sysoev.ru     }
9400Sigor@sysoev.ru 
9410Sigor@sysoev.ru     *addr++ = (u_char) (group >> 8);
9420Sigor@sysoev.ru     *addr++ = (u_char) (group & 0xff);
9430Sigor@sysoev.ru     groups_left--;
9440Sigor@sysoev.ru 
9450Sigor@sysoev.ru     if (groups_left != 0) {
9460Sigor@sysoev.ru 
9470Sigor@sysoev.ru         if (zero_start != NULL) {
9480Sigor@sysoev.ru 
9490Sigor@sysoev.ru             /* moving part before consecutive zero groups to the end */
9500Sigor@sysoev.ru 
9510Sigor@sysoev.ru             groups_left *= 2;
9520Sigor@sysoev.ru             src = addr - 1;
9530Sigor@sysoev.ru             dst = src + groups_left;
9540Sigor@sysoev.ru 
9550Sigor@sysoev.ru             while (src >= zero_start) {
9560Sigor@sysoev.ru                 *dst-- = *src--;
9570Sigor@sysoev.ru             }
9580Sigor@sysoev.ru 
9590Sigor@sysoev.ru             nxt_memzero(zero_start, groups_left);
9600Sigor@sysoev.ru 
9610Sigor@sysoev.ru             return NXT_OK;
9620Sigor@sysoev.ru         }
9630Sigor@sysoev.ru 
9640Sigor@sysoev.ru     } else {
9650Sigor@sysoev.ru         if (zero_start == NULL) {
9660Sigor@sysoev.ru             return NXT_OK;
9670Sigor@sysoev.ru         }
9680Sigor@sysoev.ru     }
9690Sigor@sysoev.ru 
9700Sigor@sysoev.ru     return NXT_ERROR;
9710Sigor@sysoev.ru }
9720Sigor@sysoev.ru 
9730Sigor@sysoev.ru #endif
974