From 4521797847eeaaef035a94d58e03f1a8111dc96f Mon Sep 17 00:00:00 2001 From: Leonid Evdokimov Date: Tue, 5 Apr 2016 02:42:34 +0300 Subject: [PATCH] Add `disclose_src` option to tell client src IP to http-connect proxy --- http-connect.c | 66 ++++++++++++++++++++++++++++++------------- parser.c | 22 +++++++++++++++ parser.h | 8 ++++++ redsocks.c | 37 +++++++++++++++--------- redsocks.conf.example | 8 ++++++ redsocks.h | 2 ++ 6 files changed, 110 insertions(+), 33 deletions(-) diff --git a/http-connect.c b/http-connect.c index 7dde4a7..044f9d2 100644 --- a/http-connect.c +++ b/http-connect.c @@ -182,6 +182,7 @@ static void httpc_read_cb(struct bufferevent *buffev, void *_arg) static struct evbuffer *httpc_mkconnect(redsocks_client *client) { struct evbuffer *buff = NULL, *retval = NULL; + char *auth_string = NULL; int len; buff = evbuffer_new(); @@ -194,7 +195,6 @@ static struct evbuffer *httpc_mkconnect(redsocks_client *client) ++auth->last_auth_count; const char *auth_scheme = NULL; - char *auth_string = NULL; if (auth->last_auth_query != NULL) { /* find previous auth challange */ @@ -218,34 +218,60 @@ static struct evbuffer *httpc_mkconnect(redsocks_client *client) } } - if (auth_string == NULL) { - len = evbuffer_add_printf(buff, - "CONNECT %s:%u HTTP/1.0\r\n\r\n", - inet_ntoa(client->destaddr.sin_addr), - ntohs(client->destaddr.sin_port) - ); - } else { - len = evbuffer_add_printf(buff, - "CONNECT %s:%u HTTP/1.0\r\n%s %s %s\r\n\r\n", - inet_ntoa(client->destaddr.sin_addr), - ntohs(client->destaddr.sin_port), - auth_response_header, - auth_scheme, - auth_string - ); - } - - free(auth_string); - + // TODO: do accurate evbuffer_expand() while cleaning up http-auth + len = evbuffer_add_printf(buff, "CONNECT %s:%u HTTP/1.0\r\n", + inet_ntoa(client->destaddr.sin_addr), + ntohs(client->destaddr.sin_port)); if (len < 0) { redsocks_log_errno(client, LOG_ERR, "evbufer_add_printf"); goto fail; } + if (auth_string) { + len = evbuffer_add_printf(buff, "%s %s %s\r\n", + auth_response_header, auth_scheme, auth_string); + if (len < 0) { + redsocks_log_errno(client, LOG_ERR, "evbufer_add_printf"); + goto fail; + } + free(auth_string); + auth_string = NULL; + } + + const enum disclose_src_e disclose_src = client->instance->config.disclose_src; + if (disclose_src != DISCLOSE_NONE) { + char clientip[INET_ADDRSTRLEN]; + const char *ip = inet_ntop(client->clientaddr.sin_family, &client->clientaddr.sin_addr, clientip, sizeof(clientip)); + if (!ip) { + redsocks_log_errno(client, LOG_ERR, "inet_ntop"); + goto fail; + } + if (disclose_src == DISCLOSE_X_FORWARDED_FOR) { + len = evbuffer_add_printf(buff, "X-Forwarded-For: %s\r\n", ip); + } else if (disclose_src == DISCLOSE_FORWARDED_IP) { + len = evbuffer_add_printf(buff, "Forwarded: for=%s\r\n", ip); + } else if (disclose_src == DISCLOSE_FORWARDED_IPPORT) { + len = evbuffer_add_printf(buff, "Forwarded: for=\"%s:%d\"\r\n", ip, + ntohs(client->clientaddr.sin_port)); + } + if (len < 0) { + redsocks_log_errno(client, LOG_ERR, "evbufer_add_printf"); + goto fail; + } + } + + len = evbuffer_add(buff, "\r\n", 2); + if (len < 0) { + redsocks_log_errno(client, LOG_ERR, "evbufer_add"); + goto fail; + } + retval = buff; buff = NULL; fail: + if (auth_string) + free(auth_string); if (buff) evbuffer_free(buff); return retval; diff --git a/parser.c b/parser.c index eb8c45e..5443aa7 100644 --- a/parser.c +++ b/parser.c @@ -270,6 +270,27 @@ static int vp_pbool(parser_context *context, void *addr, const char *token) return -1; } +static int vp_disclose_src(parser_context *context, void *addr, const char *token) +{ + enum disclose_src_e *dst = addr; + struct { char *name; enum disclose_src_e value; } opt[] = { + { "off", DISCLOSE_NONE }, + { "no", DISCLOSE_NONE }, + { "false", DISCLOSE_NONE }, + { "X-Forwarded-For", DISCLOSE_X_FORWARDED_FOR }, + { "Forwarded_ip", DISCLOSE_FORWARDED_IP }, + { "Forwarded_ipport", DISCLOSE_FORWARDED_IPPORT }, + }; + for (int i = 0; i < SIZEOF_ARRAY(opt); ++i) { + if (strcmp(token, opt[i].name) == 0) { + *dst = opt[i].value; + return 0; + } + } + parser_error(context, "disclose_src <%s> is not parsed", token); + return -1; +} + static int vp_pchar(parser_context *context, void *addr, const char *token) { char *p = strdup(token); @@ -399,6 +420,7 @@ static value_parser value_parser_by_type[] = [pt_uint16] = vp_uint16, [pt_in_addr] = vp_in_addr, [pt_in_addr2] = vp_in_addr2, + [pt_disclose_src] = vp_disclose_src, }; int parser_run(parser_context *context) diff --git a/parser.h b/parser.h index 085681c..5c83cdb 100644 --- a/parser.h +++ b/parser.h @@ -4,12 +4,20 @@ #include #include +enum disclose_src_e { + DISCLOSE_NONE, + DISCLOSE_X_FORWARDED_FOR, + DISCLOSE_FORWARDED_IP, + DISCLOSE_FORWARDED_IPPORT, +}; + typedef enum { pt_bool, // "bool" from stdbool.h, not "_Bool" or anything else pt_pchar, pt_uint16, pt_in_addr, pt_in_addr2, // inaddr[0] = net, inaddr[1] = netmask + pt_disclose_src, } parser_type; typedef struct parser_entry_t { diff --git a/redsocks.c b/redsocks.c index 3707b31..490a732 100644 --- a/redsocks.c +++ b/redsocks.c @@ -77,6 +77,7 @@ static parser_entry redsocks_entries[] = { .key = "password", .type = pt_pchar }, { .key = "listenq", .type = pt_uint16 }, { .key = "splice", .type = pt_bool }, + { .key = "disclose_src", .type = pt_disclose_src }, { .key = "min_accept_backoff", .type = pt_uint16 }, { .key = "max_accept_backoff", .type = pt_uint16 }, { } @@ -158,6 +159,7 @@ static int redsocks_onenter(parser_section *section) instance->config.min_backoff_ms = 100; instance->config.max_backoff_ms = 60000; instance->config.use_splice = is_splice_good(); + instance->config.disclose_src = DISCLOSE_NONE; for (parser_entry *entry = §ion->entries[0]; entry->key; entry++) entry->addr = @@ -170,6 +172,7 @@ static int redsocks_onenter(parser_section *section) (strcmp(entry->key, "password") == 0) ? (void*)&instance->config.password : (strcmp(entry->key, "listenq") == 0) ? (void*)&instance->config.listenq : (strcmp(entry->key, "splice") == 0) ? (void*)&instance->config.use_splice : + (strcmp(entry->key, "disclose_src") == 0) ? (void*)&instance->config.disclose_src : (strcmp(entry->key, "min_accept_backoff") == 0) ? (void*)&instance->config.min_backoff_ms : (strcmp(entry->key, "max_accept_backoff") == 0) ? (void*)&instance->config.max_backoff_ms : NULL; @@ -183,7 +186,6 @@ static int redsocks_onexit(parser_section *section) * file is not correct, so correct on-the-fly config reloading is * currently impossible. */ - const char *err = NULL; redsocks_instance *instance = section->data; section->data = NULL; @@ -202,29 +204,38 @@ static int redsocks_onexit(parser_section *section) break; } } - if (!instance->relay_ss) - err = "invalid `type` for redsocks"; + if (!instance->relay_ss) { + parser_error(section->context, "invalid `type` <%s> for redsocks", instance->config.type); + return -1; + } } else { - err = "no `type` for redsocks"; + parser_error(section->context, "no `type` for redsocks"); + return -1; } - if (!err && !instance->config.min_backoff_ms) { - err = "`min_accept_backoff` must be positive, 0 ms is too low"; + if (instance->config.disclose_src != DISCLOSE_NONE && instance->relay_ss != &http_connect_subsys) { + parser_error(section->context, "only `http-connect` supports `disclose_src` at the moment"); + return -1; } - if (!err && !instance->config.max_backoff_ms) { - err = "`max_accept_backoff` must be positive, 0 ms is too low"; + if (!instance->config.min_backoff_ms) { + parser_error(section->context, "`min_accept_backoff` must be positive, 0 ms is too low"); + return -1; } - if (!err && !(instance->config.min_backoff_ms < instance->config.max_backoff_ms)) { - err = "`min_accept_backoff` must be less than `max_accept_backoff`"; + if (!instance->config.max_backoff_ms) { + parser_error(section->context, "`max_accept_backoff` must be positive, 0 ms is too low"); + return -1; } - if (err) - parser_error(section->context, "%s", err); + if (instance->config.min_backoff_ms >= instance->config.max_backoff_ms) { + parser_error(section->context, "`min_accept_backoff` (%d) must be less than `max_accept_backoff` (%d)", + instance->config.min_backoff_ms, instance->config.max_backoff_ms); + return -1; + } - return err ? -1 : 0; + return 0; } static parser_section redsocks_conf_section = diff --git a/redsocks.conf.example b/redsocks.conf.example index b9ffd94..00dab19 100644 --- a/redsocks.conf.example +++ b/redsocks.conf.example @@ -81,6 +81,14 @@ redsocks { // login = "foobar"; // password = "baz"; + + // known ways to disclose client IP to the proxy: + // false -- disclose nothing + // http-connect supports: + // X-Forwarded-For -- X-Forwarded-For: IP + // Forwarded_ip -- Forwarded: for=IP # see RFC7239 + // Forwarded_ipport -- Forwarded: for="IP:port" # see RFC7239 + // disclose_src = false; } redudp { diff --git a/redsocks.h b/redsocks.h index 3f9d6a8..0362316 100644 --- a/redsocks.h +++ b/redsocks.h @@ -6,6 +6,7 @@ #include #include #include "list.h" +#include "parser.h" struct redsocks_client_t; @@ -35,6 +36,7 @@ typedef struct redsocks_config_t { uint16_t max_backoff_ms; // backoff capped by 65 seconds is enough :) uint16_t listenq; bool use_splice; + enum disclose_src_e disclose_src; } redsocks_config; struct tracked_event {