Replaced tabs with spaces

This commit is contained in:
2020-06-17 11:54:09 +02:00
parent e4a3383b1f
commit 8a7b381d63
21 changed files with 1617 additions and 1617 deletions

View File

@ -7,188 +7,188 @@
using namespace std; using namespace std;
string getExtension(string path) { string getExtension(string path) {
long pos = path.find_last_of('.'); long pos = path.find_last_of('.');
if (pos == string::npos) { if (pos == string::npos) {
return ""; return "";
} }
return path.substr(pos + 1, path.length() - pos); return path.substr(pos + 1, path.length() - pos);
} }
string getFilename(string path) { string getFilename(string path) {
long pos = path.find_last_of('/'); long pos = path.find_last_of('/');
if (pos == string::npos) { if (pos == string::npos) {
return ""; return "";
} }
return path.substr(pos + 1, path.length() - pos); return path.substr(pos + 1, path.length() - pos);
} }
bool isDirectory(string path) { bool isDirectory(string path) {
struct stat statbuf; struct stat statbuf;
return stat(path.c_str(), &statbuf) == 0 && S_ISDIR(statbuf.st_mode) != 0; return stat(path.c_str(), &statbuf) == 0 && S_ISDIR(statbuf.st_mode) != 0;
} }
bool isFile(string path) { bool isFile(string path) {
struct stat statbuf; struct stat statbuf;
return stat(path.c_str(), &statbuf) == 0 && S_ISDIR(statbuf.st_mode) == 0; return stat(path.c_str(), &statbuf) == 0 && S_ISDIR(statbuf.st_mode) == 0;
} }
bool fileExists(string path) { bool fileExists(string path) {
struct stat statbuf; struct stat statbuf;
return stat(path.c_str(), &statbuf) == 0; return stat(path.c_str(), &statbuf) == 0;
} }
URI::URI(string webroot, string reqpath) { URI::URI(string webroot, string reqpath) {
unsigned long pos = reqpath.find('?'); unsigned long pos = reqpath.find('?');
if (pos != string::npos) { if (pos != string::npos) {
queryinit = true; queryinit = true;
query = reqpath.substr(pos + 1, reqpath.length() - pos); query = reqpath.substr(pos + 1, reqpath.length() - pos);
reqpath.erase(pos, reqpath.length() - pos); reqpath.erase(pos, reqpath.length() - pos);
} else { } else {
query = ""; query = "";
queryinit = false; queryinit = false;
} }
if (webroot.length() >= 1 && webroot[webroot.length() - 1] == '/') { if (webroot.length() >= 1 && webroot[webroot.length() - 1] == '/') {
webroot.erase(webroot.length() - 1); webroot.erase(webroot.length() - 1);
} }
reqpath = url_decode(reqpath); reqpath = url_decode(reqpath);
if (reqpath.find("/../") != string::npos) { if (reqpath.find("/../") != string::npos) {
throw (char *) "Invalid path"; throw (char *) "Invalid path";
} }
if (reqpath[0] != '/') { if (reqpath[0] != '/') {
reqpath = '/' + reqpath; reqpath = '/' + reqpath;
} }
this->webroot = webroot; this->webroot = webroot;
this->reqpath = reqpath; this->reqpath = reqpath;
info = ""; info = "";
relpath = reqpath; relpath = reqpath;
while ((!fileExists(webroot + relpath) || (isDirectory(webroot + relpath) && !fileExists(webroot + relpath + "/index.php"))) 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 + ".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")))) { && (!fileExists(webroot + relpath + ".html") || (isDirectory(webroot + relpath + ".html") && !fileExists(webroot + relpath + ".html/index.php")))) {
long slash = relpath.find_last_of('/'); long slash = relpath.find_last_of('/');
if (slash == string::npos || relpath == "/") { if (slash == string::npos || relpath == "/") {
break; break;
} }
info = relpath.substr(slash) + info; info = relpath.substr(slash) + info;
relpath.erase(slash); relpath.erase(slash);
} }
if (!info.empty() && isDirectory(webroot + relpath)) { if (!info.empty() && isDirectory(webroot + relpath)) {
relpath.append("/"); relpath.append("/");
} }
string abs = relpath; string abs = relpath;
if (fileExists(webroot + abs)) { if (fileExists(webroot + abs)) {
string ext = getExtension(abs); string ext = getExtension(abs);
if (ext == "php" || ext == "html") { if (ext == "php" || ext == "html") {
abs.erase(abs.length() - ext.length() - 1, abs.length()); abs.erase(abs.length() - ext.length() - 1, abs.length());
} }
} }
string fname = getFilename(abs); string fname = getFilename(abs);
if (fname == "index") { if (fname == "index") {
abs.erase(abs.length() - fname.length() - 1, abs.length()); abs.erase(abs.length() - fname.length() - 1, abs.length());
} }
this->filepath = webroot + relpath; this->filepath = webroot + relpath;
if (isDirectory(webroot + abs)) { if (isDirectory(webroot + abs)) {
if (abs[abs.length() - 1] != '/') { if (abs[abs.length() - 1] != '/') {
abs += "/"; abs += "/";
} }
this->relpath = abs; this->relpath = abs;
abs += "index"; abs += "index";
if (fileExists(webroot + abs + ".php")) { if (fileExists(webroot + abs + ".php")) {
this->filepath = webroot + abs + ".php"; this->filepath = webroot + abs + ".php";
} else if (fileExists(webroot + abs + ".html")) { } else if (fileExists(webroot + abs + ".html")) {
this->filepath = webroot + abs + ".html"; this->filepath = webroot + abs + ".html";
} }
} else { } else {
if (abs[abs.length() - 1] == '/') { if (abs[abs.length() - 1] == '/') {
abs.erase(abs.length() - 1, abs.length() - 1); abs.erase(abs.length() - 1, abs.length() - 1);
} }
this->relpath = abs; this->relpath = abs;
if (fileExists(webroot + abs + ".php")) { if (fileExists(webroot + abs + ".php")) {
this->filepath = webroot + abs + ".php"; this->filepath = webroot + abs + ".php";
} else if (fileExists(webroot + abs + ".html")) { } else if (fileExists(webroot + abs + ".html")) {
this->filepath = webroot + abs + ".html"; this->filepath = webroot + abs + ".html";
} }
} }
if (isStatic() && !info.empty()) { if (isStatic() && !info.empty()) {
if (relpath[relpath.length() - 1] == '/') { if (relpath[relpath.length() - 1] == '/') {
relpath.erase(relpath.length() - 1); relpath.erase(relpath.length() - 1);
} }
newpath = relpath + info; newpath = relpath + info;
filepath = ""; filepath = "";
} else if (relpath != reqpath) { } else if (relpath != reqpath) {
if (!info.empty() && relpath[relpath.length() - 1] == '/') { if (!info.empty() && relpath[relpath.length() - 1] == '/') {
info.erase(0,1); info.erase(0,1);
} }
newpath = relpath + info; newpath = relpath + info;
} else { } else {
newpath = ""; newpath = "";
} }
} }
string URI::getWebRoot() { string URI::getWebRoot() {
return webroot; return webroot;
} }
string URI::getRelativePath() { string URI::getRelativePath() {
return relpath; return relpath;
} }
string URI::getAbsolutePath() { string URI::getAbsolutePath() {
return webroot + relpath; return webroot + relpath;
} }
string URI::getFilePath() { string URI::getFilePath() {
return filepath; return filepath;
} }
string URI::getRelativeFilePath() { string URI::getRelativeFilePath() {
string str = getFilePath(); string str = getFilePath();
long len = getWebRoot().length(); long len = getWebRoot().length();
return str.substr(len, str.length() - len); return str.substr(len, str.length() - len);
} }
string URI::getNewPath() { string URI::getNewPath() {
if (isStatic()) { if (isStatic()) {
if (hasQuery()) { if (hasQuery()) {
return getRelativePath(); return getRelativePath();
} }
} }
if (!newpath.empty() && newpath != reqpath) { if (!newpath.empty() && newpath != reqpath) {
return url_encode(newpath) + (queryinit? "?" + query : ""); return url_encode(newpath) + (queryinit? "?" + query : "");
} else { } else {
return ""; return "";
} }
} }
FILE *URI::openFile() { FILE *URI::openFile() {
return fopen64(getFilePath().c_str(), "rb"); return fopen64(getFilePath().c_str(), "rb");
} }
string URI::getFilePathInfo() { string URI::getFilePathInfo() {
return info; //getAbsolutePath().erase(getFilePath().length(), getAbsolutePath().length()); return info; //getAbsolutePath().erase(getFilePath().length(), getAbsolutePath().length());
} }
string URI::getFileType() { string URI::getFileType() {
return getMimeType(getFilePath()); return getMimeType(getFilePath());
} }
bool URI::isStatic() { bool URI::isStatic() {
return getExtension(filepath) != "php"; return getExtension(filepath) != "php";
} }
string URI::getQuery() { string URI::getQuery() {
return query; return query;
} }
bool URI::hasQuery() { bool URI::hasQuery() {
return queryinit; return queryinit;
} }

View File

@ -8,41 +8,41 @@ using namespace std;
class URI { class URI {
private: private:
string webroot; string webroot;
string reqpath; string reqpath;
string relpath; string relpath;
string query; string query;
string info; string info;
string filepath; string filepath;
string newpath; string newpath;
bool queryinit; bool queryinit;
public: public:
URI(string webroot, string reqpath); URI(string webroot, string reqpath);
string getWebRoot(); string getWebRoot();
string getRelativePath(); string getRelativePath();
string getAbsolutePath(); string getAbsolutePath();
string getFilePath(); string getFilePath();
string getRelativeFilePath(); string getRelativeFilePath();
string getNewPath(); string getNewPath();
FILE *openFile(); FILE *openFile();
string getFilePathInfo(); string getFilePathInfo();
string getFileType(); string getFileType();
bool isStatic(); bool isStatic();
string getQuery(); string getQuery();
bool hasQuery(); bool hasQuery();
}; };

View File

