From 81eed9d56a06b22528e7fa9ef80a6d894ab93531 Mon Sep 17 00:00:00 2001 From: Preston Crow Date: Fri, 6 Oct 2023 17:50:25 -0400 Subject: [PATCH] Transparent mode in a multi-stage chain will fail after the first step because the (ip,port) is already bound. With this change, the bind is retried with a different port to at least keep the same IP address, which for most uses is all that is needed. I've tested this on my own system where sslh is used downstream from stunnel, with both in transparent mode. --- common.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/common.c b/common.c index 59437f1..2d31afe 100644 --- a/common.c +++ b/common.c @@ -278,7 +278,25 @@ int bind_peer(int fd, int fd_from) } #endif /* IP_TRANSPARENT / IP_BINDANY */ res = bind(fd, from.ai_addr, from.ai_addrlen); - CHECK_RES_RETURN(res, "bind", res); + if (res == -1 && errno != EADDRINUSE) { + CHECK_RES_RETURN(res, "bind", res); + } + else if (res == -1 ) { + /* + * If there is more than one transparent mode proxy going on, such as + * using sslh as the target of stunnel also in transparent mode, then + * the (ip,port) combination will already be bound for the previous application. + * In that case, the best we can do is bind with a different port. + * This does mean the local server can't use the ident protocol as the port will + * have changed, but most people won't care. + * Also note that stunnel uses the same logic for the same situation. + */ + struct sockaddr_in *sin; + sin = from.ai_addr; + sin->sin_port = 0; /* auto-pick an unused high port */ + res = bind(fd, from.ai_addr, from.ai_addrlen); + CHECK_RES_RETURN(res, "bind", res); + } return 0; }