mirror of
https://github.com/fatedier/frp.git
synced 2025-01-22 17:42:09 +00:00
support tcp stream multiplexing by smux
This commit is contained in:
parent
a5f06489cb
commit
b600a07ec0
@ -28,6 +28,7 @@ import (
|
|||||||
"github.com/fatedier/frp/utils/net"
|
"github.com/fatedier/frp/utils/net"
|
||||||
"github.com/fatedier/frp/utils/util"
|
"github.com/fatedier/frp/utils/util"
|
||||||
"github.com/fatedier/frp/utils/version"
|
"github.com/fatedier/frp/utils/version"
|
||||||
|
"github.com/xtaci/smux"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -50,6 +51,9 @@ type Control struct {
|
|||||||
// control connection
|
// control connection
|
||||||
conn net.Conn
|
conn net.Conn
|
||||||
|
|
||||||
|
// tcp stream multiplexing, if enabled
|
||||||
|
session *smux.Session
|
||||||
|
|
||||||
// put a message in this channel to send it over control connection to server
|
// put a message in this channel to send it over control connection to server
|
||||||
sendCh chan (msg.Message)
|
sendCh chan (msg.Message)
|
||||||
|
|
||||||
@ -122,11 +126,25 @@ func (ctl *Control) Run() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (ctl *Control) NewWorkConn() {
|
func (ctl *Control) NewWorkConn() {
|
||||||
workConn, err := net.ConnectTcpServerByHttpProxy(config.ClientCommonCfg.HttpProxy,
|
var (
|
||||||
fmt.Sprintf("%s:%d", config.ClientCommonCfg.ServerAddr, config.ClientCommonCfg.ServerPort))
|
workConn net.Conn
|
||||||
if err != nil {
|
err error
|
||||||
ctl.Warn("start new work connection error: %v", err)
|
)
|
||||||
return
|
if config.ClientCommonCfg.TcpMux {
|
||||||
|
stream, err := ctl.session.OpenStream()
|
||||||
|
if err != nil {
|
||||||
|
ctl.Warn("start new work connection error: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
workConn = net.WrapConn(stream)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
workConn, err = net.ConnectTcpServerByHttpProxy(config.ClientCommonCfg.HttpProxy,
|
||||||
|
fmt.Sprintf("%s:%d", config.ClientCommonCfg.ServerAddr, config.ClientCommonCfg.ServerPort))
|
||||||
|
if err != nil {
|
||||||
|
ctl.Warn("start new work connection error: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m := &msg.NewWorkConn{
|
m := &msg.NewWorkConn{
|
||||||
@ -166,6 +184,10 @@ func (ctl *Control) login() (err error) {
|
|||||||
if ctl.conn != nil {
|
if ctl.conn != nil {
|
||||||
ctl.conn.Close()
|
ctl.conn.Close()
|
||||||
}
|
}
|
||||||
|
if ctl.session != nil {
|
||||||
|
ctl.session.Close()
|
||||||
|
}
|
||||||
|
|
||||||
conn, err := net.ConnectTcpServerByHttpProxy(config.ClientCommonCfg.HttpProxy,
|
conn, err := net.ConnectTcpServerByHttpProxy(config.ClientCommonCfg.HttpProxy,
|
||||||
fmt.Sprintf("%s:%d", config.ClientCommonCfg.ServerAddr, config.ClientCommonCfg.ServerPort))
|
fmt.Sprintf("%s:%d", config.ClientCommonCfg.ServerAddr, config.ClientCommonCfg.ServerPort))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -178,6 +200,20 @@ func (ctl *Control) login() (err error) {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
if config.ClientCommonCfg.TcpMux {
|
||||||
|
session, errRet := smux.Client(conn, nil)
|
||||||
|
if errRet != nil {
|
||||||
|
return errRet
|
||||||
|
}
|
||||||
|
stream, errRet := session.OpenStream()
|
||||||
|
if errRet != nil {
|
||||||
|
session.Close()
|
||||||
|
return errRet
|
||||||
|
}
|
||||||
|
conn = net.WrapConn(stream)
|
||||||
|
ctl.session = session
|
||||||
|
}
|
||||||
|
|
||||||
now := time.Now().Unix()
|
now := time.Now().Unix()
|
||||||
ctl.loginMsg.PrivilegeKey = util.GetAuthKey(config.ClientCommonCfg.PrivilegeToken, now)
|
ctl.loginMsg.PrivilegeKey = util.GetAuthKey(config.ClientCommonCfg.PrivilegeToken, now)
|
||||||
ctl.loginMsg.Timestamp = now
|
ctl.loginMsg.Timestamp = now
|
||||||
|
@ -22,6 +22,9 @@ privilege_token = 12345678
|
|||||||
# connections will be established in advance, default value is zero
|
# connections will be established in advance, default value is zero
|
||||||
pool_count = 5
|
pool_count = 5
|
||||||
|
|
||||||
|
# if tcp stream multiplexing is used, default is true, it must be same with frps
|
||||||
|
tcp_mux = true
|
||||||
|
|
||||||
# your proxy name will be changed to {user}.{proxy}
|
# your proxy name will be changed to {user}.{proxy}
|
||||||
user = your_name
|
user = your_name
|
||||||
|
|
||||||
@ -48,12 +51,16 @@ remote_port = 6001
|
|||||||
type = udp
|
type = udp
|
||||||
local_ip = 114.114.114.114
|
local_ip = 114.114.114.114
|
||||||
local_port = 53
|
local_port = 53
|
||||||
|
remote_port = 6002
|
||||||
|
use_encryption = false
|
||||||
|
use_compression = false
|
||||||
|
|
||||||
# Resolve your domain names to [server_addr] so you can use http://web01.yourdomain.com to browse web01 and http://web02.yourdomain.com to browse web02
|
# Resolve your domain names to [server_addr] so you can use http://web01.yourdomain.com to browse web01 and http://web02.yourdomain.com to browse web02
|
||||||
[web01]
|
[web01]
|
||||||
type = http
|
type = http
|
||||||
local_ip = 127.0.0.1
|
local_ip = 127.0.0.1
|
||||||
local_port = 80
|
local_port = 80
|
||||||
|
use_encryption = false
|
||||||
use_compression = true
|
use_compression = true
|
||||||
# http username and password are safety certification for http protocol
|
# http username and password are safety certification for http protocol
|
||||||
# if not set, you can access this custom_domains without certification
|
# if not set, you can access this custom_domains without certification
|
||||||
@ -61,14 +68,16 @@ http_user = admin
|
|||||||
http_pwd = admin
|
http_pwd = admin
|
||||||
# if domain for frps is frps.com, then you can access [web01] proxy by URL http://test.frps.com
|
# if domain for frps is frps.com, then you can access [web01] proxy by URL http://test.frps.com
|
||||||
subdomain = web01
|
subdomain = web01
|
||||||
|
|
||||||
[web02]
|
|
||||||
type = http
|
|
||||||
local_ip = 127.0.0.1
|
|
||||||
local_port = 8000
|
|
||||||
use_encryption = false
|
|
||||||
use_compression = false
|
|
||||||
custom_domains = web02.yourdomain.com
|
custom_domains = web02.yourdomain.com
|
||||||
# locations is only useful for http type
|
# locations is only useful for http type
|
||||||
locations = /,/pic
|
locations = /,/pic
|
||||||
host_header_rewrite = example.com
|
host_header_rewrite = example.com
|
||||||
|
|
||||||
|
[web02]
|
||||||
|
type = https
|
||||||
|
local_ip = 127.0.0.1
|
||||||
|
local_port = 8000
|
||||||
|
use_encryption = false
|
||||||
|
use_compression = false
|
||||||
|
subdomain = web01
|
||||||
|
custom_domains = web02.yourdomain.com
|
||||||
|
@ -37,7 +37,7 @@ privilege_token = 12345678
|
|||||||
privilege_allow_ports = 2000-3000,3001,3003,4000-50000
|
privilege_allow_ports = 2000-3000,3001,3003,4000-50000
|
||||||
|
|
||||||
# pool_count in each proxy will change to max_pool_count if they exceed the maximum value
|
# pool_count in each proxy will change to max_pool_count if they exceed the maximum value
|
||||||
max_pool_count = 10
|
max_pool_count = 5
|
||||||
|
|
||||||
# authentication_timeout means the timeout interval (seconds) when the frpc connects frps
|
# authentication_timeout means the timeout interval (seconds) when the frpc connects frps
|
||||||
# if authentication_timeout is zero, the time is not verified, default is 900s
|
# if authentication_timeout is zero, the time is not verified, default is 900s
|
||||||
@ -46,3 +46,6 @@ authentication_timeout = 900
|
|||||||
# if subdomain_host is not empty, you can set subdomain when type is http or https in frpc's configure file
|
# if subdomain_host is not empty, you can set subdomain when type is http or https in frpc's configure file
|
||||||
# when subdomain is test, the host used by routing is test.frps.com
|
# when subdomain is test, the host used by routing is test.frps.com
|
||||||
subdomain_host = frps.com
|
subdomain_host = frps.com
|
||||||
|
|
||||||
|
# if tcp stream multiplexing is used, default is true
|
||||||
|
tcp_mux = true
|
||||||
|
@ -36,6 +36,7 @@ type ClientCommonConf struct {
|
|||||||
LogMaxDays int64
|
LogMaxDays int64
|
||||||
PrivilegeToken string
|
PrivilegeToken string
|
||||||
PoolCount int
|
PoolCount int
|
||||||
|
TcpMux bool
|
||||||
User string
|
User string
|
||||||
HeartBeatInterval int64
|
HeartBeatInterval int64
|
||||||
HeartBeatTimeout int64
|
HeartBeatTimeout int64
|
||||||
@ -53,6 +54,7 @@ func GetDeaultClientCommonConf() *ClientCommonConf {
|
|||||||
LogMaxDays: 3,
|
LogMaxDays: 3,
|
||||||
PrivilegeToken: "",
|
PrivilegeToken: "",
|
||||||
PoolCount: 1,
|
PoolCount: 1,
|
||||||
|
TcpMux: true,
|
||||||
User: "",
|
User: "",
|
||||||
HeartBeatInterval: 30,
|
HeartBeatInterval: 30,
|
||||||
HeartBeatTimeout: 90,
|
HeartBeatTimeout: 90,
|
||||||
@ -120,6 +122,13 @@ func LoadClientCommonConf(conf ini.File) (cfg *ClientCommonConf, err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tmpStr, ok = conf.Get("common", "tcp_mux")
|
||||||
|
if ok && tmpStr == "false" {
|
||||||
|
cfg.TcpMux = false
|
||||||
|
} else {
|
||||||
|
cfg.TcpMux = true
|
||||||
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "user")
|
tmpStr, ok = conf.Get("common", "user")
|
||||||
if ok {
|
if ok {
|
||||||
cfg.User = tmpStr
|
cfg.User = tmpStr
|
||||||
|
@ -49,6 +49,7 @@ type ServerCommonConf struct {
|
|||||||
PrivilegeToken string
|
PrivilegeToken string
|
||||||
AuthTimeout int64
|
AuthTimeout int64
|
||||||
SubDomainHost string
|
SubDomainHost string
|
||||||
|
TcpMux bool
|
||||||
|
|
||||||
// if PrivilegeAllowPorts is not nil, tcp proxies which remote port exist in this map can be connected
|
// if PrivilegeAllowPorts is not nil, tcp proxies which remote port exist in this map can be connected
|
||||||
PrivilegeAllowPorts map[int64]struct{}
|
PrivilegeAllowPorts map[int64]struct{}
|
||||||
@ -76,7 +77,8 @@ func GetDefaultServerCommonConf() *ServerCommonConf {
|
|||||||
PrivilegeToken: "",
|
PrivilegeToken: "",
|
||||||
AuthTimeout: 900,
|
AuthTimeout: 900,
|
||||||
SubDomainHost: "",
|
SubDomainHost: "",
|
||||||
MaxPoolCount: 10,
|
TcpMux: true,
|
||||||
|
MaxPoolCount: 5,
|
||||||
HeartBeatTimeout: 90,
|
HeartBeatTimeout: 90,
|
||||||
UserConnTimeout: 10,
|
UserConnTimeout: 10,
|
||||||
}
|
}
|
||||||
@ -265,6 +267,13 @@ func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) {
|
|||||||
cfg.SubDomainHost = strings.ToLower(strings.TrimSpace(tmpStr))
|
cfg.SubDomainHost = strings.ToLower(strings.TrimSpace(tmpStr))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tmpStr, ok = conf.Get("common", "tcp_mux")
|
||||||
|
if ok && tmpStr == "false" {
|
||||||
|
cfg.TcpMux = false
|
||||||
|
} else {
|
||||||
|
cfg.TcpMux = true
|
||||||
|
}
|
||||||
|
|
||||||
tmpStr, ok = conf.Get("common", "heartbeat_timeout")
|
tmpStr, ok = conf.Get("common", "heartbeat_timeout")
|
||||||
if ok {
|
if ok {
|
||||||
v, errRet := strconv.ParseInt(tmpStr, 10, 64)
|
v, errRet := strconv.ParseInt(tmpStr, 10, 64)
|
||||||
|
@ -22,10 +22,12 @@ import (
|
|||||||
"github.com/fatedier/frp/models/config"
|
"github.com/fatedier/frp/models/config"
|
||||||
"github.com/fatedier/frp/models/msg"
|
"github.com/fatedier/frp/models/msg"
|
||||||
"github.com/fatedier/frp/utils/log"
|
"github.com/fatedier/frp/utils/log"
|
||||||
"github.com/fatedier/frp/utils/net"
|
frpNet "github.com/fatedier/frp/utils/net"
|
||||||
"github.com/fatedier/frp/utils/util"
|
"github.com/fatedier/frp/utils/util"
|
||||||
"github.com/fatedier/frp/utils/version"
|
"github.com/fatedier/frp/utils/version"
|
||||||
"github.com/fatedier/frp/utils/vhost"
|
"github.com/fatedier/frp/utils/vhost"
|
||||||
|
|
||||||
|
"github.com/xtaci/smux"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -37,7 +39,7 @@ var ServerService *Service
|
|||||||
// Server service.
|
// Server service.
|
||||||
type Service struct {
|
type Service struct {
|
||||||
// Accept connections from client.
|
// Accept connections from client.
|
||||||
listener net.Listener
|
listener frpNet.Listener
|
||||||
|
|
||||||
// For http proxies, route requests to different clients by hostname and other infomation.
|
// For http proxies, route requests to different clients by hostname and other infomation.
|
||||||
VhostHttpMuxer *vhost.HttpMuxer
|
VhostHttpMuxer *vhost.HttpMuxer
|
||||||
@ -66,7 +68,7 @@ func NewService() (svr *Service, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Listen for accepting connections from client.
|
// Listen for accepting connections from client.
|
||||||
svr.listener, err = net.ListenTcp(config.ServerCommonCfg.BindAddr, config.ServerCommonCfg.BindPort)
|
svr.listener, err = frpNet.ListenTcp(config.ServerCommonCfg.BindAddr, config.ServerCommonCfg.BindPort)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("Create server listener error, %v", err)
|
err = fmt.Errorf("Create server listener error, %v", err)
|
||||||
return
|
return
|
||||||
@ -74,8 +76,8 @@ func NewService() (svr *Service, err error) {
|
|||||||
|
|
||||||
// Create http vhost muxer.
|
// Create http vhost muxer.
|
||||||
if config.ServerCommonCfg.VhostHttpPort != 0 {
|
if config.ServerCommonCfg.VhostHttpPort != 0 {
|
||||||
var l net.Listener
|
var l frpNet.Listener
|
||||||
l, err = net.ListenTcp(config.ServerCommonCfg.BindAddr, config.ServerCommonCfg.VhostHttpPort)
|
l, err = frpNet.ListenTcp(config.ServerCommonCfg.BindAddr, config.ServerCommonCfg.VhostHttpPort)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("Create vhost http listener error, %v", err)
|
err = fmt.Errorf("Create vhost http listener error, %v", err)
|
||||||
return
|
return
|
||||||
@ -89,8 +91,8 @@ func NewService() (svr *Service, err error) {
|
|||||||
|
|
||||||
// Create https vhost muxer.
|
// Create https vhost muxer.
|
||||||
if config.ServerCommonCfg.VhostHttpsPort != 0 {
|
if config.ServerCommonCfg.VhostHttpsPort != 0 {
|
||||||
var l net.Listener
|
var l frpNet.Listener
|
||||||
l, err = net.ListenTcp(config.ServerCommonCfg.BindAddr, config.ServerCommonCfg.VhostHttpsPort)
|
l, err = frpNet.ListenTcp(config.ServerCommonCfg.BindAddr, config.ServerCommonCfg.VhostHttpsPort)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("Create vhost https listener error, %v", err)
|
err = fmt.Errorf("Create vhost https listener error, %v", err)
|
||||||
return
|
return
|
||||||
@ -123,40 +125,63 @@ func (svr *Service) Run() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Start a new goroutine for dealing connections.
|
// Start a new goroutine for dealing connections.
|
||||||
go func(frpConn net.Conn) {
|
go func(frpConn frpNet.Conn) {
|
||||||
var rawMsg msg.Message
|
dealFn := func(conn frpNet.Conn) {
|
||||||
frpConn.SetReadDeadline(time.Now().Add(connReadTimeout))
|
var rawMsg msg.Message
|
||||||
if rawMsg, err = msg.ReadMsg(frpConn); err != nil {
|
conn.SetReadDeadline(time.Now().Add(connReadTimeout))
|
||||||
log.Warn("Failed to read message: %v", err)
|
if rawMsg, err = msg.ReadMsg(conn); err != nil {
|
||||||
frpConn.Close()
|
log.Warn("Failed to read message: %v", err)
|
||||||
return
|
conn.Close()
|
||||||
}
|
return
|
||||||
frpConn.SetReadDeadline(time.Time{})
|
|
||||||
|
|
||||||
switch m := rawMsg.(type) {
|
|
||||||
case *msg.Login:
|
|
||||||
err = svr.RegisterControl(frpConn, m)
|
|
||||||
// If login failed, send error message there.
|
|
||||||
// Otherwise send success message in control's work goroutine.
|
|
||||||
if err != nil {
|
|
||||||
frpConn.Warn("%v", err)
|
|
||||||
msg.WriteMsg(frpConn, &msg.LoginResp{
|
|
||||||
Version: version.Full(),
|
|
||||||
Error: err.Error(),
|
|
||||||
})
|
|
||||||
frpConn.Close()
|
|
||||||
}
|
}
|
||||||
case *msg.NewWorkConn:
|
conn.SetReadDeadline(time.Time{})
|
||||||
svr.RegisterWorkConn(frpConn, m)
|
|
||||||
default:
|
switch m := rawMsg.(type) {
|
||||||
log.Warn("Error message type for the new connection [%s]", frpConn.RemoteAddr().String())
|
case *msg.Login:
|
||||||
frpConn.Close()
|
err = svr.RegisterControl(conn, m)
|
||||||
|
// If login failed, send error message there.
|
||||||
|
// Otherwise send success message in control's work goroutine.
|
||||||
|
if err != nil {
|
||||||
|
conn.Warn("%v", err)
|
||||||
|
msg.WriteMsg(conn, &msg.LoginResp{
|
||||||
|
Version: version.Full(),
|
||||||
|
Error: err.Error(),
|
||||||
|
})
|
||||||
|
conn.Close()
|
||||||
|
}
|
||||||
|
case *msg.NewWorkConn:
|
||||||
|
svr.RegisterWorkConn(conn, m)
|
||||||
|
default:
|
||||||
|
log.Warn("Error message type for the new connection [%s]", conn.RemoteAddr().String())
|
||||||
|
conn.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.ServerCommonCfg.TcpMux {
|
||||||
|
session, err := smux.Server(frpConn, nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("Failed to create mux connection: %v", err)
|
||||||
|
frpConn.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
stream, err := session.AcceptStream()
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("Accept new mux stream error: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
wrapConn := frpNet.WrapConn(stream)
|
||||||
|
go dealFn(wrapConn)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dealFn(frpConn)
|
||||||
}
|
}
|
||||||
}(c)
|
}(c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (svr *Service) RegisterControl(ctlConn net.Conn, loginMsg *msg.Login) (err error) {
|
func (svr *Service) RegisterControl(ctlConn frpNet.Conn, loginMsg *msg.Login) (err error) {
|
||||||
ctlConn.Info("client login info: ip [%s] version [%s] hostname [%s] os [%s] arch [%s]",
|
ctlConn.Info("client login info: ip [%s] version [%s] hostname [%s] os [%s] arch [%s]",
|
||||||
ctlConn.RemoteAddr().String(), loginMsg.Version, loginMsg.Hostname, loginMsg.Os, loginMsg.Arch)
|
ctlConn.RemoteAddr().String(), loginMsg.Version, loginMsg.Hostname, loginMsg.Os, loginMsg.Arch)
|
||||||
|
|
||||||
@ -201,7 +226,7 @@ func (svr *Service) RegisterControl(ctlConn net.Conn, loginMsg *msg.Login) (err
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RegisterWorkConn register a new work connection to control and proxies need it.
|
// RegisterWorkConn register a new work connection to control and proxies need it.
|
||||||
func (svr *Service) RegisterWorkConn(workConn net.Conn, newMsg *msg.NewWorkConn) {
|
func (svr *Service) RegisterWorkConn(workConn frpNet.Conn, newMsg *msg.NewWorkConn) {
|
||||||
ctl, exist := svr.ctlManager.GetById(newMsg.RunId)
|
ctl, exist := svr.ctlManager.GetById(newMsg.RunId)
|
||||||
if !exist {
|
if !exist {
|
||||||
workConn.Warn("No client control found for run id [%s]", newMsg.RunId)
|
workConn.Warn("No client control found for run id [%s]", newMsg.RunId)
|
||||||
|
@ -26,6 +26,18 @@ type Conn interface {
|
|||||||
log.Logger
|
log.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type WrapLogConn struct {
|
||||||
|
net.Conn
|
||||||
|
log.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func WrapConn(c net.Conn) Conn {
|
||||||
|
return WrapLogConn{
|
||||||
|
Conn: c,
|
||||||
|
Logger: log.NewPrefixLogger(""),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type Listener interface {
|
type Listener interface {
|
||||||
Accept() (Conn, error)
|
Accept() (Conn, error)
|
||||||
Close() error
|
Close() error
|
||||||
|
Loading…
Reference in New Issue
Block a user