diff --git a/nfq/desync.c b/nfq/desync.c
index cf942e1..729ca29 100644
--- a/nfq/desync.c
+++ b/nfq/desync.c
@@ -97,11 +97,11 @@ bool desync_only_first_stage(enum dpi_desync_mode mode)
 }
 bool desync_valid_second_stage(enum dpi_desync_mode mode)
 {
-	return mode==DESYNC_NONE || mode==DESYNC_DISORDER || mode==DESYNC_DISORDER2 || mode==DESYNC_SPLIT || mode==DESYNC_SPLIT2 || mode==DESYNC_IPFRAG2 || mode==DESYNC_UDPLEN || mode==DESYNC_TAMPER;
+	return mode==DESYNC_NONE || mode==DESYNC_DISORDER || mode==DESYNC_DISORDER2 || mode==DESYNC_SPLIT || mode==DESYNC_SPLIT2 || mode==DESYNC_MULTISPLIT || mode==DESYNC_MULTIDISORDER || mode==DESYNC_IPFRAG2 || mode==DESYNC_UDPLEN || mode==DESYNC_TAMPER;
 }
 bool desync_valid_second_stage_tcp(enum dpi_desync_mode mode)
 {
-	return mode==DESYNC_NONE || mode==DESYNC_DISORDER || mode==DESYNC_DISORDER2 || mode==DESYNC_SPLIT || mode==DESYNC_SPLIT2 || mode==DESYNC_IPFRAG2;
+	return mode==DESYNC_NONE || mode==DESYNC_DISORDER || mode==DESYNC_DISORDER2 || mode==DESYNC_SPLIT || mode==DESYNC_SPLIT2 || mode==DESYNC_MULTISPLIT || mode==DESYNC_MULTIDISORDER || mode==DESYNC_IPFRAG2;
 }
 bool desync_valid_second_stage_udp(enum dpi_desync_mode mode)
 {
@@ -131,6 +131,10 @@ enum dpi_desync_mode desync_mode_from_string(const char *s)
 		return DESYNC_SPLIT;
 	else if (!strcmp(s,"split2"))
 		return DESYNC_SPLIT2;
+	else if (!strcmp(s,"multisplit"))
+		return DESYNC_MULTISPLIT;
+	else if (!strcmp(s,"multidisorder"))
+		return DESYNC_MULTIDISORDER;
 	else if (!strcmp(s,"ipfrag2"))
 		return DESYNC_IPFRAG2;
 	else if (!strcmp(s,"hopbyhop"))
@@ -582,22 +586,7 @@ static bool replay_queue(struct rawpacket_tailhead *q);
 
 static size_t pos_normalize(size_t split_pos, size_t reasm_offset, size_t len_payload)
 {
-	size_t rsplit_pos = split_pos;
-	// normalize split pos to current packet
-	split_pos=(split_pos>reasm_offset && (split_pos-reasm_offset)<len_payload) ? split_pos-reasm_offset : 0;
-	if (rsplit_pos)
-	{
-		if (split_pos==rsplit_pos)
-			DLOG("split pos %zu\n",split_pos);
-		else
-		{
-			if (split_pos)
-				DLOG("split pos was normalized to packet data offset : %zu -> %zu\n",rsplit_pos,split_pos);
-			else
-				DLOG("split pos %zu is outside of this packet %zu-%zu\n",rsplit_pos,reasm_offset,reasm_offset+len_payload);
-		}
-	}
-	return split_pos;
+	return (split_pos>reasm_offset && (split_pos-reasm_offset)<len_payload) ? split_pos-reasm_offset : 0;
 }
 
 static void autottl_discover(t_ctrack *ctrack, bool bIpv6)
@@ -618,6 +607,31 @@ static void autottl_discover(t_ctrack *ctrack, bool bIpv6)
 	}
 }
 
+static size_t resolve_split(const uint8_t *data, size_t sz, t_l7proto l7proto, const struct split_pos *sp)
+{
+	switch(l7proto)
+	{
+		case HTTP:
+			return HttpPos(sp->marker, sp->pos, data, sz);
+		case TLS:
+			return TLSPos(sp->marker, sp->pos, data, sz);
+		default:
+			return AnyProtoPos(sp->marker, sp->pos, data, sz);
+	}
+}
+static void resolve_multisplit(const uint8_t *data, size_t sz, t_l7proto l7proto, const struct desync_profile *dp, size_t *pos, int *pos_count)
+{
+	int i,j;
+	for(i=j=0;i<dp->split_count;i++)
+	{
+		pos[j] = resolve_split(data,sz,l7proto,dp->splits+i);
+		if (pos[j]) j++;
+	}
+	qsort_size_t(pos, j);
+	j=unique_size_t(pos, j);
+	*pos_count=j;
+}
+
 static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint32_t fwmark, const char *ifout, struct dissect *dis)
 {
 	uint8_t verdict=VERDICT_PASS;
@@ -845,7 +859,7 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint
 		}
 
 	} // !replay
-	
+
 	if (!(dis->tcp->th_flags & TH_SYN) && dis->len_payload)
 	{
 		const uint8_t *fake;
@@ -856,6 +870,9 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint
 		const uint8_t *rdata_payload = dis->data_payload;
 		size_t rlen_payload = dis->len_payload;
 		size_t split_pos;
+		size_t multisplit_pos[MAX_SPLITS];
+		int multisplit_count;
+		int i;
 		t_l7proto l7proto = UNKNOWN;
 
 		if (replay)
@@ -1065,35 +1082,12 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint
 			}
 		}
 
-		// desync profile may have changed after hostname was revealed
-		switch(l7proto)
-		{
-			case HTTP:
-				fake = dp->fake_http;
-				fake_size = dp->fake_http_size;
-				split_pos = HttpPos(dp->desync_split_http_req, dp->desync_split_pos, rdata_payload, rlen_payload);
-				break;
-			case TLS:
-				fake = dp->fake_tls;
-				fake_size = dp->fake_tls_size;
-				split_pos = TLSPos(dp->desync_split_tls, dp->desync_split_pos, rdata_payload, rlen_payload, 0);
-				break;
-			default:
-				fake = dp->fake_unknown;
-				fake_size = dp->fake_unknown_size;
-				split_pos=dp->desync_split_pos;
-				break;
-		}
-
-		// we do not need reasm buffer anymore
-		reasm_orig_cancel(ctrack);
-		rdata_payload=NULL;
-
 		if (l7proto==UNKNOWN)
 		{
 			if (!dp->desync_any_proto)
 			{
 				DLOG("not applying tampering to unknown protocol\n");
+				reasm_orig_cancel(ctrack);
 				return verdict;
 			}
 			DLOG("applying tampering to unknown protocol\n");
@@ -1135,7 +1129,63 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint
 			}
 		}
 
