socks_proxy (client side): - tx_buf buffering on send_data failure in RELAY path, retry in tx_waiter_cb - process_http_request: version_str null-term fix (memcpy(vlen)+explicit \0) - process_http_request: assemble hdr+body+extra into heap pkt, atomic send - socks_proxy_handle_etcp: alloc failure logs write_queue state + byte counters tcp_proxy_server (exit node): - read_queue_drain_cb: tx_buf buffering on backpressure, retry_timer (500ms, force=1) - on_fin_cb: defer FIN when read_queue+tx_buf has pending data (dst_fin_deferred) - handle_data + drain_cb: per-stream byte counters (sent/recv/relayed/drain_count) - on_fin_cb/on_error_cb: detailed state logging (counters, queues, buffers) test_socks_http_proxy: - 4 workers: socks_ipv4, socks_domain (DNS resolution via SOCKS), http_proxy, https_connect - 4 file sizes: 1KB, 100KB, 1MB, 10MB — deterministic content, byte-by-byte cmp -s - HTTPS server with self-signed cert, POST/echo, HEAD, OPTIONS - 51/51 tests PASS
84 lines
3.6 KiB
C
84 lines
3.6 KiB
C
// socks_proxy.h — SOCKS5 / HTTP CONNECT proxy (client side)
|
|
#ifndef SOCKS_PROXY_H
|
|
#define SOCKS_PROXY_H
|
|
|
|
#include <stdint.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;
|
|
|
|
enum {
|
|
SOCKS_STATE_GREETING = 0, // ждём SOCKS5 greeting (ver + methods)
|
|
SOCKS_STATE_REQUEST, // ждём SOCKS5 CONNECT request
|
|
SOCKS_STATE_CONNECTING, // отправили ETCP CONNECT, ждём ответ от exit
|
|
SOCKS_STATE_RELAY // релей данных
|
|
};
|
|
|
|
enum {
|
|
HTTP_STATE_REQUEST = 0, // ждём "CONNECT host:port HTTP/1.1\r\n"
|
|
HTTP_STATE_CONNECTING, // отправили ETCP CONNECT, ждём ответ от exit
|
|
HTTP_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;
|
|
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 buf[1024];
|
|
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;
|
|
};
|
|
|
|
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);
|
|
|
|
#endif // SOCKS_PROXY_H
|