From 10464f3f303949208bb5e24e26b4302c7b60e00a Mon Sep 17 00:00:00 2001 From: Lorenz Stechauner Date: Thu, 18 Mar 2021 19:56:50 +0100 Subject: [PATCH] Added Reverse Proxy Header Support --- src/client.c | 16 ++++--- src/rev_proxy.c | 113 +++++++++++++++++++++++++++++++++++++++++++++--- src/rev_proxy.h | 9 ++++ 3 files changed, 127 insertions(+), 11 deletions(-) diff --git a/src/client.c b/src/client.c index ef486aa..6f65861 100644 --- a/src/client.c +++ b/src/client.c @@ -354,10 +354,18 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int http_add_header_field(&res.hdr, "Transfer-Encoding", "chunked"); } } - } else if (conf->type != CONFIG_TYPE_LOCAL) { + } else if (conf->type == CONFIG_TYPE_REVERSE_PROXY) { print("Reverse proxy for " BLD_STR "%s:%i" CLR_STR, conf->rev_proxy.hostname, conf->rev_proxy.port); + http_remove_header_field(&res.hdr, "Date", HTTP_REMOVE_ALL); + http_remove_header_field(&res.hdr, "Server", HTTP_REMOVE_ALL); + ret = rev_proxy_init(&req, &res, conf, client, &custom_status, err_msg); use_rev_proxy = ret == 0; + + if (http_get_header_field(&res.hdr, "Date") == NULL) + http_add_header_field(&res.hdr, "Date", http_get_date(buf0, sizeof(buf0))); + if (http_get_header_field(&res.hdr, "Server") == NULL) + http_add_header_field(&res.hdr, "Server", SERVER_STR); } else { print(ERR_STR "Unknown host type: %i" CLR_STR, conf->type); res.status = http_get_status(501); @@ -407,12 +415,8 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int } else if (http_get_header_field(&res.hdr, "Transfer-Encoding") == NULL) { server_keep_alive = 0; } - } else { - http_remove_header_field(&res.hdr, "Server", HTTP_REMOVE_ALL); - http_remove_header_field(&res.hdr, "Date", HTTP_REMOVE_ALL); - http_add_header_field(&res.hdr, "Date", http_get_date(buf0, sizeof(buf0))); - http_add_header_field(&res.hdr, "Server", SERVER_STR); } + char *conn = http_get_header_field(&res.hdr, "Connection"); int close_proxy = conn == NULL || (strcmp(conn, "keep-alive") != 0 && strcmp(conn, "Keep-Alive") != 0); http_remove_header_field(&res.hdr, "Connection", HTTP_REMOVE_ALL); diff --git a/src/rev_proxy.c b/src/rev_proxy.c index f34544f..976a674 100644 --- a/src/rev_proxy.c +++ b/src/rev_proxy.c @@ -11,9 +11,113 @@ sock rev_proxy; char *rev_proxy_host = NULL; struct timeval server_timeout = {.tv_sec = SERVER_TIMEOUT, .tv_usec = 0}; +int rev_proxy_request_header(http_req *req, int enc) { + char buf1[256]; + char buf2[256]; + http_remove_header_field(&req->hdr, "Connection", HTTP_REMOVE_ALL); + http_add_header_field(&req->hdr, "Connection", "keep-alive"); + + char *via = http_get_header_field(&req->hdr, "Via"); + sprintf(buf1, "HTTP/%s %s", req->version, DEFAULT_HOST); + if (via == NULL) { + http_add_header_field(&req->hdr, "Via", buf1); + } else { + sprintf(buf2, "%s, %s", via, buf1); + http_remove_header_field(&req->hdr, "Via", HTTP_REMOVE_ALL); + http_add_header_field(&req->hdr, "Via", buf2); + } + + char *host = http_get_header_field(&req->hdr, "Host"); + char *forwarded = http_get_header_field(&req->hdr, "Forwarded"); + int client_ipv6 = strchr(client_addr_str, ':') != NULL; + int server_ipv6 = strchr(server_addr_str, ':') != NULL; + + sprintf(buf1, "by=%s%s%s;for=%s%s%s;host=%s;proto=%s", + server_ipv6 ? "\"[" : "", server_addr_str, server_ipv6 ? "]\"" : "", + client_ipv6 ? "\"[" : "", client_addr_str, client_ipv6 ? "]\"" : "", + host, enc ? "https" : "http"); + if (forwarded == NULL) { + // TODO escape IPv6 addresses + http_add_header_field(&req->hdr, "Forwarded", buf1); + } else { + sprintf(buf2, "%s, %s", forwarded, buf1); + http_remove_header_field(&req->hdr, "Forwarded", HTTP_REMOVE_ALL); + http_add_header_field(&req->hdr, "Forwarded", buf2); + } + + char *xff = http_get_header_field(&req->hdr, "X-Forwarded-For"); + if (xff == NULL) { + http_add_header_field(&req->hdr, "X-Forwarded-For", client_addr_str); + } else { + sprintf(buf1, "%s, %s", xff, client_addr_str); + http_remove_header_field(&req->hdr, "X-Forwarded-For", HTTP_REMOVE_ALL); + http_add_header_field(&req->hdr, "X-Forwarded-For", buf1); + } + + char *xfh = http_get_header_field(&req->hdr, "X-Forwarded-Host"); + if (xfh == NULL) { + if (forwarded == NULL) { + http_add_header_field(&req->hdr, "X-Forwarded-Host", host); + } else { + char *ptr = strchr(forwarded, ','); + unsigned long len; + if (ptr != NULL) len = ptr - forwarded; + else len = strlen(forwarded); + ptr = strstr(forwarded, "host="); + if ((ptr - forwarded) < len) { + char *end = strchr(ptr, ';'); + if (end == NULL) len -= (ptr - forwarded); + else len = (end - ptr); + len -= 5; + sprintf(buf1, "%.*s", (int) len, ptr + 5); + http_add_header_field(&req->hdr, "X-Forwarded-Host", buf1); + } + } + } + + char *xfp = http_get_header_field(&req->hdr, "X-Forwarded-Proto"); + if (xfp == NULL) { + if (forwarded == NULL) { + http_add_header_field(&req->hdr, "X-Forwarded-Proto", enc ? "https" : "http"); + } else { + char *ptr = strchr(forwarded, ','); + unsigned long len; + if (ptr != NULL) len = ptr - forwarded; + else len = strlen(forwarded); + ptr = strstr(forwarded, "proto="); + if ((ptr - forwarded) < len) { + char *end = strchr(ptr, ';'); + if (end == NULL) len -= (ptr - forwarded); + else len = (end - ptr); + len -= 6; + sprintf(buf1, "%.*s", (int) len, ptr + 6); + http_add_header_field(&req->hdr, "X-Forwarded-Proto", buf1); + } + } + } + + return 0; +} + +int rev_proxy_response_header(http_req *req, http_res *res) { + char buf1[256]; + char buf2[256]; + + char *via = http_get_header_field(&res->hdr, "Via"); + sprintf(buf1, "HTTP/%s %s", req->version, DEFAULT_HOST); + if (via == NULL) { + http_add_header_field(&res->hdr, "Via", buf1); + } else { + sprintf(buf2, "%s, %s", via, buf1); + http_remove_header_field(&res->hdr, "Via", HTTP_REMOVE_ALL); + http_add_header_field(&res->hdr, "Via", buf2); + } + + return 0; +} int rev_proxy_init(http_req *req, http_res *res, host_config *conf, sock *client, http_status *custom_status, - char * err_msg) { + char *err_msg) { char buffer[CHUNK_SIZE]; long ret; int tries = 0; @@ -97,10 +201,7 @@ int rev_proxy_init(http_req *req, http_res *res, host_config *conf, sock *client print(BLUE_STR "Established new connection with " BLD_STR "[%s]:%i" CLR_STR, buffer, conf->rev_proxy.port); rev_proxy: - http_remove_header_field(&req->hdr, "Connection", HTTP_REMOVE_ALL); - http_add_header_field(&req->hdr, "Connection", "keep-alive"); - http_remove_header_field(&req->hdr, "X-Forwarded-For", HTTP_REMOVE_ALL); - http_add_header_field(&req->hdr, "X-Forwarded-For", client_addr_str); + rev_proxy_request_header(req, (int) client->enc); ret = http_send_request(&rev_proxy, req); if (ret < 0) { @@ -222,6 +323,8 @@ int rev_proxy_init(http_req *req, http_res *res, host_config *conf, sock *client } sock_recv(&rev_proxy, buffer, header_len, 0); + rev_proxy_response_header(req, res); + return 0; proxy_err: diff --git a/src/rev_proxy.h b/src/rev_proxy.h index b9b07d4..f218063 100644 --- a/src/rev_proxy.h +++ b/src/rev_proxy.h @@ -8,4 +8,13 @@ #ifndef NECRONDA_SERVER_REV_PROXY_H #define NECRONDA_SERVER_REV_PROXY_H +int rev_proxy_request_header(http_req *req, int enc); + +int rev_proxy_response_header(http_req *req, http_res *res); + +int rev_proxy_init(http_req *req, http_res *res, host_config *conf, sock *client, http_status *custom_status, + char *err_msg); + +int rev_proxy_send(sock *client, int chunked, unsigned long len_to_send); + #endif //NECRONDA_SERVER_REV_PROXY_H