-		if (dp->desync_mode==DESYNC_NONE) return verdict;
+		if (dp->desync_mode==DESYNC_NONE)
+		{
+			reasm_orig_cancel(ctrack);
+			return verdict;
+		}
+
+		const struct split_pos *spos;
+		switch(l7proto)
+		{
+			case HTTP:
+				fake = dp->fake_http;
+				fake_size = dp->fake_http_size;
+				spos = &dp->split_http;
+				break;
+			case TLS:
+				fake = dp->fake_tls;
+				fake_size = dp->fake_tls_size;
+				spos = &dp->split_tls;
+				break;
+			default:
+				fake = dp->fake_unknown;
+				fake_size = dp->fake_unknown_size;
+				spos = &dp->split_unknown;
+				break;
+		}
+		if (dp->desync_mode==DESYNC_MULTISPLIT || dp->desync_mode==DESYNC_MULTIDISORDER || dp->desync_mode2==DESYNC_MULTISPLIT || dp->desync_mode2==DESYNC_MULTIDISORDER)
+		{
+			split_pos=0;
+			resolve_multisplit(rdata_payload, rlen_payload, l7proto, dp, multisplit_pos, &multisplit_count);
+			if (params.debug)
+			{
+				if (multisplit_count)
+				{
+					DLOG("multisplit pos: ");
+					for (i=0;i<multisplit_count;i++) DLOG("%zu ",multisplit_pos[i]);
+					DLOG("\n");
+				}
+				else
+					DLOG("all multisplit pos are outside of this packet\n");
+			}
+		}
+		else if (dp->desync_mode==DESYNC_SPLIT || dp->desync_mode==DESYNC_SPLIT2 || dp->desync_mode==DESYNC_DISORDER || dp->desync_mode==DESYNC_DISORDER2 ||
+			dp->desync_mode2==DESYNC_SPLIT || dp->desync_mode2==DESYNC_SPLIT2 || dp->desync_mode2==DESYNC_DISORDER || dp->desync_mode2==DESYNC_DISORDER2)
+		{
+			multisplit_count=0;
+			split_pos = resolve_split(rdata_payload, rlen_payload, l7proto, spos);
+			DLOG("regular split pos: %zu\n",split_pos);
+		}
+		else
+		{
+			multisplit_count=0;
+			split_pos = 0;
+		}
+
+		// we do not need reasm buffer anymore
+		reasm_orig_cancel(ctrack);
+		rdata_payload=NULL;
 
 		if (params.debug)
 		{
@@ -1144,27 +1194,45 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint
 			ntop46_port((struct sockaddr *)&dst, s2, sizeof(s2));
 			DLOG("dpi desync src=%s dst=%s\n",s1,s2);
 		}
-		
+
 		if (!split_pos || split_pos>rlen_payload) split_pos=1;
 		split_pos=pos_normalize(split_pos,reasm_offset,dis->len_payload);
+		if (split_pos)
+			DLOG("normalized regular split pos : %zu\n",split_pos);
+		else
+			DLOG("regular split pos is outside of this packet\n");
+		if (multisplit_count)
+		{
+			int j;
+			for (i=j=0;i<multisplit_count;i++)
+			{
+				multisplit_pos[j]=pos_normalize(multisplit_pos[i],reasm_offset,dis->len_payload);
+				if (multisplit_pos[j]) j++;
+			}
+			multisplit_count=j;
+			if (params.debug)
+			{
+				if (multisplit_count)
+				{
+					DLOG("normalized multisplit pos: ");
+					for (i=0;i<multisplit_count;i++) DLOG("%zu ",multisplit_pos[i]);
+					DLOG("\n");
+				}
+				else
+					DLOG("all multisplit pos are outside of this packet\n");
+			}
+		}
 
-		enum dpi_desync_mode desync_mode = dp->desync_mode;
 		uint32_t fooling_orig = FOOL_NONE;
-		bool b;
+		bool bFake = false;
 		pkt1_len = sizeof(pkt1);
-		b = false;
-		switch(desync_mode)
+		switch(dp->desync_mode)
 		{
 			case DESYNC_FAKE_KNOWN:
-				if (reasm_offset)
-				{
-					desync_mode = dp->desync_mode2;
-					break;
-				}
+				if (reasm_offset) break;
 				if (l7proto==UNKNOWN)
 				{
 					DLOG("not applying fake because of unknown protocol\n");
-					desync_mode = dp->desync_mode2;
 					break;
 				}
 			case DESYNC_FAKE:
@@ -1176,14 +1244,14 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint
 				{
 					return verdict;
 				}
-				DLOG("sending fake request : ");
+				DLOG("sending fake : ");
 				hexdump_limited_dlog(fake,fake_size,PKTDATA_MAXDUMP); DLOG("\n");
-				b = true;
+				bFake = true;
 				break;
 			case DESYNC_RST:
 			case DESYNC_RSTACK:
 				if (reasm_offset) break;
-				if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, TH_RST | (desync_mode==DESYNC_RSTACK ? TH_ACK:0), dis->tcp->th_seq, dis->tcp->th_ack, dis->tcp->th_win, scale_factor, timestamps,
+				if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, TH_RST | (dp->desync_mode==DESYNC_RSTACK ? TH_ACK:0), dis->tcp->th_seq, dis->tcp->th_ack, dis->tcp->th_win, scale_factor, timestamps,
 					ttl_fake,IP4_TOS(dis->ip),IP6_FLOW(dis->ip6),
 					dp->desync_fooling_mode,dp->desync_badseq_increment,dp->desync_badseq_ack_increment,
 					NULL, 0, pkt1, &pkt1_len))
@@ -1191,15 +1259,15 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint
 					return verdict;
 				}
 				DLOG("sending fake RST/RSTACK\n");
-				b = true;
+				bFake = true;
 				break;
 			case DESYNC_HOPBYHOP:
 			case DESYNC_DESTOPT:
 			case DESYNC_IPFRAG1:
