11324Saxel.duch@nginx.com 21324Saxel.duch@nginx.com /* 31324Saxel.duch@nginx.com * Copyright (C) Axel Duch 41324Saxel.duch@nginx.com * Copyright (C) NGINX, Inc. 51324Saxel.duch@nginx.com */ 61324Saxel.duch@nginx.com 71324Saxel.duch@nginx.com #include <nxt_main.h> 81324Saxel.duch@nginx.com #include <nxt_http_route_addr.h> 91324Saxel.duch@nginx.com 101324Saxel.duch@nginx.com 111324Saxel.duch@nginx.com static nxt_bool_t nxt_str_looks_like_ipv6(const nxt_str_t *str); 121324Saxel.duch@nginx.com #if (NXT_INET6) 131324Saxel.duch@nginx.com static nxt_bool_t nxt_valid_ipv6_blocks(u_char *c, size_t len); 141324Saxel.duch@nginx.com #endif 151324Saxel.duch@nginx.com 161324Saxel.duch@nginx.com 171324Saxel.duch@nginx.com nxt_int_t 181324Saxel.duch@nginx.com nxt_http_route_addr_pattern_parse(nxt_mp_t *mp, 191324Saxel.duch@nginx.com nxt_http_route_addr_pattern_t *pattern, nxt_conf_value_t *cv) 201324Saxel.duch@nginx.com { 21*1343Saxel.duch@nginx.com u_char *delim; 221324Saxel.duch@nginx.com nxt_int_t ret, cidr_prefix; 231324Saxel.duch@nginx.com nxt_str_t addr, port; 241324Saxel.duch@nginx.com nxt_http_route_addr_base_t *base; 251324Saxel.duch@nginx.com nxt_http_route_addr_range_t *inet; 261324Saxel.duch@nginx.com 271324Saxel.duch@nginx.com if (nxt_conf_type(cv) != NXT_CONF_STRING) { 281324Saxel.duch@nginx.com return NXT_ADDR_PATTERN_CV_TYPE_ERROR; 291324Saxel.duch@nginx.com } 301324Saxel.duch@nginx.com 311324Saxel.duch@nginx.com nxt_conf_get_string(cv, &addr); 321324Saxel.duch@nginx.com 331324Saxel.duch@nginx.com base = &pattern->base; 341324Saxel.duch@nginx.com 351324Saxel.duch@nginx.com if (addr.length > 0 && addr.start[0] == '!') { 361324Saxel.duch@nginx.com addr.start++; 371324Saxel.duch@nginx.com addr.length--; 381324Saxel.duch@nginx.com 391324Saxel.duch@nginx.com base->negative = 1; 401324Saxel.duch@nginx.com 411324Saxel.duch@nginx.com } else { 421324Saxel.duch@nginx.com base->negative = 0; 431324Saxel.duch@nginx.com } 441324Saxel.duch@nginx.com 451324Saxel.duch@nginx.com if (nxt_slow_path(addr.length < 2)) { 461324Saxel.duch@nginx.com return NXT_ADDR_PATTERN_LENGTH_ERROR; 471324Saxel.duch@nginx.com } 481324Saxel.duch@nginx.com 491324Saxel.duch@nginx.com nxt_str_null(&port); 501324Saxel.duch@nginx.com 511324Saxel.duch@nginx.com if (addr.start[0] == '*' && addr.start[1] == ':') { 521324Saxel.duch@nginx.com port.start = addr.start + 2; 531324Saxel.duch@nginx.com port.length = addr.length - 2; 541324Saxel.duch@nginx.com base->addr_family = AF_UNSPEC; 551324Saxel.duch@nginx.com base->match_type = NXT_HTTP_ROUTE_ADDR_ANY; 561324Saxel.duch@nginx.com 571324Saxel.duch@nginx.com goto parse_port; 581324Saxel.duch@nginx.com } 591324Saxel.duch@nginx.com 601324Saxel.duch@nginx.com if (nxt_str_looks_like_ipv6(&addr)) { 611324Saxel.duch@nginx.com #if (NXT_INET6) 62*1343Saxel.duch@nginx.com u_char *end; 631324Saxel.duch@nginx.com uint8_t i; 641324Saxel.duch@nginx.com nxt_int_t len; 651324Saxel.duch@nginx.com nxt_http_route_in6_addr_range_t *inet6; 661324Saxel.duch@nginx.com 671324Saxel.duch@nginx.com base->addr_family = AF_INET6; 681324Saxel.duch@nginx.com 691324Saxel.duch@nginx.com if (addr.start[0] == '[') { 701324Saxel.duch@nginx.com addr.start++; 711324Saxel.duch@nginx.com addr.length--; 721324Saxel.duch@nginx.com 731324Saxel.duch@nginx.com end = addr.start + addr.length; 741324Saxel.duch@nginx.com 751324Saxel.duch@nginx.com port.start = nxt_rmemstrn(addr.start, end, "]:", 2); 761324Saxel.duch@nginx.com if (nxt_slow_path(port.start == NULL)) { 771324Saxel.duch@nginx.com return NXT_ADDR_PATTERN_FORMAT_ERROR; 781324Saxel.duch@nginx.com } 791324Saxel.duch@nginx.com 801324Saxel.duch@nginx.com addr.length = port.start - addr.start; 811324Saxel.duch@nginx.com port.start += nxt_length("]:"); 821324Saxel.duch@nginx.com port.length = end - port.start; 831324Saxel.duch@nginx.com } 841324Saxel.duch@nginx.com 851324Saxel.duch@nginx.com inet6 = &pattern->addr.v6; 861324Saxel.duch@nginx.com 871324Saxel.duch@nginx.com delim = nxt_memchr(addr.start, '-', addr.length); 881324Saxel.duch@nginx.com if (delim != NULL) { 891324Saxel.duch@nginx.com len = delim - addr.start; 901324Saxel.duch@nginx.com if (nxt_slow_path(!nxt_valid_ipv6_blocks(addr.start, len))) { 911324Saxel.duch@nginx.com return NXT_ADDR_PATTERN_FORMAT_ERROR; 921324Saxel.duch@nginx.com } 931324Saxel.duch@nginx.com 941324Saxel.duch@nginx.com ret = nxt_inet6_addr(&inet6->start, addr.start, len); 951324Saxel.duch@nginx.com if (nxt_slow_path(ret != NXT_OK)) { 961324Saxel.duch@nginx.com return NXT_ADDR_PATTERN_FORMAT_ERROR; 971324Saxel.duch@nginx.com } 981324Saxel.duch@nginx.com 991324Saxel.duch@nginx.com len = addr.start + addr.length - delim - 1; 1001324Saxel.duch@nginx.com if (nxt_slow_path(!nxt_valid_ipv6_blocks(delim + 1, len))) { 1011324Saxel.duch@nginx.com return NXT_ADDR_PATTERN_FORMAT_ERROR; 1021324Saxel.duch@nginx.com } 1031324Saxel.duch@nginx.com 1041324Saxel.duch@nginx.com ret = nxt_inet6_addr(&inet6->end, delim + 1, len); 1051324Saxel.duch@nginx.com if (nxt_slow_path(ret != NXT_OK)) { 1061324Saxel.duch@nginx.com return NXT_ADDR_PATTERN_FORMAT_ERROR; 1071324Saxel.duch@nginx.com } 1081324Saxel.duch@nginx.com 1091324Saxel.duch@nginx.com if (nxt_slow_path(nxt_memcmp(&inet6->start, &inet6->end, 1101324Saxel.duch@nginx.com sizeof(struct in6_addr)) > 0)) 1111324Saxel.duch@nginx.com { 1121324Saxel.duch@nginx.com return NXT_ADDR_PATTERN_RANGE_OVERLAP_ERROR; 1131324Saxel.duch@nginx.com } 1141324Saxel.duch@nginx.com 1151324Saxel.duch@nginx.com base->match_type = NXT_HTTP_ROUTE_ADDR_RANGE; 1161324Saxel.duch@nginx.com 1171324Saxel.duch@nginx.com goto parse_port; 1181324Saxel.duch@nginx.com } 1191324Saxel.duch@nginx.com 1201324Saxel.duch@nginx.com delim = nxt_memchr(addr.start, '/', addr.length); 1211324Saxel.duch@nginx.com if (delim != NULL) { 1221324Saxel.duch@nginx.com cidr_prefix = nxt_int_parse(delim + 1, 1231324Saxel.duch@nginx.com addr.start + addr.length - (delim + 1)); 1241324Saxel.duch@nginx.com if (nxt_slow_path(cidr_prefix < 0 || cidr_prefix > 128)) { 1251324Saxel.duch@nginx.com return NXT_ADDR_PATTERN_CIDR_ERROR; 1261324Saxel.duch@nginx.com } 1271324Saxel.duch@nginx.com 1281324Saxel.duch@nginx.com addr.length = delim - addr.start; 1291324Saxel.duch@nginx.com if (nxt_slow_path(!nxt_valid_ipv6_blocks(addr.start, 1301324Saxel.duch@nginx.com addr.length))) 1311324Saxel.duch@nginx.com { 1321324Saxel.duch@nginx.com return NXT_ADDR_PATTERN_FORMAT_ERROR; 1331324Saxel.duch@nginx.com } 1341324Saxel.duch@nginx.com 1351324Saxel.duch@nginx.com ret = nxt_inet6_addr(&inet6->start, addr.start, addr.length); 1361324Saxel.duch@nginx.com if (nxt_slow_path(ret != NXT_OK)) { 1371324Saxel.duch@nginx.com return NXT_ADDR_PATTERN_FORMAT_ERROR; 1381324Saxel.duch@nginx.com } 1391324Saxel.duch@nginx.com 1401324Saxel.duch@nginx.com if (nxt_slow_path(cidr_prefix == 0)) { 1411324Saxel.duch@nginx.com base->match_type = NXT_HTTP_ROUTE_ADDR_ANY; 1421324Saxel.duch@nginx.com 1431324Saxel.duch@nginx.com goto parse_port; 1441324Saxel.duch@nginx.com } 1451324Saxel.duch@nginx.com 1461324Saxel.duch@nginx.com if (nxt_slow_path(cidr_prefix == 128)) { 1471324Saxel.duch@nginx.com base->match_type = NXT_HTTP_ROUTE_ADDR_EXACT; 1481324Saxel.duch@nginx.com 1491324Saxel.duch@nginx.com goto parse_port; 1501324Saxel.duch@nginx.com } 1511324Saxel.duch@nginx.com 1521324Saxel.duch@nginx.com base->match_type = NXT_HTTP_ROUTE_ADDR_CIDR; 1531324Saxel.duch@nginx.com 1541324Saxel.duch@nginx.com for (i = 0; i < sizeof(struct in6_addr); i++) { 1551324Saxel.duch@nginx.com if (cidr_prefix >= 8) { 1561324Saxel.duch@nginx.com inet6->end.s6_addr[i] = 0xFF; 1571324Saxel.duch@nginx.com cidr_prefix -= 8; 1581324Saxel.duch@nginx.com 1591324Saxel.duch@nginx.com continue; 1601324Saxel.duch@nginx.com } 1611324Saxel.duch@nginx.com 1621324Saxel.duch@nginx.com if (cidr_prefix > 0) { 1631324Saxel.duch@nginx.com inet6->end.s6_addr[i] = 0xFF & (0xFF << (8 - cidr_prefix)); 1641324Saxel.duch@nginx.com inet6->start.s6_addr[i] &= inet6->end.s6_addr[i]; 1651324Saxel.duch@nginx.com cidr_prefix = 0; 1661324Saxel.duch@nginx.com 1671324Saxel.duch@nginx.com continue; 1681324Saxel.duch@nginx.com } 1691324Saxel.duch@nginx.com 1701324Saxel.duch@nginx.com inet6->start.s6_addr[i] = 0; 1711324Saxel.duch@nginx.com inet6->end.s6_addr[i] = 0; 1721324Saxel.duch@nginx.com } 1731324Saxel.duch@nginx.com 1741324Saxel.duch@nginx.com goto parse_port; 1751324Saxel.duch@nginx.com } 1761324Saxel.duch@nginx.com 1771324Saxel.duch@nginx.com base->match_type = NXT_HTTP_ROUTE_ADDR_EXACT; 1781324Saxel.duch@nginx.com 1791324Saxel.duch@nginx.com if (nxt_slow_path(!nxt_valid_ipv6_blocks(addr.start, addr.length))) { 1801324Saxel.duch@nginx.com return NXT_ADDR_PATTERN_FORMAT_ERROR; 1811324Saxel.duch@nginx.com } 1821324Saxel.duch@nginx.com 183*1343Saxel.duch@nginx.com ret = nxt_inet6_addr(&inet6->start, addr.start, addr.length); 184*1343Saxel.duch@nginx.com if (nxt_slow_path(ret != NXT_OK)) { 185*1343Saxel.duch@nginx.com return NXT_ADDR_PATTERN_FORMAT_ERROR; 186*1343Saxel.duch@nginx.com } 1871324Saxel.duch@nginx.com 1881324Saxel.duch@nginx.com goto parse_port; 1891324Saxel.duch@nginx.com #endif 1901324Saxel.duch@nginx.com return NXT_ADDR_PATTERN_NO_IPv6_ERROR; 1911324Saxel.duch@nginx.com } 1921324Saxel.duch@nginx.com 1931324Saxel.duch@nginx.com base->addr_family = AF_INET; 1941324Saxel.duch@nginx.com 1951324Saxel.duch@nginx.com delim = nxt_memchr(addr.start, ':', addr.length); 1961324Saxel.duch@nginx.com if (delim != NULL) { 1971324Saxel.duch@nginx.com port.start = delim + 1; 1981324Saxel.duch@nginx.com port.length = addr.start + addr.length - port.start; 1991324Saxel.duch@nginx.com addr.length = delim - addr.start; 2001324Saxel.duch@nginx.com } 2011324Saxel.duch@nginx.com 2021324Saxel.duch@nginx.com inet = &pattern->addr.v4; 2031324Saxel.duch@nginx.com 2041324Saxel.duch@nginx.com delim = nxt_memchr(addr.start, '-', addr.length); 2051324Saxel.duch@nginx.com if (delim != NULL) { 2061324Saxel.duch@nginx.com inet->start = nxt_inet_addr(addr.start, delim - addr.start); 2071324Saxel.duch@nginx.com if (nxt_slow_path(inet->start == INADDR_NONE)) { 2081324Saxel.duch@nginx.com return NXT_ADDR_PATTERN_FORMAT_ERROR; 2091324Saxel.duch@nginx.com } 2101324Saxel.duch@nginx.com 2111324Saxel.duch@nginx.com inet->end = nxt_inet_addr(delim + 1, 2121324Saxel.duch@nginx.com addr.start + addr.length - (delim + 1)); 2131324Saxel.duch@nginx.com if (nxt_slow_path(inet->end == INADDR_NONE)) { 2141324Saxel.duch@nginx.com return NXT_ADDR_PATTERN_FORMAT_ERROR; 2151324Saxel.duch@nginx.com } 2161324Saxel.duch@nginx.com 2171324Saxel.duch@nginx.com if (nxt_slow_path(nxt_memcmp(&inet->start, &inet->end, 2181324Saxel.duch@nginx.com sizeof(struct in_addr)) > 0)) 2191324Saxel.duch@nginx.com { 2201324Saxel.duch@nginx.com return NXT_ADDR_PATTERN_RANGE_OVERLAP_ERROR; 2211324Saxel.duch@nginx.com } 2221324Saxel.duch@nginx.com 2231324Saxel.duch@nginx.com base->match_type = NXT_HTTP_ROUTE_ADDR_RANGE; 2241324Saxel.duch@nginx.com 2251324Saxel.duch@nginx.com goto parse_port; 2261324Saxel.duch@nginx.com } 2271324Saxel.duch@nginx.com 2281324Saxel.duch@nginx.com delim = nxt_memchr(addr.start, '/', addr.length); 2291324Saxel.duch@nginx.com if (delim != NULL) { 2301324Saxel.duch@nginx.com cidr_prefix = nxt_int_parse(delim + 1, 2311324Saxel.duch@nginx.com addr.start + addr.length - (delim + 1)); 2321324Saxel.duch@nginx.com if (nxt_slow_path(cidr_prefix < 0 || cidr_prefix > 32)) { 2331324Saxel.duch@nginx.com return NXT_ADDR_PATTERN_CIDR_ERROR; 2341324Saxel.duch@nginx.com } 2351324Saxel.duch@nginx.com 2361324Saxel.duch@nginx.com addr.length = delim - addr.start; 2371324Saxel.duch@nginx.com inet->end = htonl(0xFFFFFFFF & (0xFFFFFFFF << (32 - cidr_prefix))); 2381324Saxel.duch@nginx.com 2391324Saxel.duch@nginx.com inet->start = nxt_inet_addr(addr.start, addr.length) & inet->end; 2401324Saxel.duch@nginx.com if (nxt_slow_path(inet->start == INADDR_NONE)) { 2411324Saxel.duch@nginx.com return NXT_ADDR_PATTERN_FORMAT_ERROR; 2421324Saxel.duch@nginx.com } 2431324Saxel.duch@nginx.com 2441324Saxel.duch@nginx.com if (cidr_prefix == 0) { 2451324Saxel.duch@nginx.com base->match_type = NXT_HTTP_ROUTE_ADDR_ANY; 2461324Saxel.duch@nginx.com 2471324Saxel.duch@nginx.com goto parse_port; 2481324Saxel.duch@nginx.com } 2491324Saxel.duch@nginx.com 2501324Saxel.duch@nginx.com if (cidr_prefix < 32) { 2511324Saxel.duch@nginx.com base->match_type = NXT_HTTP_ROUTE_ADDR_CIDR; 2521324Saxel.duch@nginx.com 2531324Saxel.duch@nginx.com goto parse_port; 2541324Saxel.duch@nginx.com } 2551324Saxel.duch@nginx.com } 2561324Saxel.duch@nginx.com 2571324Saxel.duch@nginx.com inet->start = nxt_inet_addr(addr.start, addr.length); 2581324Saxel.duch@nginx.com if (nxt_slow_path(inet->start == INADDR_NONE)) { 2591324Saxel.duch@nginx.com return NXT_ADDR_PATTERN_FORMAT_ERROR; 2601324Saxel.duch@nginx.com } 2611324Saxel.duch@nginx.com 2621324Saxel.duch@nginx.com base->match_type = NXT_HTTP_ROUTE_ADDR_EXACT; 2631324Saxel.duch@nginx.com 2641324Saxel.duch@nginx.com parse_port: 2651324Saxel.duch@nginx.com 2661324Saxel.duch@nginx.com if (port.length == 0) { 2671324Saxel.duch@nginx.com if (nxt_slow_path(port.start != NULL)) { 2681324Saxel.duch@nginx.com return NXT_ADDR_PATTERN_FORMAT_ERROR; 2691324Saxel.duch@nginx.com } 2701324Saxel.duch@nginx.com 2711324Saxel.duch@nginx.com base->port.start = 0; 2721324Saxel.duch@nginx.com base->port.end = 65535; 2731324Saxel.duch@nginx.com 2741324Saxel.duch@nginx.com return NXT_OK; 2751324Saxel.duch@nginx.com } 2761324Saxel.duch@nginx.com 2771324Saxel.duch@nginx.com delim = nxt_memchr(port.start, '-', port.length - 1); 2781324Saxel.duch@nginx.com if (delim != NULL) { 2791324Saxel.duch@nginx.com ret = nxt_int_parse(port.start, delim - port.start); 2801324Saxel.duch@nginx.com if (nxt_slow_path(ret < 0 || ret > 65535)) { 2811324Saxel.duch@nginx.com return NXT_ADDR_PATTERN_PORT_ERROR; 2821324Saxel.duch@nginx.com } 2831324Saxel.duch@nginx.com 2841324Saxel.duch@nginx.com base->port.start = ret; 2851324Saxel.duch@nginx.com 2861324Saxel.duch@nginx.com ret = nxt_int_parse(delim + 1, port.start + port.length - (delim + 1)); 2871324Saxel.duch@nginx.com if (nxt_slow_path(ret < base->port.start || ret > 65535)) { 2881324Saxel.duch@nginx.com return NXT_ADDR_PATTERN_PORT_ERROR; 2891324Saxel.duch@nginx.com } 2901324Saxel.duch@nginx.com 2911324Saxel.duch@nginx.com base->port.end = ret; 2921324Saxel.duch@nginx.com 2931324Saxel.duch@nginx.com } else { 2941324Saxel.duch@nginx.com ret = nxt_int_parse(port.start, port.length); 2951324Saxel.duch@nginx.com if (nxt_slow_path(ret < 0 || ret > 65535)) { 2961324Saxel.duch@nginx.com return NXT_ADDR_PATTERN_PORT_ERROR; 2971324Saxel.duch@nginx.com } 2981324Saxel.duch@nginx.com 2991324Saxel.duch@nginx.com base->port.start = ret; 3001324Saxel.duch@nginx.com base->port.end = ret; 3011324Saxel.duch@nginx.com } 3021324Saxel.duch@nginx.com 3031324Saxel.duch@nginx.com return NXT_OK; 3041324Saxel.duch@nginx.com } 3051324Saxel.duch@nginx.com 3061324Saxel.duch@nginx.com 3071324Saxel.duch@nginx.com static nxt_bool_t 3081324Saxel.duch@nginx.com nxt_str_looks_like_ipv6(const nxt_str_t *str) 3091324Saxel.duch@nginx.com { 3101324Saxel.duch@nginx.com u_char *colon, *end; 3111324Saxel.duch@nginx.com 3121324Saxel.duch@nginx.com colon = nxt_memchr(str->start, ':', str->length); 3131324Saxel.duch@nginx.com 3141324Saxel.duch@nginx.com if (colon != NULL) { 3151324Saxel.duch@nginx.com end = str->start + str->length; 3161324Saxel.duch@nginx.com colon = nxt_memchr(colon + 1, ':', end - (colon + 1)); 3171324Saxel.duch@nginx.com } 3181324Saxel.duch@nginx.com 3191324Saxel.duch@nginx.com return (colon != NULL); 3201324Saxel.duch@nginx.com } 3211324Saxel.duch@nginx.com 3221324Saxel.duch@nginx.com 3231324Saxel.duch@nginx.com #if (NXT_INET6) 3241324Saxel.duch@nginx.com 3251324Saxel.duch@nginx.com static nxt_bool_t 3261324Saxel.duch@nginx.com nxt_valid_ipv6_blocks(u_char *c, size_t len) 3271324Saxel.duch@nginx.com { 3281324Saxel.duch@nginx.com u_char *end; 3291324Saxel.duch@nginx.com nxt_uint_t colon_gap; 3301324Saxel.duch@nginx.com 3311324Saxel.duch@nginx.com end = c + len; 3321324Saxel.duch@nginx.com colon_gap = 0; 3331324Saxel.duch@nginx.com 3341324Saxel.duch@nginx.com while (c != end) { 3351324Saxel.duch@nginx.com if (*c == ':') { 3361324Saxel.duch@nginx.com colon_gap = 0; 3371324Saxel.duch@nginx.com c++; 3381324Saxel.duch@nginx.com 3391324Saxel.duch@nginx.com continue; 3401324Saxel.duch@nginx.com } 3411324Saxel.duch@nginx.com 3421324Saxel.duch@nginx.com colon_gap++; 3431324Saxel.duch@nginx.com c++; 3441324Saxel.duch@nginx.com 3451324Saxel.duch@nginx.com if (nxt_slow_path(colon_gap > 4)) { 3461324Saxel.duch@nginx.com return 0; 3471324Saxel.duch@nginx.com } 3481324Saxel.duch@nginx.com } 3491324Saxel.duch@nginx.com 3501324Saxel.duch@nginx.com return 1; 3511324Saxel.duch@nginx.com } 3521324Saxel.duch@nginx.com 3531324Saxel.duch@nginx.com #endif 354