mirror of
https://github.com/bol-van/zapret.git
synced 2025-05-24 22:32:58 +03:00
autohostlist mode
This commit is contained in:
@@ -30,6 +30,15 @@ char *strncasestr(const char *s,const char *find, size_t slen)
|
||||
return (char *)s;
|
||||
}
|
||||
|
||||
bool append_to_list_file(const char *filename, const char *s)
|
||||
{
|
||||
FILE *F = fopen(filename,"at");
|
||||
if (!F) return false;
|
||||
bool bOK = fprintf(F,"%s\n",s)>0;
|
||||
fclose(F);
|
||||
return bOK;
|
||||
}
|
||||
|
||||
void ntop46(const struct sockaddr *sa, char *str, size_t len)
|
||||
{
|
||||
if (!len) return;
|
||||
|
@@ -8,6 +8,8 @@
|
||||
|
||||
char *strncasestr(const char *s,const char *find, size_t slen);
|
||||
|
||||
bool append_to_list_file(const char *filename, const char *s);
|
||||
|
||||
void ntop46(const struct sockaddr *sa, char *str, size_t len);
|
||||
void ntop46_port(const struct sockaddr *sa, char *str, size_t len);
|
||||
void print_sockaddr(const struct sockaddr *sa);
|
||||
|
@@ -4,6 +4,7 @@
|
||||
#include "params.h"
|
||||
|
||||
|
||||
// inplace tolower() and add to pool
|
||||
static bool addpool(strpool **hostlist, char **s, const char *end)
|
||||
{
|
||||
char *p;
|
||||
@@ -22,7 +23,6 @@ static bool addpool(strpool **hostlist, char **s, const char *end)
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool AppendHostList(strpool **hostlist, char *filename)
|
||||
{
|
||||
char *p, *e, s[256], *zbuf;
|
||||
@@ -106,6 +106,12 @@ bool LoadHostLists(strpool **hostlist, struct str_list_head *file_list)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NonEmptyHostlist(strpool **hostlist)
|
||||
{
|
||||
// add impossible hostname if the list is empty
|
||||
return *hostlist ? true : StrPoolAddStrLen(hostlist, "@&()", 4);
|
||||
}
|
||||
|
||||
|
||||
bool SearchHostList(strpool *hostlist, const char *host)
|
||||
{
|
||||
@@ -126,12 +132,17 @@ bool SearchHostList(strpool *hostlist, const char *host)
|
||||
}
|
||||
|
||||
// return : true = apply fooling, false = do not apply
|
||||
bool HostlistCheck(strpool *hostlist, strpool *hostlist_exclude, const char *host)
|
||||
bool HostlistCheck(strpool *hostlist, strpool *hostlist_exclude, const char *host, bool *excluded)
|
||||
{
|
||||
if (excluded) *excluded = false;
|
||||
if (hostlist_exclude)
|
||||
{
|
||||
if (params.debug) printf("Checking exclude hostlist\n");
|
||||
if (SearchHostList(hostlist_exclude, host)) return false;
|
||||
if (SearchHostList(hostlist_exclude, host))
|
||||
{
|
||||
if (excluded) *excluded = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (hostlist)
|
||||
{
|
||||
|
@@ -1,10 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "strpool.h"
|
||||
#include "pools.h"
|
||||
|
||||
bool AppendHostList(strpool **hostlist, char *filename);
|
||||
bool LoadHostLists(strpool **hostlist, struct str_list_head *file_list);
|
||||
bool NonEmptyHostlist(strpool **hostlist);
|
||||
bool SearchHostList(strpool *hostlist, const char *host);
|
||||
// return : true = apply fooling, false = do not apply
|
||||
bool HostlistCheck(strpool *hostlist, strpool *hostlist_exclude, const char *host);
|
||||
bool HostlistCheck(strpool *hostlist, strpool *hostlist_exclude, const char *host, bool *excluded);
|
||||
|
@@ -5,7 +5,10 @@
|
||||
#include <stdint.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/queue.h>
|
||||
#include "strpool.h"
|
||||
#include "pools.h"
|
||||
|
||||
#define HOSTLIST_AUTO_FAIL_THRESHOLD_DEFAULT 2
|
||||
#define HOSTLIST_AUTO_FAIL_TIME_DEFAULT 60
|
||||
|
||||
enum splithttpreq { split_none = 0, split_method, split_host };
|
||||
enum tlsrec { tlsrec_none = 0, tlsrec_sni, tlsrec_pos };
|
||||
@@ -53,6 +56,9 @@ struct params_s
|
||||
|
||||
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;
|
||||
hostfail_pool *hostlist_auto_fail_counters;
|
||||
|
||||
int debug;
|
||||
|
||||
|
152
tpws/pools.c
Normal file
152
tpws/pools.c
Normal file
@@ -0,0 +1,152 @@
|
||||
#define _GNU_SOURCE
|
||||
#include "pools.h"
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define DESTROY_STR_POOL(etype, ppool) \
|
||||
etype *elem, *tmp; \
|
||||
HASH_ITER(hh, *ppool, elem, tmp) { \
|
||||
free(elem->str); \
|
||||
HASH_DEL(*ppool, elem); \
|
||||
free(elem); \
|
||||
}
|
||||
|
||||
#define ADD_STR_POOL(etype, ppool, keystr, keystr_len) \
|
||||
etype *elem; \
|
||||
if (!(elem = (etype*)malloc(sizeof(etype)))) \
|
||||
return false; \
|
||||
if (!(elem->str = malloc(keystr_len + 1))) \
|
||||
{ \
|
||||
free(elem); \
|
||||
return false; \
|
||||
} \
|
||||
memcpy(elem->str, keystr, keystr_len); \
|
||||
elem->str[keystr_len] = 0; \
|
||||
oom = false; \
|
||||
HASH_ADD_KEYPTR(hh, *ppool, elem->str, strlen(elem->str), elem); \
|
||||
if (oom) \
|
||||
{ \
|
||||
free(elem->str); \
|
||||
free(elem); \
|
||||
return false; \
|
||||
}
|
||||
|
||||
|
||||
#undef uthash_nonfatal_oom
|
||||
#define uthash_nonfatal_oom(elt) ut_oom_recover(elt)
|
||||
static bool oom = false;
|
||||
static void ut_oom_recover(void *elem)
|
||||
{
|
||||
oom = true;
|
||||
}
|
||||
|
||||
// for not zero terminated strings
|
||||
bool StrPoolAddStrLen(strpool **pp, const char *s, size_t slen)
|
||||
{
|
||||
ADD_STR_POOL(strpool, pp, s, slen)
|
||||
return true;
|
||||
}
|
||||
// for zero terminated strings
|
||||
bool StrPoolAddStr(strpool **pp, const char *s)
|
||||
{
|
||||
return StrPoolAddStrLen(pp, s, strlen(s));
|
||||
}
|
||||
|
||||
bool StrPoolCheckStr(strpool *p, const char *s)
|
||||
{
|
||||
strpool *elem;
|
||||
HASH_FIND_STR(p, s, elem);
|
||||
return elem != NULL;
|
||||
}
|
||||
|
||||
void StrPoolDestroy(strpool **pp)
|
||||
{
|
||||
DESTROY_STR_POOL(strpool, pp)
|
||||
}
|
||||
|
||||
|
||||
|
||||
void HostFailPoolDestroy(hostfail_pool **pp)
|
||||
{
|
||||
DESTROY_STR_POOL(hostfail_pool, pp)
|
||||
}
|
||||
hostfail_pool * HostFailPoolAdd(hostfail_pool **pp,const char *s,int fail_time)
|
||||
{
|
||||
size_t slen = strlen(s);
|
||||
ADD_STR_POOL(hostfail_pool, pp, s, slen)
|
||||
elem->expire = time(NULL) + fail_time;
|
||||
return elem;
|
||||
}
|
||||
hostfail_pool *HostFailPoolFind(hostfail_pool *p,const char *s)
|
||||
{
|
||||
hostfail_pool *elem;
|
||||
HASH_FIND_STR(p, s, elem);
|
||||
return elem;
|
||||
}
|
||||
void HostFailPoolDel(hostfail_pool **p, hostfail_pool *elem)
|
||||
{
|
||||
HASH_DEL(*p, elem);
|
||||
free(elem);
|
||||
}
|
||||
void HostFailPoolPurge(hostfail_pool **pp)
|
||||
{
|
||||
hostfail_pool *elem, *tmp;
|
||||
time_t now = time(NULL);
|
||||
HASH_ITER(hh, *pp, elem, tmp)
|
||||
{
|
||||
if (now >= elem->expire)
|
||||
{
|
||||
free(elem->str);
|
||||
HASH_DEL(*pp, elem);
|
||||
free(elem);
|
||||
}
|
||||
}
|
||||
}
|
||||
static time_t host_fail_purge_prev=0;
|
||||
void HostFailPoolPurgeRateLimited(hostfail_pool **pp)
|
||||
{
|
||||
time_t now = time(NULL);
|
||||
// do not purge too often to save resources
|
||||
if (host_fail_purge_prev != now)
|
||||
{
|
||||
HostFailPoolPurge(pp);
|
||||
host_fail_purge_prev = now;
|
||||
}
|
||||
}
|
||||
void HostFailPoolDump(hostfail_pool *p)
|
||||
{
|
||||
hostfail_pool *elem, *tmp;
|
||||
time_t now = time(NULL);
|
||||
HASH_ITER(hh, p, elem, tmp)
|
||||
printf("host=%s counter=%d time_left=%lld\n",elem->str,elem->counter,(long long int)elem->expire-now);
|
||||
}
|
||||
|
||||
|
||||
bool strlist_add(struct str_list_head *head, const char *filename)
|
||||
{
|
||||
struct str_list *entry = malloc(sizeof(struct str_list));
|
||||
if (!entry) return false;
|
||||
entry->str = strdup(filename);
|
||||
if (!entry->str)
|
||||
{
|
||||
free(entry);
|
||||
return false;
|
||||
}
|
||||
LIST_INSERT_HEAD(head, entry, next);
|
||||
return true;
|
||||
}
|
||||
static void strlist_entry_destroy(struct str_list *entry)
|
||||
{
|
||||
if (entry->str) free(entry->str);
|
||||
free(entry);
|
||||
}
|
||||
void strlist_destroy(struct str_list_head *head)
|
||||
{
|
||||
struct str_list *entry;
|
||||
while ((entry = LIST_FIRST(head)))
|
||||
{
|
||||
LIST_REMOVE(entry, next);
|
||||
strlist_entry_destroy(entry);
|
||||
}
|
||||
}
|
46
tpws/pools.h
Normal file
46
tpws/pools.h
Normal file
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <ctype.h>
|
||||
#include <sys/queue.h>
|
||||
#include <time.h>
|
||||
|
||||
//#define HASH_BLOOM 20
|
||||
#define HASH_NONFATAL_OOM 1
|
||||
#define HASH_FUNCTION HASH_BER
|
||||
#include "uthash.h"
|
||||
|
||||
typedef struct strpool {
|
||||
char *str; /* key */
|
||||
UT_hash_handle hh; /* makes this structure hashable */
|
||||
} strpool;
|
||||
|
||||
void StrPoolDestroy(strpool **pp);
|
||||
bool StrPoolAddStr(strpool **pp,const char *s);
|
||||
bool StrPoolAddStrLen(strpool **pp,const char *s,size_t slen);
|
||||
bool StrPoolCheckStr(strpool *p,const char *s);
|
||||
|
||||
struct str_list {
|
||||
char *str;
|
||||
LIST_ENTRY(str_list) next;
|
||||
};
|
||||
LIST_HEAD(str_list_head, str_list);
|
||||
|
||||
|
||||
typedef struct hostfail_pool {
|
||||
char *str; /* key */
|
||||
int counter; /* value */
|
||||
time_t expire; /* when to expire record (unixtime) */
|
||||
UT_hash_handle hh; /* makes this structure hashable */
|
||||
} hostfail_pool;
|
||||
|
||||
void HostFailPoolDestroy(hostfail_pool **pp);
|
||||
hostfail_pool *HostFailPoolAdd(hostfail_pool **pp,const char *s,int fail_time);
|
||||
hostfail_pool *HostFailPoolFind(hostfail_pool *p,const char *s);
|
||||
void HostFailPoolDel(hostfail_pool **pp, hostfail_pool *elem);
|
||||
void HostFailPoolPurge(hostfail_pool **pp);
|
||||
void HostFailPoolPurgeRateLimited(hostfail_pool **pp);
|
||||
void HostFailPoolDump(hostfail_pool *p);
|
||||
|
||||
bool strlist_add(struct str_list_head *head, const char *filename);
|
||||
void strlist_destroy(struct str_list_head *head);
|
@@ -21,29 +21,95 @@ bool IsHttp(const uint8_t *data, size_t len)
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool HttpExtractHost(const uint8_t *data, size_t len, char *host, size_t len_host)
|
||||
bool IsHttpReply(const uint8_t *data, size_t len)
|
||||
{
|
||||
const uint8_t *p, *s, *e=data+len;
|
||||
// HTTP/1.x 200\r\n
|
||||
return len>14 && !memcmp(data,"HTTP/1.",7) && (data[7]=='0' || data[7]=='1') && data[8]==' ' &&
|
||||
data[9]>='0' && data[9]<='9' &&
|
||||
data[10]>='0' && data[10]<='9' &&
|
||||
data[11]>='0' && data[11]<='9';
|
||||
}
|
||||
int HttpReplyCode(const uint8_t *data, size_t len)
|
||||
{
|
||||
return (data[9]-'0')*100 + (data[10]-'0')*10 + (data[11]-'0');
|
||||
}
|
||||
bool HttpExtractHeader(const uint8_t *data, size_t len, const char *header, char *buf, size_t len_buf)
|
||||
{
|
||||
const uint8_t *p, *s, *e = data + len;
|
||||
|
||||
p = (uint8_t*)strncasestr((char*)data, "\nHost:", len);
|
||||
p = (uint8_t*)strncasestr((char*)data, header, len);
|
||||
if (!p) return false;
|
||||
p+=6;
|
||||
while(p<e && (*p==' ' || *p=='\t')) p++;
|
||||
s=p;
|
||||
while(s<e && (*s!='\r' && *s!='\n' && *s!=' ' && *s!='\t')) s++;
|
||||
if (s>p)
|
||||
p += strlen(header);
|
||||
while (p < e && (*p == ' ' || *p == '\t')) p++;
|
||||
s = p;
|
||||
while (s < e && (*s != '\r' && *s != '\n' && *s != ' ' && *s != '\t')) s++;
|
||||
if (s > p)
|
||||
{
|
||||
size_t slen = s-p;
|
||||
if (host && len_host)
|
||||
size_t slen = s - p;
|
||||
if (buf && len_buf)
|
||||
{
|
||||
if (slen>=len_host) slen=len_host-1;
|
||||
for(size_t i=0;i<slen;i++) host[i]=tolower(p[i]);
|
||||
host[slen]=0;
|
||||
if (slen >= len_buf) slen = len_buf - 1;
|
||||
for (size_t i = 0; i < slen; i++) buf[i] = tolower(p[i]);
|
||||
buf[slen] = 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool HttpExtractHost(const uint8_t *data, size_t len, char *host, size_t len_host)
|
||||
{
|
||||
return HttpExtractHeader(data, len, "\nHost:", host, len_host);
|
||||
}
|
||||
const char *HttpFind2ndLevelDomain(const char *host)
|
||||
{
|
||||
const char *p=NULL;
|
||||
if (*host)
|
||||
{
|
||||
for (p = host + strlen(host)-1; p>host && *p!='.'; p--);
|
||||
if (*p=='.') for (p--; p>host && *p!='.'; p--);
|
||||
if (*p=='.') p++;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
// DPI redirects are global redirects to another domain
|
||||
bool HttpReplyLooksLikeDPIRedirect(const uint8_t *data, size_t len, const char *host)
|
||||
{
|
||||
char loc[256],*redirect_host, *p;
|
||||
int code;
|
||||
|
||||
if (!host || !*host) return false;
|
||||
|
||||
code = HttpReplyCode(data,len);
|
||||
|
||||
if (code!=302 && code!=307 || !HttpExtractHeader(data,len,"\nLocation:",loc,sizeof(loc))) return false;
|
||||
|
||||
// something like : https://censor.net/badpage.php?reason=denied&source=RKN
|
||||
|
||||
if (!strncmp(loc,"http://",7))
|
||||
redirect_host=loc+7;
|
||||
else if (!strncmp(loc,"https://",8))
|
||||
redirect_host=loc+8;
|
||||
else
|
||||
return false;
|
||||
|
||||
// somethinkg like : censor.net/badpage.php?reason=denied&source=RKN
|
||||
|
||||
for(p=redirect_host; *p && *p!='/' ; p++);
|
||||
*p=0;
|
||||
if (!*redirect_host) return false;
|
||||
|
||||
// somethinkg like : censor.net
|
||||
|
||||
// extract 2nd level domains
|
||||
|
||||
const char *dhost = HttpFind2ndLevelDomain(host);
|
||||
const char *drhost = HttpFind2ndLevelDomain(redirect_host);
|
||||
|
||||
return strcasecmp(dhost, drhost)!=0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool IsTLSClientHello(const uint8_t *data, size_t len)
|
||||
{
|
||||
return len>=6 && data[0]==0x16 && data[1]==0x03 && data[2]>=0x01 && data[2]<=0x03 && data[5]==0x01 && (pntoh16(data+3)+5)<=len;
|
||||
|
@@ -5,7 +5,16 @@
|
||||
#include <stdbool.h>
|
||||
|
||||
bool IsHttp(const uint8_t *data, size_t len);
|
||||
// header must be passed like this : "\nHost:"
|
||||
bool HttpExtractHeader(const uint8_t *data, size_t len, const char *header, char *buf, size_t len_buf);
|
||||
bool HttpExtractHost(const uint8_t *data, size_t len, char *host, size_t len_host);
|
||||
bool IsHttpReply(const uint8_t *data, size_t len);
|
||||
const char *HttpFind2ndLevelDomain(const char *host);
|
||||
// must be pre-checked by IsHttpReply
|
||||
int HttpReplyCode(const uint8_t *data, size_t len);
|
||||
// must be pre-checked by IsHttpReply
|
||||
bool HttpReplyLooksLikeDPIRedirect(const uint8_t *data, size_t len, const char *host);
|
||||
|
||||
bool IsTLSClientHello(const uint8_t *data, size_t len);
|
||||
bool TLSFindExt(const uint8_t *data, size_t len, uint16_t type, const uint8_t **ext, size_t *len_ext);
|
||||
bool TLSHelloExtractHost(const uint8_t *data, size_t len, char *host, size_t len_host);
|
||||
|
107
tpws/strpool.c
107
tpws/strpool.c
@@ -1,107 +0,0 @@
|
||||
#define _GNU_SOURCE
|
||||
#include "strpool.h"
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#undef uthash_nonfatal_oom
|
||||
#define uthash_nonfatal_oom(elt) ut_oom_recover(elt)
|
||||
|
||||
static bool oom = false;
|
||||
static void ut_oom_recover(strpool *elem)
|
||||
{
|
||||
oom = true;
|
||||
}
|
||||
|
||||
// for zero terminated strings
|
||||
bool StrPoolAddStr(strpool **pp, const char *s)
|
||||
{
|
||||
strpool *elem;
|
||||
if (!(elem = (strpool*)malloc(sizeof(strpool))))
|
||||
return false;
|
||||
if (!(elem->str = strdup(s)))
|
||||
{
|
||||
free(elem);
|
||||
return false;
|
||||
}
|
||||
oom = false;
|
||||
HASH_ADD_KEYPTR(hh, *pp, elem->str, strlen(elem->str), elem);
|
||||
if (oom)
|
||||
{
|
||||
free(elem->str);
|
||||
free(elem);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// for not zero terminated strings
|
||||
bool StrPoolAddStrLen(strpool **pp, const char *s, size_t slen)
|
||||
{
|
||||
strpool *elem;
|
||||
if (!(elem = (strpool*)malloc(sizeof(strpool))))
|
||||
return false;
|
||||
if (!(elem->str = malloc(slen + 1)))
|
||||
{
|
||||
free(elem);
|
||||
return false;
|
||||
}
|
||||
memcpy(elem->str, s, slen);
|
||||
elem->str[slen] = 0;
|
||||
oom = false;
|
||||
HASH_ADD_KEYPTR(hh, *pp, elem->str, strlen(elem->str), elem);
|
||||
if (oom)
|
||||
{
|
||||
free(elem->str);
|
||||
free(elem);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StrPoolCheckStr(strpool *p, const char *s)
|
||||
{
|
||||
strpool *elem;
|
||||
HASH_FIND_STR(p, s, elem);
|
||||
return elem != NULL;
|
||||
}
|
||||
|
||||
void StrPoolDestroy(strpool **p)
|
||||
{
|
||||
strpool *elem, *tmp;
|
||||
HASH_ITER(hh, *p, elem, tmp) {
|
||||
free(elem->str);
|
||||
HASH_DEL(*p, elem);
|
||||
free(elem);
|
||||
}
|
||||
*p = NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
bool strlist_add(struct str_list_head *head, const char *filename)
|
||||
{
|
||||
struct str_list *entry = malloc(sizeof(struct str_list));
|
||||
if (!entry) return false;
|
||||
entry->str = strdup(filename);
|
||||
if (!entry->str)
|
||||
{
|
||||
free(entry);
|
||||
return false;
|
||||
}
|
||||
LIST_INSERT_HEAD(head, entry, next);
|
||||
return true;
|
||||
}
|
||||
static void strlist_entry_destroy(struct str_list *entry)
|
||||
{
|
||||
if (entry->str) free(entry->str);
|
||||
free(entry);
|
||||
}
|
||||
void strlist_destroy(struct str_list_head *head)
|
||||
{
|
||||
struct str_list *entry;
|
||||
while ((entry = LIST_FIRST(head)))
|
||||
{
|
||||
LIST_REMOVE(entry, next);
|
||||
strlist_entry_destroy(entry);
|
||||
}
|
||||
}
|
@@ -1,29 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <ctype.h>
|
||||
#include <sys/queue.h>
|
||||
|
||||
//#define HASH_BLOOM 20
|
||||
#define HASH_NONFATAL_OOM 1
|
||||
#define HASH_FUNCTION HASH_BER
|
||||
#include "uthash.h"
|
||||
|
||||
typedef struct strpool {
|
||||
char *str; /* key */
|
||||
UT_hash_handle hh; /* makes this structure hashable */
|
||||
} strpool;
|
||||
|
||||
void StrPoolDestroy(strpool **p);
|
||||
bool StrPoolAddStr(strpool **pp,const char *s);
|
||||
bool StrPoolAddStrLen(strpool **pp,const char *s,size_t slen);
|
||||
bool StrPoolCheckStr(strpool *p,const char *s);
|
||||
|
||||
struct str_list {
|
||||
char *str;
|
||||
LIST_ENTRY(str_list) next;
|
||||
};
|
||||
LIST_HEAD(str_list_head, str_list);
|
||||
|
||||
bool strlist_add(struct str_list_head *head, const char *filename);
|
||||
void strlist_destroy(struct str_list_head *head);
|
197
tpws/tamper.c
197
tpws/tamper.c
@@ -25,15 +25,17 @@ bool find_host(uint8_t **pHost,uint8_t *buf,size_t bs)
|
||||
|
||||
static const char *http_methods[] = { "GET /","POST /","HEAD /","OPTIONS /","PUT /","DELETE /","CONNECT /","TRACE /",NULL };
|
||||
// segment buffer has at least 5 extra bytes to extend data block
|
||||
void modify_tcp_segment(uint8_t *segment,size_t segment_buffer_size,size_t *size,size_t *split_pos)
|
||||
void tamper_out(t_ctrack *ctrack, uint8_t *segment,size_t segment_buffer_size,size_t *size, size_t *split_pos)
|
||||
{
|
||||
uint8_t *p, *pp, *pHost = NULL;
|
||||
size_t method_len = 0, pos;
|
||||
const char **method;
|
||||
bool bIsHttp = false, bBypass = false;
|
||||
bool bIsHttp = false, bBypass = false, bHaveHost = false, bHostExcluded = false;
|
||||
char bRemovedHostSpace = 0;
|
||||
char *pc, Host[128];
|
||||
char *pc, Host[256];
|
||||
|
||||
DBGPRINT("tamper_out")
|
||||
|
||||
*split_pos=0;
|
||||
|
||||
for (method = http_methods; *method; method++)
|
||||
@@ -49,6 +51,7 @@ void modify_tcp_segment(uint8_t *segment,size_t segment_buffer_size,size_t *size
|
||||
if (bIsHttp)
|
||||
{
|
||||
VPRINT("Data block looks like http request start : %s", *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) && find_host(&pHost,segment,*size))
|
||||
{
|
||||
@@ -58,9 +61,10 @@ void modify_tcp_segment(uint8_t *segment,size_t segment_buffer_size,size_t *size
|
||||
while (pp < (segment + *size) && (pp - p) < (sizeof(Host) - 1) && *pp != '\r' && *pp != '\n') pp++;
|
||||
memcpy(Host, p, pp - p);
|
||||
Host[pp - p] = '\0';
|
||||
bHaveHost = true;
|
||||
VPRINT("Requested Host is : %s", Host)
|
||||
for(pc = Host; *pc; pc++) *pc=tolower(*pc);
|
||||
bBypass = !HostlistCheck(params.hostlist, params.hostlist_exclude, Host);
|
||||
bBypass = !HostlistCheck(params.hostlist, params.hostlist_exclude, Host, &bHostExcluded);
|
||||
}
|
||||
if (!bBypass)
|
||||
{
|
||||
@@ -210,62 +214,161 @@ void modify_tcp_segment(uint8_t *segment,size_t segment_buffer_size,size_t *size
|
||||
{
|
||||
VPRINT("Not acting on this request")
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsTLSClientHello(segment,*size))
|
||||
else if (IsTLSClientHello(segment,*size))
|
||||
{
|
||||
char host[256];
|
||||
size_t tpos=0,elen;
|
||||
const uint8_t *ext;
|
||||
|
||||
if (!ctrack->l7proto) ctrack->l7proto=TLS;
|
||||
|
||||
VPRINT("packet contains TLS ClientHello")
|
||||
// we need host only if hostlist is present
|
||||
if ((params.hostlist || params.hostlist_exclude) && TLSHelloExtractHost((uint8_t*)segment,*size,host,sizeof(host)))
|
||||
if ((params.hostlist || params.hostlist_exclude) && TLSHelloExtractHost((uint8_t*)segment,*size,Host,sizeof(Host)))
|
||||
{
|
||||
VPRINT("hostname: %s",host)
|
||||
if (!HostlistCheck(params.hostlist, params.hostlist_exclude, host))
|
||||
VPRINT("hostname: %s",Host)
|
||||
bHaveHost = true;
|
||||
bBypass = !HostlistCheck(params.hostlist, params.hostlist_exclude, Host, &bHostExcluded);
|
||||
}
|
||||
if (bBypass)
|
||||
{
|
||||
VPRINT("Not acting on this request")
|
||||
}
|
||||
else
|
||||
{
|
||||
switch(params.tlsrec)
|
||||
{
|
||||
VPRINT("Not acting on this request")
|
||||
return;
|
||||
case tlsrec_sni:
|
||||
if (TLSFindExt(segment,*size,0,&ext,&elen))
|
||||
tpos = ext-segment+1; // between typical 1st and 2nd char of hostname
|
||||
break;
|
||||
case tlsrec_pos:
|
||||
tpos = params.tlsrec_pos;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
switch(params.tlsrec)
|
||||
{
|
||||
case tlsrec_sni:
|
||||
if (TLSFindExt(segment,*size,0,&ext,&elen))
|
||||
tpos = ext-segment+1; // between typical 1st and 2nd char of hostname
|
||||
break;
|
||||
case tlsrec_pos:
|
||||
tpos = params.tlsrec_pos;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (tpos)
|
||||
{
|
||||
// construct 2 TLS records from one
|
||||
uint16_t l = pntoh16(segment+3); // length
|
||||
if (l>=2)
|
||||
if (tpos)
|
||||
{
|
||||
// length is checked in IsTLSClientHello and cannot exceed buffer size
|
||||
if (tpos>=l) tpos=1;
|
||||
VPRINT("making 2 TLS records at pos %zu",tpos)
|
||||
memmove(segment+5+tpos+5,segment+5+tpos,*size-(5+tpos));
|
||||
segment[5+tpos] = segment[0];
|
||||
segment[5+tpos+1] = segment[1];
|
||||
segment[5+tpos+2] = segment[2];
|
||||
phton16(segment+5+tpos+3,l-tpos);
|
||||
phton16(segment+3,tpos);
|
||||
*size += 5;
|
||||
// construct 2 TLS records from one
|
||||
uint16_t l = pntoh16(segment+3); // length
|
||||
if (l>=2)
|
||||
{
|
||||
// length is checked in IsTLSClientHello and cannot exceed buffer size
|
||||
if (tpos>=l) tpos=1;
|
||||
VPRINT("making 2 TLS records at pos %zu",tpos)
|
||||
memmove(segment+5+tpos+5,segment+5+tpos,*size-(5+tpos));
|
||||
segment[5+tpos] = segment[0];
|
||||
segment[5+tpos+1] = segment[1];
|
||||
segment[5+tpos+2] = segment[2];
|
||||
phton16(segment+5+tpos+3,l-tpos);
|
||||
phton16(segment+3,tpos);
|
||||
*size += 5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (params.split_pos < *size)
|
||||
*split_pos = params.split_pos;
|
||||
return;
|
||||
if (params.split_pos < *size)
|
||||
*split_pos = params.split_pos;
|
||||
}
|
||||
}
|
||||
|
||||
if (params.split_any_protocol && params.split_pos < *size)
|
||||
else if (params.split_any_protocol && params.split_pos < *size)
|
||||
*split_pos = params.split_pos;
|
||||
|
||||
if (bHaveHost && bBypass && !bHostExcluded && !ctrack->hostname && *params.hostlist_auto_filename)
|
||||
{
|
||||
DBGPRINT("tamper_out put hostname : %s", Host)
|
||||
ctrack->hostname=strdup(Host);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
static void auto_hostlist_failed(const char *hostname)
|
||||
{
|
||||
hostfail_pool *fail_counter;
|
||||
|
||||
fail_counter = HostFailPoolFind(params.hostlist_auto_fail_counters, hostname);
|
||||
if (!fail_counter)
|
||||
{
|
||||
fail_counter = HostFailPoolAdd(¶ms.hostlist_auto_fail_counters, hostname, params.hostlist_auto_fail_time);
|
||||
if (!fail_counter)
|
||||
{
|
||||
fprintf(stderr, "HostFailPoolAdd: out of memory\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
fail_counter->counter++;
|
||||
VPRINT("auto hostlist : %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 : fail threshold reached. adding %s to auto hostlist", hostname);
|
||||
HostFailPoolDel(¶ms.hostlist_auto_fail_counters, fail_counter);
|
||||
if (!StrPoolAddStr(¶ms.hostlist, hostname))
|
||||
{
|
||||
fprintf(stderr, "StrPoolAddStr out of memory\n");
|
||||
return;
|
||||
}
|
||||
if (!append_to_list_file(params.hostlist_auto_filename, hostname))
|
||||
{
|
||||
perror("write to auto hostlist:");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void tamper_in(t_ctrack *ctrack, uint8_t *segment,size_t segment_buffer_size,size_t *size)
|
||||
{
|
||||
bool bFail=false;
|
||||
|
||||
DBGPRINT("tamper_in hostname=%s", ctrack->hostname)
|
||||
|
||||
HostFailPoolPurgeRateLimited(¶ms.hostlist_auto_fail_counters);
|
||||
|
||||
if (ctrack->l7proto==HTTP && ctrack->hostname)
|
||||
{
|
||||
if (IsHttpReply(segment,*size))
|
||||
{
|
||||
VPRINT("incoming HTTP reply detected for hostname %s", ctrack->hostname);
|
||||
bFail = HttpReplyLooksLikeDPIRedirect(segment, *size, ctrack->hostname);
|
||||
if (bFail)
|
||||
VPRINT("redirect to another domain detected. possibly DPI redirect.")
|
||||
else
|
||||
VPRINT("local or in-domain redirect detected. it's not a DPI redirect.")
|
||||
}
|
||||
else
|
||||
{
|
||||
// received not http reply. do not monitor this connection anymore
|
||||
VPRINT("incoming unknown HTTP data detected for hostname %s", ctrack->hostname);
|
||||
}
|
||||
if (bFail)
|
||||
auto_hostlist_failed(ctrack->hostname);
|
||||
|
||||
}
|
||||
ctrack->bTamperInCutoff = true;
|
||||
}
|
||||
|
||||
void rst_in(t_ctrack *ctrack)
|
||||
{
|
||||
DBGPRINT("rst_in hostname=%s", ctrack->hostname)
|
||||
|
||||
HostFailPoolPurgeRateLimited(¶ms.hostlist_auto_fail_counters);
|
||||
|
||||
if (!ctrack->bTamperInCutoff && ctrack->hostname)
|
||||
{
|
||||
VPRINT("incoming RST detected for hostname %s", ctrack->hostname);
|
||||
auto_hostlist_failed(ctrack->hostname);
|
||||
}
|
||||
}
|
||||
void hup_out(t_ctrack *ctrack)
|
||||
{
|
||||
DBGPRINT("hup_out hostname=%s", ctrack->hostname)
|
||||
|
||||
HostFailPoolPurgeRateLimited(¶ms.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", ctrack->hostname);
|
||||
auto_hostlist_failed(ctrack->hostname);
|
||||
}
|
||||
}
|
||||
|
@@ -4,5 +4,19 @@
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
bool find_host(uint8_t **pHost,uint8_t *buf,size_t bs);
|
||||
void modify_tcp_segment(uint8_t *segment,size_t segment_buffer_size,size_t *size,size_t *split_pos);
|
||||
typedef enum {UNKNOWN=0, HTTP, TLS} t_l7proto;
|
||||
typedef struct
|
||||
{
|
||||
// common state
|
||||
t_l7proto l7proto;
|
||||
bool bFirstReplyChecked;
|
||||
bool bTamperInCutoff;
|
||||
char *hostname;
|
||||
} t_ctrack;
|
||||
|
||||
void tamper_out(t_ctrack *ctrack, uint8_t *segment,size_t segment_buffer_size,size_t *size, size_t *split_pos);
|
||||
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);
|
||||
// local leg closed connection (timeout waiting response ?)
|
||||
void hup_out(t_ctrack *ctrack);
|
||||
|
205
tpws/tpws.c
205
tpws/tpws.c
@@ -36,6 +36,8 @@
|
||||
#include "sec.h"
|
||||
#include "redirect.h"
|
||||
#include "helpers.h"
|
||||
#include "gzip.h"
|
||||
#include "pools.h"
|
||||
|
||||
struct params_s params;
|
||||
|
||||
@@ -62,6 +64,12 @@ void dohup(void)
|
||||
}
|
||||
}
|
||||
|
||||
static void onusr2(int sig)
|
||||
{
|
||||
printf("\nHOSTFAIL POOL DUMP\n");
|
||||
HostFailPoolDump(params.hostlist_auto_fail_counters);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
|
||||
static int8_t block_sigpipe(void)
|
||||
@@ -120,62 +128,66 @@ static int get_default_ttl(void)
|
||||
static void exithelp(void)
|
||||
{
|
||||
printf(
|
||||
" --bind-addr=<v4_addr>|<v6_addr>; for v6 link locals append %%interface_name\n"
|
||||
" --bind-iface4=<interface_name>\t; bind to the first ipv4 addr of interface\n"
|
||||
" --bind-iface6=<interface_name>\t; bind to the first ipv6 addr of interface\n"
|
||||
" --bind-linklocal=no|unwanted|prefer|force\n"
|
||||
"\t\t\t\t; prohibit, accept, prefer or force ipv6 link local bind\n"
|
||||
" --bind-wait-ifup=<sec>\t\t; wait for interface to appear and up\n"
|
||||
" --bind-wait-ip=<sec>\t\t; after ifup wait for ip address to appear up to N seconds\n"
|
||||
" --bind-wait-ip-linklocal=<sec>\t; (prefer) accept only LL first N seconds then any (unwanted) accept only globals first N seconds then LL\n"
|
||||
" --bind-wait-only\t\t; wait for bind conditions satisfaction then exit. return code 0 if success.\n"
|
||||
" --bind-addr=<v4_addr>|<v6_addr>\t; for v6 link locals append %%interface_name\n"
|
||||
" --bind-iface4=<interface_name>\t\t; bind to the first ipv4 addr of interface\n"
|
||||
" --bind-iface6=<interface_name>\t\t; bind to the first ipv6 addr of interface\n"
|
||||
" --bind-linklocal=no|unwanted|prefer|force ; prohibit, accept, prefer or force ipv6 link local bind\n"
|
||||
" --bind-wait-ifup=<sec>\t\t\t; wait for interface to appear and up\n"
|
||||
" --bind-wait-ip=<sec>\t\t\t; after ifup wait for ip address to appear up to N seconds\n"
|
||||
" --bind-wait-ip-linklocal=<sec>\t\t; (prefer) accept only LL first N seconds then any (unwanted) accept only globals first N seconds then LL\n"
|
||||
" --bind-wait-only\t\t\t; wait for bind conditions satisfaction then exit. return code 0 if success.\n"
|
||||
" * multiple binds are supported. each bind-addr, bind-iface* start new bind\n"
|
||||
" --port=<port>\t\t\t; only one port number for all binds is supported\n"
|
||||
" --socks\t\t\t; implement socks4/5 proxy instead of transparent proxy\n"
|
||||
" --no-resolve\t\t\t; disable socks5 remote dns ability (resolves are not async, they block all activity)\n"
|
||||
" --port=<port>\t\t\t\t; only one port number for all binds is supported\n"
|
||||
" --socks\t\t\t\t; implement socks4/5 proxy instead of transparent proxy\n"
|
||||
" --no-resolve\t\t\t\t; disable socks5 remote dns ability (resolves are not async, they block all activity)\n"
|
||||
" --local-rcvbuf=<bytes>\n"
|
||||
" --local-sndbuf=<bytes>\n"
|
||||
" --remote-rcvbuf=<bytes>\n"
|
||||
" --remote-sndbuf=<bytes>\n"
|
||||
" --skip-nodelay\t\t\t; do not set TCP_NODELAY option for outgoing connections (incompatible with split options)\n"
|
||||
" --skip-nodelay\t\t\t\t; do not set TCP_NODELAY option for outgoing connections (incompatible with split options)\n"
|
||||
" --maxconn=<max_connections>\n"
|
||||
#ifdef SPLICE_PRESENT
|
||||
" --maxfiles=<max_open_files>\t; should be at least (X*connections+16), where X=6 in tcp proxy mode, X=4 in tampering mode\n"
|
||||
" --maxfiles=<max_open_files>\t\t; should be at least (X*connections+16), where X=6 in tcp proxy mode, X=4 in tampering mode\n"
|
||||
#else
|
||||
" --maxfiles=<max_open_files>\t; should be at least (connections*2+16)\n"
|
||||
" --maxfiles=<max_open_files>\t\t; should be at least (connections*2+16)\n"
|
||||
#endif
|
||||
" --max-orphan-time=<sec>\t; if local leg sends something and closes and remote leg is still connecting then cancel connection attempt after N seconds\n"
|
||||
" --daemon\t\t\t; daemonize\n"
|
||||
" --pidfile=<filename>\t\t; write pid to file\n"
|
||||
" --user=<username>\t\t; drop root privs\n"
|
||||
" --uid=uid[:gid]\t\t; drop root privs\n"
|
||||
" --max-orphan-time=<sec>\t\t; if local leg sends something and closes and remote leg is still connecting then cancel connection attempt after N seconds\n"
|
||||
" --daemon\t\t\t\t; daemonize\n"
|
||||
" --pidfile=<filename>\t\t\t; write pid to file\n"
|
||||
" --user=<username>\t\t\t; drop root privs\n"
|
||||
" --uid=uid[:gid]\t\t\t; drop root privs\n"
|
||||
#if defined(BSD) && !defined(__OpenBSD__) && !defined(__APPLE__)
|
||||
" --enable-pf\t\t\t; enable PF redirector support. required in FreeBSD when used with PF firewall.\n"
|
||||
" --enable-pf\t\t\t\t; enable PF redirector support. required in FreeBSD when used with PF firewall.\n"
|
||||
#endif
|
||||
" --debug=0|1|2\t\t\t; 0(default)=silent 1=verbose 2=debug\n"
|
||||
"\nTAMPERING:\n"
|
||||
" --hostlist=<filename>\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; do not act on hosts in the list (one host per line, subdomains auto apply, gzip supported, multiple hostlists allowed)\n"
|
||||
" --split-http-req=method|host\t; split at specified logical part of plain http request\n"
|
||||
" --split-pos=<numeric_offset>\t; split at specified pos. split-http-req takes precedence for http.\n"
|
||||
" --split-any-protocol\t\t; split not only http and https\n"
|
||||
" --debug=0|1|2\t\t\t\t; 0(default)=silent 1=verbose 2=debug\n"
|
||||
"\nFILTER:\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"
|
||||
" --hostlist-auto-fail-threshold=<int>\t; how many failed attempts cause hostname to be added to auto hostlist (default : %d)\n"
|
||||
" --hostlist-auto-fail-time=<int>\t; all failed attemps must be within these seconds (default : %d)\n"
|
||||
"\nTAMPER:\n"
|
||||
" --split-http-req=method|host\t\t; split at specified logical part of plain http request\n"
|
||||
" --split-pos=<numeric_offset>\t\t; split at specified pos. split-http-req takes precedence for http.\n"
|
||||
" --split-any-protocol\t\t\t; split not only http and https\n"
|
||||
#if defined(BSD) && !defined(__APPLE__)
|
||||
" --disorder\t\t\t; when splitting simulate sending second fragment first (BSD sends entire message instead of first fragment, this is not good)\n"
|
||||
" --disorder\t\t\t\t; when splitting simulate sending second fragment first (BSD sends entire message instead of first fragment, this is not good)\n"
|
||||
#else
|
||||
" --disorder\t\t\t; when splitting simulate sending second fragment first\n"
|
||||
" --disorder\t\t\t\t; when splitting simulate sending second fragment first\n"
|
||||
#endif
|
||||
" --hostcase\t\t\t; change Host: => host:\n"
|
||||
" --hostspell\t\t\t; exact spelling of \"Host\" header. must be 4 chars. default is \"host\"\n"
|
||||
" --hostdot\t\t\t; add \".\" after Host: name\n"
|
||||
" --hosttab\t\t\t; add tab after Host: name\n"
|
||||
" --hostnospace\t\t\t; remove space after Host:\n"
|
||||
" --hostpad=<bytes>\t\t; add dummy padding headers before Host:\n"
|
||||
" --domcase\t\t\t; mix domain case : Host: TeSt.cOm\n"
|
||||
" --methodspace\t\t\t; add extra space after method\n"
|
||||
" --methodeol\t\t\t; add end-of-line before method\n"
|
||||
" --unixeol\t\t\t; replace 0D0A to 0A\n"
|
||||
" --tlsrec=sni\t\t\t; make 2 TLS records. split at SNI. don't split if SNI is not present\n"
|
||||
" --tlsrec-pos=<pos>\t\t; make 2 TLS records. split at specified pos\n"
|
||||
" --hostcase\t\t\t\t; change Host: => host:\n"
|
||||
" --hostspell\t\t\t\t; exact spelling of \"Host\" header. must be 4 chars. default is \"host\"\n"
|
||||
" --hostdot\t\t\t\t; add \".\" after Host: name\n"
|
||||
" --hosttab\t\t\t\t; add tab after Host: name\n"
|
||||
" --hostnospace\t\t\t\t; remove space after Host:\n"
|
||||
" --hostpad=<bytes>\t\t\t; add dummy padding headers before Host:\n"
|
||||
" --domcase\t\t\t\t; mix domain case : Host: TeSt.cOm\n"
|
||||
" --methodspace\t\t\t\t; add extra space after method\n"
|
||||
" --methodeol\t\t\t\t; add end-of-line before method\n"
|
||||
" --unixeol\t\t\t\t; replace 0D0A to 0A\n"
|
||||
" --tlsrec=sni\t\t\t\t; make 2 TLS records. split at SNI. don't split if SNI is not present\n"
|
||||
" --tlsrec-pos=<pos>\t\t\t; make 2 TLS records. split at specified pos\n",
|
||||
HOSTLIST_AUTO_FAIL_THRESHOLD_DEFAULT, HOSTLIST_AUTO_FAIL_TIME_DEFAULT
|
||||
);
|
||||
exit(1);
|
||||
}
|
||||
@@ -183,16 +195,9 @@ static void cleanup_params(void)
|
||||
{
|
||||
strlist_destroy(¶ms.hostlist_files);
|
||||
strlist_destroy(¶ms.hostlist_exclude_files);
|
||||
if (params.hostlist_exclude)
|
||||
{
|
||||
StrPoolDestroy(¶ms.hostlist_exclude);
|
||||
params.hostlist_exclude = NULL;
|
||||
}
|
||||
if (params.hostlist)
|
||||
{
|
||||
StrPoolDestroy(¶ms.hostlist);
|
||||
params.hostlist = NULL;
|
||||
}
|
||||
StrPoolDestroy(¶ms.hostlist_exclude);
|
||||
StrPoolDestroy(¶ms.hostlist);
|
||||
HostFailPoolDestroy(¶ms.hostlist_auto_fail_counters);
|
||||
}
|
||||
static void exithelp_clean(void)
|
||||
{
|
||||
@@ -246,6 +251,8 @@ void parse_params(int argc, char *argv[])
|
||||
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;
|
||||
LIST_INIT(¶ms.hostlist_files);
|
||||
LIST_INIT(¶ms.hostlist_exclude_files);
|
||||
|
||||
@@ -294,18 +301,22 @@ void parse_params(int argc, char *argv[])
|
||||
{ "tlsrec-pos",required_argument,0,0 },// optidx=32
|
||||
{ "hostlist",required_argument,0,0 },// optidx=33
|
||||
{ "hostlist-exclude",required_argument,0,0 },// optidx=34
|
||||
{ "pidfile",required_argument,0,0 },// optidx=35
|
||||
{ "debug",optional_argument,0,0 },// optidx=36
|
||||
{ "local-rcvbuf",required_argument,0,0 },// optidx=37
|
||||
{ "local-sndbuf",required_argument,0,0 },// optidx=38
|
||||
{ "remote-rcvbuf",required_argument,0,0 },// optidx=39
|
||||
{ "remote-sndbuf",required_argument,0,0 },// optidx=40
|
||||
{ "socks",no_argument,0,0 },// optidx=41
|
||||
{ "no-resolve",no_argument,0,0 },// optidx=42
|
||||
{ "skip-nodelay",no_argument,0,0 },// optidx=43
|
||||
{ "hostlist-auto",required_argument,0,0}, // optidx=35
|
||||
{ "hostlist-auto-fail-threshold",required_argument,0,0}, // optidx=36
|
||||
{ "hostlist-auto-fail-time",required_argument,0,0}, // optidx=37
|
||||
{ "pidfile",required_argument,0,0 },// optidx=38
|
||||
{ "debug",optional_argument,0,0 },// optidx=39
|
||||
{ "local-rcvbuf",required_argument,0,0 },// optidx=40
|
||||
{ "local-sndbuf",required_argument,0,0 },// optidx=41
|
||||
{ "remote-rcvbuf",required_argument,0,0 },// optidx=42
|
||||
{ "remote-sndbuf",required_argument,0,0 },// optidx=43
|
||||
{ "socks",no_argument,0,0 },// optidx=44
|
||||
{ "no-resolve",no_argument,0,0 },// optidx=45
|
||||
{ "skip-nodelay",no_argument,0,0 },// optidx=46
|
||||
#if defined(BSD) && !defined(__OpenBSD__) && !defined(__APPLE__)
|
||||
{ "enable-pf",no_argument,0,0 },// optidx=44
|
||||
{ "enable-pf",no_argument,0,0 },// optidx=47
|
||||
#endif
|
||||
{ "hostlist-auto-retrans-threshold",optional_argument,0,0}, // ignored. for nfqws command line compatibility
|
||||
{ NULL,0,NULL,0 }
|
||||
};
|
||||
while ((v = getopt_long_only(argc, argv, "", long_options, &option_index)) != -1)
|
||||
@@ -544,36 +555,84 @@ void parse_params(int argc, char *argv[])
|
||||
}
|
||||
params.tamper = true;
|
||||
break;
|
||||
case 35: /* pidfile */
|
||||
case 35: /* hostlist-auto */
|
||||
if (*params.hostlist_auto_filename)
|
||||
{
|
||||
fprintf(stderr, "only one auto hostlist is supported\n");
|
||||
exit_clean(1);
|
||||
}
|
||||
{
|
||||
FILE *F = fopen(optarg,"a+t");
|
||||
if (!F)
|
||||
{
|
||||
fprintf(stderr, "cannot create %s\n", optarg);
|
||||
exit_clean(1);
|
||||
}
|
||||
bool bGzip = is_gzip(F);
|
||||
fclose(F);
|
||||
if (bGzip)
|
||||
{
|
||||
fprintf(stderr, "gzipped auto hostlists are not supported\n");
|
||||
exit_clean(1);
|
||||
}
|
||||
if (params.droproot && chown(optarg, params.uid, -1))
|
||||
fprintf(stderr, "could not chown %s. auto hostlist file may not be writable after privilege drop\n", optarg);
|
||||
}
|
||||
if (!strlist_add(¶ms.hostlist_files, optarg))
|
||||
{
|
||||
fprintf(stderr, "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';
|
||||
params.tamper = true; // need to detect blocks and update autohostlist. cannot just slice.
|
||||
break;
|
||||
case 36: /* 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)
|
||||
{
|
||||
fprintf(stderr, "auto hostlist fail threshold must be within 1..20\n");
|
||||
exit_clean(1);
|
||||
}
|
||||
break;
|
||||
case 37: /* hostlist-auto-fail-time */
|
||||
params.hostlist_auto_fail_time = (uint8_t)atoi(optarg);
|
||||
if (params.hostlist_auto_fail_time<1)
|
||||
{
|
||||
fprintf(stderr, "auto hostlist fail time is not valid\n");
|
||||
exit_clean(1);
|
||||
}
|
||||
break;
|
||||
case 38: /* pidfile */
|
||||
strncpy(params.pidfile,optarg,sizeof(params.pidfile));
|
||||
params.pidfile[sizeof(params.pidfile)-1]='\0';
|
||||
break;
|
||||
case 36:
|
||||
case 39:
|
||||
params.debug = optarg ? atoi(optarg) : 1;
|
||||
break;
|
||||
case 37: /* local-rcvbuf */
|
||||
case 40: /* local-rcvbuf */
|
||||
params.local_rcvbuf = atoi(optarg)/2;
|
||||
break;
|
||||
case 38: /* local-sndbuf */
|
||||
case 41: /* local-sndbuf */
|
||||
params.local_sndbuf = atoi(optarg)/2;
|
||||
break;
|
||||
case 39: /* remote-rcvbuf */
|
||||
case 42: /* remote-rcvbuf */
|
||||
params.remote_rcvbuf = atoi(optarg)/2;
|
||||
break;
|
||||
case 40: /* remote-sndbuf */
|
||||
case 43: /* remote-sndbuf */
|
||||
params.remote_sndbuf = atoi(optarg)/2;
|
||||
break;
|
||||
case 41: /* socks */
|
||||
case 44: /* socks */
|
||||
params.proxy_type = CONN_TYPE_SOCKS;
|
||||
break;
|
||||
case 42: /* no-resolve */
|
||||
case 45: /* no-resolve */
|
||||
params.no_resolve = true;
|
||||
break;
|
||||
case 43: /* skip-nodelay */
|
||||
case 46: /* skip-nodelay */
|
||||
params.skip_nodelay = true;
|
||||
break;
|
||||
#if defined(BSD) && !defined(__OpenBSD__) && !defined(__APPLE__)
|
||||
case 44: /* enable-pf */
|
||||
case 47: /* enable-pf */
|
||||
params.pf_enable = true;
|
||||
break;
|
||||
#endif
|
||||
@@ -599,6 +658,7 @@ void parse_params(int argc, char *argv[])
|
||||
fprintf(stderr, "Include hostlist load failed\n");
|
||||
exit_clean(1);
|
||||
}
|
||||
if (*params.hostlist_auto_filename) NonEmptyHostlist(¶ms.hostlist);
|
||||
if (!LoadHostLists(¶ms.hostlist_exclude, ¶ms.hostlist_exclude_files))
|
||||
{
|
||||
fprintf(stderr, "Exclude hostlist load failed\n");
|
||||
@@ -1020,6 +1080,7 @@ int main(int argc, char *argv[])
|
||||
if (!params.tamper) printf("TCP proxy mode (no tampering)\n");
|
||||
|
||||
signal(SIGHUP, onhup);
|
||||
signal(SIGUSR2, onusr2);
|
||||
|
||||
retval = event_loop(listen_fd,params.binds_last+1);
|
||||
exit_v = retval < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
|
@@ -24,6 +24,7 @@
|
||||
#include "socks.h"
|
||||
#include "helpers.h"
|
||||
|
||||
|
||||
// keep separate legs counter. counting every time thousands of legs can consume cpu
|
||||
static int legs_local, legs_remote;
|
||||
/*
|
||||
@@ -376,6 +377,7 @@ static void free_conn(tproxy_conn_t *conn)
|
||||
}
|
||||
conn_free_buffers(conn);
|
||||
if (conn->partner) conn->partner->partner=NULL;
|
||||
if (conn->track.hostname) free(conn->track.hostname);
|
||||
free(conn);
|
||||
}
|
||||
static tproxy_conn_t *new_conn(int fd, bool remote)
|
||||
@@ -890,6 +892,25 @@ static bool handle_proxy_mode(tproxy_conn_t *conn, struct tailhead *conn_list)
|
||||
return false;
|
||||
}
|
||||
|
||||
static void tamper(tproxy_conn_t *conn, uint8_t *segment, size_t segment_buffer_size, size_t *segment_size, size_t *split_pos)
|
||||
{
|
||||
*split_pos=0;
|
||||
if (params.tamper)
|
||||
{
|
||||
if (conn->remote)
|
||||
{
|
||||
if (conn_partner_alive(conn) && !conn->partner->track.bTamperInCutoff)
|
||||
{
|
||||
tamper_in(&conn->partner->track,segment,segment_buffer_size,segment_size);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
tamper_out(&conn->track,segment,segment_buffer_size,segment_size,split_pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define RD_BLOCK_SIZE 65536
|
||||
#define MAX_WASTE (1024*1024)
|
||||
static bool handle_epoll(tproxy_conn_t *conn, struct tailhead *conn_list, uint32_t evt)
|
||||
@@ -942,7 +963,7 @@ static bool handle_epoll(tproxy_conn_t *conn, struct tailhead *conn_list, uint32
|
||||
if (numbytes>0)
|
||||
{
|
||||
#ifdef SPLICE_PRESENT
|
||||
if (!params.tamper || conn->remote)
|
||||
if (!params.tamper || conn->remote && conn->track.bTamperInCutoff)
|
||||
{
|
||||
// incoming data from remote leg we splice without touching
|
||||
// pipe is in the local leg, so its in conn->partner->splice_pipe
|
||||
@@ -978,13 +999,11 @@ static bool handle_epoll(tproxy_conn_t *conn, struct tailhead *conn_list, uint32
|
||||
{
|
||||
conn->trd+=rd;
|
||||
|
||||
size_t split_pos=0;
|
||||
size_t split_pos;
|
||||
|
||||
bs = rd;
|
||||
#ifndef SPLICE_PRESENT
|
||||
if (!conn->remote && params.tamper)
|
||||
#endif
|
||||
modify_tcp_segment(buf,sizeof(buf),&bs,&split_pos);
|
||||
|
||||
tamper(conn, buf, sizeof(buf), &bs, &split_pos);
|
||||
|
||||
if (split_pos)
|
||||
{
|
||||
@@ -1070,8 +1089,13 @@ static bool read_all_and_buffer(tproxy_conn_t *conn, int buffer_number)
|
||||
conn->partner->wr_buf[buffer_number].len = rd;
|
||||
|
||||
conn->partner->bFlowOut = true;
|
||||
|
||||
size_t split_pos;
|
||||
tamper(conn, conn->partner->wr_buf[buffer_number].data, numbytes, &conn->partner->wr_buf[buffer_number].len, &split_pos);
|
||||
|
||||
if (epoll_update_flow(conn->partner))
|
||||
return true;
|
||||
|
||||
}
|
||||
send_buffer_free(conn->partner->wr_buf+buffer_number);
|
||||
}
|
||||
@@ -1254,6 +1278,8 @@ int event_loop(const int *listen_fd, size_t listen_fd_ct)
|
||||
VPRINT("Socket fd=%d (partner_fd=%d, remote=%d) %s so_error=%d (%s)",conn->fd,conn->partner ? conn->partner->fd : 0,conn->remote,se,errn,strerror(errn));
|
||||
proxy_remote_conn_ack(conn,errn);
|
||||
read_all_and_buffer(conn,3);
|
||||
if (errn==ECONNRESET && conn->remote && conn_partner_alive(conn)) rst_in(&conn->partner->track);
|
||||
|
||||
conn_close_with_partner_check(&conn_list,&close_list,conn);
|
||||
continue;
|
||||
}
|
||||
@@ -1270,6 +1296,7 @@ int event_loop(const int *listen_fd, size_t listen_fd_ct)
|
||||
{
|
||||
DBGPRINT("EPOLLRDHUP")
|
||||
read_all_and_buffer(conn,2);
|
||||
if (!conn->remote) hup_out(&conn->track);
|
||||
|
||||
if (conn_has_unsent(conn))
|
||||
{
|
||||
|
@@ -3,6 +3,7 @@
|
||||
#include <stdbool.h>
|
||||
#include <sys/queue.h>
|
||||
#include <time.h>
|
||||
#include "tamper.h"
|
||||
|
||||
#define BACKLOG 10
|
||||
#define MAX_EPOLL_EVENTS 64
|
||||
@@ -27,7 +28,7 @@ typedef uint8_t conn_state_t;
|
||||
// when pos==len its time to free buffer
|
||||
struct send_buffer
|
||||
{
|
||||
char *data;
|
||||
uint8_t *data;
|
||||
size_t len,pos;
|
||||
int ttl;
|
||||
};
|
||||
@@ -84,6 +85,8 @@ struct tproxy_conn
|
||||
// buffer cannot be sent if there is unsent data in a lower buffer
|
||||
struct send_buffer wr_buf[4];
|
||||
|
||||
t_ctrack track;
|
||||
|
||||
//Create the struct which contains ptrs to next/prev element
|
||||
TAILQ_ENTRY(tproxy_conn) conn_ptrs;
|
||||
};
|
||||
|
Reference in New Issue
Block a user