From b6059d1b7ab147056d88e8930af14031e64a3de9 Mon Sep 17 00:00:00 2001 From: samwaf Date: Fri, 14 Nov 2025 14:38:04 +0800 Subject: [PATCH] feat: tunnel ip port server port #ID15VI --- waftunnelengine/ip_check.go | 30 ++++++++----- waftunnelengine/tcp_handler.go | 77 ++++++++++++++++++++++++---------- waftunnelengine/udp_handler.go | 60 ++++++++++++++++---------- 3 files changed, 112 insertions(+), 55 deletions(-) diff --git a/waftunnelengine/ip_check.go b/waftunnelengine/ip_check.go index 816fe78..467251b 100644 --- a/waftunnelengine/ip_check.go +++ b/waftunnelengine/ip_check.go @@ -3,24 +3,28 @@ package waftunnelengine import ( "SamWaf/common/zlog" "SamWaf/model" + "fmt" "net" "strings" ) // CheckIPAccess 检查IP是否允许访问 +// 参数: protocol 协议类型(TCP/UDP), clientIP 客户端IP, clientPort 客户端端口, serverPort 服务端口, tunnel 隧道配置 // 返回值: true表示允许访问,false表示拒绝访问 -func CheckIPAccess(clientIP string, tunnel model.Tunnel) bool { +func CheckIPAccess(protocol string, clientIP string, clientPort string, serverPort string, tunnel model.Tunnel) bool { // 如果客户端IP为空,拒绝访问 if clientIP == "" { - zlog.Warn("客户端IP为空,拒绝访问") + zlog.Warn(fmt.Sprintf("客户端IP为空,拒绝访问 [协议:%s 客户端IP:%s 客户端端口:%s 服务端口:%s]", + protocol, clientIP, clientPort, serverPort)) return false } - // 处理IP地址,去除端口部分 + // 处理IP地址,去除端口部分(如果传入的是完整地址) if strings.Contains(clientIP, ":") { host, _, err := net.SplitHostPort(clientIP) if err != nil { - zlog.Error("解析客户端IP失败: " + err.Error()) + zlog.Error(fmt.Sprintf("解析客户端IP失败 [协议:%s 客户端IP:%s 客户端端口:%s 服务端口:%s 错误:%s]", + protocol, clientIP, clientPort, serverPort, err.Error())) return false } clientIP = host @@ -40,12 +44,14 @@ func CheckIPAccess(clientIP string, tunnel model.Tunnel) bool { // CIDR格式 _, ipNet, err := net.ParseCIDR(ip) if err == nil && ipNet.Contains(net.ParseIP(clientIP)) { - zlog.Info("IP " + clientIP + " 在黑名单CIDR " + ip + " 范围内,拒绝访问") + zlog.Info(fmt.Sprintf("IP在黑名单CIDR范围内,拒绝访问 [协议:%s 客户端IP:%s 客户端端口:%s 服务端口:%s CIDR:%s]", + protocol, clientIP, clientPort, serverPort, ip)) return false } } else if ip == clientIP { // 精确匹配 - zlog.Info("IP " + clientIP + " 在黑名单中,拒绝访问") + zlog.Info(fmt.Sprintf("IP在黑名单中,拒绝访问 [协议:%s 客户端IP:%s 客户端端口:%s 服务端口:%s]", + protocol, clientIP, clientPort, serverPort)) return false } } @@ -66,22 +72,26 @@ func CheckIPAccess(clientIP string, tunnel model.Tunnel) bool { // CIDR格式 _, ipNet, err := net.ParseCIDR(ip) if err == nil && ipNet.Contains(net.ParseIP(clientIP)) { - zlog.Info("IP " + clientIP + " 在白名单CIDR " + ip + " 范围内,允许访问") + zlog.Info(fmt.Sprintf("IP在白名单CIDR范围内,允许访问 [协议:%s 客户端IP:%s 客户端端口:%s 服务端口:%s CIDR:%s]", + protocol, clientIP, clientPort, serverPort, ip)) return true } } else if ip == clientIP { // 精确匹配 - zlog.Info("IP " + clientIP + " 在白名单中,允许访问") + zlog.Info(fmt.Sprintf("IP在白名单中,允许访问 [协议:%s 客户端IP:%s 客户端端口:%s 服务端口:%s]", + protocol, clientIP, clientPort, serverPort)) return true } } // 如果有白名单但IP不在其中,拒绝访问 - zlog.Info("IP " + clientIP + " 不在白名单中,拒绝访问") + zlog.Info(fmt.Sprintf("IP不在白名单中,拒绝访问 [协议:%s 客户端IP:%s 客户端端口:%s 服务端口:%s]", + protocol, clientIP, clientPort, serverPort)) return false } // 如果没有设置白名单,且不在黑名单中,则允许访问 - zlog.Info("隧道访问 IP " + clientIP + " 通过访问控制检查,允许访问") + zlog.Info(fmt.Sprintf("隧道访问通过访问控制检查,允许访问 [协议:%s 客户端IP:%s 客户端端口:%s 服务端口:%s]", + protocol, clientIP, clientPort, serverPort)) return true } diff --git a/waftunnelengine/tcp_handler.go b/waftunnelengine/tcp_handler.go index d27b22f..350bf85 100644 --- a/waftunnelengine/tcp_handler.go +++ b/waftunnelengine/tcp_handler.go @@ -4,6 +4,7 @@ import ( "SamWaf/common/zlog" "SamWaf/model/waftunnelmodel" "context" + "fmt" "io" "net" "strconv" @@ -16,7 +17,8 @@ func (waf *WafTunnelEngine) startTCPServer(netRuntime waftunnelmodel.NetRunTime) addr := ":" + strconv.Itoa(netRuntime.Port) listener, err := net.Listen("tcp", addr) if err != nil { - zlog.Error("TCP服务器启动失败,端口: " + strconv.Itoa(netRuntime.Port) + ", 错误: " + err.Error()) + serverPort := strconv.Itoa(netRuntime.Port) + zlog.Error(fmt.Sprintf("TCP服务器启动失败 [服务端口:%s 错误:%s]", serverPort, err.Error())) return } @@ -27,19 +29,29 @@ func (waf *WafTunnelEngine) startTCPServer(netRuntime waftunnelmodel.NetRunTime) netClone.Svr = listener waf.NetListerOnline.Set(key, netClone) - zlog.Info("启动TCP服务器,端口: " + strconv.Itoa(netRuntime.Port)) + serverPort := strconv.Itoa(netRuntime.Port) + zlog.Info(fmt.Sprintf("启动TCP服务器 [服务端口:%s]", serverPort)) for { conn, err := listener.Accept() if err != nil { - zlog.Error("TCP连接接收失败: " + err.Error()) + zlog.Error(fmt.Sprintf("TCP连接接收失败 [服务端口:%s 错误:%s]", serverPort, err.Error())) break } // 获取隧道配置 tunnelInfo, ok := waf.TunnelTarget.Get("tcp" + strconv.Itoa(netRuntime.Port)) if !ok { - zlog.Error("未找到端口对应的隧道配置: " + "tcp" + strconv.Itoa(netRuntime.Port)) + // 获取客户端信息用于日志 + clientAddr := conn.RemoteAddr().String() + clientIP, clientPort, _ := net.SplitHostPort(clientAddr) + if clientIP == "" { + clientIP = clientAddr + clientPort = "unknown" + } + serverPort := strconv.Itoa(netRuntime.Port) + zlog.Error(fmt.Sprintf("未找到端口对应的隧道配置 [客户端IP:%s 客户端端口:%s 服务端口:%s]", + clientIP, clientPort, serverPort)) conn.Close() continue } @@ -48,9 +60,16 @@ func (waf *WafTunnelEngine) startTCPServer(netRuntime waftunnelmodel.NetRunTime) if tunnelInfo.Tunnel.MaxInConnect > 0 { inConnCount := waf.TCPConnections.GetPortConnsCountByType(netRuntime.Port, waftunnelmodel.ConnTypeSource) if inConnCount >= tunnelInfo.Tunnel.MaxInConnect { - zlog.Warn("TCP入站连接数超过限制,当前连接数: " + strconv.Itoa(inConnCount) + - ", 最大限制: " + strconv.Itoa(tunnelInfo.Tunnel.MaxInConnect) + - ", 端口: " + strconv.Itoa(netRuntime.Port)) + // 获取客户端信息用于日志 + clientAddr := conn.RemoteAddr().String() + clientIP, clientPort, _ := net.SplitHostPort(clientAddr) + if clientIP == "" { + clientIP = clientAddr + clientPort = "unknown" + } + serverPort := strconv.Itoa(netRuntime.Port) + zlog.Warn(fmt.Sprintf("TCP入站连接数超过限制 [客户端IP:%s 客户端端口:%s 服务端口:%s 当前连接数:%d 最大限制:%d]", + clientIP, clientPort, serverPort, inConnCount, tunnelInfo.Tunnel.MaxInConnect)) conn.Close() continue } @@ -60,25 +79,31 @@ func (waf *WafTunnelEngine) startTCPServer(netRuntime waftunnelmodel.NetRunTime) go waf.handleTCPConnection(conn, netRuntime.Port) } - zlog.Info("TCP服务器关闭,端口: " + strconv.Itoa(netRuntime.Port)) + zlog.Info(fmt.Sprintf("TCP服务器关闭 [服务端口:%s]", serverPort)) } // handleTCPConnection 处理TCP连接 func (waf *WafTunnelEngine) handleTCPConnection(clientConn net.Conn, port int) { - // 获取客户端IP - clientIP := clientConn.RemoteAddr().String() + // 获取客户端IP和端口 + clientAddr := clientConn.RemoteAddr().String() + clientIP, clientPort, _ := net.SplitHostPort(clientAddr) + if clientIP == "" { + clientIP = clientAddr + clientPort = "unknown" + } + serverPort := strconv.Itoa(port) // 获取隧道配置 tunnelInfo, ok := waf.TunnelTarget.Get("tcp" + strconv.Itoa(port)) if !ok { - zlog.Error("未找到端口对应的隧道配置: " + "tcp" + strconv.Itoa(port)) + zlog.Error(fmt.Sprintf("未找到端口对应的隧道配置 [客户端IP:%s 客户端端口:%s 服务端口:%s]", clientIP, clientPort, serverPort)) clientConn.Close() return } // 检查IP访问权限 - if !CheckIPAccess(clientIP, tunnelInfo.Tunnel) { - zlog.Warn("TCP连接被拒绝,客户端IP: " + clientIP + ", 端口: " + strconv.Itoa(port)) + if !CheckIPAccess("TCP", clientIP, clientPort, serverPort, tunnelInfo.Tunnel) { + zlog.Warn(fmt.Sprintf("TCP连接被拒绝 [客户端IP:%s 客户端端口:%s 服务端口:%s]", clientIP, clientPort, serverPort)) clientConn.Close() return } @@ -94,9 +119,8 @@ func (waf *WafTunnelEngine) handleTCPConnection(clientConn net.Conn, port int) { if tunnelInfo.Tunnel.MaxOutConnect > 0 { outConnCount := waf.TCPConnections.GetPortConnsCountByType(port, waftunnelmodel.ConnTypeTarget) if outConnCount >= tunnelInfo.Tunnel.MaxOutConnect { - zlog.Warn("TCP出站连接数超过限制,当前连接数: " + strconv.Itoa(outConnCount) + - ", 最大限制: " + strconv.Itoa(tunnelInfo.Tunnel.MaxOutConnect) + - ", 端口: " + strconv.Itoa(port)) + zlog.Warn(fmt.Sprintf("TCP出站连接数超过限制 [客户端IP:%s 客户端端口:%s 服务端口:%s 当前连接数:%d 最大限制:%d]", + clientIP, clientPort, serverPort, outConnCount, tunnelInfo.Tunnel.MaxOutConnect)) return } } @@ -105,7 +129,8 @@ func (waf *WafTunnelEngine) handleTCPConnection(clientConn net.Conn, port int) { targetAddr := tunnelInfo.Tunnel.RemoteIp + ":" + strconv.Itoa(tunnelInfo.Tunnel.RemotePort) targetConn, err := net.Dial("tcp", targetAddr) if err != nil { - zlog.Error("连接目标服务器失败: " + targetAddr + ", 错误: " + err.Error()) + zlog.Error(fmt.Sprintf("连接目标服务器失败 [客户端IP:%s 客户端端口:%s 服务端口:%s 目标地址:%s 错误:%s]", + clientIP, clientPort, serverPort, targetAddr, err.Error())) return } @@ -149,9 +174,11 @@ func (waf *WafTunnelEngine) handleTCPConnection(clientConn net.Conn, port int) { // 使用io.Copy,当连接断开时会自动返回 _, err := io.Copy(targetConn, clientConn) if err != nil { - zlog.Error("客户端->目标 数据转发结束: " + err.Error()) + zlog.Error(fmt.Sprintf("客户端->目标 数据转发结束 [客户端IP:%s 客户端端口:%s 服务端口:%s 错误:%s]", + clientIP, clientPort, serverPort, err.Error())) } else { - zlog.Debug("客户端->目标 数据转发正常结束") + zlog.Debug(fmt.Sprintf("客户端->目标 数据转发正常结束 [客户端IP:%s 客户端端口:%s 服务端口:%s]", + clientIP, clientPort, serverPort)) } }() @@ -163,9 +190,11 @@ func (waf *WafTunnelEngine) handleTCPConnection(clientConn net.Conn, port int) { // 使用io.Copy,当连接断开时会自动返回 _, err := io.Copy(clientConn, targetConn) if err != nil { - zlog.Error("目标->客户端 数据转发结束: " + err.Error()) + zlog.Error(fmt.Sprintf("目标->客户端 数据转发结束 [客户端IP:%s 客户端端口:%s 服务端口:%s 错误:%s]", + clientIP, clientPort, serverPort, err.Error())) } else { - zlog.Debug("目标->客户端 数据转发正常结束") + zlog.Debug(fmt.Sprintf("目标->客户端 数据转发正常结束 [客户端IP:%s 客户端端口:%s 服务端口:%s]", + clientIP, clientPort, serverPort)) } }() @@ -175,10 +204,12 @@ func (waf *WafTunnelEngine) handleTCPConnection(clientConn net.Conn, port int) { // context被取消时,主动关闭两个连接 clientConn.Close() targetConn.Close() - zlog.Debug("Context取消,主动关闭连接") + zlog.Debug(fmt.Sprintf("Context取消,主动关闭连接 [客户端IP:%s 客户端端口:%s 服务端口:%s]", + clientIP, clientPort, serverPort)) }() // 等待任一方向的连接断开 wg.Wait() - zlog.Info("TCP连接处理完成,端口: " + strconv.Itoa(port)) + zlog.Info(fmt.Sprintf("TCP连接处理完成 [客户端IP:%s 客户端端口:%s 服务端口:%s]", + clientIP, clientPort, serverPort)) } diff --git a/waftunnelengine/udp_handler.go b/waftunnelengine/udp_handler.go index 1ecd992..0f4489e 100644 --- a/waftunnelengine/udp_handler.go +++ b/waftunnelengine/udp_handler.go @@ -3,6 +3,7 @@ package waftunnelengine import ( "SamWaf/common/zlog" "SamWaf/model/waftunnelmodel" + "fmt" "net" "strconv" "time" @@ -13,13 +14,15 @@ func (waf *WafTunnelEngine) startUDPServer(netRuntime waftunnelmodel.NetRunTime) addr := ":" + strconv.Itoa(netRuntime.Port) udpAddr, err := net.ResolveUDPAddr("udp", addr) if err != nil { - zlog.Error("UDP地址解析失败,端口: " + strconv.Itoa(netRuntime.Port) + ", 错误: " + err.Error()) + serverPort := strconv.Itoa(netRuntime.Port) + zlog.Error(fmt.Sprintf("UDP地址解析失败 [服务端口:%s 错误:%s]", serverPort, err.Error())) return } conn, err := net.ListenUDP("udp", udpAddr) if err != nil { - zlog.Error("UDP服务器启动失败,端口: " + strconv.Itoa(netRuntime.Port) + ", 错误: " + err.Error()) + serverPort := strconv.Itoa(netRuntime.Port) + zlog.Error(fmt.Sprintf("UDP服务器启动失败 [服务端口:%s 错误:%s]", serverPort, err.Error())) return } @@ -33,21 +36,27 @@ func (waf *WafTunnelEngine) startUDPServer(netRuntime waftunnelmodel.NetRunTime) netClone.Svr = conn waf.NetListerOnline.Set(key, netClone) - zlog.Info("启动UDP服务器,端口: " + strconv.Itoa(netRuntime.Port)) + serverPort := strconv.Itoa(netRuntime.Port) + zlog.Info(fmt.Sprintf("启动UDP服务器 [服务端口:%s]", serverPort)) // 处理UDP数据 buffer := make([]byte, 1024) for { n, remoteAddr, err := conn.ReadFromUDP(buffer) if err != nil { - zlog.Error("UDP读取失败: " + err.Error()) + zlog.Error(fmt.Sprintf("UDP读取失败 [服务端口:%s 错误:%s]", serverPort, err.Error())) break } + // 获取客户端信息用于日志 + clientIP := remoteAddr.IP.String() + clientPort := strconv.Itoa(remoteAddr.Port) + // 获取隧道配置 tunnelInfo, ok := waf.TunnelTarget.Get("udp" + strconv.Itoa(netRuntime.Port)) if !ok { - zlog.Error("未找到端口对应的隧道配置: " + "udp" + strconv.Itoa(netRuntime.Port)) + zlog.Error(fmt.Sprintf("未找到端口对应的隧道配置 [客户端IP:%s 客户端端口:%s 服务端口:%s]", + clientIP, clientPort, serverPort)) continue } @@ -55,9 +64,8 @@ func (waf *WafTunnelEngine) startUDPServer(netRuntime waftunnelmodel.NetRunTime) if tunnelInfo.Tunnel.MaxInConnect > 0 { inConnCount := waf.UDPConnections.GetPortConnsCountByType(netRuntime.Port, waftunnelmodel.ConnTypeSource) if inConnCount >= tunnelInfo.Tunnel.MaxInConnect { - zlog.Warn("UDP入站连接数超过限制,当前连接数: " + strconv.Itoa(inConnCount) + - ", 最大限制: " + strconv.Itoa(tunnelInfo.Tunnel.MaxInConnect) + - ", 端口: " + strconv.Itoa(netRuntime.Port)) + zlog.Warn(fmt.Sprintf("UDP入站连接数超过限制 [客户端IP:%s 客户端端口:%s 服务端口:%s 当前连接数:%d 最大限制:%d]", + clientIP, clientPort, serverPort, inConnCount, tunnelInfo.Tunnel.MaxInConnect)) continue } } @@ -66,25 +74,29 @@ func (waf *WafTunnelEngine) startUDPServer(netRuntime waftunnelmodel.NetRunTime) go waf.handleUDPData(conn, remoteAddr, buffer[:n], netRuntime.Port) } - zlog.Info("UDP服务器关闭,端口: " + strconv.Itoa(netRuntime.Port)) + zlog.Info(fmt.Sprintf("UDP服务器关闭 [服务端口:%s]", serverPort)) waf.UDPConnections.RemoveConn(netRuntime.Port, conn) } // handleUDPData 处理UDP数据 func (waf *WafTunnelEngine) handleUDPData(serverConn *net.UDPConn, clientAddr *net.UDPAddr, data []byte, port int) { - // 获取客户端IP - clientIP := clientAddr.String() + // 获取客户端IP和端口 + clientIP := clientAddr.IP.String() + clientPort := strconv.Itoa(clientAddr.Port) + serverPort := strconv.Itoa(port) // 获取隧道配置 tunnelInfo, ok := waf.TunnelTarget.Get("udp" + strconv.Itoa(port)) if !ok { - zlog.Error("未找到端口对应的隧道配置: " + "udp" + strconv.Itoa(port)) + zlog.Error(fmt.Sprintf("未找到端口对应的隧道配置 [客户端IP:%s 客户端端口:%s 服务端口:%s]", + clientIP, clientPort, serverPort)) return } // 检查IP访问权限 - if !CheckIPAccess(clientIP, tunnelInfo.Tunnel) { - zlog.Warn("UDP数据被拒绝,客户端IP: " + clientIP + ", 端口: " + strconv.Itoa(port)) + if !CheckIPAccess("UDP", clientIP, clientPort, serverPort, tunnelInfo.Tunnel) { + zlog.Warn(fmt.Sprintf("UDP数据被拒绝 [客户端IP:%s 客户端端口:%s 服务端口:%s]", + clientIP, clientPort, serverPort)) return } @@ -92,9 +104,8 @@ func (waf *WafTunnelEngine) handleUDPData(serverConn *net.UDPConn, clientAddr *n if tunnelInfo.Tunnel.MaxOutConnect > 0 { outConnCount := waf.UDPConnections.GetPortConnsCountByType(port, waftunnelmodel.ConnTypeTarget) if outConnCount >= tunnelInfo.Tunnel.MaxOutConnect { - zlog.Warn("UDP出站连接数超过限制,当前连接数: " + strconv.Itoa(outConnCount) + - ", 最大限制: " + strconv.Itoa(tunnelInfo.Tunnel.MaxOutConnect) + - ", 端口: " + strconv.Itoa(port)) + zlog.Warn(fmt.Sprintf("UDP出站连接数超过限制 [客户端IP:%s 客户端端口:%s 服务端口:%s 当前连接数:%d 最大限制:%d]", + clientIP, clientPort, serverPort, outConnCount, tunnelInfo.Tunnel.MaxOutConnect)) return } } @@ -103,14 +114,16 @@ func (waf *WafTunnelEngine) handleUDPData(serverConn *net.UDPConn, clientAddr *n targetAddr := tunnelInfo.Tunnel.RemoteIp + ":" + strconv.Itoa(tunnelInfo.Tunnel.RemotePort) raddr, err := net.ResolveUDPAddr("udp", targetAddr) if err != nil { - zlog.Error("解析目标地址失败: " + targetAddr + ", 错误: " + err.Error()) + zlog.Error(fmt.Sprintf("解析目标地址失败 [客户端IP:%s 客户端端口:%s 服务端口:%s 目标地址:%s 错误:%s]", + clientIP, clientPort, serverPort, targetAddr, err.Error())) return } // 创建到目标的连接 targetConn, err := net.DialUDP("udp", nil, raddr) if err != nil { - zlog.Error("连接目标服务器失败: " + targetAddr + ", 错误: " + err.Error()) + zlog.Error(fmt.Sprintf("连接目标服务器失败 [客户端IP:%s 客户端端口:%s 服务端口:%s 目标地址:%s 错误:%s]", + clientIP, clientPort, serverPort, targetAddr, err.Error())) return } @@ -139,7 +152,8 @@ func (waf *WafTunnelEngine) handleUDPData(serverConn *net.UDPConn, clientAddr *n // 发送数据到目标 _, err = targetConn.Write(data) if err != nil { - zlog.Error("发送数据到目标失败: " + err.Error()) + zlog.Error(fmt.Sprintf("发送数据到目标失败 [客户端IP:%s 客户端端口:%s 服务端口:%s 错误:%s]", + clientIP, clientPort, serverPort, err.Error())) return } @@ -147,14 +161,16 @@ func (waf *WafTunnelEngine) handleUDPData(serverConn *net.UDPConn, clientAddr *n buffer := make([]byte, 4096) n, _, err := targetConn.ReadFromUDP(buffer) if err != nil { - zlog.Error("从目标接收数据失败: " + err.Error()) + zlog.Error(fmt.Sprintf("从目标接收数据失败 [客户端IP:%s 客户端端口:%s 服务端口:%s 错误:%s]", + clientIP, clientPort, serverPort, err.Error())) return } // 发送响应回客户端 _, err = serverConn.WriteToUDP(buffer[:n], clientAddr) if err != nil { - zlog.Error("发送响应到客户端失败: " + err.Error()) + zlog.Error(fmt.Sprintf("发送响应到客户端失败 [客户端IP:%s 客户端端口:%s 服务端口:%s 错误:%s]", + clientIP, clientPort, serverPort, err.Error())) return } }