diff --git a/Makefile b/Makefile index d324e1f..c079598 100644 --- a/Makefile +++ b/Makefile @@ -10,6 +10,9 @@ WIN_SRC = win_service.c OBJ = $(SRC:.c=.o) WIN_OBJ = $(WIN_SRC:.c=.o) +PREFIX := /usr/local +INSTALL_DIR := $(DESTDIR)$(PREFIX)/bin/ + all: $(TARGET) $(TARGET): $(OBJ) @@ -23,3 +26,7 @@ windows: $(OBJ) $(WIN_OBJ) clean: rm -f $(TARGET) $(TARGET).exe $(OBJ) $(WIN_OBJ) + +install: $(TARGET) + mkdir -p $(INSTALL_DIR) + install -m 755 $(TARGET) $(INSTALL_DIR) diff --git a/README.md b/README.md index a1b7650..b2e6940 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,9 @@ ciadpi --fake -1 --ttl 8 -p, --port Прослушиваемый порт, по умолчанию 1080 +-E, --transparent + Запуск в режиме прозрачного прокси, SOCKS работать не будет + -c, --max-conn Максимальное количество клиентских подключений, по умолчанию 512 @@ -53,6 +56,12 @@ ciadpi --fake -1 --ttl 8 ssl_err : В ответ на ClientHello не пришел ServerHello или SH содержит некорректный session_id none : Предыдущая группа пропущена, например из-за ограничения по доменам или протоколам +-L, --auto-mode <0|1> + 0: кешировать IP только если имеется возможность переподключиться + 1: кешировать IP также в том случае, если: + torst - таймаут/соединение сброшено во время обмена пакетами (т.е. уже после первых данных от сервера) + ssl_err - совершился лишь один круг обмена данными (запрос-ответ/запрос-ответ-запрос) + -u, --cache-ttl Время жизни значения в кеше, по умолчанию 100800 (28 часов) @@ -246,7 +255,7 @@ TCP может отсылать данные вне основного пото ------ #### Примеры: ``` ---fake -1 --ttl 10 --auto=alert,sid_inv --fake -1 --ttl 5 +--fake -1 --ttl 10 --auto=ssl_err --fake -1 --ttl 5 ``` По умолчанию использовать `fake` с ttl=10, в случае ошибки использовать `fake` с ttl=5 diff --git a/conev.c b/conev.c index a3f1ce0..41fa47a 100644 --- a/conev.c +++ b/conev.c @@ -4,18 +4,20 @@ #include #include #include +#include "error.h" struct poolhd *init_pool(int count) { struct poolhd *pool = calloc(sizeof(struct poolhd), 1); if (!pool) { + uniperror("init pool"); return 0; } pool->max = count; pool->count = 0; pool->iters = 0; - + #ifndef NOEPOLL int efd = epoll_create(count); if (efd < 0) { @@ -27,8 +29,9 @@ struct poolhd *init_pool(int count) pool->pevents = malloc(sizeof(*pool->pevents) * count); pool->links = malloc(sizeof(*pool->links) * count); pool->items = malloc(sizeof(*pool->items) * count); - + if (!pool->pevents || !pool->links || !pool->items) { + uniperror("init pool"); destroy_pool(pool); return 0; } @@ -45,19 +48,21 @@ struct eval *add_event(struct poolhd *pool, enum eid type, { assert(fd > 0); if (pool->count >= pool->max) { + LOG(LOG_E, "add_event: pool is full\n"); return 0; } struct eval *val = pool->links[pool->count]; memset(val, 0, sizeof(*val)); - + val->mod_iter = pool->iters; val->fd = fd; val->index = pool->count; val->type = type; - + #ifndef NOEPOLL struct epoll_event ev = { .events = EPOLLRDHUP | e, .data = {val} }; if (epoll_ctl(pool->efd, EPOLL_CTL_ADD, fd, &ev)) { + uniperror("add event"); return 0; } #else @@ -67,7 +72,7 @@ struct eval *add_event(struct poolhd *pool, enum eid type, pfd->events = POLLRDHUP | e; pfd->revents = 0; #endif - + pool->count++; return val; } @@ -153,9 +158,6 @@ struct eval *next_event(struct poolhd *pool, int *offs, int *type) if (i < 0) { return 0; } - if (pool->iters == UINT_MAX) { - pool->iters = 0; - } pool->iters++; } struct eval *val = pool->pevents[i].data.ptr; @@ -188,9 +190,6 @@ struct eval *next_event(struct poolhd *pool, int *offs, int *typel) return 0; } i = pool->count - 1; - if (pool->iters == UINT_MAX) { - pool->iters = 0; - } pool->iters++; } short type = pool->pevents[i].revents; diff --git a/conev.h b/conev.h index eed9079..f98a59e 100644 --- a/conev.h +++ b/conev.h @@ -68,7 +68,7 @@ struct buffer { struct eval { int fd; int index; - unsigned int mod_iter; + unsigned long long mod_iter; enum eid type; struct eval *pair; struct buffer buff; @@ -78,8 +78,11 @@ struct eval { struct sockaddr_in6 in6; }; ssize_t recv_count; + unsigned int round_count; + char last_round; int attempt; char cache; + char mark; // }; struct poolhd { @@ -93,7 +96,7 @@ struct poolhd { #else struct pollfd *pevents; #endif - unsigned int iters; + unsigned long long iters; }; struct poolhd *init_pool(int count); diff --git a/desync.c b/desync.c index 250cefe..fde50b3 100644 --- a/desync.c +++ b/desync.c @@ -1,3 +1,5 @@ +#define _GNU_SOURCE + #include "desync.h" #include @@ -16,11 +18,7 @@ #include #else #include - #include #include - #include - - #define memfd_create(name, flags) syscall(__NR_memfd_create, name, flags) #endif #else #include @@ -143,14 +141,12 @@ void wait_send(int sfd) #define wait_send_if_support(sfd) // :( #endif -#ifdef FAKE_SUPPORT -#ifndef _WIN32 +#ifdef __linux__ ssize_t send_fake(int sfd, char *buffer, int cnt, long pos, int fa, struct desync_params *opt) { struct sockaddr_in6 addr = {}; socklen_t addr_size = sizeof(addr); - #ifdef __linux__ if (opt->md5sig) { if (getpeername(sfd, (struct sockaddr *)&addr, &addr_size) < 0) { @@ -158,7 +154,6 @@ ssize_t send_fake(int sfd, char *buffer, return -1; } } - #endif struct packet pkt; if (opt->fake_data.data) { pkt = opt->fake_data; @@ -173,21 +168,16 @@ ssize_t send_fake(int sfd, char *buffer, } else pkt.size = 0; } - - int ffd = memfd_create("name", 0); - if (ffd < 0) { - uniperror("memfd_create"); + int fds[2]; + if (pipe(fds) < 0) { + uniperror("pipe"); return -1; } char *p = 0; ssize_t len = -1; while (1) { - if (ftruncate(ffd, pos) < 0) { - uniperror("ftruncate"); - break; - } - p = mmap(0, pos, PROT_WRITE, MAP_SHARED, ffd, 0); + p = mmap(0, pos, PROT_WRITE | PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); if (p == MAP_FAILED) { uniperror("mmap"); p = 0; @@ -198,8 +188,6 @@ ssize_t send_fake(int sfd, char *buffer, if (setttl(sfd, opt->ttl ? opt->ttl : 8, fa) < 0) { break; } - - #ifdef __linux__ if (opt->md5sig) { struct tcp_md5sig md5 = { .tcpm_keylen = 5 @@ -212,17 +200,22 @@ ssize_t send_fake(int sfd, char *buffer, break; } } - #endif if (opt->ip_options && fa == AF_INET && setsockopt(sfd, IPPROTO_IP, IP_OPTIONS, opt->ip_options, opt->ip_options_len) < 0) { uniperror("setsockopt IP_OPTIONS"); break; } + struct iovec vec = { .iov_base = p, .iov_len = pos }; - len = sendfile(sfd, ffd, 0, pos); + len = vmsplice(fds[1], &vec, 1, SPLICE_F_GIFT); if (len < 0) { - uniperror("sendfile"); + uniperror("vmsplice"); + break; + } + len = splice(fds[0], 0, sfd, 0, len, 0); + if (len < 0) { + uniperror("splice"); break; } wait_send(sfd); @@ -237,7 +230,6 @@ ssize_t send_fake(int sfd, char *buffer, uniperror("setsockopt IP_OPTIONS"); break; } - #ifdef __linux__ if (opt->md5sig) { struct tcp_md5sig md5 = { .tcpm_keylen = 0 @@ -250,14 +242,16 @@ ssize_t send_fake(int sfd, char *buffer, break; } } - #endif break; } if (p) munmap(p, pos); - close(ffd); + close(fds[0]); + close(fds[1]); return len; } -#else +#endif + +#ifdef _WIN32 OVERLAPPED ov = {}; ssize_t send_fake(int sfd, char *buffer, @@ -359,7 +353,6 @@ ssize_t send_fake(int sfd, char *buffer, return len; } #endif -#endif ssize_t send_oob(int sfd, char *buffer, ssize_t n, long pos, char *c) diff --git a/dist/linux/README.md b/dist/linux/README.md new file mode 100644 index 0000000..f211b46 --- /dev/null +++ b/dist/linux/README.md @@ -0,0 +1,38 @@ +# Installing on Linux + +## Building +```sh +cd byedpi/ +make +sudo make install +``` + +## Systemd Service (optional) + +You can configure the program to run as systemd service, user- or system-wide (only one at a time). + +### As user service: + +```sh +cp byedpi.service ~/.config/systemd/user/ +cp byedpi.conf ~/.config/ +systemctl --user enable --now byedpi.service +``` + +You should see the service now marked as "active": +```sh +systemctl --user status byedpi.service +``` + +### As system service: + +```sh +sudo cp byedpi.service /etc/systemd/system/ +sudo cp byedpi.conf /etc/ +sudo systemctl enable --now byedpi.service +``` + +You should see the service now marked as "active": +```sh +systemctl status byedpi.service +``` diff --git a/dist/linux/byedpi.conf b/dist/linux/byedpi.conf new file mode 100644 index 0000000..e52e18a --- /dev/null +++ b/dist/linux/byedpi.conf @@ -0,0 +1,8 @@ +# More options and their descriptions can be found here: +# https://github.com/hufrea/byedpi/blob/main/README.md +# +# By default, ciadpi listens on all interfaces, +# a specific one can be specified via "--ip 127.0.0.1". + +# Put your options here +BYEDPI_OPTIONS="--split 1 --disorder 3+s --mod-http=h,d --auto=torst --tlsrec 1+s" diff --git a/dist/linux/byedpi.service b/dist/linux/byedpi.service new file mode 100644 index 0000000..7abe067 --- /dev/null +++ b/dist/linux/byedpi.service @@ -0,0 +1,19 @@ +[Unit] +Description=ByeDPI +Documentation=https://github.com/hufrea/byedpi +Wants=network-online.target +After=network-online.target nss-lookup.target + +[Service] +NoNewPrivileges=yes +StandardOutput=null +StandardError=journal +EnvironmentFile=-/etc/byedpi.conf +EnvironmentFile=-%h/.config/byedpi.conf +ExecStart=ciadpi $BYEDPI_OPTIONS +TimeoutStopSec=5s +PrivateTmp=true +ProtectSystem=full + +[Install] +WantedBy=default.target diff --git a/error.h b/error.h index bd5e372..5eea82b 100644 --- a/error.h +++ b/error.h @@ -11,6 +11,8 @@ #include #endif +#include "params.h" + #ifdef _WIN32 #define get_e() \ unie(WSAGetLastError()) diff --git a/extend.c b/extend.c index ee07b2c..d03c326 100644 --- a/extend.c +++ b/extend.c @@ -23,6 +23,8 @@ #include "desync.h" #include "packets.h" +#define KEY_SIZE sizeof(struct sockaddr_ina) + int set_timeout(int fd, unsigned int s) { @@ -45,32 +47,67 @@ int set_timeout(int fd, unsigned int s) } -int mode_add_get(struct sockaddr_ina *dst, int m) +static ssize_t serialize_addr(const struct sockaddr_ina *dst, + uint8_t *const out, const size_t out_len) +{ + #define serialize(raw, field, len, counter){ \ + const size_t size = sizeof(field); \ + if ((counter + size) <= len) { \ + memcpy(raw + counter, &(field), size); \ + counter += size; \ + } else return 0; \ + } + size_t c = 0; + serialize(out, dst->in.sin_port, out_len, c); + serialize(out, dst->sa.sa_family, out_len, c); + + if (dst->sa.sa_family == AF_INET) { + serialize(out, dst->in.sin_addr, out_len, c); + } else { + serialize(out, dst->in6.sin6_addr, out_len, c); + } + #undef serialize + + return c; +} + + +static int mode_add_get(struct sockaddr_ina *dst, int m) { // m < 0: get, m > 0: set, m == 0: delete assert(m >= -1 && m < params.dp_count); time_t t = 0; struct elem *val = 0; - char *str = (char *)&dst->in; - int len = 0; - 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); + 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) { - mem_delete(params.mempool, str, len); + LOG(LOG_S, "delete ip: %s\n", ADDR_STR); + mem_delete(params.mempool, (char *)key, len); return 0; } - else if (m > 0) { + else { + LOG(LOG_S, "save ip: %s, m=%d\n", ADDR_STR, m); time(&t); - val = mem_add(params.mempool, str, len); + + val = mem_add(params.mempool, (char *)key, len); if (!val) { uniperror("mem_add"); return -1; @@ -79,16 +116,6 @@ int mode_add_get(struct sockaddr_ina *dst, int 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; } @@ -188,21 +215,39 @@ 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) { - return -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 (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); } - if (m >= params.dp_count) { - mode_add_get( - (struct sockaddr_ina *)&val->in6, 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; } - return reconnect(pool, val, m); + return -1; } @@ -210,21 +255,44 @@ int on_fin(struct poolhd *pool, struct eval *val) { int m = val->pair->attempt + 1; + bool can_reconn = ( + val->pair->buff.data && !val->recv_count + ); + 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) { + return -1; + } for (; m < params.dp_count; m++) { struct desync_params *dp = ¶ms.dp[m]; if (!dp->detect) { return -1; } - if (!(dp->detect & DETECT_TLS_ERR)) { - continue; + 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; + } } - char *req = val->pair->buff.data; - ssize_t qn = val->pair->buff.size; - - if (!is_tls_chello(req, qn)) { - continue; - } - return reconnect(pool, val, m); + } + if (m > 1) { // delete + mode_add_get( + (struct sockaddr_ina *)&val->in6, 0); } return -1; } @@ -279,20 +347,25 @@ int on_tunnel_check(struct poolhd *pool, struct eval *val, assert(!out); ssize_t n = recv(val->fd, buffer, bfsize, 0); if (n < 1) { - if (n) uniperror("recv"); + if (!n) { + return on_fin(pool, val); + } + uniperror("recv"); switch (get_e()) { case ECONNRESET: case ECONNREFUSED: case ETIMEDOUT: return on_torst(pool, val); } - return on_fin(pool, val); + 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); @@ -300,9 +373,12 @@ int on_tunnel_check(struct poolhd *pool, struct eval *val, uniperror("send"); 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 && + if (params.timeout && params.auto_level < 1 && set_timeout(val->fd, 0)) { return -1; } @@ -315,15 +391,7 @@ int on_tunnel_check(struct poolhd *pool, struct eval *val, if (!pair->cache) { return 0; } - struct sockaddr_ina *addr = (struct sockaddr_ina *)&val->in6; - - if (m == 0) { - LOG(LOG_S, "delete ip: m=%d\n", m); - } else { - INIT_ADDR_STR((*addr)); - LOG(LOG_S, "save ip: %s, m=%d\n", ADDR_STR, m); - } - return mode_add_get(addr, m); + return mode_add_get((struct sockaddr_ina *)&val->in6, m); } @@ -331,7 +399,8 @@ 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)) { + if (mod_etype(pool, val, POLLIN) || + mod_etype(pool, val->pair, POLLIN)) { uniperror("mod_etype"); return -1; } @@ -384,6 +453,7 @@ int on_desync(struct poolhd *pool, struct eval *val, } 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) { diff --git a/extend.h b/extend.h index 36bce5b..439b4e3 100644 --- a/extend.h +++ b/extend.h @@ -19,6 +19,10 @@ int on_desync(struct poolhd *pool, struct eval *val, 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); + #ifdef __linux__ int protect(int conn_fd, const char *path); #else diff --git a/main.c b/main.c index 7e95145..dc16943 100644 --- a/main.c +++ b/main.c @@ -23,7 +23,7 @@ #define close(fd) closesocket(fd) #endif -#define VERSION "13.1" +#define VERSION "14.1" char ip_option[1] = "\0"; @@ -54,13 +54,17 @@ struct params params = { .laddr = { .sin6_family = AF_INET }, - .debug = 0 + .debug = 0, + .auto_level = 0 }; const char help_text[] = { " -i, --ip, Listening IP, default 0.0.0.0\n" " -p, --port Listening port, default 1080\n" + #ifdef __linux__ + " -E, --transparent Transparent proxy mode\n" + #endif " -c, --max-conn Connection count limit, default 512\n" " -N, --no-domain Deny domain resolving\n" " -U, --no-udp Deny UDP association\n" @@ -74,6 +78,7 @@ const char help_text[] = { #endif " -A, --auto Try desync params after this option\n" " Detect: torst,redirect,ssl_err,none\n" + " -L, --auto-mode <0|1> 1 - handle trigger after several packets\n" " -u, --cache-ttl Lifetime of cached desync params for IP\n" #ifdef TIMEOUT_SUPPORT " -T, --timeout Timeout waiting for response, after which trigger auto\n" @@ -116,6 +121,9 @@ const struct option options[] = { {"version", 0, 0, 'v'}, {"ip", 1, 0, 'i'}, {"port", 1, 0, 'p'}, + #ifdef __linux__ + {"transparent", 0, 0, 'E'}, + #endif {"conn-ip", 1, 0, 'I'}, {"buf-size", 1, 0, 'b'}, {"max-conn", 1, 0, 'c'}, @@ -125,6 +133,7 @@ const struct option options[] = { {"tfo ", 0, 0, 'F'}, #endif {"auto", 1, 0, 'A'}, + {"auto-mode", 1, 0, 'L'}, {"cache-ttl", 1, 0, 'u'}, #ifdef TIMEOUT_SUPPORT {"timeout", 1, 0, 'T'}, @@ -352,6 +361,17 @@ int get_default_ttl() } +bool ipv6_support() +{ + int fd = socket(AF_INET6, SOCK_STREAM, 0); + if (fd < 0) { + return 0; + } + close(fd); + return 1; +} + + int parse_offset(struct part *part, const char *str) { char *end = 0; @@ -464,12 +484,16 @@ int main(int argc, char **argv) } params.laddr.sin6_port = htons(1080); + if (!ipv6_support()) { + params.baddr.sin6_family = AF_INET; + } int rez; int invalid = 0; long val = 0; char *end = 0; + bool all_limited = 1; struct desync_params *dp = add((void *)¶ms.dp, ¶ms.dp_count, sizeof(struct desync_params)); @@ -492,6 +516,11 @@ int main(int argc, char **argv) case 'U': params.udp = 0; break; + #ifdef __linux__ + case 'E': + params.transparent = 1; + break; + #endif case 'h': printf(help_text); @@ -550,7 +579,18 @@ int main(int argc, char **argv) params.tfo = 1; break; + case 'L': + val = strtol(optarg, &end, 0); + if (val < 0 || val > 1 || *end) + invalid = 1; + else + params.auto_level = val; + break; + case 'A': + if (!(dp->hosts || dp->proto || dp->pf[0] || dp->detect)) { + all_limited = 0; + } dp = add((void *)¶ms.dp, ¶ms.dp_count, sizeof(struct desync_params)); if (!dp) { @@ -844,7 +884,7 @@ int main(int argc, char **argv) clear_params(); return -1; } - if (dp->hosts || dp->proto || dp->pf[0]) { + if (all_limited) { dp = add((void *)¶ms.dp, ¶ms.dp_count, sizeof(struct desync_params)); if (!dp) { @@ -868,6 +908,7 @@ int main(int argc, char **argv) clear_params(); return -1; } + int status = run((struct sockaddr_ina *)¶ms.laddr); clear_params(); return status; diff --git a/packets.c b/packets.c index 989164f..3e04482 100644 --- a/packets.c +++ b/packets.c @@ -15,7 +15,7 @@ #endif #define ANTOHS(data, i) \ - (uint16_t)((data[i] << 8) + (uint8_t)data[i + 1]) + (((uint16_t)data[i] << 8) + (uint8_t)data[i + 1]) #define SHTONA(data, i, x) \ data[i] = (uint8_t)((x) >> 8); \ diff --git a/params.h b/params.h index d7b03e1..b4f4ba0 100644 --- a/params.h +++ b/params.h @@ -3,6 +3,7 @@ #include #include +#include #include "mpool.h" @@ -63,11 +64,11 @@ struct desync_params { int ttl; char *ip_options; ssize_t ip_options_len; - char md5sig; + bool md5sig; struct packet fake_data; int udp_fake_count; int fake_offset; - char drop_sack; + bool drop_sack; char oob_char[2]; int parts_n; @@ -90,21 +91,23 @@ struct params { int dp_count; struct desync_params *dp; long sfdelay; - char wait_send; + bool wait_send; int def_ttl; - char custom_ttl; + bool custom_ttl; - char tfo; + bool tfo; unsigned int timeout; + int auto_level; long cache_ttl; - char ipv6; - char resolve; - char udp; + bool ipv6; + bool resolve; + bool udp; int max_open; int debug; size_t bfsize; struct sockaddr_in6 baddr; struct sockaddr_in6 laddr; + bool transparent; struct mphdr *mempool; char *protect_path; diff --git a/proxy.c b/proxy.c index 8674131..eadfff6 100644 --- a/proxy.c +++ b/proxy.c @@ -32,9 +32,16 @@ #if defined(__linux__) && defined(__GLIBC__) extern int accept4(int, struct sockaddr *__restrict, socklen_t *__restrict, int); #endif + #ifdef __linux__ + /* For SO_ORIGINAL_DST only (which is 0x50) */ + #include "linux/netfilter_ipv4.h" + #ifndef IP6T_SO_ORIGINAL_DST + #define IP6T_SO_ORIGINAL_DST SO_ORIGINAL_DST + #endif + #endif #endif - + int NOT_EXIT = 1; static void on_cancel(int sig) { @@ -125,6 +132,7 @@ int resolve(char *host, int len, char rchar = host[len]; host[len] = '\0'; + LOG(LOG_S, "resolve: %s\n", host); if (getaddrinfo(host, 0, &hints, &res) || !res) { host[len] = rchar; @@ -197,6 +205,17 @@ int resp_error(int fd, int e, int flag) } return resp_s5_error(fd, e); } + #ifdef __linux__ + if (params.transparent && + (e == ECONNREFUSED || e == ETIMEDOUT)) { + struct linger l = { .l_onoff = 1 }; + if (setsockopt(fd, + SOL_SOCKET, SO_LINGER, &l, sizeof(l)) < 0) { + uniperror("setsockopt SO_LINGER"); + return -1; + } + } + #endif return 0; } @@ -242,7 +261,7 @@ 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"); + LOG(LOG_E, "ss: request too small\n"); return -S_ER_GEN; } struct s5_req *r = (struct s5_req *)buffer; @@ -393,6 +412,10 @@ int create_conn(struct poolhd *pool, close(sfd); return -1; } + if (mod_etype(pool, val, 0) < 0) { + uniperror("mod_etype"); + return -1; + } val->pair = pair; pair->pair = val; #ifdef __NetBSD__ @@ -482,7 +505,7 @@ int udp_associate(struct poolhd *pool, return -1; } struct eval *client = add_event(pool, EV_UDP_TUNNEL, cfd, POLLIN); - if (!pair) { + if (!client) { del_event(pool, pair); close(cfd); return -1; @@ -519,6 +542,38 @@ int udp_associate(struct poolhd *pool, return 0; } +#ifdef __linux__ +static inline int transp_conn(struct poolhd *pool, struct eval *val) +{ + struct sockaddr_ina remote, self; + socklen_t rlen = sizeof(remote), slen = sizeof(self); + if (getsockopt(val->fd, IPPROTO_IP, + SO_ORIGINAL_DST, &remote, &rlen) != 0) + { + if (getsockopt(val->fd, IPPROTO_IPV6, + IP6T_SO_ORIGINAL_DST, &remote, &rlen) != 0) { + uniperror("getsockopt SO_ORIGINAL_DST"); + return -1; + } + } + if (getsockname(val->fd, &self.sa, &slen) < 0) { + uniperror("getsockname"); + return -1; + } + if (self.sa.sa_family == remote.sa.sa_family && + self.in.sin_port == remote.in.sin_port && + addr_equ(&self, &remote)) { + LOG(LOG_E, "connect to self, ignore\n"); + return -1; + } + int error = connect_hook(pool, val, &remote, EV_CONNECT); + if (error) { + uniperror("connect_hook"); + return -1; + } + return 0; +} +#endif static inline int on_accept(struct poolhd *pool, struct eval *val) { @@ -565,6 +620,12 @@ static inline int on_accept(struct poolhd *pool, struct eval *val) continue; } rval->in6 = client.in6; + #ifdef __linux__ + if (params.transparent && transp_conn(pool, rval) < 0) { + del_event(pool, rval); + continue; + } + #endif } return 0; } @@ -614,11 +675,30 @@ int on_tunnel(struct poolhd *pool, struct eval *val, if (n < 0 && get_e() == EAGAIN) { break; } - if (n < 1) { - if (n) uniperror("recv"); + 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 = send(pair->fd, buffer, n, 0); if (sn != n) { @@ -753,7 +833,7 @@ static inline int on_request(struct poolhd *pool, struct eval *val, return 0; } if (n < S_SIZE_MIN) { - LOG(LOG_E, "ss: request to small (%zd)\n", n); + LOG(LOG_E, "ss: request too small (%zd)\n", n); return -1; } struct s5_req *r = (struct s5_req *)buffer; @@ -821,7 +901,8 @@ static inline int on_connect(struct poolhd *pool, struct eval *val, int e) } } else { - if (mod_etype(pool, val, POLLIN)) { + if (mod_etype(pool, val, POLLIN) || + mod_etype(pool, val->pair, POLLIN)) { uniperror("mod_etype"); return -1; } @@ -839,23 +920,24 @@ 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\n", val->fd, val->pair ? val->pair->fd : -1); + 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); del_event(pool, val); } -int event_loop(int srvfd) +int event_loop(int srvfd) { size_t bfsize = params.bfsize; struct poolhd *pool = init_pool(params.max_open * 2 + 1); if (!pool) { - uniperror("init pool"); close(srvfd); return -1; } if (!add_event(pool, EV_ACCEPT, srvfd, POLLIN)) { - uniperror("add event"); destroy_pool(pool); close(srvfd); return -1; @@ -880,7 +962,7 @@ int event_loop(int srvfd) } assert(val->type >= 0 && val->type < sizeof(eid_name)/sizeof(*eid_name)); - LOG(LOG_L, "new event: fd: %d, evt: %s, mod_iter: %d\n", val->fd, eid_name[val->type], val->mod_iter); + LOG(LOG_L, "new event: fd: %d, evt: %s, mod_iter: %llu\n", val->fd, eid_name[val->type], val->mod_iter); switch (val->type) { case EV_ACCEPT: @@ -974,6 +1056,7 @@ int run(struct sockaddr_ina *srv) uniperror("signal SIGPIPE!"); #endif signal(SIGINT, on_cancel); + signal(SIGTERM, on_cancel); int fd = listen_socket(srv); if (fd < 0) { @@ -981,4 +1064,3 @@ int run(struct sockaddr_ina *srv) } return event_loop(fd); } -