MEDIUM: connection: Upstream SOCKS4 proxy support · haproxy/haproxy@2a54bb7 · GitHub
Skip to content

Commit 2a54bb7

Browse files
Alexander Liuwtarreau
authored andcommitted
MEDIUM: connection: Upstream SOCKS4 proxy support
Have "socks4" and "check-via-socks4" server keyword added. Implement handshake with SOCKS4 proxy server for tcp stream connection. See issue #82. I have the "SOCKS: A protocol for TCP proxy across firewalls" doc found at "https://www.openssh.com/txt/socks4.protocol". Please reference to it. [wt: for now connecting to the SOCKS4 proxy over unix sockets is not supported, and mixing IPv4/IPv6 is discouraged; indeed, the control layer is unique for a connection and will be used both for connecting and for target address manipulation. As such it may for example report incorrect destination addresses in logs if the proxy is reached over IPv6]
1 parent cfbb3e6 commit 2a54bb7

12 files changed

Lines changed: 398 additions & 11 deletions

File tree

‎doc/SOCKS4.protocol.txt‎

Lines changed: 1 addition & 0 deletions

‎doc/configuration.txt‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11900,6 +11900,11 @@ check-ssl
1190011900
See the "ssl" option for more information and "no-check-ssl" to disable
1190111901
this option.
1190211902

11903+
check-via-socks4
11904+
This option enables outgoinng health checks using upstream socks4 proxy. By
11905+
default, the health checks won't go through socks tunnel even it was enabled
11906+
for normal traffic.
11907+
1190311908
ciphers <ciphers>
1190411909
This setting is only available when support for OpenSSL was built in. This
1190511910
option sets the string describing the list of cipher algorithms that is
@@ -12560,6 +12565,11 @@ stick
1256012565
It may also be used as "default-server" setting to reset any previous
1256112566
"default-server" "non-stick" setting.
1256212567

12568+
socks4 <addr>:<port>
12569+
This option enables upstream socks4 tunnel for outgoinng connections to the
12570+
server. Using this option won't force the health check to go via socks4 by
12571+
default. You will have to use the keyword "check-via-socks4" to enable it.
12572+
1256312573
tcp-ut <delay>
1256412574
Sets the TCP User Timeout for all outgoing connections to this server. This
1256512575
option is available on Linux since version 2.6.37. It allows haproxy to

‎examples/socks4.cfg‎

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
global
2+
log /dev/log local0
3+
log /dev/log local1 notice
4+
stats timeout 30s
5+
6+
defaults
7+
log global
8+
mode http
9+
option httplog
10+
option dontlognull
11+
timeout connect 5000
12+
timeout client 50000
13+
timeout server 50000
14+
15+
listen SMTP-20025
16+
bind 0.0.0.0:20025
17+
mode tcp
18+
option tcplog
19+
maxconn 2000
20+
timeout connect 5000
21+
timeout client 50000
22+
timeout server 50000
23+
option tcp-check
24+
server SMTPS1 192.0.2.1:25 check inter 30000 fastinter 1000
25+
server SMTPS2_Via_SocksProxy1 192.0.2.2:25 socks4 127.0.0.1:1080 check-via-socks4 check inter 30000 fastinter 1000 backup
26+
27+
listen SSL-20080
28+
bind 0.0.0.0:20080
29+
mode tcp
30+
option tcplog
31+
maxconn 2000
32+
timeout connect 5000
33+
timeout client 50000
34+
timeout server 50000
35+
option tcp-check
36+
server HTTPS1_Via_SocksProxy1 192.0.2.1:443 ssl verify none socks4 127.0.0.1:1080 check inter 30000 fastinter 1000
37+
server HTTPS2 192.0.2.2:443 ssl verify none check inter 30000 fastinter 1000 backup
38+
39+
# HAProxy web ui
40+
listen stats
41+
bind 0.0.0.0:20936
42+
mode http
43+
log global
44+
45+
maxconn 10
46+
timeout client 100s
47+
timeout server 100s
48+
timeout connect 100s
49+
timeout queue 100s
50+
51+
stats enable
52+
stats uri /haproxy?stats
53+
stats realm HAProxy\ Statistics
54+
stats admin if TRUE
55+
stats show-node

‎include/proto/connection.h‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@ int conn_sock_send(struct connection *conn, const void *buf, int len, int flags)
6060
/* drains any pending bytes from the socket */
6161
int conn_sock_drain(struct connection *conn);
6262

