summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAxel Duch <axel.duch@nginx.com>2019-12-24 13:58:10 +0000
committerAxel Duch <axel.duch@nginx.com>2019-12-24 13:58:10 +0000
commit8677bf8d41e002310971fddbc5e45c64034afc2f (patch)
tree0560a4f60031459c16ed19980668f86d66175bed
parent26ee4cb6c8a2248f0f7c99d8c622c86a52bf197a (diff)
downloadunit-8677bf8d41e002310971fddbc5e45c64034afc2f.tar.gz
unit-8677bf8d41e002310971fddbc5e45c64034afc2f.tar.bz2
Router: introducing routing on client address.
-rw-r--r--auto/sources1
-rw-r--r--src/nxt_conf_validation.c68
-rw-r--r--src/nxt_http_route.c243
-rw-r--r--src/nxt_http_route_addr.c349
-rw-r--r--src/nxt_http_route_addr.h73
-rw-r--r--src/nxt_sockaddr.c4
6 files changed, 734 insertions, 4 deletions
diff --git a/auto/sources b/auto/sources
index 70d6eb8d..98e4a1f4 100644
--- a/auto/sources
+++ b/auto/sources
@@ -85,6 +85,7 @@ NXT_LIB_SRCS=" \
src/nxt_http_response.c \
src/nxt_http_error.c \
src/nxt_http_route.c \
+ src/nxt_http_route_addr.c \
src/nxt_http_static.c \
src/nxt_http_proxy.c \
src/nxt_application.c \
diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c
index 923e6c8c..cbccbade 100644
--- a/src/nxt_conf_validation.c
+++ b/src/nxt_conf_validation.c
@@ -9,6 +9,8 @@
#include <nxt_cert.h>
#include <nxt_router.h>
#include <nxt_http.h>
+#include <nxt_sockaddr.h>
+#include <nxt_http_route_addr.h>
typedef enum {
@@ -82,6 +84,10 @@ static nxt_int_t nxt_conf_vldt_match_patterns_set_member(
nxt_conf_validation_t *vldt, nxt_str_t *name, nxt_conf_value_t *value);
static nxt_int_t nxt_conf_vldt_match_scheme_pattern(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value, void *data);
+static nxt_int_t nxt_conf_vldt_match_addrs(nxt_conf_validation_t *vldt,
+ nxt_conf_value_t *value, void *data);
+static nxt_int_t nxt_conf_vldt_match_addr(nxt_conf_validation_t *vldt,
+ nxt_conf_value_t *value);
static nxt_int_t nxt_conf_vldt_app_name(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value, void *data);
static nxt_int_t nxt_conf_vldt_app(nxt_conf_validation_t *vldt,
@@ -283,6 +289,11 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_match_members[] = {
&nxt_conf_vldt_match_patterns,
NULL },
+ { nxt_string("source"),
+ NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY,
+ &nxt_conf_vldt_match_addrs,
+ NULL },
+
{ nxt_string("uri"),
NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY,
&nxt_conf_vldt_match_patterns,
@@ -1155,6 +1166,63 @@ nxt_conf_vldt_match_pattern(nxt_conf_validation_t *vldt,
static nxt_int_t
+nxt_conf_vldt_match_addrs(nxt_conf_validation_t *vldt,
+ nxt_conf_value_t *value, void *data)
+{
+ if (nxt_conf_type(value) == NXT_CONF_ARRAY) {
+ return nxt_conf_vldt_array_iterator(vldt, value,
+ &nxt_conf_vldt_match_addr);
+ }
+
+ return nxt_conf_vldt_match_addr(vldt, value);
+}
+
+
+static nxt_int_t
+nxt_conf_vldt_match_addr(nxt_conf_validation_t *vldt,
+ nxt_conf_value_t *value)
+{
+ nxt_http_route_addr_pattern_t pattern;
+
+ switch (nxt_http_route_addr_pattern_parse(vldt->pool, &pattern, value)) {
+
+ case NXT_OK:
+ return NXT_OK;
+
+ case NXT_ADDR_PATTERN_PORT_ERROR:
+ return nxt_conf_vldt_error(vldt, "The \"address\" port an invalid "
+ "port.");
+
+ case NXT_ADDR_PATTERN_CV_TYPE_ERROR:
+ return nxt_conf_vldt_error(vldt, "The \"match\" pattern for "
+ "\"address\" must be a string.");
+
+ case NXT_ADDR_PATTERN_LENGTH_ERROR:
+ return nxt_conf_vldt_error(vldt, "The \"address\" is too short.");
+
+ case NXT_ADDR_PATTERN_FORMAT_ERROR:
+ return nxt_conf_vldt_error(vldt, "The \"address\" format is invalid.");
+
+ case NXT_ADDR_PATTERN_RANGE_OVERLAP_ERROR:
+ return nxt_conf_vldt_error(vldt, "The \"address\" range is "
+ "overlapping.");
+
+ case NXT_ADDR_PATTERN_CIDR_ERROR:
+ return nxt_conf_vldt_error(vldt, "The \"address\" has an invalid CIDR "
+ "prefix.");
+
+ case NXT_ADDR_PATTERN_NO_IPv6_ERROR:
+ return nxt_conf_vldt_error(vldt, "The \"address\" does not support "
+ "IPv6 with your configuration.");
+
+ default:
+ return nxt_conf_vldt_error(vldt, "The \"address\" has an unknown "
+ "format.");
+ }
+}
+
+
+static nxt_int_t
nxt_conf_vldt_match_scheme_pattern(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value, void *data)
{
diff --git a/src/nxt_http_route.c b/src/nxt_http_route.c
index 18b352ea..d14dcc07 100644
--- a/src/nxt_http_route.c
+++ b/src/nxt_http_route.c
@@ -6,6 +6,8 @@
#include <nxt_router.h>
#include <nxt_http.h>
+#include <nxt_sockaddr.h>
+#include <nxt_http_route_addr.h>
typedef enum {
@@ -16,6 +18,7 @@ typedef enum {
NXT_HTTP_ROUTE_ARGUMENT,
NXT_HTTP_ROUTE_COOKIE,
NXT_HTTP_ROUTE_SCHEME,
+ NXT_HTTP_ROUTE_SOURCE,
} nxt_http_route_object_t;
@@ -50,6 +53,7 @@ typedef struct {
nxt_conf_value_t *arguments;
nxt_conf_value_t *cookies;
nxt_conf_value_t *scheme;
+ nxt_conf_value_t *source;
} nxt_http_route_match_conf_t;
@@ -118,9 +122,18 @@ typedef struct {
} nxt_http_route_table_t;
+typedef struct {
+ /* The object must be the first field. */
+ nxt_http_route_object_t object:8;
+ uint32_t items;
+ nxt_http_route_addr_pattern_t addr_pattern[0];
+} nxt_http_route_addr_rule_t;
+
+
typedef union {
nxt_http_route_rule_t *rule;
nxt_http_route_table_t *table;
+ nxt_http_route_addr_rule_t *addr_rule;
} nxt_http_route_test_t;
@@ -170,6 +183,8 @@ static nxt_http_route_ruleset_t *nxt_http_route_ruleset_create(nxt_task_t *task,
static nxt_http_route_rule_t *nxt_http_route_rule_name_create(nxt_task_t *task,
nxt_mp_t *mp, nxt_conf_value_t *rule_cv, nxt_str_t *name,
nxt_bool_t case_sensitive);
+static nxt_http_route_addr_rule_t *nxt_http_route_addr_rule_create(
+ nxt_task_t *task, nxt_mp_t *mp, nxt_conf_value_t *cv);
static nxt_http_route_rule_t *nxt_http_route_rule_create(nxt_task_t *task,
nxt_mp_t *mp, nxt_conf_value_t *cv, nxt_bool_t case_sensitive,
nxt_http_route_pattern_case_t pattern_case);
@@ -196,6 +211,8 @@ static nxt_int_t nxt_http_route_table(nxt_http_request_t *r,
nxt_http_route_table_t *table);
static nxt_int_t nxt_http_route_ruleset(nxt_http_request_t *r,
nxt_http_route_ruleset_t *ruleset);
+static nxt_int_t nxt_http_route_addr_rule(nxt_http_request_t *r,
+ nxt_http_route_addr_rule_t *addr_rule, nxt_sockaddr_t *sockaddr);
static nxt_int_t nxt_http_route_rule(nxt_http_request_t *r,
nxt_http_route_rule_t *rule);
static nxt_int_t nxt_http_route_header(nxt_http_request_t *r,
@@ -329,6 +346,12 @@ static nxt_conf_map_t nxt_http_route_match_conf[] = {
NXT_CONF_MAP_PTR,
offsetof(nxt_http_route_match_conf_t, cookies),
},
+
+ {
+ nxt_string("source"),
+ NXT_CONF_MAP_PTR,
+ offsetof(nxt_http_route_match_conf_t, source),
+ },
};
@@ -381,6 +404,7 @@ nxt_http_route_match_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
nxt_http_route_rule_t *rule;
nxt_http_route_table_t *table;
nxt_http_route_match_t *match;
+ nxt_http_route_addr_rule_t *addr_rule;
nxt_http_route_match_conf_t mtcf;
static nxt_str_t match_path = nxt_string("/match");
@@ -505,6 +529,17 @@ nxt_http_route_match_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
test++;
}
+ if (mtcf.source != NULL) {
+ addr_rule = nxt_http_route_addr_rule_create(task, mp, mtcf.source);
+ if (addr_rule == NULL) {
+ return NULL;
+ }
+
+ addr_rule->object = NXT_HTTP_ROUTE_SOURCE;
+ test->addr_rule = addr_rule;
+ test++;
+ }
+
return match;
}
@@ -770,6 +805,53 @@ nxt_http_route_rule_create(nxt_task_t *task, nxt_mp_t *mp,
}
+static nxt_http_route_addr_rule_t *
+nxt_http_route_addr_rule_create(nxt_task_t *task, nxt_mp_t *mp,
+ nxt_conf_value_t *cv)
+{
+ size_t size;
+ uint32_t i, n;
+ nxt_bool_t array;
+ nxt_conf_value_t *value;
+ nxt_http_route_addr_rule_t *addr_rule;
+ nxt_http_route_addr_pattern_t *pattern;
+
+ array = (nxt_conf_type(cv) == NXT_CONF_ARRAY);
+ n = array ? nxt_conf_array_elements_count(cv) : 1;
+
+ size = sizeof(nxt_http_route_addr_rule_t)
+ + n * sizeof(nxt_http_route_addr_pattern_t);
+
+ addr_rule = nxt_mp_alloc(mp, size);
+ if (nxt_slow_path(addr_rule == NULL)) {
+ return NULL;
+ }
+
+ addr_rule->items = n;
+
+ if (!array) {
+ pattern = &addr_rule->addr_pattern[0];
+
+ if (nxt_http_route_addr_pattern_parse(mp, pattern, cv) != NXT_OK) {
+ return NULL;
+ }
+
+ return addr_rule;
+ }
+
+ for (i = 0; i < n; i++) {
+ pattern = &addr_rule->addr_pattern[i];
+ value = nxt_conf_get_array_element(cv, i);
+
+ if (nxt_http_route_addr_pattern_parse(mp, pattern, value) != NXT_OK) {
+ return NULL;
+ }
+ }
+
+ return addr_rule;
+}
+
+
static int
nxt_http_pattern_compare(const void *one, const void *two)
{
@@ -1141,11 +1223,16 @@ nxt_http_route_match(nxt_http_request_t *r, nxt_http_route_match_t *match)
end = test + match->items;
while (test < end) {
- if (test->rule->object != NXT_HTTP_ROUTE_TABLE) {
- ret = nxt_http_route_rule(r, test->rule);
-
- } else {
+ switch (test->rule->object) {
+ case NXT_HTTP_ROUTE_TABLE:
ret = nxt_http_route_table(r, test->table);
+ break;
+ case NXT_HTTP_ROUTE_SOURCE:
+ ret = nxt_http_route_addr_rule(r, test->addr_rule, r->remote);
+ break;
+ default:
+ ret = nxt_http_route_rule(r, test->rule);
+ break;
}
if (ret <= 0) {
@@ -1256,6 +1343,154 @@ nxt_http_route_rule(nxt_http_request_t *r, nxt_http_route_rule_t *rule)
static nxt_int_t
+nxt_http_route_addr_pattern_match(nxt_http_route_addr_pattern_t *p,
+ nxt_sockaddr_t *sa)
+{
+#if (NXT_INET6)
+ uint32_t i;
+#endif
+ in_port_t in_port;
+ nxt_int_t match;
+ struct sockaddr_in *sin;
+#if (NXT_INET6)
+ struct sockaddr_in6 *sin6;
+#endif
+ nxt_http_route_addr_base_t *base;
+
+ base = &p->base;
+
+ switch (sa->u.sockaddr.sa_family) {
+
+ case AF_INET:
+
+ match = (base->addr_family == AF_INET
+ || base->addr_family == AF_UNSPEC);
+ if (!match) {
+ break;
+ }
+
+ sin = &sa->u.sockaddr_in;
+ in_port = ntohs(sin->sin_port);
+
+ match = (in_port >= base->port.start && in_port <= base->port.end);
+ if (!match) {
+ break;
+ }
+
+ switch (base->match_type) {
+
+ case NXT_HTTP_ROUTE_ADDR_ANY:
+ break;
+
+ case NXT_HTTP_ROUTE_ADDR_EXACT:
+ match = (nxt_memcmp(&sin->sin_addr, &p->addr.v4.start,
+ sizeof(struct in_addr))
+ == 0);
+ break;
+
+ case NXT_HTTP_ROUTE_ADDR_RANGE:
+ match = (nxt_memcmp(&sin->sin_addr, &p->addr.v4.start,
+ sizeof(struct in_addr)) >= 0
+ && nxt_memcmp(&sin->sin_addr, &p->addr.v4.end,
+ sizeof(struct in_addr)) <= 0);
+ break;
+
+ case NXT_HTTP_ROUTE_ADDR_CIDR:
+ match = ((sin->sin_addr.s_addr & p->addr.v4.end)
+ == p->addr.v4.start);
+ break;
+
+ default:
+ nxt_unreachable();
+ }
+
+ break;
+
+#if (NXT_INET6)
+ case AF_INET6:
+
+ match = (base->addr_family == AF_INET6
+ || base->addr_family == AF_UNSPEC);
+ if (!match) {
+ break;
+ }
+
+ sin6 = &sa->u.sockaddr_in6;
+ in_port = ntohs(sin6->sin6_port);
+
+ match = (in_port >= base->port.start && in_port <= base->port.end);
+ if (!match) {
+ break;
+ }
+
+ switch (base->match_type) {
+
+ case NXT_HTTP_ROUTE_ADDR_ANY:
+ break;
+
+ case NXT_HTTP_ROUTE_ADDR_EXACT:
+ match = (nxt_memcmp(&sin6->sin6_addr, &p->addr.v6.start,
+ sizeof(struct in6_addr))
+ == 0);
+ break;
+
+ case NXT_HTTP_ROUTE_ADDR_RANGE:
+ match = (nxt_memcmp(&sin6->sin6_addr, &p->addr.v6.start,
+ sizeof(struct in6_addr)) >= 0
+ && nxt_memcmp(&sin6->sin6_addr, &p->addr.v6.end,
+ sizeof(struct in6_addr)) <= 0);
+ break;
+
+ case NXT_HTTP_ROUTE_ADDR_CIDR:
+ for (i = 0; i < 16; i++) {
+ match = ((sin6->sin6_addr.s6_addr[i]
+ & p->addr.v6.end.s6_addr[i])
+ == p->addr.v6.start.s6_addr[i]);
+
+ if (!match) {
+ break;
+ }
+ }
+
+ break;
+
+ default:
+ nxt_unreachable();
+ }
+
+ break;
+#endif
+
+ default:
+ match = 0;
+ break;
+ }
+
+ return match ^ base->negative;
+}
+
+
+static nxt_int_t
+nxt_http_route_addr_rule(nxt_http_request_t *r,
+ nxt_http_route_addr_rule_t *addr_rule, nxt_sockaddr_t *sa)
+{
+ uint32_t i, n;
+ nxt_http_route_addr_pattern_t *p;
+
+ n = addr_rule->items;
+
+ for (i = 0; i < n; i++) {
+ p = &addr_rule->addr_pattern[i];
+ if (nxt_http_route_addr_pattern_match(p, sa)) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+
+static nxt_int_t
nxt_http_route_header(nxt_http_request_t *r, nxt_http_route_rule_t *rule)
{
nxt_int_t ret;
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
diff --git a/src/nxt_http_route_addr.h b/src/nxt_http_route_addr.h
new file mode 100644
index 00000000..3b1e1da3
--- /dev/null
+++ b/src/nxt_http_route_addr.h
@@ -0,0 +1,73 @@
+
+/*
+ * Copyright (C) Axel Duch
+ * Copyright (C) NGINX, Inc.
+ */
+
+#include <nxt_conf.h>
+
+#ifndef _NXT_HTTP_ROUTE_ADDR_H_INCLUDED_
+#define _NXT_HTTP_ROUTE_ADDR_H_INCLUDED_
+
+
+enum {
+ NXT_HTTP_ROUTE_ADDR_ANY = 0,
+ NXT_HTTP_ROUTE_ADDR_RANGE,
+ NXT_HTTP_ROUTE_ADDR_EXACT,
+ NXT_HTTP_ROUTE_ADDR_CIDR,
+};
+
+
+enum {
+ NXT_ADDR_PATTERN_PORT_ERROR = NXT_OK + 1,
+ NXT_ADDR_PATTERN_CV_TYPE_ERROR,
+ NXT_ADDR_PATTERN_LENGTH_ERROR,
+ NXT_ADDR_PATTERN_FORMAT_ERROR,
+ NXT_ADDR_PATTERN_RANGE_OVERLAP_ERROR,
+ NXT_ADDR_PATTERN_CIDR_ERROR,
+ NXT_ADDR_PATTERN_NO_IPv6_ERROR,
+};
+
+
+typedef struct {
+ in_addr_t start;
+ in_addr_t end;
+} nxt_http_route_addr_range_t;
+
+
+#if (NXT_INET6)
+typedef struct {
+ struct in6_addr start;
+ struct in6_addr end;
+} nxt_http_route_in6_addr_range_t;
+#endif
+
+
+typedef struct {
+ uint8_t match_type:2;
+ uint8_t negative:1;
+ uint8_t addr_family;
+
+ struct {
+ uint16_t start;
+ uint16_t end;
+ } port;
+} nxt_http_route_addr_base_t;
+
+
+typedef struct {
+ nxt_http_route_addr_base_t base;
+
+ union {
+ nxt_http_route_addr_range_t v4;
+#if (NXT_INET6)
+ nxt_http_route_in6_addr_range_t v6;
+#endif
+ } addr;
+} nxt_http_route_addr_pattern_t;
+
+
+NXT_EXPORT 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);
+
+#endif /* _NXT_HTTP_ROUTE_ADDR_H_INCLUDED_ */
diff --git a/src/nxt_sockaddr.c b/src/nxt_sockaddr.c
index 57dfbfa6..af696a6b 100644
--- a/src/nxt_sockaddr.c
+++ b/src/nxt_sockaddr.c
@@ -1140,6 +1140,10 @@ nxt_inet_addr(u_char *buf, size_t length)
in_addr_t addr;
nxt_uint_t digit, octet, dots;
+ if (nxt_slow_path(*(buf + length - 1) == '.')) {
+ return INADDR_NONE;
+ }
+
addr = 0;
octet = 0;
dots = 0;