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]
This commit is contained in:
Alexander Liu 2019-05-22 19:44:48 +08:00 committed by Willy Tarreau
parent cfbb3e6560
commit 2a54bb74cd
12 changed files with 398 additions and 11 deletions

1
doc/SOCKS4.protocol.txt Normal file
View File

@ -0,0 +1 @@
Please reference to "https://www.openssh.com/txt/socks4.protocol".

View File

@ -11900,6 +11900,11 @@ check-ssl
See the "ssl" option for more information and "no-check-ssl" to disable
this option.
check-via-socks4
This option enables outgoinng health checks using upstream socks4 proxy. By
default, the health checks won't go through socks tunnel even it was enabled
for normal traffic.
ciphers <ciphers>
This setting is only available when support for OpenSSL was built in. This
option sets the string describing the list of cipher algorithms that is
@ -12560,6 +12565,11 @@ stick
It may also be used as "default-server" setting to reset any previous
"default-server" "non-stick" setting.
socks4 <addr>:<port>
This option enables upstream socks4 tunnel for outgoinng connections to the
server. Using this option won't force the health check to go via socks4 by
default. You will have to use the keyword "check-via-socks4" to enable it.
tcp-ut <delay>
Sets the TCP User Timeout for all outgoing connections to this server. This
option is available on Linux since version 2.6.37. It allows haproxy to

55
examples/socks4.cfg Normal file
View File

@ -0,0 +1,55 @@
global
log /dev/log local0
log /dev/log local1 notice
stats timeout 30s
defaults
log global
mode http
option httplog
option dontlognull
timeout connect 5000
timeout client 50000
timeout server 50000
listen SMTP-20025
bind 0.0.0.0:20025
mode tcp
option tcplog
maxconn 2000
timeout connect 5000
timeout client 50000
timeout server 50000
option tcp-check
server SMTPS1 192.0.2.1:25 check inter 30000 fastinter 1000
server SMTPS2_Via_SocksProxy1 192.0.2.2:25 socks4 127.0.0.1:1080 check-via-socks4 check inter 30000 fastinter 1000 backup
listen SSL-20080
bind 0.0.0.0:20080
mode tcp
option tcplog
maxconn 2000
timeout connect 5000
timeout client 50000
timeout server 50000
option tcp-check
server HTTPS1_Via_SocksProxy1 192.0.2.1:443 ssl verify none socks4 127.0.0.1:1080 check inter 30000 fastinter 1000
server HTTPS2 192.0.2.2:443 ssl verify none check inter 30000 fastinter 1000 backup
# HAProxy web ui
listen stats
bind 0.0.0.0:20936
mode http
log global
maxconn 10
timeout client 100s
timeout server 100s
timeout connect 100s
timeout queue 100s
stats enable
stats uri /haproxy?stats
stats realm HAProxy\ Statistics
stats admin if TRUE
stats show-node

View File

@ -60,6 +60,10 @@ int conn_sock_send(struct connection *conn, const void *buf, int len, int flags)
/* drains any pending bytes from the socket */
int conn_sock_drain(struct connection *conn);
/* scoks4 proxy handshake */
int conn_send_socks4_proxy_request(struct connection *conn);
int conn_recv_socks4_proxy_response(struct connection *conn);
/* returns true is the transport layer is ready */
static inline int conn_xprt_ready(const struct connection *conn)
{
@ -889,6 +893,11 @@ static inline const char *conn_err_code_str(struct connection *c)
case CO_ER_SSL_HANDSHAKE_HB: return "SSL handshake failure after heartbeat";
case CO_ER_SSL_KILLED_HB: return "Stopped a TLSv1 heartbeat attack (CVE-2014-0160)";
case CO_ER_SSL_NO_TARGET: return "Attempt to use SSL on an unknown target (internal error)";
case CO_ER_SOCKS4_SEND: return "SOCKS4 Proxy write error during handshake";
case CO_ER_SOCKS4_RECV: return "SOCKS4 Proxy read error during handshake";
case CO_ER_SOCKS4_DENY: return "SOCKS4 Proxy deny the request";
case CO_ER_SOCKS4_ABORT: return "SOCKS4 Proxy handshake aborted by server";
}
return NULL;
}