-				fooling_orig = (desync_mode==DESYNC_HOPBYHOP) ? FOOL_HOPBYHOP : (desync_mode==DESYNC_DESTOPT) ? FOOL_DESTOPT : FOOL_IPFRAG1;
-				desync_mode = dp->desync_mode2;
-				if (dis->ip6 && (desync_mode==DESYNC_NONE || !desync_valid_second_stage_tcp(desync_mode) ||
-					(!split_pos && (desync_mode==DESYNC_SPLIT || desync_mode==DESYNC_SPLIT2 || desync_mode==DESYNC_DISORDER || desync_mode==DESYNC_DISORDER2))))
+				fooling_orig = (dp->desync_mode==DESYNC_HOPBYHOP) ? FOOL_HOPBYHOP : (dp->desync_mode==DESYNC_DESTOPT) ? FOOL_DESTOPT : FOOL_IPFRAG1;
+				if (dis->ip6 && (dp->desync_mode2==DESYNC_NONE || !desync_valid_second_stage_tcp(dp->desync_mode2) ||
+					(!split_pos && (dp->desync_mode2==DESYNC_SPLIT || dp->desync_mode2==DESYNC_SPLIT2 || dp->desync_mode2==DESYNC_DISORDER || dp->desync_mode2==DESYNC_DISORDER2)) ||
+					(!multisplit_count && (dp->desync_mode2==DESYNC_MULTISPLIT || dp->desync_mode2==DESYNC_MULTIDISORDER))))
 				{
 					if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, flags_orig, dis->tcp->th_seq, dis->tcp->th_ack, dis->tcp->th_win, scale_factor, timestamps,
 						ttl_orig,IP4_TOS(dis->ip),IP6_FLOW(dis->ip6),
@@ -1219,24 +1287,65 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint
 				break;
 		}
 
-		if (b)
+		if (bFake)
 		{
 			if (!rawsend_rep(dp->desync_repeats,(struct sockaddr *)&dst, desync_fwmark, ifout , pkt1, pkt1_len))
 				return verdict;
-			if (dp->desync_mode2==DESYNC_NONE || !desync_valid_second_stage_tcp(dp->desync_mode2))
-			{
-				DLOG("reinjecting original packet. len=%zu len_payload=%zu\n", dis->len_pkt, dis->len_payload);
-				verdict_tcp_csum_fix(verdict, dis->tcp, dis->transport_len, dis->ip, dis->ip6);
-				if (!rawsend((struct sockaddr *)&dst, desync_fwmark, ifout , dis->data_pkt, dis->len_pkt))
-					return verdict;
-				return VERDICT_DROP;
-			}
-			desync_mode = dp->desync_mode2;
 		}
 
-		pkt1_len = sizeof(pkt1);
+		enum dpi_desync_mode desync_mode = dp->desync_mode2==DESYNC_NONE ? dp->desync_mode : dp->desync_mode2;
 		switch(desync_mode)
 		{
+			case DESYNC_MULTISPLIT:
+				if (multisplit_count)
+				{
+					size_t from,to;
+					for (i=0,from=0 ; i<=multisplit_count ; i++)
+					{
+						to = i==multisplit_count ? dis->len_payload : multisplit_pos[i];
+
+						pkt1_len = sizeof(pkt1);
+						if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, flags_orig,
+								net32_add(dis->tcp->th_seq,from), dis->tcp->th_ack,
+								dis->tcp->th_win, scale_factor, timestamps,ttl_orig,IP4_TOS(dis->ip),IP6_FLOW(dis->ip6),
+								fooling_orig,0,0,
+								dis->data_payload+from, to-from, pkt1, &pkt1_len))
+							return verdict;
+						DLOG("sending multisplit part %d %zu-%zu len=%zu : ",i+1,from,to-1,to-from);
+						hexdump_limited_dlog(dis->data_payload+from,to-from,PKTDATA_MAXDUMP); DLOG("\n");
+						if (!rawsend((struct sockaddr *)&dst, desync_fwmark, ifout , pkt1, pkt1_len))
+							return verdict;
+
+						from = to;
+					}
+					return VERDICT_DROP;
+				}
+				break;
+			case DESYNC_MULTIDISORDER:
+				if (multisplit_count)
+				{
+					size_t from,to;
+					for (i=multisplit_count-1,to=dis->len_payload ; i>=-1 ; i--)
+					{
+						from = i>=0 ? multisplit_pos[i] : 0;
+
+						pkt1_len = sizeof(pkt1);
+						if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, flags_orig,
+								net32_add(dis->tcp->th_seq,from), dis->tcp->th_ack,
+								dis->tcp->th_win, scale_factor, timestamps,ttl_orig,IP4_TOS(dis->ip),IP6_FLOW(dis->ip6),
+								fooling_orig,0,0,
+								dis->data_payload+from, to-from, pkt1, &pkt1_len))
+							return verdict;
+						DLOG("sending multisplit part %d %zu-%zu len=%zu : ",i+2,from,to-1,to-from);
+						hexdump_limited_dlog(dis->data_payload+from,to-from,PKTDATA_MAXDUMP); DLOG("\n");
+						if (!rawsend((struct sockaddr *)&dst, desync_fwmark, ifout , pkt1, pkt1_len))
+							return verdict;
+
+						to = from;
+					}
+					return VERDICT_DROP;
+				}
+				break;
 			case DESYNC_DISORDER:
 			case DESYNC_DISORDER2:
 				if (split_pos)
@@ -1270,6 +1379,7 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint
 							seg_len = dis->len_payload-split_pos;
 						}
 
+						pkt1_len = sizeof(pkt1);
 						if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, flags_orig, net32_add(dis->tcp->th_seq , split_pos - dp->desync_seqovl), dis->tcp->th_ack, dis->tcp->th_win, scale_factor, timestamps,
 								ttl_orig,IP4_TOS(dis->ip),IP6_FLOW(dis->ip6),
 								fooling_orig,dp->desync_badseq_increment,dp->desync_badseq_ack_increment,
@@ -1357,6 +1467,7 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint
 						seg_len = split_pos;
 					}
 
+					pkt1_len = sizeof(pkt1);
 					if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, flags_orig, net32_add(dis->tcp->th_seq,-dp->desync_seqovl), dis->tcp->th_ack, dis->tcp->th_win, scale_factor, timestamps,
 							ttl_orig,IP4_TOS(dis->ip),IP6_FLOW(dis->ip6),
 							fooling_orig,dp->desync_badseq_increment,dp->desync_badseq_ack_increment,
@@ -1436,7 +1547,15 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint
 			default:
 				break;
 		}
-	
+
+		if (bFake)
+		{
+			DLOG("reinjecting original packet. len=%zu len_payload=%zu\n", dis->len_pkt, dis->len_payload);
+			verdict_tcp_csum_fix(verdict, dis->tcp, dis->transport_len, dis->ip, dis->ip6);
+			if (!rawsend((struct sockaddr *)&dst, desync_fwmark, ifout , dis->data_pkt, dis->len_pkt))
+				return verdict;
+			return VERDICT_DROP;
+		}
 	}
 
 	return verdict;
@@ -1833,7 +1952,7 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, size_t reasm_offset, uint
 			case DESYNC_FAKE:
 				if (!prepare_udp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, ttl_fake, IP4_TOS(dis->ip),IP6_FLOW(dis->ip6), dp->desync_fooling_mode, NULL, 0, 0, fake, fake_size, pkt1, &pkt1_len))
 					return verdict;
