Compare commits
3 Commits
f0b27b3b37
...
c92742275a
Author | SHA1 | Date | |
---|---|---|---|
c92742275a
|
|||
041e4d43a7
|
|||
170337d4d5
|
@@ -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)
|
||||||
|
84
src/client.c
84
src/client.c
@@ -5,8 +5,8 @@
|
|||||||
* Lorenz Stechauner, 2020-12-03
|
* Lorenz Stechauner, 2020-12-03
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "client.h"
|
|
||||||
#include "necronda.h"
|
#include "necronda.h"
|
||||||
|
#include "client.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;
|
||||||
@@ -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) {
|
||||||
|
@@ -19,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;
|
||||||
|
@@ -10,6 +10,7 @@
|
|||||||
#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;
|
||||||
|
@@ -15,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];
|
||||||
|
|
||||||
|
@@ -15,6 +15,7 @@
|
|||||||
#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);
|
||||||
|
@@ -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:
|
||||||
|
@@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
#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;
|
||||||
|
@@ -108,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[];
|
||||||
|
@@ -8,6 +8,7 @@
|
|||||||
#include "../necronda.h"
|
#include "../necronda.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"},
|
||||||
|
@@ -5,10 +5,11 @@
|
|||||||
* Lorenz Stechauner, 2021-01-07
|
* Lorenz Stechauner, 2021-01-07
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "../necronda.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>
|
||||||
@@ -17,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};
|
||||||
@@ -32,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);
|
||||||
@@ -183,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) {
|
||||||
@@ -289,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);
|
||||||
@@ -453,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;
|
||||||
|
@@ -12,6 +12,8 @@
|
|||||||
#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;
|
||||||
@@ -117,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);
|
||||||
|
}
|
||||||
|
@@ -9,6 +9,7 @@
|
|||||||
#define NECRONDA_SERVER_SOCK_H
|
#define NECRONDA_SERVER_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);
|
||||||
|
|
||||||
|
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 //NECRONDA_SERVER_SOCK_H
|
#endif //NECRONDA_SERVER_SOCK_H
|
||||||
|
@@ -11,6 +11,7 @@
|
|||||||
#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;
|
||||||
|
@@ -11,6 +11,7 @@
|
|||||||
#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) {
|
||||||
@@ -191,6 +192,7 @@ int base64_encode(void *data, unsigned long data_len, char *output, unsigned lon
|
|||||||
|
|
||||||
for (int i = 0; i < base64_mod_table[data_len % 3]; i++)
|
for (int i = 0; i < base64_mod_table[data_len % 3]; i++)
|
||||||
output[out_len - 1 - i] = '=';
|
output[out_len - 1 - i] = '=';
|
||||||
|
output[out_len] = 0;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
202
src/lib/websocket.c
Normal file
202
src/lib/websocket.c
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
/**
|
||||||
|
* Necronda Web Server
|
||||||
|
* WebSocket reverse proxy
|
||||||
|
* src/lib/websocket.c
|
||||||
|
* Lorenz Stechauner, 2022-08-16
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "../necronda.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
36
src/lib/websocket.h
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
/**
|
||||||
|
* Necronda Web Server
|
||||||
|
* WebSocket reverse proxy (header file)
|
||||||
|
* src/lib/websocket.h
|
||||||
|
* Lorenz Stechauner, 2022-08-16
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NECRONDA_SERVER_WEBSOCKET_H
|
||||||
|
#define NECRONDA_SERVER_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 // NECRONDA_SERVER_WEBSOCKET_H
|
@@ -12,6 +12,8 @@
|
|||||||
#define SERVER_STR "Necronda/" NECRONDA_VERSION
|
#define SERVER_STR "Necronda/" NECRONDA_VERSION
|
||||||
#define SERVER_STR_HTML "Necronda web server " NECRONDA_VERSION
|
#define SERVER_STR_HTML "Necronda web server " NECRONDA_VERSION
|
||||||
|
|
||||||
|
#define CHUNK_SIZE 8192
|
||||||
|
|
||||||
#ifndef DEFAULT_HOST
|
#ifndef DEFAULT_HOST
|
||||||
# define DEFAULT_HOST "www.necronda.net"
|
# define DEFAULT_HOST "www.necronda.net"
|
||||||
#endif
|
#endif
|
||||||
|
24
src/server.c
24
src/server.c
@@ -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)}
|
||||||
@@ -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));
|
||||||
|
@@ -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];
|
||||||
|
Reference in New Issue
Block a user