Compare commits

..

36 Commits

Author SHA1 Message Date
yrutschle
8aa97d7118 Include OpenSUSE's security review 2025-08-06 09:55:19 +02:00
yrutschle
8ad8bfdb5d fix link 2025-08-06 09:44:15 +02:00
yrutschle
4c2e4e01f2 document new settings in ChangeLog 2025-08-06 09:42:37 +02:00
yrutschle
2ecb681277 fix warning on void return 2025-08-06 09:38:52 +02:00
yrutschle
54dc8374ab properly define function in header 2025-08-06 09:37:07 +02:00
yrutschle
8c4253f145 refactor: rename proto inc and dec functions for clarity 2025-08-06 09:36:39 +02:00
yrutschle
4483e9c7e5 support for max_connections on listen_endpoint for sslh-ev/sslh-select 2025-08-06 09:32:19 +02:00
yrutschle
65c9c4ce95 count protocol connections even if no maximum is set 2025-08-06 08:00:35 +02:00
yrutschle
5434dc59df fix tags target for Debian's version of ctags 2025-08-05 18:21:48 +02:00
yrutschle
20290bbfa6 sslh-fork can now limit the number of concurrent connections for each incoming port 2025-08-05 18:15:58 +02:00
Yves Rutschle
1dfd70c20c Merge branch 'master' of github.com:yrutschle/sslh 2025-07-24 19:49:01 +02:00
Yves Rutschle
feaf528a60 add setting for maximum connection per protocol 2025-07-24 19:48:50 +02:00
Sergei Zhmylev
11da63cf4e Fix mistype in accept(2) error handling
Due to a mistype in errno checking, sslh-fork process died repeatedly
resulting to a complete denial of service on systems with no built-in
process supervisors, like FreeBSD.

