Усилить отказоустойчивость загрузки подписок

This commit is contained in:
yandexru45
2026-04-05 15:09:38 +03:00
parent 078aecc9a1
commit c9cf5cb624
2 changed files with 487 additions and 138 deletions

View File

@@ -147,6 +147,213 @@ has_outbound_section() {
return $section_exists
}
get_subscription_json_path() {
local section="$1"
echo "$TMP_SUBSCRIPTION_FOLDER/${section}.json"
}
get_subscription_url_cache_path() {
local section="$1"
echo "$TMP_SUBSCRIPTION_FOLDER/${section}.url"
}
subscription_cache_is_usable() {
local subscription_json_path="$1"
[ -s "$subscription_json_path" ] || return 1
validate_subscription_file "$subscription_json_path"
}
wait_for_subscription_connectivity() {
local section="$1"
local subscription_url="$2"
local service_proxy_address="$3"
local attempts="${4:-12}"
local wait="${5:-5}"
local timeout="${6:-5}"
local attempt
for attempt in $(seq 1 "$attempts"); do
if check_subscription_connectivity "$subscription_url" "$service_proxy_address" 1 0 "$timeout"; then
log "Subscription connectivity check passed for section '$section'" "info"
return 0
fi
log "Subscription source is unavailable for section '$section' [$attempt/$attempts]" "warn"
[ "$attempt" -lt "$attempts" ] && sleep "$wait"
done
log "Subscription connectivity check failed for section '$section' after $attempts attempts" "error"
return 1
}
download_subscription_into_cache() {
local section="$1"
local subscription_url="$2"
local subscription_json_path="$3"
local subscription_url_cache_path="$4"
local service_proxy_address="$5"
local tmpfile
mkdir -p "$TMP_SUBSCRIPTION_FOLDER"
tmpfile="$(mktemp "$TMP_SUBSCRIPTION_FOLDER/${section}.download.XXXXXX")" || return 1
if ! download_subscription "$subscription_url" "$tmpfile" "$service_proxy_address" 3 2 10; then
rm -f "$tmpfile"
return 1
fi
if ! validate_subscription_file "$tmpfile"; then
log "Downloaded subscription for section '$section' is invalid" "error"
rm -f "$tmpfile"
return 1
fi
if [ -f "$subscription_json_path" ] && cmp -s "$tmpfile" "$subscription_json_path"; then
rm -f "$tmpfile"
printf '%s' "$subscription_url" > "$subscription_url_cache_path"
log "Subscription for section '$section' is unchanged" "info"
return 2
fi
mv "$tmpfile" "$subscription_json_path" || {
rm -f "$tmpfile"
return 1
}
printf '%s' "$subscription_url" > "$subscription_url_cache_path"
return 0
}
prepare_subscription_cache_for_startup() {
local section="$1"
local connection_type proxy_config_type subscription_url subscription_json_path subscription_url_cache_path
local cached_subscription_url service_proxy_address had_usable_cache cache_needs_refresh
config_get connection_type "$section" "connection_type"
[ "$connection_type" = "proxy" ] || return 0
config_get proxy_config_type "$section" "proxy_config_type"
[ "$proxy_config_type" = "subscription" ] || return 0
config_get subscription_url "$section" "subscription_url"
if [ -z "$subscription_url" ]; then
log "Subscription URL is not set for section '$section'. Aborted." "fatal"
exit 1
fi
subscription_json_path="$(get_subscription_json_path "$section")"
subscription_url_cache_path="$(get_subscription_url_cache_path "$section")"
cached_subscription_url=""
had_usable_cache=0
cache_needs_refresh=0
if subscription_cache_is_usable "$subscription_json_path"; then
had_usable_cache=1
else
rm -f "$subscription_json_path"
fi
if [ -f "$subscription_url_cache_path" ]; then
cached_subscription_url="$(cat "$subscription_url_cache_path" 2> /dev/null)"
fi
if [ "$had_usable_cache" -eq 0 ] || [ "$cached_subscription_url" != "$subscription_url" ]; then
cache_needs_refresh=1
fi
if [ "$cache_needs_refresh" -eq 0 ]; then
return 0
fi
service_proxy_address="$(get_service_proxy_address 2>/dev/null || echo '')"
if wait_for_subscription_connectivity "$section" "$subscription_url" "$service_proxy_address"; then
if download_subscription_into_cache \
"$section" "$subscription_url" "$subscription_json_path" "$subscription_url_cache_path" "$service_proxy_address"; then
return 0
fi
fi
if [ "$had_usable_cache" -eq 1 ]; then
log "Keeping cached subscription for section '$section' until a fresh download succeeds" "warn"
return 0
fi
log "No usable subscription cache for section '$section'; podkop startup will wait for internet connectivity" "warn"
subscription_startup_blocked=1
return 1
}
prepare_subscription_caches_for_startup() {
subscription_startup_blocked=0
config_foreach prepare_subscription_cache_for_startup "section"
[ "$subscription_startup_blocked" -eq 0 ]
}
stop_subscription_startup_retry_worker() {
local pidfile="/var/run/podkop_subscription_retry.pid"
if [ -f "$pidfile" ]; then
pid="$(cat "$pidfile" 2> /dev/null)"
if [ -n "$pid" ] && kill -0 "$pid" 2> /dev/null; then
kill "$pid" 2> /dev/null
log "Stopped deferred startup recovery worker"
fi
rm -f "$pidfile"
fi
}
start_subscription_startup_retry_worker() {
local pidfile="/var/run/podkop_subscription_retry.pid"
if [ -f "$pidfile" ]; then
pid="$(cat "$pidfile" 2> /dev/null)"
if [ -n "$pid" ] && kill -0 "$pid" 2> /dev/null; then
log "Deferred startup recovery worker is already running" "debug"
return 0
fi
rm -f "$pidfile"
fi
(
trap 'rm -f "'"$pidfile"'"' EXIT INT TERM
while true; do
config_load "$PODKOP_CONFIG"
if prepare_subscription_caches_for_startup; then
log "Subscription cache is ready, resuming deferred podkop startup" "info"
rm -f "$pidfile"
start_main
start_rc=$?
if [ "$start_rc" -eq 0 ]; then
config_get_bool dont_touch_dhcp "settings" "dont_touch_dhcp" 0
if [ "$dont_touch_dhcp" -eq 0 ]; then
dnsmasq_configure
fi
uci_set "podkop" "settings" "shutdown_correctly" 0
uci commit "podkop" && config_load "$PODKOP_CONFIG"
fi
exit "$start_rc"
fi
log "Deferred podkop startup is still waiting for subscription connectivity" "warn"
sleep 10
done
) &
echo $! > "$pidfile"
log "Started deferred startup recovery worker with PID $!" "warn"
}
start_main() {
log "Starting podkop"
@@ -167,6 +374,14 @@ start_main() {
mkdir -p "$TMP_RULESET_FOLDER"
mkdir -p "$TMP_SUBSCRIPTION_FOLDER"
if ! prepare_subscription_caches_for_startup; then
log "Podkop startup is deferred until the subscription source becomes reachable" "warn"
start_subscription_startup_retry_worker
return 2
fi
stop_subscription_startup_retry_worker
# base
route_table_rule_mark
create_nft_rules
@@ -186,6 +401,8 @@ start_main() {
stop_main() {
log "Stopping the podkop"
stop_subscription_startup_retry_worker
if [ -f /var/run/podkop_list_update.pid ]; then
pid=$(cat /var/run/podkop_list_update.pid)
if kill -0 "$pid" 2> /dev/null; then
@@ -220,6 +437,15 @@ stop_main() {
start() {
start_main
start_rc=$?
if [ "$start_rc" -eq 2 ]; then
return 0
fi
if [ "$start_rc" -ne 0 ]; then
return "$start_rc"
fi
config_get_bool dont_touch_dhcp "settings" "dont_touch_dhcp" 0
if [ "$dont_touch_dhcp" -eq 0 ]; then
@@ -245,8 +471,8 @@ stop() {
reload() {
log "Podkop reload"
stop_main
start_main
stop
start
}
restart() {
@@ -587,7 +813,7 @@ ensure_nft_ready_for_list_update() {
list_update() {
echolog "🔄 Starting lists update..."
echolog "рџ”„ Starting lists update..."
local nslookup_timeout=3
local nslookup_attempts=10
@@ -600,7 +826,7 @@ list_update() {
# DNS Check
for i in $(seq 1 $nslookup_attempts); do
if nslookup -timeout=$nslookup_timeout openwrt.org > /dev/null 2>&1; then
echolog " DNS check passed"
echolog "вњ… DNS check passed"
break
fi
echolog "DNS is unavailable [$i/$nslookup_attempts]"
@@ -608,7 +834,7 @@ list_update() {
done
if [ "$i" -eq $nslookup_attempts ]; then
echolog " DNS check failed after $nslookup_attempts attempts"
echolog "вќЊ DNS check failed after $nslookup_attempts attempts"
return 1
fi
@@ -619,12 +845,12 @@ list_update() {
if [ -n "$service_proxy_address" ]; then
if curl -s -x "http://$service_proxy_address" -m $curl_timeout https://github.com > /dev/null; then
echolog " GitHub connection check passed (via proxy)"
echolog "вњ… GitHub connection check passed (via proxy)"
break
fi
else
if curl -s -m $curl_timeout https://github.com > /dev/null; then
echolog " GitHub connection check passed"
echolog "вњ… GitHub connection check passed"
break
fi
fi
@@ -637,16 +863,16 @@ list_update() {
done
if [ "$i" -eq $curl_attempts ]; then
echolog " GitHub connection check failed after $curl_attempts attempts"
echolog "вќЊ GitHub connection check failed after $curl_attempts attempts"
return 1
fi
if ! ensure_nft_ready_for_list_update; then
echolog " NFT table is unavailable, cannot update lists"
echolog "вќЊ NFT table is unavailable, cannot update lists"
return 1
fi
echolog "📥 Downloading and processing lists..."
echolog "рџ“Ґ Downloading and processing lists..."
local update_failed=0
config_foreach import_community_subnet_lists "section" || update_failed=1
@@ -654,17 +880,19 @@ list_update() {
config_foreach import_subnets_from_remote_subnet_lists "section" || update_failed=1
if [ "$update_failed" -eq 0 ]; then
echolog " Lists update completed successfully"
echolog "вњ… Lists update completed successfully"
else
echolog " Lists update failed"
echolog "вќЊ Lists update failed"
return 1
fi
}
subscription_update() {
echolog "🔄 Starting subscription update..."
echolog "рџ”„ Starting subscription update..."
local has_subscription=0
local updated_sections=0
local failed_sections=0
_check_subscription_section() {
local section="$1"
@@ -683,13 +911,14 @@ subscription_update() {
config_foreach _check_subscription_section "section"
if [ "$has_subscription" -eq 0 ]; then
echolog " No subscription sections found, nothing to update"
echolog "в„№пёЏ No subscription sections found, nothing to update"
return 0
fi
_update_subscription_for_section() {
local section="$1"
local connection_type proxy_config_type subscription_url subscription_json_path
local subscription_url_cache_path service_proxy_address update_result outbounds_count
config_get connection_type "$section" "connection_type"
if [ "$connection_type" != "proxy" ]; then
@@ -704,31 +933,44 @@ subscription_update() {
config_get subscription_url "$section" "subscription_url"
if [ -z "$subscription_url" ]; then
echolog " Subscription URL not set for section '$section'"
echolog "вќЊ Subscription URL not set for section '$section'"
failed_sections=$((failed_sections + 1))
return
fi
mkdir -p "$TMP_SUBSCRIPTION_FOLDER"
subscription_json_path="$TMP_SUBSCRIPTION_FOLDER/${section}.json"
local subscription_url_cache_path
subscription_url_cache_path="$TMP_SUBSCRIPTION_FOLDER/${section}.url"
subscription_json_path="$(get_subscription_json_path "$section")"
subscription_url_cache_path="$(get_subscription_url_cache_path "$section")"
echolog "📥 Updating subscription for section '$section'..."
echolog "рџ“Ґ Updating subscription for section '$section'..."
local service_proxy_address
service_proxy_address="$(get_service_proxy_address 2>/dev/null || echo '')"
# Remove old cached file to force re-download
rm -f "$subscription_json_path"
download_subscription "$subscription_url" "$subscription_json_path" "$service_proxy_address"
if [ ! -f "$subscription_json_path" ] || [ ! -s "$subscription_json_path" ]; then
echolog "❌ Failed to download subscription for section '$section'"
if ! wait_for_subscription_connectivity "$section" "$subscription_url" "$service_proxy_address" 6 5 5; then
echolog "вќЊ Failed to download subscription for section '$section'"
failed_sections=$((failed_sections + 1))
return
fi
printf '%s' "$subscription_url" > "$subscription_url_cache_path"
local outbounds_count
download_subscription_into_cache \
"$section" "$subscription_url" "$subscription_json_path" "$subscription_url_cache_path" "$service_proxy_address"
update_result=$?
case "$update_result" in
0)
updated_sections=$((updated_sections + 1))
;;
2)
echolog "в„№пёЏ Subscription for section '$section' is unchanged"
return
;;
*)
echolog "вќЊ Failed to download subscription for section '$section'"
failed_sections=$((failed_sections + 1))
return
;;
esac
outbounds_count=$(jq -r '[.outbounds[] | select(
.type != "selector" and
.type != "urltest" and
@@ -737,13 +979,33 @@ subscription_update() {
.type != "block"
)] | length' "$subscription_json_path" 2>/dev/null)
echolog " Subscription updated for section '$section': $outbounds_count outbounds"
echolog "вњ… Subscription updated for section '$section': $outbounds_count outbounds"
}
config_foreach _update_subscription_for_section "section"
echolog "🔄 Restarting podkop to apply updated subscriptions..."
if [ "$updated_sections" -eq 0 ]; then
if [ "$failed_sections" -gt 0 ]; then
echolog "вќЊ Subscription update finished with errors; keeping the last working cache"
return 1
fi
echolog "в„№пёЏ Subscription update completed: no changes detected"
return 0
fi
echolog "рџ”„ Restarting podkop to apply updated subscriptions..."
restart
echolog "✅ Subscription update completed"
restart_rc=$?
if [ "$restart_rc" -ne 0 ]; then
echolog "вќЊ Subscription was downloaded, but podkop restart failed"
return "$restart_rc"
fi
if [ "$failed_sections" -gt 0 ]; then
echolog "вњ… Subscription update applied for changed sections; failed sections kept their previous cache"
else
echolog "вњ… Subscription update completed"
fi
}
# sing-box funcs
@@ -1002,12 +1264,16 @@ configure_outbound_handler() {
fi
mkdir -p "$TMP_SUBSCRIPTION_FOLDER"
subscription_json_path="$TMP_SUBSCRIPTION_FOLDER/${section}.json"
local subscription_url_cache_path cached_subscription_url should_download
subscription_url_cache_path="$TMP_SUBSCRIPTION_FOLDER/${section}.url"
subscription_json_path="$(get_subscription_json_path "$section")"
local subscription_url_cache_path cached_subscription_url should_download had_usable_cache
subscription_url_cache_path="$(get_subscription_url_cache_path "$section")"
should_download=0
had_usable_cache=0
if [ ! -f "$subscription_json_path" ] || [ ! -s "$subscription_json_path" ]; then
if subscription_cache_is_usable "$subscription_json_path"; then
had_usable_cache=1
else
rm -f "$subscription_json_path"
should_download=1
fi
@@ -1019,24 +1285,30 @@ configure_outbound_handler() {
if [ "$cached_subscription_url" != "$subscription_url" ]; then
if [ -n "$cached_subscription_url" ]; then
log "Subscription URL changed for section '$section', refreshing cache" "debug"
log "Subscription URL changed for section '$section'" "warn"
fi
if [ "$had_usable_cache" -eq 0 ]; then
should_download=1
else
log "Using cached subscription for section '$section' until a fresh download succeeds" "warn"
fi
should_download=1
rm -f "$subscription_json_path"
fi
if [ "$should_download" -eq 1 ]; then
log "Downloading subscription for section '$section'"
local service_proxy_address
service_proxy_address="$(get_service_proxy_address 2>/dev/null || echo '')"
download_subscription "$subscription_url" "$subscription_json_path" "$service_proxy_address"
if [ ! -f "$subscription_json_path" ] || [ ! -s "$subscription_json_path" ]; then
log "Failed to download subscription for section '$section'. Aborted." "fatal"
exit 1
if ! wait_for_subscription_connectivity "$section" "$subscription_url" "$service_proxy_address" 6 5 5 ||
! download_subscription_into_cache \
"$section" "$subscription_url" "$subscription_json_path" "$subscription_url_cache_path" "$service_proxy_address"; then
if [ "$had_usable_cache" -eq 1 ]; then
log "Failed to refresh subscription for section '$section', continuing with cached data" "warn"
else
log "Failed to download subscription for section '$section'. Aborted." "fatal"
exit 1
fi
fi
printf '%s' "$subscription_url" > "$subscription_url_cache_path"
fi
# Parse subscription outbounds
@@ -1169,10 +1441,10 @@ configure_outbound_handler() {
config=$(sing_box_cm_add_interface_outbound "$config" "$outbound_tag" "$interface_name" "$domain_resolver_tag")
;;
block)
log "Connection type 'block' detected for the $section section no outbound will be created (handled via reject route rules)"
log "Connection type 'block' detected for the $section section – no outbound will be created (handled via reject route rules)"
;;
exclusion)
log "Connection type 'exclusion' detected for the $section section no outbound will be created (handled via route rules)"
log "Connection type 'exclusion' detected for the $section section – no outbound will be created (handled via route rules)"
;;
*)
log "Unknown connection type '$connection_type' for the $section section. Aborted." "fatal"
@@ -2133,7 +2405,7 @@ check_nft() {
# Check if table exists
if ! nft list table inet "$NFT_TABLE_NAME" > /dev/null 2>&1; then
nolog " $NFT_TABLE_NAME not found"
nolog "вќЊ $NFT_TABLE_NAME not found"
return 1
fi
@@ -2782,9 +3054,9 @@ global_check() {
local PODKOP_LUCI_VERSION="Unknown"
[ -n "$1" ] && PODKOP_LUCI_VERSION="$1"
print_global "📡 Global check run!"
print_global "━━━━━━━━━━━━━━━━━━━━━━━━━━━"
print_global "🛠️ System info"
print_global "рџ“Ў Global check run!"
print_global "в”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓ"
print_global "рџ› пёЏ System info"
local system_info_json
system_info_json=$(get_system_info)
@@ -2799,17 +3071,17 @@ global_check() {
openwrt_version=$(echo "$system_info_json" | jq -r '.openwrt_version // "unknown"')
device_model=$(echo "$system_info_json" | jq -r '.device_model // "unknown"')
print_global "🕳️ Podkop: $podkop_version (latest: $podkop_latest_version)"
print_global "🕳️ LuCI App: $luci_app_version"
print_global "📦 Sing-box: $sing_box_version"
print_global "🛜 OpenWrt: $openwrt_version"
print_global "🛜 Device: $device_model"
print_global "рџ•іпёЏ Podkop: $podkop_version (latest: $podkop_latest_version)"
print_global "рџ•іпёЏ LuCI App: $luci_app_version"
print_global "📦 Sing-box: $sing_box_version"
print_global "рџ›њ OpenWrt: $openwrt_version"
print_global "рџ›њ Device: $device_model"
else
print_global " Failed to get system info"
print_global "вќЊ Failed to get system info"
fi
print_global "━━━━━━━━━━━━━━━━━━━━━━━━━━━"
print_global "➡️ DNS status"
print_global "в”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓ"
print_global "вћЎпёЏ DNS status"
local dns_check_json
dns_check_json=$(check_dns_available)
@@ -2828,24 +3100,24 @@ global_check() {
# Bootstrap DNS
if [ -n "$bootstrap_dns_server" ]; then
if [ "$bootstrap_dns_status" -eq 1 ]; then
print_global " Bootstrap DNS: $bootstrap_dns_server"
print_global "вњ… Bootstrap DNS: $bootstrap_dns_server"
else
print_global " Bootstrap DNS: $bootstrap_dns_server"
print_global "вќЊ Bootstrap DNS: $bootstrap_dns_server"
fi
fi
# DNS server status
if [ "$dns_status" -eq 1 ]; then
print_global " Main DNS: $dns_server [$dns_type]"
print_global "вњ… Main DNS: $dns_server [$dns_type]"
else
print_global " Main DNS: $dns_server [$dns_type]"
print_global "вќЊ Main DNS: $dns_server [$dns_type]"
fi
# DNS on router
if [ "$dns_on_router" -eq 1 ]; then
print_global " DNS on router"
print_global "вњ… DNS on router"
else
print_global " DNS on router"
print_global "вќЊ DNS on router"
fi
# DHCP configuration check
@@ -2853,20 +3125,20 @@ global_check() {
config_get dont_touch_dhcp "settings" "dont_touch_dhcp"
if [ "$dont_touch_dhcp" = "1" ]; then
print_global "⚠️ dont_touch_dhcp is enabled. 📄 DHCP config:"
print_global "вљ пёЏ dont_touch_dhcp is enabled. рџ“„ DHCP config:"
awk '/^config /{p=($2=="dnsmasq")} p' /etc/config/dhcp
elif [ "$dhcp_config_status" -eq 0 ]; then
print_global " DHCP configuration differs from template. 📄 DHCP config:"
print_global "вќЊ DHCP configuration differs from template. рџ“„ DHCP config:"
awk '/^config /{p=($2=="dnsmasq")} p' /etc/config/dhcp
else
print_global " /etc/config/dhcp"
print_global "вњ… /etc/config/dhcp"
fi
else
print_global " Failed to get DNS info"
print_global "вќЊ Failed to get DNS info"
fi
print_global "━━━━━━━━━━━━━━━━━━━━━━━━━━━"
print_global "📦 Sing-box status"
print_global "в”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓ"
print_global "📦 Sing-box status"
local singbox_check_json
singbox_check_json=$(check_sing_box)
@@ -2882,46 +3154,46 @@ global_check() {
sing_box_ports_listening=$(echo "$singbox_check_json" | jq -r '.sing_box_ports_listening // 0')
if [ "$sing_box_installed" -eq 1 ]; then
print_global " Sing-box installed"
print_global "вњ… Sing-box installed"
else
print_global " Sing-box installed"
print_global "вќЊ Sing-box installed"
fi
if [ "$sing_box_version_ok" -eq 1 ]; then
print_global " Sing-box version is compatible (newer than 1.12.4)"
print_global "вњ… Sing-box version is compatible (newer than 1.12.4)"
else
print_global " Sing-box version is not compatible (older than 1.12.4)"
print_global "вќЊ Sing-box version is not compatible (older than 1.12.4)"
fi
if [ "$sing_box_service_exist" -eq 1 ]; then
print_global " Sing-box service exist"
print_global "вњ… Sing-box service exist"
else
print_global " Sing-box service exist"
print_global "вќЊ Sing-box service exist"
fi
if [ "$sing_box_autostart_disabled" -eq 1 ]; then
print_global " Sing-box autostart disabled"
print_global "вњ… Sing-box autostart disabled"
else
print_global " Sing-box autostart disabled"
print_global "вќЊ Sing-box autostart disabled"
fi
if [ "$sing_box_process_running" -eq 1 ]; then
print_global " Sing-box process running"
print_global "вњ… Sing-box process running"
else
print_global " Sing-box process running"
print_global "вќЊ Sing-box process running"
fi
if [ "$sing_box_ports_listening" -eq 1 ]; then
print_global " Sing-box listening ports"
print_global "вњ… Sing-box listening ports"
else
print_global " Sing-box listening ports"
print_global "вќЊ Sing-box listening ports"
fi
else
print_global " Failed to get sing-box info"
print_global "вќЊ Failed to get sing-box info"
fi
print_global "━━━━━━━━━━━━━━━━━━━━━━━━━━━"
print_global "🧱 NFT rules status"
print_global "в”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓ"
print_global "рџ§± NFT rules status"
local nft_check_json
nft_check_json=$(check_nft_rules)
@@ -2939,78 +3211,78 @@ global_check() {
rules_other_mark_exist=$(echo "$nft_check_json" | jq -r '.rules_other_mark_exist // 0')
if [ "$table_exist" -eq 1 ]; then
print_global " Table exist"
print_global "вњ… Table exist"
else
print_global " Table exist"
print_global "вќЊ Table exist"
fi
if [ "$rules_mangle_exist" -eq 1 ]; then
print_global " Rules mangle exist"
print_global "вњ… Rules mangle exist"
else
print_global " Rules mangle exist"
print_global "вќЊ Rules mangle exist"
fi
if [ "$rules_mangle_counters" -eq 1 ]; then
print_global " Rules mangle counters"
print_global "вњ… Rules mangle counters"
else
print_global "⚠️ Rules mangle counters"
print_global "вљ пёЏ Rules mangle counters"
fi
if [ "$rules_mangle_output_exist" -eq 1 ]; then
print_global " Rules mangle output exist"
print_global "вњ… Rules mangle output exist"
else
print_global " Rules mangle output exist"
print_global "вќЊ Rules mangle output exist"
fi
if [ "$rules_mangle_output_counters" -eq 1 ]; then
print_global " Rules mangle output counters"
print_global "вњ… Rules mangle output counters"
else
print_global "⚠️ Rules mangle output counters"
print_global "вљ пёЏ Rules mangle output counters"
fi
if [ "$rules_proxy_exist" -eq 1 ]; then
print_global " Rules proxy exist"
print_global "вњ… Rules proxy exist"
else
print_global " Rules proxy exist"
print_global "вќЊ Rules proxy exist"
fi
if [ "$rules_proxy_counters" -eq 1 ]; then
print_global " Rules proxy counters"
print_global "вњ… Rules proxy counters"
else
print_global "⚠️ Rules proxy counters"
print_global "вљ пёЏ Rules proxy counters"
fi
if [ "$rules_other_mark_exist" -eq 1 ]; then
print_global "⚠️ Additional marking rules found:"
print_global "вљ пёЏ Additional marking rules found:"
nft list ruleset | awk '/table inet '"$NFT_TABLE_NAME"'/{flag=1; next} /^table/{flag=0} !flag' | grep -E "mark set|meta mark"
else
print_global " Additional marking rules found"
print_global "вњ… Additional marking rules found"
fi
else
print_global " Failed to get NFT rules info"
print_global "вќЊ Failed to get NFT rules info"
fi
print_global "━━━━━━━━━━━━━━━━━━━━━━━━━━━"
print_global "📄 Podkop config"
print_global "в”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓ"
print_global "рџ“„ Podkop config"
show_config
# print_global "━━━━━━━━━━━━━━━━━━━━━━━━━━━"
# print_global "🔧 System check"
# print_global "в”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓ"
# print_global "рџ”§ System check"
# if grep -E "^nameserver\s+([0-9]{1,3}\.){3}[0-9]{1,3}" "$RESOLV_CONF" | grep -vqE "127\.0\.0\.1|0\.0\.0\.0"; then
# print_global " /etc/resolv.conf contains external nameserver:"
# print_global "вќЊ /etc/resolv.conf contains external nameserver:"
# cat /etc/resolv.conf
# echo ""
# else
# print_global " /etc/resolv.conf"
# print_global "вњ… /etc/resolv.conf"
# fi
# print_global "━━━━━━━━━━━━━━━━━━━━━━━━━━━"
# print_global "🧱 NFT table"
# print_global "в”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓ"
# print_global "рџ§± NFT table"
# check_nft
print_global "━━━━━━━━━━━━━━━━━━━━━━━━━━━"
print_global "📄 WAN config"
print_global "в”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓ"
print_global "рџ“„ WAN config"
if uci show network.wan > /dev/null 2>&1; then
awk '
/^config / {
@@ -3031,20 +3303,20 @@ global_check() {
}
' /etc/config/network
else
print_global " WAN configuration not found"
print_global "вќЊ WAN configuration not found"
fi
if uci show network | grep -q endpoint_host; then
uci show network | grep endpoint_host | cut -d'=' -f2 | tr -d "'\" " | while read -r host; do
if [ "$host" = "engage.cloudflareclient.com" ]; then
print_global "⚠️ WARP detected: $host"
print_global "вљ пёЏ WARP detected: $host"
continue
fi
ip_prefix=$(echo "$host" | cut -d'.' -f1,2)
if echo "$CLOUDFLARE_OCTETS" | grep -wq "$ip_prefix"; then
print_global "━━━━━━━━━━━━━━━━━━━━━━━━━━━"
print_global "⚠️ WARP detected: $host"
print_global "в”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓ"
print_global "вљ пёЏ WARP detected: $host"
fi
done
fi
@@ -3055,19 +3327,19 @@ global_check() {
allowed_ips=$(uci get "${peer_section}.allowed_ips" 2> /dev/null)
if [ "$allowed_ips" = "0.0.0.0/0" ]; then
print_global "━━━━━━━━━━━━━━━━━━━━━━━━━━━"
print_global "⚠️ WG Route allowed IP enabled with 0.0.0.0/0"
print_global "в”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓ"
print_global "вљ пёЏ WG Route allowed IP enabled with 0.0.0.0/0"
fi
done
fi
if [ -f "/etc/init.d/zapret" ]; then
print_global "━━━━━━━━━━━━━━━━━━━━━━━━━━━"
print_global "⚠️ Zapret detected"
print_global "в”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓ"
print_global "вљ пёЏ Zapret detected"
fi
print_global "━━━━━━━━━━━━━━━━━━━━━━━━━━━"
print_global "🥸 FakeIP status"
print_global "в”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓ"
print_global "🥸 FakeIP status"
local fakeip_check_json
fakeip_check_json=$(check_fakeip)
@@ -3078,21 +3350,21 @@ global_check() {
fakeip_status=$(echo "$fakeip_check_json" | jq -r '.fakeip // false')
if [ "$fakeip_status" = "true" ]; then
print_global " Router DNS is routed through sing-box"
print_global "вњ… Router DNS is routed through sing-box"
else
print_global "⚠️ Router DNS is NOT routed through sing-box"
print_global "вљ пёЏ Router DNS is NOT routed through sing-box"
fi
else
print_global " Failed to get FakeIP info"
print_global "вќЊ Failed to get FakeIP info"
fi
local fakeip_address
fakeip_address=$(dig +short @127.0.0.42 $FAKEIP_TEST_DOMAIN)
if echo "$fakeip_address" | grep -q "^198\.18\."; then
print_global " Sing-box works with FakeIP: $fakeip_address"
print_global "вњ… Sing-box works with FakeIP: $fakeip_address"
else
print_global " Sing-box does NOT work with FakeIP: $fakeip_address"
print_global "вќЊ Sing-box does NOT work with FakeIP: $fakeip_address"
fi
}

View File

@@ -417,6 +417,7 @@ download_subscription() {
local http_proxy_address="$3"
local retries="${4:-3}"
local wait="${5:-2}"
local timeout="${6:-10}"
local sb_version device_model kernel_version hwid
sb_version="$(get_sing_box_version)"
@@ -424,24 +425,100 @@ download_subscription() {
kernel_version="$(get_kernel_version)"
hwid="$(generate_hwid)"
local header_args=""
header_args="--header='User-Agent: singbox/$sb_version'"
header_args="$header_args --header='X-HWID: $hwid'"
header_args="$header_args --header='X-Device-OS: OpenWrt Linux'"
header_args="$header_args --header='X-Device-Model: $device_model'"
header_args="$header_args --header='X-Ver-OS: $kernel_version'"
header_args="$header_args --header='Accept-Language: ru-RU,en,*'"
header_args="$header_args --header='X-Device-Locale: EN'"
local tmpfile
tmpfile="${filepath}.part.$$"
rm -f "$tmpfile"
for attempt in $(seq 1 "$retries"); do
if [ -n "$http_proxy_address" ]; then
http_proxy="http://$http_proxy_address" https_proxy="http://$http_proxy_address" \
eval wget -O "$filepath" $header_args "$url" && break
wget -T "$timeout" -t 1 -O "$tmpfile" \
--header "User-Agent: singbox/$sb_version" \
--header "X-HWID: $hwid" \
--header "X-Device-OS: OpenWrt Linux" \
--header "X-Device-Model: $device_model" \
--header "X-Ver-OS: $kernel_version" \
--header "Accept-Language: ru-RU,en,*" \
--header "X-Device-Locale: EN" \
"$url"
else
eval wget -O "$filepath" $header_args "$url" && break
wget -T "$timeout" -t 1 -O "$tmpfile" \
--header "User-Agent: singbox/$sb_version" \
--header "X-HWID: $hwid" \
--header "X-Device-OS: OpenWrt Linux" \
--header "X-Device-Model: $device_model" \
--header "X-Ver-OS: $kernel_version" \
--header "Accept-Language: ru-RU,en,*" \
--header "X-Device-Locale: EN" \
"$url"
fi
if [ $? -eq 0 ] && [ -s "$tmpfile" ]; then
mv "$tmpfile" "$filepath"
return 0
fi
rm -f "$tmpfile"
log "Attempt $attempt/$retries to download subscription from $url failed" "warn"
sleep "$wait"
done
}
rm -f "$tmpfile"
return 1
}
check_subscription_connectivity() {
local url="$1"
local http_proxy_address="$2"
local retries="${3:-3}"
local wait="${4:-2}"
local timeout="${5:-5}"
local sb_version device_model kernel_version hwid
sb_version="$(get_sing_box_version)"
device_model="$(get_device_model)"
kernel_version="$(get_kernel_version)"
hwid="$(generate_hwid)"
local attempt
for attempt in $(seq 1 "$retries"); do
if [ -n "$http_proxy_address" ]; then
http_proxy="http://$http_proxy_address" https_proxy="http://$http_proxy_address" \
wget -q -T "$timeout" -t 1 -O /dev/null \
--header "User-Agent: singbox/$sb_version" \
--header "X-HWID: $hwid" \
--header "X-Device-OS: OpenWrt Linux" \
--header "X-Device-Model: $device_model" \
--header "X-Ver-OS: $kernel_version" \
--header "Accept-Language: ru-RU,en,*" \
--header "X-Device-Locale: EN" \
"$url" && return 0
else
wget -q -T "$timeout" -t 1 -O /dev/null \
--header "User-Agent: singbox/$sb_version" \
--header "X-HWID: $hwid" \
--header "X-Device-OS: OpenWrt Linux" \
--header "X-Device-Model: $device_model" \
--header "X-Ver-OS: $kernel_version" \
--header "Accept-Language: ru-RU,en,*" \
--header "X-Device-Locale: EN" \
"$url" && return 0
fi
[ "$attempt" -lt "$retries" ] && sleep "$wait"
done
return 1
}
validate_subscription_file() {
local filepath="$1"
[ -s "$filepath" ] || return 1
jq -e '
type == "object" and
(.outbounds | type == "array") and
((.outbounds | length) > 0)
' "$filepath" > /dev/null 2>&1
}