63+
/* scoks4 proxy handshake */
64+
int conn_send_socks4_proxy_request(struct connection *conn);
65+
int conn_recv_socks4_proxy_response(struct connection *conn);
66+
6367
/* returns true is the transport layer is ready */
6468
static inline int conn_xprt_ready(const struct connection *conn)
6569
{
@@ -889,6 +893,11 @@ static inline const char *conn_err_code_str(struct connection *c)
889893
case CO_ER_SSL_HANDSHAKE_HB: return "SSL handshake failure after heartbeat";
890894
case CO_ER_SSL_KILLED_HB: return "Stopped a TLSv1 heartbeat attack (CVE-2014-0160)";
891895
case CO_ER_SSL_NO_TARGET: return "Attempt to use SSL on an unknown target (internal error)";
896+
897+
case CO_ER_SOCKS4_SEND: return "SOCKS4 Proxy write error during handshake";
898+
case CO_ER_SOCKS4_RECV: return "SOCKS4 Proxy read error during handshake";
899+
case CO_ER_SOCKS4_DENY: return "SOCKS4 Proxy deny the request";
900+
case CO_ER_SOCKS4_ABORT: return "SOCKS4 Proxy handshake aborted by server";
892901
}
893902
return NULL;
894903
}

‎include/types/checks.h‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,8 @@ struct check {
189189
char *sni; /* Server name */
190190
char *alpn_str; /* ALPN to use for checks */
191191
int alpn_len; /* ALPN string length */
192+
193+
int via_socks4; /* check the connection via socks4 proxy */
192194
};
193195

