v4.53
This commit is contained in:
1 parent
4217137e68
commit
3ba79dd3b7
1 file changed
+522
-46
+522
-46
@@ -1,6 +1,6 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
read -r _ _ ZM_NEW_VER <<'ZM_VERSION_EOF'
|
read -r _ _ ZM_NEW_VER <<'ZM_VERSION_EOF'
|
||||||
# Version: 4.47
|
# Version: 4.53
|
||||||
ZM_VERSION_EOF
|
ZM_VERSION_EOF
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
@@ -1390,6 +1390,9 @@ _fk_watch() {
|
|||||||
wait=$((120 * (1 << (n > 4 ? 4 : n))))
|
wait=$((120 * (1 << (n > 4 ? 4 : n))))
|
||||||
[ $((now - last)) -lt "$wait" ] && return 0
|
[ $((now - last)) -lt "$wait" ] && return 0
|
||||||
logger -t zapret-manager "Forkozz включён, но sing-box или его правила не работают — перезапускаем (попытка $((n + 1)))"
|
logger -t zapret-manager "Forkozz включён, но sing-box или его правила не работают — перезапускаем (попытка $((n + 1)))"
|
||||||
|
# sing-box упал, не скачав набор правил при запуске (часто — после перезагрузки роутера: кэш наборов в /tmp),
|
||||||
|
# — перед перезапуском включаем их загрузку через VPN, если она выключена
|
||||||
|
_fk_sb_alive || ( FK_LOGMARK=""; _fk_say() { :; }; _fk_rs_check recent >/dev/null 2>&1 )
|
||||||
mkdir -p "$ZM_STATE_DIR"
|
mkdir -p "$ZM_STATE_DIR"
|
||||||
echo "$((n + 1)) $now" > "$f"
|
echo "$((n + 1)) $now" > "$f"
|
||||||
_zm_run 180 /etc/init.d/netshift restart >/dev/null 2>&1
|
_zm_run 180 /etc/init.d/netshift restart >/dev/null 2>&1
|
||||||
@@ -1555,6 +1558,7 @@ zm_watch() {
|
|||||||
_ab_watch
|
_ab_watch
|
||||||
_dn_watch
|
_dn_watch
|
||||||
_zo_doh_follow
|
_zo_doh_follow
|
||||||
|
_doh_boot_fill
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -10975,6 +10979,7 @@ doh_status() {
|
|||||||
local installed="false" current="" running=false force=false list="" sep="" i url port prov n=0 provs=""
|
local installed="false" current="" running=false force=false list="" sep="" i url port prov n=0 provs=""
|
||||||
_doh_legacy_fix
|
_doh_legacy_fix
|
||||||
_doh_pkg && installed="true"
|
_doh_pkg && installed="true"
|
||||||
|
[ "$installed" = true ] && _doh_boot_fill
|
||||||
[ "$installed" = true ] && _doh_fo_on && ! grep -qF "$DOH_FO_CRON" "$CRON_FILE" 2>/dev/null && _doh_fo_cron on
|
[ "$installed" = true ] && _doh_fo_on && ! grep -qF "$DOH_FO_CRON" "$CRON_FILE" 2>/dev/null && _doh_fo_cron on
|
||||||
if [ -f "$_doh_file" ]; then
|
if [ -f "$_doh_file" ]; then
|
||||||
i=0
|
i=0
|
||||||
@@ -10982,7 +10987,7 @@ doh_status() {
|
|||||||
port="$(uci -q get "https-dns-proxy.@https-dns-proxy[$i].listen_port")"
|
port="$(uci -q get "https-dns-proxy.@https-dns-proxy[$i].listen_port")"
|
||||||
[ -n "$port" ] || port=$((5053 + i))
|
[ -n "$port" ] || port=$((5053 + i))
|
||||||
prov="$(_doh_provider_of "$url")"
|
prov="$(_doh_provider_of "$url")"
|
||||||
list="$list$sep{\"url\":\"$(esc "$url")\",\"port\":\"$(esc "$port")\",\"provider\":\"$prov\",\"bootstrap\":\"$(esc "$(uci -q get "https-dns-proxy.@https-dns-proxy[$i].bootstrap_dns")")\"}"
|
list="$list$sep{\"url\":\"$(esc "$url")\",\"port\":\"$(esc "$port")\",\"provider\":\"$prov\",\"bootstrap\":\"$(esc "$(uci -q get "https-dns-proxy.@https-dns-proxy[$i].bootstrap_dns")")\",\"boot_auto\":\"$(esc "$(_doh_boot_auto "$prov" "$url")")\",\"boot_dead\":$(grep -qxF "$url" "$DOH_BOOT_SKIP" 2>/dev/null && echo true || echo false)}"
|
||||||
sep=","; n=$((n + 1)); provs="$prov"
|
sep=","; n=$((n + 1)); provs="$prov"
|
||||||
i=$((i + 1))
|
i=$((i + 1))
|
||||||
[ "$i" -gt 16 ] && break
|
[ "$i" -gt 16 ] && break
|
||||||
@@ -10992,16 +10997,21 @@ doh_status() {
|
|||||||
fi
|
fi
|
||||||
/etc/init.d/https-dns-proxy running >/dev/null 2>&1 && running=true
|
/etc/init.d/https-dns-proxy running >/dev/null 2>&1 && running=true
|
||||||
[ -f "$DOH_STOP" ] && ! _doh_pkg && rm -f "$DOH_STOP"
|
[ -f "$DOH_STOP" ] && ! _doh_pkg && rm -f "$DOH_STOP"
|
||||||
printf '{"installed":%s,"stopped":%s,"current":"%s","running":%s,"force_dns":%s,"force_mode":"%s","resolvers":[%s],"steer_active":%s,"steer_installed":%s,"forkozz":%s,"fk_name":"%s","bootstrap_custom":"%s","hosts_extra":%s,"failover":%s,"dnsm":%s,"net":%s,"own":[%s],"zapret_doh":"%s"}\n' \
|
printf '{"installed":%s,"stopped":%s,"current":"%s","running":%s,"force_dns":%s,"force_mode":"%s","resolvers":[%s],"steer_active":%s,"steer_installed":%s,"forkozz":%s,"fk_name":"%s","bootstrap_custom":"%s","luci_boots":[%s],"hosts_extra":%s,"failover":%s,"dnsm":%s,"net":%s,"own":[%s],"zapret_doh":"%s"}\n' \
|
||||||
"$installed" "$([ -f "$DOH_STOP" ] && echo true || echo false)" "$(esc "$current")" "$running" "$force" "$(_doh_force_mode)" "$list" \
|
"$installed" "$([ -f "$DOH_STOP" ] && echo true || echo false)" "$(esc "$current")" "$running" "$force" "$(_doh_force_mode)" "$list" \
|
||||||
"$(_doh_steer_active && echo true || echo false)" \
|
"$(_doh_steer_active && echo true || echo false)" \
|
||||||
"$(grep -qx 'engine' /etc/zm-steer/owned 2>/dev/null && stl_present && echo true || echo false)" \
|
"$(grep -qx 'engine' /etc/zm-steer/owned 2>/dev/null && stl_present && echo true || echo false)" \
|
||||||
"$(_fk_installed && echo true || echo false)" "$(_fk_name)" "$(esc "$(cat "$DOH_BOOT_FILE" 2>/dev/null)")" "$(_hosts_extra)" "$(_doh_fo_json)" \
|
"$(_fk_installed && echo true || echo false)" "$(_fk_name)" "$(esc "$(cat "$DOH_BOOT_FILE" 2>/dev/null)")" "$(_doh_luci_list | awk -F'\t' '{ printf "%s{\"t\":\"%s\",\"b\":\"%s\"}", (NR > 1 ? "," : ""), $1, $2 }')" "$(_hosts_extra)" "$(_doh_fo_json)" \
|
||||||
"$(_dnsm_present && echo true || echo false)" "$(_dn_json)" "$(_doh_own_json)" \
|
"$(_dnsm_present && echo true || echo false)" "$(_dn_json)" "$(_doh_own_json)" \
|
||||||
"$(if [ ! -x /etc/init.d/zapret ] || [ ! -f "$CONF" ]; then echo none; elif [ -f "$ZO_DOH_FLAG" ]; then echo on; else echo off; fi)"
|
"$(if [ ! -x /etc/init.d/zapret ] || [ ! -f "$CONF" ]; then echo none; elif [ -f "$ZO_DOH_FLAG" ]; then echo on; else echo off; fi)"
|
||||||
}
|
}
|
||||||
|
|
||||||
DOH_BOOT_FILE="/opt/zapret-manager-luci/doh_bootstrap"
|
DOH_BOOT_FILE="/opt/zapret-manager-luci/doh_bootstrap"
|
||||||
|
# Bootstrap DNS https-dns-proxy, когда в его настройках адресов нет (DEFAULT_BOOTSTRAP в его службе:
|
||||||
|
# Cloudflare + Google). В настройки панель его НЕ пишет — только для своей проверки серверов
|
||||||
|
DOH_BOOT_DEF="1.1.1.1,1.0.0.1,8.8.8.8,8.8.4.4"
|
||||||
|
# Список провайдеров LuCI https-dns-proxy: у каждого шаблон адреса и его Bootstrap DNS
|
||||||
|
DOH_LUCI_PROV="/usr/share/https-dns-proxy/providers"
|
||||||
|
|
||||||
_doh_boot_of() {
|
_doh_boot_of() {
|
||||||
case "$1" in
|
case "$1" in
|
||||||
@@ -11012,6 +11022,9 @@ _doh_boot_of() {
|
|||||||
quad9) echo "9.9.9.9,149.112.112.112,2620:fe::fe,2620:fe::9" ;;
|
quad9) echo "9.9.9.9,149.112.112.112,2620:fe::fe,2620:fe::9" ;;
|
||||||
quad9_open) echo "9.9.9.10,149.112.112.10,2620:fe::10,2620:fe::fe:10" ;;
|
quad9_open) echo "9.9.9.10,149.112.112.10,2620:fe::10,2620:fe::fe:10" ;;
|
||||||
yandex) echo "77.88.8.8,77.88.8.1" ;;
|
yandex) echo "77.88.8.8,77.88.8.1" ;;
|
||||||
|
# Comss: обычные DNS-серверы с официальной страницы comss.ru (page.php?id=7315). В LuCI https-dns-proxy
|
||||||
|
# у Comss записаны 92.38.152.163 и 93.115.24.204 — через них https-dns-proxy у пользователей не работает
|
||||||
|
comss) echo "83.220.169.155,212.109.195.93" ;;
|
||||||
yandex_family) echo "77.88.8.7,77.88.8.3" ;;
|
yandex_family) echo "77.88.8.7,77.88.8.3" ;;
|
||||||
yandex_safe) echo "77.88.8.88,77.88.8.2" ;;
|
yandex_safe) echo "77.88.8.88,77.88.8.2" ;;
|
||||||
controld) echo "76.76.2.0,76.76.10.0" ;;
|
controld) echo "76.76.2.0,76.76.10.0" ;;
|
||||||
@@ -11023,6 +11036,199 @@ _doh_boot_of() {
|
|||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Провайдеры LuCI https-dns-proxy: «шаблон<TAB>bootstrap_dns». Берём там же, где их берёт сам LuCI
|
||||||
|
# (rpcd luci.https-dns-proxy, get_providers): собранный пакет кладёт всех провайдеров одним массивом в
|
||||||
|
# /usr/share/https-dns-proxy/providers.json, а папки providers/ в нём нет; в исходниках — по файлу на провайдера.
|
||||||
|
# В 4.50 читалась только папка, поэтому на роутере адреса Comss не находились. Разбор идёт по структуре JSON
|
||||||
|
# (ключи только самого провайдера, не его params), работает и с отформатированным, и со сжатым в строку JSON
|
||||||
|
DOH_LUCI_JSON="/usr/share/https-dns-proxy/providers.json"
|
||||||
|
_doh_luci_list() {
|
||||||
|
local c=/tmp/zm-doh-luciprov.tsv src
|
||||||
|
if [ -s "$DOH_LUCI_JSON" ]; then src="$DOH_LUCI_JSON"
|
||||||
|
elif ls "$DOH_LUCI_PROV"/*.json >/dev/null 2>&1; then src="$DOH_LUCI_PROV"
|
||||||
|
else return 0; fi
|
||||||
|
if [ -f "$c" ] && [ "$c" -nt "$src" ] && [ -n "$(find "$c" -mmin -60 2>/dev/null)" ]; then cat "$c"; return 0; fi
|
||||||
|
if [ "$src" = "$DOH_LUCI_JSON" ]; then set -- "$src"; else set -- "$src"/*.json; fi
|
||||||
|
awk '
|
||||||
|
function flush() {
|
||||||
|
gsub(/[ \t]/, "", b)
|
||||||
|
if (t ~ /^https:\/\/[^ \t\\]*$/ && b ~ /^[0-9A-Fa-f.:,]+$/) print t "\t" b
|
||||||
|
t = ""; b = ""
|
||||||
|
}
|
||||||
|
FNR == 1 { d = 0; key = ""; want = 0 }
|
||||||
|
{
|
||||||
|
ln = $0; n = length(ln); i = 1
|
||||||
|
while (i <= n) {
|
||||||
|
c = substr(ln, i, 1)
|
||||||
|
if (c == "\"") {
|
||||||
|
r = substr(ln, i + 1)
|
||||||
|
if (!match(r, /^([^"\\]|\\.)*"/)) break
|
||||||
|
v = substr(r, 1, RLENGTH - 1); i += RLENGTH + 1
|
||||||
|
if (d != 1) continue
|
||||||
|
if (want) { if (key == "template") t = v; else b = v; want = 0; key = ""; continue }
|
||||||
|
r = substr(ln, i)
|
||||||
|
if (match(r, /^[ \t\r]*:/)) { key = v; i += RLENGTH; want = (key == "template" || key == "bootstrap_dns") }
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (c == "{") { d++; if (d == 1) { t = ""; b = "" } }
|
||||||
|
else if (c == "}") { d--; if (d == 0) flush() }
|
||||||
|
if (c != " " && c != "\t" && c != "\r") want = 0
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
' "$@" 2>/dev/null > "$c.$$" && mv -f "$c.$$" "$c"
|
||||||
|
cat "$c" 2>/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
# Bootstrap DNS, который LuCI https-dns-proxy ставит этому адресу сервера ({option} в шаблоне — вариант сервера)
|
||||||
|
_doh_luci_boot() {
|
||||||
|
local u="$1" t b pre suf mid
|
||||||
|
[ -n "$u" ] || return 0
|
||||||
|
_doh_luci_list | while IFS=' ' read -r t b; do
|
||||||
|
case "$t" in
|
||||||
|
*"{option}"*)
|
||||||
|
pre="${t%%\{option\}*}"; suf="${t#*\{option\}}"
|
||||||
|
case "$u" in "$pre"*"$suf") ;; *) continue ;; esac
|
||||||
|
mid="${u#"$pre"}"; mid="${mid%"$suf"}"
|
||||||
|
case "$mid" in */*|*'?'*|*'#'*) continue ;; esac ;;
|
||||||
|
*) [ "$u" = "$t" ] || continue ;;
|
||||||
|
esac
|
||||||
|
echo "$b"; break
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# Bootstrap «Авто» для сервера — ровно как в LuCI https-dns-proxy: адреса из его шаблона провайдера
|
||||||
|
# (у Comss — 92.38.152.163, 93.115.24.204…). Нет LuCI-шаблона — адреса из таблицы панели (Яндекс и др.).
|
||||||
|
# Нет и там — пусто: bootstrap_dns в настройки не пишется, https-dns-proxy сам решает, чем пользоваться
|
||||||
|
# _doh_boot_auto ID|ССЫЛКА [ССЫЛКА]
|
||||||
|
_doh_boot_auto() {
|
||||||
|
local u="$2" b
|
||||||
|
[ -n "$u" ] || u="$(_doh_url_of "$1" 2>/dev/null)"
|
||||||
|
b="$(_doh_boot_of "$1")"
|
||||||
|
[ -n "$b" ] || b="$(_doh_boot_of "$(_doh_provider_of "$u")")"
|
||||||
|
[ -n "$b" ] || b="$(_doh_luci_boot "$u")"
|
||||||
|
echo "$b"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Имя сервера из ссылки DoH (https://ИМЯ[:порт]/…)
|
||||||
|
_doh_url_host() { local h="${1#*://}"; h="${h%%/*}"; echo "${h%%:*}"; }
|
||||||
|
|
||||||
|
# Какие из Bootstrap-адресов правда работают: через каждый (обычный DNS, порт 53) ищем имя DoH-сервера —
|
||||||
|
# ровно то, что делает https-dns-proxy при запуске. Проверка параллельная, до 4 секунд. Печатает рабочие адреса
|
||||||
|
# в исходном порядке (пусто — ни один не ответил). DoH-сервер, заданный IP-адресом, в bootstrap не нуждается
|
||||||
|
# _doh_boot_live ССЫЛКА|ИМЯ СПИСОК
|
||||||
|
_doh_boot_live() {
|
||||||
|
local host b d k=0 out="" pids=""
|
||||||
|
host="$(_doh_url_host "$1")"
|
||||||
|
case "$host" in *[!0-9.]*) ;; *) echo "$2"; return 0 ;; esac
|
||||||
|
[ -n "$2" ] || return 0
|
||||||
|
d="/tmp/zm-dohbl.$$.$(date +%s)"; mkdir -p "$d"
|
||||||
|
for b in $(printf '%s' "$2" | tr ',' ' '); do
|
||||||
|
k=$((k + 1)); [ "$k" -gt 8 ] && break
|
||||||
|
( _doh_t 4 nslookup -type=a "$host" "$b" 2>/dev/null | awk '
|
||||||
|
/^Name:/ { f = 1 }
|
||||||
|
f && /^Address/ { for (i = 2; i <= NF; i++) if ($i ~ /^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$/) { print "ok"; exit } }' > "$d/$k" ) &
|
||||||
|
pids="$pids $!"
|
||||||
|
done
|
||||||
|
# ждём только свои проверки (не чужие фоновые задачи панели)
|
||||||
|
wait $pids
|
||||||
|
k=0
|
||||||
|
for b in $(printf '%s' "$2" | tr ',' ' '); do
|
||||||
|
k=$((k + 1)); [ "$k" -gt 8 ] && break
|
||||||
|
[ -s "$d/$k" ] && out="$out${out:+,}$b"
|
||||||
|
done
|
||||||
|
rm -rf "$d"
|
||||||
|
echo "$out"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Что записать в настройки: свои адреса пользователя, если заданы, иначе «Авто» (может быть пусто)
|
||||||
|
_doh_boot_want() {
|
||||||
|
local b=""
|
||||||
|
[ -s "$DOH_BOOT_FILE" ] && b="$(head -n1 "$DOH_BOOT_FILE")"
|
||||||
|
[ -n "$b" ] || b="$(_doh_boot_auto "$1" "$2")"
|
||||||
|
echo "$b"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Bootstrap DNS в /etc/config/https-dns-proxy приводим к адресам «Авто» для сервера (как при выборе в панели):
|
||||||
|
# — у сервера нет bootstrap_dns, а свои адреса у провайдера есть — ставим их;
|
||||||
|
# — стоят стандартные Cloudflare + Google от 4.49 или адреса из шаблона LuCI (у Comss они не работают) —
|
||||||
|
# меняем на адреса «Авто»; если тех нет или ни один не отвечает — убираем, https-dns-proxy берёт встроенные.
|
||||||
|
# Адреса проверяются до записи (_doh_boot_live), после записи https-dns-proxy перезапускается и проверяется;
|
||||||
|
# не заработал — возвращаем как было и сутки этот сервер не трогаем. Адреса, заданные вручную, не трогаем.
|
||||||
|
# Всё долгое — в фоне, страница не ждёт
|
||||||
|
DOH_BOOT_449="1.1.1.1,1.0.0.1,2606:4700:4700::1111,2606:4700:4700::1001,8.8.8.8,8.8.4.4,2001:4860:4860::8888,2001:4860:4860::8844"
|
||||||
|
DOH_BOOT_SKIP="/tmp/zm-doh-bootfill.skip" # адреса «Авто» не отвечают (или DNS с ними не заработал)
|
||||||
|
DOH_BOOT_KEEP="/tmp/zm-doh-bootfill.keep" # адреса из шаблона LuCI отвечают — оставлены
|
||||||
|
_doh_boot_fill() {
|
||||||
|
local i=0 url cur want luci need="" ch="" lk=/tmp/zm-doh-bootfill.lock
|
||||||
|
[ -f "$_doh_file" ] && _doh_pkg || return 0
|
||||||
|
_dnsm_present && return 0
|
||||||
|
[ -s "$DOH_BOOT_FILE" ] && return 0
|
||||||
|
rm -f "$ZM_STATE_DIR/doh.bootfill.skip"
|
||||||
|
[ -n "$(find "$DOH_BOOT_SKIP" -mmin +1440 2>/dev/null)" ] && rm -f "$DOH_BOOT_SKIP"
|
||||||
|
[ -n "$(find "$DOH_BOOT_KEEP" -mmin +1440 2>/dev/null)" ] && rm -f "$DOH_BOOT_KEEP"
|
||||||
|
[ -d "$lk" ] && { [ -n "$(find "$lk" -maxdepth 0 -mmin +3 2>/dev/null)" ] || return 0; rmdir "$lk" 2>/dev/null; }
|
||||||
|
while url="$(uci -q get "https-dns-proxy.@https-dns-proxy[$i].resolver_url")"; do
|
||||||
|
cur="$(uci -q get "https-dns-proxy.@https-dns-proxy[$i].bootstrap_dns")"
|
||||||
|
luci="$(_doh_luci_boot "$url")"
|
||||||
|
if [ -z "$cur" ] || [ "$cur" = "$DOH_BOOT_449" ] || { [ -n "$luci" ] && [ "$cur" = "$luci" ]; }; then
|
||||||
|
want="$(_doh_boot_auto "$(_doh_provider_of "$url")" "$url")"
|
||||||
|
if [ "$want" != "$cur" ] && ! grep -qxF "$url" "$DOH_BOOT_SKIP" "$DOH_BOOT_KEEP" 2>/dev/null; then
|
||||||
|
if [ -n "$want" ]; then need="$need $i"
|
||||||
|
elif [ "$cur" = "$DOH_BOOT_449" ]; then
|
||||||
|
# своих адресов нет — без bootstrap_dns https-dns-proxy берёт те же Cloudflare + Google, перезапуск не нужен
|
||||||
|
uci -q delete "https-dns-proxy.@https-dns-proxy[$i].bootstrap_dns"; ch=1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
i=$((i + 1)); [ "$i" -gt 16 ] && break
|
||||||
|
done
|
||||||
|
[ -n "$ch" ] && uci -q commit https-dns-proxy
|
||||||
|
[ -n "$need" ] || return 0
|
||||||
|
mkdir "$lk" 2>/dev/null || return 0
|
||||||
|
(
|
||||||
|
ch=""; rs=""; port=""; urls=""
|
||||||
|
cp -p "$_doh_file" "$_doh_file.zm-bootfill" 2>/dev/null
|
||||||
|
for i in $need; do
|
||||||
|
url="$(uci -q get "https-dns-proxy.@https-dns-proxy[$i].resolver_url")" || continue
|
||||||
|
cur="$(uci -q get "https-dns-proxy.@https-dns-proxy[$i].bootstrap_dns")"
|
||||||
|
# адреса из шаблона LuCI, которые работают, не трогаем (сутки не перепроверяем)
|
||||||
|
if [ -n "$cur" ] && [ "$cur" != "$DOH_BOOT_449" ] && [ -n "$(_doh_boot_live "$url" "$cur")" ]; then
|
||||||
|
echo "$url" >> "$DOH_BOOT_KEEP"; continue
|
||||||
|
fi
|
||||||
|
want="$(_doh_boot_live "$url" "$(_doh_boot_auto "$(_doh_provider_of "$url")" "$url")")"
|
||||||
|
[ -n "$want" ] && [ "$want" = "$cur" ] && continue
|
||||||
|
if [ -n "$want" ]; then uci -q set "https-dns-proxy.@https-dns-proxy[$i].bootstrap_dns=$want"
|
||||||
|
else
|
||||||
|
# адреса провайдера не отвечают: ставить нечего, сутки не проверяем
|
||||||
|
echo "$url" >> "$DOH_BOOT_SKIP"
|
||||||
|
[ -n "$cur" ] || continue
|
||||||
|
uci -q delete "https-dns-proxy.@https-dns-proxy[$i].bootstrap_dns"
|
||||||
|
fi
|
||||||
|
ch=1; urls="$urls$url
|
||||||
|
"
|
||||||
|
[ "${want:-$DOH_BOOT_449}" = "${cur:-$DOH_BOOT_449}" ] || rs=1
|
||||||
|
[ -n "$port" ] || port="$(uci -q get "https-dns-proxy.@https-dns-proxy[$i].listen_port")"; [ -n "$port" ] || port=$((5053 + i))
|
||||||
|
done
|
||||||
|
if [ -n "$ch" ]; then
|
||||||
|
uci -q commit https-dns-proxy
|
||||||
|
logger -t zapret-manager "DoH: Bootstrap DNS приведён к рабочим адресам провайдера"
|
||||||
|
if [ -n "$rs" ] && [ ! -f "$DOH_STOP" ] && /etc/init.d/https-dns-proxy running >/dev/null 2>&1; then
|
||||||
|
_zm_run 30 /etc/init.d/https-dns-proxy restart >/dev/null 2>&1
|
||||||
|
if ! _doh_wait_local "$port" && ! { sleep 3; _doh_wait_local "$port"; }; then
|
||||||
|
mv -f "$_doh_file.zm-bootfill" "$_doh_file"
|
||||||
|
printf '%s' "$urls" >> "$DOH_BOOT_SKIP"
|
||||||
|
_zm_run 30 /etc/init.d/https-dns-proxy restart >/dev/null 2>&1
|
||||||
|
logger -t zapret-manager "DoH: с новым Bootstrap DNS DNS не заработал — вернули прежние настройки"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
rm -f "$_doh_file.zm-bootfill"
|
||||||
|
rmdir "$lk" 2>/dev/null
|
||||||
|
) >/dev/null 2>&1 &
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
_doh_ip_ok() {
|
_doh_ip_ok() {
|
||||||
case "$1" in *"$ZM_NL"*) return 1 ;; esac
|
case "$1" in *"$ZM_NL"*) return 1 ;; esac
|
||||||
printf '%s' "$1" | grep -qE '^([0-9]{1,3}\.){3}[0-9]{1,3}$' && {
|
printf '%s' "$1" | grep -qE '^([0-9]{1,3}\.){3}[0-9]{1,3}$' && {
|
||||||
@@ -11049,7 +11255,7 @@ doh_bootstrap_set() {
|
|||||||
i=0
|
i=0
|
||||||
while url="$(uci -q get "https-dns-proxy.@https-dns-proxy[$i].resolver_url")"; do
|
while url="$(uci -q get "https-dns-proxy.@https-dns-proxy[$i].resolver_url")"; do
|
||||||
b="$out"
|
b="$out"
|
||||||
[ -n "$b" ] || b="$(_doh_boot_of "$(_doh_provider_of "$url")")"
|
[ -n "$b" ] || b="$(_doh_boot_live "$url" "$(_doh_boot_auto "$(_doh_provider_of "$url")" "$url")")"
|
||||||
if [ -n "$b" ]; then uci -q set "https-dns-proxy.@https-dns-proxy[$i].bootstrap_dns=$b"
|
if [ -n "$b" ]; then uci -q set "https-dns-proxy.@https-dns-proxy[$i].bootstrap_dns=$b"
|
||||||
else uci -q delete "https-dns-proxy.@https-dns-proxy[$i].bootstrap_dns"; fi
|
else uci -q delete "https-dns-proxy.@https-dns-proxy[$i].bootstrap_dns"; fi
|
||||||
i=$((i + 1)); [ "$i" -gt 16 ] && break
|
i=$((i + 1)); [ "$i" -gt 16 ] && break
|
||||||
@@ -11099,11 +11305,11 @@ _doh_url_of() {
|
|||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Через какие DNS проверять сервер: как у https-dns-proxy — его Bootstrap DNS, а если его нет — Cloudflare + Google
|
||||||
_doh_boots_for() {
|
_doh_boots_for() {
|
||||||
local b=""
|
local b
|
||||||
[ -s "$DOH_BOOT_FILE" ] && b="$(head -n1 "$DOH_BOOT_FILE")"
|
b="$(_doh_boot_want "$1")"
|
||||||
[ -n "$b" ] || b="$(_doh_boot_of "$1")"
|
echo "${b:-$DOH_BOOT_DEF}"
|
||||||
echo "${b:-1.1.1.1,8.8.8.8}"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_doh_t() {
|
_doh_t() {
|
||||||
@@ -11112,11 +11318,23 @@ _doh_t() {
|
|||||||
_zm_tmo "$s" "$@"
|
_zm_tmo "$s" "$@"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Bootstrap-серверы для проверки: первый IPv4 и ещё один из другой сети (у стандартного списка — 1.1.1.1 и
|
||||||
|
# 8.8.8.8, а не два адреса Cloudflare подряд), чтобы недоступность одного провайдера не валила проверку
|
||||||
|
_doh_boot_pick() {
|
||||||
|
local b f="" s="" t=""
|
||||||
|
for b in $(printf '%s' "$1" | tr ',' ' '); do
|
||||||
|
case "$b" in *:*) continue ;; esac
|
||||||
|
if [ -z "$f" ]; then f="$b"
|
||||||
|
elif [ -z "$s" ] && [ "${b%%.*}" != "${f%%.*}" ]; then s="$b"
|
||||||
|
elif [ -z "$t" ]; then t="$b"; fi
|
||||||
|
done
|
||||||
|
echo $f ${s:-$t}
|
||||||
|
}
|
||||||
|
|
||||||
_doh_host_ips() {
|
_doh_host_ips() {
|
||||||
local host="$1" b ips n=0
|
local host="$1" b ips n=0
|
||||||
case "$host" in *[!0-9.]*) ;; *) echo "$host"; return 0 ;; esac
|
case "$host" in *[!0-9.]*) ;; *) echo "$host"; return 0 ;; esac
|
||||||
for b in $(printf '%s' "$2" | tr ',' ' '); do
|
for b in $(_doh_boot_pick "$2"); do
|
||||||
case "$b" in *:*) continue ;; esac
|
|
||||||
[ "$n" -ge 2 ] && break
|
[ "$n" -ge 2 ] && break
|
||||||
n=$((n + 1))
|
n=$((n + 1))
|
||||||
ips="$(_doh_t 3 nslookup -type=a "$host" "$b" 2>/dev/null | awk '
|
ips="$(_doh_t 3 nslookup -type=a "$host" "$b" 2>/dev/null | awk '
|
||||||
@@ -11376,31 +11594,60 @@ doh_set() {
|
|||||||
printf '%s\n' "$r"
|
printf '%s\n' "$r"
|
||||||
}
|
}
|
||||||
|
|
||||||
_doh_apply() {
|
# Bootstrap DNS при смене сервера: свои адреса пользователя — как есть; адреса «Авто» сначала проверяются
|
||||||
local provider="$1" url bootstrap="" n prev="" force
|
# (_doh_boot_live), в настройки идут только рабочие. Если ни один не ответил — bootstrap_dns не пишется.
|
||||||
url="$(_doh_url_of "$provider")" || { echo '{"error":"адрес сервера не подходит — нужна ссылка вида https://…/dns-query"}'; return 1; }
|
# Если DNS через https-dns-proxy с адресами «Авто» всё равно не заработал, пробуем ещё раз без них — и только
|
||||||
bootstrap="$(_doh_boot_of "$provider")"
|
# потом считаем, что не работает сам сервер
|
||||||
[ -s "$DOH_BOOT_FILE" ] && bootstrap="$(head -n1 "$DOH_BOOT_FILE")"
|
_doh_write() {
|
||||||
force="$(_doh_force_want)"
|
|
||||||
[ -f "$_doh_file" ] && cp -p "$_doh_file" "$_doh_file.zm-prev" 2>/dev/null && prev=1
|
|
||||||
{
|
{
|
||||||
_doh_main_text "$force"
|
_doh_main_text "$3"
|
||||||
echo ""
|
echo ""
|
||||||
echo "config https-dns-proxy"
|
echo "config https-dns-proxy"
|
||||||
echo " option resolver_url '$url'"
|
echo " option resolver_url '$1'"
|
||||||
[ -n "$bootstrap" ] && echo " option bootstrap_dns '$bootstrap'"
|
[ -n "$2" ] && echo " option bootstrap_dns '$2'"
|
||||||
echo " option listen_port '5053'"
|
echo " option listen_port '5053'"
|
||||||
} > "$_doh_file.zmtmp" && mv -f "$_doh_file.zmtmp" "$_doh_file"
|
} > "$_doh_file.zmtmp" && mv -f "$_doh_file.zmtmp" "$_doh_file"
|
||||||
|
}
|
||||||
|
|
||||||
|
_doh_apply() {
|
||||||
|
local provider="$1" url bootstrap="" auto="" live n prev="" force note=""
|
||||||
|
url="$(_doh_url_of "$provider")" || { echo '{"error":"адрес сервера не подходит — нужна ссылка вида https://…/dns-query"}'; return 1; }
|
||||||
|
if [ -s "$DOH_BOOT_FILE" ]; then bootstrap="$(head -n1 "$DOH_BOOT_FILE")"
|
||||||
|
else
|
||||||
|
auto="$(_doh_boot_auto "$provider" "$url")"
|
||||||
|
if [ -n "$auto" ]; then
|
||||||
|
live="$(_doh_boot_live "$url" "$auto")"
|
||||||
|
if [ -z "$live" ]; then note="адреса $(echo "$auto" | sed 's/,/, /g') не отвечают — Bootstrap DNS не задан, https-dns-proxy использует встроенные"
|
||||||
|
elif [ "$live" != "$auto" ]; then note="часть адресов не отвечает — оставили рабочие"; fi
|
||||||
|
bootstrap="$live"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
force="$(_doh_force_want)"
|
||||||
|
[ -f "$_doh_file" ] && cp -p "$_doh_file" "$_doh_file.zm-prev" 2>/dev/null && prev=1
|
||||||
|
_doh_write "$url" "$bootstrap" "$force"
|
||||||
if [ -f "$DOH_STOP" ]; then
|
if [ -f "$DOH_STOP" ]; then
|
||||||
rm -f "$_doh_file.zm-prev"
|
rm -f "$_doh_file.zm-prev"
|
||||||
printf '{"ok":true,"provider":"%s","stopped":true}\n' "$(esc "$provider")"
|
printf '{"ok":true,"provider":"%s","stopped":true,"bootstrap":"%s","boot_note":"%s"}\n' "$(esc "$provider")" "$(esc "$bootstrap")" "$(esc "$note")"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
/etc/init.d/https-dns-proxy enable >/dev/null 2>&1
|
/etc/init.d/https-dns-proxy enable >/dev/null 2>&1
|
||||||
/etc/init.d/https-dns-proxy reload >/dev/null 2>&1
|
/etc/init.d/https-dns-proxy reload >/dev/null 2>&1
|
||||||
/etc/init.d/https-dns-proxy restart >/dev/null 2>&1
|
/etc/init.d/https-dns-proxy restart >/dev/null 2>&1
|
||||||
/etc/init.d/dnsmasq restart >/dev/null 2>&1
|
/etc/init.d/dnsmasq restart >/dev/null 2>&1
|
||||||
if ! _doh_wait_local 5053; then
|
n=0
|
||||||
|
_doh_wait_local 5053 && n=1
|
||||||
|
if [ "$n" = 0 ] && [ -n "$auto" ] && [ -n "$bootstrap" ]; then
|
||||||
|
# с адресами «Авто» не поднялся — ещё раз без bootstrap_dns
|
||||||
|
_doh_write "$url" "" "$force"
|
||||||
|
/etc/init.d/https-dns-proxy restart >/dev/null 2>&1
|
||||||
|
if _doh_wait_local 5053; then
|
||||||
|
n=1
|
||||||
|
note="через адреса $(echo "$bootstrap" | sed 's/,/, /g') DNS не заработал — Bootstrap DNS не задан, https-dns-proxy использует встроенные"
|
||||||
|
bootstrap=""
|
||||||
|
logger -t zapret-manager "DoH: $(_doh_name "$provider") — $note"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
if [ "$n" = 0 ]; then
|
||||||
if [ -n "$prev" ]; then
|
if [ -n "$prev" ]; then
|
||||||
mv -f "$_doh_file.zm-prev" "$_doh_file"
|
mv -f "$_doh_file.zm-prev" "$_doh_file"
|
||||||
/etc/init.d/https-dns-proxy restart >/dev/null 2>&1
|
/etc/init.d/https-dns-proxy restart >/dev/null 2>&1
|
||||||
@@ -11412,7 +11659,10 @@ _doh_apply() {
|
|||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
rm -f "$_doh_file.zm-prev"
|
rm -f "$_doh_file.zm-prev"
|
||||||
printf '{"ok":true,"provider":"%s"}\n' "$(esc "$provider")"
|
# отметка «адреса сервера не отвечают» — для подсказки на странице и чтобы фон их не перепроверял
|
||||||
|
if [ -f "$DOH_BOOT_SKIP" ]; then grep -vxF "$url" "$DOH_BOOT_SKIP" > "$DOH_BOOT_SKIP.$$"; mv -f "$DOH_BOOT_SKIP.$$" "$DOH_BOOT_SKIP"; fi
|
||||||
|
[ -n "$auto" ] && [ -z "$bootstrap" ] && echo "$url" >> "$DOH_BOOT_SKIP"
|
||||||
|
printf '{"ok":true,"provider":"%s","bootstrap":"%s","boot_note":"%s"}\n' "$(esc "$provider")" "$(esc "$bootstrap")" "$(esc "$note")"
|
||||||
}
|
}
|
||||||
|
|
||||||
DOH_FO_FILE="/opt/zapret-manager-luci/doh_failover"
|
DOH_FO_FILE="/opt/zapret-manager-luci/doh_failover"
|
||||||
@@ -11526,6 +11776,7 @@ doh_watch() {
|
|||||||
_fk_installed && return 0
|
_fk_installed && return 0
|
||||||
_dnsm_present && return 0
|
_dnsm_present && return 0
|
||||||
_doh_legacy_fix
|
_doh_legacy_fix
|
||||||
|
_doh_boot_fill
|
||||||
mkdir "$lk" 2>/dev/null || { [ -n "$(find "$lk" -maxdepth 0 -mmin +5 2>/dev/null)" ] && rmdir "$lk" 2>/dev/null; return 0; }
|
mkdir "$lk" 2>/dev/null || { [ -n "$(find "$lk" -maxdepth 0 -mmin +5 2>/dev/null)" ] && rmdir "$lk" 2>/dev/null; return 0; }
|
||||||
mode="$(_doh_st_get mode normal)"
|
mode="$(_doh_st_get mode normal)"
|
||||||
/etc/init.d/https-dns-proxy running >/dev/null 2>&1 && run=1
|
/etc/init.d/https-dns-proxy running >/dev/null 2>&1 && run=1
|
||||||
@@ -22295,7 +22546,8 @@ _fk_mlkem_core_ok() {
|
|||||||
|
|
||||||
|
|
||||||
_fk_log_mark() {
|
_fk_log_mark() {
|
||||||
FK_LOGMARK="zm-lists-$$-$(date +%s)"
|
FK_MARKN=$((${FK_MARKN:-0} + 1))
|
||||||
|
FK_LOGMARK="zm-lists-$$-$(date +%s)-$FK_MARKN"
|
||||||
logger -t netshift "[info] $FK_LOGMARK" 2>/dev/null
|
logger -t netshift "[info] $FK_LOGMARK" 2>/dev/null
|
||||||
FK_LOGSEEN=0
|
FK_LOGSEEN=0
|
||||||
}
|
}
|
||||||
@@ -22394,6 +22646,79 @@ _fk_lists_report() {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# sing-box при запуске сам скачивает удалённые наборы правил (.srs с GitHub) и, если хоть один не скачался,
|
||||||
|
# завершается с FATAL «initial rule-set» — Forkozz целиком не работает. Кэш наборов правил sing-box держит в
|
||||||
|
# /tmp, поэтому так бывает и после каждой перезагрузки роутера. Раньше панель обрезала это сообщение до
|
||||||
|
# причины, а в конце всё равно писала «Готово: Forkozz работает».
|
||||||
|
# _fk_rs_fatal — последняя такая ошибка этого запуска: «набор<TAB>ссылка<TAB>причина по-русски»
|
||||||
|
# (recent — без отметки запуска: по последним записям журнала, для сторожа)
|
||||||
|
_fk_rs_fatal() {
|
||||||
|
local m="$FK_LOGMARK"
|
||||||
|
if [ -z "$m" ]; then [ "$1" = recent ] || return 0; fi
|
||||||
|
logread 2>/dev/null | { if [ -n "$m" ]; then cat; else tail -n 400; fi; } |
|
||||||
|
awk -v m="$m" 'm != "" && !f { if (index($0, m)) f = 1; next } /sing-box/ && /initial rule-set/ { l = $0 } END { if (l != "") print l }' |
|
||||||
|
sed 's/\x1b\[[0-9;]*m//g' | awk '
|
||||||
|
function rsw(r) {
|
||||||
|
if (r ~ /deadline exceeded|i\/o timeout|[Tt]imeout|timed out/) return "сервер не ответил вовремя"
|
||||||
|
if (r ~ /connection reset|EOF|broken pipe|forcibly closed/) return "соединение оборвалось"
|
||||||
|
if (r ~ /connection refused/) return "сервер отказал в соединении"
|
||||||
|
if (r ~ /network is unreachable|no route to host|host is unreachable/) return "нет связи с сервером"
|
||||||
|
if (r ~ /no such host|lookup |server misbehaving/) return "не удалось узнать адрес сервера (DNS)"
|
||||||
|
if (r ~ /x509|certificate|tls: /) return "не установилось защищённое соединение"
|
||||||
|
if (r ~ /operation not permitted/) return "запрос заблокировал сам роутер: правила или туннель ещё не были готовы"
|
||||||
|
if (match(r, /(status|code)[: ]+[0-9][0-9][0-9]/)) return "сервер ответил ошибкой " substr(r, RSTART + RLENGTH - 3, 3)
|
||||||
|
return substr(r, 1, 160)
|
||||||
|
}
|
||||||
|
function rsbase(u) { sub(/[?#].*$/, "", u); sub(/^.*\//, "", u); return u }
|
||||||
|
{
|
||||||
|
t = ""; u = ""; r = ""
|
||||||
|
if (match($0, /initial rule-set: [^:]+:/)) t = substr($0, RSTART + 18, RLENGTH - 19)
|
||||||
|
if (match($0, /"https?:\/\/[^"]+"/)) { u = substr($0, RSTART + 1, RLENGTH - 2); r = substr($0, RSTART + RLENGTH); sub(/^:[ ]*/, "", r) }
|
||||||
|
else if (match($0, /initial rule-set: [^:]+: /)) r = substr($0, RSTART + RLENGTH)
|
||||||
|
print t "\t" u "\t" rsw(r)
|
||||||
|
}'
|
||||||
|
}
|
||||||
|
|
||||||
|
# Отвечает ли sing-box (Clash API): пока он качает наборы правил при запуске — нет
|
||||||
|
_fk_clash_ok() { _zm_run 6 /usr/bin/netshift clash_api get_proxies 2>/dev/null | grep -q '"proxies"'; }
|
||||||
|
|
||||||
|
# Ждём, пока sing-box закончит запуск: ответит или упадёт на наборе правил. 0 — ответил
|
||||||
|
_fk_sb_settle() {
|
||||||
|
local i=0 max="${1:-60}"
|
||||||
|
while [ "$i" -lt "$max" ]; do
|
||||||
|
_fk_clash_ok && return 0
|
||||||
|
[ -n "$(_fk_rs_fatal)" ] && return 1
|
||||||
|
sleep 3; i=$((i + 3))
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Разбор ошибки «initial rule-set». 1 — такой ошибки нет. 0 — включили «Скачивать списки сервисов через VPN»
|
||||||
|
# (sing-box будет качать наборы правил через подключение) — нужен перезапуск. 2 — ошибка есть, менять нечего
|
||||||
|
_fk_rs_check() {
|
||||||
|
local rf t u r sec
|
||||||
|
rf="$(_fk_rs_fatal "$1")"
|
||||||
|
[ -n "$rf" ] || return 1
|
||||||
|
t="$(printf '%s' "$rf" | cut -f1)"; u="$(printf '%s' "$rf" | cut -f2)"; r="$(printf '%s' "$rf" | cut -f3)"
|
||||||
|
echo "!! sing-box не запустился: не скачался набор правил ${u:+${u##*/} }(${t:-?})${r:+ — $r}"
|
||||||
|
echo " · sing-box скачивает наборы правил сам при каждом запуске (и после перезагрузки роутера) и без любого из них не работает"
|
||||||
|
if [ "$(uci -q get netshift.settings.download_lists_via_proxy)" != 1 ]; then
|
||||||
|
case "$r" in *"оборвалось"*|*"вовремя"*|*"нет связи"*) echo " · Похоже, провайдер не даёт скачивать файлы с GitHub напрямую" ;; esac
|
||||||
|
sec="$(_fk_uc secnames 2>/dev/null | head -n1)"
|
||||||
|
case "$sec" in ''|*[!A-Za-z0-9_]*) sec="" ;; esac
|
||||||
|
if [ -n "$sec" ] && [ -n "$(uci -q get "netshift.$sec.connection_type")" ]; then
|
||||||
|
uci -q set netshift.settings.download_lists_via_proxy=1
|
||||||
|
uci -q set "netshift.settings.download_lists_via_proxy_section=$sec"
|
||||||
|
uci -q commit netshift
|
||||||
|
logger -t zapret-manager "Forkozz: набор правил ${u##*/} не скачался напрямую ($r) — включено «Скачивать списки сервисов через VPN»"
|
||||||
|
_fk_say "Включаем «Скачивать списки сервисов через VPN»: наборы правил sing-box будет качать через подключение"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
echo " · Наборы правил и так качаются через VPN — проверьте, что сервер подключения работает и через него открывается GitHub"
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
|
||||||
_fk_lists_errors() {
|
_fk_lists_errors() {
|
||||||
local e
|
local e
|
||||||
[ -n "$FK_LOGMARK" ] || return 0
|
[ -n "$FK_LOGMARK" ] || return 0
|
||||||
@@ -22411,6 +22736,18 @@ _fk_lists_errors() {
|
|||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
function add(s, own) { if (seen[s]++) return; out[++n] = s; if (own) nn++ }
|
function add(s, own) { if (seen[s]++) return; out[++n] = s; if (own) nn++ }
|
||||||
|
function rsw(r) {
|
||||||
|
if (r ~ /deadline exceeded|i\/o timeout|[Tt]imeout|timed out/) return "сервер не ответил вовремя"
|
||||||
|
if (r ~ /connection reset|EOF|broken pipe|forcibly closed/) return "соединение оборвалось"
|
||||||
|
if (r ~ /connection refused/) return "сервер отказал в соединении"
|
||||||
|
if (r ~ /network is unreachable|no route to host|host is unreachable/) return "нет связи с сервером"
|
||||||
|
if (r ~ /no such host|lookup |server misbehaving/) return "не удалось узнать адрес сервера (DNS)"
|
||||||
|
if (r ~ /x509|certificate|tls: /) return "не установилось защищённое соединение"
|
||||||
|
if (r ~ /operation not permitted/) return "запрос заблокировал сам роутер: правила или туннель ещё не были готовы"
|
||||||
|
if (match(r, /(status|code)[: ]+[0-9][0-9][0-9]/)) return "сервер ответил ошибкой " substr(r, RSTART + RLENGTH - 3, 3)
|
||||||
|
return substr(r, 1, 160)
|
||||||
|
}
|
||||||
|
function rsbase(u) { sub(/[?#].*$/, "", u); sub(/^.*\//, "", u); return u }
|
||||||
!f { if (index($0, m)) f = 1; next }
|
!f { if (index($0, m)) f = 1; next }
|
||||||
{
|
{
|
||||||
if (!match($0, /(netshift|sing-box)(\[[0-9]+\])?: /)) next
|
if (!match($0, /(netshift|sing-box)(\[[0-9]+\])?: /)) next
|
||||||
@@ -22418,7 +22755,12 @@ _fk_lists_errors() {
|
|||||||
l = substr($0, RSTART + RLENGTH)
|
l = substr($0, RSTART + RLENGTH)
|
||||||
}
|
}
|
||||||
src != "netshift" {
|
src != "netshift" {
|
||||||
if (l ~ /(ERROR|FATAL)/ && l ~ /rule[-_]?set/) add("sing-box: " substr(l, 1, 200))
|
gsub(/\033\[[0-9;]*m/, "", l)
|
||||||
|
# «initial rule-set» (sing-box не запустился) разбирает _fk_rs_check
|
||||||
|
if (l ~ /(ERROR|FATAL)/ && l ~ /rule[-_]?set/ && l !~ /initial rule-set/) {
|
||||||
|
if (match(l, /"https?:\/\/[^"]+"/)) { u = substr(l, RSTART + 1, RLENGTH - 2); r = substr(l, RSTART + RLENGTH); sub(/^:[ ]*/, "", r); add("sing-box: набор правил " rsbase(u) " не обновился — " rsw(r)) }
|
||||||
|
else add("sing-box: " substr(l, 1, 200))
|
||||||
|
}
|
||||||
next
|
next
|
||||||
}
|
}
|
||||||
{ sub(/^\[[a-z]+\] /, "", l) }
|
{ sub(/^\[[a-z]+\] /, "", l) }
|
||||||
@@ -22928,11 +23270,12 @@ _fk_why() {
|
|||||||
# только записи этого запуска: старая ошибка из журнала (например, «нет настроенного подключения» до
|
# только записи этого запуска: старая ошибка из журнала (например, «нет настроенного подключения» до
|
||||||
# того, как добавили сервер) больше не выдаётся за причину сейчас
|
# того, как добавили сервер) больше не выдаётся за причину сейчас
|
||||||
if [ -n "$FK_LOGMARK" ]; then
|
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)"
|
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|FATAL|Aborted|error' | tail -n 1)"
|
||||||
else
|
else
|
||||||
l="$(logread 2>/dev/null | tail -n 200 | grep -E 'netshift|sing-box' | grep -E 'fatal|Aborted|error' | tail -n 1)"
|
l="$(logread 2>/dev/null | tail -n 200 | grep -E 'netshift|sing-box' | grep -E 'fatal|FATAL|Aborted|error' | tail -n 1)"
|
||||||
fi
|
fi
|
||||||
case "$l" in
|
case "$l" in
|
||||||
|
*"initial rule-set"*) echo "sing-box не смог скачать набор правил при запуске (подробности выше)" ;;
|
||||||
*"Outbound section not found"*) echo "ни в одной секции нет настроенного подключения" ;;
|
*"Outbound section not found"*) echo "ни в одной секции нет настроенного подключения" ;;
|
||||||
*"Invalid service in community lists"*) echo "в секции выбран список, которого Forkozz не знает — сохраните сервисы заново" ;;
|
*"Invalid service in community lists"*) echo "в секции выбран список, которого Forkozz не знает — сохраните сервисы заново" ;;
|
||||||
*"configuration"*"is invalid"*) echo "sing-box не принял собранный конфиг — посмотрите журнал" ;;
|
*"configuration"*"is invalid"*) echo "sing-box не принял собранный конфиг — посмотрите журнал" ;;
|
||||||
@@ -23021,9 +23364,14 @@ do_fk_service() {
|
|||||||
_fk_wait_up 90
|
_fk_wait_up 90
|
||||||
fi
|
fi
|
||||||
if ! _fk_up; then
|
if ! _fk_up; then
|
||||||
|
_fk_rs_check
|
||||||
|
if [ "$?" = 1 ]; then
|
||||||
b="$(_fk_why)"
|
b="$(_fk_why)"
|
||||||
_rb_warn "Forkozz не поднялся${b:+: $b}"
|
_rb_warn "Forkozz не поднялся${b:+: $b}"
|
||||||
|
fi
|
||||||
_fk_say "Пробуем ещё раз: снимаем остатки прошлого запуска и запускаем заново"
|
_fk_say "Пробуем ещё раз: снимаем остатки прошлого запуска и запускаем заново"
|
||||||
|
# новая отметка в журнале: ошибки первой попытки не должны считаться ошибками повторной
|
||||||
|
_fk_log_mark
|
||||||
_fk_reset_runtime
|
_fk_reset_runtime
|
||||||
: > "$JOBS_DIR/forkop-svc.out"
|
: > "$JOBS_DIR/forkop-svc.out"
|
||||||
_fk_start_once || echo "!! Повторный запуск завершился с ошибкой"
|
_fk_start_once || echo "!! Повторный запуск завершился с ошибкой"
|
||||||
@@ -23039,6 +23387,33 @@ do_fk_service() {
|
|||||||
_fk_lists_wait 240 || _rb_warn "Списки ещё скачиваются — числа ниже могут быть неполными. Они догрузятся сами; позже нажмите «Обновить списки сейчас», чтобы увидеть итог"
|
_fk_lists_wait 240 || _rb_warn "Списки ещё скачиваются — числа ниже могут быть неполными. Они догрузятся сами; позже нажмите «Обновить списки сейчас», чтобы увидеть итог"
|
||||||
_fk_lists_report
|
_fk_lists_report
|
||||||
_fk_lists_errors
|
_fk_lists_errors
|
||||||
|
# sing-box мог упасть уже после проверки: при запуске он сам качает наборы правил, и без любого из них
|
||||||
|
# завершается. Не пишем «Forkozz работает», пока sing-box не ответил
|
||||||
|
_fk_sb_settle 60
|
||||||
|
_fk_rs_check; b=$?
|
||||||
|
if [ "$b" = 0 ]; then
|
||||||
|
_fk_say "Перезапускаем Forkozz с новой настройкой"
|
||||||
|
_fk_log_mark
|
||||||
|
_fk_reset_runtime
|
||||||
|
: > "$JOBS_DIR/forkop-svc.out"
|
||||||
|
_fk_start_once || echo "!! Запуск завершился с ошибкой"
|
||||||
|
_fk_svc_report
|
||||||
|
rm -f "$JOBS_DIR/forkop-svc.out"
|
||||||
|
_fk_ready || _fk_wait_up 90
|
||||||
|
_fk_sb_settle 90
|
||||||
|
_fk_rs_check; b=$?
|
||||||
|
if [ "$b" = 1 ] && _fk_up; then
|
||||||
|
echo " ✓ sing-box запустился — наборы правил скачались через VPN"
|
||||||
|
_fk_lists_wait 240 || _rb_warn "Списки ещё скачиваются — они догрузятся сами"
|
||||||
|
_fk_lists_errors
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
if [ "$b" != 1 ] || ! _fk_up; then
|
||||||
|
echo "ОШИБКА: Forkozz не работает — sing-box не запустился. Если GitHub у провайдера не открывается, проверьте сервер подключения и включите «Авто» в Система → Доступ к GitHub."
|
||||||
|
_zm_reboot_hint "Forkozz не запустился: sing-box не смог скачать наборы правил с GitHub."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
_fk_clash_ok || echo "!! sing-box пока не отвечает — он ещё запускается; если так и останется, нажмите «Перезапустить»"
|
||||||
_fk_servers_report
|
_fk_servers_report
|
||||||
_ab_free_sync >/dev/null 2>&1
|
_ab_free_sync >/dev/null 2>&1
|
||||||
_fk_zash_ensure
|
_fk_zash_ensure
|
||||||
@@ -23052,6 +23427,7 @@ do_fk_service() {
|
|||||||
grep -q '^Forkozz не запустился' "$ZM_REBOOT_HINT" 2>/dev/null && rm -f "$ZM_REBOOT_HINT"
|
grep -q '^Forkozz не запустился' "$ZM_REBOOT_HINT" 2>/dev/null && rm -f "$ZM_REBOOT_HINT"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
[ -n "$(_fk_rs_fatal)" ] && _fk_rs_check
|
||||||
b="$(_fk_why)"
|
b="$(_fk_why)"
|
||||||
echo "ОШИБКА: Forkozz не запустился${b:+: $b}. Если ошибка повторяется, перезагрузите роутер — это сбросит всё, что осталось от прошлых запусков."
|
echo "ОШИБКА: Forkozz не запустился${b:+: $b}. Если ошибка повторяется, перезагрузите роутер — это сбросит всё, что осталось от прошлых запусков."
|
||||||
_zm_reboot_hint "Forkozz не запустился. Перезагрузите роутер — это сбросит всё, что осталось от прошлых запусков, — и включите Forkozz снова."
|
_zm_reboot_hint "Forkozz не запустился. Перезагрузите роутер — это сбросит всё, что осталось от прошлых запусков, — и включите Forkozz снова."
|
||||||
@@ -23506,9 +23882,47 @@ _fk_dns_probe() {
|
|||||||
printf '%s|%s|%s|%s\n' "$v" "$ok" "${ms:-0}" "$how" > "$out"
|
printf '%s|%s|%s|%s\n' "$v" "$ok" "${ms:-0}" "$how" > "$out"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Ссылка DoH для проверки: сервер, путь /dns-query по умолчанию и запрос example.com
|
||||||
|
_fk_doh_url() {
|
||||||
|
local url
|
||||||
|
case "$1" in
|
||||||
|
https://*) url="$1"; case "${url#https://}" in */*) ;; *) url="$url/dns-query" ;; esac ;;
|
||||||
|
*/*) url="https://$1" ;;
|
||||||
|
*) url="https://$1/dns-query" ;;
|
||||||
|
esac
|
||||||
|
case "$url" in *\?*) echo "$url&dns=$FK_DNS_Q" ;; *) echo "$url?dns=$FK_DNS_Q" ;; esac
|
||||||
|
}
|
||||||
|
|
||||||
|
# «DNS через VPN» и DoH: серверы проверяются через тот же выход sing-box, через который Forkozz к ним ходит
|
||||||
|
_fk_dns_test_vpn() {
|
||||||
|
local list="$1" v j sep="" n=0 urls="" ms u
|
||||||
|
for v in $list; do
|
||||||
|
case "$v" in *[!A-Za-z0-9.:/_?=%-]*|'') continue ;; esac
|
||||||
|
n=$((n + 1)); [ "$n" -gt 16 ] && break
|
||||||
|
urls="$urls$(_fk_doh_url "$v")
|
||||||
|
"
|
||||||
|
done
|
||||||
|
j="$(printf '%s' "$urls" | _fk_ucj dnsvpn)" || return 1
|
||||||
|
printf '{"type":"doh","via":"vpn","results":['
|
||||||
|
n=0
|
||||||
|
for v in $list; do
|
||||||
|
case "$v" in *[!A-Za-z0-9.:/_?=%-]*|'') continue ;; esac
|
||||||
|
n=$((n + 1)); [ "$n" -gt 16 ] && break
|
||||||
|
u="$(_fk_doh_url "$v")"
|
||||||
|
ms="$(printf '%s' "$j" | jsonfilter -e "@.results[@.url='$u'].ms" 2>/dev/null | head -n1)"
|
||||||
|
case "$ms" in ''|*[!0-9]*) ms=0 ;; esac
|
||||||
|
printf '%s{"server":"%s","ok":%s,"ms":%s,"how":"vpn"}' "$sep" "$(esc "$v")" "$([ "$ms" -gt 0 ] && echo true || echo false)" "$ms"
|
||||||
|
sep=","
|
||||||
|
done
|
||||||
|
printf ']}\n'
|
||||||
|
}
|
||||||
|
|
||||||
fk_dns_test() {
|
fk_dns_test() {
|
||||||
local t="${1%%|*}" list="${1#*|}" d="$JOBS_DIR/fkdns.$$" v i=0 sep="" line ok ms how
|
local t="${1%%|*}" list="${1#*|}" d="$JOBS_DIR/fkdns.$$" v i=0 sep="" line ok ms how
|
||||||
case "$t" in udp|dot|doh) ;; *) echo '{"error":"неизвестный тип DNS"}'; return 1 ;; esac
|
case "$t" in udp|dot|doh) ;; *) echo '{"error":"неизвестный тип DNS"}'; return 1 ;; esac
|
||||||
|
if [ "$t" = doh ] && [ "$(uci -q get netshift.settings.dns_via_outbound)" = 1 ] && _fk_up; then
|
||||||
|
_fk_dns_test_vpn "$list" && return 0
|
||||||
|
fi
|
||||||
command -v dig >/dev/null 2>&1 || [ "$t" = doh ] || { echo '{"error":"нет программы dig — нажмите «Переустановить» Forkozz"}'; return 1; }
|
command -v dig >/dev/null 2>&1 || [ "$t" = doh ] || { echo '{"error":"нет программы dig — нажмите «Переустановить» Forkozz"}'; return 1; }
|
||||||
mkdir -p "$d"
|
mkdir -p "$d"
|
||||||
for v in $list; do
|
for v in $list; do
|
||||||
@@ -23518,7 +23932,7 @@ fk_dns_test() {
|
|||||||
_fk_dns_probe "$t" "$v" "$d/$i" &
|
_fk_dns_probe "$t" "$v" "$d/$i" &
|
||||||
done
|
done
|
||||||
wait
|
wait
|
||||||
printf '{"type":"%s","results":[' "$t"
|
printf '{"type":"%s","via":"%s","results":[' "$t" "$([ "$(uci -q get netshift.settings.dns_via_outbound)" = 1 ] && echo direct_detour || echo direct)"
|
||||||
for v in $(ls "$d" 2>/dev/null | sort -n); do
|
for v in $(ls "$d" 2>/dev/null | sort -n); do
|
||||||
IFS='|' read -r line ok ms how < "$d/$v"
|
IFS='|' read -r line ok ms how < "$d/$v"
|
||||||
printf '%s{"server":"%s","ok":%s,"ms":%s,"how":"%s"}' "$sep" "$(esc "$line")" "$([ "$ok" = 1 ] && echo true || echo false)" "${ms:-0}" "$how"
|
printf '%s{"server":"%s","ok":%s,"ms":%s,"how":"%s"}' "$sep" "$(esc "$line")" "$([ "$ok" = 1 ] && echo true || echo false)" "${ms:-0}" "$how"
|
||||||
@@ -28512,6 +28926,33 @@ function cmd_stats() {
|
|||||||
mem: int(j.memory || 0) > 0 ? int(j.memory) : sb_rss() });
|
mem: int(j.memory || 0) > 0 ? int(j.memory) : sb_rss() });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// «DNS через VPN»: Forkozz ходит к серверам DoH через подключение секции, поэтому и проверять их надо
|
||||||
|
// так же — запросом через тот же выход sing-box (Clash API, проверка задержки по адресу DoH). Прямая
|
||||||
|
// проверка с роутера тут обманывает: провайдер часто режет dns.google, dns.quad9.net и другие, хотя
|
||||||
|
// через VPN они работают. Ссылки — построчно на stdin: https://сервер/путь?dns=…
|
||||||
|
function cmd_dnsvpn() {
|
||||||
|
let ep = clash_ep();
|
||||||
|
if (!ep) { out({ error: "у sing-box не включён API — перезапустите Forkozz" }); return; }
|
||||||
|
let c = cursor(), st = c.get_all(CFG, "settings") || {}, p = pick(c), sec = s(st.dns_outbound_section);
|
||||||
|
if (sec == "" || c.get(CFG, sec) != "section" || !is_conn(c.get_all(CFG, sec) || {})) sec = p.sec;
|
||||||
|
if (!sec) { out({ error: "нет секции с подключением" }); return; }
|
||||||
|
let urls = filter(map(split(s(fs.stdin.read("all")), "\n"), (x) => trim(x)), (x) => !!match(x, /^https:\/\/[^ \t'"\\`$]+$/));
|
||||||
|
if (length(urls) > 16) urls = slice(urls, 0, 16);
|
||||||
|
let tag = group_tag(sec), dir = "/tmp/zm-fk-dnsvpn." + s(fs.readlink("/proc/self")), cmd = "";
|
||||||
|
if (fs.mkdir(dir) == null) { out({ error: "не удалось создать временную папку" }); return; }
|
||||||
|
for (let k = 0; k < length(urls); k++)
|
||||||
|
cmd += "(" + lat_cmd(ep, tag, urls[k], 5000) + " > " + q(dir + "/" + k) + " 2>/dev/null) & ";
|
||||||
|
sh(cmd + "wait");
|
||||||
|
let res = [];
|
||||||
|
for (let k = 0; k < length(urls); k++) {
|
||||||
|
let d = 0;
|
||||||
|
try { d = lat_parse(fs.readfile(dir + "/" + k)); } catch (e) { d = 0; }
|
||||||
|
push(res, { url: urls[k], ms: d });
|
||||||
|
}
|
||||||
|
sh("rm -rf " + q(dir));
|
||||||
|
out({ sec, results: res });
|
||||||
|
}
|
||||||
|
|
||||||
function cmd_conns() {
|
function cmd_conns() {
|
||||||
let ep = clash_ep();
|
let ep = clash_ep();
|
||||||
if (!ep) { out({ error: "у sing-box не включён API соединений — перезапустите Forkozz" }); return; }
|
if (!ep) { out({ error: "у sing-box не включён API соединений — перезапустите Forkozz" }); return; }
|
||||||
@@ -28542,7 +28983,7 @@ function cmd_conns() {
|
|||||||
let mode = ARGV[0] || "";
|
let mode = ARGV[0] || "";
|
||||||
let sec = ARGV[1] || "";
|
let sec = ARGV[1] || "";
|
||||||
if (mode != "route" && sec != "" && (ml(sec) || !match(sec, /^[A-Za-z0-9_]+$/))) fail("неверное имя секции");
|
if (mode != "route" && sec != "" && (ml(sec) || !match(sec, /^[A-Za-z0-9_]+$/))) fail("неверное имя секции");
|
||||||
if (sec == "" && index([ "get", "set", "secdel", "route", "diag", "secnames", "secstate", "stats", "conns", "fallback_config", "guard", "latmode", "nodepol" ], mode) < 0) sec = pick(cursor()).sec;
|
if (sec == "" && index([ "get", "set", "secdel", "route", "diag", "secnames", "secstate", "stats", "conns", "dnsvpn", "fallback_config", "guard", "latmode", "nodepol" ], mode) < 0) sec = pick(cursor()).sec;
|
||||||
|
|
||||||
if (mode == "device_setup") cmd_device_setup();
|
if (mode == "device_setup") cmd_device_setup();
|
||||||
else if (mode == "fallback_config") cmd_fallback_config();
|
else if (mode == "fallback_config") cmd_fallback_config();
|
||||||
@@ -28567,6 +29008,7 @@ else if (mode == "secstate") cmd_secstate(sec == "probe");
|
|||||||
else if (mode == "subinfo") cmd_subinfo(sec);
|
else if (mode == "subinfo") cmd_subinfo(sec);
|
||||||
else if (mode == "stats") cmd_stats();
|
else if (mode == "stats") cmd_stats();
|
||||||
else if (mode == "conns") cmd_conns();
|
else if (mode == "conns") cmd_conns();
|
||||||
|
else if (mode == "dnsvpn") cmd_dnsvpn();
|
||||||
else if (mode == "route") cmd_route(ARGV[1], ARGV[2], ARGV[3]);
|
else if (mode == "route") cmd_route(ARGV[1], ARGV[2], ARGV[3]);
|
||||||
else fail("неизвестная команда");
|
else fail("неизвестная команда");
|
||||||
ZM_INSTALLER_EOF
|
ZM_INSTALLER_EOF
|
||||||
@@ -31751,12 +32193,12 @@ var E = (function(raw) { return function() { var a = Array.prototype.slice.call(
|
|||||||
// k — тип сервера: plain — только шифрование, safe — защита от угроз, family — защита от угроз и сайтов для взрослых, geo — обход геоблокировок.
|
// k — тип сервера: plain — только шифрование, safe — защита от угроз, family — защита от угроз и сайтов для взрослых, geo — обход геоблокировок.
|
||||||
// v — название варианта внутри «папки» провайдера (FAMS). Адреса сверены с документацией провайдеров.
|
// v — название варианта внутри «папки» провайдера (FAMS). Адреса сверены с документацией провайдеров.
|
||||||
var PROVIDERS = [
|
var PROVIDERS = [
|
||||||
{ id: 'google', label: 'Google', url: 'https://dns.google/dns-query', boot: '8.8.8.8, 8.8.4.4', k: 'plain' },
|
{ id: 'google', label: 'Google', url: 'https://dns.google/dns-query', boot: '8.8.8.8, 8.8.4.4, 2001:4860:4860::8888, 2001:4860:4860::8844', k: 'plain' },
|
||||||
{ id: 'cloudflare', label: 'Cloudflare', v: 'Без фильтрации', url: 'https://cloudflare-dns.com/dns-query', boot: '1.1.1.1, 1.0.0.1', k: 'plain' },
|
{ id: 'cloudflare', label: 'Cloudflare', v: 'Без фильтрации', url: 'https://cloudflare-dns.com/dns-query', boot: '1.1.1.1, 1.0.0.1, 2606:4700:4700::1111, 2606:4700:4700::1001', k: 'plain' },
|
||||||
{ id: 'cloudflare_sec', label: 'Cloudflare · Защита от угроз', v: 'Защита от угроз', url: 'https://security.cloudflare-dns.com/dns-query', boot: '1.1.1.2, 1.0.0.2', k: 'safe' },
|
{ id: 'cloudflare_sec', label: 'Cloudflare · Защита от угроз', v: 'Защита от угроз', url: 'https://security.cloudflare-dns.com/dns-query', boot: '1.1.1.2, 1.0.0.2, 2606:4700:4700::1112, 2606:4700:4700::1002', k: 'safe' },
|
||||||
{ id: 'cloudflare_fam', label: 'Cloudflare · Семейный фильтр', v: 'Семейный фильтр', url: 'https://family.cloudflare-dns.com/dns-query', boot: '1.1.1.3, 1.0.0.3', k: 'family' },
|
{ id: 'cloudflare_fam', label: 'Cloudflare · Семейный фильтр', v: 'Семейный фильтр', url: 'https://family.cloudflare-dns.com/dns-query', boot: '1.1.1.3, 1.0.0.3, 2606:4700:4700::1113, 2606:4700:4700::1003', k: 'family' },
|
||||||
{ id: 'quad9_open', label: 'Quad9 · Без фильтрации', v: 'Без фильтрации', url: 'https://dns10.quad9.net/dns-query', boot: '9.9.9.10, 149.112.112.10', k: 'plain' },
|
{ id: 'quad9_open', label: 'Quad9 · Без фильтрации', v: 'Без фильтрации', url: 'https://dns10.quad9.net/dns-query', boot: '9.9.9.10, 149.112.112.10, 2620:fe::10, 2620:fe::fe:10', k: 'plain' },
|
||||||
{ id: 'quad9', label: 'Quad9 · Защита от угроз', v: 'Защита от угроз', url: 'https://dns.quad9.net/dns-query', boot: '9.9.9.9, 149.112.112.112', k: 'safe' },
|
{ id: 'quad9', label: 'Quad9 · Защита от угроз', v: 'Защита от угроз', url: 'https://dns.quad9.net/dns-query', boot: '9.9.9.9, 149.112.112.112, 2620:fe::fe, 2620:fe::9', k: 'safe' },
|
||||||
{ id: 'controld', label: 'Control D', v: 'Без фильтрации', url: 'https://freedns.controld.com/p0', boot: '76.76.2.0, 76.76.10.0', k: 'plain' },
|
{ id: 'controld', label: 'Control D', v: 'Без фильтрации', url: 'https://freedns.controld.com/p0', boot: '76.76.2.0, 76.76.10.0', k: 'plain' },
|
||||||
{ id: 'controld_sec', label: 'Control D · Защита от угроз', v: 'Защита от угроз', url: 'https://freedns.controld.com/p1', boot: '76.76.2.1, 76.76.10.1', k: 'safe' },
|
{ id: 'controld_sec', label: 'Control D · Защита от угроз', v: 'Защита от угроз', url: 'https://freedns.controld.com/p1', boot: '76.76.2.1, 76.76.10.1', k: 'safe' },
|
||||||
{ id: 'controld_fam', label: 'Control D · Семейный фильтр', v: 'Семейный фильтр', url: 'https://freedns.controld.com/family', boot: '76.76.2.4, 76.76.10.4', k: 'family' },
|
{ id: 'controld_fam', label: 'Control D · Семейный фильтр', v: 'Семейный фильтр', url: 'https://freedns.controld.com/family', boot: '76.76.2.4, 76.76.10.4', k: 'family' },
|
||||||
@@ -31767,7 +32209,7 @@ var PROVIDERS = [
|
|||||||
{ id: 'yandex_safe', label: 'Яндекс · Защита от угроз', v: 'Защита от угроз', url: 'https://safe.dot.dns.yandex.net/dns-query', boot: '77.88.8.88, 77.88.8.2', k: 'safe' },
|
{ id: 'yandex_safe', label: 'Яндекс · Защита от угроз', v: 'Защита от угроз', url: 'https://safe.dot.dns.yandex.net/dns-query', boot: '77.88.8.88, 77.88.8.2', k: 'safe' },
|
||||||
{ id: 'yandex_family', label: 'Яндекс · Семейный фильтр', v: 'Семейный фильтр', url: 'https://family.dot.dns.yandex.net/dns-query', boot: '77.88.8.7, 77.88.8.3', k: 'family' },
|
{ id: 'yandex_family', label: 'Яндекс · Семейный фильтр', v: 'Семейный фильтр', url: 'https://family.dot.dns.yandex.net/dns-query', boot: '77.88.8.7, 77.88.8.3', k: 'family' },
|
||||||
{ id: 'xbox', label: 'XBOX', url: 'https://xbox-dns.ru/dns-query', k: 'geo' },
|
{ id: 'xbox', label: 'XBOX', url: 'https://xbox-dns.ru/dns-query', k: 'geo' },
|
||||||
{ id: 'comss', label: 'Comss', url: 'https://dns.comss.one/dns-query', k: 'geo' },
|
{ id: 'comss', label: 'Comss', url: 'https://dns.comss.one/dns-query', boot: '83.220.169.155, 212.109.195.93', k: 'geo' },
|
||||||
{ id: 'dnsai', label: 'DNS-AI', url: 'https://dns.dns-ai.ru/dns-query', k: 'geo' },
|
{ id: 'dnsai', label: 'DNS-AI', url: 'https://dns.dns-ai.ru/dns-query', k: 'geo' },
|
||||||
{ id: 'bezmezhau', label: 'Bezmezhau', url: 'https://dns.bezmezhau.com/dns-query', k: 'geo' },
|
{ id: 'bezmezhau', label: 'Bezmezhau', url: 'https://dns.bezmezhau.com/dns-query', k: 'geo' },
|
||||||
{ id: 'geohide_ru', label: 'GeoHide RU', v: 'RU', d: 'российский сервер', url: 'https://geohide.ru/dns-query', k: 'geo' },
|
{ id: 'geohide_ru', label: 'GeoHide RU', v: 'RU', d: 'российский сервер', url: 'https://geohide.ru/dns-query', k: 'geo' },
|
||||||
@@ -31778,7 +32220,25 @@ var PROVIDERS = [
|
|||||||
{ id: 'webshift', label: 'WebShift', url: 'https://d.webshiftvpn.ru/dns-query', k: 'geo' },
|
{ id: 'webshift', label: 'WebShift', url: 'https://d.webshiftvpn.ru/dns-query', k: 'geo' },
|
||||||
{ id: 'dagami', label: 'Dagami', url: 'https://dns.dagami.ru/dns-query', k: 'geo' }
|
{ id: 'dagami', label: 'Dagami', url: 'https://dns.dagami.ru/dns-query', k: 'geo' }
|
||||||
];
|
];
|
||||||
var BOOT_STD = '1.1.1.1, 8.8.8.8';
|
// Bootstrap «Авто»: адреса из таблицы панели (проверенные), иначе из шаблона провайдера LuCI https-dns-proxy
|
||||||
|
function bootLuci(url) {
|
||||||
|
var out = '';
|
||||||
|
(bootLuci.list || []).some(function(x) {
|
||||||
|
var t = x.t || '', i = t.indexOf('{option}');
|
||||||
|
if (i < 0) { if (url === t) out = x.b; return !!out; }
|
||||||
|
var pre = t.slice(0, i), suf = t.slice(i + 8);
|
||||||
|
if (url.length < pre.length + suf.length || url.indexOf(pre) !== 0 || url.slice(url.length - suf.length) !== suf) return false;
|
||||||
|
if (/[\/?#]/.test(url.slice(pre.length, url.length - suf.length))) return false;
|
||||||
|
out = x.b; return true;
|
||||||
|
});
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
function bootAuto(url) {
|
||||||
|
var p = PROVIDERS.filter(function(x) { return x.url === url; })[0];
|
||||||
|
return p && p.boot ? p.boot : bootLuci(url || '');
|
||||||
|
}
|
||||||
|
function bootKey(s) { return String(s || '').split(/[\s,;]+/).filter(Boolean).join(','); }
|
||||||
|
function bootFmt(s) { return String(s || '').split(/[\s,;]+/).filter(Boolean).join(', '); }
|
||||||
var OWN_RE = /^https:\/\/[A-Za-z0-9.-]+\.[A-Za-z0-9-]+(:\d{1,5})?(\/[A-Za-z0-9._~%\/+=&?-]*)?$/;
|
var OWN_RE = /^https:\/\/[A-Za-z0-9.-]+\.[A-Za-z0-9-]+(:\d{1,5})?(\/[A-Za-z0-9._~%\/+=&?-]*)?$/;
|
||||||
var INTRO = 'Шифрованный DNS для всей сети: интернет-провайдер не видит DNS-запросы устройств и не может их подменить.';
|
var INTRO = 'Шифрованный DNS для всей сети: интернет-провайдер не видит DNS-запросы устройств и не может их подменить.';
|
||||||
var DNSM_URL = '/cgi-bin/luci/admin/services/dns-manager';
|
var DNSM_URL = '/cgi-bin/luci/admin/services/dns-manager';
|
||||||
@@ -32105,6 +32565,8 @@ return view.extend({
|
|||||||
}).then(function(res) {
|
}).then(function(res) {
|
||||||
if (res.error) throw new Error(res.error);
|
if (res.error) throw new Error(res.error);
|
||||||
logLines.push(res.stopped ? ' ✓ ' + label(id) + ' выбран — DoH остановлен, DNS пойдёт через этот сервер после «Запустить»' : ' ✓ ' + label(id) + ' применён — DNS на роутере отвечает');
|
logLines.push(res.stopped ? ' ✓ ' + label(id) + ' выбран — DoH остановлен, DNS пойдёт через этот сервер после «Запустить»' : ' ✓ ' + label(id) + ' применён — DNS на роутере отвечает');
|
||||||
|
if (res.boot_note) logLines.push(' · Bootstrap DNS: ' + res.boot_note);
|
||||||
|
else logLines.push(' · Bootstrap DNS: ' + (res.bootstrap ? bootFmt(res.bootstrap) : 'не задан — https-dns-proxy использует встроенные'));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
if (foChanged()) steps.push(function() {
|
if (foChanged()) steps.push(function() {
|
||||||
@@ -32417,9 +32879,21 @@ return view.extend({
|
|||||||
card.appendChild(E('p', { 'class': 'zm-hint' }, 'IP-адреса через запятую, до 6 штук.'));
|
card.appendChild(E('p', { 'class': 'zm-hint' }, 'IP-адреса через запятую, до 6 штук.'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
now = p && p.boot ? p.boot : BOOT_STD;
|
// показываем ровно то, что записано в /etc/config/https-dns-proxy, — это же видно и в LuCI https-dns-proxy
|
||||||
if (!data.bootstrap_custom && !pickProv && l.length === 1 && l[0].bootstrap) now = l[0].bootstrap.split(',').filter(function(x) { return x.indexOf(':') < 0; }).join(', ') || now;
|
bootLuci.list = data.luci_boots || [];
|
||||||
card.appendChild(E('p', { 'class': 'zm-hint' }, 'Сейчас: ' + now + (p && p.boot ? ' — адреса самого ' + p.label + '.' : ' — стандартные.')));
|
var r = !pickProv && l.length === 1 ? l[0] : null, name = p ? p.label : 'сервера';
|
||||||
|
var auto = r ? (r.boot_auto || '') : bootAuto(p ? p.url : sel);
|
||||||
|
var line = function(lead, v, tail) { return E('p', { 'class': 'zm-hint', 'style': 'overflow-wrap:anywhere' }, [ lead, E('b', {}, bootFmt(v)), tail ]); };
|
||||||
|
var none = function(txt) { return E('p', { 'class': 'zm-hint' }, txt + (auto ? '' : ' — у ' + name + ' своих адресов нет') + ', https-dns-proxy использует встроенные.'); };
|
||||||
|
now = r ? (r.bootstrap || '') : '';
|
||||||
|
if (!r) card.appendChild(auto ? line('Будут: ', auto, ' — адреса ' + name + ' (перед записью панель проверит, что они отвечают).') : none('Bootstrap DNS задан не будет'));
|
||||||
|
else if (!now && auto && r.boot_dead) card.appendChild(line('Сейчас: не задан — адреса ' + name + ' ', auto, ' не отвечают, https-dns-proxy использует встроенные.'));
|
||||||
|
else if (!now) card.appendChild(none('Сейчас: не задан'));
|
||||||
|
else if (data.bootstrap_custom && bootKey(now) === bootKey(data.bootstrap_custom)) {
|
||||||
|
card.appendChild(line('Сейчас: ', now, ' — ваши адреса.'));
|
||||||
|
card.appendChild(auto ? line('После сохранения: ', auto, ' — адреса ' + name + '.') : none('После сохранения задан не будет'));
|
||||||
|
} else if (bootKey(now) === bootKey(auto) || (p && bootKey(now) === bootKey(p.boot))) card.appendChild(line('Сейчас: ', now, ' — адреса ' + name + '.'));
|
||||||
|
else card.appendChild(line('Сейчас: ', now, ' — заданы вручную в настройках https-dns-proxy (LuCI), панель их не меняет, пока вы не смените сервер.'));
|
||||||
}
|
}
|
||||||
|
|
||||||
var foCard = E('div', { 'class': 'zm-card', 'style': 'display:none' }), foDraft = null, foPicking = false, foLats = {};
|
var foCard = E('div', { 'class': 'zm-card', 'style': 'display:none' }), foDraft = null, foPicking = false, foLats = {};
|
||||||
@@ -45419,7 +45893,7 @@ return view.extend({
|
|||||||
var diag = null, diagBusy = false, openGroups = {}, catWait = 0;
|
var diag = null, diagBusy = false, openGroups = {}, catWait = 0;
|
||||||
var connKind = null, connRes = null, connBusy = false;
|
var connKind = null, connRes = null, connBusy = false;
|
||||||
var secState = null, secProbe = false, secProbed = false;
|
var secState = null, secProbe = false, secProbed = false;
|
||||||
var dnsRes = {}, dnsTesting = false, dnsInput = null;
|
var dnsRes = {}, dnsVia = '', dnsTesting = false, dnsInput = null;
|
||||||
var tab = 'conn';
|
var tab = 'conn';
|
||||||
try { tab = localStorage.getItem('zm.forkozz.tab') || 'conn'; } catch (e) {}
|
try { tab = localStorage.getItem('zm.forkozz.tab') || 'conn'; } catch (e) {}
|
||||||
if (!TABS.some(function(t) { return t.id === tab; }) || (cfg && !cfg.main_exists)) tab = 'conn';
|
if (!TABS.some(function(t) { return t.id === tab; }) || (cfg && !cfg.main_exists)) tab = 'conn';
|
||||||
@@ -47259,7 +47733,7 @@ return view.extend({
|
|||||||
dnsTesting = false;
|
dnsTesting = false;
|
||||||
if (!res || res.error) { zm.toast((res && res.error) || 'Проверка не удалась', 'error'); renderDns(); return; }
|
if (!res || res.error) { zm.toast((res && res.error) || 'Проверка не удалась', 'error'); renderDns(); return; }
|
||||||
if (res.type !== draft.dns.type) { renderDns(); return; }
|
if (res.type !== draft.dns.type) { renderDns(); return; }
|
||||||
dnsRes = {};
|
dnsRes = {}; dnsVia = res.via || '';
|
||||||
(res.results || []).forEach(function(r) { dnsRes[t + '|' + r.server] = r; });
|
(res.results || []).forEach(function(r) { dnsRes[t + '|' + r.server] = r; });
|
||||||
var good = (res.results || []).filter(function(r) { return r.ok; }).sort(function(a, b) { return a.ms - b.ms; });
|
var good = (res.results || []).filter(function(r) { return r.ok; }).sort(function(a, b) { return a.ms - b.ms; });
|
||||||
if (pick) {
|
if (pick) {
|
||||||
@@ -47277,7 +47751,8 @@ return view.extend({
|
|||||||
function dnsLat(v) {
|
function dnsLat(v) {
|
||||||
var r = dnsRes[draft.dns.type + '|' + v];
|
var r = dnsRes[draft.dns.type + '|' + v];
|
||||||
if (!r) return E('span');
|
if (!r) return E('span');
|
||||||
return E('span', { 'class': 'zm-lat ' + (r.ok ? latClass(r.ms) : 'zm-lat-bad'), 'title': r.how === 'tls' ? 'TLS-соединение есть, ответ DNS проверить не удалось' : '' }, r.ok ? r.ms + ' мс' + (r.how === 'tls' ? '*' : '') : 'не отвечает');
|
return E('span', { 'class': 'zm-lat ' + (r.ok ? latClass(r.ms) : 'zm-lat-bad'), 'title': r.how === 'tls' ? 'TLS-соединение есть, ответ DNS проверить не удалось' : r.how === 'vpn' ? 'Проверено через VPN — так, как Forkozz ходит к этому серверу' : dnsVia === 'direct_detour' ? 'Проверено с роутера напрямую, а Forkozz ходит к серверу через VPN' : '' },
|
||||||
|
r.ok ? r.ms + ' мс' + (r.how === 'tls' ? '*' : '') + (r.how === 'vpn' ? ' · VPN' : '') : (r.how === 'vpn' ? 'не отвечает через VPN' : dnsVia === 'direct_detour' ? 'напрямую не отвечает' : 'не отвечает'));
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderDns() {
|
function renderDns() {
|
||||||
@@ -47298,7 +47773,7 @@ return view.extend({
|
|||||||
ol.appendChild(E('div', { 'class': 'zm-dns-item' + (i === act ? ' zm-dns-active' : '') }, [
|
ol.appendChild(E('div', { 'class': 'zm-dns-item' + (i === act ? ' zm-dns-active' : '') }, [
|
||||||
E('span', { 'class': 'zm-dns-num' }, String(i + 1)),
|
E('span', { 'class': 'zm-dns-num' }, String(i + 1)),
|
||||||
E('div', { 'class': 'zm-dns-text' }, [ E('b', {}, dnsName(t, v)), E('span', {}, v) ]),
|
E('div', { 'class': 'zm-dns-text' }, [ E('b', {}, dnsName(t, v)), E('span', {}, v) ]),
|
||||||
i === act ? badge('zm-ok', sel.length > 1 ? 'сейчас работает' : 'используется') : E([]),
|
i === act ? badge('zm-ok', 'сейчас используется') : E([]),
|
||||||
dnsLat(v),
|
dnsLat(v),
|
||||||
E('div', { 'class': 'zm-dns-btns' }, [
|
E('div', { 'class': 'zm-dns-btns' }, [
|
||||||
E('button', { 'class': 'cbi-button', 'title': 'Выше', 'disabled': i === 0 ? '' : null, 'click': function() { var a = sel.slice(); a.splice(i - 1, 0, a.splice(i, 1)[0]); dnsSet(a, true); } }, '↑'),
|
E('button', { 'class': 'cbi-button', 'title': 'Выше', 'disabled': i === 0 ? '' : null, 'click': function() { var a = sel.slice(); a.splice(i - 1, 0, a.splice(i, 1)[0]); dnsSet(a, true); } }, '↑'),
|
||||||
@@ -47342,7 +47817,8 @@ return view.extend({
|
|||||||
E('button', { 'class': 'cbi-button cbi-button-action', 'disabled': dnsTesting || busy ? '' : null, 'click': function() { dnsTest(true); } }, dnsTesting ? 'Проверяем…' : 'Подобрать лучшие'),
|
E('button', { 'class': 'cbi-button cbi-button-action', 'disabled': dnsTesting || busy ? '' : null, 'click': function() { dnsTest(true); } }, dnsTesting ? 'Проверяем…' : 'Подобрать лучшие'),
|
||||||
E('button', { 'class': 'cbi-button', 'disabled': dnsTesting || busy ? '' : null, 'click': function() { dnsTest(false); } }, 'Только проверить')
|
E('button', { 'class': 'cbi-button', 'disabled': dnsTesting || busy ? '' : null, 'click': function() { dnsTest(false); } }, 'Только проверить')
|
||||||
]));
|
]));
|
||||||
dnsCard.appendChild(E('p', { 'class': 'zm-hint' }, '«Подобрать лучшие» проверит с роутера все серверы ' + DNS_TYPES[t] + ' и поставит в список до 4 отвечающих — самые быстрые первыми.'));
|
dnsCard.appendChild(E('p', { 'class': 'zm-hint' }, '«Подобрать лучшие» проверит все серверы ' + DNS_TYPES[t] + ' и поставит в список до 4 отвечающих — самые быстрые первыми. ' +
|
||||||
|
(d.detour ? (t === 'doh' ? 'Включено «DNS через VPN» — серверы проверяются через VPN, так же, как Forkozz к ним ходит (если Forkozz запущен).' : 'Включено «DNS через VPN», а проверка ' + DNS_TYPES[t] + ' идёт с роутера напрямую: «не отвечает» может значить только, что сервер режет провайдер, — через VPN он может работать.') : 'Проверка идёт с роутера напрямую, как Forkozz и ходит к DNS.')));
|
||||||
if (t !== 'udp') {
|
if (t !== 'udp') {
|
||||||
dnsCard.appendChild(E('h4', { 'style': 'margin:16px 0 4px' }, 'Bootstrap DNS'));
|
dnsCard.appendChild(E('h4', { 'style': 'margin:16px 0 4px' }, 'Bootstrap DNS'));
|
||||||
dnsCard.appendChild(E('p', { 'class': 'zm-hint', 'style': 'margin-top:0' }, 'Обычный DNS, через который Forkozz узнаёт адреса самих серверов ' + DNS_TYPES[t] + ' (например, dns.quad9.net).'));
|
dnsCard.appendChild(E('p', { 'class': 'zm-hint', 'style': 'margin-top:0' }, 'Обычный DNS, через который Forkozz узнаёт адреса самих серверов ' + DNS_TYPES[t] + ' (например, dns.quad9.net).'));
|
||||||
|
|||||||
Reference in new issue
Block a user