summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--docs/changes.xml6
-rw-r--r--src/nxt_conf_validation.c25
-rw-r--r--src/nxt_http.h17
-rw-r--r--src/nxt_http_request.c150
-rw-r--r--src/nxt_http_route.c12
-rw-r--r--src/nxt_router.c86
-rw-r--r--src/nxt_router.h3
7 files changed, 288 insertions, 11 deletions
diff --git a/docs/changes.xml b/docs/changes.xml
index e183f907..56dfa038 100644
--- a/docs/changes.xml
+++ b/docs/changes.xml
@@ -49,6 +49,12 @@ application restart control.
</para>
</change>
+<change type="feature">
+<para>
+client IP address replacement from specified HTTP header field.
+</para>
+</change>
+
<change type="bugfix">
<para>
TLS connection was rejected for configuration with more than one
diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c
index 0ec2e811..0106ebc8 100644
--- a/src/nxt_conf_validation.c
+++ b/src/nxt_conf_validation.c
@@ -208,6 +208,7 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_setting_members[];
static nxt_conf_vldt_object_t nxt_conf_vldt_http_members[];
static nxt_conf_vldt_object_t nxt_conf_vldt_websocket_members[];
static nxt_conf_vldt_object_t nxt_conf_vldt_static_members[];
+static nxt_conf_vldt_object_t nxt_conf_vldt_client_ip_members[];
#if (NXT_TLS)
static nxt_conf_vldt_object_t nxt_conf_vldt_tls_members[];
static nxt_conf_vldt_object_t nxt_conf_vldt_session_members[];
@@ -351,6 +352,11 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_listener_members[] = {
.name = nxt_string("application"),
.type = NXT_CONF_VLDT_STRING,
.validator = nxt_conf_vldt_app_name,
+ }, {
+ .name = nxt_string("client_ip"),
+ .type = NXT_CONF_VLDT_OBJECT,
+ .validator = nxt_conf_vldt_object,
+ .u.members = nxt_conf_vldt_client_ip_members
},
#if (NXT_TLS)
@@ -366,6 +372,25 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_listener_members[] = {
};
+static nxt_conf_vldt_object_t nxt_conf_vldt_client_ip_members[] = {
+ {
+ .name = nxt_string("source"),
+ .type = NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY,
+ .validator = nxt_conf_vldt_match_addrs,
+ .flags = NXT_CONF_VLDT_REQUIRED
+ }, {
+ .name = nxt_string("header"),
+ .type = NXT_CONF_VLDT_STRING,
+ .flags = NXT_CONF_VLDT_REQUIRED
+ }, {
+ .name = nxt_string("recursive"),
+ .type = NXT_CONF_VLDT_BOOLEAN,
+ },
+
+ NXT_CONF_VLDT_END
+};
+
+
#if (NXT_TLS)
static nxt_conf_vldt_object_t nxt_conf_vldt_tls_members[] = {
diff --git a/src/nxt_http.h b/src/nxt_http.h
index faba83f2..3bc2fd61 100644
--- a/src/nxt_http.h
+++ b/src/nxt_http.h
@@ -197,8 +197,9 @@ struct nxt_http_request_s {
};
-typedef struct nxt_http_route_s nxt_http_route_t;
-typedef struct nxt_http_route_rule_s nxt_http_route_rule_t;
+typedef struct nxt_http_route_s nxt_http_route_t;
+typedef struct nxt_http_route_rule_s nxt_http_route_rule_t;
+typedef struct nxt_http_route_addr_rule_s nxt_http_route_addr_rule_t;
typedef struct {
@@ -254,6 +255,14 @@ typedef struct {
} nxt_http_proto_table_t;
+struct nxt_http_client_ip_s {
+ nxt_http_route_addr_rule_t *source;
+ nxt_str_t *header;
+ uint32_t header_hash;
+ uint8_t recursive; /* 1 bit */
+};
+
+
#define NXT_HTTP_DATE_LEN nxt_length("Wed, 31 Dec 1986 16:40:00 GMT")
nxt_inline u_char *
@@ -311,6 +320,10 @@ nxt_int_t nxt_http_pass_segments(nxt_mp_t *mp, nxt_str_t *pass,
nxt_str_t *segments, nxt_uint_t n);
nxt_http_action_t *nxt_http_pass_application(nxt_task_t *task,
nxt_router_conf_t *rtcf, nxt_str_t *name);
+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);
+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);
nxt_http_route_rule_t *nxt_http_route_types_rule_create(nxt_task_t *task,
nxt_mp_t *mp, nxt_conf_value_t *types);
nxt_int_t nxt_http_route_test_rule(nxt_http_request_t *r,
diff --git a/src/nxt_http_request.c b/src/nxt_http_request.c
index 16563a98..b71b25d9 100644
--- a/src/nxt_http_request.c
+++ b/src/nxt_http_request.c
@@ -10,6 +10,10 @@
static nxt_int_t nxt_http_validate_host(nxt_str_t *host, nxt_mp_t *mp);
static void nxt_http_request_start(nxt_task_t *task, void *obj, void *data);
+static nxt_int_t nxt_http_request_client_ip(nxt_task_t *task,
+ nxt_http_request_t *r);
+static nxt_sockaddr_t *nxt_http_request_client_ip_sockaddr(
+ nxt_http_request_t *r, u_char *start, size_t len);
static void nxt_http_request_ready(nxt_task_t *task, void *obj, void *data);
static void nxt_http_request_proto_info(nxt_task_t *task,
nxt_http_request_t *r);
@@ -272,16 +276,162 @@ static const nxt_http_request_state_t nxt_http_request_init_state
static void
nxt_http_request_start(nxt_task_t *task, void *obj, void *data)
{
+ nxt_int_t ret;
nxt_http_request_t *r;
r = obj;
r->state = &nxt_http_request_body_state;
+ ret = nxt_http_request_client_ip(task, r);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ nxt_http_request_error(task, r, NXT_HTTP_INTERNAL_SERVER_ERROR);
+ }
+
nxt_http_request_read_body(task, r);
}
+static nxt_int_t
+nxt_http_request_client_ip(nxt_task_t *task, nxt_http_request_t *r)
+{
+ u_char *start, *p;
+ nxt_int_t ret, i, len;
+ nxt_str_t *header;
+ nxt_array_t *fields_arr; /* of nxt_http_field_t * */
+ nxt_sockaddr_t *sa, *prev_sa;
+ nxt_http_field_t *f, **fields;
+ nxt_http_client_ip_t *client_ip;
+
+ client_ip = r->conf->socket_conf->client_ip;
+
+ if (client_ip == NULL) {
+ return NXT_OK;
+ }
+
+ ret = nxt_http_route_addr_rule(r, client_ip->source, r->remote);
+ if (ret <= 0) {
+ return NXT_OK;
+ }
+
+ header = client_ip->header;
+
+ fields_arr = nxt_array_create(r->mem_pool, 2, sizeof(nxt_http_field_t *));
+ if (nxt_slow_path(fields_arr == NULL)) {
+ return NXT_ERROR;
+ }
+
+ nxt_list_each(f, r->fields) {
+ if (f->hash == client_ip->header_hash
+ && f->name_length == client_ip->header->length
+ && f->value_length > 0
+ && nxt_memcasecmp(f->name, header->start, header->length) == 0)
+ {
+ fields = nxt_array_add(fields_arr);
+ if (nxt_slow_path(fields == NULL)) {
+ return NXT_ERROR;
+ }
+
+ *fields = f;
+ }
+ } nxt_list_loop;
+
+ prev_sa = r->remote;
+ fields = (nxt_http_field_t **) fields_arr->elts;
+
+ i = fields_arr->nelts;
+
+ while (i-- > 0) {
+ f = fields[i];
+ start = f->value;
+ len = f->value_length;
+
+ do {
+ for (p = start + len - 1; p > start; p--, len--) {
+ if (*p != ' ' && *p != ',') {
+ break;
+ }
+ }
+
+ for (/* void */; p > start; p--) {
+ if (*p == ' ' || *p == ',') {
+ p++;
+ break;
+ }
+ }
+
+ sa = nxt_http_request_client_ip_sockaddr(r, p, len - (p - start));
+ if (nxt_slow_path(sa == NULL)) {
+ if (prev_sa != NULL) {
+ r->remote = prev_sa;
+ }
+
+ return NXT_OK;
+ }
+
+ if (!client_ip->recursive) {
+ r->remote = sa;
+
+ return NXT_OK;
+ }
+
+ ret = nxt_http_route_addr_rule(r, client_ip->source, sa);
+ if (ret <= 0 || (i == 0 && p == start)) {
+ r->remote = sa;
+
+ return NXT_OK;
+ }
+
+ prev_sa = sa;
+ len = p - 1 - start;
+
+ } while (len > 0);
+ }
+
+ return NXT_OK;
+}
+
+
+static nxt_sockaddr_t *
+nxt_http_request_client_ip_sockaddr(nxt_http_request_t *r, u_char *start,
+ size_t len)
+{
+ nxt_str_t addr;
+ nxt_sockaddr_t *sa;
+
+ addr.start = start;
+ addr.length = len;
+
+ sa = nxt_sockaddr_parse_optport(r->mem_pool, &addr);
+ if (nxt_slow_path(sa == NULL)) {
+ return NULL;
+ }
+
+ switch (sa->u.sockaddr.sa_family) {
+ case AF_INET:
+ if (sa->u.sockaddr_in.sin_addr.s_addr == INADDR_ANY) {
+ return NULL;
+ }
+
+ break;
+
+#if (NXT_INET6)
+ case AF_INET6:
+ if (IN6_IS_ADDR_UNSPECIFIED(&sa->u.sockaddr_in6.sin6_addr)) {
+ return NULL;
+ }
+
+ break;
+#endif /* NXT_INET6 */
+
+ default:
+ return NULL;
+ }
+
+ return sa;
+}
+
+
static const nxt_http_request_state_t nxt_http_request_body_state
nxt_aligned(64) =
{
diff --git a/src/nxt_http_route.c b/src/nxt_http_route.c
index b330796f..cff69f96 100644
--- a/src/nxt_http_route.c
+++ b/src/nxt_http_route.c
@@ -135,12 +135,12 @@ typedef struct {
} nxt_http_route_table_t;
-typedef struct {
+struct nxt_http_route_addr_rule_s {
/* 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 {
@@ -194,8 +194,6 @@ 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, nxt_http_route_encoding_t encoding);
-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,
@@ -237,8 +235,6 @@ 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,
@@ -940,7 +936,7 @@ 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_t *
nxt_http_route_addr_rule_create(nxt_task_t *task, nxt_mp_t *mp,
nxt_conf_value_t *cv)
{
@@ -1927,7 +1923,7 @@ nxt_http_route_addr_pattern_match(nxt_http_route_addr_pattern_t *p,
}
-static nxt_int_t
+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)
{
diff --git a/src/nxt_router.c b/src/nxt_router.c
index 8360e75a..1aa919c2 100644
--- a/src/nxt_router.c
+++ b/src/nxt_router.c
@@ -107,6 +107,9 @@ static nxt_int_t nxt_router_conf_create(nxt_task_t *task,
nxt_router_temp_conf_t *tmcf, u_char *start, u_char *end);
static nxt_int_t nxt_router_conf_process_static(nxt_task_t *task,
nxt_router_conf_t *rtcf, nxt_conf_value_t *conf);
+static nxt_int_t nxt_router_conf_process_client_ip(nxt_task_t *task,
+ nxt_router_temp_conf_t *tmcf, nxt_socket_conf_t *skcf,
+ nxt_conf_value_t *conf);
static nxt_app_t *nxt_router_app_find(nxt_queue_t *queue, nxt_str_t *name);
static nxt_int_t nxt_router_apps_hash_test(nxt_lvlhsh_query_t *lhq, void *data);
@@ -1450,7 +1453,7 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
nxt_conf_value_t *conf, *http, *value, *websocket;
nxt_conf_value_t *applications, *application;
nxt_conf_value_t *listeners, *listener;
- nxt_conf_value_t *routes_conf, *static_conf;
+ nxt_conf_value_t *routes_conf, *static_conf, *client_ip_conf;
nxt_socket_conf_t *skcf;
nxt_http_routes_t *routes;
nxt_event_engine_t *engine;
@@ -1472,6 +1475,7 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
#endif
static nxt_str_t static_path = nxt_string("/settings/http/static");
static nxt_str_t websocket_path = nxt_string("/settings/http/websocket");
+ static nxt_str_t client_ip_path = nxt_string("/client_ip");
conf = nxt_conf_json_parse(tmcf->mem_pool, start, end, NULL);
if (conf == NULL) {
@@ -1843,6 +1847,13 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
t->length = nxt_strlen(t->start);
}
+ client_ip_conf = nxt_conf_get_path(listener, &client_ip_path);
+ ret = nxt_router_conf_process_client_ip(task, tmcf, skcf,
+ client_ip_conf);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return NXT_ERROR;
+ }
+
#if (NXT_TLS)
certificate = nxt_conf_get_path(listener, &certificate_path);
@@ -2085,6 +2096,79 @@ nxt_router_conf_process_static(nxt_task_t *task, nxt_router_conf_t *rtcf,
}
+static nxt_int_t
+nxt_router_conf_process_client_ip(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
+ nxt_socket_conf_t *skcf, nxt_conf_value_t *conf)
+{
+ char c;
+ size_t i;
+ nxt_mp_t *mp;
+ uint32_t hash;
+ nxt_str_t header;
+ nxt_conf_value_t *source_conf, *header_conf, *recursive_conf;
+ nxt_http_client_ip_t *client_ip;
+ nxt_http_route_addr_rule_t *source;
+
+ static nxt_str_t header_path = nxt_string("/header");
+ static nxt_str_t source_path = nxt_string("/source");
+ static nxt_str_t recursive_path = nxt_string("/recursive");
+
+ if (conf == NULL) {
+ skcf->client_ip = NULL;
+
+ return NXT_OK;
+ }
+
+ mp = tmcf->router_conf->mem_pool;
+
+ source_conf = nxt_conf_get_path(conf, &source_path);
+ header_conf = nxt_conf_get_path(conf, &header_path);
+ recursive_conf = nxt_conf_get_path(conf, &recursive_path);
+
+ if (source_conf == NULL || header_conf == NULL) {
+ return NXT_ERROR;
+ }
+
+ client_ip = nxt_mp_zget(mp, sizeof(nxt_http_client_ip_t));
+ if (nxt_slow_path(client_ip == NULL)) {
+ return NXT_ERROR;
+ }
+
+ source = nxt_http_route_addr_rule_create(task, mp, source_conf);
+ if (nxt_slow_path(source == NULL)) {
+ return NXT_ERROR;
+ }
+
+ client_ip->source = source;
+
+ nxt_conf_get_string(header_conf, &header);
+
+ if (recursive_conf != NULL) {
+ client_ip->recursive = nxt_conf_get_boolean(recursive_conf);
+ }
+
+ client_ip->header = nxt_str_dup(mp, NULL, &header);
+ if (nxt_slow_path(client_ip->header == NULL)) {
+ return NXT_ERROR;
+ }
+
+ hash = NXT_HTTP_FIELD_HASH_INIT;
+
+ for (i = 0; i < client_ip->header->length; i++) {
+ c = client_ip->header->start[i];
+ hash = nxt_http_field_hash_char(hash, nxt_lowcase(c));
+ }
+
+ hash = nxt_http_field_hash_end(hash) & 0xFFFF;
+
+ client_ip->header_hash = hash;
+
+ skcf->client_ip = client_ip;
+
+ return NXT_OK;
+}
+
+
static nxt_app_t *
nxt_router_app_find(nxt_queue_t *queue, nxt_str_t *name)
{
diff --git a/src/nxt_router.h b/src/nxt_router.h
index 6611cf45..fc068b53 100644
--- a/src/nxt_router.h
+++ b/src/nxt_router.h
@@ -18,6 +18,7 @@ typedef struct nxt_http_request_s nxt_http_request_t;
typedef struct nxt_http_action_s nxt_http_action_t;
typedef struct nxt_http_routes_s nxt_http_routes_t;
+typedef struct nxt_http_client_ip_s nxt_http_client_ip_t;
typedef struct nxt_upstream_s nxt_upstream_t;
typedef struct nxt_upstreams_s nxt_upstreams_t;
typedef struct nxt_router_access_log_s nxt_router_access_log_t;
@@ -195,6 +196,8 @@ typedef struct {
uint8_t discard_unsafe_fields; /* 1 bit */
+ nxt_http_client_ip_t *client_ip;
+
#if (NXT_TLS)
nxt_tls_conf_t *tls;
#endif