420 lines
21 KiB
C
420 lines
21 KiB
C
#include "etcp_api.h"
|
||
#include "etcp.h"
|
||
#include "etcp_connect.h"
|
||
#include "utun_instance.h"
|
||
#include "topo_node.h"
|
||
#include "stcp_link.h"
|
||
#include "../lib/debug_config.h"
|
||
#include "../lib/mem.h"
|
||
#include "../lib/u_async.h"
|
||
#include "../lib/socket_compat.h"
|
||
#include <string.h>
|
||
|
||
#define DEBUG_CATEGORY_ETCP_CONNECT DEBUG_CATEGORY_ETCP
|
||
|
||
struct etcp_connect_cb_node {
|
||
etcp_connect_callback_t cb;
|
||
void* arg;
|
||
uint8_t flags;
|
||
struct etcp_connect_cb_node* next;
|
||
};
|
||
|
||
struct ETCP_CONNECT {
|
||
struct ETCP_CONNECT* next;
|
||
struct UTUN_INSTANCE* instance;
|
||
uint64_t node_id;
|
||
struct ETCP_CONN* conn;
|
||
struct etcp_connect_cb_node* cb_list;
|
||
void* initial_timer;
|
||
void* settle_timer;
|
||
uint16_t min_rtt;
|
||
uint8_t early_delivered : 1;
|
||
uint8_t done : 1;
|
||
};
|
||
static void connect_init_cb(struct ETCP_CONN* conn, int event, void* arg);
|
||
|
||
static struct ETCP_CONNECT* connect_find(struct UTUN_INSTANCE* inst, uint64_t node_id) {
|
||
struct ETCP_CONNECT* ctx = inst->pending_connects;
|
||
while (ctx) { if (ctx->node_id == node_id) return ctx; ctx = ctx->next; }
|
||
return NULL;
|
||
}
|
||
|
||
static void connect_deliver(struct ETCP_CONNECT* ctx, int type, int free_nodes) {
|
||
struct etcp_connect_cb_node** pp = &ctx->cb_list;
|
||
while (*pp) {
|
||
struct etcp_connect_cb_node* node = *pp;
|
||
if (type == 0 || (node->flags & (uint8_t)type)) {
|
||
node->cb(node->arg, (type == 0) ? NULL : ctx->conn, type);
|
||
if (free_nodes) { *pp = node->next; u_free(node); } else { pp = &node->next; }
|
||
} else {
|
||
pp = &node->next;
|
||
}
|
||
}
|
||
}
|
||
|
||
static void connect_cancel(struct ETCP_CONNECT* ctx) {
|
||
if (!ctx) return;
|
||
struct UTUN_INSTANCE* inst = ctx->instance;
|
||
if (inst) {
|
||
struct ETCP_CONNECT** pp = &inst->pending_connects;
|
||
while (*pp && *pp != ctx) pp = &(*pp)->next;
|
||
if (*pp) *pp = ctx->next;
|
||
if (ctx->initial_timer) { uasync_cancel_timeout(inst->ua, ctx->initial_timer); ctx->initial_timer = NULL; }
|
||
if (ctx->settle_timer) { uasync_cancel_timeout(inst->ua, ctx->settle_timer); ctx->settle_timer = NULL; }
|
||
}
|
||
u_free(ctx);
|
||
}
|
||
|
||
static void connect_create_links_v4(struct ETCP_CONNECT* ctx, struct TOPO_GROUP_NODE* node) {
|
||
if (!node) return;
|
||
struct TOPO_NODE* ni = topo_node_registry_find(ctx->instance->topo_groups, node->node_id);
|
||
if (!ni) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "[etcp_connect] links_v4: ni NOT in registry for node=%016llx", (unsigned long long)node->node_id); return; }
|
||
int addr_count = 0, sock_count = 0, link_count = 0;
|
||
{ struct ETCP_SOCKET* s = ctx->instance->etcp_sockets; while (s) { sock_count++; s = s->next; } }
|
||
for (const struct TOPO_ADDR4* a = ni->v4_addrs; a; a = a->next) {
|
||
addr_count++;
|
||
if (a->port == 0) continue;
|
||
{ int zero = 1; for (int j = 0; j < 4; j++) if (a->addr[j] != 0) { zero = 0; break; } if (zero) continue; }
|
||
struct sockaddr_in sin; memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET;
|
||
memcpy(&sin.sin_addr.s_addr, a->addr, 4); sin.sin_port = htons(a->port);
|
||
struct sockaddr_storage sa; memcpy(&sa, &sin, sizeof(sin));
|
||
int addr_links = 0;
|
||
if (a->protocol & TOPO_PROTO_UDP) {
|
||
struct ETCP_SOCKET* s = ctx->instance->etcp_sockets;
|
||
while (s) { if (s->local_addr.ss_family == AF_INET && etcp_link_new(ctx->conn, s, &sa, 0)) { link_count++; addr_links++; } s = s->next; }
|
||
}
|
||
if (a->protocol & TOPO_PROTO_TCP) {
|
||
struct ETCP_SOCKET* s = ctx->instance->etcp_sockets;
|
||
while (s) { if (s->is_tcp && s->local_addr.ss_family == AF_INET) {
|
||
struct ETCP_LINK *tlink = etcp_link_new(ctx->conn, s, &sa, 0);
|
||
if (tlink) { tlink->is_tcp = 1; topo_node_apply_reality(tlink, topo_node_find_reality_sock(ni, a->socket_id)); etcp_tcp_link_start_connect(tlink, &sa, a->port); link_count++; addr_links++; }
|
||
} s = s->next; }
|
||
}
|
||
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[etcp_connect] link_v4: %d.%d.%d.%d:%d proto=%d links=%d conn=[%s]",
|
||
a->addr[0], a->addr[1], a->addr[2], a->addr[3], a->port, a->protocol, addr_links, ctx->conn->log_name);
|
||
}
|
||
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[etcp_connect] links_v4: TOTAL addrs=%d socks=%d links=%d%s for node=%016llx",
|
||
addr_count, sock_count, link_count, addr_count == 0 ? " (v4_addrs EMPTY)" : "", (unsigned long long)node->node_id);
|
||
}
|
||
|
||
static void connect_create_links_v6(struct ETCP_CONNECT* ctx, struct TOPO_GROUP_NODE* node) {
|
||
if (!node) return;
|
||
struct TOPO_NODE* ni = topo_node_registry_find(ctx->instance->topo_groups, node->node_id);
|
||
if (!ni) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "[etcp_connect] links_v6: ni NOT in registry for node=%016llx", (unsigned long long)node->node_id); return; }
|
||
int addr_count = 0, sock_count = 0, link_count = 0;
|
||
{ struct ETCP_SOCKET* s = ctx->instance->etcp_sockets; while (s) { sock_count++; s = s->next; } }
|
||
for (const struct TOPO_ADDR6* a = ni->v6_addrs; a; a = a->next) {
|
||
addr_count++;
|
||
if (a->port == 0) { DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[etcp_connect] link_v6: skip port==0 node=%016llx", (unsigned long long)node->node_id); continue; }
|
||
int zero = 1; for (int j = 0; j < 16; j++) if (a->addr[j] != 0) { zero = 0; break; } if (zero) { DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[etcp_connect] link_v6: skip zero addr node=%016llx", (unsigned long long)node->node_id); continue; }
|
||
struct sockaddr_in6 sin6; memset(&sin6, 0, sizeof(sin6)); sin6.sin6_family = AF_INET6;
|
||
memcpy(&sin6.sin6_addr, a->addr, 16); sin6.sin6_port = htons(a->port);
|
||
int is_ll = (a->addr[0] == 0xfe && (a->addr[1] & 0xc0) == 0x80);
|
||
int addr_links = 0;
|
||
if (a->protocol & TOPO_PROTO_UDP) {
|
||
struct ETCP_SOCKET* s = ctx->instance->etcp_sockets;
|
||
while (s) { if (s->local_addr.ss_family == AF_INET6) {
|
||
if (is_ll) sin6.sin6_scope_id = s->netif_index;
|
||
struct sockaddr_storage sa; memcpy(&sa, &sin6, sizeof(sin6));
|
||
if (etcp_link_new(ctx->conn, s, &sa, 0)) { link_count++; addr_links++; }
|
||
} s = s->next; }
|
||
}
|
||
if (a->protocol & TOPO_PROTO_TCP) {
|
||
if (is_ll) { struct ETCP_SOCKET* sv = ctx->instance->etcp_sockets;
|
||
while (sv) { if (sv->local_addr.ss_family == AF_INET6 && sv->netif_index)
|
||
{ sin6.sin6_scope_id = sv->netif_index; break; } sv = sv->next; }
|
||
}
|
||
struct sockaddr_storage sa; memcpy(&sa, &sin6, sizeof(sin6));
|
||
struct ETCP_SOCKET* s = ctx->instance->etcp_sockets;
|
||
while (s) { if (s->is_tcp && s->local_addr.ss_family == AF_INET6) {
|
||
struct ETCP_LINK *tlink = etcp_link_new(ctx->conn, s, &sa, 0);
|
||
if (tlink) { tlink->is_tcp = 1; topo_node_apply_reality(tlink, topo_node_find_reality_sock(ni, a->socket_id)); etcp_tcp_link_start_connect(tlink, &sa, a->port); link_count++; addr_links++; }
|
||
} s = s->next; }
|
||
}
|
||
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[etcp_connect] link_v6: %s:%d proto=%d links=%d conn=[%s]",
|
||
ip_to_str(a->addr, AF_INET6).str, a->port, a->protocol, addr_links, ctx->conn->log_name);
|
||
}
|
||
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[etcp_connect] links_v6: TOTAL addrs=%d socks=%d links=%d%s for node=%016llx",
|
||
addr_count, sock_count, link_count, addr_count == 0 ? " (v6_addrs EMPTY)" : "", (unsigned long long)node->node_id);
|
||
}
|
||
|
||
|
||
static void connect_initial_timeout_cb(void* arg) {
|
||
struct ETCP_CONNECT* ctx = (struct ETCP_CONNECT*)arg;
|
||
if (!ctx || ctx->done) return;
|
||
ctx->done = 1;
|
||
ctx->initial_timer = NULL;
|
||
DEBUG_ERROR(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] timeout for node 0x%016llx", (unsigned long long)ctx->node_id);
|
||
struct ETCP_CONN* conn = ctx->conn; ctx->conn = NULL;
|
||
connect_deliver(ctx, 0, 1);
|
||
if (conn) etcp_connection_close(conn);
|
||
connect_cancel(ctx);
|
||
}
|
||
|
||
static void connect_settle_timeout_cb(void* arg) {
|
||
struct ETCP_CONNECT* ctx = (struct ETCP_CONNECT*)arg;
|
||
if (!ctx || ctx->done) return;
|
||
ctx->done = 1;
|
||
ctx->settle_timer = NULL;
|
||
DEBUG_INFO(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] settle for node 0x%016llx, min_rtt=%u",
|
||
(unsigned long long)ctx->node_id, ctx->min_rtt);
|
||
struct ETCP_LINK* link = ctx->conn->links;
|
||
while (link) {
|
||
struct ETCP_LINK* next = link->next;
|
||
if (!link->initialized) {
|
||
DEBUG_INFO(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] settle: removing link=%d is_tcp=%d state=%d addr=%s for node 0x%016llx",
|
||
link->local_link_id, link->is_tcp, link->link_state,
|
||
sockaddr_storage_to_str(&link->remote_addr).str, (unsigned long long)ctx->node_id);
|
||
etcp_link_close(link);
|
||
}
|
||
link = next;
|
||
}
|
||
etcp_conn_remove_cbk(ctx->conn, connect_init_cb, ctx);
|
||
connect_deliver(ctx, ETCP_CONNECT_LATE, 1);
|
||
connect_cancel(ctx);
|
||
}
|
||
|
||
static void connect_init_cb(struct ETCP_CONN* conn, int event, void* arg) { (void)event;
|
||
struct ETCP_CONNECT* ctx = (struct ETCP_CONNECT*)arg;
|
||
if (!ctx || ctx->done) return;
|
||
|
||
int total_links = 0, up_links = 0;
|
||
struct ETCP_LINK* link = conn->links;
|
||
while (link) {
|
||
total_links++;
|
||
if (link->initialized && link->rtt_last && link->rtt_last < ctx->min_rtt)
|
||
ctx->min_rtt = link->rtt_last;
|
||
if (link->link_state == LINK_STATE_CONNECTED) up_links++;
|
||
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[etcp_connect] init_cb: link=%d state=%d init=%d is_tcp=%d rtt=%u addr=%s",
|
||
link->local_link_id, link->link_state, link->initialized, link->is_tcp, link->rtt_last,
|
||
sockaddr_storage_to_str(&link->remote_addr).str);
|
||
link = link->next;
|
||
}
|
||
|
||
if (total_links == 1 || up_links == total_links || (total_links == 0 && conn->tcp_link_count > 0)) {
|
||
if (!ctx->early_delivered) {
|
||
ctx->early_delivered = 1;
|
||
if (ctx->initial_timer) { uasync_cancel_timeout(ctx->instance->ua, ctx->initial_timer); ctx->initial_timer = NULL; }
|
||
}
|
||
DEBUG_INFO(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] init_cb: node 0x%016llx links=%d up=%d tcp=%d → immediate LATE",
|
||
(unsigned long long)ctx->node_id, total_links, up_links, conn->tcp_link_count);
|
||
{ struct etcp_connect_cb_node* n = ctx->cb_list; while (n) { struct etcp_connect_cb_node* next = n->next; if (n->flags & ETCP_CONNECT_EARLY) n->cb(n->arg, ctx->conn, ETCP_CONNECT_EARLY); if (n->flags & ETCP_CONNECT_LATE) n->cb(n->arg, ctx->conn, ETCP_CONNECT_LATE); u_free(n); n = next; } ctx->cb_list = NULL; }
|
||
connect_cancel(ctx);
|
||
return;
|
||
}
|
||
|
||
if (ctx->early_delivered) return;
|
||
ctx->early_delivered = 1;
|
||
DEBUG_INFO(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] init_cb: node 0x%016llx links=%d up=%d tcp=%d → EARLY, settle",
|
||
(unsigned long long)ctx->node_id, total_links, up_links, conn->tcp_link_count);
|
||
connect_deliver(ctx, ETCP_CONNECT_EARLY, 0);
|
||
if (ctx->initial_timer) { uasync_cancel_timeout(ctx->instance->ua, ctx->initial_timer); ctx->initial_timer = NULL; }
|
||
uint32_t settle_tb;
|
||
if (ctx->min_rtt == 0xFFFF) settle_tb = 20000;
|
||
else {
|
||
settle_tb = (uint32_t)ctx->min_rtt * 8;
|
||
if (settle_tb < 5000) settle_tb = 5000;
|
||
if (settle_tb > 20000) settle_tb = 20000;
|
||
}
|
||
ctx->settle_timer = uasync_set_timeout(ctx->instance->ua, (int)settle_tb, ctx,
|
||
connect_settle_timeout_cb, "etcp_connect_settle");
|
||
}
|
||
|
||
int etcp_connect(struct UTUN_INSTANCE* inst, struct TOPO_GROUP_NODE* node,
|
||
etcp_connect_callback_t cb, void* arg, uint8_t flags) {
|
||
if (!inst || !node || !cb || !flags || (flags & ~(ETCP_CONNECT_EARLY | ETCP_CONNECT_LATE))) {
|
||
DEBUG_ERROR(DEBUG_CATEGORY_ETCP_CONNECT, "invalid connect arguments/flags=%u", flags); return -1;
|
||
}
|
||
|
||
struct TOPO_NODE* ni = topo_node_registry_find(inst->topo_groups, node->node_id);
|
||
if (!ni) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] node not in registry"); return -1; }
|
||
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[etcp_connect] registry OK node=%016llx pub=%016llx v4addrs=%d v6addrs=%d",
|
||
(unsigned long long)node->node_id, *(uint64_t*)ni->public_key,
|
||
topo_list_count((struct _topo_head*)ni->v4_addrs),
|
||
topo_list_count((struct _topo_head*)ni->v6_addrs));
|
||
|
||
uint64_t node_id = ni->node_id;
|
||
if (node_id == 0) {
|
||
if (!ni->public_key) {
|
||
DEBUG_ERROR(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] node_id=0 and public_key is NULL");
|
||
return -1;
|
||
}
|
||
int zero = 1;
|
||
for (int i = 0; i < SC_PUBKEY_SIZE; i++)
|
||
if (ni->public_key[i]) { zero = 0; break; }
|
||
if (zero) {
|
||
DEBUG_ERROR(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] node_id=0 and public_key is all zeros");
|
||
return -1;
|
||
}
|
||
node_id = sc_derive_node_id_from_pubkey(ni->public_key);
|
||
ni->node_id = node_id;
|
||
}
|
||
|
||
struct ETCP_CONN* conn = instance_find_conn(inst, node_id);
|
||
struct ETCP_CONNECT* ctx = connect_find(inst, node_id);
|
||
|
||
// Case 1: conn && ctx — already connecting
|
||
if (conn && ctx) {
|
||
if (ctx->done) {
|
||
DEBUG_INFO(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] node 0x%016llx: existing ctx timed out, cb(NULL,0)",
|
||
(unsigned long long)node_id);
|
||
cb(arg, NULL, 0);
|
||
return 0;
|
||
}
|
||
struct etcp_connect_cb_node* cn = u_calloc(1, sizeof(struct etcp_connect_cb_node));
|
||
if (!cn) return -1;
|
||
cn->cb = cb; cn->arg = arg; cn->flags = flags; cn->next = ctx->cb_list; ctx->cb_list = cn;
|
||
DEBUG_INFO(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] node 0x%016llx already connecting, added cb",
|
||
(unsigned long long)node_id);
|
||
return 0;
|
||
}
|
||
|
||
// Case 2: !conn && ctx — context exists but conn was removed (rare)
|
||
if (!conn && ctx) {
|
||
struct etcp_connect_cb_node* cn = u_calloc(1, sizeof(struct etcp_connect_cb_node));
|
||
if (!cn) return -1;
|
||
cn->cb = cb; cn->arg = arg; cn->flags = flags; cn->next = ctx->cb_list; ctx->cb_list = cn;
|
||
return 0;
|
||
}
|
||
|
||
// Case 3: conn && !ctx — existing connection, no context
|
||
if (conn && !ctx) {
|
||
if (conn->state == 0) {
|
||
// Pending conn from another path (e.g. incoming INIT before etcp_connect)
|
||
ctx = u_calloc(1, sizeof(struct ETCP_CONNECT));
|
||
if (!ctx) return -1;
|
||
ctx->instance = inst; ctx->node_id = node_id; ctx->conn = conn;
|
||
ctx->min_rtt = 0xFFFF;
|
||
struct etcp_connect_cb_node* cn = u_calloc(1, sizeof(struct etcp_connect_cb_node));
|
||
if (!cn) { u_free(ctx); return -1; }
|
||
cn->cb = cb; cn->arg = arg; cn->flags = flags; ctx->cb_list = cn;
|
||
etcp_conn_add_cbk(conn, connect_init_cb, ctx, ETCP_CBK_EVENT_INIT);
|
||
ctx->initial_timer = uasync_set_timeout(inst->ua, (int)inst->etcp_connect_timeout_tb, ctx,
|
||
connect_initial_timeout_cb, "etcp_connect_init");
|
||
ctx->next = inst->pending_connects; inst->pending_connects = ctx;
|
||
DEBUG_INFO(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] node 0x%016llx: existing pending conn, waiting for init",
|
||
(unsigned long long)node_id);
|
||
return 0;
|
||
}
|
||
|
||
// conn->state == 1: already ready — deliver EARLY+LATE immediately (via direct loop to deliver both phases)
|
||
ctx = u_calloc(1, sizeof(struct ETCP_CONNECT));
|
||
if (!ctx) return -1;
|
||
ctx->instance = inst; ctx->node_id = node_id; ctx->conn = conn;
|
||
ctx->early_delivered = 1;
|
||
{
|
||
struct etcp_connect_cb_node* cn = u_calloc(1, sizeof(struct etcp_connect_cb_node));
|
||
if (!cn) { u_free(ctx); return -1; }
|
||
cn->cb = cb; cn->arg = arg; cn->flags = flags;
|
||
ctx->cb_list = cn;
|
||
}
|
||
|
||
DEBUG_INFO(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] node 0x%016llx: existing ready conn, delivering EARLY+LATE",
|
||
(unsigned long long)node_id);
|
||
struct etcp_connect_cb_node* n = ctx->cb_list;
|
||
while (n) {
|
||
struct etcp_connect_cb_node* next = n->next;
|
||
if (n->flags & ETCP_CONNECT_EARLY) n->cb(n->arg, ctx->conn, ETCP_CONNECT_EARLY);
|
||
if (n->flags & ETCP_CONNECT_LATE) n->cb(n->arg, ctx->conn, ETCP_CONNECT_LATE);
|
||
u_free(n); n = next;
|
||
}
|
||
u_free(ctx);
|
||
return 0;
|
||
}
|
||
|
||
// Case 4: !conn && !ctx — new connection
|
||
conn = etcp_connection_create(inst, NULL);
|
||
if (!conn) {
|
||
DEBUG_ERROR(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] failed to create ETCP_CONN for node 0x%016llx",
|
||
(unsigned long long)node_id);
|
||
cb(arg, NULL, 0);
|
||
return -1;
|
||
}
|
||
conn->peer_node_id = node_id;
|
||
etcp_update_log_name(conn);
|
||
if (conn->conn_queue && conn->conn_queue_entry) {
|
||
queue_remove_data(conn->conn_queue, conn->conn_queue_entry);
|
||
queue_entry_free(conn->conn_queue_entry);
|
||
}
|
||
{ struct ll_entry* qe = queue_entry_new(sizeof(struct conn_queue_entry)); if (qe) { struct conn_queue_entry* ce = (struct conn_queue_entry*)qe->data; ce->peer_node_id = node_id; ce->conn = conn; conn->conn_queue_entry = qe; conn->conn_queue = inst->connections; queue_data_put_with_index(inst->connections, qe); } }
|
||
|
||
if (sc_init_ctx(&conn->crypto_ctx, &inst->my_keys) != SC_OK) {
|
||
DEBUG_ERROR(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] sc_init_ctx failed for node 0x%016llx", (unsigned long long)node_id);
|
||
etcp_connection_close(conn); cb(arg, NULL, 0); return -1;
|
||
}
|
||
if (sc_set_peer_public_key(&conn->crypto_ctx, ni->public_key, 0) != SC_OK) {
|
||
DEBUG_ERROR(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] sc_set_peer_public_key failed for node 0x%016llx", (unsigned long long)node_id);
|
||
etcp_connection_close(conn); cb(arg, NULL, 0); return -1;
|
||
}
|
||
|
||
ctx = u_calloc(1, sizeof(struct ETCP_CONNECT));
|
||
if (!ctx) {
|
||
DEBUG_ERROR(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] alloc failed");
|
||
etcp_connection_close(conn); cb(arg, NULL, 0); return -1;
|
||
}
|
||
ctx->instance = inst; ctx->node_id = node_id; ctx->conn = conn;
|
||
ctx->min_rtt = 0xFFFF;
|
||
{
|
||
struct etcp_connect_cb_node* cn = u_calloc(1, sizeof(struct etcp_connect_cb_node));
|
||
if (!cn) { u_free(ctx); etcp_connection_close(conn); cb(arg, NULL, 0); return -1; }
|
||
cn->cb = cb; cn->arg = arg; cn->flags = flags; ctx->cb_list = cn;
|
||
}
|
||
|
||
etcp_conn_add_cbk(conn, connect_init_cb, ctx, ETCP_CBK_EVENT_INIT);
|
||
|
||
connect_create_links_v4(ctx, node);
|
||
connect_create_links_v6(ctx, node);
|
||
|
||
if (!conn->links && conn->tcp_link_count == 0) {
|
||
DEBUG_ERROR(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] no links created for node 0x%016llx (links=%p tcp_links=%d)",
|
||
(unsigned long long)node_id, (void*)conn->links, conn->tcp_link_count);
|
||
connect_deliver(ctx, 0, 1);
|
||
etcp_connection_close(conn); u_free(ctx); return -1;
|
||
}
|
||
|
||
ctx->initial_timer = uasync_set_timeout(inst->ua, (int)inst->etcp_connect_timeout_tb, ctx, connect_initial_timeout_cb, "etcp_connect_init");
|
||
ctx->next = inst->pending_connects; inst->pending_connects = ctx;
|
||
DEBUG_INFO(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] started for node 0x%016llx", (unsigned long long)node_id);
|
||
return 0;
|
||
}
|
||
|
||
void etcp_connect_cancel_for_conn(struct UTUN_INSTANCE* inst, struct ETCP_CONN* conn) {
|
||
if (!inst || !conn) return;
|
||
struct ETCP_CONNECT** pp = &inst->pending_connects;
|
||
while (*pp) {
|
||
struct ETCP_CONNECT* ctx = *pp;
|
||
if (ctx->conn == conn) {
|
||
*pp = ctx->next;
|
||
ctx->done = 1;
|
||
ctx->conn = NULL;
|
||
if (ctx->initial_timer) { uasync_cancel_timeout(inst->ua, ctx->initial_timer); ctx->initial_timer = NULL; }
|
||
if (ctx->settle_timer) { uasync_cancel_timeout(inst->ua, ctx->settle_timer); ctx->settle_timer = NULL; }
|
||
u_free(ctx);
|
||
} else {
|
||
pp = &(*pp)->next;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* Полная отмена pending-коннекта по node_id: снимает connect_init_cb
|
||
* с conn (conn остаётся жив, например, переиспользуется ncd), отменяет таймеры,
|
||
* вынимает ctx из pending_connects и освобождает. Безопасно вызывать, если ctx нет. */
|
||
void etcp_connect_cancel_by_node(struct UTUN_INSTANCE* inst, uint64_t node_id) {
|
||
if (!inst) return;
|
||
struct ETCP_CONNECT** pp = &inst->pending_connects;
|
||
while (*pp) {
|
||
struct ETCP_CONNECT* ctx = *pp;
|
||
if (ctx->node_id == node_id) {
|
||
*pp = ctx->next;
|
||
if (ctx->conn) {
|
||
etcp_conn_remove_cbk(ctx->conn, connect_init_cb, ctx);
|
||
}
|
||
if (ctx->initial_timer) { uasync_cancel_timeout(inst->ua, ctx->initial_timer); ctx->initial_timer = NULL; }
|
||
if (ctx->settle_timer) { uasync_cancel_timeout(inst->ua, ctx->settle_timer); ctx->settle_timer = NULL; }
|
||
u_free(ctx);
|
||
} else {
|
||
pp = &(*pp)->next;
|
||
}
|
||
}
|
||
}
|