Hi.
On 02.10.20 13:54, Christopher Faulet wrote:
Le 02/10/2020 à 08:58, Willy Tarreau a écrit :
So if anyone currently uses socks4 to talk to servers, I suggest you
run a quick test on 2.2 or 2.3 to see if health checks continue to work
over socks4 or not, in which case it's likely you'll be able to provide
an easier reproducer that will allow to fix the problem. This will save
everyone time and protect our eyeballs by keeping them away from this
blinking patch.
There is indeed a bug. The flag CO_FL_SOCKS4 is set after the connect()
for tcp-checks, making the health-checks though a socks4 proxy fail.
Here is a patch to fix this bug. I will push it very soon.
Remains the support of the SOCKS4A in Alex patches. But I will let anyone
motivated by this part working on it :)
I was curious how much the patch really change and have took the time to
adopt the patch.
What I have seen is that he current code mixes tabs and white spaces also
the goto intentions. I think this is because of the evolution of the code ;-)
Attached the patch which I have cleaned from the formatings.
The code builds but I have not tested it if it works.
The main patch is 001 the other two are just to remove double definition
of functions.
Regards
Aleks
>From 249f3e2467f3957e4af786829d5bb585de7f2df9 Mon Sep 17 00:00:00 2001
From: Alex <[email protected]>
Date: Sun, 4 Oct 2020 03:09:13 +0200
Subject: [PATCH 3/3] Sock4(A) original patch from alex v3
---
include/haproxy/connection.h | 34 ----------------------------------
1 file changed, 34 deletions(-)
diff --git a/include/haproxy/connection.h b/include/haproxy/connection.h
index e06de3c67..2189ab8c1 100644
--- a/include/haproxy/connection.h
+++ b/include/haproxy/connection.h
@@ -150,40 +150,6 @@ static inline void conn_prepare_new_for_socks4(struct connection *conn, struct s
}
}
-static inline void conn_set_domain(struct connection *conn, const char *domain)
-{
- conn_free_domain(conn);
- if (domain) {
- size_t len = strlen(domain) + 1;
- conn->requested_domain = malloc(len);
- if (!conn->requested_domain) {
- /* TODO: Handle malloc error */
- }
-
- memcpy(conn->requested_domain, domain, len);
- }
-}
-
-static int is_server_fake_address(struct server *srv)
-{
- return (srv->flags & SRV_F_SOCKS4_PROXY_FAILED_RESOLVE);
-}
-
-static inline void conn_set_domain_from_server(struct connection *conn, struct server *srv)
-{
- if (is_server_fake_address(srv))
- conn_set_domain(conn, srv->hostname);
-}
-
-static inline void conn_prepare_new_for_socks4(struct connection *conn, struct server *srv)
-{
- if (srv && (srv->flags & SRV_F_SOCKS4_PROXY)) {
- conn->send_proxy_ofs = 1;
- conn->flags |= CO_FL_SOCKS4;
- conn_set_domain_from_server(conn, srv);
- }
-}
-
/* Calls the close() function of the transport layer if any and if not done
* yet, and clears the CO_FL_XPRT_READY flag. However this is not done if the
* CO_FL_XPRT_TRACKED flag is set, which allows logs to take data from the
--
2.25.1
>From 9f9f4c33d15b3c15b37d54152b141bfc0e345ed2 Mon Sep 17 00:00:00 2001
From: Alex <[email protected]>
Date: Sun, 4 Oct 2020 03:00:29 +0200
Subject: [PATCH 2/3] Sock4(A) original patch from alex v2
---
include/haproxy/connection.h | 9 ---------
1 file changed, 9 deletions(-)
diff --git a/include/haproxy/connection.h b/include/haproxy/connection.h
index c1eec84de..e06de3c67 100644
--- a/include/haproxy/connection.h
+++ b/include/haproxy/connection.h
@@ -150,15 +150,6 @@ static inline void conn_prepare_new_for_socks4(struct connection *conn, struct s
}
}
-static inline void conn_free_domain(struct connection *conn)
-{
- if (conn->requested_domain)
- {
- free(conn->requested_domain);
- conn->requested_domain = NULL;
- }
-}
-
static inline void conn_set_domain(struct connection *conn, const char *domain)
{
conn_free_domain(conn);
--
2.25.1
>From 294196a787f7345d42553d531d39906ef7a7468b Mon Sep 17 00:00:00 2001
From: Alex <[email protected]>
Date: Sun, 4 Oct 2020 02:34:15 +0200
Subject: [PATCH 1/3] Sock4(A) original patch from alex
---
include/haproxy/connection-t.h | 3 +-
include/haproxy/connection.h | 90 ++++++++++++++++++++++++++++++++++
include/haproxy/fake_host.h | 24 +++++++++
include/haproxy/server-t.h | 1 +
src/backend.c | 17 +++----
src/connection.c | 80 +++++++++++++++++++++++++-----
src/server.c | 30 ++++++++----
src/tcpcheck.c | 3 ++
8 files changed, 215 insertions(+), 33 deletions(-)
create mode 100644 include/haproxy/fake_host.h
diff --git a/include/haproxy/connection-t.h b/include/haproxy/connection-t.h
index 9caa2ca49..669f97c12 100644
--- a/include/haproxy/connection-t.h
+++ b/include/haproxy/connection-t.h
@@ -490,7 +490,8 @@ struct connection {
struct sockaddr_storage *dst; /* destination address (pool), when known, otherwise NULL */
char *proxy_authority; /* Value of authority TLV received via PROXYv2 */
uint8_t proxy_authority_len; /* Length of authority TLV received via PROXYv2 */
- struct ist proxy_unique_id; /* Value of the unique ID TLV received via PROXYv2 */
+ struct ist proxy_unique_id; /* Value of the unique ID TLV received via PROXYv2 */
+ char *requested_domain; /* Socks 4 Domainname */
};
struct mux_proto_list {
diff --git a/include/haproxy/connection.h b/include/haproxy/connection.h
index 876f55761..c1eec84de 100644
--- a/include/haproxy/connection.h
+++ b/include/haproxy/connection.h
@@ -108,6 +108,91 @@ static inline int conn_xprt_init(struct connection *conn)
return ret;
}
+static inline void conn_free_domain(struct connection *conn)
+{
+ if (conn->requested_domain) {
+ free(conn->requested_domain);
+ conn->requested_domain = NULL;
+ }
+}
+
+static inline void conn_set_domain(struct connection *conn, const char *domain)
+{
+ conn_free_domain(conn);
+ if (domain) {
+ size_t len = strlen(domain) + 1;
+ conn->requested_domain = malloc(len);
+ if (!conn->requested_domain) {
+ /* TODO: Handle malloc error */
+ }
+
+ memcpy(conn->requested_domain, domain, len);
+ }
+}
+
+static int is_server_fake_address(struct server *srv)
+{
+ return (srv->flags & SRV_F_SOCKS4_PROXY_FAILED_RESOLVE);
+}
+
+static inline void conn_set_domain_from_server(struct connection *conn, struct server *srv)
+{
+ if (is_server_fake_address(srv))
+ conn_set_domain(conn, srv->hostname);
+}
+
+static inline void conn_prepare_new_for_socks4(struct connection *conn, struct server *srv)
+{
+ if (srv && (srv->flags & SRV_F_SOCKS4_PROXY)) {
+ conn->send_proxy_ofs = 1;
+ conn->flags |= CO_FL_SOCKS4;
+ conn_set_domain_from_server(conn, srv);
+ }
+}
+
+static inline void conn_free_domain(struct connection *conn)
+{
+ if (conn->requested_domain)
+ {
+ free(conn->requested_domain);
+ conn->requested_domain = NULL;
+ }
+}
+
+static inline void conn_set_domain(struct connection *conn, const char *domain)
+{
+ conn_free_domain(conn);
+ if (domain) {
+ size_t len = strlen(domain) + 1;
+ conn->requested_domain = malloc(len);
+ if (!conn->requested_domain) {
+ /* TODO: Handle malloc error */
+ }
+
+ memcpy(conn->requested_domain, domain, len);
+ }
+}
+
+static int is_server_fake_address(struct server *srv)
+{
+ return (srv->flags & SRV_F_SOCKS4_PROXY_FAILED_RESOLVE);
+}
+
+static inline void conn_set_domain_from_server(struct connection *conn, struct server *srv)
+{
+ if (is_server_fake_address(srv))
+ conn_set_domain(conn, srv->hostname);
+}
+
+static inline void conn_prepare_new_for_socks4(struct connection *conn, struct server *srv)
+{
+ if (srv && (srv->flags & SRV_F_SOCKS4_PROXY)) {
+ conn->send_proxy_ofs = 1;
+ conn->flags |= CO_FL_SOCKS4;
+ conn_set_domain_from_server(conn, srv);
+ }
+}
+
/* Calls the close() function of the transport layer if any and if not done
* yet, and clears the CO_FL_XPRT_READY flag. However this is not done if the
* CO_FL_XPRT_TRACKED flag is set, which allows logs to take data from the
@@ -121,6 +206,7 @@ static inline void conn_xprt_close(struct connection *conn)
conn->xprt_ctx = NULL;
conn->flags &= ~CO_FL_XPRT_READY;
}
+ conn_free_domain(conn);
}
/* Initializes the connection's control layer which essentially consists in
@@ -160,6 +246,7 @@ static inline void conn_full_close(struct connection *conn)
{
conn_xprt_close(conn);
conn_ctrl_close(conn);
+ conn_free_domain(conn);
}
/* stop tracking a connection, allowing conn_full_close() to always
@@ -295,6 +382,7 @@ static inline void conn_prepare(struct connection *conn, const struct protocol *
conn->mux = NULL;
conn->xprt_ctx = NULL;
conn->ctx = NULL;
+ conn_free_domain(conn);
}
/*
@@ -332,6 +420,7 @@ static inline void conn_init(struct connection *conn, void *target)
conn->dst = NULL;
conn->proxy_authority = NULL;
conn->proxy_unique_id = IST_NULL;
+ conn->requested_domain = NULL;
}
/* sets <owner> as the connection's owner */
@@ -464,6 +553,7 @@ static inline void conn_force_unsubscribe(struct connection *conn)
/* Releases a connection previously allocated by conn_new() */
static inline void conn_free(struct connection *conn)
{
+ conn_free_domain(conn);
if (conn->flags & CO_FL_PRIVATE) {
/* The connection is private, so remove it from the session's
* connections list, if any.
diff --git a/include/haproxy/fake_host.h b/include/haproxy/fake_host.h
new file mode 100644
index 000000000..874dfd56c
--- /dev/null
+++ b/include/haproxy/fake_host.h
@@ -0,0 +1,24 @@
+#ifndef FAKE_HOST_HEDER
+#define FAKE_HOST_HEDER
+/*
+ Todo socks4a we must skip local DNS step and send domain to socks4 server.
+ However this program cannot work if localy remote IP is absent.
+ So this is fake address for SOCKS4 backend.
+ 1. If we detect that socks4 and cannot resolve domain localy, then
+ we setup backend server to use this fake host.
+
+ 2. Next when connection to server is created it checks if server has fake flag (2 places in code - copies.).
+ If it is so, then connection copies hostname field from server to itself.
+
+ 3. Next on handshake connection checks if it has text string "requested domain", if it is so, it does 4A protocol,
+ otherwise 4.
+
+ FAKE_SOCKS4A_IP is used at that step to catch that domain string is absent, but fake ip is set.
+ This means step (2) from above was skipped, i.e. some copy-paste in code was not modified yet.
+
+ There is slim chance that was valid request to this fake IP using SOCKS4. Well, don't use SOCKS4 for this IP :)
+*/
+#define FAKE_SOCKS4A_HOST "10.10.10.10"
+#define FAKE_SOCKS4A_IP (0x0A0A0A0A)
+
+#endif
\ No newline at end of file
diff --git a/include/haproxy/server-t.h b/include/haproxy/server-t.h
index decc5e292..4fb427f4f 100644
--- a/include/haproxy/server-t.h
+++ b/include/haproxy/server-t.h
@@ -141,6 +141,7 @@ enum srv_initaddr {
#define SRV_F_FASTOPEN 0x0200 /* Use TCP Fast Open to connect to server */
#define SRV_F_SOCKS4_PROXY 0x0400 /* this server uses SOCKS4 proxy */
#define SRV_F_NO_RESOLUTION 0x0800 /* disable runtime DNS resolution on this server */
+#define SRV_F_SOCKS4_PROXY_FAILED_RESOLVE 0x1000 /*Some socks which failed to resolve address, need to go SOCKS4A*/
/* configured server options for send-proxy (server->pp_opts) */
#define SRV_PP_V1 0x0001 /* proxy protocol version 1 */
diff --git a/src/backend.c b/src/backend.c
index 490dc1f28..670570f28 100644
--- a/src/backend.c
+++ b/src/backend.c
@@ -361,7 +361,7 @@ static struct server *get_server_ph_post(struct stream *s, const struct server *
*/
if (likely(HTTP_IS_LWS(*p))) /* eol, uncertain uri len */
break;
- return NULL; /* oh, no; this is not uri-encoded.
+ return NULL; /* oh, no; this is not uri-encoded.
* This body does not contain parameters.
*/
}
@@ -1411,12 +1411,13 @@ int connect_server(struct stream *s)
srv_conn->proxy_netns = cli_conn ? cli_conn->proxy_netns : NULL;
if (!conn_xprt_ready(srv_conn) && !srv_conn->mux) {
+ unsigned int pfamily = srv_conn->dst->ss_family;
/* set the correct protocol on the output stream interface */
if (srv)
- conn_prepare(srv_conn, protocol_by_family(srv_conn->dst->ss_family), srv->xprt);
+ conn_prepare(srv_conn, protocol_by_family(pfamily), srv->xprt);
else if (obj_type(s->target) == OBJ_TYPE_PROXY) {
/* proxies exclusively run on raw_sock right now */
- conn_prepare(srv_conn, protocol_by_family(srv_conn->dst->ss_family), xprt_get(XPRT_RAW));
+ conn_prepare(srv_conn, protocol_by_family(pfamily), xprt_get(XPRT_RAW));
if (!(srv_conn->ctrl)) {
conn_free(srv_conn);
return SF_ERR_INTERNAL;
@@ -1452,11 +1453,7 @@ int connect_server(struct stream *s)
}
assign_tproxy_address(s);
-
- if (srv && (srv->flags & SRV_F_SOCKS4_PROXY)) {
- srv_conn->send_proxy_ofs = 1;
- srv_conn->flags |= CO_FL_SOCKS4;
- }
+ conn_prepare_new_for_socks4(srv_conn, srv);
}
else if (!conn_xprt_ready(srv_conn)) {
if (srv_conn->mux->reset)
@@ -1489,8 +1486,10 @@ int connect_server(struct stream *s)
}
err = si_connect(&s->si[1], srv_conn);
- if (err != SF_ERR_NONE)
+ if (err != SF_ERR_NONE) {
+ DPRINTF(stderr, "Backend errored with code 0x%08x on si_connect\n", err);
return err;
+ }
#ifdef USE_OPENSSL
if (srv && srv->ssl_ctx.sni) {
diff --git a/src/connection.c b/src/connection.c
index 9c017bcf9..18b0d355c 100644
--- a/src/connection.c
+++ b/src/connection.c
@@ -25,6 +25,7 @@
#include <haproxy/sample.h>
#include <haproxy/ssl_sock.h>
#include <haproxy/stream_interface.h>
+#include <haproxy/fake_host.h>
DECLARE_POOL(pool_head_connection, "connection", sizeof(struct connection));
@@ -1053,10 +1054,38 @@ int conn_recv_netscaler_cip(struct connection *conn, int flag)
return 0;
}
+static int writeToProxy(struct connection *conn, char *data, size_t len, int flags)
+{
+ int ret = 0;
+
+ /* we are sending the socks4_req_line here. If the data layer
+ * has a pending write, we'll also set MSG_MORE.
+ */
+#ifdef DEBUG_FULL
+ int i;
+ DPRINTF(stderr, "Writting to SOCK4(A) header (%u bytes): \n", (unsigned int)len);
+ for (i = 0; i < len; ++i)
+ DPRINTF(stderr, "%02X ", (unsigned int)*((unsigned char *)data+i));
+ DPRINTF(stderr, "\nEnd-of-block\n");
+#endif
+ ret = conn_sock_send(
+ conn, data+len+conn->send_proxy_ofs,
+ -conn->send_proxy_ofs,
+ flags);
+
+ DPRINTF(stderr, "SOCKS PROXY HS FD[%04X]: Before send remain is [%d], sent [%d]\n",
+ conn->handle.fd, -conn->send_proxy_ofs, ret);
+ if (ret > -1)
+ conn->send_proxy_ofs += ret; /* becomes zero once complete */
+
+ return ret;
+}
int conn_send_socks4_proxy_request(struct connection *conn)
{
struct socks4_request req_line;
+ int proxy_resolve;
+ size_t domainlen;
if (!conn_ctrl_ready(conn))
goto out_error;
@@ -1068,35 +1097,60 @@ int conn_send_socks4_proxy_request(struct connection *conn)
req_line.command = 0x01;
req_line.port = get_net_port(conn->dst);
req_line.ip = is_inet_addr(conn->dst);
+
+ proxy_resolve = conn->requested_domain != NULL;
+ domainlen = (proxy_resolve) ? (strlen(conn->requested_domain)+1) : 0u;
+
+ if (proxy_resolve) {
+ DPRINTF(stderr, "SOCKS PROXY HS FD[%04X]: Have conn->requested_domain set (%s). Trying SOCKS4A\n",
+ conn->handle.fd, conn->requested_domain);
+ req_line.ip = htonl(0x00000001u);
+ } else {
+ if (FAKE_SOCKS4A_IP == req_line.ip) {
+ DPRINTF(stderr, "SOCKS PROXY HS FD[%04X]: Requested fake host ip (%u) with no domain to SOCKS4. Doing error.\n",
+ conn->handle.fd, FAKE_SOCKS4A_IP);
+ goto out_error;
+ }
+ }
memcpy(req_line.user_id, "HAProxy\0", 8);
if (conn->send_proxy_ofs > 0) {
/*
* This is the first call to send the request
*/
- conn->send_proxy_ofs = -(int)sizeof(req_line);
+
+ conn->send_proxy_ofs = -(int)(sizeof(req_line)+domainlen);
}
if (conn->send_proxy_ofs < 0) {
int ret = 0;
+ const size_t hdr_size = sizeof(req_line);
+
+ const int flags = (conn->subs && conn->subs->events & SUB_RETRY_SEND) ? MSG_MORE : 0;
+ if (proxy_resolve) {
+ const size_t total = hdr_size+domainlen;
+ char *tmp = malloc(total);
+
+ if (!tmp) {
+ /* TODO: Handle malloc error */
+ }
+
+ memcpy(tmp, &req_line, hdr_size);
+ memcpy(tmp+hdr_size, conn->requested_domain, domainlen);
+ ret = writeToProxy(conn, tmp, total, flags);
+ free(tmp);
+ }
+ else
+ ret = writeToProxy(conn, (char *)(&req_line), hdr_size, flags);
/* we are sending the socks4_req_line here. If the data layer
* has a pending write, we'll also set MSG_MORE.
*/
- ret = conn_sock_send(
- conn,
- ((char *)(&req_line)) + (sizeof(req_line)+conn->send_proxy_ofs),
- -conn->send_proxy_ofs,
- (conn->subs && conn->subs->events & SUB_RETRY_SEND) ? MSG_MORE : 0);
-
- DPRINTF(stderr, "SOCKS PROXY HS FD[%04X]: Before send remain is [%d], sent [%d]\n",
- conn->handle.fd, -conn->send_proxy_ofs, ret);
if (ret < 0) {
+ DPRINTF(stderr, "writeToProxy failed!!!!\n");
goto out_error;
}
-
- conn->send_proxy_ofs += ret; /* becomes zero once complete */
if (conn->send_proxy_ofs != 0) {
goto out_wait;
}
@@ -1118,6 +1172,8 @@ int conn_send_socks4_proxy_request(struct connection *conn)
*/
conn->send_proxy_ofs = 1;
}
+ if (conn)
+ conn_free_domain(conn);
return 1;
out_error:
@@ -1126,8 +1182,6 @@ int conn_send_socks4_proxy_request(struct connection *conn)
if (conn->err_code == CO_ER_NONE) {
conn->err_code = CO_ER_SOCKS4_SEND;
}
- return 0;
-
out_wait:
return 0;
}
diff --git a/src/server.c b/src/server.c
index b1656d5ce..c4ce1b0f7 100644
--- a/src/server.c
+++ b/src/server.c
@@ -44,6 +44,7 @@
#include <haproxy/task.h>
#include <haproxy/tcpcheck.h>
#include <haproxy/time.h>
+#include <haproxy/fake_host.h>
static void srv_update_status(struct server *s);
@@ -4152,22 +4153,31 @@ out:
*/
int srv_init_addr(void)
{
- struct proxy *curproxy;
+ struct proxy *curproxy = NULL;
int return_code = 0;
curproxy = proxies_list;
while (curproxy) {
- struct server *srv;
+ struct server *srv = NULL;
/* servers are in backend only */
- if (!(curproxy->cap & PR_CAP_BE))
- goto srv_init_addr_next;
-
- for (srv = curproxy->srv; srv; srv = srv->next)
- if (srv->hostname)
- return_code |= srv_iterate_initaddr(srv);
-
- srv_init_addr_next:
+ if (curproxy->cap & PR_CAP_BE) {
+ for (srv = curproxy->srv; srv; srv = srv->next)
+ if (srv->hostname) {
+ if (srv->flags & SRV_F_SOCKS4_PROXY) {
+ int r = srv_iterate_initaddr(srv);
+ if (r) {
+ char *tmp = srv->hostname;
+ srv->hostname = FAKE_SOCKS4A_HOST;
+ srv_iterate_initaddr(srv);
+ srv->hostname = tmp;
+ srv->flags |= SRV_F_SOCKS4_PROXY_FAILED_RESOLVE;
+ }
+ }
+ else
+ return_code |= srv_iterate_initaddr(srv);
+ }
+ }
curproxy = curproxy->next;
}
diff --git a/src/tcpcheck.c b/src/tcpcheck.c
index b9ef3802b..62b81a8ca 100644
--- a/src/tcpcheck.c
+++ b/src/tcpcheck.c
@@ -995,6 +995,8 @@ enum tcpcheck_eval_ret tcpcheck_eval_connect(struct check *check, struct tcpchec
/* 2- prepare new connection */
cs = cs_new(NULL, (s ? &s->obj_type : &proxy->obj_type));
+ /* DPRINTF(stderr, "************\ttcpcheck_eval_connect for server %p, FAKE_IP: %u\n", s, is_server_fake_address(s));
+ */
if (!cs) {
chunk_printf(&trash, "TCPCHK error allocating connection at step %d",
tcpcheck_get_step_id(check, rule));
@@ -1072,6 +1074,7 @@ enum tcpcheck_eval_ret tcpcheck_eval_connect(struct check *check, struct tcpchec
conn_prepare(conn, proto, xprt);
cs_attach(cs, check, &check_conn_cb);
+ conn_prepare_new_for_socks4(conn, s);
if ((connect->options & TCPCHK_OPT_SOCKS4) && s && (s->flags & SRV_F_SOCKS4_PROXY)) {
conn->send_proxy_ofs = 1;
--
2.25.1