mirror of
https://github.com/necronicle/z2k.git
synced 2026-04-26 10:31:30 +00:00
fix: comprehensive code review — 40 fixes across 14 files
Critical: CRLF→LF in hostlists (broke nfqws2 matching), die in pipe subshell (errors silently ignored), update_z2k /bin/sh overwrite guard, missing quic_rutracker blob registration, telemetry race condition (multi-process merge). Security: sed injection via user input → grep -vxF, grep -qx → -qxF for domain matching, chmod 777/666 → 755/644 + chown nobody, predictable /tmp/z2k cleanup before mkdir. High: PID path mismatch /opt/var/run → /var/run, zapret2_nat chain in cleanup, orphaned tcpdump on monitor stop, cross-filesystem mv → cp+rm, base.sh re-source clobber fix. Medium: version mismatch, root check for default install path, early exit for help/version, backup/restore dir fix, awk special chars, HTTP RKN failure_detector, youtube_tcp_tcp typo, hostlist warning, kernel module regex anchoring, state eviction + locking in Lua. Dead code removed (~200 lines): check_installation_status, menu_view_strategy, apply_category_strategies, update_init_section, get_init_tcp/udp_params, TEST_DOMAINS, HTTP_STRATEGIES_CONF, check_keenetic_specifics, discord_tcp variable. Added .gitattributes to enforce LF line endings. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
98c87d3c6f
commit
bbd4b5bf1f
14 changed files with 333 additions and 416 deletions
7
.gitattributes
vendored
Normal file
7
.gitattributes
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
# Force LF line endings for all shell scripts and text files deployed to Linux/router
|
||||
*.sh text eol=lf
|
||||
*.lua text eol=lf
|
||||
*.txt text eol=lf
|
||||
*.ini text eol=lf
|
||||
*.conf text eol=lf
|
||||
*.md text eol=lf
|
||||
|
|
@ -33,7 +33,7 @@ is_nfqws2_running() {
|
|||
fi
|
||||
|
||||
# Fallback: check common pidfile locations (our init uses nfqws2_*.pid).
|
||||
for pidfile in /opt/var/run/nfqws2_*.pid /opt/var/run/nfqws2.pid; do
|
||||
for pidfile in /var/run/nfqws2_*.pid /var/run/nfqws2.pid; do
|
||||
[ -f "$pidfile" ] || continue
|
||||
pid="$(cat "$pidfile" 2>/dev/null)"
|
||||
[ -n "$pid" ] || continue
|
||||
|
|
|
|||
|
|
@ -193,16 +193,20 @@ ensure_autocircular_files()
|
|||
{
|
||||
local base="${EXTRA_STRATS_DIR:-$ZAPRET_BASE/extra_strats}/cache/autocircular"
|
||||
mkdir -p "$base" || return 1
|
||||
chmod 777 "$base" 2>/dev/null || true
|
||||
chmod 755 "$base" 2>/dev/null || true
|
||||
chown nobody "$base" 2>/dev/null || true
|
||||
|
||||
[ -f "$base/state.tsv" ] || : > "$base/state.tsv"
|
||||
chmod 666 "$base/state.tsv" 2>/dev/null || true
|
||||
chmod 644 "$base/state.tsv" 2>/dev/null || true
|
||||
chown nobody "$base/state.tsv" 2>/dev/null || true
|
||||
|
||||
[ -f "$base/debug.log" ] || : > "$base/debug.log"
|
||||
chmod 666 "$base/debug.log" 2>/dev/null || true
|
||||
chmod 644 "$base/debug.log" 2>/dev/null || true
|
||||
chown nobody "$base/debug.log" 2>/dev/null || true
|
||||
|
||||
# Optional debug toggle file; keep if present and readable.
|
||||
[ -f "$base/debug.flag" ] && chmod 666 "$base/debug.flag" 2>/dev/null || true
|
||||
[ -f "$base/debug.flag" ] && chmod 644 "$base/debug.flag" 2>/dev/null || true
|
||||
[ -f "$base/debug.flag" ] && chown nobody "$base/debug.flag" 2>/dev/null || true
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
|
|
@ -216,16 +220,20 @@ ensure_traffic_debug_files()
|
|||
{
|
||||
local base="${EXTRA_STRATS_DIR:-$ZAPRET_BASE/extra_strats}/cache/traffic"
|
||||
mkdir -p "$base" || return 1
|
||||
chmod 777 "$base" 2>/dev/null || true
|
||||
chmod 755 "$base" 2>/dev/null || true
|
||||
chown nobody "$base" 2>/dev/null || true
|
||||
|
||||
[ -f "$base/nfqws2.debug.log" ] || : > "$base/nfqws2.debug.log"
|
||||
chmod 666 "$base/nfqws2.debug.log" 2>/dev/null || true
|
||||
chmod 644 "$base/nfqws2.debug.log" 2>/dev/null || true
|
||||
chown nobody "$base/nfqws2.debug.log" 2>/dev/null || true
|
||||
|
||||
[ -f "$base/tcpdump.err.log" ] || : > "$base/tcpdump.err.log"
|
||||
chmod 666 "$base/tcpdump.err.log" 2>/dev/null || true
|
||||
chmod 644 "$base/tcpdump.err.log" 2>/dev/null || true
|
||||
chown nobody "$base/tcpdump.err.log" 2>/dev/null || true
|
||||
|
||||
# Optional toggle file; keep if present and readable.
|
||||
[ -f "$base/debug.flag" ] && chmod 666 "$base/debug.flag" 2>/dev/null || true
|
||||
[ -f "$base/debug.flag" ] && chmod 644 "$base/debug.flag" 2>/dev/null || true
|
||||
[ -f "$base/debug.flag" ] && chown nobody "$base/debug.flag" 2>/dev/null || true
|
||||
}
|
||||
|
||||
traffic_debug_prepare()
|
||||
|
|
@ -373,7 +381,7 @@ traffic_debug_tcpdump_start()
|
|||
tcpdump -i any -nn -s 0 -U -w "$pcap" -C 5 -W 10 "$filter" >/dev/null 2>>"$err" &
|
||||
local pid=$!
|
||||
echo "$pid" > "$Z2K_TRAFFIC_DEBUG_PIDFILE"
|
||||
chmod 666 "$Z2K_TRAFFIC_DEBUG_PIDFILE" 2>/dev/null || true
|
||||
chmod 644 "$Z2K_TRAFFIC_DEBUG_PIDFILE" 2>/dev/null || true
|
||||
echo "traffic debug: tcpdump filter: $filter"
|
||||
echo "traffic debug: tcpdump started (PID $pid)"
|
||||
}
|
||||
|
|
@ -463,6 +471,9 @@ QUIC1_BLOB="$ZAPRET_BASE/files/fake/quic_1.bin"
|
|||
QUIC_TEST_BLOB="$ZAPRET_BASE/files/fake/quic_test_00.bin"
|
||||
[ -s "$QUIC_TEST_BLOB" ] && NFQWS2_OPT_BASE="$NFQWS2_OPT_BASE --blob=quic_test:@$QUIC_TEST_BLOB"
|
||||
|
||||
QUIC_RUTRACKER_BLOB="$ZAPRET_BASE/files/fake/quic_initial_rutracker_org.bin"
|
||||
[ -s "$QUIC_RUTRACKER_BLOB" ] && NFQWS2_OPT_BASE="$NFQWS2_OPT_BASE --blob=quic_rutracker:@$QUIC_RUTRACKER_BLOB"
|
||||
|
||||
TLS_ONETRUST_BLOB="$ZAPRET_BASE/files/fake/tls_clienthello_www_onetrust_com.bin"
|
||||
[ -s "$TLS_ONETRUST_BLOB" ] && NFQWS2_OPT_BASE="$NFQWS2_OPT_BASE --blob=tls_clienthello_www_onetrust_com:@$TLS_ONETRUST_BLOB"
|
||||
|
||||
|
|
|
|||
|
|
@ -212,6 +212,90 @@ local function load_state()
|
|||
f:close()
|
||||
end
|
||||
|
||||
local MAX_ENTRIES_PER_KEY = 500
|
||||
|
||||
local function evict_state_entries(merged)
|
||||
for askey, hosts in pairs(merged) do
|
||||
local count = 0
|
||||
for _ in pairs(hosts) do count = count + 1 end
|
||||
if count > MAX_ENTRIES_PER_KEY then
|
||||
-- Collect entries with timestamps, sort by ts ascending, remove oldest
|
||||
local entries = {}
|
||||
for hostn, rec in pairs(hosts) do
|
||||
table.insert(entries, { hostn = hostn, ts = (rec and rec.ts) or 0 })
|
||||
end
|
||||
table.sort(entries, function(a, b) return a.ts < b.ts end)
|
||||
local to_remove = count - MAX_ENTRIES_PER_KEY
|
||||
for i = 1, to_remove do
|
||||
hosts[entries[i].hostn] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function evict_telemetry_entries(merged)
|
||||
for askey, hosts in pairs(merged) do
|
||||
local count = 0
|
||||
for _ in pairs(hosts) do count = count + 1 end
|
||||
if count > MAX_ENTRIES_PER_KEY then
|
||||
-- Collect entries with total attempts, sort by att ascending, remove lowest
|
||||
local entries = {}
|
||||
for hostn, strats in pairs(hosts) do
|
||||
local att = 0
|
||||
if type(strats) == "table" then
|
||||
for _, rec in pairs(strats) do
|
||||
if rec then
|
||||
att = att + (tonumber(rec.ok) or 0) + (tonumber(rec.fail) or 0)
|
||||
end
|
||||
end
|
||||
end
|
||||
table.insert(entries, { hostn = hostn, att = att })
|
||||
end
|
||||
table.sort(entries, function(a, b) return a.att < b.att end)
|
||||
local to_remove = count - MAX_ENTRIES_PER_KEY
|
||||
for i = 1, to_remove do
|
||||
hosts[entries[i].hostn] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function acquire_lock(path)
|
||||
local lockfile = path .. ".lock"
|
||||
-- Check for stale lock (older than 10 seconds)
|
||||
local lf_ts = io.open(lockfile, "r")
|
||||
if lf_ts then
|
||||
local content = lf_ts:read("*a")
|
||||
lf_ts:close()
|
||||
local lock_time = tonumber(content)
|
||||
if lock_time and ((os.time() or 0) - lock_time) > 10 then
|
||||
os.remove(lockfile)
|
||||
else
|
||||
return nil, lockfile -- lock is fresh, another process holds it
|
||||
end
|
||||
end
|
||||
-- Try exclusive create: "wx" works in Lua 5.3+/glibc; fallback to "w" with
|
||||
-- prior existence check (not perfectly atomic but good enough for our use case).
|
||||
local lf = io.open(lockfile, "wx")
|
||||
if not lf then
|
||||
-- "wx" not supported or file appeared between check and open
|
||||
local recheck = io.open(lockfile, "r")
|
||||
if recheck then
|
||||
recheck:close()
|
||||
return nil, lockfile -- another process created it
|
||||
end
|
||||
lf = io.open(lockfile, "w")
|
||||
end
|
||||
if not lf then return nil, lockfile end
|
||||
lf:write(tostring(os.time() or 0))
|
||||
lf:close()
|
||||
return true, lockfile
|
||||
end
|
||||
|
||||
local function release_lock(lockfile)
|
||||
if lockfile then os.remove(lockfile) end
|
||||
end
|
||||
|
||||
local function write_state()
|
||||
local now = os.time() or 0
|
||||
if now ~= 0 and (now - last_write) < write_interval then
|
||||
|
|
@ -223,6 +307,11 @@ local function write_state()
|
|||
|
||||
local path = choose_state_file_for_write()
|
||||
if not path then return end
|
||||
|
||||
-- Acquire lock to prevent concurrent writes
|
||||
local locked, lockfile = acquire_lock(path)
|
||||
if not locked then return end -- another process is writing, skip this cycle
|
||||
|
||||
local tmp = path .. ".tmp"
|
||||
|
||||
-- Read existing file to merge state (prevents split-brain across processes)
|
||||
|
|
@ -253,8 +342,14 @@ local function write_state()
|
|||
end
|
||||
end
|
||||
|
||||
-- Evict oldest entries if any key exceeds MAX_ENTRIES_PER_KEY
|
||||
evict_state_entries(merged_state)
|
||||
|
||||
local f = io.open(tmp, "w")
|
||||
if not f then return end
|
||||
if not f then
|
||||
release_lock(lockfile)
|
||||
return
|
||||
end
|
||||
|
||||
f:write("# z2k autocircular state (persisted circular nstrategy)\n")
|
||||
f:write("# key\thost\tstrategy\tts\n")
|
||||
|
|
@ -268,7 +363,12 @@ local function write_state()
|
|||
end
|
||||
|
||||
f:close()
|
||||
os.rename(tmp, path)
|
||||
local ok, err = os.rename(tmp, path)
|
||||
if not ok then
|
||||
DLOG("ERROR: rename %s -> %s failed: %s\n", tmp, path, tostring(err))
|
||||
os.remove(tmp)
|
||||
end
|
||||
release_lock(lockfile)
|
||||
end
|
||||
|
||||
local function telemetry_host(askey, hostn, create)
|
||||
|
|
@ -340,13 +440,63 @@ local function write_telemetry()
|
|||
|
||||
local path = choose_telemetry_file_for_write()
|
||||
if not path then return end
|
||||
|
||||
-- Acquire lock to prevent concurrent writes
|
||||
local locked, lockfile = acquire_lock(path)
|
||||
if not locked then return end
|
||||
|
||||
local tmp = path .. ".tmp"
|
||||
|
||||
-- Read existing file to merge telemetry (prevents split-brain across processes)
|
||||
local merged = {}
|
||||
local f_in = io.open(path, "r")
|
||||
if f_in then
|
||||
for line in f_in:lines() do
|
||||
if line ~= "" and not line:match("^%s*#") then
|
||||
local askey, hostn, strat, okv, failv, latv, tsv, cdv =
|
||||
line:match("^([^\t]+)\t([^\t]+)\t([^\t]+)\t([^\t]+)\t([^\t]+)\t([^\t]+)\t([^\t]*)\t?([^\t]*)")
|
||||
local s = tonumber(strat)
|
||||
if askey and hostn and s and s >= 1 then
|
||||
if not merged[askey] then merged[askey] = {} end
|
||||
if not merged[askey][hostn] then merged[askey][hostn] = {} end
|
||||
merged[askey][hostn][s] = {
|
||||
ok = tonumber(okv) or 0,
|
||||
fail = tonumber(failv) or 0,
|
||||
lat = tonumber(latv) or 0,
|
||||
ts = tonumber(tsv) or 0,
|
||||
cooldown_until = tonumber(cdv) or 0,
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
f_in:close()
|
||||
end
|
||||
|
||||
-- Apply our in-memory telemetry over the merged data
|
||||
for askey, hosts in pairs(telemetry) do
|
||||
if not merged[askey] then merged[askey] = {} end
|
||||
for hostn, strats in pairs(hosts) do
|
||||
if not merged[askey][hostn] then merged[askey][hostn] = {} end
|
||||
for s, rec in pairs(strats) do
|
||||
if rec then
|
||||
merged[askey][hostn][s] = rec
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Evict entries with lowest total attempts if any key exceeds MAX_ENTRIES_PER_KEY
|
||||
evict_telemetry_entries(merged)
|
||||
|
||||
local f = io.open(tmp, "w")
|
||||
if not f then return end
|
||||
if not f then
|
||||
release_lock(lockfile)
|
||||
return
|
||||
end
|
||||
|
||||
f:write("# z2k autocircular telemetry\n")
|
||||
f:write("# key\thost\tstrategy\tok\tfail\tlat\tts\tcooldown_until\n")
|
||||
for askey, hosts in pairs(telemetry) do
|
||||
for askey, hosts in pairs(merged) do
|
||||
for hostn, strats in pairs(hosts) do
|
||||
for s, rec in pairs(strats) do
|
||||
if rec then
|
||||
|
|
@ -365,7 +515,12 @@ local function write_telemetry()
|
|||
end
|
||||
end
|
||||
f:close()
|
||||
os.rename(tmp, path)
|
||||
local ok, err = os.rename(tmp, path)
|
||||
if not ok then
|
||||
DLOG("ERROR: rename %s -> %s failed: %s\n", tmp, path, tostring(err))
|
||||
os.remove(tmp)
|
||||
end
|
||||
release_lock(lockfile)
|
||||
end
|
||||
|
||||
local function telemetry_total_attempts(h)
|
||||
|
|
@ -623,7 +778,10 @@ local function policy_seed_strategy(desync, askey, hostn, hrec)
|
|||
end
|
||||
end
|
||||
else
|
||||
-- No telemetry for current strategy yet: let it accumulate data
|
||||
-- No telemetry for current strategy yet: let it accumulate data.
|
||||
-- This is critical for TCP profiles where success telemetry is never
|
||||
-- recorded (incoming packets don't reach circular). Without this,
|
||||
-- UCB would override persisted working strategies on every connection.
|
||||
if st then st.policy_seeded = true end
|
||||
return nil, nil
|
||||
end
|
||||
|
|
|
|||
|
|
@ -73,17 +73,17 @@ choose_capture_iface() {
|
|||
|
||||
ensure_dirs() {
|
||||
mkdir -p "$CACHE_DIR" || return 1
|
||||
chmod 777 "$CACHE_DIR" 2>/dev/null || true
|
||||
chmod 755 "$CACHE_DIR" 2>/dev/null || true
|
||||
}
|
||||
|
||||
init_output_files() {
|
||||
[ -f "$ALL_TSV" ] || echo "# ts\thost\tip\tproto\tport\treason\tdetails" > "$ALL_TSV"
|
||||
[ -f "$TCP_TSV" ] || echo "# ts\thost\tip\tproto\tport\treason\tdetails" > "$TCP_TSV"
|
||||
[ -f "$UDP_TSV" ] || echo "# ts\thost\tip\tproto\tport\treason\tdetails" > "$UDP_TSV"
|
||||
[ -f "$IPMAP_TSV" ] || echo "# ts\tip\thost" > "$IPMAP_TSV"
|
||||
[ -f "$ALL_TSV" ] || printf '# ts\thost\tip\tproto\tport\treason\tdetails\n' > "$ALL_TSV"
|
||||
[ -f "$TCP_TSV" ] || printf '# ts\thost\tip\tproto\tport\treason\tdetails\n' > "$TCP_TSV"
|
||||
[ -f "$UDP_TSV" ] || printf '# ts\thost\tip\tproto\tport\treason\tdetails\n' > "$UDP_TSV"
|
||||
[ -f "$IPMAP_TSV" ] || printf '# ts\tip\thost\n' > "$IPMAP_TSV"
|
||||
[ -f "$ERR_LOG" ] || : > "$ERR_LOG"
|
||||
[ -f "$PARSER_ERR_LOG" ] || : > "$PARSER_ERR_LOG"
|
||||
chmod 666 "$ALL_TSV" "$TCP_TSV" "$UDP_TSV" "$IPMAP_TSV" "$ERR_LOG" "$PARSER_ERR_LOG" 2>/dev/null || true
|
||||
chmod 644 "$ALL_TSV" "$TCP_TSV" "$UDP_TSV" "$IPMAP_TSV" "$ERR_LOG" "$PARSER_ERR_LOG" 2>/dev/null || true
|
||||
}
|
||||
|
||||
collect_ports() {
|
||||
|
|
@ -484,7 +484,7 @@ start_monitor() {
|
|||
echo "# iface: $iface" >> "$ALL_TSV"
|
||||
echo "# filter: $filter" >> "$ALL_TSV"
|
||||
|
||||
"$tcpdump_bin" -i "$iface" -nn -l -tt "$filter" 2>>"$ERR_LOG" | \
|
||||
( "$tcpdump_bin" -i "$iface" -nn -l -tt "$filter" 2>>"$ERR_LOG" | \
|
||||
awk \
|
||||
-v all_out="$ALL_TSV" \
|
||||
-v tcp_out="$TCP_TSV" \
|
||||
|
|
@ -498,10 +498,10 @@ start_monitor() {
|
|||
-v udp_min_out="4" \
|
||||
-v udp_max_in="1" \
|
||||
-v dedupe_sec="60" \
|
||||
-f "$AWK_FILE" 2>>"$PARSER_ERR_LOG" &
|
||||
-f "$AWK_FILE" 2>>"$PARSER_ERR_LOG" ) &
|
||||
|
||||
echo "$!" > "$PID_FILE"
|
||||
chmod 666 "$PID_FILE" 2>/dev/null || true
|
||||
chmod 644 "$PID_FILE" 2>/dev/null || true
|
||||
|
||||
sleep 1
|
||||
if ! running_pid >/dev/null; then
|
||||
|
|
@ -524,9 +524,9 @@ stop_monitor() {
|
|||
return 0
|
||||
fi
|
||||
|
||||
kill "$pid" 2>/dev/null || true
|
||||
kill -- -"$pid" 2>/dev/null || kill "$pid" 2>/dev/null || true
|
||||
sleep 1
|
||||
kill -0 "$pid" 2>/dev/null && kill -9 "$pid" 2>/dev/null || true
|
||||
kill -0 "$pid" 2>/dev/null && { kill -9 -- -"$pid" 2>/dev/null || kill -9 "$pid" 2>/dev/null || true; }
|
||||
rm -f "$PID_FILE" 2>/dev/null || true
|
||||
echo "blocked monitor stopped"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -315,7 +315,7 @@ add_custom_domain() {
|
|||
fi
|
||||
|
||||
# Проверить, не существует ли уже
|
||||
if grep -qx "$domain" "$custom_list" 2>/dev/null; then
|
||||
if grep -qxF "$domain" "$custom_list" 2>/dev/null; then
|
||||
print_warning "Домен уже в списке: $domain"
|
||||
return 0
|
||||
fi
|
||||
|
|
@ -343,8 +343,8 @@ remove_custom_domain() {
|
|||
fi
|
||||
|
||||
# Удалить домен
|
||||
if grep -qx "$domain" "$custom_list"; then
|
||||
grep -vx "$domain" "$custom_list" > "${custom_list}.tmp"
|
||||
if grep -qxF "$domain" "$custom_list"; then
|
||||
grep -vxF "$domain" "$custom_list" > "${custom_list}.tmp"
|
||||
mv "${custom_list}.tmp" "$custom_list"
|
||||
print_success "Удален домен: $domain"
|
||||
else
|
||||
|
|
@ -720,24 +720,23 @@ show_current_config() {
|
|||
print_separator
|
||||
|
||||
# Списки доменов
|
||||
if [ -d "$LISTS_DIR" ]; then
|
||||
print_info "Списки доменов:"
|
||||
for list in discord.txt youtube.txt rkn.txt custom.txt; do
|
||||
if [ -f "${LISTS_DIR}/${list}" ]; then
|
||||
local count
|
||||
count=$(wc -l < "${LISTS_DIR}/${list}" 2>/dev/null || echo "0")
|
||||
printf " %-20s: %s доменов\n" "$list" "$count"
|
||||
fi
|
||||
done
|
||||
local yt_quic_list="${ZAPRET2_DIR}/extra_strats/UDP/YT/List.txt"
|
||||
if [ -f "$yt_quic_list" ]; then
|
||||
local yt_quic_count
|
||||
yt_quic_count=$(wc -l < "$yt_quic_list" 2>/dev/null || echo "0")
|
||||
printf " %-20s: %s доменов\n" "extra_strats/UDP/YT/List.txt" "$yt_quic_count"
|
||||
print_info "Списки доменов:"
|
||||
local _list_path _list_label _list_count
|
||||
for _list_label in "RKN TCP:${ZAPRET2_DIR}/extra_strats/TCP/RKN/List.txt" \
|
||||
"YouTube TCP:${ZAPRET2_DIR}/extra_strats/TCP/YT/List.txt" \
|
||||
"YouTube GV:--hostlist-domains" \
|
||||
"QUIC YouTube:${ZAPRET2_DIR}/extra_strats/UDP/YT/List.txt" \
|
||||
"Discord TCP:${ZAPRET2_DIR}/extra_strats/TCP_Discord.txt" \
|
||||
"Custom:${LISTS_DIR}/custom.txt"; do
|
||||
_list_path="${_list_label#*:}"
|
||||
_list_label="${_list_label%%:*}"
|
||||
if [ "$_list_path" = "--hostlist-domains" ]; then
|
||||
printf " %-25s: googlevideo.com\n" "$_list_label"
|
||||
elif [ -f "$_list_path" ]; then
|
||||
_list_count=$(wc -l < "$_list_path" 2>/dev/null || echo "0")
|
||||
printf " %-25s: %s доменов\n" "$_list_label" "$_list_count"
|
||||
fi
|
||||
else
|
||||
print_info "Списки доменов: не установлены"
|
||||
fi
|
||||
done
|
||||
|
||||
print_separator
|
||||
}
|
||||
|
|
@ -858,10 +857,25 @@ restore_config() {
|
|||
[Yy]|[Yy][Ee][Ss])
|
||||
print_info "Восстановление..."
|
||||
|
||||
# Извлечь backup
|
||||
tar -xzf "$latest_backup" -C "$CONFIG_DIR" 2>/dev/null
|
||||
# Extract to a temp dir first, then move files to their correct locations.
|
||||
# The tar archive contains files from both $CONFIG_DIR and $LISTS_DIR,
|
||||
# but with different -C bases, so we cannot extract directly to one dir.
|
||||
local tmpdir="${CONFIG_DIR}/backups/.restore_tmp"
|
||||
rm -rf "$tmpdir"
|
||||
mkdir -p "$tmpdir"
|
||||
tar -xzf "$latest_backup" -C "$tmpdir" 2>/dev/null
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
# Move config files to CONFIG_DIR
|
||||
for f in strategies.conf current_strategy; do
|
||||
[ -f "${tmpdir}/${f}" ] && mv -f "${tmpdir}/${f}" "${CONFIG_DIR}/${f}"
|
||||
done
|
||||
# Move list files to LISTS_DIR
|
||||
mkdir -p "$LISTS_DIR"
|
||||
for f in custom.txt; do
|
||||
[ -f "${tmpdir}/${f}" ] && mv -f "${tmpdir}/${f}" "${LISTS_DIR}/${f}"
|
||||
done
|
||||
rm -rf "$tmpdir"
|
||||
print_success "Конфигурация восстановлена"
|
||||
|
||||
# Предложить перезапуск
|
||||
|
|
|
|||
|
|
@ -9,6 +9,9 @@
|
|||
generate_nfqws2_opt_from_strategies() {
|
||||
# Генерирует NFQWS2_OPT для config файла на основе текущих стратегий
|
||||
|
||||
# Intentionally hardcoded: this function may be called before utils.sh sets
|
||||
# the global CONFIG_DIR / ZAPRET2_DIR / LISTS_DIR variables, so we use
|
||||
# local copies with known absolute paths.
|
||||
local config_dir="/opt/etc/zapret2"
|
||||
local extra_strats_dir="/opt/zapret2/extra_strats"
|
||||
local lists_dir="/opt/zapret2/lists"
|
||||
|
|
@ -32,15 +35,14 @@ AUSTERUS_OPT
|
|||
fi
|
||||
|
||||
# Загрузить текущие стратегии из категорий
|
||||
local youtube_tcp_tcp=""
|
||||
local youtube_tcp=""
|
||||
local youtube_gv_tcp=""
|
||||
local rkn_tcp=""
|
||||
local quic_udp=""
|
||||
local discord_tcp=""
|
||||
local discord_udp=""
|
||||
# Прочитать стратегии из файлов категорий
|
||||
if [ -f "${extra_strats_dir}/TCP/YT/Strategy.txt" ]; then
|
||||
youtube_tcp_tcp=$(cat "${extra_strats_dir}/TCP/YT/Strategy.txt")
|
||||
youtube_tcp=$(cat "${extra_strats_dir}/TCP/YT/Strategy.txt")
|
||||
fi
|
||||
|
||||
if [ -f "${extra_strats_dir}/TCP/YT_GV/Strategy.txt" ]; then
|
||||
|
|
@ -69,7 +71,7 @@ AUSTERUS_OPT
|
|||
local default_strategy="--filter-tcp=443,2053,2083,2087,2096,8443 --filter-l7=tls --payload=tls_client_hello,http_req,http_reply,unknown,tls_server_hello --out-range=-s34228 --lua-desync=fake:blob=fake_default_tls:repeats=6"
|
||||
|
||||
# Использовать дефолт если стратегия пустая
|
||||
[ -z "$youtube_tcp_tcp" ] && youtube_tcp_tcp="$default_strategy"
|
||||
[ -z "$youtube_tcp" ] && youtube_tcp="$default_strategy"
|
||||
[ -z "$youtube_gv_tcp" ] && youtube_gv_tcp="$default_strategy"
|
||||
[ -z "$rkn_tcp" ] && rkn_tcp="$default_strategy"
|
||||
|
||||
|
|
@ -111,7 +113,7 @@ AUSTERUS_OPT
|
|||
printf '%s' "$out"
|
||||
}
|
||||
|
||||
youtube_tcp_tcp=$(ensure_circular_nld2 "$youtube_tcp_tcp")
|
||||
youtube_tcp=$(ensure_circular_nld2 "$youtube_tcp")
|
||||
youtube_gv_tcp=$(ensure_circular_nld2 "$youtube_gv_tcp")
|
||||
rkn_tcp=$(ensure_circular_nld2 "$rkn_tcp")
|
||||
quic_udp=$(ensure_circular_nld2 "$quic_udp")
|
||||
|
|
@ -138,7 +140,7 @@ AUSTERUS_OPT
|
|||
printf '%s' "$out"
|
||||
}
|
||||
|
||||
youtube_tcp_tcp=$(ensure_circular_failure_detector "$youtube_tcp_tcp")
|
||||
youtube_tcp=$(ensure_circular_failure_detector "$youtube_tcp")
|
||||
youtube_gv_tcp=$(ensure_circular_failure_detector "$youtube_gv_tcp")
|
||||
rkn_tcp=$(ensure_circular_failure_detector "$rkn_tcp")
|
||||
quic_udp=$(ensure_circular_failure_detector "$quic_udp")
|
||||
|
|
@ -197,7 +199,7 @@ AUSTERUS_OPT
|
|||
printf '%s' "$out"
|
||||
}
|
||||
|
||||
youtube_tcp_tcp=$(ensure_circular_payload_empty "$youtube_tcp_tcp")
|
||||
youtube_tcp=$(ensure_circular_payload_empty "$youtube_tcp")
|
||||
youtube_gv_tcp=$(ensure_circular_payload_empty "$youtube_gv_tcp")
|
||||
rkn_tcp=$(ensure_circular_payload_empty "$rkn_tcp")
|
||||
|
||||
|
|
@ -212,6 +214,7 @@ AUSTERUS_OPT
|
|||
nfqws2_opt_lines="$nfqws2_opt_lines$*\\n"
|
||||
else
|
||||
echo "WARN: hostlist file missing or empty: $list_path (skip profile)" 1>&2
|
||||
print_warning "Hostlist missing or empty: $list_path — profile skipped"
|
||||
fi
|
||||
}
|
||||
|
||||
|
|
@ -221,7 +224,7 @@ AUSTERUS_OPT
|
|||
add_hostlist_line "${extra_strats_dir}/TCP/RKN/List.txt" "--hostlist-exclude=${lists_dir}/whitelist.txt $rkn_hostlists $rkn_tcp --new"
|
||||
|
||||
# YouTube TCP
|
||||
add_hostlist_line "${extra_strats_dir}/TCP/YT/List.txt" "--hostlist-exclude=${lists_dir}/whitelist.txt --hostlist=${extra_strats_dir}/TCP/YT/List.txt $youtube_tcp_tcp --new"
|
||||
add_hostlist_line "${extra_strats_dir}/TCP/YT/List.txt" "--hostlist-exclude=${lists_dir}/whitelist.txt --hostlist=${extra_strats_dir}/TCP/YT/List.txt $youtube_tcp --new"
|
||||
|
||||
# YouTube GV (список доменов статичен)
|
||||
nfqws2_opt_lines="$nfqws2_opt_lines--hostlist-exclude=${lists_dir}/whitelist.txt --hostlist-domains=googlevideo.com $youtube_gv_tcp --new\\n"
|
||||
|
|
@ -248,7 +251,7 @@ AUSTERUS_OPT
|
|||
# Strategy 5: fakedsplit at method+2 with badsum
|
||||
# Strategy 6: z4r original (fake 0x0E + tcp_md5 + multisplit host+1)
|
||||
# Strategy 7: fake badsum + multisplit method+2
|
||||
add_hostlist_line "${extra_strats_dir}/TCP/RKN/List.txt" "--filter-tcp=80 --hostlist-exclude=${lists_dir}/whitelist.txt --hostlist=${extra_strats_dir}/TCP/RKN/List.txt --in-range=-s5556 --payload=http_req,empty --lua-desync=circular:fails=2:time=60:reset:key=http_rkn:nld=2 --lua-desync=http_methodeol:payload=http_req:dir=out:strategy=1 --lua-desync=syndata:payload=http_req:dir=out:strategy=2 --lua-desync=multisplit:payload=http_req:dir=out:strategy=2 --lua-desync=hostfakesplit:payload=http_req:dir=out:ip_ttl=2:repeats=1:strategy=3 --lua-desync=fake:payload=http_req:dir=out:blob=fake_default_http:badsum:repeats=1:strategy=4 --lua-desync=fakedsplit:payload=http_req:dir=out:pos=method+2:badsum:strategy=5 --lua-desync=fake:payload=http_req:dir=out:blob=0x0E0E0F0E:tcp_md5:strategy=6 --lua-desync=multisplit:payload=http_req:dir=out:pos=host+1:seqovl=2:strategy=6 --lua-desync=fake:payload=http_req:dir=out:blob=fake_default_http:badsum:repeats=1:strategy=7 --lua-desync=multisplit:payload=http_req:dir=out:pos=method+2:strategy=7 --in-range=x --new"
|
||||
add_hostlist_line "${extra_strats_dir}/TCP/RKN/List.txt" "--filter-tcp=80 --hostlist-exclude=${lists_dir}/whitelist.txt --hostlist=${extra_strats_dir}/TCP/RKN/List.txt --in-range=-s5556 --payload=http_req,empty --lua-desync=circular:fails=2:time=60:reset:key=http_rkn:nld=2:failure_detector=z2k_tls_alert_fatal --lua-desync=http_methodeol:payload=http_req:dir=out:strategy=1 --lua-desync=syndata:payload=http_req:dir=out:strategy=2 --lua-desync=multisplit:payload=http_req:dir=out:strategy=2 --lua-desync=hostfakesplit:payload=http_req:dir=out:ip_ttl=2:repeats=1:strategy=3 --lua-desync=fake:payload=http_req:dir=out:blob=fake_default_http:badsum:repeats=1:strategy=4 --lua-desync=fakedsplit:payload=http_req:dir=out:pos=method+2:badsum:strategy=5 --lua-desync=fake:payload=http_req:dir=out:blob=0x0E0E0F0E:tcp_md5:strategy=6 --lua-desync=multisplit:payload=http_req:dir=out:pos=host+1:seqovl=2:strategy=6 --lua-desync=fake:payload=http_req:dir=out:blob=fake_default_http:badsum:repeats=1:strategy=7 --lua-desync=multisplit:payload=http_req:dir=out:pos=method+2:strategy=7 --in-range=x --new"
|
||||
|
||||
# Catch-all TCP profile for autohostlist failure tracking
|
||||
# Upstream zapret appends --hostlist-auto to the very end of NFQWS2_OPT,
|
||||
|
|
@ -412,7 +415,7 @@ NFQWS2_UDP_PKT_IN="3"
|
|||
# ==============================================================================
|
||||
# This section is auto-generated from z2k strategy database
|
||||
# Each --new separator creates independent profile with own filters and strategy
|
||||
# Order: CF TCP → RKN TCP → YouTube TCP → YouTube GV → QUIC YT → QUIC Cloudflare → QUIC Custom → Discord TCP → Discord UDP → Custom
|
||||
# Order: RKN TCP → YouTube TCP → YouTube GV → QUIC YT → Discord UDP → HTTP RKN → Catch-all TCP
|
||||
# Profiles use explicit hostlists from z2k list files without placeholder expansion.
|
||||
# This avoids mixing with global hostlists from MODE_FILTER.
|
||||
CONFIG
|
||||
|
|
|
|||
|
|
@ -113,15 +113,18 @@ step_update_packages() {
|
|||
printf "\n"
|
||||
|
||||
# 1. Проверка архитектуры системы
|
||||
local sys_arch=$(uname -m)
|
||||
local sys_arch
|
||||
sys_arch=$(uname -m)
|
||||
print_info "Архитектура системы: $sys_arch"
|
||||
|
||||
# 2. Проверка архитектуры Entware
|
||||
if [ -f "/opt/etc/opkg.conf" ]; then
|
||||
local entware_arch=$(grep -m1 "^arch" /opt/etc/opkg.conf | awk '{print $2}')
|
||||
local entware_arch
|
||||
entware_arch=$(grep -m1 "^arch" /opt/etc/opkg.conf | awk '{print $2}')
|
||||
print_info "Архитектура Entware: ${entware_arch:-не определена}"
|
||||
|
||||
local repo_url=$(grep -m1 "^src" /opt/etc/opkg.conf | awk '{print $3}')
|
||||
local repo_url
|
||||
repo_url=$(grep -m1 "^src" /opt/etc/opkg.conf | awk '{print $3}')
|
||||
print_info "Репозиторий: $repo_url"
|
||||
|
||||
# 3. Проверка доступности репозитория
|
||||
|
|
@ -141,7 +144,8 @@ step_update_packages() {
|
|||
print_error "[FAIL] opkg --version падает (Illegal instruction)"
|
||||
print_warning "ПРИЧИНА: opkg установлен для неправильной архитектуры CPU!"
|
||||
elif opkg --version >/dev/null 2>&1; then
|
||||
local opkg_version=$(opkg --version 2>&1 | head -1)
|
||||
local opkg_version
|
||||
opkg_version=$(opkg --version 2>&1 | head -1)
|
||||
print_success "[OK] opkg бинарник запускается: $opkg_version"
|
||||
print_warning "Но 'opkg update' падает - возможно проблема в зависимости или скрипте"
|
||||
else
|
||||
|
|
@ -151,7 +155,8 @@ step_update_packages() {
|
|||
# 5. Проверка файла opkg
|
||||
if command -v file >/dev/null 2>&1; then
|
||||
if [ -f "/opt/bin/opkg" ]; then
|
||||
local opkg_file_info=$(file /opt/bin/opkg 2>&1 | head -1)
|
||||
local opkg_file_info
|
||||
opkg_file_info=$(file /opt/bin/opkg 2>&1 | head -1)
|
||||
print_info "Бинарник opkg: $opkg_file_info"
|
||||
fi
|
||||
fi
|
||||
|
|
@ -445,7 +450,7 @@ unzip
|
|||
|
||||
# Проверить busybox gzip
|
||||
if command -v gzip >/dev/null 2>&1; then
|
||||
if readlink "$(which gzip)" 2>/dev/null | grep -q busybox; then
|
||||
if readlink "$(command -v gzip)" 2>/dev/null | grep -q busybox; then
|
||||
print_info "Обнаружен busybox gzip (медленный, ~3x медленнее GNU)"
|
||||
printf "Установить GNU gzip для ускорения обработки списков? [y/N]: "
|
||||
read -r answer </dev/tty
|
||||
|
|
@ -466,7 +471,7 @@ unzip
|
|||
|
||||
# Проверить busybox sort
|
||||
if command -v sort >/dev/null 2>&1; then
|
||||
if readlink "$(which sort)" 2>/dev/null | grep -q busybox; then
|
||||
if readlink "$(command -v sort)" 2>/dev/null | grep -q busybox; then
|
||||
print_info "Обнаружен busybox sort (медленный, использует много RAM)"
|
||||
printf "Установить GNU sort для ускорения? [y/N]: "
|
||||
read -r answer </dev/tty
|
||||
|
|
@ -490,7 +495,7 @@ unzip
|
|||
}
|
||||
|
||||
# ==============================================================================
|
||||
# ШАГ 3: ЗАГРУЗКА МОДУЛЕЙ ЯДРА
|
||||
# ШАГ 4: ЗАГРУЗКА МОДУЛЕЙ ЯДРА
|
||||
# ==============================================================================
|
||||
|
||||
step_load_kernel_modules() {
|
||||
|
|
@ -507,7 +512,7 @@ step_load_kernel_modules() {
|
|||
}
|
||||
|
||||
# ==============================================================================
|
||||
# ШАГ 4: УСТАНОВКА ZAPRET2 (ИСПОЛЬЗУЯ ОФИЦИАЛЬНЫЙ install_bin.sh)
|
||||
# ШАГ 5: УСТАНОВКА ZAPRET2 (ИСПОЛЬЗУЯ ОФИЦИАЛЬНЫЙ install_bin.sh)
|
||||
# ==============================================================================
|
||||
|
||||
step_build_zapret2() {
|
||||
|
|
@ -678,7 +683,8 @@ step_build_zapret2() {
|
|||
print_info "Вывод --version:"
|
||||
./nfq2/nfqws2 --version 2>&1 | head -5 || true
|
||||
else
|
||||
local version=$(./nfq2/nfqws2 --version 2>&1 | head -1)
|
||||
local version
|
||||
version=$(./nfq2/nfqws2 --version 2>&1 | head -1)
|
||||
print_success "nfqws2 работает: $version"
|
||||
fi
|
||||
|
||||
|
|
@ -689,7 +695,7 @@ step_build_zapret2() {
|
|||
print_info "Установка в $ZAPRET2_DIR..."
|
||||
|
||||
cd "$build_dir" || return 1
|
||||
mv "$release_dir" "$ZAPRET2_DIR" || return 1
|
||||
cp -a "$release_dir" "$ZAPRET2_DIR" && rm -rf "$release_dir" || return 1
|
||||
|
||||
# ВАЖНО: Обновить ZAPRET_BASE на финальный путь (был /tmp/zapret2_build/...)
|
||||
export ZAPRET_BASE="$ZAPRET2_DIR"
|
||||
|
|
@ -735,6 +741,8 @@ step_build_zapret2() {
|
|||
print_info "Copying snapshot domain lists..."
|
||||
mkdir -p "${ZAPRET2_DIR}/files/lists"
|
||||
cp -Rf "${WORK_DIR}/files/lists/"* "${ZAPRET2_DIR}/files/lists/" 2>/dev/null || true
|
||||
# Strip CRLF from list files
|
||||
find "${ZAPRET2_DIR}" -name "*.txt" -path "*/extra_strats/*" -exec sed -i 's/\r$//' {} + 2>/dev/null || true
|
||||
fi
|
||||
# Decompress lua.gz files (if any are shipped by embedded builds)
|
||||
if [ -d "${ZAPRET2_DIR}/lua" ]; then
|
||||
|
|
@ -764,13 +772,16 @@ step_build_zapret2() {
|
|||
mkdir -p "${ZAPRET2_DIR}/lua"
|
||||
mkdir -p "${ZAPRET2_DIR}/extra_strats/cache/orchestra"
|
||||
mkdir -p "${ZAPRET2_DIR}/extra_strats/cache/autocircular"
|
||||
chmod 777 "${ZAPRET2_DIR}/extra_strats/cache/autocircular" 2>/dev/null || true
|
||||
chmod 755 "${ZAPRET2_DIR}/extra_strats/cache/autocircular" 2>/dev/null || true
|
||||
chown nobody "${ZAPRET2_DIR}/extra_strats/cache/autocircular" 2>/dev/null || true
|
||||
: > "${ZAPRET2_DIR}/extra_strats/cache/autocircular/state.tsv" 2>/dev/null || true
|
||||
chmod 666 "${ZAPRET2_DIR}/extra_strats/cache/autocircular/state.tsv" 2>/dev/null || true
|
||||
chmod 644 "${ZAPRET2_DIR}/extra_strats/cache/autocircular/state.tsv" 2>/dev/null || true
|
||||
chown nobody "${ZAPRET2_DIR}/extra_strats/cache/autocircular/state.tsv" 2>/dev/null || true
|
||||
# Debug is opt-in. Keep log file prepared, but do not enable verbose logging by default.
|
||||
rm -f "${ZAPRET2_DIR}/extra_strats/cache/autocircular/debug.flag" 2>/dev/null || true
|
||||
: > "${ZAPRET2_DIR}/extra_strats/cache/autocircular/debug.log" 2>/dev/null || true
|
||||
chmod 666 "${ZAPRET2_DIR}/extra_strats/cache/autocircular/debug.log" 2>/dev/null || true
|
||||
chmod 644 "${ZAPRET2_DIR}/extra_strats/cache/autocircular/debug.log" 2>/dev/null || true
|
||||
chown nobody "${ZAPRET2_DIR}/extra_strats/cache/autocircular/debug.log" 2>/dev/null || true
|
||||
|
||||
if curl -fsSL "https://raw.githubusercontent.com/AloofLibra/zapret4rocket/z2r/orchestra/locked.lua" \
|
||||
-o "${ZAPRET2_DIR}/lua/locked.lua"; then
|
||||
|
|
@ -834,7 +845,7 @@ step_build_zapret2() {
|
|||
}
|
||||
|
||||
# ==============================================================================
|
||||
# ШАГ 5: ПРОВЕРКА УСТАНОВКИ
|
||||
# ШАГ 6: ПРОВЕРКА УСТАНОВКИ
|
||||
# ==============================================================================
|
||||
|
||||
step_verify_installation() {
|
||||
|
|
@ -1044,7 +1055,7 @@ step_download_domain_lists() {
|
|||
}
|
||||
|
||||
# ==============================================================================
|
||||
# ШАГ 7: ОТКЛЮЧЕНИЕ HARDWARE NAT
|
||||
# ШАГ 9: ОТКЛЮЧЕНИЕ HARDWARE NAT
|
||||
# ==============================================================================
|
||||
|
||||
step_disable_hwnat_and_offload() {
|
||||
|
|
@ -1133,7 +1144,13 @@ step_configure_tmpdir() {
|
|||
# Получить объём RAM
|
||||
local ram_mb
|
||||
if [ -f "${ZAPRET2_DIR}/common/base.sh" ]; then
|
||||
# Save overrides before re-sourcing
|
||||
_saved_linux_ipt_avail="$(type linux_ipt_avail 2>/dev/null)"
|
||||
. "${ZAPRET2_DIR}/common/base.sh"
|
||||
# Restore override if it was set
|
||||
if [ -n "$_saved_linux_ipt_avail" ]; then
|
||||
linux_ipt_avail() { true; }
|
||||
fi
|
||||
ram_mb=$(get_ram_mb)
|
||||
else
|
||||
# Fallback: определить RAM вручную
|
||||
|
|
@ -1265,7 +1282,7 @@ step_create_config_and_init() {
|
|||
}
|
||||
|
||||
# ==============================================================================
|
||||
# ШАГ 9: УСТАНОВКА NETFILTER ХУКА
|
||||
# ШАГ 11: УСТАНОВКА NETFILTER ХУКА
|
||||
# ==============================================================================
|
||||
|
||||
step_install_netfilter_hook() {
|
||||
|
|
@ -1357,7 +1374,7 @@ HOOK
|
|||
}
|
||||
|
||||
# ==============================================================================
|
||||
# ШАГ 10: ФИНАЛИЗАЦИЯ
|
||||
# ШАГ 12: ФИНАЛИЗАЦИЯ
|
||||
# ==============================================================================
|
||||
|
||||
step_finalize() {
|
||||
|
|
@ -1496,7 +1513,7 @@ step_finalize() {
|
|||
printf " %-25s: %s\n" "Конфигурация" "$CONFIG_DIR"
|
||||
printf " %-25s: %s\n" "Списки доменов" "$LISTS_DIR"
|
||||
printf " %-25s: %s\n" "Стратегии" "$STRATEGIES_CONF"
|
||||
printf " %-25s: %s\n" "Tools" "$tools_dir"
|
||||
printf " %-25s: %s\n" "Tools" "${ZAPRET2_DIR}/ip2net, ${ZAPRET2_DIR}/mdig"
|
||||
|
||||
# Save local z2k entrypoint for future runs without curl.
|
||||
local local_z2k_script="${ZAPRET2_DIR}/z2k.sh"
|
||||
|
|
|
|||
84
lib/menu.sh
84
lib/menu.sh
|
|
@ -428,74 +428,6 @@ SUBMENU
|
|||
pause
|
||||
}
|
||||
|
||||
# ==============================================================================
|
||||
# ПОДМЕНЮ: ПРОСМОТР СТРАТЕГИИ
|
||||
# ==============================================================================
|
||||
|
||||
menu_view_strategy() {
|
||||
clear_screen
|
||||
print_header "[5] Текущие стратегии"
|
||||
|
||||
if ! is_zapret2_installed; then
|
||||
print_error "zapret2 не установлен"
|
||||
pause
|
||||
return
|
||||
fi
|
||||
|
||||
# Проверить наличие файла с категориями
|
||||
if [ -f "$CATEGORY_STRATEGIES_CONF" ]; then
|
||||
print_info "Стратегии по категориям:"
|
||||
print_separator
|
||||
|
||||
# Прочитать и показать стратегии для каждой категории
|
||||
while IFS=':' read -r category strategy score; do
|
||||
[ -z "$category" ] && continue
|
||||
|
||||
local params
|
||||
local type
|
||||
params=$(get_strategy "$strategy" 2>/dev/null)
|
||||
type=$(get_strategy_type "$strategy" 2>/dev/null)
|
||||
|
||||
printf "\n[%s]\n" "$(echo "$category" | tr '[:lower:]' '[:upper:]')"
|
||||
printf " Стратегия: #%s (оценка: %s/5)\n" "$strategy" "$score"
|
||||
printf " Тип: %s\n" "$type"
|
||||
done < "$CATEGORY_STRATEGIES_CONF"
|
||||
|
||||
print_separator
|
||||
else
|
||||
# Старый режим - одна стратегия
|
||||
local current
|
||||
current=$(get_current_strategy)
|
||||
|
||||
if [ "$current" = "не задана" ] || [ -z "$current" ]; then
|
||||
print_warning "Стратегия не выбрана"
|
||||
print_info "Используется стратегия по умолчанию из init скрипта"
|
||||
else
|
||||
print_info "Текущая стратегия: #$current"
|
||||
print_separator
|
||||
|
||||
local params
|
||||
params=$(get_strategy "$current")
|
||||
local type
|
||||
type=$(get_strategy_type "$current")
|
||||
|
||||
printf "Тип: %s\n\n" "$type"
|
||||
printf "Параметры:\n%s\n" "$params"
|
||||
print_separator
|
||||
fi
|
||||
fi
|
||||
|
||||
# Показать статус сервиса
|
||||
printf "\nСтатус сервиса: %s\n" "$(get_service_status)"
|
||||
|
||||
if is_zapret2_running; then
|
||||
printf "\nПроцессы nfqws2:\n"
|
||||
pgrep -af "nfqws2" 2>/dev/null || print_info "Процессы не найдены"
|
||||
fi
|
||||
|
||||
pause
|
||||
}
|
||||
|
||||
# ==============================================================================
|
||||
# ПОДМЕНЮ: ОБНОВЛЕНИЕ СПИСКОВ
|
||||
# ==============================================================================
|
||||
|
|
@ -561,6 +493,8 @@ SUBMENU
|
|||
restore_config
|
||||
;;
|
||||
3)
|
||||
print_warning "Это сбросит всю конфигурацию к значениям по умолчанию!"
|
||||
confirm "Вы уверены?" "N" || { pause; return; }
|
||||
reset_config
|
||||
;;
|
||||
[Bb])
|
||||
|
|
@ -1065,6 +999,7 @@ EOF
|
|||
print_success "Файл whitelist создан: $whitelist_file"
|
||||
fi
|
||||
|
||||
while true; do
|
||||
print_separator
|
||||
|
||||
cat <<'INFO'
|
||||
|
|
@ -1125,14 +1060,14 @@ INFO
|
|||
if ! echo "$new_domain" | grep -qE '^[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'; then
|
||||
print_error "Неверный формат домена: $new_domain"
|
||||
pause
|
||||
return 1
|
||||
continue
|
||||
fi
|
||||
|
||||
# Проверить дубликаты
|
||||
if grep -qx "$new_domain" "$whitelist_file"; then
|
||||
if grep -qxF "$new_domain" "$whitelist_file"; then
|
||||
print_warning "Домен $new_domain уже в whitelist"
|
||||
pause
|
||||
return 0
|
||||
continue
|
||||
fi
|
||||
|
||||
# Добавить домен
|
||||
|
|
@ -1155,14 +1090,14 @@ INFO
|
|||
read_input del_domain
|
||||
|
||||
# Проверить наличие
|
||||
if ! grep -qx "$del_domain" "$whitelist_file"; then
|
||||
if ! grep -qxF "$del_domain" "$whitelist_file"; then
|
||||
print_error "Домен $del_domain не найден в whitelist"
|
||||
pause
|
||||
return 1
|
||||
continue
|
||||
fi
|
||||
|
||||
# Удалить домен
|
||||
sed -i "/^${del_domain}$/d" "$whitelist_file"
|
||||
grep -vxF "$del_domain" "$whitelist_file" > "${whitelist_file}.tmp" && mv "${whitelist_file}.tmp" "$whitelist_file"
|
||||
print_success "Домен $del_domain удален из whitelist"
|
||||
print_separator
|
||||
|
||||
|
|
@ -1184,6 +1119,7 @@ INFO
|
|||
pause
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
# ==============================================================================
|
||||
|
|
|
|||
|
|
@ -3,19 +3,6 @@
|
|||
# Парсинг, тестирование, применение стратегий из strats_new2.txt
|
||||
# QUIC/UDP стратегии берутся из quic_strats.ini
|
||||
|
||||
# ==============================================================================
|
||||
# КОНСТАНТЫ ДЛЯ СТРАТЕГИЙ
|
||||
# ==============================================================================
|
||||
|
||||
# Домены для тестирования стратегий
|
||||
TEST_DOMAINS="
|
||||
http://rutracker.org
|
||||
https://rutracker.org
|
||||
https://www.youtube.com
|
||||
https://discord.com
|
||||
https://googlevideo.com
|
||||
"
|
||||
|
||||
# ==============================================================================
|
||||
# РАБОТА С ФАЙЛАМИ СТРАТЕГИЙ ПО КАТЕГОРИЯМ (CONFIG-DRIVEN АРХИТЕКТУРА)
|
||||
# ==============================================================================
|
||||
|
|
@ -446,11 +433,7 @@ apply_strategy() {
|
|||
|
||||
# Построить полные TCP параметры
|
||||
local tcp_params
|
||||
if [ "$type" = "http" ]; then
|
||||
tcp_params=$(build_http_profile_params "$params")
|
||||
else
|
||||
tcp_params=$(build_tls_profile_params "$params")
|
||||
fi
|
||||
tcp_params=$(build_tls_profile_params "$params")
|
||||
|
||||
# Получить текущие QUIC параметры
|
||||
local udp_params
|
||||
|
|
@ -723,6 +706,7 @@ EOF
|
|||
local args=""
|
||||
|
||||
while IFS= read -r line; do
|
||||
line=$(printf '%s' "$line" | sed 's/\r$//')
|
||||
line=$(echo "$line" | sed 's/^[[:space:]]*//' | sed 's/[[:space:]]*$//')
|
||||
[ -z "$line" ] && continue
|
||||
case "$line" in
|
||||
|
|
@ -1232,148 +1216,6 @@ test_strategy_range() {
|
|||
fi
|
||||
}
|
||||
|
||||
# ==============================================================================
|
||||
# ПРИМЕНЕНИЕ СТРАТЕГИЙ ПО КАТЕГОРИЯМ
|
||||
# ==============================================================================
|
||||
|
||||
# Применить разные стратегии для разных категорий
|
||||
# Параметр: строка вида "youtube:4:5 discord:7:4 custom:11:3"
|
||||
apply_category_strategies() {
|
||||
local category_strategies=$1
|
||||
local init_script="${INIT_SCRIPT:-/opt/etc/init.d/S99zapret2}"
|
||||
|
||||
if [ -z "$category_strategies" ]; then
|
||||
print_error "Не указаны стратегии для категорий"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [ ! -f "$init_script" ]; then
|
||||
print_error "Init скрипт не найден: $init_script"
|
||||
return 1
|
||||
fi
|
||||
|
||||
print_info "Применение стратегий по категориям..."
|
||||
|
||||
# Обработать каждую категорию
|
||||
for entry in $category_strategies; do
|
||||
local category=$(echo "$entry" | cut -d: -f1)
|
||||
local strategy_num=$(echo "$entry" | cut -d: -f2)
|
||||
local score=$(echo "$entry" | cut -d: -f3)
|
||||
|
||||
print_info " $category -> стратегия #$strategy_num (оценка: $score/5)"
|
||||
|
||||
# Получить параметры стратегии
|
||||
local params
|
||||
params=$(get_strategy "$strategy_num")
|
||||
|
||||
if [ -z "$params" ]; then
|
||||
print_warning "Стратегия #$strategy_num не найдена, пропускаем $category"
|
||||
continue
|
||||
fi
|
||||
|
||||
# Конвертировать в TCP/UDP профили
|
||||
local tcp_params
|
||||
local udp_params
|
||||
|
||||
# Определить тип стратегии
|
||||
local type
|
||||
type=$(get_strategy_type "$strategy_num")
|
||||
|
||||
if [ "$type" = "https" ]; then
|
||||
tcp_params="--filter-tcp=443 --filter-l7=tls --payload=tls_client_hello ${params}"
|
||||
udp_params=""
|
||||
else
|
||||
tcp_params="--filter-tcp=80,443 --filter-l7=http ${params}"
|
||||
udp_params=""
|
||||
fi
|
||||
|
||||
# Обновить маркеры в init скрипте
|
||||
case "$category" in
|
||||
youtube)
|
||||
update_init_section "YOUTUBE" "$tcp_params" "$udp_params" "$init_script"
|
||||
;;
|
||||
discord)
|
||||
update_init_section "DISCORD" "$tcp_params" "$udp_params" "$init_script"
|
||||
;;
|
||||
custom)
|
||||
update_init_section "CUSTOM" "$tcp_params" "$udp_params" "$init_script"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
print_success "Стратегии применены к init скрипту"
|
||||
|
||||
# Перезапустить сервис
|
||||
print_info "Перезапуск сервиса..."
|
||||
"$init_script" restart >/dev/null 2>&1
|
||||
|
||||
sleep 2
|
||||
|
||||
if is_zapret2_running; then
|
||||
print_success "Сервис перезапущен с новыми стратегиями"
|
||||
return 0
|
||||
else
|
||||
print_warning "Сервис не запустился, проверьте логи"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Обновить секцию в init скрипте для конкретной категории
|
||||
update_init_section() {
|
||||
local marker=$1
|
||||
local tcp_params=$2
|
||||
local udp_params=$3
|
||||
local init_script=$4
|
||||
|
||||
local start_marker="${marker}_MARKER_START"
|
||||
local end_marker="${marker}_MARKER_END"
|
||||
|
||||
# Создать временный файл
|
||||
local temp_file="${init_script}.tmp"
|
||||
|
||||
# Флаг - внутри ли мы секции для замены
|
||||
local inside_section=0
|
||||
local found_section=0
|
||||
|
||||
while IFS= read -r line; do
|
||||
if echo "$line" | grep -q "# ${start_marker}"; then
|
||||
# Начало секции - записать маркер и новые параметры
|
||||
echo "$line"
|
||||
echo "${marker}_TCP=\"${tcp_params}\""
|
||||
echo "${marker}_UDP=\"${udp_params}\""
|
||||
inside_section=1
|
||||
found_section=1
|
||||
elif echo "$line" | grep -q "# ${end_marker}"; then
|
||||
# Конец секции - записать маркер и выйти из режима
|
||||
echo "$line"
|
||||
inside_section=0
|
||||
elif [ "$inside_section" -eq 0 ]; then
|
||||
# Вне секции - просто копировать
|
||||
echo "$line"
|
||||
fi
|
||||
# Внутри секции - пропускать старые строки (кроме маркеров)
|
||||
done < "$init_script" > "$temp_file"
|
||||
|
||||
# Если секции не было в файле - добавить в конец
|
||||
if [ "$found_section" -eq 0 ]; then
|
||||
{
|
||||
echo ""
|
||||
echo "# ${start_marker}"
|
||||
echo "${marker}_TCP=\"${tcp_params}\""
|
||||
echo "${marker}_UDP=\"${udp_params}\""
|
||||
echo "# ${end_marker}"
|
||||
} >> "$temp_file"
|
||||
fi
|
||||
|
||||
# Заменить init скрипт
|
||||
mv "$temp_file" "$init_script" || {
|
||||
print_error "Не удалось обновить init скрипт"
|
||||
return 1
|
||||
}
|
||||
|
||||
chmod +x "$init_script"
|
||||
}
|
||||
|
||||
# ==============================================================================
|
||||
# АВТОТЕСТ QUIC СТРАТЕГИЙ
|
||||
# ==============================================================================
|
||||
|
|
@ -2188,33 +2030,6 @@ run_blockcheck_http() {
|
|||
return 0
|
||||
}
|
||||
|
||||
get_init_tcp_params() {
|
||||
local marker=$1
|
||||
local init_script=$2
|
||||
|
||||
if [ ! -f "$init_script" ]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
local line
|
||||
line=$(grep "^${marker}_TCP=" "$init_script" 2>/dev/null | head -n 1)
|
||||
echo "$line" | sed "s/^${marker}_TCP=\"//" | sed 's/\"$//'
|
||||
}
|
||||
|
||||
# Получить текущие UDP параметры из init скрипта для секции
|
||||
get_init_udp_params() {
|
||||
local marker=$1
|
||||
local init_script=$2
|
||||
|
||||
if [ ! -f "$init_script" ]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
local line
|
||||
line=$(grep "^${marker}_UDP=" "$init_script" 2>/dev/null | head -n 1)
|
||||
echo "$line" | sed "s/^${marker}_UDP=\"//" | sed 's/\"$//'
|
||||
}
|
||||
|
||||
# Применить разные стратегии для YouTube TCP, YouTube GV, RKN (Z4R метод)
|
||||
# Параметры: номера стратегий для каждой категории
|
||||
apply_category_strategies_v2() {
|
||||
|
|
|
|||
|
|
@ -70,59 +70,3 @@ init_system_vars() {
|
|||
|
||||
return 0
|
||||
}
|
||||
|
||||
# ==============================================================================
|
||||
# ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ
|
||||
# ==============================================================================
|
||||
|
||||
# Определить является ли система Keenetic
|
||||
is_keenetic() {
|
||||
# Проверка наличия Keenetic-специфичных файлов
|
||||
[ -f /opt/etc/init.d/rc.func ] && return 0
|
||||
|
||||
# Проверка на NDM (Keenetic firmware)
|
||||
[ -d /opt/etc/ndm ] && return 0
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
# Получить версию Keenetic firmware (если доступно)
|
||||
get_keenetic_version() {
|
||||
if [ -f /etc/os-release ]; then
|
||||
grep VERSION_ID /etc/os-release | cut -d'=' -f2 | tr -d '"'
|
||||
elif command -v ndmc >/dev/null 2>&1; then
|
||||
ndmc -c "show version" 2>/dev/null | grep "release:" | awk '{print $2}'
|
||||
else
|
||||
echo "unknown"
|
||||
fi
|
||||
}
|
||||
|
||||
# ==============================================================================
|
||||
# KEENETIC СПЕЦИФИЧНЫЕ ПРОВЕРКИ
|
||||
# ==============================================================================
|
||||
|
||||
check_keenetic_specifics() {
|
||||
if is_keenetic; then
|
||||
print_info "Обнаружен роутер Keenetic"
|
||||
|
||||
local fw_version
|
||||
fw_version=$(get_keenetic_version)
|
||||
[ "$fw_version" != "unknown" ] && print_info "Firmware версия: $fw_version"
|
||||
|
||||
# Проверить наличие NDM hooks
|
||||
if [ -d /opt/etc/ndm ]; then
|
||||
print_info "NDM hooks доступны"
|
||||
fi
|
||||
|
||||
# Проверить fastnat
|
||||
if [ -f /sys/kernel/fastnat/mode ]; then
|
||||
local fastnat_mode
|
||||
fastnat_mode=$(cat /sys/kernel/fastnat/mode 2>/dev/null || echo "unknown")
|
||||
print_info "Fastnat mode: $fastnat_mode"
|
||||
fi
|
||||
|
||||
return 0
|
||||
fi
|
||||
|
||||
return 1
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,7 +40,6 @@ Z2R_BASE_URL="https://raw.githubusercontent.com/AloofLibra/zapret4rocket/z2r"
|
|||
|
||||
# Файлы конфигурации
|
||||
STRATEGIES_CONF="${CONFIG_DIR}/strategies.conf"
|
||||
HTTP_STRATEGIES_CONF="${CONFIG_DIR}/http_strategies.conf"
|
||||
CURRENT_STRATEGY_FILE="${CONFIG_DIR}/current_strategy"
|
||||
QUIC_STRATEGIES_CONF="${CONFIG_DIR}/quic_strategies.conf"
|
||||
QUIC_STRATEGY_FILE="${CONFIG_DIR}/quic_strategy.conf"
|
||||
|
|
@ -387,7 +386,7 @@ verify_binary() {
|
|||
check_kernel_module() {
|
||||
local module=$1
|
||||
|
||||
if lsmod | grep -q "^${module}"; then
|
||||
if lsmod | grep -q "^${module} "; then
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
|
|
|
|||
49
z2k.sh
49
z2k.sh
|
|
@ -243,7 +243,7 @@ quic_5.bin
|
|||
quic_test_00.bin
|
||||
"
|
||||
|
||||
echo "$files" | while read -r file; do
|
||||
while read -r file; do
|
||||
[ -z "$file" ] && continue
|
||||
local url="${GITHUB_RAW}/files/fake/${file}"
|
||||
local output="${fake_dir}/${file}"
|
||||
|
|
@ -252,7 +252,9 @@ quic_test_00.bin
|
|||
else
|
||||
die "Ошибка загрузки files/fake/${file}"
|
||||
fi
|
||||
done
|
||||
done <<EOF
|
||||
$files
|
||||
EOF
|
||||
}
|
||||
|
||||
download_init_script() {
|
||||
|
|
@ -320,7 +322,7 @@ extra_strats/TCP/RKN/Discord.txt
|
|||
extra_strats/UDP/YT/List.txt
|
||||
"
|
||||
|
||||
echo "$list_files" | while read -r list_file; do
|
||||
while read -r list_file; do
|
||||
[ -z "$list_file" ] && continue
|
||||
local list_url="${GITHUB_RAW}/files/lists/${list_file}"
|
||||
local list_out="${lists_dir}/${list_file}"
|
||||
|
|
@ -331,7 +333,9 @@ extra_strats/UDP/YT/List.txt
|
|||
else
|
||||
die "Ошибка загрузки files/lists/${list_file}"
|
||||
fi
|
||||
done
|
||||
done <<EOF
|
||||
$list_files
|
||||
EOF
|
||||
}
|
||||
|
||||
generate_strategies_database() {
|
||||
|
|
@ -365,10 +369,10 @@ generate_strategies_database() {
|
|||
show_welcome() {
|
||||
clear_screen
|
||||
|
||||
cat <<'EOF'
|
||||
cat <<EOF
|
||||
+===================================================+
|
||||
| z2k - Zapret2 для Keenetic (BETA) |
|
||||
| Версия 2.0.0 |
|
||||
| Версия $Z2K_VERSION |
|
||||
+===================================================+
|
||||
|
||||
[INFO] Проект в активной разработке. Статус: beta.
|
||||
|
|
@ -380,18 +384,6 @@ EOF
|
|||
print_info "Инициализация..."
|
||||
}
|
||||
|
||||
check_installation_status() {
|
||||
if is_zapret2_installed; then
|
||||
print_info "zapret2 уже установлен"
|
||||
print_info "Статус сервиса: $(get_service_status)"
|
||||
print_info "Текущая стратегия: #$(get_current_strategy)"
|
||||
return 0
|
||||
else
|
||||
print_info "zapret2 не установлен"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
prompt_install_or_menu() {
|
||||
printf "\n"
|
||||
|
||||
|
|
@ -401,6 +393,7 @@ prompt_install_or_menu() {
|
|||
show_main_menu
|
||||
else
|
||||
print_info "zapret2 не установлен - запускаю установку..."
|
||||
check_root || die "Требуются права root для установки"
|
||||
run_full_install
|
||||
fi
|
||||
}
|
||||
|
|
@ -503,6 +496,13 @@ update_z2k() {
|
|||
local current_script
|
||||
current_script=$(readlink -f "$0")
|
||||
|
||||
case "$current_script" in
|
||||
*/sh|*/bash|*/ash|*/dash)
|
||||
print_error "Cannot self-update: script was run via pipe. Please download and run directly."
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
|
||||
print_info "Текущая версия: $Z2K_VERSION"
|
||||
print_info "Загрузка последней версии..."
|
||||
|
||||
|
|
@ -552,6 +552,18 @@ update_z2k() {
|
|||
# ==============================================================================
|
||||
|
||||
main() {
|
||||
# Early-exit for help/version — no downloads needed
|
||||
case "$1" in
|
||||
help|h|-h|--help)
|
||||
show_help
|
||||
exit 0
|
||||
;;
|
||||
version|v|--version)
|
||||
echo "z2k v${Z2K_VERSION}"
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
|
||||
# Показать приветствие
|
||||
show_welcome
|
||||
|
||||
|
|
@ -559,6 +571,7 @@ main() {
|
|||
check_environment
|
||||
|
||||
# Инициализировать рабочую директорию
|
||||
rm -rf "$WORK_DIR"
|
||||
mkdir -p "$WORK_DIR" "$LIB_DIR"
|
||||
|
||||
# Установить обработчики сигналов (будет переопределено после загрузки utils.sh)
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ log_info "Очистка iptables правил zapret/zapret2..."
|
|||
|
||||
# Список известных цепочек zapret/zapret2
|
||||
chains_mangle="ZAPRET ZAPRET2 z2k_connmark"
|
||||
chains_nat="z2k_masq_fix"
|
||||
chains_nat="z2k_masq_fix zapret2_nat"
|
||||
chains_raw="z2k_dpi_rst"
|
||||
|
||||
# Удаление из mangle
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue