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