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
CC ?= gcc
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:
$(CC) $(CFLAGS) $(SOURCES) -I . -o $(TARGET)

View File

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

View File

@ -131,7 +131,13 @@ ssize_t send_fake(int sfd, char *buffer,
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;
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,
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;
char path[MAX_PATH + 1];
@ -439,10 +451,7 @@ ssize_t desync(int sfd, char *buffer, size_t bfsize,
// desync
long lp = offset;
if (!type && params.de_known) {
// cancel
}
else for (int i = 0; i < dp.parts_n; i++) {
for (int i = 0; i < dp.parts_n; i++) {
struct part part = dp.parts[i];
// 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,
.ipv6 = 1,
.resolve = 1,
.udp = 1,
.max_open = 512,
.bfsize = 16384,
.baddr = {
@ -63,24 +64,25 @@ const char help_text[] = {
" -p, --port <num> Listening port, default 1080\n"
" -c, --max-conn <count> Connection count limit, default 512\n"
" -N, --no-domain Deny domain resolving\n"
" -U, --no-udp Deny UDP association\n"
" -I --conn-ip <ip> Connection binded IP, default ::\n"
" -b, --buf-size <size> Buffer size, default 16384\n"
" -x, --debug <level> Print logs, 0, 1 or 2\n"
" -g, --def-ttl <num> TTL for all outgoing connections\n"
// desync options
" -K, --desync-known Desync only HTTP and TLS with SNI\n"
#ifdef TCP_FASTOPEN_CONNECT
" -F, --tfo Enable TCP Fast Open\n"
#endif
" -L, --late-conn Waiting for request before connecting\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"
#ifdef TIMEOUT_SUPPORT
" -T, --timeout <sec> Timeout waiting for response, after which trigger auto\n"
#endif
" -K, --proto[=t,h,d,q] Protocol whitelist: tls,http,dns,quic\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 - add SNI offset\n"
" +h - add HTTP Host offset\n"
@ -93,8 +95,7 @@ const char help_text[] = {
#ifdef __linux__
" -S, --md5sig Add MD5 Signature option for fake packets\n"
#endif
" -l, --fake-tls <f|:str>\n"
" -j, --fake-http <f|:str> Set custom fake packet\n"
" -l, --fake-data <f|:str> Set custom fake packet\n"
" -n, --tls-sni <str> Change SNI in fake ClientHello\n"
#endif
" -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[] = {
{"no-domain", 0, 0, 'N'},
{"no-ipv6", 0, 0, 'X'},
{"no-udp", 0, 0, 'U'},
{"help", 0, 0, 'h'},
{"version", 0, 0, 'v'},
{"ip", 1, 0, 'i'},
@ -115,7 +117,6 @@ const struct option options[] = {
{"max-conn", 1, 0, 'c'},
{"debug", 1, 0, 'x'},
{"desync-known ", 0, 0, 'K'},
#ifdef TCP_FASTOPEN_CONNECT
{"tfo ", 0, 0, 'F'},
#endif
@ -125,6 +126,7 @@ const struct option options[] = {
#ifdef TIMEOUT_SUPPORT
{"timeout", 1, 0, 'T'},
#endif
{"proto", 2, 0, 'K'},
{"hosts", 1, 0, 'H'},
{"dst", 1, 0, 'D'},
{"split", 1, 0, 's'},
@ -137,8 +139,7 @@ const struct option options[] = {
#ifdef __linux__
{"md5sig", 0, 0, 'S'},
#endif
{"fake-tls", 1, 0, 'l'},
{"fake-http", 1, 0, 'j'},
{"fake-data", 1, 0, 'l'},
{"tls-sni", 1, 0, 'n'},
#endif
{"oob-data", 1, 0, 'e'},
@ -250,7 +251,7 @@ struct mphdr *parse_hosts(char *buffer, size_t size)
char *e = buffer, *s = buffer;
for (; e <= end; e++) {
if (*e != ' ' && *e != '\n' && e != end) {
if (*e != ' ' && *e != '\n' && *e != '\r' && e != end) {
continue;
}
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 orig_ttl = -1, fd;
@ -368,18 +409,14 @@ void clear_params(void)
free(s.parts);
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);
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) {
FREE(oob_data.data, oob_data.size);
oob_data.data = oob_char;
@ -444,6 +481,10 @@ int main(int argc, char **argv)
case 'X':
params.ipv6 = 0;
break;
case 'U':
params.udp = 0;
break;
case 'h':
printf(help_text);
clear_params();
@ -501,10 +542,6 @@ int main(int argc, char **argv)
params.late_conn = 1;
break;
case 'K':
params.de_known = 1;
break;
case 'F':
params.tfo = 1;
break;
@ -570,6 +607,35 @@ int main(int argc, char **argv)
params.timeout = val;
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':;
char *data = ftob(optarg, &val);
if (!data) {
@ -585,7 +651,7 @@ int main(int argc, char **argv)
break;
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;
else
dp->to_ip = 2;
@ -652,16 +718,8 @@ int main(int argc, char **argv)
break;
case 'l':
fake_tls.data = ftob(optarg, &fake_tls.size);
if (!fake_tls.data) {
uniperror("read/parse");
invalid = 1;
}
break;
case 'j':
fake_http.data = ftob(optarg, &fake_http.size);
if (!fake_http.data) {
dp->fake_data.data = ftob(optarg, &dp->fake_data.size);
if (!dp->fake_data.data) {
uniperror("read/parse");
invalid = 1;
}
@ -750,7 +808,7 @@ int main(int argc, char **argv)
clear_params();
return -1;
}
if (dp->hosts) {
if (dp->hosts || dp->proto) {
dp = add((void *)&params.dp,
&params.dp_count, sizeof(struct desync_params));
if (!dp) {

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)
{
if (ANTOHS(buffer, 0) != 0x1603) {
if (!is_tls_chello(buffer, bsize)) {
return 0;
}
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) {
return 0;
}
if (ANTOHS(req, 0) != 0x1603
if (!is_tls_chello(req, qn)
|| ANTOHS(resp, 0) != 0x1603) {
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)
{
char *host = 0, *par;

View File

@ -6,6 +6,8 @@
#define IS_UNKNOWN 0
#define IS_HTTP 1
#define IS_HTTPS 2
#define IS_QUIC 4
#define IS_DNS 8
#define MH_HMIX 1
#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);
bool is_tls_chello(char *buffer, size_t bsize);
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 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);
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;
};
struct packet {
ssize_t size;
char *data;
};
struct desync_params {
int ttl;
char *ip_options;
ssize_t ip_options_len;
char md5sig;
struct packet fake_data;
int parts_n;
struct part *parts;
int mod_http;
int tlsrec_n;
struct part *tlsrec;
int proto;
int detect;
struct mphdr *hosts;
@ -63,7 +73,6 @@ struct desync_params {
};
struct params {
char de_known;
int dp_count;
struct desync_params *dp;
long sfdelay;
@ -77,6 +86,7 @@ struct params {
long cache_ttl;
char ipv6;
char resolve;
char udp;
int max_open;
int debug;
size_t bfsize;
@ -86,10 +96,6 @@ struct params {
extern struct params params;
struct packet {
ssize_t size;
char *data;
};
extern struct packet fake_tls;
extern struct packet fake_http;
extern struct packet oob_data;

544
proxy.c
View File

@ -1,17 +1,16 @@
#define _GNU_SOURCE
#define EID_STR
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <time.h>
#include <proxy.h>
#include <params.h>
#include <conev.h>
#include <desync.h>
#include <packets.h>
#include <extend.h>
#include <error.h>
#ifdef _WIN32
@ -19,9 +18,6 @@
#include <ws2tcpip.h>
#define close(fd) closesocket(fd)
#ifndef TCP_MAXRT
#define TCP_MAXRT 5
#endif
#else
#include <errno.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)
{
#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,
struct sockaddr_ina *addr)
struct sockaddr_ina *addr, int type)
{
struct addrinfo hints = {0}, *res = 0;
hints.ai_socktype = SOCK_STREAM;
hints.ai_socktype = type;
hints.ai_flags = AI_ADDRCONFIG;
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) {
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);
return -1;
}
@ -241,25 +235,21 @@ int s4_get_addr(char *buff, size_t n,
}
int s5_get_addr(char *buffer, ssize_t n,
struct sockaddr_ina *addr)
int s5_get_addr(char *buffer, size_t n,
struct sockaddr_ina *addr, int type)
{
if (n < S_SIZE_MIN) {
LOG(LOG_E, "ss: request to small\n");
return -1;
return -S_ER_GEN;
}
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_I6 ? S_SIZE_I6 : 0)));
if (n < o) {
LOG(LOG_E, "ss: bad request\n");
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;
return -S_ER_GEN;
}
switch (r->atp) {
case S_ATP_I4:
@ -269,24 +259,55 @@ int s5_get_addr(char *buffer, ssize_t n,
case S_ATP_ID:
if (!params.resolve) {
return S_ER_ATP;
return -S_ER_ATP;
}
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);
return S_ER_HOST;
return -S_ER_HOST;
}
break;
case S_ATP_I6:
if (!params.ipv6)
return S_ER_ATP;
return -S_ER_ATP;
else {
addr->in6.sin6_family = AF_INET6;
addr->in6.sin6_addr = r->i6;
}
}
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;
}
@ -369,6 +390,62 @@ int create_conn(struct poolhd *pool,
pair->pair = val;
pair->in6 = dst->in6;
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;
}
@ -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)
{
ssize_t n = 0;
@ -494,61 +571,65 @@ 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
time_t t;
struct elem *val;
char *str = (char *)&dst->in;
int len = sizeof(dst->sa.sa_family);
char *data = buffer + S_SIZE_I6;
size_t data_len = bfsize - S_SIZE_I6;
do {
struct sockaddr_ina addr = {0};
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) {
if (n && errno == EAGAIN)
break;
perror("recv udp");
return -1;
}
if (!src->in.sin_port) {
src->in6 = addr.in6;
}
ssize_t ns = 0;
if (addr_equ(src, &addr)) {
if (*(data + 2) != 0) { // frag
continue;
}
int offs = s5_get_addr(data, n, &addr, SOCK_DGRAM);
if (offs < 0) {
return -1;
}
val->pair->in6 = addr.in6;
if (src->sa.sa_family == AF_INET6) {
map_fix(&addr, 6);
}
if (src->sa.sa_family != addr.sa.sa_family) {
return -1;
}
ns = udp_hook(val,
data + offs, n - offs, &addr);
}
else {
len = sizeof(dst->in6) - sizeof(dst->in6.sin6_scope_id);
}
len -= sizeof(dst->sa.sa_family);
//map_fix(&addr, 0);
memset(buffer, 0, S_SIZE_I6);
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");
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;
}
val->m = m;
val->time = t;
return 0;
ns = sendto(val->fd, data - offs, offs + n, 0,
(struct sockaddr *)&val->in6, sizeof(val->in6));
}
val = mem_get(params.mempool, str, len);
if (!val) {
if (ns < 0) {
perror("sendto");
return -1;
}
time(&t);
if (t > val->time + params.cache_ttl) {
LOG(LOG_S, "time=%ld, now=%ld, ignore\n", val->time, t);
} while(1);
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 };
addr.in.sin_port = dst->in.sin_port;
return create_conn(pool, val, &addr, next);
}
return create_conn(pool, val, dst, next);
}
static inline int on_request(struct poolhd *pool, struct eval *val,
@ -571,268 +652,58 @@ static inline int on_request(struct poolhd *pool, struct eval *val,
val->flag = FLAG_S5;
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) {
val->flag = FLAG_S4;
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 {
LOG(LOG_E, "ss: invalid version: 0x%x (%lu)\n", *buffer, n);
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) {
int en = get_e();
if (resp_error(val->fd, en ? en : error, val->flag) < 0)
uniperror("send");
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;
}
@ -929,6 +800,11 @@ int event_loop(int srvfd)
del_event(pool, val);
continue;
case EV_UDP_TUNNEL:
if (on_udp_tunnel(val, buffer, bfsize))
del_event(pool, val);
continue;
case EV_CONNECT:
if (on_connect(pool, val, etype & POLLERR))
del_event(pool, val);

14
proxy.h
View File

@ -6,6 +6,8 @@
#include <arpa/inet.h>
#endif
#include <conev.h>
struct sockaddr_ina {
union {
struct sockaddr sa;
@ -94,6 +96,18 @@ enum s4_rep {
#define S_SIZE_I6 22
#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 event_loop(int srvfd);
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
Отключить запутывание для нераспознанных протоколов
Распознаваемые протоколы: HTTP и TLS с SNI
@ -54,12 +57,12 @@ $ ./ciadpi --disorder 3 -A --tlsrec 1+s
cl_err : HTTP ответ, код которого равен 40x, но не 429
sid_inv : session_id в TLS ServerHello и ClientHello не совпадают
alert : TLS Error Alert в ответе
nop : Предыдущая группа пропущена, например из-за ограничения по hosts
none : Предыдущая группа пропущена, например из-за ограничения по доменам или протоколам
По умолчанию обрабатывается только torst
Можно указывать несколько групп опций, раделяя их данным параметром
Если соединение успешно прошло, то параметры для данного 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
@ -71,6 +74,9 @@ $ ./ciadpi --disorder 3 -A --tlsrec 1+s
В Linux переводится в миллисекунды, поэтому можно указать дробное число
Истечение таймаута будет обработано --auto
-K, --proto[=t,h,d,q]
Белый список протоколов: tls,http,dns,quic
-H, --hosts <file|:string>
Ограничить область действия параметров списком доменов
Домены должны быть разделены новой строкой или пробелом
@ -113,8 +119,7 @@ $ ./ciadpi --disorder 3 -A --tlsrec 1+s
Большинство серверов (в основном на Linux) отбрасывают пакеты с данной опцией
Поддерживается только в Linux, может быть выключен в некоторых сборках ядра (< 3.9, Android)
-l, --fake-tls <file|:str>
-j, --fake-http <file|:str>
-l, --fake-data <file|:str>
Указать свои поддельные пакеты, вместо дефолтных
-e, --oob-data <file|:str>
@ -228,25 +233,31 @@ TCP может отсылать данные вне основного пото
--auto, --hosts
Параметр auto делит опции на группы.
Для каждого запроса они обходятся слева на право.
Сначала проверяется триггер указанный в auto, затем hosts.
Сначала проверяется триггер, указанный в auto, затем proto и hosts.
Примеры:
--fake -1 --ttl 10 --auto=alert,sid_inv --fake -1 --ttl 5
По умолчанию использовать fake с ttl=10, в случае ошибки использовать fake с ttl=5
--hosts list.txt --disorder 3 --auto=nop
--hosts list.txt --disorder 3 --auto=none
Применять запутывание только для доменов из list.txt
--hosts list.txt --auto=nop --disorder 3
--hosts list.txt --auto=none --disorder 3
Не применять запутывание для доменов из 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 и игнорируя адрес из запроса.
Для остальных ничего не делать, однако если обнаружится блокировка, то попробовать применить tlsrec.
--auto=torst --hosts list.txt --disorder 3
По умолчанию ничего не делать, использовать disorder при условии, что произошла блокировка и домен входит в list.txt.
--proto=http,tls --disorder 3 --auto=none
Запутывать только HTTP и TLS
--proto=http --fake -1 --fake-data=':GET /...' --auto=none --fake -1
Переопределить фейковый пакет для HTTP
-------
Сборка:
Для сборки понадобится: