💋 Sync 2026-10-11 00:16:14
Merge-upstream / merge (push) Canceled after 0s

This commit is contained in:
github-actions[bot] committed 2026-10-11 00:16:14 +08:00
1 parent c7bafc635d
commit c71da84035
264 files changed
+8169 -3944

No files matched your search

@@ -153,21 +153,25 @@ resolve_via() {
}
curl_subscription() {
local url tmp hdr err ua extra_resolve http_code rc
local url tmp hdr err ua host ip http_code rc
url="$1"
tmp="$2"
hdr="$3"
err="$4"
ua="$5"
extra_resolve="$6"
host="$6"
ip="$7"
rm -f "$tmp" "$hdr" "$err" >/dev/null 2>&1
# shellcheck disable=SC2086
set --
if [ -n "$host" ] && [ -n "$ip" ]; then
set -- --resolve "$host:443:$ip" --resolve "$host:80:$ip"
fi
http_code="$(curl -sSL --connect-timeout 15 --max-time 60 \
--speed-time 30 --speed-limit 1 --retry 2 \
-H "User-Agent: ${ua}" -D "$hdr" -o "$tmp" \
$extra_resolve \
"$@" \
-w '%{http_code}' "$url" 2>"$err")"
rc=$?
printf '%s\n' "$http_code"
@@ -176,7 +180,7 @@ curl_subscription() {
download_subscription() {
local url target tmp hdr err rc http_code info_line ua err_msg
local host ip dns extra
local host ip dns
url="$1"
target="$2"
tmp="${TMP_PREFIX}.yaml"
@@ -187,25 +191,27 @@ download_subscription() {
[ -n "$ua" ] || ua="$DEFAULT_SUB_UA"
if command -v curl >/dev/null 2>&1; then
http_code="$(curl_subscription "$url" "$tmp" "$hdr" "$err" "$ua" "")"
http_code="$(curl_subscription "$url" "$tmp" "$hdr" "$err" "$ua" "" "")"
rc=$?
if [ "$rc" -ne 0 ] || [ "$http_code" = "000" ]; then
host="$(extract_host "$url")"
case "$host" in
''|[-.]*|*[!A-Za-z0-9.-]*) host='' ;;
esac
if [ -n "$host" ]; then
for dns in 223.5.5.5 119.29.29.29 1.1.1.1 8.8.8.8; do
ip="$(resolve_via "$host" "$dns")"
[ -n "$ip" ] || continue
log_update "DNS 回退:${host} -> ${ip} (@${dns})"
extra="--resolve ${host}:443:${ip} --resolve ${host}:80:${ip}"
http_code="$(curl_subscription "$url" "$tmp" "$hdr" "$err" "$ua" "$extra")"
http_code="$(curl_subscription "$url" "$tmp" "$hdr" "$err" "$ua" "$host" "$ip")"
rc=$?
[ "$rc" -eq 0 ] && [ "$http_code" = "200" ] && break
done
fi
fi
else
wget -q --tries=4 --timeout=20 \
wget -q --timeout=20 \
--user-agent="$ua" "$url" -O "$tmp" 2>"$err"
rc=$?
http_code=""
@@ -7,15 +7,21 @@ SECRET=$(uci get clashoo.config.dash_pass 2>/dev/null)
LAN_IP=$(uci get network.lan.ipaddr 2>/dev/null |awk -F '/' '{print $1}' 2>/dev/null)
PORT=$(uci get clashoo.config.dash_port 2>/dev/null)
urlencode() {
local data
if [ "$#" != 1 ]; then
return 1
fi
data=$(curl -s -o /dev/null -w %{url_effective} --get --data-urlencode "$1" "")
if [ ! -z "$data" ]; then
echo "${data##/?}"
fi
return 0
printf '%s' "$1" | hexdump -v -e '1/1 "%u "' | awk '
{
for (i = 1; i <= NF; i++) {
b = $i + 0
if ((b >= 48 && b <= 57) || (b >= 65 && b <= 90) ||
(b >= 97 && b <= 122) || b == 45 || b == 46 ||
b == 95 || b == 126)
printf "%c", b
else
printf "%%%02X", b
}
}'
}
cat $HISTORY_PATH |while read line
do
@@ -28,4 +34,4 @@ do
fi
done >/dev/null 2>&1
curl -m 5 --retry 2 -H "Authorization: Bearer ${SECRET}" -H "Content-Type:application/json" -X DELETE http://"$LAN_IP":"$PORT"/connections >/dev/null 2>&1
fi
fi
@@ -58,8 +58,8 @@ fetch_text() {
return $?
fi
if command -v wget >/dev/null 2>&1; then
[ -n "$proxy" ] && { http_proxy="$proxy" https_proxy="$proxy" wget -qO- --timeout="$REQUEST_TIMEOUT" --tries=1 "$url"; return $?; }
wget -qO- --timeout="$REQUEST_TIMEOUT" --tries=1 "$url"
[ -n "$proxy" ] && { http_proxy="$proxy" https_proxy="$proxy" wget -qO- --timeout="$REQUEST_TIMEOUT" "$url"; return $?; }
wget -qO- --timeout="$REQUEST_TIMEOUT" "$url"
return $?
fi
return 127
@@ -74,10 +74,10 @@ download_file() {
return $?
fi
if [ -n "$proxy" ]; then
http_proxy="$proxy" https_proxy="$proxy" wget -qO "$out" --timeout="$REQUEST_TIMEOUT" --tries=1 "$url"
http_proxy="$proxy" https_proxy="$proxy" wget -qO "$out" --timeout="$REQUEST_TIMEOUT" "$url"
return $?
fi
wget -qO "$out" --timeout="$REQUEST_TIMEOUT" --tries=1 "$url"
wget -qO "$out" --timeout="$REQUEST_TIMEOUT" "$url"
}
download_url() {
@@ -176,7 +176,7 @@ fetch_url_try() {
return $?
fi
if command -v wget >/dev/null 2>&1; then
wget -qO- --timeout="$CONNECT_TIMEOUT" --no-check-certificate --user-agent="Clash/OpenWRT" "$url"
wget -qO- --timeout="$CONNECT_TIMEOUT" --user-agent="Clash/OpenWRT" "$url"
return $?
fi
return 127
@@ -204,7 +204,7 @@ download_file_try() {
return $?
fi
if command -v wget >/dev/null 2>&1; then
wget -q --timeout="$REQUEST_TIMEOUT" --tries=1 --no-check-certificate --user-agent="Clash/OpenWRT" "$url" -O "$out"
wget -q --timeout="$REQUEST_TIMEOUT" --user-agent="Clash/OpenWRT" "$url" -O "$out"
return $?
fi
return 127
@@ -216,7 +216,7 @@ head_url_try() {
curl -fsSIL --connect-timeout "$CONNECT_TIMEOUT" --max-time "$REQUEST_TIMEOUT" -A "Clash/OpenWRT" "$url" >/dev/null 2>&1
return $?
fi
wget -q --spider --timeout="$CONNECT_TIMEOUT" --no-check-certificate --user-agent="Clash/OpenWRT" "$url"
wget -q --spider --timeout="$CONNECT_TIMEOUT" --user-agent="Clash/OpenWRT" "$url"
}
map_mihomo_arch() {
@@ -637,7 +637,7 @@ fetch_latest_tag() {
if command -v curl >/dev/null 2>&1; then
tag="$(curl -fsSIL --connect-timeout "$CONNECT_TIMEOUT" --max-time "$REQUEST_TIMEOUT" -A "Clash/OpenWRT" "$u" 2>/dev/null | sed -n 's#^[Ll]ocation: .*/releases/tag/\([^[:space:]\r]*\).*#\1#p' | head -n 1)"
else
tag="$(wget -S --spider --timeout="$CONNECT_TIMEOUT" --no-check-certificate --user-agent="Clash/OpenWRT" "$u" 2>&1 | sed -n 's#^ Location: .*/releases/tag/\([^[:space:]]*\).*#\1#p' | head -n 1)"
tag="$(wget -S --spider --timeout="$CONNECT_TIMEOUT" --user-agent="Clash/OpenWRT" "$u" 2>&1 | sed -n 's#^ Location: .*/releases/tag/\([^[:space:]]*\).*#\1#p' | head -n 1)"
fi
case "$tag" in
v*[-]*)
@@ -684,7 +684,7 @@ fetch_latest_tag() {
if command -v curl >/dev/null 2>&1; then
tag="$(curl -fsSIL --connect-timeout "$CONNECT_TIMEOUT" --max-time "$REQUEST_TIMEOUT" -A "Clash/OpenWRT" "$u" 2>/dev/null | sed -n 's#^[Ll]ocation: .*/releases/tag/\([^[:space:][:cntrl:]]*\).*#\1#p' | head -n 1)"
else
tag="$(wget -S --spider --timeout="$CONNECT_TIMEOUT" --no-check-certificate --user-agent="Clash/OpenWRT" "$u" 2>&1 | sed -n 's#^ Location: .*/releases/tag/\([^[:space:]]*\).*#\1#p' | head -n 1)"
tag="$(wget -S --spider --timeout="$CONNECT_TIMEOUT" --user-agent="Clash/OpenWRT" "$u" 2>&1 | sed -n 's#^ Location: .*/releases/tag/\([^[:space:]]*\).*#\1#p' | head -n 1)"
fi
[ -n "$tag" ] && { echo "$tag"; return 0; }
@@ -67,7 +67,7 @@ _fetch() {
fi
return $?
fi
wget -q --timeout=120 --no-check-certificate --user-agent="Clash/OpenWRT" "$url" -O "$target"
wget -q --timeout=120 --user-agent="Clash/OpenWRT" "$url" -O "$target"
}
# 多源拉取:本机代理 → 国内镜像(gh-proxy / ghfast / jsdelivr)→ 原 URL 兜底
@@ -22,7 +22,7 @@ if command -v curl >/dev/null 2>&1; then
curl -fL --connect-timeout 15 --max-time 120 -A "Clash/OpenWRT" "$DOWNLOAD_URL" -o "$TMP_PATH" 2>/dev/null
dl_rc=$?
elif command -v wget >/dev/null 2>&1; then
wget -q --timeout=30 --tries=2 --no-check-certificate -U "Clash/OpenWRT" "$DOWNLOAD_URL" -O "$TMP_PATH" 2>/dev/null
wget -q --timeout=30 -U "Clash/OpenWRT" "$DOWNLOAD_URL" -O "$TMP_PATH" 2>/dev/null
dl_rc=$?
else
log "No curl or wget found"
@@ -79,13 +79,29 @@ download_zip() {
--proxy "$proxy" -A "Clash/OpenWRT" "$url" -o "$ZIP_FILE"; then
return 0
fi
if wget -q --timeout=60 --no-check-certificate --user-agent="Clash/OpenWRT" "$url" -O "$ZIP_FILE"; then
if wget -q --timeout=60 --user-agent="Clash/OpenWRT" "$url" -O "$ZIP_FILE"; then
return 0
fi
done
return 1
}
validate_zip_entries() {
entries="$TMP_ROOT/entries.txt"
if command -v unzip >/dev/null 2>&1; then
unzip -Z1 "$ZIP_FILE" >"$entries" 2>/dev/null || return 1
elif command -v bsdtar >/dev/null 2>&1; then
bsdtar -tf "$ZIP_FILE" >"$entries" 2>/dev/null || return 1
else
return 1
fi
[ -s "$entries" ] || return 1
awk '
/^\// || /^[A-Za-z]:/ || /(^|\/)\.\.(\/|$)/ || /\\/ || /\r/ { bad = 1 }
END { exit bad }
' "$entries"
}
extract_zip() {
if command -v unzip >/dev/null 2>&1; then
unzip -oq "$ZIP_FILE" -d "$UNPACK_DIR" >/dev/null 2>&1
@@ -141,6 +157,12 @@ if ! download_zip; then
exit 1
fi
if ! validate_zip_entries; then
set_state "error" "压缩包路径无效"
log_msg "Dashboard panel archive has unsafe entries" "面板压缩包含不安全路径"
exit 1
fi
extract_zip
extract_rc=$?
if [ "$extract_rc" -ne 0 ]; then
@@ -154,6 +176,12 @@ if [ "$extract_rc" -ne 0 ]; then
exit 1
fi
if find "$UNPACK_DIR" ! -type d ! -type f | grep -q .; then
set_state "error" "压缩包文件类型无效"
log_msg "Dashboard panel archive has unsafe file types" "面板压缩包含不安全文件类型"
exit 1
fi
SRC_DIR="$(find_web_root)"
if [ -z "$SRC_DIR" ] || [ ! -f "$SRC_DIR/index.html" ]; then
set_state "error" "文件无效"
@@ -188,7 +188,7 @@ download_to() {
DOWNLOAD_ERROR="$(curl_error "$last_rc")"
return 1
fi
wget -q --tries=4 --timeout=20 --user-agent="$ua" "$url" -O "$out"
wget -q --timeout=20 --user-agent="$ua" "$url" -O "$out"
rc=$?
[ "$rc" -eq 0 ] && return 0
DOWNLOAD_ERROR="网络错误(wget rc=${rc})"
@@ -209,8 +209,7 @@ update_info() {
}
service_running() {
[ -n "${CLASHOO_SERVICE_CMD:-}" ] && return 0
"$SERVICE_CMD" status >/dev/null 2>&1
[ "$("$SERVICE_CMD" status 2>/dev/null)" = "running" ]
}
template_output_name() {
@@ -9,10 +9,11 @@ TARGET_V6="${NFT_DIR}/geoip6_cn.nft"
FW4_SCRIPT="${FW4_SCRIPT:-/usr/share/clashoo/net/fw4.sh}"
PROC_ROOT="${PROC_ROOT:-/proc}"
SINGBOX_BIN_DIR="${SINGBOX_BIN_DIR:-/usr/share/clashoo/bin}"
TMP_V4="/tmp/china_ip.txt.$$"
TMP_V6="/tmp/china_ipv6.txt.$$"
OUT_V4="/tmp/geoip_cn.nft.$$"
OUT_V6="/tmp/geoip6_cn.nft.$$"
TMP_ROOT="$(mktemp -d /tmp/clashoo_china_ip.XXXXXX)"
TMP_V4="$TMP_ROOT/china_ip.txt"
TMP_V6="$TMP_ROOT/china_ipv6.txt"
OUT_V4="$TMP_ROOT/geoip_cn.nft"
OUT_V6="$TMP_ROOT/geoip6_cn.nft"
log() {
printf ' %s - %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$1" >> "$LOG_FILE"
@@ -30,14 +31,14 @@ download_with_fallback() {
local output="$2"
local ip
if curl -fsSL "$url" -o "$output"; then
if curl -fsSL --connect-timeout 10 --max-time 120 "$url" -o "$output"; then
return 0
fi
case "$url" in
https://ispip.clang.cn/*)
for ip in 182.247.248.127 103.220.64.183; do
if curl -fsSL --resolve "ispip.clang.cn:443:${ip}" "$url" -o "$output"; then
if curl -fsSL --connect-timeout 10 --max-time 120 --resolve "ispip.clang.cn:443:${ip}" "$url" -o "$output"; then
log "DNS 异常,已使用 --resolve(${ip}) 回源下载"
return 0
fi
@@ -49,7 +50,7 @@ download_with_fallback() {
}
cleanup() {
rm -f "$TMP_V4" "$TMP_V6" "$OUT_V4" "$OUT_V6"
rm -rf "$TMP_ROOT"
}
trap cleanup EXIT INT TERM
@@ -61,6 +62,21 @@ render_nft_set() {
local set_type="$4"
awk -v set_name="$set_name" -v set_type="$set_type" '
function valid_cidr(value, parts, octets, i, prefix) {
if (split(value, parts, "/") != 2 || parts[2] !~ /^[0-9]+$/)
return 0
prefix = parts[2] + 0
if (set_type == "ipv4_addr") {
if (prefix > 32 || split(parts[1], octets, ".") != 4)
return 0
for (i = 1; i <= 4; i++)
if (octets[i] !~ /^[0-9]+$/ || length(octets[i]) > 3 || octets[i] + 0 > 255)
return 0
return 1
}
return set_type == "ipv6_addr" && prefix <= 128 &&
parts[1] ~ /^[0-9A-Fa-f:]+$/ && index(parts[1], ":") > 0
}
BEGIN {
print "set " set_name " {"
print "\ttype " set_type ";"
@@ -73,6 +89,12 @@ render_nft_set() {
gsub(/^[[:space:]]+|[[:space:]]+$/, "", $0)
if ($0 == "")
next
if (!valid_cidr($0)) {
if (!invalid)
printf "invalid CIDR at line %d\n", NR > "/dev/stderr"
invalid = 1
next
}
if (!first)
print ","
printf "\t\t%s", $0
@@ -83,8 +105,13 @@ render_nft_set() {
print ""
print "\t}"
print "}"
if (invalid || first)
exit 1
}
' "$source_file" > "$output_file"
' "$source_file" > "$output_file" || return 1
# nft catches malformed IPv6 forms and invalid network prefixes before replacement.
{ printf 'table inet clashoo_validate {\n'; cat "$output_file"; printf '\n}\n'; } |
nft -c -f - >/dev/null 2>&1
}
url4="$(uci -q get clashoo.config.china_ip_url 2>/dev/null || true)"
@@ -107,7 +134,10 @@ download_with_fallback "$url4" "$TMP_V4"
}
changed=0
render_nft_set "$TMP_V4" "$OUT_V4" clashoo_china ipv4_addr
if ! render_nft_set "$TMP_V4" "$OUT_V4" clashoo_china ipv4_addr; then
log '大陆 IPv4 白名单包含无效 CIDR,保留原文件'
exit 1
fi
if cmp -s "$OUT_V4" "$TARGET_V4"; then
log '大陆 IPv4 白名单内容无变化'
else
@@ -119,7 +149,10 @@ fi
if download_with_fallback "$url6" "$TMP_V6"; then
if [ -s "$TMP_V6" ]; then
render_nft_set "$TMP_V6" "$OUT_V6" clashoo_china6 ipv6_addr
if ! render_nft_set "$TMP_V6" "$OUT_V6" clashoo_china6 ipv6_addr; then
log '大陆 IPv6 白名单包含无效 CIDR,保留原文件'
exit 1
fi
if cmp -s "$OUT_V6" "$TARGET_V6"; then
log '大陆 IPv6 白名单内容无变化'
else