diff options
| author | Laurent Bercot <ska-skaware@skarnet.org> | 2025-06-03 22:10:36 +0000 |
|---|---|---|
| committer | Laurent Bercot <ska@appnovation.com> | 2025-06-03 22:10:36 +0000 |
| commit | 2b0d8dacd3b7def1d8839d725fd44ae75d71629d (patch) | |
| tree | 05604bd963a1af3d41e5fff133fba118422f7a48 | |
| parent | 27a5ccaa6f339f7fb00dd23b62ab3e692c90e038 (diff) | |
| download | s6-networking-2b0d8dacd3b7def1d8839d725fd44ae75d71629d.tar.gz | |
Add proxy-server
Signed-off-by: Laurent Bercot <ska@appnovation.com>
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | INSTALL | 8 | ||||
| -rw-r--r-- | NEWS | 7 | ||||
| -rw-r--r-- | doc/index.html | 12 | ||||
| -rw-r--r-- | doc/upgrade.html | 13 | ||||
| -rw-r--r-- | package/deps.mak | 2 | ||||
| -rw-r--r-- | package/info | 2 | ||||
| -rw-r--r-- | package/modes | 1 | ||||
| -rw-r--r-- | package/targets.mak | 1 | ||||
| -rw-r--r-- | src/conn-tools/deps-exe/proxy-server | 2 | ||||
| -rw-r--r-- | src/conn-tools/proxy-server.c | 358 |
11 files changed, 396 insertions, 11 deletions
@@ -5,6 +5,7 @@ *.lo *.a.xyzzy *.so.xyzzy +/proxy-server /s6-clockadd /s6-clockview /s6-getservbyname @@ -6,10 +6,10 @@ Build Instructions - A POSIX-compliant C development environment - GNU make version 3.81 or later - - skalibs version 2.14.4.0 or later: https://skarnet.org/software/skalibs/ - - Optional (but recommended): execline version 2.9.7.0 or later: https://skarnet.org/software/execline/ - - s6 version 2.13.2.0 or later: https://skarnet.org/software/s6/ - - s6-dns version 2.4.1.0 or later: https://skarnet.org/software/s6-dns/ + - skalibs version 2.14.5.0 or later: https://skarnet.org/software/skalibs/ + - Optional (but recommended): execline version 2.9.7.1 or later: https://skarnet.org/software/execline/ + - s6 version 2.13.2.1 or later: https://skarnet.org/software/s6/ + - s6-dns version 2.4.1.1 or later: https://skarnet.org/software/s6-dns/ - Depending on whether you build the SSL tools, bearssl version 0.6 or later: https://bearssl.org/ or libressl version 4.1.0 or later: https://libressl.org/ @@ -1,5 +1,12 @@ Changelog for s6-networking. +In 2.7.2.0 +---------- + + - Support for shared libraries on MacOS + - New binary: proxy-server, understanding the PROXY protocol + + In 2.7.1.0 ---------- diff --git a/doc/index.html b/doc/index.html index b349bb9..260213e 100644 --- a/doc/index.html +++ b/doc/index.html @@ -55,15 +55,15 @@ as extensions to the s6 ecosystem. <li> A POSIX-compliant system with a standard C development environment </li> <li> GNU make, version 3.81 or later </li> <li> <a href="//skarnet.org/software/skalibs/">skalibs</a> version -2.14.4.0 or later. It's a build-time requirement. It's also a run-time +2.14.5.0 or later. It's a build-time requirement. It's also a run-time requirement if you link against the shared version of the skalibs library. </li> <li> (Optional, but recommended) <a href="//skarnet.org/software/execline/">execline</a> version -2.9.7.0 or later. It's a build-time and run-time requirement. </li> +2.9.7.1 or later. It's a build-time and run-time requirement. </li> <li> <a href="//skarnet.org/software/s6/">s6</a> version -2.13.2.0 or later. It's a build-time and run-time requirement. </li> +2.13.2.1 or later. It's a build-time and run-time requirement. </li> <li> <a href="//skarnet.org/software/s6-dns/">s6-dns</a> version -2.4.1.0 or later. It's a build-time requirement. It's also a run-time +2.4.1.1 or later. It's a build-time requirement. It's also a run-time requirement if you link against the shared version of the s6-dns libraries. </li> <li> If you want to build the secure communication tools: @@ -91,8 +91,8 @@ run-time requirement if you link against its shared version. </li> <ul> <li> The current released version of s6-networking is -<a href="s6-networking-2.7.1.0.tar.gz">2.7.1.0</a>. -You can access its checksum <a href="s6-networking-2.7.1.0.tar.gz.sha256">here</a>. </li> +<a href="s6-networking-2.7.2.0.tar.gz">2.7.2.0</a>. +You can access its checksum <a href="s6-networking-2.7.2.0.tar.gz.sha256">here</a>. </li> <li> Alternatively, you can checkout a copy of the <a href="//git.skarnet.org/cgi-bin/cgit.cgi/s6-networking/">s6-networking git repository</a>: diff --git a/doc/upgrade.html b/doc/upgrade.html index 2f2171b..3f438d1 100644 --- a/doc/upgrade.html +++ b/doc/upgrade.html @@ -18,6 +18,19 @@ <h1> What has changed in s6-networking </h1> +<h2> in 2.7.2.0 </h2> + +<ul> + <li> <a href="//skarnet.org/software/skalibs/">skalibs</a> +dependency bumped to 2.14.5.0 </li> + <li> <a href="//skarnet.org/software/execline/">execline</a> +optional dependency bumped to 2.9.7.1 </li> + <li> <a href="//skarnet.org/software/s6/">s6</a> +dependency bumped to 2.13.2.1 </li> + <li> <a href="//skarnet.org/software/s6-dns/">s6-dns</a> +dependency bumped to 2.4.1.1 </li> +</ul> + <h2> in 2.7.1.0 </h2> <ul> diff --git a/package/deps.mak b/package/deps.mak index d75c6d8..76eb6a9 100644 --- a/package/deps.mak +++ b/package/deps.mak @@ -111,6 +111,8 @@ s6-taiclock: EXTRA_LIBS := ${SOCKET_LIB} ${SYSCLOCK_LIB} s6-taiclock: src/clock/s6-taiclock.o -lskarnet s6-taiclockd: EXTRA_LIBS := ${SOCKET_LIB} ${SYSCLOCK_LIB} s6-taiclockd: src/clock/s6-taiclockd.o -lskarnet +proxy-server: EXTRA_LIBS := ${SYSCLOCK_LIB} +proxy-server: src/conn-tools/proxy-server.o -lskarnet s6-getservbyname: EXTRA_LIBS := ${SOCKET_LIB} s6-getservbyname: src/conn-tools/s6-getservbyname.o -lskarnet s6-ident-client: EXTRA_LIBS := ${SOCKET_LIB} ${SYSCLOCK_LIB} diff --git a/package/info b/package/info index 73b0563..d830fec 100644 --- a/package/info +++ b/package/info @@ -1,4 +1,4 @@ package=s6-networking -version=2.7.1.0 +version=2.7.2.0 category=net package_macro_name=S6_NETWORKING diff --git a/package/modes b/package/modes index f403d4a..a2bfacd 100644 --- a/package/modes +++ b/package/modes @@ -1,3 +1,4 @@ +proxy-server 0755 s6-getservbyname 0755 s6-ident-client 0755 s6-tcpclient 0755 diff --git a/package/targets.mak b/package/targets.mak index 26b7173..95a5971 100644 --- a/package/targets.mak +++ b/package/targets.mak @@ -1,4 +1,5 @@ BIN_TARGETS := \ +proxy-server \ s6-getservbyname \ s6-ident-client \ s6-tcpclient \ diff --git a/src/conn-tools/deps-exe/proxy-server b/src/conn-tools/deps-exe/proxy-server new file mode 100644 index 0000000..a11a5f4 --- /dev/null +++ b/src/conn-tools/deps-exe/proxy-server @@ -0,0 +1,2 @@ +-lskarnet +${SYSCLOCK_LIB} diff --git a/src/conn-tools/proxy-server.c b/src/conn-tools/proxy-server.c new file mode 100644 index 0000000..5a578f8 --- /dev/null +++ b/src/conn-tools/proxy-server.c @@ -0,0 +1,358 @@ +/* ISC license. */ + +#include <stdint.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> + +#include <skalibs/gccattributes.h> +#include <skalibs/uint16.h> +#include <skalibs/uint64.h> +#include <skalibs/bytestr.h> +#include <skalibs/types.h> +#include <skalibs/fmtscan.h> +#include <skalibs/prog.h> +#include <skalibs/strerr.h> +#include <skalibs/gol.h> +#include <skalibs/tai.h> +#include <skalibs/exec.h> +#include <skalibs/unix-timed.h> + +#define NAME "proxy-server" +#define USAGE NAME "[ -1 | -2 ] [ -v verbosity ] [ -t timeout ] prog..." +#define dieusage() strerr_dieusage(100, USAGE) +#define dienomem() strerr_diefu1sys(111, "stralloc_catb") + +static unsigned int verbosity = 1 ; +static tain deadline ; + + + /* v2 */ + +struct v2hdr_s +{ + uint8_t version : 4 ; + uint8_t command : 4 ; + uint8_t family : 4 ; + uint8_t proto : 4 ; + uint16_t len ; +} ; + +static inline void v2hdr_unpack (char const *s, struct v2hdr_s *h) +{ + h->version = s[0] >> 4 ; + h->command = s[0] & 0xf ; + h->family = s[1] >> 4 ; + h->proto = s[1] & 0xf ; + uint16_unpack_big(s+2, &h->len) ; +} + +static inline void skip (uint16_t x) +{ + if (!x) return ; + char buf[x] ; + size_t r = timed_read_g(0, buf, x, &deadline) ; + if (r < x) + { + char fmtr[UINT16_FMT] ; + char fmtx[UINT16_FMT] ; + fmtr[uint16_fmt(fmtr, r)] = 0 ; + fmtx[uint16_fmt(fmtx, x)] = 0 ; + strerr_diefu4sys(111, "skip ", fmtx, " bytes: only got ", fmtr) ; + } +} + +static void process_v2_extensions (char const *s, uint16_t len, int sub) +{ + while (len) + { + uint16_t n ; + char c ; + if (len < 3) strerr_dief3x(1, "invalid TLV encoding in ", sub ? "sub-" : "", "extension") ; + c = *s++ ; + uint16_unpack_big(s, &n) ; s += 2 ; len -= 3 ; + if (n > len) strerr_dief3x(1, "invalid TLV encoding in ", sub ? "sub-" : "", "extension") ; + switch (c) + { + case 0x02 : /* PP2_TYPE_AUTHORITY */ + { + char tmp[n+1] ; + if (sub) strerr_dief1x(1, "invalid sub-extension type") ; + memcpy(tmp, s, n) ; tmp[n] = 0 ; + if (!env_mexec("SSL_TLS_SNI_SERVERNAME", tmp)) dienomem() ; + break ; + } + case 0x20 : /* PP2_TYPE_SSL */ + if (n < 5) strerr_dief1x(1, "invalid sub-TLV encoding in SSL extension") ; + if (*s & 0x01) process_v2_extensions(s + 5, n - 5, 1) ; + break ; + + case 0x21 : /* PP2_SUBTYPE_SSL_VERSION */ + { + char tmp[n+1] ; + if (!sub) strerr_dief1x(1, "invalid main extension type") ; + memcpy(tmp, s, n) ; tmp[n] = 0 ; + if (!env_mexec("SSL_PROTOCOL", tmp)) dienomem() ; + break ; + } + case 0x22 : /* PP2_SUBTYPE_SSL_CN */ + { + char tmp[n+1] ; + if (!sub) strerr_dief1x(1, "invalid main extension type") ; + memcpy(tmp, s, n) ; tmp[n] = 0 ; + if (!env_mexec("SSL_PEER_CERT_CN", tmp)) dienomem() ; + break ; + } + case 0x23 : /* PP2_SUBTYPE_SSL_CIPHER */ + { + char tmp[n+1] ; + if (!sub) strerr_dief1x(1, "invalid main extension type") ; + memcpy(tmp, s, n) ; tmp[n] = 0 ; + if (!env_mexec("SSL_CIPHER", tmp)) dienomem() ; + break ; + } + default : break ; + } + s += n ; len -= n ; + } +} + +static void do_v2 (struct v2hdr_s const *h) +{ + static uint16_t const famaddrlen[3] = { 12, 36, 216 } ; + if (h->version != 2) strerr_dief1x(1, "invalid version") ; + if (h->command == 0 && h->family && verbosity) + { + char fmt[UINT16_FMT] ; + fmt[uint16_fmt(fmt, h->family)] = 0 ; + strerr_warnw2x("received LOCAL command with family set to ", fmt) ; + } + else if (h->command > 1) + strerr_dief1x(1, "invalid command") ; + if (h->family > 3) + strerr_dief1x(1, "invalid family") ; + if (!h->family || !h->proto) + skip(h->len) ; + else if (h->proto > 2) + strerr_dief1x(1, "invalid transport protocol") ; + else if (h->proto == 2) + strerr_dief1x(2, "unsupported transport protocol: datagram") ; + else if (h->len < famaddrlen[h->family - 1]) + strerr_dief1x(1, "invalid address length") ; + else + { + char buf[h->len] ; + size_t r = timed_read_g(0, buf, h->len, &deadline) ; + if (r < h->len) strerr_diefu1sys(111, "read address block") ; + switch (h->family) + { + case 1 : + case 2 : + { + uint16_t remoteport, localport ; + char remoteip[IP6_FMT] ; + char localip[IP6_FMT] ; + char remoteportfmt[UINT16_FMT] ; + char localportfmt[UINT16_FMT] ; + remoteip[h->family == 2 ? ip6_fmt(remoteip, buf) : ip4_fmt(remoteip, buf)] = 0 ; + localip[h->family == 2 ? ip6_fmt(localip, buf+16) : ip4_fmt(localip, buf+4)] = 0 ; + uint16_unpack_big(buf + 24 * h->family - 16, &remoteport) ; + uint16_unpack_big(buf + 24 * h->family - 14, &localport) ; + remoteportfmt[uint16_fmt(remoteportfmt, remoteport)] = 0 ; + localportfmt[uint16_fmt(localportfmt, localport)] = 0 ; + + if (!env_mexec("PROTO", "TCP") + || !env_mexec("TCPREMOTEIP", remoteip) + || !env_mexec("TCPLOCALIP", localip) + || !env_mexec("TCPREMOTEPORT", remoteportfmt) + || !env_mexec("TCPLOCALPORT", localportfmt)) dienomem() ; + break ; + } + default : + { + char localpath[109] ; + strncpy(localpath, buf + 108, 108) ; + localpath[108] = 0 ; + buf[108] = 0 ; + if (!env_mexec("PROTO", "IPC") + || !env_mexec("IPCREMOTEPATH", buf) + || !env_mexec("IPCLOCALPATH", localpath)) dienomem() ; + break ; + } + } + if (h->len > famaddrlen[h->family-1]) + process_v2_extensions(buf + famaddrlen[h->family-1], h->len - famaddrlen[h->family-1], 0) ; + } +} + +static void maybe_v2 (char const *buf) +{ + struct v2hdr_s h ; + if (memcmp(buf, "\r\n\r\n\0\r\nQUIT\n", 12)) strerr_dief1x(1, "invalid magic") ; + v2hdr_unpack(buf + 12, &h) ; + do_v2(&h) ; +} + +static inline void v2 (void) +{ + char buf[16] ; + size_t r = timed_read_g(0, buf, 16, &deadline) ; + if (r < 16) + { + if (!errno) errno = EPIPE ; + strerr_diefu1sys(111, "read from stdin") ; + } + maybe_v2(buf) ; +} + + + /* v1 */ + +static void do_v1 (char const *prebuf) +{ + uint16_t len = byte_chr(prebuf, 9, '\n') ; + if (len < 8) strerr_dief1x(1, "invalid PROXY line") ; + if (len == 8 && !memcmp(prebuf, "UNKNOWN\r", 8)) return ; + + int is6 ; + char buf[102] ; + memcpy(buf, prebuf, 9) ; len = 9 ; + for (; len < 101 ; len++) + { + if (!timed_read_g(0, buf + len, 1, &deadline)) + strerr_diefu1sys(111, "read from stdin") ; + if (buf[len] == '\n') break ; + } + if (len >= 101) strerr_dief1x(1, "PROXY line too long") ; + if (buf[len-1] != '\r') strerr_dief1x(1, "invalid PROXY line") ; + if (!memcmp(buf, "UNKNOWN ", 8)) return ; + if (memcmp(buf, "TCP", 3)) strerr_dief1x(1, "invalid protocol in PROXY line") ; + if (buf[3] == '6') is6 = 1 ; + else if (buf[3] == '4') is6 = 0 ; + else strerr_dief1x(1, "invalid protocol in PROXY line") ; + + char remoteip[16] ; + char localip[16] ; + uint16_t remoteport, localport ; + uint16_t pos = 5, m ; + m = is6 ? ip6_scan(buf + pos, remoteip) : ip4_scan(buf + pos, remoteip) ; + if (!m) strerr_dief1x(1, "invalid remote ip in PROXY line") ; + pos += m ; + if (buf[pos++] != ' ') strerr_dief1x(1, "invalid PROXY line") ; + m = is6 ? ip6_scan(buf + pos, localip) : ip4_scan(buf + pos, localip) ; + if (!m) strerr_dief1x(1, "invalid local ip in PROXY line") ; + pos += m ; + if (buf[pos++] != ' ') strerr_dief1x(1, "invalid PROXY line") ; + m = uint16_scan(buf + pos, &remoteport) ; + if (!m) strerr_dief1x(1, "invalid remote port in PROXY line") ; + pos += m ; + if (buf[pos++] != ' ') strerr_dief1x(1, "invalid PROXY line") ; + m = uint16_scan(buf + pos, &localport) ; + if (!m) strerr_dief1x(1, "invalid local port in PROXY line") ; + pos += m ; + if (pos != len - 1) strerr_dief1x(1, "invalid PROXY line") ; + + if (!env_mexec("PROTO", "TCP")) dienomem() ; + pos = 0 ; + m = is6 ? ip6_fmt(buf + pos, remoteip) : ip4_fmt(buf + pos, remoteip) ; + buf[pos + m++] = 0 ; + if (!env_mexec("TCPREMOTEIP", buf + pos)) dienomem() ; + pos += m ; + m = is6 ? ip6_fmt(buf + pos, localip) : ip4_fmt(buf + pos, localip) ; + buf[pos + m++] = 0 ; + if (!env_mexec("TCPLOCALIP", buf + pos)) dienomem() ; + pos += m ; + m = uint16_fmt(buf + pos, remoteport) ; + buf[pos + m++] = 0 ; + if (!env_mexec("TCPREMOTEPORT", buf + pos)) dienomem() ; + pos += m ; + m = uint16_fmt(buf + pos, localport) ; + buf[pos + m++] = 0 ; + if (!env_mexec("TCPLOCALPORT", buf + pos)) dienomem() ; + pos += m ; +} + +static inline void v1 (void) +{ + char buf[15] ; + size_t r = timed_read_g(0, buf, 15, &deadline) ; + if (r < 15) + { + if (!errno) errno = EPIPE ; + strerr_diefu1sys(111, "read from stdin") ; + } + if (memcmp(buf, "PROXY ", 6)) strerr_dief1x(1, "invalid magic") ; + do_v1(buf + 6) ; +} + +static void both (void) +{ + char buf[16] ; + size_t r = timed_read_g(0, buf, 15, &deadline) ; + if (r < 15) + { + if (!errno) errno = EPIPE ; + strerr_diefu1sys(111, "read from stdin") ; + } + if (!memcmp(buf, "PROXY ", 6)) do_v1(buf + 6) ; + else if (!timed_read_g(0, buf + 15, 1, &deadline)) strerr_diefu1sys(111, "read from stdin") ; + maybe_v2(buf) ; +} + +enum main_golb_e +{ + MAIN_GOLB_V1, + MAIN_GOLB_V2, + MAIN_GOLB_N +} ; + +enum main_gola_e +{ + MAIN_GOLA_TIMEOUT, + MAIN_GOLA_VERBOSITY, + MAIN_GOLA_N +} ; + +int main (int argc, char const *const *argv) +{ + static gol_bool const main_golb[4] = + { + { .so = '1', .lo = "enable-v1", .set = 1, .mask = 1 << MAIN_GOLB_V1 }, + { .so = '2', .lo = "enable-v2", .set = 1, .mask = 1 << MAIN_GOLB_V2 }, + { .so = 0, .lo = "disable-v1", .set = 0, .mask = 1 << MAIN_GOLB_V1 }, + { .so = 0, .lo = "disable-v2", .set = 0, .mask = 1 << MAIN_GOLB_V2 } + } ; + static gol_arg const main_gola[MAIN_GOLA_N] = + { + { .so = 't', .lo = "timeout", .i = MAIN_GOLA_TIMEOUT }, + { .so = 'v', .lo = "verbosity", .i = MAIN_GOLA_VERBOSITY } + } ; + + uint64_t golb = 0 ; + char prog_storage[PROG_pid_len(NAME)] ; + PROG_pid_fill(prog_storage, NAME) ; + PROG = prog_storage ; + + { + char const *gola[MAIN_GOLA_N] = { 0 } ; + tain tto = TAIN_INFINITE_RELATIVE ; + unsigned int t = 0 ; + unsigned int golc = gol_main(argc, argv, main_golb, 4, main_gola, MAIN_GOLA_N, &golb, gola) ; + argc -= golc ; argv += golc ; + if (!argc) dieusage() ; + if (gola[MAIN_GOLA_TIMEOUT] && !uint0_scan(gola[MAIN_GOLA_TIMEOUT], &t)) + strerr_dief2x(100, "timeout", " must be an unsigned integer") ; + if (gola[MAIN_GOLA_VERBOSITY] && !uint0_scan(gola[MAIN_GOLA_VERBOSITY], &verbosity)) + strerr_dief2x(100, "verbosity", " must be an unsigned integer") ; + + if (t) tain_from_millisecs(&tto, t) ; + tain_now_set_stopwatch_g() ; + tain_add_g(&deadline, &tto) ; + } + + if ((golb & (1 << MAIN_GOLB_V1 | 1 << MAIN_GOLB_V2)) == 0) golb |= 1 << MAIN_GOLB_V2 ; + if ((golb & (1 << MAIN_GOLB_V1 | 1 << MAIN_GOLB_V2)) == 1 << MAIN_GOLB_V2) v2() ; + else if ((golb & (1 << MAIN_GOLB_V1 | 1 << MAIN_GOLB_V2)) == 1 << MAIN_GOLB_V1) v1() ; + else both() ; + xmexec(argv) ; +} |
