diff --git a/go-gost/x/internal/util/port/port.go b/go-gost/x/internal/util/port/port.go index 4c66d16..abae0a2 100644 --- a/go-gost/x/internal/util/port/port.go +++ b/go-gost/x/internal/util/port/port.go @@ -1,41 +1,62 @@ package port import ( - "bufio" "fmt" - "os" "os/exec" - "path/filepath" - "runtime" "strconv" "strings" + "time" ) -// ForceClosePortConnections 强制断开指定端口的所有连接 -func ForceClosePortConnections(addr string) error { +func ForceClosePortConnections(addr string) (err error) { + defer func() { + if r := recover(); r != nil { + fmt.Printf("⚠️ ForceClosePortConnections panic recovered: %v\n", r) + err = nil // 永远返回 nil + } + }() + if addr == "" { + fmt.Println("⚠️ 地址为空") return nil } _, portStr, err := splitHostPort(addr) if err != nil { - return fmt.Errorf("parse address failed: %v", err) + fmt.Printf("⚠️ 地址解析失败: %v\n", err) + return nil } port, err := strconv.Atoi(portStr) if err != nil { - return fmt.Errorf("invalid port: %v", err) - } - - if runtime.GOOS != "linux" { - fmt.Println("Warning: current OS is not Linux, skipping connection close.") + fmt.Printf("⚠️ 端口非法: %v\n", err) return nil } - return closePortConnectionsBySS(port) + cmd := exec.Command("tcpkill", "-i", "any", "port", fmt.Sprintf("%d", port)) + if err := cmd.Start(); err != nil { + fmt.Printf("⚠️ 启动 tcpkill 失败: %v\n", err) + return nil + } + + go func() { + defer func() { + if r := recover(); r != nil { + fmt.Printf("⚠️ tcpkill goroutine panic recovered: %v\n", r) + } + }() + time.Sleep(2 * time.Second) + if cmd.Process != nil { + if err := cmd.Process.Kill(); err != nil { + fmt.Printf("⚠️ 终止 tcpkill 失败: %v\n", err) + } + } + }() + + fmt.Printf("✅ 正在断开端口 %d 上的所有连接...\n", port) + return nil } -// splitHostPort 是 net.SplitHostPort 的包装,容错增强 func splitHostPort(addr string) (string, string, error) { if !strings.Contains(addr, ":") { return "", "", fmt.Errorf("missing port in address") @@ -43,7 +64,6 @@ func splitHostPort(addr string) (string, string, error) { return netSplitHostPortCompat(addr) } -// netSplitHostPortCompat 是增强版本的 SplitHostPort,兼容无 host 情况 func netSplitHostPortCompat(addr string) (string, string, error) { lastColon := strings.LastIndex(addr, ":") if lastColon < 0 { @@ -51,67 +71,3 @@ func netSplitHostPortCompat(addr string) (string, string, error) { } return addr[:lastColon], addr[lastColon+1:], nil } - -// closePortConnectionsBySS 通过 ss 找到 pid/fd 然后关闭连接 -func closePortConnectionsBySS(targetPort int) error { - cmd := exec.Command("ss", "-tnp") - out, err := cmd.Output() - if err != nil { - return fmt.Errorf("failed to execute ss: %v", err) - } - - scanner := bufio.NewScanner(strings.NewReader(string(out))) - for scanner.Scan() { - line := scanner.Text() - - if !strings.Contains(line, fmt.Sprintf(":%d", targetPort)) || !strings.Contains(line, "ESTAB") { - continue - } - - // 例如:users:(("gost",pid=1234,fd=17)) - start := strings.Index(line, "pid=") - if start == -1 { - continue - } - - rest := line[start:] - parts := strings.Split(rest, ",") - if len(parts) < 2 { - continue - } - - pidStr := strings.TrimPrefix(parts[0], "pid=") - fdStr := strings.TrimPrefix(parts[1], "fd=") - pid := strings.Trim(pidStr, ")") - fd := strings.Trim(fdStr, ")") - - fdPath := fmt.Sprintf("/proc/%s/fd/%s", pid, fd) - realPath, err := filepath.EvalSymlinks(fdPath) - if err != nil { - fmt.Printf("Warning: unable to resolve %s: %v\n", fdPath, err) - continue - } - - // 再次确认是 socket - if !strings.Contains(realPath, "socket:") { - continue - } - - // 尝试关闭 fd - func() { - defer func() { - if r := recover(); r != nil { - fmt.Printf("Warning: panic closing %s: %v\n", fdPath, r) - } - }() - f, err := os.OpenFile(fdPath, os.O_RDWR, 0) - if err != nil { - fmt.Printf("Warning: open fd failed: %v\n", err) - return - } - defer f.Close() - }() - } - - return scanner.Err() -} diff --git a/panel_install.sh b/panel_install.sh index 2eaf5da..aac2b8d 100755 --- a/panel_install.sh +++ b/panel_install.sh @@ -54,6 +54,74 @@ check_ipv6_support() { fi } +# 检查并安装 tcpkill +check_and_install_tcpkill() { + # 检查 tcpkill 是否已安装 + if command -v tcpkill &> /dev/null; then + return 0 + fi + + # 检测操作系统类型 + OS_TYPE=$(uname -s) + + # 检查是否需要 sudo + if [[ $EUID -ne 0 ]]; then + SUDO_CMD="sudo" + else + SUDO_CMD="" + fi + + if [[ "$OS_TYPE" == "Darwin" ]]; then + if command -v brew &> /dev/null; then + brew install dsniff &> /dev/null + fi + return 0 + fi + + # 检测 Linux 发行版并安装对应的包 + if [ -f /etc/os-release ]; then + . /etc/os-release + DISTRO=$ID + elif [ -f /etc/redhat-release ]; then + DISTRO="rhel" + elif [ -f /etc/debian_version ]; then + DISTRO="debian" + else + return 0 + fi + + case $DISTRO in + ubuntu|debian) + $SUDO_CMD apt update &> /dev/null + $SUDO_CMD apt install -y dsniff &> /dev/null + ;; + centos|rhel|fedora) + if command -v dnf &> /dev/null; then + $SUDO_CMD dnf install -y dsniff &> /dev/null + elif command -v yum &> /dev/null; then + $SUDO_CMD yum install -y dsniff &> /dev/null + fi + ;; + alpine) + $SUDO_CMD apk add --no-cache dsniff &> /dev/null + ;; + arch|manjaro) + $SUDO_CMD pacman -S --noconfirm dsniff &> /dev/null + ;; + opensuse*|sles) + $SUDO_CMD zypper install -y dsniff &> /dev/null + ;; + gentoo) + $SUDO_CMD emerge --ask=n net-analyzer/dsniff &> /dev/null + ;; + void) + $SUDO_CMD xbps-install -Sy dsniff &> /dev/null + ;; + esac + + return 0 +} + # 配置 Docker 启用 IPv6 configure_docker_ipv6() { echo "🔧 配置 Docker IPv6 支持..." @@ -126,8 +194,6 @@ configure_docker_ipv6() { fi } - - # 显示菜单 show_menu() { echo "===============================================" @@ -191,6 +257,8 @@ install_panel() { configure_docker_ipv6 fi + # 检查并安装 tcpkill + check_and_install_tcpkill cat > .env <