mirror of
https://github.com/yrutschle/sslh.git
synced 2025-08-11 21:11:36 +03:00
Compare commits
28 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
8aa97d7118 | ||
|
8ad8bfdb5d | ||
|
4c2e4e01f2 | ||
|
2ecb681277 | ||
|
54dc8374ab | ||
|
8c4253f145 | ||
|
4483e9c7e5 | ||
|
65c9c4ce95 | ||
|
5434dc59df | ||
|
20290bbfa6 | ||
|
1dfd70c20c | ||
|
feaf528a60 | ||
|
11da63cf4e | ||
|
28ea73301e | ||
|
cd9b85fd4f | ||
|
bd9ed060e4 | ||
|
b7556c07bd | ||
|
6b9ec3a46e | ||
|
ad66e79f46 | ||
|
b12220e640 | ||
|
204305a88f | ||
|
0f96ed8adb | ||
|
43e75a0a8c | ||
|
ad1f5d68e9 | ||
|
ff8206f7c8 | ||
|
8298daf686 | ||
|
168477ea34 | ||
|
b2bcfc26b2 |
24
ChangeLog
24
ChangeLog
@ -1,3 +1,27 @@
|
||||
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
|
||||
|
@ -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)),)
|
||||
@ -145,8 +145,8 @@ 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
|
||||
|
||||
tags:
|
||||
ctags --globals -T *.[ch]
|
||||
tags: *.c *.h
|
||||
ctags *.[ch]
|
||||
|
||||
cscope:
|
||||
-find . -name "*.[chS]" >cscope.files
|
||||
|
15
README.md
15
README.md
@ -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
|
||||
|
48
common.c
48
common.c
@ -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");
|
||||
|
||||
|
8
common.h
8
common.h
@ -109,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;
|
||||
|
||||
@ -138,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
|
||||
@ -174,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[]);
|
||||
@ -199,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
|
||||
|
@ -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
55
doc/max_connections.md
Normal 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
120
doc/podman.md
Normal 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.
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
14
example.cfg
14
example.cfg
@ -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
14
gap.c
@ -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
9
gap.h
@ -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
63
probe.c
@ -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;
|
||||
|
||||
}
|
||||
|
7
probe.h
7
probe.h
@ -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
|
||||
|
@ -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;
|
||||
|
@ -50,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);
|
||||
|
||||
@ -89,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
|
||||
|
48
sslh-conf.c
48
sslh-conf.c
@ -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);
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
54
sslh-fork.c
54
sslh-fork.c
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
17
t
@ -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};
|
||||
|
||||
|
||||
|
@ -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)) {
|
||||
|
@ -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
|
||||
|
21
test.cfg
21
test.cfg
@ -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
7
testgap/Makefile
Normal 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
102
testgap/gtest.c
Normal 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
11
testgap/hardlimit.tst
Normal 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
514
testgap/hardlimit.tst.ref
Normal 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>
|
5
testgap/insert_extend.tst
Normal file
5
testgap/insert_extend.tst
Normal file
@ -0,0 +1,5 @@
|
||||
# Insertions after the first page, to extend the gap
|
||||
|
||||
a 5 42
|
||||
a 1024 43
|
||||
a 1028 44
|
1538
testgap/insert_extend.tst.ref
Normal file
1538
testgap/insert_extend.tst.ref
Normal file
File diff suppressed because it is too large
Load Diff
6
testgap/inserts.tst
Normal file
6
testgap/inserts.tst
Normal file
@ -0,0 +1,6 @@
|
||||
# Simple insertions in the first page
|
||||
|
||||
a 5 42
|
||||
a 10 43
|
||||
|
||||
|
514
testgap/inserts.tst.ref
Normal file
514
testgap/inserts.tst.ref
Normal 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
10
testgap/remove.tst
Normal 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
514
testgap/remove.tst.ref
Normal 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
30
testgap/run
Executable 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;
|
||||
|
@ -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");
|
||||
|
Loading…
x
Reference in New Issue
Block a user