/* Generated by conf2struct (https://www.rutschle.net/tech/conf2struct/README) * on Sun Sep 11 21:43:25 2022. # conf2struct: generate libconf parsers that read to structs # Copyright (C) 2018-2021 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 #ifdef LIBCONFIG # include #endif #include #include #include #include "sslh-conf.h" #include "argtable3.h" #ifdef LIBPCRE #define PCRE2_CODE_UNIT_WIDTH 8 #include typedef struct { PCRE2_SIZE rm_so; PCRE2_SIZE rm_eo; } regmatch_t; #else #include #endif /* This gets included in the output .c file */ /* Libconfig 1.4.9 is still used by major distributions * (e.g. CentOS7) and had a different name for * config_setting_lookup */ #if LIBCONFIG_VER_MAJOR == 1 #if LIBCONFIG_VER_MINOR == 4 #if LIBCONFIG_VER_REVISION == 9 #define config_setting_lookup config_lookup_from #endif #endif #endif /* 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; /* /config_type */ const char* type2str[] = { "boolean", "int", "int64", "float", "string", "group", "array", "list", }; typedef union { int def_bool; int def_int; long long def_int64; double def_float; char* def_string; } any_val; 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; }; #ifndef LIBCONFIG /* Stubs in case you don't want libconfig */ typedef void config_setting_t; typedef int config_t; #define CONFIG_TRUE 1 #define CONFIG_FALSE 0 #define make_config_setting_lookup(type) \ int config_setting_lookup_##type(const config_setting_t* a, const char* b, void* c) { \ return 0; \ } #define make_config_setting_get(type, ret_type) \ ret_type config_setting_get_##type(const config_setting_t* a) { \ return 0; \ } enum { CONFIG_TYPE_INT, CONFIG_TYPE_BOOL, CONFIG_TYPE_INT64, CONFIG_TYPE_FLOAT, CONFIG_TYPE_STRING }; make_config_setting_lookup(bool); make_config_setting_lookup(int); make_config_setting_lookup(int64); make_config_setting_lookup(float); make_config_setting_lookup(string); make_config_setting_get(bool, int); make_config_setting_get(int, int); make_config_setting_get(int64, long long int); make_config_setting_get(float, double); make_config_setting_get(string, char*); config_setting_t* config_root_setting(config_t* c) { return NULL; } config_setting_t* config_lookup(config_t* c, const char* b) { return NULL; } void config_init(config_t* c) { return; } char* config_setting_name(config_setting_t* c) { return NULL; } int config_setting_is_list(config_setting_t* c) { return 0; } int config_setting_is_array(config_setting_t* c) { return 0; } int config_setting_is_scalar(config_setting_t* c) { return 0; } int config_setting_index(const config_setting_t *setting) { return 0; } config_setting_t* config_setting_lookup(config_setting_t* a, char* b) { return NULL; } int config_setting_remove(config_setting_t* cfg, char* name) { return 0; } int config_setting_type(config_setting_t* s) { return -1; } int config_setting_length(config_setting_t* a) { return 0; } config_setting_t* config_setting_get_elem(config_setting_t* a, int i) { return NULL; } int config_read_file(config_t* a, const char* b) { return CONFIG_TRUE; } int config_error_line(config_t* c) { return 0; } char* config_error_text(config_t* c) { return NULL; } #endif /* This is the same as config_setting_lookup_string() except it allocates a new string which belongs to the caller */ static int myconfig_setting_lookup_stringcpy( const config_setting_t* setting, const char* name, char** value) { const char* str; *value = NULL; if (config_setting_lookup_string(setting, name, &str) == CONFIG_TRUE) { asprintf(value, "%s", str); return CONFIG_TRUE; } else { return CONFIG_FALSE; } } 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)myconfig_setting_lookup_stringcpy, NULL, /* CFG_GROUP */ NULL, /* CFG_ARRAY */ NULL, /* CFG_LIST */ }; /* Copy an any_val to arbitrary memory location */ /* 0: success * <0: error */ static int 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); return -1; } return 1; } /* Copy the value of a setting to an arbitrary memory that * must be large enough */ /* 0: success * <0: error */ static int 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); return -1; } return 0; } /* Copy the value of a command line arg to arbitrary memory * that must be large enough for the type */ /* 0: success * <0: error */ static int 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); return -1; } return 0; } /* Copy the value of a string argument to arbitrary memory * location that must be large enough, converting on the way * (i.e. CFG_INT gets atoi() and so on) */ /* 0: success * <0: error */ static int 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); return -1; } return 0; } /* 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_config; struct arg_int* sslhcfg_verbose_config_error; struct arg_int* sslhcfg_verbose_connections; struct arg_int* sslhcfg_verbose_connections_try; struct arg_int* sslhcfg_verbose_connections_error; struct arg_int* sslhcfg_verbose_fd; struct arg_int* sslhcfg_verbose_packets; struct arg_int* sslhcfg_verbose_probe_info; struct arg_int* sslhcfg_verbose_probe_error; struct arg_int* sslhcfg_verbose_system_error; struct arg_int* sslhcfg_verbose_int_error; struct arg_lit* sslhcfg_version; 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_int* sslhcfg_udp_max_connections; 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_logfile; struct arg_str* sslhcfg_on_timeout; struct arg_str* sslhcfg_prefix; 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_wireguard; struct arg_str* sslhcfg_xmpp; struct arg_str* sslhcfg_http; struct arg_str* sslhcfg_adb; struct arg_str* sslhcfg_socks5; struct arg_str* sslhcfg_syslog; struct arg_str* sslhcfg_msrdp; struct arg_str* sslhcfg_anyprot; 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 */ "is_udp", /* type */ CFG_BOOL, /* sub_group*/ NULL, /* arg_cl */ NULL, /* base_addr */ NULL, /* offset */ offsetof(struct sslhcfg_protocols_item, is_udp), /* offset_len */ 0, /* offset_present */ 0, /* size */ sizeof(int), /* array_type */ -1, /* mandatory */ 0, /* optional */ 0, /* default_val*/ .default_val.def_bool = 0 }, { /* name */ "udp_timeout", /* type */ CFG_INT, /* sub_group*/ NULL, /* arg_cl */ NULL, /* base_addr */ NULL, /* offset */ offsetof(struct sslhcfg_protocols_item, udp_timeout), /* offset_len */ 0, /* offset_present */ 0, /* size */ sizeof(int), /* array_type */ -1, /* mandatory */ 0, /* optional */ 0, /* default_val*/ .default_val.def_int = 60 }, { /* 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 */ "transparent", /* type */ CFG_BOOL, /* sub_group*/ NULL, /* arg_cl */ NULL, /* base_addr */ NULL, /* offset */ offsetof(struct sslhcfg_protocols_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 */ "resolve_on_forward", /* type */ CFG_BOOL, /* sub_group*/ NULL, /* arg_cl */ NULL, /* base_addr */ NULL, /* offset */ offsetof(struct sslhcfg_protocols_item, resolve_on_forward), /* 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 */ "is_udp", /* type */ CFG_BOOL, /* sub_group*/ NULL, /* arg_cl */ NULL, /* base_addr */ NULL, /* offset */ offsetof(struct sslhcfg_listen_item, is_udp), /* offset_len */ 0, /* offset_present */ 0, /* size */ sizeof(int), /* array_type */ -1, /* mandatory */ 0, /* optional */ 0, /* default_val*/ .default_val.def_bool = 0 }, { /* 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_config", /* type */ CFG_INT, /* sub_group*/ NULL, /* arg_cl */ & sslhcfg_verbose_config, /* base_addr */ NULL, /* offset */ offsetof(struct sslhcfg_item, verbose_config), /* offset_len */ 0, /* offset_present */ 0, /* size */ sizeof(int), /* array_type */ -1, /* mandatory */ 0, /* optional */ 0, /* default_val*/ .default_val.def_int = 0 }, { /* name */ "verbose_config_error", /* type */ CFG_INT, /* sub_group*/ NULL, /* arg_cl */ & sslhcfg_verbose_config_error, /* base_addr */ NULL, /* offset */ offsetof(struct sslhcfg_item, verbose_config_error), /* offset_len */ 0, /* offset_present */ 0, /* size */ sizeof(int), /* array_type */ -1, /* mandatory */ 0, /* optional */ 0, /* default_val*/ .default_val.def_int = 3 }, { /* name */ "verbose_connections", /* type */ CFG_INT, /* sub_group*/ NULL, /* arg_cl */ & sslhcfg_verbose_connections, /* base_addr */ NULL, /* offset */ offsetof(struct sslhcfg_item, verbose_connections), /* offset_len */ 0, /* offset_present */ 0, /* size */ sizeof(int), /* array_type */ -1, /* mandatory */ 0, /* optional */ 0, /* default_val*/ .default_val.def_int = 3 }, { /* name */ "verbose_connections_try", /* type */ CFG_INT, /* sub_group*/ NULL, /* arg_cl */ & sslhcfg_verbose_connections_try, /* base_addr */ NULL, /* offset */ offsetof(struct sslhcfg_item, verbose_connections_try), /* offset_len */ 0, /* offset_present */ 0, /* size */ sizeof(int), /* array_type */ -1, /* mandatory */ 0, /* optional */ 0, /* default_val*/ .default_val.def_int = 0 }, { /* name */ "verbose_connections_error", /* type */ CFG_INT, /* sub_group*/ NULL, /* arg_cl */ & sslhcfg_verbose_connections_error, /* base_addr */ NULL, /* offset */ offsetof(struct sslhcfg_item, verbose_connections_error), /* offset_len */ 0, /* offset_present */ 0, /* size */ sizeof(int), /* array_type */ -1, /* mandatory */ 0, /* optional */ 0, /* default_val*/ .default_val.def_int = 3 }, { /* name */ "verbose_fd", /* type */ CFG_INT, /* sub_group*/ NULL, /* arg_cl */ & sslhcfg_verbose_fd, /* base_addr */ NULL, /* offset */ offsetof(struct sslhcfg_item, verbose_fd), /* offset_len */ 0, /* offset_present */ 0, /* size */ sizeof(int), /* array_type */ -1, /* mandatory */ 0, /* optional */ 0, /* default_val*/ .default_val.def_int = 0 }, { /* name */ "verbose_packets", /* type */ CFG_INT, /* sub_group*/ NULL, /* arg_cl */ & sslhcfg_verbose_packets, /* base_addr */ NULL, /* offset */ offsetof(struct sslhcfg_item, verbose_packets), /* offset_len */ 0, /* offset_present */ 0, /* size */ sizeof(int), /* array_type */ -1, /* mandatory */ 0, /* optional */ 0, /* default_val*/ .default_val.def_int = 0 }, { /* name */ "verbose_probe_info", /* type */ CFG_INT, /* sub_group*/ NULL, /* arg_cl */ & sslhcfg_verbose_probe_info, /* base_addr */ NULL, /* offset */ offsetof(struct sslhcfg_item, verbose_probe_info), /* offset_len */ 0, /* offset_present */ 0, /* size */ sizeof(int), /* array_type */ -1, /* mandatory */ 0, /* optional */ 0, /* default_val*/ .default_val.def_int = 0 }, { /* name */ "verbose_probe_error", /* type */ CFG_INT, /* sub_group*/ NULL, /* arg_cl */ & sslhcfg_verbose_probe_error, /* base_addr */ NULL, /* offset */ offsetof(struct sslhcfg_item, verbose_probe_error), /* offset_len */ 0, /* offset_present */ 0, /* size */ sizeof(int), /* array_type */ -1, /* mandatory */ 0, /* optional */ 0, /* default_val*/ .default_val.def_int = 3 }, { /* name */ "verbose_system_error", /* type */ CFG_INT, /* sub_group*/ NULL, /* arg_cl */ & sslhcfg_verbose_system_error, /* base_addr */ NULL, /* offset */ offsetof(struct sslhcfg_item, verbose_system_error), /* offset_len */ 0, /* offset_present */ 0, /* size */ sizeof(int), /* array_type */ -1, /* mandatory */ 0, /* optional */ 0, /* default_val*/ .default_val.def_int = 3 }, { /* name */ "verbose_int_error", /* type */ CFG_INT, /* sub_group*/ NULL, /* arg_cl */ & sslhcfg_verbose_int_error, /* base_addr */ NULL, /* offset */ offsetof(struct sslhcfg_item, verbose_int_error), /* offset_len */ 0, /* offset_present */ 0, /* size */ sizeof(int), /* array_type */ -1, /* mandatory */ 0, /* optional */ 0, /* default_val*/ .default_val.def_int = 3 }, { /* name */ "version", /* type */ CFG_BOOL, /* sub_group*/ NULL, /* arg_cl */ & sslhcfg_version, /* base_addr */ NULL, /* offset */ offsetof(struct sslhcfg_item, version), /* offset_len */ 0, /* offset_present */ 0, /* size */ sizeof(int), /* array_type */ -1, /* mandatory */ 0, /* optional */ 0, /* default_val*/ .default_val.def_bool = 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 */ "udp_max_connections", /* type */ CFG_INT, /* sub_group*/ NULL, /* arg_cl */ & sslhcfg_udp_max_connections, /* base_addr */ NULL, /* offset */ offsetof(struct sslhcfg_item, udp_max_connections), /* offset_len */ 0, /* offset_present */ 0, /* size */ sizeof(int), /* array_type */ -1, /* mandatory */ 0, /* optional */ 0, /* default_val*/ .default_val.def_int = 1024 }, { /* 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 */ "logfile", /* type */ CFG_STRING, /* sub_group*/ NULL, /* arg_cl */ & sslhcfg_logfile, /* base_addr */ NULL, /* offset */ offsetof(struct sslhcfg_item, logfile), /* offset_len */ 0, /* offset_present */ offsetof(struct sslhcfg_item, logfile_is_present), /* size */ sizeof(char*), /* array_type */ -1, /* mandatory */ 0, /* optional */ 1, /* default_val*/ .default_val.def_string = NULL }, { /* 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 */ "prefix", /* type */ CFG_STRING, /* sub_group*/ NULL, /* arg_cl */ & sslhcfg_prefix, /* base_addr */ NULL, /* offset */ offsetof(struct sslhcfg_item, prefix), /* offset_len */ 0, /* offset_present */ 0, /* size */ sizeof(char*), /* array_type */ -1, /* mandatory */ 0, /* optional */ 0, /* default_val*/ .default_val.def_string = "" }, { /* 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_anyprot_targets [] = { { & table_sslhcfg_protocols[0], 0, .value.def_string = "anyprot" }, { & table_sslhcfg_protocols[1], 1, .value.def_string = "0" }, { & table_sslhcfg_protocols[2], 2, .value.def_string = "0" }, { & table_sslhcfg_protocols[10], 0, .value.def_int = 1 }, { 0 } }; static struct compound_cl_target sslhcfg_msrdp_targets [] = { { & table_sslhcfg_protocols[0], 0, .value.def_string = "msrdp" }, { & table_sslhcfg_protocols[1], 1, .value.def_string = "0" }, { & table_sslhcfg_protocols[2], 2, .value.def_string = "0" }, { & table_sslhcfg_protocols[10], 0, .value.def_int = 1 }, { 0 } }; static struct compound_cl_target sslhcfg_syslog_targets [] = { { & table_sslhcfg_protocols[0], 0, .value.def_string = "syslog" }, { & table_sslhcfg_protocols[1], 1, .value.def_string = "0" }, { & table_sslhcfg_protocols[2], 2, .value.def_string = "0" }, { & table_sslhcfg_protocols[10], 0, .value.def_int = 1 }, { 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" }, { & table_sslhcfg_protocols[10], 0, .value.def_int = 1 }, { 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" }, { & table_sslhcfg_protocols[10], 0, .value.def_int = 1 }, { 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" }, { & table_sslhcfg_protocols[10], 0, .value.def_int = 1 }, { 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" }, { & table_sslhcfg_protocols[10], 0, .value.def_int = 1 }, { 0 } }; static struct compound_cl_target sslhcfg_wireguard_targets [] = { { & table_sslhcfg_protocols[0], 0, .value.def_string = "wireguard" }, { & table_sslhcfg_protocols[1], 1, .value.def_string = "0" }, { & table_sslhcfg_protocols[2], 2, .value.def_string = "0" }, { & table_sslhcfg_protocols[10], 0, .value.def_int = 1 }, { & table_sslhcfg_protocols[7], 0, .value.def_bool = 1 }, { 0 } }; static struct compound_cl_target sslhcfg_tinc_targets [] = { { & table_sslhcfg_protocols[0], 0, .value.def_string = "tinc" }, { & table_sslhcfg_protocols[1], 1, .value.def_string = "0" }, { & table_sslhcfg_protocols[2], 2, .value.def_string = "0" }, { & table_sslhcfg_protocols[10], 0, .value.def_int = 1 }, { & table_sslhcfg_protocols[7], 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[10], 0, .value.def_int = 1 }, { & table_sslhcfg_protocols[7], 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[10], 0, .value.def_int = 1 }, { & table_sslhcfg_protocols[7], 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[6], 0, .value.def_bool = 1 }, { & table_sslhcfg_protocols[10], 0, .value.def_int = 1 }, { & table_sslhcfg_protocols[7], 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 [25], .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 [26], .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 [26], .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 [26], .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 [26], .targets = sslhcfg_tinc_targets, .override_desc = & table_sslhcfg_protocols [0], .override_matchindex = 0, .override_const = "tinc", }, { /* arg: wireguard */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_wireguard, .base_entry = & table_sslhcfg [26], .targets = sslhcfg_wireguard_targets, .override_desc = & table_sslhcfg_protocols [0], .override_matchindex = 0, .override_const = "wireguard", }, { /* arg: xmpp */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_xmpp, .base_entry = & table_sslhcfg [26], .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 [26], .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 [26], .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 [26], .targets = sslhcfg_socks5_targets, .override_desc = & table_sslhcfg_protocols [0], .override_matchindex = 0, .override_const = "socks5", }, { /* arg: syslog */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_syslog, .base_entry = & table_sslhcfg [26], .targets = sslhcfg_syslog_targets, .override_desc = & table_sslhcfg_protocols [0], .override_matchindex = 0, .override_const = "syslog", }, { /* arg: msrdp */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_msrdp, .base_entry = & table_sslhcfg [26], .targets = sslhcfg_msrdp_targets, .override_desc = & table_sslhcfg_protocols [0], .override_matchindex = 0, .override_const = "msrdp", }, { /* arg: anyprot */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_anyprot, .base_entry = & table_sslhcfg [26], .targets = sslhcfg_anyprot_targets, .override_desc = & table_sslhcfg_protocols [0], .override_matchindex = 0, .override_const = "anyprot", }, { 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; } } } /* Changes all dashes to underscores in a string of * vice-versa */ static void strswap_ud(const char target, char* str) { char* c; for (c = str; *c; c++) if (*c == (target == '_' ? '-' : '_')) *c = (target == '_' ? '_' : '-'); } /* Same as config_setting_lookup() but looks up with dash or * underscore so `my_setting` and `my-setting` match the same */ static config_setting_t* config_setting_lookup_ud(config_setting_t* cfg, struct config_desc* desc) { config_setting_t* setting; char name[strlen(desc->name)+1];; strcpy(name, desc->name); strswap_ud('_', name); setting = config_setting_lookup(cfg, name); if (setting) return setting; strswap_ud('-', name); setting = config_setting_lookup(cfg, name); return setting; } static int lookup_typed_ud(config_setting_t* cfg, void* target, struct config_desc *desc) { lookup_fn lookup_fn = lookup_fns[desc->type]; char name[strlen(desc->name)+1];; strcpy(name, desc->name); strswap_ud('_', name); if (lookup_fn(cfg, name, ((char*)target) + desc->offset) == CONFIG_TRUE) return CONFIG_TRUE; strswap_ud('-', name); return lookup_fn(cfg, name, ((char*)target) + desc->offset); } /* Removes a setting, trying both underscores and dashes as * name (so deleting 'my-setting' deletes both 'my_setting' * and 'my-setting') */ static int setting_delete_ud(config_setting_t* cfg, struct config_desc *desc) { char name[strlen(desc->name)+1];; strcpy(name, desc->name); strswap_ud('_', name); if (config_setting_remove(cfg, name) == CONFIG_TRUE) return CONFIG_TRUE; strswap_ud('-', name); return config_setting_remove(cfg, name); } /* 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_ud(cfg, desc); if (setting) len = config_setting_length(setting); } block = calloc(len, desc->size); *(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 = calloc(1, 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; 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_ud(cfg, desc); 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_ud(cfg, desc); 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")); } setting_delete_ud(cfg, desc); } break; case CFG_GROUP: if (cfg) setting = config_setting_lookup_ud(cfg, desc); 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_ud(cfg, desc)) { TRACE_READ((" in config file: ")); /* setting is present in cfg, look it up */ if (lookup_typed_ud(cfg, target, desc) != 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)); setting_delete_ud(cfg, desc); 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 * discrepancy 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 #ifndef LIBPCRE static int regcompmatch_posix( 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; } #endif #ifdef LIBPCRE static int regcompmatch_pcre2( regmatch_t* pmatch, struct compound_cl_arg* arg, int arg_index, char** errmsg) { int i, error; pcre2_code* pcre; PCRE2_UCHAR8 err_str[120]; /* ample, according to pcre2api(3) */ pcre2_match_data* matches; PCRE2_SIZE error_offset; struct arg_str* arg_cl = *arg->arg_cl; pcre = pcre2_compile((PCRE2_SPTR8)arg->regex, PCRE2_ZERO_TERMINATED, 0, &error, &error_offset, NULL); if (!pcre) { pcre2_get_error_message(error, err_str, sizeof(err_str)); asprintf(errmsg, "compiling pattern /%s/:%d: %s at offset %ld\n", arg->regex, error, err_str, error_offset); return 0; } matches = pcre2_match_data_create(MAX_MATCH, NULL); int res = pcre2_match(pcre, (PCRE2_SPTR8)arg_cl->sval[arg_index], PCRE2_ZERO_TERMINATED, 0, 0, matches, NULL); if (res < 0) { pcre2_get_error_message(res, err_str, sizeof(err_str)); asprintf(errmsg, "matching %s =~ /%s/:%d: %s\n", arg_cl->sval[arg_index], arg->regex, res, err_str); return 0; } PCRE2_SIZE *ovec = pcre2_get_ovector_pointer(matches); if (res > MAX_MATCH) res = MAX_MATCH; /* From pcre2posix.c */ for (i = 0; i < res; i++) { pmatch[i].rm_so = (ovec[i*2] == PCRE2_UNSET) ? -1 : ovec[i*2]; pmatch[i].rm_eo = (ovec[i*2+1] == PCRE2_UNSET) ? -1 : ovec[i*2+1]; } for (; i < MAX_MATCH; i++) pmatch[i].rm_so = pmatch[i].rm_eo = -1; pcre2_match_data_free(matches); return 1; } #endif /* 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) { #if LIBPCRE return regcompmatch_pcre2(pmatch, arg, arg_index, errmsg); #else return regcompmatch_posix(pmatch, arg, arg_index, errmsg); #endif } /* 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 */ 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; } /* Allocates a new string that represents the setting value, which must be a scalar */ static void scalar_to_string(char** strp, config_setting_t* s) { switch(config_setting_type(s)) { case CONFIG_TYPE_INT: asprintf(strp, "%d\n", config_setting_get_int(s)); break; case CONFIG_TYPE_BOOL: asprintf(strp, "%s\n", config_setting_get_bool(s) ? "[true]" : "[false]" ); break; case CONFIG_TYPE_INT64: asprintf(strp, "%lld\n", config_setting_get_int64(s)); break; case CONFIG_TYPE_FLOAT: asprintf(strp, "%lf\n", config_setting_get_float(s)); break; case CONFIG_TYPE_STRING: asprintf(strp, "%s\n", config_setting_get_string(s)); break; default: /* This means a bug */ fprintf(stderr, "Unexpected type %d\n", config_setting_type(s)); exit(1); } } /* Typesets all the settings in a configuration as a * newly-allocated string. The string management is caller's * responsibility. * Returns the number of scalars in the configuration */ static int cfg_as_string(config_setting_t* parent, const char* path, char** strp) { int i, len, res = 0; config_setting_t* child; char* subpath, *value, *old; const char* name; len = config_setting_length(parent); for (i = 0; i < len; i++) { child = config_setting_get_elem(parent, i); name = config_setting_name(child); if (!name) name = ""; if(config_setting_is_list(parent) || config_setting_is_array(parent)) { asprintf(&subpath, "%s[%d]%s", path, config_setting_index(child), name); } else { asprintf(&subpath, "%s/%s", path, name); } if (config_setting_is_scalar(child)) { scalar_to_string(&value, child); /* Add value to the output string */ if (*strp) { asprintf(&old, "%s", *strp); free(*strp); } else { asprintf(&old, "%s", ""); } asprintf(strp, "%s%s:%s", old, subpath, value); free(value); free(old); res++; /* At least one scalar was found */ } else { /* It's an aggregate -- descend into it */ res += cfg_as_string(child, subpath, strp); } free(subpath); } return res; } /* 0: success <0: error */ 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[] = { #ifdef LIBCONFIG sslhcfg_conffile = arg_filen("F", "config", "", 0, 1, "Specify configuration file"), #endif sslhcfg_verbose_config = arg_intn(NULL, "verbose-config", "", 0, 1, "Print configuration at startup"), sslhcfg_verbose_config_error = arg_intn(NULL, "verbose-config-error", "", 0, 1, "Print configuration errors"), sslhcfg_verbose_connections = arg_intn(NULL, "verbose-connections", "", 0, 1, "Trace established incoming address to forward address"), sslhcfg_verbose_connections_try = arg_intn(NULL, "verbose-connections-try", "", 0, 1, "Connection errors"), sslhcfg_verbose_connections_error = arg_intn(NULL, "verbose-connections-error", "", 0, 1, "Connection attempts towards targets"), sslhcfg_verbose_fd = arg_intn(NULL, "verbose-fd", "", 0, 1, "File descriptor activity, open/close/whatnot"), sslhcfg_verbose_packets = arg_intn(NULL, "verbose-packets", "", 0, 1, "Hexdump packets on which probing is done"), sslhcfg_verbose_probe_info = arg_intn(NULL, "verbose-probe-info", "", 0, 1, "Trace the probe process"), sslhcfg_verbose_probe_error = arg_intn(NULL, "verbose-probe-error", "", 0, 1, "Failures and problems during probing"), sslhcfg_verbose_system_error = arg_intn(NULL, "verbose-system-error", "", 0, 1, "System call failures"), sslhcfg_verbose_int_error = arg_intn(NULL, "verbose-int-error", "", 0, 1, "Internal errors that should never happen"), sslhcfg_version = arg_litn("V", "version", 0, 1, "Print version information and exit"), 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_udp_max_connections = arg_intn(NULL, "udp-max-connections", "", 0, 1, "Number of concurrent UDP connections"), 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_logfile = arg_strn(NULL, "logfile", "", 0, 1, "Log messages to a file"), sslhcfg_on_timeout = arg_strn(NULL, "on-timeout", "", 0, 1, "Target to connect to when timing out"), sslhcfg_prefix = arg_strn(NULL, "prefix", "", 0, 1, "Reserved for testing"), 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_wireguard = arg_strn(NULL, "wireguard", "", 0, 10, "Set up WireGuard 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_syslog = arg_strn(NULL, "syslog", "", 0, 10, "Set up syslog target"), sslhcfg_msrdp = arg_strn(NULL, "msrdp", "", 0, 10, "Set up msrdp target"), sslhcfg_anyprot = arg_strn(NULL, "anyprot", "", 0, 10, "Set up default 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 -1; } config_init(&c); if (sslhcfg_conffile && sslhcfg_conffile->count) { if (!c2s_parse_file(sslhcfg_conffile->filename[0], &c, &errmsg)) { fprintf(stderr, "%s\n", errmsg); return -1; } } s = config_root_setting(&c); res = read_block(s, cfg, table_sslhcfg, &errmsg); if (!res) { fprintf(stderr, "%s\n", errmsg); return -1; } res = read_compounds(s, cfg, compound_cl_args, &errmsg); if (!res) { fprintf(stderr, "%s\n", errmsg); return -1; } errmsg = NULL; res = cfg_as_string(s, "", &errmsg); if (res) fprintf(stderr, "Unknown settings:\n%s\n", errmsg); return 0; } 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) { int i; 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, "is_udp: %d", sslhcfg_protocols->is_udp); fprintf(out, "\n"); indent(out, depth); fprintf(out, "udp_timeout: %d", sslhcfg_protocols->udp_timeout); 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, "transparent: %d", sslhcfg_protocols->transparent); fprintf(out, "\n"); indent(out, depth); fprintf(out, "resolve_on_forward: %d", sslhcfg_protocols->resolve_on_forward); 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 (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 (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 (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, "is_udp: %d", sslhcfg_listen->is_udp); 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) { int i; indent(out, depth); fprintf(out, "verbose_config: %d", sslhcfg->verbose_config); fprintf(out, "\n"); indent(out, depth); fprintf(out, "verbose_config_error: %d", sslhcfg->verbose_config_error); fprintf(out, "\n"); indent(out, depth); fprintf(out, "verbose_connections: %d", sslhcfg->verbose_connections); fprintf(out, "\n"); indent(out, depth); fprintf(out, "verbose_connections_try: %d", sslhcfg->verbose_connections_try); fprintf(out, "\n"); indent(out, depth); fprintf(out, "verbose_connections_error: %d", sslhcfg->verbose_connections_error); fprintf(out, "\n"); indent(out, depth); fprintf(out, "verbose_fd: %d", sslhcfg->verbose_fd); fprintf(out, "\n"); indent(out, depth); fprintf(out, "verbose_packets: %d", sslhcfg->verbose_packets); fprintf(out, "\n"); indent(out, depth); fprintf(out, "verbose_probe_info: %d", sslhcfg->verbose_probe_info); fprintf(out, "\n"); indent(out, depth); fprintf(out, "verbose_probe_error: %d", sslhcfg->verbose_probe_error); fprintf(out, "\n"); indent(out, depth); fprintf(out, "verbose_system_error: %d", sslhcfg->verbose_system_error); fprintf(out, "\n"); indent(out, depth); fprintf(out, "verbose_int_error: %d", sslhcfg->verbose_int_error); fprintf(out, "\n"); indent(out, depth); fprintf(out, "version: %d", sslhcfg->version); 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, "udp_max_connections: %d", sslhcfg->udp_max_connections); 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, "logfile: %s", sslhcfg->logfile); if (! sslhcfg->logfile_is_present) fprintf(out, " "); fprintf(out, "\n"); indent(out, depth); fprintf(out, "on_timeout: %s", sslhcfg->on_timeout); fprintf(out, "\n"); indent(out, depth); fprintf(out, "prefix: %s", sslhcfg->prefix); fprintf(out, "\n"); indent(out, depth); fprintf(out, "listen [%zu]:\n", sslhcfg->listen_len); for (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 (i = 0; i < sslhcfg->protocols_len; i++) { sslhcfg_protocols_fprint(out, &sslhcfg->protocols[i], depth+1); } }