mirror of
https://github.com/hufrea/byedpi.git
synced 2024-12-22 06:15:14 +00:00
UDP, --proto, dst custom port
This commit is contained in:
parent
f9fa5a14f6
commit
a48a2e87a3
2
Makefile
2
Makefile
@ -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)
|
||||
|
5
conev.h
5
conev.h
@ -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
|
||||
|
21
desync.c
21
desync.c
@ -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
413
extend.c
Normal 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 = ¶ms.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 = ¶ms.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 = ¶ms.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 = ¶ms.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 = ¶ms.dp[m];
|
||||
|
||||
if (val->flag != FLAG_CONN) {
|
||||
if (!m) for (; m < params.dp_count; m++) {
|
||||
dp = ¶ms.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
11
extend.h
Normal 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
124
main.c
@ -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 *)¶ms.dp,
|
||||
¶ms.dp_count, sizeof(struct desync_params));
|
||||
if (!dp) {
|
||||
|
27
packets.c
27
packets.c
@ -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;
|
||||
|
10
packets.h
10
packets.h
@ -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);
|
||||
|
16
params.h
16
params.h
@ -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
544
proxy.c
@ -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 *)¶ms.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 = ¶ms.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 = ¶ms.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 = ¶ms.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 = ¶ms.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
14
proxy.h
@ -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);
|
||||
|
27
readme.txt
27
readme.txt
@ -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
|
||||
|
||||
-------
|
||||
Сборка:
|
||||
Для сборки понадобится:
|
||||
|
Loading…
Reference in New Issue
Block a user