Skip to content

Commit

Permalink
Many improvements to socks5 support
Browse files Browse the repository at this point in the history
  • Loading branch information
semigodking committed May 28, 2024
1 parent 92dbff0 commit c8e1e6c
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 48 deletions.
135 changes: 87 additions & 48 deletions socks5-udp.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@

typedef struct socks5_expected_assoc_reply_t {
socks5_reply h;
socks5_addr_ipv4 ip;
union {
socks5_addr_ipv4 v4;
socks5_addr_ipv6 v6;
};
} PACKED socks5_expected_assoc_reply;

static struct evbuffer* socks5_mkmethods_plain_wrapper(void *p)
Expand All @@ -48,10 +51,7 @@ static struct evbuffer* socks5_mkpassword_plain_wrapper(void *p)

static struct evbuffer* socks5_mkassociate(void *p)
{
struct sockaddr_storage sa;
memset(&sa, 0, sizeof(sa));
sa.ss_family = ((const struct sockaddr_storage *)p)->ss_family;
return socks5_mkcommand_plain(socks5_cmd_udp_associate, &sa);
return socks5_mkcommand_plain(socks5_cmd_udp_associate, p);
}

static void socks5_fill_preamble(
Expand Down Expand Up @@ -85,7 +85,7 @@ static void socks5_fill_preamble(

typedef struct socks5_client_t {
struct event udprelay;
struct sockaddr_in udprelayaddr;
struct sockaddr_storage udprelayaddr;
struct bufferevent *relay;
int ready_fwd;
} socks5_client;
Expand Down Expand Up @@ -129,9 +129,9 @@ static void socks5_forward_pkt(redudp_client *client, struct sockaddr *destaddr,
struct iovec io[2];
size_t preamble_len = 0;

if (socks5client->udprelayaddr.sin_family != AF_INET && socks5client->udprelayaddr.sin_family != AF_INET6) {
if (socks5client->udprelayaddr.ss_family != AF_INET && socks5client->udprelayaddr.ss_family != AF_INET6) {
redudp_log_errno(client, LOG_WARNING, "Unknown address type %d",
socks5client->udprelayaddr.sin_family);
socks5client->udprelayaddr.ss_family);
return;
}

Expand Down Expand Up @@ -192,9 +192,8 @@ static void socks5_pkt_from_socks(int fd, short what, void *_arg)
return;
}

if (pkt->header.addrtype != socks5_addrtype_ipv4) {
redudp_log_error(client, LOG_NOTICE, "Got address type #%u instead of expected #%u (IPv4).",
pkt->header.addrtype, socks5_addrtype_ipv4);
if (pkt->header.addrtype != socks5_addrtype_ipv4 && pkt->header.addrtype != socks5_addrtype_ipv6) {
redudp_log_error(client, LOG_NOTICE, "Got address type #%u.", pkt->header.addrtype);
return;
}

Expand Down Expand Up @@ -222,44 +221,86 @@ static void socks5_pkt_from_socks(int fd, short what, void *_arg)
}


static size_t calc_assoc_reply_size(int ss_family) {
size_t reply_size = sizeof(socks5_reply);
if (ss_family == AF_INET)
reply_size += sizeof(socks5_addr_ipv4);
else if (ss_family == AF_INET6)
reply_size += sizeof(socks5_addr_ipv6);
return reply_size;
}

static void socks5_read_assoc_reply(struct bufferevent *buffev, void *_arg)
{
redudp_client *client = _arg;
socks5_client *socks5client = (void*)(client + 1);
socks5_expected_assoc_reply reply;
int read = evbuffer_remove(bufferevent_get_input(buffev), &reply, sizeof(reply));
struct evbuffer * input = bufferevent_get_input(buffev);
int fd = -1;
int error;
redudp_log_error(client, LOG_DEBUG, "<trace>");
size_t max_reply_size = calc_assoc_reply_size(AF_INET6);

// Inspect reply code
{
socks5_reply * reply = (socks5_reply *)evbuffer_pullup(input, -1);
size_t data_size = evbuffer_get_length(input);

if (data_size < sizeof(socks5_reply)) {
// Wait for more data
bufferevent_setwatermark(buffev, EV_READ, sizeof(socks5_reply), max_reply_size);
return;
}
if (reply->ver != socks5_ver) {
redudp_log_error(client, LOG_NOTICE, "Socks5 server reported unexpected reply version: %u", reply->ver);
goto fail;
}
if (reply->status != socks5_status_succeeded) {
redudp_log_error(client, LOG_NOTICE, "Socks5 server status: \"%s\" (%i)",
socks5_status_to_str(reply->status), reply->status);
goto fail;
}
if (reply->addrtype == socks5_addrtype_ipv4)
max_reply_size = calc_assoc_reply_size(AF_INET);
else if (reply->addrtype == socks5_addrtype_ipv6)
max_reply_size = calc_assoc_reply_size(AF_INET6);
else {
redudp_log_error(client, LOG_NOTICE, "Socks5 server replies bad address type: %d", reply->addrtype);
goto fail;
}

if (read != sizeof(reply)) {
redudp_log_errno(client, LOG_NOTICE, "evbuffer_remove returned only %i bytes instead of expected %zu",
read, sizeof(reply));
goto fail;
if (data_size < max_reply_size) {
// Wait for more data
bufferevent_setwatermark(buffev, EV_READ, max_reply_size, max_reply_size);
return;
}
}
// Enough data received
socks5_expected_assoc_reply reply;
int read = evbuffer_remove(bufferevent_get_input(buffev), &reply, max_reply_size);
redudp_log_error(client, LOG_DEBUG, "<trace>");

if (reply.h.ver != socks5_ver) {
redudp_log_error(client, LOG_NOTICE, "Socks5 server reported unexpected reply version: %u", reply.h.ver);
if (read != max_reply_size) {
// Should never occur
redudp_log_errno(client, LOG_NOTICE, "evbuffer_remove returned only %i bytes instead of expected %zu",
read, max_reply_size);
goto fail;
}

if (reply.h.status != socks5_status_succeeded) {
redudp_log_error(client, LOG_NOTICE, "Socks5 server status: \"%s\" (%i)",
socks5_status_to_str(reply.h.status), reply.h.status);
goto fail;
// Use relay address instead of address in reply.
// Unless server allocates different IP for UDP association,
// this should work.
// Use port number from UDP association reply as destination
// port.
memcpy(&socks5client->udprelayaddr,
&client->instance->config.relayaddr,
sizeof(struct sockaddr_storage));
if (reply.h.addrtype == socks5_addrtype_ipv4) {
set_sockaddr_port(&socks5client->udprelayaddr, reply.v4.port);
}

if (reply.h.addrtype != socks5_addrtype_ipv4) {
redudp_log_error(client, LOG_NOTICE, "Socks5 server reported unexpected address type for UDP dgram destination: %u",
reply.h.addrtype);
goto fail;
else if (reply.h.addrtype == socks5_addrtype_ipv6) {
set_sockaddr_port(&socks5client->udprelayaddr, reply.v6.port);
}

socks5client->udprelayaddr.sin_family = AF_INET;
socks5client->udprelayaddr.sin_port = reply.ip.port;
socks5client->udprelayaddr.sin_addr.s_addr = reply.ip.addr;

fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
fd = socket(socks5client->udprelayaddr.ss_family, SOCK_DGRAM, IPPROTO_UDP);
if (fd == -1) {
redudp_log_errno(client, LOG_ERR, "socket");
goto fail;
Expand All @@ -271,7 +312,6 @@ static void socks5_read_assoc_reply(struct bufferevent *buffev, void *_arg)
goto fail;
}


error = connect(fd, (struct sockaddr*)&socks5client->udprelayaddr, sizeof(socks5client->udprelayaddr));
if (error) {
redudp_log_errno(client, LOG_NOTICE, "connect");
Expand All @@ -288,7 +328,6 @@ static void socks5_read_assoc_reply(struct bufferevent *buffev, void *_arg)
socks5client->ready_fwd = 1;
redudp_flush_queue(client);
// TODO: bufferevent_disable ?

return;

fail:
Expand Down Expand Up @@ -318,9 +357,10 @@ static void socks5_read_auth_reply(struct bufferevent *buffev, void *_arg)
goto fail;
}

size_t reply_size = calc_assoc_reply_size(client->instance->config.relayaddr.ss_family);
error = redsocks_write_helper_ex_plain(
socks5client->relay, NULL, socks5_mkassociate, &client->destaddr, 0,
sizeof(socks5_expected_assoc_reply), sizeof(socks5_expected_assoc_reply));
sizeof(socks5_reply), reply_size);
if (error)
goto fail;

Expand All @@ -341,7 +381,7 @@ static void socks5_read_auth_methods(struct bufferevent *buffev, void *_arg)
int read = evbuffer_remove(bufferevent_get_input(buffev), &reply, sizeof(reply));
const char *error = NULL;
int ierror = 0;
redudp_log_error(client, LOG_DEBUG, "<trace>");
redudp_log_error(client, LOG_DEBUG, "do_password: %d", do_password);

if (read != sizeof(reply)) {
redudp_log_errno(client, LOG_NOTICE, "evbuffer_remove returned only %i bytes instead of expected %zu",
Expand All @@ -355,12 +395,12 @@ static void socks5_read_auth_methods(struct bufferevent *buffev, void *_arg)
goto fail;
}
else if (reply.method == socks5_auth_none) {
size_t reply_size = calc_assoc_reply_size(client->instance->config.relayaddr.ss_family);
ierror = redsocks_write_helper_ex_plain(
socks5client->relay, NULL, socks5_mkassociate, &client->destaddr, 0,
sizeof(socks5_expected_assoc_reply), sizeof(socks5_expected_assoc_reply));
sizeof(socks5_reply), reply_size);
if (ierror)
goto fail;

replace_readcb(socks5client->relay, socks5_read_assoc_reply);
}
else if (reply.method == socks5_auth_password) {
Expand All @@ -369,7 +409,6 @@ static void socks5_read_auth_methods(struct bufferevent *buffev, void *_arg)
sizeof(socks5_auth_reply), sizeof(socks5_auth_reply));
if (ierror)
goto fail;

replace_readcb(socks5client->relay, socks5_read_auth_reply);
}

Expand Down Expand Up @@ -421,13 +460,13 @@ static void socks5_connect_relay(redudp_client *client)
{
socks5_client *socks5client = (void*)(client + 1);
socks5client->relay = red_connect_relay(
NULL,
&client->instance->config.relayaddr,
NULL,
socks5_relay_connected,
socks5_relay_error,
client,
NULL);
NULL,
&client->instance->config.relayaddr,
NULL,
socks5_relay_connected,
socks5_relay_error,
client,
NULL);
if (!socks5client->relay)
redudp_drop_client(client);
}
Expand Down
11 changes: 11 additions & 0 deletions utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -620,4 +620,15 @@ int resolve_hostname(const char *hostname, int sa_family, struct sockaddr *addr)
}
}

void set_sockaddr_port(struct sockaddr_storage * addr, uint16_t port) {
if (addr->ss_family == AF_INET) {
((struct sockaddr_in *)addr)->sin_port = port;
}
else if (addr->ss_family == AF_INET6) {
((struct sockaddr_in6 *)addr)->sin6_port = port;
}
else {
log_error(LOG_ERR, "Unknown address type: %d", addr->ss_family);
}
}
/* vim:set tabstop=4 softtabstop=4 shiftwidth=4: */
1 change: 1 addition & 0 deletions utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ void replace_eventcb(struct bufferevent * buffev, bufferevent_event_cb eventcb);
#endif
char *red_inet_ntop(const struct sockaddr_storage * sa, char* buffer, size_t buffer_size);
int resolve_hostname(const char *hostname, int sa_family, struct sockaddr *addr);
void set_sockaddr_port(struct sockaddr_storage * addr, uint16_t port);

/* vim:set tabstop=4 softtabstop=4 shiftwidth=4: */
/* vim:set foldmethod=marker foldlevel=32 foldmarker={,}: */
Expand Down

0 comments on commit c8e1e6c

Please sign in to comment.