Skip to content

Commit

Permalink
tun2socks: Remove localhost limination for SOCKS5 UDP.
Browse files Browse the repository at this point in the history
The UDP socket is bound to the same IP address as was automatically selected for the TCP socket, and the port number is set to zero to let the kernel pick it. The actual bound address with the assigned port number is sent to the SOCKS server as DST.ADDR.
  • Loading branch information
ambrop72 committed Feb 1, 2020
1 parent 6241fc2 commit e837e8d
Show file tree
Hide file tree
Showing 9 changed files with 254 additions and 99 deletions.
81 changes: 47 additions & 34 deletions socks_udp_client/SocksUdpClient.c
Original file line number Diff line number Diff line change
Expand Up @@ -83,17 +83,52 @@ void socks_state_handler (struct SocksUdpClient_connection *con, int event)
DebugObject_Access(&con->client->d_obj);

switch (event) {
case BSOCKSCLIENT_EVENT_UP: {
// Figure out the localhost address.
BIPAddr localhost;
BIPAddr_InitLocalhost(&localhost, con->client->server_addr.type);
case BSOCKSCLIENT_EVENT_CONNECTED: {
// Get the local address of the SOCKS TCP connection.
BAddr tcp_local_addr;
if (!BSocksClient_GetLocalAddr(&con->socks, &tcp_local_addr)) {
BLog(BLOG_ERROR, "Failed to get TCP local address.");
return connection_free(con);
}

// Sanity check the address type (required by SetPort below).
if (tcp_local_addr.type != BADDR_TYPE_IPV4 &&
tcp_local_addr.type != BADDR_TYPE_IPV6)
{
BLog(BLOG_ERROR, "Bad address type in TCP local address.");
return connection_free(con);
}

// Bind the UDP socket to the same IP address and let the kernel pick the port.
BAddr udp_bound_addr = tcp_local_addr;
BAddr_SetPort(&udp_bound_addr, 0);
if (!BDatagram_Bind(&con->socket, udp_bound_addr)) {
BLog(BLOG_ERROR, "Failed to bind the UDP socket.");
return connection_free(con);
}

// Update udp_bound_addr to the actual address that was bound.
if (!BDatagram_GetLocalAddr(&con->socket, &udp_bound_addr)) {
BLog(BLOG_ERROR, "Failed to get UDP bound address.");
return connection_free(con);
}

// Set the DST.ADDR for SOCKS.
BSocksClient_SetDestAddr(&con->socks, udp_bound_addr);
} break;

// Get the address to send datagrams to from BSocksClient.
case BSOCKSCLIENT_EVENT_UP: {
// The remote address to send datagrams to is the BND.ADDR provided by the
// SOCKS server.
BAddr remote_addr = BSocksClient_GetBindAddr(&con->socks);

// Set the local/remote send address for BDatagram.
// Don't bother setting a source address for datagrams since we are bound.
BIPAddr local_addr;
BIPAddr_InitInvalid(&local_addr);

// Set the addresses for BDatagram.
// This will unblock the queue of outgoing packets.
BDatagram_SetSendAddrs(&con->socket, remote_addr, localhost);
BDatagram_SetSendAddrs(&con->socket, remote_addr, local_addr);
} break;

case BSOCKSCLIENT_EVENT_ERROR: {
Expand All @@ -113,10 +148,6 @@ void socks_state_handler (struct SocksUdpClient_connection *con, int event)

connection_free(con);
} break;

default: {
BLog(BLOG_ERROR, "Unknown SOCKS event");
} break;
}
}

Expand Down Expand Up @@ -294,31 +325,13 @@ struct SocksUdpClient_connection * connection_init (
goto fail2;
}

// Bind to localhost, port 0 signals the kernel to choose an open port.
BIPAddr localhost;
BIPAddr_InitLocalhost(&localhost, local_addr.type);
BAddr socket_addr = BAddr_MakeFromIpaddrAndPort(localhost, 0);
if (!BDatagram_Bind(&con->socket, socket_addr)) {
BLog(BLOG_ERROR, "Bind to localhost failed");
goto fail3;
}

// Bind succeeded, so the kernel has found an open port.
// Update socket_addr to the actual port that was bound.
uint16_t port;
if (!BDatagram_GetLocalPort(&con->socket, &port)) {
BLog(BLOG_ERROR, "Failed to get bound port");
goto fail3;
}
if (socket_addr.type == BADDR_TYPE_IPV4) {
socket_addr.ipv4.port = port;
} else {
socket_addr.ipv6.port = port;
}

// We will set the DST.ADDR for SOCKS later (BSOCKSCLIENT_EVENT_CONNECTED).
BAddr dummy_dst_addr;
BAddr_InitNone(&dummy_dst_addr);

