mirror of
https://gitee.com/IrisVega/frp.git
synced 2024-11-01 22:31:29 +08:00
8b432e179d
* feat: frps support ssh * fix: comments * fix: update pkg * fix: remove useless change --------- Co-authored-by: int7 <int7@gmail.com>
186 lines
3.9 KiB
Go
186 lines
3.9 KiB
Go
package ssh
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net"
|
|
"sync/atomic"
|
|
"time"
|
|
|
|
"golang.org/x/crypto/ssh"
|
|
|
|
"github.com/fatedier/frp/pkg/config"
|
|
v1 "github.com/fatedier/frp/pkg/config/v1"
|
|
"github.com/fatedier/frp/pkg/msg"
|
|
plugin "github.com/fatedier/frp/pkg/plugin/server"
|
|
"github.com/fatedier/frp/pkg/util/log"
|
|
frp_net "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/server/controller"
|
|
"github.com/fatedier/frp/server/proxy"
|
|
)
|
|
|
|
// VirtualService is a client VirtualService run in frps
|
|
type VirtualService struct {
|
|
clientCfg v1.ClientCommonConfig
|
|
pxyCfg v1.ProxyConfigurer
|
|
serverCfg v1.ServerConfig
|
|
|
|
sshSvc *Service
|
|
|
|
// uniq id got from frps, attach it in loginMsg
|
|
runID string
|
|
loginMsg *msg.Login
|
|
|
|
// All resource managers and controllers
|
|
rc *controller.ResourceController
|
|
|
|
exit uint32 // 0 means not exit
|
|
// SSHService context
|
|
ctx context.Context
|
|
// call cancel to stop SSHService
|
|
cancel context.CancelFunc
|
|
|
|
replyCh chan interface{}
|
|
pxy proxy.Proxy
|
|
}
|
|
|
|
func NewVirtualService(
|
|
ctx context.Context,
|
|
clientCfg v1.ClientCommonConfig,
|
|
serverCfg v1.ServerConfig,
|
|
logMsg msg.Login,
|
|
rc *controller.ResourceController,
|
|
pxyCfg v1.ProxyConfigurer,
|
|
sshSvc *Service,
|
|
replyCh chan interface{},
|
|
) (svr *VirtualService, err error) {
|
|
svr = &VirtualService{
|
|
clientCfg: clientCfg,
|
|
serverCfg: serverCfg,
|
|
rc: rc,
|
|
|
|
loginMsg: &logMsg,
|
|
|
|
sshSvc: sshSvc,
|
|
pxyCfg: pxyCfg,
|
|
|
|
ctx: ctx,
|
|
exit: 0,
|
|
|
|
replyCh: replyCh,
|
|
}
|
|
|
|
svr.runID, err = util.RandID()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
go svr.loopCheck()
|
|
|
|
return
|
|
}
|
|
|
|
func (svr *VirtualService) Run(ctx context.Context) (err error) {
|
|
ctx, cancel := context.WithCancel(ctx)
|
|
svr.ctx = xlog.NewContext(ctx, xlog.New())
|
|
svr.cancel = cancel
|
|
|
|
remoteAddr, err := svr.RegisterProxy(&msg.NewProxy{
|
|
ProxyName: svr.pxyCfg.(*v1.TCPProxyConfig).Name,
|
|
ProxyType: svr.pxyCfg.(*v1.TCPProxyConfig).Type,
|
|
RemotePort: svr.pxyCfg.(*v1.TCPProxyConfig).RemotePort,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
log.Info("run a reverse proxy on port: %v", remoteAddr)
|
|
|
|
return nil
|
|
}
|
|
|
|
func (svr *VirtualService) Close() {
|
|
svr.GracefulClose(time.Duration(0))
|
|
}
|
|
|
|
func (svr *VirtualService) GracefulClose(d time.Duration) {
|
|
atomic.StoreUint32(&svr.exit, 1)
|
|
svr.pxy.Close()
|
|
|
|
if svr.cancel != nil {
|
|
svr.cancel()
|
|
}
|
|
|
|
svr.replyCh <- &VProxyError{}
|
|
}
|
|
|
|
func (svr *VirtualService) loopCheck() {
|
|
<-svr.sshSvc.Exit()
|
|
svr.pxy.Close()
|
|
log.Info("virtual client service close")
|
|
}
|
|
|
|
func (svr *VirtualService) RegisterProxy(pxyMsg *msg.NewProxy) (remoteAddr string, err error) {
|
|
var pxyConf v1.ProxyConfigurer
|
|
pxyConf, err = config.NewProxyConfigurerFromMsg(pxyMsg, &svr.serverCfg)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
// User info
|
|
userInfo := plugin.UserInfo{
|
|
User: svr.loginMsg.User,
|
|
Metas: svr.loginMsg.Metas,
|
|
RunID: svr.runID,
|
|
}
|
|
|
|
svr.pxy, err = proxy.NewProxy(svr.ctx, &proxy.Options{
|
|
LoginMsg: svr.loginMsg,
|
|
UserInfo: userInfo,
|
|
Configurer: pxyConf,
|
|
ResourceController: svr.rc,
|
|
|
|
GetWorkConnFn: svr.GetWorkConn,
|
|
PoolCount: 10,
|
|
|
|
ServerCfg: &svr.serverCfg,
|
|
})
|
|
if err != nil {
|
|
return remoteAddr, err
|
|
}
|
|
|
|
remoteAddr, err = svr.pxy.Run()
|
|
if err != nil {
|
|
log.Warn("proxy run error: %v", err)
|
|
return
|
|
}
|
|
|
|
defer func() {
|
|
if err != nil {
|
|
log.Warn("proxy close")
|
|
svr.pxy.Close()
|
|
}
|
|
}()
|
|
|
|
return
|
|
}
|
|
|
|
func (svr *VirtualService) GetWorkConn() (workConn net.Conn, err error) {
|
|
// tell ssh client open a new stream for work
|
|
payload := forwardedTCPPayload{
|
|
Addr: svr.serverCfg.BindAddr, // TODO refine
|
|
Port: uint32(svr.pxyCfg.(*v1.TCPProxyConfig).RemotePort),
|
|
}
|
|
|
|
channel, reqs, err := svr.sshSvc.SSHConn().OpenChannel(ChannelTypeServerOpenChannel, ssh.Marshal(payload))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("open ssh channel error: %v", err)
|
|
}
|
|
go ssh.DiscardRequests(reqs)
|
|
|
|
workConn = frp_net.WrapReadWriteCloserToConn(channel, svr.sshSvc.tcpConn)
|
|
return workConn, nil
|
|
}
|