From d104a43d1b2d7c09355e7d3ed9c5db8e5d297556 Mon Sep 17 00:00:00 2001 From: Lorenz Stechauner Date: Wed, 9 Dec 2020 19:21:57 +0100 Subject: [PATCH] Refactored for version 4 --- src/URI.cpp | 196 -------- src/URI.h | 51 --- src/client.c | 25 ++ src/client.cpp | 675 ---------------------------- src/necronda-server.c | 18 + src/necronda-server.cpp | 284 ------------ src/necronda-server.h | 41 -- src/net/http.c | 8 + src/net/http.h | 11 + src/network/Address.cpp | 68 --- src/network/Address.h | 31 -- src/network/Socket.cpp | 661 --------------------------- src/network/Socket.h | 174 ------- src/network/http/Http.cpp | 32 -- src/network/http/Http.h | 22 - src/network/http/HttpConnection.cpp | 198 -------- src/network/http/HttpConnection.h | 57 --- src/network/http/HttpHeader.cpp | 122 ----- src/network/http/HttpHeader.h | 53 --- src/network/http/HttpRequest.cpp | 107 ----- src/network/http/HttpRequest.h | 52 --- src/network/http/HttpResponse.cpp | 70 --- src/network/http/HttpResponse.h | 50 --- src/network/http/HttpStatusCode.cpp | 66 --- src/network/http/HttpStatusCode.h | 15 - src/procopen.cpp | 41 -- src/procopen.h | 41 -- src/utils.c | 8 + src/utils.h | 11 + 29 files changed, 81 insertions(+), 3107 deletions(-) delete mode 100644 src/URI.cpp delete mode 100644 src/URI.h create mode 100644 src/client.c delete mode 100644 src/client.cpp create mode 100644 src/necronda-server.c delete mode 100644 src/necronda-server.cpp delete mode 100644 src/necronda-server.h create mode 100644 src/net/http.c create mode 100644 src/net/http.h delete mode 100644 src/network/Address.cpp delete mode 100644 src/network/Address.h delete mode 100644 src/network/Socket.cpp delete mode 100644 src/network/Socket.h delete mode 100644 src/network/http/Http.cpp delete mode 100644 src/network/http/Http.h delete mode 100644 src/network/http/HttpConnection.cpp delete mode 100644 src/network/http/HttpConnection.h delete mode 100644 src/network/http/HttpHeader.cpp delete mode 100644 src/network/http/HttpHeader.h delete mode 100644 src/network/http/HttpRequest.cpp delete mode 100644 src/network/http/HttpRequest.h delete mode 100644 src/network/http/HttpResponse.cpp delete mode 100644 src/network/http/HttpResponse.h delete mode 100644 src/network/http/HttpStatusCode.cpp delete mode 100644 src/network/http/HttpStatusCode.h delete mode 100644 src/procopen.cpp delete mode 100644 src/procopen.h create mode 100644 src/utils.c create mode 100644 src/utils.h diff --git a/src/URI.cpp b/src/URI.cpp deleted file mode 100644 index db4d7e7..0000000 --- a/src/URI.cpp +++ /dev/null @@ -1,196 +0,0 @@ - -#include "URI.h" -#include "necronda-server.h" -#include -#include - -using namespace std; - -string getExtension(string path) { - long pos = path.find_last_of('.'); - if (pos == string::npos) { - return ""; - } - return path.substr(pos + 1, path.length() - pos); -} - -string getFilename(string path) { - long pos = path.find_last_of('/'); - if (pos == string::npos) { - return ""; - } - return path.substr(pos + 1, path.length() - pos); -} - -bool isDirectory(string path) { - struct stat statbuf; - return stat(path.c_str(), &statbuf) == 0 && S_ISDIR(statbuf.st_mode) != 0; -} - -bool isFile(string path) { - struct stat statbuf; - return stat(path.c_str(), &statbuf) == 0 && S_ISDIR(statbuf.st_mode) == 0; -} - -bool fileExists(string path) { - struct stat statbuf; - return stat(path.c_str(), &statbuf) == 0; -} - -URI::URI() = default; - -URI::URI(string webroot, string reqpath) { - unsigned long pos = reqpath.find('?'); - if (pos != string::npos) { - queryinit = true; - query = reqpath.substr(pos + 1, reqpath.length() - pos); - reqpath.erase(pos, reqpath.length() - pos); - } else { - query = ""; - queryinit = false; - } - if (webroot.length() >= 1 && webroot[webroot.length() - 1] == '/') { - webroot.erase(webroot.length() - 1); - } - reqpath = url_decode(reqpath); - if (reqpath.find("/../") != string::npos) { - throw (char *) "Invalid path"; - } - if (reqpath[0] != '/') { - reqpath = '/' + reqpath; - } - this->webroot = webroot; - this->reqpath = reqpath; - - info = ""; - relpath = reqpath; - - while ((!fileExists(webroot + relpath) || (isDirectory(webroot + relpath) && !fileExists(webroot + relpath + "/index.php"))) - && (!fileExists(webroot + relpath + ".php") || (isDirectory(webroot + relpath + ".php") && !fileExists(webroot + relpath + ".php/index.php"))) - && (!fileExists(webroot + relpath + ".html") || (isDirectory(webroot + relpath + ".html") && !fileExists(webroot + relpath + ".html/index.php")))) { - long slash = relpath.find_last_of('/'); - if (slash == string::npos || relpath == "/") { - break; - } - info = relpath.substr(slash) + info; - relpath.erase(slash); - } - - if (!info.empty() && isDirectory(webroot + relpath)) { - relpath.append("/"); - } - - string abs = relpath; - if (fileExists(webroot + abs)) { - string ext = getExtension(abs); - if (ext == "php" || ext == "html") { - abs.erase(abs.length() - ext.length() - 1, abs.length()); - } - } - - string fname = getFilename(abs); - if (fname == "index") { - abs.erase(abs.length() - fname.length() - 1, abs.length()); - } - - this->filepath = webroot + relpath; - - if (isDirectory(webroot + abs)) { - if (abs[abs.length() - 1] != '/') { - abs += "/"; - } - this->relpath = abs; - abs += "index"; - if (fileExists(webroot + abs + ".php")) { - this->filepath = webroot + abs + ".php"; - } else if (fileExists(webroot + abs + ".html")) { - this->filepath = webroot + abs + ".html"; - } - } else { - if (abs[abs.length() - 1] == '/') { - abs.erase(abs.length() - 1, abs.length() - 1); - } - this->relpath = abs; - if (fileExists(webroot + abs + ".php")) { - this->filepath = webroot + abs + ".php"; - } else if (fileExists(webroot + abs + ".html")) { - this->filepath = webroot + abs + ".html"; - } - } - - if (isStatic() && !info.empty()) { - if (relpath[relpath.length() - 1] == '/') { - relpath.erase(relpath.length() - 1); - } - newpath = relpath + info; - filepath = ""; - } else if (relpath != reqpath) { - if (!info.empty() && relpath[relpath.length() - 1] == '/') { - info.erase(0,1); - } - newpath = relpath + info; - } else { - newpath = ""; - } - -} - -string URI::getWebRoot() { - return webroot; -} - -string URI::getRelativePath() { - return relpath; -} - -string URI::getAbsolutePath() { - return webroot + relpath; -} - -string URI::getFilePath() { - return filepath; -} - -string URI::getRelativeFilePath() { - string str = getFilePath(); - long len = getWebRoot().length(); - return str.substr(len, str.length() - len); -} - -string URI::getNewPath() { - if (isStatic()) { - if (hasQuery()) { - return getRelativePath(); - } - } - if (!newpath.empty() && newpath != reqpath) { - return url_encode(newpath) + (queryinit? "?" + query : ""); - } else { - return ""; - } -} - -FILE *URI::openFile() { - return fopen64(getFilePath().c_str(), "rb"); -} - -string URI::getFilePathInfo() { - return info; //getAbsolutePath().erase(getFilePath().length(), getAbsolutePath().length()); -} - -string URI::getFileType() { - return getMimeType(getFilePath()); -} - -bool URI::isStatic() { - return getExtension(filepath) != "php"; -} - -string URI::getQuery() { - return query; -} - -bool URI::hasQuery() { - return queryinit; -} - diff --git a/src/URI.h b/src/URI.h deleted file mode 100644 index 703e05a..0000000 --- a/src/URI.h +++ /dev/null @@ -1,51 +0,0 @@ - -#include - -#ifndef NECRONDA_PATH -#define NECRONDA_PATH - -using namespace std; - -class URI { -private: - string webroot; - string reqpath; - string relpath; - string query; - string info; - string filepath; - string newpath; - bool queryinit{}; - -public: - URI(); - - URI(string webroot, string reqpath); - - string getWebRoot(); - - string getRelativePath(); - - string getAbsolutePath(); - - string getFilePath(); - - string getRelativeFilePath(); - - string getNewPath(); - - FILE *openFile(); - - string getFilePathInfo(); - - string getFileType(); - - bool isStatic(); - - string getQuery(); - - bool hasQuery(); - -}; - -#endif diff --git a/src/client.c b/src/client.c new file mode 100644 index 0000000..ebbe5a1 --- /dev/null +++ b/src/client.c @@ -0,0 +1,25 @@ +/** + * Necronda Web Server + * Client connection and request handlers + * src/client.c + * Lorenz Stechauner, 2020-12-03 + */ + +#include "utils.h" +#include "net/http.h" + + +int websocket_handler() { + // TODO implement websocket_handler + return 0; +} + +int request_handler() { + // TODO implement request_handler + return 0; +} + +int connection_handler() { + // TODO implement connection_handler + return 0; +} diff --git a/src/client.cpp b/src/client.cpp deleted file mode 100644 index 1ffa092..0000000 --- a/src/client.cpp +++ /dev/null @@ -1,675 +0,0 @@ -#include - -#include - -/** - * Necronda Web Server 3.0 - * client.cpp - Client and Connection handler - * Lorenz Stechauner, 2018-05-16 - */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "network/Socket.h" -#include "network/http/HttpRequest.h" -#include "network/http/HttpConnection.h" -#include "necronda-server.h" -#include "network/http/HttpStatusCode.h" -#include "URI.h" -#include "procopen.h" -#include "network/Address.h" - - -typedef struct { - string host; - string cc; - string country; - string prov; - string provname; - string city; - string timezone; - string localdate; -} IpAddressInfo; - - -void log_to_file(const char *prefix, const string &str, string host) { - //FILE *file = fopen((getWebRoot(std::move(host)) + ".access.log").c_str(), "a"); - //fprintf(file, "%s%s\r\n", prefix, str.c_str()); - //fflush(file); - //fclose(file); -} - -void log_error_to_file(const char *prefix, const string &str, string host) { - log_to_file(prefix, "\x1B[1;31m" + str + "\x1B[0m", std::move(host)); -} - -/** - * Writes log messages to the console - * @param prefix The connection prefix - * @param str The string to be written - */ -void log(const char *prefix, const string &str) { - printf("%s%s\r\n", prefix, str.c_str()); - flush(cout); -} - -void log_error(const char *prefix, const string &str) { - log(prefix, "\x1B[1;31m" + str + "\x1B[0m"); -} - -void php_error_handler(const char *prefix, FILE *stderr) { - string line; - while (!(line = read_line(stderr)).empty()) { - log_error(prefix, line); - } - fclose(stderr); -} - -IpAddressInfo get_ip_address_info(Address* addr) { - FILE *name = popen(("/opt/ipinfo/ipinfo.py " + addr->toString()).c_str(), "r"); - char hostbuffer[1024]; - memset(hostbuffer, 0, 1024); - size_t size = fread(hostbuffer, 1, 1024, name); - istringstream buffer(hostbuffer); - string line; - - IpAddressInfo info; - int num = 0; - while (std::getline(buffer, line)) { - switch (num) { - case 0: info.host = line; break; - case 1: info.cc = line; break; - case 2: info.country = line; break; - case 3: info.prov = line; break; - case 4: info.provname = line; break; - case 5: info.city = line; break; - case 6: info.timezone = line; break; - case 7: info.localdate = line; break; - } - num++; - } - return info; -} - -string get_os_info(int fd) { - struct tcp_info ti; - socklen_t tisize = sizeof(ti); - getsockopt(fd, IPPROTO_TCP, TCP_INFO, &ti, &tisize); - - int ttl; - socklen_t ttlsize = sizeof(ttl); - getsockopt(fd, IPPROTO_IP, IP_TTL, &ttl, &ttlsize); - - return "win_size=" + to_string(ti.tcpi_rcv_space) + ", ttl=" + to_string(ttl); -} - -string getETag(string filename) { - ifstream etags = ifstream("/var/necronda/ETags"); - - ifstream a = ifstream(); - string line; - int index = 0; - int i = 0; - string timestamp = getTimestamp(filename); - long size = getFileSize(filename); - while (getline(etags, line)) { - i++; - if (line == filename) { - index = i; - break; - } - long p1 = line.find(':'); - if (p1 == string::npos) continue; - long p2 = line.find(':', (unsigned) p1 + 1); - if (p2 == string::npos) continue; - long p3 = line.find(':', (unsigned) p2 + 1); - if (p3 == string::npos) continue; - string FILENAME = line.substr(0, (unsigned) p1); - string HASH = line.substr((unsigned) p1 + 1, (unsigned) (p2 - p1)); - string TIMESTAMP = line.substr((unsigned) p2 + 1, (unsigned) (p3 - p2)); - long SIZE = strtol(line.substr((unsigned) p3 + 1, line.length() - p3).c_str(), nullptr, 10); - if (FILENAME == filename) { - index = i; - if (timestamp != TIMESTAMP || size != SIZE) { - break; - } else { - etags.close(); - return HASH; - } - } - } - etags.close(); - - MD5_CTX mdContext; - MD5_Init(&mdContext); - size_t bytes; - char buffer[4096]; - FILE *file = fopen(filename.c_str(), "rb"); - if (file == nullptr) { - throw (char *) "Invalid file"; - } - while ((bytes = fread(buffer, 1, 4096, file)) != 0) { - MD5_Update(&mdContext, buffer, bytes); - } - fclose(file); - unsigned char md[16]; - MD5_Final(md, &mdContext); - char md5buff[32]; - for (int i = 0; i < 16; i++) { - sprintf(md5buff + i * 2, "%02x", md[i]); - } - string md5 = string(md5buff); - - if (index == 0) { - char buff[256]; - sprintf(buff, "%s:%s:%s:%ld\n", filename.c_str(), md5.c_str(), timestamp.c_str(), size); - FILE *f = fopen("/var/necronda/ETags", "a"); - if (f == nullptr) { - throw (char *) strerror(errno); - } - fseek(f, 0, SEEK_END); - fwrite(buff, 1, strlen(buff), f); - fflush(f); - fclose(f); - } else { - - } - - return md5; -} - -#include -#include -#include - - -long getPosition(std::string str, char c, int occurence) { - int tempOccur = 0; - int num = 0; - for (auto it : str) { - num++; - if (it == c) { - if (++tempOccur == occurence) { - return num; - } - } - } - - return -1; -} - -int websocket_handler(Socket *socket, stds *pipes) { - fd_set readfd; - int maxfd = (socket->getFd() > pipes->stdout->_fileno) ? socket->getFd() : pipes->stdout->_fileno; - FD_ZERO(&readfd); - FD_SET(socket->getFd(), &readfd); - FD_SET(pipes->stdout->_fileno, &readfd); - - /*while (true) { - int ret = ::select(maxfd + 1, &readfd, nullptr, nullptr, nullptr); - if (ret < 0) { - throw (char *) strerror(errno); - } - - int c = fgetc(pipes->stdout); - if (c == -1) { - long rec = socket->receive(pipes->stdin); - } else { - ungetc(c, pipes->stdout); - socket->send(pipes->stdout); - } - }*/ -} - -/** - * Handles (keep-alive) HTTP connections - * @param prefix The connection prefix - * @param socket The socket - * @param id The client ID - * @param num The Connection Number in the client - * @return Should the server wait for another header? - */ -bool connection_handler(const char *preprefix, const char *col1, const char *col2, Socket *socket, long id, long num, IpAddressInfo *info) { - bool error = false; - char buffer[1024]; - char *prefix = (char *) preprefix; - - HttpConnection req; - try { - req = HttpConnection(socket); - } catch (char *msg) { - try { - if (msg == "Malformed header") { - log(prefix, "Unable to parse header: Malformed header"); - socket->send("HTTP/1.1 400 Bad Request\r\nConnection: close\r\n\r\n"); - return false; - } else if (msg == "timeout") { - log(prefix, "Timeout!"); - socket->send("HTTP/1.1 408 Request Timeout\r\nConnection: close\r\n\r\n"); - return false; - } else { - log(prefix, (string) "Unable to receive from socket: " + msg); - return false; - } - } catch (char *msg2) { - return false; - } - } - - try { - bool noRedirect, redir, invalidMethod, etag, compress, wantsWebsocket, websocket = false; - URI path; - pid_t childpid; - FILE *file; - int statuscode; - string hash, type, host; - thread *t; - long pos; - stds pipes; - - if (req.isExistingField("Connection") && req.getField("Connection") == "keep-alive") { - req.setField("Connection", "keep-alive"); - req.setField("Keep-Alive", "timeout=3600, max=100"); - } else { - req.setField("Connection", "close"); - error = true; - } - - host = ""; - - if (!req.isExistingField("Host")) { - req.respond(400); - goto respond; - } - host = req.getField("Host"); - pos = host.find(':'); - if (pos != string::npos) { - host.erase(pos, host.length() - pos); - } - - /* - FILE *name = popen(("dig @8.8.8.8 +time=1 -x " + socket->getPeerAddress()->toString() + - " | grep -oP \"^[^;].*\\t\\K([^ ]*)\\w\"").c_str(), "r"); - char hostbuffer[1024]; - memset(hostbuffer, 0, 1024); - size_t size = fread(hostbuffer, 1, 1024, name); - hostbuffer[size - 1] = 0; // remove \n - if (size <= 1) { - sprintf(hostbuffer, "%s", socket->getPeerAddress()->toString().c_str()); - } - */ - - sprintf(buffer, "[\x1B[1m%s\x1B[0m][%i]%s[%s][%i]%s ", host.c_str(), socket->getSocketPort(), col1, - info->host.c_str(), socket->getPeerPort(), col2); - prefix = buffer; - - log(prefix, "\x1B[1m" + req.getMethod() + " " + req.getPath() + "\x1B[0m"); - log_to_file(prefix, "\x1B[1m" + req.getMethod() + " " + req.getPath() + "\x1B[0m", host); - - noRedirect = req.getPath().find("/.well-known/") == 0 || (req.getPath().find("/files/") == 0); - redir = true; - if (!noRedirect) { - if (getWebRoot(host).empty()) { - req.redirect(303, "https://www.necronda.net" + req.getPath()); - } else if (socket->getSocketPort() != 443) { - req.redirect(302, "https://" + host + req.getPath()); - } else { - redir = false; - } - } else { - redir = false; - } - - path = URI(getWebRoot(host), req.getPath()); - childpid = 0; - - if (redir) { - goto respond; - } else if (!path.getNewPath().empty() && req.getMethod() != "POST") { - req.redirect(303, path.getNewPath()); - goto respond; - } - - file = path.openFile(); - if (file == nullptr) { - req.setField("Cache-Control", "public, max-age=60"); - req.respond(404); - goto respond; - } - type = path.getFileType(); - - if (type.find("inode/") == 0 || (path.getRelativeFilePath().find("/.") != string::npos && !noRedirect)) { - req.respond(403); - goto respond; - } - - req.setField("Content-Type", type); - req.setField("Last-Modified", getHttpDate(path.getFilePath())); - - invalidMethod = false; - etag = false; - - if (path.isStatic()) { - hash = getETag(path.getFilePath()); - req.setField("ETag", hash); - req.setField("Accept-Ranges", "bytes"); - if (type.find("text/") == 0) { - req.setField("Cache-Control", "public, max-age=3600"); - } else { - req.setField("Cache-Control", "public, max-age=86400"); - } - req.setField("Allow", "GET"); - if (req.getMethod() != "GET") { - invalidMethod = true; - } - if (req.isExistingField("If-None-Match") && req.getField("If-None-Match") == hash) { - etag = true; - } - } else { - req.setField("Accept-Ranges", "none"); - req.setField("Cache-Control", "private, no-cache"); - req.setField("Allow", "GET, POST, PUT"); - if (req.getMethod() != "GET" && req.getMethod() != "POST" && req.getMethod() != "PUT") { - invalidMethod = true; - } - } - - if (invalidMethod) { - req.respond(405); - goto respond; - } else if (etag) { - req.respond(304); - goto respond; - } - - statuscode = 0; - if (!path.isStatic()) { - string cmd = (string) "env -i" + - " REDIRECT_STATUS=" + cli_encode("CGI") + - " DOCUMENT_ROOT=" + cli_encode(getWebRoot(host)) + - " " + req.cgiExport() + - (req.isExistingField("Content-Length") ? " CONTENT_LENGTH=" + - cli_encode(req.getField( - "Content-Length")) - : "") + - (req.isExistingField("Content-Type") ? " CONTENT_TYPE=" + cli_encode( - req.getField("Content-Type")) : "") + - ((socket->isSecured()) ? " HTTPS=on" : "") + - " PATH_INFO=" + cli_encode(path.getFilePathInfo()) + - " PATH_TRANSLATED=" + cli_encode(path.getAbsolutePath()) + - " QUERY_STRING=" + cli_encode(path.getQuery()) + - " REMOTE_ADDR=" + cli_encode(socket->getPeerAddress()->toString()) + - " REMOTE_HOST=" + cli_encode(info->host) + - " REMOTE_PORT=" + cli_encode(to_string(socket->getPeerPort())) + - " REQUEST_METHOD=" + cli_encode(req.getMethod()) + - " REQUEST_URI=" + cli_encode(req.getPath()) + - " SCRIPT_FILENAME=" + cli_encode(path.getFilePath()) + - " SCRIPT_NAME=" + cli_encode(path.getRelativePath()) + - " SERVER_ADMIN=" + cli_encode("lorenz.stechauner@gmail.com") + - " SERVER_NAME=" + cli_encode(host) + - " SERVER_PORT=" + cli_encode(to_string(socket->getSocketPort())) + - " SERVER_SOFTWARE=" + cli_encode("Necronda 3.0") + - " SERVER_PROTOCOL=" + cli_encode("HTTP/1.1") + - " GATEWAY_INTERFACE=" + cli_encode("CGI/1.1") + - " REQUEST_TIMESTAMP=" + cli_encode(to_string(req.getMicrosStart())) + - " REMOTE_CC=" + cli_encode(info->cc) + - " /usr/bin/php-cgi"; - - pipes = procopen(cmd.c_str()); - childpid = pipes.pid; - - long len = req.isExistingField("Content-Length") - ? strtol(req.getField("Content-Length").c_str(), nullptr, 10) - : ((req.getMethod() == "POST" || req.getMethod() == "PUT") ? -1 : 0); - - socket->receive(pipes.stdin, len); - wantsWebsocket = req.getMethod() == "GET" && - req.isExistingResponseField("Connection") && req.getField("Connection") == "Upgrade" && - req.isExistingResponseField("Upgrade") && req.getField("Upgrade") == "websocket"; - if (!wantsWebsocket) { - // Close only if no Websocket upgrade is possible - fclose(pipes.stdin); - } - t = new thread(php_error_handler, prefix, pipes.stderr); - - string line; - while (!(line = read_line(pipes.stdout)).empty()) { - pos = line.find(':'); - string index = line.substr(0, pos); - string data = line.substr(pos + 1, line.length() - pos); - - while (index[0] == ' ') index.erase(index.begin() + 0); - while (index[index.length() - 1] == ' ') index.erase(index.end() - 1); - while (data[0] == ' ') data.erase(data.begin() + 0); - while (data[data.length() - 1] == ' ') data.erase(data.end() - 1); - - if (index == "Status") { - statuscode = (int) strtol(data.substr(0, 3).c_str(), nullptr, 10); - } else { - if (index == "Location" && statuscode == 0) { - statuscode = 303; - } else if (index == "Content-Type") { - type = data; - } - req.setField(index, data); - } - } - - websocket = statuscode == 101 && - req.isExistingResponseField("Connection") && req.getResponseField("Connection") == "Upgrade" && - req.isExistingResponseField("Upgrade") && req.getResponseField("Upgrade") == "websocket"; - - fclose(file); - file = pipes.stdout; - if (websocket) { - log(prefix, "Upgrade to WebSocket!"); - req.respond(statuscode); - goto respond; - } else { - int c = fgetc(pipes.stdout); - if (c == -1) { - // No Data -> Error - req.respond((statuscode == 0) ? 500 : statuscode); - goto respond; - } else { - ungetc(c, pipes.stdout); - } - } - } - - statuscode = (statuscode == 0) ? 200 : statuscode; - - compress = (type.find("text/") == 0 || - (type.find("application/") == 0 && type.find("+xml") != string::npos) || - type == "application/json" || - type == "application/javascript") && - req.isExistingField("Accept-Encoding") && - req.getField("Accept-Encoding").find("deflate") != string::npos; - - if (compress) { - req.setField("Accept-Ranges", "none"); - } - - if (compress && req.isExistingField("Range")) { - req.respond(416); - } else if (req.isExistingField("Range")) { - string range = req.getField("Range"); - if (range.find("bytes=") != 0 || !path.isStatic()) { - req.respond(416); - } else { - fseek(file, 0L, SEEK_END); - long len = ftell(file); - fseek(file, 0L, SEEK_SET); - long p = range.find('-'); - if (p == string::npos) { - req.respond(416); - } else { - string part1 = range.substr(6, (unsigned long) (p - 6)); - string part2 = range.substr((unsigned long) (p + 1), - range.length() - p - 1); - long num1 = stol(part1, nullptr, 10); - long num2 = len - 1; - if (!part2.empty()) { - num2 = stol(part2, nullptr, 10); - } - if (num1 < 0 || num1 >= len || num2 < 0 || num2 >= len) { - req.respond(416); - } else { - req.setField("Content-Range", - (string) "bytes " + to_string(num1) + "-" + - to_string(num2) + - "/" + to_string(len)); - req.respond(206, file, compress, num1, num2); - } - } - } - } else { - req.respond(statuscode, file, compress); - } - - fclose(file); - if (childpid > 0) { - waitpid(childpid, nullptr, 0); - } - - respond: - - HttpStatusCode status = req.getStatusCode(); - int code = status.code; - string color; - string comment; - if ((code >= 200 && code < 300) || code == 304 || code == 101) { - color = "\x1B[1;32m"; // Success (Cached, Switching Protocols): Green - } else if (code >= 100 && code < 200) { - color = "\x1B[1;93m"; // Continue: Yellow - } else if (code >= 300 && code < 400) { - color = "\x1B[1;93m"; // Redirect: Yellow - comment = " -> " + - (req.isExistingResponseField("Location") ? req.getResponseField("Location") : ""); - } else if (code >= 400 && code < 500) { - color = "\x1B[1;31m"; // Client Error: Red - //comment = " -> " + req.getPath(); - } else if (code >= 500 & code < 600) { - color = "\x1B[1;31m"; // Server Error: Red - //comment = " -> " + req.getPath(); - } - string msg = color + to_string(status.code) + " " + status.message + comment + " (" + formatTime(req.getDuration()) + ")\x1B[0m"; - log(prefix, msg); - if (!host.empty()) { - log_to_file(prefix, msg, host); - } - - if (websocket) { - websocket_handler(socket, &pipes); - log(prefix, "\x1B[1mClosing WebSocket (" + formatTime(req.getDuration()) + ")\x1B[0m"); - } - } catch (char *msg) { - HttpStatusCode status = req.getStatusCode(); - log(prefix, to_string(status.code) + " " + status.message + " (" + formatTime(req.getDuration()) + ")"); - try { - if (strncmp(msg, "timeout", strlen(msg)) == 0) { - log(prefix, "Timeout!"); - req.setField("Connection", "close"); - req.respond(408); - error = true; - } else if (strncmp(msg, "Invalid path", strlen(msg)) == 0) { - log(prefix, "Timeout!"); - req.setField("Connection", "close"); - req.respond(400); - } else { - log(prefix, (string) "Unable to receive from socket: " + msg); - error = true; - } - } catch (char *msg2) { - - } - } - return !error; -} - -/** - * Handles HTTP clients - * @param socket The socket - * @param id The client ID - */ -void client_handler(Socket *socket, long id, bool ssl) { - const char *prefix; - char const *col1; - char const *col2 = "\x1B[0m"; - IpAddressInfo info = get_ip_address_info(socket->getPeerAddress()); - auto os = get_os_info(socket->getFd()); - { - auto group = (int) (id % 6); - if (group == 0) { - col1 = "\x1B[0;31m"; // Red - } else if (group == 1) { - col1 = "\x1B[0;32m"; // Green - } else if (group == 2) { - col1 = "\x1B[0;34m"; // Blue - } else if (group == 3) { - col1 = "\x1B[0;33m"; // Yellow - } else if (group == 4) { - col1 = "\x1B[0;35m"; // Magenta - } else { - col1 = "\x1B[0;36m"; // Cyan - } - - string *a = new string("[" + socket->getSocketAddress()->toString() + "][" + - to_string(socket->getSocketPort()) + "]" + col1 + - "[" + info.host + "][" + to_string(socket->getPeerPort()) + - "]" + col2 + " "); - prefix = a->c_str(); - } - - log(prefix, "Connection established"); - log(prefix, string("Host: ") + info.host + " (" + socket->getPeerAddress()->toString() + ")"); - log(prefix, string("OS: ") + os); - log(prefix, string("Location: ") + info.cc + "/" + info.country + ", " + info.prov + "/" + info.provname + ", " + info.city); - log(prefix, string("Local Date: ") + info.localdate + " (" + info.timezone + ")"); - - - bool err = false; - try { - socket->setReceiveTimeout(3600000); - socket->setSendTimeout(36000000); - } catch (char *msg) { - log(prefix, (string) "Unable to set timeout on socket: " + msg); - err = true; - } - - try { - if (ssl) { - //socket->sslHandshake("/home/lorenz/Documents/Projects/Necronda-Server/necronda-server-3.0/privkey.pem", - // "/home/lorenz/Documents/Projects/Necronda-Server/necronda-server-3.0/fullchain.pem"); - socket->sslHandshake("/cert/necronda.net/privkey.pem", - "/cert/necronda.net/fullchain.pem"); - } - } catch (char *msg) { - log(prefix, (string) "Unable to perform handshake: " + msg); - err = true; - } - - long reqnum = 0; - if (!err) { - while (connection_handler(prefix, col1, col2, socket, id, ++reqnum, &info)); - reqnum--; - } - - log(prefix, - "Connection terminated (#:" + to_string(reqnum) + ", R: " + formatSize(socket->getBytesReceived()) + ", S: " + - formatSize(socket->getBytesSent()) + ", T: " + formatTime(socket->getDuration()) + ")"); - socket->close(); -} - - diff --git a/src/necronda-server.c b/src/necronda-server.c new file mode 100644 index 0000000..af3efc8 --- /dev/null +++ b/src/necronda-server.c @@ -0,0 +1,18 @@ +/** + * Necronda Web Server + * Main executable + * src/necronda-server.c + * Lorenz Stechauner, 2020-12-03 + */ + +#include + +#include "utils.c" +#include "net/http.c" +#include "client.c" + + +int main(int argc, const char* argv[]) { + printf("Necronda Web Server\n"); + // TODO implement main +} diff --git a/src/necronda-server.cpp b/src/necronda-server.cpp deleted file mode 100644 index ac2a6c3..0000000 --- a/src/necronda-server.cpp +++ /dev/null @@ -1,284 +0,0 @@ -/** - * Necronda Web Server 3.0 - * necronda-server.cpp - Main Executable - * Lorenz Stechauner, 2018-05-09 - */ - - -#include "necronda-server.h" -#include -#include -#include -#include -#include -#include -#include - - -using namespace std; - -const char* webroot = "/srv/necronda/"; - - -string getMimeType(string path) { - - unsigned long pos = path.find_last_of('.'); - string ext; - if (pos != string::npos) { - ext = path.substr(pos + 1, path.length() - pos); - } - - magic_t magic = magic_open(MAGIC_MIME_TYPE); - magic_load(magic, "/usr/share/misc/magic.mgc"); - string type = magic_file(magic, path.c_str()); - magic_setflags(magic, MAGIC_MIME_ENCODING); - string charset = magic_file(magic, path.c_str()); - - if (type == "text/plain") { - if (ext == "css") { - type = "text/css"; - } else if (ext == "js") { - type = "text/javascript"; - } - } - - magic_close(magic); - - return type + "; charset=" + charset; -} - -/** - * Sun, 06 Nov 1994 08:49:37 GMT - * @return - */ - -std::string getTimestamp(string path) { - struct stat attrib; - stat(path.c_str(), &attrib); - return getTimestamp(attrib.st_ctime); -} - -std::string getTimestamp(time_t time) { - char buffer[64]; - struct tm *timeinfo = gmtime(&time); - strftime(buffer, sizeof(buffer), "%Y%m%d%H%M%S", timeinfo); - return string(buffer); -} - -long getFileSize(string filename) { - struct stat stat_buf; - int rc = stat(filename.c_str(), &stat_buf); - return rc == 0 ? stat_buf.st_size : -1; -} - - -/** - * Returns a formatted time string - * @param micros Delta time to be formatted - * @return A formatted time string - */ -std::string formatTime(long micros) { - char buffer[64]; - if (micros < 1000) { - sprintf(buffer, "%.3f ms", micros / 1000.0); - } else if (micros < 10000) { - sprintf(buffer, "%.2f ms", micros / 1000.0); - } else if (micros < 100000) { - sprintf(buffer, "%.1f ms", micros / 1000.0); - } else if (micros < 1000000) { - sprintf(buffer, "%.0f ms", micros / 1000.0); - } else { - sprintf(buffer, "%.1f s", micros / 1000000.0); - } - return std::string(buffer); -} - -std::string formatSize(unsigned long bytes) { - char buffer[64]; - if (bytes > 0x10000000000) { - sprintf(buffer, "%.1f TiB", (double) bytes / 0x10000000000); - } else if (bytes > 0x40000000) { - sprintf(buffer, "%.1f GiB", (double) bytes / 0x40000000); - } else if (bytes > 0x100000) { - sprintf(buffer, "%.1f MiB", (double) bytes / 0x100000); - } else if (bytes > 0x400) { - sprintf(buffer, "%.1f KiB", (double) bytes / 0x400); - } else { - sprintf(buffer, "%ld B", bytes); - } - return std::string(buffer); -} - - - - -string url_decode(string url) { - long pos = 0; - while ((pos = url.find('+', pos + 1)) != string::npos) { - url.replace(pos, 1, 1, ' '); - } - pos = 0; - while ((pos = url.find('%', pos + 1)) != string::npos) { - const char *num = url.substr(pos + 1, 2).c_str(); - auto c = (char) strtol(num, nullptr, 16); - url.erase(pos, 3); - url.insert(pos, 1, c); - } - - return url; -} - -string url_encode(string url) { - char buff[4]; - for (long pos = 0; pos < url.length(); pos++) { - auto c = (unsigned char) url[pos]; - if (c < ' ' || c > '~' || c == ' ' || c == '#' || c == '?' || c == '&' || c == '=' || c == '\\' || c == '%') { - sprintf(buff, "%%%02X", c); - url.replace(pos, 1, buff); - } - } - return url; -} - -string html_decode(string text) { - return text; -} - -string html_encode(string text) { - return text; -} - -string cli_encode(string text) { - char buff[5]; - for (long pos = 0; pos < text.length(); pos++) { - auto c = (unsigned char) text[pos]; - if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == ',' || c == '.' || c == '_' || c == '+' || c == ':' || c == '@' || c == '%' || c == '/' || c == '-')) { - sprintf(buff, "\\%.1s", &c); - text.replace(pos, 1, buff); - pos++; - } - } - return text; -} - -string read_line(FILE* file) { - char *line; - size_t len = 0; - ssize_t read; - if ((read = getline(&line, &len, file)) < 0 || line == nullptr) { - return ""; - } - string l = string(line); - if (l[l.length()-1] == '\n') { - l.erase(l.length()-1); - } - if (l[l.length()-1] == '\r') { - l.erase(l.length()-1); - } - return l; -} - - -#include "procopen.cpp" -#include "network/Address.cpp" -#include "network/Socket.cpp" -#include "URI.cpp" -#include "network/http/Http.cpp" -#include "network/http/HttpStatusCode.cpp" -#include "network/http/HttpHeader.cpp" -#include "network/http/HttpRequest.cpp" -#include "network/http/HttpResponse.cpp" -#include "network/http/HttpConnection.cpp" - -string getWebRoot(string host) { - string root = webroot + host; - if (fileExists(root)) { - return root; - } else { - return ""; - } -} - - -#include "client.cpp" - - -long clientnum = 0; - -int main() { - cout << "Necronda Server 3.0" << endl << "by Lorenz Stechauner" << endl << endl; - - signal(SIGPIPE, SIG_IGN); - - SSL_load_error_strings(); - SSL_library_init(); - ERR_load_crypto_strings(); - OpenSSL_add_all_algorithms(); - - int ret = system("mkdir -p /var/necronda /etc/necronda /tmp/necronda; touch /var/necronda/ETags"); - - if (ret != 0) { - cout << "Unable to create server files" << endl; - exit(1); - } - - list ports = {80, 443}; - - list servers = {}; - auto it = ports.begin(); - - for (int i = 0; i < ports.size(); i++) { - unsigned short port = *it; - advance(it, 1); - Socket server = Socket(); - servers.push_back(server); - - try { - server.setReuseAddress(true); - server.setReceiveTimeout(0); - server.setSendTimeout(0); - } catch (char *msg) { - cout << "Unable to set socket option: " << msg << endl; - exit(2); - } - - try { - server.bind(port); - } catch (char *msg) { - cout << "Unable to bind socket to port " << port << ": " << msg << endl; - exit(3); - } - - try { - server.listen(256); - } catch (char *msg) { - cout << "Unable to listen on socket: " << msg << endl; - exit(4); - } - - } - - cout << "Ready for connections" << endl; - - while (true) { - try { - Socket::select(servers, {}); - for (Socket server : servers) { - try { - Socket *socket = server.accept(); - clientnum++; - thread *t = new thread(client_handler, socket, clientnum, server.getSocketPort() == 443); - } catch (char *msg) { - // Nothing - } - } - } catch (char *msg) { - cout << "Select: " << msg << endl; - break; - } - } - - return 0; -} - - diff --git a/src/necronda-server.h b/src/necronda-server.h deleted file mode 100644 index 550ad9b..0000000 --- a/src/necronda-server.h +++ /dev/null @@ -1,41 +0,0 @@ -// -// Created by lorenz on 5/17/18. -// - -#include - -#ifndef NECRONDA_SERVER -#define NECRONDA_SERVER - -using namespace std; - -unsigned long getMicros(); - -string formatTime(long micros); - -string formatSize(unsigned long bytes); - -string getWebRoot(string host); - -string getMimeType(string path); - -string getTimestamp(string path); - -string getTimestamp(time_t time); - -long getFileSize(string filename); - -string url_decode(string url); - -string url_encode(string url); - -string html_decode(string text); - -string html_encode(string text); - -string cli_encode(string text); - -string read_line(FILE *file); - - -#endif diff --git a/src/net/http.c b/src/net/http.c new file mode 100644 index 0000000..7300acf --- /dev/null +++ b/src/net/http.c @@ -0,0 +1,8 @@ +/** + * Necronda Web Server + * HTTP implementation + * src/net/http.c + * Lorenz Stechauner, 2020-12-09 + */ + +#include "http.h" diff --git a/src/net/http.h b/src/net/http.h new file mode 100644 index 0000000..41efe22 --- /dev/null +++ b/src/net/http.h @@ -0,0 +1,11 @@ +/** + * Necronda Web Server + * HTTP implementation (header file) + * src/net/http.h + * Lorenz Stechauner, 2020-12-09 + */ + +#ifndef NECRONDA_SERVER_HTTP_H +#define NECRONDA_SERVER_HTTP_H + +#endif //NECRONDA_SERVER_HTTP_H diff --git a/src/network/Address.cpp b/src/network/Address.cpp deleted file mode 100644 index f95384a..0000000 --- a/src/network/Address.cpp +++ /dev/null @@ -1,68 +0,0 @@ - - -#include -#include -#include -#include -#include "Address.h" - -using namespace std; - - -Address::Address() { - -} - -Address::Address(string addr) { - // TODO -} - -Address::Address(struct sockaddr_in *addr) { - address = ntohl(addr->sin_addr.s_addr); -} - - -struct sockaddr_in Address::toStruct(unsigned short port)const { - struct sockaddr_in addr; - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = htonl(address); - addr.sin_port = htons(port); - return addr; -} - - - -string Address::toString() const { - struct sockaddr_in addr = toStruct(0); - struct in_addr ipAddr = addr.sin_addr; - return inet_ntoa(ipAddr); -} - -bool Address::isLocal() { - string a = toString(); - return a.find("127.0.0.") == 0; -} - - -ostream& operator<<(ostream &str, const Address &addr) { - return str << addr.toString(); -} - -string operator+(string &str, const Address &addr) { - return str + addr.toString(); -} - -string operator+(string &str, const Address *addr) { - return str + addr->toString(); -} - -string operator+(const Address &addr, string &str) { - return addr.toString() + str; -} - -string operator+(const Address *addr, string &str) { - return addr->toString() + str; -} - - - diff --git a/src/network/Address.h b/src/network/Address.h deleted file mode 100644 index 66a66a4..0000000 --- a/src/network/Address.h +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Necronda Web Server 3.0 - * HttpHeader.h - HttpHeader Class definition - * Lorenz Stechauner, 2018-05-09 - */ - -#ifndef NECRONDA_ADDRESS -#define NECRONDA_ADDRESS - -using namespace std; - -class Address { -private: - unsigned int address; - -public: - Address(); - - explicit Address(string address); - - explicit Address(struct sockaddr_in *address); - - struct sockaddr_in toStruct(unsigned short port) const; - - string toString() const; - - bool isLocal(); - -}; - -#endif diff --git a/src/network/Socket.cpp b/src/network/Socket.cpp deleted file mode 100644 index 05abdf4..0000000 --- a/src/network/Socket.cpp +++ /dev/null @@ -1,661 +0,0 @@ -/** - * Necronda Web Server 3.0 - * Socket.cpp - Socket Class methods - * Lorenz Stechauner, 2018-05-09 - */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "Address.h" -#include "Socket.h" -#include "http/Http.h" - -using namespace std; - - -static void multi_ssl_init() { - SSL_load_error_strings(); - SSL_library_init(); - ERR_load_crypto_strings(); - OpenSSL_add_all_algorithms(); -} - - -char *multi_ssl_get_error(SSL *ssl, int ret) { - if (ret > 0) { - return nullptr; - } - - unsigned long ret2 = ERR_get_error(); - const char *err2 = strerror(errno); - const char *err1 = ERR_reason_error_string(ret2); - - switch (SSL_get_error(ssl, ret)) { - case SSL_ERROR_NONE: - return (char *) "none"; - case SSL_ERROR_ZERO_RETURN: - return (char *) "closed"; - case SSL_ERROR_WANT_READ: - return (char *) "want_read"; - case SSL_ERROR_WANT_WRITE: - return (char *) "want_write"; - case SSL_ERROR_WANT_CONNECT: - return (char *) "want_connect"; - case SSL_ERROR_WANT_ACCEPT: - return (char *) "want_accept"; - case SSL_ERROR_WANT_X509_LOOKUP: - return (char *) "want_x509_lookup"; - case SSL_ERROR_SYSCALL: - return (char *) ((ret2 == 0) ? (ret == 0) ? "protocol violation" : err2 : err1); - case SSL_ERROR_SSL: - return (char *) err1; - default: - return (char *) "unknown error"; - } -} - -char *strerror_socket(int nr) { - if (nr == EAGAIN || nr == EWOULDBLOCK) { - return (char *) "timeout"; - } else if (nr == ECONNRESET) { - return (char *) "closed"; - } else { - return strerror(nr); - } -} - - -Socket::Socket(int fd) { - this->fd = fd; - microsStart = getMicros(); - microsLast = microsStart; - bytesSent = 0; - bytesReceived = 0; - enc = false; - ssl = nullptr; - ctx = nullptr; - clients = false; - servers = false; -} - -Socket::Socket() { - fd = ::socket(AF_INET, SOCK_STREAM, 0); - if (fd == 0) { - throw strerror(errno); - } - enc = false; - microsStart = getMicros(); - microsLast = microsStart; - bytesSent = 0; - bytesReceived = 0; - ssl = nullptr; - ctx = nullptr; - clients = false; - servers = false; -} - -int Socket::getFd() { - return fd; -} - -void Socket::setSocketOption(int option, bool value = true) { - int val = value ? 1 : 0; - - if (::setsockopt(fd, SOL_SOCKET, option, &val, sizeof(val)) != 0) { - throw strerror(errno); - } -} - -void Socket::bind(Address *address, unsigned short port) { - struct sockaddr_in addr; - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = INADDR_ANY; // address. - addr.sin_port = htons(port); - - if (::bind(fd, (struct sockaddr *) &addr, sizeof(addr)) != 0) { - throw strerror(errno); - } -} - -void Socket::bind(unsigned short port) { - struct sockaddr_in addr; - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = INADDR_ANY; - addr.sin_port = htons(port); - - if (::bind(fd, (struct sockaddr *) &addr, sizeof(addr)) != 0) { - throw strerror(errno); - } -} - -void Socket::listen(int num) { - if (::listen(fd, num) != 0) { - throw strerror(errno); - } -} - -void Socket::connect(Address, unsigned short) { - -} - -Socket* Socket::accept() { - int newfd = ::accept(fd, nullptr, nullptr); - if (newfd < 0) { - throw strerror(errno); - } - Socket *socket = new Socket(newfd); - socket->servers = true; - return socket; -} - -void Socket::close() { - if (isSecured()) { - //SSL_shutdown(ssl); - SSL_free(ssl); - SSL_CTX_free(ctx); - } - - if (::close(fd) != 0) { - throw strerror(errno); - } -} - -Address *Socket::getPeerAddress() const { - struct sockaddr_storage addr; - socklen_t len = sizeof(addr); - getpeername(fd, (struct sockaddr *) &addr, &len); - struct sockaddr_in *s = (struct sockaddr_in *) &addr; - return new Address(s); -} - -unsigned short Socket::getPeerPort() const { - struct sockaddr_storage addr; - socklen_t len = sizeof(addr); - getpeername(fd, (struct sockaddr *) &addr, &len); - return ntohs(((struct sockaddr_in *) &addr)->sin_port); -} - -Address *Socket::getSocketAddress() const { - struct sockaddr_storage addr; - socklen_t len = sizeof(addr); - getsockname(fd, (struct sockaddr *) &addr, &len); - struct sockaddr_in *s = (struct sockaddr_in *) &addr; - return new Address(s); -} - -unsigned short Socket::getSocketPort() const { - struct sockaddr_storage addr; - socklen_t len = sizeof(addr); - getsockname(fd, (struct sockaddr *) &addr, &len); - return ntohs(((struct sockaddr_in *) &addr)->sin_port); -} - - -void Socket::setReuseAddress(bool value) { - setSocketOption(SO_REUSEADDR, value); -} - -void Socket::setReusePort(bool value) { - setSocketOption(SO_REUSEPORT, value); -} - - -string Socket::toString() const { - return "{[Socket]" + getSocketAddress()->toString() + ":" + to_string(getSocketPort()) + "<->" + - getPeerAddress()->toString() + ":" + to_string(getPeerPort()) + "}"; -} - -long Socket::send(string *str) { - return send(str->c_str(), str->length()); -} - -long Socket::send(string str) { - return send(str.c_str(), str.length()); -} - -long Socket::send(const char *str, long length) { - return send((void*) str, length); -} - -long Socket::send(const char *str) { - return send(str, strlen(str)); -} - -long Socket::send(FILE *file) { - char buffer[CPPNET_CHUNK]; - long all_len = 0; - long len = 0; - do { - len = fread(buffer, 1, CPPNET_CHUNK, file); - send(buffer, len); - all_len += len; - } while (len > 0 && len == CPPNET_CHUNK); - return all_len; -} - -Socket::~Socket() { - -} - -long Socket::receive(void *buffer, int size) { - long len; - if (isSecured()) { - len = SSL_read(ssl, buffer, size); - if (len < 0) { - throw multi_ssl_get_error(ssl, (int) len); - } - } else { - len = recv(fd, buffer, (size_t) size, 0); - if (len < 0) { - throw strerror_socket(errno); - } - } - bytesReceived += len; - return len; -} - -long Socket::peek(void *buffer, int size) { - long len; - if (isSecured()) { - len = SSL_peek(ssl, buffer, size); - if (len < 0) { - throw multi_ssl_get_error(ssl, (int) len); - } - } else { - len = recv(fd, buffer, (size_t) size, MSG_PEEK); - if (len < 0) { - throw strerror_socket(errno); - } - } - return len; -} - -long Socket::send(void *buffer, int size) { - long len; - if (isSecured()) { - if (size != 0) { - len = SSL_write(ssl, buffer, size); - if (len <= 0) { - throw multi_ssl_get_error(ssl, (int) len); - } - } else { - len = 0; - } - } else { - len = ::send(fd, buffer, (size_t) size, 0); - if (len < 0) { - throw strerror_socket(errno); - } - } - bytesSent += len; - return len; -} - - -string Socket::receive() { - string *str = new string(); - - char buffer[CPPNET_CHUNK]; - long len = 0; - do { - len = receive((void*) buffer, CPPNET_CHUNK); - str->append(buffer, (unsigned) len); - } while (len > 0 && len == CPPNET_CHUNK); - - return *str; -} - -string Socket::receive(long length) { - string *str = new string(); - - char buffer[CPPNET_CHUNK]; - long len = 0; - long reclen = 0; - do { - len = receive((void*) buffer, CPPNET_CHUNK); - reclen += len; - str->append(buffer, (unsigned) len); - } while (reclen < length); - - return *str; -} - -string Socket::receive(string until) { - string *str = new string(); - - struct pollfd ufds[1]; - ufds[0].fd = fd; - ufds[0].events = POLLIN | POLLOUT; - - char buffer[CPPNET_CHUNK]; - long len = 0; - do { - len = peek((void*) buffer, CPPNET_CHUNK); - if (len != 0) { - string s = string(buffer, (size_t) len); - size_t found = s.find(until); - long l = (found != string::npos) ? found + 1 : len; - long l2 = (found != string::npos) ? found : len; - str->append(buffer, (unsigned) l2); - receive((void *) buffer, (int) l); - if (found != string::npos) { - break; - } - } - if (poll(ufds, 1, 0) < 0) { - throw strerror_socket(errno); - } else if ((ufds[0].revents & POLLIN) == 0) { - if ((ufds[0].revents & POLLOUT) != 0) { - throw (char *) "error"; - } else { - throw (char *) "want_write"; - } - } else if ((ufds[0].revents & POLLERR) != 0) { - throw (char *) "error"; - } else if (ufds[0].revents & (POLLRDHUP | POLLHUP | POLLNVAL) != 0) { - throw (char *) "closed"; - } - } while (true); - - return *str; -} - -string Socket::receive(const char *until) { - return receive(until, (int) (strlen(until))); -} - -string Socket::receive(const char *until, unsigned long strlen) { - return receive(string(until, strlen)); -} - -long Socket::receive(FILE *file) { - char buffer[CPPNET_CHUNK]; - long len; - long rec = 0; - do { - len = receive((void*) buffer, CPPNET_CHUNK); - fwrite(buffer, 1, CPPNET_CHUNK, file); - rec += len; - } while (len > 0 && len == CPPNET_CHUNK); - return len; -} - -long Socket::receive(FILE *file, long size) { - char buffer[CPPNET_CHUNK]; - long len = 0; - long rec = 0; - while (size > rec) { - len = receive((void*) buffer, (CPPNET_CHUNK > (size - rec) && size >= 0)?(size - rec):CPPNET_CHUNK); - fwrite(buffer, 1, len, file); - rec += len; - } - return rec; -} - -string Socket::receiveLine() { - string str = receive("\n"); - if (str.length() > 0 && str.at(str.length() - 1) == '\r') { - str = str.substr(0, str.length() - 1); - } - return str; -} - - -long Socket::getDuration() { - return getMicros() - microsStart; -} - - -void Socket::setReceiveTimeout(unsigned long ms) { - struct timeval timeout; - if (ms == 0) { - timeout.tv_sec = 0; - timeout.tv_usec = 1; - } else { - timeout.tv_sec = ms / 1000; - timeout.tv_usec = (ms % 1000) * 1000; - } - if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char *) &timeout, sizeof(timeout)) < 0) { - throw strerror(errno); - } -} - -void Socket::setSendTimeout(unsigned long ms) { - struct timeval timeout; - if (ms == 0) { - timeout.tv_sec = 0; - timeout.tv_usec = 1; - } else { - timeout.tv_sec = ms / 1000; - timeout.tv_usec = (ms % 1000) * 1000; - } - if (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (char *) &timeout, sizeof(timeout)) < 0) { - throw strerror(errno); - } -} - -bool Socket::isServerSide() { - return servers; -} - -bool Socket::isSecured() { - return enc; -} - -bool Socket::isClientSide() { - return clients; -} - -void Socket::sslHandshake(map sni) { - /*if (isSecured()) { - throw (char *) "Socket already secured"; - } - - const SSL_METHOD *method; - if (isServerSide()) { - method = TLSv1_2_server_method(); - } else if (isClientSide()) { - method = TLSv1_2_client_method(); - } else { - method = TLSv1_2_method(); - } - - SSL_CTX *ctx = SSL_CTX_new(method); - SSL_CTX_set_options(ctx, SSL_OP_SINGLE_DH_USE | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3); - SSL_CTX_set_mode(ctx, SSL_MODE_ENABLE_PARTIAL_WRITE); - SSL_CTX_set_cipher_list(ctx, "HIGH:!aNULL:!kRSA:!PSK:!SRP:!MD5:!RC4"); - SSL_CTX_set_ecdh_auto(ctx, 1); - - const char *certfile = keypair.fullchain.c_str(); - const char *keyfile = keypair.privkey.c_str(); - - if (isServerSide()) { - if (SSL_CTX_use_certificate_file(ctx, certfile, SSL_FILETYPE_PEM) != 1) { - throw (char *) ERR_reason_error_string(ERR_get_error()); - } - - if (SSL_CTX_use_PrivateKey_file(ctx, keyfile, SSL_FILETYPE_PEM) != 1) { - throw (char *) ERR_reason_error_string(ERR_get_error()); - } - } - - SSL_CTX_set_tlsext_servername_callback - - this->ctx = ctx; - this->ssl = SSL_new(ctx); - SSL_set_fd(ssl, fd); - enc = true; - - while (true) { - int ret = 0; - if (isServerSide()) { - ret = SSL_accept(ssl); - } else if (isClientSide()) { - ret = SSL_connect(ssl); - } else { - ret = SSL_do_handshake(ssl); - } - - if (ret <= 0 && ((isServerSide() && SSL_get_error(ssl, ret) != SSL_ERROR_WANT_READ) || - (isClientSide() && SSL_get_error(ssl, ret) != SSL_ERROR_WANT_WRITE))) { - throw multi_ssl_get_error(ssl, ret); - } else if (ret == 1) { - break; - } - }*/ - -} - -void Socket::sslHandshake() { - sslHandshake(KeyPair{"", ""}); -} - -void Socket::sslHandshake(KeyPair keypair) { - if (isSecured()) { - throw (char *) "Socket already secured"; - } - - const SSL_METHOD *method; - if (isServerSide()) { - method = TLSv1_2_server_method(); - } else if (isClientSide()) { - method = TLSv1_2_client_method(); - } else { - method = TLSv1_2_method(); - } - - SSL_CTX *ctx = SSL_CTX_new(method); - SSL_CTX_set_options(ctx, SSL_OP_SINGLE_DH_USE); - SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, nullptr); - SSL_CTX_set_min_proto_version(ctx, SSL3_VERSION); // TLS1_VERSION - SSL_CTX_set_mode(ctx, SSL_MODE_ENABLE_PARTIAL_WRITE); - SSL_CTX_set_cipher_list(ctx, "HIGH:!aNULL:!kRSA:!PSK:!SRP:!MD5:!RC4"); - SSL_CTX_set_ecdh_auto(ctx, 1); - - const char *certfile = keypair.fullchain.c_str(); - const char *keyfile = keypair.privkey.c_str(); - - if (isServerSide()) { - if (SSL_CTX_use_certificate_chain_file(ctx, certfile) != 1) { - throw (char *) ERR_reason_error_string(ERR_get_error()); - } - - if (SSL_CTX_use_PrivateKey_file(ctx, keyfile, SSL_FILETYPE_PEM) != 1) { - throw (char *) ERR_reason_error_string(ERR_get_error()); - } - } - - this->ctx = ctx; - this->ssl = SSL_new(ctx); - SSL_set_fd(ssl, fd); - enc = true; - - while (true) { - int ret = 0; - if (isServerSide()) { - ret = SSL_accept(ssl); - } else if (isClientSide()) { - ret = SSL_connect(ssl); - } else { - ret = SSL_do_handshake(ssl); - } - - if (ret <= 0 && ((isServerSide() && SSL_get_error(ssl, ret) != SSL_ERROR_WANT_READ) || - (isClientSide() && SSL_get_error(ssl, ret) != SSL_ERROR_WANT_WRITE))) { - throw multi_ssl_get_error(ssl, ret); - } else if (ret == 1) { - break; - } - } - -} - -void Socket::sslHandshake(string privkey, string fullchain) { - sslHandshake(KeyPair{std::move(privkey), std::move(fullchain)}); -} - -long Socket::select(list read, list write, long millis) { - fd_set readfd, writefd; - int maxfd = 0; - FD_ZERO(&readfd); - FD_ZERO(&writefd); - - for (Socket s : read) { - if (s.fd > maxfd) { - maxfd = s.fd; - } - FD_SET(s.fd, &readfd); - } - - for (Socket s : write) { - if (s.fd > maxfd) { - maxfd = s.fd; - } - FD_SET(s.fd, &writefd); - } - - struct timeval *tv = new struct timeval; - if (millis < 0) { - tv = nullptr; - } else if (millis == 0) { - tv->tv_sec = 0; - tv->tv_usec = 1; - } else { - tv->tv_sec = millis / 1000; - tv->tv_usec = (millis % 1000) * 1000; - } - - int ret = ::select(maxfd + 1, &readfd, &writefd, nullptr, tv); - if (ret < 0) { - throw (char *) strerror(errno); - } - return ret; -} - -long Socket::select(list read, list write) { - Socket::select(std::move(read), std::move(write), -1); -} - -unsigned long Socket::getBytesSent() { - return bytesSent; -} - -unsigned long Socket::getBytesReceived() { - return bytesReceived; -} - - -ostream &operator<<(ostream &str, const Socket &socket) { - return str << socket.toString(); -} - -ostream &operator<<(ostream &str, const Socket *socket) { - return str << socket->toString(); -} - -string operator+(string &str, const Socket &socket) { - return str + socket.toString(); -} - -string operator+(const Socket &socket, string &str) { - return socket.toString() + str; -} - - - - diff --git a/src/network/Socket.h b/src/network/Socket.h deleted file mode 100644 index 80cf50a..0000000 --- a/src/network/Socket.h +++ /dev/null @@ -1,174 +0,0 @@ -/** - * Necronda Web Server 3.0 - * Socket.h - Socket Class definition - * Lorenz Stechauner, 2018-05-09 - */ - -#ifndef NECRONDA_SOCKET -#define NECRONDA_SOCKET - -#include - -#define CPPNET_CHUNK 16384 - -typedef struct { - string privkey; - string fullchain; -} KeyPair; - -using namespace std; - - -class Socket { -private: - int fd; - SSL *ssl; - SSL_CTX *ctx; - bool enc; - bool servers; - bool clients; - unsigned long bytesSent; - unsigned long bytesReceived; - long microsStart; - long microsLast; - - void setSocketOption(int, bool); - - long send(void *buffer, int size); - - long receive(void *buffer, int size); - - long peek(void *buffer, int size); - -public: - Socket(); - - explicit Socket(int filedescriptor); - - ~Socket(); - - void bind(Address *address, unsigned short port); - - void bind(unsigned short port); - - void listen(int count = 1); - - void connect(Address address, unsigned short port); - - Socket* accept(); - - void sslHandshake(); - - void sslHandshake(map sni); - - void sslHandshake(KeyPair keypair); - - void sslHandshake(string privkey, string fullchain); - - long send(string *str); - - long send(string str); - - long send(const char *str); - - long send(const char *str, long length); - - long send(FILE *file); - - string receive(); - - string receive(long length); - - string receive(string until); - - string receive(const char *until, unsigned long strlen); - - string receive(const char *until); - - long receive(FILE *file); - - string receiveLine(); - - void shutdown(); - - void close(); - - int getFd(); - - long getDuration(); - - Address *getSocketAddress() const; - - unsigned short getSocketPort() const; - - Address *getPeerAddress() const; - - unsigned short getPeerPort() const; - - string toString() const; - - - bool isServerSide(); - - bool isClientSide(); - - bool isSecured(); - - - void setReuseAddress(bool value = true); - - void setReusePort(bool value = true); - - void setSendBufferSize(int value); - - void setReceiveBufferSize(int value); - - void setMinReceiveBytes(int value); - - void setMinSendBytes(int value); - - void setSendTimeout(unsigned long ms); - - void setReceiveTimeout(unsigned long ms); - - - bool getReuseAddress(); - - bool getReusePort(); - - int getSendBufferSize(); - - int getReceiveBufferSize(); - - int getMinReceiveBytes(); - - int getMinSendBytes(); - - long getSendTimeout(); - - long getReceiveTimeout(); - - unsigned long getBytesSent(); - - unsigned long getBytesReceived(); - - static long select(list read, list write, long millis); - - static long select(list read, list write); - - long receive(FILE *file, long size); -}; - -Socket operator<<(Socket sock, const char *str); - -Socket operator<<(Socket sock, string str); - -ostream &operator<<(ostream &str, const Socket &socket); - -ostream &operator<<(ostream &str, const Socket *socket); - -string operator+(string &str, const Socket &socket); - -string operator+(const Socket &socket, string &str); - -#endif diff --git a/src/network/http/Http.cpp b/src/network/http/Http.cpp deleted file mode 100644 index 505e260..0000000 --- a/src/network/http/Http.cpp +++ /dev/null @@ -1,32 +0,0 @@ -// -// Created by lorenz on 7/10/18. -// - -#include -#include -#include "Http.h" - -unsigned long getMicros() { - struct timeval tv; - gettimeofday(&tv, nullptr); - return (unsigned long) (1000000 * tv.tv_sec + tv.tv_usec); -} - -string getHttpDate() { - time_t rawtime; - time(&rawtime); - return getHttpDate(rawtime); -} - -string getHttpDate(string filename) { - struct stat attrib; - stat(filename.c_str(), &attrib); - return getHttpDate(attrib.st_ctime); -} - -string getHttpDate(time_t time) { - char buffer[64]; - struct tm *timeinfo = gmtime(&time); - strftime(buffer, sizeof(buffer), "%a, %d %b %Y %H:%M:%S GMT", timeinfo); - return string(buffer); -} diff --git a/src/network/http/Http.h b/src/network/http/Http.h deleted file mode 100644 index 4f5f238..0000000 --- a/src/network/http/Http.h +++ /dev/null @@ -1,22 +0,0 @@ -// -// Created by lorenz on 7/10/18. -// - -#ifndef CPPNET_HTTP_H -#define CPPNET_HTTP_H - -#include -#include - -using namespace std; - -unsigned long getMicros(); - -string getHttpDate(time_t time); - -string getHttpDate(); - -string getHttpDate(string filename); - - -#endif //CPPNET_HTTP_H diff --git a/src/network/http/HttpConnection.cpp b/src/network/http/HttpConnection.cpp deleted file mode 100644 index 7e6dfc0..0000000 --- a/src/network/http/HttpConnection.cpp +++ /dev/null @@ -1,198 +0,0 @@ - - -#include -#include -#include -#include -#include "HttpConnection.h" -#include "../Socket.h" -#include "HttpStatusCode.h" -#include "Http.h" - - -HttpConnection::HttpConnection() = default; - -HttpConnection::HttpConnection(Socket *socket) { - this->socket = socket; - this->request = new HttpRequest(socket); - this->response = new HttpResponse(); - microsStart = getMicros(); - response->setVersion("1.1"); - response->setField("Server", "Necronda/3.0"); -} - -void HttpConnection::respond(int statuscode) { - if (statuscode >= 400 && statuscode < 600) { - respond(statuscode, - "" + to_string(statuscode) + " " + - ::getStatusCode(statuscode).message + - "

