diff --git a/docs/changes.txt b/docs/changes.txt index c50a3d1..86162da 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -85,3 +85,5 @@ v0.7 * nfqws2, zapret-lib : fix non-working % and # arg substitution under orchestrator * nfqws2, zapret-lib : structure conntrack in/out positions. pass in desync.track.pos.{client,server,direct,reverse} position tables +* nfqws2: autohostlist: trigger RST and http redirect failures only within specified relative sequence +* nfqws2: autohostlist: trigger http redirect failure if payload is http_req without connection proto check diff --git a/nfq2/conntrack.c b/nfq2/conntrack.c index f586978..363c07f 100644 --- a/nfq2/conntrack.c +++ b/nfq2/conntrack.c @@ -140,7 +140,7 @@ static void ConntrackApplyPos(const struct tcphdr *tcp, t_ctrack *t, bool bRever scale = tcp_find_scale_factor(tcp); mss = ntohs(tcp_find_mss(tcp)); - direct->seq_last = ntohl(tcp->th_seq); + direct->seq_last = ntohl(tcp->th_seq); direct->pos = direct->seq_last + len_payload; reverse->pos = reverse->seq_last = ntohl(tcp->th_ack); if (t->pos.state == SYN) diff --git a/nfq2/conntrack.h b/nfq2/conntrack.h index ba8e606..291f46e 100644 --- a/nfq2/conntrack.h +++ b/nfq2/conntrack.h @@ -63,7 +63,7 @@ typedef struct bool dp_search_complete; uint8_t req_retrans_counter; // number of request retransmissions - bool retrans_detect_finalized; + bool failure_detect_finalized; uint8_t incoming_ttl; diff --git a/nfq2/desync.c b/nfq2/desync.c index 5c6009a..4509ce1 100644 --- a/nfq2/desync.c +++ b/nfq2/desync.c @@ -252,12 +252,12 @@ static bool auto_hostlist_retrans(t_ctrack *ctrack, uint8_t l4proto, int thresho { if (l4proto == IPPROTO_TCP) { - if (ctrack->retrans_detect_finalized) + if (ctrack->failure_detect_finalized) return false; if (!seq_within(ctrack->pos.client.seq_last, ctrack->pos.client.seq0, ctrack->pos.client.seq0 + ctrack->dp->hostlist_auto_retrans_maxseq)) { - ctrack->retrans_detect_finalized = true; - DLOG("retrans : tcp seq %u not within the req range %u-%u. stop tracking.\n", ctrack->pos.client.seq_last, ctrack->pos.client.seq0, ctrack->pos.client.seq0 + ctrack->dp->hostlist_auto_retrans_maxseq); + ctrack->failure_detect_finalized = true; + DLOG("retrans : tcp seq %u not within range %u-%u. stop tracking.\n", ctrack->pos.client.seq_last, ctrack->pos.client.seq0, ctrack->pos.client.seq0 + ctrack->dp->hostlist_auto_retrans_maxseq); ctrack_stop_retrans_counter(ctrack); auto_hostlist_reset_fail_counter(ctrack->dp, ctrack->hostname, client_ip_port, l7proto); return false; @@ -270,7 +270,7 @@ static bool auto_hostlist_retrans(t_ctrack *ctrack, uint8_t l4proto, int thresho { DLOG("retrans threshold reached : %u/%u\n", ctrack->req_retrans_counter, threshold); ctrack_stop_retrans_counter(ctrack); - ctrack->retrans_detect_finalized = true; + ctrack->failure_detect_finalized = true; return true; } DLOG("retrans counter : %u/%u\n", ctrack->req_retrans_counter, threshold); @@ -1097,49 +1097,48 @@ static uint8_t dpi_desync_tcp_packet_play( // process reply packets for auto hostlist mode // by looking at RSTs or HTTP replies we decide whether original request looks like DPI blocked // we only process first-sequence replies. do not react to subsequent redirects or RSTs - if (!params.server && ctrack && ctrack->hostname && ctrack->hostname_ah_check && (ctrack->pos.server.seq_last - ctrack->pos.server.seq0) == 1) + if (!params.server && ctrack && ctrack->hostname_ah_check && !ctrack->failure_detect_finalized) { - bool bFail = false; - char client_ip_port[48]; if (*params.hostlist_auto_debuglog) ntop46_port((struct sockaddr*)&dst, client_ip_port, sizeof(client_ip_port)); else *client_ip_port = 0; + if (seq_within(ctrack->pos.server.seq_last, ctrack->pos.server.seq0, ctrack->pos.server.seq0 + dp->hostlist_auto_incoming_maxseq)) + { + bool bFail = false; + uint32_t rseq = ctrack->pos.server.seq_last - ctrack->pos.server.seq0; - if (dis->tcp->th_flags & TH_RST) - { - DLOG("incoming RST detected for hostname %s\n", ctrack->hostname); - HOSTLIST_DEBUGLOG_APPEND("%s : profile %u (%s) : client %s : proto %s : incoming RST", ctrack->hostname, ctrack->dp->n, PROFILE_NAME(dp), client_ip_port, l7proto_str(l7proto)); - bFail = true; - } - else if (dis->len_payload && l7proto == L7_HTTP) - { - if (l7payload == L7P_HTTP_REPLY) + if (dis->tcp->th_flags & TH_RST) { - DLOG("incoming HTTP reply detected for hostname %s\n", ctrack->hostname); + DLOG("incoming RST detected for hostname %s rseq %u\n", ctrack->hostname, rseq); + HOSTLIST_DEBUGLOG_APPEND("%s : profile %u (%s) : client %s : proto %s : rseq %u : incoming RST", ctrack->hostname, ctrack->dp->n, PROFILE_NAME(dp), client_ip_port, l7proto_str(l7proto), rseq); + bFail = true; + } + else if (dis->len_payload && l7payload == L7P_HTTP_REPLY) + { + DLOG("incoming HTTP reply detected for hostname %s rseq\n", ctrack->hostname, rseq); bFail = HttpReplyLooksLikeDPIRedirect(dis->data_payload, dis->len_payload, ctrack->hostname); if (bFail) { DLOG("redirect to another domain detected. possibly DPI redirect.\n"); - HOSTLIST_DEBUGLOG_APPEND("%s : profile %u (%s) : client %s : proto %s : redirect to another domain", ctrack->hostname, ctrack->dp->n, PROFILE_NAME(dp), client_ip_port, l7proto_str(l7proto)); + HOSTLIST_DEBUGLOG_APPEND("%s : profile %u (%s) : client %s : proto %s : rseq %u : redirect to another domain", ctrack->hostname, ctrack->dp->n, PROFILE_NAME(dp), client_ip_port, l7proto_str(l7proto), rseq); } else DLOG("local or in-domain redirect detected. it's not a DPI redirect.\n"); } - else + if (bFail) { - // received not http reply. do not monitor this connection anymore - DLOG("incoming unknown HTTP data detected for hostname %s\n", ctrack->hostname); + auto_hostlist_failed(dp, ctrack->hostname, ctrack->hostname_is_ip, client_ip_port, l7proto); + ctrack->failure_detect_finalized = true; } } - if (bFail) - auto_hostlist_failed(dp, ctrack->hostname, ctrack->hostname_is_ip, client_ip_port, l7proto); else - if (dis->len_payload) - auto_hostlist_reset_fail_counter(dp, ctrack->hostname, client_ip_port, l7proto); - if (dis->tcp->th_flags & TH_RST) - ctrack->hostname_ah_check = false; // do not react to further dup RSTs + { + // incoming_maxseq exceeded. treat connection as successful + auto_hostlist_reset_fail_counter(dp, ctrack->hostname, client_ip_port, l7proto); + ctrack->failure_detect_finalized = true; + } } } // not reverse diff --git a/nfq2/nfqws.c b/nfq2/nfqws.c index 5c4cfcf..18753ed 100644 --- a/nfq2/nfqws.c +++ b/nfq2/nfqws.c @@ -1435,6 +1435,7 @@ static void exithelp(void) " --hostlist-auto-fail-time=\t\t\t; all failed attemps must be within these seconds (default : %d)\n" " --hostlist-auto-retrans-threshold=\t\t; how many request retransmissions cause attempt to fail (default : %d)\n" " --hostlist-auto-retrans-maxseq=\t\t\t; count retransmissions only within this relative sequence (default : %u)\n" + " --hostlist-auto-incoming-maxseq=\t\t\t; treat tcp connection as successful if incoming relative sequence exceedes this threshold (default : %u)\n" " --hostlist-auto-debug=\t\t\t; debug auto hostlist positives (global parameter)\n" "\nLUA PACKET PASS MODE:\n" " --payload=type[,type]\t\t\t\t\t; set payload types following LUA functions should process : %s\n" @@ -1450,7 +1451,8 @@ static void exithelp(void) LUA_GC_INTERVAL, all_protos, HOSTLIST_AUTO_FAIL_THRESHOLD_DEFAULT, HOSTLIST_AUTO_FAIL_TIME_DEFAULT, - HOSTLIST_AUTO_RETRANS_THRESHOLD_DEFAULT, HOSTLIST_AUTO_RETRANS_MAXSEQ, + HOSTLIST_AUTO_RETRANS_THRESHOLD_DEFAULT, + HOSTLIST_AUTO_RETRANS_MAXSEQ, HOSTLIST_AUTO_INCOMING_MAXSEQ, all_payloads ); exit(1); @@ -1548,6 +1550,7 @@ enum opt_indices { IDX_HOSTLIST_AUTO_FAIL_TIME, IDX_HOSTLIST_AUTO_RETRANS_THRESHOLD, IDX_HOSTLIST_AUTO_RETRANS_MAXSEQ, + IDX_HOSTLIST_AUTO_INCOMING_MAXSEQ, IDX_HOSTLIST_AUTO_DEBUG, IDX_NEW, IDX_SKIP, @@ -1633,6 +1636,7 @@ static const struct option long_options[] = { [IDX_HOSTLIST_AUTO_FAIL_TIME] = {"hostlist-auto-fail-time", required_argument, 0, 0}, [IDX_HOSTLIST_AUTO_RETRANS_THRESHOLD] = {"hostlist-auto-retrans-threshold", required_argument, 0, 0}, [IDX_HOSTLIST_AUTO_RETRANS_MAXSEQ] = {"hostlist-auto-retrans-maxseq", required_argument, 0, 0}, + [IDX_HOSTLIST_AUTO_INCOMING_MAXSEQ] = {"hostlist-auto-incoming-maxseq", required_argument, 0, 0}, [IDX_HOSTLIST_AUTO_DEBUG] = {"hostlist-auto-debug", required_argument, 0, 0}, [IDX_NEW] = {"new", no_argument, 0, 0}, [IDX_SKIP] = {"skip", no_argument, 0, 0}, @@ -2100,6 +2104,9 @@ int main(int argc, char **argv) case IDX_HOSTLIST_AUTO_RETRANS_MAXSEQ: dp->hostlist_auto_retrans_maxseq = (uint32_t)atoi(optarg); break; + case IDX_HOSTLIST_AUTO_INCOMING_MAXSEQ: + dp->hostlist_auto_incoming_maxseq = (uint32_t)atoi(optarg); + break; case IDX_HOSTLIST_AUTO_DEBUG: { FILE *F = fopen(optarg, "a+t"); diff --git a/nfq2/params.c b/nfq2/params.c index f02e427..d646059 100644 --- a/nfq2/params.c +++ b/nfq2/params.c @@ -343,6 +343,7 @@ void dp_init(struct desync_profile *dp) dp->hostlist_auto_fail_time = HOSTLIST_AUTO_FAIL_TIME_DEFAULT; dp->hostlist_auto_retrans_threshold = HOSTLIST_AUTO_RETRANS_THRESHOLD_DEFAULT; dp->hostlist_auto_retrans_maxseq = HOSTLIST_AUTO_RETRANS_MAXSEQ; + dp->hostlist_auto_incoming_maxseq = HOSTLIST_AUTO_INCOMING_MAXSEQ; dp->filter_ipv4 = dp->filter_ipv6 = true; } static void dp_clear_dynamic(struct desync_profile *dp) diff --git a/nfq2/params.h b/nfq2/params.h index 6a17904..0fce471 100644 --- a/nfq2/params.h +++ b/nfq2/params.h @@ -31,6 +31,7 @@ #define HOSTLIST_AUTO_FAIL_TIME_DEFAULT 60 #define HOSTLIST_AUTO_RETRANS_THRESHOLD_DEFAULT 3 #define HOSTLIST_AUTO_RETRANS_MAXSEQ 65536 +#define HOSTLIST_AUTO_INCOMING_MAXSEQ 4096 #define IPCACHE_LIFETIME 7200 @@ -79,7 +80,7 @@ struct desync_profile // pointer to autohostlist. NULL if no autohostlist for the profile. struct hostlist_file *hostlist_auto; int hostlist_auto_fail_threshold, hostlist_auto_fail_time, hostlist_auto_retrans_threshold; - uint32_t hostlist_auto_retrans_maxseq; + uint32_t hostlist_auto_retrans_maxseq, hostlist_auto_incoming_maxseq; hostfail_pool *hostlist_auto_fail_counters;