-				DLOG("sending fake request : ");
+				DLOG("sending fake : ");
 				hexdump_limited_dlog(fake,fake_size,PKTDATA_MAXDUMP); DLOG("\n");
 				if (!rawsend_rep(dp->desync_repeats,(struct sockaddr *)&dst, desync_fwmark, ifout , pkt1, pkt1_len))
 					return verdict;
@@ -1996,7 +2115,7 @@ static void packet_debug(bool replay, const struct dissect *dis)
 			char s[80];
 			str_tcphdr(s,sizeof(s),dis->tcp);
 			DLOG(" %s\n",s);
-			if (dis->len_payload) { DLOG("TCP: "); hexdump_limited_dlog(dis->data_payload, dis->len_payload, 32); DLOG("\n"); }
+			if (dis->len_payload) { DLOG("TCP: len %zu : ",dis->len_payload); hexdump_limited_dlog(dis->data_payload, dis->len_payload, 32); DLOG("\n"); }
 
 		}
 		else if (dis->udp)
@@ -2004,7 +2123,7 @@ static void packet_debug(bool replay, const struct dissect *dis)
 			char s[30];
 			str_udphdr(s,sizeof(s),dis->udp);
 			DLOG(" %s\n",s);
-			if (dis->len_payload) { DLOG("UDP: "); hexdump_limited_dlog(dis->data_payload, dis->len_payload, 32); DLOG("\n"); }
+			if (dis->len_payload) { DLOG("UDP: len %zu : ",dis->len_payload); hexdump_limited_dlog(dis->data_payload, dis->len_payload, 32); DLOG("\n"); }
 		}
 		else
 			DLOG("\n");
diff --git a/nfq/desync.h b/nfq/desync.h
index 4aa42aa..4250f5c 100644
--- a/nfq/desync.h
+++ b/nfq/desync.h
@@ -32,6 +32,8 @@ enum dpi_desync_mode {
 	DESYNC_DISORDER2,
 	DESYNC_SPLIT,
 	DESYNC_SPLIT2,
+	DESYNC_MULTISPLIT,
+	DESYNC_MULTIDISORDER,
 	DESYNC_IPFRAG2,
 	DESYNC_HOPBYHOP,
 	DESYNC_DESTOPT,
diff --git a/nfq/helpers.c b/nfq/helpers.c
index 2d2d320..90c0452 100644
--- a/nfq/helpers.c
+++ b/nfq/helpers.c
@@ -11,6 +11,27 @@
 
 #include "params.h"
 
+int unique_size_t(size_t *pu, int ct)
+{
+	int i, j, u;
+	for (i = j = 0; j < ct; i++)
+	{
+		u = pu[j++];
+		for (; j < ct && pu[j] == u; j++);
+		pu[i] = u;
+	}
+	return i;
+}
+static int cmp_size_t(const void * a, const void * b)
+{
+	return *(size_t*)a < *(size_t*)b ? -1 : *(size_t*)a > *(size_t*)b;
+}
+void qsort_size_t(size_t *array,size_t ct)
+{
+	qsort(array,ct,sizeof(*array),cmp_size_t);
+}
+
+
 void rtrim(char *s)
 {
 	if (s)
diff --git a/nfq/helpers.h b/nfq/helpers.h
index c06862e..58051a7 100644
--- a/nfq/helpers.h
+++ b/nfq/helpers.h
@@ -17,6 +17,9 @@ typedef union
 	char _align[32];		// force 16-byte alignment for ip6_and int128 ops
 } sockaddr_in46;
 
+int unique_size_t(size_t *pu, int ct);
+void qsort_size_t(size_t *array,size_t ct);
+
 void rtrim(char *s);
 void replace_char(char *s, char from, char to);
 char *strncasestr(const char *s,const char *find, size_t slen);
diff --git a/nfq/nfqws.c b/nfq/nfqws.c
index e07054f..c9f18ed 100644
--- a/nfq/nfqws.c
+++ b/nfq/nfqws.c
@@ -673,10 +673,7 @@ static bool parse_l7_list(char *opt, uint32_t *l7)
 			*l7 |= L7_PROTO_UNKNOWN;
 		else return false;
 
-		if (e)
-		{
-			*e++=c;
-		}
+		if (e) *e++=c;
 		p = e;
 	}
 	return true;
@@ -723,14 +720,172 @@ static bool wf_make_l3(char *opt, bool *ipv4, bool *ipv6)
 			*ipv6 = true;
 		else return false;
 
-		if (e)
+		if (e) *e++=c;
+		p = e;
+	}
+	return true;
+}
+
+static bool parse_httpreqpos(const char *s, struct split_pos *sp)
+{
+	if (!strcmp(s, "method"))
+	{
+		sp->marker = PM_HTTP_METHOD;
+		sp->pos=2;
+	}
+	else if (!strcmp(s, "host"))
+	{
+		sp->marker = PM_HOST;
+		sp->pos=1;
+	}
+	else
+		return false;
+	return true;
+}
+static bool parse_tlspos(const char *s, struct split_pos *sp)
+{
+	if (!strcmp(s, "sni"))
+	{
+		sp->marker = PM_HOST;
+		sp->pos=1;
+	}
+	else if (!strcmp(s, "sniext"))
+	{
+		sp->marker = PM_SNI_EXT;
+		sp->pos=0;
+	}
+	else if (!strcmp(s, "snisld"))
+	{
+		sp->marker = PM_HOST_MIDSLD;
+		sp->pos=1;
+	}
+	else
+		return false;
+	return true;
+}
+
+static bool parse_int16(const char *p, int16_t *v)
+{
+	if (*p=='+' || *p=='-' || *p>='0' && *p<='9')
+	{
+		int i = atoi(p);
+		*v = (int16_t)i;
+		return *v==i; // check overflow
+	}
+	return false;
+}
+static bool parse_posmarker(const char *opt, uint8_t *posmarker)
+{
+	if (!strcmp(opt,"host"))
+		*posmarker = PM_HOST;
+	else if (!strcmp(opt,"endhost"))
+		*posmarker = PM_HOST_END;
+	else if (!strcmp(opt,"sld"))
+		*posmarker = PM_HOST_SLD;
+	else if (!strcmp(opt,"midsld"))
+		*posmarker = PM_HOST_MIDSLD;
+	else if (!strcmp(opt,"endsld"))
+		*posmarker = PM_HOST_ENDSLD;
+	else if (!strcmp(opt,"method"))
+		*posmarker = PM_HTTP_METHOD;
+	else if (!strcmp(opt,"sniext"))
+		*posmarker = PM_SNI_EXT;
+	else
+		return false;
+	return true;
+}
+static bool parse_split_pos(char *opt, struct split_pos *split)
+{
+	if (parse_int16(opt,&split->pos))
+	{
+		split->marker = PM_ABS;
+		return !!split->pos;
+	}
+	else
+	{
+		char c,*p=opt;
+		bool b;
+
+		for (; *opt && *opt!='+' && *opt!='-'; opt++);
+		c=*opt; *opt=0;
+		b=parse_posmarker(p,&split->marker);
+		*opt=c;
+		if (!b) return false;
+		if (*opt)
+			return parse_int16(opt,&split->pos);
+		else
+			split->pos = 0;
+	}
+	return true;
+}
+static bool parse_split_pos_list(char *opt, struct split_pos *splits, int splits_size, int *split_count)
+{
+	char c,*e,*p;
+
+	for (p=opt, *split_count=0 ; p && *split_count<splits_size ; (*split_count)++)
+	{
+		if ((e = strchr(p,',')))
 		{
-			*e++=c;
+			c=*e;
+			*e=0;
 		}
+		if (!parse_split_pos(p,splits+*split_count)) return false;
+		if (e) *e++=c;
 		p = e;
 	}
+	if (p) return false; // too much splits
 	return true;
 }
