xref: /unit/src/nxt_http_route_addr.c (revision 1343)
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