optimize some code (#3801)

This commit is contained in:
fatedier
2023-11-27 15:47:49 +08:00
committed by GitHub
parent d5b41f1e14
commit 69ae2b0b69
52 changed files with 880 additions and 600 deletions

31
pkg/auth/pass.go Normal file
View File

@@ -0,0 +1,31 @@
// Copyright 2023 The frp Authors
//
// 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 auth
import (
"github.com/fatedier/frp/pkg/msg"
)
var AlwaysPassVerifier = &alwaysPass{}
var _ Verifier = &alwaysPass{}
type alwaysPass struct{}
func (*alwaysPass) VerifyLogin(*msg.Login) error { return nil }
func (*alwaysPass) VerifyPing(*msg.Ping) error { return nil }
func (*alwaysPass) VerifyNewWorkConn(*msg.NewWorkConn) error { return nil }

View File

@@ -59,12 +59,17 @@ func RegisterProxyFlags(cmd *cobra.Command, c v1.ProxyConfigurer) {
case *v1.TCPMuxProxyConfig:
registerProxyDomainConfigFlags(cmd, &cc.DomainConfig)
cmd.Flags().StringVarP(&cc.Multiplexer, "mux", "", "", "multiplexer")
cmd.Flags().StringVarP(&cc.HTTPUser, "http_user", "", "", "http auth user")
cmd.Flags().StringVarP(&cc.HTTPPassword, "http_pwd", "", "", "http auth password")
case *v1.STCPProxyConfig:
cmd.Flags().StringVarP(&cc.Secretkey, "sk", "", "", "secret key")
cmd.Flags().StringSliceVarP(&cc.AllowUsers, "allow_users", "", []string{}, "allow visitor users")
case *v1.SUDPProxyConfig:
cmd.Flags().StringVarP(&cc.Secretkey, "sk", "", "", "secret key")
cmd.Flags().StringSliceVarP(&cc.AllowUsers, "allow_users", "", []string{}, "allow visitor users")
case *v1.XTCPProxyConfig:
cmd.Flags().StringVarP(&cc.Secretkey, "sk", "", "", "secret key")
cmd.Flags().StringSliceVarP(&cc.AllowUsers, "allow_users", "", []string{}, "allow visitor users")
}
}

View File

