修复添加用户后引起的编辑错误

This commit is contained in:
qaq
2025-06-28 22:42:20 +08:00
parent f044152c0c
commit 34f5015369
7 changed files with 368 additions and 54 deletions
+27 -2
View File
@@ -2,7 +2,9 @@ package api
import (
"fmt"
"net/http"
"strings"
"time"
@@ -10,6 +12,7 @@ import (
"github.com/go-gost/core/service"
"github.com/go-gost/x/config"
parser "github.com/go-gost/x/config/parsing/service"
kill "github.com/go-gost/x/internal/util/port"
"github.com/go-gost/x/registry"
)
@@ -534,8 +537,24 @@ func pauseService(ctx *gin.Context) {
return
}
// 关闭服务但保持注册状态
// 获取服务地址用于强制断开连接
var serviceAddr string
cfg := config.Global()
for _, s := range cfg.Services {
if s.Name == name {
serviceAddr = s.Addr
break
}
}
// 使用和 updateService 相同的方法彻底断开所有连接
svc.Close()
registry.ServiceRegistry().Unregister(name)
// 强制断开端口的所有连接
if serviceAddr != "" {
_ = kill.ForceClosePortConnections(serviceAddr)
}
// 更新配置中的暂停状态
config.OnUpdate(func(c *config.Config) error {
@@ -893,8 +912,14 @@ func pauseServices(ctx *gin.Context) {
return
}
// 暂停服务
// 使用和 updateService 相同的方法彻底断开所有连接
stp.service.Close()
registry.ServiceRegistry().Unregister(stp.name)
// 强制断开端口的所有连接
if serviceConfig.Addr != "" {
_ = kill.ForceClosePortConnections(serviceConfig.Addr)
}
// 记录已暂停的服务
pausedServices = append(pausedServices, struct {
+193
View File
@@ -0,0 +1,193 @@
package port
import (
"bufio"
"fmt"
"net"
"os"
"runtime"
"strconv"
"strings"
)
// ForceClosePortConnections 强制断开指定端口的所有连接
func ForceClosePortConnections(addr string) error {
if addr == "" {
return nil
}
// 解析地址和端口
host, portStr, err := net.SplitHostPort(addr)
if err != nil {
// 如果解析失败,可能是因为没有端口,或者格式不对
return fmt.Errorf("failed to parse address %s: %v", addr, err)
}
port, err := strconv.Atoi(portStr)
if err != nil {
return fmt.Errorf("invalid port %s: %v", portStr, err)
}
switch runtime.GOOS {
case "linux":
return forceClosePortConnectionsLinux(host, port)
default:
// 对于非Linux系统,静默忽略
return nil
}
}
// forceClosePortConnectionsLinux 在Linux上通过/proc/net/tcp*读取连接并关闭
func forceClosePortConnectionsLinux(host string, port int) error {
// 关闭IPv4连接
if err := closeConnectionsFromProcNet("/proc/net/tcp", port); err != nil {
fmt.Printf("Warning: failed to close IPv4 connections on port %d: %v\n", port, err)
}
// 关闭IPv6连接
if err := closeConnectionsFromProcNet("/proc/net/tcp6", port); err != nil {
fmt.Printf("Warning: failed to close IPv6 connections on port %d: %v\n", port, err)
}
return nil
}
// closeConnectionsFromProcNet 从/proc/net/tcp*文件读取连接信息并关闭连接
func closeConnectionsFromProcNet(procFile string, targetPort int) error {
file, err := os.Open(procFile)
if err != nil {
return err
}
defer file.Close()
scanner := bufio.NewScanner(file)
// 跳过第一行(header)
scanner.Scan()
for scanner.Scan() {
line := scanner.Text()
fields := strings.Fields(line)
if len(fields) < 10 {
continue
}
// local_address字段格式: IP:PORT (hex)
localAddr := fields[1]
parts := strings.Split(localAddr, ":")
if len(parts) != 2 {
continue
}
// 解析端口(十六进制)
portHex := parts[1]
port, err := strconv.ParseInt(portHex, 16, 32)
if err != nil {
continue
}
// 检查是否是目标端口
if int(port) == targetPort {
// 获取远程地址信息
remoteAddr := fields[2]
state := fields[3]
// 只关闭已建立的连接,跳过监听状态
if state == "0A" { // LISTEN状态
continue
}
// 尝试通过socket操作关闭连接
if err := closeConnectionByAddresses(localAddr, remoteAddr); err != nil {
fmt.Printf("Warning: failed to close connection %s->%s: %v\n", localAddr, remoteAddr, err)
}
}
}
return scanner.Err()
}
// closeConnectionByAddresses 通过地址信息关闭连接
func closeConnectionByAddresses(localAddr, remoteAddr string) error {
// 解析本地地址
localParts := strings.Split(localAddr, ":")
if len(localParts) != 2 {
return fmt.Errorf("invalid local address format")
}
// 解析远程地址
remoteParts := strings.Split(remoteAddr, ":")
if len(remoteParts) != 2 {
return fmt.Errorf("invalid remote address format")
}
// 转换十六进制IP和端口
localIP, err := hexToIP(localParts[0])
if err != nil {
return err
}
localPort, err := strconv.ParseInt(localParts[1], 16, 32)
if err != nil {
return err
}
remoteIP, err := hexToIP(remoteParts[0])
if err != nil {
return err
}
remotePort, err := strconv.ParseInt(remoteParts[1], 16, 32)
if err != nil {
return err
}
// 创建连接并立即关闭来触发RST
localAddr4 := fmt.Sprintf("%s:%d", localIP, localPort)
remoteAddr4 := fmt.Sprintf("%s:%d", remoteIP, remotePort)
return sendRSTToConnection(localAddr4, remoteAddr4)
}
// hexToIP 将十六进制IP地址转换为可读格式
func hexToIP(hexIP string) (string, error) {
if len(hexIP) == 8 { // IPv4
ip := make([]byte, 4)
for i := 0; i < 4; i++ {
val, err := strconv.ParseUint(hexIP[i*2:(i+1)*2], 16, 8)
if err != nil {
return "", err
}
ip[3-i] = byte(val) // 小端序
}
return fmt.Sprintf("%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]), nil
}
// 对于IPv6,处理更复杂,这里简化处理
return "", fmt.Errorf("IPv6 not implemented yet")
}
// sendRSTToConnection 向指定连接发送RST包
func sendRSTToConnection(localAddr, remoteAddr string) error {
// 尝试创建一个到远程地址的连接,然后立即使用SO_LINGER=0关闭
// 这会发送RST包而不是正常的FIN包
conn, err := net.Dial("tcp", remoteAddr)
if err != nil {
// 如果无法连接,说明连接可能已经不存在了
return nil
}
// 获取底层的TCP连接
tcpConn, ok := conn.(*net.TCPConn)
if !ok {
conn.Close()
return fmt.Errorf("not a TCP connection")
}
// 设置SO_LINGER为0,这样关闭时会发送RST而不是FIN
if err := tcpConn.SetLinger(0); err != nil {
tcpConn.Close()
return err
}
// 关闭连接,这会发送RST包
return tcpConn.Close()
}
+8 -1
View File
@@ -9,6 +9,7 @@ import (
"github.com/go-gost/core/service"
"github.com/go-gost/x/config"
parser "github.com/go-gost/x/config/parsing/service"
kill "github.com/go-gost/x/internal/util/port"
"github.com/go-gost/x/registry"
)
@@ -273,8 +274,14 @@ func pauseServices(req pauseServicesRequest) error {
return errors.New(fmt.Sprintf("service %s configuration not found", stp.name))
}
// 暂停服务
// 使用和 updateService 相同的方法彻底断开所有连接
stp.service.Close()
registry.ServiceRegistry().Unregister(stp.name)
// 强制断开端口的所有连接
if serviceConfig.Addr != "" {
_ = kill.ForceClosePortConnections(serviceConfig.Addr)
}
// 记录已暂停的服务
pausedServices = append(pausedServices, struct {
+7 -8
View File
@@ -341,14 +341,13 @@ func (w *WebSocketReporter) handleReceivedMessage(messageType int, message []byt
// routeCommand 路由命令到对应的处理函数
func (w *WebSocketReporter) routeCommand(cmd CommandMessage) {
//jsonBytes, errs := json.MarshalIndent(cmd, "", " ")
//if errs != nil {
// fmt.Println("Error marshaling JSON:", errs)
// return
//}
//
//// 打印 JSON 字符串
//fmt.Println("🔔 收到命令: ", string(jsonBytes))
jsonBytes, errs := json.Marshal(cmd)
if errs != nil {
fmt.Println("Error marshaling JSON:", errs)
return
}
fmt.Println("🔔 收到命令: ", string(jsonBytes))
var err error
var response CommandResponse