From 6cd5d365a877629eb9999348f277c56b386ac440 Mon Sep 17 00:00:00 2001 From: June McEnroe Date: Fri, 24 Sep 2021 10:35:38 -0400 Subject: Consume request headers using MSG_PEEK Use MSG_PEEK to determine where the request headers end and consume only up to there, leaving the CGI process to read any request body directly from the socket. --- bin/quick.c | 87 +++++++++++++++++++++++++++---------------------------------- 1 file changed, 39 insertions(+), 48 deletions(-) diff --git a/bin/quick.c b/bin/quick.c index b24aef24..06810463 100644 --- a/bin/quick.c +++ b/bin/quick.c @@ -19,7 +19,7 @@ #include #include #include -#include +#include #include #include #include @@ -30,78 +30,74 @@ #include static void request(int sock, char *argv[]) { - FILE *req = fdopen(dup(sock), "r"); - if (!req) err(EX_OSERR, "fdopen"); - fcntl(fileno(req), F_SETFD, FD_CLOEXEC); - - size_t cap = 0; - char *buf = NULL; - ssize_t len = getline(&buf, &cap, req); - if (len < 0) goto close; + struct pollfd pfd = { .fd = sock, .events = POLLIN }; + int nfds = poll(&pfd, 1, -1); + if (nfds < 0) err(EX_OSERR, "poll"); + + char buf[4096]; + ssize_t len = recv(sock, buf, sizeof(buf)-1, MSG_PEEK); + if (len < 0) { + warn("recv"); + return; + } + char *blank = memmem(buf, len, "\r\n\r\n", 4); + if (!blank) { + warnx("can't find end of request headers in peek"); + return; + } + len = recv(sock, buf, &blank[4] - buf, 0); + if (len < 0) { + warn("recv"); + return; + } + buf[len] = '\0'; char *ptr = buf; - char *method = strsep(&ptr, " "); - char *query = strsep(&ptr, " "); + char *req = strsep(&ptr, "\r\n"); + char *method = strsep(&req, " "); + char *query = strsep(&req, " "); char *path = strsep(&query, "?"); - char *proto = strsep(&ptr, "\r\n"); - if (!method || !path || !proto) goto close; - + char *proto = strsep(&req, " "); + if (!method || !path || !proto) { + warnx("invalid request line"); + return; + } setenv("REQUEST_METHOD", method, 1); setenv("PATH_INFO", path, 1); setenv("QUERY_STRING", (query ? query : ""), 1); setenv("SERVER_PROTOCOL", proto, 1); + unsetenv("CONTENT_TYPE"); unsetenv("CONTENT_LENGTH"); unsetenv("HTTP_HOST"); - - size_t bodyLen = 0; - while (0 <= (len = getline(&buf, &cap, req))) { - if (len && buf[len-1] == '\n') buf[--len] = '\0'; - if (len && buf[len-1] == '\r') buf[--len] = '\0'; - if (!len) break; - - char *value = buf; + while (ptr) { + char *value = strsep(&ptr, "\r\n"); + if (!value[0]) continue; char *header = strsep(&value, ":"); - if (!header || !value++) goto close; - + if (!header || !value++) { + warnx("invalid header"); + return; + } if (!strcasecmp(header, "Content-Type")) { setenv("CONTENT_TYPE", value, 1); } else if (!strcasecmp(header, "Content-Length")) { - bodyLen = strtoull(value, NULL, 10); setenv("CONTENT_LENGTH", value, 1); } else if (!strcasecmp(header, "Host")) { setenv("HTTP_HOST", value, 1); } } - int rw[2]; - int error = pipe(rw); - if (error) err(EX_OSERR, "pipe"); - fcntl(rw[0], F_SETFD, FD_CLOEXEC); - fcntl(rw[1], F_SETFD, FD_CLOEXEC); - dprintf(sock, "HTTP/1.1 200 OK\nConnection: close\n"); pid_t pid = fork(); if (pid < 0) err(EX_OSERR, "fork"); if (!pid) { - dup2(rw[0], STDIN_FILENO); + dup2(sock, STDIN_FILENO); dup2(sock, STDOUT_FILENO); execv(argv[0], argv); warn("%s", argv[0]); _exit(127); } - close(rw[0]); - char body[4096]; - while (bodyLen) { - size_t cap = (bodyLen < sizeof(body) ? bodyLen : sizeof(body)); - size_t len = fread(&body, 1, cap, req); - if (!len) break; - write(rw[1], body, len); - bodyLen -= len; - } - close(rw[1]); - int status; pid = wait(&status); if (pid < 0) err(EX_OSERR, "wait"); @@ -110,10 +106,6 @@ static void request(int sock, char *argv[]) { } else if (WIFSIGNALED(status)) { warnx("%s killed %d", argv[0], WTERMSIG(status)); } - -close: - fclose(req); - free(buf); } int main(int argc, char *argv[]) { @@ -160,7 +152,6 @@ int main(int argc, char *argv[]) { setenv("REMOTE_HOST", host, 1); setenv("SCRIPT_NAME", "/", 1); - signal(SIGPIPE, SIG_IGN); for (int sock; 0 <= (sock = accept(server, NULL, NULL)); close(sock)) { request(sock, &argv[optind]); } -- cgit 1.4.1 istian Kellermann 2022-03-05Update STANDARDS section authors, titles and URLsJune McEnroe 2022-03-02Show own nick on /nick without paramsJune McEnroe 2022-02-26Specify commands which depend on capsJune McEnroe 2022-02-26Only add available commands to completeJune McEnroe 2022-02-26Factor out commandAvailableJune McEnroe 2022-02-23Give examples of "general events" 2.1June McEnroe 2022-02-23Add missing unistd.h include in input.cJune McEnroe 2022-02-22Document the interfaceJune McEnroe 2022-02-20Clean up unimplemented editing mode stuffJune McEnroe 2022-02-20Save input buffer contentsJune McEnroe 2022-02-20Share a cut buffer between all edit buffersJune McEnroe 2022-02-20Assert return values in edit testsJune McEnroe 2022-02-20Move mbs out of struct Edit, use a global bufferJune McEnroe 2022-02-20Clear edit buffer before running commandJune McEnroe 2022-02-20Show indicator in status when window has pending inputJune McEnroe 2022-02-20Use separate edit buffers for each IDJune McEnroe 2022-02-20Make sure new cap is actually larger than new lengthJune McEnroe 2022-02-20Remove unused mbs.len field from struct EditJune McEnroe 2022-02-19Remove unneeded includes in ui.cJune McEnroe 2022-02-19Reimplement tab completeJune McEnroe 2022-02-19Handle errors from editFn, etc.June McEnroe 2022-02-19Reimplement text macrosJune McEnroe 2022-02-19Factor out input handling to input.cJune McEnroe 2022-02-19Factor out window management to window.cJune McEnroe 2022-02-19Enable -Wmissing-prototypesJune McEnroe 2022-02-19Fix edit.[ch] license notice additional permissionsJune McEnroe 2022-02-19Run line editing testsJune McEnroe 2022-02-18Implement new line editing "library"June McEnroe 2022-02-18Simplify cursor positioning in inputJune McEnroe 2022-02-18Fix M-f orderingJune McEnroe 2022-02-12Move sandman build to scripts/MakefileJune McEnroe 2022-02-12Use compat_readpassphrase.c on LinuxJune McEnroe 2022-02-12Copy RPP defines from oconfigureJune McEnroe