@@ -23,7 +23,7 @@ import (
func ParseClientConfig(filePath string) (
cfg ClientCommonConf,
pxyCfgs map[string]ProxyConf,
proxyCfgs map[string]ProxyConf,
visitorCfgs map[string]VisitorConf,
err error,
) {
@@ -56,7 +56,7 @@ func ParseClientConfig(filePath string) (
configBuffer.Write(buf)
// Parse all proxy and visitor configs.
pxyCfgs, visitorCfgs, err = LoadAllProxyConfsFromIni(cfg.User, configBuffer.Bytes(), cfg.Start)
proxyCfgs, visitorCfgs, err = LoadAllProxyConfsFromIni(cfg.User, configBuffer.Bytes(), cfg.Start)
if err != nil {
return
}

View File

@@ -110,6 +110,7 @@ func LoadConfigureFromFile(path string, c any, strict bool) error {
// LoadConfigure loads configuration from bytes and unmarshal into c.
// Now it supports json, yaml and toml format.
// TODO(fatedier): strict is not valide for ProxyConfigurer/VisitorConfigurer/ClientPluginOptions.
func LoadConfigure(b []byte, c any, strict bool) error {
var tomlObj interface{}
// Try to unmarshal as TOML first; swallow errors from that (assume it's not valid TOML).
@@ -188,19 +189,19 @@ func LoadClientConfig(path string, strict bool) (
) {
var (
cliCfg *v1.ClientCommonConfig
pxyCfgs = make([]v1.ProxyConfigurer, 0)
proxyCfgs = make([]v1.ProxyConfigurer, 0)
visitorCfgs = make([]v1.VisitorConfigurer, 0)
isLegacyFormat bool
)
if DetectLegacyINIFormatFromFile(path) {
legacyCommon, legacyPxyCfgs, legacyVisitorCfgs, err := legacy.ParseClientConfig(path)
legacyCommon, legacyProxyCfgs, legacyVisitorCfgs, err := legacy.ParseClientConfig(path)
if err != nil {
return nil, nil, nil, true, err
}
cliCfg = legacy.Convert_ClientCommonConf_To_v1(&legacyCommon)
for _, c := range legacyPxyCfgs {
pxyCfgs = append(pxyCfgs, legacy.Convert_ProxyConf_To_v1(c))
for _, c := range legacyProxyCfgs {
proxyCfgs = append(proxyCfgs, legacy.Convert_ProxyConf_To_v1(c))
}
for _, c := range legacyVisitorCfgs {
visitorCfgs = append(visitorCfgs, legacy.Convert_VisitorConf_To_v1(c))
@@ -213,7 +214,7 @@ func LoadClientConfig(path string, strict bool) (
}
cliCfg = &allCfg.ClientCommonConfig
for _, c := range allCfg.Proxies {
pxyCfgs = append(pxyCfgs, c.ProxyConfigurer)
proxyCfgs = append(proxyCfgs, c.ProxyConfigurer)
}
for _, c := range allCfg.Visitors {
visitorCfgs = append(visitorCfgs, c.VisitorConfigurer)
@@ -223,18 +224,18 @@ func LoadClientConfig(path string, strict bool) (
// Load additional config from includes.
// legacy ini format already handle this in ParseClientConfig.
if len(cliCfg.IncludeConfigFiles) > 0 && !isLegacyFormat {
extPxyCfgs, extVisitorCfgs, err := LoadAdditionalClientConfigs(cliCfg.IncludeConfigFiles, isLegacyFormat, strict)
extProxyCfgs, extVisitorCfgs, err := LoadAdditionalClientConfigs(cliCfg.IncludeConfigFiles, isLegacyFormat, strict)
if err != nil {
return nil, nil, nil, isLegacyFormat, err
}
pxyCfgs = append(pxyCfgs, extPxyCfgs...)
proxyCfgs = append(proxyCfgs, extProxyCfgs...)
visitorCfgs = append(visitorCfgs, extVisitorCfgs...)
}
// Filter by start
if len(cliCfg.Start) > 0 {
startSet := sets.New(cliCfg.Start...)
pxyCfgs = lo.Filter(pxyCfgs, func(c v1.ProxyConfigurer, _ int) bool {
proxyCfgs = lo.Filter(proxyCfgs, func(c v1.ProxyConfigurer, _ int) bool {
return startSet.Has(c.GetBaseConfig().Name)
})
visitorCfgs = lo.Filter(visitorCfgs, func(c v1.VisitorConfigurer, _ int) bool {
@@ -245,17 +246,17 @@ func LoadClientConfig(path string, strict bool) (
if cliCfg != nil {
cliCfg.Complete()
}
for _, c := range pxyCfgs {
for _, c := range proxyCfgs {
c.Complete(cliCfg.User)
}
for _, c := range visitorCfgs {
c.Complete(cliCfg)
}
return cliCfg, pxyCfgs, visitorCfgs, isLegacyFormat, nil
return cliCfg, proxyCfgs, visitorCfgs, isLegacyFormat, nil
}
func LoadAdditionalClientConfigs(paths []string, isLegacyFormat bool, strict bool) ([]v1.ProxyConfigurer, []v1.VisitorConfigurer, error) {
pxyCfgs := make([]v1.ProxyConfigurer, 0)
proxyCfgs := make([]v1.ProxyConfigurer, 0)
visitorCfgs := make([]v1.VisitorConfigurer, 0)
for _, path := range paths {
absDir, err := filepath.Abs(filepath.Dir(path))
@@ -281,7 +282,7 @@ func LoadAdditionalClientConfigs(paths []string, isLegacyFormat bool, strict boo
return nil, nil, fmt.Errorf("load additional config from %s error: %v", absFile, err)
}
for _, c := range cfg.Proxies {
pxyCfgs = append(pxyCfgs, c.ProxyConfigurer)
proxyCfgs = append(proxyCfgs, c.ProxyConfigurer)
}
for _, c := range cfg.Visitors {
visitorCfgs = append(visitorCfgs, c.VisitorConfigurer)
@@ -289,5 +290,5 @@ func LoadAdditionalClientConfigs(paths []string, isLegacyFormat bool, strict boo
}
}
}
return pxyCfgs, visitorCfgs, nil
return proxyCfgs, visitorCfgs, nil
}

View File

@@ -224,7 +224,9 @@ func NewProxyConfigurerByType(proxyType ProxyType) ProxyConfigurer {
if !ok {
return nil
}
return reflect.New(v).Interface().(ProxyConfigurer)
pc := reflect.New(v).Interface().(ProxyConfigurer)
pc.GetBaseConfig().Type = string(proxyType)
return pc
}
var _ ProxyConfigurer = &TCPProxyConfig{}

View File

@@ -80,7 +80,7 @@ func ValidateClientCommonConfig(c *v1.ClientCommonConfig) (Warning, error) {
return warnings, errs
}
func ValidateAllClientConfig(c *v1.ClientCommonConfig, pxyCfgs []v1.ProxyConfigurer, visitorCfgs []v1.VisitorConfigurer) (Warning, error) {
func ValidateAllClientConfig(c *v1.ClientCommonConfig, proxyCfgs []v1.ProxyConfigurer, visitorCfgs []v1.VisitorConfigurer) (Warning, error) {
var warnings Warning
if c != nil {
warning, err := ValidateClientCommonConfig(c)
@@ -90,7 +90,7 @@ func ValidateAllClientConfig(c *v1.ClientCommonConfig, pxyCfgs []v1.ProxyConfigu
}
}
for _, c := range pxyCfgs {
for _, c := range proxyCfgs {
if err := ValidateProxyConfigurerForClient(c); err != nil {
return warnings, fmt.Errorf("proxy %s: %v", c.GetBaseConfig().Name, err)
}

View File

@@ -63,6 +63,15 @@ var msgTypeMap = map[byte]interface{}{
var TypeNameNatHoleResp = reflect.TypeOf(&NatHoleResp{}).Elem().Name()
type ClientSpec struct {
// Due to the support of VirtualClient, frps needs to know the client type in order to
// differentiate the processing logic.
// Optional values: ssh-tunnel
Type string `json:"type,omitempty"`
// If the value is true, the client will not require authentication.
AlwaysAuthPass bool `json:"always_auth_pass,omitempty"`
}
// When frpc start, client send this message to login to server.
type Login struct {
Version string `json:"version,omitempty"`
@@ -75,6 +84,9 @@ type Login struct {
RunID string `json:"run_id,omitempty"`
Metas map[string]string `json:"metas,omitempty"`
// Currently only effective for VirtualClient.
ClientSpec ClientSpec `json:"client_spec,omitempty"`
// Some global configures.
PoolCount int `json:"pool_count,omitempty"`
}

View File

@@ -24,7 +24,7 @@ import (
"net/http/httputil"
v1 "github.com/fatedier/frp/pkg/config/v1"
utilnet "github.com/fatedier/frp/pkg/util/net"
netpkg "github.com/fatedier/frp/pkg/util/net"
)
func init() {
@@ -79,7 +79,7 @@ func NewHTTP2HTTPSPlugin(options v1.ClientPluginOptions) (Plugin, error) {
}
func (p *HTTP2HTTPSPlugin) Handle(conn io.ReadWriteCloser, realConn net.Conn, _ *ExtraInfo) {
wrapConn := utilnet.WrapReadWriteCloserToConn(conn, realConn)
wrapConn := netpkg.WrapReadWriteCloserToConn(conn, realConn)
_ = p.l.PutConn(wrapConn)
}

View File

@@ -29,7 +29,7 @@ import (
libnet "github.com/fatedier/golib/net"
v1 "github.com/fatedier/frp/pkg/config/v1"
utilnet "github.com/fatedier/frp/pkg/util/net"
netpkg "github.com/fatedier/frp/pkg/util/net"
"github.com/fatedier/frp/pkg/util/util"
)
@@ -68,7 +68,7 @@ func (hp *HTTPProxy) Name() string {
}
func (hp *HTTPProxy) Handle(conn io.ReadWriteCloser, realConn net.Conn, _ *ExtraInfo) {
wrapConn := utilnet.WrapReadWriteCloserToConn(conn, realConn)
wrapConn := netpkg.WrapReadWriteCloserToConn(conn, realConn)
sc, rd := libnet.NewSharedConn(wrapConn)
firstBytes := make([]byte, 7)

View File

@@ -26,7 +26,7 @@ import (
v1 "github.com/fatedier/frp/pkg/config/v1"
"github.com/fatedier/frp/pkg/transport"
utilnet "github.com/fatedier/frp/pkg/util/net"
netpkg "github.com/fatedier/frp/pkg/util/net"
)
func init() {
@@ -98,7 +98,7 @@ func (p *HTTPS2HTTPPlugin) genTLSConfig() (*tls.Config, error) {
}
func (p *HTTPS2HTTPPlugin) Handle(conn io.ReadWriteCloser, realConn net.Conn, _ *ExtraInfo) {
wrapConn := utilnet.WrapReadWriteCloserToConn(conn, realConn)
wrapConn := netpkg.WrapReadWriteCloserToConn(conn, realConn)
_ = p.l.PutConn(wrapConn)
}

View File

@@ -26,7 +26,7 @@ import (
v1 "github.com/fatedier/frp/pkg/config/v1"
"github.com/fatedier/frp/pkg/transport"
utilnet "github.com/fatedier/frp/pkg/util/net"
netpkg "github.com/fatedier/frp/pkg/util/net"
)
func init() {
@@ -104,7 +104,7 @@ func (p *HTTPS2HTTPSPlugin) genTLSConfig() (*tls.Config, error) {
}
func (p *HTTPS2HTTPSPlugin) Handle(conn io.ReadWriteCloser, realConn net.Conn, _ *ExtraInfo) {
wrapConn := utilnet.WrapReadWriteCloserToConn(conn, realConn)
wrapConn := netpkg.WrapReadWriteCloserToConn(conn, realConn)
_ = p.l.PutConn(wrapConn)
}

View File

@@ -24,7 +24,7 @@ import (
gosocks5 "github.com/armon/go-socks5"
v1 "github.com/fatedier/frp/pkg/config/v1"
utilnet "github.com/fatedier/frp/pkg/util/net"
netpkg "github.com/fatedier/frp/pkg/util/net"
)
func init() {
@@ -52,7 +52,7 @@ func NewSocks5Plugin(options v1.ClientPluginOptions) (p Plugin, err error) {
func (sp *Socks5Plugin) Handle(conn io.ReadWriteCloser, realConn net.Conn, _ *ExtraInfo) {
defer conn.Close()
wrapConn := utilnet.WrapReadWriteCloserToConn(conn, realConn)
wrapConn := netpkg.WrapReadWriteCloserToConn(conn, realConn)
_ = sp.Server.ServeConn(wrapConn)
}

View File

@@ -25,7 +25,7 @@ import (
"github.com/gorilla/mux"
v1 "github.com/fatedier/frp/pkg/config/v1"
utilnet "github.com/fatedier/frp/pkg/util/net"
netpkg "github.com/fatedier/frp/pkg/util/net"
)
func init() {
@@ -57,8 +57,8 @@ func NewStaticFilePlugin(options v1.ClientPluginOptions) (Plugin, error) {
}
router := mux.NewRouter()
router.Use(utilnet.NewHTTPAuthMiddleware(opts.HTTPUser, opts.HTTPPassword).SetAuthFailDelay(200 * time.Millisecond).Middleware)
router.PathPrefix(prefix).Handler(utilnet.MakeHTTPGzipHandler(http.StripPrefix(prefix, http.FileServer(http.Dir(opts.LocalPath))))).Methods("GET")
router.Use(netpkg.NewHTTPAuthMiddleware(opts.HTTPUser, opts.HTTPPassword).SetAuthFailDelay(200 * time.Millisecond).Middleware)
router.PathPrefix(prefix).Handler(netpkg.MakeHTTPGzipHandler(http.StripPrefix(prefix, http.FileServer(http.Dir(opts.LocalPath))))).Methods("GET")
sp.s = &http.Server{
Handler: router,
}
@@ -69,7 +69,7 @@ func NewStaticFilePlugin(options v1.ClientPluginOptions) (Plugin, error) {
}
func (sp *StaticFilePlugin) Handle(conn io.ReadWriteCloser, realConn net.Conn, _ *ExtraInfo) {
wrapConn := utilnet.WrapReadWriteCloserToConn(conn, realConn)
wrapConn := netpkg.WrapReadWriteCloserToConn(conn, realConn)
_ = sp.l.PutConn(wrapConn)
}

View File

@@ -11,7 +11,7 @@ import (
"strings"
"github.com/fatedier/frp/client"
"github.com/fatedier/frp/pkg/util/util"
httppkg "github.com/fatedier/frp/pkg/util/http"
)
type Client struct {
@@ -115,7 +115,7 @@ func (c *Client) UpdateConfig(content string) error {
func (c *Client) setAuthHeader(req *http.Request) {
if c.authUser != "" || c.authPwd != "" {
req.Header.Set("Authorization", util.BasicAuth(c.authUser, c.authPwd))
req.Header.Set("Authorization", httppkg.BasicAuth(c.authUser, c.authPwd))
}
}

View File

@@ -26,21 +26,21 @@ import (
v1 "github.com/fatedier/frp/pkg/config/v1"
"github.com/fatedier/frp/pkg/transport"
"github.com/fatedier/frp/pkg/util/log"
utilnet "github.com/fatedier/frp/pkg/util/net"
netpkg "github.com/fatedier/frp/pkg/util/net"
)
type Gateway struct {
bindPort int
ln net.Listener
serverPeerListener *utilnet.InternalListener
peerServerListener *netpkg.InternalListener
sshConfig *ssh.ServerConfig
}
func NewGateway(
cfg v1.SSHTunnelGateway, bindAddr string,
serverPeerListener *utilnet.InternalListener,
peerServerListener *netpkg.InternalListener,
) (*Gateway, error) {
sshConfig := &ssh.ServerConfig{}
@@ -71,15 +71,8 @@ func NewGateway(
}
sshConfig.AddHostKey(privateKey)
sshConfig.NoClientAuth = cfg.AuthorizedKeysFile == ""
sshConfig.PublicKeyCallback = func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) {
if cfg.AuthorizedKeysFile == "" {
return &ssh.Permissions{
Extensions: map[string]string{
"user": "",
},
}, nil
}
authorizedKeysMap, err := loadAuthorizedKeysFromFile(cfg.AuthorizedKeysFile)
if err != nil {
return nil, fmt.Errorf("internal error")
@@ -103,7 +96,7 @@ func NewGateway(
return &Gateway{
bindPort: cfg.BindPort,
ln: ln,
serverPeerListener: serverPeerListener,
peerServerListener: peerServerListener,
sshConfig: sshConfig,
}, nil
}
@@ -121,7 +114,7 @@ func (g *Gateway) Run() {
func (g *Gateway) handleConn(conn net.Conn) {
defer conn.Close()
ts, err := NewTunnelServer(conn, g.sshConfig, g.serverPeerListener)
ts, err := NewTunnelServer(conn, g.sshConfig, g.peerServerListener)
if err != nil {
return
}

View File

@@ -17,9 +17,11 @@ package ssh
import (
"context"
"encoding/binary"
"errors"
"fmt"
"net"
"strings"
"sync"
"time"
libio "github.com/fatedier/golib/io"
@@ -27,10 +29,12 @@ import (
"github.com/spf13/cobra"
"golang.org/x/crypto/ssh"
"github.com/fatedier/frp/client/proxy"
"github.com/fatedier/frp/pkg/config"
v1 "github.com/fatedier/frp/pkg/config/v1"
"github.com/fatedier/frp/pkg/msg"
utilnet "github.com/fatedier/frp/pkg/util/net"
"github.com/fatedier/frp/pkg/util/log"
netpkg "github.com/fatedier/frp/pkg/util/net"
"github.com/fatedier/frp/pkg/util/util"
"github.com/fatedier/frp/pkg/util/xlog"
"github.com/fatedier/frp/pkg/virtual"
@@ -64,15 +68,16 @@ type TunnelServer struct {
sc *ssh.ServerConfig
vc *virtual.Client
serverPeerListener *utilnet.InternalListener
peerServerListener *netpkg.InternalListener
doneCh chan struct{}
closeDoneChOnce sync.Once
}
func NewTunnelServer(conn net.Conn, sc *ssh.ServerConfig, serverPeerListener *utilnet.InternalListener) (*TunnelServer, error) {
func NewTunnelServer(conn net.Conn, sc *ssh.ServerConfig, peerServerListener *netpkg.InternalListener) (*TunnelServer, error) {
s := &TunnelServer{
underlyingConn: conn,
sc: sc,
serverPeerListener: serverPeerListener,
peerServerListener: peerServerListener,
doneCh: make(chan struct{}),
}
return s, nil
@@ -94,19 +99,35 @@ func (s *TunnelServer) Run() error {
if err != nil {
return err
}
clientCfg.User = util.EmptyOr(sshConn.Permissions.Extensions["user"], clientCfg.User)
clientCfg.Complete()
if sshConn.Permissions != nil {
clientCfg.User = util.EmptyOr(sshConn.Permissions.Extensions["user"], clientCfg.User)
}
pc.Complete(clientCfg.User)
s.vc = virtual.NewClient(clientCfg)
// join workConn and ssh channel
s.vc.SetInWorkConnCallback(func(base *v1.ProxyBaseConfig, workConn net.Conn, m *msg.StartWorkConn) bool {
c, err := s.openConn(addr)
if err != nil {
vc, err := virtual.NewClient(virtual.ClientOptions{
Common: clientCfg,
Spec: &msg.ClientSpec{
Type: "ssh-tunnel",
// If ssh does not require authentication, then the virtual client needs to authenticate through a token.
// Otherwise, once ssh authentication is passed, the virtual client does not need to authenticate again.
AlwaysAuthPass: !s.sc.NoClientAuth,
},
HandleWorkConnCb: func(base *v1.ProxyBaseConfig, workConn net.Conn, m *msg.StartWorkConn) bool {
// join workConn and ssh channel
c, err := s.openConn(addr)
if err != nil {
return false
}
libio.Join(c, workConn)
return false
}
libio.Join(c, workConn)
return false
},
})
if err != nil {
return err
}
s.vc = vc
// transfer connection from virtual client to server peer listener
go func() {
l := s.vc.PeerListener()
@@ -115,21 +136,35 @@ func (s *TunnelServer) Run() error {
if err != nil {
return
}
_ = s.serverPeerListener.PutConn(conn)
_ = s.peerServerListener.PutConn(conn)
}
}()
xl := xlog.New().AddPrefix(xlog.LogPrefix{Name: "sshVirtualClient", Value: "sshVirtualClient", Priority: 100})
ctx := xlog.NewContext(context.Background(), xl)
go func() {
_ = s.vc.Run(ctx)
// If vc.Run returns, it means that the virtual client has been closed, and the ssh tunnel connection should be closed.
// One scenario is that the virtual client exits due to login failure.
s.closeDoneChOnce.Do(func() {
_ = sshConn.Close()
close(s.doneCh)
})
}()
s.vc.UpdateProxyConfigurer([]v1.ProxyConfigurer{pc})
_ = sshConn.Wait()
_ = sshConn.Close()
if err := s.waitProxyStatusReady(pc.GetBaseConfig().Name, time.Second); err != nil {
log.Warn("wait proxy status ready error: %v", err)
} else {
_ = sshConn.Wait()
}
s.vc.Close()
close(s.doneCh)
log.Trace("ssh tunnel connection from %v closed", sshConn.RemoteAddr())
s.closeDoneChOnce.Do(func() {
_ = sshConn.Close()
close(s.doneCh)
})
return nil
}
@@ -217,6 +252,14 @@ func (s *TunnelServer) parseClientAndProxyConfigurer(_ *tcpipForward, extraPaylo
if err := cmd.ParseFlags(args); err != nil {
return nil, nil, fmt.Errorf("parse flags from ssh client error: %v", err)
}
// if name is not set, generate a random one
if pc.GetBaseConfig().Name == "" {
id, err := util.RandIDWithLen(8)
if err != nil {
return nil, nil, fmt.Errorf("generate random id error: %v", err)
}
pc.GetBaseConfig().Name = fmt.Sprintf("sshtunnel-%s-%s", proxyType, id)
}
return &clientCfg, pc, nil
}
@@ -274,6 +317,34 @@ func (s *TunnelServer) openConn(addr *tcpipForward) (net.Conn, error) {
}
go ssh.DiscardRequests(reqs)
conn := utilnet.WrapReadWriteCloserToConn(channel, s.underlyingConn)
conn := netpkg.WrapReadWriteCloserToConn(channel, s.underlyingConn)
return conn, nil
}
func (s *TunnelServer) waitProxyStatusReady(name string, timeout time.Duration) error {
ticker := time.NewTicker(100 * time.Millisecond)
defer ticker.Stop()
timer := time.NewTimer(timeout)
defer timer.Stop()
for {
select {
case <-ticker.C:
ps, err := s.vc.Service().GetProxyStatus(name)
if err != nil {
continue
}
switch ps.Phase {
case proxy.ProxyPhaseRunning:
return nil
case proxy.ProxyPhaseStartErr, proxy.ProxyPhaseClosed:
return errors.New(ps.Err)
}
case <-timer.C:
return fmt.Errorf("wait proxy status ready timeout")
case <-s.doneCh:
return fmt.Errorf("ssh tunnel server closed")
}
}
}

View File

@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package util
package http
import (
"encoding/base64"

128
pkg/util/http/server.go Normal file
View File

@@ -0,0 +1,128 @@
// Copyright 2023 The frp Authors
//
// 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 http
import (
"crypto/tls"
"net"
"net/http"
"net/http/pprof"
"strconv"
"time"
"github.com/gorilla/mux"
"github.com/fatedier/frp/assets"
v1 "github.com/fatedier/frp/pkg/config/v1"
netpkg "github.com/fatedier/frp/pkg/util/net"
)
var (
defaultReadTimeout = 60 * time.Second
defaultWriteTimeout = 60 * time.Second
)
type Server struct {
addr string
ln net.Listener
tlsCfg *tls.Config
router *mux.Router
hs *http.Server
authMiddleware mux.MiddlewareFunc
}
func NewServer(cfg v1.WebServerConfig) (*Server, error) {
if cfg.AssetsDir != "" {
assets.Load(cfg.AssetsDir)
}
addr := net.JoinHostPort(cfg.Addr, strconv.Itoa(cfg.Port))
if addr == ":" {
addr = ":http"
}
ln, err := net.Listen("tcp", addr)
if err != nil {
return nil, err
}
router := mux.NewRouter()
hs := &http.Server{
Addr: addr,
Handler: router,
ReadTimeout: defaultReadTimeout,
WriteTimeout: defaultWriteTimeout,
}
s := &Server{
addr: addr,
ln: ln,
hs: hs,
router: router,
}
if cfg.PprofEnable {
s.registerPprofHandlers()
}
if cfg.TLS != nil {
cert, err := tls.LoadX509KeyPair(cfg.TLS.CertFile, cfg.TLS.KeyFile)
if err != nil {
return nil, err
}
s.tlsCfg = &tls.Config{
Certificates: []tls.Certificate{cert},
}
}
s.authMiddleware = netpkg.NewHTTPAuthMiddleware(cfg.User, cfg.Password).SetAuthFailDelay(200 * time.Millisecond).Middleware
return s, nil
}
func (s *Server) Address() string {
return s.addr
}
func (s *Server) Run() error {
ln := s.ln
if s.tlsCfg != nil {
ln = tls.NewListener(ln, s.tlsCfg)
}
return s.hs.Serve(ln)
}
func (s *Server) Close() error {
return s.hs.Close()
}
type RouterRegisterHelper struct {
Router *mux.Router
AssetsFS http.FileSystem
AuthMiddleware mux.MiddlewareFunc
}
func (s *Server) RouteRegister(register func(helper *RouterRegisterHelper)) {
register(&RouterRegisterHelper{
Router: s.router,
AssetsFS: assets.FileSystem,
AuthMiddleware: s.authMiddleware,
})
}
func (s *Server) registerPprofHandlers() {
s.router.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
s.router.HandleFunc("/debug/pprof/profile", pprof.Profile)
s.router.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
s.router.HandleFunc("/debug/pprof/trace", pprof.Trace)
s.router.PathPrefix("/debug/pprof/").HandlerFunc(pprof.Index)
}

33
pkg/util/net/dns.go Normal file
View File

@@ -0,0 +1,33 @@
// Copyright 2023 The frp Authors
//
// 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 net
import (
"context"
"net"
)
func SetDefaultDNSAddress(dnsAddress string) {
if _, _, err := net.SplitHostPort(dnsAddress); err != nil {
dnsAddress = net.JoinHostPort(dnsAddress, "53")
}
// Change default dns server
net.DefaultResolver = &net.Resolver{
PreferGo: true,
Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
return net.Dial("udp", dnsAddress)
},
}
}

View File

@@ -52,7 +52,10 @@ func (l *InternalListener) PutConn(conn net.Conn) error {
conn.Close()
}
})
return err
if err != nil {
return fmt.Errorf("put conn error: listener is closed")
}
return nil
}
func (l *InternalListener) Close() error {

View File

@@ -24,7 +24,7 @@ import (
libnet "github.com/fatedier/golib/net"
"github.com/fatedier/frp/pkg/util/util"
httppkg "github.com/fatedier/frp/pkg/util/http"
"github.com/fatedier/frp/pkg/util/vhost"
)
@@ -59,10 +59,10 @@ func (muxer *HTTPConnectTCPMuxer) readHTTPConnectRequest(rd io.Reader) (host, ht
return
}
host, _ = util.CanonicalHost(req.Host)
host, _ = httppkg.CanonicalHost(req.Host)
proxyAuth := req.Header.Get("Proxy-Authorization")
if proxyAuth != "" {
httpUser, httpPwd, _ = util.ParseBasicAuth(proxyAuth)
httpUser, httpPwd, _ = httppkg.ParseBasicAuth(proxyAuth)
}
return
}
@@ -71,7 +71,7 @@ func (muxer *HTTPConnectTCPMuxer) sendConnectResponse(c net.Conn, _ map[string]s
if muxer.passthrough {
return nil
}
res := util.OkResponse()
res := httppkg.OkResponse()
if res.Body != nil {
defer res.Body.Close()
}
@@ -85,7 +85,7 @@ func (muxer *HTTPConnectTCPMuxer) auth(c net.Conn, username, password string, re
return true, nil
}
resp := util.ProxyUnauthorizedResponse()
resp := httppkg.ProxyUnauthorizedResponse()
if resp.Body != nil {
defer resp.Body.Close()
}

View File

@@ -31,8 +31,8 @@ import (
libio "github.com/fatedier/golib/io"
"github.com/fatedier/golib/pool"
frpLog "github.com/fatedier/frp/pkg/util/log"
"github.com/fatedier/frp/pkg/util/util"
httppkg "github.com/fatedier/frp/pkg/util/http"
logpkg "github.com/fatedier/frp/pkg/util/log"
)
var ErrNoRouteFound = errors.New("no route found")
@@ -61,7 +61,7 @@ func NewHTTPReverseProxy(option HTTPReverseProxyOptions, vhostRouter *Routers) *
Director: func(req *http.Request) {
req.URL.Scheme = "http"
reqRouteInfo := req.Context().Value(RouteInfoKey).(*RequestRouteInfo)
oldHost, _ := util.CanonicalHost(reqRouteInfo.Host)
oldHost, _ := httppkg.CanonicalHost(reqRouteInfo.Host)
rc := rp.GetRouteConfig(oldHost, reqRouteInfo.URL, reqRouteInfo.HTTPUser)
if rc != nil {
@@ -74,7 +74,7 @@ func NewHTTPReverseProxy(option HTTPReverseProxyOptions, vhostRouter *Routers) *
// ignore error here, it will use CreateConnFn instead later
endpoint, _ = rc.ChooseEndpointFn()
reqRouteInfo.Endpoint = endpoint
frpLog.Trace("choose endpoint name [%s] for http request host [%s] path [%s] httpuser [%s]",
logpkg.Trace("choose endpoint name [%s] for http request host [%s] path [%s] httpuser [%s]",
endpoint, oldHost, reqRouteInfo.URL, reqRouteInfo.HTTPUser)
}
// Set {domain}.{location}.{routeByHTTPUser}.{endpoint} as URL host here to let http transport reuse connections.
@@ -116,7 +116,7 @@ func NewHTTPReverseProxy(option HTTPReverseProxyOptions, vhostRouter *Routers) *
BufferPool: newWrapPool(),
ErrorLog: log.New(newWrapLogger(), "", 0),
ErrorHandler: func(rw http.ResponseWriter, req *http.Request, err error) {
frpLog.Warn("do http proxy request [host: %s] error: %v", req.Host, err)
logpkg.Warn("do http proxy request [host: %s] error: %v", req.Host, err)
rw.WriteHeader(http.StatusNotFound)
_, _ = rw.Write(getNotFoundPageContent())
},
@@ -143,7 +143,7 @@ func (rp *HTTPReverseProxy) UnRegister(routeCfg RouteConfig) {
func (rp *HTTPReverseProxy) GetRouteConfig(domain, location, routeByHTTPUser string) *RouteConfig {
vr, ok := rp.getVhost(domain, location, routeByHTTPUser)
if ok {
frpLog.Debug("get new HTTP request host [%s] path [%s] httpuser [%s]", domain, location, routeByHTTPUser)
logpkg.Debug("get new HTTP request host [%s] path [%s] httpuser [%s]", domain, location, routeByHTTPUser)
return vr.payload.(*RouteConfig)
}
return nil
@@ -159,7 +159,7 @@ func (rp *HTTPReverseProxy) GetHeaders(domain, location, routeByHTTPUser string)
// CreateConnection create a new connection by route config
func (rp *HTTPReverseProxy) CreateConnection(reqRouteInfo *RequestRouteInfo, byEndpoint bool) (net.Conn, error) {
host, _ := util.CanonicalHost(reqRouteInfo.Host)
host, _ := httppkg.CanonicalHost(reqRouteInfo.Host)
vr, ok := rp.getVhost(host, reqRouteInfo.URL, reqRouteInfo.HTTPUser)
if ok {
if byEndpoint {
@@ -303,7 +303,7 @@ func (rp *HTTPReverseProxy) injectRequestInfoToCtx(req *http.Request) *http.Requ
}
func (rp *HTTPReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
domain, _ := util.CanonicalHost(req.Host)
domain, _ := httppkg.CanonicalHost(req.Host)
location := req.URL.Path
user, passwd, _ := req.BasicAuth()
if !rp.CheckAuth(domain, location, user, user, passwd) {
@@ -333,6 +333,6 @@ type wrapLogger struct{}
func newWrapLogger() *wrapLogger { return &wrapLogger{} }
func (l *wrapLogger) Write(p []byte) (n int, err error) {
frpLog.Warn("%s", string(bytes.TrimRight(p, "\n")))
logpkg.Warn("%s", string(bytes.TrimRight(p, "\n")))
return len(p), nil
}

View File

@@ -20,7 +20,7 @@ import (
"net/http"
"os"
frpLog "github.com/fatedier/frp/pkg/util/log"
logpkg "github.com/fatedier/frp/pkg/util/log"
"github.com/fatedier/frp/pkg/util/version"
)
@@ -58,7 +58,7 @@ func getNotFoundPageContent() []byte {
if NotFoundPagePath != "" {
buf, err = os.ReadFile(NotFoundPagePath)
if err != nil {
frpLog.Warn("read custom 404 page error: %v", err)
logpkg.Warn("read custom 404 page error: %v", err)
buf = []byte(NotFound)
}
} else {

View File

@@ -22,7 +22,7 @@ import (
"github.com/fatedier/golib/errors"
"github.com/fatedier/frp/pkg/util/log"
utilnet "github.com/fatedier/frp/pkg/util/net"
netpkg "github.com/fatedier/frp/pkg/util/net"
"github.com/fatedier/frp/pkg/util/xlog"
)
@@ -284,7 +284,7 @@ func (l *Listener) Accept() (net.Conn, error) {
xl.Debug("rewrite host to [%s] success", l.rewriteHost)
conn = sConn
}
return utilnet.NewContextConn(l.ctx, conn), nil
return netpkg.NewContextConn(l.ctx, conn), nil
}
func (l *Listener) Close() error {

View File

@@ -21,55 +21,70 @@ import (
"github.com/fatedier/frp/client"
v1 "github.com/fatedier/frp/pkg/config/v1"
"github.com/fatedier/frp/pkg/msg"
utilnet "github.com/fatedier/frp/pkg/util/net"
netpkg "github.com/fatedier/frp/pkg/util/net"
)
type ClientOptions struct {
Common *v1.ClientCommonConfig
Spec *msg.ClientSpec
HandleWorkConnCb func(*v1.ProxyBaseConfig, net.Conn, *msg.StartWorkConn) bool
}
type Client struct {
l *utilnet.InternalListener
l *netpkg.InternalListener
svr *client.Service
}
func NewClient(cfg *v1.ClientCommonConfig) *Client {
cfg.Complete()
func NewClient(options ClientOptions) (*Client, error) {
if options.Common != nil {
options.Common.Complete()
}
ln := utilnet.NewInternalListener()
svr := client.NewService(cfg, nil, nil, "")
svr.SetConnectorCreator(func(context.Context, *v1.ClientCommonConfig) client.Connector {
return &pipeConnector{
peerListener: ln,
}
})
ln := netpkg.NewInternalListener()
serviceOptions := client.ServiceOptions{
Common: options.Common,
ClientSpec: options.Spec,
ConnectorCreator: func(context.Context, *v1.ClientCommonConfig) client.Connector {
return &pipeConnector{
peerListener: ln,
}
},
HandleWorkConnCb: options.HandleWorkConnCb,
}
svr, err := client.NewService(serviceOptions)
if err != nil {
return nil, err
}
return &Client{
l: ln,
svr: svr,
}
}, nil
}
func (c *Client) PeerListener() net.Listener {
return c.l
}
func (c *Client) SetInWorkConnCallback(cb func(*v1.ProxyBaseConfig, net.Conn, *msg.StartWorkConn) bool) {
c.svr.SetInWorkConnCallback(cb)
}
func (c *Client) UpdateProxyConfigurer(proxyCfgs []v1.ProxyConfigurer) {
_ = c.svr.ReloadConf(proxyCfgs, nil)
_ = c.svr.UpdateAllConfigurer(proxyCfgs, nil)
}
func (c *Client) Run(ctx context.Context) error {
return c.svr.Run(ctx)
}
func (c *Client) Service() *client.Service {
return c.svr
}
func (c *Client) Close() {
c.l.Close()
c.svr.Close()
c.l.Close()
}
type pipeConnector struct {
peerListener *utilnet.InternalListener
peerListener *netpkg.InternalListener
}
func (pc *pipeConnector) Open() error {