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.
530 lines
17 KiB
530 lines
17 KiB
#include "etcp_connections.h" |
|
#include <arpa/inet.h> |
|
#include "routing.h" |
|
#include "utun_instance.h" |
|
#include "crc32.h" |
|
#include "etcp.h" |
|
#include <stdlib.h> |
|
#include <string.h> |
|
#include <time.h> |
|
|
|
// Forward declaration |
|
struct utun_instance; |
|
|
|
// Бинарный поиск линка по ip_port_hash |
|
static int find_link_index(struct ETCP_CONNECTIONS* conns, uint32_t hash) { |
|
if (!conns || conns->num_channels == 0) return -1; |
|
|
|
int left = 0; |
|
int right = conns->num_channels - 1; |
|
|
|
while (left <= right) { |
|
int mid = left + (right - left) / 2; |
|
if (conns->links[mid]->ip_port_hash == hash) { |
|
return mid; |
|
} else if (conns->links[mid]->ip_port_hash < hash) { |
|
left = mid + 1; |
|
} else { |
|
right = mid - 1; |
|
} |
|
} |
|
|
|
return -(left + 1); |
|
} |
|
|
|
// Реалокация массива линков с увеличением в 2 раза |
|
static int realloc_links(struct ETCP_CONNECTIONS* conns) { |
|
size_t new_max = conns->max_channels == 0 ? 8 : conns->max_channels * 2; |
|
struct ETCP_LINK** new_links = realloc(conns->links, new_max * sizeof(struct ETCP_LINK*)); |
|
if (!new_links) return -1; |
|
|
|
conns->links = new_links; |
|
conns->max_channels = new_max; |
|
return 0; |
|
} |
|
|
|
// Вставка линка в отсортированный массив |
|
static int insert_link(struct ETCP_CONNECTIONS* conns, struct ETCP_LINK* link) { |
|
if (!conns || !link) return -1; |
|
|
|
if (conns->num_channels >= conns->max_channels) { |
|
if (realloc_links(conns) < 0) return -1; |
|
} |
|
|
|
int idx = find_link_index(conns, link->ip_port_hash); |
|
if (idx >= 0) return -1; |
|
|
|
idx = -(idx + 1); |
|
|
|
if (idx < (int)conns->num_channels) { |
|
memmove(&conns->links[idx + 1], &conns->links[idx], |
|
(conns->num_channels - idx) * sizeof(struct ETCP_LINK*)); |
|
} |
|
|
|
conns->links[idx] = link; |
|
conns->num_channels++; |
|
return 0; |
|
} |
|
|
|
// Удаление линка из массива |
|
static void remove_link(struct ETCP_CONNECTIONS* conns, uint32_t hash) { |
|
if (!conns || conns->num_channels == 0) return; |
|
|
|
int idx = find_link_index(conns, hash); |
|
if (idx < 0) return; |
|
|
|
if (idx < (int)conns->num_channels - 1) { |
|
memmove(&conns->links[idx], &conns->links[idx + 1], |
|
(conns->num_channels - idx - 1) * sizeof(struct ETCP_LINK*)); |
|
} |
|
|
|
conns->num_channels--; |
|
} |
|
|
|
struct ETCP_CONNECTIONS* etcp_connections_init(etcp_socket_t* socket) { |
|
if (!socket) return NULL; |
|
|
|
struct ETCP_CONNECTIONS* conns = calloc(1, sizeof(struct ETCP_CONNECTIONS)); |
|
if (!conns) return NULL; |
|
|
|
memcpy(&conns->socket, socket, sizeof(struct ETCP_SOCKET)); |
|
return conns; |
|
} |
|
|
|
void etcp_connections_destroy(struct ETCP_CONNECTIONS* conns) { |
|
if (!conns) return; |
|
|
|
for (size_t i = 0; i < conns->num_channels; i++) { |
|
etcp_link_close(conns->links[i]); |
|
} |
|
|
|
free(conns->links); |
|
free(conns); |
|
} |
|
|
|
struct ETCP_LINK* etcp_link_new(struct ETCP_CONN* etcp, etcp_socket_t* socket, |
|
const struct sockaddr* remote_addr, socklen_t addr_len) { |
|
if (!etcp || !socket || !remote_addr || addr_len == 0) return NULL; |
|
|
|
struct ETCP_LINK* link = calloc(1, sizeof(struct ETCP_LINK)); |
|
if (!link) return NULL; |
|
|
|
link->socket = (struct ETCP_SOCKET*)socket; |
|
link->etcp = etcp; |
|
memcpy(&link->remote_addr, remote_addr, addr_len); |
|
link->remote_addr_len = addr_len; |
|
link->last_activity = time(NULL); |
|
|
|
uint8_t* addr_bytes = (uint8_t*)remote_addr; |
|
link->ip_port_hash = crc32(0, addr_bytes, addr_len); |
|
|
|
return link; |
|
} |
|
|
|
void etcp_link_close(struct ETCP_LINK* link) { |
|
if (!link) return; |
|
free(link); |
|
} |
|
|
|
int etcp_input(packet_buffer_t* pkt, etcp_socket_t* socket, struct ETCP_CONNECTIONS* conns) { |
|
if (!pkt || !socket || !conns) return -1; |
|
return 0; |
|
} |
|
|
|
int etcp_link_send(struct ETCP_CONN* etcp, struct ETCP_LINK* link, |
|
const uint8_t* data, size_t len) { |
|
if (!etcp || !link || !data || len == 0) return -1; |
|
|
|
link->last_activity = time(NULL); |
|
|
|
return etcp_socket_send((etcp_socket_t*)link->socket, data, len, |
|
(struct sockaddr*)&link->remote_addr); |
|
} |
|
|
|
struct ETCP_LINK* etcp_link_find_by_addr(struct ETCP_CONNECTIONS* conns, |
|
const struct sockaddr* addr, socklen_t addr_len) { |
|
if (!conns || !addr || addr_len == 0) return NULL; |
|
|
|
uint32_t hash = crc32(0, (uint8_t*)addr, addr_len); |
|
int idx = find_link_index(conns, hash); |
|
|
|
if (idx >= 0) { |
|
return conns->links[idx]; |
|
} |
|
return NULL; |
|
} |
|
|
|
int etcp_link_add_to_connections(struct ETCP_CONNECTIONS* conns, struct ETCP_LINK* link) { |
|
if (!conns || !link) return -1; |
|
return insert_link(conns, link); |
|
} |
|
|
|
void etcp_link_remove_from_connections(struct ETCP_CONNECTIONS* conns, struct ETCP_LINK* link) { |
|
if (!conns || !link) return; |
|
remove_link(conns, link->ip_port_hash); |
|
} |
|
|
|
|
|
|
|
|
|
// SOCKET FUNCTIONS (moved from etcp_sockets.c) |
|
#include <unistd.h> |
|
#include <fcntl.h> |
|
#include <errno.h> |
|
#include <arpa/inet.h> |
|
|
|
etcp_socket_t* etcp_socket_create(struct ETCP_CONN* etcp, const char* bind_addr, uint16_t port) { |
|
if (!etcp) return NULL; |
|
|
|
etcp_socket_t* sock = calloc(1, sizeof(etcp_socket_t)); |
|
if (!sock) return NULL; |
|
|
|
sock->etcp = etcp; |
|
sock->socket_id = (uint32_t)(uintptr_t)sock; |
|
|
|
sock->fd = socket(AF_INET, SOCK_DGRAM, 0); |
|
if (sock->fd < 0) { |
|
free(sock); |
|
return NULL; |
|
} |
|
|
|
int flags = fcntl(sock->fd, F_GETFL, 0); |
|
fcntl(sock->fd, F_SETFL, flags | O_NONBLOCK); |
|
|
|
struct sockaddr_in* addr = (struct sockaddr_in*)&sock->local_addr; |
|
addr->sin_family = AF_INET; |
|
addr->sin_port = htons(port); |
|
sock->local_addr_len = sizeof(struct sockaddr_in); |
|
|
|
if (bind_addr) { |
|
inet_pton(AF_INET, bind_addr, &addr->sin_addr); |
|
} else { |
|
addr->sin_addr.s_addr = INADDR_ANY; |
|
} |
|
|
|
if (bind(sock->fd, (struct sockaddr*)addr, sock->local_addr_len) < 0) { |
|
close(sock->fd); |
|
free(sock); |
|
return NULL; |
|
} |
|
|
|
if (getsockname(sock->fd, (struct sockaddr*)addr, &sock->local_addr_len) < 0) { |
|
close(sock->fd); |
|
free(sock); |
|
return NULL; |
|
} |
|
|
|
return sock; |
|
} |
|
|
|
void etcp_socket_destroy(etcp_socket_t* sock) { |
|
if (!sock) return; |
|
|
|
if (sock->fd >= 0) { |
|
close(sock->fd); |
|
} |
|
|
|
free(sock); |
|
} |
|
|
|
int etcp_socket_send(etcp_socket_t* sock, const uint8_t* data, size_t len, const struct sockaddr* to_addr) { |
|
if (!sock || sock->fd < 0 || !data || !to_addr) { |
|
return -1; |
|
} |
|
|
|
socklen_t addr_len = (to_addr->sa_family == AF_INET) ? |
|
sizeof(struct sockaddr_in) : |
|
sizeof(struct sockaddr_in6); |
|
|
|
return sendto(sock->fd, data, len, 0, to_addr, addr_len); |
|
} |
|
|
|
int etcp_socket_get_fd(const etcp_socket_t* sock) { |
|
if (!sock) return -1; |
|
return sock->fd; |
|
} |
|
|
|
void etcp_socket_get_local_addr(const etcp_socket_t* sock, struct sockaddr_storage* addr, socklen_t* addr_len) { |
|
if (!sock || !addr || !addr_len) return; |
|
|
|
memcpy(addr, &sock->local_addr, sock->local_addr_len); |
|
*addr_len = sock->local_addr_len; |
|
} |
|
|
|
int etcp_socket_set_option(etcp_socket_t* sock, int level, int optname, const void* optval, socklen_t optlen) { |
|
if (!sock || sock->fd < 0) return -1; |
|
return setsockopt(sock->fd, level, optname, optval, optlen); |
|
} |
|
// Connection management functions |
|
void conn_destroy(conn_handle_t* handle) { |
|
if (!handle) return; |
|
if (handle->conns) { |
|
etcp_socket_destroy((etcp_socket_t*)&handle->conns->socket); |
|
etcp_connections_destroy(handle->conns); |
|
} |
|
if (handle->etcp) { |
|
etcp_destroy(handle->etcp); |
|
} |
|
free(handle); |
|
} |
|
|
|
int conn_send(conn_handle_t* handle, const uint8_t* data, size_t len) { |
|
if (!handle || !handle->conns || handle->conns->num_channels == 0 || !data || len == 0) { |
|
return -1; |
|
} |
|
|
|
struct ETCP_LINK* link = handle->conns->links[0]; |
|
if (!link || !link->etcp) return -1; |
|
|
|
return etcp_link_send(link->etcp, link, data, len); |
|
} |
|
|
|
// Parse address in format "IP:port" or "[IPv6]:port" and fill sockaddr |
|
static int parse_addr_port(const char* addr_str, struct sockaddr_storage* addr, socklen_t* addr_len) { |
|
if (!addr_str || !addr || !addr_len) return -1; |
|
|
|
char addr_copy[MAX_ADDR_LEN]; |
|
strncpy(addr_copy, addr_str, MAX_ADDR_LEN - 1); |
|
addr_copy[MAX_ADDR_LEN - 1] = '\0'; |
|
|
|
// Check for IPv6 format: [address]:port |
|
if (addr_copy[0] == '[') { |
|
char* closing_bracket = strchr(addr_copy, ']'); |
|
if (!closing_bracket) return -1; |
|
|
|
// Find port after ']:' |
|
char* colon = strchr(closing_bracket + 1, ':'); |
|
if (!colon) return -1; |
|
|
|
*closing_bracket = '\0'; |
|
uint16_t port = (uint16_t)atoi(colon + 1); |
|
|
|
struct sockaddr_in6* sin6 = (struct sockaddr_in6*)addr; |
|
memset(sin6, 0, sizeof(struct sockaddr_in6)); |
|
sin6->sin6_family = AF_INET6; |
|
sin6->sin6_port = htons(port); |
|
|
|
// Extract IP string and port from sockaddr_storage |
|
static int sockaddr_to_ip_port(const struct sockaddr_storage* addr, char* ip_out, size_t ip_out_len, uint16_t* port_out) { |
|
if (!addr || !ip_out || !port_out) return -1; |
|
|
|
if (addr->ss_family == AF_INET) { |
|
struct sockaddr_in* sin = (struct sockaddr_in*)addr; |
|
*port_out = ntohs(sin->sin_port); |
|
if (inet_ntop(AF_INET, &sin->sin_addr, ip_out, ip_out_len) == NULL) return -1; |
|
} else if (addr->ss_family == AF_INET6) { |
|
struct sockaddr_in6* sin6 = (struct sockaddr_in6*)addr; |
|
*port_out = ntohs(sin6->sin6_port); |
|
if (inet_ntop(AF_INET6, &sin6->sin6_addr, ip_out, ip_out_len) == NULL) return -1; |
|
} else { |
|
return -1; |
|
} |
|
|
|
return 0; |
|
} |
|
for (int i = 0; i < config->server_count; i++) { |
|
server_config_t* server = &config->servers[i]; |
|
|
|
char bind_addr[INET_ADDRSTRLEN]; |
|
struct sockaddr_storage server_addr; |
|
socklen_t server_addr_len; |
|
if (parse_addr_port(server->addr, &server_addr, &server_addr_len) < 0) { |
|
fprintf(stderr, "Invalid server address '%s'\n", server->addr); |
|
goto cleanup; |
|
} |
|
char bind_addr_str[INET6_ADDRSTRLEN]; |
|
uint16_t bind_port; |
|
if (sockaddr_to_ip_port(&server_addr, bind_addr_str, sizeof(bind_addr_str), &bind_port) < 0) { |
|
etcp_destroy(etcp); |
|
goto cleanup; |
|
} |
|
} |
|
|
|
etcp_socket_t* sock = etcp_socket_create(etcp, bind_addr, bind_port); |
|
if (!sock) { |
|
fprintf(stderr, "Failed to create socket for %s\n", server->name); |
|
etcp_destroy(etcp); |
|
goto cleanup; |
|
} |
|
|
|
if (i == 0) { |
|
instance->first_listen_socket = (struct ETCP_SOCKET*)sock; |
|
} |
|
|
|
struct ETCP_CONNECTIONS* conns = etcp_connections_init(sock); |
|
if (!conns) { |
|
etcp_socket_destroy(sock); |
|
etcp_destroy(etcp); |
|
goto cleanup; |
|
} |
|
|
|
conn_handle_t* handle = calloc(1, sizeof(conn_handle_t)); |
|
if (!handle) { |
|
etcp_connections_destroy(conns); |
|
etcp_socket_destroy(sock); |
|
etcp_destroy(etcp); |
|
goto cleanup; |
|
} |
|
|
|
handle->etcp = etcp; |
|
handle->conns = conns; |
|
|
|
for (int j = 0; j < config->client_count; j++) { |
|
client_config_t* client = &config->clients[j]; |
|
if (strcmp(client->from, server->name) != 0) continue; |
|
|
|
struct sockaddr_storage client_addr; |
|
socklen_t client_addr_len; |
|
if (parse_addr_port(client->to_addr, &client_addr, &client_addr_len) < 0) { |
|
fprintf(stderr, "Invalid client address '%s'\n", client->to_addr); |
|
continue; |
|
} |
|
memcpy(&addr, &client_addr, client_addr_len); |
|
addr_len = client_addr_len; |
|
struct sockaddr_in* sin = (struct sockaddr_in*)&addr; |
|
memset(sin, 0, sizeof(struct sockaddr_in)); |
|
sin->sin_family = AF_INET; |
|
sin->sin_port = htons(client_port); |
|
if (inet_pton(AF_INET, client_addr, &sin->sin_addr) != 1) continue; |
|
addr_len = sizeof(struct sockaddr_in); |
|
|
|
struct ETCP_LINK* link = etcp_link_new(etcp, sock, (struct sockaddr*)&addr, addr_len); |
|
if (link) { |
|
etcp_link_add_to_connections(conns, link); |
|
} |
|
} |
|
|
|
instance->connections[i] = handle; |
|
} |
|
|
|
instance->connection_count = config->server_count; |
|
return 0; |
|
|
|
cleanup: |
|
if (instance->connections) { |
|
for (int j = 0; j < 0; j++) { |
|
if (instance->connections[j]) { |
|
conn_destroy(instance->connections[j]); |
|
} |
|
} |
|
free(instance->connections); |
|
instance->connections = NULL; |
|
} |
|
instance->first_listen_socket = NULL; |
|
instance->connection_count = 0; |
|
return -1; |
|
} |
|
#include "etcp_connections.h" |
|
#include "etcp.h" |
|
#include "config_parser.h" |
|
#include "utun_instance.h" |
|
#include <string.h> |
|
#include <stdio.h> |
|
#include <stdlib.h> |
|
|
|
// Parse address in format "IP:port" or "[IPv6]:port" and fill sockaddr |
|
if (!addr || !ip_out || !port_out) return -1; |
|
|
|
if (addr->ss_family == AF_INET) { |
|
struct sockaddr_in* sin = (struct sockaddr_in*)addr; |
|
*port_out = ntohs(sin->sin_port); |
|
if (inet_ntop(AF_INET, &sin->sin_addr, ip_out, ip_out_len) == NULL) return -1; |
|
} else if (addr->ss_family == AF_INET6) { |
|
struct sockaddr_in6* sin6 = (struct sockaddr_in6*)addr; |
|
*port_out = ntohs(sin6->sin6_port); |
|
if (inet_ntop(AF_INET6, &sin6->sin6_addr, ip_out, ip_out_len) == NULL) return -1; |
|
} else { |
|
return -1; |
|
} |
|
|
|
return 0; |
|
|
|
int init_connections(utun_instance_t* instance) { |
|
if (!instance || !instance->config) { |
|
fprintf(stderr, "Invalid instance or config\n"); |
|
return -1; |
|
} |
|
|
|
utun_config_t* config = instance->config; |
|
int total_connections = config->server_count, i = 0; |
|
|
|
if (total_connections == 0) { |
|
fprintf(stderr, "No servers found in configuration\n"); |
|
return -1; |
|
} |
|
|
|
instance->connections = calloc(total_connections, sizeof(conn_handle_t*)); |
|
if (!instance->connections) { |
|
fprintf(stderr, "Failed to allocate connections array\n"); |
|
return -1; |
|
} |
|
|
|
for (i = 0; i < config->server_count; i++) { |
|
server_config_t* server = &config->servers[i]; |
|
|
|
struct sockaddr_storage server_addr; |
|
socklen_t server_addr_len; |
|
if (parse_addr_port(server->addr, &server_addr, &server_addr_len) < 0) { |
|
fprintf(stderr, "Invalid server address '%s'\n", server->addr); |
|
goto cleanup; |
|
} |
|
|
|
struct ETCP_CONN* etcp = etcp_create(instance); |
|
if (!etcp) { |
|
fprintf(stderr, "Failed to create ETCP for server %s\n", server->name); |
|
goto cleanup; |
|
} |
|
|
|
char bind_addr_str[INET6_ADDRSTRLEN]; |
|
} |
|
|
|
conn_handle_t* handle = calloc(1, sizeof(conn_handle_t)); |
|
if (!handle) { |
|
etcp_connections_destroy(conns); |
|
etcp_socket_destroy(sock); |
|
etcp_destroy(etcp); |
|
goto cleanup; |
|
} |
|
|
|
handle->etcp = etcp; |
|
handle->conns = conns; |
|
|
|
for (int j = 0; j < config->client_count; j++) { |
|
client_config_t* client = &config->clients[j]; |
|
if (strcmp(client->from, server->name) != 0) continue; |
|
|
|
struct sockaddr_storage client_addr; |
|
socklen_t client_addr_len; |
|
if (parse_addr_port(client->to_addr, &client_addr, &client_addr_len) < 0) { |
|
fprintf(stderr, "Invalid client address '%s'\n", client->to_addr); |
|
continue; |
|
} |
|
|
|
struct sockaddr_storage addr; |
|
socklen_t addr_len = client_addr_len; |
|
|
|
struct ETCP_LINK* link = etcp_link_new(etcp, sock, (struct sockaddr*)&addr, addr_len); |
|
if (link) { |
|
etcp_link_add_to_connections(conns, link); |
|
} |
|
} |
|
|
|
instance->connections[i] = handle; |
|
} |
|
|
|
instance->connection_count = config->server_count; |
|
return 0; |
|
|
|
cleanup: |
|
if (instance->connections) { |
|
for (int j = 0; j < i; j++) { |
|
if (instance->connections[j]) { |
|
conn_destroy(instance->connections[j]); |
|
} |
|
} |
|
free(instance->connections); |
|
instance->connections = NULL; |
|
} |
|
instance->first_listen_socket = NULL; |
|
instance->connection_count = 0; |
|
return -1;
|
|
|