#!/bin/bash script_version="v2025-08-01" 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[error]="" slocal[0x000001]="$Back_Green$Font_White$Font_B Open Without NAT $Font_Suffix" slocal[0x000002]="$Back_Yellow$Font_White$Font_B Full Cone NAT $Font_Suffix" slocal[0x000004]="$Back_Red$Font_White$Font_B Restricted Cone NAT $Font_Suffix" slocal[0x000008]="$Back_Red$Font_White$Font_B Port Restricted Cone NAT $Font_Suffix" slocal[0x000010]="$Back_Red$Font_White$Font_B Symmetric NAT $Font_Suffix" slocal[0x000013]="$Back_Red$Font_White$Font_B Symmetric NAT with Independent Mapping $Font_Suffix" slocal[0x00001c]="$Back_Red$Font_White$Font_B Blocked or Unreachable $Font_Suffix" slocal[0x000100]="$Back_Red$Font_White$Font_B Preserves Port $Font_Suffix" slocal[0x000200]="$Back_Red$Font_White$Font_B Hairpin $Font_Suffix" slocal[0x000400]="$Back_Red$Font_White$Font_B Independent Mapping $Font_Suffix" slocal[0x000800]="$Back_Red$Font_White$Font_B Independent Filter $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[error]="" slocal[0x000001]="$Back_Green$Font_White$Font_B 开放网络无NAT $Font_Suffix" slocal[0x000002]="$Back_Yellow$Font_White$Font_B 完全锥形NAT $Font_Suffix" slocal[0x000004]="$Back_Red$Font_White$Font_B 限制锥形NAT $Font_Suffix" slocal[0x000008]="$Back_Red$Font_White$Font_B 端口限制锥形NAT $Font_Suffix" slocal[0x000010]="$Back_Red$Font_White$Font_B 对称NAT $Font_Suffix" slocal[0x000013]="$Back_Red$Font_White$Font_B 独立映射规则对称NAT $Font_Suffix" slocal[0x00001c]="$Back_Red$Font_White$Font_B 不可达或连接被阻断 $Font_Suffix" slocal[0x000100]="$Back_Red$Font_White$Font_B NAT保留了源端口 $Font_Suffix" slocal[0x000200]="$Back_Red$Font_White$Font_B 支持发往自身的反射流量 $Font_Suffix" slocal[0x000400]="$Back_Red$Font_White$Font_B NAT映射独立 $Font_Suffix" slocal[0x000800]="$Back_Red$Font_White$Font_B NAT过滤独立 $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 $usesudo yay -S --noconfirm gcompat ;; 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]="87.0.4280.66 88.0.4324.150 89.0.4389.82" [Firefox]="83.0 84.0 85.0" [Edge]="88.0.705.50 89.0.774.57") generate_random_user_agent(){ local browsers_keys=(${!browsers[@]}) local random_browser_index=$((RANDOM%${#browsers_keys[@]})) local browser=${browsers_keys[random_browser_index]} local versions=(${browsers[$browser]}) local random_version_index=$((RANDOM%${#versions[@]})) local version=${versions[random_version_index]} case $browser in Chrome)UA_Browser="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/$version Safari/537.36" ;; Firefox)UA_Browser="Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:${version%%.*}) Gecko/20100101 Firefox/$version" ;; Edge)UA_Browser="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${version%.*}.0.0 Safari/537.36 Edg/$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=() local result=$(stun "stun.l.google.com" 2>/dev/null) getnat[nat]=$(echo "$result"|grep -oE "0x[0-9A-Fa-f]+") if [[ -z ${getnat[nat]} ]];then getnat[natresu]="${slocal[error]}" else getnat[natresu]="${slocal[${getnat[nat]}]}" fi } 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[natresu]} ]]&&echo -ne "\r$Font_Cyan${slocal[nat]}$Font_Green${getnat[natresu]}$Font_Suffix\n" 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(){ local RANDOM=$(date +%s) local indices=(1 2 3) for ((i=${#indices[@]}-1; i>0; i--));do j=$((RANDOM%(i+1))) temp=${indices[i]} indices[i]=${indices[j]} indices[j]=$temp done aad[0]=$(curl -sL --max-time 5 "${rawgithub}main/ref/sponsor.ans") aad[${indices[0]}]=$(curl -sL --max-time 5 "${rawgithub}main/ref/ad1.ans") aad[${indices[1]}]=$(curl -sL --max-time 5 "${rawgithub}main/ref/ad2.ans") aad[${indices[2]}]=$(curl -sL --max-time 5 "${rawgithub}main/ref/ad3.ans") local rows local cols read rows cols < <(stty size) if [[ $cols -ge 150 ]];then mapfile -t aad0 <<<"${aad[0]}" mapfile -t aad1 <<<"${aad[1]}" mapfile -t aad2 <<<"${aad[2]}" mapfile -t aad3 <<<"${aad[3]}" for ((i=0; i<12; i++));do printf "%-72s$Font_Suffix %-72s\n" "${aad0[$i]}" "${aad1[$i]}" 1>&2 done for ((i=0; i<12; i++));do printf "%-72s$Font_Suffix %-72s\n" "${aad2[$i]}" "${aad3[$i]}" 1>&2 done ADLines=24 else echo "${aad[0]}" 1>&2 echo "${aad[1]}" 1>&2 echo "${aad[2]}" 1>&2 echo "${aad[3]}" 1>&2 ADLines=48 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 "${getnat[natresu]:-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