Import x-panel source
This commit is contained in:
289
dnsjc.sh
Normal file
289
dnsjc.sh
Normal file
@@ -0,0 +1,289 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -u
|
||||
set -o pipefail
|
||||
|
||||
COUNT=${COUNT:-10}
|
||||
TOPN=${TOPN:-30}
|
||||
SLEEP_META=${SLEEP_META:-0.15}
|
||||
META_CACHE_DIR="${META_CACHE_DIR:-/tmp/dns_meta_cache}"
|
||||
mkdir -p "$META_CACHE_DIR" >/dev/null 2>&1 || true
|
||||
|
||||
# deps
|
||||
require_cmd() { command -v "$1" >/dev/null 2>&1 || { echo "missing dependency: $1"; exit 1; }; }
|
||||
require_cmd curl
|
||||
require_cmd awk
|
||||
require_cmd sed
|
||||
require_cmd tr
|
||||
require_cmd grep
|
||||
require_cmd ping
|
||||
|
||||
PING_BIN="ping"
|
||||
PING6_BIN="ping -6"
|
||||
if command -v ping6 >/dev/null 2>&1; then
|
||||
PING6_BIN="ping6"
|
||||
fi
|
||||
|
||||
# color banner (red)
|
||||
if [ -t 1 ] && [ "${TERM:-}" != "dumb" ]; then
|
||||
RED=$'\033[1;31m'
|
||||
RESET=$'\033[0m'
|
||||
else
|
||||
RED=""
|
||||
RESET=""
|
||||
fi
|
||||
echo ""
|
||||
printf "%b\n\n" "${RED}〔X-Panel-Pro 面板〕专属 “服务器 DNS 检测”${RESET}"
|
||||
|
||||
# extract valid IPs (IPv4 strict / IPv6 loose)
|
||||
extract_ips() {
|
||||
sed -E 's/<[^>]+>/ /g' \
|
||||
| tr -c '0-9A-Fa-f:.' '\n' \
|
||||
| grep -E '(^([0-9]{1,3}\.){3}[0-9]{1,3}$)|(^([0-9A-Fa-f]{0,4}:){2,7}[0-9A-Fa-f]{0,4}$)' \
|
||||
| awk '!seen[$0]++' \
|
||||
| awk -F. '
|
||||
$0 ~ /:/ { print; next }
|
||||
NF==4 {
|
||||
ok=1
|
||||
for(i=1;i<=4;i++){
|
||||
if($i !~ /^[0-9]+$/ || $i<0 || $i>255){ ok=0; break }
|
||||
}
|
||||
if(ok) print $0
|
||||
}
|
||||
'
|
||||
}
|
||||
|
||||
# filter private/reserved IPv4
|
||||
is_public_ipv4() {
|
||||
local ip="$1"; IFS=. read -r a b c d <<<"$ip" || return 1
|
||||
if ((a==10)) || ((a==127)) || ((a==192 && b==168)) || ((a==169 && b==254)) \
|
||||
|| ((a==172 && b>=16 && b<=31)) || ((a==100 && b>=64 && b<=127)) \
|
||||
|| ((a==0)) || ((a>=224)); then
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# normalize country to slug
|
||||
normalize_country_to_slug() {
|
||||
local raw="$1"
|
||||
local x compact
|
||||
x=$(printf '%s' "$raw" | tr '[:upper:]' '[:lower:]')
|
||||
x=$(printf '%s' "$x" | sed -E 's/^[[:space:]]+|[[:space:]]+$//g')
|
||||
compact=$(printf '%s' "$x" | sed -E 's/[^a-z0-9]+//g')
|
||||
case "$compact" in
|
||||
jp) echo "japan"; return;;
|
||||
kr|rok) echo "southkorea"; return;;
|
||||
us|usa) echo "unitedstates"; return;;
|
||||
uk|gb) echo "unitedkingdom"; return;;
|
||||
ae|uae) echo "unitedarabemirates"; return;;
|
||||
hk) echo "hongkong"; return;;
|
||||
tw) echo "taiwan"; return;;
|
||||
cn) echo "china"; return;;
|
||||
de) echo "germany"; return;;
|
||||
fr) echo "france"; return;;
|
||||
it) echo "italy"; return;;
|
||||
es) echo "spain"; return;;
|
||||
ru) echo "russia"; return;;
|
||||
sg) echo "singapore"; return;;
|
||||
th) echo "thailand"; return;;
|
||||
vn) echo "vietnam"; return;;
|
||||
ph) echo "philippines"; return;;
|
||||
id) echo "indonesia"; return;;
|
||||
my) echo "malaysia"; return;;
|
||||
au) echo "australia"; return;;
|
||||
nz) echo "newzealand"; return;;
|
||||
nl) echo "netherlands"; return;;
|
||||
be) echo "belgium"; return;;
|
||||
pl) echo "poland"; return;;
|
||||
cz) echo "czechia"; return;;
|
||||
ch) echo "switzerland"; return;;
|
||||
at) echo "austria"; return;;
|
||||
se) echo "sweden"; return;;
|
||||
no) echo "norway"; return;;
|
||||
fi) echo "finland"; return;;
|
||||
pt) echo "portugal"; return;;
|
||||
ro) echo "romania"; return;;
|
||||
hu) echo "hungary"; return;;
|
||||
sk) echo "slovakia"; return;;
|
||||
si) echo "slovenia"; return;;
|
||||
gr) echo "greece"; return;;
|
||||
ie) echo "ireland"; return;;
|
||||
mx) echo "mexico"; return;;
|
||||
ca) echo "canada"; return;;
|
||||
br) echo "brazil"; return;;
|
||||
ar) echo "argentina"; return;;
|
||||
cl) echo "chile"; return;;
|
||||
za) echo "southafrica"; return;;
|
||||
esac
|
||||
x=$(printf '%s' "$x" | sed -E 's/[[:space:]]+//g')
|
||||
echo "$x"
|
||||
}
|
||||
|
||||
# fetch IPs from publicdnsserver page
|
||||
fetch_country_ips() {
|
||||
local slug="$1"
|
||||
local url="https://publicdnsserver.com/${slug}/"
|
||||
local html
|
||||
html=$(curl -sL --max-time 15 "$url" || true)
|
||||
[ -z "$html" ] && { echo ""; return 0; }
|
||||
printf '%s' "$html" | extract_ips
|
||||
}
|
||||
|
||||
# metadata (ip-api) with 1h cache
|
||||
get_meta_json() {
|
||||
local ip="$1"
|
||||
local cache="$META_CACHE_DIR/$ip.json"
|
||||
local epoch mtime age
|
||||
if [ -s "$cache" ]; then
|
||||
epoch=$(date +%s)
|
||||
if mtime=$(stat -c %Y "$cache" 2>/dev/null); then
|
||||
age=$((epoch - mtime))
|
||||
if [ "$age" -lt 3600 ]; then
|
||||
cat "$cache"; return 0
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
local resp
|
||||
resp=$(curl -s --max-time 5 "http://ip-api.com/json/$ip?fields=status,message,country,city,as,asname,org,isp,query")
|
||||
if [ -n "$resp" ]; then
|
||||
printf '%s' "$resp" >"$cache" 2>/dev/null || true
|
||||
printf '%s' "$resp"
|
||||
else
|
||||
printf '{"status":"fail","country":"","city":"","as":"","asname":"","org":"","isp":"","query":"%s"}' "$ip"
|
||||
fi
|
||||
sleep "$SLEEP_META"
|
||||
}
|
||||
|
||||
# poor-man's json getter (no jq)
|
||||
json_get() {
|
||||
echo "$1" | sed -n "s/.*\"$2\":\"\([^\"]*\)\".*/\1/p"
|
||||
}
|
||||
|
||||
# parse ping output (returns: loss,min,avg,max,mdev)
|
||||
parse_ping() {
|
||||
local out; out=$(cat)
|
||||
local loss min avg max mdev rtt
|
||||
loss=$(printf '%s\n' "$out" | LC_ALL=C grep -Eo '[0-9]+(\.[0-9]+)?% packet loss' | sed -E 's/%.*//')
|
||||
[ -z "$loss" ] && loss="100.0"
|
||||
rtt=$(printf '%s\n' "$out" | LC_ALL=C grep -E 'min/avg/max' | tail -n1 | awk -F'=' '{print $2}' | awk '{print $1}')
|
||||
if [ -n "$rtt" ]; then
|
||||
IFS=/ read -r min avg max mdev <<<"$rtt"
|
||||
else
|
||||
min="N/A"; avg="N/A"; max="N/A"; mdev="N/A"
|
||||
fi
|
||||
printf '%s,%s,%s,%s,%s\n' "$loss" "$min" "$avg" "$max" "$mdev"
|
||||
}
|
||||
|
||||
# main
|
||||
read -rp "请输入英文国家名(如 Japan / United States / South Korea;或 ISO 两字母,如 JP): " USER_REGION || USER_REGION=""
|
||||
SLUG=$(normalize_country_to_slug "${USER_REGION:-}")
|
||||
[ -z "$SLUG" ] && SLUG="japan"
|
||||
|
||||
MAP_IPS_RAW=$(fetch_country_ips "$SLUG")
|
||||
|
||||
declare -a TARGETS TMP_LIST RESULTS
|
||||
declare -A seen
|
||||
|
||||
while IFS= read -r ip; do
|
||||
[ -z "$ip" ] && continue
|
||||
if [[ "$ip" == *:* ]]; then
|
||||
TARGETS+=("$ip")
|
||||
else
|
||||
if is_public_ipv4 "$ip"; then
|
||||
TARGETS+=("$ip")
|
||||
fi
|
||||
fi
|
||||
done < <(printf '%s\n' "$MAP_IPS_RAW" | awk 'NF{if(!seen[$0]++){print}}')
|
||||
|
||||
for ip in "${TARGETS[@]}"; do
|
||||
if [ -z "${seen[$ip]+x}" ]; then
|
||||
TMP_LIST+=("$ip"); seen["$ip"]=1
|
||||
fi
|
||||
[ "${#TMP_LIST[@]}" -ge "$TOPN" ] && break
|
||||
done
|
||||
TARGETS=("${TMP_LIST[@]}")
|
||||
|
||||
for must in 1.1.1.1 8.8.8.8; do
|
||||
if [ -z "${seen[$must]+x}" ]; then
|
||||
TARGETS+=("$must"); seen["$must"]=1
|
||||
fi
|
||||
done
|
||||
|
||||
N=${#TARGETS[@]}
|
||||
printf "地区: %s(slug: %s)| 目标数: %d | 每个目标 ping 次数: %d\n" "${USER_REGION:-N/A}" "$SLUG" "$N" "$COUNT"
|
||||
printf "将测试的目标:%s\n" "$(printf '%s ' "${TARGETS[@]}")"
|
||||
echo "开始测试 ......"
|
||||
|
||||
if [ "$N" -eq 0 ]; then
|
||||
echo "未找到可用目标。"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
idx=0
|
||||
for ip in "${TARGETS[@]}"; do
|
||||
idx=$((idx+1))
|
||||
pct=$(( idx * 100 / (N==0 ? 1 : N) ))
|
||||
|
||||
printf "进度: [%d/%d | %3d%%] #%d 正在测试: %s\r" "$idx" "$N" "$pct" "$idx" "$ip"
|
||||
|
||||
if [[ "$ip" == *:* ]]; then
|
||||
out=$(LC_ALL=C $PING6_BIN -n -c "$COUNT" -i 0.2 -w $((COUNT+4)) "$ip" 2>&1 || true)
|
||||
else
|
||||
out=$(LC_ALL=C $PING_BIN -n -c "$COUNT" -i 0.2 -w $((COUNT+4)) "$ip" 2>&1 || true)
|
||||
fi
|
||||
|
||||
stats=$(printf '%s' "$out" | parse_ping)
|
||||
IFS=, read -r loss min avg max mdev <<<"$stats"
|
||||
|
||||
meta=$(get_meta_json "$ip")
|
||||
country=$(json_get "$meta" country); [ -z "$country" ] && country="N/A"
|
||||
city=$(json_get "$meta" city); [ -z "$city" ] && city="N/A"
|
||||
asfull=$(json_get "$meta" as); [ -z "$asfull" ] && asfull="N/A"
|
||||
asname=$(json_get "$meta" asname); [ -z "$asname" ] && asname="N/A"
|
||||
org=$(json_get "$meta" org)
|
||||
isp=$(json_get "$meta" isp)
|
||||
|
||||
asn=$(printf '%s' "$asfull" | sed -n 's/.*\(AS[0-9][0-9]*\).*/\1/p')
|
||||
[ -z "$asn" ] && asn="N/A"
|
||||
|
||||
company="$asname"
|
||||
[ -z "$company" ] || [ "$company" = "N/A" ] && company="$org"
|
||||
[ -z "$company" ] || [ "$company" = "N/A" ] && company="$isp"
|
||||
[ -z "$company" ] && company="N/A"
|
||||
|
||||
printf "进度: [%d/%d | %3d%%] #%d 正在测试: %-39s | 丢包 %s%% | 最小 %sms | 平均 %sms | 最大 %sms | 抖动 %sms\n" \
|
||||
"$idx" "$N" "$pct" "$idx" "$ip" "$loss" "$min" "$avg" "$max" "$mdev"
|
||||
|
||||
sortkey="$avg"; [[ "$sortkey" == "N/A" || -z "$sortkey" ]] && sortkey=999999999
|
||||
RESULTS+=("$sortkey\t$idx\t$ip\t$country/$city\t$asn\t$company\t$loss\t$min\t$avg\t$max\t$mdev")
|
||||
done
|
||||
echo
|
||||
|
||||
HEADER=$'编号\t目标\t地区\tASN\t公司\t丢包\t最小(ms)\t平均(ms)\t最大(ms)\t抖动'
|
||||
BODY=$(
|
||||
printf '%b\n' "${RESULTS[@]}" \
|
||||
| LC_ALL=C sort -t$'\t' -k1,1n \
|
||||
| cut -f2- \
|
||||
| while IFS=$'\t' read -r idx0 ip region asn company loss min avg max mdev; do
|
||||
printf '%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n' \
|
||||
"$idx0" "$ip" "$region" "$asn" "$company" \
|
||||
"$(printf '%.1f%%' "${loss:-0}")" \
|
||||
"${min:-N/A}" "${avg:-N/A}" "${max:-N/A}" "${mdev:-N/A}"
|
||||
done
|
||||
)
|
||||
|
||||
if command -v column >/dev/null 2>&1; then
|
||||
{ printf '%s\n' "$HEADER"; printf '%s\n' "$BODY"; } | column -t -s $'\t'
|
||||
else
|
||||
printf "%-4s %-39s %-18s %-8s %-28s %-6s %-9s %-9s %-9s %-7s\n" \
|
||||
"编号" "目标" "地区" "ASN" "公司" "丢包" "最小(ms)" "平均(ms)" "最大(ms)" "抖动"
|
||||
printf -- "-----------------------------------------------------------------------------------------------\n"
|
||||
printf '%s\n' "$BODY" | while IFS=$'\t' read -r idx0 ip region asn company loss min avg max mdev; do
|
||||
printf "%-4s %-39s %-18s %-8s %-28s %-6s %-9s %-9s %-9s %-7s\n" \
|
||||
"$idx0" "$ip" "$region" "$asn" "$company" \
|
||||
"$loss" "$min" "$avg" "$max" "$mdev"
|
||||
done
|
||||
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user