194196
struct check_status {

‎include/types/connection.h‎

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,15 @@ struct server;
4747
struct session;
4848
struct pipe;
4949

50+
/* socks4 upstream proxy definitions */
51+
struct socks4_request {
52+
uint8_t version; /* SOCKS version number, 1 byte, must be 0x04 for this version */
53+
uint8_t command; /* 0x01 = establish a TCP/IP stream connection */
54+
uint16_t port; /* port number, 2 bytes (in network byte order) */
55+
uint32_t ip; /* IP address, 4 bytes (in network byte order) */
56+
char user_id[8]; /* the user ID string, variable length, terminated with a null (0x00); Using "HAProxy\0" */
57+
};
58+
5059
/* Note: subscribing to these events is only valid after the caller has really
5160
* attempted to perform the operation, and failed to proceed or complete.
5261
*/
@@ -155,8 +164,8 @@ enum {
155164

156165
CO_FL_EARLY_SSL_HS = 0x00004000, /* We have early data pending, don't start SSL handshake yet */
157166
CO_FL_EARLY_DATA = 0x00008000, /* At least some of the data are early data */
158-
/* unused : 0x00010000 */
159-
/* unused : 0x00020000 */
167+
CO_FL_SOCKS4_SEND = 0x00010000, /* handshaking with upstream SOCKS4 proxy, going to send the handshake */
168+
CO_FL_SOCKS4_RECV = 0x00020000, /* handshaking with upstream SOCKS4 proxy, going to check if handshake succeed */
160169

161170
/* flags used to remember what shutdown have been performed/reported */
162171
CO_FL_SOCK_RD_SH = 0x00040000, /* SOCK layer was notified about shutr/read0 */
@@ -182,7 +191,7 @@ enum {
182191
CO_FL_ACCEPT_CIP = 0x08000000, /* receive a valid NetScaler Client IP header */
183192

184193
/* below we have all handshake flags grouped into one */
185-
CO_FL_HANDSHAKE = CO_FL_SEND_PROXY | CO_FL_SSL_WAIT_HS | CO_FL_ACCEPT_PROXY | CO_FL_ACCEPT_CIP,
194+
CO_FL_HANDSHAKE = CO_FL_SEND_PROXY | CO_FL_SSL_WAIT_HS | CO_FL_ACCEPT_PROXY | CO_FL_ACCEPT_CIP | CO_FL_SOCKS4_SEND | CO_FL_SOCKS4_RECV,
186195

187196
/* when any of these flags is set, polling is defined by socket-layer
188197
* operations, as opposed to data-layer. Transport is explicitly not
@@ -205,8 +214,10 @@ enum {
205214
* must be done after clearing this flag.
206215
*/
207216
CO_FL_XPRT_TRACKED = 0x80000000,
208-
};
209217

218+
/* below we have all SOCKS handshake flags grouped into one */
219+
CO_FL_SOCKS4 = CO_FL_SOCKS4_SEND | CO_FL_SOCKS4_RECV,
220+
};
210221

211222
/* possible connection error codes */
212223
enum {
@@ -254,6 +265,11 @@ enum {
254265
CO_ER_SSL_KILLED_HB, /* Stopped a TLSv1 heartbeat attack (CVE-2014-0160) */
255266
CO_ER_SSL_NO_TARGET, /* unknown target (not client nor server) */
256267
CO_ER_SSL_EARLY_FAILED, /* Server refused early data */
268+
269+
CO_ER_SOCKS4_SEND, /* SOCKS4 Proxy write error during handshake */
270+
CO_ER_SOCKS4_RECV, /* SOCKS4 Proxy read error during handshake */
271+
CO_ER_SOCKS4_DENY, /* SOCKS4 Proxy deny the request */
272+
CO_ER_SOCKS4_ABORT, /* SOCKS4 Proxy handshake aborted by server */
257273
};
258274

259275
/* source address settings for outgoing connections */
@@ -425,7 +441,7 @@ struct connection {
425441
/* first cache line */
426442
enum obj_type obj_type; /* differentiates connection from applet context */
427443
unsigned char err_code; /* CO_ER_* */
428-
signed short send_proxy_ofs; /* <0 = offset to (re)send from the end, >0 = send all */
444+
signed short send_proxy_ofs; /* <0 = offset to (re)send from the end, >0 = send all (reused for SOCKS4) */
429445
unsigned int flags; /* CO_FL_* */
430446
const struct protocol *ctrl; /* operations at the socket layer */
431447
const struct xprt_ops *xprt; /* operations at the transport layer */
@@ -576,6 +592,8 @@ struct tlv_ssl {
576592
/* Max number of file descriptors we send in one sendmsg() */
577593
#define MAX_SEND_FD 253
578594

595+
#define SOCKS4_HS_RSP_LEN 8
596+
579597
#endif /* _TYPES_CONNECTION_H */
580598

581599
/*

‎include/types/server.h‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ enum srv_initaddr {
141141
#define SRV_F_AGENTADDR 0x0080 /* this server has a agent addr configured */
142142
#define SRV_F_COOKIESET 0x0100 /* this server has a cookie configured, so don't generate dynamic cookies */
143143
#define SRV_F_FASTOPEN 0x0200 /* Use TCP Fast Open to connect to server */
144+
#define SRV_F_SOCKS4_PROXY 0x0400 /* this server uses SOCKS4 proxy */
144145

145146
/* configured server options for send-proxy (server->pp_opts) */
146147
#define SRV_PP_V1 0x0001 /* proxy protocol version 1 */
@@ -336,6 +337,8 @@ struct server {
336337
char reason[128];
337338
} op_st_chg; /* operational status change's reason */
338339
char adm_st_chg_cause[48]; /* administrative status change's cause */
340+
341+
struct sockaddr_storage socks4_addr; /* the address of the SOCKS4 Proxy, including the port */
339342
};
340343

341344
/* Descriptor for a "server" keyword. The ->parse() function returns 0 in case of

‎src/backend.c‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1533,12 +1533,18 @@ int connect_server(struct stream *s)
15331533

15341534
if (srv && srv->pp_opts) {
15351535
srv_conn->flags |= CO_FL_PRIVATE;
1536+
srv_conn->flags |= CO_FL_SEND_PROXY;
15361537
srv_conn->send_proxy_ofs = 1; /* must compute size */
15371538
if (cli_conn)
15381539
conn_get_to_addr(cli_conn);
15391540
}
15401541

15411542
assign_tproxy_address(s);
1543+
1544+
if (srv && (srv->flags & SRV_F_SOCKS4_PROXY)) {
1545+
srv_conn->send_proxy_ofs = 1;
1546+
srv_conn->flags |= CO_FL_SOCKS4;
1547+
}
15421548
}
15431549
else if (!conn_xprt_ready(srv_conn)) {
15441550
if (srv_conn->mux->reset)

‎src/checks.c‎

Lines changed: 5 additions & 0 deletions

0 commit comments

Comments
 (0)