mirror of
https://github.com/yrutschle/sslh.git
synced 2026-10-22 18:12:10 +03:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
93600d1fb1 | ||
|
|
04f258e705 | ||
|
|
1fb888bbf2 | ||
|
|
1f0cab2aee | ||
|
|
ff9328fa6c | ||
|
|
e941e8dd2e | ||
|
|
54fe4b2f47 | ||
|
|
33129481cf | ||
|
|
4cc0867753 | ||
|
|
4728730abc | ||
|
|
9e7b4b751f | ||
|
|
b11f2620ab | ||
|
|
056c283145 | ||
|
|
00beb9595d | ||
|
|
27f3e9075a | ||
|
|
3912330040 | ||
|
|
4cbff962db | ||
|
|
29c949e051 | ||
|
|
e8a84e6c22 | ||
|
|
42476d3cdc | ||
|
|
f48f74c004 | ||
|
|
3af02d5c44 | ||
|
|
9e6d5cc13a | ||
|
|
74fe57147b | ||
|
|
9acec69b9a | ||
|
|
2a81ec0650 | ||
|
|
596a4ee5b4 | ||
|
|
dbfeb480fe | ||
|
|
6b8a203691 | ||
|
|
95e351e150 | ||
|
|
4b4c585107 | ||
|
|
0217e842d2 | ||
|
|
877ef1d27c | ||
|
|
b1bfd5aee3 | ||
|
|
af38544316 | ||
|
|
d4908178bf | ||
|
|
9deedec029 | ||
|
|
8ef3e8ddd8 | ||
|
|
1eeba07396 | ||
|
|
2c48b8d83b | ||
|
|
44bac28718 | ||
|
|
f520c616cc | ||
|
|
39de82ae13 | ||
|
|
08bea0e15d | ||
|
|
cb52f3cdb4 | ||
|
|
306164531f | ||
|
|
40ddc4900a | ||
|
|
32aaacf4e7 | ||
|
|
a6a73f4d32 | ||
|
|
3d959eb425 | ||
|
|
b2fd9d9daf | ||
|
|
8ff27e931f | ||
|
|
ecbc33ba29 | ||
|
|
5635dc5142 | ||
|
|
bb76bc1d31 | ||
|
|
4a5ccb75b2 | ||
|
|
6672cc0f86 | ||
|
|
9dd560493a | ||
|
|
db5ed29fa2 | ||
|
|
295dba93b5 | ||
|
|
f23da1fc36 | ||
|
|
20764074cb | ||
|
|
7b7c9231b0 | ||
|
|
397f672248 | ||
|
|
3707c5b8a6 | ||
|
|
5666a1bb9d | ||
|
|
7b9c7f0fb2 | ||
|
|
eec2446723 | ||
|
|
d29c9524bd | ||
|
|
db4ae0ef9d | ||
|
|
ee48dae8c5 | ||
|
|
842f6b0473 | ||
|
|
1f64a71cde | ||
|
|
00fc8e5d95 | ||
|
|
b9602ab98b | ||
|
|
486f8a0090 | ||
|
|
3a1ac6c8d7 | ||
|
|
9dc3e3ce56 | ||
|
|
61af0b2e09 | ||
|
|
c7ddee0409 | ||
|
|
a3640775bb |
No files matched your search
@@ -0,0 +1,63 @@
|
||||
name: Create and publish Container image
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
tags:
|
||||
- 'v*'
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_NAME: ${{ github.repository }}
|
||||
|
||||
jobs:
|
||||
build-and-push-image:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: Login to Container registry
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v4
|
||||
with:
|
||||
images: |
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
tags: |
|
||||
type=ref,event=branch
|
||||
type=ref,event=pr
|
||||
type=edge
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=semver,pattern={{major}}
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
platforms: linux/amd64,linux/386,linux/arm64,linux/arm/v6,linux/arm/v7
|
||||
context: .
|
||||
file: Dockerfile
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
@@ -1,5 +1,5 @@
|
||||
v2.0:
|
||||
New sslh-ev: this is functionaly equivalent to
|
||||
New sslh-ev: this is functionally equivalent to
|
||||
sslh-select (mono-process, only forks for specified
|
||||
protocols), but based on libev, which should make it
|
||||
scalable to large numbers of connections.
|
||||
@@ -41,7 +41,7 @@ v1.22: 17AUG2021
|
||||
combined with incoming TLS with SNI. UDP clients
|
||||
and servers need to agree on the IPv4/IPv6 they use:
|
||||
use the same protocol on all sides! Often, this
|
||||
means explicitely using 'ip4-localhost'.
|
||||
means explicitly using 'ip4-localhost'.
|
||||
UDP sender-receiver pairs (connections, so to speak)
|
||||
are kept for 60s, which can be changed with
|
||||
`udp_timeout` in the configuration.
|
||||
@@ -83,7 +83,7 @@ v1.21: 11JUL2020
|
||||
|
||||
Added TCP_FASTOPEN support for client sockets (if
|
||||
tfo_ok is specified in their configuration) and for
|
||||
listenint socket, if all client protocols support it.
|
||||
listening socket, if all client protocols support it.
|
||||
(Craig Andrews)
|
||||
|
||||
Added 'minlength' option to skip a probe if less
|
||||
@@ -109,8 +109,8 @@ v1.20: 20NOV2018
|
||||
Before, probes were tried in order, repeating on the
|
||||
same probe as long it returned PROBE_AGAIN before
|
||||
moving to the next one. This means a probe which
|
||||
requires a lot of data (i.e. returne PROBE_AGAIN for
|
||||
a long time) could prevent sucessful matches from
|
||||
requires a lot of data (i.e. return PROBE_AGAIN for
|
||||
a long time) could prevent successful matches from
|
||||
subsequent probes. The configuration file needed to
|
||||
take that into account.
|
||||
|
||||
@@ -171,7 +171,7 @@ v1.18: 29MAR2016
|
||||
v1.17: 09MAR2015
|
||||
Support RFC5952-style IPv6 addresses, e.g. [::]:443.
|
||||
|
||||
Transparant proxy support for FreeBSD.
|
||||
Transparent proxy support for FreeBSD.
|
||||
(Ruben van Staveren)
|
||||
|
||||
Using -F with no argument will try
|
||||
@@ -200,7 +200,7 @@ v1.16: 11FEB2014
|
||||
|
||||
Libcap support: Keep only CAP_NET_ADMIN if started
|
||||
as root with transparent proxying and dropping
|
||||
priviledges (enable USELIBCAP in Makefile). This
|
||||
privileges (enable USELIBCAP in Makefile). This
|
||||
avoids having to mess with filesystem capabilities.
|
||||
(Sebastian Schmidt/yath)
|
||||
|
||||
@@ -209,7 +209,7 @@ v1.16: 11FEB2014
|
||||
actual errors if connections are dropped before
|
||||
getting to getpeername).
|
||||
|
||||
Set IP_FREEDBIND if available to bind to addresses
|
||||
Set IP_FREEBIND if available to bind to addresses
|
||||
that don't yet exist.
|
||||
|
||||
v1.15: 27JUL2013
|
||||
@@ -294,7 +294,7 @@ v1.11: 21APR2012
|
||||
--user isn't specified, just run as current user.
|
||||
|
||||
No longer create PID file by default, it should be
|
||||
explicitely set with --pidfile.
|
||||
explicitly set with --pidfile.
|
||||
|
||||
No longer log to syslog if in foreground. Logs are
|
||||
instead output to stderr.
|
||||
@@ -385,7 +385,7 @@ v1.8: 15JUL2011
|
||||
v1.7: 01FEB2010
|
||||
Added CentOS init.d script (Andre Krajnik).
|
||||
|
||||
Fixed default ssl address inconsistancy, now
|
||||
Fixed default ssl address inconsistency, now
|
||||
defaults to "localhost:443" and fixed documentation
|
||||
accordingly (pointed by Markus Schalke).
|
||||
|
||||
|
||||
+32
-17
@@ -1,23 +1,38 @@
|
||||
FROM alpine:latest as build
|
||||
ARG ALPINE_VERSION="latest"
|
||||
ARG TARGET_ARCH="library"
|
||||
|
||||
ADD . /sslh
|
||||
FROM docker.io/${TARGET_ARCH}/alpine:${ALPINE_VERSION} AS build
|
||||
|
||||
RUN \
|
||||
apk add \
|
||||
gcc \
|
||||
libconfig-dev \
|
||||
make \
|
||||
musl-dev \
|
||||
pcre2-dev \
|
||||
perl && \
|
||||
cd /sslh && \
|
||||
make sslh-select && \
|
||||
strip sslh-select
|
||||
WORKDIR /sslh
|
||||
|
||||
FROM alpine:latest
|
||||
RUN apk add --no-cache \
|
||||
'gcc' \
|
||||
'libconfig-dev' \
|
||||
'make' \
|
||||
'musl-dev' \
|
||||
'pcre2-dev' \
|
||||
'perl' \
|
||||
;
|
||||
|
||||
COPY --from=build /sslh/sslh-select /sslh
|
||||
COPY . /sslh
|
||||
|
||||
RUN apk --no-cache add libconfig pcre2
|
||||
RUN make sslh-select && strip sslh-select
|
||||
|
||||
ENTRYPOINT [ "/sslh", "--foreground"]
|
||||
FROM docker.io/${TARGET_ARCH}/alpine:${ALPINE_VERSION}
|
||||
|
||||
COPY --from=build "/sslh/sslh-select" "/usr/local/bin/sslh"
|
||||
RUN apk add --no-cache \
|
||||
'libconfig' \
|
||||
'pcre2' \
|
||||
'iptables' \
|
||||
'ip6tables' \
|
||||
'libcap' \
|
||||
&& \
|
||||
adduser -s '/bin/sh' -S -D sslh && \
|
||||
setcap cap_net_bind_service,cap_net_raw+ep /usr/local/bin/sslh
|
||||
|
||||
COPY "./container-entrypoint.sh" "/init"
|
||||
ENTRYPOINT [ "/init" ]
|
||||
|
||||
# required for updating iptables
|
||||
USER root:root
|
||||
@@ -30,7 +30,8 @@ endif
|
||||
|
||||
CC ?= gcc
|
||||
AR ?= ar
|
||||
CFLAGS ?=-Wall -O2 -DLIBPCRE -g $(CFLAGS_COV) $(CFLAGS_SAN)
|
||||
CFLAGS +=-Wall -O2 -DLIBPCRE -g $(CFLAGS_COV) $(CFLAGS_SAN)
|
||||
|
||||
|
||||
LIBS=-lm -lpcre2-8
|
||||
OBJS=sslh-conf.o common.o log.o sslh-main.o probe.o tls.o argtable3.o collection.o gap.o tcp-probe.o
|
||||
@@ -88,8 +89,12 @@ sslh: sslh-fork sslh-select sslh-ev
|
||||
$(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
|
||||
|
||||
|
||||
sslh-conf.c sslh-conf.h: sslhconf.cfg
|
||||
c2s:
|
||||
conf2struct sslhconf.cfg
|
||||
conf2struct echosrv.cfg
|
||||
|
||||
sslh-conf.c sslh-conf.h: sslhconf.cfg
|
||||
$(warning "sslhconf.cfg is more recent than sslh-conf.[ch]. Use `make c2s` to rebuild using `conf2struct`")
|
||||
|
||||
sslh-fork: version.h Makefile $(FORK_OBJS)
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -o sslh-fork $(FORK_OBJS) $(LIBS)
|
||||
@@ -104,7 +109,7 @@ systemd-sslh-generator: systemd-sslh-generator.o
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -o systemd-sslh-generator systemd-sslh-generator.o -lconfig
|
||||
|
||||
echosrv-conf.c echosrv-conf.h: echosrv.cfg
|
||||
conf2struct echosrv.cfg
|
||||
$(warning "echosrv.cfg is more recent than echosrv-conf.[ch]. Use `make c2s` to rebuild using `conf2struct`")
|
||||
|
||||
echosrv: version.h echosrv-conf.c echosrv.o echosrv-conf.o argtable3.o
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -o echosrv echosrv.o echosrv-conf.o argtable3.o $(LIBS)
|
||||
|
||||
@@ -11,7 +11,7 @@ protocol that can be tested using a regular expression, can
|
||||
be recognised. A typical use case is to allow serving
|
||||
several services on port 443 (e.g. to connect to SSH from
|
||||
inside a corporate firewall, which almost never block port
|
||||
443) while still serving HTTPS on that port.
|
||||
443) while still serving HTTPS on that port.
|
||||
|
||||
Hence `sslh` acts as a protocol demultiplexer, or a
|
||||
switchboard. With the SNI and ALPN probe, it makes a good
|
||||
@@ -20,8 +20,8 @@ address.
|
||||
|
||||
`sslh` has the bells and whistles expected from a mature
|
||||
daemon: privilege and capabilities dropping, inetd support,
|
||||
systemd support, transparent proxying, chroot, logging,
|
||||
IPv4 and IPv6, TCP and UDP, a fork-based and a select-based
|
||||
systemd support, transparent proxying, chroot, logging,
|
||||
IPv4 and IPv6, TCP and UDP, a fork-based and a select-based
|
||||
model, and more.
|
||||
|
||||
Install
|
||||
@@ -44,16 +44,15 @@ How to use
|
||||
|
||||
---
|
||||
|
||||
Build docker image
|
||||
|
||||
make docker
|
||||
|
||||
|
||||
```bash
|
||||
docker run \
|
||||
--cap-add CAP_NET_RAW \
|
||||
--cap-add CAP_NET_BIND_SERVICE \
|
||||
--rm \
|
||||
-it \
|
||||
sslh:latest \
|
||||
ghcr.io/yrutschle/sslh:latest \
|
||||
--foreground \
|
||||
--listen=0.0.0.0:443 \
|
||||
--ssh=hostname:22 \
|
||||
--tls=hostname:443
|
||||
@@ -61,7 +60,7 @@ docker run \
|
||||
|
||||
docker-compose example
|
||||
|
||||
```
|
||||
```yaml
|
||||
version: "3"
|
||||
|
||||
services:
|
||||
@@ -70,7 +69,7 @@ services:
|
||||
hostname: sslh
|
||||
ports:
|
||||
- 443:443
|
||||
command: --listen=0.0.0.0:443 --tls=nginx:443 --openvpn=openvpn:1194
|
||||
command: --foreground --listen=0.0.0.0:443 --tls=nginx:443 --openvpn=openvpn:1194
|
||||
depends_on:
|
||||
- nginx
|
||||
- openvpn
|
||||
@@ -82,6 +81,85 @@ services:
|
||||
image: openvpn
|
||||
```
|
||||
|
||||
Transparent mode 1: using sslh container for networking
|
||||
|
||||
_Note: For transparent mode to work, the sslh container must be able to reach your services via **localhost**_
|
||||
```yaml
|
||||
version: "3"
|
||||
|
||||
services:
|
||||
sslh:
|
||||
build: https://github.com/yrutschle/sslh.git
|
||||
container_name: sslh
|
||||
environment:
|
||||
- TZ=${TZ}
|
||||
cap_add:
|
||||
- NET_ADMIN
|
||||
- NET_RAW
|
||||
- NET_BIND_SERVICE
|
||||
sysctls:
|
||||
- net.ipv4.conf.default.route_localnet=1
|
||||
- net.ipv4.conf.all.route_localnet=1
|
||||
command: --transparent --foreground --listen=0.0.0.0:443 --tls=localhost:8443 --openvpn=localhost:1194
|
||||
ports:
|
||||
- 443:443 #sslh
|
||||
|
||||
- 80:80 #nginx
|
||||
- 8443:8443 #nginx
|
||||
|
||||
- 1194:1194 #openvpn
|
||||
extra_hosts:
|
||||
- localbox:host-gateway
|
||||
restart: unless-stopped
|
||||
|
||||
nginx:
|
||||
image: nginx:latest
|
||||
.....
|
||||
network_mode: service:sslh #set nginx container to use sslh networking.
|
||||
# ^^^ This is required. This makes nginx reachable by sslh via localhost
|
||||
|
||||
openvpn:
|
||||
image: openvpn:latest
|
||||
.....
|
||||
network_mode: service:sslh #set openvpn container to use sslh networking
|
||||
```
|
||||
|
||||
Transparent mode 2: using host networking
|
||||
|
||||
```yaml
|
||||
version: "3"
|
||||
|
||||
services:
|
||||
sslh:
|
||||
build: https://github.com/yrutschle/sslh.git
|
||||
container_name: sslh
|
||||
environment:
|
||||
- TZ=${TZ}
|
||||
cap_add:
|
||||
- NET_ADMIN
|
||||
- NET_RAW
|
||||
- NET_BIND_SERVICE
|
||||
# must be set manually
|
||||
#sysctls:
|
||||
# - net.ipv4.conf.default.route_localnet=1
|
||||
# - net.ipv4.conf.all.route_localnet=1
|
||||
command: --transparent --foreground --listen=0.0.0.0:443 --tls=localhost:8443 --openvpn=localhost:1194
|
||||
network_mode: host
|
||||
restart: unless-stopped
|
||||
|
||||
nginx:
|
||||
image: nginx:latest
|
||||
.....
|
||||
ports:
|
||||
- 8443:8443 # bind to docker host on port 8443
|
||||
|
||||
openvpn:
|
||||
image: openvpn:latest
|
||||
.....
|
||||
ports:
|
||||
- 1194:1194 # bind to docker host on port 1194
|
||||
```
|
||||
|
||||
Comments? Questions?
|
||||
====================
|
||||
|
||||
|
||||
+13
-13
@@ -2876,9 +2876,9 @@ static void arg_file_resetfn(struct arg_file* parent) {
|
||||
static const char* arg_basename(const char* filename) {
|
||||
const char *result = NULL, *result1, *result2;
|
||||
|
||||
/* Find the last occurrence of eother file separator character. */
|
||||
/* Two alternative file separator chars are supported as legal */
|
||||
/* file separators but not both together in the same filename. */
|
||||
/* Find the last occurrence of other file separator character. */
|
||||
/* Two alternative file separator chars are supported as legal */
|
||||
/* file separators but not both together in the same filename. */
|
||||
result1 = (filename ? strrchr(filename, FILESEPARATOR1) : NULL);
|
||||
result2 = (filename ? strrchr(filename, FILESEPARATOR2) : NULL);
|
||||
|
||||
@@ -2927,7 +2927,7 @@ static int arg_file_scanfn(struct arg_file* parent, const char* argval) {
|
||||
} else if (!argval) {
|
||||
/* a valid argument with no argument value was given. */
|
||||
/* This happens when an optional argument value was invoked. */
|
||||
/* leave parent arguiment value unaltered but still count the argument. */
|
||||
/* leave parent argument value unaltered but still count the argument. */
|
||||
parent->count++;
|
||||
} else {
|
||||
parent->filename[parent->count] = argval;
|
||||
@@ -3173,7 +3173,7 @@ static int arg_int_scanfn(struct arg_int* parent, const char* argval) {
|
||||
} else if (!argval) {
|
||||
/* a valid argument with no argument value was given. */
|
||||
/* This happens when an optional argument value was invoked. */
|
||||
/* leave parent arguiment value unaltered but still count the argument. */
|
||||
/* leave parent argument value unaltered but still count the argument. */
|
||||
parent->count++;
|
||||
} else {
|
||||
long int val;
|
||||
@@ -3813,8 +3813,8 @@ static const TRexChar* g_nnames[] = {_SC("NONE"), _SC("OP_GREEDY"), _SC("OP_O
|
||||
#endif
|
||||
#define OP_GREEDY (MAX_CHAR + 1) /* * + ? {n} */
|
||||
#define OP_OR (MAX_CHAR + 2)
|
||||
#define OP_EXPR (MAX_CHAR + 3) /* parentesis () */
|
||||
#define OP_NOCAPEXPR (MAX_CHAR + 4) /* parentesis (?:) */
|
||||
#define OP_EXPR (MAX_CHAR + 3) /* parenthesis () */
|
||||
#define OP_NOCAPEXPR (MAX_CHAR + 4) /* parenthesis (?:) */
|
||||
#define OP_DOT (MAX_CHAR + 5)
|
||||
#define OP_CLASS (MAX_CHAR + 6)
|
||||
#define OP_CCLASS (MAX_CHAR + 7)
|
||||
@@ -5313,7 +5313,7 @@ static void arg_parse_untagged(int argc, char** argv, struct arg_hdr** table, st
|
||||
}
|
||||
}
|
||||
|
||||
/* if a tenative error still remains at this point then register it as a proper error */
|
||||
/* if a tentative error still remains at this point then register it as a proper error */
|
||||
if (errorlast) {
|
||||
arg_register_error(endtable, parentlast, errorlast, optarglast);
|
||||
optind++;
|
||||
@@ -5384,7 +5384,7 @@ int arg_parse(int argc, char** argv, void** argtable) {
|
||||
/*
|
||||
Fill in the local copy of argv[]. We need a local copy
|
||||
because getopt rearranges argv[] which adversely affects
|
||||
susbsequent parsing attempts.
|
||||
subsequent parsing attempts.
|
||||
*/
|
||||
for (i = 0; i < argc; i++)
|
||||
argvcopy[i] = argv[i];
|
||||
@@ -5451,7 +5451,7 @@ static void arg_cat_option(char* dest, size_t ndest, const char* shortopts, cons
|
||||
if (shortopts) {
|
||||
char option[3];
|
||||
|
||||
/* note: option array[] is initialiazed dynamically here to satisfy */
|
||||
/* note: option array[] is initialized dynamically here to satisfy */
|
||||
/* a deficiency in the watcom compiler wrt static array initializers. */
|
||||
option[0] = '-';
|
||||
option[1] = shortopts[0];
|
||||
@@ -5509,7 +5509,7 @@ static void arg_cat_optionv(char* dest, size_t ndest, const char* shortopts, con
|
||||
/* "-a|-b|-c" */
|
||||
char shortopt[3];
|
||||
|
||||
/* note: shortopt array[] is initialiazed dynamically here to satisfy */
|
||||
/* note: shortopt array[] is initialized dynamically here to satisfy */
|
||||
/* a deficiency in the watcom compiler wrt static array initializers. */
|
||||
shortopt[0] = '-';
|
||||
shortopt[1] = *c;
|
||||
@@ -5881,7 +5881,7 @@ static void arg_print_formatted_ds(arg_dstr_t ds, const unsigned lmargin, const
|
||||
* Prints the glossary in strict GNU format.
|
||||
* Differences to arg_print_glossary() are:
|
||||
* - wraps lines after 80 chars
|
||||
* - indents lines without shortops
|
||||
* - indents lines without shortopts
|
||||
* - does not accept formatstrings
|
||||
*
|
||||
* Contributed by Uli Fouquet
|
||||
@@ -5956,7 +5956,7 @@ int arg_nullcheck(void** argtable) {
|
||||
* that entry were still allocated ok. Those subsequent allocations will not be
|
||||
* deallocated by arg_free().
|
||||
* Despite the unlikeliness of the problem occurring, and the even unlikelier event
|
||||
* that it has any deliterious effect, it is fixed regardless by replacing arg_free()
|
||||
* that it has any deleterious effect, it is fixed regardless by replacing arg_free()
|
||||
* with the newer arg_freetable() function.
|
||||
* We still keep arg_free() for backwards compatibility.
|
||||
*/
|
||||
|
||||
+2
-2
@@ -87,7 +87,7 @@ typedef int(arg_comparefn)(const void* k1, const void* k2);
|
||||
* that particular arg_xxx arguments, performing post-parse checks, and
|
||||
* reporting errors.
|
||||
* These functions are private to the individual arg_xxx source code
|
||||
* and are the pointer to them are initiliased by that arg_xxx struct's
|
||||
* and are the pointer to them are initialised by that arg_xxx struct's
|
||||
* constructor function. The user could alter them after construction
|
||||
* if desired, but the original intention is for them to be set by the
|
||||
* constructor and left unaltered.
|
||||
@@ -95,7 +95,7 @@ typedef int(arg_comparefn)(const void* k1, const void* k2);
|
||||
typedef struct arg_hdr {
|
||||
char flag; /* Modifier flags: ARG_TERMINATOR, ARG_HASVALUE. */
|
||||
const char* shortopts; /* String defining the short options */
|
||||
const char* longopts; /* String defiing the long options */
|
||||
const char* longopts; /* String defining the long options */
|
||||
const char* datatype; /* Description of the argument data type */
|
||||
const char* glossary; /* Description of the option as shown by arg_print_glossary function */
|
||||
int mincount; /* Minimum number of occurences of this option accepted */
|
||||
|
||||
@@ -348,6 +348,7 @@ int connect_addr(struct connection *cnx, int fd_from, connect_blocking blocking)
|
||||
|
||||
if (transparent) {
|
||||
res = bind_peer(fd, fd_from);
|
||||
if (res == -1) close(fd);
|
||||
CHECK_RES_RETURN(res, "bind_peer", res);
|
||||
}
|
||||
res = connect(fd, a->ai_addr, a->ai_addrlen);
|
||||
|
||||
@@ -112,7 +112,7 @@ struct connection {
|
||||
struct queue q[2];
|
||||
|
||||
/* SOCK_DGRAM */
|
||||
struct sockaddr client_addr; /* Contains the remote client address */
|
||||
struct sockaddr_storage client_addr; /* Contains the remote client address */
|
||||
socklen_t addrlen;
|
||||
|
||||
int local_endpoint; /* Contains the local address */
|
||||
|
||||
Executable
+97
@@ -0,0 +1,97 @@
|
||||
#!/bin/sh
|
||||
# SPDX-License-Identifier: GPL2-or-later
|
||||
#
|
||||
# Copyright (C) 2023 Olliver Schinagl <oliver@schinagl.nl>
|
||||
#
|
||||
# A beginning user should be able to docker run image bash (or sh) without
|
||||
# needing to learn about --entrypoint
|
||||
# https://github.com/docker-library/official-images#consistency
|
||||
|
||||
set -eu
|
||||
|
||||
bin='sslh'
|
||||
|
||||
# run command if it is not starting with a "-" and is an executable in PATH
|
||||
if [ "${#}" -le 0 ] || \
|
||||
[ "${1#-}" != "${1}" ] || \
|
||||
[ -d "${1}" ] || \
|
||||
! command -v "${1}" > '/dev/null' 2>&1; then
|
||||
entrypoint='true'
|
||||
fi
|
||||
|
||||
unconfigure_iptables() {
|
||||
echo "Received SIG TERM/INT/KILL. Removing iptables / routing changes"
|
||||
|
||||
set +e # Don't exit if got error
|
||||
set -x
|
||||
|
||||
iptables -t raw -D PREROUTING ! -i lo -d 127.0.0.0/8 -j DROP
|
||||
iptables -t mangle -D POSTROUTING ! -o lo -s 127.0.0.0/8 -j DROP
|
||||
|
||||
iptables -t nat -D OUTPUT -m owner --uid-owner sslh -p tcp --tcp-flags FIN,SYN,RST,ACK SYN -j CONNMARK --set-xmark 0x01/0x0f
|
||||
iptables -t mangle -D OUTPUT ! -o lo -p tcp -m connmark --mark 0x01/0x0f -j CONNMARK --restore-mark --mask 0x0f
|
||||
|
||||
ip rule del fwmark 0x1 lookup 100
|
||||
ip route del local 0.0.0.0/0 dev lo table 100
|
||||
|
||||
|
||||
if [ $(cat /proc/sys/net/ipv6/conf/all/disable_ipv6) -eq 0 ]; then
|
||||
ip6tables -t raw -D PREROUTING ! -i lo -d ::1/128 -j DROP
|
||||
ip6tables -t mangle -D POSTROUTING ! -o lo -s ::1/128 -j DROP
|
||||
ip6tables -t nat -D OUTPUT -m owner --uid-owner sslh -p tcp --tcp-flags FIN,SYN,RST,ACK SYN -j CONNMARK --set-xmark 0x01/0x0f
|
||||
ip6tables -t mangle -D OUTPUT ! -o lo -p tcp -m connmark --mark 0x01/0x0f -j CONNMARK --restore-mark --mask 0x0f
|
||||
|
||||
ip -6 rule del fwmark 0x1 lookup 100
|
||||
ip -6 route del local ::/0 dev lo table 100
|
||||
fi
|
||||
|
||||
set -e
|
||||
set +x
|
||||
}
|
||||
|
||||
configure_iptables() {
|
||||
echo "Configuring iptables and routing..."
|
||||
|
||||
set +e # Don't exit if got error
|
||||
set -x
|
||||
|
||||
iptables -t raw -A PREROUTING ! -i lo -d 127.0.0.0/8 -j DROP
|
||||
iptables -t mangle -A POSTROUTING ! -o lo -s 127.0.0.0/8 -j DROP
|
||||
|
||||
iptables -t nat -A OUTPUT -m owner --uid-owner sslh -p tcp --tcp-flags FIN,SYN,RST,ACK SYN -j CONNMARK --set-xmark 0x01/0x0f
|
||||
iptables -t mangle -A OUTPUT ! -o lo -p tcp -m connmark --mark 0x01/0x0f -j CONNMARK --restore-mark --mask 0x0f
|
||||
|
||||
ip rule add fwmark 0x1 lookup 100
|
||||
ip route add local 0.0.0.0/0 dev lo table 100
|
||||
|
||||
if [ $(cat /proc/sys/net/ipv6/conf/all/disable_ipv6) -eq 0 ]; then
|
||||
ip6tables -t raw -A PREROUTING ! -i lo -d ::1/128 -j DROP
|
||||
ip6tables -t mangle -A POSTROUTING ! -o lo -s ::1/128 -j DROP
|
||||
ip6tables -t nat -A OUTPUT -m owner --uid-owner sslh -p tcp --tcp-flags FIN,SYN,RST,ACK SYN -j CONNMARK --set-xmark 0x01/0x0f
|
||||
ip6tables -t mangle -A OUTPUT ! -o lo -p tcp -m connmark --mark 0x01/0x0f -j CONNMARK --restore-mark --mask 0x0f
|
||||
|
||||
ip -6 rule add fwmark 0x1 lookup 100
|
||||
ip -6 route add local ::/0 dev lo table 100
|
||||
fi
|
||||
|
||||
set -e
|
||||
set +x
|
||||
}
|
||||
|
||||
for _args in "${@}" ; do
|
||||
if [ "${_args:-}" = '--transparent' ] ; then
|
||||
echo '--transparent flag is set'
|
||||
configure_iptables
|
||||
trap unconfigure_iptables TERM INT KILL
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
# Drop privileges and run as sslh user
|
||||
sslh_cmd="${entrypoint:+${bin}} ${@}"
|
||||
echo "Executing with user 'sslh': ${sslh_cmd}"
|
||||
|
||||
exec su - sslh -c "${sslh_cmd}" &
|
||||
wait "${!}"
|
||||
|
||||
exit 0
|
||||
+1
-1
@@ -7,7 +7,7 @@ doesn't work, report how what was suggested here went.
|
||||
It's also worth reading [how to ask
|
||||
questions](http://www.catb.org/~esr/faqs/smart-questions.html)
|
||||
before posting on the mailing list or opening an issue in
|
||||
Github.
|
||||
GitHub.
|
||||
|
||||
Getting more info
|
||||
=================
|
||||
|
||||
@@ -79,6 +79,20 @@ of the Makefile:
|
||||
* `USELIBBSD` compiles support for updating the process name (as shown
|
||||
by `ps`).
|
||||
|
||||
Generating the configuration parser
|
||||
-----------------------------------
|
||||
|
||||
The configuration file and command line parser is generated
|
||||
by `conf2struct`, from `sslhconf.cfg`, which generates
|
||||
`sslh-conf.c` and `sslh-conf.h`. The resulting files are
|
||||
included in the source so `sslh` can be built without
|
||||
`conf2struct` installed.
|
||||
|
||||
Further, to prevent build issues, `sslh-conf.[ch]` has no
|
||||
dependency to `sslhconf.cfg` in the Makefile. In the event
|
||||
of adding configuration settings, they need to be
|
||||
regenerated using `make c2s`.
|
||||
|
||||
Binaries
|
||||
--------
|
||||
|
||||
|
||||
+5
-3
@@ -92,7 +92,7 @@ to the executable:
|
||||
|
||||
sudo setcap cap_net_bind_service,cap_net_raw+pe sslh-select
|
||||
|
||||
Then you can run sslh-select as an unpriviledged user, e.g.:
|
||||
Then you can run sslh-select as an unprivileged user, e.g.:
|
||||
|
||||
sslh-select -p myname:443 --ssh localhost:22 --tls localhost:443
|
||||
|
||||
@@ -167,7 +167,7 @@ This parses the /etc/sslh.cfg (or /etc/sslh/sslh.cfg file if that exists
|
||||
instead) configuration file and dynamically generates a socket file to use.
|
||||
|
||||
This will also merge with any sslh.socket.d drop in configuration but will be
|
||||
overriden by a /etc/systemd/system/sslh.socket file.
|
||||
overridden by a /etc/systemd/system/sslh.socket file.
|
||||
|
||||
To use the generator place it in /usr/lib/systemd/system-generators and then
|
||||
call systemctl daemon-reload after any changes to /etc/sslh.cfg to generate
|
||||
@@ -196,6 +196,8 @@ will wait for incoming UDP packets, run the probes in the
|
||||
usual fashion, and forward packets to the appropriate
|
||||
target. `sslh` will then remember the association between
|
||||
remote host to target server for 60 seconds by default,
|
||||
which can be overriden with `udp_timeout`. This allows to
|
||||
which can be overridden with `udp_timeout`. This allows to
|
||||
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`.
|
||||
+1
-1
@@ -60,7 +60,7 @@ this scheme -- let me know if you manage that:
|
||||
ip rule add fwmark 0x1 lookup 100
|
||||
ip route add local 0.0.0.0/0 dev lo table 100
|
||||
|
||||
Tranparent proxying with IPv6 is similarly set up as follows:
|
||||
Transparent proxying with IPv6 is similarly set up as follows:
|
||||
|
||||
# Set route_localnet = 1 on all interfaces so that ssl can use "localhost" as destination
|
||||
# Not sure if this is needed for ipv6 though
|
||||
|
||||
+3
-3
@@ -365,7 +365,7 @@ static int clcpy(config_type type, void* target, const void* cl_arg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Copy the value of a string argument to arbitary memory
|
||||
/* Copy the value of a string argument to arbitrary memory
|
||||
* location that must be large enough, converting on the way
|
||||
* (i.e. CFG_INT gets atoi() and so on) */
|
||||
/* 0: success
|
||||
@@ -862,7 +862,7 @@ static int set_target_fields(void* target_addr, struct compound_cl_arg* arg, con
|
||||
if (pmatch[pmatch_cnt].rm_so == -1) {
|
||||
/* This should not happen as regexec() did
|
||||
* match before, unless there is a
|
||||
* discrepency between the regex and the
|
||||
* discrepancy between the regex and the
|
||||
* number of backreferences */
|
||||
return 0;
|
||||
}
|
||||
@@ -1155,7 +1155,7 @@ static void scalar_to_string(char** strp, config_setting_t* s)
|
||||
|
||||
/* Typesets all the settings in a configuration as a
|
||||
* newly-allocated string. The string management is caller's
|
||||
* responsability.
|
||||
* responsibility.
|
||||
* Returns the number of scalars in the configuration */
|
||||
static int cfg_as_string(config_setting_t* parent, const char* path, char** strp)
|
||||
{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* echosrv: a simple line echo server with optional prefix adding.
|
||||
*
|
||||
* echsrv --listen localhost6:1234 --prefix "ssl: "
|
||||
* echosrv --listen localhost6:1234 --prefix "ssl: "
|
||||
*
|
||||
* This will bind to 1234, and echo every line pre-pending "ssl: ". This is
|
||||
* used for testing: we create several such servers with different prefixes,
|
||||
|
||||
@@ -48,7 +48,10 @@ gap_array* gap_init(int len)
|
||||
gap->len = gap_len_alloc(elem_size);
|
||||
if (gap->len < len) gap->len = len;
|
||||
gap->array = malloc(gap->len * elem_size);
|
||||
if (!gap->array) return NULL;
|
||||
if (!gap->array) {
|
||||
free(gap);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (int i = 0; i < gap->len; i++)
|
||||
gap->array[i] = NULL;
|
||||
@@ -99,7 +102,7 @@ int gap_remove_ptr(gap_array* gap, void* ptr, int len)
|
||||
else
|
||||
return -1;
|
||||
|
||||
for (i = start; i < len; i++) {
|
||||
for (i = start; i < len - 1; i++) {
|
||||
gap->array[i] = gap->array[i+1];
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,11 @@ static inline int __attribute__((unused)) gap_set(gap_array* gap, int index, voi
|
||||
|
||||
static inline void* __attribute__((unused)) gap_get(gap_array* gap, int index)
|
||||
{
|
||||
/* sslh-ev routinely reads before it writes. It's not clear if it should be
|
||||
* its job to check the length (and add a gap_getlen()), or if it should be
|
||||
* gap_get()'s job. This will do for now */
|
||||
if (index >= gap->len) return NULL;
|
||||
|
||||
return gap->array[index];
|
||||
}
|
||||
|
||||
|
||||
@@ -10,14 +10,14 @@ fi
|
||||
if [ ! -d .git ] || ! `(git status | grep -q "On branch") 2> /dev/null`; then
|
||||
# If we don't have git, we can't work out what
|
||||
# version this is. It must have been downloaded as a
|
||||
# zip file.
|
||||
|
||||
# zip file.
|
||||
|
||||
# If downloaded from the release page, the directory
|
||||
# has the version number.
|
||||
release=`pwd | sed s/.*sslh-// | grep "[[:digit:]]"`
|
||||
|
||||
|
||||
if [ "x$release" = "x" ]; then
|
||||
# If downloaded from the head, Github creates the
|
||||
# If downloaded from the head, GitHub creates the
|
||||
# zip file with all files dated from the last
|
||||
# change: use the Makefile's modification time as a
|
||||
# release number
|
||||
@@ -28,7 +28,7 @@ fi
|
||||
if [ -d .git ] && head=`git rev-parse --verify HEAD 2>/dev/null`; then
|
||||
# generate the version info based on the tag
|
||||
release=`(git describe --tags || git --describe || git describe --all --long) \
|
||||
2>/dev/null | tr -d '\n'`
|
||||
2>/dev/null | tr -s '/' '-' | tr -d '\n'`
|
||||
|
||||
# Are there uncommitted changes?
|
||||
git update-index --refresh --unmerged > /dev/null
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
# Tests scripts are in *.tst files.
|
||||
# Corresponding output is put in *.out.
|
||||
# Reference output is put in *.ref.
|
||||
# Any discrepency will be reported!
|
||||
# Any discrepancy will be reported!
|
||||
|
||||
use strict;
|
||||
|
||||
|
||||
@@ -107,10 +107,11 @@ void print_message(msg_info info, const char* str, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, str);
|
||||
|
||||
if ((*info.verbose & MSG_STDOUT) && ! cfg.inetd)
|
||||
if ((*info.verbose & MSG_STDOUT) && ! cfg.inetd) {
|
||||
va_start(ap, str);
|
||||
vfprintf(stderr, str, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
if (*info.verbose & MSG_SYSLOG) {
|
||||
va_start(ap, str);
|
||||
@@ -194,7 +195,11 @@ void log_connection(struct connection_desc* desc, const struct connection *cnx)
|
||||
|
||||
if (!desc) {
|
||||
desc = &d;
|
||||
get_connection_desc(desc, cnx);
|
||||
if (!get_connection_desc(desc, cnx)) {
|
||||
print_message(msg_connections, "%s: lost incoming connection\n",
|
||||
cnx->proto->name);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
print_message(msg_connections, "%s:connection from %s to %s forwarded from %s to %s\n",
|
||||
|
||||
@@ -173,13 +173,13 @@ static int is_openvpn_protocol (const char*p,ssize_t len, struct sslhcfg_protoco
|
||||
* whereas the packet id is increased with every transmitted datagram.
|
||||
*/
|
||||
|
||||
if (len <= OVPN_HARD_RESET_PACKET_ID_OFFSET(OVPN_HMAC_128))
|
||||
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)
|
||||
return PROBE_MATCH;
|
||||
|
||||
if (len <= OVPN_HARD_RESET_PACKET_ID_OFFSET(OVPN_HMAC_160))
|
||||
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)
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
#CONFIG=/etc/sslh.cfg
|
||||
|
||||
#
|
||||
# Extra option to pass on comand line
|
||||
# Extra option to pass on command line
|
||||
# Those can supersede configuration file settings
|
||||
#
|
||||
#OPTIONS=
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
# but many connection attempts from the same
|
||||
# origin is reason enough to block.
|
||||
#
|
||||
# Verion: 2014-03-28
|
||||
# Version: 2014-03-28
|
||||
|
||||
[INCLUDES]
|
||||
|
||||
|
||||
+3
-3
@@ -365,7 +365,7 @@ static int clcpy(config_type type, void* target, const void* cl_arg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Copy the value of a string argument to arbitary memory
|
||||
/* Copy the value of a string argument to arbitrary memory
|
||||
* location that must be large enough, converting on the way
|
||||
* (i.e. CFG_INT gets atoi() and so on) */
|
||||
/* 0: success
|
||||
@@ -1818,7 +1818,7 @@ static int set_target_fields(void* target_addr, struct compound_cl_arg* arg, con
|
||||
if (pmatch[pmatch_cnt].rm_so == -1) {
|
||||
/* This should not happen as regexec() did
|
||||
* match before, unless there is a
|
||||
* discrepency between the regex and the
|
||||
* discrepancy between the regex and the
|
||||
* number of backreferences */
|
||||
return 0;
|
||||
}
|
||||
@@ -2111,7 +2111,7 @@ static void scalar_to_string(char** strp, config_setting_t* s)
|
||||
|
||||
/* Typesets all the settings in a configuration as a
|
||||
* newly-allocated string. The string management is caller's
|
||||
* responsability.
|
||||
* responsibility.
|
||||
* Returns the number of scalars in the configuration */
|
||||
static int cfg_as_string(config_setting_t* parent, const char* path, char** strp)
|
||||
{
|
||||
|
||||
@@ -39,8 +39,6 @@ struct watchers {
|
||||
|
||||
struct listen_endpoint* listen_sockets;
|
||||
gap_array* fd2ls; /* Array indexed by file descriptor, pointing to listen_sockets */
|
||||
|
||||
int max_fd; /* legacy to be removed, still required for UDP */
|
||||
};
|
||||
|
||||
static void cnx_read_cb(EV_P_ ev_io *w, int revents);
|
||||
@@ -80,8 +78,6 @@ void watchers_add_read(watchers* w, int fd)
|
||||
gap_set(w->ev_ior, fd, io);
|
||||
}
|
||||
ev_io_start(loop, io);
|
||||
|
||||
if (fd > w->max_fd) w->max_fd = fd + 1;
|
||||
}
|
||||
|
||||
void watchers_del_read(watchers* w, int fd)
|
||||
@@ -101,8 +97,6 @@ void watchers_add_write(watchers* w, int fd)
|
||||
gap_set(w->ev_iow, fd, io);
|
||||
}
|
||||
ev_io_start(loop, io);
|
||||
|
||||
if (fd > w->max_fd) w->max_fd = fd + 1;
|
||||
}
|
||||
|
||||
void watchers_del_write(watchers* w, int fd)
|
||||
|
||||
@@ -177,6 +177,7 @@ void tcp_listener(struct listen_endpoint* endpoint, int num_endpoints, int activ
|
||||
|
||||
while (1) {
|
||||
in_socket = accept(endpoint[active_endpoint].socketfd, 0, 0);
|
||||
CHECK_RES_RETURN(in_socket, "accept", /*void*/ );
|
||||
print_message(msg_fd, "accepted fd %d\n", in_socket);
|
||||
|
||||
switch(fork()) {
|
||||
|
||||
+2
-2
@@ -180,9 +180,9 @@ void main_loop(struct listen_endpoint listen_sockets[], int num_addr_listen)
|
||||
for (i = 0; i < fd_info.num_probing; i++) {
|
||||
struct connection* cnx = gap_get(fd_info.probing_list, i);
|
||||
if (!cnx || cnx->state != ST_PROBING) {
|
||||
print_message(msg_int_error, "Inconsistent probing: cnx=%0xp\n", cnx);
|
||||
print_message(msg_int_error, "Inconsistent probing: cnx=0x%p\n", cnx);
|
||||
if (cnx)
|
||||
print_message(msg_int_error, "Inconsistent probing: state=%d\n", cnx);
|
||||
print_message(msg_int_error, "Inconsistent probing: state=%d\n", cnx->state);
|
||||
exit(1);
|
||||
}
|
||||
if (cnx->probe_timeout < time(NULL)) {
|
||||
|
||||
@@ -55,9 +55,12 @@ static int get_listen_from_conf(const char *filename, char **listen[]) {
|
||||
config_setting_source_line(addr));
|
||||
return -1;
|
||||
} else {
|
||||
(*listen)[i] = malloc(strlen(resolve_listen(hostname, port)));
|
||||
char *resolved_listen = resolve_listen(hostname, port);
|
||||
|
||||
(*listen)[i] = malloc(strlen(resolved_listen));
|
||||
CHECK_ALLOC((*listen)[i], "malloc");
|
||||
strcpy((*listen)[i], resolve_listen(hostname, port));
|
||||
strcpy((*listen)[i], resolved_listen);
|
||||
free(resolved_listen);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -125,6 +128,7 @@ static int gen_sslh_config(char *runtime_unit_dir) {
|
||||
strcpy(runtime_conf, runtime_unit_dir);
|
||||
strcat(runtime_conf, unit_file);
|
||||
runtime_conf_fd = fopen(runtime_conf, "w");
|
||||
free(runtime_conf);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -396,7 +396,7 @@ if ($RB_RESOLVE_ADDRESS) {
|
||||
my $sslh_pid;
|
||||
if (!($sslh_pid = fork)) {
|
||||
my $user = (getpwuid $<)[0]; # Run under current username
|
||||
exec "./sslh-select -v 3 -f -u $user --listen blahblah.dontexist:9000 --ssh $ssh_address --tls $ssl_address -P $pidfile";
|
||||
exec "./sslh-select -v 3 -f -u $user --listen blahblah.nonexistent:9000 --ssh $ssh_address --tls $ssl_address -P $pidfile";
|
||||
}
|
||||
warn "spawned $sslh_pid\n";
|
||||
waitpid $sslh_pid, 0;
|
||||
|
||||
+4
-6
@@ -67,14 +67,16 @@ static void shovel(struct connection *cnx, int active_fd, struct loop_info* fd_i
|
||||
}
|
||||
|
||||
|
||||
/* Returns the queue index that contains the specified file descriptor */
|
||||
/* Returns the queue index that contains the specified file descriptor.
|
||||
* This is called by the *_process functions, which got cnx from the fd, so it
|
||||
* is impossible for fd to not be cnx, hence we die if it happens */
|
||||
static int active_queue(struct connection* cnx, int fd)
|
||||
{
|
||||
if (cnx->q[0].fd == fd) return 0;
|
||||
if (cnx->q[1].fd == fd) return 1;
|
||||
|
||||
print_message(msg_int_error, "file descriptor %d not found in connection object\n", fd);
|
||||
return -1;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Process a TCP read event on the specified file descriptor */
|
||||
@@ -156,13 +158,9 @@ static int connect_queue(struct connection* cnx,
|
||||
log_connection(NULL, cnx);
|
||||
flush_deferred(q);
|
||||
if (q->deferred_data) {
|
||||
/*
|
||||
FD_SET(q->fd, &fd_info->watchers->fds_w);
|
||||
FD_CLR(cnx->q[0].fd, &fd_info->watchers->fds_r); */
|
||||
watchers_add_write(fd_info->watchers, q->fd);
|
||||
watchers_del_read(fd_info->watchers, cnx->q[0].fd);
|
||||
}
|
||||
/* FD_SET(q->fd, &fd_info->watchers->fds_r); */
|
||||
watchers_add_read(fd_info->watchers, q->fd);
|
||||
collection_add_fd(fd_info->collection, cnx, q->fd);
|
||||
return q->fd;
|
||||
|
||||
@@ -65,6 +65,7 @@ static void tcp_protocol_list_init(void)
|
||||
if (!p->is_udp) {
|
||||
tcp_protocols_len++;
|
||||
tcp_protocols = realloc(tcp_protocols, tcp_protocols_len * sizeof(*tcp_protocols));
|
||||
CHECK_ALLOC(tcp_protocols, "realloc");
|
||||
tcp_protocols[tcp_protocols_len-1] = p;
|
||||
}
|
||||
}
|
||||
|
||||
+24
-10
@@ -86,13 +86,14 @@ static void udp_protocol_list_init(void)
|
||||
if (p->is_udp) {
|
||||
udp_protocols_len++;
|
||||
udp_protocols = realloc(udp_protocols, udp_protocols_len * sizeof(*udp_protocols));
|
||||
CHECK_ALLOC(udp_protocols, "realloc");
|
||||
udp_protocols[udp_protocols_len-1] = p;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Configuration sanity check for UDP:
|
||||
* - If there is a listening addres, there must be at least one target
|
||||
* - If there is a listening address, there must be at least one target
|
||||
*/
|
||||
static void udp_sanity_check(void)
|
||||
{
|
||||
@@ -129,7 +130,7 @@ void udp_init(struct loop_info* fd_info)
|
||||
* If yes, returns file descriptor of connection
|
||||
* If not, returns -1
|
||||
* */
|
||||
static int known_source(hash* h, struct sockaddr* addr, socklen_t addrlen)
|
||||
static int known_source(hash* h, struct sockaddr_storage* addr, socklen_t addrlen)
|
||||
{
|
||||
struct connection search;
|
||||
search.client_addr = *addr;
|
||||
@@ -221,6 +222,20 @@ static void mark_active(struct connection* cnx)
|
||||
}
|
||||
|
||||
|
||||
/* Creates a new non-blocking socket */
|
||||
static int nonblocking_socket(struct sslhcfg_protocols_item* proto)
|
||||
{
|
||||
int out = socket(proto->saddr->ai_family, SOCK_DGRAM, 0);
|
||||
int res = set_nonblock(out);
|
||||
if (res == -1) {
|
||||
print_message(msg_system_error, "%s:%d:%s:%d:%s\n", __FILE__, __LINE__, "udp:socket:nonblock", errno, strerror(errno));
|
||||
close(out);
|
||||
return -1;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
/* Process UDP coming from outside (client towards server)
|
||||
* If it's a new source, probe; otherwise, forward to previous target
|
||||
* Returns: newly allocate connections, for new connections
|
||||
@@ -229,7 +244,7 @@ static void mark_active(struct connection* cnx)
|
||||
struct connection* udp_c2s_forward(int sockfd, struct loop_info* fd_info)
|
||||
{
|
||||
char addr_str[NI_MAXHOST+1+NI_MAXSERV+1];
|
||||
struct sockaddr src_addr;
|
||||
struct sockaddr_storage src_addr;
|
||||
struct addrinfo addrinfo;
|
||||
struct sslhcfg_protocols_item* proto;
|
||||
cnx_collection* collection = fd_info->collection;
|
||||
@@ -237,7 +252,7 @@ struct connection* udp_c2s_forward(int sockfd, struct loop_info* fd_info)
|
||||
ssize_t len;
|
||||
socklen_t addrlen;
|
||||
int res, target, out = -1;
|
||||
char data[65536]; /* Theoritical max is 65507 (https://en.wikipedia.org/wiki/User_Datagram_Protocol).
|
||||
char data[65536]; /* Theoretical max is 65507 (https://en.wikipedia.org/wiki/User_Datagram_Protocol).
|
||||
This will do. Dynamic allocation is possible with the MSG_PEEK flag in recvfrom(2), but that'd imply
|
||||
malloc/free overhead for each packet, when really 64K is not that much */
|
||||
|
||||
@@ -245,13 +260,13 @@ struct connection* udp_c2s_forward(int sockfd, struct loop_info* fd_info)
|
||||
udp_timeouts(fd_info);
|
||||
|
||||
addrlen = sizeof(src_addr);
|
||||
len = recvfrom(sockfd, data, sizeof(data), 0, &src_addr, &addrlen);
|
||||
len = recvfrom(sockfd, data, sizeof(data), 0, (struct sockaddr*) &src_addr, &addrlen);
|
||||
if (len < 0) {
|
||||
perror("recvfrom");
|
||||
return NULL;
|
||||
}
|
||||
target = known_source(fd_info->hash_sources, &src_addr, addrlen);
|
||||
addrinfo.ai_addr = &src_addr;
|
||||
addrinfo.ai_addr = (struct sockaddr*) &src_addr;
|
||||
addrinfo.ai_addrlen = addrlen;
|
||||
print_message(msg_probe_info, "received %ld UDP from %d:%s\n",
|
||||
len, target, sprintaddr(addr_str, sizeof(addr_str), &addrinfo));
|
||||
@@ -266,9 +281,8 @@ struct connection* udp_c2s_forward(int sockfd, struct loop_info* fd_info)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
out = socket(proto->saddr->ai_family, SOCK_DGRAM, 0);
|
||||
res = set_nonblock(out);
|
||||
CHECK_RES_RETURN(res, "udp:socket:nonblock", NULL);
|
||||
out = nonblocking_socket(proto);
|
||||
if (out == -1) return NULL;
|
||||
struct connection* cnx = collection_alloc_cnx_from_fd(collection, out);
|
||||
if (!cnx) return NULL;
|
||||
target = out;
|
||||
@@ -281,7 +295,7 @@ struct connection* udp_c2s_forward(int sockfd, struct loop_info* fd_info)
|
||||
|
||||
res = new_source(fd_info->hash_sources, cnx);
|
||||
if (res == -1) {
|
||||
print_message(msg_connections_error, "Out of hash space for new incoming UDP connection -- increaѕe udp_max_connections");
|
||||
print_message(msg_connections_error, "Out of hash space for new incoming UDP connection -- increase udp_max_connections");
|
||||
collection_remove_cnx(collection, cnx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user