From 3cfdf4f7e0a58135bee1312bfb444f1ec6cf5817 Mon Sep 17 00:00:00 2001 From: hufrea <135158507+hufrea@users.noreply.github.com> Date: Sun, 20 Oct 2024 18:50:08 +0300 Subject: [PATCH 1/7] --round (#201) --- conev.h | 17 +- desync.c | 37 ++-- extend.c | 527 ++++++++++++++++++++++++++++++------------------------- extend.h | 19 +- main.c | 28 ++- params.h | 5 + proxy.c | 118 ++++++------- 7 files changed, 399 insertions(+), 352 deletions(-) diff --git a/conev.h b/conev.h index f98a59e..9673bf0 100644 --- a/conev.h +++ b/conev.h @@ -2,6 +2,7 @@ #define CONEV_H #include +#include #ifndef __linux__ #define NOEPOLL @@ -37,9 +38,8 @@ enum eid { EV_CONNECT, EV_IGNORE, EV_TUNNEL, - EV_PRE_TUNNEL, EV_UDP_TUNNEL, - EV_DESYNC + EV_FIRST_TUNNEL }; #define FLAG_S4 1 @@ -53,15 +53,14 @@ char *eid_name[] = { "EV_CONNECT", "EV_IGNORE", "EV_TUNNEL", - "EV_PRE_TUNNEL", "EV_UDP_TUNNEL", - "EV_DESYNC" + "EV_FIRST_TUNNEL" }; #endif struct buffer { - ssize_t size; - int offset; + size_t size; + unsigned int offset; char *data; }; @@ -78,11 +77,11 @@ struct eval { struct sockaddr_in6 in6; }; ssize_t recv_count; + ssize_t round_sent; unsigned int round_count; - char last_round; int attempt; - char cache; - char mark; // + bool cache; + bool mark; // }; struct poolhd { diff --git a/desync.c b/desync.c index feae490..0259ba2 100644 --- a/desync.c +++ b/desync.c @@ -434,19 +434,19 @@ ssize_t desync(int sfd, char *buffer, size_t bfsize, char *host = 0; int len = 0, type = 0, host_pos = 0; - // parse packet - if ((len = parse_tls(buffer, n, &host))) { - type = IS_HTTPS; + if (offset == 0) { + if ((len = parse_tls(buffer, n, &host))) { + type = IS_HTTPS; + } + else if ((len = parse_http(buffer, n, &host, 0))) { + type = IS_HTTP; + } + if (len && host) { + LOG(LOG_S, "host: %.*s (%zd)\n", + len, host, host - buffer); + host_pos = host - buffer; + } } - else if ((len = parse_http(buffer, n, &host, 0))) { - type = IS_HTTP; - } - if (len && host) { - LOG(LOG_S, "host: %.*s (%zd)\n", - len, host, host - buffer); - host_pos = host - buffer; - } - // modify packet if (type == IS_HTTP && dp.mod_http) { LOG(LOG_S, "modify HTTP: n=%zd\n", n); @@ -484,13 +484,12 @@ ssize_t desync(int sfd, char *buffer, size_t bfsize, lp = pos + 5; } } - // desync #ifdef __linux__ - if (dp.drop_sack && drop_sack(sfd)) { + if (!offset && dp.drop_sack && drop_sack(sfd)) { return -1; } #endif - long lp = offset; + long lp = 0; struct part part; int i = 0, r = 0; @@ -504,15 +503,15 @@ ssize_t desync(int sfd, char *buffer, size_t bfsize, pos += part.s * (part.r - r); - // after EAGAIN - if (offset && pos <= offset) { + if (!(part.flag & OFFSET_START) && offset && pos <= offset) { + LOG(LOG_S, "offset: %zd, skip\n", offset); continue; } - else if (pos < 0 || pos > n || pos < lp) { + if (pos < 0 || pos > n || pos < lp) { LOG(LOG_E, "split cancel: pos=%ld-%ld, n=%zd\n", lp, pos, n); break; } - // send part + ssize_t s = 0; switch (part.m) { #ifdef FAKE_SUPPORT diff --git a/extend.c b/extend.c index c396843..5ce77ad 100644 --- a/extend.c +++ b/extend.c @@ -26,7 +26,7 @@ #define KEY_SIZE sizeof(struct sockaddr_ina) -int set_timeout(int fd, unsigned int s) +static int set_timeout(int fd, unsigned int s) { #ifdef __linux__ if (setsockopt(fd, IPPROTO_TCP, @@ -72,50 +72,48 @@ static ssize_t serialize_addr(const struct sockaddr_ina *dst, } -static int mode_add_get(struct sockaddr_ina *dst, int m) +static int cache_get(struct sockaddr_ina *dst) { - // m < 0: get, m > 0: set, m == 0: delete - assert(m >= -1 && m < params.dp_count); + uint8_t key[KEY_SIZE] = { 0 }; + int len = serialize_addr(dst, key, sizeof(key)); - time_t t = 0; - struct elem *val = 0; + struct elem *val = mem_get(params.mempool, (char *)key, len); + if (!val) { + return -1; + } + time_t t = time(0); + if (t > val->time + params.cache_ttl) { + LOG(LOG_S, "time=%jd, now=%jd, ignore\n", (intmax_t)val->time, (intmax_t)t); + return 0; + } + return val->m; +} + + +static int cache_add(struct sockaddr_ina *dst, int m) +{ + assert(m >= 0 && m < params.dp_count); uint8_t key[KEY_SIZE] = { 0 }; int len = serialize_addr(dst, key, sizeof(key)); - assert(len > 0); - if (m < 0) { - val = mem_get(params.mempool, (char *)key, len); - if (!val) { - return -1; - } - time(&t); - if (t > val->time + params.cache_ttl) { - LOG(LOG_S, "time=%jd, now=%jd, ignore\n", (intmax_t)val->time, (intmax_t)t); - return 0; - } - return val->m; - } INIT_ADDR_STR((*dst)); - if (m == 0) { LOG(LOG_S, "delete ip: %s\n", ADDR_STR); mem_delete(params.mempool, (char *)key, len); return 0; } - else { - LOG(LOG_S, "save ip: %s, m=%d\n", ADDR_STR, m); - time(&t); + LOG(LOG_S, "save ip: %s, m=%d\n", ADDR_STR, m); + time_t t = time(0); - val = mem_add(params.mempool, (char *)key, len); - if (!val) { - uniperror("mem_add"); - return -1; - } - val->m = m; - val->time = t; - return 0; + struct elem *val = mem_add(params.mempool, (char *)key, len); + if (!val) { + uniperror("mem_add"); + return -1; } + val->m = m; + val->time = t; + return 0; } @@ -129,7 +127,7 @@ static inline bool check_port(uint16_t *p, struct sockaddr_in6 *dst) int connect_hook(struct poolhd *pool, struct eval *val, struct sockaddr_ina *dst, int next) { - int m = mode_add_get(dst, -1); + int m = cache_get(dst); val->cache = (m == 0); val->attempt = m < 0 ? 0 : m; @@ -151,31 +149,34 @@ int socket_mod(int fd, struct sockaddr *dst) } -int reconnect(struct poolhd *pool, struct eval *val, int m) +static int reconnect(struct poolhd *pool, struct eval *val, int m) { + assert(val->flag == FLAG_CONN); + struct eval *client = val->pair; if (create_conn(pool, client, - (struct sockaddr_ina *)&val->in6, EV_DESYNC)) { + (struct sockaddr_ina *)&val->in6, EV_FIRST_TUNNEL)) { return -1; } val->pair = 0; del_event(pool, val); - client->type = EV_IGNORE; + //client->type = EV_IGNORE; client->attempt = m; client->cache = 1; client->buff.offset = 0; + client->round_sent = 0; return 0; } -bool check_host(struct mphdr *hosts, struct eval *val) +static bool check_host(struct mphdr *hosts, char *buffer, ssize_t n) { char *host = 0; int len; - if (!(len = parse_tls(val->buff.data, val->buff.size, &host))) { - len = parse_http(val->buff.data, val->buff.size, &host, 0); + if (!(len = parse_tls(buffer, n, &host))) { + len = parse_http(buffer, n, &host, 0); } assert(len == 0 || host != 0); if (len <= 0) { @@ -194,111 +195,90 @@ bool check_host(struct mphdr *hosts, struct eval *val) } -bool check_proto_tcp(int proto, struct eval *val) +static bool check_proto_tcp(int proto, char *buffer, ssize_t n) { if (proto & IS_TCP) { return 1; } else if ((proto & IS_HTTP) && - is_http(val->buff.data, val->buff.size)) { + is_http(buffer, n)) { return 1; } else if ((proto & IS_HTTPS) && - is_tls_chello(val->buff.data, val->buff.size)) { + is_tls_chello(buffer, n)) { return 1; } return 0; } -int on_torst(struct poolhd *pool, struct eval *val) +static bool check_round(int nr[2], int r) { - int m = val->pair->attempt + 1; - - bool can_reconn = ( - val->pair->buff.data && !val->recv_count - ); - if (can_reconn || params.auto_level >= 1) { - for (; m < params.dp_count; m++) { - struct desync_params *dp = ¶ms.dp[m]; - if (!dp->detect) { - m = 0; - break; - } - if (dp->detect & DETECT_TORST) { - break; - } - } - if (m == 0) { - } - else if (m >= params.dp_count) { - if (m > 1) mode_add_get( - (struct sockaddr_ina *)&val->in6, 0); - } - else if (can_reconn) { - return reconnect(pool, val, m); - } - else mode_add_get( - (struct sockaddr_ina *)&val->in6, m); - } - struct linger l = { .l_onoff = 1 }; - if (setsockopt(val->pair->fd, SOL_SOCKET, - SO_LINGER, (char *)&l, sizeof(l)) < 0) { - uniperror("setsockopt SO_LINGER"); - return -1; - } - return -1; + return (!nr[1] && r <= 1) || (r >= nr[0] && r <= nr[1]); } -int on_fin(struct poolhd *pool, struct eval *val) +static int on_trigger(int type, struct poolhd *pool, struct eval *val) { int m = val->pair->attempt + 1; bool can_reconn = ( val->pair->buff.data && !val->recv_count + && params.auto_level > AUTO_NOBUFF ); - if (!can_reconn && params.auto_level < 1) { - return -1; - } - bool ssl_err = 0; - - if (can_reconn) { - char *req = val->pair->buff.data; - ssize_t qn = val->pair->buff.size; - - ssl_err = is_tls_chello(req, qn); - } - else if (val->mark && val->round_count <= 1) { - ssl_err = 1; - } - if (!ssl_err) { + if (!can_reconn && params.auto_level <= AUTO_NOSAVE) { return -1; } for (; m < params.dp_count; m++) { struct desync_params *dp = ¶ms.dp[m]; if (!dp->detect) { - return -1; + break; } - if (dp->detect & DETECT_TLS_ERR) { - if (can_reconn) - return reconnect(pool, val, m); - else { - mode_add_get( - (struct sockaddr_ina *)&val->in6, m); - return -1; - } + if (!(dp->detect & type)) { + continue; } + if (can_reconn) { + return reconnect(pool, val, m); + } + cache_add( + (struct sockaddr_ina *)&val->in6, m); + break; } - if (m > 1) { // delete - mode_add_get( + if (m >= params.dp_count && m > 1) { + cache_add( (struct sockaddr_ina *)&val->in6, 0); } return -1; } -int on_response(struct poolhd *pool, struct eval *val, +static int on_torst(struct poolhd *pool, struct eval *val) +{ + if (on_trigger(DETECT_TORST, pool, val) == 0) { + return 0; + } + struct linger l = { .l_onoff = 1 }; + if (setsockopt(val->pair->fd, SOL_SOCKET, + SO_LINGER, (char *)&l, sizeof(l)) < 0) { + uniperror("setsockopt SO_LINGER"); + } + return -1; +} + + +static int on_fin(struct poolhd *pool, struct eval *val) +{ + if (!(val->pair->mark && val->round_count <= 1)) { + return -1; + } + if (on_trigger(DETECT_TLS_ERR, pool, val) == 0) { + return 0; + } + return -1; +} + + +static int on_response(struct poolhd *pool, struct eval *val, char *resp, ssize_t sn) { int m = val->pair->attempt + 1; @@ -328,177 +308,238 @@ int on_response(struct poolhd *pool, struct eval *val, } -static inline void to_tunnel(struct eval *client) +static inline void free_first_req(struct eval *client) { - client->pair->type = EV_TUNNEL; client->type = EV_TUNNEL; + client->pair->type = EV_TUNNEL; assert(client->buff.data); free(client->buff.data); - client->buff.data = 0; - client->buff.size = 0; - client->buff.offset = 0; + memset(&client->buff, 0, sizeof(client->buff)); } -int on_tunnel_check(struct poolhd *pool, struct eval *val, - char *buffer, size_t bfsize, int out) +static int setup_conn(struct eval *client, char *buffer, ssize_t n) +{ + int m = client->attempt; + + if (!m) for (; m < params.dp_count; m++) { + struct desync_params *dp = ¶ms.dp[m]; + if (!dp->detect && + (!dp->pf[0] || check_port(dp->pf, &client->pair->in6)) && + (!dp->proto || check_proto_tcp(dp->proto, buffer, n)) && + (!dp->hosts || check_host(dp->hosts, buffer, n))) { + break; + } + } + if (m >= params.dp_count) { + LOG(LOG_E, "drop connection (m=%d)\n", m); + return -1; + } + if (params.auto_level > AUTO_NOBUFF && params.dp_count > 1) { + client->mark = is_tls_chello(buffer, n); + } + client->attempt = m; + + if (params.timeout + && set_timeout(client->pair->fd, params.timeout)) { + return -1; + } + return 0; +} + + +static int cancel_setup(struct eval *remote) +{ + if (params.timeout && params.auto_level <= AUTO_NOSAVE && + set_timeout(remote->fd, 0)) { + return -1; + } + if (post_desync(remote->fd, remote->pair->attempt)) { + return -1; + } + return 0; +} + + +int send_saved_req(struct poolhd *pool, + struct eval *client, char *buffer, ssize_t bfsize) +{ + ssize_t offset = client->buff.offset; + ssize_t n = client->buff.size - offset; + assert(bfsize >= n); + memcpy(buffer, client->buff.data + offset, n); + + ssize_t sn = tcp_send_hook(client->pair, buffer, bfsize, n); + if (sn < 0) { + return -1; + } + client->buff.offset += sn; + if (sn < n) { + if (mod_etype(pool, client->pair, POLLOUT) || + mod_etype(pool, client, 0)) { + uniperror("mod_etype"); + return -1; + } + } + return 0; +} + + +int on_first_tunnel(struct poolhd *pool, + struct eval *val, char *buffer, ssize_t bfsize, int etype) +{ + if ((etype & POLLOUT) && val->flag == FLAG_CONN) { + if (mod_etype(pool, val, POLLIN) || + mod_etype(pool, val->pair, POLLIN)) { + uniperror("mod_etype"); + return -1; + } + return send_saved_req(pool, val->pair, buffer, bfsize); + } + ssize_t n = tcp_recv_hook(pool, val, buffer, bfsize); + if (n < 1) { + return n; + } + if (val->flag != FLAG_CONN) { + val->buff.size += n; + + if (val->buff.size >= bfsize) { + free_first_req(val); + } + else { + val->buff.data = realloc(val->buff.data, val->buff.size); + + if (val->buff.data == 0) { + uniperror("realloc"); + return -1; + } + memcpy(val->buff.data + val->buff.size - n, buffer, n); + return send_saved_req(pool, val, buffer, bfsize); + } + } + else { + if (on_response(pool, val, buffer, n) == 0) { + return 0; + } + free_first_req(val->pair); + int m = val->pair->attempt; + + if (val->pair->cache && + cache_add((struct sockaddr_ina *)&val->in6, m) < 0) { + return -1; + } + } + if (tcp_send_hook(val->pair, buffer, bfsize, n) < n) { + return -1; + } + return 0; +} + + +ssize_t tcp_send_hook(struct eval *remote, + char *buffer, size_t bfsize, ssize_t n) +{ + ssize_t sn = -1; + int skip = remote->flag != FLAG_CONN; + + if (!skip) { + struct eval *client = remote->pair; + + if (client->recv_count == n + && setup_conn(client, buffer, n) < 0) { + return -1; + } + int m = client->attempt, r = client->round_count; + if (!check_round(params.dp[m].rounds, r)) { + skip = 1; + } + else { + LOG((m ? LOG_S : LOG_L), "desync TCP, m=%d, r=%d\n", m, r); + + ssize_t offset = remote->pair->round_sent; + if (!offset && remote->round_count) offset = -1; + + sn = desync(remote->fd, buffer, bfsize, n, + offset, (struct sockaddr *)&remote->in6, m); + } + } + if (skip) { + sn = send(remote->fd, buffer, n, 0); + if (sn < 0 && get_e() == EAGAIN) { + return 0; + } + } + remote->pair->round_sent += sn; + return sn; +} + + +ssize_t tcp_recv_hook(struct poolhd *pool, struct eval *val, + char *buffer, size_t bfsize) { - assert(!out); ssize_t n = recv(val->fd, buffer, bfsize, 0); if (n < 1) { if (!n) { + if (val->flag != FLAG_CONN) { + val = val->pair; + } return on_fin(pool, val); } + if (get_e() == EAGAIN) { + return 0; + } uniperror("recv"); switch (get_e()) { case ECONNRESET: case ECONNREFUSED: case ETIMEDOUT: - return on_torst(pool, val); + if (val->flag == FLAG_CONN) + return on_torst(pool, val); + else + return on_fin(pool, val->pair); } return -1; } - // - if (on_response(pool, val, buffer, n) == 0) { - return 0; - } val->recv_count += n; - val->round_count = 1; - val->last_round = 1; - struct eval *pair = val->pair; - - ssize_t sn = send(pair->fd, buffer, n, 0); - if (n != sn) { - uniperror("send"); + if (val->round_sent == 0) { + val->round_count++; + val->pair->round_sent = 0; + } + if (val->flag == FLAG_CONN + && check_round( + params.dp[val->pair->attempt].rounds, val->round_count) + && cancel_setup(val)) { return -1; } - if (params.auto_level > 0 && params.dp_count > 1) { - val->mark = is_tls_chello(pair->buff.data, pair->buff.size); - } - to_tunnel(pair); - - if (params.timeout && params.auto_level < 1 && - set_timeout(val->fd, 0)) { - return -1; - } - int m = pair->attempt; - - if (post_desync(val->fd, m)) { - return -1; - } - - if (!pair->cache) { - return 0; - } - return mode_add_get((struct sockaddr_ina *)&val->in6, m); -} - - -int on_desync_again(struct poolhd *pool, - struct eval *val, char *buffer, size_t bfsize) -{ - if (val->flag == FLAG_CONN) { - if (mod_etype(pool, val, POLLIN) || - mod_etype(pool, val->pair, POLLIN)) { - uniperror("mod_etype"); - return -1; - } - val = val->pair; - } - int m = val->attempt; - LOG((m ? LOG_S : LOG_L), "desync params index: %d\n", m); - - ssize_t n = val->buff.size; - assert(n > 0 && n <= params.bfsize); - 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; - } - val->buff.offset += sn; - if (sn < n) { - if (mod_etype(pool, val->pair, POLLOUT)) { - uniperror("mod_etype"); - return -1; - } - val->pair->type = EV_DESYNC; - return 0; - } - val->pair->type = EV_PRE_TUNNEL; - return 0; -} - - -int on_desync(struct poolhd *pool, struct eval *val, - char *buffer, size_t bfsize, int out) -{ - if (out) { - return on_desync_again(pool, val, buffer, bfsize); - } - if (val->buff.size == bfsize) { - to_tunnel(val); - return 0; - } - ssize_t n = recv(val->fd, buffer, bfsize - val->buff.size, 0); - if (n <= 0) { - if (n) uniperror("recv data"); - return -1; - } - val->buff.size += n; - val->recv_count += n; - val->round_count = 1; - - val->buff.data = realloc(val->buff.data, val->buff.size); - if (val->buff.data == 0) { - uniperror("realloc"); - return -1; - } - memcpy(val->buff.data + val->buff.size - n, buffer, n); - - int m = val->attempt; - if (!m) for (; m < params.dp_count; m++) { - struct desync_params *dp = ¶ms.dp[m]; - if (!dp->detect && - (!dp->pf[0] || check_port(dp->pf, &val->pair->in6)) && - (!dp->proto || check_proto_tcp(dp->proto, val)) && - (!dp->hosts || check_host(dp->hosts, val))) { - break; - } - } - if (m >= params.dp_count) { - return -1; - } - val->attempt = m; - - return on_desync_again(pool, val, buffer, bfsize); + return n; } ssize_t udp_hook(struct eval *val, char *buffer, size_t bfsize, ssize_t n, struct sockaddr_ina *dst) { - if (val->recv_count) { + struct eval *pair = val->pair->pair; + + int m = pair->attempt, r = pair->round_count; + if (!m) { + for (; m < params.dp_count; m++) { + struct desync_params *dp = ¶ms.dp[m]; + if (!dp->detect && + (!dp->proto || (dp->proto & IS_UDP)) && + (!dp->pf[0] || check_port(dp->pf, &dst->in6))) { + break; + } + } + if (m >= params.dp_count) { + return -1; + } + pair->attempt = m; + } + if (!check_round(params.dp[m].rounds, r)) { return send(val->fd, buffer, n, 0); } - int m = val->attempt; - if (!m) for (; m < params.dp_count; m++) { - struct desync_params *dp = ¶ms.dp[m]; - if (!dp->detect && - (!dp->proto || (dp->proto & IS_UDP)) && - (!dp->pf[0] || check_port(dp->pf, &dst->in6))) { - break; - } - } - if (m >= params.dp_count) { - return -1; - } + LOG(LOG_S, "desync UDP, m=%d, r=%d\n", m, r); return desync_udp(val->fd, buffer, bfsize, n, &dst->sa, m); } diff --git a/extend.h b/extend.h index 439b4e3..affce4f 100644 --- a/extend.h +++ b/extend.h @@ -10,19 +10,18 @@ int socket_mod(int fd, struct sockaddr *dst); 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, int out); - +ssize_t tcp_send_hook(struct eval *val, + char *buffer, size_t bfsize, ssize_t n); + +ssize_t tcp_recv_hook(struct poolhd *pool, struct eval *val, + char *buffer, size_t bfsize); + ssize_t udp_hook(struct eval *val, char *buffer, size_t bfsize, ssize_t n, struct sockaddr_ina *dst); -int on_torst(struct poolhd *pool, struct eval *val); - -int on_fin(struct poolhd *pool, struct eval *val); - +int on_first_tunnel(struct poolhd *pool, + struct eval *val, char *buffer, ssize_t bfsize, int etype); + #ifdef __linux__ int protect(int conn_fd, const char *path); #else diff --git a/main.c b/main.c index ace467b..f38db60 100644 --- a/main.c +++ b/main.c @@ -55,7 +55,7 @@ struct params params = { .sin6_family = AF_INET }, .debug = 0, - .auto_level = 0 + .auto_level = AUTO_NOBUFF }; @@ -86,6 +86,7 @@ const char help_text[] = { " -K, --proto Protocol whitelist: tls,http,udp\n" " -H, --hosts Hosts whitelist, filename or :string\n" " -V, --pf Ports range whitelist\n" + " -R, --round Number of request to which desync will be applied\n" " -s, --split Position format: offset[:repeats:skip][+flag1[flag2]]\n" " Flags: +s - SNI offset, +h - HTTP host offset\n" " Additional flags: +e - end, +m - middle, +r - random\n" @@ -141,6 +142,7 @@ const struct option options[] = { {"proto", 1, 0, 'K'}, {"hosts", 1, 0, 'H'}, {"pf", 1, 0, 'V'}, + {"repeats", 1, 0, 'R'}, {"split", 1, 0, 's'}, {"disorder", 1, 0, 'd'}, {"oob", 1, 0, 'o'}, @@ -418,7 +420,8 @@ int parse_offset(struct part *part, const char *str) case 'r': part->flag |= OFFSET_RAND; break; - case 's':; + case 's': + part->flag |= OFFSET_START; } } part->pos = val; @@ -648,6 +651,9 @@ int main(int argc, char **argv) end = strchr(end, ','); if (end) end++; } + if (dp->detect && params.auto_level == AUTO_NOBUFF) { + params.auto_level = AUTO_NOSAVE; + } break; case 'u': @@ -866,6 +872,24 @@ int main(int argc, char **argv) } break; + case 'R': + val = strtol(optarg, &end, 0); + if (val <= 0 || val > INT_MAX) + invalid = 1; + else { + dp->rounds[0] = val; + if (*end == '-') { + val = strtol(end + 1, &end, 0); + if (val <= 0 || val > INT_MAX) + invalid = 1; + } + if (*end) + invalid = 1; + else + dp->rounds[1] = val; + } + break; + case 'g': val = strtol(optarg, &end, 0); if (val <= 0 || val > 255 || *end) diff --git a/params.h b/params.h index c715496..c30d857 100644 --- a/params.h +++ b/params.h @@ -26,11 +26,15 @@ #define OFFSET_RAND 4 #define OFFSET_SNI 8 #define OFFSET_HOST 16 +#define OFFSET_START 32 #define DETECT_HTTP_LOCAT 1 #define DETECT_TLS_ERR 2 #define DETECT_TORST 8 +#define AUTO_NOBUFF -1 +#define AUTO_NOSAVE 0 + enum demode { DESYNC_NONE, DESYNC_SPLIT, @@ -85,6 +89,7 @@ struct desync_params { int detect; struct mphdr *hosts; uint16_t pf[2]; + int rounds[2]; char *file_ptr; ssize_t file_size; diff --git a/proxy.c b/proxy.c index eadfff6..846f1e2 100644 --- a/proxy.c +++ b/proxy.c @@ -424,7 +424,7 @@ int create_conn(struct poolhd *pool, pair->in6 = dst->in6; #endif pair->flag = FLAG_CONN; - val->type = EV_IGNORE; + //val->type = EV_IGNORE; if (params.debug) { INIT_ADDR_STR((*dst)); @@ -478,11 +478,6 @@ int udp_associate(struct poolhd *pool, } pair->in6 = addr.in6; } - if (params.debug) { - INIT_ADDR_STR((*dst)); - LOG(LOG_S, "udp associate: fd=%d, addr=%s:%d\n", - ufd, ADDR_STR, ntohs(dst->in.sin_port)); - } // socklen_t sz = sizeof(addr); @@ -510,6 +505,11 @@ int udp_associate(struct poolhd *pool, close(cfd); return -1; } + if (params.debug) { + INIT_ADDR_STR((*dst)); + LOG(LOG_S, "udp associate: fds=%d,%d addr=%s:%d\n", + ufd, cfd, ADDR_STR, ntohs(dst->in.sin_port)); + } val->type = EV_IGNORE; val->pair = client; client->pair = pair; @@ -648,15 +648,14 @@ int on_tunnel(struct poolhd *pool, struct eval *val, } n = val->buff.size - val->buff.offset; - ssize_t sn = send(pair->fd, - val->buff.data + val->buff.offset, n, 0); - if (sn != n) { - if (sn < 0 && get_e() != EAGAIN) { - uniperror("send"); - return -1; - } - if (sn > 0) - val->buff.offset += sn; + ssize_t sn = tcp_send_hook(pair, + val->buff.data + val->buff.offset, n, n); + if (sn < 0) { + uniperror("send"); + return -1; + } + if (sn < n) { + val->buff.offset += sn; return 0; } free(val->buff.data); @@ -671,44 +670,20 @@ int on_tunnel(struct poolhd *pool, struct eval *val, } } do { - n = recv(val->fd, buffer, bfsize, 0); - if (n < 0 && get_e() == EAGAIN) { + n = tcp_recv_hook(pool, val, buffer, bfsize); + //if (n < 0 && get_e() == EAGAIN) { + if (n == 0) { break; } - if (n == 0) { - if (val->flag != FLAG_CONN) - val = val->pair; - on_fin(pool, val); - return -1; - } if (n < 0) { - uniperror("recv"); - switch (get_e()) { - case ECONNRESET: - case ETIMEDOUT: - if (val->flag == FLAG_CONN) - on_torst(pool, val); - else - on_fin(pool, val->pair); - } return -1; } - val->recv_count += n; - if (!val->last_round) { - val->round_count++; - val->last_round = 1; - pair->last_round = 0; + ssize_t sn = tcp_send_hook(pair, buffer, bfsize, n); + if (sn < 0) { + uniperror("send"); + return -1; } - - ssize_t sn = send(pair->fd, buffer, n, 0); - if (sn != n) { - if (sn < 0) { - if (get_e() != EAGAIN) { - uniperror("send"); - return -1; - } - sn = 0; - } + if (sn < n) { LOG(LOG_S, "send: %zd != %zd (fd: %d)\n", sn, n, pair->fd); assert(!(val->buff.size || val->buff.offset)); @@ -741,6 +716,8 @@ int on_udp_tunnel(struct eval *val, char *buffer, size_t bfsize) data_len -= S_SIZE_I6; } struct sockaddr_ina addr = {0}; + struct eval *pair = val->flag == FLAG_CONN ? + val->pair : val->pair->pair; do { socklen_t asz = sizeof(addr); @@ -753,6 +730,11 @@ int on_udp_tunnel(struct eval *val, char *buffer, size_t bfsize) return -1; } val->recv_count += n; + if (val->round_sent == 0) { + val->round_count++; + val->round_sent += n; + pair->round_sent = 0; + } ssize_t ns; if (val->flag == FLAG_CONN) { @@ -774,24 +756,24 @@ int on_udp_tunnel(struct eval *val, char *buffer, size_t bfsize) LOG(LOG_E, "udp parse error\n"); return -1; } - if (!val->pair->in6.sin6_port) { + if (!pair->in6.sin6_port) { if (params.baddr.sin6_family == AF_INET6) { map_fix(&addr, 6); } if (params.baddr.sin6_family != addr.sa.sa_family) { return -1; } - if (socket_mod(val->pair->fd, &addr.sa) < 0) { + if (socket_mod(pair->fd, &addr.sa) < 0) { return -1; } - if (connect(val->pair->fd, &addr.sa, SA_SIZE(&addr)) < 0) { + if (connect(pair->fd, &addr.sa, SA_SIZE(&addr)) < 0) { uniperror("connect"); return -1; } - val->pair->in6 = addr.in6; + pair->in6 = addr.in6; } - ns = udp_hook(val->pair, data + offs, bfsize - offs, n - offs, - (struct sockaddr_ina *)&val->pair->in6); + ns = udp_hook(pair, data + offs, bfsize - offs, n - offs, + (struct sockaddr_ina *)&pair->in6); } else { map_fix(&addr, 0); @@ -801,7 +783,7 @@ int on_udp_tunnel(struct eval *val, char *buffer, size_t bfsize) if (offs < 0 || offs > S_SIZE_I6) { return -1; } - ns = send(val->pair->pair->fd, data - offs, offs + n, 0); + ns = send(pair->fd, data - offs, offs + n, 0); } if (ns < 0) { uniperror("sendto"); @@ -906,8 +888,10 @@ static inline int on_connect(struct poolhd *pool, struct eval *val, int e) uniperror("mod_etype"); return -1; } - val->type = EV_TUNNEL; - val->pair->type = EV_DESYNC; + int t = params.auto_level <= AUTO_NOBUFF + ? EV_TUNNEL : EV_FIRST_TUNNEL; + val->type = t; + val->pair->type = t; } if (resp_error(val->pair->fd, error, val->pair->flag) < 0) { @@ -920,10 +904,13 @@ static inline int on_connect(struct poolhd *pool, struct eval *val, int e) void close_conn(struct poolhd *pool, struct eval *val) { - LOG(LOG_S, "close: fds=%d,%d, recv: %zd,%zd, rounds: %d,%d\n", - val->fd, val->pair ? val->pair->fd : -1, - val->recv_count, val->pair ? val->pair->recv_count : 0, - val->round_count, val->pair ? val->pair->round_count : 0); + struct eval *cval = val; + do { + LOG(LOG_S, "close: fd=%d (pair=%d), recv: %zd, rounds: %d\n", + cval->fd, cval->pair ? cval->pair->fd : -1, + cval->recv_count, cval->round_count); + cval = cval->pair; + } while (cval && cval != val); del_event(pool, val); } @@ -977,9 +964,8 @@ int event_loop(int srvfd) close_conn(pool, val); continue; - case EV_PRE_TUNNEL: - if (on_tunnel_check(pool, val, - buffer, bfsize, etype & POLLOUT)) + case EV_FIRST_TUNNEL: + if (on_first_tunnel(pool, val, buffer, bfsize, etype)) close_conn(pool, val); continue; @@ -998,12 +984,6 @@ int event_loop(int srvfd) close_conn(pool, val); continue; - case EV_DESYNC: - if (on_desync(pool, val, - buffer, bfsize, etype & POLLOUT)) - close_conn(pool, val); - continue; - case EV_IGNORE: if (etype & (POLLHUP | POLLERR | POLLRDHUP)) close_conn(pool, val); From 193c93a68f7ca82ceef3c11dece8d2ac89f9d249 Mon Sep 17 00:00:00 2001 From: ruti <> Date: Sun, 20 Oct 2024 19:33:43 +0300 Subject: [PATCH 2/7] Bump version, update README.md --- README.md | 30 ++++++++++++++++++++---------- main.c | 4 ++-- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index b2e6940..c40c5d4 100644 --- a/README.md +++ b/README.md @@ -79,24 +79,34 @@ ciadpi --fake -1 --ttl 8 -V, --pf Ограничитель по портам --s, --split - Разбить запрос по указанному смещению - После числа можно добавить флаг: +-R, --round + К каким/какому запросу применять запутывание + +-s, --split + Разбить запрос по указанной позиции + Позиция имеет вид offset[:repeats:skip][+flag1[flag2]] + Флаги: +s: добавить смещение SNI +h: добавить смещение Host - Можно указывать несколько раз, чтобы разбить запрос по нескольким позициям - При указании отрицательного значения к нему прибавляется размер пакета + +n: нулевое смещение + Дополнительные флаги: + +e: конец; +m: середина; +r: случайная позиция + Примеры: + 0+sm - разбить запрос в середине SNI + 1:3:5 - разбить по позициям 1, 6 и 11 + Ключ можно указывать несколько раз, чтобы разбить запрос по нескольким позициям + Если offset отрицательный и не имеет флагов, то к нему прибавляется размер пакета --d, --disorder +-d, --disorder Подобен --split, но части отправляются в обратном порядке --o, --oob +-o, --oob Подобен --split, но часть отсылается как OOB данные --q, --disoob +-q, --disoob Подобен --disorder, но часть отсылается как OOB данные --f, --fake +-f, --fake Подобен --disorder, только перед отправкой первого куска отправляется часть поддельного Количество байт отправляемого из фейка равно рамеру разбиваемой части @@ -136,7 +146,7 @@ ciadpi --fake -1 --ttl 8 rmspace: "Host: name" -> "Host:name\t" --r, --tlsrec +-r, --tlsrec Разделить ClientHello на отдельные записи по указанному смещению Можно указывать несколько раз diff --git a/main.c b/main.c index f38db60..cab4f26 100644 --- a/main.c +++ b/main.c @@ -23,7 +23,7 @@ #define close(fd) closesocket(fd) #endif -#define VERSION "14.1" +#define VERSION "15" char ip_option[1] = "\0"; @@ -86,7 +86,7 @@ const char help_text[] = { " -K, --proto Protocol whitelist: tls,http,udp\n" " -H, --hosts Hosts whitelist, filename or :string\n" " -V, --pf Ports range whitelist\n" - " -R, --round Number of request to which desync will be applied\n" + " -R, --round Number of request to which desync will be applied\n" " -s, --split Position format: offset[:repeats:skip][+flag1[flag2]]\n" " Flags: +s - SNI offset, +h - HTTP host offset\n" " Additional flags: +e - end, +m - middle, +r - random\n" From 9318e90e4e2f8e0cade4e3d7ad1cbdacb5c1dbf3 Mon Sep 17 00:00:00 2001 From: ruti <> Date: Tue, 22 Oct 2024 10:03:56 +0300 Subject: [PATCH 3/7] Remove false assert #212 --- extend.c | 1 - 1 file changed, 1 deletion(-) diff --git a/extend.c b/extend.c index 5ce77ad..67aa459 100644 --- a/extend.c +++ b/extend.c @@ -313,7 +313,6 @@ static inline void free_first_req(struct eval *client) client->type = EV_TUNNEL; client->pair->type = EV_TUNNEL; - assert(client->buff.data); free(client->buff.data); memset(&client->buff, 0, sizeof(client->buff)); } From bbe9522242a92b2d939a040ea2da3a4be7e9640a Mon Sep 17 00:00:00 2001 From: ruti <> Date: Tue, 22 Oct 2024 11:45:54 +0300 Subject: [PATCH 4/7] Call the post desync func only once #212 --- extend.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/extend.c b/extend.c index 67aa459..37f8f70 100644 --- a/extend.c +++ b/extend.c @@ -212,7 +212,7 @@ static bool check_proto_tcp(int proto, char *buffer, ssize_t n) } -static bool check_round(int nr[2], int r) +static bool check_round(int *nr, int r) { return (!nr[1] && r <= 1) || (r >= nr[0] && r <= nr[1]); } @@ -505,11 +505,14 @@ ssize_t tcp_recv_hook(struct poolhd *pool, struct eval *val, val->round_count++; val->pair->round_sent = 0; } - if (val->flag == FLAG_CONN - && check_round( - params.dp[val->pair->attempt].rounds, val->round_count) - && cancel_setup(val)) { - return -1; + if (val->flag == FLAG_CONN && !val->round_sent) { + int *nr = params.dp[val->pair->attempt].rounds; + + if (check_round(nr, val->round_count) + && !check_round(nr, val->round_count + 1) + && cancel_setup(val)) { + return -1; + } } return n; } From c3c196e74111b0e1e662fb7a8d0371ce1fe84155 Mon Sep 17 00:00:00 2001 From: ruti <> Date: Tue, 22 Oct 2024 18:30:08 +0300 Subject: [PATCH 5/7] General socket init for TCP and UDP --- proxy.c | 56 +++++++++++++++++++++----------------------------------- 1 file changed, 21 insertions(+), 35 deletions(-) diff --git a/proxy.c b/proxy.c index 846f1e2..c918541 100644 --- a/proxy.c +++ b/proxy.c @@ -334,30 +334,27 @@ int s5_set_addr(char *buffer, size_t n, } -int create_conn(struct poolhd *pool, - struct eval *val, struct sockaddr_ina *dst, int next) +static int remote_sock(struct sockaddr_ina *dst, int type) { - struct sockaddr_ina addr = *dst; - if (params.baddr.sin6_family == AF_INET6) { - map_fix(&addr, 6); + map_fix(dst, 6); } else { - map_fix(&addr, 0); + map_fix(dst, 0); } - if (addr.sa.sa_family != params.baddr.sin6_family) { + if (dst->sa.sa_family != params.baddr.sin6_family) { LOG(LOG_E, "different addresses family\n"); return -1; } - int sfd = nb_socket(addr.sa.sa_family, SOCK_STREAM); + int sfd = nb_socket(dst->sa.sa_family, type); if (sfd < 0) { uniperror("socket"); return -1; } - if (socket_mod(sfd, &addr.sa) < 0) { + if (socket_mod(sfd, &dst->sa) < 0) { close(sfd); return -1; } - if (addr.sa.sa_family == AF_INET6) { + if (dst->sa.sa_family == AF_INET6) { int no = 0; if (setsockopt(sfd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&no, sizeof(no))) { @@ -372,6 +369,19 @@ int create_conn(struct poolhd *pool, close(sfd); return -1; } + return sfd; +} + + +int create_conn(struct poolhd *pool, + struct eval *val, struct sockaddr_ina *dst, int next) +{ + struct sockaddr_ina addr = *dst; + + int sfd = remote_sock(&addr, SOCK_STREAM); + if (sfd < 0) { + return -1; + } #ifdef __linux__ int syn_count = 1; if (setsockopt(sfd, IPPROTO_TCP, @@ -440,25 +450,8 @@ int udp_associate(struct poolhd *pool, { struct sockaddr_ina addr = *dst; - int ufd = nb_socket(params.baddr.sin6_family, SOCK_DGRAM); + int ufd = remote_sock(&addr, SOCK_DGRAM); if (ufd < 0) { - uniperror("socket"); - return -1; - } - if (params.baddr.sin6_family == AF_INET6) { - int no = 0; - if (setsockopt(ufd, IPPROTO_IPV6, - IPV6_V6ONLY, (char *)&no, sizeof(no))) { - uniperror("setsockopt IPV6_V6ONLY"); - close(ufd); - return -1; - } - map_fix(&addr, 6); - } - if (bind(ufd, (struct sockaddr *)¶ms.baddr, - SA_SIZE(¶ms.baddr)) < 0) { - uniperror("bind"); - close(ufd); return -1; } struct eval *pair = add_event(pool, EV_UDP_TUNNEL, ufd, POLLIN); @@ -467,10 +460,6 @@ int udp_associate(struct poolhd *pool, return -1; } if (dst->in6.sin6_port != 0) { - if (socket_mod(ufd, &addr.sa) < 0) { - del_event(pool, pair); - return -1; - } if (connect(ufd, &addr.sa, SA_SIZE(&addr)) < 0) { uniperror("connect"); del_event(pool, pair); @@ -763,9 +752,6 @@ int on_udp_tunnel(struct eval *val, char *buffer, size_t bfsize) if (params.baddr.sin6_family != addr.sa.sa_family) { return -1; } - if (socket_mod(pair->fd, &addr.sa) < 0) { - return -1; - } if (connect(pair->fd, &addr.sa, SA_SIZE(&addr)) < 0) { uniperror("connect"); return -1; From 0a06ba41c9a48b1fa4471ac938fbc7d7080bf291 Mon Sep 17 00:00:00 2001 From: ruti <> Date: Mon, 28 Oct 2024 22:16:01 +0300 Subject: [PATCH 6/7] Fix -R long name, remove some flags --- README.md | 2 +- main.c | 13 +++++-------- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index c40c5d4..c4ca601 100644 --- a/README.md +++ b/README.md @@ -90,7 +90,7 @@ ciadpi --fake -1 --ttl 8 +h: добавить смещение Host +n: нулевое смещение Дополнительные флаги: - +e: конец; +m: середина; +r: случайная позиция + +e: конец; +m: середина Примеры: 0+sm - разбить запрос в середине SNI 1:3:5 - разбить по позициям 1, 6 и 11 diff --git a/main.c b/main.c index cab4f26..810e1f3 100644 --- a/main.c +++ b/main.c @@ -88,8 +88,8 @@ const char help_text[] = { " -V, --pf Ports range whitelist\n" " -R, --round Number of request to which desync will be applied\n" " -s, --split Position format: offset[:repeats:skip][+flag1[flag2]]\n" - " Flags: +s - SNI offset, +h - HTTP host offset\n" - " Additional flags: +e - end, +m - middle, +r - random\n" + " Flags: +s - SNI offset, +h - HTTP host offset, +n - null\n" + " Additional flags: +e - end, +m - middle\n" " -d, --disorder Split and send reverse order\n" " -o, --oob Split and send as OOB data\n" " -q, --disoob Split and send reverse order as OOB data\n" @@ -142,7 +142,7 @@ const struct option options[] = { {"proto", 1, 0, 'K'}, {"hosts", 1, 0, 'H'}, {"pf", 1, 0, 'V'}, - {"repeats", 1, 0, 'R'}, + {"round", 1, 0, 'R'}, {"split", 1, 0, 's'}, {"disorder", 1, 0, 'd'}, {"oob", 1, 0, 'o'}, @@ -402,9 +402,6 @@ int parse_offset(struct part *part, const char *str) case 'h': part->flag = OFFSET_HOST; break; - case 'e': // - part->flag = OFFSET_END; - break; case 'n': break; default: @@ -417,10 +414,10 @@ int parse_offset(struct part *part, const char *str) case 'm': part->flag |= OFFSET_MID; break; - case 'r': + case 'r': // part->flag |= OFFSET_RAND; break; - case 's': + case 's': // part->flag |= OFFSET_START; } } From 75671fa11c57aa458e87d876efc48b40985ed78f Mon Sep 17 00:00:00 2001 From: SashaXser <24498484+SashaXser@users.noreply.github.com> Date: Tue, 5 Nov 2024 19:19:32 +0400 Subject: [PATCH 7/7] Fixes from CodeQL (#223) * Fix code scanning alert no. 4: Multiplication result converted to larger type Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Fix code scanning alert no. 5: Multiplication result converted to larger type Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --------- Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- desync.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/desync.c b/desync.c index 0259ba2..c63c749 100644 --- a/desync.c +++ b/desync.c @@ -469,7 +469,7 @@ ssize_t desync(int sfd, char *buffer, size_t bfsize, pos += gen_offset(part.pos, part.flag, n - pos - 5, lp, type, host_pos - 5, len); - pos += part.s * (part.r - r); + pos += (long)part.s * (part.r - r); if (pos < lp) { LOG(LOG_E, "tlsrec cancel: %ld < %ld\n", pos, lp); break; @@ -501,7 +501,7 @@ ssize_t desync(int sfd, char *buffer, size_t bfsize, long pos = gen_offset(part.pos, part.flag, n, lp, type, host_pos, len); - pos += part.s * (part.r - r); + pos += (long)part.s * (part.r - r); if (!(part.flag & OFFSET_START) && offset && pos <= offset) { LOG(LOG_S, "offset: %zd, skip\n", offset);