diff --git a/findtest.sh b/findtest.sh index 4bfc1a0..9214e84 100755 --- a/findtest.sh +++ b/findtest.sh @@ -12,7 +12,6 @@ NC='\033[0m' # No Color install_packages() { echo "Installing required packages..." if [ -f /etc/debian_version ]; then - # Force non-interactive mode for apt export DEBIAN_FRONTEND=noninteractive sudo apt-get update -qq sudo apt-get install -y iperf3 jq curl bc sudo || { @@ -31,7 +30,6 @@ install_packages() { exit 1 fi - # Verify installations for cmd in iperf3 jq curl bc; do if ! command -v $cmd &>/dev/null; then echo -e "${RED}Failed to install $cmd. Please install it manually.${NC}" @@ -63,6 +61,67 @@ haversine() { echo $distance } +construct_iperf_command() { + local host=$1 + local port=$2 + local command="iperf3 -c $host" + if [ -n "$port" ]; then + command="$command -p $port" + fi + echo "$command" +} + +run_iperf_test() { + local host=$1 + local port_spec=$2 + local output + + # Default command if no port spec + if [ -z "$port_spec" ]; then + iperf3 -c "$host" + return + fi + + local ports_to_try=() + if [[ "$port_spec" =~ ([0-9]+)-([0-9]+) ]]; then + local start_port=${BASH_REMATCH[1]} + local end_port=${BASH_REMATCH[2]} + # Add first port as preferred + ports_to_try+=($start_port) + # Add a random port from the rest of the range if available + if [ "$start_port" -lt "$end_port" ]; then + if command -v shuf &> /dev/null; then + ports_to_try+=($(shuf -i $((start_port + 1))-$end_port -n 1)) + else + # Fallback for systems without shuf + ports_to_try+=($((RANDOM % (end_port - start_port) + start_port + 1))) + fi + fi + else + # Single port + ports_to_try+=($port_spec) + fi + + for port in "${ports_to_try[@]}"; do + echo "Testing $host on port $port..." + output=$(iperf3 -c "$host" -p "$port" 2>&1) + echo "$output" + + # If it's NOT busy, we're done. + if ! echo "$output" | grep -q "the server is busy"; then + return 0 + fi + + # If it was busy and there are more ports to try... + if [ ${#ports_to_try[@]} -gt 1 ] && [ "$port" == "${ports_to_try[0]}" ]; then + echo -e "${YELLOW}Server is busy. Trying another port...${NC}" + fi + done + + echo -e "${RED}All attempted ports were busy.${NC}" + return 1 +} + show_top_3_servers() { local current_server=$1 local current_distance=$2 @@ -73,31 +132,30 @@ show_top_3_servers() { echo "$current_server $current_distance" > "$temp_file" while read -r server_info; do - local ip_host=$(echo "$server_info" | jq -r '.IP_HOST') - if [[ $ip_host =~ iperf3[[:space:]]+-c[[:space:]]+([^[:space:]]+) ]]; then - local ip="${BASH_REMATCH[1]}" - local site=$(echo "$server_info" | jq -r '.SITE') - local country=$(echo "$server_info" | jq -r '.COUNTRY') + local ip_host=$(echo "$server_info" | jq -r '.["IP/HOST"]') + local port=$(echo "$server_info" | jq -r '.PORT') + local site=$(echo "$server_info" | jq -r '.SITE') + local country=$(echo "$server_info" | jq -r '.COUNTRY') + local ip_host_cmd=$(construct_iperf_command "$ip_host" "$port") - [ "$ip" = "$current_server" ] && continue + [ "$ip_host" = "$current_server" ] && continue - if [[ $ip =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then - SERVER_INFO=$(curl -s "https://ipinfo.io/$ip/json") + if [[ $ip_host =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + SERVER_INFO=$(curl -s "https://ipinfo.io/$ip_host/json") + SERVER_LOC=$(echo "$SERVER_INFO" | jq -r '.loc') + else + resolved_ip=$(dig +short "$ip_host" | grep -E '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$' | head -n1) + if [ -n "$resolved_ip" ]; then + SERVER_INFO=$(curl -s "https://ipinfo.io/$resolved_ip/json") SERVER_LOC=$(echo "$SERVER_INFO" | jq -r '.loc') - else - resolved_ip=$(dig +short "$ip" | grep -E '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$' | head -n1) - if [ -n "$resolved_ip" ]; then - SERVER_INFO=$(curl -s "https://ipinfo.io/$resolved_ip/json") - SERVER_LOC=$(echo "$SERVER_INFO" | jq -r '.loc') - fi fi + fi - if [ -n "$SERVER_LOC" ]; then - SERVER_LAT=$(echo "$SERVER_LOC" | cut -d',' -f1) - SERVER_LON=$(echo "$SERVER_LOC" | cut -d',' -f2) - distance=$(haversine $MY_LAT $MY_LON $SERVER_LAT $SERVER_LON) - printf "%s\t%s\t%s\t%.2f\n" "$ip_host" "$site" "$country" "$distance" >> "$temp_file" - fi + if [ -n "$SERVER_LOC" ]; then + SERVER_LAT=$(echo "$SERVER_LOC" | cut -d',' -f1) + SERVER_LON=$(echo "$SERVER_LOC" | cut -d',' -f2) + distance=$(haversine $MY_LAT $MY_LON $SERVER_LAT $SERVER_LON) + printf "%s\t%s\t%s\t%.2f\n" "$ip_host_cmd" "$site" "$country" "$distance" >> "$temp_file" fi done < <(jq -c '.[]' "$SERVER_JSON") @@ -118,7 +176,7 @@ for cmd in iperf3 jq curl bc; do if ! command -v $cmd &>/dev/null; then echo -e "${YELLOW}$cmd not found. Installing required packages...${NC}" install_packages - break # Only need to run install_packages once + break fi done @@ -146,8 +204,8 @@ MY_LON=$(echo "$MY_LOC" | cut -d',' -f2) echo -e "${YELLOW}My location: $MY_CITY, $MY_COUNTRY ($MY_LAT, $MY_LON)${NC}\n" -SERVER_JSON="all_servers-export.json" -curl -s "https://export.iperf3serverlist.net/json/all_servers-export.json" -o "$SERVER_JSON" +SERVER_JSON="listed_iperf3_servers.json" +curl -s "https://export.iperf3serverlist.net/listed_iperf3_servers.json" -o "$SERVER_JSON" if [ ! -f "$SERVER_JSON" ]; then echo -e "${RED}Error: Unable to download server list.${NC}" exit 1 @@ -155,24 +213,26 @@ fi echo -e "${ORANGE}Finding closest servers by city name...${NC}" -NEAREST_SERVER=$(jq -r \ +NEAREST_SERVER_OBJ=$(jq \ --arg MY_CITY "$MY_CITY" \ --arg MY_COUNTRY "$MY_COUNTRY" \ - '.[] | select(.SITE==$MY_CITY and .COUNTRY==$MY_COUNTRY) | .IP_HOST' "$SERVER_JSON") + '.[] | select(.SITE==$MY_CITY and .COUNTRY==$MY_COUNTRY)' "$SERVER_JSON" | jq -s '.[0]') -if [ -z "$NEAREST_SERVER" ]; then +if [ -z "$NEAREST_SERVER_OBJ" ] || [ "$NEAREST_SERVER_OBJ" == "null" ]; then echo -e "${ORANGE}No exact match found for $MY_CITY, $MY_COUNTRY. Searching for servers in $MY_COUNTRY...${NC}" - NEAREST_SERVER=$(jq -r \ + NEAREST_SERVER_OBJ=$(jq \ --arg MY_COUNTRY "$MY_COUNTRY" \ - '.[] | select(.COUNTRY==$MY_COUNTRY) | .IP_HOST' "$SERVER_JSON" | head -n 1) + '.[] | select(.COUNTRY==$MY_COUNTRY)' "$SERVER_JSON" | jq -s '.[0]') - if [ -z "$NEAREST_SERVER" ]; then + if [ -z "$NEAREST_SERVER_OBJ" ] || [ "$NEAREST_SERVER_OBJ" == "null" ]; then echo -e "${ORANGE}No servers found in $MY_COUNTRY. Finding closest city...${NC}" - TEMP_FILE=$(mktemp) - - jq -r --arg CITY "$MY_CITY" '.[] | "\(.SITE)\t\(.COUNTRY)\t\(.IP_HOST)"' "$SERVER_JSON" | \ - while IFS=$'\t' read -r site country ip_host; do + SIMILARITY_FILE=$(mktemp) + jq -r '.[] | "\(.SITE)\t\(.COUNTRY)\t\(.["IP/HOST"])\t\(.PORT)"' "$SERVER_JSON" | \ + while IFS=$'\t' read -r site country ip_host port; do + if [ -z "$site" ] || [ "$site" == "null" ]; then + continue + fi similarity=$(echo "$MY_CITY $site" | tr '[:upper:]' '[:lower:]' | \ awk '{ a=$1; b=$2; @@ -189,79 +249,94 @@ if [ -z "$NEAREST_SERVER" ]; then } print int((matches/length(b))*100); }') - echo -e "$similarity\t$site\t$country\t$ip_host" >> "$TEMP_FILE" + echo -e "$similarity\t$site\t$country\t$ip_host\t$port" >> "$SIMILARITY_FILE" done - closest_match=$(sort -nr "$TEMP_FILE" | head -n 1) - if [ -n "$closest_match" ]; then - similarity=$(echo "$closest_match" | cut -f1) - site=$(echo "$closest_match" | cut -f2) - country=$(echo "$closest_match" | cut -f3) - ip_host=$(echo "$closest_match" | cut -f4) + CANDIDATES_FILE=$(mktemp) + sort -nr "$SIMILARITY_FILE" | head -n 20 > "$CANDIDATES_FILE" + rm -f "$SIMILARITY_FILE" - echo -e "\n${GREEN}Found closest city: $site, $country $distance${NC}\n" + DISTANCE_FILE=$(mktemp) + while IFS=$'\t' read -r sim site country ip_host port; do + distance="999999" + server_loc="" + if [[ $ip_host =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + server_info_json=$(curl -s "https://ipinfo.io/$ip_host/json") + server_loc=$(echo "$server_info_json" | jq -r '.loc') + else + resolved_ip=$(dig +short "$ip_host" | grep -E '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$' | head -n1) + if [ -n "$resolved_ip" ]; then + server_info_json=$(curl -s "https://ipinfo.io/$resolved_ip/json") + server_loc=$(echo "$server_info_json" | jq -r '.loc') + fi + fi + + if [ -n "$server_loc" ] && [ "$server_loc" != "null" ]; then + server_lat=$(echo "$server_loc" | cut -d',' -f1) + server_lon=$(echo "$server_loc" | cut -d',' -f2) + distance=$(haversine "$MY_LAT" "$MY_LON" "$server_lat" "$server_lon") + fi + echo -e "$distance\t$sim\t$site\t$country\t$ip_host\t$port" >> "$DISTANCE_FILE" + done < "$CANDIDATES_FILE" + rm -f "$CANDIDATES_FILE" + + closest_match=$(sort -n "$DISTANCE_FILE" | head -n 1) + + if [ -n "$closest_match" ]; then + distance=$(echo "$closest_match" | cut -f1) + similarity=$(echo "$closest_match" | cut -f2) + site=$(echo "$closest_match" | cut -f3) + country=$(echo "$closest_match" | cut -f4) + ip_host=$(echo "$closest_match" | cut -f5) + port=$(echo "$closest_match" | cut -f6) + ip_host_cmd=$(construct_iperf_command "$ip_host" "$port") + + echo -e "\n${GREEN}Found closest city: $site, $country${NC}\n" echo -e "Starting iPerf3 test with server from $site...\n" - if [[ $ip_host =~ iperf3[[:space:]]+-c[[:space:]]+([^[:space:]]+) ]]; then - server="${BASH_REMATCH[1]}" - iperf3 -c "$server" + run_iperf_test "$ip_host" "$port" - echo -e "\n${YELLOW}Top 3 closest iperf3-servers by name match:${NC}" - echo -e "${YELLOW}-------------------------------------------${NC}" - sort -nr "$TEMP_FILE" | head -n 3 | while IFS=$'\t' read -r sim site country cmd; do - echo -e "\nCity: $site, $country" - - if [[ $cmd =~ iperf3[[:space:]]+-c[[:space:]]+([^[:space:]]+) ]]; then - server="${BASH_REMATCH[1]}" - if [[ $server =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then - SERVER_INFO=$(curl -s "https://ipinfo.io/$server/json") - else - resolved_ip=$(dig +short "$server" | grep -E '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$' | head -n1) - if [ -n "$resolved_ip" ]; then - SERVER_INFO=$(curl -s "https://ipinfo.io/$resolved_ip/json") - fi - fi - - SERVER_LOC=$(echo "$SERVER_INFO" | jq -r '.loc') - if [ -n "$SERVER_LOC" ]; then - SERVER_LAT=$(echo "$SERVER_LOC" | cut -d',' -f1) - SERVER_LON=$(echo "$SERVER_LOC" | cut -d',' -f2) - distance=$(haversine $MY_LAT $MY_LON $SERVER_LAT $SERVER_LON) - printf "Distance: %.2f km (Match score: %d%%)\n" "$distance" "$sim" - else - echo "Distance: unavailable (Match score: ${sim}%)" - fi - fi - - echo "Command: $cmd" - if [ "$cmd" = "$ip_host" ]; then - echo -e "${GREEN}Status: Just tested${NC}" - fi - done - fi + # Display top 3 based on original similarity sort, but with calculated distances + echo -e "\n${YELLOW}Top 3 closest iperf3-servers by geographical distance:${NC}" + echo -e "${YELLOW}----------------------------------------------------${NC}" + sort -n "$DISTANCE_FILE" | head -n 3 | while IFS=$'\t' read -r dist sim site country server port; do + cmd=$(construct_iperf_command "$server" "$port") + echo -e "\nCity: $site, $country" + printf "Distance: %.2f km (Match score: %d%%)\n" "$dist" "$sim" + echo "Command: $cmd" + if [ "$cmd" = "$ip_host_cmd" ]; then + echo -e "${GREEN}Status: Just tested${NC}" + fi + done + rm -f "$DISTANCE_FILE" + else + rm -f "$DISTANCE_FILE" fi - - rm -f "$TEMP_FILE" fi fi -if [ -n "$NEAREST_SERVER" ]; then - if [[ $NEAREST_SERVER =~ iperf3[[:space:]]+-c[[:space:]]+([^[:space:]]+) ]]; then - server="${BASH_REMATCH[1]}" - echo -e "\n${GREEN}Nearest iperf3-server: $server${NC}\n" - echo -e "Starting iPerf3 test with server $server...\n" - iperf3 -c "$server" +if [ -n "$NEAREST_SERVER_OBJ" ] && [ "$NEAREST_SERVER_OBJ" != "null" ]; then + server=$(echo "$NEAREST_SERVER_OBJ" | jq -r '.["IP/HOST"]') + port=$(echo "$NEAREST_SERVER_OBJ" | jq -r '.PORT') + NEAREST_SERVER_CMD=$(construct_iperf_command "$server" "$port") + echo -e "\n${GREEN}Nearest iperf3-server: $server${NC}\n" + echo -e "Starting iPerf3 test with server $server...\n" + run_iperf_test "$server" "$port" - echo -e "\n${YELLOW}Top 3 closest iperf3-servers:${NC}" - echo -e "${YELLOW}-----------------------------${NC}" - jq -r --arg COUNTRY "$MY_COUNTRY" \ - '.[] | select(.COUNTRY==$COUNTRY) | "\(.SITE)\t\(.COUNTRY)\t\(.IP_HOST)"' "$SERVER_JSON" | \ - head -n 3 | while IFS=$'\t' read -r site country cmd; do - echo -e "\nCity: $site, $country" - echo "Command: $cmd" - if [ "$cmd" = "$NEAREST_SERVER" ]; then - echo -e "${GREEN}Status: Just tested${NC}" - fi - done - fi + echo -e "\n${YELLOW}Top 3 closest iperf3-servers:${NC}" + echo -e "${YELLOW}-----------------------------${NC}" + jq -c --arg COUNTRY "$MY_COUNTRY" \ + '.[] | select(.COUNTRY==$COUNTRY)' "$SERVER_JSON" | \ + head -n 3 | while read -r server_info; do + site=$(echo "$server_info" | jq -r '.SITE') + country=$(echo "$server_info" | jq -r '.COUNTRY') + host=$(echo "$server_info" | jq -r '.["IP/HOST"]') + port=$(echo "$server_info" | jq -r '.PORT') + cmd=$(construct_iperf_command "$host" "$port") + echo -e "\nCity: $site, $country" + echo "Command: $cmd" + if [ "$cmd" = "$NEAREST_SERVER_CMD" ]; then + echo -e "${GREEN}Status: Just tested${NC}" + fi + done fi