From fa848f2ae9cc26ed61e77863b0c7be1814fabc79 Mon Sep 17 00:00:00 2001 From: yrutschle Date: Tue, 24 Aug 2021 13:38:18 +0200 Subject: [PATCH 001/191] do not timeout TCP connections (fix #300) --- sslh-select.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/sslh-select.c b/sslh-select.c index b5c7301..59acdc3 100644 --- a/sslh-select.c +++ b/sslh-select.c @@ -475,15 +475,16 @@ static void udp_timeouts(struct select_info* fd_info) if (now < fd_info->next_timeout) return; - for (int i = 0; i < fd_info->max_fd; i++) { - time_t next_timeout = INT_MAX; + time_t next_timeout = INT_MAX; + for (int i = 0; i < fd_info->max_fd; i++) { /* if it's either in read or write set, there is a connection * behind that file descriptor */ if (FD_ISSET(i, &fd_info->fds_r) || FD_ISSET(i, &fd_info->fds_w)) { struct connection* cnx = collection_get_cnx_from_fd(fd_info->collection, i); if (cnx) { time_t timeout = udp_timeout(cnx); + if (!timeout) continue; /* Not a UDP connection */ if (cnx && (timeout <= now)) { if (cfg.verbose > 3) fprintf(stderr, "timed out UDP %d\n", cnx->target_sock); @@ -496,10 +497,10 @@ static void udp_timeouts(struct select_info* fd_info) } } } - - if (next_timeout != INT_MAX) - fd_info->next_timeout = next_timeout; } + + if (next_timeout != INT_MAX) + fd_info->next_timeout = next_timeout; } /* Main loop: the idea is as follow: From 4a6bbda60d18bc39b1ad13066168ab81220b76e1 Mon Sep 17 00:00:00 2001 From: yrutschle Date: Tue, 24 Aug 2021 14:10:14 +0200 Subject: [PATCH 002/191] remove obsolete usage string and added lost version option --- common.h | 1 - sslh-conf.c | 47 ++++++++++++++++++++++++++++++++++------------- sslh-conf.h | 3 ++- sslh-main.c | 29 ++++++----------------------- sslhconf.cfg | 3 +++ 5 files changed, 45 insertions(+), 38 deletions(-) diff --git a/common.h b/common.h index 4b8e389..9235699 100644 --- a/common.h +++ b/common.h @@ -171,7 +171,6 @@ int flush_deferred(struct queue *q); extern struct sslhcfg_item cfg; extern struct addrinfo *addr_listen; -extern const char* USAGE_STRING; extern const char* server_type; /* sslh-fork.c */ diff --git a/sslh-conf.c b/sslh-conf.c index 750a641..9c52cc6 100644 --- a/sslh-conf.c +++ b/sslh-conf.c @@ -1,5 +1,5 @@ /* Generated by conf2struct (https://www.rutschle.net/tech/conf2struct/README) - * on Fri Aug 13 18:03:18 2021. + * on Tue Aug 24 13:53:04 2021. # conf2struct: generate libconf parsers that read to structs # Copyright (C) 2018-2021 Yves Rutschle @@ -444,6 +444,7 @@ struct compound_cl_arg { struct arg_file* sslhcfg_conffile; struct arg_int* sslhcfg_verbose; + struct arg_lit* sslhcfg_version; struct arg_lit* sslhcfg_foreground; struct arg_lit* sslhcfg_inetd; struct arg_lit* sslhcfg_numeric; @@ -782,7 +783,7 @@ static struct config_desc table_sslhcfg_listen[] = { }, { 0 } }; - + static struct config_desc table_sslhcfg[] = { @@ -802,6 +803,22 @@ static struct config_desc table_sslhcfg[] = { /* default_val*/ .default_val.def_int = 0 }, + { + /* 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, @@ -1106,7 +1123,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: listen */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_listen, - .base_entry = & table_sslhcfg [12], + .base_entry = & table_sslhcfg [13], .targets = sslhcfg_listen_targets, @@ -1118,7 +1135,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: ssh */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_ssh, - .base_entry = & table_sslhcfg [13], + .base_entry = & table_sslhcfg [14], .targets = sslhcfg_ssh_targets, @@ -1130,7 +1147,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: tls */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_tls, - .base_entry = & table_sslhcfg [13], + .base_entry = & table_sslhcfg [14], .targets = sslhcfg_tls_targets, @@ -1142,7 +1159,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: openvpn */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_openvpn, - .base_entry = & table_sslhcfg [13], + .base_entry = & table_sslhcfg [14], .targets = sslhcfg_openvpn_targets, @@ -1154,7 +1171,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: tinc */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_tinc, - .base_entry = & table_sslhcfg [13], + .base_entry = & table_sslhcfg [14], .targets = sslhcfg_tinc_targets, @@ -1166,7 +1183,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: xmpp */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_xmpp, - .base_entry = & table_sslhcfg [13], + .base_entry = & table_sslhcfg [14], .targets = sslhcfg_xmpp_targets, @@ -1178,7 +1195,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: http */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_http, - .base_entry = & table_sslhcfg [13], + .base_entry = & table_sslhcfg [14], .targets = sslhcfg_http_targets, @@ -1190,7 +1207,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: adb */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_adb, - .base_entry = & table_sslhcfg [13], + .base_entry = & table_sslhcfg [14], .targets = sslhcfg_adb_targets, @@ -1202,7 +1219,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: socks5 */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_socks5, - .base_entry = & table_sslhcfg [13], + .base_entry = & table_sslhcfg [14], .targets = sslhcfg_socks5_targets, @@ -1214,7 +1231,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: syslog */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_syslog, - .base_entry = & table_sslhcfg [13], + .base_entry = & table_sslhcfg [14], .targets = sslhcfg_syslog_targets, @@ -1226,7 +1243,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: anyprot */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_anyprot, - .base_entry = & table_sslhcfg [13], + .base_entry = & table_sslhcfg [14], .targets = sslhcfg_anyprot_targets, @@ -1892,6 +1909,7 @@ int sslhcfg_cl_parse(int argc, char* argv[], struct sslhcfg_item* cfg) sslhcfg_conffile = arg_filen("F", "config", "", 0, 1, "Specify configuration file"), #endif sslhcfg_verbose = arg_intn("v", "verbose", "", 0, 1, ""), + 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"), @@ -2062,6 +2080,9 @@ void sslhcfg_fprint( fprintf(out, "verbose: %d", sslhcfg->verbose); 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); diff --git a/sslh-conf.h b/sslh-conf.h index 9f23178..7e09ccc 100644 --- a/sslh-conf.h +++ b/sslh-conf.h @@ -1,5 +1,5 @@ /* Generated by conf2struct (https://www.rutschle.net/tech/conf2struct/README) - * on Fri Aug 13 18:03:18 2021. + * on Tue Aug 24 13:53:04 2021. # conf2struct: generate libconf parsers that read to structs # Copyright (C) 2018-2021 Yves Rutschle @@ -75,6 +75,7 @@ struct sslhcfg_protocols_item { struct sslhcfg_item { int verbose; + int version; int foreground; int inetd; int numeric; diff --git a/sslh-main.c b/sslh-main.c index 02f75c9..1777c93 100644 --- a/sslh-main.c +++ b/sslh-main.c @@ -37,29 +37,6 @@ #include "common.h" #include "probe.h" -const char* USAGE_STRING = -"sslh " VERSION "\n" \ -"usage:\n" \ -"\tsslh [-v] [-i] [-V] [-f] [-n] [--transparent] [-F]\n" -"\t[-t ] [-P ] [-u ] [-C ] -p [-p ...] \n" \ -"%s\n\n" /* Dynamically built list of builtin protocols */ \ -"\t[--on-timeout ]\n" \ -"-v: verbose\n" \ -"-V: version\n" \ -"-f: foreground\n" \ -"-n: numeric output\n" \ -"-u: specify under which user to run\n" \ -"-C: specify under which chroot path to run\n" \ -"--transparent: behave as a transparent proxy\n" \ -"-F: use configuration file (warning: no space between -F and file name!)\n" \ -"--on-timeout: connect to specified address upon timeout (default: ssh address)\n" \ -"-t: seconds to wait before connecting to --on-timeout address.\n" \ -"-p: address and port to listen on.\n Can be used several times to bind to several addresses.\n" \ -"--[ssh,ssl,...]: where to connect connections from corresponding protocol.\n" \ -"-P: PID file.\n" \ -"-i: Run as a inetd service.\n" \ -""; - /* Constants for options that have no one-character shorthand */ #define OPT_ONTIMEOUT 257 @@ -209,6 +186,12 @@ int main(int argc, char *argv[], char* envp[]) if (res) exit(6); if (cfg.verbose > 3) sslhcfg_fprint(stderr, &cfg, 0); + + if (cfg.version) { + printf("%s %s\n", server_type, VERSION); + exit(0); + } + config_protocols(); config_sanity_check(&cfg); diff --git a/sslhconf.cfg b/sslhconf.cfg index 9884199..ed5b588 100644 --- a/sslhconf.cfg +++ b/sslhconf.cfg @@ -26,6 +26,9 @@ config: { type: "list", items: ( { name: "verbose"; type: "int"; default: 0; short: "v"; }, + { name: "version"; type: "bool"; default: false; + short: "V"; + description: "Print version information and exit"; }, { name: "foreground"; type: "bool"; default: false; short: "f"; description: "Run in foreground instead of as a daemon"; }, From 1a3341c2a4a66e5fb4336aa53b9ea0f23b0591ef Mon Sep 17 00:00:00 2001 From: yrutschle Date: Tue, 24 Aug 2021 20:07:28 +0200 Subject: [PATCH 003/191] be more defensive when allocating and extending gap --- collection.c | 7 ++++--- collection.h | 2 +- gap.c | 7 ++++--- gap.h | 2 +- sslh-select.c | 4 ++-- 5 files changed, 12 insertions(+), 10 deletions(-) diff --git a/collection.c b/collection.c index e0d556d..8680915 100644 --- a/collection.c +++ b/collection.c @@ -30,8 +30,9 @@ struct cnx_collection { gap_array* fd2cnx; /* Array indexed by file descriptor to things in cnx[] */ }; -/* Allocates and initialises a new collection of connections. */ -cnx_collection* collection_init(void) +/* Allocates and initialises a new collection of connections with at least + * `len` elements. */ +cnx_collection* collection_init(int len) { cnx_collection* collection; @@ -40,7 +41,7 @@ cnx_collection* collection_init(void) memset(collection, 0, sizeof(*collection)); - collection->fd2cnx = gap_init(); + collection->fd2cnx = gap_init(len); return collection; } diff --git a/collection.h b/collection.h index f44b270..8d6ebb2 100644 --- a/collection.h +++ b/collection.h @@ -4,7 +4,7 @@ typedef struct cnx_collection cnx_collection; -cnx_collection* collection_init(void); +cnx_collection* collection_init(int len); void collection_destroy(cnx_collection* collection); struct connection* collection_alloc_cnx_from_fd(cnx_collection* collection, int fd); diff --git a/gap.c b/gap.c index f3f71d2..6b96a89 100644 --- a/gap.c +++ b/gap.c @@ -42,8 +42,8 @@ static int gap_len_alloc(int elem_size) return getpagesize() / elem_size; } -/* Creates a new gap, all pointers are initialised at NULL */ -gap_array* gap_init(void) +/* Creates a new gap at least `len` big, all pointers are initialised at NULL */ +gap_array* gap_init(int len) { gap_array* gap = malloc(sizeof(*gap)); if (!gap) return NULL; @@ -51,6 +51,7 @@ gap_array* gap_init(void) int elem_size = sizeof(gap->array[0]); gap->len = gap_len_alloc(elem_size); + if (gap->len < len) gap->len = len; gap->array = malloc(gap->len * elem_size); if (!gap->array) return NULL; @@ -85,7 +86,7 @@ static int gap_extend(gap_array* gap) int gap_set(gap_array* gap, int index, void* ptr) { - if (index >= gap->len) { + while (index >= gap->len) { int res = gap_extend(gap); if (res == -1) return -1; } diff --git a/gap.h b/gap.h index 8044673..3ba5ead 100644 --- a/gap.h +++ b/gap.h @@ -3,7 +3,7 @@ typedef struct gap_array gap_array; -gap_array* gap_init(); +gap_array* gap_init(int len); void* gap_get(gap_array* gap, int index); int gap_set(gap_array* gap, int index, void* ptr); void gap_destroy(gap_array* gap); diff --git a/sslh-select.c b/sslh-select.c index 59acdc3..e8d8ca2 100644 --- a/sslh-select.c +++ b/sslh-select.c @@ -527,7 +527,7 @@ void main_loop(struct listen_endpoint listen_sockets[], int num_addr_listen) fd_info.num_probing = 0; FD_ZERO(&fd_info.fds_r); FD_ZERO(&fd_info.fds_w); - fd_info.probing_list = gap_init(); + fd_info.probing_list = gap_init(0); for (i = 0; i < num_addr_listen; i++) { FD_SET(listen_sockets[i].socketfd, &fd_info.fds_r); @@ -535,7 +535,7 @@ void main_loop(struct listen_endpoint listen_sockets[], int num_addr_listen) } fd_info.max_fd = listen_sockets[num_addr_listen-1].socketfd + 1; - fd_info.collection = collection_init(); + fd_info.collection = collection_init(fd_info.max_fd); while (1) { From a704c7f7f5d05dd0ae43312c50d0bb8bf9892067 Mon Sep 17 00:00:00 2001 From: yrutschle Date: Sat, 28 Aug 2021 16:03:58 +0200 Subject: [PATCH 004/191] fix #302 --- common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common.c b/common.c index 909729c..453e271 100644 --- a/common.c +++ b/common.c @@ -188,7 +188,7 @@ int start_listen_sockets(struct listen_endpoint *sockfd[]) for (addr = start_addr; addr; addr = addr->ai_next) { num_addr++; - *sockfd = realloc(*sockfd, num_addr * sizeof(*sockfd)); + *sockfd = realloc(*sockfd, num_addr * sizeof(*sockfd[0])); (*sockfd)[num_addr-1].socketfd = listen_single_addr(addr, keepalive, udp); (*sockfd)[num_addr-1].type = udp ? SOCK_DGRAM : SOCK_STREAM; if (cfg.verbose) From 3013658b200f517b63266a9e2c44ba9a9470479b Mon Sep 17 00:00:00 2001 From: yrutschle Date: Sat, 28 Aug 2021 16:33:20 +0200 Subject: [PATCH 005/191] test to drop connection before writing anything (fix #285) --- t | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/t b/t index 7f8185c..4ef5b1d 100755 --- a/t +++ b/t @@ -26,6 +26,7 @@ my $PROBES_NOFRAG = 1; my $PROBES_AGAIN = 1; my $SSL_MIX_SSH = 1; my $SSH_MIX_SSL = 1; +my $DROP_CNX = 1; # Robustness tests. These are mostly to achieve full test # coverage, but do not necessarily result in an actual test @@ -285,6 +286,19 @@ for my $binary (@binaries) { } } +# Test: Drop connection without writing anything + if ($DROP_CNX) { + print "***Test: Connect but don't write anything\n"; + my $cnx_h = new IO::Socket::INET(PeerHost => "localhost:$sslh_port"); + warn "$!\n" unless $cnx_h; + if ($cnx_h) { + close $cnx_h; + my_is(1, "$binary: Connect and write nothing"); + # The goal of the test is to check sslh doesn't + # crash + } + } + if ($PROBES_NOFRAG) { test_probes(no_frag => 1, binary => $binary); From 317c08604bc26f8cf05bebcf902df37a17572db5 Mon Sep 17 00:00:00 2001 From: yrutschle Date: Wed, 15 Sep 2021 21:51:11 +0200 Subject: [PATCH 006/191] move logging code to its own file --- Makefile | 2 +- common.c | 73 -------------------------------------- log.c | 106 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ log.h | 10 ++++++ 4 files changed, 117 insertions(+), 74 deletions(-) create mode 100644 log.c create mode 100644 log.h diff --git a/Makefile b/Makefile index 1c465fe..6ddc454 100644 --- a/Makefile +++ b/Makefile @@ -27,7 +27,7 @@ CC ?= gcc CFLAGS ?=-Wall -DLIBPCRE -g $(CFLAGS_COV) LIBS=-lm -lpcre2-8 -OBJS=sslh-conf.o common.o sslh-main.o probe.o tls.o argtable3.o udp-listener.o collection.o gap.o +OBJS=sslh-conf.o common.o log.o sslh-main.o probe.o tls.o argtable3.o udp-listener.o collection.o gap.o CONDITIONAL_TARGETS= diff --git a/common.c b/common.c index 453e271..2758c06 100644 --- a/common.c +++ b/common.c @@ -4,7 +4,6 @@ * No code here should assume whether sockets are blocking or not. **/ -#define SYSLOG_NAMES #define _GNU_SOURCE #include #include @@ -42,8 +41,6 @@ struct sslhcfg_item cfg; struct addrinfo *addr_listen = NULL; /* what addresses do we listen to? */ -static int do_syslog = 1; /* Should we syslog? controled by syslog_facility = "none" */ - #ifdef LIBWRAP #include int allow_severity =0, deny_severity = 0; @@ -590,23 +587,6 @@ void resolve_name(struct addrinfo **out, char* fullname) } } -/* Log to syslog or stderr if foreground */ -void log_message(int type, const char* msg, ...) -{ - va_list ap; - - va_start(ap, msg); - if (cfg.foreground) - vfprintf(stderr, msg, ap); - va_end(ap); - - if (do_syslog) { - va_start(ap, msg); - vsyslog(type, msg, ap); - va_end(ap); - } -} - /* Fills a connection description; returns 0 on failure */ int get_connection_desc(struct connection_desc* desc, const struct connection *cnx) @@ -641,30 +621,6 @@ int get_connection_desc(struct connection_desc* desc, const struct connection *c return 1; } -/* syslogs who connected to where - * desc: string description of the connection. if NULL, log_connection will - * manage on its own - * cnx: connection descriptor - * */ -void log_connection(struct connection_desc* desc, const struct connection *cnx) -{ - struct connection_desc d; - - if (cnx->proto->log_level < 1) - return; - - if (!desc) { - desc = &d; - get_connection_desc(desc, cnx); - } - - log_message(LOG_INFO, "%s:connection from %s to %s forwarded from %s to %s\n", - cnx->proto->name, - desc->peer, - desc->service, - desc->local, - desc->target); -} void set_proctitle_shovel(struct connection_desc* desc, const struct connection *cnx) { @@ -760,35 +716,6 @@ void setup_signals(void) } -/* Open syslog connection with appropriate banner; - * banner is made up of basename(bin_name)+"[pid]" */ -void setup_syslog(const char* bin_name) { - char *name1, *name2; - int res, fn; - - if (!strcmp(cfg.syslog_facility, "none")) { - do_syslog = 0; - return; - } - - name1 = strdup(bin_name); - res = asprintf(&name2, "%s[%d]", basename(name1), getpid()); - CHECK_RES_DIE(res, "asprintf"); - - for (fn = 0; facilitynames[fn].c_val != -1; fn++) - if (strcmp(facilitynames[fn].c_name, cfg.syslog_facility) == 0) - break; - if (facilitynames[fn].c_val == -1) { - fprintf(stderr, "Unknown facility %s\n", cfg.syslog_facility); - exit(1); - } - - openlog(name2, LOG_CONS, facilitynames[fn].c_val); - free(name1); - /* Don't free name2, as openlog(3) uses it (at least in glibc) */ - - log_message(LOG_INFO, "%s %s started\n", server_type, VERSION); -} /* Ask OS to keep capabilities over a setuid(nonzero) */ void set_keepcaps(int val) { diff --git a/log.c b/log.c new file mode 100644 index 0000000..fea3d65 --- /dev/null +++ b/log.c @@ -0,0 +1,106 @@ +/* +# log: processing of all outgoing messages +# +# Copyright (C) 2007-2021 Yves Rutschle +# +# This program is free software; you can redistribute it +# and/or modify it under the terms of the GNU General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later +# version. +# +# This program is distributed in the hope that it will be +# useful, but WITHOUT ANY WARRANTY; without even the implied +# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +# PURPOSE. See the GNU General Public License for more +# details. +# +# The full text for the General Public License is here: +# http://www.gnu.org/licenses/gpl.html + +*/ + + +#define SYSLOG_NAMES +#define _GNU_SOURCE +#include +#include +#include "sslh-conf.h" +#include "common.h" +#include "log.h" + +static int do_syslog = 1; /* Should we syslog? controled by syslog_facility = "none" */ + +/* Open syslog connection with appropriate banner; + * banner is made up of basename(bin_name)+"[pid]" */ +void setup_syslog(const char* bin_name) { + char *name1, *name2; + int res, fn; + + if (!strcmp(cfg.syslog_facility, "none")) { + do_syslog = 0; + return; + } + + name1 = strdup(bin_name); + res = asprintf(&name2, "%s[%d]", basename(name1), getpid()); + CHECK_RES_DIE(res, "asprintf"); + + for (fn = 0; facilitynames[fn].c_val != -1; fn++) + if (strcmp(facilitynames[fn].c_name, cfg.syslog_facility) == 0) + break; + if (facilitynames[fn].c_val == -1) { + fprintf(stderr, "Unknown facility %s\n", cfg.syslog_facility); + exit(1); + } + + openlog(name2, LOG_CONS, facilitynames[fn].c_val); + free(name1); + /* Don't free name2, as openlog(3) uses it (at least in glibc) */ + + log_message(LOG_INFO, "%s %s started\n", server_type, VERSION); +} + + +/* Log to syslog or stderr if foreground */ +void log_message(int type, const char* msg, ...) +{ + va_list ap; + + va_start(ap, msg); + if (cfg.foreground) + vfprintf(stderr, msg, ap); + va_end(ap); + + if (do_syslog) { + va_start(ap, msg); + vsyslog(type, msg, ap); + va_end(ap); + } +} + + +/* syslogs who connected to where + * desc: string description of the connection. if NULL, log_connection will + * manage on its own + * cnx: connection descriptor + * */ +void log_connection(struct connection_desc* desc, const struct connection *cnx) +{ + struct connection_desc d; + + if (cnx->proto->log_level < 1) + return; + + if (!desc) { + desc = &d; + get_connection_desc(desc, cnx); + } + + log_message(LOG_INFO, "%s:connection from %s to %s forwarded from %s to %s\n", + cnx->proto->name, + desc->peer, + desc->service, + desc->local, + desc->target); +} diff --git a/log.h b/log.h new file mode 100644 index 0000000..4bd85e1 --- /dev/null +++ b/log.h @@ -0,0 +1,10 @@ +#ifndef LOG_H +#define LOG_H + +void setup_syslog(const char* bin_name); + +void log_message(int type, const char* msg, ...); + +void log_connection(struct connection_desc* desc, const struct connection *cnx); + +#endif /* LOG_H */ From 5e27806545bf901431adf0d7cb14f0adc2571d26 Mon Sep 17 00:00:00 2001 From: yrutschle Date: Sun, 19 Sep 2021 15:13:04 +0200 Subject: [PATCH 007/191] new logging system: now with message classes --- common.c | 18 ++++++------- echosrv-conf.c | 2 +- echosrv-conf.h | 2 +- example.cfg | 8 +++++- log.c | 35 +++++++++++++++++++++++-- log.h | 9 +++++++ sslh-conf.c | 70 ++++++++++++++++++++++++++++++++++++++++---------- sslh-conf.h | 4 ++- sslh-main.c | 27 ++++++++++--------- sslhconf.cfg | 5 +++- test.cfg | 4 +++ 11 files changed, 139 insertions(+), 45 deletions(-) diff --git a/common.c b/common.c index 2758c06..9274d19 100644 --- a/common.c +++ b/common.c @@ -15,6 +15,7 @@ #include "common.h" #include "probe.h" +#include "log.h" #include "sslh-conf.h" /* Added to make the code compilable under CYGWIN @@ -174,7 +175,7 @@ int start_listen_sockets(struct listen_endpoint *sockfd[]) *sockfd = NULL; - if (cfg.verbose) fprintf(stderr, "Listening to:\n"); + print_message(msg_config, "Listening to:\n"); for (i = 0; i < cfg.listen_len; i++) { keepalive = cfg.listen[i].keepalive; @@ -188,10 +189,9 @@ int start_listen_sockets(struct listen_endpoint *sockfd[]) *sockfd = realloc(*sockfd, num_addr * sizeof(*sockfd[0])); (*sockfd)[num_addr-1].socketfd = listen_single_addr(addr, keepalive, udp); (*sockfd)[num_addr-1].type = udp ? SOCK_DGRAM : SOCK_STREAM; - if (cfg.verbose) - fprintf(stderr, "%d:\t%s\t[%s] [%s]\n", (*sockfd)[num_addr-1].socketfd, sprintaddr(buf, sizeof(buf), addr), - cfg.listen[i].keepalive ? "keepalive" : "", - cfg.listen[i].is_udp ? "udp" : ""); + print_message(msg_config, "%d:\t%s\t[%s] [%s]\n", (*sockfd)[num_addr-1].socketfd, sprintaddr(buf, sizeof(buf), addr), + cfg.listen[i].keepalive ? "keepalive" : "", + cfg.listen[i].is_udp ? "udp" : ""); } freeaddrinfo(start_addr); } @@ -793,16 +793,14 @@ void drop_privileges(const char* user_name, const char* chroot_path) if (user_name) { pw = getpwnam(user_name); if (!pw) { - fprintf(stderr, "%s: not found\n", user_name); + print_message(msg_config_error, "%s: not found\n", user_name); exit(2); } - if (cfg.verbose) - fprintf(stderr, "turning into %s\n", user_name); + print_message(msg_config, "turning into %s\n", user_name); } if (chroot_path) { - if (cfg.verbose) - fprintf(stderr, "chrooting into %s\n", chroot_path); + print_message(msg_config, "chrooting into %s\n", chroot_path); res = chroot(chroot_path); CHECK_RES_DIE(res, "chroot"); diff --git a/echosrv-conf.c b/echosrv-conf.c index 38ddc3b..a5d1a71 100644 --- a/echosrv-conf.c +++ b/echosrv-conf.c @@ -1,5 +1,5 @@ /* Generated by conf2struct (https://www.rutschle.net/tech/conf2struct/README) - * on Fri Aug 13 18:03:20 2021. + * on Sat Sep 18 17:28:37 2021. # conf2struct: generate libconf parsers that read to structs # Copyright (C) 2018-2021 Yves Rutschle diff --git a/echosrv-conf.h b/echosrv-conf.h index 4f49e9e..0218d99 100644 --- a/echosrv-conf.h +++ b/echosrv-conf.h @@ -1,5 +1,5 @@ /* Generated by conf2struct (https://www.rutschle.net/tech/conf2struct/README) - * on Fri Aug 13 18:03:20 2021. + * on Sat Sep 18 17:28:37 2021. # conf2struct: generate libconf parsers that read to structs # Copyright (C) 2018-2021 Yves Rutschle diff --git a/example.cfg b/example.cfg index 4b70d48..974a3f9 100644 --- a/example.cfg +++ b/example.cfg @@ -3,7 +3,6 @@ # not be used as a starting point for a working # configuration. Instead use basic.cfg. -verbose: 0; foreground: true; inetd: false; numeric: false; @@ -13,6 +12,13 @@ user: "nobody"; pidfile: "/var/run/sslh.pid"; chroot: "/var/empty"; +verbose: 0; + +# Logging configuration +# Value: 1: stdout; 2: syslog; 3: both +verbose-config: 0; # config: print configuration at startup + + # Specify which syslog facility to use (names for your # system are usually defined in /usr/include/*/sys/syslog.h # or equivalent) diff --git a/log.c b/log.c index fea3d65..11aed81 100644 --- a/log.c +++ b/log.c @@ -29,6 +29,39 @@ #include "common.h" #include "log.h" +msg_info msg_config = { + LOG_INFO, + &cfg.verbose_config +}; + + +msg_info msg_config_error = { + LOG_ERR, + &cfg.verbose_config_error +}; + + +/* Bitmasks in verbose-* values */ +#define MSG_STDOUT 1 +#define MSG_SYSLOG 2 + +/* Prints a message to stderr and/or syslog if appropriate */ +void print_message(msg_info info, const char* str, ...) +{ + va_list ap; + + va_start(ap, str); + + if ((*info.verbose & MSG_STDOUT) && ! cfg.inetd) + vfprintf(stderr, str, ap); + + if (*info.verbose & MSG_SYSLOG) { + va_start(ap, str); + vsyslog(info.log_level, str, ap); + va_end(ap); + } +} + static int do_syslog = 1; /* Should we syslog? controled by syslog_facility = "none" */ /* Open syslog connection with appropriate banner; @@ -57,8 +90,6 @@ void setup_syslog(const char* bin_name) { openlog(name2, LOG_CONS, facilitynames[fn].c_val); free(name1); /* Don't free name2, as openlog(3) uses it (at least in glibc) */ - - log_message(LOG_INFO, "%s %s started\n", server_type, VERSION); } diff --git a/log.h b/log.h index 4bd85e1..70a76bc 100644 --- a/log.h +++ b/log.h @@ -7,4 +7,13 @@ void log_message(int type, const char* msg, ...); void log_connection(struct connection_desc* desc, const struct connection *cnx); +typedef struct s_msg_info{ + int log_level; + int *verbose; +} msg_info; + +void print_message(msg_info info, const char* str, ...); +extern msg_info msg_config; +extern msg_info msg_config_error; + #endif /* LOG_H */ diff --git a/sslh-conf.c b/sslh-conf.c index 9c52cc6..e16dde9 100644 --- a/sslh-conf.c +++ b/sslh-conf.c @@ -1,5 +1,5 @@ /* Generated by conf2struct (https://www.rutschle.net/tech/conf2struct/README) - * on Tue Aug 24 13:53:04 2021. + * on Sat Sep 18 22:17:39 2021. # conf2struct: generate libconf parsers that read to structs # Copyright (C) 2018-2021 Yves Rutschle @@ -443,6 +443,8 @@ struct compound_cl_arg { struct arg_file* sslhcfg_conffile; + struct arg_int* sslhcfg_verbose_config; + struct arg_int* sslhcfg_verbose_config_error; struct arg_int* sslhcfg_verbose; struct arg_lit* sslhcfg_version; struct arg_lit* sslhcfg_foreground; @@ -783,10 +785,42 @@ static struct config_desc table_sslhcfg_listen[] = { }, { 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", /* type */ CFG_INT, @@ -798,7 +832,7 @@ static struct config_desc table_sslhcfg[] = { /* offset_present */ 0, /* size */ sizeof(int), /* array_type */ -1, - /* mandatory */ 0, + /* mandatory */ 1, /* optional */ 0, /* default_val*/ .default_val.def_int = 0 }, @@ -1123,7 +1157,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: listen */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_listen, - .base_entry = & table_sslhcfg [13], + .base_entry = & table_sslhcfg [15], .targets = sslhcfg_listen_targets, @@ -1135,7 +1169,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: ssh */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_ssh, - .base_entry = & table_sslhcfg [14], + .base_entry = & table_sslhcfg [16], .targets = sslhcfg_ssh_targets, @@ -1147,7 +1181,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: tls */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_tls, - .base_entry = & table_sslhcfg [14], + .base_entry = & table_sslhcfg [16], .targets = sslhcfg_tls_targets, @@ -1159,7 +1193,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: openvpn */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_openvpn, - .base_entry = & table_sslhcfg [14], + .base_entry = & table_sslhcfg [16], .targets = sslhcfg_openvpn_targets, @@ -1171,7 +1205,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: tinc */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_tinc, - .base_entry = & table_sslhcfg [14], + .base_entry = & table_sslhcfg [16], .targets = sslhcfg_tinc_targets, @@ -1183,7 +1217,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: xmpp */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_xmpp, - .base_entry = & table_sslhcfg [14], + .base_entry = & table_sslhcfg [16], .targets = sslhcfg_xmpp_targets, @@ -1195,7 +1229,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: http */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_http, - .base_entry = & table_sslhcfg [14], + .base_entry = & table_sslhcfg [16], .targets = sslhcfg_http_targets, @@ -1207,7 +1241,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: adb */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_adb, - .base_entry = & table_sslhcfg [14], + .base_entry = & table_sslhcfg [16], .targets = sslhcfg_adb_targets, @@ -1219,7 +1253,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: socks5 */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_socks5, - .base_entry = & table_sslhcfg [14], + .base_entry = & table_sslhcfg [16], .targets = sslhcfg_socks5_targets, @@ -1231,7 +1265,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: syslog */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_syslog, - .base_entry = & table_sslhcfg [14], + .base_entry = & table_sslhcfg [16], .targets = sslhcfg_syslog_targets, @@ -1243,7 +1277,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: anyprot */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_anyprot, - .base_entry = & table_sslhcfg [14], + .base_entry = & table_sslhcfg [16], .targets = sslhcfg_anyprot_targets, @@ -1908,6 +1942,8 @@ int sslhcfg_cl_parse(int argc, char* argv[], struct sslhcfg_item* cfg) #ifdef LIBCONFIG sslhcfg_conffile = arg_filen("F", "config", "", 0, 1, "Specify configuration file"), #endif + sslhcfg_verbose_config = arg_intn(NULL, "verbose-config", "", 0, 1, ""), + sslhcfg_verbose_config_error = arg_intn(NULL, "verbose-config-error", "", 0, 1, ""), sslhcfg_verbose = arg_intn("v", "verbose", "", 0, 1, ""), 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"), @@ -2076,6 +2112,12 @@ void sslhcfg_fprint( 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: %d", sslhcfg->verbose); fprintf(out, "\n"); diff --git a/sslh-conf.h b/sslh-conf.h index 7e09ccc..1f900e1 100644 --- a/sslh-conf.h +++ b/sslh-conf.h @@ -1,5 +1,5 @@ /* Generated by conf2struct (https://www.rutschle.net/tech/conf2struct/README) - * on Tue Aug 24 13:53:04 2021. + * on Sat Sep 18 22:17:39 2021. # conf2struct: generate libconf parsers that read to structs # Copyright (C) 2018-2021 Yves Rutschle @@ -74,6 +74,8 @@ struct sslhcfg_protocols_item { }; struct sslhcfg_item { + int verbose_config; + int verbose_config_error; int verbose; int version; int foreground; diff --git a/sslh-main.c b/sslh-main.c index 1777c93..bb737f5 100644 --- a/sslh-main.c +++ b/sslh-main.c @@ -36,6 +36,7 @@ #include "common.h" #include "probe.h" +#include "log.h" /* Constants for options that have no one-character shorthand */ #define OPT_ONTIMEOUT 257 @@ -50,7 +51,7 @@ static void printcaps(void) { desc = cap_to_text(caps, &len); - fprintf(stderr, "capabilities: %s\n", desc); + print_message(msg_config, "capabilities: %s\n", desc); cap_free(caps); cap_free(desc); @@ -65,7 +66,7 @@ static void printsettings(void) for (i = 0; i < cfg.protocols_len; i++ ) { p = &cfg.protocols[i]; - fprintf(stderr, + print_message(msg_config, "%s addr: %s. libwrap service: %s log_level: %d family %d %d [%s] [%s] [%s]\n", p->name, sprintaddr(buf, sizeof(buf), p->saddr), @@ -78,7 +79,7 @@ static void printsettings(void) p->transparent ? "transparent" : "" ); } - fprintf(stderr, "timeout: %d\non-timeout: %s\n", cfg.timeout, + print_message(msg_config, "timeout: %d\non-timeout: %s\n", cfg.timeout, timeout_protocol()->name); } @@ -124,13 +125,13 @@ static void config_protocols() for (i = 0; i < cfg.protocols_len; i++) { struct sslhcfg_protocols_item* p = &(cfg.protocols[i]); if (resolve_split_name(&(p->saddr), p->host, p->port)) { - fprintf(stderr, "cannot resolve %s:%s\n", p->host, p->port); + print_message(msg_config_error, "cannot resolve %s:%s\n", p->host, p->port); exit(4); } p->probe = get_probe(p->name); if (!p->probe) { - fprintf(stderr, "%s: probe unknown\n", p->name); + print_message(msg_config_error, "%s: probe unknown\n", p->name); exit(1); } @@ -155,14 +156,14 @@ static void config_protocols() void config_sanity_check(struct sslhcfg_item* cfg) { if (!cfg->protocols_len) { - fprintf(stderr, "At least one target protocol must be specified.\n"); + print_message(msg_config_error, "At least one target protocol must be specified.\n"); exit(2); } /* If compiling with systemd socket support no need to require listen address */ #ifndef SYSTEMD if (!cfg->listen_len && !cfg->inetd) { - fprintf(stderr, "No listening address specified; use at least one -p option\n"); + print_message(msg_config_error, "No listening address specified; use at least one -p option\n"); exit(1); } #endif @@ -184,8 +185,6 @@ int main(int argc, char *argv[], char* envp[]) memset(&cfg, 0, sizeof(cfg)); res = sslhcfg_cl_parse(argc, argv, &cfg); if (res) exit(6); - if (cfg.verbose > 3) - sslhcfg_fprint(stderr, &cfg, 0); if (cfg.version) { printf("%s %s\n", server_type, VERSION); @@ -202,14 +201,13 @@ int main(int argc, char *argv[], char* envp[]) exit(0); } - if (cfg.verbose) - printsettings(); + printsettings(); num_addr_listen = start_listen_sockets(&listen_sockets); #ifdef SYSTEMD if (num_addr_listen < 1) { - fprintf(stderr, "No listening sockets found, restart sockets or specify addresses in config\n"); + print_message(msg_config_error, "No listening sockets found, restart sockets or specify addresses in config\n"); exit(1); } #endif @@ -235,8 +233,9 @@ int main(int argc, char *argv[], char* envp[]) if (cfg.user || cfg.chroot) drop_privileges(cfg.user, cfg.chroot); - if (cfg.verbose) - printcaps(); + printcaps(); + + print_message(msg_config, "%s %s started\n", server_type, VERSION); main_loop(listen_sockets, num_addr_listen); diff --git a/sslhconf.cfg b/sslhconf.cfg index ed5b588..bb4b830 100644 --- a/sslhconf.cfg +++ b/sslhconf.cfg @@ -25,7 +25,10 @@ config: { name : "sslhcfg", type: "list", items: ( - { name: "verbose"; type: "int"; default: 0; short: "v"; }, + { name: "verbose-config"; type: "int"; default: 0; }, + { name: "verbose-config-error"; type: "int"; default: 3; }, + + { name: "verbose"; type: "int"; short: "v" }, # to delete { name: "version"; type: "bool"; default: false; short: "V"; description: "Print version information and exit"; }, diff --git a/test.cfg b/test.cfg index 7dd66ea..08b37df 100644 --- a/test.cfg +++ b/test.cfg @@ -11,6 +11,10 @@ pidfile: "/tmp/sslh_test.pid"; syslog_facility: "auth"; +# Logging configuration +# Value: 1: stdout; 2: syslog; 3: both +#verbose-config: 3; # config: print configuration at startup +#verbose-config-error: 3; #config-error: print configuration errors # List of interfaces on which we should listen # Options: From 098a55fd1d209aa513687b81f746717057495484 Mon Sep 17 00:00:00 2001 From: yrutschle Date: Sun, 19 Sep 2021 15:14:38 +0200 Subject: [PATCH 008/191] new logging system: now with message classes --- sslh-main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sslh-main.c b/sslh-main.c index bb737f5..99d6297 100644 --- a/sslh-main.c +++ b/sslh-main.c @@ -104,7 +104,7 @@ static void setup_regex_probe(struct sslhcfg_protocols_item *p) &error, &error_offset, NULL); if (!pattern_list[i]) { pcre2_get_error_message(error, err_str, sizeof(err_str)); - fprintf(stderr, "compiling pattern /%s/:%d:%s at offset %ld\n", + print_message(msg_config_error, "compiling pattern /%s/:%d:%s at offset %ld\n", p->regex_patterns[i], error, err_str, error_offset); exit(1); } From dbad46a358c37aeb3363ee628a8ad11ebb27b21b Mon Sep 17 00:00:00 2001 From: yrutschle Date: Sun, 19 Sep 2021 15:19:37 +0200 Subject: [PATCH 009/191] remove obsolete debug code --- sslh-select.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/sslh-select.c b/sslh-select.c index e8d8ca2..a77d883 100644 --- a/sslh-select.c +++ b/sslh-select.c @@ -38,8 +38,6 @@ #include "collection.h" #include "gap.h" -static int debug = 0; - const char* server_type = "sslh-select"; /* Global state for a select() loop */ @@ -347,8 +345,6 @@ int active_queue(struct connection* cnx, int fd) static void tcp_read_process(struct select_info* fd_info, int fd) { - if (debug) fprintf(stderr, "cnx_read_process fd %d\n", fd); - cnx_collection* collection = fd_info->collection; struct connection* cnx = collection_get_cnx_from_fd(collection, fd); /* Determine active queue (0 or 1): if fd is that of q[1], active_q = 1, @@ -403,8 +399,6 @@ static void cnx_read_process(struct select_info* fd_info, int fd) /* Process a connection that is active in write */ static void cnx_write_process(struct select_info* fd_info, int fd) { - if (debug) fprintf(stderr, "cnx_write_process fd %d\n", fd); - struct connection* cnx = collection_get_cnx_from_fd(fd_info->collection, fd); int res; int queue = active_queue(cnx, fd); @@ -433,8 +427,6 @@ void cnx_accept_process(struct select_info* fd_info, struct listen_endpoint* lis struct connection* cnx; int new_fd; - if (debug) fprintf(stderr, "cnx_accept_process fd %d\n", fd); - switch (type) { case SOCK_STREAM: cnx = accept_new_connection(fd, fd_info->collection); From f7b6f669a472407be2e6dcc085621c46c12e3329 Mon Sep 17 00:00:00 2001 From: yrutschle Date: Sun, 19 Sep 2021 20:24:46 +0200 Subject: [PATCH 010/191] sslh-select to use new log system --- log.c | 19 ++++++++- log.h | 5 +++ sslh-conf.c | 110 ++++++++++++++++++++++++++++++++++++++++++++------ sslh-conf.h | 6 ++- sslh-select.c | 48 +++++++++------------- sslhconf.cfg | 5 +++ test.cfg | 6 +++ 7 files changed, 156 insertions(+), 43 deletions(-) diff --git a/log.c b/log.c index 11aed81..4c01fcb 100644 --- a/log.c +++ b/log.c @@ -34,12 +34,29 @@ msg_info msg_config = { &cfg.verbose_config }; - msg_info msg_config_error = { LOG_ERR, &cfg.verbose_config_error }; +msg_info msg_fd = { + LOG_DEBUG, + &cfg.verbose_fd +}; + +/* Internal errors: inconsistent states, impossible values, things that should never happen, and are therefore the sign of memory corruption: hence the LOG_CRIT */ +msg_info msg_int_error = { + LOG_CRIT, + &cfg.verbose_system_error +}; + +/* System errors: when the system around us fails us: memory allocation, fork, ... */ +msg_info msg_system_error = { + LOG_ERR, + &cfg.verbose_system_error +}; + + /* Bitmasks in verbose-* values */ #define MSG_STDOUT 1 diff --git a/log.h b/log.h index 70a76bc..e99b77b 100644 --- a/log.h +++ b/log.h @@ -16,4 +16,9 @@ void print_message(msg_info info, const char* str, ...); extern msg_info msg_config; extern msg_info msg_config_error; +extern msg_info msg_fd; + +extern msg_info msg_int_error; +extern msg_info msg_system_error; + #endif /* LOG_H */ diff --git a/sslh-conf.c b/sslh-conf.c index e16dde9..6d8eb46 100644 --- a/sslh-conf.c +++ b/sslh-conf.c @@ -1,5 +1,5 @@ /* Generated by conf2struct (https://www.rutschle.net/tech/conf2struct/README) - * on Sat Sep 18 22:17:39 2021. + * on Sun Sep 19 20:20:24 2021. # conf2struct: generate libconf parsers that read to structs # Copyright (C) 2018-2021 Yves Rutschle @@ -445,6 +445,10 @@ struct compound_cl_arg { 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_fd; + struct arg_int* sslhcfg_verbose_system_error; + struct arg_int* sslhcfg_verbose_int_error; struct arg_int* sslhcfg_verbose; struct arg_lit* sslhcfg_version; struct arg_lit* sslhcfg_foreground; @@ -785,7 +789,7 @@ static struct config_desc table_sslhcfg_listen[] = { }, { 0 } }; - + static struct config_desc table_sslhcfg[] = { @@ -821,6 +825,70 @@ static struct config_desc table_sslhcfg[] = { /* 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 = 0 + }, + + { + /* 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_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 */ "verbose", /* type */ CFG_INT, @@ -1157,7 +1225,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: listen */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_listen, - .base_entry = & table_sslhcfg [15], + .base_entry = & table_sslhcfg [19], .targets = sslhcfg_listen_targets, @@ -1169,7 +1237,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: ssh */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_ssh, - .base_entry = & table_sslhcfg [16], + .base_entry = & table_sslhcfg [20], .targets = sslhcfg_ssh_targets, @@ -1181,7 +1249,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: tls */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_tls, - .base_entry = & table_sslhcfg [16], + .base_entry = & table_sslhcfg [20], .targets = sslhcfg_tls_targets, @@ -1193,7 +1261,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: openvpn */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_openvpn, - .base_entry = & table_sslhcfg [16], + .base_entry = & table_sslhcfg [20], .targets = sslhcfg_openvpn_targets, @@ -1205,7 +1273,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: tinc */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_tinc, - .base_entry = & table_sslhcfg [16], + .base_entry = & table_sslhcfg [20], .targets = sslhcfg_tinc_targets, @@ -1217,7 +1285,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: xmpp */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_xmpp, - .base_entry = & table_sslhcfg [16], + .base_entry = & table_sslhcfg [20], .targets = sslhcfg_xmpp_targets, @@ -1229,7 +1297,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: http */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_http, - .base_entry = & table_sslhcfg [16], + .base_entry = & table_sslhcfg [20], .targets = sslhcfg_http_targets, @@ -1241,7 +1309,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: adb */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_adb, - .base_entry = & table_sslhcfg [16], + .base_entry = & table_sslhcfg [20], .targets = sslhcfg_adb_targets, @@ -1253,7 +1321,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: socks5 */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_socks5, - .base_entry = & table_sslhcfg [16], + .base_entry = & table_sslhcfg [20], .targets = sslhcfg_socks5_targets, @@ -1265,7 +1333,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: syslog */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_syslog, - .base_entry = & table_sslhcfg [16], + .base_entry = & table_sslhcfg [20], .targets = sslhcfg_syslog_targets, @@ -1277,7 +1345,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: anyprot */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_anyprot, - .base_entry = & table_sslhcfg [16], + .base_entry = & table_sslhcfg [20], .targets = sslhcfg_anyprot_targets, @@ -1944,6 +2012,10 @@ int sslhcfg_cl_parse(int argc, char* argv[], struct sslhcfg_item* cfg) #endif sslhcfg_verbose_config = arg_intn(NULL, "verbose-config", "", 0, 1, ""), sslhcfg_verbose_config_error = arg_intn(NULL, "verbose-config-error", "", 0, 1, ""), + sslhcfg_verbose_connections = arg_intn(NULL, "verbose-connections", "", 0, 1, ""), + sslhcfg_verbose_fd = arg_intn(NULL, "verbose-fd", "", 0, 1, ""), + sslhcfg_verbose_system_error = arg_intn(NULL, "verbose-system-error", "", 0, 1, ""), + sslhcfg_verbose_int_error = arg_intn(NULL, "verbose-int-error", "", 0, 1, ""), sslhcfg_verbose = arg_intn("v", "verbose", "", 0, 1, ""), 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"), @@ -2119,6 +2191,18 @@ void sslhcfg_fprint( 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_fd: %d", sslhcfg->verbose_fd); + 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, "verbose: %d", sslhcfg->verbose); fprintf(out, "\n"); indent(out, depth); diff --git a/sslh-conf.h b/sslh-conf.h index 1f900e1..81ef678 100644 --- a/sslh-conf.h +++ b/sslh-conf.h @@ -1,5 +1,5 @@ /* Generated by conf2struct (https://www.rutschle.net/tech/conf2struct/README) - * on Sat Sep 18 22:17:39 2021. + * on Sun Sep 19 20:20:24 2021. # conf2struct: generate libconf parsers that read to structs # Copyright (C) 2018-2021 Yves Rutschle @@ -76,6 +76,10 @@ struct sslhcfg_protocols_item { struct sslhcfg_item { int verbose_config; int verbose_config_error; + int verbose_connections; + int verbose_fd; + int verbose_system_error; + int verbose_int_error; int verbose; int version; int foreground; diff --git a/sslh-select.c b/sslh-select.c index a77d883..65fbadf 100644 --- a/sslh-select.c +++ b/sslh-select.c @@ -37,6 +37,7 @@ #include "udp-listener.h" #include "collection.h" #include "gap.h" +#include "log.h" const char* server_type = "sslh-select"; @@ -65,8 +66,7 @@ static int tidy_connection(struct connection *cnx, struct select_info* fd_info) for (i = 0; i < 2; i++) { if (cnx->q[i].fd != -1) { - if (cfg.verbose) - fprintf(stderr, "closing fd %d\n", cnx->q[i].fd); + print_message(msg_fd, "closing fd %d\n", cnx->q[i].fd); FD_CLR(cnx->q[i].fd, fds); FD_CLR(cnx->q[i].fd, fds2); @@ -97,7 +97,7 @@ static struct connection* accept_new_connection(int listen_socket, struct cnx_co int in_socket, res; - if (cfg.verbose) fprintf(stderr, "accepting from %d\n", listen_socket); + print_message(msg_fd, "accepting from %d\n", listen_socket); in_socket = accept(listen_socket, 0, 0); CHECK_RES_RETURN(in_socket, "accept", NULL); @@ -156,8 +156,7 @@ static void shovel(struct connection *cnx, int active_fd, struct select_info* fd read_q = &cnx->q[active_fd]; write_q = &cnx->q[1-active_fd]; - if (cfg.verbose) - fprintf(stderr, "activity on fd%d\n", read_q->fd); + print_message(msg_fd, "activity on fd%d\n", read_q->fd); switch(fd2fd(write_q, read_q)) { case -1: @@ -210,16 +209,14 @@ static void shovel_single(struct connection *cnx) if (FD_ISSET(cnx->q[i].fd, &fds_w)) { res = flush_deferred(&cnx->q[i]); if ((res == -1) && ((errno == EPIPE) || (errno == ECONNRESET))) { - if (cfg.verbose) - fprintf(stderr, "%s socket closed\n", i ? "server" : "client"); + print_message(msg_fd, "%s socket closed\n", i ? "server" : "client"); return; } } if (FD_ISSET(cnx->q[i].fd, &fds_r)) { res = fd2fd(&cnx->q[1-i], &cnx->q[i]); if (!res) { - if (cfg.verbose) - fprintf(stderr, "socket closed\n"); + print_message(msg_fd, "socket closed\n"); return; } } @@ -256,8 +253,7 @@ static void connect_proxy(struct connection *cnx) close(in_socket); close(out_socket); - if (cfg.verbose) - fprintf(stderr, "connection closed down\n"); + print_message(msg_fd, "connection closed down\n"); exit(0); } @@ -315,7 +311,7 @@ static void probing_read_process(struct connection* cnx, /* free(cnx); */ connect_proxy(cnx); exit(0); - case -1: log_message(LOG_ERR, "fork failed: err %d: %s\n", errno, strerror(errno)); + case -1: print_message(msg_system_error, "fork failed: err %d: %s\n", errno, strerror(errno)); break; default: /* parent */ break; @@ -337,7 +333,7 @@ int active_queue(struct connection* cnx, int fd) if (cnx->q[0].fd == fd) return 0; if (cnx->q[1].fd == fd) return 1; - log_message(LOG_ERR, "file descriptor %d not found in connection object\n", fd); + print_message(msg_int_error, "file descriptor %d not found in connection object\n", fd); return -1; } @@ -355,7 +351,7 @@ static void tcp_read_process(struct select_info* fd_info, case ST_PROBING: if (active_q == 1) { - fprintf(stderr, "Activity on fd2 while probing, impossible\n"); + print_message(msg_int_error, "Activity on fd2 while probing, impossible\n"); dump_connection(cnx); exit(1); } @@ -369,7 +365,7 @@ static void tcp_read_process(struct select_info* fd_info, break; default: /* illegal */ - log_message(LOG_ERR, "Illegal connection state %d\n", cnx->state); + print_message(msg_int_error, "Illegal connection state %d\n", cnx->state); dump_connection(cnx); exit(1); } @@ -389,11 +385,10 @@ static void cnx_read_process(struct select_info* fd_info, int fd) break; default: - log_message(LOG_ERR, "cnx_read_process: Illegal connection type %d\n", cnx->type); + print_message(msg_int_error, "cnx_read_process: Illegal connection type %d\n", cnx->type); dump_connection(cnx); exit(1); } - } /* Process a connection that is active in write */ @@ -439,13 +434,13 @@ void cnx_accept_process(struct select_info* fd_info, struct listen_endpoint* lis case SOCK_DGRAM: new_fd = udp_c2s_forward(fd, fd_info->collection, fd_info->max_fd); - fprintf(stderr, "new_fd %d\n", new_fd); + print_message(msg_fd, "new_fd %d\n", new_fd); if (new_fd == -1) return; break; default: - log_message(LOG_ERR, "Inconsistent cnx type: %d\n", type); + print_message(msg_int_error, "Inconsistent cnx type: %d\n", type); exit(1); return; } @@ -478,8 +473,7 @@ static void udp_timeouts(struct select_info* fd_info) time_t timeout = udp_timeout(cnx); if (!timeout) continue; /* Not a UDP connection */ if (cnx && (timeout <= now)) { - if (cfg.verbose > 3) - fprintf(stderr, "timed out UDP %d\n", cnx->target_sock); + print_message(msg_fd, "timed out UDP %d\n", cnx->target_sock); close(cnx->target_sock); FD_CLR(i, &fd_info->fds_r); FD_CLR(i, &fd_info->fds_w); @@ -537,8 +531,7 @@ void main_loop(struct listen_endpoint listen_sockets[], int num_addr_listen) memcpy(&readfds, &fd_info.fds_r, sizeof(readfds)); memcpy(&writefds, &fd_info.fds_w, sizeof(writefds)); - if (cfg.verbose) - fprintf(stderr, "selecting... max_fd=%d num_probing=%d\n", + print_message(msg_fd, "selecting... max_fd=%d num_probing=%d\n", fd_info.max_fd, fd_info.num_probing); res = select(fd_info.max_fd, &readfds, &writefds, NULL, fd_info.num_probing ? &tv : NULL); @@ -570,14 +563,13 @@ void main_loop(struct listen_endpoint listen_sockets[], int num_addr_listen) for (i = 0; i < fd_info.num_probing; i++) { struct connection* cnx = gap_get(fd_info.probing_list, i); if (!cnx || cnx->state != ST_PROBING) { - log_message(LOG_ERR, "Inconsistent probing: cnx=%0xp\n", cnx); + print_message(msg_int_error, "Inconsistent probing: cnx=%0xp\n", cnx); if (cnx) - log_message(LOG_ERR, "Inconsistent probing: state=%d\n", cnx); + print_message(msg_int_error, "Inconsistent probing: state=%d\n", cnx); exit(1); } if (cnx->probe_timeout < time(NULL)) { - if (cfg.verbose) - fprintf(stderr, "timeout slot %d\n", i); + print_message(msg_fd, "timeout slot %d\n", i); probing_read_process(cnx, &fd_info); } } @@ -596,7 +588,7 @@ void main_loop(struct listen_endpoint listen_sockets[], int num_addr_listen) void start_shoveler(int listen_socket) { - fprintf(stderr, "inetd mode is not supported in select mode\n"); + print_message(msg_config_error, "inetd mode is not supported in select mode\n"); exit(1); } diff --git a/sslhconf.cfg b/sslhconf.cfg index bb4b830..6a9a0fa 100644 --- a/sslhconf.cfg +++ b/sslhconf.cfg @@ -27,6 +27,11 @@ config: { items: ( { name: "verbose-config"; type: "int"; default: 0; }, { name: "verbose-config-error"; type: "int"; default: 3; }, + { name: "verbose-connections"; type: "int"; default: 0; }, + { name: "verbose-fd"; type: "int"; default: 0; }, + + { name: "verbose-system-error"; type: "int"; default: 3; }, + { name: "verbose-int-error"; type: "int"; default: 3; }, { name: "verbose"; type: "int"; short: "v" }, # to delete { name: "version"; type: "bool"; default: false; diff --git a/test.cfg b/test.cfg index 08b37df..5f31027 100644 --- a/test.cfg +++ b/test.cfg @@ -13,8 +13,14 @@ syslog_facility: "auth"; # Logging configuration # Value: 1: stdout; 2: syslog; 3: both +# Defaults should be sensible. Generally, you want *-error +# to be always enabled, to know if something is going wrong. #verbose-config: 3; # config: print configuration at startup #verbose-config-error: 3; #config-error: print configuration errors +#verbose-connections: 3; #config-connections: track connections +#verbose-fd: 3; # file descriptor activity, open/close/whatnot +#verbose-system-error: 3; # system call problem, i.e. malloc, fork, failing +#verbose-int-error: 3; # internal errors, the kind that should never happen # List of interfaces on which we should listen # Options: From 673c40954e368b3ca5e479aa5962f81fe643118b Mon Sep 17 00:00:00 2001 From: yrutschle Date: Sun, 19 Sep 2021 20:29:43 +0200 Subject: [PATCH 011/191] migrate sslh-fork to new log system --- sslh-fork.c | 17 ++++++++--------- sslh-select.c | 7 ++----- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/sslh-fork.c b/sslh-fork.c index 128c144..4ad699e 100644 --- a/sslh-fork.c +++ b/sslh-fork.c @@ -24,6 +24,7 @@ #include "probe.h" #include "sslh-conf.h" #include "udp-listener.h" +#include "log.h" #ifdef LIBBSD #include @@ -58,8 +59,7 @@ int shovel(struct connection *cnx) if (FD_ISSET(cnx->q[i].fd, &fds)) { res = fd2fd(&cnx->q[1-i], &cnx->q[i]); if (res == FD_CNXCLOSED) { - if (cfg.verbose) - fprintf(stderr, "%s %s", i ? "client" : "server", "socket closed\n"); + print_message(msg_fd, "%s %s", i ? "client" : "server", "socket closed\n"); return res; } } @@ -100,7 +100,7 @@ void start_shoveler(int in_socket) /* Timed out: it's necessarily SSH */ cnx.proto = timeout_protocol(); if (cfg.verbose) - log_message(LOG_INFO, "timed out, connect to %s\n", cnx.proto->name); + print_message(msg_fd, "timed out, connect to %s\n", cnx.proto->name); break; } } @@ -129,8 +129,7 @@ void start_shoveler(int in_socket) close(in_socket); close(out_socket); - if (cfg.verbose) - fprintf(stderr, "connection closed down\n"); + print_message(msg_fd, "connection closed down\n"); exit(0); } @@ -179,10 +178,10 @@ void tcp_listener(struct listen_endpoint* endpoint, int num_endpoints, int activ while (1) { in_socket = accept(endpoint[active_endpoint].socketfd, 0, 0); - if (cfg.verbose) fprintf(stderr, "accepted fd %d\n", in_socket); + print_message(msg_fd, "accepted fd %d\n", in_socket); switch(fork()) { - case -1: log_message(LOG_ERR, "fork failed: err %d: %s\n", errno, strerror(errno)); + case -1: print_message(msg_system_error, "fork failed: err %d: %s\n", errno, strerror(errno)); break; case 0: /* In child process */ @@ -214,13 +213,13 @@ void main_loop(struct listen_endpoint listen_sockets[], int num_addr_listen) listener_pid[i] = fork(); switch(listener_pid[i]) { /* Log if fork() fails for some reason */ - case -1: log_message(LOG_ERR, "fork failed: err %d: %s\n", errno, strerror(errno)); + case -1: print_message(msg_system_error, "fork failed: err %d: %s\n", errno, strerror(errno)); break; /* We're in the child, we have work to do */ case 0: set_listen_procname(&listen_sockets[i]); if (listen_sockets[i].type == SOCK_DGRAM) - log_message(LOG_ERR, "UDP not (yet?) supported in sslh-fork\n"); + print_message(msg_config_error, "UDP not (yet?) supported in sslh-fork\n"); else tcp_listener(listen_sockets, num_addr_listen, i); break; diff --git a/sslh-select.c b/sslh-select.c index 65fbadf..a5cc45b 100644 --- a/sslh-select.c +++ b/sslh-select.c @@ -83,7 +83,7 @@ static int tidy_connection(struct connection *cnx, struct select_info* fd_info) * and FD_CLR. Need to drop connections if we go above that limit */ static int fd_is_in_range(int fd) { if (fd >= FD_SETSIZE) { - log_message(LOG_ERR, "too many open file descriptor to monitor them all -- dropping connection\n"); + print_message(msg_system_error, "too many open file descriptor to monitor them all -- dropping connection\n"); return 0; } return 1; @@ -286,10 +286,7 @@ static void probing_read_process(struct connection* cnx, * data so probe the protocol */ if ((cnx->probe_timeout < time(NULL))) { cnx->proto = timeout_protocol(); - if (cfg.verbose) - log_message(LOG_INFO, - "timed out, connect to %s\n", - cnx->proto->name); + print_message(msg_fd, "timed out, connect to %s\n", cnx->proto->name); } else { res = probe_client_protocol(cnx); if (res == PROBE_AGAIN) From e5f16b93ce9686e54c65c0955bbab6e74becf318 Mon Sep 17 00:00:00 2001 From: yrutschle Date: Sun, 19 Sep 2021 21:54:47 +0200 Subject: [PATCH 012/191] hexdump writes to parametrable msg_info --- common.c | 10 ++++---- echosrv-conf.c | 2 +- echosrv-conf.h | 2 +- log.c | 7 +++++ log.h | 1 + probe.c | 28 ++++++++++++-------- probe.h | 3 ++- sslh-conf.c | 70 ++++++++++++++++++++++++++++++++++++++++---------- sslh-conf.h | 4 ++- sslhconf.cfg | 4 ++- test.cfg | 1 + 11 files changed, 97 insertions(+), 35 deletions(-) diff --git a/common.c b/common.c index 9274d19..131a10b 100644 --- a/common.c +++ b/common.c @@ -427,11 +427,11 @@ void init_cnx(struct connection *cnx) void dump_connection(struct connection *cnx) { - printf("state: %d\n", cnx->state); - printf("0: fd %d, %d deferred\n", cnx->q[0].fd, cnx->q[0].deferred_data_size); - hexdump(cnx->q[0].deferred_data, cnx->q[0].deferred_data_size); - printf("1: fd %d, %d deferred\n", cnx->q[1].fd, cnx->q[1].deferred_data_size); - hexdump(cnx->q[1].deferred_data, cnx->q[1].deferred_data_size); + print_message(msg_int_error, "state: %d\n", cnx->state); + print_message(msg_int_error, "0: fd %d, %d deferred\n", cnx->q[0].fd, cnx->q[0].deferred_data_size); + hexdump(msg_int_error, cnx->q[0].deferred_data, cnx->q[0].deferred_data_size); + print_message(msg_int_error, "1: fd %d, %d deferred\n", cnx->q[1].fd, cnx->q[1].deferred_data_size); + hexdump(msg_int_error, cnx->q[1].deferred_data, cnx->q[1].deferred_data_size); } diff --git a/echosrv-conf.c b/echosrv-conf.c index a5d1a71..581c61e 100644 --- a/echosrv-conf.c +++ b/echosrv-conf.c @@ -1,5 +1,5 @@ /* Generated by conf2struct (https://www.rutschle.net/tech/conf2struct/README) - * on Sat Sep 18 17:28:37 2021. + * on Sun Sep 19 21:54:08 2021. # conf2struct: generate libconf parsers that read to structs # Copyright (C) 2018-2021 Yves Rutschle diff --git a/echosrv-conf.h b/echosrv-conf.h index 0218d99..cfc9cc8 100644 --- a/echosrv-conf.h +++ b/echosrv-conf.h @@ -1,5 +1,5 @@ /* Generated by conf2struct (https://www.rutschle.net/tech/conf2struct/README) - * on Sat Sep 18 17:28:37 2021. + * on Sun Sep 19 21:54:08 2021. # conf2struct: generate libconf parsers that read to structs # Copyright (C) 2018-2021 Yves Rutschle diff --git a/log.c b/log.c index 4c01fcb..eb89515 100644 --- a/log.c +++ b/log.c @@ -57,6 +57,13 @@ msg_info msg_system_error = { }; +msg_info msg_packets = { + LOG_INFO, + &cfg.verbose_packets +}; + + + /* Bitmasks in verbose-* values */ #define MSG_STDOUT 1 diff --git a/log.h b/log.h index e99b77b..563d217 100644 --- a/log.h +++ b/log.h @@ -17,6 +17,7 @@ extern msg_info msg_config; extern msg_info msg_config_error; extern msg_info msg_fd; +extern msg_info msg_packets; extern msg_info msg_int_error; extern msg_info msg_system_error; diff --git a/probe.c b/probe.c index f3d4529..657842e 100644 --- a/probe.c +++ b/probe.c @@ -1,7 +1,7 @@ /* # probe.c: Code for probing protocols # -# Copyright (C) 2007-2019 Yves Rutschle +# Copyright (C) 2007-2021 Yves Rutschle # # This program is free software; you can redistribute it # and/or modify it under the terms of the GNU General Public @@ -27,6 +27,7 @@ #endif #include #include "probe.h" +#include "log.h" @@ -81,33 +82,38 @@ struct sslhcfg_protocols_item* timeout_protocol(void) /* From http://grapsus.net/blog/post/Hexadecimal-dump-in-C */ #define HEXDUMP_COLS 16 -void hexdump(const char *mem, unsigned int len) +void hexdump(msg_info msg_info, const char *mem, unsigned int len) { unsigned int i, j; + char str[10 + HEXDUMP_COLS * 4 + 2]; + int c = 0; /* index in str */ for(i = 0; i < len + ((len % HEXDUMP_COLS) ? (HEXDUMP_COLS - len % HEXDUMP_COLS) : 0); i++) { /* print offset */ if(i % HEXDUMP_COLS == 0) - fprintf(stderr, "0x%06x: ", i); + c += sprintf(&str[c], "0x%06x: ", i); /* print hex data */ if(i < len) - fprintf(stderr, "%02x ", 0xFF & mem[i]); + c += sprintf(&str[c], "%02x ", 0xFF & mem[i]); else /* end of block, just aligning for ASCII dump */ - fprintf(stderr, " "); + c+= sprintf(&str[c], " "); /* print ASCII dump */ if(i % HEXDUMP_COLS == (HEXDUMP_COLS - 1)) { for(j = i - (HEXDUMP_COLS - 1); j <= i; j++) { if(j >= len) /* end of block, not really printing */ - fputc(' ', stderr); + str[c++] = ' '; else if(isprint(mem[j])) /* printable char */ - fputc(0xFF & mem[j], stderr); + str[c++] = 0xFF & mem[j]; else /* other char */ - fputc('.', stderr); + str[c++] = '.'; } - fputc('\n', stderr); + str[c++] = '\n'; + str[c++] = 0; + print_message(msg_info, str); + c = 0; } } } @@ -345,8 +351,8 @@ int probe_buffer(char* buf, int len, struct sslhcfg_protocols_item** proto) int i, res, again = 0; if (cfg.verbose > 1) { - fprintf(stderr, "hexdump of incoming packet:\n"); - hexdump(buf, len); + print_message(msg_packets, "hexdump of incoming packet:\n"); + hexdump(msg_packets, buf, len); } *proto = NULL; diff --git a/probe.h b/probe.h index ecc972f..01248fc 100644 --- a/probe.h +++ b/probe.h @@ -5,6 +5,7 @@ #include "common.h" #include "tls.h" +#include "log.h" typedef enum { PROBE_NEXT, /* Enough data, probe failed -- it's some other protocol */ @@ -59,6 +60,6 @@ void set_ontimeout(const char* name); */ struct sslhcfg_protocols_item* timeout_protocol(void); -void hexdump(const char*, unsigned int); +void hexdump(msg_info, const char*, unsigned int); #endif diff --git a/sslh-conf.c b/sslh-conf.c index 6d8eb46..1abebb6 100644 --- a/sslh-conf.c +++ b/sslh-conf.c @@ -1,5 +1,5 @@ /* Generated by conf2struct (https://www.rutschle.net/tech/conf2struct/README) - * on Sun Sep 19 20:20:24 2021. + * on Sun Sep 19 21:54:06 2021. # conf2struct: generate libconf parsers that read to structs # Copyright (C) 2018-2021 Yves Rutschle @@ -446,7 +446,9 @@ 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_error; struct arg_int* sslhcfg_verbose_fd; + struct arg_int* sslhcfg_verbose_packets; struct arg_int* sslhcfg_verbose_system_error; struct arg_int* sslhcfg_verbose_int_error; struct arg_int* sslhcfg_verbose; @@ -789,7 +791,7 @@ static struct config_desc table_sslhcfg_listen[] = { }, { 0 } }; - + static struct config_desc table_sslhcfg[] = { @@ -838,7 +840,23 @@ static struct config_desc table_sslhcfg[] = { /* array_type */ -1, /* mandatory */ 0, /* optional */ 0, - /* default_val*/ .default_val.def_int = 0 + /* default_val*/ .default_val.def_int = 3 + }, + + { + /* 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 }, { @@ -857,6 +875,22 @@ static struct config_desc table_sslhcfg[] = { /* 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_system_error", /* type */ CFG_INT, @@ -1225,7 +1259,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: listen */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_listen, - .base_entry = & table_sslhcfg [19], + .base_entry = & table_sslhcfg [21], .targets = sslhcfg_listen_targets, @@ -1237,7 +1271,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: ssh */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_ssh, - .base_entry = & table_sslhcfg [20], + .base_entry = & table_sslhcfg [22], .targets = sslhcfg_ssh_targets, @@ -1249,7 +1283,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: tls */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_tls, - .base_entry = & table_sslhcfg [20], + .base_entry = & table_sslhcfg [22], .targets = sslhcfg_tls_targets, @@ -1261,7 +1295,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: openvpn */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_openvpn, - .base_entry = & table_sslhcfg [20], + .base_entry = & table_sslhcfg [22], .targets = sslhcfg_openvpn_targets, @@ -1273,7 +1307,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: tinc */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_tinc, - .base_entry = & table_sslhcfg [20], + .base_entry = & table_sslhcfg [22], .targets = sslhcfg_tinc_targets, @@ -1285,7 +1319,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: xmpp */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_xmpp, - .base_entry = & table_sslhcfg [20], + .base_entry = & table_sslhcfg [22], .targets = sslhcfg_xmpp_targets, @@ -1297,7 +1331,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: http */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_http, - .base_entry = & table_sslhcfg [20], + .base_entry = & table_sslhcfg [22], .targets = sslhcfg_http_targets, @@ -1309,7 +1343,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: adb */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_adb, - .base_entry = & table_sslhcfg [20], + .base_entry = & table_sslhcfg [22], .targets = sslhcfg_adb_targets, @@ -1321,7 +1355,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: socks5 */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_socks5, - .base_entry = & table_sslhcfg [20], + .base_entry = & table_sslhcfg [22], .targets = sslhcfg_socks5_targets, @@ -1333,7 +1367,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: syslog */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_syslog, - .base_entry = & table_sslhcfg [20], + .base_entry = & table_sslhcfg [22], .targets = sslhcfg_syslog_targets, @@ -1345,7 +1379,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: anyprot */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_anyprot, - .base_entry = & table_sslhcfg [20], + .base_entry = & table_sslhcfg [22], .targets = sslhcfg_anyprot_targets, @@ -2013,7 +2047,9 @@ int sslhcfg_cl_parse(int argc, char* argv[], struct sslhcfg_item* cfg) sslhcfg_verbose_config = arg_intn(NULL, "verbose-config", "", 0, 1, ""), sslhcfg_verbose_config_error = arg_intn(NULL, "verbose-config-error", "", 0, 1, ""), sslhcfg_verbose_connections = arg_intn(NULL, "verbose-connections", "", 0, 1, ""), + sslhcfg_verbose_connections_error = arg_intn(NULL, "verbose-connections-error", "", 0, 1, ""), sslhcfg_verbose_fd = arg_intn(NULL, "verbose-fd", "", 0, 1, ""), + sslhcfg_verbose_packets = arg_intn(NULL, "verbose-packets", "", 0, 1, ""), sslhcfg_verbose_system_error = arg_intn(NULL, "verbose-system-error", "", 0, 1, ""), sslhcfg_verbose_int_error = arg_intn(NULL, "verbose-int-error", "", 0, 1, ""), sslhcfg_verbose = arg_intn("v", "verbose", "", 0, 1, ""), @@ -2194,9 +2230,15 @@ void sslhcfg_fprint( fprintf(out, "verbose_connections: %d", sslhcfg->verbose_connections); 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_system_error: %d", sslhcfg->verbose_system_error); fprintf(out, "\n"); indent(out, depth); diff --git a/sslh-conf.h b/sslh-conf.h index 81ef678..f51ae62 100644 --- a/sslh-conf.h +++ b/sslh-conf.h @@ -1,5 +1,5 @@ /* Generated by conf2struct (https://www.rutschle.net/tech/conf2struct/README) - * on Sun Sep 19 20:20:24 2021. + * on Sun Sep 19 21:54:06 2021. # conf2struct: generate libconf parsers that read to structs # Copyright (C) 2018-2021 Yves Rutschle @@ -77,7 +77,9 @@ struct sslhcfg_item { int verbose_config; int verbose_config_error; int verbose_connections; + int verbose_connections_error; int verbose_fd; + int verbose_packets; int verbose_system_error; int verbose_int_error; int verbose; diff --git a/sslhconf.cfg b/sslhconf.cfg index 6a9a0fa..8e89324 100644 --- a/sslhconf.cfg +++ b/sslhconf.cfg @@ -27,8 +27,10 @@ config: { items: ( { name: "verbose-config"; type: "int"; default: 0; }, { name: "verbose-config-error"; type: "int"; default: 3; }, - { name: "verbose-connections"; type: "int"; default: 0; }, + { name: "verbose-connections"; type: "int"; default: 3; }, + { name: "verbose-connections-error"; type: "int"; default: 3; }, { name: "verbose-fd"; type: "int"; default: 0; }, + { name: "verbose-packets"; type: "int"; default: 0; }, { name: "verbose-system-error"; type: "int"; default: 3; }, { name: "verbose-int-error"; type: "int"; default: 3; }, diff --git a/test.cfg b/test.cfg index 5f31027..8a83312 100644 --- a/test.cfg +++ b/test.cfg @@ -19,6 +19,7 @@ syslog_facility: "auth"; #verbose-config-error: 3; #config-error: print configuration errors #verbose-connections: 3; #config-connections: track connections #verbose-fd: 3; # file descriptor activity, open/close/whatnot +verbose-packets: 3; # hexdump packets on which probing is done #verbose-system-error: 3; # system call problem, i.e. malloc, fork, failing #verbose-int-error: 3; # internal errors, the kind that should never happen From e6cbbe9511eecfd78e01ab1357d0e5e6d957ff9d Mon Sep 17 00:00:00 2001 From: yrutschle Date: Sun, 26 Sep 2021 15:53:21 +0200 Subject: [PATCH 013/191] migrate common.c to new logging system --- common.c | 29 +++++++++++------------------ log.c | 19 ++++++++++++++++++- log.h | 4 ++++ sslh-conf.c | 47 ++++++++++++++++++++++++++++++++++------------- sslh-conf.h | 3 ++- sslhconf.cfg | 1 + test.cfg | 3 +++ 7 files changed, 73 insertions(+), 33 deletions(-) diff --git a/common.c b/common.c index 131a10b..dece661 100644 --- a/common.c +++ b/common.c @@ -323,15 +323,14 @@ int connect_addr(struct connection *cnx, int fd_from, connect_blocking blocking) /* When transparent, make sure both connections use the same address family */ if (transparent && a->ai_family != from.ai_addr->sa_family) continue; - if (cfg.verbose) - fprintf(stderr, "connecting to %s family %d len %d\n", + print_message(msg_connections_try, "trying to connect to %s family %d len %d\n", sprintaddr(buf, sizeof(buf), a), a->ai_addr->sa_family, a->ai_addrlen); /* XXX Needs to match ai_family from fd_from when being transparent! */ fd = socket(a->ai_family, SOCK_STREAM, 0); if (fd == -1) { - log_message(LOG_ERR, "forward to %s failed:socket: %s\n", + print_message(msg_connections_error, "forward to %s failed:socket: %s\n", cnx->proto->name, strerror(errno)); } else { one = 1; @@ -351,7 +350,7 @@ int connect_addr(struct connection *cnx, int fd_from, connect_blocking blocking) /* EINPROGRESS indicates it might take time. If it eventually * fails, it'll be caught as a failed read */ if ((res == -1) && (errno != EINPROGRESS)) { - log_message(LOG_ERR, "forward to %s failed:connect: %s\n", + print_message(msg_connections_error, "forward to %s failed:connect: %s\n", cnx->proto->name, strerror(errno)); close(fd); continue; /* Try the next address */ @@ -371,9 +370,8 @@ int defer_write(struct queue *q, void* data, int data_size) { char *p; ptrdiff_t data_offset = q->deferred_data - q->begin_deferred_data; - if (cfg.verbose) - fprintf(stderr, "**** writing deferred on fd %d\n", q->fd); + print_message(msg_fd, "writing deferred on fd %d\n", q->fd); p = realloc(q->begin_deferred_data, data_offset + q->deferred_data_size + data_size); CHECK_ALLOC(p, "realloc"); @@ -394,8 +392,7 @@ int flush_deferred(struct queue *q) { int n; - if (cfg.verbose) - fprintf(stderr, "flushing deferred data to fd %d\n", q->fd); + print_message(msg_fd, "flushing deferred data to fd %d\n", q->fd); n = write(q->fd, q->deferred_data, q->deferred_data_size); if (n == -1) @@ -570,7 +567,7 @@ void resolve_name(struct addrinfo **out, char* fullname) /* Find port */ char *sep = strrchr(fullname, ':'); if (!sep) { /* No separator: parameter is just a port */ - fprintf(stderr, "%s: names must be fully specified as hostname:port\n", fullname); + print_message(msg_config_error, "%s: names must be fully specified as hostname:port\n", fullname); exit(1); } serv = sep+1; @@ -580,9 +577,9 @@ void resolve_name(struct addrinfo **out, char* fullname) res = resolve_split_name(out, host, serv); if (res) { - fprintf(stderr, "%s `%s'\n", gai_strerror(res), fullname); + print_message(msg_config_error, "%s `%s'\n", gai_strerror(res), fullname); if (res == EAI_SERVICE) - fprintf(stderr, "(Check you have specified all ports)\n"); + print_message(msg_config_error, "(Check you have specified all ports)\n"); exit(4); } } @@ -664,8 +661,7 @@ int check_access_rights(int in_socket, const char* service) /* extract peer address */ res = getnameinfo(&peer.saddr, size, addr_str, sizeof(addr_str), NULL, 0, NI_NUMERICHOST); if (res) { - if (cfg.verbose) - fprintf(stderr, "getnameinfo(NI_NUMERICHOST):%s\n", gai_strerror(res)); + print_message(msg_system_error, "getnameinfo(NI_NUMERICHOST):%s\n", gai_strerror(res)); strcpy(addr_str, STRING_UNKNOWN); } /* extract peer name */ @@ -673,15 +669,12 @@ int check_access_rights(int in_socket, const char* service) if (!cfg.numeric) { res = getnameinfo(&peer.saddr, size, host, sizeof(host), NULL, 0, NI_NAMEREQD); if (res) { - if (cfg.verbose) - fprintf(stderr, "getnameinfo(NI_NAMEREQD):%s\n", gai_strerror(res)); + print_message(msg_system_error, "getnameinfo(NI_NAMEREQD):%s\n", gai_strerror(res)); } } if (!hosts_ctl(service, host, addr_str, STRING_UNKNOWN)) { - if (cfg.verbose) - fprintf(stderr, "access denied\n"); - log_message(LOG_INFO, "connection from %s(%s): access denied", host, addr_str); + print_message(msg_connections, "connection from %s(%s): access denied", host, addr_str); close(in_socket); return -1; } diff --git a/log.c b/log.c index eb89515..217d86e 100644 --- a/log.c +++ b/log.c @@ -56,12 +56,29 @@ msg_info msg_system_error = { &cfg.verbose_system_error }; - msg_info msg_packets = { LOG_INFO, &cfg.verbose_packets }; +/* additional info when attempting outgoing connections */ +msg_info msg_connections_try = { + LOG_DEBUG, + &cfg.verbose_connections_try +}; + +/* Connection information and failures (e.g. forbidden by policy) */ +msg_info msg_connections = { + LOG_INFO, + &cfg.verbose_connections +}; + +/* Connection failures, e.g. target server not present */ +msg_info msg_connections_error = { + LOG_ERR, + &cfg.verbose_connections_error +}; + diff --git a/log.h b/log.h index 563d217..7d2ea7a 100644 --- a/log.h +++ b/log.h @@ -22,4 +22,8 @@ extern msg_info msg_packets; extern msg_info msg_int_error; extern msg_info msg_system_error; +extern msg_info msg_connections_try; +extern msg_info msg_connections_error; +extern msg_info msg_connections; + #endif /* LOG_H */ diff --git a/sslh-conf.c b/sslh-conf.c index 1abebb6..e280054 100644 --- a/sslh-conf.c +++ b/sslh-conf.c @@ -1,5 +1,5 @@ /* Generated by conf2struct (https://www.rutschle.net/tech/conf2struct/README) - * on Sun Sep 19 21:54:06 2021. + * on Sun Sep 26 15:51:02 2021. # conf2struct: generate libconf parsers that read to structs # Copyright (C) 2018-2021 Yves Rutschle @@ -446,6 +446,7 @@ 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; @@ -791,7 +792,7 @@ static struct config_desc table_sslhcfg_listen[] = { }, { 0 } }; - + static struct config_desc table_sslhcfg[] = { @@ -843,6 +844,22 @@ static struct config_desc table_sslhcfg[] = { /* 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, @@ -1259,7 +1276,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: listen */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_listen, - .base_entry = & table_sslhcfg [21], + .base_entry = & table_sslhcfg [22], .targets = sslhcfg_listen_targets, @@ -1271,7 +1288,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: ssh */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_ssh, - .base_entry = & table_sslhcfg [22], + .base_entry = & table_sslhcfg [23], .targets = sslhcfg_ssh_targets, @@ -1283,7 +1300,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: tls */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_tls, - .base_entry = & table_sslhcfg [22], + .base_entry = & table_sslhcfg [23], .targets = sslhcfg_tls_targets, @@ -1295,7 +1312,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: openvpn */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_openvpn, - .base_entry = & table_sslhcfg [22], + .base_entry = & table_sslhcfg [23], .targets = sslhcfg_openvpn_targets, @@ -1307,7 +1324,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: tinc */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_tinc, - .base_entry = & table_sslhcfg [22], + .base_entry = & table_sslhcfg [23], .targets = sslhcfg_tinc_targets, @@ -1319,7 +1336,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: xmpp */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_xmpp, - .base_entry = & table_sslhcfg [22], + .base_entry = & table_sslhcfg [23], .targets = sslhcfg_xmpp_targets, @@ -1331,7 +1348,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: http */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_http, - .base_entry = & table_sslhcfg [22], + .base_entry = & table_sslhcfg [23], .targets = sslhcfg_http_targets, @@ -1343,7 +1360,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: adb */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_adb, - .base_entry = & table_sslhcfg [22], + .base_entry = & table_sslhcfg [23], .targets = sslhcfg_adb_targets, @@ -1355,7 +1372,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: socks5 */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_socks5, - .base_entry = & table_sslhcfg [22], + .base_entry = & table_sslhcfg [23], .targets = sslhcfg_socks5_targets, @@ -1367,7 +1384,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: syslog */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_syslog, - .base_entry = & table_sslhcfg [22], + .base_entry = & table_sslhcfg [23], .targets = sslhcfg_syslog_targets, @@ -1379,7 +1396,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: anyprot */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_anyprot, - .base_entry = & table_sslhcfg [22], + .base_entry = & table_sslhcfg [23], .targets = sslhcfg_anyprot_targets, @@ -2047,6 +2064,7 @@ int sslhcfg_cl_parse(int argc, char* argv[], struct sslhcfg_item* cfg) sslhcfg_verbose_config = arg_intn(NULL, "verbose-config", "", 0, 1, ""), sslhcfg_verbose_config_error = arg_intn(NULL, "verbose-config-error", "", 0, 1, ""), sslhcfg_verbose_connections = arg_intn(NULL, "verbose-connections", "", 0, 1, ""), + sslhcfg_verbose_connections_try = arg_intn(NULL, "verbose-connections-try", "", 0, 1, ""), sslhcfg_verbose_connections_error = arg_intn(NULL, "verbose-connections-error", "", 0, 1, ""), sslhcfg_verbose_fd = arg_intn(NULL, "verbose-fd", "", 0, 1, ""), sslhcfg_verbose_packets = arg_intn(NULL, "verbose-packets", "", 0, 1, ""), @@ -2230,6 +2248,9 @@ void sslhcfg_fprint( 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); diff --git a/sslh-conf.h b/sslh-conf.h index f51ae62..ad4a3e2 100644 --- a/sslh-conf.h +++ b/sslh-conf.h @@ -1,5 +1,5 @@ /* Generated by conf2struct (https://www.rutschle.net/tech/conf2struct/README) - * on Sun Sep 19 21:54:06 2021. + * on Sun Sep 26 15:51:02 2021. # conf2struct: generate libconf parsers that read to structs # Copyright (C) 2018-2021 Yves Rutschle @@ -77,6 +77,7 @@ struct sslhcfg_item { int verbose_config; int verbose_config_error; int verbose_connections; + int verbose_connections_try; int verbose_connections_error; int verbose_fd; int verbose_packets; diff --git a/sslhconf.cfg b/sslhconf.cfg index 8e89324..57fedc4 100644 --- a/sslhconf.cfg +++ b/sslhconf.cfg @@ -28,6 +28,7 @@ config: { { name: "verbose-config"; type: "int"; default: 0; }, { name: "verbose-config-error"; type: "int"; default: 3; }, { name: "verbose-connections"; type: "int"; default: 3; }, + { name: "verbose-connections-try"; type: "int"; default: 0; }, { name: "verbose-connections-error"; type: "int"; default: 3; }, { name: "verbose-fd"; type: "int"; default: 0; }, { name: "verbose-packets"; type: "int"; default: 0; }, diff --git a/test.cfg b/test.cfg index 8a83312..2b95c65 100644 --- a/test.cfg +++ b/test.cfg @@ -22,6 +22,9 @@ syslog_facility: "auth"; verbose-packets: 3; # hexdump packets on which probing is done #verbose-system-error: 3; # system call problem, i.e. malloc, fork, failing #verbose-int-error: 3; # internal errors, the kind that should never happen +#verbose-connections-try: 3; # connection attempts towards targets +#verbose-connections: 3; # trace established incoming address to forward address +# verbose-connections-error: 3; # connection errors # List of interfaces on which we should listen # Options: From e9e7ada06967e8fcf06c4b70c8e697be51cf67ca Mon Sep 17 00:00:00 2001 From: yrutschle Date: Sun, 26 Sep 2021 16:13:23 +0200 Subject: [PATCH 014/191] convert to hash-based titles --- doc/tproxy.md | 31 +++++++++++-------------------- 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/doc/tproxy.md b/doc/tproxy.md index 3baac7f..249b077 100644 --- a/doc/tproxy.md +++ b/doc/tproxy.md @@ -1,17 +1,14 @@ -Transparent Proxy to Two Hosts -============================== +# Transparent Proxy to Two Hosts Tutorial by Sean Warner. 19 June 2019 20:35 -Aim ---- +## Aim * Show that `sslh` can transparently proxy requests from the internet to services on two separate hosts that are both on the same LAN. * The IP address of the client initiating the request is what the destination should see… and not the IP address of the host that `sslh` is running on, which is what happens when `sslh` is not running in transparent mode. * The solution here only works for my very specific use-case but hopefully others can adapt it to suits their needs. -Overview of my Network ----------------------- +## Overview of my Network Two Raspberry Pis on my home LAN: * Pi A: 192.168.1.124 – `sslh` (Port 4433), Apache2 web server for https (port 443), `stunnel` (port 4480) to decrypt ssh traffic and forward to SSH server (also on Pi A at Port 1022) @@ -20,8 +17,7 @@ Two Raspberry Pis on my home LAN: ![Architecture](tproxy.svg) -`sslh` build ------------- +## `sslh` build   `sslh` Version: sslh v1.19c-2-gf451cc8-dirty. @@ -47,8 +43,7 @@ MAN=sslh.8.gz         # man page name # itself ```   -systemd setup -------------- +## systemd setup Create an sslh systemd service file... ``` @@ -83,8 +78,7 @@ Start it again to test… # systemctl start sslh ```   -Configure `sslh` ----------------- +## Configure `sslh` First stop `sslh` then open the config file and replace with below, save and start `sslh` again ``` @@ -123,8 +117,7 @@ protocols: ); ```   -Configure `stunnel` -------------------- +## Configure `stunnel` First stop `stunnel` then open the config file and replace with below, save and start `stunnel` again ``` @@ -151,8 +144,7 @@ connect = 192.168.1.124:1022 TIMEOUTclose = 0 ```   -Configure iptables for Pi A --------------------------- +## Configure iptables for Pi A The `_add.sh` script creates the rules, the `_rm.sh` script removes the rules. They will be lost if you reboot but there are ways to make them load again on start-up.. @@ -194,8 +186,7 @@ Now run the "add" script on Pi A! # piA_tproxy_rm.sh ``` -Configure iptables for Pi B --------------------------- +# Configure iptables for Pi B ``` # nano /usr/local/sbin/piB_tproxy_add.sh @@ -235,8 +226,8 @@ Now run the "add" script on Pi B! # piB_tproxy_rm.sh ```   -Testing -------- +## Testing + * Getting to sshd on PiA I did this test using 4G from my phone (outside the LAN) From 3fb1201b3fbd7a3f8ba743a002b6d29c424abc0f Mon Sep 17 00:00:00 2001 From: yrutschle Date: Sun, 26 Sep 2021 16:28:06 +0200 Subject: [PATCH 015/191] merged transparent proxy setups together, clarifying what is not known to work --- doc/config.md | 156 ++--------------------------------------- doc/tproxy.md | 187 +++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 182 insertions(+), 161 deletions(-) diff --git a/doc/config.md b/doc/config.md index ad1c903..42d609f 100644 --- a/doc/config.md +++ b/doc/config.md @@ -99,158 +99,12 @@ Then you can run sslh-select as an unpriviledged user, e.g.: Transparent proxy support ------------------------- -On Linux and FreeBSD you can use the `--transparent` option to -request transparent proxying. This means services behind `sslh` -(Apache, `sshd` and so on) will see the external IP and ports -as if the external world connected directly to them. This -simplifies IP-based access control (or makes it possible at -all). +Transparent proxying allows the target server to see the +original client IP address, i.e. `sslh` becomes invisible. +This makes it easier to use the server's logs, and potential +IP-based banning ability. -You can refer to Sean Warn'ѕ [tutorial](tproxy.md) for a -different set-up which enables transparent proxying between -two different machines. The following may only work if -`sslh` and the final servers are on the same machine. - -Note that getting this to work is very tricky and -detail-dependant: depending on whether the target server and -sslh are on the same machine, different machines, or -different dockers, and tool versions, all seem to change the -required network configuration somewhat. If it doesn't work, -it's almost certain that the problem is not linked to `sslh` -but to the network setup that surrounds it. - -Linux: - -`sslh` needs extended rights to perform this: you'll need to -give it `CAP_NET_RAW` capabilities (see appropriate chapter) -or run it as root (but don't do that). - -The firewalling tables also need to be adjusted as follows. -I don't think it is possible to have `httpd` and `sslh` both listen to 443 in -this scheme -- let me know if you manage that: - - # Set route_localnet = 1 on all interfaces so that ssl can use "localhost" as destination - sysctl -w net.ipv4.conf.default.route_localnet=1 - sysctl -w net.ipv4.conf.all.route_localnet=1 - - # DROP martian packets as they would have been if route_localnet was zero - # Note: packets not leaving the server aren't affected by this, thus sslh will still work - iptables -t raw -A PREROUTING ! -i lo -d 127.0.0.0/8 -j DROP - iptables -t mangle -A POSTROUTING ! -o lo -s 127.0.0.0/8 -j DROP - - # Mark all connections made by ssl for special treatment (here sslh is run as user "sslh") - iptables -t nat -A OUTPUT -m owner --uid-owner sslh -p tcp --tcp-flags FIN,SYN,RST,ACK SYN -j CONNMARK --set-xmark 0x01/0x0f - - # Outgoing packets that should go to sslh instead have to be rerouted, so mark them accordingly (copying over the connection mark) - iptables -t mangle -A OUTPUT ! -o lo -p tcp -m connmark --mark 0x01/0x0f -j CONNMARK --restore-mark --mask 0x0f - - # Configure routing for those marked packets - ip rule add fwmark 0x1 lookup 100 - ip route add local 0.0.0.0/0 dev lo table 100 - -Tranparent proxying with IPv6 is similarly set up as follows: - - # Set route_localnet = 1 on all interfaces so that ssl can use "localhost" as destination - # Not sure if this is needed for ipv6 though - sysctl -w net.ipv4.conf.default.route_localnet=1 - sysctl -w net.ipv4.conf.all.route_localnet=1 - - # DROP martian packets as they would have been if route_localnet was zero - # Note: packets not leaving the server aren't affected by this, thus sslh will still work - ip6tables -t raw -A PREROUTING ! -i lo -d ::1/128 -j DROP - ip6tables -t mangle -A POSTROUTING ! -o lo -s ::1/128 -j DROP - - # Mark all connections made by ssl for special treatment (here sslh is run as user "sslh") - ip6tables -t nat -A OUTPUT -m owner --uid-owner sslh -p tcp --tcp-flags FIN,SYN,RST,ACK SYN -j CONNMARK --set-xmark 0x01/0x0f - - # Outgoing packets that should go to sslh instead have to be rerouted, so mark them accordingly (copying over the connection mark) - ip6tables -t mangle -A OUTPUT ! -o lo -p tcp -m connmark --mark 0x01/0x0f -j CONNMARK --restore-mark --mask 0x0f - - # Configure routing for those marked packets - ip -6 rule add fwmark 0x1 lookup 100 - ip -6 route add local ::/0 dev lo table 100 - -Explanation: -To be able to use `localhost` as destination in your sslh config along with transparent proxying -you have to allow routing of loopback addresses as done above. -This is something you usually should not do (see [this stackoverflow post](https://serverfault.com/questions/656279/how-to-force-linux-to-accept-packet-with-loopback-ip/656484#656484)) -The two `DROP` iptables rules emulate the behaviour of `route_localnet` set to off (with one small difference: -allowing the reroute-check to happen after the fwmark is set on packets destined for sslh). -See [this diagram](https://upload.wikimedia.org/wikipedia/commons/3/37/Netfilter-packet-flow.svg) for a good visualisation -showing how packets will traverse the iptables chains. - -Note: -You have to run `sslh` as dedicated user (in this example the user is also named `sslh`), to not mess up with your normal networking. -These rules will allow you to connect directly to ssh on port -22 (or to any other service behind sslh) as well as through sslh on port 443. - -Also remember that iptables configuration and ip routes and -rules won't be necessarily persisted after you reboot. Make -sure to save them properly. For example in CentOS7, you would -do `iptables-save > /etc/sysconfig/iptables`, and add both -`ip` commands to your `/etc/rc.local`. - -FreeBSD: - -Given you have no firewall defined yet, you can use the following configuration -to have ipfw properly redirect traffic back to sslh - - /etc/rc.conf - firewall_enable="YES" - firewall_type="open" - firewall_logif="YES" - firewall_coscripts="/etc/ipfw/sslh.rules" - - -/etc/ipfw/sslh.rules - - #! /bin/sh - - # ssl - ipfw add 20000 fwd 192.0.2.1,443 log tcp from 192.0.2.1 8443 to any out - ipfw add 20010 fwd 2001:db8::1,443 log tcp from 2001:db8::1 8443 to any out - - # ssh - ipfw add 20100 fwd 192.0.2.1,443 log tcp from 192.0.2.1 8022 to any out - ipfw add 20110 fwd 2001:db8::1,443 log tcp from 2001:db8::1 8022 to any out - - # xmpp - ipfw add 20200 fwd 192.0.2.1,443 log tcp from 192.0.2.1 5222 to any out - ipfw add 20210 fwd 2001:db8::1,443 log tcp from 2001:db8::1 5222 to any out - - # openvpn (running on other internal system) - ipfw add 20300 fwd 192.0.2.1,443 log tcp from 198.51.100.7 1194 to any out - ipfw add 20310 fwd 2001:db8::1,443 log tcp from 2001:db8:1::7 1194 to any out - -General notes: - - -This will only work if `sslh` does not use any loopback -addresses (no `127.0.0.1` or `localhost`), you'll need to use -explicit IP addresses (or names): - - sslh --listen 192.168.0.1:443 --ssh 192.168.0.1:22 --tls 192.168.0.1:4443 - -This will not work: - - sslh --listen 192.168.0.1:443 --ssh 127.0.0.1:22 --tls 127.0.0.1:4443 - -Transparent proxying means the target server sees the real -origin address, so it means if the client connects using -IPv6, the server must also support IPv6. It is easy to -support both IPv4 and IPv6 by configuring the server -accordingly, and setting `sslh` to connect to a name that -resolves to both IPv4 and IPv6, e.g.: - - sslh --transparent --listen :443 --ssh insideaddr:22 - - /etc/hosts: - 192.168.0.1 insideaddr - 201::::2 insideaddr - -Upon incoming IPv6 connection, `sslh` will first try to -connect to the IPv4 address (which will fail), then connect -to the IPv6 address. +Set up can get complicated, so it has it's own [document](tproxy.md). Systemd Socket Activation ------------------------- diff --git a/doc/tproxy.md b/doc/tproxy.md index 249b077..0a91773 100644 --- a/doc/tproxy.md +++ b/doc/tproxy.md @@ -1,14 +1,181 @@ -# Transparent Proxy to Two Hosts +# Transparent proxy + +On Linux and FreeBSD you can use the `--transparent` option to +request transparent proxying. This means services behind `sslh` +(Apache, `sshd` and so on) will see the external IP and ports +as if the external world connected directly to them. This +simplifies IP-based access control (or makes it possible at +all). + +This document shows recipes that may help to do that. + +Note that getting this to work is very tricky and +detail-dependant: depending on whether the target server and +sslh are on the same machine, different machines, or +different dockers, and tool versions, all seem to change the +required network configuration somewhat. If it doesn't work, +it's almost certain that the problem is not linked to `sslh` +but to the network setup that surrounds it. If in trouble, +it might be worth trying to set up the network rules +with a simpler server than `sslh`, such as +[`socat`](http://www.dest-unreach.org/socat/) + + +Users have tried to do at least the following: + + * `sslh` and the target servers run on the same host (see [below](#transparent-proxy-to-one-host)) + * `sslh` runs on a host, and the target servers run on LXC or dockers running on the same host. No known working setup. + * `sslh` runs on a host, and the target servers run on different hosts on the same local network(see [below](#transparent-proxy-to-two-hosts)) + * `sslh` runs on a host, and the target servers run on a different host on a different network (there is a [usecase](https://github.com/yrutschle/sslh/issues/295) for this). No known working setup, and it's unclear it is possible. + + +## Transparent proxy to one host + +### Linux + +`sslh` needs extended rights to perform this: you'll need to +give it `CAP_NET_RAW` capabilities (see appropriate chapter) +or run it as root (but don't do that). + +The firewalling tables also need to be adjusted as follows. +I don't think it is possible to have `httpd` and `sslh` both listen to 443 in +this scheme -- let me know if you manage that: + + # Set route_localnet = 1 on all interfaces so that ssl can use "localhost" as destination + sysctl -w net.ipv4.conf.default.route_localnet=1 + sysctl -w net.ipv4.conf.all.route_localnet=1 + + # DROP martian packets as they would have been if route_localnet was zero + # Note: packets not leaving the server aren't affected by this, thus sslh will still work + iptables -t raw -A PREROUTING ! -i lo -d 127.0.0.0/8 -j DROP + iptables -t mangle -A POSTROUTING ! -o lo -s 127.0.0.0/8 -j DROP + + # Mark all connections made by ssl for special treatment (here sslh is run as user "sslh") + iptables -t nat -A OUTPUT -m owner --uid-owner sslh -p tcp --tcp-flags FIN,SYN,RST,ACK SYN -j CONNMARK --set-xmark 0x01/0x0f + + # Outgoing packets that should go to sslh instead have to be rerouted, so mark them accordingly (copying over the connection mark) + iptables -t mangle -A OUTPUT ! -o lo -p tcp -m connmark --mark 0x01/0x0f -j CONNMARK --restore-mark --mask 0x0f + + # Configure routing for those marked packets + ip rule add fwmark 0x1 lookup 100 + ip route add local 0.0.0.0/0 dev lo table 100 + +Tranparent proxying with IPv6 is similarly set up as follows: + + # Set route_localnet = 1 on all interfaces so that ssl can use "localhost" as destination + # Not sure if this is needed for ipv6 though + sysctl -w net.ipv4.conf.default.route_localnet=1 + sysctl -w net.ipv4.conf.all.route_localnet=1 + + # DROP martian packets as they would have been if route_localnet was zero + # Note: packets not leaving the server aren't affected by this, thus sslh will still work + ip6tables -t raw -A PREROUTING ! -i lo -d ::1/128 -j DROP + ip6tables -t mangle -A POSTROUTING ! -o lo -s ::1/128 -j DROP + + # Mark all connections made by ssl for special treatment (here sslh is run as user "sslh") + ip6tables -t nat -A OUTPUT -m owner --uid-owner sslh -p tcp --tcp-flags FIN,SYN,RST,ACK SYN -j CONNMARK --set-xmark 0x01/0x0f + + # Outgoing packets that should go to sslh instead have to be rerouted, so mark them accordingly (copying over the connection mark) + ip6tables -t mangle -A OUTPUT ! -o lo -p tcp -m connmark --mark 0x01/0x0f -j CONNMARK --restore-mark --mask 0x0f + + # Configure routing for those marked packets + ip -6 rule add fwmark 0x1 lookup 100 + ip -6 route add local ::/0 dev lo table 100 + +Explanation: +To be able to use `localhost` as destination in your sslh config along with transparent proxying +you have to allow routing of loopback addresses as done above. +This is something you usually should not do (see [this stackoverflow post](https://serverfault.com/questions/656279/how-to-force-linux-to-accept-packet-with-loopback-ip/656484#656484)) +The two `DROP` iptables rules emulate the behaviour of `route_localnet` set to off (with one small difference: +allowing the reroute-check to happen after the fwmark is set on packets destined for sslh). +See [this diagram](https://upload.wikimedia.org/wikipedia/commons/3/37/Netfilter-packet-flow.svg) for a good visualisation +showing how packets will traverse the iptables chains. + +Note: +You have to run `sslh` as dedicated user (in this example the user is also named `sslh`), to not mess up with your normal networking. +These rules will allow you to connect directly to ssh on port +22 (or to any other service behind sslh) as well as through sslh on port 443. + +Also remember that iptables configuration and ip routes and +rules won't be necessarily persisted after you reboot. Make +sure to save them properly. For example in CentOS7, you would +do `iptables-save > /etc/sysconfig/iptables`, and add both +`ip` commands to your `/etc/rc.local`. + +### FreeBSD + +Given you have no firewall defined yet, you can use the following configuration +to have ipfw properly redirect traffic back to sslh + + /etc/rc.conf + firewall_enable="YES" + firewall_type="open" + firewall_logif="YES" + firewall_coscripts="/etc/ipfw/sslh.rules" + + +/etc/ipfw/sslh.rules + + #! /bin/sh + + # ssl + ipfw add 20000 fwd 192.0.2.1,443 log tcp from 192.0.2.1 8443 to any out + ipfw add 20010 fwd 2001:db8::1,443 log tcp from 2001:db8::1 8443 to any out + + # ssh + ipfw add 20100 fwd 192.0.2.1,443 log tcp from 192.0.2.1 8022 to any out + ipfw add 20110 fwd 2001:db8::1,443 log tcp from 2001:db8::1 8022 to any out + + # xmpp + ipfw add 20200 fwd 192.0.2.1,443 log tcp from 192.0.2.1 5222 to any out + ipfw add 20210 fwd 2001:db8::1,443 log tcp from 2001:db8::1 5222 to any out + + # openvpn (running on other internal system) + ipfw add 20300 fwd 192.0.2.1,443 log tcp from 198.51.100.7 1194 to any out + ipfw add 20310 fwd 2001:db8::1,443 log tcp from 2001:db8:1::7 1194 to any out + +General notes: + + +This will only work if `sslh` does not use any loopback +addresses (no `127.0.0.1` or `localhost`), you'll need to use +explicit IP addresses (or names): + + sslh --listen 192.168.0.1:443 --ssh 192.168.0.1:22 --tls 192.168.0.1:4443 + +This will not work: + + sslh --listen 192.168.0.1:443 --ssh 127.0.0.1:22 --tls 127.0.0.1:4443 + +Transparent proxying means the target server sees the real +origin address, so it means if the client connects using +IPv6, the server must also support IPv6. It is easy to +support both IPv4 and IPv6 by configuring the server +accordingly, and setting `sslh` to connect to a name that +resolves to both IPv4 and IPv6, e.g.: + + sslh --transparent --listen :443 --ssh insideaddr:22 + + /etc/hosts: + 192.168.0.1 insideaddr + 201::::2 insideaddr + +Upon incoming IPv6 connection, `sslh` will first try to +connect to the IPv4 address (which will fail), then connect +to the IPv6 address. + + +## Transparent Proxy to Two Hosts Tutorial by Sean Warner. 19 June 2019 20:35 -## Aim +### Aim * Show that `sslh` can transparently proxy requests from the internet to services on two separate hosts that are both on the same LAN. * The IP address of the client initiating the request is what the destination should see… and not the IP address of the host that `sslh` is running on, which is what happens when `sslh` is not running in transparent mode. * The solution here only works for my very specific use-case but hopefully others can adapt it to suits their needs. -## Overview of my Network +### Overview of my Network Two Raspberry Pis on my home LAN: * Pi A: 192.168.1.124 – `sslh` (Port 4433), Apache2 web server for https (port 443), `stunnel` (port 4480) to decrypt ssh traffic and forward to SSH server (also on Pi A at Port 1022) @@ -17,7 +184,7 @@ Two Raspberry Pis on my home LAN: ![Architecture](tproxy.svg) -## `sslh` build +### `sslh` build   `sslh` Version: sslh v1.19c-2-gf451cc8-dirty. @@ -43,7 +210,7 @@ MAN=sslh.8.gz         # man page name # itself ```   -## systemd setup +### systemd setup Create an sslh systemd service file... ``` @@ -78,7 +245,7 @@ Start it again to test… # systemctl start sslh ```   -## Configure `sslh` +### Configure `sslh` First stop `sslh` then open the config file and replace with below, save and start `sslh` again ``` @@ -117,7 +284,7 @@ protocols: ); ```   -## Configure `stunnel` +### Configure `stunnel` First stop `stunnel` then open the config file and replace with below, save and start `stunnel` again ``` @@ -144,7 +311,7 @@ connect = 192.168.1.124:1022 TIMEOUTclose = 0 ```   -## Configure iptables for Pi A +### Configure iptables for Pi A The `_add.sh` script creates the rules, the `_rm.sh` script removes the rules. They will be lost if you reboot but there are ways to make them load again on start-up.. @@ -186,7 +353,7 @@ Now run the "add" script on Pi A! # piA_tproxy_rm.sh ``` -# Configure iptables for Pi B +## Configure iptables for Pi B ``` # nano /usr/local/sbin/piB_tproxy_add.sh @@ -226,7 +393,7 @@ Now run the "add" script on Pi B! # piB_tproxy_rm.sh ```   -## Testing +### Testing * Getting to sshd on PiA From 6ea7d48f86d9f9d96ff4eb2c91f8250131627323 Mon Sep 17 00:00:00 2001 From: yrutschle Date: Sun, 26 Sep 2021 16:55:31 +0200 Subject: [PATCH 016/191] migrate tls.c and probe.c to new log system --- log.c | 12 ++++++++++ log.h | 3 +++ probe.c | 9 +++---- sslh-conf.c | 68 ++++++++++++++++++++++++++++++++++++++++++---------- sslh-conf.h | 4 +++- sslhconf.cfg | 3 +++ tls.c | 13 +++++----- 7 files changed, 88 insertions(+), 24 deletions(-) diff --git a/log.c b/log.c index 217d86e..e83465a 100644 --- a/log.c +++ b/log.c @@ -80,6 +80,18 @@ msg_info msg_connections_error = { }; +/* comment the probing process */ +msg_info msg_probe_info = { + LOG_INFO, + &cfg.verbose_probe_info +}; + +/* probing errors, e.g. inconsistent data in connections */ +msg_info msg_probe_error = { + LOG_ERR, + &cfg.verbose_probe_error +}; + /* Bitmasks in verbose-* values */ diff --git a/log.h b/log.h index 7d2ea7a..622885a 100644 --- a/log.h +++ b/log.h @@ -26,4 +26,7 @@ extern msg_info msg_connections_try; extern msg_info msg_connections_error; extern msg_info msg_connections; +extern msg_info msg_probe_info; +extern msg_info msg_probe_error; + #endif /* LOG_H */ diff --git a/probe.c b/probe.c index 657842e..a1171b7 100644 --- a/probe.c +++ b/probe.c @@ -334,7 +334,7 @@ static int regex_probe(const char *p, ssize_t len, struct sslhcfg_protocols_item return 0; #else /* Should never happen as we check when loading config file */ - fprintf(stderr, "FATAL: regex probe called but not built in\n"); + print_message(msg_int_error, "FATAL: regex probe called but not built in\n"); exit(5); #endif } @@ -362,20 +362,21 @@ int probe_buffer(char* buf, int len, struct sslhcfg_protocols_item** proto) if (! p->probe) continue; - if (cfg.verbose) fprintf(stderr, "probing for %s\n", p->name); + print_message(msg_probe_info, "probing for %s\n", p->name); /* Don't probe last protocol if it is anyprot (and store last protocol) */ if ((i == cfg.protocols_len - 1) && (!strcmp(p->name, "anyprot"))) break; if (p->minlength_is_present && (len < p->minlength )) { - fprintf(stderr, "input too short, %d bytes but need %d\n", len , p->minlength); + print_message(msg_probe_info, "input too short, %d bytes but need %d\n", + len , p->minlength); again++; continue; } res = p->probe(buf, len, p); - if (cfg.verbose) fprintf(stderr, "probed for %s: %s\n", p->name, probe_str[res]); + print_message(msg_probe_info, "probed for %s: %s\n", p->name, probe_str[res]); if (res == PROBE_MATCH) { *proto = p; diff --git a/sslh-conf.c b/sslh-conf.c index e280054..9f7da74 100644 --- a/sslh-conf.c +++ b/sslh-conf.c @@ -1,5 +1,5 @@ /* Generated by conf2struct (https://www.rutschle.net/tech/conf2struct/README) - * on Sun Sep 26 15:51:02 2021. + * on Sun Sep 26 16:54:06 2021. # conf2struct: generate libconf parsers that read to structs # Copyright (C) 2018-2021 Yves Rutschle @@ -450,6 +450,8 @@ struct arg_file* sslhcfg_conffile; 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_int* sslhcfg_verbose; @@ -792,7 +794,7 @@ static struct config_desc table_sslhcfg_listen[] = { }, { 0 } }; - + static struct config_desc table_sslhcfg[] = { @@ -908,6 +910,38 @@ static struct config_desc table_sslhcfg[] = { /* 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, @@ -1276,7 +1310,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: listen */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_listen, - .base_entry = & table_sslhcfg [22], + .base_entry = & table_sslhcfg [24], .targets = sslhcfg_listen_targets, @@ -1288,7 +1322,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: ssh */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_ssh, - .base_entry = & table_sslhcfg [23], + .base_entry = & table_sslhcfg [25], .targets = sslhcfg_ssh_targets, @@ -1300,7 +1334,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: tls */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_tls, - .base_entry = & table_sslhcfg [23], + .base_entry = & table_sslhcfg [25], .targets = sslhcfg_tls_targets, @@ -1312,7 +1346,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: openvpn */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_openvpn, - .base_entry = & table_sslhcfg [23], + .base_entry = & table_sslhcfg [25], .targets = sslhcfg_openvpn_targets, @@ -1324,7 +1358,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: tinc */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_tinc, - .base_entry = & table_sslhcfg [23], + .base_entry = & table_sslhcfg [25], .targets = sslhcfg_tinc_targets, @@ -1336,7 +1370,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: xmpp */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_xmpp, - .base_entry = & table_sslhcfg [23], + .base_entry = & table_sslhcfg [25], .targets = sslhcfg_xmpp_targets, @@ -1348,7 +1382,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: http */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_http, - .base_entry = & table_sslhcfg [23], + .base_entry = & table_sslhcfg [25], .targets = sslhcfg_http_targets, @@ -1360,7 +1394,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: adb */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_adb, - .base_entry = & table_sslhcfg [23], + .base_entry = & table_sslhcfg [25], .targets = sslhcfg_adb_targets, @@ -1372,7 +1406,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: socks5 */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_socks5, - .base_entry = & table_sslhcfg [23], + .base_entry = & table_sslhcfg [25], .targets = sslhcfg_socks5_targets, @@ -1384,7 +1418,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: syslog */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_syslog, - .base_entry = & table_sslhcfg [23], + .base_entry = & table_sslhcfg [25], .targets = sslhcfg_syslog_targets, @@ -1396,7 +1430,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: anyprot */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_anyprot, - .base_entry = & table_sslhcfg [23], + .base_entry = & table_sslhcfg [25], .targets = sslhcfg_anyprot_targets, @@ -2068,6 +2102,8 @@ int sslhcfg_cl_parse(int argc, char* argv[], struct sslhcfg_item* cfg) sslhcfg_verbose_connections_error = arg_intn(NULL, "verbose-connections-error", "", 0, 1, ""), sslhcfg_verbose_fd = arg_intn(NULL, "verbose-fd", "", 0, 1, ""), sslhcfg_verbose_packets = arg_intn(NULL, "verbose-packets", "", 0, 1, ""), + sslhcfg_verbose_probe_info = arg_intn(NULL, "verbose-probe-info", "", 0, 1, ""), + sslhcfg_verbose_probe_error = arg_intn(NULL, "verbose-probe-error", "", 0, 1, ""), sslhcfg_verbose_system_error = arg_intn(NULL, "verbose-system-error", "", 0, 1, ""), sslhcfg_verbose_int_error = arg_intn(NULL, "verbose-int-error", "", 0, 1, ""), sslhcfg_verbose = arg_intn("v", "verbose", "", 0, 1, ""), @@ -2260,6 +2296,12 @@ void sslhcfg_fprint( 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); diff --git a/sslh-conf.h b/sslh-conf.h index ad4a3e2..192ae76 100644 --- a/sslh-conf.h +++ b/sslh-conf.h @@ -1,5 +1,5 @@ /* Generated by conf2struct (https://www.rutschle.net/tech/conf2struct/README) - * on Sun Sep 26 15:51:02 2021. + * on Sun Sep 26 16:54:06 2021. # conf2struct: generate libconf parsers that read to structs # Copyright (C) 2018-2021 Yves Rutschle @@ -81,6 +81,8 @@ struct sslhcfg_item { int verbose_connections_error; int verbose_fd; int verbose_packets; + int verbose_probe_info; + int verbose_probe_error; int verbose_system_error; int verbose_int_error; int verbose; diff --git a/sslhconf.cfg b/sslhconf.cfg index 57fedc4..c03b8ea 100644 --- a/sslhconf.cfg +++ b/sslhconf.cfg @@ -33,6 +33,9 @@ config: { { name: "verbose-fd"; type: "int"; default: 0; }, { name: "verbose-packets"; type: "int"; default: 0; }, + { name: "verbose-probe-info"; type: "int"; default: 0; }, + { name: "verbose-probe-error"; type: "int"; default: 3; }, + { name: "verbose-system-error"; type: "int"; default: 3; }, { name: "verbose-int-error"; type: "int"; default: 3; }, diff --git a/tls.c b/tls.c index c8b3fae..dfb3353 100644 --- a/tls.c +++ b/tls.c @@ -33,6 +33,7 @@ #include /* fnmatch() */ #include "tls.h" #include "sslh-conf.h" +#include "log.h" #define TLS_HEADER_LEN 5 #define TLS_HANDSHAKE_CONTENT_TYPE 0x16 @@ -82,14 +83,14 @@ parse_tls_header(const struct TLSProtocol *tls_data, const char *data, size_t da tls_content_type = data[0]; if (tls_content_type != TLS_HANDSHAKE_CONTENT_TYPE) { - if (cfg.verbose) fprintf(stderr, "Request did not begin with TLS handshake.\n"); + print_message(msg_probe_error, "Request did not begin with TLS handshake.\n"); return TLS_EPROTOCOL; } tls_version_major = data[1]; tls_version_minor = data[2]; if (tls_version_major < 3) { - if (cfg.verbose) fprintf(stderr, "Received SSL %d.%d handshake which cannot be parsed.\n", + print_message(msg_probe_error, "Received SSL %d.%d handshake which cannot be parsed.\n", tls_version_major, tls_version_minor); return TLS_EVERSION; @@ -111,7 +112,7 @@ parse_tls_header(const struct TLSProtocol *tls_data, const char *data, size_t da return TLS_EPROTOCOL; } if (data[pos] != TLS_HANDSHAKE_TYPE_CLIENT_HELLO) { - if (cfg.verbose) fprintf(stderr, "Not a client hello\n"); + print_message(msg_probe_error, "Not a client hello\n"); return TLS_EPROTOCOL; } @@ -228,7 +229,7 @@ parse_server_name_extension(const struct TLSProtocol *tls_data, const char *data return TLS_ENOEXT; } default: - if (cfg.verbose) fprintf(stderr, "Unknown server name extension name type: %d\n", + print_message(msg_probe_error, "Unknown server name extension name type: %d\n", data[pos]); } pos += 3 + len; @@ -254,7 +255,7 @@ parse_alpn_extension(const struct TLSProtocol *tls_data, const char *data, size_ if (len > 0 && has_match(tls_data->alpn_protocol_list, tls_data->alpn_list_len, data + pos + 1, len)) { return len; } else if (len > 0) { - if (cfg.verbose) fprintf(stderr, "Unknown ALPN name: %.*s\n", (int)len, data + pos + 1); + print_message(msg_probe_error, "Unknown ALPN name: %.*s\n", (int)len, data + pos + 1); } pos += 1 + len; } @@ -276,7 +277,7 @@ has_match(const char** list, size_t list_len, const char* name, size_t name_len) for (i = 0; i < list_len; i++) { item = &list[i]; - if (cfg.verbose) fprintf(stderr, "matching [%.*s] with [%s]\n", (int)name_len, name, *item); + print_message(msg_probe_error, "matching [%.*s] with [%s]\n", (int)name_len, name, *item); if(!fnmatch(*item, name_nullterminated, 0)) { free(name_nullterminated); return 1; From 2e11001087846c1ca424e6dd1746fb225deca682 Mon Sep 17 00:00:00 2001 From: yrutschle Date: Mon, 27 Sep 2021 12:43:03 +0200 Subject: [PATCH 017/191] migrate UDP to new log system --- udp-listener.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/udp-listener.c b/udp-listener.c index ce1021f..986a44c 100644 --- a/udp-listener.c +++ b/udp-listener.c @@ -76,15 +76,15 @@ int udp_c2s_forward(int sockfd, cnx_collection* collection, int max_fd) target = known_source(collection, max_fd, &src_addr, addrlen); addrinfo.ai_addr = &src_addr; addrinfo.ai_addrlen = addrlen; - if (cfg.verbose) - fprintf(stderr, "received %ld UDP from %d:%s\n", len, target, sprintaddr(addr_str, sizeof(addr_str), &addrinfo)); + print_message(msg_probe_info, "received %ld UDP from %d:%s\n", + len, target, sprintaddr(addr_str, sizeof(addr_str), &addrinfo)); if (target == -1) { res = probe_buffer(data, len, &proto); /* First version: if we can't work out the protocol from the first * packet, drop it. Conceivably, we could store several packets to * run probes on packet sets */ - if (cfg.verbose) fprintf(stderr, "UDP probed: %d\n", res); + print_message(msg_probe_info, "UDP probed: %d\n", res); if (res != PROBE_MATCH) { return -1; } @@ -106,7 +106,7 @@ int udp_c2s_forward(int sockfd, cnx_collection* collection, int max_fd) res = sendto(cnx->target_sock, data, len, 0, cnx->proto->saddr->ai_addr, cnx->proto->saddr->ai_addrlen); cnx->last_active = time(NULL); - fprintf(stderr, "sending %d to %s\n", + print_message(msg_fd, "sending %d to %s\n", res, sprintaddr(data, sizeof(data), cnx->proto->saddr)); return out; } @@ -119,12 +119,10 @@ void udp_s2c_forward(struct connection* cnx) int res; res = recvfrom(sockfd, data, sizeof(data), 0, NULL, NULL); - fprintf(stderr, "recvfrom %d\n", res); CHECK_RES_DIE(res, "udp_listener/recvfrom"); res = sendto(cnx->local_endpoint, data, res, 0, &cnx->client_addr, cnx->addrlen); cnx->last_active = time(NULL); - fprintf(stderr, "sendto %d to\n", res); } From 4d3cc9c925dd92575b389178142a3d82fd4cbfdf Mon Sep 17 00:00:00 2001 From: yrutschle Date: Mon, 27 Sep 2021 12:46:51 +0200 Subject: [PATCH 018/191] migrate some more common.c to new log system --- common.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/common.c b/common.c index dece661..4d85a20 100644 --- a/common.c +++ b/common.c @@ -75,7 +75,7 @@ int get_fd_sockets(struct listen_endpoint *sockfd[]) #ifdef SYSTEMD sd = sd_listen_fds(0); if (sd < 0) { - fprintf(stderr, "sd_listen_fds(): %s\n", strerror(-sd)); + print_message(msg_system_error, "sd_listen_fds(): %s\n", strerror(-sd)); exit(1); } if (sd > 0) { @@ -453,8 +453,6 @@ int fd2fd(struct queue *target_q, struct queue *from_q) if (size_r == -1) { switch (errno) { case EAGAIN: - if (cfg.verbose) - fprintf(stderr, "reading 0 from %d\n", from); return FD_NODATA; case ECONNRESET: @@ -542,7 +540,7 @@ int resolve_split_name(struct addrinfo **out, char* host, char* serv) if (host[0] == '[') { end = strrchr(host, ']'); if (!end) { - fprintf(stderr, "%s: no closing bracket in IPv6 address?\n", host); + print_message(msg_config_error, "%s: no closing bracket in IPv6 address?\n", host); return -1; } host++; /* skip first bracket */ From 66caf8a31b3e1214a5b6e3c6fb3dadbd77d13f64 Mon Sep 17 00:00:00 2001 From: yrutschle Date: Mon, 27 Sep 2021 12:51:37 +0200 Subject: [PATCH 019/191] remove log_message --- common.c | 6 +++--- common.h | 2 +- log.c | 20 +------------------- 3 files changed, 5 insertions(+), 23 deletions(-) diff --git a/common.c b/common.c index 4d85a20..3c51397 100644 --- a/common.c +++ b/common.c @@ -503,7 +503,7 @@ char* sprintaddr(char* buf, size_t size, struct addrinfo *a) cfg.numeric ? NI_NUMERICHOST | NI_NUMERICSERV : 0 ); if (res) { - log_message(LOG_ERR, "sprintaddr:getnameinfo: %s\n", gai_strerror(res)); + print_message(msg_system_error, "sprintaddr:getnameinfo: %s\n", gai_strerror(res)); /* Name resolution failed: do it numerically instead */ res = getnameinfo(a->ai_addr, a->ai_addrlen, host, sizeof(host), @@ -511,7 +511,7 @@ char* sprintaddr(char* buf, size_t size, struct addrinfo *a) NI_NUMERICHOST | NI_NUMERICSERV); /* should not fail but... */ if (res) { - log_message(LOG_ERR, "sprintaddr:getnameinfo(NUM): %s\n", gai_strerror(res)); + print_message(msg_system_error, "sprintaddr:getnameinfo(NUM): %s\n", gai_strerror(res)); strcpy(host, "?"); strcpy(serv, "?"); } @@ -549,7 +549,7 @@ int resolve_split_name(struct addrinfo **out, char* host, char* serv) res = getaddrinfo(host, serv, &hint, out); if (res) - log_message(LOG_ERR, "%s `%s:%s'\n", gai_strerror(res), host, serv); + print_message(msg_system_error, "%s `%s:%s'\n", gai_strerror(res), host, serv); return res; } diff --git a/common.h b/common.h index 9235699..acb1bd3 100644 --- a/common.h +++ b/common.h @@ -47,7 +47,7 @@ #define CHECK_RES_RETURN(res, str, ret) \ if (res == -1) { \ - log_message(LOG_CRIT, "%s:%d:%s:%d:%s\n", __FILE__, __LINE__, str, errno, strerror(errno)); \ + print_message(msg_system_error, "%s:%d:%s:%d:%s\n", __FILE__, __LINE__, str, errno, strerror(errno)); \ return ret; \ } diff --git a/log.c b/log.c index e83465a..00a25a0 100644 --- a/log.c +++ b/log.c @@ -146,24 +146,6 @@ void setup_syslog(const char* bin_name) { } -/* Log to syslog or stderr if foreground */ -void log_message(int type, const char* msg, ...) -{ - va_list ap; - - va_start(ap, msg); - if (cfg.foreground) - vfprintf(stderr, msg, ap); - va_end(ap); - - if (do_syslog) { - va_start(ap, msg); - vsyslog(type, msg, ap); - va_end(ap); - } -} - - /* syslogs who connected to where * desc: string description of the connection. if NULL, log_connection will * manage on its own @@ -181,7 +163,7 @@ void log_connection(struct connection_desc* desc, const struct connection *cnx) get_connection_desc(desc, cnx); } - log_message(LOG_INFO, "%s:connection from %s to %s forwarded from %s to %s\n", + print_message(msg_connections, "%s:connection from %s to %s forwarded from %s to %s\n", cnx->proto->name, desc->peer, desc->service, From 70b31a48d92f9844f085126966370ffa6a8fd0f7 Mon Sep 17 00:00:00 2001 From: yrutschle Date: Mon, 27 Sep 2021 12:53:41 +0200 Subject: [PATCH 020/191] migrate generic system call failure checks to new log system --- common.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common.h b/common.h index acb1bd3..d3ebd59 100644 --- a/common.h +++ b/common.h @@ -40,7 +40,7 @@ #define CHECK_RES_DIE(res, str) \ if (res == -1) { \ - fprintf(stderr, "%s:%d:", __FILE__, __LINE__); \ + print_message(msg_system_error, "%s:%d:", __FILE__, __LINE__); \ perror(str); \ exit(1); \ } @@ -53,7 +53,7 @@ #define CHECK_ALLOC(a, str) \ if (!a) { \ - fprintf(stderr, "%s:%d:", __FILE__, __LINE__); \ + print_message(msg_system_error, "%s:%d:", __FILE__, __LINE__); \ perror(str); \ exit(1); \ } From 4f0f5017bcf1853f694d4c8983bb061157fb0399 Mon Sep 17 00:00:00 2001 From: yrutschle Date: Mon, 27 Sep 2021 12:55:57 +0200 Subject: [PATCH 021/191] remove obsolete prototype --- common.h | 1 - 1 file changed, 1 deletion(-) diff --git a/common.h b/common.h index d3ebd59..ee53dd1 100644 --- a/common.h +++ b/common.h @@ -160,7 +160,6 @@ void setup_syslog(const char* bin_name); void drop_privileges(const char* user_name, const char* chroot_path); void set_capabilities(int cap_net_admin); void write_pid_file(const char* pidfile); -void log_message(int type, const char* msg, ...); void dump_connection(struct connection *cnx); int resolve_split_name(struct addrinfo **out, char* hostname, char* port); From 16bf1a6acaea88f169f857e9a71285604fab07c3 Mon Sep 17 00:00:00 2001 From: yrutschle Date: Mon, 27 Sep 2021 13:01:20 +0200 Subject: [PATCH 022/191] make echosrv independant from common macros --- echosrv.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/echosrv.c b/echosrv.c index 4d22eb4..4f08e31 100644 --- a/echosrv.c +++ b/echosrv.c @@ -100,7 +100,10 @@ void tcp_echo(struct listen_endpoint* listen_socket) { while (1) { int in_socket = accept(listen_socket->socketfd, 0, 0); - CHECK_RES_DIE(in_socket, "accept"); + if (in_socket == -1) { + perror("tcp_echo:accept"); + exit(1); + } if (!fork()) { From 4277d270633d6b535213941621b30fb8d428225e Mon Sep 17 00:00:00 2001 From: yrutschle Date: Mon, 27 Sep 2021 13:16:30 +0200 Subject: [PATCH 023/191] migrate last messages to new log system --- common.c | 2 +- tls.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/common.c b/common.c index 3c51397..7daa969 100644 --- a/common.c +++ b/common.c @@ -58,7 +58,7 @@ void check_res_dump(CR_ACTION act, int res, struct addrinfo *addr, char* syscall char buf[NI_MAXHOST]; if (res == -1) { - fprintf(stderr, "%s:%s: %s\n", + print_message(msg_system_error, "%s:%s: %s\n", sprintaddr(buf, sizeof(buf), addr), syscall, strerror(errno)); diff --git a/tls.c b/tls.c index dfb3353..9d56edb 100644 --- a/tls.c +++ b/tls.c @@ -145,7 +145,7 @@ parse_tls_header(const struct TLSProtocol *tls_data, const char *data, size_t da pos += 1 + len; if (pos == data_len && tls_version_major == 3 && tls_version_minor == 0) { - if (cfg.verbose) fprintf(stderr, "Received SSL 3.0 handshake without extensions\n"); + print_message(msg_probe_error, "Received SSL 3.0 handshake without extensions\n"); return TLS_EVERSION; } From c8fce0a02f61e46a0d277be7eeae05e2de2ee159 Mon Sep 17 00:00:00 2001 From: yrutschle Date: Mon, 27 Sep 2021 13:21:16 +0200 Subject: [PATCH 024/191] make sure no error will go to stderr if in inetd (fix #303) --- sslh-main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sslh-main.c b/sslh-main.c index 99d6297..c1076f6 100644 --- a/sslh-main.c +++ b/sslh-main.c @@ -197,6 +197,7 @@ int main(int argc, char *argv[], char* envp[]) if (cfg.inetd) { cfg.verbose = 0; + close(fileno(stderr)); /* Make sure no error will go to client */ start_shoveler(0); exit(0); } From caa62875c180ce55a11c696e670b23390b1ca82c Mon Sep 17 00:00:00 2001 From: yrutschle Date: Mon, 27 Sep 2021 13:28:21 +0200 Subject: [PATCH 025/191] remove --verbose option --- ChangeLog | 11 +++++++++++ example.cfg | 16 +++++++++++++--- probe.c | 6 ++---- sslh-conf.c | 47 +++++++++++++---------------------------------- sslh-conf.h | 3 +-- sslh-main.c | 1 - sslhconf.cfg | 1 - test.cfg | 20 ++++++++++---------- 8 files changed, 50 insertions(+), 55 deletions(-) diff --git a/ChangeLog b/ChangeLog index 5e4ad26..e75849a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,14 @@ +vNEXT: + New log system: instead of --verbose with arbitrary + levels, there are now several message classes. Each + message class can be set to go to stderr, syslog, or + both. Classes are documented in example.cfg. + + inetd merges stderr output to what is sent to the + client, which is a security issue as it might give + information to an attacker. When inetd is activated, + stderr is forcibly closed. + v1.22: 17AUG2021 sslh-select now supports UDP protocols. Probes specified in the `protocols` diff --git a/example.cfg b/example.cfg index 974a3f9..2542420 100644 --- a/example.cfg +++ b/example.cfg @@ -12,11 +12,21 @@ user: "nobody"; pidfile: "/var/run/sslh.pid"; chroot: "/var/empty"; -verbose: 0; - # Logging configuration # Value: 1: stdout; 2: syslog; 3: both -verbose-config: 0; # config: print configuration at startup +# Defaults are indicated here, and should be sensible. Generally, you want *-error +# to be always enabled, to know if something is going wrong. +verbose-config: 0; # print configuration at startup +verbose-config-error: 3; # print configuration errors +verbose-connections: 3; # trace established incoming address to forward address +verbose-connections-error: 3; # connection errors +verbose-connections-try: 0; # connection attempts towards targets +verbose-fd: 0; # file descriptor activity, open/close/whatnot +verbose-packets: 0; # hexdump packets on which probing is done +verbose-probe-info: 0; # what's happening during the probe process +verbose-probe-error: 3; # failures and problems during probing +verbose-system-error: 3; # system call problem, i.e. malloc, fork, failing +verbose-int-error: 3; # internal errors, the kind that should never happen # Specify which syslog facility to use (names for your diff --git a/probe.c b/probe.c index a1171b7..ee9f423 100644 --- a/probe.c +++ b/probe.c @@ -350,10 +350,8 @@ int probe_buffer(char* buf, int len, struct sslhcfg_protocols_item** proto) struct sslhcfg_protocols_item* p; int i, res, again = 0; - if (cfg.verbose > 1) { - print_message(msg_packets, "hexdump of incoming packet:\n"); - hexdump(msg_packets, buf, len); - } + print_message(msg_packets, "hexdump of incoming packet:\n"); + hexdump(msg_packets, buf, len); *proto = NULL; for (i = 0; i < cfg.protocols_len; i++) { diff --git a/sslh-conf.c b/sslh-conf.c index 9f7da74..0afdfb6 100644 --- a/sslh-conf.c +++ b/sslh-conf.c @@ -1,5 +1,5 @@ /* Generated by conf2struct (https://www.rutschle.net/tech/conf2struct/README) - * on Sun Sep 26 16:54:06 2021. + * on Mon Sep 27 13:21:48 2021. # conf2struct: generate libconf parsers that read to structs # Copyright (C) 2018-2021 Yves Rutschle @@ -454,7 +454,6 @@ struct arg_file* sslhcfg_conffile; struct arg_int* sslhcfg_verbose_probe_error; struct arg_int* sslhcfg_verbose_system_error; struct arg_int* sslhcfg_verbose_int_error; - struct arg_int* sslhcfg_verbose; struct arg_lit* sslhcfg_version; struct arg_lit* sslhcfg_foreground; struct arg_lit* sslhcfg_inetd; @@ -794,7 +793,7 @@ static struct config_desc table_sslhcfg_listen[] = { }, { 0 } }; - + static struct config_desc table_sslhcfg[] = { @@ -974,22 +973,6 @@ static struct config_desc table_sslhcfg[] = { /* default_val*/ .default_val.def_int = 3 }, - { - /* 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 */ 1, - /* optional */ 0, - /* default_val*/ .default_val.def_int = 0 - }, - { /* name */ "version", /* type */ CFG_BOOL, @@ -1310,7 +1293,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: listen */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_listen, - .base_entry = & table_sslhcfg [24], + .base_entry = & table_sslhcfg [23], .targets = sslhcfg_listen_targets, @@ -1322,7 +1305,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: ssh */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_ssh, - .base_entry = & table_sslhcfg [25], + .base_entry = & table_sslhcfg [24], .targets = sslhcfg_ssh_targets, @@ -1334,7 +1317,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: tls */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_tls, - .base_entry = & table_sslhcfg [25], + .base_entry = & table_sslhcfg [24], .targets = sslhcfg_tls_targets, @@ -1346,7 +1329,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: openvpn */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_openvpn, - .base_entry = & table_sslhcfg [25], + .base_entry = & table_sslhcfg [24], .targets = sslhcfg_openvpn_targets, @@ -1358,7 +1341,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: tinc */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_tinc, - .base_entry = & table_sslhcfg [25], + .base_entry = & table_sslhcfg [24], .targets = sslhcfg_tinc_targets, @@ -1370,7 +1353,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: xmpp */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_xmpp, - .base_entry = & table_sslhcfg [25], + .base_entry = & table_sslhcfg [24], .targets = sslhcfg_xmpp_targets, @@ -1382,7 +1365,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: http */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_http, - .base_entry = & table_sslhcfg [25], + .base_entry = & table_sslhcfg [24], .targets = sslhcfg_http_targets, @@ -1394,7 +1377,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: adb */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_adb, - .base_entry = & table_sslhcfg [25], + .base_entry = & table_sslhcfg [24], .targets = sslhcfg_adb_targets, @@ -1406,7 +1389,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: socks5 */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_socks5, - .base_entry = & table_sslhcfg [25], + .base_entry = & table_sslhcfg [24], .targets = sslhcfg_socks5_targets, @@ -1418,7 +1401,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: syslog */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_syslog, - .base_entry = & table_sslhcfg [25], + .base_entry = & table_sslhcfg [24], .targets = sslhcfg_syslog_targets, @@ -1430,7 +1413,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: anyprot */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_anyprot, - .base_entry = & table_sslhcfg [25], + .base_entry = & table_sslhcfg [24], .targets = sslhcfg_anyprot_targets, @@ -2106,7 +2089,6 @@ int sslhcfg_cl_parse(int argc, char* argv[], struct sslhcfg_item* cfg) sslhcfg_verbose_probe_error = arg_intn(NULL, "verbose-probe-error", "", 0, 1, ""), sslhcfg_verbose_system_error = arg_intn(NULL, "verbose-system-error", "", 0, 1, ""), sslhcfg_verbose_int_error = arg_intn(NULL, "verbose-int-error", "", 0, 1, ""), - sslhcfg_verbose = arg_intn("v", "verbose", "", 0, 1, ""), 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"), @@ -2308,9 +2290,6 @@ void sslhcfg_fprint( fprintf(out, "verbose_int_error: %d", sslhcfg->verbose_int_error); fprintf(out, "\n"); indent(out, depth); - fprintf(out, "verbose: %d", sslhcfg->verbose); - fprintf(out, "\n"); - indent(out, depth); fprintf(out, "version: %d", sslhcfg->version); fprintf(out, "\n"); indent(out, depth); diff --git a/sslh-conf.h b/sslh-conf.h index 192ae76..e50197d 100644 --- a/sslh-conf.h +++ b/sslh-conf.h @@ -1,5 +1,5 @@ /* Generated by conf2struct (https://www.rutschle.net/tech/conf2struct/README) - * on Sun Sep 26 16:54:06 2021. + * on Mon Sep 27 13:21:48 2021. # conf2struct: generate libconf parsers that read to structs # Copyright (C) 2018-2021 Yves Rutschle @@ -85,7 +85,6 @@ struct sslhcfg_item { int verbose_probe_error; int verbose_system_error; int verbose_int_error; - int verbose; int version; int foreground; int inetd; diff --git a/sslh-main.c b/sslh-main.c index c1076f6..9f326a5 100644 --- a/sslh-main.c +++ b/sslh-main.c @@ -196,7 +196,6 @@ int main(int argc, char *argv[], char* envp[]) if (cfg.inetd) { - cfg.verbose = 0; close(fileno(stderr)); /* Make sure no error will go to client */ start_shoveler(0); exit(0); diff --git a/sslhconf.cfg b/sslhconf.cfg index c03b8ea..d84c081 100644 --- a/sslhconf.cfg +++ b/sslhconf.cfg @@ -39,7 +39,6 @@ config: { { name: "verbose-system-error"; type: "int"; default: 3; }, { name: "verbose-int-error"; type: "int"; default: 3; }, - { name: "verbose"; type: "int"; short: "v" }, # to delete { name: "version"; type: "bool"; default: false; short: "V"; description: "Print version information and exit"; }, diff --git a/test.cfg b/test.cfg index 2b95c65..4330cfa 100644 --- a/test.cfg +++ b/test.cfg @@ -1,7 +1,6 @@ # Configuration file for testing (use both by sslh under # test and the test script `t`) -verbose: 4; foreground: true; inetd: false; numeric: true; @@ -15,16 +14,17 @@ syslog_facility: "auth"; # Value: 1: stdout; 2: syslog; 3: both # Defaults should be sensible. Generally, you want *-error # to be always enabled, to know if something is going wrong. -#verbose-config: 3; # config: print configuration at startup -#verbose-config-error: 3; #config-error: print configuration errors -#verbose-connections: 3; #config-connections: track connections -#verbose-fd: 3; # file descriptor activity, open/close/whatnot +verbose-config: 3; # print configuration at startup +verbose-config-error: 3; # print configuration errors +verbose-connections: 3; # trace established incoming address to forward address +verbose-connections-error: 3; # connection errors +verbose-connections-try: 3; # connection attempts towards targets +verbose-fd: 3; # file descriptor activity, open/close/whatnot verbose-packets: 3; # hexdump packets on which probing is done -#verbose-system-error: 3; # system call problem, i.e. malloc, fork, failing -#verbose-int-error: 3; # internal errors, the kind that should never happen -#verbose-connections-try: 3; # connection attempts towards targets -#verbose-connections: 3; # trace established incoming address to forward address -# verbose-connections-error: 3; # connection errors +verbose-probe-info: 3; # what's happening during the probe process +verbose-probe-error: 3; # failures and problems during probing +verbose-system-error: 3; # system call problem, i.e. malloc, fork, failing +verbose-int-error: 3; # internal errors, the kind that should never happen # List of interfaces on which we should listen # Options: From 9955cc65604f34fbb503b05355a998b757d205c1 Mon Sep 17 00:00:00 2001 From: yrutschle Date: Sat, 2 Oct 2021 15:38:22 +0200 Subject: [PATCH 026/191] describe verbose options --- sslh-conf.c | 24 ++++++++++++------------ sslh-conf.h | 2 +- sslhconf.cfg | 33 ++++++++++++++++++++++----------- 3 files changed, 35 insertions(+), 24 deletions(-) diff --git a/sslh-conf.c b/sslh-conf.c index 0afdfb6..3dd5345 100644 --- a/sslh-conf.c +++ b/sslh-conf.c @@ -1,5 +1,5 @@ /* Generated by conf2struct (https://www.rutschle.net/tech/conf2struct/README) - * on Mon Sep 27 13:21:48 2021. + * on Sat Oct 2 09:01:25 2021. # conf2struct: generate libconf parsers that read to structs # Copyright (C) 2018-2021 Yves Rutschle @@ -2078,17 +2078,17 @@ int sslhcfg_cl_parse(int argc, char* argv[], struct sslhcfg_item* cfg) #ifdef LIBCONFIG sslhcfg_conffile = arg_filen("F", "config", "", 0, 1, "Specify configuration file"), #endif - sslhcfg_verbose_config = arg_intn(NULL, "verbose-config", "", 0, 1, ""), - sslhcfg_verbose_config_error = arg_intn(NULL, "verbose-config-error", "", 0, 1, ""), - sslhcfg_verbose_connections = arg_intn(NULL, "verbose-connections", "", 0, 1, ""), - sslhcfg_verbose_connections_try = arg_intn(NULL, "verbose-connections-try", "", 0, 1, ""), - sslhcfg_verbose_connections_error = arg_intn(NULL, "verbose-connections-error", "", 0, 1, ""), - sslhcfg_verbose_fd = arg_intn(NULL, "verbose-fd", "", 0, 1, ""), - sslhcfg_verbose_packets = arg_intn(NULL, "verbose-packets", "", 0, 1, ""), - sslhcfg_verbose_probe_info = arg_intn(NULL, "verbose-probe-info", "", 0, 1, ""), - sslhcfg_verbose_probe_error = arg_intn(NULL, "verbose-probe-error", "", 0, 1, ""), - sslhcfg_verbose_system_error = arg_intn(NULL, "verbose-system-error", "", 0, 1, ""), - sslhcfg_verbose_int_error = arg_intn(NULL, "verbose-int-error", "", 0, 1, ""), + 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"), diff --git a/sslh-conf.h b/sslh-conf.h index e50197d..e284232 100644 --- a/sslh-conf.h +++ b/sslh-conf.h @@ -1,5 +1,5 @@ /* Generated by conf2struct (https://www.rutschle.net/tech/conf2struct/README) - * on Mon Sep 27 13:21:48 2021. + * on Sat Oct 2 09:01:25 2021. # conf2struct: generate libconf parsers that read to structs # Copyright (C) 2018-2021 Yves Rutschle diff --git a/sslhconf.cfg b/sslhconf.cfg index d84c081..a0ad4ec 100644 --- a/sslhconf.cfg +++ b/sslhconf.cfg @@ -25,19 +25,30 @@ config: { name : "sslhcfg", type: "list", items: ( - { name: "verbose-config"; type: "int"; default: 0; }, - { name: "verbose-config-error"; type: "int"; default: 3; }, - { name: "verbose-connections"; type: "int"; default: 3; }, - { name: "verbose-connections-try"; type: "int"; default: 0; }, - { name: "verbose-connections-error"; type: "int"; default: 3; }, - { name: "verbose-fd"; type: "int"; default: 0; }, - { name: "verbose-packets"; type: "int"; default: 0; }, + { name: "verbose-config"; type: "int"; default: 0; + description: "Print configuration at startup" }, + { name: "verbose-config-error"; type: "int"; default: 3; + description: "Print configuration errors" }, + { name: "verbose-connections"; type: "int"; default: 3; + description: "Trace established incoming address to forward address" }, + { name: "verbose-connections-try"; type: "int"; default: 0; + description: "Connection errors" }, + { name: "verbose-connections-error"; type: "int"; default: 3; + description: "Connection attempts towards targets" }, + { name: "verbose-fd"; type: "int"; default: 0; + description: "File descriptor activity, open/close/whatnot" }, + { name: "verbose-packets"; type: "int"; default: 0; + description: "Hexdump packets on which probing is done" }, - { name: "verbose-probe-info"; type: "int"; default: 0; }, - { name: "verbose-probe-error"; type: "int"; default: 3; }, + { name: "verbose-probe-info"; type: "int"; default: 0; + description: "Trace the probe process" }, + { name: "verbose-probe-error"; type: "int"; default: 3; + description: "Failures and problems during probing" }, - { name: "verbose-system-error"; type: "int"; default: 3; }, - { name: "verbose-int-error"; type: "int"; default: 3; }, + { name: "verbose-system-error"; type: "int"; default: 3; + description: "System call failures" }, + { name: "verbose-int-error"; type: "int"; default: 3; + description: "Internal errors that should never happen" }, { name: "version"; type: "bool"; default: false; short: "V"; From 7b0d486d3db6219b642ec4a080205d191091e791 Mon Sep 17 00:00:00 2001 From: yrutschle Date: Sat, 2 Oct 2021 21:23:17 +0200 Subject: [PATCH 027/191] removed obsolete check to verbose --- sslh-fork.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sslh-fork.c b/sslh-fork.c index 4ad699e..a08c2b6 100644 --- a/sslh-fork.c +++ b/sslh-fork.c @@ -99,8 +99,7 @@ void start_shoveler(int in_socket) } else { /* Timed out: it's necessarily SSH */ cnx.proto = timeout_protocol(); - if (cfg.verbose) - print_message(msg_fd, "timed out, connect to %s\n", cnx.proto->name); + print_message(msg_fd, "timed out, connect to %s\n", cnx.proto->name); break; } } From ed48d3964f4a0d2d7b2a02c3c06e4b79f1a9f878 Mon Sep 17 00:00:00 2001 From: yrutschle Date: Sat, 2 Oct 2021 21:23:39 +0200 Subject: [PATCH 028/191] removed obsolete prototye --- log.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/log.h b/log.h index 622885a..4a345c6 100644 --- a/log.h +++ b/log.h @@ -3,8 +3,6 @@ void setup_syslog(const char* bin_name); -void log_message(int type, const char* msg, ...); - void log_connection(struct connection_desc* desc, const struct connection *cnx); typedef struct s_msg_info{ From 0cde3d794a300f3a9fb2ea417d4c7f0e44bd4213 Mon Sep 17 00:00:00 2001 From: yrutschle Date: Sat, 2 Oct 2021 21:27:31 +0200 Subject: [PATCH 029/191] check return values (fix #61) --- common.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/common.c b/common.c index 7daa969..235b872 100644 --- a/common.c +++ b/common.c @@ -821,14 +821,24 @@ void drop_privileges(const char* user_name, const char* chroot_path) void write_pid_file(const char* pidfile) { FILE *f; + int res; f = fopen(pidfile, "w"); if (!f) { - perror(pidfile); + print_message(msg_system_error, "write_pid_file:%s:%s", pidfile, strerror(errno)); exit(3); } - fprintf(f, "%d\n", getpid()); - fclose(f); + res = fprintf(f, "%d\n", getpid()); + if (res < 0) { + print_message(msg_system_error, "write_pid_file:fprintf:%s", strerror(errno)); + exit(3); + } + + res = fclose(f); + if (res == EOF) { + print_message(msg_system_error, "write_pid_file:fclose:%s", strerror(errno)); + exit(3); + } } From c9eff6e38d3a6feb7f3e27ffb6081ef739a9a8dc Mon Sep 17 00:00:00 2001 From: yrutschle Date: Sun, 3 Oct 2021 17:25:31 +0200 Subject: [PATCH 030/191] removed obsolete declarations --- common.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/common.h b/common.h index ee53dd1..ed7b018 100644 --- a/common.h +++ b/common.h @@ -83,9 +83,6 @@ enum connection_state { ST_SHOVELING /* Connexion is established */ }; -/* this is used to pass protocols through the command-line parameter parsing */ -#define PROT_SHIFT 1000 /* protocol options will be 1000, 1001, etc */ - /* A 'queue' is composed of a file descriptor (which can be read from or * written to), and a queue for deferred write data */ struct queue { @@ -152,11 +149,9 @@ int fd2fd(struct queue *target, struct queue *from); char* sprintaddr(char* buf, size_t size, struct addrinfo *a); void resolve_name(struct addrinfo **out, char* fullname); int get_connection_desc(struct connection_desc* desc, const struct connection *cnx); -void log_connection(struct connection_desc* desc, const struct connection *cnx); void set_proctitle_shovel(struct connection_desc* desc, const struct connection *cnx); int check_access_rights(int in_socket, const char* service); void setup_signals(void); -void setup_syslog(const char* bin_name); void drop_privileges(const char* user_name, const char* chroot_path); void set_capabilities(int cap_net_admin); void write_pid_file(const char* pidfile); From b0aeeff46564967416613f30777162509fdae7b8 Mon Sep 17 00:00:00 2001 From: yrutschle Date: Mon, 4 Oct 2021 09:10:33 +0200 Subject: [PATCH 031/191] Include log header before defining macros that depend on log levels. (fix #308) --- common.h | 50 ++++++++++++++++++++++++++------------------------ 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/common.h b/common.h index ed7b018..424236f 100644 --- a/common.h +++ b/common.h @@ -35,30 +35,6 @@ #include "version.h" -#define MAX(a, b) (((a) > (b)) ? (a) : (b)) - - -#define CHECK_RES_DIE(res, str) \ - if (res == -1) { \ - print_message(msg_system_error, "%s:%d:", __FILE__, __LINE__); \ - perror(str); \ - exit(1); \ - } - -#define CHECK_RES_RETURN(res, str, ret) \ - if (res == -1) { \ - print_message(msg_system_error, "%s:%d:%s:%d:%s\n", __FILE__, __LINE__, str, errno, strerror(errno)); \ - return ret; \ - } - -#define CHECK_ALLOC(a, str) \ - if (!a) { \ - print_message(msg_system_error, "%s:%d:", __FILE__, __LINE__); \ - perror(str); \ - exit(1); \ - } - -#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) #if 1 #define TRACE fprintf(stderr, "%s:%d\n", __FILE__, __LINE__); @@ -140,6 +116,7 @@ typedef enum { BLOCKING = 1 } connect_blocking; +#include "log.h" /* common.c */ void init_cnx(struct connection *cnx); @@ -172,4 +149,29 @@ void start_shoveler(int); void main_loop(struct listen_endpoint *listen_sockets, int num_addr_listen); +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) + + +#define CHECK_RES_DIE(res, str) \ + if (res == -1) { \ + print_message(msg_system_error, "%s:%d:", __FILE__, __LINE__); \ + perror(str); \ + exit(1); \ + } + +#define CHECK_RES_RETURN(res, str, ret) \ + if (res == -1) { \ + print_message(msg_system_error, "%s:%d:%s:%d:%s\n", __FILE__, __LINE__, str, errno, strerror(errno)); \ + return ret; \ + } + +#define CHECK_ALLOC(a, str) \ + if (!a) { \ + print_message(msg_system_error, "%s:%d:", __FILE__, __LINE__); \ + perror(str); \ + exit(1); \ + } + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) + #endif From 2cdd60dd18d3c61089eab075efc146c1a0a1acf5 Mon Sep 17 00:00:00 2001 From: yrutschle Date: Mon, 4 Oct 2021 21:34:22 +0200 Subject: [PATCH 032/191] make systemd-sslh-generator work again (fix #308 again) --- systemd-sslh-generator.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/systemd-sslh-generator.c b/systemd-sslh-generator.c index 315e207..6d6a44f 100644 --- a/systemd-sslh-generator.c +++ b/systemd-sslh-generator.c @@ -2,7 +2,13 @@ #include #include #include -#include "common.h" + +#define CHECK_ALLOC(a, str) \ + if (!a) { \ + fprintf(stderr, "%s:%d:", __FILE__, __LINE__); \ + perror(str); \ + exit(1); \ + } static char* resolve_listen(const char *hostname, const char *port) { From 25abd765cb7ccc90933f62113f84f5f154527e43 Mon Sep 17 00:00:00 2001 From: yrutschle Date: Mon, 11 Oct 2021 22:40:46 +0200 Subject: [PATCH 033/191] refactor: abstract watchers from loop data --- processes.h | 22 ++++++++ sslh-select.c | 139 ++++++++++++++++++++++++++++---------------------- 2 files changed, 101 insertions(+), 60 deletions(-) create mode 100644 processes.h diff --git a/processes.h b/processes.h new file mode 100644 index 0000000..52e07e2 --- /dev/null +++ b/processes.h @@ -0,0 +1,22 @@ +#ifndef PROCESSES_H +#define PROCESSES_H + +#ifndef WATCHERS_TYPE_DEFINED +#error Define watchers type before including processes.h +#endif + +/* Global state for a loop */ +struct loop_info { + int num_probing; /* Number of connections currently probing + * We use this to know if we need to time out of + * select() */ + gap_array* probing_list; /* Pointers to cnx that are in probing mode */ + + watchers watchers; + + cnx_collection* collection; /* Collection of connections linked to this loop */ + + time_t next_timeout; /* time at which next UDP connection times out */ +}; + +#endif diff --git a/sslh-select.c b/sslh-select.c index a5cc45b..f2258ee 100644 --- a/sslh-select.c +++ b/sslh-select.c @@ -41,35 +41,59 @@ const char* server_type = "sslh-select"; -/* Global state for a select() loop */ -struct select_info { - int max_fd; /* Highest fd number to pass to select() */ - - int num_probing; /* Number of connections currently probing - * We use this to know if we need to time out of - * select() */ - gap_array* probing_list; /* Pointers to cnx that are in probing mode */ - +/* watcher type for a select() loop */ +typedef struct watchers { fd_set fds_r, fds_w; /* reference fd sets (used to init working copies) */ - cnx_collection* collection; /* Collection of connections linked to this loop */ + int max_fd; /* Highest fd number to pass to select() */ +} watchers; +#define WATCHERS_TYPE_DEFINED /* To notify processes.h */ - time_t next_timeout; /* time at which next UDP connection times out */ -}; +#include "processes.h" + +void watchers_init(watchers* w) +{ + FD_ZERO(&w->fds_r); + FD_ZERO(&w->fds_w); +} + +void watchers_add_read(watchers* w, int fd) +{ + FD_SET(fd, &w->fds_r); + if (fd > w->max_fd) + w->max_fd = fd + 1; +} + +void watchers_del_read(watchers* w, int fd) +{ + FD_CLR(fd, &w->fds_r); +} + +void watchers_add_write(watchers* w, int fd) +{ + FD_SET(fd, &w->fds_w); + if (fd > w->max_fd) + w->max_fd = fd + 1; +} + +void watchers_del_write(watchers* w, int fd) +{ + FD_CLR(fd, &w->fds_w); +} +/* /end watchers */ -static int tidy_connection(struct connection *cnx, struct select_info* fd_info) + +static int tidy_connection(struct connection *cnx, struct loop_info* fd_info) { int i; - fd_set* fds = &fd_info->fds_r; - fd_set* fds2 = &fd_info->fds_w; for (i = 0; i < 2; i++) { if (cnx->q[i].fd != -1) { print_message(msg_fd, "closing fd %d\n", cnx->q[i].fd); - FD_CLR(cnx->q[i].fd, fds); - FD_CLR(cnx->q[i].fd, fds2); + watchers_del_read(&fd_info->watchers, cnx->q[i].fd); + watchers_del_write(&fd_info->watchers, cnx->q[i].fd); close(cnx->q[i].fd); if (cnx->q[i].deferred_data) free(cnx->q[i].deferred_data); @@ -125,7 +149,7 @@ static struct connection* accept_new_connection(int listen_socket, struct cnx_co /* Connect queue 1 of connection to SSL; returns new file descriptor */ static int connect_queue(struct connection* cnx, - struct select_info* fd_info) + struct loop_info* fd_info) { struct queue *q = &cnx->q[1]; @@ -134,10 +158,10 @@ static int connect_queue(struct connection* cnx, log_connection(NULL, cnx); flush_deferred(q); if (q->deferred_data) { - FD_SET(q->fd, &fd_info->fds_w); - FD_CLR(cnx->q[0].fd, &fd_info->fds_r); + FD_SET(q->fd, &fd_info->watchers.fds_w); + FD_CLR(cnx->q[0].fd, &fd_info->watchers.fds_r); } - FD_SET(q->fd, &fd_info->fds_r); + FD_SET(q->fd, &fd_info->watchers.fds_r); collection_add_fd(fd_info->collection, cnx, q->fd); return q->fd; } else { @@ -149,7 +173,7 @@ static int connect_queue(struct connection* cnx, /* shovels data from active fd to the other returns after one socket closed or operation would block */ -static void shovel(struct connection *cnx, int active_fd, struct select_info* fd_info) +static void shovel(struct connection *cnx, int active_fd, struct loop_info* fd_info) { struct queue *read_q, *write_q; @@ -165,8 +189,8 @@ static void shovel(struct connection *cnx, int active_fd, struct select_info* fd break; case FD_STALLED: - FD_SET(write_q->fd, &fd_info->fds_w); - FD_CLR(read_q->fd, &fd_info->fds_r); + watchers_add_write(&fd_info->watchers, write_q->fd); + watchers_del_read(&fd_info->watchers, read_q->fd); break; default: /* Nothing */ @@ -259,13 +283,13 @@ static void connect_proxy(struct connection *cnx) } /* Removes cnx from probing list */ -static void remove_probing_cnx(struct select_info* fd_info, struct connection* cnx) +static void remove_probing_cnx(struct loop_info* fd_info, struct connection* cnx) { gap_remove_ptr(fd_info->probing_list, cnx, fd_info->num_probing); fd_info->num_probing--; } -static void add_probing_cnx(struct select_info* fd_info, struct connection* cnx) +static void add_probing_cnx(struct loop_info* fd_info, struct connection* cnx) { gap_set(fd_info->probing_list, fd_info->num_probing, cnx); fd_info->num_probing++; @@ -278,7 +302,7 @@ static void add_probing_cnx(struct select_info* fd_info, struct connection* cnx) * */ static void probing_read_process(struct connection* cnx, - struct select_info* fd_info) + struct loop_info* fd_info) { int res; @@ -318,9 +342,6 @@ static void probing_read_process(struct connection* cnx, } else { res = connect_queue(cnx, fd_info); } - - if (res >= fd_info->max_fd) - fd_info->max_fd = res + 1;; } @@ -335,7 +356,7 @@ int active_queue(struct connection* cnx, int fd) } /* Process a connection that is active in read */ -static void tcp_read_process(struct select_info* fd_info, +static void tcp_read_process(struct loop_info* fd_info, int fd) { cnx_collection* collection = fd_info->collection; @@ -368,7 +389,7 @@ static void tcp_read_process(struct select_info* fd_info, } } -static void cnx_read_process(struct select_info* fd_info, int fd) +static void cnx_read_process(struct loop_info* fd_info, int fd) { cnx_collection* collection = fd_info->collection; struct connection* cnx = collection_get_cnx_from_fd(collection, fd); @@ -389,7 +410,7 @@ static void cnx_read_process(struct select_info* fd_info, int fd) } /* Process a connection that is active in write */ -static void cnx_write_process(struct select_info* fd_info, int fd) +void cnx_write_process(struct loop_info* fd_info, int fd) { struct connection* cnx = collection_get_cnx_from_fd(fd_info->collection, fd); int res; @@ -403,21 +424,22 @@ static void cnx_write_process(struct select_info* fd_info, int fd) /* If no deferred data is left, stop monitoring the fd * for write, and restart monitoring the other one for reads*/ if (!cnx->q[queue].deferred_data_size) { - FD_CLR(cnx->q[queue].fd, &fd_info->fds_w); - FD_SET(cnx->q[1-queue].fd, &fd_info->fds_r); + watchers_del_write(&fd_info->watchers, cnx->q[queue].fd); + watchers_add_read(&fd_info->watchers, cnx->q[1-queue].fd); } } } /* Process a connection that accepts a socket * (For UDP, this means all traffic coming from remote clients) + * Returns new file descriptor, or -1 * */ -void cnx_accept_process(struct select_info* fd_info, struct listen_endpoint* listen_socket) +void cnx_accept_process(struct loop_info* fd_info, struct listen_endpoint* listen_socket) { int fd = listen_socket->socketfd; int type = listen_socket->type; struct connection* cnx; - int new_fd; + int new_fd = -1; switch (type) { case SOCK_STREAM: @@ -430,7 +452,7 @@ void cnx_accept_process(struct select_info* fd_info, struct listen_endpoint* lis break; case SOCK_DGRAM: - new_fd = udp_c2s_forward(fd, fd_info->collection, fd_info->max_fd); + new_fd = udp_c2s_forward(fd, fd_info->collection, fd_info->watchers.max_fd); print_message(msg_fd, "new_fd %d\n", new_fd); if (new_fd == -1) return; @@ -442,18 +464,16 @@ void cnx_accept_process(struct select_info* fd_info, struct listen_endpoint* lis return; } - FD_SET(new_fd, &fd_info->fds_r); - if (new_fd >= fd_info->max_fd) - fd_info->max_fd = new_fd + 1; - + watchers_add_read(&fd_info->watchers, new_fd); } + /* Check all connections to see if a UDP connections has timed out, then free * it. At the same time, keep track of the closest, next timeout. Only do the * search through connections if that timeout actually happened. If the * connection that would have timed out has had activity, it doesn't matter: we * go through connections to find the next timeout, which was needed anyway. */ -static void udp_timeouts(struct select_info* fd_info) +static void udp_timeouts(struct loop_info* fd_info) { time_t now = time(NULL); @@ -461,10 +481,10 @@ static void udp_timeouts(struct select_info* fd_info) time_t next_timeout = INT_MAX; - for (int i = 0; i < fd_info->max_fd; i++) { + for (int i = 0; i < fd_info->watchers.max_fd; i++) { /* if it's either in read or write set, there is a connection * behind that file descriptor */ - if (FD_ISSET(i, &fd_info->fds_r) || FD_ISSET(i, &fd_info->fds_w)) { + if (FD_ISSET(i, &fd_info->watchers.fds_r) || FD_ISSET(i, &fd_info->watchers.fds_w)) { struct connection* cnx = collection_get_cnx_from_fd(fd_info->collection, i); if (cnx) { time_t timeout = udp_timeout(cnx); @@ -472,8 +492,8 @@ static void udp_timeouts(struct select_info* fd_info) if (cnx && (timeout <= now)) { print_message(msg_fd, "timed out UDP %d\n", cnx->target_sock); close(cnx->target_sock); - FD_CLR(i, &fd_info->fds_r); - FD_CLR(i, &fd_info->fds_w); + watchers_del_read(&fd_info->watchers, i); + watchers_del_write(&fd_info->watchers, i); collection_remove_cnx(fd_info->collection, cnx); } else { if (timeout < next_timeout) next_timeout = timeout; @@ -502,35 +522,34 @@ static void udp_timeouts(struct select_info* fd_info) */ void main_loop(struct listen_endpoint listen_sockets[], int num_addr_listen) { - struct select_info fd_info = {0}; + struct loop_info fd_info = {0}; fd_set readfds, writefds; /* working read and write fd sets */ struct timeval tv; int i, res; fd_info.num_probing = 0; - FD_ZERO(&fd_info.fds_r); - FD_ZERO(&fd_info.fds_w); fd_info.probing_list = gap_init(0); + watchers_init(&fd_info.watchers); + for (i = 0; i < num_addr_listen; i++) { - FD_SET(listen_sockets[i].socketfd, &fd_info.fds_r); + watchers_add_read(&fd_info.watchers, listen_sockets[i].socketfd); set_nonblock(listen_sockets[i].socketfd); } - fd_info.max_fd = listen_sockets[num_addr_listen-1].socketfd + 1; - fd_info.collection = collection_init(fd_info.max_fd); + fd_info.collection = collection_init(fd_info.watchers.max_fd); while (1) { memset(&tv, 0, sizeof(tv)); tv.tv_sec = cfg.timeout; - memcpy(&readfds, &fd_info.fds_r, sizeof(readfds)); - memcpy(&writefds, &fd_info.fds_w, sizeof(writefds)); + memcpy(&readfds, &fd_info.watchers.fds_r, sizeof(readfds)); + memcpy(&writefds, &fd_info.watchers.fds_w, sizeof(writefds)); print_message(msg_fd, "selecting... max_fd=%d num_probing=%d\n", - fd_info.max_fd, fd_info.num_probing); - res = select(fd_info.max_fd, &readfds, &writefds, + fd_info.watchers.max_fd, fd_info.num_probing); + res = select(fd_info.watchers.max_fd, &readfds, &writefds, NULL, fd_info.num_probing ? &tv : NULL); if (res < 0) perror("select"); @@ -550,7 +569,7 @@ void main_loop(struct listen_endpoint listen_sockets[], int num_addr_listen) } /* Check all sockets for write activity */ - for (i = 0; i < fd_info.max_fd; i++) { + for (i = 0; i < fd_info.watchers.max_fd; i++) { if (FD_ISSET(i, &writefds)) { cnx_write_process(&fd_info, i); } @@ -572,11 +591,11 @@ void main_loop(struct listen_endpoint listen_sockets[], int num_addr_listen) } /* Check all sockets for read activity */ - for (i = 0; i < fd_info.max_fd; i++) { + for (i = 0; i < fd_info.watchers.max_fd; i++) { /* Check if it's active AND currently monitored (if a connection * died, it gets tidied, which closes both sockets, but readfs does * not know about that */ - if (FD_ISSET(i, &readfds) && FD_ISSET(i, &fd_info.fds_r)) { + if (FD_ISSET(i, &readfds) && FD_ISSET(i, &fd_info.watchers.fds_r)) { cnx_read_process(&fd_info, i); } } From 3389000ff3e16e01ad557aa7353c633a6d16709b Mon Sep 17 00:00:00 2001 From: yrutschle Date: Sun, 17 Oct 2021 16:02:49 +0200 Subject: [PATCH 034/191] move action processing to its own file --- processes.c | 343 ++++++++++++++++++++++++++++++++++++++++++++++++++ processes.h | 13 ++ sslh-select.c | 321 +--------------------------------------------- 3 files changed, 360 insertions(+), 317 deletions(-) create mode 100644 processes.c diff --git a/processes.c b/processes.c new file mode 100644 index 0000000..adeeecc --- /dev/null +++ b/processes.c @@ -0,0 +1,343 @@ +/* + Processes that are common to sslh-ev and sslh-select + +# Copyright (C) 2021 Yves Rutschle +# +# This program is free software; you can redistribute it +# and/or modify it under the terms of the GNU General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later +# version. +# +# This program is distributed in the hope that it will be +# useful, but WITHOUT ANY WARRANTY; without even the implied +# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +# PURPOSE. See the GNU General Public License for more +# details. +# +# The full text for the General Public License is here: +# http://www.gnu.org/licenses/gpl.html + +*/ + +#include "processes.h" +#include "log.h" + +/* Removes cnx from probing list */ +void remove_probing_cnx(struct loop_info* fd_info, struct connection* cnx) +{ + gap_remove_ptr(fd_info->probing_list, cnx, fd_info->num_probing); + fd_info->num_probing--; +} + +void add_probing_cnx(struct loop_info* fd_info, struct connection* cnx) +{ + gap_set(fd_info->probing_list, fd_info->num_probing, cnx); + fd_info->num_probing++; +} + +/* Process a connection that is active in read */ +static void tcp_read_process(struct loop_info* fd_info, + int fd) +{ + cnx_collection* collection = fd_info->collection; + struct connection* cnx = collection_get_cnx_from_fd(collection, fd); + /* Determine active queue (0 or 1): if fd is that of q[1], active_q = 1, + * otherwise it's 0 */ + int active_q = active_queue(cnx, fd); + + switch (cnx->state) { + + case ST_PROBING: + if (active_q == 1) { + print_message(msg_int_error, "Activity on fd2 while probing, impossible\n"); + dump_connection(cnx); + exit(1); + } + + probing_read_process(cnx, fd_info); + + break; + + case ST_SHOVELING: + shovel(cnx, active_q, fd_info); + break; + + default: /* illegal */ + print_message(msg_int_error, "Illegal connection state %d\n", cnx->state); + dump_connection(cnx); + exit(1); + } +} + + +void cnx_read_process(struct loop_info* fd_info, int fd) +{ + cnx_collection* collection = fd_info->collection; + struct connection* cnx = collection_get_cnx_from_fd(collection, fd); + switch (cnx->type) { + case SOCK_STREAM: + tcp_read_process(fd_info, fd); + break; + + case SOCK_DGRAM: + udp_s2c_forward(cnx); + break; + + default: + print_message(msg_int_error, "cnx_read_process: Illegal connection type %d\n", cnx->type); + dump_connection(cnx); + exit(1); + } +} + + +/* Process a connection that is active in write */ +void cnx_write_process(struct loop_info* fd_info, int fd) +{ + struct connection* cnx = collection_get_cnx_from_fd(fd_info->collection, fd); + int res; + int queue = active_queue(cnx, fd); + + res = flush_deferred(&cnx->q[queue]); + if ((res == -1) && ((errno == EPIPE) || (errno == ECONNRESET))) { + if (cnx->state == ST_PROBING) remove_probing_cnx(fd_info, cnx); + tidy_connection(cnx, fd_info); + } else { + /* If no deferred data is left, stop monitoring the fd + * for write, and restart monitoring the other one for reads*/ + if (!cnx->q[queue].deferred_data_size) { + watchers_del_write(&fd_info->watchers, cnx->q[queue].fd); + watchers_add_read(&fd_info->watchers, cnx->q[1-queue].fd); + } + } +} + +/* Accepts a connection from the main socket and assigns it to an empty slot. + * If no slots are available, allocate another few. If that fails, drop the + * connexion */ +static struct connection* accept_new_connection(int listen_socket, struct cnx_collection *collection) +{ + int in_socket, res; + + + print_message(msg_fd, "accepting from %d\n", listen_socket); + + in_socket = accept(listen_socket, 0, 0); + CHECK_RES_RETURN(in_socket, "accept", NULL); + + res = set_nonblock(in_socket); + if (res == -1) { + close(in_socket); + return NULL; + } + + struct connection* cnx = collection_alloc_cnx_from_fd(collection, in_socket); + if (!cnx) { + close(in_socket); + return NULL; + } + + return cnx; +} + +/* Process a connection that accepts a socket + * (For UDP, this means all traffic coming from remote clients) + * Returns new file descriptor, or -1 + * */ +void cnx_accept_process(struct loop_info* fd_info, struct listen_endpoint* listen_socket) +{ + int fd = listen_socket->socketfd; + int type = listen_socket->type; + struct connection* cnx; + int new_fd = -1; + + switch (type) { + case SOCK_STREAM: + cnx = accept_new_connection(fd, fd_info->collection); + + if (cnx) { + add_probing_cnx(fd_info, cnx); + new_fd = cnx->q[0].fd; + } + break; + + case SOCK_DGRAM: + new_fd = udp_c2s_forward(fd, fd_info->collection, fd_info->watchers.max_fd); + print_message(msg_fd, "new_fd %d\n", new_fd); + if (new_fd == -1) + return; + break; + + default: + print_message(msg_int_error, "Inconsistent cnx type: %d\n", type); + exit(1); + return; + } + + watchers_add_read(&fd_info->watchers, new_fd); +} + + +/* shovels data from active fd to the other + returns after one socket closed or operation would block + */ +static void shovel(struct connection *cnx, int active_fd, struct loop_info* fd_info) +{ + struct queue *read_q, *write_q; + + read_q = &cnx->q[active_fd]; + write_q = &cnx->q[1-active_fd]; + + print_message(msg_fd, "activity on fd%d\n", read_q->fd); + + switch(fd2fd(write_q, read_q)) { + case -1: + case FD_CNXCLOSED: + tidy_connection(cnx, fd_info); + break; + + case FD_STALLED: + watchers_add_write(&fd_info->watchers, write_q->fd); + watchers_del_read(&fd_info->watchers, read_q->fd); + break; + + default: /* Nothing */ + break; + } +} + +/* shovels data from one fd to the other and vice-versa + returns after one socket closed + */ +static void shovel_single(struct connection *cnx) +{ + fd_set fds_r, fds_w; + int res, i; + int max_fd = MAX(cnx->q[0].fd, cnx->q[1].fd) + 1; + + FD_ZERO(&fds_r); + FD_ZERO(&fds_w); + while (1) { + for (i = 0; i < 2; i++) { + if (cnx->q[i].deferred_data_size) { + FD_SET(cnx->q[i].fd, &fds_w); + FD_CLR(cnx->q[1-i].fd, &fds_r); + } else { + FD_CLR(cnx->q[i].fd, &fds_w); + FD_SET(cnx->q[1-i].fd, &fds_r); + } + } + + res = select( + max_fd, + &fds_r, + &fds_w, + NULL, + NULL + ); + CHECK_RES_DIE(res, "select"); + + for (i = 0; i < 2; i++) { + if (FD_ISSET(cnx->q[i].fd, &fds_w)) { + res = flush_deferred(&cnx->q[i]); + if ((res == -1) && ((errno == EPIPE) || (errno == ECONNRESET))) { + print_message(msg_fd, "%s socket closed\n", i ? "server" : "client"); + return; + } + } + if (FD_ISSET(cnx->q[i].fd, &fds_r)) { + res = fd2fd(&cnx->q[1-i], &cnx->q[i]); + if (!res) { + print_message(msg_fd, "socket closed\n"); + return; + } + } + } + } +} + + +/* Child process that makes internal connection and proxies + */ +static void connect_proxy(struct connection *cnx) +{ + int in_socket; + int out_socket; + + /* Minimize the file descriptor value to help select() */ + in_socket = dup(cnx->q[0].fd); + if (in_socket == -1) { + in_socket = cnx->q[0].fd; + } else { + close(cnx->q[0].fd); + cnx->q[0].fd = in_socket; + } + + /* Connect the target socket */ + out_socket = connect_addr(cnx, in_socket, BLOCKING); + CHECK_RES_DIE(out_socket, "connect"); + + cnx->q[1].fd = out_socket; + + log_connection(NULL, cnx); + + shovel_single(cnx); + + close(in_socket); + close(out_socket); + + print_message(msg_fd, "connection closed down\n"); + + exit(0); +} + + + +/* Process read activity on a socket in probe state + * IN/OUT cnx: connection data, updated if connected + * IN/OUT info: updated if connected + * */ +void probing_read_process(struct connection* cnx, + struct loop_info* fd_info) +{ + int res; + + /* If timed out it's SSH, otherwise the client sent + * data so probe the protocol */ + if ((cnx->probe_timeout < time(NULL))) { + cnx->proto = timeout_protocol(); + print_message(msg_fd, "timed out, connect to %s\n", cnx->proto->name); + } else { + res = probe_client_protocol(cnx); + if (res == PROBE_AGAIN) + return; + } + + remove_probing_cnx(fd_info, cnx); + cnx->state = ST_SHOVELING; + + /* libwrap check if required for this protocol */ + if (cnx->proto->service && + check_access_rights(cnx->q[0].fd, cnx->proto->service)) { + tidy_connection(cnx, fd_info); + res = -1; + } else if (cnx->proto->fork) { + switch (fork()) { + case 0: /* child */ + /* TODO: close all file descriptors except 2 */ + /* free(cnx); */ + connect_proxy(cnx); + exit(0); + case -1: print_message(msg_system_error, "fork failed: err %d: %s\n", errno, strerror(errno)); + break; + default: /* parent */ + break; + } + tidy_connection(cnx, fd_info); + res = -1; + } else { + res = connect_queue(cnx, fd_info); + } +} + diff --git a/processes.h b/processes.h index 52e07e2..845988c 100644 --- a/processes.h +++ b/processes.h @@ -5,6 +5,11 @@ #error Define watchers type before including processes.h #endif +#include "common.h" +#include "collection.h" +#include "gap.h" + + /* Global state for a loop */ struct loop_info { int num_probing; /* Number of connections currently probing @@ -19,4 +24,12 @@ struct loop_info { time_t next_timeout; /* time at which next UDP connection times out */ }; +void cnx_read_process(struct loop_info* fd_info, int fd); +void cnx_write_process(struct loop_info* fd_info, int fd); +void cnx_accept_process(struct loop_info* fd_info, struct listen_endpoint* listen_socket); +void probing_read_process(struct connection* cnx, struct loop_info* fd_info); + +void remove_probing_cnx(struct loop_info* fd_info, struct connection* cnx); +void add_probing_cnx(struct loop_info* fd_info, struct connection* cnx); + #endif diff --git a/sslh-select.c b/sslh-select.c index f2258ee..3a1bb24 100644 --- a/sslh-select.c +++ b/sslh-select.c @@ -113,38 +113,6 @@ static int fd_is_in_range(int fd) { return 1; } -/* Accepts a connection from the main socket and assigns it to an empty slot. - * If no slots are available, allocate another few. If that fails, drop the - * connexion */ -static struct connection* accept_new_connection(int listen_socket, struct cnx_collection *collection) -{ - int in_socket, res; - - - print_message(msg_fd, "accepting from %d\n", listen_socket); - - in_socket = accept(listen_socket, 0, 0); - CHECK_RES_RETURN(in_socket, "accept", NULL); - - if (!fd_is_in_range(in_socket)) { - close(in_socket); - return NULL; - } - - res = set_nonblock(in_socket); - if (res == -1) { - close(in_socket); - return NULL; - } - - struct connection* cnx = collection_alloc_cnx_from_fd(collection, in_socket); - if (!cnx) { - close(in_socket); - return NULL; - } - - return cnx; -} /* Connect queue 1 of connection to SSL; returns new file descriptor */ @@ -170,180 +138,6 @@ static int connect_queue(struct connection* cnx, } } -/* shovels data from active fd to the other - returns after one socket closed or operation would block - */ -static void shovel(struct connection *cnx, int active_fd, struct loop_info* fd_info) -{ - struct queue *read_q, *write_q; - - read_q = &cnx->q[active_fd]; - write_q = &cnx->q[1-active_fd]; - - print_message(msg_fd, "activity on fd%d\n", read_q->fd); - - switch(fd2fd(write_q, read_q)) { - case -1: - case FD_CNXCLOSED: - tidy_connection(cnx, fd_info); - break; - - case FD_STALLED: - watchers_add_write(&fd_info->watchers, write_q->fd); - watchers_del_read(&fd_info->watchers, read_q->fd); - break; - - default: /* Nothing */ - break; - } -} - -/* shovels data from one fd to the other and vice-versa - returns after one socket closed - */ -static void shovel_single(struct connection *cnx) -{ - fd_set fds_r, fds_w; - int res, i; - int max_fd = MAX(cnx->q[0].fd, cnx->q[1].fd) + 1; - - FD_ZERO(&fds_r); - FD_ZERO(&fds_w); - while (1) { - for (i = 0; i < 2; i++) { - if (cnx->q[i].deferred_data_size) { - FD_SET(cnx->q[i].fd, &fds_w); - FD_CLR(cnx->q[1-i].fd, &fds_r); - } else { - FD_CLR(cnx->q[i].fd, &fds_w); - FD_SET(cnx->q[1-i].fd, &fds_r); - } - } - - res = select( - max_fd, - &fds_r, - &fds_w, - NULL, - NULL - ); - CHECK_RES_DIE(res, "select"); - - for (i = 0; i < 2; i++) { - if (FD_ISSET(cnx->q[i].fd, &fds_w)) { - res = flush_deferred(&cnx->q[i]); - if ((res == -1) && ((errno == EPIPE) || (errno == ECONNRESET))) { - print_message(msg_fd, "%s socket closed\n", i ? "server" : "client"); - return; - } - } - if (FD_ISSET(cnx->q[i].fd, &fds_r)) { - res = fd2fd(&cnx->q[1-i], &cnx->q[i]); - if (!res) { - print_message(msg_fd, "socket closed\n"); - return; - } - } - } - } -} - -/* Child process that makes internal connection and proxies - */ -static void connect_proxy(struct connection *cnx) -{ - int in_socket; - int out_socket; - - /* Minimize the file descriptor value to help select() */ - in_socket = dup(cnx->q[0].fd); - if (in_socket == -1) { - in_socket = cnx->q[0].fd; - } else { - close(cnx->q[0].fd); - cnx->q[0].fd = in_socket; - } - - /* Connect the target socket */ - out_socket = connect_addr(cnx, in_socket, BLOCKING); - CHECK_RES_DIE(out_socket, "connect"); - - cnx->q[1].fd = out_socket; - - log_connection(NULL, cnx); - - shovel_single(cnx); - - close(in_socket); - close(out_socket); - - print_message(msg_fd, "connection closed down\n"); - - exit(0); -} - -/* Removes cnx from probing list */ -static void remove_probing_cnx(struct loop_info* fd_info, struct connection* cnx) -{ - gap_remove_ptr(fd_info->probing_list, cnx, fd_info->num_probing); - fd_info->num_probing--; -} - -static void add_probing_cnx(struct loop_info* fd_info, struct connection* cnx) -{ - gap_set(fd_info->probing_list, fd_info->num_probing, cnx); - fd_info->num_probing++; -} - - -/* Process read activity on a socket in probe state - * IN/OUT cnx: connection data, updated if connected - * IN/OUT info: updated if connected - * */ - -static void probing_read_process(struct connection* cnx, - struct loop_info* fd_info) -{ - int res; - - /* If timed out it's SSH, otherwise the client sent - * data so probe the protocol */ - if ((cnx->probe_timeout < time(NULL))) { - cnx->proto = timeout_protocol(); - print_message(msg_fd, "timed out, connect to %s\n", cnx->proto->name); - } else { - res = probe_client_protocol(cnx); - if (res == PROBE_AGAIN) - return; - } - - remove_probing_cnx(fd_info, cnx); - cnx->state = ST_SHOVELING; - - /* libwrap check if required for this protocol */ - if (cnx->proto->service && - check_access_rights(cnx->q[0].fd, cnx->proto->service)) { - tidy_connection(cnx, fd_info); - res = -1; - } else if (cnx->proto->fork) { - switch (fork()) { - case 0: /* child */ - /* TODO: close all file descriptors except 2 */ - /* free(cnx); */ - connect_proxy(cnx); - exit(0); - case -1: print_message(msg_system_error, "fork failed: err %d: %s\n", errno, strerror(errno)); - break; - default: /* parent */ - break; - } - tidy_connection(cnx, fd_info); - res = -1; - } else { - res = connect_queue(cnx, fd_info); - } -} - /* Returns the queue index that contains the specified file descriptor */ int active_queue(struct connection* cnx, int fd) @@ -355,117 +149,6 @@ int active_queue(struct connection* cnx, int fd) return -1; } -/* Process a connection that is active in read */ -static void tcp_read_process(struct loop_info* fd_info, - int fd) -{ - cnx_collection* collection = fd_info->collection; - struct connection* cnx = collection_get_cnx_from_fd(collection, fd); - /* Determine active queue (0 or 1): if fd is that of q[1], active_q = 1, - * otherwise it's 0 */ - int active_q = active_queue(cnx, fd); - - switch (cnx->state) { - - case ST_PROBING: - if (active_q == 1) { - print_message(msg_int_error, "Activity on fd2 while probing, impossible\n"); - dump_connection(cnx); - exit(1); - } - - probing_read_process(cnx, fd_info); - - break; - - case ST_SHOVELING: - shovel(cnx, active_q, fd_info); - break; - - default: /* illegal */ - print_message(msg_int_error, "Illegal connection state %d\n", cnx->state); - dump_connection(cnx); - exit(1); - } -} - -static void cnx_read_process(struct loop_info* fd_info, int fd) -{ - cnx_collection* collection = fd_info->collection; - struct connection* cnx = collection_get_cnx_from_fd(collection, fd); - switch (cnx->type) { - case SOCK_STREAM: - tcp_read_process(fd_info, fd); - break; - - case SOCK_DGRAM: - udp_s2c_forward(cnx); - break; - - default: - print_message(msg_int_error, "cnx_read_process: Illegal connection type %d\n", cnx->type); - dump_connection(cnx); - exit(1); - } -} - -/* Process a connection that is active in write */ -void cnx_write_process(struct loop_info* fd_info, int fd) -{ - struct connection* cnx = collection_get_cnx_from_fd(fd_info->collection, fd); - int res; - int queue = active_queue(cnx, fd); - - res = flush_deferred(&cnx->q[queue]); - if ((res == -1) && ((errno == EPIPE) || (errno == ECONNRESET))) { - if (cnx->state == ST_PROBING) remove_probing_cnx(fd_info, cnx); - tidy_connection(cnx, fd_info); - } else { - /* If no deferred data is left, stop monitoring the fd - * for write, and restart monitoring the other one for reads*/ - if (!cnx->q[queue].deferred_data_size) { - watchers_del_write(&fd_info->watchers, cnx->q[queue].fd); - watchers_add_read(&fd_info->watchers, cnx->q[1-queue].fd); - } - } -} - -/* Process a connection that accepts a socket - * (For UDP, this means all traffic coming from remote clients) - * Returns new file descriptor, or -1 - * */ -void cnx_accept_process(struct loop_info* fd_info, struct listen_endpoint* listen_socket) -{ - int fd = listen_socket->socketfd; - int type = listen_socket->type; - struct connection* cnx; - int new_fd = -1; - - switch (type) { - case SOCK_STREAM: - cnx = accept_new_connection(fd, fd_info->collection); - - if (cnx) { - add_probing_cnx(fd_info, cnx); - new_fd = cnx->q[0].fd; - } - break; - - case SOCK_DGRAM: - new_fd = udp_c2s_forward(fd, fd_info->collection, fd_info->watchers.max_fd); - print_message(msg_fd, "new_fd %d\n", new_fd); - if (new_fd == -1) - return; - break; - - default: - print_message(msg_int_error, "Inconsistent cnx type: %d\n", type); - exit(1); - return; - } - - watchers_add_read(&fd_info->watchers, new_fd); -} /* Check all connections to see if a UDP connections has timed out, then free @@ -563,6 +246,10 @@ void main_loop(struct listen_endpoint listen_sockets[], int num_addr_listen) if (FD_ISSET(listen_sockets[i].socketfd, &readfds)) { cnx_accept_process(&fd_info, &listen_sockets[i]); + if (!fd_is_in_range(0 /*TODO: retrieve fd */ )) { + /* TODO: drop the connection */ + } + /* don't also process it as a read socket */ FD_CLR(listen_sockets[i].socketfd, &readfds); } From ec033efbbcbd3c1bdfa432e50475dd75c8e29fed Mon Sep 17 00:00:00 2001 From: yrutschle Date: Tue, 26 Oct 2021 21:45:44 +0200 Subject: [PATCH 035/191] refactor more code from sslh-select to processes.c --- Makefile | 13 +++-- processes.c | 130 ++++++++++++++++++++++++++++++++++++------------- processes.h | 21 +++++--- sslh-select.c | 103 +++++++++++---------------------------- udp-listener.h | 1 + 5 files changed, 148 insertions(+), 120 deletions(-) diff --git a/Makefile b/Makefile index 6ddc454..45aba8c 100644 --- a/Makefile +++ b/Makefile @@ -27,7 +27,7 @@ CC ?= gcc CFLAGS ?=-Wall -DLIBPCRE -g $(CFLAGS_COV) LIBS=-lm -lpcre2-8 -OBJS=sslh-conf.o common.o log.o sslh-main.o probe.o tls.o argtable3.o udp-listener.o collection.o gap.o +OBJS=sslh-conf.o common.o log.o sslh-main.o probe.o tls.o argtable3.o udp-listener.o collection.o gap.o processes.o CONDITIONAL_TARGETS= @@ -70,21 +70,26 @@ all: sslh $(MAN) echosrv $(CONDITIONAL_TARGETS) version.h: ./genver.sh >version.h -sslh: sslh-fork sslh-select +sslh: sslh-fork sslh-select # sslh-ev $(OBJS): version.h common.h collection.h sslh-conf.h gap.h sslh-conf.c sslh-conf.h: sslhconf.cfg conf2struct sslhconf.cfg -sslh-fork: version.h $(OBJS) sslh-fork.o Makefile - $(CC) $(CFLAGS) $(LDFLAGS) -o sslh-fork sslh-fork.o $(OBJS) $(LIBS) +FORK_OBJS=sslh-conf.o common.o log.o sslh-main.o probe.o tls.o argtable3.o udp-listener.o collection.o gap.o +sslh-fork: version.h sslh-fork.o Makefile $(FORK_OBJS) + $(CC) $(CFLAGS) $(LDFLAGS) -o sslh-fork sslh-fork.o $(FORK_OBJS) $(LIBS) #strip sslh-fork sslh-select: version.h $(OBJS) sslh-select.o Makefile $(CC) $(CFLAGS) $(LDFLAGS) -o sslh-select sslh-select.o $(OBJS) $(LIBS) #strip sslh-select +sslh-ev: version.h $(OBJS) sslh-ev.o Makefile + $(CC) $(CFLAGS) $(LDFLAGS) -o sslh-ev sslh-ev.o $(OBJS) $(LIBS) + #strip sslh-select + systemd-sslh-generator: systemd-sslh-generator.o $(CC) $(CFLAGS) $(LDFLAGS) -o systemd-sslh-generator systemd-sslh-generator.o -lconfig diff --git a/processes.c b/processes.c index adeeecc..31b198c 100644 --- a/processes.c +++ b/processes.c @@ -20,7 +20,9 @@ */ +#include "udp-listener.h" #include "processes.h" +#include "probe.h" #include "log.h" /* Removes cnx from probing list */ @@ -36,6 +38,65 @@ void add_probing_cnx(struct loop_info* fd_info, struct connection* cnx) fd_info->num_probing++; } +/* Returns the queue index that contains the specified file descriptor */ +static int active_queue(struct connection* cnx, int fd) +{ + if (cnx->q[0].fd == fd) return 0; + if (cnx->q[1].fd == fd) return 1; + + print_message(msg_int_error, "file descriptor %d not found in connection object\n", fd); + return -1; +} + +int tidy_connection(struct connection *cnx, struct loop_info* fd_info) +{ + int i; + + for (i = 0; i < 2; i++) { + if (cnx->q[i].fd != -1) { + print_message(msg_fd, "closing fd %d\n", cnx->q[i].fd); + + watchers_del_read(fd_info->watchers, cnx->q[i].fd); + watchers_del_write(fd_info->watchers, cnx->q[i].fd); + close(cnx->q[i].fd); + if (cnx->q[i].deferred_data) + free(cnx->q[i].deferred_data); + } + } + collection_remove_cnx(fd_info->collection, cnx); + return 0; +} + + +/* shovels data from active fd to the other + returns after one socket closed or operation would block + */ +static void shovel(struct connection *cnx, int active_fd, struct loop_info* fd_info) +{ + struct queue *read_q, *write_q; + + read_q = &cnx->q[active_fd]; + write_q = &cnx->q[1-active_fd]; + + print_message(msg_fd, "activity on fd%d\n", read_q->fd); + + switch(fd2fd(write_q, read_q)) { + case -1: + case FD_CNXCLOSED: + tidy_connection(cnx, fd_info); + break; + + case FD_STALLED: + watchers_add_write(fd_info->watchers, write_q->fd); + watchers_del_read(fd_info->watchers, read_q->fd); + break; + + default: /* Nothing */ + break; + } +} + + /* Process a connection that is active in read */ static void tcp_read_process(struct loop_info* fd_info, int fd) @@ -107,8 +168,8 @@ void cnx_write_process(struct loop_info* fd_info, int fd) /* If no deferred data is left, stop monitoring the fd * for write, and restart monitoring the other one for reads*/ if (!cnx->q[queue].deferred_data_size) { - watchers_del_write(&fd_info->watchers, cnx->q[queue].fd); - watchers_add_read(&fd_info->watchers, cnx->q[1-queue].fd); + watchers_del_write(fd_info->watchers, cnx->q[queue].fd); + watchers_add_read(fd_info->watchers, cnx->q[1-queue].fd); } } } @@ -145,7 +206,7 @@ static struct connection* accept_new_connection(int listen_socket, struct cnx_co * (For UDP, this means all traffic coming from remote clients) * Returns new file descriptor, or -1 * */ -void cnx_accept_process(struct loop_info* fd_info, struct listen_endpoint* listen_socket) +int cnx_accept_process(struct loop_info* fd_info, struct listen_endpoint* listen_socket) { int fd = listen_socket->socketfd; int type = listen_socket->type; @@ -163,50 +224,22 @@ void cnx_accept_process(struct loop_info* fd_info, struct listen_endpoint* liste break; case SOCK_DGRAM: - new_fd = udp_c2s_forward(fd, fd_info->collection, fd_info->watchers.max_fd); + new_fd = udp_c2s_forward(fd, fd_info->collection, watchers_maxfd(fd_info->watchers)); print_message(msg_fd, "new_fd %d\n", new_fd); if (new_fd == -1) - return; + return -1; break; default: print_message(msg_int_error, "Inconsistent cnx type: %d\n", type); exit(1); - return; } - watchers_add_read(&fd_info->watchers, new_fd); + watchers_add_read(fd_info->watchers, new_fd); + return new_fd; } -/* shovels data from active fd to the other - returns after one socket closed or operation would block - */ -static void shovel(struct connection *cnx, int active_fd, struct loop_info* fd_info) -{ - struct queue *read_q, *write_q; - - read_q = &cnx->q[active_fd]; - write_q = &cnx->q[1-active_fd]; - - print_message(msg_fd, "activity on fd%d\n", read_q->fd); - - switch(fd2fd(write_q, read_q)) { - case -1: - case FD_CNXCLOSED: - tidy_connection(cnx, fd_info); - break; - - case FD_STALLED: - watchers_add_write(&fd_info->watchers, write_q->fd); - watchers_del_read(&fd_info->watchers, read_q->fd); - break; - - default: /* Nothing */ - break; - } -} - /* shovels data from one fd to the other and vice-versa returns after one socket closed */ @@ -293,6 +326,33 @@ static void connect_proxy(struct connection *cnx) } +/* Connect queue 1 of connection to SSL; returns new file descriptor */ +static int connect_queue(struct connection* cnx, + struct loop_info* fd_info) +{ + struct queue *q = &cnx->q[1]; + + q->fd = connect_addr(cnx, cnx->q[0].fd, NON_BLOCKING); + if (q->fd != -1) { + log_connection(NULL, cnx); + flush_deferred(q); + if (q->deferred_data) { + /* + FD_SET(q->fd, &fd_info->watchers->fds_w); + FD_CLR(cnx->q[0].fd, &fd_info->watchers->fds_r); */ + watchers_add_write(fd_info->watchers, q->fd); + watchers_del_read(fd_info->watchers, cnx->q[0].fd); + } + /* FD_SET(q->fd, &fd_info->watchers->fds_r); */ + watchers_add_read(fd_info->watchers, q->fd); + collection_add_fd(fd_info->collection, cnx, q->fd); + return q->fd; + } else { + tidy_connection(cnx, fd_info); + return -1; + } +} + /* Process read activity on a socket in probe state * IN/OUT cnx: connection data, updated if connected diff --git a/processes.h b/processes.h index 845988c..295972b 100644 --- a/processes.h +++ b/processes.h @@ -1,14 +1,13 @@ #ifndef PROCESSES_H #define PROCESSES_H -#ifndef WATCHERS_TYPE_DEFINED -#error Define watchers type before including processes.h -#endif - #include "common.h" #include "collection.h" #include "gap.h" +/* Provided by event loop, sslh-ev or sslh-select, for implementation-dependant + * data */ +typedef struct watchers watchers; /* Global state for a loop */ struct loop_info { @@ -17,7 +16,7 @@ struct loop_info { * select() */ gap_array* probing_list; /* Pointers to cnx that are in probing mode */ - watchers watchers; + watchers* watchers; cnx_collection* collection; /* Collection of connections linked to this loop */ @@ -26,10 +25,20 @@ struct loop_info { void cnx_read_process(struct loop_info* fd_info, int fd); void cnx_write_process(struct loop_info* fd_info, int fd); -void cnx_accept_process(struct loop_info* fd_info, struct listen_endpoint* listen_socket); +int cnx_accept_process(struct loop_info* fd_info, struct listen_endpoint* listen_socket); void probing_read_process(struct connection* cnx, struct loop_info* fd_info); void remove_probing_cnx(struct loop_info* fd_info, struct connection* cnx); void add_probing_cnx(struct loop_info* fd_info, struct connection* cnx); +int tidy_connection(struct connection *cnx, struct loop_info* fd_info); + + +/* These must be declared in the loop handler, sslh-ev or sslh-select */ +void watchers_init(watchers** w); +void watchers_add_read(watchers* w, int fd); +void watchers_del_read(watchers* w, int fd); +void watchers_add_write(watchers* w, int fd); +void watchers_del_write(watchers* w, int fd); +int watchers_maxfd(watchers* w); #endif diff --git a/sslh-select.c b/sslh-select.c index 3a1bb24..33240bb 100644 --- a/sslh-select.c +++ b/sslh-select.c @@ -42,18 +42,19 @@ const char* server_type = "sslh-select"; /* watcher type for a select() loop */ -typedef struct watchers { +struct watchers { fd_set fds_r, fds_w; /* reference fd sets (used to init working copies) */ int max_fd; /* Highest fd number to pass to select() */ -} watchers; +}; #define WATCHERS_TYPE_DEFINED /* To notify processes.h */ #include "processes.h" -void watchers_init(watchers* w) +void watchers_init(watchers** w) { - FD_ZERO(&w->fds_r); - FD_ZERO(&w->fds_w); + *w = malloc(sizeof(**w)); + FD_ZERO(&(*w)->fds_r); + FD_ZERO(&(*w)->fds_w); } void watchers_add_read(watchers* w, int fd) @@ -79,32 +80,22 @@ void watchers_del_write(watchers* w, int fd) { FD_CLR(fd, &w->fds_w); } + +/* To remove after moving UDP lookups to hash table */ +int watchers_maxfd(watchers* w) +{ + return w->max_fd; +} + /* /end watchers */ -static int tidy_connection(struct connection *cnx, struct loop_info* fd_info) -{ - int i; - - for (i = 0; i < 2; i++) { - if (cnx->q[i].fd != -1) { - print_message(msg_fd, "closing fd %d\n", cnx->q[i].fd); - - watchers_del_read(&fd_info->watchers, cnx->q[i].fd); - watchers_del_write(&fd_info->watchers, cnx->q[i].fd); - close(cnx->q[i].fd); - if (cnx->q[i].deferred_data) - free(cnx->q[i].deferred_data); - } - } - collection_remove_cnx(fd_info->collection, cnx); - return 0; -} - /* if fd becomes higher than FD_SETSIZE, things won't work so well with FD_SET * and FD_CLR. Need to drop connections if we go above that limit */ +#warning strange things will happen if more than FD_SETSIZE descriptors are used +/* This test is currently not done */ static int fd_is_in_range(int fd) { if (fd >= FD_SETSIZE) { print_message(msg_system_error, "too many open file descriptor to monitor them all -- dropping connection\n"); @@ -115,40 +106,6 @@ static int fd_is_in_range(int fd) { -/* Connect queue 1 of connection to SSL; returns new file descriptor */ -static int connect_queue(struct connection* cnx, - struct loop_info* fd_info) -{ - struct queue *q = &cnx->q[1]; - - q->fd = connect_addr(cnx, cnx->q[0].fd, NON_BLOCKING); - if ((q->fd != -1) && fd_is_in_range(q->fd)) { - log_connection(NULL, cnx); - flush_deferred(q); - if (q->deferred_data) { - FD_SET(q->fd, &fd_info->watchers.fds_w); - FD_CLR(cnx->q[0].fd, &fd_info->watchers.fds_r); - } - FD_SET(q->fd, &fd_info->watchers.fds_r); - collection_add_fd(fd_info->collection, cnx, q->fd); - return q->fd; - } else { - tidy_connection(cnx, fd_info); - return -1; - } -} - - -/* Returns the queue index that contains the specified file descriptor */ -int active_queue(struct connection* cnx, int fd) -{ - if (cnx->q[0].fd == fd) return 0; - if (cnx->q[1].fd == fd) return 1; - - print_message(msg_int_error, "file descriptor %d not found in connection object\n", fd); - return -1; -} - /* Check all connections to see if a UDP connections has timed out, then free @@ -164,10 +121,10 @@ static void udp_timeouts(struct loop_info* fd_info) time_t next_timeout = INT_MAX; - for (int i = 0; i < fd_info->watchers.max_fd; i++) { + for (int i = 0; i < fd_info->watchers->max_fd; i++) { /* if it's either in read or write set, there is a connection * behind that file descriptor */ - if (FD_ISSET(i, &fd_info->watchers.fds_r) || FD_ISSET(i, &fd_info->watchers.fds_w)) { + if (FD_ISSET(i, &fd_info->watchers->fds_r) || FD_ISSET(i, &fd_info->watchers->fds_w)) { struct connection* cnx = collection_get_cnx_from_fd(fd_info->collection, i); if (cnx) { time_t timeout = udp_timeout(cnx); @@ -175,8 +132,8 @@ static void udp_timeouts(struct loop_info* fd_info) if (cnx && (timeout <= now)) { print_message(msg_fd, "timed out UDP %d\n", cnx->target_sock); close(cnx->target_sock); - watchers_del_read(&fd_info->watchers, i); - watchers_del_write(&fd_info->watchers, i); + watchers_del_read(fd_info->watchers, i); + watchers_del_write(fd_info->watchers, i); collection_remove_cnx(fd_info->collection, cnx); } else { if (timeout < next_timeout) next_timeout = timeout; @@ -216,23 +173,23 @@ void main_loop(struct listen_endpoint listen_sockets[], int num_addr_listen) watchers_init(&fd_info.watchers); for (i = 0; i < num_addr_listen; i++) { - watchers_add_read(&fd_info.watchers, listen_sockets[i].socketfd); + watchers_add_read(fd_info.watchers, listen_sockets[i].socketfd); set_nonblock(listen_sockets[i].socketfd); } - fd_info.collection = collection_init(fd_info.watchers.max_fd); + fd_info.collection = collection_init(fd_info.watchers->max_fd); while (1) { memset(&tv, 0, sizeof(tv)); tv.tv_sec = cfg.timeout; - memcpy(&readfds, &fd_info.watchers.fds_r, sizeof(readfds)); - memcpy(&writefds, &fd_info.watchers.fds_w, sizeof(writefds)); + memcpy(&readfds, &fd_info.watchers->fds_r, sizeof(readfds)); + memcpy(&writefds, &fd_info.watchers->fds_w, sizeof(writefds)); print_message(msg_fd, "selecting... max_fd=%d num_probing=%d\n", - fd_info.watchers.max_fd, fd_info.num_probing); - res = select(fd_info.watchers.max_fd, &readfds, &writefds, + fd_info.watchers->max_fd, fd_info.num_probing); + res = select(fd_info.watchers->max_fd, &readfds, &writefds, NULL, fd_info.num_probing ? &tv : NULL); if (res < 0) perror("select"); @@ -246,17 +203,13 @@ void main_loop(struct listen_endpoint listen_sockets[], int num_addr_listen) if (FD_ISSET(listen_sockets[i].socketfd, &readfds)) { cnx_accept_process(&fd_info, &listen_sockets[i]); - if (!fd_is_in_range(0 /*TODO: retrieve fd */ )) { - /* TODO: drop the connection */ - } - /* don't also process it as a read socket */ FD_CLR(listen_sockets[i].socketfd, &readfds); } } /* Check all sockets for write activity */ - for (i = 0; i < fd_info.watchers.max_fd; i++) { + for (i = 0; i < fd_info.watchers->max_fd; i++) { if (FD_ISSET(i, &writefds)) { cnx_write_process(&fd_info, i); } @@ -278,11 +231,11 @@ void main_loop(struct listen_endpoint listen_sockets[], int num_addr_listen) } /* Check all sockets for read activity */ - for (i = 0; i < fd_info.watchers.max_fd; i++) { + for (i = 0; i < fd_info.watchers->max_fd; i++) { /* Check if it's active AND currently monitored (if a connection * died, it gets tidied, which closes both sockets, but readfs does * not know about that */ - if (FD_ISSET(i, &readfds) && FD_ISSET(i, &fd_info.watchers.fds_r)) { + if (FD_ISSET(i, &readfds) && FD_ISSET(i, &fd_info.watchers->fds_r)) { cnx_read_process(&fd_info, i); } } diff --git a/udp-listener.h b/udp-listener.h index 7b44f10..9439846 100644 --- a/udp-listener.h +++ b/udp-listener.h @@ -2,6 +2,7 @@ #define UDPLISTENER_H #include "collection.h" +#include "common.h" /* UDP listener: upon incoming packet, find where it should go * This is run in its own process and never returns. From d57a155bf413c96ed8f9a0a716b923480cb76168 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Ketelaars?= Date: Wed, 27 Oct 2021 18:13:17 +0200 Subject: [PATCH 036/191] Fix (some) failing tests Found a couple of failing tests on Alpine Linux and OpenBSD. For the tests to even run `ip4-localhost` has to be changed to an IP-address (127.0.0.1). `ip4-localhost` is typically not part of `/etc/hosts`. Output failing tests: ``` not ok 5 udp: 0 prefix: tls: listen [1]: host: localhost port: 9025 flushing deferred data to fd 9 selecting... max_fd=11 num_probing=0 activity on fd8 closing fd 8 closing fd 9 selecting... max_fd=11 num_probing=0 # Failed test at ./t line 59. # got: '1' # expected: 'sslh-select: Connect and write nothing' ``` ``` not ok 22 - sslh-select:ssh: probe connected correctly # Failed test 'sslh-select:ssh: probe connected correctly' # at ./t line 59. # got: 'regex' # expected: 'ssh' ``` ``` not ok 68 - sslh-select:ssh: probe connected correctly # Failed test 'sslh-select:ssh: probe connected correctly' # at ./t line 59. # got: 'regex' # expected: 'ssh' ``` Diff works around issues causing tests 22 and 68 to fail. As of yet, no workaround for test 5 has been found. --- test.cfg | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test.cfg b/test.cfg index 4330cfa..2bb0edc 100644 --- a/test.cfg +++ b/test.cfg @@ -32,7 +32,7 @@ listen: ( { host: "localhost"; port: "8080"; keepalive: true; }, { host: "localhost"; port: "8081"; keepalive: true; }, - { host: "ip4-localhost"; is_udp: true; port: "8086"; } + { host: "127.0.0.1"; is_udp: true; port: "8086"; } ); @@ -46,7 +46,7 @@ protocols: { name: "xmpp"; host: "localhost"; port: "9009"; }, { name: "adb"; host: "localhost"; port: "9010"; }, { name: "syslog"; host: "localhost"; port: "9013"; }, - { name: "regex"; host: "ip4-localhost"; is_udp: true; port: "9020"; + { name: "ssh"; host: "127.0.0.1"; is_udp: true; port: "9020"; udp_timeout: 30; regex_patterns: [ "^foo" ]; }, From 5ec1f7eb98482e0b830fa249a64bad6b180f3bec Mon Sep 17 00:00:00 2001 From: yrutschle Date: Thu, 28 Oct 2021 15:40:40 +0200 Subject: [PATCH 037/191] added missing header --- log.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/log.h b/log.h index 4a345c6..84bb32f 100644 --- a/log.h +++ b/log.h @@ -1,6 +1,8 @@ #ifndef LOG_H #define LOG_H +#include "common.h" + void setup_syslog(const char* bin_name); void log_connection(struct connection_desc* desc, const struct connection *cnx); From 4cd3ab8958e85e242fd7736ed2ef736e0adff44d Mon Sep 17 00:00:00 2001 From: yrutschle Date: Thu, 28 Oct 2021 15:41:09 +0200 Subject: [PATCH 038/191] moved watcher add to watcher init --- processes.h | 1 - sslh-select.c | 15 ++++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/processes.h b/processes.h index 295972b..a15d400 100644 --- a/processes.h +++ b/processes.h @@ -34,7 +34,6 @@ int tidy_connection(struct connection *cnx, struct loop_info* fd_info); /* These must be declared in the loop handler, sslh-ev or sslh-select */ -void watchers_init(watchers** w); void watchers_add_read(watchers* w, int fd); void watchers_del_read(watchers* w, int fd); void watchers_add_write(watchers* w, int fd); diff --git a/sslh-select.c b/sslh-select.c index 33240bb..8056e2a 100644 --- a/sslh-select.c +++ b/sslh-select.c @@ -50,11 +50,17 @@ struct watchers { #include "processes.h" -void watchers_init(watchers** w) +static void watchers_init(watchers** w, struct listen_endpoint* listen_sockets, + int num_addr_listen) { *w = malloc(sizeof(**w)); FD_ZERO(&(*w)->fds_r); FD_ZERO(&(*w)->fds_w); + + for (int i = 0; i < num_addr_listen; i++) { + watchers_add_read(*w, listen_sockets[i].socketfd); + set_nonblock(listen_sockets[i].socketfd); + } } void watchers_add_read(watchers* w, int fd) @@ -170,12 +176,7 @@ void main_loop(struct listen_endpoint listen_sockets[], int num_addr_listen) fd_info.num_probing = 0; fd_info.probing_list = gap_init(0); - watchers_init(&fd_info.watchers); - - for (i = 0; i < num_addr_listen; i++) { - watchers_add_read(fd_info.watchers, listen_sockets[i].socketfd); - set_nonblock(listen_sockets[i].socketfd); - } + watchers_init(&fd_info.watchers, listen_sockets, num_addr_listen); fd_info.collection = collection_init(fd_info.watchers->max_fd); From 17313100b5a06e801bea6a82b9963089c0ff86af Mon Sep 17 00:00:00 2001 From: yrutschle Date: Mon, 1 Nov 2021 18:48:26 +0100 Subject: [PATCH 039/191] print cnx type when dumping it --- common.c | 1 + 1 file changed, 1 insertion(+) diff --git a/common.c b/common.c index 235b872..1be58e9 100644 --- a/common.c +++ b/common.c @@ -424,6 +424,7 @@ void init_cnx(struct connection *cnx) void dump_connection(struct connection *cnx) { + print_message(msg_int_error, "type: %s\n", cnx->type == SOCK_DGRAM ? "UDP" : "TCP"); print_message(msg_int_error, "state: %d\n", cnx->state); print_message(msg_int_error, "0: fd %d, %d deferred\n", cnx->q[0].fd, cnx->q[0].deferred_data_size); hexdump(msg_int_error, cnx->q[0].deferred_data, cnx->q[0].deferred_data_size); From e28fa91b0ffcbac0938747375e73bb411b9d1142 Mon Sep 17 00:00:00 2001 From: yrutschle Date: Sun, 7 Nov 2021 13:13:05 +0100 Subject: [PATCH 040/191] reap children properly --- echosrv.c | 1 + 1 file changed, 1 insertion(+) diff --git a/echosrv.c b/echosrv.c index 4f08e31..3e69d4c 100644 --- a/echosrv.c +++ b/echosrv.c @@ -112,6 +112,7 @@ void tcp_echo(struct listen_endpoint* listen_socket) exit(0); } close(in_socket); + waitpid(-1, NULL, WNOHANG); } } From a80e2ceb273007ec960d8d48dd74a91702e3f8cd Mon Sep 17 00:00:00 2001 From: yrutschle Date: Sun, 7 Nov 2021 13:13:41 +0100 Subject: [PATCH 041/191] remove obsolete declaration --- sslh-select.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/sslh-select.c b/sslh-select.c index 8056e2a..94b0582 100644 --- a/sslh-select.c +++ b/sslh-select.c @@ -36,6 +36,7 @@ #include "probe.h" #include "udp-listener.h" #include "collection.h" +#include "processes.h" #include "gap.h" #include "log.h" @@ -46,11 +47,9 @@ struct watchers { fd_set fds_r, fds_w; /* reference fd sets (used to init working copies) */ int max_fd; /* Highest fd number to pass to select() */ }; -#define WATCHERS_TYPE_DEFINED /* To notify processes.h */ -#include "processes.h" -static void watchers_init(watchers** w, struct listen_endpoint* listen_sockets, +static void watchers_init(watchers** w, struct listen_endpoint* listen_sockets, int num_addr_listen) { *w = malloc(sizeof(**w)); From 8ddff5e388ab4d57d894ec82aed9be4c6f073e32 Mon Sep 17 00:00:00 2001 From: yrutschle Date: Sun, 7 Nov 2021 16:13:44 +0100 Subject: [PATCH 042/191] sslh-ev working for TCP --- Makefile | 22 ++++---- sslh-ev.c | 164 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 176 insertions(+), 10 deletions(-) create mode 100644 sslh-ev.c diff --git a/Makefile b/Makefile index 45aba8c..e06d269 100644 --- a/Makefile +++ b/Makefile @@ -27,7 +27,10 @@ CC ?= gcc CFLAGS ?=-Wall -DLIBPCRE -g $(CFLAGS_COV) LIBS=-lm -lpcre2-8 -OBJS=sslh-conf.o common.o log.o sslh-main.o probe.o tls.o argtable3.o udp-listener.o collection.o gap.o processes.o +OBJS=sslh-conf.o common.o log.o sslh-main.o probe.o tls.o argtable3.o udp-listener.o collection.o gap.o +FORK_OBJS=sslh-fork.o $(OBJS) +SELECT_OBJS=sslh-select.o $(OBJS) processes.o +EV_OBJS=sslh-ev.o $(OBJS) processes.o CONDITIONAL_TARGETS= @@ -70,25 +73,24 @@ all: sslh $(MAN) echosrv $(CONDITIONAL_TARGETS) version.h: ./genver.sh >version.h -sslh: sslh-fork sslh-select # sslh-ev +sslh: sslh-fork sslh-select sslh-ev $(OBJS): version.h common.h collection.h sslh-conf.h gap.h sslh-conf.c sslh-conf.h: sslhconf.cfg conf2struct sslhconf.cfg -FORK_OBJS=sslh-conf.o common.o log.o sslh-main.o probe.o tls.o argtable3.o udp-listener.o collection.o gap.o -sslh-fork: version.h sslh-fork.o Makefile $(FORK_OBJS) - $(CC) $(CFLAGS) $(LDFLAGS) -o sslh-fork sslh-fork.o $(FORK_OBJS) $(LIBS) +sslh-fork: version.h Makefile $(FORK_OBJS) + $(CC) $(CFLAGS) $(LDFLAGS) -o sslh-fork $(FORK_OBJS) $(LIBS) #strip sslh-fork -sslh-select: version.h $(OBJS) sslh-select.o Makefile - $(CC) $(CFLAGS) $(LDFLAGS) -o sslh-select sslh-select.o $(OBJS) $(LIBS) +sslh-select: version.h $(SELECT_OBJS) Makefile + $(CC) $(CFLAGS) $(LDFLAGS) -o sslh-select $(SELECT_OBJS) $(LIBS) #strip sslh-select -sslh-ev: version.h $(OBJS) sslh-ev.o Makefile - $(CC) $(CFLAGS) $(LDFLAGS) -o sslh-ev sslh-ev.o $(OBJS) $(LIBS) - #strip sslh-select +sslh-ev: version.h $(EV_OBJS) Makefile + $(CC) $(CFLAGS) $(LDFLAGS) -o sslh-ev $(EV_OBJS) $(LIBS) -lev + #strip sslh-ev systemd-sslh-generator: systemd-sslh-generator.o $(CC) $(CFLAGS) $(LDFLAGS) -o systemd-sslh-generator systemd-sslh-generator.o -lconfig diff --git a/sslh-ev.c b/sslh-ev.c new file mode 100644 index 0000000..9e7e6f8 --- /dev/null +++ b/sslh-ev.c @@ -0,0 +1,164 @@ +/* + sslh-ev: mono-processus server based on libev + +# Copyright (C) 2021 Yves Rutschle +# +# This program is free software; you can redistribute it +# and/or modify it under the terms of the GNU General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later +# version. +# +# This program is distributed in the hope that it will be +# useful, but WITHOUT ANY WARRANTY; without even the implied +# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +# PURPOSE. See the GNU General Public License for more +# details. +# +# The full text for the General Public License is here: +# http://www.gnu.org/licenses/gpl.html + +*/ + +#include +#include +#include "processes.h" +#include "gap.h" +#include "log.h" + + +const char* server_type = "sslh-ev"; + +static struct ev_loop* loop; + +/* Libev watchers */ +struct watchers { + /* one set of ev_io for read, one for write, indexed by file descriptor */ + gap_array *ev_ior, *ev_iow; + + struct listen_endpoint* listen_sockets; + gap_array* fd2ls; /* Array indexed by file descriptor, pointing to listen_sockets */ + + int max_fd; /* legacy to be removed, still required for UDP */ +}; + +static void cnx_read_cb(EV_P_ ev_io *w, int revents); +static void cnx_write_cb(EV_P_ ev_io *w, int wevents); +static void cnx_accept_cb(EV_P_ ev_io *w, int revents); + + +static void watchers_init(watchers** w, struct listen_endpoint* listen_sockets, + int num_addr_listen) +{ + *w = malloc(sizeof(**w)); + (*w)->ev_ior = gap_init(num_addr_listen); + (*w)->ev_iow = gap_init(num_addr_listen); + (*w)->listen_sockets = listen_sockets; + (*w)->fd2ls = gap_init(0); + + /* Create watchers for listen sockets */ + for (int i = 0; i < num_addr_listen; i++) { + ev_io* io = malloc(sizeof(*io)); + + ev_io_init(io, &cnx_accept_cb, listen_sockets[i].socketfd, EV_READ); + ev_io_start(EV_A_ io); + gap_set((*w)->ev_ior, i, io); + gap_set((*w)->fd2ls, listen_sockets[i].socketfd, &listen_sockets[i]); + set_nonblock(listen_sockets[i].socketfd); + } +} + +void watchers_add_read(watchers* w, int fd) +{ + ev_io* io = gap_get(w->ev_ior, fd); + if (!io) { + io = malloc(sizeof(*io)); + ev_io_init(io, &cnx_read_cb, fd, EV_READ); + ev_io_set(io, fd, EV_READ); + + gap_set(w->ev_ior, fd, io); + } + ev_io_start(loop, io); + + if (fd > w->max_fd) w->max_fd = fd + 1; +} + +void watchers_del_read(watchers* w, int fd) +{ + ev_io* io = gap_get(w->ev_ior, fd); + if (io) ev_io_stop(EV_A_ io); +} + +void watchers_add_write(watchers* w, int fd) +{ + ev_io* io = gap_get(w->ev_iow, fd); + if (!io) { + io = malloc(sizeof(*io)); + ev_io_init(io, &cnx_write_cb, fd, EV_WRITE); + ev_io_set(io, fd, EV_WRITE); + + gap_set(w->ev_iow, fd, io); + } + ev_io_start(loop, io); + + if (fd > w->max_fd) w->max_fd = fd + 1; +} + +void watchers_del_write(watchers* w, int fd) +{ + ev_io* io = gap_get(w->ev_iow, fd); + if (io) ev_io_stop(EV_A_ io); +} + +/* To remove after moving UDP lookups to hash table */ +int watchers_maxfd(watchers* w) +{ + return w->max_fd; +} + +/* /watchers */ + +#include "processes.h" + +/* Libev callbacks */ +static void cnx_read_cb(EV_P_ ev_io *w, int revents) +{ + struct loop_info* info = ev_userdata(EV_A); + cnx_read_process(info, w->fd); +} + +static void cnx_write_cb(EV_P_ ev_io *w, int wevents) +{ + struct loop_info* info = ev_userdata(EV_A); + cnx_write_process(info, w->fd); +} + + +static void cnx_accept_cb(EV_P_ ev_io *w, int revents) +{ + struct loop_info* info = ev_userdata(EV_A); + cnx_accept_process(info, gap_get(info->watchers->fd2ls, w->fd)); +} + +void main_loop(struct listen_endpoint listen_sockets[], int num_addr_listen) +{ + struct loop_info ev_info = {0}; + loop = EV_DEFAULT; + + ev_info.collection = collection_init(0); + ev_info.probing_list = gap_init(0); + watchers_init(&ev_info.watchers, listen_sockets, num_addr_listen); + ev_set_userdata(EV_A_ &ev_info); + + + /* TODO: udp timeouts */ + + ev_run(EV_A_ 0); +} + +void start_shoveler(int listen_socket) { + print_message(msg_config_error, "inetd mode is not supported in libev mode\n"); + exit(1); +} + + From d4d9dbb8e78db7d0f2f7e3d6e17da148e5b56d32 Mon Sep 17 00:00:00 2001 From: yrutschle Date: Tue, 9 Nov 2021 18:12:02 +0100 Subject: [PATCH 043/191] remove dependancy to watcher type in UDP timeout --- sslh-select.c | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/sslh-select.c b/sslh-select.c index 94b0582..3bcba86 100644 --- a/sslh-select.c +++ b/sslh-select.c @@ -126,23 +126,21 @@ static void udp_timeouts(struct loop_info* fd_info) time_t next_timeout = INT_MAX; - for (int i = 0; i < fd_info->watchers->max_fd; i++) { + for (int i = 0; i < watchers_maxfd(fd_info->watchers); i++) { /* if it's either in read or write set, there is a connection * behind that file descriptor */ - if (FD_ISSET(i, &fd_info->watchers->fds_r) || FD_ISSET(i, &fd_info->watchers->fds_w)) { - struct connection* cnx = collection_get_cnx_from_fd(fd_info->collection, i); - if (cnx) { - time_t timeout = udp_timeout(cnx); - if (!timeout) continue; /* Not a UDP connection */ - if (cnx && (timeout <= now)) { - print_message(msg_fd, "timed out UDP %d\n", cnx->target_sock); - close(cnx->target_sock); - watchers_del_read(fd_info->watchers, i); - watchers_del_write(fd_info->watchers, i); - collection_remove_cnx(fd_info->collection, cnx); - } else { - if (timeout < next_timeout) next_timeout = timeout; - } + struct connection* cnx = collection_get_cnx_from_fd(fd_info->collection, i); + if (cnx) { + time_t timeout = udp_timeout(cnx); + if (!timeout) continue; /* Not a UDP connection */ + if (cnx && (timeout <= now)) { + print_message(msg_fd, "timed out UDP %d\n", cnx->target_sock); + close(cnx->target_sock); + watchers_del_read(fd_info->watchers, i); + watchers_del_write(fd_info->watchers, i); + collection_remove_cnx(fd_info->collection, cnx); + } else { + if (timeout < next_timeout) next_timeout = timeout; } } } From 207d4821894d9b300496ebdd4443de9fb3b353a1 Mon Sep 17 00:00:00 2001 From: yrutschle Date: Tue, 9 Nov 2021 18:27:52 +0100 Subject: [PATCH 044/191] refactor: move udp timeout management to udp module --- Makefile | 6 ++--- processes.c | 2 +- sslh-select.c | 41 --------------------------------- udp-listener.c | 61 +++++++++++++++++++++++++++++++++++++++++--------- udp-listener.h | 9 ++------ 5 files changed, 57 insertions(+), 62 deletions(-) diff --git a/Makefile b/Makefile index e06d269..d8ed217 100644 --- a/Makefile +++ b/Makefile @@ -27,10 +27,10 @@ CC ?= gcc CFLAGS ?=-Wall -DLIBPCRE -g $(CFLAGS_COV) LIBS=-lm -lpcre2-8 -OBJS=sslh-conf.o common.o log.o sslh-main.o probe.o tls.o argtable3.o udp-listener.o collection.o gap.o +OBJS=sslh-conf.o common.o log.o sslh-main.o probe.o tls.o argtable3.o collection.o gap.o FORK_OBJS=sslh-fork.o $(OBJS) -SELECT_OBJS=sslh-select.o $(OBJS) processes.o -EV_OBJS=sslh-ev.o $(OBJS) processes.o +SELECT_OBJS=sslh-select.o $(OBJS) processes.o udp-listener.o +EV_OBJS=sslh-ev.o $(OBJS) processes.o udp-listener.o CONDITIONAL_TARGETS= diff --git a/processes.c b/processes.c index 31b198c..ad79282 100644 --- a/processes.c +++ b/processes.c @@ -224,7 +224,7 @@ int cnx_accept_process(struct loop_info* fd_info, struct listen_endpoint* listen break; case SOCK_DGRAM: - new_fd = udp_c2s_forward(fd, fd_info->collection, watchers_maxfd(fd_info->watchers)); + new_fd = udp_c2s_forward(fd, fd_info); print_message(msg_fd, "new_fd %d\n", new_fd); if (new_fd == -1) return -1; diff --git a/sslh-select.c b/sslh-select.c index 3bcba86..c65847f 100644 --- a/sslh-select.c +++ b/sslh-select.c @@ -30,8 +30,6 @@ #define __LINUX__ -#include - #include "common.h" #include "probe.h" #include "udp-listener.h" @@ -113,41 +111,6 @@ static int fd_is_in_range(int fd) { -/* Check all connections to see if a UDP connections has timed out, then free - * it. At the same time, keep track of the closest, next timeout. Only do the - * search through connections if that timeout actually happened. If the - * connection that would have timed out has had activity, it doesn't matter: we - * go through connections to find the next timeout, which was needed anyway. */ -static void udp_timeouts(struct loop_info* fd_info) -{ - time_t now = time(NULL); - - if (now < fd_info->next_timeout) return; - - time_t next_timeout = INT_MAX; - - for (int i = 0; i < watchers_maxfd(fd_info->watchers); i++) { - /* if it's either in read or write set, there is a connection - * behind that file descriptor */ - struct connection* cnx = collection_get_cnx_from_fd(fd_info->collection, i); - if (cnx) { - time_t timeout = udp_timeout(cnx); - if (!timeout) continue; /* Not a UDP connection */ - if (cnx && (timeout <= now)) { - print_message(msg_fd, "timed out UDP %d\n", cnx->target_sock); - close(cnx->target_sock); - watchers_del_read(fd_info->watchers, i); - watchers_del_write(fd_info->watchers, i); - collection_remove_cnx(fd_info->collection, cnx); - } else { - if (timeout < next_timeout) next_timeout = timeout; - } - } - } - - if (next_timeout != INT_MAX) - fd_info->next_timeout = next_timeout; -} /* Main loop: the idea is as follow: * - fds_r and fds_w contain the file descriptors to monitor in read and write @@ -192,10 +155,6 @@ void main_loop(struct listen_endpoint listen_sockets[], int num_addr_listen) if (res < 0) perror("select"); - - /* UDP timeouts: clear out connections after some idle time */ - udp_timeouts(&fd_info); - /* Check main socket for new connections */ for (i = 0; i < num_addr_listen; i++) { if (FD_ISSET(listen_sockets[i].socketfd, &readfds)) { diff --git a/udp-listener.c b/udp-listener.c index 986a44c..d44cf59 100644 --- a/udp-listener.c +++ b/udp-listener.c @@ -20,12 +20,58 @@ */ +#include + #include "common.h" #include "probe.h" #include "sslh-conf.h" #include "udp-listener.h" +/* returns date at which this socket times out. */ +static int udp_timeout(struct connection* cnx) +{ + if (cnx->type != SOCK_DGRAM) return 0; /* Not a UDP connection */ + + return cnx->proto->udp_timeout + cnx->last_active; +} + +/* Check all connections to see if a UDP connections has timed out, then free + * it. At the same time, keep track of the closest, next timeout. Only do the + * search through connections if that timeout actually happened. If the + * connection that would have timed out has had activity, it doesn't matter: we + * go through connections to find the next timeout, which was needed anyway. */ +void udp_timeouts(struct loop_info* fd_info) +{ + time_t now = time(NULL); + + if (now < fd_info->next_timeout) return; + + time_t next_timeout = INT_MAX; + + for (int i = 0; i < watchers_maxfd(fd_info->watchers); i++) { + /* if it's either in read or write set, there is a connection + * behind that file descriptor */ + struct connection* cnx = collection_get_cnx_from_fd(fd_info->collection, i); + if (cnx) { + time_t timeout = udp_timeout(cnx); + if (!timeout) continue; /* Not a UDP connection */ + if (cnx && (timeout <= now)) { + print_message(msg_fd, "timed out UDP %d\n", cnx->target_sock); + close(cnx->target_sock); + watchers_del_read(fd_info->watchers, i); + watchers_del_write(fd_info->watchers, i); + collection_remove_cnx(fd_info->collection, cnx); + } else { + if (timeout < next_timeout) next_timeout = timeout; + } + } + } + + if (next_timeout != INT_MAX) + fd_info->next_timeout = next_timeout; +} + /* Find if the specified source has been seen before. -1 if not found * * TODO This is linear search and needs to be changed to something better for @@ -53,12 +99,14 @@ static int known_source(cnx_collection* collection, int max_fd, struct sockaddr* * Returns: >= 0 sockfd of newly allocated socket, for new connections * -1 otherwise * */ -int udp_c2s_forward(int sockfd, cnx_collection* collection, int max_fd) +int udp_c2s_forward(int sockfd, struct loop_info* fd_info) { char addr_str[NI_MAXHOST+1+NI_MAXSERV+1]; struct sockaddr src_addr; struct addrinfo addrinfo; struct sslhcfg_protocols_item* proto; + cnx_collection* collection = fd_info->collection; + int max_fd = watchers_maxfd(fd_info->watchers); struct connection* cnx; ssize_t len; socklen_t addrlen; @@ -67,6 +115,8 @@ int udp_c2s_forward(int sockfd, cnx_collection* collection, int max_fd) This will do. Dynamic allocation is possible with the MSG_PEEK flag in recvfrom(2), but that'd imply malloc/free overhead for each packet, when really 64K is not that much */ + udp_timeouts(fd_info); + addrlen = sizeof(src_addr); len = recvfrom(sockfd, data, sizeof(data), 0, &src_addr, &addrlen); if (len < 0) { @@ -125,12 +175,3 @@ void udp_s2c_forward(struct connection* cnx) cnx->last_active = time(NULL); } - -/* returns date at which this socket times out. */ -int udp_timeout(struct connection* cnx) -{ - if (cnx->type != SOCK_DGRAM) return 0; /* Not a UDP connection */ - - return cnx->proto->udp_timeout + cnx->last_active; -} - diff --git a/udp-listener.h b/udp-listener.h index 9439846..23a62dc 100644 --- a/udp-listener.h +++ b/udp-listener.h @@ -2,6 +2,7 @@ #define UDPLISTENER_H #include "collection.h" +#include "processes.h" #include "common.h" /* UDP listener: upon incoming packet, find where it should go @@ -15,15 +16,9 @@ void udp_listener(struct listen_endpoint* endpoint, int num_endpoints, int activ * Returns: >= 0 sockfd of newly allocated socket, for new connections * -1 otherwise * */ -int udp_c2s_forward(int sockfd, cnx_collection* collection, int max_fd); +int udp_c2s_forward(int sockfd, struct loop_info* fd_info); /* Process UDP coming from inside (server towards client) */ void udp_s2c_forward(struct connection* cnx); - -/* returns how many seconds before socket times out. Negative if timed out - * already. - */ -int udp_timeout(struct connection* cnx); - #endif /* UDPLISTENER_H */ From 0a23ca133e0fc0712912401776fc362052a11075 Mon Sep 17 00:00:00 2001 From: yrutschle Date: Thu, 11 Nov 2021 21:16:37 +0100 Subject: [PATCH 045/191] finalise UDP support for sslh-ev --- sslh-ev.c | 3 --- udp-listener.c | 14 +++++++++++++- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/sslh-ev.c b/sslh-ev.c index 9e7e6f8..8410730 100644 --- a/sslh-ev.c +++ b/sslh-ev.c @@ -150,9 +150,6 @@ void main_loop(struct listen_endpoint listen_sockets[], int num_addr_listen) watchers_init(&ev_info.watchers, listen_sockets, num_addr_listen); ev_set_userdata(EV_A_ &ev_info); - - /* TODO: udp timeouts */ - ev_run(EV_A_ 0); } diff --git a/udp-listener.c b/udp-listener.c index d44cf59..ea5d4f4 100644 --- a/udp-listener.c +++ b/udp-listener.c @@ -40,7 +40,16 @@ static int udp_timeout(struct connection* cnx) * it. At the same time, keep track of the closest, next timeout. Only do the * search through connections if that timeout actually happened. If the * connection that would have timed out has had activity, it doesn't matter: we - * go through connections to find the next timeout, which was needed anyway. */ + * go through connections to find the next timeout, which was needed anyway. + * + * This gets called every time a UDP packet is received from the outside, i.e. + * every time we might need to free up resources. If no packets come in, we + * don't time out anything, as we don't need the resources. + * + * TODO: use a better algorithm to avoid going through all connections each + * time. + * + * */ void udp_timeouts(struct loop_info* fd_info) { time_t now = time(NULL); @@ -140,6 +149,8 @@ int udp_c2s_forward(int sockfd, struct loop_info* fd_info) } out = socket(proto->saddr->ai_family, SOCK_DGRAM, 0); + res = set_nonblock(out); + CHECK_RES_RETURN(res, "udp:socket:nonblock", -1); struct connection* cnx = collection_alloc_cnx_from_fd(collection, out); if (!cnx) return -1; target = out; @@ -169,6 +180,7 @@ void udp_s2c_forward(struct connection* cnx) int res; res = recvfrom(sockfd, data, sizeof(data), 0, NULL, NULL); + if ((res == -1) && ((errno == EAGAIN) || (errno == EWOULDBLOCK))) return; CHECK_RES_DIE(res, "udp_listener/recvfrom"); res = sendto(cnx->local_endpoint, data, res, 0, &cnx->client_addr, cnx->addrlen); From ae117097eae52b14d9bfc7b8ed0e5638d85c3f28 Mon Sep 17 00:00:00 2001 From: yrutschle Date: Fri, 12 Nov 2021 09:03:30 +0100 Subject: [PATCH 046/191] sslh-ev in ChangeLog --- ChangeLog | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ChangeLog b/ChangeLog index e75849a..5ce8d6b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,9 @@ vNEXT: + New sslh-ev: this is functionaly equivalent to + sslh-select (mono-process, only forks for specified + protocols), but based on libev, which should make it + scalable to large numbers of connections. + New log system: instead of --verbose with arbitrary levels, there are now several message classes. Each message class can be set to go to stderr, syslog, or From 44a78bc5151951a1c1783069c84cb5262382a833 Mon Sep 17 00:00:00 2001 From: Yves Rutschle Date: Sun, 14 Nov 2021 18:58:59 +0000 Subject: [PATCH 047/191] document dependency on libev --- doc/INSTALL.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/INSTALL.md b/doc/INSTALL.md index b06859e..7b1b87c 100644 --- a/doc/INSTALL.md +++ b/doc/INSTALL.md @@ -23,6 +23,8 @@ Makefile. * [libcap](http://packages.debian.org/source/unstable/libcap-dev), in package `libcap-dev`. You can compile with or without it using USELIBCAP in the Makefile +* [libev](http://software.schmorp.de/pkg/libev.html), in package `libev-dev`. It is only required to build `sslh-ev`, so you don't need if you only build `sslh-fork` and/or `sslh-select`. + * libbsd, to enable to change the process name (as shown in `ps`, so each forked process shows what protocol and what connection it is serving), From 93df6975edba00c00e293842d47e3844f4389c4b Mon Sep 17 00:00:00 2001 From: yrutschle Date: Sat, 20 Nov 2021 17:00:24 +0100 Subject: [PATCH 048/191] set appropriate log level for SNI matching --- tls.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tls.c b/tls.c index 9d56edb..4933bb3 100644 --- a/tls.c +++ b/tls.c @@ -277,7 +277,7 @@ has_match(const char** list, size_t list_len, const char* name, size_t name_len) for (i = 0; i < list_len; i++) { item = &list[i]; - print_message(msg_probe_error, "matching [%.*s] with [%s]\n", (int)name_len, name, *item); + print_message(msg_probe_info, "matching [%.*s] with [%s]\n", (int)name_len, name, *item); if(!fnmatch(*item, name_nullterminated, 0)) { free(name_nullterminated); return 1; From 80f2d758a4971e92006c3a43da70726c84779742 Mon Sep 17 00:00:00 2001 From: Belisarith Date: Tue, 23 Nov 2021 12:40:17 +0000 Subject: [PATCH 049/191] Correct wrong version of pcre in Dockerfile --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index cb8de3a..69710eb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,7 +8,7 @@ RUN \ libconfig-dev \ make \ musl-dev \ - pcre-dev \ + pcre2-dev \ perl && \ cd /sslh && \ make sslh-select && \ From 18eeaa579afa3032bb14b094558f457a1ebf046b Mon Sep 17 00:00:00 2001 From: Belisarith Date: Tue, 23 Nov 2021 12:57:16 +0000 Subject: [PATCH 050/191] additional fix dockerfile, otherwise docker is not runnable --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 69710eb..156075d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,7 +8,7 @@ RUN \ libconfig-dev \ make \ musl-dev \ - pcre2-dev \ + pcre2-dev \ perl && \ cd /sslh && \ make sslh-select && \ @@ -18,6 +18,6 @@ FROM alpine:latest COPY --from=build /sslh/sslh-select /sslh -RUN apk --no-cache add libconfig pcre +RUN apk --no-cache add libconfig pcre2 ENTRYPOINT [ "/sslh", "--foreground"] From adaf407b8ac2bafd8fbc9333e4026246a2864e82 Mon Sep 17 00:00:00 2001 From: yrutschle Date: Thu, 9 Dec 2021 21:29:48 +0100 Subject: [PATCH 051/191] added sslh-ev to install docs --- doc/INSTALL.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/INSTALL.md b/doc/INSTALL.md index 7b1b87c..e174cc6 100644 --- a/doc/INSTALL.md +++ b/doc/INSTALL.md @@ -73,8 +73,8 @@ of the Makefile: Binaries -------- -The Makefile produces two different executables: `sslh-fork` -and `sslh-select`: +The Makefile produces three different executables: `sslh-fork`, +`sslh-select` and `sslh-ev`: * `sslh-fork` forks a new process for each incoming connection. It is well-tested and very reliable, but incurs the overhead @@ -91,10 +91,10 @@ If you are going to use `sslh` on a "medium" setup (a few thousand ssh connections, and another few thousand ssl connections), `sslh-select` will be better. -If you have a very large site (tens of thousands of connections), -you'll need a vapourware version that would use libevent or -something like that. - +* `sslh-ev` works very much like `sslh-select`, but uses + `libev` as a backend. `libev` provides more portability +and alternative ways to support very large numbers of +connections. Installation ------------ From 122649ce3cc828114f68c55a375a7c96a1a8e2dd Mon Sep 17 00:00:00 2001 From: yrutschle Date: Sun, 12 Dec 2021 09:30:07 +0100 Subject: [PATCH 052/191] fix log level for unknown ALPN (fix #313) --- tls.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tls.c b/tls.c index 4933bb3..fb088ed 100644 --- a/tls.c +++ b/tls.c @@ -255,7 +255,7 @@ parse_alpn_extension(const struct TLSProtocol *tls_data, const char *data, size_ if (len > 0 && has_match(tls_data->alpn_protocol_list, tls_data->alpn_list_len, data + pos + 1, len)) { return len; } else if (len > 0) { - print_message(msg_probe_error, "Unknown ALPN name: %.*s\n", (int)len, data + pos + 1); + print_message(msg_probe_info, "Not in ALPN list: %.*s\n", (int)len, data + pos + 1); } pos += 1 + len; } From 58ed1853162a630ab6a918c222b7b7caef15e59d Mon Sep 17 00:00:00 2001 From: yrutschle Date: Sun, 19 Dec 2021 19:30:21 +0100 Subject: [PATCH 053/191] missing header dependency --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index d8ed217..2d9a988 100644 --- a/Makefile +++ b/Makefile @@ -75,7 +75,7 @@ version.h: sslh: sslh-fork sslh-select sslh-ev -$(OBJS): version.h common.h collection.h sslh-conf.h gap.h +$(OBJS): version.h common.h collection.h sslh-conf.h gap.h processes.h sslh-conf.c sslh-conf.h: sslhconf.cfg conf2struct sslhconf.cfg From 4e6145576e247e02f8d164fc4535d384572eecfb Mon Sep 17 00:00:00 2001 From: yrutschle Date: Thu, 23 Dec 2021 21:56:31 +0100 Subject: [PATCH 054/191] warn systemd that sslh will fork (fix #314) --- scripts/systemd.sslh.service | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/systemd.sslh.service b/scripts/systemd.sslh.service index a6a6bb9..9dcd49c 100644 --- a/scripts/systemd.sslh.service +++ b/scripts/systemd.sslh.service @@ -22,6 +22,7 @@ PrivateDevices=true RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX MemoryDenyWriteExecute=true DynamicUser=true +Type=forking [Install] WantedBy=multi-user.target From 6f497b6c8b96f4b83bce018bcc0b306128ad1f97 Mon Sep 17 00:00:00 2001 From: jeffre Date: Thu, 20 Jan 2022 20:21:12 -0700 Subject: [PATCH 055/191] docs: corrections to docker usage --- README.md | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 2c391fa..e0ff815 100644 --- a/README.md +++ b/README.md @@ -50,19 +50,18 @@ Build docker image ```bash -docker container run \ +docker run \ --rm \ -it \ + sslh:latest \ --listen=0.0.0.0:443 \ --ssh=hostname:22 \ - --tlshostname:443 \ - sslh:latest + --tls=hostname:443 ``` docker-compose example ``` ---- version: "3" services: @@ -70,19 +69,17 @@ services: image: sslh:latest hostname: sslh ports: - - 443:443/tcp - command: --listen=0.0.0.0:443 --tlshostname:443 --openvpn=openvpn:1194 + - 443:443 + command: --listen=0.0.0.0:443 --tls=nginx:443 --openvpn=openvpn:1194 depends_on: - nginx - openvpn nginx: image: nginx - hostname: nginx openvpn: - image: openvpn:latest - hostname: openvpn + image: openvpn ``` Comments? Questions? From 66f4b181218eac66d7cdfec33094d93bdf03ddfb Mon Sep 17 00:00:00 2001 From: yrutschle Date: Thu, 17 Mar 2022 21:51:01 +0100 Subject: [PATCH 056/191] ensure conf2struct is called early in the make --- Makefile | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 2d9a988..1381e33 100644 --- a/Makefile +++ b/Makefile @@ -27,9 +27,13 @@ CC ?= gcc CFLAGS ?=-Wall -DLIBPCRE -g $(CFLAGS_COV) LIBS=-lm -lpcre2-8 + +# making sslh-conf.o also makes sslh-conf.h with +# conf2struct, which may be required by other headers: it +# should be kept first OBJS=sslh-conf.o common.o log.o sslh-main.o probe.o tls.o argtable3.o collection.o gap.o -FORK_OBJS=sslh-fork.o $(OBJS) -SELECT_OBJS=sslh-select.o $(OBJS) processes.o udp-listener.o +FORK_OBJS=$(OBJS) sslh-fork.o +SELECT_OBJS=$(OBJS) sslh-select.o processes.o udp-listener.o EV_OBJS=sslh-ev.o $(OBJS) processes.o udp-listener.o CONDITIONAL_TARGETS= From 91d148f66c50e49cb98299711e02da8354542506 Mon Sep 17 00:00:00 2001 From: yrutschle Date: Thu, 17 Mar 2022 21:51:54 +0100 Subject: [PATCH 057/191] removed obsolete resolve_name function --- common.c | 30 ------------------------------ 1 file changed, 30 deletions(-) diff --git a/common.c b/common.c index 1be58e9..0e48cdf 100644 --- a/common.c +++ b/common.c @@ -554,36 +554,6 @@ int resolve_split_name(struct addrinfo **out, char* host, char* serv) return res; } -/* turns a "hostname:port" string into a list of struct addrinfo; -out: list of newly allocated addrinfo (see getaddrinfo(3)); freeaddrinfo(3) when done -fullname: input string -- it gets clobbered -*/ -void resolve_name(struct addrinfo **out, char* fullname) -{ - char *serv, *host; - int res; - - /* Find port */ - char *sep = strrchr(fullname, ':'); - if (!sep) { /* No separator: parameter is just a port */ - print_message(msg_config_error, "%s: names must be fully specified as hostname:port\n", fullname); - exit(1); - } - serv = sep+1; - *sep = 0; - - host = fullname; - - res = resolve_split_name(out, host, serv); - if (res) { - print_message(msg_config_error, "%s `%s'\n", gai_strerror(res), fullname); - if (res == EAI_SERVICE) - print_message(msg_config_error, "(Check you have specified all ports)\n"); - exit(4); - } -} - - /* Fills a connection description; returns 0 on failure */ int get_connection_desc(struct connection_desc* desc, const struct connection *cnx) { From 875fa488c9e70879429c2965127956ce3c54b456 Mon Sep 17 00:00:00 2001 From: Paul Schroeder Date: Fri, 18 Mar 2022 17:59:29 +0100 Subject: [PATCH 058/191] add option and description Signed-off-by: Paul Schroeder --- example.cfg | 2 ++ sslhconf.cfg | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/example.cfg b/example.cfg index 2542420..76383ca 100644 --- a/example.cfg +++ b/example.cfg @@ -60,6 +60,8 @@ listen: # fork: Should a new process be forked for this protocol? # (only useful for sslh-select) # tfo_ok: Set to true if the server supports TCP FAST OPEN +# resolve_on_forward: Set to true if server address should be resolved on +# (every) incoming connection (again) # transparent: Set to true to proxy this protocol # transparently (server sees the remote client IP # address). Same as the global option, but per-protocol diff --git a/sslhconf.cfg b/sslhconf.cfg index a0ad4ec..776d98f 100644 --- a/sslhconf.cfg +++ b/sslhconf.cfg @@ -108,8 +108,10 @@ config: { { name: "fork"; type: "bool"; default: false }, { name: "tfo_ok"; type: "bool"; default: false; description: "Set to true if this protocol supports TCP FAST OPEN" }, - { name: "transparent"; type: "bool"; default: false; + { name: "transparent"; type: "bool"; default: false; description: "Set to proxy this protocol transparently" }, + { name: "resolve_on_forward"; type: "bool"; default: false; + description: "Set to true if server address should be resolved on (every) incoming connection (again)" }, { name: "log_level"; type: "int"; default: 1 }, { name: "keepalive"; type: "bool"; default: false }, { name: "sni_hostnames", From 87577ae5f694aaad8513fb268d100de3e15b6f98 Mon Sep 17 00:00:00 2001 From: Paul Schroeder Date: Fri, 18 Mar 2022 17:59:54 +0100 Subject: [PATCH 059/191] add functionality Signed-off-by: Paul Schroeder --- common.c | 5 +++++ sslh-main.c | 9 +++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/common.c b/common.c index 1be58e9..b3251d8 100644 --- a/common.c +++ b/common.c @@ -319,6 +319,11 @@ int connect_addr(struct connection *cnx, int fd_from, connect_blocking blocking) res = getpeername(fd_from, from.ai_addr, &from.ai_addrlen); CHECK_RES_RETURN(res, "getpeername", res); + if (cfg.protocols.resolve_on_forward) { + resolve_split_name(&(cnx->proto->saddr), cnx->proto->host, + cnx->proto->port); + } + for (a = cnx->proto->saddr; a; a = a->ai_next) { /* When transparent, make sure both connections use the same address family */ if (transparent && a->ai_family != from.ai_addr->sa_family) diff --git a/sslh-main.c b/sslh-main.c index 9f326a5..3fe16d9 100644 --- a/sslh-main.c +++ b/sslh-main.c @@ -124,8 +124,13 @@ static void config_protocols() int i; for (i = 0; i < cfg.protocols_len; i++) { struct sslhcfg_protocols_item* p = &(cfg.protocols[i]); - if (resolve_split_name(&(p->saddr), p->host, p->port)) { - print_message(msg_config_error, "cannot resolve %s:%s\n", p->host, p->port); + + if ( + !cfg.protocols.resolve_on_forward && + resolve_split_name(&(p->saddr), p->host, p->port) + ) { + print_message(msg_config_error, "cannot resolve %s:%s\n", + p->host, p->port); exit(4); } From 1e0578c082483df15e9fc141e5de72ea8d36da71 Mon Sep 17 00:00:00 2001 From: yrutschle Date: Fri, 18 Mar 2022 18:02:32 +0100 Subject: [PATCH 060/191] don't log to syslog when testing --- Dockerfile | 4 +-- Makefile | 10 ++----- common.c | 30 ++++++++++++++++++++ common.h | 55 +++++++++++++++++++----------------- doc/INSTALL.md | 14 ++++----- scripts/systemd.sslh.service | 1 - systemd-sslh-generator.c | 8 +----- test.cfg | 26 ++++++++--------- tls.c | 4 +-- 9 files changed, 86 insertions(+), 66 deletions(-) diff --git a/Dockerfile b/Dockerfile index 156075d..cb8de3a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,7 +8,7 @@ RUN \ libconfig-dev \ make \ musl-dev \ - pcre2-dev \ + pcre-dev \ perl && \ cd /sslh && \ make sslh-select && \ @@ -18,6 +18,6 @@ FROM alpine:latest COPY --from=build /sslh/sslh-select /sslh -RUN apk --no-cache add libconfig pcre2 +RUN apk --no-cache add libconfig pcre ENTRYPOINT [ "/sslh", "--foreground"] diff --git a/Makefile b/Makefile index 1381e33..d8ed217 100644 --- a/Makefile +++ b/Makefile @@ -27,13 +27,9 @@ CC ?= gcc CFLAGS ?=-Wall -DLIBPCRE -g $(CFLAGS_COV) LIBS=-lm -lpcre2-8 - -# making sslh-conf.o also makes sslh-conf.h with -# conf2struct, which may be required by other headers: it -# should be kept first OBJS=sslh-conf.o common.o log.o sslh-main.o probe.o tls.o argtable3.o collection.o gap.o -FORK_OBJS=$(OBJS) sslh-fork.o -SELECT_OBJS=$(OBJS) sslh-select.o processes.o udp-listener.o +FORK_OBJS=sslh-fork.o $(OBJS) +SELECT_OBJS=sslh-select.o $(OBJS) processes.o udp-listener.o EV_OBJS=sslh-ev.o $(OBJS) processes.o udp-listener.o CONDITIONAL_TARGETS= @@ -79,7 +75,7 @@ version.h: sslh: sslh-fork sslh-select sslh-ev -$(OBJS): version.h common.h collection.h sslh-conf.h gap.h processes.h +$(OBJS): version.h common.h collection.h sslh-conf.h gap.h sslh-conf.c sslh-conf.h: sslhconf.cfg conf2struct sslhconf.cfg diff --git a/common.c b/common.c index 0e48cdf..1be58e9 100644 --- a/common.c +++ b/common.c @@ -554,6 +554,36 @@ int resolve_split_name(struct addrinfo **out, char* host, char* serv) return res; } +/* turns a "hostname:port" string into a list of struct addrinfo; +out: list of newly allocated addrinfo (see getaddrinfo(3)); freeaddrinfo(3) when done +fullname: input string -- it gets clobbered +*/ +void resolve_name(struct addrinfo **out, char* fullname) +{ + char *serv, *host; + int res; + + /* Find port */ + char *sep = strrchr(fullname, ':'); + if (!sep) { /* No separator: parameter is just a port */ + print_message(msg_config_error, "%s: names must be fully specified as hostname:port\n", fullname); + exit(1); + } + serv = sep+1; + *sep = 0; + + host = fullname; + + res = resolve_split_name(out, host, serv); + if (res) { + print_message(msg_config_error, "%s `%s'\n", gai_strerror(res), fullname); + if (res == EAI_SERVICE) + print_message(msg_config_error, "(Check you have specified all ports)\n"); + exit(4); + } +} + + /* Fills a connection description; returns 0 on failure */ int get_connection_desc(struct connection_desc* desc, const struct connection *cnx) { diff --git a/common.h b/common.h index 424236f..ee53dd1 100644 --- a/common.h +++ b/common.h @@ -35,6 +35,30 @@ #include "version.h" +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) + + +#define CHECK_RES_DIE(res, str) \ + if (res == -1) { \ + print_message(msg_system_error, "%s:%d:", __FILE__, __LINE__); \ + perror(str); \ + exit(1); \ + } + +#define CHECK_RES_RETURN(res, str, ret) \ + if (res == -1) { \ + print_message(msg_system_error, "%s:%d:%s:%d:%s\n", __FILE__, __LINE__, str, errno, strerror(errno)); \ + return ret; \ + } + +#define CHECK_ALLOC(a, str) \ + if (!a) { \ + print_message(msg_system_error, "%s:%d:", __FILE__, __LINE__); \ + perror(str); \ + exit(1); \ + } + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) #if 1 #define TRACE fprintf(stderr, "%s:%d\n", __FILE__, __LINE__); @@ -59,6 +83,9 @@ enum connection_state { ST_SHOVELING /* Connexion is established */ }; +/* this is used to pass protocols through the command-line parameter parsing */ +#define PROT_SHIFT 1000 /* protocol options will be 1000, 1001, etc */ + /* A 'queue' is composed of a file descriptor (which can be read from or * written to), and a queue for deferred write data */ struct queue { @@ -116,7 +143,6 @@ typedef enum { BLOCKING = 1 } connect_blocking; -#include "log.h" /* common.c */ void init_cnx(struct connection *cnx); @@ -126,9 +152,11 @@ int fd2fd(struct queue *target, struct queue *from); char* sprintaddr(char* buf, size_t size, struct addrinfo *a); void resolve_name(struct addrinfo **out, char* fullname); int get_connection_desc(struct connection_desc* desc, const struct connection *cnx); +void log_connection(struct connection_desc* desc, const struct connection *cnx); void set_proctitle_shovel(struct connection_desc* desc, const struct connection *cnx); int check_access_rights(int in_socket, const char* service); void setup_signals(void); +void setup_syslog(const char* bin_name); void drop_privileges(const char* user_name, const char* chroot_path); void set_capabilities(int cap_net_admin); void write_pid_file(const char* pidfile); @@ -149,29 +177,4 @@ void start_shoveler(int); void main_loop(struct listen_endpoint *listen_sockets, int num_addr_listen); -#define MAX(a, b) (((a) > (b)) ? (a) : (b)) - - -#define CHECK_RES_DIE(res, str) \ - if (res == -1) { \ - print_message(msg_system_error, "%s:%d:", __FILE__, __LINE__); \ - perror(str); \ - exit(1); \ - } - -#define CHECK_RES_RETURN(res, str, ret) \ - if (res == -1) { \ - print_message(msg_system_error, "%s:%d:%s:%d:%s\n", __FILE__, __LINE__, str, errno, strerror(errno)); \ - return ret; \ - } - -#define CHECK_ALLOC(a, str) \ - if (!a) { \ - print_message(msg_system_error, "%s:%d:", __FILE__, __LINE__); \ - perror(str); \ - exit(1); \ - } - -#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) - #endif diff --git a/doc/INSTALL.md b/doc/INSTALL.md index e174cc6..b06859e 100644 --- a/doc/INSTALL.md +++ b/doc/INSTALL.md @@ -23,8 +23,6 @@ Makefile. * [libcap](http://packages.debian.org/source/unstable/libcap-dev), in package `libcap-dev`. You can compile with or without it using USELIBCAP in the Makefile -* [libev](http://software.schmorp.de/pkg/libev.html), in package `libev-dev`. It is only required to build `sslh-ev`, so you don't need if you only build `sslh-fork` and/or `sslh-select`. - * libbsd, to enable to change the process name (as shown in `ps`, so each forked process shows what protocol and what connection it is serving), @@ -73,8 +71,8 @@ of the Makefile: Binaries -------- -The Makefile produces three different executables: `sslh-fork`, -`sslh-select` and `sslh-ev`: +The Makefile produces two different executables: `sslh-fork` +and `sslh-select`: * `sslh-fork` forks a new process for each incoming connection. It is well-tested and very reliable, but incurs the overhead @@ -91,10 +89,10 @@ If you are going to use `sslh` on a "medium" setup (a few thousand ssh connections, and another few thousand ssl connections), `sslh-select` will be better. -* `sslh-ev` works very much like `sslh-select`, but uses - `libev` as a backend. `libev` provides more portability -and alternative ways to support very large numbers of -connections. +If you have a very large site (tens of thousands of connections), +you'll need a vapourware version that would use libevent or +something like that. + Installation ------------ diff --git a/scripts/systemd.sslh.service b/scripts/systemd.sslh.service index 9dcd49c..a6a6bb9 100644 --- a/scripts/systemd.sslh.service +++ b/scripts/systemd.sslh.service @@ -22,7 +22,6 @@ PrivateDevices=true RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX MemoryDenyWriteExecute=true DynamicUser=true -Type=forking [Install] WantedBy=multi-user.target diff --git a/systemd-sslh-generator.c b/systemd-sslh-generator.c index 6d6a44f..315e207 100644 --- a/systemd-sslh-generator.c +++ b/systemd-sslh-generator.c @@ -2,13 +2,7 @@ #include #include #include - -#define CHECK_ALLOC(a, str) \ - if (!a) { \ - fprintf(stderr, "%s:%d:", __FILE__, __LINE__); \ - perror(str); \ - exit(1); \ - } +#include "common.h" static char* resolve_listen(const char *hostname, const char *port) { diff --git a/test.cfg b/test.cfg index 2bb0edc..c8354c3 100644 --- a/test.cfg +++ b/test.cfg @@ -14,17 +14,17 @@ syslog_facility: "auth"; # Value: 1: stdout; 2: syslog; 3: both # Defaults should be sensible. Generally, you want *-error # to be always enabled, to know if something is going wrong. -verbose-config: 3; # print configuration at startup -verbose-config-error: 3; # print configuration errors -verbose-connections: 3; # trace established incoming address to forward address -verbose-connections-error: 3; # connection errors -verbose-connections-try: 3; # connection attempts towards targets -verbose-fd: 3; # file descriptor activity, open/close/whatnot -verbose-packets: 3; # hexdump packets on which probing is done -verbose-probe-info: 3; # what's happening during the probe process -verbose-probe-error: 3; # failures and problems during probing -verbose-system-error: 3; # system call problem, i.e. malloc, fork, failing -verbose-int-error: 3; # internal errors, the kind that should never happen +verbose-config: 1; # print configuration at startup +verbose-config-error: 1; # print configuration errors +verbose-connections: 1; # trace established incoming address to forward address +verbose-connections-error: 1; # connection errors +verbose-connections-try: 1; # connection attempts towards targets +verbose-fd: 1; # file descriptor activity, open/close/whatnot +verbose-packets: 1; # hexdump packets on which probing is done +verbose-probe-info: 1; # what's happening during the probe process +verbose-probe-error: 1; # failures and problems during probing +verbose-system-error: 1; # system call problem, i.e. malloc, fork, failing +verbose-int-error: 1; # internal errors, the kind that should never happen # List of interfaces on which we should listen # Options: @@ -32,7 +32,7 @@ listen: ( { host: "localhost"; port: "8080"; keepalive: true; }, { host: "localhost"; port: "8081"; keepalive: true; }, - { host: "127.0.0.1"; is_udp: true; port: "8086"; } + { host: "ip4-localhost"; is_udp: true; port: "8086"; } ); @@ -46,7 +46,7 @@ protocols: { name: "xmpp"; host: "localhost"; port: "9009"; }, { name: "adb"; host: "localhost"; port: "9010"; }, { name: "syslog"; host: "localhost"; port: "9013"; }, - { name: "ssh"; host: "127.0.0.1"; is_udp: true; port: "9020"; + { name: "regex"; host: "ip4-localhost"; is_udp: true; port: "9020"; udp_timeout: 30; regex_patterns: [ "^foo" ]; }, diff --git a/tls.c b/tls.c index fb088ed..9d56edb 100644 --- a/tls.c +++ b/tls.c @@ -255,7 +255,7 @@ parse_alpn_extension(const struct TLSProtocol *tls_data, const char *data, size_ if (len > 0 && has_match(tls_data->alpn_protocol_list, tls_data->alpn_list_len, data + pos + 1, len)) { return len; } else if (len > 0) { - print_message(msg_probe_info, "Not in ALPN list: %.*s\n", (int)len, data + pos + 1); + print_message(msg_probe_error, "Unknown ALPN name: %.*s\n", (int)len, data + pos + 1); } pos += 1 + len; } @@ -277,7 +277,7 @@ has_match(const char** list, size_t list_len, const char* name, size_t name_len) for (i = 0; i < list_len; i++) { item = &list[i]; - print_message(msg_probe_info, "matching [%.*s] with [%s]\n", (int)name_len, name, *item); + print_message(msg_probe_error, "matching [%.*s] with [%s]\n", (int)name_len, name, *item); if(!fnmatch(*item, name_nullterminated, 0)) { free(name_nullterminated); return 1; From d922086f539f38fe623721d7863f2fefa842fb55 Mon Sep 17 00:00:00 2001 From: yrutschle Date: Fri, 18 Mar 2022 18:03:18 +0100 Subject: [PATCH 061/191] handle accept failure properly (fix #322) --- processes.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/processes.c b/processes.c index ad79282..8886e14 100644 --- a/processes.c +++ b/processes.c @@ -217,10 +217,10 @@ int cnx_accept_process(struct loop_info* fd_info, struct listen_endpoint* listen case SOCK_STREAM: cnx = accept_new_connection(fd, fd_info->collection); - if (cnx) { - add_probing_cnx(fd_info, cnx); - new_fd = cnx->q[0].fd; - } + if (!cnx) return -1; + + add_probing_cnx(fd_info, cnx); + new_fd = cnx->q[0].fd; break; case SOCK_DGRAM: From 3f5c81d2f605595e0e501953940f8b1fcc8e5fe1 Mon Sep 17 00:00:00 2001 From: Paul Schroeder Date: Fri, 18 Mar 2022 18:03:58 +0100 Subject: [PATCH 062/191] be more clearly Signed-off-by: Paul Schroeder --- example.cfg | 2 +- sslhconf.cfg | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/example.cfg b/example.cfg index 76383ca..6f0585f 100644 --- a/example.cfg +++ b/example.cfg @@ -61,7 +61,7 @@ listen: # (only useful for sslh-select) # tfo_ok: Set to true if the server supports TCP FAST OPEN # resolve_on_forward: Set to true if server address should be resolved on -# (every) incoming connection (again) +# (every) newly incoming connection (again) # transparent: Set to true to proxy this protocol # transparently (server sees the remote client IP # address). Same as the global option, but per-protocol diff --git a/sslhconf.cfg b/sslhconf.cfg index 776d98f..f0aca35 100644 --- a/sslhconf.cfg +++ b/sslhconf.cfg @@ -111,7 +111,7 @@ config: { { name: "transparent"; type: "bool"; default: false; description: "Set to proxy this protocol transparently" }, { name: "resolve_on_forward"; type: "bool"; default: false; - description: "Set to true if server address should be resolved on (every) incoming connection (again)" }, + description: "Set to true if server address should be resolved on (every) newly incoming connection (again)" }, { name: "log_level"; type: "int"; default: 1 }, { name: "keepalive"; type: "bool"; default: false }, { name: "sni_hostnames", From 78bc954769ed1732dd3a79778f99ccbac22b01af Mon Sep 17 00:00:00 2001 From: Paul Schroeder Date: Sat, 19 Mar 2022 23:18:29 +0100 Subject: [PATCH 063/191] review Signed-off-by: Paul Schroeder --- common.c | 2 +- sslh-main.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/common.c b/common.c index b3251d8..a9916d9 100644 --- a/common.c +++ b/common.c @@ -319,7 +319,7 @@ int connect_addr(struct connection *cnx, int fd_from, connect_blocking blocking) res = getpeername(fd_from, from.ai_addr, &from.ai_addrlen); CHECK_RES_RETURN(res, "getpeername", res); - if (cfg.protocols.resolve_on_forward) { + if (cnx->proto->resolve_on_forward) { resolve_split_name(&(cnx->proto->saddr), cnx->proto->host, cnx->proto->port); } diff --git a/sslh-main.c b/sslh-main.c index 3fe16d9..05acba2 100644 --- a/sslh-main.c +++ b/sslh-main.c @@ -126,7 +126,7 @@ static void config_protocols() struct sslhcfg_protocols_item* p = &(cfg.protocols[i]); if ( - !cfg.protocols.resolve_on_forward && + !p->resolve_on_forward && resolve_split_name(&(p->saddr), p->host, p->port) ) { print_message(msg_config_error, "cannot resolve %s:%s\n", From 759e68c8d2a1f583cc5ad13827c28faff02b0bfb Mon Sep 17 00:00:00 2001 From: beango1 Date: Wed, 23 Mar 2022 10:22:44 -0400 Subject: [PATCH 064/191] typo correction --- doc/config.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/config.md b/doc/config.md index 42d609f..b243db1 100644 --- a/doc/config.md +++ b/doc/config.md @@ -104,7 +104,7 @@ original client IP address, i.e. `sslh` becomes invisible. This makes it easier to use the server's logs, and potential IP-based banning ability. -Set up can get complicated, so it has it's own [document](tproxy.md). +Set up can get complicated, so it has its own [document](tproxy.md). Systemd Socket Activation ------------------------- From 9c3274359a8f649baf877412d6a43b28941d1c58 Mon Sep 17 00:00:00 2001 From: yrutschle Date: Wed, 23 Mar 2022 21:33:45 +0100 Subject: [PATCH 065/191] document resolve_on_forward --- ChangeLog | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ChangeLog b/ChangeLog index 5ce8d6b..e6a0c34 100644 --- a/ChangeLog +++ b/ChangeLog @@ -14,6 +14,11 @@ vNEXT: information to an attacker. When inetd is activated, stderr is forcibly closed. + New protocol-level option `resolve_on_forward`, + requests that target names are resolved at each + connection instead of at startup. Useful for dynamic + DNS situations. (Paul Schroeder/milkpirate) + v1.22: 17AUG2021 sslh-select now supports UDP protocols. Probes specified in the `protocols` From 454a261c95bed283e33b30a2b80dd01f7cd6bd7e Mon Sep 17 00:00:00 2001 From: yrutschle Date: Wed, 30 Mar 2022 22:56:41 +0200 Subject: [PATCH 066/191] initial hash object with testing framework --- hash.c | 259 +++++++++++++++++++++++ hash.h | 16 ++ hashtest/Makefile | 6 + hashtest/delete.tst | 8 + hashtest/delete.tst.ref | 34 +++ hashtest/delete_at_end.tst | 9 + hashtest/delete_at_end.tst.ref | 34 +++ hashtest/delete_below_floor.tst | 9 + hashtest/delete_below_floor.tst.ref | 34 +++ hashtest/delete_discont.tst | 10 + hashtest/delete_discont.tst.ref | 34 +++ hashtest/delete_empty.tst | 11 + hashtest/delete_empty.tst.ref | 34 +++ hashtest/delete_full.tst | 39 ++++ hashtest/delete_full.tst.ref | 34 +++ hashtest/delete_middle.tst | 10 + hashtest/delete_middle.tst.ref | 34 +++ hashtest/delete_wrap.tst | 8 + hashtest/delete_wrap.tst.ref | 34 +++ hashtest/delete_wrap_at_end.tst | 10 + hashtest/delete_wrap_at_end.tst.ref | 34 +++ hashtest/delete_wrap_below_floor.tst | 8 + hashtest/delete_wrap_below_floor.tst.ref | 34 +++ hashtest/delete_wrap_discont.tst | 11 + hashtest/delete_wrap_discont.tst.ref | 34 +++ hashtest/htest | Bin 0 -> 25216 bytes hashtest/htest.c | 105 +++++++++ hashtest/insert.tst | 6 + hashtest/insert.tst.ref | 34 +++ hashtest/insert_discont.tst | 8 + hashtest/insert_discont.tst.ref | 34 +++ hashtest/insert_full.tst | 40 ++++ hashtest/insert_full.tst.ref | 34 +++ hashtest/insert_full_floor.tst | 7 + hashtest/insert_full_floor.tst.ref | 34 +++ hashtest/insert_wrap.tst | 7 + hashtest/insert_wrap.tst.ref | 34 +++ hashtest/mkrand.pl | 41 ++++ hashtest/run | 30 +++ 39 files changed, 1202 insertions(+) create mode 100644 hash.c create mode 100644 hash.h create mode 100644 hashtest/Makefile create mode 100644 hashtest/delete.tst create mode 100644 hashtest/delete.tst.ref create mode 100644 hashtest/delete_at_end.tst create mode 100644 hashtest/delete_at_end.tst.ref create mode 100644 hashtest/delete_below_floor.tst create mode 100644 hashtest/delete_below_floor.tst.ref create mode 100644 hashtest/delete_discont.tst create mode 100644 hashtest/delete_discont.tst.ref create mode 100644 hashtest/delete_empty.tst create mode 100644 hashtest/delete_empty.tst.ref create mode 100644 hashtest/delete_full.tst create mode 100644 hashtest/delete_full.tst.ref create mode 100644 hashtest/delete_middle.tst create mode 100644 hashtest/delete_middle.tst.ref create mode 100644 hashtest/delete_wrap.tst create mode 100644 hashtest/delete_wrap.tst.ref create mode 100644 hashtest/delete_wrap_at_end.tst create mode 100644 hashtest/delete_wrap_at_end.tst.ref create mode 100644 hashtest/delete_wrap_below_floor.tst create mode 100644 hashtest/delete_wrap_below_floor.tst.ref create mode 100644 hashtest/delete_wrap_discont.tst create mode 100644 hashtest/delete_wrap_discont.tst.ref create mode 100755 hashtest/htest create mode 100644 hashtest/htest.c create mode 100644 hashtest/insert.tst create mode 100644 hashtest/insert.tst.ref create mode 100644 hashtest/insert_discont.tst create mode 100644 hashtest/insert_discont.tst.ref create mode 100644 hashtest/insert_full.tst create mode 100644 hashtest/insert_full.tst.ref create mode 100644 hashtest/insert_full_floor.tst create mode 100644 hashtest/insert_full_floor.tst.ref create mode 100644 hashtest/insert_wrap.tst create mode 100644 hashtest/insert_wrap.tst.ref create mode 100755 hashtest/mkrand.pl create mode 100755 hashtest/run diff --git a/hash.c b/hash.c new file mode 100644 index 0000000..0b5db47 --- /dev/null +++ b/hash.c @@ -0,0 +1,259 @@ +/* + * a fixed-sized hash + * +# Copyright (C) 2022 Yves Rutschle +# +# This program is free software; you can redistribute it +# and/or modify it under the terms of the GNU General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later +# version. +# +# This program is distributed in the hope that it will be +# useful, but WITHOUT ANY WARRANTY; without even the implied +# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +# PURPOSE. See the GNU General Public License for more +# details. +# +# The full text for the General Public License is here: +# http://www.gnu.org/licenses/gpl.html +# +# */ + + +/* * The hash is open-addressing, linear search, robin-hood insertion, with + * backward shift deletion and moving floor. + * https://codecapsule.com/2013/11/11/robin-hood-hashing/ + * https://codecapsule.com/2013/11/17/robin-hood-hashing-backward-shift-deletion/ + * This means items are reordered upon insertion and deletion, and the hash + * is well-ordered at all times with no tombstones. + * + * Items that 'wrap' around push the 'floor' up. Searching for low items will + * therefore start from the floor up. + * + * Each pointer is either: + * - to a connection struct + * - FREE (NULL) if not allocated + * + * */ + +#include +#include + +#include "gap.h" +#include "hash.h" + +static const int h_keylen = 5; /* How many bits in the hash key? (hash is 2^h_keylen big) */ +static const int hash_size = (1 << h_keylen); /* 8 => 256 */ +static void* const FREE = NULL; + +struct hash { + int item_cnt; /* Number of items in the hash */ + int floor; /* Where is the highest key. Or the lowest insert point */ + gap_array* data; + + hash_make_key_fn hash_make_key; + hash_cmp_item_fn cmp_item; +}; + +typedef struct hash hash; + + +hash* hash_init(hash_make_key_fn make_key, hash_cmp_item_fn cmp_item) +{ + hash* h = malloc(sizeof(*h)); + if (!h) return NULL; + + h->item_cnt = 0; + h->floor = 0; + h->data = gap_init(hash_size); + h->hash_make_key = make_key; + h->cmp_item = cmp_item; + + return h; +} + +/* Return the index following i in h */ +static int hash_next_index(hash* h, int i) +{ + return (i + 1) % hash_size; +} + +/* Returns the index in h of specified address, -1 if not found + * item is an item object that must return the target wanted index and for + * which comparison with the searched object will succeed. + * */ +int hash_find_index(hash* h, hash_item item) +{ + hash_item cnx; + int index = h->hash_make_key(item); + int cnt = 0; + + if (index < h->floor) index = h->floor; + + cnx = gap_get(h->data, index); +#ifdef DEBUG + fprintf(stderr, "searching %d\n", index); +#endif + while (cnx != FREE) { + if (cnt++ > hash_size) return -1; + + if (!h->cmp_item(cnx, item)) + break; + + index = hash_next_index(h, index); + cnx = gap_get(h->data, index); +#ifdef DEBUG + fprintf(stderr, "searching %d\n", index); +#endif + } + if (cnx == FREE) return -1; + return index; +} + +/* says if we should swap the bubble (element that's going up) and the current + * indexed element. + * index: current insertion index + * wanted_index: index wanted by the element at the current index + * bubble: bubble wanted index + * floor: index of lowest wanted index (or index of the highest wrapped wanted + * index) + * wrapped: whether we wrapped or not (if we reach the floor from below after + * wrapping, we should swap as it's a 'highest' element pushing the floor up. + * it we're inserting a higher-than bottom element at the floor, it should + * bubble up). + */ +static int i_should_swap(int index, int wanted_index, int bubble_index, int floor, int wrapped) +{ + int res; + + if ((index == floor)) + res = wrapped; + else + res = bubble_index < wanted_index; + +#if DEBUG + fprintf(stderr, "i_should_swap(%d, %d, %d, %d) = %d\n", index, wanted_index, bubble_index, floor, res); +#endif + + return res; +} + +int hash_insert(hash* h, hash_item new) +{ + int bubble_wanted_index = h->hash_make_key(new); + int index = bubble_wanted_index; + gap_array* hash = h->data; + + if (h->item_cnt == hash_size) + return -1; + + if (index < h->floor) index = h->floor; + + hash_item curr_item = gap_get(hash, index); + int orig_floor = h->floor; + int wrapped = 0; + while (1) { + if (curr_item == NULL) { +#if DEBUG + fprintf(stderr, "final insert at %d\n", index); +#endif + gap_set(hash, index, new); + h->item_cnt++; + return 0; + } + + int curr_wanted_index = h->hash_make_key(curr_item); + + if (i_should_swap(index, curr_wanted_index, bubble_wanted_index, orig_floor, wrapped)) { + gap_set(h->data, index, new); +#if DEBUG + fprintf(stderr, "intermediate insert [%s] at %d\n", &new->client_addr, index); +#endif + new = curr_item; + bubble_wanted_index = curr_wanted_index; + } + + index = hash_next_index(h, index); + curr_item = gap_get(hash, index); + + if (index == 0) h->floor++; + if (index == 0) wrapped = 1; + } + + return 0; +} + + + + +static int next_in_right_place(hash* h, hash_item item, int index) +{ + if (!item) return 0; + int wanted_index = h->hash_make_key(item); + return (wanted_index == index); +} + +/* Remove cnx from the hash */ +int hash_remove(hash* h, hash_item item) +{ + gap_array* hash = h->data; + + int index = hash_find_index(h, item); + if (index == -1) return -1; /* Tried to remove something that isn't there */ + + int lower_floor = 0; /* If we remove something below the floor, we'll need to lower it */ + while (1) { + if (index < h->floor) lower_floor = 1; + int next_index = hash_next_index(h, index); + hash_item next = gap_get(h->data, next_index); + if ((next == FREE) || next_in_right_place(h, next, next_index)) { + h->item_cnt--; + if (lower_floor) h->floor--; + gap_set(hash, index, FREE); + return 0; + } + + gap_set(hash, index, next); + + index = hash_next_index(h, index);; +#if DEBUG + fprintf(stderr, "index %d floor %d\n", index, h->floor); +#endif + } + return 0; +} + + +#include +#include +#define STR_LENGTH 16 +struct hash_item { + int wanted_index; + char str[STR_LENGTH]; +}; +void hash_dump(hash* h, char* filename) +{ + char str[STR_LENGTH]; + FILE* out = fopen(filename, "w"); + + if (!out) { + perror(filename); + exit(1); + } + + fprintf(out, "\n", h->floor, h->item_cnt); + for (int i = 0; i < hash_size; i++) { + hash_item item = gap_get(h->data, i); + int idx = 0; + + memset(str, 0, STR_LENGTH); + if (item) { + idx = h->hash_make_key(item); + memcpy(str, item->str, STR_LENGTH); + } + fprintf(out, "\t%d:%d:%s\n", i, idx, str); + } + fprintf(out, "\n"); + fclose(out); +} diff --git a/hash.h b/hash.h new file mode 100644 index 0000000..49a747b --- /dev/null +++ b/hash.h @@ -0,0 +1,16 @@ + + +typedef struct hash hash; +typedef struct hash_item* hash_item; +typedef int (*hash_make_key_fn)(hash_item item); + +/* Function that compares two items: returns 0 if they are the same */ +typedef int (*hash_cmp_item_fn)(hash_item item1, hash_item item2); + +hash* hash_init(hash_make_key_fn make_key, hash_cmp_item_fn cmp_item); +int hash_find_index(hash* h, hash_item item); +int hash_insert(hash* h, hash_item new); +int hash_remove(hash* h, hash_item item); + + +void hash_dump(hash* h, char* filename); /* For development only */ diff --git a/hashtest/Makefile b/hashtest/Makefile new file mode 100644 index 0000000..49e961e --- /dev/null +++ b/hashtest/Makefile @@ -0,0 +1,6 @@ + +OBJ=../hash.o ../gap.o htest.o + +htest: $(OBJ) + $(CC) -o htest $(OBJ) + diff --git a/hashtest/delete.tst b/hashtest/delete.tst new file mode 100644 index 0000000..5cee269 --- /dev/null +++ b/hashtest/delete.tst @@ -0,0 +1,8 @@ +# Basic delete +a 10 aa +a 10 ab +a 10 ac +a 20 ba +a 21 bb + +d 21 bb diff --git a/hashtest/delete.tst.ref b/hashtest/delete.tst.ref new file mode 100644 index 0000000..a95a3dc --- /dev/null +++ b/hashtest/delete.tst.ref @@ -0,0 +1,34 @@ + + 0:0: + 1:0: + 2:0: + 3:0: + 4:0: + 5:0: + 6:0: + 7:0: + 8:0: + 9:0: + 10:10:aa + 11:10:ab + 12:10:ac + 13:0: + 14:0: + 15:0: + 16:0: + 17:0: + 18:0: + 19:0: + 20:20:ba + 21:0: + 22:0: + 23:0: + 24:0: + 25:0: + 26:0: + 27:0: + 28:0: + 29:0: + 30:0: + 31:0: + diff --git a/hashtest/delete_at_end.tst b/hashtest/delete_at_end.tst new file mode 100644 index 0000000..e89ec12 --- /dev/null +++ b/hashtest/delete_at_end.tst @@ -0,0 +1,9 @@ +# Delete inside a block with nothing after + +a 10 aa +a 10 ab +a 12 ac +a 13 ad +a 14 ae + +d 14 ae diff --git a/hashtest/delete_at_end.tst.ref b/hashtest/delete_at_end.tst.ref new file mode 100644 index 0000000..b883c40 --- /dev/null +++ b/hashtest/delete_at_end.tst.ref @@ -0,0 +1,34 @@ + + 0:0: + 1:0: + 2:0: + 3:0: + 4:0: + 5:0: + 6:0: + 7:0: + 8:0: + 9:0: + 10:10:aa + 11:10:ab + 12:12:ac + 13:13:ad + 14:0: + 15:0: + 16:0: + 17:0: + 18:0: + 19:0: + 20:0: + 21:0: + 22:0: + 23:0: + 24:0: + 25:0: + 26:0: + 27:0: + 28:0: + 29:0: + 30:0: + 31:0: + diff --git a/hashtest/delete_below_floor.tst b/hashtest/delete_below_floor.tst new file mode 100644 index 0000000..432b5b9 --- /dev/null +++ b/hashtest/delete_below_floor.tst @@ -0,0 +1,9 @@ +# wrap-around and delete below floor +a 2 ba +a 30 aa +a 30 ab +a 30 ac +a 30 ad +a 2 bb + +d 30 ab diff --git a/hashtest/delete_below_floor.tst.ref b/hashtest/delete_below_floor.tst.ref new file mode 100644 index 0000000..e20cf3a --- /dev/null +++ b/hashtest/delete_below_floor.tst.ref @@ -0,0 +1,34 @@ + + 0:30:ad + 1:0: + 2:2:ba + 3:2:bb + 4:0: + 5:0: + 6:0: + 7:0: + 8:0: + 9:0: + 10:0: + 11:0: + 12:0: + 13:0: + 14:0: + 15:0: + 16:0: + 17:0: + 18:0: + 19:0: + 20:0: + 21:0: + 22:0: + 23:0: + 24:0: + 25:0: + 26:0: + 27:0: + 28:0: + 29:0: + 30:30:aa + 31:30:ac + diff --git a/hashtest/delete_discont.tst b/hashtest/delete_discont.tst new file mode 100644 index 0000000..f27d8dc --- /dev/null +++ b/hashtest/delete_discont.tst @@ -0,0 +1,10 @@ +# delete in a discontinuous block + +a 10 aa +a 11 ab +a 12 ac +a 14 ad +a 10 bc + +d 11 ab + diff --git a/hashtest/delete_discont.tst.ref b/hashtest/delete_discont.tst.ref new file mode 100644 index 0000000..07230ec --- /dev/null +++ b/hashtest/delete_discont.tst.ref @@ -0,0 +1,34 @@ + + 0:0: + 1:0: + 2:0: + 3:0: + 4:0: + 5:0: + 6:0: + 7:0: + 8:0: + 9:0: + 10:10:aa + 11:10:bc + 12:12:ac + 13:0: + 14:14:ad + 15:0: + 16:0: + 17:0: + 18:0: + 19:0: + 20:0: + 21:0: + 22:0: + 23:0: + 24:0: + 25:0: + 26:0: + 27:0: + 28:0: + 29:0: + 30:0: + 31:0: + diff --git a/hashtest/delete_empty.tst b/hashtest/delete_empty.tst new file mode 100644 index 0000000..ce29d4d --- /dev/null +++ b/hashtest/delete_empty.tst @@ -0,0 +1,11 @@ +# Delete an unexisting element. And on an empty hash + +a 10 aa + +d 10 ab +d 12 bc + +# Empty for real +d 10 aa + +d 10 aa diff --git a/hashtest/delete_empty.tst.ref b/hashtest/delete_empty.tst.ref new file mode 100644 index 0000000..e51b8b1 --- /dev/null +++ b/hashtest/delete_empty.tst.ref @@ -0,0 +1,34 @@ + + 0:0: + 1:0: + 2:0: + 3:0: + 4:0: + 5:0: + 6:0: + 7:0: + 8:0: + 9:0: + 10:0: + 11:0: + 12:0: + 13:0: + 14:0: + 15:0: + 16:0: + 17:0: + 18:0: + 19:0: + 20:0: + 21:0: + 22:0: + 23:0: + 24:0: + 25:0: + 26:0: + 27:0: + 28:0: + 29:0: + 30:0: + 31:0: + diff --git a/hashtest/delete_full.tst b/hashtest/delete_full.tst new file mode 100644 index 0000000..f950d38 --- /dev/null +++ b/hashtest/delete_full.tst @@ -0,0 +1,39 @@ +# delete on a full hash + +# First, fill the hash :-) + +a 0 aa +a 1 ab +a 2 ac +a 3 ad +a 4 ae +a 5 af +a 6 ag +a 7 ah +a 8 ai +a 9 af +a 10 ba +a 11 bb +a 12 bc +a 13 bd +a 14 be +a 15 bf +a 16 bg +a 17 bh +a 18 bi +a 19 bj +a 20 ca +a 21 cb +a 22 cd +a 23 ce +a 24 cf +a 25 cg +a 26 ch +a 27 ci +a 28 cj +a 29 ck +a 30 da +a 31 db + + +d 21 cb diff --git a/hashtest/delete_full.tst.ref b/hashtest/delete_full.tst.ref new file mode 100644 index 0000000..7dd0dea --- /dev/null +++ b/hashtest/delete_full.tst.ref @@ -0,0 +1,34 @@ + + 0:0:aa + 1:1:ab + 2:2:ac + 3:3:ad + 4:4:ae + 5:5:af + 6:6:ag + 7:7:ah + 8:8:ai + 9:9:af + 10:10:ba + 11:11:bb + 12:12:bc + 13:13:bd + 14:14:be + 15:15:bf + 16:16:bg + 17:17:bh + 18:18:bi + 19:19:bj + 20:20:ca + 21:0: + 22:22:cd + 23:23:ce + 24:24:cf + 25:25:cg + 26:26:ch + 27:27:ci + 28:28:cj + 29:29:ck + 30:30:da + 31:31:db + diff --git a/hashtest/delete_middle.tst b/hashtest/delete_middle.tst new file mode 100644 index 0000000..29b0771 --- /dev/null +++ b/hashtest/delete_middle.tst @@ -0,0 +1,10 @@ +# Delete inside a block with something discontinuous + +a 10 aa +a 10 ab +a 12 ac +a 13 ad +a 14 ae + +# ab shifts, ac and next doesn't +d 10 aa diff --git a/hashtest/delete_middle.tst.ref b/hashtest/delete_middle.tst.ref new file mode 100644 index 0000000..9484187 --- /dev/null +++ b/hashtest/delete_middle.tst.ref @@ -0,0 +1,34 @@ + + 0:0: + 1:0: + 2:0: + 3:0: + 4:0: + 5:0: + 6:0: + 7:0: + 8:0: + 9:0: + 10:10:ab + 11:0: + 12:12:ac + 13:13:ad + 14:14:ae + 15:0: + 16:0: + 17:0: + 18:0: + 19:0: + 20:0: + 21:0: + 22:0: + 23:0: + 24:0: + 25:0: + 26:0: + 27:0: + 28:0: + 29:0: + 30:0: + 31:0: + diff --git a/hashtest/delete_wrap.tst b/hashtest/delete_wrap.tst new file mode 100644 index 0000000..cea8800 --- /dev/null +++ b/hashtest/delete_wrap.tst @@ -0,0 +1,8 @@ +# Basic delete when wrapping, between wrap and floor +a 30 aa +a 30 ab +a 30 ac +a 30 ba +a 30 bb + +d 30 ac diff --git a/hashtest/delete_wrap.tst.ref b/hashtest/delete_wrap.tst.ref new file mode 100644 index 0000000..8cddd01 --- /dev/null +++ b/hashtest/delete_wrap.tst.ref @@ -0,0 +1,34 @@ + + 0:30:ba + 1:30:bb + 2:0: + 3:0: + 4:0: + 5:0: + 6:0: + 7:0: + 8:0: + 9:0: + 10:0: + 11:0: + 12:0: + 13:0: + 14:0: + 15:0: + 16:0: + 17:0: + 18:0: + 19:0: + 20:0: + 21:0: + 22:0: + 23:0: + 24:0: + 25:0: + 26:0: + 27:0: + 28:0: + 29:0: + 30:30:aa + 31:30:ab + diff --git a/hashtest/delete_wrap_at_end.tst b/hashtest/delete_wrap_at_end.tst new file mode 100644 index 0000000..e2b1ce1 --- /dev/null +++ b/hashtest/delete_wrap_at_end.tst @@ -0,0 +1,10 @@ +# Delete inside a block with wrapping, with something after + +a 30 aa +a 30 ab +a 30 ac +a 1 ad +a 3 ae + +# shift ad but not ae +d 14 ae diff --git a/hashtest/delete_wrap_at_end.tst.ref b/hashtest/delete_wrap_at_end.tst.ref new file mode 100644 index 0000000..73ea879 --- /dev/null +++ b/hashtest/delete_wrap_at_end.tst.ref @@ -0,0 +1,34 @@ + + 0:30:ac + 1:1:ad + 2:0: + 3:3:ae + 4:0: + 5:0: + 6:0: + 7:0: + 8:0: + 9:0: + 10:0: + 11:0: + 12:0: + 13:0: + 14:0: + 15:0: + 16:0: + 17:0: + 18:0: + 19:0: + 20:0: + 21:0: + 22:0: + 23:0: + 24:0: + 25:0: + 26:0: + 27:0: + 28:0: + 29:0: + 30:30:aa + 31:30:ab + diff --git a/hashtest/delete_wrap_below_floor.tst b/hashtest/delete_wrap_below_floor.tst new file mode 100644 index 0000000..7fa5038 --- /dev/null +++ b/hashtest/delete_wrap_below_floor.tst @@ -0,0 +1,8 @@ +# delete before wrap +a 30 aa +a 30 ab +a 30 ac +a 30 ad + +# shift ac and ad +d 30 ab diff --git a/hashtest/delete_wrap_below_floor.tst.ref b/hashtest/delete_wrap_below_floor.tst.ref new file mode 100644 index 0000000..e94683e --- /dev/null +++ b/hashtest/delete_wrap_below_floor.tst.ref @@ -0,0 +1,34 @@ + + 0:30:ad + 1:0: + 2:0: + 3:0: + 4:0: + 5:0: + 6:0: + 7:0: + 8:0: + 9:0: + 10:0: + 11:0: + 12:0: + 13:0: + 14:0: + 15:0: + 16:0: + 17:0: + 18:0: + 19:0: + 20:0: + 21:0: + 22:0: + 23:0: + 24:0: + 25:0: + 26:0: + 27:0: + 28:0: + 29:0: + 30:30:aa + 31:30:ac + diff --git a/hashtest/delete_wrap_discont.tst b/hashtest/delete_wrap_discont.tst new file mode 100644 index 0000000..1c49746 --- /dev/null +++ b/hashtest/delete_wrap_discont.tst @@ -0,0 +1,11 @@ +# Delete with wrapping in discontinuous group + +a 30 aa +a 30 ab +a 30 ac +a 31 ad +a 2 ba +a 3 bb + +# shift ac and ad but not ba and bb +d 30 ab diff --git a/hashtest/delete_wrap_discont.tst.ref b/hashtest/delete_wrap_discont.tst.ref new file mode 100644 index 0000000..83873da --- /dev/null +++ b/hashtest/delete_wrap_discont.tst.ref @@ -0,0 +1,34 @@ + + 0:31:ad + 1:0: + 2:2:ba + 3:3:bb + 4:0: + 5:0: + 6:0: + 7:0: + 8:0: + 9:0: + 10:0: + 11:0: + 12:0: + 13:0: + 14:0: + 15:0: + 16:0: + 17:0: + 18:0: + 19:0: + 20:0: + 21:0: + 22:0: + 23:0: + 24:0: + 25:0: + 26:0: + 27:0: + 28:0: + 29:0: + 30:30:aa + 31:30:ac + diff --git a/hashtest/htest b/hashtest/htest new file mode 100755 index 0000000000000000000000000000000000000000..55dc1c826c084febd1827f72a982837d2aa404a2 GIT binary patch literal 25216 zcmeHvdw5&bwda=PW6AchBtMckZzUupKw>)&66XbS5+w>vf`J&qBb>;3*dnqd^vHt} zn#RDOnsAd_hC*+qa4#+8QYf^;w3N$N!lQL43=O>nQf}J%mUKpL(qbS`Lj%6QwIA}4 zVt<|aXXg9vmz`K^ujgKS?X~wg`@FvF+pyMZu`pF^>=H(pyGBsFl5zN2Spo2}7FLDd z^Vm#Q4!n%h1m7zFxT17fi=ji(ZGfcrsN|QzDN;^B=K`0}3*?kR&~gsuC}S z^PECXLB%h$=Mj8X3b|466g0I*vB}R9dij;bQ1$nS?7wP{P|_QcdP7oA!G3ZGML`u$ ziV6L_EbW!THKc^1Ajwi``$Hx~^%EY#&W5iEGE-3LT>(Av^Z&G{uwNm%aXXp#_gW{Tj*E;*V_bep$YP8nY6u4u5ZiU+-S}L9a?j;uV#9 zfst%!c@hLqDG_)J{zjAck3pY327TWc^e>G;4~;>86?6~&O3URSjOORmG3Yc7qsfmS zgFbf*des>8tz*#N0o{YY(h>tFgqdMP(~02HrA8zj48$WW5{xC%Vb(1a z(wPt}u*l9-G!tf#WGWiZM4%Jg6)+;vcpye5(wS7SI|(YeV5BpFRL1BIMB^-y3WwRc z4eM8}HWoB4DbyA;Ud)X3n>QM;9qx>#GvUP}kRe1z6%;eY zhWVoY5ykvZ;1nqf=oUaVpC?44)ikpnkhJWiXaz|>Ou{H?-Ba-$l*g4pxvnYtQ<8p{ zm#Z;FSNO3@1)b(5%~e(UO?2~mlsD1gP@xQ(=(0)98#2+&`K$7SmUmhQRB7;vnjL`R zp^C>VYDA~?MwK~UQ6suqLy6MtrC-K~t_~rDw|MDS(21vt*DGp7Q!yfn-%GzjzEV{A zZ<|*jBu{fnl`URTBf74LfZMzRAvy-OP`bRLN_2Usc|xS3MVj za}hWffpZZ!7lCsT`2QFIf6pn0zgPQB6HX!SIGnK#ANBXV?0Bi*(eTB;2Wxot3H-XI zdr>32i}(Zm!zi=gA)HQu299$4mxNmhKg{u;6Hcc>15a`MMZ)P6Xy74^KT9~B`V1W4 z_zwuDQ@Me?9Dj^(I_(%pa{QkNr&FJSZ5;mw;dF{O(8lqy(`}bi)EYI{XHueQ`MjQLq_-au6!0qejd7% z@50Z|v{!&zwkkaZ2fhSmJ9IGc9)E66-k;0%KktE^{8v679`@&aL;jvULo9251~hik z@9*^u^--1anwOuvKH~wN>&y3I-2Ay0iQ~_;A&~%k+hMcKE$jWLH&FeRmw2nWJwv(d z>0aOIp2L3s61eLtM*9$Z4F!A>A(VXvepKlNfORjzJS!!_U(cA3L(8!;RNgepNu^$bg53lpK zFmlUahuFeaE5t`@BP`qCvGOnV4Q0P}(sEz~lMUI>U(x^Eo>RGOKG%LCx3Pas&Ud`m zcWSw>KkMlA9nbmBoOJd2PAvE3U55_4es~y!Uf-EQXuKG$BN(m2aO;Q+&0!fD5~Pt6 z90x`b{qY^?WwOWFO5Wpv>6oXSLE#(X;X8s*LX~>o>+2WYy$6E~8-r35e)joJ@zD3| z8D!Z;#OiSGMxlU+_2nBW4!l7GnkqRr55mCr#CR!x2c^HqdiR{_-FUpW{Y-E67?Ptk zHv}vHh6F_D&nE-uphGr8gVL9Om3+cT!mq=}5VZxFOT-w+Qt6nU+g7G;4-#s17B@I;=&6%o=pbQkenP1?O|f7>v(>!!G5C|uOCqPCSi zOaAn3q{at@ehcEu8#qZB(O<-y0$!1)pZ}FSJu`5jImTX7P9OH~zxYn9<22agKjll& zAmWaE8;>Rr6WImw^Av;pY!aY^Lz3XPt&F^afpx+F_bUI;Cp5HY$nSxJ_#sgm1^6YR z^axFdoG-6}z2A5AS=y}rgjA@(OJD&$g=2P@?eU$l@MUzV?~Lp20HVUXkbB`-I=20UdWmDj zKLE-5$qSzPefdK}!@~y(3Fgl2Ig_sd`+>jgIa8oE=k^?bp8Aph9;9goh&WdA*43OG zEbi)65XC&8#<3`&?+?JG94+mGIds}L*vppsj^9?ntL?`X&GjU*?|zEaD&7`XkjH{S zp7M7bR>zVSIT$Bt25}~ZDELeN3%|GdEl2(0FhhY+?VY*xO{&VCiDL%{2t1AAX_Ia* z6AR^4IIgu&R`vPv)E7t}>208eyf5E}^~Hjf;{;Y&_?`C$j}luj8l#ybt(eCMS-#ZQpK;PC;h=*x@7A|)$P?8)PH`MUgK+<( zSHy2^iKU(wEquiH>y%e{IU~(Iv7jTS2A=1gD#T25DwjnD(VXXVA15uChGJGf@KIqE zm%UfXf!yE$r*j@YQF9xQs{UT+@1=EmBZdoS9Pmrb+gbbwFXycbmc_W(GUa^5{<&DU zpGT&#`~k34KzmNL<@P*>hMvOYUybp^WR3#l_tFSpO3HS7_B?0Hplz*gHZ2e@JbOMY zsoDP{9P{#*!KzsFno)yiPl<%3$niSj!4LOOAqEawhW20l2tR=LnP8! zK9GgU-%yS2jpTcT=+U3~UiHW+c+|h&kF^bltNqzmp^NxaHqT|dE8&@P|F7yb3QPg?n+H$%7~7QI?DAVd%M423c={Cv5xXDE=FGEjrnu4m|G z*X^Iflb)fp>-Kw48Tb@Gya~z^G?+w#+R5{9=J|#{_W`>37C*qt!TG_5nQfA8B+=za1Wk{aEWY>vM0nUcEl| zacg^P?z2|xSZ2FDjJ5ms&m(x%h70Ia-;u9_dH)Na*)o&o|C?(%+mQQoL+;}>x%XO! z>v4MXlEuIDH`zbZ-tYRYtv9r8ZN0J8z@CAVP0(3uz3YIzMp^7X! z*@~_}y2}%ZB@(HXvqPS6EZhxvNhPb89a={J)0J#R6A6LFW(PeGBAO`eY1|oG=IP3W z(;3f-bTAc7W;~H-EPRP)MJU^y6!l7$Vu4^LnutStnP{t)4&z zd2M$w>;kot6{?UXJy?VmvrM)vKLWQJ@VDZ<;o*8LH&X_OhgSg31v~+I z&HKZ{n}PoqzzpEG0PiIn)8=Ww=K+5O*o0N=G++v_9-;dh=J_JPf5lR>8E^nF1Naio zB<=;w13nFSFQ)G?zz+e30H@)ktpU^j2;d69j{t7~)No>ZD_{cf5a4l~;6Der0^5W# zET^-Mg-~T{+JBZlobl+)RU0rUy86FASw}M=S z_)azRw{SLZ`w-|3@ZUA_t-mekKL`4&;J29h=@I?opuY=#65~;5-?rUC5>r4rgTI9s zx7C0J{%s@rQ=oS>_%n+1rxf%lKd!+_>@DIiFYs>weJApO?rj(BUom3;R`9#Qf5*(X zzR3m1zeC`kkNm-re?)(q(D#7!9Qbq`dECtR_1LWcG}1;tWS)kMVV1F8S7?K#_lMx~ zoKRyF8EL}<{^#I7R;2%JApjcrxdt+~Bd_@*DauCIhz}v~4}woyjzS#1IKsaj{I7t2 zlbLS~a{;pdE%09j-vBJ=lPn75-pimjV18^R!YZfx@2oy&!#y^i)3d+K=bUr5=65#V zS?+hX^w`%qy*D{qTAj_U&N-``o>k5U$ggrb`2A`f^Qaki5VCXSTm;TV;9LaGMc`Zn z&PCu{1pd=UKs|S-p0iWuKnmjSrYQU*ZsggoS_LUjy}Ad1>ss&?=4irTu> ztCxA^tO<8S194AFS z%z6XK&7Q!4RjWpBTmOh3+xb(1;8hmm_}tcVxPiBW?O7LRN8Pi@uX|){Or%TG*yv zNY>WYp9a#UFCyRk-0P&iiL9;XNJf8+IG0r&g7hx^2|_m327&C==OYC6O)lEJ?$cM1 zwaXo!!dj2+Cj(cwDgo}&kCM){)sI5pfKEGi`}GsXfpfpUj=FurL=TXM^a}{t%H4TX z|1!+jZ>)G4I#20$P#26!+GIbYUrnaBao-N>_Y5rk-Bp zbS{RWa>oGwC5DSqh5T|;Iuq~T!E^o?ta67NbmtoQQSO+GDwkcXWb0UED?}@D&Q&0l z^TDos1tu!K$|;rq2xlsKMg3JsR!-vC;rawJJE2m;>@k4K?QpGf@+2bJ9A!Txr%2>M z;M^%Mv;Q6H71zUF)ilc_VpQBf$aH~N?*^hz;WMY|E!5rBPdYw;lpcWT>hEhmL7nL{ z(2?pNR1(7~`eFr}H&M;3H$ueOP+5QF#k4=5nG=9FE(WYZB_WE8NDacA{0c>t7uY zf~GGc3-45X0d))$MxoEk+Etvh zMgJx_x|(yg>9kJQuHl?6{S+ZS&PnPHLe_HAyY%mnwRIf1Rlkbb@mJA<5qtIFG9c^g zXo&98XHictbI{^(03-En5Uc2L_h{`##~m=Jd&$lwZineLg*^vxu8 z1xIu}OwC=%5w{*A&Q%<#*Iy(9n>o^;JE`q1^T1 z9Q|%WuI0#F{W&sy9Y>n=KNIJAjx5sMWZ(vlw1|<}%8?cN)1-4FN4)wL^1$H88vVX9 zAlrBb_;p$)YXh9qrhi8L>EN6#`aNVI$T{2eqZING=XB|P#0hgGsehl42uF75w~)0? zj_lQcN1QG`fOqNhsN2zLuVZ8m=(Ob3-o!cg>kkuW`wU9xhuHXFq?6Tscgp*~D(8Sx z_xMB_z{+)St?r3>I#gF_V7luo-EYviQPR~{Rd@lIPESJC>y-~e!@;a}!m4fm46(`_ zY}LDKNTqTG)oVn31F6@Ex(2uE>qY&yL?18eACsL4qV9qp^%F&XF9KOVNz@}GKUvi8 zB|B3^{auoo#_QI-7=>{))!m4}1TW+%O&h334gK^(5kP7NK&|=*s5R7Hk0TB-e;fH2xy6iY;z&L0+znOhM~KAuvPu19lj?5EOt+(wv57Ifr)4=$hV2t@ zEVFcCIG{d$9&DS; z8;CFTa&*I#E75mLlDB6)*fli@#4UI+wD>FdH|Uz& zBqJpH>pjm40qemKo9;t;SsHa>|4+FUc!#u6EQdDf2pAu-#))1~tOqwt<>TAz7WTG@ z-Yk;t-!P+FIMS;ARh4Mc0xNp8$%7U&Q$6AmPKfHjB~vboWqlpV zyJw*BCdk#yK)-4ySj@)i%*L=L6dHq5q&!aYxsTM?czEbg{TCii7-up^3M0VGp1sxZv8SO{h|BGRI3BQ8p<^TyyIqI7m_Wp88kwpjk^ti~^Y6`$~?Y&u0R zJvVg*gH3qb*;u?tzWUr5&o;F*MdQI(HWX%DP9kT^bwo4izaf&|T`ci8#K~e)W_J>c zNm0}BFmBi5rC5eZ))++J@IGrO6pl1@;gpea1ynJGR25y%#_`r_2xJ@^@G@;8nK6Qi zR9ML2b=Yh$W2{}j!3P4?ijf+Wq6wh}?+v_K8-pRK7VX6Qw33+!ZV!Y)DaipAR#LDK z&&CoZ^Af>=<{hfs%5&qTY)7dV$r$GYYP6LIPdN-)ulH)};bmgp3fWGc}?;wTgX zUSXPrRan((VVh&o^e41QTQtiT7ii<3&>U}TmHdQd3J2Wk$=|F5vS{;FX+5`eev{?kw+7tlGWjN^v zh-mutXm-BldO{n&PqTbDU#oaUo0!#RM6`-;YV)tvYBSo@63omdRNgN}?xa4E3 zzudsf-XVid0gPZ5y@ef!8j)C_GmZDEqw&Q?mKo`;L@Lvfjj--?rx6J;O0{S_l7Ivy zRts4N2YC|@^M_p0ctRz$BO1iJ*=PWoMn?cIi6e!KSa?S`h8muD;Xm1U8i^bZd0>cb z4~LU@M|?*ZPr!7BcNxh*1}}TZ)6k03G)rVNEZCJwBr*(hh~6}(cA-gAgTJ?K)`EMK z*|*?DaZ`iT&W>=Z18=P(3`k|EOP#_S>3F+5&hV}}T1=>3MWUTjs1UDkJj5ppPI}2o zCJ{`;(gt37@1~$EHptri#Uu5F3rxm9PSeZuh+80*jE@+=5M)z`V=UVZClI<|A`}kp z$T0Mrzv-Tcg=jeGrF@Kya5<4kb_9ak86PE2Q9R`+m`A*1po~mvH%()Tup!>zPhzln z zbc0I+OdaMQ4&d1cokVO$*ub0pkwj`|AccTq4UE&MlfxlAaFt5zW|>qVo=yf*l+F;` z&a$B-A9qTYWHdxW$zz{Hkis}hsKKXYt}w7t&>Sghgg=OdJfYcv4IE; z4EH0v3mJnsQ8bXeKbT~Z=q@a@I|;?iqo&h%228labB|9WX@$yd{0!M%~JRt~EU zP@K{MgoaP5k*JXybZCMz(LzoYvQ*?IRH+{rGL=7p_--C~I_eY}H;p64IUL^+O~Gx$ z2nBY>qMcpH+bBuYj!-_9Zp4q6cRaXVijMw0DAmjv3L>H5JP@s;7LaIqH%mCq>y_QTZ1Mf zhK77lF;S3sSPj)GfTcWi@kpvr1KBu|Ahdwi7n;-fV2x+>{3YTRh-Y4RCL0g0>q69os5ke(NI_j@-LP!%3$+2;$U;jh%kNsL^!nBXuI^h&1gKNi!m?J2oGMtmE-)ZrPGjxTl8m@SC94PWYTk4kqjbzj%4Wurzt zW{R>?!%mD!uVqio7)`#e@ZA?P*M_G_)&1dO`ZzY)eMlP{&(wEcisiAH_L?c8Zo?D4 zdq<^@eCNlkWn=O(keP_Ojj6la#S9yN8nT#1n2qr#T+d?I*tD}~LVo&LbnKC^(RhEj zl!2=l$Z{w1mW(4V8gP2Dgigaj^6EanvbO{k3%+;ss8>*^Jt}MQXBlN&x|6kmT#+g9581eE@JYKi`w`YOkiQ?9lxRvQv72%z*%oX2%UW$v-Yri*685 znJDSMmUK0Z&zJN8Nxw;mBQ~SkUkru?U)VY`OA4C6X#T7rL-=x0>AY*_cFvx6H%fW+ zoQE<`_rqwBDP0F{=XTDXkADH(N>6R5j!Hw{0KFFR@yMyc&4TwBr%NFdpgXA^6RxT| zvA%moneTK9O{Rzv1oXl#l}%&FcT4%wE5=#S$^X4xu3q@rH-?@2#-M+f z)17Bsq5bcmdkR)ZDldXwgG*y~c}Hke`vuV{89ain=KF7@{n9J)f0FdWa-we)YK-5| zk##d@L!8`L-=6ZCsb~|&d&;+~_o<(^ZQR{H_N^@hWxk3p#KZXI(&l z9dyqq?dCzBISRcD13K#W^n$5OI+Kk=a7}RJJ9S2;+rS6%;<$*2%~QhYj3qh(F(Z^o zq|!zpyNj-{C1c@CIMldg;R1Z-ZZskKavru@bUKeM4Q<#XDV&uWc)h3_dpDf77BGI2 zUL;EIEy1NOV|#cvZ4w9<`<=B{`g{VVZ4WeQqks*1S0an64C$S?MGy ze`he5&hkL=uMB#<>NA6X`{RN|-#Z*pTq0t}cSXF9;#DqYg!yhld>3&ZiZeOQ$CM;8?jiB4AJI~HsqOr`0^q4QS@2GVnO)@#$qCA)7H22yNO09oiMrr z@eqAek*+b&g&%`9Y20)3wUCjJ^8SmjKNh#bJ0o;Nq>#lSnPu|(lEn&!(UHb>l0FBC z&q>nfAnB8o#WLdil*KgD*C~q`^j*thiuf*NG0iYGZ(J=CkRl_ux%k}VsNaw*{+4C2 zDY26+rm@EK?rvOA0L-KW>{2ychX|*VtTCR*gd1B|t)G_(bV>~OJ{mi+QG5nA8e)xv zQ9M~=Xm=d;1jI!t&QVvD%!mPADjW-tf~+ND8P>>q*NAFkCoZBkX6S-PBdsqGO7O&O z40p*Dr3)8M3Ybup7ZtdMs-p#5{t3dDM20NGIoeJ)Vl-JJ-9ex$bB&>JN466xfjB-S zs!+cFR;-SWRCtHt;N!PpRY$l+S&08HNpyI^p%dq7qNshgg6i(EWOG?nCwLzIaQ~~I z|FSfspa(p2QOA8nn~#cF|Af?6u=F&yRDTWh@#>i6fBLO>i_Lhf0dL|*E0%Gc9`_B zOE@cj_kczsl;mx5B@V3bn8F z)&5Sw*OZ`yrR_goqOYz8DcDAn!&FM!e+4|cmZ8MdzEgc4yi{J{3erB`Wztvs+(EhV zR&o&GMd1qmJE)XA)lal9Wq5>fiVhJEAD0G8?Uu@pA4C7RRNO-s&`hONf94qa*RB*wv(?0qHcIuI z$I!p+QlaoA71%NK>B&8^S=!G}Rtbf>=-_B7s(mvoagj^kb(v5)CKr$yK*=SQH){PR jc8QBzaoc7NSVPH#SM3Te?fFL6-?c?ZtSeC{LD_!*kT!S5 literal 0 HcmV?d00001 diff --git a/hashtest/htest.c b/hashtest/htest.c new file mode 100644 index 0000000..a61da13 --- /dev/null +++ b/hashtest/htest.c @@ -0,0 +1,105 @@ +/* Wee testing program from the hash code: + * htest