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`. 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/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 {}; 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/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/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; diff --git a/socks_udp_client/CMakeLists.txt b/socks_udp_client/CMakeLists.txt new file mode 100644 index 000000000..4598b48f8 --- /dev/null +++ b/socks_udp_client/CMakeLists.txt @@ -0,0 +1 @@ +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 new file mode 100644 index 000000000..524ec0a51 --- /dev/null +++ b/socks_udp_client/SocksUdpClient.c @@ -0,0 +1,634 @@ +/* + * 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: + * 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 +#include +#include +#include +#include + +#include + +#include + +static const int DnsPort = 53; + +static int addr_comparator (void *unused, BAddr *v1, BAddr *v2); +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 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_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 (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 socks_state_handler (struct SocksUdpClient_connection *con, int event) +{ + DebugObject_Access(&con->client->d_obj); + + switch (event) { + 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; + + 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); + + // 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, local_addr); + } break; + + case BSOCKSCLIENT_EVENT_ERROR: { + 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; + } +} + +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, + "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) +{ + 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) +{ + DebugObject_Access(&con->client->d_obj); + SocksUdpClient *o = con->client; + ASSERT(data_len >= 0) + ASSERT(data_len <= o->socks_mtu) + + // accept packet + PacketPassInterface_Done(&con->recv_if); + + // check header + struct socks_udp_header header; + if (data_len < sizeof(header)) { + BLog(BLOG_ERROR, "Missing SOCKS-UDP header."); + return; + } + memcpy(&header, data, sizeof(header)); + data += sizeof(header); + data_len -= sizeof(header); + + // parse address + BAddr remote_addr; + 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; + 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; + 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; + } + + // check remaining data + 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, data, data_len); + + // Was this connection used for a DNS query? + if (con->dns_id >= 0) { + // 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."); + + 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) +{ + ASSERT(o->num_connections <= o->max_connections) + ASSERT(!find_connection(o, local_addr)) + + 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 *)BAlloc(sizeof(*con)); + if (!con) { + BLog(BLOG_ERROR, "BAlloc connection failed"); + goto fail0; + } + + // 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; + + // 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. + 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 fail2; + } + + // 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, + dummy_dst_addr, true, (BSocksClient_handler)socks_state_handler, con, o->reactor)) + { + BLog(BLOG_ERROR, "Failed to initialize SOCKS client"); + goto fail3; + } + + // 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, 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), o->send_buf_size, pg)) + { + BLog(BLOG_ERROR, "Send buffer init failed"); + goto fail4; + } + + // Receive pipeline: socket -> recv_buffer -> recv_if + 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 fail5; + } + + // 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; + +fail5: + PacketPassInterface_Free(&con->recv_if); + BDatagram_RecvAsync_Free(&con->socket); + PacketBuffer_Free(&con->send_buffer); +fail4: + BufferWriter_Free(&con->send_writer); + PacketPassInactivityMonitor_Free(&con->send_monitor); + BDatagram_SendAsync_Free(&con->socket); + BSocksClient_Free(&con->socks); +fail3: + BDatagram_Free(&con->socket); +fail2: + BPending_Free(&con->first_job); + BFree(con->first_data); +fail1: + BFree(con); +fail0: + return NULL; +} + +void connection_free (struct SocksUdpClient_connection *con) +{ + SocksUdpClient *o = con->client; + + // 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 SOCKS client + BSocksClient_Free(&con->socks); + + // Free UDP socket + BDatagram_Free(&con->socket); + + // Free first job + BPending_Free(&con->first_job); + + // 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) +{ + ASSERT(data_len >= 0) + ASSERT(data_len <= con->client->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 in outgoing packet."); + return; + } break; + } + + // 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 <= con->client->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; + 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; + 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; + 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 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) +{ + 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_socks_mtu (int udp_mtu) +{ + 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; +} + +// 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 (ntoh16(BAddr_GetPort(remote_addr)) == DnsPort && data_len >= 2) { + return (data[0] << 8) | data[1]; + } else { + return -1; + } +} + +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(max_connections > 0) + ASSERT(send_buf_size > 0) + + // init simple things + o->server_addr = server_addr; + o->auth_info = auth_info; + o->num_auth_info = num_auth_info; + 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; + + // 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); + + 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)) { + BAVLNode *node = BAVL_GetFirst(&o->connections_tree); + struct SocksUdpClient_connection *con = + UPPER_OBJECT(node, struct SocksUdpClient_connection, connections_tree_node); + connection_free(con); + } +} + +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(o, local_addr); + if (!con) { + if (o->num_connections >= o->max_connections) { + // Drop the packet. + 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); + } +} diff --git a/socks_udp_client/SocksUdpClient.h b/socks_udp_client/SocksUdpClient.h new file mode 100644 index 000000000..52ded104a --- /dev/null +++ b/socks_udp_client/SocksUdpClient.h @@ -0,0 +1,144 @@ +/* + * 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: + * 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 + +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 send_buf_size; + int udp_mtu; + int socks_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 only initialzies the object and does not perform network access. + * + * @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 + * @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_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. + * @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); + +#endif diff --git a/socksclient/BSocksClient.c b/socksclient/BSocksClient.c index 21415af4c..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; } @@ -329,9 +347,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 +385,28 @@ 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; + 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; + 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); + } + // free buffer BFree(o->buffer); o->buffer = NULL; @@ -373,6 +415,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 @@ -465,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); @@ -476,7 +528,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: { @@ -523,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, 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 || @@ -540,12 +591,17 @@ 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; // 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)) { @@ -561,6 +617,7 @@ int BSocksClient_Init (BSocksClient *o, return 1; fail0: + BPending_Free(&o->continue_job); return 0; } @@ -585,15 +642,43 @@ 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) + 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); @@ -602,6 +687,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 f19b3a85c..5311e4021 100644 --- a/socksclient/BSocksClient.h +++ b/socksclient/BSocksClient.h @@ -34,31 +34,42 @@ #ifndef BADVPN_SOCKS_BSOCKSCLIENT_H #define BADVPN_SOCKS_BSOCKSCLIENT_H +#include #include +#include #include #include #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); @@ -78,6 +89,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; @@ -85,6 +98,7 @@ typedef struct { char *buffer; BConnector connector; BConnection con; + BPending continue_job; union { struct { PacketPassInterface *send_if; @@ -104,20 +118,34 @@ 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 - * @param dest_addr remote 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 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, 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. @@ -126,9 +154,44 @@ int BSocksClient_Init (BSocksClient *o, */ void BSocksClient_Free (BSocksClient *o); +/** + * 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 BND.ADDR, 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 @@ -137,7 +200,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/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..1241c5be0 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 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", 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/BDatagram.h b/system/BDatagram.h index 33efb4512..9b9b5c24e 100644 --- a/system/BDatagram.h +++ b/system/BDatagram.h @@ -123,6 +123,30 @@ void BDatagram_SetSendAddrs (BDatagram *o, BAddr remote_addr, BIPAddr local_addr */ int BDatagram_GetLastReceiveAddrs (BDatagram *o, BAddr *remote_addr, BIPAddr *local_addr); +/** + * 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. + * @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_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 b49b865c8..61b8f8d90 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_GetLocalAddr (BDatagram *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, "BDatagram_GetLocalAddr: getsockname failed"); + return 0; + } + + BAddr addr; + addr_sys_to_socket(&addr, sysaddr); + + 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; + } + + *local_addr = addr; + return 1; +} + int BDatagram_GetFd (BDatagram *o) { DebugObject_Access(&o->d_obj); diff --git a/system/BDatagram_win.c b/system/BDatagram_win.c index b8b9b1f24..cf6d3ffa3 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_GetLocalAddr (BDatagram *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, "BDatagram_GetLocalAddr: 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, "BDatagram_GetLocalAddr: Unsupported address family " + "from getsockname: %d", sysaddr.addr.generic.sa_family); + return 0; + } + + *local_addr = addr; + return 1; +} + int BDatagram_SetReuseAddr (BDatagram *o, int reuse) { DebugObject_Access(&o->d_obj); 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/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 87efb14c9..996510a8b 100644 --- a/tun2socks/SocksUdpGwClient.c +++ b/tun2socks/SocksUdpGwClient.c @@ -404,7 +404,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, (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; } @@ -471,8 +474,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 b4352bcf9..641d10c41 100644 --- a/tun2socks/tun2socks.c +++ b/tun2socks/tun2socks.c @@ -65,6 +65,7 @@ #include #include #include +#include #ifndef BADVPN_USE_WINAPI #include @@ -206,6 +207,7 @@ struct { #else char *tundev; #endif + int socks5_udp; } options; // TCP client @@ -270,10 +272,17 @@ 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; +// SOCKS5-UDP client +SocksUdpClient socks_udp_client; + // TCP timer BTimer tcp_timer; int tcp_timer_mod4; @@ -297,9 +306,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); @@ -340,7 +349,7 @@ 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); #ifdef __ANDROID__ static void daemonize(const char* path) { @@ -411,7 +420,7 @@ int wait_for_fd() } fcntl(sock, F_SETFL, flags | O_NONBLOCK); - char *path = "/data/data/com.github.shadowsocks/sock_path"; + char *path = "./sock_path"; if (options.sock_path != NULL) { path = options.sock_path; } @@ -485,12 +494,6 @@ int main (int argc, char **argv) goto fail0; } - if (options.fake_proc) { - // Fake process name to cheat on Lollipop - strcpy(argv[0], "com.github.shadowsocks"); - prctl(PR_SET_NAME, "com.github.shadowsocks"); - } - // handle --help and --version if (options.help) { print_version(); @@ -608,8 +611,8 @@ 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 + // 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)); @@ -621,6 +624,9 @@ int main (int argc, char **argv) udp_mtu = 0; } + 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) { @@ -629,13 +635,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, dnsgw, socks_auth_info, socks_num_auth_info, - udpgw_remote_server_addr, UDPGW_RECONNECT_TIME, &ss, NULL, udpgw_client_handler_received - )) { + if (!SocksUdpGwClient_Init(&udpgw_client, udp_mtu, DEFAULT_UDPGW_MAX_CONNECTIONS, + options.udpgw_connection_buffer_size, UDPGW_KEEPALIVE_TIME, socks_server_addr, + dnsgw, 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, + 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 @@ -704,8 +720,10 @@ int main (int argc, char **argv) fail5: BPending_Free(&lwip_init_job); - if (options.udpgw_remote_server_addr) { + if (udp_mode == UdpModeUdpgw) { SocksUdpGwClient_Free(&udpgw_client); + } else if (udp_mode == UdpModeSocks) { + SocksUdpClient_Free(&socks_udp_client); } fail4a: SinglePacketBuffer_Free(&device_read_buffer); @@ -782,6 +800,7 @@ void print_help (const char *name) " [--udpgw-connection-buffer-size ]\n" " [--udpgw-transparent-dns]\n" #endif + " [--socks5-udp]\n" "Address format is a.b.c.d:port (IPv4) or [addr]:port (IPv6).\n", name ); @@ -829,6 +848,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++) { @@ -1058,6 +1078,9 @@ int parse_arguments (int argc, char *argv[]) options.udpgw_transparent_dns = 1; } #endif + else if (!strcmp(arg, "--socks5-udp")) { + options.socks5_udp = 1; + } else { fprintf(stderr, "unknown option: %s\n", arg); return 0; @@ -1412,14 +1435,14 @@ 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) { + // do nothing if we don't use udpgw or SOCKS UDP + if (udp_mode == UdpModeNone) { goto fail; } BAddr local_addr; BAddr remote_addr; - int is_dns; + int is_dns = 0; uint8_t ip_version = 0; if (data_len > 0) { @@ -1459,6 +1482,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 +1499,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: { @@ -1529,8 +1554,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); + // 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 (udp_mode == UdpModeSocks) { + SocksUdpClient_SubmitPacket(&socks_udp_client, local_addr, remote_addr, data, data_len); + } return 1; @@ -1678,8 +1708,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, (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; } @@ -1995,9 +2027,6 @@ void client_socks_handler (struct tcp_client *client, int event) client_free_socks(client); } break; - - default: - ASSERT(0); } } @@ -2193,13 +2222,15 @@ 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(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) { @@ -2207,7 +2238,7 @@ void udpgw_client_handler_received (void *unused, BAddr local_addr, BAddr remote #ifdef __ANDROID__ BLog(BLOG_INFO, "UDP: from udprelay %d bytes", data_len); #else - BLog(BLOG_INFO, "UDP: from udpgw %d bytes", data_len); + BLog(BLOG_INFO, "UDP: from %s %d bytes", source_name, data_len); #endif if (data_len > UINT16_MAX - (sizeof(struct ipv4_header) + sizeof(struct udp_header)) || @@ -2250,14 +2281,14 @@ void udpgw_client_handler_received (void *unused, BAddr local_addr, BAddr remote #ifdef __ANDROID__ BLog(BLOG_INFO, "UDP/IPv6: from udprelay %d bytes", data_len); #else - BLog(BLOG_INFO, "UDP/IPv6: from udpgw %d bytes", data_len); + BLog(BLOG_INFO, "UDP/IPv6: from %s %d bytes", source_name, data_len); #endif if (!options.netif_ip6addr) { #ifdef __ANDROID__ BLog(BLOG_ERROR, "got IPv6 packet from udprelay but IPv6 is disabled"); #else - 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); #endif 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