+static void split_compat(struct desync_profile *dp)
+{
+	// make it mostly compatible with old versions
+	int i;
+	dp->split_unknown.marker=PM_ABS;
+	dp->split_unknown.pos=2;
+	for (i=0;i<dp->split_count;i++)
+	{
+		if (dp->splits[i].marker==PM_ABS)
+		{
+			dp->split_unknown.pos=dp->splits[i].pos;
+			break;
+		}
+	}
+	if (SPLIT_POS_EMPTY(&dp->split_http))
+	{
+		dp->split_http=dp->split_unknown;
+		for (i=0;i<dp->split_count;i++)
+			if (IsHostMarker(dp->splits[i].marker) || dp->splits[i].marker==PM_HTTP_METHOD)
+			{
+				dp->split_http = dp->splits[i];
+				break;
+			}
+	}
+	if (SPLIT_POS_EMPTY(&dp->split_tls))
+	{
+		dp->split_tls=dp->split_unknown;
+		for (i=0;i<dp->split_count;i++)
+			if (IsHostMarker(dp->splits[i].marker) || dp->splits[i].marker==PM_SNI_EXT)
+			{
+				dp->split_tls = dp->splits[i];
+				break;
+			}
+	}
+}
+static void SplitDebug(void)
+{
+	struct desync_profile_list *dpl;
+	const struct desync_profile *dp;
+	LIST_FOREACH(dpl, &params.desync_profiles, next)
+	{
+		dp = &dpl->dp;
+		DLOG("profile %d split_http %s %d\n",dp->n,posmarker_name(dp->split_http.marker),dp->split_http.pos);
+		DLOG("profile %d split_tls %s %d\n",dp->n,posmarker_name(dp->split_tls.marker),dp->split_tls.pos);
+		DLOG("profile %d split_unknown %s %d\n",dp->n,posmarker_name(dp->split_unknown.marker),dp->split_unknown.pos);
+		for(int x=0;x<dp->split_count;x++)
+			DLOG("profile %d multisplit %s %d\n",dp->n,posmarker_name(dp->splits[x].marker),dp->splits[x].pos);
+	}
+}
+
 
 #ifdef __CYGWIN__
 static bool wf_make_pf(char *opt, const char *l4, const char *portname, char *buf, size_t len)
@@ -757,10 +912,7 @@ static bool wf_make_pf(char *opt, const char *l4, const char *portname, char *bu
 		if (n) strncat(buf," or ",len-strlen(buf)-1);
 		strncat(buf, s1, len-strlen(buf)-1);
 
-		if (e)
-		{
-			*e++=c;
-		}
+		if (e) *e++=c;
 		p = e;
 	}
 	strncat(buf, ")", len-strlen(buf)-1);
@@ -912,7 +1064,9 @@ static void exithelp(void)
 		" --hostspell\t\t\t\t\t; exact spelling of \"Host\" header. must be 4 chars. default is \"host\"\n"
 		" --hostnospace\t\t\t\t\t; remove space after Host: and add it to User-Agent: to preserve packet size\n"
 		" --domcase\t\t\t\t\t; mix domain case : Host: TeSt.cOm\n"
-		" --dpi-desync=[<mode0>,]<mode>[,<mode2>]\t; try to desync dpi state. modes : synack syndata fake fakeknown rst rstack hopbyhop destopt ipfrag1 disorder disorder2 split split2 ipfrag2 udplen tamper\n"
+		" --dpi-desync=[<mode0>,]<mode>[,<mode2>]\t; try to desync dpi state. modes :\n"
+		"\t\t\t\t\t\t; synack syndata fake fakeknown rst rstack hopbyhop destopt ipfrag1\n"
+		"\t\t\t\t\t\t; disorder disorder2 split split2 multisplit multidisorder ipfrag2 udplen tamper\n"
 #ifdef __linux__
 		" --dpi-desync-fwmark=<int|0xHEX>\t\t; override fwmark for desync packet. default = 0x%08X (%u)\n"
 #elif defined(SO_USER_COOKIE)
@@ -925,9 +1079,9 @@ static void exithelp(void)
 		" --dpi-desync-fooling=<mode>[,<mode>]\t\t; can use multiple comma separated values. modes : none md5sig ts badseq badsum datanoack hopbyhop hopbyhop2\n"
 		" --dpi-desync-repeats=<N>\t\t\t; send every desync packet N times\n"
 		" --dpi-desync-skip-nosni=0|1\t\t\t; 1(default)=do not act on ClientHello without SNI (ESNI ?)\n"
-		" --dpi-desync-split-pos=<1..%u>\t\t; data payload split position\n"
-		" --dpi-desync-split-http-req=method|host\t; split at specified logical part of plain http request\n"
-		" --dpi-desync-split-tls=sni|sniext|snisld\t; split at specified logical part of TLS ClientHello\n"
+		" --dpi-desync-split-pos=N|-N|marker+N|marker-N\t; comma separated list of split positions. markers: method,host,endhost,sld,endsld,midsld,sniext\n"
+		"\t\t\t\t\t\t; full list is only used by multisplit and multidisorder\n"
+		"\t\t\t\t\t\t; single split takes first l7-protocol-compatible parameter if present, first abs value otherwise\n"
 		" --dpi-desync-split-seqovl=<int>\t\t; use sequence overlap before first sent original split segment\n"
 		" --dpi-desync-split-seqovl-pattern=<filename>|0xHEX ; pattern for the fake part of overlap\n"
 		" --dpi-desync-ipfrag-pos-tcp=<8..%u>\t\t; ip frag position starting from the transport header. multiple of 8, default %u.\n"
@@ -953,7 +1107,6 @@ static void exithelp(void)
 		DPI_DESYNC_FWMARK_DEFAULT,DPI_DESYNC_FWMARK_DEFAULT,
 #endif
 		AUTOTTL_DEFAULT_DELTA,AUTOTTL_DEFAULT_MIN,AUTOTTL_DEFAULT_MAX,