// Initiate connection to socks server
if (!BSocksClient_Init(&con->socks, o->server_addr, o->auth_info, o->num_auth_info,
socket_addr, true, (BSocksClient_handler)socks_state_handler, con, o->reactor))
dummy_dst_addr, true, (BSocksClient_handler)socks_state_handler, con, o->reactor))
{
BLog(BLOG_ERROR, "Failed to initialize SOCKS client");
goto fail3;
Expand Down
7 changes: 1 addition & 6 deletions socks_udp_client/SocksUdpClient.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,17 +94,12 @@ struct SocksUdpClient_connection {
*
* This function only initialzies the object and does not perform network access.
*
* Currently, this function only supports connection to a SOCKS5 server that is routable from
* localhost (i.e. running on the local machine). It may be possible to add support for
* remote servers, but SOCKS5 does not support UDP if there is a NAT or firewall between the
* client and the proxy.
*
* @param o the object
* @param udp_mtu the maximum size of packets that will be sent through the tunnel
* @param max_connections how many local ports to track before dropping packets
* @param send_buf_size maximum number of buffered outgoing packets per connection
* @param keepalive_time how long to track an idle local port before forgetting it
* @param server_addr SOCKS5 server address. MUST BE ON LOCALHOST.
* @param server_addr SOCKS5 server address
* @param auth_info List of authentication info for BSocksClient. The pointer must remain
* valid while this object exists, the data is not copied.
* @param num_auth_info Number of the above.
Expand Down
121 changes: 85 additions & 36 deletions socksclient/BSocksClient.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,15 @@
#include <generated/blog_channel_BSocksClient.h>

#define STATE_CONNECTING 1
#define STATE_SENDING_HELLO 2
#define STATE_SENT_HELLO 3
#define STATE_SENDING_PASSWORD 10
#define STATE_SENT_PASSWORD 11
#define STATE_SENDING_REQUEST 4
#define STATE_SENT_REQUEST 5
#define STATE_RECEIVED_REPLY_HEADER 6
#define STATE_UP 7
#define STATE_CONNECTED_HANDLER 2
#define STATE_SENDING_HELLO 3
#define STATE_SENT_HELLO 4
#define STATE_SENDING_PASSWORD 5
#define STATE_SENT_PASSWORD 6
#define STATE_SENDING_REQUEST 7
#define STATE_SENT_REQUEST 8
#define STATE_RECEIVED_REPLY_HEADER 9
#define STATE_UP 10

static void report_error (BSocksClient *o, int error);
static void init_control_io (BSocksClient *o);
Expand All @@ -57,6 +58,7 @@ static void start_receive (BSocksClient *o, uint8_t *dest, int total);
static void do_receive (BSocksClient *o);
static void connector_handler (BSocksClient* o, int is_error);
static void connection_handler (BSocksClient* o, int event);
static void continue_job_handler (BSocksClient *o);
static void recv_handler_done (BSocksClient *o, int data_len);
static void send_handler_done (BSocksClient *o);
static void auth_finished (BSocksClient *p);
Expand Down Expand Up @@ -166,12 +168,45 @@ void connector_handler (BSocksClient* o, int is_error)
// init control I/O
init_control_io(o);

// go to STATE_CONNECTED_HANDLER and set the continue job in order to continue
// in continue_job_handler
o->state = STATE_CONNECTED_HANDLER;
BPending_Set(&o->continue_job);

// call the handler with the connected event
o->handler(o->user, BSOCKSCLIENT_EVENT_CONNECTED);
return;

fail0:
report_error(o, BSOCKSCLIENT_EVENT_ERROR);
return;
}

void connection_handler (BSocksClient* o, int event)
{
DebugObject_Access(&o->d_obj);
ASSERT(o->state != STATE_CONNECTING)

if (o->state == STATE_UP && event == BCONNECTION_EVENT_RECVCLOSED) {
report_error(o, BSOCKSCLIENT_EVENT_ERROR_CLOSED);
return;
}

report_error(o, BSOCKSCLIENT_EVENT_ERROR);
return;
}

void continue_job_handler (BSocksClient *o)
{
DebugObject_Access(&o->d_obj);
ASSERT(o->state == STATE_CONNECTED_HANDLER)

// check number of methods
if (o->num_auth_info == 0 || o->num_auth_info > 255) {
BLog(BLOG_ERROR, "invalid number of authentication methods");
goto fail1;
goto fail0;
}

// allocate buffer for sending hello
bsize_t size = bsize_add(
bsize_fromsize(sizeof(struct socks_client_hello_header)),
Expand All @@ -181,7 +216,7 @@ void connector_handler (BSocksClient* o, int is_error)
)
);
if (!reserve_buffer(o, size)) {
goto fail1;
goto fail0;
}

// write hello header
Expand All @@ -202,27 +237,10 @@ void connector_handler (BSocksClient* o, int is_error)

// set state
o->state = STATE_SENDING_HELLO;

return;

fail1:
free_control_io(o);
BConnection_Free(&o->con);
fail0:
report_error(o, BSOCKSCLIENT_EVENT_ERROR);

return;
}

