docs(proxy): clarify flow control and HTTP parser contracts
This commit is contained in:
1 parent
34d1dcb7c8
commit
a4abe30b18
3 files changed
+131
-93
No files matched your search
+49
-35
@@ -1,7 +1,11 @@
|
||||
/* Потоковый HTTP/1.x codec локального proxy. Не владеет сокетами/DNS/ETCP.
|
||||
* Заголовки проверяются целиком до пересылки; тело передаётся ограниченными порциями.
|
||||
* emit получает исходное число байт для возврата ETCP credit после доставки результата.
|
||||
* Возврат feed: 0=нужно продолжение, 1=сообщение завершено, <0=HTTP error status. */
|
||||
* Для нового сообщения обнулить message/request, затем headers_feed → request/response_parse → body_feed.
|
||||
* parse меняет headers и сохраняет в fields указатели внутрь него: message нельзя копировать после parse.
|
||||
* emit вызывается синхронно, буфер заимствован только на время вызова; source_len нужен для возврата
|
||||
* ETCP credit после доставки результата. Ошибку feed/parse нельзя продолжать как успешное сообщение.
|
||||
* Возврат feed: 0=нужно продолжение, 1=заголовки/тело завершены, <0=отрицательный HTTP status.
|
||||
* HTTP_BODY_EOF завершает владелец по FIN, отдельной функции EOF у codec нет. */
|
||||
#ifndef HTTP_PROXY_H
|
||||
#define HTTP_PROXY_H
|
||||
|
||||
@@ -12,54 +16,64 @@
|
||||
#define HTTP_PROXY_HEADER_LIMIT 32768
|
||||
#define HTTP_PROXY_FIELDS_LIMIT 512
|
||||
|
||||
// Результат: >=0 = принято целиком, <0 = ошибка вывода (codec вернёт -500); частичный приём не поддержан.
|
||||
// source_len может отличаться от len; len=0/data=NULL допустимы при удалении исходных байтов.
|
||||
typedef int (*http_proxy_emit)(void* arg, const uint8_t* data, size_t len, size_t source_len);
|
||||
|
||||
struct http_proxy_field { char* name; char* value; };
|
||||
struct http_proxy_field { char* name; char* value; }; // Заимствованные NUL-строки внутри message.headers.
|
||||
// NONE — исходное нулевое состояние; EOF — тело до внешнего FIN; DONE — точная граница сообщения.
|
||||
enum http_proxy_body_state { HTTP_BODY_NONE, HTTP_BODY_LENGTH, HTTP_BODY_EOF, HTTP_BODY_CHUNK_SIZE,
|
||||
HTTP_BODY_CHUNK_DATA, HTTP_BODY_CHUNK_CRLF, HTTP_BODY_TRAILERS, HTTP_BODY_DONE };
|
||||
|
||||
struct http_proxy_message {
|
||||
char headers[HTTP_PROXY_HEADER_LIMIT + 1];
|
||||
size_t header_len;
|
||||
size_t first_line_len;
|
||||
struct http_proxy_field fields[HTTP_PROXY_FIELDS_LIMIT];
|
||||
unsigned field_count;
|
||||
uint8_t headers_done;
|
||||
uint8_t minor;
|
||||
uint8_t close;
|
||||
uint8_t chunked;
|
||||
uint8_t has_length;
|
||||
uint8_t upgrade;
|
||||
uint8_t decode_chunks;
|
||||
unsigned status;
|
||||
uint64_t length;
|
||||
enum http_proxy_body_state body;
|
||||
uint64_t remaining;
|
||||
char line[HTTP_PROXY_LINE_LIMIT + 1];
|
||||
size_t line_len;
|
||||
size_t trailer_len;
|
||||
unsigned crlf_pos;
|
||||
char headers[HTTP_PROXY_HEADER_LIMIT + 1]; // До parse — CRLF-блок, после — строки с NUL-разделителями.
|
||||
size_t header_len; // Число исходных байтов, включая CRLFCRLF.
|
||||
size_t first_line_len; // Длина стартовой строки с CRLF.
|
||||
struct http_proxy_field fields[HTTP_PROXY_FIELDS_LIMIT]; // Разобранные поля собственного headers.
|
||||
unsigned field_count; // Число заполненных fields.
|
||||
uint8_t headers_done; // headers_feed получил полный CRLFCRLF.
|
||||
uint8_t minor; // Версия исходного HTTP: 0 или 1.
|
||||
uint8_t close; // Запрос: Connection close; ответ: решение закрыть frontend.
|
||||
uint8_t chunked; // Единственный поддержанный Transfer-Encoding — chunked.
|
||||
uint8_t has_length; // Присутствует проверенный Content-Length (включая нулевой).
|
||||
uint8_t upgrade; // В Connection найден token upgrade.
|
||||
uint8_t decode_chunks; // Убрать framing chunked для ответа HTTP/1.0.
|
||||
unsigned status; // HTTP status ответа; у запроса 0.
|
||||
uint64_t length; // Content-Length; отсутствие различает has_length.
|
||||
enum http_proxy_body_state body; // Состояние потокового разбора тела.
|
||||
uint64_t remaining; // Остаток Content-Length или текущего chunk.
|
||||
char line[HTTP_PROXY_LINE_LIMIT + 1]; // Неполная строка размера chunk или trailer.
|
||||
size_t line_len; // Сколько байт накоплено в line.
|
||||
size_t trailer_len; // Суммарный размер trailers, лимит HEADER_LIMIT.
|
||||
unsigned crlf_pos; // 0/1: сколько байт CRLF после chunk уже принято.
|
||||
};
|
||||
|
||||
struct http_proxy_request {
|
||||
char host[256];
|
||||
char authority[264];
|
||||
char upgrade[HTTP_PROXY_LINE_LIMIT + 1];
|
||||
uint16_t port;
|
||||
uint8_t connect;
|
||||
uint8_t head;
|
||||
uint8_t close;
|
||||
uint8_t minor;
|
||||
char host[256]; // IPv4 literal или DNS-имя; IPv6 отклоняется с -502.
|
||||
char authority[264]; // Нормализованный host:port для upstream Host.
|
||||
char upgrade[HTTP_PROXY_LINE_LIMIT + 1]; // Предложенные protocol[/version], пусто без Upgrade.
|
||||
uint16_t port; // Host byte order; HTTP default=80, CONNECT требует порт.
|
||||
uint8_t connect; // Метод CONNECT: после успеха используется сырой туннель.
|
||||
uint8_t head; // Метод HEAD: у ответа нет тела.
|
||||
uint8_t close; // Закрыть frontend: HTTP/1.0 или Connection close.
|
||||
uint8_t minor; // Версия клиента, определяет сериализацию ответа.
|
||||
};
|
||||
|
||||
// Накапливает только заголовки; хвост входа остаётся у вызывающей стороны.
|
||||
// Накапливает заголовки из заимствованного data; *used = число принятых байтов, хвост остаётся вызывающему.
|
||||
// m первоначально обнулён; 1 = CRLFCRLF получен, 0 = ждать продолжения, <0 = -400/-414/-431.
|
||||
int http_proxy_headers_feed(struct http_proxy_message* m, const uint8_t* data, size_t len, size_t* used);
|
||||
// Проверяет запрос и отправляет реконструированные заголовки через emit (кроме CONNECT).
|
||||
// Однократно после headers_feed==1: проверяет запрос, заполняет обнулённый r и body в m.
|
||||
// Обычный запрос требует absolute http:// URL; emit получает origin-form HTTP/1.1 headers.
|
||||
// CONNECT проверяет authority и не вызывает emit. 0 = успех, <0 = отрицательный HTTP status.
|
||||
int http_proxy_request_parse(struct http_proxy_message* m, struct http_proxy_request* r, http_proxy_emit emit, void* arg);
|
||||
// Проверяет ответ, учитывает HEAD/Upgrade и формирует собственные connection headers.
|
||||
// Однократно после headers_feed==1; r — исходный запрос, close_client — решение закрыть frontend.
|
||||
// Проверяет HEAD/Upgrade, задаёт body, выводит headers в версии клиента. 0 = успех, <0 = -502/-500.
|
||||
// Для следующего промежуточного ответа владелец обнуляет m; r сохраняется до final response.
|
||||
int http_proxy_response_parse(struct http_proxy_message* m, const struct http_proxy_request* r,
|
||||
int close_client, http_proxy_emit emit, void* arg);
|
||||
// Определяет точную границу тела, фильтрует trailers; поддерживает произвольные TCP-разбиения.
|
||||
// После parse: передаёт тело из data через emit, *used = принятые байты до точной границы тела.
|
||||
// Фильтрует trailers; 1 = DONE, 0 = продолжить, <0 = отрицательный HTTP status.
|
||||
// Для HTTP_BODY_EOF возвращает 0, завершение по FIN проверяет владелец. Вывод не откатывается при ошибке.
|
||||
int http_proxy_body_feed(struct http_proxy_message* m, const uint8_t* data, size_t len, size_t* used,
|
||||
http_proxy_emit emit, void* arg);
|
||||
|
||||
|
||||
+41
-23
@@ -1,6 +1,11 @@
|
||||
// Общий TCP proxy протокол. DATA ограничен окном каждого потока; WINDOW возвращает
|
||||
// кредит после потребления данных локальным TCP. FIN следует за всеми DATA своего
|
||||
// направления. CONNECTED подтверждает реальный connect exit → destination.
|
||||
/* Общий TCP proxy протокол и inline-машина окна; все вызовы выполняются в uasync владельца.
|
||||
* init задаёт окно, но ready устанавливает владелец после CONNECTED (на exit — flush).
|
||||
* Перед destroy владельца обязателен proxy_flow_destroy: retry хранит указатель на flow.
|
||||
* Отправка: svc(1), cmd(1), sid(4), payload; приём: router header(26), cmd(1), sid(4), payload.
|
||||
* sid и WINDOW uint32_t копируются в host byte order; IPv4/порт CONNECT — network byte order.
|
||||
* CONNECTED подтверждает реальный connect exit → destination. DATA ограничен окном потока.
|
||||
* FIN завершает одно направление; владелец ставит fin_pending только после всех своих DATA.
|
||||
* CLOSE/ERROR и CONNECT отправляет владелец напрямую: общий retry их не сохраняет. */
|
||||
#ifndef PROXY_PROTOCOL_H
|
||||
#define PROXY_PROTOCOL_H
|
||||
|
||||
@@ -22,30 +27,32 @@
|
||||
#define TCP_PROXY_SUBCMD_WINDOW 0x07
|
||||
#define TCP_PROXY_HDR_SIZE 6
|
||||
#define TCP_PROXY_CONNECT_HDR_SIZE 12
|
||||
#define TCP_PROXY_RECV_HDR_SIZE (ROUTER_SVC_PAYLOAD_OFF + 5)
|
||||
#define TCP_PROXY_RECV_HDR_SIZE (ROUTER_SVC_PAYLOAD_OFF + 5) // 31 байт с метаданными router, не wire header
|
||||
#define TCP_PROXY_CHUNK 4096
|
||||
#define TCP_PROXY_WINDOW 65536u
|
||||
|
||||
struct proxy_flow {
|
||||
struct UTUN_INSTANCE* inst;
|
||||
struct UASYNC* ua;
|
||||
uint64_t peer;
|
||||
uint32_t sid;
|
||||
uint8_t svc;
|
||||
uint8_t ready;
|
||||
uint8_t connected_pending;
|
||||
uint8_t fin_pending;
|
||||
uint8_t fin_sent;
|
||||
uint8_t fin_received;
|
||||
uint32_t tx_credit;
|
||||
uint32_t rx_credit;
|
||||
uint32_t consumed;
|
||||
void* retry;
|
||||
void (*wake)(void* arg);
|
||||
void* arg;
|
||||
struct UTUN_INSTANCE* inst; // Заимствованный instance для отправки в TOPO_GROUP_UTUN, mode=0.
|
||||
struct UASYNC* ua; // Заимствованный event loop; живёт дольше flow и retry.
|
||||
uint64_t peer; // Конечный узел противоположной стороны потока.
|
||||
uint32_t sid; // ID потока; на exit уникален только вместе с peer.
|
||||
uint8_t svc; // Сервис получателя: TCP_PROXY_SERVER или TCP_PROXY_CLIENT.
|
||||
uint8_t ready; // Разрешены DATA и FIN; начальное значение 0.
|
||||
uint8_t connected_pending; // Exit должен поставить CONNECTED в router перед остальными control.
|
||||
uint8_t fin_pending; // Владелец уже передал все DATA и просит отправить FIN.
|
||||
uint8_t fin_sent; // FIN принят router; это не ACK конечного TCP.
|
||||
uint8_t fin_received; // Владелец принял FIN; последующие DATA отклоняются.
|
||||
uint32_t tx_credit; // Сколько DATA ещё разрешено отправить, изначально TCP_PROXY_WINDOW.
|
||||
uint32_t rx_credit; // Сколько DATA разрешено принять до следующего WINDOW.
|
||||
uint32_t consumed; // Потреблённые байты, кредит которых ещё не передан router.
|
||||
void* retry; // Отменяемый таймер control, 5000 tb = 500 мс.
|
||||
void (*wake)(void* arg); // Возобновление очередей владельца после timer flush; может освободить владельца.
|
||||
void* arg; // Заимствованный аргумент wake.
|
||||
};
|
||||
|
||||
// Сообщение целиком передаётся маршрутизатору, который освобождает entry при любом результате.
|
||||
// Копирует заимствованный data (len<=4096; при len>0 не NULL), отправляет в UTUN с mode=0.
|
||||
// 0 = принято router, <0 = ошибка; force обходит лимит send_q, не гарантирует доставку.
|
||||
// Созданный entry целиком передаётся router, который освобождает его при любом результате.
|
||||
static inline int proxy_send(struct UTUN_INSTANCE* inst, uint64_t peer, uint8_t svc, uint8_t cmd,
|
||||
uint32_t sid, const uint8_t* data, size_t len, int force) {
|
||||
if (len > TCP_PROXY_CHUNK) {
|
||||
@@ -66,6 +73,8 @@ static inline int proxy_send(struct UTUN_INSTANCE* inst, uint64_t peer, uint8_t
|
||||
return etcp_route_send(inst, TOPO_GROUP_UTUN, peer, e, force, 0);
|
||||
}
|
||||
|
||||
// Отправляет pending CONNECTED → WINDOW → FIN с force=1; неудачу повторяет через 500 мс.
|
||||
// Владелец сам дренирует DATA перед fin_pending; flush не проверяет его очереди.
|
||||
static inline void proxy_flow_flush(struct proxy_flow* f);
|
||||
|
||||
// Повторяем только недоставленные control-сообщения; DATA остаётся у владельца потока.
|
||||
@@ -76,6 +85,8 @@ static inline void proxy_flow_retry(void* arg) {
|
||||
if (f->wake) f->wake(f->arg);
|
||||
}
|
||||
|
||||
// Обнуляет неактивный f, задаёт peer/svc/sid и оба окна по 64 KiB; указатели заимствуются.
|
||||
// Перед повторным init нужно отменить прежний retry через destroy; ready остаётся 0.
|
||||
static inline void proxy_flow_init(struct proxy_flow* f, struct UTUN_INSTANCE* inst, struct UASYNC* ua,
|
||||
uint64_t peer, uint8_t svc, uint32_t sid, void (*wake)(void*), void* arg) {
|
||||
memset(f, 0, sizeof(*f));
|
||||
@@ -84,6 +95,7 @@ static inline void proxy_flow_init(struct proxy_flow* f, struct UTUN_INSTANCE* i
|
||||
f->wake = wake; f->arg = arg;
|
||||
}
|
||||
|
||||
// Отменяет только control retry; память flow, очереди и router waiter освобождает владелец.
|
||||
static inline void proxy_flow_destroy(struct proxy_flow* f) {
|
||||
if (f->retry) { uasync_cancel_timeout(f->ua, f->retry); f->retry = NULL; }
|
||||
}
|
||||
@@ -114,7 +126,8 @@ retry:
|
||||
if (!f->retry) f->retry = uasync_set_timeout(f->ua, 5000, f, proxy_flow_retry, "proxy_control");
|
||||
}
|
||||
|
||||
// 0 = отправлено, -2 = ждём окно/CONNECTED, -1 = router backpressure/ошибка выделения.
|
||||
// Копирует DATA; 0 = принято router, -2 = ждём окно/CONNECTED, -1 = неверный DATA/ошибка router или выделения.
|
||||
// При ошибке восстанавливает tx_credit; исходный буфер остаётся у владельца для повтора.
|
||||
static inline int proxy_flow_send(struct proxy_flow* f, const uint8_t* data, size_t len, int force) {
|
||||
if (!f->ready || len > f->tx_credit) return -2;
|
||||
if (!len || len > TCP_PROXY_CHUNK || f->fin_sent) {
|
||||
@@ -127,6 +140,8 @@ static inline int proxy_flow_send(struct proxy_flow* f, const uint8_t* data, siz
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Проверяет DATA len=1..4096, доступное rx_credit и отсутствие FIN; 0 списывает кредит, -1 = ошибка.
|
||||
// Проверку ready и доставку байтов локальному TCP выполняет вызывающая сторона.
|
||||
static inline int proxy_flow_receive(struct proxy_flow* f, size_t len) {
|
||||
if (!len || len > TCP_PROXY_CHUNK || len > f->rx_credit || f->fin_received) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "proxy: invalid receive sid=%08x len=%zu credit=%u fin=%u",
|
||||
@@ -137,6 +152,8 @@ static inline int proxy_flow_receive(struct proxy_flow* f, size_t len) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// WINDOW: заимствованные 4 байта host-order credit; 0 добавляет кредит, -1 = неверный размер/кредит.
|
||||
// Не вызывает wake: владелец после успеха сам возобновляет отправку.
|
||||
static inline int proxy_flow_window(struct proxy_flow* f, const uint8_t* data, size_t len) {
|
||||
uint32_t credit = 0;
|
||||
if (len == 4) memcpy(&credit, data, 4);
|
||||
@@ -149,7 +166,8 @@ static inline int proxy_flow_window(struct proxy_flow* f, const uint8_t* data, s
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Вызывать только после передачи байтов локальному TCP, не после получения ETCP DATA.
|
||||
// Возвращает кредит через flush после передачи len байт локальному TCP (OS send/lwIP tcp_write).
|
||||
// Владелец гарантирует, что байты ранее приняты и не учтены повторно; проверки здесь нет.
|
||||
static inline void proxy_flow_consume(struct proxy_flow* f, uint32_t len) {
|
||||
f->consumed += len;
|
||||
proxy_flow_flush(f);
|
||||
|
||||
+41
-35
@@ -1,6 +1,9 @@
|
||||
/* Локальные SOCKS5 CONNECT и HTTP proxy/CONNECT: DNS, handshake и передача TCP через удалённый exit.
|
||||
* tcp_proxy_client владеет listener и списками соединений; их callbacks выполняются в uasync.
|
||||
* init_listen заимствует указатели на списки/счётчики до close_listen; закрытие listener не освобождает клиентов. */
|
||||
* init_listen заимствует inst/ua/списки/счётчики; они живут до закрытия listener И освобождения клиентов.
|
||||
* Сначала закрыть listener, затем conn_free_all; закрытие listener само по себе не освобождает клиентов.
|
||||
* DNS выполняется локально (первая A-запись); SOCKS поддерживает только CONNECT без аутентификации.
|
||||
* HTTP keep-alive использует новый stream для каждой транзакции; CONNECT/101 переходят в сырой туннель. */
|
||||
#ifndef SOCKS_PROXY_H
|
||||
#define SOCKS_PROXY_H
|
||||
|
||||
@@ -25,80 +28,83 @@ struct socks_http_state;
|
||||
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_CONNECTING, // ждём DNS или CONNECTED от exit (== HTTP_STATE_CONNECTING)
|
||||
SOCKS_STATE_RELAY // релей данных (== HTTP_STATE_RELAY)
|
||||
};
|
||||
|
||||
// DNS/connect и сырой туннель используют общие состояния; HTTP framing хранится отдельно в http.
|
||||
enum {
|
||||
HTTP_STATE_REQUEST = SOCKS_STATE_REQUEST, // ждём заголовки очередного HTTP-запроса
|
||||
HTTP_STATE_CONNECTING = SOCKS_STATE_CONNECTING, // DNS в процессе
|
||||
HTTP_STATE_CONNECTING = SOCKS_STATE_CONNECTING, // DNS или реальный connect на exit
|
||||
HTTP_STATE_RELAY = SOCKS_STATE_RELAY // релей данных
|
||||
};
|
||||
|
||||
struct socks_proxy_conn {
|
||||
struct socks_proxy_conn* next;
|
||||
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;
|
||||
struct tcp_conn* tc; // собственный OS TCP к локальному клиенту
|
||||
struct UASYNC* ua; // заимствованный event loop
|
||||
struct UTUN_INSTANCE* inst; // заимствованный instance
|
||||
uint64_t via_node_id; // настроенный exit
|
||||
struct proxy_flow flow; // окно и control retry текущего upstream
|
||||
uint32_t stream_id; // текущий upstream; HTTP между транзакциями может быть 0
|
||||
uint32_t* next_stream_id; // HTTP: новый поток для следующей транзакции
|
||||
struct socks_http_state* http;
|
||||
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 state;
|
||||
uint8_t freed;
|
||||
uint8_t close_queued;
|
||||
struct socks_http_state* http; // собственное HTTP-состояние; NULL для SOCKS
|
||||
uint8_t dest_ip[4]; // IPv4 назначения, network byte order
|
||||
uint16_t dest_port; // порт назначения, network byte order
|
||||
uint8_t fin_remote; // FIN от exit; FIN локального клиента хранится в tc->fin_remote
|
||||
uint8_t rem_closed; // прекращена обработка потока/клиента
|
||||
uint8_t close_sent; // CLOSE принят router
|
||||
uint8_t close_pending; // неудавшийся CLOSE; последняя попытка при free
|
||||
uint8_t state; // SOCKS_STATE_* / HTTP_STATE_*
|
||||
uint8_t freed; // освобождение уже началось
|
||||
uint8_t close_queued; // close-маркер уже поставлен в write_queue
|
||||
void* free_soon_id; // handle отложенного освобождения (uasync_call_soon)
|
||||
uint8_t buf[16384];
|
||||
uint16_t buf_len;
|
||||
uint8_t buf[16384]; // handshake или неразобранный хвост read block
|
||||
uint16_t buf_len; // число сохранённых байтов в buf
|
||||
uint8_t* tx_buf; // буфер при backpressure (retry в tx_waiter_cb)
|
||||
uint16_t tx_len;
|
||||
uint16_t tx_len; // остаток tx_buf; отправляется блоками до 4096 байт
|
||||
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 queue_waiter_handle tx_waiter; // ожидание router send_q, отменить до free
|
||||
void* tx_retry_timer; // safety-net 500 мс, повтор с обычным force=0
|
||||
uint8_t fin_deferred; // зарезервировано, текущая реализация не использует
|
||||
struct adns_query* dns_q; // активный DNS-запрос (NULL когда нет)
|
||||
uint8_t dns_kind; // DNSK_SOCKS / DNSK_HTTP_CONNECT / DNSK_HTTP_PROXY
|
||||
};
|
||||
|
||||
struct listen_ctx;
|
||||
|
||||
// Создать слушающий сокет для SOCKS/HTTP proxy. addr_str = "IP:PORT".
|
||||
// listen_ctx должен быть освобождён через socks_proxy_close_listen.
|
||||
// Возвращает listen_ctx при успехе, NULL при ошибке.
|
||||
// Создать IPv4 listener: addr_str = "IPv4:PORT", 0 разрешает выбор порта ОС.
|
||||
// conns/count/next_stream_id и inst/ua заимствуются; is_http=0 — SOCKS5, иначе HTTP.
|
||||
// Общий генератор sid нужен для всех списков одного tcp_proxy_client. Возврат: ctx или NULL при ошибке.
|
||||
// Не регистрирует router callback: входящие ответы должен передавать tcp_proxy_client.
|
||||
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.
|
||||
// Закрыть listener и освободить ctx в том же ua. NULL ctx допустим; активные клиенты сохраняются.
|
||||
// Если sock_out задан, в него записывается SOCKET_INVALID: сокет уже закрыт.
|
||||
void socks_proxy_close_listen(struct UASYNC* ua, struct listen_ctx* ctx, socket_t* sock_out);
|
||||
// Фактический порт активного listener (IP:0 выбирает ОС); 0 при ошибке.
|
||||
uint16_t socks_proxy_get_port(const struct listen_ctx* ctx);
|
||||
|
||||
// Найти соединение по stream_id
|
||||
// Найти текущий stream_id в head; заимствованный результат или NULL, список не меняется.
|
||||
struct socks_proxy_conn* socks_proxy_find_conn(struct socks_proxy_conn* head, uint32_t stream_id);
|
||||
|
||||
// Обработать ответ exit: CONNECTED/DATA/WINDOW_UPDATE/CLOSE/ERROR/FIN.
|
||||
// Возвращает 1 если обработано, 0 если stream_id не найден
|
||||
// Передать ответ exit: CONNECTED/DATA/WINDOW/CLOSE/ERROR/FIN; data заимствован на время вызова.
|
||||
// Проверку peer/group выполняет внешний dispatcher. Может закрыть/удалить соединение.
|
||||
// Возвращает 1 при найденном sid (в том числе неизвестная команда), 0 если sid не найден.
|
||||
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);
|
||||
|
||||
// Освободить одно соединение (удаляет из списка)
|
||||
// Освободить c и его TCP/DNS/HTTP/таймеры/waiter, удалить из списка; NULL допустим.
|
||||
// Активный HTTP upstream отменяется; в остальных случаях free не гарантирует уведомление exit.
|
||||
void socks_proxy_conn_free(struct socks_proxy_conn* c);
|
||||
|
||||
// Освободить все соединения в списке
|
||||
// Освободить все соединения; head/count — те же живые адреса, что переданы init_listen.
|
||||
void socks_proxy_conn_free_all(struct socks_proxy_conn** head, int* count);
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user