mirror of
https://github.com/cedar2025/hysteria.git
synced 2025-06-15 00:49:54 +00:00
UDP TProxy implementation, no ACL support yet
This commit is contained in:
parent
2b4e660324
commit
7b3e1a5b41
@ -273,6 +273,35 @@ func client(config *clientConfig) {
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(config.UDPTProxy.Listen) > 0 {
|
||||||
|
go func() {
|
||||||
|
rl, err := tproxy.NewUDPTProxy(client, config.UDPTProxy.Listen,
|
||||||
|
time.Duration(config.UDPTProxy.Timeout)*time.Second, aclEngine,
|
||||||
|
func(addr net.Addr) {
|
||||||
|
logrus.WithFields(logrus.Fields{
|
||||||
|
"src": addr.String(),
|
||||||
|
}).Debug("UDP TProxy request")
|
||||||
|
},
|
||||||
|
func(addr net.Addr, err error) {
|
||||||
|
if err != tproxy.ErrTimeout {
|
||||||
|
logrus.WithFields(logrus.Fields{
|
||||||
|
"error": err,
|
||||||
|
"src": addr.String(),
|
||||||
|
}).Info("UDP TProxy error")
|
||||||
|
} else {
|
||||||
|
logrus.WithFields(logrus.Fields{
|
||||||
|
"src": addr.String(),
|
||||||
|
}).Debug("UDP TProxy session closed")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
logrus.WithField("error", err).Fatal("Failed to initialize UDP TProxy")
|
||||||
|
}
|
||||||
|
logrus.WithField("addr", config.UDPTProxy.Listen).Info("UDP TProxy up and running")
|
||||||
|
errChan <- rl.ListenAndServe()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
err = <-errChan
|
err = <-errChan
|
||||||
logrus.WithField("error", err).Fatal("Client shutdown")
|
logrus.WithField("error", err).Fatal("Client shutdown")
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,7 @@ func NewUDPRelay(hyClient *core.Client, listen, remote string, timeout time.Dura
|
|||||||
return r, nil
|
return r, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type cmEntry struct {
|
type connEntry struct {
|
||||||
HyConn core.UDPConn
|
HyConn core.UDPConn
|
||||||
Deadline atomic.Value
|
Deadline atomic.Value
|
||||||
}
|
}
|
||||||
@ -55,7 +55,7 @@ func (r *UDPRelay) ListenAndServe() error {
|
|||||||
}
|
}
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
// src <-> HyClient UDPConn
|
// src <-> HyClient UDPConn
|
||||||
connMap := make(map[string]*cmEntry)
|
connMap := make(map[string]*connEntry)
|
||||||
var connMapMutex sync.RWMutex
|
var connMapMutex sync.RWMutex
|
||||||
// Read loop
|
// Read loop
|
||||||
buf := make([]byte, udpBufferSize)
|
buf := make([]byte, udpBufferSize)
|
||||||
@ -77,7 +77,7 @@ func (r *UDPRelay) ListenAndServe() error {
|
|||||||
r.ErrorFunc(rAddr, err)
|
r.ErrorFunc(rAddr, err)
|
||||||
} else {
|
} else {
|
||||||
// Add it to the map
|
// Add it to the map
|
||||||
ent := &cmEntry{HyConn: hyConn}
|
ent := &connEntry{HyConn: hyConn}
|
||||||
ent.Deadline.Store(time.Now().Add(r.Timeout))
|
ent.Deadline.Store(time.Now().Add(r.Timeout))
|
||||||
connMapMutex.Lock()
|
connMapMutex.Lock()
|
||||||
connMap[rAddr.String()] = ent
|
connMap[rAddr.String()] = ent
|
||||||
|
124
pkg/tproxy/udp_linux.go
Normal file
124
pkg/tproxy/udp_linux.go
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
package tproxy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"github.com/LiamHaworth/go-tproxy"
|
||||||
|
"github.com/tobyxdd/hysteria/pkg/acl"
|
||||||
|
"github.com/tobyxdd/hysteria/pkg/core"
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const udpBufferSize = 65535
|
||||||
|
|
||||||
|
var ErrTimeout = errors.New("inactivity timeout")
|
||||||
|
|
||||||
|
type UDPTProxy struct {
|
||||||
|
HyClient *core.Client
|
||||||
|
ListenAddr *net.UDPAddr
|
||||||
|
Timeout time.Duration
|
||||||
|
ACLEngine *acl.Engine
|
||||||
|
|
||||||
|
ConnFunc func(addr net.Addr)
|
||||||
|
ErrorFunc func(addr net.Addr, err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUDPTProxy(hyClient *core.Client, listen string, timeout time.Duration, aclEngine *acl.Engine,
|
||||||
|
connFunc func(addr net.Addr), errorFunc func(addr net.Addr, err error)) (*UDPTProxy, error) {
|
||||||
|
uAddr, err := net.ResolveUDPAddr("udp", listen)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
r := &UDPTProxy{
|
||||||
|
HyClient: hyClient,
|
||||||
|
ListenAddr: uAddr,
|
||||||
|
Timeout: timeout,
|
||||||
|
ACLEngine: aclEngine,
|
||||||
|
ConnFunc: connFunc,
|
||||||
|
ErrorFunc: errorFunc,
|
||||||
|
}
|
||||||
|
if timeout == 0 {
|
||||||
|
r.Timeout = 1 * time.Minute
|
||||||
|
}
|
||||||
|
return r, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type connEntry struct {
|
||||||
|
HyConn core.UDPConn
|
||||||
|
Deadline atomic.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *UDPTProxy) ListenAndServe() error {
|
||||||
|
conn, err := tproxy.ListenUDP("udp", r.ListenAddr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
// src <-> HyClient UDPConn
|
||||||
|
connMap := make(map[string]*connEntry)
|
||||||
|
var connMapMutex sync.RWMutex
|
||||||
|
// Read loop
|
||||||
|
buf := make([]byte, udpBufferSize)
|
||||||
|
for {
|
||||||
|
n, srcAddr, dstAddr, err := tproxy.ReadFromUDP(conn, buf)
|
||||||
|
if n > 0 {
|
||||||
|
connMapMutex.RLock()
|
||||||
|
cme := connMap[srcAddr.String()]
|
||||||
|
connMapMutex.RUnlock()
|
||||||
|
if cme != nil {
|
||||||
|
// Existing conn
|
||||||
|
cme.Deadline.Store(time.Now().Add(r.Timeout))
|
||||||
|
_ = cme.HyConn.WriteTo(buf[:n], dstAddr.String())
|
||||||
|
} else {
|
||||||
|
// New
|
||||||
|
r.ConnFunc(srcAddr)
|
||||||
|
hyConn, err := r.HyClient.DialUDP()
|
||||||
|
if err != nil {
|
||||||
|
r.ErrorFunc(srcAddr, err)
|
||||||
|
} else {
|
||||||
|
// Add it to the map
|
||||||
|
ent := &connEntry{HyConn: hyConn}
|
||||||
|
ent.Deadline.Store(time.Now().Add(r.Timeout))
|
||||||
|
connMapMutex.Lock()
|
||||||
|
connMap[srcAddr.String()] = ent
|
||||||
|
connMapMutex.Unlock()
|
||||||
|
// Start remote to local
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
bs, _, err := hyConn.ReadFrom()
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
ent.Deadline.Store(time.Now().Add(r.Timeout))
|
||||||
|
_, _ = conn.WriteToUDP(bs, srcAddr)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
// Timeout cleanup routine
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
ttl := ent.Deadline.Load().(time.Time).Sub(time.Now())
|
||||||
|
if ttl <= 0 {
|
||||||
|
// Time to die
|
||||||
|
connMapMutex.Lock()
|
||||||
|
_ = hyConn.Close()
|
||||||
|
delete(connMap, srcAddr.String())
|
||||||
|
connMapMutex.Unlock()
|
||||||
|
r.ErrorFunc(srcAddr, ErrTimeout)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
time.Sleep(ttl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
// Send the packet
|
||||||
|
_ = hyConn.WriteTo(buf[:n], dstAddr.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
24
pkg/tproxy/udp_stub.go
Normal file
24
pkg/tproxy/udp_stub.go
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
// +build !linux
|
||||||
|
|
||||||
|
package tproxy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"github.com/tobyxdd/hysteria/pkg/acl"
|
||||||
|
"github.com/tobyxdd/hysteria/pkg/core"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ErrTimeout = errors.New("inactivity timeout")
|
||||||
|
|
||||||
|
type UDPTProxy struct{}
|
||||||
|
|
||||||
|
func NewUDPTProxy(hyClient *core.Client, listen string, timeout time.Duration, aclEngine *acl.Engine,
|
||||||
|
connFunc func(addr net.Addr), errorFunc func(addr net.Addr, err error)) (*UDPTProxy, error) {
|
||||||
|
return nil, errors.New("not supported on the current system")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *UDPTProxy) ListenAndServe() error {
|
||||||
|
return nil
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user