mirror of
https://github.com/shadowsocks/shadowsocks-libev.git
synced 2026-10-29 04:03:45 +00:00
Refine local server_handshake
* Removed STAGE_PARSE and the 5-year-old hacky "fake" reply; * SOCKS5 server returns more error code than 0x07; * Ensure `ip` is initialized in all cases; (I guess security guys don't want to audit the local part) * Code more readable magically.
This commit is contained in:
@@ -51,7 +51,6 @@ int send_traffic_stat(uint64_t tx, uint64_t rx);
|
||||
#define STAGE_ERROR -1 /* Error detected */
|
||||
#define STAGE_INIT 0 /* Initial stage */
|
||||
#define STAGE_HANDSHAKE 1 /* Handshake with client */
|
||||
#define STAGE_PARSE 2 /* Parse the SOCKS5 header */
|
||||
#define STAGE_SNI 3 /* Parse HTTP/SNI header */
|
||||
#define STAGE_RESOLVE 4 /* Resolve the hostname */
|
||||
#define STAGE_STREAM 5 /* Stream between client and server */
|
||||
|
||||
+123
-110
@@ -277,6 +277,58 @@ delayed_connect_cb(EV_P_ ev_timer *watcher, int revents)
|
||||
server_recv_cb(EV_A_ & server->recv_ctx->io, revents);
|
||||
}
|
||||
|
||||
static int
|
||||
server_handshake_reply(EV_P_ ev_io *w, int udp_assc, struct socks5_response *response)
|
||||
{
|
||||
server_ctx_t *server_recv_ctx = (server_ctx_t *)w;
|
||||
server_t *server = server_recv_ctx->server;
|
||||
remote_t *remote = server->remote;
|
||||
if (server->stage != STAGE_HANDSHAKE) return 0;
|
||||
|
||||
struct sockaddr_in sock_addr;
|
||||
if (udp_assc) {
|
||||
socklen_t addr_len = sizeof(sock_addr);
|
||||
if (getsockname(udp_fd, (struct sockaddr *)&sock_addr, &addr_len) < 0) {
|
||||
LOGE("getsockname: %s", strerror(errno));
|
||||
response->rep = SOCKS5_REP_CONN_REFUSED;
|
||||
send(server->fd, (char *)response, sizeof(struct socks5_response), 0);
|
||||
close_and_free_remote(EV_A_ remote);
|
||||
close_and_free_server(EV_A_ server);
|
||||
return -1;
|
||||
}
|
||||
} else memset(&sock_addr, 0, sizeof(sock_addr));
|
||||
|
||||
buffer_t resp_to_send;
|
||||
buffer_t *resp_buf = &resp_to_send;
|
||||
balloc(resp_buf, BUF_SIZE);
|
||||
|
||||
memcpy(resp_buf->data, response, sizeof(struct socks5_response));
|
||||
memcpy(resp_buf->data + sizeof(struct socks5_response),
|
||||
&sock_addr.sin_addr, sizeof(sock_addr.sin_addr));
|
||||
memcpy(resp_buf->data + sizeof(struct socks5_response) +
|
||||
sizeof(sock_addr.sin_addr),
|
||||
&sock_addr.sin_port, sizeof(sock_addr.sin_port));
|
||||
|
||||
int reply_size = sizeof(struct socks5_response) +
|
||||
sizeof(sock_addr.sin_addr) + sizeof(sock_addr.sin_port);
|
||||
|
||||
int s = send(server->fd, resp_buf->data, reply_size, 0);
|
||||
|
||||
bfree(resp_buf);
|
||||
|
||||
if (s < reply_size) {
|
||||
LOGE("failed to send fake reply");
|
||||
close_and_free_remote(EV_A_ remote);
|
||||
close_and_free_server(EV_A_ server);
|
||||
return -1;
|
||||
}
|
||||
if (udp_assc) {
|
||||
// Wait until client closes the connection
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
server_handshake(EV_P_ ev_io *w, buffer_t *buf)
|
||||
{
|
||||
@@ -286,30 +338,25 @@ server_handshake(EV_P_ ev_io *w, buffer_t *buf)
|
||||
|
||||
struct socks5_request *request = (struct socks5_request *)buf->data;
|
||||
size_t request_len = sizeof(struct socks5_request);
|
||||
struct sockaddr_in sock_addr;
|
||||
memset(&sock_addr, 0, sizeof(sock_addr));
|
||||
|
||||
if (buf->len < request_len) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int udp_assc = 0;
|
||||
struct socks5_response response;
|
||||
response.ver = SVERSION;
|
||||
response.rep = SOCKS5_REP_SUCCEEDED;
|
||||
response.rsv = 0;
|
||||
response.atyp = SOCKS5_ATYP_IPV4;
|
||||
|
||||
if (request->cmd == 3) {
|
||||
udp_assc = 1;
|
||||
socklen_t addr_len = sizeof(sock_addr);
|
||||
getsockname(udp_fd, (struct sockaddr *)&sock_addr,
|
||||
&addr_len);
|
||||
if (request->cmd == SOCKS5_CMD_UDP_ASSOCIATE) {
|
||||
if (verbose) {
|
||||
LOGI("udp assc request accepted");
|
||||
}
|
||||
} else if (request->cmd != 1) {
|
||||
return server_handshake_reply(EV_A_ w, 1, &response);
|
||||
} else if (request->cmd != SOCKS5_CMD_CONNECT) {
|
||||
LOGE("unsupported cmd: %d", request->cmd);
|
||||
struct socks5_response response;
|
||||
response.ver = SVERSION;
|
||||
response.rep = CMD_NOT_SUPPORTED;
|
||||
response.rsv = 0;
|
||||
response.atyp = 1;
|
||||
response.rep = SOCKS5_REP_CMD_NOT_SUPPORTED;
|
||||
char *send_buf = (char *)&response;
|
||||
send(server->fd, send_buf, 4, 0);
|
||||
close_and_free_remote(EV_A_ remote);
|
||||
@@ -317,46 +364,6 @@ server_handshake(EV_P_ ev_io *w, buffer_t *buf)
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Fake reply
|
||||
if (server->stage == STAGE_HANDSHAKE) {
|
||||
struct socks5_response response;
|
||||
response.ver = SVERSION;
|
||||
response.rep = 0;
|
||||
response.rsv = 0;
|
||||
response.atyp = 1;
|
||||
|
||||
buffer_t resp_to_send;
|
||||
buffer_t *resp_buf = &resp_to_send;
|
||||
balloc(resp_buf, BUF_SIZE);
|
||||
|
||||
memcpy(resp_buf->data, &response, sizeof(struct socks5_response));
|
||||
memcpy(resp_buf->data + sizeof(struct socks5_response),
|
||||
&sock_addr.sin_addr, sizeof(sock_addr.sin_addr));
|
||||
memcpy(resp_buf->data + sizeof(struct socks5_response) +
|
||||
sizeof(sock_addr.sin_addr),
|
||||
&sock_addr.sin_port, sizeof(sock_addr.sin_port));
|
||||
|
||||
int reply_size = sizeof(struct socks5_response) +
|
||||
sizeof(sock_addr.sin_addr) + sizeof(sock_addr.sin_port);
|
||||
|
||||
int s = send(server->fd, resp_buf->data, reply_size, 0);
|
||||
|
||||
bfree(resp_buf);
|
||||
|
||||
if (s < reply_size) {
|
||||
LOGE("failed to send fake reply");
|
||||
close_and_free_remote(EV_A_ remote);
|
||||
close_and_free_server(EV_A_ server);
|
||||
return -1;
|
||||
}
|
||||
if (udp_assc) {
|
||||
// Wait until client closes the connection
|
||||
return -1;
|
||||
}
|
||||
|
||||
server->stage = STAGE_PARSE;
|
||||
}
|
||||
|
||||
char host[257], ip[INET6_ADDRSTRLEN], port[16];
|
||||
|
||||
buffer_t *abuf = server->abuf;
|
||||
@@ -367,8 +374,7 @@ server_handshake(EV_P_ ev_io *w, buffer_t *buf)
|
||||
int atyp = request->atyp;
|
||||
|
||||
// get remote addr and port
|
||||
if (atyp == 1) {
|
||||
// IP V4
|
||||
if (atyp == SOCKS5_ATYP_IPV4) {
|
||||
size_t in_addr_len = sizeof(struct in_addr);
|
||||
if (buf->len < request_len + in_addr_len + 2) {
|
||||
return -1;
|
||||
@@ -378,12 +384,18 @@ server_handshake(EV_P_ ev_io *w, buffer_t *buf)
|
||||
|
||||
if (acl || verbose) {
|
||||
uint16_t p = ntohs(*(uint16_t *)(buf->data + request_len + in_addr_len));
|
||||
inet_ntop(AF_INET, (const void *)(buf->data + request_len),
|
||||
ip, INET_ADDRSTRLEN);
|
||||
if (!inet_ntop(AF_INET, (const void *)(buf->data + request_len),
|
||||
ip, INET_ADDRSTRLEN)) {
|
||||
LOGI("inet_ntop(AF_INET): %s", strerror(errno));
|
||||
ip[0] = '\0';
|
||||
}
|
||||
sprintf(port, "%d", p);
|
||||
}
|
||||
} else if (atyp == 3) {
|
||||
// Domain name
|
||||
} else if (atyp == SOCKS5_ATYP_DOMAIN
|
||||
#ifdef __ANDROID__
|
||||
&& !vpn // protecting DNS packets isn't supported yet
|
||||
#endif
|
||||
) {
|
||||
uint8_t name_len = *(uint8_t *)(buf->data + request_len);
|
||||
if (buf->len < request_len + 1 + name_len + 2) {
|
||||
return -1;
|
||||
@@ -399,8 +411,7 @@ server_handshake(EV_P_ ev_io *w, buffer_t *buf)
|
||||
host[name_len] = '\0';
|
||||
sprintf(port, "%d", p);
|
||||
}
|
||||
} else if (atyp == 4) {
|
||||
// IP V6
|
||||
} else if (atyp == SOCKS5_ATYP_IPV6) {
|
||||
size_t in6_addr_len = sizeof(struct in6_addr);
|
||||
if (buf->len < request_len + in6_addr_len + 2) {
|
||||
return -1;
|
||||
@@ -410,12 +421,18 @@ server_handshake(EV_P_ ev_io *w, buffer_t *buf)
|
||||
|
||||
if (acl || verbose) {
|
||||
uint16_t p = ntohs(*(uint16_t *)(buf->data + request_len + in6_addr_len));
|
||||
inet_ntop(AF_INET6, (const void *)(buf->data + request_len),
|
||||
ip, INET6_ADDRSTRLEN);
|
||||
if (!inet_ntop(AF_INET6, (const void *)(buf->data + request_len),
|
||||
ip, INET6_ADDRSTRLEN)) {
|
||||
LOGI("inet_ntop(AF_INET6): %s", strerror(errno));
|
||||
ip[0] = '\0';
|
||||
}
|
||||
sprintf(port, "%d", p);
|
||||
}
|
||||
} else {
|
||||
LOGE("unsupported addrtype: %d", request->atyp);
|
||||
response.rep = SOCKS5_REP_ADDRTYPE_NOT_SUPPORTED;
|
||||
char *send_buf = (char *)&response;
|
||||
send(server->fd, send_buf, 4, 0);
|
||||
close_and_free_remote(EV_A_ remote);
|
||||
close_and_free_server(EV_A_ server);
|
||||
return -1;
|
||||
@@ -428,7 +445,7 @@ server_handshake(EV_P_ ev_io *w, buffer_t *buf)
|
||||
char *hostname;
|
||||
uint16_t dst_port = ntohs(*(uint16_t *)(abuf->data + abuf->len - 2));
|
||||
|
||||
if (atyp == 1 || atyp == 4) {
|
||||
if (atyp == SOCKS5_ATYP_IPV4 || atyp == SOCKS5_ATYP_IPV6) {
|
||||
if (dst_port == http_protocol->default_port)
|
||||
ret = http_protocol->parse_packet(buf->data + 3 + abuf->len,
|
||||
buf->len - 3 - abuf->len, &hostname);
|
||||
@@ -436,6 +453,7 @@ server_handshake(EV_P_ ev_io *w, buffer_t *buf)
|
||||
ret = tls_protocol->parse_packet(buf->data + 3 + abuf->len,
|
||||
buf->len - 3 - abuf->len, &hostname);
|
||||
if (ret == -1 && buf->len < BUF_SIZE && server->stage != STAGE_SNI) {
|
||||
if (server_handshake_reply(EV_A_ w, 0, &response) < 0) return -1;
|
||||
server->stage = STAGE_SNI;
|
||||
ev_timer_start(EV_A_ & server->delayed_connect_watcher);
|
||||
return -1;
|
||||
@@ -449,6 +467,7 @@ server_handshake(EV_P_ ev_io *w, buffer_t *buf)
|
||||
}
|
||||
}
|
||||
|
||||
if (server_handshake_reply(EV_A_ w, 0, &response) < 0) return -1;
|
||||
server->stage = STAGE_STREAM;
|
||||
|
||||
buf->len -= (3 + abuf_len);
|
||||
@@ -457,11 +476,11 @@ server_handshake(EV_P_ ev_io *w, buffer_t *buf)
|
||||
}
|
||||
|
||||
if (verbose) {
|
||||
if (sni_detected || atyp == 3)
|
||||
if (sni_detected || atyp == SOCKS5_ATYP_DOMAIN)
|
||||
LOGI("connect to %s:%s", host, port);
|
||||
else if (atyp == 1)
|
||||
else if (atyp == SOCKS5_ATYP_IPV4)
|
||||
LOGI("connect to %s:%s", ip, port);
|
||||
else if (atyp == 4)
|
||||
else if (atyp == SOCKS5_ATYP_IPV6)
|
||||
LOGI("connect to [%s]:%s", ip, port);
|
||||
}
|
||||
|
||||
@@ -471,15 +490,13 @@ server_handshake(EV_P_ ev_io *w, buffer_t *buf)
|
||||
#endif
|
||||
) {
|
||||
int bypass = 0;
|
||||
#ifndef __ANDROID__
|
||||
int resolved = 0;
|
||||
#endif
|
||||
struct sockaddr_storage storage;
|
||||
memset(&storage, 0, sizeof(struct sockaddr_storage));
|
||||
int err;
|
||||
|
||||
int host_match = 0;
|
||||
if (sni_detected || atyp == 3)
|
||||
if (sni_detected || atyp == SOCKS5_ATYP_DOMAIN)
|
||||
host_match = acl_match_host(host);
|
||||
|
||||
if (host_match > 0)
|
||||
@@ -487,59 +504,55 @@ server_handshake(EV_P_ ev_io *w, buffer_t *buf)
|
||||
else if (host_match < 0)
|
||||
bypass = 0; // proxy hostnames in white list
|
||||
else {
|
||||
#ifndef __ANDROID__
|
||||
if (atyp == 3) { // resolve domain so we can bypass domain with geoip
|
||||
err = get_sockaddr(host, port, &storage, 0, ipv6first);
|
||||
if (err != -1) {
|
||||
resolved = 1;
|
||||
switch (((struct sockaddr *)&storage)->sa_family) {
|
||||
case AF_INET:
|
||||
{
|
||||
struct sockaddr_in *addr_in = (struct sockaddr_in *)&storage;
|
||||
inet_ntop(AF_INET, &(addr_in->sin_addr), ip, INET_ADDRSTRLEN);
|
||||
break;
|
||||
}
|
||||
case AF_INET6:
|
||||
{
|
||||
struct sockaddr_in6 *addr_in6 = (struct sockaddr_in6 *)&storage;
|
||||
inet_ntop(AF_INET6, &(addr_in6->sin6_addr), ip, INET6_ADDRSTRLEN);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (atyp == SOCKS5_ATYP_DOMAIN) { // resolve domain so we can bypass domain with geoip
|
||||
if (get_sockaddr(host, port, &storage, 0, ipv6first)) goto not_bypass;
|
||||
resolved = 1;
|
||||
switch (((struct sockaddr *)&storage)->sa_family) {
|
||||
case AF_INET:
|
||||
{
|
||||
struct sockaddr_in *addr_in = (struct sockaddr_in *)&storage;
|
||||
if (!inet_ntop(AF_INET, &(addr_in->sin_addr), ip, INET_ADDRSTRLEN)) goto not_bypass;
|
||||
break;
|
||||
}
|
||||
case AF_INET6:
|
||||
{
|
||||
struct sockaddr_in6 *addr_in6 = (struct sockaddr_in6 *)&storage;
|
||||
if (!inet_ntop(AF_INET6, &(addr_in6->sin6_addr), ip, INET6_ADDRSTRLEN)) goto not_bypass;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
goto not_bypass;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
int ip_match = acl_match_host(ip);
|
||||
switch (get_acl_mode()) {
|
||||
case BLACK_LIST:
|
||||
if (ip_match > 0)
|
||||
bypass = 1; // bypass IPs in black list
|
||||
break;
|
||||
case WHITE_LIST:
|
||||
bypass = 1;
|
||||
if (ip_match < 0)
|
||||
bypass = 0; // proxy IPs in white list
|
||||
break;
|
||||
if (resolved) {
|
||||
int ip_match = acl_match_host(ip);
|
||||
switch (get_acl_mode()) {
|
||||
case BLACK_LIST:
|
||||
if (ip_match > 0)
|
||||
bypass = 1; // bypass IPs in black list
|
||||
break;
|
||||
case WHITE_LIST:
|
||||
bypass = 1;
|
||||
if (ip_match < 0)
|
||||
bypass = 0; // proxy IPs in white list
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bypass) {
|
||||
if (verbose) {
|
||||
if (sni_detected || atyp == 3)
|
||||
if (sni_detected || atyp == SOCKS5_ATYP_DOMAIN)
|
||||
LOGI("bypass %s:%s", host, port);
|
||||
else if (atyp == 1)
|
||||
LOGI("bypass %s:%s", ip, port);
|
||||
else if (atyp == 4)
|
||||
LOGI("bypass [%s]:%s", ip, port);
|
||||
}
|
||||
#ifndef __ANDROID__
|
||||
if (atyp == 3 && resolved != 1)
|
||||
if (atyp == SOCKS5_ATYP_DOMAIN && !resolved)
|
||||
err = get_sockaddr(host, port, &storage, 0, ipv6first);
|
||||
else
|
||||
#endif
|
||||
err = get_sockaddr(ip, port, &storage, 0, ipv6first);
|
||||
err = get_sockaddr(ip, port, &storage, 0, ipv6first);
|
||||
if (err != -1) {
|
||||
remote = create_remote(server->listener, (struct sockaddr *)&storage);
|
||||
if (remote != NULL)
|
||||
@@ -548,6 +561,7 @@ server_handshake(EV_P_ ev_io *w, buffer_t *buf)
|
||||
}
|
||||
}
|
||||
|
||||
not_bypass:
|
||||
// Not bypass
|
||||
if (remote == NULL) {
|
||||
remote = create_remote(server->listener, NULL);
|
||||
@@ -897,7 +911,6 @@ server_recv_cb(EV_P_ ev_io *w, int revents)
|
||||
buf->len = 0;
|
||||
return;
|
||||
} else if (server->stage == STAGE_HANDSHAKE ||
|
||||
server->stage == STAGE_PARSE ||
|
||||
server->stage == STAGE_SNI) {
|
||||
int ret = server_handshake(EV_A_ w, buf);
|
||||
if (ret)
|
||||
|
||||
+20
-5
@@ -24,13 +24,28 @@
|
||||
#define _SOCKS5_H
|
||||
|
||||
#define SVERSION 0x05
|
||||
#define CONNECT 0x01
|
||||
#define IPV4 0x01
|
||||
#define DOMAIN 0x03
|
||||
#define IPV6 0x04
|
||||
#define METHOD_NOAUTH 0x00
|
||||
#define METHOD_UNACCEPTABLE 0xff
|
||||
#define CMD_NOT_SUPPORTED 0x07
|
||||
|
||||
// see also: https://www.ietf.org/rfc/rfc1928.txt
|
||||
#define SOCKS5_CMD_CONNECT 0x01
|
||||
#define SOCKS5_CMD_BIND 0x02
|
||||
#define SOCKS5_CMD_UDP_ASSOCIATE 0x02
|
||||
|
||||
#define SOCKS5_ATYP_IPV4 0x01
|
||||
#define SOCKS5_ATYP_DOMAIN 0x03
|
||||
#define SOCKS5_ATYP_IPV6 0x04
|
||||
|
||||
#define SOCKS5_REP_SUCCEEDED 0x00
|
||||
#define SOCKS5_REP_GENERAL 0x01
|
||||
#define SOCKS5_REP_CONN_DISALLOWED 0x02
|
||||
#define SOCKS5_REP_NETWORK_UNREACHABLE 0x03
|
||||
#define SOCKS5_REP_HOST_UNREACHABLE 0x04
|
||||
#define SOCKS5_REP_CONN_REFUSED 0x05
|
||||
#define SOCKS5_REP_TTL_EXPIRED 0x06
|
||||
#define SOCKS5_REP_CMD_NOT_SUPPORTED 0x07
|
||||
#define SOCKS5_REP_ADDRTYPE_NOT_SUPPORTED 0x08
|
||||
#define SOCKS5_REP_FF_UNASSIGNED 0x09
|
||||
|
||||
struct method_select_request {
|
||||
unsigned char ver;
|
||||
|
||||
Reference in New Issue
Block a user