" + to_string(statuscode) + " " + - ::getStatusCode(statuscode).message + - "

" + - ((request->isExistingField("Host")) ? - (request->isExistingField("Referer") && - request->getField("Referer").find(request->getField("Host")) != string::npos) ? - "

Go back to the last page you visited: getField("Referer") + "\">" + - request->getField("Referer") + "

" : - "

Go back to the home page of getField("Host") + "/\">" + - request->getField("Host") + - "

" : "") + "
\r\n" - ); - } else { - respond(statuscode, ""); - } -} - -void HttpConnection::respond(int statuscode, string payload) { - response->setStatusCode(statuscode); - response->setField("Date", getHttpDate()); - response->setField("Content-Length", to_string(payload.length())); - response->sendHeader(socket); - socket->send(std::move(payload)); -} - -void HttpConnection::respond(int statuscode, FILE *file, bool compress, long start, long end) { - response->setStatusCode(statuscode); - response->setField("Transfer-Encoding", "chunked"); - response->setField("Date", getHttpDate()); - - long shouldTransfer; - long transfered = 0; - - fseek(file, 0, SEEK_END); - long len = ftell(file); - - if (start != -1 && end != -1) { - fseek(file, start, SEEK_SET); - response->setField("Content-Length", to_string(end - start + 1)); - shouldTransfer = end - start + 1; - compress = false; - } else { - fseek(file, 0, SEEK_SET); - shouldTransfer = len; - if (len >= 0 && !compress) { - response->setField("Content-Length", to_string(len)); - } - } - - if (compress) { - response->setField("Content-Encoding", "deflate"); - } - - response->sendHeader(socket); - - if (compress) { - int level = 1; - int ret, flush; - unsigned have; - z_stream strm; - unsigned char in[CPPNET_CHUNK]; - unsigned char out[CPPNET_CHUNK]; - - strm.zalloc = Z_NULL; - strm.zfree = Z_NULL; - strm.opaque = Z_NULL; - ret = deflateInit(&strm, level); - if (ret != Z_OK) { - throw (char *) "Unable to open file"; - } - - do { - strm.avail_in = (uInt) fread(in, 1, CPPNET_CHUNK, file); - - if (ferror(file)) { - (void) deflateEnd(&strm); - throw (char *) strerror(errno); - } - flush = feof(file) ? Z_FINISH : Z_NO_FLUSH; - strm.next_in = in; - do { - strm.avail_out = CPPNET_CHUNK; - strm.next_out = out; - ret = deflate(&strm, flush); - assert(ret != Z_STREAM_ERROR); - have = CPPNET_CHUNK - strm.avail_out; - - if (have != 0) { - char buffer[64]; - sprintf(buffer, "%X\r\n", have); - socket->send(buffer); - socket->send((const char *) out, have); - socket->send("\r\n"); - } - } while (strm.avail_out == 0); - assert(strm.avail_in == 0); - } while (flush != Z_FINISH); - assert(ret == Z_STREAM_END); - socket->send("0\r\n\r\n"); - deflateEnd(&strm); - } else { - char buffer[CPPNET_CHUNK]; - char buff[64]; - while (true) { - unsigned long size = fread(buffer, 1, (size_t) ((CPPNET_CHUNK > (shouldTransfer - transfered) && shouldTransfer > 0) ? (shouldTransfer - transfered) : CPPNET_CHUNK), file); - transfered += size; - sprintf(buff, "%lX\r\n", size); - socket->send(buff); - socket->send((const char *) buffer, size); - socket->send("\r\n"); - if (size == 0) { - break; - } - } - } -} - -string HttpConnection::getField(string index) { - return request->getField(std::move(index)); -} - -string HttpConnection::getPath() { - return request->getPath(); -} - -void HttpConnection::setField(string index, string data) { - response->setField(std::move(index), std::move(data)); -} - -bool HttpConnection::isExistingField(string index) { - return request->isExistingField(std::move(index)); -} - -string HttpConnection::getMethod() { - return request->getMethod(); -} - -unsigned long HttpConnection::getDuration() { - return getMicros() - microsStart; -} - -unsigned long HttpConnection::getMicrosStart() { - return microsStart; -} - -HttpStatusCode HttpConnection::getStatusCode() { - return response->getStatusCode(); -} - -void HttpConnection::redirect(int statuscode, string location) { - setField("Location", std::move(location)); - respond(statuscode, ""); -} - -string HttpConnection::getResponseField(string index) { - return response->getField(std::move(index)); -} - -bool HttpConnection::isExistingResponseField(string index) { - return response->isExistingField(std::move(index)); -} - -string HttpConnection::cgiExport() { - return request->cgiExport(); -} - -void HttpConnection::removeField(string index) { - response->removeField(std::move(index)); -} diff --git a/src/network/http/HttpConnection.h b/src/network/http/HttpConnection.h deleted file mode 100644 index 7b94d01..0000000 --- a/src/network/http/HttpConnection.h +++ /dev/null @@ -1,57 +0,0 @@ - - -#ifndef NECRONDA_HTTP_CONNECTION -#define NECRONDA_HTTP_CONNECTION - - -#include "../Socket.h" -#include "HttpResponse.h" -#include "HttpRequest.h" - - -class HttpConnection { -private: - Socket *socket{}; - HttpRequest *request{}; - HttpResponse *response{}; - unsigned long microsStart{}; - -public: - explicit HttpConnection(); - - explicit HttpConnection(Socket *socket); - - void respond(int statuscode); - - void respond(int statuscode, string payload); - - void respond(int statuscode, FILE *file, bool compress = false, long start = -1, long end = -1); - - void redirect(int statuscode, string location); - - bool isExistingField(string index); - - bool isExistingResponseField(string index); - - string getField(string index); - - string getResponseField(string index); - - string getPath(); - - string getMethod(); - - void setField(string index, string data); - - unsigned long getDuration(); - - unsigned long getMicrosStart(); - - HttpStatusCode getStatusCode(); - - string cgiExport(); - - void removeField(string index); -}; - -#endif diff --git a/src/network/http/HttpHeader.cpp b/src/network/http/HttpHeader.cpp deleted file mode 100644 index 410e46c..0000000 --- a/src/network/http/HttpHeader.cpp +++ /dev/null @@ -1,122 +0,0 @@ -/** - * Necronda Web Server 3.0 - * HttpHeader.cpp - HttpHeader Class methods - * Lorenz Stechauner, 2018-05-09 - */ - - -#include -#include -#include "../Socket.h" - -#include "HttpHeader.h" - - -using namespace std; - -string to_cgi(string text) { - for (auto & c: text) c = (char) toupper(c); - long pos = 0; - while ((pos = text.find('-', pos + 1)) != string::npos) { - text.replace(pos, 1, 1, '_'); - } - return text; -} - - -/** - * Default Constructor - */ -HttpHeader::HttpHeader() { - fields = fields; -} - -HttpHeader::HttpHeader(Socket *socket) : HttpHeader::HttpHeader() { - parse(socket); -} - - -void HttpHeader::parse(Socket *socket) { - while (true) { - string line = socket->receiveLine(); - if (line.length() == 0) { - break; - } else { - unsigned long pos = line.find(':'); - if (pos == string::npos) { - throw (char *) "Malformed header"; - } - string index = line.substr(0, pos); - string data = line.substr(pos + 1, line.length() - pos); - while (index[0] == ' ') index.erase(index.begin() + 0); - while (index[index.length() - 1] == ' ') index.erase(index.end() - 1); - while (data[0] == ' ') data.erase(data.begin() + 0); - while (data[data.length() - 1] == ' ') data.erase(data.end() - 1); - setField(index, data); - } - } -} - - -/** - * Default Destructor - */ -HttpHeader::~HttpHeader() { - fields.clear(); -} - - -/** - * Sets a field in the HTTP header - * e.g. Content-Length: 42 - * @param index The field index - * @param data The field data - */ -void HttpHeader::setField(string index, string data) { - removeField(index); - fields.insert(make_pair(index, data)); -} - -void HttpHeader::removeField(string index) { - fields.erase(index); -} - -/** - * Gets a field from the HTTP header - * e.g. Content-Length: 42 - * @param index The field index - * @return The field data - */ -string HttpHeader::getField(string index) { - auto i = fields.find(index); - if (i != fields.end()) { - return fields.at(index); - } else { - return ""; - } -} - - -bool HttpHeader::isExistingField(string index) { - auto i = fields.find(index); - return i != fields.end(); -} - -string HttpHeader::toString() { - string header = ""; - for (auto it = fields.begin(); it != fields.end(); it++ ) { - header += it->first + ": " + it->second + "\r\n"; - } - return header; -} - -string HttpHeader::cgiExport() { - string header = ""; - for (auto it = fields.begin(); it != fields.end(); it++ ) { - header += "HTTP_" + to_cgi(it->first) + "=" + cli_encode(it->second) + " "; - } - return header; -} - - - diff --git a/src/network/http/HttpHeader.h b/src/network/http/HttpHeader.h deleted file mode 100644 index a6c6116..0000000 --- a/src/network/http/HttpHeader.h +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Necronda Web Server 3.0 - * HttpHeader.h - HttpHeader Class definition - * Lorenz Stechauner, 2018-05-09 - */ - -#ifndef NECRONDA_HTTP_HEADER -#define NECRONDA_HTTP_HEADER - -#include - -using namespace std; - -struct comp { - bool operator()(const std::string& lhs, const std::string& rhs) const { - return strcasecmp(lhs.c_str(), rhs.c_str()) < 0; - } -}; - -/** - * Stores Key-Value Pairs for a HTTP header - * e.g. - * Content-Length: 64 - * Host: example.org - */ -class HttpHeader { -private: - map fields; - -public: - HttpHeader(); - - explicit HttpHeader(Socket *socket); - - ~HttpHeader(); - - void setField(string index, string data); - - string getField(string index); - - void removeField(string index); - - bool isExistingField(string index); - - void parse(Socket *socket); - - string toString(); - - string cgiExport(); - -}; - -#endif diff --git a/src/network/http/HttpRequest.cpp b/src/network/http/HttpRequest.cpp deleted file mode 100644 index 955d359..0000000 --- a/src/network/http/HttpRequest.cpp +++ /dev/null @@ -1,107 +0,0 @@ - - -#include -#include -#include -#include "../Socket.h" -#include "HttpHeader.h" -#include "HttpRequest.h" - - -HttpRequest::HttpRequest() { - this->header = HttpHeader(); -} - -HttpRequest::HttpRequest(Socket *socket) : HttpRequest::HttpRequest() { - parseHeader(socket); -} - -HttpRequest::HttpRequest(string method, string path, string version) : HttpRequest::HttpRequest() { - this->method = std::move(method); - this->path = std::move(path); - this->version = std::move(version); -} - -void HttpRequest::parseHeader(Socket *socket) { - string line = socket->receiveLine(); - - unsigned long pos1 = line.find(' '); - unsigned long pos2; - - bool invalid = false; - - if (pos1 != string::npos) { - pos2 = line.find(' ', pos1 + 1); - if (pos2 != string::npos) { - method = line.substr(0, pos1); - for (auto &c: method) c = (char) toupper(c); - path = line.substr(pos1 + 1, pos2 - pos1 - 1); - version = line.substr(pos2 + 6, 3); - } else { - invalid = true; - } - } else { - pos2 = string::npos; - invalid = true; - } - - - if (!invalid && (line.substr(pos2 + 1, 5) != "HTTP/" || version[1] != '.' || path[0] != '/' || !(version[0] >= '0' && version[0] <= '9') || !(version[2] >= '0' && version[2] <= '9'))) { - invalid = true; - } - - if (invalid) { - method = ""; - path = ""; - version = ""; - throw (char *) "Malformed header"; - } - - header.parse(socket); -} - -string HttpRequest::getMethod() { - return method; -} - -string HttpRequest::getPath() { - return path; -} - -string HttpRequest::getVersion() { - return version; -} - -void HttpRequest::setMethod(string method) { - this->method = std::move(method); -} - -void HttpRequest::setPath(string path) { - this->path = std::move(path); -} - -void HttpRequest::setVersion(string version) { - this->version = std::move(version); -} - -string HttpRequest::getField(string index) { - return header.getField(std::move(index)); -} - -void HttpRequest::setField(string index, string data) { - header.setField(std::move(index), std::move(data)); -} - -bool HttpRequest::isExistingField(string index) { - return header.isExistingField(std::move(index)); -} - -string HttpRequest::cgiExport() { - return header.cgiExport(); -} - - - - - - diff --git a/src/network/http/HttpRequest.h b/src/network/http/HttpRequest.h deleted file mode 100644 index aa0c8f2..0000000 --- a/src/network/http/HttpRequest.h +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Necronda Web Server 3.0 - * HttpHeader.h - HttpHeader Class definition - * Lorenz Stechauner, 2018-05-09 - */ - -#ifndef NECRONDA_HTTP_REQUEST -#define NECRONDA_HTTP_REQUEST - -using namespace std; - -class HttpRequest { -private: - HttpHeader header; - string method; - string path; - string version; - -public: - HttpRequest(); - - explicit HttpRequest(Socket *socket); - - HttpRequest(string method, string path, string version = "1.1"); - - void parseHeader(Socket *socket); - - void sendHeader(Socket *socket); - - string getField(string index); - - void setField(string index, string data); - - bool isExistingField(string index); - - string getMethod(); - - string getPath(); - - string getVersion(); - - void setMethod(string method); - - void setPath(string path); - - void setVersion(string version); - - string cgiExport(); - -}; - -#endif diff --git a/src/network/http/HttpResponse.cpp b/src/network/http/HttpResponse.cpp deleted file mode 100644 index e07aa2d..0000000 --- a/src/network/http/HttpResponse.cpp +++ /dev/null @@ -1,70 +0,0 @@ -// -// Created by lorenz on 5/17/18. -// - -#include "HttpResponse.h" -#include -#include -#include "HttpStatusCode.h" - - -HttpResponse::HttpResponse() { - this->header = HttpHeader(); -} - -HttpResponse::HttpResponse(Socket *socket) : HttpResponse::HttpResponse() { - this->parseHeader(socket); -} - -HttpResponse::HttpResponse(int statuscode, string version) : HttpResponse::HttpResponse(::getStatusCode(statuscode), std::move(version)) { -} - -HttpResponse::HttpResponse(HttpStatusCode statuscode, string version) : HttpResponse::HttpResponse() { - this->statuscode = statuscode; - this->version = std::move(version); -} - -void HttpResponse::sendHeader(Socket *socket) { - socket->send("HTTP/" + version + " " + to_string(statuscode.code) + " " + statuscode.message + "\r\n" + - header.toString() + "\r\n"); -} - -string HttpResponse::getField(string index) { - return header.getField(std::move(index)); -} - -void HttpResponse::setField(string index, string data) { - header.setField(std::move(index), std::move(data)); -} - -bool HttpResponse::isExistingField(string index) { - return header.isExistingField(std::move(index)); -} - -HttpStatusCode HttpResponse::getStatusCode() { - return statuscode; -} - -string HttpResponse::getVersion() { - return version; -} - -void HttpResponse::setStatusCode(HttpStatusCode statuscode) { - this->statuscode = statuscode; -} - -void HttpResponse::setStatusCode(int statuscode) { - this->statuscode = ::getStatusCode(statuscode); -} - -void HttpResponse::setVersion(string version) { - this->version = std::move(version); -} - -void HttpResponse::parseHeader(Socket *socket) { - -} - -void HttpResponse::removeField(string index) { - header.removeField(std::move(index)); -} diff --git a/src/network/http/HttpResponse.h b/src/network/http/HttpResponse.h deleted file mode 100644 index d254724..0000000 --- a/src/network/http/HttpResponse.h +++ /dev/null @@ -1,50 +0,0 @@ - - -#ifndef NECRONDA_HTTP_RESPONSE -#define NECRONDA_HTTP_RESPONSE - - -#include -#include "HttpHeader.h" -#include "HttpStatusCode.h" -#include "../Socket.h" - -class HttpResponse { -private: - HttpHeader header; - HttpStatusCode statuscode; - string version; - -public: - HttpResponse(); - - explicit HttpResponse(Socket *socket); - - explicit HttpResponse(int statuscode, string version = "1.1"); - - explicit HttpResponse(HttpStatusCode statuscode, string version = "1.1"); - - void parseHeader(Socket *socket); - - void sendHeader(Socket *socket); - - string getField(string index); - - void setField(string index, string data); - - bool isExistingField(string index); - - HttpStatusCode getStatusCode(); - - string getVersion(); - - void setStatusCode(HttpStatusCode statuscode); - - void setStatusCode(int statuscode); - - void setVersion(string version); - - void removeField(string index); -}; - -#endif diff --git a/src/network/http/HttpStatusCode.cpp b/src/network/http/HttpStatusCode.cpp deleted file mode 100644 index 461b842..0000000 --- a/src/network/http/HttpStatusCode.cpp +++ /dev/null @@ -1,66 +0,0 @@ -#include "HttpStatusCode.h" - -/** - * Necronda Web Server 3.0 - * HttpStatusCode.cpp - HTTP Status Code definition - * Lorenz Stechauner, 2018-05-16 - * Reference: https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html - */ - - -HttpStatusCode httpStatusCodes[] = { - HttpStatusCode{100, "Informational", "Continue", ""}, - HttpStatusCode{101, "Informational", "Switching Protocols", ""}, - - HttpStatusCode{200, "Success", "OK", ""}, - HttpStatusCode{201, "Success", "Created", ""}, - HttpStatusCode{202, "Success", "Accepted", ""}, - HttpStatusCode{203, "Success", "Non-Authoritative Information", ""}, - HttpStatusCode{204, "Success", "No Content", ""}, - HttpStatusCode{205, "Success", "Reset Content", ""}, - HttpStatusCode{206, "Success", "Partial Content", ""}, - - HttpStatusCode{300, "Redirection", "Multiple Choices", ""}, - HttpStatusCode{301, "Redirection", "Moved Permanently", ""}, - HttpStatusCode{302, "Redirection", "Found", ""}, - HttpStatusCode{303, "Redirection", "See Other", ""}, - HttpStatusCode{304, "Redirection", "Not Modified", ""}, - HttpStatusCode{305, "Redirection", "Use Proxy", ""}, - HttpStatusCode{307, "Redirection", "Temporary Redirect", ""}, - HttpStatusCode{308, "Redirection", "Permanent Redirect", ""}, - - HttpStatusCode{400, "Client Error", "Bad Request", ""}, - HttpStatusCode{401, "Client Error", "Unauthorized", ""}, - HttpStatusCode{402, "Client Error", "Payment Required", ""}, - HttpStatusCode{403, "Client Error", "Forbidden", ""}, - HttpStatusCode{404, "Client Error", "Not Found", ""}, - HttpStatusCode{405, "Client Error", "Method Not Allowed", ""}, - HttpStatusCode{406, "Client Error", "Not Acceptable", ""}, - HttpStatusCode{407, "Client Error", "Proxy Authentication Required", ""}, - HttpStatusCode{408, "Client Error", "Request Timeout", ""}, - HttpStatusCode{409, "Client Error", "Conflict", ""}, - HttpStatusCode{410, "Client Error", "Gone", ""}, - HttpStatusCode{411, "Client Error", "Length Required", ""}, - HttpStatusCode{412, "Client Error", "Precondition Failed", ""}, - HttpStatusCode{413, "Client Error", "Request Entity Too Large", ""}, - HttpStatusCode{414, "Client Error", "Request-URI Too Long", ""}, - HttpStatusCode{415, "Client Error", "Unsupported Media Type", ""}, - HttpStatusCode{416, "Client Error", "Requested Range Not Satisfiable", ""}, - HttpStatusCode{417, "Client Error", "Expectation Failed", ""}, - - HttpStatusCode{500, "Server Error", "Internal Server Error", ""}, - HttpStatusCode{501, "Server Error", "Not Implemented", ""}, - HttpStatusCode{502, "Server Error", "Bad Gateway", ""}, - HttpStatusCode{503, "Server Error", "Service Unavailable", ""}, - HttpStatusCode{504, "Server Error", "Gateway Timeout", ""}, - HttpStatusCode{505, "Server Error", "HTTP Version Not Supported", ""}, -}; - -HttpStatusCode getStatusCode(int statuscode) { - for (HttpStatusCode sc : httpStatusCodes) { - if (sc.code == statuscode) { - return sc; - } - } - throw (char *) "Invalid status code"; -} diff --git a/src/network/http/HttpStatusCode.h b/src/network/http/HttpStatusCode.h deleted file mode 100644 index f04b3c3..0000000 --- a/src/network/http/HttpStatusCode.h +++ /dev/null @@ -1,15 +0,0 @@ - - -#ifndef NECRONDA_HTTP_STATUSCODE -#define NECRONDA_HTTP_STATUSCODE - -typedef struct { - short code; // The status code (e.g. 200) - const char *type; // The status type type (e.g Success) - const char *message; // The status code message (e.g. OK) - const char *description; // The status code description (currently not used) -} HttpStatusCode; - -HttpStatusCode getStatusCode(int statuscode); - -#endif \ No newline at end of file diff --git a/src/procopen.cpp b/src/procopen.cpp deleted file mode 100644 index 551d689..0000000 --- a/src/procopen.cpp +++ /dev/null @@ -1,41 +0,0 @@ -// -// Created by lorenz on 5/30/18. -// - -#include "procopen.h" - -stds procopen(const char* command) { - - int pipes[3][2]; - - pipe(pipes[PARENT_READ_PIPE]); - pipe(pipes[PARENT_WRITE_PIPE]); - pipe(pipes[PARENT_ERROR_PIPE]); - - int pid = fork(); - - if(pid == 0) { - dup2(CHILD_READ_FD, STDIN_FILENO); - dup2(CHILD_WRITE_FD, STDOUT_FILENO); - dup2(CHILD_ERROR_FD, STDERR_FILENO); - - close(CHILD_READ_FD); - close(CHILD_WRITE_FD); - close(CHILD_ERROR_FD); - - close(PARENT_READ_FD); - close(PARENT_WRITE_FD); - close(PARENT_ERROR_FD); - - system(command); - exit(0); - } else { - close(CHILD_READ_FD); - close(CHILD_WRITE_FD); - close(CHILD_ERROR_FD); - - return stds{fdopen(PARENT_WRITE_FD, "w"), fdopen(PARENT_READ_FD, "r"), fdopen(PARENT_ERROR_FD, "r"), (pid_t) pid}; - } -} - - diff --git a/src/procopen.h b/src/procopen.h deleted file mode 100644 index 1ec622e..0000000 --- a/src/procopen.h +++ /dev/null @@ -1,41 +0,0 @@ -// -// Created by lorenz on 5/30/18. -// - - -#include -#include -#include - -#ifndef NECRONDA_PROCOPEN -#define NECRONDA_PROCOPEN - - -#define PARENT_WRITE_PIPE 0 -#define PARENT_READ_PIPE 1 -#define PARENT_ERROR_PIPE 2 - -#define READ_FD 0 -#define WRITE_FD 1 - -#define PARENT_READ_FD ( pipes[PARENT_READ_PIPE][READ_FD] ) -#define PARENT_WRITE_FD ( pipes[PARENT_WRITE_PIPE][WRITE_FD] ) -#define PARENT_ERROR_FD ( pipes[PARENT_ERROR_PIPE][READ_FD] ) - -#define CHILD_READ_FD ( pipes[PARENT_WRITE_PIPE][READ_FD] ) -#define CHILD_WRITE_FD ( pipes[PARENT_READ_PIPE][WRITE_FD] ) -#define CHILD_ERROR_FD ( pipes[PARENT_ERROR_PIPE][WRITE_FD] ) - - -typedef struct { - FILE* stdin; - FILE* stdout; - FILE* stderr; - pid_t pid; -} stds; - - -stds procopen(const char* command); - - -#endif diff --git a/src/utils.c b/src/utils.c new file mode 100644 index 0000000..4e917db --- /dev/null +++ b/src/utils.c @@ -0,0 +1,8 @@ +/** + * Necronda Web Server + * Utilities + * src/utils.c + * Lorenz Stechauner, 2020-12-03 + */ + +#include "utils.h" diff --git a/src/utils.h b/src/utils.h new file mode 100644 index 0000000..febf08c --- /dev/null +++ b/src/utils.h @@ -0,0 +1,11 @@ +/** + * Necronda Web Server + * Utilities (header file) + * src/utils.h + * Lorenz Stechauner, 2020-12-03 + */ + +#ifndef NECRONDA_SERVER_UTILS_H +#define NECRONDA_SERVER_UTILS_H + +#endif //NECRONDA_SERVER_UTILS_H