UDP, --proto, dst custom port

This commit is contained in:
ruti 2024-04-23 08:47:27 +03:00
parent f9fa5a14f6
commit a48a2e87a3
13 changed files with 833 additions and 399 deletions

View File

@ -1,7 +1,7 @@
TARGET = ciadpi TARGET = ciadpi
CC ?= gcc CC ?= gcc
CFLAGS += -std=c99 -O2 -D_XOPEN_SOURCE=500 CFLAGS += -std=c99 -O2 -D_XOPEN_SOURCE=500
SOURCES = packets.c main.c conev.c proxy.c desync.c mpool.c SOURCES = packets.c main.c conev.c proxy.c desync.c mpool.c extend.c
all: all:
$(CC) $(CFLAGS) $(SOURCES) -I . -o $(TARGET) $(CC) $(CFLAGS) $(SOURCES) -I . -o $(TARGET)

View File

@ -1,3 +1,4 @@
#pragma once
#include <stdint.h> #include <stdint.h>
#ifndef __linux__ #ifndef __linux__
@ -35,6 +36,7 @@ enum eid {
EV_IGNORE, EV_IGNORE,
EV_TUNNEL, EV_TUNNEL,
EV_PRE_TUNNEL, EV_PRE_TUNNEL,
EV_UDP_TUNNEL,
EV_DESYNC EV_DESYNC
}; };
@ -42,7 +44,7 @@ enum eid {
#define FLAG_S5 2 #define FLAG_S5 2
#define FLAG_CONN 4 #define FLAG_CONN 4
#ifndef CONEV_H #ifdef EID_STR
char *eid_name[] = { char *eid_name[] = {
"EV_ACCEPT", "EV_ACCEPT",
"EV_REQUEST", "EV_REQUEST",
@ -50,6 +52,7 @@ char *eid_name[] = {
"EV_IGNORE", "EV_IGNORE",
"EV_TUNNEL", "EV_TUNNEL",
"EV_PRE_TUNNEL", "EV_PRE_TUNNEL",
"EV_UDP_TUNNEL",
"EV_DESYNC" "EV_DESYNC"
}; };
#endif #endif

View File

@ -131,7 +131,13 @@ ssize_t send_fake(int sfd, char *buffer,
return -1; return -1;
} }
} }
struct packet pkt = cnt != IS_HTTP ? fake_tls : fake_http; struct packet pkt;
if (opt->fake_data.data) {
pkt = opt->fake_data;
}
else {
pkt = cnt != IS_HTTP ? fake_tls : fake_http;
}
size_t psz = pkt.size; size_t psz = pkt.size;
int ffd = memfd_create("name", O_RDWR); int ffd = memfd_create("name", O_RDWR);
@ -218,7 +224,13 @@ ssize_t send_fake(int sfd, char *buffer,
ssize_t send_fake(int sfd, char *buffer, ssize_t send_fake(int sfd, char *buffer,
int cnt, long pos, int fa, struct desync_params *opt) int cnt, long pos, int fa, struct desync_params *opt)
{ {
struct packet pkt = cnt != IS_HTTP ? fake_tls : fake_http; struct packet pkt;
if (opt->fake_data.data) {
pkt = opt->fake_data;
}
else {
pkt = cnt != IS_HTTP ? fake_tls : fake_http;
}
size_t psz = pkt.size; size_t psz = pkt.size;
char path[MAX_PATH + 1]; char path[MAX_PATH + 1];
@ -439,10 +451,7 @@ ssize_t desync(int sfd, char *buffer, size_t bfsize,
// desync // desync
long lp = offset; long lp = offset;
if (!type && params.de_known) { for (int i = 0; i < dp.parts_n; i++) {
// cancel
}
else for (int i = 0; i < dp.parts_n; i++) {
struct part part = dp.parts[i]; struct part part = dp.parts[i];
// change pos // change pos

413
extend.c Normal file
View File

@ -0,0 +1,413 @@
#ifdef _WIN32
#include <ws2tcpip.h>
#ifndef TCP_MAXRT
#define TCP_MAXRT 5
#endif
#else
#include <arpa/inet.h>
#include <netinet/tcp.h>
#endif
#include <string.h>
#include <proxy.h>
#include <error.h>
#include <params.h>
#include <desync.h>
#include <packets.h>
int set_timeout(int fd, unsigned int s)
{
#ifdef __linux__
if (setsockopt(fd, IPPROTO_TCP,
TCP_USER_TIMEOUT, (char *)&s, sizeof(s))) {
uniperror("setsockopt TCP_USER_TIMEOUT");
return -1;
}
#else
#ifdef _WIN32
if (setsockopt(fd, IPPROTO_TCP,
TCP_MAXRT, (char *)&s, sizeof(s))) {
uniperror("setsockopt TCP_MAXRT");
return -1;
}
#endif
#endif
return 0;
}
int mode_add_get(struct sockaddr_ina *dst, int m)
{
// m < 0: get, m > 0: set, m == 0: delete
time_t t;
struct elem *val;
char *str = (char *)&dst->in;
int len = sizeof(dst->sa.sa_family);
if (dst->sa.sa_family == AF_INET) {
len = sizeof(dst->in);
}
else {
len = sizeof(dst->in6) - sizeof(dst->in6.sin6_scope_id);
}
len -= sizeof(dst->sa.sa_family);
if (m == 0) {
mem_delete(params.mempool, str, len);
return 0;
}
else if (m > 0) {
time(&t);
val = mem_add(params.mempool, str, len);
if (!val) {
uniperror("mem_add");
return -1;
}
val->m = m;
val->time = t;
return 0;
}
val = mem_get(params.mempool, str, len);
if (!val) {
return -1;
}
time(&t);
if (t > val->time + params.cache_ttl) {
LOG(LOG_S, "time=%ld, now=%ld, ignore\n", val->time, t);
return 0;
}
return val->m;
}
int ext_connect(struct poolhd *pool, struct eval *val,
struct sockaddr_ina *dst, int next, int m)
{
struct desync_params *dp = &params.dp[m];
if (dp->to_ip == 2) {
struct sockaddr_ina addr = { .in6 = dp->addr };
if (!addr.in.sin_port) {
addr.in.sin_port = dst->in.sin_port;
}
return create_conn(pool, val, &addr, next);
}
return create_conn(pool, val, dst, next);
}
int connect_hook(struct poolhd *pool, struct eval *val,
struct sockaddr_ina *dst, int next)
{
int m = mode_add_get(dst, -1);
val->cache = (m == 0);
val->attempt = m < 0 ? 0 : m;
if (params.late_conn) {
val->type = EV_DESYNC;
if (resp_error(val->fd, 0, val->flag) < 0) {
perror("send");
return -1;
}
val->in6 = dst->in6;
return 0;
}
return ext_connect(pool, val, dst, next, m);
}
int reconnect(struct poolhd *pool, struct eval *val, int m)
{
struct eval *client = val->pair;
if (ext_connect(pool, client,
(struct sockaddr_ina *)&val->in6, EV_DESYNC, m)) {
return -1;
}
val->pair = 0;
del_event(pool, val);
client->type = EV_IGNORE;
client->attempt = m;
client->cache = 1;
return 0;
}
bool check_host(struct mphdr *hosts, struct eval *val)
{
char *host;
int len;
if (!(len = parse_tls(val->buff.data, val->buff.size, &host))) {
len = parse_http(val->buff.data, val->buff.size, &host, 0);
}
return mem_get(hosts, host, len) != 0;
}
bool check_proto_tcp(int proto, struct eval *val)
{
if ((proto & IS_HTTP) &&
is_http(val->buff.data, val->buff.size)) {
return 1;
}
if ((proto & IS_HTTPS) &&
is_tls_chello(val->buff.data, val->buff.size)) {
return 1;
}
return 0;
}
bool check_proto_udp(int proto, char *buffer, size_t n)
{
if ((proto & IS_QUIC) &&
is_quic_inital(buffer, n)) {
return 1;
}
if ((proto & IS_DNS) &&
is_dns_req(buffer, n)) {
return 1;
}
return 0;
}
int on_torst(struct poolhd *pool, struct eval *val)
{
int m = val->pair->attempt + 1;
for (; m < params.dp_count; m++) {
struct desync_params *dp = &params.dp[m];
if (!(dp->detect & DETECT_TORST)) {
continue;
}
if ((!dp->hosts || check_host(dp->hosts, val->pair)) &&
(!dp->proto || check_proto_tcp(dp->proto, val))) {
break;
}
}
if (m >= params.dp_count) {
mode_add_get(
(struct sockaddr_ina *)&val->in6, 0);
return -1;
}
return reconnect(pool, val, m);
}
int on_response(struct poolhd *pool, struct eval *val,
char *resp, ssize_t sn)
{
int m = val->pair->attempt + 1;
char *req = val->pair->buff.data;
ssize_t qn = val->pair->buff.size;
for (; m < params.dp_count; m++) {
struct desync_params *dp = &params.dp[m];
switch (0) {
default:
if ((dp->detect & DETECT_HTTP_LOCAT)
&& is_http_redirect(req, qn, resp, sn)) {
break;
}
else if ((dp->detect & DETECT_TLS_INVSID)
&& neq_tls_sid(req, qn, resp, sn)
&& !neq_tls_sid(
fake_tls.data, fake_tls.size, resp, sn)) {
break;
}
else if ((dp->detect & DETECT_TLS_ALERT)
&& is_tls_alert(resp, sn)) {
break;
}
else if (dp->detect & DETECT_HTTP_CLERR) {
int code = get_http_code(resp, sn);
if (code > 400 && code < 451 && code != 429) {
break;
}
}
continue;
}
if ((!dp->hosts || check_host(dp->hosts, val->pair)) &&
(!dp->proto || check_proto_tcp(dp->proto, val))) {
break;
}
}
if (m < params.dp_count) {
return reconnect(pool, val, m);
}
return -1;
}
int on_tunnel_check(struct poolhd *pool, struct eval *val,
char *buffer, size_t bfsize, int out)
{
if (out) {
return on_tunnel(pool, val, buffer, bfsize, out);
}
ssize_t n = recv(val->fd, buffer, bfsize, 0);
if (n < 1) {
uniperror("recv");
switch (get_e()) {
case ECONNRESET:
case ETIMEDOUT:
break;
default: return -1;
}
return on_torst(pool, val);
}
//
if (on_response(pool, val, buffer, n) == 0) {
return 0;
}
struct eval *pair = val->pair;
ssize_t sn = send(pair->fd, buffer, n, 0);
if (n != sn) {
uniperror("send");
return -1;
}
val->type = EV_TUNNEL;
pair->type = EV_TUNNEL;
free(pair->buff.data);
pair->buff.data = 0;
pair->buff.size = 0;
if (params.timeout &&
set_timeout(val->fd, 0)) {
return -1;
}
int m = pair->attempt;
if (!pair->cache) {
return 0;
}
if (m == 0) {
LOG(LOG_S, "delete ip: m=%d\n", m);
} else {
LOG(LOG_S, "save ip: m=%d\n", m);
}
return mode_add_get(
(struct sockaddr_ina *)&val->in6, m);
}
int on_desync(struct poolhd *pool, struct eval *val,
char *buffer, size_t bfsize)
{
if (val->flag == FLAG_CONN) {
if (mod_etype(pool, val, POLLOUT, 0)) {
uniperror("mod_etype");
return -1;
}
val = val->pair;
}
ssize_t n;
int m = val->attempt;
LOG((m ? LOG_S : LOG_L), "desync params index: %d\n", m);
if (!val->buff.data) {
n = recv(val->fd, buffer, bfsize, 0);
if (n <= 0) {
if (n) uniperror("recv data");
return -1;
}
val->buff.size = n;
val->recv_count += n;
if (!(val->buff.data = malloc(n))) {
uniperror("malloc");
return -1;
}
memcpy(val->buff.data, buffer, n);
if (!m) for (; m < params.dp_count; m++) {
struct desync_params *dp = &params.dp[m];
if (!dp->detect &&
(!dp->hosts || check_host(dp->hosts, val)) &&
(!dp->proto || check_proto_tcp(dp->proto, val))) {
break;
}
}
if (m >= params.dp_count) {
return -1;
}
val->attempt = m;
if (params.late_conn) {
return ext_connect(pool, val,
(struct sockaddr_ina *)&val->in6, EV_DESYNC, m);
}
}
else {
n = val->buff.size;
memcpy(buffer, val->buff.data, n);
}
if (params.timeout &&
set_timeout(val->pair->fd, params.timeout)) {
return -1;
}
ssize_t sn = desync(val->pair->fd, buffer, bfsize,
n, val->buff.offset, (struct sockaddr *)&val->pair->in6, m);
if (sn < 0) {
return -1;
}
if (sn < n) {
val->buff.offset = sn;
if (mod_etype(pool, val->pair, POLLOUT, 1)) {
uniperror("mod_etype");
return -1;
}
val->pair->type = EV_DESYNC;
return 0;
}
val->type = EV_TUNNEL;
val->pair->type = EV_PRE_TUNNEL;
return 0;
}
ssize_t udp_hook(struct eval *val,
char *buffer, size_t n, struct sockaddr_ina *dst)
{
int m = val->attempt;
struct desync_params *dp = &params.dp[m];
if (val->flag != FLAG_CONN) {
if (!m) for (; m < params.dp_count; m++) {
dp = &params.dp[m];
if ((!dp->proto || check_proto_udp(dp->proto, buffer, n))) {
break;
}
}
if (m >= params.dp_count) {
return -1;
}
val->attempt = m;
val->flag = FLAG_CONN;
}
struct sockaddr_ina addr;
if (dp->to_ip) {
addr.in6 = dp->addr;
if (dst->sa.sa_family == AF_INET6) {
map_fix(&addr, 6);
}
else {
map_fix(&addr, 0);
}
if (!addr.in.sin_port) {
addr.in.sin_port = dst->in.sin_port;
}
dst = &addr;
}
return sendto(val->fd, buffer, n, 0, &dst->sa, sizeof(*dst));
}

11
extend.h Normal file
View File

@ -0,0 +1,11 @@
int connect_hook(struct poolhd *pool, struct eval *val,
struct sockaddr_ina *dst, int next);
int on_tunnel_check(struct poolhd *pool, struct eval *val,
char *buffer, size_t bfsize, int out);
int on_desync(struct poolhd *pool, struct eval *val,
char *buffer, size_t bfsize);
ssize_t udp_hook(struct eval *val,
char *buffer, size_t n, struct sockaddr_ina *dst);

124
main.c
View File

@ -49,6 +49,7 @@ struct params params = {
.cache_ttl = 100800, .cache_ttl = 100800,
.ipv6 = 1, .ipv6 = 1,
.resolve = 1, .resolve = 1,
.udp = 1,
.max_open = 512, .max_open = 512,
.bfsize = 16384, .bfsize = 16384,
.baddr = { .baddr = {
@ -63,24 +64,25 @@ const char help_text[] = {
" -p, --port <num> Listening port, default 1080\n" " -p, --port <num> Listening port, default 1080\n"
" -c, --max-conn <count> Connection count limit, default 512\n" " -c, --max-conn <count> Connection count limit, default 512\n"
" -N, --no-domain Deny domain resolving\n" " -N, --no-domain Deny domain resolving\n"
" -U, --no-udp Deny UDP association\n"
" -I --conn-ip <ip> Connection binded IP, default ::\n" " -I --conn-ip <ip> Connection binded IP, default ::\n"
" -b, --buf-size <size> Buffer size, default 16384\n" " -b, --buf-size <size> Buffer size, default 16384\n"
" -x, --debug <level> Print logs, 0, 1 or 2\n" " -x, --debug <level> Print logs, 0, 1 or 2\n"
" -g, --def-ttl <num> TTL for all outgoing connections\n" " -g, --def-ttl <num> TTL for all outgoing connections\n"
// desync options // desync options
" -K, --desync-known Desync only HTTP and TLS with SNI\n"
#ifdef TCP_FASTOPEN_CONNECT #ifdef TCP_FASTOPEN_CONNECT
" -F, --tfo Enable TCP Fast Open\n" " -F, --tfo Enable TCP Fast Open\n"
#endif #endif
" -L, --late-conn Waiting for request before connecting\n" " -L, --late-conn Waiting for request before connecting\n"
" -A, --auto[=t,r,c,s,a,n] Try desync params after this option\n" " -A, --auto[=t,r,c,s,a,n] Try desync params after this option\n"
" Detect: torst,redirect,cl_err,sid_inv,alert,nop\n" " Detect: torst,redirect,cl_err,sid_inv,alert,none\n"
" -u, --cache-ttl <sec> Lifetime of cached desync params for IP\n" " -u, --cache-ttl <sec> Lifetime of cached desync params for IP\n"
#ifdef TIMEOUT_SUPPORT #ifdef TIMEOUT_SUPPORT
" -T, --timeout <sec> Timeout waiting for response, after which trigger auto\n" " -T, --timeout <sec> Timeout waiting for response, after which trigger auto\n"
#endif #endif
" -K, --proto[=t,h,d,q] Protocol whitelist: tls,http,dns,quic\n"
" -H, --hosts <file|:str> Hosts whitelist\n" " -H, --hosts <file|:str> Hosts whitelist\n"
" -D, --dst <addr> Custom destination IP\n" " -D, --dst <ip[:port]> Custom destination IP\n"
" -s, --split <n[+s]> Split packet at n\n" " -s, --split <n[+s]> Split packet at n\n"
" +s - add SNI offset\n" " +s - add SNI offset\n"
" +h - add HTTP Host offset\n" " +h - add HTTP Host offset\n"
@ -93,8 +95,7 @@ const char help_text[] = {
#ifdef __linux__ #ifdef __linux__
" -S, --md5sig Add MD5 Signature option for fake packets\n" " -S, --md5sig Add MD5 Signature option for fake packets\n"
#endif #endif
" -l, --fake-tls <f|:str>\n" " -l, --fake-data <f|:str> Set custom fake packet\n"
" -j, --fake-http <f|:str> Set custom fake packet\n"
" -n, --tls-sni <str> Change SNI in fake ClientHello\n" " -n, --tls-sni <str> Change SNI in fake ClientHello\n"
#endif #endif
" -e, --oob-data <f|:str> Set custom OOB data, filename or :string\n" " -e, --oob-data <f|:str> Set custom OOB data, filename or :string\n"
@ -106,6 +107,7 @@ const char help_text[] = {
const struct option options[] = { const struct option options[] = {
{"no-domain", 0, 0, 'N'}, {"no-domain", 0, 0, 'N'},
{"no-ipv6", 0, 0, 'X'}, {"no-ipv6", 0, 0, 'X'},
{"no-udp", 0, 0, 'U'},
{"help", 0, 0, 'h'}, {"help", 0, 0, 'h'},
{"version", 0, 0, 'v'}, {"version", 0, 0, 'v'},
{"ip", 1, 0, 'i'}, {"ip", 1, 0, 'i'},
@ -115,7 +117,6 @@ const struct option options[] = {
{"max-conn", 1, 0, 'c'}, {"max-conn", 1, 0, 'c'},
{"debug", 1, 0, 'x'}, {"debug", 1, 0, 'x'},
{"desync-known ", 0, 0, 'K'},
#ifdef TCP_FASTOPEN_CONNECT #ifdef TCP_FASTOPEN_CONNECT
{"tfo ", 0, 0, 'F'}, {"tfo ", 0, 0, 'F'},
#endif #endif
@ -125,6 +126,7 @@ const struct option options[] = {
#ifdef TIMEOUT_SUPPORT #ifdef TIMEOUT_SUPPORT
{"timeout", 1, 0, 'T'}, {"timeout", 1, 0, 'T'},
#endif #endif
{"proto", 2, 0, 'K'},
{"hosts", 1, 0, 'H'}, {"hosts", 1, 0, 'H'},
{"dst", 1, 0, 'D'}, {"dst", 1, 0, 'D'},
{"split", 1, 0, 's'}, {"split", 1, 0, 's'},
@ -137,8 +139,7 @@ const struct option options[] = {
#ifdef __linux__ #ifdef __linux__
{"md5sig", 0, 0, 'S'}, {"md5sig", 0, 0, 'S'},
#endif #endif
{"fake-tls", 1, 0, 'l'}, {"fake-data", 1, 0, 'l'},
{"fake-http", 1, 0, 'j'},
{"tls-sni", 1, 0, 'n'}, {"tls-sni", 1, 0, 'n'},
#endif #endif
{"oob-data", 1, 0, 'e'}, {"oob-data", 1, 0, 'e'},
@ -250,7 +251,7 @@ struct mphdr *parse_hosts(char *buffer, size_t size)
char *e = buffer, *s = buffer; char *e = buffer, *s = buffer;
for (; e <= end; e++) { for (; e <= end; e++) {
if (*e != ' ' && *e != '\n' && e != end) { if (*e != ' ' && *e != '\n' && *e != '\r' && e != end) {
continue; continue;
} }
if (s == e) { if (s == e) {
@ -288,6 +289,46 @@ int get_addr(const char *str, struct sockaddr_ina *addr)
} }
int get_addr_with_port(const char *str, struct sockaddr_ina *addr)
{
uint16_t port = 0;
char *s = (char *)str, *e = 0;
char *end = 0, *p = s;
if (*str == '[') {
e = strchr(str, ']');
if (!e) return -1;
s++; p = e + 1;
}
p = strchr(p, ':');
if (p) {
long val = strtol(p + 1, &end, 0);
if (val <= 0 || val > 0xffff || *end)
return -1;
else
port = htons(val);
if (!e) e = p;
}
if (!e) {
e = strchr(s, 0);
}
if ((e - s) < 7) {
return -1;
}
char str_ip[(e - s) + 1];
memcpy(str_ip, s, e - s);
str_ip[e - s] = 0;
if (get_addr(str_ip, addr) < 0) {
return -1;
}
if (port) {
addr->in6.sin6_port = port;
}
return 0;
}
int get_default_ttl() int get_default_ttl()
{ {
int orig_ttl = -1, fd; int orig_ttl = -1, fd;
@ -368,18 +409,14 @@ void clear_params(void)
free(s.parts); free(s.parts);
s.parts = 0; s.parts = 0;
} }
if (s.fake_data.data) {
FREE(s.fake_data.data, s.fake_data.size);
s.fake_data.data = 0;
}
} }
free(params.dp); free(params.dp);
params.dp = 0; params.dp = 0;
} }
if (fake_tls.data != tls_data) {
FREE(fake_tls.data, fake_tls.size);
fake_tls.data = tls_data;
}
if (fake_http.data != http_data) {
FREE(fake_http.data, fake_http.size);
fake_http.data = http_data;
}
if (oob_data.data != oob_char) { if (oob_data.data != oob_char) {
FREE(oob_data.data, oob_data.size); FREE(oob_data.data, oob_data.size);
oob_data.data = oob_char; oob_data.data = oob_char;
@ -444,6 +481,10 @@ int main(int argc, char **argv)
case 'X': case 'X':
params.ipv6 = 0; params.ipv6 = 0;
break; break;
case 'U':
params.udp = 0;
break;
case 'h': case 'h':
printf(help_text); printf(help_text);
clear_params(); clear_params();
@ -501,10 +542,6 @@ int main(int argc, char **argv)
params.late_conn = 1; params.late_conn = 1;
break; break;
case 'K':
params.de_known = 1;
break;
case 'F': case 'F':
params.tfo = 1; params.tfo = 1;
break; break;
@ -570,6 +607,35 @@ int main(int argc, char **argv)
params.timeout = val; params.timeout = val;
break; break;
case 'K':
if (!optarg) {
dp->proto |= 0xffffffff;
break;
}
end = optarg;
while (end && !invalid) {
switch (*end) {
case 't':
dp->proto |= IS_HTTPS;
break;
case 'h':
dp->proto |= IS_HTTP;
break;
case 'd':
dp->proto |= IS_DNS;
break;
case 'q':
dp->proto |= IS_QUIC;
break;
default:
invalid = 1;
continue;
}
end = strchr(end, ',');
if (end) end++;
}
break;
case 'H':; case 'H':;
char *data = ftob(optarg, &val); char *data = ftob(optarg, &val);
if (!data) { if (!data) {
@ -585,7 +651,7 @@ int main(int argc, char **argv)
break; break;
case 'D': case 'D':
if (get_addr(optarg, (struct sockaddr_ina *)&dp->addr) < 0) if (get_addr_with_port(optarg, (struct sockaddr_ina *)&dp->addr) < 0)
invalid = 1; invalid = 1;
else else
dp->to_ip = 2; dp->to_ip = 2;
@ -652,16 +718,8 @@ int main(int argc, char **argv)
break; break;
case 'l': case 'l':
fake_tls.data = ftob(optarg, &fake_tls.size); dp->fake_data.data = ftob(optarg, &dp->fake_data.size);
if (!fake_tls.data) { if (!dp->fake_data.data) {
uniperror("read/parse");
invalid = 1;
}
break;
case 'j':
fake_http.data = ftob(optarg, &fake_http.size);
if (!fake_http.data) {
uniperror("read/parse"); uniperror("read/parse");
invalid = 1; invalid = 1;
} }
@ -750,7 +808,7 @@ int main(int argc, char **argv)
clear_params(); clear_params();
return -1; return -1;
} }
if (dp->hosts) { if (dp->hosts || dp->proto) {
dp = add((void *)&params.dp, dp = add((void *)&params.dp,
&params.dp_count, sizeof(struct desync_params)); &params.dp_count, sizeof(struct desync_params));
if (!dp) { if (!dp) {

View File

@ -97,4 +97,4 @@ void mem_destroy(struct mphdr *hdr)
free(e); free(e);
} }
free(hdr); free(hdr);
} }

View File

@ -141,9 +141,17 @@ int change_tls_sni(const char *host, char *buffer, size_t bsize)
} }
bool is_tls_chello(char *buffer, size_t bsize)
{
return (bsize > 5 &&
ANTOHS(buffer, 0) == 0x1603 &&
buffer[5] == 0x01);
}
int parse_tls(char *buffer, size_t bsize, char **hs) int parse_tls(char *buffer, size_t bsize, char **hs)
{ {
if (ANTOHS(buffer, 0) != 0x1603) { if (!is_tls_chello(buffer, bsize)) {
return 0; return 0;
} }
size_t sni_offs = find_tls_ext_offset(0x00, buffer, bsize); size_t sni_offs = find_tls_ext_offset(0x00, buffer, bsize);
@ -312,7 +320,7 @@ bool neq_tls_sid(char *req, size_t qn, char *resp, size_t sn)
if (qn < 75 || sn < 75) { if (qn < 75 || sn < 75) {
return 0; return 0;
} }
if (ANTOHS(req, 0) != 0x1603 if (!is_tls_chello(req, qn)
|| ANTOHS(resp, 0) != 0x1603) { || ANTOHS(resp, 0) != 0x1603) {
return 0; return 0;
} }
@ -330,6 +338,21 @@ bool is_tls_alert(char *resp, size_t sn) {
} }
bool is_dns_req(char *buffer, size_t n)
{
if (n < 12) {
return 0;
}
return !memcmp(buffer + 2, "\1\0\0\1\0\0\0\0\0\0", 10);
}
bool is_quic_inital(char *buffer, size_t bsize)
{
return (bsize > 64 && (buffer[0] & 0xc0) == 0xc0);
}
int mod_http(char *buffer, size_t bsize, int m) int mod_http(char *buffer, size_t bsize, int m)
{ {
char *host = 0, *par; char *host = 0, *par;

View File

@ -6,6 +6,8 @@
#define IS_UNKNOWN 0 #define IS_UNKNOWN 0
#define IS_HTTP 1 #define IS_HTTP 1
#define IS_HTTPS 2 #define IS_HTTPS 2
#define IS_QUIC 4
#define IS_DNS 8
#define MH_HMIX 1 #define MH_HMIX 1
#define MH_SPACE 2 #define MH_SPACE 2
@ -16,8 +18,12 @@ extern char http_data[43];
int change_tls_sni(const char *host, char *buffer, size_t bsize); int change_tls_sni(const char *host, char *buffer, size_t bsize);
bool is_tls_chello(char *buffer, size_t bsize);
int parse_tls(char *buffer, size_t bsize, char **hs); int parse_tls(char *buffer, size_t bsize, char **hs);
bool is_http(char *buffer, size_t bsize);
int parse_http(char *buffer, size_t bsize, char **hs, uint16_t *port); int parse_http(char *buffer, size_t bsize, char **hs, uint16_t *port);
int mod_http(char *buffer, size_t bsize, int m); int mod_http(char *buffer, size_t bsize, int m);
@ -31,3 +37,7 @@ bool neq_tls_sid(char *req, size_t qn, char *resp, size_t sn);
bool is_tls_alert(char *resp, size_t sn); bool is_tls_alert(char *resp, size_t sn);
int part_tls(char *buffer, size_t bsize, ssize_t n, long pos); int part_tls(char *buffer, size_t bsize, ssize_t n, long pos);
bool is_dns_req(char *buffer, size_t n);
bool is_quic_inital(char *buffer, size_t bsize);

View File

@ -45,16 +45,26 @@ struct part {
long pos; long pos;
}; };
struct packet {
ssize_t size;
char *data;
};
struct desync_params { struct desync_params {
int ttl; int ttl;
char *ip_options; char *ip_options;
ssize_t ip_options_len; ssize_t ip_options_len;
char md5sig; char md5sig;
struct packet fake_data;
int parts_n; int parts_n;
struct part *parts; struct part *parts;
int mod_http; int mod_http;
int tlsrec_n; int tlsrec_n;
struct part *tlsrec; struct part *tlsrec;
int proto;
int detect; int detect;
struct mphdr *hosts; struct mphdr *hosts;
@ -63,7 +73,6 @@ struct desync_params {
}; };
struct params { struct params {
char de_known;
int dp_count; int dp_count;
struct desync_params *dp; struct desync_params *dp;
long sfdelay; long sfdelay;
@ -77,6 +86,7 @@ struct params {
long cache_ttl; long cache_ttl;
char ipv6; char ipv6;
char resolve; char resolve;
char udp;
int max_open; int max_open;
int debug; int debug;
size_t bfsize; size_t bfsize;
@ -86,10 +96,6 @@ struct params {
extern struct params params; extern struct params params;
struct packet {
ssize_t size;
char *data;
};
extern struct packet fake_tls; extern struct packet fake_tls;
extern struct packet fake_http; extern struct packet fake_http;
extern struct packet oob_data; extern struct packet oob_data;

560
proxy.c
View File

@ -1,17 +1,16 @@
#define _GNU_SOURCE #define _GNU_SOURCE
#define EID_STR
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <signal.h> #include <signal.h>
#include <time.h>
#include <proxy.h> #include <proxy.h>
#include <params.h> #include <params.h>
#include <conev.h> #include <conev.h>
#include <desync.h> #include <extend.h>
#include <packets.h>
#include <error.h> #include <error.h>
#ifdef _WIN32 #ifdef _WIN32
@ -19,9 +18,6 @@
#include <ws2tcpip.h> #include <ws2tcpip.h>
#define close(fd) closesocket(fd) #define close(fd) closesocket(fd)
#ifndef TCP_MAXRT
#define TCP_MAXRT 5
#endif
#else #else
#include <errno.h> #include <errno.h>
#include <unistd.h> #include <unistd.h>
@ -65,6 +61,25 @@ void map_fix(struct sockaddr_ina *addr, char f6)
} }
static inline char addr_equ(
struct sockaddr_ina *a, struct sockaddr_ina *b)
{
if (a->in.sin_port != b->in.sin_port) {
return 0;
}
if (a->sa.sa_family == AF_INET) {
return
*((uint32_t *)(&a->in.sin_addr)) ==
*((uint32_t *)(&b->in.sin_addr));
}
return
*((uint64_t *)(&a->in6.sin6_addr)) ==
*((uint64_t *)(&b->in6.sin6_addr)) &&
*((uint64_t *)(&a->in6.sin6_addr) + 1) ==
*((uint64_t *)(&b->in6.sin6_addr) + 1);
}
static inline int nb_socket(int domain, int type) static inline int nb_socket(int domain, int type)
{ {
#ifdef __linux__ #ifdef __linux__
@ -96,33 +111,12 @@ static inline int nb_socket(int domain, int type)
} }
int set_timeout(int fd, unsigned int s)
{
#ifdef __linux__
if (setsockopt(fd, IPPROTO_TCP,
TCP_USER_TIMEOUT, (char *)&s, sizeof(s))) {
uniperror("setsockopt TCP_USER_TIMEOUT");
return -1;
}
#else
#ifdef _WIN32
if (setsockopt(fd, IPPROTO_TCP,
TCP_MAXRT, (char *)&s, sizeof(s))) {
uniperror("setsockopt TCP_MAXRT");
return -1;
}
#endif
#endif
return 0;
}
int resolve(char *host, int len, int resolve(char *host, int len,
struct sockaddr_ina *addr) struct sockaddr_ina *addr, int type)
{ {
struct addrinfo hints = {0}, *res = 0; struct addrinfo hints = {0}, *res = 0;
hints.ai_socktype = SOCK_STREAM; hints.ai_socktype = type;
hints.ai_flags = AI_ADDRCONFIG; hints.ai_flags = AI_ADDRCONFIG;
hints.ai_family = params.ipv6 ? AF_UNSPEC : AF_INET; hints.ai_family = params.ipv6 ? AF_UNSPEC : AF_INET;
@ -227,7 +221,7 @@ int s4_get_addr(char *buff, size_t n,
if (len < 3 || len > 255) { if (len < 3 || len > 255) {
return -1; return -1;
} }
if (resolve(id_end + 1, len, dst)) { if (resolve(id_end + 1, len, dst, SOCK_STREAM)) {
LOG(LOG_E, "not resolved: %.*s\n", len, id_end + 1); LOG(LOG_E, "not resolved: %.*s\n", len, id_end + 1);
return -1; return -1;
} }
@ -241,25 +235,21 @@ int s4_get_addr(char *buff, size_t n,
} }
int s5_get_addr(char *buffer, ssize_t n, int s5_get_addr(char *buffer, size_t n,
struct sockaddr_ina *addr) struct sockaddr_ina *addr, int type)
{ {
if (n < S_SIZE_MIN) { if (n < S_SIZE_MIN) {
LOG(LOG_E, "ss: request to small\n"); LOG(LOG_E, "ss: request to small\n");
return -1; return -S_ER_GEN;
} }
struct s5_req *r = (struct s5_req *)buffer; struct s5_req *r = (struct s5_req *)buffer;
int o = (r->atp == S_ATP_I4 ? S_SIZE_I4 : size_t o = (r->atp == S_ATP_I4 ? S_SIZE_I4 :
(r->atp == S_ATP_ID ? r->id.len + S_SIZE_ID : (r->atp == S_ATP_ID ? r->id.len + S_SIZE_ID :
(r->atp == S_ATP_I6 ? S_SIZE_I6 : 0))); (r->atp == S_ATP_I6 ? S_SIZE_I6 : 0)));
if (n < o) { if (n < o) {
LOG(LOG_E, "ss: bad request\n"); LOG(LOG_E, "ss: bad request\n");
return S_ER_GEN; return -S_ER_GEN;
}
if (r->cmd != S_CMD_CONN) {
LOG(LOG_E, "ss: unsupported cmd: 0x%x\n", r->cmd);
return S_ER_CMD;
} }
switch (r->atp) { switch (r->atp) {
case S_ATP_I4: case S_ATP_I4:
@ -269,24 +259,55 @@ int s5_get_addr(char *buffer, ssize_t n,
case S_ATP_ID: case S_ATP_ID:
if (!params.resolve) { if (!params.resolve) {
return S_ER_ATP; return -S_ER_ATP;
} }
if (r->id.len < 3 || if (r->id.len < 3 ||
resolve(r->id.domain, r->id.len, addr)) { resolve(r->id.domain, r->id.len, addr, type)) {
LOG(LOG_E, "not resolved: %.*s\n", r->id.len, r->id.domain); LOG(LOG_E, "not resolved: %.*s\n", r->id.len, r->id.domain);
return S_ER_HOST; return -S_ER_HOST;
} }
break; break;
case S_ATP_I6: case S_ATP_I6:
if (!params.ipv6) if (!params.ipv6)
return S_ER_ATP; return -S_ER_ATP;
else { else {
addr->in6.sin6_family = AF_INET6; addr->in6.sin6_family = AF_INET6;
addr->in6.sin6_addr = r->i6; addr->in6.sin6_addr = r->i6;
} }
} }
addr->in.sin_port = *(uint16_t *)&buffer[o - 2]; addr->in.sin_port = *(uint16_t *)&buffer[o - 2];
return o;
}
int s5_set_addr(char *buffer, size_t n,
struct sockaddr_ina *addr, char end)
{
struct s5_req *r = (struct s5_req *)buffer;
if (n < S_SIZE_I4) {
return -1;
}
if (addr->sa.sa_family == AF_INET) {
if (end) {
r = (struct s5_req *)(buffer - S_SIZE_I4);
}
r->atp = S_ATP_I4;
r->i4 = addr->in.sin_addr;
r->p4 = addr->in.sin_port;
return S_SIZE_I4;
} else {
if (n < S_SIZE_I6) {
return -1;
}
if (end) {
r = (struct s5_req *)(buffer - S_SIZE_I6);
}
r->atp = S_ATP_I6;
r->i6 = addr->in6.sin6_addr;
r->p6 = addr->in6.sin6_port;
return S_SIZE_I6;
}
return 0; return 0;
} }
@ -369,6 +390,62 @@ int create_conn(struct poolhd *pool,
pair->pair = val; pair->pair = val;
pair->in6 = dst->in6; pair->in6 = dst->in6;
pair->flag = FLAG_CONN; pair->flag = FLAG_CONN;
val->type = EV_IGNORE;
return 0;
}
int udp_associate(struct poolhd *pool,
struct eval *val, struct sockaddr_ina *dst)
{
struct sockaddr_ina addr = {};
int ufd = nb_socket(params.baddr.sin6_family, SOCK_DGRAM);
if (ufd < 0) {
perror("socket");
return -1;
}
if (params.baddr.sin6_family == AF_INET6) {
int no = 0;
if (setsockopt(ufd, IPPROTO_IPV6,
IPV6_V6ONLY, (char *)&no, sizeof(no))) {
perror("setsockopt IPV6_V6ONLY");
close(ufd);
return -1;
}
}
if (bind(ufd, (struct sockaddr *)&params.baddr,
sizeof(params.baddr)) < 0) {
uniperror("bind");
close(ufd);
return -1;
}
socklen_t sz = sizeof(addr);
if (getsockname(ufd, &addr.sa, &sz)) {
perror("getsockname");
close(ufd);
return -1;
}
struct eval *pair = add_event(pool, EV_UDP_TUNNEL, ufd, POLLIN);
if (!pair) {
close(ufd);
return -1;
}
val->pair = pair;
pair->pair = val;
pair->in6.sin6_port = 0;
struct s5_req s5r = {
.ver = 0x05
};
int len = s5_set_addr((char *)&s5r, sizeof(s5r), &addr, 0);
if (len < 0) {
return -1;
}
if (send(val->fd, (char *)&s5r, len, 0) < 0) {
perror("send");
return -1;
}
return 0; return 0;
} }
@ -422,7 +499,7 @@ static inline int on_accept(struct poolhd *pool, struct eval *val)
} }
static inline int on_tunnel(struct poolhd *pool, struct eval *val, int on_tunnel(struct poolhd *pool, struct eval *val,
char *buffer, size_t bfsize, int out) char *buffer, size_t bfsize, int out)
{ {
ssize_t n = 0; ssize_t n = 0;
@ -494,60 +571,64 @@ static inline int on_tunnel(struct poolhd *pool, struct eval *val,
} }
int mode_add_get(struct sockaddr_ina *dst, int m) int on_udp_tunnel(struct eval *val, char *buffer, size_t bfsize)
{ {
// m < 0: get, m > 0: set, m == 0: delete char *data = buffer + S_SIZE_I6;
time_t t; size_t data_len = bfsize - S_SIZE_I6;
struct elem *val; do {
char *str = (char *)&dst->in; struct sockaddr_ina addr = {0};
int len = sizeof(dst->sa.sa_family); struct sockaddr_ina *src = (struct sockaddr_ina *)&val->in6;
socklen_t asz = sizeof(addr);
if (dst->sa.sa_family == AF_INET) {
len = sizeof(dst->in); ssize_t n = recvfrom(val->fd, data, data_len, 0, &addr.sa, &asz);
} if (n < 1) {
else { if (n && errno == EAGAIN)
len = sizeof(dst->in6) - sizeof(dst->in6.sin6_scope_id); break;
} perror("recv udp");
len -= sizeof(dst->sa.sa_family);
if (m == 0) {
mem_delete(params.mempool, str, len);
return 0;
}
else if (m > 0) {
time(&t);
val = mem_add(params.mempool, str, len);
if (!val) {
uniperror("mem_add");
return -1; return -1;
} }
val->m = m; if (!src->in.sin_port) {
val->time = t; src->in6 = addr.in6;
return 0; }
} ssize_t ns = 0;
val = mem_get(params.mempool, str, len);
if (!val) { if (addr_equ(src, &addr)) {
return -1; if (*(data + 2) != 0) { // frag
} continue;
time(&t); }
if (t > val->time + params.cache_ttl) { int offs = s5_get_addr(data, n, &addr, SOCK_DGRAM);
LOG(LOG_S, "time=%ld, now=%ld, ignore\n", val->time, t); if (offs < 0) {
return 0; return -1;
} }
return val->m; val->pair->in6 = addr.in6;
}
if (src->sa.sa_family == AF_INET6) {
map_fix(&addr, 6);
int ext_connect(struct poolhd *pool, struct eval *val, }
struct sockaddr_ina *dst, int next, int m) if (src->sa.sa_family != addr.sa.sa_family) {
{ return -1;
struct desync_params *dp = &params.dp[m]; }
if (dp->to_ip == 2) { ns = udp_hook(val,
struct sockaddr_ina addr = { .in6 = dp->addr }; data + offs, n - offs, &addr);
addr.in.sin_port = dst->in.sin_port; }
return create_conn(pool, val, &addr, next); else {
} //map_fix(&addr, 0);
return create_conn(pool, val, dst, next); memset(buffer, 0, S_SIZE_I6);
int offs = s5_set_addr(data, S_SIZE_I6,
(struct sockaddr_ina *)&val->pair->in6, 1);
if (offs < 0 || offs > S_SIZE_I6) {
return -1;
}
ns = sendto(val->fd, data - offs, offs + n, 0,
(struct sockaddr *)&val->in6, sizeof(val->in6));
}
if (ns < 0) {
perror("sendto");
return -1;
}
} while(1);
return 0;
} }
@ -571,268 +652,58 @@ static inline int on_request(struct poolhd *pool, struct eval *val,
val->flag = FLAG_S5; val->flag = FLAG_S5;
return 0; return 0;
} }
error = s5_get_addr(buffer, n, &dst); if (n < S_SIZE_MIN) {
LOG(LOG_E, "ss: request to small (%ld)\n", n);
return -1;
}
struct s5_req *r = (struct s5_req *)buffer;
int s5e = 0;
switch (r->cmd) {
case S_CMD_CONN:
s5e = s5_get_addr(buffer, n, &dst, SOCK_STREAM);
if (s5e >= 0) {
error = connect_hook(pool, val, &dst, EV_CONNECT);
}
break;
case S_CMD_AUDP:
if (params.udp) {
s5e = s5_get_addr(buffer, n, &dst, SOCK_DGRAM);
if (s5e >= 0) {
error = udp_associate(pool, val, &dst);
}
break;
}
default:
LOG(LOG_E, "ss: unsupported cmd: 0x%x\n", r->cmd);
s5e = -S_ER_CMD;
}
if (s5e < 0) {
if (resp_s5_error(val->fd, -s5e) < 0)
perror("send");
return -1;
}
} }
else if (*buffer == S_VER4) { else if (*buffer == S_VER4) {
val->flag = FLAG_S4; val->flag = FLAG_S4;
error = s4_get_addr(buffer, n, &dst); error = s4_get_addr(buffer, n, &dst);
if (error) {
if (resp_error(val->fd, error, FLAG_S4) < 0)
perror("send");
return -1;
}
error = connect_hook(pool, val, &dst, EV_CONNECT);
} }
else { else {
LOG(LOG_E, "ss: invalid version: 0x%x (%lu)\n", *buffer, n); LOG(LOG_E, "ss: invalid version: 0x%x (%lu)\n", *buffer, n);
return -1; return -1;
} }
if (error) {
if ((val->flag == FLAG_S4
&& resp_error(val->fd, error, FLAG_S4) < 0)
|| (resp_s5_error(val->fd, error) < 0)) {
uniperror("send");
}
return -1;
}
if (params.late_conn) {
val->type = EV_DESYNC;
if (resp_error(val->fd, 0, val->flag) < 0) {
perror("send");
return -1;
}
val->in6 = dst.in6;
return 0;
}
int m = mode_add_get(&dst, -1);
val->cache = (m == 0);
val->attempt = m < 0 ? 0 : m;
error = ext_connect(pool, val, &dst, EV_CONNECT, m);
if (error) { if (error) {
int en = get_e(); int en = get_e();
if (resp_error(val->fd, en ? en : error, val->flag) < 0) if (resp_error(val->fd, en ? en : error, val->flag) < 0)
uniperror("send"); uniperror("send");
return -1; return -1;
} }
val->type = EV_IGNORE;
return 0;
}
int reconnect(struct poolhd *pool, struct eval *val, int m)
{
struct eval *client = val->pair;
if (ext_connect(pool, client,
(struct sockaddr_ina *)&val->in6, EV_DESYNC, m)) {
return -1;
}
val->pair = 0;
del_event(pool, val);
client->type = EV_IGNORE;
client->attempt = m;
client->cache = 1;
return 0;
}
bool check_host(struct mphdr *hosts, struct eval *val)
{
char *host;
int len;
if (!(len = parse_tls(val->buff.data, val->buff.size, &host))) {
len = parse_http(val->buff.data, val->buff.size, &host, 0);
}
return mem_get(hosts, host, len) != 0;
}
int on_torst(struct poolhd *pool, struct eval *val)
{
int m = val->pair->attempt + 1;
for (; m < params.dp_count; m++) {
struct desync_params *dp = &params.dp[m];
if (!(dp->detect & DETECT_TORST)) {
continue;
}
if (!dp->hosts || check_host(dp->hosts, val->pair)) {
break;
}
}
if (m >= params.dp_count) {
mode_add_get(
(struct sockaddr_ina *)&val->in6, 0);
return -1;
}
return reconnect(pool, val, m);
}
int on_response(struct poolhd *pool, struct eval *val,
char *resp, ssize_t sn)
{
int m = val->pair->attempt + 1;
char *req = val->pair->buff.data;
ssize_t qn = val->pair->buff.size;
for (; m < params.dp_count; m++) {
struct desync_params *dp = &params.dp[m];
switch (0) {
default:
if ((dp->detect & DETECT_HTTP_LOCAT)
&& is_http_redirect(req, qn, resp, sn)) {
break;
}
else if ((dp->detect & DETECT_TLS_INVSID)
&& neq_tls_sid(req, qn, resp, sn)
&& !neq_tls_sid(
fake_tls.data, fake_tls.size, resp, sn)) {
break;
}
else if ((dp->detect & DETECT_TLS_ALERT)
&& is_tls_alert(resp, sn)) {
break;
}
else if (dp->detect & DETECT_HTTP_CLERR) {
int code = get_http_code(resp, sn);
if (code > 400 && code < 451 && code != 429) {
break;
}
}
continue;
}
if (!dp->hosts || check_host(dp->hosts, val->pair)) {
break;
}
}
if (m < params.dp_count) {
return reconnect(pool, val, m);
}
return -1;
}
int on_tunnel_check(struct poolhd *pool, struct eval *val,
char *buffer, size_t bfsize, int out)
{
if (out) {
return on_tunnel(pool, val, buffer, bfsize, out);
}
ssize_t n = recv(val->fd, buffer, bfsize, 0);
if (n < 1) {
uniperror("recv");
switch (get_e()) {
case ECONNRESET:
case ETIMEDOUT:
break;
default: return -1;
}
return on_torst(pool, val);
}
//
if (on_response(pool, val, buffer, n) == 0) {
return 0;
}
struct eval *pair = val->pair;
ssize_t sn = send(pair->fd, buffer, n, 0);
if (n != sn) {
uniperror("send");
return -1;
}
val->type = EV_TUNNEL;
pair->type = EV_TUNNEL;
free(pair->buff.data);
pair->buff.data = 0;
pair->buff.size = 0;
if (params.timeout &&
set_timeout(val->fd, 0)) {
return -1;
}
int m = pair->attempt;
if (!pair->cache) {
return 0;
}
if (m == 0) {
LOG(LOG_S, "delete ip: m=%d\n", m);
} else {
LOG(LOG_S, "save ip: m=%d\n", m);
}
return mode_add_get(
(struct sockaddr_ina *)&val->in6, m);
}
int on_desync(struct poolhd *pool, struct eval *val,
char *buffer, size_t bfsize)
{
if (val->flag == FLAG_CONN) {
if (mod_etype(pool, val, POLLOUT, 0)) {
uniperror("mod_etype");
return -1;
}
val = val->pair;
}
ssize_t n;
int m = val->attempt;
LOG((m ? LOG_S : LOG_L), "desync params index: %d\n", m);
if (!val->buff.data) {
n = recv(val->fd, buffer, bfsize, 0);
if (n <= 0) {
if (n) uniperror("recv data");
return -1;
}
val->buff.size = n;
val->recv_count += n;
if (!(val->buff.data = malloc(n))) {
uniperror("malloc");
return -1;
}
memcpy(val->buff.data, buffer, n);
if (!m) for (; m < params.dp_count; m++) {
struct desync_params *dp = &params.dp[m];
if (!dp->detect &&
(!dp->hosts || check_host(dp->hosts, val))) {
break;
}
}
if (m >= params.dp_count) return -1;
val->attempt = m;
if (params.late_conn) {
return ext_connect(pool, val,
(struct sockaddr_ina *)&val->in6, EV_DESYNC, m);
}
}
else {
n = val->buff.size;
memcpy(buffer, val->buff.data, n);
}
if (params.timeout &&
set_timeout(val->pair->fd, params.timeout)) {
return -1;
}
ssize_t sn = desync(val->pair->fd, buffer, bfsize,
n, val->buff.offset, (struct sockaddr *)&val->pair->in6, m);
if (sn < 0) {
return -1;
}
if (sn < n) {
val->buff.offset = sn;
if (mod_etype(pool, val->pair, POLLOUT, 1)) {
uniperror("mod_etype");
return -1;
}
val->pair->type = EV_DESYNC;
return 0;
}
val->type = EV_TUNNEL;
val->pair->type = EV_PRE_TUNNEL;
return 0; return 0;
} }
@ -929,6 +800,11 @@ int event_loop(int srvfd)
del_event(pool, val); del_event(pool, val);
continue; continue;
case EV_UDP_TUNNEL:
if (on_udp_tunnel(val, buffer, bfsize))
del_event(pool, val);
continue;
case EV_CONNECT: case EV_CONNECT:
if (on_connect(pool, val, etype & POLLERR)) if (on_connect(pool, val, etype & POLLERR))
del_event(pool, val); del_event(pool, val);

14
proxy.h
View File

@ -6,6 +6,8 @@
#include <arpa/inet.h> #include <arpa/inet.h>
#endif #endif
#include <conev.h>
struct sockaddr_ina { struct sockaddr_ina {
union { union {
struct sockaddr sa; struct sockaddr sa;
@ -94,6 +96,18 @@ enum s4_rep {
#define S_SIZE_I6 22 #define S_SIZE_I6 22
#define S_SIZE_ID 7 #define S_SIZE_ID 7
void map_fix(struct sockaddr_ina *addr, char f6);
int resp_error(int fd, int e, int flag);
int create_conn(struct poolhd *pool,
struct eval *val, struct sockaddr_ina *dst, int next);
int on_tunnel(struct poolhd *pool, struct eval *val,
char *buffer, size_t bfsize, int out);
int listen_socket(struct sockaddr_ina *srv); int listen_socket(struct sockaddr_ina *srv);
int event_loop(int srvfd); int event_loop(int srvfd);
int run(struct sockaddr_ina *srv); int run(struct sockaddr_ina *srv);

View File

@ -31,6 +31,9 @@ $ ./ciadpi --disorder 3 -A --tlsrec 1+s
Отбрасывать запросы, если в качестве адреса указан домен Отбрасывать запросы, если в качестве адреса указан домен
Т.к. резолвинг выполняется синхронно, то он может замедлить или даже заморозить работу Т.к. резолвинг выполняется синхронно, то он может замедлить или даже заморозить работу
-U, --no-udp
Не проксировать UDP
-K, --desync-known -K, --desync-known
Отключить запутывание для нераспознанных протоколов Отключить запутывание для нераспознанных протоколов
Распознаваемые протоколы: HTTP и TLS с SNI Распознаваемые протоколы: HTTP и TLS с SNI
@ -54,12 +57,12 @@ $ ./ciadpi --disorder 3 -A --tlsrec 1+s
cl_err : HTTP ответ, код которого равен 40x, но не 429 cl_err : HTTP ответ, код которого равен 40x, но не 429
sid_inv : session_id в TLS ServerHello и ClientHello не совпадают sid_inv : session_id в TLS ServerHello и ClientHello не совпадают
alert : TLS Error Alert в ответе alert : TLS Error Alert в ответе
nop : Предыдущая группа пропущена, например из-за ограничения по hosts none : Предыдущая группа пропущена, например из-за ограничения по доменам или протоколам
По умолчанию обрабатывается только torst По умолчанию обрабатывается только torst
Можно указывать несколько групп опций, раделяя их данным параметром Можно указывать несколько групп опций, раделяя их данным параметром
Если соединение успешно прошло, то параметры для данного IP будут закешированны Если соединение успешно прошло, то параметры для данного IP будут закешированны
Параметры, которые можно вынести в отдельную группу: Параметры, которые можно вынести в отдельную группу:
hosts, dst, split, disorder, oob, fake, ttl, ip-opt, md5sig, mod-http, tlsrec proto, hosts, dst, split, disorder, oob, fake, ttl, ip-opt, md5sig, fake-data, mod-http, tlsrec
Пример: Пример:
--auto=redirect --split=1+h --auto=torst --fake -1 --auto=sid_inv,alert --tlsrec 1+s --auto=redirect --split=1+h --auto=torst --fake -1 --auto=sid_inv,alert --tlsrec 1+s
@ -71,6 +74,9 @@ $ ./ciadpi --disorder 3 -A --tlsrec 1+s
В Linux переводится в миллисекунды, поэтому можно указать дробное число В Linux переводится в миллисекунды, поэтому можно указать дробное число
Истечение таймаута будет обработано --auto Истечение таймаута будет обработано --auto
-K, --proto[=t,h,d,q]
Белый список протоколов: tls,http,dns,quic
-H, --hosts <file|:string> -H, --hosts <file|:string>
Ограничить область действия параметров списком доменов Ограничить область действия параметров списком доменов
Домены должны быть разделены новой строкой или пробелом Домены должны быть разделены новой строкой или пробелом
@ -113,8 +119,7 @@ $ ./ciadpi --disorder 3 -A --tlsrec 1+s
Большинство серверов (в основном на Linux) отбрасывают пакеты с данной опцией Большинство серверов (в основном на Linux) отбрасывают пакеты с данной опцией
Поддерживается только в Linux, может быть выключен в некоторых сборках ядра (< 3.9, Android) Поддерживается только в Linux, может быть выключен в некоторых сборках ядра (< 3.9, Android)
-l, --fake-tls <file|:str> -l, --fake-data <file|:str>
-j, --fake-http <file|:str>
Указать свои поддельные пакеты, вместо дефолтных Указать свои поддельные пакеты, вместо дефолтных
-e, --oob-data <file|:str> -e, --oob-data <file|:str>
@ -228,25 +233,31 @@ TCP может отсылать данные вне основного пото
--auto, --hosts --auto, --hosts
Параметр auto делит опции на группы. Параметр auto делит опции на группы.
Для каждого запроса они обходятся слева на право. Для каждого запроса они обходятся слева на право.
Сначала проверяется триггер указанный в auto, затем hosts. Сначала проверяется триггер, указанный в auto, затем proto и hosts.
Примеры: Примеры:
--fake -1 --ttl 10 --auto=alert,sid_inv --fake -1 --ttl 5 --fake -1 --ttl 10 --auto=alert,sid_inv --fake -1 --ttl 5
По умолчанию использовать fake с ttl=10, в случае ошибки использовать fake с ttl=5 По умолчанию использовать fake с ttl=10, в случае ошибки использовать fake с ttl=5
--hosts list.txt --disorder 3 --auto=nop --hosts list.txt --disorder 3 --auto=none
Применять запутывание только для доменов из list.txt Применять запутывание только для доменов из list.txt
--hosts list.txt --auto=nop --disorder 3 --hosts list.txt --auto=none --disorder 3
Не применять запутывание для доменов из list.txt Не применять запутывание для доменов из list.txt
--late-conn --hosts ':one.one.one.one' --dst 1.1.1.1 --disorder 3 --auto=nop --auto=torst --timeout 3 --tlsrec 1+s --late-conn --hosts ':one.one.one.one' --dst 1.1.1.1 --disorder 3 --auto=none --auto=torst --timeout 3 --tlsrec 1+s
Для указанного домена применять disorder, подключаясь только на указанный IP и игнорируя адрес из запроса. Для указанного домена применять disorder, подключаясь только на указанный IP и игнорируя адрес из запроса.
Для остальных ничего не делать, однако если обнаружится блокировка, то попробовать применить tlsrec. Для остальных ничего не делать, однако если обнаружится блокировка, то попробовать применить tlsrec.
--auto=torst --hosts list.txt --disorder 3 --auto=torst --hosts list.txt --disorder 3
По умолчанию ничего не делать, использовать disorder при условии, что произошла блокировка и домен входит в list.txt. По умолчанию ничего не делать, использовать disorder при условии, что произошла блокировка и домен входит в list.txt.
--proto=http,tls --disorder 3 --auto=none
Запутывать только HTTP и TLS
--proto=http --fake -1 --fake-data=':GET /...' --auto=none --fake -1
Переопределить фейковый пакет для HTTP
------- -------
Сборка: Сборка:
Для сборки понадобится: Для сборки понадобится: