mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-10 21:23:58 +01:00
Merge commit 'public/socks-client'
Resolved conflict in: src/or/or.h
This commit is contained in:
commit
707a6bd659
19
doc/tor.1.in
19
doc/tor.1.in
@ -299,6 +299,25 @@ HTTPS proxy authentication that Tor supports; feel free to submit a
|
||||
patch if you want it to support others.
|
||||
.LP
|
||||
.TP
|
||||
\fBSocks4Proxy\fR \fIhost\fR[:\fIport\fR]\fP
|
||||
Tor will make all OR connections through the SOCKS 4 proxy at host:port
|
||||
(or host:1080 if port is not specified).
|
||||
.LP
|
||||
.TP
|
||||
\fBSocks5Proxy\fR \fIhost\fR[:\fIport\fR]\fP
|
||||
Tor will make all OR connections through the SOCKS 5 proxy at host:port
|
||||
(or host:1080 if port is not specified).
|
||||
.LP
|
||||
.TP
|
||||
\fBSocks5ProxyUsername\fR \fIusername\fP
|
||||
.LP
|
||||
.TP
|
||||
\fBSocks5ProxyPassword\fR \fIpassword\fP
|
||||
If defined, authenticate to the SOCKS 5 server using username and password
|
||||
in accordance to RFC 1929. Both username and password must be between 1 and 255
|
||||
characters.
|
||||
.LP
|
||||
.TP
|
||||
\fBKeepalivePeriod \fR\fINUM\fP
|
||||
To keep firewalls from expiring connections, send a padding keepalive
|
||||
cell every NUM seconds on open connections that are in use. If the
|
||||
|
126
src/or/buffers.c
126
src/or/buffers.c
@ -1611,6 +1611,132 @@ fetch_from_buf_socks(buf_t *buf, socks_request_t *req,
|
||||
}
|
||||
}
|
||||
|
||||
/** Inspect a reply from SOCKS server stored in <b>buf</b> according
|
||||
* to <b>state</b>, removing the protocol data upon success. Return 0 on
|
||||
* incomplete response, 1 on success and -1 on error, in which case
|
||||
* <b>reason</b> is set to a descriptive message (free() when finished
|
||||
* with it).
|
||||
*
|
||||
* As a special case, 2 is returned when user/pass is required
|
||||
* during SOCKS5 handshake and user/pass is configured.
|
||||
*/
|
||||
int
|
||||
fetch_from_buf_socks_client(buf_t *buf, int state, char **reason)
|
||||
{
|
||||
unsigned char *data;
|
||||
size_t addrlen;
|
||||
|
||||
if (buf->datalen < 2)
|
||||
return 0;
|
||||
|
||||
buf_pullup(buf, 128, 0);
|
||||
tor_assert(buf->head && buf->head->datalen >= 2);
|
||||
|
||||
data = (unsigned char *) buf->head->data;
|
||||
|
||||
switch (state) {
|
||||
case PROXY_SOCKS4_WANT_CONNECT_OK:
|
||||
/* Wait for the complete response */
|
||||
if (buf->head->datalen < 8)
|
||||
return 0;
|
||||
|
||||
if (data[1] != 0x5a) {
|
||||
*reason = tor_strdup(socks4_response_code_to_string(data[1]));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Success */
|
||||
buf_remove_from_front(buf, 8);
|
||||
return 1;
|
||||
|
||||
case PROXY_SOCKS5_WANT_AUTH_METHOD_NONE:
|
||||
/* we don't have any credentials */
|
||||
if (data[1] != 0x00) {
|
||||
*reason = tor_strdup("server doesn't support any of our "
|
||||
"available authentication methods");
|
||||
return -1;
|
||||
}
|
||||
|
||||
log_info(LD_NET, "SOCKS 5 client: continuing without authentication");
|
||||
buf_clear(buf);
|
||||
return 1;
|
||||
|
||||
case PROXY_SOCKS5_WANT_AUTH_METHOD_RFC1929:
|
||||
/* we have a username and password. return 1 if we can proceed without
|
||||
* providing authentication, or 2 otherwise. */
|
||||
switch (data[1]) {
|
||||
case 0x00:
|
||||
log_info(LD_NET, "SOCKS 5 client: we have auth details but server "
|
||||
"doesn't require authentication.");
|
||||
buf_clear(buf);
|
||||
return 1;
|
||||
case 0x02:
|
||||
log_info(LD_NET, "SOCKS 5 client: need authentication.");
|
||||
buf_clear(buf);
|
||||
return 2;
|
||||
/* fall through */
|
||||
}
|
||||
|
||||
*reason = tor_strdup("server doesn't support any of our available "
|
||||
"authentication methods");
|
||||
return -1;
|
||||
|
||||
case PROXY_SOCKS5_WANT_AUTH_RFC1929_OK:
|
||||
/* handle server reply to rfc1929 authentication */
|
||||
if (data[1] != 0x00) {
|
||||
*reason = tor_strdup("authentication failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
log_info(LD_NET, "SOCKS 5 client: authentication successful.");
|
||||
buf_clear(buf);
|
||||
return 1;
|
||||
|
||||
case PROXY_SOCKS5_WANT_CONNECT_OK:
|
||||
/* response is variable length. BND.ADDR, etc, isn't needed
|
||||
* (don't bother with buf_pullup()), but make sure to eat all
|
||||
* the data used */
|
||||
|
||||
/* wait for address type field to arrive */
|
||||
if (buf->datalen < 4)
|
||||
return 0;
|
||||
|
||||
switch (data[3]) {
|
||||
case 0x01: /* ip4 */
|
||||
addrlen = 4;
|
||||
break;
|
||||
case 0x04: /* ip6 */
|
||||
addrlen = 16;
|
||||
break;
|
||||
case 0x03: /* fqdn (can this happen here?) */
|
||||
if (buf->datalen < 5)
|
||||
return 0;
|
||||
addrlen = 1 + data[4];
|
||||
break;
|
||||
default:
|
||||
*reason = tor_strdup("invalid response to connect request");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* wait for address and port */
|
||||
if (buf->datalen < 6 + addrlen)
|
||||
return 0;
|
||||
|
||||
if (data[1] != 0x00) {
|
||||
*reason = tor_strdup(socks5_response_code_to_string(data[1]));
|
||||
return -1;
|
||||
}
|
||||
|
||||
buf_remove_from_front(buf, 6 + addrlen);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* shouldn't get here... */
|
||||
tor_assert(0);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/** Return 1 iff buf looks more like it has an (obsolete) v0 controller
|
||||
* command on it than any valid v1 controller command. */
|
||||
int
|
||||
|
@ -246,6 +246,10 @@ static config_var_t _option_vars[] = {
|
||||
V(HttpProxyAuthenticator, STRING, NULL),
|
||||
V(HttpsProxy, STRING, NULL),
|
||||
V(HttpsProxyAuthenticator, STRING, NULL),
|
||||
V(Socks4Proxy, STRING, NULL),
|
||||
V(Socks5Proxy, STRING, NULL),
|
||||
V(Socks5ProxyUsername, STRING, NULL),
|
||||
V(Socks5ProxyPassword, STRING, NULL),
|
||||
OBSOLETE("IgnoreVersion"),
|
||||
V(KeepalivePeriod, INTERVAL, "5 minutes"),
|
||||
VAR("Log", LINELIST, Logs, NULL),
|
||||
@ -3482,7 +3486,7 @@ options_validate(or_options_t *old_options, or_options_t *options,
|
||||
REJECT("Failed to parse accounting options. See logs for details.");
|
||||
|
||||
if (options->HttpProxy) { /* parse it now */
|
||||
if (parse_addr_port(LOG_WARN, options->HttpProxy, NULL,
|
||||
if (tor_addr_port_parse(options->HttpProxy,
|
||||
&options->HttpProxyAddr, &options->HttpProxyPort) < 0)
|
||||
REJECT("HttpProxy failed to parse or resolve. Please fix.");
|
||||
if (options->HttpProxyPort == 0) { /* give it a default */
|
||||
@ -3496,7 +3500,7 @@ options_validate(or_options_t *old_options, or_options_t *options,
|
||||
}
|
||||
|
||||
if (options->HttpsProxy) { /* parse it now */
|
||||
if (parse_addr_port(LOG_WARN, options->HttpsProxy, NULL,
|
||||
if (tor_addr_port_parse(options->HttpsProxy,
|
||||
&options->HttpsProxyAddr, &options->HttpsProxyPort) <0)
|
||||
REJECT("HttpsProxy failed to parse or resolve. Please fix.");
|
||||
if (options->HttpsProxyPort == 0) { /* give it a default */
|
||||
@ -3509,6 +3513,42 @@ options_validate(or_options_t *old_options, or_options_t *options,
|
||||
REJECT("HttpsProxyAuthenticator is too long (>= 48 chars).");
|
||||
}
|
||||
|
||||
if (options->Socks4Proxy) { /* parse it now */
|
||||
if (tor_addr_port_parse(options->Socks4Proxy,
|
||||
&options->Socks4ProxyAddr,
|
||||
&options->Socks4ProxyPort) <0)
|
||||
REJECT("Socks4Proxy failed to parse or resolve. Please fix.");
|
||||
if (options->Socks4ProxyPort == 0) { /* give it a default */
|
||||
options->Socks4ProxyPort = 1080;
|
||||
}
|
||||
}
|
||||
|
||||
if (options->Socks5Proxy) { /* parse it now */
|
||||
if (tor_addr_port_parse(options->Socks5Proxy,
|
||||
&options->Socks5ProxyAddr,
|
||||
&options->Socks5ProxyPort) <0)
|
||||
REJECT("Socks5Proxy failed to parse or resolve. Please fix.");
|
||||
if (options->Socks5ProxyPort == 0) { /* give it a default */
|
||||
options->Socks5ProxyPort = 1080;
|
||||
}
|
||||
}
|
||||
|
||||
if (options->Socks5ProxyUsername) {
|
||||
size_t len;
|
||||
|
||||
len = strlen(options->Socks5ProxyUsername);
|
||||
if (len < 1 || len > 255)
|
||||
REJECT("Socks5ProxyUsername must be between 1 and 255 characters.");
|
||||
|
||||
if (!options->Socks5ProxyPassword)
|
||||
REJECT("Socks5ProxyPassword must be included with Socks5ProxyUsername.");
|
||||
|
||||
len = strlen(options->Socks5ProxyPassword);
|
||||
if (len < 1 || len > 255)
|
||||
REJECT("Socks5ProxyPassword must be between 1 and 255 characters.");
|
||||
} else if (options->Socks5ProxyPassword)
|
||||
REJECT("Socks5ProxyPassword must be included with Socks5ProxyUsername.");
|
||||
|
||||
if (options->HashedControlPassword) {
|
||||
smartlist_t *sl = decode_hashed_passwords(options->HashedControlPassword);
|
||||
if (!sl) {
|
||||
@ -3657,6 +3697,12 @@ options_validate(or_options_t *old_options, or_options_t *options,
|
||||
if (options->PreferTunneledDirConns && !options->TunnelDirConns)
|
||||
REJECT("Must set TunnelDirConns if PreferTunneledDirConns is set.");
|
||||
|
||||
if ((options->Socks4Proxy || options->Socks5Proxy) &&
|
||||
!options->HttpProxy && !options->PreferTunneledDirConns)
|
||||
REJECT("When Socks4Proxy or Socks5Proxy is configured, "
|
||||
"PreferTunneledDirConns and TunnelDirConns must both be "
|
||||
"set to 1, or HttpProxy must be configured.");
|
||||
|
||||
if (options->AutomapHostsSuffixes) {
|
||||
SMARTLIST_FOREACH(options->AutomapHostsSuffixes, char *, suf,
|
||||
{
|
||||
|
@ -32,6 +32,10 @@ static int connection_process_inbuf(connection_t *conn, int package_partial);
|
||||
static void client_check_address_changed(int sock);
|
||||
static void set_constrained_socket_buffers(int sock, int size);
|
||||
|
||||
static const char *connection_proxy_state_to_string(int state);
|
||||
static int connection_read_https_proxy_response(connection_t *conn);
|
||||
static void connection_send_socks5_connect(connection_t *conn);
|
||||
|
||||
/** The last IPv4 address that our network interface seemed to have been
|
||||
* binding to, in host order. We use this to detect when our IP changes. */
|
||||
static uint32_t last_interface_ip = 0;
|
||||
@ -92,8 +96,7 @@ conn_state_to_string(int type, int state)
|
||||
case CONN_TYPE_OR:
|
||||
switch (state) {
|
||||
case OR_CONN_STATE_CONNECTING: return "connect()ing";
|
||||
case OR_CONN_STATE_PROXY_FLUSHING: return "proxy flushing";
|
||||
case OR_CONN_STATE_PROXY_READING: return "proxy reading";
|
||||
case OR_CONN_STATE_PROXY_HANDSHAKING: return "handshaking (proxy)";
|
||||
case OR_CONN_STATE_TLS_HANDSHAKING: return "handshaking (TLS)";
|
||||
case OR_CONN_STATE_TLS_CLIENT_RENEGOTIATING:
|
||||
return "renegotiating (TLS)";
|
||||
@ -1289,6 +1292,353 @@ connection_connect(connection_t *conn, const char *address,
|
||||
return inprogress ? 0 : 1;
|
||||
}
|
||||
|
||||
/** Convert state number to string representation for logging purposes.
|
||||
*/
|
||||
static const char *
|
||||
connection_proxy_state_to_string(int state)
|
||||
{
|
||||
static const char *unknown = "???";
|
||||
static const char *states[] = {
|
||||
"PROXY_NONE",
|
||||
"PROXY_HTTPS_WANT_CONNECT_OK",
|
||||
"PROXY_SOCKS4_WANT_CONNECT_OK",
|
||||
"PROXY_SOCKS5_WANT_AUTH_METHOD_NONE",
|
||||
"PROXY_SOCKS5_WANT_AUTH_METHOD_RFC1929",
|
||||
"PROXY_SOCKS5_WANT_AUTH_RFC1929_OK",
|
||||
"PROXY_SOCKS5_WANT_CONNECT_OK",
|
||||
"PROXY_CONNECTED",
|
||||
};
|
||||
|
||||
if (state < PROXY_NONE || state > PROXY_CONNECTED)
|
||||
return unknown;
|
||||
|
||||
return states[state];
|
||||
}
|
||||
|
||||
/** 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.
|
||||
*
|
||||
* Returns -1 if conn->addr is incompatible with the proxy protocol, and
|
||||
* 0 otherwise.
|
||||
*
|
||||
* Use connection_read_proxy_handshake() to complete the handshake.
|
||||
*/
|
||||
int
|
||||
connection_proxy_connect(connection_t *conn, int type)
|
||||
{
|
||||
or_options_t *options;
|
||||
|
||||
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) {
|
||||
tor_snprintf(buf, sizeof(buf), "CONNECT %s:%d HTTP/1.1\r\n"
|
||||
"Proxy-Authorization: Basic %s\r\n\r\n",
|
||||
fmt_addr(&conn->addr),
|
||||
conn->port, base64_authenticator);
|
||||
tor_free(base64_authenticator);
|
||||
} else {
|
||||
tor_snprintf(buf, sizeof(buf), "CONNECT %s:%d HTTP/1.0\r\n\r\n",
|
||||
fmt_addr(&conn->addr), conn->port);
|
||||
}
|
||||
|
||||
connection_write_to_buf(buf, strlen(buf), conn);
|
||||
conn->proxy_state = PROXY_HTTPS_WANT_CONNECT_OK;
|
||||
break;
|
||||
}
|
||||
|
||||
case PROXY_SOCKS4: {
|
||||
unsigned char buf[9];
|
||||
uint16_t portn;
|
||||
uint32_t ip4addr;
|
||||
|
||||
/* Send a SOCKS4 connect request with empty user id */
|
||||
|
||||
if (tor_addr_family(&conn->addr) != AF_INET) {
|
||||
log_warn(LD_NET, "SOCKS4 client is incompatible with with IPv6");
|
||||
return -1;
|
||||
}
|
||||
|
||||
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 */
|
||||
buf[8] = 0; /* userid (empty) */
|
||||
|
||||
connection_write_to_buf((char *)buf, sizeof(buf), conn);
|
||||
conn->proxy_state = PROXY_SOCKS4_WANT_CONNECT_OK;
|
||||
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 */
|
||||
|
||||
/* number of auth methods */
|
||||
if (options->Socks5ProxyUsername) {
|
||||
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_write_to_buf((char *)buf, 2 + buf[1], conn);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
log_err(LD_BUG, "Invalid proxy protocol, %d", type);
|
||||
tor_fragile_assert();
|
||||
return -1;
|
||||
}
|
||||
|
||||
log_debug(LD_NET, "set state %s",
|
||||
connection_proxy_state_to_string(conn->proxy_state));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Read conn's inbuf. If the http response from the proxy is all
|
||||
* here, make sure it's good news, then return 1. If it's bad news,
|
||||
* return -1. Else return 0 and hope for better luck next time.
|
||||
*/
|
||||
static int
|
||||
connection_read_https_proxy_response(connection_t *conn)
|
||||
{
|
||||
char *headers;
|
||||
char *reason=NULL;
|
||||
int status_code;
|
||||
time_t date_header;
|
||||
|
||||
switch (fetch_from_buf_http(conn->inbuf,
|
||||
&headers, MAX_HEADERS_SIZE,
|
||||
NULL, NULL, 10000, 0)) {
|
||||
case -1: /* overflow */
|
||||
log_warn(LD_PROTOCOL,
|
||||
"Your https proxy sent back an oversized response. Closing.");
|
||||
return -1;
|
||||
case 0:
|
||||
log_info(LD_NET,"https proxy response not all here yet. Waiting.");
|
||||
return 0;
|
||||
/* case 1, fall through */
|
||||
}
|
||||
|
||||
if (parse_http_response(headers, &status_code, &date_header,
|
||||
NULL, &reason) < 0) {
|
||||
log_warn(LD_NET,
|
||||
"Unparseable headers from proxy (connecting to '%s'). Closing.",
|
||||
conn->address);
|
||||
tor_free(headers);
|
||||
return -1;
|
||||
}
|
||||
if (!reason) reason = tor_strdup("[no reason given]");
|
||||
|
||||
if (status_code == 200) {
|
||||
log_info(LD_NET,
|
||||
"HTTPS connect to '%s' successful! (200 %s) Starting TLS.",
|
||||
conn->address, escaped(reason));
|
||||
tor_free(reason);
|
||||
return 1;
|
||||
}
|
||||
/* else, bad news on the status code */
|
||||
log_warn(LD_NET,
|
||||
"The https proxy sent back an unexpected status code %d (%s). "
|
||||
"Closing.",
|
||||
status_code, escaped(reason));
|
||||
tor_free(reason);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/** Send SOCKS5 CONNECT command to <b>conn</b>, copying <b>conn->addr</b>
|
||||
* and <b>conn->port</b> into the request.
|
||||
*/
|
||||
static void
|
||||
connection_send_socks5_connect(connection_t *conn)
|
||||
{
|
||||
unsigned char buf[1024];
|
||||
size_t reqsize = 6;
|
||||
uint16_t port = htons(conn->port);
|
||||
|
||||
buf[0] = 5; /* version */
|
||||
buf[1] = SOCKS_COMMAND_CONNECT; /* command */
|
||||
buf[2] = 0; /* reserved */
|
||||
|
||||
if (tor_addr_family(&conn->addr) == AF_INET) {
|
||||
uint32_t addr = tor_addr_to_ipv4n(&conn->addr);
|
||||
|
||||
buf[3] = 1;
|
||||
reqsize += 4;
|
||||
memcpy(buf + 4, &addr, 4);
|
||||
memcpy(buf + 8, &port, 2);
|
||||
} else { /* AF_INET6 */
|
||||
buf[3] = 4;
|
||||
reqsize += 16;
|
||||
memcpy(buf + 4, tor_addr_to_in6(&conn->addr), 16);
|
||||
memcpy(buf + 20, &port, 2);
|
||||
}
|
||||
|
||||
connection_write_to_buf((char *)buf, reqsize, conn);
|
||||
|
||||
conn->proxy_state = PROXY_SOCKS5_WANT_CONNECT_OK;
|
||||
}
|
||||
|
||||
/** Call this from connection_*_process_inbuf() to advance the proxy
|
||||
* handshake.
|
||||
*
|
||||
* No matter what proxy protocol is used, if this function returns 1, the
|
||||
* handshake is complete, and the data remaining on inbuf may contain the
|
||||
* start of the communication with the requested server.
|
||||
*
|
||||
* Returns 0 if the current buffer contains an incomplete response, and -1
|
||||
* on error.
|
||||
*/
|
||||
int
|
||||
connection_read_proxy_handshake(connection_t *conn)
|
||||
{
|
||||
int ret = 0;
|
||||
char *reason = NULL;
|
||||
|
||||
log_debug(LD_NET, "enter state %s",
|
||||
connection_proxy_state_to_string(conn->proxy_state));
|
||||
|
||||
switch (conn->proxy_state) {
|
||||
case PROXY_HTTPS_WANT_CONNECT_OK:
|
||||
ret = connection_read_https_proxy_response(conn);
|
||||
if (ret == 1)
|
||||
conn->proxy_state = PROXY_CONNECTED;
|
||||
break;
|
||||
|
||||
case PROXY_SOCKS4_WANT_CONNECT_OK:
|
||||
ret = fetch_from_buf_socks_client(conn->inbuf,
|
||||
conn->proxy_state,
|
||||
&reason);
|
||||
if (ret == 1)
|
||||
conn->proxy_state = PROXY_CONNECTED;
|
||||
break;
|
||||
|
||||
case PROXY_SOCKS5_WANT_AUTH_METHOD_NONE:
|
||||
ret = fetch_from_buf_socks_client(conn->inbuf,
|
||||
conn->proxy_state,
|
||||
&reason);
|
||||
/* no auth needed, do connect */
|
||||
if (ret == 1) {
|
||||
connection_send_socks5_connect(conn);
|
||||
ret = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case PROXY_SOCKS5_WANT_AUTH_METHOD_RFC1929:
|
||||
ret = fetch_from_buf_socks_client(conn->inbuf,
|
||||
conn->proxy_state,
|
||||
&reason);
|
||||
|
||||
/* send auth if needed, otherwise do connect */
|
||||
if (ret == 1) {
|
||||
connection_send_socks5_connect(conn);
|
||||
ret = 0;
|
||||
} else if (ret == 2) {
|
||||
unsigned char buf[1024];
|
||||
size_t reqsize, usize, psize;
|
||||
const char *user, *pass;
|
||||
|
||||
user = get_options()->Socks5ProxyUsername;
|
||||
pass = get_options()->Socks5ProxyPassword;
|
||||
tor_assert(user && pass);
|
||||
|
||||
/* XXX len of user and pass must be <= 255 !!! */
|
||||
usize = strlen(user);
|
||||
psize = strlen(pass);
|
||||
tor_assert(usize <= 255 && psize <= 255);
|
||||
reqsize = 3 + usize + psize;
|
||||
|
||||
buf[0] = 1; /* negotiation version */
|
||||
buf[1] = usize;
|
||||
memcpy(buf + 2, user, usize);
|
||||
buf[2 + usize] = psize;
|
||||
memcpy(buf + 3 + usize, pass, psize);
|
||||
|
||||
connection_write_to_buf((char *)buf, reqsize, conn);
|
||||
|
||||
conn->proxy_state = PROXY_SOCKS5_WANT_AUTH_RFC1929_OK;
|
||||
ret = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case PROXY_SOCKS5_WANT_AUTH_RFC1929_OK:
|
||||
ret = fetch_from_buf_socks_client(conn->inbuf,
|
||||
conn->proxy_state,
|
||||
&reason);
|
||||
/* send the connect request */
|
||||
if (ret == 1) {
|
||||
connection_send_socks5_connect(conn);
|
||||
ret = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case PROXY_SOCKS5_WANT_CONNECT_OK:
|
||||
ret = fetch_from_buf_socks_client(conn->inbuf,
|
||||
conn->proxy_state,
|
||||
&reason);
|
||||
if (ret == 1)
|
||||
conn->proxy_state = PROXY_CONNECTED;
|
||||
break;
|
||||
|
||||
default:
|
||||
log_err(LD_BUG, "Invalid proxy_state for reading, %d",
|
||||
conn->proxy_state);
|
||||
tor_fragile_assert();
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
log_debug(LD_NET, "leaving state %s",
|
||||
connection_proxy_state_to_string(conn->proxy_state));
|
||||
|
||||
if (ret < 0) {
|
||||
if (reason) {
|
||||
log_warn(LD_NET, "Proxy Client: unable to connect to %s:%d (%s)",
|
||||
conn->address, conn->port, escaped(reason));
|
||||
tor_free(reason);
|
||||
} else {
|
||||
log_warn(LD_NET, "Proxy Client: unable to connect to %s:%d",
|
||||
conn->address, conn->port);
|
||||
}
|
||||
} else if (ret == 1) {
|
||||
log_info(LD_NET, "Proxy Client: connection to %s:%d successful",
|
||||
conn->address, conn->port);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Launch any configured listener connections of type <b>type</b>. (A
|
||||
* listener is configured if <b>port_option</b> is non-zero. If any
|
||||
@ -2055,7 +2405,7 @@ connection_read_to_buf(connection_t *conn, int *max_to_read, int *socket_error)
|
||||
}
|
||||
|
||||
if (connection_speaks_cells(conn) &&
|
||||
conn->state > OR_CONN_STATE_PROXY_READING) {
|
||||
conn->state > OR_CONN_STATE_PROXY_HANDSHAKING) {
|
||||
int pending;
|
||||
or_connection_t *or_conn = TO_OR_CONN(conn);
|
||||
size_t initial_size;
|
||||
@ -2283,7 +2633,7 @@ connection_handle_write(connection_t *conn, int force)
|
||||
: connection_bucket_write_limit(conn, now);
|
||||
|
||||
if (connection_speaks_cells(conn) &&
|
||||
conn->state > OR_CONN_STATE_PROXY_READING) {
|
||||
conn->state > OR_CONN_STATE_PROXY_HANDSHAKING) {
|
||||
or_connection_t *or_conn = TO_OR_CONN(conn);
|
||||
if (conn->state == OR_CONN_STATE_TLS_HANDSHAKING ||
|
||||
conn->state == OR_CONN_STATE_TLS_CLIENT_RENEGOTIATING) {
|
||||
@ -3016,7 +3366,7 @@ assert_connection_ok(connection_t *conn, time_t now)
|
||||
}
|
||||
// tor_assert(conn->addr && conn->port);
|
||||
tor_assert(conn->address);
|
||||
if (conn->state > OR_CONN_STATE_PROXY_READING)
|
||||
if (conn->state > OR_CONN_STATE_PROXY_HANDSHAKING)
|
||||
tor_assert(or_conn->tls);
|
||||
}
|
||||
|
||||
|
@ -187,66 +187,6 @@ connection_or_reached_eof(or_connection_t *conn)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Read conn's inbuf. If the http response from the proxy is all
|
||||
* here, make sure it's good news, and begin the tls handshake. If
|
||||
* it's bad news, close the connection and return -1. Else return 0
|
||||
* and hope for better luck next time.
|
||||
*/
|
||||
static int
|
||||
connection_or_read_proxy_response(or_connection_t *or_conn)
|
||||
{
|
||||
char *headers;
|
||||
char *reason=NULL;
|
||||
int status_code;
|
||||
time_t date_header;
|
||||
connection_t *conn = TO_CONN(or_conn);
|
||||
|
||||
switch (fetch_from_buf_http(conn->inbuf,
|
||||
&headers, MAX_HEADERS_SIZE,
|
||||
NULL, NULL, 10000, 0)) {
|
||||
case -1: /* overflow */
|
||||
log_warn(LD_PROTOCOL,
|
||||
"Your https proxy sent back an oversized response. Closing.");
|
||||
return -1;
|
||||
case 0:
|
||||
log_info(LD_OR,"https proxy response not all here yet. Waiting.");
|
||||
return 0;
|
||||
/* case 1, fall through */
|
||||
}
|
||||
|
||||
if (parse_http_response(headers, &status_code, &date_header,
|
||||
NULL, &reason) < 0) {
|
||||
log_warn(LD_OR,
|
||||
"Unparseable headers from proxy (connecting to '%s'). Closing.",
|
||||
conn->address);
|
||||
tor_free(headers);
|
||||
return -1;
|
||||
}
|
||||
if (!reason) reason = tor_strdup("[no reason given]");
|
||||
|
||||
if (status_code == 200) {
|
||||
log_info(LD_OR,
|
||||
"HTTPS connect to '%s' successful! (200 %s) Starting TLS.",
|
||||
conn->address, escaped(reason));
|
||||
tor_free(reason);
|
||||
if (connection_tls_start_handshake(or_conn, 0) < 0) {
|
||||
/* TLS handshaking error of some kind. */
|
||||
connection_mark_for_close(conn);
|
||||
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
/* else, bad news on the status code */
|
||||
log_warn(LD_OR,
|
||||
"The https proxy sent back an unexpected status code %d (%s). "
|
||||
"Closing.",
|
||||
status_code, escaped(reason));
|
||||
tor_free(reason);
|
||||
connection_mark_for_close(conn);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/** Handle any new bytes that have come in on connection <b>conn</b>.
|
||||
* If conn is in 'open' state, hand it to
|
||||
* connection_or_process_cells_from_inbuf()
|
||||
@ -255,11 +195,24 @@ connection_or_read_proxy_response(or_connection_t *or_conn)
|
||||
int
|
||||
connection_or_process_inbuf(or_connection_t *conn)
|
||||
{
|
||||
int ret;
|
||||
tor_assert(conn);
|
||||
|
||||
switch (conn->_base.state) {
|
||||
case OR_CONN_STATE_PROXY_READING:
|
||||
return connection_or_read_proxy_response(conn);
|
||||
case OR_CONN_STATE_PROXY_HANDSHAKING:
|
||||
ret = connection_read_proxy_handshake(TO_CONN(conn));
|
||||
|
||||
/* start TLS after handshake completion, or deal with error */
|
||||
if (ret == 1) {
|
||||
tor_assert(TO_CONN(conn)->proxy_state == PROXY_CONNECTED);
|
||||
if (connection_tls_start_handshake(conn, 0) < 0)
|
||||
ret = -1;
|
||||
}
|
||||
if (ret < 0) {
|
||||
connection_mark_for_close(TO_CONN(conn));
|
||||
}
|
||||
|
||||
return ret;
|
||||
case OR_CONN_STATE_OPEN:
|
||||
case OR_CONN_STATE_OR_HANDSHAKING:
|
||||
return connection_or_process_cells_from_inbuf(conn);
|
||||
@ -312,11 +265,7 @@ connection_or_finished_flushing(or_connection_t *conn)
|
||||
assert_connection_ok(TO_CONN(conn),0);
|
||||
|
||||
switch (conn->_base.state) {
|
||||
case OR_CONN_STATE_PROXY_FLUSHING:
|
||||
log_debug(LD_OR,"finished sending CONNECT to proxy.");
|
||||
conn->_base.state = OR_CONN_STATE_PROXY_READING;
|
||||
connection_stop_writing(TO_CONN(conn));
|
||||
break;
|
||||
case OR_CONN_STATE_PROXY_HANDSHAKING:
|
||||
case OR_CONN_STATE_OPEN:
|
||||
case OR_CONN_STATE_OR_HANDSHAKING:
|
||||
connection_stop_writing(TO_CONN(conn));
|
||||
@ -334,6 +283,7 @@ connection_or_finished_flushing(or_connection_t *conn)
|
||||
int
|
||||
connection_or_finished_connecting(or_connection_t *or_conn)
|
||||
{
|
||||
int proxy_type;
|
||||
connection_t *conn;
|
||||
tor_assert(or_conn);
|
||||
conn = TO_CONN(or_conn);
|
||||
@ -343,28 +293,24 @@ connection_or_finished_connecting(or_connection_t *or_conn)
|
||||
conn->address,conn->port);
|
||||
control_event_bootstrap(BOOTSTRAP_STATUS_HANDSHAKE, 0);
|
||||
|
||||
if (get_options()->HttpsProxy) {
|
||||
char buf[1024];
|
||||
char *base64_authenticator=NULL;
|
||||
const char *authenticator = get_options()->HttpsProxyAuthenticator;
|
||||
proxy_type = PROXY_NONE;
|
||||
|
||||
if (authenticator) {
|
||||
base64_authenticator = alloc_http_authenticator(authenticator);
|
||||
if (!base64_authenticator)
|
||||
log_warn(LD_OR, "Encoding https authenticator failed");
|
||||
if (get_options()->HttpsProxy)
|
||||
proxy_type = PROXY_CONNECT;
|
||||
else if (get_options()->Socks4Proxy)
|
||||
proxy_type = PROXY_SOCKS4;
|
||||
else if (get_options()->Socks5Proxy)
|
||||
proxy_type = PROXY_SOCKS5;
|
||||
|
||||
if (proxy_type != PROXY_NONE) {
|
||||
/* start proxy handshake */
|
||||
if (connection_proxy_connect(conn, proxy_type) < 0) {
|
||||
connection_mark_for_close(conn);
|
||||
return -1;
|
||||
}
|
||||
if (base64_authenticator) {
|
||||
tor_snprintf(buf, sizeof(buf), "CONNECT %s:%d HTTP/1.1\r\n"
|
||||
"Proxy-Authorization: Basic %s\r\n\r\n",
|
||||
fmt_addr(&conn->addr),
|
||||
conn->port, base64_authenticator);
|
||||
tor_free(base64_authenticator);
|
||||
} else {
|
||||
tor_snprintf(buf, sizeof(buf), "CONNECT %s:%d HTTP/1.0\r\n\r\n",
|
||||
fmt_addr(&conn->addr), conn->port);
|
||||
}
|
||||
connection_write_to_buf(buf, strlen(buf), conn);
|
||||
conn->state = OR_CONN_STATE_PROXY_FLUSHING;
|
||||
|
||||
connection_start_reading(conn);
|
||||
conn->state = OR_CONN_STATE_PROXY_HANDSHAKING;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -753,6 +699,7 @@ connection_or_connect(const tor_addr_t *_addr, uint16_t port,
|
||||
or_connection_t *conn;
|
||||
or_options_t *options = get_options();
|
||||
int socket_error = 0;
|
||||
int using_proxy = 0;
|
||||
tor_addr_t addr;
|
||||
|
||||
tor_assert(_addr);
|
||||
@ -771,19 +718,27 @@ connection_or_connect(const tor_addr_t *_addr, uint16_t port,
|
||||
conn->_base.state = OR_CONN_STATE_CONNECTING;
|
||||
control_event_or_conn_status(conn, OR_CONN_EVENT_LAUNCHED, 0);
|
||||
|
||||
/* use a proxy server if available */
|
||||
if (options->HttpsProxy) {
|
||||
/* we shouldn't connect directly. use the https proxy instead. */
|
||||
tor_addr_from_ipv4h(&addr, options->HttpsProxyAddr);
|
||||
using_proxy = 1;
|
||||
tor_addr_copy(&addr, &options->HttpsProxyAddr);
|
||||
port = options->HttpsProxyPort;
|
||||
} else if (options->Socks4Proxy) {
|
||||
using_proxy = 1;
|
||||
tor_addr_copy(&addr, &options->Socks4ProxyAddr);
|
||||
port = options->Socks4ProxyPort;
|
||||
} else if (options->Socks5Proxy) {
|
||||
using_proxy = 1;
|
||||
tor_addr_copy(&addr, &options->Socks5ProxyAddr);
|
||||
port = options->Socks5ProxyPort;
|
||||
}
|
||||
|
||||
switch (connection_connect(TO_CONN(conn), conn->_base.address,
|
||||
&addr, port, &socket_error)) {
|
||||
case -1:
|
||||
/* If the connection failed immediately, and we're using
|
||||
* an https proxy, our https proxy is down. Don't blame the
|
||||
* Tor server. */
|
||||
if (!options->HttpsProxy)
|
||||
* a proxy, our proxy is down. Don't blame the Tor server. */
|
||||
if (!using_proxy)
|
||||
entry_guard_register_connect_status(conn->identity_digest,
|
||||
0, 1, time(NULL));
|
||||
connection_or_connect_failed(conn,
|
||||
|
@ -747,6 +747,15 @@ directory_initiate_command_rend(const char *address, const tor_addr_t *_addr,
|
||||
|
||||
log_debug(LD_DIR, "Initiating %s", dir_conn_purpose_to_string(dir_purpose));
|
||||
|
||||
/* ensure that we don't make direct connections when a SOCKS server is
|
||||
* configured. */
|
||||
if (!anonymized_connection && !use_begindir && !options->HttpProxy &&
|
||||
(options->Socks4Proxy || options->Socks5Proxy)) {
|
||||
log_warn(LD_DIR, "Cannot connect to a directory server through a "
|
||||
"SOCKS proxy!");
|
||||
return;
|
||||
}
|
||||
|
||||
conn = dir_connection_new(AF_INET);
|
||||
|
||||
/* set up conn so it's got all the data we need to remember */
|
||||
@ -772,7 +781,7 @@ directory_initiate_command_rend(const char *address, const tor_addr_t *_addr,
|
||||
/* then we want to connect to dirport directly */
|
||||
|
||||
if (options->HttpProxy) {
|
||||
tor_addr_from_ipv4h(&addr, options->HttpProxyAddr);
|
||||
tor_addr_copy(&addr, &options->HttpProxyAddr);
|
||||
dir_port = options->HttpProxyPort;
|
||||
}
|
||||
|
||||
|
58
src/or/or.h
58
src/or/or.h
@ -223,6 +223,21 @@ typedef enum {
|
||||
/* !!!! If _CONN_TYPE_MAX is ever over 15, we must grow the type field in
|
||||
* connection_t. */
|
||||
|
||||
/* Proxy client types */
|
||||
#define PROXY_NONE 0
|
||||
#define PROXY_CONNECT 1
|
||||
#define PROXY_SOCKS4 2
|
||||
#define PROXY_SOCKS5 3
|
||||
|
||||
/* Proxy client handshake states */
|
||||
#define PROXY_HTTPS_WANT_CONNECT_OK 1
|
||||
#define PROXY_SOCKS4_WANT_CONNECT_OK 2
|
||||
#define PROXY_SOCKS5_WANT_AUTH_METHOD_NONE 3
|
||||
#define PROXY_SOCKS5_WANT_AUTH_METHOD_RFC1929 4
|
||||
#define PROXY_SOCKS5_WANT_AUTH_RFC1929_OK 5
|
||||
#define PROXY_SOCKS5_WANT_CONNECT_OK 6
|
||||
#define PROXY_CONNECTED 7
|
||||
|
||||
/** True iff <b>x</b> is an edge connection. */
|
||||
#define CONN_IS_EDGE(x) \
|
||||
((x)->type == CONN_TYPE_EXIT || (x)->type == CONN_TYPE_AP)
|
||||
@ -243,26 +258,24 @@ typedef enum {
|
||||
#define _OR_CONN_STATE_MIN 1
|
||||
/** State for a connection to an OR: waiting for connect() to finish. */
|
||||
#define OR_CONN_STATE_CONNECTING 1
|
||||
/** State for a connection to an OR: waiting for proxy command to flush. */
|
||||
#define OR_CONN_STATE_PROXY_FLUSHING 2
|
||||
/** State for a connection to an OR: waiting for proxy response. */
|
||||
#define OR_CONN_STATE_PROXY_READING 3
|
||||
/** State for a connection to an OR: waiting for proxy handshake to complete */
|
||||
#define OR_CONN_STATE_PROXY_HANDSHAKING 2
|
||||
/** State for a connection to an OR or client: SSL is handshaking, not done
|
||||
* yet. */
|
||||
#define OR_CONN_STATE_TLS_HANDSHAKING 4
|
||||
#define OR_CONN_STATE_TLS_HANDSHAKING 3
|
||||
/** State for a connection to an OR: We're doing a second SSL handshake for
|
||||
* renegotiation purposes. */
|
||||
#define OR_CONN_STATE_TLS_CLIENT_RENEGOTIATING 5
|
||||
#define OR_CONN_STATE_TLS_CLIENT_RENEGOTIATING 4
|
||||
/** State for a connection at an OR: We're waiting for the client to
|
||||
* renegotiate. */
|
||||
#define OR_CONN_STATE_TLS_SERVER_RENEGOTIATING 6
|
||||
#define OR_CONN_STATE_TLS_SERVER_RENEGOTIATING 5
|
||||
/** State for a connection to an OR: We're done with our SSL handshake, but we
|
||||
* haven't yet negotiated link protocol versions and sent a netinfo cell.
|
||||
*/
|
||||
#define OR_CONN_STATE_OR_HANDSHAKING 7
|
||||
#define OR_CONN_STATE_OR_HANDSHAKING 6
|
||||
/** State for a connection to an OR: Ready to send/receive cells. */
|
||||
#define OR_CONN_STATE_OPEN 8
|
||||
#define _OR_CONN_STATE_MAX 8
|
||||
#define OR_CONN_STATE_OPEN 7
|
||||
#define _OR_CONN_STATE_MAX 7
|
||||
|
||||
#define _EXIT_CONN_STATE_MIN 1
|
||||
/** State for an exit connection: waiting for response from DNS farm. */
|
||||
@ -932,6 +945,9 @@ typedef struct connection_t {
|
||||
* connection. */
|
||||
unsigned int linked_conn_is_closed:1;
|
||||
|
||||
/** CONNECT/SOCKS proxy client handshake state (for outgoing connections). */
|
||||
unsigned int proxy_state:4;
|
||||
|
||||
/** Our socket; -1 if this connection is closed, or has no socket. */
|
||||
evutil_socket_t s;
|
||||
int conn_array_index; /**< Index into the global connection array. */
|
||||
@ -979,6 +995,7 @@ typedef struct connection_t {
|
||||
/** Unique ID for measuring tunneled network status requests. */
|
||||
uint64_t dirreq_id;
|
||||
#endif
|
||||
|
||||
} connection_t;
|
||||
|
||||
/** Stores flags and information related to the portion of a v2 Tor OR
|
||||
@ -2366,15 +2383,25 @@ typedef struct {
|
||||
char *ContactInfo; /**< Contact info to be published in the directory. */
|
||||
|
||||
char *HttpProxy; /**< hostname[:port] to use as http proxy, if any. */
|
||||
uint32_t HttpProxyAddr; /**< Parsed IPv4 addr for http proxy, if any. */
|
||||
tor_addr_t HttpProxyAddr; /**< Parsed IPv4 addr for http proxy, if any. */
|
||||
uint16_t HttpProxyPort; /**< Parsed port for http proxy, if any. */
|
||||
char *HttpProxyAuthenticator; /**< username:password string, if any. */
|
||||
|
||||
char *HttpsProxy; /**< hostname[:port] to use as https proxy, if any. */
|
||||
uint32_t HttpsProxyAddr; /**< Parsed IPv4 addr for https proxy, if any. */
|
||||
tor_addr_t HttpsProxyAddr; /**< Parsed addr for https proxy, if any. */
|
||||
uint16_t HttpsProxyPort; /**< Parsed port for https proxy, if any. */
|
||||
char *HttpsProxyAuthenticator; /**< username:password string, if any. */
|
||||
|
||||
char *Socks4Proxy;
|
||||
tor_addr_t Socks4ProxyAddr;
|
||||
uint16_t Socks4ProxyPort;
|
||||
|
||||
char *Socks5Proxy;
|
||||
tor_addr_t Socks5ProxyAddr;
|
||||
uint16_t Socks5ProxyPort;
|
||||
char *Socks5ProxyUsername;
|
||||
char *Socks5ProxyPassword;
|
||||
|
||||
/** List of configuration lines for replacement directory authorities.
|
||||
* If you just want to replace one class of authority at a time,
|
||||
* use the "Alternate*Authority" options below instead. */
|
||||
@ -2738,6 +2765,7 @@ int fetch_from_buf_http(buf_t *buf,
|
||||
int force_complete);
|
||||
int fetch_from_buf_socks(buf_t *buf, socks_request_t *req,
|
||||
int log_sockstype, int safe_socks);
|
||||
int fetch_from_buf_socks_client(buf_t *buf, int state, char **reason);
|
||||
int fetch_from_buf_line(buf_t *buf, char *data_out, size_t *data_len);
|
||||
|
||||
int peek_buf_has_control0_command(buf_t *buf);
|
||||
@ -3000,6 +3028,10 @@ void connection_expire_held_open(void);
|
||||
int connection_connect(connection_t *conn, const char *address,
|
||||
const tor_addr_t *addr,
|
||||
uint16_t port, int *socket_error);
|
||||
|
||||
int connection_proxy_connect(connection_t *conn, int type);
|
||||
int connection_read_proxy_handshake(connection_t *conn);
|
||||
|
||||
int retry_all_listeners(smartlist_t *replaced_conns,
|
||||
smartlist_t *new_conns);
|
||||
|
||||
@ -4037,6 +4069,8 @@ int tls_error_to_orconn_end_reason(int e);
|
||||
int errno_to_orconn_end_reason(int e);
|
||||
|
||||
const char *circuit_end_reason_to_control_string(int reason);
|
||||
const char *socks4_response_code_to_string(uint8_t code);
|
||||
const char *socks5_response_code_to_string(uint8_t code);
|
||||
|
||||
/********************************* relay.c ***************************/
|
||||
|
||||
|
@ -326,3 +326,47 @@ circuit_end_reason_to_control_string(int reason)
|
||||
}
|
||||
}
|
||||
|
||||
const char *
|
||||
socks4_response_code_to_string(uint8_t code)
|
||||
{
|
||||
switch (code) {
|
||||
case 0x5a:
|
||||
return "connection accepted";
|
||||
case 0x5b:
|
||||
return "server rejected connection";
|
||||
case 0x5c:
|
||||
return "server cannot connect to identd on this client";
|
||||
case 0x5d:
|
||||
return "user id does not match identd";
|
||||
default:
|
||||
return "invalid SOCKS 4 response code";
|
||||
}
|
||||
}
|
||||
|
||||
const char *
|
||||
socks5_response_code_to_string(uint8_t code)
|
||||
{
|
||||
switch (code) {
|
||||
case 0x00:
|
||||
return "connection accepted";
|
||||
case 0x01:
|
||||
return "general SOCKS server failure";
|
||||
case 0x02:
|
||||
return "connection not allowed by ruleset";
|
||||
case 0x03:
|
||||
return "Network unreachable";
|
||||
case 0x04:
|
||||
return "Host unreachable";
|
||||
case 0x05:
|
||||
return "Connection refused";
|
||||
case 0x06:
|
||||
return "TTL expired";
|
||||
case 0x07:
|
||||
return "Command not supported";
|
||||
case 0x08:
|
||||
return "Address type not supported";
|
||||
default:
|
||||
return "unknown reason";
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user