#!/bin/bash script_version="v2025-09-18" check_bash(){ current_bash_version=$(bash --version|head -n 1|awk '{for(i=1;i<=NF;i++) if ($i ~ /^[0-9]+\.[0-9]+(\.[0-9]+)?/) print $i}') major_version=$(echo "$current_bash_version"|cut -d'.' -f1) minor_version=$(echo "$current_bash_version"|cut -d'.' -f2) if [ "$major_version" -lt 4 ]||{ [ "$major_version" -eq 4 ]&&[ "$minor_version" -lt 3 ];};then echo "ERROR: Bash version is $current_bash_version lower than 4.3!" echo "Tips: Run the following script to automatically upgrade Bash." echo "bash <(curl -sL https://raw.githubusercontent.com/xykt/NetQuality/main/ref/upgrade_bash.sh)" exit 0 fi } check_bash Font_B="\033[1m" Font_D="\033[2m" Font_I="\033[3m" Font_U="\033[4m" Font_Black="\033[30m" Font_Red="\033[31m" Font_Green="\033[32m" Font_Yellow="\033[33m" Font_Blue="\033[34m" Font_Purple="\033[35m" Font_Cyan="\033[36m" Font_White="\033[37m" Back_Black="\033[40m" Back_Red="\033[41m" Back_Green="\033[42m" Back_Yellow="\033[43m" Back_Blue="\033[44m" Back_Purple="\033[45m" Back_Cyan="\033[46m" Back_White="\033[47m" Font_Suffix="\033[0m" Font_LineClear="\033[2K" Font_LineUp="\033[1A" declare ADLines declare -A aad declare IP="" declare IPhide declare fullIP=0 declare YY="cn" declare is_dep=1 declare is_nexttrace=1 declare is_speedtest=1 declare -A bgp declare -A getbgp declare -A getnat declare -A gettcp declare -A conn declare -A casn declare -A corg declare -A ctarget declare -A ctier1 declare -A cupstream declare -A pcode declare -A pshort declare -A pname declare -A pdm declare -A presu declare -A pavg declare -A pout declare -A iperfresu declare -A icity declare -A idm declare -A iportl declare -A iportu declare -A iresu declare -A isout declare -A ipout declare -A iavg declare -A scity declare -A spv declare -A sid declare -A sout declare -A sresu declare -A rww declare -A rcn declare -A routww declare -A routcn declare rgia=0 declare -A AS_MAPPING declare -A rmcode declare -A rmresu declare rmtestnum declare -A rmallasn declare -A rmallgeo declare -A rmmaxhop declare -A rmcnhop declare -A rmcn declare -A rmww declare rmgia=0 declare IPV4 declare IPV6 declare IPV4check=1 declare IPV6check=1 declare IPV4work=0 declare IPV6work=0 declare ERRORcode=0 declare shelp declare -A swarn declare -A sinfo declare -A shead declare -A sbgp declare -A slocal declare -A sconn declare -A sdelay declare -A sroute declare -A siperf declare -A sspeedtest declare -A stail declare ibar=0 declare bar_pid declare ibar_step=0 declare main_pid=$$ declare PADDING="" declare useNIC="" declare usePROXY="" declare CurlARG="" declare UA_Browser declare rawgithub declare ISO3166 declare display_max_len=80 declare mode_ping=0 declare mode_route=0 declare mode_route_pv="" declare mode_low=0 declare mode_json=0 declare mode_no=0 declare mode_yes=0 declare mode_skip="" declare mode_menu=0 declare mode_output=0 declare mode_privacy=0 declare ping_test_count=10 declare pingww_test_count=12 declare usesudo="sudo" declare netdata shelp_lines=( "NETWORK QUALITY CHECK SCRIPT 网络质量体检脚本" "Interactive Interface: bash <(curl -sL https://Net.Check.Place) -EM" "交互界面: bash <(curl -sL https://Net.Check.Place) -M" "Parameters 参数运行: bash <(curl -sL https://Net.Check.Place) [-4] [-6] [-f] [-h] [-j] [-l language] [-n] [-y] [-E] [-L] [-M] [-P] [-R province] [-S chapters]" " -4 Test IPv4 测试IPv4" " -6 Test IPv6 测试IPv6" " -f Show full IP on reports 报告展示完整IP地址" " -h Help information 帮助信息" " -j JSON output JSON输出" " -l cn|en Specify script language 指定报告语言" " -n No OS or dependencies check 跳过系统检测及依赖安装" " -o /path/to/file.ansi Output ANSI report to file 输出ANSI报告至文件" " /path/to/file.json Output JSON result to file 输出JSON结果至文件" " /path/to/file.anyother Output plain text report to file 输出纯文本报告至文件" " -p Privacy mode - no generate report link 隐私模式:不生成报告链接" " -y Install dependencies without interupt 自动安装依赖" " -E Specify English Output 指定英文输出" " -L Low data mode 低数据模式(跳过测速环节)" " -M Run with Interactive Interface 交互界面方式运行" " -P Ping mode 三网延迟模式" " -R [Province] Route mode [Specify Province] 三网完整路由模式[可选指定省份]" " -S 1234567 Skip sections by number 跳过相应章节") shelp=$(printf "%s\n" "${shelp_lines[@]}") set_language(){ case "$YY" in "en"|"jp"|"es"|"de"|"fr"|"ru"|"pt")swarn[1]="ERROR: Unsupported parameters!" swarn[2]="ERROR: IP address format error!" swarn[3]="ERROR: Dependent programs are missing. Please run as root or install sudo!" swarn[4]="ERROR: Parameter -4 conflicts with -i or -6!" swarn[6]="ERROR: Parameter -6 conflicts with -i or -4!" swarn[9]="ERROR: It is not allowed to skip all funcions!" swarn[10]="ERROR: Output file already exist!" swarn[11]="ERROR: Output file is not writable!" swarn[21]="ERROR: Wrong province code or name!" swarn[40]="ERROR: IPv4 is not available!" swarn[60]="ERROR: IPv6 is not available!" sinfo[bgp]="Checking BGP database" sinfo[lbgp]=21 sinfo[neighbor]="Checking Active Neighbors" sinfo[lneighbor]=25 sinfo[nat]="Checking NAT Type" sinfo[lnat]=17 sinfo[tcp]="Checking TCP Settings" sinfo[ltcp]=21 sinfo[delay]="Checking China Mainland TCP Delay" sinfo[ldelay]=33 sinfo[route]="Checking Route to China Mainland" sinfo[lroute]=32 sinfo[moderoute]="Checking Route details to Specified Province" sinfo[lmoderoute]=44 sinfo[speedtest]="Checking Speedtest of China " sinfo[lspeedtest]=28 sinfo[iperf]="Checking Global Transfer of " sinfo[liperf]=28 sinfo[delayww]="Checking Global TCP Delay" sinfo[ldelayww]=25 shead[title]="NET QUALITY CHECK REPORT: " shead[ver]="Version: $script_version" shead[bash]="bash <(curl -sL https://Check.Place) -EN" shead[git]="https://github.com/xykt/NetQuality" shead[time]=$(date -u +"Report Time: %Y-%m-%d %H:%M:%S UTC") shead[ltitle]=26 shead[ptime]=$(printf '%11s' '') sbgp[title]="1. BGP Information (${Font_I}BGP.TOOLS & HE.NET$Font_Suffix)" sbgp[ipinfo]="Reg Info: " sbgp[country]="Region: " sbgp[address]="Address: " sbgp[geofeed]="GeoFeed: " sbgp[date]="Reg/Mod Date: " sbgp[neighbor]="Active Neighbors: " slocal[title]="2. Local Status" slocal[nat]="NAT Type: " slocal[tcpcc]="Congestion Control: " slocal[qdisc]="Queue Discipline: " slocal[rmem]="TCP Receive Buffer: " slocal[wmem]="TCP Send Buffer: " slocal[port]="Port: " slocal[mapping]="Mapping: " slocal[filter]="Filter: " slocal[hairpin]="Hairpin: " slocal[m0]="${Font_Green}Independent $Font_Suffix" slocal[m1]="${Font_Red}Dependent $Font_Suffix" slocal[f1]="${Font_Green}Independent $Font_Suffix" slocal[f2]="${Font_Yellow}AddressDependent $Font_Suffix" slocal[f3]="${Font_Red}PortDependent $Font_Suffix" slocal[p0]="${Font_Yellow}Random $Font_Suffix" slocal[p1]="${Font_Green}Preserve $Font_Suffix" slocal[h0]="${Font_Green}Yes$Font_Suffix" slocal[h1]="${Font_Red}No$Font_Suffix" slocal[open]="$Back_Green$Font_White$Font_B Open Without NAT $Font_Suffix" slocal[full]="$Font_White$Font_B Full Cone $Font_Suffix" slocal[rest]="$Back_Yellow$Font_White$Font_B Restricted Cone $Font_Suffix" slocal[portrest]="$Back_Yellow$Font_White$Font_B Port Restricted Cone $Font_Suffix" slocal[symm]="$Back_Red$Font_White$Font_B Symmetric $Font_Suffix" slocal[fail]="$Back_Red$Font_White$Font_B Checking Error $Font_Suffix" slocal[firewall]="$Back_Yellow$Font_White$Font_B Firewall $Font_Suffix" slocal[block]="$Back_Red$Font_White$Font_B Connection Failed $Font_Suffix" slocal[unknown]="$Back_Yellow$Font_White$Font_B Unknown NAT Type $Font_Suffix" sconn[title]="3. Connectivity ($Back_Green$Font_White$Font_B*$Font_Suffix$Font_I=Tier1 $Font_Suffix$Back_Yellow$Font_White$Font_B*$Font_Suffix$Font_I=Non-Tier1 $Font_Suffix$Font_U*$Font_Suffix$Font_I=Upstream$Font_Suffix)" sconn[ix]="IXPs Counts: " sconn[upstreams]="Upstreams Counts: " sconn[peers]="Peers Counts: " sdelay[title]="4. China Mainland TCP Delay (${Font_I}CT|CU|CM Step=80ms$Font_Suffix) " sdelay[pingmode]="$(printf '%8s'|tr ' ' '*')China Telecom$(printf '%13s'|tr ' ' '*')China Unicom$(printf '%14s'|tr ' ' '*')China Mobile$(printf '%8s'|tr ' ' '*')" sroute[title]="5. Route to China Mainland (May vary with network congestion)" sroute[ct]="CTel" sroute[cu]="CUni" sroute[cm]="CMob" sroute[bj]="BJ " sroute[sh]="SH " sroute[gz]="GZ " sroute[tcp]="TCP: " sroute[udp]="UDP: " sspeedtest[title]="6. CN NetSpeed $Font_I${Font_U}Send$Font_Suffix ${Font_I}Delay ${Font_U}Receive$Font_Suffix ${Font_I}Delay$Font_Suffix||Unit: ${Font_U}Mbps$Font_Suffix$Font_I ms ${Font_U}Send$Font_Suffix ${Font_I}Delay ${Font_U}Receive$Font_Suffix ${Font_I}Delay$Font_Suffix" siperf[title]="7. Global Network $Font_I${Font_U}Send$Font_Suffix ${Font_I}Retr ${Font_U}Recv$Font_Suffix ${Font_I}Retr$Font_Suffix||Unit: ms ${Font_U}Mbps$Font_Suffix$Font_I Delay ${Font_U}Send$Font_Suffix ${Font_I}Retr ${Font_U}Recv$Font_Suffix ${Font_I}Retr$Font_Suffix" siperf[send]=" Send" siperf[recv]=" Receive" stail[stoday]="Network Checks Today: " stail[stotal]="; Total: " stail[thanks]=". Thanks for running xy scripts!" stail[link]="${Font_I}Report Link: $Font_U" ;; "cn")swarn[1]="错误:不支持的参数!" swarn[2]="错误:IP地址格式错误!" swarn[3]="错误:未安装依赖程序,请以root执行此脚本,或者安装sudo命令!" swarn[4]="错误:参数-4与-i/-6冲突!" swarn[6]="错误:参数-6与-i/-4冲突!" swarn[9]="错误: 不允许跳过所有功能!" swarn[10]="错误:输出文件已存在!" swarn[11]="错误:输出文件不可写!" swarn[21]="错误: 错误的省份名称或代码!" swarn[40]="错误:IPV4不可用!" swarn[60]="错误:IPV6不可用!" sinfo[bgp]="正在检测BGP数据库 " sinfo[lbgp]=18 sinfo[neighbor]="正在检测活跃邻居 " sinfo[lneighbor]=17 sinfo[nat]="正在检测NAT类型 " sinfo[lnat]=16 sinfo[tcp]="正在检测TCP设置 " sinfo[ltcp]=16 sinfo[delay]="正在检测大陆三网TCP大包延迟" sinfo[ldelay]=27 sinfo[route]="正在检测大陆三网回程线路" sinfo[lroute]=24 sinfo[moderoute]="正在检测TCP回程详细路由" sinfo[lmoderoute]=23 sinfo[speedtest]="正在检测三网Speedtest之" sinfo[lspeedtest]=23 sinfo[iperf]="正在检测国际互连:" sinfo[liperf]=18 sinfo[delayww]="正在检测国际互连TCP大包延迟" sinfo[ldelayww]=27 shead[title]="网络质量体检报告:" shead[ver]="脚本版本:$script_version" shead[bash]="bash <(curl -sL https://Check.Place) -N" shead[git]="https://github.com/xykt/NetQuality" shead[time]=$(TZ="Asia/Shanghai" date +"报告时间:%Y-%m-%d %H:%M:%S CST") shead[ltitle]=18 shead[ptime]=$(printf '%12s' '') sbgp[title]="一、BGP信息(${Font_I}BGP.TOOLS & HE.NET$Font_Suffix)" sbgp[ipinfo]="注册信息: " sbgp[country]="地区: " sbgp[address]="地址: " sbgp[geofeed]="地理数据供给: " sbgp[date]="注册/修改日期: " sbgp[neighbor]="活跃邻居: " slocal[title]="二、本地策略" slocal[nat]="NAT类型: " slocal[tcpcc]="TCP拥塞控制算法:" slocal[qdisc]="队列调度算法: " slocal[rmem]="TCP接收缓冲区(rmem):" slocal[wmem]="TCP发送缓冲区(wmem):" slocal[port]="端口行为:" slocal[mapping]="映射行为:" slocal[filter]="过滤行为:" slocal[hairpin]="环回:" slocal[m0]="$Font_Green独立映射 $Font_Suffix" slocal[m1]="$Font_Red依赖映射 $Font_Suffix" slocal[f1]="$Font_Green端点独立过滤 $Font_Suffix" slocal[f2]="$Font_Yellow地址依赖过滤 $Font_Suffix" slocal[f3]="$Font_Red端口依赖过滤 $Font_Suffix" slocal[p0]="$Font_Yellow端口随机 $Font_Suffix" slocal[p1]="$Font_Green端口保留 $Font_Suffix" slocal[h0]="$Font_Green支持$Font_Suffix" slocal[h1]="$Font_Red不支持$Font_Suffix" slocal[open]="$Back_Green$Font_White$Font_B 开放网络无NAT $Font_Suffix" slocal[full]="$Font_White$Font_B 全锥形 $Font_Suffix" slocal[rest]="$Back_Yellow$Font_White$Font_B 受限锥形 $Font_Suffix" slocal[portrest]="$Back_Yellow$Font_White$Font_B 端口受限锥形 $Font_Suffix" slocal[symm]="$Back_Red$Font_White$Font_B 对称型 $Font_Suffix" slocal[fail]="$Back_Red$Font_White$Font_B 检测错误 $Font_Suffix" slocal[firewall]="$Back_Yellow$Font_White$Font_B 防火墙 $Font_Suffix" slocal[block]="$Back_Red$Font_White$Font_B 连接失败 $Font_Suffix" slocal[unknown]="$Back_Yellow$Font_White$Font_B 未知NAT类型 $Font_Suffix" sconn[title]="三、接入信息($Back_Green$Font_White$Font_B*$Font_Suffix$Font_I=Tier1 $Font_Suffix$Back_Yellow$Font_White$Font_B*$Font_Suffix$Font_I=非Tier1 $Font_Suffix$Font_U*$Font_Suffix$Font_I=上游$Font_Suffix)" sconn[ix]="互联网交换点接入数:" sconn[upstreams]="上游数量:" sconn[peers]="对等互联数量:" sdelay[title]="四、三网TCP大包延迟($Font_I依次为电信|联通|移动 Step=80ms$Font_Suffix) " sdelay[pingmode]="$(printf '%12s'|tr ' ' '*')电 信$(printf '%21s'|tr ' ' '*')联 通$(printf '%21s'|tr ' ' '*')移 动$(printf '%11s'|tr ' ' '*')" sroute[title]="五、三网回程路由($Font_I线路可能随网络负载动态变化$Font_Suffix)" sroute[ct]="电信" sroute[cu]="联通" sroute[cm]="移动" sroute[bj]="北京" sroute[sh]="上海" sroute[gz]="广州" sroute[tcp]="TCP:" sroute[udp]="UDP:" sspeedtest[title]="六、国内测速 $Font_I$Font_U发送$Font_Suffix $Font_I延迟 $Font_U接收$Font_Suffix $Font_I延迟$Font_Suffix||单位:${Font_U}Mbps$Font_Suffix$Font_I ms $Font_U发送$Font_Suffix $Font_I延迟 $Font_U接收$Font_Suffix $Font_I延迟$Font_Suffix" siperf[title]="七、国际互连 $Font_I延迟 $Font_U发送$Font_Suffix $Font_I重传 $Font_U接收$Font_Suffix $Font_I重传$Font_Suffix||单位:ms ${Font_U}Mbps$Font_Suffix$Font_I 延迟 $Font_U发送$Font_Suffix $Font_I重传 $Font_U接收$Font_Suffix $Font_I重传$Font_Suffix" siperf[send]="之发送" siperf[recv]="之接收" stail[stoday]="今日网络检测量:" stail[stotal]=";总检测量:" stail[thanks]="。感谢使用xy系列脚本!" stail[link]="$Font_I报告链接:$Font_U" ;; *)echo -ne "ERROR: Language not supported!" esac } countRunTimes(){ local RunTimes=$(curl $CurlARG -s --max-time 10 "https://hits.xykt.de/net?action=hit" 2>&1) stail[today]=$(echo "$RunTimes"|jq '.daily') stail[total]=$(echo "$RunTimes"|jq '.total') } show_progress_bar(){ show_progress_bar_ "$@" 1>&2 } show_progress_bar_(){ local bar="\u280B\u2819\u2839\u2838\u283C\u2834\u2826\u2827\u2807\u280F" local n=${#bar} while sleep 0.1;do if ! kill -0 $main_pid 2>/dev/null;then echo -ne "" exit fi echo -ne "\r$Font_Cyan$Font_B[$IP]# $1$Font_Cyan$Font_B$(printf '%*s' "$2" ''|tr ' ' '.') ${bar:ibar++*6%n:6} $(printf '%02d%%' $ibar_step) $Font_Suffix" done } kill_progress_bar(){ kill "$bar_pid" 2>/dev/null&&echo -ne "\r" } install_dependencies(){ local is_dep=1 local is_nexttrace=1 local is_speedtest=1 local is_stun=1 if [ "$(uname)" == "Darwin" ]||[ $(id -u) -eq 0 ];then usesudo="" fi if ! jq --version >/dev/null 2>&1||! curl --version >/dev/null 2>&1||! command -v convert >/dev/null 2>&1||! command -v mtr >/dev/null 2>&1||! command -v iperf3 >/dev/null 2>&1||! command -v stun >/dev/null 2>&1||(! command -v stun >/dev/null 2>&1&&! command -v pkg >/dev/null 2>&1&&[[ "$(uname)" != "Darwin" ]])||(! command -v free >/dev/null 2>&1&&[[ "$(uname)" != "Darwin" ]]);then is_dep=0 fi if ! command -v nexttrace >/dev/null 2>&1;then is_nexttrace=0 fi if ! command -v speedtest >/dev/null 2>&1;then is_speedtest=0 fi if ! command -v stun >/dev/null 2>&1&&(command -v apk >/dev/null 2>&1||command -v yum >/dev/null 2>&1||command -v zypper >/dev/null 2>&1||command -v xbps-install >/dev/null 2>&1||command -v pacman >/dev/null 2>&1);then is_stun=0 fi if [[ $is_dep -eq 0 || $is_nexttrace -eq 0 || $is_speedtest -eq 0 ]];then echo -e "Lacking necessary dependencies." [[ $is_dep -eq 0 ]]&&echo -e "Packages $Font_I${Font_Cyan}jq curl imagemagick mtr iperf3 stun bc$Font_Suffix will be installed using ${Font_Green}package manager$Font_Suffix." [[ $is_nexttrace -eq 0 ]]&&echo -e "Application $Font_I${Font_Cyan}nexttrace$Font_Suffix will be installed via $Font_Green${Font_I}curl nxtrace.org/nt |bash$Font_Suffix by ${Font_U}https://www.nxtrace.org/$Font_Suffix official." [[ $is_speedtest -eq 0 ]]&&echo -e "Application $Font_I${Font_Cyan}speedtest$Font_Suffix will be installed using ${Font_B}Speedtest.net$Font_Suffix official installation method ${Font_U}https://www.speedtest.net/apps/cli$Font_Suffix." if [[ $mode_yes -eq 0 ]];then prompt=$(printf "Continue? (${Font_Green}y$Font_Suffix/${Font_Red}n$Font_Suffix): ") read -p "$prompt" choice case "$choice" in y|Y|yes|Yes|YES)echo "Continue to execute script..." ;; n|N|no|No|NO)echo "Script exited." exit 0 ;; *)echo "Invalid input, script exited." exit 1 esac else echo -e "Detected parameter $Font_Green-y$Font_Suffix. Continue installation..." fi if [[ $is_dep -eq 0 ]];then if [ "$(uname)" == "Darwin" ];then install_packages "brew" "brew install" elif [ -f /etc/os-release ];then . /etc/os-release if [ $(id -u) -ne 0 ]&&! command -v sudo >/dev/null 2>&1;then ERRORcode=3 fi case $ID in debian)install_packages "apt" "apt-get install -y" ;; ubuntu|linuxmint)install_packages "apt" "apt-get install -y" ;; rhel|centos|almalinux|rocky|anolis)if [ "$(echo $VERSION_ID|cut -d '.' -f1)" -ge 8 ] then install_packages "dnf" "dnf install -y" else install_packages "yum" "yum install -y" fi ;; arch|manjaro)install_packages "pacman" "pacman -S --noconfirm" ;; alpine)install_packages "apk" "apk add" ;; fedora)install_packages "dnf" "dnf install -y" ;; alinux)install_packages "yum" "yum install -y" ;; suse|opensuse*)install_packages "zypper" "zypper install -y" ;; void)install_packages "xbps" "xbps-install -Sy" ;; *)echo "Unsupported distribution: $ID" exit 1 esac elif [ -n "$PREFIX" ];then install_packages "pkg" "pkg install" else echo "Cannot detect distribution because /etc/os-release is missing." exit 1 fi fi if [[ $is_nexttrace -eq 0 ]];then curl -s nxtrace.org/nt|bash fi if [[ $is_speedtest -eq 0 ]];then install_speedtest fi if [[ $is_stun -eq 0 ]];then install_stun fi fi } install_packages(){ local package_manager=$1 local install_command=$2 case $package_manager in apt)$usesudo apt update $usesudo $install_command jq curl imagemagick mtr-tiny iperf3 stun bc procps ;; dnf)$usesudo $install_command epel-release $usesudo $package_manager makecache $usesudo $install_command jq curl ImageMagick mtr iperf3 stun bc procps-ng ;; yum)$usesudo $install_command epel-release $usesudo $package_manager makecache $usesudo $install_command jq curl ImageMagick mtr iperf3 bc procps-ng libstdc++ ;; pacman)$usesudo pacman -Sy $usesudo $install_command jq curl imagemagick mtr iperf3 bc procps-ng if command -v paru >/dev/null 2>&1;then paru -S --noconfirm gcompat elif command -v yay >/dev/null 2>&1;then yay -S --noconfirm gcompat fi ;; apk)$usesudo apk update $usesudo $install_command jq curl imagemagick mtr iperf3 bc procps gcompat libstdc++ ;; pkg)$usesudo $package_manager update $usesudo $package_manager $install_command jq curl imagemagick mtr iperf3 bc procps libstdcxx ;; brew)eval "$(/opt/homebrew/bin/brew shellenv)" $install_command jq curl imagemagick mtr iperf3 bc ;; zypper)$usesudo zypper refresh $usesudo $install_command jq curl imagemagick mtr iperf3 bc procps libstdc++6 ;; xbps)$usesudo xbps-install -Sy $usesudo $install_command jq curl imagemagick mtr iperf3 bc procps-ng gcompat libstdc++ esac } install_speedtest(){ if [ "$(uname)" == "Darwin" ];then brew tap teamookla/speedtest brew update brew install speedtest --force elif [ "$(uname)" == "FreeBSD" ];then $usesudo pkg update&&sudo pkg install -g libidn2 ca_root_nss freebsd_version=$(freebsd-version|cut -d '-' -f 1) case $freebsd_version in 12.*)$usesudo pkg add "https://install.speedtest.net/app/cli/ookla-speedtest-1.2.0-freebsd12-x86_64.pkg" ;; 13.*)$usesudo pkg add "https://install.speedtest.net/app/cli/ookla-speedtest-1.2.0-freebsd13-x86_64.pkg" ;; *)return 1 esac else local sys_type="" local sysarch="$(uname -m)" case "$sysarch" in "x86_64"|"x86"|"amd64"|"x64")sys_type="x86_64";; "i386"|"i686")sys_type="i386";; "aarch64"|"armv7l"|"armv8"|"armv8l")sys_type="aarch64";; *)return 1 esac $usesudo curl -sL -o /usr/bin/speedtest "${rawgithub}main/ref/speedtest/speedtest-$sys_type" $usesudo chmod +x /usr/bin/speedtest fi } install_stun(){ local arch local sys_type arch=$(uname -m) case "$arch" in x86_64)if [[ "$(getconf LONG_BIT)" == "32" ]] then sys_type="i386" else sys_type="amd64" fi ;; aarch64|arm64)sys_type="arm64" ;; i386|i486|i586|i686)sys_type="i386" ;; mips64|mips64el)echo "mips64el" ;; riscv64)echo "riscv64" ;; *)return 1 esac $usesudo curl -sL -o /usr/bin/stun "${rawgithub}main/ref/stun/stun_$sys_type" $usesudo chmod +x /usr/bin/stun } declare -A browsers=( [Chrome]="139.0.7258.128 139.0.7258.67 138.0.7204.185 138.0.7204.170 138.0.7204.159 138.0.7204.102 138.0.7204.100 138.0.7204.51 138.0.7204.49 137.0.7151.122 138.0.7204.35 137.0.7151.121 137.0.7151.105 137.0.7151.104 137.0.7151.57 137.0.7151.55 136.0.7103.116 137.0.7151.40 136.0.7103.113 136.0.7103.92 135.0.7049.117 136.0.7103.48 135.0.7049.114 135.0.7049.86 135.0.7049.42 135.0.7049.41 134.0.6998.167 134.0.6998.119 134.0.6998.117 134.0.6998.37 134.0.6998.35 133.0.6943.128 133.0.6943.100 133.0.6943.59 133.0.6943.53 132.0.6834.162 133.0.6943.35 132.0.6834.160 132.0.6834.112 132.0.6834.110 131.0.6778.267 132.0.6834.83 131.0.6778.264 131.0.6778.204 131.0.6778.139 131.0.6778.109 131.0.6778.71 131.0.6778.69 130.0.6723.119 131.0.6778.33 130.0.6723.116 130.0.6723.71 130.0.6723.60 130.0.6723.58 129.0.6668.103 130.0.6723.44 129.0.6668.100 129.0.6668.72 129.0.6668.60 129.0.6668.42 128.0.6613.122 128.0.6613.121 128.0.6613.115 128.0.6613.113 127.0.6533.122 128.0.6613.36 127.0.6533.119 127.0.6533.100 127.0.6533.74 127.0.6533.72 126.0.6478.185 127.0.6533.57 126.0.6478.183 126.0.6478.128 126.0.6478.116 126.0.6478.114 126.0.6478.61 125.0.6422.176 126.0.6478.56 125.0.6422.144 126.0.6478.36 125.0.6422.142 125.0.6422.114 125.0.6422.77 125.0.6422.76 124.0.6367.210 125.0.6422.60 124.0.6367.208 124.0.6367.201 124.0.6367.156 125.0.6422.41 124.0.6367.155 124.0.6367.119 124.0.6367.92 124.0.6367.63 124.0.6367.61 123.0.6312.124 124.0.6367.60 123.0.6312.122 123.0.6312.106 123.0.6312.105 123.0.6312.60 123.0.6312.58 122.0.6261.131 123.0.6312.46 122.0.6261.129 122.0.6261.128 122.0.6261.112 122.0.6261.111 122.0.6261.71 122.0.6261.69 121.0.6167.189 122.0.6261.57 121.0.6167.187 121.0.6167.186 121.0.6167.162 121.0.6167.160 121.0.6167.140 121.0.6167.86 121.0.6167.85 120.0.6099.227 120.0.6099.225 121.0.6167.75 120.0.6099.224 120.0.6099.218 120.0.6099.216 120.0.6099.200 120.0.6099.199 120.0.6099.129 120.0.6099.110 120.0.6099.109 120.0.6099.62 120.0.6099.56" [Firefox]="132.0 131.0 130.0 129.0 128.0 127.0 126.0 125.0 124.0 123.0 122.0 121.0 120.0") generate_random_user_agent(){ local browsers_keys=(${!browsers[@]}) local random_browser_index=$((RANDOM%${#browsers_keys[@]})) local browser=${browsers_keys[random_browser_index]} case $browser in Chrome)local versions=(${browsers[Chrome]}) local version=${versions[RANDOM%${#versions[@]}]} UA_Browser="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/$version Safari/537.36" ;; Firefox)local versions=(${browsers[Firefox]}) local version=${versions[RANDOM%${#versions[@]}]} UA_Browser="Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:$version) Gecko/20100101 Firefox/$version" esac } check_connectivity(){ local url="https://www.google.com/generate_204" local timeout=2 local http_code http_code=$(curl -s -o /dev/null -w "%{http_code}" --connect-timeout "$timeout" "$url" 2>/dev/null) if [[ $http_code == "204" ]];then rawgithub="https://github.com/xykt/NetQuality/raw/" return 0 else rawgithub="https://testingcf.jsdelivr.net/gh/xykt/NetQuality@" return 1 fi } is_valid_ipv4(){ local ip=$1 if [[ $ip =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]];then IFS='.' read -r -a octets <<<"$ip" for octet in "${octets[@]}";do if ((octet<0||octet>255));then IPV4work=0 return 1 fi done IPV4work=1 return 0 else IPV4work=0 return 1 fi } is_private_ipv4(){ local ip_address=$1 if [[ -z $ip_address ]];then return 0 fi if [[ $ip_address =~ ^10\. ]]||[[ $ip_address =~ ^172\.(1[6-9]|2[0-9]|3[0-1])\. ]]||[[ $ip_address =~ ^192\.168\. ]]||[[ $ip_address =~ ^127\. ]]||[[ $ip_address =~ ^0\. ]]||[[ $ip_address =~ ^22[4-9]\. ]]||[[ $ip_address =~ ^23[0-9]\. ]];then return 0 fi return 1 } get_ipv4(){ local response local API_NET=("ip.sb" "ping0.cc" "icanhazip.com" "api64.ipify.org" "ifconfig.co" "ident.me") for p in "${API_NET[@]}";do response=$(curl $CurlARG -s4 --max-time 8 "$p") if [[ $? -eq 0 && ! $response =~ error ]];then IPV4="$response" break fi done } hide_ipv4(){ if [[ -n $1 ]];then IFS='.' read -r -a ip_parts <<<"$1" IPhide="${ip_parts[0]}.${ip_parts[1]}.*.*" else IPhide="" fi } is_valid_ipv6(){ local ip=$1 if [[ $ip =~ ^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$ || $ip =~ ^([0-9a-fA-F]{1,4}:){1,7}:$ || $ip =~ ^:([0-9a-fA-F]{1,4}:){1,7}$ || $ip =~ ^([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}$ || $ip =~ ^([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}$ || $ip =~ ^([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}$ || $ip =~ ^([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}$ || $ip =~ ^([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}$ || $ip =~ ^[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})$ || $ip =~ ^:((:[0-9a-fA-F]{1,4}){1,7}|:)$ || $ip =~ ^fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}$ || $ip =~ ^::(ffff(:0{1,4}){0,1}:){0,1}(([0-9]{1,3}\.){3}[0-9]{1,3})$ || $ip =~ ^([0-9a-fA-F]{1,4}:){1,4}:(([0-9]{1,3}\.){3}[0-9]{1,3})$ ]];then IPV6work=1 return 0 else IPV6work=0 return 1 fi } is_private_ipv6(){ local address=$1 if [[ -z $address ]];then return 0 fi if [[ $address =~ ^fe80: ]]||[[ $address =~ ^fc00: ]]||[[ $address =~ ^fd00: ]]||[[ $address =~ ^2001:db8: ]]||[[ $address == ::1 ]]||[[ $address =~ ^::ffff: ]]||[[ $address =~ ^2002: ]]||[[ $address =~ ^2001: ]];then return 0 fi return 1 } get_ipv6(){ local response local API_NET=("ip.sb" "ping0.cc" "icanhazip.com" "api64.ipify.org" "ifconfig.co" "ident.me") for p in "${API_NET[@]}";do response=$(curl $CurlARG -s6k --max-time 8 "$p") if [[ $? -eq 0 && ! $response =~ error ]];then IPV6="$response" break fi done } hide_ipv6(){ if [[ -n $1 ]];then local expanded_ip=$(echo "$1"|sed 's/::/:0000:0000:0000:0000:0000:0000:0000:0000:/g'|cut -d ':' -f1-8) IFS=':' read -r -a ip_parts <<<"$expanded_ip" while [ ${#ip_parts[@]} -lt 8 ];do ip_parts+=(0000) done IPhide="${ip_parts[0]:-0}:${ip_parts[1]:-0}:${ip_parts[2]:-0}:*:*:*:*:*" IPhide=$(echo "$IPhide"|sed 's/:0\{1,\}/:/g'|sed 's/::\+/:/g') else IPhide="" fi } calculate_display_width(){ local string="$1" local length=0 local char for ((i=0; i<${#string}; i++));do char=$(echo "$string"|od -An -N1 -tx1 -j $((i))|tr -d ' ') if [ "$(printf '%d\n' 0x$char)" -gt 127 ];then length=$((length+2)) i=$((i+1)) else length=$((length+1)) fi done echo "$length" } calc_padding(){ local input_text="$1" local total_width=$2 local title_length=$(calculate_display_width "$input_text") local left_padding=$(((total_width-title_length)/2)) if [[ $left_padding -gt 0 ]];then PADDING=$(printf '%*s' $left_padding) else PADDING="" fi } parse_date(){ local input="$1" local parsed_date="" [[ -z $input ]]&&echo ""&&return if [[ $input =~ ^[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}$ ]];then parsed_date="$(echo "$input"|awk -F'-' '{printf "%04d-%02d-%02d", $1, $2, $3}')" elif [[ $input =~ ^[0-9]{8}$ ]];then parsed_date="$(echo "$input"|sed -E 's/(.{4})(.{2})(.{2})/\1-\2-\3/')" elif [[ $input =~ ^[0-9]{4}/[0-9]{1,2}/[0-9]{1,2}$ ]];then parsed_date="$(echo "$input"|awk -F'/' '{printf "%04d-%02d-%02d", $1, $2, $3}')" elif [[ $input =~ ^[0-9]{1,2}\ [A-Za-z]{3}\ [0-9]{4} ]];then parsed_date="$(date -d "$input" +"%Y-%m-%d" 2>/dev/null)" elif [[ $input =~ ^[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z$ ]];then parsed_date="$(echo "$input"|awk -F'[T-]' '{printf "%04d-%02d-%02d", $1, $2, $3}')" fi echo "${parsed_date:-}" } wrap_text(){ local indent=$1 local text=$2 local max_len=$((80-indent)) local indent_spaces=$(printf '%*s' $indent) local result="" while [[ ${#text} -gt $max_len ]];do local cut_pos=$(echo "${text:0:max_len}"|awk '{print length($0)-length($NF)}') result+="${text:0:cut_pos}\n\r$indent_spaces" text="${text:$((cut_pos))}" done result+="$text" echo -e "$result" } generate_uuidv4(){ local uuid="" local chars="0123456789abcdef" for i in {1..36};do if [[ $i == 9 || $i == 14 || $i == 19 || $i == 24 ]];then uuid+="-" elif [[ $i == 15 ]];then uuid+="4" elif [[ $i == 20 ]];then r=$((RANDOM%16)) y=$((r&0x3|0x8)) uuid+=${chars:y:1} else r=$((RANDOM%16)) uuid+=${chars:r:1} fi done echo "$uuid" } replace_html_entities(){ echo "$1"|sed -E \ -e 's/-/-/g' \ -e 's/&/\&/g' \ -e 's/'/'\''/g' \ -e 's/"/"/g' \ -e 's/<//g' \ -e 's/ / /g' \ -e 's/&/\&/g' } calc_upstream(){ local RESPONSE=$2 local src_path=$(echo "$RESPONSE"|sed -n 's|.*src="\(/pathimg/[^"]*\)".*|\1|p') local uuid=$(generate_uuidv4) local extra_string="&loggedin" local svg_content=$(curl $CurlARG -$1 --user-agent "$UA_Browser" --max-time 10 -Ls "https://bgp.tools$src_path?$uuid$extra_string") local as_cards=() local arrows=() local inside_as_card=0 local inside_arrow=0 local current_block="" while IFS= read -r line;do if [[ $line =~ \"* ]];then as_cards+=("$current_block") inside_as_card=0 current_block="" fi fi if [[ $inside_arrow -eq 1 ]];then current_block+=$'\n'"$line" if [[ $line == *""* ]];then arrows+=("$current_block") inside_arrow=0 current_block="" fi fi done <<<"$svg_content" local as_card local target_as_number="" for as_card in "${as_cards[@]}";do local id=$(echo "$as_card"|awk -F 'id="node|"' '/]' '//{print $3}'|sed 's/AS//') casn[$id]="${casn[$id]:-0}" if [[ ${casn[$id]} -eq 0 ]];then corg[$id]="IX Route" continue else corg[$id]=$(echo "$as_card"|awk -F '[<>]' '//{print $3}') corg[$id]=$(replace_html_entities "${corg[$id]}") corg[$id]="${corg[$id]:-"unknown"}" fi if echo "$as_card"|grep -q 'stroke="limegreen"';then target_as_number="${casn[$id]}" ctarget[$id]=true ctier1[$id]=false elif echo "$as_card"|grep -q 'fill="none"';then ctarget[$id]=false ctier1[$id]=false elif echo "$as_card"|grep -q 'fill="white"';then ctarget[$id]=false ctier1[$id]=true fi cupstream[$id]=false for arrow in "${arrows[@]}";do local arrow_title=$(echo "$arrow"|awk -F '[<>]' '//{print $3}') arrow_title=$(replace_html_entities "$arrow_title") if [[ $arrow_title =~ AS$target_as_number.*AS${casn[$id]} ]];then for sub_arrow in "${arrows[@]}";do local sub_arrow_title=$(echo "$sub_arrow"|awk -F '[<>]' '/<title>/{print $3}') sub_arrow_title=$(replace_html_entities "$sub_arrow_title") if [[ $sub_arrow_title =~ AS${casn[$id]}.*AS[0-9]+ ]];then cupstream[$id]=true break 2 fi done fi done done } calc_ix(){ local RESULT=$(curl $CurlARG -$1 --user-agent "$UA_Browser" --max-time 10 -Ls "https://bgp.tools/ixp-rs-route/${bgp[prefix]}") local ROWS=$(echo "$RESULT"|sed -n '/<table id="upstreamTable"/,/<\/table>/p'|grep "<tr>"|wc -l) conn[ix]=$((ROWS-1)) } calc_peers(){ local RESULT="$1" if [[ $RESULT == *"This network is transit-free."* ]];then conn[upstreams]=-2 else local ROWS=$(echo "$RESULT"|sed -n '/<table id="upstreamTable"/,/<\/table>/p'|grep "<tr>"|wc -l) conn[upstreams]=$((ROWS-1)) fi ROWS=$(echo "$RESULT"|sed -n '/<table id="peersTable"/,/<\/table>/p'|grep "<tr>"|wc -l) conn[peers]=$((ROWS-1)) } get_bgp(){ bgp[org]="${bgp[org]:-${getbgp[org-name]:-${getbgp[organization]:-${getbgp[orgname]:-${getbgp[owner]:-${getbgp[dscr]%%,*:-${getbgp[netname]:-${getbgp[ownerid]:-${getbgp[mnt-by]}}}}}}}}}" [[ ${getbgp[source]} =~ ^(RIPE|APNIC|ARIN|LACNIC|AFRINIC)$ ]]&&bgp[rir]="${getbgp[source]}" [[ -z ${bgp[rir]} && ${getbgp[mnt-by]} =~ ^(RIPE|APNIC|ARIN|LACNIC|AFRINIC)- ]]&&bgp[rir]="${BASH_REMATCH[1]}" [[ -z ${bgp[rir]} && -n ${getbgp[ownerid]} ]]&&bgp[rir]="LACNIC" [[ -z ${bgp[rir]} && -n ${getbgp[orgtechhandle]} ]]&&bgp[rir]="ARIN" if [[ -n ${getbgp[country]} ]];then [[ -z ${bgp[countrycode]} ]]&&bgp[countrycode]="${getbgp[country]}" [[ -z ${bgp[country]} ]]&&bgp[country]=$(echo "$ISO3166"|jq --arg code "${bgp[countrycode]}" -r '.[] | select(.["alpha-2"] == $code) | .name // ""') [[ -z ${bgp[region]} ]]&&bgp[region]=$(echo "$ISO3166"|jq --arg code "${bgp[countrycode]}" -r '.[] | select(.["alpha-2"] == $code) | .region // ""') [[ -z ${bgp[subregion]} ]]&&bgp[subregion]=$(echo "$ISO3166"|jq --arg code "${bgp[countrycode]}" -r '.[] | select(.["alpha-2"] == $code) | ."sub-region" // ""') [[ -z ${bgp[intermediateregion]} ]]&&bgp[intermediateregion]=$(echo "$ISO3166"|jq --arg code "${bgp[countrycode]}" -r '.[] | select(.["alpha-2"] == $code) | ."intermediate-region" // ""') [[ ${bgp[region]} == "null" ]]&&bgp[region]="" [[ ${bgp[subregion]} == "null" ]]&&bgp[subregion]="" [[ ${bgp[intermediateregion]} == "null" ]]&&bgp[intermediateregion]="" fi [[ -z ${bgp[address]} && -n ${getbgp[address]} ]]&&bgp[address]="$(echo "${getbgp[address]}"|sed 's/,\+/,/g')" [[ -z ${bgp[geofeed]} && -n ${getbgp[geofeed]} ]]&&bgp[geofeed]="${getbgp[geofeed]}" [[ -z ${bgp[regdate]} ]]&&bgp[regdate]="$(parse_date "${getbgp[created]:-${getbgp[regdate]}}")" [[ -z ${bgp[moddate]} ]]&&bgp[moddate]="$(parse_date "${getbgp[last-modified]:-${getbgp[updated]:-${getbgp[changed]}}}")" } db_bgptools(){ local temp_info="$Font_Cyan$Font_B${sinfo[bgp]}${Font_I}BGP.TOOLS $Font_Suffix" ((ibar_step+=1)) show_progress_bar "$temp_info" $((50-10-${sinfo[lbgp]}))& bar_pid="$!"&&disown "$bar_pid" trap "kill_progress_bar" RETURN bgp=() getbgp=() conn=() casn=() corg=() ctarget=() ctier1=() cupstream=() local RESPONSE=$(curl $CurlARG -$1 --user-agent "$UA_Browser" --max-time 10 -Ls "https://bgp.tools/prefix/$IP") if [[ $RESPONSE == *"Overlapping Prefixes Detected"* ]];then bgp[prefix]=$(echo "$RESPONSE"|grep -o 'href="/prefix/[^"]*'|head -1|cut -d'/' -f3-) RESPONSE=$(curl $CurlARG -$1 --user-agent "$UA_Browser" --max-time 10 -Ls "https://bgp.tools/prefix/${bgp[prefix]}") fi bgp[prefix]=$(echo "$RESPONSE"|sed -n 's/.*<p id="network-name" class="heading-xlarge">\([^<]*\)<\/p>.*/\1/p') if [[ ${bgp[prefix]} == */* ]];then bgp[ip0]="${bgp[prefix]%%/*}" bgp[prefixnum]="${bgp[prefix]##*/}" fi bgp[asn]=$(echo "$RESPONSE"|awk -v RS='<strong>' '/Originated by/ {getline; print $0}'|sed 's/<[^>]*>//g'|head -n 1) bgp[asn]="${bgp[asn]%%,*}" bgp[org]=$(echo "$RESPONSE"|sed -n 's/.*AS Name: <strong>\([^<]*\)<\/strong>.*/\1/p') local last_field_name="" local CONTENT=$(echo "$RESPONSE"|sed -n '/<div style="display: none" id="whois-page">/,/<\/div>/p'|sed -n '/<pre style="white-space: pre-wrap;">/,/<\/pre>/p'|sed 's/<pre style="white-space: pre-wrap;">//'|sed 's/<[^>]*>//g') while IFS= read -r line;do [[ -z $line ]]&&continue if [[ $line == *:* ]];then if [[ $line =~ ^([^:]+):(.*)$ ]];then field_name="${BASH_REMATCH[1],,}" field_value=$(echo "${BASH_REMATCH[2]}"|sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//') fi [[ -z $field_value ]]&&continue if [[ $field_name == "$last_field_name" ]];then getbgp["$field_name"]+=", $field_value" elif [[ -z ${getbgp[$field_name]} ]];then getbgp["$field_name"]="$field_value" last_field_name="$field_name" else last_field_name="" fi fi done <<<"$CONTENT" get_bgp calc_upstream $1 "$RESPONSE" calc_ix $1 calc_peers "$RESPONSE" } db_henet(){ local temp_info="$Font_Cyan$Font_B${sinfo[bgp]}${Font_I}HE.NET $Font_Suffix" ((ibar_step+=4)) show_progress_bar "$temp_info" $((50-7-${sinfo[lbgp]}))& bar_pid="$!"&&disown "$bar_pid" trap "kill_progress_bar" RETURN getbgp=() local last_field_name="" local attempts=0 local max_attempts=5 local RESPONSE="" local CONTENT="" while [[ $attempts -lt $max_attempts ]];do RESPONSE=$(curl $CurlARG -$1 --user-agent "$UA_Browser" --max-time 10 -Ls "https://bgp.he.net/whois/ip/$IP") if echo "$RESPONSE"|jq empty 2>/dev/null;then CONTENT=$(echo "$RESPONSE"|jq -r '.data' 2>/dev/null) if [[ -n $CONTENT ]];then break fi fi ((attempts++)) sleep 3 done CONTENT=$(echo "$CONTENT"|sed 's/\\n/\n/g'|sed 's/\\t/\t/g') while IFS= read -r line;do [[ -z $line ]]&&continue if [[ $line == *:* ]];then if [[ $line =~ ^([^:]+):(.*)$ ]];then field_name="${BASH_REMATCH[1],,}" field_value=$(echo "${BASH_REMATCH[2]}"|sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//') fi [[ -z $field_value ]]&&continue if [[ $field_name == "$last_field_name" ]];then getbgp["$field_name"]+=", $field_value" elif [[ -z ${getbgp[$field_name]} ]];then getbgp["$field_name"]="$field_value" last_field_name="$field_name" else last_field_name="" fi fi done <<<"$CONTENT" bgp[asn]="${bgp[asn]:-${getbgp[origin]:-${getbgp[originas]:-${getbgp[aut-num]}}}}" [[ ${bgp[asn]} == "N/A" ]]&&bgp[asn]="" bgp[prefix]="${bgp[prefix]:-${getbgp[route]:-${getbgp[route6]:-${getbgp[cidr]:-${getbgp[inetrev]}}}}}" if [[ ${bgp[prefix]} == */* ]];then bgp[ip0]="${bgp[prefix]%%/*}" bgp[prefixnum]="${bgp[prefix]##*/}" fi get_bgp } get_neighbor(){ local temp_info="$Font_Cyan$Font_B${sinfo[neighbor]}$Font_Suffix" ((ibar_step+=1)) show_progress_bar "$temp_info" $((50-${sinfo[lneighbor]}))& bar_pid="$!"&&disown "$bar_pid" trap "kill_progress_bar" RETURN local convert_output local cidr="${IP%.*}.0/24" if [[ ${bgp[prefixnum]} != "24" ]];then bgp[neighbortotal]="256" convert_output=$(curl -s -m 10 --user-agent "$UA_Browser" "https://bgp.tools/pfximg/$cidr"|convert png:- txt:- 2>/dev/null) [[ -n $convert_output ]]&&bgp[neighboractive]=$(grep -E -c "#0003FF|#4041BE" <<<"$convert_output") fi bgp[iptotal]="$((2**(32-${bgp[prefixnum]})))" convert_output=$(curl -s -m 10 --user-agent "$UA_Browser" "https://bgp.tools/pfximg/${bgp[prefix]}"|convert png:- txt:- 2>/dev/null) [[ -n $convert_output ]]&&bgp[ipactive]=$(grep -E -c "#0003FF|#4041BE" <<<"$convert_output") } generate_uuidv4(){ local uuid="" local chars="0123456789abcdef" for i in {1..36};do if [[ $i == 9 || $i == 14 || $i == 19 || $i == 24 ]];then uuid+="-" elif [[ $i == 15 ]];then uuid+="4" elif [[ $i == 20 ]];then r=$((RANDOM%16)) y=$((r&0x3|0x8)) uuid+=${chars:y:1} else r=$((RANDOM%16)) uuid+=${chars:r:1} fi done echo "$uuid" } get_nat(){ local temp_info="$Font_Cyan$Font_B${sinfo[nat]}$Font_Suffix" ((ibar_step+=1)) show_progress_bar "$temp_info" $((50-${sinfo[lnat]}))& bar_pid="$!"&&disown "$bar_pid" trap "kill_progress_bar" RETURN getnat=() if [[ $rawgithub == *"github.com"* ]];then local result=$(stun "stun.l.google.com" 2>/dev/null) else local result=$(stun "stun.miwifi.com" 2>/dev/null) fi getnat[nat]=$(echo "$result"|grep -oE "0x[0-9A-Fa-f]+") [[ -z ${getnat[nat]} ]]&&return 1 local val=$((${getnat[nat]})) local portpres=$((val&0x01)) local filtering=$(((val&0x06)>>1)) local mapping=$(((val&0x08)>>3)) local hairpin=$(((val&0x10)>>4)) getnat[m]=$mapping getnat[f]=$filtering getnat[p]=$portpres getnat[h]=$hairpin local ntype="fail" getnat[char]=0 if [[ ${getnat[nat]} == "0xFFFFFFFF" || ${getnat[nat]} == "0x0000EE" ]];then ntype="fail" elif [[ $mapping -eq 1 && $filtering -eq 1 ]];then ntype="firewall" elif [[ $mapping -eq 1 && $filtering -eq 2 ]];then ntype="block" elif [[ $mapping -eq 1 && $filtering -eq 3 ]];then ntype="unknown" elif [[ $mapping -eq 0 && $filtering -eq 0 ]];then ntype="open" elif [[ $mapping -eq 0 && $filtering -eq 1 ]];then ntype="full" [[ $hairpin -eq 0 ]]&&slocal[full]="$Back_Green${slocal[full]}"||slocal[full]="$Back_Yellow${slocal[full]}" getnat[char]=1 elif [[ $mapping -eq 0 && $filtering -eq 2 ]];then ntype="rest" getnat[char]=1 elif [[ $mapping -eq 0 && $filtering -eq 3 ]];then ntype="portrest" getnat[char]=1 elif [[ $mapping -eq 1 ]];then ntype="symm" getnat[char]=1 fi getnat[type]=$ntype } get_tcp(){ local temp_info="$Font_Cyan$Font_B${sinfo[tcp]}$Font_Suffix" ((ibar_step+=1)) show_progress_bar "$temp_info" $((50-${sinfo[ltcp]}))& bar_pid="$!"&&disown "$bar_pid" trap "kill_progress_bar" RETURN gettcp=() gettcp[tcpcc]=$(sysctl -n net.ipv4.tcp_congestion_control 2>/dev/null|awk '{$1=$1};1'||echo "N/A") gettcp[qdisc]=$(sysctl -n net.core.default_qdisc 2>/dev/null|awk '{$1=$1};1'||echo "N/A") gettcp[rmem]=$(sysctl -n net.ipv4.tcp_rmem 2>/dev/null|awk '{$1=$1};1'||echo "N/A") gettcp[wmem]=$(sysctl -n net.ipv4.tcp_wmem 2>/dev/null|awk '{$1=$1};1'||echo "N/A") } calculate_delay(){ local delay=0x2800 local num1=$1 local num2=$2 if [[ $(echo "$num1 > 0 && $num1 <= 80"|bc) -eq 1 ]];then delay=$((delay+0x40)) elif [[ $(echo "$num1 > 80 && $num1 <= 160"|bc) -eq 1 ]];then delay=$((delay+0x44)) elif [[ $(echo "$num1 > 160 && $num1 <= 240"|bc) -eq 1 ]];then delay=$((delay+0x46)) elif [[ $(echo "$num1 > 200"|bc) -eq 1 ]];then delay=$((delay+0x47)) fi if [[ $(echo "$num2 > 0 && $num2 <= 80"|bc) -eq 1 ]];then delay=$((delay+0x80)) elif [[ $(echo "$num2 > 80 && $num2 <= 160"|bc) -eq 1 ]];then delay=$((delay+0xA0)) elif [[ $(echo "$num2 > 160 && $num2 <= 240"|bc) -eq 1 ]];then delay=$((delay+0xB0)) elif [[ $(echo "$num2 > 240"|bc) -eq 1 ]];then delay=$((delay+0xB8)) fi local delay_hex=$(printf "%X" "$delay") local unicode_char=$(echo -e "\u${delay_hex: -4}") if [[ $(echo "$num1 > 240"|bc) -eq 1 || $(echo "$num2 > 240"|bc) -eq 1 || $(echo "$num1 == 0"|bc) -eq 1 || $(echo "$num2 == 0"|bc) -eq 1 ]];then echo "$Font_Red$unicode_char" elif [[ $(echo "$num1 > 150"|bc) -eq 1 || $(echo "$num2 > 150"|bc) -eq 1 ]];then echo "$Font_Yellow$unicode_char" elif [[ $(echo "$num1 <= 150"|bc) -eq 1 && $(echo "$num2 <= 150"|bc) -eq 1 ]];then echo "$Font_Green$unicode_char" else echo "$Font_Red$unicode_char" fi } ping_test(){ local domain=$1 local protocol=$2 local psize=$3 local count=$4 local pv=$5 local provider=$6 local ipv=$7 local port=$8 [[ -z $domain || -z $ipv || -z $protocol || -z $psize || -z $count || -z $pv || -z $provider ]]&&return 1 [[ $ipv != "4" && $ipv != "6" ]]&&return 1 [[ $protocol != "ICMP" && $protocol != "TCP" && $protocol != "UDP" ]]&&return 1 [[ -z $port ]]&&port=80 local response local tmpresu="" local pingcom="" if [[ $protocol == "ICMP" ]];then pingcom="mtr -$ipv -c 1 -f 100 -C -G 1 -s $psize $domain" elif [[ $protocol == "TCP" ]];then pingcom="mtr -$ipv --tcp -P $port -c 1 -f 100 -C -G 1 -s $psize $domain" elif [[ $protocol == "UDP" ]];then pingcom="mtr -$ipv --udp -P $port -c 1 -f 100 -C -G 1 -s $psize $domain" fi response=$($pingcom 2>&1) tmpresu=$(echo "$response"|tr -d '\n'|awk -F',' '{print $24}') [[ -z $tmpresu || ! $tmpresu =~ ^[0-9]+(\.[0-9]+)?$ || ${#tmpresu} -gt 6 ]]&&tmpresu=0.00 echo "$pv $provider $ipv $count $tmpresu" } process_pingtestresult(){ local testresult=$1 local -A midresu local tmp_space="" local ipv local index local numbers local total local count local lost local result [[ $mode_ping -eq 1 ]]&&tmp_space=" " IFS=$'\n' read -r -d '' -a lines <<<"$testresult" for line in "${lines[@]}";do line=$(echo "$line"|xargs) [[ -z $line ]]&&continue IFS=' ' read -ra parts <<<"$line" index="${parts[0]}${parts[1]}${parts[2]}${parts[3]}" ipv="${parts[2]}" pout[$index]="${parts[4]}" done for province in $(echo "${!pcode[@]}"|sort -n);do for j in 1 2 3;do total=0 count=0 lost="" for i in $(seq 1 $ping_test_count);do numbers=${pout[$province$j$ipv$i]} if [[ $numbers =~ ^0\.0*$ ]];then lost="$Font_Red" else total=$(echo "$total + $numbers"|bc) ((count++)) fi done if ((count>0));then local avg=$(echo "scale=0; $total / $count"|bc) else local avg=0 fi result="" for ((i=1; i<ping_test_count; i+=2));do local A="${pout[$province$j$ipv$i]}" local B="${pout[$province$j$ipv$((i+1))]}" local char=$(calculate_delay "$A" "$B") result+="$char" done if [[ $avg -gt 240 || $lost == "$Font_Red" ]];then lost="$Font_Red" elif [[ $avg -gt 150 ]];then lost="$Font_Yellow" elif [[ $avg -le 150 ]];then lost="$Font_Green" else lost="$Font_Red" fi pavg[$province$j$ipv]="$avg" midresu[$province$j$ipv]="$Font_Green$result$lost$Font_B$(printf '%-3s' "$avg")$Font_Suffix" done local prov_space="" [[ $mode_ping -eq 1 ]]&&prov_space=" " if [[ $YY == "cn" ]];then presu[$province]="$Font_Cyan${pshort[$province]}$prov_space${midresu[${province}1$ipv]}$tmp_space${midresu[${province}2$ipv]}$tmp_space${midresu[${province}3$ipv]}" else presu[$province]="$Font_Cyan${pcode[$province]}$prov_space${midresu[${province}1$ipv]}$tmp_space${midresu[${province}2$ipv]}$tmp_space${midresu[${province}3$ipv]}" fi done } get_delay(){ local temp_info="$Font_Cyan$Font_B${sinfo[delay]}$Font_Suffix" ((ibar_step+=1)) show_progress_bar "$temp_info" $((50-${sinfo[ldelay]}))& bar_pid="$!"&&disown "$bar_pid" trap "kill_progress_bar" RETURN [[ $mode_ping -eq 1 ]]&&ping_test_count=44 local max_threads=93 local available_memory=1024 [[ "$(uname)" != "Darwin" ]]&&available_memory=$(free -m|awk '/Mem:/ {print $7}') local max_threads_by_memory=$(echo "$available_memory / 8"|bc) ((max_threads_by_memory<max_threads))&&max_threads=$max_threads_by_memory local current_threads=0 local tmpresult=$(for i in $(seq 1 $ping_test_count) do for province in $(echo "${!pcode[@]}"|sort -n);do for j in 1 2 3;do ping_test "${pdm[$province$j$1]}" "TCP" 1400 "$i" "$province" "$j" "$1"& ((current_threads++)) if ((current_threads>=max_threads));then wait -n ((current_threads--)) fi done done done wait) process_pingtestresult "$tmpresult" } show_delay(){ local count=0 local resu_per_line=$(((display_max_len+1)/(ping_test_count*3/2+12))) if [[ $mode_ping -eq 1 ]];then resu_per_line=1 echo -ne "\r${sdelay[pingmode]}\n" else count=2 echo -ne "\r${sdelay[title]}" fi for key in $(echo "${!pcode[@]}"|tr ' ' '\n'|sort -n);do ((count++)) if ((resu_per_line==1));then echo -ne "\r${presu[$key]}\n" elif ((count%resu_per_line==0));then echo -ne "${presu[$key]}\n" elif ((count%resu_per_line==1));then echo -ne "\r${presu[$key]} " else echo -ne "${presu[$key]} " fi done ((count%resu_per_line!=0))&&echo } nexttrace_test(){ local domain="$1" local rmode="$2" local rnum="$3" local ipv="$4" local response local max_retries=10 local retry_delay=5 local retry_count=0 while [[ $retry_count -lt $max_retries ]];do response=$(timeout -s SIGKILL 50 nexttrace -p 80 -q 8 -"$ipv" --"$rmode" --raw --psize 1400 "$domain" 2>/dev/null) [[ $response != *"*please try again later*"* && $response == *"traceroute to"* ]]&&break retry_count=$((retry_count+1)) [[ $retry_count -lt $max_retries ]]&&sleep "$retry_delay" done declare -A ips asns regions orgs local max_hop=0 local cn_hop=0 local all_asn="" local tresucn="" local tresuww="NoData" while IFS= read -r line;do if [[ $line != *"|"* ]];then continue fi IFS='|' read -r -a elements <<<"$line" local hop="${elements[0]}" local ip="${elements[1]}" local asn="${elements[4]}" local region="${elements[5]}" [[ ${elements[6]} == "香港" || ${elements[6]} == "澳门" || ${elements[6]} == "台湾" ]]&®ion="${elements[6]}" local org="${elements[9]}" [[ -n ${ips[$hop]} ]]&&continue [[ $ip == 59.43.* ]]&&asn="4809" [[ $org == *CTGNet* ]]&&asn="23764" [[ $cn_hop == 0 && $region == *中国* ]]&&cn_hop=$hop [[ -n $asn ]]&&all_asn="${all_asn}AS$asn " ips["$hop"]="$ip" asns["$hop"]="$asn" regions["$hop"]="$region" orgs["$hop"]="$org" if ((hop>max_hop));then max_hop="$hop" fi done <<<"$response" [[ $cn_hop == 0 || $cn_hop == $max_hop ]]&&tresucn="Hidden" [[ ${asns[$cn_hop]} == "17676" ]]&&cn_hop=$((cn_hop+1)) case "${asns[$cn_hop]}" in "4134")tresucn="163" ;; "4837")tresucn="4837" if ((cn_hop>1));then [[ ${asns[$((cn_hop-1))]} == "10099" ]]&&tresucn="10099" fi ;; "58453")tresucn="CMI" ;; "58807")tresucn="CMIN2" ;; "9808")tresucn="CMI" [[ $all_asn == *AS58807* ]]&&tresucn="CMIN2" ;; "9929")tresucn="9929" ;; "10099")tresucn="10099" [[ $all_asn == *AS9929* ]]&&tresucn="9929" ;; "4809")tresucn="CN2GIA" if ((cn_hop>1));then [[ $all_asn == *AS23764* ]]&&tresucn="CTGGIA" else for ((hop=cn_hop; hop<=max_hop; hop++));do [[ ${asns[$hop]} == "4809" || ${asns[$hop]} == "23764" ]]&&continue if [[ ${ips[$hop]} == 202.97* ]];then tresucn="CN2GT" fi break done fi ;; "23764")tresucn="CTGGIA" ;; "4538")tresucn="CERNET" ;; "7497")tresucn="CSTNET" ;; *)tresucn="NoData" if [[ $all_asn == *AS58807* ]];then tresucn="CMIN2" elif [[ $all_asn == *AS9929* ]];then tresucn="9929" elif [[ $all_asn == *AS10099* ]];then tresucn="10099" elif [[ $all_asn == *AS4809* ]];then tresucn="CN2" elif [[ $all_asn == *AS9808* ]];then tresucn="CMI" elif [[ $all_asn == *AS4134* ]];then tresucn="163" elif [[ $all_asn == *AS4837* ]];then tresucn="4837" fi esac for ((hop=cn_hop-1; hop>0; hop--));do if [[ -n ${asns[$hop]} && ${asns[$hop]} != "58453" && ${asns[$hop]} != "58807" && ${asns[$hop]} != "4837" && ${asns[$hop]} != "10099" && ${asns[$hop]} != "9929" && ${asns[$hop]} != "4134" && ${asns[$hop]} != "4809" && ${asns[$hop]} != "4808" && ${asns[$hop]} != "23764" && ${asns[$hop]} != "4538" && ${asns[$hop]} != "7497" ]];then tresuww="AS${asns[$hop]}" break fi done if [[ -n $tresuww ]];then [[ -n ${AS_MAPPING[$tresuww]} ]]&&tresuww="${AS_MAPPING[$tresuww]}" fi echo "$rnum $tresuww $tresucn" } mtr_test(){ local domain="$1" local rmode="$2" local rnum="$3" local ipv="$4" declare -A ips declare -A asns local max_hop=0 local cn_hop=0 local all_asn="" local tresucn="NoData" local tresuww="NoData" local max_retries=10 local retry_delay=5 local retry_count=0 while [[ $retry_count -lt $max_retries ]];do response=$(timeout 60 mtr -"$ipv" --"$rmode" --no-dns -y 0 -P 80 -c 8 -C -Z 1 -G 1 -s 1400 "$domain" 2>/dev/null) [[ $response != *"*mtr:*"* && $response == *"OK,"* ]]&&break retry_count=$((retry_count+1)) [[ $retry_count -lt $max_retries ]]&&sleep "$retry_delay" done while IFS= read -r line;do [[ $line != *"OK,"* ]]&&continue IFS=',' read -r -a elements <<<"$line" local hop="${elements[4]}" local ip="${elements[5]}" [[ ${elements[6]} != *"?"* ]]&&local asn="${elements[6]#AS}" ips["$hop"]="$ip" asns["$hop"]="$asn" [[ -n $asn && $asn != *"?"* ]]&&all_asn="$all_asn$asn " ((hop>max_hop))&&max_hop="$hop" [[ $cn_hop -eq 0 ]]&&[[ $asn =~ ^("58453"|"58807"|"4837"|"10099"|"9929"|"4134"|"4809"|"23764"|"4538"|"7497")$ ]]&&cn_hop="$hop" done <<<"$response" [[ $cn_hop == 0 || $cn_hop == $max_hop ]]&&tresucn="Hidden" [[ ${asns[$cn_hop]} == "17676" ]]&&cn_hop=$((cn_hop+1)) case "${asns[$cn_hop]}" in "4134")tresucn="163" if [[ $all_asn == *AS9929* ]];then tresucn="9929" elif [[ $all_asn == *AS10099* ]];then tresucn="10099" elif [[ $all_asn == *AS23764* && $all_asn == *AS4134* ]];then tresucn="CN2GT" elif [[ $all_asn == *AS4809* && $all_asn == *AS4134* ]];then tresucn="CN2GT" elif [[ $all_asn == *AS23764* ]];then tresucn="CTGGIA" elif [[ $all_asn == *AS4809* ]];then tresucn="CN2GIA" elif [[ $all_asn == *AS58807* ]];then tresucn="CMIN2" fi ;; "4837")tresucn="4837" if [[ $all_asn == *AS9929* ]];then tresucn="9929" elif [[ $all_asn == *AS10099* ]];then tresucn="10099" elif [[ $all_asn == *AS23764* && $all_asn == *AS4134* ]];then tresucn="CN2GT" elif [[ $all_asn == *AS4809* && $all_asn == *AS4134* ]];then tresucn="CN2GT" elif [[ $all_asn == *AS23764* ]];then tresucn="CTGGIA" elif [[ $all_asn == *AS4809* ]];then tresucn="CN2GIA" elif [[ $all_asn == *AS58807* ]];then tresucn="CMIN2" fi ;; "58453")tresucn="CMI" if [[ $all_asn == *AS9929* ]];then tresucn="9929" elif [[ $all_asn == *AS10099* ]];then tresucn="10099" elif [[ $all_asn == *AS23764* && $all_asn == *AS4134* ]];then tresucn="CN2GT" elif [[ $all_asn == *AS4809* && $all_asn == *AS4134* ]];then tresucn="CN2GT" elif [[ $all_asn == *AS23764* ]];then tresucn="CTGGIA" elif [[ $all_asn == *AS4809* ]];then tresucn="CN2GIA" elif [[ $all_asn == *AS58807* ]];then tresucn="CMIN2" fi ;; "58807")tresucn="CMIN2" ;; "9808")tresucn="CMI" if [[ $all_asn == *AS9929* ]];then tresucn="9929" elif [[ $all_asn == *AS10099* ]];then tresucn="10099" elif [[ $all_asn == *AS23764* && $all_asn == *AS4134* ]];then tresucn="CN2GT" elif [[ $all_asn == *AS4809* && $all_asn == *AS4134* ]];then tresucn="CN2GT" elif [[ $all_asn == *AS23764* ]];then tresucn="CTGGIA" elif [[ $all_asn == *AS4809* ]];then tresucn="CN2GIA" elif [[ $all_asn == *AS58807* ]];then tresucn="CMIN2" fi ;; "9929")tresucn="9929" ;; "10099")tresucn="10099" [[ $all_asn == *AS9929* ]]&&tresucn="9929" ;; "4809")tresucn="CN2GIA" if ((cn_hop>1));then [[ $all_asn == *AS23764* ]]&&tresucn="CTGGIA" else for ((hop=cn_hop; hop<=max_hop; hop++));do [[ ${asns[$hop]} == "4809" || ${asns[$hop]} == "23764" ]]&&continue if [[ ${ips[$hop]} == 202.97* ]];then tresucn="CN2GT" fi break done fi ;; "23764")tresucn="CTGGIA" ;; "4538")tresucn="CERNET" ;; "7497")tresucn="CSTNET" ;; *)tresucn="NoData" if [[ $all_asn == *AS58807* ]];then tresucn="CMIN2" elif [[ $all_asn == *AS9929* ]];then tresucn="9929" elif [[ $all_asn == *AS10099* ]];then tresucn="10099" elif [[ $all_asn == *AS4809* ]];then tresucn="CN2" elif [[ $all_asn == *AS9808* ]];then tresucn="CMI" elif [[ $all_asn == *AS4134* ]];then tresucn="163" elif [[ $all_asn == *AS4837* ]];then tresucn="4837" fi esac for ((hop=cn_hop-1; hop>0; hop--));do if [[ -n ${asns[$hop]} && ${asns[$hop]} != "58453" && ${asns[$hop]} != "58807" && ${asns[$hop]} != "4837" && ${asns[$hop]} != "10099" && ${asns[$hop]} != "9929" && ${asns[$hop]} != "4134" && ${asns[$hop]} != "4809" && ${asns[$hop]} != "23764" && ${asns[$hop]} != "4538" && ${asns[$hop]} != "7497" ]];then tresuww="AS${asns[$hop]}" break fi done if [[ -n $tresuww ]];then [[ -n ${AS_MAPPING[$tresuww]} ]]&&tresuww="${AS_MAPPING[$tresuww]}" fi echo "$rnum $tresuww $tresucn" } get_route(){ ibar_step=19 local temp_info="$Font_Cyan$Font_B${sinfo[route]}$Font_Suffix" ((ibar_step+=1)) show_progress_bar "$temp_info" $((50-${sinfo[lroute]}))& bar_pid="$!"&&disown "$bar_pid" trap "kill_progress_bar" RETURN local ipv=$1 local rdomain rdomain[1]="bj-ct-v$ipv.ip.zstaticcdn.com" rdomain[2]="bj-cu-v$ipv.ip.zstaticcdn.com" rdomain[3]="bj-cm-v$ipv.ip.zstaticcdn.com" rdomain[4]="sh-ct-v$ipv.ip.zstaticcdn.com" rdomain[5]="sh-cu-v$ipv.ip.zstaticcdn.com" rdomain[6]="sh-cm-v$ipv.ip.zstaticcdn.com" rdomain[7]="gd-ct-v$ipv.ip.zstaticcdn.com" rdomain[8]="gd-cu-v$ipv.ip.zstaticcdn.com" rdomain[9]="gd-cm-v$ipv.ip.zstaticcdn.com" local max_threads=18 local available_memory=1024 [[ "$(uname)" != "Darwin" ]]&&available_memory=$(free -m|awk '/Mem:/ {print $7}') local max_threads_by_memory=$(echo "$available_memory / 28"|bc) ((max_threads_by_memory<max_threads))&&max_threads=$max_threads_by_memory local current_threads=0 local tmpresult=$(for i in $(seq 1 18) do local protocol="tcp" ((i%2==0))&&protocol="udp" if [[ $protocol == "udp" && $ipv == "6" ]];then mtr_test "${rdomain[$(((i+1)/2))]}" "$protocol" "$i" "$ipv"& else nexttrace_test "${rdomain[$(((i+1)/2))]}" "$protocol" "$i" "$ipv"& fi ((current_threads++)) if ((current_threads>=max_threads));then wait -n ((current_threads--)) else sleep 2 fi done wait) while IFS= read -r line;do [[ -z $line ]]&&continue read -r index rww_value rcn_value <<<"$line" [[ $rcn_value == *GIA ]]&&rgia=1 done <<<"$tmpresult" while IFS= read -r line;do [[ -z $line ]]&&continue read -r index rww_value rcn_value <<<"$line" rww["$index"]="$rww_value" rcn["$index"]="$rcn_value" [[ ${rcn["$index"]} == "CN2GT" && $rgia -eq 1 ]]&&rcn["$index"]="CN2GIA" printf -v spaceww "%$((8-${#rww_value}))s" "" printf -v spacecn "%$((6-${#rcn_value}))s" "" case "$rww_value" in "Hidden")colorww="$Back_Red$Font_White$Font_U$Font_B";; "NoData")colorww="$Back_Yellow$Font_White$Font_U$Font_B";; *)colorww="$Back_Blue$Font_White$Font_U$Font_B" esac case "$rcn_value" in "Hidden")colorcn="$Back_Red$Font_White$Font_U$Font_B";; "NoData")colorcn="$Back_Yellow$Font_White$Font_U$Font_B";; *)colorcn="$Back_Green$Font_White$Font_U$Font_B" esac routww["$index"]="$spaceww$colorww$rww_value$Font_Suffix" routcn["$index"]="$colorcn$rcn_value$Font_Suffix$spacecn" done <<<"$tmpresult" } function colorize_latency(){ local latency_ms=$1 local total_length=${2:-2} local value local pure_text local padding local colored_result if [[ ! $latency_ms =~ ^[0-9]+(\.[0-9]+)?ms$ ]];then echo "" return 1 fi value=$(echo "$latency_ms"|sed 's/ms//') if (($(echo "$value <= 150"|bc -l)));then colored_result="$Font_Green$latency_ms" elif (($(echo "$value <= 240"|bc -l)));then colored_result="$Font_Yellow$latency_ms" else colored_result="$Font_Red$latency_ms" fi pure_text="$latency_ms" pure_length=${#pure_text} if ((pure_length<total_length));then padding=$((total_length-pure_length)) colored_result="$(printf "%${padding}s" "")$colored_result" fi colored_result="$colored_result$Font_Suffix" echo -n "$colored_result" return 0 } show_route(){ echo -ne "\r${sroute[title]}\n" if [[ $mode_route -eq 0 ]];then echo -ne "\r$Font_Cyan北京TCP:$Font_Green电信 $Font_Suffix${routww[1]}$Font_Green->$Font_Suffix${routcn[1]} || $Font_Green联通$Font_Suffix $Font_Suffix${routww[3]}$Font_Green->$Font_Suffix${routcn[3]} || $Font_Green移动$Font_Suffix $Font_Suffix${routww[5]}$Font_Green->$Font_Suffix${routcn[5]}\n" echo -ne "\r$Font_Cyan北京UDP:$Font_Green电信 $Font_Suffix${routww[2]}$Font_Green->$Font_Suffix${routcn[2]} || $Font_Green联通$Font_Suffix $Font_Suffix${routww[4]}$Font_Green->$Font_Suffix${routcn[4]} || $Font_Green移动$Font_Suffix $Font_Suffix${routww[6]}$Font_Green->$Font_Suffix${routcn[6]}\n" echo -ne "\r$Font_Cyan上海TCP:$Font_Green电信 $Font_Suffix${routww[7]}$Font_Green->$Font_Suffix${routcn[7]} || $Font_Green联通$Font_Suffix $Font_Suffix${routww[9]}$Font_Green->$Font_Suffix${routcn[9]} || $Font_Green移动$Font_Suffix $Font_Suffix${routww[11]}$Font_Green->$Font_Suffix${routcn[11]}\n" echo -ne "\r$Font_Cyan上海UDP:$Font_Green电信 $Font_Suffix${routww[8]}$Font_Green->$Font_Suffix${routcn[8]} || $Font_Green联通$Font_Suffix $Font_Suffix${routww[10]}$Font_Green->$Font_Suffix${routcn[10]} || $Font_Green移动$Font_Suffix $Font_Suffix${routww[12]}$Font_Green->$Font_Suffix${routcn[12]}\n" echo -ne "\r$Font_Cyan广州TCP:$Font_Green电信 $Font_Suffix${routww[13]}$Font_Green->$Font_Suffix${routcn[13]} || $Font_Green联通$Font_Suffix $Font_Suffix${routww[15]}$Font_Green->$Font_Suffix${routcn[15]} || $Font_Green移动$Font_Suffix $Font_Suffix${routww[17]}$Font_Green->$Font_Suffix${routcn[17]}\n" echo -ne "\r$Font_Cyan广州UDP:$Font_Green电信 $Font_Suffix${routww[14]}$Font_Green->$Font_Suffix${routcn[14]} || $Font_Green联通$Font_Suffix $Font_Suffix${routww[16]}$Font_Green->$Font_Suffix${routcn[16]} || $Font_Green移动$Font_Suffix $Font_Suffix${routww[18]}$Font_Green->$Font_Suffix${routcn[18]}\n" else local tmppv="" local tmpfont="" local tmpback="" local tmpdelay="" for ((i=1; i<=rmtestnum; i++));do ii=$(printf "%02d" "$i") case $((i%3)) in 1)tmppv="电信" tmpfont="$Font_Cyan" tmpback="$Back_Cyan" ;; 2)tmppv="联通" tmpfont="$Font_Green" tmpback="$Back_Green" ;; 0)tmppv="移动" tmpfont="$Font_Purple" tmpback="$Back_Purple" esac echo -ne "\r$tmpback$Font_White$Font_B ${pname[${rmcode[$i]}]} $tmppv $Font_Suffix$Back_White$tmpfont$Font_B ${rmww[$ii]} -> ${rmcn[$ii]} $Font_Suffix\n" echo -ne "\r$Back_Blue$Font_White地理路径:${rmallgeo[$ii]} 自治系统路径:${rmallasn[$ii]} $Font_Suffix\n" local varb="${rmmaxhop[$ii]:-0}" varb=${varb#0} local mergejump=" " for ((j=1; j<=varb; j++));do jj=$(printf "%02d" "$j") mergeroute=0 mergejump=" " if [[ -n ${rmresu[$ii${jj}2]} ]];then local compA="${rmresu[$ii${jj}5]#"${rmresu[$ii${jj}5]%%[![:space:]\*]*}"}" compA=$(awk '{print $1 " " $2}' <<<"$compA") for ((k=j+1; k<varb; k++));do kk=$(printf "%02d" "$k") local compB="${rmresu[$ii${kk}5]#"${rmresu[$ii${kk}5]%%[![:space:]\*]*}"}" compB=$(awk '{print $1 " " $2}' <<<"$compB") if [[ -n ${rmresu[$ii${kk}2]} && ${rmresu[$ii${jj}3]} == "${rmresu[$ii${kk}3]}" && ${rmresu[$ii${jj}4]} == "${rmresu[$ii${kk}4]}" ]]&&[[ $compA == *"$compB"* || $compB == *"$compA"* ]];then mergejump="-$(printf '%-2s' "$k")" continue fi [[ -n ${rmresu[$ii${kk}2]} ]]&&break done if ((j!=varb));then for ((k=j-1; k>0; k--));do kk=$(printf "%02d" "$k") local compB="${rmresu[$ii${kk}5]#"${rmresu[$ii${kk}5]%%[![:space:]\*]*}"}" compB=$(awk '{print $1 " " $2}' <<<"$compB") if [[ -n ${rmresu[$ii${kk}2]} && ${rmresu[$ii${jj}3]} == "${rmresu[$ii${kk}3]}" && ${rmresu[$ii${jj}4]} == "${rmresu[$ii${kk}4]}" ]]&&[[ $compA == *"$compB"* || $compB == *"$compA"* ]];then continue 2 fi done fi tmpdelay=$(colorize_latency "${rmresu[$ii${jj}1]}" 9) if [[ $1 -eq 4 ]];then echo -ne "\r$Font_B$(printf '%2s' "$j")$mergejump$Font_Suffix $tmpdelay $(printf '%-13s' "${rmresu[$ii${jj}2]}")$Font_B$(printf '%-10s' "${rmresu[$ii${jj}3]}")$(printf '%-18s' "${rmresu[$ii${jj}4]}")$Font_Suffix${rmresu[$ii${jj}5]}\n" else echo -ne "\r$Font_B$(printf '%2s' "$j")$mergejump$Font_Suffix $tmpdelay $(printf '%-27s' "${rmresu[$ii${jj}2]}")$Font_B$(printf '%-10s' "${rmresu[$ii${jj}3]}")$Font_Suffix${rmresu[$ii${jj}5]}\n" fi fi done done fi } function mask_ip(){ local ip=$1 if [[ $ip =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]];then echo "$ip"|awk -F. '{print $1"."$2".*.*"}' elif [[ $ip =~ ^([0-9a-fA-F]{0,4}:){2,7}[0-9a-fA-F]{0,4}$ ]];then echo "$ip"|awk -F: '{ # 计算需要保留的段数(总段数8减去要掩码的5段) keep = 8 - 5; for(i=1; i<=NF; i++) { if(i <= keep) { printf "%s", $i; } else { printf "*"; } if(i < 8) printf ":"; } # 处理压缩形式的IPv6(::) if(NF < 8) { for(i=NF+1; i<=8; i++) { if(i <= keep) { printf "0"; } else { printf "*"; } if(i < 8) printf ":"; } } }' else return 1 fi } function extract_region(){ local input=$1 IFS=' ' read -ra parts <<<"$input" local discard_patterns=("*" "中国" "电信" "联通" "移动") local suffixes=("省" "市" "县" "维吾尔自治区" "回族自治区" "壮族自治区" "自治区" "特别行政区") for part in "${parts[@]}";do if [[ -z $part || $part =~ ^[[:punct:]]+$ || $part == *"."* || $part == *"RFC"* || $part == *"rfc"* || $part == *"Private"* || $part == *"Local"* || $part == *"DOD"* || $part == *"Anycast"* || $part == *"ASAPI"* || $part == *"网络故障"* ]];then continue fi for pattern in "${discard_patterns[@]}";do [[ $part == "$pattern" ]]&&continue 2 done for suffix in "${suffixes[@]}";do if [[ $part == *"$suffix" ]];then part="${part%"$suffix"}" break fi done echo "$part" return done echo "" } nexttrace_route(){ local domain="$1" local rmode="$2" local rnum="$3" local ipv="$4" local tmode case "$rmode" in 1)tmode="--tcp";; 2)tmode="--udp" esac local output local max_retries=10 local retry_delay=5 local retry_count=0 while [[ $retry_count -lt $max_retries ]];do output=$(timeout -s SIGKILL 50 nexttrace -p 80 -q 8 -"$ipv" "$tmode" --psize 1400 "$domain" 2>/dev/null) [[ $output != *"*please try again later*"* && $output == *"traceroute to"* ]]&&break retry_count=$((retry_count+1)) [[ $retry_count -lt $max_retries ]]&&sleep "$retry_delay" done output=$(echo "$output"|sed 's/\x1b\[[0-9;]*m//g') echo "$output"|awk -v rnum="$rnum" ' { if ($1 ~ /^[0-9]+$/) { hop = $1 ip = $2 if (ip != "*") { as = "" bracket = "" desc = "" ms = "" # Extract AS information for (i = 3; i <= NF; i++) { if ($i ~ /^AS[0-9]+/) { as = $i } else if ($i ~ /^\[.*\]$/) { bracket = $i } else { desc = desc " " $i } } # 去除 desc 开头的空格 sub(/^ /, "", desc) # Get the first millisecond value from the next line getline next_line if (next_line ~ /[0-9]+\.[0-9]+ ms/) { match(next_line, /[0-9]+\.[0-9]+ ms/) ms = substr(next_line, RSTART, RLENGTH) gsub(/ /, "", ms) } # Print the formatted result printf "|%s|%s|%s|%s|%s|%s|%s|\n", rnum, hop, ms, ip, as, bracket, desc } } }' } get_route_mode(){ ibar_step=19 local temp_info="$Font_Cyan$Font_B${sinfo[moderoute]}$Font_Suffix" ((ibar_step+=1)) show_progress_bar "$temp_info" $((50-${sinfo[lmoderoute]}))& bar_pid="$!"&&disown "$bar_pid" trap "kill_progress_bar" RETURN local ipv=$1 local rdomain rmresu=() rmcode[0]=31 if [[ -z $mode_route_pv ]];then rmtestnum=9 rmcode[1]=11 rmcode[2]=11 rmcode[3]=11 rmcode[4]=31 rmcode[5]=31 rmcode[6]=31 rmcode[7]=44 rmcode[8]=44 rmcode[9]=44 rdomain[0]="sh-ct-v$ipv.ip.zstaticcdn.com" rdomain[1]="bj-ct-v$ipv.ip.zstaticcdn.com" rdomain[2]="bj-cu-v$ipv.ip.zstaticcdn.com" rdomain[3]="bj-cm-v$ipv.ip.zstaticcdn.com" rdomain[4]="sh-ct-v$ipv.ip.zstaticcdn.com" rdomain[5]="sh-cu-v$ipv.ip.zstaticcdn.com" rdomain[6]="sh-cm-v$ipv.ip.zstaticcdn.com" rdomain[7]="gd-ct-v$ipv.ip.zstaticcdn.com" rdomain[8]="gd-cu-v$ipv.ip.zstaticcdn.com" rdomain[9]="gd-cm-v$ipv.ip.zstaticcdn.com" else rmtestnum=3 rmcode[1]="$mode_route_pv" rmcode[2]="$mode_route_pv" rmcode[3]="$mode_route_pv" rdomain[0]="sh-ct-v$ipv.ip.zstaticcdn.com" rdomain[1]="${pcode[$mode_route_pv]}-ct-v$ipv.ip.zstaticcdn.com" rdomain[2]="${pcode[$mode_route_pv]}-cu-v$ipv.ip.zstaticcdn.com" rdomain[3]="${pcode[$mode_route_pv]}-cm-v$ipv.ip.zstaticcdn.com" fi local max_threads=18 local available_memory=1024 [[ "$(uname)" != "Darwin" ]]&&available_memory=$(free -m|awk '/Mem:/ {print $7}') local max_threads_by_memory=$(echo "$available_memory / 28"|bc) ((max_threads_by_memory<max_threads))&&max_threads=$max_threads_by_memory local current_threads=0 local tmpresult=$(for i in $(seq 0 $rmtestnum) do local protocol=1 nexttrace_route "${rdomain[$i]}" "$protocol" "$i" "$ipv"& ((current_threads++)) if ((current_threads>=max_threads));then wait -n ((current_threads--)) else sleep 2 fi done wait) rmmaxhop=() rmcnhop=() rmallasn=() rmallgeo=() while IFS= read -r line;do [[ -z $line ]]&&continue IFS='|' read -ra fields <<<"$line" i1=$(printf "%02d" "${fields[1]}") i2=$(printf "%02d" "${fields[2]}") rmresu["$i1${i2}1"]="${fields[3]}" rmresu["$i1${i2}2"]=$(mask_ip "${fields[4]}") rmresu["$i1${i2}3"]="${fields[5]}" rmresu["$i1${i2}4"]="${fields[6]}" rmresu["$i1${i2}5"]="${fields[7]}" rmresu["$i1${i2}6"]=$(extract_region "${rmresu[$i1${i2}5]}") [[ ${rmresu["$i1${i2}2"]} == 59.43.* ]]&&rmresu["$i1${i2}3"]="AS4809" [[ ${rmresu["$i1${i2}5"]} == *CTGNet* ]]&&rmresu["$i1${i2}3"]="AS23764" if [[ -z ${rmcnhop["$i1"]} ]]&&[[ ${rmresu["$i1${i2}5"]} == *中国* ]]&&[[ ${rmresu["$i1${i2}5"]} != *香港* ]]&&[[ ${rmresu["$i1${i2}5"]} != *澳门* ]]&&[[ ${rmresu["$i1${i2}5"]} != *台湾* ]];then rmcnhop["$i1"]="$i2" fi if [[ -n ${rmresu[$i1${i2}6]} ]];then if [[ ${rmallgeo[$i1]} == *"${rmresu[$i1${i2}6]}"* ]];then rmallgeo[$i1]="${rmallgeo[$i1]%${rmresu[$i1${i2}6]}*}${rmresu[$i1${i2}6]}" else [[ -n ${rmallgeo[$i1]} ]]&&rmallgeo[$i1]="${rmallgeo[$i1]} -> " rmallgeo[$i1]="${rmallgeo[$i1]}${rmresu[$i1${i2}6]}" fi fi if [[ ${rmallgeo[$i1]} != *"${rmresu[$i1${i2}6]}" ]];then [[ -n ${rmallgeo[$i1]} ]]&&rmallgeo[$i1]="${rmallgeo[$i1]} -> " rmallgeo[$i1]="${rmallgeo[$i1]}${rmresu[$i1${i2}6]}" fi if [[ -n ${rmresu["$i1${i2}3"]} && ${rmallasn[$i1]} != *"${rmresu[$i1${i2}3]}" ]];then [[ -n ${rmallasn["$i1"]} ]]&&rmallasn["$i1"]="${rmallasn["$i1"]} -> " rmallasn["$i1"]="${rmallasn["$i1"]}${rmresu[$i1${i2}3]}" fi local vara=${i2#0} local varb="${rmmaxhop["$i1"]:-0}" varb=${varb#0} if ((vara>varb));then rmmaxhop["$i1"]="$i2" fi done <<<"$tmpresult" rmcn=() rmww=() for i in $(seq 0 $rmtestnum);do ii=$(printf "%02d" "$i") rmcn[$ii]="Unknown" rmww[$ii]="Unknown" [[ ${rmcnhop[$ii]} == 0 || ${rmcnhop[$ii]} == ${rmmaxhop[$ii]} ]]&&rmcn[$ii]="Hidden" local vara="${rmcnhop[$ii]:-0}" vara=${vara#0} [[ ${rmresu[$ii${rmcnhop[$ii]}3]} == "AS17676" ]]&&rmcnhop[$ii]=$(printf "%02d" "$((vara+1))") case "${rmresu[$ii${rmcnhop[$ii]}3]}" in "AS4134")rmcn[$ii]="163" ;; "AS4837")rmcn[$ii]="4837" local vara="${rmcnhop[$ii]:-0}" vara=${vara#0} if ((vara>1));then varb=$(printf "%02d" "$((vara-1))") [[ ${rmresu[$ii${varb}3]} == "AS10099" ]]&&rmcn[$ii]="10099" fi ;; "AS58453")rmcn[$ii]="CMI" ;; "AS58807")rmcn[$ii]="CMIN2" ;; "AS9808")rmcn[$ii]="CMI" [[ ${rmallasn[$ii]} == *AS58807* ]]&&rmcn[$ii]="CMIN2" ;; "AS9929")rmcn[$ii]="9929" ;; "AS10099")rmcn[$ii]="10099" [[ ${rmallasn[$ii]} == *AS9929* ]]&&rmcn[$ii]="9929" ;; "AS4809")rmcn[$ii]="CN2GIA" local vara="${rmcnhop[$ii]:-0}" local varb="${rmmaxhop[$ii]:-0}" vara=${vara#0} varb=${varb#0} if ((vara>1));then [[ ${rmallasn[$ii]} == *AS23764* ]]&&rmcn[$ii]="CTGGIA" fi if [[ $rmgia -ne 1 ]];then for ((thop=vara; thop<=varb; thop++));do hop=$(printf "%02d" "$thop") [[ ${rmresu[$ii${hop}3]} == "AS4809" || ${rmresu[$ii${hop}3]} == "AS23764" ]]&&continue if [[ ${rmresu[$ii${hop}2]} == 202.97* ]];then rmcn[$ii]="CN2GT" fi break done fi ;; "AS23764")rmcn[$ii]="CTGGIA" if [[ $rmgia -ne 1 ]];then local vara="${rmcnhop[$ii]:-0}" local varb="${rmmaxhop[$ii]:-0}" vara=${vara#0} varb=${varb#0} for ((thop=vara; thop<=varb; thop++));do hop=$(printf "%02d" "$thop") [[ ${rmresu[$ii${hop}3]} == "AS4809" || ${rmresu[$ii${hop}3]} == "AS23764" ]]&&continue if [[ ${rmresu[$ii${hop}2]} == 202.97* ]];then rmcn[$ii]="CN2GT" fi break done fi ;; "AS4538")rmcn[$ii]="CERNET" ;; "AS7497")rmcn[$ii]="CSTNET" ;; *)rmcn[$ii]="NoData" if [[ ${rmallasn[$ii]} == *AS58807* ]];then rmcn[$ii]="CMIN2" elif [[ ${rmallasn[$ii]} == *AS9929* ]];then rmcn[$ii]="9929" elif [[ ${rmallasn[$ii]} == *AS10099* ]];then rmcn[$ii]="10099" elif [[ ${rmallasn[$ii]} == *AS4809* ]];then rmcn[$ii]="CN2" elif [[ ${rmallasn[$ii]} == *AS9808* ]];then rmcn[$ii]="CMI" elif [[ ${rmallasn[$ii]} == *AS4134* ]];then rmcn[$ii]="163" elif [[ ${rmallasn[$ii]} == *AS4837* ]];then rmcn[$ii]="4837" fi esac [[ ${rmcn[$ii]} == *GIA ]]&&rmgia=1 local vara="${rmcnhop[$ii]:-0}" vara=${vara#0} for ((thop=vara-1; thop>0; thop--));do hop=$(printf "%02d" "$thop") if [[ -n ${rmresu[$ii${hop}3]} && ${rmresu[$ii${hop}3]} != "AS58453" && ${rmresu[$ii${hop}3]} != "AS58807" && ${rmresu[$ii${hop}3]} != "AS4837" && ${rmresu[$ii${hop}3]} != "AS10099" && ${rmresu[$ii${hop}3]} != "AS9929" && ${rmresu[$ii${hop}3]} != "AS4134" && ${rmresu[$ii${hop}3]} != "AS4809" && ${rmresu[$ii${hop}3]} != "AS4808" && ${rmresu[$ii${hop}3]} != "AS23764" && ${rmresu[$ii${hop}3]} != "AS4538" && ${rmresu[$ii${hop}3]} != "AS7497" ]];then rmww[$ii]="${rmresu[$ii${hop}3]}" break fi done if [[ -n ${rmww[$ii]} ]];then [[ -n ${AS_MAPPING[${rmww[$ii]}]} ]]&&rmww[$ii]="${AS_MAPPING[${rmww[$ii]}]}" fi done } parse_iperf3_result(){ local server=$1 local portl=$2 local portu=$3 local ipv="-4" [[ $4 -eq 6 ]]&&ipv="-6" local sendrecv="" [[ $5 -eq 1 ]]&&sendrecv=" -R" local maxtry=5 local port=0 iperfresu[s]=-1 iperfresu[r]=-1 local infolen if [[ $YY == "cn" ]];then infolen=$((${#6}*2)) else infolen=${#6} fi local temp_info="$Font_Cyan$Font_B${sinfo[iperf]}$6$Font_Suffix" ((ibar_step+=2)) show_progress_bar "$temp_info" $((50-${sinfo[liperf]}-infolen))& bar_pid="$!"&&disown "$bar_pid" trap "kill_progress_bar" RETURN for ((i=1; i<=maxtry; i++));do port=$((RANDOM%(portu-portl+1)+portl)) local response=$(timeout 20 iperf3 $ipv$sendrecv -J -t 6 -c "$server" -p "$port" 2>&1) if [[ -n $response && $response != *"iperf3: error"* && $response != *"\"error\":"* ]];then local bits_per_second=$(echo "$response"|jq -r '.end.sum_received.bits_per_second') local retransmits=$(echo "$response"|jq -r '.end.sum_sent.retransmits') if [[ -n $bits_per_second && $bits_per_second != "null" && -n $retransmits && $retransmits != "null" ]];then iperfresu[s]=$bits_per_second iperfresu[r]=$retransmits return 0 fi fi done return 1 } convert_b2m(){ local bps=$1 local mbps local color if (($(echo "$bps < 0"|bc -l)));then echo -e "$Font_Red E$Font_Suffix" return else mbps=$(echo "$bps / 1000000"|bc) fi if [ "$mbps" -gt 99999 ];then echo -e "$Font_Green ${Font_U}100G+$Font_Suffix" return fi if [ "$mbps" -lt 50 ];then color=$Font_Red elif [ "$mbps" -ge 50 ]&&[ "$mbps" -lt 200 ];then color=$Font_Yellow elif [ "$mbps" -ge 200 ];then color=$Font_Green else color=$Font_Red fi local tmp_space=$((6-${#mbps})) echo "$color$(printf "%${tmp_space}s\n")$Font_U$mbps$Font_Suffix" } convert_retr(){ local num=$1 local color local result if [ "$num" -lt 0 ];then echo -e "${Font_Red}RROR$Font_Suffix" return fi if [[ $num -eq 0 ]];then color=$Font_Green elif [[ $num -ge 1 && $num -le 99 ]];then color=$Font_Yellow elif [[ $num -gt 99 ]];then color=$Font_Red else color=$Font_Red fi if [[ $num -lt 1000 ]];then result=$num elif [[ $num -ge 1000 && $num -lt 10000 ]];then result="${num:0:1}k" elif [[ $num -ge 10000 && $num -lt 100000 ]];then result="${num:0:2}k" elif [[ $num -ge 100000 && $num -lt 1000000 ]];then result=".${num:0:1}m" else result="1m+" fi echo "$color$(printf '%4s' "$result")$Font_Suffix" } process_wwpingtestresult(){ local testresult=$1 local -A midresu local -A midresu2 local tmp_space local ipv local index local numbers local total local count local lost local result IFS=$'\n' read -r -d '' -a lines <<<"$testresult" for line in "${lines[@]}";do line=$(echo "$line"|xargs) [[ -z $line ]]&&continue IFS=' ' read -ra parts <<<"$line" index="${parts[1]}${parts[2]}${parts[3]}" ipv="${parts[2]}" ipout[$index]="${parts[4]}" done local keys=($(echo "${!icity[@]}"|tr ' ' '\n'|sort -n)) for key in "${keys[@]}";do total=0 count=0 lost="" for i in $(seq 1 $pingww_test_count);do numbers=${ipout[$key$ipv$i]} if [[ $numbers =~ ^0\.0*$ ]];then lost="$Font_Red" else total=$(echo "$total + $numbers"|bc) ((count++)) fi done if ((count>0));then local avg=$(echo "scale=0; $total / $count"|bc) else local avg=0 fi result="" for ((i=1; i<pingww_test_count; i+=2));do local A="${ipout[$key$ipv$i]}" local B="${ipout[$key$ipv$((i+1))]}" local char=$(calculate_delay "$A" "$B") result+="$char" done if [[ $avg -gt 240 || $lost == "$Font_Red" ]];then lost="$Font_Red" elif [[ $avg -gt 150 ]];then lost="$Font_Yellow" elif [[ $avg -le 150 ]];then lost="$Font_Green" else lost="$Font_Red" fi iavg[$key]="$avg" midresu[$key$ipv]="$Font_Green$result$lost$Font_B$(printf '%3s' "$avg")$Font_Suffix" if [[ $YY == "cn" ]];then tmp_space=$((10-${#icity[$key]}*2)) else tmp_space=$((10-${#icity[$key]})) fi if [[ $mode_low -eq 1 ]];then iresu[$key]="$Font_Cyan${icity[$key]}$(printf "%${tmp_space}s\n")${midresu[$key$ipv]} ${Font_Green}SKIP$Font_Suffix " else iresu[$key]="$Font_Cyan${icity[$key]}$(printf "%${tmp_space}s\n")${midresu[$key$ipv]}$(convert_b2m ${isout[$key${ipv}1]})$(convert_retr ${isout[$key${ipv}2]})$(convert_b2m ${isout[$key${ipv}3]})$(convert_retr ${isout[$key${ipv}4]})" fi done } iperf_test(){ ibar_step=48 local ipv=$1 local port=0 local json_data=$(curl -sL "${rawgithub}main/ref/iperf.json") while IFS=" " read -r code server portl portu city cityzh;do if [[ $YY == "cn" ]];then icity["$code"]="$cityzh" else icity["$code"]="$city" fi idm["$code"]="$server" iportl["$code"]="$portl" iportu["$code"]="$portu" done < <(echo "$json_data"|jq -r '.[] | "\(.code) \(.server) \(.portl) \(.portu) \(.city) \(.cityzh)"') local keys=($(echo "${!icity[@]}"|tr ' ' '\n'|sort -n)) if [[ $mode_low -eq 0 ]];then for key in "${keys[@]}";do parse_iperf3_result "${idm[$key]}" ${iportl[$key]} ${iportu[$key]} $ipv 0 "${icity[$key]}${siperf[send]}" isout["$key${ipv}1"]=${iperfresu[s]} isout["$key${ipv}2"]=${iperfresu[r]} parse_iperf3_result "${idm[$key]}" ${iportl[$key]} ${iportu[$key]} $ipv 1 "${icity[$key]}${siperf[recv]}" isout["$key${ipv}3"]=${iperfresu[s]} isout["$key${ipv}4"]=${iperfresu[r]} done fi local temp_info="$Font_Cyan$Font_B${sinfo[delayww]}$Font_Suffix" ((ibar_step+=2)) show_progress_bar "$temp_info" $((50-${sinfo[ldelayww]}))& bar_pid="$!"&&disown "$bar_pid" trap "kill_progress_bar" RETURN local max_threads=10 local available_memory=1024 [[ "$(uname)" != "Darwin" ]]&&available_memory=$(free -m|awk '/Mem:/ {print $7}') local max_threads_by_memory=$(echo "$available_memory / 8"|bc) ((max_threads_by_memory<max_threads))&&max_threads=$max_threads_by_memory local current_threads=0 local tmpresult=$(for i in $(seq 1 $pingww_test_count) do for key in "${keys[@]}";do if [[ $key -eq 41 ]];then ping_test "speedtest.fra1.de.leaseweb.net" "TCP" 1400 "$i" "${icity[$key]}" "$key" "$ipv"& elif [[ $key -eq 43 ]];then ping_test "speedtest.ams1.nl.leaseweb.net" "TCP" 1400 "$i" "${icity[$key]}" "$key" "$ipv"& else ping_test "${idm[$key]}" "TCP" 1400 "$i" "${icity[$key]}" "$key" "$ipv"& fi ((current_threads++)) if ((current_threads>=max_threads));then wait -n ((current_threads--)) fi done done wait) process_wwpingtestresult "$tmpresult" } show_iperf(){ echo -ne "\r${siperf[title]}\n" local count=0 local keys=($(echo "${!icity[@]}"|tr ' ' '\n'|sort -n)) for key in "${keys[@]}";do ((count++)) if ((count%2==0));then echo -ne "${iresu[$key]}\n" else echo -ne "\r${iresu[$key]}||" fi done ((count%2!=0))&&echo } parse_speedtest_result(){ local tid="$1" local tcode="$2" local infolen if [[ $YY == "cn" ]];then infolen=$((${#scity[$tcode]}*2+${#spv[$tcode]}*2)) infotxt="${scity[$tcode]}${spv[$tcode]}" else infolen=$((${#scity[$tcode]}+${#spv[$tcode]}+1)) infotxt="${scity[$tcode]} ${spv[$tcode]}" fi local temp_info="$Font_Cyan$Font_B${sinfo[speedtest]}$infotxt$Font_Suffix" ((ibar_step+=2)) show_progress_bar "$temp_info" $((50-${sinfo[lspeedtest]}-infolen))& bar_pid="$!"&&disown "$bar_pid" trap "kill_progress_bar" RETURN local maxtry=1 sout["${tcode}1"]=-1 sout["${tcode}2"]=-1 sout["${tcode}3"]=-1 sout["${tcode}4"]=-1 local i for ((i=1; i<=maxtry; i++));do local response=$(speedtest --accept-gdpr --accept-license -f json -s "$tid" 2>&1) response=$(echo "$response"|sed -n '/^{/,/^}/p') if [[ -n $response && $response != *"[error]"* && $response != *"\"error\""* ]];then local download_bandwidth=$(echo "$response"|jq '.download.bandwidth') sout["${tcode}3"]=$((download_bandwidth*8)) local upload_bandwidth=$(echo "$response"|jq '.upload.bandwidth') sout["${tcode}1"]=$((upload_bandwidth*8)) local download_latency_iqm=$(echo "$response"|jq '.download.latency.iqm') sout["${tcode}4"]=${download_latency_iqm%.*} local upload_latency_iqm=$(echo "$response"|jq '.upload.latency.iqm') sout["${tcode}2"]=${upload_latency_iqm%.*} return 0 fi done return 1 } convert_delay(){ local num=$1 local color local result if [[ ! $num =~ ^-?[0-9]+$ ]];then echo -e "$Font_Red -$Font_Suffix" return fi if [ "$num" -lt 0 ];then echo -e "${Font_Red}RROR $Font_Suffix" return fi if [[ $num -eq 0 ]];then color=$Font_Red elif [[ $num -ge 1 && $num -le 150 ]];then color=$Font_Green elif [[ $num -ge 151 && $num -le 240 ]];then color=$Font_Yellow else color=$Font_Red fi if [[ $num -lt 1000 ]];then result=$num elif [[ $num -ge 1000 && $num -lt 10000 ]];then result="${num:0:1}k" elif [[ $num -ge 10000 && $num -lt 100000 ]];then result="${num:0:2}k" elif [[ $num -ge 100000 && $num -lt 1000000 ]];then result=".${num:0:1}m" else result="1m+" fi echo "$color$(printf '%6s' "$result")$Font_Suffix" } speedtest_test(){ ibar_step=36 local json_data=$(curl -sL "${rawgithub}main/ref/speedtest_cn.json") declare -A codemax codemax[1]=0 codemax[2]=0 codemax[3]=0 codemax[4]=0 while IFS=" " read -r code id city cityzh provider providerzh;do if [[ $YY == "cn" ]];then scity["$code"]="$cityzh" spv["$code"]="$providerzh" else scity["$code"]="$city" spv["$code"]="$provider" fi sid["$code"]="$id" ten_digit="${code:0:1}" one_digit="${code:1}" if [[ $one_digit -gt ${codemax[$ten_digit]} ]];then codemax["$ten_digit"]="$one_digit" fi done < <(echo "$json_data"|jq -r '.[] | "\(.code) \(.id) \(.city) \(.cityzh) \(.provider) \(.providerzh)"') local skip local key local pvi local pvj for ((pvi=1; pvi<=3; pvi++));do skip=0 for ((pvj=1; pvj<=2; pvj++));do for ((try=1; codemax[$pvi]-3+pvj-skip>0; try++));do key=$((pvj+skip)) parse_speedtest_result "${sid[$pvi$key]}" "$pvi$key" if [[ sout[$pvi${key}1] -eq -1 && sout[$pvi${key}2] -eq -1 && sout[$pvi${key}3] -eq -1 && sout[$pvi${key}4] -eq -1 ]];then ((skip++)) ((ibar_step-=2)) else break fi done if [[ $YY == "cn" ]];then tmp_space=$((13-${#scity[$pvi$key]}*2-${#spv[$pvi$key]}*2)) sresu[$pvi$pvj]="$Font_Cyan${scity[$pvi$key]}${spv[$pvi$key]}$(printf "%${tmp_space}s\n")" else tmp_space=$((10-${#scity[$pvi$key]})) sresu[$pvi$pvj]="$Font_Cyan${scity[$pvi$key]}$(printf "%${tmp_space}s\n")${spv[$pvi$key]} $Font_Suffix" fi sresu[$pvi$pvj]+="$(convert_b2m ${sout[$pvi${key}1]})$(convert_delay ${sout[$pvi${key}2]}) $(convert_b2m ${sout[$pvi${key}3]})$(convert_delay ${sout[$pvi${key}4]})" done done } show_speedtest(){ echo -ne "\r${sspeedtest[title]}\n" local count=0 local keys=($(echo "${!sresu[@]}"|tr ' ' '\n'|sort -n)) for key in "${keys[@]}";do ((count++)) if ((count%2==0));then echo -ne "${sresu[$key]}\n" else echo -ne "\r${sresu[$key]}||" fi done ((count%2!=0))&&echo } show_head(){ echo -ne "\r$(printf '%80s'|tr ' ' '*')\n" if [ $fullIP -eq 1 ];then calc_padding "$(printf '%*s' "${shead[ltitle]}" '')$IP" 80 echo -ne "\r$PADDING$Font_B${shead[title]}$Font_Cyan$IP$Font_Suffix\n" else calc_padding "$(printf '%*s' "${shead[ltitle]}" '')$IPhide" 80 echo -ne "\r$PADDING$Font_B${shead[title]}$Font_Cyan$IPhide$Font_Suffix\n" fi calc_padding "${shead[git]}" 80 echo -ne "\r$PADDING$Font_U${shead[git]}$Font_Suffix\n" calc_padding "${shead[bash]}" 80 echo -ne "\r$PADDING${shead[bash]}\n" echo -ne "\r${shead[ptime]}${shead[time]} ${shead[ver]}\n" echo -ne "\r$(printf '%80s'|tr ' ' '*')\n" } show_bgp(){ echo -ne "\r${sbgp[title]}\n" if [[ -n ${bgp[asn]} && ${bgp[asn]} != "null" || -n ${bgp[org]} && ${bgp[org]} != "null" || -n ${bgp[prefixnum]} && ${bgp[prefixnum]} != "null" || -n ${bgp[rir]} && ${bgp[rir]} != "null" ]];then local tmpstr="" local tmpinfo="" [[ -n ${bgp[rir]} && ${bgp[rir]} != "null" ]]&&tmpinfo="${bgp[rir]}"&&tmpstr=", " [[ -n ${bgp[asn]} && ${bgp[asn]} != "null" ]]&&tmpinfo="$tmpinfo$tmpstr${bgp[asn]}"&&tmpstr=" " [[ -n ${bgp[org]} && ${bgp[org]} != "null" ]]&&tmpinfo="$tmpinfo$tmpstr${bgp[org]}"&&tmpstr=", " [[ $tmpstr == " " ]]&&tmpstr=", " [[ -n ${bgp[prefixnum]} && ${bgp[prefixnum]} != "null" ]]&&tmpinfo="$tmpinfo${tmpstr}Prefix/${bgp[prefixnum]}" echo -ne "\r$Font_Cyan${sbgp[ipinfo]}$Font_Green$(wrap_text 20 "$tmpinfo")$Font_Suffix\n" fi if [[ -n ${bgp[regdate]} && -n ${bgp[moddate]} ]];then echo -ne "\r$Font_Cyan${sbgp[date]}$Font_Green${bgp[regdate]} / ${bgp[moddate]}$Font_Suffix\n" elif [[ -n ${bgp[regdate]} && -z ${bgp[moddate]} ]];then echo -ne "\r$Font_Cyan${sbgp[date]}$Font_Green${bgp[regdate]} / NoRecord$Font_Suffix\n" elif [[ -z ${bgp[regdate]} && -n ${bgp[moddate]} ]];then echo -ne "\r$Font_Cyan${sbgp[date]}${Font_Green}NoRecord / ${bgp[moddate]}$Font_Suffix\n" fi if [[ -n ${bgp[countrycode]} && ${bgp[countrycode]} != "null" ]];then local fullcountry="[${bgp[countrycode]}]" [[ -n ${bgp[country]} ]]&&fullcountry="$fullcountry${bgp[country]}" if [[ -n ${bgp[intermediateregion]} ]];then fullcountry="$fullcountry, ${bgp[intermediateregion]}" elif [[ -n ${bgp[subregion]} ]];then fullcountry="$fullcountry, ${bgp[subregion]}" fi [[ -n ${bgp[region]} ]]&&fullcountry="$fullcountry, ${bgp[region]}" fi [[ -n $fullcountry && $fullcountry != "null" ]]&&echo -ne "\r$Font_Cyan${sbgp[country]}$Font_Green$(wrap_text 20 "$fullcountry")$Font_Suffix\n" [[ -n ${bgp[address]} && ${bgp[address]} != "null" ]]&&echo -ne "\r$Font_Cyan${sbgp[address]}$Font_Green$(wrap_text 20 "${bgp[address]}")$Font_Suffix\n" [[ -n ${bgp[geofeed]} && ${bgp[geofeed]} != "null" ]]&&echo -ne "\r$Font_Cyan${sbgp[geofeed]}$Font_Green${bgp[geofeed]}$Font_Suffix\n" local neighborresu="" if [[ ${bgp[neighbortotal]} -gt 0 && -n ${bgp[neighboractive]} ]];then neighbor_ratio=$(echo "scale=2; ${bgp[neighboractive]}/${bgp[neighbortotal]}"|bc) if (($(echo "$neighbor_ratio < 0.5"|bc -l)));then neighbor_bg=$Back_Green elif (($(echo "$neighbor_ratio >= 0.5"|bc -l)))&&(($(echo "$neighbor_ratio < 0.8"|bc -l)));then neighbor_bg=$Back_Yellow elif (($(echo "$neighbor_ratio >= 0.8"|bc -l)));then neighbor_bg=$Back_Red else neighbor_bg=$Back_Green fi neighborresu="${Font_Green}Subnet/24 $neighbor_bg$Font_B$Font_White ${bgp[neighboractive]} / ${bgp[neighbortotal]} $Font_Suffix " fi if [[ ${bgp[iptotal]} -gt 0 && -n ${bgp[ipactive]} ]];then ip_ratio=$(echo "scale=2; ${bgp[ipactive]}/${bgp[iptotal]}"|bc) if (($(echo "$ip_ratio < 0.5"|bc -l)));then ip_bg=$Back_Green elif (($(echo "$ip_ratio >= 0.5"|bc -l)))&&(($(echo "$ip_ratio < 0.8"|bc -l)));then ip_bg=$Back_Yellow elif (($(echo "$ip_ratio >= 0.8"|bc -l)));then ip_bg=$Back_Red else ip_bg=$Back_Green fi neighborresu+="${Font_Green}Prefix/${bgp[prefixnum]} $ip_bg$Font_B$Font_White ${bgp[ipactive]} / ${bgp[iptotal]} $Font_Suffix" fi [[ -n $neighborresu ]]&&echo -ne "\r$Font_Cyan${sbgp[neighbor]}$neighborresu$Font_Suffix\n" } show_local(){ echo -ne "\r${slocal[title]}\n" [[ -n ${getnat[type]} ]]&&echo -ne "\r$Font_Cyan${slocal[nat]}${slocal[${getnat[type]}]}$Font_Suffix\n" if [[ ${getnat[char]} -eq 1 ]];then echo -ne "\r$Font_Cyan${slocal[mapping]}${slocal[m${getnat[m]}]}$Font_Suffix$Font_Cyan${slocal[filter]}${slocal[f${getnat[f]}]}$Font_Suffix$Font_Cyan${slocal[port]}${slocal[p${getnat[p]}]}$Font_Suffix$Font_Cyan${slocal[hairpin]}${slocal[h${getnat[h]}]}$Font_Suffix\n" fi echo -ne "\r$Font_Cyan${slocal[tcpcc]}$Font_Green$(printf '%-13s' "${gettcp[tcpcc]}")$Font_Cyan${slocal[rmem]}$Font_Green${gettcp[rmem]}$Font_Suffix\n" echo -ne "\r$Font_Cyan${slocal[qdisc]}$Font_Green$(printf '%-13s' "${gettcp[qdisc]}")$Font_Cyan${slocal[wmem]}$Font_Green${gettcp[wmem]}$Font_Suffix\n" } show_conn(){ echo -ne "\r${sconn[title]}\n" if [[ ${conn[ix]} -eq 99 ]];then echo -ne "\r$Font_Cyan${sconn[ix]}$Font_Green$(printf '%-10s' "${conn[ix]}+")" elif [[ ${conn[ix]} -eq -1 ]];then echo -ne "\r$Font_Cyan${sconn[ix]}$Font_Green$(printf '%-10s' "-")" else echo -ne "\r$Font_Cyan${sconn[ix]}$Font_Green$(printf '%-10s' "${conn[ix]}")" fi if [[ ${conn[upstreams]} -eq -2 ]];then echo -ne "$Font_Cyan${sconn[upstreams]}${Font_Green}Transit-Free \n" elif [[ ${conn[upstreams]} -eq -1 ]];then echo -ne "$Font_Cyan${sconn[upstreams]}$Font_Green$(printf '%-10s' "-")" else echo -ne "$Font_Cyan${sconn[upstreams]}$Font_Green$(printf '%-10s' "${conn[upstreams]}")" fi if [[ ${conn[peers]} -eq -1 ]];then echo -ne "$Font_Cyan${sconn[peers]}$Font_Green-$Font_Suffix\n" else echo -ne "$Font_Cyan${sconn[peers]}$Font_Green${conn[peers]}$Font_Suffix\n" fi local clenth=0 conn[asn]="" conn[org]="" for id in $(echo "${!casn[@]}"|tr ' ' '\n'|sort -n);do local raw_as_number="AS${casn[$id]}" local raw_as_name="${corg[$id]}" if [[ ${casn[$id]} -eq 0 || ${ctarget[$id]} == "true" ]];then continue fi local len_as_number=${#raw_as_number} local len_as_name=${#raw_as_name} local max_len=$((len_as_number>len_as_name?len_as_number:len_as_name)) if ((len_as_number<max_len));then local spaces_to_add=$((max_len-len_as_number)) local left_spaces=$((spaces_to_add/2)) local right_spaces=$((spaces_to_add-left_spaces)) raw_as_number="$(printf "%*s%s%*s" "$left_spaces" "" "$raw_as_number" "$right_spaces" "")" fi if ((len_as_name<max_len));then local spaces_to_add=$((max_len-len_as_name)) local right_spaces=$((spaces_to_add/2)) local left_spaces=$((spaces_to_add-right_spaces)) raw_as_name="$(printf "%*s%s%*s" "$left_spaces" "" "$raw_as_name" "$right_spaces" "")" fi local as_number_style="" local as_name_style="" local reset_style="$Font_Suffix" [[ ${cupstream[$id]} == "true" ]]&&as_name_style+="$Font_U" as_number_style+="$Font_B" as_name_style+="$Font_B" as_number_style+="$Font_White$Back_Blue" [[ ${ctier1[$id]} == "true" ]]&&as_name_style+="$Font_White$Back_Green" [[ ${ctier1[$id]} == "false" ]]&&as_name_style+="$Font_White$Back_Yellow" local as_number="$as_number_style$raw_as_number$reset_style" local as_name="$as_name_style$raw_as_name$reset_style" clenth=$((clenth+max_len+1)) if ((clenth>81));then clenth=$((max_len+1)) echo -ne "\r${conn[asn]}\n\r${conn[org]}\n" conn[asn]="$as_number " conn[org]="$as_name " else conn[asn]="${conn[asn]}$as_number " conn[org]="${conn[org]}$as_name " fi done [[ -n ${conn[asn]} ]]&&echo -ne "\r${conn[asn]}\n" [[ -n ${conn[org]} ]]&&echo -ne "\r${conn[org]}\n" } show_tail(){ echo -ne "\r$(printf '%80s'|tr ' ' '=')\n" echo -ne "\r$Font_I${stail[stoday]}${stail[today]}${stail[stotal]}${stail[total]}${stail[thanks]} $Font_Suffix\n" echo -e "" } get_opts(){ local args=() while [[ $# -gt 0 ]];do case "$1" in -[loSR])args+=("$1") if [[ $# -gt 1 && $2 != -* ]];then args+=("$2") shift fi shift ;; -[loSR]*)ERRORcode=1 shift ;; -[46fhjnpyELMP]*)local opt="$1" shift for ((i=1; i<${#opt}; i++));do args+=("-${opt:i:1}") done ;; --*|-*[!-]*)args+=("$1") shift ;; -*)ERRORcode=1 shift ;; *)shift esac done set -- "${args[@]}" while [[ $# -gt 0 ]];do case "$1" in -4)if [[ IPV4check -ne 0 ]] then IPV6check=0 else ERRORcode=4 fi shift ;; -6)if [[ IPV6check -ne 0 ]] then IPV4check=0 else ERRORcode=6 fi shift ;; -f)fullIP=1 shift ;; -h)show_help shift ;; -j)mode_json=1 shift ;; -l)shift [[ $1 == -* ]]&&ERRORcode=1&&break YY=$(echo "$1"|tr '[:upper:]' '[:lower:]') shift ;; -n)mode_no=1 shift ;; -o)shift [[ $1 == -* ]]&&{ ERRORcode=1 break } mode_output=1 outputfile="$1" [[ -z $outputfile ]]&&{ ERRORcode=1 break } [[ -e $outputfile ]]&&{ ERRORcode=10 break } touch "$outputfile" 2>/dev/null||{ ERRORcode=11 break } shift ;; -p)mode_privacy=1 shift ;; -y)mode_yes=1 shift ;; -E)YY="en" shift ;; -L)mode_low=1 shift ;; -M)mode_menu=1 shift ;; -P)mode_ping=1 shift ;; -R)mode_route=1 shift if [[ $# -gt 0 && $1 != -* ]];then mode_route_pv=$(echo "$1"|tr '[:upper:]' '[:lower:]'|sed 's/省//g; s/市//g; s/县//g; s/维吾尔//g; s/回族//g; s/壮族//g; s/自治区//g; s/特别行政区//g') shift fi ;; -S)shift if [[ $# -gt 0 ]];then mode_skip="$1" shift else ERRORcode=1 fi ;; -*)ERRORcode=1 shift ;; *)shift esac done if [[ $mode_menu -eq 1 ]];then if [[ $YY == "cn" ]];then eval "bash <(curl -sL https://Check.Place) -N" else eval "bash <(curl -sL https://Check.Place) -EN" fi exit 0 fi [[ $mode_ping -eq 1 ]]&&mode_skip+="567" [[ $mode_low -eq 1 ]]&&mode_skip+="6" [[ $mode_route -eq 1 ]]&&mode_skip+="467" [[ $mode_ping -eq 1 && $mode_skip == *"4"* ]]&&ERRORcode=9 [[ $mode_route -eq 1 && $mode_skip == *"5"* ]]&&ERRORcode=9 [[ $mode_skip == *"1"* && $mode_skip == *"2"* && $mode_skip == *"3"* && $mode_skip == *"4"* && $mode_skip == *"5"* && $mode_skip == *"6"* && $mode_skip == *"7"* ]]&&ERRORcode=9 [[ $IPV4check -eq 1 && $IPV6check -eq 0 && $IPV4work -eq 0 ]]&&ERRORcode=40 [[ $IPV4check -eq 0 && $IPV6check -eq 1 && $IPV6work -eq 0 ]]&&ERRORcode=60 CurlARG="$useNIC$usePROXY" } show_help(){ echo -ne "\r$shelp\n" exit 0 } show_ad(){ RANDOM=$(date +%s) local -a ads=() local i=1 while :;do local content content=$(curl -fsL --max-time 5 "${rawgithub}main/ref/ad$i.ans")||break ads+=("$content") ((i++)) done local adCount=${#ads[@]} local -a indices=() for ((i=1; i<=adCount; i++));do indices+=("$i");done for ((i=adCount-1; i>0; i--));do local j=$((RANDOM%(i+1))) local tmp=${indices[i]} indices[i]=${indices[j]} indices[j]=$tmp done local -a aad aad[0]=$(curl -sL --max-time 5 "${rawgithub}main/ref/sponsor.ans") for ((i=0; i<adCount; i++));do aad[${indices[i]}]="${ads[i]}" done local rows cols if ! read rows cols < <(stty size 2>/dev/null);then cols=0;fi ADLines=0 print_pair(){ local left="$1" right="$2" local -a L R mapfile -t L <<<"$left" mapfile -t R <<<"$right" local i for ((i=0; i<12; i++));do printf "%-72s$Font_Suffix %-72s\n" "${L[i]}" "${R[i]}" 1>&2 done ADLines=$((ADLines+12)) } print_block(){ echo "$1" 1>&2 ADLines=$((ADLines+12)) } if [[ $cols -ge 150 ]];then if ((adCount==0));then print_block "${aad[0]}" elif ((adCount%2==1));then print_pair "${aad[0]}" "${aad[1]}" local k for ((k=2; k<=adCount; k+=2));do print_pair "${aad[$k]}" "${aad[$((k+1))]}" done else print_block "${aad[0]}" local k for ((k=1; k<=adCount; k+=2));do print_pair "${aad[$k]}" "${aad[$((k+1))]}" done fi else echo "${aad[0]}" 1>&2 for ((i=1; i<=adCount; i++));do echo "${aad[$i]}" 1>&2 done ADLines=$(((adCount+1)*12)) fi } read_ref(){ ISO3166=$(curl -sL -m 10 "${rawgithub}main/ref/iso3166.json") RESPONSE=$(curl -sL -m 10 "${rawgithub}main/ref/province.json") while IFS=" " read -r province code short name;do pcode[$province]=$code pshort[$province]=$short pname[$province]=$(echo "$name"|sed -E 's/(省|市|自治区|维吾尔|壮族|回族)//g') pcode_lower=$(echo "$code"|tr '[:upper:]' '[:lower:]') pdm[${province}14]="$pcode_lower-ct-v4.ip.zstaticcdn.com" pdm[${province}24]="$pcode_lower-cu-v4.ip.zstaticcdn.com" pdm[${province}34]="$pcode_lower-cm-v4.ip.zstaticcdn.com" pdm[${province}16]="$pcode_lower-ct-v6.ip.zstaticcdn.com" pdm[${province}26]="$pcode_lower-cu-v6.ip.zstaticcdn.com" pdm[${province}36]="$pcode_lower-cm-v6.ip.zstaticcdn.com" done < <(echo "$RESPONSE"|jq -r '.[] | select(.province < 70) | "\(.province) \(.code) \(.short) \(.name)"') if [[ -n $mode_route_pv ]];then lower_optarg="$mode_route_pv" mode_route_pv="" for province in "${!pcode[@]}";do lower_pcode=$(echo "${pcode[$province]}"|tr '[:upper:]' '[:lower:]') if [[ $lower_optarg == "$lower_pcode" || $lower_optarg == "${pshort[$province]}" || $lower_optarg == "${pname[$province]}" ]];then mode_route_pv="$province" break fi done [[ -z $mode_route_pv ]]&&ERRORcode=21 fi while read -r as name;do AS_MAPPING["$as"]="$name" done < <(curl -sL "${rawgithub}main/ref/AS_Mapping.txt") } save_json(){ local head_updates="" local bgp_updates="" local local_updates="" local connectivity_updates="" if [ $fullIP -eq 1 ];then head_updates+=".Head |= map(. + { IP: \"${IP:-null}\" }) | " else head_updates+=".Head |= map(. + { IP: \"${IPhide:-null}\" }) | " fi head_updates+=".Head |= map(. + { Command: \"${shead[bash]:-null}\" }) | " head_updates+=".Head |= map(. + { GitHub: \"${shead[git]:-null}\" }) | " head_updates+=".Head |= map(. + { Time: \"${shead[time]:-null}\" }) | " head_updates+=".Head |= map(. + { Version: \"${shead[ver]:-null}\" }) | " local first_asn=$(echo "${bgp[asn]}"|awk -F',' '{print $1}'|sed 's/^AS//') first_asn=${first_asn:-null} bgp_updates+=".BGP |= map(. + { ASN: \"$first_asn\" }) | " bgp_updates+=".BGP |= map(. + { Organization: \"${bgp[org]:-null}\" }) | " bgp_updates+=".BGP |= map(. + { Prefix: ${bgp[prefixnum]:-null} }) | " bgp_updates+=".BGP |= map(. + { RIR: \"${bgp[rir]:-null}\" }) | " bgp_updates+=".BGP |= map(. + { RegDate: \"${bgp[regdate]:-null}\" }) | " bgp_updates+=".BGP |= map(. + { ModDate: \"${bgp[moddate]:-null}\" }) | " bgp_updates+=".BGP |= map(. + { Country: \"${bgp[country]:-null}\" }) | " bgp_updates+=".BGP |= map(. + { IntermediateRegion: \"${bgp[intermediateregion]:-null}\" }) | " bgp_updates+=".BGP |= map(. + { SubRegion: \"${bgp[subregion]:-null}\" }) | " bgp_updates+=".BGP |= map(. + { Region: \"${bgp[region]:-null}\" }) | " bgp_updates+=".BGP |= map(. + { Address: \"${bgp[address]:-null}\" }) | " bgp_updates+=".BGP |= map(. + { GeoFeed: \"${bgp[geofeed]:-null}\" }) | " if [[ -n ${bgp[iptotal]} && -n ${bgp[ipactive]} ]];then bgp_updates+=".BGP |= map(. + { IPinTotal: ${bgp[iptotal]:-null} }) | " bgp_updates+=".BGP |= map(. + { IPActive: ${bgp[ipactive]:-null} }) | " bgp_updates+=".BGP |= map(. + { NeighborinTotal: ${bgp[neighbortotal]:-null} }) | " bgp_updates+=".BGP |= map(. + { NeighborActive: ${bgp[neighboractive]:-null} }) | " elif [[ -n ${bgp[neighbortotal]} && -n ${bgp[neighboractive]} ]];then bgp_updates+=".BGP |= map(. + { IPinTotal: ${bgp[neighbortotal]:-null} }) | " bgp_updates+=".BGP |= map(. + { IPActive: ${bgp[neighboractive]:-null} }) | " bgp_updates+=".BGP |= map(. + { NeighborinTotal: ${bgp[neighbortotal]:-null} }) | " bgp_updates+=".BGP |= map(. + { NeighborActive: ${bgp[neighboractive]:-null} }) | " else bgp_updates+=".BGP |= map(. + { IPinTotal: null }) | " bgp_updates+=".BGP |= map(. + { IPActive: null }) | " bgp_updates+=".BGP |= map(. + { NeighborinTotal: null }) | " bgp_updates+=".BGP |= map(. + { NeighborActive: null }) | " fi local_updates+=".Local |= map(. + { NAT: \"${getnat[nat]:-null}\" }) | " local_updates+=".Local |= map(. + { NATDescribe: \"$(echo -e "${slocal[${getnat[type]}]:-null}"|sed -E 's/\x1B\[[0-9;]*[a-zA-Z]//g'|xargs)\" }) | " local_updates+=".Local |= map(. + { Mapping: \"$(echo -e "${slocal[m${getnat[m]}]:-null}"|sed -E 's/\x1B\[[0-9;]*[a-zA-Z]//g'|xargs)\" }) | " local_updates+=".Local |= map(. + { Filter: \"$(echo -e "${slocal[f${getnat[f]}]:-null}"|sed -E 's/\x1B\[[0-9;]*[a-zA-Z]//g'|xargs)\" }) | " local_updates+=".Local |= map(. + { Port: \"$(echo -e "${slocal[p${getnat[p]}]:-null}"|sed -E 's/\x1B\[[0-9;]*[a-zA-Z]//g'|xargs)\" }) | " local_updates+=".Local |= map(. + { Hairpin: \"$(echo -e "${slocal[h${getnat[h]}]:-null}"|sed -E 's/\x1B\[[0-9;]*[a-zA-Z]//g'|xargs)\" }) | " local_updates+=".Local |= map(. + { TCPCongestionControl: \"${gettcp[tcpcc]:-null}\" }) | " local_updates+=".Local |= map(. + { QueueDiscipline: \"${gettcp[qdisc]:-null}\" }) | " local_updates+=".Local |= map(. + { TCPReceiveBuffer: \"${gettcp[rmem]:-null}\" }) | " local_updates+=".Local |= map(. + { TCPSendBuffer: \"${gettcp[wmem]:-null}\" }) | " bgp_updates+=".BGP |= map(. + { IXCount: ${conn[ix]:-null} }) | " bgp_updates+=".BGP |= map(. + { UpstreamsCount: ${conn[upstreams]:-null} }) | " bgp_updates+=".BGP |= map(. + { PeersCount: ${conn[peers]:-null} }) | " for id in $(echo "${!casn[@]}"|tr ' ' '\n'|sort -n);do if [[ -z ${casn[$id]} ]];then continue fi connectivity_updates+=".Connectivity += [{\"ID\": $id, \"ASN\": ${casn[$id]:-null}, \"Org\": \"${corg[$id]:-null}\", \"IsTarget\": $([[ ${ctarget[$id]} == "true" ]]&&echo true||echo false), \"IsTier1\": $([[ ${ctier1[$id]} == "true" ]]&&echo true||echo false), \"IsUpstream\": $([[ ${cupstream[$id]} == "true" ]]&&echo true||echo false)}] | " done netdata=$(echo "$netdata"|jq "$head_updates$bgp_updates$local_updates$connectivity_updates.") local delay_objects=() local keys=($(echo "${!pcode[@]}"|tr ' ' '\n'|sort -n)) for key in "${keys[@]}";do delay_object="{ \"Code\": \"${pcode[$key]:-null}\", \"Name\": \"${pshort[$key]:-null}\", \"CT\": { \"Average\": \"${pavg[${key}1$1]:-null}\"" for ((resu=1; resu<=ping_test_count; resu++));do delay_object+=", \"$resu\": \"${pout[${key}1$1$resu]:-null}\"" done delay_object+="}, \"CU\": { \"Average\": \"${pavg[${key}2$1]:-null}\"" for ((resu=1; resu<=ping_test_count; resu++));do delay_object+=", \"$resu\": \"${pout[${key}2$1$resu]:-null}\"" done delay_object+="}, \"CM\": { \"Average\": \"${pavg[${key}3$1]:-null}\"" for ((resu=1; resu<=ping_test_count; resu++));do delay_object+=", \"$resu\": \"${pout[${key}3$1$resu]:-null}\"" done delay_object+="}}" delay_objects+=("$delay_object") done delay_array=$(printf '%s\n' "${delay_objects[@]}"|jq -s .) netdata=$(echo "$netdata"|jq --argjson delay_array "$delay_array" '.Delay = $delay_array') local transfer_object=() local keys=($(echo "${!icity[@]}"|tr ' ' '\n'|sort -n)) for key in "${keys[@]}";do transfer_object="{ \"City\": \"${icity[$key]:-null}\", \"SendSpeed\": \"${isout[$key$11]:-null}\", \"SendRetransmits\": \"${isout[$key$12]:-null}\", \"ReceiveSpeed\": \"${isout[$key$13]:-null}\", \"ReceiveRetransmits\": \"${isout[$key$14]:-null}\", \"Delay\": { \"Average\": \"${iavg[$key]:-null}\"" for ((resu=1; resu<=10; resu++));do transfer_object+=", \"$resu\": \"${ipout[$key$1$resu]:-null}\"" done transfer_object+="}}" netdata=$(echo "$netdata"|jq --argjson transfer_object "$transfer_object" '.Transfer += [$transfer_object]') done local speedtest_object=() local keys=($(echo "${!scity[@]}"|tr ' ' '\n'|sort -n)) for key in "${keys[@]}";do if [[ ${sout[${key}1]} -gt 0 || ${sout[${key}2]} -gt 0 || ${sout[${key}3]} -gt 0 || ${sout[${key}4]} -gt 0 ]];then speedtest_object="{ \"City\": \"${scity[$key]:-null}\", \"Provider\": \"${spv[$key]:-null}\", \"ID\": \"${sid[$key]:-null}\", \"SendSpeed\": \"${sout[${key}1]:-null}\", \"SendDelay\": \"${sout[${key}2]:-null}\", \"ReceiveSpeed\": \"${sout[${key}3]:-null}\", \"ReceiveDelay\": \"${sout[${key}4]:-null}\" }" netdata=$(echo "$netdata"|jq --argjson speedtest_object "$speedtest_object" '.Speedtest += [$speedtest_object]') fi done } check_Net(){ IP=$1 ibar_step=0 netdata='{ "Head": [{}], "BGP": [{}], "Local": [{}], "Connectivity": [], "Delay": [], "Speedtest": [], "Transfer": [] }' [[ $2 -eq 4 ]]&&hide_ipv4 $IP [[ $2 -eq 6 ]]&&hide_ipv6 $IP countRunTimes [[ $mode_skip != *"1"* || $mode_skip != *"3"* ]]&&db_bgptools $2 [[ $mode_skip != *"1"* ]]&&db_henet $2 [[ $mode_skip != *"1"* && $2 -eq 4 && -n ${bgp[prefixnum]} ]]&&get_neighbor getnat=() [[ $mode_skip != *"2"* && $2 -eq 4 ]]&&get_nat [[ $mode_skip != *"2"* ]]&&get_tcp [[ $mode_skip != *"4"* ]]&&get_delay $2 [[ $mode_skip != *"5"* && $mode_route -eq 0 ]]&&get_route $2 [[ $mode_skip != *"5"* && $mode_route -eq 1 ]]&&get_route_mode $2 [[ $mode_skip != *"6"* && $2 -eq 4 ]]&&speedtest_test [[ $mode_skip != *"7"* ]]&&iperf_test $2 echo -ne "$Font_LineClear" 1>&2 if [ $2 -eq 4 ]||[[ $IPV4work -eq 0 || $IPV4check -eq 0 ]];then for ((i=0; i<ADLines; i++));do echo -ne "$Font_LineUp" 1>&2 echo -ne "$Font_LineClear" 1>&2 done fi local net_report=$(show_head [[ $mode_skip != *"1"* ]]&&show_bgp [[ $mode_skip != *"2"* ]]&&show_local [[ $mode_skip != *"3"* ]]&&show_conn [[ $mode_skip != *"4"* ]]&&show_delay [[ $mode_skip != *"5"* ]]&&show_route $2 [[ $mode_skip != *"6"* && $2 -eq 4 ]]&&show_speedtest [[ $mode_skip != *"7"* ]]&&show_iperf show_tail) [[ mode_json -eq 1 || mode_output -eq 1 || mode_privacy -eq 0 ]]&&save_json $2 [[ mode_privacy -eq 0 ]]&&report_link=$(curl -$2 -s -X POST https://upload.check.place -d "type=net" --data-urlencode "json=$netdata" --data-urlencode "content=$net_report") [[ mode_json -eq 0 ]]&&echo -ne "\r$net_report\n" [[ mode_json -eq 0 && mode_privacy -eq 0 && $report_link == *"https://Report.Check.Place/"* ]]&&echo -ne "\r${stail[link]}$report_link$Font_Suffix\n" [[ mode_json -eq 1 ]]&&echo -ne "\r$netdata\n" echo -ne "\r\n" if [[ mode_output -eq 1 ]];then case "$outputfile" in *.[aA][nN][sS][iI])echo "$net_report" >>"$outputfile" 2>/dev/null ;; *.[jJ][sS][oO][nN])echo "$netdata" >>"$outputfile" 2>/dev/null ;; *)echo -e "$net_report"|sed 's/\x1b\[[0-9;]*[mGKHF]//g' >>"$outputfile" 2>/dev/null esac fi } generate_random_user_agent export LC_CTYPE=en_US.UTF-8 2>/dev/null check_connectivity get_ipv4 get_ipv6 is_valid_ipv4 $IPV4 is_valid_ipv6 $IPV6 get_opts "$@" [[ mode_no -eq 0 ]]&&install_dependencies set_language read_ref if [[ $ERRORcode -ne 0 ]];then echo -ne "\r$Font_B$Font_Red${swarn[$ERRORcode]}$Font_Suffix\n" exit $ERRORcode fi clear show_ad [[ $IPV4work -ne 0 && $IPV4check -ne 0 ]]&&check_Net "$IPV4" 4 [[ $IPV6work -ne 0 && $IPV6check -ne 0 ]]&&check_Net "$IPV6" 6