Update http header parser

This commit is contained in:
2022-08-16 18:35:03 +02:00
parent 50bb537074
commit bc1c6d3498
10 changed files with 306 additions and 96 deletions

View File

@ -1,6 +1,6 @@
CC=gcc CC=gcc
CFLAGS=-std=gnu11 -Wall CFLAGS=-std=gnu11 -Wall -Wno-unused-but-set-variable
LIBS=-lssl -lcrypto -lmagic -lz -lmaxminddb -lbrotlienc 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\"" 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: compile:
@mkdir -p bin @mkdir -p bin
$(CC) src/lib/*.c -o bin/libnecrondaserver.so --shared -fPIC $(CFLAGS) $(LIBS) $(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 -Lbin -lnecrondaserver -Wl,-rpath=$(shell pwd)/bin
compile-prod: compile-prod:
@mkdir -p bin @mkdir -p bin
$(CC) src/lib/*.c -o bin/libnecrondaserver.so --shared -fPIC $(CFLAGS) $(LIBS) $(DEBIAN_OPTS) -O3 $(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 -Lbin -lnecrondaserver -Wl,-rpath=$(shell pwd)/bin

View File

@ -5,6 +5,10 @@
* Lorenz Stechauner, 2020-12-03 * Lorenz Stechauner, 2020-12-03
*/ */
#include "client.h"
#include "necronda.h"
#include "necronda-server.h"
#include "lib/utils.h" #include "lib/utils.h"
#include "lib/config.h" #include "lib/config.h"
#include "lib/sock.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_buf[8192], msg_pre_buf_1[4096], msg_pre_buf_2[4096], err_msg[256];
char msg_content[1024]; char msg_content[1024];
char buffer[CHUNK_SIZE]; char buffer[CHUNK_SIZE];
char host[256], *host_ptr, *hdr_connection; char host[256];
const char *host_ptr, *hdr_connection;
msg_buf[0] = 0; msg_buf[0] = 0;
err_msg[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}; fastcgi_conn fcgi_conn = {.socket = 0, .req_id = 0};
http_status custom_status; 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}; http_status_ctx ctx = {.status = 0, .origin = NONE};
clock_gettime(CLOCK_MONOTONIC, &begin); 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); 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++) { 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; 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."); sprintf(err_msg, "Unable to communicate with internal file cache.");
goto respond; 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); http_add_header_field(&res.hdr, "Last-Modified", last_modified);
sprintf(buf1, "%s; charset=%s", uri.meta->type, uri.meta->charset); sprintf(buf1, "%s; charset=%s", uri.meta->type, uri.meta->charset);
http_add_header_field(&res.hdr, "Content-Type", buf1); 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; int enc = 0;
if (accept_encoding != NULL) { if (accept_encoding != NULL) {
if (uri.meta->filename_comp_br[0] != 0 && strstr(accept_encoding, "br") != 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"); 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"); const 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_none_match = http_get_header_field(&req.hdr, "If-None-Match");
if ((if_none_match != NULL && strstr(if_none_match, uri.meta->etag) == NULL) || 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)) (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; 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 (range != NULL) {
if (strlen(range) <= 6 || strncmp(range, "bytes=", 6) != 0) { if (strlen(range) <= 6 || strncmp(range, "bytes=", 6) != 0) {
res.status = http_get_status(416); res.status = http_get_status(416);
@ -399,7 +405,7 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int
goto respond; 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) { if (client_content_length != NULL) {
unsigned long client_content_len = strtoul(client_content_length, NULL, 10); unsigned long client_content_len = strtoul(client_content_length, NULL, 10);
ret = fastcgi_receive(&fcgi_conn, client, client_content_len); 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; goto respond;
} }
char *status = http_get_header_field(&res.hdr, "Status"); const char *status = http_get_header_field(&res.hdr, "Status");
if (status != NULL) { if (status != NULL) {
int status_code = (int) strtoul(status, NULL, 10); int status_code = (int) strtoul(status, NULL, 10);
res.status = http_get_status(status_code); 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)); 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, "Connection", HTTP_REMOVE_ALL);
http_remove_header_field(&res.hdr, "Keep-Alive", 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); http_send_response(client, &res);
clock_gettime(CLOCK_MONOTONIC, &end); 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; 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, 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 : "", 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; snd_len += ret;
} }
} else if (use_fastcgi) { } 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 chunked = (transfer_encoding != NULL && strcmp(transfer_encoding, "chunked") == 0);
int flags = (chunked ? FASTCGI_CHUNKED : 0) | (use_fastcgi & (FASTCGI_COMPRESS | FASTCGI_COMPRESS_HOLD)); int flags = (chunked ? FASTCGI_CHUNKED : 0) | (use_fastcgi & (FASTCGI_COMPRESS | FASTCGI_COMPRESS_HOLD));
ret = fastcgi_send(&fcgi_conn, client, flags); ret = fastcgi_send(&fcgi_conn, client, flags);
} else if (use_rev_proxy) { } 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; 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; unsigned long len_to_send = 0;
if (content_len != NULL) { if (content_len != NULL) {
len_to_send = strtol(content_len, NULL, 10); len_to_send = strtol(content_len, NULL, 10);

20
src/client.h Normal file
View File

@ -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 <arpa/inet.h>
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

View File

@ -41,9 +41,9 @@ char *fastcgi_add_param(char *buf, const char *key, const char *value) {
ptr += 4; ptr += 4;
} }
memcpy(ptr, key, key_len); strcpy(ptr, key);
ptr += key_len; ptr += key_len;
memcpy(ptr, value, val_len); strcpy(ptr, value);
ptr += val_len; ptr += val_len;
return ptr; 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, "PATH_INFO", buf0);
//param_ptr = fastcgi_add_param(param_ptr, "AUTH_TYPE", ""); //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 : ""); 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 : ""); param_ptr = fastcgi_add_param(param_ptr, "CONTENT_TYPE", content_type != NULL ? content_type : "");
if (client_geoip != NULL) { if (client_geoip != NULL) {
param_ptr = fastcgi_add_param(param_ptr, "REMOTE_INFO", client_geoip); param_ptr = fastcgi_add_param(param_ptr, "REMOTE_INFO", client_geoip);
} }
for (int i = 0; i < req->hdr.field_num; i++) { 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; char *ptr = buf0;
ptr += sprintf(ptr, "HTTP_"); ptr += sprintf(ptr, "HTTP_");
for (int j = 0; j < strlen(req->hdr.fields[i][0]); j++, ptr++) { for (int j = 0; j < strlen(name); j++, ptr++) {
char ch = req->hdr.fields[i][0][j]; char ch = name[j];
if ((ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9')) { if ((ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9')) {
ch = ch; ch = ch;
} else if (ch >= 'a' && ch <= 'z') { } 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[0] = ch;
ptr[1] = 0; 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); unsigned short param_len = param_ptr - param_buf - sizeof(header);

View File

@ -11,8 +11,10 @@
#include <string.h> #include <string.h>
void http_to_camel_case(char *str, int mode) { void http_to_camel_case(char *str, int mode) {
char last = '-'; if (mode == HTTP_PRESERVE)
char ch; return;
char ch, last = '-';
for (int i = 0; i < strlen(str); i++) { for (int i = 0; i < strlen(str); i++) {
ch = str[i]; ch = str[i];
if (mode == HTTP_CAMEL && last == '-' && ch >= 'a' && ch <= 'z') { 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) { void http_free_hdr(http_hdr *hdr) {
for (int i = 0; i < hdr->field_num; i++) { for (int i = 0; i < hdr->field_num; i++) {
free(hdr->fields[i][0]); http_field *f = &hdr->fields[i];
free(hdr->fields[i][1]); 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->field_num = 0;
hdr->last_field_num = -1;
} }
void http_free_req(http_req *req) { 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) { int http_parse_header_field(http_hdr *hdr, const char *buf, const char *end_ptr) {
char *pos1 = memchr(buf, ':', end_ptr - buf); if (hdr->last_field_num > hdr->field_num) {
char *pos2; 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) { if (pos1 == NULL) {
print(ERR_STR "Unable to parse header" CLR_STR); print(ERR_STR "Unable to parse header" CLR_STR);
return 3; return 3;
} }
long len1 = pos1 - buf - 1;
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);
pos1++; pos1++;
pos2 = (char *) end_ptr - 1; str_trim_lws(&pos1, &pos2);
while (pos1[0] == ' ') pos1++; long len2 = pos2 - pos1;
while (pos2[0] == ' ') pos2--;
len = pos2 - pos1 + 1;
if (len <= 0) { char field_num = hdr->field_num;
hdr->fields[(int) hdr->field_num][1] = malloc(1); int found = http_get_header_field_num_len(hdr, buf, len1);
hdr->fields[(int) hdr->field_num][1][0] = 0; if (found == -1) {
} else { if (http_add_header_field_len(hdr, buf, len1, pos1, len2 < 0 ? 0 : len2) != 0) {
hdr->fields[(int) hdr->field_num][1] = malloc(len + 1); print(ERR_STR "Unable to parse header: Too many header fields" CLR_STR);
sprintf(hdr->fields[(int) hdr->field_num][1], "%.*s", (int) len, pos1); return 3;
} }
hdr->field_num++; } else {
field_num = (char) found;
http_append_to_header_field(&hdr->fields[found], ", ", 2);
http_append_to_header_field(&hdr->fields[found], pos1, len2);
}
hdr->last_field_num = (char) field_num;
return 0; return 0;
} }
int http_receive_request(sock *client, http_req *req) { int http_receive_request(sock *client, http_req *req) {
long rcv_len, len; long rcv_len, len;
char *ptr, *pos0, *pos1, *pos2;
char buf[CLIENT_MAX_HEADER_SIZE]; char buf[CLIENT_MAX_HEADER_SIZE];
char *ptr, *pos0 = buf, *pos1, *pos2;
memset(buf, 0, sizeof(buf)); memset(buf, 0, sizeof(buf));
memset(req->method, 0, sizeof(req->method)); memset(req->method, 0, sizeof(req->method));
memset(req->version, 0, sizeof(req->version)); memset(req->version, 0, sizeof(req->version));
req->uri = NULL; req->uri = NULL;
req->hdr.field_num = 0; req->hdr.field_num = 0;
req->hdr.last_field_num = -1;
while (1) { while (1) {
rcv_len = sock_recv(client, buf, CLIENT_MAX_HEADER_SIZE, 0); 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; return 0;
} }
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) {
char field_name_1[256], field_name_2[256]; return http_get_header_field_len(hdr, field_name, strlen(field_name));
strcpy(field_name_1, field_name); }
http_to_camel_case(field_name_1, HTTP_LOWER);
for (int i = 0; i < hdr->field_num; i++) { const char *http_get_header_field_len(const http_hdr *hdr, const char *field_name, unsigned long len) {
strcpy(field_name_2, hdr->fields[i][0]); return http_field_get_value(&hdr->fields[http_get_header_field_num_len(hdr, field_name, len)]);
http_to_camel_case(field_name_2, HTTP_LOWER); }
if (strcmp(field_name_1, field_name_2) == 0) {
return hdr->fields[i][1]; 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));
} }
return NULL;
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);
} }
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;
hdr->field_num++; 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) { 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; diff = -1;
} }
for (; i < hdr->field_num && i >= 0; i += diff) { 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); http_to_camel_case(field_name_2, HTTP_LOWER);
if (strcmp(field_name_1, field_name_2) == 0) { if (strcmp(field_name_1, field_name_2) == 0) {
for (int j = i; j < hdr->field_num - 1; j++) { memmove(&hdr->fields[i], &hdr->fields[i + 1], sizeof(hdr->fields[0]) * (hdr->field_num - i));
memcpy(hdr->fields[j], hdr->fields[j + 1], sizeof(hdr->fields[0]));
}
hdr->field_num--; hdr->field_num--;
if (mode == HTTP_REMOVE_ALL) { if (mode == HTTP_REMOVE_ALL) {
i -= diff; i -= diff;
@ -223,7 +331,8 @@ int http_send_response(sock *client, http_res *res) {
char buf[CLIENT_MAX_HEADER_SIZE]; char buf[CLIENT_MAX_HEADER_SIZE];
long off = sprintf(buf, "HTTP/%s %03i %s\r\n", res->version, res->status->code, res->status->msg); 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++) { 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"); off += sprintf(buf + off, "\r\n");
if (sock_send(client, buf, off, 0) < 0) { 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]; char buf[CLIENT_MAX_HEADER_SIZE];
long off = sprintf(buf, "%s %s HTTP/%s\r\n", req->method, req->uri, req->version); 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++) { 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"); off += sprintf(buf + off, "\r\n");
long ret = sock_send(server, buf, off, 0); 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) { int http_get_compression(const http_req *req, const http_res *res) {
char *accept_encoding = http_get_header_field(&req->hdr, "Accept-Encoding"); const char *accept_encoding = http_get_header_field(&req->hdr, "Accept-Encoding");
char *content_type = http_get_header_field(&res->hdr, "Content-Type"); const char *content_type = http_get_header_field(&res->hdr, "Content-Type");
char *content_encoding = http_get_header_field(&res->hdr, "Content-Encoding"); 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 (mime_is_compressible(content_type) && content_encoding == NULL && accept_encoding != NULL) {
if (strstr(accept_encoding, "br") != NULL) { if (strstr(accept_encoding, "br") != NULL) {
return COMPRESS_BR; return COMPRESS_BR;

View File

@ -18,6 +18,10 @@
#define HTTP_REMOVE_ALL 1 #define HTTP_REMOVE_ALL 1
#define HTTP_REMOVE_LAST 2 #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_1XX_STR "\x1B[1;32m"
#define HTTP_2XX_STR "\x1B[1;32m" #define HTTP_2XX_STR "\x1B[1;32m"
#define HTTP_3XX_STR "\x1B[1;33m" #define HTTP_3XX_STR "\x1B[1;33m"
@ -30,6 +34,7 @@
#define HTTP_COLOR_ERROR "#C00000" #define HTTP_COLOR_ERROR "#C00000"
#define CLIENT_MAX_HEADER_SIZE 8192 #define CLIENT_MAX_HEADER_SIZE 8192
#define HTTP_MAX_HEADER_FIELD_NUM 64
#ifndef SERVER_STR #ifndef SERVER_STR
# define SERVER_STR "Necronda" # define SERVER_STR "Necronda"
@ -57,9 +62,28 @@ typedef struct {
const char *doc; const char *doc;
} http_doc_info; } 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 { typedef struct {
char field_num; char field_num;
char *fields[64][2]; char last_field_num;
http_field fields[HTTP_MAX_HEADER_FIELD_NUM];
} http_hdr; } http_hdr;
typedef struct { typedef struct {
@ -102,6 +126,10 @@ extern const char http_info_icon[];
void http_to_camel_case(char *str, int mode); 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_hdr(http_hdr *hdr);
void http_free_req(http_req *req); 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) ; 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); void http_remove_header_field(http_hdr *hdr, const char *field_name, int mode);

View File

@ -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_remove_header_field(&req->hdr, "Connection", HTTP_REMOVE_ALL);
http_add_header_field(&req->hdr, "Connection", "keep-alive"); 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); sprintf(buf1, "HTTP/%s %s", req->version, SERVER_NAME);
if (via == NULL) { if (via == NULL) {
http_add_header_field(&req->hdr, "Via", buf1); 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); http_add_header_field(&req->hdr, "Via", buf2);
} }
char *host = http_get_header_field(&req->hdr, "Host"); const char *host = http_get_header_field(&req->hdr, "Host");
char *forwarded = http_get_header_field(&req->hdr, "Forwarded"); const char *forwarded = http_get_header_field(&req->hdr, "Forwarded");
int client_ipv6 = strchr(client_addr_str, ':') != NULL; int client_ipv6 = strchr(client_addr_str, ':') != NULL;
int server_ipv6 = strchr(server_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); 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) { if (xff == NULL) {
http_add_header_field(&req->hdr, "X-Forwarded-For", client_addr_str); http_add_header_field(&req->hdr, "X-Forwarded-For", client_addr_str);
} else { } 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); 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 (xfh == NULL) {
if (forwarded == NULL) { if (forwarded == NULL) {
http_add_header_field(&req->hdr, "X-Forwarded-Host", host); 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 (xfp == NULL) {
if (forwarded == NULL) { if (forwarded == NULL) {
http_add_header_field(&req->hdr, "X-Forwarded-Proto", enc ? "https" : "http"); 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]; char buf2[256];
int p_len; 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); p_len = snprintf(buf1, sizeof(buf1), "HTTP/%s %s", req->version, SERVER_NAME);
if (p_len < 0 || p_len >= sizeof(buf1)) { if (p_len < 0 || p_len >= sizeof(buf1)) {
print(ERR_STR "Appended part of header field 'Via' too long" CLR_STR); 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); 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) { if (location != NULL) {
char *hostnames[] = {conf->name, conf->rev_proxy.hostname}; char *hostnames[] = {conf->name, conf->rev_proxy.hostname};
for (int i = 0; i < sizeof(hostnames) / sizeof(hostnames[0]); i++) { 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; 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) { if (content_length != NULL) {
unsigned long content_len = strtoul(content_length, NULL, 10); unsigned long content_len = strtoul(content_length, NULL, 10);
if (client->buf_len - client->buf_off > 0) { 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) { int rev_proxy_send(sock *client, unsigned long len_to_send, int flags) {
// TODO handle websockets // TODO handle websockets
long ret; long ret = 0;
char buffer[CHUNK_SIZE]; char buffer[CHUNK_SIZE];
char comp_out[CHUNK_SIZE]; char comp_out[CHUNK_SIZE];
char buf[256]; 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)) { if (len_to_send == 0 && (flags & REV_PROXY_COMPRESS)) {
finish_comp = 1; finish_comp = 1;
len = 0; len = 0;
ptr = NULL;
goto out; goto out;
finish: finish:
compress_free(&comp_ctx); 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; long buf_len = len;
if (flags & REV_PROXY_COMPRESS) { if (flags & REV_PROXY_COMPRESS) {
avail_out = sizeof(comp_out); avail_out = sizeof(comp_out);
compress_compress(&comp_ctx, next_in + len - avail_in, &avail_in, comp_out, &avail_out, compress_compress(&comp_ctx, next_in + len - avail_in, &avail_in, comp_out, &avail_out, finish_comp);
finish_comp);
ptr = comp_out; ptr = comp_out;
buf_len = (int) (sizeof(comp_out) - avail_out); buf_len = (int) (sizeof(comp_out) - avail_out);
snd_len += (long) (len - avail_in); snd_len += (long) (len - avail_in);

View File

@ -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) { int strcpy_rem_webroot(char *dst, const char *src, long len, const char *webroot) {
strncpy(dst, src, len); strncpy(dst, src, len);
if (webroot == NULL) return 0; if (webroot == NULL)
return 0;
char *pos; char *pos;
const unsigned long webroot_len = strlen(webroot);
if (webroot_len == 0)
return 0;
while ((pos = strstr(dst, webroot)) != NULL) { 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; return 0;
} }

View File

@ -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 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 #endif //NECRONDA_SERVER_UTILS_H

View File

@ -9,13 +9,14 @@
#include "necronda.h" #include "necronda.h"
#include "necronda-server.h" #include "necronda-server.h"
#include "client.c" #include "client.h"
#include "lib/cache.h" #include "lib/cache.h"
#include "lib/config.h" #include "lib/config.h"
#include "lib/sock.h" #include "lib/sock.h"
#include "lib/rev_proxy.h" #include "lib/rev_proxy.h"
#include "lib/geoip.h" #include "lib/geoip.h"
#include "lib/utils.h"
#include <stdio.h> #include <stdio.h>
#include <sys/socket.h> #include <sys/socket.h>