@ -73,6 +73,11 @@ struct cm_slot_ctx {
int slot_idx ;
} ;
struct cm_node_changed_ctx {
struct chat_conn_mgr * mgr ;
uint64_t node_id ;
} ;
struct cm_node {
uint64_t node_id ;
uint8_t pubkey [ SC_PUBKEY_SIZE ] ;
@ -114,6 +119,8 @@ static void cm_ping_timer_cb(void* arg);
static void cm_ping_result_cb ( int success , uint16_t rtt , void * arg , uint64_t nonce , const uint8_t * resp_data , size_t resp_data_len ) ;
static struct ETCP_SOCKET * cm_best_socket ( struct chat_conn_mgr * mgr ) ;
static void cm_invite_result_cb ( int result , uint64_t node_id , void * arg ) ;
static void cm_node_changed_cb ( struct ETCP_CONN * conn , void * arg ) ;
static void cm_node_changed_deferred ( void * arg ) ;
/* ─── init / destroy ─── */
@ -309,6 +316,7 @@ static void cm_rotation(struct cm_node* node) {
if ( ! ctx ) { etcp_connection_close ( conn ) ; slot - > state = CM_SLOT_FREE ; return ; }
ctx - > node = node ; ctx - > slot_idx = ( int ) ( slot - node - > slots ) ;
etcp_conn_add_init_cbk ( conn , cm_init_cb , ctx ) ;
etcp_conn_add_node_changed_cbk ( conn , cm_node_changed_cb , mgr ) ;
if ( ! etcp_link_new ( conn , sock , & node - > addrs [ idx ] . sa , 0 ) ) {
DEBUG_ERROR ( DEBUG_CATEGORY_CONNECTIVITY , " %s: link_new failed node=0x%016llx idx=%d " , CM_ID , ( unsigned long long ) node - > node_id , idx ) ;
etcp_connection_close ( conn ) ; u_free ( ctx ) ; slot - > state = CM_SLOT_DEAD ;
@ -332,6 +340,7 @@ static void cm_rotation(struct cm_node* node) {
if ( ! ctx ) { etcp_connection_close ( conn ) ; slot - > state = CM_SLOT_FREE ; return ; }
ctx - > node = node ; ctx - > slot_idx = ( int ) ( slot - node - > slots ) ;
etcp_conn_add_init_cbk ( conn , cm_init_cb , ctx ) ;
etcp_conn_add_node_changed_cbk ( conn , cm_node_changed_cb , mgr ) ;
if ( ! etcp_link_new ( conn , sock , & node - > addrs [ slot - > addr_idx ] . sa , 0 ) ) {
DEBUG_ERROR ( DEBUG_CATEGORY_CONNECTIVITY , " %s: link_new(retry) failed node=0x%016llx idx=%d " , CM_ID , ( unsigned long long ) node - > node_id , slot - > addr_idx ) ;
etcp_connection_close ( conn ) ; u_free ( ctx ) ; slot - > state = CM_SLOT_DEAD ;
@ -438,6 +447,60 @@ static void cm_retry_cb(void* arg) {
node - > retry_timer = uasync_set_timeout ( node - > mgr - > inst - > ua , CM_RETRY_TB , node , cm_retry_cb , " cm_retry " ) ;
}
/* ─── node_changed ─── */
static void cm_node_changed_deferred ( void * arg ) {
struct cm_node_changed_ctx * ctx = ( struct cm_node_changed_ctx * ) arg ;
struct cm_node * node = cm_find_node ( ctx - > mgr , ctx - > node_id ) ;
if ( node ) {
for ( int i = 0 ; i < CM_MAX_PARALLEL ; i + + ) {
struct cm_slot * slot = & node - > slots [ i ] ;
if ( slot - > conn ) { etcp_connection_close ( slot - > conn ) ; slot - > conn = NULL ; }
slot - > state = CM_SLOT_FREE ; slot - > addr_idx = 255 ;
}
cm_deliver_all ( node , CC_ERR_INTERNAL ) ;
node - > delivered = 0 ;
DEBUG_INFO ( DEBUG_CATEGORY_CONNECTIVITY , " %s: NODE_CHANGED deferred close done node=0x%016llx " ,
CM_ID , ( unsigned long long ) ctx - > node_id ) ;
}
u_free ( ctx ) ;
}
static void cm_node_changed_cb ( struct ETCP_CONN * conn , void * arg ) {
struct chat_conn_mgr * mgr = ( struct chat_conn_mgr * ) arg ;
if ( ! mgr | | ! conn ) return ;
uint64_t node_id = conn - > peer_node_id ;
struct cm_node * node = cm_find_node ( mgr , node_id ) ;
if ( ! node ) return ;
DEBUG_INFO ( DEBUG_CATEGORY_CONNECTIVITY , " %s: NODE_CHANGED node=0x%016llx — clearing addresses and closing connections " ,
CM_ID , ( unsigned long long ) node_id ) ;
sqlite3_stmt * st = NULL ;
if ( sqlite3_prepare_v2 ( mgr - > db , " DELETE FROM node_addresses WHERE node_id=? " , - 1 , & st , NULL ) = = SQLITE_OK ) {
sqlite3_bind_int64 ( st , 1 , ( sqlite3_int64 ) node_id ) ;
sqlite3_step ( st ) ; sqlite3_finalize ( st ) ;
}
node - > addr_loaded = 0 ;
node - > addr_count = 0 ;
if ( node - > retry_timer ) { uasync_cancel_timeout ( mgr - > inst - > ua , node - > retry_timer ) ; node - > retry_timer = NULL ; }
for ( int i = 0 ; i < CM_MAX_PARALLEL ; i + + ) {
struct cm_slot * slot = & node - > slots [ i ] ;
if ( slot - > init_timer ) { uasync_cancel_timeout ( mgr - > inst - > ua , slot - > init_timer ) ; slot - > init_timer = NULL ; }
if ( slot - > conn & & slot - > conn ! = conn ) { etcp_connection_close ( slot - > conn ) ; slot - > conn = NULL ; }
if ( slot - > conn ! = conn ) { slot - > state = CM_SLOT_FREE ; slot - > addr_idx = 255 ; }
}
struct cm_node_changed_ctx * ctx = u_calloc ( 1 , sizeof ( * ctx ) ) ;
if ( ctx ) { ctx - > mgr = mgr ; ctx - > node_id = node_id ; uasync_post ( mgr - > inst - > ua , cm_node_changed_deferred , ctx ) ; }
uint8_t evt [ 8 ] ; memcpy ( evt , & node_id , 8 ) ;
gui_bridge_post ( GUI_EVT_NODE_CHANGED , evt , 8 ) ;
}
/* ─── фоновый пинг ─── */
static void cm_ping_timer_cb ( void * arg ) {