commit 08e1e1d8c72aacdd27794a07a80d803171b27b5a Author: ruti <> Date: Sat Jun 3 21:52:10 2023 +0200 init 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