mirror of
https://github.com/bol-van/zapret-win-bundle.git
synced 2025-12-16 20:07:04 +03:00
893 lines
37 KiB
Lua
893 lines
37 KiB
Lua
--[[
|
|
|
|
NFQWS2 ANTIDPI LIBRARY
|
|
|
|
--lua-init=@zapret-lib.lua --lua-init=@zapret-antidpi.lua
|
|
--lua-desync=func1:arg1[=val1]:arg2[=val2] --lua-desync=func2:arg1[=val1]:arg2[=val2] .... --lua-desync=funcN:arg1[=val1]:arg2[=val2]
|
|
|
|
BLOBS
|
|
|
|
blobs can be 0xHEX, field name in desync or global var
|
|
standard way to bring binary data to lua code is using the "--blob" parameter of nfqws2
|
|
dynamic blobs can be inside desync table. one function can prepare data for next functions.
|
|
|
|
STANDARD FUNCTION ARGS
|
|
|
|
standard direction :
|
|
|
|
* dir = in|out|any
|
|
|
|
standard fooling :
|
|
|
|
* ip_ttl=N - set ipv.ip_ttl to N
|
|
* ip6_ttl=N - set ip6.ip6_hlim to N
|
|
* ip_autottl=delta,min-max - set ip.ip_ttl to auto discovered ttl
|
|
* ip6_autottl=delta,min-max - set ip.ip_ttl to auto discovered ttl
|
|
|
|
* ip6_hopbyhop[=hex] - add hopbyhop ipv6 header with optional data. data size must be 6+N*8. all zero by default.
|
|
* ip6_hopbyhop2[=hex] - add second hopbyhop ipv6 header with optional data. data size must be 6+N*8. all zero by default.
|
|
* ip6_destopt[=hex] - add destopt ipv6 header with optional data. data size must be 6+N*8. all zero by default.
|
|
* ip6_destopt2[=hex] - add second destopt ipv6 header with optional data. data size must be 6+N*8. all zero by default.
|
|
* ip6_routing[=hex] - add routing ipv6 header with optional data. data size must be 6+N*8. all zero by default.
|
|
* ip6_ah[=hex] - add authentication ipv6 header with optional data. data size must be 6+N*4. 0000 + 4 random bytes by default.
|
|
|
|
* tcp_seq=N - add N to tcp.th_seq
|
|
* tcp_ack=N - add N to tcp.th_ack
|
|
* tcp_ts=N - add N to timestamp value
|
|
* tcp_md5[=hex] - add MD5 header with optional 16-byte data. all zero by default.
|
|
* tcp_flags_set=<list> - set tcp flags in comma separated list
|
|
* tcp_flags_unset=<list> - unset tcp flags in comma separated list
|
|
* tcp_ts_up - move timestamp tcp option to the top if present (workaround for badack without badseq fooling)
|
|
|
|
* fool=fool_function - custom fooling function : fool_func(dis, fooling_options)
|
|
|
|
standard reconstruct :
|
|
|
|
* badsum - make L4 checksum invalid
|
|
|
|
standard rawsend :
|
|
|
|
* repeats - how many time send the packet
|
|
* ifout - override outbound interface (if --bind_fix4, --bind-fix6 enabled)
|
|
* fwmark - override fwmark. desync mark bit(s) will be set unconditionally
|
|
|
|
standard payload :
|
|
|
|
* payload - comma separarated list of allowed payload types. if not present - allow non-empty known payloads.
|
|
|
|
standard ip_id :
|
|
|
|
* ip_id - seq|rnd|zero|none
|
|
* ip_id_conn - in 'seq' mode save current ip_id in track.lua_state to use it between packets
|
|
|
|
standard ipfrag :
|
|
|
|
* ipfrag[=frag_function] - ipfrag function name. "ipfrag2" by default if empty
|
|
* ipfrag_disorder - send fragments from last to first
|
|
* ipfrag2 : ipfrag_pos_udp - udp frag position. ipv4 : starting from L4 header. ipb6: starting from fragmentable part. must be multiple of 8. default 8
|
|
* ipfrag2 : ipfrag_pos_tcp - tcp frag position. ipv4 : starting from L4 header. ipb6: starting from fragmentable part. must be multiple of 8. default 32
|
|
* ipfrag2 : ipfrag_next - next protocol field in ipv6 fragment extenstion header of the second fragment. same as first by default.
|
|
|
|
]]
|
|
|
|
|
|
-- drop packet
|
|
-- standard args : direction, payload
|
|
function drop(ctx, desync)
|
|
direction_cutoff_opposite(ctx, desync, "any")
|
|
if direction_check(desync, "any") and payload_check(desync,"all") then
|
|
DLOG("drop")
|
|
return VERDICT_DROP
|
|
end
|
|
end
|
|
|
|
-- nfqws1 : "--dup"
|
|
-- standard args : direction, fooling, ip_id, ipfrag, rawsend, reconstruct
|
|
function send(ctx, desync)
|
|
direction_cutoff_opposite(ctx, desync, "any")
|
|
if direction_check(desync, "any") then
|
|
DLOG("send")
|
|
local dis = deepcopy(desync.dis)
|
|
apply_fooling(desync, dis)
|
|
apply_ip_id(desync, dis, nil, "none")
|
|
-- it uses rawsend, reconstruct and ipfrag options
|
|
rawsend_dissect_ipfrag(dis, desync_opts(desync))
|
|
end
|
|
end
|
|
|
|
-- nfqws1 : "--orig"
|
|
-- apply modification to current packet
|
|
-- standard args : direction, fooling, ip_id
|
|
function pktmod(ctx, desync)
|
|
direction_cutoff_opposite(ctx, desync, "any")
|
|
if direction_check(desync, "any") then
|
|
-- apply to current packet
|
|
apply_fooling(desync)
|
|
apply_ip_id(desync, nil, nil, "none")
|
|
DLOG("pktmod: applied")
|
|
return VERDICT_MODIFY
|
|
end
|
|
end
|
|
|
|
-- nfqws1 : "--domcase"
|
|
-- standard args : direction
|
|
function http_domcase(ctx, desync)
|
|
if not desync.dis.tcp then
|
|
instance_cutoff_shim(ctx, desync)
|
|
return
|
|
end
|
|
direction_cutoff_opposite(ctx, desync)
|
|
if desync.l7payload=="http_req" and direction_check(desync) then
|
|
local host_range = resolve_multi_pos(desync.dis.payload,desync.l7payload,"host,endhost")
|
|
if #host_range == 2 then
|
|
local host = string.sub(desync.dis.payload,host_range[1],host_range[2]-1)
|
|
local newhost="", i
|
|
for i = 1, #host do
|
|
newhost=newhost..((i%2)==0 and string.lower(string.sub(host,i,i)) or string.upper(string.sub(host,i,i)))
|
|
end
|
|
DLOG("http_domcase: "..host.." => "..newhost)
|
|
desync.dis.payload = string.sub(desync.dis.payload, 1, host_range[1]-1)..newhost..string.sub(desync.dis.payload, host_range[2])
|
|
return VERDICT_MODIFY
|
|
else
|
|
DLOG("http_domcase: cannot find host range")
|
|
end
|
|
end
|
|
end
|
|
|
|
-- nfqws1 : "--hostcase"
|
|
-- standard args : direction
|
|
-- arg : spell=<str> . spelling of the "Host" header. must be exactly 4 chars long
|
|
function http_hostcase(ctx, desync)
|
|
if not desync.dis.tcp then
|
|
instance_cutoff_shim(ctx, desync)
|
|
return
|
|
end
|
|
direction_cutoff_opposite(ctx, desync)
|
|
if desync.l7payload=="http_req" and direction_check(desync) then
|
|
local spell = desync.arg.spell or "host"
|
|
if #spell ~= 4 then
|
|
error("http_hostcase: invalid host spelling '"..spell.."'")
|
|
else
|
|
local hdis = http_dissect_req(desync.dis.payload)
|
|
if hdis.headers.host then
|
|
DLOG("http_hostcase: 'Host:' => '"..spell.."'")
|
|
desync.dis.payload = string.sub(desync.dis.payload,1,hdis.headers.host.pos_start-1)..spell..string.sub(desync.dis.payload,hdis.headers.host.pos_header_end+1)
|
|
return VERDICT_MODIFY
|
|
else
|
|
DLOG("http_hostcase: 'Host:' header not found")
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- nfqws1 : "--methodeol"
|
|
-- standard args : direction
|
|
function http_methodeol(ctx, desync)
|
|
if not desync.dis.tcp then
|
|
instance_cutoff_shim(ctx, desync)
|
|
return
|
|
end
|
|
direction_cutoff_opposite(ctx, desync)
|
|
if desync.l7payload=="http_req" and direction_check(desync) then
|
|
local hdis = http_dissect_req(desync.dis.payload)
|
|
local ua = hdis.headers["user-agent"]
|
|
if ua then
|
|
if (ua.pos_end - ua.pos_value_start) < 2 then
|
|
DLOG("http_methodeol: 'User-Agent:' header is too short")
|
|
else
|
|
DLOG("http_methodeol: applied")
|
|
desync.dis.payload="\r\n"..string.sub(desync.dis.payload,1,ua.pos_end-2)..(string.sub(desync.dis.payload,ua.pos_end+1) or "");
|
|
return VERDICT_MODIFY
|
|
end
|
|
else
|
|
DLOG("http_methodeol: 'User-Agent:' header not found")
|
|
end
|
|
end
|
|
end
|
|
|
|
-- nfqws1 : "--synack-split"
|
|
-- standard args : rawsend, reconstruct, ipfrag
|
|
-- arg : mode=syn|synack|acksyn . "synack" by default
|
|
function synack_split(ctx, desync)
|
|
if desync.dis.tcp then
|
|
if bitand(desync.dis.tcp.th_flags, TH_SYN + TH_ACK) == (TH_SYN + TH_ACK) then
|
|
local mode = desync.arg.mode or "synack"
|
|
local options = desync_opts(desync)
|
|
if mode=="syn" then
|
|
local dis = deepcopy(desync.dis)
|
|
dis.tcp.th_flags = bitand(desync.dis.tcp.th_flags, bitnot(TH_ACK))
|
|
DLOG("synack_split: sending SYN")
|
|
if not rawsend_dissect_ipfrag(dis, options) then return VERDICT_PASS end
|
|
return VERDICT_DROP
|
|
elseif mode=="synack" then
|
|
local dis = deepcopy(desync.dis)
|
|
dis.tcp.th_flags = bitand(desync.dis.tcp.th_flags, bitnot(TH_ACK))
|
|
DLOG("synack_split: sending SYN")
|
|
if not rawsend_dissect_ipfrag(dis, options) then return VERDICT_PASS end
|
|
dis.tcp.th_flags = bitand(desync.dis.tcp.th_flags, bitnot(TH_SYN))
|
|
DLOG("synack_split: sending ACK")
|
|
if not rawsend_dissect_ipfrag(dis, options) then return VERDICT_PASS end
|
|
return VERDICT_DROP
|
|
elseif mode=="acksyn" then
|
|
local dis = deepcopy(desync.dis)
|
|
dis.tcp.th_flags = bitand(desync.dis.tcp.th_flags, bitnot(TH_SYN))
|
|
DLOG("synack_split: sending ACK")
|
|
if not rawsend_dissect_ipfrag(dis, options) then return VERDICT_PASS end
|
|
dis.tcp.th_flags = bitand(desync.dis.tcp.th_flags, bitnot(TH_ACK))
|
|
DLOG("synack_split: sending SYN")
|
|
if not rawsend_dissect_ipfrag(dis, options) then return VERDICT_PASS end
|
|
return VERDICT_DROP
|
|
else
|
|
error("synack_split: bad mode '"..mode.."'")
|
|
end
|
|
else
|
|
instance_cutoff_shim(ctx, desync) -- mission complete
|
|
end
|
|
else
|
|
instance_cutoff_shim(ctx, desync)
|
|
end
|
|
end
|
|
|
|
-- nfqws1 : "--dpi-desync=synack"
|
|
-- standard args : rawsend, reconstruct, ipfrag
|
|
function synack(ctx, desync)
|
|
if desync.dis.tcp then
|
|
if bitand(desync.dis.tcp.th_flags, TH_SYN + TH_ACK)==TH_SYN then
|
|
local dis = deepcopy(desync.dis)
|
|
dis.tcp.th_flags = bitor(dis.tcp.th_flags, TH_ACK)
|
|
DLOG("synack: sending")
|
|
rawsend_dissect_ipfrag(dis, desync_opts(desync))
|
|
else
|
|
instance_cutoff_shim(ctx, desync) -- mission complete
|
|
end
|
|
else
|
|
instance_cutoff_shim(ctx, desync)
|
|
end
|
|
end
|
|
|
|
|
|
-- nfqws1 : "--wsize"
|
|
-- arg : wsize=N . tcp window size
|
|
-- arg : scale=N . tcp option scale factor
|
|
function wsize(ctx, desync)
|
|
if desync.dis.tcp then
|
|
if bitand(desync.dis.tcp.th_flags, TH_SYN + TH_ACK) == (TH_SYN + TH_ACK) then
|
|
if wsize_rewrite(desync.dis, desync.arg) then
|
|
return VERDICT_MODIFY
|
|
end
|
|
else
|
|
instance_cutoff_shim(ctx, desync) -- mission complete
|
|
end
|
|
else
|
|
instance_cutoff_shim(ctx, desync)
|
|
end
|
|
end
|
|
|
|
-- nfqws1 : "--wssize"
|
|
-- standard args : direction
|
|
-- arg : wsize=N . tcp window size
|
|
-- arg : scale=N . tcp option scale factor
|
|
-- arg : forced_cutoff=<list> - comma separated list of payloads that trigger forced wssize cutoff. by default - any non-empty payload
|
|
function wssize(ctx, desync)
|
|
if not desync.dis.tcp then
|
|
instance_cutoff_shim(ctx, desync)
|
|
return
|
|
end
|
|
local verdict = VERDICT_PASS
|
|
direction_cutoff_opposite(ctx, desync)
|
|
if direction_check(desync) then
|
|
if wsize_rewrite(desync.dis, desync.arg) then
|
|
verdict = VERDICT_MODIFY
|
|
end
|
|
if #desync.dis.payload>0 and (not desync.arg.forced_cutoff or in_list(desync.arg.forced_cutoff, desync.l7payload)) then
|
|
DLOG("wssize: forced cutoff")
|
|
instance_cutoff_shim(ctx, desync)
|
|
end
|
|
end
|
|
return verdict
|
|
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>. 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
|
|
local dis = deepcopy(desync.dis)
|
|
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_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
|
|
return VERDICT_DROP
|
|
end
|
|
else
|
|
instance_cutoff_shim(ctx, desync) -- mission complete
|
|
end
|
|
else
|
|
instance_cutoff_shim(ctx, desync)
|
|
end
|
|
end
|
|
|
|
-- nfqws1 : "--dpi-desync=rst"
|
|
-- standard args : direction, payload, fooling, ip_id, rawsend, reconstruct, ipfrag
|
|
-- arg : rstack - send RST,ACK instead of RST
|
|
function rst(ctx, desync)
|
|
if not desync.dis.tcp then
|
|
instance_cutoff_shim(ctx, desync)
|
|
return
|
|
end
|
|
direction_cutoff_opposite(ctx, desync)
|
|
if direction_check(desync, "any") and payload_check(desync) then
|
|
if replay_first(desync) then
|
|
local dis = deepcopy(desync.dis)
|
|
dis.payload = ""
|
|
dis.tcp.th_flags = TH_RST + (desync.arg.rstack and TH_ACK or 0)
|
|
apply_fooling(desync, dis)
|
|
apply_ip_id(desync, dis, nil, "none")
|
|
DLOG("rst")
|
|
-- it uses rawsend, reconstruct and ipfrag options
|
|
rawsend_dissect_ipfrag(dis, desync_opts(desync))
|
|
else
|
|
DLOG("rst: not acting on further replay pieces")
|
|
end
|
|
end
|
|
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 . sni=%var is supported
|
|
function fake(ctx, desync)
|
|
direction_cutoff_opposite(ctx, desync)
|
|
-- by default process only outgoing known payloads
|
|
if direction_check(desync) and payload_check(desync) then
|
|
if replay_first(desync) then
|
|
if not desync.arg.blob then
|
|
error("fake: 'blob' arg required")
|
|
end
|
|
local fake_payload = blob(desync, desync.arg.blob)
|
|
if desync.reasm_data and desync.arg.tls_mod then
|
|
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
|
|
rawsend_payload_segmented(desync,fake_payload)
|
|
else
|
|
DLOG("fake: not acting on further replay pieces")
|
|
end
|
|
end
|
|
end
|
|
|
|
-- nfqws1 : "--dpi-desync=multisplit"
|
|
-- standard args : direction, payload, fooling, ip_id, rawsend, reconstruct, ipfrag
|
|
-- arg : pos=<posmarker list> . position marker list. for example : "1,host,midsld+1,-10"
|
|
-- 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 pattern
|
|
-- arg : blob=<blob> - use this data instead of desync.dis.payload
|
|
-- arg : nodrop - do not drop current dissect
|
|
function multisplit(ctx, desync)
|
|
if not desync.dis.tcp then
|
|
instance_cutoff_shim(ctx, desync)
|
|
return
|
|
end
|
|
direction_cutoff_opposite(ctx, desync)
|
|
-- by default process only outgoing known payloads
|
|
local data = blob_or_def(desync, desync.arg.blob) or desync.reasm_data or desync.dis.payload
|
|
if #data>0 and direction_check(desync) and payload_check(desync) then
|
|
if replay_first(desync) then
|
|
local spos = desync.arg.pos or "2"
|
|
-- check debug to save CPU
|
|
if b_debug then DLOG("multisplit: split pos: "..spos) end
|
|
local pos = resolve_multi_pos(data, desync.l7payload, spos)
|
|
if b_debug then DLOG("multisplit: resolved split pos: "..table.concat(zero_based_pos(pos)," ")) end
|
|
delete_pos_1(pos) -- cannot split at the first byte
|
|
if #pos>0 then
|
|
for i=0,#pos do
|
|
local pos_start = pos[i] or 1
|
|
local pos_end = i<#pos and pos[i+1]-1 or #data
|
|
local part = string.sub(data,pos_start,pos_end)
|
|
local seqovl=0
|
|
if i==0 and desync.arg.seqovl and tonumber(desync.arg.seqovl)>0 then
|
|
seqovl = tonumber(desync.arg.seqovl)
|
|
local pat = desync.arg.seqovl_pattern and blob(desync,desync.arg.seqovl_pattern) or "\x00"
|
|
part = pattern(pat,1,seqovl)..part
|
|
end
|
|
if b_debug then DLOG("multisplit: sending part "..(i+1).." "..(pos_start-1).."-"..(pos_end-1).." len="..#part.." seqovl="..seqovl.." : "..hexdump_dlog(part)) end
|
|
if not rawsend_payload_segmented(desync,part,pos_start-1-seqovl) then
|
|
return VERDICT_PASS
|
|
end
|
|
end
|
|
replay_drop_set(desync)
|
|
return desync.arg.nodrop and VERDICT_PASS or VERDICT_DROP
|
|
else
|
|
DLOG("multisplit: no valid split positions")
|
|
end
|
|
else
|
|
DLOG("multisplit: not acting on further replay pieces")
|
|
end
|
|
-- drop replayed packets if reasm was sent successfully in splitted form
|
|
if replay_drop(desync) then
|
|
return desync.arg.nodrop and VERDICT_PASS or VERDICT_DROP
|
|
end
|
|
end
|
|
end
|
|
|
|
-- nfqws1 : "--dpi-desync=multidisorder"
|
|
-- standard args : direction, payload, fooling, ip_id, rawsend, reconstruct, ipfrag
|
|
-- 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 reasm_data
|
|
-- arg : nodrop - do not drop current dissect
|
|
function multidisorder(ctx, desync)
|
|
if not desync.dis.tcp then
|
|
instance_cutoff_shim(ctx, desync)
|
|
return
|
|
end
|
|
direction_cutoff_opposite(ctx, desync)
|
|
-- by default process only outgoing known payloads
|
|
local data = blob_or_def(desync, desync.arg.blob) or desync.reasm_data or desync.dis.payload
|
|
if #data>0 and direction_check(desync) and payload_check(desync) then
|
|
if replay_first(desync) then
|
|
local spos = desync.arg.pos or "2"
|
|
-- check debug to save CPU
|
|
if b_debug then DLOG("multidisorder: split pos: "..spos) end
|
|
local pos = resolve_multi_pos(data, desync.l7payload, spos)
|
|
if b_debug then DLOG("multidisorder: resolved split pos: "..table.concat(zero_based_pos(pos)," ")) end
|
|
delete_pos_1(pos) -- cannot split at the first byte
|
|
if #pos>0 then
|
|
for i=#pos,0,-1 do
|
|
local pos_start = pos[i] or 1
|
|
local pos_end = i<#pos and pos[i+1]-1 or #data
|
|
local part = string.sub(data,pos_start,pos_end)
|
|
local seqovl=0
|
|
if i==1 and desync.arg.seqovl then
|
|
seqovl = resolve_pos(data, desync.l7payload, desync.arg.seqovl)
|
|
if not seqovl then
|
|
DLOG("multidisorder: seqovl cancelled because could not resolve marker '"..desync.arg.seqovl.."'")
|
|
seqovl = 0
|
|
else
|
|
seqovl = seqovl - 1
|
|
if seqovl>=(pos[1]-1) then
|
|
DLOG("multidisorder: seqovl cancelled because seqovl "..seqovl.." is not less than the first split pos "..(pos[1]-1))
|
|
seqovl = 0
|
|
else
|
|
local pat = desync.arg.seqovl_pattern and blob(desync,desync.arg.seqovl_pattern) or "\x00"
|
|
part = pattern(pat,1,seqovl)..part
|
|
end
|
|
end
|
|
end
|
|
if b_debug then DLOG("multidisorder: sending part "..(i+1).." "..(pos_start-1).."-"..(pos_end-1).." len="..#part.." seqovl="..seqovl.." : "..hexdump_dlog(part)) end
|
|
if not rawsend_payload_segmented(desync,part,pos_start-1-seqovl) then
|
|
return VERDICT_PASS
|
|
end
|
|
end
|
|
replay_drop_set(desync)
|
|
return desync.arg.nodrop and VERDICT_PASS or VERDICT_DROP
|
|
else
|
|
DLOG("multidisorder: no valid split positions")
|
|
end
|
|
else
|
|
DLOG("multidisorder: not acting on further replay pieces")
|
|
end
|
|
-- drop replayed packets if reasm was sent successfully in splitted form
|
|
if replay_drop(desync) then
|
|
return desync.arg.nodrop and VERDICT_PASS or VERDICT_DROP
|
|
end
|
|
end
|
|
end
|
|
|
|
-- nfqws1 : "--dpi-desync=hostfakesplit"
|
|
-- standard args : direction, payload, fooling, ip_id, rawsend, reconstruct. FOOLING AND REPEATS APPLIED ONLY TO FAKES.
|
|
-- arg : host=<str> - hostname template. generate hosts like "random.template". example : e8nzn.vk.com
|
|
-- arg : midhost=<posmarker> - additionally split segment containing host at specified posmarker. must be within host+1 .. endhost-1 or split won't happen. example : "midsld"
|
|
-- arg : nofake1, nofake2 - do not send individual fakes
|
|
-- arg : disorder_after=<posmarker> - send after_host part in 2 disordered segments. if posmarker is empty string use marker "-1"
|
|
-- arg : blob=<blob> - use this data instead of desync.dis.payload
|
|
-- arg : nodrop - do not drop current dissect
|
|
function hostfakesplit(ctx, desync)
|
|
if not desync.dis.tcp then
|
|
instance_cutoff_shim(ctx, desync)
|
|
return
|
|
end
|
|
direction_cutoff_opposite(ctx, desync)
|
|
-- by default process only outgoing known payloads
|
|
local data = blob_or_def(desync, desync.arg.blob) or desync.reasm_data or desync.dis.payload
|
|
if #data>0 and direction_check(desync) and payload_check(desync) then
|
|
if replay_first(desync) then
|
|
local pos = resolve_range(data, desync.l7payload, "host,endhost-1", true)
|
|
if pos then
|
|
if b_debug then DLOG("hostfakesplit: resolved host range: "..table.concat(zero_based_pos(pos)," ")) end
|
|
|
|
-- do not apply fooling to original parts except tcp_ts_up but apply ip_id
|
|
local part, fakehost
|
|
local opts_orig = {rawsend = rawsend_opts_base(desync), reconstruct = {}, ipfrag = {}, ipid = desync.arg, fooling = {tcp_ts_up = desync.arg.tcp_ts_up}}
|
|
local opts_fake = {rawsend = rawsend_opts(desync), reconstruct = reconstruct_opts(desync), ipfrag = {}, ipid = desync.arg, fooling = desync.arg}
|
|
|
|
part = string.sub(data,1,pos[1]-1)
|
|
if b_debug then DLOG("hostfakesplit: sending before_host part 0-"..(pos[1]-2).." len="..#part.." : "..hexdump_dlog(part)) end
|
|
if not rawsend_payload_segmented(desync,part,0, opts_orig) then return VERDICT_PASS end
|
|
|
|
fakehost = genhost(pos[2]-pos[1]+1, desync.arg.host)
|
|
|
|
if not desync.arg.nofake1 then
|
|
if b_debug then DLOG("hostfakesplit: sending fake host part (1) "..(pos[1]-1).."-"..(pos[2]-1).." len="..#fakehost.." : "..hexdump_dlog(fakehost)) end
|
|
if not rawsend_payload_segmented(desync,fakehost,pos[1]-1, opts_fake) then return VERDICT_PASS end
|
|
end
|
|
|
|
local midhost
|
|
if desync.arg.midhost then
|
|
midhost = resolve_pos(data,desync.l7payload,desync.arg.midhost)
|
|
if not midhost then
|
|
DLOG("hostfakesplit: cannot resolve midhost marker '"..desync.arg.midhost.."'")
|
|
end
|
|
DLOG("hosfakesplit: midhost marker resolved to "..midhost)
|
|
if midhost<=pos[1] or midhost>pos[2] then
|
|
DLOG("hostfakesplit: midhost is not inside the host range")
|
|
midhost = nil
|
|
end
|
|
end
|
|
-- if present apply ipfrag only to real host parts. fakes and parts outside of the host must be visible to DPI.
|
|
if midhost then
|
|
part = string.sub(data,pos[1],midhost-1)
|
|
if b_debug then DLOG("hostfakesplit: sending real host part 1 "..(pos[1]-1).."-"..(midhost-2).." len="..#part.." : "..hexdump_dlog(part)) end
|
|
if not rawsend_payload_segmented(desync,part,pos[1]-1, opts_orig) then return VERDICT_PASS end
|
|
|
|
part = string.sub(data,midhost,pos[2])
|
|
if b_debug then DLOG("hostfakesplit: sending real host part 2 "..(midhost-1).."-"..(pos[2]-1).." len="..#part.." : "..hexdump_dlog(part)) end
|
|
if not rawsend_payload_segmented(desync,part,midhost-1, opts_orig) then return VERDICT_PASS end
|
|
else
|
|
part = string.sub(data,pos[1],pos[2])
|
|
if b_debug then DLOG("hostfakesplit: sending real host part "..(pos[1]-1).."-"..(pos[2]-1).." len="..#part.." : "..hexdump_dlog(part)) end
|
|
if not rawsend_payload_segmented(desync,part,pos[1]-1, opts_orig) then return VERDICT_PASS end
|
|
end
|
|
|
|
if not desync.arg.nofake2 then
|
|
if b_debug then DLOG("hostfakesplit: sending fake host part (2) "..(pos[1]-1).."-"..(pos[2]-1).." len="..#fakehost.." : "..hexdump_dlog(fakehost)) end
|
|
if not rawsend_payload_segmented(desync,fakehost,pos[1]-1, opts_fake) then return VERDICT_PASS end
|
|
end
|
|
|
|
local disorder_after_pos
|
|
if desync.arg.disorder_after then
|
|
disorder_after_pos = resolve_pos(data, desync.l7payload, desync.arg.disorder_after=="" and "-1" or desync.arg.disorder_after)
|
|
if disorder_after_pos then
|
|
-- pos[2] points to the last letter of the host starting from 1
|
|
if disorder_after_pos<=(pos[2]+1) then
|
|
DLOG("hostfakesplit: disorder_after marker '"..(disorder_after_pos-1).."' resolved to pos not after after_host pos "..pos[2])
|
|
disorder_after_pos = nil
|
|
end
|
|
|
|
else
|
|
DLOG("hostfakesplit: could not resolve disorder_after marker '"..desync.arg.disorder_after.."'")
|
|
end
|
|
end
|
|
if disorder_after_pos then
|
|
part = string.sub(data,disorder_after_pos)
|
|
if b_debug then DLOG("hostfakesplit: sending after_host part (2) "..(disorder_after_pos-1).."-"..(#data-1).." len="..#part.." : "..hexdump_dlog(part)) end
|
|
if not rawsend_payload_segmented(desync,part,disorder_after_pos-1, opts_orig) then return VERDICT_PASS end
|
|
|
|
part = string.sub(data,pos[2]+1,disorder_after_pos-1)
|
|
if b_debug then DLOG("hostfakesplit: sending after_host part (1) "..pos[2].."-"..(disorder_after_pos-2).." len="..#part.." : "..hexdump_dlog(part)) end
|
|
else
|
|
part = string.sub(data,pos[2]+1)
|
|
if b_debug then DLOG("hostfakesplit: sending after_host part "..pos[2].."-"..(#data-1).." len="..#part.." : "..hexdump_dlog(part)) end
|
|
end
|
|
if not rawsend_payload_segmented(desync,part,pos[2], opts_orig) then return VERDICT_PASS end
|
|
|
|
replay_drop_set(desync)
|
|
return desync.arg.nodrop and VERDICT_PASS or VERDICT_DROP
|
|
else
|
|
DLOG("hostfakesplit: host range cannot be resolved")
|
|
end
|
|
else
|
|
DLOG("hostfakesplit: not acting on further replay pieces")
|
|
end
|
|
-- drop replayed packets if reasm was sent successfully in splitted form
|
|
if replay_drop(desync) then
|
|
return desync.arg.nodrop and VERDICT_PASS or VERDICT_DROP
|
|
end
|
|
end
|
|
end
|
|
|
|
-- nfqws1 : "--dpi-desync=fakedsplit"
|
|
-- standard args : direction, payload, fooling, ip_id, rawsend, reconstruct. FOOLING AND REPEATS APPLIED ONLY TO FAKES.
|
|
-- arg : pos=<posmarker> - split position marker
|
|
-- arg : nofake1, nofake2, nofake3, nofake4 - do not send individual fakes
|
|
-- 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 reasm_data
|
|
-- arg : nodrop - do not drop current dissect
|
|
function fakedsplit(ctx, desync)
|
|
if not desync.dis.tcp then
|
|
instance_cutoff_shim(ctx, desync)
|
|
return
|
|
end
|
|
direction_cutoff_opposite(ctx, desync)
|
|
-- by default process only outgoing known payloads
|
|
local data = blob_or_def(desync, desync.arg.blob) or desync.reasm_data or desync.dis.payload
|
|
if #data>0 and direction_check(desync) and payload_check(desync) then
|
|
if replay_first(desync) then
|
|
local spos = desync.arg.pos or "2"
|
|
local pos = resolve_pos(data, desync.l7payload, spos)
|
|
if pos then
|
|
if pos == 1 then
|
|
DLOG("multidisorder: split pos resolved to 0. cannot split.")
|
|
else
|
|
if b_debug then DLOG("fakedsplit: resolved split pos: "..tostring(pos-1)) end
|
|
|
|
-- do not apply fooling to original parts except tcp_ts_up but apply ip_id
|
|
local fake, fakepat, part, pat
|
|
local opts_orig = {rawsend = rawsend_opts_base(desync), reconstruct = {}, ipfrag = {}, ipid = desync.arg, fooling = {tcp_ts_up = desync.arg.tcp_ts_up}}
|
|
local opts_fake = {rawsend = rawsend_opts(desync), reconstruct = reconstruct_opts(desync), ipfrag = {}, ipid = desync.arg, fooling = desync.arg}
|
|
|
|
fakepat = desync.arg.pattern and blob(desync,desync.arg.pattern) or "\x00"
|
|
|
|
-- first fake
|
|
fake = pattern(fakepat,1,pos-1)
|
|
|
|
if not desync.arg.nofake1 then
|
|
if b_debug then DLOG("fakedsplit: sending fake part 1 (1) : 0-"..(pos-2).." len="..#fake.." : "..hexdump_dlog(fake)) end
|
|
if not rawsend_payload_segmented(desync,fake,0, opts_fake) then return VERDICT_PASS end
|
|
end
|
|
|
|
-- first real
|
|
part = string.sub(data,1,pos-1)
|
|
local seqovl=0
|
|
if desync.arg.seqovl and tonumber(desync.arg.seqovl)>0 then
|
|
seqovl = tonumber(desync.arg.seqovl)
|
|
pat = desync.arg.seqovl_pattern and blob(desync,desync.arg.seqovl_pattern) or "\x00"
|
|
part = pattern(pat,1,seqovl)..part
|
|
end
|
|
if b_debug then DLOG("fakedsplit: sending real part 1 : 0-"..(pos-2).." len="..#part.." seqovl="..seqovl.." : "..hexdump_dlog(part)) end
|
|
if not rawsend_payload_segmented(desync,part,-seqovl, opts_orig) then return VERDICT_PASS end
|
|
|
|
-- first fake again
|
|
if not desync.arg.nofake2 then
|
|
if b_debug then DLOG("fakedsplit: sending fake part 1 (2) : 0-"..(pos-2).." len="..#fake.." : "..hexdump_dlog(fake)) end
|
|
if not rawsend_payload_segmented(desync,fake,0, opts_fake) then return VERDICT_PASS end
|
|
end
|
|
|
|
-- second fake
|
|
fake = pattern(fakepat,pos,#data-pos+1)
|
|
if not desync.arg.nofake3 then
|
|
if b_debug then DLOG("fakedsplit: sending fake part 2 (1) : "..(pos-1).."-"..(#data-1).." len="..#fake.." : "..hexdump_dlog(fake)) end
|
|
if not rawsend_payload_segmented(desync,fake,pos-1, opts_fake) then return VERDICT_PASS end
|
|
end
|
|
|
|
-- second real
|
|
part = string.sub(data,pos)
|
|
if b_debug then DLOG("fakedsplit: sending real part 2 : "..(pos-1).."-"..(#data-1).." len="..#part.." : "..hexdump_dlog(part)) end
|
|
if not rawsend_payload_segmented(desync,part,pos-1, opts_orig) then return VERDICT_PASS end
|
|
|
|
-- second fake again
|
|
if not desync.arg.nofake4 then
|
|
if b_debug then DLOG("fakedsplit: sending fake part 2 (2) : "..(pos-1).."-"..(#data-1).." len="..#fake.." : "..hexdump_dlog(fake)) end
|
|
if not rawsend_payload_segmented(desync,fake,pos-1, opts_fake) then return VERDICT_PASS end
|
|
end
|
|
|
|
replay_drop_set(desync)
|
|
return desync.arg.nodrop and VERDICT_PASS or VERDICT_DROP
|
|
end
|
|
else
|
|
DLOG("fakedsplit: cannot resolve pos '"..desync.arg.pos.."'")
|
|
end
|
|
else
|
|
DLOG("fakedsplit: not acting on further replay pieces")
|
|
end
|
|
-- drop replayed packets if reasm was sent successfully in splitted form
|
|
if replay_drop(desync) then
|
|
return desync.arg.nodrop and VERDICT_PASS or VERDICT_DROP
|
|
end
|
|
end
|
|
end
|
|
|
|
-- nfqws1 : "--dpi-desync=fakeddisorder"
|
|
-- standard args : direction, payload, fooling, ip_id, rawsend, reconstruct. FOOLING AND REPEATS APPLIED ONLY TO FAKES.
|
|
-- arg : pos=<posmarker> - split position marker
|
|
-- arg : nofake1, nofake2, nofake3, nofake4 - do not send individual fakes
|
|
-- arg : pattern=<blob> . fill fake parts with this pattern
|
|
-- arg : seqovl=N . decrease seq number of the second segment by N and fill N bytes with pattern (default - all zero). N must be less than the split pos.
|
|
-- arg : seqovl_pattern=<blob> . override seqovl pattern
|
|
-- arg : blob=<blob> - use this data instead of desync.dis.payload
|
|
-- arg : nodrop - do not drop current dissect
|
|
function fakeddisorder(ctx, desync)
|
|
if not desync.dis.tcp then
|
|
instance_cutoff_shim(ctx, desync)
|
|
return
|
|
end
|
|
direction_cutoff_opposite(ctx, desync)
|
|
-- by default process only outgoing known payloads
|
|
local data = blob_or_def(desync, desync.arg.blob) or desync.reasm_data or desync.dis.payload
|
|
if #data>0 and direction_check(desync) and payload_check(desync) then
|
|
if replay_first(desync) then
|
|
local spos = desync.arg.pos or "2"
|
|
local pos = resolve_pos(data, desync.l7payload, spos)
|
|
if pos then
|
|
if pos == 1 then
|
|
DLOG("multidisorder: split pos resolved to 0. cannot split.")
|
|
else
|
|
if b_debug then DLOG("fakeddisorder: resolved split pos: "..tostring(pos-1)) end
|
|
|
|
-- do not apply fooling to original parts except tcp_ts_up but apply ip_id
|
|
local fake, part, pat
|
|
local opts_orig = {rawsend = rawsend_opts_base(desync), reconstruct = {}, ipfrag = {}, ipid = desync.arg, fooling = {tcp_ts_up = desync.arg.tcp_ts_up}}
|
|
local opts_fake = {rawsend = rawsend_opts(desync), reconstruct = reconstruct_opts(desync), ipfrag = {}, ipid = desync.arg, fooling = desync.arg}
|
|
|
|
fakepat = desync.arg.pattern and blob(desync,desync.arg.pattern) or "\x00"
|
|
|
|
-- second fake
|
|
fake = pattern(fakepat,pos,#data-pos+1)
|
|
if not desync.arg.nofake1 then
|
|
if b_debug then DLOG("fakeddisorder: sending fake part 2 (1) : "..(pos-1).."-"..(#data-1).." len="..#fake.." : "..hexdump_dlog(fake)) end
|
|
if not rawsend_payload_segmented(desync,fake,pos-1, opts_fake) then return VERDICT_PASS end
|
|
end
|
|
|
|
-- second real
|
|
part = string.sub(data,pos)
|
|
local seqovl = 0
|
|
if desync.arg.seqovl then
|
|
seqovl = resolve_pos(data, desync.l7payload, desync.arg.seqovl)
|
|
if seqovl then
|
|
seqovl = seqovl - 1
|
|
if seqovl>=(pos-1) then
|
|
DLOG("fakeddisorder: seqovl cancelled because seqovl "..seqovl.." is not less than the split pos "..(pos-1))
|
|
seqovl = 0
|
|
else
|
|
local pat = desync.arg.seqovl_pattern and blob(desync,desync.arg.seqovl_pattern) or "\x00"
|
|
part = pattern(pat,1,seqovl)..part
|
|
end
|
|
else
|
|
DLOG("fakeddisorder: seqovl cancelled because could not resolve marker '"..desync.arg.seqovl.."'")
|
|
seqovl = 0
|
|
end
|
|
end
|
|
if b_debug then DLOG("fakeddisorder: sending real part 2 : "..(pos-1).."-"..(#data-1).." len="..#part.." seqovl="..seqovl.." : "..hexdump_dlog(part)) end
|
|
if not rawsend_payload_segmented(desync,part,pos-1-seqovl, opts_orig) then return VERDICT_PASS end
|
|
|
|
-- second fake again
|
|
if not desync.arg.nofake2 then
|
|
if b_debug then DLOG("fakeddisorder: sending fake part 2 (2) : "..(pos-1).."-"..(#data-1).." len="..#fake.." : "..hexdump_dlog(fake)) end
|
|
if not rawsend_payload_segmented(desync,fake,pos-1, opts_fake) then return VERDICT_PASS end
|
|
end
|
|
|
|
-- first fake
|
|
fake = pattern(fakepat,1,pos-1)
|
|
if not desync.arg.nofake3 then
|
|
if b_debug then DLOG("fakeddisorder: sending fake part 1 (1) : 0-"..(pos-2).." len="..#fake.." : "..hexdump_dlog(fake)) end
|
|
if not rawsend_payload_segmented(desync,fake,0, opts_fake) then return VERDICT_PASS end
|
|
end
|
|
|
|
-- first real
|
|
part = string.sub(data,1,pos-1)
|
|
if b_debug then DLOG("fakeddisorder: sending real part 1 : 0-"..(pos-2).." len="..#part.." : "..hexdump_dlog(part)) end
|
|
if not rawsend_payload_segmented(desync,part,0, opts_orig) then return VERDICT_PASS end
|
|
|
|
-- first fake again
|
|
if not desync.arg.nofake4 then
|
|
if b_debug then DLOG("fakeddisorder: sending fake part 1 (2) : 0-"..(pos-2).." len="..#fake.." : "..hexdump_dlog(fake)) end
|
|
if not rawsend_payload_segmented(desync,fake,0, opts_fake) then return VERDICT_PASS end
|
|
end
|
|
|
|
replay_drop_set(desync)
|
|
return desync.arg.nodrop and VERDICT_PASS or VERDICT_DROP
|
|
end
|
|
else
|
|
DLOG("fakeddisorder: cannot resolve pos '"..desync.arg.pos.."'")
|
|
end
|
|
else
|
|
DLOG("fakeddisorder: not acting on further replay pieces")
|
|
end
|
|
-- drop replayed packets if reasm was sent successfully in splitted form
|
|
if replay_drop(desync) then
|
|
return desync.arg.nodrop and VERDICT_PASS or VERDICT_DROP
|
|
end
|
|
end
|
|
end
|
|
|
|
-- nfqws1 : not available
|
|
-- standard args : direction, payload, fooling, ip_id, rawsend, reconstruct, ipfrag
|
|
-- arg : pos=<postmarker list> . position marker list. 2 pos required, only 2 first pos used. example : "host,endhost"
|
|
-- 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 pattern
|
|
-- arg : blob=<blob> - use this data instead of desync.dis.payload
|
|
function tcpseg(ctx, desync)
|
|
if not desync.dis.tcp then
|
|
instance_cutoff_shim(ctx, desync)
|
|
return
|
|
end
|
|
direction_cutoff_opposite(ctx, desync)
|
|
if not desync.arg.pos then
|
|
error("tcpseg: no pos specified")
|
|
end
|
|
-- by default process only outgoing known payloads
|
|
local data = blob_or_def(desync, desync.arg.blob) or desync.reasm_data or desync.dis.payload
|
|
if #data>0 and direction_check(desync) and payload_check(desync) then
|
|
if replay_first(desync) then
|
|
if b_debug then DLOG("tcpseg: pos: "..desync.arg.pos) end
|
|
-- always returns 2 positions or nil or causes error
|
|
local pos = resolve_range(data, desync.l7payload, desync.arg.pos)
|
|
if pos then
|
|
-- check debug to save CPU
|
|
if b_debug then DLOG("tcpseg: resolved range: "..table.concat(zero_based_pos(pos)," ")) end
|
|
local part = string.sub(data,pos[1],pos[2])
|
|
local seqovl=0
|
|
if desync.arg.seqovl and tonumber(desync.arg.seqovl)>0 then
|
|
seqovl = tonumber(desync.arg.seqovl)
|
|
local pat = desync.arg.seqovl_pattern and blob(desync,desync.arg.seqovl_pattern) or "\x00"
|
|
part = pattern(pat,1,seqovl)..part
|
|
end
|
|
if b_debug then DLOG("tcpseg: sending "..(pos[1]-1).."-"..(pos[2]-1).." len="..#part.." seqovl="..seqovl.." : "..hexdump_dlog(part)) end
|
|
rawsend_payload_segmented(desync,part,pos[1]-1-seqovl)
|
|
else
|
|
DLOG("tcpseg: range cannot be resolved")
|
|
end
|
|
else
|
|
DLOG("tcpseg: not acting on further replay pieces")
|
|
end
|
|
end
|
|
end
|
|
|
|
-- nfqws1 : "--dpi-desync=udplen"
|
|
-- standard args : direction, payload
|
|
-- arg : min=N . do not act on payloads smaller than N bytes
|
|
-- arg : max=N . do not act on payloads larger than N bytes
|
|
-- arg : increment=N . 2 by default. negative increment shrinks the packet, positive grows it.
|
|
-- arg : pattern=<blob> . used to fill extra bytes when length increases
|
|
-- arg : pattern_offset=N . offset in the pattern. 0 by default
|
|
function udplen(ctx, desync)
|
|
if not desync.dis.udp then
|
|
instance_cutoff_shim(ctx, desync)
|
|
return
|
|
end
|
|
direction_cutoff_opposite(ctx, desync)
|
|
if direction_check(desync) and payload_check(desync) then
|
|
local len = #desync.dis.payload
|
|
if (desync.arg.min and #desync.dis.payload < tonumber(desync.arg.min)) then
|
|
DLOG("udplen: payload size "..len.." is less than the minimum size "..desync.arg.min)
|
|
elseif (desync.arg.max and #desync.dis.payload > tonumber(desync.arg.max)) then
|
|
DLOG("udplen: payload size "..len.." is more than the maximum size "..desync.arg.max)
|
|
else
|
|
local inc = desync.arg.increment and tonumber(desync.arg.increment) or 2
|
|
if inc>0 then
|
|
local pat = desync.arg.pattern and blob(desync,desync.arg.pattern) or "\x00"
|
|
local pat_offset = desync.arg.pattern_offset and (tonumber(desync.arg.pattern_offset)+1) or 1
|
|
desync.dis.payload = desync.dis.payload .. pattern(pat, pat_offset, inc)
|
|
DLOG("udplen: "..len.." => "..#desync.dis.payload)
|
|
return VERDICT_MODIFY
|
|
elseif inc<0 then
|
|
if (len+inc)<1 then
|
|
DLOG("udplen: will not shrink to zero length")
|
|
else
|
|
desync.dis.payload = string.sub(desync.dis.payload,1,len+inc)
|
|
DLOG("udplen: "..len.." => "..#desync.dis.payload)
|
|
end
|
|
return VERDICT_MODIFY
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- nfqws1 : "--dpi-desync=tamper" for dht proto
|
|
-- standard args : direction
|
|
-- arg : dn=N - message starts from "dN". 2 by default
|
|
function dht_dn(ctx, desync)
|
|
if not desync.dis.udp then
|
|
instance_cutoff_shim(ctx, desync)
|
|
return
|
|
end
|
|
direction_cutoff_opposite(ctx, desync)
|
|
if desync.l7payload=="dht" and direction_check(desync) then
|
|
local N = tonumber(desync.arg.dn) or 2
|
|
-- remove "d1" from the start not breaking bencode
|
|
local prefix = "d"..tostring(N)..":"..string.rep("0",N).."1:x"
|
|
desync.dis.payload = prefix..string.sub(desync.dis.payload,2)
|
|
DLOG("dht_dn: tampered dht to start with '"..prefix.."' instead of 'd1:'")
|
|
return VERDICT_MODIFY
|
|
end
|
|
end
|