package transport import ( "context" "net" "sync" "go.uber.org/zap" ) // OnDialTrigger 当从本地端口收到包但没有远端连接时触发 type OnDialTrigger func(peerKey string) // Relay 数据转发器 // 负责从本地端口收包 → 查路由 → 通过 conn 发送 // 从 conn 收包 → 发到本地端口 type Relay struct { plugin ProtocolPlugin connMgr *ConnManager localPorts map[uint32]net.PacketConn // route_id → local_port lastAddr map[uint32]net.Addr // route_id → last wg source addr portMu sync.RWMutex logger *zap.Logger OnDialTrigger OnDialTrigger // 拨号触发回调 } // NewRelay 创建数据转发器 func NewRelay(plugin ProtocolPlugin, connMgr *ConnManager, logger *zap.Logger) *Relay { return &Relay{ plugin: plugin, connMgr: connMgr, localPorts: make(map[uint32]net.PacketConn), lastAddr: make(map[uint32]net.Addr), logger: logger, } } // RegisterLocalPort 注册本地端口(用于接收 WG 密文包) func (r *Relay) RegisterLocalPort(routeID uint32, port net.PacketConn) { r.portMu.Lock() defer r.portMu.Unlock() r.localPorts[routeID] = port r.logger.Info("注册本地端口", zap.Uint32("route_id", routeID), zap.String("addr", port.LocalAddr().String())) } // UnregisterLocalPort 注销本地端口 func (r *Relay) UnregisterLocalPort(routeID uint32) { r.portMu.Lock() defer r.portMu.Unlock() if port, ok := r.localPorts[routeID]; ok { port.Close() delete(r.localPorts, routeID) delete(r.lastAddr, routeID) r.logger.Info("注销本地端口", zap.Uint32("route_id", routeID)) } } // StartReadFromLocalPort 从本地端口读取 WG 密文包并转发(发送到远端) func (r *Relay) StartReadFromLocalPort(ctx context.Context, routeID uint32, peerKey string) { r.portMu.RLock() port, ok := r.localPorts[routeID] r.portMu.RUnlock() if !ok { r.logger.Warn("本地端口未注册", zap.Uint32("route_id", routeID)) return } go func() { buf := make([]byte, 65535) for { select { case <-ctx.Done(): return default: n, addr, err := port.ReadFrom(buf) if err != nil { // 检查是否是由于关闭引起的错误 select { case <-ctx.Done(): return default: } r.logger.Debug("读取本地端口失败", zap.Uint32("route_id", routeID), zap.Error(err)) return } // 记录 WG 的来源地址,以便后续把包发回去 r.portMu.Lock() r.lastAddr[routeID] = addr r.portMu.Unlock() packet := buf[:n] r.forwardOutgoing(ctx, packet, peerKey) } } }() r.logger.Info("启动本地端口读取协程", zap.Uint32("route_id", routeID), zap.String("peer_key", peerKey)) } // StartReadFromRemoteConn 从远端连接读取数据并转发给本地监听端口(接收远端数据) func (r *Relay) StartReadFromRemoteConn(ctx context.Context, peerKey string, conn net.Conn) { if conn == nil { return } go func() { buf := make([]byte, 65535) for { select { case <-ctx.Done(): return default: n, err := conn.Read(buf) if err != nil { r.logger.Debug("读取远端连接失败,停止读取协程", zap.String("peer_key", peerKey), zap.Error(err)) return } packet := buf[:n] // 远端进来的包,需要根据 packet 里的索引转发给对应的 localPort r.forwardIncoming(packet, peerKey) } } }() r.logger.Info("启动远端连接读取协程", zap.String("peer_key", peerKey), zap.String("addr", conn.RemoteAddr().String())) } // forwardOutgoing 处理发出去的包(Local -> Remote) func (r *Relay) forwardOutgoing(ctx context.Context, packet []byte, peerKey string) { // 获取或触发建连 conn, ok := r.connMgr.Get(peerKey) if !ok || conn == nil { // 没有连接,触发拨号 if r.OnDialTrigger != nil { r.OnDialTrigger(peerKey) } r.logger.Debug("尚未建立连接,包已丢弃,触发静默拨号", zap.String("peer_key", peerKey)) return } // 转发给远端 _, err := conn.Write(packet) if err != nil { r.logger.Debug("转发包到远端失败", zap.String("peer_key", peerKey), zap.Error(err)) } } // forwardIncoming 处理进来的包(Remote -> Local) func (r *Relay) forwardIncoming(packet []byte, _ string) { // 1. 判断是否为控制包/数据包并提取 routeID // 无论哪种 WG 包,前几位都是 routeID (receiver index) routeID, err := r.plugin.ExtractRouteID(packet) if err != nil { r.logger.Debug("提取包内索引失败", zap.Error(err)) return } // 2. 这里的 routeID 是我们 RegisterLocalPort 时用的 ID r.portMu.RLock() port, ok := r.localPorts[routeID] addr, addrOk := r.lastAddr[routeID] r.portMu.RUnlock() if !ok || port == nil { r.logger.Debug("未找到转发目标的本地端口", zap.Uint32("route_id", routeID)) return } if !addrOk || addr == nil { // 如果还没收到过 WG 的包,尝试发给 127.0.0.1:0 (通常不会成功,但作为 fallback) // 实际上 WG 发送握手包后就会刷新 addr addr = &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0} } // 3. 转发给本地 WG _, err = port.WriteTo(packet, addr) if err != nil { r.logger.Debug("转发给本地 WG 失败", zap.Uint32("route_id", routeID), zap.Error(err)) } }