Initial commit

This commit is contained in:
2026-06-30 15:14:37 +08:00
commit 15dab96872
311 changed files with 95639 additions and 0 deletions
+99
View File
@@ -0,0 +1,99 @@
package transport
import (
"net"
"sync"
"go.uber.org/zap"
)
// ConnManager 连接管理器
// 维护 peer_key → net.Conn 的映射关系
type ConnManager struct {
conns map[string]net.Conn
mu sync.RWMutex
logger *zap.Logger
}
// NewConnManager 创建连接管理器
func NewConnManager(logger *zap.Logger) *ConnManager {
return &ConnManager{
conns: make(map[string]net.Conn),
logger: logger,
}
}
// Add 添加连接
func (m *ConnManager) Add(peerKey string, conn net.Conn) {
m.mu.Lock()
defer m.mu.Unlock()
// 如果已存在,先关闭旧连接
if oldConn, ok := m.conns[peerKey]; ok {
oldConn.Close()
m.logger.Debug("关闭旧连接", zap.String("peer_key", peerKey))
}
m.conns[peerKey] = conn
m.logger.Info("添加新连接",
zap.String("peer_key", peerKey),
zap.String("remote_addr", conn.RemoteAddr().String()))
}
// Get 获取连接
func (m *ConnManager) Get(peerKey string) (net.Conn, bool) {
m.mu.RLock()
defer m.mu.RUnlock()
conn, ok := m.conns[peerKey]
return conn, ok
}
// Remove 移除连接
func (m *ConnManager) Remove(peerKey string) {
m.mu.Lock()
defer m.mu.Unlock()
if conn, ok := m.conns[peerKey]; ok {
conn.Close()
delete(m.conns, peerKey)
m.logger.Info("移除连接", zap.String("peer_key", peerKey))
}
}
// Count 获取连接数量
func (m *ConnManager) Count() int {
m.mu.RLock()
defer m.mu.RUnlock()
return len(m.conns)
}
// CloseAll 关闭所有连接
func (m *ConnManager) CloseAll() {
m.mu.Lock()
defer m.mu.Unlock()
for peerKey, conn := range m.conns {
conn.Close()
m.logger.Debug("关闭连接", zap.String("peer_key", peerKey))
}
m.conns = make(map[string]net.Conn)
}
// List 列出所有连接(返回副本)
func (m *ConnManager) List() map[string]net.Conn {
m.mu.RLock()
defer m.mu.RUnlock()
result := make(map[string]net.Conn)
for k, v := range m.conns {
result[k] = v
}
return result
}
// GetAll 获取所有连接(同 List,为了兼容)
func (m *ConnManager) GetAll() map[string]net.Conn {
return m.List()
}
+17
View File
@@ -0,0 +1,17 @@
package transport
// ProtocolPlugin 协议插件接口
// relay.go 通过这个接口适配不同协议,不感知具体协议细节
type ProtocolPlugin interface {
// IsControlPacket 判断是否为控制包
// 控制包用于建连协商,需要透传到对端
IsControlPacket(packet []byte) bool
// IsDataPacket 判断是否为数据包
// 数据包包含路由标识,需要查表转发
IsDataPacket(packet []byte) bool
// ExtractRouteID 从数据包中提取路由标识
// 返回的 route_id 用于查找对应的本地端口
ExtractRouteID(packet []byte) (uint32, error)
}
+197
View File
@@ -0,0 +1,197 @@
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))
}
}