@ -7,6 +7,7 @@
# include "u_async.h"
# include "ll_queue.h"
# include "settings.h"
# include "config_parser.h"
# include <stdlib.h>
# include <string.h>
@ -19,6 +20,46 @@
# include <netinet/in.h>
# include <arpa/inet.h>
# include <fcntl.h>
# include <net/if.h> /* for struct ifreq, IFNAMSIZ */
# ifdef __linux__
# include <linux/netfilter.h> /* for SO_MARK */
# endif
/* Helper function: hex string to binary */
static int hex_to_bin ( const char * hex , uint8_t * bin , size_t bin_len ) {
if ( ! hex | | ! bin ) return - 1 ;
size_t hex_len = strlen ( hex ) ;
if ( hex_len % 2 ! = 0 | | hex_len / 2 > bin_len ) return - 1 ;
for ( size_t i = 0 ; i < hex_len ; i + = 2 ) {
char byte_str [ 3 ] = { hex [ i ] , hex [ i + 1 ] , ' \0 ' } ;
char * endptr ;
long val = strtol ( byte_str , & endptr , 16 ) ;
if ( * endptr ! = ' \0 ' | | val < 0 | | val > 255 ) return - 1 ;
bin [ i / 2 ] = ( uint8_t ) val ;
}
return hex_len / 2 ;
}
/* Socket options */
# ifdef __linux__
# include <linux/netfilter_ipv4.h> /* for SO_MARK */
# include <net/if.h> /* for struct ifreq, IFNAMSIZ */
# endif
/* Структура маршрута (server:client pair) */
typedef struct conn_route {
int sockfd ;
struct sockaddr_in local_addr ;
struct sockaddr_in remote_addr ;
int so_mark ;
char netif [ MAX_NETIF_LEN ] ;
char server_name [ MAX_CONN_NAME_LEN ] ;
char client_name [ MAX_CONN_NAME_LEN ] ;
uint8_t is_active ;
} conn_route_t ;
/* Внутренняя структура подключения */
struct conn_handle {
@ -56,6 +97,12 @@ struct conn_handle {
/* Async instance */
uasync_t * ua ;
/* Multi-route support */
conn_route_t * routes ;
int route_count ;
int route_capacity ;
int active_route_idx ;
} ;
/* Внутренние функции */
@ -594,6 +641,85 @@ static int create_udp_socket(const char* ip, uint16_t port, struct sockaddr_in*
return sock ;
}
static int create_udp_socket_with_opts ( const char * ip , uint16_t port ,
struct sockaddr_in * addr ,
int so_mark , const char * netif ) {
int sock = socket ( AF_INET , SOCK_DGRAM , 0 ) ;
if ( sock < 0 ) {
return - 1 ;
}
/* Suppress unused parameter warnings */
( void ) so_mark ;
( void ) netif ;
/* Set socket options */
# ifdef SO_MARK
if ( so_mark > 0 ) {
if ( setsockopt ( sock , SOL_SOCKET , SO_MARK , & so_mark , sizeof ( so_mark ) ) < 0 ) {
fprintf ( stderr , " Failed to set SO_MARK=%d: %s \n " , so_mark , strerror ( errno ) ) ;
/* Continue anyway */
}
# ifdef ETCP_DEBUG
else {
fprintf ( stderr , " Successfully set SO_MARK=%d \n " , so_mark ) ;
}
# endif
}
# endif
# ifdef SO_BINDTODEVICE
if ( netif & & netif [ 0 ] ! = ' \0 ' ) {
struct ifreq ifr ;
memset ( & ifr , 0 , sizeof ( ifr ) ) ;
strncpy ( ifr . ifr_name , netif , IFNAMSIZ - 1 ) ;
if ( setsockopt ( sock , SOL_SOCKET , SO_BINDTODEVICE , & ifr , sizeof ( ifr ) ) < 0 ) {
fprintf ( stderr , " Failed to bind to device '%s': %s \n " , netif , strerror ( errno ) ) ;
/* Continue anyway */
}
# ifdef ETCP_DEBUG
else {
fprintf ( stderr , " Successfully bound socket to device '%s' \n " , netif ) ;
}
# endif
}
# endif
/* Установка non-blocking режима */
int flags = fcntl ( sock , F_GETFL , 0 ) ;
if ( flags < 0 ) {
close ( sock ) ;
return - 1 ;
}
if ( fcntl ( sock , F_SETFL , flags | O_NONBLOCK ) < 0 ) {
close ( sock ) ;
return - 1 ;
}
/* Настройка адреса */
memset ( addr , 0 , sizeof ( * addr ) ) ;
addr - > sin_family = AF_INET ;
addr - > sin_port = htons ( port ) ;
if ( ip & & ip [ 0 ] ! = ' \0 ' ) {
if ( inet_pton ( AF_INET , ip , & addr - > sin_addr ) ! = 1 ) {
close ( sock ) ;
return - 1 ;
}
} else {
addr - > sin_addr . s_addr = INADDR_ANY ;
}
/* Bind к адресу */
if ( bind ( sock , ( struct sockaddr * ) addr , sizeof ( * addr ) ) < 0 ) {
close ( sock ) ;
return - 1 ;
}
return sock ;
}
static void socket_read_callback ( void * arg )
{
conn_handle_t * conn = ( conn_handle_t * ) arg ;
@ -644,7 +770,27 @@ static void etcp_tx_callback(epkt_t* epkt, uint8_t* data, uint16_t len, void* ar
{
( void ) epkt ; /* unused parameter */
conn_handle_t * conn = ( conn_handle_t * ) arg ;
if ( ! conn | | conn - > is_closing | | conn - > sockfd < 0 ) {
/* Determine active socket and remote address */
int active_sockfd = - 1 ;
struct sockaddr_in * active_remote_addr = NULL ;
uint8_t remote_defined = 0 ;
if ( conn - > route_count > 0 & & conn - > active_route_idx > = 0 & &
conn - > active_route_idx < conn - > route_count ) {
/* Use active route */
conn_route_t * route = & conn - > routes [ conn - > active_route_idx ] ;
active_sockfd = route - > sockfd ;
active_remote_addr = & route - > remote_addr ;
remote_defined = 1 ; /* Assume route has remote address defined */
} else if ( conn - > sockfd > = 0 ) {
/* Fallback to legacy fields */
active_sockfd = conn - > sockfd ;
active_remote_addr = & conn - > remote_addr ;
remote_defined = conn - > remote_defined ;
}
if ( ! conn | | conn - > is_closing | | active_sockfd < 0 ) {
free ( data ) ; /* Данные были выделены в etcp.c */
return ;
}
@ -659,11 +805,11 @@ static void etcp_tx_callback(epkt_t* epkt, uint8_t* data, uint16_t len, void* ar
}
/* Отправка через UDP сокет */
if ( conn - > remote_defined ) {
if ( remote_defined ) {
printf ( " [CONN DEBUG] etcp_tx_callback: sending packet len=%u, stats.packets_sent=%u \n " , packet_len , conn - > stats . packets_sent ) ;
ssize_t sent = sendto ( conn - > sockfd , packet_to_send , packet_len , 0 ,
( struct sockaddr * ) & conn - > remote_addr ,
sizeof ( conn - > remote_addr ) ) ;
ssize_t sent = sendto ( active_ sockfd, packet_to_send , packet_len , 0 ,
( struct sockaddr * ) active_ remote_addr,
sizeof ( * active_ remote_addr) ) ;
if ( sent = = ( ssize_t ) packet_len ) {
conn - > stats . packets_sent + + ;
@ -771,3 +917,190 @@ static void conn_etcp_reset_callback(epkt_t* epkt, void* arg)
/* Note: Данные в очередях приложения (app_input_queue, app_output_queue) не очищаются,
о н и о с т а ю т с я д л я п о в т о р н о й о т п р а в к и п о с л е з а в е р ш е н и я reset handshake */
}
int conn_init_v2 ( conn_handle_t * conn , const utun_config_t * config , int conn_idx ) {
if ( ! conn | | ! config | | conn_idx < 0 | | conn_idx > = config - > connection_v2_count ) {
return - 1 ;
}
const connection_config_v2_t * conn_v2 = & config - > connections_v2 [ conn_idx ] ;
/* Initialize route array */
conn - > route_capacity = conn_v2 - > route_count ;
conn - > route_count = 0 ;
conn - > active_route_idx = - 1 ;
conn - > routes = calloc ( conn - > route_capacity , sizeof ( conn_route_t ) ) ;
if ( ! conn - > routes ) {
return - 1 ;
}
/* For each route pair */
for ( int i = 0 ; i < conn_v2 - > route_count ; i + + ) {
const route_pair_t * route_pair = & conn_v2 - > routes [ i ] ;
conn_route_t * route = & conn - > routes [ i ] ;
/* Find server config */
const server_config_t * server = NULL ;
for ( int j = 0 ; j < config - > server_count ; j + + ) {
if ( strcmp ( config - > servers [ j ] . name , route_pair - > server_name ) = = 0 ) {
server = & config - > servers [ j ] ;
break ;
}
}
if ( ! server ) {
fprintf ( stderr , " Server '%s' not found in config \n " , route_pair - > server_name ) ;
goto error ;
}
/* Find client config */
const client_config_t * client = NULL ;
for ( int j = 0 ; j < config - > client_count ; j + + ) {
if ( strcmp ( config - > clients [ j ] . name , route_pair - > client_name ) = = 0 ) {
client = & config - > clients [ j ] ;
break ;
}
}
if ( ! client ) {
fprintf ( stderr , " Client '%s' not found in config \n " , route_pair - > client_name ) ;
goto error ;
}
/* Parse server address (ip:port) */
char server_ip [ 64 ] ;
uint16_t server_port ;
if ( sscanf ( server - > addr , " %63[^:]:%hu " , server_ip , & server_port ) ! = 2 ) {
fprintf ( stderr , " Invalid server address format: %s \n " , server - > addr ) ;
goto error ;
}
/* Parse client remote address (ip:port) */
char client_ip [ 64 ] ;
uint16_t client_port ;
if ( sscanf ( client - > to_addr , " %63[^:]:%hu " , client_ip , & client_port ) ! = 2 ) {
fprintf ( stderr , " Invalid client address format: %s \n " , client - > to_addr ) ;
goto error ;
}
/* Create socket with options */
struct sockaddr_in local_addr ;
int sockfd = create_udp_socket_with_opts ( server_ip , server_port , & local_addr ,
server - > so_mark , server - > netif ) ;
if ( sockfd < 0 ) {
fprintf ( stderr , " Failed to create socket for route %s:%s \n " ,
route_pair - > server_name , route_pair - > client_name ) ;
goto error ;
}
/* Set remote address */
struct sockaddr_in remote_addr ;
memset ( & remote_addr , 0 , sizeof ( remote_addr ) ) ;
remote_addr . sin_family = AF_INET ;
remote_addr . sin_port = htons ( client_port ) ;
if ( inet_pton ( AF_INET , client_ip , & remote_addr . sin_addr ) ! = 1 ) {
fprintf ( stderr , " Invalid client IP: %s \n " , client_ip ) ;
close ( sockfd ) ;
goto error ;
}
/* Fill route structure */
route - > sockfd = sockfd ;
memcpy ( & route - > local_addr , & local_addr , sizeof ( local_addr ) ) ;
memcpy ( & route - > remote_addr , & remote_addr , sizeof ( remote_addr ) ) ;
route - > so_mark = server - > so_mark ;
strncpy ( route - > netif , server - > netif , sizeof ( route - > netif ) - 1 ) ;
route - > netif [ sizeof ( route - > netif ) - 1 ] = ' \0 ' ;
strncpy ( route - > server_name , server - > name , sizeof ( route - > server_name ) - 1 ) ;
strncpy ( route - > client_name , client - > name , sizeof ( route - > client_name ) - 1 ) ;
route - > is_active = ( i = = 0 ) ? 1 : 0 ; /* First route active */
conn - > route_count + + ;
/* Set legacy sockfd and remote_addr for compatibility (use first route) */
if ( i = = 0 ) {
conn - > sockfd = sockfd ;
memcpy ( & conn - > remote_addr , & remote_addr , sizeof ( remote_addr ) ) ;
conn - > remote_defined = 1 ;
conn - > active_route_idx = 0 ;
}
}
if ( conn - > route_count = = 0 ) {
fprintf ( stderr , " No valid routes configured \n " ) ;
goto error ;
}
/* Set cryptographic keys */
/* Convert HEX keys to binary */
uint8_t my_pub_key [ 64 ] = { 0 } ;
uint8_t my_priv_key [ 32 ] = { 0 } ;
uint8_t peer_pub_key [ 64 ] = { 0 } ;
/* Convert keys from HEX strings if provided */
const uint8_t * my_pub_key_ptr = NULL ;
const uint8_t * my_priv_key_ptr = NULL ;
const uint8_t * peer_pub_key_ptr = NULL ;
if ( strlen ( config - > global . my_public_key_hex ) > 0 ) {
if ( hex_to_bin ( config - > global . my_public_key_hex , my_pub_key , sizeof ( my_pub_key ) ) > 0 ) {
my_pub_key_ptr = my_pub_key ;
} else {
fprintf ( stderr , " Warning: Invalid my_public_key format, using auto-generation \n " ) ;
}
}
if ( strlen ( config - > global . my_private_key_hex ) > 0 ) {
if ( hex_to_bin ( config - > global . my_private_key_hex , my_priv_key , sizeof ( my_priv_key ) ) > 0 ) {
my_priv_key_ptr = my_priv_key ;
} else {
fprintf ( stderr , " Warning: Invalid my_private_key format, using auto-generation \n " ) ;
}
}
if ( strlen ( conn_v2 - > peer_public_key_hex ) > 0 ) {
if ( hex_to_bin ( conn_v2 - > peer_public_key_hex , peer_pub_key , sizeof ( peer_pub_key ) ) > 0 ) {
peer_pub_key_ptr = peer_pub_key ;
} else {
fprintf ( stderr , " Warning: Invalid peer_public_key format, connection may fail \n " ) ;
}
}
if ( conn_set_keys ( conn , my_pub_key_ptr , my_priv_key_ptr , peer_pub_key_ptr ) ! = 0 ) {
fprintf ( stderr , " Failed to set keys \n " ) ;
goto error ;
}
/* Set keepalive interval if needed (future use) */
/* conn_v2->keepalive */
return 0 ;
error :
/* Cleanup any created sockets */
for ( int i = 0 ; i < conn - > route_count ; i + + ) {
if ( conn - > routes [ i ] . sockfd > = 0 ) {
close ( conn - > routes [ i ] . sockfd ) ;
}
}
free ( conn - > routes ) ;
conn - > routes = NULL ;
conn - > route_count = 0 ;
conn - > route_capacity = 0 ;
conn - > active_route_idx = - 1 ;
return - 1 ;
}
ll_queue_t * conn_get_output_queue ( conn_handle_t * conn )
{
if ( ! conn ) {
return NULL ;
}
return conn - > app_input_queue ;
}
ll_queue_t * conn_get_input_queue ( conn_handle_t * conn )
{
if ( ! conn ) {
return NULL ;
}
return conn - > app_output_queue ;
}