From b37757fc736ed9345258e9c3e7e0845b4350ab08 Mon Sep 17 00:00:00 2001 From: Lorenz Stechauner Date: Sun, 7 Nov 2021 19:48:09 +0100 Subject: [PATCH] Add reverse proxy error documents --- Makefile | 2 +- src/client.c | 63 +++++++++++++++++++---- src/lib/http.h | 10 ++++ src/lib/http_static.c | 114 +++++++++++++++++++++++++++++++++++++++--- src/lib/rev_proxy.c | 34 +++++++++++-- src/lib/rev_proxy.h | 10 +++- src/necronda-server.h | 4 -- src/necronda.h | 8 +++ 8 files changed, 217 insertions(+), 28 deletions(-) diff --git a/Makefile b/Makefile index ca7cad6..0e771b1 100644 --- a/Makefile +++ b/Makefile @@ -17,7 +17,7 @@ compile: @mkdir -p bin $(CC) src/lib/*.c -o bin/libnecrondaserver.so --shared -fPIC $(CFLAGS) $(LIBS) $(CC) src/necronda-server.c -o bin/necronda-server $(CFLAGS) $(LIBS) \ - -Lbin -lnecronda-server -Wl,-rpath=$(shell pwd)/bin + -Lbin -lnecrondaserver -Wl,-rpath=$(shell pwd)/bin compile-prod: @mkdir -p bin diff --git a/src/client.c b/src/client.c index 831f7fe..67badab 100644 --- a/src/client.c +++ b/src/client.c @@ -55,7 +55,7 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int long ret; int client_keep_alive; char buf0[1024], buf1[1024]; - char msg_buf[4096], msg_pre_buf[4096], err_msg[256]; + char msg_buf[8192], msg_pre_buf_1[4096], msg_pre_buf_2[4096], err_msg[256]; char buffer[CHUNK_SIZE]; err_msg[0] = 0; char host[256], *host_ptr, *hdr_connection; @@ -70,10 +70,8 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int fastcgi_conn fcgi_conn = {.socket = 0, .req_id = 0}; http_status custom_status; - http_res res; - sprintf(res.version, "1.1"); - res.status = http_get_status(501); - res.hdr.field_num = 0; + http_res res = {.version = "1.1", .status = http_get_status(501), .hdr.field_num = 0}; + http_status_ctx ctx = {.status = 0, .origin = NONE}; clock_gettime(CLOCK_MONOTONIC, &begin); @@ -460,9 +458,24 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int 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); + ret = rev_proxy_init(&req, &res, &ctx, conf, client, &custom_status, err_msg); use_rev_proxy = (ret == 0); + if (use_rev_proxy && res.status->code >= 400 && res.status->code < 600) { + const char *content_type = http_get_header_field(&res.hdr, "Content-Type"); + const char *content_length_f = http_get_header_field(&res.hdr, "Content-Length"); + if (content_type != NULL && content_length_f != NULL && strncmp(content_type, "text/html", 9) == 0) { + long content_len = strtol(content_length_f, NULL, 10); + if (content_len <= 1000) { + ctx.status = res.status->code; + ctx.origin = SERVER; + + use_rev_proxy = 0; + rev_proxy_void(); + } + } + } + /* char *content_encoding = http_get_header_field(&res.hdr, "Content-Encoding"); if (use_rev_proxy && content_encoding == NULL) { @@ -499,25 +512,53 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int if (http_get_header_field(&res.hdr, "Accept-Ranges") == NULL) { http_add_header_field(&res.hdr, "Accept-Ranges", "none"); } - if (!use_fastcgi && !use_rev_proxy && file == NULL && - ((res.status->code >= 400 && res.status->code < 600) || err_msg[0] != 0)) { + if (!use_fastcgi && file == NULL && ((res.status->code >= 400 && res.status->code < 600) || err_msg[0] != 0)) { http_remove_header_field(&res.hdr, "Date", HTTP_REMOVE_ALL); http_remove_header_field(&res.hdr, "Server", HTTP_REMOVE_ALL); + http_remove_header_field(&res.hdr, "Cache-Control", HTTP_REMOVE_ALL); + http_remove_header_field(&res.hdr, "Content-Type", HTTP_REMOVE_ALL); + http_remove_header_field(&res.hdr, "Content-Encoding", 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); + http_add_header_field(&res.hdr, "Cache-Control", "no-cache"); + http_add_header_field(&res.hdr, "Content-Type", "text/html; charset=UTF-8"); // TODO list Locations on 3xx Redirects const http_doc_info *info = http_get_status_info(res.status); const http_status_msg *http_msg = http_get_error_msg(res.status); - sprintf(msg_pre_buf, info->doc, res.status->code, res.status->msg, + char *rev_proxy_doc = ""; + if (conf->type == CONFIG_TYPE_REVERSE_PROXY) { + const http_status *status = http_get_status(ctx.status); + char stat_str[4]; + sprintf(stat_str, "%03i", ctx.status); + sprintf(msg_pre_buf_2, http_rev_proxy_document, + " success", + (ctx.origin == CLIENT_REQ) ? " error" : " success", + (ctx.origin == INTERNAL) ? " error" : " success", + (ctx.origin == SERVER_REQ) ? " error" : (ctx.status == 0 ? "" : " success"), + (ctx.origin == CLIENT_RES) ? " error" : " success", + (ctx.origin == SERVER) ? " error" : (ctx.status == 0 ? "" : " success"), + (ctx.origin == SERVER_RES) ? " error" : (ctx.status == 0 ? "" : " success"), + (ctx.origin == INTERNAL) ? " error" : " success", + (ctx.origin == INTERNAL || ctx.origin == SERVER) ? " error" : " success", + res.status->code, + res.status->msg, + (ctx.status == 0) ? "???" : stat_str, + (status != NULL) ? status->msg : "", + host); + rev_proxy_doc = msg_pre_buf_2; + } + + sprintf(msg_pre_buf_1, info->doc, res.status->code, res.status->msg, http_msg != NULL ? http_msg->msg : "", err_msg[0] != 0 ? err_msg : ""); content_length = snprintf(msg_buf, sizeof(msg_buf), http_default_document, res.status->code, - res.status->msg, msg_pre_buf, info->mode, info->icon, info->color, host); - http_add_header_field(&res.hdr, "Content-Type", "text/html; charset=UTF-8"); + res.status->msg, msg_pre_buf_1, info->mode, info->icon, info->color, host, + rev_proxy_doc); } if (content_length >= 0) { sprintf(buf0, "%li", content_length); + http_remove_header_field(&res.hdr, "Content-Length", HTTP_REMOVE_ALL); http_add_header_field(&res.hdr, "Content-Length", buf0); } else if (http_get_header_field(&res.hdr, "Transfer-Encoding") == NULL) { server_keep_alive = 0; diff --git a/src/lib/http.h b/src/lib/http.h index 93da970..002d1e7 100644 --- a/src/lib/http.h +++ b/src/lib/http.h @@ -75,12 +75,22 @@ typedef struct { http_hdr hdr; } http_res; +typedef enum { + NONE, INTERNAL, CLIENT_REQ, SERVER_REQ, SERVER, SERVER_RES, CLIENT_RES +} http_error_origin; + +typedef struct { + unsigned short status; + http_error_origin origin; +} http_status_ctx; + extern const http_status http_statuses[]; extern const http_status_msg http_status_messages[]; extern const int http_statuses_size; extern const int http_status_messages_size; extern const char http_default_document[]; +extern const char http_rev_proxy_document[]; extern const char http_error_document[]; extern const char http_error_icon[]; extern const char http_warning_document[]; diff --git a/src/lib/http_static.c b/src/lib/http_static.c index d63cd1f..bd433f3 100644 --- a/src/lib/http_static.c +++ b/src/lib/http_static.c @@ -116,20 +116,83 @@ const char http_default_document[] = "\t\n" "%5$s" "\t\n" "\n" @@ -139,10 +202,49 @@ const char http_default_document[] = "%3$s" "\t\t\t
%7$s - " SERVER_STR_HTML "
\n" "\t\t\n" + "%8$s" "\t\n" "\n" "\n"; +const char http_rev_proxy_document[] = + "\t\t
\n" + "\t\t\t
\n" + "\t\t\t\t
\n" + "\t\t\t\t\tClient\n" + "\t\t\t\t\t\n" + "\t\t\t\t\tYour Browser\n" + "\t\t\t\t
\n" + "\t\t\t\t
\n" + "\t\t\t
\n" + "\t\t\t
\n" + "\t\t\t
\n" + "\t\t\t\t
\n" + "\t\t\t\t\tReverse Proxy\n" + "\t\t\t\t\t

%10$03i

\n" + "\t\t\t\t\t

%11$s

\n" + "\t\t\t\t\t" SERVER_NAME "\n" + "\t\t\t\t
\n" + "\t\t\t\t
\n" + "\t\t\t\t
\n" + "\t\t\t
\n" + "\t\t\t
\n" + "\t\t\t
\n" + "\t\t\t\t
\n" + "\t\t\t\t\tServer\n" + "\t\t\t\t\t

%12$s

\n" + "\t\t\t\t\t

%13$s

\n" + "\t\t\t\t\t%14$s\n" + "\t\t\t\t
\n" + "\t\t\t\t
\n" + "\t\t\t
\n" + "\t\t
\n"; + const char http_error_document[] = "\t\t\t

%1$i

\n" "\t\t\t

%2$s :(

\n" diff --git a/src/lib/rev_proxy.c b/src/lib/rev_proxy.c index 0366aac..29bdb20 100644 --- a/src/lib/rev_proxy.c +++ b/src/lib/rev_proxy.c @@ -36,7 +36,7 @@ int rev_proxy_request_header(http_req *req, int enc) { 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); + sprintf(buf1, "HTTP/%s %s", req->version, SERVER_NAME); if (via == NULL) { http_add_header_field(&req->hdr, "Via", buf1); } else { @@ -134,7 +134,7 @@ int rev_proxy_response_header(http_req *req, http_res *res) { int p_len; char *via = http_get_header_field(&res->hdr, "Via"); - p_len = snprintf(buf1, sizeof(buf1), "HTTP/%s %s", req->version, DEFAULT_HOST); + p_len = snprintf(buf1, sizeof(buf1), "HTTP/%s %s", req->version, SERVER_NAME); if (p_len < 0 || p_len >= sizeof(buf1)) { print(ERR_STR "Appended part of header field 'Via' too long" CLR_STR); return -1; @@ -154,8 +154,8 @@ int rev_proxy_response_header(http_req *req, http_res *res) { return 0; } -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_init(http_req *req, http_res *res, http_status_ctx *ctx, host_config *conf, sock *client, + http_status *custom_status, char *err_msg) { char buffer[CHUNK_SIZE]; long ret; int tries = 0; @@ -177,6 +177,7 @@ int rev_proxy_init(http_req *req, http_res *res, host_config *conf, sock *client if (rev_proxy.socket < 0) { print(ERR_STR "Unable to create socket: %s" CLR_STR, strerror(errno)); res->status = http_get_status(500); + ctx->origin = INTERNAL; return -1; } @@ -187,6 +188,7 @@ int rev_proxy_init(http_req *req, http_res *res, host_config *conf, sock *client if (setsockopt(rev_proxy.socket, SOL_SOCKET, SO_SNDTIMEO, &server_timeout, sizeof(server_timeout)) < 0) { rev_proxy_timeout_err: res->status = http_get_status(500); + ctx->origin = INTERNAL; print(ERR_STR "Unable to set timeout for socket: %s" CLR_STR, strerror(errno)); sprintf(err_msg, "Unable to set timeout for socket: %s", strerror(errno)); goto proxy_err; @@ -197,6 +199,7 @@ int rev_proxy_init(http_req *req, http_res *res, host_config *conf, sock *client host_ent = gethostbyname2(conf->rev_proxy.hostname, AF_INET); if (host_ent == NULL) { res->status = http_get_status(503); + ctx->origin = SERVER_REQ; print(ERR_STR "Unable to connect to server: Name or service not known" CLR_STR); sprintf(err_msg, "Unable to connect to server: Name or service not known."); goto proxy_err; @@ -218,10 +221,13 @@ int rev_proxy_init(http_req *req, http_res *res, host_config *conf, sock *client if (connect(rev_proxy.socket, (struct sockaddr *) &address, sizeof(address)) < 0) { if (errno == ETIMEDOUT) { res->status = http_get_status(504); + ctx->origin = SERVER_REQ; } else if (errno == ECONNREFUSED) { res->status = http_get_status(503); + ctx->origin = SERVER_REQ; } else { res->status = http_get_status(500); + ctx->origin = INTERNAL; } print(ERR_STR "Unable to connect to [%s]:%i: %s" CLR_STR, buffer, conf->rev_proxy.port, strerror(errno)); sprintf(err_msg, "Unable to connect to server: %s.", strerror(errno)); @@ -240,6 +246,7 @@ int rev_proxy_init(http_req *req, http_res *res, host_config *conf, sock *client rev_proxy.enc = 1; if (ret < 0) { res->status = http_get_status(502); + ctx->origin = SERVER_REQ; print(ERR_STR "Unable to perform handshake: %s" CLR_STR, sock_strerror(&rev_proxy)); sprintf(err_msg, "Unable to perform handshake: %s.", sock_strerror(&rev_proxy)); goto proxy_err; @@ -253,12 +260,14 @@ int rev_proxy_init(http_req *req, http_res *res, host_config *conf, sock *client ret = rev_proxy_request_header(req, (int) client->enc); if (ret != 0) { res->status = http_get_status(500); + ctx->origin = INTERNAL; return -1; } ret = http_send_request(&rev_proxy, req); if (ret < 0) { res->status = http_get_status(502); + ctx->origin = SERVER_REQ; print(ERR_STR "Unable to send request to server (1): %s" CLR_STR, sock_strerror(&rev_proxy)); sprintf(err_msg, "Unable to send request to server: %s.", sock_strerror(&rev_proxy)); retry = tries < 4; @@ -276,6 +285,7 @@ int rev_proxy_init(http_req *req, http_res *res, host_config *conf, sock *client ret = sock_send(&rev_proxy, client->buf, len, 0); if (ret <= 0) { res->status = http_get_status(502); + ctx->origin = SERVER_REQ; print(ERR_STR "Unable to send request to server (2): %s" CLR_STR, sock_strerror(&rev_proxy)); sprintf(err_msg, "Unable to send request to server: %s.", sock_strerror(&rev_proxy)); retry = tries < 4; @@ -288,16 +298,19 @@ int rev_proxy_init(http_req *req, http_res *res, host_config *conf, sock *client if (ret <= 0) { if (ret == -1) { res->status = http_get_status(502); + ctx->origin = SERVER_REQ; print(ERR_STR "Unable to send request to server (3): %s" CLR_STR, sock_strerror(&rev_proxy)); sprintf(err_msg, "Unable to send request to server: %s.", sock_strerror(&rev_proxy)); goto proxy_err; } else if (ret == -2) { res->status = http_get_status(400); + ctx->origin = CLIENT_REQ; print(ERR_STR "Unable to receive request from client: %s" CLR_STR, sock_strerror(client)); sprintf(err_msg, "Unable to receive request from client: %s.", sock_strerror(client)); return -1; } res->status = http_get_status(500); + ctx->origin = INTERNAL; print(ERR_STR "Unknown Error" CLR_STR); return -1; } @@ -307,6 +320,7 @@ int rev_proxy_init(http_req *req, http_res *res, host_config *conf, sock *client ret = sock_recv(&rev_proxy, buffer, sizeof(buffer), MSG_PEEK); if (ret <= 0) { res->status = http_get_status(502); + ctx->origin = SERVER_RES; print(ERR_STR "Unable to receive response from server: %s" CLR_STR, sock_strerror(&rev_proxy)); sprintf(err_msg, "Unable to receive response from server: %s.", sock_strerror(&rev_proxy)); goto proxy_err; @@ -317,6 +331,7 @@ int rev_proxy_init(http_req *req, http_res *res, host_config *conf, sock *client if (header_len <= 0) { res->status = http_get_status(502); + ctx->origin = SERVER_RES; print(ERR_STR "Unable to parse header: End of header not found" CLR_STR); sprintf(err_msg, "Unable to parser header: End of header not found."); goto proxy_err; @@ -325,6 +340,7 @@ int rev_proxy_init(http_req *req, http_res *res, host_config *conf, sock *client for (int i = 0; i < header_len; i++) { if ((buf[i] >= 0x00 && buf[i] <= 0x1F && buf[i] != '\r' && buf[i] != '\n') || buf[i] == 0x7F) { res->status = http_get_status(502); + ctx->origin = SERVER_RES; print(ERR_STR "Unable to parse header: Header contains illegal characters" CLR_STR); sprintf(err_msg, "Unable to parse header: Header contains illegal characters."); goto proxy_err; @@ -336,6 +352,7 @@ int rev_proxy_init(http_req *req, http_res *res, host_config *conf, sock *client char *pos0 = strstr(ptr, "\r\n"); if (pos0 == NULL) { res->status = http_get_status(502); + ctx->origin = SERVER_RES; print(ERR_STR "Unable to parse header: Invalid header format" CLR_STR); sprintf(err_msg, "Unable to parse header: Invalid header format."); goto proxy_err; @@ -343,6 +360,7 @@ int rev_proxy_init(http_req *req, http_res *res, host_config *conf, sock *client if (ptr == buf) { if (strncmp(ptr, "HTTP/", 5) != 0) { res->status = http_get_status(502); + ctx->origin = SERVER_RES; print(ERR_STR "Unable to parse header: Invalid header format" CLR_STR); sprintf(err_msg, "Unable to parse header: Invalid header format."); goto proxy_err; @@ -357,6 +375,7 @@ int rev_proxy_init(http_req *req, http_res *res, host_config *conf, sock *client res->status = custom_status; } else if (res->status == NULL) { res->status = http_get_status(502); + ctx->origin = SERVER_RES; print(ERR_STR "Unable to parse header: Invalid or unknown status code" CLR_STR); sprintf(err_msg, "Unable to parse header: Invalid or unknown status code."); goto proxy_err; @@ -365,6 +384,7 @@ int rev_proxy_init(http_req *req, http_res *res, host_config *conf, sock *client ret = http_parse_header_field(&res->hdr, ptr, pos0); if (ret != 0) { res->status = http_get_status(502); + ctx->origin = SERVER_RES; print(ERR_STR "Unable to parse header" CLR_STR); sprintf(err_msg, "Unable to parse header."); goto proxy_err; @@ -380,6 +400,7 @@ int rev_proxy_init(http_req *req, http_res *res, host_config *conf, sock *client ret = rev_proxy_response_header(req, res); if (ret != 0) { res->status = http_get_status(500); + ctx->origin = INTERNAL; return -1; } @@ -497,3 +518,8 @@ int rev_proxy_send(sock *client, unsigned long len_to_send, int flags) { return 0; } + +int rev_proxy_void() { + while (sock_recv(&rev_proxy, NULL, 1024, 0) > 0); + return 0; +} diff --git a/src/lib/rev_proxy.h b/src/lib/rev_proxy.h index 11c1684..5d14eea 100644 --- a/src/lib/rev_proxy.h +++ b/src/lib/rev_proxy.h @@ -13,6 +13,10 @@ #define REV_PROXY_COMPRESS_BR 4 #define REV_PROXY_COMPRESS 6 +#ifndef SERVER_NAME +# define SERVER_NAME "revproxy" +#endif + #include "http.h" #include "config.h" @@ -24,9 +28,11 @@ 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_init(http_req *req, http_res *res, http_status_ctx *ctx, host_config *conf, sock *client, + http_status *custom_status, char *err_msg); int rev_proxy_send(sock *client, unsigned long len_to_send, int flags); +int rev_proxy_void(); + #endif //NECRONDA_SERVER_REV_PROXY_H diff --git a/src/necronda-server.h b/src/necronda-server.h index 6c9bd9c..c1a7faa 100644 --- a/src/necronda-server.h +++ b/src/necronda-server.h @@ -21,10 +21,6 @@ #define CHUNK_SIZE 8192 -#ifndef DEFAULT_HOST -# define DEFAULT_HOST "www.necronda.net" -#endif - extern int sockets[NUM_SOCKETS]; extern pid_t children[MAX_CHILDREN]; extern MMDB_s mmdbs[MAX_MMDB]; diff --git a/src/necronda.h b/src/necronda.h index ec9bf0d..73367e7 100644 --- a/src/necronda.h +++ b/src/necronda.h @@ -12,4 +12,12 @@ #define SERVER_STR "Necronda/" NECRONDA_VERSION #define SERVER_STR_HTML "Necronda web server " NECRONDA_VERSION +#ifndef DEFAULT_HOST +# define DEFAULT_HOST "www.necronda.net" +#endif + +#ifndef SERVER_NAME +# define SERVER_NAME DEFAULT_HOST +#endif + #endif //NECRONDA_SERVER_NECRONDA_H