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.
527 lines
20 KiB
527 lines
20 KiB
// test_socks_client.c — SOCKS5-клиент: dial через mock-прокси (no-auth, auth, reject) |
|
#include "socks_client.h" |
|
#include "../lib/u_async.h" |
|
#include "../lib/debug_config.h" |
|
#include "../lib/mem.h" |
|
#include "../lib/socket_compat.h" |
|
#include "../lib/platform_compat.h" |
|
#include <stdio.h> |
|
#include <string.h> |
|
#include <stdlib.h> |
|
|
|
static int test_failed = 0; |
|
#define TASSERT(cond) do { \ |
|
if (!(cond)) { DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, " FAIL: %s", #cond); test_failed = 1; return test_failed; } \ |
|
} while(0) |
|
|
|
#define MOCK_PORT 24771 |
|
|
|
enum { MOCK_NOAUTH = 0, MOCK_AUTH = 1, MOCK_REJECT = 2 }; |
|
|
|
struct mock { |
|
struct UASYNC *ua; |
|
socket_t listen_sock; |
|
void *listen_id; |
|
socket_t conn_sock; |
|
void *conn_id; |
|
uint8_t mode; |
|
uint8_t state; // 0=greeting, 1=auth, 2=request, 3=relay |
|
uint8_t buf[512]; |
|
size_t buf_len; |
|
int got_connect; |
|
}; |
|
|
|
static void mock_send(struct mock *m, const uint8_t *d, size_t n) { |
|
ssize_t s = send(m->conn_sock, d, n, 0); |
|
(void)s; |
|
} |
|
|
|
static void mock_process(struct mock *m) { |
|
for (;;) { |
|
if (m->state == 0) { // greeting |
|
if (m->buf_len < 3) return; |
|
uint8_t nm = m->buf[1]; |
|
if (m->buf_len < (size_t)(2 + nm)) return; |
|
if (m->mode == MOCK_AUTH) { uint8_t r[] = {0x05, 0x02}; mock_send(m, r, 2); m->state = 1; } |
|
else { uint8_t r[] = {0x05, 0x00}; mock_send(m, r, 2); m->state = 2; } |
|
size_t c = 2 + nm; memmove(m->buf, m->buf + c, m->buf_len - c); m->buf_len -= c; |
|
continue; |
|
} |
|
if (m->state == 1) { // auth (RFC1929) |
|
if (m->buf_len < 2) return; |
|
size_t ulen = m->buf[1]; |
|
if (m->buf_len < 2 + ulen + 1) return; |
|
size_t plen = m->buf[2 + ulen]; |
|
size_t total = 2 + ulen + 1 + plen; |
|
if (m->buf_len < total) return; |
|
uint8_t r[] = {0x01, 0x00}; mock_send(m, r, 2); m->state = 2; |
|
memmove(m->buf, m->buf + total, m->buf_len - total); m->buf_len -= total; |
|
continue; |
|
} |
|
if (m->state == 2) { // CONNECT request |
|
if (m->buf_len < 4) return; |
|
uint8_t atyp = m->buf[3]; |
|
size_t need = 4; |
|
if (atyp == 0x01) need += 6; |
|
else if (atyp == 0x03) { if (m->buf_len < 5) return; need += 1 + (size_t)m->buf[4] + 2; } |
|
else if (atyp == 0x04) need += 18; |
|
else return; |
|
if (m->buf_len < need) return; |
|
if (m->mode == MOCK_REJECT) { uint8_t r[] = {0x05, 0x05, 0x00, 0x01, 0,0,0,0, 0,0}; mock_send(m, r, 10); } |
|
else { uint8_t r[] = {0x05, 0x00, 0x00, 0x01, 0,0,0,0, 0,0}; mock_send(m, r, 10); m->got_connect = 1; } |
|
m->state = 3; |
|
memmove(m->buf, m->buf + need, m->buf_len - need); m->buf_len -= need; |
|
continue; |
|
} |
|
while (m->buf_len > 0) { // relay: echo |
|
mock_send(m, m->buf, 1); |
|
memmove(m->buf, m->buf + 1, m->buf_len - 1); m->buf_len -= 1; |
|
} |
|
return; |
|
} |
|
} |
|
|
|
static void mock_read_cb(socket_t sock, void *arg) { |
|
struct mock *m = (struct mock *)arg; |
|
(void)sock; |
|
uint8_t tmp[512]; |
|
ssize_t n = recv(m->conn_sock, tmp, sizeof(tmp), 0); |
|
if (n <= 0) return; |
|
memcpy(m->buf + m->buf_len, tmp, (size_t)n); |
|
m->buf_len += (size_t)n; |
|
mock_process(m); |
|
} |
|
|
|
static void mock_accept_cb(socket_t sock, void *arg) { |
|
struct mock *m = (struct mock *)arg; |
|
struct sockaddr_storage sa; socklen_t sl = sizeof(sa); |
|
m->conn_sock = accept(sock, (struct sockaddr *)&sa, &sl); |
|
if (m->conn_sock == SOCKET_INVALID) return; |
|
socket_set_nonblocking(m->conn_sock); |
|
m->conn_id = uasync_add_socket_t(m->ua, m->conn_sock, mock_read_cb, NULL, NULL, "mock_conn", m); |
|
} |
|
|
|
static int mock_start(struct mock *m, struct UASYNC *ua, int port, uint8_t mode) { |
|
memset(m, 0, sizeof(*m)); |
|
m->ua = ua; m->mode = mode; m->conn_sock = SOCKET_INVALID; |
|
m->listen_sock = socket(AF_INET, SOCK_STREAM, 0); |
|
if (m->listen_sock == SOCKET_INVALID) return -1; |
|
socket_set_reuseaddr(m->listen_sock, 1); |
|
socket_set_nonblocking(m->listen_sock); |
|
struct sockaddr_in a; memset(&a, 0, sizeof(a)); |
|
a.sin_family = AF_INET; a.sin_addr.s_addr = htonl(INADDR_LOOPBACK); a.sin_port = htons((uint16_t)port); |
|
if (bind(m->listen_sock, (struct sockaddr *)&a, sizeof(a)) < 0) return -1; |
|
if (listen(m->listen_sock, 8) < 0) return -1; |
|
m->listen_id = uasync_add_socket_t(ua, m->listen_sock, mock_accept_cb, NULL, NULL, "mock_listen", m); |
|
return m->listen_id ? 0 : -1; |
|
} |
|
|
|
static void mock_stop(struct mock *m) { |
|
if (m->conn_id) uasync_remove_socket_t(m->ua, m->conn_sock); |
|
if (m->conn_sock != SOCKET_INVALID) socket_close_wrapper(m->conn_sock); |
|
if (m->listen_id) uasync_remove_socket_t(m->ua, m->listen_sock); |
|
socket_close_wrapper(m->listen_sock); |
|
} |
|
|
|
// ── Mock SOCKS5 UDP ASSOCIATE: TCP-control (greeting+associate) + UDP-релей с эхом ── |
|
|
|
struct udp_mock { |
|
struct UASYNC *ua; |
|
socket_t listen_sock; // TCP-control listener |
|
void *listen_id; |
|
socket_t ctrl_sock; // принятый control-коннект |
|
void *ctrl_id; |
|
uint8_t cbuf[512]; |
|
size_t cblen; |
|
uint8_t cstate; // 0=greeting, 1=associate |
|
socket_t udp_sock; // UDP-релей |
|
void *udp_id; |
|
uint16_t udp_port; |
|
int got_associate; |
|
int echo_count; |
|
uint8_t last_payload[256]; |
|
size_t last_payload_len; |
|
struct sockaddr_storage last_peer; |
|
}; |
|
|
|
static void umock_ctrl_process(struct udp_mock *m) { |
|
for (;;) { |
|
if (m->cstate == 0) { // greeting |
|
if (m->cblen < 3) return; |
|
uint8_t nm = m->cbuf[1]; |
|
if (m->cblen < (size_t)(2 + nm)) return; |
|
uint8_t r[] = {0x05, 0x00}; // no-auth |
|
send(m->ctrl_sock, r, 2, 0); |
|
m->cstate = 1; |
|
size_t c = 2 + nm; memmove(m->cbuf, m->cbuf + c, m->cblen - c); m->cblen -= c; |
|
continue; |
|
} |
|
if (m->cstate == 1) { // UDP ASSOCIATE request |
|
if (m->cblen < 4) return; |
|
uint8_t atyp = m->cbuf[3]; |
|
size_t need = 4; |
|
if (atyp == 0x01) need += 6; |
|
else if (atyp == 0x04) need += 18; |
|
else return; |
|
if (m->cblen < need) return; |
|
uint8_t r[10]; |
|
r[0] = 0x05; r[1] = 0x00; r[2] = 0x00; r[3] = 0x01; |
|
uint32_t ip = htonl(INADDR_LOOPBACK); |
|
memcpy(r + 4, &ip, 4); |
|
uint16_t port = htons(m->udp_port); |
|
memcpy(r + 8, &port, 2); |
|
send(m->ctrl_sock, r, 10, 0); |
|
m->got_associate++; |
|
m->cstate = 2; |
|
memmove(m->cbuf, m->cbuf + need, m->cblen - need); m->cblen -= need; |
|
continue; |
|
} |
|
m->cblen = 0; // idle |
|
return; |
|
} |
|
} |
|
|
|
static void umock_ctrl_read_cb(socket_t sock, void *arg) { |
|
struct udp_mock *m = (struct udp_mock *)arg; |
|
(void)sock; |
|
uint8_t tmp[512]; |
|
ssize_t n = recv(m->ctrl_sock, tmp, sizeof(tmp), 0); |
|
if (n <= 0) return; |
|
memcpy(m->cbuf + m->cblen, tmp, (size_t)n); m->cblen += (size_t)n; |
|
umock_ctrl_process(m); |
|
} |
|
|
|
static void umock_accept_cb(socket_t sock, void *arg) { |
|
struct udp_mock *m = (struct udp_mock *)arg; |
|
struct sockaddr_storage sa; socklen_t sl = sizeof(sa); |
|
socket_t cs = accept(sock, (struct sockaddr *)&sa, &sl); |
|
if (cs == SOCKET_INVALID) return; |
|
socket_set_nonblocking(cs); |
|
m->ctrl_sock = cs; |
|
m->cstate = 0; |
|
m->cblen = 0; |
|
m->ctrl_id = uasync_add_socket_t(m->ua, cs, umock_ctrl_read_cb, NULL, NULL, "umock_ctrl", m); |
|
} |
|
|
|
// Релей: принять обёрнутую датаграмму, сохранить dst (peer) + payload, отправить эхо с SRC=peer. |
|
static void umock_udp_read_cb(socket_t sock, void *arg) { |
|
struct udp_mock *m = (struct udp_mock *)arg; |
|
(void)sock; |
|
uint8_t raw[2048]; |
|
struct sockaddr_storage from; socklen_t fl = sizeof(from); |
|
ssize_t n = recvfrom(m->udp_sock, raw, sizeof(raw), 0, (struct sockaddr *)&from, &fl); |
|
if (n < 10 || raw[0] || raw[1] || raw[2]) return; |
|
uint8_t atyp = raw[3]; |
|
size_t hlen; |
|
if (atyp == 0x01) hlen = 10; |
|
else if (atyp == 0x04) hlen = 22; |
|
else return; |
|
if ((size_t)n < hlen) return; |
|
|
|
memset(&m->last_peer, 0, sizeof(m->last_peer)); |
|
if (atyp == 0x01) { |
|
struct sockaddr_in *sa = (struct sockaddr_in *)&m->last_peer; |
|
sa->sin_family = AF_INET; |
|
memcpy(&sa->sin_addr.s_addr, raw + 4, 4); |
|
memcpy(&sa->sin_port, raw + 8, 2); |
|
} else { |
|
struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)&m->last_peer; |
|
sa6->sin6_family = AF_INET6; |
|
memcpy(&sa6->sin6_addr, raw + 4, 16); |
|
memcpy(&sa6->sin6_port, raw + 20, 2); |
|
} |
|
size_t plen = (size_t)n - hlen; |
|
memcpy(m->last_payload, raw + hlen, plen); |
|
m->last_payload_len = plen; |
|
m->echo_count++; |
|
|
|
uint8_t out[2048]; |
|
size_t o = 0; |
|
out[o++] = 0; out[o++] = 0; out[o++] = 0; |
|
if (m->last_peer.ss_family == AF_INET) { |
|
struct sockaddr_in *sa = (struct sockaddr_in *)&m->last_peer; |
|
out[o++] = 0x01; |
|
memcpy(out + o, &sa->sin_addr.s_addr, 4); o += 4; |
|
memcpy(out + o, &sa->sin_port, 2); o += 2; |
|
} else { |
|
struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)&m->last_peer; |
|
out[o++] = 0x04; |
|
memcpy(out + o, &sa6->sin6_addr, 16); o += 16; |
|
memcpy(out + o, &sa6->sin6_port, 2); o += 2; |
|
} |
|
memcpy(out + o, m->last_payload, plen); o += plen; |
|
sendto(m->udp_sock, out, o, 0, (struct sockaddr *)&from, fl); |
|
} |
|
|
|
static int umock_start(struct udp_mock *m, struct UASYNC *ua, int listen_port) { |
|
memset(m, 0, sizeof(*m)); |
|
m->ua = ua; |
|
m->ctrl_sock = SOCKET_INVALID; |
|
m->listen_sock = socket(AF_INET, SOCK_STREAM, 0); |
|
if (m->listen_sock == SOCKET_INVALID) return -1; |
|
socket_set_reuseaddr(m->listen_sock, 1); |
|
socket_set_nonblocking(m->listen_sock); |
|
struct sockaddr_in a; memset(&a, 0, sizeof(a)); |
|
a.sin_family = AF_INET; a.sin_addr.s_addr = htonl(INADDR_LOOPBACK); a.sin_port = htons((uint16_t)listen_port); |
|
if (bind(m->listen_sock, (struct sockaddr *)&a, sizeof(a)) < 0) return -1; |
|
if (listen(m->listen_sock, 8) < 0) return -1; |
|
m->listen_id = uasync_add_socket_t(ua, m->listen_sock, umock_accept_cb, NULL, NULL, "umock_listen", m); |
|
|
|
m->udp_sock = socket(AF_INET, SOCK_DGRAM, 0); |
|
if (m->udp_sock == SOCKET_INVALID) return -1; |
|
socket_set_nonblocking(m->udp_sock); |
|
struct sockaddr_in ua_addr; memset(&ua_addr, 0, sizeof(ua_addr)); |
|
ua_addr.sin_family = AF_INET; ua_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); |
|
if (bind(m->udp_sock, (struct sockaddr *)&ua_addr, sizeof(ua_addr)) < 0) return -1; |
|
struct sockaddr_in got; socklen_t gl = sizeof(got); |
|
getsockname(m->udp_sock, (struct sockaddr *)&got, &gl); |
|
m->udp_port = ntohs(got.sin_port); |
|
m->udp_id = uasync_add_socket_t(ua, m->udp_sock, umock_udp_read_cb, NULL, NULL, "umock_udp", m); |
|
return (m->listen_id && m->udp_id) ? 0 : -1; |
|
} |
|
|
|
static void umock_stop(struct udp_mock *m) { |
|
if (m->ctrl_id) uasync_remove_socket_t(m->ua, m->ctrl_sock); |
|
if (m->ctrl_sock != SOCKET_INVALID) socket_close_wrapper(m->ctrl_sock); |
|
if (m->listen_id) uasync_remove_socket_t(m->ua, m->listen_sock); |
|
socket_close_wrapper(m->listen_sock); |
|
if (m->udp_id) uasync_remove_socket_t(m->ua, m->udp_sock); |
|
socket_close_wrapper(m->udp_sock); |
|
} |
|
|
|
static int g_su_ready = 0, g_su_err = 0; |
|
static void su_ready_cb(struct socks_udp *s, int err, void *arg) { |
|
(void)s; (void)arg; |
|
g_su_ready++; |
|
g_su_err = err; |
|
} |
|
|
|
static socket_t g_sock = SOCKET_INVALID; |
|
static int g_err = 0; |
|
static int g_done = 0; |
|
static void dial_cb(socket_t sock, int err, void *arg) { |
|
(void)arg; |
|
g_sock = sock; g_err = err; g_done = 1; |
|
} |
|
static void read_echo_cb(socket_t sock, void *arg) { |
|
int *got = (int *)arg; |
|
uint8_t b; |
|
if (recv(sock, &b, 1, 0) == 1) *got = b; |
|
} |
|
|
|
// Запустить dial, дождаться результата, вернуть 0 (успех) или err. |
|
static int run_dial(struct UASYNC *ua, struct socks_cfg *cfg, const char *target, uint16_t tport) { |
|
g_done = 0; g_sock = SOCKET_INVALID; g_err = 0; |
|
void *h = socks_dial(ua, cfg, target, tport, dial_cb, NULL, 0); |
|
if (!h) return -1; |
|
int ticks = 0; |
|
while (!g_done && ticks < 20000) { uasync_poll(ua, 10); ticks++; } |
|
if (!g_done) return -1; |
|
return g_err; |
|
} |
|
|
|
static int test1_noauth_v4(void) { |
|
struct UASYNC *ua = uasync_create(); TASSERT(ua); |
|
struct mock m; |
|
TASSERT(mock_start(&m, ua, MOCK_PORT, MOCK_NOAUTH) == 0); |
|
struct socks_cfg cfg; memset(&cfg, 0, sizeof(cfg)); |
|
strcpy(cfg.host, "127.0.0.1"); cfg.port = MOCK_PORT; |
|
|
|
TASSERT(run_dial(ua, &cfg, "127.0.0.1", 12345) == 0); |
|
TASSERT(g_sock != SOCKET_INVALID); |
|
TASSERT(m.got_connect == 1); |
|
|
|
int got = 0; |
|
void *sid = uasync_add_socket_t(ua, g_sock, read_echo_cb, NULL, NULL, "echo", &got); |
|
TASSERT(sid != NULL); |
|
uint8_t x = 'A'; |
|
send(g_sock, &x, 1, 0); |
|
int ticks = 0; |
|
while (got == 0 && ticks < 5000) { uasync_poll(ua, 10); ticks++; } |
|
TASSERT(got == 'A'); |
|
|
|
uasync_remove_socket_t(ua, g_sock); |
|
socket_close_wrapper(g_sock); |
|
mock_stop(&m); |
|
uasync_destroy(ua, 1); |
|
return 0; |
|
} |
|
|
|
static int test2_auth_domain(void) { |
|
struct UASYNC *ua = uasync_create(); TASSERT(ua); |
|
struct mock m; |
|
TASSERT(mock_start(&m, ua, MOCK_PORT, MOCK_AUTH) == 0); |
|
struct socks_cfg cfg; memset(&cfg, 0, sizeof(cfg)); |
|
strcpy(cfg.host, "127.0.0.1"); cfg.port = MOCK_PORT; |
|
strcpy(cfg.user, "alice"); strcpy(cfg.pass, "secret"); |
|
|
|
TASSERT(run_dial(ua, &cfg, "example.com", 443) == 0); |
|
TASSERT(g_sock != SOCKET_INVALID); |
|
TASSERT(m.got_connect == 1); |
|
|
|
socket_close_wrapper(g_sock); |
|
mock_stop(&m); |
|
uasync_destroy(ua, 1); |
|
return 0; |
|
} |
|
|
|
static int test3_reject(void) { |
|
struct UASYNC *ua = uasync_create(); TASSERT(ua); |
|
struct mock m; |
|
TASSERT(mock_start(&m, ua, MOCK_PORT, MOCK_REJECT) == 0); |
|
struct socks_cfg cfg; memset(&cfg, 0, sizeof(cfg)); |
|
strcpy(cfg.host, "127.0.0.1"); cfg.port = MOCK_PORT; |
|
|
|
int err = run_dial(ua, &cfg, "127.0.0.1", 12345); |
|
TASSERT(err != 0); |
|
TASSERT(g_sock == SOCKET_INVALID); |
|
|
|
mock_stop(&m); |
|
uasync_destroy(ua, 1); |
|
return 0; |
|
} |
|
|
|
static int test4_udp_associate(void) { |
|
struct UASYNC *ua = uasync_create(); TASSERT(ua); |
|
struct udp_mock m; |
|
TASSERT(umock_start(&m, ua, MOCK_PORT) == 0); |
|
|
|
socket_t lfd = socket(AF_INET, SOCK_DGRAM, 0); |
|
TASSERT(lfd != SOCKET_INVALID); |
|
socket_set_nonblocking(lfd); |
|
struct sockaddr_in lb; memset(&lb, 0, sizeof(lb)); |
|
lb.sin_family = AF_INET; lb.sin_addr.s_addr = htonl(INADDR_LOOPBACK); |
|
TASSERT(bind(lfd, (struct sockaddr *)&lb, sizeof(lb)) == 0); |
|
|
|
struct socks_cfg cfg; memset(&cfg, 0, sizeof(cfg)); |
|
strcpy(cfg.host, "127.0.0.1"); cfg.port = MOCK_PORT; |
|
|
|
g_su_ready = 0; g_su_err = 0; |
|
struct socks_udp *su = socks_udp_associate(ua, &cfg, lfd, su_ready_cb, NULL); |
|
TASSERT(su != NULL); |
|
|
|
int ticks = 0; |
|
while (g_su_ready == 0 && ticks < 20000) { uasync_poll(ua, 10); ticks++; } |
|
TASSERT(g_su_ready == 1); |
|
TASSERT(g_su_err == 0); |
|
TASSERT(m.got_associate == 1); |
|
|
|
struct sockaddr_storage peer; memset(&peer, 0, sizeof(peer)); |
|
struct sockaddr_in *p = (struct sockaddr_in *)&peer; |
|
p->sin_family = AF_INET; p->sin_addr.s_addr = htonl(INADDR_LOOPBACK); p->sin_port = htons(9999); |
|
|
|
ssize_t snd = socks_udp_sendto(su, (const uint8_t *)"hello", 5, &peer); |
|
TASSERT(snd > 0); |
|
|
|
uint8_t raw[2048]; |
|
struct sockaddr_storage from; socklen_t fl = sizeof(from); |
|
ssize_t got = -1; |
|
ticks = 0; |
|
while (ticks < 5000) { |
|
got = recvfrom(lfd, raw, sizeof(raw), 0, (struct sockaddr *)&from, &fl); |
|
if (got > 0) break; |
|
uasync_poll(ua, 10); ticks++; |
|
} |
|
TASSERT(got > 0); |
|
|
|
uint8_t payload[256]; |
|
struct sockaddr_storage src; |
|
ssize_t plen = socks_udp_unwrap(su, raw, (size_t)got, payload, sizeof(payload), &src); |
|
TASSERT(plen == 5); |
|
TASSERT(memcmp(payload, "hello", 5) == 0); |
|
TASSERT(src.ss_family == AF_INET); |
|
TASSERT(((struct sockaddr_in *)&src)->sin_port == p->sin_port); |
|
|
|
socks_udp_destroy(su); |
|
socket_close_wrapper(lfd); |
|
umock_stop(&m); |
|
uasync_destroy(ua, 1); |
|
return 0; |
|
} |
|
|
|
static int test5_udp_reassociate(void) { |
|
struct UASYNC *ua = uasync_create(); TASSERT(ua); |
|
struct udp_mock m; |
|
TASSERT(umock_start(&m, ua, MOCK_PORT) == 0); |
|
|
|
socket_t lfd = socket(AF_INET, SOCK_DGRAM, 0); |
|
TASSERT(lfd != SOCKET_INVALID); |
|
socket_set_nonblocking(lfd); |
|
struct sockaddr_in lb; memset(&lb, 0, sizeof(lb)); |
|
lb.sin_family = AF_INET; lb.sin_addr.s_addr = htonl(INADDR_LOOPBACK); |
|
TASSERT(bind(lfd, (struct sockaddr *)&lb, sizeof(lb)) == 0); |
|
|
|
struct socks_cfg cfg; memset(&cfg, 0, sizeof(cfg)); |
|
strcpy(cfg.host, "127.0.0.1"); cfg.port = MOCK_PORT; |
|
|
|
g_su_ready = 0; g_su_err = 0; |
|
struct socks_udp *su = socks_udp_associate(ua, &cfg, lfd, su_ready_cb, NULL); |
|
TASSERT(su != NULL); |
|
|
|
int ticks = 0; |
|
while (g_su_ready == 0 && ticks < 20000) { uasync_poll(ua, 10); ticks++; } |
|
TASSERT(g_su_ready == 1); |
|
TASSERT(m.got_associate == 1); |
|
|
|
// Обрыв control-канала: закрываем серверную сторону → клиент должен пересоздать ассоциацию. |
|
if (m.ctrl_id) { uasync_remove_socket_t(m.ua, m.ctrl_sock); m.ctrl_id = NULL; } |
|
if (m.ctrl_sock != SOCKET_INVALID) { socket_close_wrapper(m.ctrl_sock); m.ctrl_sock = SOCKET_INVALID; } |
|
|
|
int ready_before = g_su_ready; |
|
ticks = 0; |
|
while (g_su_ready == ready_before && ticks < 50000) { uasync_poll(ua, 10); ticks++; } // backoff ~1с |
|
TASSERT(g_su_ready > ready_before); |
|
TASSERT(m.got_associate >= 2); |
|
|
|
// После re-associate канал снова работает. |
|
struct sockaddr_storage peer; memset(&peer, 0, sizeof(peer)); |
|
struct sockaddr_in *p = (struct sockaddr_in *)&peer; |
|
p->sin_family = AF_INET; p->sin_addr.s_addr = htonl(INADDR_LOOPBACK); p->sin_port = htons(7777); |
|
|
|
TASSERT(socks_udp_sendto(su, (const uint8_t *)"ping", 4, &peer) > 0); |
|
uint8_t raw[2048]; |
|
struct sockaddr_storage from; socklen_t fl = sizeof(from); |
|
ssize_t got = -1; |
|
ticks = 0; |
|
while (ticks < 5000) { |
|
got = recvfrom(lfd, raw, sizeof(raw), 0, (struct sockaddr *)&from, &fl); |
|
if (got > 0) break; |
|
uasync_poll(ua, 10); ticks++; |
|
} |
|
TASSERT(got > 0); |
|
uint8_t payload[256]; |
|
struct sockaddr_storage src; |
|
ssize_t plen = socks_udp_unwrap(su, raw, (size_t)got, payload, sizeof(payload), &src); |
|
TASSERT(plen == 4); |
|
TASSERT(memcmp(payload, "ping", 4) == 0); |
|
|
|
socks_udp_destroy(su); |
|
socket_close_wrapper(lfd); |
|
umock_stop(&m); |
|
uasync_destroy(ua, 1); |
|
return 0; |
|
} |
|
|
|
int main(void) { |
|
debug_set_level(DEBUG_LEVEL_ERROR); |
|
socket_platform_init(); |
|
|
|
struct { const char *name; int (*fn)(void); } tests[] = { |
|
{"test1_noauth_v4", test1_noauth_v4}, |
|
{"test2_auth_domain", test2_auth_domain}, |
|
{"test3_reject", test3_reject}, |
|
{"test4_udp_associate", test4_udp_associate}, |
|
{"test5_udp_reassociate", test5_udp_reassociate}, |
|
}; |
|
int n = (int)(sizeof(tests) / sizeof(tests[0])); |
|
int passed = 0; |
|
for (int i = 0; i < n; i++) { |
|
test_failed = 0; |
|
int r = tests[i].fn(); |
|
if (r == 0) { passed++; DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "PASS: %s", tests[i].name); } |
|
else { DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "FAIL: %s", tests[i].name); } |
|
} |
|
socket_platform_cleanup(); |
|
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "Results: %d/%d passed", passed, n); |
|
return passed == n ? 0 : 1; |
|
}
|
|
|