From 08e8b2f1ccd5dc95554755ccab207c794efbb383 Mon Sep 17 00:00:00 2001 From: evgeny Date: Fri, 25 Sep 2026 16:01:19 +0300 Subject: [PATCH] =?UTF-8?q?route=5Fconnectivity:=20=D1=84=D0=B8=D0=BA?= =?UTF-8?q?=D1=81=20UAF=20=D0=BF=D1=80=D0=B8=20=D1=83=D0=B4=D0=B0=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B8=20=D1=83=D0=B7=D0=BB=D0=B0=20=D1=81=20?= =?UTF-8?q?=D0=B2=D0=BB=D1=91=D1=82=D0=BD=D1=8B=D0=BC=D0=B8=20=D0=BF=D1=80?= =?UTF-8?q?=D0=BE=D0=B1=D0=B0=D0=BC=D0=B8;=20=D0=BE=D1=82=D0=BC=D0=B5?= =?UTF-8?q?=D0=BD=D0=B0=20TCP-=D0=BF=D0=B8=D0=BD=D0=B3=D0=BE=D0=B2=20?= =?UTF-8?q?=D0=BF=D1=80=D0=B8=20teardown?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - topo_nodeq_remove_node: единый хелпер удаления nq (cancel проб + free paths/subnets + remove + free), заменяет все free-сайты - route_connectivity_cancel_all: итерация по всем группам + зануление ctx->nq + отмена standby-defer - etcp_send_tcp_ping: out_cli — хендл stcp_client для досрочной отмены; conn_probe_ctx.tcp_cli - conn_mgr: cand_tcp_pings[] — отмена влётных candidate TCP-пингов в conn_mgr_destroy --- src/routing_layer/conn_mgr_core.c | 8 ++-- src/routing_layer/conn_mgr_monitor.c | 17 +++++++-- src/routing_layer/conn_mgr_priv.h | 2 + src/routing_layer/route_connectivity.c | 52 +++++++++++++++----------- src/routing_layer/route_connectivity.h | 3 +- src/routing_layer/topo_group.c | 33 ++++++---------- src/routing_layer/topo_group_invite.c | 2 +- src/routing_layer/topo_node.c | 12 ++++++ src/routing_layer/topo_node.h | 3 ++ src/transport_layer/etcp_connections.c | 5 ++- src/transport_layer/etcp_connections.h | 5 ++- tests/test_stcp_ping.c | 4 +- 12 files changed, 91 insertions(+), 55 deletions(-) diff --git a/src/routing_layer/conn_mgr_core.c b/src/routing_layer/conn_mgr_core.c index 6092727b..d20142b2 100644 --- a/src/routing_layer/conn_mgr_core.c +++ b/src/routing_layer/conn_mgr_core.c @@ -16,6 +16,7 @@ #include "utun_instance.h" #include "etcp.h" #include "etcp_connections.h" +#include "stcp_client.h" #include "etcp_router.h" #include "config_parser.h" #include "topo_node.h" @@ -237,9 +238,7 @@ void cm_cleanup_db_node(struct CONN_MGR_ENTRY* entry) { return; } DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "conn_mgr: removing db_node 0x%016llx from group (no paths)", (unsigned long long)entry->node_id); - queue_remove_data(group->nodes, &nq->ll); - topo_nodeq_free_group_fields(entry->mgr->instance->topo_groups, nq); - queue_entry_free(&nq->ll); + topo_nodeq_remove_node(group, nq); } /* ═══════ NCD коллбэки ═══════ */ @@ -343,6 +342,9 @@ void conn_mgr_destroy(struct CONN_MGR* mgr) { if (mgr->bg_ping_wait) { standby_wait_cancel(mgr->bg_ping_wait); mgr->bg_ping_wait = NULL; } if (mgr->candidate_ping_wait) { standby_wait_cancel(mgr->candidate_ping_wait); mgr->candidate_ping_wait = NULL; } #endif + /* Досрочно закрыть влётные candidate TCP-пинги — иначе их stcp_client утечёт до uasync_destroy. */ + for (int i = 0; i < CONN_MGR_MAX_CANDIDATES; i++) + if (mgr->cand_tcp_pings[i]) { stcp_client_destroy(mgr->cand_tcp_pings[i]); mgr->cand_tcp_pings[i] = NULL; } { size_t ec = queue_entry_count(mgr->entries); DEBUG_INFO(DEBUG_CATEGORY_SYS, "[CM_DESTROY] 3 entries count=%zu", ec); struct ll_entry* e = mgr->entries->head; diff --git a/src/routing_layer/conn_mgr_monitor.c b/src/routing_layer/conn_mgr_monitor.c index 32398ab3..1f05a97a 100644 --- a/src/routing_layer/conn_mgr_monitor.c +++ b/src/routing_layer/conn_mgr_monitor.c @@ -59,6 +59,14 @@ void conn_mgr_update_best_candidates(struct CONN_MGR* mgr, uint64_t node_id, uin static void cm_bg_ping_noop_cb(int s, uint16_t r, void* a, uint64_t n, const uint8_t* d, size_t l) { (void)s;(void)r;(void)a;(void)n;(void)d;(void)l; } +/* Candidate TCP-пинг: по завершении (успех/ошибка/таймаут) зануляет свой слот в + * mgr->cand_tcp_pings, чтобы conn_mgr_destroy не трогал уже освобождённый handle. */ +static void cm_cand_tcp_ping_cb(int s, uint16_t r, void* arg, uint64_t n, const uint8_t* d, size_t l) { + (void)s;(void)r;(void)n;(void)d;(void)l; + struct stcp_client** slot = (struct stcp_client**)arg; + if (slot) *slot = NULL; +} + static void cm_bg_ping_to_node(struct CONN_MGR* mgr, struct TOPO_NODE* ni) { for (const struct TOPO_ADDR4* a = ni->v4_addrs; a; a = a->next) { if (a->type != TOPO_ADDR_NAT && a->type != TOPO_ADDR_INTERFACE) continue; @@ -179,9 +187,12 @@ void cm_candidate_ping_timer_cb(void* arg) { struct TOPO_NODE* ni = topo_node_registry_find(mgr->instance->topo_groups, nq->node_id); /* пир в SLEEP-фазе — прямое соединение живо, пинг будит его: пропускаем */ if (ni && dc && dc->links && !dc->peer_sleep_phase) { - if (dc->links->is_tcp) - etcp_send_tcp_ping(mgr->instance, ni->public_key, &dc->links->remote_addr, CONN_PROBE_TIMEOUT_MS, cm_bg_ping_noop_cb, NULL); - else + if (dc->links->is_tcp) { + struct stcp_client** slot = NULL; + for (uint8_t i = 0; i < CONN_MGR_MAX_CANDIDATES; i++) + if (!mgr->cand_tcp_pings[i]) { slot = &mgr->cand_tcp_pings[i]; break; } + if (slot) etcp_send_tcp_ping(mgr->instance, ni->public_key, &dc->links->remote_addr, CONN_PROBE_TIMEOUT_MS, cm_cand_tcp_ping_cb, slot, slot); + } else etcp_send_ping(mgr->instance, ni->public_key, &dc->links->remote_addr, CONN_PROBE_TIMEOUT_MS, cm_bg_ping_noop_cb, NULL, NULL, 0); } } else route_connectivity_probe_node(mgr->instance, mgr->group, nq); diff --git a/src/routing_layer/conn_mgr_priv.h b/src/routing_layer/conn_mgr_priv.h index 44bc47f3..da9c163e 100644 --- a/src/routing_layer/conn_mgr_priv.h +++ b/src/routing_layer/conn_mgr_priv.h @@ -17,6 +17,7 @@ struct ETCP_ROUTER_CONN; struct TOPO_GROUP_NODE; struct TOPO_GROUP; struct stcp_link; +struct stcp_client; /* ═══════ константы ═══════ */ @@ -118,6 +119,7 @@ struct CONN_MGR { void *bg_ping_wait, *candidate_ping_wait; /* standby_wait handle (Android) */ size_t bg_ping_cursor; uint64_t bg_ping_cycle_start_tb; struct CONN_MGR_CANDIDATE best_candidates[CONN_MGR_MAX_CANDIDATES]; uint8_t best_candidate_count; + struct stcp_client* cand_tcp_pings[CONN_MGR_MAX_CANDIDATES]; /* влётные candidate TCP-пинги (для отмены при destroy) */ uint32_t next_request_id, direct_timeout_ms; uint8_t initialized; struct cm_reverse_pending* reverse_pending; struct cm_exchange_pending* exchange_pending; diff --git a/src/routing_layer/route_connectivity.c b/src/routing_layer/route_connectivity.c index 58346fc2..4ecc566a 100644 --- a/src/routing_layer/route_connectivity.c +++ b/src/routing_layer/route_connectivity.c @@ -13,6 +13,7 @@ #include "utun_instance.h" #include "etcp.h" #include "etcp_connections.h" +#include "stcp_client.h" #include "topo_node.h" #include "topo_group.h" #include "route_connectivity.h" @@ -51,6 +52,7 @@ struct conn_probe_ctx { uint16_t min_rtt; // min RTT в текущей серии uint16_t timeout_ms; void* ping_timer; + struct stcp_client* tcp_cli; // хендл влётного TCP-пинга (для отмены), NULL для UDP }; // ---- forward ---- @@ -145,7 +147,7 @@ static int conn_match_candidate_sockets(struct UTUN_INSTANCE* instance, static int conn_probe_send(struct conn_probe_ctx* ctx) { if (ctx->is_tcp) { return etcp_send_tcp_ping(ctx->instance, ctx->peer_pubkey, &ctx->target_addr, - ctx->timeout_ms, conn_probe_single_cb, ctx); + ctx->timeout_ms, conn_probe_single_cb, ctx, &ctx->tcp_cli); } struct ETCP_SOCKET* sock = ctx->candidate_sockets[ctx->candidate_index]; return etcp_send_ping_to_socket(ctx->instance, sock, ctx->peer_pubkey, @@ -395,8 +397,11 @@ void route_connectivity_probe_node(struct UTUN_INSTANCE* instance, struct TOPO_G } } -void route_connectivity_cancel_node(struct UTUN_INSTANCE* instance, struct TOPO_GROUP_NODE* nq) { - if (!instance || !nq) return; +/* Отсоединяет все влётные пробы узла: зануляет ctx->nq (чтобы conn_probe_finish не + * трогал освобождаемый nq) и досрочно закрывает влётные TCP-пинги. UDP-пробы не имеют + * хендла — они отсоединяются и освобождаются своим штатным ping_timeout/PONG. */ +static void conn_probe_cancel_nq(struct TOPO_GROUP_NODE* nq) { + if (!nq) return; #ifdef UTUN_HAVE_STANDBY { struct route_probe_defer* d = (struct route_probe_defer*)nq->connectivity.probe_deferred_wait; if (d) { @@ -409,31 +414,36 @@ void route_connectivity_cancel_node(struct UTUN_INSTANCE* instance, struct TOPO_ nq->connectivity.probe_status = PROBE_STATUS_NONE; nq->connectivity.pending_count = 0; struct conn_probe_ctx* ctx = (struct conn_probe_ctx*)nq->connectivity.probe_list; - while (ctx) { ctx->nq = NULL; ctx = ctx->next; } nq->connectivity.probe_list = NULL; + while (ctx) { + struct conn_probe_ctx* next = ctx->next; + ctx->nq = NULL; + struct stcp_client* cli = ctx->tcp_cli; + if (cli) stcp_client_destroy(cli); // синхронно: ping_close_cb → conn_probe_finish → u_free(ctx) + ctx = next; + } +} + +void route_connectivity_cancel_node(struct TOPO_GROUP_NODE* nq) { + if (!nq) return; + conn_probe_cancel_nq(nq); DEBUG_INFO(DEBUG_CATEGORY_BGP, "connectivity probe cancelled for node %016llx", (unsigned long long)nq->node_id); } void route_connectivity_cancel_all(struct UTUN_INSTANCE* instance) { - struct TOPO_GROUP* group = topo_groups_get_default(instance->topo_groups); - if (!instance || !group || !group->nodes) return; + if (!instance || !instance->topo_groups || !instance->topo_groups->group_list) return; int count = 0; - struct ll_entry* e = group->nodes->head; - while (e) { - struct TOPO_GROUP_NODE* nq = (struct TOPO_GROUP_NODE*)e; -#ifdef UTUN_HAVE_STANDBY - { struct route_probe_defer* d = (struct route_probe_defer*)nq->connectivity.probe_deferred_wait; - if (d) { - if (d->wait_handle) standby_wait_cancel(d->wait_handle); - u_free(d); - nq->connectivity.probe_deferred_wait = NULL; - } + struct ll_entry* ge = instance->topo_groups->group_list->head; + while (ge) { + struct TOPO_GROUP* group = (struct TOPO_GROUP*)ge; + struct ll_entry* e = group->nodes ? group->nodes->head : NULL; + while (e) { + struct TOPO_GROUP_NODE* nq = (struct TOPO_GROUP_NODE*)e; + conn_probe_cancel_nq(nq); + e = e->next; + count++; } -#endif - nq->connectivity.probe_status = PROBE_STATUS_NONE; - nq->connectivity.pending_count = 0; - e = e->next; - count++; + ge = ge->next; } DEBUG_INFO(DEBUG_CATEGORY_BGP, "probe cancel ALL: %d nodes", count); } diff --git a/src/routing_layer/route_connectivity.h b/src/routing_layer/route_connectivity.h index 8a002924..4dd18313 100644 --- a/src/routing_layer/route_connectivity.h +++ b/src/routing_layer/route_connectivity.h @@ -17,8 +17,7 @@ void route_connectivity_probe_node(struct UTUN_INSTANCE* instance, struct TOPO_G struct TOPO_GROUP_NODE* nq); // Отменяет все pending пробы для узла (при удалении / withdraw) -void route_connectivity_cancel_node(struct UTUN_INSTANCE* instance, - struct TOPO_GROUP_NODE* nq); +void route_connectivity_cancel_node(struct TOPO_GROUP_NODE* nq); // Отменяет все pending пробы для всех узлов (при destroy) void route_connectivity_cancel_all(struct UTUN_INSTANCE* instance); diff --git a/src/routing_layer/topo_group.c b/src/routing_layer/topo_group.c index e6821220..a9d901f2 100644 --- a/src/routing_layer/topo_group.c +++ b/src/routing_layer/topo_group.c @@ -392,13 +392,12 @@ static void topo_group_destroy(struct TOPO_GROUP* group) { DEBUG_INFO(DEBUG_CATEGORY_SYS, "[GRP_DESTROY] 4 conn_mgr done"); if (group->nodes) { - struct ll_entry* e; int nc = 0; - while ((e = queue_data_get(group->nodes)) != NULL) { - struct TOPO_GROUP_NODE* nq = (struct TOPO_GROUP_NODE*)e; - route_connectivity_cancel_node(group->instance, nq); - if (nq->paths) { queue_free(nq->paths); nq->paths = NULL; } - topo_nodeq_free_group_fields(group->instance->topo_groups, nq); - queue_entry_free(e); nc++; + int nc = 0; + struct ll_entry* e = group->nodes->head; + while (e) { + struct ll_entry* next = e->next; + topo_nodeq_remove_node(group, (struct TOPO_GROUP_NODE*)e); + e = next; nc++; } queue_free(group->nodes); if (nc) DEBUG_INFO(DEBUG_CATEGORY_SYS, "[GRP_DESTROY] 4b nodes cleaned (%d)", nc); @@ -766,11 +765,7 @@ void topo_group_remove_conn(struct TOPO_GROUP* group, struct ETCP_CONN* conn) { topo_fire_nodeinfo_cbk(conn->instance, group, nq); if (group->group_type != TOPO_GROUP_TYPE_CHAT && rt) route_delete(rt, nq); if (conn->instance && conn->instance->control_srv) control_server_notify_node_removed(conn->instance->control_srv, key); - route_connectivity_cancel_node(conn->instance, nq); - if (nq->paths) { queue_free(nq->paths); nq->paths = NULL; } - topo_nodeq_free_group_fields(group->instance->topo_groups, nq); - struct ll_entry* entry = node_entry; - if (entry) { queue_remove_data(group->nodes, entry); queue_entry_free(entry); } + topo_nodeq_remove_node(group, nq); nodes_removed++; DEBUG_INFO(DEBUG_CATEGORY_BGP, "node removed from group: node=%016llx grp=%016llx type=%u ch=%s (link_down)", (unsigned long long)key, (unsigned long long)group->group_id, group->group_type, group->channel_id); @@ -954,7 +949,6 @@ int topo_group_process_nodeinfo(struct TOPO_GROUP* group, struct ETCP_CONN* from struct ll_queue* paths = NULL; int is_new_node = 0; if (nodeinfo1) { - topo_nodeq_free_group_fields(group->instance->topo_groups, nodeinfo1); paths = nodeinfo1->paths; nodeinfo1->paths = NULL; } else { is_new_node = 1; } @@ -966,7 +960,7 @@ int topo_group_process_nodeinfo(struct TOPO_GROUP* group, struct ETCP_CONN* from uint16_t incoming_cumulative_rtt = 0; if (topo_node_deserialize(group, ser_data, ser_len, &new_ni, &new_subnets, &new_hop_list, &new_hop_count, &incoming_cumulative_rtt) != 0) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "NODEINFO deserialize failed from %s nid=%016llx", from->log_name, (unsigned long long)node_id); - if (nodeinfo1) { queue_remove_data(group->nodes, &nodeinfo1->ll); queue_free(paths); queue_entry_free(&nodeinfo1->ll); } + if (nodeinfo1) { topo_nodeq_remove_node(group, nodeinfo1); queue_free(paths); } return -1; } { int v4c = topo_list_count((struct _topo_head*)new_ni->v4_addrs); @@ -980,7 +974,7 @@ int topo_group_process_nodeinfo(struct TOPO_GROUP* group, struct ETCP_CONN* from (unsigned long long)node_id, from->log_name); topo_node_destroy(group->instance->topo_groups, new_ni); u_free(new_subnets); u_free(new_hop_list); - if (nodeinfo1) { queue_remove_data(group->nodes, &nodeinfo1->ll); queue_free(paths); queue_entry_free(&nodeinfo1->ll); } + if (nodeinfo1) { topo_nodeq_remove_node(group, nodeinfo1); queue_free(paths); } return -1; } { @@ -996,7 +990,7 @@ int topo_group_process_nodeinfo(struct TOPO_GROUP* group, struct ETCP_CONN* from (unsigned long long)node_id, ekchk, sig_len, from->log_name); topo_node_destroy(group->instance->topo_groups, new_ni); u_free(new_subnets); u_free(new_hop_list); - if (nodeinfo1) { queue_remove_data(group->nodes, &nodeinfo1->ll); queue_free(paths); queue_entry_free(&nodeinfo1->ll); } + if (nodeinfo1) { topo_nodeq_remove_node(group, nodeinfo1); queue_free(paths); } return -1; } DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "NODEINFO x25519_self_sig OK node=%016llx ed_pubkey=%016llx... sig_len=%d from=%s", @@ -1005,6 +999,7 @@ int topo_group_process_nodeinfo(struct TOPO_GROUP* group, struct ETCP_CONN* from } if (nodeinfo1) { + topo_nodeq_free_group_fields(group->instance->topo_groups, nodeinfo1); { struct TOPO_NODE* stored = topo_node_registry_store(group->instance->topo_groups, new_ni); if (stored != new_ni) new_ni = stored; } nodeinfo1->node_id = new_ni ? new_ni->node_id : 0; @@ -1118,11 +1113,7 @@ int topo_group_process_withdraw(struct TOPO_GROUP* group, struct ETCP_CONN* send topo_fire_nodeinfo_cbk(group->instance, group, nq); if (group->group_type != TOPO_GROUP_TYPE_CHAT && group->instance && group->instance->rt) route_delete(group->instance->rt, nq); if (group->instance->control_srv) control_server_notify_node_removed(group->instance->control_srv, node_id); - route_connectivity_cancel_node(group->instance, nq); - if (nq->paths) { queue_free(nq->paths); nq->paths = NULL; } - topo_nodeq_free_group_fields(group->instance->topo_groups, nq); - struct ll_entry* entry = queue_find_data_by_index(group->nodes, &node_id); - if (entry) { queue_remove_data(group->nodes, entry); queue_entry_free(entry); } + topo_nodeq_remove_node(group, nq); DEBUG_INFO(DEBUG_CATEGORY_BGP, "node removed from group: node=%016llx grp=%016llx type=%u ch=%s (withdraw)", (unsigned long long)node_id, (unsigned long long)group->group_id, group->group_type, group->channel_id); topo_group_broadcast_withdraw(group, node_id, wd_source, sender); diff --git a/src/routing_layer/topo_group_invite.c b/src/routing_layer/topo_group_invite.c index 8b693231..f6e5770c 100644 --- a/src/routing_layer/topo_group_invite.c +++ b/src/routing_layer/topo_group_invite.c @@ -404,7 +404,7 @@ static void tgi_cancel(struct tgi_pending* p) { if (p->ncd_handle) { node_conn_direct_force_close(p->ncd_handle); p->ncd_handle = NULL; } if (p->temp_nq) { struct TOPO_GROUP* g = topo_groups_find(p->inst->topo_groups, p->group_id); - if (g) { queue_remove_data(g->nodes, &p->temp_nq->ll); topo_nodeq_free_group_fields(p->inst->topo_groups, p->temp_nq); queue_entry_free(&p->temp_nq->ll); } + if (g) topo_nodeq_remove_node(g, p->temp_nq); p->temp_nq = NULL; } tgi_unlink(p); diff --git a/src/routing_layer/topo_node.c b/src/routing_layer/topo_node.c index 432f4136..15054d00 100644 --- a/src/routing_layer/topo_node.c +++ b/src/routing_layer/topo_node.c @@ -11,6 +11,7 @@ #include "config_parser.h" #include "topo_node.h" #include "topo_group.h" +#include "route_connectivity.h" #include "route_lib.h" #include "etcp_debug.h" #include "topo_node_sqlite.h" @@ -186,6 +187,17 @@ void topo_nodeq_free_group_fields(struct TOPO_GROUPS* groups, struct TOPO_GROUP_ nq->handle = NULL; } +/* Полное удаление узла из группы: отмена влётных проб, освобождение paths/subnets, + * извлечение из group->nodes и освобождение самого nq. Единственный корректный способ удаления. */ +void topo_nodeq_remove_node(struct TOPO_GROUP* group, struct TOPO_GROUP_NODE* nq) { + if (!group || !nq) return; + route_connectivity_cancel_node(nq); + if (nq->paths) { queue_free(nq->paths); nq->paths = NULL; } + queue_remove_data(group->nodes, &nq->ll); + topo_nodeq_free_group_fields(group->instance ? group->instance->topo_groups : NULL, nq); + queue_entry_free(&nq->ll); +} + // ===== глобальный реестр TOPO_NODE ===== /* Ищет TOPO_NODE в глобальном реестре по node_id (NULL, если узла нет). */ diff --git a/src/routing_layer/topo_node.h b/src/routing_layer/topo_node.h index e6bb246f..9cf4ae9e 100644 --- a/src/routing_layer/topo_node.h +++ b/src/routing_layer/topo_node.h @@ -226,6 +226,9 @@ void topo_node_destroy(struct TOPO_GROUPS* groups, struct TOPO_NODE* ni); // API — per-group struct TOPO_GROUP_NODE* topo_node_find_by_id(struct TOPO_GROUP* group, uint64_t node_id); void topo_nodeq_free_group_fields(struct TOPO_GROUPS* groups, struct TOPO_GROUP_NODE* nq); +/* Полное удаление узла из группы: отменяет влётные пробы, освобождает paths/subnets, + * убирает из group->nodes и освобождает сам nq. Единственный корректный способ удаления узла. */ +void topo_nodeq_remove_node(struct TOPO_GROUP* group, struct TOPO_GROUP_NODE* nq); int topo_node_dyn_size(const struct TOPOMSG_NODE* msg); int topo_node_serialize(struct TOPO_NODE* ni, struct TOPO_GROUP_NODE* nq, diff --git a/src/transport_layer/etcp_connections.c b/src/transport_layer/etcp_connections.c index 9c0a5c20..8aa6a019 100644 --- a/src/transport_layer/etcp_connections.c +++ b/src/transport_layer/etcp_connections.c @@ -1597,12 +1597,14 @@ static void tcp_ping_cb_adapter(int success, uint16_t rtt, void* arg) { // TCP-пинг: STCP-хендшейк с пиром и замер RTT по handshake (через stcp_ping_send). int etcp_send_tcp_ping(struct UTUN_INSTANCE* instance, const uint8_t* peer_pubkey_bin, const struct sockaddr_storage* addr, int timeout_ms, - etcp_ping_callback_t cb, void* user_arg) { + etcp_ping_callback_t cb, void* user_arg, + struct stcp_client** out_cli) { if (!instance || !peer_pubkey_bin || !addr || timeout_ms <= 0 || !cb) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "bad args: inst=%p pubkey=%p addr=%p timeout=%d cb=%p", (void*)instance, (void*)peer_pubkey_bin, (void*)addr, timeout_ms, (void*)(uintptr_t)cb); return -1; } + if (out_cli) *out_cli = NULL; char addr_str[INET6_ADDRSTRLEN]; uint16_t port; if (addr->ss_family == AF_INET) { @@ -1637,6 +1639,7 @@ int etcp_send_tcp_ping(struct UTUN_INSTANCE* instance, const uint8_t* peer_pubke instance->my_ed25519_pubkey, instance->client_type, instance->keepalive_interval, timeout_ms, tcp_ping_cb_adapter, a, socks); if (!cli) { u_free(a); return -4; } + if (out_cli) *out_cli = cli; DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "tcp ping to %s:%u timeout=%d", addr_str, (unsigned)port, timeout_ms); return 0; } diff --git a/src/transport_layer/etcp_connections.h b/src/transport_layer/etcp_connections.h index 72f6518d..eff7b7c0 100644 --- a/src/transport_layer/etcp_connections.h +++ b/src/transport_layer/etcp_connections.h @@ -372,6 +372,7 @@ void tcp_socket_remove(struct ETCP_SOCKET* sock); int etcp_create_server_socket(struct UTUN_INSTANCE* instance, struct CFG_SERVER* server, struct ETCP_SOCKET** out_sock); struct stcp_link; +struct stcp_client; void tcp_server_on_link(struct stcp_link *link, struct ETCP_SOCKET *tcp_sock); // connection functions @@ -422,9 +423,11 @@ int etcp_send_ping_to_socket(struct UTUN_INSTANCE* instance, struct ETCP_SOCKET* // TCP-ping: подключение к TCP-адресу peer + хендшейк с флагом PING. RTT меряется // по хендшейку (send→response). cb вызывается один раз (success=1/rtt или success=0). +// out_cli (опционально) — возвращает хендл stcp_client для досрочной отмены (stcp_client_destroy). int etcp_send_tcp_ping(struct UTUN_INSTANCE* instance, const uint8_t* peer_pubkey_bin, const struct sockaddr_storage* addr, int timeout_ms, - etcp_ping_callback_t cb, void* user_arg); + etcp_ping_callback_t cb, void* user_arg, + struct stcp_client** out_cli); void etcp_connections_read_callback_socket(socket_t sock, void* arg); diff --git a/tests/test_stcp_ping.c b/tests/test_stcp_ping.c index f768691c..51015a37 100644 --- a/tests/test_stcp_ping.c +++ b/tests/test_stcp_ping.c @@ -69,7 +69,7 @@ int main(void) { struct sockaddr_in *sin = (struct sockaddr_in *)&addr; sin->sin_family = AF_INET; sin->sin_addr.s_addr = htonl(0x7f000001); sin->sin_port = htons(port); - int rc = etcp_send_tcp_ping(cli, srv_pub, &addr, 2000, ping_cb, NULL); + int rc = etcp_send_tcp_ping(cli, srv_pub, &addr, 2000, ping_cb, NULL, NULL); if (rc != 0) { DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "etcp_send_tcp_ping failed rc=%d", rc); return 1; } int ticks = 0; @@ -82,7 +82,7 @@ int main(void) { struct sockaddr_storage bad_addr; memset(&bad_addr, 0, sizeof(bad_addr)); struct sockaddr_in *bsin = (struct sockaddr_in *)&bad_addr; bsin->sin_family = AF_INET; bsin->sin_addr.s_addr = htonl(0x7f000001); bsin->sin_port = htons(port + 5000); - rc = etcp_send_tcp_ping(cli, srv_pub, &bad_addr, 1000, ping_cb, NULL); + rc = etcp_send_tcp_ping(cli, srv_pub, &bad_addr, 1000, ping_cb, NULL, NULL); ticks = 0; while (ping_ok < 0 && ticks < 3000) { uasync_poll(ua, 10); ticks++; } if (ping_ok != 0) { DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "=== FAILED (failure path): success=%d rtt=%u rc=%d ===", ping_ok, ping_rtt, rc); return 1; }