-		DPI_DESYNC_MAX_FAKE_LEN,
 		DPI_DESYNC_MAX_FAKE_LEN, IPFRAG_UDP_DEFAULT,
 		DPI_DESYNC_MAX_FAKE_LEN, IPFRAG_TCP_DEFAULT,
 		BADSEQ_INCREMENT_DEFAULT, BADSEQ_ACK_INCREMENT_DEFAULT,
@@ -967,29 +1120,6 @@ static void exithelp_clean(void)
 	exithelp();
 }
 
-bool parse_httpreqpos(const char *s, enum httpreqpos *pos)
-{
-	if (!strcmp(s, "method"))
-		*pos = httpreqpos_method;
-	else if (!strcmp(s, "host"))
-		*pos = httpreqpos_host;
-	else
-		return false;
-	return true;
-}
-bool parse_tlspos(const char *s, enum tlspos *pos)
-{
-	if (!strcmp(s, "sni"))
-		*pos = tlspos_sni;
-	else if (!strcmp(s, "sniext"))
-		*pos = tlspos_sniext;
-	else if (!strcmp(s, "snisld"))
-		*pos = tlspos_snisld;
-	else
-		return false;
-	return true;
-}
-
 #ifndef __OpenBSD__
 // no static to not allow optimizer to inline this func (save stack)
 void config_from_file(const char *filename)
@@ -1447,21 +1577,27 @@ int main(int argc, char **argv)
 			dp->desync_skip_nosni = !optarg || atoi(optarg);
 			break;
 		case 23: /* dpi-desync-split-pos */
-			if (sscanf(optarg,"%u",&dp->desync_split_pos)<1 || dp->desync_split_pos<1)
 			{
-				DLOG_ERR("dpi-desync-split-pos is not valid\n");
-				exit_clean(1);
+				int ct;
+				if (!parse_split_pos_list(optarg,dp->splits+dp->split_count,MAX_SPLITS-dp->split_count,&ct))
+				{
+					DLOG_ERR("could not parse split pos list or too much positions (before parsing - %u, max - %u) : %s\n",dp->split_count,MAX_SPLITS,optarg);
+					exit_clean(1);
+				}
+				dp->split_count += ct;
 			}
 			break;
 		case 24: /* dpi-desync-split-http-req */
-			if (!parse_httpreqpos(optarg, &dp->desync_split_http_req))
+			// obsolete arg
+			if (!parse_httpreqpos(optarg, &dp->split_http))
 			{
 				DLOG_ERR("Invalid argument for dpi-desync-split-http-req\n");
 				exit_clean(1);
 			}
 			break;
 		case 25: /* dpi-desync-split-tls */
-			if (!parse_tlspos(optarg, &dp->desync_split_tls))
+			// obsolete arg
+			if (!parse_tlspos(optarg, &dp->split_tls))
 			{
 				DLOG_ERR("Invalid argument for dpi-desync-split-tls\n");
 				exit_clean(1);
@@ -1919,8 +2055,7 @@ int main(int argc, char **argv)
 			DLOG("[profile %d] autottl ipv4 %u:%u-%u\n",dp->n,dp->desync_autottl.delta,dp->desync_autottl.min,dp->desync_autottl.max);
 		if (AUTOTTL_ENABLED(dp->desync_autottl6))
 			DLOG("[profile %d] autottl ipv6 %u:%u-%u\n",dp->n,dp->desync_autottl6.delta,dp->desync_autottl6.min,dp->desync_autottl6.max);
-		if (dp->desync_split_tls==tlspos_none && dp->desync_split_pos) dp->desync_split_tls=tlspos_pos;
-		if (dp->desync_split_http_req==httpreqpos_none && dp->desync_split_pos) dp->desync_split_http_req=httpreqpos_pos;
+		split_compat(dp);
 	}
 
 	if (!LoadAllHostLists())
@@ -1937,6 +2072,9 @@ int main(int argc, char **argv)
 	DLOG("\nlists summary:\n");
 	HostlistsDebug();
 	IpsetsDebug();
+
+	DLOG("\nsplits summary:\n");
+	SplitDebug();
 	DLOG("\n");
 
 	if (daemon) daemonize();
diff --git a/nfq/params.c b/nfq/params.c
index 3cece72..8a7702c 100644
--- a/nfq/params.c
+++ b/nfq/params.c
@@ -169,7 +169,6 @@ struct desync_profile_list *dp_list_add(struct desync_profile_list_head *head)
 
 	memcpy(entry->dp.hostspell, "host", 4); // default hostspell
 	entry->dp.desync_skip_nosni = true;
-	entry->dp.desync_split_pos = 2;
 	entry->dp.desync_ipfrag_pos_udp = IPFRAG_UDP_DEFAULT;
 	entry->dp.desync_ipfrag_pos_tcp = IPFRAG_TCP_DEFAULT;
 	entry->dp.desync_repeats = 1;
diff --git a/nfq/params.h b/nfq/params.h
index 7af45c6..4fff9dc 100644
--- a/nfq/params.h
+++ b/nfq/params.h
@@ -40,6 +40,14 @@
 
 enum log_target { LOG_TARGET_CONSOLE=0, LOG_TARGET_FILE, LOG_TARGET_SYSLOG };
 
