diff --git a/ChangeLog b/ChangeLog index e6a0c34..19acec4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -9,6 +9,11 @@ vNEXT: message class can be set to go to stderr, syslog, or both. Classes are documented in example.cfg. + UDP connections are now managed in a hash to avoid + linear searches. The downside is that the number of + UDP connections is a hard limit, configurable with + the 'udp_max_connections', which defaults to 1024. + 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, diff --git a/echo_test.cfg b/echo_test.cfg new file mode 100644 index 0000000..5d88e70 --- /dev/null +++ b/echo_test.cfg @@ -0,0 +1,12 @@ + +# TODO: c2s does not warn if udp: 1 (instead of 'true') + +udp: true; + +prefix: "hello"; + +listen: "localhost:9000"; + +listen-host: "localhost"; +listen-port: "9000"; + diff --git a/echosrv-conf.c b/echosrv-conf.c index 25fddf4..2678eeb 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 Apr 1 19:34:31 2022. + * on Sun Apr 10 08:52:32 2022. # 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 a8ecc3f..70c7ceb 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 Apr 1 19:34:31 2022. + * on Sun Apr 10 08:52:32 2022. # conf2struct: generate libconf parsers that read to structs # Copyright (C) 2018-2021 Yves Rutschle diff --git a/echoѕrv-conf.h b/echoѕrv-conf.h new file mode 100644 index 0000000..58701a7 --- /dev/null +++ b/echoѕrv-conf.h @@ -0,0 +1,109 @@ +/* Generated by conf2struct (https://www.rutschle.net/tech/conf2struct/README) + * on Sat Nov 7 09:19:26 2020. + +# conf2struct: generate libconf parsers that read to structs +# Copyright (C) 2018-2019 Yves Rutschle +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef C2S_SSLHCFG_H +#define C2S_SSLHCFG_H +#ifdef LIBCONFIG +# include +#endif + + +#include "probe.h" +#include +#include +#include + +struct sslhcfg_listen_item { + char* host; + char* port; + int keepalive; +}; + +struct sslhcfg_protocols_item { + char* name; + char* host; + char* port; + int service_is_present; + char* service; + int fork; + int tfo_ok; + int log_level; + int keepalive; + size_t sni_hostnames_len; + char** sni_hostnames; + size_t alpn_protocols_len; + char** alpn_protocols; + size_t regex_patterns_len; + char** regex_patterns; + int minlength_is_present; + int minlength; + T_PROBE* probe; + struct addrinfo* saddr; + void* data; +}; + +struct sslhcfg_item { + char* prefix; + int verbose; + int foreground; + int inetd; + int numeric; + int transparent; + int timeout; + int user_is_present; + char* user; + int pidfile_is_present; + char* pidfile; + int chroot_is_present; + char* chroot; + char* syslog_facility; + char* on_timeout; + size_t listen_len; + struct sslhcfg_listen_item* listen; + size_t protocols_len; + struct sslhcfg_protocols_item* protocols; +}; + +int sslhcfg_parse_file( + const char* filename, + struct sslhcfg_item* sslhcfg, + const char** errmsg); + +void sslhcfg_fprint( + FILE* out, + struct sslhcfg_item *sslhcfg, + int depth); + +int sslhcfg_cl_parse( + int argc, + char* argv[], + struct sslhcfg_item *sslhcfg); + +#endif diff --git a/sslh-conf.c b/sslh-conf.c index 0d3268e..c196f5a 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 Apr 1 19:34:30 2022. + * on Sun Apr 10 08:52:30 2022. # conf2struct: generate libconf parsers that read to structs # Copyright (C) 2018-2021 Yves Rutschle @@ -460,6 +460,7 @@ struct arg_file* sslhcfg_conffile; struct arg_lit* sslhcfg_numeric; struct arg_lit* sslhcfg_transparent; struct arg_int* sslhcfg_timeout; + struct arg_int* sslhcfg_udp_max_connections; struct arg_str* sslhcfg_user; struct arg_str* sslhcfg_pidfile; struct arg_str* sslhcfg_chroot; @@ -809,7 +810,7 @@ static struct config_desc table_sslhcfg_listen[] = { }, { 0 } }; - + static struct config_desc table_sslhcfg[] = { @@ -1085,6 +1086,22 @@ static struct config_desc table_sslhcfg[] = { /* default_val*/ .default_val.def_int = 5 }, + { + /* name */ "udp_max_connections", + /* type */ CFG_INT, + /* sub_group*/ NULL, + /* arg_cl */ & sslhcfg_udp_max_connections, + /* base_addr */ NULL, + /* offset */ offsetof(struct sslhcfg_item, udp_max_connections), + /* offset_len */ 0, + /* offset_present */ 0, + /* size */ sizeof(int), + /* array_type */ -1, + /* mandatory */ 0, + /* optional */ 0, + /* default_val*/ .default_val.def_int = 1024 + }, + { /* name */ "user", /* type */ CFG_STRING, @@ -1309,7 +1326,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: listen */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_listen, - .base_entry = & table_sslhcfg [23], + .base_entry = & table_sslhcfg [24], .targets = sslhcfg_listen_targets, @@ -1321,7 +1338,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: ssh */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_ssh, - .base_entry = & table_sslhcfg [24], + .base_entry = & table_sslhcfg [25], .targets = sslhcfg_ssh_targets, @@ -1333,7 +1350,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: tls */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_tls, - .base_entry = & table_sslhcfg [24], + .base_entry = & table_sslhcfg [25], .targets = sslhcfg_tls_targets, @@ -1345,7 +1362,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: openvpn */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_openvpn, - .base_entry = & table_sslhcfg [24], + .base_entry = & table_sslhcfg [25], .targets = sslhcfg_openvpn_targets, @@ -1357,7 +1374,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: tinc */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_tinc, - .base_entry = & table_sslhcfg [24], + .base_entry = & table_sslhcfg [25], .targets = sslhcfg_tinc_targets, @@ -1369,7 +1386,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: xmpp */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_xmpp, - .base_entry = & table_sslhcfg [24], + .base_entry = & table_sslhcfg [25], .targets = sslhcfg_xmpp_targets, @@ -1381,7 +1398,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: http */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_http, - .base_entry = & table_sslhcfg [24], + .base_entry = & table_sslhcfg [25], .targets = sslhcfg_http_targets, @@ -1393,7 +1410,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: adb */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_adb, - .base_entry = & table_sslhcfg [24], + .base_entry = & table_sslhcfg [25], .targets = sslhcfg_adb_targets, @@ -1405,7 +1422,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: socks5 */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_socks5, - .base_entry = & table_sslhcfg [24], + .base_entry = & table_sslhcfg [25], .targets = sslhcfg_socks5_targets, @@ -1417,7 +1434,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: syslog */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_syslog, - .base_entry = & table_sslhcfg [24], + .base_entry = & table_sslhcfg [25], .targets = sslhcfg_syslog_targets, @@ -1429,7 +1446,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: anyprot */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_anyprot, - .base_entry = & table_sslhcfg [24], + .base_entry = & table_sslhcfg [25], .targets = sslhcfg_anyprot_targets, @@ -2111,6 +2128,7 @@ int sslhcfg_cl_parse(int argc, char* argv[], struct sslhcfg_item* cfg) sslhcfg_numeric = arg_litn("n", "numeric", 0, 1, "Print IP addresses and ports as numbers"), sslhcfg_transparent = arg_litn(NULL, "transparent", 0, 1, "Set up as a transparent proxy"), sslhcfg_timeout = arg_intn("t", "timeout", "", 0, 1, "Set up timeout before connecting to default target"), + sslhcfg_udp_max_connections = arg_intn(NULL, "udp-max-connections", "", 0, 1, "Number of concurrent UDP connections"), sslhcfg_user = arg_strn("u", "user", "", 0, 1, "Username to change to after set-up"), sslhcfg_pidfile = arg_strn("P", "pidfile", "", 0, 1, "Path to file to store PID of current instance"), sslhcfg_chroot = arg_strn("C", "chroot", "", 0, 1, "Root to change to after set-up"), @@ -2327,6 +2345,9 @@ void sslhcfg_fprint( fprintf(out, "timeout: %d", sslhcfg->timeout); fprintf(out, "\n"); indent(out, depth); + fprintf(out, "udp_max_connections: %d", sslhcfg->udp_max_connections); + fprintf(out, "\n"); + indent(out, depth); fprintf(out, "user: %s", sslhcfg->user); if (! sslhcfg->user_is_present) fprintf(out, " "); diff --git a/sslh-conf.h b/sslh-conf.h index 527b7fa..6a51b38 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 Apr 1 19:34:30 2022. + * on Sun Apr 10 08:52:30 2022. # conf2struct: generate libconf parsers that read to structs # Copyright (C) 2018-2021 Yves Rutschle @@ -92,6 +92,7 @@ struct sslhcfg_item { int numeric; int transparent; int timeout; + int udp_max_connections; int user_is_present; char* user; int pidfile_is_present; diff --git a/sslh-main.c b/sslh-main.c index 05acba2..48d1824 100644 --- a/sslh-main.c +++ b/sslh-main.c @@ -67,20 +67,25 @@ static void printsettings(void) for (i = 0; i < cfg.protocols_len; i++ ) { p = &cfg.protocols[i]; 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), - p->service, - p->log_level, - p->saddr->ai_family, - p->saddr->ai_addr->sa_family, - p->keepalive ? "keepalive" : "", - p->fork ? "fork" : "", - p->transparent ? "transparent" : "" - ); + "%s addr: %s. libwrap service: %s log_level: %d family %d %d [%s] [%s] [%s]\n", + p->name, + sprintaddr(buf, sizeof(buf), p->saddr), + p->service, + p->log_level, + p->saddr->ai_family, + p->saddr->ai_addr->sa_family, + p->keepalive ? "keepalive" : "", + p->fork ? "fork" : "", + p->transparent ? "transparent" : "" + ); } - print_message(msg_config, "timeout: %d\non-timeout: %s\n", cfg.timeout, - timeout_protocol()->name); + print_message(msg_config, + "timeout: %d\n" + "on-timeout: %s\n" + "UDP hash size: %d\n", + cfg.timeout, + timeout_protocol()->name, + cfg.udp_max_connections); } diff --git a/sslhconf.cfg b/sslhconf.cfg index f0aca35..9667e29 100644 --- a/sslhconf.cfg +++ b/sslhconf.cfg @@ -67,6 +67,8 @@ config: { { name: "timeout"; type: "int"; default: 5; short: "t"; description: "Set up timeout before connecting to default target"; }, + { name: "udp_max_connections"; type: "int"; default: 1024; + description: "Number of concurrent UDP connections"; }, { name: "user"; type: "string"; optional: true; short: "u"; description: "Username to change to after set-up"; }, diff --git a/udp-listener.c b/udp-listener.c index 0f72193..da0f4f6 100644 --- a/udp-listener.c +++ b/udp-listener.c @@ -27,9 +27,6 @@ #include "sslh-conf.h" #include "udp-listener.h" -/* How many concurrent connections we manage */ -#define HASH_SIZE 1024 - /* returns date at which this socket times out. */ static int udp_timeout(struct connection* cnx) { @@ -93,7 +90,7 @@ static int hash_make_key(hash_item new) * */ void udp_init(struct loop_info* fd_info) { - fd_info->hash_sources = hash_init(HASH_SIZE, &hash_make_key, &cnx_cmp); + fd_info->hash_sources = hash_init(cfg.udp_max_connections, &hash_make_key, &cnx_cmp); } @@ -228,7 +225,7 @@ int udp_c2s_forward(int sockfd, struct loop_info* fd_info) res = new_source(fd_info->hash_sources, cnx); if (res == -1) { - print_message(msg_connections_error, "Out of hash space for new incoming UDP connection"); + print_message(msg_connections_error, "Out of hash space for new incoming UDP connection -- increaѕe udp_max_connections"); collection_remove_cnx(collection, cnx); return -1; }