badvpn/tun2socks/SocksUdpGwClient.c
Ambroz Bizjak e837e8d552 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.
2020-02-01 15:02:11 -08:00

230 lines
7.3 KiB
C

/*
* Copyright (C) Ambroz Bizjak <ambrop7@gmail.com>
* Contributions:
* Transparent DNS: Copyright (C) Kerem Hadimli <kerem.hadimli@gmail.com>
*
* 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 <misc/debug.h>
#include <base/BLog.h>
#include <tun2socks/SocksUdpGwClient.h>
#include <generated/blog_channel_SocksUdpGwClient.h>
static void free_socks (SocksUdpGwClient *o);
static void try_connect (SocksUdpGwClient *o);
static void reconnect_timer_handler (SocksUdpGwClient *o);
static void socks_client_handler (SocksUdpGwClient *o, int event);
static void udpgw_handler_servererror (SocksUdpGwClient *o);
static void udpgw_handler_received (SocksUdpGwClient *o, BAddr local_addr, BAddr remote_addr, const uint8_t *data, int data_len);
static void free_socks (SocksUdpGwClient *o)
{
ASSERT(o->have_socks)
// disconnect udpgw client from SOCKS
if (o->socks_up) {
UdpGwClient_DisconnectServer(&o->udpgw_client);
}
// free SOCKS client
BSocksClient_Free(&o->socks_client);
// set have no SOCKS
o->have_socks = 0;
}
static void try_connect (SocksUdpGwClient *o)
{
ASSERT(!o->have_socks)
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, /*udp=*/false,
(BSocksClient_handler)socks_client_handler, o, o->reactor))
{
BLog(BLOG_ERROR, "BSocksClient_Init failed");
goto fail0;
}
// set have SOCKS
o->have_socks = 1;
// set SOCKS not up
o->socks_up = 0;
return;
fail0:
// set reconnect timer
BReactor_SetTimer(o->reactor, &o->reconnect_timer);
}
static void reconnect_timer_handler (SocksUdpGwClient *o)
{
DebugObject_Access(&o->d_obj);
ASSERT(!o->have_socks)
// try connecting
try_connect(o);
}
static void socks_client_handler (SocksUdpGwClient *o, int event)
{
DebugObject_Access(&o->d_obj);
ASSERT(o->have_socks)
switch (event) {
case BSOCKSCLIENT_EVENT_UP: {
ASSERT(!o->socks_up)
BLog(BLOG_INFO, "SOCKS up");
// connect udpgw client to SOCKS
if (!UdpGwClient_ConnectServer(&o->udpgw_client, BSocksClient_GetSendInterface(&o->socks_client), BSocksClient_GetRecvInterface(&o->socks_client))) {
BLog(BLOG_ERROR, "UdpGwClient_ConnectServer failed");
goto fail0;
}
// set SOCKS up
o->socks_up = 1;
return;
fail0:
// free SOCKS
free_socks(o);
// set reconnect timer
BReactor_SetTimer(o->reactor, &o->reconnect_timer);
} break;
case BSOCKSCLIENT_EVENT_ERROR:
case BSOCKSCLIENT_EVENT_ERROR_CLOSED: {
BLog(BLOG_INFO, "SOCKS error");
// free SOCKS
free_socks(o);
// set reconnect timer
BReactor_SetTimer(o->reactor, &o->reconnect_timer);
} break;
}
}
static void udpgw_handler_servererror (SocksUdpGwClient *o)
{
DebugObject_Access(&o->d_obj);
ASSERT(o->have_socks)
ASSERT(o->socks_up)
BLog(BLOG_ERROR, "client reports server error");
// free SOCKS
free_socks(o);
// set reconnect timer
BReactor_SetTimer(o->reactor, &o->reconnect_timer);
}
static void udpgw_handler_received (SocksUdpGwClient *o, BAddr local_addr, BAddr remote_addr, const uint8_t *data, int data_len)
{
DebugObject_Access(&o->d_obj);
// submit to user
o->handler_received(o->user, local_addr, remote_addr, data, data_len);
return;
}
int SocksUdpGwClient_Init (SocksUdpGwClient *o, int udp_mtu, int max_connections, int send_buffer_size, btime_t keepalive_time,
BAddr socks_server_addr, const struct BSocksClient_auth_info *auth_info, size_t num_auth_info,
BAddr remote_udpgw_addr, btime_t reconnect_time, BReactor *reactor, void *user,
SocksUdpGwClient_handler_received handler_received)
{
// see asserts in UdpGwClient_Init
ASSERT(!BAddr_IsInvalid(&socks_server_addr))
ASSERT(remote_udpgw_addr.type == BADDR_TYPE_IPV4 || remote_udpgw_addr.type == BADDR_TYPE_IPV6)
// init arguments
o->udp_mtu = udp_mtu;
o->socks_server_addr = socks_server_addr;
o->auth_info = auth_info;
o->num_auth_info = num_auth_info;
o->remote_udpgw_addr = remote_udpgw_addr;
o->reactor = reactor;
o->user = user;
o->handler_received = handler_received;
// init udpgw client
if (!UdpGwClient_Init(&o->udpgw_client, udp_mtu, max_connections, send_buffer_size, keepalive_time, o->reactor, o,
(UdpGwClient_handler_servererror)udpgw_handler_servererror,
(UdpGwClient_handler_received)udpgw_handler_received
)) {
goto fail0;
}
// init reconnect timer
BTimer_Init(&o->reconnect_timer, reconnect_time, (BTimer_handler)reconnect_timer_handler, o);
// set have no SOCKS
o->have_socks = 0;
// try connecting
try_connect(o);
DebugObject_Init(&o->d_obj);
return 1;
fail0:
return 0;
}
void SocksUdpGwClient_Free (SocksUdpGwClient *o)
{
DebugObject_Free(&o->d_obj);
// free SOCKS
if (o->have_socks) {
free_socks(o);
}
// free reconnect timer
BReactor_RemoveTimer(o->reactor, &o->reconnect_timer);
// free udpgw client
UdpGwClient_Free(&o->udpgw_client);
}
void SocksUdpGwClient_SubmitPacket (SocksUdpGwClient *o, BAddr local_addr, BAddr remote_addr, int is_dns, const uint8_t *data, int data_len)
{
DebugObject_Access(&o->d_obj);
// see asserts in UdpGwClient_SubmitPacket
// submit to udpgw client
UdpGwClient_SubmitPacket(&o->udpgw_client, local_addr, remote_addr, is_dns, data, data_len);
}