Add config file

This commit is contained in:
2021-01-06 15:49:52 +01:00
parent 2f6ba62715
commit 080d729f31
6 changed files with 249 additions and 81 deletions

View File

@ -204,9 +204,13 @@ int cache_init() {
int cache_unload() {
int shm_id = shmget(SHM_KEY_CACHE, 0, 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 get shared memory id: %s" CLR_STR "\n", strerror(errno));
shmdt(cache);
return -1;
} else if (shmctl(shm_id, IPC_RMID, NULL) < 0) {
fprintf(stderr, ERR_STR "Unable to configure shared memory: %s" CLR_STR "\n", strerror(errno));
shmdt(cache);
return -1;
}
shmdt(cache);
return 0;

View File

@ -19,23 +19,13 @@ char *client_addr_str, *client_addr_str_ptr, *server_addr_str, *server_addr_str_
struct timeval client_timeout = {.tv_sec = CLIENT_TIMEOUT, .tv_usec = 0};
char *get_webroot(const char *http_host) {
char *webroot = malloc(strlen(webroot_base) + strlen(http_host) + 1);
unsigned long len = strlen(webroot_base);
while (webroot_base[len - 1] == '/') len--;
long pos = strchr(http_host, ':') - http_host;
sprintf(webroot, "%.*s/%.*s", (int) len, webroot_base, (int) (pos < 0 ? strlen(http_host) : pos), http_host);
return path_is_directory(webroot) ? webroot : NULL;
}
int get_dir_mode(const char *webroot) {
char buf[256];
struct stat statbuf;
sprintf(buf, "%s/.necronda-server/dir_mode_info", webroot);
if (stat(buf, &statbuf) == 0) return URI_DIR_MODE_INFO;
sprintf(buf, "%s/.necronda-server/dir_mode_list", webroot);
if (stat(buf, &statbuf) == 0) return URI_DIR_MODE_LIST;
return URI_DIR_MODE_FORBIDDEN;
host_config *get_host_config(const char *host) {
for (int i = 0; i < MAX_HOST_CONFIG; i++) {
host_config *hc = &config[i];
if (hc->type == CONFIG_TYPE_UNSET) break;
if (strcmp(hc->name, host) == 0) return hc;
}
return NULL;
}
void client_terminate() {
@ -49,12 +39,13 @@ int client_websocket_handler() {
int client_request_handler(sock *client, unsigned long client_num, unsigned int req_num) {
struct timespec begin, end;
int ret, client_keep_alive, dir_mode;
int ret, client_keep_alive;
char buf0[1024], buf1[1024];
char msg_buf[4096], msg_pre_buf[4096], err_msg[256];
char buffer[CHUNK_SIZE];
err_msg[0] = 0;
char host[256], *host_ptr, *hdr_connection, *webroot;
char host[256], *host_ptr, *hdr_connection;
host_config *conf;
long content_length = 0;
FILE *file = NULL;
msg_buf[0] = 0;
@ -133,18 +124,22 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int
log_prefix = log_req_prefix;
print(BLD_STR "%s %s" CLR_STR, req.method, req.uri);
// TODO Reverse Proxy
webroot = get_webroot(host);
if (webroot == NULL) {
conf = get_host_config(host);
if (conf == NULL) {
res.status = http_get_status(307);
sprintf(buf0, "https://%s%s", DEFAULT_HOST, req.uri);
http_add_header_field(&res.hdr, "Location", buf0);
goto respond;
}
dir_mode = get_dir_mode(webroot);
if (conf->type != CONFIG_TYPE_LOCAL) {
// TODO Reverse Proxy
res.status = http_get_status(501);
goto respond;
}
http_uri uri;
ret = uri_init(&uri, webroot, req.uri, dir_mode);
ret = uri_init(&uri, conf->local.webroot, req.uri, conf->local.dir_mode);
if (ret != 0) {
if (ret == 1) {
sprintf(err_msg, "Invalid URI: has to start with slash.");
@ -183,7 +178,7 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int
} else if (uri.filename == NULL || (strlen(uri.pathinfo) > 0 && (int) uri.is_static)) {
res.status = http_get_status(404);
goto respond;
} else if (strlen(uri.pathinfo) != 0 && dir_mode != URI_DIR_MODE_INFO) {
} else if (strlen(uri.pathinfo) != 0 && conf->local.dir_mode != URI_DIR_MODE_INFO) {
res.status = http_get_status(404);
goto respond;
}
@ -460,7 +455,7 @@ int client_connection_handler(sock *client, unsigned long client_num) {
clock_gettime(CLOCK_MONOTONIC, &begin);
if (dns_server != NULL) {
if (dns_server[0] != 0) {
sprintf(buf, "dig @%s +short +time=1 -x %s", dns_server, client_addr_str);
FILE *dig = popen(buf, "r");
if (dig == NULL) {

View File

@ -6,12 +6,179 @@
*/
#include "config.h"
#include "uri.h"
#include <sys/ipc.h>
#include <sys/shm.h>
int config_init() {
int shm_id = shmget(SHM_KEY_CONFIG, MAX_HOST_CONFIG * sizeof(host_config), IPC_CREAT | IPC_EXCL | 0640);
if (shm_id < 0) {
fprintf(stderr, ERR_STR "Unable to create shared memory: %s" CLR_STR "\n", strerror(errno));
return -1;
}
void *shm = shmat(shm_id, NULL, SHM_RDONLY);
if (shm == (void *) -1) {
fprintf(stderr, ERR_STR "Unable to attach shared memory (ro): %s" CLR_STR "\n", strerror(errno));
return -2;
}
config = shm;
void *shm_rw = shmat(shm_id, NULL, 0);
if (shm_rw == (void *) -1) {
fprintf(stderr, ERR_STR "Unable to attach shared memory (rw): %s" CLR_STR "\n", strerror(errno));
return -3;
}
config = shm_rw;
memset(config, 0, MAX_HOST_CONFIG * sizeof(host_config));
shmdt(shm_rw);
config = shm;
return 0;
}
int config_load() {
int config_unload() {
int shm_id = shmget(SHM_KEY_CONFIG, 0, 0);
if (shm_id < 0) {
fprintf(stderr, ERR_STR "Unable to get shared memory id: %s" CLR_STR "\n", strerror(errno));
shmdt(config);
return -1;
} else if (shmctl(shm_id, IPC_RMID, NULL) < 0) {
fprintf(stderr, ERR_STR "Unable to configure shared memory: %s" CLR_STR "\n", strerror(errno));
shmdt(config);
return -1;
}
shmdt(config);
return 0;
}
int config_load(const char *filename) {
FILE *file = fopen(filename, "r");
if (file == NULL) {
fprintf(stderr, ERR_STR "Unable to open config file: %s" CLR_STR "\n", strerror(errno));
return -1;
}
fseek(file, 0, SEEK_END);
unsigned long len = ftell(file);
fseek(file, 0, SEEK_SET);
char *conf = malloc(len);
fread(conf, 1, len, file);
fclose(file);
host_config *tmp_config = malloc(MAX_HOST_CONFIG * sizeof(host_config));
memset(tmp_config, 0, MAX_HOST_CONFIG * sizeof(host_config));
int i = 0;
int mode = 0;
char *ptr = NULL;
char host[256], *source, *target;
host[0] = 0;
while ((ptr = strtok(ptr == NULL ? conf : NULL, "\n")) != NULL) {
char *comment = strchr(ptr, '#');
if (comment != NULL) comment[0] = 0;
len = strlen(ptr);
if (ptr[0] == '[') {
if (ptr[len - 1] != ']') goto err;
strncpy(tmp_config[i].name, ptr + 1, len - 2);
i++;
continue;
} else if (i == 0) {
if (len > 12 && strncmp(ptr, "certificate", 11) == 0 && (ptr[11] == ' ' || ptr[11] == '\t')) {
source = ptr + 11;
target = cert_file;
} else if (len > 12 && strncmp(ptr, "private_key", 11) == 0 && (ptr[11] == ' ' || ptr[11] == '\t')) {
source = ptr + 11;
target = key_file;
} else if (len > 10 && strncmp(ptr, "geoip_dir", 9) == 0 && (ptr[9] == ' ' || ptr[9] == '\t')) {
source = ptr + 9;
target = geoip_dir;
} else if (len > 11 && strncmp(ptr, "dns_server", 10) == 0 && (ptr[10] == ' ' || ptr[10] == '\t')) {
source = ptr + 10;
target = dns_server;
}
} else {
host_config *hc = &tmp_config[i - 1];
if (len > 8 && strncmp(ptr, "webroot", 7) == 0 && (ptr[7] == ' ' || ptr[7] == '\t')) {
source = ptr + 7;
target = hc->local.webroot;
if (hc->type != 0 && hc->type != CONFIG_TYPE_LOCAL) {
goto err;
} else {
hc->type = CONFIG_TYPE_LOCAL;
}
} else if (len > 9 && strncmp(ptr, "dir_mode", 8) == 0 && (ptr[8] == ' ' || ptr[8] == '\t')) {
source = ptr + 8;
target = NULL;
mode = 1;
if (hc->type != 0 && hc->type != CONFIG_TYPE_LOCAL) {
goto err;
} else {
hc->type = CONFIG_TYPE_LOCAL;
}
} else if (len > 9 && strncmp(ptr, "hostname", 8) == 0 && (ptr[8] == ' ' || ptr[8] == '\t')) {
source = ptr + 8;
target = hc->rev_proxy.hostname;
if (hc->type != 0 && hc->type != CONFIG_TYPE_REVERSE_PROXY) {
goto err;
} else {
hc->type = CONFIG_TYPE_REVERSE_PROXY;
}
} else if (len > 5 && strncmp(ptr, "port", 4) == 0 && (ptr[4] == ' ' || ptr[4] == '\t')) {
source = ptr + 4;
target = NULL;
mode = 2;
if (hc->type != 0 && hc->type != CONFIG_TYPE_REVERSE_PROXY) {
goto err;
} else {
hc->type = CONFIG_TYPE_REVERSE_PROXY;
}
}
}
char *end_ptr = source + strlen(source) - 1;
while (source[0] == ' ' || source[0] == '\t') source++;
while (end_ptr[0] == ' ' || end_ptr[0] == '\t') end_ptr--;
if (end_ptr <= source) {
err:
free(conf);
free(tmp_config);
fprintf(stderr, ERR_STR "Unable to parse config file" CLR_STR "\n");
return -2;
}
end_ptr[1] = 0;
if (target != NULL) {
strcpy(target, source);
} else if (mode == 1) {
if (strcmp(source, "forbidden") == 0) {
tmp_config[i - 1].local.dir_mode = URI_DIR_MODE_FORBIDDEN;
} else if (strcmp(source, "info") == 0) {
tmp_config[i - 1].local.dir_mode = URI_DIR_MODE_INFO;
} else if (strcmp(source, "list") == 0) {
tmp_config[i - 1].local.dir_mode = URI_DIR_MODE_LIST;
} else {
goto err;
}
} else if (mode == 2) {
tmp_config[i - 1].rev_proxy.port = (unsigned short) strtoul(source, NULL, 10);
}
}
free(conf);
int shm_id = shmget(SHM_KEY_CONFIG, 0, 0);
if (shm_id < 0) {
fprintf(stderr, ERR_STR "Unable to get shared memory id: %s" CLR_STR "\n", strerror(errno));
shmdt(config);
return -3;
}
void *shm_rw = shmat(shm_id, NULL, 0);
if (shm_rw == (void *) -1) {
free(tmp_config);
fprintf(stderr, ERR_STR "Unable to attach shared memory (rw): %s" CLR_STR "\n", strerror(errno));
return -4;
}
memcpy(shm_rw, tmp_config, MAX_HOST_CONFIG * sizeof(host_config));
free(tmp_config);
shmdt(shm_rw);
return 0;
}

View File

@ -9,24 +9,35 @@
#ifndef NECRONDA_SERVER_CONFIG_H
#define NECRONDA_SERVER_CONFIG_H
#define CONFIG_TYPE_UNSET 0
#define CONFIG_TYPE_LOCAL 1
#define CONFIG_TYPE_REVERSE_PROXY 2
typedef struct {
int type;
char name[256];
union {
struct {
char address[256];
char hostname[256];
unsigned short port;
};
} rev_proxy;
struct {
char webroot[256];
unsigned char dir_mode;
};
} local;
};
} host_config;
host_config *config;
char cert_file[256], key_file[256], geoip_dir[256], dns_server[256];
int config_init();
int config_load();
int config_load(const char *filename);
int config_unload();
#endif //NECRONDA_SERVER_CONFIG_H

View File

@ -19,6 +19,7 @@
int active = 1;
const char *config_file;
void openssl_init() {
@ -88,6 +89,7 @@ void destroy() {
fprintf(stderr, ERR_STR "Killed %i child process(es)" CLR_STR "\n", kills);
}
cache_unload();
config_unload();
exit(2);
}
@ -156,6 +158,7 @@ void terminate() {
fprintf(stderr, "Goodbye\n");
}
cache_unload();
config_unload();
exit(0);
}
@ -190,68 +193,42 @@ int main(int argc, const char *argv[]) {
}
printf("Necronda Web Server\n");
ret = config_init();
if (ret != 0) {
return 1;
}
config_file = NULL;
for (int i = 1; i < argc; i++) {
const char *arg = argv[i];
unsigned long len = strlen(arg);
if (strcmp(arg, "-h") == 0 || strcmp(arg, "--help") == 0) {
printf("Usage: necronda-server [-h] -w <PATH> -c <CERT-FILE> -p <KEY-FILE> [-g <DB-DIR>] [-d <DNS-SERVER>]\n"
printf("Usage: necronda-server [-h] [-c <CONFIG-FILE>]\n"
"\n"
"Options:\n"
" -c, --cert <CERT-FILE> path to the full chain certificate file\n"
" -d, --dns <DNS-SERVER> ip address or hostname of a DNS server for dig\n"
" -g, --geoip <DB-DIR> path to a Maxmind GeoIP Database file\n"
" -h, --help print this dialogue\n"
" -p, --privkey <KEY-FILE> path to the private key file\n"
" -w, --webroot <PATH> path to the web root directory\n");
" -c, --config <CONFIG-FILE> path to the config file. If not provided, default will be used\n"
" -h, --help print this dialogue\n");
config_unload();
return 0;
} else if (strcmp(arg, "-w") == 0 || strcmp(arg, "--webroot") == 0) {
} else if (strcmp(arg, "-c") == 0 || strcmp(arg, "--config") == 0) {
if (i == argc - 1) {
fprintf(stderr, ERR_STR "Unable to parse argument %s, usage: --webroot <WEBROOT>" CLR_STR "\n", arg);
fprintf(stderr, ERR_STR "Unable to parse argument %s, usage: --config <CONFIG-FILE>" CLR_STR "\n", arg);
config_unload();
return 1;
}
webroot_base = argv[++i];
} else if (strcmp(arg, "-c") == 0 || strcmp(arg, "--cert") == 0) {
if (i == argc - 1) {
fprintf(stderr, ERR_STR "Unable to parse argument %s, usage: --cert <CERT-FILE>" CLR_STR "\n", arg);
return 1;
}
cert_file = argv[++i];
} else if (strcmp(arg, "-p") == 0 || strcmp(arg, "--privkey") == 0) {
if (i == argc - 1) {
fprintf(stderr, ERR_STR "Unable to parse argument %s, usage: --privkey <KEY-FILE>" CLR_STR "\n", arg);
return 1;
}
key_file = argv[++i];
} else if (strcmp(arg, "-g") == 0 || strcmp(arg, "--geoip") == 0) {
if (i == argc - 1) {
fprintf(stderr, ERR_STR "Unable to parse argument %s, usage: --geoip <DB-DIR>" CLR_STR "\n", arg);
return 1;
}
geoip_dir = argv[++i];
} else if (strcmp(arg, "-d") == 0 || strcmp(arg, "--dns") == 0) {
if (i == argc - 1) {
fprintf(stderr, ERR_STR "Unable to parse argument %s, usage: --dns <DNS-SERVER>" CLR_STR "\n", arg);
return 1;
}
dns_server = argv[++i];
config_file = argv[++i];
} else {
fprintf(stderr, ERR_STR "Unable to parse argument '%s'" CLR_STR "\n", arg);
config_unload();
return 1;
}
}
if (webroot_base == NULL) {
fprintf(stderr, ERR_STR "Error: --webroot is missing" CLR_STR "\n");
return 1;
}
if (cert_file == NULL) {
fprintf(stderr, ERR_STR "Error: --cert is missing" CLR_STR "\n");
return 1;
}
if (key_file == NULL) {
fprintf(stderr, ERR_STR "Error: --privkey is missing" CLR_STR "\n");
ret = config_load(config_file == NULL ? DEFAULT_CONFIG_FILE : config_file);
if (ret != 0) {
config_unload();
return 1;
}
printf("%s %s\n", cert_file, key_file);
sockets[0] = socket(AF_INET6, SOCK_STREAM, 0);
if (sockets[0] < 0) goto socket_err;
@ -259,12 +236,14 @@ int main(int argc, const char *argv[]) {
if (sockets[1] < 0) {
socket_err:
fprintf(stderr, ERR_STR "Unable to create socket: %s" CLR_STR "\n", strerror(errno));
config_unload();
return 1;
}
for (int i = 0; i < NUM_SOCKETS; i++) {
if (setsockopt(sockets[i], SOL_SOCKET, SO_REUSEADDR, &YES, sizeof(YES)) < 0) {
fprintf(stderr, ERR_STR "Unable to set options for socket %i: %s" CLR_STR "\n", i, strerror(errno));
config_unload();
return 1;
}
}
@ -273,16 +252,18 @@ int main(int argc, const char *argv[]) {
if (bind(sockets[1], (struct sockaddr *) &addresses[1], sizeof(addresses[1])) < 0) {
bind_err:
fprintf(stderr, ERR_STR "Unable to bind socket to address: %s" CLR_STR "\n", strerror(errno));
config_unload();
return 1;
}
signal(SIGINT, terminate);
signal(SIGTERM, terminate);
if (geoip_dir != NULL) {
if (geoip_dir[0] != 0) {
DIR *geoip = opendir(geoip_dir);
if (geoip == NULL) {
fprintf(stderr, ERR_STR "Unable to open GeoIP dir: %s" CLR_STR "\n", strerror(errno));
config_unload();
return 1;
}
struct dirent *dir;
@ -291,18 +272,21 @@ int main(int argc, const char *argv[]) {
if (strcmp(dir->d_name + strlen(dir->d_name) - 5, ".mmdb") != 0) continue;
if (i >= MAX_MMDB) {
fprintf(stderr, ERR_STR "Too many .mmdb files" CLR_STR "\n");
config_unload();
return 1;
}
sprintf(buf, "%s/%s", geoip_dir, dir->d_name);
ret = MMDB_open(buf, 0, &mmdbs[i]);
if (ret != MMDB_SUCCESS) {
fprintf(stderr, ERR_STR "Unable to open .mmdb file: %s" CLR_STR "\n", MMDB_strerror(ret));
config_unload();
return 1;
}
i++;
}
if (i == 0) {
fprintf(stderr, ERR_STR "No .mmdb files found in %s" CLR_STR "\n", geoip_dir);
config_unload();
return 1;
}
closedir(geoip);
@ -324,17 +308,20 @@ int main(int argc, const char *argv[]) {
if (SSL_CTX_use_certificate_chain_file(client.ctx, cert_file) != 1) {
fprintf(stderr, ERR_STR "Unable to load certificate chain file: %s: %s" CLR_STR "\n",
ERR_reason_error_string(ERR_get_error()), cert_file);
config_unload();
return 1;
}
if (SSL_CTX_use_PrivateKey_file(client.ctx, key_file, SSL_FILETYPE_PEM) != 1) {
fprintf(stderr, ERR_STR "Unable to load private key file: %s: %s" CLR_STR "\n",
ERR_reason_error_string(ERR_get_error()), key_file);
config_unload();
return 1;
}
for (int i = 0; i < NUM_SOCKETS; i++) {
if (listen(sockets[i], LISTEN_BACKLOG) < 0) {
fprintf(stderr, ERR_STR "Unable to listen on socket %i: %s" CLR_STR "\n", i, strerror(errno));
config_unload();
return 1;
}
}
@ -349,6 +336,7 @@ int main(int argc, const char *argv[]) {
ret = cache_init();
if (ret < 0) {
config_unload();
return 1;
} else if (ret != 0) {
return 0;
@ -363,6 +351,7 @@ int main(int argc, const char *argv[]) {
ready_sockets_num = select(max_socket_fd + 1, &read_socket_fds, NULL, NULL, &timeout);
if (ready_sockets_num < 0) {
fprintf(stderr, ERR_STR "Unable to select sockets: %s" CLR_STR "\n", strerror(errno));
terminate();
return 1;
}

View File

@ -32,6 +32,7 @@
#define NUM_SOCKETS 2
#define MAX_CHILDREN 1024
#define MAX_MMDB 3
#define MAX_HOST_CONFIG 64
#define LISTEN_BACKLOG 16
#define REQ_PER_CONNECTION 100
#define CLIENT_TIMEOUT 3600
@ -70,13 +71,14 @@
#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
int sockets[NUM_SOCKETS];
pid_t children[MAX_CHILDREN];
MMDB_s mmdbs[MAX_MMDB];
const char *cert_file, *key_file, *webroot_base, *geoip_dir, *dns_server;
typedef struct {
unsigned int enc:1;
int socket;