Compare commits

..

5 Commits

Author SHA1 Message Date
bol-van
03d51a3ccc blockcheck: remove obsolete mss-pf 2024-09-19 21:51:58 +03:00
bol-van
a2c0f81457 update bins 2024-09-19 21:24:58 +03:00
bol-van
6c426c59f9 nfqws,tpws: do not accept extra characters in port filters 2024-09-19 21:15:17 +03:00
bol-van
d4a7eef17e tpws: multi-strategy 2024-09-19 21:06:58 +03:00
bol-van
9684e647ab nfqws: show profile numbers in auto hostlist messages 2024-09-19 20:52:01 +03:00
37 changed files with 644 additions and 341 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1224,7 +1224,7 @@ tpws_check_domain_http_bypass_()
done done
else else
for mss in '' 88; do for mss in '' 88; do
s3=${mss:+--mss=$mss --mss-pf=$HTTPS_PORT} s3=${mss:+--mss=$mss}
for s2 in '' '--oob' '--disorder' '--oob --disorder'; do for s2 in '' '--oob' '--disorder' '--oob --disorder'; do
for pos in sni sniext; do for pos in sni sniext; do
s="--split-tls=$pos" s="--split-tls=$pos"

View File

@ -282,8 +282,8 @@ static void auto_hostlist_reset_fail_counter(struct desync_profile *dp, const ch
if (fail_counter) if (fail_counter)
{ {
HostFailPoolDel(&dp->hostlist_auto_fail_counters, fail_counter); HostFailPoolDel(&dp->hostlist_auto_fail_counters, fail_counter);
DLOG("auto hostlist : %s : fail counter reset. website is working.\n", hostname); DLOG("auto hostlist (profile %d) : %s : fail counter reset. website is working.\n", dp->n, hostname);
HOSTLIST_DEBUGLOG_APPEND("%s : fail counter reset. website is working.", hostname); HOSTLIST_DEBUGLOG_APPEND("%s : profile %d : fail counter reset. website is working.", hostname, dp->n);
} }
} }
} }
@ -331,19 +331,19 @@ static void auto_hostlist_failed(struct desync_profile *dp, const char *hostname
} }
} }
fail_counter->counter++; fail_counter->counter++;
DLOG("auto hostlist : %s : fail counter %d/%d\n", hostname, fail_counter->counter, dp->hostlist_auto_fail_threshold); DLOG("auto hostlist (profile %d) : %s : fail counter %d/%d\n", dp->n, hostname, fail_counter->counter, dp->hostlist_auto_fail_threshold);
HOSTLIST_DEBUGLOG_APPEND("%s : fail counter %d/%d", hostname, fail_counter->counter, dp->hostlist_auto_fail_threshold); HOSTLIST_DEBUGLOG_APPEND("%s : profile %d : fail counter %d/%d", hostname, dp->n, fail_counter->counter, dp->hostlist_auto_fail_threshold);
if (fail_counter->counter >= dp->hostlist_auto_fail_threshold) if (fail_counter->counter >= dp->hostlist_auto_fail_threshold)
{ {
DLOG("auto hostlist : fail threshold reached. about to add %s to auto hostlist\n", hostname); DLOG("auto hostlist (profile %d) : fail threshold reached. about to add %s to auto hostlist\n", dp->n, hostname);
HostFailPoolDel(&dp->hostlist_auto_fail_counters, fail_counter); HostFailPoolDel(&dp->hostlist_auto_fail_counters, fail_counter);
DLOG("auto hostlist : rechecking %s to avoid duplicates\n", hostname); DLOG("auto hostlist (profile %d) : rechecking %s to avoid duplicates\n", dp->n, hostname);
bool bExcluded=false; bool bExcluded=false;
if (!HostlistCheck(dp, hostname, &bExcluded) && !bExcluded) if (!HostlistCheck(dp, hostname, &bExcluded) && !bExcluded)
{ {
DLOG("auto hostlist : adding %s\n", hostname); DLOG("auto hostlist (profile %d) : adding %s to %s\n", dp->n, hostname, dp->hostlist_auto_filename);
HOSTLIST_DEBUGLOG_APPEND("%s : adding", hostname); HOSTLIST_DEBUGLOG_APPEND("%s : profile %d : adding to %s", hostname, dp->n, dp->hostlist_auto_filename);
if (!StrPoolAddStr(&dp->hostlist, hostname)) if (!StrPoolAddStr(&dp->hostlist, hostname))
{ {
fprintf(stderr, "StrPoolAddStr out of memory\n"); fprintf(stderr, "StrPoolAddStr out of memory\n");
@ -358,8 +358,8 @@ static void auto_hostlist_failed(struct desync_profile *dp, const char *hostname
} }
else else
{ {
DLOG("auto hostlist : NOT adding %s\n", hostname); DLOG("auto hostlist (profile %d) : NOT adding %s\n", dp->n, hostname);
HOSTLIST_DEBUGLOG_APPEND("%s : NOT adding, duplicate detected", hostname); HOSTLIST_DEBUGLOG_APPEND("%s : profile %d : NOT adding, duplicate detected", hostname, dp->n);
} }
} }
} }
@ -368,7 +368,7 @@ static void process_retrans_fail(t_ctrack *ctrack, uint8_t proto)
{ {
if (ctrack && ctrack->dp && ctrack->hostname && auto_hostlist_retrans(ctrack, proto, ctrack->dp->hostlist_auto_retrans_threshold)) if (ctrack && ctrack->dp && ctrack->hostname && auto_hostlist_retrans(ctrack, proto, ctrack->dp->hostlist_auto_retrans_threshold))
{ {
HOSTLIST_DEBUGLOG_APPEND("%s : tcp retrans threshold reached", ctrack->hostname); HOSTLIST_DEBUGLOG_APPEND("%s : profile %d : tcp retrans threshold reached", ctrack->hostname, ctrack->dp->n);
auto_hostlist_failed(ctrack->dp, ctrack->hostname); auto_hostlist_failed(ctrack->dp, ctrack->hostname);
} }
} }
@ -683,7 +683,7 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint
if (tcphdr->th_flags & TH_RST) if (tcphdr->th_flags & TH_RST)
{ {
DLOG("incoming RST detected for hostname %s\n", ctrack->hostname); DLOG("incoming RST detected for hostname %s\n", ctrack->hostname);
HOSTLIST_DEBUGLOG_APPEND("%s : incoming RST", ctrack->hostname); HOSTLIST_DEBUGLOG_APPEND("%s : profile %d : incoming RST", ctrack->hostname, ctrack->dp->n);
bFail = true; bFail = true;
} }
else if (len_payload && ctrack->l7proto==HTTP) else if (len_payload && ctrack->l7proto==HTTP)
@ -695,7 +695,7 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint
if (bFail) if (bFail)
{ {
DLOG("redirect to another domain detected. possibly DPI redirect.\n"); DLOG("redirect to another domain detected. possibly DPI redirect.\n");
HOSTLIST_DEBUGLOG_APPEND("%s : redirect to another domain", ctrack->hostname); HOSTLIST_DEBUGLOG_APPEND("%s : profile %d : redirect to another domain", ctrack->hostname, ctrack->dp->n);
} }
else else
DLOG("local or in-domain redirect detected. it's not a DPI redirect.\n"); DLOG("local or in-domain redirect detected. it's not a DPI redirect.\n");

View File

@ -319,6 +319,7 @@ bool pf_in_range(uint16_t port, const port_filter *pf)
bool pf_parse(const char *s, port_filter *pf) bool pf_parse(const char *s, port_filter *pf)
{ {
unsigned int v1,v2; unsigned int v1,v2;
char c;
if (!s) return false; if (!s) return false;
if (*s=='~') if (*s=='~')
@ -328,13 +329,13 @@ bool pf_parse(const char *s, port_filter *pf)
} }
else else
pf->neg=false; pf->neg=false;
if (sscanf(s,"%u-%u",&v1,&v2)==2) if (sscanf(s,"%u-%u%c",&v1,&v2,&c)==2)
{ {
if (v1>65535 || v2>65535 || v1>v2) return false; if (v1>65535 || v2>65535 || v1>v2) return false;
pf->from=(uint16_t)v1; pf->from=(uint16_t)v1;
pf->to=(uint16_t)v2; pf->to=(uint16_t)v2;
} }
else if (sscanf(s,"%u",&v1)==1) else if (sscanf(s,"%u%c",&v1,&c)==1)
{ {
if (v1>65535) return false; if (v1>65535) return false;
pf->to=pf->from=(uint16_t)v1; pf->to=pf->from=(uint16_t)v1;

View File

@ -164,6 +164,21 @@ bool saconvmapped(struct sockaddr_storage *a)
return false; return false;
} }
void sacopy(struct sockaddr_storage *sa_dest, const struct sockaddr *sa)
{
switch(sa->sa_family)
{
case AF_INET:
memcpy(sa_dest,sa,sizeof(struct sockaddr_in));
break;
case AF_INET6:
memcpy(sa_dest,sa,sizeof(struct sockaddr_in6));
break;
default:
sa_dest->ss_family = 0;
}
}
bool is_localnet(const struct sockaddr *a) bool is_localnet(const struct sockaddr *a)
{ {
// match 127.0.0.0/8, 0.0.0.0, ::1, ::0, :ffff:127.0.0.0/104, :ffff:0.0.0.0 // match 127.0.0.0/8, 0.0.0.0, ::1, ::0, :ffff:127.0.0.0/104, :ffff:0.0.0.0
@ -241,27 +256,34 @@ bool pf_in_range(uint16_t port, const port_filter *pf)
bool pf_parse(const char *s, port_filter *pf) bool pf_parse(const char *s, port_filter *pf)
{ {
unsigned int v1,v2; unsigned int v1,v2;
char c;
if (!s) return false; if (!s) return false;
if (*s=='~') if (*s=='~')
{ {
pf->neg=true; pf->neg=true;
s++; s++;
} }
else else
pf->neg=false; pf->neg=false;
if (sscanf(s,"%u-%u",&v1,&v2)==2) if (sscanf(s,"%u-%u%c",&v1,&v2,&c)==2)
{ {
if (!v1 || v1>65535 || v2>65535 || v1>v2) return false; if (v1>65535 || v2>65535 || v1>v2) return false;
pf->from=(uint16_t)v1; pf->from=(uint16_t)v1;
pf->to=(uint16_t)v2; pf->to=(uint16_t)v2;
} }
else if (sscanf(s,"%u",&v1)==1) else if (sscanf(s,"%u%c",&v1,&c)==1)
{ {
if (!v1 || v1>65535) return false; if (v1>65535) return false;
pf->to=pf->from=(uint16_t)v1; pf->to=pf->from=(uint16_t)v1;
} }
else else
return false; return false;
// deny all case
if (!pf->from && !pf->to) pf->neg=true;
return true; return true;
} }
bool pf_is_empty(const port_filter *pf)
{
return !pf->neg && !pf->from && !pf->to;
}

View File

@ -25,6 +25,8 @@ uint16_t saport(const struct sockaddr *sa);
// true = was converted // true = was converted
bool saconvmapped(struct sockaddr_storage *a); bool saconvmapped(struct sockaddr_storage *a);
void sacopy(struct sockaddr_storage *sa_dest, const struct sockaddr *sa);
bool is_localnet(const struct sockaddr *a); bool is_localnet(const struct sockaddr *a);
bool is_linklocal(const struct sockaddr_in6* a); bool is_linklocal(const struct sockaddr_in6* a);
bool is_private6(const struct sockaddr_in6* a); bool is_private6(const struct sockaddr_in6* a);
@ -55,6 +57,7 @@ typedef struct
} port_filter; } port_filter;
bool pf_in_range(uint16_t port, const port_filter *pf); bool pf_in_range(uint16_t port, const port_filter *pf);
bool pf_parse(const char *s, port_filter *pf); bool pf_parse(const char *s, port_filter *pf);
bool pf_is_empty(const port_filter *pf);
#ifndef IN_LOOPBACK #ifndef IN_LOOPBACK
#define IN_LOOPBACK(a) ((((uint32_t) (a)) & 0xff000000) == 0x7f000000) #define IN_LOOPBACK(a) ((((uint32_t) (a)) & 0xff000000) == 0x7f000000)

View File

@ -154,35 +154,53 @@ static bool HostlistCheck_(strpool *hostlist, strpool *hostlist_exclude, const c
return true; return true;
} }
// return : true = apply fooling, false = do not apply static bool LoadIncludeHostListsForProfile(struct desync_profile *dp)
bool HostlistCheck(const char *host, bool *excluded)
{ {
if (*params.hostlist_auto_filename) if (!LoadHostLists(&dp->hostlist, &dp->hostlist_files))
return false;
if (*dp->hostlist_auto_filename)
{ {
time_t t = file_mod_time(params.hostlist_auto_filename); dp->hostlist_auto_mod_time = file_mod_time(dp->hostlist_auto_filename);
if (t!=params.hostlist_auto_mod_time) NonEmptyHostlist(&dp->hostlist);
}
return true;
}
// return : true = apply fooling, false = do not apply
bool HostlistCheck(struct desync_profile *dp, const char *host, bool *excluded)
{
VPRINT("* Hostlist check for profile %d\n",dp->n);
if (*dp->hostlist_auto_filename)
{
time_t t = file_mod_time(dp->hostlist_auto_filename);
if (t!=dp->hostlist_auto_mod_time)
{ {
DLOG_CONDUP("Autohostlist was modified by another process. Reloading include hostslist.\n"); DLOG_CONDUP("Autohostlist '%s' from profile %d was modified. Reloading include hostlists for this profile.\n",dp->hostlist_auto_filename, dp->n);
if (!LoadIncludeHostLists()) if (!LoadIncludeHostListsForProfile(dp))
{ {
// what will we do without hostlist ?? sure, gonna die // what will we do without hostlist ?? sure, gonna die
exit(1); exit(1);
} }
params.hostlist_auto_mod_time = t; dp->hostlist_auto_mod_time = t;
NonEmptyHostlist(&dp->hostlist);
} }
} }
return HostlistCheck_(params.hostlist, params.hostlist_exclude, host, excluded); return HostlistCheck_(dp->hostlist, dp->hostlist_exclude, host, excluded);
} }
bool LoadIncludeHostLists() bool LoadIncludeHostLists()
{ {
if (!LoadHostLists(&params.hostlist, &params.hostlist_files)) struct desync_profile_list *dpl;
return false; LIST_FOREACH(dpl, &params.desync_profiles, next)
if (*params.hostlist_auto_filename) if (!LoadIncludeHostListsForProfile(&dpl->dp))
params.hostlist_auto_mod_time = file_mod_time(params.hostlist_auto_filename); return false;
return true; return true;
} }
bool LoadExcludeHostLists() bool LoadExcludeHostLists()
{ {
return LoadHostLists(&params.hostlist_exclude, &params.hostlist_exclude_files); struct desync_profile_list *dpl;
LIST_FOREACH(dpl, &params.desync_profiles, next)
if (!LoadHostLists(&dpl->dp.hostlist_exclude, &dpl->dp.hostlist_exclude_files))
return false;
return true;
} }

