This commit is contained in:
StressOzz authored and GitHub committed 2026-10-24 21:52:04 +05:00
1 parent 7982ba3877
commit 96743a4b11
1 file changed
+76 -32
+76 -32
View File
@@ -1,6 +1,6 @@
#!/bin/sh
read -r _ _ ZM_NEW_VER <<'ZM_VERSION_EOF'
# Version: 4.41
# Version: 4.43
ZM_VERSION_EOF
set -e
@@ -3214,6 +3214,23 @@ _hosts_append() {
echo "$1" >> "$HOSTS_FILE"
}
# Дописывает в hosts строки из stdin, которых там ещё нет, — за один проход по файлу. Раньше каждая строка
# блока проверялась отдельным проходом: «Финские IP для Discord» — это 200 строк, и с большим hosts
# (GeoHide, dns.malw.link) включение шло дольше, чем ждёт панель, — «hosts_toggle: нет данных»
_hosts_add_lines() {
local list="$HOSTS_FILE.zmadd" add="$HOSTS_FILE.zmnew"
cat > "$list"
[ -f "$HOSTS_FILE" ] || : > "$HOSTS_FILE"
awk -v hf="$HOSTS_FILE" 'function n(s) { gsub(/\r/, "", s); gsub(/[ \t]+/, " ", s); sub(/^ /, "", s); sub(/ $/, "", s); return s }
BEGIN { while ((getline l < hf) > 0) have[n(l)] = 1; close(hf) }
{ k = n($0); if (k == "" || (k in have)) next; have[k] = 1; print }' "$list" > "$add"
if [ -s "$add" ]; then
[ -s "$HOSTS_FILE" ] && [ -n "$(tail -c 1 "$HOSTS_FILE" 2>/dev/null)" ] && echo >> "$HOSTS_FILE"
cat "$add" >> "$HOSTS_FILE"
fi
rm -f "$list" "$add"
}
_hosts_has_line() {
awk -v want="$1" 'function n(s) { gsub(/\r/, "", s); gsub(/[ \t]+/, " ", s); sub(/^ /, "", s); sub(/ $/, "", s); return s }
BEGIN { want = n(want) } n($0) == want { f = 1; exit } END { exit !f }' "$HOSTS_FILE" 2>/dev/null
@@ -3247,9 +3264,7 @@ hosts_toggle() {
if [ "$enabled" = "true" ]; then
printf '%s\n' "$content" | _hosts_drop_lines
else
while IFS= read -r line; do [ -z "$line" ] && continue; _hosts_has_line "$line" || _hosts_append "$line"; done <<-EOF
$content
EOF
printf '%s\n' "$content" | _hosts_add_lines
fi
/etc/init.d/dnsmasq restart >/dev/null 2>&1
printf '{"ok":true,"block":"%s","enabled":%s}\n' "$(esc "$block")" "$([ "$enabled" = "true" ] && echo false || echo true)"
@@ -21165,7 +21180,7 @@ _fk_ui_dir() {
ui="$(jsonfilter -i "$c" -e '@.experimental.clash_api.external_ui' 2>/dev/null)"
[ -n "$ui" ] || return 1
case "$ui" in /*) [ -d "$ui" ] && { echo "$ui"; return 0; }; return 1 ;; esac
pid="$(pidof sing-box 2>/dev/null | awk '{ print $1 }')"
pid="$(pidof sing-box sing-box-core 2>/dev/null | awk '{ print $1 }')"
base=""
[ -n "$pid" ] && base="$(readlink "/proc/$pid/cwd" 2>/dev/null)"
for base in "$base" /etc/sing-box /tmp/sing-box /var/run/sing-box; do
@@ -21237,7 +21252,13 @@ _fk_say() { echo "==> $*"; }
_fk_installed() { [ -x /usr/bin/netshift ] && [ -f /usr/lib/netshift/constants.sh ]; }
_fk_version() { sed -n 's/^NETSHIFT_VERSION="\([^"]*\)".*/\1/p' /usr/lib/netshift/constants.sh 2>/dev/null | head -n1; }
_fk_enabled() { ls /etc/rc.d/S*netshift >/dev/null 2>&1; }
_fk_up() { nft list table inet "$FK_NFT" >/dev/null 2>&1 && pidof sing-box >/dev/null 2>&1; }
# sing-box Forkozz работает? Сжатая (UPX) сборка расширенного облегчённого sing-box запускается не как
# sing-box, а как /usr/libexec/sing-box-core (/usr/bin/sing-box там — обёртка, которая передаёт ему
# управление). Раньше панель искала только «sing-box» и на таких роутерах считала, что Forkozz не
# работает: ждала по 90 секунд, писала «не поднялся», а сторож перезапускал рабочий Forkozz
FK_SB_PROCS="sing-box sing-box-core"
_fk_sb_alive() { pidof $FK_SB_PROCS >/dev/null 2>&1; }
_fk_up() { nft list table inet "$FK_NFT" >/dev/null 2>&1 && _fk_sb_alive; }
FK_SERIAL_DIR="/var/run/netshift-zm-serial"
_fk_engine_busy() {
local p
@@ -21727,10 +21748,10 @@ _fk_sb_purge() {
/etc/init.d/sing-box stop >/dev/null 2>&1
/etc/init.d/sing-box disable >/dev/null 2>&1
fi
if pidof sing-box >/dev/null 2>&1; then
killall sing-box >/dev/null 2>&1
if _fk_sb_alive; then
killall $FK_SB_PROCS >/dev/null 2>&1
sleep 1
killall -9 sing-box >/dev/null 2>&1
killall -9 $FK_SB_PROCS >/dev/null 2>&1
fi
ZM_PKG_QUIET=1
for p in $had; do _zm_pkg_purge "$p"; done
@@ -22471,12 +22492,20 @@ _fk_servers_report() {
_fk_check_live() {
local j
if pidof sing-box >/dev/null 2>&1; then echo " ✓ sing-box работает"; else echo " ✗ sing-box не запущен"; fi
if _fk_sb_alive; then echo " ✓ sing-box работает"; else echo " ✗ sing-box не запущен"; fi
if nft list table inet "$FK_NFT" >/dev/null 2>&1; then echo " ✓ Правила перехвата трафика на месте"; else echo " ✗ Правил перехвата трафика нет"; fi
j="$(_zm_run 20 /usr/bin/netshift check_fakeip 2>/dev/null)"
# сразу после запуска или применения настроек DNS sing-box поднимается не мгновенно — даём ему до
# ~15 секунд, а не пишем «не получил адрес» после первой же попытки
local t=0
while :; do
j="$(_zm_run 20 /usr/bin/netshift check_fakeip 2>/dev/null)"
case "$j" in *'"fakeip": true'*|*'"fakeip":true'*) break ;; esac
t=$((t + 1)); [ "$t" -ge 5 ] && break
sleep 3
done
case "$j" in
*'"fakeip": true'*|*'"fakeip":true'*) echo " ✓ DNS отдаёт адреса Forkozz — выбранные сервисы идут через туннель" ;;
*) echo " ! Проверочный домен не получил адрес Forkozz — DNS может ещё прогреваться" ;;
*) echo " ! Проверочный домен не получил адрес Forkozz — нажмите «Проверить» через минуту; если так и останется, перезапустите Forkozz" ;;
esac
}
@@ -22549,7 +22578,8 @@ do_fk_install() {
fi
sed -i "s#__COMPILED_VERSION_VARIABLE__#$tag#g" "$src/usr/lib/constants.sh"
grep -q "^NETSHIFT_VERSION=\"$tag\"" "$src/usr/lib/constants.sh" || { rm -rf "$tmp"; echo "ОШИБКА: не удалось прописать версию движка Forkozz"; return 1; }
_fk_say "Источники: GitHub и репозиторий OpenWrt, без зеркал"
if [ -n "$(gh_mirrors)" ]; then _fk_say "Источники: GitHub (если не отвечает — через зеркала) и репозиторий OpenWrt"
else _fk_say "Источники: GitHub и репозиторий OpenWrt, без зеркал"; fi
if _fk_foreign; then
_fk_say "Убираем отдельный пакет движка с LuCI, настройки оставляем"
@@ -22728,7 +22758,7 @@ _fk_sb_park() {
fi
_fk_say "Оставляем sing-box $(_fk_sb_ver) ($(_fk_sb_name "$(_fk_sb_var)"))"
[ -x /etc/init.d/sing-box ] && { /etc/init.d/sing-box stop >/dev/null 2>&1; /etc/init.d/sing-box disable >/dev/null 2>&1; }
pidof sing-box >/dev/null 2>&1 && { killall sing-box >/dev/null 2>&1; sleep 1; killall -9 sing-box >/dev/null 2>&1; }
_fk_sb_alive && { killall $FK_SB_PROCS >/dev/null 2>&1; sleep 1; killall -9 $FK_SB_PROCS >/dev/null 2>&1; }
rm -rf /tmp/sing-box 2>/dev/null
echo " ✓ Он остановлен и убран из автозапуска — без Forkozz ему нечего делать. Удалить его можно на вкладке Forkozz"
}
@@ -22753,9 +22783,9 @@ do_fk_remove() {
/etc/init.d/netshift disable >/dev/null 2>&1
_zm_run 90 $FK_REL /etc/init.d/netshift stop >/dev/null 2>&1
fi
if pidof sing-box >/dev/null 2>&1 && [ -z "$(_fk_sb_users)" ]; then
if _fk_sb_alive && [ -z "$(_fk_sb_users)" ]; then
[ -x /etc/init.d/sing-box ] && /etc/init.d/sing-box stop >/dev/null 2>&1
pidof sing-box >/dev/null 2>&1 && { killall sing-box >/dev/null 2>&1; sleep 1; killall -9 sing-box >/dev/null 2>&1; }
_fk_sb_alive && { killall $FK_SB_PROCS >/dev/null 2>&1; sleep 1; killall -9 $FK_SB_PROCS >/dev/null 2>&1; }
fi
echo " ✓ Forkozz остановлен и убран из автозапуска"
_fk_say "Снимаем перехват трафика и возвращаем DNS роутера"
@@ -22866,7 +22896,13 @@ _fk_wait_up() {
_fk_why() {
local l
l="$(logread 2>/dev/null | grep -E 'netshift|sing-box' | grep -E 'fatal|Aborted|error' | tail -n 1)"
# только записи этого запуска: старая ошибка из журнала (например, «нет настроенного подключения» до
# того, как добавили сервер) больше не выдаётся за причину сейчас
if [ -n "$FK_LOGMARK" ]; then
l="$(logread 2>/dev/null | awk -v m="$FK_LOGMARK" 'f { print } index($0, m) { f = 1 }' | grep -E 'netshift|sing-box' | grep -E 'fatal|Aborted|error' | tail -n 1)"
else
l="$(logread 2>/dev/null | tail -n 200 | grep -E 'netshift|sing-box' | grep -E 'fatal|Aborted|error' | tail -n 1)"
fi
case "$l" in
*"Outbound section not found"*) echo "ни в одной секции нет настроенного подключения" ;;
*"Invalid service in community lists"*) echo "в секции выбран список, которого Forkozz не знает — сохраните сервисы заново" ;;
@@ -22880,11 +22916,11 @@ _fk_why() {
_fk_reset_runtime() {
_zm_run 90 /etc/init.d/netshift stop >/dev/null 2>&1
if pidof sing-box >/dev/null 2>&1 && [ -z "$(_fk_sb_users)" ]; then
if _fk_sb_alive && [ -z "$(_fk_sb_users)" ]; then
[ -x /etc/init.d/sing-box ] && /etc/init.d/sing-box stop >/dev/null 2>&1
killall sing-box >/dev/null 2>&1
killall $FK_SB_PROCS >/dev/null 2>&1
sleep 2
pidof sing-box >/dev/null 2>&1 && killall -9 sing-box >/dev/null 2>&1
_fk_sb_alive && killall -9 $FK_SB_PROCS >/dev/null 2>&1
echo " ✓ Остановлен оставшийся процесс sing-box"
fi
nft delete table inet "$FK_NFT" >/dev/null 2>&1
@@ -22945,7 +22981,7 @@ do_fk_service() {
_zm_run 240 /usr/bin/netshift reload >> "$JOBS_DIR/forkop-svc.out" 2>&1 || echo "!! Перезагрузка настроек завершилась с ошибкой"
else
_fk_say "Запускаем Forkozz: собираем конфиг sing-box, скачиваем списки, ставим правила"
if _fk_up || pidof sing-box >/dev/null 2>&1 || nft list table inet "$FK_NFT" >/dev/null 2>&1; then
if _fk_up || _fk_sb_alive || nft list table inet "$FK_NFT" >/dev/null 2>&1; then
_zm_run 90 /etc/init.d/netshift stop >> "$JOBS_DIR/forkop-svc.out" 2>&1
fi
_fk_start_once || echo "!! Запуск завершился с ошибкой"
@@ -22991,7 +23027,11 @@ do_fk_service() {
echo "ОШИБКА: Forkozz не запустился${b:+: $b}. Если ошибка повторяется, перезагрузите роутер — это сбросит всё, что осталось от прошлых запусков."
_zm_reboot_hint "Forkozz не запустился. Перезагрузите роутер — это сбросит всё, что осталось от прошлых запусков, — и включите Forkozz снова."
echo "Последние записи журнала:"
logread 2>/dev/null | grep -E 'netshift|sing-box' | grep -v 'DEBUG\|\[debug\]' | tail -n 10 | sed 's/^[^]]*\]: //; s/^/ /'
# записи этого запуска (если метка есть), без цветовых кодов, служебного шума udhcpc и предупреждений
# sing-box об устаревших настройках — они ни на что не влияют и только мешают увидеть причину
logread 2>/dev/null | { if [ -n "$FK_LOGMARK" ]; then awk -v m="$FK_LOGMARK" 'f { print } index($0, m) { f = 1 }'; else cat; fi; } |
grep -E 'netshift|sing-box' | grep -v 'DEBUG\|\[debug\]\|udhcpc\|is deprecated\|deprecated in sing-box' |
sed 's/\x1b\[[0-9;]*m//g; s/\[[0-9;]*m//g' | tail -n 10 | sed 's/^[^]]*\]: //; s/^/ /'
return 1
}
@@ -23020,7 +23060,7 @@ do_fk_lists() {
if [ "$rc" != 0 ]; then
echo "!! Скачивание списков завершилось с ошибкой — работают прежние"
else
echo " ✓ Списки скачаны — sing-box подхватит их сам"
echo " ✓ Загрузка списков закончилась — sing-box подхватит новые сам"
fi
_fk_say "Проверяем, что списки на месте и работают"
_fk_check_live
@@ -26693,7 +26733,7 @@ function port_list(v, what) {
}
function engine_caps() {
let b = s(fs.readfile(BIN));
let b = s(fs.readfile(BIN)), fc = s(fs.readfile(NS_LIB + "/sing_box_config_facade.sh"));
return {
naive: !!fs.access(NS_LIB + "/naive.sh"),
kill: !!fs.access(NS_LIB + "/kill_switch.sh"),
@@ -26702,7 +26742,10 @@ function engine_caps() {
hijack: index(b, "nft_add_dns_hijack() {") >= 0,
ports: index(b, "sing_box_configure_port_rules() {") >= 0,
quic_all: index(b, "disable_quic_mode() {") >= 0,
prefixes: prefix_ok()
prefixes: prefix_ok(),
// ссылки tuic:// и anytls:// движок понимает с версий новее 0.9.11 — разрешаем их, как только он их знает
tuic: index(fc, "\n tuic)") >= 0,
anytls: index(fc, "\n anytls)") >= 0
};
}
@@ -26884,10 +26927,11 @@ function cmd_set() {
else links[length(links) - 1] += "%20" + w;
}
links = uniq(links);
let naive_ok = !!fs.access(NS_LIB + "/naive.sh");
let naive_ok = !!fs.access(NS_LIB + "/naive.sh"), ec = engine_caps();
let more = (ec.tuic ? ", tuic" : "") + (ec.anytls ? ", anytls" : "");
for (let x in links)
if (!match(x, SCHEMES) && !(naive_ok && match(x, SCHEMES_NAIVE)))
fail(match(x, /^naive\+/i) ? (naive_ok ? "ссылке NaiveProxy нужны логин и пароль: naive+https://логин:пароль@сервер" : "NaiveProxy работает с движком Forkozz 0.9.11 и новее — обновите Forkozz") : match(x, /^(tuic|http|https|anytls|wireguard|hysteria):\/\//i) ? "Forkozz не поддерживает такие ссылки: " + substr(x, 0, 40) + " — подойдут vless, vmess, trojan, ss, socks5, hysteria2" + (naive_ok ? ", naive" : "") : "ссылка не поддерживается: " + substr(x, 0, 40));
if (!match(x, SCHEMES) && !(naive_ok && match(x, SCHEMES_NAIVE)) && !(ec.tuic && match(x, /^tuic:\/\/[^ \t\r\n]+$/i)) && !(ec.anytls && match(x, /^anytls:\/\/[^ \t\r\n]+$/i)))
fail(match(x, /^naive\+/i) ? (naive_ok ? "ссылке NaiveProxy нужны логин и пароль: naive+https://логин:пароль@сервер" : "NaiveProxy работает с движком Forkozz 0.9.11 и новее — обновите Forkozz") : match(x, /^(tuic|http|https|anytls|wireguard|hysteria):\/\//i) ? "Forkozz не поддерживает такие ссылки: " + substr(x, 0, 40) + " — подойдут vless, vmess, trojan, ss, socks5, hysteria2" + (naive_ok ? ", naive" : "") + more : "ссылка не поддерживается: " + substr(x, 0, 40));
let subs = uniq(lines(type(d.subs) == "array" && length(d.subs) ? d.subs : d.sub)), iface = trim(s(d.iface));
if (mode == "links" && !length(links)) fail("добавьте хотя бы одну ссылку на сервер");
if (mode == "sub") {
@@ -28018,7 +28062,7 @@ function cmd_diag() {
function add(v, what, why) { push(checks, { verdict: v, what, why: why || "" }); }
let en = length(filter(fs.lsdir("/etc/rc.d") || [], (f) => !!match(f, /^S[0-9]+netshift$/))) > 0;
let nft = sh("nft list table inet " + NFT_TABLE + " >/dev/null 2>&1 && echo y") != "";
let sbr = sh("pidof sing-box") != "";
let sbr = sh("pidof sing-box sing-box-core") != "";
add(nft ? "ok" : "fail", "Служба Forkozz", nft ? "запущена" + (en ? " и включена в автозапуск" : ", но не в автозапуске") : "не запущена — нажмите «Включить»");
add(sbr ? "ok" : "fail", "sing-box", sbr ? "работает" : "не работает — посмотрите журнал ниже");
let sb = jcmd(BIN + " get_sing_box_status") || {};
@@ -28341,7 +28385,7 @@ function cmd_route(target, addr, source) {
}
function sb_rss() {
let pid = trim(split(trim(sh("pidof sing-box")), " ")[0] || "");
let pid = trim(split(trim(sh("pidof sing-box sing-box-core")), " ")[0] || "");
if (!match(pid, /^[0-9]+$/)) return null;
let m = match(s(fs.readfile("/proc/" + pid + "/status")), /VmRSS:[ \t]*([0-9]+)/);
return m ? int(m[1]) * 1024 : null;
@@ -45311,8 +45355,8 @@ return view.extend({
return ta;
}
function linkOk(x) { return LINK_RE.test(x) || (!!CAPS.naive && NAIVE_RE.test(x)); }
function linkKinds() { return 'vless, vmess, trojan, ss, socks5, hysteria2' + (CAPS.naive ? ', naive' : ''); }
function linkOk(x) { return LINK_RE.test(x) || (!!CAPS.naive && NAIVE_RE.test(x)) || (!!CAPS.tuic && /^tuic:\/\/\S+$/i.test(x)) || (!!CAPS.anytls && /^anytls:\/\/\S+$/i.test(x)); }
function linkKinds() { return 'vless, vmess, trojan, ss, socks5, hysteria2' + (CAPS.naive ? ', naive' : '') + (CAPS.tuic ? ', tuic' : '') + (CAPS.anytls ? ', anytls' : ''); }
var taLinks = area('links', 'vless://…\nss://…\ntrojan://…', 130);
var linksOpt = { value: draft.links, re: { test: linkOk }, placeholder: 'vless://…\nss://…\ntrojan://…',
empty: 'Серверов пока нет — вставьте ссылки ниже и нажмите «Добавить».',