e837e8d552
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.
1948 lines
58 KiB
C
1948 lines
58 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 <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stddef.h>
|
|
#include <string.h>
|
|
#include <limits.h>
|
|
|
|
#include <misc/version.h>
|
|
#include <misc/loggers_string.h>
|
|
#include <misc/loglevel.h>
|
|
#include <misc/minmax.h>
|
|
#include <misc/offset.h>
|
|
#include <misc/dead.h>
|
|
#include <misc/ipv4_proto.h>
|
|
#include <misc/ipv6_proto.h>
|
|
#include <misc/udp_proto.h>
|
|
#include <misc/byteorder.h>
|
|
#include <misc/balloc.h>
|
|
#include <misc/open_standard_streams.h>
|
|
#include <misc/read_file.h>
|
|
#include <misc/ipaddr6.h>
|
|
#include <misc/concat_strings.h>
|
|
#include <structure/LinkedList1.h>
|
|
#include <base/BLog.h>
|
|
#include <system/BReactor.h>
|
|
#include <system/BSignal.h>
|
|
#include <system/BAddr.h>
|
|
#include <system/BNetwork.h>
|
|
#include <flow/SinglePacketBuffer.h>
|
|
#include <socksclient/BSocksClient.h>
|
|
#include <tuntap/BTap.h>
|
|
#include <lwip/init.h>
|
|
#include <lwip/ip_addr.h>
|
|
#include <lwip/priv/tcp_priv.h>
|
|
#include <lwip/netif.h>
|
|
#include <lwip/tcp.h>
|
|
#include <lwip/ip4_frag.h>
|
|
#include <lwip/nd6.h>
|
|
#include <lwip/ip6_frag.h>
|
|
#include <tun2socks/SocksUdpGwClient.h>
|
|
#include <socks_udp_client/SocksUdpClient.h>
|
|
|
|
#ifndef BADVPN_USE_WINAPI
|
|
#include <base/BLog_syslog.h>
|
|
#endif
|
|
|
|
#include <tun2socks/tun2socks.h>
|
|
|
|
#include <generated/blog_channel_tun2socks.h>
|
|
|
|
#define LOGGER_STDOUT 1
|
|
#define LOGGER_SYSLOG 2
|
|
|
|
#define SYNC_DECL \
|
|
BPending sync_mark; \
|
|
|
|
#define SYNC_FROMHERE \
|
|
BPending_Init(&sync_mark, BReactor_PendingGroup(&ss), NULL, NULL); \
|
|
BPending_Set(&sync_mark);
|
|
|
|
#define SYNC_BREAK \
|
|
BPending_Free(&sync_mark);
|
|
|
|
#define SYNC_COMMIT \
|
|
BReactor_Synchronize(&ss, &sync_mark.base); \
|
|
BPending_Free(&sync_mark);
|
|
|
|
// command-line options
|
|
struct {
|
|
int help;
|
|
int version;
|
|
int logger;
|
|
#ifndef BADVPN_USE_WINAPI
|
|
char *logger_syslog_facility;
|
|
char *logger_syslog_ident;
|
|
#endif
|
|
int loglevel;
|
|
int loglevels[BLOG_NUM_CHANNELS];
|
|
char *tundev;
|
|
char *netif_ipaddr;
|
|
char *netif_netmask;
|
|
char *netif_ip6addr;
|
|
char *socks_server_addr;
|
|
char *username;
|
|
char *password;
|
|
char *password_file;
|
|
int append_source_to_username;
|
|
char *udpgw_remote_server_addr;
|
|
int udpgw_max_connections;
|
|
int udpgw_connection_buffer_size;
|
|
int udpgw_transparent_dns;
|
|
int socks5_udp;
|
|
} options;
|
|
|
|
// TCP client
|
|
struct tcp_client {
|
|
int aborted;
|
|
dead_t dead_aborted;
|
|
LinkedList1Node list_node;
|
|
BAddr local_addr;
|
|
BAddr remote_addr;
|
|
struct tcp_pcb *pcb;
|
|
int client_closed;
|
|
uint8_t buf[TCP_WND];
|
|
int buf_used;
|
|
char *socks_username;
|
|
BSocksClient socks_client;
|
|
int socks_up;
|
|
int socks_closed;
|
|
StreamPassInterface *socks_send_if;
|
|
StreamRecvInterface *socks_recv_if;
|
|
uint8_t socks_recv_buf[CLIENT_SOCKS_RECV_BUF_SIZE];
|
|
int socks_recv_buf_used;
|
|
int socks_recv_buf_sent;
|
|
int socks_recv_waiting;
|
|
int socks_recv_tcp_pending;
|
|
};
|
|
|
|
// IP address of netif
|
|
BIPAddr netif_ipaddr;
|
|
|
|
// netmask of netif
|
|
BIPAddr netif_netmask;
|
|
|
|
// IP6 address of netif
|
|
struct ipv6_addr netif_ip6addr;
|
|
|
|
// SOCKS server address
|
|
BAddr socks_server_addr;
|
|
|
|
// allocated password file contents
|
|
uint8_t *password_file_contents;
|
|
|
|
// SOCKS authentication information
|
|
struct BSocksClient_auth_info socks_auth_info[2];
|
|
size_t socks_num_auth_info;
|
|
|
|
// remote udpgw server addr, if provided
|
|
BAddr udpgw_remote_server_addr;
|
|
|
|
// reactor
|
|
BReactor ss;
|
|
|
|
// set to 1 by terminate
|
|
int quitting;
|
|
|
|
// TUN device
|
|
BTap device;
|
|
|
|
// device write buffer
|
|
uint8_t *device_write_buf;
|
|
|
|
// device reading
|
|
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;
|
|
|
|
// job for initializing lwip
|
|
BPending lwip_init_job;
|
|
|
|
// lwip netif
|
|
int have_netif;
|
|
struct netif the_netif;
|
|
|
|
// lwip TCP listener
|
|
struct tcp_pcb *listener;
|
|
|
|
// lwip TCP/IPv6 listener
|
|
struct tcp_pcb *listener_ip6;
|
|
|
|
// TCP clients
|
|
LinkedList1 tcp_clients;
|
|
|
|
// number of clients
|
|
int num_clients;
|
|
|
|
static void terminate (void);
|
|
static void print_help (const char *name);
|
|
static void print_version (void);
|
|
static int parse_arguments (int argc, char *argv[]);
|
|
static int process_arguments (void);
|
|
static void signal_handler (void *unused);
|
|
static BAddr baddr_from_lwip (const ip_addr_t *ip_addr, uint16_t port_hostorder);
|
|
static void lwip_init_job_hadler (void *unused);
|
|
static void tcp_timer_handler (void *unused);
|
|
static void device_error_handler (void *unused);
|
|
static void device_read_handler_send (void *unused, uint8_t *data, int data_len);
|
|
static int process_device_udp_packet (uint8_t *data, int data_len);
|
|
static err_t netif_init_func (struct netif *netif);
|
|
static err_t netif_output_func (struct netif *netif, struct pbuf *p, const ip4_addr_t *ipaddr);
|
|
static err_t netif_output_ip6_func (struct netif *netif, struct pbuf *p, const ip6_addr_t *ipaddr);
|
|
static err_t common_netif_output (struct netif *netif, struct pbuf *p);
|
|
static err_t netif_input_func (struct pbuf *p, struct netif *inp);
|
|
static void client_logfunc (struct tcp_client *client);
|
|
static void client_log (struct tcp_client *client, int level, const char *fmt, ...);
|
|
static err_t listener_accept_func (void *arg, struct tcp_pcb *newpcb, err_t err);
|
|
static void client_handle_freed_client (struct tcp_client *client);
|
|
static void client_free_client (struct tcp_client *client);
|
|
static void client_abort_client (struct tcp_client *client);
|
|
static void client_abort_pcb (struct tcp_client *client);
|
|
static void client_free_socks (struct tcp_client *client);
|
|
static void client_murder (struct tcp_client *client);
|
|
static void client_dealloc (struct tcp_client *client);
|
|
static void client_err_func (void *arg, err_t err);
|
|
static err_t client_recv_func (void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err);
|
|
static void client_socks_handler (struct tcp_client *client, int event);
|
|
static void client_send_to_socks (struct tcp_client *client);
|
|
static void client_socks_send_handler_done (struct tcp_client *client, int data_len);
|
|
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 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) {
|
|
return 1;
|
|
}
|
|
|
|
// open standard streams
|
|
open_standard_streams();
|
|
|
|
// parse command-line arguments
|
|
if (!parse_arguments(argc, argv)) {
|
|
fprintf(stderr, "Failed to parse arguments\n");
|
|
print_help(argv[0]);
|
|
goto fail0;
|
|
}
|
|
|
|
// handle --help and --version
|
|
if (options.help) {
|
|
print_version();
|
|
print_help(argv[0]);
|
|
return 0;
|
|
}
|
|
if (options.version) {
|
|
print_version();
|
|
return 0;
|
|
}
|
|
|
|
// initialize logger
|
|
switch (options.logger) {
|
|
case LOGGER_STDOUT:
|
|
BLog_InitStdout();
|
|
break;
|
|
#ifndef BADVPN_USE_WINAPI
|
|
case LOGGER_SYSLOG:
|
|
if (!BLog_InitSyslog(options.logger_syslog_ident, options.logger_syslog_facility)) {
|
|
fprintf(stderr, "Failed to initialize syslog logger\n");
|
|
goto fail0;
|
|
}
|
|
break;
|
|
#endif
|
|
default:
|
|
ASSERT(0);
|
|
}
|
|
|
|
// configure logger channels
|
|
for (int i = 0; i < BLOG_NUM_CHANNELS; i++) {
|
|
if (options.loglevels[i] >= 0) {
|
|
BLog_SetChannelLoglevel(i, options.loglevels[i]);
|
|
}
|
|
else if (options.loglevel >= 0) {
|
|
BLog_SetChannelLoglevel(i, options.loglevel);
|
|
}
|
|
}
|
|
|
|
BLog(BLOG_NOTICE, "initializing "GLOBAL_PRODUCT_NAME" "PROGRAM_NAME" "GLOBAL_VERSION);
|
|
|
|
// clear password contents pointer
|
|
password_file_contents = NULL;
|
|
|
|
// initialize network
|
|
if (!BNetwork_GlobalInit()) {
|
|
BLog(BLOG_ERROR, "BNetwork_GlobalInit failed");
|
|
goto fail1;
|
|
}
|
|
|
|
// process arguments
|
|
if (!process_arguments()) {
|
|
BLog(BLOG_ERROR, "Failed to process arguments");
|
|
goto fail1;
|
|
}
|
|
|
|
// init time
|
|
BTime_Init();
|
|
|
|
// init reactor
|
|
if (!BReactor_Init(&ss)) {
|
|
BLog(BLOG_ERROR, "BReactor_Init failed");
|
|
goto fail1;
|
|
}
|
|
|
|
// set not quitting
|
|
quitting = 0;
|
|
|
|
// setup signal handler
|
|
if (!BSignal_Init(&ss, signal_handler, NULL)) {
|
|
BLog(BLOG_ERROR, "BSignal_Init failed");
|
|
goto fail2;
|
|
}
|
|
|
|
// init TUN device
|
|
if (!BTap_Init(&device, &ss, options.tundev, device_error_handler, NULL, 1)) {
|
|
BLog(BLOG_ERROR, "BTap_Init failed");
|
|
goto fail3;
|
|
}
|
|
|
|
// NOTE: the order of the following is important:
|
|
// first device writing must evaluate,
|
|
// then lwip (so it can send packets to the device),
|
|
// then device reading (so it can pass received packets to lwip).
|
|
|
|
// init device reading
|
|
PacketPassInterface_Init(&device_read_interface, BTap_GetMTU(&device), device_read_handler_send, NULL, BReactor_PendingGroup(&ss));
|
|
if (!SinglePacketBuffer_Init(&device_read_buffer, BTap_GetOutput(&device), &device_read_interface, BReactor_PendingGroup(&ss))) {
|
|
BLog(BLOG_ERROR, "SinglePacketBuffer_Init failed");
|
|
goto fail4;
|
|
}
|
|
|
|
// 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));
|
|
if (udp_mtu < udp_ip6_mtu) {
|
|
udp_mtu = udp_ip6_mtu;
|
|
}
|
|
}
|
|
if (udp_mtu < 0) {
|
|
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) {
|
|
BLog(BLOG_ERROR, "device MTU is too large for UDP");
|
|
goto fail4a;
|
|
}
|
|
|
|
// 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))
|
|
{
|
|
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
|
|
BPending_Init(&lwip_init_job, BReactor_PendingGroup(&ss), lwip_init_job_hadler, NULL);
|
|
BPending_Set(&lwip_init_job);
|
|
|
|
// init device write buffer
|
|
if (!(device_write_buf = (uint8_t *)BAlloc(BTap_GetMTU(&device)))) {
|
|
BLog(BLOG_ERROR, "BAlloc failed");
|
|
goto fail5;
|
|
}
|
|
|
|
// init TCP timer
|
|
// it won't trigger before lwip is initialized, becuase the lwip init is a job
|
|
BTimer_Init(&tcp_timer, TCP_TMR_INTERVAL, tcp_timer_handler, NULL);
|
|
BReactor_SetTimer(&ss, &tcp_timer);
|
|
tcp_timer_mod4 = 0;
|
|
|
|
// set no netif
|
|
have_netif = 0;
|
|
|
|
// set no listener
|
|
listener = NULL;
|
|
listener_ip6 = NULL;
|
|
|
|
// init clients list
|
|
LinkedList1_Init(&tcp_clients);
|
|
|
|
// init number of clients
|
|
num_clients = 0;
|
|
|
|
// enter event loop
|
|
BLog(BLOG_NOTICE, "entering event loop");
|
|
BReactor_Exec(&ss);
|
|
|
|
// free clients
|
|
LinkedList1Node *node;
|
|
while (node = LinkedList1_GetFirst(&tcp_clients)) {
|
|
struct tcp_client *client = UPPER_OBJECT(node, struct tcp_client, list_node);
|
|
client_murder(client);
|
|
}
|
|
|
|
// free listener
|
|
if (listener_ip6) {
|
|
tcp_close(listener_ip6);
|
|
}
|
|
if (listener) {
|
|
tcp_close(listener);
|
|
}
|
|
|
|
// free netif
|
|
if (have_netif) {
|
|
netif_remove(&the_netif);
|
|
}
|
|
|
|
BReactor_RemoveTimer(&ss, &tcp_timer);
|
|
BFree(device_write_buf);
|
|
fail5:
|
|
BPending_Free(&lwip_init_job);
|
|
if (udp_mode == UdpModeUdpgw) {
|
|
SocksUdpGwClient_Free(&udpgw_client);
|
|
} else if (udp_mode == UdpModeSocks) {
|
|
SocksUdpClient_Free(&socks_udp_client);
|
|
}
|
|
fail4a:
|
|
SinglePacketBuffer_Free(&device_read_buffer);
|
|
fail4:
|
|
PacketPassInterface_Free(&device_read_interface);
|
|
BTap_Free(&device);
|
|
fail3:
|
|
BSignal_Finish();
|
|
fail2:
|
|
BReactor_Free(&ss);
|
|
fail1:
|
|
BFree(password_file_contents);
|
|
BLog(BLOG_NOTICE, "exiting");
|
|
BLog_Free();
|
|
fail0:
|
|
DebugObjectGlobal_Finish();
|
|
|
|
return 1;
|
|
}
|
|
|
|
void terminate (void)
|
|
{
|
|
ASSERT(!quitting)
|
|
|
|
BLog(BLOG_NOTICE, "tearing down");
|
|
|
|
// set quitting
|
|
quitting = 1;
|
|
|
|
// exit event loop
|
|
BReactor_Quit(&ss, 1);
|
|
}
|
|
|
|
void print_help (const char *name)
|
|
{
|
|
printf(
|
|
"Usage:\n"
|
|
" %s\n"
|
|
" [--help]\n"
|
|
" [--version]\n"
|
|
" [--logger <"LOGGERS_STRING">]\n"
|
|
#ifndef BADVPN_USE_WINAPI
|
|
" (logger=syslog?\n"
|
|
" [--syslog-facility <string>]\n"
|
|
" [--syslog-ident <string>]\n"
|
|
" )\n"
|
|
#endif
|
|
" [--loglevel <0-5/none/error/warning/notice/info/debug>]\n"
|
|
" [--channel-loglevel <channel-name> <0-5/none/error/warning/notice/info/debug>] ...\n"
|
|
" [--tundev <name>]\n"
|
|
" --netif-ipaddr <ipaddr>\n"
|
|
" --netif-netmask <ipnetmask>\n"
|
|
" --socks-server-addr <addr>\n"
|
|
" [--netif-ip6addr <addr>]\n"
|
|
" [--username <username>]\n"
|
|
" [--password <password>]\n"
|
|
" [--password-file <file>]\n"
|
|
" [--append-source-to-username]\n"
|
|
" [--udpgw-remote-server-addr <addr>]\n"
|
|
" [--udpgw-max-connections <number>]\n"
|
|
" [--udpgw-connection-buffer-size <number>]\n"
|
|
" [--udpgw-transparent-dns]\n"
|
|
" [--socks5-udp]\n"
|
|
"Address format is a.b.c.d:port (IPv4) or [addr]:port (IPv6).\n",
|
|
name
|
|
);
|
|
}
|
|
|
|
void print_version (void)
|
|
{
|
|
printf(GLOBAL_PRODUCT_NAME" "PROGRAM_NAME" "GLOBAL_VERSION"\n"GLOBAL_COPYRIGHT_NOTICE"\n");
|
|
}
|
|
|
|
int parse_arguments (int argc, char *argv[])
|
|
{
|
|
if (argc <= 0) {
|
|
return 0;
|
|
}
|
|
|
|
options.help = 0;
|
|
options.version = 0;
|
|
options.logger = LOGGER_STDOUT;
|
|
#ifndef BADVPN_USE_WINAPI
|
|
options.logger_syslog_facility = "daemon";
|
|
options.logger_syslog_ident = argv[0];
|
|
#endif
|
|
options.loglevel = -1;
|
|
for (int i = 0; i < BLOG_NUM_CHANNELS; i++) {
|
|
options.loglevels[i] = -1;
|
|
}
|
|
options.tundev = NULL;
|
|
options.netif_ipaddr = NULL;
|
|
options.netif_netmask = NULL;
|
|
options.netif_ip6addr = NULL;
|
|
options.socks_server_addr = NULL;
|
|
options.username = NULL;
|
|
options.password = NULL;
|
|
options.password_file = NULL;
|
|
options.append_source_to_username = 0;
|
|
options.udpgw_remote_server_addr = NULL;
|
|
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++) {
|
|
char *arg = argv[i];
|
|
if (!strcmp(arg, "--help")) {
|
|
options.help = 1;
|
|
}
|
|
else if (!strcmp(arg, "--version")) {
|
|
options.version = 1;
|
|
}
|
|
else if (!strcmp(arg, "--logger")) {
|
|
if (1 >= argc - i) {
|
|
fprintf(stderr, "%s: requires an argument\n", arg);
|
|
return 0;
|
|
}
|
|
char *arg2 = argv[i + 1];
|
|
if (!strcmp(arg2, "stdout")) {
|
|
options.logger = LOGGER_STDOUT;
|
|
}
|
|
#ifndef BADVPN_USE_WINAPI
|
|
else if (!strcmp(arg2, "syslog")) {
|
|
options.logger = LOGGER_SYSLOG;
|
|
}
|
|
#endif
|
|
else {
|
|
fprintf(stderr, "%s: wrong argument\n", arg);
|
|
return 0;
|
|
}
|
|
i++;
|
|
}
|
|
#ifndef BADVPN_USE_WINAPI
|
|
else if (!strcmp(arg, "--syslog-facility")) {
|
|
if (1 >= argc - i) {
|
|
fprintf(stderr, "%s: requires an argument\n", arg);
|
|
return 0;
|
|
}
|
|
options.logger_syslog_facility = argv[i + 1];
|
|
i++;
|
|
}
|
|
else if (!strcmp(arg, "--syslog-ident")) {
|
|
if (1 >= argc - i) {
|
|
fprintf(stderr, "%s: requires an argument\n", arg);
|
|
return 0;
|
|
}
|
|
options.logger_syslog_ident = argv[i + 1];
|
|
i++;
|
|
}
|
|
#endif
|
|
else if (!strcmp(arg, "--loglevel")) {
|
|
if (1 >= argc - i) {
|
|
fprintf(stderr, "%s: requires an argument\n", arg);
|
|
return 0;
|
|
}
|
|
if ((options.loglevel = parse_loglevel(argv[i + 1])) < 0) {
|
|
fprintf(stderr, "%s: wrong argument\n", arg);
|
|
return 0;
|
|
}
|
|
i++;
|
|
}
|
|
else if (!strcmp(arg, "--channel-loglevel")) {
|
|
if (2 >= argc - i) {
|
|
fprintf(stderr, "%s: requires two arguments\n", arg);
|
|
return 0;
|
|
}
|
|
int channel = BLogGlobal_GetChannelByName(argv[i + 1]);
|
|
if (channel < 0) {
|
|
fprintf(stderr, "%s: wrong channel argument\n", arg);
|
|
return 0;
|
|
}
|
|
int loglevel = parse_loglevel(argv[i + 2]);
|
|
if (loglevel < 0) {
|
|
fprintf(stderr, "%s: wrong loglevel argument\n", arg);
|
|
return 0;
|
|
}
|
|
options.loglevels[channel] = loglevel;
|
|
i += 2;
|
|
}
|
|
else if (!strcmp(arg, "--tundev")) {
|
|
if (1 >= argc - i) {
|
|
fprintf(stderr, "%s: requires an argument\n", arg);
|
|
return 0;
|
|
}
|
|
options.tundev = argv[i + 1];
|
|
i++;
|
|
}
|
|
else if (!strcmp(arg, "--netif-ipaddr")) {
|
|
if (1 >= argc - i) {
|
|
fprintf(stderr, "%s: requires an argument\n", arg);
|
|
return 0;
|
|
}
|
|
options.netif_ipaddr = argv[i + 1];
|
|
i++;
|
|
}
|
|
else if (!strcmp(arg, "--netif-netmask")) {
|
|
if (1 >= argc - i) {
|
|
fprintf(stderr, "%s: requires an argument\n", arg);
|
|
return 0;
|
|
}
|
|
options.netif_netmask = argv[i + 1];
|
|
i++;
|
|
}
|
|
else if (!strcmp(arg, "--netif-ip6addr")) {
|
|
if (1 >= argc - i) {
|
|
fprintf(stderr, "%s: requires an argument\n", arg);
|
|
return 0;
|
|
}
|
|
options.netif_ip6addr = argv[i + 1];
|
|
i++;
|
|
}
|
|
else if (!strcmp(arg, "--socks-server-addr")) {
|
|
if (1 >= argc - i) {
|
|
fprintf(stderr, "%s: requires an argument\n", arg);
|
|
return 0;
|
|
}
|
|
options.socks_server_addr = argv[i + 1];
|
|
i++;
|
|
}
|
|
else if (!strcmp(arg, "--username")) {
|
|
if (1 >= argc - i) {
|
|
fprintf(stderr, "%s: requires an argument\n", arg);
|
|
return 0;
|
|
}
|
|
options.username = argv[i + 1];
|
|
i++;
|
|
}
|
|
else if (!strcmp(arg, "--password")) {
|
|
if (1 >= argc - i) {
|
|
fprintf(stderr, "%s: requires an argument\n", arg);
|
|
return 0;
|
|
}
|
|
options.password = argv[i + 1];
|
|
i++;
|
|
}
|
|
else if (!strcmp(arg, "--password-file")) {
|
|
if (1 >= argc - i) {
|
|
fprintf(stderr, "%s: requires an argument\n", arg);
|
|
return 0;
|
|
}
|
|
options.password_file = argv[i + 1];
|
|
i++;
|
|
}
|
|
else if (!strcmp(arg, "--append-source-to-username")) {
|
|
options.append_source_to_username = 1;
|
|
}
|
|
else if (!strcmp(arg, "--udpgw-remote-server-addr")) {
|
|
if (1 >= argc - i) {
|
|
fprintf(stderr, "%s: requires an argument\n", arg);
|
|
return 0;
|
|
}
|
|
options.udpgw_remote_server_addr = argv[i + 1];
|
|
i++;
|
|
}
|
|
else if (!strcmp(arg, "--udpgw-max-connections")) {
|
|
if (1 >= argc - i) {
|
|
fprintf(stderr, "%s: requires an argument\n", arg);
|
|
return 0;
|
|
}
|
|
if ((options.udpgw_max_connections = atoi(argv[i + 1])) <= 0) {
|
|
fprintf(stderr, "%s: wrong argument\n", arg);
|
|
return 0;
|
|
}
|
|
i++;
|
|
}
|
|
else if (!strcmp(arg, "--udpgw-connection-buffer-size")) {
|
|
if (1 >= argc - i) {
|
|
fprintf(stderr, "%s: requires an argument\n", arg);
|
|
return 0;
|
|
}
|
|
if ((options.udpgw_connection_buffer_size = atoi(argv[i + 1])) <= 0) {
|
|
fprintf(stderr, "%s: wrong argument\n", arg);
|
|
return 0;
|
|
}
|
|
i++;
|
|
}
|
|
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;
|
|
}
|
|
}
|
|
|
|
if (options.help || options.version) {
|
|
return 1;
|
|
}
|
|
|
|
if (!options.netif_ipaddr) {
|
|
fprintf(stderr, "--netif-ipaddr is required\n");
|
|
return 0;
|
|
}
|
|
|
|
if (!options.netif_netmask) {
|
|
fprintf(stderr, "--netif-netmask is required\n");
|
|
return 0;
|
|
}
|
|
|
|
if (!options.socks_server_addr) {
|
|
fprintf(stderr, "--socks-server-addr is required\n");
|
|
return 0;
|
|
}
|
|
|
|
if (options.username) {
|
|
if (!options.password && !options.password_file) {
|
|
fprintf(stderr, "username given but password not given\n");
|
|
return 0;
|
|
}
|
|
|
|
if (options.password && options.password_file) {
|
|
fprintf(stderr, "--password and --password-file cannot both be given\n");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int process_arguments (void)
|
|
{
|
|
ASSERT(!password_file_contents)
|
|
|
|
// resolve netif ipaddr
|
|
if (!BIPAddr_Resolve(&netif_ipaddr, options.netif_ipaddr, 0)) {
|
|
BLog(BLOG_ERROR, "netif ipaddr: BIPAddr_Resolve failed");
|
|
return 0;
|
|
}
|
|
if (netif_ipaddr.type != BADDR_TYPE_IPV4) {
|
|
BLog(BLOG_ERROR, "netif ipaddr: must be an IPv4 address");
|
|
return 0;
|
|
}
|
|
|
|
// resolve netif netmask
|
|
if (!BIPAddr_Resolve(&netif_netmask, options.netif_netmask, 0)) {
|
|
BLog(BLOG_ERROR, "netif netmask: BIPAddr_Resolve failed");
|
|
return 0;
|
|
}
|
|
if (netif_netmask.type != BADDR_TYPE_IPV4) {
|
|
BLog(BLOG_ERROR, "netif netmask: must be an IPv4 address");
|
|
return 0;
|
|
}
|
|
|
|
// parse IP6 address
|
|
if (options.netif_ip6addr) {
|
|
if (!ipaddr6_parse_ipv6_addr(MemRef_MakeCstr(options.netif_ip6addr), &netif_ip6addr)) {
|
|
BLog(BLOG_ERROR, "netif ip6addr: incorrect");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// resolve SOCKS server address
|
|
if (!BAddr_Parse2(&socks_server_addr, options.socks_server_addr, NULL, 0, 0)) {
|
|
BLog(BLOG_ERROR, "socks server addr: BAddr_Parse2 failed");
|
|
return 0;
|
|
}
|
|
|
|
// add none socks authentication method
|
|
socks_auth_info[0] = BSocksClient_auth_none();
|
|
socks_num_auth_info = 1;
|
|
|
|
// add password socks authentication method
|
|
if (options.username) {
|
|
const char *password;
|
|
size_t password_len;
|
|
if (options.password) {
|
|
password = options.password;
|
|
password_len = strlen(options.password);
|
|
} else {
|
|
if (!read_file(options.password_file, &password_file_contents, &password_len)) {
|
|
BLog(BLOG_ERROR, "failed to read password file");
|
|
return 0;
|
|
}
|
|
password = (char *)password_file_contents;
|
|
}
|
|
|
|
socks_auth_info[socks_num_auth_info++] = BSocksClient_auth_password(
|
|
options.username, strlen(options.username),
|
|
password, password_len
|
|
);
|
|
}
|
|
|
|
// resolve remote udpgw server address
|
|
if (options.udpgw_remote_server_addr) {
|
|
if (!BAddr_Parse2(&udpgw_remote_server_addr, options.udpgw_remote_server_addr, NULL, 0, 0)) {
|
|
BLog(BLOG_ERROR, "remote udpgw server addr: BAddr_Parse2 failed");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
void signal_handler (void *unused)
|
|
{
|
|
ASSERT(!quitting)
|
|
|
|
BLog(BLOG_NOTICE, "termination requested");
|
|
|
|
terminate();
|
|
}
|
|
|
|
BAddr baddr_from_lwip (const ip_addr_t *ip_addr, uint16_t port_hostorder)
|
|
{
|
|
BAddr addr;
|
|
if (IP_IS_V6(ip_addr)) {
|
|
BAddr_InitIPv6(&addr, (uint8_t *)ip_addr->u_addr.ip6.addr, hton16(port_hostorder));
|
|
} else {
|
|
BAddr_InitIPv4(&addr, ip_addr->u_addr.ip4.addr, hton16(port_hostorder));
|
|
}
|
|
return addr;
|
|
}
|
|
|
|
void lwip_init_job_hadler (void *unused)
|
|
{
|
|
ASSERT(!quitting)
|
|
ASSERT(netif_ipaddr.type == BADDR_TYPE_IPV4)
|
|
ASSERT(netif_netmask.type == BADDR_TYPE_IPV4)
|
|
ASSERT(!have_netif)
|
|
ASSERT(!listener)
|
|
ASSERT(!listener_ip6)
|
|
|
|
BLog(BLOG_DEBUG, "lwip init");
|
|
|
|
// NOTE: the device may fail during this, but there's no harm in not checking
|
|
// for that at every step
|
|
|
|
// init lwip
|
|
lwip_init();
|
|
|
|
// make addresses for netif
|
|
ip4_addr_t addr;
|
|
addr.addr = netif_ipaddr.ipv4;
|
|
ip4_addr_t netmask;
|
|
netmask.addr = netif_netmask.ipv4;
|
|
ip4_addr_t gw;
|
|
ip4_addr_set_any(&gw);
|
|
|
|
// init netif
|
|
if (!netif_add(&the_netif, &addr, &netmask, &gw, NULL, netif_init_func, netif_input_func)) {
|
|
BLog(BLOG_ERROR, "netif_add failed");
|
|
goto fail;
|
|
}
|
|
have_netif = 1;
|
|
|
|
// set netif up
|
|
netif_set_up(&the_netif);
|
|
|
|
// set netif link up, otherwise ip route will refuse to route
|
|
netif_set_link_up(&the_netif);
|
|
|
|
// set netif pretend TCP
|
|
netif_set_pretend_tcp(&the_netif, 1);
|
|
|
|
// set netif default
|
|
netif_set_default(&the_netif);
|
|
|
|
if (options.netif_ip6addr) {
|
|
// add IPv6 address
|
|
ip6_addr_t ip6addr;
|
|
memset(&ip6addr, 0, sizeof(ip6addr)); // clears any "zone"
|
|
memcpy(ip6addr.addr, netif_ip6addr.bytes, sizeof(netif_ip6addr.bytes));
|
|
netif_ip6_addr_set(&the_netif, 0, &ip6addr);
|
|
netif_ip6_addr_set_state(&the_netif, 0, IP6_ADDR_VALID);
|
|
}
|
|
|
|
// init listener
|
|
struct tcp_pcb *l = tcp_new_ip_type(IPADDR_TYPE_V4);
|
|
if (!l) {
|
|
BLog(BLOG_ERROR, "tcp_new_ip_type failed");
|
|
goto fail;
|
|
}
|
|
|
|
// bind listener
|
|
if (tcp_bind_to_netif(l, "ho0") != ERR_OK) {
|
|
BLog(BLOG_ERROR, "tcp_bind_to_netif failed");
|
|
tcp_close(l);
|
|
goto fail;
|
|
}
|
|
|
|
// ensure the listener only accepts connections from this netif
|
|
tcp_bind_netif(l, &the_netif);
|
|
|
|
// listen listener
|
|
if (!(listener = tcp_listen(l))) {
|
|
BLog(BLOG_ERROR, "tcp_listen failed");
|
|
tcp_close(l);
|
|
goto fail;
|
|
}
|
|
|
|
// setup listener accept handler
|
|
tcp_accept(listener, listener_accept_func);
|
|
|
|
if (options.netif_ip6addr) {
|
|
struct tcp_pcb *l_ip6 = tcp_new_ip_type(IPADDR_TYPE_V6);
|
|
if (!l_ip6) {
|
|
BLog(BLOG_ERROR, "tcp_new_ip_type failed");
|
|
goto fail;
|
|
}
|
|
|
|
if (tcp_bind_to_netif(l_ip6, "ho0") != ERR_OK) {
|
|
BLog(BLOG_ERROR, "tcp_bind_to_netif failed");
|
|
tcp_close(l_ip6);
|
|
goto fail;
|
|
}
|
|
|
|
tcp_bind_netif(l_ip6, &the_netif);
|
|
|
|
if (!(listener_ip6 = tcp_listen(l_ip6))) {
|
|
BLog(BLOG_ERROR, "tcp_listen failed");
|
|
tcp_close(l_ip6);
|
|
goto fail;
|
|
}
|
|
|
|
tcp_accept(listener_ip6, listener_accept_func);
|
|
}
|
|
|
|
return;
|
|
|
|
fail:
|
|
if (!quitting) {
|
|
terminate();
|
|
}
|
|
}
|
|
|
|
void tcp_timer_handler (void *unused)
|
|
{
|
|
ASSERT(!quitting)
|
|
|
|
BLog(BLOG_DEBUG, "TCP timer");
|
|
|
|
// schedule next timer
|
|
BReactor_SetTimer(&ss, &tcp_timer);
|
|
|
|
// call the TCP timer function (every 1/4 second)
|
|
tcp_tmr();
|
|
|
|
// increment tcp_timer_mod4
|
|
tcp_timer_mod4 = (tcp_timer_mod4 + 1) % 4;
|
|
|
|
// every second, call other timer functions
|
|
if (tcp_timer_mod4 == 0) {
|
|
#if IP_REASSEMBLY
|
|
ASSERT(IP_TMR_INTERVAL == 4 * TCP_TMR_INTERVAL)
|
|
ip_reass_tmr();
|
|
#endif
|
|
|
|
#if LWIP_IPV6
|
|
ASSERT(ND6_TMR_INTERVAL == 4 * TCP_TMR_INTERVAL)
|
|
nd6_tmr();
|
|
#endif
|
|
|
|
#if LWIP_IPV6 && LWIP_IPV6_REASS
|
|
ASSERT(IP6_REASS_TMR_INTERVAL == 4 * TCP_TMR_INTERVAL)
|
|
ip6_reass_tmr();
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void device_error_handler (void *unused)
|
|
{
|
|
ASSERT(!quitting)
|
|
|
|
BLog(BLOG_ERROR, "device error");
|
|
|
|
terminate();
|
|
return;
|
|
}
|
|
|
|
void device_read_handler_send (void *unused, uint8_t *data, int data_len)
|
|
{
|
|
ASSERT(!quitting)
|
|
ASSERT(data_len >= 0)
|
|
|
|
BLog(BLOG_DEBUG, "device: received packet");
|
|
|
|
// accept packet
|
|
PacketPassInterface_Done(&device_read_interface);
|
|
|
|
// process UDP directly
|
|
if (process_device_udp_packet(data, data_len)) {
|
|
return;
|
|
}
|
|
|
|
// obtain pbuf
|
|
if (data_len > UINT16_MAX) {
|
|
BLog(BLOG_WARNING, "device read: packet too large");
|
|
return;
|
|
}
|
|
struct pbuf *p = pbuf_alloc(PBUF_RAW, data_len, PBUF_POOL);
|
|
if (!p) {
|
|
BLog(BLOG_WARNING, "device read: pbuf_alloc failed");
|
|
return;
|
|
}
|
|
|
|
// write packet to pbuf
|
|
ASSERT_FORCE(pbuf_take(p, data, data_len) == ERR_OK)
|
|
|
|
// pass pbuf to input
|
|
if (the_netif.input(p, &the_netif) != ERR_OK) {
|
|
BLog(BLOG_WARNING, "device read: input failed");
|
|
pbuf_free(p);
|
|
}
|
|
}
|
|
|
|
int process_device_udp_packet (uint8_t *data, int data_len)
|
|
{
|
|
ASSERT(data_len >= 0)
|
|
|
|
// 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;
|
|
|
|
uint8_t ip_version = 0;
|
|
if (data_len > 0) {
|
|
ip_version = (data[0] >> 4);
|
|
}
|
|
|
|
switch (ip_version) {
|
|
case 4: {
|
|
// ignore non-UDP packets
|
|
if (data_len < sizeof(struct ipv4_header) || data[offsetof(struct ipv4_header, protocol)] != IPV4_PROTOCOL_UDP) {
|
|
goto fail;
|
|
}
|
|
|
|
// parse IPv4 header
|
|
struct ipv4_header ipv4_header;
|
|
if (!ipv4_check(data, data_len, &ipv4_header, &data, &data_len)) {
|
|
goto fail;
|
|
}
|
|
|
|
// parse UDP
|
|
struct udp_header udp_header;
|
|
if (!udp_check(data, data_len, &udp_header, &data, &data_len)) {
|
|
goto fail;
|
|
}
|
|
|
|
// verify UDP checksum
|
|
uint16_t checksum_in_packet = udp_header.checksum;
|
|
udp_header.checksum = 0;
|
|
uint16_t checksum_computed = udp_checksum(&udp_header, data, data_len, ipv4_header.source_address, ipv4_header.destination_address);
|
|
if (checksum_in_packet != checksum_computed) {
|
|
goto fail;
|
|
}
|
|
|
|
BLog(BLOG_INFO, "UDP: from device %d bytes", data_len);
|
|
|
|
// construct addresses
|
|
BAddr_InitIPv4(&local_addr, ipv4_header.source_address, udp_header.source_port);
|
|
BAddr_InitIPv4(&remote_addr, ipv4_header.destination_address, udp_header.dest_port);
|
|
|
|
// if transparent DNS is enabled, any packet arriving at out netif
|
|
// address to port 53 is considered a DNS packet
|
|
is_dns = (options.udpgw_transparent_dns &&
|
|
ipv4_header.destination_address == netif_ipaddr.ipv4 &&
|
|
udp_header.dest_port == hton16(53));
|
|
} break;
|
|
|
|
case 6: {
|
|
// ignore if IPv6 support is disabled
|
|
if (!options.netif_ip6addr) {
|
|
goto fail;
|
|
}
|
|
|
|
// ignore non-UDP packets
|
|
if (data_len < sizeof(struct ipv6_header) || data[offsetof(struct ipv6_header, next_header)] != IPV6_NEXT_UDP) {
|
|
goto fail;
|
|
}
|
|
|
|
// parse IPv6 header
|
|
struct ipv6_header ipv6_header;
|
|
if (!ipv6_check(data, data_len, &ipv6_header, &data, &data_len)) {
|
|
goto fail;
|
|
}
|
|
|
|
// parse UDP
|
|
struct udp_header udp_header;
|
|
if (!udp_check(data, data_len, &udp_header, &data, &data_len)) {
|
|
goto fail;
|
|
}
|
|
|
|
// verify UDP checksum
|
|
uint16_t checksum_in_packet = udp_header.checksum;
|
|
udp_header.checksum = 0;
|
|
uint16_t checksum_computed = udp_ip6_checksum(&udp_header, data, data_len, ipv6_header.source_address, ipv6_header.destination_address);
|
|
if (checksum_in_packet != checksum_computed) {
|
|
goto fail;
|
|
}
|
|
|
|
BLog(BLOG_INFO, "UDP/IPv6: from device %d bytes", data_len);
|
|
|
|
// construct addresses
|
|
BAddr_InitIPv6(&local_addr, ipv6_header.source_address, udp_header.source_port);
|
|
BAddr_InitIPv6(&remote_addr, ipv6_header.destination_address, udp_header.dest_port);
|
|
|
|
// TODO dns
|
|
is_dns = 0;
|
|
} break;
|
|
|
|
default: {
|
|
goto fail;
|
|
} break;
|
|
}
|
|
|
|
// check payload length
|
|
if (data_len > udp_mtu) {
|
|
BLog(BLOG_ERROR, "packet is too large, cannot send to udpgw");
|
|
goto fail;
|
|
}
|
|
|
|
// 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;
|
|
|
|
fail:
|
|
return 0;
|
|
}
|
|
|
|
err_t netif_init_func (struct netif *netif)
|
|
{
|
|
BLog(BLOG_DEBUG, "netif func init");
|
|
|
|
netif->name[0] = 'h';
|
|
netif->name[1] = 'o';
|
|
netif->output = netif_output_func;
|
|
netif->output_ip6 = netif_output_ip6_func;
|
|
|
|
return ERR_OK;
|
|
}
|
|
|
|
err_t netif_output_func (struct netif *netif, struct pbuf *p, const ip4_addr_t *ipaddr)
|
|
{
|
|
return common_netif_output(netif, p);
|
|
}
|
|
|
|
err_t netif_output_ip6_func (struct netif *netif, struct pbuf *p, const ip6_addr_t *ipaddr)
|
|
{
|
|
return common_netif_output(netif, p);
|
|
}
|
|
|
|
err_t common_netif_output (struct netif *netif, struct pbuf *p)
|
|
{
|
|
SYNC_DECL
|
|
|
|
BLog(BLOG_DEBUG, "device write: send packet");
|
|
|
|
if (quitting) {
|
|
return ERR_OK;
|
|
}
|
|
|
|
// if there is just one chunk, send it directly, else via buffer
|
|
if (!p->next) {
|
|
if (p->len > BTap_GetMTU(&device)) {
|
|
BLog(BLOG_WARNING, "netif func output: no space left");
|
|
goto out;
|
|
}
|
|
|
|
SYNC_FROMHERE
|
|
BTap_Send(&device, (uint8_t *)p->payload, p->len);
|
|
SYNC_COMMIT
|
|
} else {
|
|
int len = 0;
|
|
do {
|
|
if (p->len > BTap_GetMTU(&device) - len) {
|
|
BLog(BLOG_WARNING, "netif func output: no space left");
|
|
goto out;
|
|
}
|
|
memcpy(device_write_buf + len, p->payload, p->len);
|
|
len += p->len;
|
|
} while (p = p->next);
|
|
|
|
SYNC_FROMHERE
|
|
BTap_Send(&device, device_write_buf, len);
|
|
SYNC_COMMIT
|
|
}
|
|
|
|
out:
|
|
return ERR_OK;
|
|
}
|
|
|
|
err_t netif_input_func (struct pbuf *p, struct netif *inp)
|
|
{
|
|
uint8_t ip_version = 0;
|
|
if (p->len > 0) {
|
|
ip_version = (((uint8_t *)p->payload)[0] >> 4);
|
|
}
|
|
|
|
switch (ip_version) {
|
|
case 4: {
|
|
return ip_input(p, inp);
|
|
} break;
|
|
case 6: {
|
|
if (options.netif_ip6addr) {
|
|
return ip6_input(p, inp);
|
|
}
|
|
} break;
|
|
}
|
|
|
|
pbuf_free(p);
|
|
return ERR_OK;
|
|
}
|
|
|
|
void client_logfunc (struct tcp_client *client)
|
|
{
|
|
char local_addr_s[BADDR_MAX_PRINT_LEN];
|
|
BAddr_Print(&client->local_addr, local_addr_s);
|
|
char remote_addr_s[BADDR_MAX_PRINT_LEN];
|
|
BAddr_Print(&client->remote_addr, remote_addr_s);
|
|
|
|
BLog_Append("%05d (%s %s): ", num_clients, local_addr_s, remote_addr_s);
|
|
}
|
|
|
|
void client_log (struct tcp_client *client, int level, const char *fmt, ...)
|
|
{
|
|
va_list vl;
|
|
va_start(vl, fmt);
|
|
BLog_LogViaFuncVarArg((BLog_logfunc)client_logfunc, client, BLOG_CURRENT_CHANNEL, level, fmt, vl);
|
|
va_end(vl);
|
|
}
|
|
|
|
err_t listener_accept_func (void *arg, struct tcp_pcb *newpcb, err_t err)
|
|
{
|
|
ASSERT(err == ERR_OK)
|
|
|
|
// allocate client structure
|
|
struct tcp_client *client = (struct tcp_client *)malloc(sizeof(*client));
|
|
if (!client) {
|
|
BLog(BLOG_ERROR, "listener accept: malloc failed");
|
|
goto fail0;
|
|
}
|
|
client->socks_username = NULL;
|
|
|
|
SYNC_DECL
|
|
SYNC_FROMHERE
|
|
|
|
// read addresses
|
|
client->local_addr = baddr_from_lwip(&newpcb->local_ip, newpcb->local_port);
|
|
client->remote_addr = baddr_from_lwip(&newpcb->remote_ip, newpcb->remote_port);
|
|
|
|
// get destination address
|
|
BAddr addr = client->local_addr;
|
|
#ifdef OVERRIDE_DEST_ADDR
|
|
ASSERT_FORCE(BAddr_Parse2(&addr, OVERRIDE_DEST_ADDR, NULL, 0, 1))
|
|
#endif
|
|
|
|
// add source address to username if requested
|
|
if (options.username && options.append_source_to_username) {
|
|
char addr_str[BADDR_MAX_PRINT_LEN];
|
|
BAddr_Print(&client->remote_addr, addr_str);
|
|
client->socks_username = concat_strings(3, options.username, "@", addr_str);
|
|
if (!client->socks_username) {
|
|
goto fail1;
|
|
}
|
|
socks_auth_info[1].password.username = client->socks_username;
|
|
socks_auth_info[1].password.username_len = strlen(client->socks_username);
|
|
}
|
|
|
|
// init SOCKS
|
|
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;
|
|
}
|
|
|
|
// init aborted and dead_aborted
|
|
client->aborted = 0;
|
|
DEAD_INIT(client->dead_aborted);
|
|
|
|
// add to linked list
|
|
LinkedList1_Append(&tcp_clients, &client->list_node);
|
|
|
|
// increment counter
|
|
ASSERT(num_clients >= 0)
|
|
num_clients++;
|
|
|
|
// set pcb
|
|
client->pcb = newpcb;
|
|
|
|
// set client not closed
|
|
client->client_closed = 0;
|
|
|
|
// setup handler argument
|
|
tcp_arg(client->pcb, client);
|
|
|
|
// setup handlers
|
|
tcp_err(client->pcb, client_err_func);
|
|
tcp_recv(client->pcb, client_recv_func);
|
|
|
|
// setup buffer
|
|
client->buf_used = 0;
|
|
|
|
// set SOCKS not up, not closed
|
|
client->socks_up = 0;
|
|
client->socks_closed = 0;
|
|
|
|
client_log(client, BLOG_INFO, "accepted");
|
|
|
|
DEAD_ENTER(client->dead_aborted)
|
|
SYNC_COMMIT
|
|
DEAD_LEAVE2(client->dead_aborted)
|
|
|
|
// Return ERR_ABRT if and only if tcp_abort was called from this callback.
|
|
return (DEAD_KILLED > 0) ? ERR_ABRT : ERR_OK;
|
|
|
|
fail1:
|
|
SYNC_BREAK
|
|
free(client->socks_username);
|
|
free(client);
|
|
fail0:
|
|
return ERR_MEM;
|
|
}
|
|
|
|
void client_handle_freed_client (struct tcp_client *client)
|
|
{
|
|
ASSERT(!client->client_closed)
|
|
|
|
// pcb was taken care of by the caller
|
|
|
|
// set client closed
|
|
client->client_closed = 1;
|
|
|
|
// if we have data to be sent to SOCKS and can send it, keep sending
|
|
if (client->buf_used > 0 && !client->socks_closed) {
|
|
client_log(client, BLOG_INFO, "waiting untill buffered data is sent to SOCKS");
|
|
} else {
|
|
if (!client->socks_closed) {
|
|
client_free_socks(client);
|
|
} else {
|
|
client_dealloc(client);
|
|
}
|
|
}
|
|
}
|
|
|
|
void client_free_client (struct tcp_client *client)
|
|
{
|
|
ASSERT(!client->client_closed)
|
|
|
|
// remove callbacks
|
|
tcp_err(client->pcb, NULL);
|
|
tcp_recv(client->pcb, NULL);
|
|
tcp_sent(client->pcb, NULL);
|
|
|
|
// free pcb
|
|
err_t err = tcp_close(client->pcb);
|
|
if (err != ERR_OK) {
|
|
client_log(client, BLOG_ERROR, "tcp_close failed (%d)", err);
|
|
client_abort_pcb(client);
|
|
}
|
|
|
|
client_handle_freed_client(client);
|
|
}
|
|
|
|
void client_abort_client (struct tcp_client *client)
|
|
{
|
|
ASSERT(!client->client_closed)
|
|
|
|
// remove callbacks
|
|
tcp_err(client->pcb, NULL);
|
|
tcp_recv(client->pcb, NULL);
|
|
tcp_sent(client->pcb, NULL);
|
|
|
|
// abort
|
|
client_abort_pcb(client);
|
|
|
|
client_handle_freed_client(client);
|
|
}
|
|
|
|
void client_abort_pcb (struct tcp_client *client)
|
|
{
|
|
ASSERT(!client->aborted)
|
|
|
|
// abort the PCB
|
|
tcp_abort(client->pcb);
|
|
|
|
// mark aborted
|
|
client->aborted = 1;
|
|
|
|
// kill dead_aborted with value 1 signaling that tcp_abort was done;
|
|
// this is contrasted to killing with value -1 from client_dealloc
|
|
// signaling that the client was freed without tcp_abort
|
|
DEAD_KILL_WITH(client->dead_aborted, 1);
|
|
}
|
|
|
|
void client_free_socks (struct tcp_client *client)
|
|
{
|
|
ASSERT(!client->socks_closed)
|
|
|
|
// stop sending to SOCKS
|
|
if (client->socks_up) {
|
|
// stop receiving from client
|
|
if (!client->client_closed) {
|
|
tcp_recv(client->pcb, NULL);
|
|
}
|
|
}
|
|
|
|
// free SOCKS
|
|
BSocksClient_Free(&client->socks_client);
|
|
|
|
// set SOCKS closed
|
|
client->socks_closed = 1;
|
|
|
|
// if we have data to be sent to the client and we can send it, keep sending
|
|
if (client->socks_up && (client->socks_recv_buf_used >= 0 || client->socks_recv_tcp_pending > 0) && !client->client_closed) {
|
|
client_log(client, BLOG_INFO, "waiting until buffered data is sent to client");
|
|
} else {
|
|
if (!client->client_closed) {
|
|
client_free_client(client);
|
|
} else {
|
|
client_dealloc(client);
|
|
}
|
|
}
|
|
}
|
|
|
|
void client_murder (struct tcp_client *client)
|
|
{
|
|
// free client
|
|
if (!client->client_closed) {
|
|
// remove callbacks
|
|
tcp_err(client->pcb, NULL);
|
|
tcp_recv(client->pcb, NULL);
|
|
tcp_sent(client->pcb, NULL);
|
|
|
|
// abort
|
|
client_abort_pcb(client);
|
|
|
|
// set client closed
|
|
client->client_closed = 1;
|
|
}
|
|
|
|
// free SOCKS
|
|
if (!client->socks_closed) {
|
|
// free SOCKS
|
|
BSocksClient_Free(&client->socks_client);
|
|
|
|
// set SOCKS closed
|
|
client->socks_closed = 1;
|
|
}
|
|
|
|
// dealloc entry
|
|
client_dealloc(client);
|
|
}
|
|
|
|
void client_dealloc (struct tcp_client *client)
|
|
{
|
|
ASSERT(client->client_closed)
|
|
ASSERT(client->socks_closed)
|
|
|
|
// decrement counter
|
|
ASSERT(num_clients > 0)
|
|
num_clients--;
|
|
|
|
// remove client entry
|
|
LinkedList1_Remove(&tcp_clients, &client->list_node);
|
|
|
|
// kill dead_aborted with value -1 unless already aborted
|
|
if (!client->aborted) {
|
|
DEAD_KILL_WITH(client->dead_aborted, -1);
|
|
}
|
|
|
|
// free memory
|
|
free(client->socks_username);
|
|
free(client);
|
|
}
|
|
|
|
void client_err_func (void *arg, err_t err)
|
|
{
|
|
struct tcp_client *client = (struct tcp_client *)arg;
|
|
ASSERT(!client->client_closed)
|
|
|
|
client_log(client, BLOG_INFO, "client error (%d)", (int)err);
|
|
|
|
// the pcb was taken care of by the caller
|
|
client_handle_freed_client(client);
|
|
}
|
|
|
|
err_t client_recv_func (void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
|
|
{
|
|
struct tcp_client *client = (struct tcp_client *)arg;
|
|
ASSERT(!client->client_closed)
|
|
ASSERT(err == ERR_OK) // checked in lwIP source. Otherwise, I've no idea what should
|
|
// be done with the pbuf in case of an error.
|
|
|
|
DEAD_ENTER(client->dead_aborted)
|
|
|
|
if (!p) {
|
|
client_log(client, BLOG_INFO, "client closed");
|
|
client_free_client(client);
|
|
} else {
|
|
ASSERT(p->tot_len > 0)
|
|
|
|
// check if we have enough buffer
|
|
if (p->tot_len > sizeof(client->buf) - client->buf_used) {
|
|
client_log(client, BLOG_ERROR, "no buffer for data !?!");
|
|
DEAD_LEAVE2(client->dead_aborted)
|
|
return ERR_MEM;
|
|
}
|
|
|
|
// copy data to buffer
|
|
ASSERT_EXECUTE(pbuf_copy_partial(p, client->buf + client->buf_used, p->tot_len, 0) == p->tot_len)
|
|
client->buf_used += p->tot_len;
|
|
|
|
// free pbuff
|
|
int p_tot_len = p->tot_len;
|
|
pbuf_free(p);
|
|
|
|
// if there was nothing in the buffer before, and SOCKS is up, start send data
|
|
if (client->buf_used == p_tot_len && client->socks_up) {
|
|
ASSERT(!client->socks_closed) // this callback is removed when SOCKS is closed
|
|
|
|
SYNC_DECL
|
|
SYNC_FROMHERE
|
|
client_send_to_socks(client);
|
|
SYNC_COMMIT
|
|
}
|
|
}
|
|
|
|
DEAD_LEAVE2(client->dead_aborted)
|
|
|
|
// Return ERR_ABRT if and only if tcp_abort was called from this callback.
|
|
return (DEAD_KILLED > 0) ? ERR_ABRT : ERR_OK;
|
|
}
|
|
|
|
void client_socks_handler (struct tcp_client *client, int event)
|
|
{
|
|
ASSERT(!client->socks_closed)
|
|
|
|
switch (event) {
|
|
case BSOCKSCLIENT_EVENT_ERROR: {
|
|
client_log(client, BLOG_INFO, "SOCKS error");
|
|
|
|
client_free_socks(client);
|
|
} break;
|
|
|
|
case BSOCKSCLIENT_EVENT_UP: {
|
|
ASSERT(!client->socks_up)
|
|
|
|
client_log(client, BLOG_INFO, "SOCKS up");
|
|
|
|
// init sending
|
|
client->socks_send_if = BSocksClient_GetSendInterface(&client->socks_client);
|
|
StreamPassInterface_Sender_Init(client->socks_send_if, (StreamPassInterface_handler_done)client_socks_send_handler_done, client);
|
|
|
|
// init receiving
|
|
client->socks_recv_if = BSocksClient_GetRecvInterface(&client->socks_client);
|
|
StreamRecvInterface_Receiver_Init(client->socks_recv_if, (StreamRecvInterface_handler_done)client_socks_recv_handler_done, client);
|
|
client->socks_recv_buf_used = -1;
|
|
client->socks_recv_tcp_pending = 0;
|
|
if (!client->client_closed) {
|
|
tcp_sent(client->pcb, client_sent_func);
|
|
}
|
|
|
|
// set up
|
|
client->socks_up = 1;
|
|
|
|
// start sending data if there is any
|
|
if (client->buf_used > 0) {
|
|
client_send_to_socks(client);
|
|
}
|
|
|
|
// start receiving data if client is still up
|
|
if (!client->client_closed) {
|
|
client_socks_recv_initiate(client);
|
|
}
|
|
} break;
|
|
|
|
case BSOCKSCLIENT_EVENT_ERROR_CLOSED: {
|
|
ASSERT(client->socks_up)
|
|
|
|
client_log(client, BLOG_INFO, "SOCKS closed");
|
|
|
|
client_free_socks(client);
|
|
} break;
|
|
}
|
|
}
|
|
|
|
void client_send_to_socks (struct tcp_client *client)
|
|
{
|
|
ASSERT(!client->socks_closed)
|
|
ASSERT(client->socks_up)
|
|
ASSERT(client->buf_used > 0)
|
|
|
|
// schedule sending
|
|
StreamPassInterface_Sender_Send(client->socks_send_if, client->buf, client->buf_used);
|
|
}
|
|
|
|
void client_socks_send_handler_done (struct tcp_client *client, int data_len)
|
|
{
|
|
ASSERT(!client->socks_closed)
|
|
ASSERT(client->socks_up)
|
|
ASSERT(client->buf_used > 0)
|
|
ASSERT(data_len > 0)
|
|
ASSERT(data_len <= client->buf_used)
|
|
|
|
// remove sent data from buffer
|
|
memmove(client->buf, client->buf + data_len, client->buf_used - data_len);
|
|
client->buf_used -= data_len;
|
|
|
|
if (!client->client_closed) {
|
|
// confirm sent data
|
|
tcp_recved(client->pcb, data_len);
|
|
}
|
|
|
|
if (client->buf_used > 0) {
|
|
// send any further data
|
|
StreamPassInterface_Sender_Send(client->socks_send_if, client->buf, client->buf_used);
|
|
}
|
|
else if (client->client_closed) {
|
|
// client was closed we've sent everything we had buffered; we're done with it
|
|
client_log(client, BLOG_INFO, "removing after client went down");
|
|
|
|
client_free_socks(client);
|
|
}
|
|
}
|
|
|
|
void client_socks_recv_initiate (struct tcp_client *client)
|
|
{
|
|
ASSERT(!client->client_closed)
|
|
ASSERT(!client->socks_closed)
|
|
ASSERT(client->socks_up)
|
|
ASSERT(client->socks_recv_buf_used == -1)
|
|
|
|
StreamRecvInterface_Receiver_Recv(client->socks_recv_if, client->socks_recv_buf, sizeof(client->socks_recv_buf));
|
|
}
|
|
|
|
void client_socks_recv_handler_done (struct tcp_client *client, int data_len)
|
|
{
|
|
ASSERT(data_len > 0)
|
|
ASSERT(data_len <= sizeof(client->socks_recv_buf))
|
|
ASSERT(!client->socks_closed)
|
|
ASSERT(client->socks_up)
|
|
ASSERT(client->socks_recv_buf_used == -1)
|
|
|
|
// if client was closed, stop receiving
|
|
if (client->client_closed) {
|
|
return;
|
|
}
|
|
|
|
// set amount of data in buffer
|
|
client->socks_recv_buf_used = data_len;
|
|
client->socks_recv_buf_sent = 0;
|
|
client->socks_recv_waiting = 0;
|
|
|
|
// send to client
|
|
if (client_socks_recv_send_out(client) < 0) {
|
|
return;
|
|
}
|
|
|
|
// continue receiving if needed
|
|
if (client->socks_recv_buf_used == -1) {
|
|
client_socks_recv_initiate(client);
|
|
}
|
|
}
|
|
|
|
int client_socks_recv_send_out (struct tcp_client *client)
|
|
{
|
|
ASSERT(!client->client_closed)
|
|
ASSERT(client->socks_up)
|
|
ASSERT(client->socks_recv_buf_used > 0)
|
|
ASSERT(client->socks_recv_buf_sent < client->socks_recv_buf_used)
|
|
ASSERT(!client->socks_recv_waiting)
|
|
|
|
// return value -1 means tcp_abort() was done,
|
|
// 0 means it wasn't and the client (pcb) is still up
|
|
|
|
do {
|
|
int to_write = bmin_int(client->socks_recv_buf_used - client->socks_recv_buf_sent, tcp_sndbuf(client->pcb));
|
|
if (to_write == 0) {
|
|
break;
|
|
}
|
|
|
|
err_t err = tcp_write(client->pcb, client->socks_recv_buf + client->socks_recv_buf_sent, to_write, TCP_WRITE_FLAG_COPY);
|
|
if (err != ERR_OK) {
|
|
if (err == ERR_MEM) {
|
|
break;
|
|
}
|
|
|
|
client_log(client, BLOG_INFO, "tcp_write failed (%d)", (int)err);
|
|
|
|
client_abort_client(client);
|
|
return -1;
|
|
}
|
|
|
|
client->socks_recv_buf_sent += to_write;
|
|
client->socks_recv_tcp_pending += to_write;
|
|
} while (client->socks_recv_buf_sent < client->socks_recv_buf_used);
|
|
|
|
// start sending now
|
|
err_t err = tcp_output(client->pcb);
|
|
if (err != ERR_OK) {
|
|
client_log(client, BLOG_INFO, "tcp_output failed (%d)", (int)err);
|
|
|
|
client_abort_client(client);
|
|
return -1;
|
|
}
|
|
|
|
// more data to queue?
|
|
if (client->socks_recv_buf_sent < client->socks_recv_buf_used) {
|
|
if (client->socks_recv_tcp_pending == 0) {
|
|
client_log(client, BLOG_ERROR, "can't queue data, but all data was confirmed !?!");
|
|
|
|
client_abort_client(client);
|
|
return -1;
|
|
}
|
|
|
|
// set waiting, continue in client_sent_func
|
|
client->socks_recv_waiting = 1;
|
|
return 0;
|
|
}
|
|
|
|
// everything was queued
|
|
client->socks_recv_buf_used = -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
err_t client_sent_func (void *arg, struct tcp_pcb *tpcb, u16_t len)
|
|
{
|
|
struct tcp_client *client = (struct tcp_client *)arg;
|
|
|
|
ASSERT(!client->client_closed)
|
|
ASSERT(client->socks_up)
|
|
ASSERT(len > 0)
|
|
ASSERT(len <= client->socks_recv_tcp_pending)
|
|
|
|
DEAD_ENTER(client->dead_aborted)
|
|
|
|
// decrement pending
|
|
client->socks_recv_tcp_pending -= len;
|
|
|
|
// continue queuing
|
|
if (client->socks_recv_buf_used > 0) {
|
|
ASSERT(client->socks_recv_waiting)
|
|
ASSERT(client->socks_recv_buf_sent < client->socks_recv_buf_used)
|
|
|
|
// set not waiting
|
|
client->socks_recv_waiting = 0;
|
|
|
|
// possibly send more data
|
|
if (client_socks_recv_send_out(client) < 0) {
|
|
goto out;
|
|
}
|
|
|
|
// we just queued some data, so it can't have been confirmed yet
|
|
ASSERT(client->socks_recv_tcp_pending > 0)
|
|
|
|
// continue receiving if needed
|
|
if (client->socks_recv_buf_used == -1 && !client->socks_closed) {
|
|
SYNC_DECL
|
|
SYNC_FROMHERE
|
|
client_socks_recv_initiate(client);
|
|
SYNC_COMMIT
|
|
}
|
|
} else {
|
|
// have we sent everything after SOCKS was closed?
|
|
if (client->socks_closed && client->socks_recv_tcp_pending == 0) {
|
|
client_log(client, BLOG_INFO, "removing after SOCKS went down");
|
|
client_free_client(client);
|
|
}
|
|
}
|
|
|
|
out:
|
|
DEAD_LEAVE2(client->dead_aborted)
|
|
|
|
// Return ERR_ABRT if and only if tcp_abort was called from this callback.
|
|
return (DEAD_KILLED > 0) ? ERR_ABRT : ERR_OK;
|
|
}
|
|
|
|
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 %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))
|
|
) {
|
|
BLog(BLOG_ERROR, "UDP: packet is too large");
|
|
return;
|
|
}
|
|
|
|
// build IP header
|
|
struct ipv4_header iph;
|
|
iph.version4_ihl4 = IPV4_MAKE_VERSION_IHL(sizeof(iph));
|
|
iph.ds = hton8(0);
|
|
iph.total_length = hton16(sizeof(iph) + sizeof(struct udp_header) + data_len);
|
|
iph.identification = hton16(0);
|
|
iph.flags3_fragmentoffset13 = hton16(0);
|
|
iph.ttl = hton8(64);
|
|
iph.protocol = hton8(IPV4_PROTOCOL_UDP);
|
|
iph.checksum = hton16(0);
|
|
iph.source_address = remote_addr.ipv4.ip;
|
|
iph.destination_address = local_addr.ipv4.ip;
|
|
iph.checksum = ipv4_checksum(&iph, NULL, 0);
|
|
|
|
// build UDP header
|
|
struct udp_header udph;
|
|
udph.source_port = remote_addr.ipv4.port;
|
|
udph.dest_port = local_addr.ipv4.port;
|
|
udph.length = hton16(sizeof(udph) + data_len);
|
|
udph.checksum = hton16(0);
|
|
udph.checksum = udp_checksum(&udph, data, data_len, iph.source_address, iph.destination_address);
|
|
|
|
// write packet
|
|
memcpy(device_write_buf, &iph, sizeof(iph));
|
|
memcpy(device_write_buf + sizeof(iph), &udph, sizeof(udph));
|
|
memcpy(device_write_buf + sizeof(iph) + sizeof(udph), data, data_len);
|
|
packet_length = sizeof(iph) + sizeof(udph) + data_len;
|
|
} break;
|
|
|
|
case BADDR_TYPE_IPV6: {
|
|
BLog(BLOG_INFO, "UDP/IPv6: from %s %d bytes", source_name, data_len);
|
|
|
|
if (!options.netif_ip6addr) {
|
|
BLog(BLOG_ERROR, "got IPv6 packet from %s but IPv6 is disabled", source_name);
|
|
return;
|
|
}
|
|
|
|
if (data_len > UINT16_MAX - sizeof(struct udp_header) ||
|
|
data_len > BTap_GetMTU(&device) - (int)(sizeof(struct ipv6_header) + sizeof(struct udp_header))
|
|
) {
|
|
BLog(BLOG_ERROR, "UDP/IPv6: packet is too large");
|
|
return;
|
|
}
|
|
|
|
// build IPv6 header
|
|
struct ipv6_header iph;
|
|
iph.version4_tc4 = hton8((6 << 4));
|
|
iph.tc4_fl4 = hton8(0);
|
|
iph.fl = hton16(0);
|
|
iph.payload_length = hton16(sizeof(struct udp_header) + data_len);
|
|
iph.next_header = hton8(IPV6_NEXT_UDP);
|
|
iph.hop_limit = hton8(64);
|
|
memcpy(iph.source_address, remote_addr.ipv6.ip, 16);
|
|
memcpy(iph.destination_address, local_addr.ipv6.ip, 16);
|
|
|
|
// build UDP header
|
|
struct udp_header udph;
|
|
udph.source_port = remote_addr.ipv6.port;
|
|
udph.dest_port = local_addr.ipv6.port;
|
|
udph.length = hton16(sizeof(udph) + data_len);
|
|
udph.checksum = hton16(0);
|
|
udph.checksum = udp_ip6_checksum(&udph, data, data_len, iph.source_address, iph.destination_address);
|
|
|
|
// write packet
|
|
memcpy(device_write_buf, &iph, sizeof(iph));
|
|
memcpy(device_write_buf + sizeof(iph), &udph, sizeof(udph));
|
|
memcpy(device_write_buf + sizeof(iph) + sizeof(udph), data, data_len);
|
|
packet_length = sizeof(iph) + sizeof(udph) + data_len;
|
|
} break;
|
|
}
|
|
|
|
// submit packet
|
|
BTap_Send(&device, device_write_buf, packet_length);
|
|
}
|