View File

@ -189,6 +189,8 @@ struct check {
char *sni; /* Server name */
char *alpn_str; /* ALPN to use for checks */
int alpn_len; /* ALPN string length */
int via_socks4; /* check the connection via socks4 proxy */
};
struct check_status {

View File

@ -47,6 +47,15 @@ struct server;
struct session;
struct pipe;
/* socks4 upstream proxy definitions */
struct socks4_request {
uint8_t version; /* SOCKS version number, 1 byte, must be 0x04 for this version */
uint8_t command; /* 0x01 = establish a TCP/IP stream connection */
uint16_t port; /* port number, 2 bytes (in network byte order) */
uint32_t ip; /* IP address, 4 bytes (in network byte order) */
char user_id[8]; /* the user ID string, variable length, terminated with a null (0x00); Using "HAProxy\0" */
};
/* Note: subscribing to these events is only valid after the caller has really
* attempted to perform the operation, and failed to proceed or complete.
*/
@ -155,8 +164,8 @@ enum {
CO_FL_EARLY_SSL_HS = 0x00004000, /* We have early data pending, don't start SSL handshake yet */
CO_FL_EARLY_DATA = 0x00008000, /* At least some of the data are early data */
/* unused : 0x00010000 */
/* unused : 0x00020000 */
CO_FL_SOCKS4_SEND = 0x00010000, /* handshaking with upstream SOCKS4 proxy, going to send the handshake */
CO_FL_SOCKS4_RECV = 0x00020000, /* handshaking with upstream SOCKS4 proxy, going to check if handshake succeed */
/* flags used to remember what shutdown have been performed/reported */
CO_FL_SOCK_RD_SH = 0x00040000, /* SOCK layer was notified about shutr/read0 */
@ -182,7 +191,7 @@ enum {
CO_FL_ACCEPT_CIP = 0x08000000, /* receive a valid NetScaler Client IP header */
/* below we have all handshake flags grouped into one */
CO_FL_HANDSHAKE = CO_FL_SEND_PROXY | CO_FL_SSL_WAIT_HS | CO_FL_ACCEPT_PROXY | CO_FL_ACCEPT_CIP,
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,
/* when any of these flags is set, polling is defined by socket-layer
* operations, as opposed to data-layer. Transport is explicitly not
@ -205,8 +214,10 @@ enum {
* must be done after clearing this flag.
*/
CO_FL_XPRT_TRACKED = 0x80000000,
};
/* below we have all SOCKS handshake flags grouped into one */
CO_FL_SOCKS4 = CO_FL_SOCKS4_SEND | CO_FL_SOCKS4_RECV,
};
/* possible connection error codes */
enum {
@ -254,6 +265,11 @@ enum {
CO_ER_SSL_KILLED_HB, /* Stopped a TLSv1 heartbeat attack (CVE-2014-0160) */
CO_ER_SSL_NO_TARGET, /* unknown target (not client nor server) */
CO_ER_SSL_EARLY_FAILED, /* Server refused early data */
CO_ER_SOCKS4_SEND, /* SOCKS4 Proxy write error during handshake */
CO_ER_SOCKS4_RECV, /* SOCKS4 Proxy read error during handshake */
CO_ER_SOCKS4_DENY, /* SOCKS4 Proxy deny the request */
CO_ER_SOCKS4_ABORT, /* SOCKS4 Proxy handshake aborted by server */
};
/* source address settings for outgoing connections */
@ -425,7 +441,7 @@ struct connection {
/* first cache line */
enum obj_type obj_type; /* differentiates connection from applet context */
unsigned char err_code; /* CO_ER_* */
signed short send_proxy_ofs; /* <0 = offset to (re)send from the end, >0 = send all */
signed short send_proxy_ofs; /* <0 = offset to (re)send from the end, >0 = send all (reused for SOCKS4) */
unsigned int flags; /* CO_FL_* */
const struct protocol *ctrl; /* operations at the socket layer */
const struct xprt_ops *xprt; /* operations at the transport layer */
@ -576,6 +592,8 @@ struct tlv_ssl {
/* Max number of file descriptors we send in one sendmsg() */
#define MAX_SEND_FD 253
#define SOCKS4_HS_RSP_LEN 8
#endif /* _TYPES_CONNECTION_H */
/*

View File

@ -141,6 +141,7 @@ enum srv_initaddr {
#define SRV_F_AGENTADDR 0x0080 /* this server has a agent addr configured */
#define SRV_F_COOKIESET 0x0100 /* this server has a cookie configured, so don't generate dynamic cookies */
#define SRV_F_FASTOPEN 0x0200 /* Use TCP Fast Open to connect to server */
#define SRV_F_SOCKS4_PROXY 0x0400 /* this server uses SOCKS4 proxy */
/* configured server options for send-proxy (server->pp_opts) */
#define SRV_PP_V1 0x0001 /* proxy protocol version 1 */
@ -336,6 +337,8 @@ struct server {
char reason[128];
} op_st_chg; /* operational status change's reason */
char adm_st_chg_cause[48]; /* administrative status change's cause */
struct sockaddr_storage socks4_addr; /* the address of the SOCKS4 Proxy, including the port */
};
/* Descriptor for a "server" keyword. The ->parse() function returns 0 in case of

View File

@ -1533,12 +1533,18 @@ int connect_server(struct stream *s)
if (srv && srv->pp_opts) {
srv_conn->flags |= CO_FL_PRIVATE;
srv_conn->flags |= CO_FL_SEND_PROXY;
srv_conn->send_proxy_ofs = 1; /* must compute size */
if (cli_conn)
conn_get_to_addr(cli_conn);
}
assign_tproxy_address(s);
if (srv && (srv->flags & SRV_F_SOCKS4_PROXY)) {
srv_conn->send_proxy_ofs = 1;
srv_conn->flags |= CO_FL_SOCKS4;
}
}
else if (!conn_xprt_ready(srv_conn)) {
if (srv_conn->mux->reset)

View File

@ -1612,6 +1612,11 @@ static int connect_conn_chk(struct task *t)
conn->addr.to = s->addr;
}
if (s->check.via_socks4 && (s->flags & SRV_F_SOCKS4_PROXY)) {
conn->send_proxy_ofs = 1;
conn->flags |= CO_FL_SOCKS4;
}
proto = protocol_by_family(conn->addr.to.ss_family);
conn->target = &s->obj_type;

View File

@ -27,6 +27,8 @@
#include <proto/sample.h>
#include <proto/ssl_sock.h>
#include <common/debug.h>
DECLARE_POOL(pool_head_connection, "connection", sizeof(struct connection));
DECLARE_POOL(pool_head_connstream, "conn_stream", sizeof(struct conn_stream));
@ -69,6 +71,14 @@ void conn_fd_handler(int fd)
if (unlikely(conn->flags & CO_FL_ERROR))
goto leave;
if (conn->flags & CO_FL_SOCKS4_SEND)
if (!conn_send_socks4_proxy_request(conn))
goto leave;
if (conn->flags & CO_FL_SOCKS4_RECV)
if (!conn_recv_socks4_proxy_response(conn))
goto leave;
if (conn->flags & CO_FL_ACCEPT_CIP)
if (!conn_recv_netscaler_cip(conn, CO_FL_ACCEPT_CIP))
goto leave;
@ -959,6 +969,209 @@ int conn_recv_netscaler_cip(struct connection *conn, int flag)
return 0;
}
int conn_send_socks4_proxy_request(struct connection *conn)
{
struct socks4_request req_line;
/* we might have been called just after an asynchronous shutw */
if (conn->flags & CO_FL_SOCK_WR_SH)
goto out_error;
if (!conn_ctrl_ready(conn))
goto out_error;
req_line.version = 0x04;
req_line.command = 0x01;
req_line.port = get_net_port(&(conn->addr.to));
req_line.ip = is_inet_addr(&(conn->addr.to));
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);
}
if (conn->send_proxy_ofs < 0) {
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.
*/
ret = conn_sock_send(
conn,
((char *)(&req_line)) + (sizeof(req_line)+conn->send_proxy_ofs),
-conn->send_proxy_ofs,
(conn->flags & CO_FL_XPRT_WR_ENA) ? 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) {
goto out_error;
}
conn->send_proxy_ofs += ret; /* becomes zero once complete */
if (conn->send_proxy_ofs != 0) {
goto out_wait;
}
}
/* OK we've the whole request sent */
conn->flags &= ~CO_FL_SOCKS4_SEND;
__conn_sock_stop_send(conn);
/* The connection is ready now, simply return and let the connection
* handler notify upper layers if needed.
*/
if (conn->flags & CO_FL_WAIT_L4_CONN)
conn->flags &= ~CO_FL_WAIT_L4_CONN;
if (conn->flags & CO_FL_SEND_PROXY) {
/*
* Get the send_proxy_ofs ready for the send_proxy due to we are
* reusing the "send_proxy_ofs", and SOCKS4 handshake should be done
* before sending PROXY Protocol.
*/
conn->send_proxy_ofs = 1;
}
return 1;
out_error:
/* Write error on the file descriptor */
conn->flags |= CO_FL_ERROR;
if (conn->err_code == CO_ER_NONE) {
conn->err_code = CO_ER_SOCKS4_SEND;
}
return 0;
out_wait:
__conn_sock_stop_recv(conn);
return 0;
}
int conn_recv_socks4_proxy_response(struct connection *conn)
{
char line[SOCKS4_HS_RSP_LEN];
int ret;
/* we might have been called just after an asynchronous shutr */
if (conn->flags & CO_FL_SOCK_RD_SH)
goto fail;
if (!conn_ctrl_ready(conn))
goto fail;
if (!fd_recv_ready(conn->handle.fd))
return 0;
do {
/* SOCKS4 Proxy will response with 8 bytes, 0x00 | 0x5A | 0x00 0x00 | 0x00 0x00 0x00 0x00
* Try to peek into it, before all 8 bytes ready.
*/
ret = recv(conn->handle.fd, line, SOCKS4_HS_RSP_LEN, MSG_PEEK);
if (ret == 0) {
/* the socket has been closed or shutdown for send */
DPRINTF(stderr, "SOCKS PROXY HS FD[%04X]: Received ret[%d], errno[%d], looks like the socket has been closed or shutdown for send\n",
conn->handle.fd, ret, errno);
if (conn->err_code == CO_ER_NONE) {
conn->err_code = CO_ER_SOCKS4_RECV;
}
goto fail;
}
if (ret > 0) {
if (ret == SOCKS4_HS_RSP_LEN) {
DPRINTF(stderr, "SOCKS PROXY HS FD[%04X]: Received 8 bytes, the response is [%02X|%02X|%02X %02X|%02X %02X %02X %02X]\n",
conn->handle.fd, line[0], line[1], line[2], line[3], line[4], line[5], line[6], line[7]);
}else{
DPRINTF(stderr, "SOCKS PROXY HS FD[%04X]: Received ret[%d], first byte is [%02X], last bye is [%02X]\n", conn->handle.fd, ret, line[0], line[ret-1]);
}
} else {
DPRINTF(stderr, "SOCKS PROXY HS FD[%04X]: Received ret[%d], errno[%d]\n", conn->handle.fd, ret, errno);
}
if (ret < 0) {
if (errno == EINTR) {
continue;
}
if (errno == EAGAIN) {
fd_cant_recv(conn->handle.fd);
__conn_sock_want_recv(conn);
return 0;
}
goto recv_abort;
}
} while (0);
if (ret < SOCKS4_HS_RSP_LEN) {
/* Missing data. Since we're using MSG_PEEK, we can only poll again if
* we are not able to read enough data.
*/
fd_cant_recv(conn->handle.fd);
__conn_sock_want_recv(conn);
return 0;
}
/*
* Base on the SOCSK4 protocol:
*
* +----+----+----+----+----+----+----+----+
* | VN | CD | DSTPORT | DSTIP |
* +----+----+----+----+----+----+----+----+
* # of bytes: 1 1 2 4
* VN is the version of the reply code and should be 0. CD is the result
* code with one of the following values:
* 90: request granted
* 91: request rejected or failed
* 92: request rejected becasue SOCKS server cannot connect to identd on the client
* 93: request rejected because the client program and identd report different user-ids
* The remaining fields are ignored.
*/
if (line[1] != 90) {
conn->flags &= ~CO_FL_SOCKS4_RECV;
DPRINTF(stderr, "SOCKS PROXY HS FD[%04X]: FAIL, the response is [%02X|%02X|%02X %02X|%02X %02X %02X %02X]\n",
conn->handle.fd, line[0], line[1], line[2], line[3], line[4], line[5], line[6], line[7]);
if (conn->err_code == CO_ER_NONE) {
conn->err_code = CO_ER_SOCKS4_DENY;
}
goto fail;
}
/* remove the 8 bytes response from the stream */
do {
ret = recv(conn->handle.fd, line, SOCKS4_HS_RSP_LEN, 0);
if (ret < 0 && errno == EINTR) {
continue;
}
if (ret != SOCKS4_HS_RSP_LEN) {
if (conn->err_code == CO_ER_NONE) {
conn->err_code = CO_ER_SOCKS4_RECV;
}
goto fail;
}
} while (0);
conn->flags &= ~CO_FL_SOCKS4_RECV;
return 1;
recv_abort:
if (conn->err_code == CO_ER_NONE) {
conn->err_code = CO_ER_SOCKS4_ABORT;
}
conn->flags |= (CO_FL_SOCK_RD_SH | CO_FL_SOCK_WR_SH);
goto fail;
fail:
__conn_sock_stop_both(conn);
conn->flags |= CO_FL_ERROR;
return 0;
}
/* Note: <remote> is explicitly allowed to be NULL */
int make_proxy_line(char *buf, int buf_len, struct server *srv, struct connection *remote)
{

View File

@ -294,6 +294,7 @@ int tcp_connect_server(struct connection *conn, int flags)
struct proxy *be;
struct conn_src *src;
int use_fastopen = 0;
struct sockaddr_storage *addr;
conn->flags |= CO_FL_WAIT_L4_CONN; /* connection in progress */
@ -514,7 +515,8 @@ int tcp_connect_server(struct connection *conn, int flags)
if (global.tune.server_rcvbuf)
setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &global.tune.server_rcvbuf, sizeof(global.tune.server_rcvbuf));
if (connect(fd, (struct sockaddr *)&conn->addr.to, get_addr_len(&conn->addr.to)) == -1) {
addr = (conn->flags & CO_FL_SOCKS4) ? &srv->socks4_addr : &conn->addr.to;
if (connect(fd, (const struct sockaddr *)addr, get_addr_len(addr)) == -1) {
if (errno == EINPROGRESS || errno == EALREADY) {
/* common case, let's wait for connect status */
conn->flags |= CO_FL_WAIT_L4_CONN;
@ -567,10 +569,6 @@ int tcp_connect_server(struct connection *conn, int flags)
conn->flags |= CO_FL_ADDR_TO_SET;
/* Prepare to send a few handshakes related to the on-wire protocol. */
if (conn->send_proxy_ofs)
conn->flags |= CO_FL_SEND_PROXY;
conn_ctrl_init(conn); /* registers the FD */
fdtab[fd].linger_risk = 1; /* close hard if needed */
@ -663,6 +661,7 @@ int tcp_get_dst(int fd, struct sockaddr *sa, socklen_t salen, int dir)
*/
int tcp_connect_probe(struct connection *conn)
{
struct sockaddr_storage *addr;
int fd = conn->handle.fd;
socklen_t lskerr;
int skerr;
@ -701,7 +700,11 @@ int tcp_connect_probe(struct connection *conn)
* - connecting (EALREADY, EINPROGRESS)
* - connected (EISCONN, 0)
*/
if (connect(fd, (struct sockaddr *)&conn->addr.to, get_addr_len(&conn->addr.to)) < 0) {
addr = &conn->addr.to;
if ((conn->flags & CO_FL_SOCKS4) && obj_type(conn->target) == OBJ_TYPE_SERVER)
addr = &objt_server(conn->target)->socks4_addr;
if (connect(fd, (const struct sockaddr *)addr, get_addr_len(addr)) == -1) {
if (errno == EALREADY || errno == EINPROGRESS) {
__conn_sock_stop_recv(conn);
fd_cant_send(fd);

View File

@ -322,6 +322,14 @@ static int srv_parse_check_send_proxy(char **args, int *cur_arg,
return 0;
}
/* Parse the "check-via-socks4" server keyword */
static int srv_parse_check_via_socks4(char **args, int *cur_arg,
struct proxy *curproxy, struct server *newsrv, char **err)
{
newsrv->check.via_socks4 = 1;
return 0;
}
/* Parse the "cookie" server keyword */
static int srv_parse_cookie(char **args, int *cur_arg,
struct proxy *curproxy, struct server *newsrv, char **err)
@ -888,6 +896,55 @@ static int srv_parse_track(char **args, int *cur_arg,
return 0;
}
/* Parse the "socks4" server keyword */
static int srv_parse_socks4(char **args, int *cur_arg,
struct proxy *curproxy, struct server *newsrv, char **err)
{
char *errmsg;
int port_low, port_high;
struct sockaddr_storage *sk;
struct protocol *proto;
errmsg = NULL;
if (!*args[*cur_arg + 1]) {
memprintf(err, "'%s' expects <addr>:<port> as argument.\n", args[*cur_arg]);
goto err;
}
/* 'sk' is statically allocated (no need to be freed). */
sk = str2sa_range(args[*cur_arg + 1], NULL, &port_low, &port_high, &errmsg, NULL, NULL, 1);
if (!sk) {
memprintf(err, "'%s %s' : %s\n", args[*cur_arg], args[*cur_arg + 1], errmsg);
goto err;
}
proto = protocol_by_family(sk->ss_family);
if (!proto || !proto->connect) {
ha_alert("'%s %s' : connect() not supported for this address family.\n", args[*cur_arg], args[*cur_arg + 1]);
goto err;
}
newsrv->flags |= SRV_F_SOCKS4_PROXY;
newsrv->socks4_addr = *sk;
if (port_low != port_high) {
ha_alert("'%s' does not support port offsets (found '%s').\n", args[*cur_arg], args[*cur_arg + 1]);
goto err;
}
if (!port_low) {
ha_alert("'%s': invalid port range %d-%d.\n", args[*cur_arg], port_low, port_high);
goto err;
}
return 0;
err:
free(errmsg);
return ERR_ALERT | ERR_FATAL;
}
/* parse the "tfo" server keyword */
static int srv_parse_tfo(char **args, int *cur_arg, struct proxy *px, struct server *newsrv, char **err)
@ -1286,6 +1343,8 @@ static struct srv_kw_list srv_kws = { "ALL", { }, {
{ "stick", srv_parse_stick, 0, 1 }, /* Enable stick-table persistence */
{ "tfo", srv_parse_tfo, 0, 0 }, /* enable TCP Fast Open of server */
{ "track", srv_parse_track, 1, 1 }, /* Set the current state of the server, tracking another one */
{ "socks4", srv_parse_socks4, 1, 1 }, /* Set the socks4 proxy of the server*/
{ "check-via-socks4", srv_parse_check_via_socks4, 0, 1 }, /* enable socks4 proxy for health checks */
{ NULL, NULL, 0 },
}};
@ -1721,6 +1780,9 @@ static void srv_settings_cpy(struct server *srv, struct server *src, int srv_tmp
if (srv_tmpl)
srv->srvrq = src->srvrq;
srv->check.via_socks4 = src->check.via_socks4;
srv->socks4_addr = src->socks4_addr;
}
struct server *new_server(struct proxy *proxy)