From f4dc06b09c8bc18c85695263968b80e2ea52e6bc Mon Sep 17 00:00:00 2001 From: anten-ka Date: Sat, 7 Mar 2026 15:08:58 +0300 Subject: [PATCH] Add TCP ping fallback for servers that block ICMP (XRay, VLESS, Reality) Made-with: Cursor --- _ssh_debug.py | 23 +++++++++++++++++++ _test_tcp_ping.py | 58 +++++++++++++++++++++++++++++++++++++++++++++++ install.sh | 45 ++++++++++++++++++++++++++++++------ 3 files changed, 119 insertions(+), 7 deletions(-) create mode 100644 _ssh_debug.py create mode 100644 _test_tcp_ping.py diff --git a/_ssh_debug.py b/_ssh_debug.py new file mode 100644 index 0000000..10c2070 --- /dev/null +++ b/_ssh_debug.py @@ -0,0 +1,23 @@ +import paramiko, sys + +host = "185.250.47.205" +user = "root" +pwd = "0i1sbf9NM36FkG5dFH" + +def run(cmd, timeout=30): + ssh = paramiko.SSHClient() + ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + ssh.connect(host, username=user, password=pwd, timeout=10) + stdin, stdout, stderr = ssh.exec_command(cmd, timeout=timeout) + out = stdout.read().decode(errors="replace") + err = stderr.read().decode(errors="replace") + rc = stdout.channel.recv_exit_status() + ssh.close() + return rc, out, err + +if __name__ == "__main__": + cmd = " ".join(sys.argv[1:]) if len(sys.argv) > 1 else "echo ok" + rc, out, err = run(cmd) + if out: print(out, end="") + if err: print("STDERR:", err, end="") + print(f"\n[exit {rc}]") diff --git a/_test_tcp_ping.py b/_test_tcp_ping.py new file mode 100644 index 0000000..f266ff7 --- /dev/null +++ b/_test_tcp_ping.py @@ -0,0 +1,58 @@ +import paramiko + +host = "185.250.47.205" +user = "root" +pwd = "0i1sbf9NM36FkG5dFH" + +test_script = """#!/bin/bash +tcp_ping() { + local ip="$1" port="$2" tout="${3:-3}" + local raw + raw=$(curl -so /dev/null -w '%{time_connect}' --max-time "$tout" --connect-timeout "$tout" "http://${ip}:${port}/" 2>/dev/null) + [ -z "$raw" ] && return 1 + local ms + ms=$(awk "BEGIN {v=$raw*1000; if(v<0.5) exit 1; printf \\"%.2f\\", v}" 2>/dev/null) || return 1 + echo "$ms" +} + +smart_ping() { + local ip="$1" tout="${2:-3}" port="${3:-}" + local ms + ms=$(ping -c 1 -W "$tout" "$ip" 2>/dev/null | sed -n 's/.*time=\\([0-9.]*\\).*/\\1/p') + if [ -n "$ms" ]; then echo "ICMP: ${ms}ms"; return 0; fi + [ -z "$port" ] && { echo "NO_PORT"; return 1; } + ms=$(tcp_ping "$ip" "$port" "$tout") + if [ -n "$ms" ]; then echo "TCP: ${ms}ms"; return 0; fi + echo "TIMEOUT" + return 1 +} + +echo "=== Test 1: ICMP to 8.8.8.8 ===" +smart_ping "8.8.8.8" 3 + +echo "=== Test 2: ICMP to 193.124.225.26 (no port) ===" +smart_ping "193.124.225.26" 3 + +echo "=== Test 3: smart_ping 193.124.225.26 TCP:443 ===" +smart_ping "193.124.225.26" 3 "443" + +echo "=== Test 4: tcp_ping raw ===" +result=$(tcp_ping "193.124.225.26" "443" 3) +echo "result=$result rc=$?" +""" + +ssh = paramiko.SSHClient() +ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) +ssh.connect(host, username=user, password=pwd, timeout=10) + +sftp = ssh.open_sftp() +with sftp.file("/tmp/_kaskad_test.sh", "w") as f: + f.write(test_script) +sftp.close() + +stdin, stdout, stderr = ssh.exec_command("bash /tmp/_kaskad_test.sh", timeout=30) +print(stdout.read().decode(errors="replace")) +err = stderr.read().decode(errors="replace") +if err: + print("STDERR:", err) +ssh.close() diff --git a/install.sh b/install.sh index 965c9ad..67f7709 100644 --- a/install.sh +++ b/install.sh @@ -261,7 +261,9 @@ probe_server_cli() { [ -n "$_RET_NOTE" ] && set_alias_note "$ip" "$_RET_NOTE" if [ ${#pings[@]} -eq 0 ]; then - echo -e "${YELLOW}[WARN] Сервер не отвечает на ping.${NC}" + echo -e "${YELLOW}[WARN] Сервер не отвечает на ICMP ping.${NC}" + echo -e "${WHITE}(Это нормально для VLESS/XRay — ICMP часто заблокирован)${NC}" + echo -e "${WHITE}TCP-проверка будет доступна после добавления правила.${NC}" read -p "Продолжить? (y/n): " ans [[ "$ans" != "y" ]] && return 1 fi @@ -403,6 +405,31 @@ get_target_ips() { get_rules_list | awk -F'|' '{split($3,a,":"); print a[1]}' | sort -u } +get_port_for_ip() { + local ip="$1" + get_rules_list | awk -F'|' -v ip="$ip" '{split($3,a,":"); if(a[1]==ip){print a[2]; exit}}' +} + +tcp_ping() { + local ip="$1" port="$2" tout="${3:-3}" + local raw + raw=$(curl -so /dev/null -w '%{time_connect}' --max-time "$tout" --connect-timeout "$tout" "http://${ip}:${port}/" 2>/dev/null) + [ -z "$raw" ] && return 1 + local ms + ms=$(awk "BEGIN {v=$raw*1000; if(v<0.5) exit 1; printf \"%.2f\", v}" 2>/dev/null) || return 1 + echo "$ms" +} + +smart_ping() { + local ip="$1" tout="${2:-3}" port="${3:-}" + local ms + ms=$(ping -c 1 -W "$tout" "$ip" 2>/dev/null | sed -n 's/.*time=\([0-9.]*\).*/\1/p') + if [ -n "$ms" ]; then echo "$ms"; return 0; fi + [ -z "$port" ] && port=$(get_port_for_ip "$ip") + [ -z "$port" ] && return 1 + tcp_ping "$ip" "$port" "$tout" +} + remove_rules_for_port() { local proto="$1" in_port="$2" iptables -t nat -S PREROUTING 2>/dev/null | grep "DNAT" | grep -- "--dport ${in_port} " | grep -- "-p ${proto} " | while read -r rule; do @@ -717,14 +744,18 @@ ping_live() { trap 'running=0' INT + local _port; _port=$(get_port_for_ip "$ip") + local _mode="ICMP" + [ -n "$_port" ] && _mode="ICMP/TCP:${_port}" + while [ "$running" -eq 1 ]; do local ms - ms=$(ping -c 1 -W 3 "$ip" 2>/dev/null | sed -n 's/.*time=\([0-9.]*\).*/\1/p') + ms=$(smart_ping "$ip" 3 "${_port:-}") ((count++)) clear echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" - echo -e "${CYAN} Live Ping: ${WHITE}$label${CYAN} [Ctrl+C — стоп]${NC}" + echo -e "${CYAN} Live Ping: ${WHITE}$label${CYAN} (${_mode}) [Ctrl+C — стоп]${NC}" echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" if [ -n "$ms" ]; then @@ -885,7 +916,7 @@ monitor_daemon() { [ -f "$ckf" ] && lc=$(cat "$ckf") if (( now - lc >= MON_INTERVAL )); then echo "$now" > "$ckf" - local pr; pr=$(ping -c 1 -W 3 "$MON_IP" 2>/dev/null | sed -n 's/.*time=\([0-9.]*\).*/\1/p') + local pr; pr=$(smart_ping "$MON_IP" 3) if [ -z "$pr" ]; then monitor_alert "$MON_IP" "TIMEOUT" "$MON_THRESHOLD" "$MON_COOLDOWN" else @@ -1204,7 +1235,7 @@ bot_handle_callback() { ps:*) local ip="${data#ps:}"; local lb; lb=$(fmt_ip_short "$ip") tg_edit "$chat_id" "$msg_id" "🏓 $lb\nРежим:" "$(kbd_ping_opts "$ip")" ;; po:*) local ip="${data#po:}"; local lb; lb=$(fmt_ip_short "$ip") - ( local ms; ms=$(ping -c 1 -W 3 "$ip" 2>/dev/null | sed -n 's/.*time=\([0-9.]*\).*/\1/p') + ( local ms; ms=$(smart_ping "$ip" 3) [ -n "$ms" ] && tg_send "$chat_id" "🏓 $lb\n${ms} ms" "$(kbd_back)" > /dev/null \ || tg_send "$chat_id" "🏓 $lb\ntimeout" "$(kbd_back)" > /dev/null ) & ;; p10:*) local ip="${data#p10:}"; local lb; lb=$(fmt_ip_short "$ip") @@ -1212,7 +1243,7 @@ bot_handle_callback() { local mid; mid=$(echo "$resp" | jq -r '.result.message_id // empty') local -a res=(); local lost=0 txt="" for n in $(seq 1 10); do - local ms; ms=$(ping -c 1 -W 3 "$ip" 2>/dev/null | sed -n 's/.*time=\([0-9.]*\).*/\1/p') + local ms; ms=$(smart_ping "$ip" 3) [ -n "$ms" ] && res+=("$ms") && txt+="#$n: ${ms}ms\n" || { ((lost++)); txt+="#$n: timeout\n"; } sleep 1 done @@ -1226,7 +1257,7 @@ bot_handle_callback() { local mid; mid=$(echo "$resp" | jq -r '.result.message_id // empty') local -a res=(); local lost=0 for n in $(seq 1 60); do - local ms; ms=$(ping -c 1 -W 3 "$ip" 2>/dev/null | sed -n 's/.*time=\([0-9.]*\).*/\1/p') + local ms; ms=$(smart_ping "$ip" 3) [ -n "$ms" ] && res+=("$ms") || ((lost++)) if (( n % 10 == 0 )) && [ -n "$mid" ]; then local p="🏓 $lb: ${n}/60с\nОК: ${#res[@]} | Lost: $lost"