From 7cb4f40d22d826e1b04ae63284da911ee30dc2d6 Mon Sep 17 00:00:00 2001 From: Lorenz Stechauner Date: Mon, 28 Dec 2020 13:18:38 +0100 Subject: [PATCH] Process FastCGI header fields --- src/client.c | 33 ++++++++++++++---- src/fastcgi.c | 30 ++++++++++++++++- src/http.c | 92 +++++++++++++++++++++++++++++++++++---------------- src/http.h | 12 +++++-- 4 files changed, 129 insertions(+), 38 deletions(-) diff --git a/src/client.c b/src/client.c index 9c5813b..2fcb15d 100644 --- a/src/client.c +++ b/src/client.c @@ -87,9 +87,11 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int } else if (ret == 2) { sprintf(err_msg, "Unable to parse header: Invalid method."); } else if (ret == 3) { - sprintf(err_msg, "Unable to parse header: Invalid version"); + sprintf(err_msg, "Unable to parse header: Invalid version."); } else if (ret == 4) { - sprintf(err_msg, "Unable to parse header: Header contains illegal characters"); + sprintf(err_msg, "Unable to parse header: Header contains illegal characters."); + } else if (ret == 5) { + sprintf(err_msg, "Unable to parse header: End of header not found."); } res.status = http_get_status(400); goto respond; @@ -235,6 +237,8 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int if (ret != 0) { if (ret < 0) { goto abort; + } else { + sprintf(err_msg, "Unable to communicate with PHP-FPM."); } res.status = http_get_status(502); goto respond; @@ -243,15 +247,32 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int fastcgi_end: fastcgi_close_stdin(&php_fpm); + ret = fastcgi_header(&php_fpm, &res, err_msg); + if (ret != 0) { + if (ret < 0) { + goto abort; + } else { + sprintf(err_msg, "Unable to communicate with PHP-FPM."); + } + res.status = http_get_status(502); + goto respond; + } + char *status = http_get_header_field(&res.hdr, "Status"); + if (status != NULL) { + res.status = http_get_status(strtoul(status, NULL, 10)); + http_remove_header_field(&res.hdr, "Status", HTTP_REMOVE_ALL); + if (res.status == NULL){ + res.status = http_get_status(500); + sprintf(err_msg, "The status code was set to an invalid or unknown value."); + goto respond; + } + } + char *accept_encoding = http_get_header_field(&req.hdr, "Accept-Encoding"); if (accept_encoding != NULL && strstr(accept_encoding, "deflate") != NULL) { http_add_header_field(&res.hdr, "Content-Encoding", "deflate"); } - if (fastcgi_header(&php_fpm, &res, err_msg) != 0) { - goto respond; - } - content_length = -1; use_fastcgi = 1; if (http_get_header_field(&res.hdr, "Content-Length") == NULL) { diff --git a/src/fastcgi.c b/src/fastcgi.c index a5b8181..1269f7e 100644 --- a/src/fastcgi.c +++ b/src/fastcgi.c @@ -349,7 +349,35 @@ int fastcgi_header(fastcgi_conn *conn, http_res *res, char *err_msg) { conn->out_len = content_len; conn->out_off = (unsigned short) (strstr(content, "\r\n\r\n") - content + 4); - // TODO process headers and add fields to res + char *buf = content; + unsigned short header_len = conn->out_off; + if (header_len <= 0) { + print(ERR_STR "Unable to parse header: End of header not found" CLR_STR); + return 1; + } + + for (int i = 0; i < header_len; i++) { + if ((buf[i] >= 0x00 && buf[i] <= 0x1F && buf[i] != '\r' && buf[i] != '\n') || buf[i] == 0x7F) { + print(ERR_STR "Unable to parse header: Header contains illegal characters" CLR_STR); + return 2; + } + } + + char *ptr = buf; + while (header_len != (ptr - buf)) { + char *pos0 = strstr(ptr, "\r\n"); + if (pos0 == NULL) { + print(ERR_STR "Unable to parse header: Invalid header format" CLR_STR); + return 1; + } + + ret = http_parse_header_field(&res->hdr, ptr, pos0); + if (ret != 0) return ret; + if (pos0[2] == '\r' && pos0[3] == '\n') { + return 0; + } + ptr = pos0 + 2; + } return 0; } diff --git a/src/http.c b/src/http.c index a15979c..85138b4 100644 --- a/src/http.c +++ b/src/http.c @@ -9,14 +9,14 @@ #include "utils.h" -void http_to_camel_case(char *str, int strict) { +void http_to_camel_case(char *str, int mode) { char last = '-'; char ch; for (int i = 0; i < strlen(str); i++) { ch = str[i]; - if (last == '-' && ch >= 'a' && ch <= 'z') { + if (mode == HTTP_CAMEL && last == '-' && ch >= 'a' && ch <= 'z') { str[i] = (char) ((int) ch & 0x5F); - } else if (last != '-' && ch >= 'A' && ch <= 'Z' && strict == HTTP_LOWER) { + } else if (mode == HTTP_LOWER && ch >= 'A' && ch <= 'Z') { str[i] = (char) ((int) ch | 0x20); } last = str[i]; @@ -41,8 +41,33 @@ void http_free_res(http_res *res) { http_free_hdr(&res->hdr); } +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 (pos1 == NULL) { + print(ERR_STR "Unable to parse header: Invalid version" CLR_STR); + return 3; + } + + unsigned long len = pos1 - buf; + hdr->fields[hdr->field_num][0] = malloc(len + 1); + sprintf(hdr->fields[hdr->field_num][0], "%.*s", (int) len, buf); + http_to_camel_case(hdr->fields[hdr->field_num][0], HTTP_CAMEL); + + pos1++; + pos2 = (char *) end_ptr - 1; + while (pos1[0] == ' ') pos1++; + while (pos2[0] == ' ') pos2--; + len = pos2 - pos1 + 1; + hdr->fields[hdr->field_num][1] = malloc(len + 1); + sprintf(hdr->fields[hdr->field_num][1], "%.*s", (int) len, pos1); + hdr->field_num++; + + return 0; +} + int http_receive_request(sock *client, http_req *req) { - ssize_t rcv_len, len; + unsigned long rcv_len, len; char *ptr, *pos0, *pos1, *pos2; char buf[CLIENT_MAX_HEADER_SIZE]; memset(buf, 0, sizeof(buf)); @@ -71,7 +96,13 @@ int http_receive_request(sock *client, http_req *req) { return -1; } - for (int i = 0; i < rcv_len; i++) { + unsigned long header_len = strstr(buf, "\r\n\r\n") - buf + 4; + if (header_len <= 0) { + print(ERR_STR "Unable to parse header: End of header not found" CLR_STR); + return 5; + } + + for (int i = 0; i < header_len; i++) { if ((buf[i] >= 0x00 && buf[i] <= 0x1F && buf[i] != '\r' && buf[i] != '\n') || buf[i] == 0x7F) { print(ERR_STR "Unable to parse header: Header contains illegal characters" CLR_STR); return 4; @@ -79,9 +110,9 @@ int http_receive_request(sock *client, http_req *req) { } ptr = buf; - while (rcv_len != (ptr - buf)) { - pos0 = memchr(ptr, '\r', rcv_len - (ptr - buf)); - if (pos0 == NULL || pos0[1] != '\n') { + while (header_len != (ptr - buf)) { + pos0 = strstr(ptr, "\r\n"); + if (pos0 == NULL) { print(ERR_STR "Unable to parse header: Invalid header format" CLR_STR); return 1; } @@ -127,26 +158,8 @@ int http_receive_request(sock *client, http_req *req) { sprintf(req->uri, "%.*s", (int) len, pos1); sprintf(req->version, "%.3s", pos2 + 5); } else { - pos1 = memchr(ptr, ':', pos0 - ptr); - if (pos1 == NULL) { - print(ERR_STR "Unable to parse header: Invalid version" CLR_STR); - return 3; - } - - len = pos1 - ptr; - req->hdr.fields[req->hdr.field_num][0] = malloc(len + 1); - sprintf(req->hdr.fields[req->hdr.field_num][0], "%.*s", (int) len, ptr); - http_to_camel_case(req->hdr.fields[req->hdr.field_num][0], HTTP_LOWER); - - pos1++; - pos2 = pos0 - 1; - while (pos1[0] == ' ') pos1++; - while (pos2[0] == ' ') pos2--; - len = pos2 - pos1 + 1; - req->hdr.fields[req->hdr.field_num][1] = malloc(len + 1); - sprintf(req->hdr.fields[req->hdr.field_num][1], "%.*s", (int) len, pos1); - - req->hdr.field_num++; + int ret = http_parse_header_field(&req->hdr, ptr, pos0); + if (ret != 0) return ret; } if (pos0[2] == '\r' && pos0[3] == '\n') { return 0; @@ -177,12 +190,33 @@ void http_add_header_field(http_hdr *hdr, const char *field_name, const char *fi 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_UPPER); + http_to_camel_case(_field_name, HTTP_PRESERVE); hdr->fields[hdr->field_num][0] = _field_name; hdr->fields[hdr->field_num][1] = _field_value; hdr->field_num++; } +void http_remove_header_field(http_hdr *hdr, const char *field_name, int mode) { + 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) { + for (int j = i; j < hdr->field_num - 1; j++) { + memcpy(hdr->fields[j], hdr->fields[j + 1], sizeof(hdr->fields[0])); + } + hdr->field_num--; + if (mode == HTTP_REMOVE_ONE) { + return; + } else if (mode == HTTP_REMOVE_ALL) { + i--; + } + } + } +} + int http_send_response(sock *client, http_res *res) { char buf[CLIENT_MAX_HEADER_SIZE]; int len = 0; diff --git a/src/http.h b/src/http.h index d7d54bf..030a7e4 100644 --- a/src/http.h +++ b/src/http.h @@ -8,8 +8,12 @@ #ifndef NECRONDA_SERVER_HTTP_H #define NECRONDA_SERVER_HTTP_H +#define HTTP_PRESERVE 0 #define HTTP_LOWER 1 -#define HTTP_PRESERVE_UPPER 0 +#define HTTP_CAMEL 2 + +#define HTTP_REMOVE_ONE 0 +#define HTTP_REMOVE_ALL 1 typedef struct { unsigned short code; @@ -154,7 +158,7 @@ const char *http_error_icon = "eTonQXJpYWwnLHNhbnMtc2VyaWYiPjooPC90ZXh0Pjwvc3ZnPgo=\"/>\n"; -void http_to_camel_case(char *str, int strict); +void http_to_camel_case(char *str, int mode); void http_free_hdr(http_hdr *hdr); @@ -164,10 +168,14 @@ void http_free_res(http_res *res); 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); void http_add_header_field(http_hdr *hdr, const char *field_name, const char *field_value); +void http_remove_header_field(http_hdr *hdr, const char *field_name, int mode); + int http_send_response(sock *client, http_res *res); http_status *http_get_status(unsigned short status_code);