+struct split_pos
+{
+	int16_t pos;
+	uint8_t marker;
+};
+#define SPLIT_POS_EMPTY(sp) ((sp)->marker==PM_ABS && (sp)->pos==0)
+#define MAX_SPLITS	64
+
 struct desync_profile
 {
 	int n;	// number of the profile
@@ -53,9 +61,14 @@ struct desync_profile
 	char hostspell[4];
 	enum dpi_desync_mode desync_mode0,desync_mode,desync_mode2;
 	bool desync_retrans,desync_skip_nosni,desync_any_proto;
-	unsigned int desync_repeats,desync_split_pos,desync_seqovl,desync_ipfrag_pos_tcp,desync_ipfrag_pos_udp;
-	enum httpreqpos desync_split_http_req;
-	enum tlspos desync_split_tls;
+	unsigned int desync_repeats,desync_seqovl,desync_ipfrag_pos_tcp,desync_ipfrag_pos_udp;
+
+	// multisplit
+	struct split_pos splits[MAX_SPLITS];
+	int split_count;
+	// single split pos cache
+	struct split_pos split_http,split_tls,split_unknown;
+
 	char desync_start_mode, desync_cutoff_mode; // n - packets, d - data packets, s - relative sequence
 	unsigned int desync_start, desync_cutoff;
 	uint8_t desync_ttl, desync_ttl6;
diff --git a/nfq/protocol.c b/nfq/protocol.c
index bec8362..1283e2c 100644
--- a/nfq/protocol.c
+++ b/nfq/protocol.c
@@ -24,6 +24,88 @@ static bool FindNLD(const uint8_t *dom, size_t dlen, int level, const uint8_t **
 	return true;
 }
 
+
+#define PM_ABS		0
+#define PM_HOST		1
+#define PM_HOST_END	2
+#define PM_HOST_SLD	3
+#define PM_HOST_MIDSLD	4
+#define PM_HOST_ENDSLD	5
+#define PM_HTTP_METHOD	6
+#define PM_SNI_EXT	7
+bool IsHostMarker(uint8_t posmarker)
+{
+	switch(posmarker)
+	{
+		case PM_HOST:
+		case PM_HOST_END:
+		case PM_HOST_SLD:
+		case PM_HOST_MIDSLD:
+		case PM_HOST_ENDSLD:
+			return true;
+		default:
+			return false;
+	}
+}
+const char *posmarker_name(uint8_t posmarker)
+{
+	switch(posmarker)
+	{
+		case PM_ABS: return "abs";
+		case PM_HOST: return "host";
+		case PM_HOST_END: return "endhost";
+		case PM_HOST_SLD: return "sld";
+		case PM_HOST_MIDSLD: return "midsld";
+		case PM_HOST_ENDSLD: return "endsld";
+		case PM_HTTP_METHOD: return "method";
+		case PM_SNI_EXT: return "sniext";
+		default: return "?";
+	}
+}
+
+static size_t CheckPos(size_t sz, ssize_t offset)
+{
+	return (offset>=0 && offset<sz) ? offset : 0;
+}
+size_t AnyProtoPos(uint8_t posmarker, int16_t pos, const uint8_t *data, size_t sz)
+{
+	ssize_t offset;
+	switch(posmarker)
+	{
+		case PM_ABS:
+			offset = (pos<0) ? sz+pos : pos;
+			return CheckPos(sz,offset);
+		default:
+			return 0;
+	}
+}
+static size_t HostPos(uint8_t posmarker, int16_t pos, const uint8_t *data, size_t sz, size_t offset_host, size_t len_host)
+{
+	ssize_t offset;
+	const uint8_t *p;
+	size_t slen;
+
+	switch(posmarker)
+	{
+		case PM_HOST:
+			offset = offset_host+pos;
+			break;
+		case PM_HOST_END:
+			offset = offset_host+len_host+pos;
+			break;
+		case PM_HOST_SLD:
+		case PM_HOST_MIDSLD:
+		case PM_HOST_ENDSLD:
+			if (((offset_host+len_host)<=sz) && FindNLD(data+offset_host,len_host,2,&p,&slen))
+				offset = (posmarker==PM_HOST_SLD ? p-data : posmarker==PM_HOST_ENDSLD ? p-data+slen : slen==1 ? p+1-data : p+slen/2-data) + pos;
+			else
+				offset = 0;
+			break;
+	}
+	return CheckPos(sz,offset);
+}
+
+
 const char *http_methods[] = { "GET /","POST /","HEAD /","OPTIONS /","PUT /","DELETE /","CONNECT /","TRACE /",NULL };
 const char *HttpMethod(const uint8_t *data, size_t len)
 {
@@ -170,39 +252,49 @@ bool HttpReplyLooksLikeDPIRedirect(const uint8_t *data, size_t len, const char *
 	// compare 2nd level domains		
 	return strcasecmp(dhost, drhost)!=0;
 }
-size_t HttpPos(enum httpreqpos tpos_type, size_t hpos_pos, const uint8_t *http, size_t sz)
+size_t HttpPos(uint8_t posmarker, int16_t pos, const uint8_t *data, size_t sz)
 {
-	const uint8_t *method, *host=NULL;
+	const uint8_t *method, *host=NULL, *p;
+	size_t offset_host,len_host;
+	ssize_t offset;
 	int i;
 	
-	switch(tpos_type)
+	switch(posmarker)
 	{
-		case httpreqpos_method:
+		case PM_HTTP_METHOD:
 			// recognize some tpws pre-applied hacks
-			method=http;
+			method=data;
 			if (sz<10) break;
 			if (*method=='\n' || *method=='\r') method++;
 			if (*method=='\n' || *method=='\r') method++;
-			for (i=0;i<7;i++) if (*method>='A' && *method<='Z') method++;
-			if (i<3 || *method!=' ') break;
-			return method-http-1;
-		case httpreqpos_host:
-			if (HttpFindHostConst(&host,http,sz) && (host-http+7)<sz)
+			for (p=method,i=0;i<7;i++) if (*p>='A' && *p<='Z') p++;
+			if (i<3 || *p!=' ') break;
+			return CheckPos(sz,method-data+pos);
+		case PM_HOST:
+		case PM_HOST_END:
+		case PM_HOST_SLD:
+		case PM_HOST_MIDSLD:
+		case PM_HOST_ENDSLD:
+			if (HttpFindHostConst(&host,data,sz) && (host-data+7)<sz)
 			{
 				host+=5;
-				if (*host==' ') host++;
-				return host-http;
+				if (*host==' ' || *host=='\t') host++;
+				offset_host = host-data;
+				if (posmarker!=PM_HOST)
+					for (len_host=0; (offset_host+len_host)<sz && data[offset_host+len_host]!='\r' && data[offset_host+len_host]!='\n'; len_host++);
+				else
+					len_host = 0;
+				return HostPos(posmarker,pos,data,sz,offset_host,len_host);
 			}
 			break;
-		case httpreqpos_pos:
-			break;
 		default:
-			return 0;
+			return AnyProtoPos(posmarker,pos,data,sz);
 	}
-	return hpos_pos<sz ? hpos_pos : 0;
+	return 0;
 }
 
 
+
 uint16_t TLSRecordDataLen(const uint8_t *data)
 {
 	return pntoh16(data + 3);
@@ -355,45 +447,40 @@ bool TLSHelloExtractHostFromHandshake(const uint8_t *data, size_t len, char *hos
 	return TLSExtractHostFromExt(ext, elen, host, len_host);
 }
 
-// find N level domain in SNI
-static bool TLSHelloFindNLDInSNI(const uint8_t *ext, size_t elen, int level, const uint8_t **p, size_t *len)
-{
-	size_t slen;
-	return TLSAdvanceToHostInSNI(&ext,&elen,&slen) && FindNLD(ext,slen,level,p,len);
-}
-// find the middle of second level domain (SLD) in SNI ext : www.sobaka.ru => aka.ru
-// return false if SNI ext is bad or SLD is not found
-static bool TLSHelloFindMiddleOfSLDInSNI(const uint8_t *ext, size_t elen, const uint8_t **p)
-{
-	size_t len;
-	if (!TLSHelloFindNLDInSNI(ext,elen,2,p,&len))
-		return false;
-	// in case of one letter SLD (x.com) we split at '.' to prevent appearance of the whole SLD
-	*p = (len==1) ? *p+1 : *p+len/2;
-	return true;
-}
-size_t TLSPos(enum tlspos tpos_type, size_t tpos_pos, const uint8_t *tls, size_t sz, uint8_t type)
+size_t TLSPos(uint8_t posmarker, int16_t pos, const uint8_t *data, size_t sz)
 {
 	size_t elen;
 	const uint8_t *ext, *p;
-	switch(tpos_type)
+	size_t offset_host,len_host;
+	ssize_t offset;
+
+	switch(posmarker)
 	{
-		case tlspos_sni:
-		case tlspos_sniext:
-			if (TLSFindExt(tls,sz,0,&ext,&elen,false))
-				return (tpos_type==tlspos_sni) ? ext-tls+6 : ext-tls+1;
-			break;
-		case tlspos_snisld:
-			if (TLSFindExt(tls,sz,0,&ext,&elen,false))
-				if (TLSHelloFindMiddleOfSLDInSNI(ext,elen,&p))
-					return p-tls;
-			break;
-		case tlspos_pos:
-			break;
-		default:
+		case PM_HOST:
+		case PM_HOST_END:
+		case PM_HOST_SLD:
+		case PM_HOST_MIDSLD:
+		case PM_HOST_ENDSLD:
+		case PM_SNI_EXT:
+			if (TLSFindExt(data,sz,0,&ext,&elen,false))
+			{
+				if (posmarker==PM_SNI_EXT)
+				{
+					offset = ext-data+1+pos;
+					return (offset>=0 && offset<sz) ? offset : 0;
+				}
+				else
+				{
+					if (!TLSAdvanceToHostInSNI(&ext,&elen,&len_host))
+						return 0;
+					offset_host = ext-data;
+					return HostPos(posmarker,pos,data,sz,offset_host,len_host);
+				}
+			}
 			return 0;
+		default:
+			return AnyProtoPos(posmarker,pos,data,sz);
 	}
-	return tpos_pos<sz ? tpos_pos : 0;
 }
 
 
@@ -739,6 +826,7 @@ bool QUICDefragCrypto(const uint8_t *clean,size_t clean_len, uint8_t *defrag,siz
 	return found;
 }
 
+/*
 bool QUICExtractHostFromInitial(const uint8_t *data, size_t data_len, char *host, size_t len_host, bool *bDecryptOK, bool *bIsCryptoHello)
 {
 	if (bIsCryptoHello) *bIsCryptoHello=false;
@@ -758,8 +846,9 @@ bool QUICExtractHostFromInitial(const uint8_t *data, size_t data_len, char *host
 	if (!IsQUICCryptoHello(defrag, defrag_len, &hello_offset, &hello_len)) return false;
 	if (bIsCryptoHello) *bIsCryptoHello=true;
 
-	return TLSHelloExtractHostFromHandshake(defrag + hello_offset, hello_len, host, len_host, true);
+	return TLSHelloExtractHostFromHandshake(defrag + hello_offset, hello_len, host, len_host, NULL, true);
 }
+*/
 
 bool IsQUICInitial(const uint8_t *data, size_t len)
 {
diff --git a/nfq/protocol.h b/nfq/protocol.h
index 9b74f53..9ffa6a9 100644
--- a/nfq/protocol.h
+++ b/nfq/protocol.h
@@ -7,6 +7,21 @@
 #include "crypto/aes-gcm.h"
 #include "helpers.h"
 
+// pos markers
+#define PM_ABS		0
+#define PM_HOST		1
+#define PM_HOST_END	2
+#define PM_HOST_SLD	3
+#define PM_HOST_MIDSLD	4
+#define PM_HOST_ENDSLD	5
+#define PM_HTTP_METHOD	6
+#define PM_SNI_EXT	7
+bool IsHostMarker(uint8_t posmarker);
+const char *posmarker_name(uint8_t posmarker);
+size_t AnyProtoPos(uint8_t posmarker, int16_t pos, const uint8_t *data, size_t sz);
+size_t HttpPos(uint8_t posmarker, int16_t pos, const uint8_t *data, size_t sz);
+size_t TLSPos(uint8_t posmarker, int16_t pos, const uint8_t *data, size_t sz);
+
 extern const char *http_methods[9];
 const char *HttpMethod(const uint8_t *data, size_t len);
 bool IsHttp(const uint8_t *data, size_t len);
@@ -21,8 +36,6 @@ const char *HttpFind2ndLevelDomain(const char *host);
 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);
-enum httpreqpos { httpreqpos_none = 0, httpreqpos_method, httpreqpos_host, httpreqpos_pos };
-size_t HttpPos(enum httpreqpos tpos_type, size_t hpos_pos, const uint8_t *http, size_t sz);
 
 uint16_t TLSRecordDataLen(const uint8_t *data);
 size_t TLSRecordLen(const uint8_t *data);
@@ -35,8 +48,6 @@ bool TLSFindExt(const uint8_t *data, size_t len, uint16_t type, const uint8_t **
 bool TLSFindExtInHandshake(const uint8_t *data, size_t len, uint16_t type, const uint8_t **ext, size_t *len_ext, bool bPartialIsOK);
 bool TLSHelloExtractHost(const uint8_t *data, size_t len, char *host, size_t len_host, bool bPartialIsOK);
 bool TLSHelloExtractHostFromHandshake(const uint8_t *data, size_t len, char *host, size_t len_host, bool bPartialIsOK);
-enum tlspos { tlspos_none = 0, tlspos_sni, tlspos_sniext, tlspos_snisld, tlspos_pos };
-size_t TLSPos(enum tlspos tpos_type, size_t tpos_pos, const uint8_t *tls, size_t sz, uint8_t type);
 
 bool IsWireguardHandshakeInitiation(const uint8_t *data, size_t len);
 bool IsDhtD1(const uint8_t *data, size_t len);
@@ -56,4 +67,4 @@ bool QUICExtractDCID(const uint8_t *data, size_t len, quic_cid_t *cid);
 
 bool QUICDecryptInitial(const uint8_t *data, size_t data_len, uint8_t *clean, size_t *clean_len);
 bool QUICDefragCrypto(const uint8_t *clean,size_t clean_len, uint8_t *defrag,size_t *defrag_len);
-bool QUICExtractHostFromInitial(const uint8_t *data, size_t data_len, char *host, size_t len_host, bool *bDecryptOK, bool *bIsCryptoHello);
+//bool QUICExtractHostFromInitial(const uint8_t *data, size_t data_len, char *host, size_t len_host, bool *bDecryptOK, bool *bIsCryptoHello);