diff --git a/sslh-conf.c b/sslh-conf.c new file mode 100644 index 0000000..e23f5f2 --- /dev/null +++ b/sslh-conf.c @@ -0,0 +1,1605 @@ +/* Generated by conf2struct (https://www.rutschle.net/tech/conf2struct/README) + * on Fri Dec 27 18:18:30 2019. + +# conf2struct: generate libconf parsers that read to structs +# Copyright (C) 2018-2019 Yves Rutschle +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +*/ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include "sslh-conf.h" +#include "argtable3.h" +#ifdef LIBPCRE +#include +#else +#include +#endif + + +/* This gets included in the output .c file */ + + +/* config_type, lookup_fns, type2str are related, keep them together */ +typedef enum { + CFG_BOOL, + CFG_INT, + CFG_INT64, + CFG_FLOAT, + CFG_STRING, + CFG_GROUP, + CFG_ARRAY, + CFG_LIST, +} config_type; + +typedef int (*lookup_fn)(const config_setting_t*, const char*, void*); +lookup_fn lookup_fns[] = { + (lookup_fn)config_setting_lookup_bool, + (lookup_fn)config_setting_lookup_int, + (lookup_fn)config_setting_lookup_int64, + (lookup_fn)config_setting_lookup_float, + (lookup_fn)config_setting_lookup_string, + NULL, /* CFG_GROUP */ + NULL, /* CFG_ARRAY */ + NULL, /* CFG_LIST */ +}; + +const char* type2str[] = { + "boolean", + "int", + "int64", + "float", + "string", + "group", + "array", + "list", +}; + +/* /config_type */ + +typedef union { + int def_bool; + int def_int; + long long def_int64; + double def_float; + char* def_string; +} any_val; + +/* Copy an any_val to arbitrary memory location */ +static void any_valcpy(config_type type, void* target, any_val val) +{ + switch(type) { + case CFG_BOOL: + *(int*)target = val.def_bool; + break; + + case CFG_INT: + *(int*)target = val.def_int; + break; + + case CFG_INT64: + *(long long*)target = val.def_int64; + break; + + case CFG_FLOAT: + *(double*)target = val.def_float; + break; + + case CFG_STRING: + *(char**)target = val.def_string; + break; + + default: + fprintf(stderr, "Unknown type specification %d\n", type); + exit(1); + } +} + + +/* Copy the value of a setting to an arbitrary memory that +* must be large enough */ +static void settingcpy(config_type type, void* target, const config_setting_t* setting) +{ + any_val val; + char* str; + + switch(type) { + case CFG_BOOL: + val.def_bool = config_setting_get_bool(setting); + *(int*)target = val.def_bool; + break; + + case CFG_INT: + val.def_int = config_setting_get_int(setting); + *(int*)target = val.def_int; + break; + + case CFG_INT64: + val.def_int64 = config_setting_get_int64(setting); + *(long long*)target = val.def_int64; + break; + + case CFG_FLOAT: + val.def_float = config_setting_get_float(setting); + *(double*)target = val.def_int64; + break; + + case CFG_STRING: + asprintf(&str, "%s", config_setting_get_string(setting)); + val.def_string = str; + *(char**)target = val.def_string; + break; + + default: + fprintf(stderr, "Unknown type specification %d\n", type); + exit(1); + } +} + +/* Copy the value of a command line arg to arbitrary memory +* that must be large enough for the type */ +static void clcpy(config_type type, void* target, const void* cl_arg) +{ + any_val val; + char* str; + + switch(type) { + case CFG_BOOL: + val.def_bool = (*(struct arg_lit**)cl_arg)->count; + *(int*)target = val.def_bool; + break; + + case CFG_INT: + val.def_int = (*(struct arg_int**)cl_arg)->ival[0]; + *(int*)target = val.def_int; + break; + + case CFG_INT64: + val.def_int64 = (*(struct arg_int**)cl_arg)->ival[0]; + *(long long*)target = val.def_int64; + break; + + case CFG_FLOAT: + val.def_float = (*(struct arg_dbl**)cl_arg)->dval[0]; + *(double*)target = val.def_float; + break; + + case CFG_STRING: + asprintf(&str, "%s", (*(struct arg_str**)cl_arg)->sval[0]); + val.def_string = str; + *(char**)target = val.def_string; + break; + + default: + fprintf(stderr, "Unknown type specification %d\n", type); + exit(1); + } +} + +/* Copy the value of a string argument to arbitary memory +* location that must be large enough, converting on the way +* (i.e. CFG_INT gets atoi() and so on) */ +static void stringcpy(config_type type, void* target, char* from) +{ + any_val val; + + switch(type) { + case CFG_BOOL: + val.def_bool = (*from != '0'); + *(int*)target = val.def_bool; + break; + + case CFG_INT: + val.def_int = strtol(from, NULL, 10); + *(int*)target = val.def_int; + break; + + case CFG_INT64: + val.def_int64 = strtoll(from, NULL, 10); + *(long long*)target = val.def_int64; + break; + + case CFG_FLOAT: + val.def_float = strtod(from, NULL); + *(double*)target = val.def_float; + break; + + case CFG_STRING: + val.def_string = from; + *(char**)target = val.def_string; + break; + + default: + fprintf(stderr, "Unknown type specification %d\n", type); + exit(1); + } +} + + +struct config_desc { + const char* name; + int type; + struct config_desc * sub_group; /* Table for compound types (list and group) */ + void* arg_cl; /* command-line argument for this setting */ + void* base_addr; /* Base of the structure (filled at runtime). Probably not useable for list elements */ + size_t offset; /* Offset of setting in the structure */ + size_t offset_len; /* Offset of *_len field, for arrays and lists */ + size_t offset_present; /* offset of *_is_present field, for optional settings */ + size_t size; /* Size of element, or size of group for groups and lists */ + int array_type; /* type of array elements, when type == CFG_ARRAY */ + int mandatory; + int optional; + any_val default_val; +}; + +/* Element to describe the target of a compound element +* element: which config entry is being changed +* match: if >0, index in pmatch to set +* if 0, don't match but init with value +* value: constant if not matching */ +struct compound_cl_target { + struct config_desc * element; + int match; + any_val value; +}; + +/* Element to describe one compound command line argument + * An argument is string that gets matched against a regex, + * then match-groups get evaluated to each targets[]. + * For lists, base_entry points to the config_setting so we + * can append to it */ +struct compound_cl_arg { + const char* regex; + struct arg_str** arg_cl; /* arg_str entry for this compound option */ + struct config_desc * base_entry; + struct compound_cl_target* targets; + + /* If override_desc is set, it points to the descriptor of the element in + the group which will be checked for override. Then, override_matchindex + indicates the command-line parameter match used to compare against + override_desc to know if this group is overridden. If override_matchindex + is 0, we don't match from the command-line but from a constant stored in + override_const instead */ + struct config_desc * override_desc; + int override_matchindex; + char* override_const; +}; + + +struct arg_file* sslhcfg_conffile; + struct arg_int* sslhcfg_verbose; + struct arg_lit* sslhcfg_foreground; + struct arg_lit* sslhcfg_inetd; + struct arg_lit* sslhcfg_numeric; + struct arg_lit* sslhcfg_transparent; + struct arg_int* sslhcfg_timeout; + struct arg_str* sslhcfg_user; + struct arg_str* sslhcfg_pidfile; + struct arg_str* sslhcfg_chroot; + struct arg_str* sslhcfg_syslog_facility; + struct arg_str* sslhcfg_on_timeout; + struct arg_str* sslhcfg_listen; + struct arg_str* sslhcfg_ssh; + struct arg_str* sslhcfg_tls; + struct arg_str* sslhcfg_openvpn; + struct arg_str* sslhcfg_tinc; + struct arg_str* sslhcfg_xmpp; + struct arg_str* sslhcfg_http; + struct arg_str* sslhcfg_adb; + struct arg_str* sslhcfg_socks5; + struct arg_end* sslhcfg_end; + + +static struct config_desc table_sslhcfg_protocols[] = { + + + { + /* name */ "name", + /* type */ CFG_STRING, + /* sub_group*/ NULL, + /* arg_cl */ NULL, + /* base_addr */ NULL, + /* offset */ offsetof(struct sslhcfg_protocols_item, name), + /* offset_len */ 0, + /* offset_present */ 0, + /* size */ sizeof(char*), + /* array_type */ -1, + /* mandatory */ 1, + /* optional */ 0, + /* default_val*/ .default_val.def_string = NULL + }, + + { + /* name */ "host", + /* type */ CFG_STRING, + /* sub_group*/ NULL, + /* arg_cl */ NULL, + /* base_addr */ NULL, + /* offset */ offsetof(struct sslhcfg_protocols_item, host), + /* offset_len */ 0, + /* offset_present */ 0, + /* size */ sizeof(char*), + /* array_type */ -1, + /* mandatory */ 1, + /* optional */ 0, + /* default_val*/ .default_val.def_string = NULL + }, + + { + /* name */ "port", + /* type */ CFG_STRING, + /* sub_group*/ NULL, + /* arg_cl */ NULL, + /* base_addr */ NULL, + /* offset */ offsetof(struct sslhcfg_protocols_item, port), + /* offset_len */ 0, + /* offset_present */ 0, + /* size */ sizeof(char*), + /* array_type */ -1, + /* mandatory */ 1, + /* optional */ 0, + /* default_val*/ .default_val.def_string = NULL + }, + + { + /* name */ "service", + /* type */ CFG_STRING, + /* sub_group*/ NULL, + /* arg_cl */ NULL, + /* base_addr */ NULL, + /* offset */ offsetof(struct sslhcfg_protocols_item, service), + /* offset_len */ 0, + /* offset_present */ offsetof(struct sslhcfg_protocols_item, service_is_present), + /* size */ sizeof(char*), + /* array_type */ -1, + /* mandatory */ 0, + /* optional */ 1, + /* default_val*/ .default_val.def_string = NULL + }, + + { + /* name */ "fork", + /* type */ CFG_BOOL, + /* sub_group*/ NULL, + /* arg_cl */ NULL, + /* base_addr */ NULL, + /* offset */ offsetof(struct sslhcfg_protocols_item, fork), + /* offset_len */ 0, + /* offset_present */ 0, + /* size */ sizeof(int), + /* array_type */ -1, + /* mandatory */ 0, + /* optional */ 0, + /* default_val*/ .default_val.def_bool = 0 + }, + + { + /* name */ "tfo_ok", + /* type */ CFG_BOOL, + /* sub_group*/ NULL, + /* arg_cl */ NULL, + /* base_addr */ NULL, + /* offset */ offsetof(struct sslhcfg_protocols_item, tfo_ok), + /* offset_len */ 0, + /* offset_present */ 0, + /* size */ sizeof(int), + /* array_type */ -1, + /* mandatory */ 0, + /* optional */ 0, + /* default_val*/ .default_val.def_bool = 0 + }, + + { + /* name */ "log_level", + /* type */ CFG_INT, + /* sub_group*/ NULL, + /* arg_cl */ NULL, + /* base_addr */ NULL, + /* offset */ offsetof(struct sslhcfg_protocols_item, log_level), + /* offset_len */ 0, + /* offset_present */ 0, + /* size */ sizeof(int), + /* array_type */ -1, + /* mandatory */ 0, + /* optional */ 0, + /* default_val*/ .default_val.def_int = 1 + }, + + { + /* name */ "keepalive", + /* type */ CFG_BOOL, + /* sub_group*/ NULL, + /* arg_cl */ NULL, + /* base_addr */ NULL, + /* offset */ offsetof(struct sslhcfg_protocols_item, keepalive), + /* offset_len */ 0, + /* offset_present */ 0, + /* size */ sizeof(int), + /* array_type */ -1, + /* mandatory */ 0, + /* optional */ 0, + /* default_val*/ .default_val.def_bool = 0 + }, + + { + /* name */ "sni_hostnames", + /* type */ CFG_ARRAY, + /* sub_group*/ NULL, + /* arg_cl */ NULL, + /* base_addr */ NULL, + /* offset */ offsetof(struct sslhcfg_protocols_item, sni_hostnames), + /* offset_len */ offsetof(struct sslhcfg_protocols_item, sni_hostnames_len), + /* offset_present */ 0, + /* size */ sizeof(char*), + /* array_type */ CFG_STRING, + /* mandatory */ 1, + /* optional */ 0, + /* default_val*/ .default_val.def_int = 0 + }, + + { + /* name */ "alpn_protocols", + /* type */ CFG_ARRAY, + /* sub_group*/ NULL, + /* arg_cl */ NULL, + /* base_addr */ NULL, + /* offset */ offsetof(struct sslhcfg_protocols_item, alpn_protocols), + /* offset_len */ offsetof(struct sslhcfg_protocols_item, alpn_protocols_len), + /* offset_present */ 0, + /* size */ sizeof(char*), + /* array_type */ CFG_STRING, + /* mandatory */ 1, + /* optional */ 0, + /* default_val*/ .default_val.def_int = 0 + }, + + { + /* name */ "regex_patterns", + /* type */ CFG_ARRAY, + /* sub_group*/ NULL, + /* arg_cl */ NULL, + /* base_addr */ NULL, + /* offset */ offsetof(struct sslhcfg_protocols_item, regex_patterns), + /* offset_len */ offsetof(struct sslhcfg_protocols_item, regex_patterns_len), + /* offset_present */ 0, + /* size */ sizeof(char*), + /* array_type */ CFG_STRING, + /* mandatory */ 1, + /* optional */ 0, + /* default_val*/ .default_val.def_int = 0 + }, + + { + /* name */ "minlength", + /* type */ CFG_INT, + /* sub_group*/ NULL, + /* arg_cl */ NULL, + /* base_addr */ NULL, + /* offset */ offsetof(struct sslhcfg_protocols_item, minlength), + /* offset_len */ 0, + /* offset_present */ offsetof(struct sslhcfg_protocols_item, minlength_is_present), + /* size */ sizeof(int), + /* array_type */ -1, + /* mandatory */ 0, + /* optional */ 1, + /* default_val*/ .default_val.def_int = 0 + }, + { 0 } +}; + +static struct config_desc table_sslhcfg_listen[] = { + + + { + /* name */ "host", + /* type */ CFG_STRING, + /* sub_group*/ NULL, + /* arg_cl */ NULL, + /* base_addr */ NULL, + /* offset */ offsetof(struct sslhcfg_listen_item, host), + /* offset_len */ 0, + /* offset_present */ 0, + /* size */ sizeof(char*), + /* array_type */ -1, + /* mandatory */ 1, + /* optional */ 0, + /* default_val*/ .default_val.def_string = NULL + }, + + { + /* name */ "port", + /* type */ CFG_STRING, + /* sub_group*/ NULL, + /* arg_cl */ NULL, + /* base_addr */ NULL, + /* offset */ offsetof(struct sslhcfg_listen_item, port), + /* offset_len */ 0, + /* offset_present */ 0, + /* size */ sizeof(char*), + /* array_type */ -1, + /* mandatory */ 1, + /* optional */ 0, + /* default_val*/ .default_val.def_string = NULL + }, + + { + /* name */ "keepalive", + /* type */ CFG_BOOL, + /* sub_group*/ NULL, + /* arg_cl */ NULL, + /* base_addr */ NULL, + /* offset */ offsetof(struct sslhcfg_listen_item, keepalive), + /* offset_len */ 0, + /* offset_present */ 0, + /* size */ sizeof(int), + /* array_type */ -1, + /* mandatory */ 0, + /* optional */ 0, + /* default_val*/ .default_val.def_bool = 0 + }, + { 0 } +}; + +static struct config_desc table_sslhcfg[] = { + + + { + /* name */ "verbose", + /* type */ CFG_INT, + /* sub_group*/ NULL, + /* arg_cl */ & sslhcfg_verbose, + /* base_addr */ NULL, + /* offset */ offsetof(struct sslhcfg_item, verbose), + /* offset_len */ 0, + /* offset_present */ 0, + /* size */ sizeof(int), + /* array_type */ -1, + /* mandatory */ 0, + /* optional */ 0, + /* default_val*/ .default_val.def_int = 0 + }, + + { + /* name */ "foreground", + /* type */ CFG_BOOL, + /* sub_group*/ NULL, + /* arg_cl */ & sslhcfg_foreground, + /* base_addr */ NULL, + /* offset */ offsetof(struct sslhcfg_item, foreground), + /* offset_len */ 0, + /* offset_present */ 0, + /* size */ sizeof(int), + /* array_type */ -1, + /* mandatory */ 0, + /* optional */ 0, + /* default_val*/ .default_val.def_bool = 0 + }, + + { + /* name */ "inetd", + /* type */ CFG_BOOL, + /* sub_group*/ NULL, + /* arg_cl */ & sslhcfg_inetd, + /* base_addr */ NULL, + /* offset */ offsetof(struct sslhcfg_item, inetd), + /* offset_len */ 0, + /* offset_present */ 0, + /* size */ sizeof(int), + /* array_type */ -1, + /* mandatory */ 0, + /* optional */ 0, + /* default_val*/ .default_val.def_bool = 0 + }, + + { + /* name */ "numeric", + /* type */ CFG_BOOL, + /* sub_group*/ NULL, + /* arg_cl */ & sslhcfg_numeric, + /* base_addr */ NULL, + /* offset */ offsetof(struct sslhcfg_item, numeric), + /* offset_len */ 0, + /* offset_present */ 0, + /* size */ sizeof(int), + /* array_type */ -1, + /* mandatory */ 0, + /* optional */ 0, + /* default_val*/ .default_val.def_bool = 0 + }, + + { + /* name */ "transparent", + /* type */ CFG_BOOL, + /* sub_group*/ NULL, + /* arg_cl */ & sslhcfg_transparent, + /* base_addr */ NULL, + /* offset */ offsetof(struct sslhcfg_item, transparent), + /* offset_len */ 0, + /* offset_present */ 0, + /* size */ sizeof(int), + /* array_type */ -1, + /* mandatory */ 0, + /* optional */ 0, + /* default_val*/ .default_val.def_bool = 0 + }, + + { + /* name */ "timeout", + /* type */ CFG_INT, + /* sub_group*/ NULL, + /* arg_cl */ & sslhcfg_timeout, + /* base_addr */ NULL, + /* offset */ offsetof(struct sslhcfg_item, timeout), + /* offset_len */ 0, + /* offset_present */ 0, + /* size */ sizeof(int), + /* array_type */ -1, + /* mandatory */ 0, + /* optional */ 0, + /* default_val*/ .default_val.def_int = 5 + }, + + { + /* name */ "user", + /* type */ CFG_STRING, + /* sub_group*/ NULL, + /* arg_cl */ & sslhcfg_user, + /* base_addr */ NULL, + /* offset */ offsetof(struct sslhcfg_item, user), + /* offset_len */ 0, + /* offset_present */ offsetof(struct sslhcfg_item, user_is_present), + /* size */ sizeof(char*), + /* array_type */ -1, + /* mandatory */ 0, + /* optional */ 1, + /* default_val*/ .default_val.def_string = NULL + }, + + { + /* name */ "pidfile", + /* type */ CFG_STRING, + /* sub_group*/ NULL, + /* arg_cl */ & sslhcfg_pidfile, + /* base_addr */ NULL, + /* offset */ offsetof(struct sslhcfg_item, pidfile), + /* offset_len */ 0, + /* offset_present */ offsetof(struct sslhcfg_item, pidfile_is_present), + /* size */ sizeof(char*), + /* array_type */ -1, + /* mandatory */ 0, + /* optional */ 1, + /* default_val*/ .default_val.def_string = NULL + }, + + { + /* name */ "chroot", + /* type */ CFG_STRING, + /* sub_group*/ NULL, + /* arg_cl */ & sslhcfg_chroot, + /* base_addr */ NULL, + /* offset */ offsetof(struct sslhcfg_item, chroot), + /* offset_len */ 0, + /* offset_present */ offsetof(struct sslhcfg_item, chroot_is_present), + /* size */ sizeof(char*), + /* array_type */ -1, + /* mandatory */ 0, + /* optional */ 1, + /* default_val*/ .default_val.def_string = NULL + }, + + { + /* name */ "syslog_facility", + /* type */ CFG_STRING, + /* sub_group*/ NULL, + /* arg_cl */ & sslhcfg_syslog_facility, + /* base_addr */ NULL, + /* offset */ offsetof(struct sslhcfg_item, syslog_facility), + /* offset_len */ 0, + /* offset_present */ 0, + /* size */ sizeof(char*), + /* array_type */ -1, + /* mandatory */ 0, + /* optional */ 0, + /* default_val*/ .default_val.def_string = "auth" + }, + + { + /* name */ "on_timeout", + /* type */ CFG_STRING, + /* sub_group*/ NULL, + /* arg_cl */ & sslhcfg_on_timeout, + /* base_addr */ NULL, + /* offset */ offsetof(struct sslhcfg_item, on_timeout), + /* offset_len */ 0, + /* offset_present */ 0, + /* size */ sizeof(char*), + /* array_type */ -1, + /* mandatory */ 0, + /* optional */ 0, + /* default_val*/ .default_val.def_string = "ssh" + }, + + { + /* name */ "listen", + /* type */ CFG_LIST, + /* sub_group*/ table_sslhcfg_listen, + /* arg_cl */ NULL, + /* base_addr */ NULL, + /* offset */ offsetof(struct sslhcfg_item, listen), + /* offset_len */ offsetof(struct sslhcfg_item, listen_len), + /* offset_present */ 0, + /* size */ sizeof(struct sslhcfg_listen_item), + /* array_type */ -1, + /* mandatory */ 1, + /* optional */ 0, + /* default_val*/ .default_val.def_int = 0 + }, + + { + /* name */ "protocols", + /* type */ CFG_LIST, + /* sub_group*/ table_sslhcfg_protocols, + /* arg_cl */ NULL, + /* base_addr */ NULL, + /* offset */ offsetof(struct sslhcfg_item, protocols), + /* offset_len */ offsetof(struct sslhcfg_item, protocols_len), + /* offset_present */ 0, + /* size */ sizeof(struct sslhcfg_protocols_item), + /* array_type */ -1, + /* mandatory */ 1, + /* optional */ 0, + /* default_val*/ .default_val.def_int = 0 + }, + { 0 } +}; +static struct compound_cl_target sslhcfg_socks5_targets [] = { + { & table_sslhcfg_protocols[0], 0, .value.def_string = "socks5" }, + { & table_sslhcfg_protocols[1], 1, .value.def_string = "0" }, + { & table_sslhcfg_protocols[2], 2, .value.def_string = "0" }, + { 0 } +}; + +static struct compound_cl_target sslhcfg_adb_targets [] = { + { & table_sslhcfg_protocols[0], 0, .value.def_string = "adb" }, + { & table_sslhcfg_protocols[1], 1, .value.def_string = "0" }, + { & table_sslhcfg_protocols[2], 2, .value.def_string = "0" }, + { 0 } +}; + +static struct compound_cl_target sslhcfg_http_targets [] = { + { & table_sslhcfg_protocols[0], 0, .value.def_string = "http" }, + { & table_sslhcfg_protocols[1], 1, .value.def_string = "0" }, + { & table_sslhcfg_protocols[2], 2, .value.def_string = "0" }, + { 0 } +}; + +static struct compound_cl_target sslhcfg_xmpp_targets [] = { + { & table_sslhcfg_protocols[0], 0, .value.def_string = "xmpp" }, + { & table_sslhcfg_protocols[1], 1, .value.def_string = "0" }, + { & table_sslhcfg_protocols[2], 2, .value.def_string = "0" }, + { 0 } +}; + +static struct compound_cl_target sslhcfg_tinc_targets [] = { + { & table_sslhcfg_protocols[0], 0, .value.def_string = "openvpn" }, + { & table_sslhcfg_protocols[1], 1, .value.def_string = "0" }, + { & table_sslhcfg_protocols[2], 2, .value.def_string = "0" }, + { & table_sslhcfg_protocols[5], 0, .value.def_bool = 1 }, + { 0 } +}; + +static struct compound_cl_target sslhcfg_openvpn_targets [] = { + { & table_sslhcfg_protocols[0], 0, .value.def_string = "openvpn" }, + { & table_sslhcfg_protocols[1], 1, .value.def_string = "0" }, + { & table_sslhcfg_protocols[2], 2, .value.def_string = "0" }, + { & table_sslhcfg_protocols[5], 0, .value.def_bool = 1 }, + { 0 } +}; + +static struct compound_cl_target sslhcfg_tls_targets [] = { + { & table_sslhcfg_protocols[0], 0, .value.def_string = "tls" }, + { & table_sslhcfg_protocols[1], 1, .value.def_string = "0" }, + { & table_sslhcfg_protocols[2], 2, .value.def_string = "0" }, + { & table_sslhcfg_protocols[5], 0, .value.def_bool = 1 }, + { 0 } +}; + +static struct compound_cl_target sslhcfg_ssh_targets [] = { + { & table_sslhcfg_protocols[0], 0, .value.def_string = "ssh" }, + { & table_sslhcfg_protocols[1], 1, .value.def_string = "0" }, + { & table_sslhcfg_protocols[2], 2, .value.def_string = "0" }, + { & table_sslhcfg_protocols[4], 0, .value.def_bool = 1 }, + { & table_sslhcfg_protocols[5], 0, .value.def_bool = 1 }, + { 0 } +}; + +static struct compound_cl_target sslhcfg_listen_targets [] = { + { & table_sslhcfg_listen[0], 1, .value.def_string = "0" }, + { & table_sslhcfg_listen[1], 2, .value.def_string = "0" }, + { 0 } +}; + +static struct compound_cl_arg compound_cl_args[] = { + { /* arg: listen */ + .regex = "(.+):(\\w+)", + .arg_cl = & sslhcfg_listen, + .base_entry = & table_sslhcfg [11], + .targets = sslhcfg_listen_targets, + + + .override_desc = NULL, + .override_matchindex = 0, + .override_const = NULL, + }, + + { /* arg: ssh */ + .regex = "(.+):(\\w+)", + .arg_cl = & sslhcfg_ssh, + .base_entry = & table_sslhcfg [12], + .targets = sslhcfg_ssh_targets, + + + .override_desc = & table_sslhcfg_protocols [0], + .override_matchindex = 0, + .override_const = "ssh", + }, + + { /* arg: tls */ + .regex = "(.+):(\\w+)", + .arg_cl = & sslhcfg_tls, + .base_entry = & table_sslhcfg [12], + .targets = sslhcfg_tls_targets, + + + .override_desc = & table_sslhcfg_protocols [0], + .override_matchindex = 0, + .override_const = "tls", + }, + + { /* arg: openvpn */ + .regex = "(.+):(\\w+)", + .arg_cl = & sslhcfg_openvpn, + .base_entry = & table_sslhcfg [12], + .targets = sslhcfg_openvpn_targets, + + + .override_desc = & table_sslhcfg_protocols [0], + .override_matchindex = 0, + .override_const = "openvpn", + }, + + { /* arg: tinc */ + .regex = "(.+):(\\w+)", + .arg_cl = & sslhcfg_tinc, + .base_entry = & table_sslhcfg [12], + .targets = sslhcfg_tinc_targets, + + + .override_desc = & table_sslhcfg_protocols [0], + .override_matchindex = 0, + .override_const = "openvpn", + }, + + { /* arg: xmpp */ + .regex = "(.+):(\\w+)", + .arg_cl = & sslhcfg_xmpp, + .base_entry = & table_sslhcfg [12], + .targets = sslhcfg_xmpp_targets, + + + .override_desc = & table_sslhcfg_protocols [0], + .override_matchindex = 0, + .override_const = "xmpp", + }, + + { /* arg: http */ + .regex = "(.+):(\\w+)", + .arg_cl = & sslhcfg_http, + .base_entry = & table_sslhcfg [12], + .targets = sslhcfg_http_targets, + + + .override_desc = & table_sslhcfg_protocols [0], + .override_matchindex = 0, + .override_const = "http", + }, + + { /* arg: adb */ + .regex = "(.+):(\\w+)", + .arg_cl = & sslhcfg_adb, + .base_entry = & table_sslhcfg [12], + .targets = sslhcfg_adb_targets, + + + .override_desc = & table_sslhcfg_protocols [0], + .override_matchindex = 0, + .override_const = "adb", + }, + + { /* arg: socks5 */ + .regex = "(.+):(\\w+)", + .arg_cl = & sslhcfg_socks5, + .base_entry = & table_sslhcfg [12], + .targets = sslhcfg_socks5_targets, + + + .override_desc = & table_sslhcfg_protocols [0], + .override_matchindex = 0, + .override_const = "socks5", + }, + + { 0 } +}; + + +/* Enable debug to follow the parsing of tables */ +#if 0 +#define TRACE_READ(x) printf x +#define TRACE_READ_PRINT_SETTING 1 +#else +#define TRACE_READ(x) +#define TRACE_READ_PRINT_SETTING 0 +#endif + +/* Enable debug to follow the parsing of compound options */ +#if 0 +#define TRACE_CMPD(x) printf x +#define TRACE_CMPD_PRINT_SETTING 1 +#else +#define TRACE_CMPD(x) +#define TRACE_CMPD_PRINT_SETTING 0 +#endif + +static void print_setting(config_type type, void* val) +{ + if (TRACE_READ_PRINT_SETTING || TRACE_CMPD_PRINT_SETTING) { + switch(type) { + case CFG_BOOL: + case CFG_INT: + printf("%d", *(int*)val); + break; + case CFG_INT64: + printf("%lld", *(long long*)val); + break; + case CFG_FLOAT: + printf("%f", *(double*)val); + break; + case CFG_STRING: + printf("`%s'", *(char**)val); + break; + case CFG_GROUP: + case CFG_LIST: + case CFG_ARRAY: + break; + } + } +} + + +/* When traversing configuration, allocate memory for plural +* types, init for scalars */ +static void read_block_init(void* target, config_setting_t* cfg, struct config_desc* desc) +{ + size_t len = 0; + void* block; + config_setting_t* setting; + + switch (desc->type) { + case CFG_LIST: + case CFG_ARRAY: + if (cfg) { + setting = config_setting_lookup(cfg, desc->name); + if (setting) + len = config_setting_length(setting); + } + block = malloc(desc->size * len); + + *(size_t*)(((char*)target) + desc->offset_len) = len; + *(void**)(((char*)target) + desc->offset) = block; + TRACE_READ((" sizing for %zu elems ", len)); + break; + + case CFG_GROUP: + block = malloc(desc->size); + *(void**)(((char*)target) + desc->offset) = block; + TRACE_READ((" sizing for %zu elems ", len)); + break; + + default: + /* scalar types: copy default */ + memcpy(((char*)target) + desc->offset, &desc->default_val, desc->size); + TRACE_READ(("setting %s to default ", desc->name)); + print_setting(desc->type,(char*)target + desc->offset); + break; + } +} + +static int read_block(config_setting_t* cfg, + void* target, + struct config_desc* desc, + char** errmsg); + +/* When traversing configuration, set value from config +* file, or command line +* return: 0 if not set, 1 if set somehow */ +static int read_block_setval(void* target, + config_setting_t* cfg, + struct config_desc* desc, + char** errmsg) +{ + int i; + size_t len = 0; + void* block; + lookup_fn lookup_fn; + int in_cfg = 0, in_cl = 0; /* Present in config file? present on command line? */ + config_setting_t* setting = NULL; + + switch (desc->type) { + case CFG_LIST: + if (cfg) { + setting = config_setting_lookup(cfg, desc->name); + if (setting) + len = config_setting_length(setting); + block = *(void**)(((char*)target) + desc->offset); + for (i = 0; i < len; i++) { + config_setting_t* elem = config_setting_get_elem(setting, i); + if (!read_block(elem, (char*)block + desc->size * i, desc->sub_group, errmsg)) + return 0; + } + } + break; + + case CFG_ARRAY: + if (cfg) { + setting = config_setting_lookup(cfg, desc->name); + if (setting) + len = config_setting_length(setting); + block = *(void**)(((char*)target) + desc->offset); + for (i = 0; i < len; i++) { + config_setting_t* elem = config_setting_get_elem(setting, i); + settingcpy(desc->array_type, (char*)block + desc->size * i, elem); + TRACE_READ(("[%d] = ", i)); + print_setting(desc->array_type, (char*)block + desc->size *i); TRACE_READ(("\n")); + } + } + break; + + case CFG_GROUP: + if (cfg) setting = config_setting_lookup(cfg, desc->name); + block = *(void**)(((char*)target) + desc->offset); + if (!read_block(setting, block, desc->sub_group, errmsg)) return 0; + break; + + default: /* scalar types */ + TRACE_READ((" `%s'", desc->name)); + if (cfg && config_setting_lookup(cfg, desc->name)) { + TRACE_READ((" in config file: ")); + /* setting is present in cfg, look it up */ + lookup_fn = lookup_fns[desc->type]; + if (lookup_fn(cfg, desc->name, ((char*)target) + desc->offset) != CONFIG_TRUE) { + TRACE_READ((" but wrong type (expected %s) ", type2str[desc->type])); + asprintf(errmsg, "Option \"%s\" wrong type, expected %s\n", + desc->name, type2str[desc->type]); + return 0; + } + print_setting(desc->type, (((char*)target) + desc->offset)); + in_cfg = 1; + } else { + TRACE_READ((" not in config file")); + } + if (desc->arg_cl && (*(struct arg_int**)desc->arg_cl)->count) { + clcpy(desc->type, ((char*)target) + desc->offset, desc->arg_cl); + TRACE_READ((", command line sets to ")); + print_setting(desc->type, (((char*)target) + desc->offset)); + in_cl = 1; + } else { + TRACE_READ((", not in command line")); + } + if (!(in_cfg || in_cl)) { + TRACE_READ(("\n")); + return 0; + } + TRACE_READ(("\n")); + break; + } + return 1; +} + +/* Set *_is_present flag for target */ +static void target_is_present(void* target, struct config_desc* desc, int val) +{ + if (desc->optional) { /* _is_present only exists in target for optional settings */ + TRACE_READ((" mark as set")); + *(int*)((char*)target + desc->offset_present) = val; + } +} + +/* traverses the configuration; allocates memory if needed, +* set to default if exists, +* fill from configuration file if present, overrides or set from +* command line if present, verifies mandatory options have +* been set +* target: base address of the group being processed +*/ +static int read_block(config_setting_t* cfg, void* target, struct config_desc* desc, char** errmsg) +{ + int set; + + for (; desc->name; desc++) { + TRACE_READ(("reading %s%s%s: ", desc->optional ? "optional " : "", desc->mandatory ? "mandatory " : "", desc->name)); + desc->base_addr = target; + + + read_block_init(target, cfg, desc); + set = read_block_setval(target, cfg, desc, errmsg); + + if (!set && desc->mandatory) { + asprintf(errmsg, "Mandatory option \"%s\" not found", desc->name); + return 0; + } + + if (desc->optional) target_is_present(target, desc, set && desc->optional); + } + return 1; +} + +/* Copy regex match into newly allocated string + * out: newly allocated string (caller has to free it) + * in: string into which the match was made + * pmatch: the match to extract */ +static void pmatchcpy(char** out, const char* in, regmatch_t* pmatch) +{ + int len = pmatch->rm_eo - pmatch->rm_so; + *out = calloc(len+1, 1); + memcpy(*out, in + pmatch->rm_so, len); +} + +/* Processes a list of targets within one element, setting +* the values in the target setting +* target: where to put the data +* arg: CL arg containing the target fields +* clval: command line parameter +* pmatch: regex match array into clval +*/ +static int set_target_fields(void* target_addr, struct compound_cl_arg* arg, const char* clval, regmatch_t* pmatch) +{ + int pmatch_cnt = 1; + struct compound_cl_target* target; + + for (target = arg->targets; target->element; target++) { + struct config_desc * element = target->element; + if (target->match) { + TRACE_CMPD((" match %d rm_so %d rm_eo %d type %d\n", + pmatch_cnt, pmatch[pmatch_cnt].rm_so, pmatch[pmatch_cnt].rm_eo, element->type )); + if (pmatch[pmatch_cnt].rm_so == -1) { + /* This should not happen as regexec() did + * match before, unless there is a + * discrepency between the regex and the + * number of backreferences */ + return 0; + } + char* str; + pmatchcpy(&str, clval, &pmatch[pmatch_cnt]); + + stringcpy(element->type, (char*)target_addr + element->offset, str); + TRACE_CMPD(("setting %p+%zu to : ", target_addr , element->offset)); + print_setting(element->type , (char*)target_addr + element->offset); + TRACE_CMPD(("\n")); + + /* str is temporary buffer for type conversion, except for strings which we + * need to keep around so don't free them */ + if (element->type != CFG_STRING) + free(str); + pmatch_cnt++; + } else { /* don't use matching, set constant */ + any_valcpy(element->type, (char*)target_addr + element->offset, + target->value); + } + } + + return 1; +} + +/* Goes over a list, finds if a group matches the specified string and overwrite +* it if it does. */ +static int override_on_str(struct compound_cl_arg* arg, const char* str, regmatch_t* pmatch) +{ + struct config_desc * desc = arg->base_entry; + void* list_base = *(void**)(desc->base_addr + desc->offset); + size_t list_len = *(size_t*)(desc->base_addr + desc->offset_len); + size_t elem_size = desc->size; + int i; + + for (i = 0; i < list_len; i++) { + char* group_base = ((char*)list_base + i * elem_size); + + char* cfg_member = *(char**)(group_base + arg->override_desc->offset); + if (!strcmp(str, cfg_member)) { + memset(group_base, 0, elem_size); + struct arg_str* arg_cl = *arg->arg_cl; + if (!set_target_fields(group_base, arg, arg_cl->sval[0], pmatch)) + return 0; + return 1; + } + } + return 0; +} + +/* Goes over a list and override group if needed */ +static int override_elem(struct compound_cl_arg* arg, int arg_index, regmatch_t* pmatch) +{ + char* str; + int allocated = 0; + int res; + + if (arg->override_matchindex) { + struct arg_str* arg_cl = *arg->arg_cl; + pmatchcpy(&str, arg_cl->sval[arg_index], &pmatch[arg->override_matchindex]); + allocated = 1; + } else { + str = arg->override_const; + } + + res = override_on_str(arg, str, pmatch); + + if (allocated) free(str); + + return res; +} + +/* Add an argument to a list, overriding if required or +* appending otherwise */ +static int add_arg_to_list(struct compound_cl_arg* arg, int arg_index, regmatch_t* pmatch) +{ + struct config_desc * desc = arg->base_entry; + void* list_base = *(void**)(desc->base_addr + desc->offset); + size_t list_len = *(size_t*)(desc->base_addr + desc->offset_len); + size_t elem_size = desc->size; + + /* are we overriding an existing group? */ + if (arg->override_desc) + if (override_elem(arg, arg_index, pmatch)) + return 1; + + /* override not found or no override, append element and * zero it out */ + list_len++; + list_base = realloc(list_base, list_len * elem_size); + *(size_t*)(desc->base_addr + desc->offset_len) = list_len; + *(void**)(desc->base_addr + desc->offset) = list_base; + memset(list_base + (list_len - 1) * elem_size, 0, elem_size); + + struct arg_str* arg_cl = *arg->arg_cl; + if (!set_target_fields((char*)list_base + (list_len - 1) * elem_size, arg, arg_cl->sval[arg_index], pmatch)) { + return 0; + } + return 1; +} + +/* TODO: pass pmatch size as parameter or something */ +#define MAX_MATCH 10 + +/* Regex fiddling: uses info in arg to fill pmatch +* arg: description of the command line argument +* arg_index: occurence of this argument on the command line +*/ +static int regcompmatch(regmatch_t* pmatch, + struct compound_cl_arg* arg, + int arg_index, + char** errmsg) +{ + char* regerr; + struct arg_str* arg_cl = *arg->arg_cl; + regex_t preg; + int res = regcomp(&preg, arg->regex, REG_EXTENDED); + if (res) { + int errlen = regerror(res, &preg, NULL, 0); + regerr = malloc(errlen); + regerror(res, &preg, regerr, errlen); + asprintf(errmsg, "compiling pattern /%s/:%s", arg->regex, regerr); + free(regerr); + return 0; + } + res = regexec(&preg, arg_cl->sval[arg_index], MAX_MATCH, &pmatch[0], 0); + if (res) { + asprintf(errmsg, "--%s %s: Illegal argument", + arg_cl->hdr.longopts, + arg->regex); + return 0; + } + return 1; +} + +/* Read compound options described in `arg`, from `cfg`, to `setting` */ +static int read_compounds(config_setting_t* cfg, + void* setting, + struct compound_cl_arg* arg, + char** errmsg) +{ + int arg_i; + struct arg_str* arg_cl; + regmatch_t pmatch[MAX_MATCH]; + + for (; arg->regex; arg++) { + arg_cl = *arg->arg_cl; + TRACE_CMPD(("Compound %s occurs %d : ", arg_cl->hdr.longopts, arg_cl->count)); + for (arg_i = 0; arg_i < arg_cl->count; arg_i++) { + if (!regcompmatch(&pmatch[0], arg, arg_i, errmsg)) + return 0; + TRACE_CMPD(("`%s' matched\n", arg_cl->sval[arg_i])); + + switch (arg->base_entry->type) { + case CFG_LIST: + /* In a list, find the end or the element to override */ + if (!add_arg_to_list(arg, arg_i, pmatch)) { + return 0; + } + break; + + /* Semantics for CFG_ARRAY TBD */ + + case CFG_GROUP: + if (!set_target_fields( + /* base_addr is the same for all elements in the group */ + arg->targets[0].element->base_addr, + arg, + arg_cl->sval[arg_i], + pmatch)) + return 0; + + default: + TRACE_CMPD(("error, compound on type %d\n", arg->base_entry->type)); + break; + } + } + TRACE_CMPD(("done %s\n", arg_cl->hdr.longopts)); + } + return 1; +} + +/* read config file `filename` into `c` */ +static int c2s_parse_file(const char* filename, config_t* c, char**errmsg) +{ + /* Read config file */ + config_init(c); + if (config_read_file(c, filename) == CONFIG_FALSE) { + if (config_error_line(c) != 0) { + asprintf(errmsg, "%s:%d:%s", + filename, + config_error_line(c), + config_error_text(c)); + return 0; + } + asprintf(errmsg, "%s:%s", filename, config_error_text(c)); + return 0; + } + return 1; +} + +int sslhcfg_cl_parse(int argc, char* argv[], struct sslhcfg_item* cfg) +{ + int nerrors, res; + config_t c; + char* errmsg; + config_setting_t* s; + void* argtable[] = { + sslhcfg_conffile = arg_filen("F", "conffile", "", 0, 1, "Specify configuration file"), + sslhcfg_verbose = arg_intn("v", "verbose", "", 0, 1, ""), + sslhcfg_foreground = arg_litn("f", "foreground", 0, 1, "Run in foreground instead of as a daemon"), + sslhcfg_inetd = arg_litn("i", "inetd", 0, 1, "Run in inetd mode: use stdin/stdout instead of network listen"), + sslhcfg_numeric = arg_litn("n", "numeric", 0, 1, "Print IP addresses and ports as numbers"), + sslhcfg_transparent = arg_litn(NULL, "transparent", 0, 1, "Set up as a transparent proxy"), + sslhcfg_timeout = arg_intn("t", "timeout", "", 0, 1, "Set up timeout before connecting to default target"), + sslhcfg_user = arg_strn("u", "user", "", 0, 1, "Username to change to after set-up"), + sslhcfg_pidfile = arg_strn("P", "pidfile", "", 0, 1, "Path to file to store PID of current instance"), + sslhcfg_chroot = arg_strn("C", "chroot", "", 0, 1, "Root to change to after set-up"), + sslhcfg_syslog_facility = arg_strn(NULL, "syslog-facility", "", 0, 1, "Facility to syslog to"), + sslhcfg_on_timeout = arg_strn(NULL, "on-timeout", "", 0, 1, "Target to connect to when timing out"), + sslhcfg_listen = arg_strn("p", "listen", "", 0, 10, "Listen on host:port"), + sslhcfg_ssh = arg_strn(NULL, "ssh", "", 0, 10, "Set up ssh target"), + sslhcfg_tls = arg_strn(NULL, "tls", "", 0, 10, "Set up TLS/SSL target"), + sslhcfg_openvpn = arg_strn(NULL, "openvpn", "", 0, 10, "Set up OpenVPN target"), + sslhcfg_tinc = arg_strn(NULL, "tinc", "", 0, 10, "Set up tinc target"), + sslhcfg_xmpp = arg_strn(NULL, "xmpp", "", 0, 10, "Set up XMPP target"), + sslhcfg_http = arg_strn(NULL, "http", "", 0, 10, "Set up HTTP (plain) target"), + sslhcfg_adb = arg_strn(NULL, "adb", "", 0, 10, "Set up ADB (Android Debug) target"), + sslhcfg_socks5 = arg_strn(NULL, "socks5", "", 0, 10, "Set up socks5 target"), + sslhcfg_end = arg_end(10) + + }; + + /* Parse command line */ + nerrors = arg_parse(argc, argv, argtable); + if (nerrors) { + arg_print_errors(stdout, sslhcfg_end, "sslhcfg"); + arg_print_syntax(stdout, argtable, "\\n"); + arg_print_glossary(stdout, argtable, " %-25s\t%s\n"); + return 0; + } + + + if (sslhcfg_conffile->count) { + if (!c2s_parse_file(sslhcfg_conffile->filename[0], &c, &errmsg)) { + fprintf(stderr, "%s\n", errmsg); + exit(1); + } + } + + s = config_lookup(&c, "/"); + + res = read_block(s, cfg, table_sslhcfg, &errmsg); + if (!res) { + fprintf(stderr, "%s\n", errmsg); + return res; + } + + res = read_compounds(s, cfg, compound_cl_args, &errmsg); + if (!res) { + fprintf(stderr, "%s\n", errmsg); + return res; + } + + return res; +} + + +static void indent(FILE* out, int depth) +{ + int i; + for (i = 0; i < depth; i++) + fprintf(out, " "); +} + +static void sslhcfg_protocols_fprint( + FILE* out, + struct sslhcfg_protocols_item* sslhcfg_protocols, + int depth) +{ + indent(out, depth); + fprintf(out, "name: %s", sslhcfg_protocols->name); + fprintf(out, "\n"); + indent(out, depth); + fprintf(out, "host: %s", sslhcfg_protocols->host); + fprintf(out, "\n"); + indent(out, depth); + fprintf(out, "port: %s", sslhcfg_protocols->port); + fprintf(out, "\n"); + indent(out, depth); + fprintf(out, "service: %s", sslhcfg_protocols->service); + if (! sslhcfg_protocols->service_is_present) + fprintf(out, " "); + fprintf(out, "\n"); + indent(out, depth); + fprintf(out, "fork: %d", sslhcfg_protocols->fork); + fprintf(out, "\n"); + indent(out, depth); + fprintf(out, "tfo_ok: %d", sslhcfg_protocols->tfo_ok); + fprintf(out, "\n"); + indent(out, depth); + fprintf(out, "log_level: %d", sslhcfg_protocols->log_level); + fprintf(out, "\n"); + indent(out, depth); + fprintf(out, "keepalive: %d", sslhcfg_protocols->keepalive); + fprintf(out, "\n"); + indent(out, depth); + fprintf(out, "sni_hostnames [%zu]:\n", sslhcfg_protocols->sni_hostnames_len); + for (int i = 0; i < sslhcfg_protocols->sni_hostnames_len; i++) { + indent(out, depth+1); + fprintf(out, "%d:\t%s\n", i, sslhcfg_protocols->sni_hostnames[i]); + } + indent(out, depth); + fprintf(out, "alpn_protocols [%zu]:\n", sslhcfg_protocols->alpn_protocols_len); + for (int i = 0; i < sslhcfg_protocols->alpn_protocols_len; i++) { + indent(out, depth+1); + fprintf(out, "%d:\t%s\n", i, sslhcfg_protocols->alpn_protocols[i]); + } + indent(out, depth); + fprintf(out, "regex_patterns [%zu]:\n", sslhcfg_protocols->regex_patterns_len); + for (int i = 0; i < sslhcfg_protocols->regex_patterns_len; i++) { + indent(out, depth+1); + fprintf(out, "%d:\t%s\n", i, sslhcfg_protocols->regex_patterns[i]); + } + indent(out, depth); + fprintf(out, "minlength: %d", sslhcfg_protocols->minlength); + if (! sslhcfg_protocols->minlength_is_present) + fprintf(out, " "); + fprintf(out, "\n"); +} + +static void sslhcfg_listen_fprint( + FILE* out, + struct sslhcfg_listen_item* sslhcfg_listen, + int depth) +{ + indent(out, depth); + fprintf(out, "host: %s", sslhcfg_listen->host); + fprintf(out, "\n"); + indent(out, depth); + fprintf(out, "port: %s", sslhcfg_listen->port); + fprintf(out, "\n"); + indent(out, depth); + fprintf(out, "keepalive: %d", sslhcfg_listen->keepalive); + fprintf(out, "\n"); +} + +void sslhcfg_fprint( + FILE* out, + struct sslhcfg_item* sslhcfg, + int depth) +{ + indent(out, depth); + fprintf(out, "verbose: %d", sslhcfg->verbose); + fprintf(out, "\n"); + indent(out, depth); + fprintf(out, "foreground: %d", sslhcfg->foreground); + fprintf(out, "\n"); + indent(out, depth); + fprintf(out, "inetd: %d", sslhcfg->inetd); + fprintf(out, "\n"); + indent(out, depth); + fprintf(out, "numeric: %d", sslhcfg->numeric); + fprintf(out, "\n"); + indent(out, depth); + fprintf(out, "transparent: %d", sslhcfg->transparent); + fprintf(out, "\n"); + indent(out, depth); + fprintf(out, "timeout: %d", sslhcfg->timeout); + fprintf(out, "\n"); + indent(out, depth); + fprintf(out, "user: %s", sslhcfg->user); + if (! sslhcfg->user_is_present) + fprintf(out, " "); + fprintf(out, "\n"); + indent(out, depth); + fprintf(out, "pidfile: %s", sslhcfg->pidfile); + if (! sslhcfg->pidfile_is_present) + fprintf(out, " "); + fprintf(out, "\n"); + indent(out, depth); + fprintf(out, "chroot: %s", sslhcfg->chroot); + if (! sslhcfg->chroot_is_present) + fprintf(out, " "); + fprintf(out, "\n"); + indent(out, depth); + fprintf(out, "syslog_facility: %s", sslhcfg->syslog_facility); + fprintf(out, "\n"); + indent(out, depth); + fprintf(out, "on_timeout: %s", sslhcfg->on_timeout); + fprintf(out, "\n"); + + indent(out, depth); + fprintf(out, "listen [%zu]:\n", sslhcfg->listen_len); + for (int i = 0; i < sslhcfg->listen_len; i++) { + sslhcfg_listen_fprint(out, &sslhcfg->listen[i], depth+1); + } + + indent(out, depth); + fprintf(out, "protocols [%zu]:\n", sslhcfg->protocols_len); + for (int i = 0; i < sslhcfg->protocols_len; i++) { + sslhcfg_protocols_fprint(out, &sslhcfg->protocols[i], depth+1); + } +} diff --git a/sslh-conf.h b/sslh-conf.h new file mode 100644 index 0000000..44ba818 --- /dev/null +++ b/sslh-conf.h @@ -0,0 +1,106 @@ +/* Generated by conf2struct (https://www.rutschle.net/tech/conf2struct/README) + * on Fri Dec 27 18:18:30 2019. + +# conf2struct: generate libconf parsers that read to structs +# Copyright (C) 2018-2019 Yves Rutschle +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef C2S_SSLHCFG_H +#define C2S_SSLHCFG_H +#include + + +#include "probe.h" +#include +#include +#include + +struct sslhcfg_listen_item { + char* host; + char* port; + int keepalive; +}; + +struct sslhcfg_protocols_item { + char* name; + char* host; + char* port; + int service_is_present; + char* service; + int fork; + int tfo_ok; + int log_level; + int keepalive; + size_t sni_hostnames_len; + char** sni_hostnames; + size_t alpn_protocols_len; + char** alpn_protocols; + size_t regex_patterns_len; + char** regex_patterns; + int minlength_is_present; + int minlength; + T_PROBE* probe; + struct addrinfo* saddr; + void* data; +}; + +struct sslhcfg_item { + int verbose; + int foreground; + int inetd; + int numeric; + int transparent; + int timeout; + int user_is_present; + char* user; + int pidfile_is_present; + char* pidfile; + int chroot_is_present; + char* chroot; + char* syslog_facility; + char* on_timeout; + size_t listen_len; + struct sslhcfg_listen_item* listen; + size_t protocols_len; + struct sslhcfg_protocols_item* protocols; +}; + +int sslhcfg_parse_file( + const char* filename, + struct sslhcfg_item* sslhcfg, + const char** errmsg); + +void sslhcfg_fprint( + FILE* out, + struct sslhcfg_item *sslhcfg, + int depth); + +int sslhcfg_cl_parse( + int argc, + char* argv[], + struct sslhcfg_item *sslhcfg); + +#endif