diff options
-rw-r--r-- | docs/changes.xml | 6 | ||||
-rw-r--r-- | src/nxt_conf_validation.c | 25 | ||||
-rw-r--r-- | src/nxt_http.h | 17 | ||||
-rw-r--r-- | src/nxt_http_request.c | 150 | ||||
-rw-r--r-- | src/nxt_http_route.c | 12 | ||||
-rw-r--r-- | src/nxt_router.c | 86 | ||||
-rw-r--r-- | src/nxt_router.h | 3 |
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 |