--redirect

This commit is contained in:
ruti 2024-03-11 18:38:39 +03:00
parent 69286c71db
commit 015095d2ef
6 changed files with 299 additions and 141 deletions

View file

@ -35,12 +35,15 @@ enum eid {
EV_IGNORE, EV_IGNORE,
EV_TUNNEL, EV_TUNNEL,
EV_PRE_TUNNEL, EV_PRE_TUNNEL,
EV_REDIRECT,
EV_DESYNC EV_DESYNC
}; };
#define FLAG_S4 1 #define FLAG_S4 1
#define FLAG_S5 2 #define FLAG_S5 2
#define FLAG_CONN 4 #define FLAG_CONN 4
#define FLAG_AUTH 8
#define FLAG_ANSWER 16
#ifndef CONEV_H #ifndef CONEV_H
char *eid_name[] = { char *eid_name[] = {
@ -50,6 +53,7 @@ char *eid_name[] = {
"EV_IGNORE", "EV_IGNORE",
"EV_TUNNEL", "EV_TUNNEL",
"EV_PRE_TUNNEL", "EV_PRE_TUNNEL",
"EV_REDIRECT",
"EV_DESYNC" "EV_DESYNC"
}; };
#endif #endif
@ -72,8 +76,7 @@ struct eval {
struct sockaddr_in6 in6; struct sockaddr_in6 in6;
}; };
ssize_t recv_count; ssize_t recv_count;
int try_count; int attempt;
int saved_m;
#ifndef NOEPOLL #ifndef NOEPOLL
uint32_t events; uint32_t events;
#endif #endif

View file

