frp/server/service.go
OoyonghongoO 84d34c8298 UPLOAD
2024-10-05 10:25:11 +08:00

763 lines
24 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Copyright 2017 fatedier, fatedier@gmail.com
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package server
import (
"bytes"
"context"
"crypto/tls"
"fmt"
"io"
"io/ioutil"
"net"
"net/http"
"os"
"regexp"
"strconv"
"time"
"github.com/fatedier/frp/extend/api"
"github.com/fatedier/frp/pkg/auth"
v1 "github.com/fatedier/frp/pkg/config/v1"
modelmetrics "github.com/fatedier/frp/pkg/metrics"
"github.com/fatedier/frp/pkg/msg"
"github.com/fatedier/frp/pkg/nathole"
plugin "github.com/fatedier/frp/pkg/plugin/server"
"github.com/fatedier/frp/pkg/ssh"
"github.com/fatedier/frp/pkg/transport"
httppkg "github.com/fatedier/frp/pkg/util/http"
"github.com/fatedier/frp/pkg/util/log"
netpkg "github.com/fatedier/frp/pkg/util/net"
"github.com/fatedier/frp/pkg/util/tcpmux"
"github.com/fatedier/frp/pkg/util/util"
"github.com/fatedier/frp/pkg/util/version"
"github.com/fatedier/frp/pkg/util/vhost"
"github.com/fatedier/frp/pkg/util/xlog"
"github.com/fatedier/frp/server/controller"
"github.com/fatedier/frp/server/group"
"github.com/fatedier/frp/server/metrics"
"github.com/fatedier/frp/server/ports"
"github.com/fatedier/frp/server/proxy"
"github.com/fatedier/frp/server/visitor"
"github.com/fatedier/golib/crypto"
"github.com/fatedier/golib/net/mux"
fmux "github.com/hashicorp/yamux"
quic "github.com/quic-go/quic-go"
"github.com/samber/lo"
)
const (
connReadTimeout time.Duration = 10 * time.Second
vhostReadWriteTimeout time.Duration = 30 * time.Second
)
func init() {
crypto.DefaultSalt = "frp"
// Disable quic-go's receive buffer warning.
os.Setenv("QUIC_GO_DISABLE_RECEIVE_BUFFER_WARNING", "true")
// Disable quic-go's ECN support by default. It may cause issues on certain operating systems.
if os.Getenv("QUIC_GO_DISABLE_ECN") == "" {
os.Setenv("QUIC_GO_DISABLE_ECN", "true")
}
}
// Server service
type Service struct {
// Dispatch connections to different handlers listen on same port
muxer *mux.Mux
// Accept connections from client
listener net.Listener
// Accept connections using kcp
kcpListener net.Listener
// Accept connections using quic
quicListener *quic.Listener
// Accept connections using websocket
websocketListener net.Listener
// Accept frp tls connections
tlsListener net.Listener
// Accept pipe connections from ssh tunnel gateway
sshTunnelListener *netpkg.InternalListener
// Manage all controllers
ctlManager *ControlManager
// Manage all proxies
pxyManager *proxy.Manager
// Manage all plugins
pluginManager *plugin.Manager
// HTTP vhost router
httpVhostRouter *vhost.Routers
// All resource managers and controllers
rc *controller.ResourceController
// web server for dashboard UI and apis
webServer *httppkg.Server
sshTunnelGateway *ssh.Gateway
// Verifies authentication based on selected method
authVerifier auth.Verifier
tlsConfig *tls.Config
cfg *v1.ServerConfig
// service context
ctx context.Context
// call cancel to stop service
cancel context.CancelFunc
ctl *Control
}
func NewService(cfg *v1.ServerConfig) (*Service, error) {
tlsConfig, err := transport.NewServerTLSConfig(
cfg.Transport.TLS.CertFile,
cfg.Transport.TLS.KeyFile,
cfg.Transport.TLS.TrustedCaFile)
if err != nil {
return nil, err
}
var webServer *httppkg.Server
if cfg.WebServer.Port > 0 {
ws, err := httppkg.NewServer(cfg.WebServer)
if err != nil {
return nil, err
}
webServer = ws
modelmetrics.EnableMem()
if cfg.EnablePrometheus {
modelmetrics.EnablePrometheus()
}
}
svr := &Service{
ctlManager: NewControlManager(),
pxyManager: proxy.NewManager(),
pluginManager: plugin.NewManager(),
rc: &controller.ResourceController{
VisitorManager: visitor.NewManager(),
TCPPortManager: ports.NewManager("tcp", cfg.ProxyBindAddr, cfg.AllowPorts),
UDPPortManager: ports.NewManager("udp", cfg.ProxyBindAddr, cfg.AllowPorts),
},
sshTunnelListener: netpkg.NewInternalListener(),
httpVhostRouter: vhost.NewRouters(),
authVerifier: auth.NewAuthVerifier(cfg.Auth),
webServer: webServer,
tlsConfig: tlsConfig,
cfg: cfg,
ctx: context.Background(),
}
if webServer != nil {
webServer.RouteRegister(svr.registerRouteHandlers)
}
// Create tcpmux httpconnect multiplexer.
if cfg.TCPMuxHTTPConnectPort > 0 {
var l net.Listener
address := net.JoinHostPort(cfg.ProxyBindAddr, strconv.Itoa(cfg.TCPMuxHTTPConnectPort))
l, err = net.Listen("tcp", address)
if err != nil {
return nil, fmt.Errorf("[HayFrp] 创建服务器监听时发送错误, %v", err)
}
svr.rc.TCPMuxHTTPConnectMuxer, err = tcpmux.NewHTTPConnectTCPMuxer(l, cfg.TCPMuxPassthrough, vhostReadWriteTimeout)
if err != nil {
return nil, fmt.Errorf("[HayFrp] 创建Vhost TcpMux时错误, %v", err)
}
log.Infof("[HayFrp] TcpMux HttpConnect多路复用器正在 %s 上监听, 通过: %v", address, cfg.TCPMuxPassthrough)
}
// Init all plugins
for _, p := range cfg.HTTPPlugins {
svr.pluginManager.Register(plugin.NewHTTPPluginOptions(p))
log.Infof("[HayFrp] 插件 [%s] 已注册", p.Name)
}
svr.rc.PluginManager = svr.pluginManager
// Init group controller
svr.rc.TCPGroupCtl = group.NewTCPGroupCtl(svr.rc.TCPPortManager)
// Init HTTP group controller
svr.rc.HTTPGroupCtl = group.NewHTTPGroupController(svr.httpVhostRouter)
// Init TCP mux group controller
svr.rc.TCPMuxGroupCtl = group.NewTCPMuxGroupCtl(svr.rc.TCPMuxHTTPConnectMuxer)
// Init 404 not found page
vhost.NotFoundPagePath = cfg.Custom404Page
var (
httpMuxOn bool
httpsMuxOn bool
)
if cfg.BindAddr == cfg.ProxyBindAddr {
if cfg.BindPort == cfg.VhostHTTPPort {
httpMuxOn = true
}
if cfg.BindPort == cfg.VhostHTTPSPort {
httpsMuxOn = true
}
}
// Listen for accepting connections from client.
address := net.JoinHostPort(cfg.BindAddr, strconv.Itoa(cfg.BindPort))
ln, err := net.Listen("tcp", address)
if err != nil {
return nil, fmt.Errorf("[HayFrp] 创建服务监听时发生错误, %v", err)
}
svr.muxer = mux.NewMux(ln)
svr.muxer.SetKeepAlive(time.Duration(cfg.Transport.TCPKeepAlive) * time.Second)
go func() {
_ = svr.muxer.Serve()
}()
ln = svr.muxer.DefaultListener()
svr.listener = ln
log.Infof("[HayFrp] TCP链接端口已开放在 %s", address)
// Listen for accepting connections from client using kcp protocol.
if cfg.KCPBindPort > 0 {
address := net.JoinHostPort(cfg.BindAddr, strconv.Itoa(cfg.KCPBindPort))
svr.kcpListener, err = netpkg.ListenKcp(address)
if err != nil {
return nil, fmt.Errorf("[HayFrp] 开放KCP监听在UDP端口 %s 时发生错误: %v", address, err)
}
log.Infof("[HayFrp] KCP监听已开放在UDP端口 %s", address)
}
if cfg.QUICBindPort > 0 {
address := net.JoinHostPort(cfg.BindAddr, strconv.Itoa(cfg.QUICBindPort))
quicTLSCfg := tlsConfig.Clone()
quicTLSCfg.NextProtos = []string{"frp"}
svr.quicListener, err = quic.ListenAddr(address, quicTLSCfg, &quic.Config{
MaxIdleTimeout: time.Duration(cfg.Transport.QUIC.MaxIdleTimeout) * time.Second,
MaxIncomingStreams: int64(cfg.Transport.QUIC.MaxIncomingStreams),
KeepAlivePeriod: time.Duration(cfg.Transport.QUIC.KeepalivePeriod) * time.Second,
})
if err != nil {
return nil, fmt.Errorf("[HayFrp] 收听QUIC UDP地址 %s 时发生错误: %v", address, err)
}
log.Infof("[HayFrp] QUIC已在监听于 %s", address)
}
if cfg.SSHTunnelGateway.BindPort > 0 {
sshGateway, err := ssh.NewGateway(cfg.SSHTunnelGateway, cfg.ProxyBindAddr, svr.sshTunnelListener)
if err != nil {
return nil, fmt.Errorf("[HayFrp] 创建SSH网关时发生错误: %v", err)
}
svr.sshTunnelGateway = sshGateway
log.Infof("[HayFrp] SSH隧道网关已成功监听于端口 %d", cfg.SSHTunnelGateway.BindPort)
}
// Listen for accepting connections from client using websocket protocol.
websocketPrefix := []byte("GET " + netpkg.FrpWebsocketPath)
websocketLn := svr.muxer.Listen(0, uint32(len(websocketPrefix)), func(data []byte) bool {
return bytes.Equal(data, websocketPrefix)
})
svr.websocketListener = netpkg.NewWebsocketListener(websocketLn)
// Create http vhost muxer.
if cfg.VhostHTTPPort > 0 {
rp := vhost.NewHTTPReverseProxy(vhost.HTTPReverseProxyOptions{
ResponseHeaderTimeoutS: cfg.VhostHTTPTimeout,
}, svr.httpVhostRouter)
svr.rc.HTTPReverseProxy = rp
address := net.JoinHostPort(cfg.ProxyBindAddr, strconv.Itoa(cfg.VhostHTTPPort))
server := &http.Server{
Addr: address,
Handler: rp,
ReadHeaderTimeout: 60 * time.Second,
}
var l net.Listener
if httpMuxOn {
l = svr.muxer.ListenHTTP(1)
} else {
l, err = net.Listen("tcp", address)
if err != nil {
return nil, fmt.Errorf("[HayFrp] 创建Vhost HTTP时发生错误, %v", err)
}
}
go func() {
_ = server.Serve(l)
}()
log.Infof("[HayFrp] HTTP协议已成功监听于端口 %s", address)
}
// Create https vhost muxer.
if cfg.VhostHTTPSPort > 0 {
var l net.Listener
if httpsMuxOn {
l = svr.muxer.ListenHTTPS(1)
} else {
address := net.JoinHostPort(cfg.ProxyBindAddr, strconv.Itoa(cfg.VhostHTTPSPort))
l, err = net.Listen("tcp", address)
if err != nil {
return nil, fmt.Errorf("[HayFrp] 创建服务监听时发生错误, %v", err)
}
log.Infof("[HayFrp] HTTPS协议已成功监听于端口 %s", address)
}
svr.rc.VhostHTTPSMuxer, err = vhost.NewHTTPSMuxer(l, vhostReadWriteTimeout)
if err != nil {
return nil, fmt.Errorf("[HayFrp] 创建Vhost HttpsMuxer时发生错误, %v", err)
}
}
// frp tls listener
svr.tlsListener = svr.muxer.Listen(2, 1, func(data []byte) bool {
// tls first byte can be 0x16 only when vhost https port is not same with bind port
return int(data[0]) == netpkg.FRPTLSHeadByte || int(data[0]) == 0x16
})
// Create nat hole controller.
nc, err := nathole.NewController(time.Duration(cfg.NatHoleAnalysisDataReserveHours) * time.Hour)
if err != nil {
return nil, fmt.Errorf("[HayFrp] 创建NAT打洞控制器错误, %v", err)
}
svr.rc.NatHoleController = nc
return svr, nil
}
func (svr *Service) Run(ctx context.Context) {
ctx, cancel := context.WithCancel(ctx)
svr.ctx = ctx
svr.cancel = cancel
// run dashboard web server.
if svr.webServer != nil {
go func() {
log.Infof("[HayFrp] 仪表盘节点API已监听于端口 %s", svr.webServer.Address())
if err := svr.webServer.Run(); err != nil {
log.Warnf("[HayFrp] 仪表盘节点API已退出: %v", err)
}
}()
}
go svr.HandleListener(svr.sshTunnelListener, true)
if svr.kcpListener != nil {
go svr.HandleListener(svr.kcpListener, false)
}
if svr.quicListener != nil {
go svr.HandleQUICListener(svr.quicListener)
}
go svr.HandleListener(svr.websocketListener, false)
go svr.HandleListener(svr.tlsListener, false)
if svr.rc.NatHoleController != nil {
go svr.rc.NatHoleController.CleanWorker(svr.ctx)
}
if svr.sshTunnelGateway != nil {
go svr.sshTunnelGateway.Run()
}
svr.HandleListener(svr.listener, false)
<-svr.ctx.Done()
// service context may not be canceled by svr.Close(), we should call it here to release resources
if svr.listener != nil {
svr.Close()
}
}
func (svr *Service) Close() error {
if svr.kcpListener != nil {
svr.kcpListener.Close()
svr.kcpListener = nil
}
if svr.quicListener != nil {
svr.quicListener.Close()
svr.quicListener = nil
}
if svr.websocketListener != nil {
svr.websocketListener.Close()
svr.websocketListener = nil
}
if svr.tlsListener != nil {
svr.tlsListener.Close()
svr.tlsConfig = nil
}
if svr.listener != nil {
svr.listener.Close()
svr.listener = nil
}
svr.ctlManager.Close()
if svr.cancel != nil {
svr.cancel()
}
return nil
}
func (svr *Service) handleConnection(ctx context.Context, conn net.Conn, internal bool) {
xl := xlog.FromContextSafe(ctx)
var (
rawMsg msg.Message
err error
)
_ = conn.SetReadDeadline(time.Now().Add(connReadTimeout))
if rawMsg, err = msg.ReadMsg(conn); err != nil {
log.Tracef("[HayFrp] 无法读取信息: %v", err)
conn.Close()
return
}
_ = conn.SetReadDeadline(time.Time{})
switch m := rawMsg.(type) {
case *msg.Login:
// server plugin hook
content := &plugin.LoginContent{
Login: *m,
ClientAddress: conn.RemoteAddr().String(),
}
retContent, err := svr.pluginManager.Login(content)
if err == nil {
m = &retContent.Login
err = svr.RegisterControl(conn, m, internal)
}
// If login failed, send error message there.
// Otherwise send success message in control's work goroutine.
if err != nil {
xl.Warnf("[HayFrp] 注册控制器时发生错误: %v", err)
_ = msg.WriteMsg(conn, &msg.LoginResp{
Version: version.Full(),
Error: util.GenerateResponseErrorString("[HayFrp] 注册控制器时发生错误", err, lo.FromPtr(svr.cfg.DetailedErrorsToClient)),
})
conn.Close()
}
case *msg.NewWorkConn:
if err := svr.RegisterWorkConn(conn, m); err != nil {
conn.Close()
}
case *msg.NewVisitorConn:
if err = svr.RegisterVisitorConn(conn, m); err != nil {
xl.Warnf("[HayFrp] 注册访问者连接错误: %v", err)
_ = msg.WriteMsg(conn, &msg.NewVisitorConnResp{
ProxyName: m.ProxyName,
Error: util.GenerateResponseErrorString("[HayFrp] 注册访问者连接错误", err, lo.FromPtr(svr.cfg.DetailedErrorsToClient)),
})
conn.Close()
} else {
_ = msg.WriteMsg(conn, &msg.NewVisitorConnResp{
ProxyName: m.ProxyName,
Error: "",
})
}
default:
log.Warnf("[HayFrp] 新连接 [%s] 发送错误的消息类型", conn.RemoteAddr().String())
conn.Close()
}
}
// HandleListener accepts connections from client and call handleConnection to handle them.
// If internal is true, it means that this listener is used for internal communication like ssh tunnel gateway.
// TODO(fatedier): Pass some parameters of listener/connection through context to avoid passing too many parameters.
func (svr *Service) HandleListener(l net.Listener, internal bool) {
// Listen for incoming connections from client.
for {
c, err := l.Accept()
if err != nil {
log.Warnf("[HayFrp] 客户端传入连接的侦听器已关闭")
return
}
// inject xlog object into net.Conn context
xl := xlog.New()
ctx := context.Background()
c = netpkg.NewContextConn(xlog.NewContext(ctx, xl), c)
if !internal {
log.Tracef("[HayFrp] 开始检查TLS链接...")
originConn := c
forceTLS := svr.cfg.Transport.TLS.Force
var isTLS, custom bool
c, isTLS, custom, err = netpkg.CheckAndEnableTLSServerConnWithTimeout(c, svr.tlsConfig, forceTLS, connReadTimeout)
if err != nil {
log.Warnf("[HayFrp] 检查与启用TLS服务器链接与超时 发生错误: %v", err)
originConn.Close()
continue
}
log.Tracef("[HayFrp] 检查TLS链接成功, 是TLS: %v 自定义: %v 内部: %v", isTLS, custom, internal)
}
// Start a new goroutine to handle connection.
go func(ctx context.Context, frpConn net.Conn) {
if lo.FromPtr(svr.cfg.Transport.TCPMux) && !internal {
fmuxCfg := fmux.DefaultConfig()
fmuxCfg.KeepAliveInterval = time.Duration(svr.cfg.Transport.TCPMuxKeepaliveInterval) * time.Second
fmuxCfg.LogOutput = io.Discard
fmuxCfg.MaxStreamWindowSize = 6 * 1024 * 1024
session, err := fmux.Server(frpConn, fmuxCfg)
if err != nil {
log.Warnf("[HayFrp] 创建多路复用器连接失败: %v", err)
frpConn.Close()
return
}
for {
stream, err := session.AcceptStream()
if err != nil {
log.Debugf("[HayFrp] 接受新的多路复用流错误: %v", err)
session.Close()
return
}
go svr.handleConnection(ctx, stream, internal)
}
} else {
svr.handleConnection(ctx, frpConn, internal)
}
}(ctx, c)
}
}
func (svr *Service) HandleQUICListener(l *quic.Listener) {
// Listen for incoming connections from client.
for {
c, err := l.Accept(context.Background())
if err != nil {
log.Warnf("[HayFrp] 客户端传入连接的QUIC监听已关闭")
return
}
// Start a new goroutine to handle connection.
go func(ctx context.Context, frpConn quic.Connection) {
for {
stream, err := frpConn.AcceptStream(context.Background())
if err != nil {
log.Debugf("[HayFrp] 接受新的QUIC多路复用流错误: %v", err)
_ = frpConn.CloseWithError(0, "")
return
}
go svr.handleConnection(ctx, netpkg.QuicStreamToNetConn(stream, frpConn), false)
}
}(context.Background(), c)
}
}
func (svr *Service) RegisterControl(ctlConn net.Conn, loginMsg *msg.Login, internal bool) error {
// If client's RunID is empty, it's a new client, we just create a new controller.
// Otherwise, we check if there is one controller has the same run id. If so, we release previous controller and start new one.
var err error
// 请求一堆API
if loginMsg.RunID == "" {
randid, err := util.RandID()
if err != nil {
return err
}
serverTime := time.Now()
greeting := ""
hour := serverTime.Hour()
if hour >= 0 && hour < 6 {
greeting = "午夜好,又在修仙呢......"
} else if hour >= 6 && hour < 9 {
greeting = "早上好,美好的一天开始了~"
} else if hour >= 9 && hour < 12 {
greeting = "中午好,饱饱地睡一觉~"
} else if hour >= 12 && hour < 18 {
greeting = "下午好,运动运动,活动筋骨~"
} else if hour >= 18 && hour < 24 {
greeting = "晚上好,别修仙了~"
}
t := time.Now()
layout := "2006-01-02 15:04:05.888"
str := t.Format(layout)
loginMsg.RunID = "HayFrpToken" + loginMsg.User + "," + "ConnectToken" + randid + "] Getting data from HayFrp API......" + "\n" + str + " [I] [server/hayfrp.go:102] [HayFrp] " + greeting + "\n" + str + " [I] [server/hayfrp.go:145] [HayFrp] 已将HayFrp终端介入客户端Frpc!" + "\n" + str + " [W] [server/hayfrp.go:187] [HayFrp] 友情提示若需要分享错误日志请为Token打码否则可能导致信息泄露!" + "\n" + str + " [I] [server/hayfrp.go:245] [HayFrp] 当前服务器准时: " + serverTime.String() + "\n" + str + " [I] [server/hayfrp.go:199] [HayFrp] 您已成功连接至HayFrp云服务" + "\n" + str + " [W] [server/hayfrp.go:425] [HayFrp] 检测到您正在" + loginMsg.Os + "-" + loginMsg.Arch + "系统下运行客户端" + "\n" + str + " [W] [server/hayfrp.go:665] [HayFrp] 检测到您的客户端版本为" + loginMsg.Version + "\n"
// 发起 GET 请求获取 API 返回的内容(API服务状态查询)
resp, err := http.Get("https://api.hayfrp.org/")
if err != nil {
return err
}
defer resp.Body.Close()
// 读取 API 返回的内容
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
// 将 API 返回的内容添加到 loginMsg.RunID 后面
loginMsg.RunID += str + " [I] [api/hayfrp.go:423] [HayFrp] " + string(body) + "\n"
// 发起 GET 请求获取 API 返回的内容(今日启动获取服务)
resp, err = http.Get("https://api.hayfrp.org/NodeAPI?type=userlogin&utoken=" + loginMsg.User + "&token=" + svr.cfg.ApiToken + "&system=" + loginMsg.Os + "&ver=" + loginMsg.Version + "&arch=" + loginMsg.Arch)
if err != nil {
return err
}
defer resp.Body.Close()
// 读取 API 返回的内容
body, err = ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
// 将 API 返回的内容添加到 loginMsg.RunII 后面
loginMsg.RunID += str + " [I] [hayfrp.go:507] [HayFrp] " + string(body) + "\n" + str + " [I] [root.go:490] [HayFrp"
}
ctx := netpkg.NewContextFromConn(ctlConn)
xl := xlog.FromContextSafe(ctx)
xl.AppendPrefix(loginMsg.RunID)
ctx = xlog.NewContext(ctx, xl)
xl.Infof("[HayFrp] 客户端登录信息: IP地址 [%s] 客户端版本 [%s] 主机名 [%s] 系统 [%s] 架构 [%s]",
ctlConn.RemoteAddr().String(), loginMsg.Version, loginMsg.Hostname, loginMsg.Os, loginMsg.Arch)
// Check auth.
authVerifier := svr.authVerifier
if internal && loginMsg.ClientSpec.AlwaysAuthPass {
authVerifier = auth.AlwaysPassVerifier
}
if err := authVerifier.VerifyLogin(loginMsg); err != nil {
return err
}
var (
inLimit uint64
outLimit uint64
)
if svr.cfg.EnableApi {
nowTime := time.Now().Unix()
s, err := api.NewService(svr.cfg.ApiBaseUrl)
if err != nil {
return err
}
r := regexp.MustCompile(`^[A-Za-z0-9]{1,32}$`)
mm := r.FindAllStringSubmatch(loginMsg.User, -1)
if len(mm) < 1 {
return fmt.Errorf("[HayFrp] 未知用户名")
}
// Connect to API server and verify the user.
valid, err := s.CheckToken(loginMsg.User, loginMsg.PrivilegeKey, nowTime, svr.cfg.ApiToken)
if err != nil {
return err
}
if !valid {
return fmt.Errorf("[HayFrp] 认证失败")
}
inLimit, outLimit, err = s.GetProxyLimit(loginMsg.User, nowTime, svr.cfg.ApiToken)
if err != nil {
return err
}
xl.Debugf("[HayFrp] %s 客户端速率: %dKB/s (上行) / %dKB/s (下行)", loginMsg.User, inLimit, outLimit)
}
// TODO(fatedier): use SessionContext
ctl, err := NewControl(ctx, svr.rc, svr.pxyManager, svr.pluginManager, authVerifier, ctlConn, !internal, loginMsg, svr.cfg)
if err != nil {
xl.Warnf("[HayFrp] 创建新控制器错误: %v", err)
// don't return detailed errors to client
return fmt.Errorf("[HayFrp] 创建新控制器时出现意外错误")
}
if oldCtl := svr.ctlManager.Add(loginMsg.RunID, ctl); oldCtl != nil {
oldCtl.WaitClosed()
}
ctl.Start()
// for statistics
metrics.Server.NewClient()
go func() {
// block until control closed
ctl.WaitClosed()
svr.ctlManager.Del(loginMsg.RunID, ctl)
}()
return nil
}
// RegisterWorkConn register a new work connection to control and proxies need it.
func (svr *Service) RegisterWorkConn(workConn net.Conn, newMsg *msg.NewWorkConn) error {
xl := netpkg.NewLogFromConn(workConn)
ctl, exist := svr.ctlManager.GetByID(newMsg.RunID)
if !exist {
xl.Warnf("[HayFrp] 未找到运行ID [%s] 的客户端控件", newMsg.RunID)
return fmt.Errorf("[HayFrp] 未找到运行ID [%s] 的客户端控件", newMsg.RunID)
}
// server plugin hook
content := &plugin.NewWorkConnContent{
User: plugin.UserInfo{
User: ctl.loginMsg.User,
Metas: ctl.loginMsg.Metas,
RunID: ctl.loginMsg.RunID,
},
NewWorkConn: *newMsg,
}
retContent, err := svr.pluginManager.NewWorkConn(content)
if err == nil {
newMsg = &retContent.NewWorkConn
// Check auth.
err = ctl.authVerifier.VerifyNewWorkConn(newMsg)
}
if err != nil {
xl.Warnf("[HayFrp] 运行ID为 [%s] 的新活动链接无效", newMsg.RunID)
_ = msg.WriteMsg(workConn, &msg.StartWorkConn{
Error: util.GenerateResponseErrorString("[HayFrp] 无效 新活动链接", err, lo.FromPtr(svr.cfg.DetailedErrorsToClient)),
})
return fmt.Errorf("[HayFrp] 运行ID为 [%s] 的新活动链接无效", newMsg.RunID)
}
return ctl.RegisterWorkConn(workConn)
}
func (svr *Service) RegisterVisitorConn(visitorConn net.Conn, newMsg *msg.NewVisitorConn) error {
visitorUser := ""
// TODO(deprecation): Compatible with old versions, can be without runID, user is empty. In later versions, it will be mandatory to include runID.
// If runID is required, it is not compatible with versions prior to v0.50.0.
if newMsg.RunID != "" {
ctl, exist := svr.ctlManager.GetByID(newMsg.RunID)
if !exist {
return fmt.Errorf("[HayFrp] 未找到运行ID为 [%s] 的客户端控件", newMsg.RunID)
}
visitorUser = ctl.loginMsg.User
}
return svr.rc.VisitorManager.NewConn(newMsg.ProxyName, visitorConn, newMsg.Timestamp, newMsg.SignKey,
newMsg.UseEncryption, newMsg.UseCompression, visitorUser)
}
func (svr *Service) CloseUser(user string) error {
ctl, ok := svr.ctlManager.SearchByID(user)
if !ok {
return fmt.Errorf("[HayFrp] 客户端用户没有登录")
}
ctl.conn.Close()
return nil
}