mirror of
https://github.com/Sagit-chu/flvx.git
synced 2026-10-12 04:06:37 +08:00
118 lines
2.7 KiB
Go
118 lines
2.7 KiB
Go
package port
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"runtime"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
// ForceClosePortConnections 强制断开指定端口的所有连接
|
|
func ForceClosePortConnections(addr string) error {
|
|
if addr == "" {
|
|
return nil
|
|
}
|
|
|
|
_, portStr, err := splitHostPort(addr)
|
|
if err != nil {
|
|
return fmt.Errorf("parse address failed: %v", err)
|
|
}
|
|
|
|
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.")
|
|
return nil
|
|
}
|
|
|
|
return closePortConnectionsBySS(port)
|
|
}
|
|
|
|
// splitHostPort 是 net.SplitHostPort 的包装,容错增强
|
|
func splitHostPort(addr string) (string, string, error) {
|
|
if !strings.Contains(addr, ":") {
|
|
return "", "", fmt.Errorf("missing port in address")
|
|
}
|
|
return netSplitHostPortCompat(addr)
|
|
}
|
|
|
|
// netSplitHostPortCompat 是增强版本的 SplitHostPort,兼容无 host 情况
|
|
func netSplitHostPortCompat(addr string) (string, string, error) {
|
|
lastColon := strings.LastIndex(addr, ":")
|
|
if lastColon < 0 {
|
|
return "", "", fmt.Errorf("missing port")
|
|
}
|
|
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()
|
|
}
|