1325 lines
38 KiB
C
1325 lines
38 KiB
C
/**
|
|
* configuration.c
|
|
*
|
|
* Author: Brane F. Gracnar
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <stdarg.h>
|
|
#include <stdlib.h>
|
|
#include <ctype.h>
|
|
#include <unistd.h>
|
|
#include <getopt.h>
|
|
#include <libgen.h>
|
|
#include <sys/types.h>
|
|
#include <pwd.h>
|
|
#include <grp.h>
|
|
#include <sys/stat.h>
|
|
#include <syslog.h>
|
|
|
|
#include "configuration.h"
|
|
#include "version.h"
|
|
|
|
#define ADDR_LEN 150
|
|
#define PORT_LEN 6
|
|
#define CFG_BOOL_ON "on"
|
|
|
|
// BEGIN: configuration parameters
|
|
#define CFG_CIPHERS "ciphers"
|
|
#define CFG_SSL_ENGINE "ssl-engine"
|
|
#define CFG_PREFER_SERVER_CIPHERS "prefer-server-ciphers"
|
|
#define CFG_BACKEND "backend"
|
|
#define CFG_FRONTEND "frontend"
|
|
#define CFG_WORKERS "workers"
|
|
#define CFG_BACKLOG "backlog"
|
|
#define CFG_KEEPALIVE "keepalive"
|
|
#define CFG_CHROOT "chroot"
|
|
#define CFG_USER "user"
|
|
#define CFG_GROUP "group"
|
|
#define CFG_QUIET "quiet"
|
|
#define CFG_SYSLOG "syslog"
|
|
#define CFG_SYSLOG_FACILITY "syslog-facility"
|
|
#define CFG_PARAM_SYSLOG_FACILITY 11015
|
|
#define CFG_DAEMON "daemon"
|
|
#define CFG_WRITE_IP "write-ip"
|
|
#define CFG_WRITE_PROXY "write-proxy"
|
|
#define CFG_PEM_FILE "pem-file"
|
|
#define CFG_PROXY_PROXY "proxy-proxy"
|
|
|
|
#ifdef USE_SHARED_CACHE
|
|
#define CFG_SHARED_CACHE "shared-cache"
|
|
#define CFG_SHARED_CACHE_LISTEN "shared-cache-listen"
|
|
#define CFG_SHARED_CACHE_PEER "shared-cache-peer"
|
|
#define CFG_SHARED_CACHE_MCASTIF "shared-cache-if"
|
|
#endif
|
|
|
|
#ifndef NO_CONFIG_FILE
|
|
#define FMT_STR "%s = %s\n"
|
|
#define FMT_QSTR "%s = \"%s\"\n"
|
|
#define FMT_ISTR "%s = %d\n"
|
|
|
|
#define CONFIG_MAX_LINES 10000
|
|
#define CONFIG_BUF_SIZE 1024
|
|
#define CFG_PARAM_CFGFILE 10000
|
|
#define CFG_PARAM_DEFCFG 10001
|
|
|
|
#define CFG_CONFIG "config"
|
|
#define CFG_CONFIG_DEFAULT "default-config"
|
|
#endif
|
|
// END: configuration parameters
|
|
|
|
static char var_buf[CONFIG_BUF_SIZE];
|
|
static char val_buf[CONFIG_BUF_SIZE];
|
|
static char error_buf[CONFIG_BUF_SIZE];
|
|
static char tmp_buf[150];
|
|
|
|
// for testing configuration only
|
|
#include <openssl/ssl.h>
|
|
SSL_CTX * init_openssl();
|
|
|
|
static void config_error_set (char *fmt, ...) {
|
|
memset(error_buf, '\0', sizeof(error_buf));
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
vsnprintf(error_buf, (sizeof(error_buf) - 1), fmt, args);
|
|
va_end(args);
|
|
}
|
|
|
|
char * config_error_get (void) {
|
|
return error_buf;
|
|
}
|
|
|
|
void config_die (char *fmt, ...) {
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
vfprintf(stderr, fmt, args);
|
|
va_end(args);
|
|
fprintf(stderr, "\n");
|
|
|
|
exit(1);
|
|
}
|
|
|
|
stud_config * config_new (void) {
|
|
stud_config *r = NULL;
|
|
r = malloc(sizeof(stud_config));
|
|
if (r == NULL) {
|
|
config_error_set("Unable to allocate memory for configuration structure: %s", strerror(errno));
|
|
return NULL;
|
|
}
|
|
|
|
// set default values
|
|
|
|
r->ETYPE = ENC_TLS;
|
|
r->PMODE = SSL_SERVER;
|
|
r->WRITE_IP_OCTET = 0;
|
|
r->WRITE_PROXY_LINE = 0;
|
|
r->PROXY_PROXY_LINE = 0;
|
|
r->CHROOT = NULL;
|
|
r->UID = 0;
|
|
r->GID = 0;
|
|
r->FRONT_IP = NULL;
|
|
r->FRONT_PORT = strdup("1119");
|
|
r->BACK_IP = strdup("127.0.0.1");
|
|
r->BACK_PORT = strdup("45678");
|
|
r->NCORES = 1;
|
|
r->CERT_FILES = NULL;
|
|
r->CIPHER_SUITE = NULL;
|
|
r->ENGINE = NULL;
|
|
r->BACKLOG = 100;
|
|
|
|
#ifdef USE_SHARED_CACHE
|
|
r->SHARED_CACHE = 0;
|
|
r->SHCUPD_IP = NULL;
|
|
r->SHCUPD_PORT = NULL;
|
|
|
|
for (int i = 0 ; i < MAX_SHCUPD_PEERS; i++)
|
|
memset(&r->SHCUPD_PEERS[i], 0, sizeof(shcupd_peer_opt));
|
|
|
|
r->SHCUPD_MCASTIF = NULL;
|
|
r->SHCUPD_MCASTTTL = NULL;
|
|
#endif
|
|
|
|
r->QUIET = 0;
|
|
r->SYSLOG = 0;
|
|
r->SYSLOG_FACILITY = LOG_DAEMON;
|
|
r->TCP_KEEPALIVE_TIME = 3600;
|
|
r->DAEMONIZE = 1;
|
|
r->PREFER_SERVER_CIPHERS = 0;
|
|
|
|
return r;
|
|
}
|
|
|
|
void config_destroy (stud_config *cfg) {
|
|
// printf("config_destroy() in pid %d: %p\n", getpid(), cfg);
|
|
if (cfg == NULL) return;
|
|
|
|
// free all members!
|
|
if (cfg->CHROOT != NULL) free(cfg->CHROOT);
|
|
if (cfg->FRONT_IP != NULL) free(cfg->FRONT_IP);
|
|
if (cfg->FRONT_PORT != NULL) free(cfg->FRONT_PORT);
|
|
if (cfg->BACK_IP != NULL) free(cfg->BACK_IP);
|
|
if (cfg->BACK_PORT != NULL) free(cfg->BACK_PORT);
|
|
if (cfg->CERT_FILES != NULL) {
|
|
struct cert_files *curr = cfg->CERT_FILES, *next;
|
|
while (cfg->CERT_FILES != NULL) {
|
|
next = curr->NEXT;
|
|
free(curr);
|
|
curr = next;
|
|
}
|
|
}
|
|
if (cfg->CIPHER_SUITE != NULL) free(cfg->CIPHER_SUITE);
|
|
if (cfg->ENGINE != NULL) free(cfg->ENGINE);
|
|
|
|
#ifdef USE_SHARED_CACHE
|
|
if (cfg->SHCUPD_IP != NULL) free(cfg->SHCUPD_IP);
|
|
if (cfg->SHCUPD_PORT != NULL) free(cfg->SHCUPD_PORT);
|
|
|
|
for (int i = 0; i < MAX_SHCUPD_PEERS; i++) {
|
|
if (cfg->SHCUPD_PEERS[i].ip != NULL)
|
|
free(cfg->SHCUPD_PEERS[i].ip);
|
|
if (cfg->SHCUPD_PEERS[i].port != NULL)
|
|
free(cfg->SHCUPD_PEERS[i].port);
|
|
}
|
|
|
|
if (cfg->SHCUPD_MCASTIF != NULL) free(cfg->SHCUPD_MCASTIF);
|
|
if (cfg->SHCUPD_MCASTTTL != NULL) free(cfg->SHCUPD_MCASTTTL);
|
|
#endif
|
|
|
|
free(cfg);
|
|
}
|
|
|
|
char * config_get_param (char *str) {
|
|
char *ptr;
|
|
int i;
|
|
|
|
if (str == NULL) return NULL;
|
|
/** empty string? */
|
|
if (strlen(str) < 1 || str[0] == '\n' || strcmp(str, "\r\n") == 0) return NULL;
|
|
|
|
ptr = str;
|
|
|
|
/** comments? */
|
|
if (str[0] == '#') return NULL;
|
|
/** first alpha character */
|
|
while (ptr != NULL && ! isalpha(*ptr))
|
|
ptr++;
|
|
|
|
/** overwrite alpha chars */
|
|
memset(var_buf, '\0', sizeof(var_buf));
|
|
i = 0;
|
|
while(ptr != NULL && (isalnum(*ptr) || *ptr == '-')) {
|
|
var_buf[i] = *ptr;
|
|
i++;
|
|
ptr++;
|
|
}
|
|
|
|
if (strlen(var_buf) < 1) return NULL;
|
|
return var_buf;
|
|
}
|
|
|
|
char * config_get_value (char *str) {
|
|
char *ptr;
|
|
int i = 0;
|
|
|
|
if (str == NULL) return NULL;
|
|
if (strlen(str) < 1) return NULL;
|
|
|
|
/** find '=' char */
|
|
ptr = str;
|
|
while (ptr != NULL && (*ptr) != '=')
|
|
ptr++;
|
|
ptr++;
|
|
|
|
/** skip whitespaces **/
|
|
while (ptr != NULL && ! isgraph(*ptr))
|
|
ptr++;
|
|
|
|
/** no value found? */
|
|
if (ptr == NULL) return NULL;
|
|
|
|
/** overwrite alpha chars */
|
|
memset(val_buf, '\0', sizeof(val_buf));
|
|
while(ptr != NULL && isgraph(*ptr)) {
|
|
val_buf[i++] = *ptr;
|
|
ptr++;
|
|
}
|
|
|
|
if (strlen(val_buf) < 1) return NULL;
|
|
return val_buf;
|
|
}
|
|
|
|
char * str_rtrim(char *str) {
|
|
char *ptr;
|
|
int len;
|
|
|
|
len = strlen(str);
|
|
ptr = str + len - 1;
|
|
while (ptr >= str && (isspace((int)*ptr ) || (char) *ptr == '"' || (char) *ptr == '\'')) --ptr;
|
|
|
|
ptr[1] = '\0';
|
|
|
|
return str;
|
|
}
|
|
|
|
char * str_ltrim(char *str) {
|
|
char *ptr;
|
|
int len;
|
|
|
|
for (ptr = str; (*ptr && (isspace((int)*ptr) || (char) *ptr == '"' || (char) *ptr == '\'')); ++ptr);
|
|
|
|
len = strlen(ptr);
|
|
memmove(str, ptr, len + 1);
|
|
|
|
return str;
|
|
}
|
|
|
|
char * str_trim(char *str) {
|
|
char *ptr;
|
|
ptr = str_rtrim(str);
|
|
str = str_ltrim(ptr);
|
|
return str;
|
|
}
|
|
|
|
char * config_assign_str (char **dst, char *v) {
|
|
if (*dst == NULL) {
|
|
if (v != NULL && strlen(v) > 0)
|
|
*dst = strdup(v);
|
|
} else {
|
|
if (v != NULL && strlen(v) > 0) {
|
|
// we assume that there is enough room for v in *dst
|
|
memset(*dst, '\0', strlen(v) + 1);
|
|
memcpy(*dst, v, strlen(v));
|
|
}
|
|
else
|
|
free(*dst);
|
|
}
|
|
return *dst;
|
|
}
|
|
|
|
int config_param_val_bool (char *val, int *res) {
|
|
if (val == NULL) return 0;
|
|
if (
|
|
strcasecmp(val, CFG_BOOL_ON) == 0 ||
|
|
strcasecmp(val, "yes") == 0 ||
|
|
strcasecmp(val, "y") == 0 ||
|
|
strcasecmp(val, "true") == 0 ||
|
|
strcasecmp(val, "t") == 0 ||
|
|
strcasecmp(val, "1") == 0) {
|
|
*res = 1;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
char * config_param_val_str (char *val) {
|
|
return strdup(val);
|
|
}
|
|
|
|
int config_param_host_port_wildcard (char *str, char **addr, char **port, int wildcard_okay) {
|
|
int len = (str != NULL) ? strlen(str) : 0;
|
|
if (str == NULL || ! len) {
|
|
config_error_set("Invalid/unset host/port string.");
|
|
return 0;
|
|
}
|
|
|
|
// address/port buffers
|
|
char port_buf[PORT_LEN];
|
|
char addr_buf[ADDR_LEN];
|
|
|
|
memset(port_buf, '\0', sizeof(port_buf));
|
|
memset(addr_buf, '\0', sizeof(addr_buf));
|
|
|
|
// NEW FORMAT: [address]:port
|
|
if (*str == '[') {
|
|
char *ptr = str + 1;
|
|
char *x = strrchr(ptr, ']');
|
|
if (x == NULL) {
|
|
config_error_set("Invalid address '%s'.", str);
|
|
return 0;
|
|
}
|
|
|
|
// address
|
|
memcpy(addr_buf, ptr, (x - ptr));
|
|
|
|
// port
|
|
x += 2;
|
|
memcpy(port_buf, x, sizeof(port_buf) - 1);
|
|
}
|
|
// OLD FORMAT: address,port
|
|
else {
|
|
char *x = strrchr(str, ',');
|
|
if (x == NULL) {
|
|
config_error_set("Invalid address string '%s'", str);
|
|
return 0;
|
|
}
|
|
// addr
|
|
int addr_len = x - str;
|
|
memcpy(addr_buf, str, addr_len);
|
|
// port
|
|
memcpy(port_buf, (++x), sizeof(port_buf));
|
|
}
|
|
|
|
// printf("PARSED ADDR '%s', PORT '%s'\n", addr_buf, port_buf);
|
|
|
|
// check port
|
|
int p = atoi(port_buf);
|
|
if (p < 1 || p > 65536) {
|
|
config_error_set("Invalid port number '%s'", port_buf);
|
|
return 0;
|
|
}
|
|
|
|
// write
|
|
if (strcmp(addr_buf, "*") == 0) {
|
|
if (wildcard_okay)
|
|
free(*addr);
|
|
else {
|
|
config_error_set("Invalid address: wildcards are not allowed.");
|
|
return 0;
|
|
}
|
|
} else {
|
|
//if (*addr != NULL) free(*addr);
|
|
*addr = strdup(addr_buf);
|
|
}
|
|
// if (**port != NULL) free(*port);
|
|
*port = strdup(port_buf);
|
|
|
|
// printf("ADDR FINAL: '%s', '%s'\n", *addr, *port);
|
|
|
|
return 1;
|
|
}
|
|
|
|
int config_param_host_port (char *str, char **addr, char **port) {
|
|
return config_param_host_port_wildcard(str, addr, port, 0);
|
|
}
|
|
|
|
int config_param_val_int (char *str, int *dst) {
|
|
*dst = (str != NULL) ? atoi(str) : 0;
|
|
return 1;
|
|
}
|
|
|
|
int config_param_val_int_pos (char *str, int *dst) {
|
|
int num = 0;
|
|
if (str != NULL)
|
|
num = atoi(str);
|
|
|
|
if (num < 1) {
|
|
config_error_set("Not a positive number.");
|
|
return 0;
|
|
}
|
|
|
|
*dst = num;
|
|
return 1;
|
|
}
|
|
|
|
int config_param_val_intl (char *str, long int *dst) {
|
|
*dst = (str != NULL) ? atol(str) : 0;
|
|
return 1;
|
|
}
|
|
|
|
int config_param_val_intl_pos (char *str, long int *dst) {
|
|
long int num = 0;
|
|
if (str != NULL)
|
|
num = atol(str);
|
|
|
|
if (num < 1) {
|
|
config_error_set("Not a positive number.");
|
|
return 0;
|
|
}
|
|
|
|
*dst = num;
|
|
return 1;
|
|
}
|
|
|
|
#ifdef USE_SHARED_CACHE
|
|
/* Parse mcast and ttl options */
|
|
int config_param_shcupd_mcastif (char *str, char **iface, char **ttl) {
|
|
char buf[150];
|
|
char *sp;
|
|
|
|
if (strlen(str) >= sizeof buf) {
|
|
config_error_set("Invalid option for IFACE[,TTL]");
|
|
return 0;
|
|
}
|
|
|
|
sp = strchr(str, ',');
|
|
if (!sp) {
|
|
if (!strcmp(str, "*"))
|
|
*iface = NULL;
|
|
else
|
|
*iface = str;
|
|
*ttl = NULL;
|
|
return 1;
|
|
}
|
|
else if (!strncmp(str, "*", sp - str)) {
|
|
*iface = NULL;
|
|
}
|
|
else {
|
|
*sp = 0;
|
|
*iface = str;
|
|
}
|
|
*ttl = sp + 1;
|
|
|
|
return 1;
|
|
}
|
|
|
|
int config_param_shcupd_peer (char *str, stud_config *cfg) {
|
|
if (cfg == NULL) {
|
|
config_error_set("Configuration pointer is NULL.");
|
|
return 0;
|
|
}
|
|
|
|
// parse result
|
|
int r = 1;
|
|
|
|
// find place for new peer
|
|
int offset = 0;
|
|
int i = 0;
|
|
for (i = 0; i < MAX_SHCUPD_PEERS; i++) {
|
|
if (cfg->SHCUPD_PEERS[i].ip == NULL && cfg->SHCUPD_PEERS[i].port == NULL) {
|
|
offset = i;
|
|
break;
|
|
}
|
|
}
|
|
if (i >= MAX_SHCUPD_PEERS) {
|
|
config_error_set(
|
|
"Reached maximum number of shared cache update peers (%d).",
|
|
MAX_SHCUPD_PEERS
|
|
);
|
|
return 0;
|
|
}
|
|
|
|
// create place for new peer
|
|
char *addr = malloc(ADDR_LEN);
|
|
if (addr == NULL) {
|
|
config_error_set(
|
|
"Unable to allocate memory for new shared cache update peer address: %s",
|
|
strerror(errno)
|
|
);
|
|
r = 0;
|
|
goto outta_parse_peer;
|
|
}
|
|
memset(addr, '\0', ADDR_LEN);
|
|
char *port = malloc(PORT_LEN);
|
|
if (port == NULL) {
|
|
config_error_set(
|
|
"Unable to allocate memory for new shared cache update peer port: %s",
|
|
strerror(errno)
|
|
);
|
|
r = 0;
|
|
goto outta_parse_peer;
|
|
}
|
|
memset(port, '\0', PORT_LEN);
|
|
|
|
// try to parse address
|
|
if (! config_param_host_port(str, &addr, &port)) {
|
|
r = 0;
|
|
goto outta_parse_peer;
|
|
}
|
|
|
|
outta_parse_peer:
|
|
|
|
if (! r) {
|
|
if (addr != NULL) free(addr);
|
|
if (port != NULL) free(port);
|
|
} else {
|
|
cfg->SHCUPD_PEERS[offset].ip = addr;
|
|
cfg->SHCUPD_PEERS[offset].port = port;
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
#endif /* USE_SHARED_CACHE */
|
|
|
|
void config_param_validate (char *k, char *v, stud_config *cfg, char *file, int line) {
|
|
int r = 1;
|
|
struct stat st;
|
|
|
|
if (strcmp(k, "tls") == 0) {
|
|
cfg->ETYPE = ENC_TLS;
|
|
}
|
|
else if (strcmp(k, "ssl") == 0) {
|
|
cfg->ETYPE = ENC_SSL;
|
|
}
|
|
else if (strcmp(k, CFG_CIPHERS) == 0) {
|
|
if (v != NULL && strlen(v) > 0) {
|
|
config_assign_str(&cfg->CIPHER_SUITE, v);
|
|
}
|
|
}
|
|
else if (strcmp(k, CFG_SSL_ENGINE) == 0) {
|
|
if (v != NULL && strlen(v) > 0) {
|
|
config_assign_str(&cfg->ENGINE, v);
|
|
}
|
|
}
|
|
else if (strcmp(k, CFG_PREFER_SERVER_CIPHERS) == 0) {
|
|
r = config_param_val_bool(v, &cfg->PREFER_SERVER_CIPHERS);
|
|
}
|
|
else if (strcmp(k, CFG_FRONTEND) == 0) {
|
|
r = config_param_host_port_wildcard(v, &cfg->FRONT_IP, &cfg->FRONT_PORT, 1);
|
|
}
|
|
else if (strcmp(k, CFG_BACKEND) == 0) {
|
|
r = config_param_host_port(v, &cfg->BACK_IP, &cfg->BACK_PORT);
|
|
}
|
|
else if (strcmp(k, CFG_WORKERS) == 0) {
|
|
r = config_param_val_intl_pos(v, &cfg->NCORES);
|
|
}
|
|
else if (strcmp(k, CFG_BACKLOG) == 0) {
|
|
r = config_param_val_int(v, &cfg->BACKLOG);
|
|
if (r && cfg->BACKLOG < -1) cfg->BACKLOG = -1;
|
|
}
|
|
else if (strcmp(k, CFG_KEEPALIVE) == 0) {
|
|
r = config_param_val_int_pos(v, &cfg->TCP_KEEPALIVE_TIME);
|
|
}
|
|
#ifdef USE_SHARED_CACHE
|
|
else if (strcmp(k, CFG_SHARED_CACHE) == 0) {
|
|
r = config_param_val_int(v, &cfg->SHARED_CACHE);
|
|
}
|
|
else if (strcmp(k, CFG_SHARED_CACHE_LISTEN) == 0) {
|
|
if (v != NULL && strlen(v) > 0)
|
|
r = config_param_host_port_wildcard(v, &cfg->SHCUPD_IP, &cfg->SHCUPD_PORT, 1);
|
|
}
|
|
else if (strcmp(k, CFG_SHARED_CACHE_PEER) == 0) {
|
|
r = config_param_shcupd_peer(v, cfg);
|
|
}
|
|
else if (strcmp(k, CFG_SHARED_CACHE_MCASTIF) == 0) {
|
|
r = config_param_shcupd_mcastif(v, &cfg->SHCUPD_MCASTIF, &cfg->SHCUPD_MCASTTTL);
|
|
}
|
|
#endif
|
|
else if (strcmp(k, CFG_CHROOT) == 0) {
|
|
if (v != NULL && strlen(v) > 0) {
|
|
// check directory
|
|
if (stat(v, &st) != 0) {
|
|
config_error_set("Unable to stat directory '%s': %s'.", v, strerror(errno));
|
|
r = 0;
|
|
} else {
|
|
if (! S_ISDIR(st.st_mode)) {
|
|
config_error_set("Bad chroot directory '%s': Not a directory.", v, strerror(errno));
|
|
r = 0;
|
|
} else {
|
|
config_assign_str(&cfg->CHROOT, v);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (strcmp(k, CFG_USER) == 0) {
|
|
if (v != NULL && strlen(v) > 0) {
|
|
struct passwd *passwd;
|
|
passwd = getpwnam(v);
|
|
if (!passwd) {
|
|
config_error_set("Invalid user '%s'.", v);
|
|
r = 0;
|
|
} else {
|
|
cfg->UID = passwd->pw_uid;
|
|
cfg->GID = passwd->pw_gid;
|
|
}
|
|
}
|
|
}
|
|
else if (strcmp(k, CFG_GROUP) == 0) {
|
|
if (v != NULL && strlen(v) > 0) {
|
|
struct group *grp;
|
|
grp = getgrnam(v);
|
|
if (!grp) {
|
|
config_error_set("Invalid group '%s'.", v);
|
|
r = 0;
|
|
} else {
|
|
cfg->GID = grp->gr_gid;
|
|
}
|
|
}
|
|
}
|
|
else if (strcmp(k, CFG_QUIET) == 0) {
|
|
r = config_param_val_bool(v, &cfg->QUIET);
|
|
}
|
|
else if (strcmp(k, CFG_SYSLOG) == 0) {
|
|
r = config_param_val_bool(v, &cfg->SYSLOG);
|
|
}
|
|
else if (strcmp(k, CFG_SYSLOG_FACILITY) == 0) {
|
|
r = 1;
|
|
if (!strcmp(v, "auth") || !strcmp(v, "authpriv"))
|
|
cfg->SYSLOG_FACILITY = LOG_AUTHPRIV;
|
|
else if (!strcmp(v, "cron"))
|
|
cfg->SYSLOG_FACILITY = LOG_CRON;
|
|
else if (!strcmp(v, "daemon"))
|
|
cfg->SYSLOG_FACILITY = LOG_DAEMON;
|
|
else if (!strcmp(v, "ftp"))
|
|
cfg->SYSLOG_FACILITY = LOG_FTP;
|
|
else if (!strcmp(v, "local0"))
|
|
cfg->SYSLOG_FACILITY = LOG_LOCAL0;
|
|
else if (!strcmp(v, "local1"))
|
|
cfg->SYSLOG_FACILITY = LOG_LOCAL1;
|
|
else if (!strcmp(v, "local2"))
|
|
cfg->SYSLOG_FACILITY = LOG_LOCAL2;
|
|
else if (!strcmp(v, "local3"))
|
|
cfg->SYSLOG_FACILITY = LOG_LOCAL3;
|
|
else if (!strcmp(v, "local4"))
|
|
cfg->SYSLOG_FACILITY = LOG_LOCAL4;
|
|
else if (!strcmp(v, "local5"))
|
|
cfg->SYSLOG_FACILITY = LOG_LOCAL5;
|
|
else if (!strcmp(v, "local6"))
|
|
cfg->SYSLOG_FACILITY = LOG_LOCAL6;
|
|
else if (!strcmp(v, "local7"))
|
|
cfg->SYSLOG_FACILITY = LOG_LOCAL7;
|
|
else if (!strcmp(v, "lpr"))
|
|
cfg->SYSLOG_FACILITY = LOG_LPR;
|
|
else if (!strcmp(v, "mail"))
|
|
cfg->SYSLOG_FACILITY = LOG_MAIL;
|
|
else if (!strcmp(v, "news"))
|
|
cfg->SYSLOG_FACILITY = LOG_NEWS;
|
|
else if (!strcmp(v, "user"))
|
|
cfg->SYSLOG_FACILITY = LOG_USER;
|
|
else if (!strcmp(v, "uucp"))
|
|
cfg->SYSLOG_FACILITY = LOG_UUCP;
|
|
else {
|
|
config_error_set("Invalid facility '%s'.", v);
|
|
r = 0;
|
|
}
|
|
}
|
|
else if (strcmp(k, CFG_DAEMON) == 0) {
|
|
r = config_param_val_bool(v, &cfg->DAEMONIZE);
|
|
}
|
|
else if (strcmp(k, CFG_WRITE_IP) == 0) {
|
|
r = config_param_val_bool(v, &cfg->WRITE_IP_OCTET);
|
|
}
|
|
else if (strcmp(k, CFG_WRITE_PROXY) == 0) {
|
|
r = config_param_val_bool(v, &cfg->WRITE_PROXY_LINE);
|
|
}
|
|
else if (strcmp(k, CFG_PROXY_PROXY) == 0) {
|
|
r = config_param_val_bool(v, &cfg->PROXY_PROXY_LINE);
|
|
}
|
|
else if (strcmp(k, CFG_PEM_FILE) == 0) {
|
|
if (v != NULL && strlen(v) > 0) {
|
|
if (stat(v, &st) != 0) {
|
|
config_error_set("Unable to stat x509 certificate PEM file '%s': ", v, strerror(errno));
|
|
r = 0;
|
|
}
|
|
else if (! S_ISREG(st.st_mode)) {
|
|
config_error_set("Invalid x509 certificate PEM file '%s': Not a file.", v);
|
|
r = 0;
|
|
} else {
|
|
struct cert_files *cert = calloc(1, sizeof(*cert));
|
|
config_assign_str(&cert->CERT_FILE, v);
|
|
cert->NEXT = cfg->CERT_FILES;
|
|
cfg->CERT_FILES = cert;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
fprintf(
|
|
stderr,
|
|
"Ignoring unknown configuration key '%s' in configuration file '%s', line %d\n",
|
|
k, file, line
|
|
);
|
|
}
|
|
|
|
if (! r) {
|
|
if (file != NULL)
|
|
config_die("Error in configuration file '%s', line %d: %s\n", file, line, config_error_get());
|
|
else
|
|
config_die("Invalid parameter '%s': %s", k, config_error_get());
|
|
}
|
|
}
|
|
|
|
#ifndef NO_CONFIG_FILE
|
|
int config_file_parse (char *file, stud_config *cfg) {
|
|
if (cfg == NULL)
|
|
config_die("Undefined stud options; THIS IS A BUG!\n");
|
|
|
|
char line[CONFIG_BUF_SIZE];
|
|
FILE *fd = NULL;
|
|
|
|
// should we read stdin?
|
|
if (file == NULL || strlen(file) < 1 || strcmp(file, "-") == 0) {
|
|
fd = stdin;
|
|
} else {
|
|
fd = fopen(file, "r");
|
|
}
|
|
if (fd == NULL)
|
|
config_die("Unable to open configuration file '%s': %s\n", file, strerror(errno));
|
|
|
|
// read config
|
|
int i = 0;
|
|
while (i < CONFIG_MAX_LINES) {
|
|
memset(line, '\0', sizeof(line));
|
|
if (fgets(line, (sizeof(line) - 1), fd) == NULL) break;
|
|
i++;
|
|
|
|
// get configuration key
|
|
char *key, *val;
|
|
key = config_get_param(line);
|
|
if (key == NULL) continue;
|
|
|
|
// get configuration key value...
|
|
val = config_get_value(line);
|
|
if (val == NULL) continue;
|
|
str_trim(val);
|
|
|
|
// printf("File '%s', line %d, key: '%s', value: '%s'\n", file, i, key, val);
|
|
|
|
// validate configuration key => value
|
|
config_param_validate(key, val, cfg, file, i);
|
|
}
|
|
|
|
fclose(fd);
|
|
|
|
return 1;
|
|
}
|
|
#endif /* NO_CONFIG_FILE */
|
|
|
|
char * config_disp_str (char *str) {
|
|
return (str == NULL) ? "" : str;
|
|
}
|
|
|
|
char * config_disp_bool (int v) {
|
|
return (v > 0) ? CFG_BOOL_ON : "off";
|
|
}
|
|
|
|
char * config_disp_uid (uid_t uid) {
|
|
memset(tmp_buf, '\0', sizeof(tmp_buf));
|
|
if (uid == 0 && geteuid() != 0) return tmp_buf;
|
|
struct passwd *pw = getpwuid(uid);
|
|
if (pw) {
|
|
memcpy(tmp_buf, pw->pw_name, strlen(pw->pw_name));
|
|
}
|
|
return tmp_buf;
|
|
}
|
|
|
|
char * config_disp_gid (gid_t gid) {
|
|
memset(tmp_buf, '\0', sizeof(tmp_buf));
|
|
if (gid == 0 && geteuid() != 0) return tmp_buf;
|
|
struct group *gr = getgrgid(gid);
|
|
if (gr) {
|
|
memcpy(tmp_buf, gr->gr_name, strlen(gr->gr_name));
|
|
}
|
|
return tmp_buf;
|
|
}
|
|
|
|
char * config_disp_hostport (char *host, char *port) {
|
|
memset(tmp_buf, '\0', sizeof(tmp_buf));
|
|
if (host == NULL && port == NULL)
|
|
return "";
|
|
|
|
strcat(tmp_buf, "[");
|
|
if (host == NULL)
|
|
strcat(tmp_buf, "*");
|
|
else {
|
|
strncat(tmp_buf, host, 40);
|
|
}
|
|
strcat(tmp_buf, "]:");
|
|
strncat(tmp_buf, port, 5);
|
|
return tmp_buf;
|
|
}
|
|
|
|
const char * config_disp_log_facility (int facility) {
|
|
switch (facility)
|
|
{
|
|
case LOG_AUTHPRIV:
|
|
return "authpriv";
|
|
case LOG_CRON:
|
|
return "cron";
|
|
case LOG_DAEMON:
|
|
return "daemon";
|
|
case LOG_FTP:
|
|
return "ftp";
|
|
case LOG_LOCAL0:
|
|
return "local0";
|
|
case LOG_LOCAL1:
|
|
return "local1";
|
|
case LOG_LOCAL2:
|
|
return "local2";
|
|
case LOG_LOCAL3:
|
|
return "local3";
|
|
case LOG_LOCAL4:
|
|
return "local4";
|
|
case LOG_LOCAL5:
|
|
return "local5";
|
|
case LOG_LOCAL6:
|
|
return "local6";
|
|
case LOG_LOCAL7:
|
|
return "local7";
|
|
case LOG_LPR:
|
|
return "lpr";
|
|
case LOG_MAIL:
|
|
return "mail";
|
|
case LOG_NEWS:
|
|
return "news";
|
|
case LOG_USER:
|
|
return "user";
|
|
case LOG_UUCP:
|
|
return "uucp";
|
|
default:
|
|
return "UNKNOWN";
|
|
}
|
|
}
|
|
|
|
void config_print_usage_fd (char *prog, stud_config *cfg, FILE *out) {
|
|
if (out == NULL) out = stderr;
|
|
fprintf(out, "Usage: %s [OPTIONS] PEM\n\n", basename(prog));
|
|
fprintf(out, "This is stud, The Scalable TLS Unwrapping Daemon.\n\n");
|
|
#ifndef NO_CONFIG_FILE
|
|
fprintf(out, "CONFIGURATION:\n");
|
|
fprintf(out, "\n");
|
|
fprintf(out, " --config=FILE Load configuration from specified file.\n");
|
|
fprintf(out, " --default-config Prints default configuration to stdout.\n");
|
|
fprintf(out, "\n");
|
|
#endif
|
|
fprintf(out, "ENCRYPTION METHODS:\n");
|
|
fprintf(out, "\n");
|
|
fprintf(out, " --tls TLSv1 (default)\n");
|
|
fprintf(out, " --ssl SSLv3 (implies no TLSv1)\n");
|
|
fprintf(out, " -c --ciphers=SUITE Sets allowed ciphers (Default: \"%s\")\n", config_disp_str(cfg->CIPHER_SUITE));
|
|
fprintf(out, " -e --ssl-engine=NAME Sets OpenSSL engine (Default: \"%s\")\n", config_disp_str(cfg->ENGINE));
|
|
fprintf(out, " -O --prefer-server-ciphers Prefer server list order\n");
|
|
fprintf(out, "\n");
|
|
fprintf(out, "SOCKET:\n");
|
|
fprintf(out, "\n");
|
|
fprintf(out, " --client Enable client proxy mode\n");
|
|
fprintf(out, " -b --backend=HOST,PORT Backend [connect] (default is \"%s\")\n", config_disp_hostport(cfg->BACK_IP, cfg->BACK_PORT));
|
|
fprintf(out, " -f --frontend=HOST,PORT Frontend [bind] (default is \"%s\")\n", config_disp_hostport(cfg->FRONT_IP, cfg->FRONT_PORT));
|
|
|
|
#ifdef USE_SHARED_CACHE
|
|
fprintf(out, "\n");
|
|
fprintf(out, " -U --shared-cache-listen=HOST,PORT\n");
|
|
fprintf(out, " Accept cache updates on UDP (Default: \"%s\")\n", config_disp_hostport(cfg->SHCUPD_IP, cfg->SHCUPD_PORT));
|
|
fprintf(out, " NOTE: This option requires enabled SSL session cache.\n");
|
|
fprintf(out, " -P --shared-cache-peer=HOST,PORT\n");
|
|
fprintf(out, " Send cache updates to specified peer\n");
|
|
fprintf(out, " NOTE: This option can be specified multiple times.\n");
|
|
fprintf(out, " -M --shared-cache-if=IFACE[,TTL]\n");
|
|
fprintf(out, " Force iface and ttl to receive and send multicast updates\n");
|
|
#endif
|
|
|
|
fprintf(out, "\n");
|
|
fprintf(out, "PERFORMANCE:\n");
|
|
fprintf(out, "\n");
|
|
fprintf(out, " -n --workers=NUM Number of worker processes (Default: %ld)\n", cfg->NCORES);
|
|
fprintf(out, " -B --backlog=NUM Set listen backlog size (Default: %d)\n", cfg->BACKLOG);
|
|
fprintf(out, " -k --keepalive=SECS TCP keepalive on client socket (Default: %d)\n", cfg->TCP_KEEPALIVE_TIME);
|
|
|
|
#ifdef USE_SHARED_CACHE
|
|
fprintf(out, " -C --session-cache=NUM Enable and set SSL session cache to specified number\n");
|
|
fprintf(out, " of sessions (Default: %d)\n", cfg->SHARED_CACHE);
|
|
#endif
|
|
|
|
fprintf(out, "\n");
|
|
fprintf(out, "SECURITY:\n");
|
|
fprintf(out, "\n");
|
|
fprintf(out, " -r --chroot=DIR Sets chroot directory (Default: \"%s\")\n", config_disp_str(cfg->CHROOT));
|
|
fprintf(out, " -u --user=USER Set uid/gid after binding the socket (Default: \"%s\")\n", config_disp_uid(cfg->UID));
|
|
fprintf(out, " -g --group=GROUP Set gid after binding the socket (Default: \"%s\")\n", config_disp_gid(cfg->GID));
|
|
fprintf(out, "\n");
|
|
fprintf(out, "LOGGING:\n");
|
|
fprintf(out, " -q --quiet Be quiet; emit only error messages\n");
|
|
fprintf(out, " -s --syslog Send log message to syslog in addition to stderr/stdout\n");
|
|
fprintf(out, " --syslog-facility=FACILITY Syslog facility to use (Default: \"%s\")\n", config_disp_log_facility(cfg->SYSLOG_FACILITY));
|
|
fprintf(out, "\n");
|
|
fprintf(out, "OTHER OPTIONS:\n");
|
|
fprintf(out, " --daemon Fork into background and become a daemon (Default: %s)\n", config_disp_bool(cfg->DAEMONIZE));
|
|
fprintf(out, " --write-ip Write 1 octet with the IP family followed by the IP\n");
|
|
fprintf(out, " address in 4 (IPv4) or 16 (IPv6) octets little-endian\n");
|
|
fprintf(out, " to backend before the actual data\n");
|
|
fprintf(out, " (Default: %s)\n", config_disp_bool(cfg->WRITE_IP_OCTET));
|
|
fprintf(out, " --write-proxy Write HaProxy's PROXY (IPv4 or IPv6) protocol line\n" );
|
|
fprintf(out, " before actual data\n");
|
|
fprintf(out, " (Default: %s)\n", config_disp_bool(cfg->WRITE_PROXY_LINE));
|
|
fprintf(out, " --proxy-proxy Proxy HaProxy's PROXY (IPv4 or IPv6) protocol line\n" );
|
|
fprintf(out, " before actual data\n");
|
|
fprintf(out, " (Default: %s)\n", config_disp_bool(cfg->PROXY_PROXY_LINE));
|
|
fprintf(out, "\n");
|
|
fprintf(out, " -t --test Test configuration and exit\n");
|
|
fprintf(out, " -V --version Print program version and exit\n");
|
|
fprintf(out, " -h --help This help message\n");
|
|
}
|
|
|
|
#ifndef NO_CONFIG_FILE
|
|
void config_print_default (FILE *fd, stud_config *cfg) {
|
|
if (fd == NULL) return;
|
|
fprintf(fd, "#\n");
|
|
fprintf(fd, "# stud(8), The Scalable TLS Unwrapping Daemon's configuration\n");
|
|
fprintf(fd, "#\n");
|
|
fprintf(fd, "\n");
|
|
fprintf(fd, "# NOTE: all config file parameters can be overriden\n");
|
|
fprintf(fd, "# from command line!\n");
|
|
fprintf(fd, "\n");
|
|
|
|
fprintf(fd, "# Listening address. REQUIRED.\n");
|
|
fprintf(fd, "#\n");
|
|
fprintf(fd, "# type: string\n");
|
|
fprintf(fd, "# syntax: [HOST]:PORT\n");
|
|
fprintf(fd, FMT_QSTR, CFG_FRONTEND, config_disp_hostport(cfg->FRONT_IP, cfg->FRONT_PORT));
|
|
fprintf(fd, "\n");
|
|
|
|
fprintf(fd, "# Upstream server address. REQUIRED.\n");
|
|
fprintf(fd, "#\n");
|
|
fprintf(fd, "# type: string\n");
|
|
fprintf(fd, "# syntax: [HOST]:PORT.\n");
|
|
fprintf(fd, FMT_QSTR, CFG_BACKEND, config_disp_hostport(cfg->BACK_IP, cfg->BACK_PORT));
|
|
fprintf(fd, "\n");
|
|
|
|
fprintf(fd, "# SSL x509 certificate file. REQUIRED.\n");
|
|
fprintf(fd, "# List multiple certs to use SNI. Certs are used in the order they\n");
|
|
fprintf(fd, "# are listed; the last cert listed will be used if none of the others match\n");
|
|
fprintf(fd, "#\n");
|
|
fprintf(fd, "# type: string\n");
|
|
fprintf(fd, FMT_QSTR, CFG_PEM_FILE, "");
|
|
fprintf(fd, "\n");
|
|
|
|
fprintf(fd, "# SSL protocol.\n");
|
|
fprintf(fd, "#\n");
|
|
fprintf(fd, "# tls = on\n");
|
|
fprintf(fd, "# ssl = off\n");
|
|
fprintf(fd, "\n");
|
|
|
|
fprintf(fd, "# List of allowed SSL ciphers.\n");
|
|
fprintf(fd, "#\n");
|
|
fprintf(fd, "# Run openssl ciphers for list of available ciphers.\n");
|
|
fprintf(fd, "# type: string\n");
|
|
fprintf(fd, FMT_QSTR, CFG_CIPHERS, config_disp_str(cfg->CIPHER_SUITE));
|
|
fprintf(fd, "\n");
|
|
|
|
fprintf(fd, "# Enforce server cipher list order\n");
|
|
fprintf(fd, "#\n");
|
|
fprintf(fd, "# type: boolean\n");
|
|
fprintf(fd, FMT_STR, CFG_PREFER_SERVER_CIPHERS, config_disp_bool(cfg->PREFER_SERVER_CIPHERS));
|
|
fprintf(fd, "\n");
|
|
|
|
fprintf(fd, "# Use specified SSL engine\n");
|
|
fprintf(fd, "#\n");
|
|
fprintf(fd, "# type: string\n");
|
|
fprintf(fd, FMT_QSTR, CFG_SSL_ENGINE, config_disp_str(cfg->ENGINE));
|
|
fprintf(fd, "\n");
|
|
|
|
fprintf(fd, "# Number of worker processes\n");
|
|
fprintf(fd, "#\n");
|
|
fprintf(fd, "# type: integer\n");
|
|
fprintf(fd, FMT_ISTR, CFG_WORKERS, (int) cfg->NCORES);
|
|
fprintf(fd, "\n");
|
|
|
|
fprintf(fd, "# Listen backlog size\n");
|
|
fprintf(fd, "#\n");
|
|
fprintf(fd, "# type: integer\n");
|
|
fprintf(fd, FMT_ISTR, CFG_BACKLOG, cfg->BACKLOG);
|
|
fprintf(fd, "\n");
|
|
|
|
fprintf(fd, "# TCP socket keepalive interval in seconds\n");
|
|
fprintf(fd, "#\n");
|
|
fprintf(fd, "# type: integer\n");
|
|
fprintf(fd, FMT_ISTR, CFG_KEEPALIVE, cfg->TCP_KEEPALIVE_TIME);
|
|
fprintf(fd, "\n");
|
|
|
|
#ifdef USE_SHARED_CACHE
|
|
fprintf(fd, "# SSL session cache size\n");
|
|
fprintf(fd, "#\n");
|
|
fprintf(fd, "# type: integer\n");
|
|
fprintf(fd, FMT_ISTR, CFG_SHARED_CACHE, cfg->SHARED_CACHE);
|
|
fprintf(fd, "\n");
|
|
|
|
fprintf(fd, "# Accept shared SSL cache updates on specified listener.\n");
|
|
fprintf(fd, "#\n");
|
|
fprintf(fd, "# type: string\n");
|
|
fprintf(fd, "# syntax: [HOST]:PORT\n");
|
|
fprintf(fd, FMT_QSTR, CFG_SHARED_CACHE_LISTEN, config_disp_hostport(cfg->SHCUPD_IP, cfg->SHCUPD_PORT));
|
|
fprintf(fd, "\n");
|
|
|
|
fprintf(fd, "# Shared cache peer address.\n");
|
|
fprintf(fd, "# Multiple stud processes on multiple hosts (host limit: %d)\n", MAX_SHCUPD_PEERS);
|
|
fprintf(fd, "# can share SSL session cache by sending updates to peers.\n");
|
|
fprintf(fd, "#\n");
|
|
fprintf(fd, "# NOTE: This parameter can be specified multiple times in order\n");
|
|
fprintf(fd, "# to specify multiple peers.\n");
|
|
fprintf(fd, "#\n");
|
|
fprintf(fd, "# type: string\n");
|
|
fprintf(fd, "# syntax: [HOST]:PORT\n");
|
|
fprintf(fd, "# " FMT_QSTR, CFG_SHARED_CACHE_PEER, config_disp_hostport(NULL, NULL));
|
|
for (int i = 0; i < MAX_SHCUPD_PEERS; i++) {
|
|
if (cfg->SHCUPD_PEERS[i].ip == NULL && cfg->SHCUPD_PEERS[i].port == NULL) break;
|
|
fprintf(fd, FMT_QSTR, CFG_SHARED_CACHE_PEER, config_disp_hostport(cfg->SHCUPD_PEERS[i].ip, cfg->SHCUPD_PEERS[i].port));
|
|
}
|
|
fprintf(fd, "\n");
|
|
|
|
fprintf(fd, "# Shared cache interface name and optional TTL\n");
|
|
fprintf(fd, "#\n");
|
|
fprintf(fd, "# type: string\n");
|
|
fprintf(fd, "# syntax: iface[,TTL]\n");
|
|
fprintf(fd, "# %s = \"%s", CFG_SHARED_CACHE_MCASTIF, config_disp_str(cfg->SHCUPD_MCASTIF));
|
|
if (cfg->SHCUPD_MCASTTTL != NULL && strlen(cfg->SHCUPD_MCASTTTL) > 0) {
|
|
fprintf(fd, ",%s", cfg->SHCUPD_MCASTTTL);
|
|
}
|
|
fprintf(fd, "\"\n");
|
|
fprintf(fd, "\n");
|
|
#endif
|
|
|
|
fprintf(fd, "# Chroot directory\n");
|
|
fprintf(fd, "#\n");
|
|
fprintf(fd, "# type: string\n");
|
|
fprintf(fd, FMT_QSTR, CFG_CHROOT, config_disp_str(cfg->CHROOT));
|
|
fprintf(fd, "\n");
|
|
|
|
fprintf(fd, "# Set uid after binding a socket\n");
|
|
fprintf(fd, "#\n");
|
|
fprintf(fd, "# type: string\n");
|
|
fprintf(fd, FMT_QSTR, CFG_USER, config_disp_uid(cfg->UID));
|
|
fprintf(fd, "\n");
|
|
|
|
fprintf(fd, "# Set gid after binding a socket\n");
|
|
fprintf(fd, "#\n");
|
|
fprintf(fd, "# type: string\n");
|
|
fprintf(fd, FMT_QSTR, CFG_GROUP, config_disp_gid(cfg->GID));
|
|
fprintf(fd, "\n");
|
|
|
|
fprintf(fd, "# Quiet execution, report only error messages\n");
|
|
fprintf(fd, "#\n");
|
|
fprintf(fd, "# type: boolean\n");
|
|
fprintf(fd, FMT_STR, CFG_QUIET, config_disp_bool(cfg->QUIET));
|
|
fprintf(fd, "\n");
|
|
|
|
fprintf(fd, "# Use syslog for logging\n");
|
|
fprintf(fd, "#\n");
|
|
fprintf(fd, "# type: boolean\n");
|
|
fprintf(fd, FMT_STR, CFG_SYSLOG, config_disp_bool(cfg->SYSLOG));
|
|
fprintf(fd, "\n");
|
|
|
|
fprintf(fd, "# Syslog facility to use\n");
|
|
fprintf(fd, "#\n");
|
|
fprintf(fd, "# type: string\n");
|
|
fprintf(fd, FMT_QSTR, CFG_SYSLOG_FACILITY, config_disp_log_facility(cfg->SYSLOG_FACILITY));
|
|
fprintf(fd, "\n");
|
|
|
|
fprintf(fd, "# Run as daemon\n");
|
|
fprintf(fd, "#\n");
|
|
fprintf(fd, "# type: boolean\n");
|
|
fprintf(fd, FMT_STR, CFG_DAEMON, config_disp_bool(cfg->DAEMONIZE));
|
|
fprintf(fd, "\n");
|
|
|
|
fprintf(fd, "# Report client address by writing IP before sending data\n");
|
|
fprintf(fd, "#\n");
|
|
fprintf(fd, "# NOTE: This option is mutually exclusive with option %s and %s.\n", CFG_WRITE_PROXY, CFG_PROXY_PROXY);
|
|
fprintf(fd, "#\n");
|
|
fprintf(fd, "# type: boolean\n");
|
|
fprintf(fd, FMT_STR, CFG_WRITE_IP, config_disp_bool(cfg->WRITE_IP_OCTET));
|
|
fprintf(fd, "\n");
|
|
|
|
fprintf(fd, "# Report client address using SENDPROXY protocol, see\n");
|
|
fprintf(fd, "# http://haproxy.1wt.eu/download/1.5/doc/proxy-protocol.txt\n");
|
|
fprintf(fd, "# for details.\n");
|
|
fprintf(fd, "#\n");
|
|
fprintf(fd, "# NOTE: This option is mutually exclusive with option %s and %s.\n", CFG_WRITE_IP, CFG_PROXY_PROXY);
|
|
fprintf(fd, "#\n");
|
|
fprintf(fd, "# type: boolean\n");
|
|
fprintf(fd, FMT_STR, CFG_WRITE_PROXY, config_disp_bool(cfg->WRITE_PROXY_LINE));
|
|
fprintf(fd, "\n");
|
|
|
|
fprintf(fd, "# Proxy an existing SENDPROXY protocol header through this request.\n");
|
|
fprintf(fd, "#\n");
|
|
fprintf(fd, "# NOTE: This option is mutually exclusive with option %s and %s.\n", CFG_WRITE_IP, CFG_WRITE_PROXY);
|
|
fprintf(fd, "#\n");
|
|
fprintf(fd, "# type: boolean\n");
|
|
fprintf(fd, FMT_STR, CFG_PROXY_PROXY, config_disp_bool(cfg->PROXY_PROXY_LINE));
|
|
fprintf(fd, "\n");
|
|
|
|
fprintf(fd, "# EOF\n");
|
|
}
|
|
#endif /* NO_CONFIG_FILE */
|
|
|
|
void config_print_usage (char *prog, stud_config *cfg) {
|
|
config_print_usage_fd(prog, cfg, stdout);
|
|
}
|
|
|
|
void config_parse_cli(int argc, char **argv, stud_config *cfg) {
|
|
static int tls = 0, ssl = 0;
|
|
static int client = 0;
|
|
int c, i;
|
|
int test_only = 0;
|
|
char *prog;
|
|
|
|
struct option long_options[] = {
|
|
#ifndef NO_CONFIG_FILE
|
|
{ CFG_CONFIG, 1, NULL, CFG_PARAM_CFGFILE },
|
|
{ CFG_CONFIG_DEFAULT, 0, NULL, CFG_PARAM_DEFCFG },
|
|
#endif
|
|
|
|
{ "tls", 0, &tls, 1},
|
|
{ "ssl", 0, &ssl, 1},
|
|
{ "client", 0, &client, 1},
|
|
{ CFG_CIPHERS, 1, NULL, 'c' },
|
|
{ CFG_PREFER_SERVER_CIPHERS, 0, NULL, 'O' },
|
|
{ CFG_BACKEND, 1, NULL, 'b' },
|
|
{ CFG_FRONTEND, 1, NULL, 'f' },
|
|
{ CFG_WORKERS, 1, NULL, 'n' },
|
|
{ CFG_BACKLOG, 1, NULL, 'B' },
|
|
#ifdef USE_SHARED_CACHE
|
|
{ CFG_SHARED_CACHE, 1, NULL, 'C' },
|
|
{ CFG_SHARED_CACHE_LISTEN, 1, NULL, 'U' },
|
|
{ CFG_SHARED_CACHE_PEER, 1, NULL, 'P' },
|
|
{ CFG_SHARED_CACHE_MCASTIF, 1, NULL, 'M' },
|
|
#endif
|
|
{ CFG_KEEPALIVE, 1, NULL, 'k' },
|
|
{ CFG_CHROOT, 1, NULL, 'r' },
|
|
{ CFG_USER, 1, NULL, 'u' },
|
|
{ CFG_GROUP, 1, NULL, 'g' },
|
|
{ CFG_QUIET, 0, NULL, 'q' },
|
|
{ CFG_SYSLOG, 0, NULL, 's' },
|
|
{ CFG_SYSLOG_FACILITY, 1, NULL, CFG_PARAM_SYSLOG_FACILITY },
|
|
{ CFG_DAEMON, 0, &cfg->DAEMONIZE, 1 },
|
|
{ CFG_WRITE_IP, 0, &cfg->WRITE_IP_OCTET, 1 },
|
|
{ CFG_WRITE_PROXY, 0, &cfg->WRITE_PROXY_LINE, 1 },
|
|
{ CFG_PROXY_PROXY, 0, &cfg->PROXY_PROXY_LINE, 1 },
|
|
|
|
{ "test", 0, NULL, 't' },
|
|
{ "version", 0, NULL, 'V' },
|
|
{ "help", 0, NULL, 'h' },
|
|
{ 0, 0, 0, 0 }
|
|
};
|
|
|
|
while (1) {
|
|
int option_index = 0;
|
|
c = getopt_long(
|
|
argc, argv,
|
|
"c:e:Ob:f:n:B:C:U:P:M:k:r:u:g:qstVh",
|
|
long_options, &option_index
|
|
);
|
|
|
|
if (c == -1)
|
|
break;
|
|
|
|
switch (c) {
|
|
case 0:
|
|
break;
|
|
#ifndef NO_CONFIG_FILE
|
|
case CFG_PARAM_CFGFILE:
|
|
if (!config_file_parse(optarg, cfg))
|
|
config_die("%s", config_error_get());
|
|
break;
|
|
case CFG_PARAM_DEFCFG:
|
|
config_print_default(stdout, cfg);
|
|
exit(0);
|
|
break;
|
|
#endif
|
|
case CFG_PARAM_SYSLOG_FACILITY:
|
|
config_param_validate(CFG_SYSLOG_FACILITY, optarg, cfg, NULL, 0);
|
|
break;
|
|
case 'c':
|
|
config_param_validate(CFG_CIPHERS, optarg, cfg, NULL, 0);
|
|
break;
|
|
case 'e':
|
|
config_param_validate(CFG_SSL_ENGINE, optarg, cfg, NULL, 0);
|
|
break;
|
|
case 'O':
|
|
config_param_validate(CFG_PREFER_SERVER_CIPHERS, CFG_BOOL_ON, cfg, NULL, 0);
|
|
break;
|
|
case 'b':
|
|
config_param_validate(CFG_BACKEND, optarg, cfg, NULL, 0);
|
|
break;
|
|
case 'f':
|
|
config_param_validate(CFG_FRONTEND, optarg, cfg, NULL, 0);
|
|
break;
|
|
case 'n':
|
|
config_param_validate(CFG_WORKERS, optarg, cfg, NULL, 0);
|
|
break;
|
|
case 'B':
|
|
config_param_validate(CFG_BACKLOG, optarg, cfg, NULL, 0);
|
|
break;
|
|
#ifdef USE_SHARED_CACHE
|
|
case 'C':
|
|
config_param_validate(CFG_SHARED_CACHE, optarg, cfg, NULL, 0);
|
|
break;
|
|
case 'U':
|
|
config_param_validate(CFG_SHARED_CACHE_LISTEN, optarg, cfg, NULL, 0);
|
|
break;
|
|
case 'P':
|
|
config_param_validate(CFG_SHARED_CACHE_PEER, optarg, cfg, NULL, 0);
|
|
break;
|
|
case 'M':
|
|
config_param_validate(CFG_SHARED_CACHE_MCASTIF, optarg, cfg, NULL, 0);
|
|
break;
|
|
#endif
|
|
case 'k':
|
|
config_param_validate(CFG_KEEPALIVE, optarg, cfg, NULL, 0);
|
|
break;
|
|
case 'r':
|
|
config_param_validate(CFG_CHROOT, optarg, cfg, NULL, 0);
|
|
break;
|
|
case 'u':
|
|
config_param_validate(CFG_USER, optarg, cfg, NULL, 0);
|
|
break;
|
|
case 'g':
|
|
config_param_validate(CFG_GROUP, optarg, cfg, NULL, 0);
|
|
break;
|
|
case 'q':
|
|
config_param_validate(CFG_QUIET, CFG_BOOL_ON, cfg, NULL, 0);
|
|
break;
|
|
case 's':
|
|
config_param_validate(CFG_SYSLOG, CFG_BOOL_ON, cfg, NULL, 0);
|
|
break;
|
|
case 't':
|
|
test_only = 1;
|
|
break;
|
|
case 'V':
|
|
printf("%s %s\n", basename(argv[0]), STUD_VERSION);
|
|
exit(0);
|
|
break;
|
|
case 'h':
|
|
config_print_usage(argv[0], cfg);
|
|
exit(0);
|
|
break;
|
|
|
|
default:
|
|
config_die("Invalid command line parameters. Run %s --help for instructions.", basename(argv[0]));
|
|
}
|
|
}
|
|
|
|
prog = argv[0];
|
|
|
|
if (tls && ssl)
|
|
config_die("Options --tls and --ssl are mutually exclusive.");
|
|
else {
|
|
if (ssl)
|
|
cfg->ETYPE = ENC_SSL;
|
|
else if (tls)
|
|
cfg->ETYPE = ENC_TLS;
|
|
}
|
|
|
|
if (client) {
|
|
cfg->PMODE = SSL_CLIENT;
|
|
}
|
|
|
|
if (cfg->WRITE_IP_OCTET && cfg->WRITE_PROXY_LINE)
|
|
config_die("Options --write-ip and --write-proxy are mutually exclusive.");
|
|
|
|
if (cfg->WRITE_PROXY_LINE && cfg->PROXY_PROXY_LINE)
|
|
config_die("Options --write-proxy and --proxy-proxy are mutually exclusive.");
|
|
|
|
if (cfg->WRITE_IP_OCTET && cfg->PROXY_PROXY_LINE)
|
|
config_die("Options --write-ip and --proxy-proxy are mutually exclusive.");
|
|
|
|
if (cfg->DAEMONIZE) {
|
|
cfg->SYSLOG = 1;
|
|
cfg->QUIET = 1;
|
|
}
|
|
|
|
#ifdef USE_SHARED_CACHE
|
|
if (cfg->SHCUPD_IP != NULL && ! cfg->SHARED_CACHE)
|
|
config_die("Shared cache update listener is defined, but shared cache is disabled.");
|
|
#endif
|
|
|
|
// Any arguments left are presumed to be PEM files
|
|
argc -= optind;
|
|
argv += optind;
|
|
for (i = 0; i < argc; i++) {
|
|
config_param_validate(CFG_PEM_FILE, argv[i], cfg, NULL, 0);
|
|
}
|
|
if (cfg->PMODE == SSL_SERVER && cfg->CERT_FILES == NULL) {
|
|
config_die("No x509 certificate PEM file specified!");
|
|
}
|
|
|
|
// was this only a test?
|
|
if (test_only) {
|
|
fprintf(stderr, "Trying to initialize SSL contexts with your certificates");
|
|
if (!init_openssl()) {
|
|
config_die("Error initializing OpenSSL.");
|
|
}
|
|
printf("%s configuration looks ok.\n", basename(prog));
|
|
exit(0);
|
|
}
|
|
}
|