mirror of
https://github.com/bol-van/zapret2.git
synced 2026-03-14 06:13:09 +00:00
Compare commits
28 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
776155a326 | ||
|
|
30423596ca | ||
|
|
27ef67adf9 | ||
|
|
bb604f111c | ||
|
|
e5174bc9ad | ||
|
|
6c29bf6702 | ||
|
|
976033be37 | ||
|
|
f9b2135688 | ||
|
|
844fa6ab47 | ||
|
|
dc0fe70bd6 | ||
|
|
2752c26795 | ||
|
|
1600b41135 | ||
|
|
2017889207 | ||
|
|
146ab847df | ||
|
|
cf9059ed22 | ||
|
|
c94264c79e | ||
|
|
04cb71150a | ||
|
|
378ee514c4 | ||
|
|
1a190fcf9e | ||
|
|
0f8a788351 | ||
|
|
4c00f11c15 | ||
|
|
0f8cfd7022 | ||
|
|
4563b6ddcb | ||
|
|
9ae6927a0e | ||
|
|
8540278c9b | ||
|
|
76b9ab5075 | ||
|
|
3a153035e8 | ||
|
|
2b5eb3cd2d |
49
blockcheck2.d/standard/24-syndata.sh
Normal file
49
blockcheck2.d/standard/24-syndata.sh
Normal file
@@ -0,0 +1,49 @@
|
||||
. "$TESTDIR/def.inc"
|
||||
|
||||
pktws_check_http()
|
||||
{
|
||||
# $1 - test function
|
||||
# $2 - domain
|
||||
|
||||
local PAYLOAD="--payload http_req" split
|
||||
|
||||
for split in '' multisplit multidisorder; do
|
||||
pktws_curl_test_update "$1" "$2" --lua-desync=syndata ${split:+$PAYLOAD --lua-desync=$split}
|
||||
pktws_curl_test_update "$1" "$2" --lua-desync=syndata:blob=fake_default_http $PAYLOAD ${split:+$PAYLOAD --lua-desync=$split}
|
||||
done
|
||||
}
|
||||
|
||||
pktws_check_https_tls()
|
||||
{
|
||||
# $1 - test function
|
||||
# $2 - domain
|
||||
# $3 - PRE args for nfqws2
|
||||
|
||||
local PAYLOAD="--payload tls_client_hello" ok=0 pre="$3" split
|
||||
|
||||
for split in '' multisplit multidisorder; do
|
||||
pktws_curl_test_update "$1" "$2" $pre --lua-desync=syndata ${split:+$PAYLOAD --lua-desync=$split} && ok=1
|
||||
pktws_curl_test_update "$1" "$2" $pre --lua-desync=syndata:blob=0x1603 ${split:+$PAYLOAD --lua-desync=$split} && ok=1
|
||||
pktws_curl_test_update "$1" "$2" $pre --lua-desync=syndata:blob=fake_default_tls:tls_mod=rnd,dupsid,rndsni ${split:+$PAYLOAD --lua-desync=$split} && ok=1
|
||||
pktws_curl_test_update "$1" "$2" $pre --lua-desync=syndata:blob=fake_default_tls:tls_mod=rnd,dupsid,sni=google.com ${split:+$PAYLOAD --lua-desync=$split} && ok=1
|
||||
done
|
||||
|
||||
[ "$ok" = 1 ]
|
||||
}
|
||||
|
||||
pktws_check_https_tls12()
|
||||
{
|
||||
# $1 - test function
|
||||
# $2 - domain
|
||||
|
||||
pktws_check_https_tls "$1" "$2" && [ "$SCANLEVEL" != force ] && return
|
||||
pktws_check_https_tls "$1" "$2" --lua-desync=wssize:wsize=1:scale=6
|
||||
}
|
||||
|
||||
pktws_check_https_tls13()
|
||||
{
|
||||
# $1 - test function
|
||||
# $2 - domain
|
||||
|
||||
pktws_check_https_tls "$1" "$2"
|
||||
}
|
||||
@@ -2,6 +2,6 @@ FOOLINGS46_TCP=${FOOLINGS46_TCP:-"tcp_md5 badsum tcp_seq=-3000 tcp_seq=1000000 t
|
||||
FOOLINGS6_TCP=${FOOLINGS6_TCP:-"ip6_hopbyhop ip6_hopbyhop:ip6_hopbyhop2 ip6_destopt ip6_routing ip6_ah"}
|
||||
FOOLINGS_TCP="$FOOLINGS46_TCP"
|
||||
[ "$IPV" = 6 ] && FOOLINGS_TCP="$FOOLINGS_TCP $FOOLINGS6_TCP"
|
||||
FOOLINGS_UDP="badsum"
|
||||
FOOLINGS_UDP="${FOOLINGS_UDP:-badsum}"
|
||||
|
||||
FAKE_REPEATS=${FAKE_REPEATS:-1}
|
||||
|
||||
@@ -1199,8 +1199,8 @@ test_runner()
|
||||
[ -f "$script" ] || continue
|
||||
unset -f $FUNC
|
||||
. "$script"
|
||||
echo
|
||||
existf $FUNC && {
|
||||
echo
|
||||
echo "* script : $TEST/$(basename "$script")"
|
||||
$FUNC "$@"
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ filter_apply_hostlist_target()
|
||||
{
|
||||
# $1 - var name of nfqws params
|
||||
|
||||
local v parm parm1 parm2 parm3 parm4 parm5 parm6 parm7 parm8 parmNA
|
||||
local v parm parm1 parm2 parm3 parm4 parm5 parm6 parm7 parm8 parm9 parmNA
|
||||
eval v="\$$1"
|
||||
if contains "$v" "$HOSTLIST_MARKER" || contains "$v" "$HOSTLIST_NOAUTO_MARKER"; then
|
||||
[ "$MODE_FILTER" = hostlist -o "$MODE_FILTER" = autohostlist ] &&
|
||||
@@ -40,10 +40,11 @@ filter_apply_hostlist_target()
|
||||
parm5="${AUTOHOSTLIST_FAIL_THRESHOLD:+--hostlist-auto-fail-threshold=$AUTOHOSTLIST_FAIL_THRESHOLD}"
|
||||
parm6="${AUTOHOSTLIST_FAIL_TIME:+--hostlist-auto-fail-time=$AUTOHOSTLIST_FAIL_TIME}"
|
||||
parm7="${AUTOHOSTLIST_RETRANS_THRESHOLD:+--hostlist-auto-retrans-threshold=$AUTOHOSTLIST_RETRANS_THRESHOLD}"
|
||||
parm8="--hostlist=$HOSTLIST_AUTO"
|
||||
parm8="${AUTOHOSTLIST_RETRANS_MAXSEQ:+--hostlist-auto-retrans-maxseq=$AUTOHOSTLIST_RETRANS_MAXSEQ}"
|
||||
parm9="--hostlist=$HOSTLIST_AUTO"
|
||||
}
|
||||
parm="$parm1${parm2:+ $parm2}${parm3:+ $parm3}${parm4:+ $parm4}${parm5:+ $parm5}${parm6:+ $parm6}${parm7:+ $parm7}"
|
||||
parmNA="$parm1${parm2:+ $parm2}${parm3:+ $parm3}${parm8:+ $parm8}"
|
||||
parm="$parm1${parm2:+ $parm2}${parm3:+ $parm3}${parm4:+ $parm4}${parm5:+ $parm5}${parm6:+ $parm6}${parm7:+ $parm7}${parm8:+ $parm8}"
|
||||
parmNA="$parm1${parm2:+ $parm2}${parm3:+ $parm3}${parm9:+ $parm9}"
|
||||
}
|
||||
v="$(replace_str $HOSTLIST_NOAUTO_MARKER "$parmNA" "$v")"
|
||||
v="$(replace_str $HOSTLIST_MARKER "$parm" "$v")"
|
||||
|
||||
@@ -26,6 +26,7 @@ IPSET_OPT="hashsize 262144 maxelem $SET_MAXELEM"
|
||||
IP2NET_OPT4="--prefix-length=22-30 --v4-threshold=3/4"
|
||||
IP2NET_OPT6="--prefix-length=56-64 --v6-threshold=5"
|
||||
# options for auto hostlist
|
||||
AUTOHOSTLIST_RETRANS_MAXSEQ=65536
|
||||
AUTOHOSTLIST_RETRANS_THRESHOLD=3
|
||||
AUTOHOSTLIST_FAIL_THRESHOLD=3
|
||||
AUTOHOSTLIST_FAIL_TIME=60
|
||||
|
||||
@@ -66,3 +66,17 @@ v0.5.1
|
||||
|
||||
* zapret-auto: separate failure detection logic
|
||||
* blockcheck2: fix broken http3 test
|
||||
|
||||
v0.6
|
||||
|
||||
* zapret-lib,zapret-antidpi: tls_mod_shim supports sni=%var subst
|
||||
* blockcheck2: syndata tests
|
||||
* nfqws2: reasm support negative overlaps. gaps are not supported.
|
||||
* nfqws2,zapret-auto: changed retransmission detection scheme.
|
||||
* zapret-auto: udp_in/udp_out failure detection
|
||||
|
||||
v0.6.1
|
||||
|
||||
* zapret-lib, zapret-auto: condition and stopif orchestrators
|
||||
* zapret-lib: detect_payload_str - sample lua payload detector
|
||||
* blockcheck2: unterminated string fix
|
||||
|
||||
@@ -290,7 +290,7 @@ end
|
||||
-- nfqws1 : "--dpi-desync=syndata"
|
||||
-- standard args : fooling, rawsend, reconstruct, ipfrag
|
||||
-- arg : blob=<blob> - fake payload. must fit to single packet. no segmentation possible. default - 16 zero bytes.
|
||||
-- arg : tls_mod=<list> - comma separated list of tls mods : rnd,rndsni,sni=<str>
|
||||
-- arg : tls_mod=<list> - comma separated list of tls mods : rnd,rndsni,sni=<str>. sni=%var is supported
|
||||
function syndata(ctx, desync)
|
||||
if desync.dis.tcp then
|
||||
if bitand(desync.dis.tcp.th_flags, TH_SYN + TH_ACK)==TH_SYN then
|
||||
@@ -298,7 +298,7 @@ function syndata(ctx, desync)
|
||||
dis.payload = blob(desync, desync.arg.blob, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
|
||||
apply_fooling(desync, dis)
|
||||
if desync.arg.tls_mod then
|
||||
dis.payload = tls_mod(dis.payload, desync.arg.tls_mod, nil)
|
||||
dis.payload = tls_mod_shim(desync, dis.payload, desync.arg.tls_mod, nil)
|
||||
end
|
||||
if b_debug then DLOG("syndata: "..hexdump_dlog(dis.payload)) end
|
||||
if rawsend_dissect_ipfrag(dis, desync_opts(desync)) then
|
||||
@@ -340,7 +340,7 @@ end
|
||||
-- nfqws1 : "--dpi-desync=fake"
|
||||
-- standard args : direction, payload, fooling, ip_id, rawsend, reconstruct, ipfrag
|
||||
-- arg : blob=<blob> - fake payload
|
||||
-- arg : tls_mod=<list> - comma separated list of tls mods : rnd,rndsni,sni=<str>,dupsid,padencap
|
||||
-- arg : tls_mod=<list> - comma separated list of tls mods : rnd,rndsni,sni=<str>,dupsid,padencap . sni=%var is supported
|
||||
function fake(ctx, desync)
|
||||
direction_cutoff_opposite(ctx, desync)
|
||||
-- by default process only outgoing known payloads
|
||||
@@ -351,7 +351,7 @@ function fake(ctx, desync)
|
||||
end
|
||||
local fake_payload = blob(desync, desync.arg.blob)
|
||||
if desync.reasm_data and desync.arg.tls_mod then
|
||||
fake_payload = tls_mod(fake_payload, desync.arg.tls_mod, desync.reasm_data)
|
||||
fake_payload = tls_mod_shim(desync, fake_payload, desync.arg.tls_mod, desync.reasm_data)
|
||||
end
|
||||
-- check debug to save CPU
|
||||
if b_debug then DLOG("fake: "..hexdump_dlog(fake_payload)) end
|
||||
@@ -421,7 +421,7 @@ end
|
||||
-- arg : pos=<postmarker list> . position marker list. example : "1,host,midsld+1,-10"
|
||||
-- arg : seqovl=N . decrease seq number of the second segment in the original order by N and fill N bytes with pattern (default - all zero). N must be less than the first split pos.
|
||||
-- arg : seqovl_pattern=<blob> . override pattern
|
||||
-- arg : blob=<blob> - use this data instead of desync.dis.payload
|
||||
-- arg : blob=<blob> - use this data instead of reasm_data
|
||||
-- arg : nodrop - do not drop current dissect
|
||||
function multidisorder(ctx, desync)
|
||||
if not desync.dis.tcp then
|
||||
@@ -600,7 +600,7 @@ end
|
||||
-- arg : pattern=<blob> . fill fake parts with this pattern
|
||||
-- arg : seqovl=N . decrease seq number of the first segment by N and fill N bytes with pattern (default - all zero)
|
||||
-- arg : seqovl_pattern=<blob> . override seqovl pattern
|
||||
-- arg : blob=<blob> - use this data instead of desync.dis.payload
|
||||
-- arg : blob=<blob> - use this data instead of reasm_data
|
||||
-- arg : nodrop - do not drop current dissect
|
||||
function fakedsplit(ctx, desync)
|
||||
if not desync.dis.tcp then
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
-- standard automation/orchestration code
|
||||
-- this is related to making dynamic strategy decisions without rewriting or altering strategy function code
|
||||
-- orchestrators can decide which instances to call or not to call or pass them dynamic arguments
|
||||
-- failure detectors test potential block conditions for orchestrators
|
||||
|
||||
-- arg: reqhost - require hostname, do not work with ip
|
||||
function automate_host_record(desync)
|
||||
local key
|
||||
@@ -70,54 +75,77 @@ function is_dpi_redirect(hostname, location)
|
||||
end
|
||||
|
||||
-- standard failure detector
|
||||
-- works with tcp only
|
||||
-- works with tcp and udp
|
||||
-- detected failures:
|
||||
-- incoming RST (within `options.seq_rst`)
|
||||
-- incoming http redirection (if no 'options.no_http_redirect')
|
||||
-- outgoing retransmissions (`options.retrans` threshold)
|
||||
-- stops dectecting after seq 'options.maxseq'
|
||||
function standard_failure_detector(desync, crec, options)
|
||||
if not desync.dis.tcp or crec.nocheck then return false end
|
||||
local seq = pos_get(desync,'s')
|
||||
if options.maxseq and seq>options.maxseq then
|
||||
DLOG("standard_failure_detector: s"..seq.." is beyond s"..options.maxseq..". treating connection as successful")
|
||||
crec.nocheck = true
|
||||
return false
|
||||
end
|
||||
-- incoming RST
|
||||
-- incoming http redirection
|
||||
-- outgoing retransmissions
|
||||
-- udp too much out with too few in
|
||||
-- arg: seq=<rseq> - tcp: if packet is beyond this relative sequence number treat this connection as successful. default is 64K
|
||||
-- arg: retrans=N - tcp: retrans count threshold. default is 3
|
||||
-- arg: rst=<rseq> - tcp: maximum relative sequence number to treat incoming RST as DPI reset. default is 1
|
||||
-- arg: no_http_redirect - tcp: disable http_reply dpi redirect trigger
|
||||
-- arg: udp_out - udp: >= outgoing udp packets. default is 3
|
||||
-- arg: udp_in - udp: with <= incoming udp packets. default is 1
|
||||
function standard_failure_detector(desync, crec, arg)
|
||||
if crec.nocheck then return false end
|
||||
|
||||
local seq_rst = tonumber(arg.rst) or 1
|
||||
local retrans = tonumber(arg.retrans) or 3
|
||||
local maxseq = tonumber(arg.seq) or 0x10000
|
||||
local udp_in = tonumber(arg.udp_in) or 1
|
||||
local udp_out = tonumber(arg.udp_out) or 3
|
||||
|
||||
local trigger = false
|
||||
local seq = pos_get(desync,'s')
|
||||
if desync.outgoing then
|
||||
if #desync.dis.payload>0 and options.retrans and (crec.retrans or 0)<options.retrans then
|
||||
if not crec.uppos then crec.uppos=0 end
|
||||
if desync.track.tcp.pos_orig<=crec.uppos then
|
||||
crec.retrans = crec.retrans and (crec.retrans+1) or 1
|
||||
DLOG("standard_failure_detector: retransmission "..crec.retrans.."/"..options.retrans)
|
||||
trigger = crec.retrans>=options.retrans
|
||||
end
|
||||
if desync.track.tcp.pos_orig>crec.uppos then
|
||||
crec.uppos=desync.track.tcp.pos_orig
|
||||
end
|
||||
if desync.dis.tcp then
|
||||
local seq = pos_get(desync,'s')
|
||||
if maxseq and seq>maxseq then
|
||||
DLOG("standard_failure_detector: s"..seq.." is beyond s"..maxseq..". treating connection as successful")
|
||||
crec.nocheck = true
|
||||
return false
|
||||
end
|
||||
else
|
||||
if options.seq_rst and bitand(desync.dis.tcp.th_flags, TH_RST)~=0 then
|
||||
trigger = seq<=options.seq_rst
|
||||
if b_debug then
|
||||
if trigger then
|
||||
DLOG("standard_failure_detector: incoming RST s"..seq.." in range s"..options.seq_rst)
|
||||
else
|
||||
DLOG("standard_failure_detector: not counting incoming RST s"..seq.." beyond s"..options.seq_rst)
|
||||
|
||||
if desync.outgoing then
|
||||
if #desync.dis.payload>0 and retrans and (crec.retrans or 0)<retrans then
|
||||
if is_retransmission(desync) then
|
||||
crec.retrans = crec.retrans and (crec.retrans+1) or 1
|
||||
DLOG("standard_failure_detector: retransmission "..crec.retrans.."/"..retrans)
|
||||
trigger = crec.retrans>=retrans
|
||||
end
|
||||
end
|
||||
elseif not options.no_http_redirect and desync.l7payload=="http_reply" and desync.track.hostname then
|
||||
local hdis = http_dissect_reply(desync.dis.payload)
|
||||
if hdis and (hdis.code==302 or hdis.code==307) and hdis.headers.location and hdis.headers.location then
|
||||
trigger = is_dpi_redirect(desync.track.hostname, hdis.headers.location.value)
|
||||
else
|
||||
if seq_rst and bitand(desync.dis.tcp.th_flags, TH_RST)~=0 then
|
||||
trigger = seq<=seq_rst
|
||||
if b_debug then
|
||||
if trigger then
|
||||
DLOG("standard_failure_detector: http redirect "..hdis.code.." to '"..hdis.headers.location.value.."'. looks like DPI redirect.")
|
||||
DLOG("standard_failure_detector: incoming RST s"..seq.." in range s"..seq_rst)
|
||||
else
|
||||
DLOG("standard_failure_detector: http redirect "..hdis.code.." to '"..hdis.headers.location.value.."'. NOT a DPI redirect.")
|
||||
DLOG("standard_failure_detector: not counting incoming RST s"..seq.." beyond s"..seq_rst)
|
||||
end
|
||||
end
|
||||
elseif not arg.no_http_redirect and desync.l7payload=="http_reply" and desync.track.hostname then
|
||||
local hdis = http_dissect_reply(desync.dis.payload)
|
||||
if hdis and (hdis.code==302 or hdis.code==307) and hdis.headers.location and hdis.headers.location then
|
||||
trigger = is_dpi_redirect(desync.track.hostname, hdis.headers.location.value)
|
||||
if b_debug then
|
||||
if trigger then
|
||||
DLOG("standard_failure_detector: http redirect "..hdis.code.." to '"..hdis.headers.location.value.."'. looks like DPI redirect.")
|
||||
else
|
||||
DLOG("standard_failure_detector: http redirect "..hdis.code.." to '"..hdis.headers.location.value.."'. NOT a DPI redirect.")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
elseif desync.dis.udp then
|
||||
if desync.outgoing then
|
||||
if udp_out then
|
||||
local udp_in = udp_in or 0
|
||||
trigger = desync.track.pcounter_orig>=udp_out and desync.track.pcounter_reply<=udp_in
|
||||
if trigger then
|
||||
crec.nocheck = true
|
||||
if b_debug then
|
||||
DLOG("standard_failure_detector: udp_out "..desync.track.pcounter_orig..">="..udp_out.." udp_in "..desync.track.pcounter_reply.."<="..udp_in)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -132,19 +160,17 @@ end
|
||||
-- each orchestrated instance must have strategy=N arg, where N starts from 1 and increment without gaps
|
||||
-- if 'final' arg is present in an orchestrated instance it stops rotation
|
||||
-- arg: fails=N - failture count threshold. default is 3
|
||||
-- arg: retrans=N - retrans count threshold. default is 3
|
||||
-- arg: seq=<rseq> - if packet is beyond this relative sequence number treat this connection as successful. default is 64K
|
||||
-- arg: rst=<rseq> - maximum relative sequence number to treat incoming RST as DPI reset. default is 1
|
||||
-- arg: time=<sec> - if last failure happened earlier than `maxtime` seconds ago - reset failure counter. default is 60.
|
||||
-- arg: reqhost - pass with no tampering if hostname is unavailable
|
||||
-- arg: detector - failure detector function name
|
||||
-- arg: detector - failure detector function name.
|
||||
-- args for failure detector - see standard_failure_detector or your own detector
|
||||
-- test case: nfqws2 --qnum 200 --debug --lua-init=@zapret-lib.lua --lua-init=@zapret-auto.lua --in-range=-s1 --lua-desync=circular --lua-desync=argdebug:strategy=1 --lua-desync=argdebug:strategy=2
|
||||
function circular(ctx, desync)
|
||||
local function count_strategies(hrec, plan)
|
||||
local function count_strategies(hrec)
|
||||
if not hrec.ctstrategy then
|
||||
local uniq={}
|
||||
local n=0
|
||||
for i,instance in pairs(plan) do
|
||||
for i,instance in pairs(desync.plan) do
|
||||
if instance.arg.strategy then
|
||||
n = tonumber(instance.arg.strategy)
|
||||
if not n or n<1 then
|
||||
@@ -167,52 +193,24 @@ function circular(ctx, desync)
|
||||
end
|
||||
end
|
||||
|
||||
-- take over orchestration. prevent further instance execution in case of error
|
||||
execution_plan_cancel(ctx)
|
||||
-- take over execution. prevent further instance execution in case of error
|
||||
orchestrate(ctx, desync)
|
||||
|
||||
if not desync.dis.tcp then
|
||||
DLOG_ERR("circular: this orchestrator is tcp only. use filters to avoid udp traffic.")
|
||||
instance_cutoff(ctx)
|
||||
return
|
||||
end
|
||||
if not desync.track then
|
||||
DLOG_ERR("circular: conntrack is missing but required")
|
||||
return
|
||||
end
|
||||
|
||||
local plan = execution_plan(ctx)
|
||||
if #plan==0 then
|
||||
DLOG("circular: need some desync instances or useless")
|
||||
return
|
||||
end
|
||||
|
||||
local hrec = automate_host_record(desync)
|
||||
if not hrec then
|
||||
DLOG("circular: passing with no tampering")
|
||||
return
|
||||
end
|
||||
|
||||
count_strategies(hrec, plan)
|
||||
count_strategies(hrec)
|
||||
if hrec.ctstrategy==0 then
|
||||
error("circular: add strategy=N tag argument to each following instance ! N must start from 1 and increment")
|
||||
end
|
||||
|
||||
local seq_rst = tonumber(desync.arg.rst) or 1
|
||||
local maxseq = tonumber(desync.arg.seq) or 0x10000
|
||||
local retrans = tonumber(desync.arg.retrans) or 3
|
||||
local fails = tonumber(desync.arg.fails) or 3
|
||||
local maxtime = tonumber(desync.arg.time) or 60
|
||||
local crec = automate_conn_record(desync)
|
||||
local failure_detector
|
||||
if desync.arg.detector then
|
||||
if type(_G[desync.arg.detector])~="function" then
|
||||
error("circular: invalid failure detector function '"..desync.arg.detector.."'")
|
||||
end
|
||||
failure_detector = _G[desync.arg.detector]
|
||||
else
|
||||
failure_detector = standard_failure_detector
|
||||
end
|
||||
|
||||
if not hrec.nstrategy then
|
||||
DLOG("circular: start from strategy 1")
|
||||
hrec.nstrategy = 1
|
||||
@@ -220,9 +218,22 @@ function circular(ctx, desync)
|
||||
|
||||
local verdict = VERDICT_PASS
|
||||
if hrec.final~=hrec.nstrategy then
|
||||
if failure_detector(desync,crec,{retrans=retrans,maxseq=maxseq,seq_rst=seq_rst}) then
|
||||
local crec = automate_conn_record(desync)
|
||||
local fails = tonumber(desync.arg.fails) or 3
|
||||
local maxtime = tonumber(desync.arg.time) or 60
|
||||
local failure_detector
|
||||
if desync.arg.detector then
|
||||
if type(_G[desync.arg.detector])~="function" then
|
||||
error("circular: invalid failure detector function '"..desync.arg.detector.."'")
|
||||
end
|
||||
failure_detector = _G[desync.arg.detector]
|
||||
else
|
||||
failure_detector = standard_failure_detector
|
||||
end
|
||||
if failure_detector(desync,crec,desync.arg) then
|
||||
-- failure happened. count failures.
|
||||
if automate_failure_counter(hrec, crec, fails, maxtime) then
|
||||
-- circular strategy change
|
||||
-- counter reaches threshold. circular strategy change
|
||||
hrec.nstrategy = (hrec.nstrategy % hrec.ctstrategy) + 1
|
||||
DLOG("circular: rotate strategy to "..hrec.nstrategy)
|
||||
if hrec.nstrategy == hrec.final then
|
||||
@@ -234,11 +245,76 @@ function circular(ctx, desync)
|
||||
|
||||
DLOG("circular: current strategy "..hrec.nstrategy)
|
||||
local dcopy = desync_copy(desync)
|
||||
for i=1,#plan do
|
||||
if plan[i].arg.strategy and tonumber(plan[i].arg.strategy)==hrec.nstrategy then
|
||||
verdict = plan_instance_execute(dcopy, verdict, plan[i])
|
||||
while true do
|
||||
local instance = plan_instance_pop(desync)
|
||||
if not instance then break end
|
||||
if instance.arg.strategy and tonumber(instance.arg.strategy)==hrec.nstrategy then
|
||||
verdict = plan_instance_execute(dcopy, verdict, instance)
|
||||
end
|
||||
end
|
||||
|
||||
return verdict
|
||||
end
|
||||
|
||||
-- test iff functions
|
||||
function cond_true(desync)
|
||||
return true
|
||||
end
|
||||
function cond_false(desync)
|
||||
return false
|
||||
end
|
||||
-- arg: percent - of true . 50 by default
|
||||
function cond_random(desync)
|
||||
return math.random(0,99)<(tonumber(desync.arg.percent) or 50)
|
||||
end
|
||||
-- this iif function detects packets having 'arg.pattern' string in their payload
|
||||
-- test case : nfqws2 --qnum 200 --debug --lua-init=@zapret-lib.lua --lua-init=@zapret-auto.lua --lua-desync=condition:iff=cond_payload_str:pattern=1234 --lua-desync=argdebug:testarg=1 --lua-desync=argdebug:testarg=2:morearg=xyz
|
||||
-- test case (true) : echo aaz1234zzz | ncat -4u 1.1.1.1 443
|
||||
-- test case (false) : echo aaze124zzz | ncat -4u 1.1.1.1 443
|
||||
function cond_payload_str(desync)
|
||||
if not desync.arg.pattern then
|
||||
error("cond_payload_str: missing 'pattern'")
|
||||
end
|
||||
return string.find(desync.dis.payload,desync.arg.pattern,1,true)
|
||||
end
|
||||
-- check iff function available. error if not
|
||||
function require_iff(desync, name)
|
||||
if not desync.arg.iff then
|
||||
error(name..": missing 'iff' function")
|
||||
end
|
||||
if type(_G[desync.arg.iff])~="function" then
|
||||
error(name..": invalid 'iff' function '"..desync.arg.iff.."'")
|
||||
end
|
||||
end
|
||||
-- execute further desync instances only if user-provided 'iff' function returns true
|
||||
-- for example, this can be used by custom protocol detectors
|
||||
-- arg: iff - condition function. takes desync as arg and returns bool. (cant use 'if' because of reserved word)
|
||||
-- arg: neg - invert condition function result
|
||||
-- test case : nfqws2 --qnum 200 --debug --lua-init=@zapret-lib.lua --lua-init=@zapret-auto.lua --lua-desync=condition:iff=cond_random --lua-desync=argdebug:testarg=1 --lua-desync=argdebug:testarg=2:morearg=xyz
|
||||
function condition(ctx, desync)
|
||||
require_iff(desync, "condition")
|
||||
orchestrate(ctx, desync)
|
||||
if logical_xor(_G[desync.arg.iff](desync), desync.arg.neg) then
|
||||
DLOG("condition: true")
|
||||
return replay_execution_plan(desync)
|
||||
else
|
||||
DLOG("condition: false")
|
||||
plan_clear(desync)
|
||||
end
|
||||
end
|
||||
-- clear execution plan if user provided 'iff' functions returns true
|
||||
-- can be used with other orchestrators to stop execution conditionally
|
||||
-- arg: iff - condition function. takes desync as arg and returns bool. (cant use 'if' because of reserved word)
|
||||
-- arg: neg - invert condition function result
|
||||
-- test case : nfqws2 --qnum 200 --debug --lua-init=@zapret-lib.lua --lua-init=@zapret-auto.lua --in-range=-s1 --lua-desync=circular --lua-desync=stopif:iff=cond_random:strategy=1 --lua-desync=argdebug:strategy=1 --lua-desync=argdebug:strategy=2
|
||||
function stopif(ctx, desync)
|
||||
require_iff(desync, "stopif")
|
||||
orchestrate(ctx, desync)
|
||||
if logical_xor(_G[desync.arg.iff](desync), desync.arg.neg) then
|
||||
DLOG("stopif: true")
|
||||
plan_clear(desync)
|
||||
else
|
||||
-- do not do anything. allow other orchestrator to finish the plan
|
||||
DLOG("stopif: false")
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
HEXDUMP_DLOG_MAX = HEXDUMP_DLOG_MAX or 32
|
||||
NOT3=bitnot(3)
|
||||
NOT7=bitnot(7)
|
||||
math.randomseed(os.time())
|
||||
|
||||
-- xor pid,tid,sec,nsec
|
||||
math.randomseed(bitxor(getpid(),gettid(),clock_gettime()))
|
||||
|
||||
-- basic desync function
|
||||
-- execute given lua code. "desync" is temporary set as global var to be accessible to the code
|
||||
@@ -61,6 +61,30 @@ function posdebug(ctx,desync)
|
||||
DLOG(s)
|
||||
end
|
||||
|
||||
-- basic desync function
|
||||
-- set l7payload to 'arg.payload' if reasm.data or desync.dis.payload contains 'arg.pattern' substring
|
||||
-- NOTE : this does not set payload on C code side !
|
||||
-- NOTE : C code will not see payload change. --payload args take only payloads known to C code and cause error if unknown.
|
||||
-- arg: pattern - substring for search inside reasm_data or desync.dis.payload
|
||||
-- arg: payload - set desync.l7payload to this if detected
|
||||
-- arg: undetected - set desync.l7payload to this if not detected
|
||||
-- test case : nfqws2 --qnum 200 --debug --lua-init=@zapret-lib.lua --lua-init=@zapret-antidpi.lua --lua-init=@zapret-auto.lua --lua-desync=detect_payload_str:pattern=1234:payload=my --lua-desync=fake:blob=0x1234:payload=my
|
||||
function detect_payload_str(ctx, desync)
|
||||
if not desync.arg.pattern then
|
||||
error("detect_payload_str: missing 'pattern'")
|
||||
end
|
||||
local data = desync.reasm_data or desync.dis.payload
|
||||
local b = string.find(data,desync.arg.pattern,1,true)
|
||||
if b then
|
||||
DLOG("detect_payload_str: detected '"..desync.arg.payload.."'")
|
||||
if desync.arg.payload then desync.l7payload = desync.arg.payload end
|
||||
else
|
||||
DLOG("detect_payload_str: not detected '"..desync.arg.payload.."'")
|
||||
if desync.arg.undetected then desync.l7payload = desync.arg.undetected end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- this shim is needed then function is orchestrated. ctx services not available
|
||||
-- have to emulate cutoff in LUA using connection persistent table track.lua_state
|
||||
function instance_cutoff_shim(ctx, desync, dir)
|
||||
@@ -153,16 +177,19 @@ function plan_instance_execute(desync, verdict, instance)
|
||||
end
|
||||
return verdict
|
||||
end
|
||||
|
||||
-- redo what whould be done without orchestration
|
||||
function replay_execution_plan(desync, plan)
|
||||
local verdict = VERDICT_PASS
|
||||
for i=1,#plan do
|
||||
verdict = plan_instance_execute(desync, verdict, plan[i])
|
||||
end
|
||||
return verdict
|
||||
function plan_instance_pop(desync)
|
||||
return (desync.plan and #desync.plan>0) and table.remove(desync.plan, 1)
|
||||
end
|
||||
function plan_clear(desync)
|
||||
while table.remove(desync.plan) do end
|
||||
end
|
||||
-- this approach allows nested orchestrators
|
||||
function orchestrate(ctx, desync)
|
||||
if not desync.plan then
|
||||
execution_plan_cancel(ctx)
|
||||
desync.plan = execution_plan(ctx)
|
||||
end
|
||||
end
|
||||
|
||||
-- copy desync preserving lua_state
|
||||
function desync_copy(desync)
|
||||
local dcopy = deepcopy(desync)
|
||||
@@ -170,23 +197,33 @@ function desync_copy(desync)
|
||||
-- preserve lua state
|
||||
dcopy.track.lua_state = desync.track.lua_state
|
||||
end
|
||||
if desync.plan then
|
||||
-- preserve execution plan
|
||||
dcopy.plan = desync.plan
|
||||
end
|
||||
return dcopy
|
||||
end
|
||||
|
||||
-- redo what whould be done without orchestration
|
||||
function replay_execution_plan(desync)
|
||||
local dcopy = desync_copy(desync)
|
||||
local verdict = VERDICT_PASS
|
||||
while true do
|
||||
local instance = plan_instance_pop(dcopy)
|
||||
if not instance then break end
|
||||
verdict = plan_instance_execute(dcopy, verdict, instance)
|
||||
end
|
||||
return verdict
|
||||
end
|
||||
-- this function demonstrates how to stop execution of upcoming desync instances and take over their job
|
||||
-- this can be used, for example, for orchestrating conditional processing without modifying of desync functions code
|
||||
-- test case : nfqws2 --qnum 200 --debug --lua-init=@zapret-lib.lua --lua-desync=desync_orchestrator_example --lua-desync=pass --lua-desync=pass
|
||||
function desync_orchestrator_example(ctx, desync)
|
||||
local plan = execution_plan(ctx)
|
||||
if #plan>0 then
|
||||
DLOG("orchestrator: taking over upcoming desync instances")
|
||||
local dcopy = desync_copy(desync)
|
||||
execution_plan_cancel(ctx)
|
||||
return replay_execution_plan(dcopy, plan)
|
||||
end
|
||||
DLOG("orchestrator: taking over upcoming desync instances")
|
||||
orchestrate(ctx, desync)
|
||||
return replay_execution_plan(desync)
|
||||
end
|
||||
|
||||
-- these function duplicate range check logic from C code
|
||||
-- these functions duplicate range check logic from C code
|
||||
-- mode must be n,d,b,s,x,a
|
||||
-- pos is {mode,pos}
|
||||
-- range is {from={mode,pos}, to={mode,pos}, upper_cutoff}
|
||||
@@ -238,7 +275,9 @@ end
|
||||
function pos_str(desync, pos)
|
||||
return pos.mode..pos_get(desync, pos.mode)
|
||||
end
|
||||
|
||||
function is_retransmission(desync)
|
||||
return desync.track and desync.track.tcp and 0==bitand(u32add(desync.track.tcp.uppos_orig_prev, -desync.track.tcp.pos_orig), 0x80000000)
|
||||
end
|
||||
|
||||
-- prepare standard rawsend options from desync
|
||||
-- repeats - how many time send the packet
|
||||
@@ -310,6 +349,9 @@ function str_or_hex(s)
|
||||
return s
|
||||
end
|
||||
end
|
||||
function logical_xor(a,b)
|
||||
return a and not b or not a and b
|
||||
end
|
||||
-- print to DLOG any variable. tables are expanded in the tree form, unprintables strings are hex dumped
|
||||
function var_debug(v)
|
||||
local function dbg(v,level)
|
||||
@@ -572,6 +614,19 @@ function dissect_nld(domain, level)
|
||||
return nil
|
||||
end
|
||||
|
||||
-- support sni=%var
|
||||
function tls_mod_shim(desync, blob, modlist, payload)
|
||||
local p1,p2 = string.find(modlist,"sni=%%[^,]+")
|
||||
if p1 then
|
||||
local var = string.sub(modlist,p1+5,p2)
|
||||
local val = desync[var] or _G[var]
|
||||
if not val then
|
||||
error("tls_mod_shim: non-existent var '"..var.."'")
|
||||
end
|
||||
modlist = string.sub(modlist,1,p1+3)..val..string.sub(modlist,p2+1)
|
||||
end
|
||||
return tls_mod(blob,modlist,payload)
|
||||
end
|
||||
|
||||
-- convert comma separated list of tcp flags to tcp.th_flags bit field
|
||||
function parse_tcp_flags(s)
|
||||
@@ -1315,16 +1370,3 @@ function ipfrag2(dis, ipfrag_options)
|
||||
|
||||
return {dis1,dis2}
|
||||
end
|
||||
|
||||
|
||||
-- location is url compatible with Location: header
|
||||
-- hostname is original hostname
|
||||
function is_dpi_redirect(hostname, location)
|
||||
local ds = dissect_url(location)
|
||||
if ds.domain then
|
||||
local sld1 = dissect_nld(hostname,2)
|
||||
local sld2 = dissect_nld(ds.domain,2)
|
||||
return sld2 and sld1~=sld2
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
@@ -180,9 +180,17 @@ static void ConntrackFeedPacket(t_ctrack *t, bool bReverse, const struct tcphdr
|
||||
mss = ntohs(tcp_find_mss(tcphdr));
|
||||
if (bReverse)
|
||||
{
|
||||
t->pos.pos_orig = t->pos.seq_last = ntohl(tcphdr->th_ack);
|
||||
t->pos.ack_last = ntohl(tcphdr->th_seq);
|
||||
t->pos.pos_orig = t->pos.seq_last = ntohl(tcphdr->th_ack);
|
||||
t->pos.pos_reply = t->pos.ack_last + len_payload;
|
||||
if (t->pos.state == SYN)
|
||||
t->pos.uppos_reply_prev = t->pos.uppos_reply = t->pos.pos_reply;
|
||||
else if (len_payload)
|
||||
{
|
||||
t->pos.uppos_reply_prev = t->pos.uppos_reply;
|
||||
if (!((t->pos.pos_reply - t->pos.uppos_reply) & 0x80000000))
|
||||
t->pos.uppos_reply = t->pos.pos_reply;
|
||||
}
|
||||
t->pos.winsize_reply = ntohs(tcphdr->th_win);
|
||||
t->pos.winsize_reply_calc = t->pos.winsize_reply;
|
||||
if (t->pos.scale_reply != SCALE_NONE) t->pos.winsize_reply_calc <<= t->pos.scale_reply;
|
||||
@@ -194,6 +202,14 @@ static void ConntrackFeedPacket(t_ctrack *t, bool bReverse, const struct tcphdr
|
||||
t->pos.seq_last = ntohl(tcphdr->th_seq);
|
||||
t->pos.pos_orig = t->pos.seq_last + len_payload;
|
||||
t->pos.pos_reply = t->pos.ack_last = ntohl(tcphdr->th_ack);
|
||||
if (t->pos.state == SYN)
|
||||
t->pos.uppos_orig_prev = t->pos.uppos_orig = t->pos.pos_orig;
|
||||
else if (len_payload)
|
||||
{
|
||||
t->pos.uppos_orig_prev = t->pos.uppos_orig;
|
||||
if (!((t->pos.pos_orig - t->pos.uppos_orig) & 0x80000000))
|
||||
t->pos.uppos_orig = t->pos.pos_orig;
|
||||
}
|
||||
t->pos.winsize_orig = ntohs(tcphdr->th_win);
|
||||
t->pos.winsize_orig_calc = t->pos.winsize_orig;
|
||||
if (t->pos.scale_orig != SCALE_NONE) t->pos.winsize_orig_calc <<= t->pos.scale_orig;
|
||||
@@ -394,17 +410,30 @@ bool ReasmResize(t_reassemble *reasm, size_t new_size)
|
||||
if (reasm->size_present > new_size) reasm->size_present = new_size;
|
||||
return true;
|
||||
}
|
||||
#define REASM_MAX_NEG 0x100000
|
||||
bool ReasmFeed(t_reassemble *reasm, uint32_t seq, const void *payload, size_t len)
|
||||
{
|
||||
if (reasm->seq != seq) return false; // fail session if out of sequence
|
||||
|
||||
size_t szcopy;
|
||||
szcopy = reasm->size - reasm->size_present;
|
||||
if (len < szcopy) szcopy = len;
|
||||
memcpy(reasm->packet + reasm->size_present, payload, szcopy);
|
||||
reasm->size_present += szcopy;
|
||||
reasm->seq += (uint32_t)szcopy;
|
||||
uint32_t dseq = seq - reasm->seq;
|
||||
if (dseq && (dseq < REASM_MAX_NEG))
|
||||
return false; // fail session if a gap about to appear
|
||||
uint32_t neg_overlap = reasm->seq - seq;
|
||||
if (neg_overlap > REASM_MAX_NEG)
|
||||
return false; // too big minus
|
||||
|
||||
size_t szcopy, szignore;
|
||||
szignore = (neg_overlap > reasm->size_present) ? neg_overlap - reasm->size_present : 0;
|
||||
if (szignore>=len) return true; // everyting is before the starting pos
|
||||
szcopy = len - szignore;
|
||||
neg_overlap -= szignore;
|
||||
if ((reasm->size_present - neg_overlap + szcopy) > reasm->size)
|
||||
return false; // buffer overflow
|
||||
// in case of seq overlap new data replaces old - unix behavior
|
||||
memcpy(reasm->packet + reasm->size_present - neg_overlap, payload + szignore, szcopy);
|
||||
if (szcopy>neg_overlap)
|
||||
{
|
||||
reasm->size_present += szcopy - neg_overlap;
|
||||
reasm->seq += (uint32_t)szcopy - neg_overlap;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool ReasmHasSpace(t_reassemble *reasm, size_t len)
|
||||
|
||||
@@ -43,7 +43,7 @@ typedef struct
|
||||
// this structure helps to reassemble continuous packets streams. it does not support out-of-orders
|
||||
typedef struct {
|
||||
uint8_t *packet; // allocated for size during reassemble request. requestor must know the message size.
|
||||
uint32_t seq; // current seq number. if a packet comes with an unexpected seq - it fails reassemble session.
|
||||
uint32_t seq; // current seq number. if a packet comes with unsupported seq overlap - it fails reassemble session.
|
||||
size_t size; // expected message size. success means that we have received exactly 'size' bytes and have them in 'packet'
|
||||
size_t size_present; // how many bytes already stored in 'packet'
|
||||
} t_reassemble;
|
||||
@@ -61,8 +61,7 @@ typedef struct
|
||||
bool dp_search_complete;
|
||||
|
||||
uint8_t req_retrans_counter; // number of request retransmissions
|
||||
bool req_seq_present,req_seq_finalized,req_seq_abandoned;
|
||||
uint32_t req_seq_start,req_seq_end; // sequence interval of the request (to track retransmissions)
|
||||
bool retrans_detect_finalized;
|
||||
|
||||
uint8_t incoming_ttl;
|
||||
|
||||
|
||||
@@ -20,6 +20,8 @@ typedef struct
|
||||
uint64_t pdcounter_orig, pdcounter_reply; // data packet counter (with payload)
|
||||
uint64_t pbcounter_orig, pbcounter_reply; // transferred byte counter. includes retransmissions. it's not the same as relative seq.
|
||||
uint32_t pos_orig, pos_reply; // TCP: seq_last+payload, ack_last+payload UDP: sum of all seen payload lenghts including current
|
||||
uint32_t uppos_orig, uppos_reply; // max seen position. useful to detect retransmissions
|
||||
uint32_t uppos_orig_prev, uppos_reply_prev; // previous max seen position. useful to detect retransmissions
|
||||
uint32_t seq_last, ack_last; // TCP: last seen seq and ack UDP: sum of all seen payload lenghts NOT including current
|
||||
|
||||
// tcp only state, not used in udp
|
||||
|
||||
@@ -240,6 +240,11 @@ static void auto_hostlist_reset_fail_counter(struct desync_profile *dp, const ch
|
||||
}
|
||||
}
|
||||
|
||||
static bool is_retransmission(const t_ctrack *ctrack)
|
||||
{
|
||||
return !((ctrack->pos.uppos_orig_prev - ctrack->pos.pos_orig) & 0x80000000);
|
||||
}
|
||||
|
||||
// return true if retrans trigger fires
|
||||
static bool auto_hostlist_retrans(t_ctrack *ctrack, uint8_t l4proto, int threshold, const char *client_ip_port, t_l7proto l7proto)
|
||||
{
|
||||
@@ -247,24 +252,28 @@ static bool auto_hostlist_retrans(t_ctrack *ctrack, uint8_t l4proto, int thresho
|
||||
{
|
||||
if (l4proto == IPPROTO_TCP)
|
||||
{
|
||||
if (!ctrack->req_seq_finalized || ctrack->req_seq_abandoned)
|
||||
if (ctrack->retrans_detect_finalized)
|
||||
return false;
|
||||
if (!seq_within(ctrack->pos.seq_last, ctrack->req_seq_start, ctrack->req_seq_end))
|
||||
if (!seq_within(ctrack->pos.seq_last, ctrack->pos.seq0, ctrack->pos.seq0 + ctrack->dp->hostlist_auto_retrans_maxseq))
|
||||
{
|
||||
DLOG("req retrans : tcp seq %u not within the req range %u-%u. stop tracking.\n", ctrack->pos.seq_last, ctrack->req_seq_start, ctrack->req_seq_end);
|
||||
ctrack->retrans_detect_finalized = true;
|
||||
DLOG("retrans : tcp seq %u not within the req range %u-%u. stop tracking.\n", ctrack->pos.seq_last, ctrack->pos.seq0, ctrack->pos.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;
|
||||
}
|
||||
if (!is_retransmission(ctrack))
|
||||
return false;
|
||||
}
|
||||
ctrack->req_retrans_counter++;
|
||||
if (ctrack->req_retrans_counter >= threshold)
|
||||
{
|
||||
DLOG("req retrans threshold reached : %u/%u\n", ctrack->req_retrans_counter, threshold);
|
||||
DLOG("retrans threshold reached : %u/%u\n", ctrack->req_retrans_counter, threshold);
|
||||
ctrack_stop_retrans_counter(ctrack);
|
||||
ctrack->retrans_detect_finalized = true;
|
||||
return true;
|
||||
}
|
||||
DLOG("req retrans counter : %u/%u\n", ctrack->req_retrans_counter, threshold);
|
||||
DLOG("retrans counter : %u/%u\n", ctrack->req_retrans_counter, threshold);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -1169,17 +1178,6 @@ static uint8_t dpi_desync_tcp_packet_play(
|
||||
DLOG("not applying tampering to HTTP without Host:\n");
|
||||
goto pass;
|
||||
}
|
||||
if (ctrack)
|
||||
{
|
||||
// we do not reassemble http
|
||||
if (!ctrack->req_seq_present)
|
||||
{
|
||||
ctrack->req_seq_start = ctrack->pos.seq_last;
|
||||
ctrack->req_seq_end = ctrack->pos.pos_orig - 1;
|
||||
ctrack->req_seq_present = ctrack->req_seq_finalized = true;
|
||||
DLOG("req retrans : tcp seq interval %u-%u\n", ctrack->req_seq_start, ctrack->req_seq_end);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (IsTLSClientHello(rdata_payload, rlen_payload, TLS_PARTIALS_ENABLE))
|
||||
{
|
||||
@@ -1198,27 +1196,12 @@ static uint8_t dpi_desync_tcp_packet_play(
|
||||
if (ctrack && !(params.reasm_payload_disable && l7_payload_match(l7payload, params.reasm_payload_disable)))
|
||||
{
|
||||
// do not reasm retransmissions
|
||||
if (!bReqFull && ReasmIsEmpty(&ctrack->reasm_orig) && !ctrack->req_seq_abandoned &&
|
||||
!(ctrack->req_seq_finalized && seq_within(ctrack->pos.seq_last, ctrack->req_seq_start, ctrack->req_seq_end)))
|
||||
if (!bReqFull && ReasmIsEmpty(&ctrack->reasm_orig) && !is_retransmission(ctrack))
|
||||
{
|
||||
// do not reconstruct unexpected large payload (they are feeding garbage ?)
|
||||
if (!reasm_orig_start(ctrack, IPPROTO_TCP, TLSRecordLen(dis->data_payload), TCP_MAX_REASM, dis->data_payload, dis->len_payload))
|
||||
goto pass_reasm_cancel;
|
||||
}
|
||||
if (!ctrack->req_seq_finalized)
|
||||
{
|
||||
if (!ctrack->req_seq_present)
|
||||
{
|
||||
// lower bound of request seq interval
|
||||
ctrack->req_seq_start = ctrack->pos.seq_last;
|
||||
ctrack->req_seq_present = true;
|
||||
}
|
||||
// upper bound of request seq interval
|
||||
// it can grow on every packet until request is complete. then interval is finalized and never touched again.
|
||||
ctrack->req_seq_end = ctrack->pos.pos_orig - 1;
|
||||
DLOG("req retrans : seq interval %u-%u\n", ctrack->req_seq_start, ctrack->req_seq_end);
|
||||
ctrack->req_seq_finalized |= bReqFull;
|
||||
}
|
||||
|
||||
if (!ReasmIsEmpty(&ctrack->reasm_orig))
|
||||
{
|
||||
@@ -1259,12 +1242,6 @@ static uint8_t dpi_desync_tcp_packet_play(
|
||||
};
|
||||
protocol_probe(testers, sizeof(testers) / sizeof(*testers), dis->data_payload, dis->len_payload, ctrack, &l7proto, &l7payload);
|
||||
}
|
||||
if (ctrack && ctrack->req_seq_finalized)
|
||||
{
|
||||
uint32_t dseq = ctrack->pos.seq_last - ctrack->req_seq_end;
|
||||
// do not react to 32-bit overflowed sequence numbers. allow 16 Mb grace window then cutoff.
|
||||
if (dseq >= 0x1000000 && !(dseq & 0x80000000)) ctrack->req_seq_abandoned = true;
|
||||
}
|
||||
|
||||
if (bHaveHost)
|
||||
{
|
||||
@@ -1327,7 +1304,6 @@ static uint8_t dpi_desync_tcp_packet_play(
|
||||
DLOG("desync profile changed by revealed l7 protocol or hostname !\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (bHaveHost && !PROFILE_HOSTLISTS_EMPTY(dp))
|
||||
{
|
||||
if (!bCheckDone)
|
||||
|
||||
@@ -1291,17 +1291,21 @@ void lua_pushf_ctrack(const t_ctrack *ctrack, const t_ctrack_position *pos)
|
||||
if (ctrack->ipproto == IPPROTO_TCP)
|
||||
{
|
||||
lua_pushliteral(params.L, "tcp");
|
||||
lua_createtable(params.L, 0, 14);
|
||||
lua_createtable(params.L, 0, 18);
|
||||
lua_pushf_lint("seq0", pos->seq0);
|
||||
lua_pushf_lint("seq", pos->seq_last);
|
||||
lua_pushf_lint("ack0", pos->ack0);
|
||||
lua_pushf_lint("ack", pos->ack_last);
|
||||
lua_pushf_int("pos_orig", pos->pos_orig - pos->seq0);
|
||||
lua_pushf_int("uppos_orig", pos->uppos_orig - pos->seq0);
|
||||
lua_pushf_int("uppos_orig_prev", pos->uppos_orig_prev - pos->seq0);
|
||||
lua_pushf_int("winsize_orig", pos->winsize_orig);
|
||||
lua_pushf_int("winsize_orig_calc", pos->winsize_orig_calc);
|
||||
lua_pushf_int("scale_orig", pos->scale_orig);
|
||||
lua_pushf_int("mss_orig", pos->mss_orig);
|
||||
lua_pushf_int("pos_reply", pos->pos_reply - pos->ack0);
|
||||
lua_pushf_int("uppos_reply", pos->uppos_reply - pos->ack0);
|
||||
lua_pushf_int("uppos_reply_prev", pos->uppos_reply_prev - pos->ack0);
|
||||
lua_pushf_int("winsize_reply", pos->winsize_reply);
|
||||
lua_pushf_int("winsize_reply_calc", pos->winsize_reply_calc);
|
||||
lua_pushf_int("scale_reply", pos->scale_reply);
|
||||
|
||||
25
nfq2/nfqws.c
25
nfq2/nfqws.c
@@ -1434,6 +1434,7 @@ static void exithelp(void)
|
||||
" --hostlist-auto-fail-threshold=<int>\t\t\t; how many failed attempts cause hostname to be added to auto hostlist (default : %d)\n"
|
||||
" --hostlist-auto-fail-time=<int>\t\t\t; all failed attemps must be within these seconds (default : %d)\n"
|
||||
" --hostlist-auto-retrans-threshold=<int>\t\t; how many request retransmissions cause attempt to fail (default : %d)\n"
|
||||
" --hostlist-auto-retrans-maxseq=<int>\t\t\t; count retransmissions only within this relative sequence (default : %u)\n"
|
||||
" --hostlist-auto-debug=<logfile>\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"
|
||||
@@ -1448,7 +1449,8 @@ static void exithelp(void)
|
||||
IPCACHE_LIFETIME,
|
||||
LUA_GC_INTERVAL,
|
||||
all_protos,
|
||||
HOSTLIST_AUTO_FAIL_THRESHOLD_DEFAULT, HOSTLIST_AUTO_FAIL_TIME_DEFAULT, HOSTLIST_AUTO_RETRANS_THRESHOLD_DEFAULT,
|
||||
HOSTLIST_AUTO_FAIL_THRESHOLD_DEFAULT, HOSTLIST_AUTO_FAIL_TIME_DEFAULT,
|
||||
HOSTLIST_AUTO_RETRANS_THRESHOLD_DEFAULT, HOSTLIST_AUTO_RETRANS_MAXSEQ,
|
||||
all_payloads
|
||||
);
|
||||
exit(1);
|
||||
@@ -1545,6 +1547,7 @@ enum opt_indices {
|
||||
IDX_HOSTLIST_AUTO_FAIL_THRESHOLD,
|
||||
IDX_HOSTLIST_AUTO_FAIL_TIME,
|
||||
IDX_HOSTLIST_AUTO_RETRANS_THRESHOLD,
|
||||
IDX_HOSTLIST_AUTO_RETRANS_MAXSEQ,
|
||||
IDX_HOSTLIST_AUTO_DEBUG,
|
||||
IDX_NEW,
|
||||
IDX_SKIP,
|
||||
@@ -1629,6 +1632,7 @@ static const struct option long_options[] = {
|
||||
[IDX_HOSTLIST_AUTO_FAIL_THRESHOLD] = {"hostlist-auto-fail-threshold", required_argument, 0, 0},
|
||||
[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_DEBUG] = {"hostlist-auto-debug", required_argument, 0, 0},
|
||||
[IDX_NEW] = {"new", no_argument, 0, 0},
|
||||
[IDX_SKIP] = {"skip", no_argument, 0, 0},
|
||||
@@ -1691,6 +1695,22 @@ static const struct option long_options[] = {
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
/*
|
||||
t_reassemble t;
|
||||
ReasmInit(&t,16,-10);
|
||||
memset(t.packet,0,16);
|
||||
bool b;
|
||||
b=ReasmFeed(&t,-10,"0123456789",10);
|
||||
printf("b=%u size=%zu seq=%d s=%s\n",b,t.size_present,t.seq,t.packet);
|
||||
b=ReasmFeed(&t,0,"YOREK",5);
|
||||
printf("b=%u size=%zu seq=%d s=%s\n",b,t.size_present,t.seq,t.packet);
|
||||
b=ReasmFeed(&t,-12,"XOR",3);
|
||||
printf("b=%u size=%zu seq=%d s=%s\n",b,t.size_present,t.seq,t.packet);
|
||||
b=ReasmFeed(&t,3,"abc",3);
|
||||
printf("b=%u size=%zu seq=%d s=%s\n",b,t.size_present,t.seq,t.packet);
|
||||
return 0;
|
||||
*/
|
||||
|
||||
#ifdef __CYGWIN__
|
||||
if (service_run(argc, argv))
|
||||
{
|
||||
@@ -2077,6 +2097,9 @@ int main(int argc, char **argv)
|
||||
exit_clean(1);
|
||||
}
|
||||
break;
|
||||
case IDX_HOSTLIST_AUTO_RETRANS_MAXSEQ:
|
||||
dp->hostlist_auto_retrans_maxseq = (uint32_t)atoi(optarg);
|
||||
break;
|
||||
case IDX_HOSTLIST_AUTO_DEBUG:
|
||||
{
|
||||
FILE *F = fopen(optarg, "a+t");
|
||||
|
||||
@@ -342,6 +342,7 @@ void dp_init(struct desync_profile *dp)
|
||||
dp->hostlist_auto_fail_threshold = HOSTLIST_AUTO_FAIL_THRESHOLD_DEFAULT;
|
||||
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->filter_ipv4 = dp->filter_ipv6 = true;
|
||||
}
|
||||
static void dp_clear_dynamic(struct desync_profile *dp)
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#define HOSTLIST_AUTO_FAIL_THRESHOLD_DEFAULT 3
|
||||
#define HOSTLIST_AUTO_FAIL_TIME_DEFAULT 60
|
||||
#define HOSTLIST_AUTO_RETRANS_THRESHOLD_DEFAULT 3
|
||||
#define HOSTLIST_AUTO_RETRANS_MAXSEQ 65536
|
||||
|
||||
#define IPCACHE_LIFETIME 7200
|
||||
|
||||
@@ -78,6 +79,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;
|
||||
|
||||
hostfail_pool *hostlist_auto_fail_counters;
|
||||
|
||||
|
||||
@@ -848,7 +848,7 @@ bool TLSMod(const struct fake_tls_mod *tls_mod, const uint8_t *payload, size_t p
|
||||
{
|
||||
if (tls_mod->mod & FAKE_TLS_MOD_DUP_SID)
|
||||
{
|
||||
if (IsTLSClientHello(payload, payload_len, false))
|
||||
if (IsTLSClientHelloPartial(payload, payload_len))
|
||||
{
|
||||
if (payload_len < 44)
|
||||
{
|
||||
|
||||
@@ -127,7 +127,7 @@ bool TLSHelloExtractHostFromHandshake(const uint8_t *data, size_t len, char *hos
|
||||
|
||||
struct fake_tls_mod
|
||||
{
|
||||
char sni[128];
|
||||
char sni[256];
|
||||
uint32_t mod;
|
||||
};
|
||||
#define FAKE_TLS_MOD_RND 0x01
|
||||
|
||||
Reference in New Issue
Block a user