mirror of
https://github.com/bol-van/zapret.git
synced 2024-11-26 20:20:53 +03:00
tpws: multi-strategy
This commit is contained in:
parent
9684e647ab
commit
d4a7eef17e
@ -164,6 +164,21 @@ bool saconvmapped(struct sockaddr_storage *a)
|
||||
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)
|
||||
{
|
||||
// match 127.0.0.0/8, 0.0.0.0, ::1, ::0, :ffff:127.0.0.0/104, :ffff:0.0.0.0
|
||||
@ -252,16 +267,22 @@ bool pf_parse(const char *s, port_filter *pf)
|
||||
pf->neg=false;
|
||||
if (sscanf(s,"%u-%u",&v1,&v2)==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->to=(uint16_t)v2;
|
||||
}
|
||||
else if (sscanf(s,"%u",&v1)==1)
|
||||
{
|
||||
if (!v1 || v1>65535) return false;
|
||||
if (v1>65535) return false;
|
||||
pf->to=pf->from=(uint16_t)v1;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
// deny all case
|
||||
if (!pf->from && !pf->to) pf->neg=true;
|
||||
return true;
|
||||
}
|
||||
bool pf_is_empty(const port_filter *pf)
|
||||
{
|
||||
return !pf->neg && !pf->from && !pf->to;
|
||||
}
|
||||
|
@ -25,6 +25,8 @@ uint16_t saport(const struct sockaddr *sa);
|
||||
// true = was converted
|
||||
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_linklocal(const struct sockaddr_in6* a);
|
||||
bool is_private6(const struct sockaddr_in6* a);
|
||||
@ -55,6 +57,7 @@ typedef struct
|
||||
} port_filter;
|
||||
bool pf_in_range(uint16_t port, const port_filter *pf);
|
||||
bool pf_parse(const char *s, port_filter *pf);
|
||||
bool pf_is_empty(const port_filter *pf);
|
||||
|
||||
#ifndef IN_LOOPBACK
|
||||
#define IN_LOOPBACK(a) ((((uint32_t) (a)) & 0xff000000) == 0x7f000000)
|
||||
|
@ -154,35 +154,53 @@ static bool HostlistCheck_(strpool *hostlist, strpool *hostlist_exclude, const c
|
||||
return true;
|
||||
}
|
||||
|
||||
// return : true = apply fooling, false = do not apply
|
||||
bool HostlistCheck(const char *host, bool *excluded)
|
||||
static bool LoadIncludeHostListsForProfile(struct desync_profile *dp)
|
||||
{
|
||||
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);
|
||||
if (t!=params.hostlist_auto_mod_time)
|
||||
dp->hostlist_auto_mod_time = file_mod_time(dp->hostlist_auto_filename);
|
||||
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");
|
||||
if (!LoadIncludeHostLists())
|
||||
DLOG_CONDUP("Autohostlist '%s' from profile %d was modified. Reloading include hostlists for this profile.\n",dp->hostlist_auto_filename, dp->n);
|
||||
if (!LoadIncludeHostListsForProfile(dp))
|
||||
{
|
||||
// what will we do without hostlist ?? sure, gonna die
|
||||
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()
|
||||
{
|
||||
if (!LoadHostLists(¶ms.hostlist, ¶ms.hostlist_files))
|
||||
return false;
|
||||
if (*params.hostlist_auto_filename)
|
||||
params.hostlist_auto_mod_time = file_mod_time(params.hostlist_auto_filename);
|
||||
struct desync_profile_list *dpl;
|
||||
LIST_FOREACH(dpl, ¶ms.desync_profiles, next)
|
||||
if (!LoadIncludeHostListsForProfile(&dpl->dp))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
bool LoadExcludeHostLists()
|
||||
{
|
||||
return LoadHostLists(¶ms.hostlist_exclude, ¶ms.hostlist_exclude_files);
|
||||
struct desync_profile_list *dpl;
|
||||
LIST_FOREACH(dpl, ¶ms.desync_profiles, next)
|
||||
if (!LoadHostLists(&dpl->dp.hostlist_exclude, &dpl->dp.hostlist_exclude_files))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "pools.h"
|
||||
#include "params.h"
|
||||
|
||||
bool AppendHostList(strpool **hostlist, char *filename);
|
||||
bool LoadHostLists(strpool **hostlist, struct str_list_head *file_list);
|
||||
@ -10,4 +11,4 @@ bool LoadExcludeHostLists();
|
||||
bool NonEmptyHostlist(strpool **hostlist);
|
||||
bool SearchHostList(strpool *hostlist, const char *host);
|
||||
// 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);
|
@ -138,3 +138,56 @@ int HOSTLIST_DEBUGLOG_APPEND(const char *format, ...)
|
||||
else
|
||||
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(¶ms.desync_profiles);
|
||||
if (dpl)
|
||||
{
|
||||
while ((dpn=LIST_NEXT(dpl,next))) dpl = dpn;
|
||||
LIST_INSERT_AFTER(dpl, entry, next);
|
||||
}
|
||||
else
|
||||
LIST_INSERT_HEAD(¶ms.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;
|
||||
}
|
||||
|
@ -27,27 +27,10 @@ struct bind_s
|
||||
|
||||
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 binds_last;
|
||||
bool bind_wait_only;
|
||||
uint16_t port;
|
||||
int n; // number of the profile
|
||||
|
||||
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;
|
||||
int hostpad;
|
||||
char hostspell[4];
|
||||
@ -60,30 +43,59 @@ struct params_s
|
||||
bool disorder, disorder_http, disorder_tls;
|
||||
bool oob, oob_http, oob_tls;
|
||||
uint8_t oob_byte;
|
||||
int ttl_default;
|
||||
|
||||
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;
|
||||
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_in6 connect_bind6;
|
||||
char connect_bind6_ifname[IF_NAMESIZE];
|
||||
|
||||
int debug;
|
||||
enum log_target debug_target;
|
||||
char debug_logfile[PATH_MAX];
|
||||
uint8_t proxy_type;
|
||||
bool no_resolve;
|
||||
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)
|
||||
bool pf_enable;
|
||||
@ -91,6 +103,13 @@ struct params_s
|
||||
#ifdef SPLICE_PRESENT
|
||||
bool nosplice;
|
||||
#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;
|
||||
|
@ -25,12 +25,45 @@ bool IsHttp(const uint8_t *data, size_t 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: ..."
|
||||
bool HttpFindHost(uint8_t **pHost,uint8_t *buf,size_t bs)
|
||||
{
|
||||
if (!*pHost)
|
||||
{
|
||||
*pHost = memmem(buf, bs, "\nHost:", 6);
|
||||
*pHost = FindHostIn(buf, bs);
|
||||
if (*pHost) (*pHost)++;
|
||||
}
|
||||
return !!*pHost;
|
||||
@ -39,7 +72,7 @@ bool HttpFindHostConst(const uint8_t **pHost,const uint8_t *buf,size_t bs)
|
||||
{
|
||||
if (!*pHost)
|
||||
{
|
||||
*pHost = memmem(buf, bs, "\nHost:", 6);
|
||||
*pHost = FindHostInConst(buf, bs);
|
||||
if (*pHost) (*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)
|
||||
{
|
||||
const uint8_t *method, *host;
|
||||
const uint8_t *method, *host=NULL;
|
||||
int i;
|
||||
|
||||
switch(tpos_type)
|
||||
|
312
tpws/tamper.c
312
tpws/tamper.c
@ -1,24 +1,87 @@
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include "tamper.h"
|
||||
#include "params.h"
|
||||
#include "hostlist.h"
|
||||
#include "protocol.h"
|
||||
#include "helpers.h"
|
||||
#include <string.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(¶ms.desync_profiles, dest->sa_family==AF_INET6, saport(dest), ctrack->hostname);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 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;
|
||||
size_t method_len = 0, pos;
|
||||
size_t tpos, spos;
|
||||
const char *method;
|
||||
bool bBypass = false, bHaveHost = false, bHostExcluded = false;
|
||||
bool bHaveHost = false;
|
||||
char *pc, Host[256];
|
||||
t_l7proto l7proto;
|
||||
|
||||
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_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;
|
||||
VPRINT("Data block looks like http request start : %s\n", method);
|
||||
if (!ctrack->l7proto) ctrack->l7proto=HTTP;
|
||||
// cpu saving : we search host only if and when required. we do not research host every time we need its position
|
||||
if ((params.hostlist || params.hostlist_exclude) && HttpFindHost(&pHost,segment,*size))
|
||||
l7proto=HTTP;
|
||||
if (HttpFindHost(&pHost,segment,*size))
|
||||
{
|
||||
p = pHost + 5;
|
||||
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);
|
||||
Host[pp - p] = '\0';
|
||||
bHaveHost = true;
|
||||
VPRINT("Requested Host is : %s\n", Host);
|
||||
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;
|
||||
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
|
||||
}
|
||||
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");
|
||||
if (params.unixeol)
|
||||
if (ctrack->dp->unixeol)
|
||||
{
|
||||
memmove(segment + 1, segment, *size);
|
||||
(*size)++;;
|
||||
@ -79,7 +185,7 @@ void tamper_out(t_ctrack *ctrack, uint8_t *segment,size_t segment_buffer_size,si
|
||||
}
|
||||
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
|
||||
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
|
||||
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;
|
||||
while (p < (segment + *size) && *p != '\r' && *p != '\n') p++;
|
||||
if (p < (segment + *size))
|
||||
{
|
||||
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);
|
||||
*p = params.hostdot ? '.' : '\t'; // insert dot or tab
|
||||
*p = ctrack->dp->hostdot ? '.' : '\t'; // insert dot or tab
|
||||
(*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;
|
||||
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++)
|
||||
*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;
|
||||
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);
|
||||
(*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);
|
||||
memcpy(pHost, params.hostspell, 4);
|
||||
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, 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]
|
||||
char s[8];
|
||||
size_t hsize = params.unixeol ? 8 : 9;
|
||||
size_t hostpad = params.hostpad<hsize ? hsize : params.hostpad;
|
||||
size_t hsize = ctrack->dp->unixeol ? 8 : 9;
|
||||
size_t hostpad = ctrack->dp->hostpad<hsize ? hsize : ctrack->dp->hostpad;
|
||||
|
||||
if ((hsize+*size)>segment_buffer_size)
|
||||
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;
|
||||
memset(p,'a'+rand()%('z'-'a'+1),padsize);
|
||||
p+=padsize;
|
||||
if (params.unixeol)
|
||||
if (ctrack->dp->unixeol)
|
||||
*p++='\n';
|
||||
else
|
||||
{
|
||||
@ -171,40 +277,16 @@ void tamper_out(t_ctrack *ctrack, uint8_t *segment,size_t segment_buffer_size,si
|
||||
pHost = NULL; // invalidate
|
||||
}
|
||||
}
|
||||
*split_pos = HttpPos(params.split_http_req, params.split_pos, segment, *size);
|
||||
if (params.disorder_http) *split_flags |= SPLIT_FLAG_DISORDER;
|
||||
if (params.oob_http) *split_flags |= SPLIT_FLAG_OOB;
|
||||
}
|
||||
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);
|
||||
*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;
|
||||
break;
|
||||
|
||||
case TLS:
|
||||
spos = TLSPos(ctrack->dp->split_tls, ctrack->dp->split_pos, segment, *size, 0);
|
||||
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)
|
||||
{
|
||||
// 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)
|
||||
{
|
||||
VPRINT("split pos %zu\n",spos);
|
||||
*split_pos = spos;
|
||||
}
|
||||
|
||||
if (params.disorder_tls) *split_flags |= SPLIT_FLAG_DISORDER;
|
||||
if (params.oob_tls) *split_flags |= SPLIT_FLAG_OOB;
|
||||
}
|
||||
}
|
||||
else if (params.split_any_protocol && params.split_pos < *size)
|
||||
*split_pos = params.split_pos;
|
||||
if (ctrack->dp->disorder_tls) *split_flags |= SPLIT_FLAG_DISORDER;
|
||||
if (ctrack->dp->oob_tls) *split_flags |= SPLIT_FLAG_OOB;
|
||||
|
||||
if (bHaveHost && bBypass && !bHostExcluded && *params.hostlist_auto_filename)
|
||||
{
|
||||
DBGPRINT("tamper_out put hostname : %s\n", Host);
|
||||
if (ctrack->hostname) free(ctrack->hostname);
|
||||
ctrack->hostname=strdup(Host);
|
||||
break;
|
||||
|
||||
default:
|
||||
if (ctrack->dp->split_any_protocol && ctrack->dp->split_pos < *size)
|
||||
*split_pos = ctrack->dp->split_pos;
|
||||
}
|
||||
if (params.disorder) *split_flags |= SPLIT_FLAG_DISORDER;
|
||||
if (params.oob) *split_flags |= SPLIT_FLAG_OOB;
|
||||
|
||||
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(const char *hostname)
|
||||
static void auto_hostlist_reset_fail_counter(struct desync_profile *dp, const char *hostname)
|
||||
{
|
||||
if (hostname)
|
||||
{
|
||||
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)
|
||||
{
|
||||
HostFailPoolDel(¶ms.hostlist_auto_fail_counters, fail_counter);
|
||||
VPRINT("auto hostlist : %s : fail counter reset. website is working.\n", hostname);
|
||||
HOSTLIST_DEBUGLOG_APPEND("%s : fail counter reset. website is working.", hostname);
|
||||
HostFailPoolDel(&dp->hostlist_auto_fail_counters, fail_counter);
|
||||
VPRINT("auto hostlist (profile %d) : %s : fail counter reset. website is working.\n", dp->n, 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;
|
||||
|
||||
fail_counter = HostFailPoolFind(params.hostlist_auto_fail_counters, hostname);
|
||||
fail_counter = HostFailPoolFind(dp->hostlist_auto_fail_counters, hostname);
|
||||
if (!fail_counter)
|
||||
{
|
||||
fail_counter = HostFailPoolAdd(¶ms.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)
|
||||
{
|
||||
DLOG_ERR("HostFailPoolAdd: out of memory\n");
|
||||
@ -281,35 +357,35 @@ static void auto_hostlist_failed(const char *hostname)
|
||||
}
|
||||
}
|
||||
fail_counter->counter++;
|
||||
VPRINT("auto hostlist : %s : fail counter %d/%d\n", hostname, fail_counter->counter, params.hostlist_auto_fail_threshold);
|
||||
HOSTLIST_DEBUGLOG_APPEND("%s : fail counter %d/%d", hostname, fail_counter->counter, params.hostlist_auto_fail_threshold);
|
||||
if (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 : 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)
|
||||
{
|
||||
VPRINT("auto hostlist : fail threshold reached. adding %s to auto hostlist\n", hostname);
|
||||
HostFailPoolDel(¶ms.hostlist_auto_fail_counters, fail_counter);
|
||||
VPRINT("auto hostlist (profile %d) : fail threshold reached. adding %s to auto hostlist\n", dp->n , hostname);
|
||||
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;
|
||||
if (!HostlistCheck(hostname, &bExcluded) && !bExcluded)
|
||||
if (!HostlistCheck(dp, hostname, &bExcluded) && !bExcluded)
|
||||
{
|
||||
VPRINT("auto hostlist : adding %s\n", hostname);
|
||||
HOSTLIST_DEBUGLOG_APPEND("%s : adding", hostname);
|
||||
if (!StrPoolAddStr(¶ms.hostlist, hostname))
|
||||
VPRINT("auto hostlist (profile %d) : adding %s to %s\n", dp->n, hostname, dp->hostlist_auto_filename);
|
||||
HOSTLIST_DEBUGLOG_APPEND("%s : profile %d : adding to %s", hostname, dp->n, dp->hostlist_auto_filename);
|
||||
if (!StrPoolAddStr(&dp->hostlist, hostname))
|
||||
{
|
||||
DLOG_ERR("StrPoolAddStr out of memory\n");
|
||||
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:");
|
||||
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
|
||||
{
|
||||
VPRINT("auto hostlist : NOT adding %s\n", hostname);
|
||||
HOSTLIST_DEBUGLOG_APPEND("%s : NOT adding, duplicate detected", hostname);
|
||||
VPRINT("auto hostlist (profile %d) : NOT adding %s\n", dp->n, 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);
|
||||
|
||||
if (*params.hostlist_auto_filename)
|
||||
if (ctrack->dp && ctrack->b_ah_check)
|
||||
{
|
||||
HostFailPoolPurgeRateLimited(¶ms.hostlist_auto_fail_counters);
|
||||
HostFailPoolPurgeRateLimited(&ctrack->dp->hostlist_auto_fail_counters);
|
||||
|
||||
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)
|
||||
{
|
||||
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
|
||||
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
|
||||
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;
|
||||
}
|
||||
@ -356,30 +430,32 @@ void rst_in(t_ctrack *ctrack)
|
||||
{
|
||||
DBGPRINT("rst_in hostname=%s\n", ctrack->hostname);
|
||||
|
||||
if (!*params.hostlist_auto_filename) return;
|
||||
|
||||
HostFailPoolPurgeRateLimited(¶ms.hostlist_auto_fail_counters);
|
||||
|
||||
if (!ctrack->bTamperInCutoff && ctrack->hostname)
|
||||
if (ctrack->dp && ctrack->b_ah_check)
|
||||
{
|
||||
VPRINT("incoming RST detected for hostname %s\n", ctrack->hostname);
|
||||
HOSTLIST_DEBUGLOG_APPEND("%s : incoming RST", ctrack->hostname);
|
||||
auto_hostlist_failed(ctrack->hostname);
|
||||
HostFailPoolPurgeRateLimited(&ctrack->dp->hostlist_auto_fail_counters);
|
||||
|
||||
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)
|
||||
{
|
||||
DBGPRINT("hup_out hostname=%s\n", ctrack->hostname);
|
||||
|
||||
if (!*params.hostlist_auto_filename) return;
|
||||
|
||||
HostFailPoolPurgeRateLimited(¶ms.hostlist_auto_fail_counters);
|
||||
|
||||
if (!ctrack->bTamperInCutoff && ctrack->hostname)
|
||||
if (ctrack->dp && ctrack->b_ah_check)
|
||||
{
|
||||
// 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 : client closed connection without server reply", ctrack->hostname);
|
||||
auto_hostlist_failed(ctrack->hostname);
|
||||
HostFailPoolPurgeRateLimited(&ctrack->dp->hostlist_auto_fail_counters);
|
||||
|
||||
if (!ctrack->bTamperInCutoff && 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,8 @@
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "params.h"
|
||||
|
||||
#define SPLIT_FLAG_DISORDER 0x01
|
||||
#define SPLIT_FLAG_OOB 0x02
|
||||
|
||||
@ -14,10 +16,14 @@ typedef struct
|
||||
t_l7proto l7proto;
|
||||
bool bFirstReplyChecked;
|
||||
bool bTamperInCutoff;
|
||||
bool b_ah_check;
|
||||
char *hostname;
|
||||
struct desync_profile *dp; // desync profile cache
|
||||
} 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);
|
||||
// connection reset by remote leg
|
||||
void rst_in(t_ctrack *ctrack);
|
||||
|
251
tpws/tpws.c
251
tpws/tpws.c
@ -46,8 +46,7 @@ bool bHup = false;
|
||||
static void onhup(int sig)
|
||||
{
|
||||
printf("HUP received !\n");
|
||||
if (params.hostlist || params.hostlist_exclude)
|
||||
printf("Will reload hostlists on next request\n");
|
||||
printf("Will reload hostlist on next request (if any)\n");
|
||||
bHup = true;
|
||||
}
|
||||
// should be called in normal execution
|
||||
@ -67,7 +66,14 @@ void dohup(void)
|
||||
static void onusr2(int sig)
|
||||
{
|
||||
printf("\nHOSTFAIL POOL DUMP\n");
|
||||
HostFailPoolDump(params.hostlist_auto_fail_counters);
|
||||
|
||||
struct desync_profile_list *dpl;
|
||||
LIST_FOREACH(dpl, ¶ms.desync_profiles, next)
|
||||
{
|
||||
printf("\nDESYNC PROFILE %d\n",dpl->dp.n);
|
||||
HostFailPoolDump(dpl->dp.hostlist_auto_fail_counters);
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
@ -170,7 +176,11 @@ static void exithelp(void)
|
||||
#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-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-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"
|
||||
@ -203,7 +213,6 @@ static void exithelp(void)
|
||||
" --tlsrec-pos=<pos>\t\t\t; make 2 TLS records. split at specified pos\n"
|
||||
#ifdef __linux__
|
||||
" --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
|
||||
" --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",
|
||||
@ -216,11 +225,7 @@ static void exithelp(void)
|
||||
}
|
||||
static void cleanup_params(void)
|
||||
{
|
||||
strlist_destroy(¶ms.hostlist_files);
|
||||
strlist_destroy(¶ms.hostlist_exclude_files);
|
||||
StrPoolDestroy(¶ms.hostlist_exclude);
|
||||
StrPoolDestroy(¶ms.hostlist);
|
||||
HostFailPoolDestroy(¶ms.hostlist_auto_fail_counters);
|
||||
dp_list_destroy(¶ms.desync_profiles);
|
||||
}
|
||||
static void exithelp_clean(void)
|
||||
{
|
||||
@ -285,6 +290,32 @@ bool parse_tlspos(const char *s, enum tlspos *pos)
|
||||
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[])
|
||||
{
|
||||
@ -292,18 +323,13 @@ void parse_params(int argc, char *argv[])
|
||||
int v, i;
|
||||
|
||||
memset(¶ms, 0, sizeof(params));
|
||||
memcpy(params.hostspell, "host", 4); // default hostspell
|
||||
params.maxconn = DEFAULT_MAX_CONN;
|
||||
params.max_orphan_time = DEFAULT_MAX_ORPHAN_TIME;
|
||||
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__)
|
||||
params.tcp_user_timeout_local = DEFAULT_TCP_USER_TIMEOUT_LOCAL;
|
||||
params.tcp_user_timeout_remote = DEFAULT_TCP_USER_TIMEOUT_REMOTE;
|
||||
#endif
|
||||
LIST_INIT(¶ms.hostlist_files);
|
||||
LIST_INIT(¶ms.hostlist_exclude_files);
|
||||
|
||||
#if defined(__OpenBSD__) || defined(__APPLE__)
|
||||
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;
|
||||
}
|
||||
|
||||
struct desync_profile_list *dpl;
|
||||
struct desync_profile *dp;
|
||||
int desync_profile_count=0;
|
||||
if (!(dpl = dp_list_add(¶ms.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[] = {
|
||||
{ "help",no_argument,0,0 },// optidx=0
|
||||
{ "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-cutoff",required_argument,0,0 },// optidx=54
|
||||
{ "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__)
|
||||
{ "enable-pf",no_argument,0,0 },// optidx=56
|
||||
{ "enable-pf",no_argument,0,0 },// optidx=59
|
||||
#elif defined(__APPLE__)
|
||||
{ "local-tcp-user-timeout",required_argument,0,0 },// optidx=56
|
||||
{ "remote-tcp-user-timeout",required_argument,0,0 },// optidx=57
|
||||
{ "local-tcp-user-timeout",required_argument,0,0 },// optidx=59
|
||||
{ "remote-tcp-user-timeout",required_argument,0,0 },// optidx=60
|
||||
#elif defined(__linux__)
|
||||
{ "local-tcp-user-timeout",required_argument,0,0 },// optidx=56
|
||||
{ "remote-tcp-user-timeout",required_argument,0,0 },// optidx=57
|
||||
{ "mss",required_argument,0,0 },// optidx=58
|
||||
{ "mss-pf",required_argument,0,0 },// optidx=59
|
||||
{ "local-tcp-user-timeout",required_argument,0,0 },// optidx=59
|
||||
{ "remote-tcp-user-timeout",required_argument,0,0 },// optidx=60
|
||||
{ "mss",required_argument,0,0 },// optidx=61
|
||||
#ifdef SPLICE_PRESENT
|
||||
{ "nosplice",no_argument,0,0 },// optidx=60
|
||||
{ "nosplice",no_argument,0,0 },// optidx=62
|
||||
#endif
|
||||
#endif
|
||||
{ "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;
|
||||
case 17: /* hostcase */
|
||||
params.hostcase = true;
|
||||
dp->hostcase = true;
|
||||
params.tamper = true;
|
||||
break;
|
||||
case 18: /* hostspell */
|
||||
@ -522,28 +563,28 @@ void parse_params(int argc, char *argv[])
|
||||
DLOG_ERR("hostspell must be exactly 4 chars long\n");
|
||||
exit_clean(1);
|
||||
}
|
||||
params.hostcase = true;
|
||||
memcpy(params.hostspell, optarg, 4);
|
||||
dp->hostcase = true;
|
||||
memcpy(dp->hostspell, optarg, 4);
|
||||
params.tamper = true;
|
||||
break;
|
||||
case 19: /* hostdot */
|
||||
params.hostdot = true;
|
||||
dp->hostdot = true;
|
||||
params.tamper = true;
|
||||
break;
|
||||
case 20: /* hostnospace */
|
||||
params.hostnospace = true;
|
||||
dp->hostnospace = true;
|
||||
params.tamper = true;
|
||||
break;
|
||||
case 21: /* hostpad */
|
||||
params.hostpad = atoi(optarg);
|
||||
dp->hostpad = atoi(optarg);
|
||||
params.tamper = true;
|
||||
break;
|
||||
case 22: /* domcase */
|
||||
params.domcase = true;
|
||||
dp->domcase = true;
|
||||
params.tamper = true;
|
||||
break;
|
||||
case 23: /* split-http-req */
|
||||
if (!parse_httpreqpos(optarg, ¶ms.split_http_req))
|
||||
if (!parse_httpreqpos(optarg, &dp->split_http_req))
|
||||
{
|
||||
DLOG_ERR("Invalid argument for split-http-req\n");
|
||||
exit_clean(1);
|
||||
@ -551,7 +592,7 @@ void parse_params(int argc, char *argv[])
|
||||
params.tamper = true;
|
||||
break;
|
||||
case 24: /* split-tls */
|
||||
if (!parse_tlspos(optarg, ¶ms.split_tls))
|
||||
if (!parse_tlspos(optarg, &dp->split_tls))
|
||||
{
|
||||
DLOG_ERR("Invalid argument for split-tls\n");
|
||||
exit_clean(1);
|
||||
@ -561,7 +602,7 @@ void parse_params(int argc, char *argv[])
|
||||
case 25: /* split-pos */
|
||||
i = atoi(optarg);
|
||||
if (i>0)
|
||||
params.split_pos = i;
|
||||
dp->split_pos = i;
|
||||
else
|
||||
{
|
||||
DLOG_ERR("Invalid argument for split-pos\n");
|
||||
@ -570,13 +611,13 @@ void parse_params(int argc, char *argv[])
|
||||
params.tamper = true;
|
||||
break;
|
||||
case 26: /* split-any-protocol */
|
||||
params.split_any_protocol = true;
|
||||
dp->split_any_protocol = true;
|
||||
break;
|
||||
case 27: /* disorder */
|
||||
if (optarg)
|
||||
{
|
||||
if (!strcmp(optarg,"http")) params.disorder_http=true;
|
||||
else if (!strcmp(optarg,"tls")) params.disorder_tls=true;
|
||||
if (!strcmp(optarg,"http")) dp->disorder_http=true;
|
||||
else if (!strcmp(optarg,"tls")) dp->disorder_tls=true;
|
||||
else
|
||||
{
|
||||
DLOG_ERR("Invalid argument for disorder\n");
|
||||
@ -584,14 +625,14 @@ void parse_params(int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
else
|
||||
params.disorder = true;
|
||||
dp->disorder = true;
|
||||
save_default_ttl();
|
||||
break;
|
||||
case 28: /* oob */
|
||||
if (optarg)
|
||||
{
|
||||
if (!strcmp(optarg,"http")) params.oob_http=true;
|
||||
else if (!strcmp(optarg,"tls")) params.oob_tls=true;
|
||||
if (!strcmp(optarg,"http")) dp->oob_http=true;
|
||||
else if (!strcmp(optarg,"tls")) dp->oob_tls=true;
|
||||
else
|
||||
{
|
||||
DLOG_ERR("Invalid argument for oob\n");
|
||||
@ -599,39 +640,39 @@ void parse_params(int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
else
|
||||
params.oob = true;
|
||||
dp->oob = true;
|
||||
break;
|
||||
case 29: /* oob-data */
|
||||
{
|
||||
size_t l = strlen(optarg);
|
||||
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)
|
||||
{
|
||||
DLOG_ERR("Invalid argument for oob-data\n");
|
||||
exit_clean(1);
|
||||
}
|
||||
else params.oob_byte = (uint8_t)bt;
|
||||
else dp->oob_byte = (uint8_t)bt;
|
||||
}
|
||||
break;
|
||||
case 30: /* methodspace */
|
||||
params.methodspace = true;
|
||||
dp->methodspace = true;
|
||||
params.tamper = true;
|
||||
break;
|
||||
case 31: /* methodeol */
|
||||
params.methodeol = true;
|
||||
dp->methodeol = true;
|
||||
params.tamper = true;
|
||||
break;
|
||||
case 32: /* hosttab */
|
||||
params.hosttab = true;
|
||||
dp->hosttab = true;
|
||||
params.tamper = true;
|
||||
break;
|
||||
case 33: /* unixeol */
|
||||
params.unixeol = true;
|
||||
dp->unixeol = true;
|
||||
params.tamper = true;
|
||||
break;
|
||||
case 34: /* tlsrec */
|
||||
if (!parse_tlspos(optarg, ¶ms.tlsrec))
|
||||
if (!parse_tlspos(optarg, &dp->tlsrec))
|
||||
{
|
||||
DLOG_ERR("Invalid argument for tlsrec\n");
|
||||
exit_clean(1);
|
||||
@ -639,8 +680,8 @@ void parse_params(int argc, char *argv[])
|
||||
params.tamper = true;
|
||||
break;
|
||||
case 35: /* tlsrec-pos */
|
||||
if ((params.tlsrec_pos = atoi(optarg))>0)
|
||||
params.tlsrec = tlspos_pos;
|
||||
if ((dp->tlsrec_pos = atoi(optarg))>0)
|
||||
dp->tlsrec = tlspos_pos;
|
||||
else
|
||||
{
|
||||
DLOG_ERR("Invalid argument for tlsrec-pos\n");
|
||||
@ -649,7 +690,7 @@ void parse_params(int argc, char *argv[])
|
||||
params.tamper = true;
|
||||
break;
|
||||
case 36: /* hostlist */
|
||||
if (!strlist_add(¶ms.hostlist_files, optarg))
|
||||
if (!strlist_add(&dp->hostlist_files, optarg))
|
||||
{
|
||||
DLOG_ERR("strlist_add failed\n");
|
||||
exit_clean(1);
|
||||
@ -657,7 +698,7 @@ void parse_params(int argc, char *argv[])
|
||||
params.tamper = true;
|
||||
break;
|
||||
case 37: /* hostlist-exclude */
|
||||
if (!strlist_add(¶ms.hostlist_exclude_files, optarg))
|
||||
if (!strlist_add(&dp->hostlist_exclude_files, optarg))
|
||||
{
|
||||
DLOG_ERR("strlist_add failed\n");
|
||||
exit_clean(1);
|
||||
@ -665,9 +706,9 @@ void parse_params(int argc, char *argv[])
|
||||
params.tamper = true;
|
||||
break;
|
||||
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);
|
||||
}
|
||||
{
|
||||
@ -687,26 +728,26 @@ void parse_params(int argc, char *argv[])
|
||||
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 (!strlist_add(¶ms.hostlist_files, optarg))
|
||||
if (!strlist_add(&dp->hostlist_files, optarg))
|
||||
{
|
||||
DLOG_ERR("strlist_add failed\n");
|
||||
exit_clean(1);
|
||||
}
|
||||
strncpy(params.hostlist_auto_filename, optarg, sizeof(params.hostlist_auto_filename));
|
||||
params.hostlist_auto_filename[sizeof(params.hostlist_auto_filename) - 1] = '\0';
|
||||
strncpy(dp->hostlist_auto_filename, optarg, sizeof(dp->hostlist_auto_filename));
|
||||
dp->hostlist_auto_filename[sizeof(dp->hostlist_auto_filename) - 1] = '\0';
|
||||
params.tamper = true; // need to detect blocks and update autohostlist. cannot just slice.
|
||||
break;
|
||||
case 39: /* hostlist-auto-fail-threshold */
|
||||
params.hostlist_auto_fail_threshold = (uint8_t)atoi(optarg);
|
||||
if (params.hostlist_auto_fail_threshold<1 || params.hostlist_auto_fail_threshold>20)
|
||||
dp->hostlist_auto_fail_threshold = (uint8_t)atoi(optarg);
|
||||
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");
|
||||
exit_clean(1);
|
||||
}
|
||||
break;
|
||||
case 40: /* hostlist-auto-fail-time */
|
||||
params.hostlist_auto_fail_time = (uint8_t)atoi(optarg);
|
||||
if (params.hostlist_auto_fail_time<1)
|
||||
dp->hostlist_auto_fail_time = (uint8_t)atoi(optarg);
|
||||
if (dp->hostlist_auto_fail_time<1)
|
||||
{
|
||||
DLOG_ERR("auto hostlist fail time is not valid\n");
|
||||
exit_clean(1);
|
||||
@ -820,26 +861,28 @@ void parse_params(int argc, char *argv[])
|
||||
const char *p=optarg;
|
||||
if (*p=='n')
|
||||
{
|
||||
params.tamper_start_n=true;
|
||||
dp->tamper_start_n=true;
|
||||
p++;
|
||||
}
|
||||
else
|
||||
params.tamper_start_n=false;
|
||||
params.tamper_start = atoi(p);
|
||||
dp->tamper_start_n=false;
|
||||
dp->tamper_start = atoi(p);
|
||||
}
|
||||
params.tamper_lim = true;
|
||||
break;
|
||||
case 54: /* tamper-cutoff */
|
||||
{
|
||||
const char *p=optarg;
|
||||
if (*p=='n')
|
||||
{
|
||||
params.tamper_cutoff_n=true;
|
||||
dp->tamper_cutoff_n=true;
|
||||
p++;
|
||||
}
|
||||
else
|
||||
params.tamper_cutoff_n=false;
|
||||
params.tamper_cutoff = atoi(p);
|
||||
dp->tamper_cutoff_n=false;
|
||||
dp->tamper_cutoff = atoi(p);
|
||||
}
|
||||
params.tamper_lim = true;
|
||||
break;
|
||||
case 55: /* connect-bind-addr */
|
||||
{
|
||||
@ -868,12 +911,37 @@ void parse_params(int argc, char *argv[])
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case 56: /* new */
|
||||
if (!(dpl = dp_list_add(¶ms.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__)
|
||||
case 56: /* enable-pf */
|
||||
case 59: /* enable-pf */
|
||||
params.pf_enable = true;
|
||||
break;
|
||||
#elif defined(__linux__) || defined(__APPLE__)
|
||||
case 56: /* local-tcp-user-timeout */
|
||||
case 59: /* local-tcp-user-timeout */
|
||||
params.tcp_user_timeout_local = atoi(optarg);
|
||||
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);
|
||||
}
|
||||
break;
|
||||
case 57: /* remote-tcp-user-timeout */
|
||||
case 60: /* remote-tcp-user-timeout */
|
||||
params.tcp_user_timeout_remote = atoi(optarg);
|
||||
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
|
||||
|
||||
#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
|
||||
params.mss = atoi(optarg);
|
||||
if (params.mss<88 || params.mss>32767)
|
||||
dp->mss = atoi(optarg);
|
||||
if (dp->mss<88 || dp->mss>32767)
|
||||
{
|
||||
DLOG_ERR("Invalid value for MSS. Linux accepts MSS 88-32767.\n");
|
||||
exit_clean(1);
|
||||
}
|
||||
break;
|
||||
case 59: /* mss-pf */
|
||||
if (!pf_parse(optarg,¶ms.mss_pf))
|
||||
{
|
||||
DLOG_ERR("Invalid MSS port filter.\n");
|
||||
exit_clean(1);
|
||||
}
|
||||
break;
|
||||
#ifdef SPLICE_PRESENT
|
||||
case 60: /* nosplice */
|
||||
case 62: /* nosplice */
|
||||
params.nosplice = true;
|
||||
break;
|
||||
#endif
|
||||
@ -925,22 +986,36 @@ void parse_params(int argc, char *argv[])
|
||||
{
|
||||
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(¶ms.desync_profiles)))
|
||||
{
|
||||
DLOG_ERR("Cannot split with --skip-nodelay\n");
|
||||
DLOG_ERR("desync_profile_add: out of memory\n");
|
||||
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, ¶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 (*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())
|
||||
{
|
||||
DLOG_ERR("Include hostlist load failed\n");
|
||||
exit_clean(1);
|
||||
}
|
||||
if (*params.hostlist_auto_filename) NonEmptyHostlist(¶ms.hostlist);
|
||||
if (!LoadExcludeHostLists())
|
||||
{
|
||||
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
|
||||
// 16 for listen_fd, epoll, hostlist, ...
|
||||
#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
|
||||
fdmax = 2 * params.maxconn;
|
||||
#endif
|
||||
|
115
tpws/tpws_conn.c
115
tpws/tpws_conn.c
@ -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
|
||||
//remote_addr.
|
||||
//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;
|
||||
|
||||
@ -406,24 +406,18 @@ static int connect_remote(const struct sockaddr *remote_addr, bool bApplyConnect
|
||||
close(remote_fd);
|
||||
return -1;
|
||||
}
|
||||
if (bApplyConnectionFooling && params.mss)
|
||||
#ifdef __linux__
|
||||
if (mss)
|
||||
{
|
||||
uint16_t port = saport(remote_addr);
|
||||
if (pf_in_range(port,¶ms.mss_pf))
|
||||
VPRINT("Setting MSS %d\n", mss);
|
||||
if (setsockopt(remote_fd, IPPROTO_TCP, TCP_MAXSEG, &mss, sizeof(int)) <0)
|
||||
{
|
||||
VPRINT("Setting MSS %d\n",params.mss);
|
||||
if (setsockopt(remote_fd, IPPROTO_TCP, TCP_MAXSEG, ¶ms.mss, sizeof(int)) <0)
|
||||
{
|
||||
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);
|
||||
DLOG_PERROR("setsockopt (TCP_MAXSEG, connect_remote)");
|
||||
close(remote_fd);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// if no bind address specified - address family will be 0 in params_connect_bindX
|
||||
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;
|
||||
|
||||
//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");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(conn, 0, sizeof(tproxy_conn_t));
|
||||
conn->state = CONN_UNAVAILABLE;
|
||||
conn->fd = fd;
|
||||
conn->remote = remote;
|
||||
@ -508,7 +501,7 @@ static tproxy_conn_t *new_conn(int fd, bool remote)
|
||||
#ifdef SPLICE_PRESENT
|
||||
// if dont tamper - both legs are spliced, create 2 pipes
|
||||
// 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");
|
||||
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 ((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");
|
||||
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)
|
||||
{
|
||||
sacopy(&conn->dest, (struct sockaddr *)&orig_dst);
|
||||
|
||||
if(!(conn->partner = new_conn(remote_fd, true)))
|
||||
{
|
||||
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);
|
||||
legs_remote++;
|
||||
}
|
||||
|
||||
if (proxy_type==CONN_TYPE_TRANSPARENT)
|
||||
apply_desync_profile(&conn->track, (struct sockaddr *)&conn->dest);
|
||||
|
||||
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;
|
||||
|
||||
if (params.debug>=1)
|
||||
{
|
||||
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);
|
||||
}
|
||||
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");
|
||||
socks_send_rep(conn->socks_ver, conn->fd, S5_REP_NOT_ALLOWED_BY_RULESET);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool bConnFooling=true;
|
||||
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");
|
||||
}
|
||||
apply_desync_profile(&conn->track, (struct sockaddr *)&conn->dest);
|
||||
|
||||
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);
|
||||
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;
|
||||
char buf[sizeof(s5_req)]; // s5_req - the largest possible req
|
||||
struct sockaddr_storage ss;
|
||||
|
||||
// receive proxy control message
|
||||
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);
|
||||
return false;
|
||||
}
|
||||
ss.ss_family = AF_INET;
|
||||
((struct sockaddr_in*)&ss)->sin_port = m->port;
|
||||
((struct sockaddr_in*)&ss)->sin_addr.s_addr = m->ip;
|
||||
return proxy_mode_connect_remote((struct sockaddr *)&ss, conn, conn_list);
|
||||
conn->dest.ss_family = AF_INET;
|
||||
((struct sockaddr_in*)&conn->dest)->sin_port = m->port;
|
||||
((struct sockaddr_in*)&conn->dest)->sin_addr.s_addr = m->ip;
|
||||
return proxy_mode_connect_remote(conn, conn_list);
|
||||
}
|
||||
break;
|
||||
case S_WAIT_REQUEST:
|
||||
@ -960,16 +952,16 @@ static bool handle_proxy_mode(tproxy_conn_t *conn, struct tailhead *conn_list)
|
||||
switch(m->atyp)
|
||||
{
|
||||
case S5_ATYP_IP4:
|
||||
ss.ss_family = AF_INET;
|
||||
((struct sockaddr_in*)&ss)->sin_port = m->d4.port;
|
||||
((struct sockaddr_in*)&ss)->sin_addr = m->d4.addr;
|
||||
conn->dest.ss_family = AF_INET;
|
||||
((struct sockaddr_in*)&conn->dest)->sin_port = m->d4.port;
|
||||
((struct sockaddr_in*)&conn->dest)->sin_addr = m->d4.addr;
|
||||
break;
|
||||
case S5_ATYP_IP6:
|
||||
ss.ss_family = AF_INET6;
|
||||
((struct sockaddr_in6*)&ss)->sin6_port = m->d6.port;
|
||||
((struct sockaddr_in6*)&ss)->sin6_addr = m->d6.addr;
|
||||
((struct sockaddr_in6*)&ss)->sin6_flowinfo = 0;
|
||||
((struct sockaddr_in6*)&ss)->sin6_scope_id = 0;
|
||||
conn->dest.ss_family = AF_INET6;
|
||||
((struct sockaddr_in6*)&conn->dest)->sin6_port = m->d6.port;
|
||||
((struct sockaddr_in6*)&conn->dest)->sin6_addr = m->d6.addr;
|
||||
((struct sockaddr_in6*)&conn->dest)->sin6_flowinfo = 0;
|
||||
((struct sockaddr_in6*)&conn->dest)->sin6_scope_id = 0;
|
||||
break;
|
||||
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 proxy_mode_connect_remote((struct sockaddr *)&ss,conn,conn_list);
|
||||
return proxy_mode_connect_remote(conn,conn_list);
|
||||
}
|
||||
break;
|
||||
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);
|
||||
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
|
||||
@ -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)
|
||||
{
|
||||
return (params.tamper_start_n ? (conn->tnrd+1) : conn->trd) >= params.tamper_start &&
|
||||
(!params.tamper_cutoff || (params.tamper_cutoff_n ? (conn->tnrd+1) : conn->trd) < params.tamper_cutoff);
|
||||
if (!conn->track.dp) return true;
|
||||
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)
|
||||
@ -1072,33 +1074,26 @@ static void tamper(tproxy_conn_t *conn, uint8_t *segment, size_t segment_buffer_
|
||||
if (conn->remote)
|
||||
{
|
||||
if (conn_partner_alive(conn) && !conn->partner->track.bTamperInCutoff)
|
||||
{
|
||||
tamper_in(&conn->partner->track,segment,segment_buffer_size,segment_size);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bool in_range = in_tamper_out_range(conn);
|
||||
DBGPRINT("tamper_out stream pos %" PRIu64 "(n%" PRIu64 "). tamper range %s%u-%s%u (%s)\n",
|
||||
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);
|
||||
if (in_tamper_out_range(conn))
|
||||
tamper_out(&conn->track,(struct sockaddr*)&conn->dest,segment,segment_buffer_size,segment_size,split_pos,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)
|
||||
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;
|
||||
if (oob)
|
||||
{
|
||||
VPRINT("Sending OOB byte %02X\n", params.oob_byte);
|
||||
VPRINT("Sending OOB byte %02X\n", oob_byte);
|
||||
uint8_t oob_save;
|
||||
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);
|
||||
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" : "");
|
||||
|
||||
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);
|
||||
if (wr >= 0)
|
||||
{
|
||||
|
@ -54,6 +54,7 @@ struct tproxy_conn
|
||||
int splice_pipe[2];
|
||||
conn_state_t state;
|
||||
conn_type_t conn_type;
|
||||
struct sockaddr_storage dest;
|
||||
|
||||
struct tproxy_conn *partner; // other leg
|
||||
time_t orphan_since;
|
||||
|
Loading…
Reference in New Issue
Block a user