From bc1c6d349863cdd11fbe7ad5bc052ac6ea6ab0df Mon Sep 17 00:00:00 2001 From: Lorenz Stechauner Date: Tue, 16 Aug 2022 18:35:03 +0200 Subject: [PATCH] Update http header parser --- Makefile | 6 +- src/client.c | 36 ++++--- src/client.h | 20 ++++ src/lib/fastcgi.c | 16 ++-- src/lib/http.c | 216 +++++++++++++++++++++++++++++++----------- src/lib/http.h | 44 ++++++++- src/lib/rev_proxy.c | 24 ++--- src/lib/utils.c | 33 ++++++- src/lib/utils.h | 4 + src/necronda-server.c | 3 +- 10 files changed, 306 insertions(+), 96 deletions(-) create mode 100644 src/client.h diff --git a/Makefile b/Makefile index 0e771b1..02caf81 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ CC=gcc -CFLAGS=-std=gnu11 -Wall +CFLAGS=-std=gnu11 -Wall -Wno-unused-but-set-variable LIBS=-lssl -lcrypto -lmagic -lz -lmaxminddb -lbrotlienc DEBIAN_OPTS=-D CACHE_MAGIC_FILE="\"/usr/share/file/magic.mgc\"" -D PHP_FPM_SOCKET="\"/var/run/php/php7.4-fpm.sock\"" @@ -16,11 +16,11 @@ permit: 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) \ + $(CC) src/necronda-server.c src/client.c -o bin/necronda-server $(CFLAGS) $(LIBS) \ -Lbin -lnecrondaserver -Wl,-rpath=$(shell pwd)/bin compile-prod: @mkdir -p bin $(CC) src/lib/*.c -o bin/libnecrondaserver.so --shared -fPIC $(CFLAGS) $(LIBS) $(DEBIAN_OPTS) -O3 - $(CC) src/necronda-server.c -o bin/necronda-server $(CFLAGS) $(LIBS) $(DEBIAN_OPTS) -O3 \ + $(CC) src/necronda-server.c src/client.c -o bin/necronda-server $(CFLAGS) $(LIBS) $(DEBIAN_OPTS) -O3 \ -Lbin -lnecrondaserver -Wl,-rpath=$(shell pwd)/bin diff --git a/src/client.c b/src/client.c index b677c9f..485dbf6 100644 --- a/src/client.c +++ b/src/client.c @@ -5,6 +5,10 @@ * Lorenz Stechauner, 2020-12-03 */ +#include "client.h" +#include "necronda.h" +#include "necronda-server.h" + #include "lib/utils.h" #include "lib/config.h" #include "lib/sock.h" @@ -63,7 +67,8 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int char msg_buf[8192], msg_pre_buf_1[4096], msg_pre_buf_2[4096], err_msg[256]; char msg_content[1024]; char buffer[CHUNK_SIZE]; - char host[256], *host_ptr, *hdr_connection; + char host[256]; + const char *host_ptr, *hdr_connection; msg_buf[0] = 0; err_msg[0] = 0; @@ -81,7 +86,7 @@ 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 = {.version = "1.1", .status = http_get_status(501), .hdr.field_num = 0}; + http_res res = {.version = "1.1", .status = http_get_status(501), .hdr.field_num = 0, .hdr.last_field_num = -1}; http_status_ctx ctx = {.status = 0, .origin = NONE}; clock_gettime(CLOCK_MONOTONIC, &begin); @@ -218,7 +223,8 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int content_length = snprintf(msg_buf, sizeof(msg_buf) - content_length, "%s %s HTTP/%s\r\n", req.method, req.uri, req.version); for (int i = 0; i < req.hdr.field_num; i++) { - content_length += snprintf(msg_buf + content_length, sizeof(msg_buf) - content_length, "%s: %s\r\n", req.hdr.fields[i][0], req.hdr.fields[i][1]); + const http_field *f = &req.hdr.fields[i]; + content_length += snprintf(msg_buf + content_length, sizeof(msg_buf) - content_length, "%s: %s\r\n", http_field_get_name(f), http_field_get_value(f)); } goto respond; @@ -269,13 +275,13 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int sprintf(err_msg, "Unable to communicate with internal file cache."); goto respond; } - char *last_modified = http_format_date(uri.meta->stat.st_mtime, buf0, sizeof(buf0)); + const char *last_modified = http_format_date(uri.meta->stat.st_mtime, buf0, sizeof(buf0)); http_add_header_field(&res.hdr, "Last-Modified", last_modified); sprintf(buf1, "%s; charset=%s", uri.meta->type, uri.meta->charset); http_add_header_field(&res.hdr, "Content-Type", buf1); - char *accept_encoding = http_get_header_field(&req.hdr, "Accept-Encoding"); + const char *accept_encoding = http_get_header_field(&req.hdr, "Accept-Encoding"); int enc = 0; if (accept_encoding != NULL) { if (uri.meta->filename_comp_br[0] != 0 && strstr(accept_encoding, "br") != NULL) { @@ -315,8 +321,8 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int http_add_header_field(&res.hdr, "Cache-Control", "public, max-age=86400"); } - char *if_modified_since = http_get_header_field(&req.hdr, "If-Modified-Since"); - char *if_none_match = http_get_header_field(&req.hdr, "If-None-Match"); + const char *if_modified_since = http_get_header_field(&req.hdr, "If-Modified-Since"); + const char *if_none_match = http_get_header_field(&req.hdr, "If-None-Match"); if ((if_none_match != NULL && strstr(if_none_match, uri.meta->etag) == NULL) || (accept_if_modified_since && if_modified_since != NULL && strcmp(if_modified_since, last_modified) == 0)) { @@ -324,7 +330,7 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int goto respond; } - char *range = http_get_header_field(&req.hdr, "Range"); + const char *range = http_get_header_field(&req.hdr, "Range"); if (range != NULL) { if (strlen(range) <= 6 || strncmp(range, "bytes=", 6) != 0) { res.status = http_get_status(416); @@ -399,7 +405,7 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int goto respond; } - char *client_content_length = http_get_header_field(&req.hdr, "Content-Length"); + const char *client_content_length = http_get_header_field(&req.hdr, "Content-Length"); if (client_content_length != NULL) { unsigned long client_content_len = strtoul(client_content_length, NULL, 10); ret = fastcgi_receive(&fcgi_conn, client, client_content_len); @@ -421,7 +427,7 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int goto respond; } - char *status = http_get_header_field(&res.hdr, "Status"); + const char *status = http_get_header_field(&res.hdr, "Status"); if (status != NULL) { int status_code = (int) strtoul(status, NULL, 10); res.status = http_get_status(status_code); @@ -605,7 +611,7 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int } } - char *conn = http_get_header_field(&res.hdr, "Connection"); + const 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); http_remove_header_field(&res.hdr, "Keep-Alive", HTTP_REMOVE_ALL); @@ -619,7 +625,7 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int http_send_response(client, &res); clock_gettime(CLOCK_MONOTONIC, &end); - char *location = http_get_header_field(&res.hdr, "Location"); + const char *location = http_get_header_field(&res.hdr, "Location"); unsigned long micros = (end.tv_nsec - begin.tv_nsec) / 1000 + (end.tv_sec - begin.tv_sec) * 1000000; print("%s%s%03i %s%s%s (%s)%s", http_get_status_color(res.status), use_rev_proxy ? "-> " : "", res.status->code, res.status->msg, location != NULL ? " -> " : "", location != NULL ? location : "", @@ -650,16 +656,16 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int snd_len += ret; } } else if (use_fastcgi) { - char *transfer_encoding = http_get_header_field(&res.hdr, "Transfer-Encoding"); + const char *transfer_encoding = http_get_header_field(&res.hdr, "Transfer-Encoding"); int chunked = (transfer_encoding != NULL && strcmp(transfer_encoding, "chunked") == 0); int flags = (chunked ? FASTCGI_CHUNKED : 0) | (use_fastcgi & (FASTCGI_COMPRESS | FASTCGI_COMPRESS_HOLD)); ret = fastcgi_send(&fcgi_conn, client, flags); } else if (use_rev_proxy) { - char *transfer_encoding = http_get_header_field(&res.hdr, "Transfer-Encoding"); + const char *transfer_encoding = http_get_header_field(&res.hdr, "Transfer-Encoding"); int chunked = transfer_encoding != NULL && strstr(transfer_encoding, "chunked") != NULL; - char *content_len = http_get_header_field(&res.hdr, "Content-Length"); + const char *content_len = http_get_header_field(&res.hdr, "Content-Length"); unsigned long len_to_send = 0; if (content_len != NULL) { len_to_send = strtol(content_len, NULL, 10); diff --git a/src/client.h b/src/client.h new file mode 100644 index 0000000..e89edf1 --- /dev/null +++ b/src/client.h @@ -0,0 +1,20 @@ +/** + * Necronda Web Server + * Client connection and request handlers + * src/client.h + * Lorenz Stechauner, 2022-08-16 + */ + +#ifndef NECRONDA_SERVER_NECRONDA_CLIENT_H +#define NECRONDA_SERVER_NECRONDA_CLIENT_H + +#include "lib/config.h" +#include "lib/sock.h" + +#include + +host_config *get_host_config(const char *host); + +int client_handler(sock *client, unsigned long client_num, struct sockaddr_in6 *client_addr); + +#endif //NECRONDA_SERVER_NECRONDA_CLIENT_H diff --git a/src/lib/fastcgi.c b/src/lib/fastcgi.c index c70a056..145354d 100644 --- a/src/lib/fastcgi.c +++ b/src/lib/fastcgi.c @@ -41,9 +41,9 @@ char *fastcgi_add_param(char *buf, const char *key, const char *value) { ptr += 4; } - memcpy(ptr, key, key_len); + strcpy(ptr, key); ptr += key_len; - memcpy(ptr, value, val_len); + strcpy(ptr, value); ptr += val_len; return ptr; @@ -149,19 +149,21 @@ int fastcgi_init(fastcgi_conn *conn, int mode, unsigned int client_num, unsigned param_ptr = fastcgi_add_param(param_ptr, "PATH_INFO", buf0); //param_ptr = fastcgi_add_param(param_ptr, "AUTH_TYPE", ""); - char *content_length = http_get_header_field(&req->hdr, "Content-Length"); + const char *content_length = http_get_header_field(&req->hdr, "Content-Length"); param_ptr = fastcgi_add_param(param_ptr, "CONTENT_LENGTH", content_length != NULL ? content_length : ""); - char *content_type = http_get_header_field(&req->hdr, "Content-Type"); + const char *content_type = http_get_header_field(&req->hdr, "Content-Type"); param_ptr = fastcgi_add_param(param_ptr, "CONTENT_TYPE", content_type != NULL ? content_type : ""); if (client_geoip != NULL) { param_ptr = fastcgi_add_param(param_ptr, "REMOTE_INFO", client_geoip); } for (int i = 0; i < req->hdr.field_num; i++) { + const http_field *f = &req->hdr.fields[i]; + const char *name = http_field_get_name(f); char *ptr = buf0; ptr += sprintf(ptr, "HTTP_"); - for (int j = 0; j < strlen(req->hdr.fields[i][0]); j++, ptr++) { - char ch = req->hdr.fields[i][0][j]; + for (int j = 0; j < strlen(name); j++, ptr++) { + char ch = name[j]; if ((ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9')) { ch = ch; } else if (ch >= 'a' && ch <= 'z') { @@ -172,7 +174,7 @@ int fastcgi_init(fastcgi_conn *conn, int mode, unsigned int client_num, unsigned ptr[0] = ch; ptr[1] = 0; } - param_ptr = fastcgi_add_param(param_ptr, buf0, req->hdr.fields[i][1]); + param_ptr = fastcgi_add_param(param_ptr, buf0, http_field_get_value(f)); } unsigned short param_len = param_ptr - param_buf - sizeof(header); diff --git a/src/lib/http.c b/src/lib/http.c index 94c7e36..afbf0ea 100644 --- a/src/lib/http.c +++ b/src/lib/http.c @@ -11,8 +11,10 @@ #include void http_to_camel_case(char *str, int mode) { - char last = '-'; - char ch; + if (mode == HTTP_PRESERVE) + return; + + char ch, last = '-'; for (int i = 0; i < strlen(str); i++) { ch = str[i]; if (mode == HTTP_CAMEL && last == '-' && ch >= 'a' && ch <= 'z') { @@ -24,12 +26,47 @@ void http_to_camel_case(char *str, int mode) { } } +const char *http_field_get_name(const http_field *field) { + if (field->type == HTTP_FIELD_NORMAL) { + return field->normal.name; + } else if (field->type == HTTP_FIELD_EX_VALUE) { + return field->ex_value.name; + } else if (field->type == HTTP_FIELD_EX_NAME) { + return field->ex_name.name; + } + return NULL; +} + +const char *http_field_get_value(const http_field *field) { + if (field->type == HTTP_FIELD_NORMAL) { + return field->normal.value; + } else if (field->type == HTTP_FIELD_EX_VALUE) { + return field->ex_value.value; + } else if (field->type == HTTP_FIELD_EX_NAME) { + return field->ex_name.value; + } + return NULL; +} + void http_free_hdr(http_hdr *hdr) { for (int i = 0; i < hdr->field_num; i++) { - free(hdr->fields[i][0]); - free(hdr->fields[i][1]); + http_field *f = &hdr->fields[i]; + if (f->type == HTTP_FIELD_NORMAL) { + f->normal.name[0] = 0; + f->normal.value[0] = 0; + } else if (f->type == HTTP_FIELD_EX_VALUE) { + f->ex_value.name[0] = 0; + free(f->ex_value.value); + f->ex_value.value = NULL; + } else if (f->type == HTTP_FIELD_EX_NAME) { + free(f->ex_name.name); + free(f->ex_name.value); + f->ex_name.name = NULL; + f->ex_name.value = NULL; + } } hdr->field_num = 0; + hdr->last_field_num = -1; } void http_free_req(http_req *req) { @@ -43,44 +80,63 @@ void http_free_res(http_res *res) { } int http_parse_header_field(http_hdr *hdr, const char *buf, const char *end_ptr) { - char *pos1 = memchr(buf, ':', end_ptr - buf); - char *pos2; + if (hdr->last_field_num > hdr->field_num) { + print(ERR_STR "Unable to parse header: Invalid state" CLR_STR); + return 3; + } + + char *pos1 = (char *) buf, *pos2 = (char *) end_ptr; + if (buf[0] == ' ' || buf[0] == '\t') { + if (hdr->last_field_num == -1) { + print(ERR_STR "Unable to parse header" CLR_STR); + return 3; + } + http_field *f = &hdr->fields[(int) hdr->last_field_num]; + + str_trim_lws(&pos1, &pos2); + http_append_to_header_field(f, pos1, pos2 - pos1); + + return 0; + } + + pos1 = memchr(buf, ':', end_ptr - buf); if (pos1 == NULL) { print(ERR_STR "Unable to parse header" CLR_STR); return 3; } - - long len = pos1 - buf; - hdr->fields[(int) hdr->field_num][0] = malloc(len + 1); - sprintf(hdr->fields[(int) hdr->field_num][0], "%.*s", (int) len, buf); - http_to_camel_case(hdr->fields[(int) hdr->field_num][0], HTTP_CAMEL); + long len1 = pos1 - buf - 1; pos1++; - pos2 = (char *) end_ptr - 1; - while (pos1[0] == ' ') pos1++; - while (pos2[0] == ' ') pos2--; - len = pos2 - pos1 + 1; + str_trim_lws(&pos1, &pos2); + long len2 = pos2 - pos1; - if (len <= 0) { - hdr->fields[(int) hdr->field_num][1] = malloc(1); - hdr->fields[(int) hdr->field_num][1][0] = 0; + char field_num = hdr->field_num; + int found = http_get_header_field_num_len(hdr, buf, len1); + if (found == -1) { + if (http_add_header_field_len(hdr, buf, len1, pos1, len2 < 0 ? 0 : len2) != 0) { + print(ERR_STR "Unable to parse header: Too many header fields" CLR_STR); + return 3; + } } else { - hdr->fields[(int) hdr->field_num][1] = malloc(len + 1); - sprintf(hdr->fields[(int) hdr->field_num][1], "%.*s", (int) len, pos1); + field_num = (char) found; + http_append_to_header_field(&hdr->fields[found], ", ", 2); + http_append_to_header_field(&hdr->fields[found], pos1, len2); } - hdr->field_num++; + + hdr->last_field_num = (char) field_num; return 0; } int http_receive_request(sock *client, http_req *req) { long rcv_len, len; - char *ptr, *pos0, *pos1, *pos2; char buf[CLIENT_MAX_HEADER_SIZE]; + char *ptr, *pos0 = buf, *pos1, *pos2; memset(buf, 0, sizeof(buf)); memset(req->method, 0, sizeof(req->method)); memset(req->version, 0, sizeof(req->version)); req->uri = NULL; req->hdr.field_num = 0; + req->hdr.last_field_num = -1; while (1) { rcv_len = sock_recv(client, buf, CLIENT_MAX_HEADER_SIZE, 0); @@ -164,31 +220,85 @@ int http_receive_request(sock *client, http_req *req) { return 0; } -char *http_get_header_field(const http_hdr *hdr, const char *field_name) { - char field_name_1[256], field_name_2[256]; - strcpy(field_name_1, field_name); - http_to_camel_case(field_name_1, HTTP_LOWER); - for (int i = 0; i < hdr->field_num; i++) { - strcpy(field_name_2, hdr->fields[i][0]); - http_to_camel_case(field_name_2, HTTP_LOWER); - if (strcmp(field_name_1, field_name_2) == 0) { - return hdr->fields[i][1]; - } - } - return NULL; +const char *http_get_header_field(const http_hdr *hdr, const char *field_name) { + return http_get_header_field_len(hdr, field_name, strlen(field_name)); } -void http_add_header_field(http_hdr *hdr, const char *field_name, const char *field_value) { - size_t len_name = strlen(field_name); - size_t len_value = strlen(field_value); - char *_field_name = malloc(len_name + 1); - char *_field_value = malloc(len_value + 1); - strcpy(_field_name, field_name); - strcpy(_field_value, field_value); - http_to_camel_case(_field_name, HTTP_PRESERVE); - hdr->fields[(int) hdr->field_num][0] = _field_name; - hdr->fields[(int) hdr->field_num][1] = _field_value; +const char *http_get_header_field_len(const http_hdr *hdr, const char *field_name, unsigned long len) { + return http_field_get_value(&hdr->fields[http_get_header_field_num_len(hdr, field_name, len)]); +} + +int http_get_header_field_num(const http_hdr *hdr, const char *field_name) { + return http_get_header_field_num_len(hdr, field_name, strlen(field_name)); +} + +int http_get_header_field_num_len(const http_hdr *hdr, const char *field_name, unsigned long len) { + char field_name_1[256], field_name_2[256]; + memcpy(field_name_1, field_name, len); + http_to_camel_case(field_name_1, HTTP_LOWER); + for (int i = 0; i < hdr->field_num; i++) { + strcpy(field_name_2, http_field_get_name(&hdr->fields[i])); + http_to_camel_case(field_name_2, HTTP_LOWER); + if (strcmp(field_name_1, field_name_2) == 0) { + return i; + } + } + return -1; +} + +int http_add_header_field(http_hdr *hdr, const char *field_name, const char *field_value) { + return http_add_header_field_len(hdr, field_name, strlen(field_name), field_value, strlen(field_value)); +} + +int http_add_header_field_len(http_hdr *hdr, const char *name, unsigned long name_len, const char *value, unsigned long value_len) { + if (hdr->field_num >= HTTP_MAX_HEADER_FIELD_NUM) + return -1; + + http_field *f = &hdr->fields[(int) hdr->field_num]; + + if (name_len < sizeof(f->normal.name) && value_len < sizeof(f->normal.value)) { + f->type = HTTP_FIELD_NORMAL; + strncpy(f->normal.name, name, name_len); + strncpy(f->normal.value, value, value_len); + http_to_camel_case(f->normal.name, HTTP_PRESERVE); + } else if (name_len < sizeof(f->ex_value.name)) { + f->type = HTTP_FIELD_EX_VALUE; + f->ex_value.value = malloc(value_len + 1); + strncpy(f->ex_value.name, name, name_len); + strncpy(f->ex_value.value, value, value_len); + http_to_camel_case(f->ex_value.name, HTTP_PRESERVE); + } else { + f->type = HTTP_FIELD_EX_NAME; + f->ex_name.name = malloc(name_len + 1); + f->ex_name.value = malloc(value_len + 1); + strncpy(f->ex_name.name, name, name_len); + strncpy(f->ex_name.value, value, value_len); + http_to_camel_case(f->ex_name.name, HTTP_PRESERVE); + } + hdr->field_num++; + return 0; +} + +void http_append_to_header_field(http_field *field, const char *value, unsigned long len) { + if (field->type == HTTP_FIELD_NORMAL) { + unsigned long total_len = strlen(field->normal.value) + len + 1; + if (total_len < sizeof(field->normal.value)) { + strncat(field->normal.value, value, len); + } else { + field->type = HTTP_FIELD_EX_VALUE; + char *new = malloc(total_len); + strcpy(new, field->normal.value); + strncat(new, value, len); + field->ex_value.value = new; + } + } else if (field->type == HTTP_FIELD_EX_VALUE) { + field->ex_value.value = realloc(field->ex_value.value, strlen(field->ex_value.value) + len + 1); + strncat(field->ex_value.value, value, len); + } else if (field->type == HTTP_FIELD_EX_NAME) { + field->ex_name.value = realloc(field->ex_name.value, strlen(field->ex_name.value) + len + 1); + strncat(field->ex_name.value, value, len); + } } void http_remove_header_field(http_hdr *hdr, const char *field_name, int mode) { @@ -203,12 +313,10 @@ void http_remove_header_field(http_hdr *hdr, const char *field_name, int mode) { diff = -1; } for (; i < hdr->field_num && i >= 0; i += diff) { - strcpy(field_name_2, hdr->fields[i][0]); + strcpy(field_name_2, http_field_get_name(&hdr->fields[i])); http_to_camel_case(field_name_2, HTTP_LOWER); if (strcmp(field_name_1, field_name_2) == 0) { - for (int j = i; j < hdr->field_num - 1; j++) { - memcpy(hdr->fields[j], hdr->fields[j + 1], sizeof(hdr->fields[0])); - } + memmove(&hdr->fields[i], &hdr->fields[i + 1], sizeof(hdr->fields[0]) * (hdr->field_num - i)); hdr->field_num--; if (mode == HTTP_REMOVE_ALL) { i -= diff; @@ -223,7 +331,8 @@ int http_send_response(sock *client, http_res *res) { char buf[CLIENT_MAX_HEADER_SIZE]; long off = sprintf(buf, "HTTP/%s %03i %s\r\n", res->version, res->status->code, res->status->msg); for (int i = 0; i < res->hdr.field_num; i++) { - off += sprintf(buf + off, "%s: %s\r\n", res->hdr.fields[i][0], res->hdr.fields[i][1]); + const http_field *f = &res->hdr.fields[i]; + off += sprintf(buf + off, "%s: %s\r\n", http_field_get_name(f), http_field_get_value(f)); } off += sprintf(buf + off, "\r\n"); if (sock_send(client, buf, off, 0) < 0) { @@ -236,7 +345,8 @@ int http_send_request(sock *server, http_req *req) { char buf[CLIENT_MAX_HEADER_SIZE]; long off = sprintf(buf, "%s %s HTTP/%s\r\n", req->method, req->uri, req->version); for (int i = 0; i < req->hdr.field_num; i++) { - off += sprintf(buf + off, "%s: %s\r\n", req->hdr.fields[i][0], req->hdr.fields[i][1]); + const http_field *f = &req->hdr.fields[i]; + off += sprintf(buf + off, "%s: %s\r\n", http_field_get_name(f), http_field_get_value(f)); } off += sprintf(buf + off, "\r\n"); long ret = sock_send(server, buf, off, 0); @@ -314,9 +424,9 @@ const http_doc_info *http_get_status_info(const http_status *status) { } int http_get_compression(const http_req *req, const http_res *res) { - char *accept_encoding = http_get_header_field(&req->hdr, "Accept-Encoding"); - char *content_type = http_get_header_field(&res->hdr, "Content-Type"); - char *content_encoding = http_get_header_field(&res->hdr, "Content-Encoding"); + const char *accept_encoding = http_get_header_field(&req->hdr, "Accept-Encoding"); + const char *content_type = http_get_header_field(&res->hdr, "Content-Type"); + const char *content_encoding = http_get_header_field(&res->hdr, "Content-Encoding"); if (mime_is_compressible(content_type) && content_encoding == NULL && accept_encoding != NULL) { if (strstr(accept_encoding, "br") != NULL) { return COMPRESS_BR; diff --git a/src/lib/http.h b/src/lib/http.h index 002d1e7..c624396 100644 --- a/src/lib/http.h +++ b/src/lib/http.h @@ -18,6 +18,10 @@ #define HTTP_REMOVE_ALL 1 #define HTTP_REMOVE_LAST 2 +#define HTTP_FIELD_NORMAL 0 +#define HTTP_FIELD_EX_VALUE 1 +#define HTTP_FIELD_EX_NAME 2 + #define HTTP_1XX_STR "\x1B[1;32m" #define HTTP_2XX_STR "\x1B[1;32m" #define HTTP_3XX_STR "\x1B[1;33m" @@ -30,6 +34,7 @@ #define HTTP_COLOR_ERROR "#C00000" #define CLIENT_MAX_HEADER_SIZE 8192 +#define HTTP_MAX_HEADER_FIELD_NUM 64 #ifndef SERVER_STR # define SERVER_STR "Necronda" @@ -57,9 +62,28 @@ typedef struct { const char *doc; } http_doc_info; +typedef struct { + char type; + union { + struct { + char name[64]; + char value[192]; + } normal; + struct { + char name[192]; + char *value; + } ex_value; + struct { + char *name; + char *value; + } ex_name; + }; +} http_field; + typedef struct { char field_num; - char *fields[64][2]; + char last_field_num; + http_field fields[HTTP_MAX_HEADER_FIELD_NUM]; } http_hdr; typedef struct { @@ -102,6 +126,10 @@ extern const char http_info_icon[]; void http_to_camel_case(char *str, int mode); +const char *http_field_get_name(const http_field *field); + +const char *http_field_get_value(const http_field *field); + void http_free_hdr(http_hdr *hdr); void http_free_req(http_req *req); @@ -112,9 +140,19 @@ int http_receive_request(sock *client, http_req *req); int http_parse_header_field(http_hdr *hdr, const char *buf, const char *end_ptr) ; -char *http_get_header_field(const http_hdr *hdr, const char *field_name); +const char *http_get_header_field(const http_hdr *hdr, const char *field_name); -void http_add_header_field(http_hdr *hdr, const char *field_name, const char *field_value); +const char *http_get_header_field_len(const http_hdr *hdr, const char *field_name, unsigned long len); + +int http_get_header_field_num(const http_hdr *hdr, const char *field_name); + +int http_get_header_field_num_len(const http_hdr *hdr, const char *field_name, unsigned long len); + +int http_add_header_field(http_hdr *hdr, const char *field_name, const char *field_value); + +int http_add_header_field_len(http_hdr *hdr, const char *name, unsigned long name_len, const char *value, unsigned long value_len); + +void http_append_to_header_field(http_field *field, const char *value, unsigned long len); void http_remove_header_field(http_hdr *hdr, const char *field_name, int mode); diff --git a/src/lib/rev_proxy.c b/src/lib/rev_proxy.c index 3447a95..0b7e26f 100644 --- a/src/lib/rev_proxy.c +++ b/src/lib/rev_proxy.c @@ -35,7 +35,7 @@ int rev_proxy_request_header(http_req *req, int enc) { 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"); + const char *via = http_get_header_field(&req->hdr, "Via"); sprintf(buf1, "HTTP/%s %s", req->version, SERVER_NAME); if (via == NULL) { http_add_header_field(&req->hdr, "Via", buf1); @@ -49,8 +49,8 @@ int rev_proxy_request_header(http_req *req, int enc) { 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"); + const char *host = http_get_header_field(&req->hdr, "Host"); + const 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; @@ -74,7 +74,7 @@ int rev_proxy_request_header(http_req *req, int enc) { http_add_header_field(&req->hdr, "Forwarded", buf2); } - char *xff = http_get_header_field(&req->hdr, "X-Forwarded-For"); + const 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 { @@ -83,7 +83,7 @@ int rev_proxy_request_header(http_req *req, int enc) { http_add_header_field(&req->hdr, "X-Forwarded-For", buf1); } - char *xfh = http_get_header_field(&req->hdr, "X-Forwarded-Host"); + const 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); @@ -104,7 +104,7 @@ int rev_proxy_request_header(http_req *req, int enc) { } } - char *xfp = http_get_header_field(&req->hdr, "X-Forwarded-Proto"); + const 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"); @@ -133,7 +133,7 @@ int rev_proxy_response_header(http_req *req, http_res *res, host_config *conf) { char buf2[256]; int p_len; - char *via = http_get_header_field(&res->hdr, "Via"); + const char *via = http_get_header_field(&res->hdr, "Via"); 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); @@ -151,7 +151,7 @@ int rev_proxy_response_header(http_req *req, http_res *res, host_config *conf) { http_add_header_field(&res->hdr, "Via", buf2); } - char *location = http_get_header_field(&res->hdr, "Location"); + const char *location = http_get_header_field(&res->hdr, "Location"); if (location != NULL) { char *hostnames[] = {conf->name, conf->rev_proxy.hostname}; for (int i = 0; i < sizeof(hostnames) / sizeof(hostnames[0]); i++) { @@ -307,7 +307,7 @@ int rev_proxy_init(http_req *req, http_res *res, http_status_ctx *ctx, host_conf goto proxy_err; } - char *content_length = http_get_header_field(&req->hdr, "Content-Length"); + const char *content_length = http_get_header_field(&req->hdr, "Content-Length"); if (content_length != NULL) { unsigned long content_len = strtoul(content_length, NULL, 10); if (client->buf_len - client->buf_off > 0) { @@ -455,7 +455,7 @@ int rev_proxy_init(http_req *req, http_res *res, http_status_ctx *ctx, host_conf int rev_proxy_send(sock *client, unsigned long len_to_send, int flags) { // TODO handle websockets - long ret; + long ret = 0; char buffer[CHUNK_SIZE]; char comp_out[CHUNK_SIZE]; char buf[256]; @@ -498,6 +498,7 @@ int rev_proxy_send(sock *client, unsigned long len_to_send, int flags) { if (len_to_send == 0 && (flags & REV_PROXY_COMPRESS)) { finish_comp = 1; len = 0; + ptr = NULL; goto out; finish: compress_free(&comp_ctx); @@ -519,8 +520,7 @@ int rev_proxy_send(sock *client, unsigned long len_to_send, int flags) { long buf_len = len; if (flags & REV_PROXY_COMPRESS) { avail_out = sizeof(comp_out); - compress_compress(&comp_ctx, next_in + len - avail_in, &avail_in, comp_out, &avail_out, - finish_comp); + compress_compress(&comp_ctx, next_in + len - avail_in, &avail_in, comp_out, &avail_out, finish_comp); ptr = comp_out; buf_len = (int) (sizeof(comp_out) - avail_out); snd_len += (long) (len - avail_in); diff --git a/src/lib/utils.c b/src/lib/utils.c index 2e62483..a843fd2 100644 --- a/src/lib/utils.c +++ b/src/lib/utils.c @@ -136,10 +136,39 @@ int mime_is_compressible(const char *type) { int strcpy_rem_webroot(char *dst, const char *src, long len, const char *webroot) { strncpy(dst, src, len); - if (webroot == NULL) return 0; + if (webroot == NULL) + return 0; + char *pos; + const unsigned long webroot_len = strlen(webroot); + if (webroot_len == 0) + return 0; + while ((pos = strstr(dst, webroot)) != NULL) { - strcpy(pos, pos + strlen(webroot)); + strcpy(pos, pos + webroot_len); } + + return 0; +} + +int str_trim(char **start, char **end) { + if (start == NULL || end == NULL || *start == NULL || *end == NULL) + return -1; + + (*end)--; + while (*start[0] == ' ' || *start[0] == '\t' || *start[0] == '\r' || *start[0] == '\n') (*start)++; + while (*end[0] == ' ' || *end[0] == '\t' || *end[0] == '\r' || *end[0] == '\n') (*end)--; + (*end)++; + return 0; +} + +int str_trim_lws(char **start, char **end) { + if (start == NULL || end == NULL || *start == NULL || *end == NULL) + return -1; + + (*end)--; + while (*start[0] == ' ' || *start[0] == '\t') (*start)++; + while (*end[0] == ' ' || *end[0] == '\t') (*end)--; + (*end)++; return 0; } diff --git a/src/lib/utils.h b/src/lib/utils.h index a573581..f772295 100644 --- a/src/lib/utils.h +++ b/src/lib/utils.h @@ -42,4 +42,8 @@ int mime_is_compressible(const char *type); int strcpy_rem_webroot(char *dst, const char *str, long len, const char *webroot); +int str_trim(char **start, char **end); + +int str_trim_lws(char **start, char **end); + #endif //NECRONDA_SERVER_UTILS_H diff --git a/src/necronda-server.c b/src/necronda-server.c index d915aa5..a8d5639 100644 --- a/src/necronda-server.c +++ b/src/necronda-server.c @@ -9,13 +9,14 @@ #include "necronda.h" #include "necronda-server.h" -#include "client.c" +#include "client.h" #include "lib/cache.h" #include "lib/config.h" #include "lib/sock.h" #include "lib/rev_proxy.h" #include "lib/geoip.h" +#include "lib/utils.h" #include #include