circuit: Refactor connection_proxy_connect

Since connection_proxy_connect is too long now, it's better to create
new functions (connection_https_proxy_connect,
connection_socks4_proxy_connect, and connection_socks5_proxy_connect) to
make connection_proxy_connect shorter.
This commit is contained in:
Suphanat Chunhapanya 2019-08-23 10:35:50 +08:00
parent 101bdeb02d
commit 52e59640f9

View File

@ -2326,10 +2326,173 @@ conn_get_proxy_type(const connection_t *conn)
username NUL: */
#define SOCKS4_STANDARD_BUFFER_SIZE (1 + 1 + 2 + 4 + 1)
/** Write a proxy request of <b>type</b> (socks4, socks5, https) to conn
* for conn->addr:conn->port, authenticating with the auth details given
* in the configuration (if available). SOCKS 5 and HTTP CONNECT proxies
* support authentication.
/** Write a proxy request of https to conn for conn->addr:conn->port,
* authenticating with the auth details given in the configuration
* (if available).
*
* Returns -1 if conn->addr is incompatible with the proxy protocol, and
* 0 otherwise.
*/
static int
connection_https_proxy_connect(connection_t *conn)
{
tor_assert(conn);
const or_options_t *options = get_options();
char buf[1024];
char *base64_authenticator = NULL;
const char *authenticator = options->HTTPSProxyAuthenticator;
/* Send HTTP CONNECT and authentication (if available) in
* one request */
if (authenticator) {
base64_authenticator = alloc_http_authenticator(authenticator);
if (!base64_authenticator)
log_warn(LD_OR, "Encoding https authenticator failed");
}
if (base64_authenticator) {
const char *addrport = fmt_addrport(&conn->addr, conn->port);
tor_snprintf(buf, sizeof(buf), "CONNECT %s HTTP/1.1\r\n"
"Host: %s\r\n"
"Proxy-Authorization: Basic %s\r\n\r\n",
addrport,
addrport,
base64_authenticator);
tor_free(base64_authenticator);
} else {
tor_snprintf(buf, sizeof(buf), "CONNECT %s HTTP/1.0\r\n\r\n",
fmt_addrport(&conn->addr, conn->port));
}
connection_buf_add(buf, strlen(buf), conn);
conn->proxy_state = PROXY_HTTPS_WANT_CONNECT_OK;
return 0;
}
/** Write a proxy request of socks4 to conn for conn->addr:conn->port.
*
* Returns -1 if conn->addr is incompatible with the proxy protocol, and
* 0 otherwise.
*/
static int
connection_socks4_proxy_connect(connection_t *conn)
{
tor_assert(conn);
unsigned char *buf;
uint16_t portn;
uint32_t ip4addr;
size_t buf_size = 0;
char *socks_args_string = NULL;
/* Send a SOCKS4 connect request */
if (tor_addr_family(&conn->addr) != AF_INET) {
log_warn(LD_NET, "SOCKS4 client is incompatible with IPv6");
return -1;
}
{ /* If we are here because we are trying to connect to a
pluggable transport proxy, check if we have any SOCKS
arguments to transmit. If we do, compress all arguments to
a single string in 'socks_args_string': */
if (conn_get_proxy_type(conn) == PROXY_PLUGGABLE) {
socks_args_string =
pt_get_socks_args_for_proxy_addrport(&conn->addr, conn->port);
if (socks_args_string)
log_debug(LD_NET, "Sending out '%s' as our SOCKS argument string.",
socks_args_string);
}
}
{ /* Figure out the buffer size we need for the SOCKS message: */
buf_size = SOCKS4_STANDARD_BUFFER_SIZE;
/* If we have a SOCKS argument string, consider its size when
calculating the buffer size: */
if (socks_args_string)
buf_size += strlen(socks_args_string);
}
buf = tor_malloc_zero(buf_size);
ip4addr = tor_addr_to_ipv4n(&conn->addr);
portn = htons(conn->port);
buf[0] = 4; /* version */
buf[1] = SOCKS_COMMAND_CONNECT; /* command */
memcpy(buf + 2, &portn, 2); /* port */
memcpy(buf + 4, &ip4addr, 4); /* addr */
/* Next packet field is the userid. If we have pluggable
transport SOCKS arguments, we have to embed them
there. Otherwise, we use an empty userid. */
if (socks_args_string) { /* place the SOCKS args string: */
tor_assert(strlen(socks_args_string) > 0);
tor_assert(buf_size >=
SOCKS4_STANDARD_BUFFER_SIZE + strlen(socks_args_string));
strlcpy((char *)buf + 8, socks_args_string, buf_size - 8);
tor_free(socks_args_string);
} else {
buf[8] = 0; /* no userid */
}
connection_buf_add((char *)buf, buf_size, conn);
tor_free(buf);
conn->proxy_state = PROXY_SOCKS4_WANT_CONNECT_OK;
return 0;
}
/** Write a proxy request of socks5 to conn for conn->addr:conn->port,
* authenticating with the auth details given in the configuration
* (if available).
*
* Returns -1 if conn->addr is incompatible with the proxy protocol, and
* 0 otherwise.
*/
static int
connection_socks5_proxy_connect(connection_t *conn)
{
tor_assert(conn);
const or_options_t *options = get_options();
unsigned char buf[4]; /* fields: vers, num methods, method list */
/* Send a SOCKS5 greeting (connect request must wait) */
buf[0] = 5; /* version */
/* We have to use SOCKS5 authentication, if we have a
Socks5ProxyUsername or if we want to pass arguments to our
pluggable transport proxy: */
if ((options->Socks5ProxyUsername) ||
(conn_get_proxy_type(conn) == PROXY_PLUGGABLE &&
(get_socks_args_by_bridge_addrport(&conn->addr, conn->port)))) {
/* number of auth methods */
buf[1] = 2;
buf[2] = 0x00; /* no authentication */
buf[3] = 0x02; /* rfc1929 Username/Passwd auth */
conn->proxy_state = PROXY_SOCKS5_WANT_AUTH_METHOD_RFC1929;
} else {
buf[1] = 1;
buf[2] = 0x00; /* no authentication */
conn->proxy_state = PROXY_SOCKS5_WANT_AUTH_METHOD_NONE;
}
connection_buf_add((char *)buf, 2 + buf[1], conn);
return 0;
}
/** Write a proxy request of <b>type</b> (socks4, socks5, https, haproxy)
* to conn for conn->addr:conn->port, authenticating with the auth details
* given in the configuration (if available). SOCKS 5 and HTTP CONNECT
* proxies support authentication.
*
* Returns -1 if conn->addr is incompatible with the proxy protocol, and
* 0 otherwise.
@ -2339,152 +2502,36 @@ conn_get_proxy_type(const connection_t *conn)
int
connection_proxy_connect(connection_t *conn, int type)
{
const or_options_t *options;
int ret = 0;
tor_assert(conn);
options = get_options();
switch (type) {
case PROXY_CONNECT: {
char buf[1024];
char *base64_authenticator=NULL;
const char *authenticator = options->HTTPSProxyAuthenticator;
/* Send HTTP CONNECT and authentication (if available) in
* one request */
if (authenticator) {
base64_authenticator = alloc_http_authenticator(authenticator);
if (!base64_authenticator)
log_warn(LD_OR, "Encoding https authenticator failed");
}
if (base64_authenticator) {
const char *addrport = fmt_addrport(&conn->addr, conn->port);
tor_snprintf(buf, sizeof(buf), "CONNECT %s HTTP/1.1\r\n"
"Host: %s\r\n"
"Proxy-Authorization: Basic %s\r\n\r\n",
addrport,
addrport,
base64_authenticator);
tor_free(base64_authenticator);
} else {
tor_snprintf(buf, sizeof(buf), "CONNECT %s HTTP/1.0\r\n\r\n",
fmt_addrport(&conn->addr, conn->port));
}
connection_buf_add(buf, strlen(buf), conn);
conn->proxy_state = PROXY_HTTPS_WANT_CONNECT_OK;
case PROXY_CONNECT:
ret = connection_https_proxy_connect(conn);
break;
}
case PROXY_SOCKS4: {
unsigned char *buf;
uint16_t portn;
uint32_t ip4addr;
size_t buf_size = 0;
char *socks_args_string = NULL;
/* Send a SOCKS4 connect request */
if (tor_addr_family(&conn->addr) != AF_INET) {
log_warn(LD_NET, "SOCKS4 client is incompatible with IPv6");
return -1;
}
{ /* If we are here because we are trying to connect to a
pluggable transport proxy, check if we have any SOCKS
arguments to transmit. If we do, compress all arguments to
a single string in 'socks_args_string': */
if (conn_get_proxy_type(conn) == PROXY_PLUGGABLE) {
socks_args_string =
pt_get_socks_args_for_proxy_addrport(&conn->addr, conn->port);
if (socks_args_string)
log_debug(LD_NET, "Sending out '%s' as our SOCKS argument string.",
socks_args_string);
}
}
{ /* Figure out the buffer size we need for the SOCKS message: */
buf_size = SOCKS4_STANDARD_BUFFER_SIZE;
/* If we have a SOCKS argument string, consider its size when
calculating the buffer size: */
if (socks_args_string)
buf_size += strlen(socks_args_string);
}
buf = tor_malloc_zero(buf_size);
ip4addr = tor_addr_to_ipv4n(&conn->addr);
portn = htons(conn->port);
buf[0] = 4; /* version */
buf[1] = SOCKS_COMMAND_CONNECT; /* command */
memcpy(buf + 2, &portn, 2); /* port */
memcpy(buf + 4, &ip4addr, 4); /* addr */
/* Next packet field is the userid. If we have pluggable
transport SOCKS arguments, we have to embed them
there. Otherwise, we use an empty userid. */
if (socks_args_string) { /* place the SOCKS args string: */
tor_assert(strlen(socks_args_string) > 0);
tor_assert(buf_size >=
SOCKS4_STANDARD_BUFFER_SIZE + strlen(socks_args_string));
strlcpy((char *)buf + 8, socks_args_string, buf_size - 8);
tor_free(socks_args_string);
} else {
buf[8] = 0; /* no userid */
}
connection_buf_add((char *)buf, buf_size, conn);
tor_free(buf);
conn->proxy_state = PROXY_SOCKS4_WANT_CONNECT_OK;
case PROXY_SOCKS4:
ret = connection_socks4_proxy_connect(conn);
break;
}
case PROXY_SOCKS5: {
unsigned char buf[4]; /* fields: vers, num methods, method list */
/* Send a SOCKS5 greeting (connect request must wait) */
buf[0] = 5; /* version */
/* We have to use SOCKS5 authentication, if we have a
Socks5ProxyUsername or if we want to pass arguments to our
pluggable transport proxy: */
if ((options->Socks5ProxyUsername) ||
(conn_get_proxy_type(conn) == PROXY_PLUGGABLE &&
(get_socks_args_by_bridge_addrport(&conn->addr, conn->port)))) {
/* number of auth methods */
buf[1] = 2;
buf[2] = 0x00; /* no authentication */
buf[3] = 0x02; /* rfc1929 Username/Passwd auth */
conn->proxy_state = PROXY_SOCKS5_WANT_AUTH_METHOD_RFC1929;
} else {
buf[1] = 1;
buf[2] = 0x00; /* no authentication */
conn->proxy_state = PROXY_SOCKS5_WANT_AUTH_METHOD_NONE;
}
connection_buf_add((char *)buf, 2 + buf[1], conn);
case PROXY_SOCKS5:
ret = connection_socks5_proxy_connect(conn);
break;
}
default:
log_err(LD_BUG, "Invalid proxy protocol, %d", type);
tor_fragile_assert();
return -1;
ret = -1;
break;
}
log_debug(LD_NET, "set state %s",
connection_proxy_state_to_string(conn->proxy_state));
if (ret == 0) {
log_debug(LD_NET, "set state %s",
connection_proxy_state_to_string(conn->proxy_state));
}
return 0;
return ret;
}
/** Read conn's inbuf. If the http response from the proxy is all