// socks_proxy.h — SOCKS5 / HTTP CONNECT proxy (client side) #ifndef SOCKS_PROXY_H #define SOCKS_PROXY_H #ifdef __cplusplus extern "C" { #endif #include #include "proxy_protocol.h" #include "../lib/socket_compat.h" #include "../lib/ll_queue.h" struct UASYNC; struct UTUN_INSTANCE; struct ll_entry; struct ll_queue; struct tcp_conn; struct adns_query; enum { SOCKS_STATE_GREETING = 0, // ждём SOCKS5 greeting (ver + methods) SOCKS_STATE_REQUEST, // ждём SOCKS5 CONNECT request (== HTTP_STATE_REQUEST) SOCKS_STATE_CONNECTING, // DNS в процессе (== HTTP_STATE_CONNECTING) SOCKS_STATE_RELAY // релей данных (== HTTP_STATE_RELAY) }; // HTTP-состояния совпадают по значениям с SOCKS-состояниями (общая state-машина // в on_read_cb), поэтому явно привязаны к SOCKS_STATE_* — иначе возникает коллизия // SOCKS_STATE_CONNECTING(2) == HTTP_STATE_RELAY(2) и RELAY-данные дропаются. enum { HTTP_STATE_REQUEST = SOCKS_STATE_REQUEST, // ждём первую строку HTTP-запроса HTTP_STATE_CONNECTING = SOCKS_STATE_CONNECTING, // DNS в процессе HTTP_STATE_RELAY = SOCKS_STATE_RELAY // релей данных }; struct socks_proxy_conn { struct socks_proxy_conn* next; struct socks_proxy_conn** head; // указатель на голову списка (для удаления) int* count; // указатель на счётчик списка struct tcp_conn* tc; struct UASYNC* ua; struct UTUN_INSTANCE* inst; uint64_t via_node_id; struct proxy_flow flow; uint32_t stream_id; uint8_t dest_ip[4]; uint16_t dest_port; uint8_t fin_remote; uint8_t rem_closed; uint8_t close_sent; uint8_t close_pending; uint8_t is_http; uint8_t state; uint8_t freed; uint8_t close_queued; void* free_soon_id; // handle отложенного освобождения (uasync_call_soon) uint8_t buf[16384]; uint16_t buf_len; uint8_t* tx_buf; // буфер при backpressure (retry в tx_waiter_cb) uint16_t tx_len; uint32_t bytes_to_client; // байт записано клиенту (curl) через write_queue uint32_t bytes_from_exit; // байт получено от exit node через ETCP DATA struct queue_waiter_handle tx_waiter; void* tx_retry_timer; // safety-net таймер backpressure (force=1) uint8_t fin_deferred; // FIN отложен до сброса tx_buf struct adns_query* dns_q; // активный DNS-запрос (NULL когда нет) uint8_t dns_kind; // DNSK_SOCKS / DNSK_HTTP_CONNECT / DNSK_HTTP_PROXY uint8_t* dns_pending; // HTTP proxy: накопленный запрос (headers+body) до DNS uint16_t dns_pending_len; }; struct listen_ctx; // Создать слушающий сокет для SOCKS/HTTP proxy. addr_str = "IP:PORT". // listen_ctx должен быть освобождён через socks_proxy_close_listen. // Возвращает listen_ctx при успехе, NULL при ошибке. struct listen_ctx* socks_proxy_init_listen(struct UASYNC* ua, const char* addr_str, struct socks_proxy_conn** conns, int* count, uint32_t* next_stream_id, struct UTUN_INSTANCE* inst, uint64_t via_node_id, int is_http); // Закрыть слушающий сокет и освободить listen_ctx. // sock_out получает значение сокета (для последующего close). void socks_proxy_close_listen(struct UASYNC* ua, struct listen_ctx* ctx, socket_t* sock_out); // Найти соединение по stream_id struct socks_proxy_conn* socks_proxy_find_conn(struct socks_proxy_conn* head, uint32_t stream_id); // Обработать входящее ETCP сообщение (DATA/CLOSE/ERROR/FIN) // Возвращает 1 если обработано, 0 если stream_id не найден int socks_proxy_handle_etcp(struct socks_proxy_conn** head, int* count, uint32_t stream_id, uint8_t subcmd, const uint8_t* data, size_t data_len); // Освободить одно соединение (удаляет из списка) void socks_proxy_conn_free(struct socks_proxy_conn* c); // Освободить все соединения в списке void socks_proxy_conn_free_all(struct socks_proxy_conn** head, int* count); #ifdef __cplusplus } #endif #endif // SOCKS_PROXY_H