@ -181,7 +181,9 @@ int desync(int sfd, char *buffer, size_t bfsize,
ssize_t n, struct sockaddr *dst, int dp_c) ssize_t n, struct sockaddr *dst, int dp_c)
{ {
struct desync_params dp = params.dp[dp_c]; struct desync_params dp = params.dp[dp_c];
if (dp.redirect) {
dst = (struct sockaddr *)&dp.ext_proxy;
}
char *host = 0; char *host = 0;
int len = 0, type = 0; int len = 0, type = 0;
int fa = get_family(dst); int fa = get_family(dst);
@ -296,7 +298,7 @@ int desync(int sfd, char *buffer, size_t bfsize,
lp = pos; lp = pos;
} }
if (lp < n) { if (lp < n) {
LOG(LOG_S, "send: pos=%ld-%ld\n", lp, n); LOG((lp ? LOG_S : LOG_L), "send: pos=%ld-%ld\n", lp, n);
if (send(sfd, buffer + lp, n - lp, 0) < 0) { if (send(sfd, buffer + lp, n - lp, 0) < 0) {
uniperror("send"); uniperror("send");
return -1; return -1;

46
main.c
View file

@ -46,7 +46,7 @@ struct params params = {
.custom_ttl = 0, .custom_ttl = 0,
.de_known = 0, .de_known = 0,
.cache_ttl = 3600, .cache_ttl = 21600,
.ipv6 = 1, .ipv6 = 1,
.resolve = 1, .resolve = 1,
.max_open = 512, .max_open = 512,
@ -71,6 +71,7 @@ const char help_text[] = {
" -K, --desync-known Desync only HTTP and TLS with SNI\n" " -K, --desync-known Desync only HTTP and TLS with SNI\n"
" -A, --auto Try desync params after this option\n" " -A, --auto Try desync params after this option\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"
" -q, --redirect <ip:port> Redirect to external SOCKS5 proxy\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"
@ -119,7 +120,7 @@ const struct option options[] = {
{"tlsrec", 1, 0, 'r'}, {"tlsrec", 1, 0, 'r'},
{"def-ttl", 1, 0, 'g'}, {"def-ttl", 1, 0, 'g'},
{"delay", 1, 0, 'w'}, // {"delay", 1, 0, 'w'}, //
{"redirect", 1, 0, 'q'},
{0} {0}
}; };
@ -156,15 +157,40 @@ char *ftob(char *name, ssize_t *sl)
} }
int get_addr(const char *str, struct sockaddr_ina *addr) int get_addr(char *str, struct sockaddr_ina *addr)
{ {
uint16_t port = 0;
char *s = str, *e = 0;
char *end = 0, *p = str;
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 - s) < 7) {
return -1;
}
char str_ip[(e - s) + 1];
memcpy(str_ip, s, e - s);
str_ip[e - s] = 0;
struct addrinfo hints = {0}, *res = 0; struct addrinfo hints = {0}, *res = 0;
hints.ai_socktype = SOCK_STREAM; hints.ai_socktype = SOCK_STREAM;
hints.ai_family = AF_UNSPEC; hints.ai_family = AF_UNSPEC;
hints.ai_flags = AI_NUMERICHOST; hints.ai_flags = AI_NUMERICHOST;
if (getaddrinfo(str, 0, &hints, &res) || !res) { if (getaddrinfo(str_ip, 0, &hints, &res) || !res) {
return -1; return -1;
} }
if (res->ai_addr->sa_family == AF_INET6) if (res->ai_addr->sa_family == AF_INET6)
@ -172,6 +198,10 @@ int get_addr(const char *str, struct sockaddr_ina *addr)
else else
addr->in = *(struct sockaddr_in *)res->ai_addr; addr->in = *(struct sockaddr_in *)res->ai_addr;
freeaddrinfo(res); freeaddrinfo(res);
if (port) {
addr->in6.sin6_port = port;
}
return 0; return 0;
} }
@ -284,6 +314,7 @@ int main(int argc, char **argv)
long val = 0; long val = 0;
char *end = 0; char *end = 0;
uint16_t port = htons(1080); uint16_t port = htons(1080);
struct desync_params *dp = add_dparams( struct desync_params *dp = add_dparams(
@ -487,6 +518,13 @@ int main(int argc, char **argv)
invalid = 1; invalid = 1;
break; break;
case 'q':
if (get_addr(optarg, (struct sockaddr_ina *)&dp->ext_proxy) < 0)
invalid = 1;
else
dp->redirect = 1;
break;
case 0: case 0:
break; break;

View file

@ -25,6 +25,8 @@ struct part {
}; };
struct desync_params { struct desync_params {
char redirect;
struct sockaddr_in6 ext_proxy;
int ttl; int ttl;
int parts_n; int parts_n;
struct part *parts; struct part *parts;

369
proxy.c
View file

@ -263,7 +263,7 @@ int s5_get_addr(char *buffer, ssize_t n,
int create_conn(struct poolhd *pool, int create_conn(struct poolhd *pool,
struct eval *val, struct sockaddr_ina *dst) struct eval *val, struct sockaddr_ina *dst, int next)
{ {
struct sockaddr_ina addr = *dst; struct sockaddr_ina addr = *dst;
@ -319,7 +319,7 @@ int create_conn(struct poolhd *pool,
close(sfd); close(sfd);
return -1; return -1;
} }
struct eval *pair = add_event(pool, EV_CONNECT, sfd, POLLOUT); struct eval *pair = add_event(pool, next, sfd, POLLOUT);
if (!pair) { if (!pair) {
close(sfd); close(sfd);
return -1; return -1;
@ -332,60 +332,6 @@ int create_conn(struct poolhd *pool,
} }
static inline int on_request(struct poolhd *pool, struct eval *val,
char *buffer, size_t bfsize)
{
struct sockaddr_ina dst = {0};
ssize_t n = recv(val->fd, buffer, bfsize, 0);
if (n < 1) {
if (n) uniperror("ss recv");
return -1;
}
if (*buffer == S_VER5) {
if (val->flag != FLAG_S5) {
if (auth_socks5(val->fd, buffer, n)) {
return -1;
}
val->flag = FLAG_S5;
return 0;
}
if (n < S_SIZE_MIN) {
LOG(LOG_E, "ss: request to small\n");
return -1;
}
int s5e = s5_get_addr(buffer, n, &dst);
if (!s5e &&
create_conn(pool, val, &dst)) {
s5e = S_ER_GEN;
}
if (s5e) {
resp_s5_error(val->fd, s5e);
return -1;
}
}
else if (*buffer == S_VER4) {
val->flag = FLAG_S4;
int error = s4_get_addr(buffer, n, &dst);
if (!error) {
error = create_conn(pool, val, &dst);
}
if (error) {
if (resp_error(val->fd, error, FLAG_S4) < 0)
uniperror("send");
return -1;
}
}
else {
LOG(LOG_E, "ss: invalid version: 0x%x (%lu)\n", *buffer, n);
return -1;
}
val->type = EV_IGNORE;
return 0;
}
static inline int on_accept(struct poolhd *pool, struct eval *val) static inline int on_accept(struct poolhd *pool, struct eval *val)
{ {
struct sockaddr_ina client; struct sockaddr_ina client;
@ -507,26 +453,9 @@ static inline int on_tunnel(struct poolhd *pool, struct eval *val,
} }
int try_again(struct poolhd *pool, struct eval *val)
{
struct eval *client = val->pair;
int e = create_conn(pool, client,
(struct sockaddr_ina *)&val->in6);
if (e) {
return -1;
}
val->pair = 0;
del_event(pool, val);
client->type = EV_IGNORE;
client->try_count++;
return 0;
}
int mode_add_get(struct sockaddr_ina *dst, int m) int mode_add_get(struct sockaddr_ina *dst, int m)
{ {
// m < 0: get, m > 0: set, m == 0: delete
char *data; char *data;
int len; int len;
time_t t; time_t t;
@ -561,28 +490,130 @@ int mode_add_get(struct sockaddr_ina *dst, int m)
val = params.mempool->values[i]; val = params.mempool->values[i];
time(&t); time(&t);
if (t > val->time + params.cache_ttl) { if (t > val->time + params.cache_ttl) {
LOG(LOG_S, "cache value is too old, ignore\n"); LOG(LOG_S, "time=%ld, now=%ld, ignore\n", val->time, t);
return -1; return 0;
} }
return val->m; return val->m;
} }
static inline int on_request(struct poolhd *pool, struct eval *val,
char *buffer, size_t bfsize)
{
struct sockaddr_ina dst = {0};
ssize_t n = recv(val->fd, buffer, bfsize, 0);
if (n < 1) {
if (n) uniperror("ss recv");
return -1;
}
int e = 0, s5e = 0, m;
if (*buffer == S_VER5) {
if (val->flag != FLAG_S5) {
if (auth_socks5(val->fd, buffer, n)) {
return -1;
}
val->flag = FLAG_S5;
return 0;
}
if (n < S_SIZE_MIN) {
LOG(LOG_E, "ss: request to small\n");
return -1;
}
e = s5_get_addr(buffer, n, &dst);
s5e = e;
}
else if (*buffer == S_VER4) {
val->flag = FLAG_S4;
e = s4_get_addr(buffer, n, &dst);
}
else {
LOG(LOG_E, "ss: invalid version: 0x%x (%lu)\n", *buffer, n);
return -1;
}
if (!e) {
m = mode_add_get(&dst, -1);
int next = EV_CONNECT;
if (m >= 0) {
struct desync_params *dp = &params.dp[m];
if (dp->redirect) {
next = EV_REDIRECT;
e = create_conn(pool, val,
(struct sockaddr_ina *)&dp->ext_proxy, next);
val->pair->in6 = dst.in6;
}
}
if (next == EV_CONNECT) {
e = create_conn(pool, val, &dst, next);
}
}
if (e) {
if (s5e) {
resp_s5_error(val->fd, s5e);
}
else if (resp_error(val->fd, e, val->flag) < 0) {
uniperror("ss: send");
}
return -1;
}
if (m >= 0) {
val->attempt = m;
}
val->pair->attempt = m;
val->type = EV_IGNORE;
return 0;
}
int try_again(struct poolhd *pool, struct eval *val)
{
struct eval *client = val->pair;
int e = 0;
int m = client->attempt + 1;
if (m >= params.dp_count) {
mode_add_get(
(struct sockaddr_ina *)&val->in6, 0);
return -1;
}
struct desync_params *dp = &params.dp[m];
if (dp->redirect) {
e = create_conn(pool, client,
(struct sockaddr_ina *)&dp->ext_proxy, EV_REDIRECT);
client->pair->in6 = val->in6;
}
else {
e = create_conn(pool, client,
(struct sockaddr_ina *)&val->in6, EV_DESYNC);
}
if (e) {
return -1;
}
val->pair = 0;
del_event(pool, val);
client->type = EV_IGNORE;
client->attempt++;
return 0;
}
int on_tunnel_check(struct poolhd *pool, struct eval *val, int on_tunnel_check(struct poolhd *pool, struct eval *val,
char *buffer, size_t bfsize, int out) char *buffer, size_t bfsize, int out)
{ {
if (!out && val->flag == FLAG_CONN) { int e = on_tunnel(pool, val, buffer, bfsize, out);
int e = on_tunnel(pool, val, buffer, bfsize, out);
if (val->flag == FLAG_CONN) {
if (out) {
return e;
}
if (e) { if (e) {
if (unie(e) != ECONNRESET) { if (unie(e) != ECONNRESET) {
return -1; return -1;
} }
if (val->pair->try_count + 1 >= params.dp_count) { return try_again(pool, val);
mode_add_get(
(struct sockaddr_ina *)&val->in6, 0);
return -1;
}
return try_again(pool, val);;
} }
struct eval *pair = val->pair; struct eval *pair = val->pair;
val->type = EV_TUNNEL; val->type = EV_TUNNEL;
@ -592,48 +623,37 @@ int on_tunnel_check(struct poolhd *pool, struct eval *val,
pair->buff.data = 0; pair->buff.data = 0;
pair->buff.size = 0; pair->buff.size = 0;
int m = pair->try_count; int m = pair->attempt;
if (m == 0 && !val->saved_m) {
if ((m == 0 && val->attempt < 0)
|| (m && m == val->attempt)) {
return 0; 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( return mode_add_get(
(struct sockaddr_ina *)&val->in6, m); (struct sockaddr_ina *)&val->in6, m);
} }
else { return e;
int e = on_tunnel(pool, val, buffer, bfsize, out);
if (e) {
if (val->flag == FLAG_CONN) {
val = val->pair;
}
int m = val->try_count + 1;
if (m >= params.dp_count) {
m = 0;
}
mode_add_get(
(struct sockaddr_ina *)&val->pair->in6, m);
return -1;
}
}
return 0;
} }
int on_desync(struct poolhd *pool, struct eval *val, int on_desync(struct poolhd *pool, struct eval *val,
char *buffer, size_t bfsize) char *buffer, size_t bfsize)
{ {
ssize_t n; if (val->flag == FLAG_CONN) {
int m; if (mod_etype(pool, val, POLLOUT, 0)) {
uniperror("mod_etype");
if (!val->try_count) { return -1;
m = mode_add_get(
(struct sockaddr_ina *)&val->pair->in6, -1);
if (m >= 0) {
val->saved_m = m + 1;
val->try_count = m;
} }
val = val->pair;
} }
m = val->try_count; ssize_t n;
LOG(LOG_S, "desync params index: %d\n", m); int m = val->attempt;
LOG((m ? LOG_S : LOG_L), "desync params index: %d\n", m);
if (!val->buff.data) { if (!val->buff.data) {
n = recv(val->fd, buffer, bfsize, 0); n = recv(val->fd, buffer, bfsize, 0);
@ -664,39 +684,120 @@ int on_desync(struct poolhd *pool, struct eval *val,
} }
static inline int on_connect(struct poolhd *pool, struct eval *val, static inline int on_connect(struct poolhd *pool, struct eval *val, int e)
char *buffer, size_t bfsize, int e)
{ {
int error = 0; int error = 0;
socklen_t len = sizeof(error); socklen_t len = sizeof(error);
struct eval *pair = val->pair;
if (e) { if (e) {
if (getsockopt(val->fd, SOL_SOCKET, if (getsockopt(val->fd, SOL_SOCKET,
SO_ERROR, (char *)&error, &len)) { SO_ERROR, (char *)&error, &len)) {
uniperror("getsockopt SO_ERROR"); uniperror("getsockopt SO_ERROR");
return -1; return -1;
} }
} if (unie(error) == ECONNREFUSED) {
if (!val->pair->try_count) { e = try_again(pool, val);
if (resp_error(val->pair->fd, if (!e) error = 0;
error, val->pair->flag) < 0) {
uniperror("send");
return -1;
} }
} }
if (e) { else {
if (mod_etype(pool, val, POLLOUT, 0)) {
uniperror("mod_etype");
return -1;
}
val->type = EV_TUNNEL;
val->pair->type = EV_DESYNC;
}
if (resp_error(pair->fd,
error, pair->flag) < 0) {
uniperror("send");
return -1; return -1;
} }
if (mod_etype(pool, val, POLLOUT, 0)) { return e ? -1 : 0;
uniperror("mod_etype"); }
return -1;
}
val->type = EV_DESYNC; static inline int on_redirect(struct poolhd *pool, struct eval *val,
val->pair->type = EV_DESYNC; char *buffer, size_t bfsize)
{
struct eval *pair = val->pair;
int e = -1;
if (val->pair->try_count) { switch (val->flag) {
case FLAG_CONN:;
char a[3] = "\5\1\0";
if (send(val->fd, &a, sizeof(a), 0) < 0) {
uniperror("rr: send");
break;
}
if (mod_etype(pool, val, POLLOUT, 0)) {
uniperror("mod_etype");
break;
}
val->flag = FLAG_AUTH;
return 0;
case FLAG_AUTH:;
ssize_t n = recv(val->fd, buffer, bfsize, 0);
if (n < 2) {
break;
}
if (!(buffer[0] == 5 && buffer[1] == 0)) {
LOG(LOG_E, "rr: auth error: 0x%x\n", buffer[1]);
break;
}
struct s5_req r = {.ver = 5, .cmd = S_CMD_CONN};
switch (val->in.sin_family) {
case AF_INET:
r.atp = S_ATP_I4;
r.i4 = val->in.sin_addr;
r.p4 = val->in.sin_port;
break;
case AF_INET6:
r.atp = S_ATP_I6;
r.i6 = val->in6.sin6_addr;
r.p6 = val->in6.sin6_port;
break;
default:
return -1;
}
if (send(val->fd, &r, sizeof(r), 0) < 0) {
uniperror("rr: send");
break;
}
val->flag = FLAG_ANSWER;
return 0;
case FLAG_ANSWER:
n = recv(val->fd, buffer, bfsize, 0);
if (n < sizeof(struct s5_rep)) {
uniperror("rr: recv");
break;
}
struct s5_rep *rr = (struct s5_rep *)buffer;
if (rr->ver != 5 || rr->code != 0) {
LOG(LOG_E, "rr: socks returned: %d\n", rr->code);
break;
}
val->flag = FLAG_CONN;
val->type = EV_TUNNEL;
pair->type = EV_DESYNC;
e = 0;
}
char is_first = (val->attempt == pair->attempt);
if (!e && !is_first) {
return on_desync(pool, val->pair, buffer, bfsize); return on_desync(pool, val->pair, buffer, bfsize);
} }
return 0; else if (e) {
e = try_again(pool, val);
}
if (is_first && resp_error(pair->fd,
e, pair->flag) < 0) {
return -1;
}
return e;
} }
@ -765,11 +866,15 @@ int event_loop(int srvfd)
continue; continue;
case EV_CONNECT: case EV_CONNECT:
if (on_connect(pool, val, if (on_connect(pool, val, etype & POLLERR))
buffer, bfsize, etype & POLLERR))
del_event(pool, val); del_event(pool, val);
continue; continue;
case EV_REDIRECT:
if (on_redirect(pool, val, buffer, bfsize))
del_event(pool, val);
continue;
case EV_DESYNC: case EV_DESYNC:
if (on_desync(pool, val, buffer, bfsize)) if (on_desync(pool, val, buffer, bfsize))
del_event(pool, val); del_event(pool, val);

View file

@ -37,14 +37,22 @@ $ ./ciadpi --disorder 3 -A --tlsrec 1+s
-A, --auto -A, --auto
Автоматический режим Автоматический режим
Если обнаружены признаки блокировки (соединение разорвано сразу после первого пакета), Если будет выполнено одно из следующих условий,
то будут применены параметры обхода, следующие за данной опцией то будут применены параметры обхода, следующие за данной опцией
Условия:
- Сервер сбросил подключение, не отправив ответ
- Сервер прислал RST на этапе подключения
- Резервный прокси отказывает в соединении
Можно указывать несколько групп параметров, раделяя их данным флагом Можно указывать несколько групп параметров, раделяя их данным флагом
Если соединение успешно прошло, то параметры для данного IP будут закешированны Если соединение успешно прошло, то параметры для данного IP будут закешированны
-u, --cache-ttl <sec> -u, --cache-ttl <sec>
Время жизни значения в кеше, указывается в секундах Время жизни значения в кеше, указывается в секундах
-q, --redirect <ip:port>
Перенаправить подключение на другой SOCKS5 прокси
Имеет смысл указывать после --auto, для перенаправление только заблокированных ресурсов
-s, --split <n[+s]> -s, --split <n[+s]>
Разбить запрос по указанному смещению Разбить запрос по указанному смещению
После числа можно добавить флаг: После числа можно добавить флаг: