mirror of
https://github.com/necronicle/z2k.git
synced 2026-04-28 11:30:30 +00:00
Some Russian ISPs (MGTS, Rostelecom recent) intermittently block
raw.githubusercontent.com via DNS poisoning or SNI filter, making the
first curl in z2k.sh fail and the entire install abort. Borrow z4r's
DNS-override trick, stack three mirrors in front of it.
New z2k_fetch() wrapper, defined inline in z2k.sh (bootstrap-safe) and
duplicated in lib/utils.sh (for modules). Tries in order:
1. raw.githubusercontent.com — primary
2. cdn.jsdelivr.net/gh/<owner>/<repo>@<branch>/<p> — CDN, 12h edge
cache (purgeable via purge.jsdelivr.net/gh/...)
3. gh-proxy.com/<raw-url> — reverse-proxy,
no cache, works from RU
4. Keenetic-only: nslookup <host> 8.8.8.8 + ndmc "ip host <host> <ip>"
then retry layers 1+2 — mirrors zapret4rocket z4r.sh:1075-1107.
Replaces every direct curl to raw.github or ${GITHUB_RAW}/... across
z2k.sh (14 sites), lib/install.sh (4 sites: two AloofLibra/zapret4rocket
lua/orchestrator fetches + two bol-van/zapret2 custom.d examples),
lib/menu.sh (tg-mtproxy binary download), and the standalone cron
files/z2k-update-lists.sh (which gets its own inline copy of the
function — it is not source'd via utils.sh).
Smoke-tested: all three delivering layers fetch the same 29272-byte
z2k.sh, and a bogus path correctly falls through all layers and
returns 1. Layer 4 (ndmc) not tested on the Mac — semantics copied
verbatim from z4r, which proves it on Keenetic in production.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
222 lines
7.6 KiB
Bash
222 lines
7.6 KiB
Bash
#!/bin/sh
|
||
# z2k-update-lists.sh - Автоматическое обновление списков доменов
|
||
# Предназначен для вызова из cron: 0 4 * * * sh /opt/zapret2/z2k-update-lists.sh
|
||
#
|
||
# При обнаружении изменений автоматически перезапускает сервис.
|
||
|
||
ZAPRET2_DIR="/opt/zapret2"
|
||
INIT_SCRIPT="/opt/etc/init.d/S99zapret2"
|
||
LOG_FILE="${ZAPRET2_DIR}/update-lists.log"
|
||
MAX_LOG_LINES=200
|
||
|
||
GITHUB_RAW="https://raw.githubusercontent.com/necronicle/z2k/master"
|
||
|
||
# ==============================================================================
|
||
# z2k_fetch — загрузка файла с GitHub через цепочку зеркал.
|
||
# ==============================================================================
|
||
# Дублирует логику z2k.sh / lib/utils.sh для standalone cron-запуска (этот
|
||
# скрипт не source'ит utils.sh). Слои: raw.github → jsdelivr → gh-proxy →
|
||
# Keenetic DNS override через 8.8.8.8 + ndmc.
|
||
z2k_fetch() {
|
||
local src="$1"
|
||
local dest="$2"
|
||
local url
|
||
|
||
case "$src" in
|
||
http://*|https://*) url="$src" ;;
|
||
/*) url="${GITHUB_RAW}${src}" ;;
|
||
*) url="${GITHUB_RAW}/${src}" ;;
|
||
esac
|
||
|
||
local jsdelivr="" gh_proxy=""
|
||
case "$url" in
|
||
https://raw.githubusercontent.com/*)
|
||
local _rest="${url#https://raw.githubusercontent.com/}"
|
||
local _owner="${_rest%%/*}"; _rest="${_rest#*/}"
|
||
local _repo="${_rest%%/*}"; _rest="${_rest#*/}"
|
||
local _branch="${_rest%%/*}"; _rest="${_rest#*/}"
|
||
jsdelivr="https://cdn.jsdelivr.net/gh/${_owner}/${_repo}@${_branch}/${_rest}"
|
||
gh_proxy="https://gh-proxy.com/${url}"
|
||
;;
|
||
esac
|
||
|
||
if curl -fsSL --connect-timeout 10 --max-time 180 -o "$dest" "$url" 2>/dev/null; then
|
||
return 0
|
||
fi
|
||
if [ -n "$jsdelivr" ] && \
|
||
curl -fsSL --connect-timeout 10 --max-time 180 -o "$dest" "$jsdelivr" 2>/dev/null; then
|
||
return 0
|
||
fi
|
||
if [ -n "$gh_proxy" ] && \
|
||
curl -fsSL --connect-timeout 10 --max-time 180 -o "$dest" "$gh_proxy" 2>/dev/null; then
|
||
return 0
|
||
fi
|
||
|
||
if command -v ndmc >/dev/null 2>&1 && command -v nslookup >/dev/null 2>&1; then
|
||
local resolved_any=0 host ip
|
||
for host in raw.githubusercontent.com cdn.jsdelivr.net api.github.com; do
|
||
ip=$(nslookup "$host" 8.8.8.8 2>/dev/null \
|
||
| awk '/^Name:/ {s=1; next} s && /^Address [0-9]+: [0-9]+\./ {print $3; exit}')
|
||
if [ -n "$ip" ] && [ "$ip" != "127.0.0.1" ] && [ "$ip" != "8.8.8.8" ]; then
|
||
ndmc -c "ip host $host $ip" >/dev/null 2>&1 && resolved_any=1
|
||
fi
|
||
done
|
||
if [ "$resolved_any" = "1" ]; then
|
||
sleep 1
|
||
if curl -fsSL --connect-timeout 10 --max-time 180 -o "$dest" "$url" 2>/dev/null; then
|
||
return 0
|
||
fi
|
||
if [ -n "$jsdelivr" ] && \
|
||
curl -fsSL --connect-timeout 10 --max-time 180 -o "$dest" "$jsdelivr" 2>/dev/null; then
|
||
return 0
|
||
fi
|
||
fi
|
||
fi
|
||
|
||
return 1
|
||
}
|
||
|
||
# ==============================================================================
|
||
# ЛОГИРОВАНИЕ
|
||
# ==============================================================================
|
||
|
||
log_msg() {
|
||
local msg
|
||
msg="$(date '+%Y-%m-%d %H:%M:%S') $1"
|
||
echo "$msg" >> "$LOG_FILE" 2>/dev/null
|
||
|
||
# Ротация лога
|
||
if [ -f "$LOG_FILE" ]; then
|
||
local lines
|
||
lines=$(wc -l < "$LOG_FILE" 2>/dev/null || echo 0)
|
||
if [ "$lines" -gt "$MAX_LOG_LINES" ]; then
|
||
local tmp
|
||
tmp=$(mktemp "${LOG_FILE}.XXXXXX") || return
|
||
tail -n "$((MAX_LOG_LINES / 2))" "$LOG_FILE" > "$tmp" 2>/dev/null
|
||
mv -f "$tmp" "$LOG_FILE" 2>/dev/null || rm -f "$tmp"
|
||
fi
|
||
fi
|
||
}
|
||
|
||
# ==============================================================================
|
||
# ОБНОВЛЕНИЕ СПИСКОВ
|
||
# ==============================================================================
|
||
|
||
update_list() {
|
||
local name=$1
|
||
local url=$2
|
||
local dest=$3
|
||
|
||
if [ -z "$url" ] || [ -z "$dest" ]; then
|
||
return 1
|
||
fi
|
||
|
||
local tmp
|
||
tmp=$(mktemp "${dest}.XXXXXX") || return 1
|
||
|
||
if ! z2k_fetch "$url" "$tmp"; then
|
||
log_msg "FAIL: download $name from $url (all mirrors failed)"
|
||
rm -f "$tmp"
|
||
return 1
|
||
fi
|
||
|
||
# Проверить что файл не пустой
|
||
if [ ! -s "$tmp" ]; then
|
||
log_msg "FAIL: $name is empty"
|
||
rm -f "$tmp"
|
||
return 1
|
||
fi
|
||
|
||
# Убрать CRLF
|
||
sed -i 's/\r$//' "$tmp" 2>/dev/null
|
||
|
||
# Сравнить с текущим
|
||
if [ -f "$dest" ]; then
|
||
local old_hash new_hash
|
||
if command -v md5sum >/dev/null 2>&1; then
|
||
old_hash=$(md5sum "$dest" 2>/dev/null | awk '{print $1}')
|
||
new_hash=$(md5sum "$tmp" 2>/dev/null | awk '{print $1}')
|
||
else
|
||
old_hash=$(wc -c < "$dest" 2>/dev/null)
|
||
new_hash=$(wc -c < "$tmp" 2>/dev/null)
|
||
fi
|
||
|
||
if [ "$old_hash" = "$new_hash" ]; then
|
||
rm -f "$tmp"
|
||
return 0 # Без изменений
|
||
fi
|
||
fi
|
||
|
||
# Обновить
|
||
mkdir -p "$(dirname "$dest")" 2>/dev/null
|
||
mv -f "$tmp" "$dest"
|
||
log_msg "OK: $name updated ($(wc -l < "$dest") lines)"
|
||
return 2 # Код 2 = есть изменения
|
||
}
|
||
|
||
# ==============================================================================
|
||
# ОСНОВНОЙ ПРОЦЕСС
|
||
# ==============================================================================
|
||
|
||
main() {
|
||
# Убедиться что директория для логов существует
|
||
mkdir -p "$(dirname "$LOG_FILE")" 2>/dev/null
|
||
|
||
log_msg "--- Update lists started ---"
|
||
|
||
local changes=0
|
||
|
||
# RKN список
|
||
update_list "RKN" \
|
||
"${GITHUB_RAW}/files/lists/extra_strats/TCP/RKN/List.txt" \
|
||
"${ZAPRET2_DIR}/extra_strats/TCP/RKN/List.txt"
|
||
[ $? -eq 2 ] && changes=$((changes + 1))
|
||
|
||
# Discord
|
||
update_list "Discord" \
|
||
"${GITHUB_RAW}/files/lists/extra_strats/TCP/RKN/Discord.txt" \
|
||
"${ZAPRET2_DIR}/extra_strats/TCP/RKN/Discord.txt"
|
||
[ $? -eq 2 ] && changes=$((changes + 1))
|
||
|
||
# YouTube TCP
|
||
update_list "YouTube TCP" \
|
||
"${GITHUB_RAW}/files/lists/extra_strats/TCP/YT/List.txt" \
|
||
"${ZAPRET2_DIR}/extra_strats/TCP/YT/List.txt"
|
||
[ $? -eq 2 ] && changes=$((changes + 1))
|
||
|
||
# YouTube UDP/QUIC
|
||
update_list "YouTube QUIC" \
|
||
"${GITHUB_RAW}/files/lists/extra_strats/UDP/YT/List.txt" \
|
||
"${ZAPRET2_DIR}/extra_strats/UDP/YT/List.txt"
|
||
[ $? -eq 2 ] && changes=$((changes + 1))
|
||
|
||
# Roblox IPs (legacy path — kept for rollback safety, new installs use game_ips.txt)
|
||
update_list "Roblox IPs" \
|
||
"${GITHUB_RAW}/files/lists/roblox_ips.txt" \
|
||
"${ZAPRET2_DIR}/lists/roblox_ips.txt"
|
||
[ $? -eq 2 ] && changes=$((changes + 1))
|
||
|
||
# Game IPs (Roblox AS22697 — used as positive --ipset by the game UDP profile)
|
||
update_list "Game IPs" \
|
||
"${GITHUB_RAW}/files/lists/game_ips.txt" \
|
||
"${ZAPRET2_DIR}/lists/game_ips.txt"
|
||
[ $? -eq 2 ] && changes=$((changes + 1))
|
||
|
||
if [ "$changes" -gt 0 ]; then
|
||
log_msg "Changes detected ($changes lists), restarting service..."
|
||
if [ -x "$INIT_SCRIPT" ]; then
|
||
"$INIT_SCRIPT" restart 2>/dev/null
|
||
if [ $? -eq 0 ]; then
|
||
log_msg "Service restarted successfully"
|
||
else
|
||
log_msg "FAIL: Service restart failed"
|
||
fi
|
||
fi
|
||
else
|
||
log_msg "No changes detected"
|
||
fi
|
||
|
||
log_msg "--- Update lists finished ---"
|
||
}
|
||
|
||
main "$@"
|