View File

@ -2,6 +2,7 @@
#include <stdbool.h> #include <stdbool.h>
#include "pools.h" #include "pools.h"
#include "params.h"
bool AppendHostList(strpool **hostlist, char *filename); bool AppendHostList(strpool **hostlist, char *filename);
bool LoadHostLists(strpool **hostlist, struct str_list_head *file_list); bool LoadHostLists(strpool **hostlist, struct str_list_head *file_list);
@ -10,4 +11,4 @@ bool LoadExcludeHostLists();
bool NonEmptyHostlist(strpool **hostlist); bool NonEmptyHostlist(strpool **hostlist);
bool SearchHostList(strpool *hostlist, const char *host); bool SearchHostList(strpool *hostlist, const char *host);
// return : true = apply fooling, false = do not apply // return : true = apply fooling, false = do not apply
bool HostlistCheck(const char *host, bool *excluded); bool HostlistCheck(struct desync_profile *dp,const char *host, bool *excluded);

View File

@ -138,3 +138,56 @@ int HOSTLIST_DEBUGLOG_APPEND(const char *format, ...)
else else
return 0; return 0;
} }
struct desync_profile_list *dp_list_add(struct desync_profile_list_head *head)
{
struct desync_profile_list *entry = calloc(1,sizeof(struct desync_profile_list));
if (!entry) return NULL;
LIST_INIT(&entry->dp.hostlist_files);
LIST_INIT(&entry->dp.hostlist_exclude_files);
entry->dp.filter_ipv4 = entry->dp.filter_ipv6 = true;
memcpy(entry->dp.hostspell, "host", 4); // default hostspell
entry->dp.hostlist_auto_fail_threshold = HOSTLIST_AUTO_FAIL_THRESHOLD_DEFAULT;
entry->dp.hostlist_auto_fail_time = HOSTLIST_AUTO_FAIL_TIME_DEFAULT;
// add to the tail
struct desync_profile_list *dpn,*dpl=LIST_FIRST(&params.desync_profiles);
if (dpl)
{
while ((dpn=LIST_NEXT(dpl,next))) dpl = dpn;
LIST_INSERT_AFTER(dpl, entry, next);
}
else
LIST_INSERT_HEAD(&params.desync_profiles, entry, next);
return entry;
}
static void dp_entry_destroy(struct desync_profile_list *entry)
{
strlist_destroy(&entry->dp.hostlist_files);
strlist_destroy(&entry->dp.hostlist_exclude_files);
StrPoolDestroy(&entry->dp.hostlist_exclude);
StrPoolDestroy(&entry->dp.hostlist);
HostFailPoolDestroy(&entry->dp.hostlist_auto_fail_counters);
free(entry);
}
void dp_list_destroy(struct desync_profile_list_head *head)
{
struct desync_profile_list *entry;
while ((entry = LIST_FIRST(head)))
{
LIST_REMOVE(entry, next);
dp_entry_destroy(entry);
}
}
bool dp_list_have_autohostlist(struct desync_profile_list_head *head)
{
struct desync_profile_list *dpl;
LIST_FOREACH(dpl, head, next)
if (*dpl->dp.hostlist_auto_filename)
return true;
return false;
}

View File

@ -27,27 +27,10 @@ struct bind_s
enum log_target { LOG_TARGET_CONSOLE=0, LOG_TARGET_FILE, LOG_TARGET_SYSLOG }; enum log_target { LOG_TARGET_CONSOLE=0, LOG_TARGET_FILE, LOG_TARGET_SYSLOG };
struct params_s struct desync_profile
{ {
struct bind_s binds[MAX_BINDS]; int n; // number of the profile
int binds_last;
bool bind_wait_only;
uint16_t port;
uint8_t proxy_type;
bool no_resolve;
bool skip_nodelay;
bool droproot;
uid_t uid;
gid_t gid;
bool daemon;
int maxconn,resolver_threads,maxfiles,max_orphan_time;
int local_rcvbuf,local_sndbuf,remote_rcvbuf,remote_sndbuf;
#if defined(__linux__) || defined(__APPLE__)
int tcp_user_timeout_local,tcp_user_timeout_remote;
#endif
bool tamper; // any tamper option is set
bool hostcase, hostdot, hosttab, hostnospace, methodspace, methodeol, unixeol, domcase; bool hostcase, hostdot, hosttab, hostnospace, methodspace, methodeol, unixeol, domcase;
int hostpad; int hostpad;
char hostspell[4]; char hostspell[4];
@ -60,30 +43,59 @@ struct params_s
bool disorder, disorder_http, disorder_tls; bool disorder, disorder_http, disorder_tls;
bool oob, oob_http, oob_tls; bool oob, oob_http, oob_tls;
uint8_t oob_byte; uint8_t oob_byte;
int ttl_default;
int mss; int mss;
port_filter mss_pf;
char pidfile[256];
strpool *hostlist, *hostlist_exclude;
struct str_list_head hostlist_files, hostlist_exclude_files;
char hostlist_auto_filename[PATH_MAX], hostlist_auto_debuglog[PATH_MAX];
int hostlist_auto_fail_threshold, hostlist_auto_fail_time;
time_t hostlist_auto_mod_time;
hostfail_pool *hostlist_auto_fail_counters;
bool tamper_start_n,tamper_cutoff_n; bool tamper_start_n,tamper_cutoff_n;
unsigned int tamper_start,tamper_cutoff; unsigned int tamper_start,tamper_cutoff;
bool filter_ipv4,filter_ipv6;
port_filter pf_tcp;
strpool *hostlist, *hostlist_exclude;
struct str_list_head hostlist_files, hostlist_exclude_files;
char hostlist_auto_filename[PATH_MAX];
int hostlist_auto_fail_threshold, hostlist_auto_fail_time;
time_t hostlist_auto_mod_time;
hostfail_pool *hostlist_auto_fail_counters;
};
struct desync_profile_list {
struct desync_profile dp;
LIST_ENTRY(desync_profile_list) next;
};
LIST_HEAD(desync_profile_list_head, desync_profile_list);
struct desync_profile_list *dp_list_add(struct desync_profile_list_head *head);
void dp_list_destroy(struct desync_profile_list_head *head);
bool dp_list_have_autohostlist(struct desync_profile_list_head *head);
struct params_s
{
int debug;
enum log_target debug_target;
char debug_logfile[PATH_MAX];
struct bind_s binds[MAX_BINDS];
int binds_last;
bool bind_wait_only;
uint16_t port;
struct sockaddr_in connect_bind4; struct sockaddr_in connect_bind4;
struct sockaddr_in6 connect_bind6; struct sockaddr_in6 connect_bind6;
char connect_bind6_ifname[IF_NAMESIZE]; char connect_bind6_ifname[IF_NAMESIZE];
int debug; uint8_t proxy_type;
enum log_target debug_target; bool no_resolve;
char debug_logfile[PATH_MAX]; bool skip_nodelay;
bool droproot;
uid_t uid;
gid_t gid;
bool daemon;
char pidfile[256];
int maxconn,resolver_threads,maxfiles,max_orphan_time;
int local_rcvbuf,local_sndbuf,remote_rcvbuf,remote_sndbuf;
#if defined(__linux__) || defined(__APPLE__)
int tcp_user_timeout_local,tcp_user_timeout_remote;
#endif
#if defined(BSD) #if defined(BSD)
bool pf_enable; bool pf_enable;
@ -91,6 +103,13 @@ struct params_s
#ifdef SPLICE_PRESENT #ifdef SPLICE_PRESENT
bool nosplice; bool nosplice;
#endif #endif
int ttl_default;
char hostlist_auto_debuglog[PATH_MAX];
bool tamper; // any tamper option is set
bool tamper_lim; // tamper-start or tamper-cutoff set in any profile
struct desync_profile_list_head desync_profiles;
}; };
extern struct params_s params; extern struct params_s params;

View File

