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 | ||
|
|
40c616e94c | ||
|
|
555717e345 | ||
|
|
02573eb44b | ||
|
|
d166b8977c | ||
|
|
4c3b52dda5 | ||
|
|
c981ae9853 | ||
|
|
8a0de7b628 | ||
|
|
5154630fe0 | ||
|
|
63d5ecddca | ||
|
|
e412811ff1 | ||
|
|
7e3f723699 | ||
|
|
a1db2e8a92 | ||
|
|
72c743e1e1 | ||
|
|
61af0b2e09 | ||
|
|
5cba44f5fa | ||
|
|
9a36854ed3 | ||
|
|
b19f8a6046 | ||
|
|
bb685f8467 | ||
|
|
f418ae6128 | ||
|
|
a6df18527c | ||
|
|
e690cb5622 | ||
|
|
64c3e0ed1e | ||
|
|
b971f3edcd | ||
|
|
d2ec01c072 | ||
|
|
c7ddee0409 | ||
|
|
fb8fe57bd8 | ||
|
|
79c8af6ed1 | ||
|
|
c4e7261a51 | ||
|
|
7e2bb7f01f | ||
|
|
4cf3749e73 | ||
|
|
8d124e1085 | ||
|
|
a6c5e07d69 | ||
|
|
aa17061e26 | ||
|
|
9d10989d55 | ||
|
|
8b604a3db7 | ||
|
|
5168fe081a | ||
|
|
7b923f793e | ||
|
|
a4db163a69 | ||
|
|
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.
|
||||
@@ -25,6 +25,8 @@ v2.0:
|
||||
connection instead of at startup. Useful for dynamic
|
||||
DNS situations. (Paul Schroeder/milkpirate)
|
||||
|
||||
New probe for MSRDP (akappner).
|
||||
|
||||
v1.22: 17AUG2021
|
||||
sslh-select now supports UDP protocols.
|
||||
Probes specified in the `protocols`
|
||||
@@ -39,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.
|
||||
@@ -81,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
|
||||
@@ -107,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.
|
||||
|
||||
@@ -169,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
|
||||
@@ -198,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)
|
||||
|
||||
@@ -207,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
|
||||
@@ -292,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.
|
||||
@@ -383,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
|
||||
@@ -29,13 +29,16 @@ ifneq ($(strip $(COV_TEST)),)
|
||||
endif
|
||||
|
||||
CC ?= gcc
|
||||
CFLAGS ?=-Wall -O2 -DLIBPCRE -g $(CFLAGS_COV) $(CFLAGS_SAN)
|
||||
AR ?= ar
|
||||
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
|
||||
FORK_OBJS=$(OBJS) sslh-fork.o
|
||||
SELECT_OBJS=$(OBJS) processes.o udp-listener.o sslh-select.o hash.o tcp-listener.o
|
||||
EV_OBJS=$(OBJS) processes.o udp-listener.o sslh-ev.o hash.o tcp-listener.o
|
||||
OBJS_A=libsslh.a
|
||||
FORK_OBJS=sslh-fork.o $(OBJS_A)
|
||||
SELECT_OBJS=processes.o udp-listener.o sslh-select.o hash.o tcp-listener.o $(OBJS_A)
|
||||
EV_OBJS=processes.o udp-listener.o sslh-ev.o hash.o tcp-listener.o $(OBJS_A)
|
||||
|
||||
CONDITIONAL_TARGETS=
|
||||
|
||||
@@ -72,8 +75,11 @@ endif
|
||||
|
||||
all: sslh $(MAN) echosrv $(CONDITIONAL_TARGETS)
|
||||
|
||||
.c.o: *.h version.h
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) -c $<
|
||||
%.o: %.c %.h version.h
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@
|
||||
|
||||
$(OBJS_A): $(OBJS)
|
||||
$(AR) rcs $(OBJS_A) $(OBJS)
|
||||
|
||||
version.h:
|
||||
./genver.sh >version.h
|
||||
@@ -82,26 +88,28 @@ 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)
|
||||
#strip sslh-fork
|
||||
|
||||
sslh-select: version.h $(SELECT_OBJS) Makefile
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -o sslh-select $(SELECT_OBJS) $(LIBS)
|
||||
#strip sslh-select
|
||||
|
||||
sslh-ev: version.h $(EV_OBJS) Makefile
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -o sslh-ev $(EV_OBJS) $(LIBS) -lev
|
||||
#strip sslh-ev
|
||||
|
||||
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)
|
||||
@@ -145,7 +153,7 @@ distclean: clean
|
||||
rm -f tags sslh-conf.[ch] echosrv-conf.[ch] cscope.*
|
||||
|
||||
clean:
|
||||
rm -f sslh-fork sslh-select echosrv version.h $(MAN) systemd-sslh-generator *.o *.gcov *.gcno *.gcda *.png *.html *.css *.info
|
||||
rm -f sslh-fork sslh-select sslh-ev echosrv version.h $(MAN) systemd-sslh-generator *.o *.gcov *.gcno *.gcda *.png *.html *.css *.info
|
||||
|
||||
tags:
|
||||
ctags --globals -T *.[ch]
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -1,23 +1,25 @@
|
||||
# This is a basic configuration file that should provide
|
||||
# sensible values for "standard" setup.
|
||||
|
||||
verbose: 0;
|
||||
foreground: false;
|
||||
inetd: false;
|
||||
numeric: false;
|
||||
transparent: false;
|
||||
# You will find extensive examples with explanations in
|
||||
# example.cfg
|
||||
|
||||
timeout: 2;
|
||||
user: "nobody";
|
||||
pidfile: "/var/run/sslh.pid";
|
||||
chroot: "/var/empty";
|
||||
|
||||
|
||||
# Change hostname with your external address name.
|
||||
# Change hostname with your external address name, or the IP
|
||||
# of the interface that receives connections
|
||||
listen:
|
||||
(
|
||||
{ host: "thelonious"; port: "443"; }
|
||||
);
|
||||
|
||||
|
||||
# Change to the protocols you want to forward to. The
|
||||
# defaults here are sensible for services running on
|
||||
# localhost
|
||||
protocols:
|
||||
(
|
||||
{ name: "ssh"; service: "ssh"; host: "localhost"; port: "22"; fork: true; },
|
||||
|
||||
@@ -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
|
||||
=================
|
||||
|
||||
+38
-14
@@ -7,7 +7,7 @@ Dependencies
|
||||
`sslh` uses:
|
||||
|
||||
* [libconfig](http://www.hyperrealm.com/libconfig/). For
|
||||
Debian this is contained in package `libconfig8-dev`. You
|
||||
Debian this is contained in package `libconfig-dev`. You
|
||||
can compile with or without it using USELIBCONFIG in the
|
||||
Makefile.
|
||||
|
||||
@@ -29,6 +29,13 @@ Makefile.
|
||||
which requires `libbsd` at runtime, and `libbsd-dev` at
|
||||
compile-time.
|
||||
|
||||
* libpcre2, in package `libpcre-dev`. You can compile
|
||||
with or without it using ENABLE_REGEX in the Makefile.
|
||||
|
||||
* libev-dev, in package `libev-dev`. If you build a binary
|
||||
specifically and do not build `sslh-ev`, you don't need
|
||||
this.
|
||||
|
||||
|
||||
For OpenSUSE, these are contained in packages libconfig9 and
|
||||
libconfig-dev in repository
|
||||
@@ -44,6 +51,10 @@ distclean` for example), you will also need to add
|
||||
[conf2struct](https://www.rutschle.net/tech/conf2struct/README.html)
|
||||
(v1.5) to your path.
|
||||
|
||||
|
||||
The test scripts are written in Perl, and will require
|
||||
IO::Socket::INET6 (libio-socket-inet6-perl in Debian).
|
||||
|
||||
Compilation
|
||||
-----------
|
||||
|
||||
@@ -68,11 +79,25 @@ 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
|
||||
--------
|
||||
|
||||
The Makefile produces two different executables: `sslh-fork`
|
||||
and `sslh-select`:
|
||||
The Makefile produces three different executables: `sslh-fork`,
|
||||
`sslh-select` and `sslh-ev`:
|
||||
|
||||
* `sslh-fork` forks a new process for each incoming connection.
|
||||
It is well-tested and very reliable, but incurs the overhead
|
||||
@@ -81,18 +106,17 @@ If you are going to use `sslh` for a "small" setup (less than
|
||||
a dozen ssh connections and a low-traffic https server) then
|
||||
`sslh-fork` is probably more suited for you.
|
||||
|
||||
* `sslh-select` uses only one thread, which monitors all connections
|
||||
at once. It is more recent and less tested, but only incurs a 16
|
||||
byte overhead per connection. Also, if it stops, you'll lose all
|
||||
connections, which means you can't upgrade it remotely.
|
||||
If you are going to use `sslh` on a "medium" setup (a few thousand ssh
|
||||
connections, and another few thousand ssl connections),
|
||||
`sslh-select` will be better.
|
||||
|
||||
If you have a very large site (tens of thousands of connections),
|
||||
you'll need a vapourware version that would use libevent or
|
||||
something like that.
|
||||
* `sslh-select` uses only one thread, which monitors all
|
||||
connections at once. It only incurs a 16 byte overhead per
|
||||
connection. Also, if it stops, you'll lose all connections,
|
||||
which means you can't upgrade it remotely. If you are going
|
||||
to use `sslh` on a "medium" setup (a few hundreds of
|
||||
connections), or if you are on a system where forking is
|
||||
expensive (e.g. Windows), `sslh-select` will be better.
|
||||
|
||||
* `sslh-ev` is similar to `sslh-select`, but uses `libev` as
|
||||
a backend. This allows using specific kernel APIs that
|
||||
allow to manage thousands of connections concurrently.
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
+9
-7
@@ -65,13 +65,13 @@ Configuration goes like this on the server side, using `stunnel3`:
|
||||
* `-f` for foreground/debugging
|
||||
* `-p` for specifying the key and certificate
|
||||
* `-d` for specifying which interface and port
|
||||
we're listening to for incoming connexions
|
||||
we're listening to for incoming connections
|
||||
* `-l` summons `sslh` in inetd mode.
|
||||
|
||||
* sslh options:
|
||||
* `-i` for inetd mode
|
||||
* `--http` to forward HTTP connexions to port 80,
|
||||
and SSH connexions to port 22.
|
||||
* `--http` to forward HTTP connections to port 80,
|
||||
and SSH connections to port 22.
|
||||
|
||||
Capabilities support
|
||||
--------------------
|
||||
@@ -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
|
||||
@@ -189,13 +189,15 @@ UDP
|
||||
---
|
||||
|
||||
`sslh` can perform demultiplexing on UDP packets as well.
|
||||
This only works with `sslh-select` (it is not possible to
|
||||
This does not work with `sslh-fork` (it is not possible to
|
||||
support UDP with a forking model). Specify a listening
|
||||
address and target protocols with `is_udp: true`. `sslh`
|
||||
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,
|
||||
|
||||
+1
-1
@@ -123,7 +123,7 @@ protocols:
|
||||
udp_timeout: 20; # Time after which the "connection" is forgotten
|
||||
regex_patterns: [ "hello" ]; },
|
||||
# Forward Teamspeak3 (Voice only)
|
||||
{ name: "regex"; host: "localhost"; is_udp: true; port: "9987"; regex_patterns: [ "TS3INIT1" ]; },
|
||||
{ name: "teamspeak"; host: "localhost"; is_udp: true; port: "9987"; },
|
||||
# Forward IETF QUIC-50 ("Q050" -> "\x51\x30\x35\x30")
|
||||
# Remember that the regex needs to be adjusted for every supported QUIC version.
|
||||
{ name: "regex"; host: "localhost"; is_udp: true; port: "4433"; regex_patterns: [ "\x51\x30\x35\x30" ]; },
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
|
||||
static int is_ssh_protocol(const char *p, ssize_t len, struct sslhcfg_protocols_item*);
|
||||
static int is_openvpn_protocol(const char *p, ssize_t len, struct sslhcfg_protocols_item*);
|
||||
static int is_wireguard_protocol(const char *p, ssize_t len, struct sslhcfg_protocols_item*);
|
||||
static int is_tinc_protocol(const char *p, ssize_t len, struct sslhcfg_protocols_item*);
|
||||
static int is_xmpp_protocol(const char *p, ssize_t len, struct sslhcfg_protocols_item*);
|
||||
static int is_http_protocol(const char *p, ssize_t len, struct sslhcfg_protocols_item*);
|
||||
@@ -40,6 +41,8 @@ static int is_tls_protocol(const char *p, ssize_t len, struct sslhcfg_protocols_
|
||||
static int is_adb_protocol(const char *p, ssize_t len, struct sslhcfg_protocols_item*);
|
||||
static int is_socks5_protocol(const char *p, ssize_t len, struct sslhcfg_protocols_item*);
|
||||
static int is_syslog_protocol(const char *p, ssize_t len, struct sslhcfg_protocols_item*);
|
||||
static int is_teamspeak_protocol(const char *p, ssize_t len, struct sslhcfg_protocols_item*);
|
||||
static int is_msrdp_protocol(const char *p, ssize_t len, struct sslhcfg_protocols_item*);
|
||||
static int is_true(const char *p, ssize_t len, struct sslhcfg_protocols_item* proto) { return 1; }
|
||||
|
||||
/* Table of protocols that have a built-in probe
|
||||
@@ -48,6 +51,7 @@ static struct protocol_probe_desc builtins[] = {
|
||||
/* description probe */
|
||||
{ "ssh", is_ssh_protocol},
|
||||
{ "openvpn", is_openvpn_protocol },
|
||||
{ "wireguard", is_wireguard_protocol },
|
||||
{ "tinc", is_tinc_protocol },
|
||||
{ "xmpp", is_xmpp_protocol },
|
||||
{ "http", is_http_protocol },
|
||||
@@ -55,6 +59,8 @@ static struct protocol_probe_desc builtins[] = {
|
||||
{ "adb", is_adb_protocol },
|
||||
{ "socks5", is_socks5_protocol },
|
||||
{ "syslog", is_syslog_protocol },
|
||||
{ "teamspeak", is_teamspeak_protocol },
|
||||
{ "msrdp", is_msrdp_protocol },
|
||||
{ "anyprot", is_true }
|
||||
};
|
||||
|
||||
@@ -112,7 +118,7 @@ void hexdump(msg_info msg_info, const char *mem, unsigned int len)
|
||||
}
|
||||
str[c++] = '\n';
|
||||
str[c++] = 0;
|
||||
print_message(msg_info, str);
|
||||
print_message(msg_info, "%s", str);
|
||||
c = 0;
|
||||
}
|
||||
}
|
||||
@@ -137,15 +143,66 @@ static int is_ssh_protocol(const char *p, ssize_t len, struct sslhcfg_protocols_
|
||||
* http://www.fengnet.com/book/vpns%20illustrated%20tunnels%20%20vpnsand%20ipsec/ch08lev1sec5.html
|
||||
* and OpenVPN ssl.c, ssl.h and options.c
|
||||
*/
|
||||
#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_HMAC_128 16
|
||||
#define OVPN_HMAC_160 20
|
||||
#define OVPN_HARD_RESET_PACKET_ID_OFFSET(hmac_size) (9 + hmac_size)
|
||||
static int is_openvpn_protocol (const char*p,ssize_t len, struct sslhcfg_protocols_item* proto)
|
||||
{
|
||||
int packet_len;
|
||||
|
||||
if (len < 2)
|
||||
return PROBE_AGAIN;
|
||||
if (proto->is_udp == 0)
|
||||
{
|
||||
if (len < 2)
|
||||
return PROBE_AGAIN;
|
||||
|
||||
packet_len = ntohs(*(uint16_t*)p);
|
||||
return packet_len == len - 2;
|
||||
packet_len = ntohs(*(uint16_t*)p);
|
||||
return packet_len == len - 2;
|
||||
} else {
|
||||
if (len < 1)
|
||||
return PROBE_NEXT;
|
||||
|
||||
if ((p[0] & OVPN_OPCODE_MASK) != OVPN_CONTROL_HARD_RESET_CLIENT_V1 &&
|
||||
(p[0] & OVPN_OPCODE_MASK) != OVPN_CONTROL_HARD_RESET_CLIENT_V2)
|
||||
return PROBE_NEXT;
|
||||
|
||||
/* The detection pattern above may not be reliable enough.
|
||||
* Check the packet id: OpenVPN sents five initial packets
|
||||
* whereas the packet id is increased with every transmitted datagram.
|
||||
*/
|
||||
|
||||
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) + sizeof(uint32_t))
|
||||
return PROBE_NEXT;
|
||||
|
||||
if (ntohl(*(uint32_t*)(p + OVPN_HARD_RESET_PACKET_ID_OFFSET(OVPN_HMAC_160))) <= 5u)
|
||||
return PROBE_MATCH;
|
||||
|
||||
return PROBE_NEXT;
|
||||
}
|
||||
}
|
||||
|
||||
static int is_wireguard_protocol(const char *p, ssize_t len, struct sslhcfg_protocols_item* proto)
|
||||
{
|
||||
if (proto->is_udp == 0)
|
||||
return PROBE_NEXT;
|
||||
|
||||
// Handshake Init: 148 bytes
|
||||
if (len != 148)
|
||||
return PROBE_NEXT;
|
||||
|
||||
// Handshake Init: p[0] = 0x01, p[1..3] = 0x000000 (reserved)
|
||||
if (ntohl(*(uint32_t*)p) != 0x01000000)
|
||||
return PROBE_NEXT;
|
||||
|
||||
return PROBE_MATCH;
|
||||
}
|
||||
|
||||
/* Is the buffer the beginning of a tinc connections?
|
||||
@@ -318,6 +375,27 @@ static int is_syslog_protocol(const char *p, ssize_t len, struct sslhcfg_protoco
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int is_teamspeak_protocol(const char *p, ssize_t len, struct sslhcfg_protocols_item* proto)
|
||||
{
|
||||
if (len < 8)
|
||||
return PROBE_NEXT;
|
||||
|
||||
return !strncmp(p, "TS3INIT1", len);
|
||||
}
|
||||
|
||||
static int is_msrdp_protocol(const char *p, ssize_t len, struct sslhcfg_protocols_item* proto)
|
||||
{
|
||||
char version;
|
||||
char packet_len;
|
||||
if (len < 7)
|
||||
return PROBE_NEXT;
|
||||
version=*p;
|
||||
if (version!=0x03)
|
||||
return 0;
|
||||
packet_len = ntohs(*(uint16_t*)(p+2));
|
||||
return packet_len == len;
|
||||
}
|
||||
|
||||
static int regex_probe(const char *p, ssize_t len, struct sslhcfg_protocols_item* proto)
|
||||
{
|
||||
#ifdef ENABLE_REGEX
|
||||
@@ -395,6 +473,13 @@ int probe_buffer(char* buf, int len,
|
||||
return PROBE_AGAIN;
|
||||
|
||||
/* Everything failed: match the last one */
|
||||
|
||||
if (proto_len == 0) {
|
||||
/* This should be caught by configuration sanity checks, but just in
|
||||
* case, die gracefully rather than segfaulting */
|
||||
print_message(msg_int_error, "Received traffic on transport that has no target\n");
|
||||
exit(0);
|
||||
}
|
||||
*proto_out = proto_in[proto_len-1];
|
||||
return PROBE_MATCH;
|
||||
}
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
[Definition]
|
||||
|
||||
failregex = ^.+ sslh\[.+\]: connection from <HOST>:.+ to .+ forwarded
|
||||
failregex = ^.+ sslh\[.+\]: ssh:connection from <HOST>:.+ to .+ forwarded
|
||||
from .+ to .+:ssh\s*$
|
||||
|
||||
ignoreregex =
|
||||
|
||||
+49
-4
@@ -1,5 +1,5 @@
|
||||
/* Generated by conf2struct (https://www.rutschle.net/tech/conf2struct/README)
|
||||
* on Sat Apr 30 09:55:01 2022.
|
||||
* on Sun Sep 11 21:43:25 2022.
|
||||
|
||||
# conf2struct: generate libconf parsers that read to structs
|
||||
# Copyright (C) 2018-2021 Yves Rutschle
|
||||
@@ -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
|
||||
@@ -473,11 +473,13 @@ struct arg_file* sslhcfg_conffile;
|
||||
struct arg_str* sslhcfg_tls;
|
||||
struct arg_str* sslhcfg_openvpn;
|
||||
struct arg_str* sslhcfg_tinc;
|
||||
struct arg_str* sslhcfg_wireguard;
|
||||
struct arg_str* sslhcfg_xmpp;
|
||||
struct arg_str* sslhcfg_http;
|
||||
struct arg_str* sslhcfg_adb;
|
||||
struct arg_str* sslhcfg_socks5;
|
||||
struct arg_str* sslhcfg_syslog;
|
||||
struct arg_str* sslhcfg_msrdp;
|
||||
struct arg_str* sslhcfg_anyprot;
|
||||
struct arg_end* sslhcfg_end;
|
||||
|
||||
@@ -1256,6 +1258,14 @@ static struct compound_cl_target sslhcfg_anyprot_targets [] = {
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
static struct compound_cl_target sslhcfg_msrdp_targets [] = {
|
||||
{ & table_sslhcfg_protocols[0], 0, .value.def_string = "msrdp" },
|
||||
{ & table_sslhcfg_protocols[1], 1, .value.def_string = "0" },
|
||||
{ & table_sslhcfg_protocols[2], 2, .value.def_string = "0" },
|
||||
{ & table_sslhcfg_protocols[10], 0, .value.def_int = 1 },
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
static struct compound_cl_target sslhcfg_syslog_targets [] = {
|
||||
{ & table_sslhcfg_protocols[0], 0, .value.def_string = "syslog" },
|
||||
{ & table_sslhcfg_protocols[1], 1, .value.def_string = "0" },
|
||||
@@ -1296,6 +1306,15 @@ static struct compound_cl_target sslhcfg_xmpp_targets [] = {
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
static struct compound_cl_target sslhcfg_wireguard_targets [] = {
|
||||
{ & table_sslhcfg_protocols[0], 0, .value.def_string = "wireguard" },
|
||||
{ & table_sslhcfg_protocols[1], 1, .value.def_string = "0" },
|
||||
{ & table_sslhcfg_protocols[2], 2, .value.def_string = "0" },
|
||||
{ & table_sslhcfg_protocols[10], 0, .value.def_int = 1 },
|
||||
{ & table_sslhcfg_protocols[7], 0, .value.def_bool = 1 },
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
static struct compound_cl_target sslhcfg_tinc_targets [] = {
|
||||
{ & table_sslhcfg_protocols[0], 0, .value.def_string = "tinc" },
|
||||
{ & table_sslhcfg_protocols[1], 1, .value.def_string = "0" },
|
||||
@@ -1400,6 +1419,18 @@ static struct compound_cl_arg compound_cl_args[] = {
|
||||
.override_const = "tinc",
|
||||
},
|
||||
|
||||
{ /* arg: wireguard */
|
||||
.regex = "(.+):(\\w+)",
|
||||
.arg_cl = & sslhcfg_wireguard,
|
||||
.base_entry = & table_sslhcfg [26],
|
||||
.targets = sslhcfg_wireguard_targets,
|
||||
|
||||
|
||||
.override_desc = & table_sslhcfg_protocols [0],
|
||||
.override_matchindex = 0,
|
||||
.override_const = "wireguard",
|
||||
},
|
||||
|
||||
{ /* arg: xmpp */
|
||||
.regex = "(.+):(\\w+)",
|
||||
.arg_cl = & sslhcfg_xmpp,
|
||||
@@ -1460,6 +1491,18 @@ static struct compound_cl_arg compound_cl_args[] = {
|
||||
.override_const = "syslog",
|
||||
},
|
||||
|
||||
{ /* arg: msrdp */
|
||||
.regex = "(.+):(\\w+)",
|
||||
.arg_cl = & sslhcfg_msrdp,
|
||||
.base_entry = & table_sslhcfg [26],
|
||||
.targets = sslhcfg_msrdp_targets,
|
||||
|
||||
|
||||
.override_desc = & table_sslhcfg_protocols [0],
|
||||
.override_matchindex = 0,
|
||||
.override_const = "msrdp",
|
||||
},
|
||||
|
||||
{ /* arg: anyprot */
|
||||
.regex = "(.+):(\\w+)",
|
||||
.arg_cl = & sslhcfg_anyprot,
|
||||
@@ -1775,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;
|
||||
}
|
||||
@@ -2068,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)
|
||||
{
|
||||
@@ -2158,11 +2201,13 @@ int sslhcfg_cl_parse(int argc, char* argv[], struct sslhcfg_item* cfg)
|
||||
sslhcfg_tls = arg_strn(NULL, "tls", "<host:port>", 0, 10, "Set up TLS/SSL target"),
|
||||
sslhcfg_openvpn = arg_strn(NULL, "openvpn", "<host:port>", 0, 10, "Set up OpenVPN target"),
|
||||
sslhcfg_tinc = arg_strn(NULL, "tinc", "<host:port>", 0, 10, "Set up tinc target"),
|
||||
sslhcfg_wireguard = arg_strn(NULL, "wireguard", "<host:port>", 0, 10, "Set up WireGuard target"),
|
||||
sslhcfg_xmpp = arg_strn(NULL, "xmpp", "<host:port>", 0, 10, "Set up XMPP target"),
|
||||
sslhcfg_http = arg_strn(NULL, "http", "<host:port>", 0, 10, "Set up HTTP (plain) target"),
|
||||
sslhcfg_adb = arg_strn(NULL, "adb", "<host:port>", 0, 10, "Set up ADB (Android Debug) target"),
|
||||
sslhcfg_socks5 = arg_strn(NULL, "socks5", "<host:port>", 0, 10, "Set up socks5 target"),
|
||||
sslhcfg_syslog = arg_strn(NULL, "syslog", "<host:port>", 0, 10, "Set up syslog target"),
|
||||
sslhcfg_msrdp = arg_strn(NULL, "msrdp", "<host:port>", 0, 10, "Set up msrdp target"),
|
||||
sslhcfg_anyprot = arg_strn(NULL, "anyprot", "<host:port>", 0, 10, "Set up default target"),
|
||||
sslhcfg_end = arg_end(10)
|
||||
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/* Generated by conf2struct (https://www.rutschle.net/tech/conf2struct/README)
|
||||
* on Sat Apr 30 09:55:01 2022.
|
||||
* on Sun Sep 11 21:43:25 2022.
|
||||
|
||||
# conf2struct: generate libconf parsers that read to structs
|
||||
# Copyright (C) 2018-2021 Yves Rutschle
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
+34
-5
@@ -167,11 +167,9 @@ static void config_protocols()
|
||||
}
|
||||
|
||||
|
||||
void config_sanity_check(struct sslhcfg_item* cfg) {
|
||||
if (!cfg->protocols_len) {
|
||||
print_message(msg_config_error, "At least one target protocol must be specified.\n");
|
||||
exit(2);
|
||||
}
|
||||
void config_sanity_check(struct sslhcfg_item* cfg)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
/* If compiling with systemd socket support no need to require listen address */
|
||||
#ifndef SYSTEMD
|
||||
@@ -180,6 +178,37 @@ void config_sanity_check(struct sslhcfg_item* cfg) {
|
||||
exit(1);
|
||||
}
|
||||
#endif
|
||||
|
||||
for (i = 0; i < cfg->protocols_len; ++i) {
|
||||
if (strcmp(cfg->protocols[i].name, "tls")) {
|
||||
if (cfg->protocols[i].sni_hostnames_len) {
|
||||
print_message(msg_config_error, "name: \"%s\"; host: \"%s\"; port: \"%s\": "
|
||||
"Config option sni_hostnames is only applicable for tls\n",
|
||||
cfg->protocols[i].name, cfg->protocols[i].host, cfg->protocols[i].port);
|
||||
exit(1);
|
||||
}
|
||||
if (cfg->protocols[i].alpn_protocols_len) {
|
||||
print_message(msg_config_error, "name: \"%s\"; host: \"%s\"; port: \"%s\": "
|
||||
"Config option alpn_protocols is only applicable for tls\n",
|
||||
cfg->protocols[i].name, cfg->protocols[i].host, cfg->protocols[i].port);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (cfg->protocols[i].is_udp) {
|
||||
if (cfg->protocols[i].tfo_ok) {
|
||||
print_message(msg_config_error, "name: \"%s\"; host: \"%s\"; port: \"%s\": "
|
||||
"Config option tfo_ok is not applicable for udp connections\n",
|
||||
cfg->protocols[i].name, cfg->protocols[i].host, cfg->protocols[i].port);
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
if (!strcmp(cfg->protocols[i].name, "wireguard")) {
|
||||
print_message(msg_config_error, "Wireguard works only with UDP\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
+11
-5
@@ -52,6 +52,9 @@ static void watchers_init(watchers** w, struct listen_endpoint* listen_sockets,
|
||||
int num_addr_listen)
|
||||
{
|
||||
*w = malloc(sizeof(**w));
|
||||
CHECK_ALLOC(*w, "malloc");
|
||||
|
||||
memset(*w, 0, sizeof(**w));
|
||||
FD_ZERO(&(*w)->fds_r);
|
||||
FD_ZERO(&(*w)->fds_w);
|
||||
|
||||
@@ -143,9 +146,9 @@ void main_loop(struct listen_endpoint listen_sockets[], int num_addr_listen)
|
||||
memcpy(&readfds, &fd_info.watchers->fds_r, sizeof(readfds));
|
||||
memcpy(&writefds, &fd_info.watchers->fds_w, sizeof(writefds));
|
||||
|
||||
print_message(msg_fd, "selecting... max_fd=%d num_probing=%d\n",
|
||||
print_message(msg_fd, "selecting... max_fd=%d num_probing=%d\n",
|
||||
fd_info.watchers->max_fd, fd_info.num_probing);
|
||||
res = select(fd_info.watchers->max_fd, &readfds, &writefds,
|
||||
res = select(fd_info.watchers->max_fd + 1, &readfds, &writefds,
|
||||
NULL, fd_info.num_probing ? &tv : NULL);
|
||||
if (res < 0)
|
||||
perror("select");
|
||||
@@ -165,7 +168,10 @@ void main_loop(struct listen_endpoint listen_sockets[], int num_addr_listen)
|
||||
|
||||
/* Check all sockets for write activity */
|
||||
for (i = 0; i < fd_info.watchers->max_fd; i++) {
|
||||
if (FD_ISSET(i, &writefds)) {
|
||||
/* Check if it's active AND currently monitored (if a connection
|
||||
* died, it gets tidied, which closes both sockets, but writefs does
|
||||
* not know about that */
|
||||
if (FD_ISSET(i, &writefds) && FD_ISSET(i, &fd_info.watchers->fds_w)) {
|
||||
cnx_write_process(&fd_info, i);
|
||||
}
|
||||
}
|
||||
@@ -174,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)) {
|
||||
|
||||
@@ -210,6 +210,18 @@ cl_groups: (
|
||||
{ path: "tfo_ok"; value: 1 }
|
||||
);
|
||||
},
|
||||
{ name: "wireguard"; pattern: "(.+):(\w+)"; description: "Set up WireGuard target";
|
||||
list: "protocols";
|
||||
override: "name";
|
||||
argdesc: "<host:port>";
|
||||
targets: (
|
||||
{ path: "name"; value: "wireguard" },
|
||||
{ path: "host"; value: "$1" },
|
||||
{ path: "port"; value: "$2" },
|
||||
{ path: "log_level"; value: 1 },
|
||||
{ path: "tfo_ok"; value: 1 }
|
||||
);
|
||||
},
|
||||
{ name: "xmpp"; pattern: "(.+):(\w+)"; description: "Set up XMPP target";
|
||||
list: "protocols";
|
||||
override: "name";
|
||||
@@ -265,6 +277,17 @@ cl_groups: (
|
||||
{ path: "log_level"; value: 1 }
|
||||
);
|
||||
},
|
||||
{ name: "msrdp"; pattern: "(.+):(\w+)"; description: "Set up msrdp target";
|
||||
list: "protocols";
|
||||
override: "name";
|
||||
argdesc: "<host:port>";
|
||||
targets: (
|
||||
{ path: "name"; value: "msrdp" },
|
||||
{ path: "host"; value: "$1" },
|
||||
{ path: "port"; value: "$2" },
|
||||
{ path: "log_level"; value: 1 }
|
||||
);
|
||||
},
|
||||
{ name: "anyprot"; pattern: "(.+):(\w+)"; description: "Set up default target";
|
||||
list: "protocols";
|
||||
override: "name";
|
||||
|
||||
@@ -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;
|
||||
|
||||
+14
-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 */
|
||||
@@ -83,6 +85,11 @@ void tcp_read_process(struct loop_info* fd_info,
|
||||
{
|
||||
cnx_collection* collection = fd_info->collection;
|
||||
struct connection* cnx = collection_get_cnx_from_fd(collection, fd);
|
||||
|
||||
/* connection can get tidied if there is an error on the other file
|
||||
* descriptor -- then cnx is NULL */
|
||||
if (!cnx) return;
|
||||
|
||||
/* Determine active queue (0 or 1): if fd is that of q[1], active_q = 1,
|
||||
* otherwise it's 0 */
|
||||
int active_q = active_queue(cnx, fd);
|
||||
@@ -151,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;
|
||||
@@ -306,6 +309,11 @@ void probing_read_process(struct connection* cnx,
|
||||
void cnx_write_process(struct loop_info* fd_info, int fd)
|
||||
{
|
||||
struct connection* cnx = collection_get_cnx_from_fd(fd_info->collection, fd);
|
||||
|
||||
/* connection can get tidied if there is an error on the other file
|
||||
* descriptor -- then cnx is NULL */
|
||||
if (!cnx) return;
|
||||
|
||||
int res;
|
||||
int queue = active_queue(cnx, fd);
|
||||
|
||||
|
||||
+23
@@ -65,12 +65,35 @@ 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Configuration sanity check for TCP:
|
||||
* - If there is a listening socket, there must be at least one target
|
||||
*/
|
||||
static void tcp_sanity_check(void)
|
||||
{
|
||||
int tcp_present = 0;
|
||||
|
||||
for (int i = 0; i < cfg.listen_len; i++) {
|
||||
struct sslhcfg_listen_item* p = &cfg.listen[i];
|
||||
if (!p->is_udp) {
|
||||
tcp_present = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (tcp_present && !tcp_protocols_len) {
|
||||
print_message(msg_config_error, "At least one TCP target protocol must be specified.\n");
|
||||
exit(2);
|
||||
}
|
||||
}
|
||||
|
||||
void tcp_init(void)
|
||||
{
|
||||
tcp_protocol_list_init();
|
||||
tcp_sanity_check();
|
||||
}
|
||||
@@ -83,7 +83,7 @@ parse_tls_header(const struct TLSProtocol *tls_data, const char *data, size_t da
|
||||
|
||||
tls_content_type = data[0];
|
||||
if (tls_content_type != TLS_HANDSHAKE_CONTENT_TYPE) {
|
||||
print_message(msg_probe_error, "Request did not begin with TLS handshake.\n");
|
||||
print_message(msg_probe_info, "Request did not begin with TLS handshake.\n");
|
||||
return TLS_EPROTOCOL;
|
||||
}
|
||||
|
||||
|
||||
+45
-9
@@ -86,11 +86,33 @@ 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 address, there must be at least one target
|
||||
*/
|
||||
static void udp_sanity_check(void)
|
||||
{
|
||||
int udp_present = 0;
|
||||
|
||||
for (int i = 0; i < cfg.listen_len; i++) {
|
||||
struct sslhcfg_listen_item* p = &cfg.listen[i];
|
||||
if (p->is_udp) {
|
||||
udp_present = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (udp_present && !udp_protocols_len) {
|
||||
print_message(msg_config_error, "At least one UDP target protocol must be specified.\n");
|
||||
exit(2);
|
||||
}
|
||||
}
|
||||
|
||||
/* Init the UDP subsystem.
|
||||
* - Initialise the hash
|
||||
* - that's all, folks
|
||||
@@ -100,6 +122,7 @@ void udp_init(struct loop_info* fd_info)
|
||||
fd_info->hash_sources = hash_init(cfg.udp_max_connections, &hash_make_key, &cnx_cmp);
|
||||
|
||||
udp_protocol_list_init();
|
||||
udp_sanity_check();
|
||||
}
|
||||
|
||||
|
||||
@@ -107,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;
|
||||
@@ -199,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
|
||||
@@ -207,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;
|
||||
@@ -215,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 */
|
||||
|
||||
@@ -223,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));
|
||||
@@ -244,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;
|
||||
@@ -259,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