package fwd import ( "bufio" "context" "encoding/binary" "io" "log/slog" "net" "proxy-server/pkg/utils" "proxy-server/server/fwd/socks" "proxy-server/server/pkg/env" "strconv" "sync" "time" "github.com/pkg/errors" ) type Config struct { } type Service struct { Config *Config ctx context.Context cancel context.CancelFunc userConnMap sync.Map ctrlConnWg utils.CountWaitGroup dataConnWg utils.CountWaitGroup fwdLesWg utils.CountWaitGroup } func New(config *Config) *Service { if config == nil { config = &Config{} } ctx, cancel := context.WithCancel(context.Background()) return &Service{ Config: config, ctx: ctx, cancel: cancel, userConnMap: sync.Map{}, ctrlConnWg: utils.CountWaitGroup{}, dataConnWg: utils.CountWaitGroup{}, fwdLesWg: utils.CountWaitGroup{}, } } func (s *Service) Close() { start := time.Now() s.cancel() slog.Debug("退出服务", "duration", time.Since(start)) } func (s *Service) Run() { slog.Info("启动 fwd 服务") errQuit := make(chan struct{}) defer close(errQuit) wg := sync.WaitGroup{} // 控制通道监听 wg.Add(1) go func() { defer wg.Done() err := s.startCtrlTun() if err != nil { slog.Error("fwd 控制通道监听发生错误", "err", err) errQuit <- struct{}{} return } }() // 数据通道监听 wg.Add(1) go func() { defer wg.Done() err := s.startDataTun() if err != nil { slog.Error("fwd 数据通道监听发生错误", "err", err) errQuit <- struct{}{} return } }() // 等待退出 select { case <-s.ctx.Done(): slog.Info("fwd 服务主动退出") case <-errQuit: slog.Warn("fwd 服务异常退出") s.Close() } // 清理资源 s.userConnMap.Range(func(key, value any) bool { conn := value.(socks.ProxyConn) utils.Close(conn) s.userConnMap.Delete(key) return true }) s.userConnMap.Clear() s.ctrlConnWg.Wait() slog.Debug("控制通道连接已关闭") s.dataConnWg.Wait() slog.Debug("数据通道连接已关闭") s.fwdLesWg.Wait() slog.Debug("转发服务已关闭") wg.Wait() slog.Info("fwd 服务已退出") } func (s *Service) startCtrlTun() error { ctrlPort := env.AppCtrlPort slog.Debug("监听控制通道", slog.Uint64("port", uint64(ctrlPort))) // 监听端口 ls, err := net.Listen("tcp", ":"+strconv.Itoa(int(ctrlPort))) if err != nil { return errors.Wrap(err, "监听控制通道失败") } defer utils.Close(ls) // 等待连接 connCh := utils.ChanConnAccept(s.ctx, ls) defer close(connCh) // 处理连接 for { select { case <-s.ctx.Done(): slog.Debug("服务关闭 startCtrlTun") return nil case conn, ok := <-connCh: if !ok { slog.Debug("结束处理连接,由于获取连接失败") return errors.New("获取连接失败") } s.ctrlConnWg.Add(1) go func() { defer s.ctrlConnWg.Done() defer utils.Close(conn) err := s.processCtrlConn(conn) if err != nil { slog.Error("处理控制通道连接失败", "err", err) } }() } } } func (s *Service) processCtrlConn(controller net.Conn) error { slog.Debug("客户端连入", "addr", controller.RemoteAddr().String()) reader := bufio.NewReader(controller) // 获取转发端口 portBuf, err := utils.ReadBuffer(reader, 2) if err != nil { return errors.Wrap(err, "获取转发端口失败") } port := binary.BigEndian.Uint16(portBuf) // 开放转发端口 todo 混合转发 slog.Debug("开放转发端口", "port", port) proxy, err := socks.New(&socks.Config{ Name: strconv.Itoa(int(port)), Port: port, AuthMethods: []socks.Authenticator{ &UserPassAuthenticator{}, &NoAuthAuthenticator{}, }, }) if err != nil { return errors.Wrap(err, "创建 socks 转发服务失败") } defer proxy.Close() s.fwdLesWg.Add(1) go func() { defer s.fwdLesWg.Done() err := proxy.Run() if err != nil { slog.Error("代理服务启动失败", "err", err) return } }() // 等待用户连接 wg := sync.WaitGroup{} for loop := true; loop; { select { case <-s.ctx.Done(): loop = false case user, ok := <-proxy.Conn: if !ok { loop = false err = errors.New("无法获取连接") } wg.Add(1) go func() { defer wg.Done() tag := user.Tag() tagLen := len(tag) tagBuf := make([]byte, 1+tagLen) tagBuf[0] = byte(tagLen) copy(tagBuf[1:], tag) _, err := controller.Write(tagBuf) if err != nil { utils.Close(user) slog.Error("向客户端发送 tag 失败", "err", err) return } s.userConnMap.Store(tag, user) }() } } wg.Wait() return nil } func (s *Service) startDataTun() error { dataPort := env.AppDataPort slog.Debug("监听数据通道", slog.Uint64("port", uint64(dataPort))) // 监听端口 ls, err := net.Listen("tcp", ":"+strconv.Itoa(int(dataPort))) if err != nil { return errors.Wrap(err, "监听数据通道失败") } defer utils.Close(ls) // 等待连接 connCh := utils.ChanConnAccept(s.ctx, ls) defer close(connCh) // 处理连接 for { select { case <-s.ctx.Done(): slog.Debug("服务关闭 startDataTun") return nil case conn, ok := <-connCh: if !ok { slog.Debug("结束处理连接,由于获取连接失败") return errors.New("获取连接失败") } s.dataConnWg.Add(1) go func() { defer s.dataConnWg.Done() defer utils.Close(conn) err := s.processDataConn(conn) if err != nil { slog.Error("处理数据通道失败", "err", err) } }() } } } func (s *Service) processDataConn(client net.Conn) error { slog.Info("客户端准备接收数据 " + client.RemoteAddr().String()) // 读取 tag tagLen, err := utils.ReadByte(client) if err != nil { return errors.Wrap(err, "从客户端获取 tag 失败") } tagBuf, err := utils.ReadBuffer(client, int(tagLen)) if err != nil { return errors.Wrap(err, "从客户端获取 tag 失败") } tag := string(tagBuf) // 找到用户连接 var data socks.ProxyConn select { case <-s.ctx.Done(): return nil default: dataAny, ok := s.userConnMap.Load(tag) if !ok { return errors.New("查找用户连接失败") } data = dataAny.(socks.ProxyConn) defer func() { s.userConnMap.Delete(tag) utils.Close(data) }() } // 响应用户 user := data.Conn err = socks.SendSuccess(user, client) if err != nil { // todo 考虑是否需要处理服务关闭后导致用户连接被关闭的情况 return errors.Wrap(err, "向用户发送成功消息失败") } // 发送目标地址 dest := data.Dest destLen := len(dest) destBuf := make([]byte, 1+destLen) destBuf[0] = byte(destLen) copy(destBuf[1:], dest) _, err = client.Write(destBuf) if err != nil { return errors.Wrap(err, "向客户端发送目标地址失败") } // 数据转发 slog.Info("开始数据转发 " + client.RemoteAddr().String() + " <-> " + data.Dest) userPipeReader, userPipeWriter := io.Pipe() defer utils.Close(userPipeWriter) teeUser := io.TeeReader(user, userPipeWriter) go func() { err := analysisAndLog(data, userPipeReader) if err != nil { slog.Error("数据解析失败", "err", err) } }() wg := sync.WaitGroup{} wg.Add(1) go func() { defer wg.Done() _, err := io.Copy(client, teeUser) if err != nil { slog.Error("数据转发失败 user->client", "err", err) } }() wg.Add(1) go func() { defer wg.Done() _, err := io.Copy(user, client) if err != nil { if errors.Is(err, net.ErrClosed) { } else { // slog.Error("数据转发失败 client->user", "err", err, "errType", reflect.TypeOf(err)) } } }() wg.Wait() slog.Info("数据转发结束 " + client.RemoteAddr().String() + " <-> " + data.Dest) return nil }