From 89c62ba0739421b44c2f6d54a89011d81bf9e57c Mon Sep 17 00:00:00 2001 From: cflip Date: Mon, 7 Aug 2023 09:44:56 -0600 Subject: Improve HTTP request parsing and pass cookies to CGI scripts The HTTP request parser has been mostly rewritten, and now it is able to parse header lines. This makes it possible to get the cookie string from the request and pass it to CGI scripts, which was needed for the cflip forum to retain login status. It is now possible to run the forum through cfws, log in and submit threads and posts! --- file.c | 4 ++ http.c | 139 ++++++++++++++++++++++++++++++++++++++++++++++------------------- http.h | 1 + 3 files changed, 104 insertions(+), 40 deletions(-) diff --git a/file.c b/file.c index 4e42c58..e1c209a 100644 --- a/file.c +++ b/file.c @@ -126,6 +126,9 @@ static void cgi_setup_env(const char *filepath, const struct http_request *req) if (req->query_str) setenv("QUERY_STRING", req->query_str, 1); + if (req->cookie) + setenv("HTTP_COOKIE", req->cookie, 1); + if (req->body) { static char intbuf[20]; static char *content_type = "application/x-www-form-urlencoded"; @@ -196,6 +199,7 @@ int file_read_cgi(const char *filepath, const char *program, const struct http_r write(sockfd, buffer, bytes_read); unsetenv("QUERY_STRING"); + unsetenv("HTTP_COOKIE"); unsetenv("CONTENT"); unsetenv("CONTENT_LENGTH"); diff --git a/http.c b/http.c index 3521e32..53d300f 100644 --- a/http.c +++ b/http.c @@ -1,69 +1,126 @@ #include "http.h" +#include #include #include #include #include #include -struct http_request http_parse_request(const char *reqstr) +ssize_t http_parse_request_line(const char *reqline, struct http_request *req_out) { char uribuf[CFWS_MAXURI]; size_t urilen = 0, query_len = 0; - const char *counter; - struct http_request req; - req.method = HTTP_METHOD_UNKNOWN; - req.uri = NULL; - req.query_str = NULL; - req.body = NULL; + const char *ch = reqline; - if (strncmp(reqstr, "GET ", 4) == 0) { - req.method = HTTP_METHOD_GET; - counter = reqstr + 4; - } else if (strncmp(reqstr, "POST ", 5) == 0) { - req.method = HTTP_METHOD_POST; - counter = reqstr + 5; + /* Quick 'n' dirty strncmp to determine the request method. */ + if (strncmp(reqline, "GET ", 4) == 0) { + req_out->method = HTTP_METHOD_GET; + ch += 4; + } else if (strncmp(reqline, "POST ", 5) == 0) { + req_out->method = HTTP_METHOD_POST; + ch += 5; } else { - fprintf(stderr, "Unhandled request type: %s\n", reqstr); - return req; + fprintf(stderr, "Unhandled request type: '%s'\n", reqline); + return -1; + } + + /* The first char after the first space should be a slash, otherwise + * there's either an invalid URI or something we don't handle yet. */ + if (*ch != '/') { + fprintf(stderr, "Invalid URI string: '%s'\n", ch); + return -1; } - while (*counter != ' ' && *counter != '?' && *counter != 0 - && urilen < CFWS_MAXURI) { - uribuf[urilen++] = *counter; - ++counter; + /* Copy URI path into a buffer, until we reach a space, query string, or + * one of the buffers reaches the end. */ + while (*ch != ' ' && *ch != '?' && *ch != 0 && urilen < CFWS_MAXURI) { + uribuf[urilen++] = *ch; + ++ch; } - req.uri = malloc(urilen + 1); - memcpy(req.uri, uribuf, urilen); - req.uri[urilen] = '\0'; + req_out->uri = malloc(urilen + 1); + memcpy(req_out->uri, uribuf, urilen); + req_out->uri[urilen] = '\0'; /* Parse the query string if one exists. */ - if (*counter == '?') { - /* Skip the question mark at the beginning. */ - counter++; - - while (*counter != ' ' && *counter != 0 - && query_len < CFWS_MAXURI) { - uribuf[query_len++] = *counter; - ++counter; + if (*ch == '?') { + ch++; /* Skip the question mark at the beginning. */ + + while (*ch != ' ' && *ch != 0 && query_len < CFWS_MAXURI) { + uribuf[query_len++] = *ch; + ++ch; } - req.query_str = malloc(query_len + 1); - memcpy(req.query_str, uribuf, query_len); - req.query_str[query_len] = '\0'; + req_out->query_str = malloc(query_len + 1); + memcpy(req_out->query_str, uribuf, query_len); + req_out->query_str[query_len] = '\0'; + } + + /* Lastly, read the HTTP version and for now make sure it's HTTP/1.1. */ + if (strncmp(ch, " HTTP/1.1\r\n", 9) != 0) { + fprintf(stderr, "Invalid HTTP version: '%s'", ch); + return -1; } - /* TODO: Parse request headers. For now they are just ignored. */ - counter = strstr(reqstr, "\r\n\r\n"); - if (req.method == HTTP_METHOD_POST) { - size_t body_len; - counter += 4; + ch += 9; + return ch - reqline; +} + +struct http_request http_parse_request(const char *reqstr) +{ + const char *current_line = reqstr; + bool has_parsed_header = false; - body_len = strlen(counter); + struct http_request req; + req.method = HTTP_METHOD_UNKNOWN; + req.uri = NULL; + req.query_str = NULL; + req.cookie = NULL; + req.body = NULL; + + while (current_line) { + char *next_line = strstr(current_line, "\r\n"); + size_t line_length = 0; + if (next_line) { + line_length = next_line - current_line; + *next_line = '\0'; + next_line++; + } + + if (!has_parsed_header) { + http_parse_request_line(current_line, &req); + has_parsed_header = true; + } else { + if (line_length == 0) { + /* An empty line means we are at the end of the + * request headers. */ + current_line = next_line ? (next_line + 1) : NULL; + break; + } + + if (strncmp(current_line, "Cookie: ", 8) == 0) { + const char *cookie_str = current_line + 8; + size_t cookie_length = next_line - cookie_str; + + req.cookie = malloc(cookie_length + 1); + memcpy(req.cookie, cookie_str, cookie_length); + req.cookie[cookie_length] = '\0'; + } + } + + if (next_line) + *next_line = '\n'; + current_line = next_line ? (next_line + 1) : NULL; + } + + /* Copy the request body if we have one */ + if (current_line && *current_line != '\0') { + size_t body_len; + body_len = strlen(current_line); req.body = malloc(body_len + 1); - memcpy(req.body, counter, body_len); + memcpy(req.body, current_line, body_len); req.body[body_len] = '\0'; } @@ -75,6 +132,8 @@ void http_free_request(struct http_request *req) free(req->uri); if (req->query_str) free(req->query_str); + if (req->cookie) + free(req->cookie); if (req->body) free(req->body); } diff --git a/http.h b/http.h index caeb28d..09dac27 100644 --- a/http.h +++ b/http.h @@ -23,6 +23,7 @@ struct http_request { int method; char *uri; char *query_str; + char *cookie; char *body; }; -- cgit v1.2.3