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