13 Commits
v4.0 ... v4.1

12 changed files with 292 additions and 136 deletions

2
.gitignore vendored
View File

@ -4,6 +4,4 @@
!run.sh
!Makefile
!.gitignore
!CppNet
!CppNet/**
!README.md

View File

@ -7,11 +7,11 @@ packages:
compile:
@mkdir -p bin
gcc src/necronda-server.c -o bin/necronda-server -std=c11 -lssl -lcrypto -lmagic -lz
gcc src/necronda-server.c -o bin/necronda-server -std=c11 -lssl -lcrypto -lmagic -lz -lmaxminddb
compile-debian:
@mkdir -p bin
gcc src/necronda-server.c -o bin/necronda-server -std=c11 -lssl -lcrypto -lmagic -lz \
gcc src/necronda-server.c -o bin/necronda-server -std=c11 -lssl -lcrypto -lmagic -lz -lmaxminddb \
-D MAGIC_FILE="\"/usr/share/file/magic.mgc\"" \
-D PHP_FPM_SOCKET="\"/var/run/php/php7.3-fpm.sock\""

View File

@ -13,12 +13,10 @@ int magic_init() {
magic = magic_open(MAGIC_MIME);
if (magic == NULL) {
fprintf(stderr, ERR_STR "Unable to open magic cookie: %s" CLR_STR "\n", strerror(errno));
fflush(stderr);
return -1;
}
if (magic_load(magic, MAGIC_FILE) != 0) {
fprintf(stderr, ERR_STR "Unable to load magic cookie: %s" CLR_STR "\n", magic_error(magic));
fflush(stderr);
return -2;
}
return 0;
@ -35,7 +33,6 @@ int cache_process() {
int shm_id = shmget(SHM_KEY, FILE_CACHE_SIZE * sizeof(cache_entry), 0);
if (shm_id < 0) {
fprintf(stderr, ERR_STR "Unable to create shared memory: %s" CLR_STR "\n", strerror(errno));
fflush(stderr);
return -1;
}
@ -43,7 +40,6 @@ int cache_process() {
void *shm_rw = shmat(shm_id, NULL, 0);
if (shm_rw == (void *) -1) {
fprintf(stderr, ERR_STR "Unable to attach shared memory (rw): %s" CLR_STR "\n", strerror(errno));
fflush(stderr);
return -2;
}
cache = shm_rw;
@ -51,7 +47,6 @@ int cache_process() {
if (mkdir("/var/necronda-server/", 0755) < 0) {
if (errno != EEXIST) {
fprintf(stderr, ERR_STR "Unable to create directory '/var/necronda-server/': %s" CLR_STR "\n", strerror(errno));
fflush(stderr);
return -3;
}
}
@ -106,14 +101,12 @@ int cache_process() {
if (comp_file == NULL) {
compress = 0;
fprintf(stderr, ERR_STR "Unable to open cache file: %s" CLR_STR "\n", strerror(errno));
fflush(stderr);
} else {
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
if (deflateInit(&strm, level) != Z_OK) {
fprintf(stderr, ERR_STR "Unable to init deflate: %s" CLR_STR "\n", strerror(errno));
fflush(stderr);
compress = 0;
fclose(comp_file);
}
@ -168,14 +161,12 @@ int cache_init() {
int shm_id = shmget(SHM_KEY, FILE_CACHE_SIZE * sizeof(cache_entry), IPC_CREAT | IPC_EXCL | 0600);
if (shm_id < 0) {
fprintf(stderr, ERR_STR "Unable to create shared memory: %s" CLR_STR "\n", strerror(errno));
fflush(stderr);
return -2;
}
void *shm = shmat(shm_id, NULL, SHM_RDONLY);
if (shm == (void *) -1) {
fprintf(stderr, ERR_STR "Unable to attach shared memory (ro): %s" CLR_STR "\n", strerror(errno));
fflush(stderr);
return -3;
}
cache = shm;
@ -183,7 +174,6 @@ int cache_init() {
void *shm_rw = shmat(shm_id, NULL, 0);
if (shm_rw == (void *) -1) {
fprintf(stderr, ERR_STR "Unable to attach shared memory (rw): %s" CLR_STR "\n", strerror(errno));
fflush(stderr);
return -4;
}
cache = shm_rw;
@ -202,11 +192,9 @@ int cache_init() {
} else if (pid > 0) {
// parent
fprintf(stderr, "Started child process with PID %i as cache-updater\n", pid);
fflush(stderr);
children[0] = pid;
} else {
fprintf(stderr, ERR_STR "Unable to create child process: %s" CLR_STR "\n", strerror(errno));
fflush(stderr);
return -5;
}
@ -217,10 +205,8 @@ int cache_unload() {
int shm_id = shmget(SHM_KEY, 0, 0);
if (shm_id < 0) {
fprintf(stderr, ERR_STR "Unable to create shared memory: %s" CLR_STR "\n", strerror(errno));
fflush(stderr);
} else if (shmctl(shm_id, IPC_RMID, NULL) < 0) {
fprintf(stderr, ERR_STR "Unable to configure shared memory: %s" CLR_STR "\n", strerror(errno));
fflush(stderr);
}
shmdt(cache);
return 0;
@ -248,7 +234,7 @@ int cache_update_entry(int entry_num, const char *filename, const char *webroot)
char type_new[24];
sprintf(type_new, "%s", type);
if (strcmp(type, "text/plain") == 0) {
if (strncmp(filename + strlen(filename) - 4, ".css", 4) == 0) {
if (strcmp(filename + strlen(filename) - 4, ".css") == 0) {
sprintf(type_new, "text/css");
} else if (strcmp(filename + strlen(filename) - 3, ".js") == 0) {
sprintf(type_new, "text/javascript");

View File

@ -15,7 +15,7 @@
int server_keep_alive = 1;
char *client_addr_str, *client_addr_str_ptr, *server_addr_str, *server_addr_str_ptr,
*log_client_prefix, *log_conn_prefix, *log_req_prefix,
*client_host_str;
*client_host_str, *client_geoip;
struct timeval client_timeout = {.tv_sec = CLIENT_TIMEOUT, .tv_usec = 0};
@ -25,7 +25,7 @@ char *get_webroot(const char *http_host) {
while (webroot_base[len - 1] == '/') len--;
long pos = strchr(http_host, ':') - http_host;
sprintf(webroot, "%.*s/%.*s", (int) len, webroot_base, (int) (pos < 0 ? strlen(http_host) : pos), http_host);
return webroot;
return path_is_directory(webroot) ? webroot : NULL;
}
void client_terminate() {
@ -99,7 +99,7 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int
}
hdr_connection = http_get_header_field(&req.hdr, "Connection");
client_keep_alive = hdr_connection != NULL && strncmp(hdr_connection, "keep-alive", 10) == 0;
client_keep_alive = hdr_connection != NULL && strcmp(hdr_connection, "keep-alive") == 0;
host = http_get_header_field(&req.hdr, "Host");
if (host == NULL || strchr(host, '/') != NULL) {
res.status = http_get_status(400);
@ -111,15 +111,16 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int
log_prefix = log_req_prefix;
print(BLD_STR "%s %s" CLR_STR, req.method, req.uri);
// TODO Reverse Proxy
webroot = get_webroot(host);
if (webroot == NULL) {
res.status = http_get_status(307);
sprintf(buf0, "https://%s%s", NECRONDA_DEFAULT, req.uri);
http_add_header_field(&req.hdr, "Location", buf0);
sprintf(buf0, "https://%s%s", DEFAULT_HOST, req.uri);
http_add_header_field(&res.hdr, "Location", buf0);
goto respond;
}
dir_mode = URI_DIR_MODE_FORBIDDEN;
dir_mode = URI_DIR_MODE_INFO;
http_uri uri;
ret = uri_init(&uri, webroot, req.uri, dir_mode);
if (ret != 0) {
@ -166,7 +167,7 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int
res.status = http_get_status(200);
http_add_header_field(&res.hdr, "Allow", "GET, HEAD");
http_add_header_field(&res.hdr, "Accept-Ranges", "bytes");
if (strncmp(req.method, "GET", 3) != 0 && strncmp(req.method, "HEAD", 4) != 0) {
if (strcmp(req.method, "GET") != 0 && strcmp(req.method, "HEAD") != 0) {
res.status = http_get_status(405);
goto respond;
}
@ -193,7 +194,7 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int
char *if_modified_since = http_get_header_field(&req.hdr, "If-Modified-Since");
char *if_none_match = http_get_header_field(&req.hdr, "If-None-Match");
if ((if_none_match != NULL && strstr(if_none_match, uri.meta->etag) == NULL) || (accept_if_modified_since &&
if_modified_since != NULL && strncmp(if_modified_since, last_modified, strlen(last_modified)) == 0)) {
if_modified_since != NULL && strcmp(if_modified_since, last_modified) == 0)) {
res.status = http_get_status(304);
goto respond;
}
@ -270,7 +271,7 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int
goto respond;
}
if (strncmp(req.method, "POST", 4) == 0 || strncmp(req.method, "PUT", 3) == 0) {
if (strcmp(req.method, "POST") == 0 || strcmp(req.method, "PUT") == 0) {
char *client_content_length = http_get_header_field(&req.hdr, "Content-Length");
unsigned long client_content_len = 0;
if (client_content_length == NULL) {
@ -295,10 +296,7 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int
if (ret != 0) {
if (ret < 0) {
goto abort;
} else {
sprintf(err_msg, "Unable to communicate with PHP-FPM.");
}
res.status = http_get_status(502);
goto respond;
}
char *status = http_get_header_field(&res.hdr, "Status");
@ -359,7 +357,7 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int
print("%s%03i %s%s%s (%s)%s", http_get_status_color(res.status), res.status->code, res.status->msg,
location != NULL ? " -> " : "", location != NULL ? location : "", format_duration(micros, buf0), CLR_STR);
if (strncmp(req.method, "HEAD", 4) != 0) {
if (strcmp(req.method, "HEAD") != 0) {
unsigned long snd_len = 0;
unsigned long len = 0;
if (msg_buf[0] != 0) {
@ -404,9 +402,9 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int
}
} else if (use_fastcgi) {
char *transfer_encoding = http_get_header_field(&res.hdr, "Transfer-Encoding");
int chunked = transfer_encoding != NULL && strncmp(transfer_encoding, "chunked", 7) == 0;
int chunked = transfer_encoding != NULL && strcmp(transfer_encoding, "chunked") == 0;
char *content_encoding = http_get_header_field(&res.hdr, "Content-Encoding");
int comp = content_encoding != NULL && strncmp(content_encoding, "deflate", 7) == 0;
int comp = content_encoding != NULL && strcmp(content_encoding, "deflate") == 0;
int flags = (chunked ? FASTCGI_CHUNKED : 0) | (comp ? FASTCGI_COMPRESS : 0);
fastcgi_send(&php_fpm, client, flags);
}
@ -421,21 +419,99 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int
if (php_fpm.socket != 0) close(php_fpm.socket);
http_free_req(&req);
http_free_res(&res);
if (client->buf != NULL) {
free(client->buf);
client->buf = NULL;
client->buf_off = 0;
client->buf_len = 0;
}
return !client_keep_alive;
}
int client_connection_handler(sock *client, unsigned long client_num) {
struct timespec begin, end;
int ret, req_num;
char buf[16];
char buf[1024];
clock_gettime(CLOCK_MONOTONIC, &begin);
// TODO get geoip data for ip address
// TODO Reverse DNS request
client_host_str = client_addr_str;
if (dns_server != NULL) {
sprintf(buf, "dig @%s +short +time=1 -x %s", dns_server, client_addr_str);
FILE *dig = popen(buf, "r");
if (dig == NULL) {
fprintf(stderr, ERR_STR "Unable to start dig: %s" CLR_STR "\n", strerror(errno));
goto dig_err;
}
unsigned long read = fread(buf, 1, sizeof(buf), dig);
ret = pclose(dig);
if (ret != 0) {
fprintf(stderr, ERR_STR "Dig terminated with exit code %i" CLR_STR "\n", ret);
goto dig_err;
}
char *ptr = memchr(buf, '\n', read);
if (ptr == buf || ptr == NULL) {
goto dig_err;
}
ptr[-1] = 0;
client_host_str = malloc(strlen(buf) + 1);
strcpy(client_host_str, buf);
} else {
dig_err:
client_host_str = NULL;
}
print("Connection accepted from %s (%s) [%s]", client_addr_str, client_host_str, "N/A");
client_geoip = malloc(GEOIP_MAX_SIZE);
long str_off = 0;
for (int i = 0; i < MAX_MMDB && mmdbs[i].filename != NULL; i++) {
int gai_error, mmdb_res;
MMDB_lookup_result_s result = MMDB_lookup_string(&mmdbs[i], client_addr_str, &gai_error, &mmdb_res);
if (mmdb_res != MMDB_SUCCESS) {
print(ERR_STR "Unable to lookup geoip info: %s" CLR_STR "\n", MMDB_strerror(mmdb_res));
continue;
} else if (gai_error != 0) {
print(ERR_STR "Unable to lookup geoip info" CLR_STR "\n");
continue;
} else if (!result.found_entry) {
continue;
}
MMDB_entry_data_list_s *list;
mmdb_res = MMDB_get_entry_data_list(&result.entry, &list);
if (mmdb_res != MMDB_SUCCESS) {
print(ERR_STR "Unable to lookup geoip info: %s" CLR_STR "\n", MMDB_strerror(mmdb_res));
continue;
}
long prev = str_off;
if (str_off != 0) {
str_off--;
}
mmdb_json(list, client_geoip, &str_off, GEOIP_MAX_SIZE);
if (prev != 0) {
client_geoip[prev - 1] = ',';
}
MMDB_free_entry_data_list(list);
}
char client_cc[3];
client_cc[0] = 0;
if (str_off == 0) {
free(client_geoip);
client_geoip = NULL;
} else {
char *pos = client_geoip;
pos = strstr(pos, "\"country\":");
if (pos != NULL) {
pos = strstr(pos, "\"iso_code\":");
pos += 12;
strncpy(client_cc, pos, 2);
}
}
print("Connection accepted from %s %s%s%s[%s]", client_addr_str, client_host_str != NULL ? "(" : "",
client_host_str != NULL ? client_host_str : "", client_host_str != NULL ? ") " : "",
client_cc[0] != 0 ? client_cc : "N/A");
client_timeout.tv_sec = CLIENT_TIMEOUT;
client_timeout.tv_usec = 0;
@ -524,9 +600,18 @@ int client_handler(sock *client, unsigned long client_num, struct sockaddr_in6 *
ret = client_connection_handler(client, client_num);
free(client_addr_str_ptr);
client_addr_str_ptr = NULL;
free(server_addr_str_ptr);
server_addr_str_ptr = NULL;
if (client_host_str != NULL) {
free(client_host_str);
client_host_str = NULL;
}
free(log_conn_prefix);
log_conn_prefix = NULL;
free(log_req_prefix);
log_req_prefix = NULL;
free(log_client_prefix);
log_client_prefix = NULL;
return ret;
}

View File

@ -118,17 +118,8 @@ int fastcgi_init(fastcgi_conn *conn, unsigned int client_num, unsigned int req_n
addr = (struct sockaddr_in6 *) &addr_storage;
sprintf(buf0, "%i", addr->sin6_port);
param_ptr = fastcgi_add_param(param_ptr, "REMOTE_PORT", buf0);
char addr_str[INET6_ADDRSTRLEN];
char *addr_ptr;
inet_ntop(addr->sin6_family, (void *) &addr->sin6_addr, addr_str, INET6_ADDRSTRLEN);
if (strncmp(addr_str, "::ffff:", 7) == 0) {
addr_ptr = addr_str + 7;
} else {
addr_ptr = addr_str;
}
param_ptr = fastcgi_add_param(param_ptr, "REMOTE_ADDR", addr_ptr);
param_ptr = fastcgi_add_param(param_ptr, "REMOTE_HOST", addr_ptr);
param_ptr = fastcgi_add_param(param_ptr, "REMOTE_ADDR", client_addr_str);
param_ptr = fastcgi_add_param(param_ptr, "REMOTE_HOST", client_host_str != NULL ? client_host_str : client_addr_str);
//param_ptr = fastcgi_add_param(param_ptr, "REMOTE_IDENT", "");
//param_ptr = fastcgi_add_param(param_ptr, "REMOTE_USER", "");
@ -151,6 +142,9 @@ int fastcgi_init(fastcgi_conn *conn, unsigned int client_num, unsigned int req_n
param_ptr = fastcgi_add_param(param_ptr, "CONTENT_LENGTH", content_length != NULL ? content_length : "");
char *content_type = http_get_header_field(&req->hdr, "Content-Type");
param_ptr = fastcgi_add_param(param_ptr, "CONTENT_TYPE", content_type != NULL ? content_type : "");
if (client_geoip != NULL) {
param_ptr = fastcgi_add_param(param_ptr, "REMOTE_INFO", client_geoip);
}
for (int i = 0; i < req->hdr.field_num; i++) {
char *ptr = buf0;
@ -287,12 +281,12 @@ int fastcgi_header(fastcgi_conn *conn, http_res *res, char *err_msg) {
res->status = http_get_status(502);
sprintf(err_msg, "Unable to communicate with PHP-FPM.");
print(ERR_STR "Unable to receive from PHP-FPM: %s" CLR_STR, strerror(errno));
return -1;
return 1;
} else if (ret != sizeof(header)) {
res->status = http_get_status(502);
sprintf(err_msg, "Unable to communicate with PHP-FPM.");
print(ERR_STR "Unable to receive from PHP-FPM" CLR_STR);
return -1;
return 1;
}
req_id = (header.requestIdB1 << 8) | header.requestIdB0;
content_len = (header.contentLengthB1 << 8) | header.contentLengthB0;
@ -303,13 +297,13 @@ int fastcgi_header(fastcgi_conn *conn, http_res *res, char *err_msg) {
sprintf(err_msg, "Unable to communicate with PHP-FPM.");
print(ERR_STR "Unable to receive from PHP-FPM: %s" CLR_STR, strerror(errno));
free(content);
return -1;
return 1;
} else if (ret != (content_len + header.paddingLength)) {
res->status = http_get_status(502);
sprintf(err_msg, "Unable to communicate with PHP-FPM.");
print(ERR_STR "Unable to receive from PHP-FPM" CLR_STR);
free(content);
return -1;
return 1;
}
if (req_id != conn->req_id) {
@ -329,7 +323,7 @@ int fastcgi_header(fastcgi_conn *conn, http_res *res, char *err_msg) {
close(conn->socket);
conn->socket = 0;
free(content);
return -2;
return 1;
} else if (header.type == FCGI_STDERR) {
err = err || fastcgi_php_error(content, content_len, err_msg);
} else if (header.type == FCGI_STDOUT) {
@ -342,7 +336,7 @@ int fastcgi_header(fastcgi_conn *conn, http_res *res, char *err_msg) {
}
if (err) {
res->status = http_get_status(500);
return -3;
return 2;
}
conn->out_buf = content;
@ -519,6 +513,13 @@ int fastcgi_receive(fastcgi_conn *conn, sock *client, unsigned long len) {
.paddingLength = 0,
.reserved = 0
};
if (client->buf != NULL && client->buf_len - client->buf_off > 0) {
ret = (int) (client->buf_len - client->buf_off);
memcpy(buf, client->buf + client->buf_off, ret);
goto send;
}
while (rcv_len < len) {
if (client->enc) {
ret = SSL_read(client->ssl, buf, sizeof(buf));
@ -533,6 +534,7 @@ int fastcgi_receive(fastcgi_conn *conn, sock *client, unsigned long len) {
return -1;
}
}
send:
rcv_len += ret;
header.contentLengthB1 = (ret >> 8) & 0xFF;
header.contentLengthB0 = ret & 0xFF;

View File

@ -115,7 +115,7 @@ int http_receive_request(sock *client, http_req *req) {
}
ptr = buf;
while (header_len != (ptr - buf)) {
while (header_len > (ptr - buf + 2)) {
pos0 = strstr(ptr, "\r\n");
if (pos0 == NULL) {
print(ERR_STR "Unable to parse header: Invalid header format" CLR_STR);
@ -123,29 +123,22 @@ int http_receive_request(sock *client, http_req *req) {
}
if (req->version[0] == 0) {
if (memcmp(ptr, "GET ", 4) == 0) {
strcpy(req->method, "GET");
} else if (memcmp(ptr, "HEAD ", 5) == 0) {
strcpy(req->method, "HEAD");
} else if (memcmp(ptr, "POST ", 5) == 0) {
strcpy(req->method, "POST");
} else if (memcmp(ptr, "PUT ", 4) == 0) {
strcpy(req->method, "PUT");
} else if (memcmp(ptr, "DELETE ", 7) == 0) {
strcpy(req->method, "DELETE");
} else if (memcmp(ptr, "CONNECT ", 7) == 0) {
strcpy(req->method, "CONNECT");
} else if (memcmp(ptr, "OPTIONS ", 7) == 0) {
strcpy(req->method, "OPTIONS");
} else if (memcmp(ptr, "TRACE ", 6) == 0) {
strcpy(req->method, "TRACE");
} else {
print(ERR_STR "Unable to parse header: Invalid method" CLR_STR);
pos1 = memchr(ptr, ' ', rcv_len - (ptr - buf)) + 1;
if (pos1 == NULL) goto err_hdr_fmt;
if (pos1 - ptr - 1 >= sizeof(req->method)) {
print(ERR_STR "Unable to parse header: Method name too long" CLR_STR);
return 2;
}
pos1 = memchr(ptr, ' ', rcv_len - (ptr - buf)) + 1;
if (pos1 == NULL) goto err_hdr_fmt;
for (int i = 0; i < (pos1 - ptr - 1); i++) {
if (ptr[i] < 'A' || ptr[i] > 'Z') {
print(ERR_STR "Unable to parse header: Invalid method" CLR_STR);
return 2;
}
}
strncpy(req->method, ptr, pos1 - ptr - 1);
pos2 = memchr(pos1, ' ', rcv_len - (pos1 - buf)) + 1;
if (pos2 == NULL) {
err_hdr_fmt:
@ -166,12 +159,21 @@ int http_receive_request(sock *client, http_req *req) {
int ret = http_parse_header_field(&req->hdr, ptr, pos0);
if (ret != 0) return ret;
}
if (pos0[2] == '\r' && pos0[3] == '\n') {
return 0;
}
ptr = pos0 + 2;
}
if (pos0[2] == '\r' && pos0[3] == '\n') {
break;
}
}
client->buf_len = rcv_len - (pos0 - buf + 4);
if (client->buf_len > 0) {
client->buf = malloc(client->buf_len);
client->buf_off = 0;
memcpy(client->buf, pos0 + 4, client->buf_len);
}
return 0;
}
char *http_get_header_field(const http_hdr *hdr, const char *field_name) {

View File

@ -32,7 +32,7 @@ typedef struct {
} http_hdr;
typedef struct {
char method[8];
char method[16];
char *uri;
char version[3];
http_hdr hdr;

View File

@ -62,7 +62,6 @@ char *ssl_get_error(SSL *ssl, int ret) {
void destroy() {
fprintf(stderr, "\n" ERR_STR "Terminating forcefully!" CLR_STR "\n");
fflush(stderr);
int status = 0;
int ret;
int kills = 0;
@ -72,13 +71,11 @@ void destroy() {
if (ret < 0) {
fprintf(stderr, ERR_STR "Unable to wait for child process (PID %i): %s" CLR_STR "\n",
children[i], strerror(errno));
fflush(stderr);
} else if (ret == children[i]) {
children[i] = 0;
if (status != 0) {
fprintf(stderr, ERR_STR "Child process with PID %i terminated with exit code %i" CLR_STR "\n",
ret, status);
fflush(stderr);
}
} else {
kill(children[i], SIGKILL);
@ -88,7 +85,6 @@ void destroy() {
}
if (kills > 0) {
fprintf(stderr, ERR_STR "Killed %i child process(es)" CLR_STR "\n", kills);
fflush(stderr);
}
cache_unload();
exit(2);
@ -96,7 +92,6 @@ void destroy() {
void terminate() {
fprintf(stderr, "\nTerminating gracefully...\n");
fflush(stderr);
active = 0;
signal(SIGINT, destroy);
@ -116,13 +111,11 @@ void terminate() {
if (ret < 0) {
fprintf(stderr, ERR_STR "Unable to wait for child process (PID %i): %s" CLR_STR "\n",
children[i], strerror(errno));
fflush(stderr);
} else if (ret == children[i]) {
children[i] = 0;
if (status != 0) {
fprintf(stderr, ERR_STR "Child process with PID %i terminated with exit code %i" CLR_STR "\n",
ret, status);
fflush(stderr);
}
} else {
kill(children[i], SIGTERM);
@ -133,7 +126,6 @@ void terminate() {
if (wait_num > 0) {
fprintf(stderr, "Waiting for %i child process(es)...\n", wait_num);
fflush(stderr);
}
for (int i = 0; i < MAX_CHILDREN; i++) {
@ -142,13 +134,11 @@ void terminate() {
if (ret < 0) {
fprintf(stderr, ERR_STR "Unable to wait for child process (PID %i): %s" CLR_STR "\n",
children[i], strerror(errno));
fflush(stderr);
} else if (ret == children[i]) {
children[i] = 0;
if (status != 0) {
fprintf(stderr, ERR_STR "Child process with PID %i terminated with exit code %i" CLR_STR "\n",
ret, status);
fflush(stderr);
}
}
}
@ -161,10 +151,8 @@ void terminate() {
struct timespec ts = {.tv_sec = 0, .tv_nsec = 50000000};
nanosleep(&ts, &ts);
fprintf(stderr, "\nGoodbye\n");
fflush(stderr);
} else {
fprintf(stderr, "Goodbye\n");
fflush(stderr);
}
cache_unload();
exit(0);
@ -184,6 +172,10 @@ int main(int argc, const char *argv[]) {
struct sockaddr_in6 client_addr;
unsigned int client_addr_len = sizeof(client_addr);
memset(sockets, 0, sizeof(sockets));
memset(children, 0, sizeof(children));
memset(mmdbs, 0, sizeof(mmdbs));
struct timeval timeout;
const struct sockaddr_in6 addresses[2] = {
@ -191,70 +183,72 @@ int main(int argc, const char *argv[]) {
{.sin6_family = AF_INET6, .sin6_addr = IN6ADDR_ANY_INIT, .sin6_port = htons(443)}
};
if (setvbuf(stdout, NULL, _IONBF, 0) != 0) {
fprintf(stderr, ERR_STR "Unable to set stdout to unbuffered mode: %s" CLR_STR, strerror(errno));
return 1;
}
printf("Necronda Web Server\n");
fflush(stdout);
for (int i = 1; i < argc; i++) {
const char *arg = argv[i];
unsigned long len = strlen(arg);
if ((len == 2 && strncmp(arg, "-h", 2) == 0) || (len == 6 && strncmp(arg, "--help", 6) == 0)) {
printf("Usage: necronda-server [-h] -w <PATH> -c <CERT-FILE> -p <KEY-FILE> [-g <DB-FILE>]\n"
if (strcmp(arg, "-h") == 0 || strcmp(arg, "--help") == 0) {
printf("Usage: necronda-server [-h] -w <PATH> -c <CERT-FILE> -p <KEY-FILE> [-g <DB-DIR>] [-d <DNS-SERVER>]\n"
"\n"
"Options:\n"
" -c, --cert <CERT-FILE> path to the full chain certificate file\n"
" -g, --geoip <DB-FILE> path to a Maxmind GeoIP Database file\n"
" -d, --dns <DNS-SERVER> ip address or hostname of a DNS server for dig\n"
" -g, --geoip <DB-DIR> path to a Maxmind GeoIP Database file\n"
" -h, --help print this dialogue\n"
" -p, --privkey <KEY-FILE> path to the private key file\n"
" -w, --webroot <PATH> path to the web root directory\n");
return 0;
} else if ((len == 2 && strncmp(arg, "-w", 2) == 0) || (len == 9 && strncmp(arg, "--webroot", 9) == 0)) {
} else if (strcmp(arg, "-w") == 0 || strcmp(arg, "--webroot") == 0) {
if (i == argc - 1) {
fprintf(stderr, ERR_STR "Unable to parse argument %s, usage: --webroot <WEBROOT>" CLR_STR "\n", arg);
fflush(stderr);
return 1;
}
webroot_base = argv[++i];
} else if ((len == 2 && strncmp(arg, "-c", 2) == 0) || (len == 6 && strncmp(arg, "--cert", 6) == 0)) {
} else if (strcmp(arg, "-c") == 0 || strcmp(arg, "--cert") == 0) {
if (i == argc - 1) {
fprintf(stderr, ERR_STR "Unable to parse argument %s, usage: --cert <CERT-FILE>" CLR_STR "\n", arg);
fflush(stderr);
return 1;
}
cert_file = argv[++i];
} else if ((len == 2 && strncmp(arg, "-p", 2) == 0) || (len == 9 && strncmp(arg, "--privkey", 9) == 0)) {
} else if (strcmp(arg, "-p") == 0 || strcmp(arg, "--privkey") == 0) {
if (i == argc - 1) {
fprintf(stderr, ERR_STR "Unable to parse argument %s, usage: --privkey <KEY-FILE>" CLR_STR "\n", arg);
fflush(stderr);
return 1;
}
key_file = argv[++i];
} else if ((len == 2 && strncmp(arg, "-g", 2) == 0) || (len == 7 && strncmp(arg, "--geoip", 7) == 0)) {
} else if (strcmp(arg, "-g") == 0 || strcmp(arg, "--geoip") == 0) {
if (i == argc - 1) {
fprintf(stderr, ERR_STR "Unable to parse argument %s, usage: --geoip <DB-FILE>" CLR_STR "\n", arg);
fflush(stderr);
fprintf(stderr, ERR_STR "Unable to parse argument %s, usage: --geoip <DB-DIR>" CLR_STR "\n", arg);
return 1;
}
geoip_file = argv[++i];
geoip_dir = argv[++i];
} else if (strcmp(arg, "-d") == 0 || strcmp(arg, "--dns") == 0) {
if (i == argc - 1) {
fprintf(stderr, ERR_STR "Unable to parse argument %s, usage: --dns <DNS-SERVER>" CLR_STR "\n", arg);
return 1;
}
dns_server = argv[++i];
} else {
fprintf(stderr, ERR_STR "Unable to parse argument '%s'" CLR_STR "\n", arg);
fflush(stderr);
return 1;
}
}
if (webroot_base == NULL) {
fprintf(stderr, ERR_STR "Error: --webroot is missing" CLR_STR "\n");
fflush(stderr);
return 1;
}
if (cert_file == NULL) {
fprintf(stderr, ERR_STR "Error: --cert is missing" CLR_STR "\n");
fflush(stderr);
return 1;
}
if (key_file == NULL) {
fprintf(stderr, ERR_STR "Error: --privkey is missing" CLR_STR "\n");
fflush(stderr);
return 1;
}
@ -264,14 +258,12 @@ int main(int argc, const char *argv[]) {
if (sockets[1] < 0) {
socket_err:
fprintf(stderr, ERR_STR "Unable to create socket: %s" CLR_STR "\n", strerror(errno));
fflush(stderr);
return 1;
}
for (int i = 0; i < NUM_SOCKETS; i++) {
if (setsockopt(sockets[i], SOL_SOCKET, SO_REUSEADDR, &YES, sizeof(YES)) < 0) {
fprintf(stderr, ERR_STR "Unable to set options for socket %i: %s" CLR_STR "\n", i, strerror(errno));
fflush(stderr);
return 1;
}
}
@ -280,24 +272,46 @@ int main(int argc, const char *argv[]) {
if (bind(sockets[1], (struct sockaddr *) &addresses[1], sizeof(addresses[1])) < 0) {
bind_err:
fprintf(stderr, ERR_STR "Unable to bind socket to address: %s" CLR_STR "\n", strerror(errno));
fflush(stderr);
return 1;
}
signal(SIGINT, terminate);
signal(SIGTERM, terminate);
ret = cache_init();
if (ret < 0) {
return 1;
} else if (ret != 0) {
return 0;
if (geoip_dir != NULL) {
DIR *geoip = opendir(geoip_dir);
if (geoip == NULL) {
fprintf(stderr, ERR_STR "Unable to open GeoIP dir: %s" CLR_STR "\n", strerror(errno));
return 1;
}
struct dirent *dir;
int i = 0;
while ((dir = readdir(geoip)) != NULL) {
if (strcmp(dir->d_name + strlen(dir->d_name) - 5, ".mmdb") != 0) continue;
if (i >= MAX_MMDB) {
fprintf(stderr, ERR_STR "Too many .mmdb files" CLR_STR "\n");
return 1;
}
sprintf(buf, "%s/%s", geoip_dir, dir->d_name);
ret = MMDB_open(buf, 0, &mmdbs[i]);
if (ret != MMDB_SUCCESS) {
fprintf(stderr, ERR_STR "Unable to open .mmdb file: %s" CLR_STR "\n", MMDB_strerror(ret));
return 1;
}
i++;
}
if (i == 0) {
fprintf(stderr, ERR_STR "No .mmdb files found in %s" CLR_STR "\n", geoip_dir);
return 1;
}
closedir(geoip);
}
// TODO init geoip database
openssl_init();
client.buf = NULL;
client.buf_len = 0;
client.buf_off = 0;
client.ctx = SSL_CTX_new(TLS_server_method());
SSL_CTX_set_options(client.ctx, SSL_OP_SINGLE_DH_USE);
SSL_CTX_set_verify(client.ctx, SSL_VERIFY_NONE, NULL);
@ -309,20 +323,17 @@ int main(int argc, const char *argv[]) {
if (SSL_CTX_use_certificate_chain_file(client.ctx, cert_file) != 1) {
fprintf(stderr, ERR_STR "Unable to load certificate chain file: %s: %s" CLR_STR "\n",
ERR_reason_error_string(ERR_get_error()), cert_file);
fflush(stderr);
return 1;
}
if (SSL_CTX_use_PrivateKey_file(client.ctx, key_file, SSL_FILETYPE_PEM) != 1) {
fprintf(stderr, ERR_STR "Unable to load private key file: %s: %s" CLR_STR "\n",
ERR_reason_error_string(ERR_get_error()), key_file);
fflush(stderr);
return 1;
}
for (int i = 0; i < NUM_SOCKETS; i++) {
if (listen(sockets[i], LISTEN_BACKLOG) < 0) {
fprintf(stderr, ERR_STR "Unable to listen on socket %i: %s" CLR_STR "\n", i, strerror(errno));
fflush(stderr);
return 1;
}
}
@ -335,8 +346,14 @@ int main(int argc, const char *argv[]) {
}
}
ret = cache_init();
if (ret < 0) {
return 1;
} else if (ret != 0) {
return 0;
}
fprintf(stderr, "Ready to accept connections\n");
fflush(stderr);
while (active) {
timeout.tv_sec = 1;
@ -345,7 +362,6 @@ int main(int argc, const char *argv[]) {
ready_sockets_num = select(max_socket_fd + 1, &read_socket_fds, NULL, NULL, &timeout);
if (ready_sockets_num < 0) {
fprintf(stderr, ERR_STR "Unable to select sockets: %s" CLR_STR "\n", strerror(errno));
fflush(stderr);
return 1;
}
@ -354,7 +370,6 @@ int main(int argc, const char *argv[]) {
client_fd = accept(sockets[i], (struct sockaddr *) &client_addr, &client_addr_len);
if (client_fd < 0) {
fprintf(stderr, ERR_STR "Unable to accept connection: %s" CLR_STR "\n", strerror(errno));
fflush(stderr);
continue;
}
@ -379,7 +394,6 @@ int main(int argc, const char *argv[]) {
}
} else {
fprintf(stderr, ERR_STR "Unable to create child process: %s" CLR_STR "\n", strerror(errno));
fflush(stderr);
}
}
}
@ -391,13 +405,11 @@ int main(int argc, const char *argv[]) {
if (ret < 0) {
fprintf(stderr, ERR_STR "Unable to wait for child process (PID %i): %s" CLR_STR "\n",
children[i], strerror(errno));
fflush(stderr);
} else if (ret == children[i]) {
children[i] = 0;
if (status != 0) {
fprintf(stderr, ERR_STR "Child process with PID %i terminated with exit code %i" CLR_STR "\n",
ret, status);
fflush(stderr);
}
}
}

View File

@ -25,10 +25,13 @@
#include <openssl/conf.h>
#include <openssl/engine.h>
#include <openssl/dh.h>
#include <maxminddb.h>
#include <dirent.h>
#define NUM_SOCKETS 2
#define MAX_CHILDREN 1024
#define MAX_MMDB 3
#define LISTEN_BACKLOG 16
#define REQ_PER_CONNECTION 100
#define CLIENT_TIMEOUT 3600
@ -37,6 +40,7 @@
#define CLIENT_MAX_HEADER_SIZE 8192
#define FILE_CACHE_SIZE 1024
#define SHM_KEY 255641
#define GEOIP_MAX_SIZE 8192
#define ERR_STR "\x1B[1;31m"
#define CLR_STR "\x1B[0m"
@ -51,11 +55,13 @@
#define HTTP_4XX_STR "\x1B[1;31m"
#define HTTP_5XX_STR "\x1B[1;31m"
#define NECRONDA_VERSION "4.0"
#define NECRONDA_VERSION "4.1"
#define SERVER_STR "Necronda/" NECRONDA_VERSION
#define NECRONDA_DEFAULT "www.necronda.net"
#define NECRONDA_ZLIB_LEVEL 9
#ifndef DEFAULT_HOST
#define DEFAULT_HOST "www.necronda.net"
#endif
#ifndef MAGIC_FILE
#define MAGIC_FILE "/usr/share/file/misc/magic.mgc"
#endif
@ -65,14 +71,18 @@
int sockets[NUM_SOCKETS];
pid_t children[MAX_CHILDREN];
MMDB_s mmdbs[MAX_MMDB];
const char *cert_file, *key_file, *webroot_base, *geoip_file;
const char *cert_file, *key_file, *webroot_base, *geoip_dir, *dns_server;
typedef struct {
unsigned int enc:1;
int socket;
SSL_CTX *ctx;
SSL *ssl;
char *buf;
unsigned long buf_len;
unsigned long buf_off;
} sock;
char *ssl_get_error(SSL *ssl, int ret);

View File

@ -101,9 +101,9 @@ int uri_init(http_uri *uri, const char *webroot, const char *uri_str, int dir_mo
uri->filename = malloc(strlen(buf0) + 1);
strcpy(uri->filename, buf0);
ssize_t len = strlen(uri->path);
if (strncmp(uri->path + len - 5, ".html", 5) == 0) {
if (strcmp(uri->path + len - 5, ".html") == 0) {
uri->path[len - 5] = 0;
} else if (strncmp(uri->path + len - 4, ".php", 4) == 0) {
} else if (strcmp(uri->path + len - 4, ".php") == 0) {
uri->path[len - 4] = 0;
uri->is_static = 0;
}

View File

@ -94,3 +94,64 @@ int url_decode(const char *str, char *dec, ssize_t *size) {
*size = ptr - dec;
return 0;
}
MMDB_entry_data_list_s *mmdb_json(MMDB_entry_data_list_s *list, char *str, long *str_off, long str_len) {
switch (list->entry_data.type) {
case MMDB_DATA_TYPE_MAP:
*str_off += sprintf(str + *str_off, "{");
break;
case MMDB_DATA_TYPE_ARRAY:
*str_off += sprintf(str + *str_off, "[");
break;
case MMDB_DATA_TYPE_UTF8_STRING:
*str_off += sprintf(str + *str_off, "\"%.*s\"", list->entry_data.data_size, list->entry_data.utf8_string);
break;
case MMDB_DATA_TYPE_UINT16:
*str_off += sprintf(str + *str_off, "%u", list->entry_data.uint16);
break;
case MMDB_DATA_TYPE_UINT32:
*str_off += sprintf(str + *str_off, "%u", list->entry_data.uint32);
break;
case MMDB_DATA_TYPE_UINT64:
*str_off += sprintf(str + *str_off, "%lu", list->entry_data.uint64);
break;
case MMDB_DATA_TYPE_UINT128:
*str_off += sprintf(str + *str_off, "%llu", list->entry_data.uint128);
break;
case MMDB_DATA_TYPE_INT32:
*str_off += sprintf(str + *str_off, "%i", list->entry_data.uint32);
break;
case MMDB_DATA_TYPE_BOOLEAN:
*str_off += sprintf(str + *str_off, "%s", list->entry_data.boolean ? "true" : "false");
break;
case MMDB_DATA_TYPE_FLOAT:
*str_off += sprintf(str + *str_off, "%f", list->entry_data.float_value);
break;
case MMDB_DATA_TYPE_DOUBLE:
*str_off += sprintf(str + *str_off, "%f", list->entry_data.double_value);
break;
}
if (list->entry_data.type != MMDB_DATA_TYPE_MAP && list->entry_data.type != MMDB_DATA_TYPE_ARRAY) {
return list->next;
}
MMDB_entry_data_list_s *next = list->next;
int stat = 0;
for (int i = 0; i < list->entry_data.data_size; i++) {
next = mmdb_json(next, str, str_off, str_len);
if (list->entry_data.type == MMDB_DATA_TYPE_MAP) {
stat = !stat;
if (stat) {
i--;
*str_off += sprintf(str + *str_off, ":");
continue;
}
}
if (i != list->entry_data.data_size - 1) *str_off += sprintf(str + *str_off, ",");
}
if (list->entry_data.type == MMDB_DATA_TYPE_MAP) {
*str_off += sprintf(str + *str_off, "}");
} else {
*str_off += sprintf(str + *str_off, "]");
}
return next;
}

View File

@ -17,7 +17,7 @@ char *log_prefix;
#define print(...) out_x(, ##__VA_ARGS__, out_2(__VA_ARGS__), out_2(__VA_ARGS__), out_2(__VA_ARGS__), \
out_2(__VA_ARGS__), out_2(__VA_ARGS__), out_2(__VA_ARGS__), out_2(__VA_ARGS__), \
out_2(__VA_ARGS__), out_1(__VA_ARGS__)); fflush(stdout)
out_2(__VA_ARGS__), out_1(__VA_ARGS__))
char *format_duration(unsigned long micros, char *buf);