From d78b147995f7a4cf506fb8eaa43fb9e13cbb4576 Mon Sep 17 00:00:00 2001 From: Ambroz Bizjak Date: Sat, 25 May 2019 20:41:17 +0200 Subject: [PATCH 01/11] Remove obsolete BUILD-WINDOWS.md. --- BUILD-WINDOWS.md | 23 ----------------------- 1 file changed, 23 deletions(-) delete mode 100644 BUILD-WINDOWS.md diff --git a/BUILD-WINDOWS.md b/BUILD-WINDOWS.md deleted file mode 100644 index 830267c1e..000000000 --- a/BUILD-WINDOWS.md +++ /dev/null @@ -1,23 +0,0 @@ -To build on Windows, you need Visual Studio 2015 or 2017 and CMake. - -If you only want to build tun2socks, then no additional dependencies are needed, but pass `-DBUILD_NOTHING_BY_DEFAULT=1 -DBUILD_TUN2SOCKS=1` options to the CMake command line (see below). If the VPN system components are needed, then you will need to build OpenSSL and NSS yourself and we provide no instructions for that. - -Create a build directory (either in or outside the source directory). - -Open a command line and navigate to the build directory. - -Run CMake: - -``` -cmake "" -G "" -DCMAKE_INSTALL_PREFIX="" [options...] -``` - -See [here](https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html) for the list of generators, for example you may want "Visual Studio 15 2017 Win64" for a 64-build with VS2017. - -Once cmake is successful, build with the following command: - -``` -cmake --build . --target install -``` - -If successful you will have the binaries in `/bin`. From 0f37b52fd42a8ef0f38fe1a69531695eea88f08e Mon Sep 17 00:00:00 2001 From: Mygod Date: Tue, 20 Aug 2019 09:52:27 +0800 Subject: [PATCH 02/11] Cherry pick http://git.savannah.nongnu.org/cgit/lwip.git/tree/src/core/tcp_out.c?id=04b983b4f376697f04980b8d547fa4d03aa10600 --- lwip/src/core/tcp_out.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lwip/src/core/tcp_out.c b/lwip/src/core/tcp_out.c index 58fdaac36..5f7bc1703 100644 --- a/lwip/src/core/tcp_out.c +++ b/lwip/src/core/tcp_out.c @@ -1875,12 +1875,18 @@ tcp_split_unsent_seg(struct tcp_pcb *pcb, u16_t split) seg->flags |= TF_SEG_DATA_CHECKSUMMED; #endif /* TCP_CHECKSUM_ON_COPY */ + /* Remove this segment from the queue since trimming it may free pbufs */ + pcb->snd_queuelen -= pbuf_clen(useg->p); + /* Trim the original pbuf into our split size. At this point our remainder segment must be setup successfully because we are modifying the original segment */ pbuf_realloc(useg->p, useg->p->tot_len - remainder); useg->len -= remainder; TCPH_SET_FLAG(useg->tcphdr, split_flags); + /* Add back to the queue with new trimmed pbuf */ + pcb->snd_queuelen += pbuf_clen(useg->p); + #if TCP_CHECKSUM_ON_COPY /* The checksum on the split segment is now incorrect. We need to re-run it over the split */ useg->chksum = 0; @@ -1904,7 +1910,7 @@ tcp_split_unsent_seg(struct tcp_pcb *pcb, u16_t split) /* Update number of segments on the queues. Note that length now may * exceed TCP_SND_QUEUELEN! We don't have to touch pcb->snd_buf * because the total amount of data is constant when packet is split */ - pcb->snd_queuelen++; + pcb->snd_queuelen += pbuf_clen(seg->p); /* Finally insert remainder into queue after split (which stays head) */ seg->next = useg->next; From ae4edfbf3e047926bb329c8603e83aa30425cc0c Mon Sep 17 00:00:00 2001 From: Ben Schwartz Date: Fri, 14 Dec 2018 10:35:40 -0500 Subject: [PATCH 03/11] Implement optional support for SOCKS5-UDP This change adds a new option, --socks5-udp. If this option is present, and no UDP gateway is specified, UDP packets will no longer be dropped. Instead, the client will use the SOCKS5 UDP ASSOCIATE command to route UDP packets through the proxy server. This implementation is intended for use with any UDP data, and it includes an optimization for packets containing DNS queries. However, this implementation is currently limited to localhost SOCKS5 servers. SOCKS5-UDP does not perform well over actual network links, as it requires several roundtrips to the server and is not compatible with NAT. This implementation is currently in use in a fork of tun2socks used by Outline (https://getoutline.org) and Intra (https://getintra.org). Fixes https://github.com/ambrop72/badvpn/issues/56 --- CMakeLists.txt | 1 + blog_channels.txt | 1 + generated/blog_channel_SocksUdpClient.h | 4 + generated/blog_channels_defines.h | 115 +++--- generated/blog_channels_list.h | 1 + misc/socks_proto.h | 10 +- socks_udp_client/CMakeLists.txt | 1 + socks_udp_client/SocksUdpClient.c | 529 ++++++++++++++++++++++++ socks_udp_client/SocksUdpClient.h | 134 ++++++ socksclient/BSocksClient.c | 27 +- socksclient/BSocksClient.h | 5 +- system/BDatagram.h | 10 + system/BDatagram_unix.c | 24 ++ system/BDatagram_win.c | 25 ++ tun2socks/CMakeLists.txt | 2 +- tun2socks/SocksUdpGwClient.c | 2 +- tun2socks/tun2socks.c | 61 ++- 17 files changed, 869 insertions(+), 83 deletions(-) create mode 100644 generated/blog_channel_SocksUdpClient.h create mode 100644 socks_udp_client/CMakeLists.txt create mode 100644 socks_udp_client/SocksUdpClient.c create mode 100644 socks_udp_client/SocksUdpClient.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 02c6ddcb6..6751e1385 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -332,6 +332,7 @@ endif () if (BUILD_TUN2SOCKS) add_subdirectory(socksclient) add_subdirectory(udpgw_client) + add_subdirectory(socks_udp_client) add_subdirectory(lwip) endif () diff --git a/blog_channels.txt b/blog_channels.txt index 8daa1e398..18657d5eb 100644 --- a/blog_channels.txt +++ b/blog_channels.txt @@ -89,6 +89,7 @@ NCDRfkillMonitor 4 udpgw 4 UdpGwClient 4 SocksUdpGwClient 4 +SocksUdpClient 4 BNetwork 4 BConnection 4 BSSLConnection 4 diff --git a/generated/blog_channel_SocksUdpClient.h b/generated/blog_channel_SocksUdpClient.h new file mode 100644 index 000000000..25779f48b --- /dev/null +++ b/generated/blog_channel_SocksUdpClient.h @@ -0,0 +1,4 @@ +#ifdef BLOG_CURRENT_CHANNEL +#undef BLOG_CURRENT_CHANNEL +#endif +#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_SocksUdpClient diff --git a/generated/blog_channels_defines.h b/generated/blog_channels_defines.h index 4f554e406..804c91487 100644 --- a/generated/blog_channels_defines.h +++ b/generated/blog_channels_defines.h @@ -89,60 +89,61 @@ #define BLOG_CHANNEL_udpgw 88 #define BLOG_CHANNEL_UdpGwClient 89 #define BLOG_CHANNEL_SocksUdpGwClient 90 -#define BLOG_CHANNEL_BNetwork 91 -#define BLOG_CHANNEL_BConnection 92 -#define BLOG_CHANNEL_BSSLConnection 93 -#define BLOG_CHANNEL_BDatagram 94 -#define BLOG_CHANNEL_PeerChat 95 -#define BLOG_CHANNEL_BArpProbe 96 -#define BLOG_CHANNEL_NCDModuleIndex 97 -#define BLOG_CHANNEL_NCDModuleProcess 98 -#define BLOG_CHANNEL_NCDValGenerator 99 -#define BLOG_CHANNEL_ncd_from_string 100 -#define BLOG_CHANNEL_ncd_to_string 101 -#define BLOG_CHANNEL_ncd_value 102 -#define BLOG_CHANNEL_ncd_try 103 -#define BLOG_CHANNEL_ncd_sys_request_server 104 -#define BLOG_CHANNEL_NCDRequest 105 -#define BLOG_CHANNEL_ncd_net_ipv6_wait_dynamic_addr 106 -#define BLOG_CHANNEL_NCDRequestClient 107 -#define BLOG_CHANNEL_ncd_request 108 -#define BLOG_CHANNEL_ncd_sys_request_client 109 -#define BLOG_CHANNEL_ncd_exit 110 -#define BLOG_CHANNEL_ncd_getargs 111 -#define BLOG_CHANNEL_ncd_arithmetic 112 -#define BLOG_CHANNEL_ncd_parse 113 -#define BLOG_CHANNEL_ncd_valuemetic 114 -#define BLOG_CHANNEL_ncd_file 115 -#define BLOG_CHANNEL_ncd_netmask 116 -#define BLOG_CHANNEL_ncd_implode 117 -#define BLOG_CHANNEL_ncd_call2 118 -#define BLOG_CHANNEL_ncd_assert 119 -#define BLOG_CHANNEL_ncd_reboot 120 -#define BLOG_CHANNEL_ncd_explode 121 -#define BLOG_CHANNEL_NCDPlaceholderDb 122 -#define BLOG_CHANNEL_NCDVal 123 -#define BLOG_CHANNEL_ncd_net_ipv6_addr 124 -#define BLOG_CHANNEL_ncd_net_ipv6_route 125 -#define BLOG_CHANNEL_ncd_net_ipv4_addr_in_network 126 -#define BLOG_CHANNEL_ncd_net_ipv6_addr_in_network 127 -#define BLOG_CHANNEL_dostest_server 128 -#define BLOG_CHANNEL_dostest_attacker 129 -#define BLOG_CHANNEL_ncd_timer 130 -#define BLOG_CHANNEL_ncd_file_open 131 -#define BLOG_CHANNEL_ncd_backtrack 132 -#define BLOG_CHANNEL_ncd_socket 133 -#define BLOG_CHANNEL_ncd_depend_scope 134 -#define BLOG_CHANNEL_ncd_substr 135 -#define BLOG_CHANNEL_ncd_sys_start_process 136 -#define BLOG_CHANNEL_NCDBuildProgram 137 -#define BLOG_CHANNEL_ncd_log 138 -#define BLOG_CHANNEL_ncd_log_msg 139 -#define BLOG_CHANNEL_ncd_buffer 140 -#define BLOG_CHANNEL_ncd_getenv 141 -#define BLOG_CHANNEL_BThreadSignal 142 -#define BLOG_CHANNEL_BLockReactor 143 -#define BLOG_CHANNEL_ncd_load_module 144 -#define BLOG_CHANNEL_ncd_basic_functions 145 -#define BLOG_CHANNEL_ncd_objref 146 -#define BLOG_NUM_CHANNELS 147 +#define BLOG_CHANNEL_SocksUdpClient 91 +#define BLOG_CHANNEL_BNetwork 92 +#define BLOG_CHANNEL_BConnection 93 +#define BLOG_CHANNEL_BSSLConnection 94 +#define BLOG_CHANNEL_BDatagram 95 +#define BLOG_CHANNEL_PeerChat 96 +#define BLOG_CHANNEL_BArpProbe 97 +#define BLOG_CHANNEL_NCDModuleIndex 98 +#define BLOG_CHANNEL_NCDModuleProcess 99 +#define BLOG_CHANNEL_NCDValGenerator 100 +#define BLOG_CHANNEL_ncd_from_string 101 +#define BLOG_CHANNEL_ncd_to_string 102 +#define BLOG_CHANNEL_ncd_value 103 +#define BLOG_CHANNEL_ncd_try 104 +#define BLOG_CHANNEL_ncd_sys_request_server 105 +#define BLOG_CHANNEL_NCDRequest 106 +#define BLOG_CHANNEL_ncd_net_ipv6_wait_dynamic_addr 107 +#define BLOG_CHANNEL_NCDRequestClient 108 +#define BLOG_CHANNEL_ncd_request 109 +#define BLOG_CHANNEL_ncd_sys_request_client 110 +#define BLOG_CHANNEL_ncd_exit 111 +#define BLOG_CHANNEL_ncd_getargs 112 +#define BLOG_CHANNEL_ncd_arithmetic 113 +#define BLOG_CHANNEL_ncd_parse 114 +#define BLOG_CHANNEL_ncd_valuemetic 115 +#define BLOG_CHANNEL_ncd_file 116 +#define BLOG_CHANNEL_ncd_netmask 117 +#define BLOG_CHANNEL_ncd_implode 118 +#define BLOG_CHANNEL_ncd_call2 119 +#define BLOG_CHANNEL_ncd_assert 120 +#define BLOG_CHANNEL_ncd_reboot 121 +#define BLOG_CHANNEL_ncd_explode 122 +#define BLOG_CHANNEL_NCDPlaceholderDb 123 +#define BLOG_CHANNEL_NCDVal 124 +#define BLOG_CHANNEL_ncd_net_ipv6_addr 125 +#define BLOG_CHANNEL_ncd_net_ipv6_route 126 +#define BLOG_CHANNEL_ncd_net_ipv4_addr_in_network 127 +#define BLOG_CHANNEL_ncd_net_ipv6_addr_in_network 128 +#define BLOG_CHANNEL_dostest_server 129 +#define BLOG_CHANNEL_dostest_attacker 130 +#define BLOG_CHANNEL_ncd_timer 131 +#define BLOG_CHANNEL_ncd_file_open 132 +#define BLOG_CHANNEL_ncd_backtrack 133 +#define BLOG_CHANNEL_ncd_socket 134 +#define BLOG_CHANNEL_ncd_depend_scope 135 +#define BLOG_CHANNEL_ncd_substr 136 +#define BLOG_CHANNEL_ncd_sys_start_process 137 +#define BLOG_CHANNEL_NCDBuildProgram 138 +#define BLOG_CHANNEL_ncd_log 139 +#define BLOG_CHANNEL_ncd_log_msg 140 +#define BLOG_CHANNEL_ncd_buffer 141 +#define BLOG_CHANNEL_ncd_getenv 142 +#define BLOG_CHANNEL_BThreadSignal 143 +#define BLOG_CHANNEL_BLockReactor 144 +#define BLOG_CHANNEL_ncd_load_module 145 +#define BLOG_CHANNEL_ncd_basic_functions 146 +#define BLOG_CHANNEL_ncd_objref 147 +#define BLOG_NUM_CHANNELS 148 diff --git a/generated/blog_channels_list.h b/generated/blog_channels_list.h index d099b2ba8..930bd8b28 100644 --- a/generated/blog_channels_list.h +++ b/generated/blog_channels_list.h @@ -89,6 +89,7 @@ {"udpgw", 4}, {"UdpGwClient", 4}, {"SocksUdpGwClient", 4}, +{"SocksUdpClient", 4}, {"BNetwork", 4}, {"BConnection", 4}, {"BSSLConnection", 4}, diff --git a/misc/socks_proto.h b/misc/socks_proto.h index 41f5a1fde..89e277503 100644 --- a/misc/socks_proto.h +++ b/misc/socks_proto.h @@ -112,7 +112,15 @@ B_START_PACKED struct socks_addr_ipv6 { uint8_t addr[16]; uint16_t port; -} B_PACKED; +} B_PACKED; +B_END_PACKED + +B_START_PACKED +struct socks_udp_header { + uint16_t rsv; + uint8_t frag; + uint8_t atyp; +} B_PACKED; B_END_PACKED #endif diff --git a/socks_udp_client/CMakeLists.txt b/socks_udp_client/CMakeLists.txt new file mode 100644 index 000000000..166eea5ef --- /dev/null +++ b/socks_udp_client/CMakeLists.txt @@ -0,0 +1 @@ +badvpn_add_library(socks_udp_client "system;flow;flowextra" "" SocksUdpClient.c) diff --git a/socks_udp_client/SocksUdpClient.c b/socks_udp_client/SocksUdpClient.c new file mode 100644 index 000000000..430cb1750 --- /dev/null +++ b/socks_udp_client/SocksUdpClient.c @@ -0,0 +1,529 @@ +/* + * Copyright (C) 2018 Jigsaw Operations LLC + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include + +#define DNS_PORT 53 + +static int addr_comparator (void *unused, BAddr *v1, BAddr *v2); +static struct SocksUdpClient_connection * find_connection_by_addr (SocksUdpClient *o, BAddr addr); +static void init_localhost4(uint32_t *ip4); +static void init_localhost6(uint8_t ip6[16]); +static void socks_state_handler(struct SocksUdpClient_connection *con, int event); +static void datagram_state_handler(struct SocksUdpClient_connection *con, int event); +static void send_monitor_handler (struct SocksUdpClient_connection *con); +static void recv_if_handler_send (struct SocksUdpClient_connection *con, uint8_t *data, int data_len); +static struct SocksUdpClient_connection * connection_init(SocksUdpClient *o, BAddr local_addr, + BAddr first_remote_addr, + const uint8_t *first_data, + int first_data_len); +static void connection_free (struct SocksUdpClient_connection *con); +static void connection_send (struct SocksUdpClient_connection *con, BAddr remote_addr, const uint8_t *data, int data_len); +static void first_job_handler(struct SocksUdpClient_connection *con); +static int compute_mtu(int udp_mtu); +static int get_dns_id(BAddr *remote_addr, const uint8_t *data, int data_len); + +int addr_comparator (void *unused, BAddr *v1, BAddr *v2) +{ + return BAddr_CompareOrder(v1, v2); +} + +struct SocksUdpClient_connection * find_connection_by_addr (SocksUdpClient *o, BAddr addr) +{ + BAVLNode *tree_node = BAVL_LookupExact(&o->connections_tree, &addr); + if (!tree_node) { + return NULL; + } + + return UPPER_OBJECT(tree_node, struct SocksUdpClient_connection, connections_tree_node); +} + +void init_localhost4(uint32_t *ip4) +{ + *ip4 = 1<<24 | 127; +} + +void init_localhost6(uint8_t ip6[16]) +{ + memset(ip6, 0, 16); + ip6[15] = 1; +} + +void socks_state_handler(struct SocksUdpClient_connection *con, int event) +{ + switch (event) { + case BSOCKSCLIENT_EVENT_UP: { + BIPAddr localhost; + localhost.type = con->client->server_addr.type; + if (localhost.type == BADDR_TYPE_IPV4) { + init_localhost4(&localhost.ipv4); + } else if (localhost.type == BADDR_TYPE_IPV6) { + init_localhost6(localhost.ipv6); + } else { + BLog(BLOG_ERROR, "Bad address type"); + } + // This will unblock the queue of pending packets. + BDatagram_SetSendAddrs(&con->socket, con->socks.bind_addr, localhost); + } break; + case BSOCKSCLIENT_EVENT_ERROR: { + BLog(BLOG_ERROR, "Socks error event"); + } // Fallthrough + case BSOCKSCLIENT_EVENT_ERROR_CLOSED: { + connection_free(con); + } break; + default: { + BLog(BLOG_ERROR, "Unknown event"); + } + } +} + +void datagram_state_handler(struct SocksUdpClient_connection *con, int event) +{ + if (event == BDATAGRAM_EVENT_ERROR) { + char local_buffer[BADDR_MAX_PRINT_LEN]; + BAddr_Print(&con->local_addr, local_buffer); + BLog(BLOG_ERROR, "Failing connection for %s due to a datagram send error", local_buffer); + connection_free(con); + } +} + +void send_monitor_handler (struct SocksUdpClient_connection *con) +{ + // The connection has passed its idle timeout. Remove it. + connection_free(con); +} + +void recv_if_handler_send(struct SocksUdpClient_connection *con, uint8_t *data, int data_len) +{ + SocksUdpClient *o = con->client; + DebugObject_Access(&con->client->d_obj); + ASSERT(data_len >= 0) + ASSERT(data_len <= compute_mtu(o->udp_mtu)) + + // accept packet + PacketPassInterface_Done(&con->recv_if); + + // check header + if (data_len < sizeof(struct socks_udp_header)) { + BLog(BLOG_ERROR, "missing header"); + return; + } + struct socks_udp_header *header = (struct socks_udp_header *)data; + uint8_t *addr_data = data + sizeof(struct socks_udp_header); + + // parse address + BAddr remote_addr; + size_t addr_size; + switch (header->atyp) { + case SOCKS_ATYP_IPV4: { + remote_addr.type = BADDR_TYPE_IPV4; + struct socks_addr_ipv4 *addr_ipv4 = (struct socks_addr_ipv4 *)addr_data; + remote_addr.ipv4.ip = addr_ipv4->addr; + remote_addr.ipv4.port = addr_ipv4->port; + addr_size = sizeof(*addr_ipv4); + } break; + case SOCKS_ATYP_IPV6: { + remote_addr.type = BADDR_TYPE_IPV6; + struct socks_addr_ipv6 *addr_ipv6 = (struct socks_addr_ipv6 *)addr_data; + memcpy(remote_addr.ipv6.ip, addr_ipv6->addr, sizeof(remote_addr.ipv6.ip)); + remote_addr.ipv6.port = addr_ipv6->port; + addr_size = sizeof(*addr_ipv6); + } break; + default: { + BLog(BLOG_ERROR, "Bad address type"); + return; + } + } + + uint8_t *body_data = addr_data + addr_size; + size_t body_len = data_len - (body_data - data); + + // check remaining data + if (body_len > o->udp_mtu) { + BLog(BLOG_ERROR, "too much data"); + return; + } + + // pass packet to user + SocksUdpClient *client = con->client; + client->handler_received(client->user, con->local_addr, remote_addr, body_data, body_len); + + if (con->dns_id >= 0) { + // This connection has only been used for a single DNS query. + int recv_dns_id = get_dns_id(&remote_addr, body_data, body_len); + if (recv_dns_id == con->dns_id) { + // We have now forwarded the response, so this connection is no longer needed. + connection_free(con); + } else { + BLog(BLOG_INFO, + "DNS client port received an unexpected non-DNS packet. " + "Disabling DNS optimization."); + con->dns_id = -1; + } + } +} + +struct SocksUdpClient_connection *connection_init(SocksUdpClient *o, BAddr local_addr, + BAddr first_remote_addr, + const uint8_t *first_data, + int first_data_len) +{ + DebugObject_Access(&o->d_obj); + ASSERT(o->num_connections <= o->max_connections) + ASSERT(!find_connection_by_addr(o, local_addr)) + + char buffer[BADDR_MAX_PRINT_LEN]; + BAddr_Print(&local_addr, buffer); + BLog(BLOG_DEBUG, "Creating new connection for %s", buffer); + + // allocate structure + struct SocksUdpClient_connection *con = (struct SocksUdpClient_connection *)malloc(sizeof(*con)); + if (!con) { + BLog(BLOG_ERROR, "malloc failed"); + goto fail0; + } + + // init arguments + con->client = o; + con->local_addr = local_addr; + con->first_data = BAlloc(first_data_len); + con->first_data_len = first_data_len; + con->first_remote_addr = first_remote_addr; + memcpy(con->first_data, first_data, first_data_len); + + con->dns_id = get_dns_id(&first_remote_addr, first_data, first_data_len); + + BPendingGroup *pg = BReactor_PendingGroup(o->reactor); + + // init first job, to send the first packet asynchronously. This has to happen asynchronously + // because con->send_writer (a BufferWriter) cannot accept writes until after it is linked with + // its PacketBuffer (con->send_buffer), which happens asynchronously. + BPending_Init(&con->first_job, pg, (BPending_handler)first_job_handler, con); + // Add the first job to the pending set. BPending acts as a LIFO stack, and first_job_handler + // needs to run after async actions that occur in PacketBuffer_Init, so we need to put first_job + // on the stack first. + BPending_Set(&con->first_job); + + // Create a datagram socket + if (!BDatagram_Init(&con->socket, con->local_addr.type, o->reactor, con, + (BDatagram_handler)datagram_state_handler)) { + BLog(BLOG_ERROR, "Failed to create a UDP socket"); + goto fail1; + } + + // Bind to 127.0.0.1:0 (or [::1]:0). Port 0 signals the kernel to choose an open port. + BAddr socket_addr; + socket_addr.type = local_addr.type; + if (local_addr.type == BADDR_TYPE_IPV4) { + init_localhost4(&socket_addr.ipv4.ip); + socket_addr.ipv4.port = 0; + } else if (local_addr.type == BADDR_TYPE_IPV6) { + init_localhost6(socket_addr.ipv6.ip); + socket_addr.ipv6.port = 0; + } else { + BLog(BLOG_ERROR, "Unknown local address type"); + goto fail2; + } + if (!BDatagram_Bind(&con->socket, socket_addr)) { + BLog(BLOG_ERROR, "Bind to localhost failed"); + goto fail2; + } + + // 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 fail2; + } + if (socket_addr.type == BADDR_TYPE_IPV4) { + socket_addr.ipv4.port = port; + } else { + socket_addr.ipv6.port = port; + } + + // 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)) { + BLog(BLOG_ERROR, "Failed to initialize SOCKS client"); + goto fail2; + } + + // Ensure that the UDP handling pipeline can handle queries big enough to include + // all data plus the SOCKS-UDP header. + int socks_mtu = compute_mtu(o->udp_mtu); + + // Send pipeline: send_writer -> send_buffer -> send_monitor -> send_if -> socket. + BDatagram_SendAsync_Init(&con->socket, socks_mtu); + PacketPassInterface *send_if = BDatagram_SendAsync_GetIf(&con->socket); + PacketPassInactivityMonitor_Init(&con->send_monitor, send_if, o->reactor, o->keepalive_time, + (PacketPassInactivityMonitor_handler)send_monitor_handler, con); + BufferWriter_Init(&con->send_writer, compute_mtu(o->udp_mtu), pg); + if (!PacketBuffer_Init(&con->send_buffer, BufferWriter_GetOutput(&con->send_writer), + PacketPassInactivityMonitor_GetInput(&con->send_monitor), + SOCKS_UDP_SEND_BUFFER_PACKETS, pg)) { + BLog(BLOG_ERROR, "Send buffer init failed"); + goto fail3; + } + + // Receive pipeline: socket -> recv_buffer -> recv_if + BDatagram_RecvAsync_Init(&con->socket, socks_mtu); + PacketPassInterface_Init(&con->recv_if, socks_mtu, + (PacketPassInterface_handler_send)recv_if_handler_send, con, pg); + if (!SinglePacketBuffer_Init(&con->recv_buffer, BDatagram_RecvAsync_GetIf(&con->socket), + &con->recv_if, pg)) { + BLog(BLOG_ERROR, "Receive buffer init failed"); + goto fail4; + } + + // insert to connections tree + ASSERT_EXECUTE(BAVL_Insert(&o->connections_tree, &con->connections_tree_node, NULL)) + + o->num_connections++; + + return con; + +fail4: + PacketPassInterface_Free(&con->recv_if); + BDatagram_RecvAsync_Free(&con->socket); + PacketBuffer_Free(&con->send_buffer); +fail3: + BufferWriter_Free(&con->send_writer); + PacketPassInactivityMonitor_Free(&con->send_monitor); + BDatagram_SendAsync_Free(&con->socket); +fail2: + BDatagram_Free(&con->socket); +fail1: + BPending_Free(&con->first_job); + BFree(con->first_data); + free(con); +fail0: + return NULL; +} + +void connection_free (struct SocksUdpClient_connection *con) +{ + SocksUdpClient *o = con->client; + DebugObject_Access(&o->d_obj); + + // decrement number of connections + o->num_connections--; + + // remove from connections tree + BAVL_Remove(&o->connections_tree, &con->connections_tree_node); + + // Free UDP send pipeline components + PacketBuffer_Free(&con->send_buffer); + BufferWriter_Free(&con->send_writer); + PacketPassInactivityMonitor_Free(&con->send_monitor); + BDatagram_SendAsync_Free(&con->socket); + + // Free UDP receive pipeline components + SinglePacketBuffer_Free(&con->recv_buffer); + PacketPassInterface_Free(&con->recv_if); + BDatagram_RecvAsync_Free(&con->socket); + + // Free UDP socket + BDatagram_Free(&con->socket); + + // Free SOCKS client + BSocksClient_Free(&con->socks); + + BPending_Free(&con->first_job); + if (con->first_data) { + BFree(con->first_data); + } + // free structure + free(con); +} + +void connection_send (struct SocksUdpClient_connection *con, BAddr remote_addr, + const uint8_t *data, int data_len) +{ + SocksUdpClient *o = con->client; + DebugObject_Access(&o->d_obj); + ASSERT(data_len >= 0) + ASSERT(data_len <= o->udp_mtu) + + if (con->dns_id >= 0) { + // So far, this connection has only sent a single DNS query. + int new_dns_id = get_dns_id(&remote_addr, data, data_len); + if (new_dns_id != con->dns_id) { + BLog(BLOG_DEBUG, "Client reused DNS query port. Disabling DNS optimization."); + con->dns_id = -1; + } + } + + // Check if we're sending to an IPv4 or IPv6 destination. + int atyp; + size_t address_size; + // write address + switch (remote_addr.type) { + case BADDR_TYPE_IPV4: { + atyp = SOCKS_ATYP_IPV4; + address_size = sizeof(struct socks_addr_ipv4); + } break; + case BADDR_TYPE_IPV6: { + atyp = SOCKS_ATYP_IPV6; + address_size = sizeof(struct socks_addr_ipv6); + } break; + default: { + BLog(BLOG_ERROR, "bad address type"); + return; + } + } + + // Wrap the payload in a UDP SOCKS header. + size_t socks_data_len = sizeof(struct socks_udp_header) + address_size + data_len; + if (socks_data_len > compute_mtu(o->udp_mtu)) { + BLog(BLOG_ERROR, "Packet is too big: %d > %d", socks_data_len, compute_mtu(o->udp_mtu)); + return; + } + uint8_t *socks_data; + if (!BufferWriter_StartPacket(&con->send_writer, &socks_data)) { + BLog(BLOG_ERROR, "Send buffer is full"); + return; + } + // Write header + struct socks_udp_header *header = (struct socks_udp_header *)socks_data; + header->rsv = 0; + header->frag = 0; + header->atyp = atyp; + uint8_t *addr_data = socks_data + sizeof(struct socks_udp_header); + switch (atyp) { + case SOCKS_ATYP_IPV4: { + struct socks_addr_ipv4 *addr_ipv4 = (struct socks_addr_ipv4 *)addr_data; + addr_ipv4->addr = remote_addr.ipv4.ip; + addr_ipv4->port = remote_addr.ipv4.port; + } break; + case SOCKS_ATYP_IPV6: { + struct socks_addr_ipv6 *addr_ipv6 = (struct socks_addr_ipv6 *)addr_data; + memcpy(addr_ipv6->addr, remote_addr.ipv6.ip, sizeof(addr_ipv6->addr)); + addr_ipv6->port = remote_addr.ipv6.port; + } break; + } + // write packet to buffer + memcpy(addr_data + address_size, data, data_len); + BufferWriter_EndPacket(&con->send_writer, socks_data_len); +} + +void first_job_handler(struct SocksUdpClient_connection *con) +{ + connection_send(con, con->first_remote_addr, con->first_data, con->first_data_len); + BFree(con->first_data); + con->first_data = NULL; + con->first_data_len = 0; +} + +int compute_mtu(int udp_mtu) +{ + return udp_mtu + sizeof(struct socks_udp_header) + sizeof(struct socks_addr_ipv6); +} + +int get_dns_id(BAddr *remote_addr, const uint8_t *data, int data_len) +{ + if (BAddr_GetPort(remote_addr) == htons(DNS_PORT) && data_len >= 2) { + return (data[0] << 8) + data[1]; + } + return -1; +} + +void SocksUdpClient_Init (SocksUdpClient *o, int udp_mtu, int max_connections, btime_t keepalive_time, + BAddr server_addr, const struct BSocksClient_auth_info *auth_info, size_t num_auth_info, + BReactor *reactor, void *user, + SocksUdpClient_handler_received handler_received) +{ + ASSERT(udp_mtu >= 0) + ASSERT(compute_mtu(udp_mtu) >= 0) + ASSERT(max_connections > 0) + + // init arguments + o->server_addr = server_addr; + o->auth_info = auth_info; + o->num_auth_info = num_auth_info; + o->udp_mtu = udp_mtu; + o->max_connections = max_connections; + o->num_connections = 0; + o->keepalive_time = keepalive_time; + o->reactor = reactor; + o->user = user; + o->handler_received = handler_received; + + // limit max connections to number of conid's + if (o->max_connections > UINT16_MAX + 1) { + o->max_connections = UINT16_MAX + 1; + } + + // init connections tree + BAVL_Init(&o->connections_tree, OFFSET_DIFF(struct SocksUdpClient_connection, local_addr, connections_tree_node), (BAVL_comparator)addr_comparator, NULL); + + DebugObject_Init(&o->d_obj); +} + +void SocksUdpClient_Free (SocksUdpClient *o) +{ + // free connections + while (!BAVL_IsEmpty(&o->connections_tree)) { + struct SocksUdpClient_connection *con = UPPER_OBJECT(BAVL_GetFirst(&o->connections_tree), struct SocksUdpClient_connection, connections_tree_node); + connection_free(con); + } + + DebugObject_Free(&o->d_obj); +} + +void SocksUdpClient_SubmitPacket (SocksUdpClient *o, BAddr local_addr, BAddr remote_addr, const uint8_t *data, int data_len) +{ + DebugObject_Access(&o->d_obj); + ASSERT(local_addr.type == BADDR_TYPE_IPV4 || local_addr.type == BADDR_TYPE_IPV6) + ASSERT(remote_addr.type == BADDR_TYPE_IPV4 || remote_addr.type == BADDR_TYPE_IPV6) + ASSERT(data_len >= 0) + + // lookup connection + struct SocksUdpClient_connection *con = find_connection_by_addr(o, local_addr); + if (!con) { + if (o->num_connections == o->max_connections) { + // Drop the packet. + BLog(BLOG_ERROR, "Dropping UDP packet, reached max number of connections."); + return; + } + // create new connection and enqueue the packet + connection_init(o, local_addr, remote_addr, data, data_len); + } else { + // send packet + connection_send(con, remote_addr, data, data_len); + } +} diff --git a/socks_udp_client/SocksUdpClient.h b/socks_udp_client/SocksUdpClient.h new file mode 100644 index 000000000..4e0409ec3 --- /dev/null +++ b/socks_udp_client/SocksUdpClient.h @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2018 Jigsaw Operations LLC + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BADVPN_SOCKS_UDP_CLIENT_SOCKSUDPCLIENT_H +#define BADVPN_SOCKS_UDP_CLIENT_SOCKSUDPCLIENT_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// This sets the number of packets to accept while waiting for SOCKS server to authenticate and +// connect. A slow or far-away SOCKS server could require 300 ms to connect, and a chatty +// client (e.g. STUN) could send a packet every 20 ms, so a limit of 16 seems reasonable. +#define SOCKS_UDP_SEND_BUFFER_PACKETS 16 + +typedef void (*SocksUdpClient_handler_received) (void *user, BAddr local_addr, BAddr remote_addr, const uint8_t *data, int data_len); + +typedef struct { + BAddr server_addr; + const struct BSocksClient_auth_info *auth_info; + size_t num_auth_info; + int num_connections; + int max_connections; + int udp_mtu; + btime_t keepalive_time; + BReactor *reactor; + void *user; + SocksUdpClient_handler_received handler_received; + BAVL connections_tree; // By local_addr + DebugObject d_obj; +} SocksUdpClient; + +struct SocksUdpClient_connection { + SocksUdpClient *client; + BAddr local_addr; + BSocksClient socks; + BufferWriter send_writer; + PacketBuffer send_buffer; + PacketPassInactivityMonitor send_monitor; + PacketPassInterface send_if; + BDatagram socket; + PacketPassInterface recv_if; + SinglePacketBuffer recv_buffer; + // The first_* members represent the initial packet, which has to be stored so it can wait for + // send_writer to become ready. + uint8_t *first_data; + int first_data_len; + BAddr first_remote_addr; + // If all packets sent so far have been sent to the same IP, port 53, with the + // same DNS ID, then this is that ID. Otherwise, it is -1. This is used to + // close ephemeral DNS query connections once a response is received. + int dns_id; + BPending first_job; + BAVLNode connections_tree_node; +}; + +/** + * Initializes the SOCKS5-UDP client object. + * This function does not perform network access, so it will always succeed if the arguments + * are valid. + * + * 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 keepalive_time how long to track an idle local port before forgetting it + * @param server_addr SOCKS5 server address. MUST BE ON LOCALHOST. + * @param reactor reactor we live in + * @param user value passed to handler + * @param handler_received handler for incoming UDP packets + */ +void SocksUdpClient_Init (SocksUdpClient *o, int udp_mtu, int max_connections, btime_t keepalive_time, + BAddr server_addr, const struct BSocksClient_auth_info *auth_info, size_t num_auth_info, + BReactor *reactor, void *user, SocksUdpClient_handler_received handler_received); +void SocksUdpClient_Free (SocksUdpClient *o); + +/** + * Submit a packet to be sent through the proxy. + * + * This will reuse an existing connection for packets from local_addr, or create one if + * there is none. If the number of live connections exceeds max_connections, or if the number of + * buffered packets from this port exceeds a limit, packets will be dropped silently. + * + * As a resource optimization, if a connection has only been used to send one DNS query, then + * the connection will be closed and freed once the reply is received. + * + * @param o the object + * @param local_addr the UDP packet's source address, and the expected destination for replies + * @param remote_addr the destination of the packet after it exits the proxy + * @param data the packet contents. Caller retains ownership. + */ +void SocksUdpClient_SubmitPacket (SocksUdpClient *o, BAddr local_addr, BAddr remote_addr, const uint8_t *data, int data_len); + +#endif diff --git a/socksclient/BSocksClient.c b/socksclient/BSocksClient.c index 21415af4c..a0cae06f9 100644 --- a/socksclient/BSocksClient.c +++ b/socksclient/BSocksClient.c @@ -329,9 +329,11 @@ void recv_handler_done (BSocksClient *o, int data_len) int addr_len; switch (ntoh8(imsg.atyp)) { case SOCKS_ATYP_IPV4: + o->bind_addr.type = BADDR_TYPE_IPV4; addr_len = sizeof(struct socks_addr_ipv4); break; case SOCKS_ATYP_IPV6: + o->bind_addr.type = BADDR_TYPE_IPV6; addr_len = sizeof(struct socks_addr_ipv6); break; default: @@ -365,6 +367,26 @@ void recv_handler_done (BSocksClient *o, int data_len) case STATE_RECEIVED_REPLY_HEADER: { BLog(BLOG_DEBUG, "received reply rest"); + // Record the address of the new socket bound by the server. + // For a CONNECT command, this is the address of the TCP client socket to dest_addr. + // Knowing this address is usually not important. + // For a UDP_ASSOCIATE command, this is the UDP address to which to send SOCKS UDP. + // Recording this address is a prerequisite to send traffic on a SOCKS-UDP association. + void *addr_buffer = o->buffer + sizeof(struct socks_reply_header); + switch (o->bind_addr.type) { + case BADDR_TYPE_IPV4: { + struct socks_addr_ipv4 *ip4 = addr_buffer; + o->bind_addr.ipv4.ip = ip4->addr; + o->bind_addr.ipv4.port = ip4->port; + } break; + case BADDR_TYPE_IPV6: { + struct socks_addr_ipv6 *ip6 = addr_buffer; + memcpy(o->bind_addr.ipv6.ip, ip6->addr, sizeof(ip6->addr)); + o->bind_addr.ipv6.port = ip6->port; + } break; + default: ASSERT(0); + } + // free buffer BFree(o->buffer); o->buffer = NULL; @@ -476,7 +498,7 @@ void auth_finished (BSocksClient *o) // write request struct socks_request_header header; header.ver = hton8(SOCKS_VERSION); - header.cmd = hton8(SOCKS_CMD_CONNECT); + header.cmd = hton8(o->udp ? SOCKS_CMD_UDP_ASSOCIATE : SOCKS_CMD_CONNECT); header.rsv = hton8(0); switch (o->dest_addr.type) { case BADDR_TYPE_IPV4: { @@ -525,7 +547,7 @@ struct BSocksClient_auth_info BSocksClient_auth_password (const char *username, int BSocksClient_Init (BSocksClient *o, BAddr server_addr, const struct BSocksClient_auth_info *auth_info, size_t num_auth_info, - BAddr dest_addr, BSocksClient_handler handler, void *user, BReactor *reactor) + 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) @@ -540,6 +562,7 @@ int BSocksClient_Init (BSocksClient *o, o->auth_info = auth_info; o->num_auth_info = num_auth_info; o->dest_addr = dest_addr; + o->udp = udp; o->handler = handler; o->user = user; o->reactor = reactor; diff --git a/socksclient/BSocksClient.h b/socksclient/BSocksClient.h index f19b3a85c..7bb775472 100644 --- a/socksclient/BSocksClient.h +++ b/socksclient/BSocksClient.h @@ -35,6 +35,7 @@ #define BADVPN_SOCKS_BSOCKSCLIENT_H #include +#include #include #include @@ -78,6 +79,8 @@ typedef struct { const struct BSocksClient_auth_info *auth_info; size_t num_auth_info; BAddr dest_addr; + bool udp; + BAddr bind_addr; BSocksClient_handler handler; void *user; BReactor *reactor; @@ -117,7 +120,7 @@ struct BSocksClient_auth_info BSocksClient_auth_password (const char *username, */ int BSocksClient_Init (BSocksClient *o, BAddr server_addr, const struct BSocksClient_auth_info *auth_info, size_t num_auth_info, - BAddr dest_addr, BSocksClient_handler handler, void *user, BReactor *reactor) WARN_UNUSED; + BAddr dest_addr, bool udp, BSocksClient_handler handler, void *user, BReactor *reactor) WARN_UNUSED; /** * Frees the object. diff --git a/system/BDatagram.h b/system/BDatagram.h index 33efb4512..63e55ecc3 100644 --- a/system/BDatagram.h +++ b/system/BDatagram.h @@ -123,6 +123,16 @@ void BDatagram_SetSendAddrs (BDatagram *o, BAddr remote_addr, BIPAddr local_addr */ int BDatagram_GetLastReceiveAddrs (BDatagram *o, BAddr *remote_addr, BIPAddr *local_addr); +/** + * Returns the bound port. + * Fails if and only if a port is not yet bound. + * + * @param o the object + * @param local_port returns the local bound port. + * @return 1 on success, 0 on failure + */ +int BDatagram_GetLocalPort (BDatagram *o, uint16_t *local_port); + #ifndef BADVPN_USE_WINAPI /** * Returns the underlying socket file descriptor of the datagram object. diff --git a/system/BDatagram_unix.c b/system/BDatagram_unix.c index b49b865c8..a6e42e37a 100644 --- a/system/BDatagram_unix.c +++ b/system/BDatagram_unix.c @@ -734,6 +734,30 @@ int BDatagram_GetLastReceiveAddrs (BDatagram *o, BAddr *remote_addr, BIPAddr *lo return 1; } +int BDatagram_GetLocalPort (BDatagram *o, uint16_t *local_port) +{ + DebugObject_Access(&o->d_obj); + + struct sys_addr sysaddr; + BAddr addr; + sysaddr.len = sizeof(sysaddr.addr); + if (getsockname(o->fd, &sysaddr.addr.generic, &sysaddr.len) != 0) { + BLog(BLOG_ERROR, "getsockname failed"); + return 0; + } + addr_sys_to_socket(&addr, sysaddr); + if (addr.type == BADDR_TYPE_IPV4) { + *local_port = addr.ipv4.port; + return 1; + } + if (addr.type == BADDR_TYPE_IPV6) { + *local_port = addr.ipv6.port; + return 1; + } + BLog(BLOG_ERROR, "Unknown address type from getsockname: %d", addr.type); + return 0; +} + int BDatagram_GetFd (BDatagram *o) { DebugObject_Access(&o->d_obj); diff --git a/system/BDatagram_win.c b/system/BDatagram_win.c index b8b9b1f24..2cdc9c833 100644 --- a/system/BDatagram_win.c +++ b/system/BDatagram_win.c @@ -635,6 +635,31 @@ int BDatagram_GetLastReceiveAddrs (BDatagram *o, BAddr *remote_addr, BIPAddr *lo return 1; } +int BDatagram_GetLocalPort (BDatagram *o, uint16_t *local_port) +{ + DebugObject_Access(&o->d_obj); + + struct BDatagram_sys_addr sysaddr; + BAddr addr; + socklen_t addr_size = sizeof(sysaddr.addr.generic); + if (getsockname(o->sock, &sysaddr.addr.generic, &addr_size) != 0) { + BLog(BLOG_ERROR, "getsockname failed"); + return 0; + } + + addr_sys_to_socket(&addr, sysaddr); + if (addr.type == BADDR_TYPE_IPV4) { + *local_port = addr.ipv4.port; + return 1; + } + if (addr.type == BADDR_TYPE_IPV6) { + *local_port = addr.ipv6.port; + return 1; + } + BLog(BLOG_ERROR, "Unknown address type from getsockname: %d", addr.type); + return 0; +} + int BDatagram_SetReuseAddr (BDatagram *o, int reuse) { DebugObject_Access(&o->d_obj); diff --git a/tun2socks/CMakeLists.txt b/tun2socks/CMakeLists.txt index 4246fd052..3d5ec329a 100644 --- a/tun2socks/CMakeLists.txt +++ b/tun2socks/CMakeLists.txt @@ -2,7 +2,7 @@ add_executable(badvpn-tun2socks tun2socks.c SocksUdpGwClient.c ) -target_link_libraries(badvpn-tun2socks system flow tuntap lwip socksclient udpgw_client) +target_link_libraries(badvpn-tun2socks system flow tuntap lwip socksclient udpgw_client socks_udp_client) install( TARGETS badvpn-tun2socks diff --git a/tun2socks/SocksUdpGwClient.c b/tun2socks/SocksUdpGwClient.c index 949d114ba..5ce68d9e5 100644 --- a/tun2socks/SocksUdpGwClient.c +++ b/tun2socks/SocksUdpGwClient.c @@ -62,7 +62,7 @@ static void try_connect (SocksUdpGwClient *o) ASSERT(!BTimer_IsRunning(&o->reconnect_timer)) // init SOCKS client - if (!BSocksClient_Init(&o->socks_client, o->socks_server_addr, o->auth_info, o->num_auth_info, o->remote_udpgw_addr, (BSocksClient_handler)socks_client_handler, o, o->reactor)) { + if (!BSocksClient_Init(&o->socks_client, o->socks_server_addr, o->auth_info, o->num_auth_info, o->remote_udpgw_addr, false, (BSocksClient_handler)socks_client_handler, o, o->reactor)) { BLog(BLOG_ERROR, "BSocksClient_Init failed"); goto fail0; } diff --git a/tun2socks/tun2socks.c b/tun2socks/tun2socks.c index 36c72c7ac..bf951ec5a 100644 --- a/tun2socks/tun2socks.c +++ b/tun2socks/tun2socks.c @@ -65,6 +65,7 @@ #include #include #include +#include #ifndef BADVPN_USE_WINAPI #include @@ -115,6 +116,7 @@ struct { int udpgw_max_connections; int udpgw_connection_buffer_size; int udpgw_transparent_dns; + int socks5_udp; } options; // TCP client @@ -183,6 +185,9 @@ PacketPassInterface device_read_interface; SocksUdpGwClient udpgw_client; int udp_mtu; +// SOCKS5-UDP client +SocksUdpClient socks_udp_client; + // TCP timer BTimer tcp_timer; int tcp_timer_mod4; @@ -242,7 +247,8 @@ static void client_socks_recv_initiate (struct tcp_client *client); static void client_socks_recv_handler_done (struct tcp_client *client, int data_len); static int client_socks_recv_send_out (struct tcp_client *client); static err_t client_sent_func (void *arg, struct tcp_pcb *tpcb, u16_t len); -static void udpgw_client_handler_received (void *unused, BAddr local_addr, BAddr remote_addr, const uint8_t *data, int data_len); +static void udp_send_packet_to_device (void *unused, BAddr local_addr, BAddr remote_addr, const uint8_t *data, int data_len); + int main (int argc, char **argv) { @@ -351,19 +357,19 @@ int main (int argc, char **argv) goto fail4; } - if (options.udpgw_remote_server_addr) { - // compute maximum UDP payload size we need to pass through udpgw - udp_mtu = BTap_GetMTU(&device) - (int)(sizeof(struct ipv4_header) + sizeof(struct udp_header)); - if (options.netif_ip6addr) { - int udp_ip6_mtu = BTap_GetMTU(&device) - (int)(sizeof(struct ipv6_header) + sizeof(struct udp_header)); - if (udp_mtu < udp_ip6_mtu) { - udp_mtu = udp_ip6_mtu; - } - } - if (udp_mtu < 0) { - udp_mtu = 0; + // compute maximum UDP payload size we need to pass through udpgw + udp_mtu = BTap_GetMTU(&device) - (int)(sizeof(struct ipv4_header) + sizeof(struct udp_header)); + if (options.netif_ip6addr) { + int udp_ip6_mtu = BTap_GetMTU(&device) - (int)(sizeof(struct ipv6_header) + sizeof(struct udp_header)); + if (udp_mtu < udp_ip6_mtu) { + udp_mtu = udp_ip6_mtu; } - + } + if (udp_mtu < 0) { + udp_mtu = 0; + } + + if (options.udpgw_remote_server_addr) { // make sure our UDP payloads aren't too large for udpgw int udpgw_mtu = udpgw_compute_mtu(udp_mtu); if (udpgw_mtu < 0 || udpgw_mtu > PACKETPROTO_MAXPAYLOAD) { @@ -374,11 +380,15 @@ int main (int argc, char **argv) // init udpgw client if (!SocksUdpGwClient_Init(&udpgw_client, udp_mtu, DEFAULT_UDPGW_MAX_CONNECTIONS, options.udpgw_connection_buffer_size, UDPGW_KEEPALIVE_TIME, socks_server_addr, socks_auth_info, socks_num_auth_info, - udpgw_remote_server_addr, UDPGW_RECONNECT_TIME, &ss, NULL, udpgw_client_handler_received + udpgw_remote_server_addr, UDPGW_RECONNECT_TIME, &ss, NULL, udp_send_packet_to_device )) { BLog(BLOG_ERROR, "SocksUdpGwClient_Init failed"); goto fail4a; } + } else if (options.socks5_udp) { + SocksUdpClient_Init(&socks_udp_client, udp_mtu, DEFAULT_UDPGW_MAX_CONNECTIONS, + UDPGW_KEEPALIVE_TIME, socks_server_addr, socks_auth_info, + socks_num_auth_info, &ss, NULL, udp_send_packet_to_device); } // init lwip init job @@ -440,6 +450,8 @@ int main (int argc, char **argv) BPending_Free(&lwip_init_job); if (options.udpgw_remote_server_addr) { SocksUdpGwClient_Free(&udpgw_client); + } else if (options.socks5_udp) { + SocksUdpClient_Free(&socks_udp_client); } fail4a: SinglePacketBuffer_Free(&device_read_buffer); @@ -502,6 +514,7 @@ void print_help (const char *name) " [--udpgw-max-connections ]\n" " [--udpgw-connection-buffer-size ]\n" " [--udpgw-transparent-dns]\n" + " [--socks5-udp]\n" "Address format is a.b.c.d:port (IPv4) or [addr]:port (IPv6).\n", name ); @@ -542,6 +555,7 @@ int parse_arguments (int argc, char *argv[]) options.udpgw_max_connections = DEFAULT_UDPGW_MAX_CONNECTIONS; options.udpgw_connection_buffer_size = DEFAULT_UDPGW_CONNECTION_BUFFER_SIZE; options.udpgw_transparent_dns = 0; + options.socks5_udp = 0; int i; for (i = 1; i < argc; i++) { @@ -719,6 +733,9 @@ int parse_arguments (int argc, char *argv[]) else if (!strcmp(arg, "--udpgw-transparent-dns")) { options.udpgw_transparent_dns = 1; } + else if (!strcmp(arg, "--socks5-udp")) { + options.socks5_udp = 1; + } else { fprintf(stderr, "unknown option: %s\n", arg); return 0; @@ -1050,7 +1067,7 @@ int process_device_udp_packet (uint8_t *data, int data_len) ASSERT(data_len >= 0) // do nothing if we don't have udpgw - if (!options.udpgw_remote_server_addr) { + if (!options.udpgw_remote_server_addr && !options.socks5_udp) { goto fail; } @@ -1155,8 +1172,13 @@ int process_device_udp_packet (uint8_t *data, int data_len) goto fail; } - // submit packet to udpgw - SocksUdpGwClient_SubmitPacket(&udpgw_client, local_addr, remote_addr, is_dns, data, data_len); + if (options.udpgw_remote_server_addr) { + // submit packet to udpgw + SocksUdpGwClient_SubmitPacket(&udpgw_client, local_addr, remote_addr, + is_dns, data, data_len); + } else if (options.socks5_udp) { + SocksUdpClient_SubmitPacket(&socks_udp_client, local_addr, remote_addr, data, data_len); + } return 1; @@ -1305,7 +1327,7 @@ err_t listener_accept_func (void *arg, struct tcp_pcb *newpcb, err_t err) // init SOCKS if (!BSocksClient_Init(&client->socks_client, socks_server_addr, socks_auth_info, socks_num_auth_info, - addr, (BSocksClient_handler)client_socks_handler, client, &ss)) { + addr, false, (BSocksClient_handler)client_socks_handler, client, &ss)) { BLog(BLOG_ERROR, "listener accept: BSocksClient_Init failed"); goto fail1; } @@ -1816,9 +1838,8 @@ err_t client_sent_func (void *arg, struct tcp_pcb *tpcb, u16_t len) return (DEAD_KILLED > 0) ? ERR_ABRT : ERR_OK; } -void udpgw_client_handler_received (void *unused, BAddr local_addr, BAddr remote_addr, const uint8_t *data, int data_len) +void udp_send_packet_to_device (void *unused, BAddr local_addr, BAddr remote_addr, const uint8_t *data, int data_len) { - ASSERT(options.udpgw_remote_server_addr) ASSERT(local_addr.type == BADDR_TYPE_IPV4 || local_addr.type == BADDR_TYPE_IPV6) ASSERT(local_addr.type == remote_addr.type) ASSERT(data_len >= 0) From 6241fc29783a68934ce6ab979915abc8845900a2 Mon Sep 17 00:00:00 2001 From: Ambroz Bizjak Date: Fri, 31 Jan 2020 21:32:28 -0800 Subject: [PATCH 04/11] Fixes and refactoring for SOCKS5 UDP. --- compile-tun2socks.sh | 2 + socks_udp_client/CMakeLists.txt | 2 +- socks_udp_client/SocksUdpClient.c | 510 ++++++++++++++++++------------ socks_udp_client/SocksUdpClient.h | 65 ++-- socksclient/BSocksClient.c | 26 +- socksclient/BSocksClient.h | 24 +- system/BAddr.h | 18 ++ system/BDatagram.h | 18 +- system/BDatagram_common.c | 52 +++ system/BDatagram_unix.c | 24 +- system/BDatagram_win.c | 24 +- system/CMakeLists.txt | 1 + tun2socks/SocksUdpGwClient.c | 5 +- tun2socks/tun2socks.c | 57 ++-- tun2socks/tun2socks.h | 6 + 15 files changed, 544 insertions(+), 290 deletions(-) create mode 100644 system/BDatagram_common.c diff --git a/compile-tun2socks.sh b/compile-tun2socks.sh index 19a87211c..e06f43bc0 100755 --- a/compile-tun2socks.sh +++ b/compile-tun2socks.sh @@ -61,6 +61,7 @@ system/BConnection_common.c system/BTime.c system/BUnixSignal.c system/BNetwork.c +system/BDatagram_common.c system/BDatagram_unix.c flow/StreamRecvInterface.c flow/PacketRecvInterface.c @@ -109,6 +110,7 @@ base/BPending.c flowextra/PacketPassInactivityMonitor.c tun2socks/SocksUdpGwClient.c udpgw_client/UdpGwClient.c +socks_udp_client/SocksUdpClient.c " set -e diff --git a/socks_udp_client/CMakeLists.txt b/socks_udp_client/CMakeLists.txt index 166eea5ef..4598b48f8 100644 --- a/socks_udp_client/CMakeLists.txt +++ b/socks_udp_client/CMakeLists.txt @@ -1 +1 @@ -badvpn_add_library(socks_udp_client "system;flow;flowextra" "" SocksUdpClient.c) +badvpn_add_library(socks_udp_client "base;system;flow;flowextra;socksclient" "" SocksUdpClient.c) diff --git a/socks_udp_client/SocksUdpClient.c b/socks_udp_client/SocksUdpClient.c index 430cb1750..a7446da6e 100644 --- a/socks_udp_client/SocksUdpClient.c +++ b/socks_udp_client/SocksUdpClient.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2018 Jigsaw Operations LLC + * Copyright (C) 2019 Ambroz Bizjak (modifications) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -24,6 +25,8 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include +#include #include #include @@ -31,38 +34,41 @@ #include #include #include +#include +#include +#include #include +#include #include #include -#define DNS_PORT 53 +static const int DnsPort = 53; static int addr_comparator (void *unused, BAddr *v1, BAddr *v2); -static struct SocksUdpClient_connection * find_connection_by_addr (SocksUdpClient *o, BAddr addr); -static void init_localhost4(uint32_t *ip4); -static void init_localhost6(uint8_t ip6[16]); -static void socks_state_handler(struct SocksUdpClient_connection *con, int event); -static void datagram_state_handler(struct SocksUdpClient_connection *con, int event); +static struct SocksUdpClient_connection * find_connection (SocksUdpClient *o, BAddr addr); +static void socks_state_handler (struct SocksUdpClient_connection *con, int event); +static void datagram_state_handler (struct SocksUdpClient_connection *con, int event); static void send_monitor_handler (struct SocksUdpClient_connection *con); -static void recv_if_handler_send (struct SocksUdpClient_connection *con, uint8_t *data, int data_len); -static struct SocksUdpClient_connection * connection_init(SocksUdpClient *o, BAddr local_addr, - BAddr first_remote_addr, - const uint8_t *first_data, - int first_data_len); +static void recv_if_handler_send ( + struct SocksUdpClient_connection *con, uint8_t *data, int data_len); +static struct SocksUdpClient_connection * connection_init ( + SocksUdpClient *o, BAddr local_addr, BAddr first_remote_addr, + const uint8_t *first_data, int first_data_len); static void connection_free (struct SocksUdpClient_connection *con); -static void connection_send (struct SocksUdpClient_connection *con, BAddr remote_addr, const uint8_t *data, int data_len); -static void first_job_handler(struct SocksUdpClient_connection *con); -static int compute_mtu(int udp_mtu); -static int get_dns_id(BAddr *remote_addr, const uint8_t *data, int data_len); +static void connection_send (struct SocksUdpClient_connection *con, + BAddr remote_addr, const uint8_t *data, int data_len); +static void first_job_handler (struct SocksUdpClient_connection *con); +static int compute_socks_mtu (int udp_mtu); +static int get_dns_id (BAddr *remote_addr, const uint8_t *data, int data_len); int addr_comparator (void *unused, BAddr *v1, BAddr *v2) { return BAddr_CompareOrder(v1, v2); } -struct SocksUdpClient_connection * find_connection_by_addr (SocksUdpClient *o, BAddr addr) +struct SocksUdpClient_connection * find_connection (SocksUdpClient *o, BAddr addr) { BAVLNode *tree_node = BAVL_LookupExact(&o->connections_tree, &addr); if (!tree_node) { @@ -72,195 +78,229 @@ struct SocksUdpClient_connection * find_connection_by_addr (SocksUdpClient *o, B return UPPER_OBJECT(tree_node, struct SocksUdpClient_connection, connections_tree_node); } -void init_localhost4(uint32_t *ip4) +void socks_state_handler (struct SocksUdpClient_connection *con, int event) { - *ip4 = 1<<24 | 127; -} - -void init_localhost6(uint8_t ip6[16]) -{ - memset(ip6, 0, 16); - ip6[15] = 1; -} + DebugObject_Access(&con->client->d_obj); -void socks_state_handler(struct SocksUdpClient_connection *con, int event) -{ switch (event) { case BSOCKSCLIENT_EVENT_UP: { + // Figure out the localhost address. BIPAddr localhost; - localhost.type = con->client->server_addr.type; - if (localhost.type == BADDR_TYPE_IPV4) { - init_localhost4(&localhost.ipv4); - } else if (localhost.type == BADDR_TYPE_IPV6) { - init_localhost6(localhost.ipv6); - } else { - BLog(BLOG_ERROR, "Bad address type"); - } - // This will unblock the queue of pending packets. - BDatagram_SetSendAddrs(&con->socket, con->socks.bind_addr, localhost); + BIPAddr_InitLocalhost(&localhost, con->client->server_addr.type); + + // Get the address to send datagrams to from BSocksClient. + BAddr remote_addr = BSocksClient_GetBindAddr(&con->socks); + + // Set the local/remote send address for BDatagram. + // This will unblock the queue of outgoing packets. + BDatagram_SetSendAddrs(&con->socket, remote_addr, localhost); } break; + case BSOCKSCLIENT_EVENT_ERROR: { - BLog(BLOG_ERROR, "Socks error event"); - } // Fallthrough + char local_buffer[BADDR_MAX_PRINT_LEN]; + BAddr_Print(&con->local_addr, local_buffer); + BLog(BLOG_ERROR, + "SOCKS error event for %s, removing connection.", local_buffer); + + connection_free(con); + } break; + case BSOCKSCLIENT_EVENT_ERROR_CLOSED: { + char local_buffer[BADDR_MAX_PRINT_LEN]; + BAddr_Print(&con->local_addr, local_buffer); + BLog(BLOG_WARNING, + "SOCKS closed event for %s, removing connection.", local_buffer); + connection_free(con); } break; + default: { - BLog(BLOG_ERROR, "Unknown event"); - } + BLog(BLOG_ERROR, "Unknown SOCKS event"); + } break; } } -void datagram_state_handler(struct SocksUdpClient_connection *con, int event) +void datagram_state_handler (struct SocksUdpClient_connection *con, int event) { + DebugObject_Access(&con->client->d_obj); + if (event == BDATAGRAM_EVENT_ERROR) { char local_buffer[BADDR_MAX_PRINT_LEN]; BAddr_Print(&con->local_addr, local_buffer); - BLog(BLOG_ERROR, "Failing connection for %s due to a datagram send error", local_buffer); + BLog(BLOG_ERROR, + "Low-level datagram error %s, removing connection.", local_buffer); + + // Remove the connection. Note that BDatagram requires that we free + // the BDatagram after an error is reported. connection_free(con); } } void send_monitor_handler (struct SocksUdpClient_connection *con) { - // The connection has passed its idle timeout. Remove it. + DebugObject_Access(&con->client->d_obj); + + char local_buffer[BADDR_MAX_PRINT_LEN]; + BAddr_Print(&con->local_addr, local_buffer); + BLog(BLOG_INFO, + "Removing connection for %s due to inactivity.", local_buffer); + + // The connection has passed its idle timeout. Remove it. connection_free(con); } -void recv_if_handler_send(struct SocksUdpClient_connection *con, uint8_t *data, int data_len) +void recv_if_handler_send ( + struct SocksUdpClient_connection *con, uint8_t *data, int data_len) { - SocksUdpClient *o = con->client; DebugObject_Access(&con->client->d_obj); + SocksUdpClient *o = con->client; ASSERT(data_len >= 0) - ASSERT(data_len <= compute_mtu(o->udp_mtu)) + ASSERT(data_len <= o->socks_mtu) // accept packet PacketPassInterface_Done(&con->recv_if); // check header - if (data_len < sizeof(struct socks_udp_header)) { - BLog(BLOG_ERROR, "missing header"); + struct socks_udp_header header; + if (data_len < sizeof(header)) { + BLog(BLOG_ERROR, "Missing SOCKS-UDP header."); return; } - struct socks_udp_header *header = (struct socks_udp_header *)data; - uint8_t *addr_data = data + sizeof(struct socks_udp_header); + memcpy(&header, data, sizeof(header)); + data += sizeof(header); + data_len -= sizeof(header); // parse address BAddr remote_addr; - size_t addr_size; - switch (header->atyp) { + switch (header.atyp) { case SOCKS_ATYP_IPV4: { + struct socks_addr_ipv4 addr_ipv4; + if (data_len < sizeof(addr_ipv4)) { + BLog(BLOG_ERROR, "Missing IPv4 address."); + return; + } + memcpy(&addr_ipv4, data, sizeof(addr_ipv4)); + data += sizeof(addr_ipv4); + data_len -= sizeof(addr_ipv4); remote_addr.type = BADDR_TYPE_IPV4; - struct socks_addr_ipv4 *addr_ipv4 = (struct socks_addr_ipv4 *)addr_data; - remote_addr.ipv4.ip = addr_ipv4->addr; - remote_addr.ipv4.port = addr_ipv4->port; - addr_size = sizeof(*addr_ipv4); + remote_addr.ipv4.ip = addr_ipv4.addr; + remote_addr.ipv4.port = addr_ipv4.port; } break; case SOCKS_ATYP_IPV6: { + struct socks_addr_ipv6 addr_ipv6; + if (data_len < sizeof(addr_ipv6)) { + BLog(BLOG_ERROR, "Missing IPv6 address."); + return; + } + memcpy(&addr_ipv6, data, sizeof(addr_ipv6)); + data += sizeof(addr_ipv6); + data_len -= sizeof(addr_ipv6); remote_addr.type = BADDR_TYPE_IPV6; - struct socks_addr_ipv6 *addr_ipv6 = (struct socks_addr_ipv6 *)addr_data; - memcpy(remote_addr.ipv6.ip, addr_ipv6->addr, sizeof(remote_addr.ipv6.ip)); - remote_addr.ipv6.port = addr_ipv6->port; - addr_size = sizeof(*addr_ipv6); + memcpy(remote_addr.ipv6.ip, addr_ipv6.addr, sizeof(remote_addr.ipv6.ip)); + remote_addr.ipv6.port = addr_ipv6.port; } break; default: { BLog(BLOG_ERROR, "Bad address type"); return; - } + } break; } - uint8_t *body_data = addr_data + addr_size; - size_t body_len = data_len - (body_data - data); - // check remaining data - if (body_len > o->udp_mtu) { + if (data_len > o->udp_mtu) { BLog(BLOG_ERROR, "too much data"); return; } // pass packet to user SocksUdpClient *client = con->client; - client->handler_received(client->user, con->local_addr, remote_addr, body_data, body_len); + client->handler_received(client->user, con->local_addr, remote_addr, data, data_len); + // Was this connection used for a DNS query? if (con->dns_id >= 0) { - // This connection has only been used for a single DNS query. - int recv_dns_id = get_dns_id(&remote_addr, body_data, body_len); + // Get the DNS transaction ID of the response. + int recv_dns_id = get_dns_id(&remote_addr, data, data_len); + + // Does the transaction ID matche that of the request? if (recv_dns_id == con->dns_id) { // We have now forwarded the response, so this connection is no longer needed. + char local_buffer[BADDR_MAX_PRINT_LEN]; + BAddr_Print(&con->local_addr, local_buffer); + BLog(BLOG_DEBUG, + "Removing connection for %s after the DNS response.", local_buffer); + connection_free(con); } else { - BLog(BLOG_INFO, - "DNS client port received an unexpected non-DNS packet. " - "Disabling DNS optimization."); + BLog(BLOG_INFO, "DNS client port received an unexpected non-DNS packet, " + "disabling DNS optimization."); + con->dns_id = -1; } } } -struct SocksUdpClient_connection *connection_init(SocksUdpClient *o, BAddr local_addr, - BAddr first_remote_addr, - const uint8_t *first_data, - int first_data_len) +struct SocksUdpClient_connection * connection_init ( + SocksUdpClient *o, BAddr local_addr, BAddr first_remote_addr, + const uint8_t *first_data, int first_data_len) { - DebugObject_Access(&o->d_obj); ASSERT(o->num_connections <= o->max_connections) - ASSERT(!find_connection_by_addr(o, local_addr)) + ASSERT(!find_connection(o, local_addr)) - char buffer[BADDR_MAX_PRINT_LEN]; - BAddr_Print(&local_addr, buffer); - BLog(BLOG_DEBUG, "Creating new connection for %s", buffer); + char local_buffer[BADDR_MAX_PRINT_LEN]; + BAddr_Print(&local_addr, local_buffer); + BLog(BLOG_DEBUG, "Creating connection for %s.", local_buffer); // allocate structure - struct SocksUdpClient_connection *con = (struct SocksUdpClient_connection *)malloc(sizeof(*con)); + struct SocksUdpClient_connection *con = + (struct SocksUdpClient_connection *)BAlloc(sizeof(*con)); if (!con) { - BLog(BLOG_ERROR, "malloc failed"); + BLog(BLOG_ERROR, "BAlloc connection failed"); goto fail0; } - // init arguments + // set basic things con->client = o; con->local_addr = local_addr; + + // store first outgoing packet con->first_data = BAlloc(first_data_len); + if (!con->first_data) { + BLog(BLOG_ERROR, "BAlloc first data failed"); + goto fail1; + } + memcpy(con->first_data, first_data, first_data_len); con->first_data_len = first_data_len; con->first_remote_addr = first_remote_addr; - memcpy(con->first_data, first_data, first_data_len); + // Get the DNS transaction ID from the packet, if any. con->dns_id = get_dns_id(&first_remote_addr, first_data, first_data_len); BPendingGroup *pg = BReactor_PendingGroup(o->reactor); - // init first job, to send the first packet asynchronously. This has to happen asynchronously - // because con->send_writer (a BufferWriter) cannot accept writes until after it is linked with - // its PacketBuffer (con->send_buffer), which happens asynchronously. + // Init first job, to send the first packet asynchronously. This has to happen + // asynchronously because con->send_writer (a BufferWriter) cannot accept writes until + // after it is linked with its PacketBuffer (con->send_buffer), which happens + // asynchronously. BPending_Init(&con->first_job, pg, (BPending_handler)first_job_handler, con); - // Add the first job to the pending set. BPending acts as a LIFO stack, and first_job_handler - // needs to run after async actions that occur in PacketBuffer_Init, so we need to put first_job - // on the stack first. + // Add the first job to the pending set. BPending acts as a LIFO stack, and + // first_job_handler needs to run after async actions that occur in PacketBuffer_Init, + // so we need to put first_job on the stack first. BPending_Set(&con->first_job); // Create a datagram socket if (!BDatagram_Init(&con->socket, con->local_addr.type, o->reactor, con, - (BDatagram_handler)datagram_state_handler)) { + (BDatagram_handler)datagram_state_handler)) + { BLog(BLOG_ERROR, "Failed to create a UDP socket"); - goto fail1; - } - - // Bind to 127.0.0.1:0 (or [::1]:0). Port 0 signals the kernel to choose an open port. - BAddr socket_addr; - socket_addr.type = local_addr.type; - if (local_addr.type == BADDR_TYPE_IPV4) { - init_localhost4(&socket_addr.ipv4.ip); - socket_addr.ipv4.port = 0; - } else if (local_addr.type == BADDR_TYPE_IPV6) { - init_localhost6(socket_addr.ipv6.ip); - socket_addr.ipv6.port = 0; - } else { - BLog(BLOG_ERROR, "Unknown local address type"); 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 fail2; + goto fail3; } // Bind succeeded, so the kernel has found an open port. @@ -268,7 +308,7 @@ struct SocksUdpClient_connection *connection_init(SocksUdpClient *o, BAddr local uint16_t port; if (!BDatagram_GetLocalPort(&con->socket, &port)) { BLog(BLOG_ERROR, "Failed to get bound port"); - goto fail2; + goto fail3; } if (socket_addr.type == BADDR_TYPE_IPV4) { socket_addr.ipv4.port = port; @@ -277,60 +317,66 @@ struct SocksUdpClient_connection *connection_init(SocksUdpClient *o, BAddr local } // 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)) { + 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)) + { BLog(BLOG_ERROR, "Failed to initialize SOCKS client"); - goto fail2; + goto fail3; } - // Ensure that the UDP handling pipeline can handle queries big enough to include - // all data plus the SOCKS-UDP header. - int socks_mtu = compute_mtu(o->udp_mtu); - + // Since we use o->socks_mtu for send and receive pipelines, we can handle maximally + // sized packets (o->udp_mtu) including the SOCKS-UDP header. + // Send pipeline: send_writer -> send_buffer -> send_monitor -> send_if -> socket. - BDatagram_SendAsync_Init(&con->socket, socks_mtu); - PacketPassInterface *send_if = BDatagram_SendAsync_GetIf(&con->socket); - PacketPassInactivityMonitor_Init(&con->send_monitor, send_if, o->reactor, o->keepalive_time, - (PacketPassInactivityMonitor_handler)send_monitor_handler, con); - BufferWriter_Init(&con->send_writer, compute_mtu(o->udp_mtu), pg); + BDatagram_SendAsync_Init(&con->socket, o->socks_mtu); + PacketPassInactivityMonitor_Init(&con->send_monitor, + BDatagram_SendAsync_GetIf(&con->socket), o->reactor, o->keepalive_time, + (PacketPassInactivityMonitor_handler)send_monitor_handler, con); + BufferWriter_Init(&con->send_writer, o->socks_mtu, pg); if (!PacketBuffer_Init(&con->send_buffer, BufferWriter_GetOutput(&con->send_writer), - PacketPassInactivityMonitor_GetInput(&con->send_monitor), - SOCKS_UDP_SEND_BUFFER_PACKETS, pg)) { + PacketPassInactivityMonitor_GetInput(&con->send_monitor), o->send_buf_size, pg)) + { BLog(BLOG_ERROR, "Send buffer init failed"); - goto fail3; + goto fail4; } // Receive pipeline: socket -> recv_buffer -> recv_if - BDatagram_RecvAsync_Init(&con->socket, socks_mtu); - PacketPassInterface_Init(&con->recv_if, socks_mtu, - (PacketPassInterface_handler_send)recv_if_handler_send, con, pg); - if (!SinglePacketBuffer_Init(&con->recv_buffer, BDatagram_RecvAsync_GetIf(&con->socket), - &con->recv_if, pg)) { + BDatagram_RecvAsync_Init(&con->socket, o->socks_mtu); + PacketPassInterface_Init(&con->recv_if, o->socks_mtu, + (PacketPassInterface_handler_send)recv_if_handler_send, con, pg); + if (!SinglePacketBuffer_Init(&con->recv_buffer, + BDatagram_RecvAsync_GetIf(&con->socket), &con->recv_if, pg)) + { BLog(BLOG_ERROR, "Receive buffer init failed"); - goto fail4; + goto fail5; } - // insert to connections tree - ASSERT_EXECUTE(BAVL_Insert(&o->connections_tree, &con->connections_tree_node, NULL)) + // Insert to connections tree, it must succeed because of the assert. + int inserted = BAVL_Insert(&o->connections_tree, &con->connections_tree_node, NULL); + ASSERT(inserted) + B_USE(inserted) + // increment number of connections o->num_connections++; return con; -fail4: +fail5: PacketPassInterface_Free(&con->recv_if); BDatagram_RecvAsync_Free(&con->socket); PacketBuffer_Free(&con->send_buffer); -fail3: +fail4: BufferWriter_Free(&con->send_writer); PacketPassInactivityMonitor_Free(&con->send_monitor); BDatagram_SendAsync_Free(&con->socket); -fail2: + BSocksClient_Free(&con->socks); +fail3: BDatagram_Free(&con->socket); -fail1: +fail2: BPending_Free(&con->first_job); BFree(con->first_data); - free(con); +fail1: + BFree(con); fail0: return NULL; } @@ -338,44 +384,44 @@ struct SocksUdpClient_connection *connection_init(SocksUdpClient *o, BAddr local void connection_free (struct SocksUdpClient_connection *con) { SocksUdpClient *o = con->client; - DebugObject_Access(&o->d_obj); // decrement number of connections + ASSERT(o->num_connections > 0) o->num_connections--; // remove from connections tree BAVL_Remove(&o->connections_tree, &con->connections_tree_node); + // Free UDP receive pipeline components + SinglePacketBuffer_Free(&con->recv_buffer); + PacketPassInterface_Free(&con->recv_if); + BDatagram_RecvAsync_Free(&con->socket); + // Free UDP send pipeline components PacketBuffer_Free(&con->send_buffer); BufferWriter_Free(&con->send_writer); PacketPassInactivityMonitor_Free(&con->send_monitor); BDatagram_SendAsync_Free(&con->socket); - // Free UDP receive pipeline components - SinglePacketBuffer_Free(&con->recv_buffer); - PacketPassInterface_Free(&con->recv_if); - BDatagram_RecvAsync_Free(&con->socket); + // Free SOCKS client + BSocksClient_Free(&con->socks); // Free UDP socket BDatagram_Free(&con->socket); - // Free SOCKS client - BSocksClient_Free(&con->socks); - + // Free first job BPending_Free(&con->first_job); - if (con->first_data) { - BFree(con->first_data); - } - // free structure - free(con); + + // Free first outgoing packet + BFree(con->first_data); + + // Free structure + BFree(con); } -void connection_send (struct SocksUdpClient_connection *con, BAddr remote_addr, - const uint8_t *data, int data_len) +void connection_send (struct SocksUdpClient_connection *con, + BAddr remote_addr, const uint8_t *data, int data_len) { - SocksUdpClient *o = con->client; - DebugObject_Access(&o->d_obj); ASSERT(data_len >= 0) ASSERT(data_len <= o->udp_mtu) @@ -383,7 +429,7 @@ void connection_send (struct SocksUdpClient_connection *con, BAddr remote_addr, // So far, this connection has only sent a single DNS query. int new_dns_id = get_dns_id(&remote_addr, data, data_len); if (new_dns_id != con->dns_id) { - BLog(BLOG_DEBUG, "Client reused DNS query port. Disabling DNS optimization."); + BLog(BLOG_DEBUG, "Client reused DNS query port. Disabling DNS optimization."); con->dns_id = -1; } } @@ -402,128 +448,174 @@ void connection_send (struct SocksUdpClient_connection *con, BAddr remote_addr, address_size = sizeof(struct socks_addr_ipv6); } break; default: { - BLog(BLOG_ERROR, "bad address type"); - return; - } + BLog(BLOG_ERROR, "Bad address type in outgoing packet."); + return; + } break; } - // Wrap the payload in a UDP SOCKS header. - size_t socks_data_len = sizeof(struct socks_udp_header) + address_size + data_len; - if (socks_data_len > compute_mtu(o->udp_mtu)) { - BLog(BLOG_ERROR, "Packet is too big: %d > %d", socks_data_len, compute_mtu(o->udp_mtu)); - return; - } - uint8_t *socks_data; - if (!BufferWriter_StartPacket(&con->send_writer, &socks_data)) { - BLog(BLOG_ERROR, "Send buffer is full"); + // Determine total packet size in the buffer. + // This cannot exceed o->socks_mtu because data_len is required to not exceed + // o->udp_mtu and o->socks_mtu is calculated to accomodate any UDP packet not + // not exceeding o->udp_mtu. + size_t total_len = sizeof(struct socks_udp_header) + address_size + data_len; + ASSERT(total_len <= o->socks_mtu) + + // Get a pointer to write the packet to. + uint8_t *out_data_begin; + if (!BufferWriter_StartPacket(&con->send_writer, &out_data_begin)) { + BLog(BLOG_ERROR, "Send buffer is full."); return; } + uint8_t *out_data = out_data_begin; + // Write header - struct socks_udp_header *header = (struct socks_udp_header *)socks_data; - header->rsv = 0; - header->frag = 0; - header->atyp = atyp; - uint8_t *addr_data = socks_data + sizeof(struct socks_udp_header); + struct socks_udp_header header; + header.rsv = 0; + header.frag = 0; + header.atyp = atyp; + memcpy(out_data, &header, sizeof(header)); + out_data += sizeof(header); + + // Write address switch (atyp) { case SOCKS_ATYP_IPV4: { - struct socks_addr_ipv4 *addr_ipv4 = (struct socks_addr_ipv4 *)addr_data; - addr_ipv4->addr = remote_addr.ipv4.ip; - addr_ipv4->port = remote_addr.ipv4.port; + struct socks_addr_ipv4 addr_ipv4; + addr_ipv4.addr = remote_addr.ipv4.ip; + addr_ipv4.port = remote_addr.ipv4.port; + memcpy(out_data, &addr_ipv4, sizeof(addr_ipv4)); + out_data += sizeof(addr_ipv4); } break; case SOCKS_ATYP_IPV6: { - struct socks_addr_ipv6 *addr_ipv6 = (struct socks_addr_ipv6 *)addr_data; - memcpy(addr_ipv6->addr, remote_addr.ipv6.ip, sizeof(addr_ipv6->addr)); - addr_ipv6->port = remote_addr.ipv6.port; + struct socks_addr_ipv6 addr_ipv6; + memcpy(addr_ipv6.addr, remote_addr.ipv6.ip, sizeof(addr_ipv6.addr)); + addr_ipv6.port = remote_addr.ipv6.port; + memcpy(out_data, &addr_ipv6, sizeof(addr_ipv6)); + out_data += sizeof(addr_ipv6); } break; } - // write packet to buffer - memcpy(addr_data + address_size, data, data_len); - BufferWriter_EndPacket(&con->send_writer, socks_data_len); + + // Write payload + memcpy(out_data, data, data_len); + out_data += data_len; + + ASSERT(out_data - out_data_begin == total_len) + + // Submit packet to buffer + BufferWriter_EndPacket(&con->send_writer, total_len); } -void first_job_handler(struct SocksUdpClient_connection *con) +void first_job_handler (struct SocksUdpClient_connection *con) { + DebugObject_Access(&con->client->d_obj); + ASSERT(con->first_data) + + // Send the first packet. connection_send(con, con->first_remote_addr, con->first_data, con->first_data_len); + + // Release the first packet buffer. BFree(con->first_data); con->first_data = NULL; con->first_data_len = 0; } -int compute_mtu(int udp_mtu) +int compute_socks_mtu (int udp_mtu) { - return udp_mtu + sizeof(struct socks_udp_header) + sizeof(struct socks_addr_ipv6); + bsize_t bs = bsize_add( + bsize_fromint(udp_mtu), + bsize_add( + bsize_fromsize(sizeof(struct socks_udp_header)), + bsize_fromsize(sizeof(struct socks_addr_ipv6)) + ) + ); + int s; + return bsize_toint(bs, &s) ? s : -1; } -int get_dns_id(BAddr *remote_addr, const uint8_t *data, int data_len) +// Get the DNS transaction ID, or -1 if this does not look like a DNS packet. +int get_dns_id (BAddr *remote_addr, const uint8_t *data, int data_len) { - if (BAddr_GetPort(remote_addr) == htons(DNS_PORT) && data_len >= 2) { - return (data[0] << 8) + data[1]; + if (ntoh16(BAddr_GetPort(remote_addr)) == DnsPort && data_len >= 2) { + return (data[0] << 8) | data[1]; + } else { + return -1; } - return -1; } -void SocksUdpClient_Init (SocksUdpClient *o, int udp_mtu, int max_connections, btime_t keepalive_time, - BAddr server_addr, const struct BSocksClient_auth_info *auth_info, size_t num_auth_info, - BReactor *reactor, void *user, - SocksUdpClient_handler_received handler_received) +int SocksUdpClient_Init (SocksUdpClient *o, int udp_mtu, int max_connections, + int send_buf_size, btime_t keepalive_time, BAddr server_addr, + const struct BSocksClient_auth_info *auth_info, size_t num_auth_info, + BReactor *reactor, void *user, SocksUdpClient_handler_received handler_received) { ASSERT(udp_mtu >= 0) - ASSERT(compute_mtu(udp_mtu) >= 0) ASSERT(max_connections > 0) + ASSERT(send_buf_size > 0) - // init arguments + // init simple things o->server_addr = server_addr; o->auth_info = auth_info; o->num_auth_info = num_auth_info; - o->udp_mtu = udp_mtu; - o->max_connections = max_connections; o->num_connections = 0; + o->max_connections = max_connections; + o->send_buf_size = send_buf_size; + o->udp_mtu = udp_mtu; o->keepalive_time = keepalive_time; o->reactor = reactor; o->user = user; o->handler_received = handler_received; - - // limit max connections to number of conid's - if (o->max_connections > UINT16_MAX + 1) { - o->max_connections = UINT16_MAX + 1; + + // calculate full MTU with SOCKS header + o->socks_mtu = compute_socks_mtu(udp_mtu); + if (o->socks_mtu < 0) { + BLog(BLOG_ERROR, "SocksUdpClient_Init: MTU too large."); + goto fail0; } // init connections tree - BAVL_Init(&o->connections_tree, OFFSET_DIFF(struct SocksUdpClient_connection, local_addr, connections_tree_node), (BAVL_comparator)addr_comparator, NULL); + BAVL_Init(&o->connections_tree, + OFFSET_DIFF(struct SocksUdpClient_connection, local_addr, connections_tree_node), + (BAVL_comparator)addr_comparator, NULL); DebugObject_Init(&o->d_obj); + return 1; + +fail0: + return 0; } void SocksUdpClient_Free (SocksUdpClient *o) { + DebugObject_Free(&o->d_obj); + // free connections while (!BAVL_IsEmpty(&o->connections_tree)) { - struct SocksUdpClient_connection *con = UPPER_OBJECT(BAVL_GetFirst(&o->connections_tree), struct SocksUdpClient_connection, connections_tree_node); + BAVLNode *node = BAVL_GetFirst(&o->connections_tree); + struct SocksUdpClient_connection *con = + UPPER_OBJECT(node, struct SocksUdpClient_connection, connections_tree_node); connection_free(con); } - - DebugObject_Free(&o->d_obj); } -void SocksUdpClient_SubmitPacket (SocksUdpClient *o, BAddr local_addr, BAddr remote_addr, const uint8_t *data, int data_len) +void SocksUdpClient_SubmitPacket (SocksUdpClient *o, + BAddr local_addr, BAddr remote_addr, const uint8_t *data, int data_len) { DebugObject_Access(&o->d_obj); ASSERT(local_addr.type == BADDR_TYPE_IPV4 || local_addr.type == BADDR_TYPE_IPV6) ASSERT(remote_addr.type == BADDR_TYPE_IPV4 || remote_addr.type == BADDR_TYPE_IPV6) ASSERT(data_len >= 0) + ASSERT(data_len <= o->udp_mtu) // lookup connection - struct SocksUdpClient_connection *con = find_connection_by_addr(o, local_addr); + struct SocksUdpClient_connection *con = find_connection(o, local_addr); if (!con) { - if (o->num_connections == o->max_connections) { + if (o->num_connections >= o->max_connections) { // Drop the packet. - BLog(BLOG_ERROR, "Dropping UDP packet, reached max number of connections."); + BLog(BLOG_WARNING, "Dropping UDP packet, reached max number of connections."); return; } // create new connection and enqueue the packet connection_init(o, local_addr, remote_addr, data, data_len); } else { - // send packet - connection_send(con, remote_addr, data, data_len); + // send packet + connection_send(con, remote_addr, data, data_len); } } diff --git a/socks_udp_client/SocksUdpClient.h b/socks_udp_client/SocksUdpClient.h index 4e0409ec3..b81c2ea9e 100644 --- a/socks_udp_client/SocksUdpClient.h +++ b/socks_udp_client/SocksUdpClient.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2018 Jigsaw Operations LLC + * Copyright (C) 2019 Ambroz Bizjak (modifications) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -27,6 +28,7 @@ #ifndef BADVPN_SOCKS_UDP_CLIENT_SOCKSUDPCLIENT_H #define BADVPN_SOCKS_UDP_CLIENT_SOCKSUDPCLIENT_H +#include #include #include @@ -34,9 +36,8 @@ #include #include #include +#include #include -#include -#include #include #include #include @@ -44,12 +45,8 @@ #include #include -// This sets the number of packets to accept while waiting for SOCKS server to authenticate and -// connect. A slow or far-away SOCKS server could require 300 ms to connect, and a chatty -// client (e.g. STUN) could send a packet every 20 ms, so a limit of 16 seems reasonable. -#define SOCKS_UDP_SEND_BUFFER_PACKETS 16 - -typedef void (*SocksUdpClient_handler_received) (void *user, BAddr local_addr, BAddr remote_addr, const uint8_t *data, int data_len); +typedef void (*SocksUdpClient_handler_received) ( + void *user, BAddr local_addr, BAddr remote_addr, const uint8_t *data, int data_len); typedef struct { BAddr server_addr; @@ -57,7 +54,9 @@ typedef struct { size_t num_auth_info; int num_connections; int max_connections; + int send_buf_size; int udp_mtu; + int socks_mtu; btime_t keepalive_time; BReactor *reactor; void *user; @@ -77,8 +76,8 @@ struct SocksUdpClient_connection { BDatagram socket; PacketPassInterface recv_if; SinglePacketBuffer recv_buffer; - // The first_* members represent the initial packet, which has to be stored so it can wait for - // send_writer to become ready. + // The first_* members represent the initial packet, which has to be stored so it can + // wait for send_writer to become ready. uint8_t *first_data; int first_data_len; BAddr first_remote_addr; @@ -92,43 +91,59 @@ struct SocksUdpClient_connection { /** * Initializes the SOCKS5-UDP client object. - * This function does not perform network access, so it will always succeed if the arguments - * are valid. + * + * 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. + * 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 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. * @param reactor reactor we live in * @param user value passed to handler * @param handler_received handler for incoming UDP packets + * @return 1 on success, 0 on failure + */ +int SocksUdpClient_Init (SocksUdpClient *o, int udp_mtu, int max_connections, + int send_buf_size, btime_t keepalive_time, BAddr server_addr, + const struct BSocksClient_auth_info *auth_info, size_t num_auth_info, + BReactor *reactor, void *user, SocksUdpClient_handler_received handler_received); + +/** + * Frees the SOCKS5-UDP client object. + * + * @param o the object */ -void SocksUdpClient_Init (SocksUdpClient *o, int udp_mtu, int max_connections, btime_t keepalive_time, - BAddr server_addr, const struct BSocksClient_auth_info *auth_info, size_t num_auth_info, - BReactor *reactor, void *user, SocksUdpClient_handler_received handler_received); void SocksUdpClient_Free (SocksUdpClient *o); /** * Submit a packet to be sent through the proxy. * * This will reuse an existing connection for packets from local_addr, or create one if - * there is none. If the number of live connections exceeds max_connections, or if the number of - * buffered packets from this port exceeds a limit, packets will be dropped silently. + * there is none. If the number of live connections exceeds max_connections, or if the + * number of buffered packets from this port exceeds a limit, packets will be dropped + * silently. * - * As a resource optimization, if a connection has only been used to send one DNS query, then - * the connection will be closed and freed once the reply is received. + * As a resource optimization, if a connection has only been used to send one DNS query, + * then the connection will be closed and freed once the reply is received. * * @param o the object - * @param local_addr the UDP packet's source address, and the expected destination for replies + * @param local_addr the UDP packet's source address, and the expected destination for + * replies * @param remote_addr the destination of the packet after it exits the proxy - * @param data the packet contents. Caller retains ownership. + * @param data the packet contents. Caller retains ownership. + * @param data_len number of bytes in the data */ -void SocksUdpClient_SubmitPacket (SocksUdpClient *o, BAddr local_addr, BAddr remote_addr, const uint8_t *data, int data_len); +void SocksUdpClient_SubmitPacket (SocksUdpClient *o, + BAddr local_addr, BAddr remote_addr, const uint8_t *data, int data_len); #endif diff --git a/socksclient/BSocksClient.c b/socksclient/BSocksClient.c index a0cae06f9..f068215b3 100644 --- a/socksclient/BSocksClient.c +++ b/socksclient/BSocksClient.c @@ -375,14 +375,16 @@ void recv_handler_done (BSocksClient *o, int data_len) void *addr_buffer = o->buffer + sizeof(struct socks_reply_header); switch (o->bind_addr.type) { case BADDR_TYPE_IPV4: { - struct socks_addr_ipv4 *ip4 = addr_buffer; - o->bind_addr.ipv4.ip = ip4->addr; - o->bind_addr.ipv4.port = ip4->port; + struct socks_addr_ipv4 ip4; + memcpy(&ip4, addr_buffer, sizeof(ip4)); + o->bind_addr.ipv4.ip = ip4.addr; + o->bind_addr.ipv4.port = ip4.port; } break; case BADDR_TYPE_IPV6: { - struct socks_addr_ipv6 *ip6 = addr_buffer; - memcpy(o->bind_addr.ipv6.ip, ip6->addr, sizeof(ip6->addr)); - o->bind_addr.ipv6.port = ip6->port; + struct socks_addr_ipv6 ip6; + memcpy(&ip6, addr_buffer, sizeof(ip6)); + memcpy(o->bind_addr.ipv6.ip, ip6.addr, sizeof(ip6.addr)); + o->bind_addr.ipv6.port = ip6.port; } break; default: ASSERT(0); } @@ -395,6 +397,8 @@ void recv_handler_done (BSocksClient *o, int data_len) free_control_io(o); // init up I/O + // Initializing this is not needed for UDP ASSOCIATE but it doesn't hurt. + // We anyway don't allow the user to use these interfaces in that case. init_up_io(o); // set state @@ -614,9 +618,18 @@ void BSocksClient_Free (BSocksClient *o) } } +BAddr BSocksClient_GetBindAddr (BSocksClient *o) +{ + ASSERT(o->state == STATE_UP) + DebugObject_Access(&o->d_obj); + + return o->bind_addr; +} + StreamPassInterface * BSocksClient_GetSendInterface (BSocksClient *o) { ASSERT(o->state == STATE_UP) + ASSERT(!o->udp) DebugObject_Access(&o->d_obj); return BConnection_SendAsync_GetIf(&o->con); @@ -625,6 +638,7 @@ StreamPassInterface * BSocksClient_GetSendInterface (BSocksClient *o) StreamRecvInterface * BSocksClient_GetRecvInterface (BSocksClient *o) { ASSERT(o->state == STATE_UP) + ASSERT(!o->udp) DebugObject_Access(&o->d_obj); return BConnection_RecvAsync_GetIf(&o->con); diff --git a/socksclient/BSocksClient.h b/socksclient/BSocksClient.h index 7bb775472..09368071d 100644 --- a/socksclient/BSocksClient.h +++ b/socksclient/BSocksClient.h @@ -34,6 +34,7 @@ #ifndef BADVPN_SOCKS_BSOCKSCLIENT_H #define BADVPN_SOCKS_BSOCKSCLIENT_H +#include #include #include @@ -112,7 +113,14 @@ struct BSocksClient_auth_info BSocksClient_auth_password (const char *username, * * @param o the object * @param server_addr SOCKS5 server address + * @param auth_info List of supported authentication methods and associated parameters. + * Initialize these using functions such as BSocksClient_auth_none() and + * BSocksClient_auth_password(). The pointer must remain valid while this object + * exists, the data is not copied. + * @param num_auth_info Number of the above. There should be at least one, otherwise it + * certainly won't work. * @param dest_addr remote address + * @param udp whether to do UDP ASSOCIATE instead of CONNECT * @param handler handler for up and error events * @param user value passed to handler * @param reactor reactor we live in @@ -129,9 +137,20 @@ int BSocksClient_Init (BSocksClient *o, */ void BSocksClient_Free (BSocksClient *o); +/** + * Return the bind address that the SOCKS server reported. + * The object must be in up state. The bind address is needed for UDP ASSOCIATE + * because it is the address that the client should send UDP packets to. + * + * @param o the object + * @return The bind address, of type BADDR_TYPE_IPV4 or BADDR_TYPE_IPV6. + */ +BAddr BSocksClient_GetBindAddr (BSocksClient *o); + /** * Returns the send interface. - * The object must be in up state. + * The object must be in up state. Additionally this must not be called if the + * object was initialized in UDP ASSOCIATE mode. * * @param o the object * @return send interface @@ -140,7 +159,8 @@ StreamPassInterface * BSocksClient_GetSendInterface (BSocksClient *o); /** * Returns the receive interface. - * The object must be in up state. + * The object must be in up state. Additionally this must not be called if the + * object was initialized in UDP ASSOCIATE mode. * * @param o the object * @return receive interface diff --git a/system/BAddr.h b/system/BAddr.h index a5f3f10b9..70d4fa1a1 100644 --- a/system/BAddr.h +++ b/system/BAddr.h @@ -97,6 +97,8 @@ static int BIPAddr_Resolve (BIPAddr *addr, char *str, int noresolve) WARN_UNUSED static int BIPAddr_Compare (BIPAddr *addr1, BIPAddr *addr2); +static void BIPAddr_InitLocalhost (BIPAddr *addr, int addr_type); + /** * Converts an IP address to human readable form. * @@ -805,4 +807,20 @@ int BAddr_CompareOrder (BAddr *addr1, BAddr *addr2) } } +void BIPAddr_InitLocalhost (BIPAddr *addr, int addr_type) +{ + if (addr_type == BADDR_TYPE_IPV4) { + addr->type = addr_type; + addr->ipv4 = hton32(0x7f000001); + } + else if (addr_type == BADDR_TYPE_IPV6) { + addr->type = addr_type; + memset(addr->ipv6, 0, 16); + addr->ipv6[15] = 1; + } + else { + addr->type = BADDR_TYPE_NONE; + } +} + #endif diff --git a/system/BDatagram.h b/system/BDatagram.h index 63e55ecc3..9b9b5c24e 100644 --- a/system/BDatagram.h +++ b/system/BDatagram.h @@ -124,8 +124,22 @@ void BDatagram_SetSendAddrs (BDatagram *o, BAddr remote_addr, BIPAddr local_addr int BDatagram_GetLastReceiveAddrs (BDatagram *o, BAddr *remote_addr, BIPAddr *local_addr); /** - * Returns the bound port. - * Fails if and only if a port is not yet bound. + * Determines the local address. + * + * This calls getsockname() to determine the local address and returns the result as + * BAddr. This function fails if the address cannot be determined or translated to + * BAddr (it never succeeds but returns a BADDR_TYPE_NONE address). + * + * @param o the object + * @param local_addr returns the local bound address. + * @return 1 on success, 0 on failure + */ +int BDatagram_GetLocalAddr (BDatagram *o, BAddr *local_addr); + +/** + * Returns the local port. + * + * This is a convenience function implemented based on BDatagram_GetLocalAddr. * * @param o the object * @param local_port returns the local bound port. diff --git a/system/BDatagram_common.c b/system/BDatagram_common.c new file mode 100644 index 000000000..542114060 --- /dev/null +++ b/system/BDatagram_common.c @@ -0,0 +1,52 @@ +/** + * @file BDatagram_unix.c + * @author Ambroz Bizjak + * + * @section LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include "BDatagram.h" + +#include + +int BDatagram_GetLocalPort (BDatagram *o, uint16_t *local_port) +{ + BAddr addr; + if (!BDatagram_GetLocalAddr(o, &addr)) { + return 0; + } + + if (addr.type != BADDR_TYPE_IPV4 && addr.type != BADDR_TYPE_IPV6) { + BLog(BLOG_ERROR, + "BDatagram_GetLocalPort: Port not defined for this address type."); + return 0; + } + + *local_port = BAddr_GetPort(&addr); + return 1; +} diff --git a/system/BDatagram_unix.c b/system/BDatagram_unix.c index a6e42e37a..61b8f8d90 100644 --- a/system/BDatagram_unix.c +++ b/system/BDatagram_unix.c @@ -734,28 +734,28 @@ int BDatagram_GetLastReceiveAddrs (BDatagram *o, BAddr *remote_addr, BIPAddr *lo return 1; } -int BDatagram_GetLocalPort (BDatagram *o, uint16_t *local_port) +int BDatagram_GetLocalAddr (BDatagram *o, BAddr *local_addr) { DebugObject_Access(&o->d_obj); struct sys_addr sysaddr; - BAddr addr; sysaddr.len = sizeof(sysaddr.addr); if (getsockname(o->fd, &sysaddr.addr.generic, &sysaddr.len) != 0) { - BLog(BLOG_ERROR, "getsockname failed"); + BLog(BLOG_ERROR, "BDatagram_GetLocalAddr: getsockname failed"); return 0; } + + BAddr addr; addr_sys_to_socket(&addr, sysaddr); - if (addr.type == BADDR_TYPE_IPV4) { - *local_port = addr.ipv4.port; - return 1; - } - if (addr.type == BADDR_TYPE_IPV6) { - *local_port = addr.ipv6.port; - return 1; + + if (addr.type == BADDR_TYPE_NONE) { + BLog(BLOG_ERROR, "BDatagram_GetLocalAddr: Unsupported address family " + "from getsockname: %d", (int)sysaddr.addr.generic.sa_family); + return 0; } - BLog(BLOG_ERROR, "Unknown address type from getsockname: %d", addr.type); - return 0; + + *local_addr = addr; + return 1; } int BDatagram_GetFd (BDatagram *o) diff --git a/system/BDatagram_win.c b/system/BDatagram_win.c index 2cdc9c833..3fd637059 100644 --- a/system/BDatagram_win.c +++ b/system/BDatagram_win.c @@ -635,29 +635,29 @@ int BDatagram_GetLastReceiveAddrs (BDatagram *o, BAddr *remote_addr, BIPAddr *lo return 1; } -int BDatagram_GetLocalPort (BDatagram *o, uint16_t *local_port) +int BDatagram_GetLocalAddr (BDatagram *o, BAddr *local_addr) { DebugObject_Access(&o->d_obj); struct BDatagram_sys_addr sysaddr; - BAddr addr; socklen_t addr_size = sizeof(sysaddr.addr.generic); if (getsockname(o->sock, &sysaddr.addr.generic, &addr_size) != 0) { - BLog(BLOG_ERROR, "getsockname failed"); + BLog(BLOG_ERROR, "BDatagram_GetLocalAddr: getsockname failed"); return 0; } + sysaddr.len = addr_size; + BAddr addr; addr_sys_to_socket(&addr, sysaddr); - if (addr.type == BADDR_TYPE_IPV4) { - *local_port = addr.ipv4.port; - return 1; - } - if (addr.type == BADDR_TYPE_IPV6) { - *local_port = addr.ipv6.port; - return 1; + + if (addr.type == BADDR_TYPE_NONE) { + BLog(BLOG_ERROR, "BDatagram_GetLocalAddr: Unsupported address family " + "from getsockname: %d", int(sysaddr.addr.generic.sa_family)); + return 0; } - BLog(BLOG_ERROR, "Unknown address type from getsockname: %d", addr.type); - return 0; + + *local_addr = addr; + return 1; } int BDatagram_SetReuseAddr (BDatagram *o, int reuse) diff --git a/system/CMakeLists.txt b/system/CMakeLists.txt index 796dc27c0..fb41acb3e 100644 --- a/system/CMakeLists.txt +++ b/system/CMakeLists.txt @@ -6,6 +6,7 @@ if (NOT EMSCRIPTEN) BSignal.c BNetwork.c BConnection_common.c + BDatagram_common.c ) if (WIN32) diff --git a/tun2socks/SocksUdpGwClient.c b/tun2socks/SocksUdpGwClient.c index 5ce68d9e5..0d38e2220 100644 --- a/tun2socks/SocksUdpGwClient.c +++ b/tun2socks/SocksUdpGwClient.c @@ -62,7 +62,10 @@ static void try_connect (SocksUdpGwClient *o) ASSERT(!BTimer_IsRunning(&o->reconnect_timer)) // init SOCKS client - if (!BSocksClient_Init(&o->socks_client, o->socks_server_addr, o->auth_info, o->num_auth_info, o->remote_udpgw_addr, false, (BSocksClient_handler)socks_client_handler, o, o->reactor)) { + if (!BSocksClient_Init(&o->socks_client, o->socks_server_addr, + o->auth_info, o->num_auth_info, o->remote_udpgw_addr, /*udp=*/false, + (BSocksClient_handler)socks_client_handler, o, o->reactor)) + { BLog(BLOG_ERROR, "BSocksClient_Init failed"); goto fail0; } diff --git a/tun2socks/tun2socks.c b/tun2socks/tun2socks.c index bf951ec5a..12157ca4d 100644 --- a/tun2socks/tun2socks.c +++ b/tun2socks/tun2socks.c @@ -181,6 +181,10 @@ uint8_t *device_write_buf; SinglePacketBuffer device_read_buffer; PacketPassInterface device_read_interface; +// UDP support mode +enum UdpMode {UdpModeNone, UdpModeUdpgw, UdpModeSocks}; +enum UdpMode udp_mode; + // udpgw client SocksUdpGwClient udpgw_client; int udp_mtu; @@ -249,7 +253,6 @@ static int client_socks_recv_send_out (struct tcp_client *client); static err_t client_sent_func (void *arg, struct tcp_pcb *tpcb, u16_t len); static void udp_send_packet_to_device (void *unused, BAddr local_addr, BAddr remote_addr, const uint8_t *data, int data_len); - int main (int argc, char **argv) { if (argc <= 0) { @@ -357,7 +360,8 @@ int main (int argc, char **argv) goto fail4; } - // compute maximum UDP payload size we need to pass through udpgw + // Compute the largest possible UDP payload that we can receive from or send to the + // TUN device. udp_mtu = BTap_GetMTU(&device) - (int)(sizeof(struct ipv4_header) + sizeof(struct udp_header)); if (options.netif_ip6addr) { int udp_ip6_mtu = BTap_GetMTU(&device) - (int)(sizeof(struct ipv6_header) + sizeof(struct udp_header)); @@ -370,6 +374,8 @@ int main (int argc, char **argv) } if (options.udpgw_remote_server_addr) { + udp_mode = UdpModeUdpgw; + // make sure our UDP payloads aren't too large for udpgw int udpgw_mtu = udpgw_compute_mtu(udp_mtu); if (udpgw_mtu < 0 || udpgw_mtu > PACKETPROTO_MAXPAYLOAD) { @@ -378,17 +384,23 @@ int main (int argc, char **argv) } // init udpgw client - if (!SocksUdpGwClient_Init(&udpgw_client, udp_mtu, DEFAULT_UDPGW_MAX_CONNECTIONS, options.udpgw_connection_buffer_size, UDPGW_KEEPALIVE_TIME, - socks_server_addr, socks_auth_info, socks_num_auth_info, - udpgw_remote_server_addr, UDPGW_RECONNECT_TIME, &ss, NULL, udp_send_packet_to_device - )) { + if (!SocksUdpGwClient_Init(&udpgw_client, udp_mtu, DEFAULT_UDPGW_MAX_CONNECTIONS, + options.udpgw_connection_buffer_size, UDPGW_KEEPALIVE_TIME, socks_server_addr, + socks_auth_info, socks_num_auth_info, udpgw_remote_server_addr, + UDPGW_RECONNECT_TIME, &ss, NULL, udp_send_packet_to_device)) + { BLog(BLOG_ERROR, "SocksUdpGwClient_Init failed"); goto fail4a; } } else if (options.socks5_udp) { + udp_mode = UdpModeSocks; + + // init SOCKS UDP client SocksUdpClient_Init(&socks_udp_client, udp_mtu, DEFAULT_UDPGW_MAX_CONNECTIONS, - UDPGW_KEEPALIVE_TIME, socks_server_addr, socks_auth_info, - socks_num_auth_info, &ss, NULL, udp_send_packet_to_device); + SOCKS_UDP_SEND_BUFFER_PACKETS, UDPGW_KEEPALIVE_TIME, socks_server_addr, + socks_auth_info, socks_num_auth_info, &ss, NULL, udp_send_packet_to_device); + } else { + udp_mode = UdpModeNone; } // init lwip init job @@ -448,9 +460,9 @@ int main (int argc, char **argv) BFree(device_write_buf); fail5: BPending_Free(&lwip_init_job); - if (options.udpgw_remote_server_addr) { + if (udp_mode == UdpModeUdpgw) { SocksUdpGwClient_Free(&udpgw_client); - } else if (options.socks5_udp) { + } else if (udp_mode == UdpModeSocks) { SocksUdpClient_Free(&socks_udp_client); } fail4a: @@ -1066,8 +1078,8 @@ int process_device_udp_packet (uint8_t *data, int data_len) { ASSERT(data_len >= 0) - // do nothing if we don't have udpgw - if (!options.udpgw_remote_server_addr && !options.socks5_udp) { + // do nothing if we don't use udpgw or SOCKS UDP + if (udp_mode == UdpModeNone) { goto fail; } @@ -1172,11 +1184,11 @@ int process_device_udp_packet (uint8_t *data, int data_len) goto fail; } - if (options.udpgw_remote_server_addr) { - // submit packet to udpgw + // submit packet to udpgw or SOCKS UDP + if (udp_mode == UdpModeUdpgw) { SocksUdpGwClient_SubmitPacket(&udpgw_client, local_addr, remote_addr, is_dns, data, data_len); - } else if (options.socks5_udp) { + } else if (udp_mode == UdpModeSocks) { SocksUdpClient_SubmitPacket(&socks_udp_client, local_addr, remote_addr, data, data_len); } @@ -1326,8 +1338,10 @@ err_t listener_accept_func (void *arg, struct tcp_pcb *newpcb, err_t err) } // init SOCKS - if (!BSocksClient_Init(&client->socks_client, socks_server_addr, socks_auth_info, socks_num_auth_info, - addr, false, (BSocksClient_handler)client_socks_handler, client, &ss)) { + if (!BSocksClient_Init(&client->socks_client, + socks_server_addr, socks_auth_info, socks_num_auth_info, addr, /*udp=*/false, + (BSocksClient_handler)client_socks_handler, client, &ss)) + { BLog(BLOG_ERROR, "listener accept: BSocksClient_Init failed"); goto fail1; } @@ -1840,15 +1854,18 @@ err_t client_sent_func (void *arg, struct tcp_pcb *tpcb, u16_t len) void udp_send_packet_to_device (void *unused, BAddr local_addr, BAddr remote_addr, const uint8_t *data, int data_len) { + ASSERT(udp_mode != UdpModeNone) ASSERT(local_addr.type == BADDR_TYPE_IPV4 || local_addr.type == BADDR_TYPE_IPV6) ASSERT(local_addr.type == remote_addr.type) ASSERT(data_len >= 0) + + char const *source_name = (udp_mode == UdpModeUdpgw) ? "udpgw" : "SOCKS UDP"; int packet_length = 0; switch (local_addr.type) { case BADDR_TYPE_IPV4: { - BLog(BLOG_INFO, "UDP: from udpgw %d bytes", data_len); + BLog(BLOG_INFO, "UDP: from %s %d bytes", source_name, data_len); if (data_len > UINT16_MAX - (sizeof(struct ipv4_header) + sizeof(struct udp_header)) || data_len > BTap_GetMTU(&device) - (int)(sizeof(struct ipv4_header) + sizeof(struct udp_header)) @@ -1887,10 +1904,10 @@ void udp_send_packet_to_device (void *unused, BAddr local_addr, BAddr remote_add } break; case BADDR_TYPE_IPV6: { - BLog(BLOG_INFO, "UDP/IPv6: from udpgw %d bytes", data_len); + BLog(BLOG_INFO, "UDP/IPv6: from %s %d bytes", source_name, data_len); if (!options.netif_ip6addr) { - BLog(BLOG_ERROR, "got IPv6 packet from udpgw but IPv6 is disabled"); + BLog(BLOG_ERROR, "got IPv6 packet from %s but IPv6 is disabled", source_name); return; } diff --git a/tun2socks/tun2socks.h b/tun2socks/tun2socks.h index caf577827..27d412949 100644 --- a/tun2socks/tun2socks.h +++ b/tun2socks/tun2socks.h @@ -44,3 +44,9 @@ // option to override the destination addresses to give the SOCKS server //#define OVERRIDE_DEST_ADDR "10.111.0.2:2000" + +// Max number of buffered outgoing UDP packets for SOCKS5-UDP. It should be large +// enough to prevent packet loss while the SOCKS UDP association is being set up. A slow +// or far-away SOCKS server could require 300 ms to connect, and a chatty client (e.g. +// STUN) could send a packet every 20 ms, so a default limit of 16 seems reasonable. +#define SOCKS_UDP_SEND_BUFFER_PACKETS 16 From e837e8d552156e26ed52a67923a91268d6e1192a Mon Sep 17 00:00:00 2001 From: Ambroz Bizjak Date: Sat, 1 Feb 2020 14:35:35 -0800 Subject: [PATCH 05/11] tun2socks: Remove localhost limination for SOCKS5 UDP. 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. --- socks_udp_client/SocksUdpClient.c | 81 +++++++++++--------- socks_udp_client/SocksUdpClient.h | 7 +- socksclient/BSocksClient.c | 121 +++++++++++++++++++++--------- socksclient/BSocksClient.h | 77 ++++++++++++++----- system/BConnection.h | 13 ++++ system/BConnection_unix.c | 24 ++++++ system/BConnection_win.c | 25 ++++++ tun2socks/SocksUdpGwClient.c | 2 - tun2socks/tun2socks.c | 3 - 9 files changed, 254 insertions(+), 99 deletions(-) diff --git a/socks_udp_client/SocksUdpClient.c b/socks_udp_client/SocksUdpClient.c index a7446da6e..661d351fc 100644 --- a/socks_udp_client/SocksUdpClient.c +++ b/socks_udp_client/SocksUdpClient.c @@ -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: { @@ -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; } } @@ -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; diff --git a/socks_udp_client/SocksUdpClient.h b/socks_udp_client/SocksUdpClient.h index b81c2ea9e..52ded104a 100644 --- a/socks_udp_client/SocksUdpClient.h +++ b/socks_udp_client/SocksUdpClient.h @@ -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. diff --git a/socksclient/BSocksClient.c b/socksclient/BSocksClient.c index f068215b3..e87bf5475 100644 --- a/socksclient/BSocksClient.c +++ b/socksclient/BSocksClient.c @@ -38,14 +38,15 @@ #include #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); @@ -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); @@ -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)), @@ -181,7 +216,7 @@ void connector_handler (BSocksClient* o, int is_error) ) ); if (!reserve_buffer(o, size)) { - goto fail1; + goto fail0; } // write hello header @@ -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; } @@ -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); @@ -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 || @@ -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)) { @@ -588,6 +617,7 @@ int BSocksClient_Init (BSocksClient *o, return 1; fail0: + BPending_Free(&o->continue_job); return 0; } @@ -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) diff --git a/socksclient/BSocksClient.h b/socksclient/BSocksClient.h index 09368071d..5311e4021 100644 --- a/socksclient/BSocksClient.h +++ b/socksclient/BSocksClient.h @@ -43,24 +43,33 @@ #include #include #include +#include #include #include #define BSOCKSCLIENT_EVENT_ERROR 1 #define BSOCKSCLIENT_EVENT_UP 2 #define BSOCKSCLIENT_EVENT_ERROR_CLOSED 3 +#define BSOCKSCLIENT_EVENT_CONNECTED 4 /** * Handler for events generated by the SOCKS client. * + * The event is one of the following: + * - BSOCKSCLIENT_EVENT_ERROR: An error has occured. The object must be freed from the + * job closure of the handler and no further I/O must be attempted. + * - BSOCKSCLIENT_EVENT_ERROR_CLOSED: The server has closed the connection. This event + * can only be reported after BSOCKSCLIENT_EVENT_UP. The object must be freed from + * the job closure of the handler and no further I/O must be attempted. + * - BSOCKSCLIENT_EVENT_UP: The CONNECT or UDP ASSOCIATE operation was successful. In + * the case of CONNECT, application I/O may now begin. + * - BSOCKSCLIENT_EVENT_CONNECTED: The TCP connection to the server has been established + * and the SOCKS protocol is about to begin. The local address of the TCP connection is + * now available via @ref BSocksClient_GetLocalAddr. The job closure of this callback + * is the last chance to call @ref BSocksClient_SetDestAddr. + * * @param user as in {@link BSocksClient_Init} - * @param event event type. One of BSOCKSCLIENT_EVENT_ERROR, BSOCKSCLIENT_EVENT_UP - * and BSOCKSCLIENT_EVENT_ERROR_CLOSED. - * If event is BSOCKSCLIENT_EVENT_UP, the object was previously in down - * state and has transitioned to up state; I/O can be done from this point on. - * If event is BSOCKSCLIENT_EVENT_ERROR or BSOCKSCLIENT_EVENT_ERROR_CLOSED, - * the object must be freed from within the job closure of this handler, - * and no further I/O must be attempted. + * @param event See above. */ typedef void (*BSocksClient_handler) (void *user, int event); @@ -89,6 +98,7 @@ typedef struct { char *buffer; BConnector connector; BConnection con; + BPending continue_job; union { struct { PacketPassInterface *send_if; @@ -108,8 +118,12 @@ struct BSocksClient_auth_info BSocksClient_auth_password (const char *username, /** * Initializes the object. - * The object is initialized in down state. The object must transition to up - * state before the user may begin any I/O. + * + * This object connects to a SOCKS5 server and performs a CONNECT or UDP ASSOCIATE + * operation. In any case, the object reports the BSOCKSCLIENT_EVENT_UP event via the + * handler when the operation was completed successfully. In the case of CONNECT, the + * user may then use the send and receive interfaces to exchange data through the + * connection (@ref BSocksClient_GetSendInterface and @ref BSocksClient_GetRecvInterface). * * @param o the object * @param server_addr SOCKS5 server address @@ -119,16 +133,19 @@ struct BSocksClient_auth_info BSocksClient_auth_password (const char *username, * exists, the data is not copied. * @param num_auth_info Number of the above. There should be at least one, otherwise it * certainly won't work. - * @param dest_addr remote address - * @param udp whether to do UDP ASSOCIATE instead of CONNECT + * @param dest_addr Address to send as DST.ADDR in the CONNECT or UDP ASSOCIATE request. + * It is also possible to specify it later from the BSOCKSCLIENT_EVENT_CONNECTED + * event callback using @ref BSocksClient_SetDestAddr; this is necessary for UDP + * if the local TCP connection address must be known to bind the UDP socket. + * @param udp false to perform a CONNECT, true to perform a UDP ASSOCIATE * @param handler handler for up and error events * @param user value passed to handler * @param reactor reactor we live in * @return 1 on success, 0 on failure */ -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) WARN_UNUSED; +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) WARN_UNUSED; /** * Frees the object. @@ -138,12 +155,36 @@ int BSocksClient_Init (BSocksClient *o, void BSocksClient_Free (BSocksClient *o); /** - * Return the bind address that the SOCKS server reported. - * The object must be in up state. The bind address is needed for UDP ASSOCIATE - * because it is the address that the client should send UDP packets to. + * Get the local address of the TCP socket for the SOCKS server connection. + * + * This may only be called after the BSOCKSCLIENT_EVENT_CONNECTED event was reported. + * + * @param o the object + * @param local_addr On success the local address is returned here. + * @return 1 on success, 0 on failure + */ +int BSocksClient_GetLocalAddr (BSocksClient *o, BAddr *local_addr); + +/** + * Set the DST.ADDR to send, overriding that specified in @ref BSocksClient_Init. + * + * The last chance to call this function is in the job closure of the + * BSOCKSCLIENT_EVENT_CONNECTED event, this must not be called after that. + * + * @param o the object + * @param dest_addr DST.ADDR to set. + */ +void BSocksClient_SetDestAddr (BSocksClient *o, BAddr dest_addr); + +/** + * Return the BND.ADDR that the SOCKS server reported. + * + * This may only be called after the BSOCKSCLIENT_EVENT_UP event was reported. + * This address is needed for UDP ASSOCIATE because it is the address that the + * client should send UDP packets to. * * @param o the object - * @return The bind address, of type BADDR_TYPE_IPV4 or BADDR_TYPE_IPV6. + * @return The BND.ADDR, of type BADDR_TYPE_IPV4 or BADDR_TYPE_IPV6. */ BAddr BSocksClient_GetBindAddr (BSocksClient *o); diff --git a/system/BConnection.h b/system/BConnection.h index 0b2ca6c31..e3d6a9e06 100644 --- a/system/BConnection.h +++ b/system/BConnection.h @@ -359,6 +359,19 @@ void BConnection_SetHandlers (BConnection *o, void *user, BConnection_handler ha */ int BConnection_SetSendBuffer (BConnection *o, int buf_size); +/** + * Determines the local address. + * + * This calls getsockname() to determine the local address and returns the result as + * BAddr. This function fails if the address cannot be determined or translated to + * BAddr (it never succeeds but returns a BADDR_TYPE_NONE address). + * + * @param o the object + * @param local_addr returns the local bound address. + * @return 1 on success, 0 on failure + */ +int BConnection_GetLocalAddress (BConnection *o, BAddr *local_addr); + /** * Initializes the send interface for the connection. * The send interface must not be initialized. diff --git a/system/BConnection_unix.c b/system/BConnection_unix.c index fc1e2944e..8c8d86ca2 100644 --- a/system/BConnection_unix.c +++ b/system/BConnection_unix.c @@ -876,6 +876,30 @@ int BConnection_SetSendBuffer (BConnection *o, int buf_size) return 1; } +int BConnection_GetLocalAddress (BConnection *o, BAddr *local_addr) +{ + DebugObject_Access(&o->d_obj); + + struct sys_addr sysaddr; + sysaddr.len = sizeof(sysaddr.addr); + if (getsockname(o->fd, &sysaddr.addr.generic, &sysaddr.len) != 0) { + BLog(BLOG_ERROR, "BConnection_GetLocalAddress: getsockname failed"); + return 0; + } + + BAddr addr; + addr_sys_to_socket(&addr, sysaddr); + + if (addr.type == BADDR_TYPE_NONE) { + BLog(BLOG_ERROR, "BConnection_GetLocalAddress: Unsupported address family " + "from getsockname: %d", (int)sysaddr.addr.generic.sa_family); + return 0; + } + + *local_addr = addr; + return 1; +} + void BConnection_SendAsync_Init (BConnection *o) { DebugObject_Access(&o->d_obj); diff --git a/system/BConnection_win.c b/system/BConnection_win.c index 1f3f900c9..a5fac7ea7 100644 --- a/system/BConnection_win.c +++ b/system/BConnection_win.c @@ -792,6 +792,31 @@ int BConnection_SetSendBuffer (BConnection *o, int buf_size) return 1; } +int BConnection_GetLocalAddress (BConnection *o, BAddr *local_addr) +{ + DebugObject_Access(&o->d_obj); + + struct BDatagram_sys_addr sysaddr; + socklen_t addr_size = sizeof(sysaddr.addr.generic); + if (getsockname(o->sock, &sysaddr.addr.generic, &addr_size) != 0) { + BLog(BLOG_ERROR, "BConnection_GetLocalAddress: getsockname failed"); + return 0; + } + sysaddr.len = addr_size; + + BAddr addr; + addr_sys_to_socket(&addr, sysaddr); + + if (addr.type == BADDR_TYPE_NONE) { + BLog(BLOG_ERROR, "BConnection_GetLocalAddress: Unsupported address family " + "from getsockname: %d", int(sysaddr.addr.generic.sa_family)); + return 0; + } + + *local_addr = addr; + return 1; +} + void BConnection_SendAsync_Init (BConnection *o) { DebugObject_Access(&o->d_obj); diff --git a/tun2socks/SocksUdpGwClient.c b/tun2socks/SocksUdpGwClient.c index 0d38e2220..61c8b9d65 100644 --- a/tun2socks/SocksUdpGwClient.c +++ b/tun2socks/SocksUdpGwClient.c @@ -132,8 +132,6 @@ static void socks_client_handler (SocksUdpGwClient *o, int event) // set reconnect timer BReactor_SetTimer(o->reactor, &o->reconnect_timer); } break; - - default: ASSERT(0); } } diff --git a/tun2socks/tun2socks.c b/tun2socks/tun2socks.c index 12157ca4d..2f5b2d16b 100644 --- a/tun2socks/tun2socks.c +++ b/tun2socks/tun2socks.c @@ -1654,9 +1654,6 @@ void client_socks_handler (struct tcp_client *client, int event) client_free_socks(client); } break; - - default: - ASSERT(0); } } From 2bfa72d0f71b9261bcffe25d80e33ae2703c2730 Mon Sep 17 00:00:00 2001 From: Ambroz Bizjak Date: Sat, 1 Feb 2020 14:36:58 -0800 Subject: [PATCH 06/11] build.nix: Enable passing nixpkgs as an argument. --- build.nix | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build.nix b/build.nix index 8a5264d86..8589ea2b3 100644 --- a/build.nix +++ b/build.nix @@ -1,4 +1,5 @@ -with import {}; +{ pkgs ? (import {}) }: +with pkgs; rec { badvpnFunc = import ./badvpn.nix; badvpn = pkgs.callPackage badvpnFunc {}; From 96b98372776371aff4b8cad312615c8c1e3b75f7 Mon Sep 17 00:00:00 2001 From: Ambroz Bizjak Date: Sat, 8 Feb 2020 15:11:26 -0800 Subject: [PATCH 07/11] SocksUdpClient:Fix compile error when assertions are enabled. --- socks_udp_client/SocksUdpClient.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/socks_udp_client/SocksUdpClient.c b/socks_udp_client/SocksUdpClient.c index 661d351fc..524ec0a51 100644 --- a/socks_udp_client/SocksUdpClient.c +++ b/socks_udp_client/SocksUdpClient.c @@ -436,7 +436,7 @@ void connection_send (struct SocksUdpClient_connection *con, BAddr remote_addr, const uint8_t *data, int data_len) { ASSERT(data_len >= 0) - ASSERT(data_len <= o->udp_mtu) + ASSERT(data_len <= con->client->udp_mtu) if (con->dns_id >= 0) { // So far, this connection has only sent a single DNS query. @@ -471,7 +471,7 @@ void connection_send (struct SocksUdpClient_connection *con, // o->udp_mtu and o->socks_mtu is calculated to accomodate any UDP packet not // not exceeding o->udp_mtu. size_t total_len = sizeof(struct socks_udp_header) + address_size + data_len; - ASSERT(total_len <= o->socks_mtu) + ASSERT(total_len <= con->client->socks_mtu) // Get a pointer to write the packet to. uint8_t *out_data_begin; From a18fd2b4b51c3a5429ca98d16700cf59e96e4612 Mon Sep 17 00:00:00 2001 From: Andrii Briantsev Date: Sat, 6 Jun 2020 01:57:43 +0200 Subject: [PATCH 08/11] This fixes build of the project on Windows --- system/BConnection_win.c | 4 ++-- system/BDatagram_win.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/system/BConnection_win.c b/system/BConnection_win.c index a5fac7ea7..1241c5be0 100644 --- a/system/BConnection_win.c +++ b/system/BConnection_win.c @@ -796,7 +796,7 @@ int BConnection_GetLocalAddress (BConnection *o, BAddr *local_addr) { DebugObject_Access(&o->d_obj); - struct BDatagram_sys_addr sysaddr; + struct sys_addr sysaddr; socklen_t addr_size = sizeof(sysaddr.addr.generic); if (getsockname(o->sock, &sysaddr.addr.generic, &addr_size) != 0) { BLog(BLOG_ERROR, "BConnection_GetLocalAddress: getsockname failed"); @@ -809,7 +809,7 @@ int BConnection_GetLocalAddress (BConnection *o, BAddr *local_addr) if (addr.type == BADDR_TYPE_NONE) { BLog(BLOG_ERROR, "BConnection_GetLocalAddress: Unsupported address family " - "from getsockname: %d", int(sysaddr.addr.generic.sa_family)); + "from getsockname: %d", sysaddr.addr.generic.sa_family); return 0; } diff --git a/system/BDatagram_win.c b/system/BDatagram_win.c index 3fd637059..cf6d3ffa3 100644 --- a/system/BDatagram_win.c +++ b/system/BDatagram_win.c @@ -652,7 +652,7 @@ int BDatagram_GetLocalAddr (BDatagram *o, BAddr *local_addr) if (addr.type == BADDR_TYPE_NONE) { BLog(BLOG_ERROR, "BDatagram_GetLocalAddr: Unsupported address family " - "from getsockname: %d", int(sysaddr.addr.generic.sa_family)); + "from getsockname: %d", sysaddr.addr.generic.sa_family); return 0; } From 2e51791d81dbf6418f6ccf41d3750097566bc652 Mon Sep 17 00:00:00 2001 From: ssrlive <30760636+ssrlive@users.noreply.github.com> Date: Sun, 22 Aug 2021 11:58:46 +0800 Subject: [PATCH 09/11] Update tun2socks.c --- tun2socks/tun2socks.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/tun2socks/tun2socks.c b/tun2socks/tun2socks.c index b4352bcf9..024db04b6 100644 --- a/tun2socks/tun2socks.c +++ b/tun2socks/tun2socks.c @@ -76,6 +76,10 @@ #ifdef __ANDROID__ +#ifndef ANDROID_APP_PACKAGE_NAME +#define ANDROID_APP_PACKAGE_NAME "com.github.shadowsocks" +#endif + #include #include @@ -297,9 +301,9 @@ LinkedList1 tcp_clients; // number of clients int num_clients; -#ifdef __ANDROID__ // Address of dnsgw BAddr dnsgw; +#ifdef __ANDROID__ void terminate (void); #else static void terminate (void); @@ -411,7 +415,7 @@ int wait_for_fd() } fcntl(sock, F_SETFL, flags | O_NONBLOCK); - char *path = "/data/data/com.github.shadowsocks/sock_path"; + char *path = "/data/data/" ANDROID_APP_PACKAGE_NAME "/sock_path"; if (options.sock_path != NULL) { path = options.sock_path; } @@ -485,11 +489,13 @@ int main (int argc, char **argv) goto fail0; } +#ifdef __ANDROID__ if (options.fake_proc) { // Fake process name to cheat on Lollipop - strcpy(argv[0], "com.github.shadowsocks"); - prctl(PR_SET_NAME, "com.github.shadowsocks"); + strcpy(argv[0], ANDROID_APP_PACKAGE_NAME); + prctl(PR_SET_NAME, ANDROID_APP_PACKAGE_NAME); } +#endif // handle --help and --version if (options.help) { @@ -1419,7 +1425,7 @@ int process_device_udp_packet (uint8_t *data, int data_len) BAddr local_addr; BAddr remote_addr; - int is_dns; + int is_dns = 0; uint8_t ip_version = 0; if (data_len > 0) { @@ -1459,6 +1465,7 @@ int process_device_udp_packet (uint8_t *data, int data_len) BAddr_InitIPv4(&local_addr, ipv4_header.source_address, udp_header.source_port); BAddr_InitIPv4(&remote_addr, ipv4_header.destination_address, udp_header.dest_port); +#ifdef __ANDROID__ // if transparent DNS is enabled, any packet arriving at out netif // address to port 53 is considered a DNS packet is_dns = (options.dnsgw && udp_header.dest_port == hton16(53)); @@ -1475,6 +1482,7 @@ int process_device_udp_packet (uint8_t *data, int data_len) is_dns = (header->qr == 0 && header->rcode == 0 && header->ans_count == 0 && header->auth_count == 0); } } +#endif } break; case 6: { From 1ca35ff5e86a0898d29640b3e241194d3a655dc7 Mon Sep 17 00:00:00 2001 From: Mygod Date: Mon, 23 Aug 2021 00:04:57 -0400 Subject: [PATCH 10/11] Remove support for unused options --- tun2socks/tun2socks.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/tun2socks/tun2socks.c b/tun2socks/tun2socks.c index 024db04b6..1c5ee9fe7 100644 --- a/tun2socks/tun2socks.c +++ b/tun2socks/tun2socks.c @@ -76,10 +76,6 @@ #ifdef __ANDROID__ -#ifndef ANDROID_APP_PACKAGE_NAME -#define ANDROID_APP_PACKAGE_NAME "com.github.shadowsocks" -#endif - #include #include @@ -415,7 +411,7 @@ int wait_for_fd() } fcntl(sock, F_SETFL, flags | O_NONBLOCK); - char *path = "/data/data/" ANDROID_APP_PACKAGE_NAME "/sock_path"; + char *path = "./sock_path"; if (options.sock_path != NULL) { path = options.sock_path; } @@ -489,14 +485,6 @@ int main (int argc, char **argv) goto fail0; } -#ifdef __ANDROID__ - if (options.fake_proc) { - // Fake process name to cheat on Lollipop - strcpy(argv[0], ANDROID_APP_PACKAGE_NAME); - prctl(PR_SET_NAME, ANDROID_APP_PACKAGE_NAME); - } -#endif - // handle --help and --version if (options.help) { print_version(); From 2814301fbd7e1838634580a18a34f8b68587f931 Mon Sep 17 00:00:00 2001 From: Mygod Date: Sat, 2 Nov 2024 01:01:56 -0700 Subject: [PATCH 11/11] Fix build --- protocol/udpgw_proto.h | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/protocol/udpgw_proto.h b/protocol/udpgw_proto.h index 7978ccdb5..f927136a1 100644 --- a/protocol/udpgw_proto.h +++ b/protocol/udpgw_proto.h @@ -33,22 +33,15 @@ #include #include +#ifdef __ANDROID__ +#include +#endif #define UDPGW_CLIENT_FLAG_KEEPALIVE (1 << 0) #define UDPGW_CLIENT_FLAG_REBIND (1 << 1) #define UDPGW_CLIENT_FLAG_DNS (1 << 2) #define UDPGW_CLIENT_FLAG_IPV6 (1 << 3) -#ifdef __ANDROID__ -B_START_PACKED -struct socks_udp_header { - uint16_t rsv; - uint8_t frag; - uint8_t atyp; -} B_PACKED; -B_END_PACKED -#endif - B_START_PACKED struct udpgw_header { uint8_t flags;