change log method

This commit is contained in:
fatedier 2019-10-12 20:13:12 +08:00
parent 5dc8175fc8
commit 649f47c345
44 changed files with 670 additions and 688 deletions

View File

@ -16,7 +16,7 @@ file:
fmt:
go fmt ./...
frps:
go build -o bin/frps ./cmd/frps

View File

@ -15,9 +15,11 @@
package client
import (
"context"
"crypto/tls"
"fmt"
"io"
"net"
"runtime/debug"
"sync"
"time"
@ -25,8 +27,8 @@ import (
"github.com/fatedier/frp/client/proxy"
"github.com/fatedier/frp/models/config"
"github.com/fatedier/frp/models/msg"
"github.com/fatedier/frp/utils/log"
frpNet "github.com/fatedier/frp/utils/net"
"github.com/fatedier/frp/utils/xlog"
"github.com/fatedier/golib/control/shutdown"
"github.com/fatedier/golib/crypto"
@ -45,7 +47,7 @@ type Control struct {
vm *VisitorManager
// control connection
conn frpNet.Conn
conn net.Conn
// tcp stream multiplexing, if enabled
session *fmux.Session
@ -76,12 +78,19 @@ type Control struct {
mu sync.RWMutex
log.Logger
xl *xlog.Logger
// service context
ctx context.Context
}
func NewControl(runId string, conn frpNet.Conn, session *fmux.Session, clientCfg config.ClientCommonConf,
pxyCfgs map[string]config.ProxyConf, visitorCfgs map[string]config.VisitorConf, serverUDPPort int) *Control {
func NewControl(ctx context.Context, runId string, conn net.Conn, session *fmux.Session,
clientCfg config.ClientCommonConf,
pxyCfgs map[string]config.ProxyConf,
visitorCfgs map[string]config.VisitorConf,
serverUDPPort int) *Control {
// new xlog instance
ctl := &Control{
runId: runId,
conn: conn,
@ -96,11 +105,12 @@ func NewControl(runId string, conn frpNet.Conn, session *fmux.Session, clientCfg
writerShutdown: shutdown.New(),
msgHandlerShutdown: shutdown.New(),
serverUDPPort: serverUDPPort,
Logger: log.NewPrefixLogger(""),
xl: xlog.FromContextSafe(ctx),
ctx: ctx,
}
ctl.pm = proxy.NewProxyManager(ctl.sendCh, runId, clientCfg, serverUDPPort)
ctl.pm = proxy.NewProxyManager(ctl.ctx, ctl.sendCh, clientCfg, serverUDPPort)
ctl.vm = NewVisitorManager(ctl)
ctl.vm = NewVisitorManager(ctl.ctx, ctl)
ctl.vm.Reload(visitorCfgs)
return ctl
}
@ -117,6 +127,7 @@ func (ctl *Control) Run() {
}
func (ctl *Control) HandleReqWorkConn(inMsg *msg.ReqWorkConn) {
xl := ctl.xl
workConn, err := ctl.connectServer()
if err != nil {
return
@ -126,31 +137,31 @@ func (ctl *Control) HandleReqWorkConn(inMsg *msg.ReqWorkConn) {
RunId: ctl.runId,
}
if err = msg.WriteMsg(workConn, m); err != nil {
ctl.Warn("work connection write to server error: %v", err)
xl.Warn("work connection write to server error: %v", err)
workConn.Close()
return
}
var startMsg msg.StartWorkConn
if err = msg.ReadMsgInto(workConn, &startMsg); err != nil {
ctl.Error("work connection closed, %v", err)
xl.Error("work connection closed before response StartWorkConn message: %v", err)
workConn.Close()
return
}
workConn.AddLogPrefix(startMsg.ProxyName)
// dispatch this work connection to related proxy
ctl.pm.HandleWorkConn(startMsg.ProxyName, workConn, &startMsg)
}
func (ctl *Control) HandleNewProxyResp(inMsg *msg.NewProxyResp) {
xl := ctl.xl
// Server will return NewProxyResp message to each NewProxy message.
// Start a new proxy handler if no error got
err := ctl.pm.StartProxy(inMsg.ProxyName, inMsg.RemoteAddr, inMsg.Error)
if err != nil {
ctl.Warn("[%s] start error: %v", inMsg.ProxyName, err)
xl.Warn("[%s] start error: %v", inMsg.ProxyName, err)
} else {
ctl.Info("[%s] start proxy success", inMsg.ProxyName)
xl.Info("[%s] start proxy success", inMsg.ProxyName)
}
}
@ -169,15 +180,16 @@ func (ctl *Control) ClosedDoneCh() <-chan struct{} {
}
// connectServer return a new connection to frps
func (ctl *Control) connectServer() (conn frpNet.Conn, err error) {
func (ctl *Control) connectServer() (conn net.Conn, err error) {
xl := ctl.xl
if ctl.clientCfg.TcpMux {
stream, errRet := ctl.session.OpenStream()
if errRet != nil {
err = errRet
ctl.Warn("start new connection to server error: %v", err)
xl.Warn("start new connection to server error: %v", err)
return
}
conn = frpNet.WrapConn(stream)
conn = stream
} else {
var tlsConfig *tls.Config
if ctl.clientCfg.TLSEnable {
@ -188,7 +200,7 @@ func (ctl *Control) connectServer() (conn frpNet.Conn, err error) {
conn, err = frpNet.ConnectServerByProxyWithTLS(ctl.clientCfg.HttpProxy, ctl.clientCfg.Protocol,
fmt.Sprintf("%s:%d", ctl.clientCfg.ServerAddr, ctl.clientCfg.ServerPort), tlsConfig)
if err != nil {
ctl.Warn("start new connection to server error: %v", err)
xl.Warn("start new connection to server error: %v", err)
return
}
}
@ -197,10 +209,11 @@ func (ctl *Control) connectServer() (conn frpNet.Conn, err error) {
// reader read all messages from frps and send to readCh
func (ctl *Control) reader() {
xl := ctl.xl
defer func() {
if err := recover(); err != nil {
ctl.Error("panic error: %v", err)
ctl.Error(string(debug.Stack()))
xl.Error("panic error: %v", err)
xl.Error(string(debug.Stack()))
}
}()
defer ctl.readerShutdown.Done()
@ -210,10 +223,10 @@ func (ctl *Control) reader() {
for {
if m, err := msg.ReadMsg(encReader); err != nil {
if err == io.EOF {
ctl.Debug("read from control connection EOF")
xl.Debug("read from control connection EOF")
return
} else {
ctl.Warn("read error: %v", err)
xl.Warn("read error: %v", err)
ctl.conn.Close()
return
}
@ -225,20 +238,21 @@ func (ctl *Control) reader() {
// writer writes messages got from sendCh to frps
func (ctl *Control) writer() {
xl := ctl.xl
defer ctl.writerShutdown.Done()
encWriter, err := crypto.NewWriter(ctl.conn, []byte(ctl.clientCfg.Token))
if err != nil {
ctl.conn.Error("crypto new writer error: %v", err)
xl.Error("crypto new writer error: %v", err)
ctl.conn.Close()
return
}
for {
if m, ok := <-ctl.sendCh; !ok {
ctl.Info("control writer is closing")
xl.Info("control writer is closing")
return
} else {
if err := msg.WriteMsg(encWriter, m); err != nil {
ctl.Warn("write message to control connection error: %v", err)
xl.Warn("write message to control connection error: %v", err)
return
}
}
@ -247,10 +261,11 @@ func (ctl *Control) writer() {
// msgHandler handles all channel events and do corresponding operations.
func (ctl *Control) msgHandler() {
xl := ctl.xl
defer func() {
if err := recover(); err != nil {
ctl.Error("panic error: %v", err)
ctl.Error(string(debug.Stack()))
xl.Error("panic error: %v", err)
xl.Error(string(debug.Stack()))
}
}()
defer ctl.msgHandlerShutdown.Done()
@ -266,11 +281,11 @@ func (ctl *Control) msgHandler() {
select {
case <-hbSend.C:
// send heartbeat to server
ctl.Debug("send heartbeat to server")
xl.Debug("send heartbeat to server")
ctl.sendCh <- &msg.Ping{}
case <-hbCheck.C:
if time.Since(ctl.lastPong) > time.Duration(ctl.clientCfg.HeartBeatTimeout)*time.Second {
ctl.Warn("heartbeat timeout")
xl.Warn("heartbeat timeout")
// let reader() stop
ctl.conn.Close()
return
@ -287,7 +302,7 @@ func (ctl *Control) msgHandler() {
ctl.HandleNewProxyResp(m)
case *msg.Pong:
ctl.lastPong = time.Now()
ctl.Debug("receive heartbeat from server")
xl.Debug("receive heartbeat from server")
}
}
}

View File

@ -24,7 +24,7 @@ import (
"net/http"
"time"
"github.com/fatedier/frp/utils/log"
"github.com/fatedier/frp/utils/xlog"
)
var (
@ -50,11 +50,11 @@ type HealthCheckMonitor struct {
ctx context.Context
cancel context.CancelFunc
l log.Logger
}
func NewHealthCheckMonitor(checkType string, intervalS int, timeoutS int, maxFailedTimes int, addr string, url string,
func NewHealthCheckMonitor(ctx context.Context, checkType string,
intervalS int, timeoutS int, maxFailedTimes int,
addr string, url string,
statusNormalFn func(), statusFailedFn func()) *HealthCheckMonitor {
if intervalS <= 0 {
@ -66,7 +66,7 @@ func NewHealthCheckMonitor(checkType string, intervalS int, timeoutS int, maxFai
if maxFailedTimes <= 0 {
maxFailedTimes = 1
}
ctx, cancel := context.WithCancel(context.Background())
newctx, cancel := context.WithCancel(ctx)
return &HealthCheckMonitor{
checkType: checkType,
interval: time.Duration(intervalS) * time.Second,
@ -77,15 +77,11 @@ func NewHealthCheckMonitor(checkType string, intervalS int, timeoutS int, maxFai
statusOK: false,
statusNormalFn: statusNormalFn,
statusFailedFn: statusFailedFn,
ctx: ctx,
ctx: newctx,
cancel: cancel,
}
}
func (monitor *HealthCheckMonitor) SetLogger(l log.Logger) {
monitor.l = l
}
func (monitor *HealthCheckMonitor) Start() {
go monitor.checkWorker()
}
@ -95,6 +91,7 @@ func (monitor *HealthCheckMonitor) Stop() {
}
func (monitor *HealthCheckMonitor) checkWorker() {
xl := xlog.FromContextSafe(monitor.ctx)
for {
doCtx, cancel := context.WithDeadline(monitor.ctx, time.Now().Add(monitor.timeout))
err := monitor.doCheck(doCtx)
@ -109,25 +106,17 @@ func (monitor *HealthCheckMonitor) checkWorker() {
}
if err == nil {
if monitor.l != nil {
monitor.l.Trace("do one health check success")
}
xl.Trace("do one health check success")
if !monitor.statusOK && monitor.statusNormalFn != nil {
if monitor.l != nil {
monitor.l.Info("health check status change to success")
}
xl.Info("health check status change to success")
monitor.statusOK = true
monitor.statusNormalFn()
}
} else {
if monitor.l != nil {
monitor.l.Warn("do one health check failed: %v", err)
}
xl.Warn("do one health check failed: %v", err)
monitor.failedTimes++
if monitor.statusOK && int(monitor.failedTimes) >= monitor.maxFailedTimes && monitor.statusFailedFn != nil {
if monitor.l != nil {
monitor.l.Warn("health check status change to failed")
}
xl.Warn("health check status change to failed")
monitor.statusOK = false
monitor.statusFailedFn()
}

View File

@ -16,6 +16,7 @@ package proxy
import (
"bytes"
"context"
"fmt"
"io"
"io/ioutil"
@ -29,8 +30,8 @@ import (
"github.com/fatedier/frp/models/msg"
"github.com/fatedier/frp/models/plugin"
"github.com/fatedier/frp/models/proto/udp"
"github.com/fatedier/frp/utils/log"
frpNet "github.com/fatedier/frp/utils/net"
"github.com/fatedier/frp/utils/xlog"
"github.com/fatedier/golib/errors"
frpIo "github.com/fatedier/golib/io"
@ -44,17 +45,17 @@ type Proxy interface {
Run() error
// InWorkConn accept work connections registered to server.
InWorkConn(frpNet.Conn, *msg.StartWorkConn)
InWorkConn(net.Conn, *msg.StartWorkConn)
Close()
log.Logger
}
func NewProxy(pxyConf config.ProxyConf, clientCfg config.ClientCommonConf, serverUDPPort int) (pxy Proxy) {
func NewProxy(ctx context.Context, pxyConf config.ProxyConf, clientCfg config.ClientCommonConf, serverUDPPort int) (pxy Proxy) {
baseProxy := BaseProxy{
Logger: log.NewPrefixLogger(pxyConf.GetBaseInfo().ProxyName),
clientCfg: clientCfg,
serverUDPPort: serverUDPPort,
xl: xlog.FromContextSafe(ctx),
ctx: ctx,
}
switch cfg := pxyConf.(type) {
case *config.TcpProxyConf:
@ -93,10 +94,12 @@ func NewProxy(pxyConf config.ProxyConf, clientCfg config.ClientCommonConf, serve
type BaseProxy struct {
closed bool
mu sync.RWMutex
clientCfg config.ClientCommonConf
serverUDPPort int
log.Logger
mu sync.RWMutex
xl *xlog.Logger
ctx context.Context
}
// TCP
@ -123,8 +126,8 @@ func (pxy *TcpProxy) Close() {
}
}
func (pxy *TcpProxy) InWorkConn(conn frpNet.Conn, m *msg.StartWorkConn) {
HandleTcpWorkConnection(&pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, conn,
func (pxy *TcpProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) {
HandleTcpWorkConnection(pxy.ctx, &pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, conn,
[]byte(pxy.clientCfg.Token), m)
}
@ -152,8 +155,8 @@ func (pxy *HttpProxy) Close() {
}
}
func (pxy *HttpProxy) InWorkConn(conn frpNet.Conn, m *msg.StartWorkConn) {
HandleTcpWorkConnection(&pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, conn,
func (pxy *HttpProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) {
HandleTcpWorkConnection(pxy.ctx, &pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, conn,
[]byte(pxy.clientCfg.Token), m)
}
@ -181,8 +184,8 @@ func (pxy *HttpsProxy) Close() {
}
}
func (pxy *HttpsProxy) InWorkConn(conn frpNet.Conn, m *msg.StartWorkConn) {
HandleTcpWorkConnection(&pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, conn,
func (pxy *HttpsProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) {
HandleTcpWorkConnection(pxy.ctx, &pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, conn,
[]byte(pxy.clientCfg.Token), m)
}
@ -210,8 +213,8 @@ func (pxy *StcpProxy) Close() {
}
}
func (pxy *StcpProxy) InWorkConn(conn frpNet.Conn, m *msg.StartWorkConn) {
HandleTcpWorkConnection(&pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, conn,
func (pxy *StcpProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) {
HandleTcpWorkConnection(pxy.ctx, &pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, conn,
[]byte(pxy.clientCfg.Token), m)
}
@ -239,12 +242,13 @@ func (pxy *XtcpProxy) Close() {
}
}
func (pxy *XtcpProxy) InWorkConn(conn frpNet.Conn, m *msg.StartWorkConn) {
func (pxy *XtcpProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) {
xl := pxy.xl
defer conn.Close()
var natHoleSidMsg msg.NatHoleSid
err := msg.ReadMsgInto(conn, &natHoleSidMsg)
if err != nil {
pxy.Error("xtcp read from workConn error: %v", err)
xl.Error("xtcp read from workConn error: %v", err)
return
}
@ -259,7 +263,7 @@ func (pxy *XtcpProxy) InWorkConn(conn frpNet.Conn, m *msg.StartWorkConn) {
err = msg.WriteMsg(clientConn, natHoleClientMsg)
if err != nil {
pxy.Error("send natHoleClientMsg to server error: %v", err)
xl.Error("send natHoleClientMsg to server error: %v", err)
return
}
@ -270,28 +274,28 @@ func (pxy *XtcpProxy) InWorkConn(conn frpNet.Conn, m *msg.StartWorkConn) {
buf := pool.GetBuf(1024)
n, err := clientConn.Read(buf)
if err != nil {
pxy.Error("get natHoleRespMsg error: %v", err)
xl.Error("get natHoleRespMsg error: %v", err)
return
}
err = msg.ReadMsgInto(bytes.NewReader(buf[:n]), &natHoleRespMsg)
if err != nil {
pxy.Error("get natHoleRespMsg error: %v", err)
xl.Error("get natHoleRespMsg error: %v", err)
return
}
clientConn.SetReadDeadline(time.Time{})
clientConn.Close()
if natHoleRespMsg.Error != "" {
pxy.Error("natHoleRespMsg get error info: %s", natHoleRespMsg.Error)
xl.Error("natHoleRespMsg get error info: %s", natHoleRespMsg.Error)
return
}
pxy.Trace("get natHoleRespMsg, sid [%s], client address [%s] visitor address [%s]", natHoleRespMsg.Sid, natHoleRespMsg.ClientAddr, natHoleRespMsg.VisitorAddr)
xl.Trace("get natHoleRespMsg, sid [%s], client address [%s] visitor address [%s]", natHoleRespMsg.Sid, natHoleRespMsg.ClientAddr, natHoleRespMsg.VisitorAddr)
// Send detect message
array := strings.Split(natHoleRespMsg.VisitorAddr, ":")
if len(array) <= 1 {
pxy.Error("get NatHoleResp visitor address error: %v", natHoleRespMsg.VisitorAddr)
xl.Error("get NatHoleResp visitor address error: %v", natHoleRespMsg.VisitorAddr)
}
laddr, _ := net.ResolveUDPAddr("udp", clientConn.LocalAddr().String())
/*
@ -301,18 +305,18 @@ func (pxy *XtcpProxy) InWorkConn(conn frpNet.Conn, m *msg.StartWorkConn) {
*/
port, err := strconv.ParseInt(array[1], 10, 64)
if err != nil {
pxy.Error("get natHoleResp visitor address error: %v", natHoleRespMsg.VisitorAddr)
xl.Error("get natHoleResp visitor address error: %v", natHoleRespMsg.VisitorAddr)
return
}
pxy.sendDetectMsg(array[0], int(port), laddr, []byte(natHoleRespMsg.Sid))
pxy.Trace("send all detect msg done")
xl.Trace("send all detect msg done")
msg.WriteMsg(conn, &msg.NatHoleClientDetectOK{})
// Listen for clientConn's address and wait for visitor connection
lConn, err := net.ListenUDP("udp", laddr)
if err != nil {
pxy.Error("listen on visitorConn's local adress error: %v", err)
xl.Error("listen on visitorConn's local adress error: %v", err)
return
}
defer lConn.Close()
@ -322,22 +326,22 @@ func (pxy *XtcpProxy) InWorkConn(conn frpNet.Conn, m *msg.StartWorkConn) {
var uAddr *net.UDPAddr
n, uAddr, err = lConn.ReadFromUDP(sidBuf)
if err != nil {
pxy.Warn("get sid from visitor error: %v", err)
xl.Warn("get sid from visitor error: %v", err)
return
}
lConn.SetReadDeadline(time.Time{})
if string(sidBuf[:n]) != natHoleRespMsg.Sid {
pxy.Warn("incorrect sid from visitor")
xl.Warn("incorrect sid from visitor")
return
}
pool.PutBuf(sidBuf)
pxy.Info("nat hole connection make success, sid [%s]", natHoleRespMsg.Sid)
xl.Info("nat hole connection make success, sid [%s]", natHoleRespMsg.Sid)
lConn.WriteToUDP(sidBuf[:n], uAddr)
kcpConn, err := frpNet.NewKcpConnFromUdp(lConn, false, natHoleRespMsg.VisitorAddr)
if err != nil {
pxy.Error("create kcp connection from udp connection error: %v", err)
xl.Error("create kcp connection from udp connection error: %v", err)
return
}
@ -346,18 +350,18 @@ func (pxy *XtcpProxy) InWorkConn(conn frpNet.Conn, m *msg.StartWorkConn) {
fmuxCfg.LogOutput = ioutil.Discard
sess, err := fmux.Server(kcpConn, fmuxCfg)
if err != nil {
pxy.Error("create yamux server from kcp connection error: %v", err)
xl.Error("create yamux server from kcp connection error: %v", err)
return
}
defer sess.Close()
muxConn, err := sess.Accept()
if err != nil {
pxy.Error("accept for yamux connection error: %v", err)
xl.Error("accept for yamux connection error: %v", err)
return
}
HandleTcpWorkConnection(&pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf,
frpNet.WrapConn(muxConn), []byte(pxy.cfg.Sk), m)
HandleTcpWorkConnection(pxy.ctx, &pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf,
muxConn, []byte(pxy.cfg.Sk), m)
}
func (pxy *XtcpProxy) sendDetectMsg(addr string, port int, laddr *net.UDPAddr, content []byte) (err error) {
@ -390,7 +394,7 @@ type UdpProxy struct {
// include msg.UdpPacket and msg.Ping
sendCh chan msg.Message
workConn frpNet.Conn
workConn net.Conn
}
func (pxy *UdpProxy) Run() (err error) {
@ -419,8 +423,9 @@ func (pxy *UdpProxy) Close() {
}
}
func (pxy *UdpProxy) InWorkConn(conn frpNet.Conn, m *msg.StartWorkConn) {
pxy.Info("incoming a new work connection for udp proxy, %s", conn.RemoteAddr().String())
func (pxy *UdpProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) {
xl := pxy.xl
xl.Info("incoming a new work connection for udp proxy, %s", conn.RemoteAddr().String())
// close resources releated with old workConn
pxy.Close()
@ -435,32 +440,32 @@ func (pxy *UdpProxy) InWorkConn(conn frpNet.Conn, m *msg.StartWorkConn) {
for {
var udpMsg msg.UdpPacket
if errRet := msg.ReadMsgInto(conn, &udpMsg); errRet != nil {
pxy.Warn("read from workConn for udp error: %v", errRet)
xl.Warn("read from workConn for udp error: %v", errRet)
return
}
if errRet := errors.PanicToError(func() {
pxy.Trace("get udp package from workConn: %s", udpMsg.Content)
xl.Trace("get udp package from workConn: %s", udpMsg.Content)
readCh <- &udpMsg
}); errRet != nil {
pxy.Info("reader goroutine for udp work connection closed: %v", errRet)
xl.Info("reader goroutine for udp work connection closed: %v", errRet)
return
}
}
}
workConnSenderFn := func(conn net.Conn, sendCh chan msg.Message) {
defer func() {
pxy.Info("writer goroutine for udp work connection closed")
xl.Info("writer goroutine for udp work connection closed")
}()
var errRet error
for rawMsg := range sendCh {
switch m := rawMsg.(type) {
case *msg.UdpPacket:
pxy.Trace("send udp package to workConn: %s", m.Content)
xl.Trace("send udp package to workConn: %s", m.Content)
case *msg.Ping:
pxy.Trace("send ping message to udp workConn")
xl.Trace("send ping message to udp workConn")
}
if errRet = msg.WriteMsg(conn, rawMsg); errRet != nil {
pxy.Error("udp work write error: %v", errRet)
xl.Error("udp work write error: %v", errRet)
return
}
}
@ -472,7 +477,7 @@ func (pxy *UdpProxy) InWorkConn(conn frpNet.Conn, m *msg.StartWorkConn) {
if errRet = errors.PanicToError(func() {
sendCh <- &msg.Ping{}
}); errRet != nil {
pxy.Trace("heartbeat goroutine for udp work connection closed")
xl.Trace("heartbeat goroutine for udp work connection closed")
break
}
}
@ -485,20 +490,22 @@ func (pxy *UdpProxy) InWorkConn(conn frpNet.Conn, m *msg.StartWorkConn) {
}
// Common handler for tcp work connections.
func HandleTcpWorkConnection(localInfo *config.LocalSvrConf, proxyPlugin plugin.Plugin,
baseInfo *config.BaseProxyConf, workConn frpNet.Conn, encKey []byte, m *msg.StartWorkConn) {
func HandleTcpWorkConnection(ctx context.Context, localInfo *config.LocalSvrConf, proxyPlugin plugin.Plugin,
baseInfo *config.BaseProxyConf, workConn net.Conn, encKey []byte, m *msg.StartWorkConn) {
xl := xlog.FromContextSafe(ctx)
var (
remote io.ReadWriteCloser
err error
)
remote = workConn
xl.Trace("handle tcp work connection, use_encryption: %t, use_compression: %t",
baseInfo.UseEncryption, baseInfo.UseCompression)
if baseInfo.UseEncryption {
remote, err = frpIo.WithEncryption(remote, encKey)
if err != nil {
workConn.Close()
workConn.Error("create encryption stream error: %v", err)
xl.Error("create encryption stream error: %v", err)
return
}
}
@ -541,19 +548,19 @@ func HandleTcpWorkConnection(localInfo *config.LocalSvrConf, proxyPlugin plugin.
if proxyPlugin != nil {
// if plugin is set, let plugin handle connections first
workConn.Debug("handle by plugin: %s", proxyPlugin.Name())
xl.Debug("handle by plugin: %s", proxyPlugin.Name())
proxyPlugin.Handle(remote, workConn, extraInfo)
workConn.Debug("handle by plugin finished")
xl.Debug("handle by plugin finished")
return
} else {
localConn, err := frpNet.ConnectServer("tcp", fmt.Sprintf("%s:%d", localInfo.LocalIp, localInfo.LocalPort))
if err != nil {
workConn.Close()
workConn.Error("connect to local service [%s:%d] error: %v", localInfo.LocalIp, localInfo.LocalPort, err)
xl.Error("connect to local service [%s:%d] error: %v", localInfo.LocalIp, localInfo.LocalPort, err)
return
}
workConn.Debug("join connections, localConn(l[%s] r[%s]) workConn(l[%s] r[%s])", localConn.LocalAddr().String(),
xl.Debug("join connections, localConn(l[%s] r[%s]) workConn(l[%s] r[%s])", localConn.LocalAddr().String(),
localConn.RemoteAddr().String(), workConn.LocalAddr().String(), workConn.RemoteAddr().String())
if len(extraInfo) > 0 {
@ -561,6 +568,6 @@ func HandleTcpWorkConnection(localInfo *config.LocalSvrConf, proxyPlugin plugin.
}
frpIo.Join(localConn, remote)
workConn.Debug("join connections closed")
xl.Debug("join connections closed")
}
}

View File

@ -1,14 +1,15 @@
package proxy
import (
"context"
"fmt"
"net"
"sync"
"github.com/fatedier/frp/client/event"
"github.com/fatedier/frp/models/config"
"github.com/fatedier/frp/models/msg"
"github.com/fatedier/frp/utils/log"
frpNet "github.com/fatedier/frp/utils/net"
"github.com/fatedier/frp/utils/xlog"
"github.com/fatedier/golib/errors"
)
@ -25,19 +26,17 @@ type ProxyManager struct {
// The UDP port that the server is listening on
serverUDPPort int
logPrefix string
log.Logger
ctx context.Context
}
func NewProxyManager(msgSendCh chan (msg.Message), logPrefix string, clientCfg config.ClientCommonConf, serverUDPPort int) *ProxyManager {
func NewProxyManager(ctx context.Context, msgSendCh chan (msg.Message), clientCfg config.ClientCommonConf, serverUDPPort int) *ProxyManager {
return &ProxyManager{
proxies: make(map[string]*ProxyWrapper),
sendCh: msgSendCh,
proxies: make(map[string]*ProxyWrapper),
closed: false,
clientCfg: clientCfg,
serverUDPPort: serverUDPPort,
logPrefix: logPrefix,
Logger: log.NewPrefixLogger(logPrefix),
ctx: ctx,
}
}
@ -65,7 +64,7 @@ func (pm *ProxyManager) Close() {
pm.proxies = make(map[string]*ProxyWrapper)
}
func (pm *ProxyManager) HandleWorkConn(name string, workConn frpNet.Conn, m *msg.StartWorkConn) {
func (pm *ProxyManager) HandleWorkConn(name string, workConn net.Conn, m *msg.StartWorkConn) {
pm.mu.RLock()
pw, ok := pm.proxies[name]
pm.mu.RUnlock()
@ -104,6 +103,7 @@ func (pm *ProxyManager) GetAllProxyStatus() []*ProxyStatus {
}
func (pm *ProxyManager) Reload(pxyCfgs map[string]config.ProxyConf) {
xl := xlog.FromContextSafe(pm.ctx)
pm.mu.Lock()
defer pm.mu.Unlock()
@ -127,13 +127,13 @@ func (pm *ProxyManager) Reload(pxyCfgs map[string]config.ProxyConf) {
}
}
if len(delPxyNames) > 0 {
pm.Info("proxy removed: %v", delPxyNames)
xl.Info("proxy removed: %v", delPxyNames)
}
addPxyNames := make([]string, 0)
for name, cfg := range pxyCfgs {
if _, ok := pm.proxies[name]; !ok {
pxy := NewProxyWrapper(cfg, pm.clientCfg, pm.HandleEvent, pm.logPrefix, pm.serverUDPPort)
pxy := NewProxyWrapper(pm.ctx, cfg, pm.clientCfg, pm.HandleEvent, pm.serverUDPPort)
pm.proxies[name] = pxy
addPxyNames = append(addPxyNames, name)
@ -141,6 +141,6 @@ func (pm *ProxyManager) Reload(pxyCfgs map[string]config.ProxyConf) {
}
}
if len(addPxyNames) > 0 {
pm.Info("proxy added: %v", addPxyNames)
xl.Info("proxy added: %v", addPxyNames)
}
}

View File

@ -1,7 +1,9 @@
package proxy
import (
"context"
"fmt"
"net"
"sync"
"sync/atomic"
"time"
@ -10,8 +12,7 @@ import (
"github.com/fatedier/frp/client/health"
"github.com/fatedier/frp/models/config"
"github.com/fatedier/frp/models/msg"
"github.com/fatedier/frp/utils/log"
frpNet "github.com/fatedier/frp/utils/net"
"github.com/fatedier/frp/utils/xlog"
"github.com/fatedier/golib/errors"
)
@ -62,11 +63,13 @@ type ProxyWrapper struct {
healthNotifyCh chan struct{}
mu sync.RWMutex
log.Logger
xl *xlog.Logger
ctx context.Context
}
func NewProxyWrapper(cfg config.ProxyConf, clientCfg config.ClientCommonConf, eventHandler event.EventHandler, logPrefix string, serverUDPPort int) *ProxyWrapper {
func NewProxyWrapper(ctx context.Context, cfg config.ProxyConf, clientCfg config.ClientCommonConf, eventHandler event.EventHandler, serverUDPPort int) *ProxyWrapper {
baseInfo := cfg.GetBaseInfo()
xl := xlog.FromContextSafe(ctx).Spawn().AppendPrefix(baseInfo.ProxyName)
pw := &ProxyWrapper{
ProxyStatus: ProxyStatus{
Name: baseInfo.ProxyName,
@ -77,20 +80,19 @@ func NewProxyWrapper(cfg config.ProxyConf, clientCfg config.ClientCommonConf, ev
closeCh: make(chan struct{}),
healthNotifyCh: make(chan struct{}),
handler: eventHandler,
Logger: log.NewPrefixLogger(logPrefix),
xl: xl,
ctx: xlog.NewContext(ctx, xl),
}
pw.AddLogPrefix(pw.Name)
if baseInfo.HealthCheckType != "" {
pw.health = 1 // means failed
pw.monitor = health.NewHealthCheckMonitor(baseInfo.HealthCheckType, baseInfo.HealthCheckIntervalS,
pw.monitor = health.NewHealthCheckMonitor(pw.ctx, baseInfo.HealthCheckType, baseInfo.HealthCheckIntervalS,
baseInfo.HealthCheckTimeoutS, baseInfo.HealthCheckMaxFailed, baseInfo.HealthCheckAddr,
baseInfo.HealthCheckUrl, pw.statusNormalCallback, pw.statusFailedCallback)
pw.monitor.SetLogger(pw.Logger)
pw.Trace("enable health check monitor")
xl.Trace("enable health check monitor")
}
pw.pxy = NewProxy(pw.Cfg, clientCfg, serverUDPPort)
pw.pxy = NewProxy(pw.ctx, pw.Cfg, clientCfg, serverUDPPort)
return pw
}
@ -147,6 +149,7 @@ func (pw *ProxyWrapper) Stop() {
}
func (pw *ProxyWrapper) checkWorker() {
xl := pw.xl
if pw.monitor != nil {
// let monitor do check request first
time.Sleep(500 * time.Millisecond)
@ -161,7 +164,7 @@ func (pw *ProxyWrapper) checkWorker() {
(pw.Status == ProxyStatusWaitStart && now.After(pw.lastSendStartMsg.Add(waitResponseTimeout))) ||
(pw.Status == ProxyStatusStartErr && now.After(pw.lastStartErr.Add(startErrTimeout))) {
pw.Trace("change status from [%s] to [%s]", pw.Status, ProxyStatusWaitStart)
xl.Trace("change status from [%s] to [%s]", pw.Status, ProxyStatusWaitStart)
pw.Status = ProxyStatusWaitStart
var newProxyMsg msg.NewProxy
@ -180,7 +183,7 @@ func (pw *ProxyWrapper) checkWorker() {
ProxyName: pw.Name,
},
})
pw.Trace("change status from [%s] to [%s]", pw.Status, ProxyStatusCheckFailed)
xl.Trace("change status from [%s] to [%s]", pw.Status, ProxyStatusCheckFailed)
pw.Status = ProxyStatusCheckFailed
}
pw.mu.Unlock()
@ -196,6 +199,7 @@ func (pw *ProxyWrapper) checkWorker() {
}
func (pw *ProxyWrapper) statusNormalCallback() {
xl := pw.xl
atomic.StoreUint32(&pw.health, 0)
errors.PanicToError(func() {
select {
@ -203,10 +207,11 @@ func (pw *ProxyWrapper) statusNormalCallback() {
default:
}
})
pw.Info("health check success")
xl.Info("health check success")
}
func (pw *ProxyWrapper) statusFailedCallback() {
xl := pw.xl
atomic.StoreUint32(&pw.health, 1)
errors.PanicToError(func() {
select {
@ -214,15 +219,16 @@ func (pw *ProxyWrapper) statusFailedCallback() {
default:
}
})
pw.Info("health check failed")
xl.Info("health check failed")
}
func (pw *ProxyWrapper) InWorkConn(workConn frpNet.Conn, m *msg.StartWorkConn) {
func (pw *ProxyWrapper) InWorkConn(workConn net.Conn, m *msg.StartWorkConn) {
xl := pw.xl
pw.mu.RLock()
pxy := pw.pxy
pw.mu.RUnlock()
if pxy != nil {
workConn.Debug("start a new work connection, localAddr: %s remoteAddr: %s", workConn.LocalAddr().String(), workConn.RemoteAddr().String())
xl.Debug("start a new work connection, localAddr: %s remoteAddr: %s", workConn.LocalAddr().String(), workConn.RemoteAddr().String())
go pxy.InWorkConn(workConn, m)
} else {
workConn.Close()

View File

@ -15,9 +15,11 @@
package client
import (
"context"
"crypto/tls"
"fmt"
"io/ioutil"
"net"
"runtime"
"sync"
"sync/atomic"
@ -30,6 +32,7 @@ import (
frpNet "github.com/fatedier/frp/utils/net"
"github.com/fatedier/frp/utils/util"
"github.com/fatedier/frp/utils/version"
"github.com/fatedier/frp/utils/xlog"
fmux "github.com/hashicorp/yamux"
)
@ -55,19 +58,25 @@ type Service struct {
// This is configured by the login response from frps
serverUDPPort int
exit uint32 // 0 means not exit
closedCh chan int
exit uint32 // 0 means not exit
// service context
ctx context.Context
// call cancel to stop service
cancel context.CancelFunc
}
// NewService creates a new client service with the given configuration.
func NewService(cfg config.ClientCommonConf, pxyCfgs map[string]config.ProxyConf, visitorCfgs map[string]config.VisitorConf, cfgFile string) (svr *Service, err error) {
ctx, cancel := context.WithCancel(context.Background())
svr = &Service{
cfg: cfg,
cfgFile: cfgFile,
pxyCfgs: pxyCfgs,
visitorCfgs: visitorCfgs,
exit: 0,
closedCh: make(chan int),
ctx: xlog.NewContext(ctx, xlog.New()),
cancel: cancel,
}
return
}
@ -79,11 +88,13 @@ func (svr *Service) GetController() *Control {
}
func (svr *Service) Run() error {
// first login
xl := xlog.FromContextSafe(svr.ctx)
// login to frps
for {
conn, session, err := svr.login()
if err != nil {
log.Warn("login to server failed: %v", err)
xl.Warn("login to server failed: %v", err)
// if login_fail_exit is true, just exit this program
// otherwise sleep a while and try again to connect to server
@ -94,7 +105,7 @@ func (svr *Service) Run() error {
}
} else {
// login success
ctl := NewControl(svr.runId, conn, session, svr.cfg, svr.pxyCfgs, svr.visitorCfgs, svr.serverUDPPort)
ctl := NewControl(svr.ctx, svr.runId, conn, session, svr.cfg, svr.pxyCfgs, svr.visitorCfgs, svr.serverUDPPort)
ctl.Run()
svr.ctlMu.Lock()
svr.ctl = ctl
@ -118,12 +129,12 @@ func (svr *Service) Run() error {
}
log.Info("admin server listen on %s:%d", svr.cfg.AdminAddr, svr.cfg.AdminPort)
}
<-svr.closedCh
<-svr.ctx.Done()
return nil
}
func (svr *Service) keepControllerWorking() {
xl := xlog.FromContextSafe(svr.ctx)
maxDelayTime := 20 * time.Second
delayTime := time.Second
@ -134,10 +145,10 @@ func (svr *Service) keepControllerWorking() {
}
for {
log.Info("try to reconnect to server...")
xl.Info("try to reconnect to server...")
conn, session, err := svr.login()
if err != nil {
log.Warn("reconnect to server error: %v", err)
xl.Warn("reconnect to server error: %v", err)
time.Sleep(delayTime)
delayTime = delayTime * 2
if delayTime > maxDelayTime {
@ -148,7 +159,7 @@ func (svr *Service) keepControllerWorking() {
// reconnect success, init delayTime
delayTime = time.Second
ctl := NewControl(svr.runId, conn, session, svr.cfg, svr.pxyCfgs, svr.visitorCfgs, svr.serverUDPPort)
ctl := NewControl(svr.ctx, svr.runId, conn, session, svr.cfg, svr.pxyCfgs, svr.visitorCfgs, svr.serverUDPPort)
ctl.Run()
svr.ctlMu.Lock()
svr.ctl = ctl
@ -161,7 +172,8 @@ func (svr *Service) keepControllerWorking() {
// login creates a connection to frps and registers it self as a client
// conn: control connection
// session: if it's not nil, using tcp mux
func (svr *Service) login() (conn frpNet.Conn, session *fmux.Session, err error) {
func (svr *Service) login() (conn net.Conn, session *fmux.Session, err error) {
xl := xlog.FromContextSafe(svr.ctx)
var tlsConfig *tls.Config
if svr.cfg.TLSEnable {
tlsConfig = &tls.Config{
@ -197,7 +209,7 @@ func (svr *Service) login() (conn frpNet.Conn, session *fmux.Session, err error)
err = errRet
return
}
conn = frpNet.WrapConn(stream)
conn = stream
}
now := time.Now().Unix()
@ -225,13 +237,16 @@ func (svr *Service) login() (conn frpNet.Conn, session *fmux.Session, err error)
if loginRespMsg.Error != "" {
err = fmt.Errorf("%s", loginRespMsg.Error)
log.Error("%s", loginRespMsg.Error)
xl.Error("%s", loginRespMsg.Error)
return
}
svr.runId = loginRespMsg.RunId
xl.ResetPrefixes()
xl.AppendPrefix(svr.runId)
svr.serverUDPPort = loginRespMsg.ServerUdpPort
log.Info("login to server success, get run id [%s], server udp port [%d]", loginRespMsg.RunId, loginRespMsg.ServerUdpPort)
xl.Info("login to server success, get run id [%s], server udp port [%d]", loginRespMsg.RunId, loginRespMsg.ServerUdpPort)
return
}
@ -247,5 +262,5 @@ func (svr *Service) ReloadConf(pxyCfgs map[string]config.ProxyConf, visitorCfgs
func (svr *Service) Close() {
atomic.StoreUint32(&svr.exit, 1)
svr.ctl.Close()
close(svr.closedCh)
svr.cancel()
}

View File

@ -16,6 +16,7 @@ package client
import (
"bytes"
"context"
"fmt"
"io"
"io/ioutil"
@ -25,9 +26,9 @@ import (
"github.com/fatedier/frp/models/config"
"github.com/fatedier/frp/models/msg"
"github.com/fatedier/frp/utils/log"
frpNet "github.com/fatedier/frp/utils/net"
"github.com/fatedier/frp/utils/util"
"github.com/fatedier/frp/utils/xlog"
frpIo "github.com/fatedier/golib/io"
"github.com/fatedier/golib/pool"
@ -38,13 +39,13 @@ import (
type Visitor interface {
Run() error
Close()
log.Logger
}
func NewVisitor(ctl *Control, cfg config.VisitorConf) (visitor Visitor) {
func NewVisitor(ctx context.Context, ctl *Control, cfg config.VisitorConf) (visitor Visitor) {
xl := xlog.FromContextSafe(ctx).Spawn().AppendPrefix(cfg.GetBaseInfo().ProxyName)
baseVisitor := BaseVisitor{
ctl: ctl,
Logger: log.NewPrefixLogger(cfg.GetBaseInfo().ProxyName),
ctl: ctl,
ctx: xlog.NewContext(ctx, xl),
}
switch cfg := cfg.(type) {
case *config.StcpVisitorConf:
@ -63,10 +64,11 @@ func NewVisitor(ctl *Control, cfg config.VisitorConf) (visitor Visitor) {
type BaseVisitor struct {
ctl *Control
l frpNet.Listener
l net.Listener
closed bool
mu sync.RWMutex
log.Logger
mu sync.RWMutex
ctx context.Context
}
type StcpVisitor struct {
@ -76,7 +78,7 @@ type StcpVisitor struct {
}
func (sv *StcpVisitor) Run() (err error) {
sv.l, err = frpNet.ListenTcp(sv.cfg.BindAddr, sv.cfg.BindPort)
sv.l, err = net.Listen("tcp", fmt.Sprintf("%s:%d", sv.cfg.BindAddr, sv.cfg.BindPort))
if err != nil {
return
}
@ -90,10 +92,11 @@ func (sv *StcpVisitor) Close() {
}
func (sv *StcpVisitor) worker() {
xl := xlog.FromContextSafe(sv.ctx)
for {
conn, err := sv.l.Accept()
if err != nil {
sv.Warn("stcp local listener closed")
xl.Warn("stcp local listener closed")
return
}
@ -101,10 +104,11 @@ func (sv *StcpVisitor) worker() {
}
}
func (sv *StcpVisitor) handleConn(userConn frpNet.Conn) {
func (sv *StcpVisitor) handleConn(userConn net.Conn) {
xl := xlog.FromContextSafe(sv.ctx)
defer userConn.Close()
sv.Debug("get a new stcp user connection")
xl.Debug("get a new stcp user connection")
visitorConn, err := sv.ctl.connectServer()
if err != nil {
return
@ -121,7 +125,7 @@ func (sv *StcpVisitor) handleConn(userConn frpNet.Conn) {
}
err = msg.WriteMsg(visitorConn, newVisitorConnMsg)
if err != nil {
sv.Warn("send newVisitorConnMsg to server error: %v", err)
xl.Warn("send newVisitorConnMsg to server error: %v", err)
return
}
@ -129,13 +133,13 @@ func (sv *StcpVisitor) handleConn(userConn frpNet.Conn) {
visitorConn.SetReadDeadline(time.Now().Add(10 * time.Second))
err = msg.ReadMsgInto(visitorConn, &newVisitorConnRespMsg)
if err != nil {
sv.Warn("get newVisitorConnRespMsg error: %v", err)
xl.Warn("get newVisitorConnRespMsg error: %v", err)
return
}
visitorConn.SetReadDeadline(time.Time{})
if newVisitorConnRespMsg.Error != "" {
sv.Warn("start new visitor connection error: %s", newVisitorConnRespMsg.Error)
xl.Warn("start new visitor connection error: %s", newVisitorConnRespMsg.Error)
return
}
@ -144,7 +148,7 @@ func (sv *StcpVisitor) handleConn(userConn frpNet.Conn) {
if sv.cfg.UseEncryption {
remote, err = frpIo.WithEncryption(remote, []byte(sv.cfg.Sk))
if err != nil {
sv.Error("create encryption stream error: %v", err)
xl.Error("create encryption stream error: %v", err)
return
}
}
@ -163,7 +167,7 @@ type XtcpVisitor struct {
}
func (sv *XtcpVisitor) Run() (err error) {
sv.l, err = frpNet.ListenTcp(sv.cfg.BindAddr, sv.cfg.BindPort)
sv.l, err = net.Listen("tcp", fmt.Sprintf("%s:%d", sv.cfg.BindAddr, sv.cfg.BindPort))
if err != nil {
return
}
@ -177,10 +181,11 @@ func (sv *XtcpVisitor) Close() {
}
func (sv *XtcpVisitor) worker() {
xl := xlog.FromContextSafe(sv.ctx)
for {
conn, err := sv.l.Accept()
if err != nil {
sv.Warn("xtcp local listener closed")
xl.Warn("xtcp local listener closed")
return
}
@ -188,25 +193,26 @@ func (sv *XtcpVisitor) worker() {
}
}
func (sv *XtcpVisitor) handleConn(userConn frpNet.Conn) {
func (sv *XtcpVisitor) handleConn(userConn net.Conn) {
xl := xlog.FromContextSafe(sv.ctx)
defer userConn.Close()
sv.Debug("get a new xtcp user connection")
xl.Debug("get a new xtcp user connection")
if sv.ctl.serverUDPPort == 0 {
sv.Error("xtcp is not supported by server")
xl.Error("xtcp is not supported by server")
return
}
raddr, err := net.ResolveUDPAddr("udp",
fmt.Sprintf("%s:%d", sv.ctl.clientCfg.ServerAddr, sv.ctl.serverUDPPort))
if err != nil {
sv.Error("resolve server UDP addr error")
xl.Error("resolve server UDP addr error")
return
}
visitorConn, err := net.DialUDP("udp", nil, raddr)
if err != nil {
sv.Warn("dial server udp addr error: %v", err)
xl.Warn("dial server udp addr error: %v", err)
return
}
defer visitorConn.Close()
@ -219,7 +225,7 @@ func (sv *XtcpVisitor) handleConn(userConn frpNet.Conn) {
}
err = msg.WriteMsg(visitorConn, natHoleVisitorMsg)
if err != nil {
sv.Warn("send natHoleVisitorMsg to server error: %v", err)
xl.Warn("send natHoleVisitorMsg to server error: %v", err)
return
}
@ -229,24 +235,24 @@ func (sv *XtcpVisitor) handleConn(userConn frpNet.Conn) {
buf := pool.GetBuf(1024)
n, err := visitorConn.Read(buf)
if err != nil {
sv.Warn("get natHoleRespMsg error: %v", err)
xl.Warn("get natHoleRespMsg error: %v", err)
return
}
err = msg.ReadMsgInto(bytes.NewReader(buf[:n]), &natHoleRespMsg)
if err != nil {
sv.Warn("get natHoleRespMsg error: %v", err)
xl.Warn("get natHoleRespMsg error: %v", err)
return
}
visitorConn.SetReadDeadline(time.Time{})
pool.PutBuf(buf)
if natHoleRespMsg.Error != "" {
sv.Error("natHoleRespMsg get error info: %s", natHoleRespMsg.Error)
xl.Error("natHoleRespMsg get error info: %s", natHoleRespMsg.Error)
return
}
sv.Trace("get natHoleRespMsg, sid [%s], client address [%s], visitor address [%s]", natHoleRespMsg.Sid, natHoleRespMsg.ClientAddr, natHoleRespMsg.VisitorAddr)
xl.Trace("get natHoleRespMsg, sid [%s], client address [%s], visitor address [%s]", natHoleRespMsg.Sid, natHoleRespMsg.ClientAddr, natHoleRespMsg.VisitorAddr)
// Close visitorConn, so we can use it's local address.
visitorConn.Close()
@ -255,12 +261,12 @@ func (sv *XtcpVisitor) handleConn(userConn frpNet.Conn) {
laddr, _ := net.ResolveUDPAddr("udp", visitorConn.LocalAddr().String())
daddr, err := net.ResolveUDPAddr("udp", natHoleRespMsg.ClientAddr)
if err != nil {
sv.Error("resolve client udp address error: %v", err)
xl.Error("resolve client udp address error: %v", err)
return
}
lConn, err := net.DialUDP("udp", laddr, daddr)
if err != nil {
sv.Error("dial client udp address error: %v", err)
xl.Error("dial client udp address error: %v", err)
return
}
defer lConn.Close()
@ -272,23 +278,23 @@ func (sv *XtcpVisitor) handleConn(userConn frpNet.Conn) {
lConn.SetReadDeadline(time.Now().Add(8 * time.Second))
n, err = lConn.Read(sidBuf)
if err != nil {
sv.Warn("get sid from client error: %v", err)
xl.Warn("get sid from client error: %v", err)
return
}
lConn.SetReadDeadline(time.Time{})
if string(sidBuf[:n]) != natHoleRespMsg.Sid {
sv.Warn("incorrect sid from client")
xl.Warn("incorrect sid from client")
return
}
pool.PutBuf(sidBuf)
sv.Info("nat hole connection make success, sid [%s]", natHoleRespMsg.Sid)
xl.Info("nat hole connection make success, sid [%s]", natHoleRespMsg.Sid)
// wrap kcp connection
var remote io.ReadWriteCloser
remote, err = frpNet.NewKcpConnFromUdp(lConn, true, natHoleRespMsg.ClientAddr)
if err != nil {
sv.Error("create kcp connection from udp connection error: %v", err)
xl.Error("create kcp connection from udp connection error: %v", err)
return
}
@ -297,13 +303,13 @@ func (sv *XtcpVisitor) handleConn(userConn frpNet.Conn) {
fmuxCfg.LogOutput = ioutil.Discard
sess, err := fmux.Client(remote, fmuxCfg)
if err != nil {
sv.Error("create yamux session error: %v", err)
xl.Error("create yamux session error: %v", err)
return
}
defer sess.Close()
muxConn, err := sess.Open()
if err != nil {
sv.Error("open yamux stream error: %v", err)
xl.Error("open yamux stream error: %v", err)
return
}
@ -311,7 +317,7 @@ func (sv *XtcpVisitor) handleConn(userConn frpNet.Conn) {
if sv.cfg.UseEncryption {
muxConnRWCloser, err = frpIo.WithEncryption(muxConnRWCloser, []byte(sv.cfg.Sk))
if err != nil {
sv.Error("create encryption stream error: %v", err)
xl.Error("create encryption stream error: %v", err)
return
}
}
@ -320,5 +326,5 @@ func (sv *XtcpVisitor) handleConn(userConn frpNet.Conn) {
}
frpIo.Join(userConn, muxConnRWCloser)
sv.Debug("join connections closed")
xl.Debug("join connections closed")
}

View File

@ -15,11 +15,12 @@
package client
import (
"context"
"sync"
"time"
"github.com/fatedier/frp/models/config"
"github.com/fatedier/frp/utils/log"
"github.com/fatedier/frp/utils/xlog"
)
type VisitorManager struct {
@ -30,26 +31,29 @@ type VisitorManager struct {
checkInterval time.Duration
mu sync.Mutex
mu sync.Mutex
ctx context.Context
}
func NewVisitorManager(ctl *Control) *VisitorManager {
func NewVisitorManager(ctx context.Context, ctl *Control) *VisitorManager {
return &VisitorManager{
ctl: ctl,
cfgs: make(map[string]config.VisitorConf),
visitors: make(map[string]Visitor),
checkInterval: 10 * time.Second,
ctx: ctx,
}
}
func (vm *VisitorManager) Run() {
xl := xlog.FromContextSafe(vm.ctx)
for {
time.Sleep(vm.checkInterval)
vm.mu.Lock()
for _, cfg := range vm.cfgs {
name := cfg.GetBaseInfo().ProxyName
if _, exist := vm.visitors[name]; !exist {
log.Info("try to start visitor [%s]", name)
xl.Info("try to start visitor [%s]", name)
vm.startVisitor(cfg)
}
}
@ -59,19 +63,21 @@ func (vm *VisitorManager) Run() {
// Hold lock before calling this function.
func (vm *VisitorManager) startVisitor(cfg config.VisitorConf) (err error) {
xl := xlog.FromContextSafe(vm.ctx)
name := cfg.GetBaseInfo().ProxyName
visitor := NewVisitor(vm.ctl, cfg)
visitor := NewVisitor(vm.ctx, vm.ctl, cfg)
err = visitor.Run()
if err != nil {
visitor.Warn("start error: %v", err)
xl.Warn("start error: %v", err)
} else {
vm.visitors[name] = visitor
visitor.Info("start visitor success")
xl.Info("start visitor success")
}
return
}
func (vm *VisitorManager) Reload(cfgs map[string]config.VisitorConf) {
xl := xlog.FromContextSafe(vm.ctx)
vm.mu.Lock()
defer vm.mu.Unlock()
@ -97,7 +103,7 @@ func (vm *VisitorManager) Reload(cfgs map[string]config.VisitorConf) {
}
}
if len(delNames) > 0 {
log.Info("visitor removed: %v", delNames)
xl.Info("visitor removed: %v", delNames)
}
addNames := make([]string, 0)
@ -109,7 +115,7 @@ func (vm *VisitorManager) Reload(cfgs map[string]config.VisitorConf) {
}
}
if len(addNames) > 0 {
log.Info("visitor added: %v", addNames)
xl.Info("visitor added: %v", addNames)
}
return
}

View File

@ -202,7 +202,7 @@ func runServer(cfg config.ServerCommonConf) (err error) {
if err != nil {
return err
}
log.Info("Start frps success")
log.Info("start frps success")
svr.Run()
return
}

14
go.sum
View File

@ -1,14 +1,10 @@
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fatedier/beego v0.0.0-20171024143340-6c6a4f5bd5eb h1:wCrNShQidLmvVWn/0PikGmpdP0vtQmnvyRg3ZBEhczw=
github.com/fatedier/beego v0.0.0-20171024143340-6c6a4f5bd5eb/go.mod h1:wx3gB6dbIfBRcucp94PI9Bt3I0F2c/MyNEWuhzpWiwk=
github.com/fatedier/golib v0.0.0-20181107124048-ff8cd814b049 h1:teH578mf2ii42NHhIp3PhgvjU5bv+NFMq9fSQR8NaG8=
github.com/fatedier/golib v0.0.0-20181107124048-ff8cd814b049/go.mod h1:DqIrnl0rp3Zybg9zbJmozTy1n8fYJoX+QoAj9slIkKM=
github.com/fatedier/kcp-go v2.0.4-0.20190803094908-fe8645b0a904+incompatible h1:ssXat9YXFvigNge/IkkZvFMn8yeYKFX+uI6wn2mLJ74=
github.com/fatedier/kcp-go v2.0.4-0.20190803094908-fe8645b0a904+incompatible/go.mod h1:YpCOaxj7vvMThhIQ9AfTOPW2sfztQR5WDfs7AflSy4s=
github.com/golang/snappy v0.0.0-20170215233205-553a64147049 h1:K9KHZbXKpGydfDN0aZrsoHpLJlZsBrGMFWbgLDGnPZk=
github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw=
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
@ -26,28 +22,18 @@ github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/pires/go-proxyproto v0.0.0-20190111085350-4d51b51e3bfc h1:lNOt1SMsgHXTdpuGw+RpnJtzUcCb/oRKZP65pBy9pr8=
github.com/pires/go-proxyproto v0.0.0-20190111085350-4d51b51e3bfc/go.mod h1:6/gX3+E/IYGa0wMORlSMla999awQFdbaeQCHjSMKIzY=
github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rakyll/statik v0.1.1 h1:fCLHsIMajHqD5RKigbFXpvX3dN7c80Pm12+NCrI3kvg=
github.com/rakyll/statik v0.1.1/go.mod h1:OEi9wJV/fMUAGx1eNjq75DKDsJVuEv1U0oYdX6GX8Zs=
github.com/rodaine/table v1.0.0 h1:UaCJG5Axc/cNXVGXqnCrffm1KxP0OfYLe1HuJLf5sFY=
github.com/rodaine/table v1.0.0/go.mod h1:YAUzwPOji0DUJNEvggdxyQcUAl4g3hDRcFlyjnnR51I=
github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/pflag v1.0.1 h1:aCvUg6QPl3ibpQUxyLkrEkCHtPqYJL4x9AuhqVqFis4=
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/templexxx/cpufeat v0.0.0-20170927014610-3794dfbfb047 h1:K+jtWCOuZgCra7eXZ/VWn2FbJmrA/D058mTXhh2rq+8=
github.com/templexxx/cpufeat v0.0.0-20170927014610-3794dfbfb047/go.mod h1:wM7WEvslTq+iOEAMDLSzhVuOt5BRZ05WirO+b09GHQU=
github.com/templexxx/xor v0.0.0-20170926022130-0af8e873c554 h1:pexgSe+JCFuxG+uoMZLO+ce8KHtdHGhst4cs6rw3gmk=
github.com/templexxx/xor v0.0.0-20170926022130-0af8e873c554/go.mod h1:5XA7W9S6mni3h5uvOC75dA3m9CCCaS83lltmc0ukdi4=
github.com/tjfoc/gmsm v0.0.0-20171124023159-98aa888b79d8 h1:6CNSDqI1wiE+JqyOy5Qt/yo/DoNI2/QmmOZeiCid2Nw=
github.com/tjfoc/gmsm v0.0.0-20171124023159-98aa888b79d8/go.mod h1:XxO4hdhhrzAd+G4CjDqaOkd0hUzmtPR/d3EiBBMn/wc=
github.com/vaughan0/go-ini v0.0.0-20130923145212-a98ad7ee00ec h1:DGmKwyZwEB8dI7tbLt/I/gQuP559o/0FrAkHKlQM/Ks=
github.com/vaughan0/go-ini v0.0.0-20130923145212-a98ad7ee00ec/go.mod h1:owBmyHYMLkxyrugmfwE/DLJyW8Ro9mkphwuVErQ0iUw=
github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae h1:J0GxkO96kL4WF+AIT3M4mfUVinOCPgf2uUWYFUzN0sM=
github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae/go.mod h1:gXtu8J62kEgmN++bm9BVICuT/e8yiLI2KFobd/TRFsE=

View File

@ -64,7 +64,7 @@ func (hp *HttpProxy) Name() string {
return PluginHttpProxy
}
func (hp *HttpProxy) Handle(conn io.ReadWriteCloser, realConn frpNet.Conn, extraBufToLocal []byte) {
func (hp *HttpProxy) Handle(conn io.ReadWriteCloser, realConn net.Conn, extraBufToLocal []byte) {
wrapConn := frpNet.WrapReadWriteCloserToConn(conn, realConn)
sc, rd := gnet.NewSharedConn(wrapConn)

View File

@ -18,6 +18,7 @@ import (
"crypto/tls"
"fmt"
"io"
"net"
"net/http"
"net/http/httputil"
"strings"
@ -115,7 +116,7 @@ func (p *HTTPS2HTTPPlugin) genTLSConfig() (*tls.Config, error) {
return config, nil
}
func (p *HTTPS2HTTPPlugin) Handle(conn io.ReadWriteCloser, realConn frpNet.Conn, extraBufToLocal []byte) {
func (p *HTTPS2HTTPPlugin) Handle(conn io.ReadWriteCloser, realConn net.Conn, extraBufToLocal []byte) {
wrapConn := frpNet.WrapReadWriteCloserToConn(conn, realConn)
p.l.PutConn(wrapConn)
}

View File

@ -20,8 +20,6 @@ import (
"net"
"sync"
frpNet "github.com/fatedier/frp/utils/net"
"github.com/fatedier/golib/errors"
)
@ -46,7 +44,9 @@ func Create(name string, params map[string]string) (p Plugin, err error) {
type Plugin interface {
Name() string
Handle(conn io.ReadWriteCloser, realConn frpNet.Conn, extraBufToLocal []byte)
// extraBufToLocal will send to local connection first, then join conn with local connection
Handle(conn io.ReadWriteCloser, realConn net.Conn, extraBufToLocal []byte)
Close() error
}

View File

@ -18,6 +18,7 @@ import (
"io"
"io/ioutil"
"log"
"net"
frpNet "github.com/fatedier/frp/utils/net"
@ -53,7 +54,7 @@ func NewSocks5Plugin(params map[string]string) (p Plugin, err error) {
return
}
func (sp *Socks5Plugin) Handle(conn io.ReadWriteCloser, realConn frpNet.Conn, extraBufToLocal []byte) {
func (sp *Socks5Plugin) Handle(conn io.ReadWriteCloser, realConn net.Conn, extraBufToLocal []byte) {
defer conn.Close()
wrapConn := frpNet.WrapReadWriteCloserToConn(conn, realConn)
sp.Server.ServeConn(wrapConn)

View File

@ -16,6 +16,7 @@ package plugin
import (
"io"
"net"
"net/http"
frpNet "github.com/fatedier/frp/utils/net"
@ -72,7 +73,7 @@ func NewStaticFilePlugin(params map[string]string) (Plugin, error) {
return sp, nil
}
func (sp *StaticFilePlugin) Handle(conn io.ReadWriteCloser, realConn frpNet.Conn, extraBufToLocal []byte) {
func (sp *StaticFilePlugin) Handle(conn io.ReadWriteCloser, realConn net.Conn, extraBufToLocal []byte) {
wrapConn := frpNet.WrapReadWriteCloserToConn(conn, realConn)
sp.l.PutConn(wrapConn)
}

View File

@ -19,8 +19,6 @@ import (
"io"
"net"
frpNet "github.com/fatedier/frp/utils/net"
frpIo "github.com/fatedier/golib/io"
)
@ -53,7 +51,7 @@ func NewUnixDomainSocketPlugin(params map[string]string) (p Plugin, err error) {
return
}
func (uds *UnixDomainSocketPlugin) Handle(conn io.ReadWriteCloser, realConn frpNet.Conn, extraBufToLocal []byte) {
func (uds *UnixDomainSocketPlugin) Handle(conn io.ReadWriteCloser, realConn net.Conn, extraBufToLocal []byte) {
localConn, err := net.DialUnix("unix", nil, uds.UnixAddr)
if err != nil {
return

View File

@ -15,8 +15,10 @@
package server
import (
"context"
"fmt"
"io"
"net"
"runtime/debug"
"sync"
"time"
@ -28,8 +30,8 @@ import (
"github.com/fatedier/frp/server/controller"
"github.com/fatedier/frp/server/proxy"
"github.com/fatedier/frp/server/stats"
"github.com/fatedier/frp/utils/net"
"github.com/fatedier/frp/utils/version"
"github.com/fatedier/frp/utils/xlog"
"github.com/fatedier/golib/control/shutdown"
"github.com/fatedier/golib/crypto"
@ -131,9 +133,12 @@ type Control struct {
// Server configuration information
serverCfg config.ServerCommonConf
xl *xlog.Logger
ctx context.Context
}
func NewControl(rc *controller.ResourceController, pxyManager *proxy.ProxyManager,
func NewControl(ctx context.Context, rc *controller.ResourceController, pxyManager *proxy.ProxyManager,
statsCollector stats.Collector, ctlConn net.Conn, loginMsg *msg.Login,
serverCfg config.ServerCommonConf) *Control {
@ -161,6 +166,8 @@ func NewControl(rc *controller.ResourceController, pxyManager *proxy.ProxyManage
managerShutdown: shutdown.New(),
allShutdown: shutdown.New(),
serverCfg: serverCfg,
xl: xlog.FromContextSafe(ctx),
ctx: ctx,
}
}
@ -185,18 +192,19 @@ func (ctl *Control) Start() {
}
func (ctl *Control) RegisterWorkConn(conn net.Conn) {
xl := ctl.xl
defer func() {
if err := recover(); err != nil {
ctl.conn.Error("panic error: %v", err)
ctl.conn.Error(string(debug.Stack()))
xl.Error("panic error: %v", err)
xl.Error(string(debug.Stack()))
}
}()
select {
case ctl.workConnCh <- conn:
ctl.conn.Debug("new work connection registered")
xl.Debug("new work connection registered")
default:
ctl.conn.Debug("work connection pool is full, discarding")
xl.Debug("work connection pool is full, discarding")
conn.Close()
}
}
@ -206,10 +214,11 @@ func (ctl *Control) RegisterWorkConn(conn net.Conn) {
// and wait until it is available.
// return an error if wait timeout
func (ctl *Control) GetWorkConn() (workConn net.Conn, err error) {
xl := ctl.xl
defer func() {
if err := recover(); err != nil {
ctl.conn.Error("panic error: %v", err)
ctl.conn.Error(string(debug.Stack()))
xl.Error("panic error: %v", err)
xl.Error(string(debug.Stack()))
}
}()
@ -221,14 +230,14 @@ func (ctl *Control) GetWorkConn() (workConn net.Conn, err error) {
err = frpErr.ErrCtlClosed
return
}
ctl.conn.Debug("get work connection from pool")
xl.Debug("get work connection from pool")
default:
// no work connections available in the poll, send message to frpc to get more
err = errors.PanicToError(func() {
ctl.sendCh <- &msg.ReqWorkConn{}
})
if err != nil {
ctl.conn.Error("%v", err)
xl.Error("%v", err)
return
}
@ -236,13 +245,13 @@ func (ctl *Control) GetWorkConn() (workConn net.Conn, err error) {
case workConn, ok = <-ctl.workConnCh:
if !ok {
err = frpErr.ErrCtlClosed
ctl.conn.Warn("no work connections avaiable, %v", err)
xl.Warn("no work connections avaiable, %v", err)
return
}
case <-time.After(time.Duration(ctl.serverCfg.UserConnTimeout) * time.Second):
err = fmt.Errorf("timeout trying to get work connection")
ctl.conn.Warn("%v", err)
xl.Warn("%v", err)
return
}
}
@ -255,16 +264,18 @@ func (ctl *Control) GetWorkConn() (workConn net.Conn, err error) {
}
func (ctl *Control) Replaced(newCtl *Control) {
ctl.conn.Info("Replaced by client [%s]", newCtl.runId)
xl := ctl.xl
xl.Info("Replaced by client [%s]", newCtl.runId)
ctl.runId = ""
ctl.allShutdown.Start()
}
func (ctl *Control) writer() {
xl := ctl.xl
defer func() {
if err := recover(); err != nil {
ctl.conn.Error("panic error: %v", err)
ctl.conn.Error(string(debug.Stack()))
xl.Error("panic error: %v", err)
xl.Error(string(debug.Stack()))
}
}()
@ -273,17 +284,17 @@ func (ctl *Control) writer() {
encWriter, err := crypto.NewWriter(ctl.conn, []byte(ctl.serverCfg.Token))
if err != nil {
ctl.conn.Error("crypto new writer error: %v", err)
xl.Error("crypto new writer error: %v", err)
ctl.allShutdown.Start()
return
}
for {
if m, ok := <-ctl.sendCh; !ok {
ctl.conn.Info("control writer is closing")
xl.Info("control writer is closing")
return
} else {
if err := msg.WriteMsg(encWriter, m); err != nil {
ctl.conn.Warn("write message to control connection error: %v", err)
xl.Warn("write message to control connection error: %v", err)
return
}
}
@ -291,10 +302,11 @@ func (ctl *Control) writer() {
}
func (ctl *Control) reader() {
xl := ctl.xl
defer func() {
if err := recover(); err != nil {
ctl.conn.Error("panic error: %v", err)
ctl.conn.Error(string(debug.Stack()))
xl.Error("panic error: %v", err)
xl.Error(string(debug.Stack()))
}
}()
@ -305,10 +317,10 @@ func (ctl *Control) reader() {
for {
if m, err := msg.ReadMsg(encReader); err != nil {
if err == io.EOF {
ctl.conn.Debug("control connection closed")
xl.Debug("control connection closed")
return
} else {
ctl.conn.Warn("read error: %v", err)
xl.Warn("read error: %v", err)
ctl.conn.Close()
return
}
@ -319,10 +331,11 @@ func (ctl *Control) reader() {
}
func (ctl *Control) stoper() {
xl := ctl.xl
defer func() {
if err := recover(); err != nil {
ctl.conn.Error("panic error: %v", err)
ctl.conn.Error(string(debug.Stack()))
xl.Error("panic error: %v", err)
xl.Error(string(debug.Stack()))
}
}()
@ -355,7 +368,7 @@ func (ctl *Control) stoper() {
}
ctl.allShutdown.Done()
ctl.conn.Info("client exit success")
xl.Info("client exit success")
ctl.statsCollector.Mark(stats.TypeCloseClient, &stats.CloseClientPayload{})
}
@ -366,10 +379,11 @@ func (ctl *Control) WaitClosed() {
}
func (ctl *Control) manager() {
xl := ctl.xl
defer func() {
if err := recover(); err != nil {
ctl.conn.Error("panic error: %v", err)
ctl.conn.Error(string(debug.Stack()))
xl.Error("panic error: %v", err)
xl.Error(string(debug.Stack()))
}
}()
@ -383,7 +397,7 @@ func (ctl *Control) manager() {
select {
case <-heartbeat.C:
if time.Since(ctl.lastPing) > time.Duration(ctl.serverCfg.HeartBeatTimeout)*time.Second {
ctl.conn.Warn("heartbeat timeout")
xl.Warn("heartbeat timeout")
return
}
case rawMsg, ok := <-ctl.readCh:
@ -400,10 +414,10 @@ func (ctl *Control) manager() {
}
if err != nil {
resp.Error = err.Error()
ctl.conn.Warn("new proxy [%s] error: %v", m.ProxyName, err)
xl.Warn("new proxy [%s] error: %v", m.ProxyName, err)
} else {
resp.RemoteAddr = remoteAddr
ctl.conn.Info("new proxy [%s] success", m.ProxyName)
xl.Info("new proxy [%s] success", m.ProxyName)
ctl.statsCollector.Mark(stats.TypeNewProxy, &stats.NewProxyPayload{
Name: m.ProxyName,
ProxyType: m.ProxyType,
@ -412,10 +426,10 @@ func (ctl *Control) manager() {
ctl.sendCh <- resp
case *msg.CloseProxy:
ctl.CloseProxy(m)
ctl.conn.Info("close proxy [%s] success", m.ProxyName)
xl.Info("close proxy [%s] success", m.ProxyName)
case *msg.Ping:
ctl.lastPing = time.Now()
ctl.conn.Debug("receive heartbeat")
xl.Debug("receive heartbeat")
ctl.sendCh <- &msg.Pong{}
}
}
@ -432,7 +446,7 @@ func (ctl *Control) RegisterProxy(pxyMsg *msg.NewProxy) (remoteAddr string, err
// NewProxy will return a interface Proxy.
// In fact it create different proxies by different proxy type, we just call run() here.
pxy, err := proxy.NewProxy(ctl.runId, ctl.rc, ctl.statsCollector, ctl.poolCount, ctl.GetWorkConn, pxyConf, ctl.serverCfg)
pxy, err := proxy.NewProxy(ctl.ctx, ctl.runId, ctl.rc, ctl.statsCollector, ctl.poolCount, ctl.GetWorkConn, pxyConf, ctl.serverCfg)
if err != nil {
return remoteAddr, err
}

View File

@ -17,6 +17,7 @@ package controller
import (
"fmt"
"io"
"net"
"sync"
frpNet "github.com/fatedier/frp/utils/net"
@ -55,7 +56,7 @@ func (vm *VisitorManager) Listen(name string, sk string) (l *frpNet.CustomListen
return
}
func (vm *VisitorManager) NewConn(name string, conn frpNet.Conn, timestamp int64, signKey string,
func (vm *VisitorManager) NewConn(name string, conn net.Conn, timestamp int64, signKey string,
useEncryption bool, useCompression bool) (err error) {
vm.mu.RLock()

View File

@ -2,11 +2,10 @@ package group
import (
"fmt"
"net"
"sync"
"sync/atomic"
frpNet "github.com/fatedier/frp/utils/net"
"github.com/fatedier/frp/utils/vhost"
)
@ -131,7 +130,7 @@ func (g *HTTPGroup) UnRegister(proxyName string) (isEmpty bool) {
return
}
func (g *HTTPGroup) createConn(remoteAddr string) (frpNet.Conn, error) {
func (g *HTTPGroup) createConn(remoteAddr string) (net.Conn, error) {
var f vhost.CreateConnFunc
newIndex := atomic.AddUint64(&g.index, 1)

View File

@ -36,6 +36,7 @@ type HttpProxy struct {
}
func (pxy *HttpProxy) Run() (remoteAddr string, err error) {
xl := pxy.xl
routeConfig := vhost.VhostRouteConfig{
RewriteHost: pxy.cfg.HostHeaderRewrite,
Headers: pxy.cfg.Headers,
@ -88,7 +89,7 @@ func (pxy *HttpProxy) Run() (remoteAddr string, err error) {
})
}
addrs = append(addrs, util.CanonicalAddr(routeConfig.Domain, int(pxy.serverCfg.VhostHttpPort)))
pxy.Info("http proxy listen for host [%s] location [%s] group [%s]", routeConfig.Domain, routeConfig.Location, pxy.cfg.Group)
xl.Info("http proxy listen for host [%s] location [%s] group [%s]", routeConfig.Domain, routeConfig.Location, pxy.cfg.Group)
}
}
@ -120,7 +121,7 @@ func (pxy *HttpProxy) Run() (remoteAddr string, err error) {
}
addrs = append(addrs, util.CanonicalAddr(tmpDomain, pxy.serverCfg.VhostHttpPort))
pxy.Info("http proxy listen for host [%s] location [%s] group [%s]", routeConfig.Domain, routeConfig.Location, pxy.cfg.Group)
xl.Info("http proxy listen for host [%s] location [%s] group [%s]", routeConfig.Domain, routeConfig.Location, pxy.cfg.Group)
}
}
remoteAddr = strings.Join(addrs, ",")
@ -131,10 +132,11 @@ func (pxy *HttpProxy) GetConf() config.ProxyConf {
return pxy.cfg
}
func (pxy *HttpProxy) GetRealConn(remoteAddr string) (workConn frpNet.Conn, err error) {
func (pxy *HttpProxy) GetRealConn(remoteAddr string) (workConn net.Conn, err error) {
xl := pxy.xl
rAddr, errRet := net.ResolveTCPAddr("tcp", remoteAddr)
if errRet != nil {
pxy.Warn("resolve TCP addr [%s] error: %v", remoteAddr, errRet)
xl.Warn("resolve TCP addr [%s] error: %v", remoteAddr, errRet)
// we do not return error here since remoteAddr is not necessary for proxies without proxy protocol enabled
}
@ -148,7 +150,7 @@ func (pxy *HttpProxy) GetRealConn(remoteAddr string) (workConn frpNet.Conn, err
if pxy.cfg.UseEncryption {
rwc, err = frpIo.WithEncryption(rwc, []byte(pxy.serverCfg.Token))
if err != nil {
pxy.Error("create encryption stream error: %v", err)
xl.Error("create encryption stream error: %v", err)
return
}
}

View File

@ -28,6 +28,7 @@ type HttpsProxy struct {
}
func (pxy *HttpsProxy) Run() (remoteAddr string, err error) {
xl := pxy.xl
routeConfig := &vhost.VhostRouteConfig{}
defer func() {
@ -42,26 +43,24 @@ func (pxy *HttpsProxy) Run() (remoteAddr string, err error) {
}
routeConfig.Domain = domain
l, errRet := pxy.rc.VhostHttpsMuxer.Listen(routeConfig)
l, errRet := pxy.rc.VhostHttpsMuxer.Listen(pxy.ctx, routeConfig)
if errRet != nil {
err = errRet
return
}
l.AddLogPrefix(pxy.name)
pxy.Info("https proxy listen for host [%s]", routeConfig.Domain)
xl.Info("https proxy listen for host [%s]", routeConfig.Domain)
pxy.listeners = append(pxy.listeners, l)
addrs = append(addrs, util.CanonicalAddr(routeConfig.Domain, pxy.serverCfg.VhostHttpsPort))
}
if pxy.cfg.SubDomain != "" {
routeConfig.Domain = pxy.cfg.SubDomain + "." + pxy.serverCfg.SubDomainHost
l, errRet := pxy.rc.VhostHttpsMuxer.Listen(routeConfig)
l, errRet := pxy.rc.VhostHttpsMuxer.Listen(pxy.ctx, routeConfig)
if errRet != nil {
err = errRet
return
}
l.AddLogPrefix(pxy.name)
pxy.Info("https proxy listen for host [%s]", routeConfig.Domain)
xl.Info("https proxy listen for host [%s]", routeConfig.Domain)
pxy.listeners = append(pxy.listeners, l)
addrs = append(addrs, util.CanonicalAddr(routeConfig.Domain, int(pxy.serverCfg.VhostHttpsPort)))
}

View File

@ -15,6 +15,7 @@
package proxy
import (
"context"
"fmt"
"io"
"net"
@ -25,48 +26,54 @@ import (
"github.com/fatedier/frp/models/msg"
"github.com/fatedier/frp/server/controller"
"github.com/fatedier/frp/server/stats"
"github.com/fatedier/frp/utils/log"
frpNet "github.com/fatedier/frp/utils/net"
"github.com/fatedier/frp/utils/xlog"
frpIo "github.com/fatedier/golib/io"
)
type GetWorkConnFn func() (frpNet.Conn, error)
type GetWorkConnFn func() (net.Conn, error)
type Proxy interface {
Context() context.Context
Run() (remoteAddr string, err error)
GetName() string
GetConf() config.ProxyConf
GetWorkConnFromPool(src, dst net.Addr) (workConn frpNet.Conn, err error)
GetWorkConnFromPool(src, dst net.Addr) (workConn net.Conn, err error)
GetUsedPortsNum() int
Close()
log.Logger
}
type BaseProxy struct {
name string
rc *controller.ResourceController
statsCollector stats.Collector
listeners []frpNet.Listener
listeners []net.Listener
usedPortsNum int
poolCount int
getWorkConnFn GetWorkConnFn
serverCfg config.ServerCommonConf
mu sync.RWMutex
log.Logger
mu sync.RWMutex
xl *xlog.Logger
ctx context.Context
}
func (pxy *BaseProxy) GetName() string {
return pxy.name
}
func (pxy *BaseProxy) Context() context.Context {
return pxy.ctx
}
func (pxy *BaseProxy) GetUsedPortsNum() int {
return pxy.usedPortsNum
}
func (pxy *BaseProxy) Close() {
pxy.Info("proxy closing")
xl := xlog.FromContextSafe(pxy.ctx)
xl.Info("proxy closing")
for _, l := range pxy.listeners {
l.Close()
}
@ -74,15 +81,17 @@ func (pxy *BaseProxy) Close() {
// GetWorkConnFromPool try to get a new work connections from pool
// for quickly response, we immediately send the StartWorkConn message to frpc after take out one from pool
func (pxy *BaseProxy) GetWorkConnFromPool(src, dst net.Addr) (workConn frpNet.Conn, err error) {
func (pxy *BaseProxy) GetWorkConnFromPool(src, dst net.Addr) (workConn net.Conn, err error) {
xl := xlog.FromContextSafe(pxy.ctx)
// try all connections from the pool
for i := 0; i < pxy.poolCount+1; i++ {
if workConn, err = pxy.getWorkConnFn(); err != nil {
pxy.Warn("failed to get work connection: %v", err)
xl.Warn("failed to get work connection: %v", err)
return
}
pxy.Info("get a new work connection: [%s]", workConn.RemoteAddr().String())
workConn.AddLogPrefix(pxy.GetName())
xl.Info("get a new work connection: [%s]", workConn.RemoteAddr().String())
xl.Spawn().AppendPrefix(pxy.GetName())
workConn = frpNet.NewContextConn(workConn, pxy.ctx)
var (
srcAddr string
@ -109,7 +118,7 @@ func (pxy *BaseProxy) GetWorkConnFromPool(src, dst net.Addr) (workConn frpNet.Co
DstPort: uint16(dstPort),
})
if err != nil {
workConn.Warn("failed to send message to work connection from pool: %v, times: %d", err, i)
xl.Warn("failed to send message to work connection from pool: %v, times: %d", err, i)
workConn.Close()
} else {
break
@ -117,7 +126,7 @@ func (pxy *BaseProxy) GetWorkConnFromPool(src, dst net.Addr) (workConn frpNet.Co
}
if err != nil {
pxy.Error("try to get work connection failed in the end")
xl.Error("try to get work connection failed in the end")
return
}
return
@ -126,36 +135,39 @@ func (pxy *BaseProxy) GetWorkConnFromPool(src, dst net.Addr) (workConn frpNet.Co
// startListenHandler start a goroutine handler for each listener.
// p: p will just be passed to handler(Proxy, frpNet.Conn).
// handler: each proxy type can set different handler function to deal with connections accepted from listeners.
func (pxy *BaseProxy) startListenHandler(p Proxy, handler func(Proxy, frpNet.Conn, stats.Collector, config.ServerCommonConf)) {
func (pxy *BaseProxy) startListenHandler(p Proxy, handler func(Proxy, net.Conn, stats.Collector, config.ServerCommonConf)) {
xl := xlog.FromContextSafe(pxy.ctx)
for _, listener := range pxy.listeners {
go func(l frpNet.Listener) {
go func(l net.Listener) {
for {
// block
// if listener is closed, err returned
c, err := l.Accept()
if err != nil {
pxy.Info("listener is closed")
xl.Info("listener is closed")
return
}
pxy.Debug("get a user connection [%s]", c.RemoteAddr().String())
xl.Debug("get a user connection [%s]", c.RemoteAddr().String())
go handler(p, c, pxy.statsCollector, pxy.serverCfg)
}
}(listener)
}
}
func NewProxy(runId string, rc *controller.ResourceController, statsCollector stats.Collector, poolCount int,
func NewProxy(ctx context.Context, runId string, rc *controller.ResourceController, statsCollector stats.Collector, poolCount int,
getWorkConnFn GetWorkConnFn, pxyConf config.ProxyConf, serverCfg config.ServerCommonConf) (pxy Proxy, err error) {
xl := xlog.FromContextSafe(ctx).Spawn().AppendPrefix(pxyConf.GetBaseInfo().ProxyName)
basePxy := BaseProxy{
name: pxyConf.GetBaseInfo().ProxyName,
rc: rc,
statsCollector: statsCollector,
listeners: make([]frpNet.Listener, 0),
listeners: make([]net.Listener, 0),
poolCount: poolCount,
getWorkConnFn: getWorkConnFn,
Logger: log.NewPrefixLogger(runId),
serverCfg: serverCfg,
xl: xl,
ctx: xlog.NewContext(ctx, xl),
}
switch cfg := pxyConf.(type) {
case *config.TcpProxyConf:
@ -193,13 +205,13 @@ func NewProxy(runId string, rc *controller.ResourceController, statsCollector st
default:
return pxy, fmt.Errorf("proxy type not support")
}
pxy.AddLogPrefix(pxy.GetName())
return
}
// HandleUserTcpConnection is used for incoming tcp user connections.
// It can be used for tcp, http, https type.
func HandleUserTcpConnection(pxy Proxy, userConn frpNet.Conn, statsCollector stats.Collector, serverCfg config.ServerCommonConf) {
func HandleUserTcpConnection(pxy Proxy, userConn net.Conn, statsCollector stats.Collector, serverCfg config.ServerCommonConf) {
xl := xlog.FromContextSafe(pxy.Context())
defer userConn.Close()
// try all connections from the pool
@ -211,17 +223,18 @@ func HandleUserTcpConnection(pxy Proxy, userConn frpNet.Conn, statsCollector sta
var local io.ReadWriteCloser = workConn
cfg := pxy.GetConf().GetBaseInfo()
xl.Trace("handler user tcp connection, use_encryption: %t, use_compression: %t", cfg.UseEncryption, cfg.UseCompression)
if cfg.UseEncryption {
local, err = frpIo.WithEncryption(local, []byte(serverCfg.Token))
if err != nil {
pxy.Error("create encryption stream error: %v", err)
xl.Error("create encryption stream error: %v", err)
return
}
}
if cfg.UseCompression {
local = frpIo.WithCompression(local)
}
pxy.Debug("join connections, workConn(l[%s] r[%s]) userConn(l[%s] r[%s])", workConn.LocalAddr().String(),
xl.Debug("join connections, workConn(l[%s] r[%s]) userConn(l[%s] r[%s])", workConn.LocalAddr().String(),
workConn.RemoteAddr().String(), userConn.LocalAddr().String(), userConn.RemoteAddr().String())
statsCollector.Mark(stats.TypeOpenConnection, &stats.OpenConnectionPayload{ProxyName: pxy.GetName()})
@ -235,7 +248,7 @@ func HandleUserTcpConnection(pxy Proxy, userConn frpNet.Conn, statsCollector sta
ProxyName: pxy.GetName(),
TrafficBytes: outCount,
})
pxy.Debug("join connections closed")
xl.Debug("join connections closed")
}
type ProxyManager struct {

View File

@ -24,14 +24,14 @@ type StcpProxy struct {
}
func (pxy *StcpProxy) Run() (remoteAddr string, err error) {
xl := pxy.xl
listener, errRet := pxy.rc.VisitorManager.Listen(pxy.GetName(), pxy.cfg.Sk)
if errRet != nil {
err = errRet
return
}
listener.AddLogPrefix(pxy.name)
pxy.listeners = append(pxy.listeners, listener)
pxy.Info("stcp proxy custom listen success")
xl.Info("stcp proxy custom listen success")
pxy.startListenHandler(pxy, HandleUserTcpConnection)
return

View File

@ -16,9 +16,9 @@ package proxy
import (
"fmt"
"net"
"github.com/fatedier/frp/models/config"
frpNet "github.com/fatedier/frp/utils/net"
)
type TcpProxy struct {
@ -29,6 +29,7 @@ type TcpProxy struct {
}
func (pxy *TcpProxy) Run() (remoteAddr string, err error) {
xl := pxy.xl
if pxy.cfg.Group != "" {
l, realPort, errRet := pxy.rc.TcpGroupCtl.Listen(pxy.name, pxy.cfg.Group, pxy.cfg.GroupKey, pxy.serverCfg.ProxyBindAddr, pxy.cfg.RemotePort)
if errRet != nil {
@ -41,10 +42,8 @@ func (pxy *TcpProxy) Run() (remoteAddr string, err error) {
}
}()
pxy.realPort = realPort
listener := frpNet.WrapLogListener(l)
listener.AddLogPrefix(pxy.name)
pxy.listeners = append(pxy.listeners, listener)
pxy.Info("tcp proxy listen port [%d] in group [%s]", pxy.cfg.RemotePort, pxy.cfg.Group)
pxy.listeners = append(pxy.listeners, l)
xl.Info("tcp proxy listen port [%d] in group [%s]", pxy.cfg.RemotePort, pxy.cfg.Group)
} else {
pxy.realPort, err = pxy.rc.TcpPortManager.Acquire(pxy.name, pxy.cfg.RemotePort)
if err != nil {
@ -55,14 +54,13 @@ func (pxy *TcpProxy) Run() (remoteAddr string, err error) {
pxy.rc.TcpPortManager.Release(pxy.realPort)
}
}()
listener, errRet := frpNet.ListenTcp(pxy.serverCfg.ProxyBindAddr, pxy.realPort)
listener, errRet := net.Listen("tcp", fmt.Sprintf("%s:%d", pxy.serverCfg.ProxyBindAddr, pxy.realPort))
if errRet != nil {
err = errRet
return
}
listener.AddLogPrefix(pxy.name)
pxy.listeners = append(pxy.listeners, listener)
pxy.Info("tcp proxy listen port [%d]", pxy.cfg.RemotePort)
xl.Info("tcp proxy listen port [%d]", pxy.cfg.RemotePort)
}
pxy.cfg.RemotePort = pxy.realPort

View File

@ -54,6 +54,7 @@ type UdpProxy struct {
}
func (pxy *UdpProxy) Run() (remoteAddr string, err error) {
xl := pxy.xl
pxy.realPort, err = pxy.rc.UdpPortManager.Acquire(pxy.name, pxy.cfg.RemotePort)
if err != nil {
return
@ -74,10 +75,10 @@ func (pxy *UdpProxy) Run() (remoteAddr string, err error) {
udpConn, errRet := net.ListenUDP("udp", addr)
if errRet != nil {
err = errRet
pxy.Warn("listen udp port error: %v", err)
xl.Warn("listen udp port error: %v", err)
return
}
pxy.Info("udp proxy listen port [%d]", pxy.cfg.RemotePort)
xl.Info("udp proxy listen port [%d]", pxy.cfg.RemotePort)
pxy.udpConn = udpConn
pxy.sendCh = make(chan *msg.UdpPacket, 1024)
@ -91,11 +92,11 @@ func (pxy *UdpProxy) Run() (remoteAddr string, err error) {
rawMsg msg.Message
errRet error
)
pxy.Trace("loop waiting message from udp workConn")
xl.Trace("loop waiting message from udp workConn")
// client will send heartbeat in workConn for keeping alive
conn.SetReadDeadline(time.Now().Add(time.Duration(60) * time.Second))
if rawMsg, errRet = msg.ReadMsg(conn); errRet != nil {
pxy.Warn("read from workConn for udp error: %v", errRet)
xl.Warn("read from workConn for udp error: %v", errRet)
conn.Close()
// notify proxy to start a new work connection
// ignore error here, it means the proxy is closed
@ -107,11 +108,11 @@ func (pxy *UdpProxy) Run() (remoteAddr string, err error) {
conn.SetReadDeadline(time.Time{})
switch m := rawMsg.(type) {
case *msg.Ping:
pxy.Trace("udp work conn get ping message")
xl.Trace("udp work conn get ping message")
continue
case *msg.UdpPacket:
if errRet := errors.PanicToError(func() {
pxy.Trace("get udp message from workConn: %s", m.Content)
xl.Trace("get udp message from workConn: %s", m.Content)
pxy.readCh <- m
pxy.statsCollector.Mark(stats.TypeAddTrafficOut, &stats.AddTrafficOutPayload{
ProxyName: pxy.GetName(),
@ -119,7 +120,7 @@ func (pxy *UdpProxy) Run() (remoteAddr string, err error) {
})
}); errRet != nil {
conn.Close()
pxy.Info("reader goroutine for udp work connection closed")
xl.Info("reader goroutine for udp work connection closed")
return
}
}
@ -133,15 +134,15 @@ func (pxy *UdpProxy) Run() (remoteAddr string, err error) {
select {
case udpMsg, ok := <-pxy.sendCh:
if !ok {
pxy.Info("sender goroutine for udp work connection closed")
xl.Info("sender goroutine for udp work connection closed")
return
}
if errRet = msg.WriteMsg(conn, udpMsg); errRet != nil {
pxy.Info("sender goroutine for udp work connection closed: %v", errRet)
xl.Info("sender goroutine for udp work connection closed: %v", errRet)
conn.Close()
return
} else {
pxy.Trace("send message to udp workConn: %s", udpMsg.Content)
xl.Trace("send message to udp workConn: %s", udpMsg.Content)
pxy.statsCollector.Mark(stats.TypeAddTrafficIn, &stats.AddTrafficInPayload{
ProxyName: pxy.GetName(),
TrafficBytes: int64(len(udpMsg.Content)),
@ -149,7 +150,7 @@ func (pxy *UdpProxy) Run() (remoteAddr string, err error) {
continue
}
case <-ctx.Done():
pxy.Info("sender goroutine for udp work connection closed")
xl.Info("sender goroutine for udp work connection closed")
return
}
}

View File

@ -31,8 +31,10 @@ type XtcpProxy struct {
}
func (pxy *XtcpProxy) Run() (remoteAddr string, err error) {
xl := pxy.xl
if pxy.rc.NatHoleController == nil {
pxy.Error("udp port for xtcp is not specified.")
xl.Error("udp port for xtcp is not specified.")
err = fmt.Errorf("xtcp is not supported in frps")
return
}
@ -53,7 +55,7 @@ func (pxy *XtcpProxy) Run() (remoteAddr string, err error) {
}
errRet = msg.WriteMsg(workConn, m)
if errRet != nil {
pxy.Warn("write nat hole sid package error, %v", errRet)
xl.Warn("write nat hole sid package error, %v", errRet)
workConn.Close()
break
}
@ -61,12 +63,12 @@ func (pxy *XtcpProxy) Run() (remoteAddr string, err error) {
go func() {
raw, errRet := msg.ReadMsg(workConn)
if errRet != nil {
pxy.Warn("read nat hole client ok package error: %v", errRet)
xl.Warn("read nat hole client ok package error: %v", errRet)
workConn.Close()
return
}
if _, ok := raw.(*msg.NatHoleClientDetectOK); !ok {
pxy.Warn("read nat hole client ok package format error")
xl.Warn("read nat hole client ok package format error")
workConn.Close()
return
}

View File

@ -16,6 +16,7 @@ package server
import (
"bytes"
"context"
"crypto/rand"
"crypto/rsa"
"crypto/tls"
@ -42,6 +43,7 @@ import (
"github.com/fatedier/frp/utils/util"
"github.com/fatedier/frp/utils/version"
"github.com/fatedier/frp/utils/vhost"
"github.com/fatedier/frp/utils/xlog"
"github.com/fatedier/golib/net/mux"
fmux "github.com/hashicorp/yamux"
@ -57,16 +59,16 @@ type Service struct {
muxer *mux.Mux
// Accept connections from client
listener frpNet.Listener
listener net.Listener
// Accept connections using kcp
kcpListener frpNet.Listener
kcpListener net.Listener
// Accept connections using websocket
websocketListener frpNet.Listener
websocketListener net.Listener
// Accept frp tls connections
tlsListener frpNet.Listener
tlsListener net.Listener
// Manage all controllers
ctlManager *ControlManager
@ -135,7 +137,7 @@ func NewService(cfg config.ServerCommonConf) (svr *Service, err error) {
go svr.muxer.Serve()
ln = svr.muxer.DefaultListener()
svr.listener = frpNet.WrapLogListener(ln)
svr.listener = ln
log.Info("frps tcp listen on %s:%d", cfg.BindAddr, cfg.BindPort)
// Listen for accepting connections from client using kcp protocol.
@ -194,7 +196,7 @@ func NewService(cfg config.ServerCommonConf) (svr *Service, err error) {
}
}
svr.rc.VhostHttpsMuxer, err = vhost.NewHttpsMuxer(frpNet.WrapLogListener(l), 30*time.Second)
svr.rc.VhostHttpsMuxer, err = vhost.NewHttpsMuxer(l, 30*time.Second)
if err != nil {
err = fmt.Errorf("Create vhost httpsMuxer error, %v", err)
return
@ -203,10 +205,9 @@ func NewService(cfg config.ServerCommonConf) (svr *Service, err error) {
}
// frp tls listener
tlsListener := svr.muxer.Listen(1, 1, func(data []byte) bool {
svr.tlsListener = svr.muxer.Listen(1, 1, func(data []byte) bool {
return int(data[0]) == frpNet.FRP_TLS_HEAD_BYTE
})
svr.tlsListener = frpNet.WrapLogListener(tlsListener)
// Create nat hole controller.
if cfg.BindUdpPort > 0 {
@ -258,7 +259,7 @@ func (svr *Service) Run() {
svr.HandleListener(svr.listener)
}
func (svr *Service) HandleListener(l frpNet.Listener) {
func (svr *Service) HandleListener(l net.Listener) {
// Listen for incoming connections from client.
for {
c, err := l.Accept()
@ -266,6 +267,9 @@ func (svr *Service) HandleListener(l frpNet.Listener) {
log.Warn("Listener for incoming connections from client closed")
return
}
// inject xlog object into net.Conn context
xl := xlog.New()
c = frpNet.NewContextConn(c, xlog.NewContext(context.Background(), xl))
log.Trace("start check TLS connection...")
originConn := c
@ -278,8 +282,8 @@ func (svr *Service) HandleListener(l frpNet.Listener) {
log.Trace("success check TLS connection")
// Start a new goroutine for dealing connections.
go func(frpConn frpNet.Conn) {
dealFn := func(conn frpNet.Conn) {
go func(frpConn net.Conn) {
dealFn := func(conn net.Conn) {
var rawMsg msg.Message
conn.SetReadDeadline(time.Now().Add(connReadTimeout))
if rawMsg, err = msg.ReadMsg(conn); err != nil {
@ -295,7 +299,7 @@ func (svr *Service) HandleListener(l frpNet.Listener) {
// If login failed, send error message there.
// Otherwise send success message in control's work goroutine.
if err != nil {
conn.Warn("%v", err)
xl.Warn("register control error: %v", err)
msg.WriteMsg(conn, &msg.LoginResp{
Version: version.Full(),
Error: err.Error(),
@ -306,7 +310,7 @@ func (svr *Service) HandleListener(l frpNet.Listener) {
svr.RegisterWorkConn(conn, m)
case *msg.NewVisitorConn:
if err = svr.RegisterVisitorConn(conn, m); err != nil {
conn.Warn("%v", err)
xl.Warn("register visitor conn error: %v", err)
msg.WriteMsg(conn, &msg.NewVisitorConnResp{
ProxyName: m.ProxyName,
Error: err.Error(),
@ -342,8 +346,7 @@ func (svr *Service) HandleListener(l frpNet.Listener) {
session.Close()
return
}
wrapConn := frpNet.WrapConn(stream)
go dealFn(wrapConn)
go dealFn(stream)
}
} else {
dealFn(frpConn)
@ -352,8 +355,21 @@ func (svr *Service) HandleListener(l frpNet.Listener) {
}
}
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]",
func (svr *Service) RegisterControl(ctlConn net.Conn, loginMsg *msg.Login) (err error) {
// 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.
if loginMsg.RunId == "" {
loginMsg.RunId, err = util.RandId()
if err != nil {
return
}
}
ctx := frpNet.NewContextFromConn(ctlConn)
xl := xlog.FromContextSafe(ctx)
xl.AppendPrefix(loginMsg.RunId)
ctx = xlog.NewContext(ctx, xl)
xl.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)
// Check client version.
@ -368,22 +384,12 @@ func (svr *Service) RegisterControl(ctlConn frpNet.Conn, loginMsg *msg.Login) (e
return
}
// 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.
if loginMsg.RunId == "" {
loginMsg.RunId, err = util.RandId()
if err != nil {
return
}
}
ctl := NewControl(svr.rc, svr.pxyManager, svr.statsCollector, ctlConn, loginMsg, svr.cfg)
ctl := NewControl(ctx, svr.rc, svr.pxyManager, svr.statsCollector, ctlConn, loginMsg, svr.cfg)
if oldCtl := svr.ctlManager.Add(loginMsg.RunId, ctl); oldCtl != nil {
oldCtl.allShutdown.WaitDone()
}
ctlConn.AddLogPrefix(loginMsg.RunId)
ctl.Start()
// for statistics
@ -398,17 +404,18 @@ func (svr *Service) RegisterControl(ctlConn frpNet.Conn, loginMsg *msg.Login) (e
}
// RegisterWorkConn register a new work connection to control and proxies need it.
func (svr *Service) RegisterWorkConn(workConn frpNet.Conn, newMsg *msg.NewWorkConn) {
func (svr *Service) RegisterWorkConn(workConn net.Conn, newMsg *msg.NewWorkConn) {
xl := frpNet.NewLogFromConn(workConn)
ctl, exist := svr.ctlManager.GetById(newMsg.RunId)
if !exist {
workConn.Warn("No client control found for run id [%s]", newMsg.RunId)
xl.Warn("No client control found for run id [%s]", newMsg.RunId)
return
}
ctl.RegisterWorkConn(workConn)
return
}
func (svr *Service) RegisterVisitorConn(visitorConn frpNet.Conn, newMsg *msg.NewVisitorConn) error {
func (svr *Service) RegisterVisitorConn(visitorConn net.Conn, newMsg *msg.NewVisitorConn) error {
return svr.rc.VisitorManager.NewConn(newMsg.ProxyName, visitorConn, newMsg.Timestamp, newMsg.SignKey,
newMsg.UseEncryption, newMsg.UseCompression)
}

View File

@ -2,8 +2,7 @@
server_addr = 127.0.0.1
server_port = 10700
log_file = console
# debug, info, warn, error
log_level = debug
log_level = trace
token = 123456
admin_port = 10600
admin_user = abc

View File

@ -2,8 +2,7 @@
bind_addr = 0.0.0.0
bind_port = 10700
vhost_http_port = 10804
log_file = console
log_level = debug
log_level = trace
token = 123456
allow_ports = 10000-20000,20002,30000-50000
subdomain_host = sub.com

View File

@ -11,7 +11,7 @@ import (
)
type EchoServer struct {
l frpNet.Listener
l net.Listener
port int
repeatedNum int
@ -30,7 +30,7 @@ func NewEchoServer(port int, repeatedNum int, specifyStr string) *EchoServer {
}
func (es *EchoServer) Start() error {
l, err := frpNet.ListenTcp("127.0.0.1", es.port)
l, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", es.port))
if err != nil {
fmt.Printf("echo server listen error: %v\n", err)
return err

View File

@ -14,7 +14,6 @@ import (
"time"
"github.com/fatedier/frp/client"
frpNet "github.com/fatedier/frp/utils/net"
)
func GetProxyStatus(statusAddr string, user string, passwd string, name string) (status *client.ProxyStatusResp, err error) {
@ -98,7 +97,7 @@ func ReloadConf(reloadAddr string, user string, passwd string) error {
}
func SendTcpMsg(addr string, msg string) (res string, err error) {
c, err := frpNet.ConnectTcpServer(addr)
c, err := net.Dial("tcp", addr)
if err != nil {
err = fmt.Errorf("connect to tcp server error: %v", err)
return

View File

@ -91,71 +91,3 @@ func Debug(format string, v ...interface{}) {
func Trace(format string, v ...interface{}) {
Log.Trace(format, v...)
}
// Logger is the log interface
type Logger interface {
AddLogPrefix(string)
GetPrefixStr() string
GetAllPrefix() []string
ClearLogPrefix()
Error(string, ...interface{})
Warn(string, ...interface{})
Info(string, ...interface{})
Debug(string, ...interface{})
Trace(string, ...interface{})
}
type PrefixLogger struct {
prefix string
allPrefix []string
}
func NewPrefixLogger(prefix string) *PrefixLogger {
logger := &PrefixLogger{
allPrefix: make([]string, 0),
}
logger.AddLogPrefix(prefix)
return logger
}
func (pl *PrefixLogger) AddLogPrefix(prefix string) {
if len(prefix) == 0 {
return
}
pl.prefix += "[" + prefix + "] "
pl.allPrefix = append(pl.allPrefix, prefix)
}
func (pl *PrefixLogger) GetPrefixStr() string {
return pl.prefix
}
func (pl *PrefixLogger) GetAllPrefix() []string {
return pl.allPrefix
}
func (pl *PrefixLogger) ClearLogPrefix() {
pl.prefix = ""
pl.allPrefix = make([]string, 0)
}
func (pl *PrefixLogger) Error(format string, v ...interface{}) {
Log.Error(pl.prefix+format, v...)
}
func (pl *PrefixLogger) Warn(format string, v ...interface{}) {
Log.Warn(pl.prefix+format, v...)
}
func (pl *PrefixLogger) Info(format string, v ...interface{}) {
Log.Info(pl.prefix+format, v...)
}
func (pl *PrefixLogger) Debug(format string, v ...interface{}) {
Log.Debug(pl.prefix+format, v...)
}
func (pl *PrefixLogger) Trace(format string, v ...interface{}) {
Log.Trace(pl.prefix+format, v...)
}

View File

@ -15,6 +15,7 @@
package net
import (
"context"
"crypto/tls"
"errors"
"fmt"
@ -23,41 +24,64 @@ import (
"sync/atomic"
"time"
"github.com/fatedier/frp/utils/log"
"github.com/fatedier/frp/utils/xlog"
gnet "github.com/fatedier/golib/net"
kcp "github.com/fatedier/kcp-go"
)
// Conn is the interface of connections used in frp.
type Conn interface {
net.Conn
log.Logger
type ContextGetter interface {
Context() context.Context
}
type WrapLogConn struct {
net.Conn
log.Logger
type ContextSetter interface {
WithContext(ctx context.Context)
}
func WrapConn(c net.Conn) Conn {
return &WrapLogConn{
Conn: c,
Logger: log.NewPrefixLogger(""),
func NewLogFromConn(conn net.Conn) *xlog.Logger {
if c, ok := conn.(ContextGetter); ok {
return xlog.FromContextSafe(c.Context())
}
return xlog.New()
}
func NewContextFromConn(conn net.Conn) context.Context {
if c, ok := conn.(ContextGetter); ok {
return c.Context()
}
return context.Background()
}
// ContextConn is the connection with context
type ContextConn struct {
net.Conn
ctx context.Context
}
func NewContextConn(c net.Conn, ctx context.Context) *ContextConn {
return &ContextConn{
Conn: c,
ctx: ctx,
}
}
func (c *ContextConn) WithContext(ctx context.Context) {
c.ctx = ctx
}
func (c *ContextConn) Context() context.Context {
return c.ctx
}
type WrapReadWriteCloserConn struct {
io.ReadWriteCloser
log.Logger
underConn net.Conn
}
func WrapReadWriteCloserToConn(rwc io.ReadWriteCloser, underConn net.Conn) Conn {
func WrapReadWriteCloserToConn(rwc io.ReadWriteCloser, underConn net.Conn) net.Conn {
return &WrapReadWriteCloserConn{
ReadWriteCloser: rwc,
Logger: log.NewPrefixLogger(""),
underConn: underConn,
}
}
@ -99,7 +123,6 @@ func (conn *WrapReadWriteCloserConn) SetWriteDeadline(t time.Time) error {
type CloseNotifyConn struct {
net.Conn
log.Logger
// 1 means closed
closeFlag int32
@ -108,10 +131,9 @@ type CloseNotifyConn struct {
}
// closeFn will be only called once
func WrapCloseNotifyConn(c net.Conn, closeFn func()) Conn {
func WrapCloseNotifyConn(c net.Conn, closeFn func()) net.Conn {
return &CloseNotifyConn{
Conn: c,
Logger: log.NewPrefixLogger(""),
closeFn: closeFn,
}
}
@ -128,7 +150,7 @@ func (cc *CloseNotifyConn) Close() (err error) {
}
type StatsConn struct {
Conn
net.Conn
closed int64 // 1 means closed
totalRead int64
@ -136,7 +158,7 @@ type StatsConn struct {
statsFunc func(totalRead, totalWrite int64)
}
func WrapStatsConn(conn Conn, statsFunc func(total, totalWrite int64)) *StatsConn {
func WrapStatsConn(conn net.Conn, statsFunc func(total, totalWrite int64)) *StatsConn {
return &StatsConn{
Conn: conn,
statsFunc: statsFunc,
@ -166,10 +188,10 @@ func (statsConn *StatsConn) Close() (err error) {
return
}
func ConnectServer(protocol string, addr string) (c Conn, err error) {
func ConnectServer(protocol string, addr string) (c net.Conn, err error) {
switch protocol {
case "tcp":
return ConnectTcpServer(addr)
return net.Dial("tcp", addr)
case "kcp":
kcpConn, errRet := kcp.DialWithOptions(addr, nil, 10, 3)
if errRet != nil {
@ -184,21 +206,17 @@ func ConnectServer(protocol string, addr string) (c Conn, err error) {
kcpConn.SetACKNoDelay(false)
kcpConn.SetReadBuffer(4194304)
kcpConn.SetWriteBuffer(4194304)
c = WrapConn(kcpConn)
c = kcpConn
return
default:
return nil, fmt.Errorf("unsupport protocol: %s", protocol)
}
}
func ConnectServerByProxy(proxyUrl string, protocol string, addr string) (c Conn, err error) {
func ConnectServerByProxy(proxyURL string, protocol string, addr string) (c net.Conn, err error) {
switch protocol {
case "tcp":
var conn net.Conn
if conn, err = gnet.DialTcpByProxy(proxyUrl, addr); err != nil {
return
}
return WrapConn(conn), nil
return gnet.DialTcpByProxy(proxyURL, addr)
case "kcp":
// http proxy is not supported for kcp
return ConnectServer(protocol, addr)
@ -209,7 +227,7 @@ func ConnectServerByProxy(proxyUrl string, protocol string, addr string) (c Conn
}
}
func ConnectServerByProxyWithTLS(proxyUrl string, protocol string, addr string, tlsConfig *tls.Config) (c Conn, err error) {
func ConnectServerByProxyWithTLS(proxyUrl string, protocol string, addr string, tlsConfig *tls.Config) (c net.Conn, err error) {
c, err = ConnectServerByProxy(proxyUrl, protocol, addr)
if err != nil {
return

View File

@ -18,17 +18,13 @@ import (
"fmt"
"net"
"github.com/fatedier/frp/utils/log"
kcp "github.com/fatedier/kcp-go"
)
type KcpListener struct {
net.Addr
listener net.Listener
accept chan Conn
acceptCh chan net.Conn
closeFlag bool
log.Logger
}
func ListenKcp(bindAddr string, bindPort int) (l *KcpListener, err error) {
@ -40,11 +36,9 @@ func ListenKcp(bindAddr string, bindPort int) (l *KcpListener, err error) {
listener.SetWriteBuffer(4194304)
l = &KcpListener{
Addr: listener.Addr(),
listener: listener,
accept: make(chan Conn),
acceptCh: make(chan net.Conn),
closeFlag: false,
Logger: log.NewPrefixLogger(""),
}
go func() {
@ -52,7 +46,7 @@ func ListenKcp(bindAddr string, bindPort int) (l *KcpListener, err error) {
conn, err := listener.AcceptKCP()
if err != nil {
if l.closeFlag {
close(l.accept)
close(l.acceptCh)
return
}
continue
@ -64,14 +58,14 @@ func ListenKcp(bindAddr string, bindPort int) (l *KcpListener, err error) {
conn.SetWindowSize(1024, 1024)
conn.SetACKNoDelay(false)
l.accept <- WrapConn(conn)
l.acceptCh <- conn
}
}()
return l, err
}
func (l *KcpListener) Accept() (Conn, error) {
conn, ok := <-l.accept
func (l *KcpListener) Accept() (net.Conn, error) {
conn, ok := <-l.acceptCh
if !ok {
return conn, fmt.Errorf("channel for kcp listener closed")
}
@ -86,6 +80,10 @@ func (l *KcpListener) Close() error {
return nil
}
func (l *KcpListener) Addr() net.Addr {
return l.listener.Addr()
}
func NewKcpConnFromUdp(conn *net.UDPConn, connected bool, raddr string) (net.Conn, error) {
kcpConn, err := kcp.NewConnEx(1, connected, raddr, nil, 10, 3, conn)
if err != nil {

View File

@ -19,65 +19,34 @@ import (
"net"
"sync"
"github.com/fatedier/frp/utils/log"
"github.com/fatedier/golib/errors"
)
type Listener interface {
Accept() (Conn, error)
Close() error
log.Logger
}
type LogListener struct {
l net.Listener
net.Listener
log.Logger
}
func WrapLogListener(l net.Listener) Listener {
return &LogListener{
l: l,
Listener: l,
Logger: log.NewPrefixLogger(""),
}
}
func (logL *LogListener) Accept() (Conn, error) {
c, err := logL.l.Accept()
return WrapConn(c), err
}
// Custom listener
type CustomListener struct {
conns chan Conn
closed bool
mu sync.Mutex
log.Logger
acceptCh chan net.Conn
closed bool
mu sync.Mutex
}
func NewCustomListener() *CustomListener {
return &CustomListener{
conns: make(chan Conn, 64),
Logger: log.NewPrefixLogger(""),
acceptCh: make(chan net.Conn, 64),
}
}
func (l *CustomListener) Accept() (Conn, error) {
conn, ok := <-l.conns
func (l *CustomListener) Accept() (net.Conn, error) {
conn, ok := <-l.acceptCh
if !ok {
return nil, fmt.Errorf("listener closed")
}
conn.AddLogPrefix(l.GetPrefixStr())
return conn, nil
}
func (l *CustomListener) PutConn(conn Conn) error {
func (l *CustomListener) PutConn(conn net.Conn) error {
err := errors.PanicToError(func() {
select {
case l.conns <- conn:
case l.acceptCh <- conn:
default:
conn.Close()
}
@ -89,7 +58,7 @@ func (l *CustomListener) Close() error {
l.mu.Lock()
defer l.mu.Unlock()
if !l.closed {
close(l.conns)
close(l.acceptCh)
l.closed = true
}
return nil

View File

@ -1,111 +0,0 @@
// Copyright 2016 fatedier, fatedier@gmail.com
//
// 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 (
"fmt"
"net"
"github.com/fatedier/frp/utils/log"
)
type TcpListener struct {
net.Addr
listener net.Listener
accept chan Conn
closeFlag bool
log.Logger
}
func ListenTcp(bindAddr string, bindPort int) (l *TcpListener, err error) {
tcpAddr, err := net.ResolveTCPAddr("tcp", fmt.Sprintf("%s:%d", bindAddr, bindPort))
if err != nil {
return l, err
}
listener, err := net.ListenTCP("tcp", tcpAddr)
if err != nil {
return l, err
}
l = &TcpListener{
Addr: listener.Addr(),
listener: listener,
accept: make(chan Conn),
closeFlag: false,
Logger: log.NewPrefixLogger(""),
}
go func() {
for {
conn, err := listener.AcceptTCP()
if err != nil {
if l.closeFlag {
close(l.accept)
return
}
continue
}
c := NewTcpConn(conn)
l.accept <- c
}
}()
return l, err
}
// Wait util get one new connection or listener is closed
// if listener is closed, err returned.
func (l *TcpListener) Accept() (Conn, error) {
conn, ok := <-l.accept
if !ok {
return conn, fmt.Errorf("channel for tcp listener closed")
}
return conn, nil
}
func (l *TcpListener) Close() error {
if !l.closeFlag {
l.closeFlag = true
l.listener.Close()
}
return nil
}
// Wrap for TCPConn.
type TcpConn struct {
net.Conn
log.Logger
}
func NewTcpConn(conn net.Conn) (c *TcpConn) {
c = &TcpConn{
Conn: conn,
Logger: log.NewPrefixLogger(""),
}
return
}
func ConnectTcpServer(addr string) (c Conn, err error) {
servertAddr, err := net.ResolveTCPAddr("tcp", addr)
if err != nil {
return
}
conn, err := net.DialTCP("tcp", nil, servertAddr)
if err != nil {
return
}
c = NewTcpConn(conn)
return
}

View File

@ -26,13 +26,13 @@ var (
FRP_TLS_HEAD_BYTE = 0x17
)
func WrapTLSClientConn(c net.Conn, tlsConfig *tls.Config) (out Conn) {
func WrapTLSClientConn(c net.Conn, tlsConfig *tls.Config) (out net.Conn) {
c.Write([]byte{byte(FRP_TLS_HEAD_BYTE)})
out = WrapConn(tls.Client(c, tlsConfig))
out = tls.Client(c, tlsConfig)
return
}
func CheckAndEnableTLSServerConnWithTimeout(c net.Conn, tlsConfig *tls.Config, timeout time.Duration) (out Conn, err error) {
func CheckAndEnableTLSServerConnWithTimeout(c net.Conn, tlsConfig *tls.Config, timeout time.Duration) (out net.Conn, err error) {
sc, r := gnet.NewSharedConnSize(c, 2)
buf := make([]byte, 1)
var n int
@ -44,9 +44,9 @@ func CheckAndEnableTLSServerConnWithTimeout(c net.Conn, tlsConfig *tls.Config, t
}
if n == 1 && int(buf[0]) == FRP_TLS_HEAD_BYTE {
out = WrapConn(tls.Server(c, tlsConfig))
out = tls.Server(c, tlsConfig)
} else {
out = WrapConn(sc)
out = sc
}
return
}

View File

@ -21,8 +21,6 @@ import (
"sync"
"time"
"github.com/fatedier/frp/utils/log"
"github.com/fatedier/golib/pool"
)
@ -33,7 +31,6 @@ type UdpPacket struct {
}
type FakeUdpConn struct {
log.Logger
l *UdpListener
localAddr net.Addr
@ -47,7 +44,6 @@ type FakeUdpConn struct {
func NewFakeUdpConn(l *UdpListener, laddr, raddr net.Addr) *FakeUdpConn {
fc := &FakeUdpConn{
Logger: log.NewPrefixLogger(""),
l: l,
localAddr: laddr,
remoteAddr: raddr,
@ -157,15 +153,13 @@ func (c *FakeUdpConn) SetWriteDeadline(t time.Time) error {
}
type UdpListener struct {
net.Addr
accept chan Conn
addr net.Addr
acceptCh chan net.Conn
writeCh chan *UdpPacket
readConn net.Conn
closeFlag bool
fakeConns map[string]*FakeUdpConn
log.Logger
}
func ListenUDP(bindAddr string, bindPort int) (l *UdpListener, err error) {
@ -176,11 +170,10 @@ func ListenUDP(bindAddr string, bindPort int) (l *UdpListener, err error) {
readConn, err := net.ListenUDP("udp", udpAddr)
l = &UdpListener{
Addr: udpAddr,
accept: make(chan Conn),
addr: udpAddr,
acceptCh: make(chan net.Conn),
writeCh: make(chan *UdpPacket, 1000),
fakeConns: make(map[string]*FakeUdpConn),
Logger: log.NewPrefixLogger(""),
}
// for reading
@ -189,19 +182,19 @@ func ListenUDP(bindAddr string, bindPort int) (l *UdpListener, err error) {
buf := pool.GetBuf(1450)
n, remoteAddr, err := readConn.ReadFromUDP(buf)
if err != nil {
close(l.accept)
close(l.acceptCh)
close(l.writeCh)
return
}
fakeConn, exist := l.fakeConns[remoteAddr.String()]
if !exist || fakeConn.IsClosed() {
fakeConn = NewFakeUdpConn(l, l.Addr, remoteAddr)
fakeConn = NewFakeUdpConn(l, l.Addr(), remoteAddr)
l.fakeConns[remoteAddr.String()] = fakeConn
}
fakeConn.putPacket(buf[:n])
l.accept <- fakeConn
l.acceptCh <- fakeConn
}
}()
@ -226,7 +219,6 @@ func (l *UdpListener) writeUdpPacket(packet *UdpPacket) (err error) {
defer func() {
if errRet := recover(); errRet != nil {
err = fmt.Errorf("udp write closed listener")
l.Info("udp write closed listener")
}
}()
l.writeCh <- packet
@ -243,8 +235,8 @@ func (l *UdpListener) WriteMsg(buf []byte, remoteAddr *net.UDPAddr) (err error)
return
}
func (l *UdpListener) Accept() (Conn, error) {
conn, ok := <-l.accept
func (l *UdpListener) Accept() (net.Conn, error) {
conn, ok := <-l.acceptCh
if !ok {
return conn, fmt.Errorf("channel for udp listener closed")
}
@ -258,3 +250,7 @@ func (l *UdpListener) Close() error {
}
return nil
}
func (l *UdpListener) Addr() net.Addr {
return l.addr
}

View File

@ -8,8 +8,6 @@ import (
"net/url"
"time"
"github.com/fatedier/frp/utils/log"
"golang.org/x/net/websocket"
)
@ -22,10 +20,8 @@ const (
)
type WebsocketListener struct {
net.Addr
ln net.Listener
accept chan Conn
log.Logger
ln net.Listener
acceptCh chan net.Conn
server *http.Server
httpMutex *http.ServeMux
@ -35,9 +31,7 @@ type WebsocketListener struct {
// ln: tcp listener for websocket connections
func NewWebsocketListener(ln net.Listener) (wl *WebsocketListener) {
wl = &WebsocketListener{
Addr: ln.Addr(),
accept: make(chan Conn),
Logger: log.NewPrefixLogger(""),
acceptCh: make(chan net.Conn),
}
muxer := http.NewServeMux()
@ -46,7 +40,7 @@ func NewWebsocketListener(ln net.Listener) (wl *WebsocketListener) {
conn := WrapCloseNotifyConn(c, func() {
close(notifyCh)
})
wl.accept <- conn
wl.acceptCh <- conn
<-notifyCh
}))
@ -68,8 +62,8 @@ func ListenWebsocket(bindAddr string, bindPort int) (*WebsocketListener, error)
return l, nil
}
func (p *WebsocketListener) Accept() (Conn, error) {
c, ok := <-p.accept
func (p *WebsocketListener) Accept() (net.Conn, error) {
c, ok := <-p.acceptCh
if !ok {
return nil, ErrWebsocketListenerClosed
}
@ -80,8 +74,12 @@ func (p *WebsocketListener) Close() error {
return p.server.Close()
}
func (p *WebsocketListener) Addr() net.Addr {
return p.ln.Addr()
}
// addr: domain:port
func ConnectWebsocketServer(addr string) (Conn, error) {
func ConnectWebsocketServer(addr string) (net.Conn, error) {
addr = "ws://" + addr + FrpWebsocketPath
uri, err := url.Parse(addr)
if err != nil {
@ -101,6 +99,5 @@ func ConnectWebsocketServer(addr string) (Conn, error) {
if err != nil {
return nil, err
}
c := WrapConn(conn)
return c, nil
return conn, nil
}

View File

@ -17,11 +17,10 @@ package vhost
import (
"fmt"
"io"
"net"
"strings"
"time"
frpNet "github.com/fatedier/frp/utils/net"
gnet "github.com/fatedier/golib/net"
"github.com/fatedier/golib/pool"
)
@ -48,7 +47,7 @@ type HttpsMuxer struct {
*VhostMuxer
}
func NewHttpsMuxer(listener frpNet.Listener, timeout time.Duration) (*HttpsMuxer, error) {
func NewHttpsMuxer(listener net.Listener, timeout time.Duration) (*HttpsMuxer, error) {
mux, err := NewVhostMuxer(listener, GetHttpsHostname, nil, nil, timeout)
return &HttpsMuxer{mux}, err
}
@ -182,7 +181,7 @@ func readHandshake(rd io.Reader) (host string, err error) {
return
}
func GetHttpsHostname(c frpNet.Conn) (_ frpNet.Conn, _ map[string]string, err error) {
func GetHttpsHostname(c net.Conn) (_ net.Conn, _ map[string]string, err error) {
reqInfoMap := make(map[string]string, 0)
sc, rd := gnet.NewSharedConn(c)
host, err := readHandshake(rd)
@ -191,5 +190,5 @@ func GetHttpsHostname(c frpNet.Conn) (_ frpNet.Conn, _ map[string]string, err er
}
reqInfoMap["Host"] = host
reqInfoMap["Scheme"] = "https"
return frpNet.WrapConn(sc), reqInfoMap, nil
return sc, reqInfoMap, nil
}

View File

@ -13,22 +13,25 @@
package vhost
import (
"context"
"fmt"
"net"
"strings"
"time"
"github.com/fatedier/frp/utils/log"
frpNet "github.com/fatedier/frp/utils/net"
"github.com/fatedier/frp/utils/xlog"
"github.com/fatedier/golib/errors"
)
type muxFunc func(frpNet.Conn) (frpNet.Conn, map[string]string, error)
type httpAuthFunc func(frpNet.Conn, string, string, string) (bool, error)
type hostRewriteFunc func(frpNet.Conn, string) (frpNet.Conn, error)
type muxFunc func(net.Conn) (net.Conn, map[string]string, error)
type httpAuthFunc func(net.Conn, string, string, string) (bool, error)
type hostRewriteFunc func(net.Conn, string) (net.Conn, error)
type VhostMuxer struct {
listener frpNet.Listener
listener net.Listener
timeout time.Duration
vhostFunc muxFunc
authFunc httpAuthFunc
@ -36,7 +39,7 @@ type VhostMuxer struct {
registryRouter *VhostRouters
}
func NewVhostMuxer(listener frpNet.Listener, vhostFunc muxFunc, authFunc httpAuthFunc, rewriteFunc hostRewriteFunc, timeout time.Duration) (mux *VhostMuxer, err error) {
func NewVhostMuxer(listener net.Listener, vhostFunc muxFunc, authFunc httpAuthFunc, rewriteFunc hostRewriteFunc, timeout time.Duration) (mux *VhostMuxer, err error) {
mux = &VhostMuxer{
listener: listener,
timeout: timeout,
@ -49,7 +52,7 @@ func NewVhostMuxer(listener frpNet.Listener, vhostFunc muxFunc, authFunc httpAut
return mux, nil
}
type CreateConnFunc func(remoteAddr string) (frpNet.Conn, error)
type CreateConnFunc func(remoteAddr string) (net.Conn, error)
// VhostRouteConfig is the params used to match HTTP requests
type VhostRouteConfig struct {
@ -65,7 +68,7 @@ type VhostRouteConfig struct {
// listen for a new domain name, if rewriteHost is not empty and rewriteFunc is not nil
// then rewrite the host header to rewriteHost
func (v *VhostMuxer) Listen(cfg *VhostRouteConfig) (l *Listener, err error) {
func (v *VhostMuxer) Listen(ctx context.Context, cfg *VhostRouteConfig) (l *Listener, err error) {
l = &Listener{
name: cfg.Domain,
location: cfg.Location,
@ -73,8 +76,8 @@ func (v *VhostMuxer) Listen(cfg *VhostRouteConfig) (l *Listener, err error) {
userName: cfg.Username,
passWord: cfg.Password,
mux: v,
accept: make(chan frpNet.Conn),
Logger: log.NewPrefixLogger(""),
accept: make(chan net.Conn),
ctx: ctx,
}
err = v.registryRouter.Add(cfg.Domain, cfg.Location, l)
if err != nil {
@ -123,7 +126,7 @@ func (v *VhostMuxer) run() {
}
}
func (v *VhostMuxer) handle(c frpNet.Conn) {
func (v *VhostMuxer) handle(c net.Conn) {
if err := c.SetDeadline(time.Now().Add(v.timeout)); err != nil {
c.Close()
return
@ -146,13 +149,14 @@ func (v *VhostMuxer) handle(c frpNet.Conn) {
c.Close()
return
}
xl := xlog.FromContextSafe(l.ctx)
// if authFunc is exist and userName/password is set
// then verify user access
if l.mux.authFunc != nil && l.userName != "" && l.passWord != "" {
bAccess, err := l.mux.authFunc(c, l.userName, l.passWord, reqInfoMap["Authorization"])
if bAccess == false || err != nil {
l.Debug("check http Authorization failed")
xl.Debug("check http Authorization failed")
res := noAuthResponse()
res.Write(c)
c.Close()
@ -166,12 +170,12 @@ func (v *VhostMuxer) handle(c frpNet.Conn) {
}
c = sConn
l.Debug("get new http request host [%s] path [%s]", name, path)
xl.Debug("get new http request host [%s] path [%s]", name, path)
err = errors.PanicToError(func() {
l.accept <- c
})
if err != nil {
l.Warn("listener is already closed, ignore this request")
xl.Warn("listener is already closed, ignore this request")
}
}
@ -182,11 +186,12 @@ type Listener struct {
userName string
passWord string
mux *VhostMuxer // for closing VhostMuxer
accept chan frpNet.Conn
log.Logger
accept chan net.Conn
ctx context.Context
}
func (l *Listener) Accept() (frpNet.Conn, error) {
func (l *Listener) Accept() (net.Conn, error) {
xl := xlog.FromContextSafe(l.ctx)
conn, ok := <-l.accept
if !ok {
return nil, fmt.Errorf("Listener closed")
@ -198,17 +203,13 @@ func (l *Listener) Accept() (frpNet.Conn, error) {
if l.mux.rewriteFunc != nil {
sConn, err := l.mux.rewriteFunc(conn, l.rewriteHost)
if err != nil {
l.Warn("host header rewrite failed: %v", err)
xl.Warn("host header rewrite failed: %v", err)
return nil, fmt.Errorf("host header rewrite failed")
}
l.Debug("rewrite host to [%s] success", l.rewriteHost)
xl.Debug("rewrite host to [%s] success", l.rewriteHost)
conn = sConn
}
for _, prefix := range l.GetAllPrefix() {
conn.AddLogPrefix(prefix)
}
return conn, nil
return frpNet.NewContextConn(conn, l.ctx), nil
}
func (l *Listener) Close() error {
@ -220,3 +221,7 @@ func (l *Listener) Close() error {
func (l *Listener) Name() string {
return l.name
}
func (l *Listener) Addr() net.Addr {
return (*net.TCPAddr)(nil)
}

42
utils/xlog/ctx.go Normal file
View File

@ -0,0 +1,42 @@
// Copyright 2019 fatedier, fatedier@gmail.com
//
// 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 xlog
import (
"context"
)
type key int
const (
xlogKey key = 0
)
func NewContext(ctx context.Context, xl *Logger) context.Context {
return context.WithValue(ctx, xlogKey, xl)
}
func FromContext(ctx context.Context) (xl *Logger, ok bool) {
xl, ok = ctx.Value(xlogKey).(*Logger)
return
}
func FromContextSafe(ctx context.Context) *Logger {
xl, ok := ctx.Value(xlogKey).(*Logger)
if !ok {
xl = New()
}
return xl
}

73
utils/xlog/xlog.go Normal file
View File

@ -0,0 +1,73 @@
// Copyright 2019 fatedier, fatedier@gmail.com
//
// 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 xlog
import (
"github.com/fatedier/frp/utils/log"
)
// Logger is not thread safety for operations on prefix
type Logger struct {
prefixes []string
prefixString string
}
func New() *Logger {
return &Logger{
prefixes: make([]string, 0),
}
}
func (l *Logger) ResetPrefixes() (old []string) {
old = l.prefixes
l.prefixes = make([]string, 0)
l.prefixString = ""
return
}
func (l *Logger) AppendPrefix(prefix string) *Logger {
l.prefixes = append(l.prefixes, prefix)
l.prefixString += "[" + prefix + "] "
return l
}
func (l *Logger) Spawn() *Logger {
nl := New()
for _, v := range l.prefixes {
nl.AppendPrefix(v)
}
return nl
}
func (l *Logger) Error(format string, v ...interface{}) {
log.Log.Error(l.prefixString+format, v...)
}
func (l *Logger) Warn(format string, v ...interface{}) {
log.Log.Warn(l.prefixString+format, v...)
}
func (l *Logger) Info(format string, v ...interface{}) {
log.Log.Info(l.prefixString+format, v...)
}
func (l *Logger) Debug(format string, v ...interface{}) {
log.Log.Debug(l.prefixString+format, v...)
}
func (l *Logger) Trace(format string, v ...interface{}) {
log.Log.Trace(l.prefixString+format, v...)
}