@ -25,12 +25,45 @@ bool IsHttp(const uint8_t *data, size_t len)
{ {
return !!HttpMethod(data,len); return !!HttpMethod(data,len);
} }
static bool IsHostAt(const uint8_t *p)
{
return \
p[0]=='\n' &&
(p[1]=='H' || p[1]=='h') &&
(p[2]=='o' || p[2]=='O') &&
(p[3]=='s' || p[3]=='S') &&
(p[4]=='t' || p[4]=='T') &&
p[5]==':';
}
static uint8_t *FindHostIn(uint8_t *buf, size_t bs)
{
size_t pos;
if (bs<6) return NULL;
bs-=6;
for(pos=0;pos<=bs;pos++)
if (IsHostAt(buf+pos))
return buf+pos;
return NULL;
}
static const uint8_t *FindHostInConst(const uint8_t *buf, size_t bs)
{
size_t pos;
if (bs<6) return NULL;
bs-=6;
for(pos=0;pos<=bs;pos++)
if (IsHostAt(buf+pos))
return buf+pos;
return NULL;
}
// pHost points to "Host: ..." // pHost points to "Host: ..."
bool HttpFindHost(uint8_t **pHost,uint8_t *buf,size_t bs) bool HttpFindHost(uint8_t **pHost,uint8_t *buf,size_t bs)
{ {
if (!*pHost) if (!*pHost)
{ {
*pHost = memmem(buf, bs, "\nHost:", 6); *pHost = FindHostIn(buf, bs);
if (*pHost) (*pHost)++; if (*pHost) (*pHost)++;
} }
return !!*pHost; return !!*pHost;
@ -39,7 +72,7 @@ bool HttpFindHostConst(const uint8_t **pHost,const uint8_t *buf,size_t bs)
{ {
if (!*pHost) if (!*pHost)
{ {
*pHost = memmem(buf, bs, "\nHost:", 6); *pHost = FindHostInConst(buf, bs);
if (*pHost) (*pHost)++; if (*pHost) (*pHost)++;
} }
return !!*pHost; return !!*pHost;
@ -132,7 +165,7 @@ bool HttpReplyLooksLikeDPIRedirect(const uint8_t *data, size_t len, const char *
} }
size_t HttpPos(enum httpreqpos tpos_type, size_t hpos_pos, const uint8_t *http, size_t sz) size_t HttpPos(enum httpreqpos tpos_type, size_t hpos_pos, const uint8_t *http, size_t sz)
{ {
const uint8_t *method, *host; const uint8_t *method, *host=NULL;
int i; int i;
switch(tpos_type) switch(tpos_type)

View File

@ -1,24 +1,87 @@
#define _GNU_SOURCE #define _GNU_SOURCE
#include "tamper.h" #include "tamper.h"
#include "params.h"
#include "hostlist.h" #include "hostlist.h"
#include "protocol.h" #include "protocol.h"
#include "helpers.h" #include "helpers.h"
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
static bool dp_match_l3l4(struct desync_profile *dp, bool ipv6, uint16_t tcp_port)
{
return \
((!ipv6 && dp->filter_ipv4) || (ipv6 && dp->filter_ipv6)) &&
(!tcp_port || pf_in_range(tcp_port,&dp->pf_tcp));
}
static bool dp_match(struct desync_profile *dp, bool ipv6, uint16_t tcp_port, const char *hostname)
{
if (dp_match_l3l4(dp,ipv6,tcp_port))
{
// autohostlist profile matching l3/l4 filter always win
if (*dp->hostlist_auto_filename) return true;
if (dp->hostlist || dp->hostlist_exclude)
{
// without known hostname first profile matching l3/l4 filter and without hostlist filter wins
if (hostname)
return HostlistCheck(dp, hostname, NULL);
}
else
// profile without hostlist filter wins
return true;
}
return false;
}
static struct desync_profile *dp_find(struct desync_profile_list_head *head, bool ipv6, uint16_t tcp_port, const char *hostname)
{
struct desync_profile_list *dpl;
VPRINT("desync profile search for hostname='%s' ipv6=%u tcp_port=%u\n", hostname ? hostname : "", ipv6, tcp_port);
LIST_FOREACH(dpl, head, next)
{
if (dp_match(&dpl->dp,ipv6,tcp_port,hostname))
{
VPRINT("desync profile %d matches\n",dpl->dp.n);
return &dpl->dp;
}
}
VPRINT("desync profile not found\n");
return NULL;
}
void apply_desync_profile(t_ctrack *ctrack, const struct sockaddr *dest)
{
ctrack->dp = dp_find(&params.desync_profiles, dest->sa_family==AF_INET6, saport(dest), ctrack->hostname);
}
// segment buffer has at least 5 extra bytes to extend data block // segment buffer has at least 5 extra bytes to extend data block
void tamper_out(t_ctrack *ctrack, 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 *split_pos, uint8_t *split_flags)
{ {
uint8_t *p, *pp, *pHost = NULL; uint8_t *p, *pp, *pHost = NULL;
size_t method_len = 0, pos; size_t method_len = 0, pos;
size_t tpos, spos;
const char *method; const char *method;
bool bBypass = false, bHaveHost = false, bHostExcluded = false; bool bHaveHost = false;
char *pc, Host[256]; char *pc, Host[256];
t_l7proto l7proto;
DBGPRINT("tamper_out\n"); DBGPRINT("tamper_out\n");
if (params.debug)
{
char ip_port[48];
ntop46_port(dest,ip_port,sizeof(ip_port));
VPRINT("tampering tcp segment with size %zu to %s\n", *size, ip_port);
if (ctrack->dp) VPRINT("using cached desync profile %d\n",ctrack->dp->n);
if (ctrack->hostname) VPRINT("connection hostname: %s\n", ctrack->hostname);
}
if (dest->sa_family!=AF_INET && dest->sa_family!=AF_INET6)
{
DLOG_ERR("tamper_out dest family unknown\n");
return;
}
*split_pos=0; *split_pos=0;
*split_flags=0; *split_flags=0;
@ -26,9 +89,8 @@ void tamper_out(t_ctrack *ctrack, uint8_t *segment,size_t segment_buffer_size,si
{ {
method_len = strlen(method)-2; method_len = strlen(method)-2;
VPRINT("Data block looks like http request start : %s\n", method); VPRINT("Data block looks like http request start : %s\n", method);
if (!ctrack->l7proto) ctrack->l7proto=HTTP; l7proto=HTTP;
// cpu saving : we search host only if and when required. we do not research host every time we need its position if (HttpFindHost(&pHost,segment,*size))
if ((params.hostlist || params.hostlist_exclude) && HttpFindHost(&pHost,segment,*size))
{ {
p = pHost + 5; p = pHost + 5;
while (p < (segment + *size) && (*p == ' ' || *p == '\t')) p++; while (p < (segment + *size) && (*p == ' ' || *p == '\t')) p++;
@ -37,13 +99,57 @@ void tamper_out(t_ctrack *ctrack, uint8_t *segment,size_t segment_buffer_size,si
memcpy(Host, p, pp - p); memcpy(Host, p, pp - p);
Host[pp - p] = '\0'; Host[pp - p] = '\0';
bHaveHost = true; bHaveHost = true;
VPRINT("Requested Host is : %s\n", Host);
for(pc = Host; *pc; pc++) *pc=tolower(*pc); for(pc = Host; *pc; pc++) *pc=tolower(*pc);
bBypass = !HostlistCheck(Host, &bHostExcluded);
} }
if (!bBypass) }
else if (IsTLSClientHello(segment,*size,false))
{
VPRINT("Data block contains TLS ClientHello\n");
l7proto=TLS;
bHaveHost=TLSHelloExtractHost((uint8_t*)segment,*size,Host,sizeof(Host),false);
}
else
{
VPRINT("Data block contains unknown payload\n");
l7proto = UNKNOWN;
}
if (ctrack->l7proto==UNKNOWN) ctrack->l7proto=l7proto;
if (bHaveHost)
{
VPRINT("request hostname: %s\n", Host);
if (!ctrack->hostname)
{ {
if (params.unixeol) if (!(ctrack->hostname=strdup(Host)))
{
DLOG_ERR("strdup hostname : out of memory\n");
return;
}
struct desync_profile *dp_prev = ctrack->dp;
apply_desync_profile(ctrack, dest);
if (ctrack->dp!=dp_prev)
VPRINT("desync profile changed by revealed hostname !\n");
else if (*ctrack->dp->hostlist_auto_filename)
{
bool bHostExcluded;
if (!HostlistCheck(ctrack->dp, Host, &bHostExcluded))
{
ctrack->b_ah_check = !bHostExcluded;
VPRINT("Not acting on this request\n");
return;
}
}
}
}
if (!ctrack->dp) return;
switch(l7proto)
{
case HTTP:
if (ctrack->dp->unixeol)
{ {
p = pp = segment; p = pp = segment;
while ((p = memmem(p, segment + *size - p, "\r\n", 2))) while ((p = memmem(p, segment + *size - p, "\r\n", 2)))
@ -61,10 +167,10 @@ void tamper_out(t_ctrack *ctrack, uint8_t *segment,size_t segment_buffer_size,si
} }
pHost = NULL; // invalidate pHost = NULL; // invalidate
} }
if (params.methodeol && (*size+1+!params.unixeol)<=segment_buffer_size) if (ctrack->dp->methodeol && (*size+1+!ctrack->dp->unixeol)<=segment_buffer_size)
{ {
VPRINT("Adding EOL before method\n"); VPRINT("Adding EOL before method\n");
if (params.unixeol) if (ctrack->dp->unixeol)
{ {
memmove(segment + 1, segment, *size); memmove(segment + 1, segment, *size);
(*size)++;; (*size)++;;
@ -79,7 +185,7 @@ void tamper_out(t_ctrack *ctrack, uint8_t *segment,size_t segment_buffer_size,si
} }
pHost = NULL; // invalidate pHost = NULL; // invalidate
} }
if (params.methodspace && *size<segment_buffer_size) if (ctrack->dp->methodspace && *size<segment_buffer_size)
{ {
// we only work with data blocks looking as HTTP query, so method is at the beginning // we only work with data blocks looking as HTTP query, so method is at the beginning
VPRINT("Adding extra space after method\n"); VPRINT("Adding extra space after method\n");
@ -90,20 +196,20 @@ void tamper_out(t_ctrack *ctrack, uint8_t *segment,size_t segment_buffer_size,si
(*size)++; // block will grow by 1 byte (*size)++; // block will grow by 1 byte
if (pHost) pHost++; // Host: position will move by 1 byte if (pHost) pHost++; // Host: position will move by 1 byte
} }
if ((params.hostdot || params.hosttab) && *size<segment_buffer_size && HttpFindHost(&pHost,segment,*size)) if ((ctrack->dp->hostdot || ctrack->dp->hosttab) && *size<segment_buffer_size && HttpFindHost(&pHost,segment,*size))
{ {
p = pHost + 5; p = pHost + 5;
while (p < (segment + *size) && *p != '\r' && *p != '\n') p++; while (p < (segment + *size) && *p != '\r' && *p != '\n') p++;
if (p < (segment + *size)) if (p < (segment + *size))
{ {
pos = p - segment; pos = p - segment;
VPRINT("Adding %s to host name at pos %zu\n", params.hostdot ? "dot" : "tab", pos); VPRINT("Adding %s to host name at pos %zu\n", ctrack->dp->hostdot ? "dot" : "tab", pos);
memmove(p + 1, p, *size - pos); memmove(p + 1, p, *size - pos);
*p = params.hostdot ? '.' : '\t'; // insert dot or tab *p = ctrack->dp->hostdot ? '.' : '\t'; // insert dot or tab
(*size)++; // block will grow by 1 byte (*size)++; // block will grow by 1 byte
} }
} }
if (params.domcase && HttpFindHost(&pHost,segment,*size)) if (ctrack->dp->domcase && HttpFindHost(&pHost,segment,*size))
{ {
p = pHost + 5; p = pHost + 5;
pos = p - segment; pos = p - segment;
@ -111,7 +217,7 @@ void tamper_out(t_ctrack *ctrack, uint8_t *segment,size_t segment_buffer_size,si
for (; p < (segment + *size) && *p != '\r' && *p != '\n'; p++) for (; p < (segment + *size) && *p != '\r' && *p != '\n'; p++)
*p = (((size_t)p) & 1) ? tolower(*p) : toupper(*p); *p = (((size_t)p) & 1) ? tolower(*p) : toupper(*p);
} }
if (params.hostnospace && HttpFindHost(&pHost,segment,*size) && (pHost+5)<(segment+*size) && pHost[5] == ' ') if (ctrack->dp->hostnospace && HttpFindHost(&pHost,segment,*size) && (pHost+5)<(segment+*size) && pHost[5] == ' ')
{ {
p = pHost + 6; p = pHost + 6;
pos = p - segment; pos = p - segment;
@ -119,17 +225,17 @@ void tamper_out(t_ctrack *ctrack, uint8_t *segment,size_t segment_buffer_size,si
memmove(p - 1, p, *size - pos); memmove(p - 1, p, *size - pos);
(*size)--; // block will shrink by 1 byte (*size)--; // block will shrink by 1 byte
} }
if (params.hostcase && HttpFindHost(&pHost,segment,*size)) if (ctrack->dp->hostcase && HttpFindHost(&pHost,segment,*size))
{ {
VPRINT("Changing 'Host:' => '%c%c%c%c:' at pos %td\n", params.hostspell[0], params.hostspell[1], params.hostspell[2], params.hostspell[3], pHost - segment); VPRINT("Changing 'Host:' => '%c%c%c%c:' at pos %td\n", ctrack->dp->hostspell[0], ctrack->dp->hostspell[1], ctrack->dp->hostspell[2], ctrack->dp->hostspell[3], pHost - segment);
memcpy(pHost, params.hostspell, 4); memcpy(pHost, ctrack->dp->hostspell, 4);
} }
if (params.hostpad && HttpFindHost(&pHost,segment,*size)) if (ctrack->dp->hostpad && HttpFindHost(&pHost,segment,*size))
{ {
// add : XXXXX: <padding?[\r\n|\n] // add : XXXXX: <padding?[\r\n|\n]
char s[8]; char s[8];
size_t hsize = params.unixeol ? 8 : 9; size_t hsize = ctrack->dp->unixeol ? 8 : 9;
size_t hostpad = params.hostpad<hsize ? hsize : params.hostpad; size_t hostpad = ctrack->dp->hostpad<hsize ? hsize : ctrack->dp->hostpad;
if ((hsize+*size)>segment_buffer_size) if ((hsize+*size)>segment_buffer_size)
VPRINT("could not add host padding : buffer too small\n"); VPRINT("could not add host padding : buffer too small\n");
@ -159,7 +265,7 @@ void tamper_out(t_ctrack *ctrack, uint8_t *segment,size_t segment_buffer_size,si
p+=7; p+=7;
memset(p,'a'+rand()%('z'-'a'+1),padsize); memset(p,'a'+rand()%('z'-'a'+1),padsize);
p+=padsize; p+=padsize;
if (params.unixeol) if (ctrack->dp->unixeol)
*p++='\n'; *p++='\n';
else else
{ {
@ -171,40 +277,16 @@ void tamper_out(t_ctrack *ctrack, uint8_t *segment,size_t segment_buffer_size,si
pHost = NULL; // invalidate pHost = NULL; // invalidate
} }
} }
*split_pos = HttpPos(params.split_http_req, params.split_pos, segment, *size); *split_pos = HttpPos(ctrack->dp->split_http_req, ctrack->dp->split_pos, segment, *size);
if (params.disorder_http) *split_flags |= SPLIT_FLAG_DISORDER; if (ctrack->dp->disorder_http) *split_flags |= SPLIT_FLAG_DISORDER;
if (params.oob_http) *split_flags |= SPLIT_FLAG_OOB; if (ctrack->dp->oob_http) *split_flags |= SPLIT_FLAG_OOB;
} break;
else
{
VPRINT("Not acting on this request\n");
}
}
else if (IsTLSClientHello(segment,*size,false))
{
size_t tpos=0,spos=0;
if (!ctrack->l7proto) ctrack->l7proto=TLS;
VPRINT("packet contains TLS ClientHello\n");
// we need host only if hostlist is present
if ((params.hostlist || params.hostlist_exclude) && TLSHelloExtractHost((uint8_t*)segment,*size,Host,sizeof(Host),false))
{
VPRINT("hostname: %s\n",Host);
bHaveHost = true;
bBypass = !HostlistCheck(Host, &bHostExcluded);
}
if (bBypass)
{
VPRINT("Not acting on this request\n");
}
else
{
spos = TLSPos(params.split_tls, params.split_pos, segment, *size, 0);
case TLS:
spos = TLSPos(ctrack->dp->split_tls, ctrack->dp->split_pos, segment, *size, 0);
if ((5+*size)<=segment_buffer_size) if ((5+*size)<=segment_buffer_size)
{ {
tpos = TLSPos(params.tlsrec, params.tlsrec_pos+5, segment, *size, 0); tpos = TLSPos(ctrack->dp->tlsrec, ctrack->dp->tlsrec_pos+5, segment, *size, 0);
if (tpos>5) if (tpos>5)
{ {
// construct 2 TLS records from one // construct 2 TLS records from one
@ -228,52 +310,46 @@ void tamper_out(t_ctrack *ctrack, uint8_t *segment,size_t segment_buffer_size,si
} }
if (spos && spos < *size) if (spos && spos < *size)
{
VPRINT("split pos %zu\n",spos);
*split_pos = spos; *split_pos = spos;
}
if (params.disorder_tls) *split_flags |= SPLIT_FLAG_DISORDER; if (ctrack->dp->disorder_tls) *split_flags |= SPLIT_FLAG_DISORDER;
if (params.oob_tls) *split_flags |= SPLIT_FLAG_OOB; 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;
} }
else if (params.split_any_protocol && params.split_pos < *size)
*split_pos = params.split_pos;
if (bHaveHost && bBypass && !bHostExcluded && *params.hostlist_auto_filename) if (ctrack->dp->disorder) *split_flags |= SPLIT_FLAG_DISORDER;
{ if (ctrack->dp->oob) *split_flags |= SPLIT_FLAG_OOB;
DBGPRINT("tamper_out put hostname : %s\n", Host);
if (ctrack->hostname) free(ctrack->hostname);
ctrack->hostname=strdup(Host);
}
if (params.disorder) *split_flags |= SPLIT_FLAG_DISORDER;
if (params.oob) *split_flags |= SPLIT_FLAG_OOB;
} }
static void auto_hostlist_reset_fail_counter(const char *hostname) static void auto_hostlist_reset_fail_counter(struct desync_profile *dp, const char *hostname)
{ {
if (hostname) if (hostname)
{ {
hostfail_pool *fail_counter; hostfail_pool *fail_counter;
fail_counter = HostFailPoolFind(params.hostlist_auto_fail_counters, hostname); fail_counter = HostFailPoolFind(dp->hostlist_auto_fail_counters, hostname);
if (fail_counter) if (fail_counter)
{ {
HostFailPoolDel(&params.hostlist_auto_fail_counters, fail_counter); HostFailPoolDel(&dp->hostlist_auto_fail_counters, fail_counter);
VPRINT("auto hostlist : %s : fail counter reset. website is working.\n", hostname); VPRINT("auto hostlist (profile %d) : %s : fail counter reset. website is working.\n", dp->n, hostname);
HOSTLIST_DEBUGLOG_APPEND("%s : fail counter reset. website is working.", hostname); HOSTLIST_DEBUGLOG_APPEND("%s : profile %d : fail counter reset. website is working.", hostname, dp->n);
} }
} }
} }
static void auto_hostlist_failed(const char *hostname) static void auto_hostlist_failed(struct desync_profile *dp, const char *hostname)
{ {
hostfail_pool *fail_counter; hostfail_pool *fail_counter;
fail_counter = HostFailPoolFind(params.hostlist_auto_fail_counters, hostname); fail_counter = HostFailPoolFind(dp->hostlist_auto_fail_counters, hostname);
if (!fail_counter) if (!fail_counter)
{ {
fail_counter = HostFailPoolAdd(&params.hostlist_auto_fail_counters, hostname, params.hostlist_auto_fail_time); fail_counter = HostFailPoolAdd(&dp->hostlist_auto_fail_counters, hostname, dp->hostlist_auto_fail_time);
if (!fail_counter) if (!fail_counter)
{ {
DLOG_ERR("HostFailPoolAdd: out of memory\n"); DLOG_ERR("HostFailPoolAdd: out of memory\n");
@ -281,35 +357,35 @@ static void auto_hostlist_failed(const char *hostname)
} }
} }
fail_counter->counter++; fail_counter->counter++;
VPRINT("auto hostlist : %s : fail counter %d/%d\n", hostname, fail_counter->counter, params.hostlist_auto_fail_threshold); VPRINT("auto hostlist (profile %d) : %s : fail counter %d/%d\n", dp->n , hostname, fail_counter->counter, dp->hostlist_auto_fail_threshold);
HOSTLIST_DEBUGLOG_APPEND("%s : fail counter %d/%d", hostname, fail_counter->counter, params.hostlist_auto_fail_threshold); HOSTLIST_DEBUGLOG_APPEND("%s : profile %d : fail counter %d/%d", hostname, dp->n, fail_counter->counter, dp->hostlist_auto_fail_threshold);
if (fail_counter->counter >= params.hostlist_auto_fail_threshold) if (fail_counter->counter >= dp->hostlist_auto_fail_threshold)
{ {
VPRINT("auto hostlist : fail threshold reached. adding %s to auto hostlist\n", hostname); VPRINT("auto hostlist (profile %d) : fail threshold reached. adding %s to auto hostlist\n", dp->n , hostname);
HostFailPoolDel(&params.hostlist_auto_fail_counters, fail_counter); HostFailPoolDel(&dp->hostlist_auto_fail_counters, fail_counter);
VPRINT("auto hostlist : rechecking %s to avoid duplicates\n", hostname); VPRINT("auto hostlist (profile %d) : rechecking %s to avoid duplicates\n", dp->n, hostname);
bool bExcluded=false; bool bExcluded=false;
if (!HostlistCheck(hostname, &bExcluded) && !bExcluded) if (!HostlistCheck(dp, hostname, &bExcluded) && !bExcluded)
{ {
VPRINT("auto hostlist : adding %s\n", hostname); VPRINT("auto hostlist (profile %d) : adding %s to %s\n", dp->n, hostname, dp->hostlist_auto_filename);
HOSTLIST_DEBUGLOG_APPEND("%s : adding", hostname); HOSTLIST_DEBUGLOG_APPEND("%s : profile %d : adding to %s", hostname, dp->n, dp->hostlist_auto_filename);
if (!StrPoolAddStr(&params.hostlist, hostname)) if (!StrPoolAddStr(&dp->hostlist, hostname))
{ {
DLOG_ERR("StrPoolAddStr out of memory\n"); DLOG_ERR("StrPoolAddStr out of memory\n");
return; return;
} }
if (!append_to_list_file(params.hostlist_auto_filename, hostname)) if (!append_to_list_file(dp->hostlist_auto_filename, hostname))
{ {
DLOG_PERROR("write to auto hostlist:"); DLOG_PERROR("write to auto hostlist:");
return; return;
} }
params.hostlist_auto_mod_time = file_mod_time(params.hostlist_auto_filename); dp->hostlist_auto_mod_time = file_mod_time(dp->hostlist_auto_filename);
} }
else else
{ {
VPRINT("auto hostlist : NOT adding %s\n", hostname); VPRINT("auto hostlist (profile %d) : NOT adding %s\n", dp->n, hostname);
HOSTLIST_DEBUGLOG_APPEND("%s : NOT adding, duplicate detected", hostname); HOSTLIST_DEBUGLOG_APPEND("%s : profile %d : NOT adding, duplicate detected", hostname, dp->n);
} }
} }
} }
@ -320,9 +396,9 @@ void tamper_in(t_ctrack *ctrack, uint8_t *segment,size_t segment_buffer_size,siz
DBGPRINT("tamper_in hostname=%s\n", ctrack->hostname); DBGPRINT("tamper_in hostname=%s\n", ctrack->hostname);
if (*params.hostlist_auto_filename) if (ctrack->dp && ctrack->b_ah_check)
{ {
HostFailPoolPurgeRateLimited(&params.hostlist_auto_fail_counters); HostFailPoolPurgeRateLimited(&ctrack->dp->hostlist_auto_fail_counters);
if (ctrack->l7proto==HTTP && ctrack->hostname) if (ctrack->l7proto==HTTP && ctrack->hostname)
{ {
@ -333,7 +409,7 @@ void tamper_in(t_ctrack *ctrack, uint8_t *segment,size_t segment_buffer_size,siz
if (bFail) if (bFail)
{ {
VPRINT("redirect to another domain detected. possibly DPI redirect.\n"); VPRINT("redirect to another domain detected. possibly DPI redirect.\n");
HOSTLIST_DEBUGLOG_APPEND("%s : redirect to another domain", ctrack->hostname); HOSTLIST_DEBUGLOG_APPEND("%s : profile %d : redirect to another domain", ctrack->hostname, ctrack->dp->n);
} }
else else
VPRINT("local or in-domain redirect detected. it's not a DPI redirect.\n"); VPRINT("local or in-domain redirect detected. it's not a DPI redirect.\n");
@ -343,11 +419,9 @@ void tamper_in(t_ctrack *ctrack, uint8_t *segment,size_t segment_buffer_size,siz
// received not http reply. do not monitor this connection anymore // received not http reply. do not monitor this connection anymore
VPRINT("incoming unknown HTTP data detected for hostname %s\n", ctrack->hostname); VPRINT("incoming unknown HTTP data detected for hostname %s\n", ctrack->hostname);
} }
if (bFail) auto_hostlist_failed(ctrack->hostname); if (bFail) auto_hostlist_failed(ctrack->dp, ctrack->hostname);
} }
if (!bFail) auto_hostlist_reset_fail_counter(ctrack->hostname); if (!bFail) auto_hostlist_reset_fail_counter(ctrack->dp, ctrack->hostname);
} }
ctrack->bTamperInCutoff = true; ctrack->bTamperInCutoff = true;
} }
@ -356,30 +430,32 @@ void rst_in(t_ctrack *ctrack)
{ {
DBGPRINT("rst_in hostname=%s\n", ctrack->hostname); DBGPRINT("rst_in hostname=%s\n", ctrack->hostname);
if (!*params.hostlist_auto_filename) return; if (ctrack->dp && ctrack->b_ah_check)
HostFailPoolPurgeRateLimited(&params.hostlist_auto_fail_counters);
if (!ctrack->bTamperInCutoff && ctrack->hostname)
{ {
VPRINT("incoming RST detected for hostname %s\n", ctrack->hostname); HostFailPoolPurgeRateLimited(&ctrack->dp->hostlist_auto_fail_counters);
HOSTLIST_DEBUGLOG_APPEND("%s : incoming RST", ctrack->hostname);
auto_hostlist_failed(ctrack->hostname); if (!ctrack->bTamperInCutoff && ctrack->hostname)
{
VPRINT("incoming RST detected for hostname %s\n", ctrack->hostname);
HOSTLIST_DEBUGLOG_APPEND("%s : profile %d : incoming RST", ctrack->hostname, ctrack->dp->n);
auto_hostlist_failed(ctrack->dp, ctrack->hostname);
}
} }
} }
void hup_out(t_ctrack *ctrack) void hup_out(t_ctrack *ctrack)
{ {
DBGPRINT("hup_out hostname=%s\n", ctrack->hostname); DBGPRINT("hup_out hostname=%s\n", ctrack->hostname);
if (!*params.hostlist_auto_filename) return; if (ctrack->dp && ctrack->b_ah_check)
HostFailPoolPurgeRateLimited(&params.hostlist_auto_fail_counters);
if (!ctrack->bTamperInCutoff && ctrack->hostname)
{ {
// local leg dropped connection after first request. probably due to timeout. HostFailPoolPurgeRateLimited(&ctrack->dp->hostlist_auto_fail_counters);
VPRINT("local leg closed connection after first request (timeout ?). hostname: %s\n", ctrack->hostname);
HOSTLIST_DEBUGLOG_APPEND("%s : client closed connection without server reply", ctrack->hostname); if (!ctrack->bTamperInCutoff && ctrack->hostname)
auto_hostlist_failed(ctrack->hostname); {
// local leg dropped connection after first request. probably due to timeout.
VPRINT("local leg closed connection after first request (timeout ?). hostname: %s\n", ctrack->hostname);
HOSTLIST_DEBUGLOG_APPEND("%s : profile %d : client closed connection without server reply", ctrack->hostname, ctrack->dp->n);
auto_hostlist_failed(ctrack->dp, ctrack->hostname);
}
} }
} }

View File

@ -4,6 +4,8 @@
#include <stdint.h> #include <stdint.h>
#include <sys/types.h> #include <sys/types.h>
#include "params.h"
#define SPLIT_FLAG_DISORDER 0x01 #define SPLIT_FLAG_DISORDER 0x01
#define SPLIT_FLAG_OOB 0x02 #define SPLIT_FLAG_OOB 0x02
@ -14,10 +16,14 @@ typedef struct
t_l7proto l7proto; t_l7proto l7proto;
bool bFirstReplyChecked; bool bFirstReplyChecked;
bool bTamperInCutoff; bool bTamperInCutoff;
bool b_ah_check;
char *hostname; char *hostname;
struct desync_profile *dp; // desync profile cache
} t_ctrack; } t_ctrack;
void tamper_out(t_ctrack *ctrack, uint8_t *segment,size_t segment_buffer_size,size_t *size, size_t *split_pos, uint8_t *split_flags); 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_in(t_ctrack *ctrack, uint8_t *segment,size_t segment_buffer_size,size_t *size); void tamper_in(t_ctrack *ctrack, uint8_t *segment,size_t segment_buffer_size,size_t *size);
// connection reset by remote leg // connection reset by remote leg
void rst_in(t_ctrack *ctrack); void rst_in(t_ctrack *ctrack);

View File

@ -46,8 +46,7 @@ bool bHup = false;
static void onhup(int sig) static void onhup(int sig)
{ {
printf("HUP received !\n"); printf("HUP received !\n");
if (params.hostlist || params.hostlist_exclude) printf("Will reload hostlist on next request (if any)\n");
printf("Will reload hostlists on next request\n");
bHup = true; bHup = true;
} }
// should be called in normal execution // should be called in normal execution
@ -67,7 +66,14 @@ void dohup(void)
static void onusr2(int sig) static void onusr2(int sig)
{ {
printf("\nHOSTFAIL POOL DUMP\n"); printf("\nHOSTFAIL POOL DUMP\n");
HostFailPoolDump(params.hostlist_auto_fail_counters);
struct desync_profile_list *dpl;
LIST_FOREACH(dpl, &params.desync_profiles, next)
{
printf("\nDESYNC PROFILE %d\n",dpl->dp.n);
HostFailPoolDump(dpl->dp.hostlist_auto_fail_counters);
}
printf("\n"); printf("\n");
} }
@ -170,7 +176,11 @@ static void exithelp(void)
#endif #endif
" --debug=0|1|2|syslog|@<filename>\t; 1 and 2 means log to console and set debug level. for other targets use --debug-level.\n" " --debug=0|1|2|syslog|@<filename>\t; 1 and 2 means log to console and set debug level. for other targets use --debug-level.\n"
" --debug-level=0|1|2\t\t\t; specify debug level\n" " --debug-level=0|1|2\t\t\t; specify debug level\n"
"\nFILTER:\n" "\nMULTI-STRATEGY:\n"
" --new\t\t\t\t\t; begin new strategy\n"
" --filter-l3=ipv4|ipv6\t\t\t; L3 protocol filter. multiple comma separated values allowed.\n"
" --filter-tcp=[~]port1[-port2]\t\t; TCP port filter. ~ means negation\n"
"\nHOSTLIST FILTER:\n"
" --hostlist=<filename>\t\t\t; only act on hosts in the list (one host per line, subdomains auto apply, gzip supported, multiple hostlists allowed)\n" " --hostlist=<filename>\t\t\t; only act on hosts in the list (one host per line, subdomains auto apply, gzip supported, multiple hostlists allowed)\n"
" --hostlist-exclude=<filename>\t\t; do not act on hosts in the list (one host per line, subdomains auto apply, gzip supported, multiple hostlists allowed)\n" " --hostlist-exclude=<filename>\t\t; do not act on hosts in the list (one host per line, subdomains auto apply, gzip supported, multiple hostlists allowed)\n"
" --hostlist-auto=<filename>\t\t; detect DPI blocks and build hostlist automatically\n" " --hostlist-auto=<filename>\t\t; detect DPI blocks and build hostlist automatically\n"
@ -203,7 +213,6 @@ static void exithelp(void)
" --tlsrec-pos=<pos>\t\t\t; make 2 TLS records. split at specified pos\n" " --tlsrec-pos=<pos>\t\t\t; make 2 TLS records. split at specified pos\n"
#ifdef __linux__ #ifdef __linux__
" --mss=<int>\t\t\t\t; set client MSS. forces server to split messages but significantly decreases speed !\n" " --mss=<int>\t\t\t\t; set client MSS. forces server to split messages but significantly decreases speed !\n"
" --mss-pf=[~]port1[-port2]\t\t; MSS port filter. ~ means negation\n"
#endif #endif
" --tamper-start=[n]<pos>\t\t; start tampering only from specified outbound stream position. default is 0. 'n' means data block number.\n" " --tamper-start=[n]<pos>\t\t; start tampering only from specified outbound stream position. default is 0. 'n' means data block number.\n"
" --tamper-cutoff=[n]<pos>\t\t; do not tamper anymore after specified outbound stream position. default is unlimited.\n", " --tamper-cutoff=[n]<pos>\t\t; do not tamper anymore after specified outbound stream position. default is unlimited.\n",
@ -216,11 +225,7 @@ static void exithelp(void)
} }
static void cleanup_params(void) static void cleanup_params(void)
{ {
strlist_destroy(&params.hostlist_files); dp_list_destroy(&params.desync_profiles);
strlist_destroy(&params.hostlist_exclude_files);
StrPoolDestroy(&params.hostlist_exclude);
StrPoolDestroy(&params.hostlist);
HostFailPoolDestroy(&params.hostlist_auto_fail_counters);
} }
static void exithelp_clean(void) static void exithelp_clean(void)
{ {
@ -285,6 +290,32 @@ bool parse_tlspos(const char *s, enum tlspos *pos)
return true; return true;
} }
static bool wf_make_l3(char *opt, bool *ipv4, bool *ipv6)
{
char *e,*p,c;
for (p=opt,*ipv4=*ipv6=false ; p ; )
{
if ((e = strchr(p,',')))
{
c=*e;
*e=0;
}
if (!strcmp(p,"ipv4"))
*ipv4 = true;
else if (!strcmp(p,"ipv6"))
*ipv6 = true;
else return false;
if (e)
{
*e++=c;
}
p = e;
}
return true;
}
void parse_params(int argc, char *argv[]) void parse_params(int argc, char *argv[])
{ {
@ -292,18 +323,13 @@ void parse_params(int argc, char *argv[])
int v, i; int v, i;
memset(&params, 0, sizeof(params)); memset(&params, 0, sizeof(params));
memcpy(params.hostspell, "host", 4); // default hostspell
params.maxconn = DEFAULT_MAX_CONN; params.maxconn = DEFAULT_MAX_CONN;
params.max_orphan_time = DEFAULT_MAX_ORPHAN_TIME; params.max_orphan_time = DEFAULT_MAX_ORPHAN_TIME;
params.binds_last = -1; params.binds_last = -1;
params.hostlist_auto_fail_threshold = HOSTLIST_AUTO_FAIL_THRESHOLD_DEFAULT;
params.hostlist_auto_fail_time = HOSTLIST_AUTO_FAIL_TIME_DEFAULT;
#if defined(__linux__) || defined(__APPLE__) #if defined(__linux__) || defined(__APPLE__)
params.tcp_user_timeout_local = DEFAULT_TCP_USER_TIMEOUT_LOCAL; params.tcp_user_timeout_local = DEFAULT_TCP_USER_TIMEOUT_LOCAL;
params.tcp_user_timeout_remote = DEFAULT_TCP_USER_TIMEOUT_REMOTE; params.tcp_user_timeout_remote = DEFAULT_TCP_USER_TIMEOUT_REMOTE;
#endif #endif
LIST_INIT(&params.hostlist_files);
LIST_INIT(&params.hostlist_exclude_files);
#if defined(__OpenBSD__) || defined(__APPLE__) #if defined(__OpenBSD__) || defined(__APPLE__)
params.pf_enable = true; // OpenBSD and MacOS have no other choice params.pf_enable = true; // OpenBSD and MacOS have no other choice
@ -314,6 +340,17 @@ void parse_params(int argc, char *argv[])
params.droproot = true; params.droproot = true;
} }
struct desync_profile_list *dpl;
struct desync_profile *dp;
int desync_profile_count=0;
if (!(dpl = dp_list_add(&params.desync_profiles)))
{
DLOG_ERR("desync_profile_add: out of memory\n");
exit_clean(1);
}
dp = &dpl->dp;
dp->n = ++desync_profile_count;
const struct option long_options[] = { const struct option long_options[] = {
{ "help",no_argument,0,0 },// optidx=0 { "help",no_argument,0,0 },// optidx=0
{ "h",no_argument,0,0 },// optidx=1 { "h",no_argument,0,0 },// optidx=1
@ -371,18 +408,22 @@ void parse_params(int argc, char *argv[])
{ "tamper-start",required_argument,0,0 },// optidx=53 { "tamper-start",required_argument,0,0 },// optidx=53
{ "tamper-cutoff",required_argument,0,0 },// optidx=54 { "tamper-cutoff",required_argument,0,0 },// optidx=54
{ "connect-bind-addr",required_argument,0,0 },// optidx=55 { "connect-bind-addr",required_argument,0,0 },// optidx=55
{ "new",no_argument,0,0 }, // optidx=56
{ "filter-l3",required_argument,0,0 }, // optidx=57
{ "filter-tcp",required_argument,0,0 }, // optidx=58
#if defined(__FreeBSD__) #if defined(__FreeBSD__)
{ "enable-pf",no_argument,0,0 },// optidx=56 { "enable-pf",no_argument,0,0 },// optidx=59
#elif defined(__APPLE__) #elif defined(__APPLE__)
{ "local-tcp-user-timeout",required_argument,0,0 },// optidx=56 { "local-tcp-user-timeout",required_argument,0,0 },// optidx=59
{ "remote-tcp-user-timeout",required_argument,0,0 },// optidx=57 { "remote-tcp-user-timeout",required_argument,0,0 },// optidx=60
#elif defined(__linux__) #elif defined(__linux__)
{ "local-tcp-user-timeout",required_argument,0,0 },// optidx=56 { "local-tcp-user-timeout",required_argument,0,0 },// optidx=59
{ "remote-tcp-user-timeout",required_argument,0,0 },// optidx=57 { "remote-tcp-user-timeout",required_argument,0,0 },// optidx=60
{ "mss",required_argument,0,0 },// optidx=58 { "mss",required_argument,0,0 },// optidx=61
{ "mss-pf",required_argument,0,0 },// optidx=59
#ifdef SPLICE_PRESENT #ifdef SPLICE_PRESENT
{ "nosplice",no_argument,0,0 },// optidx=60 { "nosplice",no_argument,0,0 },// optidx=62
#endif #endif
#endif #endif
{ "hostlist-auto-retrans-threshold",optional_argument,0,0}, // ignored. for nfqws command line compatibility { "hostlist-auto-retrans-threshold",optional_argument,0,0}, // ignored. for nfqws command line compatibility
@ -513,7 +554,7 @@ void parse_params(int argc, char *argv[])
} }
break; break;
case 17: /* hostcase */ case 17: /* hostcase */
params.hostcase = true; dp->hostcase = true;
params.tamper = true; params.tamper = true;
break; break;
case 18: /* hostspell */ case 18: /* hostspell */
@ -522,28 +563,28 @@ void parse_params(int argc, char *argv[])
DLOG_ERR("hostspell must be exactly 4 chars long\n"); DLOG_ERR("hostspell must be exactly 4 chars long\n");
exit_clean(1); exit_clean(1);
} }
params.hostcase = true; dp->hostcase = true;
memcpy(params.hostspell, optarg, 4); memcpy(dp->hostspell, optarg, 4);
params.tamper = true; params.tamper = true;
break; break;
case 19: /* hostdot */ case 19: /* hostdot */
params.hostdot = true; dp->hostdot = true;
params.tamper = true; params.tamper = true;
break; break;
case 20: /* hostnospace */ case 20: /* hostnospace */
params.hostnospace = true; dp->hostnospace = true;
params.tamper = true; params.tamper = true;
break; break;
case 21: /* hostpad */ case 21: /* hostpad */
params.hostpad = atoi(optarg); dp->hostpad = atoi(optarg);
params.tamper = true; params.tamper = true;
break; break;
case 22: /* domcase */ case 22: /* domcase */
params.domcase = true; dp->domcase = true;
params.tamper = true; params.tamper = true;
break; break;
case 23: /* split-http-req */ case 23: /* split-http-req */
if (!parse_httpreqpos(optarg, &params.split_http_req)) if (!parse_httpreqpos(optarg, &dp->split_http_req))
{ {
DLOG_ERR("Invalid argument for split-http-req\n"); DLOG_ERR("Invalid argument for split-http-req\n");
exit_clean(1); exit_clean(1);
@ -551,7 +592,7 @@ void parse_params(int argc, char *argv[])
params.tamper = true; params.tamper = true;
break; break;
case 24: /* split-tls */ case 24: /* split-tls */
if (!parse_tlspos(optarg, &params.split_tls)) if (!parse_tlspos(optarg, &dp->split_tls))
{ {
DLOG_ERR("Invalid argument for split-tls\n"); DLOG_ERR("Invalid argument for split-tls\n");
exit_clean(1); exit_clean(1);
@ -561,7 +602,7 @@ void parse_params(int argc, char *argv[])
case 25: /* split-pos */ case 25: /* split-pos */
i = atoi(optarg); i = atoi(optarg);
if (i>0) if (i>0)
params.split_pos = i; dp->split_pos = i;
else else
{ {
DLOG_ERR("Invalid argument for split-pos\n"); DLOG_ERR("Invalid argument for split-pos\n");
@ -570,13 +611,13 @@ void parse_params(int argc, char *argv[])
params.tamper = true; params.tamper = true;
break; break;
case 26: /* split-any-protocol */ case 26: /* split-any-protocol */
params.split_any_protocol = true; dp->split_any_protocol = true;
break; break;
case 27: /* disorder */ case 27: /* disorder */
if (optarg) if (optarg)
{ {
if (!strcmp(optarg,"http")) params.disorder_http=true; if (!strcmp(optarg,"http")) dp->disorder_http=true;
else if (!strcmp(optarg,"tls")) params.disorder_tls=true; else if (!strcmp(optarg,"tls")) dp->disorder_tls=true;
else else
{ {
DLOG_ERR("Invalid argument for disorder\n"); DLOG_ERR("Invalid argument for disorder\n");
@ -584,14 +625,14 @@ void parse_params(int argc, char *argv[])
} }
} }
else else
params.disorder = true; dp->disorder = true;
save_default_ttl(); save_default_ttl();
break; break;
case 28: /* oob */ case 28: /* oob */
if (optarg) if (optarg)
{ {
if (!strcmp(optarg,"http")) params.oob_http=true; if (!strcmp(optarg,"http")) dp->oob_http=true;
else if (!strcmp(optarg,"tls")) params.oob_tls=true; else if (!strcmp(optarg,"tls")) dp->oob_tls=true;
else else
{ {
DLOG_ERR("Invalid argument for oob\n"); DLOG_ERR("Invalid argument for oob\n");
@ -599,39 +640,39 @@ void parse_params(int argc, char *argv[])
} }
} }
else else
params.oob = true; dp->oob = true;
break; break;
case 29: /* oob-data */ case 29: /* oob-data */
{ {
size_t l = strlen(optarg); size_t l = strlen(optarg);
unsigned int bt; unsigned int bt;
if (l==1) params.oob_byte = (uint8_t)*optarg; if (l==1) dp->oob_byte = (uint8_t)*optarg;
else if (l!=4 || sscanf(optarg,"0x%02X",&bt)!=1) else if (l!=4 || sscanf(optarg,"0x%02X",&bt)!=1)
{ {
DLOG_ERR("Invalid argument for oob-data\n"); DLOG_ERR("Invalid argument for oob-data\n");
exit_clean(1); exit_clean(1);
} }
else params.oob_byte = (uint8_t)bt; else dp->oob_byte = (uint8_t)bt;
} }
break; break;
case 30: /* methodspace */ case 30: /* methodspace */
params.methodspace = true; dp->methodspace = true;
params.tamper = true; params.tamper = true;
break; break;
case 31: /* methodeol */ case 31: /* methodeol */
params.methodeol = true; dp->methodeol = true;
params.tamper = true; params.tamper = true;
break; break;
case 32: /* hosttab */ case 32: /* hosttab */
params.hosttab = true; dp->hosttab = true;
params.tamper = true; params.tamper = true;
break; break;
case 33: /* unixeol */ case 33: /* unixeol */
params.unixeol = true; dp->unixeol = true;
params.tamper = true; params.tamper = true;
break; break;
case 34: /* tlsrec */ case 34: /* tlsrec */
if (!parse_tlspos(optarg, &params.tlsrec)) if (!parse_tlspos(optarg, &dp->tlsrec))
{ {
DLOG_ERR("Invalid argument for tlsrec\n"); DLOG_ERR("Invalid argument for tlsrec\n");
exit_clean(1); exit_clean(1);
@ -639,8 +680,8 @@ void parse_params(int argc, char *argv[])
params.tamper = true; params.tamper = true;
break; break;
case 35: /* tlsrec-pos */ case 35: /* tlsrec-pos */
if ((params.tlsrec_pos = atoi(optarg))>0) if ((dp->tlsrec_pos = atoi(optarg))>0)
params.tlsrec = tlspos_pos; dp->tlsrec = tlspos_pos;
else else
{ {
DLOG_ERR("Invalid argument for tlsrec-pos\n"); DLOG_ERR("Invalid argument for tlsrec-pos\n");
@ -649,7 +690,7 @@ void parse_params(int argc, char *argv[])
params.tamper = true; params.tamper = true;
break; break;
case 36: /* hostlist */ case 36: /* hostlist */
if (!strlist_add(&params.hostlist_files, optarg)) if (!strlist_add(&dp->hostlist_files, optarg))
{ {
DLOG_ERR("strlist_add failed\n"); DLOG_ERR("strlist_add failed\n");
exit_clean(1); exit_clean(1);
@ -657,7 +698,7 @@ void parse_params(int argc, char *argv[])
params.tamper = true; params.tamper = true;
break; break;
case 37: /* hostlist-exclude */ case 37: /* hostlist-exclude */
if (!strlist_add(&params.hostlist_exclude_files, optarg)) if (!strlist_add(&dp->hostlist_exclude_files, optarg))
{ {
DLOG_ERR("strlist_add failed\n"); DLOG_ERR("strlist_add failed\n");
exit_clean(1); exit_clean(1);
@ -665,9 +706,9 @@ void parse_params(int argc, char *argv[])
params.tamper = true; params.tamper = true;
break; break;
case 38: /* hostlist-auto */ case 38: /* hostlist-auto */
if (*params.hostlist_auto_filename) if (*dp->hostlist_auto_filename)
{ {
DLOG_ERR("only one auto hostlist is supported\n"); DLOG_ERR("only one auto hostlist per profile is supported\n");
exit_clean(1); exit_clean(1);
} }
{ {
@ -687,26 +728,26 @@ void parse_params(int argc, char *argv[])
if (params.droproot && chown(optarg, params.uid, -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); DLOG_ERR("could not chown %s. auto hostlist file may not be writable after privilege drop\n", optarg);
} }
if (!strlist_add(&params.hostlist_files, optarg)) if (!strlist_add(&dp->hostlist_files, optarg))
{ {
DLOG_ERR("strlist_add failed\n"); DLOG_ERR("strlist_add failed\n");
exit_clean(1); exit_clean(1);
} }
strncpy(params.hostlist_auto_filename, optarg, sizeof(params.hostlist_auto_filename)); strncpy(dp->hostlist_auto_filename, optarg, sizeof(dp->hostlist_auto_filename));
params.hostlist_auto_filename[sizeof(params.hostlist_auto_filename) - 1] = '\0'; dp->hostlist_auto_filename[sizeof(dp->hostlist_auto_filename) - 1] = '\0';
params.tamper = true; // need to detect blocks and update autohostlist. cannot just slice. params.tamper = true; // need to detect blocks and update autohostlist. cannot just slice.
break; break;
case 39: /* hostlist-auto-fail-threshold */ case 39: /* hostlist-auto-fail-threshold */
params.hostlist_auto_fail_threshold = (uint8_t)atoi(optarg); dp->hostlist_auto_fail_threshold = (uint8_t)atoi(optarg);
if (params.hostlist_auto_fail_threshold<1 || params.hostlist_auto_fail_threshold>20) if (dp->hostlist_auto_fail_threshold<1 || dp->hostlist_auto_fail_threshold>20)
{ {
DLOG_ERR("auto hostlist fail threshold must be within 1..20\n"); DLOG_ERR("auto hostlist fail threshold must be within 1..20\n");
exit_clean(1); exit_clean(1);
} }
break; break;
case 40: /* hostlist-auto-fail-time */ case 40: /* hostlist-auto-fail-time */
params.hostlist_auto_fail_time = (uint8_t)atoi(optarg); dp->hostlist_auto_fail_time = (uint8_t)atoi(optarg);
if (params.hostlist_auto_fail_time<1) if (dp->hostlist_auto_fail_time<1)
{ {
DLOG_ERR("auto hostlist fail time is not valid\n"); DLOG_ERR("auto hostlist fail time is not valid\n");
exit_clean(1); exit_clean(1);
@ -820,26 +861,28 @@ void parse_params(int argc, char *argv[])
const char *p=optarg; const char *p=optarg;
if (*p=='n') if (*p=='n')
{ {
params.tamper_start_n=true; dp->tamper_start_n=true;
p++; p++;
} }
else else
params.tamper_start_n=false; dp->tamper_start_n=false;
params.tamper_start = atoi(p); dp->tamper_start = atoi(p);
} }
params.tamper_lim = true;
break; break;
case 54: /* tamper-cutoff */ case 54: /* tamper-cutoff */
{ {
const char *p=optarg; const char *p=optarg;
if (*p=='n') if (*p=='n')
{ {
params.tamper_cutoff_n=true; dp->tamper_cutoff_n=true;
p++; p++;
} }
else else
params.tamper_cutoff_n=false; dp->tamper_cutoff_n=false;
params.tamper_cutoff = atoi(p); dp->tamper_cutoff = atoi(p);
} }
params.tamper_lim = true;
break; break;
case 55: /* connect-bind-addr */ case 55: /* connect-bind-addr */
{ {
@ -868,12 +911,37 @@ void parse_params(int argc, char *argv[])
} }
break; break;
case 56: /* new */
if (!(dpl = dp_list_add(&params.desync_profiles)))
{
DLOG_ERR("desync_profile_add: out of memory\n");
exit_clean(1);
}
dp = &dpl->dp;
dp->n = ++desync_profile_count;
break;
case 57: /* filter-l3 */
if (!wf_make_l3(optarg,&dp->filter_ipv4,&dp->filter_ipv6))
{
DLOG_ERR("bad value for --filter-l3\n");
exit_clean(1);
}
break;
case 58: /* filter-tcp */
if (!pf_parse(optarg,&dp->pf_tcp))
{
DLOG_ERR("Invalid port filter : %s\n",optarg);
exit_clean(1);
}
break;
#if defined(__FreeBSD__) #if defined(__FreeBSD__)
case 56: /* enable-pf */ case 59: /* enable-pf */
params.pf_enable = true; params.pf_enable = true;
break; break;
#elif defined(__linux__) || defined(__APPLE__) #elif defined(__linux__) || defined(__APPLE__)
case 56: /* local-tcp-user-timeout */ case 59: /* local-tcp-user-timeout */
params.tcp_user_timeout_local = atoi(optarg); params.tcp_user_timeout_local = atoi(optarg);
if (params.tcp_user_timeout_local<0 || params.tcp_user_timeout_local>86400) if (params.tcp_user_timeout_local<0 || params.tcp_user_timeout_local>86400)
{ {
@ -881,7 +949,7 @@ void parse_params(int argc, char *argv[])
exit_clean(1); exit_clean(1);
} }
break; break;
case 57: /* remote-tcp-user-timeout */ case 60: /* remote-tcp-user-timeout */
params.tcp_user_timeout_remote = atoi(optarg); params.tcp_user_timeout_remote = atoi(optarg);
if (params.tcp_user_timeout_remote<0 || params.tcp_user_timeout_remote>86400) if (params.tcp_user_timeout_remote<0 || params.tcp_user_timeout_remote>86400)
{ {
@ -892,24 +960,17 @@ void parse_params(int argc, char *argv[])
#endif #endif
#if defined(__linux__) #if defined(__linux__)
case 58: /* mss */ case 61: /* mss */
// this option does not work in any BSD and MacOS. OS may accept but it changes nothing // this option does not work in any BSD and MacOS. OS may accept but it changes nothing
params.mss = atoi(optarg); dp->mss = atoi(optarg);
if (params.mss<88 || params.mss>32767) if (dp->mss<88 || dp->mss>32767)
{ {
DLOG_ERR("Invalid value for MSS. Linux accepts MSS 88-32767.\n"); DLOG_ERR("Invalid value for MSS. Linux accepts MSS 88-32767.\n");
exit_clean(1); exit_clean(1);
} }
break; break;
case 59: /* mss-pf */
if (!pf_parse(optarg,&params.mss_pf))
{
DLOG_ERR("Invalid MSS port filter.\n");
exit_clean(1);
}
break;
#ifdef SPLICE_PRESENT #ifdef SPLICE_PRESENT
case 60: /* nosplice */ case 62: /* nosplice */
params.nosplice = true; params.nosplice = true;
break; break;
#endif #endif
@ -925,22 +986,36 @@ void parse_params(int argc, char *argv[])
{ {
params.binds_last=0; // default bind to all params.binds_last=0; // default bind to all
} }
if (params.skip_nodelay && (params.split_http_req || params.split_pos)) if (!params.resolver_threads) params.resolver_threads = 5 + params.maxconn/50;
VPRINT("adding low-priority default empty desync profile\n");
// add default empty profile
if (!(dpl = dp_list_add(&params.desync_profiles)))
{ {
DLOG_ERR("Cannot split with --skip-nodelay\n"); DLOG_ERR("desync_profile_add: out of memory\n");
exit_clean(1); exit_clean(1);
} }
if (!params.resolver_threads) params.resolver_threads = 5 + params.maxconn/50;
if (params.split_tls==tlspos_none && params.split_pos) params.split_tls=tlspos_pos;
if (params.split_http_req==httpreqpos_none && params.split_pos) params.split_http_req=httpreqpos_pos;
if (*params.hostlist_auto_filename) params.hostlist_auto_mod_time = file_mod_time(params.hostlist_auto_filename); DLOG_CONDUP("we have %d user defined desync profile(s) and default low priority profile 0\n",desync_profile_count);
LIST_FOREACH(dpl, &params.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 (*dp->hostlist_auto_filename) dp->hostlist_auto_mod_time = file_mod_time(dp->hostlist_auto_filename);
if (params.skip_nodelay && (dp->split_tls || dp->split_http_req || dp->split_pos))
{
DLOG_ERR("Cannot split with --skip-nodelay\n");
exit_clean(1);
}
}
if (!LoadIncludeHostLists()) if (!LoadIncludeHostLists())
{ {
DLOG_ERR("Include hostlist load failed\n"); DLOG_ERR("Include hostlist load failed\n");
exit_clean(1); exit_clean(1);
} }
if (*params.hostlist_auto_filename) NonEmptyHostlist(&params.hostlist);
if (!LoadExcludeHostLists()) if (!LoadExcludeHostLists())
{ {
DLOG_ERR("Exclude hostlist load failed\n"); DLOG_ERR("Exclude hostlist load failed\n");
@ -1057,7 +1132,7 @@ static bool set_ulimit(void)
// additional 1/2 for unpaired remote legs sending buffers // additional 1/2 for unpaired remote legs sending buffers
// 16 for listen_fd, epoll, hostlist, ... // 16 for listen_fd, epoll, hostlist, ...
#ifdef SPLICE_PRESENT #ifdef SPLICE_PRESENT
fdmax = (params.nosplice ? 2 : (params.tamper && !params.tamper_start && !params.tamper_cutoff ? 4 : 6)) * params.maxconn; fdmax = (params.nosplice ? 2 : (params.tamper && !params.tamper_lim ? 4 : 6)) * params.maxconn;
#else #else
fdmax = 2 * params.maxconn; fdmax = 2 * params.maxconn;
#endif #endif

View File

@ -368,7 +368,7 @@ static void set_user_timeout(int fd, int timeout)
//Createas a socket and initiates the connection to the host specified by //Createas a socket and initiates the connection to the host specified by
//remote_addr. //remote_addr.
//Returns -1 if something fails, >0 on success (socket fd). //Returns -1 if something fails, >0 on success (socket fd).
static int connect_remote(const struct sockaddr *remote_addr, bool bApplyConnectionFooling) static int connect_remote(const struct sockaddr *remote_addr, int mss)
{ {
int remote_fd = 0, yes = 1, no = 0; int remote_fd = 0, yes = 1, no = 0;
@ -406,24 +406,18 @@ static int connect_remote(const struct sockaddr *remote_addr, bool bApplyConnect
close(remote_fd); close(remote_fd);
return -1; return -1;
} }
if (bApplyConnectionFooling && params.mss) #ifdef __linux__
if (mss)
{ {
uint16_t port = saport(remote_addr); VPRINT("Setting MSS %d\n", mss);
if (pf_in_range(port,&params.mss_pf)) if (setsockopt(remote_fd, IPPROTO_TCP, TCP_MAXSEG, &mss, sizeof(int)) <0)
{ {
VPRINT("Setting MSS %d\n",params.mss); DLOG_PERROR("setsockopt (TCP_MAXSEG, connect_remote)");
if (setsockopt(remote_fd, IPPROTO_TCP, TCP_MAXSEG, &params.mss, sizeof(int)) <0) close(remote_fd);
{ return -1;
DLOG_PERROR("setsockopt (TCP_MAXSEG, connect_remote)");
close(remote_fd);
return -1;
}
}
else
{
VPRINT("Not setting MSS. Port %u is out of MSS port range.\n",port);
} }
} }
#endif
// if no bind address specified - address family will be 0 in params_connect_bindX // if no bind address specified - address family will be 0 in params_connect_bindX
if(remote_addr->sa_family == params.connect_bind4.sin_family) if(remote_addr->sa_family == params.connect_bind4.sin_family)
@ -494,13 +488,12 @@ static tproxy_conn_t *new_conn(int fd, bool remote)
tproxy_conn_t *conn; tproxy_conn_t *conn;
//Create connection object and fill in information //Create connection object and fill in information
if((conn = (tproxy_conn_t*) malloc(sizeof(tproxy_conn_t))) == NULL) if((conn = (tproxy_conn_t*) calloc(1, sizeof(tproxy_conn_t))) == NULL)
{ {
DLOG_ERR("Could not allocate memory for connection\n"); DLOG_ERR("Could not allocate memory for connection\n");
return NULL; return NULL;
} }
memset(conn, 0, sizeof(tproxy_conn_t));
conn->state = CONN_UNAVAILABLE; conn->state = CONN_UNAVAILABLE;
conn->fd = fd; conn->fd = fd;
conn->remote = remote; conn->remote = remote;
@ -508,7 +501,7 @@ static tproxy_conn_t *new_conn(int fd, bool remote)
#ifdef SPLICE_PRESENT #ifdef SPLICE_PRESENT
// if dont tamper - both legs are spliced, create 2 pipes // if dont tamper - both legs are spliced, create 2 pipes
// otherwise create pipe only in local leg // otherwise create pipe only in local leg
if (!params.nosplice && ( !remote || !params.tamper || params.tamper_start || params.tamper_cutoff ) && pipe2(conn->splice_pipe, O_NONBLOCK) != 0) if (!params.nosplice && ( !remote || !params.tamper || params.tamper_lim ) && pipe2(conn->splice_pipe, O_NONBLOCK) != 0)
{ {
DLOG_ERR("Could not create the splice pipe\n"); DLOG_ERR("Could not create the splice pipe\n");
free_conn(conn); free_conn(conn);
@ -606,7 +599,7 @@ static tproxy_conn_t* add_tcp_connection(int efd, struct tailhead *conn_list,int
if (proxy_type==CONN_TYPE_TRANSPARENT) if (proxy_type==CONN_TYPE_TRANSPARENT)
{ {
if ((remote_fd = connect_remote((struct sockaddr *)&orig_dst, true)) < 0) if ((remote_fd = connect_remote((struct sockaddr *)&orig_dst, 0)) < 0)
{ {
DLOG_ERR("Failed to connect\n"); DLOG_ERR("Failed to connect\n");
close(local_fd); close(local_fd);
@ -626,6 +619,8 @@ static tproxy_conn_t* add_tcp_connection(int efd, struct tailhead *conn_list,int
if (proxy_type==CONN_TYPE_TRANSPARENT) if (proxy_type==CONN_TYPE_TRANSPARENT)
{ {
sacopy(&conn->dest, (struct sockaddr *)&orig_dst);
if(!(conn->partner = new_conn(remote_fd, true))) if(!(conn->partner = new_conn(remote_fd, true)))
{ {
free_conn(conn); free_conn(conn);
@ -667,6 +662,10 @@ static tproxy_conn_t* add_tcp_connection(int efd, struct tailhead *conn_list,int
TAILQ_INSERT_HEAD(conn_list, conn->partner, conn_ptrs); TAILQ_INSERT_HEAD(conn_list, conn->partner, conn_ptrs);
legs_remote++; legs_remote++;
} }
if (proxy_type==CONN_TYPE_TRANSPARENT)
apply_desync_profile(&conn->track, (struct sockaddr *)&conn->dest);
return conn; return conn;
} }
@ -782,32 +781,26 @@ static bool handle_unsent(tproxy_conn_t *conn)
} }
bool proxy_mode_connect_remote(const struct sockaddr *sa, tproxy_conn_t *conn, struct tailhead *conn_list) static bool proxy_mode_connect_remote(tproxy_conn_t *conn, struct tailhead *conn_list)
{ {
int remote_fd; int remote_fd;
if (params.debug>=1) if (params.debug>=1)
{ {
char ip_port[48]; char ip_port[48];
ntop46_port(sa,ip_port,sizeof(ip_port)); ntop46_port((struct sockaddr *)&conn->dest,ip_port,sizeof(ip_port));
VPRINT("socks target for fd=%d is : %s\n", conn->fd, ip_port); VPRINT("socks target for fd=%d is : %s\n", conn->fd, ip_port);
} }
if (check_local_ip((struct sockaddr *)sa)) if (check_local_ip((struct sockaddr *)&conn->dest))
{ {
VPRINT("Dropping connection to local address for security reasons\n"); VPRINT("Dropping connection to local address for security reasons\n");
socks_send_rep(conn->socks_ver, conn->fd, S5_REP_NOT_ALLOWED_BY_RULESET); socks_send_rep(conn->socks_ver, conn->fd, S5_REP_NOT_ALLOWED_BY_RULESET);
return false; return false;
} }
bool bConnFooling=true; apply_desync_profile(&conn->track, (struct sockaddr *)&conn->dest);
if (conn->track.hostname && params.mss)
{
bConnFooling=HostlistCheck(conn->track.hostname, NULL);
if (!bConnFooling)
VPRINT("0-phase desync hostlist check negative. not acting on this connection.\n");
}
if ((remote_fd = connect_remote(sa, bConnFooling)) < 0) if ((remote_fd = connect_remote((struct sockaddr *)&conn->dest, conn->track.dp ? conn->track.dp->mss : 0)) < 0)
{ {
DLOG_ERR("socks failed to connect (1) errno=%d\n", errno); DLOG_ERR("socks failed to connect (1) errno=%d\n", errno);
socks_send_rep_errno(conn->socks_ver, conn->fd, errno); socks_send_rep_errno(conn->socks_ver, conn->fd, errno);
@ -845,7 +838,6 @@ static bool handle_proxy_mode(tproxy_conn_t *conn, struct tailhead *conn_list)
ssize_t rd,wr; ssize_t rd,wr;
char buf[sizeof(s5_req)]; // s5_req - the largest possible req char buf[sizeof(s5_req)]; // s5_req - the largest possible req
struct sockaddr_storage ss;
// receive proxy control message // receive proxy control message
rd=recv(conn->fd, buf, sizeof(buf), MSG_DONTWAIT); rd=recv(conn->fd, buf, sizeof(buf), MSG_DONTWAIT);
@ -928,10 +920,10 @@ static bool handle_proxy_mode(tproxy_conn_t *conn, struct tailhead *conn_list)
socks4_send_rep(conn->fd, S4_REP_FAILED); socks4_send_rep(conn->fd, S4_REP_FAILED);
return false; return false;
} }
ss.ss_family = AF_INET; conn->dest.ss_family = AF_INET;
((struct sockaddr_in*)&ss)->sin_port = m->port; ((struct sockaddr_in*)&conn->dest)->sin_port = m->port;
((struct sockaddr_in*)&ss)->sin_addr.s_addr = m->ip; ((struct sockaddr_in*)&conn->dest)->sin_addr.s_addr = m->ip;
return proxy_mode_connect_remote((struct sockaddr *)&ss, conn, conn_list); return proxy_mode_connect_remote(conn, conn_list);
} }
break; break;
case S_WAIT_REQUEST: case S_WAIT_REQUEST:
@ -960,16 +952,16 @@ static bool handle_proxy_mode(tproxy_conn_t *conn, struct tailhead *conn_list)
switch(m->atyp) switch(m->atyp)
{ {
case S5_ATYP_IP4: case S5_ATYP_IP4:
ss.ss_family = AF_INET; conn->dest.ss_family = AF_INET;
((struct sockaddr_in*)&ss)->sin_port = m->d4.port; ((struct sockaddr_in*)&conn->dest)->sin_port = m->d4.port;
((struct sockaddr_in*)&ss)->sin_addr = m->d4.addr; ((struct sockaddr_in*)&conn->dest)->sin_addr = m->d4.addr;
break; break;
case S5_ATYP_IP6: case S5_ATYP_IP6:
ss.ss_family = AF_INET6; conn->dest.ss_family = AF_INET6;
((struct sockaddr_in6*)&ss)->sin6_port = m->d6.port; ((struct sockaddr_in6*)&conn->dest)->sin6_port = m->d6.port;
((struct sockaddr_in6*)&ss)->sin6_addr = m->d6.addr; ((struct sockaddr_in6*)&conn->dest)->sin6_addr = m->d6.addr;
((struct sockaddr_in6*)&ss)->sin6_flowinfo = 0; ((struct sockaddr_in6*)&conn->dest)->sin6_flowinfo = 0;
((struct sockaddr_in6*)&ss)->sin6_scope_id = 0; ((struct sockaddr_in6*)&conn->dest)->sin6_scope_id = 0;
break; break;
case S5_ATYP_DOM: case S5_ATYP_DOM:
{ {
@ -1006,7 +998,7 @@ static bool handle_proxy_mode(tproxy_conn_t *conn, struct tailhead *conn_list)
return false; // should not be here. S5_REQ_CONNECT_VALID checks for valid atyp return false; // should not be here. S5_REQ_CONNECT_VALID checks for valid atyp
} }
return proxy_mode_connect_remote((struct sockaddr *)&ss,conn,conn_list); return proxy_mode_connect_remote(conn,conn_list);
} }
break; break;
case S_WAIT_RESOLVE: case S_WAIT_RESOLVE:
@ -1045,7 +1037,8 @@ static bool resolve_complete(struct resolve_item *ri, struct tailhead *conn_list
DBGPRINT("resolve_complete put hostname : %s\n", ri->dom); DBGPRINT("resolve_complete put hostname : %s\n", ri->dom);
conn->track.hostname = strdup(ri->dom); conn->track.hostname = strdup(ri->dom);
} }
return proxy_mode_connect_remote((struct sockaddr *)&ri->ss,conn,conn_list); sacopy(&conn->dest, (struct sockaddr *)&ri->ss);
return proxy_mode_connect_remote(conn,conn_list);
} }
} }
else else
@ -1060,8 +1053,17 @@ static bool resolve_complete(struct resolve_item *ri, struct tailhead *conn_list
static bool in_tamper_out_range(tproxy_conn_t *conn) static bool in_tamper_out_range(tproxy_conn_t *conn)
{ {
return (params.tamper_start_n ? (conn->tnrd+1) : conn->trd) >= params.tamper_start && if (!conn->track.dp) return true;
(!params.tamper_cutoff || (params.tamper_cutoff_n ? (conn->tnrd+1) : conn->trd) < params.tamper_cutoff); bool in_range = \
((conn->track.dp->tamper_start_n ? (conn->tnrd+1) : conn->trd) >= conn->track.dp->tamper_start &&
(!conn->track.dp->tamper_cutoff || (conn->track.dp->tamper_cutoff_n ? (conn->tnrd+1) : conn->trd) < conn->track.dp->tamper_cutoff));
DBGPRINT("tamper_out range check. stream pos %" PRIu64 "(n%" PRIu64 "). tamper range %s%u-%s%u (%s)\n",
conn->trd, conn->tnrd+1,
conn->track.dp ? conn->track.dp->tamper_start_n ? "n" : "" : "?" , conn->track.dp ? conn->track.dp->tamper_start : 0,
conn->track.dp ? conn->track.dp->tamper_cutoff_n ? "n" : "" : "?" , conn->track.dp ? conn->track.dp->tamper_cutoff : 0,
in_range ? "IN RANGE" : "OUT OF RANGE");
return in_range;
} }
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 *split_pos, uint8_t *split_flags)
@ -1072,33 +1074,26 @@ static void tamper(tproxy_conn_t *conn, uint8_t *segment, size_t segment_buffer_
if (conn->remote) if (conn->remote)
{ {
if (conn_partner_alive(conn) && !conn->partner->track.bTamperInCutoff) if (conn_partner_alive(conn) && !conn->partner->track.bTamperInCutoff)
{
tamper_in(&conn->partner->track,segment,segment_buffer_size,segment_size); tamper_in(&conn->partner->track,segment,segment_buffer_size,segment_size);
}
} }
else else
{ {
bool in_range = in_tamper_out_range(conn); if (in_tamper_out_range(conn))
DBGPRINT("tamper_out stream pos %" PRIu64 "(n%" PRIu64 "). tamper range %s%u-%s%u (%s)\n", tamper_out(&conn->track,(struct sockaddr*)&conn->dest,segment,segment_buffer_size,segment_size,split_pos,split_flags);
conn->trd, conn->tnrd+1,
params.tamper_start_n ? "n" : "" , params.tamper_start,
params.tamper_cutoff_n ? "n" : "" , params.tamper_cutoff,
in_range ? "IN RANGE" : "OUT OF RANGE");
if (in_range) tamper_out(&conn->track,segment,segment_buffer_size,segment_size,split_pos,split_flags);
} }
} }
} }
// buffer must have at least one extra byte for OOB // 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) 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)
{ {
ssize_t wr; ssize_t wr;
if (oob) if (oob)
{ {
VPRINT("Sending OOB byte %02X\n", params.oob_byte); VPRINT("Sending OOB byte %02X\n", oob_byte);
uint8_t oob_save; uint8_t oob_save;
oob_save = buf[len]; oob_save = buf[len];
buf[len] = params.oob_byte; buf[len] = oob_byte;
wr = send_or_buffer(sb, fd, buf, len+1, MSG_OOB, ttl); wr = send_or_buffer(sb, fd, buf, len+1, MSG_OOB, ttl);
buf[len] = oob_save; buf[len] = oob_save;
} }
@ -1211,7 +1206,7 @@ static bool handle_epoll(tproxy_conn_t *conn, struct tailhead *conn_list, uint32
{ {
VPRINT("Splitting at pos %zu%s\n", split_pos, (split_flags & SPLIT_FLAG_DISORDER) ? " with disorder" : ""); 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)); 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); DBGPRINT("send_or_buffer(1) fd=%d wr=%zd err=%d\n",conn->partner->fd,wr,errno);
if (wr >= 0) if (wr >= 0)
{ {

View File

@ -54,6 +54,7 @@ struct tproxy_conn
int splice_pipe[2]; int splice_pipe[2];
conn_state_t state; conn_state_t state;
conn_type_t conn_type; conn_type_t conn_type;
struct sockaddr_storage dest;
struct tproxy_conn *partner; // other leg struct tproxy_conn *partner; // other leg
time_t orphan_since; time_t orphan_since;