diff --git a/src/transport_layer/node_conn_direct.c b/src/transport_layer/node_conn_direct.c index df1d7492..39ccf238 100644 --- a/src/transport_layer/node_conn_direct.c +++ b/src/transport_layer/node_conn_direct.c @@ -361,7 +361,46 @@ int node_conn_direct_compatible(struct UTUN_INSTANCE* inst, uint64_t node_id) { if (conn && conn->state != 2 && !conn->close_requested && conn->links_up) return 1; struct TOPO_NODE* node = topo_node_registry_find(inst->topo_groups, node_id); if (!node) { DEBUG_DEBUG(DEBUG_CATEGORY_NCD, "[ncd] compatibility unknown snapshot node=%016llx", (unsigned long long)node_id); return 0; } - return ncd_create_links(NULL, node, NULL, inst) > 0; + if (ncd_create_links(NULL, node, NULL, inst) > 0) return 1; + /* Входящее подключение тоже допустимо: REVERSE_REQ просит пира открыть наш опубликованный адрес. + * TCP имеет собственный исходящий socket fallback; для UDP нужен известный UDP-сокет пира. + * Это возможность попытки, не подтверждение маршрута/работоспособности скрытых сокетов пира. */ + const struct TOPO_NODE* self = topo_node_registry_find(inst->topo_groups, inst->node_id); + if (!self) return 0; + for (const struct TOPO_ADDR4* a = self->v4_addrs; a; a = a->next) { + uint32_t ip; memcpy(&ip, a->addr, 4); + if (!ip || !a->port) continue; + const struct TOPO_SOCKMETA4* meta = self->v4_sock_meta; + while (meta && meta->id != a->socket_id) meta = meta->next; + uint8_t cfg = meta ? meta->config_type : CFG_SERVER_TYPE_UNKNOWN, nat = meta ? meta->nat_type : NAT_TYPE_UNKNOWN; + if (sock_target_class(a->type, cfg, nat) != SOCK_TC_PUB) continue; + if ((a->protocol & TOPO_PROTO_REALITY) && !topo_node_find_reality_sock(self, a->socket_id)) continue; + if (a->protocol & TOPO_PROTO_TCP) return 1; + if (!(a->protocol & TOPO_PROTO_UDP)) continue; + for (const struct TOPO_ADDR4* remote = node->v4_addrs; remote; remote = remote->next) { + if (!(remote->protocol & TOPO_PROTO_UDP)) continue; + const struct TOPO_SOCKMETA4* rm = node->v4_sock_meta; + while (rm && rm->id != remote->socket_id) rm = rm->next; + struct sock_view view = {.has_inet = 1}; + view.is_eim = rm && (rm->nat_type == NAT_TYPE_EIM || rm->nat_type == NAT_VERIFIED_EIM); + for (const struct TOPO_ADDR4* ra = node->v4_addrs; ra; ra = ra->next) + if (ra->socket_id == remote->socket_id && ra->type == TOPO_ADDR_NAT) memcpy(&view.public_ip4, ra->addr, 4); + if (sock_match(&view, a->type, cfg, nat, ip)) return 1; + } + } + for (const struct TOPO_ADDR6* a = self->v6_addrs; a; a = a->next) { + int nonzero = 0; for (unsigned i = 0; i < 16; i++) nonzero |= a->addr[i]; + if (!nonzero || !a->port || (a->addr[0] == 0xfe && (a->addr[1] & 0xc0) == 0x80)) continue; + const struct TOPO_SOCKMETA6* meta = self->v6_sock_meta; + while (meta && meta->id != a->socket_id) meta = meta->next; + if (meta && (meta->config_type == CFG_SERVER_TYPE_PRIVATE || meta->config_type == CFG_SERVER_TYPE_LOCAL)) continue; + if ((a->protocol & TOPO_PROTO_REALITY) && !topo_node_find_reality_sock(self, a->socket_id)) continue; + if (a->protocol & TOPO_PROTO_TCP) return 1; + if (!(a->protocol & TOPO_PROTO_UDP)) continue; + for (const struct TOPO_ADDR6* remote = node->v6_addrs; remote; remote = remote->next) + if (remote->protocol & TOPO_PROTO_UDP) return 1; + } + return 0; } int node_conn_direct_update_node(struct UTUN_INSTANCE* inst, uint64_t node_id) { diff --git a/src/transport_layer/node_conn_direct.h b/src/transport_layer/node_conn_direct.h index 53713fa9..3cb3d79d 100644 --- a/src/transport_layer/node_conn_direct.h +++ b/src/transport_layer/node_conn_direct.h @@ -44,8 +44,9 @@ struct ETCP_SOCKET; /* ─── Opaque handle ─── */ struct NODE_CONN_DIRECT; -/* Совместимая пара адрес/сокет либо рабочий ETCP; без линков, handles, таймеров и сетевых проб. - * Использует тот же обход, что open, включая IPv6 и TCP без слушающего сокета. Не доказывает достижимость. */ +/* Рабочий ETCP, совместимая исходящая пара либо возможность reverse на наш опубликованный адрес. + * Без линков/handles/таймеров/проб. Исходящие пары — тот же обход, что open; reverse — sock_match/публикуемые адреса. + * Учитывает IPv6 и исходящий TCP fallback; возможность попытки не доказывает достижимость. */ int node_conn_direct_compatible(struct UTUN_INSTANCE* inst, uint64_t node_id); /* ─── Callback events (единый callback на handle) ─── */