This commit is contained in:
2018-05-31 15:13:12 +02:00
parent c9c053d95a
commit 13bef5e9cc
7 changed files with 325 additions and 33 deletions

2
run.sh
View File

@ -4,4 +4,4 @@ make && \
echo -e "-- Successfully finished compiling!\n" && \
sleep 0.0625 && \
echo -e "-- Starting Server...\n" && \
./bin/necronda-server
authbind ./bin/necronda-server

View File

@ -50,6 +50,7 @@ URI::URI(string webroot, string reqpath) {
if (webroot[webroot.length() - 1] == '/') {
webroot.erase(webroot.length() - 1);
}
reqpath = url_decode(reqpath);
if (reqpath.find("/../") != string::npos) {
throw (char *) "Invalid path";
}
@ -116,8 +117,9 @@ string URI::getFilePath() {
}
string URI::getRelativeFilePath() {
string rel = getRelativePath();
// TODO
string str = getFilePath();
long len = getWebRoot().length();
return str.substr(len, str.length() - len);
}
string URI::getNewPath() {
@ -127,7 +129,7 @@ string URI::getNewPath() {
}
}
if (relpath != reqpath) {
return relpath + (queryinit? "?" + query : "");
return url_encode(relpath) + (queryinit? "?" + query : "");
} else {
return "";
}
@ -138,7 +140,7 @@ FILE *URI::openFile() {
}
string URI::getFilePathInfo() {
return getAbsolutePath().erase(getFilePath().length(), getAbsolutePath().length());
return ""; //getAbsolutePath().erase(getFilePath().length(), getAbsolutePath().length());
}
string URI::getFileType() {
@ -146,7 +148,7 @@ string URI::getFileType() {
}
bool URI::isStatic() {
return true;
return getExtension(filepath) != "php";
}
string URI::getQuery() {

View File

@ -19,18 +19,23 @@
#include "necronda-server.h"
#include "network/http/HttpStatusCode.h"
#include "URI.h"
#include "procopen.h"
/**
* Writes log messages to the console
* @param prefix The connection prefix
* @param string The string to be written
* @param str The string to be written
*/
void log(const char *prefix, const string &string) {
printf("%s%s\r\n", prefix, string.c_str());
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");
}
string getETag(string filename) {
ifstream etags = ifstream("/var/necronda/ETags");
@ -106,6 +111,24 @@ string getETag(string filename) {
return md5;
}
#include <iostream>
#include <wait.h>
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;
}
/**
* Handles (keep-alive) HTTP connections
* @param prefix The connection prefix
@ -114,8 +137,9 @@ string getETag(string filename) {
* @param num The Connection Number in the client
* @return Should the server wait for another header?
*/
bool connection_handler(const char *prefix, Socket *socket, long id, long num) {
bool connection_handler(const char *preprefix, const char *col1, const char *col2, Socket *socket, long id, long num) {
bool error = false;
char *prefix = (char *) preprefix;
try {
HttpConnection req(socket);
try {
@ -131,19 +155,32 @@ bool connection_handler(const char *prefix, Socket *socket, long id, long num) {
req.respond(400);
} else {
string host = req.getField("Host");
long pos = host.find(':');
if (pos != string::npos) {
host.erase(pos, host.length() - pos);
}
string str = string(prefix);
unsigned long pos = str.find('[', 8);
string n = str.substr(0, 8) + host + str.substr(pos - 1, str.length() - pos + 1);
FILE *name = popen(("dig +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());
}
char buffer[1024];
sprintf(buffer, "[\x1B[1m%s\x1B[0m][%i]%s[%s][%i]%s ", host.c_str(), socket->getSocketPort(), col1,
hostbuffer, socket->getPeerPort(), col2);
char buffer[256];
sprintf(buffer, "%s", n.c_str());
prefix = buffer;
URI path = URI(getWebRoot(host), req.getPath());
log(prefix, req.getMethod() + " " + req.getPath());
log(prefix, "\x1B[1m" + req.getMethod() + " " + req.getPath() + "\x1B[0m");
FILE *file = path.openFile();
pid_t childpid = 0;
if (!path.getNewPath().empty()) {
req.redirect(302, path.getNewPath());
@ -157,6 +194,8 @@ bool connection_handler(const char *prefix, Socket *socket, long id, long num) {
if (type.find("inode/") == 0) {
req.respond(403);
} else if (path.getRelativeFilePath().find("/.") != string::npos) {
req.respond(403);
} else {
req.setField("Content-Type", type);
req.setField("Last-Modified", getHttpDate(path.getFilePath()));
@ -183,7 +222,6 @@ bool connection_handler(const char *prefix, Socket *socket, long id, long num) {
if (req.getMethod() != "GET" && req.getMethod() != "POST" && req.getMethod() != "PUT") {
invalidMethod = true;
}
system("php");
}
if (invalidMethod) {
@ -191,15 +229,82 @@ bool connection_handler(const char *prefix, Socket *socket, long id, long num) {
} else if (etag) {
req.respond(304);
} else {
int statuscode = 200;
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(hostbuffer) +
" 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") +
" /usr/bin/php-cgi";
bool compress = type.find("text/") == 0 && req.isExistingField("Accept-Encoding") &&
stds pipes = procopen(cmd.c_str());
childpid = pipes.pid;
char fdbuffer[4096];
if (req.getMethod() == "POST" || req.getMethod() == "PUT") {
long len = req.isExistingField("Content-Length") ? strtol(req.getField("Content-Length").c_str(), nullptr, 10) : -1;
socket->receive(pipes.stdin);
}
fclose(pipes.stdin);
string line;
while (!(line = read_line(pipes.stderr)).empty()) {
log_error(prefix, line);
}
fclose(pipes.stderr);
while (!(line = read_line(pipes.stdout)).empty()) {
long 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 == "Location") {
statuscode = 303;
}
req.setField(index, data);
}
fclose(file);
file = pipes.stdout;
}
bool compress = path.isStatic() && type.find("text/") == 0 && req.isExistingField("Accept-Encoding") &&
req.getField("Accept-Encoding").find("deflate") != string::npos;
if (compress) {
req.setField("Accept-Ranges", "none");
}
if (req.isExistingField("Range")) {
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);
@ -231,16 +336,39 @@ bool connection_handler(const char *prefix, Socket *socket, long id, long num) {
}
}
} else {
req.respond(200, file, compress);
req.respond(statuscode, file, compress);
}
}
}
fclose(file);
if (childpid > 0) {
waitpid(childpid, nullptr, 0);
}
}
}
}
HttpStatusCode status = req.getStatusCode();
log(prefix, to_string(status.code) + " " + status.message + " (" + formatTime(req.getDuration()) + ")");
int code = status.code;
string color = "";
string comment = "";
if ((code >= 200 && code < 300) || code == 304) {
color = "\x1B[1;32m"; // Success (Cached): 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") : "<invalid>");
} 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();
}
log(prefix,
color + to_string(status.code) + " " + status.message + comment + " (" + formatTime(req.getDuration()) +
")\x1B[0m");
} catch (char *msg) {
HttpStatusCode status = req.getStatusCode();
log(prefix, to_string(status.code) + " " + status.message + " (" + formatTime(req.getDuration()) + ")");
@ -290,26 +418,25 @@ bool connection_handler(const char *prefix, Socket *socket, long id, long num) {
*/
void client_handler(Socket *socket, long id, bool ssl) {
const char *prefix;
char const *col1;
char const *col2 = "\x1B[0m";
{
char const *col1;
char const *col2 = "\x1B[0m";
auto group = (int) (id % 6);
if (group == 0) {
col1 = "\x1B[1;31m";
col1 = "\x1B[0;31m"; // Red
} else if (group == 1) {
col1 = "\x1B[1;32m";
col1 = "\x1B[0;32m"; // Green
} else if (group == 2) {
col1 = "\x1B[1;34m";
col1 = "\x1B[0;34m"; // Blue
} else if (group == 3) {
col1 = "\x1B[1;33m";
col1 = "\x1B[0;33m"; // Yellow
} else if (group == 4) {
col1 = "\x1B[1;35m";
col1 = "\x1B[0;35m"; // Magenta
} else {
col1 = "\x1B[1;36m";
col1 = "\x1B[0;36m"; // Cyan
}
string *a = new string((string)
col1 + "[" + socket->getSocketAddress()->toString() + "][" +
to_string(socket->getSocketPort()) + "]" +
string *a = new string("[" + socket->getSocketAddress()->toString() + "][" +
to_string(socket->getSocketPort()) + "]" + col1 +
"[" + socket->getPeerAddress()->toString() + "][" + to_string(socket->getPeerPort()) +
"]" + col2 + " ");
prefix = a->c_str();
@ -338,7 +465,7 @@ void client_handler(Socket *socket, long id, bool ssl) {
long reqnum = 0;
if (!err) {
while (connection_handler(prefix, socket, id, ++reqnum));
while (connection_handler(prefix, col1, col2, socket, id, ++reqnum));
reqnum--;
}

View File

@ -142,6 +142,75 @@ string getWebRoot(string host) {
}
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 = nullptr;
size_t len = 0;
ssize_t read;
if ((read = getline(&line, &len, file)) < 0) {
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"

View File

@ -33,5 +33,17 @@ 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

41
src/procopen.cpp Normal file
View File

@ -0,0 +1,41 @@
//
// 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};
}
}

41
src/procopen.h Normal file
View File

@ -0,0 +1,41 @@
//
// Created by lorenz on 5/30/18.
//
#include <zconf.h>
#include <cstdlib>
#include <cstdio>
#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