@ -58,20 +58,20 @@ void log_error_to_file(const char *prefix, const string &str, string host) {
* @param str The string to be written * @param str The string to be written
*/ */
void log(const char *prefix, const string &str) { void log(const char *prefix, const string &str) {
printf("%s%s\r\n", prefix, str.c_str()); printf("%s%s\r\n", prefix, str.c_str());
flush(cout); flush(cout);
} }
void log_error(const char *prefix, const string &str) { void log_error(const char *prefix, const string &str) {
log(prefix, "\x1B[1;31m" + str + "\x1B[0m"); log(prefix, "\x1B[1;31m" + str + "\x1B[0m");
} }
void php_error_handler(const char *prefix, FILE *stderr) { void php_error_handler(const char *prefix, FILE *stderr) {
string line; string line;
while (!(line = read_line(stderr)).empty()) { while (!(line = read_line(stderr)).empty()) {
log_error(prefix, line); log_error(prefix, line);
} }
fclose(stderr); fclose(stderr);
} }
IpAddressInfo get_ip_address_info(Address* addr) { IpAddressInfo get_ip_address_info(Address* addr) {
@ -101,78 +101,78 @@ IpAddressInfo get_ip_address_info(Address* addr) {
} }
string getETag(string filename) { string getETag(string filename) {
ifstream etags = ifstream("/var/necronda/ETags"); ifstream etags = ifstream("/var/necronda/ETags");
ifstream a = ifstream(); ifstream a = ifstream();
string line; string line;
int index = 0; int index = 0;
int i = 0; int i = 0;
string timestamp = getTimestamp(filename); string timestamp = getTimestamp(filename);
long size = getFileSize(filename); long size = getFileSize(filename);
while (getline(etags, line)) { while (getline(etags, line)) {
i++; i++;
if (line == filename) { if (line == filename) {
index = i; index = i;
break; break;
} }
long p1 = line.find(':'); long p1 = line.find(':');
if (p1 == string::npos) continue; if (p1 == string::npos) continue;
long p2 = line.find(':', (unsigned) p1 + 1); long p2 = line.find(':', (unsigned) p1 + 1);
if (p2 == string::npos) continue; if (p2 == string::npos) continue;
long p3 = line.find(':', (unsigned) p2 + 1); long p3 = line.find(':', (unsigned) p2 + 1);
if (p3 == string::npos) continue; if (p3 == string::npos) continue;
string FILENAME = line.substr(0, (unsigned) p1); string FILENAME = line.substr(0, (unsigned) p1);
string HASH = line.substr((unsigned) p1 + 1, (unsigned) (p2 - p1)); string HASH = line.substr((unsigned) p1 + 1, (unsigned) (p2 - p1));
string TIMESTAMP = line.substr((unsigned) p2 + 1, (unsigned) (p3 - p2)); 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); long SIZE = strtol(line.substr((unsigned) p3 + 1, line.length() - p3).c_str(), nullptr, 10);
if (FILENAME == filename) { if (FILENAME == filename) {
index = i; index = i;
if (timestamp != TIMESTAMP || size != SIZE) { if (timestamp != TIMESTAMP || size != SIZE) {
break; break;
} else { } else {
etags.close(); etags.close();
return HASH; return HASH;
} }
} }
} }
etags.close(); etags.close();
MD5_CTX mdContext; MD5_CTX mdContext;
MD5_Init(&mdContext); MD5_Init(&mdContext);
size_t bytes; size_t bytes;
char buffer[4096]; char buffer[4096];
FILE *file = fopen(filename.c_str(), "rb"); FILE *file = fopen(filename.c_str(), "rb");
if (file == nullptr) { if (file == nullptr) {
throw (char *) "Invalid file"; throw (char *) "Invalid file";
} }
while ((bytes = fread(buffer, 1, 4096, file)) != 0) { while ((bytes = fread(buffer, 1, 4096, file)) != 0) {
MD5_Update(&mdContext, buffer, bytes); MD5_Update(&mdContext, buffer, bytes);
} }
fclose(file); fclose(file);
unsigned char md[16]; unsigned char md[16];
MD5_Final(md, &mdContext); MD5_Final(md, &mdContext);
char md5buff[32]; char md5buff[32];
for (int i = 0; i < 16; i++) { for (int i = 0; i < 16; i++) {
sprintf(md5buff + i * 2, "%02x", md[i]); sprintf(md5buff + i * 2, "%02x", md[i]);
} }
string md5 = string(md5buff); string md5 = string(md5buff);
if (index == 0) { if (index == 0) {
char buff[256]; char buff[256];
sprintf(buff, "%s:%s:%s:%ld\n", filename.c_str(), md5.c_str(), timestamp.c_str(), size); sprintf(buff, "%s:%s:%s:%ld\n", filename.c_str(), md5.c_str(), timestamp.c_str(), size);
FILE *f = fopen("/var/necronda/ETags", "a"); FILE *f = fopen("/var/necronda/ETags", "a");
if (f == nullptr) { if (f == nullptr) {
throw (char *) strerror(errno); throw (char *) strerror(errno);
} }
fseek(f, 0, SEEK_END); fseek(f, 0, SEEK_END);
fwrite(buff, 1, strlen(buff), f); fwrite(buff, 1, strlen(buff), f);
fflush(f); fflush(f);
fclose(f); fclose(f);
} else { } else {
} }
return md5; return md5;
} }
#include <iostream> #include <iostream>
@ -181,18 +181,18 @@ string getETag(string filename) {
long getPosition(std::string str, char c, int occurence) { long getPosition(std::string str, char c, int occurence) {
int tempOccur = 0; int tempOccur = 0;
int num = 0; int num = 0;
for (auto it : str) { for (auto it : str) {
num++; num++;
if (it == c) { if (it == c) {
if (++tempOccur == occurence) { if (++tempOccur == occurence) {
return num; return num;
} }
} }
} }
return -1; return -1;
} }
/** /**
@ -204,314 +204,314 @@ long getPosition(std::string str, char c, int occurence) {
* @return Should the server wait for another header? * @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 connection_handler(const char *preprefix, const char *col1, const char *col2, Socket *socket, long id, long num, IpAddressInfo *info) {
bool error = false; bool error = false;
char buffer[1024]; char buffer[1024];
char *prefix = (char *) preprefix; char *prefix = (char *) preprefix;
try { try {
HttpConnection req(socket); HttpConnection req(socket);
try { try {
if (req.isExistingField("Connection") && req.getField("Connection") == "keep-alive") { if (req.isExistingField("Connection") && req.getField("Connection") == "keep-alive") {
req.setField("Connection", "keep-alive"); req.setField("Connection", "keep-alive");
req.setField("Keep-Alive", "timeout=60, max=100"); req.setField("Keep-Alive", "timeout=60, max=100");
} else { } else {
req.setField("Connection", "close"); req.setField("Connection", "close");
error = true; error = true;
} }
string host = ""; string host = "";
if (!req.isExistingField("Host")) { if (!req.isExistingField("Host")) {
req.respond(400); req.respond(400);
} else { } else {
host = req.getField("Host"); host = req.getField("Host");
long pos = host.find(':'); long pos = host.find(':');
if (pos != string::npos) { if (pos != string::npos) {
host.erase(pos, host.length() - pos); host.erase(pos, host.length() - pos);
} }
/*FILE *name = popen(("dig @8.8.8.8 +time=1 -x " + socket->getPeerAddress()->toString() + /*FILE *name = popen(("dig @8.8.8.8 +time=1 -x " + socket->getPeerAddress()->toString() +
" | grep -oP \"^[^;].*\\t\\K([^ ]*)\\w\"").c_str(), "r"); " | grep -oP \"^[^;].*\\t\\K([^ ]*)\\w\"").c_str(), "r");
char hostbuffer[1024]; char hostbuffer[1024];
memset(hostbuffer, 0, 1024); memset(hostbuffer, 0, 1024);
size_t size = fread(hostbuffer, 1, 1024, name); size_t size = fread(hostbuffer, 1, 1024, name);
hostbuffer[size - 1] = 0; // remove \n hostbuffer[size - 1] = 0; // remove \n
if (size <= 1) { if (size <= 1) {
sprintf(hostbuffer, "%s", socket->getPeerAddress()->toString().c_str()); 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, 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); info->host.c_str(), socket->getPeerPort(), col2);
prefix = buffer; prefix = buffer;
log(prefix, "\x1B[1m" + req.getMethod() + " " + req.getPath() + "\x1B[0m"); log(prefix, "\x1B[1m" + req.getMethod() + " " + req.getPath() + "\x1B[0m");
log_to_file(prefix, "\x1B[1m" + req.getMethod() + " " + req.getPath() + "\x1B[0m", host); log_to_file(prefix, "\x1B[1m" + req.getMethod() + " " + req.getPath() + "\x1B[0m", host);
bool noRedirect = req.getPath().find("/.well-known/") == 0 || (req.getPath().find("/files/") == 0); bool noRedirect = req.getPath().find("/.well-known/") == 0 || (req.getPath().find("/files/") == 0);
bool redir = true; bool redir = true;
if (!noRedirect) { if (!noRedirect) {
if (getWebRoot(host).empty()) { if (getWebRoot(host).empty()) {
req.redirect(303, "https://www.necronda.net" + req.getPath()); req.redirect(303, "https://www.necronda.net" + req.getPath());
} else if (socket->getSocketPort() != 443) { } else if (socket->getSocketPort() != 443) {
req.redirect(302, "https://" + host + req.getPath()); req.redirect(302, "https://" + host + req.getPath());
} else { } else {
redir = false; redir = false;
} }
} else { } else {
redir = false; redir = false;
} }
URI path = URI(getWebRoot(host), req.getPath()); URI path = URI(getWebRoot(host), req.getPath());
pid_t childpid = 0; pid_t childpid = 0;
if (redir) { if (redir) {
} else if (!path.getNewPath().empty() && req.getMethod() != "POST") { } else if (!path.getNewPath().empty() && req.getMethod() != "POST") {
req.redirect(303, path.getNewPath()); req.redirect(303, path.getNewPath());
} else { } else {
FILE *file = path.openFile(); FILE *file = path.openFile();
if (file == nullptr) { if (file == nullptr) {
req.setField("Cache-Control", "public, max-age=60"); req.setField("Cache-Control", "public, max-age=60");
req.respond(404); req.respond(404);
} else { } else {
string type = path.getFileType(); string type = path.getFileType();
if (type.find("inode/") == 0) { if (type.find("inode/") == 0) {
req.respond(403); req.respond(403);
} else if (path.getRelativeFilePath().find("/.") != string::npos && !noRedirect) { } else if (path.getRelativeFilePath().find("/.") != string::npos && !noRedirect) {
req.respond(403); req.respond(403);
} else { } else {
req.setField("Content-Type", type); req.setField("Content-Type", type);
req.setField("Last-Modified", getHttpDate(path.getFilePath())); req.setField("Last-Modified", getHttpDate(path.getFilePath()));
bool invalidMethod = false; bool invalidMethod = false;
bool etag = false; bool etag = false;
if (path.isStatic()) { if (path.isStatic()) {
string hash = getETag(path.getFilePath()); string hash = getETag(path.getFilePath());
req.setField("ETag", hash); req.setField("ETag", hash);
req.setField("Accept-Ranges", "bytes"); req.setField("Accept-Ranges", "bytes");
if (type.find("text/") == 0) { if (type.find("text/") == 0) {
req.setField("Cache-Control", "public, max-age=3600"); req.setField("Cache-Control", "public, max-age=3600");
} else { } else {
req.setField("Cache-Control", "public, max-age=86400"); req.setField("Cache-Control", "public, max-age=86400");
} }
req.setField("Allow", "GET"); req.setField("Allow", "GET");
if (req.getMethod() != "GET") { if (req.getMethod() != "GET") {
invalidMethod = true; invalidMethod = true;
} }
if (req.isExistingField("If-None-Match") && req.getField("If-None-Match") == hash) { if (req.isExistingField("If-None-Match") && req.getField("If-None-Match") == hash) {
etag = true; etag = true;
} }
} else { } else {
req.setField("Accept-Ranges", "none"); req.setField("Accept-Ranges", "none");
req.setField("Cache-Control", "private, no-cache"); req.setField("Cache-Control", "private, no-cache");
req.setField("Allow", "GET, POST, PUT"); req.setField("Allow", "GET, POST, PUT");
if (req.getMethod() != "GET" && req.getMethod() != "POST" && req.getMethod() != "PUT") { if (req.getMethod() != "GET" && req.getMethod() != "POST" && req.getMethod() != "PUT") {
invalidMethod = true; invalidMethod = true;
} }
} }
if (invalidMethod) { if (invalidMethod) {
req.respond(405); req.respond(405);
} else if (etag) { } else if (etag) {
req.respond(304); req.respond(304);
} else { } else {
int statuscode = 0; int statuscode = 0;
if (!path.isStatic()) { if (!path.isStatic()) {
string cmd = (string) "env -i" + string cmd = (string) "env -i" +
" REDIRECT_STATUS=" + cli_encode("CGI") + " REDIRECT_STATUS=" + cli_encode("CGI") +
" DOCUMENT_ROOT=" + cli_encode(getWebRoot(host)) + " DOCUMENT_ROOT=" + cli_encode(getWebRoot(host)) +
" " + req.cgiExport() + " " + req.cgiExport() +
(req.isExistingField("Content-Length") ? " CONTENT_LENGTH=" + (req.isExistingField("Content-Length") ? " CONTENT_LENGTH=" +
cli_encode(req.getField( cli_encode(req.getField(
"Content-Length")) "Content-Length"))
: "") + : "") +
(req.isExistingField("Content-Type") ? " CONTENT_TYPE=" + cli_encode( (req.isExistingField("Content-Type") ? " CONTENT_TYPE=" + cli_encode(
req.getField("Content-Type")) : "") + req.getField("Content-Type")) : "") +
((socket->isSecured()) ? " HTTPS=on" : "") + ((socket->isSecured()) ? " HTTPS=on" : "") +
" PATH_INFO=" + cli_encode(path.getFilePathInfo()) + " PATH_INFO=" + cli_encode(path.getFilePathInfo()) +
" PATH_TRANSLATED=" + cli_encode(path.getAbsolutePath()) + " PATH_TRANSLATED=" + cli_encode(path.getAbsolutePath()) +
" QUERY_STRING=" + cli_encode(path.getQuery()) + " QUERY_STRING=" + cli_encode(path.getQuery()) +
" REMOTE_ADDR=" + cli_encode(socket->getPeerAddress()->toString()) + " REMOTE_ADDR=" + cli_encode(socket->getPeerAddress()->toString()) +
" REMOTE_HOST=" + cli_encode(info->host) + " REMOTE_HOST=" + cli_encode(info->host) +
" REMOTE_PORT=" + cli_encode(to_string(socket->getPeerPort())) + " REMOTE_PORT=" + cli_encode(to_string(socket->getPeerPort())) +
" REQUEST_METHOD=" + cli_encode(req.getMethod()) + " REQUEST_METHOD=" + cli_encode(req.getMethod()) +
" REQUEST_URI=" + cli_encode(req.getPath()) + " REQUEST_URI=" + cli_encode(req.getPath()) +
" SCRIPT_FILENAME=" + cli_encode(path.getFilePath()) + " SCRIPT_FILENAME=" + cli_encode(path.getFilePath()) +
" SCRIPT_NAME=" + cli_encode(path.getRelativePath()) + " SCRIPT_NAME=" + cli_encode(path.getRelativePath()) +
" SERVER_ADMIN=" + cli_encode("lorenz.stechauner@gmail.com") + " SERVER_ADMIN=" + cli_encode("lorenz.stechauner@gmail.com") +
" SERVER_NAME=" + cli_encode(host) + " SERVER_NAME=" + cli_encode(host) +
" SERVER_PORT=" + cli_encode(to_string(socket->getSocketPort())) + " SERVER_PORT=" + cli_encode(to_string(socket->getSocketPort())) +
" SERVER_SOFTWARE=" + cli_encode("Necronda 3.0") + " SERVER_SOFTWARE=" + cli_encode("Necronda 3.0") +
" SERVER_PROTOCOL=" + cli_encode("HTTP/1.1") + " SERVER_PROTOCOL=" + cli_encode("HTTP/1.1") +
" GATEWAY_INTERFACE=" + cli_encode("CGI/1.1") + " GATEWAY_INTERFACE=" + cli_encode("CGI/1.1") +
" /usr/bin/php-cgi"; " /usr/bin/php-cgi";
stds pipes = procopen(cmd.c_str()); stds pipes = procopen(cmd.c_str());
childpid = pipes.pid; 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; 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); socket->receive(pipes.stdin, len);
fclose(pipes.stdin); fclose(pipes.stdin);
thread *t = new thread(php_error_handler, prefix, pipes.stderr); thread *t = new thread(php_error_handler, prefix, pipes.stderr);
string line; string line;
while (!(line = read_line(pipes.stdout)).empty()) { while (!(line = read_line(pipes.stdout)).empty()) {
long pos = line.find(':'); long pos = line.find(':');
string index = line.substr(0, pos); string index = line.substr(0, pos);
string data = line.substr(pos + 1, line.length() - pos); string data = line.substr(pos + 1, line.length() - pos);
while (index[0] == ' ') index.erase(index.begin() + 0); while (index[0] == ' ') index.erase(index.begin() + 0);
while (index[index.length() - 1] == ' ') index.erase(index.end() - 1); while (index[index.length() - 1] == ' ') index.erase(index.end() - 1);
while (data[0] == ' ') data.erase(data.begin() + 0); while (data[0] == ' ') data.erase(data.begin() + 0);
while (data[data.length() - 1] == ' ') data.erase(data.end() - 1); while (data[data.length() - 1] == ' ') data.erase(data.end() - 1);
if (index == "Status") { if (index == "Status") {
statuscode = (int) strtol(data.substr(0, 3).c_str(), nullptr, 10); statuscode = (int) strtol(data.substr(0, 3).c_str(), nullptr, 10);
} else { } else {
if (index == "Location" && statuscode == 0) { if (index == "Location" && statuscode == 0) {
statuscode = 303; statuscode = 303;
} }
req.setField(index, data); req.setField(index, data);
} }
} }
fclose(file); fclose(file);
int c = fgetc(pipes.stdout); int c = fgetc(pipes.stdout);
if (c == -1) { if (c == -1) {
// No Data -> Error // No Data -> Error
req.respond((statuscode == 0) ? 500 : statuscode); req.respond((statuscode == 0) ? 500 : statuscode);
statuscode = -1; statuscode = -1;
} else { } else {
ungetc(c, pipes.stdout); ungetc(c, pipes.stdout);
} }
file = pipes.stdout; file = pipes.stdout;
} }
if (statuscode != -1) { if (statuscode != -1) {
statuscode = (statuscode == 0) ? 200 : statuscode; statuscode = (statuscode == 0) ? 200 : statuscode;
bool compress = /*path.isStatic() &&*/ type.find("text/") == 0 && bool compress = /*path.isStatic() &&*/ type.find("text/") == 0 &&
req.isExistingField("Accept-Encoding") && req.isExistingField("Accept-Encoding") &&
req.getField("Accept-Encoding").find( req.getField("Accept-Encoding").find(
"deflate") != string::npos; "deflate") != string::npos;
if (compress) { if (compress) {
req.setField("Accept-Ranges", "none"); req.setField("Accept-Ranges", "none");
} }
if (compress && req.isExistingField("Range")) { if (compress && req.isExistingField("Range")) {
req.respond(416); req.respond(416);
} else if (req.isExistingField("Range")) { } else if (req.isExistingField("Range")) {
string range = req.getField("Range"); string range = req.getField("Range");
if (range.find("bytes=") != 0 || !path.isStatic()) { if (range.find("bytes=") != 0 || !path.isStatic()) {
req.respond(416); req.respond(416);
} else { } else {
fseek(file, 0L, SEEK_END); fseek(file, 0L, SEEK_END);
long len = ftell(file); long len = ftell(file);
fseek(file, 0L, SEEK_SET); fseek(file, 0L, SEEK_SET);
long p = range.find('-'); long p = range.find('-');
if (p == string::npos) { if (p == string::npos) {
req.respond(416); req.respond(416);
} else { } else {
string part1 = range.substr(6, (unsigned long) (p - 6)); string part1 = range.substr(6, (unsigned long) (p - 6));
string part2 = range.substr((unsigned long) (p + 1), string part2 = range.substr((unsigned long) (p + 1),
range.length() - p - 1); range.length() - p - 1);
long num1 = stol(part1, nullptr, 10); long num1 = stol(part1, nullptr, 10);
long num2 = len - 1; long num2 = len - 1;
if (!part2.empty()) { if (!part2.empty()) {
num2 = stol(part2, nullptr, 10); num2 = stol(part2, nullptr, 10);
} }
if (num1 < 0 || num1 >= len || num2 < 0 || num2 >= len) { if (num1 < 0 || num1 >= len || num2 < 0 || num2 >= len) {
req.respond(416); req.respond(416);
} else { } else {
req.setField("Content-Range", req.setField("Content-Range",
(string) "bytes " + to_string(num1) + "-" + (string) "bytes " + to_string(num1) + "-" +
to_string(num2) + to_string(num2) +
"/" + to_string(len)); "/" + to_string(len));
req.respond(206, file, compress, num1, num2); req.respond(206, file, compress, num1, num2);
} }
} }
} }
} else { } else {
req.respond(statuscode, file, compress); req.respond(statuscode, file, compress);
} }
} }
} }
} }
fclose(file); fclose(file);
if (childpid > 0) { if (childpid > 0) {
waitpid(childpid, nullptr, 0); waitpid(childpid, nullptr, 0);
} }
} }
} }
} }
HttpStatusCode status = req.getStatusCode(); HttpStatusCode status = req.getStatusCode();
int code = status.code; int code = status.code;
string color = ""; string color = "";
string comment = ""; string comment = "";
if ((code >= 200 && code < 300) || code == 304) { if ((code >= 200 && code < 300) || code == 304) {
color = "\x1B[1;32m"; // Success (Cached): Green color = "\x1B[1;32m"; // Success (Cached): Green
} else if (code >= 100 && code < 200) { } else if (code >= 100 && code < 200) {
color = "\x1B[1;93m"; // Continue: Yellow color = "\x1B[1;93m"; // Continue: Yellow
} else if (code >= 300 && code < 400) { } else if (code >= 300 && code < 400) {
color = "\x1B[1;93m"; // Redirect: Yellow color = "\x1B[1;93m"; // Redirect: Yellow
comment = " -> " + comment = " -> " +
(req.isExistingResponseField("Location") ? req.getResponseField("Location") : "<invalid>"); (req.isExistingResponseField("Location") ? req.getResponseField("Location") : "<invalid>");
} else if (code >= 400 && code < 500) { } else if (code >= 400 && code < 500) {
color = "\x1B[1;31m"; // Client Error: Red color = "\x1B[1;31m"; // Client Error: Red
//comment = " -> " + req.getPath(); //comment = " -> " + req.getPath();
} else if (code >= 500 & code < 600) { } else if (code >= 500 & code < 600) {
color = "\x1B[1;31m"; // Server Error: Red color = "\x1B[1;31m"; // Server Error: Red
//comment = " -> " + req.getPath(); //comment = " -> " + req.getPath();
} }
string msg = color + to_string(status.code) + " " + status.message + comment + " (" + formatTime(req.getDuration()) + ")\x1B[0m"; string msg = color + to_string(status.code) + " " + status.message + comment + " (" + formatTime(req.getDuration()) + ")\x1B[0m";
log(prefix, msg); log(prefix, msg);
if (!host.empty()) { if (!host.empty()) {
log_to_file(prefix, msg, host); log_to_file(prefix, msg, host);
} }
} catch (char *msg) { } catch (char *msg) {
HttpStatusCode status = req.getStatusCode(); HttpStatusCode status = req.getStatusCode();
log(prefix, to_string(status.code) + " " + status.message + " (" + formatTime(req.getDuration()) + ")"); log(prefix, to_string(status.code) + " " + status.message + " (" + formatTime(req.getDuration()) + ")");
try { try {
if (strncmp(msg, "timeout", strlen(msg)) == 0) { if (strncmp(msg, "timeout", strlen(msg)) == 0) {
log(prefix, "Timeout!"); log(prefix, "Timeout!");
req.setField("Connection", "close"); req.setField("Connection", "close");
req.respond(408); req.respond(408);
error = true; error = true;
} else if (strncmp(msg, "Invalid path", strlen(msg)) == 0) { } else if (strncmp(msg, "Invalid path", strlen(msg)) == 0) {
log(prefix, "Timeout!"); log(prefix, "Timeout!");
req.setField("Connection", "close"); req.setField("Connection", "close");
req.respond(400); req.respond(400);
} else { } else {
log(prefix, (string) "Unable to receive from socket: " + msg); log(prefix, (string) "Unable to receive from socket: " + msg);
error = true; error = true;
} }
} catch (char *msg2) { } catch (char *msg2) {
} }
} }
} catch (char *msg) { } catch (char *msg) {
try { try {
if (msg == "Malformed header") { if (msg == "Malformed header") {
log(prefix, "Unable to parse header: 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"); socket->send("HTTP/1.1 400 Bad Request\r\nConnection: close\r\n\r\n");
error = true; error = true;
} else if (msg == "timeout") { } else if (msg == "timeout") {
log(prefix, "Timeout!"); log(prefix, "Timeout!");
socket->send("HTTP/1.1 408 Request Timeout\r\nConnection: close\r\n\r\n"); socket->send("HTTP/1.1 408 Request Timeout\r\nConnection: close\r\n\r\n");
error = true; error = true;
} else { } else {
log(prefix, (string) "Unable to receive from socket: " + msg); log(prefix, (string) "Unable to receive from socket: " + msg);
error = true; error = true;
} }
} catch (char *msg2) { } catch (char *msg2) {
} }
} }
return !error; return !error;
} }
/** /**
@ -520,70 +520,70 @@ bool connection_handler(const char *preprefix, const char *col1, const char *col
* @param id The client ID * @param id The client ID
*/ */
void client_handler(Socket *socket, long id, bool ssl) { void client_handler(Socket *socket, long id, bool ssl) {
const char *prefix; const char *prefix;
char const *col1; char const *col1;
char const *col2 = "\x1B[0m"; char const *col2 = "\x1B[0m";
IpAddressInfo info = get_ip_address_info(socket->getPeerAddress()); IpAddressInfo info = get_ip_address_info(socket->getPeerAddress());
{ {
auto group = (int) (id % 6); auto group = (int) (id % 6);
if (group == 0) { if (group == 0) {
col1 = "\x1B[0;31m"; // Red col1 = "\x1B[0;31m"; // Red
} else if (group == 1) { } else if (group == 1) {
col1 = "\x1B[0;32m"; // Green col1 = "\x1B[0;32m"; // Green
} else if (group == 2) { } else if (group == 2) {
col1 = "\x1B[0;34m"; // Blue col1 = "\x1B[0;34m"; // Blue
} else if (group == 3) { } else if (group == 3) {
col1 = "\x1B[0;33m"; // Yellow col1 = "\x1B[0;33m"; // Yellow
} else if (group == 4) { } else if (group == 4) {
col1 = "\x1B[0;35m"; // Magenta col1 = "\x1B[0;35m"; // Magenta
} else { } else {
col1 = "\x1B[0;36m"; // Cyan col1 = "\x1B[0;36m"; // Cyan
} }
string *a = new string("[" + socket->getSocketAddress()->toString() + "][" + string *a = new string("[" + socket->getSocketAddress()->toString() + "][" +
to_string(socket->getSocketPort()) + "]" + col1 + to_string(socket->getSocketPort()) + "]" + col1 +
"[" + info.host + "][" + to_string(socket->getPeerPort()) + "[" + info.host + "][" + to_string(socket->getPeerPort()) +
"]" + col2 + " "); "]" + col2 + " ");
prefix = a->c_str(); prefix = a->c_str();
} }
log(prefix, "Connection established"); log(prefix, "Connection established");
log(prefix, string("Host: ") + info.host + " (" + socket->getPeerAddress()->toString() + ")"); log(prefix, string("Host: ") + info.host + " (" + socket->getPeerAddress()->toString() + ")");
log(prefix, string("Location: ") + info.cc + "/" + info.country + ", " + info.prov + "/" + info.provname + ", " + info.city); log(prefix, string("Location: ") + info.cc + "/" + info.country + ", " + info.prov + "/" + info.provname + ", " + info.city);
log(prefix, string("Local Date: ") + info.localdate + " (" + info.timezone + ")"); log(prefix, string("Local Date: ") + info.localdate + " (" + info.timezone + ")");
bool err = false; bool err = false;
try { try {
socket->setReceiveTimeout(60000); socket->setReceiveTimeout(60000);
socket->setSendTimeout(60000); socket->setSendTimeout(60000);
} catch (char *msg) { } catch (char *msg) {
log(prefix, (string) "Unable to set timeout on socket: " + msg); log(prefix, (string) "Unable to set timeout on socket: " + msg);
err = true; err = true;
} }
try { try {
if (ssl) { if (ssl) {
//socket->sslHandshake("/home/lorenz/Documents/Projects/Necronda-Server/necronda-server-3.0/privkey.pem", //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"); // "/home/lorenz/Documents/Projects/Necronda-Server/necronda-server-3.0/fullchain.pem");
socket->sslHandshake("/cert/necronda.net/privkey.pem", socket->sslHandshake("/cert/necronda.net/privkey.pem",
"/cert/necronda.net/fullchain.pem"); "/cert/necronda.net/fullchain.pem");
} }
} catch (char *msg) { } catch (char *msg) {
log(prefix, (string) "Unable to perform handshake: " + msg); log(prefix, (string) "Unable to perform handshake: " + msg);
err = true; err = true;
} }
long reqnum = 0; long reqnum = 0;
if (!err) { if (!err) {
while (connection_handler(prefix, col1, col2, socket, id, ++reqnum, &info)); while (connection_handler(prefix, col1, col2, socket, id, ++reqnum, &info));
reqnum--; reqnum--;
} }
log(prefix, log(prefix,
"Connection terminated (#:" + to_string(reqnum) + ", R: " + formatSize(socket->getBytesReceived()) + ", S: " + "Connection terminated (#:" + to_string(reqnum) + ", R: " + formatSize(socket->getBytesReceived()) + ", S: " +
formatSize(socket->getBytesSent()) + ", T: " + formatTime(socket->getDuration()) + ")"); formatSize(socket->getBytesSent()) + ", T: " + formatTime(socket->getDuration()) + ")");
socket->close(); socket->close();
} }

