Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package sslh for openSUSE:Factory checked in at 2026-03-11 20:54:48 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/sslh (Old) and /work/SRC/openSUSE:Factory/.sslh.new.8177 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "sslh" Wed Mar 11 20:54:48 2026 rev:18 rq:1338189 version:2.3.1 Changes: -------- --- /work/SRC/openSUSE:Factory/sslh/sslh.changes 2025-09-12 21:11:19.551770544 +0200 +++ /work/SRC/openSUSE:Factory/.sslh.new.8177/sslh.changes 2026-03-11 20:56:24.508617649 +0100 @@ -1,0 +2,23 @@ +Wed Mar 11 05:20:34 UTC 2026 - Michael Vetter <[email protected]> + +- Update to 2.3.1: + * Add file access when using Landlock and Libwrap together. + * Mixed IPv4/IPv6 proxyprotocol setups will move both addresses to IPv6 + format on the server side. + * Add CAP_NET_RAW to systemd config so transparent + proxying can work. Removed sslh-select systemd file: + don't repeat the same configuration file! + * Changed SNI matching log messages from + `msg_probe_error` to `msg_probe_info`: beware if you + happen to use them... + * Close unused file descriptors upon forking a worker + for a connection (in sslh-ev and sslh-select). In + particular, this prevents forked processes to + inherit the listen sockets and block a new main + process from starting. + * More configuration sanity checks (e.g. using regex + or proxyprotocol options if libraries are not compiled in) + * -V now outputs compile-time options so it is + possible to know what is compiled in. + +------------------------------------------------------------------- Old: ---- v2.3.0.tar.gz New: ---- v2.3.1.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ sslh.spec ++++++ --- /var/tmp/diff_new_pack.mQsWn5/_old 2026-03-11 20:56:25.588662208 +0100 +++ /var/tmp/diff_new_pack.mQsWn5/_new 2026-03-11 20:56:25.592662373 +0100 @@ -1,7 +1,7 @@ # # spec file for package sslh # -# Copyright (c) 2025 SUSE LLC and contributors +# Copyright (c) 2026 SUSE LLC and contributors # Copyright (c) 2012 by Lars Vogdt # # All modifications and additions to the file contributed by third parties @@ -18,7 +18,7 @@ Name: sslh -Version: 2.3.0 +Version: 2.3.1 Release: 0 Summary: SSL/SSH multiplexer License: GPL-2.0-or-later @@ -57,7 +57,6 @@ make PREFIX=%{_prefix} DESTDIR=%{buildroot} install install -Dm644 scripts/[email protected] %{buildroot}%{_unitdir}/%{name}@.service -install -Dm644 scripts/[email protected] %{buildroot}%{_unitdir}/%{name}[email protected] install -Dm644 %{SOURCE3} %{buildroot}%{_sysconfdir}/conf.d/%{name} ln -sf %{_sbindir}/service %{buildroot}/%{_sbindir}/rc%{name} @@ -83,7 +82,6 @@ %{_sbindir}/%{name} %{_sbindir}/rc%{name} %{_unitdir}/%{name}@.service -%{_unitdir}/%{name}[email protected] %dir %{_sysconfdir}/conf.d %config(noreplace) %{_sysconfdir}/conf.d/%{name} %config(noreplace) %{_sysconfdir}/default/%{name} ++++++ v2.3.0.tar.gz -> v2.3.1.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sslh-2.3.0/ChangeLog new/sslh-2.3.1/ChangeLog --- old/sslh-2.3.0/ChangeLog 2025-09-10 15:50:52.000000000 +0200 +++ new/sslh-2.3.1/ChangeLog 2026-03-05 19:14:57.000000000 +0100 @@ -1,3 +1,32 @@ +v2.3.1: + Fix segmentation fault in sslh-fork. + + Add file access when using Landlock and Libwrap + together. + + Mixed IPv4/IPv6 proxyprotocol setups will move both + addresses to IPv6 format on the server side. + + Add CAP_NET_RAW to systemd config so transparent + proxying can work. Removed sslh-select systemd file: + don't repeat the same configuration file! + + Changed SNI matching log messages from + `msg_probe_error` to `msg_probe_info`: beware if you + happen to use them... + + Close unused file descriptors upon forking a worker + for a connection (in sslh-ev and sslh-select). In + particular, this prevents forked processes to + inherit the listen sockets and block a new main + process from starting. + + More configuration sanity checks (e.g. using regex + or proxyprotocol options if libraries are not compiled in) + + -V now outputs compile-time options so it is + possible to know what is compiled in. + v2.3.0: Added `max_connections` setting to `listen` and `protocol` configuration; see the diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sslh-2.3.0/Dockerfile new/sslh-2.3.1/Dockerfile --- old/sslh-2.3.0/Dockerfile 2025-09-10 15:50:52.000000000 +0200 +++ new/sslh-2.3.1/Dockerfile 2026-03-05 19:14:57.000000000 +0100 @@ -3,8 +3,7 @@ FROM docker.io/${TARGET_ARCH}/alpine:${ALPINE_VERSION} AS build -WORKDIR /sslh - +# Install build dependencies for sslh and libproxyprotocol RUN apk add --no-cache \ 'gcc' \ 'libconfig-dev' \ @@ -12,15 +11,39 @@ 'musl-dev' \ 'pcre2-dev' \ 'perl' \ + 'git' \ + 'autoconf' \ + 'automake' \ + 'libtool' \ ; +# Build libproxyprotocol +WORKDIR /tmp +RUN git clone https://github.com/kosmas-valianos/libproxyprotocol.git && \ + cd libproxyprotocol && \ + make && \ + mkdir -p /usr/local/include/ && \ + mv libs/libproxyprotocol.so /usr/local/lib/ && \ + mv src/* /usr/local/include/ + +# Set environment variables for sslh to find libproxyprotocol +ENV C_INCLUDE_PATH=/usr/local/include +ENV LIBRARY_PATH=/usr/local/lib +ENV LD_LIBRARY_PATH=/usr/local/lib + +WORKDIR /sslh COPY . /sslh +# Configure and build sslh +# The configure script should automatically detect libproxyprotocol RUN ./configure && make sslh-select && strip sslh-select FROM docker.io/${TARGET_ARCH}/alpine:${ALPINE_VERSION} +# Copy libproxyprotocol.so from the build stage +COPY --from=build /usr/local/lib/libproxyprotocol.so* /usr/local/lib/ COPY --from=build "/sslh/sslh-select" "/usr/local/bin/sslh" + RUN apk add --no-cache \ 'libconfig' \ 'pcre2' \ @@ -35,4 +58,4 @@ ENTRYPOINT [ "/init" ] # required for updating iptables -USER root:root +USER root:root \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sslh-2.3.0/Makefile.in new/sslh-2.3.1/Makefile.in --- old/sslh-2.3.0/Makefile.in 2025-09-10 15:50:52.000000000 +0200 +++ new/sslh-2.3.1/Makefile.in 2026-03-05 19:14:57.000000000 +0100 @@ -22,11 +22,11 @@ # itself ifneq ($(strip $(ENABLE_SANITIZER)),) - CFLAGS_SAN=-fsanitize=address -fsanitize=leak -fsanitize=undefined -fsanitize=alignment + CFLAGS_SAN=-DENABLE_SANITIZER -fsanitize=address -fsanitize=leak -fsanitize=undefined -fsanitize=alignment endif ifneq ($(strip $(COV_TEST)),) - CFLAGS_COV=-fprofile-arcs -ftest-coverage + CFLAGS_COV=-DCOV_TEST -fprofile-arcs -ftest-coverage endif CC ?= gcc diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sslh-2.3.0/collection.c new/sslh-2.3.1/collection.c --- old/sslh-2.3.0/collection.c 2025-09-10 15:50:52.000000000 +0200 +++ new/sslh-2.3.1/collection.c 2026-03-05 19:14:57.000000000 +0100 @@ -28,6 +28,7 @@ /* Info to keep track of all connections */ struct cnx_collection { gap_array* fd2cnx; /* Array indexed by file descriptor to things in cnx[] */ + int max_fd; }; /* Allocates and initialises a new collection of connections with at least @@ -42,6 +43,7 @@ memset(collection, 0, sizeof(*collection)); collection->fd2cnx = gap_init(len); + collection->max_fd = 0; return collection; } @@ -54,10 +56,17 @@ free(collection); } +int collection_max_fd(cnx_collection* collection) +{ + return collection->max_fd; +} + /* Points the file descriptor to the specified connection index */ int collection_add_fd(cnx_collection* collection, struct connection* cnx, int fd) { gap_set(collection->fd2cnx, fd, cnx); + if (fd > collection->max_fd) + collection->max_fd = fd; return 0; } @@ -74,8 +83,7 @@ cnx->state = ST_PROBING; cnx->probe_timeout = time(NULL) + cfg.timeout; - gap_set(collection->fd2cnx, fd, cnx); - + collection_add_fd(collection, cnx, fd); return cnx; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sslh-2.3.0/collection.h new/sslh-2.3.1/collection.h --- old/sslh-2.3.0/collection.h 2025-09-10 15:50:52.000000000 +0200 +++ new/sslh-2.3.1/collection.h 2026-03-05 19:14:57.000000000 +0100 @@ -9,6 +9,7 @@ struct connection* collection_alloc_cnx_from_fd(cnx_collection* collection, int fd); int collection_add_fd(cnx_collection* collection, struct connection* cnx, int fd); +int collection_max_fd(cnx_collection* collection); /* Remove a connection from the collection */ int collection_remove_cnx(cnx_collection* collection, struct connection *cnx); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sslh-2.3.0/common.c new/sslh-2.3.1/common.c --- old/sslh-2.3.0/common.c 2025-09-10 15:50:52.000000000 +0200 +++ new/sslh-2.3.1/common.c 2026-03-05 19:14:57.000000000 +0100 @@ -25,12 +25,6 @@ #include <sys/prctl.h> #endif -/* Added to make the code compilable under CYGWIN - * */ -#ifndef SA_NOCLDWAIT -#define SA_NOCLDWAIT 0 -#endif - /* Make use of systemd socket activation * */ #ifdef SYSTEMD @@ -428,9 +422,9 @@ cnx->proto->port); } for (a = cnx->proto->saddr; a; a = a->ai_next) { - /* When transparent or using proxyprotocol, make sure both - * connections use the same address family (e.g. IP4 on both sides) */ - if ((transparent || cnx->proto->proxyprotocol_is_present) && (a->ai_family != from.ai_addr->sa_family)) + /* When transparent, make sure both connections use + * the same address family (e.g. IP4 on both sides) */ + if (transparent && (a->ai_family != from.ai_addr->sa_family)) continue; print_message(msg_connections_try, "trying to connect to %s family %d len %d\n", sprintaddr(buf, sizeof(buf), a), @@ -905,7 +899,7 @@ } if (!hosts_ctl((char*)service, host, addr_str, STRING_UNKNOWN)) { - print_message(msg_connections, "connection from %s(%s): access denied", host, addr_str); + print_message(msg_connections, "connection from %s(%s): access denied\n", host, addr_str); close(in_socket); return -1; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sslh-2.3.0/common.h new/sslh-2.3.1/common.h --- old/sslh-2.3.0/common.h 2025-09-10 15:50:52.000000000 +0200 +++ new/sslh-2.3.1/common.h 2026-03-05 19:14:57.000000000 +0100 @@ -9,6 +9,13 @@ #define FD_SETSIZE 4096 #endif +/* Cygwin does not define some symbols + * */ +#ifndef SA_NOCLDWAIT +#define SA_NOCLDWAIT 0 +#endif + + #define _GNU_SOURCE #include <sys/types.h> #include <fcntl.h> @@ -197,7 +204,7 @@ #endif /* sslh-fork.c */ -void start_shoveler(int); +void main_inetd(void); void main_loop(struct listen_endpoint *listen_sockets, int num_addr_listen); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sslh-2.3.0/doc/proxyprotocol.md new/sslh-2.3.1/doc/proxyprotocol.md --- old/sslh-2.3.0/doc/proxyprotocol.md 2025-09-10 15:50:52.000000000 +0200 +++ new/sslh-2.3.1/doc/proxyprotocol.md 2026-03-05 19:14:57.000000000 +0100 @@ -27,7 +27,6 @@ ( { name: "ssh"; host: "localhost"; port: "2222"; }, { name: "tls"; host: "localhost"; port: "8080"; proxyprotocol: 2; } - ); ); ``` @@ -77,7 +76,6 @@ ( { name: "ssh"; host: "localhost"; port: "2222"; proxyprotocol: 0; }, { name: "tls"; host: "localhost"; port: "8080"; } - ); ); ``` diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sslh-2.3.0/landlock.c new/sslh-2.3.1/landlock.c --- old/sslh-2.3.0/landlock.c 2025-09-10 15:50:52.000000000 +0200 +++ new/sslh-2.3.1/landlock.c 2026-03-05 19:14:57.000000000 +0100 @@ -118,7 +118,7 @@ static int add_libwrap(int ruleset_fd) { /* Files for libwrap */ -#ifdef LIBWRAP +#ifdef HAVE_LIBWRAP add_path_ro(ruleset_fd, LL_FILE, "/etc/hosts.allow"); add_path_ro(ruleset_fd, LL_FILE, "/etc/hosts.deny"); #endif diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sslh-2.3.0/processes.c new/sslh-2.3.1/processes.c --- old/sslh-2.3.0/processes.c 2025-09-10 15:50:52.000000000 +0200 +++ new/sslh-2.3.1/processes.c 2026-03-05 19:14:57.000000000 +0100 @@ -196,7 +196,7 @@ return n + 1; } -void loop_init(struct loop_info* loop, int num_addr_listen) +void loop_init(struct loop_info* loop, struct listen_endpoint* listen_sockets, int num_addr_listen) { memset(loop, 0, sizeof(*loop)); loop->collection = collection_init(0); @@ -206,6 +206,7 @@ udp_init(loop); tcp_init(); + loop->listen_sockets = listen_sockets; loop->num_addr_listen = num_addr_listen; int max_forks = next_power_of_two(max_forking_connections()); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sslh-2.3.0/processes.h new/sslh-2.3.1/processes.h --- old/sslh-2.3.0/processes.h 2025-09-10 15:50:52.000000000 +0200 +++ new/sslh-2.3.1/processes.h 2026-03-05 19:14:57.000000000 +0100 @@ -23,6 +23,8 @@ hash* pid2proto; /* to follow which forked PID is processing what protocol for connection count */ watchers* watchers; + + struct listen_endpoint* listen_sockets; int num_addr_listen; /* How many listen endpoints do we have here */ cnx_collection* collection; /* Collection of connections linked to this loop */ @@ -32,7 +34,7 @@ struct connection* cnx_accept_process(struct loop_info* fd_info, struct listen_endpoint* listen_socket); int tidy_connection(struct connection *cnx, struct loop_info* fd_info); -void loop_init(struct loop_info* loop, int num_addr_listen); +void loop_init(struct loop_info* loop, struct listen_endpoint* listen_sockets, int num_addr_listen); void remember_child_data(struct loop_info* fd_info, struct connection* cnx, pid_t pid); @@ -47,4 +49,6 @@ void watcher_sigchld(struct loop_info* fd_info, struct connection* cnx, pid_t pid); +void close_listen_endpoints(struct loop_info* loop); + #endif diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sslh-2.3.0/proxyprotocol.c new/sslh-2.3.1/proxyprotocol.c --- old/sslh-2.3.0/proxyprotocol.c 2025-09-10 15:50:52.000000000 +0200 +++ new/sslh-2.3.1/proxyprotocol.c 2026-03-05 19:14:57.000000000 +0100 @@ -112,7 +112,7 @@ if (res == -1) return -1; pp_info_in_v1.address_family = family_to_pp(addr.ai_addr->sa_family); - res = get_info(cnx->q[1].fd, SOCK, + res = get_info(cnx->q[0].fd, SOCK, &addr, &pp_info_in_v1.dst_addr, &pp_info_in_v1.dst_port @@ -122,7 +122,7 @@ uint8_t *pp1_hdr = pp_create_hdr(pp_version, &pp_info_in_v1, &pp1_hdr_len, &error); if (!pp1_hdr) { - print_message(msg_system_error, "pp_create_hrd:%d:%s\n", error, pp_strerror(error)); + print_message(msg_system_error, "pp_create_hdr:%d:%s\n", error, pp_strerror(error)); return -1; } defer_write_before(&cnx->q[1], pp1_hdr, pp1_hdr_len); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sslh-2.3.0/scripts/[email protected] new/sslh-2.3.1/scripts/[email protected] --- old/sslh-2.3.0/scripts/[email protected] 2025-09-10 15:50:52.000000000 +0200 +++ new/sslh-2.3.1/scripts/[email protected] 1970-01-01 01:00:00.000000000 +0100 @@ -1,27 +0,0 @@ -[Unit] -Description=SSL/SSH multiplexer (select mode) for %I -After=network.target - -[Service] -EnvironmentFile=/etc/default/sslh -ExecStart=/usr/sbin/sslh-select -F/etc/sslh/%I.cfg -f $DAEMON_OPTS -KillMode=process -#Hardening -PrivateTmp=true -CapabilityBoundingSet=CAP_NET_BIND_SERVICE -AmbientCapabilities=CAP_NET_BIND_SERVICE -SecureBits=noroot-locked -ProtectSystem=strict -ProtectHome=true -ProtectKernelModules=true -ProtectKernelTunables=true -ProtectControlGroups=true -MountFlags=private -NoNewPrivileges=true -PrivateDevices=true -RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX -MemoryDenyWriteExecute=true -DynamicUser=true - -[Install] -WantedBy=multi-user.target diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sslh-2.3.0/scripts/[email protected] new/sslh-2.3.1/scripts/[email protected] --- old/sslh-2.3.0/scripts/[email protected] 2025-09-10 15:50:52.000000000 +0200 +++ new/sslh-2.3.1/scripts/[email protected] 2026-03-05 19:14:57.000000000 +0100 @@ -1,5 +1,5 @@ [Unit] -Description=SSL/SSH multiplexer (fork mode) for %I +Description=SSL/SSH multiplexer for %I After=network.target [Service] @@ -8,8 +8,8 @@ KillMode=process #Hardening PrivateTmp=true -CapabilityBoundingSet=CAP_NET_BIND_SERVICE -AmbientCapabilities=CAP_NET_BIND_SERVICE +CapabilityBoundingSet=CAP_SETGIT CAP_SETUID CAP_NET_BIND_SERVICE CAP_NET_RAW +AmbientCapabilities=CAP_NET_BIND_SERVICE CAP_NET_RAW SecureBits=noroot-locked ProtectSystem=strict ProtectHome=true diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sslh-2.3.0/sslh-ev.c new/sslh-2.3.1/sslh-ev.c --- old/sslh-2.3.0/sslh-ev.c 2025-09-10 15:50:52.000000000 +0200 +++ new/sslh-2.3.1/sslh-ev.c 2026-03-05 19:14:57.000000000 +0100 @@ -207,13 +207,12 @@ ev_child_start(EV_DEFAULT_ cw); } - void main_loop(struct listen_endpoint listen_sockets[], int num_addr_listen) { struct loop_info ev_info; loop = EV_DEFAULT; - loop_init(&ev_info, num_addr_listen); + loop_init(&ev_info, listen_sockets, num_addr_listen); watchers_init(&ev_info.watchers, listen_sockets, num_addr_listen); ev_set_userdata(EV_A_ &ev_info); @@ -221,7 +220,7 @@ ev_run(EV_A_ 0); } -void start_shoveler(int listen_socket) { +void main_inetd(void) { print_message(msg_config_error, "inetd mode is not supported in libev mode\n"); exit(1); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sslh-2.3.0/sslh-fork.c new/sslh-2.3.1/sslh-fork.c --- old/sslh-2.3.0/sslh-fork.c 2025-09-10 15:50:52.000000000 +0200 +++ new/sslh-2.3.1/sslh-fork.c 2026-03-05 19:14:57.000000000 +0100 @@ -69,7 +69,7 @@ /* Child process that finds out what to connect to and proxies */ -void start_shoveler(int in_socket) +static void start_shoveler(int in_socket, struct listen_endpoint* endpoint) { fd_set fds; struct timeval tv; @@ -79,6 +79,7 @@ init_cnx(&cnx); cnx.q[0].fd = in_socket; + cnx.endpoint = endpoint; FD_ZERO(&fds); FD_SET(in_socket, &fds); @@ -130,6 +131,22 @@ exit(0); } + +void main_inetd(void) +{ + struct listen_endpoint endpoint = {0}; + struct sslhcfg_listen_item endpoint_cfg = {0}; + + /* Empty configuration: no connection limits, not proxyprotocol... */ + endpoint.endpoint_cfg = &endpoint_cfg; + + close(fileno(stderr)); /* Make sure no error will go to client */ + tcp_init(); + start_shoveler(0, &endpoint); + exit(0); +} + + static pid_t *listener_pid; static int listener_pid_number = 0; @@ -252,7 +269,7 @@ /* Shoveler processes don't need to hog file descriptors */ for (i = 0; i < num_endpoints; i++) close(endpoint[i].socketfd); - start_shoveler(in_socket); + start_shoveler(in_socket, endpoint); exit(0); default: /* In parent process */ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sslh-2.3.0/sslh-main.c new/sslh-2.3.1/sslh-main.c --- old/sslh-2.3.0/sslh-main.c 2025-09-10 15:50:52.000000000 +0200 +++ new/sslh-2.3.1/sslh-main.c 2026-03-05 19:14:57.000000000 +0100 @@ -220,6 +220,62 @@ } } +static void config_sanity_regex(const struct sslhcfg_protocols_item* prot) +{ + if (prot->regex_patterns_len > 0) { + if (strcmp(prot->name, "regex") != 0) { + print_message(msg_config_error, "name: \"%s\"; host: \"%s\"; port: \"%s\": " + "regex_patterns setting is only applicable to `regex' probe. Ignoring setting.\n", + prot->name, prot->host, prot->port); + } +#ifndef ENABLE_REGEX + print_message(msg_config_error, "name: \"%s\"; host: \"%s\"; port: \"%s\": " + "Uses regex_patterns, but libpcre2 support was not compiled in. Ignoring setting.\n", + prot->name, prot->host, prot->port); +#endif + } +} + +/* If user specified proxyprotocol but it is not compiled in, it should not be + * ignored as the resulting configuration will most likely not work. In that + * case, die. */ +static void config_sanity_proxyprotocol(const struct sslhcfg_protocols_item* prot) +{ +#ifndef HAVE_PROXYPROTOCOL + if (prot->proxyprotocol_is_present) { + print_message(msg_config_error, "name: \"%s\"; host: \"%s\"; port: \"%s\": " + "Uses proxyprotocol, but libproxyprotocol support was not compiled in.\n", + prot->name, prot->host, prot->port); + exit(1); + } +#endif +} + +static void config_sanity_listen_proxyprotocol(const struct sslhcfg_listen_item* endpoint) +{ +#ifndef HAVE_PROXYPROTOCOL + if (endpoint->proxyprotocol) { + print_message(msg_config_error, "listen on host: \"%s\"; port: \"%s\": " + "Uses proxyprotocol, but libproxyprotocol support was not compiled in.\n", + endpoint->host, endpoint->port); + exit(1); + } +#endif +} + + +/* If user specifies libwrap setting but it is not compiled in, it should + * result in a working configuration, but not what they expected, so warn */ +static void config_sanity_libwrap(const struct sslhcfg_protocols_item* prot) +{ +#ifndef HAVE_LIBWRAP + if (prot->service_is_present) { + print_message(msg_config_error, "name: \"%s\"; host: \"%s\"; port: \"%s\": " + "WARNING: `service' specified but libwrap not compiled in. Ignoring setting.\n", + prot->name, prot->host, prot->port); + } +#endif +} void config_sanity_check(struct sslhcfg_item* cfg) { @@ -233,6 +289,10 @@ } #endif + for (i = 0; i < cfg->listen_len; i++) { + config_sanity_listen_proxyprotocol(&cfg->listen[i]); + } + for (i = 0; i < cfg->protocols_len; ++i) { if (strcmp(cfg->protocols[i].name, "tls") != 0) { if (cfg->protocols[i].sni_hostnames_len) { @@ -249,6 +309,10 @@ } } + config_sanity_regex(&cfg->protocols[i]); + config_sanity_proxyprotocol(&cfg->protocols[i]); + config_sanity_libwrap(&cfg->protocols[i]); + if (cfg->protocols[i].is_udp) { if (cfg->protocols[i].tfo_ok) { print_message(msg_config_error, "name: \"%s\"; host: \"%s\"; port: \"%s\": " @@ -284,6 +348,41 @@ } } +static void print_version(void) +{ + printf("%s %s\n", server_type, VERSION); +#ifdef ENABLE_SANITIZER + printf("ENABLE_SANITIZER\n"); +#endif +#ifdef ENABLE_REGEX + printf("ENABLE_REGEX\n"); +#endif +#ifdef LIBCONFIG + printf("LIBCONFIG\n"); +#endif +#ifdef SYSTEMD + printf("SYSTEMD\n"); +#endif +#ifdef COV_TEST + printf("COV_TEST\n"); +#endif +#ifdef HAVE_LIBWRAP + printf("HAVE_LIBWRAP\n"); +#endif +#ifdef HAVE_LANDLOCK + printf("HAVE_LANDLOCK\n"); +#endif +#ifdef HAVE_PROXYPROTOCOL + printf("HAVE_PROXYPROTOCOL\n"); +#endif +#ifdef HAVE_LIBCAP + printf("HAVE_LIBCAP\n"); +#endif +#ifdef HAVE_LIBBSD + printf("HAVE_LIBBSD\n"); +#endif +} + int main(int argc, char *argv[], char* envp[]) { int res, num_addr_listen; @@ -299,7 +398,7 @@ config_finish(&cfg); if (cfg.version) { - printf("%s %s\n", server_type, VERSION); + print_version(); exit(0); } @@ -308,10 +407,7 @@ if (cfg.inetd) { - close(fileno(stderr)); /* Make sure no error will go to client */ - tcp_init(); - start_shoveler(0); - exit(0); + main_inetd(); /* Does not return */ } printsettings(); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sslh-2.3.0/sslh-select.c new/sslh-2.3.1/sslh-select.c --- old/sslh-2.3.0/sslh-select.c 2025-09-10 15:50:52.000000000 +0200 +++ new/sslh-2.3.1/sslh-select.c 2026-03-05 19:14:57.000000000 +0100 @@ -204,7 +204,7 @@ int i, res; setup_sigalrm(); - loop_init(&fd_info, num_addr_listen); + loop_init(&fd_info, listen_sockets, num_addr_listen); watchers_init(&fd_info.watchers, listen_sockets, num_addr_listen); @@ -292,7 +292,7 @@ } -void start_shoveler(int listen_socket) { +void main_inetd(void) { print_message(msg_config_error, "inetd mode is not supported in select mode\n"); exit(1); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sslh-2.3.0/t/run new/sslh-2.3.1/t/run --- old/sslh-2.3.0/t/run 2025-09-10 15:50:52.000000000 +0200 +++ new/sslh-2.3.1/t/run 2026-03-05 19:14:57.000000000 +0100 @@ -10,6 +10,7 @@ # ./run # run all tests # ./run -l # list all tests # ./run 1 3 5 # run specified tests +# ./run --binary sslh-ev # run only one binary use strict; use IO::Socket::INET6; @@ -17,9 +18,13 @@ use Conf::Libconfig 1.0.3; use Getopt::Long; -my ($coverage, $list_tests); +# --cover: request test coverage analysis (not implemented) +# --binary: specify which binary to run (default: all) +# --list: list all available tests (do not run any) +my ($coverage, $list_tests, $param_binary); GetOptions( 'cover' => \$coverage, + 'binary=s' => \$param_binary, 'list' => \$list_tests, ); @@ -192,7 +197,8 @@ # Runs one test for one probe. Start echosrv's if required. # Connect to port specified in the test, otherwise to the -# first port in the sslh configuration. +# first port in the sslh configuration; to host specified in +# the test, otherwise to localhost # run sslh, connect, write the test pattern, read the result, # check it connected to the right echosrv, check the data # was transfered ok. @@ -206,9 +212,10 @@ my $expected = $test->{expected}; + my $sslh_host = $test->{host} // "localhost"; my $sslh_port = $test->{port} // $conf->value("listen")->[0]->{port}; - print "test_probe [$expected] $sslh_port\n"; - my $cnx = new IO::Socket::INET(PeerHost => "localhost:$sslh_port"); + print "test_probe [$expected] $sslh_host:$sslh_port\n"; + my $cnx = new IO::Socket::INET(PeerHost => "$sslh_host:$sslh_port"); warn "t: $!\n" unless $cnx; return unless $cnx; @@ -316,6 +323,7 @@ data_expected => "^PROXY TCP4 127.0.0.1 127.0.0.1 \\d+ \\d+\x0D\x0AINPUT_DATA", }, + # check socks5 does not { desc => "Server-side ProxyProtocol not active", @@ -325,6 +333,55 @@ expected => "socks5", }, + + # Check proxyprotocol from IPv4 to IPv4 works. + { + desc => "Server-side IPv4 to IPv4 ProxyProtocol active", + host => "ip4-localhost", + run => \&run_test_probe, + cfg => "test_pp3.cfg", + data => "SSH-2.0 hello", + expected => "ssh", + data_expected => "^PROXY TCP4 127.0.0.1 127.0.0.1 \\d+ 8080\x0D\x0AINPUT_DATA", + }, + + + # Check pp with IPv6 to IPv6 works. + { + desc => "Server-side IPv6 to IPv6 ProxyProtocol active", + host => "ip6-localhost", + run => \&run_test_probe, + cfg => "test_pp4.cfg", + data => "SSH-2.0 hello", + expected => "ssh", + data_expected => "^PROXY TCP6 ::ffff:127.0.0.1 ::ffff:127.0.0.1 \\d+ 8080\x0D\x0AINPUT_DATA", + }, + + + # Check pp with IPv4 to IPv6 works. + { + desc => "Server-side IPv4 to IPv6 ProxyProtocol active", + host => "ip4-localhost", + run => \&run_test_probe, + cfg => "test_pp5.cfg", + data => "SSH-2.0 hello", + expected => "ssh", + data_expected => "^PROXY TCP6 ::ffff:127.0.0.1 ::ffff:127.0.0.1 \\d+ 8080\x0D\x0AINPUT_DATA", + }, + + + # Check pp with IPv6 to IPv4 works. + { + desc => "Server-side IPv6 to IPv4 ProxyProtocol active", + host => "ip6-localhost", + run => \&run_test_probe, + cfg => "test_pp5.cfg", + data => "SSH-2.0 hello", + expected => "ssh", + data_expected => "^PROXY TCP6 ::ffff:127.0.0.1 ::ffff:127.0.0.1 \\d+ 8080\x0D\x0AINPUT_DATA", + }, + + # test_pp2.cfg has proxyprotocol on port 8080 { desc => "Client-side ProxyProtocol active", @@ -388,10 +445,15 @@ ################################################################################ # Run selected tests -foreach my $test (@tests) { - warn "Running test: $test->{desc}\n"; - my $binary = 'sslh-ev'; - my $code = ($test->{run})->($binary, $test); + +my @binaries = qw/sslh-fork sslh-select sslh-ev/; +@binaries = $param_binary if defined $param_binary; + +foreach my $binary (@binaries) { + foreach my $test (@tests) { + warn "Running test: $test->{desc}\n"; + my $code = ($test->{run})->($binary, $test); + } } stop_echosrv(); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sslh-2.3.0/t/test_pp1.cfg new/sslh-2.3.1/t/test_pp1.cfg --- old/sslh-2.3.0/t/test_pp1.cfg 2025-09-10 15:50:52.000000000 +0200 +++ new/sslh-2.3.1/t/test_pp1.cfg 2026-03-05 19:14:57.000000000 +0100 @@ -29,15 +29,15 @@ # Options: listen: ( - { host: "localhost"; port: "8080"; keepalive: true; } + { host: "ip4-localhost"; port: "8080"; keepalive: true; } ); protocols: ( - { name: "ssh"; host: "localhost"; port: "9000"; proxyprotocol: 1; }, - { name: "socks5"; host: "localhost"; port: "9001"; }, - { name: "anyprot"; host: "localhost"; port: "9099"; } + { name: "ssh"; host: "ip4-localhost"; port: "9000"; proxyprotocol: 1; }, + { name: "socks5"; host: "ip4-localhost"; port: "9001"; }, + { name: "anyprot"; host: "ip4-localhost"; port: "9099"; } ); on_timeout: "ssh"; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sslh-2.3.0/t/test_pp3.cfg new/sslh-2.3.1/t/test_pp3.cfg --- old/sslh-2.3.0/t/test_pp3.cfg 1970-01-01 01:00:00.000000000 +0100 +++ new/sslh-2.3.1/t/test_pp3.cfg 2026-03-05 19:14:57.000000000 +0100 @@ -0,0 +1,44 @@ +# Testing for server-side ProxyProtocol, IPv4 to IPv4 + +foreground: true; +inetd: false; +numeric: true; +transparent: false; +timeout: 10; # Probe test writes slowly +pidfile: "/tmp/sslh_test.pid"; + +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: 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: +listen: +( + { host: "ip4-localhost"; port: "8080"; keepalive: true; } +); + + +protocols: +( + { name: "ssh"; host: "ip4-localhost"; port: "9000"; proxyprotocol: 1; }, + { name: "socks5"; host: "localhost"; port: "9001"; }, + { name: "anyprot"; host: "localhost"; port: "9099"; } +); + +on_timeout: "ssh"; + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sslh-2.3.0/t/test_pp4.cfg new/sslh-2.3.1/t/test_pp4.cfg --- old/sslh-2.3.0/t/test_pp4.cfg 1970-01-01 01:00:00.000000000 +0100 +++ new/sslh-2.3.1/t/test_pp4.cfg 2026-03-05 19:14:57.000000000 +0100 @@ -0,0 +1,44 @@ +# Testing for server-side ProxyProtocol, IPv6 to IPv6 + +foreground: true; +inetd: false; +numeric: true; +transparent: false; +timeout: 10; # Probe test writes slowly +pidfile: "/tmp/sslh_test.pid"; + +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: 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: +listen: +( + { host: "ip6-localhost"; port: "8080"; keepalive: true; } +); + + +protocols: +( + { name: "ssh"; host: "ip6-localhost"; port: "9000"; proxyprotocol: 1; }, + { name: "socks5"; host: "ip6-localhost"; port: "9001"; }, + { name: "anyprot"; host: "ip6-localhost"; port: "9099"; } +); + +on_timeout: "ssh"; + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sslh-2.3.0/t/test_pp5.cfg new/sslh-2.3.1/t/test_pp5.cfg --- old/sslh-2.3.0/t/test_pp5.cfg 1970-01-01 01:00:00.000000000 +0100 +++ new/sslh-2.3.1/t/test_pp5.cfg 2026-03-05 19:14:57.000000000 +0100 @@ -0,0 +1,44 @@ +# Testing for server-side ProxyProtocol, IPv4 to IPv6 + +foreground: true; +inetd: false; +numeric: true; +transparent: false; +timeout: 10; # Probe test writes slowly +pidfile: "/tmp/sslh_test.pid"; + +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: 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: +listen: +( + { host: "ip4-localhost"; port: "8080"; keepalive: true; } +); + + +protocols: +( + { name: "ssh"; host: "ip6-localhost"; port: "9000"; proxyprotocol: 1; }, + { name: "socks5"; host: "ip6-localhost"; port: "9001"; }, + { name: "anyprot"; host: "ip6-localhost"; port: "9099"; } +); + +on_timeout: "ssh"; + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sslh-2.3.0/t/test_pp6.cfg new/sslh-2.3.1/t/test_pp6.cfg --- old/sslh-2.3.0/t/test_pp6.cfg 1970-01-01 01:00:00.000000000 +0100 +++ new/sslh-2.3.1/t/test_pp6.cfg 2026-03-05 19:14:57.000000000 +0100 @@ -0,0 +1,44 @@ +# Testing for server-side ProxyProtocol, IPv6 to IPv4 + +foreground: true; +inetd: false; +numeric: true; +transparent: false; +timeout: 10; # Probe test writes slowly +pidfile: "/tmp/sslh_test.pid"; + +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: 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: +listen: +( + { host: "ip6-localhost"; port: "8080"; keepalive: true; } +); + + +protocols: +( + { name: "ssh"; host: "ip4-localhost"; port: "9000"; proxyprotocol: 1; }, + { name: "socks5"; host: "ip6-localhost"; port: "9001"; }, + { name: "anyprot"; host: "ip6-localhost"; port: "9099"; } +); + +on_timeout: "ssh"; + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sslh-2.3.0/tcp-listener.c new/sslh-2.3.1/tcp-listener.c --- old/sslh-2.3.0/tcp-listener.c 2025-09-10 15:50:52.000000000 +0200 +++ new/sslh-2.3.1/tcp-listener.c 2026-03-05 19:14:57.000000000 +0100 @@ -296,6 +296,37 @@ } +/* This ensures the kernel will close the listening socket when the + * parent process closes; otherwise, the listening socket is kept + * around by the inactive file descriptor in the child */ +void close_listen_endpoints(struct loop_info* loop) +{ + struct listen_endpoint* listen_sockets = loop->listen_sockets; + watchers* w = loop->watchers; + + for (int i = 0; i < loop->num_addr_listen; i++) { + int fd = listen_sockets[i].socketfd; + watchers_del_read(w, fd); + watchers_del_write(w, fd); + close(fd); + } +} + + +/* Close all connections except specified. + * This saves file descriptors and allocated memory when forking */ +void tidy_other_connections(struct loop_info* fd_info, struct connection* keep_cnx) +{ + struct connection* cnx; + cnx_collection* cnx_coll = fd_info->collection; + + for (int i = 0; i < collection_max_fd(cnx_coll); i++) { + cnx = collection_get_cnx_from_fd(cnx_coll, i); + if (cnx && (cnx != keep_cnx)) + tidy_connection(cnx, fd_info); + } +} + /* Forks a shoveler process for one protocol */ void fork_shoveling_process(struct loop_info* fd_info, struct connection* cnx) { pid_t pid; @@ -307,8 +338,8 @@ switch (pid = fork()) { case 0: /* child */ - /* TODO: close all file descriptors except 2 */ - /* free(cnx); */ + close_listen_endpoints(fd_info); + tidy_other_connections(fd_info, cnx); connect_proxy(cnx); exit(0); case -1: print_message(msg_system_error, "fork failed: err %d: %s\n", errno, strerror(errno)); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sslh-2.3.0/tls.c new/sslh-2.3.1/tls.c --- old/sslh-2.3.0/tls.c 2025-09-10 15:50:52.000000000 +0200 +++ new/sslh-2.3.1/tls.c 2026-03-05 19:14:57.000000000 +0100 @@ -277,7 +277,7 @@ 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;
