/* swebs - a simple web server Copyright (C) 2022 Nate Choe This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include int newConnection(Stream *stream, Connection *ret, int portind) { struct timespec currentTime; ret->stream = stream; ret->progress = RECEIVE_REQUEST; ret->currLineAlloc = 30; ret->currLineLen = 0; ret->currLine = malloc(ret->currLineAlloc); if (ret->currLine == NULL) return 1; ret->allocatedFields = 10; ret->fields = malloc(sizeof(Field) * ret->allocatedFields); if (ret->fields == NULL) { free(ret->currLine); return 1; } ret->fieldCount = 0; ret->allocatedPathFields = 10; ret->pathFields = malloc(ret->allocatedPathFields * sizeof(PathField)); if (ret->pathFields == NULL) { free(ret->currLine); free(ret->fields); return 1; } ret->pathFieldCount = 0; ret->body = NULL; /* * pointers to things that are allocated within functions should be * initialized to NULL so that free() doens't fail. * */ if (clock_gettime(CLOCK_MONOTONIC, ¤tTime) < 0) { free(ret->currLine); free(ret->fields); free(ret->pathFields); return 1; } memcpy(&ret->lastdata, ¤tTime, sizeof(struct timespec)); ret->portind = portind; return 0; } void resetConnection(Connection *conn) { long i; conn->progress = RECEIVE_REQUEST; free(conn->body); conn->body = NULL; for (i = 0; i < conn->fieldCount; i++) { free(conn->fields[i].field); free(conn->fields[i].value); } conn->fieldCount = 0; for (i = 0; i < conn->pathFieldCount; i++) { free(conn->pathFields[i].var.data); free(conn->pathFields[i].value.data); } conn->pathFieldCount = 0; } void freeConnection(Connection *conn) { long i; freeStream(conn->stream); free(conn->currLine); free(conn->body); for (i = 0; i < conn->fieldCount; i++) { free(conn->fields[i].field); free(conn->fields[i].value); } for (i = 0; i < conn->pathFieldCount; i++) { free(conn->pathFields[i].var.data); free(conn->pathFields[i].value.data); } free(conn->pathFields); free(conn->fields); } static int createBinaryString(BinaryString *ret) { ret->allocatedLen = 50; ret->data = malloc(ret->allocatedLen); if (ret->data == NULL) return 1; ret->len = 0; return 0; } static int appendBinaryString(BinaryString *ret, char c) { if (ret->len >= ret->allocatedLen) { char *newdata; ret->allocatedLen *= 2; newdata = realloc(ret->data, ret->allocatedLen); if (newdata == NULL) { free(ret->data); return 1; } ret->data = newdata; } ret->data[ret->len++] = c; return 0; } static int hexval(char c) { if (isdigit(c)) return c - '0'; if ('a' <= c && c <= 'f') return c - 'a' + 10; if ('A' <= c && c <= 'F') return c - 'A' + 10; return -1; } static int getBinaryString(BinaryString *ret, char *path, char stop, long *lenReturn) { long i; createBinaryString(ret); for (i = 0; path[i] != stop && path[i] != '\0'; i++) { char c; if (path[i] == '%') { int v; v = hexval(path[i + 1]); if (v < 0) { free(ret->data); return 1; } c = v << 4; v = hexval(path[i + 2]); if (v < 0) { free(ret->data); return 1; } c |= v; i += 2; } else c = path[i]; appendBinaryString(ret, c); } appendBinaryString(ret, '\0'); *lenReturn = i; return 0; } static int processPath(Connection *conn, char *path) { long len; if (getBinaryString(&conn->path, path, '?', &len)) return 1; path += len; while (path[0] != '\0') { if (conn->pathFieldCount >= conn->allocatedPathFields) { PathField *newPathFields; conn->allocatedPathFields *= 2; newPathFields = realloc(conn->pathFields, conn->allocatedPathFields * sizeof(PathField)); if (newPathFields == NULL) goto error; conn->pathFields = newPathFields; } path++; if (getBinaryString( &conn->pathFields[conn->pathFieldCount].var, path, '=', &len)) goto error; path += len; if (path[0] == '\0') { free(conn->pathFields[conn->pathFieldCount].var.data); goto error; } path++; if (getBinaryString( &conn->pathFields[conn->pathFieldCount].value, path, '&', &len)) { free(conn->pathFields[conn->pathFieldCount].var.data); goto error; } conn->pathFieldCount++; path += len; } return 0; error: { long i; for (i = 0; i < conn->fieldCount; i++) { free(conn->pathFields[i].var.data); free(conn->pathFields[i].value.data); } free(conn->pathFields); free(conn->path.data); return 1; } } static int processRequest(Connection *conn) { char *line; long i; line = conn->currLine; /* * line is not necessarily always conn->currentLine. It's the beginning * of the currently parsing thing. * */ for (i = 0;; i++) { if (line[i] == ' ') { line[i] = '\0'; conn->type = getType(line); if (conn->type == INVALID) return 1; line += i + 1; break; } if (line[i] == '\0') { return 1; } } for (i = 0;; i++) { if (line[i] == ' ') { line[i] = '\0'; if (processPath(conn, line)) return 1; line += i + 1; break; } if (line[i] == '\0') { return 1; } } if (strcmp(line, "HTTP/1.1")) return 1; conn->progress = RECEIVE_HEADER; conn->fieldCount = 0; return 0; } static int processField(Connection *conn) { long i; char *line, *split, *field, *value; size_t linelen; Field *newfields; if (conn->currLineLen == 0) { conn->progress = RECEIVE_BODY; for (i = 0; i < conn->fieldCount; i++) { if (strcmp(conn->fields[i].field, "Content-Length") == 0) { conn->bodylen = atol(conn->fields[i].value); goto foundlen; } } conn->bodylen = 0; conn->body = NULL; goto lendone; foundlen: conn->body = malloc(conn->bodylen); if (conn->body == NULL) return 1; lendone: conn->receivedBody = 0; return 0; } if (conn->fieldCount >= conn->allocatedFields) { conn->allocatedFields *= 2; newfields = realloc(conn->fields, conn->allocatedFields * sizeof(char *[2])); if (newfields == NULL) return 1; conn->fields = newfields; } line = conn->currLine; split = strstr(line, ": "); linelen = conn->currLineLen; if (split == NULL) return 1; field = malloc(split - line + 1); if (field == NULL) return 1; memcpy(field, line, split - line); field[split - line] = '\0'; linelen -= split - line + 2; line += split - line + 2; value = malloc(linelen + 1); if (value == NULL) { free(field); return 1; } memcpy(value, line, linelen + 1); conn->fields[conn->fieldCount].field = field; conn->fields[conn->fieldCount].value = value; conn->fieldCount++; return 0; } static int processChar(Connection *conn, char c, Sitefile *site) { if (conn->progress < RECEIVE_BODY) { if (conn->currLineLen >= conn->currLineAlloc) { char *newline; conn->currLineAlloc *= 2; newline = realloc(conn->currLine, conn->currLineAlloc); if (newline == NULL) return 1; conn->currLine = newline; } if (c == '\n') { if (--conn->currLineLen < 0) return 1; if (conn->currLine[conn->currLineLen] != '\r') return 1; conn->currLine[conn->currLineLen] = '\0'; if (conn->progress == RECEIVE_REQUEST) { if (processRequest(conn)) return 1; } else if (conn->progress == RECEIVE_HEADER) { if (processField(conn)) return 1; } conn->currLineLen = 0; } else conn->currLine[conn->currLineLen++] = c; } else if (conn->progress == RECEIVE_BODY) { if (conn->receivedBody < conn->bodylen) conn->body[conn->receivedBody++] = c; } if (conn->progress == RECEIVE_BODY && conn->receivedBody >= conn->bodylen) if (sendResponse(conn, site)) return 1; return 0; } static long diff(struct timespec *t1, struct timespec *t2) { /* returns the difference in times in milliseconds */ return (t2->tv_sec - t1->tv_sec) * 1000 + (t2->tv_nsec - t1->tv_nsec) / 1000000; } int updateConnection(Connection *conn, Sitefile *site) { size_t totalReceived = 0; for (;;) { char buff[300]; ssize_t received; unsigned long i; struct timespec currentTime; const Port *port = site->ports + conn->portind; if (clock_gettime(CLOCK_MONOTONIC, ¤tTime) < 0) { createLog("Error getting current time"); return 1; } if (port->timeout > 0 && diff(&conn->lastdata, ¤tTime) > port->timeout) { createLog("Connection timed out"); return 1; } createFormatLog("Attempting to receive %ld bytes", sizeof buff); received = recvStream(conn->stream, buff, sizeof buff); createFormatLog("Received %ld bytes", received); if (received < 0) return errno != EAGAIN && totalReceived <= 0; if (received == 0) return 1; totalReceived += received; memcpy(&conn->lastdata, ¤tTime, sizeof(struct timespec)); for (i = 0; i < received; i++) { if (processChar(conn, buff[i], site)) return 1; } } }