@ -45,7 +45,8 @@ static err_t tcp_proxy_client_output_cb(void *arg, struct pbuf *p, uint32_t src_
static void tcp_proxy_client_feed_from_transport ( struct tcp_proxy_client_conn * pc ) ;
static struct ll_entry * tcp_proxy_client_entry_from_data ( struct memory_pool * pool , const uint8_t * data , uint16_t len ) ;
static void tcp_proxy_client_conn_free ( struct tcp_proxy_client_conn * pc ) ;
static void tcp_proxy_client_conn_finish ( struct tcp_proxy_client_conn * pc ) ;
static void tcp_proxy_client_conn_detach ( struct tcp_proxy_client_conn * pc ) ;
static void tcp_proxy_client_abort_pcb ( struct tcp_proxy_client_conn * pc , const char * why ) ;
static int tcp_proxy_client_send_msg ( struct UTUN_INSTANCE * inst , uint64_t group_id , uint64_t dst , uint8_t subcmd , uint32_t sid , const uint8_t * data , size_t len , int force ) ;
static int tcp_proxy_client_send_data ( struct tcp_proxy_client_conn * pc , const uint8_t * data , uint16_t len , int force ) ;
static void tcp_proxy_client_tx_queue_drain_cb ( struct ll_queue * q , void * arg ) ;
@ -268,8 +269,14 @@ static int tcp_proxy_client_fin_flush(struct tcp_proxy_client_conn* pc) {
pc - > flow . fin_pending = 1 ;
proxy_flow_flush ( & pc - > flow ) ;
}
// Локальная сторона (браузер) завершена — отсоединяем pcb сразу, не дожидаясь
// доставки FIN в exit (иначе таймер TIME_WAIT освободит pcb без err_cb, pc->pcb
// останется висячим и последующий tcp_abort даст double free).
if ( pc - > fin_local & & pc - > fin_remote & & ! pc - > fin_deferred & & ! pc - > to_lwip - > head ) {
tcp_proxy_client_conn_detach ( pc ) ;
}
if ( pc - > flow . fin_sent & & pc - > fin_remote & & ! pc - > fin_deferred & & ! pc - > tx_queue - > head & & ! pc - > to_lwip - > head ) {
tcp_proxy_client_conn_finish ( pc ) ;
tcp_proxy_client_conn_free ( pc ) ;
return 1 ;
}
return 0 ;
@ -422,17 +429,7 @@ static err_t tcp_proxy_client_poll_cb(void *arg, struct tcp_pcb *pcb) {
if ( pc - > error ) {
DEBUG_ERROR ( DEBUG_CATEGORY_PROXY , " PROXY CLEANUP error sid=%08x " , pc - > stream_id ) ;
if ( pc - > pcb ) {
DEBUG_DEBUG ( DEBUG_CATEGORY_DEBUG , " PROXY POLL_ABORT pcb=%p state=%s sid=%08x " ,
( void * ) pc - > pcb , tcp_debug_state_str ( pc - > pcb - > state ) , pc - > stream_id ) ;
tcp_arg ( pc - > pcb , NULL ) ;
tcp_recv ( pc - > pcb , NULL ) ;
tcp_sent ( pc - > pcb , NULL ) ;
tcp_err ( pc - > pcb , NULL ) ;
tcp_poll ( pc - > pcb , NULL , 0 ) ;
tcp_abort ( pc - > pcb ) ;
pc - > pcb = NULL ;
}
tcp_proxy_client_abort_pcb ( pc , " POLL_ABORT " ) ;
tcp_proxy_client_conn_free ( pc ) ;
return LERR_ABRT ;
}
@ -533,13 +530,11 @@ static void tcp_proxy_client_conn_free(struct tcp_proxy_client_conn *pc) {
u_free ( pc ) ;
}
// Завершить локальное TCP-соединение: отвязать коллбэки, сбросить pc->pcb
// (lwIP дальше сам освободит pcb — в т.ч. молча по истечении TIME_WAIT) и освободить conn.
// Вызывается, когда обе стороны обменялись FIN (fin_local && fin_remote) — релей завершён.
static void tcp_proxy_client_conn_finish ( struct tcp_proxy_client_conn * pc ) {
if ( ! pc ) return ;
if ( pc - > pcb ) {
DEBUG_DEBUG ( DEBUG_CATEGORY_DEBUG , " PROXY FINISH pcb=%p state=%s sid=%08x " ,
// Отвязать pcb от conn: сбросить коллбэки и pc->pcb. lwIP дальше сам освободит pcb
// (в т.ч. молча по истечении TIME_WAIT). Контракт: после вызова pc->pcb == NULL.
static void tcp_proxy_client_conn_detach ( struct tcp_proxy_client_conn * pc ) {
if ( ! pc | | ! pc - > pcb ) return ;
DEBUG_DEBUG ( DEBUG_CATEGORY_DEBUG , " PROXY DETACH pcb=%p state=%s sid=%08x " ,
( void * ) pc - > pcb , tcp_debug_state_str ( pc - > pcb - > state ) , pc - > stream_id ) ;
tcp_arg ( pc - > pcb , NULL ) ;
tcp_recv ( pc - > pcb , NULL ) ;
@ -548,7 +543,37 @@ static void tcp_proxy_client_conn_finish(struct tcp_proxy_client_conn *pc) {
tcp_poll ( pc - > pcb , NULL , 0 ) ;
pc - > pcb = NULL ;
}
tcp_proxy_client_conn_free ( pc ) ;
// Проверка, что pc->pcb ещё не освобождён пулом (защита от висячего указателя:
// таймер TIME_WAIT мог освободить pcb без err_cb). Возвращает 0 и сбрасывает
// pc->pcb, если pcb освобождён; иначе 1 (pcb валиден).
static int tcp_proxy_client_pcb_valid ( struct tcp_proxy_client_conn * pc , const char * why ) {
struct tcp_pcb * pcb = pc - > pcb ;
struct lwip_tcp_ctx * ctx = pc - > proxy ? pc - > proxy - > lwip : NULL ;
if ( ctx & & memory_pool_is_freed ( ctx - > pcb_pool , pcb ) ) {
DEBUG_WARN ( DEBUG_CATEGORY_PROXY , " PROXY %s: pcb=%p already freed (sid=%08x) — skip " ,
why , ( void * ) pcb , pc - > stream_id ) ;
pc - > pcb = NULL ;
return 0 ;
}
return 1 ;
}
// Безопасный abort с защитой от висячего pc->pcb (pcb уже освобождён пулом, например
// таймером TIME_WAIT). Если pcb освобождён — только сбрасываем ссылку, не трогая память.
static void tcp_proxy_client_abort_pcb ( struct tcp_proxy_client_conn * pc , const char * why ) {
if ( ! pc | | ! pc - > pcb ) return ;
struct tcp_pcb * pcb = pc - > pcb ;
if ( ! tcp_proxy_client_pcb_valid ( pc , why ) ) return ;
DEBUG_DEBUG ( DEBUG_CATEGORY_DEBUG , " PROXY %s pcb=%p state=%s sid=%08x " ,
why , ( void * ) pcb , tcp_debug_state_str ( pcb - > state ) , pc - > stream_id ) ;
tcp_arg ( pcb , NULL ) ;
tcp_recv ( pcb , NULL ) ;
tcp_sent ( pcb , NULL ) ;
tcp_err ( pcb , NULL ) ;
tcp_poll ( pcb , NULL , 0 ) ;
tcp_abort ( pcb ) ;
pc - > pcb = NULL ;
}
// ====================================================================
@ -575,7 +600,7 @@ static void tcp_proxy_client_handle_data(struct tcp_proxy_client* p, struct ETCP
if ( e ) { queue_dgram_free ( e ) ; queue_entry_free ( e ) ; }
DEBUG_ERROR ( DEBUG_CATEGORY_PROXY , " proxy: receive failed sid=%08x " , stream_id ) ;
tcp_proxy_client_send_error ( pc ) ;
if ( pc - > pcb ) { DEBUG_DEBUG ( DEBUG_CATEGORY_DEBUG , " PROXY DATA_ABORT pcb=%p state=%s sid=%08x " , ( void * ) pc - > pcb , tcp_debug_state_str ( pc - > pcb - > state ) , stream_id ) ; tcp_arg ( pc - > pcb , NULL ) ; tcp_err ( pc - > pcb , NULL ) ; tcp_abort ( pc - > pcb ) ; pc - > pcb = NULL ; }
tcp_proxy_client_abort_pcb ( pc , " DATA_ABORT " ) ;
tcp_proxy_client_conn_free ( pc ) ;
} else {
queue_data_put ( pc - > to_lwip , e ) ;
@ -588,9 +613,9 @@ static void tcp_proxy_client_handle_data(struct tcp_proxy_client* p, struct ETCP
static void tcp_proxy_client_handle_close ( struct tcp_proxy_client * p , uint32_t stream_id ) {
struct tcp_proxy_client_conn * pc = tcp_proxy_client_find_conn ( p , stream_id ) ;
if ( ! pc ) { DEBUG_WARN ( DEBUG_CATEGORY_PROXY , " PROXY CLOSE sid=%08x — no conn, dropping " , stream_id ) ; return ; }
DEBUG_INFO ( DEBUG_CATEGORY_PROXY , " PROXY REM_CLOSED sid=%08x fin_local=%d pcb_state=%u " , stream_id , pc - > fin_local , pc - > pcb ? pc - > pcb - > state : 0 ) ;
DEBUG_INFO ( DEBUG_CATEGORY_PROXY , " PROXY REM_CLOSED sid=%08x fin_local=%d pcb=%p " , stream_id , pc - > fin_local , ( void * ) pc - > pcb ) ;
pc - > rem_closed = 1 ;
if ( pc - > pcb ) {
if ( pc - > pcb & & tcp_proxy_client_pcb_valid ( pc , " CLOSE " ) ) {
DEBUG_DEBUG ( DEBUG_CATEGORY_DEBUG , " PROXY CLOSE pcb=%p state=%s sid=%08x " ,
( void * ) pc - > pcb , tcp_debug_state_str ( pc - > pcb - > state ) , stream_id ) ;
tcp_arg ( pc - > pcb , NULL ) ;
@ -608,17 +633,7 @@ static void tcp_proxy_client_handle_error(struct tcp_proxy_client* p, uint32_t s
struct tcp_proxy_client_conn * pc = tcp_proxy_client_find_conn ( p , stream_id ) ;
if ( ! pc ) { DEBUG_INFO ( DEBUG_CATEGORY_PROXY , " PROXY ERROR sid=%08x — no conn, dropping " , stream_id ) ; return ; }
DEBUG_ERROR ( DEBUG_CATEGORY_PROXY , " PROXY ERROR from exit sid=%08x fin_local=%d " , stream_id , pc - > fin_local ) ;
if ( pc - > pcb ) {
DEBUG_DEBUG ( DEBUG_CATEGORY_DEBUG , " PROXY ERROR pcb=%p state=%s sid=%08x " ,
( void * ) pc - > pcb , tcp_debug_state_str ( pc - > pcb - > state ) , stream_id ) ;
tcp_arg ( pc - > pcb , NULL ) ;
tcp_recv ( pc - > pcb , NULL ) ;
tcp_sent ( pc - > pcb , NULL ) ;
tcp_err ( pc - > pcb , NULL ) ;
tcp_poll ( pc - > pcb , NULL , 0 ) ;
tcp_abort ( pc - > pcb ) ;
pc - > pcb = NULL ;
}
tcp_proxy_client_abort_pcb ( pc , " ERROR " ) ;
pc - > rem_closed = 1 ;
tcp_proxy_client_conn_free ( pc ) ;
}
@ -664,17 +679,7 @@ void tcp_proxy_client_router_recv_cb(struct ETCP_CONN* conn, struct ll_entry* en
struct tcp_proxy_client_conn * pc , * next ;
for ( pc = proxy - > conns ; pc ; pc = next ) {
next = pc - > next ;
if ( pc - > pcb ) {
DEBUG_DEBUG ( DEBUG_CATEGORY_DEBUG , " PROXY CLOSE_ALL pcb=%p state=%s sid=%08x " ,
( void * ) pc - > pcb , tcp_debug_state_str ( pc - > pcb - > state ) , pc - > stream_id ) ;
tcp_arg ( pc - > pcb , NULL ) ;
tcp_recv ( pc - > pcb , NULL ) ;
tcp_sent ( pc - > pcb , NULL ) ;
tcp_err ( pc - > pcb , NULL ) ;
tcp_poll ( pc - > pcb , NULL , 0 ) ;
tcp_abort ( pc - > pcb ) ;
pc - > pcb = NULL ;
}
tcp_proxy_client_abort_pcb ( pc , " CLOSE_ALL " ) ;
tcp_proxy_client_conn_free ( pc ) ;
}
proxy - > conns = NULL ; proxy - > conn_count = 0 ;