summaryrefslogtreecommitdiffhomepage
path: root/src/nxt_http_route_addr.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nxt_http_route_addr.c')
-rw-r--r--src/nxt_http_route_addr.c349
1 files changed, 349 insertions, 0 deletions
diff --git a/src/nxt_http_route_addr.c b/src/nxt_http_route_addr.c
new file mode 100644
index 00000000..3c7e9c84
--- /dev/null
+++ b/src/nxt_http_route_addr.c
@@ -0,0 +1,349 @@
+
+/*
+ * Copyright (C) Axel Duch
+ * Copyright (C) NGINX, Inc.
+ */
+
+#include <nxt_main.h>
+#include <nxt_http_route_addr.h>
+
+
+static nxt_bool_t nxt_str_looks_like_ipv6(const nxt_str_t *str);
+#if (NXT_INET6)
+static nxt_bool_t nxt_valid_ipv6_blocks(u_char *c, size_t len);
+#endif
+
+
+nxt_int_t
+nxt_http_route_addr_pattern_parse(nxt_mp_t *mp,
+ nxt_http_route_addr_pattern_t *pattern, nxt_conf_value_t *cv)
+{
+ u_char *delim, *end;
+ nxt_int_t ret, cidr_prefix;
+ nxt_str_t addr, port;
+ nxt_http_route_addr_base_t *base;
+ nxt_http_route_addr_range_t *inet;
+
+ if (nxt_conf_type(cv) != NXT_CONF_STRING) {
+ return NXT_ADDR_PATTERN_CV_TYPE_ERROR;
+ }
+
+ nxt_conf_get_string(cv, &addr);
+
+ base = &pattern->base;
+
+ if (addr.length > 0 && addr.start[0] == '!') {
+ addr.start++;
+ addr.length--;
+
+ base->negative = 1;
+
+ } else {
+ base->negative = 0;
+ }
+
+ if (nxt_slow_path(addr.length < 2)) {
+ return NXT_ADDR_PATTERN_LENGTH_ERROR;
+ }
+
+ nxt_str_null(&port);
+
+ if (addr.start[0] == '*' && addr.start[1] == ':') {
+ port.start = addr.start + 2;
+ port.length = addr.length - 2;
+ base->addr_family = AF_UNSPEC;
+ base->match_type = NXT_HTTP_ROUTE_ADDR_ANY;
+
+ goto parse_port;
+ }
+
+ if (nxt_str_looks_like_ipv6(&addr)) {
+#if (NXT_INET6)
+ uint8_t i;
+ nxt_int_t len;
+ nxt_http_route_in6_addr_range_t *inet6;
+
+ base->addr_family = AF_INET6;
+
+ if (addr.start[0] == '[') {
+ addr.start++;
+ addr.length--;
+
+ end = addr.start + addr.length;
+
+ port.start = nxt_rmemstrn(addr.start, end, "]:", 2);
+ if (nxt_slow_path(port.start == NULL)) {
+ return NXT_ADDR_PATTERN_FORMAT_ERROR;
+ }
+
+ addr.length = port.start - addr.start;
+ port.start += nxt_length("]:");
+ port.length = end - port.start;
+ }
+
+ inet6 = &pattern->addr.v6;
+
+ delim = nxt_memchr(addr.start, '-', addr.length);
+ if (delim != NULL) {
+ len = delim - addr.start;
+ if (nxt_slow_path(!nxt_valid_ipv6_blocks(addr.start, len))) {
+ return NXT_ADDR_PATTERN_FORMAT_ERROR;
+ }
+
+ ret = nxt_inet6_addr(&inet6->start, addr.start, len);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return NXT_ADDR_PATTERN_FORMAT_ERROR;
+ }
+
+ len = addr.start + addr.length - delim - 1;
+ if (nxt_slow_path(!nxt_valid_ipv6_blocks(delim + 1, len))) {
+ return NXT_ADDR_PATTERN_FORMAT_ERROR;
+ }
+
+ ret = nxt_inet6_addr(&inet6->end, delim + 1, len);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return NXT_ADDR_PATTERN_FORMAT_ERROR;
+ }
+
+ if (nxt_slow_path(nxt_memcmp(&inet6->start, &inet6->end,
+ sizeof(struct in6_addr)) > 0))
+ {
+ return NXT_ADDR_PATTERN_RANGE_OVERLAP_ERROR;
+ }
+
+ base->match_type = NXT_HTTP_ROUTE_ADDR_RANGE;
+
+ goto parse_port;
+ }
+
+ delim = nxt_memchr(addr.start, '/', addr.length);
+ if (delim != NULL) {
+ cidr_prefix = nxt_int_parse(delim + 1,
+ addr.start + addr.length - (delim + 1));
+ if (nxt_slow_path(cidr_prefix < 0 || cidr_prefix > 128)) {
+ return NXT_ADDR_PATTERN_CIDR_ERROR;
+ }
+
+ addr.length = delim - addr.start;
+ if (nxt_slow_path(!nxt_valid_ipv6_blocks(addr.start,
+ addr.length)))
+ {
+ return NXT_ADDR_PATTERN_FORMAT_ERROR;
+ }
+
+ ret = nxt_inet6_addr(&inet6->start, addr.start, addr.length);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return NXT_ADDR_PATTERN_FORMAT_ERROR;
+ }
+
+ if (nxt_slow_path(cidr_prefix == 0)) {
+ base->match_type = NXT_HTTP_ROUTE_ADDR_ANY;
+
+ goto parse_port;
+ }
+
+ if (nxt_slow_path(cidr_prefix == 128)) {
+ base->match_type = NXT_HTTP_ROUTE_ADDR_EXACT;
+
+ goto parse_port;
+ }
+
+ base->match_type = NXT_HTTP_ROUTE_ADDR_CIDR;
+
+ for (i = 0; i < sizeof(struct in6_addr); i++) {
+ if (cidr_prefix >= 8) {
+ inet6->end.s6_addr[i] = 0xFF;
+ cidr_prefix -= 8;
+
+ continue;
+ }
+
+ if (cidr_prefix > 0) {
+ inet6->end.s6_addr[i] = 0xFF & (0xFF << (8 - cidr_prefix));
+ inet6->start.s6_addr[i] &= inet6->end.s6_addr[i];
+ cidr_prefix = 0;
+
+ continue;
+ }
+
+ inet6->start.s6_addr[i] = 0;
+ inet6->end.s6_addr[i] = 0;
+ }
+
+ goto parse_port;
+ }
+
+ base->match_type = NXT_HTTP_ROUTE_ADDR_EXACT;
+
+ if (nxt_slow_path(!nxt_valid_ipv6_blocks(addr.start, addr.length))) {
+ return NXT_ADDR_PATTERN_FORMAT_ERROR;
+ }
+
+ nxt_inet6_addr(&inet6->start, addr.start, addr.length);
+
+ goto parse_port;
+#endif
+ return NXT_ADDR_PATTERN_NO_IPv6_ERROR;
+ }
+
+ base->addr_family = AF_INET;
+
+ delim = nxt_memchr(addr.start, ':', addr.length);
+ if (delim != NULL) {
+ port.start = delim + 1;
+ port.length = addr.start + addr.length - port.start;
+ addr.length = delim - addr.start;
+ }
+
+ inet = &pattern->addr.v4;
+
+ delim = nxt_memchr(addr.start, '-', addr.length);
+ if (delim != NULL) {
+ inet->start = nxt_inet_addr(addr.start, delim - addr.start);
+ if (nxt_slow_path(inet->start == INADDR_NONE)) {
+ return NXT_ADDR_PATTERN_FORMAT_ERROR;
+ }
+
+ inet->end = nxt_inet_addr(delim + 1,
+ addr.start + addr.length - (delim + 1));
+ if (nxt_slow_path(inet->end == INADDR_NONE)) {
+ return NXT_ADDR_PATTERN_FORMAT_ERROR;
+ }
+
+ if (nxt_slow_path(nxt_memcmp(&inet->start, &inet->end,
+ sizeof(struct in_addr)) > 0))
+ {
+ return NXT_ADDR_PATTERN_RANGE_OVERLAP_ERROR;
+ }
+
+ base->match_type = NXT_HTTP_ROUTE_ADDR_RANGE;
+
+ goto parse_port;
+ }
+
+ delim = nxt_memchr(addr.start, '/', addr.length);
+ if (delim != NULL) {
+ cidr_prefix = nxt_int_parse(delim + 1,
+ addr.start + addr.length - (delim + 1));
+ if (nxt_slow_path(cidr_prefix < 0 || cidr_prefix > 32)) {
+ return NXT_ADDR_PATTERN_CIDR_ERROR;
+ }
+
+ addr.length = delim - addr.start;
+ inet->end = htonl(0xFFFFFFFF & (0xFFFFFFFF << (32 - cidr_prefix)));
+
+ inet->start = nxt_inet_addr(addr.start, addr.length) & inet->end;
+ if (nxt_slow_path(inet->start == INADDR_NONE)) {
+ return NXT_ADDR_PATTERN_FORMAT_ERROR;
+ }
+
+ if (cidr_prefix == 0) {
+ base->match_type = NXT_HTTP_ROUTE_ADDR_ANY;
+
+ goto parse_port;
+ }
+
+ if (cidr_prefix < 32) {
+ base->match_type = NXT_HTTP_ROUTE_ADDR_CIDR;
+
+ goto parse_port;
+ }
+ }
+
+ inet->start = nxt_inet_addr(addr.start, addr.length);
+ if (nxt_slow_path(inet->start == INADDR_NONE)) {
+ return NXT_ADDR_PATTERN_FORMAT_ERROR;
+ }
+
+ base->match_type = NXT_HTTP_ROUTE_ADDR_EXACT;
+
+parse_port:
+
+ if (port.length == 0) {
+ if (nxt_slow_path(port.start != NULL)) {
+ return NXT_ADDR_PATTERN_FORMAT_ERROR;
+ }
+
+ base->port.start = 0;
+ base->port.end = 65535;
+
+ return NXT_OK;
+ }
+
+ delim = nxt_memchr(port.start, '-', port.length - 1);
+ if (delim != NULL) {
+ ret = nxt_int_parse(port.start, delim - port.start);
+ if (nxt_slow_path(ret < 0 || ret > 65535)) {
+ return NXT_ADDR_PATTERN_PORT_ERROR;
+ }
+
+ base->port.start = ret;
+
+ ret = nxt_int_parse(delim + 1, port.start + port.length - (delim + 1));
+ if (nxt_slow_path(ret < base->port.start || ret > 65535)) {
+ return NXT_ADDR_PATTERN_PORT_ERROR;
+ }
+
+ base->port.end = ret;
+
+ } else {
+ ret = nxt_int_parse(port.start, port.length);
+ if (nxt_slow_path(ret < 0 || ret > 65535)) {
+ return NXT_ADDR_PATTERN_PORT_ERROR;
+ }
+
+ base->port.start = ret;
+ base->port.end = ret;
+ }
+
+ return NXT_OK;
+}
+
+
+static nxt_bool_t
+nxt_str_looks_like_ipv6(const nxt_str_t *str)
+{
+ u_char *colon, *end;
+
+ colon = nxt_memchr(str->start, ':', str->length);
+
+ if (colon != NULL) {
+ end = str->start + str->length;
+ colon = nxt_memchr(colon + 1, ':', end - (colon + 1));
+ }
+
+ return (colon != NULL);
+}
+
+
+#if (NXT_INET6)
+
+static nxt_bool_t
+nxt_valid_ipv6_blocks(u_char *c, size_t len)
+{
+ u_char *end;
+ nxt_uint_t colon_gap;
+
+ end = c + len;
+ colon_gap = 0;
+
+ while (c != end) {
+ if (*c == ':') {
+ colon_gap = 0;
+ c++;
+
+ continue;
+ }
+
+ colon_gap++;
+ c++;
+
+ if (nxt_slow_path(colon_gap > 4)) {
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+#endif