添加强制断开链接

This commit is contained in:
qaq
2025-06-30 14:45:21 +08:00
parent 0499f97e04
commit 2321a7cb8b
2 changed files with 108 additions and 81 deletions
+35 -79
View File
@@ -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()
}