Skip to content

Commit

Permalink
new proxy type: stcp(secret tcp)
Browse files Browse the repository at this point in the history
  • Loading branch information
fatedier committed Jun 25, 2017
1 parent e3fc73d commit 171bc8d
Show file tree
Hide file tree
Showing 14 changed files with 581 additions and 63 deletions.
98 changes: 62 additions & 36 deletions client/control.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import (
"github.com/fatedier/frp/models/msg"
"github.com/fatedier/frp/utils/crypto"
"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/version"
"github.com/xtaci/smux"
Expand All @@ -48,8 +48,14 @@ type Control struct {
// proxies
proxies map[string]Proxy

// vistor configures
vistorCfgs map[string]config.ProxyConf

// vistors
vistors map[string]Vistor

// control connection
conn net.Conn
conn frpNet.Conn

// tcp stream multiplexing, if enabled
session *smux.Session
Expand Down Expand Up @@ -77,7 +83,7 @@ type Control struct {
log.Logger
}

func NewControl(svr *Service, pxyCfgs map[string]config.ProxyConf) *Control {
func NewControl(svr *Service, pxyCfgs map[string]config.ProxyConf, vistorCfgs map[string]config.ProxyConf) *Control {
loginMsg := &msg.Login{
Arch: runtime.GOARCH,
Os: runtime.GOOS,
Expand All @@ -86,14 +92,16 @@ func NewControl(svr *Service, pxyCfgs map[string]config.ProxyConf) *Control {
Version: version.Full(),
}
return &Control{
svr: svr,
loginMsg: loginMsg,
pxyCfgs: pxyCfgs,
proxies: make(map[string]Proxy),
sendCh: make(chan msg.Message, 10),
readCh: make(chan msg.Message, 10),
closedCh: make(chan int),
Logger: log.NewPrefixLogger(""),
svr: svr,
loginMsg: loginMsg,
pxyCfgs: pxyCfgs,
vistorCfgs: vistorCfgs,
proxies: make(map[string]Proxy),
vistors: make(map[string]Vistor),
sendCh: make(chan msg.Message, 10),
readCh: make(chan msg.Message, 10),
closedCh: make(chan int),
Logger: log.NewPrefixLogger(""),
}
}

Expand All @@ -105,16 +113,17 @@ func NewControl(svr *Service, pxyCfgs map[string]config.ProxyConf) *Control {
// 6. In controler(): ini readCh, sendCh, closedCh
// 7. In controler(): start new reader(), writer(), manager()
// controler() will keep running
func (ctl *Control) Run() error {
func (ctl *Control) Run() (err error) {
for {
err := ctl.login()
err = ctl.login()
if err != nil {
ctl.Warn("login to server failed: %v", err)

// if login_fail_exit is true, just exit this program
// otherwise sleep a while and continues relogin to server
if config.ClientCommonCfg.LoginFailExit {
return err
return
} else {
ctl.Warn("login to server fail: %v", err)
time.Sleep(30 * time.Second)
}
} else {
Expand All @@ -133,29 +142,25 @@ func (ctl *Control) Run() error {
cfg.UnMarshalToMsg(&newProxyMsg)
ctl.sendCh <- &newProxyMsg
}
return nil
}

func (ctl *Control) NewWorkConn() {
var (
workConn net.Conn
err error
)
if config.ClientCommonCfg.TcpMux {
stream, err := ctl.session.OpenStream()
// start all local vistors
for _, cfg := range ctl.vistorCfgs {
vistor := NewVistor(ctl, cfg)
err = vistor.Run()
if err != nil {
ctl.Warn("start new work connection error: %v", err)
return
vistor.Warn("start error: %v", err)
continue
}
workConn = net.WrapConn(stream)
ctl.vistors[cfg.GetName()] = vistor
vistor.Info("start vistor success")
}
return nil
}

} else {
workConn, err = net.ConnectServerByHttpProxy(config.ClientCommonCfg.HttpProxy, config.ClientCommonCfg.Protocol,
fmt.Sprintf("%s:%d", config.ClientCommonCfg.ServerAddr, config.ClientCommonCfg.ServerPort))
if err != nil {
ctl.Warn("start new work connection error: %v", err)
return
}
func (ctl *Control) NewWorkConn() {
workConn, err := ctl.connectServer()
if err != nil {
return
}

m := &msg.NewWorkConn{
Expand Down Expand Up @@ -199,7 +204,7 @@ func (ctl *Control) login() (err error) {
ctl.session.Close()
}

conn, err := net.ConnectServerByHttpProxy(config.ClientCommonCfg.HttpProxy, config.ClientCommonCfg.Protocol,
conn, err := frpNet.ConnectServerByHttpProxy(config.ClientCommonCfg.HttpProxy, config.ClientCommonCfg.Protocol,
fmt.Sprintf("%s:%d", config.ClientCommonCfg.ServerAddr, config.ClientCommonCfg.ServerPort))
if err != nil {
return err
Expand All @@ -221,7 +226,7 @@ func (ctl *Control) login() (err error) {
session.Close()
return errRet
}
conn = net.WrapConn(stream)
conn = frpNet.WrapConn(stream)
ctl.session = session
}

Expand Down Expand Up @@ -261,6 +266,27 @@ func (ctl *Control) login() (err error) {
return nil
}

func (ctl *Control) connectServer() (conn frpNet.Conn, err error) {
if config.ClientCommonCfg.TcpMux {
stream, errRet := ctl.session.OpenStream()
if errRet != nil {
err = errRet
ctl.Warn("start new connection to server error: %v", err)
return
}
conn = frpNet.WrapConn(stream)

} else {
conn, err = frpNet.ConnectServerByHttpProxy(config.ClientCommonCfg.HttpProxy, config.ClientCommonCfg.Protocol,
fmt.Sprintf("%s:%d", config.ClientCommonCfg.ServerAddr, config.ClientCommonCfg.ServerPort))
if err != nil {
ctl.Warn("start new connection to server error: %v", err)
return
}
}
return
}

func (ctl *Control) reader() {
defer func() {
if err := recover(); err != nil {
Expand Down
35 changes: 34 additions & 1 deletion client/proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import (
frpNet "github.com/fatedier/frp/utils/net"
)

// Proxy defines how to work for different proxy type.
// Proxy defines how to deal with work connections for different proxy type.
type Proxy interface {
Run() error

Expand Down Expand Up @@ -67,6 +67,11 @@ func NewProxy(ctl *Control, pxyConf config.ProxyConf) (pxy Proxy) {
BaseProxy: baseProxy,
cfg: cfg,
}
case *config.StcpProxyConf:
pxy = &StcpProxy{
BaseProxy: baseProxy,
cfg: cfg,
}
}
return
}
Expand Down Expand Up @@ -162,6 +167,34 @@ func (pxy *HttpsProxy) InWorkConn(conn frpNet.Conn) {
HandleTcpWorkConnection(&pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, conn)
}

// STCP
type StcpProxy struct {
BaseProxy

cfg *config.StcpProxyConf
proxyPlugin plugin.Plugin
}

func (pxy *StcpProxy) Run() (err error) {
if pxy.cfg.Plugin != "" {
pxy.proxyPlugin, err = plugin.Create(pxy.cfg.Plugin, pxy.cfg.PluginParams)
if err != nil {
return
}
}
return
}

func (pxy *StcpProxy) Close() {
if pxy.proxyPlugin != nil {
pxy.proxyPlugin.Close()
}
}

func (pxy *StcpProxy) InWorkConn(conn frpNet.Conn) {
HandleTcpWorkConnection(&pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, conn)
}

// UDP
type UdpProxy struct {
BaseProxy
Expand Down
4 changes: 2 additions & 2 deletions client/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ type Service struct {
closedCh chan int
}

func NewService(pxyCfgs map[string]config.ProxyConf) (svr *Service) {
func NewService(pxyCfgs map[string]config.ProxyConf, vistorCfgs map[string]config.ProxyConf) (svr *Service) {
svr = &Service{
closedCh: make(chan int),
}
ctl := NewControl(svr, pxyCfgs)
ctl := NewControl(svr, pxyCfgs, vistorCfgs)
svr.ctl = ctl
return
}
Expand Down
145 changes: 145 additions & 0 deletions client/vistor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
// Copyright 2017 fatedier, [email protected]
//
// 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 client

import (
"io"
"sync"
"time"

"github.com/fatedier/frp/models/config"
"github.com/fatedier/frp/models/msg"
frpIo "github.com/fatedier/frp/utils/io"
"github.com/fatedier/frp/utils/log"
frpNet "github.com/fatedier/frp/utils/net"
"github.com/fatedier/frp/utils/util"
)

// Vistor is used for forward traffics from local port tot remote service.
type Vistor interface {
Run() error
Close()
log.Logger
}

func NewVistor(ctl *Control, pxyConf config.ProxyConf) (vistor Vistor) {
baseVistor := BaseVistor{
ctl: ctl,
Logger: log.NewPrefixLogger(pxyConf.GetName()),
}
switch cfg := pxyConf.(type) {
case *config.StcpProxyConf:
vistor = &StcpVistor{
BaseVistor: baseVistor,
cfg: cfg,
}
}
return
}

type BaseVistor struct {
ctl *Control
l frpNet.Listener
closed bool
mu sync.RWMutex
log.Logger
}

type StcpVistor struct {
BaseVistor

cfg *config.StcpProxyConf
}

func (sv *StcpVistor) Run() (err error) {
sv.l, err = frpNet.ListenTcp(sv.cfg.BindAddr, int64(sv.cfg.BindPort))
if err != nil {
return
}

go sv.worker()
return
}

func (sv *StcpVistor) Close() {
sv.l.Close()
}

func (sv *StcpVistor) worker() {
for {
conn, err := sv.l.Accept()
if err != nil {
sv.Warn("stcp local listener closed")
return
}

go sv.handleConn(conn)
}
}

func (sv *StcpVistor) handleConn(userConn frpNet.Conn) {
defer userConn.Close()

sv.Debug("get a new stcp user connection")
vistorConn, err := sv.ctl.connectServer()
if err != nil {
return
}
defer vistorConn.Close()

now := time.Now().Unix()
newVistorConnMsg := &msg.NewVistorConn{
ProxyName: sv.cfg.ServerName,
SignKey: util.GetAuthKey(sv.cfg.Sk, now),
Timestamp: now,
UseEncryption: sv.cfg.UseEncryption,
UseCompression: sv.cfg.UseCompression,
}
err = msg.WriteMsg(vistorConn, newVistorConnMsg)
if err != nil {
sv.Warn("send newVistorConnMsg to server error: %v", err)
return
}

var newVistorConnRespMsg msg.NewVistorConnResp
vistorConn.SetReadDeadline(time.Now().Add(10 * time.Second))
err = msg.ReadMsgInto(vistorConn, &newVistorConnRespMsg)
if err != nil {
sv.Warn("get newVistorConnRespMsg error: %v", err)
return
}
vistorConn.SetReadDeadline(time.Time{})

if newVistorConnRespMsg.Error != "" {
sv.Warn("start new vistor connection error: %s", newVistorConnRespMsg.Error)
return
}

var remote io.ReadWriteCloser
remote = vistorConn
if sv.cfg.UseEncryption {
remote, err = frpIo.WithEncryption(remote, []byte(sv.cfg.Sk))
if err != nil {
sv.Error("create encryption stream error: %v", err)
return
}
}

if sv.cfg.UseCompression {
remote = frpIo.WithCompression(remote)
}

frpIo.Join(userConn, remote)
}
Loading

0 comments on commit 171bc8d

Please sign in to comment.