10 Commits

32 changed files with 537 additions and 184 deletions

View File

@@ -11,16 +11,16 @@ packages:
@echo "Finished downloading!" @echo "Finished downloading!"
permit: permit:
sudo setcap 'cap_net_bind_service=+ep' "$(shell pwd)/bin/necronda-server" sudo setcap 'cap_net_bind_service=+ep' "$(shell pwd)/bin/sesimos"
compile: compile:
@mkdir -p bin @mkdir -p bin
$(CC) src/lib/*.c -o bin/libnecrondaserver.so --shared -fPIC $(CFLAGS) $(LIBS) $(CC) src/lib/*.c -o bin/libsesimos.so --shared -fPIC $(CFLAGS) $(LIBS)
$(CC) src/server.c src/client.c -o bin/necronda-server $(CFLAGS) $(LIBS) \ $(CC) src/server.c src/client.c -o bin/sesimos $(CFLAGS) $(LIBS) \
-Lbin -lnecrondaserver -Wl,-rpath=$(shell pwd)/bin -Lbin -lsesimos -Wl,-rpath=$(shell pwd)/bin
compile-prod: compile-prod:
@mkdir -p bin @mkdir -p bin
$(CC) src/lib/*.c -o bin/libnecrondaserver.so --shared -fPIC $(CFLAGS) $(LIBS) $(DEBIAN_OPTS) -O3 $(CC) src/lib/*.c -o bin/libsesimos.so --shared -fPIC $(CFLAGS) $(LIBS) $(DEBIAN_OPTS) -O3
$(CC) src/server.c src/client.c -o bin/necronda-server $(CFLAGS) $(LIBS) $(DEBIAN_OPTS) -O3 \ $(CC) src/server.c src/client.c -o bin/sesimos $(CFLAGS) $(LIBS) $(DEBIAN_OPTS) -O3 \
-Lbin -lnecrondaserver -Wl,-rpath=$(shell pwd)/bin -Lbin -lsesimos -Wl,-rpath=$(shell pwd)/bin

View File

@@ -1,6 +1,6 @@
Necronda web server Sesimos Secure, simple, modern web server
=================== ===========================================
## Features ## Features
@@ -10,7 +10,7 @@ Necronda web server
* File compression ([gzip](https://www.gzip.org/), [Brotli](https://www.brotli.org/)) * File compression ([gzip](https://www.gzip.org/), [Brotli](https://www.brotli.org/))
* Disk cache for compressed files * Disk cache for compressed files
* Reverse proxy for other HTTP and HTTPS servers * Reverse proxy for other HTTP and HTTPS servers
* Transparent WebSocket reverse proxy **[WIP]** * Transparent WebSocket reverse proxy
* FastCGI support (e.g. [PHP-FPM](https://php-fpm.org/)) * FastCGI support (e.g. [PHP-FPM](https://php-fpm.org/))
* Automatic path info detection (e.g. `/my/file/extra/path` -> script: `/my/file.php`, path info: `extra/path`) * Automatic path info detection (e.g. `/my/file/extra/path` -> script: `/my/file.php`, path info: `extra/path`)
* Support for [MaxMind's GeoIP Database](https://www.maxmind.com/en/geoip2-services-and-databases) * Support for [MaxMind's GeoIP Database](https://www.maxmind.com/en/geoip2-services-and-databases)

View File

@@ -1,12 +1,12 @@
/** /**
* Necronda Web Server * sesimos - secure, simple, modern web server
* Client connection and request handlers * Client connection and request handlers
* src/client.c * src/client.c
* Lorenz Stechauner, 2020-12-03 * Lorenz Stechauner, 2020-12-03
*/ */
#include "defs.h"
#include "client.h" #include "client.h"
#include "necronda.h"
#include "server.h" #include "server.h"
#include "lib/utils.h" #include "lib/utils.h"
@@ -18,9 +18,9 @@
#include "lib/cache.h" #include "lib/cache.h"
#include "lib/geoip.h" #include "lib/geoip.h"
#include "lib/compress.h" #include "lib/compress.h"
#include "lib/websocket.h"
#include <string.h> #include <string.h>
#include <sys/select.h>
#include <errno.h> #include <errno.h>
#include <unistd.h> #include <unistd.h>
#include <openssl/ssl.h> #include <openssl/ssl.h>
@@ -28,13 +28,12 @@
#include <signal.h> #include <signal.h>
#include <arpa/inet.h> #include <arpa/inet.h>
int server_keep_alive = 1; int server_keep_alive = 1;
struct timeval client_timeout = {.tv_sec = CLIENT_TIMEOUT, .tv_usec = 0}; struct timeval client_timeout = {.tv_sec = CLIENT_TIMEOUT, .tv_usec = 0};
int server_keep_alive;
char *log_client_prefix, *log_conn_prefix, *log_req_prefix, *client_geoip; char *log_client_prefix, *log_conn_prefix, *log_req_prefix, *client_geoip;
char *client_addr_str, *client_addr_str_ptr, *server_addr_str, *server_addr_str_ptr, *client_host_str; char *client_addr_str, *client_addr_str_ptr, *server_addr_str, *server_addr_str_ptr, *client_host_str;
struct timeval client_timeout;
host_config *get_host_config(const char *host) { host_config *get_host_config(const char *host) {
for (int i = 0; i < CONFIG_MAX_HOST_CONFIG; i++) { for (int i = 0; i < CONFIG_MAX_HOST_CONFIG; i++) {
@@ -53,11 +52,6 @@ void client_terminate() {
server_keep_alive = 0; server_keep_alive = 0;
} }
int client_websocket_handler() {
// TODO implement client_websocket_handler
return 0;
}
int client_request_handler(sock *client, unsigned long client_num, unsigned int req_num) { int client_request_handler(sock *client, unsigned long client_num, unsigned int req_num) {
struct timespec begin, end; struct timespec begin, end;
long ret; long ret;
@@ -87,16 +81,12 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int
http_status custom_status; http_status custom_status;
http_res res = {.version = "1.1", .status = http_get_status(501), .hdr.field_num = 0, .hdr.last_field_num = -1}; http_res res = {.version = "1.1", .status = http_get_status(501), .hdr.field_num = 0, .hdr.last_field_num = -1};
http_status_ctx ctx = {.status = 0, .origin = NONE}; http_status_ctx ctx = {.status = 0, .origin = NONE, .ws_key = NULL};
clock_gettime(CLOCK_MONOTONIC, &begin); clock_gettime(CLOCK_MONOTONIC, &begin);
fd_set socket_fds; ret = sock_poll_read(&client, NULL, 1, CLIENT_TIMEOUT * 1000);
FD_ZERO(&socket_fds);
FD_SET(client->socket, &socket_fds);
client_timeout.tv_sec = CLIENT_TIMEOUT;
client_timeout.tv_usec = 0;
ret = select(client->socket + 1, &socket_fds, NULL, NULL, &client_timeout);
http_add_header_field(&res.hdr, "Date", http_get_date(buf0, sizeof(buf0))); http_add_header_field(&res.hdr, "Date", http_get_date(buf0, sizeof(buf0)));
http_add_header_field(&res.hdr, "Server", SERVER_STR); http_add_header_field(&res.hdr, "Server", SERVER_STR);
if (ret <= 0) { if (ret <= 0) {
@@ -130,7 +120,7 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int
} }
hdr_connection = http_get_header_field(&req.hdr, "Connection"); hdr_connection = http_get_header_field(&req.hdr, "Connection");
client_keep_alive = (hdr_connection != NULL && (strcmp(hdr_connection, "keep-alive") == 0 || strcmp(hdr_connection, "Keep-Alive") == 0)); client_keep_alive = (hdr_connection != NULL && (strstr(hdr_connection, "keep-alive") != NULL || strstr(hdr_connection, "Keep-Alive") != NULL));
host_ptr = http_get_header_field(&req.hdr, "Host"); host_ptr = http_get_header_field(&req.hdr, "Host");
if (host_ptr != NULL && strlen(host_ptr) > 255) { if (host_ptr != NULL && strlen(host_ptr) > 255) {
host[0] = 0; host[0] = 0;
@@ -384,7 +374,7 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int
} else { } else {
int mode; int mode;
if (strcmp(uri.filename + strlen(uri.filename) - 4, ".ncr") == 0) { if (strcmp(uri.filename + strlen(uri.filename) - 4, ".ncr") == 0) {
mode = FASTCGI_NECRONDA; mode = FASTCGI_SESIMOS;
} else if (strcmp(uri.filename + strlen(uri.filename) - 4, ".php") == 0) { } else if (strcmp(uri.filename + strlen(uri.filename) - 4, ".php") == 0) {
mode = FASTCGI_PHP; mode = FASTCGI_PHP;
} else { } else {
@@ -493,6 +483,25 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int
ret = rev_proxy_init(&req, &res, &ctx, conf, client, &custom_status, err_msg); ret = rev_proxy_init(&req, &res, &ctx, conf, client, &custom_status, err_msg);
use_rev_proxy = (ret == 0); use_rev_proxy = (ret == 0);
if (res.status->code == 101) {
const char *connection = http_get_header_field(&res.hdr, "Connection");
const char *upgrade = http_get_header_field(&res.hdr, "Upgrade");
if (connection != NULL && upgrade != NULL &&
(strstr(connection, "upgrade") != NULL || strstr(connection, "Upgrade") != NULL) &&
strcmp(upgrade, "websocket") == 0)
{
const char *ws_accept = http_get_header_field(&res.hdr, "Sec-WebSocket-Accept");
if (ws_calc_accept_key(ctx.ws_key, buf0) == 0) {
use_rev_proxy = (strcmp(buf0, ws_accept) == 0) ? 2 : 1;
}
} else {
print("Fail Test1");
ctx.status = 101;
ctx.origin = INTERNAL;
res.status = http_get_status(501);
}
}
// Let 300 be formatted by origin server // Let 300 be formatted by origin server
if (use_rev_proxy && res.status->code >= 301 && res.status->code < 600) { if (use_rev_proxy && res.status->code >= 301 && res.status->code < 600) {
const char *content_type = http_get_header_field(&res.hdr, "Content-Type"); const char *content_type = http_get_header_field(&res.hdr, "Content-Type");
@@ -501,8 +510,10 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int
if (content_encoding == NULL && content_type != NULL && content_length_f != NULL && strncmp(content_type, "text/html", 9) == 0) { if (content_encoding == NULL && content_type != NULL && content_length_f != NULL && strncmp(content_type, "text/html", 9) == 0) {
long content_len = strtol(content_length_f, NULL, 10); long content_len = strtol(content_length_f, NULL, 10);
if (content_len <= sizeof(msg_content) - 1) { if (content_len <= sizeof(msg_content) - 1) {
ctx.status = res.status->code; if (ctx.status != 101) {
ctx.origin = res.status->code >= 400 ? SERVER : NONE; ctx.status = res.status->code;
ctx.origin = res.status->code >= 400 ? SERVER : NONE;
}
use_rev_proxy = 0; use_rev_proxy = 0;
rev_proxy_dump(msg_content, content_len); rev_proxy_dump(msg_content, content_len);
} }
@@ -609,16 +620,19 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int
} }
} }
const char *conn = http_get_header_field(&res.hdr, "Connection"); int close_proxy = 0;
int close_proxy = (conn == NULL || (strcmp(conn, "keep-alive") != 0 && strcmp(conn, "Keep-Alive") != 0)); if (use_rev_proxy != 2) {
http_remove_header_field(&res.hdr, "Connection", HTTP_REMOVE_ALL); const char *conn = http_get_header_field(&res.hdr, "Connection");
http_remove_header_field(&res.hdr, "Keep-Alive", HTTP_REMOVE_ALL); close_proxy = (conn == NULL || (strstr(conn, "keep-alive") == NULL && strstr(conn, "Keep-Alive") == NULL));
if (server_keep_alive && client_keep_alive) { http_remove_header_field(&res.hdr, "Connection", HTTP_REMOVE_ALL);
http_add_header_field(&res.hdr, "Connection", "keep-alive"); http_remove_header_field(&res.hdr, "Keep-Alive", HTTP_REMOVE_ALL);
sprintf(buf0, "timeout=%i, max=%i", CLIENT_TIMEOUT, REQ_PER_CONNECTION); if (server_keep_alive && client_keep_alive) {
http_add_header_field(&res.hdr, "Keep-Alive", buf0); http_add_header_field(&res.hdr, "Connection", "keep-alive");
} else { sprintf(buf0, "timeout=%i, max=%i", CLIENT_TIMEOUT, REQ_PER_CONNECTION);
http_add_header_field(&res.hdr, "Connection", "close"); http_add_header_field(&res.hdr, "Keep-Alive", buf0);
} else {
http_add_header_field(&res.hdr, "Connection", "close");
}
} }
http_send_response(client, &res); http_send_response(client, &res);
@@ -631,7 +645,17 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int
// TODO access/error log file // TODO access/error log file
if (strcmp(req.method, "HEAD") != 0) { if (use_rev_proxy == 2) {
// WebSocket
print("Upgrading connection to WebSocket connection");
ret = ws_handle_connection(client, &rev_proxy);
if (ret != 0) {
client_keep_alive = 0;
close_proxy = 1;
}
print("WebSocket connection closed");
} else if (strcmp(req.method, "HEAD") != 0) {
// default response
unsigned long snd_len = 0; unsigned long snd_len = 0;
unsigned long len; unsigned long len;
if (msg_buf[0] != 0) { if (msg_buf[0] != 0) {

View File

@@ -1,12 +1,12 @@
/** /**
* Necronda Web Server * sesimos - secure, simple, modern web server
* Client connection and request handlers (header file) * Client connection and request handlers (header file)
* src/client.h * src/client.h
* Lorenz Stechauner, 2022-08-16 * Lorenz Stechauner, 2022-08-16
*/ */
#ifndef NECRONDA_SERVER_NECRONDA_CLIENT_H #ifndef SESIMOS_CLIENT_H
#define NECRONDA_SERVER_NECRONDA_CLIENT_H #define SESIMOS_CLIENT_H
#include "lib/config.h" #include "lib/config.h"
#include "lib/sock.h" #include "lib/sock.h"
@@ -17,4 +17,4 @@ host_config *get_host_config(const char *host);
int client_handler(sock *client, unsigned long client_num, struct sockaddr_in6 *client_addr); int client_handler(sock *client, unsigned long client_num, struct sockaddr_in6 *client_addr);
#endif //NECRONDA_SERVER_NECRONDA_CLIENT_H #endif //SESIMOS_CLIENT_H

25
src/defs.h Normal file
View File

@@ -0,0 +1,25 @@
/**
* sesimos - secure, simple, modern web server
* Definitions
* src/defs.h
* Lorenz Stechauner, 2021-05-04
*/
#ifndef SESIMOS_DEF_H
#define SESIMOS_DEF_H
#define SERVER_VERSION "4.6"
#define SERVER_STR "Sesimos/" SERVER_VERSION
#define SERVER_STR_HTML "Sesimos&nbsp;web&nbsp;server&nbsp;" SERVER_VERSION
#define CHUNK_SIZE 8192
#ifndef DEFAULT_HOST
# define DEFAULT_HOST "www.necronda.net"
#endif
#ifndef SERVER_NAME
# define SERVER_NAME DEFAULT_HOST
#endif
#endif //SESIMOS_DEF_H

View File

@@ -1,5 +1,5 @@
/** /**
* Necronda Web Server * sesimos - secure, simple, modern web server
* File cache implementation * File cache implementation
* src/lib/cache.c * src/lib/cache.c
* Lorenz Stechauner, 2020-12-19 * Lorenz Stechauner, 2020-12-19
@@ -8,6 +8,7 @@
#include "cache.h" #include "cache.h"
#include "utils.h" #include "utils.h"
#include "compress.h" #include "compress.h"
#include <stdio.h> #include <stdio.h>
#include <magic.h> #include <magic.h>
#include <sys/ipc.h> #include <sys/ipc.h>
@@ -18,6 +19,7 @@
#include <openssl/sha.h> #include <openssl/sha.h>
#include <malloc.h> #include <malloc.h>
int cache_continue = 1; int cache_continue = 1;
magic_t magic; magic_t magic;
cache_entry *cache; cache_entry *cache;
@@ -57,17 +59,17 @@ int cache_process() {
} }
cache = shm_rw; cache = shm_rw;
if (mkdir("/var/necronda/", 0755) < 0 && errno != EEXIST) { if (mkdir("/var/sesimos/", 0755) < 0 && errno != EEXIST) {
fprintf(stderr, ERR_STR "Unable to create directory '/var/necronda/': %s" CLR_STR "\n", strerror(errno)); fprintf(stderr, ERR_STR "Unable to create directory '/var/sesimos/': %s" CLR_STR "\n", strerror(errno));
return -3; return -3;
} }
if (mkdir("/var/necronda/server/", 0755) < 0 && errno != EEXIST) { if (mkdir("/var/sesimos/server/", 0755) < 0 && errno != EEXIST) {
fprintf(stderr, ERR_STR "Unable to create directory '/var/necronda/server/': %s" CLR_STR "\n", strerror(errno)); fprintf(stderr, ERR_STR "Unable to create directory '/var/sesimos/server/': %s" CLR_STR "\n", strerror(errno));
return -3; return -3;
} }
FILE *cache_file = fopen("/var/necronda/server/cache", "rb"); FILE *cache_file = fopen("/var/sesimos/server/cache", "rb");
if (cache_file != NULL) { if (cache_file != NULL) {
fread(cache, sizeof(cache_entry), CACHE_ENTRIES, cache_file); fread(cache, sizeof(cache_entry), CACHE_ENTRIES, cache_file);
fclose(cache_file); fclose(cache_file);
@@ -102,13 +104,13 @@ int cache_process() {
FILE *comp_file_gz = NULL; FILE *comp_file_gz = NULL;
FILE *comp_file_br = NULL; FILE *comp_file_br = NULL;
if (compress) { if (compress) {
sprintf(buf, "%.*s/.necronda-server", cache[i].webroot_len, cache[i].filename); sprintf(buf, "%.*s/.sesimos", cache[i].webroot_len, cache[i].filename);
if (mkdir(buf, 0755) != 0 && errno != EEXIST) { if (mkdir(buf, 0755) != 0 && errno != EEXIST) {
fprintf(stderr, ERR_STR "Unable to create directory %s: %s" CLR_STR "\n", buf, strerror(errno)); fprintf(stderr, ERR_STR "Unable to create directory %s: %s" CLR_STR "\n", buf, strerror(errno));
goto comp_err; goto comp_err;
} }
sprintf(buf, "%.*s/.necronda-server/cache", cache[i].webroot_len, cache[i].filename); sprintf(buf, "%.*s/.sesimos/cache", cache[i].webroot_len, cache[i].filename);
if (mkdir(buf, 0700) != 0 && errno != EEXIST) { if (mkdir(buf, 0700) != 0 && errno != EEXIST) {
fprintf(stderr, ERR_STR "Unable to create directory %s: %s" CLR_STR "\n", buf, strerror(errno)); fprintf(stderr, ERR_STR "Unable to create directory %s: %s" CLR_STR "\n", buf, strerror(errno));
goto comp_err; goto comp_err;
@@ -123,10 +125,10 @@ int cache_process() {
buf[strlen(rel_path)] = 0; buf[strlen(rel_path)] = 0;
p_len_gz = snprintf(filename_comp_gz, sizeof(filename_comp_gz), p_len_gz = snprintf(filename_comp_gz, sizeof(filename_comp_gz),
"%.*s/.necronda-server/cache/%s.gz", "%.*s/.sesimos/cache/%s.gz",
cache[i].webroot_len, cache[i].filename, buf); cache[i].webroot_len, cache[i].filename, buf);
p_len_br = snprintf(filename_comp_br, sizeof(filename_comp_br), p_len_br = snprintf(filename_comp_br, sizeof(filename_comp_br),
"%.*s/.necronda-server/cache/%s.br", "%.*s/.sesimos/cache/%s.br",
cache[i].webroot_len, cache[i].filename, buf); cache[i].webroot_len, cache[i].filename, buf);
if (p_len_gz < 0 || p_len_gz >= sizeof(filename_comp_gz) || if (p_len_gz < 0 || p_len_gz >= sizeof(filename_comp_gz) ||
p_len_br < 0 || p_len_br >= sizeof(filename_comp_br)) p_len_br < 0 || p_len_br >= sizeof(filename_comp_br))
@@ -201,7 +203,7 @@ int cache_process() {
if (cache_changed) { if (cache_changed) {
cache_changed = 0; cache_changed = 0;
cache_file = fopen("/var/necronda/server/cache", "wb"); cache_file = fopen("/var/sesimos/server/cache", "wb");
if (cache_file == NULL) { if (cache_file == NULL) {
fprintf(stderr, ERR_STR "Unable to open cache file: %s" CLR_STR "\n", strerror(errno)); fprintf(stderr, ERR_STR "Unable to open cache file: %s" CLR_STR "\n", strerror(errno));
free(buf); free(buf);

View File

@@ -1,12 +1,12 @@
/** /**
* Necronda Web Server * sesimos - secure, simple, modern web server
* File cache implementation (header file) * File cache implementation (header file)
* src/lib/cache.h * src/lib/cache.h
* Lorenz Stechauner, 2020-12-19 * Lorenz Stechauner, 2020-12-19
*/ */
#ifndef NECRONDA_SERVER_CACHE_H #ifndef SESIMOS_CACHE_H
#define NECRONDA_SERVER_CACHE_H #define SESIMOS_CACHE_H
#include "uri.h" #include "uri.h"
@@ -46,4 +46,4 @@ int cache_filename_comp_invalid(const char *filename);
int uri_cache_init(http_uri *uri); int uri_cache_init(http_uri *uri);
#endif //NECRONDA_SERVER_CACHE_H #endif //SESIMOS_CACHE_H

View File

@@ -1,14 +1,16 @@
/** /**
* Necronda Web Server * sesimos - secure, simple, modern web server
* Compression interface * Compression interface
* src/lib/compress.c * src/lib/compress.c
* Lorenz Stechauner, 2021-05-05 * Lorenz Stechauner, 2021-05-05
*/ */
#include "compress.h" #include "compress.h"
#include <malloc.h> #include <malloc.h>
#include <errno.h> #include <errno.h>
int compress_init(compress_ctx *ctx, int mode) { int compress_init(compress_ctx *ctx, int mode) {
ctx->gzip = NULL; ctx->gzip = NULL;
ctx->brotli = NULL; ctx->brotli = NULL;

View File

@@ -1,12 +1,12 @@
/** /**
* Necronda Web Server * sesimos - secure, simple, modern web server
* Compression interface (header file) * Compression interface (header file)
* src/lib/compress.h * src/lib/compress.h
* Lorenz Stechauner, 2021-05-05 * Lorenz Stechauner, 2021-05-05
*/ */
#ifndef NECRONDA_SERVER_COMPRESS_H #ifndef SESIMOS_COMPRESS_H
#define NECRONDA_SERVER_COMPRESS_H #define SESIMOS_COMPRESS_H
#include <zlib.h> #include <zlib.h>
#include <brotli/encode.h> #include <brotli/encode.h>
@@ -34,4 +34,4 @@ int compress_compress_mode(compress_ctx *ctx, int mode, const char *in, unsigned
int compress_free(compress_ctx *ctx); int compress_free(compress_ctx *ctx);
#endif //NECRONDA_SERVER_COMPRESS_H #endif //SESIMOS_COMPRESS_H

View File

@@ -1,5 +1,5 @@
/** /**
* Necronda Web Server * sesimos - secure, simple, modern web server
* Configuration file loader * Configuration file loader
* src/lib/config.c * src/lib/config.c
* Lorenz Stechauner, 2021-01-05 * Lorenz Stechauner, 2021-01-05
@@ -7,6 +7,7 @@
#include "config.h" #include "config.h"
#include "utils.h" #include "utils.h"
#include <stdio.h> #include <stdio.h>
#include <sys/ipc.h> #include <sys/ipc.h>
#include <sys/shm.h> #include <sys/shm.h>
@@ -14,6 +15,7 @@
#include <errno.h> #include <errno.h>
#include <stdlib.h> #include <stdlib.h>
t_config *config; t_config *config;
char geoip_dir[256], dns_server[256]; char geoip_dir[256], dns_server[256];

View File

@@ -1,12 +1,12 @@
/** /**
* Necronda Web Server * sesimos - secure, simple, modern web server
* Configuration file loader (header file) * Configuration file loader (header file)
* src/lib/config.h * src/lib/config.h
* Lorenz Stechauner, 2021-01-05 * Lorenz Stechauner, 2021-01-05
*/ */
#ifndef NECRONDA_SERVER_CONFIG_H #ifndef SESIMOS_CONFIG_H
#define NECRONDA_SERVER_CONFIG_H #define SESIMOS_CONFIG_H
#include "uri.h" #include "uri.h"
@@ -19,7 +19,7 @@
#define CONFIG_TYPE_REVERSE_PROXY 2 #define CONFIG_TYPE_REVERSE_PROXY 2
#ifndef DEFAULT_CONFIG_FILE #ifndef DEFAULT_CONFIG_FILE
# define DEFAULT_CONFIG_FILE "/etc/necronda/server.conf" # define DEFAULT_CONFIG_FILE "/etc/sesimos/server.conf"
#endif #endif
@@ -61,4 +61,4 @@ int config_load(const char *filename);
int config_unload(); int config_unload();
#endif //NECRONDA_SERVER_CONFIG_H #endif //SESIMOS_CONFIG_H

View File

@@ -1,5 +1,5 @@
/** /**
* Necronda Web Server * sesimos - secure, simple, modern web server
* FastCGI interface implementation * FastCGI interface implementation
* src/lib/fastcgi.c * src/lib/fastcgi.c
* Lorenz Stechauner, 2020-12-26 * Lorenz Stechauner, 2020-12-26
@@ -9,11 +9,13 @@
#include "utils.h" #include "utils.h"
#include "compress.h" #include "compress.h"
#include "../server.h" #include "../server.h"
#include <sys/un.h> #include <sys/un.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <errno.h> #include <errno.h>
#include <string.h> #include <string.h>
char *fastcgi_add_param(char *buf, const char *key, const char *value) { char *fastcgi_add_param(char *buf, const char *key, const char *value) {
char *ptr = buf; char *ptr = buf;
unsigned long key_len = strlen(key); unsigned long key_len = strlen(key);
@@ -71,8 +73,8 @@ int fastcgi_init(fastcgi_conn *conn, int mode, unsigned int client_num, unsigned
conn->socket = fcgi_sock; conn->socket = fcgi_sock;
struct sockaddr_un sock_addr = {AF_UNIX}; struct sockaddr_un sock_addr = {AF_UNIX};
if (conn->mode == FASTCGI_NECRONDA) { if (conn->mode == FASTCGI_SESIMOS) {
snprintf(sock_addr.sun_path, sizeof(sock_addr.sun_path) - 1, "%s", NECRONDA_BACKEND_SOCKET); snprintf(sock_addr.sun_path, sizeof(sock_addr.sun_path) - 1, "%s", SESIMOS_BACKEND_SOCKET);
} else if (conn->mode == FASTCGI_PHP) { } else if (conn->mode == FASTCGI_PHP) {
snprintf(sock_addr.sun_path, sizeof(sock_addr.sun_path) - 1, "%s", PHP_FPM_SOCKET); snprintf(sock_addr.sun_path, sizeof(sock_addr.sun_path) - 1, "%s", PHP_FPM_SOCKET);
} }
@@ -342,7 +344,7 @@ int fastcgi_header(fastcgi_conn *conn, http_res *res, char *err_msg) {
free(content); free(content);
return 1; return 1;
} else if (header.type == FCGI_STDERR) { } else if (header.type == FCGI_STDERR) {
// TODO implement Necronda backend error handling // TODO implement Sesimos backend error handling
if (conn->mode == FASTCGI_PHP) { if (conn->mode == FASTCGI_PHP) {
err = err || fastcgi_php_error(conn, content, content_len, err_msg); err = err || fastcgi_php_error(conn, content, content_len, err_msg);
} }
@@ -385,7 +387,7 @@ int fastcgi_header(fastcgi_conn *conn, http_res *res, char *err_msg) {
return 1; return 1;
} }
ret = http_parse_header_field(&res->hdr, ptr, pos0); ret = http_parse_header_field(&res->hdr, ptr, pos0, 0);
if (ret != 0) return (int) ret; if (ret != 0) return (int) ret;
if (pos0[2] == '\r' && pos0[3] == '\n') { if (pos0[2] == '\r' && pos0[3] == '\n') {
return 0; return 0;
@@ -483,7 +485,7 @@ int fastcgi_send(fastcgi_conn *conn, sock *client, int flags) {
return 0; return 0;
} else if (header.type == FCGI_STDERR) { } else if (header.type == FCGI_STDERR) {
// TODO implement Necronda backend error handling // TODO implement Sesimos backend error handling
if (conn->mode == FASTCGI_PHP) { if (conn->mode == FASTCGI_PHP) {
fastcgi_php_error(conn, content, content_len, buf0); fastcgi_php_error(conn, content, content_len, buf0);
} }
@@ -569,7 +571,7 @@ int fastcgi_dump(fastcgi_conn *conn, char *buf, long len) {
return 0; return 0;
} else if (header.type == FCGI_STDERR) { } else if (header.type == FCGI_STDERR) {
// TODO implement Necronda backend error handling // TODO implement Sesimos backend error handling
if (conn->mode == FASTCGI_PHP) { if (conn->mode == FASTCGI_PHP) {
fastcgi_php_error(conn, content, content_len, buf0); fastcgi_php_error(conn, content, content_len, buf0);
} }

View File

@@ -1,12 +1,12 @@
/** /**
* Necronda Web Server * sesimos - secure, simple, modern web server
* FastCGI interface implementation (header file) * FastCGI interface implementation (header file)
* src/lib/fastcgi.h * src/lib/fastcgi.h
* Lorenz Stechauner, 2020-12-26 * Lorenz Stechauner, 2020-12-26
*/ */
#ifndef NECRONDA_SERVER_FASTCGI_H #ifndef SESIMOS_FASTCGI_H
#define NECRONDA_SERVER_FASTCGI_H #define SESIMOS_FASTCGI_H
#include "include/fastcgi.h" #include "include/fastcgi.h"
#include "http.h" #include "http.h"
@@ -19,13 +19,13 @@
#define FASTCGI_COMPRESS_HOLD 8 #define FASTCGI_COMPRESS_HOLD 8
#define FASTCGI_PHP 1 #define FASTCGI_PHP 1
#define FASTCGI_NECRONDA 2 #define FASTCGI_SESIMOS 2
#ifndef PHP_FPM_SOCKET #ifndef PHP_FPM_SOCKET
# define PHP_FPM_SOCKET "/var/run/php-fpm/php-fpm.sock" # define PHP_FPM_SOCKET "/var/run/php-fpm/php-fpm.sock"
#endif #endif
#define NECRONDA_BACKEND_SOCKET "/var/run/necronda/necronda-backend.sock" #define SESIMOS_BACKEND_SOCKET "/var/run/sesimos/backend.sock"
typedef struct { typedef struct {
int mode; int mode;
@@ -54,4 +54,4 @@ int fastcgi_dump(fastcgi_conn *conn, char *buf, long len);
int fastcgi_receive(fastcgi_conn *conn, sock *client, unsigned long len); int fastcgi_receive(fastcgi_conn *conn, sock *client, unsigned long len);
#endif //NECRONDA_SERVER_FASTCGI_H #endif //SESIMOS_FASTCGI_H

View File

@@ -1,5 +1,5 @@
/** /**
* Necronda Web Server * sesimos - secure, simple, modern web server
* MaxMind GeoIP Database interface * MaxMind GeoIP Database interface
* src/lib/geoip.c * src/lib/geoip.c
* Lorenz Stechauner, 2021-05-04 * Lorenz Stechauner, 2021-05-04
@@ -7,6 +7,7 @@
#include "geoip.h" #include "geoip.h"
MMDB_entry_data_list_s *mmdb_json(MMDB_entry_data_list_s *list, char *str, long *str_off, long str_len) { 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) { switch (list->entry_data.type) {
case MMDB_DATA_TYPE_MAP: case MMDB_DATA_TYPE_MAP:

View File

@@ -1,13 +1,13 @@
/** /**
* Necronda Web Server * sesimos - secure, simple, modern web server
* MaxMind GeoIP Database interface (header file) * MaxMind GeoIP Database interface (header file)
* src/lib/geoip.h * src/lib/geoip.h
* Lorenz Stechauner, 2021-05-04 * Lorenz Stechauner, 2021-05-04
*/ */
#ifndef NECRONDA_SERVER_GEOIP_H #ifndef SESIMOS_GEOIP_H
#define NECRONDA_SERVER_GEOIP_H #define SESIMOS_GEOIP_H
#include <maxminddb.h> #include <maxminddb.h>
@@ -15,4 +15,4 @@
MMDB_entry_data_list_s *mmdb_json(MMDB_entry_data_list_s *list, char *str, long *str_off, long str_len); MMDB_entry_data_list_s *mmdb_json(MMDB_entry_data_list_s *list, char *str, long *str_off, long str_len);
#endif //NECRONDA_SERVER_GEOIP_H #endif //SESIMOS_GEOIP_H

View File

@@ -1,5 +1,5 @@
/** /**
* Necronda Web Server * sesimos - secure, simple, modern web server
* HTTP implementation * HTTP implementation
* src/lib/http.c * src/lib/http.c
* Lorenz Stechauner, 2020-12-09 * Lorenz Stechauner, 2020-12-09
@@ -8,8 +8,10 @@
#include "http.h" #include "http.h"
#include "utils.h" #include "utils.h"
#include "compress.h" #include "compress.h"
#include <string.h> #include <string.h>
void http_to_camel_case(char *str, int mode) { void http_to_camel_case(char *str, int mode) {
if (mode == HTTP_PRESERVE) if (mode == HTTP_PRESERVE)
return; return;
@@ -82,7 +84,7 @@ void http_free_res(http_res *res) {
http_free_hdr(&res->hdr); http_free_hdr(&res->hdr);
} }
int http_parse_header_field(http_hdr *hdr, const char *buf, const char *end_ptr) { int http_parse_header_field(http_hdr *hdr, const char *buf, const char *end_ptr, int flags) {
if (hdr->last_field_num > hdr->field_num) { if (hdr->last_field_num > hdr->field_num) {
print(ERR_STR "Unable to parse header: Invalid state" CLR_STR); print(ERR_STR "Unable to parse header: Invalid state" CLR_STR);
return 3; return 3;
@@ -115,7 +117,7 @@ int http_parse_header_field(http_hdr *hdr, const char *buf, const char *end_ptr)
char field_num = hdr->field_num; char field_num = hdr->field_num;
int found = http_get_header_field_num_len(hdr, buf, len1); int found = http_get_header_field_num_len(hdr, buf, len1);
if (found == -1) { if (!(flags & HTTP_MERGE_FIELDS) || found == -1) {
if (http_add_header_field_len(hdr, buf, len1, pos1, len2 < 0 ? 0 : len2) != 0) { if (http_add_header_field_len(hdr, buf, len1, pos1, len2 < 0 ? 0 : len2) != 0) {
print(ERR_STR "Unable to parse header: Too many header fields" CLR_STR); print(ERR_STR "Unable to parse header: Too many header fields" CLR_STR);
return 3; return 3;
@@ -203,7 +205,7 @@ int http_receive_request(sock *client, http_req *req) {
sprintf(req->uri, "%.*s", (int) len, pos1); sprintf(req->uri, "%.*s", (int) len, pos1);
sprintf(req->version, "%.3s", pos2 + 5); sprintf(req->version, "%.3s", pos2 + 5);
} else { } else {
int ret = http_parse_header_field(&req->hdr, ptr, pos0); int ret = http_parse_header_field(&req->hdr, ptr, pos0, HTTP_MERGE_FIELDS);
if (ret != 0) return ret; if (ret != 0) return ret;
} }
ptr = pos0 + 2; ptr = pos0 + 2;

View File

@@ -1,12 +1,12 @@
/** /**
* Necronda Web Server * sesimos - secure, simple, modern web server
* HTTP implementation (header file) * HTTP implementation (header file)
* src/lib/http.h * src/lib/http.h
* Lorenz Stechauner, 2020-12-09 * Lorenz Stechauner, 2020-12-09
*/ */
#ifndef NECRONDA_SERVER_HTTP_H #ifndef SESIMOS_HTTP_H
#define NECRONDA_SERVER_HTTP_H #define SESIMOS_HTTP_H
#include "sock.h" #include "sock.h"
@@ -22,6 +22,8 @@
#define HTTP_FIELD_EX_VALUE 1 #define HTTP_FIELD_EX_VALUE 1
#define HTTP_FIELD_EX_NAME 2 #define HTTP_FIELD_EX_NAME 2
#define HTTP_MERGE_FIELDS 1
#define HTTP_1XX_STR "\x1B[1;32m" #define HTTP_1XX_STR "\x1B[1;32m"
#define HTTP_2XX_STR "\x1B[1;32m" #define HTTP_2XX_STR "\x1B[1;32m"
#define HTTP_3XX_STR "\x1B[1;33m" #define HTTP_3XX_STR "\x1B[1;33m"
@@ -37,11 +39,11 @@
#define HTTP_MAX_HEADER_FIELD_NUM 64 #define HTTP_MAX_HEADER_FIELD_NUM 64
#ifndef SERVER_STR #ifndef SERVER_STR
# define SERVER_STR "Necronda" # define SERVER_STR "Sesimos"
#endif #endif
#ifndef SERVER_STR_HTML #ifndef SERVER_STR_HTML
# define SERVER_STR_HTML "Necronda&nbsp;web&nbsp;server" # define SERVER_STR_HTML "Sesimos&nbsp;web&nbsp;server"
#endif #endif
typedef struct { typedef struct {
@@ -106,6 +108,7 @@ typedef enum {
typedef struct { typedef struct {
unsigned short status; unsigned short status;
http_error_origin origin; http_error_origin origin;
const char* ws_key;
} http_status_ctx; } http_status_ctx;
extern const http_status http_statuses[]; extern const http_status http_statuses[];
@@ -140,7 +143,7 @@ void http_free_res(http_res *res);
int http_receive_request(sock *client, http_req *req); int http_receive_request(sock *client, http_req *req);
int http_parse_header_field(http_hdr *hdr, const char *buf, const char *end_ptr) ; int http_parse_header_field(http_hdr *hdr, const char *buf, const char *end_ptr, int flags);
const char *http_get_header_field(const http_hdr *hdr, const char *field_name); const char *http_get_header_field(const http_hdr *hdr, const char *field_name);
@@ -176,4 +179,4 @@ const http_doc_info *http_get_status_info(const http_status *status);
int http_get_compression(const http_req *req, const http_res *res); int http_get_compression(const http_req *req, const http_res *res);
#endif //NECRONDA_SERVER_HTTP_H #endif //SESIMOS_HTTP_H

View File

@@ -1,13 +1,14 @@
/** /**
* Necronda Web Server * sesimos - secure, simple, modern web server
* HTTP static implementation * HTTP static implementation
* src/lib/http_static.c * src/lib/http_static.c
* Lorenz Stechauner, 2021-05-03 * Lorenz Stechauner, 2021-05-03
*/ */
#include "../necronda.h" #include "../defs.h"
#include "http.h" #include "http.h"
const http_status http_statuses[] = { const http_status http_statuses[] = {
{100, "Informational", "Continue"}, {100, "Informational", "Continue"},
{101, "Informational", "Switching Protocols"}, {101, "Informational", "Switching Protocols"},

View File

@@ -1,12 +1,12 @@
/** /**
* Necronda Web Server * sesimos - secure, simple, modern web server
* FastCGI header file * FastCGI header file
* src/lib/include/fastcgi.h * src/lib/include/fastcgi.h
* Lorenz Stechauner, 2021-05-03 * Lorenz Stechauner, 2021-05-03
*/ */
#ifndef NECRONDA_SERVER_EXTERN_FASTCGI_H #ifndef SESIMOS_EXTERN_FASTCGI_H
#define NECRONDA_SERVER_EXTERN_FASTCGI_H #define SESIMOS_EXTERN_FASTCGI_H
/* /*
* Listening socket file number * Listening socket file number
@@ -119,4 +119,4 @@ typedef struct {
FCGI_UnknownTypeBody body; FCGI_UnknownTypeBody body;
} FCGI_UnknownTypeRecord; } FCGI_UnknownTypeRecord;
#endif //NECRONDA_SERVER_EXTERN_FASTCGI_H #endif //SESIMOS_EXTERN_FASTCGI_H

View File

@@ -1,14 +1,16 @@
/** /**
* Necronda Web Server * sesimos - secure, simple, modern web server
* Reverse proxy * Reverse proxy
* src/lib/rev_proxy.c * src/lib/rev_proxy.c
* Lorenz Stechauner, 2021-01-07 * Lorenz Stechauner, 2021-01-07
*/ */
#include "../defs.h"
#include "../server.h"
#include "rev_proxy.h" #include "rev_proxy.h"
#include "utils.h" #include "utils.h"
#include "compress.h" #include "compress.h"
#include "../server.h"
#include <openssl/ssl.h> #include <openssl/ssl.h>
#include <string.h> #include <string.h>
#include <errno.h> #include <errno.h>
@@ -16,6 +18,7 @@
#include <arpa/inet.h> #include <arpa/inet.h>
#include <sys/time.h> #include <sys/time.h>
sock rev_proxy; sock rev_proxy;
char *rev_proxy_host = NULL; char *rev_proxy_host = NULL;
struct timeval server_timeout = {.tv_sec = SERVER_TIMEOUT, .tv_usec = 0}; struct timeval server_timeout = {.tv_sec = SERVER_TIMEOUT, .tv_usec = 0};
@@ -31,8 +34,6 @@ int rev_proxy_preload() {
int rev_proxy_request_header(http_req *req, int enc) { int rev_proxy_request_header(http_req *req, int enc) {
char buf1[256], buf2[256]; char buf1[256], buf2[256];
int p_len; int p_len;
http_remove_header_field(&req->hdr, "Connection", HTTP_REMOVE_ALL);
http_add_header_field(&req->hdr, "Connection", "keep-alive");
const char *via = http_get_header_field(&req->hdr, "Via"); const char *via = http_get_header_field(&req->hdr, "Via");
sprintf(buf1, "HTTP/%s %s", req->version, SERVER_NAME); sprintf(buf1, "HTTP/%s %s", req->version, SERVER_NAME);
@@ -182,12 +183,12 @@ int rev_proxy_response_header(http_req *req, http_res *res, host_config *conf) {
int rev_proxy_init(http_req *req, http_res *res, http_status_ctx *ctx, host_config *conf, sock *client, http_status *custom_status, char *err_msg) { int rev_proxy_init(http_req *req, http_res *res, http_status_ctx *ctx, host_config *conf, sock *client, http_status *custom_status, char *err_msg) {
char buffer[CHUNK_SIZE]; char buffer[CHUNK_SIZE];
const char *connection, *upgrade, *ws_version;
long ret; long ret;
int tries = 0, retry = 0; int tries = 0, retry = 0;
if (rev_proxy.socket != 0 && strcmp(rev_proxy_host, conf->name) == 0 && sock_check(&rev_proxy) == 0) { if (rev_proxy.socket != 0 && strcmp(rev_proxy_host, conf->name) == 0 && sock_check(&rev_proxy) == 0)
goto rev_proxy; goto rev_proxy;
}
retry: retry:
if (rev_proxy.socket != 0) { if (rev_proxy.socket != 0) {
@@ -288,6 +289,22 @@ int rev_proxy_init(http_req *req, http_res *res, http_status_ctx *ctx, host_conf
print(BLUE_STR "Established new connection with " BLD_STR "[%s]:%i" CLR_STR, buffer, conf->rev_proxy.port); print(BLUE_STR "Established new connection with " BLD_STR "[%s]:%i" CLR_STR, buffer, conf->rev_proxy.port);
rev_proxy: rev_proxy:
connection = http_get_header_field(&req->hdr, "Connection");
if (connection != NULL && (strstr(connection, "upgrade") != NULL || strstr(connection, "Upgrade") != NULL)) {
upgrade = http_get_header_field(&req->hdr, "Upgrade");
ws_version = http_get_header_field(&req->hdr, "Sec-WebSocket-Version");
if (upgrade != NULL && ws_version != NULL && strcmp(upgrade, "websocket") == 0 && strcmp(ws_version, "13") == 0) {
ctx->ws_key = http_get_header_field(&req->hdr, "Sec-WebSocket-Key");
} else {
res->status = http_get_status(501);
ctx->origin = INTERNAL;
return -1;
}
} else {
http_remove_header_field(&req->hdr, "Connection", HTTP_REMOVE_ALL);
http_add_header_field(&req->hdr, "Connection", "keep-alive");
}
ret = rev_proxy_request_header(req, (int) client->enc); ret = rev_proxy_request_header(req, (int) client->enc);
if (ret != 0) { if (ret != 0) {
res->status = http_get_status(500); res->status = http_get_status(500);
@@ -421,7 +438,7 @@ int rev_proxy_init(http_req *req, http_res *res, http_status_ctx *ctx, host_conf
goto proxy_err; goto proxy_err;
} }
} else { } else {
ret = http_parse_header_field(&res->hdr, ptr, pos0); ret = http_parse_header_field(&res->hdr, ptr, pos0, 0);
if (ret != 0) { if (ret != 0) {
res->status = http_get_status(502); res->status = http_get_status(502);
ctx->origin = SERVER_RES; ctx->origin = SERVER_RES;
@@ -452,7 +469,6 @@ int rev_proxy_init(http_req *req, http_res *res, http_status_ctx *ctx, host_conf
} }
int rev_proxy_send(sock *client, unsigned long len_to_send, int flags) { int rev_proxy_send(sock *client, unsigned long len_to_send, int flags) {
// TODO handle websockets
char buffer[CHUNK_SIZE], comp_out[CHUNK_SIZE], buf[256], *ptr; char buffer[CHUNK_SIZE], comp_out[CHUNK_SIZE], buf[256], *ptr;
long ret = 0, len, snd_len; long ret = 0, len, snd_len;
int finish_comp = 0; int finish_comp = 0;

View File

@@ -1,12 +1,12 @@
/** /**
* Necronda Web Server * sesimos - secure, simple, modern web server
* Reverse proxy (header file) * Reverse proxy (header file)
* src/lib/rev_proxy.h * src/lib/rev_proxy.h
* Lorenz Stechauner, 2021-01-07 * Lorenz Stechauner, 2021-01-07
*/ */
#ifndef NECRONDA_SERVER_REV_PROXY_H #ifndef SESIMOS_REV_PROXY_H
#define NECRONDA_SERVER_REV_PROXY_H #define SESIMOS_REV_PROXY_H
#define REV_PROXY_CHUNKED 1 #define REV_PROXY_CHUNKED 1
#define REV_PROXY_COMPRESS_GZ 2 #define REV_PROXY_COMPRESS_GZ 2
@@ -35,4 +35,4 @@ int rev_proxy_send(sock *client, unsigned long len_to_send, int flags);
int rev_proxy_dump(char *buf, long len); int rev_proxy_dump(char *buf, long len);
#endif //NECRONDA_SERVER_REV_PROXY_H #endif //SESIMOS_REV_PROXY_H

View File

@@ -1,16 +1,19 @@
/** /**
* Necronda Web Server * sesimos - secure, simple, modern web server
* Basic TCP and TLS socket * Basic TCP and TLS socket
* src/lib/sock.c * src/lib/sock.c
* Lorenz Stechauner, 2021-01-07 * Lorenz Stechauner, 2021-01-07
*/ */
#include "sock.h" #include "sock.h"
#include <openssl/err.h> #include <openssl/err.h>
#include <openssl/ssl.h> #include <openssl/ssl.h>
#include <string.h> #include <string.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <unistd.h> #include <unistd.h>
#include <poll.h>
int sock_enc_error(sock *s) { int sock_enc_error(sock *s) {
return (int) s->enc ? SSL_get_error(s->ssl, (int) s->_last_ret) : 0; return (int) s->enc ? SSL_get_error(s->ssl, (int) s->_last_ret) : 0;
@@ -116,3 +119,29 @@ int sock_check(sock *s) {
char buf; char buf;
return recv(s->socket, &buf, 1, MSG_PEEK | MSG_DONTWAIT) == 1; return recv(s->socket, &buf, 1, MSG_PEEK | MSG_DONTWAIT) == 1;
} }
int sock_poll(sock *sockets[], sock *ready[], short events, int n_sock, int timeout_ms) {
struct pollfd fds[n_sock];
for (int i = 0; i < n_sock; i++) {
fds[i].fd = sockets[i]->socket;
fds[i].events = events;
}
int ret = poll(fds, n_sock, timeout_ms);
if (ret < 0 || ready == NULL) return ret;
int j = 0;
for (int i = 0; i < n_sock; i++) {
if (fds[i].revents & events)
ready[j++] = sockets[i];
}
return j;
}
int sock_poll_read(sock *sockets[], sock *readable[], int n_sock, int timeout_ms) {
return sock_poll(sockets, readable, POLLIN, n_sock, timeout_ms);
}
int sock_poll_write(sock *sockets[], sock *writable[], int n_sock, int timeout_ms) {
return sock_poll(sockets, writable, POLLOUT, n_sock, timeout_ms);
}

View File

@@ -1,14 +1,15 @@
/** /**
* Necronda Web Server * sesimos - secure, simple, modern web server
* Basic TCP and TLS socket (header file) * Basic TCP and TLS socket (header file)
* src/lib/sock.h * src/lib/sock.h
* Lorenz Stechauner, 2021-01-07 * Lorenz Stechauner, 2021-01-07
*/ */
#ifndef NECRONDA_SERVER_SOCK_H #ifndef SESIMOS_SOCK_H
#define NECRONDA_SERVER_SOCK_H #define SESIMOS_SOCK_H
#include <openssl/crypto.h> #include <openssl/crypto.h>
#include <sys/socket.h>
typedef struct { typedef struct {
unsigned int enc:1; unsigned int enc:1;
@@ -37,4 +38,10 @@ int sock_close(sock *s);
int sock_check(sock *s); int sock_check(sock *s);
#endif //NECRONDA_SERVER_SOCK_H int sock_poll(sock *sockets[], sock *readable[], short events, int n_sock, int timeout_ms);
int sock_poll_read(sock *sockets[], sock *readable[], int n_sock, int timeout_ms);
int sock_poll_write(sock *sockets[], sock *writable[], int n_sock, int timeout_ms);
#endif //SESIMOS_SOCK_H

View File

@@ -1,5 +1,5 @@
/** /**
* Necronda Web Server * sesimos - secure, simple, modern web server
* URI and path handlers * URI and path handlers
* src/lib/uri.c * src/lib/uri.c
* Lorenz Stechauner, 2020-12-13 * Lorenz Stechauner, 2020-12-13
@@ -7,9 +7,11 @@
#include "uri.h" #include "uri.h"
#include "utils.h" #include "utils.h"
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
int path_is_directory(const char *path) { int path_is_directory(const char *path) {
struct stat statbuf; struct stat statbuf;
return stat(path, &statbuf) == 0 && S_ISDIR(statbuf.st_mode) != 0; return stat(path, &statbuf) == 0 && S_ISDIR(statbuf.st_mode) != 0;

View File

@@ -1,12 +1,12 @@
/** /**
* Necronda Web Server * sesimos - secure, simple, modern web server
* URI and path handlers (header file) * URI and path handlers (header file)
* src/lib/uri.h * src/lib/uri.h
* Lorenz Stechauner, 2020-12-13 * Lorenz Stechauner, 2020-12-13
*/ */
#ifndef NECRONDA_SERVER_URI_H #ifndef SESIMOS_URI_H
#define NECRONDA_SERVER_URI_H #define SESIMOS_URI_H
#include <sys/stat.h> #include <sys/stat.h>
@@ -44,4 +44,4 @@ int uri_init_cache(http_uri *uri);
void uri_free(http_uri *uri); void uri_free(http_uri *uri);
#endif //NECRONDA_SERVER_URI_H #endif //SESIMOS_URI_H

View File

@@ -1,15 +1,17 @@
/** /**
* Necronda Web Server * sesimos - secure, simple, modern web server
* Utilities * Utilities
* src/lib/utils.c * src/lib/utils.c
* Lorenz Stechauner, 2020-12-03 * Lorenz Stechauner, 2020-12-03
*/ */
#include "utils.h" #include "utils.h"
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
char *log_prefix; char *log_prefix;
char *format_duration(unsigned long micros, char *buf) { char *format_duration(unsigned long micros, char *buf) {
@@ -172,3 +174,25 @@ int str_trim_lws(char **start, char **end) {
(*end)++; (*end)++;
return 0; return 0;
} }
int base64_encode(void *data, unsigned long data_len, char *output, unsigned long *output_len) {
unsigned long out_len = 4 * ((data_len + 2) / 3);
if (output_len != NULL) *output_len = out_len;
for (int i = 0, j = 0; i < data_len;) {
unsigned int octet_a = (i < data_len) ? ((unsigned char *) data)[i++] : 0;
unsigned int octet_b = (i < data_len) ? ((unsigned char *) data)[i++] : 0;
unsigned int octet_c = (i < data_len) ? ((unsigned char *) data)[i++] : 0;
unsigned int triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c;
output[j++] = base64_encode_table[(triple >> 3 * 6) & 0x3F];
output[j++] = base64_encode_table[(triple >> 2 * 6) & 0x3F];
output[j++] = base64_encode_table[(triple >> 1 * 6) & 0x3F];
output[j++] = base64_encode_table[(triple >> 0 * 6) & 0x3F];
}
for (int i = 0; i < base64_mod_table[data_len % 3]; i++)
output[out_len - 1 - i] = '=';
output[out_len] = 0;
return 0;
}

View File

@@ -1,12 +1,12 @@
/** /**
* Necronda Web Server * sesimos - secure, simple, modern web server
* Utilities (header file) * Utilities (header file)
* src/lib/utils.h * src/lib/utils.h
* Lorenz Stechauner, 2020-12-03 * Lorenz Stechauner, 2020-12-03
*/ */
#ifndef NECRONDA_SERVER_UTILS_H #ifndef SESIMOS_UTILS_H
#define NECRONDA_SERVER_UTILS_H #define SESIMOS_UTILS_H
#include <stdio.h> #include <stdio.h>
@@ -20,6 +20,10 @@
extern char *log_prefix; extern char *log_prefix;
static const char base64_encode_table[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static const int base64_mod_table[3] = {0, 2, 1};
#define out_1(fmt) fprintf(stdout, "%s" fmt "\n", log_prefix) #define out_1(fmt) fprintf(stdout, "%s" fmt "\n", log_prefix)
#define out_2(fmt, args...) fprintf(stdout, "%s" fmt "\n", log_prefix, args) #define out_2(fmt, args...) fprintf(stdout, "%s" fmt "\n", log_prefix, args)
@@ -46,4 +50,6 @@ int str_trim(char **start, char **end);
int str_trim_lws(char **start, char **end); int str_trim_lws(char **start, char **end);
#endif //NECRONDA_SERVER_UTILS_H int base64_encode(void *data, unsigned long data_len, char *output, unsigned long *output_len);
#endif //SESIMOS_UTILS_H

201
src/lib/websocket.c Normal file
View File

@@ -0,0 +1,201 @@
/**
* sesimos - secure, simple, modern web server
* WebSocket reverse proxy
* src/lib/websocket.c
* Lorenz Stechauner, 2022-08-16
*/
#include "../defs.h"
#include "websocket.h"
#include "utils.h"
#include <string.h>
#include <openssl/sha.h>
#include <errno.h>
#include <signal.h>
int terminate = 0;
void ws_terminate() {
terminate = 1;
}
int ws_calc_accept_key(const char *key, char *accept_key) {
if (key == NULL || accept_key == NULL)
return -1;
char input[256] = "";
unsigned char output[SHA_DIGEST_LENGTH];
strcat(input, key);
strcat(input, ws_key_uuid);
if (SHA1((unsigned char *) input, strlen(input), output) == NULL) {
return -2;
}
base64_encode(output, sizeof(output), accept_key, NULL);
return 0;
}
int ws_recv_frame_header(sock *s, ws_frame *frame) {
unsigned char buf[12];
long ret = sock_recv(s, buf, 2, 0);
if (ret < 0) {
print(ERR_STR "Unable to receive from socket: %s" CLR_STR, strerror(errno));
return -1;
} else if (ret != 2) {
print(ERR_STR "Unable to receive 2 bytes from socket" CLR_STR);
return -2;
}
unsigned short bits = (buf[0] << 8) | buf[1];
frame->f_fin = (bits >> 15) & 1;
frame->f_rsv1 = (bits >> 14) & 1;
frame->f_rsv2 = (bits >> 13) & 1;
frame->f_rsv3 = (bits >> 12) & 1;
frame->opcode = (bits >> 8) & 0xF;
frame->f_mask = (bits >> 7) & 1;
unsigned short len = (bits & 0x7F);
int remaining = frame->f_mask ? 4 : 0;
if (len == 126) {
remaining += 2;
} else if (len == 127) {
remaining += 8;
}
ret = sock_recv(s, buf, remaining, 0);
if (ret < 0) {
print(ERR_STR "Unable to receive from socket: %s" CLR_STR, strerror(errno));
return -1;
} else if (ret != remaining) {
print(ERR_STR "Unable to receive correct number of bytes from socket" CLR_STR);
return -2;
}
if (len == 126) {
frame->len = (((unsigned long) buf[0]) << 8) | ((unsigned long) buf[1]);
} else if (len == 127) {
frame->len =
(((unsigned long) buf[0]) << 56) |
(((unsigned long) buf[1]) << 48) |
(((unsigned long) buf[2]) << 40) |
(((unsigned long) buf[3]) << 32) |
(((unsigned long) buf[4]) << 24) |
(((unsigned long) buf[5]) << 16) |
(((unsigned long) buf[6]) << 8) |
(((unsigned long) buf[7]) << 0);
} else {
frame->len = len;
}
if (frame->f_mask) memcpy(frame->masking_key, buf + (remaining - 4), 4);
return 0;
}
int ws_send_frame_header(sock *s, ws_frame *frame) {
unsigned char buf[14], *ptr = buf;
unsigned short len;
if (frame->len > 0x7FFF) {
len = 127;
} else if (frame->len > 125) {
len = 126;
} else {
len = frame->len;
}
unsigned short bits =
(frame->f_fin << 15) |
(frame->f_rsv1 << 14) |
(frame->f_rsv2 << 13) |
(frame->f_rsv3 << 12) |
(frame->opcode << 8) |
(frame->f_mask << 7) |
len;
ptr++[0] = bits >> 8;
ptr++[0] = bits & 0xFF;
if (len >= 126) {
for (int i = (len == 126 ? 2 : 8) - 1; i >= 0; i--)
ptr++[0] = (unsigned char) ((frame->len >> (i * 8)) & 0xFF);
}
if (frame->f_mask) {
memcpy(ptr, frame->masking_key, 4);
ptr += 4;
}
long ret = sock_send(s, buf, ptr - buf, frame->len != 0 ? MSG_MORE : 0);
if (ret < 0) {
print(ERR_STR "Unable to send to socket: %s" CLR_STR, strerror(errno));
return -1;
} else if (ret != ptr - buf) {
print(ERR_STR "Unable to send to socket" CLR_STR);
return -2;
}
return 0;
}
int ws_handle_connection(sock *s1, sock *s2) {
sock *poll_socks[2] = {s1, s2};
sock *readable[2];
int n_sock = 2;
ws_frame frame;
char buf[CHUNK_SIZE];
int poll, closes = 0;
long ret;
signal(SIGINT, ws_terminate);
signal(SIGTERM, ws_terminate);
while (!terminate && closes != 3) {
poll = sock_poll_read(poll_socks, readable, n_sock, WS_TIMEOUT * 1000);
if (terminate) {
break;
} else if (poll < 0) {
print(ERR_STR "Unable to poll sockets: %s" CLR_STR, strerror(errno));
return -1;
} else if (poll == 0) {
print(ERR_STR "Connection timed out" CLR_STR);
return -2;
}
for (int i = 0; i < poll; i++) {
sock *s = readable[i];
sock *o = (s == s1) ? s2 : s1;
if (ws_recv_frame_header(s, &frame) != 0) return -3;
if (frame.opcode == 0x8) {
n_sock--;
if (s == s1) {
poll_socks[0] = s2;
closes |= 1;
} else {
closes |= 2;
}
}
if (ws_send_frame_header(o, &frame) != 0) return -3;
if (frame.len > 0) {
ret = sock_splice(o, s, buf, sizeof(buf), frame.len);
if (ret < 0) {
print(ERR_STR "Unable to forward data in WebSocket: %s" CLR_STR, strerror(errno));
return -3;
} else if (ret != frame.len) {
print(ERR_STR "Unable to forward correct number of bytes in WebSocket" CLR_STR);
return -3;
}
}
}
}
return 0;
}

36
src/lib/websocket.h Normal file
View File

@@ -0,0 +1,36 @@
/**
* sesimos - secure, simple, modern web server
* WebSocket reverse proxy (header file)
* src/lib/websocket.h
* Lorenz Stechauner, 2022-08-16
*/
#ifndef SESIMOS_WEBSOCKET_H
#define SESIMOS_WEBSOCKET_H
#include "sock.h"
#define WS_TIMEOUT 3600
const char *ws_key_uuid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
typedef struct {
unsigned char f_fin:1;
unsigned char f_rsv1:1;
unsigned char f_rsv2:1;
unsigned char f_rsv3:1;
unsigned char opcode:4;
unsigned char f_mask:1;
unsigned long len;
char masking_key[4];
} ws_frame;
int ws_calc_accept_key(const char *key, char *accept_key);
int ws_recv_frame_header(sock *s, ws_frame *frame);
int ws_send_frame_header(sock *s, ws_frame *frame);
int ws_handle_connection(sock *s1, sock *s2);
#endif //SESIMOS_WEBSOCKET_H

View File

@@ -1,23 +0,0 @@
/**
* Necronda Web Server
* Definitions
* src/necronda.h
* Lorenz Stechauner, 2021-05-04
*/
#ifndef NECRONDA_SERVER_NECRONDA_H
#define NECRONDA_SERVER_NECRONDA_H
#define NECRONDA_VERSION "4.5"
#define SERVER_STR "Necronda/" NECRONDA_VERSION
#define SERVER_STR_HTML "Necronda&nbsp;web&nbsp;server&nbsp;" NECRONDA_VERSION
#ifndef DEFAULT_HOST
# define DEFAULT_HOST "www.necronda.net"
#endif
#ifndef SERVER_NAME
# define SERVER_NAME DEFAULT_HOST
#endif
#endif //NECRONDA_SERVER_NECRONDA_H

View File

@@ -1,13 +1,13 @@
/** /**
* Necronda Web Server * Sesimos - secure, simple, modern web server
* Main executable * Main executable
* src/necronda-server.c * src/server.c
* Lorenz Stechauner, 2020-12-03 * Lorenz Stechauner, 2020-12-03
*/ */
#define _POSIX_C_SOURCE 199309L #define _POSIX_C_SOURCE 199309L
#include "necronda.h" #include "defs.h"
#include "server.h" #include "server.h"
#include "client.h" #include "client.h"
@@ -22,7 +22,7 @@
#include <sys/socket.h> #include <sys/socket.h>
#include <signal.h> #include <signal.h>
#include <unistd.h> #include <unistd.h>
#include <sys/select.h> #include <poll.h>
#include <string.h> #include <string.h>
#include <errno.h> #include <errno.h>
#include <arpa/inet.h> #include <arpa/inet.h>
@@ -34,6 +34,7 @@
#include <openssl/conf.h> #include <openssl/conf.h>
#include <dirent.h> #include <dirent.h>
int active = 1; int active = 1;
const char *config_file; const char *config_file;
int sockets[NUM_SOCKETS]; int sockets[NUM_SOCKETS];
@@ -153,8 +154,7 @@ void terminate() {
int main(int argc, const char *argv[]) { int main(int argc, const char *argv[]) {
const int YES = 1; const int YES = 1;
fd_set socket_fds, read_socket_fds; struct pollfd poll_fds[NUM_SOCKETS];
int max_socket_fd = 0;
int ready_sockets_num; int ready_sockets_num;
long client_num = 0; long client_num = 0;
char buf[1024]; char buf[1024];
@@ -169,8 +169,6 @@ int main(int argc, const char *argv[]) {
memset(children, 0, sizeof(children)); memset(children, 0, sizeof(children));
memset(mmdbs, 0, sizeof(mmdbs)); memset(mmdbs, 0, sizeof(mmdbs));
struct timeval timeout;
const struct sockaddr_in6 addresses[2] = { const struct sockaddr_in6 addresses[2] = {
{.sin6_family = AF_INET6, .sin6_addr = IN6ADDR_ANY_INIT, .sin6_port = htons(80)}, {.sin6_family = AF_INET6, .sin6_addr = IN6ADDR_ANY_INIT, .sin6_port = htons(80)},
{.sin6_family = AF_INET6, .sin6_addr = IN6ADDR_ANY_INIT, .sin6_port = htons(443)} {.sin6_family = AF_INET6, .sin6_addr = IN6ADDR_ANY_INIT, .sin6_port = htons(443)}
@@ -180,7 +178,7 @@ int main(int argc, const char *argv[]) {
fprintf(stderr, ERR_STR "Unable to set stdout to line-buffered mode: %s" CLR_STR, strerror(errno)); fprintf(stderr, ERR_STR "Unable to set stdout to line-buffered mode: %s" CLR_STR, strerror(errno));
return 1; return 1;
} }
printf("Necronda Web Server " NECRONDA_VERSION "\n"); printf("Sesimos web server " SERVER_VERSION "\n");
ret = config_init(); ret = config_init();
if (ret != 0) { if (ret != 0) {
@@ -191,7 +189,7 @@ int main(int argc, const char *argv[]) {
for (int i = 1; i < argc; i++) { for (int i = 1; i < argc; i++) {
const char *arg = argv[i]; const char *arg = argv[i];
if (strcmp(arg, "-h") == 0 || strcmp(arg, "--help") == 0) { if (strcmp(arg, "-h") == 0 || strcmp(arg, "--help") == 0) {
printf("Usage: necronda-server [-h] [-c <CONFIG-FILE>]\n" printf("Usage: sesimos [-h] [-c <CONFIG-FILE>]\n"
"\n" "\n"
"Options:\n" "Options:\n"
" -c, --config <CONFIG-FILE> path to the config file. If not provided, default will be used\n" " -c, --config <CONFIG-FILE> path to the config file. If not provided, default will be used\n"
@@ -338,29 +336,23 @@ int main(int argc, const char *argv[]) {
} }
} }
FD_ZERO(&socket_fds);
for (int i = 0; i < NUM_SOCKETS; i++) { for (int i = 0; i < NUM_SOCKETS; i++) {
FD_SET(sockets[i], &socket_fds); poll_fds[i].fd = sockets[i];
if (sockets[i] > max_socket_fd) { poll_fds[i].events = POLLIN;
max_socket_fd = sockets[i];
}
} }
fprintf(stderr, "Ready to accept connections\n"); fprintf(stderr, "Ready to accept connections\n");
while (active) { while (active) {
timeout.tv_sec = 1; ready_sockets_num = poll(poll_fds, NUM_SOCKETS, 1000);
timeout.tv_usec = 0;
read_socket_fds = socket_fds;
ready_sockets_num = select(max_socket_fd + 1, &read_socket_fds, NULL, NULL, &timeout);
if (ready_sockets_num < 0) { if (ready_sockets_num < 0) {
fprintf(stderr, ERR_STR "Unable to select sockets: %s" CLR_STR "\n", strerror(errno)); fprintf(stderr, ERR_STR "Unable to poll sockets: %s" CLR_STR "\n", strerror(errno));
terminate(); terminate();
return 1; return 1;
} }
for (int i = 0; i < NUM_SOCKETS; i++) { for (int i = 0; i < NUM_SOCKETS; i++) {
if (FD_ISSET(sockets[i], &read_socket_fds)) { if (poll_fds[i].revents & POLLIN) {
client_fd = accept(sockets[i], (struct sockaddr *) &client_addr, &client_addr_len); client_fd = accept(sockets[i], (struct sockaddr *) &client_addr, &client_addr_len);
if (client_fd < 0) { if (client_fd < 0) {
fprintf(stderr, ERR_STR "Unable to accept connection: %s" CLR_STR "\n", strerror(errno)); fprintf(stderr, ERR_STR "Unable to accept connection: %s" CLR_STR "\n", strerror(errno));

View File

@@ -1,12 +1,12 @@
/** /**
* Necronda Web Server * sesimos - secure, simple, modern web server
* Main executable (header file) * Main executable (header file)
* src/necronda-server.h * src/server.h
* Lorenz Stechauner, 2020-12-03 * Lorenz Stechauner, 2020-12-03
*/ */
#ifndef NECRONDA_SERVER_SERVER_H #ifndef SESIMOS_SERVER_H
#define NECRONDA_SERVER_SERVER_H #define SESIMOS_SERVER_H
#include <sys/time.h> #include <sys/time.h>
#include <maxminddb.h> #include <maxminddb.h>
@@ -20,7 +20,6 @@
#define SERVER_TIMEOUT_INIT 4 #define SERVER_TIMEOUT_INIT 4
#define SERVER_TIMEOUT 3600 #define SERVER_TIMEOUT 3600
#define CHUNK_SIZE 8192
extern int sockets[NUM_SOCKETS]; extern int sockets[NUM_SOCKETS];
extern pid_t children[MAX_CHILDREN]; extern pid_t children[MAX_CHILDREN];
@@ -31,4 +30,4 @@ extern char *log_client_prefix, *log_conn_prefix, *log_req_prefix, *client_geoip
extern char *client_addr_str, *client_addr_str_ptr, *server_addr_str, *server_addr_str_ptr, *client_host_str; extern char *client_addr_str, *client_addr_str_ptr, *server_addr_str, *server_addr_str_ptr, *client_host_str;
extern struct timeval client_timeout; extern struct timeval client_timeout;
#endif //NECRONDA_SERVER_SERVER_H #endif //SESIMOS_SERVER_H