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
else
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 pos in sni sniext; do
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)
{
HostFailPoolDel(&dp->hostlist_auto_fail_counters, fail_counter);
DLOG("auto hostlist : %s : fail counter reset. website is working.\n", hostname);
HOSTLIST_DEBUGLOG_APPEND("%s : fail counter reset. website is working.", hostname);
DLOG("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);
}
}
}
@ -331,19 +331,19 @@ static void auto_hostlist_failed(struct desync_profile *dp, const char *hostname
}
}
fail_counter->counter++;
DLOG("auto hostlist : %s : fail counter %d/%d\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);
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 : 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)
{
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);
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;
if (!HostlistCheck(dp, hostname, &bExcluded) && !bExcluded)
{
DLOG("auto hostlist : adding %s\n", hostname);
HOSTLIST_DEBUGLOG_APPEND("%s : adding", hostname);
DLOG("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))
{
fprintf(stderr, "StrPoolAddStr out of memory\n");
@ -358,8 +358,8 @@ static void auto_hostlist_failed(struct desync_profile *dp, const char *hostname
}
else
{
DLOG("auto hostlist : NOT adding %s\n", hostname);
HOSTLIST_DEBUGLOG_APPEND("%s : NOT adding, duplicate detected", hostname);
DLOG("auto hostlist (profile %d) : NOT adding %s\n", dp->n, 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))
{
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);
}
}
@ -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)
{
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;
}
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)
{
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
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)
{
unsigned int v1,v2;
char c;
if (!s) return false;
if (*s=='~')
@ -328,13 +329,13 @@ bool pf_parse(const char *s, port_filter *pf)
}
else
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;
pf->from=(uint16_t)v1;
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;
pf->to=pf->from=(uint16_t)v1;

View File

@ -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
@ -241,27 +256,34 @@ bool pf_in_range(uint16_t port, const port_filter *pf)
bool pf_parse(const char *s, port_filter *pf)
{
unsigned int v1,v2;
char c;
if (!s) return false;
if (*s=='~')
if (*s=='~')
{
pf->neg=true;
s++;
}
else
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->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;
}
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;
}

View File

@ -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)

View File

@ -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(&params.hostlist, &params.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, &params.desync_profiles, next)
if (!LoadIncludeHostListsForProfile(&dpl->dp))
return false;
return true;
}
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 "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);

View File

@ -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(&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 };
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;

View File

@ -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)

View File

@ -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(&params.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;
}
if (ctrack->dp->disorder_tls) *split_flags |= SPLIT_FLAG_DISORDER;
if (ctrack->dp->oob_tls) *split_flags |= SPLIT_FLAG_OOB;
break;
default:
if (ctrack->dp->split_any_protocol && ctrack->dp->split_pos < *size)
*split_pos = ctrack->dp->split_pos;
}
else if (params.split_any_protocol && params.split_pos < *size)
*split_pos = params.split_pos;
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);
}
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(&params.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(&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)
{
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(&params.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(&params.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(&params.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(&params.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(&params.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);
}
}
}

View File

@ -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);

View File

@ -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, &params.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(&params.hostlist_files);
strlist_destroy(&params.hostlist_exclude_files);
StrPoolDestroy(&params.hostlist_exclude);
StrPoolDestroy(&params.hostlist);
HostFailPoolDestroy(&params.hostlist_auto_fail_counters);
dp_list_destroy(&params.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(&params, 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(&params.hostlist_files);
LIST_INIT(&params.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(&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[] = {
{ "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, &params.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, &params.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, &params.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(&params.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(&params.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(&params.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(&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__)
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,&params.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(&params.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, &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())
{
DLOG_ERR("Include hostlist load failed\n");
exit_clean(1);
}
if (*params.hostlist_auto_filename) NonEmptyHostlist(&params.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

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
//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,&params.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, &params.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)
{

View File

@ -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;