void connection_handler (BSocksClient* o, int event)
{
DebugObject_Access(&o->d_obj);
ASSERT(o->state != STATE_CONNECTING)

if (o->state == STATE_UP && event == BCONNECTION_EVENT_RECVCLOSED) {
report_error(o, BSOCKSCLIENT_EVENT_ERROR_CLOSED);
return;
}

fail0:
report_error(o, BSOCKSCLIENT_EVENT_ERROR);
return;
}
Expand Down Expand Up @@ -491,8 +509,16 @@ void auth_finished (BSocksClient *o)
// allocate request buffer
bsize_t size = bsize_fromsize(sizeof(struct socks_request_header));
switch (o->dest_addr.type) {
case BADDR_TYPE_IPV4: size = bsize_add(size, bsize_fromsize(sizeof(struct socks_addr_ipv4))); break;
case BADDR_TYPE_IPV6: size = bsize_add(size, bsize_fromsize(sizeof(struct socks_addr_ipv6))); break;
case BADDR_TYPE_IPV4:
size = bsize_add(size, bsize_fromsize(sizeof(struct socks_addr_ipv4)));
break;
case BADDR_TYPE_IPV6:
size = bsize_add(size, bsize_fromsize(sizeof(struct socks_addr_ipv6)));
break;
default:
BLog(BLOG_ERROR, "Invalid dest_addr address type.");
report_error(o, BSOCKSCLIENT_EVENT_ERROR);
return;
}
if (!reserve_buffer(o, size)) {
report_error(o, BSOCKSCLIENT_EVENT_ERROR);
Expand Down Expand Up @@ -549,12 +575,11 @@ struct BSocksClient_auth_info BSocksClient_auth_password (const char *username,
return info;
}

int BSocksClient_Init (BSocksClient *o,
BAddr server_addr, const struct BSocksClient_auth_info *auth_info, size_t num_auth_info,
BAddr dest_addr, bool udp, BSocksClient_handler handler, void *user, BReactor *reactor)
int BSocksClient_Init (BSocksClient *o, BAddr server_addr,
const struct BSocksClient_auth_info *auth_info, size_t num_auth_info, BAddr dest_addr,
bool udp, BSocksClient_handler handler, void *user, BReactor *reactor)
{
ASSERT(!BAddr_IsInvalid(&server_addr))
ASSERT(dest_addr.type == BADDR_TYPE_IPV4 || dest_addr.type == BADDR_TYPE_IPV6)
#ifndef NDEBUG
for (size_t i = 0; i < num_auth_info; i++) {
ASSERT(auth_info[i].auth_type == SOCKS_METHOD_NO_AUTHENTICATION_REQUIRED ||
Expand All @@ -573,6 +598,10 @@ int BSocksClient_Init (BSocksClient *o,

// set no buffer
o->buffer = NULL;

// init continue_job
BPending_Init(&o->continue_job, BReactor_PendingGroup(o->reactor),
(BPending_handler)continue_job_handler, o);

// init connector
if (!BConnector_Init(&o->connector, server_addr, o->reactor, o, (BConnector_handler)connector_handler)) {
Expand All @@ -588,6 +617,7 @@ int BSocksClient_Init (BSocksClient *o,
return 1;

fail0:
BPending_Free(&o->continue_job);
return 0;
}

Expand All @@ -612,12 +642,31 @@ void BSocksClient_Free (BSocksClient *o)
// free connector
BConnector_Free(&o->connector);

// free continue job
BPending_Free(&o->continue_job);

// free buffer
if (o->buffer) {
BFree(o->buffer);
}
}

int BSocksClient_GetLocalAddr (BSocksClient *o, BAddr *local_addr)
{
ASSERT(o->state != STATE_CONNECTING)
DebugObject_Access(&o->d_obj);

return BConnection_GetLocalAddress(&o->con, local_addr);
}

void BSocksClient_SetDestAddr (BSocksClient *o, BAddr dest_addr)
{
ASSERT(o->state == STATE_CONNECTING || o->state == STATE_CONNECTED_HANDLER)
DebugObject_Access(&o->d_obj);

o->dest_addr = dest_addr;
}

BAddr BSocksClient_GetBindAddr (BSocksClient *o)
{
ASSERT(o->state == STATE_UP)
Expand Down
Loading

0 comments on commit e837e8d

Please sign in to comment.