forked from Mxmilu666/frp
Merge remote-tracking branch 'upstream/dev' into dev
# Conflicts: # .github/workflows/build-and-push-image.yml # cmd/frpc/sub/verify.go # go.mod # go.sum # pkg/util/version/version.go
This commit is contained in:
+108
-44
@@ -18,6 +18,7 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
@@ -51,7 +52,6 @@ import (
|
||||
"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/registry"
|
||||
@@ -64,6 +64,8 @@ const (
|
||||
vhostReadWriteTimeout time.Duration = 30 * time.Second
|
||||
)
|
||||
|
||||
var errControlReplaced = errors.New("control was replaced during login")
|
||||
|
||||
func init() {
|
||||
crypto.DefaultSalt = "frp"
|
||||
// Disable quic-go's receive buffer warning.
|
||||
@@ -161,9 +163,10 @@ func NewService(cfg *v1.ServerConfig) (*Service, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
clientRegistry := registry.NewClientRegistry()
|
||||
svr := &Service{
|
||||
ctlManager: NewControlManager(),
|
||||
clientRegistry: registry.NewClientRegistry(),
|
||||
ctlManager: NewControlManager(clientRegistry),
|
||||
clientRegistry: clientRegistry,
|
||||
pxyManager: proxy.NewManager(),
|
||||
pluginManager: plugin.NewManager(),
|
||||
rc: &controller.ResourceController{
|
||||
@@ -303,10 +306,14 @@ func NewService(cfg *v1.ServerConfig) (*Service, error) {
|
||||
svr.rc.HTTPReverseProxy = rp
|
||||
|
||||
address := net.JoinHostPort(cfg.ProxyBindAddr, strconv.Itoa(cfg.VhostHTTPPort))
|
||||
protocols := new(http.Protocols)
|
||||
protocols.SetHTTP1(true)
|
||||
protocols.SetUnencryptedHTTP2(true)
|
||||
server := &http.Server{
|
||||
Addr: address,
|
||||
Handler: rp,
|
||||
ReadHeaderTimeout: 60 * time.Second,
|
||||
Protocols: protocols,
|
||||
}
|
||||
var l net.Listener
|
||||
if httpMuxOn {
|
||||
@@ -469,12 +476,15 @@ func (svr *Service) handleConnection(ctx context.Context, conn net.Conn, interna
|
||||
}
|
||||
}
|
||||
if err == nil {
|
||||
ctl, err = svr.RegisterControl(controlConn, m, internal, acceptedConn.wireProtocol)
|
||||
ctl, err = svr.RegisterControl(controlConn, m, internal, acceptedConn.wireProtocol, acceptedConn.udpPacketCodec)
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
xl.Warnf("register control error: %v", err)
|
||||
if ctl != nil {
|
||||
svr.ctlManager.Remove(ctl)
|
||||
}
|
||||
if writeErr := writeWithDeadline(conn, connWriteTimeout, func() error {
|
||||
return acceptedConn.conn.WriteMsg(&msg.LoginResp{
|
||||
Version: version.Full(),
|
||||
@@ -483,31 +493,34 @@ func (svr *Service) handleConnection(ctx context.Context, conn net.Conn, interna
|
||||
}); writeErr != nil {
|
||||
xl.Warnf("write login error response error: %v", writeErr)
|
||||
}
|
||||
conn.Close()
|
||||
if ctl != nil {
|
||||
_ = ctl.Close()
|
||||
} else {
|
||||
conn.Close()
|
||||
}
|
||||
return
|
||||
}
|
||||
if err = writeWithDeadline(conn, connWriteTimeout, func() error {
|
||||
return acceptedConn.conn.WriteMsg(&msg.LoginResp{
|
||||
Version: version.Full(),
|
||||
RunID: ctl.runID,
|
||||
Error: "",
|
||||
if err = svr.completeControlLogin(ctl, func() error {
|
||||
return writeWithDeadline(conn, connWriteTimeout, func() error {
|
||||
return acceptedConn.conn.WriteMsg(&msg.LoginResp{
|
||||
Version: version.Full(),
|
||||
RunID: ctl.runID,
|
||||
Error: "",
|
||||
})
|
||||
})
|
||||
}); err != nil {
|
||||
xl.Warnf("write login response error: %v", err)
|
||||
svr.ctlManager.Del(m.RunID, ctl)
|
||||
svr.clientRegistry.MarkOfflineByRunID(m.RunID)
|
||||
conn.Close()
|
||||
xl.Warnf("complete control login error: %v", err)
|
||||
svr.ctlManager.Remove(ctl)
|
||||
_ = ctl.Close()
|
||||
return
|
||||
}
|
||||
ctl.Start()
|
||||
metrics.Server.NewClient()
|
||||
go func() {
|
||||
// block until control closed
|
||||
ctl.WaitClosed()
|
||||
svr.ctlManager.Del(m.RunID, ctl)
|
||||
}()
|
||||
case *msg.NewWorkConn:
|
||||
if err := svr.RegisterWorkConn(acceptedConn.conn, m); err != nil {
|
||||
if err := svr.RegisterWorkConn(
|
||||
acceptedConn.conn,
|
||||
m,
|
||||
acceptedConn.wireProtocol,
|
||||
acceptedConn.clientHelloPresent,
|
||||
); err != nil {
|
||||
_ = acceptedConn.conn.WriteMsg(&msg.StartWorkConn{
|
||||
Error: util.GenerateResponseErrorString("invalid NewWorkConn", err, lo.FromPtr(svr.cfg.DetailedErrorsToClient)),
|
||||
})
|
||||
@@ -533,11 +546,24 @@ func (svr *Service) handleConnection(ctx context.Context, conn net.Conn, interna
|
||||
}
|
||||
}
|
||||
|
||||
func (svr *Service) completeControlLogin(ctl *Control, writeSuccess func() error) error {
|
||||
committed, err := svr.ctlManager.completeLogin(ctl, writeSuccess)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !committed {
|
||||
return errControlReplaced
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type acceptedConnection struct {
|
||||
conn *msg.Conn
|
||||
wireProtocol string
|
||||
cryptoContext *wire.CryptoContext
|
||||
firstMsg msg.Message
|
||||
conn *msg.Conn
|
||||
wireProtocol string
|
||||
clientHelloPresent bool
|
||||
udpPacketCodec string
|
||||
cryptoContext *wire.CryptoContext
|
||||
firstMsg msg.Message
|
||||
}
|
||||
|
||||
func (svr *Service) acceptConnection(ctx context.Context, conn net.Conn) (*acceptedConnection, error) {
|
||||
@@ -605,6 +631,7 @@ func (ac *acceptedConnection) readFirstV2Msg(conn net.Conn, wireConn *wire.Conn)
|
||||
return nil, fmt.Errorf("read v2 frame: %w", err)
|
||||
}
|
||||
if frame.Type == wire.FrameTypeClientHello {
|
||||
ac.clientHelloPresent = true
|
||||
if err := ac.handleClientHello(conn, wireConn, frame); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -653,6 +680,7 @@ func (ac *acceptedConnection) handleClientHello(conn net.Conn, wireConn *wire.Co
|
||||
return fmt.Errorf("write ServerHello: %w", err)
|
||||
}
|
||||
ac.cryptoContext = cryptoContext
|
||||
ac.udpPacketCodec = serverHello.Selected.Message.UDPPacketCodec
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -746,7 +774,20 @@ func (svr *Service) RegisterControl(
|
||||
loginMsg *msg.Login,
|
||||
internal bool,
|
||||
wireProtocol string,
|
||||
udpPacketCodec string,
|
||||
) (*Control, error) {
|
||||
switch wireProtocol {
|
||||
case wire.ProtocolV1:
|
||||
if udpPacketCodec != "" {
|
||||
return nil, fmt.Errorf("UDP packet codec %q requires wire protocol v2", udpPacketCodec)
|
||||
}
|
||||
case wire.ProtocolV2:
|
||||
if udpPacketCodec != "" && udpPacketCodec != wire.UDPPacketCodecBinary {
|
||||
return nil, fmt.Errorf("unsupported UDP packet codec selection: %s", udpPacketCodec)
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported wire protocol: %s", wireProtocol)
|
||||
}
|
||||
// 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
|
||||
@@ -782,8 +823,8 @@ func (svr *Service) RegisterControl(
|
||||
Conn: ctlConn,
|
||||
LoginMsg: loginMsg,
|
||||
ServerCfg: svr.cfg,
|
||||
ClientRegistry: svr.clientRegistry,
|
||||
WireProtocol: wireProtocol,
|
||||
UDPPacketCodec: udpPacketCodec,
|
||||
})
|
||||
if err != nil {
|
||||
xl.Warnf("create new controller error: %v", err)
|
||||
@@ -791,31 +832,41 @@ func (svr *Service) RegisterControl(
|
||||
return nil, fmt.Errorf("unexpected error when creating new controller")
|
||||
}
|
||||
|
||||
if oldCtl := svr.ctlManager.Add(loginMsg.RunID, ctl); oldCtl != nil {
|
||||
oldCtl.WaitClosed()
|
||||
if err := svr.ctlManager.Add(ctl); err != nil {
|
||||
return ctl, err
|
||||
}
|
||||
ctl.WaitForHandoff()
|
||||
|
||||
remoteAddr := ctlConn.RemoteAddr().String()
|
||||
if host, _, err := net.SplitHostPort(remoteAddr); err == nil {
|
||||
remoteAddr = host
|
||||
active, err := svr.ctlManager.Activate(ctl)
|
||||
if err != nil {
|
||||
return ctl, err
|
||||
}
|
||||
_, conflict := svr.clientRegistry.Register(loginMsg.User, loginMsg.ClientID, loginMsg.RunID, loginMsg.Hostname, loginMsg.Version, remoteAddr, wireProtocol)
|
||||
if conflict {
|
||||
svr.ctlManager.Del(loginMsg.RunID, ctl)
|
||||
return nil, fmt.Errorf("client_id [%s] for user [%s] is already online", loginMsg.ClientID, loginMsg.User)
|
||||
if !active {
|
||||
return ctl, errControlReplaced
|
||||
}
|
||||
|
||||
return ctl, nil
|
||||
}
|
||||
|
||||
// RegisterWorkConn register a new work connection to control and proxies need it.
|
||||
func (svr *Service) RegisterWorkConn(workConn *msg.Conn, newMsg *msg.NewWorkConn) error {
|
||||
func (svr *Service) RegisterWorkConn(
|
||||
workConn *msg.Conn,
|
||||
newMsg *msg.NewWorkConn,
|
||||
workWireProtocol string,
|
||||
workClientHelloPresent bool,
|
||||
) error {
|
||||
if workClientHelloPresent {
|
||||
return fmt.Errorf("ClientHello is not allowed on work connections")
|
||||
}
|
||||
xl := netpkg.NewLogFromConn(workConn)
|
||||
ctl, exist := svr.ctlManager.GetByID(newMsg.RunID)
|
||||
if !exist {
|
||||
xl.Warnf("no client control found for run id [%s]", newMsg.RunID)
|
||||
return fmt.Errorf("no client control found for run id [%s]", newMsg.RunID)
|
||||
}
|
||||
if workWireProtocol != ctl.sessionCtx.WireProtocol {
|
||||
return fmt.Errorf("work connection wire protocol mismatch: got %s want %s", workWireProtocol, ctl.sessionCtx.WireProtocol)
|
||||
}
|
||||
|
||||
// server plugin hook
|
||||
content := &plugin.NewWorkConnContent{
|
||||
@@ -836,20 +887,33 @@ func (svr *Service) RegisterWorkConn(workConn *msg.Conn, newMsg *msg.NewWorkConn
|
||||
xl.Warnf("invalid NewWorkConn with run id [%s]", newMsg.RunID)
|
||||
return err
|
||||
}
|
||||
return ctl.RegisterWorkConn(proxy.NewWorkConn(workConn))
|
||||
return svr.ctlManager.RegisterWorkConn(ctl, proxy.NewWorkConn(workConn))
|
||||
}
|
||||
|
||||
func (svr *Service) RegisterVisitorConn(visitorConn net.Conn, newMsg *msg.NewVisitorConn, wireProtocol string) error {
|
||||
visitorUser := ""
|
||||
admit := func(visitorUser, visitorWireProtocol, visitorUDPPacketCodec string) error {
|
||||
if visitorWireProtocol == "" {
|
||||
visitorWireProtocol = wireProtocol
|
||||
}
|
||||
return svr.rc.VisitorManager.NewConn(newMsg.ProxyName, visitorConn, newMsg.Timestamp, newMsg.SignKey,
|
||||
newMsg.UseEncryption, newMsg.UseCompression, visitorUser, visitorWireProtocol, visitorUDPPacketCodec)
|
||||
}
|
||||
// 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 {
|
||||
admitted, err := svr.ctlManager.admitVisitorByRunID(newMsg.RunID, func(visitorUser, controlWireProtocol, controlUDPPacketCodec string) error {
|
||||
if wireProtocol != controlWireProtocol {
|
||||
return fmt.Errorf("visitor connection wire protocol mismatch: got %s want %s", wireProtocol, controlWireProtocol)
|
||||
}
|
||||
return admit(visitorUser, controlWireProtocol, controlUDPPacketCodec)
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !admitted {
|
||||
return fmt.Errorf("no client control found for run id [%s]", newMsg.RunID)
|
||||
}
|
||||
visitorUser = ctl.sessionCtx.LoginMsg.User
|
||||
return nil
|
||||
}
|
||||
return svr.rc.VisitorManager.NewConn(newMsg.ProxyName, visitorConn, newMsg.Timestamp, newMsg.SignKey,
|
||||
newMsg.UseEncryption, newMsg.UseCompression, visitorUser, wireProtocol)
|
||||
return admit("", wireProtocol, "")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user