diff --git a/ZapretManager_LuCI.sh b/ZapretManager_LuCI.sh index 15a9a86..4ba3776 100644 --- a/ZapretManager_LuCI.sh +++ b/ZapretManager_LuCI.sh @@ -1,6 +1,6 @@ #!/bin/sh read -r _ _ ZM_NEW_VER <<'ZM_VERSION_EOF' -# Version: 4.39 +# Version: 4.41 ZM_VERSION_EOF set -e @@ -11244,17 +11244,25 @@ doh_probe() { printf '{%s}\n' "$(_doh_res_json "$(_doh_probe "$url" "$(_doh_boots_for "$1")")")" } -# Ответ nslookup от DNS-сервера — живой? Адрес (Name:), «такого имени нет» (NXDOMAIN) и «имя есть, а адреса -# нет» (NODATA — busybox пишет «No answer») — сервер ответил. Часть серверов DoH (WebShift, Comss, Яндекс) -# на случайное имя отвечает именно NODATA, и раньше сторож считал их неработающими: DNS переключался на -# запасной и через две проверки возвращался — так по кругу каждые 4 минуты. -# Не ответ: SERVFAIL, REFUSED, тишина (timed out / no servers) и пустой вывод -_doh_ns_ans() { - case "$1" in - *SERVFAIL*|*REFUSED*|*[Rr]efused*|*"timed out"*|*"no servers"*) return 1 ;; - *NXDOMAIN*|*"Name:"*|*"No answer"*|*NODATA*) return 0 ;; - esac - return 1 +# Ответил ли DNS-сервер на запрос nslookup. Решаем по коду выхода и коду ответа, а не по тексту. +# busybox nslookup (networking/nslookup.c): шапку «Server:» печатает, только когда пришёл ответ; при ответе +# NOERROR выходит с 0 — и с адресом, и без него (NODATA: «имя есть, адреса нет» — тогда после шапки нет +# НИЧЕГО, ни «Name:», ни «No answer»). Именно так WebShift, XBOX, Dagami, Comss и Яндекс отвечают на случайное +# имя, и сторож 4.38–4.39 принимал их за неработающие — DNS прыгал между серверами каждые 4 минуты. +# NXDOMAIN («такого имени нет») — тоже ответ, хотя nslookup выходит с 1. Не ответ: SERVFAIL, REFUSED, +# тишина (timed out / no servers / «No answer» — так busybox пишет о запросе, на который ответа не было) +# и снятый по таймеру nslookup. То же верно и для nslookup из bind. +# _doh_ns_q ИМЯ ПОРТ — 0, если сервер ответил; вывод — в DOH_NS_OUT +_doh_ns_q() { + local rc + DOH_NS_OUT="$(_doh_t 7 nslookup -type=a "$1" "127.0.0.1:$2" 2>&1)"; rc=$? + case "$DOH_NS_OUT" in *SERVFAIL*|*REFUSED*|*[Rr]efused*|*"timed out"*|*"no servers"*|*"Parse error"*) return 1 ;; esac + case "$DOH_NS_OUT" in *NXDOMAIN*) return 0 ;; esac + [ "$rc" = 0 ] || return 1 + case "$DOH_NS_OUT" in *"Server:"*) ;; *) return 1 ;; esac + # у busybox «No answer» с кодом 0 — это запрос без ответа; у nslookup из bind — NODATA, но там есть «answer:» + case "$DOH_NS_OUT" in *"No answer"*) case "$DOH_NS_OUT" in *"answer:"*) return 0 ;; esac; return 1 ;; esac + return 0 } _doh_local_ok() { @@ -11267,8 +11275,8 @@ _doh_local_ok() { _doh_live() { local o n n="zm$(printf '%08d' $(( ($$ * 7919 + $(date +%s)) % 100000000 ))).example.com" - o="$(_doh_t 4 nslookup -type=a "$n" "127.0.0.1:${1:-5053}" 2>&1)" - _doh_ns_ans "$o" && return 0 + _doh_ns_q "$n" "${1:-5053}" && return 0 + o="$DOH_NS_OUT" case "$o" in *SERVFAIL*|*REFUSED*|*[Rr]efused*|*"timed out"*|*"no servers"*) return 1 ;; esac @@ -11278,10 +11286,9 @@ _doh_live() { _doh_live_real() { local o n port="${1:-5053}" n="zm$(printf '%08d' $(( ($$ * 7919 + $(date +%s)) % 100000000 ))).example.com" - o="$(_doh_t 5 nslookup -type=a "$n" "127.0.0.1:$port" 2>&1)" - _doh_ns_ans "$o" || return 1 - o="$(_doh_t 5 nslookup -type=a example.com "127.0.0.1:$port" 2>&1)" - case "$o" in *"Name:"*) return 0 ;; esac + _doh_ns_q "$n" "$port" || return 1 + _doh_ns_q example.com "$port" || return 1 + case "$DOH_NS_OUT" in *"Name:"*) return 0 ;; esac return 1 } _doh_wait_real() { @@ -11465,17 +11472,24 @@ _doh_bad_mark() { _doh_bad_clear() { [ -f "$DOH_FO_STATE" ] && grep -q "^$(_doh_bad_key "$1")=" "$DOH_FO_STATE" && _doh_st_put "$(_doh_bad_key "$1")" ""; return 0; } _doh_ok() { case "$(_doh_probe "$(_doh_url_of "$1")" "$(_doh_boots_for "$1")" "/tmp/zm-dohfo.$$")" in "ok "*) return 0 ;; esac; return 1; } _doh_cur_alive() { - local port + local port lf port="$(uci -q get 'https-dns-proxy.@https-dns-proxy[0].listen_port')"; port="${port:-5053}" - _doh_live_real "$port" && return 0 + _doh_live_real "$port" && { _doh_st_put lfail 0; return 0; } sleep 2 - _doh_live_real "$port" && return 0 + _doh_live_real "$port" && { _doh_st_put lfail 0; return 0; } + # сервер не отвечает и напрямую — вот тогда он правда упал, переключаемся _doh_ok "$1" || return 1 - # перезапуск уже не помог два раза подряд — не дёргаем DNS роутера снова, решает переключение - [ "${2:-0}" -lt 2 ] || return 1 - _zm_run 30 /etc/init.d/https-dns-proxy restart >/dev/null 2>&1 - _doh_wait_real "$port" 10 && { _doh_fo_event "$(_doh_name "$1") отвечает, а DNS на роутере завис — перезапустили https-dns-proxy"; return 0; } - return 1 + # Сервер напрямую отвечает, а через https-dns-proxy роутера — нет. Другой сервер тут почти никогда не + # помогает (прокси тот же), поэтому сначала чиним прокси и ждём. Переключаемся, только если так + # держится три проверки подряд (6 минут) — одиночный сбой проверки DNS больше не двигает + lf="$(_doh_st_get lfail 0)"; case "$lf" in ''|*[!0-9]*) lf=0 ;; esac + lf=$((lf + 1)); _doh_st_put lfail "$lf" + if [ "$lf" = 1 ] || [ "$lf" = 3 ]; then + _zm_run 30 /etc/init.d/https-dns-proxy restart >/dev/null 2>&1 + _doh_wait_real "$port" 10 && { _doh_st_put lfail 0; _doh_fo_event "$(_doh_name "$1") отвечает, а DNS на роутере завис — перезапустили https-dns-proxy"; return 0; } + fi + [ "$lf" -ge 3 ] && return 1 + return 0 } _doh_fo_chain() { local id @@ -21550,7 +21564,9 @@ _fk_sb_pkg_switch() { _fk_sb_drop_pkgs() { local p had="" k="$JOBS_DIR/fk-sb-keep" for p in $FK_SB_PKGS; do _pkg_is_installed "$p" && had="$had $p"; done - [ -n "$had" ] || { _fk_sb_leftovers; return 0; } + # sing-box-tiny «предоставляет» sing-box: apk видит оба имени, хотя пакет один + case " $had " in *" sing-box-tiny "*) had="$(echo $had | tr ' ' '\n' | grep -vx 'sing-box' | tr '\n' ' ')" ;; esac + [ -n "$(echo $had)" ] || { _fk_sb_leftovers; return 0; } rm -rf "$k"; mkdir -p "$k" cp -p /etc/init.d/sing-box "$k/init" 2>/dev/null cp -p /etc/config/sing-box "$k/config" 2>/dev/null @@ -21575,15 +21591,105 @@ _fk_sb_leftovers() { return 0 } +# Служба запуска sing-box — ровно та, что в пакете sing-box OpenWrt (openwrt/packages, net/sing-box/files). +# Движок Forkozz запускает и останавливает sing-box только через /etc/init.d/sing-box и берёт настройки +# из /etc/config/sing-box. Расширенный облегчённый sing-box — один файл программы без службы, поэтому +# раньше сначала ставился пакет sing-box-tiny (только ради службы), а потом сразу заменялся. Теперь +# служба пишется сама, и облегчённый ставится сразу — без лишней загрузки пакета +_fk_sb_init_write() { + [ -x /etc/init.d/sing-box ] && [ -s /etc/config/sing-box ] && return 1 + mkdir -p /etc/init.d /etc/config /etc/sing-box + if [ ! -x /etc/init.d/sing-box ]; then + cat > /etc/init.d/sing-box << 'ZM_SB_INIT_EOF' +#!/bin/sh /etc/rc.common + +USE_PROCD=1 +START=99 + +script=$(readlink "$initscript") +NAME="$(basename ${script:-$initscript})" +PROG="/usr/bin/sing-box" + +start_service() { + config_load "$NAME" + + local enabled user group conffile workdir ifaces + config_get_bool enabled "main" "enabled" "0" + [ "$enabled" -eq "1" ] || return 0 + + config_get user "main" "user" "root" + config_get conffile "main" "conffile" + config_get ifaces "main" "ifaces" + config_get workdir "main" "workdir" "/usr/share/sing-box" + + mkdir -p "$workdir" + local group="$(id -ng $user)" + chown $user:$group "$workdir" + + procd_open_instance "$NAME.main" + procd_set_param command "$PROG" run -c "$conffile" -D "$workdir" + + # Use root user if you want to use the TUN mode. + procd_set_param user "$user" + procd_set_param file "$conffile" + [ -z "$ifaces" ] || procd_set_param netdev $ifaces + procd_set_param stdout 1 + procd_set_param stderr 1 + procd_set_param respawn + + procd_close_instance +} + +service_triggers() { + local ifaces + config_load "$NAME" + config_get ifaces "main" "ifaces" + procd_open_trigger + for iface in $ifaces; do + procd_add_interface_trigger "interface.*.up" $iface /etc/init.d/$NAME restart + done + procd_close_trigger + procd_add_reload_trigger "$NAME" +} +ZM_SB_INIT_EOF + chmod 0755 /etc/init.d/sing-box + fi + if [ ! -s /etc/config/sing-box ]; then + printf "\nconfig sing-box 'main'\n\toption enabled '0'\n\toption user 'root'\n\toption conffile '/etc/sing-box/config.json'\n\toption workdir '/usr/share/sing-box'\n" > /etc/config/sing-box + chmod 0644 /etc/config/sing-box + fi + return 0 +} + _fk_sb_install() { - local v + local v up=0 own="" v="$(_fk_sb_ver)" if [ -n "$v" ] && ! _st_ver_lt "$v" 1.12.0 && [ -x /etc/init.d/sing-box ]; then _fk_say "sing-box $v уже установлен ($(_fk_sb_name "$(_fk_sb_var)")) — оставляем его" return 0 fi if [ -n "$v" ] && _st_ver_lt "$v" 1.12.0; then _fk_say "sing-box $v слишком старый для Forkozz (нужен 1.12 и новее) — заменяем" - elif [ -n "$v" ]; then _fk_say "У sing-box $v нет службы запуска — ставим пакет со службой"; fi + elif [ -n "$v" ]; then _fk_say "У sing-box $v нет службы запуска — ставим заново"; fi + # По умолчанию — расширенный облегчённый sing-box (yandexru45/sing-box-extended-lite): все протоколы + # Forkozz (XHTTP, VMess, VLESS Encryption, Reality, Hysteria2, AmneziaWG) при 10–17 МБ на флеше + if [ "$1" != stable ] && _fk_lite_arch >/dev/null && _fk_lite_engine; then + /etc/init.d/netshift running >/dev/null 2>&1 && up=1 + _fk_sb_init_write && own=1 + ZM_FK_SB_NESTED=1; ZM_FK_SB_FRESH=1 + do_fk_singbox lite norestart; v=$? + ZM_FK_SB_NESTED=""; ZM_FK_SB_FRESH="" + # движок после смены ядра перезапускает Forkozz, даже если тот не был запущен, — возвращаем как было + [ "$up" = 0 ] && /etc/init.d/netshift running >/dev/null 2>&1 && _zm_run 60 $FK_REL /etc/init.d/netshift stop >/dev/null 2>&1 + if [ "$v" = 0 ] && [ "$(_fk_sb_var)" = lite ]; then + mkdir -p "$ZM_STATE_DIR"; echo sing-box > "$FK_SB_OWN" + return 0 + fi + # не встал — убираем свою службу, чтобы пакет OpenWrt поставил свою, и ставим пакет + [ -n "$own" ] && [ -z "$(_fk_sb_pkg)" ] && rm -f /etc/init.d/sing-box /etc/config/sing-box + _fk_say "Расширенный облегчённый sing-box не встал — ставим облегчённый из репозитория OpenWrt. Перейти на расширенный облегчённый можно позже в «Настройках» → sing-box" + elif [ "$1" != stable ] && ! _fk_lite_arch >/dev/null; then + _fk_say "Расширенного облегчённого sing-box для этого роутера ($TG_ARCH) нет — ставим облегчённый из репозитория OpenWrt" + fi if [ "$1" != stable ] && _fk_sb_pkg_switch tiny; then : else [ "$1" != stable ] && _fk_say "Облегчённого sing-box для этого роутера нет — ставим обычный" @@ -21591,18 +21697,6 @@ _fk_sb_install() { fi _fk_sb_pkg > "$FK_SB_OWN" _fk_say "sing-box $(_fk_sb_ver) установлен ($(_fk_sb_name "$(_fk_sb_var)"))" - # По умолчанию Forkozz работает на расширенном облегчённом sing-box (yandexru45/sing-box-extended-lite): - # все протоколы (XHTTP, VMess, VLESS Encryption, AmneziaWG…) при 10–17 МБ. Пакет выше нужен ради - # службы запуска sing-box. Не встал облегчённый или нет сборки под роутер — работаем на пакетном - if [ "$1" != stable ] && _fk_lite_arch >/dev/null && _fk_lite_engine; then - local up=0 - /etc/init.d/netshift running >/dev/null 2>&1 && up=1 - ZM_FK_SB_NESTED=1 - do_fk_singbox lite norestart || _rb_warn "Расширенный облегчённый sing-box не встал — Forkozz работает на $(_fk_sb_name "$(_fk_sb_var)") sing-box $(_fk_sb_ver). Поставить его можно позже в «Настройках» → sing-box" - ZM_FK_SB_NESTED="" - # движок после смены ядра перезапускает Forkozz, даже если тот не был запущен, — возвращаем как было - [ "$up" = 0 ] && /etc/init.d/netshift running >/dev/null 2>&1 && _zm_run 60 $FK_REL /etc/init.d/netshift stop >/dev/null 2>&1 - fi return 0 } @@ -23109,7 +23203,7 @@ _fk_sbext_run() { while IFS= read -r l; do case "$l" in *"Downloading sing-box-extended"*|*"Downloading sing-box extended lite"*) dl=1 ;; - *"Restarting netshift"*) unp=1; [ "$rs" = 1 ] && continue; rs=1 ;; + *"Restarting netshift"*) unp=1; [ "$rs" = 1 ] && continue; rs=1; [ -n "$ZM_FK_SB_FRESH" ] && continue ;; esac _fk_sbext_say "$l" done < "$new" @@ -23130,7 +23224,7 @@ _fk_sbext_run() { fi fi if [ "$unp" = 1 ] && [ "$rs" = 0 ] && ! ls -d $td >/dev/null 2>&1; then - rs=1; echo " → Новое ядро на месте — движок перезапускает Forkozz на нём" + rs=1; [ -n "$ZM_FK_SB_FRESH" ] || echo " → Новое ядро на месте — движок перезапускает Forkozz на нём" fi done wait "$p" 2>/dev/null @@ -23176,21 +23270,6 @@ do_fk_singbox() { fi echo " ✓ Места хватает: флеш $(((free + ${have:-0}) / 1024)) МБ, /tmp $((tfree / 1024)) МБ${old:+. Если новый не встанет, останется прежний $old}" case "$want" in - extended) - _zm_net_prepare api.github.com github.com objects.githubusercontent.com || return 1 - _fk_say "Скачиваем расширенный sing-box с GitHub (github.com/shtorm-7/sing-box-extended) — около 40 МБ" - _fk_sbext_run "$out" - _fk_sb_is_ext && [ "$(jsonfilter -i "$out" -e '@.success' 2>/dev/null)" = true ] && echo " ✓ Расширенный sing-box $(_fk_sb_ver) скачан и поставлен" - if [ "$(jsonfilter -i "$out" -e '@.success' 2>/dev/null)" != true ] || ! _fk_sb_is_ext; then - msg="$(jsonfilter -i "$out" -e '@.message' 2>/dev/null)" - echo "ОШИБКА: расширенный sing-box не встал${msg:+: $msg}" - [ -n "$old" ] && [ "$(_fk_sb_ver)" = "$old" ] && echo " ✓ Прежний sing-box $old на месте — Forkozz работает как раньше" - rm -f "$out" - return 1 - fi - rm -f "$out" - _fk_settle 180 "Движок перезапускает Forkozz на новом ядре — ждём, это до пары минут" || _rb_warn "Перезапуск Forkozz затянулся — продолжаем" - _fk_sb_drop_pkgs ;; lite) _zm_net_prepare api.github.com github.com objects.githubusercontent.com || return 1 _fk_say "Скачиваем расширенный облегчённый sing-box с GitHub (github.com/$FK_SBL_REPO) — около 10–17 МБ" @@ -23208,7 +23287,8 @@ do_fk_singbox() { esac [ "$(jsonfilter -i "$out" -e '@.warning' 2>/dev/null)" = upx_ram_spike ] && _rb_warn "Памяти у роутера меньше 256 МБ: сжатая сборка при каждом запуске ненадолго занимает 35–45 МБ — если Forkozz будет падать, выберите «Облегчённый»" rm -f "$out" - _fk_settle 180 "Движок перезапускает Forkozz на новом ядре — ждём, это до пары минут" || _rb_warn "Перезапуск Forkozz затянулся — продолжаем" + if [ -n "$ZM_FK_SB_FRESH" ]; then _fk_settle 180 "Движок заканчивает установку ядра — ждём" || _rb_warn "Движок долго заканчивает установку — продолжаем" + else _fk_settle 180 "Движок перезапускает Forkozz на новом ядре — ждём, это до пары минут" || _rb_warn "Перезапуск Forkozz затянулся — продолжаем"; fi _fk_sb_drop_pkgs ;; *) _zm_net_prepare "$(_zm_feed_host)" || return 1