You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
965 lines
40 KiB
965 lines
40 KiB
#include "etcp_connections.h" |
|
#include "../lib/socket_compat.h" |
|
#include "../lib/platform_compat.h" |
|
#ifndef _WIN32 |
|
#include <net/if.h> |
|
#endif |
|
#include <unistd.h> |
|
#include <string.h> |
|
#include "route_lib.h" |
|
#include "route_bgp.h" |
|
#include "utun_instance.h" |
|
#include "config_parser.h" |
|
#include "crc32.h" |
|
#include "etcp.h" |
|
#include "../lib/memory_pool.h" |
|
#include "../lib/u_async.h" |
|
#include "../lib/debug_config.h" |
|
#include "etcp_loadbalancer.h" |
|
#include <stdlib.h> |
|
#include <time.h> |
|
|
|
// Forward declaration |
|
static void etcp_connections_read_callback_socket(socket_t sock, void* arg); |
|
struct ETCP_CONN* etcp_connection_create(struct UTUN_INSTANCE* instance); |
|
static void etcp_link_remove_from_connections(struct ETCP_SOCKET* conn, struct ETCP_LINK* link); |
|
|
|
// Unified packet dump function - uses configured format |
|
static void packet_dump(const char* prefix, const uint8_t* data, size_t len, struct ETCP_LINK* link) { |
|
DEBUG_TRACE(DEBUG_CATEGORY_CONNECTION, ""); |
|
if (!data || len == 0) return; |
|
|
|
// Full multi-line format (legacy) |
|
log_dump(prefix, data, len); |
|
} |
|
|
|
static void etcp_link_send_init(struct ETCP_LINK* link); |
|
static int etcp_link_send_reset(struct ETCP_LINK* link); |
|
static void etcp_link_init_timer_cbk(void* arg); |
|
|
|
#define INIT_TIMEOUT_INITIAL 500 |
|
#define INIT_TIMEOUT_MAX 50000 |
|
|
|
static void etcp_link_send_init(struct ETCP_LINK* link) { |
|
DEBUG_TRACE(DEBUG_CATEGORY_CONNECTION, ""); |
|
DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "etcp_link_send_init link=%p, is_server=%d", link, link ? link->is_server : -1); |
|
if (!link || !link->etcp || !link->etcp->instance) return; |
|
|
|
struct ETCP_DGRAM* dgram = malloc(sizeof(struct ETCP_DGRAM) + 100); |
|
if (!dgram) { |
|
DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "etcp_link_send_init: malloc failed"); |
|
return; |
|
} |
|
|
|
dgram->link = link; |
|
dgram->noencrypt_len = SC_PUBKEY_SIZE; |
|
size_t offset = 0; |
|
|
|
dgram->data[offset++] = ETCP_INIT_REQUEST; |
|
|
|
uint64_t node_id = link->etcp->instance->node_id; |
|
dgram->data[offset++] = (node_id >> 56) & 0xFF; |
|
dgram->data[offset++] = (node_id >> 48) & 0xFF; |
|
dgram->data[offset++] = (node_id >> 40) & 0xFF; |
|
dgram->data[offset++] = (node_id >> 32) & 0xFF; |
|
dgram->data[offset++] = (node_id >> 24) & 0xFF; |
|
dgram->data[offset++] = (node_id >> 16) & 0xFF; |
|
dgram->data[offset++] = (node_id >> 8) & 0xFF; |
|
dgram->data[offset++] = node_id & 0xFF; |
|
|
|
dgram->data[offset++] = (link->mtu >> 8) & 0xFF; |
|
dgram->data[offset++] = link->mtu & 0xFF; |
|
|
|
dgram->data[offset++] = (link->keepalive_interval >> 8) & 0xFF; |
|
dgram->data[offset++] = link->keepalive_interval & 0xFF; |
|
|
|
dgram->data[offset++] = link->local_link_id; |
|
|
|
memcpy(dgram->data + offset, link->etcp->instance->my_keys.public_key, SC_PUBKEY_SIZE); |
|
// Шифруем public_key inplace используя собственный ключ |
|
sc_sha_transcode(link->etcp->crypto_ctx.peer_public_key, SC_PUBKEY_SIZE, dgram->data + offset, SC_PUBKEY_SIZE); |
|
dgram->data_len = offset + SC_PUBKEY_SIZE; |
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "Sending INIT request to link, node_id=%llu, retry=%d", (unsigned long long)node_id, link->init_retry_count); |
|
|
|
// Debug: print remote address before sending |
|
if (link->remote_addr.ss_family == AF_INET) { |
|
struct sockaddr_in* sin = (struct sockaddr_in*)&link->remote_addr; |
|
char addr_str[INET_ADDRSTRLEN]; |
|
inet_ntop(AF_INET, &sin->sin_addr, addr_str, INET_ADDRSTRLEN); |
|
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[ETCP] INIT sending to %s:%d, link=%p", addr_str, ntohs(sin->sin_port), link); |
|
} |
|
|
|
etcp_encrypt_send(dgram); |
|
free(dgram); |
|
|
|
link->init_retry_count++; |
|
|
|
if (!link->init_timer && link->is_server == 0) { |
|
link->init_timeout = INIT_TIMEOUT_INITIAL; |
|
link->init_timer = uasync_set_timeout(link->etcp->instance->ua, link->init_timeout, link, etcp_link_init_timer_cbk); |
|
} else if (link->init_timer) { |
|
if ((link->init_retry_count % 10) == 0 && link->init_timeout < INIT_TIMEOUT_MAX) { |
|
link->init_timeout *= 2; |
|
if (link->init_timeout > INIT_TIMEOUT_MAX) link->init_timeout = INIT_TIMEOUT_MAX; |
|
} |
|
uasync_cancel_timeout(link->etcp->instance->ua, link->init_timer); |
|
link->init_timer = uasync_set_timeout(link->etcp->instance->ua, link->init_timeout, link, etcp_link_init_timer_cbk); |
|
} |
|
} |
|
|
|
static void etcp_link_init_timer_cbk(void* arg) { |
|
DEBUG_TRACE(DEBUG_CATEGORY_CONNECTION, ""); |
|
struct ETCP_LINK* link = (struct ETCP_LINK*)arg; |
|
if (!link || link->initialized || link->is_server != 0) return; |
|
|
|
link->init_timer = NULL; |
|
etcp_link_send_init(link); |
|
} |
|
|
|
static int etcp_link_send_reset(struct ETCP_LINK* link) { |
|
DEBUG_TRACE(DEBUG_CATEGORY_CONNECTION, ""); |
|
if (!link) return -1; |
|
|
|
struct ETCP_DGRAM* dgram = malloc(sizeof(struct ETCP_DGRAM) + 1); |
|
if (!dgram) { |
|
DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "etcp_link_send_reset: malloc failed"); |
|
return -1; |
|
} |
|
|
|
dgram->link = link; |
|
dgram->data_len = 1; |
|
dgram->noencrypt_len = 0; |
|
dgram->data[0] = 0x06; |
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "Sending RESET to link"); |
|
int ret = etcp_encrypt_send(dgram); |
|
free(dgram); |
|
return ret; |
|
} |
|
|
|
static uint32_t sockaddr_hash(struct sockaddr_storage* addr) { |
|
DEBUG_TRACE(DEBUG_CATEGORY_CONNECTION, ""); |
|
socklen_t addr_len = (addr->ss_family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6); |
|
return crc32_calc((void*)addr, addr_len); |
|
} |
|
|
|
// Бинарный поиск линка по ip_port_hash |
|
static int find_link_index(struct ETCP_SOCKET* e_sock, uint32_t hash) { |
|
DEBUG_TRACE(DEBUG_CATEGORY_CONNECTION, ""); |
|
if (!e_sock || e_sock->num_channels == 0) return -1; |
|
|
|
int left = 0; |
|
int right = e_sock->num_channels - 1; |
|
|
|
while (left <= right) { |
|
int mid = left + (right - left) / 2; |
|
if (e_sock->links[mid]->ip_port_hash == hash) { |
|
return mid; |
|
} else if (e_sock->links[mid]->ip_port_hash < hash) { |
|
left = mid + 1; |
|
} else { |
|
right = mid - 1; |
|
} |
|
} |
|
|
|
return -(left + 1); |
|
} |
|
|
|
// Реалокация массива линков с увеличением в 2 раза |
|
static int realloc_links(struct ETCP_SOCKET* e_sock) { |
|
DEBUG_TRACE(DEBUG_CATEGORY_CONNECTION, ""); |
|
size_t new_max = e_sock->max_channels == 0 ? 8 : e_sock->max_channels * 2; |
|
struct ETCP_LINK** new_links = realloc(e_sock->links, new_max * sizeof(struct ETCP_LINK*)); |
|
if (!new_links) { |
|
DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "realloc_links: realloc failed"); |
|
return -1; |
|
} |
|
|
|
e_sock->links = new_links; |
|
e_sock->max_channels = new_max; |
|
return 0; |
|
} |
|
|
|
// Вставка линка в отсортированный массив |
|
static int insert_link(struct ETCP_SOCKET* e_sock, struct ETCP_LINK* link) { |
|
DEBUG_TRACE(DEBUG_CATEGORY_CONNECTION, ""); |
|
if (!e_sock || !link) return -1; |
|
|
|
if (e_sock->num_channels >= e_sock->max_channels) { |
|
if (realloc_links(e_sock) < 0) return -1; |
|
} |
|
|
|
int idx = find_link_index(e_sock, link->ip_port_hash); |
|
if (idx >= 0) return -1; |
|
|
|
idx = -(idx + 1); |
|
|
|
if (idx < (int)e_sock->num_channels) { |
|
memmove(&e_sock->links[idx + 1], &e_sock->links[idx], |
|
(e_sock->num_channels - idx) * sizeof(struct ETCP_LINK*)); |
|
} |
|
|
|
e_sock->links[idx] = link; |
|
e_sock->num_channels++; |
|
return 0; |
|
} |
|
|
|
// Удаление линка из массива |
|
static void remove_link(struct ETCP_SOCKET* e_sock, uint32_t hash) { |
|
DEBUG_TRACE(DEBUG_CATEGORY_CONNECTION, ""); |
|
if (!e_sock || e_sock->num_channels == 0) return; |
|
|
|
int idx = find_link_index(e_sock, hash); |
|
if (idx < 0) return; |
|
|
|
if (idx < (int)e_sock->num_channels - 1) { |
|
memmove(&e_sock->links[idx], &e_sock->links[idx + 1], |
|
(e_sock->num_channels - idx - 1) * sizeof(struct ETCP_LINK*)); |
|
} |
|
|
|
e_sock->num_channels--; |
|
} |
|
|
|
// надо править, используй sockaddr_hash |
|
struct ETCP_LINK* etcp_link_find_by_addr(struct ETCP_SOCKET* e_sock, struct sockaddr_storage* addr) { |
|
DEBUG_TRACE(DEBUG_CATEGORY_CONNECTION, ""); |
|
if (!e_sock || !addr) return NULL; |
|
|
|
int idx = find_link_index(e_sock, sockaddr_hash(addr)); |
|
if (idx < 0) return NULL; |
|
|
|
return e_sock->links[idx]; |
|
} |
|
|
|
int etcp_find_free_local_link_id(struct ETCP_CONN* etcp) { |
|
DEBUG_TRACE(DEBUG_CATEGORY_CONNECTION, ""); |
|
if (!etcp) return -1; |
|
|
|
// Битовый массив для 256 id (32 байта * 8 бит = 256) |
|
uint8_t used_ids[32] = {0}; |
|
|
|
// Помечаем занятые id |
|
struct ETCP_LINK* link = etcp->links; |
|
while (link) { |
|
if (link->local_link_id < 256) { |
|
used_ids[link->local_link_id >> 3] |= (1 << (link->local_link_id & 7)); |
|
} |
|
link = link->next; |
|
} |
|
|
|
// Ищем первый свободный id |
|
for (int i = 0; i < 32; i++) { |
|
if (used_ids[i] != 0xFF) { |
|
// Есть свободные биты в этом байте |
|
for (int bit = 0; bit < 8; bit++) { |
|
if (!(used_ids[i] & (1 << bit))) { |
|
return (i << 3) + bit; |
|
} |
|
} |
|
} |
|
} |
|
|
|
// Все id заняты |
|
return -1; |
|
} |
|
|
|
|
|
// =============================== |
|
|
|
struct ETCP_SOCKET* etcp_socket_add(struct UTUN_INSTANCE* instance, struct sockaddr_storage* ip, uint32_t netif_index, int so_mark, uint8_t type) { |
|
DEBUG_TRACE(DEBUG_CATEGORY_CONNECTION, ""); |
|
if (!instance) return NULL; |
|
|
|
struct ETCP_SOCKET* e_sock = calloc(1, sizeof(struct ETCP_SOCKET)); |
|
if (!e_sock) { |
|
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "Failed to allocate connection"); |
|
return NULL; |
|
} |
|
e_sock->fd = SOCKET_INVALID; // Initialize to invalid socket |
|
|
|
int family = AF_INET; |
|
if (ip) { |
|
family = ip->ss_family; |
|
if (family != AF_INET && family != AF_INET6) { |
|
DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "Unsupported address family: %d", family); |
|
free(e_sock); |
|
return NULL; |
|
} |
|
} |
|
|
|
e_sock->fd = socket_create_udp(family); |
|
if (e_sock->fd == SOCKET_INVALID) { |
|
DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "Failed to create socket: %s", |
|
socket_strerror(socket_get_error())); |
|
free(e_sock); |
|
return NULL; |
|
} |
|
|
|
// Строго не используем reuseaddr, даже в тестах! |
|
socket_set_reuseaddr(e_sock->fd, 0); |
|
|
|
// Increase socket buffers for high throughput |
|
socket_set_buffers(e_sock->fd, 4 * 1024 * 1024, 4 * 1024 * 1024); |
|
|
|
if (socket_set_nonblocking(e_sock->fd) != 0) { |
|
DEBUG_WARN(DEBUG_CATEGORY_CONNECTION, "Failed to set non-blocking mode"); |
|
} |
|
|
|
// Set socket mark if specified (Linux only) |
|
if (so_mark > 0) { |
|
socket_set_mark(e_sock->fd, so_mark); |
|
} |
|
|
|
// Bind to interface if specified (Linux only) |
|
#ifndef _WIN32 |
|
if (netif_index > 0) { |
|
char ifname[IF_NAMESIZE]; |
|
if (if_indextoname(netif_index, ifname)) { |
|
socket_bind_to_device(e_sock->fd, ifname); |
|
} |
|
} |
|
#endif |
|
|
|
// Store the local address and bind socket if provided |
|
if (ip) { |
|
memcpy(&e_sock->local_addr, ip, sizeof(struct sockaddr_storage)); |
|
|
|
// CRITICAL: Actually bind the socket to the address |
|
socklen_t addr_len = (ip->ss_family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6); |
|
if (bind(e_sock->fd, (struct sockaddr*)ip, addr_len) < 0) { |
|
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "[ETCP] Failed to bind socket to address family %d: %s", |
|
ip->ss_family, socket_strerror(socket_get_error())); |
|
if (ip->ss_family == AF_INET) { |
|
struct sockaddr_in* sin = (struct sockaddr_in*)ip; |
|
char addr_str[INET_ADDRSTRLEN]; |
|
inet_ntop(AF_INET, &sin->sin_addr, addr_str, INET_ADDRSTRLEN); |
|
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "[ETCP] Failed to bind to %s:%d", addr_str, ntohs(sin->sin_port)); |
|
} |
|
socket_close_wrapper(e_sock->fd); |
|
free(e_sock); |
|
return NULL; |
|
} |
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[ETCP] Successfully bound socket to local address, family=%d", ip->ss_family); |
|
} |
|
|
|
e_sock->instance = instance; |
|
e_sock->errorcode = 0; |
|
e_sock->pkt_format_errors = 0; |
|
|
|
// Add to instance's socket list |
|
e_sock->next = instance->etcp_sockets; |
|
instance->etcp_sockets = e_sock; |
|
|
|
// Register socket with uasync for receiving packets |
|
e_sock->socket_id = uasync_add_socket_t(instance->ua, e_sock->fd, |
|
etcp_connections_read_callback_socket, |
|
NULL, NULL, e_sock); |
|
if (!e_sock->socket_id) { |
|
DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "Failed to register socket with uasync"); |
|
socket_close_wrapper(e_sock->fd); |
|
free(e_sock); |
|
return NULL; |
|
} |
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "Registered ETCP socket with uasync"); |
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[ETCP] Socket %p registered and active", e_sock); |
|
|
|
return e_sock; |
|
} |
|
|
|
void etcp_socket_remove(struct ETCP_SOCKET* conn) { |
|
DEBUG_TRACE(DEBUG_CATEGORY_CONNECTION, ""); |
|
if (!conn) return; |
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[ETCP] Removing socket %p, socket_id=%p", conn, conn->socket_id); |
|
|
|
// Remove from uasync if registered |
|
if (conn->socket_id) { |
|
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[ETCP] Removing socket from uasync, instance=%p, ua=%p", conn->instance, conn->instance->ua); |
|
uasync_remove_socket_t(conn->instance->ua, conn->fd); |
|
conn->socket_id = NULL; |
|
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[ETCP] Unregistered socket from uasync"); |
|
} |
|
|
|
if (conn->fd != SOCKET_INVALID) { |
|
socket_close_wrapper(conn->fd); |
|
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[ETCP] Closed socket"); |
|
} |
|
|
|
for (size_t i = 0; i < conn->num_channels; i++) { |
|
etcp_link_close(conn->links[i]); |
|
} |
|
free(conn->links); |
|
|
|
free(conn); |
|
} |
|
|
|
|
|
struct ETCP_LINK* etcp_link_new(struct ETCP_CONN* etcp, struct ETCP_SOCKET* conn, struct sockaddr_storage* remote_addr, uint8_t is_server) { |
|
DEBUG_TRACE(DEBUG_CATEGORY_CONNECTION, ""); |
|
if (!remote_addr) return NULL; |
|
|
|
struct ETCP_LINK* link = calloc(1, sizeof(struct ETCP_LINK)); |
|
if (!link) { |
|
DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "etcp_link_new: calloc failed"); |
|
return NULL; |
|
} |
|
|
|
link->conn = conn; |
|
link->etcp = etcp; |
|
link->is_server = is_server; |
|
link->mtu = 1500; |
|
link->keepalive_interval = 30; |
|
link->initialized = 0; |
|
link->init_timer = NULL; |
|
link->init_timeout = 0; |
|
link->init_retry_count = 0; |
|
|
|
// Выделяем свободный local_link_id |
|
int free_id = etcp_find_free_local_link_id(etcp); |
|
if (free_id < 0) { |
|
DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "etcp_link_new: no free local_link_id available"); |
|
free(link); |
|
return NULL; |
|
} |
|
link->local_link_id = (uint8_t)free_id; |
|
|
|
memcpy(&link->remote_addr, remote_addr, sizeof(struct sockaddr_storage)); |
|
// link->last_activity = time(NULL); |
|
|
|
link->ip_port_hash = sockaddr_hash(remote_addr); |
|
|
|
insert_link(conn, link); |
|
|
|
struct ETCP_LINK* l=etcp->links; |
|
while (l && l->next) l=l->next; |
|
if (l) l->next = link; else etcp->links = link; |
|
|
|
if (is_server == 0) { |
|
DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "etcp_link_new: client link, calling etcp_link_send_init"); |
|
etcp_link_send_init(link); |
|
} |
|
|
|
return link; |
|
} |
|
|
|
void etcp_link_close(struct ETCP_LINK* link) { |
|
DEBUG_TRACE(DEBUG_CATEGORY_CONNECTION, ""); |
|
if (!link || !link->etcp) return; |
|
|
|
// Cancel init timer if active |
|
if (link->init_timer) { |
|
uasync_cancel_timeout(link->etcp->instance->ua, link->init_timer); |
|
link->init_timer = NULL; |
|
} |
|
|
|
// универсальное удаление из односвязного списка |
|
struct ETCP_LINK **pp = &link->etcp->links; |
|
while (*pp) { |
|
if (*pp == link) { |
|
*pp = link->next; |
|
break; |
|
} |
|
pp = &(*pp)->next; |
|
} |
|
|
|
remove_link(link->conn, link->ip_port_hash); |
|
|
|
free(link); |
|
} |
|
|
|
int etcp_encrypt_send(struct ETCP_DGRAM* dgram) { |
|
DEBUG_TRACE(DEBUG_CATEGORY_CONNECTION, ""); |
|
// DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "etcp_encrypt_send called, link=%p", dgram ? dgram->link : NULL); |
|
// printf("[ETCP DEBUG] etcp_encrypt_send: ENTERING FUNCTION\n"); |
|
|
|
int errcode=0; |
|
sc_context_t* sc = &dgram->link->etcp->crypto_ctx; |
|
int len=dgram->data_len-dgram->noencrypt_len;// не забываем добавить timestamp (2 bytes) |
|
if (len<=0 || len>1480) { dgram->link->send_errors++; errcode=1; goto es_err; } |
|
uint8_t enc_buf[1600]; |
|
size_t enc_buf_len=0; |
|
dgram->timestamp=get_current_timestamp(); |
|
|
|
// DUMP: Show packet before encryption |
|
if (debug_should_output(DEBUG_LEVEL_DEBUG, DEBUG_CATEGORY_CRYPTO)) log_dump("ECTP_ENCRYPT_SEND", dgram->data, dgram->data_len); |
|
// DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Encrypt start"); |
|
sc_encrypt(sc, (uint8_t*)&dgram->timestamp/*не править это, тут верно!*/, sizeof(uint16_t) + len, enc_buf, &enc_buf_len); |
|
// DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Encrypt end"); |
|
if (enc_buf_len == 0) { |
|
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "etcp_encrypt_send: encryption failed for node %llu", (unsigned long long)dgram->link->etcp->instance->node_id); |
|
dgram->link->send_errors++; |
|
errcode=2; |
|
goto es_err; |
|
} |
|
if (enc_buf_len + dgram->noencrypt_len > 1480) { dgram->link->send_errors++; errcode=2; goto es_err; } |
|
memcpy(enc_buf+enc_buf_len, dgram->data+len, dgram->noencrypt_len); |
|
|
|
// DUMP: Show complete packet before sending |
|
if (debug_should_output(DEBUG_LEVEL_DEBUG, DEBUG_CATEGORY_CRYPTO)) log_dump("ENCRYPTED, READY TO SEND", enc_buf, enc_buf_len + dgram->noencrypt_len); |
|
|
|
struct sockaddr_storage* addr=&dgram->link->remote_addr; |
|
socklen_t addr_len = (addr->ss_family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6); |
|
|
|
// Debug: print where we're sending the packet |
|
if (addr->ss_family == AF_INET) { |
|
struct sockaddr_in* sin = (struct sockaddr_in*)addr; |
|
char addr_str[INET_ADDRSTRLEN]; |
|
inet_ntop(AF_INET, &sin->sin_addr, addr_str, INET_ADDRSTRLEN); |
|
// DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[ETCP] Sending packet to %s:%d, size=%zd", addr_str, ntohs(sin->sin_port), enc_buf_len + dgram->noencrypt_len); |
|
} |
|
|
|
ssize_t sent = socket_sendto(dgram->link->conn->fd, enc_buf, enc_buf_len + dgram->noencrypt_len, |
|
(struct sockaddr*)addr, addr_len); |
|
if (sent < 0) { |
|
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "sendto failed, sock_err=%d", socket_get_error()); |
|
dgram->link->send_errors++; errcode=3; goto es_err; |
|
} else { |
|
// DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "sendto succeeded, sent=%zd bytes to port %d", sent, ntohs(((struct sockaddr_in*)addr)->sin_port)); |
|
dgram->link->total_encrypted += sent; |
|
} |
|
return (int)sent; |
|
es_err: |
|
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "[ETCP] encrypt_send error %d", errcode); |
|
return -1; |
|
} |
|
|
|
static void etcp_connections_read_callback_socket(socket_t sock, void* arg) { |
|
DEBUG_TRACE(DEBUG_CATEGORY_CONNECTION, ""); |
|
// DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "etcp_connections_read_callback fd=%d, socket=%p", fd, arg); |
|
// !!!!!! DANGER: в этой функции ПРЕДЕЛЬНАЯ АККУРАТНОСТЬ. Если кажется что не туда указатель то невнимательно аланизировал !!!!! |
|
// НЕ РУИНИТЬ (uint8_t*)&pkt->timestamp - это правильно !!!! |
|
// |
|
// Ошибки функции (errorcode): |
|
// 1 - пакет слишком маленький для init (< SC_PUBKEY_SIZE) |
|
// 2 - не удалось установить peer public key при init |
|
// 3 - не удалось расшифровать init пакет |
|
// 4 - не init пакет (неверный код) |
|
// 5 - коллизия peer ID и ключей |
|
// 6 - не удалось расшифровать обычный пакет |
|
// 13 - переполнение при парсинге пакета |
|
// 46 - расшифрованный пакет слишком маленький (< 3 байта) |
|
// 55 - не удалось создать подключение |
|
// 66 - не удалось создать линк |
|
struct ETCP_SOCKET* e_sock = (struct ETCP_SOCKET*)arg; |
|
if (!e_sock) return; |
|
|
|
// printf("[ETCP] Read callback triggered for fd=%d, socket=%p\n", fd, e_sock); |
|
|
|
struct sockaddr_storage addr; |
|
uint8_t data[PACKET_DATA_SIZE]; |
|
socklen_t addr_len=sizeof(addr); |
|
memset(&addr, 0, sizeof(addr)); |
|
ssize_t recv_len = socket_recvfrom(sock, data, PACKET_DATA_SIZE, (struct sockaddr*)&addr, &addr_len); |
|
|
|
if (recv_len <= 0) { |
|
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "etcp_connections_read_callback: recvfrom failed, error=%zd, sock_err=%d", recv_len, socket_get_error()); |
|
return; |
|
} |
|
|
|
// printf("[ETCP] Received packet: %zd bytes from address\n", recv_len); |
|
|
|
// DUMP: Show received packet content |
|
if (debug_should_output(DEBUG_LEVEL_DEBUG, DEBUG_CATEGORY_CRYPTO)) log_dump("RECV in:", data, recv_len); // link unknown at this point |
|
|
|
struct ETCP_DGRAM* pkt = memory_pool_alloc(e_sock->instance->pkt_pool); |
|
if (!pkt) return; |
|
size_t pkt_len=0; |
|
int errorcode=0; |
|
|
|
struct ETCP_LINK* link=etcp_link_find_by_addr(e_sock, &addr); |
|
// printf("[ETCP DEBUG] Received packet, link=%p, recv_len=%zd\n", link, recv_len); |
|
|
|
// Try normal decryption first if we have an established link with session keys |
|
// This is the common case for data packets and responses |
|
if (link!=NULL && link->etcp!=NULL && link->etcp->crypto_ctx.session_ready) { |
|
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Decrypt start (normal)"); |
|
if (!sc_decrypt(&link->etcp->crypto_ctx, data, recv_len, (uint8_t*)&pkt->timestamp, &pkt_len)) { |
|
// Normal decryption succeeded - process packet normally |
|
goto process_decrypted; |
|
} |
|
// Normal decryption failed - might be INIT packet, fall through to INIT handling |
|
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Normal decryption failed, trying INIT decryption"); |
|
} |
|
|
|
// Try INIT decryption (for incoming connection requests) |
|
// This handles: no link found, or link without session, or normal decrypt failed |
|
if (recv_len<=SC_PUBKEY_SIZE) { |
|
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "etcp_connections_read_callback: packet too small for init, size=%zd", recv_len); |
|
errorcode=1; |
|
goto ec_fr; |
|
} |
|
|
|
struct secure_channel sc; |
|
sc_init_ctx(&sc, &e_sock->instance->my_keys); |
|
|
|
// Расшифровываем public_key inplace используя собственный ключ |
|
sc_sha_transcode(e_sock->instance->my_keys.public_key, SC_PUBKEY_SIZE, data + recv_len - SC_PUBKEY_SIZE, SC_PUBKEY_SIZE); |
|
|
|
if (sc_set_peer_public_key(&sc, &data[recv_len-SC_PUBKEY_SIZE], SC_PEER_PUBKEY_BIN)!=SC_OK) { |
|
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "etcp_connections_read_callback: failed to set peer public key during init"); |
|
errorcode=2; |
|
goto ec_fr; |
|
} |
|
if (sc_decrypt(&sc, data, recv_len-SC_PUBKEY_SIZE, (uint8_t*)&pkt->timestamp, &pkt_len)) { |
|
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "etcp_connections_read_callback: failed to decrypt init packet"); |
|
errorcode=3; |
|
goto ec_fr; |
|
} |
|
|
|
// INIT decryption succeeded - process as new incoming connection |
|
pkt->data_len=pkt_len-2; |
|
pkt->noencrypt_len=0; |
|
struct { |
|
uint8_t code; |
|
uint8_t id[8]; |
|
uint8_t mtu[2]; |
|
uint8_t keepalive[2]; |
|
uint8_t link_id; |
|
uint8_t pubkey[SC_PUBKEY_SIZE]; |
|
} *ack_hdr=(void*)&pkt->data[0]; |
|
uint64_t peer_id; |
|
memcpy(&peer_id, &ack_hdr->id[0], 8); |
|
if (ack_hdr->code!=ETCP_INIT_REQUEST && ack_hdr->code!=ETCP_CHANNEL_INIT) { |
|
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "etcp_connections_read_callback: not an init packet, code=%02x", ack_hdr->code); |
|
errorcode=4; |
|
goto ec_fr; |
|
}// не init |
|
|
|
struct ETCP_CONN* conn=e_sock->instance->connections; |
|
while (conn) {// ищем есть ли подключение к этому пиру |
|
if (conn->peer_node_id==peer_id) break; |
|
conn=conn->next; |
|
} |
|
|
|
int new_conn=0; |
|
if (!conn || conn->peer_node_id!=peer_id) {// создаём новое |
|
new_conn=1; |
|
conn=etcp_connection_create(e_sock->instance); |
|
if (!conn) { errorcode=55; DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "etcp_connections_read_callback: failed to create connection"); goto ec_fr; }// облом |
|
memcpy(&conn->crypto_ctx, &sc, sizeof(sc));// добавляем ключ |
|
conn->peer_node_id=peer_id; |
|
etcp_update_log_name(conn); // Update log_name with peer_node_id |
|
char buf[128]; |
|
addr_to_string(&addr, buf, sizeof(buf)); |
|
DEBUG_DEBUG(DEBUG_CATEGORY_CONNECTION, "New connection from %s peer_id=%ld etcp=%p", buf, peer_id, conn); |
|
// Add connection to instance list |
|
conn->next = e_sock->instance->connections; |
|
e_sock->instance->connections = conn; |
|
e_sock->instance->connections_count++; |
|
DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "Added incoming connection %p to instance, total count: %d", conn, e_sock->instance->connections_count); |
|
} |
|
else {// check keys если существующее подключение |
|
if (memcmp(conn->crypto_ctx.peer_public_key, sc.peer_public_key, SC_PUBKEY_SIZE)) { errorcode=5; DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "etcp_connections_read_callback: peer key mismatch for node %llu", (unsigned long long)peer_id); goto ec_fr; }// коллизия - peer id совпал а ключи разные. |
|
} |
|
link = etcp_link_new(conn, e_sock, &addr, 1); |
|
if (!link) { if (new_conn) etcp_connection_close(conn); errorcode=66; DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "etcp_connections_read_callback: failed to create link for connection"); goto ec_fr; }// облом |
|
link->remote_link_id = ack_hdr->link_id; |
|
if (ack_hdr->code==0x02) etcp_conn_reset(conn); |
|
|
|
struct { |
|
uint8_t code; |
|
uint8_t id[8]; |
|
uint8_t mtu[2]; |
|
uint8_t link_id; |
|
uint8_t peer_ipv4[4]; |
|
uint8_t peer_port[2]; |
|
} *ack_repl_hdr=(void*)&pkt->data[0]; |
|
ack_repl_hdr->code+=1; |
|
memcpy(ack_repl_hdr->id, &e_sock->instance->node_id, 8); |
|
int mtu=e_sock->instance->config->global.mtu; |
|
ack_repl_hdr->mtu[0]=mtu>>8; |
|
ack_repl_hdr->mtu[1]=mtu; |
|
ack_repl_hdr->link_id = link->local_link_id; |
|
// Add client's IP:port (so client behind NAT can know its external address) |
|
if (addr.ss_family == AF_INET) { |
|
struct sockaddr_in *sin = (struct sockaddr_in*)&addr; |
|
memcpy(ack_repl_hdr->peer_ipv4, &sin->sin_addr.s_addr, 4); |
|
uint16_t port = ntohs(sin->sin_port); |
|
ack_repl_hdr->peer_port[0] = port >> 8; |
|
ack_repl_hdr->peer_port[1] = port & 0xFF; |
|
} else { |
|
// For IPv6, set to 0 (not supported for NAT traversal) |
|
memset(ack_repl_hdr->peer_ipv4, 0, 4); |
|
memset(ack_repl_hdr->peer_port, 0, 2); |
|
} |
|
pkt->data_len=sizeof(*ack_repl_hdr); |
|
pkt->noencrypt_len=0; |
|
pkt->link=link; |
|
DEBUG_DEBUG(DEBUG_CATEGORY_CONNECTION, "Sending INIT RESPONSE, link=%p, local_link_id=%d, remote_link_id=%d", link, link->local_link_id, link->remote_link_id); |
|
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[ETCP DEBUG] Send INIT RESPONSE"); |
|
etcp_encrypt_send(pkt); |
|
// printf("[ETCP DEBUG] Send INIT RESPONSE ok\n"); |
|
|
|
memory_pool_free(e_sock->instance->pkt_pool, pkt); |
|
link->initialized = 1;// получен init request (server), считаем линк уже готовым к работе |
|
loadbalancer_link_ready(link); |
|
|
|
// Notify BGP about new connection to send routing table |
|
if (link->etcp && link->etcp->instance && link->etcp->instance->bgp) { |
|
route_bgp_new_conn(link->etcp); |
|
} |
|
|
|
return; |
|
|
|
process_decrypted: |
|
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Decrypt end"); |
|
if (pkt_len<3) { errorcode=46; DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "etcp_connections_read_callback: decrypted packet too small, size=%zu", pkt_len); goto ec_fr; } |
|
pkt->data_len=pkt_len-2; |
|
pkt->noencrypt_len=0; |
|
pkt->link=link; |
|
|
|
link->last_recv_local_time=get_current_time_units(); |
|
link->last_recv_timestamp=pkt->timestamp; |
|
link->last_recv_updated=1; |
|
|
|
size_t offset = 0; |
|
uint8_t code = pkt->data[offset++]; |
|
|
|
if (code == ETCP_INIT_RESPONSE || code == ETCP_CHANNEL_RESPONSE) { |
|
// Parse response |
|
if (code == ETCP_INIT_RESPONSE) etcp_conn_reset(link->etcp); |
|
uint64_t server_node_id = 0; |
|
for (int i = 0; i < 8; i++) { |
|
server_node_id = (server_node_id << 8) | pkt->data[offset++]; |
|
} |
|
link->mtu = (pkt->data[offset++] << 8) | pkt->data[offset++]; |
|
link->remote_link_id = pkt->data[offset++]; |
|
|
|
// Parse NAT IP:port from response (new format includes 4+2 bytes) |
|
if (pkt_len >= 18) { |
|
uint32_t new_nat_ip = (pkt->data[offset] << 24) | (pkt->data[offset+1] << 16) | |
|
(pkt->data[offset+2] << 8) | pkt->data[offset+3]; |
|
offset += 4; |
|
uint16_t new_nat_port = (pkt->data[offset] << 8) | pkt->data[offset+1]; |
|
offset += 2; |
|
|
|
// Check if NAT address changed |
|
if (link->nat_ip == 0 && link->nat_port == 0) { |
|
// First time receiving NAT info |
|
link->nat_ip = new_nat_ip; |
|
link->nat_port = new_nat_port; |
|
char ip_str[INET_ADDRSTRLEN]; |
|
struct in_addr addr; |
|
addr.s_addr = htonl(new_nat_ip); |
|
inet_ntop(AF_INET, &addr, ip_str, sizeof(ip_str)); |
|
DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "[%s] NAT address initialized: %s:%u", |
|
link->etcp->log_name, ip_str, new_nat_port); |
|
} else if (link->nat_ip != new_nat_ip || link->nat_port != new_nat_port) { |
|
// NAT address changed |
|
char old_ip_str[INET_ADDRSTRLEN], new_ip_str[INET_ADDRSTRLEN]; |
|
struct in_addr old_addr, new_addr; |
|
old_addr.s_addr = htonl(link->nat_ip); |
|
new_addr.s_addr = htonl(new_nat_ip); |
|
inet_ntop(AF_INET, &old_addr, old_ip_str, sizeof(old_ip_str)); |
|
inet_ntop(AF_INET, &new_addr, new_ip_str, sizeof(new_ip_str)); |
|
|
|
link->nat_ip = new_nat_ip; |
|
link->nat_port = new_nat_port; |
|
link->nat_changes_count++; |
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "[%s] NAT address changed: %s:%u -> %s:%u (change #%u)", |
|
link->etcp->log_name, old_ip_str, link->nat_port, new_ip_str, new_nat_port, |
|
link->nat_changes_count); |
|
} else { |
|
// NAT address unchanged |
|
link->nat_hits_count++; |
|
DEBUG_DEBUG(DEBUG_CATEGORY_CONNECTION, "[%s] NAT address unchanged (%u matches)", |
|
link->etcp->log_name, link->nat_hits_count); |
|
} |
|
} else { |
|
// Legacy format without NAT info |
|
DEBUG_DEBUG(DEBUG_CATEGORY_CONNECTION, "[%s] Received legacy INIT_RESPONSE without NAT info", |
|
link->etcp->log_name); |
|
} |
|
|
|
if (offset > pkt_len) { errorcode=13; DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "etcp_connections_read_callback: packet parsing overflow, offset=%zu, pkt_len=%zu", offset, pkt_len); goto ec_fr; } |
|
|
|
// DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "Received INIT_RESPONSE from server_node_id=%llu, mtu=%d", (unsigned long long)server_node_id, link->mtu); |
|
|
|
link->etcp->peer_node_id = server_node_id; // If not set |
|
etcp_update_log_name(link->etcp); // Update log_name with peer_node_id |
|
|
|
// Mark link as initialized |
|
// DEBUG_DEBUG(DEBUG_CATEGORY_CONNECTION, "Setting link->initialized=1, link=%p, is_server=%d", link, link->is_server); |
|
link->initialized = 1;// получен init response (client) |
|
loadbalancer_link_ready(link); |
|
|
|
// Notify BGP about new connection to send routing table |
|
if (link->etcp && link->etcp->instance && link->etcp->instance->bgp) { |
|
route_bgp_new_conn(link->etcp); |
|
} |
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "etcp client: Link initialized successfully! Server node_id=%llu, mtu=%d, local_link_id=%d, remote_link_id=%d", (unsigned long long)server_node_id, link->mtu, link->local_link_id, link->remote_link_id); |
|
|
|
// Cancel init timer if exists |
|
if (link->init_timer) { |
|
uasync_cancel_timeout(link->etcp->instance->ua, link->init_timer); |
|
link->init_timer = NULL; |
|
} |
|
|
|
memory_pool_free(e_sock->instance->pkt_pool, pkt); |
|
return; // INIT_RESPONSE is handled, no further processing needed |
|
} |
|
|
|
// packet_dump("RECV decrypted:", pkt->data, pkt->data_len, link); |
|
|
|
etcp_conn_input(pkt); |
|
return; |
|
|
|
ec_fr: |
|
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "etcp_connections_read_callback: error %d", errorcode); |
|
e_sock->pkt_format_errors++; |
|
e_sock->errorcode=errorcode; |
|
memory_pool_free(e_sock->instance->pkt_pool, pkt); |
|
return; |
|
} |
|
|
|
int init_connections(struct UTUN_INSTANCE* instance) { |
|
DEBUG_TRACE(DEBUG_CATEGORY_CONNECTION, ""); |
|
if (!instance || !instance->config) return -1; |
|
|
|
struct utun_config* config = instance->config; |
|
|
|
// Initialize servers first - create sockets for incoming connections |
|
struct CFG_SERVER* server = config->servers; |
|
while (server) { |
|
// Create socket for this server |
|
struct ETCP_SOCKET* e_sock = etcp_socket_add(instance, &server->ip, server->netif_index, server->so_mark, server->type); |
|
if (!e_sock) { |
|
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Failed to create socket for server %s", server->name); |
|
server = server->next; |
|
continue; |
|
} |
|
|
|
// Convert IP to string for logging |
|
char addr_str[INET6_ADDRSTRLEN + 6]; |
|
if (server->ip.ss_family == AF_INET) { |
|
struct sockaddr_in* sin = (struct sockaddr_in*)&server->ip; |
|
inet_ntop(AF_INET, &sin->sin_addr, addr_str, INET_ADDRSTRLEN); |
|
sprintf(addr_str + strlen(addr_str), ":%d", ntohs(sin->sin_port)); |
|
} else { |
|
struct sockaddr_in6* sin6 = (struct sockaddr_in6*)&server->ip; |
|
inet_ntop(AF_INET6, &sin6->sin6_addr, addr_str, INET6_ADDRSTRLEN); |
|
sprintf(addr_str + strlen(addr_str), ":%d", ntohs(sin6->sin6_port)); |
|
} |
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "Initialized server %s on %s (links: %zu)", |
|
server->name, addr_str, e_sock->num_channels); |
|
server = server->next; |
|
} |
|
|
|
// Initialize clients - create outgoing connections |
|
struct CFG_CLIENT* client = config->clients; |
|
DEBUG_DEBUG(DEBUG_CATEGORY_CONNECTION, "init_connections called, instance=%p, config=%p, clients=%p, connections_count=%d", |
|
instance, config, config ? config->clients : NULL, instance ? instance->connections_count : -1); |
|
while (client) { |
|
// Check if client has required configuration |
|
DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "Client %s - keepalive=%d, links=%p, peer_key_len=%zu", |
|
client->name, client->keepalive, client->links, |
|
strlen(client->peer_public_key_hex)); |
|
|
|
// Create ETCP connection for this client |
|
struct ETCP_CONN* etcp_conn = etcp_connection_create(instance); |
|
|
|
if (!etcp_conn) { |
|
DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "Failed to create ETCP connection for client %s", client->name); |
|
client = client->next; |
|
continue; |
|
} |
|
|
|
// Initialize crypto context for this connection |
|
if (sc_init_ctx(&etcp_conn->crypto_ctx, &instance->my_keys) != SC_OK) { |
|
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "init_connections: failed to initialize crypto context for client %s", client->name); |
|
etcp_connection_close(etcp_conn); |
|
client = client->next; |
|
continue; |
|
} |
|
// If client has peer public key configured, set it |
|
if (strlen(client->peer_public_key_hex) > 0) { |
|
// For now, set peer node ID to indicate we have peer key |
|
// The actual peer key will be exchanged during connection establishment |
|
etcp_conn->peer_node_id = 1; // Simple indicator |
|
etcp_update_log_name(etcp_conn); // Update log_name with peer_node_id |
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_CRYPTO, "init_connections: setting peer public key for client %s", client->name); |
|
// Set peer public key (assuming hex format) |
|
if (sc_set_peer_public_key(&etcp_conn->crypto_ctx, client->peer_public_key_hex, 1) != SC_OK) { |
|
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "init_connections: failed to set peer public key for client %s", client->name); |
|
} else { |
|
DEBUG_INFO(DEBUG_CATEGORY_CRYPTO, "init_connections: successfully set peer public key for client %s", client->name); |
|
} |
|
} else { |
|
DEBUG_WARN(DEBUG_CATEGORY_CONFIG, "init_connections: no peer public key configured for client %s", client->name); |
|
} |
|
|
|
etcp_conn->routing_exchange_active=1;// инициируем обмен маршрутами |
|
|
|
// Create links for this client |
|
struct CFG_CLIENT_LINK* client_link = client->links; |
|
while (client_link) { |
|
// Find the local server for this link |
|
struct CFG_SERVER* local_server = client_link->local_srv; |
|
if (!local_server) { |
|
client_link = client_link->next; |
|
continue; |
|
} |
|
|
|
// Find the socket for this server |
|
struct ETCP_SOCKET* e_sock = NULL; |
|
struct ETCP_SOCKET* sock = instance->etcp_sockets; |
|
while (sock) { |
|
if (sock->local_addr.ss_family == local_server->ip.ss_family) { |
|
if (sock->local_addr.ss_family == AF_INET) { |
|
struct sockaddr_in* sock_addr = (struct sockaddr_in*)&sock->local_addr; |
|
struct sockaddr_in* srv_addr = (struct sockaddr_in*)&local_server->ip; |
|
if (sock_addr->sin_addr.s_addr == srv_addr->sin_addr.s_addr && |
|
sock_addr->sin_port == srv_addr->sin_port) { |
|
e_sock = sock; |
|
break; |
|
} |
|
} |
|
} |
|
sock = sock->next; |
|
} |
|
|
|
if (!e_sock) { |
|
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "No socket found for client %s link", client->name); |
|
client_link = client_link->next; |
|
continue; |
|
} |
|
|
|
// Create link for this client connection |
|
struct ETCP_LINK* link = etcp_link_new(etcp_conn, e_sock, &client_link->remote_addr, 0); // 0 = client initiates |
|
if (!link) { |
|
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Failed to create link for client %s", client->name); |
|
client_link = client_link->next; |
|
continue; |
|
} |
|
DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "Created link %p for client %s, socket=%p", |
|
link, client->name, e_sock); |
|
|
|
client_link = client_link->next; |
|
} |
|
|
|
etcp_conn->next = instance->connections; |
|
instance->connections = etcp_conn; |
|
instance->connections_count++; |
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "Added connection %p to instance, total count: %d", etcp_conn, instance->connections_count); |
|
client = client->next; |
|
} |
|
|
|
// If there are clients configured but no connections created, that's an error |
|
// If there are no clients (server-only mode), 0 connections is OK (server will accept incoming) |
|
if (instance->connections_count == 0 && config->clients != NULL) { |
|
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Clients configured but no connections initialized"); |
|
return -1; |
|
} |
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "Initialized %d connections", instance->connections_count); |
|
return 0; |
|
}
|
|
|