You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

292 lines
16 KiB

// Реальные локальные TCP-сокеты и управляемый транспорт: проверяем границы
// handshake, подтверждение connect, изоляцию потоков и порядок DATA/FIN.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "utun_instance.h"
#include "etcp_connections.h"
#include "etcp.h"
#include "proxy/socks_proxy.h"
#include "proxy/udp_proxy.h"
#include "proxy/icmp_proxy.h"
#include "lwip_tcp/lwip_tcp.h"
#include "tun_if.h"
#include "debug_config.h"
#include "mem.h"
#ifndef _WIN32
#include <unistd.h>
#else
#define SHUT_WR SD_SEND
#endif
#define CHECK(x) do { if (!(x)) { fprintf(stderr, "FAIL line %d: %s\n", __LINE__, #x); exit(1); } } while (0)
struct message { struct UTUN_INSTANCE* inst; uint64_t peer; uint8_t svc, cmd; uint32_t sid; size_t len; uint8_t data[4096]; };
static struct message messages[512];
static unsigned message_count;
static struct UASYNC* ua;
// Перехватывается только транспорт. Парсеры, tcp_io, очереди и lifecycle — настоящие.
int __wrap_etcp_route_send(struct UTUN_INSTANCE* inst, uint64_t group, uint64_t peer,
struct ll_entry* entry, int force, int mode) {
(void)group; (void)force; (void)mode;
CHECK(message_count < 512 && entry->len >= 6 && entry->len <= 4102);
struct message* m = &messages[message_count++];
m->inst = inst; m->peer = peer; m->svc = entry->dgram[0]; m->cmd = entry->dgram[1];
memcpy(&m->sid, entry->dgram + 2, 4); m->len = entry->len - 6;
memcpy(m->data, entry->dgram + 6, m->len);
queue_dgram_free(entry); queue_entry_free(entry);
return 0;
}
static void pump(void) { for (int i = 0; i < 12; i++) uasync_poll(ua, 1); }
static socket_t listen_local(uint16_t* port) {
socket_t fd = socket(AF_INET, SOCK_STREAM, 0);
CHECK(fd != SOCKET_INVALID);
struct sockaddr_in a = {.sin_family = AF_INET, .sin_addr.s_addr = htonl(INADDR_LOOPBACK)};
CHECK(bind(fd, (struct sockaddr*)&a, sizeof(a)) == 0 && listen(fd, 8) == 0);
socklen_t len = sizeof(a); CHECK(getsockname(fd, (struct sockaddr*)&a, &len) == 0);
*port = ntohs(a.sin_port); socket_set_nonblocking(fd);
return fd;
}
static socket_t connect_local(uint16_t port) {
socket_t fd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in a = {.sin_family = AF_INET, .sin_port = htons(port), .sin_addr.s_addr = htonl(INADDR_LOOPBACK)};
CHECK(connect(fd, (struct sockaddr*)&a, sizeof(a)) == 0);
socket_set_nonblocking(fd); pump(); return fd;
}
static void send_bytes(socket_t fd, const void* bytes, size_t len) {
CHECK(send(fd, bytes, len, 0) == (ssize_t)len); pump();
}
static unsigned count_cmd(uint8_t cmd) {
unsigned count = 0;
for (unsigned i = 0; i < message_count; i++) if (messages[i].cmd == cmd) count++;
return count;
}
static void deliver(struct UTUN_INSTANCE* inst, uint64_t peer, uint8_t svc, uint8_t cmd,
uint32_t sid, const void* data, size_t len) {
struct ll_entry* e = queue_entry_new(0);
CHECK(e);
e->len = cmd ? TCP_PROXY_RECV_HDR_SIZE + len : ROUTER_SVC_HDR_SIZE;
e->dgram = u_calloc(1, e->len); CHECK(e->dgram);
e->dgram[0] = svc;
uint64_t group = TOPO_GROUP_UTUN;
memcpy(e->dgram + ROUTER_SVC_SRC_OFF, &peer, 8);
memcpy(e->dgram + ROUTER_SVC_GROUP_OFF, &group, 8);
if (cmd) {
e->dgram[ROUTER_SVC_PAYLOAD_OFF] = cmd;
memcpy(e->dgram + ROUTER_SVC_PAYLOAD_OFF + 1, &sid, 4);
if (len) memcpy(e->dgram + TCP_PROXY_RECV_HDR_SIZE, data, len);
}
struct ETCP_CONN conn = {0}; conn.instance = inst;
if (svc == ETCP_RT_ID_TCP_PROXY_SERVER) tcp_proxy_server_recv_cb(&conn, e);
else tcp_proxy_client_router_recv_cb(&conn, e);
pump();
}
static void parser_tests(struct UTUN_INSTANCE* inst) {
uint16_t sp, hp;
socket_t reserved = listen_local(&sp); socket_close_wrapper(reserved);
reserved = listen_local(&hp); socket_close_wrapper(reserved);
char socks[64], http[64];
snprintf(socks, sizeof(socks), "127.0.0.1:%u", sp); snprintf(http, sizeof(http), "127.0.0.1:%u", hp);
struct tcp_proxy_client* p = tcp_proxy_client_create(inst, ua, NULL, NULL, 1500, 1, NULL, 0, 42, 1, socks, 1, http);
CHECK(p); inst->tcp_proxy_client = p;
socket_t fd = connect_local(sp);
// Greeting, CONNECT и ранние данные намеренно одним write.
const uint8_t request[] = {5,1,0, 5,1,0,1,127,0,0,1,0,80, 'a','b','c'};
message_count = 0; send_bytes(fd, request, sizeof(request));
CHECK(count_cmd(TCP_PROXY_SUBCMD_CONNECT) == 1 && count_cmd(TCP_PROXY_SUBCMD_DATA) == 0);
uint8_t reply[256]; CHECK(recv(fd, reply, sizeof(reply), 0) == 2 && reply[1] == 0);
uint32_t sid = p->socks_conns->stream_id;
deliver(inst, 99, ETCP_RT_ID_TCP_PROXY_CLIENT, TCP_PROXY_SUBCMD_CONNECTED, sid, NULL, 0);
deliver(inst, 99, ETCP_RT_ID_TCP_PROXY_CLIENT, 0, 0, NULL, 0);
CHECK(p->socks_conn_count == 1 && !p->socks_conns->flow.ready);
deliver(inst, 42, ETCP_RT_ID_TCP_PROXY_CLIENT, TCP_PROXY_SUBCMD_CONNECTED, sid, NULL, 0);
CHECK(recv(fd, reply, sizeof(reply), 0) == 10 && reply[1] == 0);
CHECK(count_cmd(TCP_PROXY_SUBCMD_DATA) == 1 && messages[message_count-1].len == 3);
CHECK(memcmp(messages[message_count-1].data, "abc", 3) == 0);
// Закрытое окно: FIN обоих направлений не должен уничтожать ожидающую передачу.
p->socks_conns->flow.tx_credit = 0;
message_count = 0; send_bytes(fd, "tail", 4);
CHECK(shutdown(fd, SHUT_WR) == 0); pump();
CHECK(count_cmd(TCP_PROXY_SUBCMD_FIN) == 0);
deliver(inst, 42, ETCP_RT_ID_TCP_PROXY_CLIENT, TCP_PROXY_SUBCMD_FIN, sid, NULL, 0);
CHECK(p->socks_conn_count == 1);
uint32_t credit = 4;
deliver(inst, 42, ETCP_RT_ID_TCP_PROXY_CLIENT, TCP_PROXY_SUBCMD_WINDOW, sid, &credit, 4);
CHECK(count_cmd(TCP_PROXY_SUBCMD_DATA) == 1 && count_cmd(TCP_PROXY_SUBCMD_FIN) == 1);
CHECK(messages[0].cmd == TCP_PROXY_SUBCMD_DATA && memcmp(messages[0].data, "tail", 4) == 0);
CHECK(p->socks_conn_count == 0); socket_close_wrapper(fd);
fd = connect_local(hp); message_count = 0;
const char first[] = "CONNECT 127.0.0.1:443 HTTP/1.1\r\n";
send_bytes(fd, first, sizeof(first)-1); CHECK(message_count == 0);
CHECK(recv(fd, reply, sizeof(reply), 0) < 0);
const char second[] = "Host: 127.0.0.1\r\n\r\nTLS";
send_bytes(fd, second, sizeof(second)-1); CHECK(count_cmd(TCP_PROXY_SUBCMD_CONNECT) == 1);
sid = p->http_conns->stream_id;
CHECK(recv(fd, reply, sizeof(reply), 0) < 0);
deliver(inst, 42, ETCP_RT_ID_TCP_PROXY_CLIENT, TCP_PROXY_SUBCMD_CONNECTED, sid, NULL, 0);
ssize_t n = recv(fd, reply, sizeof(reply), 0); CHECK(n > 12 && memcmp(reply, "HTTP/1.1 200", 12) == 0);
CHECK(messages[message_count-1].len == 3 && memcmp(messages[message_count-1].data, "TLS", 3) == 0);
deliver(inst, 42, ETCP_RT_ID_TCP_PROXY_CLIENT, TCP_PROXY_SUBCMD_ERROR, sid, NULL, 0);
socket_close_wrapper(fd);
fd = connect_local(hp); message_count = 0;
send_bytes(fd, first, sizeof(first)-1); send_bytes(fd, second, sizeof(second)-1);
sid = p->http_conns->stream_id;
deliver(inst, 42, ETCP_RT_ID_TCP_PROXY_CLIENT, TCP_PROXY_SUBCMD_ERROR, sid, NULL, 0);
n = recv(fd, reply, sizeof(reply), 0); CHECK(n > 12 && memcmp(reply, "HTTP/1.1 502", 12) == 0);
socket_close_wrapper(fd);
fd = connect_local(sp); message_count = 0;
const uint8_t auth[] = {5,1,2}; send_bytes(fd, auth, sizeof(auth));
CHECK(recv(fd, reply, sizeof(reply), 0) == 2 && reply[1] == 255 && message_count == 0);
socket_close_wrapper(fd);
fd = connect_local(sp); message_count = 0;
uint8_t v6[25] = {5,1,0,5,1,0,4}; v6[24] = 80;
send_bytes(fd, v6, sizeof(v6));
n = recv(fd, reply, sizeof(reply), 0); CHECK(n == 12 && reply[3] == 8 && message_count == 0);
socket_close_wrapper(fd);
// Большие заголовки и body, поступающий до CONNECTED, должны сохраниться и разбиться на DATA.
fd = connect_local(hp); message_count = 0;
char upload[32000], expected[32000];
int header = snprintf(upload, sizeof(upload), "POST http://127.0.0.1/upload HTTP/1.1\r\nHost: 127.0.0.1\r\nX-Pad: ");
memset(upload + header, 'x', 9000); header += 9000;
header += snprintf(upload + header, sizeof(upload) - header, "\r\nContent-Length: 20000\r\n\r\n");
memset(upload + header, 'B', 20000);
for (int off = 0; off < header + 20000;) {
int chunk = header + 20000 - off; if (chunk > 1024) chunk = 1024;
send_bytes(fd, upload + off, chunk); off += chunk;
}
CHECK(count_cmd(TCP_PROXY_SUBCMD_CONNECT) == 1 && count_cmd(TCP_PROXY_SUBCMD_DATA) == 0);
sid = p->http_conns->stream_id;
CHECK(p->http_conns->tc->read_queue->count <= 8);
deliver(inst, 42, ETCP_RT_ID_TCP_PROXY_CLIENT, TCP_PROXY_SUBCMD_CONNECTED, sid, NULL, 0);
for (int i = 0; i < 10; i++) pump();
size_t total = 0;
for (unsigned i = 0; i < message_count; i++) if (messages[i].cmd == TCP_PROXY_SUBCMD_DATA) {
CHECK(messages[i].len <= TCP_PROXY_CHUNK && total + messages[i].len <= sizeof(expected));
memcpy(expected + total, messages[i].data, messages[i].len); total += messages[i].len;
}
const size_t prefix = strlen("http://127.0.0.1");
CHECK(total == header + 20000 - prefix);
CHECK(memcmp(expected, "POST ", 5) == 0 && memcmp(expected + 5, upload + 5 + prefix, total - 5) == 0);
deliver(inst, 42, ETCP_RT_ID_TCP_PROXY_CLIENT, TCP_PROXY_SUBCMD_ERROR, sid, NULL, 0);
socket_close_wrapper(fd);
tcp_proxy_client_destroy(p); inst->tcp_proxy_client = NULL; pump();
puts("[PASS] SOCKS/HTTP split/coalesced headers, CONNECTED/error, source identity and half-close");
}
static void server_tests(struct UTUN_INSTANCE* inst) {
uint16_t port; socket_t listener = listen_local(&port);
inst->tcp_proxy_server.enabled = 1; inst->tcp_proxy_server.inst = inst;
uint8_t connect_data[6] = {127,0,0,1}; uint16_t wire_port = htons(port); memcpy(connect_data+4, &wire_port, 2);
deliver(inst, 11, ETCP_RT_ID_TCP_PROXY_SERVER, TCP_PROXY_SUBCMD_CONNECT, 2, connect_data, 6);
socket_t a = accept(listener, NULL, NULL); CHECK(a != SOCKET_INVALID); socket_set_nonblocking(a);
deliver(inst, 22, ETCP_RT_ID_TCP_PROXY_SERVER, TCP_PROXY_SUBCMD_CONNECT, 2, connect_data, 6);
socket_t b = accept(listener, NULL, NULL); CHECK(b != SOCKET_INVALID); socket_set_nonblocking(b);
CHECK(inst->tcp_proxy_server.conn_count == 2);
deliver(inst, 11, ETCP_RT_ID_TCP_PROXY_SERVER, TCP_PROXY_SUBCMD_DATA, 2, "AAA", 3);
deliver(inst, 22, ETCP_RT_ID_TCP_PROXY_SERVER, TCP_PROXY_SUBCMD_DATA, 2, "BBB", 3);
uint8_t buf[16]; CHECK(recv(a, buf, sizeof(buf), 0) == 3 && memcmp(buf, "AAA", 3) == 0);
CHECK(recv(b, buf, sizeof(buf), 0) == 3 && memcmp(buf, "BBB", 3) == 0);
deliver(inst, 33, ETCP_RT_ID_TCP_PROXY_SERVER, TCP_PROXY_SUBCMD_CLOSE, 2, NULL, 0);
CHECK(inst->tcp_proxy_server.conn_count == 2);
deliver(inst, 11, ETCP_RT_ID_TCP_PROXY_SERVER, TCP_PROXY_SUBCMD_CONNECT, 2, connect_data, 6);
CHECK(inst->tcp_proxy_server.conn_count == 2);
deliver(inst, 11, ETCP_RT_ID_TCP_PROXY_SERVER, TCP_PROXY_SUBCMD_CLOSE, 2, NULL, 0);
CHECK(inst->tcp_proxy_server.conn_count == 1);
deliver(inst, 22, ETCP_RT_ID_TCP_PROXY_SERVER, TCP_PROXY_SUBCMD_CLOSE, 2, NULL, 0);
CHECK(inst->tcp_proxy_server.conn_count == 0);
socket_close_wrapper(a); socket_close_wrapper(b); socket_close_wrapper(listener);
inst->tcp_proxy_server.enabled = 0; pump();
puts("[PASS] exit streams isolated by node + stream ID, duplicate CONNECT rejected");
}
static void instance_tests(struct UTUN_INSTANCE* a) {
struct UTUN_INSTANCE* b = u_calloc(1, sizeof(*b)); CHECK(b); b->ua = ua;
CHECK(udp_proxy_init(a, ua) == 0 && udp_proxy_init(b, ua) == 0);
CHECK(icmp_proxy_init(a, ua) == 0 && icmp_proxy_init(b, ua) == 0);
CHECK(a->udp_proxy != b->udp_proxy && a->icmp_proxy != b->icmp_proxy);
struct udp_proxy_ctx* udp = b->udp_proxy; struct icmp_proxy_ctx* icmp = b->icmp_proxy;
udp_proxy_destroy(a); icmp_proxy_destroy(a);
CHECK(b->udp_proxy == udp && b->icmp_proxy == icmp);
CHECK(udp_proxy_init(b, ua) == 0 && b->udp_proxy == udp);
udp_proxy_destroy(b); icmp_proxy_destroy(b); u_free(b);
puts("[PASS] UDP/ICMP contexts and destruction are instance-local");
}
// Принимающий callback lwIP запускаем с PCB установленного соединения; дальнейшие
// recv/abort/shutdown проходят через настоящие callbacks proxy и стек lwIP.
static struct tcp_pcb* accept_tun(struct tcp_proxy_client* p) {
struct tcp_pcb* pcb = tcp_new(p->lwip); CHECK(pcb);
pcb->state = ESTABLISHED; pcb->local_port = 80; pcb->remote_port = 12345;
pcb->local_ip = htonl(0xc0000201); pcb->remote_ip = htonl(0x0a000002);
pcb->next = p->lwip->active_pcbs; p->lwip->active_pcbs = pcb;
struct tcp_pcb_listen* listener = (struct tcp_pcb_listen*)p->lwip->listen_pcbs; CHECK(listener && listener->accept);
CHECK(listener->accept(p, pcb, LERR_OK) == LERR_OK);
return pcb;
}
static void tun_tests(struct UTUN_INSTANCE* inst) {
#ifndef _WIN32
struct tcp_proxy_client_mapping_config map = {.local_port=80, .remote_port=80, .remote_ip="192.0.2.1"};
struct tcp_proxy_client* p = tcp_proxy_client_create(inst, ua, "testproxy", "10.0.0.1", 1500, 1, &map, 1, 42, 0, NULL, 0, NULL);
CHECK(p); inst->tcp_proxy_client = p;
message_count = 0;
struct tcp_pcb* pcb = accept_tun(p); CHECK(p->conn_count == 1);
uint32_t sid = p->conns->stream_id;
deliver(inst, 42, ETCP_RT_ID_TCP_PROXY_CLIENT, TCP_PROXY_SUBCMD_CONNECTED, sid, NULL, 0);
p->conns->flow.tx_credit = 0;
struct pbuf* pb = pbuf_alloc(PBUF_RAW, 4); CHECK(pb); pbuf_take(pb, "tail", 4);
CHECK(pcb->recv(pcb->callback_arg, pcb, pb, LERR_OK) == LERR_OK);
pcb->state = CLOSE_WAIT;
CHECK(pcb->recv(pcb->callback_arg, pcb, NULL, LERR_OK) == LERR_OK);
deliver(inst, 42, ETCP_RT_ID_TCP_PROXY_CLIENT, TCP_PROXY_SUBCMD_FIN, sid, NULL, 0);
CHECK(p->conn_count == 1 && p->conns->tx_queue->count == 1);
message_count = 0; uint32_t credit = 4;
deliver(inst, 42, ETCP_RT_ID_TCP_PROXY_CLIENT, TCP_PROXY_SUBCMD_WINDOW, sid, &credit, 4);
CHECK(p->conn_count == 0 && count_cmd(TCP_PROXY_SUBCMD_DATA) == 1 && count_cmd(TCP_PROXY_SUBCMD_FIN) == 1);
CHECK(messages[0].cmd == TCP_PROXY_SUBCMD_DATA && memcmp(messages[0].data, "tail", 4) == 0);
pcb = accept_tun(p); CHECK(p->conn_count == 1);
tcp_abort(pcb); CHECK(p->conn_count == 0);
// IPv4 options смещают UDP-заголовок; последующий фрагмент не является UDP-запросом.
uint8_t ip[36] = {0x46, 0, 0, 36, 0, 0, 0, 0, 64, 17};
uint32_t src = htonl(0x0a000002), dst = htonl(0xc0000201);
memcpy(ip+12, &src, 4); memcpy(ip+16, &dst, 4);
uint16_t sport=htons(12345), dport=htons(53), ulen=htons(12);
memcpy(ip+24, &sport, 2); memcpy(ip+26, &dport, 2); memcpy(ip+28, &ulen, 2); memcpy(ip+32, "data", 4);
message_count = 0;
for (int fragment = 0; fragment < 2; fragment++) {
struct ll_entry* e = queue_entry_new(0); CHECK(e);
e->dgram = u_calloc(1, sizeof(ip)+1); CHECK(e->dgram); e->len = sizeof(ip)+1;
ip[7] = fragment; memcpy(e->dgram+1, ip, sizeof(ip));
queue_data_put(p->tun->output_queue, e); pump();
}
CHECK(message_count == 1 && messages[0].svc == ETCP_RT_ID_UDP_PROXY && messages[0].len == 12);
CHECK(memcmp(messages[0].data+6, &dport, 2) == 0 && memcmp(messages[0].data+8, "data", 4) == 0);
tcp_proxy_client_destroy(p); inst->tcp_proxy_client = NULL; pump();
puts("[PASS] TUN pending DATA survives FIN; aborted PCB freed; IPv4 options/fragment validation");
#endif
}
int main(void) {
debug_config_init(); debug_set_level(DEBUG_LEVEL_WARN);
if (getenv("UTUN_TEST_DEBUG")) debug_set_category_level(DEBUG_CATEGORY_PROXY, DEBUG_LEVEL_DEBUG);
CHECK(socket_platform_init() == 0);
ua = uasync_create(); CHECK(ua);
struct UTUN_INSTANCE* inst = u_calloc(1, sizeof(*inst)); CHECK(inst);
struct utun_config config = {0}; inst->config = &config; inst->ua = ua;
instance_tests(inst); parser_tests(inst); server_tests(inst); tun_tests(inst);
u_free(inst); pump(); uasync_destroy(ua, 0);
CHECK(u_get_allocated_count() == 0);
socket_platform_cleanup();
return 0;
}