Signed-off-by: Sergei Zhmylev <zhmylove@narod.ru>
2025-06-30 21:57:12 +02:00
Yves Rutschle
28ea73301e new gap feature: hardlimit, with test suite 2025-06-22 16:45:50 +02:00
Adrian Bunk
cd9b85fd4f Add a missing change in the second part of the CVE-2025-46806 fix 2025-06-19 22:34:30 +02:00
Yves Rutschle
bd9ed060e4 fix memory leak in regex probe (fix #500) 2025-06-19 22:31:02 +02:00
Yves Rutschle
b7556c07bd added podman transparent setup (fix #448) 2025-05-29 16:07:57 +02:00
Yves Rutschle
6b9ec3a46e remove obsolete getopt definitions (fix #489) 2025-05-29 15:46:08 +02:00
Yves Rutschle
ad66e79f46 Fix proxyprotocol target field (#496) 2025-05-29 15:23:10 +02:00
Yves Rutschle
b12220e640 v2.2.4 ChangeLog 2025-05-28 17:51:04 +02:00
Yves Rutschle
204305a88f fix unaligned read in OpenVPN UDP probe 2025-05-25 11:27:26 +02:00
Yves Rutschle
0f96ed8adb fix test script for new command line behaviour 2025-05-25 11:27:05 +02:00
Yves Rutschle
43e75a0a8c upgrade API to Conf::Libconfig 1.0.3 2025-05-22 22:19:00 +02:00
Yves Rutschle
ad1f5d68e9 fix potential parsing of undefined data is syslog probe and make probe more robust 2025-05-09 17:08:30 +02:00
Yves Rutschle
ff8206f7c8 fix segmentation fault upon too many UDP connection. Also, accepts multiple connection in one cycle 2025-05-08 17:58:51 +02:00
Yves Rutschle
8298daf686 change magic numbers for constants 2025-05-06 22:12:29 +02:00
Yves Rutschle
168477ea34 Merge branch 'master' of github.com:yrutschle/sslh 2025-05-04 11:49:45 +02:00
Yves Rutschle
b5d4d4662b v2.2.3 ChangeLog 2025-05-04 11:47:44 +02:00
Yves Rutschle
6e6d94752a Revert "include version.h in repo"
This reverts commit acdbb79d43ca867ccc86a340d9cc90858e38059f.
2025-05-04 11:45:33 +02:00
Yves Rutschle
b2bcfc26b2 version.h 2025-05-04 11:33:58 +02:00
Yves Rutschle
8fb62445c4 v2.2.2 ChangeLog 2025-05-04 11:28:11 +02:00
Yves Rutschle
c0cc45975c clean up inline define 2025-04-15 22:27:27 +02:00
npt-1707
0fe9bd5a95 common.c: Fix that symlink does not interferer 2025-04-15 22:20:26 +02:00
Yves Rutschle
fe25928e18 Merge branch 'master' of github.com:yrutschle/sslh 2025-04-08 21:41:23 +02:00
Sergey Fedorov
eccf7dbdc4 common.h: add a declaration of hosts_ctl for macOS
Fixes: https://github.com/yrutschle/sslh/issues/492
2025-04-08 21:40:58 +02:00
Yves Rutschle
e0bcf282ff include config.h. unclear how this worked without that 2025-04-08 21:36:12 +02:00
42 changed files with 3882 additions and 97 deletions

1
.gitignore vendored
View File

@ -10,6 +10,7 @@ sslh-ev
systemd-sslh-generator
sslh.8.gz
tags
version.h
/config.status
/config.log
/config.h

View File

@ -1,3 +1,36 @@
vNEXT:
Added `max_connections` setting to `listen` and
`protocol` configuration; see the
[guide](doc/max_connections.md) for more
information.
(still missing: protocol count for forked protocols
in sslh-[ev|select])
Fix proxyprotocol target field (Thanks to Github user 404-Not-Found)
Fix memory leak in regex probe.
v2.2.4:
Fix CVE-2025-46806 for "Misaligned Memory Accesses in `is_openvpn_protocol()`"
Fix CVE-2025-46807 for "File Descriptor Exhaustion in sslh-select and sslh-ev"
Fix potential parsing of undefined data in syslog
probe (no CVE assigned)
Thanks to Matthias Gerstner of the SUSE security
team for the security review that found these
defects!
v2.2.3:
Reverse older commit: version.h cannot be included
without breaking the build (everything recompiles
every time) and the release archive creation (which
relies on git tags).
v2.2.2:
Fix potential vulnerability similar to CVE-2020-28935
v2.2.1:
Added a boolean setting "is_unix" for listen and
protocol entries. This will use the 'host' setting

View File

@ -22,7 +22,7 @@ MAN=sslh.8.gz # man page name
# itself
ifneq ($(strip $(ENABLE_SANITIZER)),)
CFLAGS_SAN=-fsanitize=address -fsanitize=leak -fsanitize=undefined
CFLAGS_SAN=-fsanitize=address -fsanitize=leak -fsanitize=undefined -fsanitize=alignment
endif
ifneq ($(strip $(COV_TEST)),)
@ -70,9 +70,8 @@ all: sslh-fork sslh-select $(MAN) echosrv $(CONDITIONAL_TARGETS)
$(OBJS_A): $(OBJS)
$(AR) rcs $(OBJS_A) $(OBJS)
version.h: .FORCE
version.h:
./genver.sh >version.h
.FORCE:
$(OBJS) $(FORK_OBJS) $(SELECT_OBJS) $(EV_OBJS): argtable3.h collection.h common.h gap.h hash.h log.h probe.h processes.h sslh-conf.h tcp-listener.h tcp-probe.h tls.h udp-listener.h version.h
@ -145,10 +144,9 @@ distclean: clean
clean:
rm -f sslh-fork sslh-select $(CONDITIONAL_TARGETS) echosrv version.h $(MAN) systemd-sslh-generator *.o *.gcov *.gcno *.gcda *.png *.html *.css *.info
echo "// this is a placeholder for version.h, to make code-checking editors happy" > version.h
tags:
ctags --globals -T *.[ch]
tags: *.c *.h
ctags *.[ch]
cscope:
-find . -name "*.[chS]" >cscope.files

View File

@ -30,6 +30,20 @@ Install
Please refer to the [install guide](doc/INSTALL.md).
Security
========
Matthias Gerstner from OpenSUSE has performed a code review
of `sslh` from a security point of view, which revealed a
number of problems, including two CVE. His findings have
already been taken partly into account for the more critical
ones. The [full
review](https://security.opensuse.org/2025/06/13/sslh-denial-of-service-vulnerabilities.html)
is well worth reading if you are using `sslh` in production.
Part of the securing your installation involves configuring
connection limits. This is described in [this
guide](doc/max_connections.md).
Configuration
=============
@ -59,6 +73,7 @@ interfaces. The principle and basic setup is described
[here](doc/simple_transparent_proxy.md), with further
scenarios described [there](doc/scenarios-for-simple-transparent-proxy.md).
There is also a guide to use [podman](doc/podman.md).
Another method uses iptable packet marking features, and is
highly dependent on your network environment and

View File

@ -185,6 +185,7 @@ static int start_listen_inet(struct listen_endpoint *sockfd[], int num_addr, str
(*sockfd)[num_addr-1].socketfd = listen_single_addr(addr, cfg->keepalive, cfg->is_udp);
(*sockfd)[num_addr-1].type = cfg->is_udp ? SOCK_DGRAM : SOCK_STREAM;
(*sockfd)[num_addr-1].family = AF_INET;
(*sockfd)[num_addr-1].endpoint_cfg = cfg;
print_message(msg_config, "%d:\t%s\t[%s] [%s]\n", (*sockfd)[num_addr-1].socketfd, sprintaddr(buf, sizeof(buf), addr),
cfg->keepalive ? "keepalive" : "",
cfg->is_udp ? "udp" : "");
@ -219,6 +220,7 @@ static int start_listen_unix(struct listen_endpoint *sockfd[], int num_addr, str
(*sockfd)[num_addr-1].socketfd = fd;
(*sockfd)[num_addr-1].type = cfg->is_udp ? SOCK_DGRAM : SOCK_STREAM;
(*sockfd)[num_addr-1].family = AF_INET;
(*sockfd)[num_addr-1].endpoint_cfg = cfg;
return num_addr;
}
@ -595,6 +597,37 @@ void dump_connection(struct connection *cnx)
}
/* *cnx must have its proto field probed already.
* If required, increment the connection count for this protocol.
* Returns 1 if connection count is exceeded, 0 otherwise */
int inc_proto_connections(struct connection* cnx)
{
cnx->proto->num_connections++;
if (cnx->proto->max_connections_is_present) {
print_message(msg_connections, "Proto %s +1: %d/%d cnx\n",
cnx->proto->name,
cnx->proto->num_connections,
cnx->proto->max_connections);
if (cnx->proto->num_connections > cnx->proto->max_connections) {
print_message(msg_connections_error, "%s: too many connections, dropping", cnx->proto->name);
return 1;
}
}
return 0;
}
void dec_proto_connections(struct connection* cnx)
{
if (cnx->proto) {
cnx->proto->num_connections--;
print_message(msg_connections, "Proto %s -1: %d/%d cnx\n",
cnx->proto->name,
cnx->proto->num_connections,
cnx->proto->max_connections);
}
}
/*
* moves data from one fd to other
*
@ -849,16 +882,23 @@ int check_access_rights(int in_socket, const char* service)
return 0;
}
volatile sig_atomic_t received_sigchld;
void sig_sigchld(int sig)
{
received_sigchld = 1;
}
void setup_signals(void)
{
int res;
struct sigaction action;
/* Request no SIGCHLD is sent upon termination of
* the children */
/* Set SIGCHLD to sig_sigchld() */
memset(&action, 0, sizeof(action));
action.sa_handler = NULL;
action.sa_flags = SA_NOCLDWAIT;
action.sa_handler = sig_sigchld;
action.sa_flags = SA_NOCLDSTOP;
res = sigaction(SIGCHLD, &action, NULL);
CHECK_RES_DIE(res, "sigaction");
@ -986,26 +1026,48 @@ void drop_privileges(const char* user_name, const char* chroot_path)
}
}
#ifndef O_NOFOLLOW
#define O_NOFOLLOW 0
#endif
/* Writes my PID */
void write_pid_file(const char* pidfile)
{
FILE *f;
int res;
int fd;
char pidbuf[32];
size_t len, written = 0;
ssize_t res;
f = fopen(pidfile, "w");
if (!f) {
/* Format PID as string */
len = snprintf(pidbuf, sizeof(pidbuf), "%d\n", getpid());
if (len >= sizeof(pidbuf)) {
print_message(msg_system_error, "write_pid_file: PID string too long\n");
return;
}
/* Open file with O_NOFOLLOW to prevent symlink attacks (Similar to CVE-2020-28935) */
fd = open(pidfile, O_WRONLY | O_CREAT | O_TRUNC | O_NOFOLLOW ,0644);
if (fd == -1) {
print_message(msg_system_error, "write_pid_file: %s: %s\n", pidfile, strerror(errno));
return;
}
res = fprintf(f, "%d\n", getpid());
if (res < 0) {
print_message(msg_system_error, "write_pid_file: fprintf: %s\n", strerror(errno));
/* Write PID to file with proper error handling */
while (written < len) {
res = write(fd, pidbuf + written, len - written);
if (res == -1) {
if (errno == EINTR || errno == EAGAIN)
continue;
print_message(msg_system_error, "write_pid_file: write: %s\n", strerror(errno));
break;
}
written += res;
}
res = fclose(f);
if (res == EOF) {
print_message(msg_system_error, "write_pid_file: fclose: %s\n", strerror(errno));
return;
/* Close file */
if (close(fd) == -1) {
print_message(msg_system_error, "write_pid_file: close: %s\n", strerror(errno));
}
}

View File

@ -34,6 +34,10 @@
#include <sys/capability.h>
#endif
#ifdef __APPLE__
#include <AvailabilityMacros.h>
#endif
#include "config.h"
#include "version.h"
@ -105,6 +109,7 @@ struct connection {
struct sslhcfg_protocols_item* proto; /* Where to connect to */
/* SOCK_STREAM */
struct listen_endpoint* endpoint; /* Client-facing listen fd */
enum connection_state state;
time_t probe_timeout;
@ -134,6 +139,8 @@ struct listen_endpoint {
int socketfd; /* file descriptor of listening socket */
int type; /* SOCK_DGRAM | SOCK_STREAM */
int family; /* AF_INET | AF_UNIX */
int num_connections; /* How many active connections on this endpoint */
struct sslhcfg_listen_item* endpoint_cfg; /* the configuration item that corresponds to this endpoint */
};
#define FD_CNXCLOSED 0
@ -170,6 +177,8 @@ 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 dump_connection(struct connection *cnx);
int inc_proto_connections(struct connection* cnx);
void dec_proto_connections(struct connection* cnx);
int resolve_split_name(struct addrinfo **out, char* hostname, char* port);
int start_listen_sockets(struct listen_endpoint *sockfd[]);
@ -182,6 +191,10 @@ extern struct sslhcfg_item cfg;
extern struct addrinfo *addr_listen;
extern const char* server_type;
#if defined(__APPLE__) && (MAC_OS_X_VERSION_MIN_REQUIRED < 1080)
extern int hosts_ctl();
#endif
/* sslh-fork.c */
void start_shoveler(int);
@ -191,4 +204,7 @@ void main_loop(struct listen_endpoint *listen_sockets, int num_addr_listen);
void setup_landlock(void);
/* sslh-fork.c or processes.c, depending on the model */
void setup_sigchld(void);
#endif

View File

@ -200,3 +200,12 @@ process both single-datagram protocols such as DNS, and
connection-based protocols such as QUIC.
An example for supporting QUIC is shown in `example.cfg`.
Limiting file descriptor consumption
------------------------------------
There are various mechanisms to limit the number of
concurrent connections, which allows to limit the usage of
file descriptor. This is described in a separate
[guide](max_connections.md).

55
doc/max_connections.md Normal file
View File

@ -0,0 +1,55 @@
Limiting the number of connections
----------------------------------
As a network-facing service, `sslh` can be attacked by very
simple denial-of-service attacks by creating a lot of
concurrent connections. Protecting against such attacks is
complicated from the server side. This attack will cause
`sslh` to create a large number of file handles, which can
cause exgagerated resource consumption.
This threat can be mitigated using several limitation
mechanisms. Keep in mind that limiting the number of
connections means that in case of an attack, the server
resources are protected, but this might be at the expense of
serving legitimate connections. In particular, in some cases
this might mean SSH is no longer available.
There are several mechanisms to limit the number of
connections:
- Use `ulimit` (see bash(3) or your shell's man page) to
limit the number of file descriptors.
Then, `sslh` provides several mechanisms to limit the number
of concurrent connections, which in turns limits the number
of file descriptors used.
Essentially there are two ways to do this:
- you can set `max_connections` per protocol, and `sslh`
will drop connections after probing if the count is
exceeded. This should help in keeping SSH connections
available even if an attacker is stuffing other protocols.
Currently this does not work for forking protocols (support
is planned).
- you can set `max_connections` for each `listen` entry. So
the `sslh` process for each port will limit the number of
concurrent connections to that port. This is similar to the
`udp_max_connections` setting, but for TCP.
`sslh-select` and `sslh-ev` support both limit types.
`sslh-fork` has an explicit design goal to be as simple as
possible, which makes it impossible to implement limits per
protocol (because protocol probing is performed after the
process has forked). Limits will only work for `listen`
entries.
As of sslh 2.2.5, this is an experimental feature and not
all use cases have been tested. If 2.2.5 does not exist yet,
you must be on the Git's head, so it is even more
experimtental! Do not hesitate to send feedback if you
notice inconsistent behaviour.

120
doc/podman.md Normal file
View File

@ -0,0 +1,120 @@
Here is an example of deployment of sslh in transparent mode
using ansible and podman, [submitted by Github user
olegstepura](https://github.com/yrutschle/sslh/issues/448)
```yaml
# ansible podman task
- name: "Create sslh-co pod"
containers.podman.podman_pod:
name: sslh-co # read as "sslh and company"
state: started
ports:
- "80:80"
- "443:444"
# ... (more ports if needed)
network:
- '{{ containers.config.network }}' # other services from this network can access containers in this network for example prometheus can read caddy metrics at sslh-co:2020, also caddy itself can connect to other services to act as a reverse proxy
- name: "Create the sslh container"
containers.podman.podman_container:
name: sslh
image: "yrutschle/sslh:latest"
pod: sslh-co
capabilities:
- NET_RAW
- NET_BIND_SERVICE
- NET_ADMIN
sysctl:
net.ipv4.conf.default.route_localnet: 1
net.ipv4.conf.all.route_localnet: 1
expose:
- 444
volume:
# ... (make sure to mount config as you like)
command: --transparent -F/etc/sslh/sslh.cfg # parameter --transparent here is needed to trigger configure_iptables in init script
state: started
- name: "Create the caddy container"
containers.podman.podman_container:
name: caddy
image: "lucaslorentz/caddy-docker-proxy:alpine" # regular caddy or nginx image will also work
pod: sslh-co
expose:
- 80
- 443
- 2020 # metrics, since caddy-docker-proxy uses :2019 internally
volume:
# ... (mount your configs and other stuff here)
- "/var/run/podman/podman.sock:/var/run/docker.sock"
state: started
notify: podman restart sslh
- name: "Create the SSH proxy to host container"
containers.podman.podman_container:
name: ssh-proxy
image: "alpine/socat:latest"
pod: sslh-co
expose:
- 222
command: TCP-LISTEN:222,fork TCP:host.containers.internal:22
state: started
```
```ini
# sslh config
foreground: true;
inetd: false;
numeric: true;
transparent: true;
timeout: 5;
listen:
(
{ host: "0.0.0.0"; port: "444"; keepalive: true; },
);
protocols:
(
{
name: "ssh";
service: "ssh";
host: "localhost";
port: "222";
fork: true;
},
{
name: "http";
host: "localhost";
port: "80";
},
{
name: "tls";
host: "localhost";
port: "443";
},
);
```
I omitted caddy configs here as it's not important. Some unrelated container configs were also dropped.
In the example above `sslh`, `caddy` and `ssh-proxy` are 3 containers in the same pod, all listening on `localhost`. SSLH has to listen on `444` because caddy already listens on `443` and it's more complex to reconfigure caddy port because of let's encrypt (caddy itself "thinks" it is bound to your host interface).
Scheme of port mapping is (all containers share same `localhost`):
```
host 443 → pod 444 (sslh) → pod 443 (caddy)
```
- podman connects host `443` port to pod's `444` port
- `sslh` listens `444` on `localhost`, reroutes tls to `caddy` on `localhost:443`
- `caddy` listens `443` on `localhost` (reverse-proxies to other apps on private network)
Reverse-proxied services get correct IP in `X-Forwarded-For` header.
Takeaways:
- `net.ipv4.conf.default.route_localnet` is setup only in container, not on host, which is nice.
- same with iptables and route rules applied by `init` script in sslh container
- `sslh` proxy to other services in the private network will not work (because transparent mode is enabled) even if that other service does not need real IP
- with transparent mode all services that `sslh` will connect to should be attached to this pod making it listen on `localhost` of the pod
- ports of containers should not clash
- ports should be published on pod level, not on containers
- containers should not be configured to connect to custom networks
- because of the above proxying ssh to host will also not work out of the box, `sslh` should connect to `localhost` and not to a random IP. While adding another proxy as a container to the same pod sounds like an overkill I don't see any other solution, so `socat` is used as an additional proxy.
- `reverse_proxy` by caddy to other containers in the same custom network (as pod is attached to) works (e.g. caddy can connect to other IPs from custom network). `socat` can also connect to host and/or other containers in custom network.

View File

@ -1,5 +1,5 @@
/* Generated by conf2struct (https://www.rutschle.net/tech/conf2struct/README)
* on Sun Apr 6 11:44:59 2025.
* on Wed Jul 16 17:59:23 2025.
# conf2struct: generate libconf parsers that read to structs
# Copyright (C) 2018-2024 Yves Rutschle

View File

@ -1,5 +1,5 @@
/* Generated by conf2struct (https://www.rutschle.net/tech/conf2struct/README)
* on Sun Apr 6 11:44:59 2025.
* on Wed Jul 16 17:59:23 2025.
# conf2struct: generate libconf parsers that read to structs
# Copyright (C) 2018-2024 Yves Rutschle

View File

@ -26,7 +26,6 @@
#include <pwd.h>
#include <syslog.h>
#include <libgen.h>
#include <getopt.h>
#include <errno.h>
#define cfg sslhcfg
@ -323,9 +322,6 @@ int start_listen_sockets(struct listen_endpoint *sockfd[])
int main(int argc, char *argv[])
{
extern char *optarg;
extern int optind;
int num_addr_listen;
struct listen_endpoint *listen_sockets;

View File

@ -49,9 +49,15 @@ syslog_facility: "auth";
# List of interfaces on which we should listen
# Options:
# host: name of the interface to listen to
# port: name of the service to listen to
# is_udp: listen in the UDP domain (default is TCP)
# is_unix: listen in the UNIX socket domain (default is TCP)
# keepalive: set TCP_KEEPALIVE
# max_connections: drop incoming connections after this count (sslh-fork only)
listen:
(
{ host: "thelonious"; port: "443"; },
{ host: "thelonious"; port: "443"; max_connections: 5; },
{ host: "thelonious"; port: "8080"; keepalive: true; },
{ host: "thelonious"; is_udp: true; port: "443"; },
{ host: "/tmp/unix_socket"; is_unix: true; port: ""; }
@ -70,7 +76,11 @@ listen:
# keepalive: Should TCP keepalive be on or off for that
# connection (default is off)
# fork: Should a new process be forked for this protocol?
# (only useful for sslh-select)
# (only useful for sslh-select and sslh-ev)
# max_connections: further connections for this protocol
# will be rejected once the limit is reached. Does not
# work when fork is used (either in sslh-fork or if the
# previous option is set).
# 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) newly incoming connection (again)

14
gap.c
View File

@ -59,6 +59,11 @@ gap_array* gap_init(int len)
return gap;
}
void gap_set_hardlimit(gap_array* gap, int index)
{
gap->hardlimit = index;
}
int gap_extend(gap_array* gap)
{
int elem_size = sizeof(gap->array[0]);
@ -88,7 +93,11 @@ void gap_destroy(gap_array* gap)
* is considered len elements long.
* A poor man's list, if you will. Currently only used to remove probing
* connections, so it only copies a few pointers at most.
* Returns -1 if ptr was not found */
* Returns:
* -1 if ptr was not found
* -2 if `len` is inconsistent with gap->len (i.e. trying to shift beyond the
* end of the array)
* */
int gap_remove_ptr(gap_array* gap, void* ptr, int len)
{
int start, i;
@ -102,6 +111,9 @@ int gap_remove_ptr(gap_array* gap, void* ptr, int len)
else
return -1;
if (gap->len < len)
return -2;
for (i = start; i < len - 1; i++) {
gap->array[i] = gap->array[i+1];
}

9
gap.h
View File

@ -4,6 +4,7 @@
typedef struct gap_array gap_array;
gap_array* gap_init(int len);
void gap_set_hardlimit(gap_array* ga, int index);
static void* gap_get(gap_array* gap, int index);
static int gap_set(gap_array* gap, int index, void* ptr);
void gap_destroy(gap_array* gap);
@ -13,7 +14,8 @@ int gap_remove_ptr(gap_array* gap, void* ptr, int len);
/* Private declarations to allow inlining.
* Don't assume my implementation. */
typedef struct gap_array {
int len; /* Number of elements in array */
int len; /* Number of elements in array (corresponds to the number of pages allocated) */
int hardlimit; /* Maximum index allowed after which sets will fail; 0 means no limit */
void** array;
} gap_array;
@ -21,6 +23,9 @@ int gap_extend(gap_array* gap);
static inline int __attribute__((unused)) gap_set(gap_array* gap, int index, void* ptr)
{
if (gap->hardlimit && (index > gap->hardlimit))
return -1;
while (index >= gap->len) {
int res = gap_extend(gap);
if (res == -1) return -1;
@ -37,6 +42,8 @@ static inline void* __attribute__((unused)) gap_get(gap_array* gap, int index)
* gap_get()'s job. This will do for now */
if (index >= gap->len) return NULL;
if (gap->hardlimit && (index > gap->hardlimit)) return NULL;
return gap->array[index];
}

63
probe.c
View File

@ -21,10 +21,7 @@
#define _GNU_SOURCE
#include <stdio.h>
#ifdef ENABLE_REGEX
#define PCRE2_CODE_UNIT_WIDTH 8
#include <pcre2.h>
#endif
#include <regex.h>
#include <ctype.h>
#include "probe.h"
#include "log.h"
@ -146,6 +143,7 @@ static int is_ssh_protocol(const char *p, ssize_t len, struct sslhcfg_protocols_
#define OVPN_OPCODE_MASK 0xF8
#define OVPN_CONTROL_HARD_RESET_CLIENT_V1 (0x01 << 3)
#define OVPN_CONTROL_HARD_RESET_CLIENT_V2 (0x07 << 3)
#define OVPN_CONTROL_HARD_RESET_CLIENT_V3 (0x0A << 3)
#define OVPN_HMAC_128 16
#define OVPN_HMAC_160 20
#define OVPN_HARD_RESET_PACKET_ID_OFFSET(hmac_size) (9 + hmac_size)
@ -164,8 +162,12 @@ static int is_openvpn_protocol (const char*p,ssize_t len, struct sslhcfg_protoco
if (len < 1)
return PROBE_NEXT;
printf("opcode: %d\n", (p[0] & OVPN_OPCODE_MASK) >> 3);
if ((p[0] & OVPN_OPCODE_MASK) != OVPN_CONTROL_HARD_RESET_CLIENT_V1 &&
(p[0] & OVPN_OPCODE_MASK) != OVPN_CONTROL_HARD_RESET_CLIENT_V2)
(p[0] & OVPN_OPCODE_MASK) != OVPN_CONTROL_HARD_RESET_CLIENT_V2 &&
(p[0] & OVPN_OPCODE_MASK) != OVPN_CONTROL_HARD_RESET_CLIENT_V3
)
return PROBE_NEXT;
/* The detection pattern above may not be reliable enough.
@ -176,13 +178,19 @@ static int is_openvpn_protocol (const char*p,ssize_t len, struct sslhcfg_protoco
if (len <= OVPN_HARD_RESET_PACKET_ID_OFFSET(OVPN_HMAC_128) + sizeof(uint32_t))
return PROBE_NEXT;
if (ntohl(*(uint32_t*)(p + OVPN_HARD_RESET_PACKET_ID_OFFSET(OVPN_HMAC_128))) <= 5u)
uint32_t i;
/* OVPN_HMAC_128 is unaligned, which requires special care e.g. on ARM */
memcpy(&i, (p + OVPN_HARD_RESET_PACKET_ID_OFFSET(OVPN_HMAC_128)), sizeof(i));
i = ntohl(i);
if (i <= 5u)
return PROBE_MATCH;
if (len <= OVPN_HARD_RESET_PACKET_ID_OFFSET(OVPN_HMAC_160) + sizeof(uint32_t))
return PROBE_NEXT;
if (ntohl(*(uint32_t*)(p + OVPN_HARD_RESET_PACKET_ID_OFFSET(OVPN_HMAC_160))) <= 5u)
memcpy(&i, (p + OVPN_HARD_RESET_PACKET_ID_OFFSET(OVPN_HMAC_160)), sizeof(i));
i = ntohl(i);
if (i <= 5u)
return PROBE_MATCH;
return PROBE_NEXT;
@ -362,17 +370,38 @@ static int is_socks5_protocol(const char *p_in, ssize_t len, struct sslhcfg_prot
return PROBE_MATCH;
}
/* ******************
* is_syslog_protocol
* */
static regex_t syslog_preg;
static int configured_syslog_regex = 0;
static void config_syslog_regex(void)
{
/* two patterns for syslog messages:
* <12> My message
* 15 <12> My message
* 12 is 'priority', 1 to 3 digits (RFC4234)
* 15 is 'message length', a TCP-only option (RFC6587)
*/
int res = regcomp(&syslog_preg, "^([0-9]{1,3} )?<[0-9]{1,3}>", REG_EXTENDED);
if (res) {
print_message(msg_system_error, "regcomp");
exit(1);
}
configured_syslog_regex = 1;
}
static int is_syslog_protocol(const char *p, ssize_t len, struct sslhcfg_protocols_item* proto)
{
int res, i, j;
char buf[len+1];
res = sscanf(p, "<%d>", &i);
if (res == 1) return 1;
if (!configured_syslog_regex) config_syslog_regex();
res = sscanf(p, "%d <%d>", &i, &j);
if (res == 2) return 1;
strncpy(buf, p, len);
buf[len] = 0;
return 0;
return (regexec(&syslog_preg, buf, (size_t)0, NULL, 0) == 0);
}
static int is_teamspeak_protocol(const char *p, ssize_t len, struct sslhcfg_protocols_item* proto)
@ -396,16 +425,18 @@ static int is_msrdp_protocol(const char *p, ssize_t len, struct sslhcfg_protocol
return packet_len == len;
}
#ifdef ENABLE_REGEX
pcre2_match_data* probe_regex_matches;
#endif
static int regex_probe(const char *p, ssize_t len, struct sslhcfg_protocols_item* proto)
{
#ifdef ENABLE_REGEX
pcre2_code**probe = (pcre2_code**)proto->data;
pcre2_match_data* matches;
matches = pcre2_match_data_create(1, NULL);
for (; *probe; probe++) {
int res = pcre2_match(*probe, (PCRE2_SPTR8)p, len, 0, 0, matches, NULL);
int res = pcre2_match(*probe, (PCRE2_SPTR8)p, len, 0, 0, probe_regex_matches, NULL);
if (res >= 0) return 1;
}

View File

@ -66,4 +66,11 @@ struct sslhcfg_protocols_item* timeout_protocol(void);
void hexdump(msg_info, const char*, unsigned int);
#ifdef ENABLE_REGEX
#define PCRE2_CODE_UNIT_WIDTH 8
#include <pcre2.h>
extern pcre2_match_data* probe_regex_matches;
#endif
#endif

View File

@ -49,6 +49,9 @@ int tidy_connection(struct connection *cnx, struct loop_info* fd_info)
if (gap_remove_ptr(fd_info->probing_list, cnx, fd_info->num_probing) != -1)
fd_info->num_probing--;
dec_proto_connections(cnx);
dec_listen_connections(cnx);
collection_remove_cnx(fd_info->collection, cnx);
return 0;
}
@ -90,7 +93,7 @@ struct connection* cnx_accept_process(struct loop_info* fd_info, struct listen_e
switch (type) {
case SOCK_STREAM:
cnx = accept_new_connection(fd, fd_info);
cnx = accept_new_connection(listen_socket, fd_info);
if (!cnx) return NULL;
break;

View File

@ -20,6 +20,8 @@
*/
#include "config.h"
#ifdef HAVE_PROXYPROTOCOL
#include <proxy_protocol.h>
@ -48,22 +50,38 @@ static int family_to_pp(int af_family)
typedef char libpp_addr[108]; /* This is hardcoded in libproxyprotocol/proxy_protocol.h */
typedef enum {
PEER,
SOCK
} sockpeer_t;
/* Fills *addr, *host and *serv with the connection information corresponding
* to fd. *host is the IP address as string and *serv is the service (port)
* to fd. sockpeer indicates if if is filled with the remote, or local, part of
* the socket.
* *host is the IP address as string and *serv is the service (port)
* */
static int get_info(int fd, struct addrinfo* addr, libpp_addr* host, uint16_t* serv)
static int get_info(int fd, sockpeer_t sockpeer, struct addrinfo* addr, libpp_addr* host, uint16_t* serv)
{
char serv_str[NI_MAXSERV];
int res;
res = getpeername(fd, addr->ai_addr, &addr->ai_addrlen);
CHECK_RES_RETURN(res, "getpeername", -1);
if (sockpeer == PEER) {
res = getpeername(fd, addr->ai_addr, &addr->ai_addrlen);
CHECK_RES_RETURN(res, "getpeername", -1);
} else {
res = getsockname(fd, addr->ai_addr, &addr->ai_addrlen);
CHECK_RES_RETURN(res, "getsockname", -1);
}
res = getnameinfo(addr->ai_addr, addr->ai_addrlen,
(char*)host, sizeof(*host),
serv_str, sizeof(serv_str),
NI_NUMERICHOST | NI_NUMERICSERV );
CHECK_RES_RETURN(res, "getnameinfo", -1);
if (res != 0) {
print_message(msg_system_error, "getnameinfo: %s\n", gai_strerror(res));
return -1;
}
*serv = atoi(serv_str);
@ -87,14 +105,14 @@ int pp_write_header(int pp_version, struct connection* cnx)
addr.ai_addr = (struct sockaddr*)&ss;
addr.ai_addrlen = sizeof(ss);
res = get_info(cnx->q[0].fd,
res = get_info(cnx->q[0].fd, PEER,
&addr,
&pp_info_in_v1.src_addr,
&pp_info_in_v1.src_port);
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,
res = get_info(cnx->q[1].fd, SOCK,
&addr,
&pp_info_in_v1.dst_addr,
&pp_info_in_v1.dst_port

View File

@ -1,5 +1,5 @@
/* Generated by conf2struct (https://www.rutschle.net/tech/conf2struct/README)
* on Sun Apr 6 11:44:58 2025.
* on Mon Aug 4 18:25:44 2025.
# conf2struct: generate libconf parsers that read to structs
# Copyright (C) 2018-2024 Yves Rutschle
@ -500,7 +500,7 @@ struct arg_file* sslhcfg_conffile;
struct arg_str* sslhcfg_anyprot;
struct arg_end* sslhcfg_end;
static struct config_desc table_sslhcfg_protocols[] = {
@ -712,6 +712,22 @@ static struct config_desc table_sslhcfg_protocols[] = {
/* default_val*/ .default_val.def_bool = 0
},
{
/* name */ "max_connections",
/* type */ CFG_INT,
/* sub_group*/ NULL,
/* arg_cl */ NULL,
/* base_addr */ NULL,
/* offset */ offsetof(struct sslhcfg_protocols_item, max_connections),
/* offset_len */ 0,
/* offset_present */ offsetof(struct sslhcfg_protocols_item, max_connections_is_present),
/* size */ sizeof(int),
/* array_type */ -1,
/* mandatory */ 0,
/* optional */ 1,
/* default_val*/ .default_val.def_int = 0
},
{
/* name */ "sni_hostnames",
/* type */ CFG_ARRAY,
@ -793,7 +809,7 @@ static struct config_desc table_sslhcfg_protocols[] = {
},
{ 0 }
};
static struct config_desc table_sslhcfg_listen[] = {
@ -829,6 +845,22 @@ static struct config_desc table_sslhcfg_listen[] = {
/* default_val*/ .default_val.def_string = NULL
},
{
/* name */ "max_connections",
/* type */ CFG_INT,
/* sub_group*/ NULL,
/* arg_cl */ NULL,
/* base_addr */ NULL,
/* offset */ offsetof(struct sslhcfg_listen_item, max_connections),
/* offset_len */ 0,
/* offset_present */ offsetof(struct sslhcfg_listen_item, max_connections_is_present),
/* size */ sizeof(int),
/* array_type */ -1,
/* mandatory */ 0,
/* optional */ 1,
/* default_val*/ .default_val.def_int = 0
},
{
/* name */ "is_udp",
/* type */ CFG_BOOL,
@ -2421,6 +2453,11 @@ static void sslhcfg_protocols_fprint(
fprintf(out, "keepalive: %d", sslhcfg_protocols->keepalive);
fprintf(out, "\n");
indent(out, depth);
fprintf(out, "max_connections: %d", sslhcfg_protocols->max_connections);
if (! sslhcfg_protocols->max_connections_is_present)
fprintf(out, " <unset>");
fprintf(out, "\n");
indent(out, depth);
fprintf(out, "sni_hostnames [%zu]:\n", sslhcfg_protocols->sni_hostnames_len);
for (i = 0; i < sslhcfg_protocols->sni_hostnames_len; i++) {
indent(out, depth+1);
@ -2463,6 +2500,11 @@ static void sslhcfg_listen_fprint(
fprintf(out, "port: %s", sslhcfg_listen->port);
fprintf(out, "\n");
indent(out, depth);
fprintf(out, "max_connections: %d", sslhcfg_listen->max_connections);
if (! sslhcfg_listen->max_connections_is_present)
fprintf(out, " <unset>");
fprintf(out, "\n");
indent(out, depth);
fprintf(out, "is_udp: %d", sslhcfg_listen->is_udp);
fprintf(out, "\n");
indent(out, depth);

View File

@ -1,5 +1,5 @@
/* Generated by conf2struct (https://www.rutschle.net/tech/conf2struct/README)
* on Sun Apr 6 11:44:58 2025.
* on Mon Aug 4 18:25:44 2025.
# conf2struct: generate libconf parsers that read to structs
# Copyright (C) 2018-2024 Yves Rutschle
@ -43,6 +43,8 @@
struct sslhcfg_listen_item {
char* host;
char* port;
int max_connections_is_present;
int max_connections;
int is_udp;
int is_unix;
int keepalive;
@ -63,6 +65,8 @@ struct sslhcfg_protocols_item {
int resolve_on_forward;
int log_level;
int keepalive;
int max_connections_is_present;
int max_connections;
size_t sni_hostnames_len;
char** sni_hostnames;
size_t alpn_protocols_len;
@ -76,6 +80,7 @@ struct sslhcfg_protocols_item {
T_PROBE* probe;
struct addrinfo* saddr;
void* data;
unsigned int num_connections;
dl_list timeouts;
};

View File

@ -1,7 +1,7 @@
/*
sslh-fork: forking server
# Copyright (C) 2007-2021 Yves Rutschle
# Copyright (C) 2007-2025 Yves Rutschle
#
# This program is free software; you can redistribute it
# and/or modify it under the terms of the GNU General Public
@ -160,6 +160,35 @@ void set_listen_procname(struct listen_endpoint *listen_socket)
#endif
}
static void mask_sigchld(void)
{
struct sigaction action;
memset(&action, 0, sizeof(action));
action.sa_handler = NULL;
action.sa_flags = SA_NOCLDWAIT;
int res = sigaction(SIGCHLD, &action, NULL);
CHECK_RES_DIE(res, "sigaction");
}
extern volatile sig_atomic_t received_sigchld;
/* EINTR, so we probably received a signal; check if it's SIGCHLD */
static void process_signals(struct listen_endpoint* endpoint)
{
int chld;
if (received_sigchld) {
received_sigchld = 0;
do {
chld = waitpid(-1, NULL, WNOHANG);
CHECK_RES_RETURN(chld, "waitpid", );
if (chld) {
endpoint->num_connections--;
print_message(msg_fd, "child died, %d concurrent connections remaining\n", endpoint->num_connections);
}
} while (chld);
}
}
/* At least MacOS does not know these two options, so define them to something
* equivalent for our use case */
@ -182,9 +211,9 @@ void tcp_listener(struct listen_endpoint* endpoint, int num_endpoints, int activ
while (1) {
in_socket = accept(endpoint[active_endpoint].socketfd, 0, 0);
if (in_socket == -1) {
print_message(msg_system_error, "%s:%d:%s:%d:%s\n",
print_message(msg_system_error, "%s:%d:%s:%d:%s\n",
__FILE__, __LINE__, "accept", errno, strerror(errno));
switch(in_socket) {
switch(errno) {
case ENETDOWN: /* accept(2) cites all these errnos as "you should retry" */
case EPROTO:
case ENOPROTOOPT:
@ -196,11 +225,24 @@ void tcp_listener(struct listen_endpoint* endpoint, int num_endpoints, int activ
case ECONNABORTED:
continue;
case EINTR:
process_signals(endpoint);
continue;
default: /* Otherwise, it's something wrong in our parameters, we fail */
return;
}
}
print_message(msg_fd, "accepted fd %d\n", in_socket);
endpoint->num_connections++;
print_message(msg_fd, "accepted fd %d (%d concurrent connections)\n", in_socket, endpoint->num_connections);
if ((endpoint->endpoint_cfg->max_connections_is_present)
&& (endpoint->num_connections > endpoint->endpoint_cfg->max_connections))
{
print_message(msg_fd, "too many connection, reclosing fd %d\n", in_socket);
endpoint->num_connections--;
close(in_socket);
continue;
}
switch(fork()) {
case -1: print_message(msg_system_error, "fork failed: err %d: %s\n", errno, strerror(errno));
@ -250,8 +292,10 @@ void main_loop(struct listen_endpoint listen_sockets[], int num_addr_listen)
exit(0);
break;
/* We're in the parent, we don't need to do anything */
/* We're in the parent, which does nothing but wait to be killed by
* SIGTERM, which it will send to its children */
default:
mask_sigchld();
break;
}
}

View File

@ -130,6 +130,7 @@ static void setup_regex_probe(struct sslhcfg_protocols_item *p)
exit(1);
}
}
probe_regex_matches = pcre2_match_data_create(1, NULL);
}
#else
{
@ -280,9 +281,6 @@ void close_std(void)
int main(int argc, char *argv[], char* envp[])
{
extern char *optarg;
extern int optind;
int res, num_addr_listen;
struct listen_endpoint *listen_sockets;

View File

@ -156,13 +156,15 @@ void main_loop(struct listen_endpoint listen_sockets[], int num_addr_listen)
/* Check main socket for new connections */
for (i = 0; i < num_addr_listen; i++) {
if (FD_ISSET(listen_sockets[i].socketfd, &readfds)) {
struct connection* new_cnx = cnx_accept_process(&fd_info, &listen_sockets[i]);
if (fd_out_of_range(new_cnx->q[0].fd))
tidy_connection(new_cnx, &fd_info);
/* don't also process it as a read socket */
FD_CLR(listen_sockets[i].socketfd, &readfds);
struct connection* new_cnx;
while ((new_cnx = cnx_accept_process(&fd_info, &listen_sockets[i]))) {
if (fd_out_of_range(new_cnx->q[0].fd))
tidy_connection(new_cnx, &fd_info);
}
}
}

View File

@ -97,6 +97,7 @@ config: {
items: (
{ name: "host"; type: "string"; var: true; },
{ name: "port"; type: "string"; var: true; },
{ name: "max_connections"; type: "int"; optional: true; },
{ name: "is_udp"; type: "bool"; default: false },
{ name: "is_unix"; type: "bool"; default: false },
{ name: "keepalive"; type: "bool"; default: false; }
@ -123,6 +124,7 @@ config: {
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: "max_connections"; type: "int"; optional: true },
{ name: "sni_hostnames",
type: "array",
element_type: "string"
@ -142,6 +144,7 @@ config: {
{ name: "probe"; type: "runtime"; c_type: "T_PROBE*" },
{ name: "saddr"; type: "runtime"; c_type: "struct addrinfo*" },
{ name: "data"; type: "runtime"; c_type: "void*" },
{ name: "num_connections"; type: "runtime"; c_type: "unsigned int" },
{ name: "timeouts"; type: "runtime"; c_type: "dl_list" }
)
}

17
t
View File

@ -9,7 +9,7 @@
use strict;
use IO::Socket::INET6;
use Test::More qw/no_plan/;
use Conf::Libconfig;
use Conf::Libconfig 1.0.3;
my $conf = new Conf::Libconfig;
$conf->read_file("test.cfg");
@ -17,7 +17,7 @@ $conf->read_file("test.cfg");
my $no_listen = 8083; # Port on which no-one listens
my $pidfile = $conf->lookup_value("pidfile");
my $sslh_port = $conf->fetch_array("listen")->[0]->{port};
my $sslh_port = $conf->value("listen")->[0]->{port};
my $user = (getpwuid $<)[0]; # Run under current username
# Which tests do we run
@ -84,8 +84,9 @@ sub make_sni_alpn_name {
sub test_probe {
my (%opts) = @_;
print "test_probe [$opts{expected}] $sslh_port\n";
my $cnx = new IO::Socket::INET(PeerHost => "localhost:$sslh_port");
warn "$!\n" unless $cnx;
warn "t: $!\n" unless $cnx;
return unless $cnx;
my $pattern = $opts{data};
@ -119,7 +120,7 @@ sub test_probe {
sub test_probes {
my (%in_opts) = @_;
my @probes = @{$conf->fetch_array("protocols")};
my @probes = @{$conf->value("protocols")};
foreach my $p (@probes) {
my %protocols = (
'ssh' => { data => "SSH-2.0 tester" },
@ -194,7 +195,7 @@ sub test_probes {
# Start an echoserver for each service
foreach my $s (@{$conf->fetch_array("protocols")}) {
foreach my $s (@{$conf->value("protocols")}) {
my $prefix = $s->{name};
$prefix =~ s/^ssl/tls/; # To remove in 1.21
@ -217,7 +218,7 @@ for my $binary (@binaries) {
my ($sslh_pid, $valgrind);
if (!($sslh_pid = fork)) {
my $user = (getpwuid $<)[0]; # Run under current username
my $cmd = "./$binary -v 4 -f -u $user -F test.cfg";
my $cmd = "./$binary -u $user -F test.cfg";
#$valgrind = 1;
#$cmd = "valgrind --leak-check=full $cmd";
verbose_exec $cmd;
@ -339,11 +340,11 @@ if ($RB_CNX_NOSERVER) {
}
my $ssh_conf = (grep { $_->{name} eq "ssh" } @{$conf->fetch_array("protocols")})[0];
my $ssh_conf = (grep { $_->{name} eq "ssh" } @{$conf->value("protocols")})[0];
my $ssh_address = $ssh_conf->{host} . ":" . $ssh_conf->{port};
# Use the last TLS echoserv (no SNI/ALPN)
my $ssl_conf = (grep { $_->{name} eq "tls" } @{$conf->fetch_array("protocols")})[-1];
my $ssl_conf = (grep { $_->{name} eq "tls" } @{$conf->value ("protocols")})[-1];
my $ssl_address = $ssl_conf->{host} . ":" . $ssl_conf->{port};

View File

@ -118,12 +118,45 @@ void tcp_read_process(struct loop_info* fd_info,
}
}
/* *cnx must have its endpoint field filled;
* Increment the connection count for the listen endpoint.
* Return 1 if connection count is exceeded, 0 otherwise */
static int inc_listen_connections(struct connection* cnx)
{
cnx->endpoint->num_connections++;
if (cnx->endpoint->endpoint_cfg->max_connections_is_present) {
int num_cnx = cnx->endpoint->num_connections;
int max_cnx = cnx->endpoint->endpoint_cfg->max_connections;
print_message(msg_connections, "Endpoint %d +1: %d/%d cnx\n",
cnx->endpoint->socketfd, num_cnx, max_cnx);
if (num_cnx > max_cnx) {
print_message(msg_connections_error, "Endpoint %d: too many connections, dropping\n", cnx->endpoint->socketfd);
return 1;
}
}
return 0;
}
void dec_listen_connections(struct connection* cnx)
{
cnx->endpoint->num_connections--;
print_message(msg_connections, "Endpoint %d -1: %d/%d cnx\n",
cnx->endpoint->socketfd,
cnx->endpoint->num_connections,
cnx->endpoint->endpoint_cfg->max_connections);
}
/* 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 */
struct connection* accept_new_connection(int listen_socket, struct loop_info* fd_info)
struct connection* accept_new_connection(struct listen_endpoint* endpoint, struct loop_info* fd_info)
{
int in_socket, res;
int listen_socket = endpoint->socketfd;
print_message(msg_fd, "accepting from %d\n", listen_socket);
@ -141,12 +174,16 @@ struct connection* accept_new_connection(int listen_socket, struct loop_info* fd
close(in_socket);
return NULL;
}
cnx->endpoint = endpoint;
if (inc_listen_connections(cnx)) {
tidy_connection(cnx, fd_info);
return NULL;
}
add_probing_cnx(fd_info, cnx);
return cnx;
}
/* Connect queue 1 of connection to SSL; returns new file descriptor */
static int connect_queue(struct connection* cnx,
struct loop_info* fd_info)
@ -276,6 +313,11 @@ void probing_read_process(struct connection* cnx,
remove_probing_cnx(fd_info, cnx);
cnx->state = ST_SHOVELING;
if (inc_proto_connections(cnx)) {
tidy_connection(cnx, fd_info);
return;
}
/* libwrap check if required for this protocol */
if (cnx->proto->service &&
check_access_rights(cnx->q[0].fd, cnx->proto->service)) {

View File

@ -6,8 +6,9 @@
#include "tcp-probe.h"
void tcp_read_process(struct loop_info* fd_info, int fd);
struct connection* accept_new_connection(int listen_socket, struct loop_info* fd_info);
struct connection* accept_new_connection(struct listen_endpoint* endpoint, struct loop_info* fd_info);
void probing_read_process(struct connection* cnx, struct loop_info* fd_info);
void cnx_write_process(struct loop_info* fd_info, int fd);
void dec_listen_connections(struct connection* cnx);
#endif

View File

@ -19,7 +19,7 @@ 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: 0; # file descriptor activity, open/close/whatnot
verbose-fd: 1; # file descriptor activity, open/close/whatnot
verbose-packets: 1; # hexdump packets on which probing is done
verbose-probe-info: 0; # what's happening during the probe process
verbose-probe-error: 1; # failures and problems during probing
@ -30,9 +30,9 @@ verbose-int-error: 1; # internal errors, the kind that should never happen
# Options:
listen:
(
{ host: "localhost"; port: "8080"; keepalive: true; },
{ host: "localhost"; port: "8080"; keepalive: true; max_connections: 2; },
{ host: "localhost"; port: "8081"; keepalive: true; },
{ host: "ip4-localhost"; is_udp: true; port: "8086"; },
# { host: "ip4-localhost"; is_udp: true; port: "8086"; },
{ host: "/tmp/sslh.sock"; is_unix: true; port: ""; }
);
@ -43,7 +43,8 @@ listen:
protocols:
(
{ name: "ssh"; host: "localhost"; port: "9000"; fork: true; transparent: true; resolve_on_forward: true; },
{ name: "ssh"; host: "localhost"; port: "9000"; fork: true; transparent: true; resolve_on_forward: true;
max_connections: 1; },
{ name: "socks5"; host: "localhost"; port: "9001"; },
{ name: "http"; host: "localhost"; port: "80"; proxyprotocol: 2; },
{ name: "tinc"; host: "localhost"; port: "9003"; },
@ -51,11 +52,12 @@ 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";
udp_timeout: 30;
regex_patterns: [ "^foo" ];
resolve_on_forward: true;
},
# { name: "regex"; host: "ip4-localhost"; is_udp: true; port: "9020";
# max_connections: 1;
# udp_timeout: 30;
# regex_patterns: [ "^foo" ];
# resolve_on_forward: true;
# },
{ name: "regex"; host: "localhost"; port: "9011";
regex_patterns: [ "^foo", "^bar" ];
minlength: 4;
@ -72,6 +74,7 @@ protocols:
{ name: "tls"; host: "localhost"; port: "9023"; alpn_protocols: [ "alpn3" ]; },
{ name: "tls"; host: "localhost"; port: "9024"; sni_hostnames: [ "sni3" ]; },
{ name: "tls"; host: "localhost"; port: "9025"; },
# { name: "anyprot"; host: "ip4-localhost"; is_udp: true; port: "9999"; },
{ name: "anyprot"; host: "localhost"; port: "9099"; }
);

7
testgap/Makefile Normal file
View File

@ -0,0 +1,7 @@
CFLAGS=-DHASH_TESTING -O2 -Wall
OBJ=../gap.o gtest.o
gtest: $(OBJ)
$(CC) -o gtest $(OBJ)

102
testgap/gtest.c Normal file
View File

@ -0,0 +1,102 @@
/* Wee testing program from the gap code, this is very similar to the hash
* testing code.
* Instead of pointers, we use integers
*
* script language:
* a 5 12 # adds value 12 at index 5
* d 512 42 # remove value 42, 512 is the length of the array
* h 16 0 # set hard limit to 16 (value is not used)
*/
#include <stdlib.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include "../gap.h"
static void gtest_next_line(FILE* f, char* action, int* index, void** value)
{
int res = 0;
while ((res != 3) && (res != EOF))
res = fscanf(f, "%c %d %p\n", action, index, value);
if (res == EOF) exit(0);
}
static void gap_dump(gap_array* gap, char* filename)
{
int i;
FILE* out = fopen(filename, "w");
if (!out) {
perror(filename);
exit(1);
}
fprintf(out, "<gap len=%d hardlimit=%d>\n", gap->len, gap->hardlimit);
for (i = 0; i < gap->len; i++) {
void* value = gap_get(gap, i);
fprintf(out, "[%d] = %p\n", i, value);
}
fprintf(out, "</gap>\n");
fclose(out);
}
int main(int argc, char* argv[])
{
gap_array* gap = gap_init(0);
char action;
int line = 0, index;
void* value;
FILE* f;
if (argc != 3) {
fprintf(stderr, "Usage: gtest <script file> <dump file>\n");
exit(1);
}
char* script_file = argv[1];
char* dump_file = argv[2];
f = fopen(argv[1], "r");
if (!f) {
perror(script_file);
exit(1);
}
while (1) {
action = ' ';
line++;
gtest_next_line(f, &action, &index, &value);
fprintf(stderr, "action %d: %c %d %p\n", line, action, index, value);
switch (action) {
case 'a': /* add */
fprintf(stderr, "[%d] = %p\n", index, value);
gap_set(gap, index, value);
break;
case 'd': /* del */
fprintf(stderr, "removing %p\n", value);
int res = gap_remove_ptr(gap, (void*)value, index);
fprintf(stderr, "remove: %d\n", res);
break;
case 'h': /* hardlimit */
fprintf(stderr, "setting hard limit %d\n", index);
gap_set_hardlimit(gap, index);
case 'g': /* get */
fprintf(stderr, "searching\n");
value = gap_get(gap, index);
fprintf(stderr, "got [%d]= %p\n", index, value);
break;
}
gap_dump(gap, dump_file);
}
return 0;
}

11
testgap/hardlimit.tst Normal file
View File

@ -0,0 +1,11 @@
# Test hard limit
h 15 0 # set hard limit
a 5 42
a 6 43
a 15 44
a 16 45
a 17 46

514
testgap/hardlimit.tst.ref Normal file
View File

@ -0,0 +1,514 @@
<gap len=512 hardlimit=15>
[0] = (nil)
[1] = (nil)
[2] = (nil)
[3] = (nil)
[4] = (nil)
[5] = 0x42
[6] = 0x43
[7] = (nil)
[8] = (nil)
[9] = (nil)
[10] = (nil)
[11] = (nil)
[12] = (nil)
[13] = (nil)
[14] = (nil)
[15] = 0x44
[16] = (nil)
[17] = (nil)
[18] = (nil)
[19] = (nil)
[20] = (nil)
[21] = (nil)
[22] = (nil)
[23] = (nil)
[24] = (nil)
[25] = (nil)
[26] = (nil)
[27] = (nil)
[28] = (nil)
[29] = (nil)
[30] = (nil)
[31] = (nil)
[32] = (nil)
[33] = (nil)
[34] = (nil)
[35] = (nil)
[36] = (nil)
[37] = (nil)
[38] = (nil)
[39] = (nil)
[40] = (nil)
[41] = (nil)
[42] = (nil)
[43] = (nil)
[44] = (nil)
[45] = (nil)
[46] = (nil)
[47] = (nil)
[48] = (nil)
[49] = (nil)
[50] = (nil)
[51] = (nil)
[52] = (nil)
[53] = (nil)
[54] = (nil)
[55] = (nil)
[56] = (nil)
[57] = (nil)
[58] = (nil)
[59] = (nil)
[60] = (nil)
[61] = (nil)
[62] = (nil)
[63] = (nil)
[64] = (nil)
[65] = (nil)
[66] = (nil)
[67] = (nil)
[68] = (nil)
[69] = (nil)
[70] = (nil)
[71] = (nil)
[72] = (nil)
[73] = (nil)
[74] = (nil)
[75] = (nil)
[76] = (nil)
[77] = (nil)
[78] = (nil)
[79] = (nil)
[80] = (nil)
[81] = (nil)
[82] = (nil)
[83] = (nil)
[84] = (nil)
[85] = (nil)
[86] = (nil)
[87] = (nil)
[88] = (nil)
[89] = (nil)
[90] = (nil)
[91] = (nil)
[92] = (nil)
[93] = (nil)
[94] = (nil)
[95] = (nil)
[96] = (nil)
[97] = (nil)
[98] = (nil)
[99] = (nil)
[100] = (nil)
[101] = (nil)
[102] = (nil)
[103] = (nil)
[104] = (nil)
[105] = (nil)
[106] = (nil)
[107] = (nil)
[108] = (nil)
[109] = (nil)
[110] = (nil)
[111] = (nil)
[112] = (nil)
[113] = (nil)
[114] = (nil)
[115] = (nil)
[116] = (nil)
[117] = (nil)
[118] = (nil)
[119] = (nil)
[120] = (nil)
[121] = (nil)
[122] = (nil)
[123] = (nil)
[124] = (nil)
[125] = (nil)
[126] = (nil)
[127] = (nil)
[128] = (nil)
[129] = (nil)
[130] = (nil)
[131] = (nil)
[132] = (nil)
[133] = (nil)
[134] = (nil)
[135] = (nil)
[136] = (nil)
[137] = (nil)
[138] = (nil)
[139] = (nil)
[140] = (nil)
[141] = (nil)
[142] = (nil)
[143] = (nil)
[144] = (nil)
[145] = (nil)
[146] = (nil)
[147] = (nil)
[148] = (nil)
[149] = (nil)
[150] = (nil)
[151] = (nil)
[152] = (nil)
[153] = (nil)
[154] = (nil)
[155] = (nil)
[156] = (nil)
[157] = (nil)
[158] = (nil)
[159] = (nil)
[160] = (nil)
[161] = (nil)
[162] = (nil)
[163] = (nil)
[164] = (nil)
[165] = (nil)
[166] = (nil)
[167] = (nil)
[168] = (nil)
[169] = (nil)
[170] = (nil)
[171] = (nil)
[172] = (nil)
[173] = (nil)
[174] = (nil)
[175] = (nil)
[176] = (nil)
[177] = (nil)
[178] = (nil)
[179] = (nil)
[180] = (nil)
[181] = (nil)
[182] = (nil)
[183] = (nil)
[184] = (nil)
[185] = (nil)
[186] = (nil)
[187] = (nil)
[188] = (nil)
[189] = (nil)
[190] = (nil)
[191] = (nil)
[192] = (nil)
[193] = (nil)
[194] = (nil)
[195] = (nil)
[196] = (nil)
[197] = (nil)
[198] = (nil)
[199] = (nil)
[200] = (nil)
[201] = (nil)
[202] = (nil)
[203] = (nil)
[204] = (nil)
[205] = (nil)
[206] = (nil)
[207] = (nil)
[208] = (nil)
[209] = (nil)
[210] = (nil)
[211] = (nil)
[212] = (nil)
[213] = (nil)
[214] = (nil)
[215] = (nil)
[216] = (nil)
[217] = (nil)
[218] = (nil)
[219] = (nil)
[220] = (nil)
[221] = (nil)
[222] = (nil)
[223] = (nil)
[224] = (nil)
[225] = (nil)
[226] = (nil)
[227] = (nil)
[228] = (nil)
[229] = (nil)
[230] = (nil)
[231] = (nil)
[232] = (nil)
[233] = (nil)
[234] = (nil)
[235] = (nil)
[236] = (nil)
[237] = (nil)
[238] = (nil)
[239] = (nil)
[240] = (nil)
[241] = (nil)
[242] = (nil)
[243] = (nil)
[244] = (nil)
[245] = (nil)
[246] = (nil)
[247] = (nil)
[248] = (nil)
[249] = (nil)
[250] = (nil)
[251] = (nil)
[252] = (nil)
[253] = (nil)
[254] = (nil)
[255] = (nil)
[256] = (nil)
[257] = (nil)
[258] = (nil)
[259] = (nil)
[260] = (nil)
[261] = (nil)
[262] = (nil)
[263] = (nil)
[264] = (nil)
[265] = (nil)
[266] = (nil)
[267] = (nil)
[268] = (nil)
[269] = (nil)
[270] = (nil)
[271] = (nil)
[272] = (nil)
[273] = (nil)
[274] = (nil)
[275] = (nil)
[276] = (nil)
[277] = (nil)
[278] = (nil)
[279] = (nil)
[280] = (nil)
[281] = (nil)
[282] = (nil)
[283] = (nil)
[284] = (nil)
[285] = (nil)
[286] = (nil)
[287] = (nil)
[288] = (nil)
[289] = (nil)
[290] = (nil)
[291] = (nil)
[292] = (nil)
[293] = (nil)
[294] = (nil)
[295] = (nil)
[296] = (nil)
[297] = (nil)
[298] = (nil)
[299] = (nil)
[300] = (nil)
[301] = (nil)
[302] = (nil)
[303] = (nil)
[304] = (nil)
[305] = (nil)
[306] = (nil)
[307] = (nil)
[308] = (nil)
[309] = (nil)
[310] = (nil)
[311] = (nil)
[312] = (nil)
[313] = (nil)
[314] = (nil)
[315] = (nil)
[316] = (nil)
[317] = (nil)
[318] = (nil)
[319] = (nil)
[320] = (nil)
[321] = (nil)
[322] = (nil)
[323] = (nil)
[324] = (nil)
[325] = (nil)
[326] = (nil)
[327] = (nil)
[328] = (nil)
[329] = (nil)
[330] = (nil)
[331] = (nil)
[332] = (nil)
[333] = (nil)
[334] = (nil)
[335] = (nil)
[336] = (nil)
[337] = (nil)
[338] = (nil)
[339] = (nil)
[340] = (nil)
[341] = (nil)
[342] = (nil)
[343] = (nil)
[344] = (nil)
[345] = (nil)
[346] = (nil)
[347] = (nil)
[348] = (nil)
[349] = (nil)
[350] = (nil)
[351] = (nil)
[352] = (nil)
[353] = (nil)
[354] = (nil)
[355] = (nil)
[356] = (nil)
[357] = (nil)
[358] = (nil)
[359] = (nil)
[360] = (nil)
[361] = (nil)
[362] = (nil)
[363] = (nil)
[364] = (nil)
[365] = (nil)
[366] = (nil)
[367] = (nil)
[368] = (nil)
[369] = (nil)
[370] = (nil)
[371] = (nil)
[372] = (nil)
[373] = (nil)
[374] = (nil)
[375] = (nil)
[376] = (nil)
[377] = (nil)
[378] = (nil)
[379] = (nil)
[380] = (nil)
[381] = (nil)
[382] = (nil)
[383] = (nil)
[384] = (nil)
[385] = (nil)
[386] = (nil)
[387] = (nil)
[388] = (nil)
[389] = (nil)
[390] = (nil)
[391] = (nil)
[392] = (nil)
[393] = (nil)
[394] = (nil)
[395] = (nil)
[396] = (nil)
[397] = (nil)
[398] = (nil)
[399] = (nil)
[400] = (nil)
[401] = (nil)
[402] = (nil)
[403] = (nil)
[404] = (nil)
[405] = (nil)
[406] = (nil)
[407] = (nil)
[408] = (nil)
[409] = (nil)
[410] = (nil)
[411] = (nil)
[412] = (nil)
[413] = (nil)
[414] = (nil)
[415] = (nil)
[416] = (nil)
[417] = (nil)
[418] = (nil)
[419] = (nil)
[420] = (nil)
[421] = (nil)
[422] = (nil)
[423] = (nil)
[424] = (nil)
[425] = (nil)
[426] = (nil)
[427] = (nil)
[428] = (nil)
[429] = (nil)
[430] = (nil)
[431] = (nil)
[432] = (nil)
[433] = (nil)
[434] = (nil)
[435] = (nil)
[436] = (nil)
[437] = (nil)
[438] = (nil)
[439] = (nil)
[440] = (nil)
[441] = (nil)
[442] = (nil)
[443] = (nil)
[444] = (nil)
[445] = (nil)
[446] = (nil)
[447] = (nil)
[448] = (nil)
[449] = (nil)
[450] = (nil)
[451] = (nil)
[452] = (nil)
[453] = (nil)
[454] = (nil)
[455] = (nil)
[456] = (nil)
[457] = (nil)
[458] = (nil)
[459] = (nil)
[460] = (nil)
[461] = (nil)
[462] = (nil)
[463] = (nil)
[464] = (nil)
[465] = (nil)
[466] = (nil)
[467] = (nil)
[468] = (nil)
[469] = (nil)
[470] = (nil)
[471] = (nil)
[472] = (nil)
[473] = (nil)
[474] = (nil)
[475] = (nil)
[476] = (nil)
[477] = (nil)
[478] = (nil)
[479] = (nil)
[480] = (nil)
[481] = (nil)
[482] = (nil)
[483] = (nil)
[484] = (nil)
[485] = (nil)
[486] = (nil)
[487] = (nil)
[488] = (nil)
[489] = (nil)
[490] = (nil)
[491] = (nil)
[492] = (nil)
[493] = (nil)
[494] = (nil)
[495] = (nil)
[496] = (nil)
[497] = (nil)
[498] = (nil)
[499] = (nil)
[500] = (nil)
[501] = (nil)
[502] = (nil)
[503] = (nil)
[504] = (nil)
[505] = (nil)
[506] = (nil)
[507] = (nil)
[508] = (nil)
[509] = (nil)
[510] = (nil)
[511] = (nil)
</gap>

View File

@ -0,0 +1,5 @@
# Insertions after the first page, to extend the gap
a 5 42
a 1024 43
a 1028 44

File diff suppressed because it is too large Load Diff

6
testgap/inserts.tst Normal file
View File

@ -0,0 +1,6 @@
# Simple insertions in the first page
a 5 42
a 10 43

514
testgap/inserts.tst.ref Normal file
View File

@ -0,0 +1,514 @@
<gap len=512 hardlimit=0>
[0] = (nil)
[1] = (nil)
[2] = (nil)
[3] = (nil)
[4] = (nil)
[5] = 0x42
[6] = (nil)
[7] = (nil)
[8] = (nil)
[9] = (nil)
[10] = 0x43
[11] = (nil)
[12] = (nil)
[13] = (nil)
[14] = (nil)
[15] = (nil)
[16] = (nil)
[17] = (nil)
[18] = (nil)
[19] = (nil)
[20] = (nil)
[21] = (nil)
[22] = (nil)
[23] = (nil)
[24] = (nil)
[25] = (nil)
[26] = (nil)
[27] = (nil)
[28] = (nil)
[29] = (nil)
[30] = (nil)
[31] = (nil)
[32] = (nil)
[33] = (nil)
[34] = (nil)
[35] = (nil)
[36] = (nil)
[37] = (nil)
[38] = (nil)
[39] = (nil)
[40] = (nil)
[41] = (nil)
[42] = (nil)
[43] = (nil)
[44] = (nil)
[45] = (nil)
[46] = (nil)
[47] = (nil)
[48] = (nil)
[49] = (nil)
[50] = (nil)
[51] = (nil)
[52] = (nil)
[53] = (nil)
[54] = (nil)
[55] = (nil)
[56] = (nil)
[57] = (nil)
[58] = (nil)
[59] = (nil)
[60] = (nil)
[61] = (nil)
[62] = (nil)
[63] = (nil)
[64] = (nil)
[65] = (nil)
[66] = (nil)
[67] = (nil)
[68] = (nil)
[69] = (nil)
[70] = (nil)
[71] = (nil)
[72] = (nil)
[73] = (nil)
[74] = (nil)
[75] = (nil)
[76] = (nil)
[77] = (nil)
[78] = (nil)
[79] = (nil)
[80] = (nil)
[81] = (nil)
[82] = (nil)
[83] = (nil)
[84] = (nil)
[85] = (nil)
[86] = (nil)
[87] = (nil)
[88] = (nil)
[89] = (nil)
[90] = (nil)
[91] = (nil)
[92] = (nil)
[93] = (nil)
[94] = (nil)
[95] = (nil)
[96] = (nil)
[97] = (nil)
[98] = (nil)
[99] = (nil)
[100] = (nil)
[101] = (nil)
[102] = (nil)
[103] = (nil)
[104] = (nil)
[105] = (nil)
[106] = (nil)
[107] = (nil)
[108] = (nil)
[109] = (nil)
[110] = (nil)
[111] = (nil)
[112] = (nil)
[113] = (nil)
[114] = (nil)
[115] = (nil)
[116] = (nil)
[117] = (nil)
[118] = (nil)
[119] = (nil)
[120] = (nil)
[121] = (nil)
[122] = (nil)
[123] = (nil)
[124] = (nil)
[125] = (nil)
[126] = (nil)
[127] = (nil)
[128] = (nil)
[129] = (nil)
[130] = (nil)
[131] = (nil)
[132] = (nil)
[133] = (nil)
[134] = (nil)
[135] = (nil)
[136] = (nil)
[137] = (nil)
[138] = (nil)
[139] = (nil)
[140] = (nil)
[141] = (nil)
[142] = (nil)
[143] = (nil)
[144] = (nil)
[145] = (nil)
[146] = (nil)
[147] = (nil)
[148] = (nil)
[149] = (nil)
[150] = (nil)
[151] = (nil)
[152] = (nil)
[153] = (nil)
[154] = (nil)
[155] = (nil)
[156] = (nil)
[157] = (nil)
[158] = (nil)
[159] = (nil)
[160] = (nil)
[161] = (nil)
[162] = (nil)
[163] = (nil)
[164] = (nil)
[165] = (nil)
[166] = (nil)
[167] = (nil)
[168] = (nil)
[169] = (nil)
[170] = (nil)
[171] = (nil)
[172] = (nil)
[173] = (nil)
[174] = (nil)
[175] = (nil)
[176] = (nil)
[177] = (nil)
[178] = (nil)
[179] = (nil)
[180] = (nil)
[181] = (nil)
[182] = (nil)
[183] = (nil)
[184] = (nil)
[185] = (nil)
[186] = (nil)
[187] = (nil)
[188] = (nil)
[189] = (nil)
[190] = (nil)
[191] = (nil)
[192] = (nil)
[193] = (nil)
[194] = (nil)
[195] = (nil)
[196] = (nil)
[197] = (nil)
[198] = (nil)
[199] = (nil)
[200] = (nil)
[201] = (nil)
[202] = (nil)
[203] = (nil)
[204] = (nil)
[205] = (nil)
[206] = (nil)
[207] = (nil)
[208] = (nil)
[209] = (nil)
[210] = (nil)
[211] = (nil)
[212] = (nil)
[213] = (nil)
[214] = (nil)
[215] = (nil)
[216] = (nil)
[217] = (nil)
[218] = (nil)
[219] = (nil)
[220] = (nil)
[221] = (nil)
[222] = (nil)
[223] = (nil)
[224] = (nil)
[225] = (nil)
[226] = (nil)
[227] = (nil)
[228] = (nil)
[229] = (nil)
[230] = (nil)
[231] = (nil)
[232] = (nil)
[233] = (nil)
[234] = (nil)
[235] = (nil)
[236] = (nil)
[237] = (nil)
[238] = (nil)
[239] = (nil)
[240] = (nil)
[241] = (nil)
[242] = (nil)
[243] = (nil)
[244] = (nil)
[245] = (nil)
[246] = (nil)
[247] = (nil)
[248] = (nil)
[249] = (nil)
[250] = (nil)
[251] = (nil)
[252] = (nil)
[253] = (nil)
[254] = (nil)
[255] = (nil)
[256] = (nil)
[257] = (nil)
[258] = (nil)
[259] = (nil)
[260] = (nil)
[261] = (nil)
[262] = (nil)
[263] = (nil)
[264] = (nil)
[265] = (nil)
[266] = (nil)
[267] = (nil)
[268] = (nil)
[269] = (nil)
[270] = (nil)
[271] = (nil)
[272] = (nil)
[273] = (nil)
[274] = (nil)
[275] = (nil)
[276] = (nil)
[277] = (nil)
[278] = (nil)
[279] = (nil)
[280] = (nil)
[281] = (nil)
[282] = (nil)
[283] = (nil)
[284] = (nil)
[285] = (nil)
[286] = (nil)
[287] = (nil)
[288] = (nil)
[289] = (nil)
[290] = (nil)
[291] = (nil)
[292] = (nil)
[293] = (nil)
[294] = (nil)
[295] = (nil)
[296] = (nil)
[297] = (nil)
[298] = (nil)
[299] = (nil)
[300] = (nil)
[301] = (nil)
[302] = (nil)
[303] = (nil)
[304] = (nil)
[305] = (nil)
[306] = (nil)
[307] = (nil)
[308] = (nil)
[309] = (nil)
[310] = (nil)
[311] = (nil)
[312] = (nil)
[313] = (nil)
[314] = (nil)
[315] = (nil)
[316] = (nil)
[317] = (nil)
[318] = (nil)
[319] = (nil)
[320] = (nil)
[321] = (nil)
[322] = (nil)
[323] = (nil)
[324] = (nil)
[325] = (nil)
[326] = (nil)
[327] = (nil)
[328] = (nil)
[329] = (nil)
[330] = (nil)
[331] = (nil)
[332] = (nil)
[333] = (nil)
[334] = (nil)
[335] = (nil)
[336] = (nil)
[337] = (nil)
[338] = (nil)
[339] = (nil)
[340] = (nil)
[341] = (nil)
[342] = (nil)
[343] = (nil)
[344] = (nil)
[345] = (nil)
[346] = (nil)
[347] = (nil)
[348] = (nil)
[349] = (nil)
[350] = (nil)
[351] = (nil)
[352] = (nil)
[353] = (nil)
[354] = (nil)
[355] = (nil)
[356] = (nil)
[357] = (nil)
[358] = (nil)
[359] = (nil)
[360] = (nil)
[361] = (nil)
[362] = (nil)
[363] = (nil)
[364] = (nil)
[365] = (nil)
[366] = (nil)
[367] = (nil)
[368] = (nil)
[369] = (nil)
[370] = (nil)
[371] = (nil)
[372] = (nil)
[373] = (nil)
[374] = (nil)
[375] = (nil)
[376] = (nil)
[377] = (nil)
[378] = (nil)
[379] = (nil)
[380] = (nil)
[381] = (nil)
[382] = (nil)
[383] = (nil)
[384] = (nil)
[385] = (nil)
[386] = (nil)
[387] = (nil)
[388] = (nil)
[389] = (nil)
[390] = (nil)
[391] = (nil)
[392] = (nil)
[393] = (nil)
[394] = (nil)
[395] = (nil)
[396] = (nil)
[397] = (nil)
[398] = (nil)
[399] = (nil)
[400] = (nil)
[401] = (nil)
[402] = (nil)
[403] = (nil)
[404] = (nil)
[405] = (nil)
[406] = (nil)
[407] = (nil)
[408] = (nil)
[409] = (nil)
[410] = (nil)
[411] = (nil)
[412] = (nil)
[413] = (nil)
[414] = (nil)
[415] = (nil)
[416] = (nil)
[417] = (nil)
[418] = (nil)
[419] = (nil)
[420] = (nil)
[421] = (nil)
[422] = (nil)
[423] = (nil)
[424] = (nil)
[425] = (nil)
[426] = (nil)
[427] = (nil)
[428] = (nil)
[429] = (nil)
[430] = (nil)
[431] = (nil)
[432] = (nil)
[433] = (nil)
[434] = (nil)
[435] = (nil)
[436] = (nil)
[437] = (nil)
[438] = (nil)
[439] = (nil)
[440] = (nil)
[441] = (nil)
[442] = (nil)
[443] = (nil)
[444] = (nil)
[445] = (nil)
[446] = (nil)
[447] = (nil)
[448] = (nil)
[449] = (nil)
[450] = (nil)
[451] = (nil)
[452] = (nil)
[453] = (nil)
[454] = (nil)
[455] = (nil)
[456] = (nil)
[457] = (nil)
[458] = (nil)
[459] = (nil)
[460] = (nil)
[461] = (nil)
[462] = (nil)
[463] = (nil)
[464] = (nil)
[465] = (nil)
[466] = (nil)
[467] = (nil)
[468] = (nil)
[469] = (nil)
[470] = (nil)
[471] = (nil)
[472] = (nil)
[473] = (nil)
[474] = (nil)
[475] = (nil)
[476] = (nil)
[477] = (nil)
[478] = (nil)
[479] = (nil)
[480] = (nil)
[481] = (nil)
[482] = (nil)
[483] = (nil)
[484] = (nil)
[485] = (nil)
[486] = (nil)
[487] = (nil)
[488] = (nil)
[489] = (nil)
[490] = (nil)
[491] = (nil)
[492] = (nil)
[493] = (nil)
[494] = (nil)
[495] = (nil)
[496] = (nil)
[497] = (nil)
[498] = (nil)
[499] = (nil)
[500] = (nil)
[501] = (nil)
[502] = (nil)
[503] = (nil)
[504] = (nil)
[505] = (nil)
[506] = (nil)
[507] = (nil)
[508] = (nil)
[509] = (nil)
[510] = (nil)
[511] = (nil)
</gap>

10
testgap/remove.tst Normal file
View File

@ -0,0 +1,10 @@
# Remove values
a 5 42
a 10 43
a 13 44
a 510 45
a 511 46
d 512 43

514
testgap/remove.tst.ref Normal file
View File

@ -0,0 +1,514 @@
<gap len=512 hardlimit=0>
[0] = (nil)
[1] = (nil)
[2] = (nil)
[3] = (nil)
[4] = (nil)
[5] = 0x42
[6] = (nil)
[7] = (nil)
[8] = (nil)
[9] = (nil)
[10] = (nil)
[11] = (nil)
[12] = 0x44
[13] = (nil)
[14] = (nil)
[15] = (nil)
[16] = (nil)
[17] = (nil)
[18] = (nil)
[19] = (nil)
[20] = (nil)
[21] = (nil)
[22] = (nil)
[23] = (nil)
[24] = (nil)
[25] = (nil)
[26] = (nil)
[27] = (nil)
[28] = (nil)
[29] = (nil)
[30] = (nil)
[31] = (nil)
[32] = (nil)
[33] = (nil)
[34] = (nil)
[35] = (nil)
[36] = (nil)
[37] = (nil)
[38] = (nil)
[39] = (nil)
[40] = (nil)
[41] = (nil)
[42] = (nil)
[43] = (nil)
[44] = (nil)
[45] = (nil)
[46] = (nil)
[47] = (nil)
[48] = (nil)
[49] = (nil)
[50] = (nil)
[51] = (nil)
[52] = (nil)
[53] = (nil)
[54] = (nil)
[55] = (nil)
[56] = (nil)
[57] = (nil)
[58] = (nil)
[59] = (nil)
[60] = (nil)
[61] = (nil)
[62] = (nil)
[63] = (nil)
[64] = (nil)
[65] = (nil)
[66] = (nil)
[67] = (nil)
[68] = (nil)
[69] = (nil)
[70] = (nil)
[71] = (nil)
[72] = (nil)
[73] = (nil)
[74] = (nil)
[75] = (nil)
[76] = (nil)
[77] = (nil)
[78] = (nil)
[79] = (nil)
[80] = (nil)
[81] = (nil)
[82] = (nil)
[83] = (nil)
[84] = (nil)
[85] = (nil)
[86] = (nil)
[87] = (nil)
[88] = (nil)
[89] = (nil)
[90] = (nil)
[91] = (nil)
[92] = (nil)
[93] = (nil)
[94] = (nil)
[95] = (nil)
[96] = (nil)
[97] = (nil)
[98] = (nil)
[99] = (nil)
[100] = (nil)
[101] = (nil)
[102] = (nil)
[103] = (nil)
[104] = (nil)
[105] = (nil)
[106] = (nil)
[107] = (nil)
[108] = (nil)
[109] = (nil)
[110] = (nil)
[111] = (nil)
[112] = (nil)
[113] = (nil)
[114] = (nil)
[115] = (nil)
[116] = (nil)
[117] = (nil)
[118] = (nil)
[119] = (nil)
[120] = (nil)
[121] = (nil)
[122] = (nil)
[123] = (nil)
[124] = (nil)
[125] = (nil)
[126] = (nil)
[127] = (nil)
[128] = (nil)
[129] = (nil)
[130] = (nil)
[131] = (nil)
[132] = (nil)
[133] = (nil)
[134] = (nil)
[135] = (nil)
[136] = (nil)
[137] = (nil)
[138] = (nil)
[139] = (nil)
[140] = (nil)
[141] = (nil)
[142] = (nil)
[143] = (nil)
[144] = (nil)
[145] = (nil)
[146] = (nil)
[147] = (nil)
[148] = (nil)
[149] = (nil)
[150] = (nil)
[151] = (nil)
[152] = (nil)
[153] = (nil)
[154] = (nil)
[155] = (nil)
[156] = (nil)
[157] = (nil)
[158] = (nil)
[159] = (nil)
[160] = (nil)
[161] = (nil)
[162] = (nil)
[163] = (nil)
[164] = (nil)
[165] = (nil)
[166] = (nil)
[167] = (nil)
[168] = (nil)
[169] = (nil)
[170] = (nil)
[171] = (nil)
[172] = (nil)
[173] = (nil)
[174] = (nil)
[175] = (nil)
[176] = (nil)
[177] = (nil)
[178] = (nil)
[179] = (nil)
[180] = (nil)
[181] = (nil)
[182] = (nil)
[183] = (nil)
[184] = (nil)
[185] = (nil)
[186] = (nil)
[187] = (nil)
[188] = (nil)
[189] = (nil)
[190] = (nil)
[191] = (nil)
[192] = (nil)
[193] = (nil)
[194] = (nil)
[195] = (nil)
[196] = (nil)
[197] = (nil)
[198] = (nil)
[199] = (nil)
[200] = (nil)
[201] = (nil)
[202] = (nil)
[203] = (nil)
[204] = (nil)
[205] = (nil)
[206] = (nil)
[207] = (nil)
[208] = (nil)
[209] = (nil)
[210] = (nil)
[211] = (nil)
[212] = (nil)
[213] = (nil)
[214] = (nil)
[215] = (nil)
[216] = (nil)
[217] = (nil)
[218] = (nil)
[219] = (nil)
[220] = (nil)
[221] = (nil)
[222] = (nil)
[223] = (nil)
[224] = (nil)
[225] = (nil)
[226] = (nil)
[227] = (nil)
[228] = (nil)
[229] = (nil)
[230] = (nil)
[231] = (nil)
[232] = (nil)
[233] = (nil)
[234] = (nil)
[235] = (nil)
[236] = (nil)
[237] = (nil)
[238] = (nil)
[239] = (nil)
[240] = (nil)
[241] = (nil)
[242] = (nil)
[243] = (nil)
[244] = (nil)
[245] = (nil)
[246] = (nil)
[247] = (nil)
[248] = (nil)
[249] = (nil)
[250] = (nil)
[251] = (nil)
[252] = (nil)
[253] = (nil)
[254] = (nil)
[255] = (nil)
[256] = (nil)
[257] = (nil)
[258] = (nil)
[259] = (nil)
[260] = (nil)
[261] = (nil)
[262] = (nil)
[263] = (nil)
[264] = (nil)
[265] = (nil)
[266] = (nil)
[267] = (nil)
[268] = (nil)
[269] = (nil)
[270] = (nil)
[271] = (nil)
[272] = (nil)
[273] = (nil)
[274] = (nil)
[275] = (nil)
[276] = (nil)
[277] = (nil)
[278] = (nil)
[279] = (nil)
[280] = (nil)
[281] = (nil)
[282] = (nil)
[283] = (nil)
[284] = (nil)
[285] = (nil)
[286] = (nil)
[287] = (nil)
[288] = (nil)
[289] = (nil)
[290] = (nil)
[291] = (nil)
[292] = (nil)
[293] = (nil)
[294] = (nil)
[295] = (nil)
[296] = (nil)
[297] = (nil)
[298] = (nil)
[299] = (nil)
[300] = (nil)
[301] = (nil)
[302] = (nil)
[303] = (nil)
[304] = (nil)
[305] = (nil)
[306] = (nil)
[307] = (nil)
[308] = (nil)
[309] = (nil)
[310] = (nil)
[311] = (nil)
[312] = (nil)
[313] = (nil)
[314] = (nil)
[315] = (nil)
[316] = (nil)
[317] = (nil)
[318] = (nil)
[319] = (nil)
[320] = (nil)
[321] = (nil)
[322] = (nil)
[323] = (nil)
[324] = (nil)
[325] = (nil)
[326] = (nil)
[327] = (nil)
[328] = (nil)
[329] = (nil)
[330] = (nil)
[331] = (nil)
[332] = (nil)
[333] = (nil)
[334] = (nil)
[335] = (nil)
[336] = (nil)
[337] = (nil)
[338] = (nil)
[339] = (nil)
[340] = (nil)
[341] = (nil)
[342] = (nil)
[343] = (nil)
[344] = (nil)
[345] = (nil)
[346] = (nil)
[347] = (nil)
[348] = (nil)
[349] = (nil)
[350] = (nil)
[351] = (nil)
[352] = (nil)
[353] = (nil)
[354] = (nil)
[355] = (nil)
[356] = (nil)
[357] = (nil)
[358] = (nil)
[359] = (nil)
[360] = (nil)
[361] = (nil)
[362] = (nil)
[363] = (nil)
[364] = (nil)
[365] = (nil)
[366] = (nil)
[367] = (nil)
[368] = (nil)
[369] = (nil)
[370] = (nil)
[371] = (nil)
[372] = (nil)
[373] = (nil)
[374] = (nil)
[375] = (nil)
[376] = (nil)
[377] = (nil)
[378] = (nil)
[379] = (nil)
[380] = (nil)
[381] = (nil)
[382] = (nil)
[383] = (nil)
[384] = (nil)
[385] = (nil)
[386] = (nil)
[387] = (nil)
[388] = (nil)
[389] = (nil)
[390] = (nil)
[391] = (nil)
[392] = (nil)
[393] = (nil)
[394] = (nil)
[395] = (nil)
[396] = (nil)
[397] = (nil)
[398] = (nil)
[399] = (nil)
[400] = (nil)
[401] = (nil)
[402] = (nil)
[403] = (nil)
[404] = (nil)
[405] = (nil)
[406] = (nil)
[407] = (nil)
[408] = (nil)
[409] = (nil)
[410] = (nil)
[411] = (nil)
[412] = (nil)
[413] = (nil)
[414] = (nil)
[415] = (nil)
[416] = (nil)
[417] = (nil)
[418] = (nil)
[419] = (nil)
[420] = (nil)
[421] = (nil)
[422] = (nil)
[423] = (nil)
[424] = (nil)
[425] = (nil)
[426] = (nil)
[427] = (nil)
[428] = (nil)
[429] = (nil)
[430] = (nil)
[431] = (nil)
[432] = (nil)
[433] = (nil)
[434] = (nil)
[435] = (nil)
[436] = (nil)
[437] = (nil)
[438] = (nil)
[439] = (nil)
[440] = (nil)
[441] = (nil)
[442] = (nil)
[443] = (nil)
[444] = (nil)
[445] = (nil)
[446] = (nil)
[447] = (nil)
[448] = (nil)
[449] = (nil)
[450] = (nil)
[451] = (nil)
[452] = (nil)
[453] = (nil)
[454] = (nil)
[455] = (nil)
[456] = (nil)
[457] = (nil)
[458] = (nil)
[459] = (nil)
[460] = (nil)
[461] = (nil)
[462] = (nil)
[463] = (nil)
[464] = (nil)
[465] = (nil)
[466] = (nil)
[467] = (nil)
[468] = (nil)
[469] = (nil)
[470] = (nil)
[471] = (nil)
[472] = (nil)
[473] = (nil)
[474] = (nil)
[475] = (nil)
[476] = (nil)
[477] = (nil)
[478] = (nil)
[479] = (nil)
[480] = (nil)
[481] = (nil)
[482] = (nil)
[483] = (nil)
[484] = (nil)
[485] = (nil)
[486] = (nil)
[487] = (nil)
[488] = (nil)
[489] = (nil)
[490] = (nil)
[491] = (nil)
[492] = (nil)
[493] = (nil)
[494] = (nil)
[495] = (nil)
[496] = (nil)
[497] = (nil)
[498] = (nil)
[499] = (nil)
[500] = (nil)
[501] = (nil)
[502] = (nil)
[503] = (nil)
[504] = (nil)
[505] = (nil)
[506] = (nil)
[507] = (nil)
[508] = (nil)
[509] = 0x45
[510] = 0x46
[511] = 0x46
</gap>

30
testgap/run Executable file
View File

@ -0,0 +1,30 @@
#! /usr/bin/perl -w
# This runs all the tests.
# Tests scripts are in *.tst files.
# Corresponding output is put in *.out.
# Reference output is put in *.ref.
# Any discrepancy will be reported!
use strict;
my @res;
foreach my $fn (`ls *.tst`) {
chomp $fn;
my $cmd = "./gtest $fn $fn.out";
print "$cmd\n";
`$cmd`;
my $res = system("diff -u $fn.ref $fn.out");
push @res, [$fn, ($res == 0 ? "OK" : "*KO*")];
}
format =
@<<<<<<<<<<<<<<<<<<<<<<<<<< @>>>
$_->[0], $_->[1]
.
#format_name STDOUT test_result;
map { write; } @res;

View File

@ -302,6 +302,11 @@ struct connection* udp_c2s_forward(int sockfd, struct loop_info* fd_info)
cnx->addrlen = addrlen;
cnx->local_endpoint = sockfd;
if (inc_proto_connections(cnx)) {
tidy_connection(cnx, fd_info);
return NULL;
}
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 -- increase udp_max_connections");

View File

@ -1,5 +0,0 @@
#ifndef VERSION_H
#define VERSION_H
#define VERSION "v2.2.0-dirty"
#endif