mirror of
https://github.com/fatedier/frp.git
synced 2025-02-01 23:44:22 +00:00
support udp
This commit is contained in:
parent
a1023fdfc2
commit
54bbfe26b0
@ -146,6 +146,8 @@ func (ctl *Control) NewWorkConn() {
|
|||||||
if pxy, ok := ctl.proxies[startMsg.ProxyName]; ok {
|
if pxy, ok := ctl.proxies[startMsg.ProxyName]; ok {
|
||||||
go pxy.InWorkConn(workConn)
|
go pxy.InWorkConn(workConn)
|
||||||
workConn.Info("start a new work connection")
|
workConn.Info("start a new work connection")
|
||||||
|
} else {
|
||||||
|
workConn.Close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
109
client/proxy.go
109
client/proxy.go
@ -17,10 +17,15 @@ package client
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"net"
|
||||||
|
|
||||||
"github.com/fatedier/frp/models/config"
|
"github.com/fatedier/frp/models/config"
|
||||||
|
"github.com/fatedier/frp/models/msg"
|
||||||
"github.com/fatedier/frp/models/proto/tcp"
|
"github.com/fatedier/frp/models/proto/tcp"
|
||||||
"github.com/fatedier/frp/utils/net"
|
"github.com/fatedier/frp/models/proto/udp"
|
||||||
|
"github.com/fatedier/frp/utils/errors"
|
||||||
|
"github.com/fatedier/frp/utils/log"
|
||||||
|
frpNet "github.com/fatedier/frp/utils/net"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Proxy defines how to work for different proxy type.
|
// Proxy defines how to work for different proxy type.
|
||||||
@ -28,40 +33,51 @@ type Proxy interface {
|
|||||||
Run() error
|
Run() error
|
||||||
|
|
||||||
// InWorkConn accept work connections registered to server.
|
// InWorkConn accept work connections registered to server.
|
||||||
InWorkConn(conn net.Conn)
|
InWorkConn(conn frpNet.Conn)
|
||||||
Close()
|
Close()
|
||||||
|
log.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewProxy(ctl *Control, pxyConf config.ProxyConf) (pxy Proxy) {
|
func NewProxy(ctl *Control, pxyConf config.ProxyConf) (pxy Proxy) {
|
||||||
|
baseProxy := BaseProxy{
|
||||||
|
ctl: ctl,
|
||||||
|
Logger: log.NewPrefixLogger(pxyConf.GetName()),
|
||||||
|
}
|
||||||
switch cfg := pxyConf.(type) {
|
switch cfg := pxyConf.(type) {
|
||||||
case *config.TcpProxyConf:
|
case *config.TcpProxyConf:
|
||||||
pxy = &TcpProxy{
|
pxy = &TcpProxy{
|
||||||
cfg: cfg,
|
BaseProxy: baseProxy,
|
||||||
ctl: ctl,
|
cfg: cfg,
|
||||||
}
|
}
|
||||||
case *config.UdpProxyConf:
|
case *config.UdpProxyConf:
|
||||||
pxy = &UdpProxy{
|
pxy = &UdpProxy{
|
||||||
cfg: cfg,
|
BaseProxy: baseProxy,
|
||||||
ctl: ctl,
|
cfg: cfg,
|
||||||
}
|
}
|
||||||
case *config.HttpProxyConf:
|
case *config.HttpProxyConf:
|
||||||
pxy = &HttpProxy{
|
pxy = &HttpProxy{
|
||||||
cfg: cfg,
|
BaseProxy: baseProxy,
|
||||||
ctl: ctl,
|
cfg: cfg,
|
||||||
}
|
}
|
||||||
case *config.HttpsProxyConf:
|
case *config.HttpsProxyConf:
|
||||||
pxy = &HttpsProxy{
|
pxy = &HttpsProxy{
|
||||||
cfg: cfg,
|
BaseProxy: baseProxy,
|
||||||
ctl: ctl,
|
cfg: cfg,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type BaseProxy struct {
|
||||||
|
ctl *Control
|
||||||
|
log.Logger
|
||||||
|
}
|
||||||
|
|
||||||
// TCP
|
// TCP
|
||||||
type TcpProxy struct {
|
type TcpProxy struct {
|
||||||
|
BaseProxy
|
||||||
|
|
||||||
cfg *config.TcpProxyConf
|
cfg *config.TcpProxyConf
|
||||||
ctl *Control
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pxy *TcpProxy) Run() (err error) {
|
func (pxy *TcpProxy) Run() (err error) {
|
||||||
@ -71,15 +87,16 @@ func (pxy *TcpProxy) Run() (err error) {
|
|||||||
func (pxy *TcpProxy) Close() {
|
func (pxy *TcpProxy) Close() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pxy *TcpProxy) InWorkConn(conn net.Conn) {
|
func (pxy *TcpProxy) InWorkConn(conn frpNet.Conn) {
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
HandleTcpWorkConnection(&pxy.cfg.LocalSvrConf, &pxy.cfg.BaseProxyConf, conn)
|
HandleTcpWorkConnection(&pxy.cfg.LocalSvrConf, &pxy.cfg.BaseProxyConf, conn)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HTTP
|
// HTTP
|
||||||
type HttpProxy struct {
|
type HttpProxy struct {
|
||||||
|
BaseProxy
|
||||||
|
|
||||||
cfg *config.HttpProxyConf
|
cfg *config.HttpProxyConf
|
||||||
ctl *Control
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pxy *HttpProxy) Run() (err error) {
|
func (pxy *HttpProxy) Run() (err error) {
|
||||||
@ -89,15 +106,16 @@ func (pxy *HttpProxy) Run() (err error) {
|
|||||||
func (pxy *HttpProxy) Close() {
|
func (pxy *HttpProxy) Close() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pxy *HttpProxy) InWorkConn(conn net.Conn) {
|
func (pxy *HttpProxy) InWorkConn(conn frpNet.Conn) {
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
HandleTcpWorkConnection(&pxy.cfg.LocalSvrConf, &pxy.cfg.BaseProxyConf, conn)
|
HandleTcpWorkConnection(&pxy.cfg.LocalSvrConf, &pxy.cfg.BaseProxyConf, conn)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HTTPS
|
// HTTPS
|
||||||
type HttpsProxy struct {
|
type HttpsProxy struct {
|
||||||
|
BaseProxy
|
||||||
|
|
||||||
cfg *config.HttpsProxyConf
|
cfg *config.HttpsProxyConf
|
||||||
ctl *Control
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pxy *HttpsProxy) Run() (err error) {
|
func (pxy *HttpsProxy) Run() (err error) {
|
||||||
@ -107,31 +125,80 @@ func (pxy *HttpsProxy) Run() (err error) {
|
|||||||
func (pxy *HttpsProxy) Close() {
|
func (pxy *HttpsProxy) Close() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pxy *HttpsProxy) InWorkConn(conn net.Conn) {
|
func (pxy *HttpsProxy) InWorkConn(conn frpNet.Conn) {
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
HandleTcpWorkConnection(&pxy.cfg.LocalSvrConf, &pxy.cfg.BaseProxyConf, conn)
|
HandleTcpWorkConnection(&pxy.cfg.LocalSvrConf, &pxy.cfg.BaseProxyConf, conn)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UDP
|
// UDP
|
||||||
type UdpProxy struct {
|
type UdpProxy struct {
|
||||||
|
BaseProxy
|
||||||
|
|
||||||
cfg *config.UdpProxyConf
|
cfg *config.UdpProxyConf
|
||||||
ctl *Control
|
|
||||||
|
localAddr *net.UDPAddr
|
||||||
|
readCh chan *msg.UdpPacket
|
||||||
|
sendCh chan *msg.UdpPacket
|
||||||
|
workConn frpNet.Conn
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pxy *UdpProxy) Run() (err error) {
|
func (pxy *UdpProxy) Run() (err error) {
|
||||||
|
pxy.localAddr, err = net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", pxy.cfg.LocalIp, pxy.cfg.LocalPort))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pxy *UdpProxy) Close() {
|
func (pxy *UdpProxy) Close() {
|
||||||
|
pxy.workConn.Close()
|
||||||
|
close(pxy.readCh)
|
||||||
|
close(pxy.sendCh)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pxy *UdpProxy) InWorkConn(conn net.Conn) {
|
func (pxy *UdpProxy) InWorkConn(conn frpNet.Conn) {
|
||||||
defer conn.Close()
|
if pxy.workConn != nil {
|
||||||
|
pxy.workConn.Close()
|
||||||
|
close(pxy.readCh)
|
||||||
|
close(pxy.sendCh)
|
||||||
|
}
|
||||||
|
pxy.workConn = conn
|
||||||
|
pxy.readCh = make(chan *msg.UdpPacket, 64)
|
||||||
|
pxy.sendCh = make(chan *msg.UdpPacket, 64)
|
||||||
|
|
||||||
|
workConnReaderFn := func(conn net.Conn) {
|
||||||
|
for {
|
||||||
|
var udpMsg msg.UdpPacket
|
||||||
|
if errRet := msg.ReadMsgInto(conn, &udpMsg); errRet != nil {
|
||||||
|
pxy.Warn("read from workConn for udp error: %v", errRet)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if errRet := errors.PanicToError(func() {
|
||||||
|
pxy.readCh <- &udpMsg
|
||||||
|
}); errRet != nil {
|
||||||
|
pxy.Info("reader goroutine for udp work connection closed")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
workConnSenderFn := func(conn net.Conn) {
|
||||||
|
var errRet error
|
||||||
|
for udpMsg := range pxy.sendCh {
|
||||||
|
if errRet = msg.WriteMsg(conn, udpMsg); errRet != nil {
|
||||||
|
pxy.Info("sender goroutine for udp work connection closed")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
go workConnSenderFn(pxy.workConn)
|
||||||
|
go workConnReaderFn(pxy.workConn)
|
||||||
|
udp.Forwarder(pxy.localAddr, pxy.readCh, pxy.sendCh)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Common handler for tcp work connections.
|
// Common handler for tcp work connections.
|
||||||
func HandleTcpWorkConnection(localInfo *config.LocalSvrConf, baseInfo *config.BaseProxyConf, workConn net.Conn) {
|
func HandleTcpWorkConnection(localInfo *config.LocalSvrConf, baseInfo *config.BaseProxyConf, workConn frpNet.Conn) {
|
||||||
localConn, err := net.ConnectTcpServer(fmt.Sprintf("%s:%d", localInfo.LocalIp, localInfo.LocalPort))
|
localConn, err := frpNet.ConnectTcpServer(fmt.Sprintf("%s:%d", localInfo.LocalIp, localInfo.LocalPort))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
workConn.Error("connect to local service [%s:%d] error: %v", localInfo.LocalIp, localInfo.LocalPort, err)
|
workConn.Error("connect to local service [%s:%d] error: %v", localInfo.LocalIp, localInfo.LocalPort, err)
|
||||||
return
|
return
|
||||||
|
@ -14,7 +14,10 @@
|
|||||||
|
|
||||||
package msg
|
package msg
|
||||||
|
|
||||||
import "reflect"
|
import (
|
||||||
|
"net"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
TypeLogin = 'o'
|
TypeLogin = 'o'
|
||||||
@ -26,6 +29,7 @@ const (
|
|||||||
TypeStartWorkConn = 's'
|
TypeStartWorkConn = 's'
|
||||||
TypePing = 'h'
|
TypePing = 'h'
|
||||||
TypePong = '4'
|
TypePong = '4'
|
||||||
|
TypeUdpPacket = 'u'
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -46,6 +50,7 @@ func init() {
|
|||||||
TypeMap[TypeStartWorkConn] = reflect.TypeOf(StartWorkConn{})
|
TypeMap[TypeStartWorkConn] = reflect.TypeOf(StartWorkConn{})
|
||||||
TypeMap[TypePing] = reflect.TypeOf(Ping{})
|
TypeMap[TypePing] = reflect.TypeOf(Ping{})
|
||||||
TypeMap[TypePong] = reflect.TypeOf(Pong{})
|
TypeMap[TypePong] = reflect.TypeOf(Pong{})
|
||||||
|
TypeMap[TypeUdpPacket] = reflect.TypeOf(UdpPacket{})
|
||||||
|
|
||||||
for k, v := range TypeMap {
|
for k, v := range TypeMap {
|
||||||
TypeStringMap[v] = k
|
TypeStringMap[v] = k
|
||||||
@ -116,3 +121,9 @@ type Ping struct {
|
|||||||
|
|
||||||
type Pong struct {
|
type Pong struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type UdpPacket struct {
|
||||||
|
Content string `json:"c"`
|
||||||
|
LocalAddr *net.UDPAddr `json:"l"`
|
||||||
|
RemoteAddr *net.UDPAddr `json:"r"`
|
||||||
|
}
|
||||||
|
@ -17,6 +17,9 @@ package msg
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2016 fatedier, fatedier@gmail.com
|
// Copyright 2017 fatedier, fatedier@gmail.com
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
@ -16,57 +16,120 @@ package udp
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/json"
|
|
||||||
"net"
|
"net"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/fatedier/frp/models/msg"
|
||||||
|
"github.com/fatedier/frp/utils/errors"
|
||||||
|
"github.com/fatedier/frp/utils/pool"
|
||||||
)
|
)
|
||||||
|
|
||||||
type UdpPacket struct {
|
func NewUdpPacket(buf []byte, laddr, raddr *net.UDPAddr) *msg.UdpPacket {
|
||||||
Content []byte `json:"-"`
|
return &msg.UdpPacket{
|
||||||
Src *net.UDPAddr `json:"-"`
|
Content: base64.StdEncoding.EncodeToString(buf),
|
||||||
Dst *net.UDPAddr `json:"-"`
|
LocalAddr: laddr,
|
||||||
|
RemoteAddr: raddr,
|
||||||
EncodeContent string `json:"content"`
|
}
|
||||||
SrcStr string `json:"src"`
|
|
||||||
DstStr string `json:"dst"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewUdpPacket(content []byte, src, dst *net.UDPAddr) *UdpPacket {
|
func GetContent(m *msg.UdpPacket) (buf []byte, err error) {
|
||||||
up := &UdpPacket{
|
buf, err = base64.StdEncoding.DecodeString(m.Content)
|
||||||
Src: src,
|
return
|
||||||
Dst: dst,
|
|
||||||
EncodeContent: base64.StdEncoding.EncodeToString(content),
|
|
||||||
SrcStr: src.String(),
|
|
||||||
DstStr: dst.String(),
|
|
||||||
}
|
|
||||||
return up
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse one udp packet struct to bytes
|
func ForwardUserConn(udpConn *net.UDPConn, readCh <-chan *msg.UdpPacket, sendCh chan<- *msg.UdpPacket) {
|
||||||
func (up *UdpPacket) Pack() []byte {
|
// read
|
||||||
b, _ := json.Marshal(up)
|
go func() {
|
||||||
return b
|
for udpMsg := range readCh {
|
||||||
|
buf, err := GetContent(udpMsg)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
udpConn.WriteToUDP(buf, udpMsg.RemoteAddr)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// write
|
||||||
|
go func() {
|
||||||
|
buf := pool.GetBuf(1500)
|
||||||
|
defer pool.PutBuf(buf)
|
||||||
|
for {
|
||||||
|
n, remoteAddr, err := udpConn.ReadFromUDP(buf)
|
||||||
|
if err != nil {
|
||||||
|
udpConn.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
udpMsg := NewUdpPacket(buf[:n], nil, remoteAddr)
|
||||||
|
select {
|
||||||
|
case sendCh <- udpMsg:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse from bytes to UdpPacket struct
|
func Forwarder(dstAddr *net.UDPAddr, readCh <-chan *msg.UdpPacket, sendCh chan<- *msg.UdpPacket) {
|
||||||
func (up *UdpPacket) UnPack(packet []byte) error {
|
var (
|
||||||
err := json.Unmarshal(packet, &up)
|
mu sync.RWMutex
|
||||||
if err != nil {
|
)
|
||||||
return err
|
udpConnMap := make(map[string]*net.UDPConn)
|
||||||
|
|
||||||
|
// read from dstAddr and write to sendCh
|
||||||
|
writerFn := func(raddr *net.UDPAddr, udpConn *net.UDPConn) {
|
||||||
|
addr := raddr.String()
|
||||||
|
defer func() {
|
||||||
|
mu.Lock()
|
||||||
|
delete(udpConnMap, addr)
|
||||||
|
mu.Unlock()
|
||||||
|
}()
|
||||||
|
|
||||||
|
buf := pool.GetBuf(1500)
|
||||||
|
for {
|
||||||
|
udpConn.SetReadDeadline(time.Now().Add(30 * time.Second))
|
||||||
|
n, _, err := udpConn.ReadFromUDP(buf)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
udpMsg := NewUdpPacket(buf[:n], nil, raddr)
|
||||||
|
if err = errors.PanicToError(func() {
|
||||||
|
select {
|
||||||
|
case sendCh <- udpMsg:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
up.Content, err = base64.StdEncoding.DecodeString(up.EncodeContent)
|
// read from readCh
|
||||||
if err != nil {
|
go func() {
|
||||||
return err
|
for udpMsg := range readCh {
|
||||||
}
|
buf, err := GetContent(udpMsg)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
mu.Lock()
|
||||||
|
udpConn, ok := udpConnMap[udpMsg.RemoteAddr.String()]
|
||||||
|
if !ok {
|
||||||
|
udpConn, err = net.DialUDP("udp", nil, dstAddr)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
udpConnMap[udpMsg.RemoteAddr.String()] = udpConn
|
||||||
|
}
|
||||||
|
mu.Unlock()
|
||||||
|
|
||||||
up.Src, err = net.ResolveUDPAddr("udp", up.SrcStr)
|
_, err = udpConn.Write(buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
udpConn.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
up.Dst, err = net.ResolveUDPAddr("udp", up.DstStr)
|
if !ok {
|
||||||
if err != nil {
|
go writerFn(udpMsg.RemoteAddr, udpConn)
|
||||||
return err
|
}
|
||||||
}
|
}
|
||||||
return nil
|
}()
|
||||||
}
|
}
|
||||||
|
@ -1,50 +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 udp
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
content string = "udp packet test"
|
|
||||||
src string = "1.1.1.1:1000"
|
|
||||||
dst string = "2.2.2.2:2000"
|
|
||||||
|
|
||||||
udpMsg *UdpPacket
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
srcAddr, _ := net.ResolveUDPAddr("udp", src)
|
|
||||||
dstAddr, _ := net.ResolveUDPAddr("udp", dst)
|
|
||||||
udpMsg = NewUdpPacket([]byte(content), srcAddr, dstAddr)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPack(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
msg := udpMsg.Pack()
|
|
||||||
assert.Equal(string(msg), `{"content":"dWRwIHBhY2tldCB0ZXN0","src":"1.1.1.1:1000","dst":"2.2.2.2:2000"}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUnpack(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
udpMsg.UnPack([]byte(`{"content":"dWRwIHBhY2tldCB0ZXN0","src":"1.1.1.1:1000","dst":"2.2.2.2:2000"}`))
|
|
||||||
assert.Equal(content, string(udpMsg.Content))
|
|
||||||
assert.Equal(src, udpMsg.Src.String())
|
|
||||||
assert.Equal(dst, udpMsg.Dst.String())
|
|
||||||
}
|
|
@ -131,7 +131,7 @@ func (ctl *Control) GetWorkConn() (workConn net.Conn, err error) {
|
|||||||
select {
|
select {
|
||||||
case workConn, ok = <-ctl.workConnCh:
|
case workConn, ok = <-ctl.workConnCh:
|
||||||
if !ok {
|
if !ok {
|
||||||
err = fmt.Errorf("no work connections available, control is closing")
|
err = errors.ErrCtlClosed
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ctl.conn.Debug("get work connection from pool")
|
ctl.conn.Debug("get work connection from pool")
|
||||||
@ -148,8 +148,8 @@ func (ctl *Control) GetWorkConn() (workConn net.Conn, err error) {
|
|||||||
select {
|
select {
|
||||||
case workConn, ok = <-ctl.workConnCh:
|
case workConn, ok = <-ctl.workConnCh:
|
||||||
if !ok {
|
if !ok {
|
||||||
err = fmt.Errorf("no work connections available, control is closing")
|
err = errors.ErrCtlClosed
|
||||||
ctl.conn.Warn("%v", err)
|
ctl.conn.Warn("no work connections avaiable, %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -251,8 +251,8 @@ func (ctl *Control) stoper() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, pxy := range ctl.proxies {
|
for _, pxy := range ctl.proxies {
|
||||||
ctl.svr.DelProxy(pxy.GetName())
|
|
||||||
pxy.Close()
|
pxy.Close()
|
||||||
|
ctl.svr.DelProxy(pxy.GetName())
|
||||||
}
|
}
|
||||||
|
|
||||||
ctl.allShutdown.Done()
|
ctl.allShutdown.Done()
|
||||||
|
183
server/proxy.go
183
server/proxy.go
@ -1,14 +1,19 @@
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/fatedier/frp/models/config"
|
"github.com/fatedier/frp/models/config"
|
||||||
"github.com/fatedier/frp/models/msg"
|
"github.com/fatedier/frp/models/msg"
|
||||||
"github.com/fatedier/frp/models/proto/tcp"
|
"github.com/fatedier/frp/models/proto/tcp"
|
||||||
|
"github.com/fatedier/frp/models/proto/udp"
|
||||||
|
"github.com/fatedier/frp/utils/errors"
|
||||||
"github.com/fatedier/frp/utils/log"
|
"github.com/fatedier/frp/utils/log"
|
||||||
"github.com/fatedier/frp/utils/net"
|
frpNet "github.com/fatedier/frp/utils/net"
|
||||||
"github.com/fatedier/frp/utils/vhost"
|
"github.com/fatedier/frp/utils/vhost"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -17,6 +22,7 @@ type Proxy interface {
|
|||||||
GetControl() *Control
|
GetControl() *Control
|
||||||
GetName() string
|
GetName() string
|
||||||
GetConf() config.ProxyConf
|
GetConf() config.ProxyConf
|
||||||
|
GetWorkConnFromPool() (workConn frpNet.Conn, err error)
|
||||||
Close()
|
Close()
|
||||||
log.Logger
|
log.Logger
|
||||||
}
|
}
|
||||||
@ -24,7 +30,7 @@ type Proxy interface {
|
|||||||
type BaseProxy struct {
|
type BaseProxy struct {
|
||||||
name string
|
name string
|
||||||
ctl *Control
|
ctl *Control
|
||||||
listeners []net.Listener
|
listeners []frpNet.Listener
|
||||||
log.Logger
|
log.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,12 +49,41 @@ func (pxy *BaseProxy) Close() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (pxy *BaseProxy) GetWorkConnFromPool() (workConn frpNet.Conn, err error) {
|
||||||
|
ctl := pxy.GetControl()
|
||||||
|
// try all connections from the pool
|
||||||
|
for i := 0; i < ctl.poolCount+1; i++ {
|
||||||
|
if workConn, err = ctl.GetWorkConn(); err != nil {
|
||||||
|
pxy.Warn("failed to get work connection: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pxy.Info("get a new work connection: [%s]", workConn.RemoteAddr().String())
|
||||||
|
workConn.AddLogPrefix(pxy.GetName())
|
||||||
|
|
||||||
|
err := msg.WriteMsg(workConn, &msg.StartWorkConn{
|
||||||
|
ProxyName: pxy.GetName(),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
workConn.Warn("failed to send message to work connection from pool: %v, times: %d", err, i)
|
||||||
|
workConn.Close()
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
pxy.Error("try to get work connection failed in the end")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// startListenHandler start a goroutine handler for each listener.
|
// startListenHandler start a goroutine handler for each listener.
|
||||||
// p: p will just be passed to handler(Proxy, net.Conn).
|
// 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.
|
// 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, net.Conn)) {
|
func (pxy *BaseProxy) startListenHandler(p Proxy, handler func(Proxy, frpNet.Conn)) {
|
||||||
for _, listener := range pxy.listeners {
|
for _, listener := range pxy.listeners {
|
||||||
go func(l net.Listener) {
|
go func(l frpNet.Listener) {
|
||||||
for {
|
for {
|
||||||
// block
|
// block
|
||||||
// if listener is closed, err returned
|
// if listener is closed, err returned
|
||||||
@ -68,7 +103,7 @@ func NewProxy(ctl *Control, pxyConf config.ProxyConf) (pxy Proxy, err error) {
|
|||||||
basePxy := BaseProxy{
|
basePxy := BaseProxy{
|
||||||
name: pxyConf.GetName(),
|
name: pxyConf.GetName(),
|
||||||
ctl: ctl,
|
ctl: ctl,
|
||||||
listeners: make([]net.Listener, 0),
|
listeners: make([]frpNet.Listener, 0),
|
||||||
Logger: log.NewPrefixLogger(ctl.runId),
|
Logger: log.NewPrefixLogger(ctl.runId),
|
||||||
}
|
}
|
||||||
switch cfg := pxyConf.(type) {
|
switch cfg := pxyConf.(type) {
|
||||||
@ -105,7 +140,7 @@ type TcpProxy struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (pxy *TcpProxy) Run() error {
|
func (pxy *TcpProxy) Run() error {
|
||||||
listener, err := net.ListenTcp(config.ServerCommonCfg.BindAddr, pxy.cfg.RemotePort)
|
listener, err := frpNet.ListenTcp(config.ServerCommonCfg.BindAddr, pxy.cfg.RemotePort)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -226,10 +261,106 @@ func (pxy *HttpsProxy) Close() {
|
|||||||
type UdpProxy struct {
|
type UdpProxy struct {
|
||||||
BaseProxy
|
BaseProxy
|
||||||
cfg *config.UdpProxyConf
|
cfg *config.UdpProxyConf
|
||||||
|
|
||||||
|
udpConn *net.UDPConn
|
||||||
|
workConn net.Conn
|
||||||
|
sendCh chan *msg.UdpPacket
|
||||||
|
readCh chan *msg.UdpPacket
|
||||||
|
checkCloseCh chan int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pxy *UdpProxy) Run() (err error) {
|
func (pxy *UdpProxy) Run() (err error) {
|
||||||
return
|
addr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", config.ServerCommonCfg.BindAddr, pxy.cfg.RemotePort))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
udpConn, err := net.ListenUDP("udp", addr)
|
||||||
|
if err != nil {
|
||||||
|
pxy.Warn("listen udp port error: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
pxy.Info("udp proxy listen port [%d]", pxy.cfg.RemotePort)
|
||||||
|
|
||||||
|
pxy.udpConn = udpConn
|
||||||
|
pxy.sendCh = make(chan *msg.UdpPacket, 64)
|
||||||
|
pxy.readCh = make(chan *msg.UdpPacket, 64)
|
||||||
|
pxy.checkCloseCh = make(chan int)
|
||||||
|
|
||||||
|
workConnReaderFn := func(conn net.Conn) {
|
||||||
|
for {
|
||||||
|
var udpMsg msg.UdpPacket
|
||||||
|
if errRet := msg.ReadMsgInto(conn, &udpMsg); errRet != nil {
|
||||||
|
pxy.Warn("read from workConn for udp error: %v", errRet)
|
||||||
|
conn.Close()
|
||||||
|
// notity proxy to start a new work connection
|
||||||
|
errors.PanicToError(func() {
|
||||||
|
pxy.checkCloseCh <- 1
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if errRet := errors.PanicToError(func() {
|
||||||
|
pxy.readCh <- &udpMsg
|
||||||
|
}); errRet != nil {
|
||||||
|
pxy.Info("reader goroutine for udp work connection closed")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
workConnSenderFn := func(conn net.Conn, ctx context.Context) {
|
||||||
|
var errRet error
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case udpMsg, ok := <-pxy.sendCh:
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if errRet = msg.WriteMsg(conn, udpMsg); errRet != nil {
|
||||||
|
pxy.Info("sender goroutine for udp work connection closed: %v", errRet)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
case <-ctx.Done():
|
||||||
|
pxy.Info("sender goroutine for udp work connection closed")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
// Sleep a while for waiting control send the NewProxyResp to client.
|
||||||
|
time.Sleep(500 * time.Millisecond)
|
||||||
|
workConn, err := pxy.GetWorkConnFromPool()
|
||||||
|
if err != nil {
|
||||||
|
time.Sleep(5 * time.Second)
|
||||||
|
// check if proxy is closed
|
||||||
|
select {
|
||||||
|
case _, ok := <-pxy.checkCloseCh:
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
pxy.workConn = workConn
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
go workConnReaderFn(workConn)
|
||||||
|
go workConnSenderFn(workConn, ctx)
|
||||||
|
_, ok := <-pxy.checkCloseCh
|
||||||
|
cancel()
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Read from user connections and send wrapped udp message to sendCh.
|
||||||
|
// Client will transfor udp message to local udp service and waiting for response for a while.
|
||||||
|
// Response will be wrapped to be transfored in work connection to server.
|
||||||
|
udp.ForwardUserConn(udpConn, pxy.readCh, pxy.sendCh)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pxy *UdpProxy) GetConf() config.ProxyConf {
|
func (pxy *UdpProxy) GetConf() config.ProxyConf {
|
||||||
@ -238,42 +369,24 @@ func (pxy *UdpProxy) GetConf() config.ProxyConf {
|
|||||||
|
|
||||||
func (pxy *UdpProxy) Close() {
|
func (pxy *UdpProxy) Close() {
|
||||||
pxy.BaseProxy.Close()
|
pxy.BaseProxy.Close()
|
||||||
|
pxy.workConn.Close()
|
||||||
|
pxy.udpConn.Close()
|
||||||
|
close(pxy.checkCloseCh)
|
||||||
|
close(pxy.readCh)
|
||||||
|
close(pxy.sendCh)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HandleUserTcpConnection is used for incoming tcp user connections.
|
// HandleUserTcpConnection is used for incoming tcp user connections.
|
||||||
// It can be used for tcp, http, https type.
|
// It can be used for tcp, http, https type.
|
||||||
func HandleUserTcpConnection(pxy Proxy, userConn net.Conn) {
|
func HandleUserTcpConnection(pxy Proxy, userConn frpNet.Conn) {
|
||||||
defer userConn.Close()
|
defer userConn.Close()
|
||||||
ctl := pxy.GetControl()
|
|
||||||
var (
|
|
||||||
workConn net.Conn
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
// try all connections from the pool
|
// try all connections from the pool
|
||||||
for i := 0; i < ctl.poolCount+1; i++ {
|
workConn, err := pxy.GetWorkConnFromPool()
|
||||||
if workConn, err = ctl.GetWorkConn(); err != nil {
|
|
||||||
pxy.Warn("failed to get work connection: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer workConn.Close()
|
|
||||||
pxy.Info("get a new work connection: [%s]", workConn.RemoteAddr().String())
|
|
||||||
workConn.AddLogPrefix(pxy.GetName())
|
|
||||||
|
|
||||||
err := msg.WriteMsg(workConn, &msg.StartWorkConn{
|
|
||||||
ProxyName: pxy.GetName(),
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
workConn.Warn("failed to send message to work connection from pool: %v, times: %d", err, i)
|
|
||||||
workConn.Close()
|
|
||||||
} else {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
pxy.Error("try to get work connection failed in the end")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
defer workConn.Close()
|
||||||
|
|
||||||
var local io.ReadWriteCloser = workConn
|
var local io.ReadWriteCloser = workConn
|
||||||
cfg := pxy.GetConf().GetBaseInfo()
|
cfg := pxy.GetConf().GetBaseInfo()
|
||||||
|
@ -20,7 +20,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrMsgType = errors.New("message type error")
|
ErrMsgType = errors.New("message type error")
|
||||||
|
ErrCtlClosed = errors.New("control is closed")
|
||||||
)
|
)
|
||||||
|
|
||||||
func PanicToError(fn func()) (err error) {
|
func PanicToError(fn func()) (err error) {
|
||||||
|
@ -221,12 +221,25 @@ func ListenUDP(bindAddr string, bindPort int64) (l *UdpListener, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *UdpListener) writeUdpPacket(packet *UdpPacket) {
|
func (l *UdpListener) writeUdpPacket(packet *UdpPacket) (err error) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if err := recover(); err != nil {
|
if errRet := recover(); errRet != nil {
|
||||||
|
err = fmt.Errorf("udp write closed listener")
|
||||||
|
l.Info("udp write closed listener")
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
l.writeCh <- packet
|
l.writeCh <- packet
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *UdpListener) WriteMsg(buf []byte, remoteAddr *net.UDPAddr) (err error) {
|
||||||
|
// only set remote addr here
|
||||||
|
packet := &UdpPacket{
|
||||||
|
Buf: buf,
|
||||||
|
RemoteAddr: remoteAddr,
|
||||||
|
}
|
||||||
|
err = l.writeUdpPacket(packet)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *UdpListener) Accept() (Conn, error) {
|
func (l *UdpListener) Accept() (Conn, error) {
|
||||||
|
Loading…
Reference in New Issue
Block a user