View File

@ -22,29 +22,29 @@ const char* webroot = "/srv/necronda/";
string getMimeType(string path) { string getMimeType(string path) {
unsigned long pos = path.find_last_of('.'); unsigned long pos = path.find_last_of('.');
string ext; string ext;
if (pos != string::npos) { if (pos != string::npos) {
ext = path.substr(pos + 1, path.length() - pos); ext = path.substr(pos + 1, path.length() - pos);
} }
magic_t magic = magic_open(MAGIC_MIME_TYPE); magic_t magic = magic_open(MAGIC_MIME_TYPE);
magic_load(magic, "/usr/share/misc/magic.mgc"); magic_load(magic, "/usr/share/misc/magic.mgc");
string type = magic_file(magic, path.c_str()); string type = magic_file(magic, path.c_str());
magic_setflags(magic, MAGIC_MIME_ENCODING); magic_setflags(magic, MAGIC_MIME_ENCODING);
string charset = magic_file(magic, path.c_str()); string charset = magic_file(magic, path.c_str());
if (type == "text/plain") { if (type == "text/plain") {
if (ext == "css") { if (ext == "css") {
type = "text/css"; type = "text/css";
} else if (ext == "js") { } else if (ext == "js") {
type = "text/javascript"; type = "text/javascript";
} }
} }
magic_close(magic); magic_close(magic);
return type + "; charset=" + charset; return type + "; charset=" + charset;
} }
/** /**
@ -53,22 +53,22 @@ string getMimeType(string path) {
*/ */
std::string getTimestamp(string path) { std::string getTimestamp(string path) {
struct stat attrib; struct stat attrib;
stat(path.c_str(), &attrib); stat(path.c_str(), &attrib);
return getTimestamp(attrib.st_ctime); return getTimestamp(attrib.st_ctime);
} }
std::string getTimestamp(time_t time) { std::string getTimestamp(time_t time) {
char buffer[64]; char buffer[64];
struct tm *timeinfo = gmtime(&time); struct tm *timeinfo = gmtime(&time);
strftime(buffer, sizeof(buffer), "%Y%m%d%H%M%S", timeinfo); strftime(buffer, sizeof(buffer), "%Y%m%d%H%M%S", timeinfo);
return string(buffer); return string(buffer);
} }
long getFileSize(string filename) { long getFileSize(string filename) {
struct stat stat_buf; struct stat stat_buf;
int rc = stat(filename.c_str(), &stat_buf); int rc = stat(filename.c_str(), &stat_buf);
return rc == 0 ? stat_buf.st_size : -1; return rc == 0 ? stat_buf.st_size : -1;
} }
@ -78,104 +78,104 @@ long getFileSize(string filename) {
* @return A formatted time string * @return A formatted time string
*/ */
std::string formatTime(long micros) { std::string formatTime(long micros) {
char buffer[64]; char buffer[64];
if (micros < 1000) { if (micros < 1000) {
sprintf(buffer, "%.3f ms", micros / 1000.0); sprintf(buffer, "%.3f ms", micros / 1000.0);
} else if (micros < 10000) { } else if (micros < 10000) {
sprintf(buffer, "%.2f ms", micros / 1000.0); sprintf(buffer, "%.2f ms", micros / 1000.0);
} else if (micros < 100000) { } else if (micros < 100000) {
sprintf(buffer, "%.1f ms", micros / 1000.0); sprintf(buffer, "%.1f ms", micros / 1000.0);
} else if (micros < 1000000) { } else if (micros < 1000000) {
sprintf(buffer, "%.0f ms", micros / 1000.0); sprintf(buffer, "%.0f ms", micros / 1000.0);
} else { } else {
sprintf(buffer, "%.1f s", micros / 1000000.0); sprintf(buffer, "%.1f s", micros / 1000000.0);
} }
return std::string(buffer); return std::string(buffer);
} }
std::string formatSize(unsigned long bytes) { std::string formatSize(unsigned long bytes) {
char buffer[64]; char buffer[64];
if (bytes > 0x10000000000) { if (bytes > 0x10000000000) {
sprintf(buffer, "%.1f TiB", (double) bytes / 0x10000000000); sprintf(buffer, "%.1f TiB", (double) bytes / 0x10000000000);
} else if (bytes > 0x40000000) { } else if (bytes > 0x40000000) {
sprintf(buffer, "%.1f GiB", (double) bytes / 0x40000000); sprintf(buffer, "%.1f GiB", (double) bytes / 0x40000000);
} else if (bytes > 0x100000) { } else if (bytes > 0x100000) {
sprintf(buffer, "%.1f MiB", (double) bytes / 0x100000); sprintf(buffer, "%.1f MiB", (double) bytes / 0x100000);
} else if (bytes > 0x400) { } else if (bytes > 0x400) {
sprintf(buffer, "%.1f KiB", (double) bytes / 0x400); sprintf(buffer, "%.1f KiB", (double) bytes / 0x400);
} else { } else {
sprintf(buffer, "%ld B", bytes); sprintf(buffer, "%ld B", bytes);
} }
return std::string(buffer); return std::string(buffer);
} }
string url_decode(string url) { string url_decode(string url) {
long pos = 0; long pos = 0;
while ((pos = url.find('+', pos + 1)) != string::npos) { while ((pos = url.find('+', pos + 1)) != string::npos) {
url.replace(pos, 1, 1, ' '); url.replace(pos, 1, 1, ' ');
} }
pos = 0; pos = 0;
while ((pos = url.find('%', pos + 1)) != string::npos) { while ((pos = url.find('%', pos + 1)) != string::npos) {
const char *num = url.substr(pos + 1, 2).c_str(); const char *num = url.substr(pos + 1, 2).c_str();
auto c = (char) strtol(num, nullptr, 16); auto c = (char) strtol(num, nullptr, 16);
url.erase(pos, 3); url.erase(pos, 3);
url.insert(pos, 1, c); url.insert(pos, 1, c);
} }
return url; return url;
} }
string url_encode(string url) { string url_encode(string url) {
char buff[4]; char buff[4];
for (long pos = 0; pos < url.length(); pos++) { for (long pos = 0; pos < url.length(); pos++) {
auto c = (unsigned char) url[pos]; auto c = (unsigned char) url[pos];
if (c < ' ' || c > '~' || c == ' ' || c == '#' || c == '?' || c == '&' || c == '=' || c == '\\' || c == '%') { if (c < ' ' || c > '~' || c == ' ' || c == '#' || c == '?' || c == '&' || c == '=' || c == '\\' || c == '%') {
sprintf(buff, "%%%02X", c); sprintf(buff, "%%%02X", c);
url.replace(pos, 1, buff); url.replace(pos, 1, buff);
} }
} }
return url; return url;
} }
string html_decode(string text) { string html_decode(string text) {
return text; return text;
} }
string html_encode(string text) { string html_encode(string text) {
return text; return text;
} }
string cli_encode(string text) { string cli_encode(string text) {
char buff[5]; char buff[5];
for (long pos = 0; pos < text.length(); pos++) { for (long pos = 0; pos < text.length(); pos++) {
auto c = (unsigned char) text[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 == '-')) { 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); sprintf(buff, "\\%.1s", &c);
text.replace(pos, 1, buff); text.replace(pos, 1, buff);
pos++; pos++;
} }
} }
return text; return text;
} }
string read_line(FILE* file) { string read_line(FILE* file) {
char *line; char *line;
size_t len = 0; size_t len = 0;
ssize_t read; ssize_t read;
if ((read = getline(&line, &len, file)) < 0 || line == nullptr) { if ((read = getline(&line, &len, file)) < 0 || line == nullptr) {
return ""; return "";
} }
string l = string(line); string l = string(line);
if (l[l.length()-1] == '\n') { if (l[l.length()-1] == '\n') {
l.erase(l.length()-1); l.erase(l.length()-1);
} }
if (l[l.length()-1] == '\r') { if (l[l.length()-1] == '\r') {
l.erase(l.length()-1); l.erase(l.length()-1);
} }
return l; return l;
} }
@ -191,12 +191,12 @@ string read_line(FILE* file) {
#include "network/http/HttpConnection.cpp" #include "network/http/HttpConnection.cpp"
string getWebRoot(string host) { string getWebRoot(string host) {
string root = webroot + host; string root = webroot + host;
if (fileExists(root)) { if (fileExists(root)) {
return root; return root;
} else { } else {
return ""; return "";
} }
} }
@ -206,79 +206,79 @@ string getWebRoot(string host) {
long clientnum = 0; long clientnum = 0;
int main() { int main() {
cout << "Necronda Server 3.0" << endl << "by Lorenz Stechauner" << endl << endl; cout << "Necronda Server 3.0" << endl << "by Lorenz Stechauner" << endl << endl;
signal(SIGPIPE, SIG_IGN); signal(SIGPIPE, SIG_IGN);
SSL_load_error_strings(); SSL_load_error_strings();
SSL_library_init(); SSL_library_init();
ERR_load_crypto_strings(); ERR_load_crypto_strings();
OpenSSL_add_all_algorithms(); OpenSSL_add_all_algorithms();
int ret = system("mkdir -p /var/necronda /etc/necronda /tmp/necronda; touch /var/necronda/ETags"); int ret = system("mkdir -p /var/necronda /etc/necronda /tmp/necronda; touch /var/necronda/ETags");
if (ret != 0) { if (ret != 0) {
cout << "Unable to create server files" << endl; cout << "Unable to create server files" << endl;
exit(1); exit(1);
} }
list<unsigned short> ports = {80, 443}; list<unsigned short> ports = {80, 443};
list<Socket> servers = {}; list<Socket> servers = {};
auto it = ports.begin(); auto it = ports.begin();
for (int i = 0; i < ports.size(); i++) { for (int i = 0; i < ports.size(); i++) {
unsigned short port = *it; unsigned short port = *it;
advance(it, 1); advance(it, 1);
Socket server = Socket(); Socket server = Socket();
servers.push_back(server); servers.push_back(server);
try { try {
server.setReuseAddress(true); server.setReuseAddress(true);
server.setReceiveTimeout(0); server.setReceiveTimeout(0);
server.setSendTimeout(0); server.setSendTimeout(0);
} catch (char *msg) { } catch (char *msg) {
cout << "Unable to set socket option: " << msg << endl; cout << "Unable to set socket option: " << msg << endl;
exit(2); exit(2);
} }
try { try {
server.bind(port); server.bind(port);
} catch (char *msg) { } catch (char *msg) {
cout << "Unable to bind socket to port " << port << ": " << msg << endl; cout << "Unable to bind socket to port " << port << ": " << msg << endl;
exit(3); exit(3);
} }
try { try {
server.listen(256); server.listen(256);
} catch (char *msg) { } catch (char *msg) {
cout << "Unable to listen on socket: " << msg << endl; cout << "Unable to listen on socket: " << msg << endl;
exit(4); exit(4);
} }
} }
cout << "Ready for connections" << endl; cout << "Ready for connections" << endl;
while (true) { while (true) {
try { try {
Socket::select(servers, {}); Socket::select(servers, {});
for (Socket server : servers) { for (Socket server : servers) {
try { try {
Socket *socket = server.accept(); Socket *socket = server.accept();
clientnum++; clientnum++;
thread *t = new thread(client_handler, socket, clientnum, server.getSocketPort() == 443); thread *t = new thread(client_handler, socket, clientnum, server.getSocketPort() == 443);
} catch (char *msg) { } catch (char *msg) {
// Nothing // Nothing
} }
} }
} catch (char *msg) { } catch (char *msg) {
cout << "Select: " << msg << endl; cout << "Select: " << msg << endl;
break; break;
} }
} }
return 0; return 0;
} }

View File

@ -14,54 +14,54 @@ Address::Address() {
} }
Address::Address(string addr) { Address::Address(string addr) {
// TODO // TODO
} }
Address::Address(struct sockaddr_in *addr) { Address::Address(struct sockaddr_in *addr) {
address = ntohl(addr->sin_addr.s_addr); address = ntohl(addr->sin_addr.s_addr);
} }
struct sockaddr_in Address::toStruct(unsigned short port)const { struct sockaddr_in Address::toStruct(unsigned short port)const {
struct sockaddr_in addr; struct sockaddr_in addr;
addr.sin_family = AF_INET; addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(address); addr.sin_addr.s_addr = htonl(address);
addr.sin_port = htons(port); addr.sin_port = htons(port);
return addr; return addr;
} }
string Address::toString() const { string Address::toString() const {
struct sockaddr_in addr = toStruct(0); struct sockaddr_in addr = toStruct(0);
struct in_addr ipAddr = addr.sin_addr; struct in_addr ipAddr = addr.sin_addr;
return inet_ntoa(ipAddr); return inet_ntoa(ipAddr);
} }
bool Address::isLocal() { bool Address::isLocal() {
string a = toString(); string a = toString();
return a.find("127.0.0.") == 0; return a.find("127.0.0.") == 0;
} }
ostream& operator<<(ostream &str, const Address &addr) { ostream& operator<<(ostream &str, const Address &addr) {
return str << addr.toString(); return str << addr.toString();
} }
string operator+(string &str, const Address &addr) { string operator+(string &str, const Address &addr) {
return str + addr.toString(); return str + addr.toString();
} }
string operator+(string &str, const Address *addr) { string operator+(string &str, const Address *addr) {
return str + addr->toString(); return str + addr->toString();
} }
string operator+(const Address &addr, string &str) { string operator+(const Address &addr, string &str) {
return addr.toString() + str; return addr.toString() + str;
} }
string operator+(const Address *addr, string &str) { string operator+(const Address *addr, string &str) {
return addr->toString() + str; return addr->toString() + str;
} }

View File

@ -11,20 +11,20 @@ using namespace std;
class Address { class Address {
private: private:
unsigned int address; unsigned int address;
public: public:
Address(); Address();
explicit Address(string address); explicit Address(string address);
explicit Address(struct sockaddr_in *address); explicit Address(struct sockaddr_in *address);
struct sockaddr_in toStruct(unsigned short port) const; struct sockaddr_in toStruct(unsigned short port) const;
string toString() const; string toString() const;
bool isLocal(); bool isLocal();
}; };

File diff suppressed because it is too large Load Diff

View File

@ -12,8 +12,8 @@
#define CPPNET_CHUNK 16384 #define CPPNET_CHUNK 16384
typedef struct { typedef struct {
string privkey; string privkey;
string fullchain; string fullchain;
} KeyPair; } KeyPair;
using namespace std; using namespace std;
@ -21,138 +21,138 @@ using namespace std;
class Socket { class Socket {
private: private:
int fd; int fd;
SSL *ssl; SSL *ssl;
SSL_CTX *ctx; SSL_CTX *ctx;
bool enc; bool enc;
bool servers; bool servers;
bool clients; bool clients;
unsigned long bytesSent; unsigned long bytesSent;
unsigned long bytesReceived; unsigned long bytesReceived;
long microsStart; long microsStart;
long microsLast; long microsLast;
void setSocketOption(int, bool); void setSocketOption(int, bool);
long send(void *buffer, int size); long send(void *buffer, int size);
long receive(void *buffer, int size); long receive(void *buffer, int size);
long peek(void *buffer, int size); long peek(void *buffer, int size);
public: public:
Socket(); Socket();
explicit Socket(int filedescriptor); explicit Socket(int filedescriptor);
~Socket(); ~Socket();
void bind(Address *address, unsigned short port); void bind(Address *address, unsigned short port);
void bind(unsigned short port); void bind(unsigned short port);
void listen(int count = 1); void listen(int count = 1);
void connect(Address address, unsigned short port); void connect(Address address, unsigned short port);
Socket* accept(); Socket* accept();
void sslHandshake(); void sslHandshake();
void sslHandshake(map<string, KeyPair> sni); void sslHandshake(map<string, KeyPair> sni);
void sslHandshake(KeyPair keypair); void sslHandshake(KeyPair keypair);
void sslHandshake(string privkey, string fullchain); void sslHandshake(string privkey, string fullchain);
long send(string *str); long send(string *str);
long send(string str); long send(string str);
long send(const char *str); long send(const char *str);
long send(const char *str, long length); long send(const char *str, long length);
string receive(); string receive();
string receive(long length); string receive(long length);
string receive(string until); string receive(string until);
string receive(const char *until, unsigned long strlen); string receive(const char *until, unsigned long strlen);
string receive(const char *until); string receive(const char *until);
void receive(FILE *file); void receive(FILE *file);
string receiveLine(); string receiveLine();
void shutdown(); void shutdown();
void close(); void close();
long getDuration(); long getDuration();
Address *getSocketAddress() const; Address *getSocketAddress() const;
unsigned short getSocketPort() const; unsigned short getSocketPort() const;
Address *getPeerAddress() const; Address *getPeerAddress() const;
unsigned short getPeerPort() const; unsigned short getPeerPort() const;
string toString() const; string toString() const;
bool isServerSide(); bool isServerSide();
bool isClientSide(); bool isClientSide();
bool isSecured(); bool isSecured();
void setReuseAddress(bool value = true); void setReuseAddress(bool value = true);
void setReusePort(bool value = true); void setReusePort(bool value = true);
void setSendBufferSize(int value); void setSendBufferSize(int value);
void setReceiveBufferSize(int value); void setReceiveBufferSize(int value);
void setMinReceiveBytes(int value); void setMinReceiveBytes(int value);
void setMinSendBytes(int value); void setMinSendBytes(int value);
void setSendTimeout(unsigned long ms); void setSendTimeout(unsigned long ms);
void setReceiveTimeout(unsigned long ms); void setReceiveTimeout(unsigned long ms);
bool getReuseAddress(); bool getReuseAddress();
bool getReusePort(); bool getReusePort();
int getSendBufferSize(); int getSendBufferSize();
int getReceiveBufferSize(); int getReceiveBufferSize();
int getMinReceiveBytes(); int getMinReceiveBytes();
int getMinSendBytes(); int getMinSendBytes();
long getSendTimeout(); long getSendTimeout();
long getReceiveTimeout(); long getReceiveTimeout();
unsigned long getBytesSent(); unsigned long getBytesSent();
unsigned long getBytesReceived(); unsigned long getBytesReceived();
static long select(list<Socket> read, list<Socket> write, long millis); static long select(list<Socket> read, list<Socket> write, long millis);
static long select(list<Socket> read, list<Socket> write); static long select(list<Socket> read, list<Socket> write);
void receive(FILE *file, long size); void receive(FILE *file, long size);
}; };
Socket operator<<(Socket sock, const char *str); Socket operator<<(Socket sock, const char *str);

View File

@ -7,26 +7,26 @@
#include "Http.h" #include "Http.h"
unsigned long getMicros() { unsigned long getMicros() {
struct timeval tv; struct timeval tv;
gettimeofday(&tv, nullptr); gettimeofday(&tv, nullptr);
return (unsigned long) (1000000 * tv.tv_sec + tv.tv_usec); return (unsigned long) (1000000 * tv.tv_sec + tv.tv_usec);
} }
string getHttpDate() { string getHttpDate() {
time_t rawtime; time_t rawtime;
time(&rawtime); time(&rawtime);
return getHttpDate(rawtime); return getHttpDate(rawtime);
} }
string getHttpDate(string filename) { string getHttpDate(string filename) {
struct stat attrib; struct stat attrib;
stat(filename.c_str(), &attrib); stat(filename.c_str(), &attrib);
return getHttpDate(attrib.st_ctime); return getHttpDate(attrib.st_ctime);
} }
string getHttpDate(time_t time) { string getHttpDate(time_t time) {
char buffer[64]; char buffer[64];
struct tm *timeinfo = gmtime(&time); struct tm *timeinfo = gmtime(&time);
strftime(buffer, sizeof(buffer), "%a, %d %b %Y %H:%M:%S GMT", timeinfo); strftime(buffer, sizeof(buffer), "%a, %d %b %Y %H:%M:%S GMT", timeinfo);
return string(buffer); return string(buffer);
} }

View File

@ -10,184 +10,184 @@
#include "Http.h" #include "Http.h"
HttpConnection::HttpConnection(Socket *socket) { HttpConnection::HttpConnection(Socket *socket) {
this->socket = socket; this->socket = socket;
this->request = new HttpRequest(socket); this->request = new HttpRequest(socket);
this->response = new HttpResponse(); this->response = new HttpResponse();
microsStart = getMicros(); microsStart = getMicros();
response->setVersion("1.1"); response->setVersion("1.1");
response->setField("Server", "Necronda/3.0"); response->setField("Server", "Necronda/3.0");
} }
void HttpConnection::respond(int statuscode) { void HttpConnection::respond(int statuscode) {
if (statuscode >= 400 && statuscode < 600) { if (statuscode >= 400 && statuscode < 600) {
respond(statuscode, respond(statuscode,
"<!DOCTYPE html><html><head><title>" + to_string(statuscode) + " " + "<!DOCTYPE html><html><head><title>" + to_string(statuscode) + " " +
::getStatusCode(statuscode).message + ::getStatusCode(statuscode).message +
"</title></head><body><center><h1>" + to_string(statuscode) + " " + "</title></head><body><center><h1>" + to_string(statuscode) + " " +
::getStatusCode(statuscode).message + ::getStatusCode(statuscode).message +
"</h1>" + "</h1>" +
((request->isExistingField("Host")) ? ((request->isExistingField("Host")) ?
(request->isExistingField("Referer") && (request->isExistingField("Referer") &&
request->getField("Referer").find(request->getField("Host")) != string::npos) ? request->getField("Referer").find(request->getField("Host")) != string::npos) ?
"<p>Go back to the last page you visited: <a href=\"" + request->getField("Referer") + "\">" + "<p>Go back to the last page you visited: <a href=\"" + request->getField("Referer") + "\">" +
request->getField("Referer") + "</a></p>" : request->getField("Referer") + "</a></p>" :
"<p>Go back to the home page of <a href=\"//" + "<p>Go back to the home page of <a href=\"//" +
request->getField("Host") + "/\">" + request->getField("Host") + "/\">" +
request->getField("Host") + request->getField("Host") +
"</a></p>" : "") + "</center></body></html>\r\n" "</a></p>" : "") + "</center></body></html>\r\n"
); );
} else { } else {
respond(statuscode, ""); respond(statuscode, "");
} }
} }
void HttpConnection::respond(int statuscode, string payload) { void HttpConnection::respond(int statuscode, string payload) {
response->setStatusCode(statuscode); response->setStatusCode(statuscode);
response->setField("Date", getHttpDate()); response->setField("Date", getHttpDate());
response->setField("Content-Length", to_string(payload.length())); response->setField("Content-Length", to_string(payload.length()));
response->sendHeader(socket); response->sendHeader(socket);
socket->send(std::move(payload)); socket->send(std::move(payload));
} }
void HttpConnection::respond(int statuscode, FILE *file, bool compress, long start, long end) { void HttpConnection::respond(int statuscode, FILE *file, bool compress, long start, long end) {
response->setStatusCode(statuscode); response->setStatusCode(statuscode);
response->setField("Transfer-Encoding", "chunked"); response->setField("Transfer-Encoding", "chunked");
response->setField("Date", getHttpDate()); response->setField("Date", getHttpDate());
long shouldTransfer; long shouldTransfer;
long transfered = 0; long transfered = 0;
fseek(file, 0, SEEK_END); fseek(file, 0, SEEK_END);
long len = ftell(file); long len = ftell(file);
if (start != -1 && end != -1) { if (start != -1 && end != -1) {
fseek(file, start, SEEK_SET); fseek(file, start, SEEK_SET);
response->setField("Content-Length", to_string(end - start + 1)); response->setField("Content-Length", to_string(end - start + 1));
shouldTransfer = end - start + 1; shouldTransfer = end - start + 1;
compress = false; compress = false;
} else { } else {
fseek(file, 0, SEEK_SET); fseek(file, 0, SEEK_SET);
shouldTransfer = len; shouldTransfer = len;
if (len >= 0 && !compress) { if (len >= 0 && !compress) {
response->setField("Content-Length", to_string(len)); response->setField("Content-Length", to_string(len));
} }
} }
if (compress) { if (compress) {
response->setField("Content-Encoding", "deflate"); response->setField("Content-Encoding", "deflate");
} }
response->sendHeader(socket); response->sendHeader(socket);
if (compress) { if (compress) {
int level = 1; int level = 1;
int ret, flush; int ret, flush;
unsigned have; unsigned have;
z_stream strm; z_stream strm;
unsigned char in[CPPNET_CHUNK]; unsigned char in[CPPNET_CHUNK];
unsigned char out[CPPNET_CHUNK]; unsigned char out[CPPNET_CHUNK];
strm.zalloc = Z_NULL; strm.zalloc = Z_NULL;
strm.zfree = Z_NULL; strm.zfree = Z_NULL;
strm.opaque = Z_NULL; strm.opaque = Z_NULL;
ret = deflateInit(&strm, level); ret = deflateInit(&strm, level);
if (ret != Z_OK) { if (ret != Z_OK) {
throw (char *) "Unable to open file"; throw (char *) "Unable to open file";
} }
do { do {
strm.avail_in = (uInt) fread(in, 1, CPPNET_CHUNK, file); strm.avail_in = (uInt) fread(in, 1, CPPNET_CHUNK, file);
if (ferror(file)) { if (ferror(file)) {
(void) deflateEnd(&strm); (void) deflateEnd(&strm);
throw (char *) strerror(errno); throw (char *) strerror(errno);
} }
flush = feof(file) ? Z_FINISH : Z_NO_FLUSH; flush = feof(file) ? Z_FINISH : Z_NO_FLUSH;
strm.next_in = in; strm.next_in = in;
do { do {
strm.avail_out = CPPNET_CHUNK; strm.avail_out = CPPNET_CHUNK;
strm.next_out = out; strm.next_out = out;
ret = deflate(&strm, flush); ret = deflate(&strm, flush);
assert(ret != Z_STREAM_ERROR); assert(ret != Z_STREAM_ERROR);
have = CPPNET_CHUNK - strm.avail_out; have = CPPNET_CHUNK - strm.avail_out;
if (have != 0) { if (have != 0) {
char buffer[64]; char buffer[64];
sprintf(buffer, "%X\r\n", have); sprintf(buffer, "%X\r\n", have);
socket->send(buffer); socket->send(buffer);
socket->send((const char *) out, have); socket->send((const char *) out, have);
socket->send("\r\n"); socket->send("\r\n");
} }
} while (strm.avail_out == 0); } while (strm.avail_out == 0);
assert(strm.avail_in == 0); assert(strm.avail_in == 0);
} while (flush != Z_FINISH); } while (flush != Z_FINISH);
assert(ret == Z_STREAM_END); assert(ret == Z_STREAM_END);
socket->send("0\r\n\r\n"); socket->send("0\r\n\r\n");
deflateEnd(&strm); deflateEnd(&strm);
} else { } else {
char buffer[CPPNET_CHUNK]; char buffer[CPPNET_CHUNK];
char buff[64]; char buff[64];
while (true) { while (true) {
unsigned long size = fread(buffer, 1, (size_t) ((CPPNET_CHUNK > (shouldTransfer - transfered) && shouldTransfer > 0) ? (shouldTransfer - transfered) : CPPNET_CHUNK), file); unsigned long size = fread(buffer, 1, (size_t) ((CPPNET_CHUNK > (shouldTransfer - transfered) && shouldTransfer > 0) ? (shouldTransfer - transfered) : CPPNET_CHUNK), file);
transfered += size; transfered += size;
sprintf(buff, "%lX\r\n", size); sprintf(buff, "%lX\r\n", size);
socket->send(buff); socket->send(buff);
socket->send((const char *) buffer, size); socket->send((const char *) buffer, size);
socket->send("\r\n"); socket->send("\r\n");
if (size == 0) { if (size == 0) {
break; break;
} }
} }
} }
} }
string HttpConnection::getField(string index) { string HttpConnection::getField(string index) {
return request->getField(std::move(index)); return request->getField(std::move(index));
} }
string HttpConnection::getPath() { string HttpConnection::getPath() {
return request->getPath(); return request->getPath();
} }
void HttpConnection::setField(string index, string data) { void HttpConnection::setField(string index, string data) {
response->setField(std::move(index), std::move(data)); response->setField(std::move(index), std::move(data));
} }
bool HttpConnection::isExistingField(string index) { bool HttpConnection::isExistingField(string index) {
return request->isExistingField(std::move(index)); return request->isExistingField(std::move(index));
} }
string HttpConnection::getMethod() { string HttpConnection::getMethod() {
return request->getMethod(); return request->getMethod();
} }
long HttpConnection::getDuration() { long HttpConnection::getDuration() {
return getMicros() - microsStart; return getMicros() - microsStart;
} }
HttpStatusCode HttpConnection::getStatusCode() { HttpStatusCode HttpConnection::getStatusCode() {
return response->getStatusCode(); return response->getStatusCode();
} }
void HttpConnection::redirect(int statuscode, string location) { void HttpConnection::redirect(int statuscode, string location) {
setField("Location", std::move(location)); setField("Location", std::move(location));
respond(statuscode, ""); respond(statuscode, "");
} }
string HttpConnection::getResponseField(string index) { string HttpConnection::getResponseField(string index) {
return response->getField(std::move(index)); return response->getField(std::move(index));
} }
bool HttpConnection::isExistingResponseField(string index) { bool HttpConnection::isExistingResponseField(string index) {
return response->isExistingField(std::move(index)); return response->isExistingField(std::move(index));
} }
string HttpConnection::cgiExport() { string HttpConnection::cgiExport() {
return request->cgiExport(); return request->cgiExport();
} }
void HttpConnection::removeField(string index) { void HttpConnection::removeField(string index) {
response->removeField(std::move(index)); response->removeField(std::move(index));
} }

View File

@ -11,43 +11,43 @@
class HttpConnection { class HttpConnection {
private: private:
Socket *socket; Socket *socket;
HttpRequest *request; HttpRequest *request;
HttpResponse *response; HttpResponse *response;
long microsStart; long microsStart;
public: public:
explicit HttpConnection(Socket *socket); explicit HttpConnection(Socket *socket);
void respond(int statuscode); void respond(int statuscode);
void respond(int statuscode, string payload); void respond(int statuscode, string payload);
void respond(int statuscode, FILE *file, bool compress = false, long start = -1, long end = -1); void respond(int statuscode, FILE *file, bool compress = false, long start = -1, long end = -1);
void redirect(int statuscode, string location); void redirect(int statuscode, string location);
bool isExistingField(string index); bool isExistingField(string index);
bool isExistingResponseField(string index); bool isExistingResponseField(string index);
string getField(string index); string getField(string index);
string getResponseField(string index); string getResponseField(string index);
string getPath(); string getPath();
string getMethod(); string getMethod();
void setField(string index, string data); void setField(string index, string data);
long getDuration(); long getDuration();
HttpStatusCode getStatusCode(); HttpStatusCode getStatusCode();
string cgiExport(); string cgiExport();
void removeField(string index); void removeField(string index);
}; };
#endif #endif

View File

@ -15,12 +15,12 @@
using namespace std; using namespace std;
string to_cgi(string text) { string to_cgi(string text) {
for (auto & c: text) c = (char) toupper(c); for (auto & c: text) c = (char) toupper(c);
long pos = 0; long pos = 0;
while ((pos = text.find('-', pos + 1)) != string::npos) { while ((pos = text.find('-', pos + 1)) != string::npos) {
text.replace(pos, 1, 1, '_'); text.replace(pos, 1, 1, '_');
} }
return text; return text;
} }
@ -28,33 +28,33 @@ string to_cgi(string text) {
* Default Constructor * Default Constructor
*/ */
HttpHeader::HttpHeader() { HttpHeader::HttpHeader() {
fields = fields; fields = fields;
} }
HttpHeader::HttpHeader(Socket *socket) : HttpHeader::HttpHeader() { HttpHeader::HttpHeader(Socket *socket) : HttpHeader::HttpHeader() {
parse(socket); parse(socket);
} }
void HttpHeader::parse(Socket *socket) { void HttpHeader::parse(Socket *socket) {
while (true) { while (true) {
string line = socket->receiveLine(); string line = socket->receiveLine();
if (line.length() == 0) { if (line.length() == 0) {
break; break;
} else { } else {
unsigned long pos = line.find(':'); unsigned long pos = line.find(':');
if (pos == string::npos) { if (pos == string::npos) {
throw (char *) "Malformed header"; throw (char *) "Malformed header";
} }
string index = line.substr(0, pos); string index = line.substr(0, pos);
string data = line.substr(pos + 1, line.length() - pos); string data = line.substr(pos + 1, line.length() - pos);
while (index[0] == ' ') index.erase(index.begin() + 0); while (index[0] == ' ') index.erase(index.begin() + 0);
while (index[index.length() - 1] == ' ') index.erase(index.end() - 1); while (index[index.length() - 1] == ' ') index.erase(index.end() - 1);
while (data[0] == ' ') data.erase(data.begin() + 0); while (data[0] == ' ') data.erase(data.begin() + 0);
while (data[data.length() - 1] == ' ') data.erase(data.end() - 1); while (data[data.length() - 1] == ' ') data.erase(data.end() - 1);
setField(index, data); setField(index, data);
} }
} }
} }
@ -62,7 +62,7 @@ void HttpHeader::parse(Socket *socket) {
* Default Destructor * Default Destructor
*/ */
HttpHeader::~HttpHeader() { HttpHeader::~HttpHeader() {
fields.clear(); fields.clear();
} }
@ -73,12 +73,12 @@ HttpHeader::~HttpHeader() {
* @param data The field data * @param data The field data
*/ */
void HttpHeader::setField(string index, string data) { void HttpHeader::setField(string index, string data) {
removeField(index); removeField(index);
fields.insert(make_pair(index, data)); fields.insert(make_pair(index, data));
} }
void HttpHeader::removeField(string index) { void HttpHeader::removeField(string index) {
fields.erase(index); fields.erase(index);
} }
/** /**
@ -88,34 +88,34 @@ void HttpHeader::removeField(string index) {
* @return The field data * @return The field data
*/ */
string HttpHeader::getField(string index) { string HttpHeader::getField(string index) {
auto i = fields.find(index); auto i = fields.find(index);
if (i != fields.end()) { if (i != fields.end()) {
return fields.at(index); return fields.at(index);
} else { } else {
return ""; return "";
} }
} }
bool HttpHeader::isExistingField(string index) { bool HttpHeader::isExistingField(string index) {
auto i = fields.find(index); auto i = fields.find(index);
return i != fields.end(); return i != fields.end();
} }
string HttpHeader::toString() { string HttpHeader::toString() {
string header = ""; string header = "";
for (auto it = fields.begin(); it != fields.end(); it++ ) { for (auto it = fields.begin(); it != fields.end(); it++ ) {
header += it->first + ": " + it->second + "\r\n"; header += it->first + ": " + it->second + "\r\n";
} }
return header; return header;
} }
string HttpHeader::cgiExport() { string HttpHeader::cgiExport() {
string header = ""; string header = "";
for (auto it = fields.begin(); it != fields.end(); it++ ) { for (auto it = fields.begin(); it != fields.end(); it++ ) {
header += "HTTP_" + to_cgi(it->first) + "=" + cli_encode(it->second) + " "; header += "HTTP_" + to_cgi(it->first) + "=" + cli_encode(it->second) + " ";
} }
return header; return header;
} }

View File

@ -12,9 +12,9 @@
using namespace std; using namespace std;
struct comp { struct comp {
bool operator()(const std::string& lhs, const std::string& rhs) const { bool operator()(const std::string& lhs, const std::string& rhs) const {
return strcasecmp(lhs.c_str(), rhs.c_str()) < 0; return strcasecmp(lhs.c_str(), rhs.c_str()) < 0;
} }
}; };
/** /**
@ -25,28 +25,28 @@ struct comp {
*/ */
class HttpHeader { class HttpHeader {
private: private:
map<string, string, comp> fields; map<string, string, comp> fields;
public: public:
HttpHeader(); HttpHeader();
explicit HttpHeader(Socket *socket); explicit HttpHeader(Socket *socket);
~HttpHeader(); ~HttpHeader();
void setField(string index, string data); void setField(string index, string data);
string getField(string index); string getField(string index);
void removeField(string index); void removeField(string index);
bool isExistingField(string index); bool isExistingField(string index);
void parse(Socket *socket); void parse(Socket *socket);
string toString(); string toString();
string cgiExport(); string cgiExport();
}; };

View File

@ -9,95 +9,95 @@
HttpRequest::HttpRequest() { HttpRequest::HttpRequest() {
this->header = HttpHeader(); this->header = HttpHeader();
} }
HttpRequest::HttpRequest(Socket *socket) : HttpRequest::HttpRequest() { HttpRequest::HttpRequest(Socket *socket) : HttpRequest::HttpRequest() {
parseHeader(socket); parseHeader(socket);
} }
HttpRequest::HttpRequest(string method, string path, string version) : HttpRequest::HttpRequest() { HttpRequest::HttpRequest(string method, string path, string version) : HttpRequest::HttpRequest() {
this->method = std::move(method); this->method = std::move(method);
this->path = std::move(path); this->path = std::move(path);
this->version = std::move(version); this->version = std::move(version);
} }
void HttpRequest::parseHeader(Socket *socket) { void HttpRequest::parseHeader(Socket *socket) {
string line = socket->receiveLine(); string line = socket->receiveLine();
unsigned long pos1 = line.find(' '); unsigned long pos1 = line.find(' ');
unsigned long pos2; unsigned long pos2;
bool invalid = false; bool invalid = false;
if (pos1 != string::npos) { if (pos1 != string::npos) {
pos2 = line.find(' ', pos1 + 1); pos2 = line.find(' ', pos1 + 1);
if (pos2 != string::npos) { if (pos2 != string::npos) {
method = line.substr(0, pos1); method = line.substr(0, pos1);
for (auto &c: method) c = (char) toupper(c); for (auto &c: method) c = (char) toupper(c);
path = line.substr(pos1 + 1, pos2 - pos1 - 1); path = line.substr(pos1 + 1, pos2 - pos1 - 1);
version = line.substr(pos2 + 6, 3); version = line.substr(pos2 + 6, 3);
} else { } else {
invalid = true; invalid = true;
} }
} else { } else {
pos2 = string::npos; pos2 = string::npos;
invalid = true; 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'))) { 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; invalid = true;
} }
if (invalid) { if (invalid) {
method = ""; method = "";
path = ""; path = "";
version = ""; version = "";
throw (char *) "Malformed header"; throw (char *) "Malformed header";
} }
header.parse(socket); header.parse(socket);
} }
string HttpRequest::getMethod() { string HttpRequest::getMethod() {
return method; return method;
} }
string HttpRequest::getPath() { string HttpRequest::getPath() {
return path; return path;
} }
string HttpRequest::getVersion() { string HttpRequest::getVersion() {
return version; return version;
} }
void HttpRequest::setMethod(string method) { void HttpRequest::setMethod(string method) {
this->method = std::move(method); this->method = std::move(method);
} }
void HttpRequest::setPath(string path) { void HttpRequest::setPath(string path) {
this->path = std::move(path); this->path = std::move(path);
} }
void HttpRequest::setVersion(string version) { void HttpRequest::setVersion(string version) {
this->version = std::move(version); this->version = std::move(version);
} }
string HttpRequest::getField(string index) { string HttpRequest::getField(string index) {
return header.getField(std::move(index)); return header.getField(std::move(index));
} }
void HttpRequest::setField(string index, string data) { void HttpRequest::setField(string index, string data) {
header.setField(std::move(index), std::move(data)); header.setField(std::move(index), std::move(data));
} }
bool HttpRequest::isExistingField(string index) { bool HttpRequest::isExistingField(string index) {
return header.isExistingField(std::move(index)); return header.isExistingField(std::move(index));
} }
string HttpRequest::cgiExport() { string HttpRequest::cgiExport() {
return header.cgiExport(); return header.cgiExport();
} }

View File

@ -11,41 +11,41 @@ using namespace std;
class HttpRequest { class HttpRequest {
private: private:
HttpHeader header; HttpHeader header;
string method; string method;
string path; string path;
string version; string version;
public: public:
HttpRequest(); HttpRequest();
explicit HttpRequest(Socket *socket); explicit HttpRequest(Socket *socket);
HttpRequest(string method, string path, string version = "1.1"); HttpRequest(string method, string path, string version = "1.1");
void parseHeader(Socket *socket); void parseHeader(Socket *socket);
void sendHeader(Socket *socket); void sendHeader(Socket *socket);
string getField(string index); string getField(string index);
void setField(string index, string data); void setField(string index, string data);
bool isExistingField(string index); bool isExistingField(string index);
string getMethod(); string getMethod();
string getPath(); string getPath();
string getVersion(); string getVersion();
void setMethod(string method); void setMethod(string method);
void setPath(string path); void setPath(string path);
void setVersion(string version); void setVersion(string version);
string cgiExport(); string cgiExport();
}; };

View File

@ -9,56 +9,56 @@
HttpResponse::HttpResponse() { HttpResponse::HttpResponse() {
this->header = HttpHeader(); this->header = HttpHeader();
} }
HttpResponse::HttpResponse(Socket *socket) : HttpResponse::HttpResponse() { HttpResponse::HttpResponse(Socket *socket) : HttpResponse::HttpResponse() {
this->parseHeader(socket); this->parseHeader(socket);
} }
HttpResponse::HttpResponse(int statuscode, string version) : HttpResponse::HttpResponse(::getStatusCode(statuscode), std::move(version)) { HttpResponse::HttpResponse(int statuscode, string version) : HttpResponse::HttpResponse(::getStatusCode(statuscode), std::move(version)) {
} }
HttpResponse::HttpResponse(HttpStatusCode statuscode, string version) : HttpResponse::HttpResponse() { HttpResponse::HttpResponse(HttpStatusCode statuscode, string version) : HttpResponse::HttpResponse() {
this->statuscode = statuscode; this->statuscode = statuscode;
this->version = std::move(version); this->version = std::move(version);
} }
void HttpResponse::sendHeader(Socket *socket) { void HttpResponse::sendHeader(Socket *socket) {
socket->send("HTTP/" + version + " " + to_string(statuscode.code) + " " + statuscode.message + "\r\n" + socket->send("HTTP/" + version + " " + to_string(statuscode.code) + " " + statuscode.message + "\r\n" +
header.toString() + "\r\n"); header.toString() + "\r\n");
} }
string HttpResponse::getField(string index) { string HttpResponse::getField(string index) {
return header.getField(std::move(index)); return header.getField(std::move(index));
} }
void HttpResponse::setField(string index, string data) { void HttpResponse::setField(string index, string data) {
header.setField(std::move(index), std::move(data)); header.setField(std::move(index), std::move(data));
} }
bool HttpResponse::isExistingField(string index) { bool HttpResponse::isExistingField(string index) {
return header.isExistingField(std::move(index)); return header.isExistingField(std::move(index));
} }
HttpStatusCode HttpResponse::getStatusCode() { HttpStatusCode HttpResponse::getStatusCode() {
return statuscode; return statuscode;
} }
string HttpResponse::getVersion() { string HttpResponse::getVersion() {
return version; return version;
} }
void HttpResponse::setStatusCode(HttpStatusCode statuscode) { void HttpResponse::setStatusCode(HttpStatusCode statuscode) {
this->statuscode = statuscode; this->statuscode = statuscode;
} }
void HttpResponse::setStatusCode(int statuscode) { void HttpResponse::setStatusCode(int statuscode) {
this->statuscode = ::getStatusCode(statuscode); this->statuscode = ::getStatusCode(statuscode);
} }
void HttpResponse::setVersion(string version) { void HttpResponse::setVersion(string version) {
this->version = std::move(version); this->version = std::move(version);
} }
void HttpResponse::parseHeader(Socket *socket) { void HttpResponse::parseHeader(Socket *socket) {
@ -66,5 +66,5 @@ void HttpResponse::parseHeader(Socket *socket) {
} }
void HttpResponse::removeField(string index) { void HttpResponse::removeField(string index) {
header.removeField(std::move(index)); header.removeField(std::move(index));
} }

View File

@ -11,40 +11,40 @@
class HttpResponse { class HttpResponse {
private: private:
HttpHeader header; HttpHeader header;
HttpStatusCode statuscode; HttpStatusCode statuscode;
string version; string version;
public: public:
HttpResponse(); HttpResponse();
explicit HttpResponse(Socket *socket); explicit HttpResponse(Socket *socket);
explicit HttpResponse(int statuscode, string version = "1.1"); explicit HttpResponse(int statuscode, string version = "1.1");
explicit HttpResponse(HttpStatusCode statuscode, string version = "1.1"); explicit HttpResponse(HttpStatusCode statuscode, string version = "1.1");
void parseHeader(Socket *socket); void parseHeader(Socket *socket);
void sendHeader(Socket *socket); void sendHeader(Socket *socket);
string getField(string index); string getField(string index);
void setField(string index, string data); void setField(string index, string data);
bool isExistingField(string index); bool isExistingField(string index);
HttpStatusCode getStatusCode(); HttpStatusCode getStatusCode();
string getVersion(); string getVersion();
void setStatusCode(HttpStatusCode statuscode); void setStatusCode(HttpStatusCode statuscode);
void setStatusCode(int statuscode); void setStatusCode(int statuscode);
void setVersion(string version); void setVersion(string version);
void removeField(string index); void removeField(string index);
}; };
#endif #endif

View File

@ -9,57 +9,57 @@
HttpStatusCode httpStatusCodes[] = { HttpStatusCode httpStatusCodes[] = {
HttpStatusCode{100, "Informational", "Continue", ""}, HttpStatusCode{100, "Informational", "Continue", ""},
HttpStatusCode{101, "Informational", "Switching Protocols", ""}, HttpStatusCode{101, "Informational", "Switching Protocols", ""},
HttpStatusCode{200, "Success", "OK", ""}, HttpStatusCode{200, "Success", "OK", ""},
HttpStatusCode{201, "Success", "Created", ""}, HttpStatusCode{201, "Success", "Created", ""},
HttpStatusCode{202, "Success", "Accepted", ""}, HttpStatusCode{202, "Success", "Accepted", ""},
HttpStatusCode{203, "Success", "Non-Authoritative Information", ""}, HttpStatusCode{203, "Success", "Non-Authoritative Information", ""},
HttpStatusCode{204, "Success", "No Centent", ""}, HttpStatusCode{204, "Success", "No Centent", ""},
HttpStatusCode{205, "Success", "Reset Content", ""}, HttpStatusCode{205, "Success", "Reset Content", ""},
HttpStatusCode{206, "Success", "Partial Content", ""}, HttpStatusCode{206, "Success", "Partial Content", ""},
HttpStatusCode{300, "Redirection", "Multiple Choices", ""}, HttpStatusCode{300, "Redirection", "Multiple Choices", ""},
HttpStatusCode{301, "Redirection", "Moved Permanently", ""}, HttpStatusCode{301, "Redirection", "Moved Permanently", ""},
HttpStatusCode{302, "Redirection", "Found", ""}, HttpStatusCode{302, "Redirection", "Found", ""},
HttpStatusCode{303, "Redirection", "See Other", ""}, HttpStatusCode{303, "Redirection", "See Other", ""},
HttpStatusCode{304, "Redirection", "Not Modified", ""}, HttpStatusCode{304, "Redirection", "Not Modified", ""},
HttpStatusCode{305, "Redirection", "Use Proxy", ""}, HttpStatusCode{305, "Redirection", "Use Proxy", ""},
HttpStatusCode{307, "Redirection", "Temporary Redirect", ""}, HttpStatusCode{307, "Redirection", "Temporary Redirect", ""},
HttpStatusCode{400, "Client Error", "Bad Request", ""}, HttpStatusCode{400, "Client Error", "Bad Request", ""},
HttpStatusCode{401, "Client Error", "Unauthorized", ""}, HttpStatusCode{401, "Client Error", "Unauthorized", ""},
HttpStatusCode{402, "Client Error", "Payment Required", ""}, HttpStatusCode{402, "Client Error", "Payment Required", ""},
HttpStatusCode{403, "Client Error", "Forbidden", ""}, HttpStatusCode{403, "Client Error", "Forbidden", ""},
HttpStatusCode{404, "Client Error", "Not Found", ""}, HttpStatusCode{404, "Client Error", "Not Found", ""},
HttpStatusCode{405, "Client Error", "Method Not Allowed", ""}, HttpStatusCode{405, "Client Error", "Method Not Allowed", ""},
HttpStatusCode{406, "Client Error", "Not Acceptable", ""}, HttpStatusCode{406, "Client Error", "Not Acceptable", ""},
HttpStatusCode{407, "Client Error", "Proxy Authentication Required", ""}, HttpStatusCode{407, "Client Error", "Proxy Authentication Required", ""},
HttpStatusCode{408, "Client Error", "Request Timeout", ""}, HttpStatusCode{408, "Client Error", "Request Timeout", ""},
HttpStatusCode{409, "Client Error", "Conflict", ""}, HttpStatusCode{409, "Client Error", "Conflict", ""},
HttpStatusCode{410, "Client Error", "Gone", ""}, HttpStatusCode{410, "Client Error", "Gone", ""},
HttpStatusCode{411, "Client Error", "Length Required", ""}, HttpStatusCode{411, "Client Error", "Length Required", ""},
HttpStatusCode{412, "Client Error", "Precondition Failed", ""}, HttpStatusCode{412, "Client Error", "Precondition Failed", ""},
HttpStatusCode{413, "Client Error", "Request Entity Too Large", ""}, HttpStatusCode{413, "Client Error", "Request Entity Too Large", ""},
HttpStatusCode{414, "Client Error", "Request-URI Too Long", ""}, HttpStatusCode{414, "Client Error", "Request-URI Too Long", ""},
HttpStatusCode{415, "Client Error", "Unsupported Media Type", ""}, HttpStatusCode{415, "Client Error", "Unsupported Media Type", ""},
HttpStatusCode{416, "Client Error", "Requested Range Not Satisfiable", ""}, HttpStatusCode{416, "Client Error", "Requested Range Not Satisfiable", ""},
HttpStatusCode{417, "Client Error", "Expectation Failed", ""}, HttpStatusCode{417, "Client Error", "Expectation Failed", ""},
HttpStatusCode{500, "Server Error", "Internal Server Error", ""}, HttpStatusCode{500, "Server Error", "Internal Server Error", ""},
HttpStatusCode{501, "Server Error", "Not Implemented", ""}, HttpStatusCode{501, "Server Error", "Not Implemented", ""},
HttpStatusCode{502, "Server Error", "Bad Gateway", ""}, HttpStatusCode{502, "Server Error", "Bad Gateway", ""},
HttpStatusCode{503, "Server Error", "Service Unavailable", ""}, HttpStatusCode{503, "Server Error", "Service Unavailable", ""},
HttpStatusCode{504, "Server Error", "Gateway Timeout", ""}, HttpStatusCode{504, "Server Error", "Gateway Timeout", ""},
HttpStatusCode{505, "Server Error", "HTTP Version Not Supported", ""}, HttpStatusCode{505, "Server Error", "HTTP Version Not Supported", ""},
}; };
HttpStatusCode getStatusCode(int statuscode) { HttpStatusCode getStatusCode(int statuscode) {
for (HttpStatusCode sc : httpStatusCodes) { for (HttpStatusCode sc : httpStatusCodes) {
if (sc.code == statuscode) { if (sc.code == statuscode) {
return sc; return sc;
} }
} }
throw (char *) "Invalid status code"; throw (char *) "Invalid status code";
} }

View File

@ -4,10 +4,10 @@
#define NECRONDA_HTTP_STATUSCODE #define NECRONDA_HTTP_STATUSCODE
typedef struct { typedef struct {
short code; // The status code (e.g. 200) short code; // The status code (e.g. 200)
const char *type; // The status type type (e.g Success) const char *type; // The status type type (e.g Success)
const char *message; // The status code message (e.g. OK) const char *message; // The status code message (e.g. OK)
const char *description; // The status code description (currently not used) const char *description; // The status code description (currently not used)
} HttpStatusCode; } HttpStatusCode;
HttpStatusCode getStatusCode(int statuscode); HttpStatusCode getStatusCode(int statuscode);

View File

@ -6,36 +6,36 @@
stds procopen(const char* command) { stds procopen(const char* command) {
int pipes[3][2]; int pipes[3][2];
pipe(pipes[PARENT_READ_PIPE]); pipe(pipes[PARENT_READ_PIPE]);
pipe(pipes[PARENT_WRITE_PIPE]); pipe(pipes[PARENT_WRITE_PIPE]);
pipe(pipes[PARENT_ERROR_PIPE]); pipe(pipes[PARENT_ERROR_PIPE]);
int pid = fork(); int pid = fork();
if(pid == 0) { if(pid == 0) {
dup2(CHILD_READ_FD, STDIN_FILENO); dup2(CHILD_READ_FD, STDIN_FILENO);
dup2(CHILD_WRITE_FD, STDOUT_FILENO); dup2(CHILD_WRITE_FD, STDOUT_FILENO);
dup2(CHILD_ERROR_FD, STDERR_FILENO); dup2(CHILD_ERROR_FD, STDERR_FILENO);
close(CHILD_READ_FD); close(CHILD_READ_FD);
close(CHILD_WRITE_FD); close(CHILD_WRITE_FD);
close(CHILD_ERROR_FD); close(CHILD_ERROR_FD);
close(PARENT_READ_FD); close(PARENT_READ_FD);
close(PARENT_WRITE_FD); close(PARENT_WRITE_FD);
close(PARENT_ERROR_FD); close(PARENT_ERROR_FD);
system(command); system(command);
exit(0); exit(0);
} else { } else {
close(CHILD_READ_FD); close(CHILD_READ_FD);
close(CHILD_WRITE_FD); close(CHILD_WRITE_FD);
close(CHILD_ERROR_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}; return stds{fdopen(PARENT_WRITE_FD, "w"), fdopen(PARENT_READ_FD, "r"), fdopen(PARENT_ERROR_FD, "r"), (pid_t) pid};
} }
} }

View File

@ -28,10 +28,10 @@
typedef struct { typedef struct {
FILE* stdin; FILE* stdin;
FILE* stdout; FILE* stdout;
FILE* stderr; FILE* stderr;
pid_t pid; pid_t pid;
} stds; } stds;