mirror of
https://github.com/bol-van/zapret.git
synced 2024-11-26 12:10:53 +03:00
Compare commits
3 Commits
ef9f9ae428
...
36cd8ca3b2
Author | SHA1 | Date | |
---|---|---|---|
|
36cd8ca3b2 | ||
|
9ec2d685e3 | ||
|
46d31003e2 |
@ -55,7 +55,7 @@ TPPORT_SOCKS=987
|
||||
# <HOSTLIST_NOAUTO> appends ipset/zapret-hosts-auto.txt as normal list
|
||||
TPWS_SOCKS_OPT="
|
||||
--filter-tcp=80 --methodeol <HOSTLIST> --new
|
||||
--filter-tcp=443 --split-tls=sni --disorder <HOSTLIST>
|
||||
--filter-tcp=443 --split-pos=midsld --disorder <HOSTLIST>
|
||||
"
|
||||
|
||||
TPWS_ENABLE=0
|
||||
@ -65,7 +65,7 @@ TPWS_PORTS=80,443
|
||||
# <HOSTLIST_NOAUTO> appends ipset/zapret-hosts-auto.txt as normal list
|
||||
TPWS_OPT="
|
||||
--filter-tcp=80 --methodeol <HOSTLIST> --new
|
||||
--filter-tcp=443 --split-tls=sni --disorder <HOSTLIST>
|
||||
--filter-tcp=443 --split-pos=midsld --disorder <HOSTLIST>
|
||||
"
|
||||
|
||||
NFQWS_ENABLE=0
|
||||
@ -89,8 +89,8 @@ NFQWS_UDP_PKT_IN=0
|
||||
# hostlist markers are replaced to empty string if MODE_FILTER does not satisfy
|
||||
# <HOSTLIST_NOAUTO> appends ipset/zapret-hosts-auto.txt as normal list
|
||||
NFQWS_OPT="
|
||||
--filter-tcp=80 --dpi-desync=fake,split2 --dpi-desync-fooling=md5sig <HOSTLIST> --new
|
||||
--filter-tcp=443 --dpi-desync=fake,disorder2 --dpi-desync-fooling=md5sig <HOSTLIST> --new
|
||||
--filter-tcp=80 --dpi-desync=fake,multisplit --dpi-desync-split-pos=method+2 --dpi-desync-fooling=md5sig <HOSTLIST> --new
|
||||
--filter-tcp=443 --dpi-desync=fake,multidisorder --dpi-desync-split-pos=midsld --dpi-desync-fooling=md5sig <HOSTLIST> --new
|
||||
--filter-udp=443 --dpi-desync=fake --dpi-desync-repeats=6 <HOSTLIST_NOAUTO>
|
||||
"
|
||||
|
||||
|
@ -5,7 +5,7 @@ CFLAGS_MAC = -mmacosx-version-min=10.8
|
||||
CFLAGS_CYGWIN = -Wno-address-of-packed-member -static
|
||||
LIBS_LINUX = -lnetfilter_queue -lnfnetlink -lz
|
||||
LIBS_BSD = -lz
|
||||
LIBS_CYGWIN = -lz -Lwindows/windivert -Iwindows -lwlanapi -lole32 -loleaut32 -luuid
|
||||
LIBS_CYGWIN = -lz -Lwindows/windivert -Iwindows -lwlanapi -lole32 -loleaut32
|
||||
LIBS_CYGWIN32 = -lwindivert32
|
||||
LIBS_CYGWIN64 = -lwindivert64
|
||||
RES_CYGWIN32 = windows/res/32/winmanifest.o windows/res/32/winicon.o
|
||||
|
@ -22,6 +22,7 @@
|
||||
#endif
|
||||
|
||||
#ifdef __CYGWIN__
|
||||
#define INITGUID
|
||||
#include "windivert/windivert.h"
|
||||
#endif
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include "helpers.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <sys/socket.h>
|
||||
@ -11,6 +11,29 @@
|
||||
#include <time.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "helpers.h"
|
||||
|
||||
int unique_size_t(size_t *pu, int ct)
|
||||
{
|
||||
int i, j, u;
|
||||
for (i = j = 0; j < ct; i++)
|
||||
{
|
||||
u = pu[j++];
|
||||
for (; j < ct && pu[j] == u; j++);
|
||||
pu[i] = u;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
static int cmp_size_t(const void * a, const void * b)
|
||||
{
|
||||
return *(size_t*)a < *(size_t*)b ? -1 : *(size_t*)a > *(size_t*)b;
|
||||
}
|
||||
void qsort_size_t(size_t *array,size_t ct)
|
||||
{
|
||||
qsort(array,ct,sizeof(*array),cmp_size_t);
|
||||
}
|
||||
|
||||
|
||||
void rtrim(char *s)
|
||||
{
|
||||
if (s)
|
||||
|
@ -15,6 +15,9 @@ typedef union
|
||||
struct sockaddr_in6 sa6; // size 28
|
||||
} sockaddr_in46;
|
||||
|
||||
int unique_size_t(size_t *pu, int ct);
|
||||
void qsort_size_t(size_t *array,size_t ct);
|
||||
|
||||
void rtrim(char *s);
|
||||
void replace_char(char *s, char from, char to);
|
||||
char *strncasestr(const char *s,const char *find, size_t slen);
|
||||
|
@ -139,6 +139,22 @@ int HOSTLIST_DEBUGLOG_APPEND(const char *format, ...)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void hexdump_limited_dlog(const uint8_t *data, size_t size, size_t limit)
|
||||
{
|
||||
size_t k;
|
||||
bool bcut = false;
|
||||
if (size > limit)
|
||||
{
|
||||
size = limit;
|
||||
bcut = true;
|
||||
}
|
||||
if (!size) return;
|
||||
for (k = 0; k < size; k++) VPRINT("%02X ", data[k]);
|
||||
VPRINT(bcut ? "... : " : ": ");
|
||||
for (k = 0; k < size; k++) VPRINT("%c", data[k] >= 0x20 && data[k] <= 0x7F ? (char)data[k] : '.');
|
||||
if (bcut) VPRINT(" ...");
|
||||
}
|
||||
|
||||
|
||||
struct desync_profile_list *dp_list_add(struct desync_profile_list_head *head)
|
||||
{
|
||||
|
@ -29,6 +29,8 @@ struct bind_s
|
||||
int bind_wait_ifup,bind_wait_ip,bind_wait_ip_ll;
|
||||
};
|
||||
|
||||
#define MAX_SPLITS 16
|
||||
|
||||
enum log_target { LOG_TARGET_CONSOLE=0, LOG_TARGET_FILE, LOG_TARGET_SYSLOG };
|
||||
|
||||
struct desync_profile
|
||||
@ -38,16 +40,16 @@ struct desync_profile
|
||||
bool hostcase, hostdot, hosttab, hostnospace, methodspace, methodeol, unixeol, domcase;
|
||||
int hostpad;
|
||||
char hostspell[4];
|
||||
enum httpreqpos split_http_req;
|
||||
enum tlspos tlsrec;
|
||||
int tlsrec_pos;
|
||||
enum tlspos split_tls;
|
||||
bool split_any_protocol;
|
||||
int split_pos;
|
||||
bool disorder, disorder_http, disorder_tls;
|
||||
bool oob, oob_http, oob_tls;
|
||||
uint8_t oob_byte;
|
||||
|
||||
// multisplit
|
||||
struct proto_pos splits[MAX_SPLITS];
|
||||
int split_count;
|
||||
struct proto_pos tlsrec;
|
||||
|
||||
int mss;
|
||||
|
||||
bool tamper_start_n,tamper_cutoff_n;
|
||||
@ -140,6 +142,7 @@ int DLOG_CONDUP(const char *format, ...);
|
||||
int DLOG_ERR(const char *format, ...);
|
||||
int DLOG_PERROR(const char *s);
|
||||
int HOSTLIST_DEBUGLOG_APPEND(const char *format, ...);
|
||||
void hexdump_limited_dlog(const uint8_t *data, size_t size, size_t limit);
|
||||
|
||||
#define VPRINT(format, ...) DLOG(format, 1, ##__VA_ARGS__)
|
||||
#define DBGPRINT(format, ...) DLOG(format, 2, ##__VA_ARGS__)
|
||||
|
211
tpws/protocol.c
211
tpws/protocol.c
@ -24,6 +24,133 @@ static bool FindNLD(const uint8_t *dom, size_t dlen, int level, const uint8_t **
|
||||
return true;
|
||||
}
|
||||
|
||||
const char *l7proto_str(t_l7proto l7)
|
||||
{
|
||||
switch(l7)
|
||||
{
|
||||
case HTTP: return "http";
|
||||
case TLS: return "tls";
|
||||
case QUIC: return "quic";
|
||||
case WIREGUARD: return "wireguard";
|
||||
case DHT: return "dht";
|
||||
default: return "unknown";
|
||||
}
|
||||
}
|
||||
bool l7_proto_match(t_l7proto l7proto, uint32_t filter_l7)
|
||||
{
|
||||
return (l7proto==UNKNOWN && (filter_l7 & L7_PROTO_UNKNOWN)) ||
|
||||
(l7proto==HTTP && (filter_l7 & L7_PROTO_HTTP)) ||
|
||||
(l7proto==TLS && (filter_l7 & L7_PROTO_TLS)) ||
|
||||
(l7proto==QUIC && (filter_l7 & L7_PROTO_QUIC)) ||
|
||||
(l7proto==WIREGUARD && (filter_l7 & L7_PROTO_WIREGUARD)) ||
|
||||
(l7proto==DHT && (filter_l7 & L7_PROTO_DHT));
|
||||
}
|
||||
|
||||
#define PM_ABS 0
|
||||
#define PM_HOST 1
|
||||
#define PM_HOST_END 2
|
||||
#define PM_HOST_SLD 3
|
||||
#define PM_HOST_MIDSLD 4
|
||||
#define PM_HOST_ENDSLD 5
|
||||
#define PM_HTTP_METHOD 6
|
||||
#define PM_SNI_EXT 7
|
||||
bool IsHostMarker(uint8_t posmarker)
|
||||
{
|
||||
switch(posmarker)
|
||||
{
|
||||
case PM_HOST:
|
||||
case PM_HOST_END:
|
||||
case PM_HOST_SLD:
|
||||
case PM_HOST_MIDSLD:
|
||||
case PM_HOST_ENDSLD:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
const char *posmarker_name(uint8_t posmarker)
|
||||
{
|
||||
switch(posmarker)
|
||||
{
|
||||
case PM_ABS: return "abs";
|
||||
case PM_HOST: return "host";
|
||||
case PM_HOST_END: return "endhost";
|
||||
case PM_HOST_SLD: return "sld";
|
||||
case PM_HOST_MIDSLD: return "midsld";
|
||||
case PM_HOST_ENDSLD: return "endsld";
|
||||
case PM_HTTP_METHOD: return "method";
|
||||
case PM_SNI_EXT: return "sniext";
|
||||
default: return "?";
|
||||
}
|
||||
}
|
||||
|
||||
static size_t CheckPos(size_t sz, ssize_t offset)
|
||||
{
|
||||
return (offset>=0 && offset<sz) ? offset : 0;
|
||||
}
|
||||
size_t AnyProtoPos(uint8_t posmarker, int16_t pos, const uint8_t *data, size_t sz)
|
||||
{
|
||||
ssize_t offset;
|
||||
switch(posmarker)
|
||||
{
|
||||
case PM_ABS:
|
||||
offset = (pos<0) ? sz+pos : pos;
|
||||
return CheckPos(sz,offset);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
static size_t HostPos(uint8_t posmarker, int16_t pos, const uint8_t *data, size_t sz, size_t offset_host, size_t len_host)
|
||||
{
|
||||
ssize_t offset;
|
||||
const uint8_t *p;
|
||||
size_t slen;
|
||||
|
||||
switch(posmarker)
|
||||
{
|
||||
case PM_HOST:
|
||||
offset = offset_host+pos;
|
||||
break;
|
||||
case PM_HOST_END:
|
||||
offset = offset_host+len_host+pos;
|
||||
break;
|
||||
case PM_HOST_SLD:
|
||||
case PM_HOST_MIDSLD:
|
||||
case PM_HOST_ENDSLD:
|
||||
if (((offset_host+len_host)<=sz) && FindNLD(data+offset_host,len_host,2,&p,&slen))
|
||||
offset = (posmarker==PM_HOST_SLD ? p-data : posmarker==PM_HOST_ENDSLD ? p-data+slen : slen==1 ? p+1-data : p+slen/2-data) + pos;
|
||||
else
|
||||
offset = 0;
|
||||
break;
|
||||
}
|
||||
return CheckPos(sz,offset);
|
||||
}
|
||||
size_t ResolvePos(const uint8_t *data, size_t sz, t_l7proto l7proto, const struct proto_pos *sp)
|
||||
{
|
||||
switch(l7proto)
|
||||
{
|
||||
case HTTP:
|
||||
return HttpPos(sp->marker, sp->pos, data, sz);
|
||||
case TLS:
|
||||
return TLSPos(sp->marker, sp->pos, data, sz);
|
||||
default:
|
||||
return AnyProtoPos(sp->marker, sp->pos, data, sz);
|
||||
}
|
||||
}
|
||||
void ResolveMultiPos(const uint8_t *data, size_t sz, t_l7proto l7proto, const struct proto_pos *splits, int split_count, size_t *pos, int *pos_count)
|
||||
{
|
||||
int i,j;
|
||||
for(i=j=0;i<split_count;i++)
|
||||
{
|
||||
pos[j] = ResolvePos(data,sz,l7proto,splits+i);
|
||||
if (pos[j]) j++;
|
||||
}
|
||||
qsort_size_t(pos, j);
|
||||
j=unique_size_t(pos, j);
|
||||
*pos_count=j;
|
||||
}
|
||||
|
||||
|
||||
const char *http_methods[] = { "GET /","POST /","HEAD /","OPTIONS /","PUT /","DELETE /","CONNECT /","TRACE /",NULL };
|
||||
const char *HttpMethod(const uint8_t *data, size_t len)
|
||||
{
|
||||
@ -169,36 +296,45 @@ bool HttpReplyLooksLikeDPIRedirect(const uint8_t *data, size_t len, const char *
|
||||
// compare 2nd level domains
|
||||
return strcasecmp(dhost, drhost)!=0;
|
||||
}
|
||||
size_t HttpPos(enum httpreqpos tpos_type, size_t hpos_pos, const uint8_t *http, size_t sz)
|
||||
size_t HttpPos(uint8_t posmarker, int16_t pos, const uint8_t *data, size_t sz)
|
||||
{
|
||||
const uint8_t *method, *host=NULL;
|
||||
const uint8_t *method, *host=NULL, *p;
|
||||
size_t offset_host,len_host;
|
||||
ssize_t offset;
|
||||
int i;
|
||||
|
||||
switch(tpos_type)
|
||||
switch(posmarker)
|
||||
{
|
||||
case httpreqpos_method:
|
||||
case PM_HTTP_METHOD:
|
||||
// recognize some tpws pre-applied hacks
|
||||
method=http;
|
||||
method=data;
|
||||
if (sz<10) break;
|
||||
if (*method=='\n' || *method=='\r') method++;
|
||||
if (*method=='\n' || *method=='\r') method++;
|
||||
for (i=0;i<7;i++) if (*method>='A' && *method<='Z') method++;
|
||||
if (i<3 || *method!=' ') break;
|
||||
return method-http-1;
|
||||
case httpreqpos_host:
|
||||
if (HttpFindHostConst(&host,http,sz) && (host-http+7)<sz)
|
||||
for (p=method,i=0;i<7;i++) if (*p>='A' && *p<='Z') p++;
|
||||
if (i<3 || *p!=' ') break;
|
||||
return CheckPos(sz,method-data+pos);
|
||||
case PM_HOST:
|
||||
case PM_HOST_END:
|
||||
case PM_HOST_SLD:
|
||||
case PM_HOST_MIDSLD:
|
||||
case PM_HOST_ENDSLD:
|
||||
if (HttpFindHostConst(&host,data,sz) && (host-data+7)<sz)
|
||||
{
|
||||
host+=5;
|
||||
if (*host==' ') host++;
|
||||
return host-http;
|
||||
if (*host==' ' || *host=='\t') host++;
|
||||
offset_host = host-data;
|
||||
if (posmarker!=PM_HOST)
|
||||
for (len_host=0; (offset_host+len_host)<sz && data[offset_host+len_host]!='\r' && data[offset_host+len_host]!='\n'; len_host++);
|
||||
else
|
||||
len_host = 0;
|
||||
return HostPos(posmarker,pos,data,sz,offset_host,len_host);
|
||||
}
|
||||
break;
|
||||
case httpreqpos_pos:
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
return AnyProtoPos(posmarker,pos,data,sz);
|
||||
}
|
||||
return hpos_pos<sz ? hpos_pos : 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@ -361,26 +497,37 @@ static bool TLSHelloFindMiddleOfSLDInSNI(const uint8_t *ext, size_t elen, const
|
||||
*p = (len==1) ? *p+1 : *p+len/2;
|
||||
return true;
|
||||
}
|
||||
size_t TLSPos(enum tlspos tpos_type, size_t tpos_pos, const uint8_t *tls, size_t sz, uint8_t type)
|
||||
size_t TLSPos(uint8_t posmarker, int16_t pos, const uint8_t *data, size_t sz)
|
||||
{
|
||||
size_t elen;
|
||||
const uint8_t *ext, *p;
|
||||
switch(tpos_type)
|
||||
size_t offset_host,len_host;
|
||||
ssize_t offset;
|
||||
|
||||
switch(posmarker)
|
||||
{
|
||||
case tlspos_sni:
|
||||
case tlspos_sniext:
|
||||
if (TLSFindExt(tls,sz,0,&ext,&elen,false))
|
||||
return (tpos_type==tlspos_sni) ? ext-tls+6 : ext-tls+1;
|
||||
break;
|
||||
case tlspos_snisld:
|
||||
if (TLSFindExt(tls,sz,0,&ext,&elen,false))
|
||||
if (TLSHelloFindMiddleOfSLDInSNI(ext,elen,&p))
|
||||
return p-tls;
|
||||
break;
|
||||
case tlspos_pos:
|
||||
break;
|
||||
default:
|
||||
case PM_HOST:
|
||||
case PM_HOST_END:
|
||||
case PM_HOST_SLD:
|
||||
case PM_HOST_MIDSLD:
|
||||
case PM_HOST_ENDSLD:
|
||||
case PM_SNI_EXT:
|
||||
if (TLSFindExt(data,sz,0,&ext,&elen,false))
|
||||
{
|
||||
if (posmarker==PM_SNI_EXT)
|
||||
{
|
||||
return CheckPos(sz,ext-data+pos);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!TLSAdvanceToHostInSNI(&ext,&elen,&len_host))
|
||||
return 0;
|
||||
offset_host = ext-data;
|
||||
return HostPos(posmarker,pos,data,sz,offset_host,len_host);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
default:
|
||||
return AnyProtoPos(posmarker,pos,data,sz);
|
||||
}
|
||||
return tpos_pos<sz ? tpos_pos : 0;
|
||||
}
|
||||
|
@ -4,6 +4,40 @@
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef enum {UNKNOWN=0, HTTP, TLS, QUIC, WIREGUARD, DHT} t_l7proto;
|
||||
#define L7_PROTO_HTTP 0x00000001
|
||||
#define L7_PROTO_TLS 0x00000002
|
||||
#define L7_PROTO_QUIC 0x00000004
|
||||
#define L7_PROTO_WIREGUARD 0x00000008
|
||||
#define L7_PROTO_DHT 0x00000010
|
||||
#define L7_PROTO_UNKNOWN 0x80000000
|
||||
const char *l7proto_str(t_l7proto l7);
|
||||
bool l7_proto_match(t_l7proto l7proto, uint32_t filter_l7);
|
||||
|
||||
// pos markers
|
||||
#define PM_ABS 0
|
||||
#define PM_HOST 1
|
||||
#define PM_HOST_END 2
|
||||
#define PM_HOST_SLD 3
|
||||
#define PM_HOST_MIDSLD 4
|
||||
#define PM_HOST_ENDSLD 5
|
||||
#define PM_HTTP_METHOD 6
|
||||
#define PM_SNI_EXT 7
|
||||
struct proto_pos
|
||||
{
|
||||
int16_t pos;
|
||||
uint8_t marker;
|
||||
};
|
||||
#define PROTO_POS_EMPTY(sp) ((sp)->marker==PM_ABS && (sp)->pos==0)
|
||||
bool IsHostMarker(uint8_t posmarker);
|
||||
const char *posmarker_name(uint8_t posmarker);
|
||||
size_t AnyProtoPos(uint8_t posmarker, int16_t pos, const uint8_t *data, size_t sz);
|
||||
size_t HttpPos(uint8_t posmarker, int16_t pos, const uint8_t *data, size_t sz);
|
||||
size_t TLSPos(uint8_t posmarker, int16_t pos, const uint8_t *data, size_t sz);
|
||||
size_t ResolvePos(const uint8_t *data, size_t sz, t_l7proto l7proto, const struct proto_pos *sp);
|
||||
void ResolveMultiPos(const uint8_t *data, size_t sz, t_l7proto l7proto, const struct proto_pos *splits, int split_count, size_t *pos, int *pos_count);
|
||||
|
||||
|
||||
extern const char *http_methods[9];
|
||||
const char *HttpMethod(const uint8_t *data, size_t len);
|
||||
bool IsHttp(const uint8_t *data, size_t len);
|
||||
@ -18,8 +52,6 @@ const char *HttpFind2ndLevelDomain(const char *host);
|
||||
int HttpReplyCode(const uint8_t *data, size_t len);
|
||||
// must be pre-checked by IsHttpReply
|
||||
bool HttpReplyLooksLikeDPIRedirect(const uint8_t *data, size_t len, const char *host);
|
||||
enum httpreqpos { httpreqpos_none = 0, httpreqpos_method, httpreqpos_host, httpreqpos_pos };
|
||||
size_t HttpPos(enum httpreqpos tpos_type, size_t hpos_pos, const uint8_t *http, size_t sz);
|
||||
|
||||
uint16_t TLSRecordDataLen(const uint8_t *data);
|
||||
size_t TLSRecordLen(const uint8_t *data);
|
||||
@ -29,5 +61,3 @@ bool TLSFindExt(const uint8_t *data, size_t len, uint16_t type, const uint8_t **
|
||||
bool TLSFindExtInHandshake(const uint8_t *data, size_t len, uint16_t type, const uint8_t **ext, size_t *len_ext, bool bPartialIsOK);
|
||||
bool TLSHelloExtractHost(const uint8_t *data, size_t len, char *host, size_t len_host, bool bPartialIsOK);
|
||||
bool TLSHelloExtractHostFromHandshake(const uint8_t *data, size_t len, char *host, size_t len_host, bool bPartialIsOK);
|
||||
enum tlspos { tlspos_none = 0, tlspos_sni, tlspos_sniext, tlspos_snisld, tlspos_pos };
|
||||
size_t TLSPos(enum tlspos tpos_type, size_t tpos_pos, const uint8_t *tls, size_t sz, uint8_t type);
|
||||
|
@ -221,7 +221,7 @@ bool resolver_init(int threads, int fd_signal_pipe)
|
||||
if (pthread_attr_init(&attr)) goto ex;
|
||||
// set minimum thread stack size
|
||||
|
||||
if (pthread_attr_setstacksize(&attr,PTHREAD_STACK_MIN>20480 ? PTHREAD_STACK_MIN : 20480))
|
||||
if (pthread_attr_setstacksize(&attr,PTHREAD_STACK_MIN>32768 ? PTHREAD_STACK_MIN : 32768))
|
||||
{
|
||||
pthread_attr_destroy(&attr);
|
||||
goto ex;
|
||||
|
@ -8,22 +8,6 @@
|
||||
#include "protocol.h"
|
||||
#include "helpers.h"
|
||||
|
||||
const char *l7proto_str(t_l7proto l7)
|
||||
{
|
||||
switch(l7)
|
||||
{
|
||||
case HTTP: return "http";
|
||||
case TLS: return "tls";
|
||||
default: return "unknown";
|
||||
}
|
||||
}
|
||||
static bool l7_proto_match(t_l7proto l7proto, uint32_t filter_l7)
|
||||
{
|
||||
return (l7proto==UNKNOWN && (filter_l7 & L7_PROTO_UNKNOWN)) ||
|
||||
(l7proto==HTTP && (filter_l7 & L7_PROTO_HTTP)) ||
|
||||
(l7proto==TLS && (filter_l7 & L7_PROTO_TLS));
|
||||
}
|
||||
|
||||
static bool dp_match(struct desync_profile *dp, const struct sockaddr *dest, const char *hostname, t_l7proto l7proto)
|
||||
{
|
||||
bool bHostlistsEmpty;
|
||||
@ -87,11 +71,10 @@ void apply_desync_profile(t_ctrack *ctrack, const struct sockaddr *dest)
|
||||
|
||||
|
||||
// segment buffer has at least 5 extra bytes to extend data block
|
||||
void tamper_out(t_ctrack *ctrack, const struct sockaddr *dest, uint8_t *segment,size_t segment_buffer_size,size_t *size, size_t *split_pos, uint8_t *split_flags)
|
||||
void tamper_out(t_ctrack *ctrack, const struct sockaddr *dest, uint8_t *segment,size_t segment_buffer_size,size_t *size, size_t *multisplit_pos, int *multisplit_count, uint8_t *split_flags)
|
||||
{
|
||||
uint8_t *p, *pp, *pHost = NULL;
|
||||
size_t method_len = 0, pos;
|
||||
size_t tpos, spos;
|
||||
size_t method_len = 0, pos, tpos, orig_size=*size;
|
||||
const char *method;
|
||||
bool bHaveHost = false;
|
||||
char *pc, Host[256];
|
||||
@ -116,8 +99,8 @@ void tamper_out(t_ctrack *ctrack, const struct sockaddr *dest, uint8_t *segment,
|
||||
return;
|
||||
}
|
||||
|
||||
*split_pos=0;
|
||||
*split_flags=0;
|
||||
if (multisplit_count) *multisplit_count=0;
|
||||
if (split_flags) *split_flags=0;
|
||||
|
||||
if ((method = HttpMethod(segment,*size)))
|
||||
{
|
||||
@ -193,7 +176,6 @@ void tamper_out(t_ctrack *ctrack, const struct sockaddr *dest, uint8_t *segment,
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
switch(l7proto)
|
||||
{
|
||||
case HTTP:
|
||||
@ -325,22 +307,26 @@ void tamper_out(t_ctrack *ctrack, const struct sockaddr *dest, uint8_t *segment,
|
||||
pHost = NULL; // invalidate
|
||||
}
|
||||
}
|
||||
*split_pos = HttpPos(ctrack->dp->split_http_req, ctrack->dp->split_pos, segment, *size);
|
||||
if (ctrack->dp->disorder_http) *split_flags |= SPLIT_FLAG_DISORDER;
|
||||
if (ctrack->dp->oob_http) *split_flags |= SPLIT_FLAG_OOB;
|
||||
if (multisplit_pos) ResolveMultiPos(segment, *size, l7proto, ctrack->dp->splits, ctrack->dp->split_count, multisplit_pos, multisplit_count);
|
||||
if (split_flags)
|
||||
{
|
||||
if (ctrack->dp->disorder_http) *split_flags |= SPLIT_FLAG_DISORDER;
|
||||
if (ctrack->dp->oob_http) *split_flags |= SPLIT_FLAG_OOB;
|
||||
}
|
||||
break;
|
||||
|
||||
case TLS:
|
||||
spos = TLSPos(ctrack->dp->split_tls, ctrack->dp->split_pos, segment, *size, 0);
|
||||
if (multisplit_pos) ResolveMultiPos(segment, *size, l7proto, ctrack->dp->splits, ctrack->dp->split_count, multisplit_pos, multisplit_count);
|
||||
if ((5+*size)<=segment_buffer_size)
|
||||
{
|
||||
tpos = TLSPos(ctrack->dp->tlsrec, ctrack->dp->tlsrec_pos+5, segment, *size, 0);
|
||||
tpos = ResolvePos(segment, *size, l7proto, &ctrack->dp->tlsrec);
|
||||
if (tpos>5)
|
||||
{
|
||||
// construct 2 TLS records from one
|
||||
uint16_t l = pntoh16(segment+3); // length
|
||||
if (l>=2)
|
||||
{
|
||||
int i;
|
||||
// length is checked in IsTLSClientHello and cannot exceed buffer size
|
||||
if ((tpos-5)>=l) tpos=5+1;
|
||||
VPRINT("making 2 TLS records at pos %zu\n",tpos);
|
||||
@ -351,27 +337,40 @@ void tamper_out(t_ctrack *ctrack, const struct sockaddr *dest, uint8_t *segment,
|
||||
phton16(segment+tpos+3,l-(tpos-5));
|
||||
phton16(segment+3,tpos-5);
|
||||
*size += 5;
|
||||
// split pos present and it is not before tlsrec split. increase split pos by tlsrec header size (5 bytes)
|
||||
if (spos && spos>=tpos) spos+=5;
|
||||
// fix split positions after tlsrec. increase split pos by tlsrec header size (5 bytes)
|
||||
if (multisplit_pos)
|
||||
for(i=0;i<*multisplit_count;i++)
|
||||
if (multisplit_pos[i]>tpos) multisplit_pos[i]+=5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (spos && spos < *size)
|
||||
*split_pos = spos;
|
||||
|
||||
if (ctrack->dp->disorder_tls) *split_flags |= SPLIT_FLAG_DISORDER;
|
||||
if (ctrack->dp->oob_tls) *split_flags |= SPLIT_FLAG_OOB;
|
||||
|
||||
if (split_flags)
|
||||
{
|
||||
if (ctrack->dp->disorder_tls) *split_flags |= SPLIT_FLAG_DISORDER;
|
||||
if (ctrack->dp->oob_tls) *split_flags |= SPLIT_FLAG_OOB;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if (ctrack->dp->split_any_protocol && ctrack->dp->split_pos < *size)
|
||||
*split_pos = ctrack->dp->split_pos;
|
||||
if (multisplit_pos && ctrack->dp->split_any_protocol)
|
||||
ResolveMultiPos(segment, *size, l7proto, ctrack->dp->splits, ctrack->dp->split_count, multisplit_pos, multisplit_count);
|
||||
}
|
||||
|
||||
if (split_flags)
|
||||
{
|
||||
if (ctrack->dp->disorder) *split_flags |= SPLIT_FLAG_DISORDER;
|
||||
if (ctrack->dp->oob) *split_flags |= SPLIT_FLAG_OOB;
|
||||
}
|
||||
if (orig_size!=*size)
|
||||
{
|
||||
VPRINT("segment size changed: %zu -> %zu\n", orig_size, *size);
|
||||
}
|
||||
if (params.debug && multisplit_count && *multisplit_count)
|
||||
{
|
||||
VPRINT("multisplit pos: ");
|
||||
for (int i=0;i<*multisplit_count;i++) VPRINT("%zu ",multisplit_pos[i]);
|
||||
VPRINT("\n");
|
||||
}
|
||||
|
||||
if (ctrack->dp->disorder) *split_flags |= SPLIT_FLAG_DISORDER;
|
||||
if (ctrack->dp->oob) *split_flags |= SPLIT_FLAG_OOB;
|
||||
}
|
||||
|
||||
static void auto_hostlist_reset_fail_counter(struct desync_profile *dp, const char *hostname, const char *client_ip_port, t_l7proto l7proto)
|
||||
|
@ -9,12 +9,6 @@
|
||||
#define SPLIT_FLAG_DISORDER 0x01
|
||||
#define SPLIT_FLAG_OOB 0x02
|
||||
|
||||
typedef enum {UNKNOWN=0, HTTP, TLS} t_l7proto;
|
||||
#define L7_PROTO_HTTP 1
|
||||
#define L7_PROTO_TLS 2
|
||||
#define L7_PROTO_UNKNOWN 0x80000000
|
||||
const char *l7proto_str(t_l7proto l7);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
// common state
|
||||
@ -28,7 +22,7 @@ typedef struct
|
||||
|
||||
void apply_desync_profile(t_ctrack *ctrack, const struct sockaddr *dest);
|
||||
|
||||
void tamper_out(t_ctrack *ctrack, const struct sockaddr *dest, uint8_t *segment,size_t segment_buffer_size,size_t *size, size_t *split_pos, uint8_t *split_flags);
|
||||
void tamper_out(t_ctrack *ctrack, const struct sockaddr *dest, uint8_t *segment,size_t segment_buffer_size,size_t *size, size_t *multisplit_pos, int *multisplit_count, uint8_t *split_flags);
|
||||
void tamper_in(t_ctrack *ctrack, const struct sockaddr *client, uint8_t *segment,size_t segment_buffer_size,size_t *size);
|
||||
// connection reset by remote leg
|
||||
void rst_in(t_ctrack *ctrack, const struct sockaddr *client);
|
||||
|
189
tpws/tpws.c
189
tpws/tpws.c
@ -182,9 +182,8 @@ static void exithelp(void)
|
||||
" --hostlist-auto-fail-time=<int>\t; all failed attemps must be within these seconds (default : %d)\n"
|
||||
" --hostlist-auto-debug=<logfile>\t; debug auto hostlist positives\n"
|
||||
"\nTAMPER:\n"
|
||||
" --split-http-req=method|host\t\t; split at specified logical part of plain http request\n"
|
||||
" --split-tls=sni|sniext|snisld\t\t; split at specified logical part of TLS ClientHello\n"
|
||||
" --split-pos=<numeric_offset>\t\t; split at specified pos. split-http-req or split-tls take precedence for http.\n"
|
||||
" --split-pos=N|-N|marker+N|marker-N\t; comma separated list of split positions\n"
|
||||
"\t\t\t\t\t; markers: method,host,endhost,sld,endsld,midsld,sniext\n"
|
||||
" --split-any-protocol\t\t\t; split not only http and https\n"
|
||||
#if defined(BSD) && !defined(__APPLE__)
|
||||
" --disorder[=http|tls]\t\t\t; when splitting simulate sending second fragment first (BSD sends entire message instead of first fragment, this is not good)\n"
|
||||
@ -203,8 +202,7 @@ static void exithelp(void)
|
||||
" --methodspace\t\t\t\t; add extra space after method\n"
|
||||
" --methodeol\t\t\t\t; add end-of-line before method\n"
|
||||
" --unixeol\t\t\t\t; replace 0D0A to 0A\n"
|
||||
" --tlsrec=sni|sniext|snisld\t\t; make 2 TLS records. split at specified logical part. don't split if SNI is not present\n"
|
||||
" --tlsrec-pos=<pos>\t\t\t; make 2 TLS records. split at specified pos\n"
|
||||
" --tlsrec=N|-N|marker+N|marker-N\t; make 2 TLS records. split records at specified position.\n"
|
||||
#ifdef __linux__
|
||||
" --mss=<int>\t\t\t\t; set client MSS. forces server to split messages but significantly decreases speed !\n"
|
||||
#endif
|
||||
@ -276,29 +274,131 @@ void save_default_ttl(void)
|
||||
}
|
||||
}
|
||||
|
||||
bool parse_httpreqpos(const char *s, enum httpreqpos *pos)
|
||||
static bool parse_httpreqpos(const char *s, struct proto_pos *sp)
|
||||
{
|
||||
if (!strcmp(s, "method"))
|
||||
*pos = httpreqpos_method;
|
||||
{
|
||||
sp->marker = PM_HTTP_METHOD;
|
||||
sp->pos=2;
|
||||
}
|
||||
else if (!strcmp(s, "host"))
|
||||
*pos = httpreqpos_host;
|
||||
{
|
||||
sp->marker = PM_HOST;
|
||||
sp->pos=1;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
bool parse_tlspos(const char *s, enum tlspos *pos)
|
||||
static bool parse_tlspos(const char *s, struct proto_pos *sp)
|
||||
{
|
||||
if (!strcmp(s, "sni"))
|
||||
*pos = tlspos_sni;
|
||||
{
|
||||
sp->marker = PM_HOST;
|
||||
sp->pos=1;
|
||||
}
|
||||
else if (!strcmp(s, "sniext"))
|
||||
*pos = tlspos_sniext;
|
||||
{
|
||||
sp->marker = PM_SNI_EXT;
|
||||
sp->pos=1;
|
||||
}
|
||||
else if (!strcmp(s, "snisld"))
|
||||
*pos = tlspos_snisld;
|
||||
{
|
||||
sp->marker = PM_HOST_MIDSLD;
|
||||
sp->pos=0;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool parse_int16(const char *p, int16_t *v)
|
||||
{
|
||||
if (*p=='+' || *p=='-' || *p>='0' && *p<='9')
|
||||
{
|
||||
int i = atoi(p);
|
||||
*v = (int16_t)i;
|
||||
return *v==i; // check overflow
|
||||
}
|
||||
return false;
|
||||
}
|
||||
static bool parse_posmarker(const char *opt, uint8_t *posmarker)
|
||||
{
|
||||
if (!strcmp(opt,"host"))
|
||||
*posmarker = PM_HOST;
|
||||
else if (!strcmp(opt,"endhost"))
|
||||
*posmarker = PM_HOST_END;
|
||||
else if (!strcmp(opt,"sld"))
|
||||
*posmarker = PM_HOST_SLD;
|
||||
else if (!strcmp(opt,"midsld"))
|
||||
*posmarker = PM_HOST_MIDSLD;
|
||||
else if (!strcmp(opt,"endsld"))
|
||||
*posmarker = PM_HOST_ENDSLD;
|
||||
else if (!strcmp(opt,"method"))
|
||||
*posmarker = PM_HTTP_METHOD;
|
||||
else if (!strcmp(opt,"sniext"))
|
||||
*posmarker = PM_SNI_EXT;
|
||||
else
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
static bool parse_split_pos(char *opt, struct proto_pos *split)
|
||||
{
|
||||
if (parse_int16(opt,&split->pos))
|
||||
{
|
||||
split->marker = PM_ABS;
|
||||
return !!split->pos;
|
||||
}
|
||||
else
|
||||
{
|
||||
char c,*p=opt;
|
||||
bool b;
|
||||
|
||||
for (; *opt && *opt!='+' && *opt!='-'; opt++);
|
||||
c=*opt; *opt=0;
|
||||
b=parse_posmarker(p,&split->marker);
|
||||
*opt=c;
|
||||
if (!b) return false;
|
||||
if (*opt)
|
||||
return parse_int16(opt,&split->pos);
|
||||
else
|
||||
split->pos = 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
static bool parse_split_pos_list(char *opt, struct proto_pos *splits, int splits_size, int *split_count)
|
||||
{
|
||||
char c,*e,*p;
|
||||
|
||||
for (p=opt, *split_count=0 ; p && *split_count<splits_size ; (*split_count)++)
|
||||
{
|
||||
if ((e = strchr(p,',')))
|
||||
{
|
||||
c=*e;
|
||||
*e=0;
|
||||
}
|
||||
if (!parse_split_pos(p,splits+*split_count)) return false;
|
||||
if (e) *e++=c;
|
||||
p = e;
|
||||
}
|
||||
if (p) return false; // too much splits
|
||||
return true;
|
||||
}
|
||||
static void SplitDebug(void)
|
||||
{
|
||||
struct desync_profile_list *dpl;
|
||||
const struct desync_profile *dp;
|
||||
int x;
|
||||
LIST_FOREACH(dpl, ¶ms.desync_profiles, next)
|
||||
{
|
||||
dp = &dpl->dp;
|
||||
for(x=0;x<dp->split_count;x++)
|
||||
VPRINT("profile %d multisplit %s %d\n",dp->n,posmarker_name(dp->splits[x].marker),dp->splits[x].pos);
|
||||
if (!PROTO_POS_EMPTY(&dp->tlsrec))
|
||||
VPRINT("profile %d tlsrec %s %d\n",dp->n,posmarker_name(dp->tlsrec.marker),dp->tlsrec.pos);
|
||||
}
|
||||
}
|
||||
|
||||
static bool wf_make_l3(char *opt, bool *ipv4, bool *ipv6)
|
||||
{
|
||||
char *e,*p,c;
|
||||
@ -682,29 +782,45 @@ void parse_params(int argc, char *argv[])
|
||||
params.tamper = true;
|
||||
break;
|
||||
case 23: /* split-http-req */
|
||||
if (!parse_httpreqpos(optarg, &dp->split_http_req))
|
||||
DLOG_CONDUP("WARNING ! --split-http-req is deprecated. use --split-pos with markers.\n",MAX_SPLITS);
|
||||
if (dp->split_count>=MAX_SPLITS)
|
||||
{
|
||||
DLOG_ERR("Too much splits. max splits: %u\n",MAX_SPLITS);
|
||||
exit_clean(1);
|
||||
}
|
||||
if (!parse_httpreqpos(optarg, dp->splits + dp->split_count))
|
||||
{
|
||||
DLOG_ERR("Invalid argument for split-http-req\n");
|
||||
exit_clean(1);
|
||||
}
|
||||
dp->split_count++;
|
||||
params.tamper = true;
|
||||
break;
|
||||
case 24: /* split-tls */
|
||||
if (!parse_tlspos(optarg, &dp->split_tls))
|
||||
// obsolete arg
|
||||
DLOG_CONDUP("WARNING ! --split-tls is deprecated. use --split-pos with markers.\n",MAX_SPLITS);
|
||||
if (dp->split_count>=MAX_SPLITS)
|
||||
{
|
||||
DLOG_ERR("Too much splits. max splits: %u\n",MAX_SPLITS);
|
||||
exit_clean(1);
|
||||
}
|
||||
if (!parse_tlspos(optarg, dp->splits + dp->split_count))
|
||||
{
|
||||
DLOG_ERR("Invalid argument for split-tls\n");
|
||||
exit_clean(1);
|
||||
}
|
||||
dp->split_count++;
|
||||
params.tamper = true;
|
||||
break;
|
||||
case 25: /* split-pos */
|
||||
i = atoi(optarg);
|
||||
if (i>0)
|
||||
dp->split_pos = i;
|
||||
else
|
||||
{
|
||||
DLOG_ERR("Invalid argument for split-pos\n");
|
||||
exit_clean(1);
|
||||
int ct;
|
||||
if (!parse_split_pos_list(optarg,dp->splits+dp->split_count,MAX_SPLITS-dp->split_count,&ct))
|
||||
{
|
||||
DLOG_ERR("could not parse split pos list or too much positions (before parsing - %u, max - %u) : %s\n",dp->split_count,MAX_SPLITS,optarg);
|
||||
exit_clean(1);
|
||||
}
|
||||
dp->split_count += ct;
|
||||
}
|
||||
params.tamper = true;
|
||||
break;
|
||||
@ -724,7 +840,6 @@ void parse_params(int argc, char *argv[])
|
||||
}
|
||||
else
|
||||
dp->disorder = true;
|
||||
save_default_ttl();
|
||||
break;
|
||||
case 28: /* oob */
|
||||
if (optarg)
|
||||
@ -770,7 +885,7 @@ void parse_params(int argc, char *argv[])
|
||||
params.tamper = true;
|
||||
break;
|
||||
case 34: /* tlsrec */
|
||||
if (!parse_tlspos(optarg, &dp->tlsrec))
|
||||
if (!parse_split_pos(optarg, &dp->tlsrec) && !parse_tlspos(optarg, &dp->tlsrec))
|
||||
{
|
||||
DLOG_ERR("Invalid argument for tlsrec\n");
|
||||
exit_clean(1);
|
||||
@ -778,9 +893,11 @@ void parse_params(int argc, char *argv[])
|
||||
params.tamper = true;
|
||||
break;
|
||||
case 35: /* tlsrec-pos */
|
||||
if ((dp->tlsrec_pos = atoi(optarg))>0)
|
||||
dp->tlsrec = tlspos_pos;
|
||||
else
|
||||
// obsolete arg
|
||||
i = atoi(optarg);
|
||||
dp->tlsrec.marker = PM_ABS;
|
||||
dp->tlsrec.pos = (int16_t)i;
|
||||
if (!dp->tlsrec.pos || i!=dp->tlsrec.pos)
|
||||
{
|
||||
DLOG_ERR("Invalid argument for tlsrec-pos\n");
|
||||
exit_clean(1);
|
||||
@ -823,8 +940,6 @@ void parse_params(int argc, char *argv[])
|
||||
DLOG_ERR("gzipped auto hostlists are not supported\n");
|
||||
exit_clean(1);
|
||||
}
|
||||
if (params.droproot && chown(optarg, params.uid, -1))
|
||||
DLOG_ERR("could not chown %s. auto hostlist file may not be writable after privilege drop\n", optarg);
|
||||
}
|
||||
if (!(dp->hostlist_auto=RegisterHostlist(dp, false, optarg)))
|
||||
{
|
||||
@ -858,8 +973,6 @@ void parse_params(int argc, char *argv[])
|
||||
exit_clean(1);
|
||||
}
|
||||
fclose(F);
|
||||
if (params.droproot && chown(optarg, params.uid, -1))
|
||||
DLOG_ERR("could not chown %s. auto hostlist debug log may not be writable after privilege drop\n", optarg);
|
||||
strncpy(params.hostlist_auto_debuglog, optarg, sizeof(params.hostlist_auto_debuglog));
|
||||
params.hostlist_auto_debuglog[sizeof(params.hostlist_auto_debuglog) - 1] = '\0';
|
||||
}
|
||||
@ -881,8 +994,6 @@ void parse_params(int argc, char *argv[])
|
||||
fprintf(stderr, "cannot create %s\n", params.debug_logfile);
|
||||
exit_clean(1);
|
||||
}
|
||||
if (params.droproot && chown(params.debug_logfile, params.uid, -1))
|
||||
fprintf(stderr, "could not chown %s. log file may not be writable after privilege drop\n", params.debug_logfile);
|
||||
if (!params.debug) params.debug = 1;
|
||||
params.debug_target = LOG_TARGET_FILE;
|
||||
}
|
||||
@ -1054,7 +1165,7 @@ void parse_params(int argc, char *argv[])
|
||||
}
|
||||
params.tamper = true;
|
||||
break;
|
||||
|
||||
|
||||
#if defined(__FreeBSD__)
|
||||
case 62: /* enable-pf */
|
||||
params.pf_enable = true;
|
||||
@ -1117,16 +1228,21 @@ void parse_params(int argc, char *argv[])
|
||||
|
||||
DLOG_CONDUP("we have %d user defined desync profile(s) and default low priority profile 0\n",desync_profile_count);
|
||||
|
||||
save_default_ttl();
|
||||
if (params.debug_target == LOG_TARGET_FILE && params.droproot && chown(params.debug_logfile, params.uid, -1))
|
||||
fprintf(stderr, "could not chown %s. log file may not be writable after privilege drop\n", params.debug_logfile);
|
||||
if (params.droproot && *params.hostlist_auto_debuglog && chown(params.hostlist_auto_debuglog, params.uid, -1))
|
||||
DLOG_ERR("could not chown %s. auto hostlist debug log may not be writable after privilege drop\n", params.hostlist_auto_debuglog);
|
||||
LIST_FOREACH(dpl, ¶ms.desync_profiles, next)
|
||||
{
|
||||
dp = &dpl->dp;
|
||||
if (dp->split_tls==tlspos_none && dp->split_pos) dp->split_tls=tlspos_pos;
|
||||
if (dp->split_http_req==httpreqpos_none && dp->split_pos) dp->split_http_req=httpreqpos_pos;
|
||||
if (params.skip_nodelay && (dp->split_tls || dp->split_http_req || dp->split_pos))
|
||||
if (params.skip_nodelay && dp->split_count)
|
||||
{
|
||||
DLOG_ERR("Cannot split with --skip-nodelay\n");
|
||||
exit_clean(1);
|
||||
}
|
||||
if (params.droproot && dp->hostlist_auto && chown(dp->hostlist_auto->filename, params.uid, -1))
|
||||
DLOG_ERR("could not chown %s. auto hostlist file may not be writable after privilege drop\n", dp->hostlist_auto->filename);
|
||||
}
|
||||
|
||||
if (!LoadAllHostLists())
|
||||
@ -1143,6 +1259,9 @@ void parse_params(int argc, char *argv[])
|
||||
VPRINT("\nlists summary:\n");
|
||||
HostlistsDebug();
|
||||
IpsetsDebug();
|
||||
|
||||
VPRINT("\nsplits summary:\n");
|
||||
SplitDebug();
|
||||
VPRINT("\n");
|
||||
|
||||
#ifndef __OpenBSD__
|
||||
|
110
tpws/tpws_conn.c
110
tpws/tpws_conn.c
@ -24,6 +24,7 @@
|
||||
#include "helpers.h"
|
||||
#include "hostlist.h"
|
||||
|
||||
#define PKTDATA_MAXDUMP 32
|
||||
|
||||
// keep separate legs counter. counting every time thousands of legs can consume cpu
|
||||
static int legs_local, legs_remote;
|
||||
@ -92,26 +93,43 @@ static bool socks_send_rep_errno(uint8_t ver, int fd, int errn)
|
||||
return ver==5 ? socks5_send_rep_errno(fd,errn) : socks4_send_rep_errno(fd, errn);
|
||||
}
|
||||
|
||||
static void packet_debug(const uint8_t *data, size_t sz)
|
||||
{
|
||||
hexdump_limited_dlog(data, sz, PKTDATA_MAXDUMP); VPRINT("\n");
|
||||
}
|
||||
|
||||
|
||||
static bool cork(int fd, int enable)
|
||||
{
|
||||
#ifdef __linux__
|
||||
int e = errno;
|
||||
if (setsockopt(fd, SOL_TCP, TCP_CORK, &enable, sizeof(enable))<0)
|
||||
{
|
||||
DLOG_PERROR("setsockopt (TCP_CORK)");
|
||||
errno = e;
|
||||
return false;
|
||||
}
|
||||
errno = e;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
ssize_t send_with_ttl(int fd, const void *buf, size_t len, int flags, int ttl)
|
||||
{
|
||||
ssize_t wr;
|
||||
ssize_t wr;
|
||||
|
||||
if (ttl)
|
||||
if (!params.skip_nodelay)
|
||||
{
|
||||
int ttl_apply = ttl ? ttl : params.ttl_default;
|
||||
DBGPRINT("send_with_ttl %d fd=%d\n",ttl,fd);
|
||||
if (!set_ttl_hl(fd, ttl))
|
||||
if (!set_ttl_hl(fd, ttl_apply))
|
||||
//DLOG_ERR("could not set ttl %d to fd=%d\n",ttl,fd);
|
||||
DLOG_ERR("could not set ttl %d to fd=%d\n",ttl,fd);
|
||||
DLOG_ERR("could not set ttl %d to fd=%d\n",ttl_apply,fd);
|
||||
cork(fd,true);
|
||||
}
|
||||
wr = send(fd, buf, len, flags);
|
||||
if (ttl)
|
||||
{
|
||||
int e=errno;
|
||||
if (!set_ttl_hl(fd, params.ttl_default))
|
||||
DLOG_ERR("could not set ttl %d to fd=%d\n",params.ttl_default,fd);
|
||||
errno=e;
|
||||
}
|
||||
if (!params.skip_nodelay)
|
||||
cork(fd,false);
|
||||
return wr;
|
||||
}
|
||||
|
||||
@ -308,19 +326,18 @@ bool set_socket_buffers(int fd, int rcvbuf, int sndbuf)
|
||||
if (rcvbuf && setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(int)) <0)
|
||||
{
|
||||
DLOG_PERROR("setsockopt (SO_RCVBUF)");
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
if (sndbuf && setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(int)) <0)
|
||||
{
|
||||
DLOG_PERROR("setsockopt (SO_SNDBUF)");
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
dbgprint_socket_buffers(fd);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool proxy_remote_conn_ack(tproxy_conn_t *conn, int sock_err)
|
||||
{
|
||||
// if proxy mode acknowledge connection request
|
||||
@ -393,7 +410,10 @@ static int connect_remote(const struct sockaddr *remote_addr, int mss)
|
||||
return -1;
|
||||
}
|
||||
if (!set_socket_buffers(remote_fd, params.remote_rcvbuf, params.remote_sndbuf))
|
||||
{
|
||||
close(remote_fd);
|
||||
return -1;
|
||||
}
|
||||
if (!set_keepalive(remote_fd))
|
||||
{
|
||||
DLOG_PERROR("set_keepalive");
|
||||
@ -1068,9 +1088,9 @@ static bool in_tamper_out_range(tproxy_conn_t *conn)
|
||||
|
||||
}
|
||||
|
||||
static void tamper(tproxy_conn_t *conn, uint8_t *segment, size_t segment_buffer_size, size_t *segment_size, size_t *split_pos, uint8_t *split_flags)
|
||||
static void tamper(tproxy_conn_t *conn, uint8_t *segment, size_t segment_buffer_size, size_t *segment_size, size_t *multisplit_pos, int *multisplit_count, uint8_t *split_flags)
|
||||
{
|
||||
*split_pos=0;
|
||||
if (multisplit_count) *multisplit_count=0;
|
||||
if (params.tamper)
|
||||
{
|
||||
if (conn->remote)
|
||||
@ -1081,26 +1101,26 @@ static void tamper(tproxy_conn_t *conn, uint8_t *segment, size_t segment_buffer_
|
||||
else
|
||||
{
|
||||
if (in_tamper_out_range(conn))
|
||||
tamper_out(&conn->track,(struct sockaddr*)&conn->dest,segment,segment_buffer_size,segment_size,split_pos,split_flags);
|
||||
tamper_out(&conn->track,(struct sockaddr*)&conn->dest,segment,segment_buffer_size,segment_size,multisplit_pos,multisplit_count,split_flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// buffer must have at least one extra byte for OOB
|
||||
static ssize_t send_or_buffer_oob(send_buffer_t *sb, int fd, uint8_t *buf, size_t len, int ttl, bool oob, uint8_t oob_byte)
|
||||
static ssize_t send_oob(int fd, uint8_t *buf, size_t len, int ttl, bool oob, uint8_t oob_byte)
|
||||
{
|
||||
ssize_t wr;
|
||||
if (oob)
|
||||
{
|
||||
VPRINT("Sending OOB byte %02X\n", oob_byte);
|
||||
uint8_t oob_save;
|
||||
oob_save = buf[len];
|
||||
buf[len] = oob_byte;
|
||||
wr = send_or_buffer(sb, fd, buf, len+1, MSG_OOB, ttl);
|
||||
wr = send_with_ttl(fd, buf, len+1, MSG_OOB, ttl);
|
||||
buf[len] = oob_save;
|
||||
if (wr<0 && errno==EAGAIN) wr=0;
|
||||
}
|
||||
else
|
||||
wr = send_or_buffer(sb, fd, buf, len, 0, ttl);
|
||||
wr = send_with_ttl(fd, buf, len, 0, ttl);
|
||||
return wr;
|
||||
}
|
||||
|
||||
@ -1186,36 +1206,53 @@ static bool handle_epoll(tproxy_conn_t *conn, struct tailhead *conn_list, uint32
|
||||
#endif
|
||||
{
|
||||
// incoming data from local leg
|
||||
uint8_t buf[RD_BLOCK_SIZE + 5];
|
||||
uint8_t buf[RD_BLOCK_SIZE + 6];
|
||||
|
||||
rd = recv(conn->fd, buf, RD_BLOCK_SIZE, MSG_DONTWAIT);
|
||||
DBGPRINT("recv fd=%d rd=%zd err=%d\n",conn->fd, rd,errno);
|
||||
if (rd<0 && errno==EAGAIN) rd=0;
|
||||
if (rd>0)
|
||||
{
|
||||
size_t split_pos;
|
||||
size_t multisplit_pos[MAX_SPLITS];
|
||||
int multisplit_count;
|
||||
|
||||
uint8_t split_flags;
|
||||
|
||||
bs = rd;
|
||||
|
||||
// tamper needs to know stream position of the block start
|
||||
tamper(conn, buf, sizeof(buf), &bs, &split_pos, &split_flags);
|
||||
tamper(conn, buf, sizeof(buf), &bs, multisplit_pos, &multisplit_count, &split_flags);
|
||||
// increase after tamper
|
||||
conn->tnrd++;
|
||||
conn->trd+=rd;
|
||||
|
||||
if (split_pos && bs<sizeof(buf) && split_pos<sizeof(buf))
|
||||
if (multisplit_count)
|
||||
{
|
||||
VPRINT("Splitting at pos %zu%s\n", split_pos, (split_flags & SPLIT_FLAG_DISORDER) ? " with disorder" : "");
|
||||
|
||||
wr = send_or_buffer_oob(conn->partner->wr_buf, conn->partner->fd, buf, split_pos, !!(split_flags & SPLIT_FLAG_DISORDER), !!(split_flags & SPLIT_FLAG_OOB), conn->track.dp ? conn->track.dp->oob_byte : 0);
|
||||
DBGPRINT("send_or_buffer(1) fd=%d wr=%zd err=%d\n",conn->partner->fd,wr,errno);
|
||||
if (wr >= 0)
|
||||
ssize_t from,to,len;
|
||||
int i;
|
||||
bool bApplyDisorder, bApplyOOB;
|
||||
for (i=0,from=0;i<=multisplit_count;i++)
|
||||
{
|
||||
to = i==multisplit_count ? bs : multisplit_pos[i];
|
||||
|
||||
bApplyDisorder = !(i & 1) && i<multisplit_count && (split_flags & SPLIT_FLAG_DISORDER);
|
||||
bApplyOOB = i==0 && (split_flags & SPLIT_FLAG_OOB);
|
||||
len = to-from;
|
||||
VPRINT("Sending multisplit part %d %zd-%zd (len %zd)%s%s : ", i+1, from, to, len, bApplyDisorder ? " with disorder" : "", bApplyOOB ? " with OOB" : "");
|
||||
packet_debug(buf+from,len);
|
||||
wr = send_oob(conn->partner->fd, buf+from, len, bApplyDisorder, bApplyOOB, conn->track.dp ? conn->track.dp->oob_byte : 0);
|
||||
if (wr<0) break;
|
||||
conn->partner->twr += wr;
|
||||
wr = send_or_buffer(conn->partner->wr_buf + 1, conn->partner->fd, buf + split_pos, bs - split_pos, 0, 0);
|
||||
DBGPRINT("send_or_buffer(2) fd=%d wr=%zd err=%d\n",conn->partner->fd,wr,errno);
|
||||
if (wr>0) conn->partner->twr += wr;
|
||||
if (wr<len)
|
||||
{
|
||||
from+=wr;
|
||||
VPRINT("Cannot send part %d immediately. only %zd bytes were sent (%zd left in segment). cancelling split.\n", i+1, wr, bs-from);
|
||||
wr = send_or_buffer(conn->partner->wr_buf, conn->partner->fd, buf+from, bs-from, 0, 0);
|
||||
if (wr>0) conn->partner->twr += wr;
|
||||
break;
|
||||
}
|
||||
|
||||
from = to;
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -1279,7 +1316,7 @@ static bool read_all_and_buffer(tproxy_conn_t *conn, int buffer_number)
|
||||
DBGPRINT("read_all_and_buffer(%d) numbytes=%d\n",buffer_number,numbytes);
|
||||
if (numbytes>0)
|
||||
{
|
||||
if (send_buffer_create(conn->partner->wr_buf+buffer_number, NULL, numbytes, 5, 0, 0))
|
||||
if (send_buffer_create(conn->partner->wr_buf+buffer_number, NULL, numbytes, 6, 0, 0))
|
||||
{
|
||||
ssize_t rd = recv(conn->fd, conn->partner->wr_buf[buffer_number].data, numbytes, MSG_DONTWAIT);
|
||||
if (rd>0)
|
||||
@ -1289,10 +1326,7 @@ static bool read_all_and_buffer(tproxy_conn_t *conn, int buffer_number)
|
||||
|
||||
conn->partner->bFlowOut = true;
|
||||
|
||||
size_t split_pos;
|
||||
uint8_t split_flags;
|
||||
|
||||
tamper(conn, conn->partner->wr_buf[buffer_number].data, numbytes+5, &conn->partner->wr_buf[buffer_number].len, &split_pos, &split_flags);
|
||||
tamper(conn, conn->partner->wr_buf[buffer_number].data, numbytes+6, &conn->partner->wr_buf[buffer_number].len, NULL, NULL, NULL);
|
||||
|
||||
if (epoll_update_flow(conn->partner))
|
||||
return true;
|
||||
@ -1369,7 +1403,7 @@ static bool handle_resolve_pipe(tproxy_conn_t **conn, struct tailhead *conn_list
|
||||
else if (rd!=sizeof(void*))
|
||||
{
|
||||
// partial pointer read is FATAL. in any case it will cause pointer corruption and coredump
|
||||
DLOG_ERR("resolve_pipe not full read %zu\n",rd);
|
||||
DLOG_ERR("resolve_pipe not full read %zd\n",rd);
|
||||
exit(1000);
|
||||
}
|
||||
b = resolve_complete(ri, conn_list);
|
||||
|
Loading…
Reference in New Issue
Block a user