From 08e1e1d8c72aacdd27794a07a80d803171b27b5a Mon Sep 17 00:00:00 2001 From: ruti <> Date: Sat, 3 Jun 2023 21:52:10 +0200 Subject: [PATCH] init --- Makefile | 10 + conev.c | 194 +++++++++++++++ conev.h | 83 +++++++ desync.c | 159 ++++++++++++ desync.h | 1 + main.c | 407 +++++++++++++++++++++++++++++++ packets.c | 214 +++++++++++++++++ packets.h | 21 ++ params.h | 48 ++++ proxy.c | 692 +++++++++++++++++++++++++++++++++++++++++++++++++++++ proxy.h | 81 +++++++ readme.txt | 37 +++ 12 files changed, 1947 insertions(+) create mode 100644 Makefile create mode 100644 conev.c create mode 100644 conev.h create mode 100644 desync.c create mode 100644 desync.h create mode 100644 main.c create mode 100644 packets.c create mode 100644 packets.h create mode 100644 params.h create mode 100644 proxy.c create mode 100644 proxy.h create mode 100644 readme.txt diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..ebd663e --- /dev/null +++ b/Makefile @@ -0,0 +1,10 @@ +TARGET = ciadpi +CC ?= gcc +CFLAGS += -std=c99 -O2 +SOURCES = packets.c main.c conev.c proxy.c desync.c + +all: + $(CC) $(CFLAGS) $(SOURCES) -I . -o $(TARGET) + +clean: + rm -f $(TARGET) *.o diff --git a/conev.c b/conev.c new file mode 100644 index 0000000..fdc05cb --- /dev/null +++ b/conev.c @@ -0,0 +1,194 @@ +#define CONEV_H +#include + +#include +#include +#include +#include + + +struct poolhd *init_pool(int count) +{ + struct poolhd *pool = malloc(sizeof(struct poolhd)); + if (!pool) { + return 0; + } + pool->max = count; + pool->count = 0; + + #ifndef NOEPOLL + int efd = epoll_create(count); + if (efd < 0) { + perror("epoll_create"); + free(pool); + return 0; + } + pool->efd = efd; + #endif + 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) { + destroy_pool(pool); + return 0; + } + for (int i = 0; i < count; i++) { + pool->links[i] = &(pool->items[i]); + } + return pool; +} + + +struct eval *add_event(struct poolhd *pool, enum eid type, + int fd, int e) +{ + if (pool->count >= pool->max) + return 0; + struct eval *val = pool->links[pool->count]; + + val->fd = fd; + val->index = pool->count; + val->type = type; + val->pair = 0; + val->send_count = 0; + val->flag = 0; + + #ifndef NOEPOLL + struct epoll_event ev = { + EPOLLIN | EPOLLERR | EPOLLRDHUP | e, {val} + }; + if (epoll_ctl(pool->efd, EPOLL_CTL_ADD, fd, &ev)) { + return 0; + } + val->events = ev.events; + #else + struct pollfd *pfd = &(pool->pevents[pool->count]); + + pfd->fd = fd; + pfd->events = POLLIN | POLLERR | POLLRDHUP | e; + pfd->revents = 0; + #endif + + pool->count++; + return val; +} + + +void del_event(struct poolhd *pool, struct eval *val) +{ + if (!val->fd) { + return; + } + close(val->fd); + val->fd = 0; + pool->count--; + + struct eval *ev = pool->links[pool->count]; + if (ev != val) + { + int index = val->index; + pool->links[index] = ev; + pool->links[pool->count] = val; + #ifdef NOEPOLL + pool->pevents[index] = pool->pevents[pool->count]; + #endif + ev->index = index; + } + if (val->pair) { + val->pair->pair = 0; + del_event(pool, val->pair); + } +} + + +void destroy_pool(struct poolhd *pool) +{ + for (int x = 0; x < pool->count; x++) { + struct eval *val = pool->links[x]; + if (val->fd) close(val->fd); + } + if (pool->items) { + free(pool->items); + } + if (pool->links) { + free(pool->links); + } + if (pool->pevents) { + free(pool->pevents); + } + #ifndef NOEPOLL + if (pool->efd) + close(pool->efd); + #endif + memset(pool, 0, sizeof(*pool)); + free(pool); +} + + +#ifndef NOEPOLL +struct eval *next_event(struct poolhd *pool, int *offs, int *type) +{ + int i = *offs; + if (i < 0) { + i = (epoll_wait(pool->efd, pool->pevents, pool->max, -1) - 1); + if (i < 0) { + perror("epoll_wait"); + return 0; + } + } + *offs = i - 1; + *type = pool->pevents[i].events; + return pool->pevents[i].data.ptr; +} + + +int mod_etype(struct poolhd *pool, struct eval *val, int type, char add) +{ + struct epoll_event ev = { + .events = val->events, .data = {val} + }; + if (add) + ev.events |= type; + else + ev.events &= ~type; + val->events = ev.events; + int s = epoll_ctl(pool->efd, EPOLL_CTL_MOD, val->fd, &ev); + if (s) + perror("epoll_ctl"); + return s; +} + +#else +struct eval *next_event(struct poolhd *pool, int *offs, int *typel) +{ + for (int i = *offs; ; i--) { + if (i < 0) { + if (poll(pool->pevents, pool->count, -1) <= 0) { + perror("poll"); + return 0; + } + i = pool->count - 1; + } + short type = pool->pevents[i].revents; + if (!type) + continue; + + pool->pevents[i].revents = 0; + *offs = i - 1; + *typel = type; + return pool->links[i]; + } +} + + +int mod_etype(struct poolhd *pool, struct eval *val, int type, char add) +{ + int index = val->index; + if (add) + pool->pevents[index].events |= type; + else + pool->pevents[index].events &= ~type; + return 0; +} +#endif \ No newline at end of file diff --git a/conev.h b/conev.h new file mode 100644 index 0000000..726c86e --- /dev/null +++ b/conev.h @@ -0,0 +1,83 @@ +#include +#include + +#ifndef __linux__ +#define NOEPOLL +#endif + +#ifndef NOEPOLL +#include +#define POLLIN EPOLLIN +#define POLLOUT EPOLLOUT +#define POLLERR EPOLLERR +#define POLLHUP EPOLLHUP +#define POLLRDHUP EPOLLRDHUP +#else +#include +#ifndef POLLRDHUP +#define POLLRDHUP POLLHUP +#endif +#endif + +enum eid { + EV_ACCEPT, + EV_REQUEST, + EV_CONNECT, + EV_IGNORE, + EV_TUNNEL +}; + +#define FLAG_NOSEND 1 +#define FLAG_HTTP 2 +#define FLAG_S4 4 +#define FLAG_S5 8 +#define FLAG_CONN 16 + +#ifndef CONEV_H +char *eid_name[] = { + "EV_ACCEPT", + "EV_REQUEST", + "EV_CONNECT", + "EV_IGNORE", + "EV_TUNNEL" +}; +#endif + +struct eval { + int fd; + int index; + enum eid type; + struct eval *pair; + size_t send_count; + int flag; + #ifndef NOEPOLL + uint32_t events; + #endif +}; + +struct poolhd { + int max; + int count; + int efd; + struct eval **links; + struct eval *items; +#ifndef NOEPOLL + struct epoll_event *pevents; +#else + struct pollfd *pevents; +#endif +}; + +struct poolhd *init_pool(int count); + +struct eval *add_event(struct poolhd *pool, enum eid type, int fd, int e); + +struct eval *add_pair(struct poolhd *pool, struct eval *val, int sfd, int e); + +void del_event(struct poolhd *pool, struct eval *val); + +void destroy_pool(struct poolhd *pool); + +struct eval *next_event(struct poolhd *pool, int *offs, int *type); + +int mod_etype(struct poolhd *pool, struct eval *val, int type, char rm); \ No newline at end of file diff --git a/desync.c b/desync.c new file mode 100644 index 0000000..b66f6a3 --- /dev/null +++ b/desync.c @@ -0,0 +1,159 @@ +#include +#include +#include +#include +#include + +#ifdef __linux__ +#include +#else +#include +#define sendfile(outfd, infd, start, len) sendfile(infd, outfd, start, len, 0, 0) +#endif + +#ifdef MFD_CLOEXEC +#include +#define memfd_create(name, flags) syscall(__NR_memfd_create, name, flags); +#else +#define memfd_create(name, flags) fileno(tmpfile()) +#endif + +#include +#include + + +int fake_attack(int sfd, char *buffer, ssize_t n, int cnt, int pos) +{ + struct packet pkt = cnt != IS_HTTP ? fake_tls : fake_http; + size_t psz = pkt.size; + + int ffd = memfd_create("name", O_RDWR); + if (ffd < 0) { + perror("memfd_create"); + return -1; + } + char *p = 0; + int status = -1; + + while (status) { + if (ftruncate(ffd, pos) < 0) { + perror("ftruncate"); + break; + } + p = mmap(0, pos, PROT_WRITE, MAP_SHARED, ffd, 0); + if (p == MAP_FAILED) { + perror("mmap"); + break; + } + memcpy(p, pkt.data, psz < pos ? psz : pos); + + if (setsockopt(sfd, IPPROTO_IP, IP_TTL, + ¶ms.ttl, sizeof(params.ttl)) < 0) { + perror("setsockopt IP_TTL"); + break; + } + if (sendfile(sfd, ffd, 0, pos) < 0) { + perror("sendfile"); + break; + } + usleep(params.sfdelay); + memcpy(p, buffer, pos); + + if (setsockopt(sfd, IPPROTO_IP, IP_TTL, + ¶ms.def_ttl, sizeof(int)) < 0) { + perror("setsockopt IP_TTL"); + break; + } + if (send(sfd, buffer + pos, n - pos, 0) < 0) { + perror("send"); + break; + } + status = 0; + } + if (p) munmap(p, pos); + close(ffd); + return status; +} + + +int disorder_attack(int sfd, char *buffer, ssize_t n, int pos) +{ + int bttl = 1; + if (setsockopt(sfd, IPPROTO_IP, IP_TTL, + (char *)&bttl, sizeof(bttl)) < 0) { + perror("setsockopt IP_TTL"); + return -1; + } + if (send(sfd, buffer, pos, 0) < 0) { + perror("send"); + return -1; + } + if (setsockopt(sfd, IPPROTO_IP, IP_TTL, + (char *)¶ms.def_ttl, sizeof(int)) < 0) { + perror("setsockopt IP_TTL"); + return -1; + } + if (send(sfd, buffer + pos, n - pos, 0) < 0) { + perror("send"); + return -1; + } + return 0; +} + + +int desync(int sfd, char *buffer, ssize_t n) +{ + int pos = params.split; + char *host = 0; + int len = 0, type = 0; + + if ((len = parse_tls(buffer, n, &host))) { + type = IS_HTTPS; + } + else if ((len = parse_http(buffer, n, &host, 0))) { + type = IS_HTTP; + } + LOG(LOG_S, "host: %.*s\n", len, host); + + if (type == IS_HTTP && params.mod_http) { + if (mod_http(buffer, n, params.mod_http)) { + fprintf(stderr, "mod http error\n"); + return -1; + } + } + if (host && params.split_host) + pos += (host - buffer); + else if (pos < 0) + pos += n; + + LOG(LOG_L, "split pos: %d, n: %ld\n", pos, n); + + if (pos <= 0 || pos >= n || + params.attack == DESYNC_NONE || + (!type && params.de_known)) + { + if (send(sfd, buffer, n, 0) < 0) { + perror("send"); + return -1; + } + } + else switch (params.attack) { + case DESYNC_FAKE: + return fake_attack(sfd, buffer, n, type, pos); + + case DESYNC_DISORDER: + return disorder_attack(sfd, buffer, n, pos); + + case DESYNC_SPLIT: + default: + if (send(sfd, buffer, pos, 0) < 0) { + perror("send"); + return -1; + } + if (send(sfd, buffer + pos, n - pos, 0) < 0) { + perror("send"); + return -1; + } + } + return 0; +} \ No newline at end of file diff --git a/desync.h b/desync.h new file mode 100644 index 0000000..3da3a27 --- /dev/null +++ b/desync.h @@ -0,0 +1 @@ +int desync(int sfd, char *buffer, ssize_t n); \ No newline at end of file diff --git a/main.c b/main.c new file mode 100644 index 0000000..bcb39de --- /dev/null +++ b/main.c @@ -0,0 +1,407 @@ +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#define VERSION 1 + +struct packet fake_tls = { + sizeof(tls_data), tls_data +}, +fake_http = { + sizeof(http_data), http_data +}; + + +struct params params = { + .ttl = 8, + .split = 3, + .sfdelay = 3000, + .attack = DESYNC_NONE, + .split_host = 0, + .def_ttl = 0, + .mod_http = 0, + + .mode = MODE_PROXY_S, + .ipv6 = 1, + .resolve = 1, + .de_known = 0, + .max_open = 512, + + .bfsize = 16384, + .send_bfsz = 65536, + .nack_max = 65536 * 2 - 16384, + .debug = 0 +}; + + +char *ftob(char *name) +{ + char *buffer = 0; + + FILE *file = fopen(name, "rb"); + if (!file) + return 0; + do { + if (fseek(file, 0, SEEK_END)) { + break; + } + long size = ftell(file); + if (!size || fseek(file, 0, SEEK_SET)) { + break; + } + if (!(buffer = malloc(size))) { + break; + } + if (fread(buffer, 1, size, file) != size) { + free(buffer); + buffer = 0; + } + } while (0); + fclose(file); + return buffer; +} + + +void daemonize(void) +{ + pid_t pid = fork(); + if (pid < 0) { + perror("fork"); + exit(1); + } + else if (pid) { + exit(0); + } + if (setsid() < 0) { + exit(1); + } + close(STDIN_FILENO); + close(STDOUT_FILENO); + close(STDERR_FILENO); + + open("/dev/null", O_RDWR); + dup(0); + dup(0); +} + + +int main(int argc, char **argv) +{ + struct sockaddr_ina s = { + .in = { + .sin_family = AF_INET, + .sin_port = htons(1080) + }}; + + char daemon = 0; + char *pidfile = 0; + + const char help_text[] = { + //"Proxy:\n" + " -i, --ip, Listening IP address\n" + " -p, --port Listening port num\n" + " -D, --daemon Daemonize\n" + " -f, --pidfile Write pid to file\n" + " -c, --max-conn Connection count limit, default 512\n" + #ifdef __linux__ + " -T, --transparent Get address with getsockopt\n" + #endif + " -N, --no-domain Deny domain resolving\n" + " -K, --desync-known Desync only HTTP and TLS with SNI\n" + //"Desync:\n" + " -m, --method Desync method: split,disorder,fake\n" + " -s, --split-pos Split position, default 3\n" + " -H, --split-at-host Add Host/SNI offset to split position\n" + " -t, --ttl TTL of fake packets, default 8\n" + " -l, --fake-tls \n" + " -o, --fake-http Set custom fake packet\n" + " -n, --tls-sni Change SNI in fake CH\n" + " -M, --mod-http Modify http: hcsmix,dcsmix,rmspace\n" + }; + + const struct option options[] = { + {"daemon", 0, 0, 'D'}, + {"transparent", 0, 0, 'T'}, + {"http", 0, 0, 'P'}, // + {"socks", 0, 0, 'O'}, + {"no-domain", 0, 0, 'N'}, + {"no-ipv6", 0, 0, 'X'}, // + {"desync-known ", 0, 0, 'K'}, + {"split-at-host", 0, 0, 'H'}, + {"help", 0, 0, 'h'}, + {"version", 0, 0, 'v'}, + {"pidfile", 1, 0, 'f'}, + {"ip", 1, 0, 'i'}, + {"port", 1, 0, 'p'}, + {"bfs", 1, 0, 'b'}, // + {"snd-bfs", 1, 0, 'B'}, // + {"max-conn", 1, 0, 'c'}, + {"method", 1, 0, 'm'}, + {"split-pos", 1, 0, 's'}, + {"ttl", 1, 0, 't'}, + {"fake-tls", 1, 0, 'l'}, + {"fake-http", 1, 0, 'o'}, + {"tls-sni", 1, 0, 'n'}, + {"mod-http", 1, 0, 'M'}, + {"global-ttl", 1, 0, 'g'}, // + {"delay", 1, 0, 'w'}, // + {"debug", 1, 0, 'x'}, // + + {0} + }; + int rez; + int invalid = 0; + + long val = 0; + char *end = 0; + + while (!invalid && (rez = getopt_long_only(argc, argv, + "DTPONXKHhvf:i:p:b:B:c:m:s:t:l:o:n:M:g:w:x:", options, 0)) != -1) { + switch (rez) { + + case 'D': + daemon = 1; + break; + case 'f': + pidfile = optarg; + break; + case 'T': + params.mode = MODE_TRANSPARENT; + break; + case 'O': + params.mode = MODE_PROXY_S; + break; + case 'N': + params.resolve = 0; + break; + case 'X': + params.ipv6 = 0; + break; + case 'P': + params.mode = MODE_PROXY_H; + break; + case 'K': + params.de_known = 1; + break; + case 'h': + printf(help_text); + return 0; + case 'v': + printf("%d\n", VERSION); + return 0; + + case 'b': // + val = strtol(optarg, &end, 0); + if (val <= 0 || val > INT_MAX/4 || *end) + invalid = 1; + else + params.bfsize = val; + break; + + case 'B': // + val = strtol(optarg, &end, 0); + if (val <= 0 || val > INT_MAX || *end) + invalid = 1; + else + params.send_bfsz = val; + break; + + case 'i': + if (strchr(optarg, ':')) + s.in.sin_family = AF_INET6; + else + s.in.sin_family = AF_INET; + + if (!inet_pton(s.in.sin_family, optarg, + (s.in.sin_family == AF_INET ? + (char *)&s.in.sin_addr : (char *)&s.in6.sin6_addr))) + invalid = 1; + break; + + case 'p': + val = strtol(optarg, &end, 0); + if (val <= 0 || val > 0xffff || *end) + invalid = 1; + else + s.in.sin_port = htons(val); + break; + + case 'c': + val = strtol(optarg, &end, 0); + if (val <= 0 || val >= (0xffff/2) || *end) + invalid = 1; + else + params.max_open = val; + break; + + case 'm': + if (params.attack != DESYNC_NONE) { + fprintf(stderr, "methods incompatible\n"); + invalid = 1; + } + else switch (*optarg) { + case 's': + params.attack = DESYNC_SPLIT; + break; + case 'd': + params.attack = DESYNC_DISORDER; + break; + case 'f': + params.attack = DESYNC_FAKE; + break; + default: + invalid = 1; + } + break; + + case 's': + val = strtol(optarg, &end, 0); + if (val < INT_MIN || val > INT_MAX || *end) + invalid = 1; + else + params.split = val; + break; + + case 'H': + params.split_host = 1; + break; + + case 't': + val = strtol(optarg, &end, 0); + if (val <= 0 || val > 255 || *end) + invalid = 1; + else + params.ttl = val; + break; + + case 'n': + if (change_tls_sni(optarg, fake_tls.data, fake_tls.size)) { + fprintf(stderr, "error chsni\n"); + return -1; + } + printf("sni: %s\n", optarg); + break; + + case 'l': + fake_tls.data = ftob(optarg); + if (!fake_tls.data) { + perror("read file"); + return -1; + } + break; + + case 'o': + fake_http.data = ftob(optarg); + if (!fake_http.data) { + perror("read file"); + return -1; + } + break; + + case 'M': + end = optarg; + while (end && !invalid) { + switch (*end) { + case 'r': + params.mod_http |= MH_SPACE; + break; + case 'h': + params.mod_http |= MH_HMIX; + break; + case 'd': + params.mod_http |= MH_DMIX; + break; + default: + invalid = 1; + continue; + } + end = strchr(end, ','); + if (end) end++; + } + break; + + case 'g': // + val = strtol(optarg, &end, 0); + if (val <= 0 || val > 255 || *end) + invalid = 1; + else + params.def_ttl = val; + break; + + case 'w': // + params.sfdelay = strtoul(optarg, &end, 0); + if (optarg == end || *end) + invalid = 1; + break; + + case 'x': // + params.debug = strtol(optarg, 0, 0); + if (params.debug < 0) + invalid = 1; + break; + + case 0: + break; + + case '?': + return -1; + + default: + printf("?: %c\n", rez); + return -1; + } + } + if (invalid) { + fprintf(stderr, "invalid value: -%c %s\n", rez, optarg); + return -1; + } + if (params.send_bfsz * 2 <= params.bfsize) { + fprintf(stderr, "send buffer too small\n"); + return -1; + } else { + params.nack_max = val * 2 - params.bfsize; + } + FILE *file; + if (pidfile) { + file = fopen(pidfile, "w"); + if (!file) { + perror("fopen"); + return -1; + } + } + if (daemon) { + daemonize(); + } + if (pidfile) { + fprintf(file, "%d", getpid()); + fclose(file); + } + + if (!params.def_ttl) { + int orig_ttl, fd; + socklen_t tsize = sizeof(orig_ttl); + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + perror("socket"); + return -1; + } + if (getsockopt(fd, IPPROTO_IP, IP_TTL, + (char *)&orig_ttl, &tsize) < 0) { + perror("getsockopt IP_TTL"); + close(fd); + return -1; + } + close(fd); + params.def_ttl = orig_ttl; + } + return listener(s); +} \ No newline at end of file diff --git a/packets.c b/packets.c new file mode 100644 index 0000000..efda598 --- /dev/null +++ b/packets.c @@ -0,0 +1,214 @@ +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include + +#define ANTOHS(data, i) \ + (uint16_t)((data[i] << 8) + (uint8_t)data[i + 1]) + + +char tls_data[517] = { + "\x16\x03\x01\x02\x00\x01\x00\x01\xfc\x03\x03\x03\x5f" + "\x6f\x2c\xed\x13\x22\xf8\xdc\xb2\xf2\x60\x48\x2d\x72" + "\x66\x6f\x57\xdd\x13\x9d\x1b\x37\xdc\xfa\x36\x2e\xba" + "\xf9\x92\x99\x3a\x20\xf9\xdf\x0c\x2e\x8a\x55\x89\x82" + "\x31\x63\x1a\xef\xa8\xbe\x08\x58\xa7\xa3\x5a\x18\xd3" + "\x96\x5f\x04\x5c\xb4\x62\xaf\x89\xd7\x0f\x8b\x00\x3e" + "\x13\x02\x13\x03\x13\x01\xc0\x2c\xc0\x30\x00\x9f\xcc" + "\xa9\xcc\xa8\xcc\xaa\xc0\x2b\xc0\x2f\x00\x9e\xc0\x24" + "\xc0\x28\x00\x6b\xc0\x23\xc0\x27\x00\x67\xc0\x0a\xc0" + "\x14\x00\x39\xc0\x09\xc0\x13\x00\x33\x00\x9d\x00\x9c" + "\x00\x3d\x00\x3c\x00\x35\x00\x2f\x00\xff\x01\x00\x01" + "\x75\x00\x00\x00\x16\x00\x14\x00\x00\x11\x77\x77\x77" + "\x2e\x77\x69\x6b\x69\x70\x65\x64\x69\x61\x2e\x6f\x72" + "\x67\x00\x0b\x00\x04\x03\x00\x01\x02\x00\x0a\x00\x16" + "\x00\x14\x00\x1d\x00\x17\x00\x1e\x00\x19\x00\x18\x01" + "\x00\x01\x01\x01\x02\x01\x03\x01\x04\x00\x10\x00\x0e" + "\x00\x0c\x02\x68\x32\x08\x68\x74\x74\x70\x2f\x31\x2e" + "\x31\x00\x16\x00\x00\x00\x17\x00\x00\x00\x31\x00\x00" + "\x00\x0d\x00\x2a\x00\x28\x04\x03\x05\x03\x06\x03\x08" + "\x07\x08\x08\x08\x09\x08\x0a\x08\x0b\x08\x04\x08\x05" + "\x08\x06\x04\x01\x05\x01\x06\x01\x03\x03\x03\x01\x03" + "\x02\x04\x02\x05\x02\x06\x02\x00\x2b\x00\x09\x08\x03" + "\x04\x03\x03\x03\x02\x03\x01\x00\x2d\x00\x02\x01\x01" + "\x00\x33\x00\x26\x00\x24\x00\x1d\x00\x20\x11\x8c\xb8" + "\x8c\xe8\x8a\x08\x90\x1e\xee\x19\xd9\xdd\xe8\xd4\x06" + "\xb1\xd1\xe2\xab\xe0\x16\x63\xd6\xdc\xda\x84\xa4\xb8" + "\x4b\xfb\x0e\x00\x15\x00\xac\x00\x00\x00\x00\x00\x00" +}; + +char http_data[43] = { + "GET / HTTP/1.1\r\n" + "Host: www.wikipedia.org\r\n\r\n" +}; + + +int find_tls_ext_offset(uint16_t type, char *data, size_t size) +{ + if (size < 44) { + return 0; + } + uint8_t sid_len = data[43]; + if (size < 44 + sid_len + 2) { + return 0; + } + uint16_t cip_len = ANTOHS(data, 44 + sid_len); + + size_t skip = 44 + sid_len + 2 + cip_len + 4; + if (size <= skip) { + return 0; + } + uint16_t ext_len = ANTOHS(data, skip - 2); + + if (ext_len < (size - skip)) { + size = ext_len + skip; + } + while ((skip + 4) < size) { + uint16_t epyt = ANTOHS(data, skip); + if (type == epyt) { + return skip; + } + uint16_t len = ANTOHS(data, skip + 2); + skip += (len + 4); + } + return 0; +} + + +int change_tls_sni(char *host, char *buffer, size_t bsize) +{ + int sni_offs, pad_offs; + + if (!(sni_offs = find_tls_ext_offset(0x00, buffer, bsize))) { + return -1; + } + if (!(pad_offs = find_tls_ext_offset(0x15, buffer, bsize))) { + return -1; + } + char *sni = &buffer[sni_offs]; + char *pad = &buffer[pad_offs]; + + uint16_t old_sz = ANTOHS(buffer, sni_offs + 2) - 5; + uint16_t free_sz = ANTOHS(buffer, pad_offs + 2); + uint16_t new_sz = strlen(host); + + ssize_t diff = new_sz - old_sz; + + if ((free_sz != (bsize - pad_offs - 4)) + || free_sz < diff) { + return -1; + } + *(uint16_t *)(sni + 2) = htons(old_sz + diff + 5); + *(uint16_t *)(sni + 4) = htons(old_sz + diff + 3); + *(uint16_t *)(sni + 7) = htons(old_sz + diff); + *(uint16_t *)(pad + 2) = htons(free_sz - diff); + + char *host_end = sni + 9 + old_sz; + int oth_sz = bsize - (sni_offs + 9 + old_sz); + + memmove(host_end + diff, host_end, oth_sz); + memcpy(sni + 9, host, new_sz); + return 0; +} + + +int parse_tls(char *buffer, size_t bsize, char **hs) +{ + if (ANTOHS(buffer, 0) != 0x1603) { + return 0; + } + int sni_offs = find_tls_ext_offset(0x00, buffer, bsize); + + if (!sni_offs || (sni_offs + 12) >= bsize) { + return 0; + } + uint16_t len = ANTOHS(buffer, sni_offs + 7); + + if ((sni_offs + 9 + len) > bsize) { + return 0; + } + *hs = &buffer[sni_offs + 9]; + return len; +} + + +int parse_http(char *buffer, size_t bsize, char **hs, uint16_t *port) +{ + char *host = buffer, *h_end; + size_t osz = bsize; + + if (bsize < 16 || *buffer > 'T' || *buffer < 'C') { + return 0; + } + while (1) { + host = memchr(host, '\n', osz); + if (!host) + return 0; + host++; + osz = bsize - (host - buffer); + if (osz < 6) + return 0; + if (!strncasecmp(host, "Host:", 5)) + break; + } + host += 5; osz -= 5; + for (; osz && isblank(*host); host++, osz--) {} + + char *l_end = memchr(host, '\n', osz); + if (!l_end) { + return 0; + } + for (; isspace(*(l_end - 1)); l_end--) {} + + if (!(isdigit(*(l_end - 1)))) + h_end = 0; + else + h_end = memrchr(host, ':', l_end - host); + + if (!h_end) { + if (port) *port = 80; + h_end = l_end; + } + else if (port) { + char *end; + long i = strtol(h_end + 1, &end, 10); + if (i <= 0 || end != l_end || i > 0xffff) + return 0; + *port = i; + } + *hs = host; + return h_end - host; +} + + +int mod_http(char *buffer, size_t bsize, int m) +{ + char *host = 0, *par; + int hlen = parse_http(buffer, bsize, &host, 0); + if (!hlen) + return -1; + for (par = host - 1; *par != ':'; par--) {} + par -= 4; + if (m & MH_HMIX) { + par[0] = tolower(par[0]); + par[1] = toupper(par[1]); + par[3] = toupper(par[3]); + } + if (m & MH_DMIX) { + for (int i = 0; i < hlen; i += 2) { + host[i] = toupper(host[i]); + } + } + if (m & MH_SPACE) { + for (; !isspace(*(host + hlen)); hlen++) {} + int sc = host - (par + 5); + memmove(par + 5, host, hlen); + memset(par + 5 + hlen, '\t', sc); + } + return 0; +} \ No newline at end of file diff --git a/packets.h b/packets.h new file mode 100644 index 0000000..8a16c60 --- /dev/null +++ b/packets.h @@ -0,0 +1,21 @@ +#include +#include + +#define IS_UNKNOWN 0 +#define IS_HTTP 1 +#define IS_HTTPS 2 + +#define MH_HMIX 1 +#define MH_SPACE 2 +#define MH_DMIX 4 + +extern char tls_data[517]; +extern char http_data[43]; + +int change_tls_sni(char *host, char *buffer, size_t bsize); + +int parse_tls(char *buffer, size_t bsize, char **hs); + +int parse_http(char *buffer, size_t bsize, char **hs, uint16_t *port); + +int mod_http(char *buffer, size_t bsize, int m); \ No newline at end of file diff --git a/params.h b/params.h new file mode 100644 index 0000000..970cb4a --- /dev/null +++ b/params.h @@ -0,0 +1,48 @@ +enum mode { + MODE_PROXY_H, + MODE_PROXY_S, + MODE_TRANSPARENT +}; + +enum demode { + DESYNC_NONE, + DESYNC_SPLIT, + DESYNC_DISORDER, + DESYNC_FAKE +}; + +struct params { + int ttl; + int split; + size_t sfdelay; + enum demode attack; + char split_host; + int def_ttl; + int mod_http; + + enum mode mode; + char ipv6; + char resolve; + char de_known; + int max_open; + + int debug; + size_t bfsize; + size_t nack_max; + int send_bfsz; +}; + +extern struct params params; + +struct packet { + size_t size; + char *data; +}; +extern struct packet fake_tls; +extern struct packet fake_http; + +#define LOG_S 1 +#define LOG_L 2 + +#define LOG(s, str, ...) \ + if (params.debug >= s) printf(str, ##__VA_ARGS__) \ No newline at end of file diff --git a/proxy.c b/proxy.c new file mode 100644 index 0000000..486e1c0 --- /dev/null +++ b/proxy.c @@ -0,0 +1,692 @@ +#define _GNU_SOURCE + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#ifdef __linux__ +#include +#endif + +int NOT_EXIT = 1; + +static void on_cancel(int sig) { + NOT_EXIT = 0; +} + + +static inline int is_binded_addr(int fd, struct sockaddr_ina *dst) +{ + struct sockaddr_ina me; + socklen_t alen = sizeof(me); + + if (getsockname(fd, &me.sa, &alen)) { + perror("getsockname"); + return -1; + } + if (dst->sa.sa_family != me.sa.sa_family || + dst->in.sin_port != me.in.sin_port) { + return 0; + } + if (dst->sa.sa_family == AF_INET6 ? + !memcmp(&dst->in6.sin6_addr, &me.in6.sin6_addr, 16) : + dst->in.sin_addr.s_addr == me.in.sin_addr.s_addr) { + return 1; + } + return 0; +} + + +int resolve(char *host, int len, struct sockaddr_ina *addr) +{ + struct addrinfo hints = {0}, *res = 0; + + hints.ai_socktype = SOCK_STREAM; + hints.ai_family = AF_UNSPEC; + hints.ai_flags = AI_ADDRCONFIG; + + char rchar = host[len]; + host[len] = '\0'; + + if (getaddrinfo(host, 0, &hints, &res) || !res) { + host[len] = rchar; + return -1; + } + if (res->ai_addr->sa_family == AF_INET6) + addr->in6 = *(struct sockaddr_in6 *)res->ai_addr; + else + addr->in = *(struct sockaddr_in *)res->ai_addr; + freeaddrinfo(res); + + host[len] = rchar; + return 0; +} + + +int auth_socks5(int fd, char *buffer, ssize_t n) +{ + if (n <= 2 || (uint8_t)buffer[1] != (n - 2)) { + return -1; + } + buffer[1] = S_AUTH_BAD; + long i = 2; + for (; i < n; i++) + if (buffer[i] == S_AUTH_NONE) { + buffer[1] = S_AUTH_NONE; + break; + } + if (send(fd, buffer, 2, 0) < 0) { + perror("send"); + return -1; + } + return i < n ? 0 : -1; +} + + +int resp_error(int fd, int e, int flag) +{ + if (flag & FLAG_HTTP) { + const char *r; + if (e) r = "HTTP/1.1 504\r\n\r\n"; + else r = "HTTP/1.1 200\r\n\r\n"; + return send(fd, r, 16, 0); + } + else if (flag & FLAG_S4) { + struct s4_req s4r = { + .cmd = e ? S4_ER : S4_OK + }; + return send(fd, &s4r, sizeof(s4r), 0); + } + else if (flag & FLAG_S5) { + uint8_t se; + switch (e) { + case 0: se = S_ER_OK; + break; + case ECONNREFUSED: + se = S_ER_CONN; + break; + case EHOSTUNREACH: + case ETIMEDOUT: + se = S_ER_HOST; + break; + case ENETUNREACH: + se = S_ER_NET; + break; + default: se = S_ER_GEN; + } + struct s5_rep s5r = { + .ver = 0x05, .code = se, + .atp = S_ATP_I4 + }; + return send(fd, &s5r, sizeof(s5r), 0); + } + return 0; +} + + +int handle_socks4(int fd, char *bf, + size_t n, struct sockaddr_ina *dst) +{ + if (n < sizeof(struct s4_req) + 1) { + return -1; + } + struct s4_req *r = (struct s4_req *)bf; + char er = 0; + + if (r->cmd != S_CMD_CONN) { + er = 1; + } + else if (ntohl(r->i4.s_addr) <= 255) do { + er = 1; + if (!params.resolve || bf[n - 1]) + break; + char *ie = strchr(bf + sizeof(*r), 0); + if (!ie) + break; + int len = (bf + n - ie) - 2; + if (len > 2) { + if (resolve(ie + 1, len, dst)) { + fprintf(stderr, "not resolved: %.*s\n", len, ie + 1); + break; + } + er = 0; + } + } while (0); + else { + dst->in.sin_family = AF_INET; + dst->in.sin_addr = r->i4; + } + if (er) { + struct s4_req s4r = { + .cmd = S4_ER + }; + if (send(fd, &s4r, sizeof(s4r), 0) < 0) + perror("send"); + return -1; + } + dst->in.sin_port = r->port; + return 0; +} + + +int handle_socks5(int fd, char *buffer, + size_t n, struct sockaddr_ina *addr) +{ + if (n < sizeof(struct s5_rep)) { + return -1; + } + struct s5_req *r = (struct s5_req *)buffer; + uint8_t er = 0; + + if (n != (r->atp == S_ATP_I4 ? 10 : + (r->atp == S_ATP_ID ? r->id.len + 7 : + (r->atp == S_ATP_I6 ? 22 : 0)))) { + fprintf(stderr, "ss: bad request\n"); + return -1; + } + else if (r->cmd != S_CMD_CONN) { + fprintf(stderr, "ss: unsupported cmd: 0x%x\n", r->cmd); + er = S_ER_CMD; + } + else switch (r->atp) { + case S_ATP_I4: + addr->in.sin_family = AF_INET; + addr->in.sin_addr = r->i4; + break; + + case S_ATP_ID: + if (!params.resolve) { + er = S_ER_ATP; + break; + } + if (resolve(r->id.domain, r->id.len, addr)) { + fprintf(stderr, "not resolved: %.*s\n", r->id.len, r->id.domain); + er = S_ER_HOST; + } + break; + + case S_ATP_I6: + if (!params.ipv6) + er = S_ER_ATP; + else { + addr->in6.sin6_family = AF_INET6; + addr->in6.sin6_addr = r->i6; + } + } + LOG(LOG_L, "s5r: cmd: 0x%x, atp: 0x%x\n", r->cmd, r->atp); + + if (er) { + struct s5_rep s5r = { + .ver = 0x05, .code = er, + .atp = S_ATP_I4 + }; + if (send(fd, &s5r, sizeof(s5r), 0) < 0) + perror("send"); + return -1; + } + addr->in.sin_port = *(uint16_t *)&buffer[n - 2]; + return 0; +} + + +int handle_http(int fd, char *buffer, + size_t bfsize, struct sockaddr_ina *dst) +{ + char *host = 0; + uint16_t port = 443; + int cnt = 0; + + ssize_t n = recv(fd, buffer, bfsize, MSG_PEEK); + if (n <= 0) { + perror("recv proxy"); + return -1; + } + int len = parse_http(buffer, n, &host, &port); + if (len <= 2) { + fprintf(stderr, "parse error\n"); + return -1; + } + if (!memcmp(buffer, "CONNECT", 7)) { + if (recv(fd, buffer, n, 0) != n) { + perror("recv"); + return -1; + } + cnt = 1; + } + if (*host == '[' && host[len - 1] == ']') { + host++; len -= 2; + } + if (resolve(host, len, dst)) { + fprintf(stderr, "not resolved: %.*s\n", len, host); + return -1; + } + dst->in.sin_port = htons(port); + return cnt; +} + + +int nsendc(int fd, int *wn) +{ + #ifdef SO_NWRITE + socklen_t len = sizeof(*wn); + if (getsockopt(fd, SOL_SOCKET, SO_NWRITE, wn, &len) < 0) { + perror("getsockopt SO_NWRITE"); + return -1; + } + #else + if (ioctl(fd, TIOCOUTQ, wn) < 0 ) { + perror("ioctl"); + return -1; + } + #endif + return 0; +} + + +static inline int create_conn(struct poolhd *pool, struct eval *val, + enum eid nxt, struct sockaddr_ina *dst) +{ + int sfd = socket(dst->sa.sa_family, SOCK_STREAM, 0); + if (sfd < 0) { + perror("socket"); + return -1; + } + #ifdef __linux__ + int syn_count = 1; + if (setsockopt(sfd, IPPROTO_TCP, + TCP_SYNCNT, &syn_count, sizeof(syn_count))) { + perror("setsockopt TCP_SYNCNT"); + close(sfd); + return -1; + } + #endif + int one = 1; + if (setsockopt(sfd, IPPROTO_TCP, + TCP_NODELAY, (char *)&one, sizeof(one))) { + perror("setsockopt TCP_NODELAY"); + close(sfd); + return -1; + } + if (setsockopt(sfd, SOL_SOCKET, SO_SNDBUF, + ¶ms.send_bfsz, sizeof(params.send_bfsz)) < 0) { + close(sfd); + perror("setsockopt SO_SNDBUF"); + return -1; + } + if (fcntl(sfd, F_SETFL, O_NONBLOCK) < 0) { + perror("fcntl"); + close(sfd); + return -1; + } + int status = connect(sfd, &dst->sa, sizeof(*dst)); + if (!status || errno != EINPROGRESS) { + perror("connect"); + close(sfd); + return -1; + } + struct eval *pair = add_event(pool, nxt, sfd, POLLOUT); + if (!pair) { + close(sfd); + return -1; + } + val->pair = pair; + pair->pair = val; + pair->flag = FLAG_CONN; + return 0; +} + + +static inline int on_request(struct poolhd *pool, struct eval *val, + char *buffer, size_t bfsize) +{ + struct sockaddr_ina dst = {0}; + + #ifdef __linux__ + if (params.mode == MODE_TRANSPARENT) { + socklen_t alen = sizeof(dst); + + if (getsockopt(val->fd, SOL_IP, SO_ORIGINAL_DST, &dst, &alen)) { + perror("getsockopt SO_ORIGINAL_DST"); + return -1; + } + if (is_binded_addr(val->fd, &dst)) { + fprintf(stderr, "drop connection to self\n"); + return -1; + } + mod_etype(pool, val, POLLIN, 0); + } else + #endif + if (params.mode == MODE_PROXY_H) { + int cnt = handle_http(val->fd, buffer, bfsize, &dst); + if (cnt < 0) + return -1; + else if (cnt) + val->flag = FLAG_HTTP; + else + mod_etype(pool, val, POLLIN, 0); + } + else { + ssize_t n = recv(val->fd, buffer, bfsize, 0); + if (n < 1) { + if (n) perror("ss recv"); + return -1; + } + if (*buffer == S_VER5) { + if (val->flag != FLAG_S5) { + if (auth_socks5(val->fd, buffer, n)) { + return -1; + } + val->flag = FLAG_S5; + return 0; + } + if (handle_socks5(val->fd, buffer, n, &dst)) { + return -1; + } + } + else if (*buffer == S_VER4) { + if (handle_socks4(val->fd, buffer, n, &dst)) { + return -1; + } + val->flag = FLAG_S4; + } + else { + fprintf(stderr, "ss: invalid version: 0x%x (%lu)\n", *buffer, n); + return -1; + } + } + if (create_conn(pool, val, EV_CONNECT, &dst)) { + if (resp_error(val->fd, errno, val->flag) < 0) + perror("send"); + return -1; + } + val->type = EV_IGNORE; + return 0; +} + + +static inline int on_accept(struct poolhd *pool, struct eval *val) +{ + struct sockaddr_ina client; + struct eval *rval; + + while (1) { + socklen_t len = sizeof(client); + int c = accept(val->fd, &client.sa, &len); + if (c < 0) { + if (errno == EAGAIN) + break; + perror("accept"); + return -1; + } + if (fcntl(c, F_SETFL, O_NONBLOCK) < 0) { + perror("fcntl"); + close(c); + continue; + } + int one = 1; + if (setsockopt(c, IPPROTO_TCP, + TCP_NODELAY, (char *)&one, sizeof(one))) { + perror("setsockopt TCP_NODELAY"); + close(c); + continue; + } + if (setsockopt(c, SOL_SOCKET, SO_SNDBUF, + ¶ms.send_bfsz, sizeof(params.send_bfsz)) < 0) { + perror("setsockopt SO_SNDBUF"); + close(c); + continue; + } + if (!(rval = add_event(pool, EV_REQUEST, c, 0))) { + close(c); + continue; + } + } + return 0; +} + + +static inline int on_data(struct eval *val, char *buffer, size_t bfsize) +{ + ssize_t n = recv(val->fd, buffer, bfsize, 0); + if (n <= 0) { + if (n) perror("recv data"); + return -1; + } + if (desync(val->pair->fd, buffer, n)) { + return -1; + } + val->type = EV_TUNNEL; + return 0; +} + + +static inline int on_connect(struct poolhd *pool, struct eval *val, + char *buffer, size_t bfsize, int e) +{ + if (val->flag & FLAG_CONN) { + if (!e) { + val->type = EV_TUNNEL; + mod_etype(pool, val, POLLOUT, 0); + } + if (val->pair->flag) { + int error = 0; + socklen_t len = sizeof(error); + if (e) { + if (getsockopt(val->fd, SOL_SOCKET, + SO_ERROR, (char *)&error, &len)) { + perror("getsockopt SO_ERROR"); + return -1; + } + } + if (resp_error(val->pair->fd, + error, val->pair->flag) < 0) { + perror("send"); + return -1; + } + if (e) return -1; + val->pair->type = EV_CONNECT; + return 0; + } + else if (!e) { + val = val->pair; + mod_etype(pool, val, POLLIN, 1); + } + } + if (e) return -1; + return on_data(val, buffer, bfsize); +} + + +static inline int on_tunnel(struct poolhd *pool, struct eval *val, + char *buffer, size_t bfsize, int out) +{ + ssize_t n = 0; + int peek = 0; + struct eval *pair = val->pair; + + if (val->flag & FLAG_NOSEND && out) { + val->flag &= ~FLAG_NOSEND; + + mod_etype(pool, val, POLLOUT, 0); + mod_etype(pool, val->pair, POLLIN, 1); + + pair = val; + val = val->pair; + } + do { + if (pair->send_count >= params.nack_max) { + int wn = 0; + if (nsendc(pair->fd, &wn)) { + return -1; + } + pair->send_count = wn; + if (wn) { + LOG(LOG_S, "not ack: %d (fd: %d)\n", wn, pair->fd); + } + if (wn >= params.nack_max) { + peek = MSG_PEEK; + } + } + n = recv(val->fd, buffer, bfsize, peek); + + if (n < 0 && errno == EAGAIN) + break; + if (n < 1) { + if (n) perror("recv server"); + return -1; + } + if (send(pair->fd, buffer, n, 0) < 0) { + if (errno == EAGAIN && peek) { + LOG(LOG_S, "EAGAIN, set POLLOUT (fd: %d)\n", pair->fd); + + mod_etype(pool, val, POLLIN, 0); + mod_etype(pool, pair, POLLOUT, 1); + + pair->flag |= FLAG_NOSEND; + break; + } + perror("send"); + return -1; + } + if (peek) { + if (recv(val->fd, buffer, n, 0) != n) { + perror("recv"); + return -1; + } + } + pair->send_count += n; + + } while (n == bfsize); + return 0; +} + + +int big_loop(int srvfd) +{ + size_t bfsize = params.bfsize; + + struct poolhd *pool = init_pool(params.max_open * 2 + 1); + if (!pool) { + perror("init pool"); + return -1; + } + char *buffer = malloc(params.bfsize); + if (!buffer) { + perror("malloc"); + return -1; + } + add_event(pool, EV_ACCEPT, srvfd, 0); + + struct eval *val; + int i = -1, etype; + + while (NOT_EXIT) { + val = next_event(pool, &i, &etype); + if (!val) { + if (errno == EINTR) + continue; + perror("(e)poll"); + break; + } + LOG(LOG_L, "new event: fd: %d, evt: %s\n", val->fd, eid_name[val->type]); + + if (!val->fd) { + continue; + } + switch (val->type) { + case EV_ACCEPT: + if (on_accept(pool, val)) + NOT_EXIT = 0; + continue; + + case EV_REQUEST: + if ((etype & POLLHUP) || + on_request(pool, val, buffer, bfsize)) + del_event(pool, val); + continue; + + case EV_TUNNEL: + if ((etype & POLLHUP) || + on_tunnel(pool, val, buffer, bfsize, etype & POLLOUT)) + del_event(pool, val); + continue; + + case EV_CONNECT: + if (on_connect(pool, val, buffer, bfsize, etype & POLLERR)) + del_event(pool, val); + continue; + + case EV_IGNORE: + if (etype & (POLLHUP | POLLERR)) + del_event(pool, val); + continue; + + default: + fprintf(stderr, "???\n"); + NOT_EXIT = 0; + } + } + fprintf(stderr, "exit\n"); + free(buffer); + destroy_pool(pool); + return 0; +} + + +int listener(struct sockaddr_ina srv) +{ + if (signal(SIGPIPE, SIG_IGN)) + perror("signal SIGPIPE!"); + + if (signal(SIGINT, on_cancel)) + perror("signal SIGINT!"); + + int srvfd = socket(srv.sa.sa_family, SOCK_STREAM, 0); + if (srvfd < 0) { + perror("socket"); + return -1; + } + if (fcntl(srvfd, F_SETFL, O_NONBLOCK) < 0) { + perror("fcntl"); + close(srvfd); + return -1; + } + int opt = 1; + if (setsockopt(srvfd, SOL_SOCKET, + SO_REUSEADDR, (char *)&opt, sizeof(opt)) == -1) { + perror("setsockopt"); + close(srvfd); + return -1; + } + if (bind(srvfd, &srv.sa, sizeof(srv)) < 0) { + perror("bind"); + close(srvfd); + return -1; + } + if (listen(srvfd, 10)) { + perror("listen"); + close(srvfd); + return -1; + } + int status = big_loop(srvfd); + close(srvfd); + return status; +} \ No newline at end of file diff --git a/proxy.h b/proxy.h new file mode 100644 index 0000000..0fdab52 --- /dev/null +++ b/proxy.h @@ -0,0 +1,81 @@ +#include +#include + +struct sockaddr_ina { + union { + struct sockaddr sa; + struct sockaddr_in in; + struct sockaddr_in6 in6; + }; +}; + +#pragma pack(push, 1) + +struct s4_req { + uint8_t ver, cmd; + uint16_t port; + struct in_addr i4; +}; + +struct s5_req { + uint8_t ver, cmd, zero, atp; + union { + struct in_addr i4; + struct in6_addr i6; + struct { + uint8_t len; + char domain[]; + } id; + }; +}; + +struct s5_rep { + uint8_t ver, code, zero, atp; + struct { + struct in_addr i4; + uint16_t port; + }; +}; + +#pragma pack(pop) + +enum s_auth { + S_AUTH_NONE = 0x00, + S_AUTH_GSSAPI = 0x01, + S_AUTH_USPA = 0x02, + S_AUTH_BAD = 0xff +}; + +enum s_atp { + S_ATP_I4 = 0x01, + S_ATP_ID = 0x03, + S_ATP_I6 = 0x04 +}; + +enum s_cmd { + S_CMD_CONN = 0x01, + S_CMD_BIND = 0x02, + S_CMD_AUDP = 0x03 +}; + +enum s_err { + S_ER_OK = 0x00, + S_ER_GEN = 0x01, + S_ER_DENY = 0x02, + S_ER_NET = 0x03, + S_ER_HOST = 0x04, + S_ER_CONN = 0x05, + S_ER_TTL = 0x06, + S_ER_CMD = 0x07, + S_ER_ATP = 0x08 +}; + +enum s4_rep { + S4_OK = 0x5a, + S4_ER = 0x5b +}; + +#define S_VER5 0x05 +#define S_VER4 0x04 + +int listener(struct sockaddr_ina srv); \ No newline at end of file diff --git a/readme.txt b/readme.txt new file mode 100644 index 0000000..41b8380 --- /dev/null +++ b/readme.txt @@ -0,0 +1,37 @@ +Реализация некоторых способов запутывания DPI на Linux в виде SOCKS прокси + +В дополнение к "--help": +-m, --method + Способ десинхронизации, комбинировать нельзя + split: + Разбить первый запрос на два по указанному смещению + Если смещение отрицательное, то считать относительно конца + Реализация: два вызова send + disorder: + Как split, но части отправляются в обратном порядке + Реализация: устанавливаем TTL=1, отправляем первую часть, затем восстанавливаем значение TTL и отправляем вторую + (из-за низкого TTL первая часть не прибудет получателю и ОС выполнит ретрансмисию и снова отправит первую часть, но уже после второй и с нормальным TTL) + fake: + Как disorder, только перед первым запросом отправляется поддельный такого же размера + Реализация: тут используется возможность перезаписи данных после вызове sendfile + помещаем в буффер поддельные данные, вызываем sendfile с таким TTL, чтобы тот не дошел до сервера, + затем перезаписываем данные оригинальными, восстанавливаем TTL и отправляем вторую часть с помощью send + +-H, --split-at-host + Если найден SNI или Host, то считать смещение относительно позиции домена + +-t, --ttl + TTL для поддельного пакета, чтобы тот не дошел до сервера, но был обработан DPI + +-n, --tls-sni + В качестве поддельного пакета для TLS используется заранее записанный ClientHello, + данный параметр модифицирует его, изменяя SNI на указанный + +-M, --mod-http + Всякие манипуляции с HTTP пакетом, можно комбинировать + hcsmix: + "Host: name" -> "hOsT: name" + dcsmix: + "Host: name" -> "Host: NaMe" + rmspace: + "Host: name" -> "Host:name\t" \ No newline at end of file