diff --git a/ChangeLog b/ChangeLog index 07f61e1..0e96d4b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +vNEXT: + Added 'minlength' option to skip a probe if less + than that many bytes have been received (mostly for + regex) + + Moved configuration and command-line management to + use conf2struct. Hopefully this should be transparent + to users. + v1.20: 20NOV2018 Added support for socks5 protocol (Eugene Protozanov) diff --git a/example.cfg b/example.cfg index b5edd48..3a746d6 100644 --- a/example.cfg +++ b/example.cfg @@ -93,9 +93,11 @@ port: "dns" } ), # OpenVPN { name: "regex"; host: "localhost"; port: "1194"; regex_patterns: [ "^\x00[\x0D-\xFF]$", "^\x00[\x0D-\xFF]\x38" ]; }, # Jabber - { name: "regex"; host: "localhost"; port: "5222"; regex_patterns: [ "jabber" ]; }, + { name: "regex"; host: "localhost"; port: "5222"; regex_patterns: [ "jabber" ]; + minlength: 60; # Won't even try to match the regex if we don't have that many bytes + }, -# Catch-all +# Catch-all (but better use 'anyprot') { name: "regex"; host: "localhost"; port: "443"; regex_patterns: [ "" ]; }, # Where to connect in case of timeout (defaults to ssh) diff --git a/probe.c b/probe.c index aaf4ac8..ed54426 100644 --- a/probe.c +++ b/probe.c @@ -1,7 +1,7 @@ /* # probe.c: Code for probing protocols # -# Copyright (C) 2007-2015 Yves Rutschle +# Copyright (C) 2007-2019 Yves Rutschle # # This program is free software; you can redistribute it # and/or modify it under the terms of the GNU General Public @@ -360,11 +360,18 @@ int probe_client_protocol(struct connection *cnx) if (! p->probe) continue; + if (cfg.verbose) fprintf(stderr, "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 (cfg.verbose) fprintf(stderr, "probing for %s\n", p->name); + if (p->minlength_is_present && (cnx->q[1].deferred_data_size < p->minlength )) { + fprintf(stderr, "input too short, %d bytes but need %d\n", cnx->q[1].deferred_data_size , p->minlength); + again++; + continue; + } + res = p->probe(cnx->q[1].begin_deferred_data, cnx->q[1].deferred_data_size, p); if (cfg.verbose) fprintf(stderr, "probed for %s: %s\n", p->name, probe_str[res]); diff --git a/sslh-conf.c b/sslh-conf.c index 1a8136e..a9e98ac 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 Jan 27 18:51:35 2019. */ + * on Sun Feb 3 11:11:15 2019. */ #define _GNU_SOURCE #include @@ -12,8 +12,6 @@ static void sslhcfg_protocols_init(struct sslhcfg_protocols_item* cfg) { cfg->fork = 0; cfg->log_level = 1; cfg->keepalive = 0; - cfg->minlength = 0; - cfg->maxlength = 0; } static void sslhcfg_listen_init(struct sslhcfg_listen_item* cfg) { @@ -145,13 +143,10 @@ static int sslhcfg_protocols_parser( if (config_setting_lookup_int(cfg, "minlength", &sslhcfg_protocols->minlength) == CONFIG_FALSE) { *errmsg = "Parsing of option \"minlength\" failed"; return 0; - } ; - } - if (config_setting_lookup(cfg, "maxlength")) { - if (config_setting_lookup_int(cfg, "maxlength", &sslhcfg_protocols->maxlength) == CONFIG_FALSE) { - *errmsg = "Parsing of option \"maxlength\" failed"; - return 0; - } ; + } else { + sslhcfg_protocols->minlength_is_present = 1; + } +; } return 1; } @@ -385,8 +380,6 @@ static void sslhcfg_protocols_print( } indent(depth); printf("minlength: %d\n", sslhcfg_protocols->minlength); - indent(depth); - printf("maxlength: %d\n", sslhcfg_protocols->maxlength); } static void sslhcfg_listen_print( diff --git a/sslh-conf.h b/sslh-conf.h index 551fa89..17c1295 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 Jan 27 18:51:35 2019. */ + * on Sun Feb 3 11:11:15 2019. */ #ifndef C2S_SSLHCFG_H #define C2S_SSLHCFG_H @@ -31,8 +31,8 @@ struct sslhcfg_protocols_item { const char** alpn_protocols; size_t regex_patterns_len; const char** regex_patterns; + int minlength_is_present; int minlength; - int maxlength; T_PROBE* probe; struct addrinfo* saddr; void* data; diff --git a/sslhconf.cfg b/sslhconf.cfg index e77e378..9942ad8 100644 --- a/sslhconf.cfg +++ b/sslhconf.cfg @@ -80,9 +80,7 @@ config: { type: "array", element_type: "string" }, - { name: "minlength"; type: "int"; default: 0 }, - { name: "maxlength"; type: "int"; default: 0 }, - + { name: "minlength"; type: "int"; optional: true }, # Runtime data { name: "probe"; type: "runtime"; c_type: "T_PROBE*" }, diff --git a/t b/t index 057e4d5..7b071ce 100755 --- a/t +++ b/t @@ -31,11 +31,11 @@ my $SSH_MIX_SSL = 1; # coverage, but do not necessarily result in an actual test # (e.g. some tests need to be run with valgrind to check all # memory management code). -my $RB_CNX_NOSERVER = 0; -my $RB_PARAM_NOHOST = 0; -my $RB_WRONG_USERNAME = 0; -my $RB_OPEN_PID_FILE = 0; -my $RB_RESOLVE_ADDRESS = 0; +my $RB_CNX_NOSERVER = 1; +my $RB_PARAM_NOHOST = 1; +my $RB_WRONG_USERNAME = 1; +my $RB_OPEN_PID_FILE = 1; +my $RB_RESOLVE_ADDRESS = 1; `lcov --directory . --zerocounters`; @@ -162,12 +162,10 @@ sub test_probes { } } } elsif ($p->{name} eq 'regex') { - foreach my $pattern (@{$p->{regex_patterns}}) { - $pattern =~ /(\w+)/; - my $out = $1; + foreach my $test (@{$p->{test_patterns}}) { test_probe( - data => $out, - expected => $p->{name}, + data => $test->{pattern}, + expected => $test->{result}, %opts ); } @@ -199,6 +197,7 @@ foreach my $s (@{$conf->fetch_array("protocols")}) { my @binaries = ('sslh-select', 'sslh-fork'); + for my $binary (@binaries) { warn "Testing $binary\n"; @@ -206,10 +205,9 @@ for my $binary (@binaries) { my $sslh_pid; if (!($sslh_pid = fork)) { my $user = (getpwuid $<)[0]; # Run under current username - #my $cmd = "./$binary -v -f -u $user --listen localhost:$sslh_port --ssh $ssh_address --ssl $ssl_address -P $pidfile"; - my $cmd = "./$binary -v -f -u $user -Ftest.cfg"; + my $cmd = "./$binary -v 3 -f -u $user -Ftest.cfg"; verbose_exec $cmd; - #exec "valgrind --leak-check=full ./$binary -v -f -u $user --listen localhost:$sslh_port --ssh $ssh_address -ssl $ssl_address -P $pidfile"; + #exec "valgrind --leak-check=full ./$binary -v 3 -f -u $user --listen localhost:$sslh_port --ssh $ssh_address -ssl $ssl_address -P $pidfile"; exit 0; } warn "spawned $sslh_pid\n"; @@ -294,7 +292,7 @@ if ($RB_CNX_NOSERVER) { print "***Test: Connecting to non-existant server\n"; my $sslh_pid; if (!($sslh_pid = fork)) { - exec "./sslh-select -v -f -u $user --listen localhost:$sslh_port --ssh localhost:$no_listen --ssl localhost:$no_listen -P $pidfile"; + exec "./sslh-select -v 3 -f -u $user --listen localhost:$sslh_port --ssh localhost:$no_listen --tls localhost:$no_listen -P $pidfile"; } warn "spawned $sslh_pid\n"; @@ -327,7 +325,7 @@ if ($RB_PARAM_NOHOST) { print "***Test: No hostname in address\n"; my $sslh_pid; if (!($sslh_pid = fork)) { - exec "./sslh-select -v -f -u $user --listen $sslh_port --ssh $ssh_address --ssl $ssl_address -P $pidfile"; + exec "./sslh-select -v 3 -f -u $user --listen $sslh_port --ssh $ssh_address --tls $ssl_address -P $pidfile"; } warn "spawned $sslh_pid\n"; waitpid $sslh_pid, 0; @@ -341,7 +339,7 @@ if ($RB_WRONG_USERNAME) { print "***Test: Changing to non-existant username\n"; my $sslh_pid; if (!($sslh_pid = fork)) { - exec "./sslh-select -v -f -u ${user}_doesnt_exist --listen localhost:$sslh_port --ssh $ssh_address --ssl $ssl_address -P $pidfile"; + exec "./sslh-select -v 3 -f -u ${user}_doesnt_exist --listen localhost:$sslh_port --ssh $ssh_address --tls $ssl_address -P $pidfile"; } warn "spawned $sslh_pid\n"; waitpid $sslh_pid, 0; @@ -355,7 +353,7 @@ if ($RB_OPEN_PID_FILE) { print "***Test: Can't open PID file\n"; my $sslh_pid; if (!($sslh_pid = fork)) { - exec "./sslh-select -v -f -u $user --listen localhost:$sslh_port --ssh $ssh_address --ssl $ssl_address -P /dont_exist/$pidfile"; + exec "./sslh-select -v 3 -f -u $user --listen localhost:$sslh_port --ssh $ssh_address --tls $ssl_address -P /dont_exist/$pidfile"; # You don't have a /dont_exist/ directory, do you?! } warn "spawned $sslh_pid\n"; @@ -371,7 +369,7 @@ if ($RB_RESOLVE_ADDRESS) { my $sslh_pid; if (!($sslh_pid = fork)) { my $user = (getpwuid $<)[0]; # Run under current username - exec "./sslh-select -v -f -u $user --listen blahblah.dontexist:9000 --ssh $ssh_address --ssl $ssl_address -P $pidfile"; + exec "./sslh-select -v 3 -f -u $user --listen blahblah.dontexist:9000 --ssh $ssh_address --tls $ssl_address -P $pidfile"; } warn "spawned $sslh_pid\n"; waitpid $sslh_pid, 0; diff --git a/test.cfg b/test.cfg index 55a5e53..44790ea 100644 --- a/test.cfg +++ b/test.cfg @@ -31,7 +31,15 @@ protocols: { name: "xmpp"; host: "localhost"; port: "9009"; }, { name: "adb"; host: "localhost"; port: "9010"; }, { name: "regex"; host: "localhost"; port: "9011"; - regex_patterns: [ "^foo", "^bar" ]; + regex_patterns: [ "^foo", "^bar" ]; + minlength: 4; + test_patterns: ( + { pattern: "foo"; result: "ssh"; }, # After timeout + { pattern: "fooo"; result: "regex"; }, + { pattern: "bar"; result: "ssh"; }, + { pattern: "barr"; result: "regex"; }, + { pattern: "barrrr"; result: "regex"; } + ); }, { name: "tls"; host: "localhost"; port: "9021"; alpn_protocols: [ "alpn1", "alpn2" ]; sni_hostnames: [ "sni1" ]; }, { name: "tls"; host: "localhost"; port: "9022"; alpn_protocols: [ "alpn1", "alpn2" ]; sni_hostnames: [ "sni2", "sni3" ]; },