Compare commits
23 Commits
10d405e745
...
v4.4
Author | SHA1 | Date | |
---|---|---|---|
43c512dc5a
|
|||
3f5eee236d
|
|||
0b157bcb74
|
|||
30b163c6fa
|
|||
cf2c0de697
|
|||
7aa47cac61
|
|||
456deeae20
|
|||
b08481818c
|
|||
49ad349775
|
|||
2b823cabd6
|
|||
ecd4f16afe
|
|||
45c5f20345
|
|||
c42f27e961
|
|||
de8ab406f6
|
|||
22d50ed4bd
|
|||
de44f4a3fe
|
|||
cc29250d76
|
|||
c2f8f4c962
|
|||
ff708230bd
|
|||
52ea541833
|
|||
f4bd426f3c
|
|||
ffbbcc6490
|
|||
80986325ce
|
8
Makefile
8
Makefile
@ -1,9 +1,9 @@
|
|||||||
|
|
||||||
CFLAGS=-std=c11 -Wall
|
CFLAGS=-std=c11 -Wall
|
||||||
INCLUDE=-lssl -lcrypto -lmagic -lz -lmaxminddb
|
INCLUDE=-lssl -lcrypto -lmagic -lz -lmaxminddb -lbrotlienc
|
||||||
LIBS=src/lib/*.c
|
LIBS=src/lib/*.c
|
||||||
|
|
||||||
DEBIAN_OPTS=-D MAGIC_FILE="\"/usr/share/file/magic.mgc\"" -D PHP_FPM_SOCKET="\"/var/run/php/php7.3-fpm.sock\""
|
DEBIAN_OPTS=-D CACHE_MAGIC_FILE="\"/usr/share/file/magic.mgc\"" -D PHP_FPM_SOCKET="\"/var/run/php/php7.3-fpm.sock\""
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
@echo "Installing packages..."
|
@echo "Installing packages..."
|
||||||
@ -22,7 +22,7 @@ compile:
|
|||||||
compile-debian:
|
compile-debian:
|
||||||
@mkdir -p bin
|
@mkdir -p bin
|
||||||
gcc $(LIBS) -o bin/libnecronda-server.so --shared -fPIC $(CFLAGS) $(INCLUDE) \
|
gcc $(LIBS) -o bin/libnecronda-server.so --shared -fPIC $(CFLAGS) $(INCLUDE) \
|
||||||
$(DEBIAN_OPTS)
|
$(DEBIAN_OPTS) -O3
|
||||||
gcc src/necronda-server.c -o bin/necronda-server $(CFLAGS) $(INCLUDE) \
|
gcc src/necronda-server.c -o bin/necronda-server $(CFLAGS) $(INCLUDE) \
|
||||||
-Lbin -lnecronda-server -Wl,-rpath=$(shell pwd)/bin \
|
-Lbin -lnecronda-server -Wl,-rpath=$(shell pwd)/bin \
|
||||||
$(DEBIAN_OPTS)
|
$(DEBIAN_OPTS) -O3
|
||||||
|
@ -6,11 +6,11 @@ Necronda web server
|
|||||||
|
|
||||||
* Full IPv4 and IPv6 support
|
* Full IPv4 and IPv6 support
|
||||||
* Serving local files via HTTP and HTTPS
|
* Serving local files via HTTP and HTTPS
|
||||||
* File compression and disk cache for compressed files
|
* File compression ([gzip](https://www.gzip.org/), [Brotli](https://www.brotli.org/)) and disk cache for compressed files
|
||||||
* Reverse proxy for other HTTP and HTTPS servers
|
* Reverse proxy for other HTTP and HTTPS servers
|
||||||
* FastCGI support (e.g. [PHP-FPM](https://php-fpm.org/))
|
* FastCGI support (e.g. [PHP-FPM](https://php-fpm.org/))
|
||||||
* 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)
|
||||||
* DNS reverse lookup for connecting hosts
|
* Optional DNS reverse lookup for connecting hosts
|
||||||
* Automatic URL rewrite (e.g. `/index.html` -> `/`, `/test.php` -> `/test`)
|
* Automatic URL rewrite (e.g. `/index.html` -> `/`, `/test.php` -> `/test`)
|
||||||
* Modern looking and responsive error documents
|
* Modern looking and responsive error documents
|
||||||
|
|
||||||
|
126
src/client.c
126
src/client.c
@ -5,7 +5,6 @@
|
|||||||
* Lorenz Stechauner, 2020-12-03
|
* Lorenz Stechauner, 2020-12-03
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "client.h"
|
|
||||||
#include "lib/utils.h"
|
#include "lib/utils.h"
|
||||||
#include "lib/config.h"
|
#include "lib/config.h"
|
||||||
#include "lib/sock.h"
|
#include "lib/sock.h"
|
||||||
@ -13,6 +12,8 @@
|
|||||||
#include "lib/rev_proxy.h"
|
#include "lib/rev_proxy.h"
|
||||||
#include "lib/fastcgi.h"
|
#include "lib/fastcgi.h"
|
||||||
#include "lib/cache.h"
|
#include "lib/cache.h"
|
||||||
|
#include "lib/geoip.h"
|
||||||
|
#include "lib/compress.h"
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/select.h>
|
#include <sys/select.h>
|
||||||
@ -32,7 +33,7 @@ char *client_addr_str, *client_addr_str_ptr, *server_addr_str, *server_addr_str_
|
|||||||
struct timeval client_timeout;
|
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 < MAX_HOST_CONFIG; i++) {
|
for (int i = 0; i < CONFIG_MAX_HOST_CONFIG; i++) {
|
||||||
host_config *hc = &config[i];
|
host_config *hc = &config[i];
|
||||||
if (hc->type == CONFIG_TYPE_UNSET) break;
|
if (hc->type == CONFIG_TYPE_UNSET) break;
|
||||||
if (strcmp(hc->name, host) == 0) return hc;
|
if (strcmp(hc->name, host) == 0) return hc;
|
||||||
@ -101,15 +102,15 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int
|
|||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto abort;
|
goto abort;
|
||||||
} else if (ret == 1) {
|
} else if (ret == 1) {
|
||||||
sprintf(err_msg, "Unable to parse header: Invalid header format.");
|
sprintf(err_msg, "Unable to parse http header: Invalid header format.");
|
||||||
} else if (ret == 2) {
|
} else if (ret == 2) {
|
||||||
sprintf(err_msg, "Unable to parse header: Invalid method.");
|
sprintf(err_msg, "Unable to parse http header: Invalid method.");
|
||||||
} else if (ret == 3) {
|
} else if (ret == 3) {
|
||||||
sprintf(err_msg, "Unable to parse header: Invalid version.");
|
sprintf(err_msg, "Unable to parse http header: Invalid version.");
|
||||||
} else if (ret == 4) {
|
} else if (ret == 4) {
|
||||||
sprintf(err_msg, "Unable to parse header: Header contains illegal characters.");
|
sprintf(err_msg, "Unable to parse http header: Header contains illegal characters.");
|
||||||
} else if (ret == 5) {
|
} else if (ret == 5) {
|
||||||
sprintf(err_msg, "Unable to parse header: End of header not found.");
|
sprintf(err_msg, "Unable to parse http header: End of header not found.");
|
||||||
}
|
}
|
||||||
res.status = http_get_status(400);
|
res.status = http_get_status(400);
|
||||||
goto respond;
|
goto respond;
|
||||||
@ -137,7 +138,7 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int
|
|||||||
strcpy(host, host_ptr);
|
strcpy(host, host_ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
sprintf(log_req_prefix, "[%s%24s%s]%s ", BLD_STR, host, CLR_STR, log_client_prefix);
|
sprintf(log_req_prefix, "[%6i][%s%24s%s]%s ", getpid(), BLD_STR, host, CLR_STR, log_client_prefix);
|
||||||
log_prefix = log_req_prefix;
|
log_prefix = log_req_prefix;
|
||||||
print(BLD_STR "%s %s" CLR_STR, req.method, req.uri);
|
print(BLD_STR "%s %s" CLR_STR, req.method, req.uri);
|
||||||
|
|
||||||
@ -218,6 +219,10 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int
|
|||||||
goto respond;
|
goto respond;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (strncmp(uri.req_path, "/.well-known/", 13) == 0) {
|
||||||
|
http_add_header_field(&res.hdr, "Access-Control-Allow-Origin", "*");
|
||||||
|
}
|
||||||
|
|
||||||
if (strncmp(uri.req_path, "/.well-known/", 13) != 0 && strstr(uri.path, "/.") != NULL) {
|
if (strncmp(uri.req_path, "/.well-known/", 13) != 0 && strstr(uri.path, "/.") != NULL) {
|
||||||
res.status = http_get_status(403);
|
res.status = http_get_status(403);
|
||||||
sprintf(err_msg, "Parts of this URI are hidden.");
|
sprintf(err_msg, "Parts of this URI are hidden.");
|
||||||
@ -263,9 +268,43 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int
|
|||||||
http_add_header_field(&res.hdr, "Last-Modified", last_modified);
|
http_add_header_field(&res.hdr, "Last-Modified", last_modified);
|
||||||
sprintf(buf1, "%s; charset=%s", uri.meta->type, uri.meta->charset);
|
sprintf(buf1, "%s; charset=%s", uri.meta->type, uri.meta->charset);
|
||||||
http_add_header_field(&res.hdr, "Content-Type", buf1);
|
http_add_header_field(&res.hdr, "Content-Type", buf1);
|
||||||
|
|
||||||
|
|
||||||
|
char *accept_encoding = http_get_header_field(&req.hdr, "Accept-Encoding");
|
||||||
|
int enc = 0;
|
||||||
|
if (accept_encoding != NULL) {
|
||||||
|
if (uri.meta->filename_comp_br[0] != 0 && strstr(accept_encoding, "br") != NULL) {
|
||||||
|
file = fopen(uri.meta->filename_comp_br, "rb");
|
||||||
|
if (file == NULL) {
|
||||||
|
cache_filename_comp_invalid(uri.filename);
|
||||||
|
} else {
|
||||||
|
http_add_header_field(&res.hdr, "Content-Encoding", "br");
|
||||||
|
enc = COMPRESS_BR;
|
||||||
|
}
|
||||||
|
} else if (uri.meta->filename_comp_gz[0] != 0 && strstr(accept_encoding, "gzip") != NULL) {
|
||||||
|
file = fopen(uri.meta->filename_comp_gz, "rb");
|
||||||
|
if (file == NULL) {
|
||||||
|
cache_filename_comp_invalid(uri.filename);
|
||||||
|
} else {
|
||||||
|
http_add_header_field(&res.hdr, "Content-Encoding", "gzip");
|
||||||
|
enc = COMPRESS_GZ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (enc != 0) {
|
||||||
|
http_add_header_field(&res.hdr, "Vary", "Accept-Encoding");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (uri.meta->etag[0] != 0) {
|
if (uri.meta->etag[0] != 0) {
|
||||||
|
if (enc) {
|
||||||
|
sprintf(buf0, "%s-%s", uri.meta->etag,
|
||||||
|
(enc & COMPRESS_BR) ? "br" : (enc & COMPRESS_GZ) ? "gzip" : "");
|
||||||
|
http_add_header_field(&res.hdr, "ETag", buf0);
|
||||||
|
} else {
|
||||||
http_add_header_field(&res.hdr, "ETag", uri.meta->etag);
|
http_add_header_field(&res.hdr, "ETag", uri.meta->etag);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (strncmp(uri.meta->type, "text/", 5) == 0) {
|
if (strncmp(uri.meta->type, "text/", 5) == 0) {
|
||||||
http_add_header_field(&res.hdr, "Cache-Control", "public, max-age=3600");
|
http_add_header_field(&res.hdr, "Cache-Control", "public, max-age=3600");
|
||||||
} else {
|
} else {
|
||||||
@ -325,19 +364,10 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int
|
|||||||
goto respond;
|
goto respond;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *accept_encoding = http_get_header_field(&req.hdr, "Accept-Encoding");
|
|
||||||
if (uri.meta->filename_comp[0] != 0 && accept_encoding != NULL &&
|
|
||||||
strstr(accept_encoding, "deflate") != NULL) {
|
|
||||||
file = fopen(uri.meta->filename_comp, "rb");
|
|
||||||
if (file == NULL) {
|
if (file == NULL) {
|
||||||
cache_filename_comp_invalid(uri.filename);
|
|
||||||
goto not_compressed;
|
|
||||||
}
|
|
||||||
http_add_header_field(&res.hdr, "Content-Encoding", "deflate");
|
|
||||||
} else {
|
|
||||||
not_compressed:
|
|
||||||
file = fopen(uri.filename, "rb");
|
file = fopen(uri.filename, "rb");
|
||||||
}
|
}
|
||||||
|
|
||||||
fseek(file, 0, SEEK_END);
|
fseek(file, 0, SEEK_END);
|
||||||
content_length = ftell(file);
|
content_length = ftell(file);
|
||||||
fseek(file, 0, SEEK_SET);
|
fseek(file, 0, SEEK_SET);
|
||||||
@ -397,13 +427,17 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int
|
|||||||
content_length = -1;
|
content_length = -1;
|
||||||
use_fastcgi = 1;
|
use_fastcgi = 1;
|
||||||
|
|
||||||
char *accept_encoding = http_get_header_field(&req.hdr, "Accept-Encoding");
|
int http_comp = http_get_compression(&req, &res);
|
||||||
char *content_type = http_get_header_field(&res.hdr, "Content-Type");
|
if (http_comp & COMPRESS) {
|
||||||
char *content_encoding = http_get_header_field(&res.hdr, "Content-Encoding");
|
if (http_comp & COMPRESS_BR) {
|
||||||
if (mime_is_compressible(content_type) && content_encoding == NULL &&
|
use_fastcgi |= FASTCGI_COMPRESS_BR;
|
||||||
accept_encoding != NULL && strstr(accept_encoding, "deflate") != NULL) {
|
sprintf(buf0, "br");
|
||||||
http_add_header_field(&res.hdr, "Content-Encoding", "deflate");
|
} else if (http_comp & COMPRESS_GZ) {
|
||||||
use_fastcgi = 2;
|
use_fastcgi |= FASTCGI_COMPRESS_GZ;
|
||||||
|
sprintf(buf0, "gzip");
|
||||||
|
}
|
||||||
|
http_add_header_field(&res.hdr, "Vary", "Accept-Encoding");
|
||||||
|
http_add_header_field(&res.hdr, "Content-Encoding", buf0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (http_get_header_field(&res.hdr, "Content-Length") == NULL) {
|
if (http_get_header_field(&res.hdr, "Content-Length") == NULL) {
|
||||||
@ -414,8 +448,33 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int
|
|||||||
print("Reverse proxy for " BLD_STR "%s:%i" CLR_STR, conf->rev_proxy.hostname, conf->rev_proxy.port);
|
print("Reverse proxy for " BLD_STR "%s:%i" CLR_STR, conf->rev_proxy.hostname, conf->rev_proxy.port);
|
||||||
http_remove_header_field(&res.hdr, "Date", HTTP_REMOVE_ALL);
|
http_remove_header_field(&res.hdr, "Date", HTTP_REMOVE_ALL);
|
||||||
http_remove_header_field(&res.hdr, "Server", HTTP_REMOVE_ALL);
|
http_remove_header_field(&res.hdr, "Server", HTTP_REMOVE_ALL);
|
||||||
|
|
||||||
ret = rev_proxy_init(&req, &res, conf, client, &custom_status, err_msg);
|
ret = rev_proxy_init(&req, &res, conf, client, &custom_status, err_msg);
|
||||||
use_rev_proxy = ret == 0;
|
use_rev_proxy = (ret == 0);
|
||||||
|
|
||||||
|
/*
|
||||||
|
char *content_encoding = http_get_header_field(&res.hdr, "Content-Encoding");
|
||||||
|
if (use_rev_proxy && content_encoding == NULL) {
|
||||||
|
int http_comp = http_get_compression(&req, &res);
|
||||||
|
if (http_comp & COMPRESS_BR) {
|
||||||
|
use_rev_proxy |= REV_PROXY_COMPRESS_BR;
|
||||||
|
} else if (http_comp & COMPRESS_GZ) {
|
||||||
|
use_rev_proxy |= REV_PROXY_COMPRESS_GZ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char *transfer_encoding = http_get_header_field(&res.hdr, "Transfer-Encoding");
|
||||||
|
int chunked = transfer_encoding != NULL && strcmp(transfer_encoding, "chunked") == 0;
|
||||||
|
http_remove_header_field(&res.hdr, "Transfer-Encoding", HTTP_REMOVE_ALL);
|
||||||
|
ret = sprintf(buf0, "%s%s%s",
|
||||||
|
(use_rev_proxy & REV_PROXY_COMPRESS_BR) ? "br" :
|
||||||
|
((use_rev_proxy & REV_PROXY_COMPRESS_GZ) ? "gzip" : ""),
|
||||||
|
((use_rev_proxy & REV_PROXY_COMPRESS) && chunked) ? ", " : "",
|
||||||
|
chunked ? "chunked" : "");
|
||||||
|
if (ret > 0) {
|
||||||
|
http_add_header_field(&res.hdr, "Transfer-Encoding", buf0);
|
||||||
|
}
|
||||||
|
*/
|
||||||
} else {
|
} else {
|
||||||
print(ERR_STR "Unknown host type: %i" CLR_STR, conf->type);
|
print(ERR_STR "Unknown host type: %i" CLR_STR, conf->type);
|
||||||
res.status = http_get_status(501);
|
res.status = http_get_status(501);
|
||||||
@ -501,17 +560,21 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int
|
|||||||
} else if (use_fastcgi) {
|
} else if (use_fastcgi) {
|
||||||
char *transfer_encoding = http_get_header_field(&res.hdr, "Transfer-Encoding");
|
char *transfer_encoding = http_get_header_field(&res.hdr, "Transfer-Encoding");
|
||||||
int chunked = transfer_encoding != NULL && strcmp(transfer_encoding, "chunked") == 0;
|
int chunked = transfer_encoding != NULL && strcmp(transfer_encoding, "chunked") == 0;
|
||||||
int flags = (chunked ? FASTCGI_CHUNKED : 0) | ((use_fastcgi == 2) ? FASTCGI_COMPRESS : 0);
|
|
||||||
|
int flags = (chunked ? FASTCGI_CHUNKED : 0) | (use_fastcgi & FASTCGI_COMPRESS);
|
||||||
fastcgi_send(&php_fpm, client, flags);
|
fastcgi_send(&php_fpm, client, flags);
|
||||||
} else if (use_rev_proxy) {
|
} else if (use_rev_proxy) {
|
||||||
char *transfer_encoding = http_get_header_field(&res.hdr, "Transfer-Encoding");
|
char *transfer_encoding = http_get_header_field(&res.hdr, "Transfer-Encoding");
|
||||||
int chunked = transfer_encoding != NULL && strcmp(transfer_encoding, "chunked") == 0;
|
int chunked = transfer_encoding != NULL && strstr(transfer_encoding, "chunked") != NULL;
|
||||||
|
|
||||||
char *content_len = http_get_header_field(&res.hdr, "Content-Length");
|
char *content_len = http_get_header_field(&res.hdr, "Content-Length");
|
||||||
unsigned long len_to_send = 0;
|
unsigned long len_to_send = 0;
|
||||||
if (content_len != NULL) {
|
if (content_len != NULL) {
|
||||||
len_to_send = strtol(content_len, NULL, 10);
|
len_to_send = strtol(content_len, NULL, 10);
|
||||||
}
|
}
|
||||||
rev_proxy_send(client, chunked, len_to_send);
|
|
||||||
|
int flags = (chunked ? REV_PROXY_CHUNKED : 0) | (use_rev_proxy & REV_PROXY_COMPRESS);
|
||||||
|
rev_proxy_send(client, len_to_send, flags);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -648,6 +711,7 @@ int client_connection_handler(sock *client, unsigned long client_num) {
|
|||||||
client->_ssl_error = ERR_get_error();
|
client->_ssl_error = ERR_get_error();
|
||||||
if (ret <= 0) {
|
if (ret <= 0) {
|
||||||
print(ERR_STR "Unable to perform handshake: %s" CLR_STR, sock_strerror(client));
|
print(ERR_STR "Unable to perform handshake: %s" CLR_STR, sock_strerror(client));
|
||||||
|
ret = -1;
|
||||||
goto close;
|
goto close;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -710,12 +774,13 @@ int client_handler(sock *client, unsigned long client_num, struct sockaddr_in6 *
|
|||||||
ntohs(client_addr->sin6_port), CLR_STR);
|
ntohs(client_addr->sin6_port), CLR_STR);
|
||||||
|
|
||||||
log_conn_prefix = malloc(256);
|
log_conn_prefix = malloc(256);
|
||||||
sprintf(log_conn_prefix, "[%24s]%s ", server_addr_str, log_client_prefix);
|
sprintf(log_conn_prefix, "[%6i][%24s]%s ", getpid(), server_addr_str, log_client_prefix);
|
||||||
log_prefix = log_conn_prefix;
|
log_prefix = log_conn_prefix;
|
||||||
|
|
||||||
print("Started child process with PID %i", getpid());
|
print("Started child process with PID %i", getpid());
|
||||||
|
|
||||||
ret = client_connection_handler(client, client_num);
|
ret = client_connection_handler(client, client_num);
|
||||||
|
|
||||||
free(client_addr_str_ptr);
|
free(client_addr_str_ptr);
|
||||||
client_addr_str_ptr = NULL;
|
client_addr_str_ptr = NULL;
|
||||||
free(server_addr_str_ptr);
|
free(server_addr_str_ptr);
|
||||||
@ -730,5 +795,6 @@ int client_handler(sock *client, unsigned long client_num, struct sockaddr_in6 *
|
|||||||
log_req_prefix = NULL;
|
log_req_prefix = NULL;
|
||||||
free(log_client_prefix);
|
free(log_client_prefix);
|
||||||
log_client_prefix = NULL;
|
log_client_prefix = NULL;
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
18
src/client.h
18
src/client.h
@ -1,18 +0,0 @@
|
|||||||
/**
|
|
||||||
* Necronda Web Server
|
|
||||||
* Client connection and request handlers (header file)
|
|
||||||
* src/client.h
|
|
||||||
* Lorenz Stechauner, 2021-01-17
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef NECRONDA_SERVER_CLIENT_H
|
|
||||||
#define NECRONDA_SERVER_CLIENT_H
|
|
||||||
|
|
||||||
#include <sys/time.h>
|
|
||||||
|
|
||||||
extern int server_keep_alive;
|
|
||||||
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 struct timeval client_timeout;
|
|
||||||
|
|
||||||
#endif //NECRONDA_SERVER_CLIENT_H
|
|
129
src/lib/cache.c
129
src/lib/cache.c
@ -7,9 +7,8 @@
|
|||||||
|
|
||||||
#include "cache.h"
|
#include "cache.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "../necronda-server.h"
|
#include "compress.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <zlib.h>
|
|
||||||
#include <magic.h>
|
#include <magic.h>
|
||||||
#include <sys/ipc.h>
|
#include <sys/ipc.h>
|
||||||
#include <sys/shm.h>
|
#include <sys/shm.h>
|
||||||
@ -17,6 +16,7 @@
|
|||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <openssl/sha.h>
|
#include <openssl/sha.h>
|
||||||
|
#include <malloc.h>
|
||||||
|
|
||||||
int cache_continue = 1;
|
int cache_continue = 1;
|
||||||
magic_t magic;
|
magic_t magic;
|
||||||
@ -28,7 +28,7 @@ int magic_init() {
|
|||||||
fprintf(stderr, ERR_STR "Unable to open magic cookie: %s" CLR_STR "\n", strerror(errno));
|
fprintf(stderr, ERR_STR "Unable to open magic cookie: %s" CLR_STR "\n", strerror(errno));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (magic_load(magic, MAGIC_FILE) != 0) {
|
if (magic_load(magic, CACHE_MAGIC_FILE) != 0) {
|
||||||
fprintf(stderr, ERR_STR "Unable to load magic cookie: %s" CLR_STR "\n", magic_error(magic));
|
fprintf(stderr, ERR_STR "Unable to load magic cookie: %s" CLR_STR "\n", magic_error(magic));
|
||||||
return -2;
|
return -2;
|
||||||
}
|
}
|
||||||
@ -43,7 +43,7 @@ int cache_process() {
|
|||||||
signal(SIGINT, cache_process_term);
|
signal(SIGINT, cache_process_term);
|
||||||
signal(SIGTERM, cache_process_term);
|
signal(SIGTERM, cache_process_term);
|
||||||
|
|
||||||
int shm_id = shmget(SHM_KEY_CACHE, FILE_CACHE_SIZE * sizeof(cache_entry), 0);
|
int shm_id = shmget(CACHE_SHM_KEY, CACHE_ENTRIES * sizeof(cache_entry), 0);
|
||||||
if (shm_id < 0) {
|
if (shm_id < 0) {
|
||||||
fprintf(stderr, ERR_STR "Unable to create shared memory: %s" CLR_STR "\n", strerror(errno));
|
fprintf(stderr, ERR_STR "Unable to create shared memory: %s" CLR_STR "\n", strerror(errno));
|
||||||
return -1;
|
return -1;
|
||||||
@ -66,26 +66,28 @@ int cache_process() {
|
|||||||
|
|
||||||
FILE *cache_file = fopen("/var/necronda-server/cache", "rb");
|
FILE *cache_file = fopen("/var/necronda-server/cache", "rb");
|
||||||
if (cache_file != NULL) {
|
if (cache_file != NULL) {
|
||||||
fread(cache, sizeof(cache_entry), FILE_CACHE_SIZE, cache_file);
|
fread(cache, sizeof(cache_entry), CACHE_ENTRIES, cache_file);
|
||||||
fclose(cache_file);
|
fclose(cache_file);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < FILE_CACHE_SIZE; i++) {
|
for (int i = 0; i < CACHE_ENTRIES; i++) {
|
||||||
cache[i].is_updating = 0;
|
cache[i].is_updating = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
FILE *file;
|
FILE *file;
|
||||||
char buf[16384];
|
char *buf = malloc(CACHE_BUF_SIZE);
|
||||||
char comp_buf[16384];
|
char *comp_buf = malloc(CACHE_BUF_SIZE);
|
||||||
char filename_comp[256];
|
char filename_comp_gz[256];
|
||||||
|
char filename_comp_br[256];
|
||||||
unsigned long read;
|
unsigned long read;
|
||||||
int compress;
|
int compress;
|
||||||
SHA_CTX ctx;
|
SHA_CTX ctx;
|
||||||
unsigned char hash[SHA_DIGEST_LENGTH];
|
unsigned char hash[SHA_DIGEST_LENGTH];
|
||||||
int cache_changed = 0;
|
int cache_changed = 0;
|
||||||
int p_len;
|
int p_len_gz, p_len_br;
|
||||||
|
int ret;
|
||||||
while (cache_continue) {
|
while (cache_continue) {
|
||||||
for (int i = 0; i < FILE_CACHE_SIZE; i++) {
|
for (int i = 0; i < CACHE_ENTRIES; i++) {
|
||||||
if (cache[i].filename[0] != 0 && cache[i].meta.etag[0] == 0 && !cache[i].is_updating) {
|
if (cache[i].filename[0] != 0 && cache[i].meta.etag[0] == 0 && !cache[i].is_updating) {
|
||||||
cache[i].is_updating = 1;
|
cache[i].is_updating = 1;
|
||||||
fprintf(stdout, "[cache] Hashing file %s\n", cache[i].filename);
|
fprintf(stdout, "[cache] Hashing file %s\n", cache[i].filename);
|
||||||
@ -93,9 +95,9 @@ int cache_process() {
|
|||||||
file = fopen(cache[i].filename, "rb");
|
file = fopen(cache[i].filename, "rb");
|
||||||
compress = mime_is_compressible(cache[i].meta.type);
|
compress = mime_is_compressible(cache[i].meta.type);
|
||||||
|
|
||||||
int level = NECRONDA_ZLIB_LEVEL;
|
compress_ctx comp_ctx;
|
||||||
z_stream strm;
|
FILE *comp_file_gz = NULL;
|
||||||
FILE *comp_file = 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/.necronda-server", cache[i].webroot_len, cache[i].filename);
|
||||||
mkdir(buf, 0755);
|
mkdir(buf, 0755);
|
||||||
@ -110,53 +112,70 @@ int cache_process() {
|
|||||||
buf[j] = ch;
|
buf[j] = ch;
|
||||||
}
|
}
|
||||||
buf[strlen(rel_path)] = 0;
|
buf[strlen(rel_path)] = 0;
|
||||||
p_len = snprintf(filename_comp, sizeof(filename_comp), "%.*s/.necronda-server/cache/%s.z",
|
|
||||||
|
p_len_gz = snprintf(filename_comp_gz, sizeof(filename_comp_gz),
|
||||||
|
"%.*s/.necronda-server/cache/%s.gz",
|
||||||
cache[i].webroot_len, cache[i].filename, buf);
|
cache[i].webroot_len, cache[i].filename, buf);
|
||||||
if (p_len < 0 || p_len >= sizeof(filename_comp)) {
|
p_len_br = snprintf(filename_comp_br, sizeof(filename_comp_br),
|
||||||
|
"%.*s/.necronda-server/cache/%s.br",
|
||||||
|
cache[i].webroot_len, cache[i].filename, buf);
|
||||||
|
if (p_len_gz < 0 || p_len_gz >= sizeof(filename_comp_gz) ||
|
||||||
|
p_len_br < 0 || p_len_br >= sizeof(filename_comp_br)) {
|
||||||
fprintf(stderr, ERR_STR "Unable to open cached file: "
|
fprintf(stderr, ERR_STR "Unable to open cached file: "
|
||||||
"File name for compressed file too long" CLR_STR "\n");
|
"File name for compressed file too long" CLR_STR "\n");
|
||||||
goto comp_err;
|
goto comp_err;
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf(stdout, "[cache] Compressing file %s\n", cache[i].filename);
|
fprintf(stdout, "[cache] Compressing file %s\n", cache[i].filename);
|
||||||
comp_file = fopen(filename_comp, "wb");
|
|
||||||
if (comp_file == NULL) {
|
comp_file_gz = fopen(filename_comp_gz, "wb");
|
||||||
|
comp_file_br = fopen(filename_comp_br, "wb");
|
||||||
|
if (comp_file_gz == NULL || comp_file_br == NULL) {
|
||||||
fprintf(stderr, ERR_STR "Unable to open cached file: %s" CLR_STR "\n", strerror(errno));
|
fprintf(stderr, ERR_STR "Unable to open cached file: %s" CLR_STR "\n", strerror(errno));
|
||||||
comp_err:
|
comp_err:
|
||||||
compress = 0;
|
compress = 0;
|
||||||
} else {
|
} else {
|
||||||
strm.zalloc = Z_NULL;
|
ret = compress_init(&comp_ctx, COMPRESS_GZ | COMPRESS_BR);
|
||||||
strm.zfree = Z_NULL;
|
if (ret != 0) {
|
||||||
strm.opaque = Z_NULL;
|
fprintf(stderr, ERR_STR "Unable to init compression: %s" CLR_STR "\n", strerror(errno));
|
||||||
if (deflateInit(&strm, level) != Z_OK) {
|
|
||||||
fprintf(stderr, ERR_STR "Unable to init deflate: %s" CLR_STR "\n", strerror(errno));
|
|
||||||
compress = 0;
|
compress = 0;
|
||||||
fclose(comp_file);
|
fclose(comp_file_gz);
|
||||||
|
fclose(comp_file_br);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
while ((read = fread(buf, 1, sizeof(buf), file)) > 0) {
|
while ((read = fread(buf, 1, CACHE_BUF_SIZE, file)) > 0) {
|
||||||
SHA1_Update(&ctx, buf, read);
|
SHA1_Update(&ctx, buf, read);
|
||||||
if (compress) {
|
if (compress) {
|
||||||
strm.avail_in = read;
|
unsigned long avail_in, avail_out;
|
||||||
strm.next_in = (unsigned char *) buf;
|
avail_in = read;
|
||||||
do {
|
do {
|
||||||
strm.avail_out = sizeof(comp_buf);
|
avail_out = CACHE_BUF_SIZE;
|
||||||
strm.next_out = (unsigned char *) comp_buf;
|
compress_compress_mode(&comp_ctx, COMPRESS_GZ,buf + read - avail_in, &avail_in,
|
||||||
deflate(&strm, feof(file) ? Z_FINISH : Z_NO_FLUSH);
|
comp_buf, &avail_out, feof(file));
|
||||||
fwrite(comp_buf, 1, sizeof(comp_buf) - strm.avail_out, comp_file);
|
fwrite(comp_buf, 1, CACHE_BUF_SIZE - avail_out, comp_file_gz);
|
||||||
strm.avail_in = 0;
|
} while (avail_in != 0 || avail_out != CACHE_BUF_SIZE);
|
||||||
} while (strm.avail_out == 0);
|
avail_in = read;
|
||||||
|
do {
|
||||||
|
avail_out = CACHE_BUF_SIZE;
|
||||||
|
compress_compress_mode(&comp_ctx, COMPRESS_BR, buf + read - avail_in, &avail_in,
|
||||||
|
comp_buf, &avail_out, feof(file));
|
||||||
|
fwrite(comp_buf, 1, CACHE_BUF_SIZE - avail_out, comp_file_br);
|
||||||
|
} while (avail_in != 0 || avail_out != CACHE_BUF_SIZE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (compress) {
|
if (compress) {
|
||||||
deflateEnd(&strm);
|
compress_free(&comp_ctx);
|
||||||
fclose(comp_file);
|
fclose(comp_file_gz);
|
||||||
|
fclose(comp_file_br);
|
||||||
fprintf(stdout, "[cache] Finished compressing file %s\n", cache[i].filename);
|
fprintf(stdout, "[cache] Finished compressing file %s\n", cache[i].filename);
|
||||||
strcpy(cache[i].meta.filename_comp, filename_comp);
|
strcpy(cache[i].meta.filename_comp_gz, filename_comp_gz);
|
||||||
|
strcpy(cache[i].meta.filename_comp_br, filename_comp_br);
|
||||||
} else {
|
} else {
|
||||||
memset(cache[i].meta.filename_comp, 0, sizeof(cache[i].meta.filename_comp));
|
memset(cache[i].meta.filename_comp_gz, 0, sizeof(cache[i].meta.filename_comp_gz));
|
||||||
|
memset(cache[i].meta.filename_comp_br, 0, sizeof(cache[i].meta.filename_comp_br));
|
||||||
}
|
}
|
||||||
SHA1_Final(hash, &ctx);
|
SHA1_Final(hash, &ctx);
|
||||||
memset(cache[i].meta.etag, 0, sizeof(cache[i].meta.etag));
|
memset(cache[i].meta.etag, 0, sizeof(cache[i].meta.etag));
|
||||||
@ -173,12 +192,20 @@ 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/necronda-server/cache", "wb");
|
||||||
fwrite(cache, sizeof(cache_entry), FILE_CACHE_SIZE, cache_file);
|
if (cache_file == NULL) {
|
||||||
|
fprintf(stderr, ERR_STR "Unable to open cache file: %s" CLR_STR "\n", strerror(errno));
|
||||||
|
free(buf);
|
||||||
|
free(comp_buf);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
fwrite(cache, sizeof(cache_entry), CACHE_ENTRIES, cache_file);
|
||||||
fclose(cache_file);
|
fclose(cache_file);
|
||||||
} else {
|
} else {
|
||||||
sleep(1);
|
sleep(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
free(buf);
|
||||||
|
free(comp_buf);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,7 +214,7 @@ int cache_init() {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int shm_id = shmget(SHM_KEY_CACHE, FILE_CACHE_SIZE * sizeof(cache_entry), IPC_CREAT | IPC_EXCL | 0600);
|
int shm_id = shmget(CACHE_SHM_KEY, CACHE_ENTRIES * sizeof(cache_entry), IPC_CREAT | IPC_EXCL | 0600);
|
||||||
if (shm_id < 0) {
|
if (shm_id < 0) {
|
||||||
fprintf(stderr, ERR_STR "Unable to create shared memory: %s" CLR_STR "\n", strerror(errno));
|
fprintf(stderr, ERR_STR "Unable to create shared memory: %s" CLR_STR "\n", strerror(errno));
|
||||||
return -2;
|
return -2;
|
||||||
@ -206,7 +233,7 @@ int cache_init() {
|
|||||||
return -4;
|
return -4;
|
||||||
}
|
}
|
||||||
cache = shm_rw;
|
cache = shm_rw;
|
||||||
memset(cache, 0, FILE_CACHE_SIZE * sizeof(cache_entry));
|
memset(cache, 0, CACHE_ENTRIES * sizeof(cache_entry));
|
||||||
shmdt(shm_rw);
|
shmdt(shm_rw);
|
||||||
cache = shm;
|
cache = shm;
|
||||||
|
|
||||||
@ -229,7 +256,7 @@ int cache_init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int cache_unload() {
|
int cache_unload() {
|
||||||
int shm_id = shmget(SHM_KEY_CACHE, 0, 0);
|
int shm_id = shmget(CACHE_SHM_KEY, 0, 0);
|
||||||
if (shm_id < 0) {
|
if (shm_id < 0) {
|
||||||
fprintf(stderr, ERR_STR "Unable to get shared memory id: %s" CLR_STR "\n", strerror(errno));
|
fprintf(stderr, ERR_STR "Unable to get shared memory id: %s" CLR_STR "\n", strerror(errno));
|
||||||
shmdt(cache);
|
shmdt(cache);
|
||||||
@ -245,7 +272,7 @@ int cache_unload() {
|
|||||||
|
|
||||||
int cache_update_entry(int entry_num, const char *filename, const char *webroot) {
|
int cache_update_entry(int entry_num, const char *filename, const char *webroot) {
|
||||||
void *cache_ro = cache;
|
void *cache_ro = cache;
|
||||||
int shm_id = shmget(SHM_KEY_CACHE, 0, 0);
|
int shm_id = shmget(CACHE_SHM_KEY, 0, 0);
|
||||||
void *shm_rw = shmat(shm_id, NULL, 0);
|
void *shm_rw = shmat(shm_id, NULL, 0);
|
||||||
if (shm_rw == (void *) -1) {
|
if (shm_rw == (void *) -1) {
|
||||||
print(ERR_STR "Unable to attach shared memory (rw): %s" CLR_STR, strerror(errno));
|
print(ERR_STR "Unable to attach shared memory (rw): %s" CLR_STR, strerror(errno));
|
||||||
@ -264,11 +291,11 @@ int cache_update_entry(int entry_num, const char *filename, const char *webroot)
|
|||||||
const char *type = magic_file(magic, filename);
|
const char *type = magic_file(magic, filename);
|
||||||
char type_new[24];
|
char type_new[24];
|
||||||
sprintf(type_new, "%s", type);
|
sprintf(type_new, "%s", type);
|
||||||
if (strcmp(type, "text/plain") == 0) {
|
if (strncmp(type, "text/", 5) == 0) {
|
||||||
if (strcmp(filename + strlen(filename) - 4, ".css") == 0) {
|
if (strcmp(filename + strlen(filename) - 4, ".css") == 0) {
|
||||||
sprintf(type_new, "text/css");
|
sprintf(type_new, "text/css");
|
||||||
} else if (strcmp(filename + strlen(filename) - 3, ".js") == 0) {
|
} else if (strcmp(filename + strlen(filename) - 3, ".js") == 0) {
|
||||||
sprintf(type_new, "text/javascript");
|
sprintf(type_new, "application/javascript");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
strcpy(cache[entry_num].meta.type, type_new);
|
strcpy(cache[entry_num].meta.type, type_new);
|
||||||
@ -277,7 +304,8 @@ int cache_update_entry(int entry_num, const char *filename, const char *webroot)
|
|||||||
strcpy(cache[entry_num].meta.charset, magic_file(magic, filename));
|
strcpy(cache[entry_num].meta.charset, magic_file(magic, filename));
|
||||||
|
|
||||||
memset(cache[entry_num].meta.etag, 0, sizeof(cache[entry_num].meta.etag));
|
memset(cache[entry_num].meta.etag, 0, sizeof(cache[entry_num].meta.etag));
|
||||||
memset(cache[entry_num].meta.filename_comp, 0, sizeof(cache[entry_num].meta.filename_comp));
|
memset(cache[entry_num].meta.filename_comp_gz, 0, sizeof(cache[entry_num].meta.filename_comp_gz));
|
||||||
|
memset(cache[entry_num].meta.filename_comp_br, 0, sizeof(cache[entry_num].meta.filename_comp_br));
|
||||||
cache[entry_num].is_updating = 0;
|
cache[entry_num].is_updating = 0;
|
||||||
|
|
||||||
shmdt(shm_rw);
|
shmdt(shm_rw);
|
||||||
@ -287,7 +315,7 @@ int cache_update_entry(int entry_num, const char *filename, const char *webroot)
|
|||||||
|
|
||||||
int cache_filename_comp_invalid(const char *filename) {
|
int cache_filename_comp_invalid(const char *filename) {
|
||||||
void *cache_ro = cache;
|
void *cache_ro = cache;
|
||||||
int shm_id = shmget(SHM_KEY_CACHE, 0, 0);
|
int shm_id = shmget(CACHE_SHM_KEY, 0, 0);
|
||||||
void *shm_rw = shmat(shm_id, NULL, 0);
|
void *shm_rw = shmat(shm_id, NULL, 0);
|
||||||
if (shm_rw == (void *) -1) {
|
if (shm_rw == (void *) -1) {
|
||||||
print(ERR_STR "Unable to attach shared memory (rw): %s" CLR_STR, strerror(errno));
|
print(ERR_STR "Unable to attach shared memory (rw): %s" CLR_STR, strerror(errno));
|
||||||
@ -296,7 +324,7 @@ int cache_filename_comp_invalid(const char *filename) {
|
|||||||
cache = shm_rw;
|
cache = shm_rw;
|
||||||
|
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < FILE_CACHE_SIZE; i++) {
|
for (i = 0; i < CACHE_ENTRIES; i++) {
|
||||||
if (cache[i].filename[0] != 0 && strlen(cache[i].filename) == strlen(filename) &&
|
if (cache[i].filename[0] != 0 && strlen(cache[i].filename) == strlen(filename) &&
|
||||||
strcmp(cache[i].filename, filename) == 0) {
|
strcmp(cache[i].filename, filename) == 0) {
|
||||||
if (cache[i].is_updating) {
|
if (cache[i].is_updating) {
|
||||||
@ -308,7 +336,8 @@ int cache_filename_comp_invalid(const char *filename) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
memset(cache[i].meta.etag, 0, sizeof(cache[i].meta.etag));
|
memset(cache[i].meta.etag, 0, sizeof(cache[i].meta.etag));
|
||||||
memset(cache[i].meta.filename_comp, 0, sizeof(cache[i].meta.filename_comp));
|
memset(cache[i].meta.filename_comp_gz, 0, sizeof(cache[i].meta.filename_comp_gz));
|
||||||
|
memset(cache[i].meta.filename_comp_br, 0, sizeof(cache[i].meta.filename_comp_br));
|
||||||
cache[i].is_updating = 0;
|
cache[i].is_updating = 0;
|
||||||
|
|
||||||
shmdt(shm_rw);
|
shmdt(shm_rw);
|
||||||
@ -322,7 +351,7 @@ int uri_cache_init(http_uri *uri) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < FILE_CACHE_SIZE; i++) {
|
for (i = 0; i < CACHE_ENTRIES; i++) {
|
||||||
if (cache[i].filename[0] != 0 && strlen(cache[i].filename) == strlen(uri->filename) &&
|
if (cache[i].filename[0] != 0 && strlen(cache[i].filename) == strlen(uri->filename) &&
|
||||||
strcmp(cache[i].filename, uri->filename) == 0) {
|
strcmp(cache[i].filename, uri->filename) == 0) {
|
||||||
uri->meta = &cache[i].meta;
|
uri->meta = &cache[i].meta;
|
||||||
@ -335,7 +364,7 @@ int uri_cache_init(http_uri *uri) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (uri->meta == NULL) {
|
if (uri->meta == NULL) {
|
||||||
for (i = 0; i < FILE_CACHE_SIZE; i++) {
|
for (i = 0; i < CACHE_ENTRIES; i++) {
|
||||||
if (cache[i].filename[0] == 0) {
|
if (cache[i].filename[0] == 0) {
|
||||||
if (cache_update_entry(i, uri->filename, uri->webroot) != 0) {
|
if (cache_update_entry(i, uri->filename, uri->webroot) != 0) {
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -10,6 +10,19 @@
|
|||||||
|
|
||||||
#include "uri.h"
|
#include "uri.h"
|
||||||
|
|
||||||
|
#define CACHE_SHM_KEY 255641
|
||||||
|
#define CACHE_ENTRIES 1024
|
||||||
|
#define CACHE_BUF_SIZE 16384
|
||||||
|
|
||||||
|
#ifndef CACHE_MAGIC_FILE
|
||||||
|
# define CACHE_MAGIC_FILE "/usr/share/file/misc/magic.mgc"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef DEFAULT_CONFIG_FILE
|
||||||
|
# define DEFAULT_CONFIG_FILE "/etc/necronda-server/necronda-server.conf"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char filename[256];
|
char filename[256];
|
||||||
unsigned char webroot_len;
|
unsigned char webroot_len;
|
||||||
|
79
src/lib/compress.c
Normal file
79
src/lib/compress.c
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
/**
|
||||||
|
* Necronda Web Server
|
||||||
|
* Compression interface
|
||||||
|
* src/lib/compress.c
|
||||||
|
* Lorenz Stechauner, 2021-05-05
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "compress.h"
|
||||||
|
#include <malloc.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
int compress_init(compress_ctx *ctx, int mode) {
|
||||||
|
ctx->gzip = NULL;
|
||||||
|
ctx->brotli = NULL;
|
||||||
|
ctx->mode = 0;
|
||||||
|
int ret;
|
||||||
|
if (mode & COMPRESS_GZ) {
|
||||||
|
ctx->mode |= COMPRESS_GZ;
|
||||||
|
ctx->gzip = malloc(sizeof(z_stream));
|
||||||
|
ctx->gzip->zalloc = Z_NULL;
|
||||||
|
ctx->gzip->zfree = Z_NULL;
|
||||||
|
ctx->gzip->opaque = Z_NULL;
|
||||||
|
ret = deflateInit2(ctx->gzip, COMPRESS_LEVEL_GZIP, Z_DEFLATED, 15 + 16, 9, Z_DEFAULT_STRATEGY);
|
||||||
|
if (ret != Z_OK) return -1;
|
||||||
|
}
|
||||||
|
if (mode & COMPRESS_BR) {
|
||||||
|
ctx->mode |= COMPRESS_BR;
|
||||||
|
ctx->brotli = BrotliEncoderCreateInstance(NULL, NULL, NULL);
|
||||||
|
if (ctx->brotli == NULL) return -1;
|
||||||
|
BrotliEncoderSetParameter(ctx->brotli, BROTLI_PARAM_MODE, BROTLI_MODE_GENERIC);
|
||||||
|
BrotliEncoderSetParameter(ctx->brotli, BROTLI_PARAM_QUALITY, COMPRESS_LEVEL_BROTLI);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int compress_compress(compress_ctx *ctx, const char *in, unsigned long *in_len, char *out, unsigned long *out_len, int finish) {
|
||||||
|
if ((ctx->mode & COMPRESS_GZ) && (ctx->mode & COMPRESS_BR)) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return compress_compress_mode(ctx, ctx->mode, in, in_len, out, out_len, finish);
|
||||||
|
}
|
||||||
|
|
||||||
|
int compress_compress_mode(compress_ctx *ctx, int mode, const char *in, unsigned long *in_len, char *out, unsigned long *out_len, int finish) {
|
||||||
|
if ((mode & COMPRESS_GZ) && (mode & COMPRESS_BR)) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
} else if (mode & COMPRESS_GZ) {
|
||||||
|
ctx->gzip->next_in = (unsigned char*) in;
|
||||||
|
ctx->gzip->avail_in = *in_len;
|
||||||
|
ctx->gzip->next_out = (unsigned char*) out;
|
||||||
|
ctx->gzip->avail_out = *out_len;
|
||||||
|
int ret = deflate(ctx->gzip, finish ? Z_FINISH : Z_NO_FLUSH);
|
||||||
|
*in_len = ctx->gzip->avail_in;
|
||||||
|
*out_len = ctx->gzip->avail_out;
|
||||||
|
return ret;
|
||||||
|
} else if (mode & COMPRESS_BR) {
|
||||||
|
int ret = BrotliEncoderCompressStream(ctx->brotli, finish ? BROTLI_OPERATION_FINISH : BROTLI_OPERATION_PROCESS,
|
||||||
|
in_len, (const unsigned char**) &in, out_len, (unsigned char **) &out, NULL);
|
||||||
|
return (ret == BROTLI_TRUE) ? 0 : -1;
|
||||||
|
} else {
|
||||||
|
errno = EINVAL;
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int compress_free(compress_ctx *ctx) {
|
||||||
|
if (ctx->gzip != NULL) {
|
||||||
|
deflateEnd(ctx->gzip);
|
||||||
|
free(ctx->gzip);
|
||||||
|
ctx->gzip = NULL;
|
||||||
|
}
|
||||||
|
if (ctx->brotli != NULL) {
|
||||||
|
BrotliEncoderDestroyInstance(ctx->brotli);
|
||||||
|
ctx->brotli = NULL;
|
||||||
|
}
|
||||||
|
ctx->mode = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
37
src/lib/compress.h
Normal file
37
src/lib/compress.h
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
/**
|
||||||
|
* Necronda Web Server
|
||||||
|
* Compression interface (header file)
|
||||||
|
* src/lib/compress.h
|
||||||
|
* Lorenz Stechauner, 2021-05-05
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NECRONDA_SERVER_COMPRESS_H
|
||||||
|
#define NECRONDA_SERVER_COMPRESS_H
|
||||||
|
|
||||||
|
#include <zlib.h>
|
||||||
|
#include <brotli/encode.h>
|
||||||
|
|
||||||
|
#define COMPRESS_LEVEL_GZIP 9
|
||||||
|
#define COMPRESS_LEVEL_BROTLI BROTLI_MAX_QUALITY
|
||||||
|
|
||||||
|
#define COMPRESS_GZ 1
|
||||||
|
#define COMPRESS_BR 2
|
||||||
|
#define COMPRESS 3
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int mode;
|
||||||
|
z_stream *gzip;
|
||||||
|
BrotliEncoderState *brotli;
|
||||||
|
} compress_ctx;
|
||||||
|
|
||||||
|
int compress_init(compress_ctx *ctx, int mode);
|
||||||
|
|
||||||
|
int compress_compress(compress_ctx *ctx, const char *in, unsigned long *in_len, char *out, unsigned long *out_len,
|
||||||
|
int finish);
|
||||||
|
|
||||||
|
int compress_compress_mode(compress_ctx *ctx, int mode, const char *in, unsigned long *in_len, char *out,
|
||||||
|
unsigned long *out_len, int finish);
|
||||||
|
|
||||||
|
int compress_free(compress_ctx *ctx);
|
||||||
|
|
||||||
|
#endif //NECRONDA_SERVER_COMPRESS_H
|
@ -6,20 +6,19 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "../necronda-server.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>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <malloc.h>
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
host_config *config;
|
host_config *config;
|
||||||
char cert_file[256], key_file[256], geoip_dir[256], dns_server[256];
|
char cert_file[256], key_file[256], geoip_dir[256], dns_server[256];
|
||||||
|
|
||||||
int config_init() {
|
int config_init() {
|
||||||
int shm_id = shmget(SHM_KEY_CONFIG, MAX_HOST_CONFIG * sizeof(host_config), IPC_CREAT | IPC_EXCL | 0640);
|
int shm_id = shmget(CONFIG_SHM_KEY, CONFIG_MAX_HOST_CONFIG * sizeof(host_config), IPC_CREAT | IPC_EXCL | 0640);
|
||||||
if (shm_id < 0) {
|
if (shm_id < 0) {
|
||||||
fprintf(stderr, ERR_STR "Unable to create shared memory: %s" CLR_STR "\n", strerror(errno));
|
fprintf(stderr, ERR_STR "Unable to create shared memory: %s" CLR_STR "\n", strerror(errno));
|
||||||
return -1;
|
return -1;
|
||||||
@ -38,14 +37,14 @@ int config_init() {
|
|||||||
return -3;
|
return -3;
|
||||||
}
|
}
|
||||||
config = shm_rw;
|
config = shm_rw;
|
||||||
memset(config, 0, MAX_HOST_CONFIG * sizeof(host_config));
|
memset(config, 0, CONFIG_MAX_HOST_CONFIG * sizeof(host_config));
|
||||||
shmdt(shm_rw);
|
shmdt(shm_rw);
|
||||||
config = shm;
|
config = shm;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int config_unload() {
|
int config_unload() {
|
||||||
int shm_id = shmget(SHM_KEY_CONFIG, 0, 0);
|
int shm_id = shmget(CONFIG_SHM_KEY, 0, 0);
|
||||||
if (shm_id < 0) {
|
if (shm_id < 0) {
|
||||||
fprintf(stderr, ERR_STR "Unable to get shared memory id: %s" CLR_STR "\n", strerror(errno));
|
fprintf(stderr, ERR_STR "Unable to get shared memory id: %s" CLR_STR "\n", strerror(errno));
|
||||||
shmdt(config);
|
shmdt(config);
|
||||||
@ -73,8 +72,8 @@ int config_load(const char *filename) {
|
|||||||
fread(conf, 1, len, file);
|
fread(conf, 1, len, file);
|
||||||
fclose(file);
|
fclose(file);
|
||||||
|
|
||||||
host_config *tmp_config = malloc(MAX_HOST_CONFIG * sizeof(host_config));
|
host_config *tmp_config = malloc(CONFIG_MAX_HOST_CONFIG * sizeof(host_config));
|
||||||
memset(tmp_config, 0, MAX_HOST_CONFIG * sizeof(host_config));
|
memset(tmp_config, 0, CONFIG_MAX_HOST_CONFIG * sizeof(host_config));
|
||||||
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
int mode = 0;
|
int mode = 0;
|
||||||
@ -195,7 +194,7 @@ int config_load(const char *filename) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int shm_id = shmget(SHM_KEY_CONFIG, 0, 0);
|
int shm_id = shmget(CONFIG_SHM_KEY, 0, 0);
|
||||||
if (shm_id < 0) {
|
if (shm_id < 0) {
|
||||||
fprintf(stderr, ERR_STR "Unable to get shared memory id: %s" CLR_STR "\n", strerror(errno));
|
fprintf(stderr, ERR_STR "Unable to get shared memory id: %s" CLR_STR "\n", strerror(errno));
|
||||||
shmdt(config);
|
shmdt(config);
|
||||||
@ -208,7 +207,7 @@ int config_load(const char *filename) {
|
|||||||
fprintf(stderr, ERR_STR "Unable to attach shared memory (rw): %s" CLR_STR "\n", strerror(errno));
|
fprintf(stderr, ERR_STR "Unable to attach shared memory (rw): %s" CLR_STR "\n", strerror(errno));
|
||||||
return -4;
|
return -4;
|
||||||
}
|
}
|
||||||
memcpy(shm_rw, tmp_config, MAX_HOST_CONFIG * sizeof(host_config));
|
memcpy(shm_rw, tmp_config, CONFIG_MAX_HOST_CONFIG * sizeof(host_config));
|
||||||
free(tmp_config);
|
free(tmp_config);
|
||||||
shmdt(shm_rw);
|
shmdt(shm_rw);
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -10,6 +10,9 @@
|
|||||||
|
|
||||||
#include "uri.h"
|
#include "uri.h"
|
||||||
|
|
||||||
|
#define CONFIG_SHM_KEY 255642
|
||||||
|
#define CONFIG_MAX_HOST_CONFIG 64
|
||||||
|
|
||||||
#define CONFIG_TYPE_UNSET 0
|
#define CONFIG_TYPE_UNSET 0
|
||||||
#define CONFIG_TYPE_LOCAL 1
|
#define CONFIG_TYPE_LOCAL 1
|
||||||
#define CONFIG_TYPE_REVERSE_PROXY 2
|
#define CONFIG_TYPE_REVERSE_PROXY 2
|
||||||
|
@ -7,10 +7,9 @@
|
|||||||
|
|
||||||
#include "fastcgi.h"
|
#include "fastcgi.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "../client.h"
|
#include "compress.h"
|
||||||
#include "../necronda-server.h"
|
#include "../necronda-server.h"
|
||||||
#include <sys/un.h>
|
#include <sys/un.h>
|
||||||
#include <zlib.h>
|
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@ -279,7 +278,7 @@ int fastcgi_header(fastcgi_conn *conn, http_res *res, char *err_msg) {
|
|||||||
FCGI_Header header;
|
FCGI_Header header;
|
||||||
char *content;
|
char *content;
|
||||||
unsigned short content_len, req_id;
|
unsigned short content_len, req_id;
|
||||||
int ret;
|
long ret;
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
@ -373,7 +372,7 @@ int fastcgi_header(fastcgi_conn *conn, http_res *res, char *err_msg) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ret = http_parse_header_field(&res->hdr, ptr, pos0);
|
ret = http_parse_header_field(&res->hdr, ptr, pos0);
|
||||||
if (ret != 0) return 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;
|
||||||
}
|
}
|
||||||
@ -385,7 +384,7 @@ int fastcgi_header(fastcgi_conn *conn, http_res *res, char *err_msg) {
|
|||||||
|
|
||||||
int fastcgi_send(fastcgi_conn *conn, sock *client, int flags) {
|
int fastcgi_send(fastcgi_conn *conn, sock *client, int flags) {
|
||||||
FCGI_Header header;
|
FCGI_Header header;
|
||||||
int ret;
|
long ret;
|
||||||
char buf0[256];
|
char buf0[256];
|
||||||
int len;
|
int len;
|
||||||
char *content, *ptr;
|
char *content, *ptr;
|
||||||
@ -393,15 +392,18 @@ int fastcgi_send(fastcgi_conn *conn, sock *client, int flags) {
|
|||||||
char comp_out[4096];
|
char comp_out[4096];
|
||||||
int finish_comp = 0;
|
int finish_comp = 0;
|
||||||
|
|
||||||
z_stream strm;
|
compress_ctx comp_ctx;
|
||||||
if (flags & FASTCGI_COMPRESS) {
|
if (flags & FASTCGI_COMPRESS_BR) {
|
||||||
int level = NECRONDA_ZLIB_LEVEL;
|
flags &= ~FASTCGI_COMPRESS_GZ;
|
||||||
strm.zalloc = Z_NULL;
|
if (compress_init(&comp_ctx, COMPRESS_BR) != 0) {
|
||||||
strm.zfree = Z_NULL;
|
print(ERR_STR "Unable to init brotli: %s" CLR_STR, strerror(errno));
|
||||||
strm.opaque = Z_NULL;
|
flags &= ~FASTCGI_COMPRESS_BR;
|
||||||
if (deflateInit(&strm, level) != Z_OK) {
|
}
|
||||||
print(ERR_STR "Unable to init deflate: %s" CLR_STR, strerror(errno));
|
} else if (flags & FASTCGI_COMPRESS_GZ) {
|
||||||
flags &= !FASTCGI_COMPRESS;
|
flags &= ~FASTCGI_COMPRESS_BR;
|
||||||
|
if (compress_init(&comp_ctx, COMPRESS_GZ) != 0) {
|
||||||
|
print(ERR_STR "Unable to init gzip: %s" CLR_STR, strerror(errno));
|
||||||
|
flags &= ~FASTCGI_COMPRESS_GZ;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -421,6 +423,7 @@ int fastcgi_send(fastcgi_conn *conn, sock *client, int flags) {
|
|||||||
print(ERR_STR "Unable to receive from PHP-FPM" CLR_STR);
|
print(ERR_STR "Unable to receive from PHP-FPM" CLR_STR);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
req_id = (header.requestIdB1 << 8) | header.requestIdB0;
|
req_id = (header.requestIdB1 << 8) | header.requestIdB0;
|
||||||
content_len = (header.contentLengthB1 << 8) | header.contentLengthB0;
|
content_len = (header.contentLengthB1 << 8) | header.contentLengthB0;
|
||||||
content = malloc(content_len + header.paddingLength);
|
content = malloc(content_len + header.paddingLength);
|
||||||
@ -455,7 +458,7 @@ int fastcgi_send(fastcgi_conn *conn, sock *client, int flags) {
|
|||||||
content_len = 0;
|
content_len = 0;
|
||||||
goto out;
|
goto out;
|
||||||
finish:
|
finish:
|
||||||
deflateEnd(&strm);
|
compress_free(&comp_ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flags & FASTCGI_CHUNKED) {
|
if (flags & FASTCGI_CHUNKED) {
|
||||||
@ -466,20 +469,18 @@ int fastcgi_send(fastcgi_conn *conn, sock *client, int flags) {
|
|||||||
} else if (header.type == FCGI_STDERR) {
|
} else if (header.type == FCGI_STDERR) {
|
||||||
fastcgi_php_error(content, content_len, buf0);
|
fastcgi_php_error(content, content_len, buf0);
|
||||||
} else if (header.type == FCGI_STDOUT) {
|
} else if (header.type == FCGI_STDOUT) {
|
||||||
|
unsigned long avail_in, avail_out;
|
||||||
out:
|
out:
|
||||||
if (flags & FASTCGI_COMPRESS) {
|
avail_in = content_len;
|
||||||
strm.avail_in = content_len;
|
void *next_in = ptr;
|
||||||
strm.next_in = (unsigned char *) ptr;
|
|
||||||
}
|
|
||||||
do {
|
do {
|
||||||
int buf_len = content_len;
|
int buf_len = content_len;
|
||||||
if (flags & FASTCGI_COMPRESS) {
|
if (flags & FASTCGI_COMPRESS) {
|
||||||
strm.avail_out = sizeof(comp_out);
|
avail_out = sizeof(comp_out);
|
||||||
strm.next_out = (unsigned char *) comp_out;
|
compress_compress(&comp_ctx, next_in + content_len - avail_in, &avail_in, comp_out, &avail_out,
|
||||||
deflate(&strm, finish_comp ? Z_FINISH : Z_NO_FLUSH);
|
finish_comp);
|
||||||
strm.avail_in = 0;
|
|
||||||
ptr = comp_out;
|
ptr = comp_out;
|
||||||
buf_len = (int) (sizeof(comp_out) - strm.avail_out);
|
buf_len = (int) (sizeof(comp_out) - avail_out);
|
||||||
}
|
}
|
||||||
if (buf_len != 0) {
|
if (buf_len != 0) {
|
||||||
len = sprintf(buf0, "%X\r\n", buf_len);
|
len = sprintf(buf0, "%X\r\n", buf_len);
|
||||||
@ -487,7 +488,7 @@ int fastcgi_send(fastcgi_conn *conn, sock *client, int flags) {
|
|||||||
sock_send(client, ptr, buf_len, 0);
|
sock_send(client, ptr, buf_len, 0);
|
||||||
if (flags & FASTCGI_CHUNKED) sock_send(client, "\r\n", 2, 0);
|
if (flags & FASTCGI_CHUNKED) sock_send(client, "\r\n", 2, 0);
|
||||||
}
|
}
|
||||||
} while ((flags & FASTCGI_COMPRESS) && strm.avail_out == 0);
|
} while ((flags & FASTCGI_COMPRESS) && (avail_in != 0 || avail_out != sizeof(comp_out)));
|
||||||
if (finish_comp) goto finish;
|
if (finish_comp) goto finish;
|
||||||
} else {
|
} else {
|
||||||
print(ERR_STR "Unknown FastCGI type: %i" CLR_STR, header.type);
|
print(ERR_STR "Unknown FastCGI type: %i" CLR_STR, header.type);
|
||||||
|
@ -13,7 +13,13 @@
|
|||||||
#include "uri.h"
|
#include "uri.h"
|
||||||
|
|
||||||
#define FASTCGI_CHUNKED 1
|
#define FASTCGI_CHUNKED 1
|
||||||
#define FASTCGI_COMPRESS 2
|
#define FASTCGI_COMPRESS_GZ 2
|
||||||
|
#define FASTCGI_COMPRESS_BR 4
|
||||||
|
#define FASTCGI_COMPRESS 6
|
||||||
|
|
||||||
|
#ifndef PHP_FPM_SOCKET
|
||||||
|
# define PHP_FPM_SOCKET "/var/run/php-fpm/php-fpm.sock"
|
||||||
|
#endif
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int socket;
|
int socket;
|
||||||
|
69
src/lib/geoip.c
Normal file
69
src/lib/geoip.c
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
/**
|
||||||
|
* Necronda Web Server
|
||||||
|
* MaxMind GeoIP Database interface
|
||||||
|
* src/lib/geoip.c
|
||||||
|
* Lorenz Stechauner, 2021-05-04
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "geoip.h"
|
||||||
|
|
||||||
|
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", (unsigned long long) 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;
|
||||||
|
}
|
18
src/lib/geoip.h
Normal file
18
src/lib/geoip.h
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
/**
|
||||||
|
* Necronda Web Server
|
||||||
|
* MaxMind GeoIP Database interface (header file)
|
||||||
|
* src/lib/geoip.h
|
||||||
|
* Lorenz Stechauner, 2021-05-04
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef NECRONDA_SERVER_GEOIP_H
|
||||||
|
#define NECRONDA_SERVER_GEOIP_H
|
||||||
|
|
||||||
|
#include <maxminddb.h>
|
||||||
|
|
||||||
|
#define GEOIP_MAX_SIZE 8192
|
||||||
|
|
||||||
|
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
|
@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
#include "http.h"
|
#include "http.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "../necronda-server.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) {
|
||||||
@ -33,7 +33,7 @@ void http_free_hdr(http_hdr *hdr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void http_free_req(http_req *req) {
|
void http_free_req(http_req *req) {
|
||||||
if (req->uri == NULL) free(req->uri);
|
if (req->uri != NULL) free(req->uri);
|
||||||
req->uri = NULL;
|
req->uri = NULL;
|
||||||
http_free_hdr(&req->hdr);
|
http_free_hdr(&req->hdr);
|
||||||
}
|
}
|
||||||
@ -85,19 +85,19 @@ int http_receive_request(sock *client, http_req *req) {
|
|||||||
while (1) {
|
while (1) {
|
||||||
rcv_len = sock_recv(client, buf, CLIENT_MAX_HEADER_SIZE, 0);
|
rcv_len = sock_recv(client, buf, CLIENT_MAX_HEADER_SIZE, 0);
|
||||||
if (rcv_len <= 0) {
|
if (rcv_len <= 0) {
|
||||||
print("Unable to receive: %s", sock_strerror(client));
|
print("Unable to receive http header: %s", sock_strerror(client));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned long header_len = strstr(buf, "\r\n\r\n") - buf + 4;
|
unsigned long header_len = strstr(buf, "\r\n\r\n") - buf + 4;
|
||||||
if (header_len <= 0) {
|
if (header_len <= 0) {
|
||||||
print(ERR_STR "Unable to parse header: End of header not found" CLR_STR);
|
print(ERR_STR "Unable to parse http header: End of header not found" CLR_STR);
|
||||||
return 5;
|
return 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < header_len; i++) {
|
for (int i = 0; i < header_len; i++) {
|
||||||
if ((buf[i] >= 0x00 && buf[i] <= 0x1F && buf[i] != '\r' && buf[i] != '\n') || buf[i] == 0x7F) {
|
if ((buf[i] >= 0x00 && buf[i] <= 0x1F && buf[i] != '\r' && buf[i] != '\n') || buf[i] == 0x7F) {
|
||||||
print(ERR_STR "Unable to parse header: Header contains illegal characters" CLR_STR);
|
print(ERR_STR "Unable to parse http header: Header contains illegal characters" CLR_STR);
|
||||||
return 4;
|
return 4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -106,7 +106,7 @@ int http_receive_request(sock *client, http_req *req) {
|
|||||||
while (header_len > (ptr - buf + 2)) {
|
while (header_len > (ptr - buf + 2)) {
|
||||||
pos0 = strstr(ptr, "\r\n");
|
pos0 = strstr(ptr, "\r\n");
|
||||||
if (pos0 == NULL) {
|
if (pos0 == NULL) {
|
||||||
print(ERR_STR "Unable to parse header: Invalid header format" CLR_STR);
|
print(ERR_STR "Unable to parse http header: Invalid header format" CLR_STR);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,13 +115,13 @@ int http_receive_request(sock *client, http_req *req) {
|
|||||||
if (pos1 == NULL) goto err_hdr_fmt;
|
if (pos1 == NULL) goto err_hdr_fmt;
|
||||||
|
|
||||||
if (pos1 - ptr - 1 >= sizeof(req->method)) {
|
if (pos1 - ptr - 1 >= sizeof(req->method)) {
|
||||||
print(ERR_STR "Unable to parse header: Method name too long" CLR_STR);
|
print(ERR_STR "Unable to parse http header: Method name too long" CLR_STR);
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < (pos1 - ptr - 1); i++) {
|
for (int i = 0; i < (pos1 - ptr - 1); i++) {
|
||||||
if (ptr[i] < 'A' || ptr[i] > 'Z') {
|
if (ptr[i] < 'A' || ptr[i] > 'Z') {
|
||||||
print(ERR_STR "Unable to parse header: Invalid method" CLR_STR);
|
print(ERR_STR "Unable to parse http header: Invalid method" CLR_STR);
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -130,12 +130,12 @@ int http_receive_request(sock *client, http_req *req) {
|
|||||||
pos2 = memchr(pos1, ' ', rcv_len - (pos1 - buf)) + 1;
|
pos2 = memchr(pos1, ' ', rcv_len - (pos1 - buf)) + 1;
|
||||||
if (pos2 == NULL) {
|
if (pos2 == NULL) {
|
||||||
err_hdr_fmt:
|
err_hdr_fmt:
|
||||||
print(ERR_STR "Unable to parse header: Invalid header format" CLR_STR);
|
print(ERR_STR "Unable to parse http header: Invalid header format" CLR_STR);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (memcmp(pos2, "HTTP/", 5) != 0 || memcmp(pos2 + 8, "\r\n", 2) != 0) {
|
if (memcmp(pos2, "HTTP/", 5) != 0 || memcmp(pos2 + 8, "\r\n", 2) != 0) {
|
||||||
print(ERR_STR "Unable to parse header: Invalid version" CLR_STR);
|
print(ERR_STR "Unable to parse http header: Invalid version" CLR_STR);
|
||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -312,3 +312,17 @@ const http_doc_info *http_get_status_info(const http_status *status) {
|
|||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int http_get_compression(const http_req *req, const http_res *res) {
|
||||||
|
char *accept_encoding = http_get_header_field(&req->hdr, "Accept-Encoding");
|
||||||
|
char *content_type = http_get_header_field(&res->hdr, "Content-Type");
|
||||||
|
char *content_encoding = http_get_header_field(&res->hdr, "Content-Encoding");
|
||||||
|
if (mime_is_compressible(content_type) && content_encoding == NULL && accept_encoding != NULL) {
|
||||||
|
if (strstr(accept_encoding, "br") != NULL) {
|
||||||
|
return COMPRESS_BR;
|
||||||
|
} else if (strstr(accept_encoding, "gzip") != NULL) {
|
||||||
|
return COMPRESS_GZ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
@ -18,11 +18,27 @@
|
|||||||
#define HTTP_REMOVE_ALL 1
|
#define HTTP_REMOVE_ALL 1
|
||||||
#define HTTP_REMOVE_LAST 2
|
#define HTTP_REMOVE_LAST 2
|
||||||
|
|
||||||
|
#define HTTP_1XX_STR "\x1B[1;32m"
|
||||||
|
#define HTTP_2XX_STR "\x1B[1;32m"
|
||||||
|
#define HTTP_3XX_STR "\x1B[1;33m"
|
||||||
|
#define HTTP_4XX_STR "\x1B[1;31m"
|
||||||
|
#define HTTP_5XX_STR "\x1B[1;31m"
|
||||||
|
|
||||||
#define HTTP_COLOR_SUCCESS "#008000"
|
#define HTTP_COLOR_SUCCESS "#008000"
|
||||||
#define HTTP_COLOR_INFO "#606060"
|
#define HTTP_COLOR_INFO "#606060"
|
||||||
#define HTTP_COLOR_WARNING "#E0C000"
|
#define HTTP_COLOR_WARNING "#E0C000"
|
||||||
#define HTTP_COLOR_ERROR "#C00000"
|
#define HTTP_COLOR_ERROR "#C00000"
|
||||||
|
|
||||||
|
#define CLIENT_MAX_HEADER_SIZE 8192
|
||||||
|
|
||||||
|
#ifndef SERVER_STR
|
||||||
|
# define SERVER_STR "Necronda"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef SERVER_STR_HTML
|
||||||
|
# define SERVER_STR_HTML "Necronda web server"
|
||||||
|
#endif
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
unsigned short code;
|
unsigned short code;
|
||||||
char type[16];
|
char type[16];
|
||||||
@ -49,13 +65,13 @@ typedef struct {
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
char method[16];
|
char method[16];
|
||||||
char *uri;
|
char *uri;
|
||||||
char version[3];
|
char version[4];
|
||||||
http_hdr hdr;
|
http_hdr hdr;
|
||||||
} http_req;
|
} http_req;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
const http_status *status;
|
const http_status *status;
|
||||||
char version[3];
|
char version[4];
|
||||||
http_hdr hdr;
|
http_hdr hdr;
|
||||||
} http_res;
|
} http_res;
|
||||||
|
|
||||||
@ -108,4 +124,6 @@ char *http_get_date(char *buf, size_t size);
|
|||||||
|
|
||||||
const http_doc_info *http_get_status_info(const http_status *status);
|
const http_doc_info *http_get_status_info(const http_status *status);
|
||||||
|
|
||||||
|
int http_get_compression(const http_req *req, const http_res *res);
|
||||||
|
|
||||||
#endif //NECRONDA_SERVER_HTTP_H
|
#endif //NECRONDA_SERVER_HTTP_H
|
||||||
|
@ -5,8 +5,8 @@
|
|||||||
* Lorenz Stechauner, 2021-05-03
|
* Lorenz Stechauner, 2021-05-03
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "../necronda.h"
|
||||||
#include "http.h"
|
#include "http.h"
|
||||||
#include "../necronda-server.h"
|
|
||||||
|
|
||||||
const http_status http_statuses[] = {
|
const http_status http_statuses[] = {
|
||||||
{100, "Informational", "Continue"},
|
{100, "Informational", "Continue"},
|
||||||
@ -137,7 +137,7 @@ const char http_default_document[] =
|
|||||||
"\t<main>\n"
|
"\t<main>\n"
|
||||||
"\t\t<section>\n"
|
"\t\t<section>\n"
|
||||||
"%3$s"
|
"%3$s"
|
||||||
"\t\t\t<div class=\"footer\"><a href=\"https://%7$s/\">%7$s</a> - Necronda web server " NECRONDA_VERSION "</div>\n"
|
"\t\t\t<div class=\"footer\"><a href=\"https://%7$s/\">%7$s</a> - " SERVER_STR_HTML "</div>\n"
|
||||||
"\t\t</section>\n"
|
"\t\t</section>\n"
|
||||||
"\t</main>\n"
|
"\t</main>\n"
|
||||||
"</body>\n"
|
"</body>\n"
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
#include "rev_proxy.h"
|
#include "rev_proxy.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "../client.h"
|
#include "compress.h"
|
||||||
#include "../necronda-server.h"
|
#include "../necronda-server.h"
|
||||||
#include <openssl/ssl.h>
|
#include <openssl/ssl.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@ -378,46 +378,110 @@ int rev_proxy_init(http_req *req, http_res *res, host_config *conf, sock *client
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int rev_proxy_send(sock *client, int chunked, unsigned long len_to_send) {
|
int rev_proxy_send(sock *client, unsigned long len_to_send, int flags) {
|
||||||
|
// TODO handle websockets
|
||||||
long ret;
|
long ret;
|
||||||
char buffer[CHUNK_SIZE];
|
char buffer[CHUNK_SIZE];
|
||||||
|
char comp_out[CHUNK_SIZE];
|
||||||
|
char buf[256];
|
||||||
long len, snd_len;
|
long len, snd_len;
|
||||||
// TODO handle websockets
|
int finish_comp = 0;
|
||||||
do {
|
char *ptr;
|
||||||
if (chunked) {
|
|
||||||
ret = sock_recv(&rev_proxy, buffer, 16, MSG_PEEK);
|
compress_ctx comp_ctx;
|
||||||
if (ret <= 0) {
|
if (flags & REV_PROXY_COMPRESS_BR) {
|
||||||
print("Unable to receive: %s", sock_strerror(&rev_proxy));
|
flags &= ~REV_PROXY_COMPRESS_GZ;
|
||||||
break;
|
if (compress_init(&comp_ctx, COMPRESS_BR) != 0) {
|
||||||
|
print(ERR_STR "Unable to init brotli: %s" CLR_STR, strerror(errno));
|
||||||
|
flags &= ~REV_PROXY_COMPRESS_BR;
|
||||||
}
|
}
|
||||||
|
} else if (flags & REV_PROXY_COMPRESS_GZ) {
|
||||||
|
flags &= ~REV_PROXY_COMPRESS_BR;
|
||||||
|
if (compress_init(&comp_ctx, COMPRESS_GZ) != 0) {
|
||||||
|
print(ERR_STR "Unable to init gzip: %s" CLR_STR, strerror(errno));
|
||||||
|
flags &= ~REV_PROXY_COMPRESS_GZ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
snd_len = 0;
|
||||||
|
if (flags & REV_PROXY_CHUNKED) {
|
||||||
|
char *pos;
|
||||||
|
ret = sock_recv(&rev_proxy, buffer, 16, MSG_PEEK);
|
||||||
|
if (ret <= 0) goto err0;
|
||||||
|
|
||||||
len_to_send = strtol(buffer, NULL, 16);
|
len_to_send = strtol(buffer, NULL, 16);
|
||||||
char *pos = strstr(buffer, "\r\n");
|
pos = strstr(buffer, "\r\n");
|
||||||
len = pos - buffer + 2;
|
len = pos - buffer + 2;
|
||||||
ret = sock_send(client, buffer, len, 0);
|
|
||||||
|
|
||||||
sock_recv(&rev_proxy, buffer, len, 0);
|
sock_recv(&rev_proxy, buffer, len, 0);
|
||||||
if (ret <= 0) break;
|
if (ret <= 0) {
|
||||||
|
err0:
|
||||||
|
print("Unable to receive from server: %s", sock_strerror(&rev_proxy));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len_to_send == 0 && (flags & REV_PROXY_COMPRESS)) {
|
||||||
|
finish_comp = 1;
|
||||||
|
len = 0;
|
||||||
|
goto out;
|
||||||
|
finish:
|
||||||
|
compress_free(&comp_ctx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
snd_len = 0;
|
|
||||||
while (snd_len < len_to_send) {
|
while (snd_len < len_to_send) {
|
||||||
len = sock_recv(&rev_proxy, buffer, CHUNK_SIZE < (len_to_send - snd_len) ? CHUNK_SIZE : len_to_send - snd_len, 0);
|
unsigned long avail_in, avail_out;
|
||||||
ret = sock_send(client, buffer, len, 0);
|
ret = sock_recv(&rev_proxy, buffer, CHUNK_SIZE < (len_to_send - snd_len) ? CHUNK_SIZE : len_to_send - snd_len, 0);
|
||||||
if (ret <= 0) {
|
if (ret <= 0) {
|
||||||
|
print("Unable to receive from server: %s", sock_strerror(&rev_proxy));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
len = ret;
|
||||||
|
ptr = buffer;
|
||||||
|
out:
|
||||||
|
avail_in = len;
|
||||||
|
void *next_in = ptr;
|
||||||
|
do {
|
||||||
|
long buf_len = len;
|
||||||
|
if (flags & REV_PROXY_COMPRESS) {
|
||||||
|
avail_out = sizeof(comp_out);
|
||||||
|
compress_compress(&comp_ctx, next_in + len - avail_in, &avail_in, comp_out, &avail_out,
|
||||||
|
finish_comp);
|
||||||
|
ptr = comp_out;
|
||||||
|
buf_len = (int) (sizeof(comp_out) - avail_out);
|
||||||
|
snd_len += (long) (len - avail_in);
|
||||||
|
}
|
||||||
|
if (buf_len != 0) {
|
||||||
|
len = sprintf(buf, "%lX\r\n", buf_len);
|
||||||
|
ret = 1;
|
||||||
|
if (flags & REV_PROXY_CHUNKED) ret = sock_send(client, buf, len, 0);
|
||||||
|
if (ret <= 0) goto err;
|
||||||
|
ret = sock_send(client, ptr, buf_len, 0);
|
||||||
|
if (ret <= 0) goto err;
|
||||||
|
if (!(flags & REV_PROXY_COMPRESS)) snd_len += ret;
|
||||||
|
if (flags & REV_PROXY_CHUNKED) ret = sock_send(client, "\r\n", 2, 0);
|
||||||
|
if (ret <= 0) {
|
||||||
|
err:
|
||||||
print(ERR_STR "Unable to send: %s" CLR_STR, sock_strerror(client));
|
print(ERR_STR "Unable to send: %s" CLR_STR, sock_strerror(client));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
snd_len += ret;
|
}
|
||||||
|
} while ((flags & REV_PROXY_COMPRESS) && (avail_in != 0 || avail_out != sizeof(comp_out)));
|
||||||
|
if (ret <= 0) break;
|
||||||
|
if (finish_comp) goto finish;
|
||||||
}
|
}
|
||||||
if (ret <= 0) break;
|
if (ret <= 0) break;
|
||||||
if (chunked) {
|
if (flags & REV_PROXY_CHUNKED) sock_recv(&rev_proxy, buffer, 2, 0);
|
||||||
sock_recv(&rev_proxy, buffer, 2, 0);
|
} while ((flags & REV_PROXY_CHUNKED) && len_to_send > 0);
|
||||||
ret = sock_send(client, "\r\n", 2, 0);
|
|
||||||
|
if (ret <= 0) return (int) -1;
|
||||||
|
|
||||||
|
if (flags & REV_PROXY_CHUNKED) {
|
||||||
|
ret = sock_send(client, "0\r\n\r\n", 5, 0);
|
||||||
if (ret <= 0) {
|
if (ret <= 0) {
|
||||||
print(ERR_STR "Unable to send: %s" CLR_STR, sock_strerror(client));
|
print(ERR_STR "Unable to send: %s" CLR_STR, sock_strerror(client));
|
||||||
break;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} while (chunked && len_to_send > 0);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,11 @@
|
|||||||
#ifndef NECRONDA_SERVER_REV_PROXY_H
|
#ifndef NECRONDA_SERVER_REV_PROXY_H
|
||||||
#define NECRONDA_SERVER_REV_PROXY_H
|
#define NECRONDA_SERVER_REV_PROXY_H
|
||||||
|
|
||||||
|
#define REV_PROXY_CHUNKED 1
|
||||||
|
#define REV_PROXY_COMPRESS_GZ 2
|
||||||
|
#define REV_PROXY_COMPRESS_BR 4
|
||||||
|
#define REV_PROXY_COMPRESS 6
|
||||||
|
|
||||||
#include "http.h"
|
#include "http.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
@ -22,6 +27,6 @@ int rev_proxy_response_header(http_req *req, http_res *res);
|
|||||||
int rev_proxy_init(http_req *req, http_res *res, host_config *conf, sock *client, http_status *custom_status,
|
int rev_proxy_init(http_req *req, http_res *res, host_config *conf, sock *client, http_status *custom_status,
|
||||||
char *err_msg);
|
char *err_msg);
|
||||||
|
|
||||||
int rev_proxy_send(sock *client, int chunked, unsigned long len_to_send);
|
int rev_proxy_send(sock *client, unsigned long len_to_send, int flags);
|
||||||
|
|
||||||
#endif //NECRONDA_SERVER_REV_PROXY_H
|
#endif //NECRONDA_SERVER_REV_PROXY_H
|
||||||
|
@ -52,12 +52,12 @@ long sock_send(sock *s, void *buf, unsigned long len, int flags) {
|
|||||||
long ret;
|
long ret;
|
||||||
if (s->enc) {
|
if (s->enc) {
|
||||||
ret = SSL_write(s->ssl, buf, (int) len);
|
ret = SSL_write(s->ssl, buf, (int) len);
|
||||||
|
s->_ssl_error = ERR_get_error();
|
||||||
} else {
|
} else {
|
||||||
ret = send(s->socket, buf, len, flags);
|
ret = send(s->socket, buf, len, flags);
|
||||||
}
|
}
|
||||||
s->_last_ret = ret;
|
s->_last_ret = ret;
|
||||||
s->_errno = errno;
|
s->_errno = errno;
|
||||||
s->_ssl_error = ERR_get_error();
|
|
||||||
return ret >= 0 ? ret : -1;
|
return ret >= 0 ? ret : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,12 +69,12 @@ long sock_recv(sock *s, void *buf, unsigned long len, int flags) {
|
|||||||
} else {
|
} else {
|
||||||
ret = SSL_read(s->ssl, buf, (int) len);
|
ret = SSL_read(s->ssl, buf, (int) len);
|
||||||
}
|
}
|
||||||
|
s->_ssl_error = ERR_get_error();
|
||||||
} else {
|
} else {
|
||||||
ret = recv(s->socket, buf, len, flags);
|
ret = recv(s->socket, buf, len, flags);
|
||||||
}
|
}
|
||||||
s->_last_ret = ret;
|
s->_last_ret = ret;
|
||||||
s->_errno = errno;
|
s->_errno = errno;
|
||||||
s->_ssl_error = ERR_get_error();
|
|
||||||
return ret >= 0 ? ret : -1;
|
return ret >= 0 ? ret : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,14 +97,14 @@ long sock_splice(sock *dst, sock *src, void *buf, unsigned long buf_len, unsigne
|
|||||||
|
|
||||||
int sock_close(sock *s) {
|
int sock_close(sock *s) {
|
||||||
if ((int) s->enc && s->ssl != NULL) {
|
if ((int) s->enc && s->ssl != NULL) {
|
||||||
SSL_shutdown(s->ssl);
|
if (s->_last_ret >= 0) SSL_shutdown(s->ssl);
|
||||||
SSL_free(s->ssl);
|
SSL_free(s->ssl);
|
||||||
|
s->ssl = NULL;
|
||||||
}
|
}
|
||||||
shutdown(s->socket, SHUT_RDWR);
|
shutdown(s->socket, SHUT_RDWR);
|
||||||
close(s->socket);
|
close(s->socket);
|
||||||
s->socket = 0;
|
s->socket = 0;
|
||||||
s->enc = 0;
|
s->enc = 0;
|
||||||
s->ssl = NULL;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,7 +19,8 @@ typedef struct {
|
|||||||
char etag[64];
|
char etag[64];
|
||||||
char type[24];
|
char type[24];
|
||||||
char charset[16];
|
char charset[16];
|
||||||
char filename_comp[256];
|
char filename_comp_gz[256];
|
||||||
|
char filename_comp_br[256];
|
||||||
struct stat stat;
|
struct stat stat;
|
||||||
} meta_data;
|
} meta_data;
|
||||||
|
|
||||||
|
103
src/lib/utils.c
103
src/lib/utils.c
@ -6,6 +6,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
@ -26,7 +27,7 @@ char *format_duration(unsigned long micros, char *buf) {
|
|||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
int url_encode_component(const char *str, char *enc, ssize_t *size) {
|
int url_encode_component(const char *str, char *enc, long *size) {
|
||||||
char *ptr = enc;
|
char *ptr = enc;
|
||||||
char ch;
|
char ch;
|
||||||
memset(enc, 0, *size);
|
memset(enc, 0, *size);
|
||||||
@ -53,7 +54,7 @@ int url_encode_component(const char *str, char *enc, ssize_t *size) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int url_encode(const char *str, char *enc, ssize_t *size) {
|
int url_encode(const char *str, char *enc, long *size) {
|
||||||
char *ptr = enc;
|
char *ptr = enc;
|
||||||
unsigned char ch;
|
unsigned char ch;
|
||||||
memset(enc, 0, *size);
|
memset(enc, 0, *size);
|
||||||
@ -76,7 +77,7 @@ int url_encode(const char *str, char *enc, ssize_t *size) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int url_decode(const char *str, char *dec, ssize_t *size) {
|
int url_decode(const char *str, char *dec, long *size) {
|
||||||
char *ptr = dec;
|
char *ptr = dec;
|
||||||
char ch, buf[3];
|
char ch, buf[3];
|
||||||
memset(dec, 0, *size);
|
memset(dec, 0, *size);
|
||||||
@ -103,76 +104,30 @@ int url_decode(const char *str, char *dec, ssize_t *size) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int mime_is_compressible(const char *type) {
|
int mime_is_compressible(const char *type) {
|
||||||
|
if (type == NULL) return 0;
|
||||||
|
char type_parsed[64];
|
||||||
|
strncpy(type_parsed, type, sizeof(type_parsed) - 1);
|
||||||
|
char *pos = strchr(type_parsed, ';');
|
||||||
|
if (pos != NULL) pos[0] = 0;
|
||||||
return
|
return
|
||||||
strncmp(type, "text/", 5) == 0 ||
|
strncmp(type_parsed, "text/", 5) == 0 ||
|
||||||
strncmp(type, "message/", 7) == 0 ||
|
strncmp(type_parsed, "message/", 7) == 0 ||
|
||||||
strstr(type, "+xml") != NULL ||
|
strstr(type_parsed, "+xml") != NULL ||
|
||||||
strcmp(type, "application/javascript") == 0 ||
|
strstr(type_parsed, "+json") != NULL ||
|
||||||
strcmp(type, "application/json") == 0 ||
|
strcmp(type_parsed, "application/javascript") == 0 ||
|
||||||
strcmp(type, "application/xml") == 0 ||
|
strcmp(type_parsed, "application/json") == 0 ||
|
||||||
strcmp(type, "application/x-www-form-urlencoded") == 0 ||
|
strcmp(type_parsed, "application/xml") == 0 ||
|
||||||
strcmp(type, "application/x-tex") == 0 ||
|
strcmp(type_parsed, "application/x-www-form-urlencoded") == 0 ||
|
||||||
strcmp(type, "application/x-httpd-php") == 0 ||
|
strcmp(type_parsed, "application/x-tex") == 0 ||
|
||||||
strcmp(type, "application/x-latex") == 0;
|
strcmp(type_parsed, "application/x-httpd-php") == 0 ||
|
||||||
}
|
strcmp(type_parsed, "application/x-latex") == 0 ||
|
||||||
|
strcmp(type_parsed, "application/vnd.ms-fontobject") == 0 ||
|
||||||
MMDB_entry_data_list_s *mmdb_json(MMDB_entry_data_list_s *list, char *str, long *str_off, long str_len) {
|
strcmp(type_parsed, "application/x-font-ttf") == 0 ||
|
||||||
switch (list->entry_data.type) {
|
strcmp(type_parsed, "application/x-javascript") == 0 ||
|
||||||
case MMDB_DATA_TYPE_MAP:
|
strcmp(type_parsed, "application/x-web-app-manifest+json") == 0 ||
|
||||||
*str_off += sprintf(str + *str_off, "{");
|
strcmp(type_parsed, "font/eot") == 0 ||
|
||||||
break;
|
strcmp(type_parsed, "font/opentype") == 0 ||
|
||||||
case MMDB_DATA_TYPE_ARRAY:
|
strcmp(type_parsed, "image/bmp") == 0 ||
|
||||||
*str_off += sprintf(str + *str_off, "[");
|
strcmp(type_parsed, "image/vnd.microsoft.icon") == 0 ||
|
||||||
break;
|
strcmp(type_parsed, "image/x-icon") == 0;
|
||||||
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", (unsigned long long) 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;
|
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,15 @@
|
|||||||
#ifndef NECRONDA_SERVER_UTILS_H
|
#ifndef NECRONDA_SERVER_UTILS_H
|
||||||
#define NECRONDA_SERVER_UTILS_H
|
#define NECRONDA_SERVER_UTILS_H
|
||||||
|
|
||||||
#include <maxminddb.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#define ERR_STR "\x1B[1;31m"
|
||||||
|
#define CLR_STR "\x1B[0m"
|
||||||
|
#define BLD_STR "\x1B[1m"
|
||||||
|
#define WRN_STR "\x1B[1;33m"
|
||||||
|
#define BLUE_STR "\x1B[34m"
|
||||||
|
#define HTTP_STR "\x1B[1;31m"
|
||||||
|
#define HTTPS_STR "\x1B[1;32m"
|
||||||
|
|
||||||
extern char *log_prefix;
|
extern char *log_prefix;
|
||||||
|
|
||||||
@ -24,14 +32,12 @@ extern char *log_prefix;
|
|||||||
|
|
||||||
char *format_duration(unsigned long micros, char *buf);
|
char *format_duration(unsigned long micros, char *buf);
|
||||||
|
|
||||||
int url_encode_component(const char *str, char *enc, ssize_t *size);
|
int url_encode_component(const char *str, char *enc, long *size);
|
||||||
|
|
||||||
int url_encode(const char *str, char *enc, ssize_t *size);
|
int url_encode(const char *str, char *enc, long *size);
|
||||||
|
|
||||||
int url_decode(const char *str, char *dec, ssize_t *size);
|
int url_decode(const char *str, char *dec, long *size);
|
||||||
|
|
||||||
int mime_is_compressible(const char *type);
|
int mime_is_compressible(const char *type);
|
||||||
|
|
||||||
MMDB_entry_data_list_s *mmdb_json(MMDB_entry_data_list_s *list, char *str, long *str_off, long str_len);
|
|
||||||
|
|
||||||
#endif //NECRONDA_SERVER_UTILS_H
|
#endif //NECRONDA_SERVER_UTILS_H
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
#define _POSIX_C_SOURCE 199309L
|
#define _POSIX_C_SOURCE 199309L
|
||||||
|
|
||||||
|
#include "necronda.h"
|
||||||
#include "necronda-server.h"
|
#include "necronda-server.h"
|
||||||
#include "client.c"
|
#include "client.c"
|
||||||
|
|
||||||
@ -14,6 +15,7 @@
|
|||||||
#include "lib/config.h"
|
#include "lib/config.h"
|
||||||
#include "lib/sock.h"
|
#include "lib/sock.h"
|
||||||
#include "lib/rev_proxy.h"
|
#include "lib/rev_proxy.h"
|
||||||
|
#include "lib/geoip.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
@ -29,7 +31,6 @@
|
|||||||
#include <openssl/pem.h>
|
#include <openssl/pem.h>
|
||||||
#include <openssl/ssl.h>
|
#include <openssl/ssl.h>
|
||||||
#include <openssl/conf.h>
|
#include <openssl/conf.h>
|
||||||
#include <maxminddb.h>
|
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
|
|
||||||
int active = 1;
|
int active = 1;
|
||||||
@ -274,6 +275,16 @@ int main(int argc, const char *argv[]) {
|
|||||||
closedir(geoip);
|
closedir(geoip);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret = cache_init();
|
||||||
|
if (ret < 0) {
|
||||||
|
config_unload();
|
||||||
|
return 1;
|
||||||
|
} else if (ret != 0) {
|
||||||
|
children[0] = ret; // pid
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
openssl_init();
|
openssl_init();
|
||||||
|
|
||||||
client.buf = NULL;
|
client.buf = NULL;
|
||||||
@ -282,7 +293,7 @@ int main(int argc, const char *argv[]) {
|
|||||||
client.ctx = SSL_CTX_new(TLS_server_method());
|
client.ctx = SSL_CTX_new(TLS_server_method());
|
||||||
SSL_CTX_set_options(client.ctx, SSL_OP_SINGLE_DH_USE);
|
SSL_CTX_set_options(client.ctx, SSL_OP_SINGLE_DH_USE);
|
||||||
SSL_CTX_set_verify(client.ctx, SSL_VERIFY_NONE, NULL);
|
SSL_CTX_set_verify(client.ctx, SSL_VERIFY_NONE, NULL);
|
||||||
SSL_CTX_set_min_proto_version(client.ctx, TLS1_VERSION);
|
SSL_CTX_set_min_proto_version(client.ctx, TLS1_2_VERSION);
|
||||||
SSL_CTX_set_mode(client.ctx, SSL_MODE_ENABLE_PARTIAL_WRITE);
|
SSL_CTX_set_mode(client.ctx, SSL_MODE_ENABLE_PARTIAL_WRITE);
|
||||||
SSL_CTX_set_cipher_list(client.ctx, "HIGH:!aNULL:!kRSA:!PSK:!SRP:!MD5:!RC4");
|
SSL_CTX_set_cipher_list(client.ctx, "HIGH:!aNULL:!kRSA:!PSK:!SRP:!MD5:!RC4");
|
||||||
SSL_CTX_set_ecdh_auto(client.ctx, 1);
|
SSL_CTX_set_ecdh_auto(client.ctx, 1);
|
||||||
@ -318,16 +329,6 @@ int main(int argc, const char *argv[]) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = cache_init();
|
|
||||||
if (ret < 0) {
|
|
||||||
config_unload();
|
|
||||||
return 1;
|
|
||||||
} else if (ret != 0) {
|
|
||||||
children[0] = ret; // pid
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf(stderr, "Ready to accept connections\n");
|
fprintf(stderr, "Ready to accept connections\n");
|
||||||
|
|
||||||
while (active) {
|
while (active) {
|
||||||
@ -374,6 +375,7 @@ int main(int argc, const char *argv[]) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO outsource in thread
|
||||||
int status = 0;
|
int status = 0;
|
||||||
for (int i = 0; i < MAX_CHILDREN; i++) {
|
for (int i = 0; i < MAX_CHILDREN; i++) {
|
||||||
if (children[i] != 0) {
|
if (children[i] != 0) {
|
||||||
|
@ -1,66 +1,37 @@
|
|||||||
/**
|
/**
|
||||||
* Necronda Web Server
|
* Necronda Web Server
|
||||||
* Main executable (header file)
|
* Main executable (header file)
|
||||||
* src/necronda-server.c
|
* src/necronda-server.h
|
||||||
* Lorenz Stechauner, 2020-12-03
|
* Lorenz Stechauner, 2020-12-03
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef NECRONDA_SERVER_NECRONDA_SERVER_H
|
#ifndef NECRONDA_SERVER_NECRONDA_SERVER_H
|
||||||
#define NECRONDA_SERVER_NECRONDA_SERVER_H
|
#define NECRONDA_SERVER_NECRONDA_SERVER_H
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/time.h>
|
||||||
#include <maxminddb.h>
|
#include <maxminddb.h>
|
||||||
|
|
||||||
#define NUM_SOCKETS 2
|
#define NUM_SOCKETS 2
|
||||||
#define MAX_CHILDREN 1024
|
#define MAX_CHILDREN 1024
|
||||||
#define MAX_MMDB 3
|
#define MAX_MMDB 3
|
||||||
#define MAX_HOST_CONFIG 64
|
|
||||||
#define LISTEN_BACKLOG 16
|
#define LISTEN_BACKLOG 16
|
||||||
#define REQ_PER_CONNECTION 100
|
#define REQ_PER_CONNECTION 200
|
||||||
#define CLIENT_TIMEOUT 3600
|
#define CLIENT_TIMEOUT 3600
|
||||||
#define SERVER_TIMEOUT 4
|
#define SERVER_TIMEOUT 4
|
||||||
|
|
||||||
#define CHUNK_SIZE 8192
|
#define CHUNK_SIZE 8192
|
||||||
#define CLIENT_MAX_HEADER_SIZE 8192
|
|
||||||
#define FILE_CACHE_SIZE 1024
|
|
||||||
#define GEOIP_MAX_SIZE 8192
|
|
||||||
|
|
||||||
#define SHM_KEY_CACHE 255641
|
|
||||||
#define SHM_KEY_CONFIG 255642
|
|
||||||
|
|
||||||
#define ERR_STR "\x1B[1;31m"
|
|
||||||
#define CLR_STR "\x1B[0m"
|
|
||||||
#define BLD_STR "\x1B[1m"
|
|
||||||
#define WRN_STR "\x1B[1;33m"
|
|
||||||
#define BLUE_STR "\x1B[34m"
|
|
||||||
#define HTTP_STR "\x1B[1;31m"
|
|
||||||
#define HTTPS_STR "\x1B[1;32m"
|
|
||||||
|
|
||||||
#define HTTP_1XX_STR "\x1B[1;32m"
|
|
||||||
#define HTTP_2XX_STR "\x1B[1;32m"
|
|
||||||
#define HTTP_3XX_STR "\x1B[1;33m"
|
|
||||||
#define HTTP_4XX_STR "\x1B[1;31m"
|
|
||||||
#define HTTP_5XX_STR "\x1B[1;31m"
|
|
||||||
|
|
||||||
#define NECRONDA_VERSION "4.3"
|
|
||||||
#define SERVER_STR "Necronda/" NECRONDA_VERSION
|
|
||||||
#define NECRONDA_ZLIB_LEVEL 9
|
|
||||||
|
|
||||||
#ifndef DEFAULT_HOST
|
#ifndef DEFAULT_HOST
|
||||||
# define DEFAULT_HOST "www.necronda.net"
|
# define DEFAULT_HOST "www.necronda.net"
|
||||||
#endif
|
#endif
|
||||||
#ifndef MAGIC_FILE
|
|
||||||
#define MAGIC_FILE "/usr/share/file/misc/magic.mgc"
|
|
||||||
#endif
|
|
||||||
#ifndef PHP_FPM_SOCKET
|
|
||||||
#define PHP_FPM_SOCKET "/var/run/php-fpm/php-fpm.sock"
|
|
||||||
#endif
|
|
||||||
#ifndef DEFAULT_CONFIG_FILE
|
|
||||||
#define DEFAULT_CONFIG_FILE "/etc/necronda-server/necronda-server.conf"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
extern int sockets[NUM_SOCKETS];
|
extern int sockets[NUM_SOCKETS];
|
||||||
extern pid_t children[MAX_CHILDREN];
|
extern pid_t children[MAX_CHILDREN];
|
||||||
extern MMDB_s mmdbs[MAX_MMDB];
|
extern MMDB_s mmdbs[MAX_MMDB];
|
||||||
|
|
||||||
|
extern int server_keep_alive;
|
||||||
|
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 struct timeval client_timeout;
|
||||||
|
|
||||||
#endif //NECRONDA_SERVER_NECRONDA_SERVER_H
|
#endif //NECRONDA_SERVER_NECRONDA_SERVER_H
|
||||||
|
15
src/necronda.h
Normal file
15
src/necronda.h
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
/**
|
||||||
|
* 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.4"
|
||||||
|
#define SERVER_STR "Necronda/" NECRONDA_VERSION
|
||||||
|
#define SERVER_STR_HTML "Necronda web server " NECRONDA_VERSION
|
||||||
|
|
||||||
|
#endif //NECRONDA_SERVER_NECRONDA_H
|
Reference in New Issue
Block a user