mirror of
https://github.com/cmz0228/hysteria-dev.git
synced 2025-06-09 05:49:54 +00:00
183 lines
3.9 KiB
Go
183 lines
3.9 KiB
Go
// +build cgo
|
|
|
|
package tun
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"fmt"
|
|
tun2socks "github.com/eycorsican/go-tun2socks/core"
|
|
"github.com/tobyxdd/hysteria/pkg/acl"
|
|
"github.com/tobyxdd/hysteria/pkg/core"
|
|
"io"
|
|
"net"
|
|
"strconv"
|
|
"sync/atomic"
|
|
"time"
|
|
)
|
|
|
|
const udpBufferSize = 65535
|
|
|
|
type udpConnInfo struct {
|
|
hyConn core.UDPConn
|
|
target string
|
|
expire atomic.Value
|
|
}
|
|
|
|
func (s *Server) fetchUDPInput(conn tun2socks.UDPConn, ci *udpConnInfo) {
|
|
defer func() {
|
|
s.closeUDPConn(conn)
|
|
}()
|
|
|
|
if s.Timeout > 0 {
|
|
go func() {
|
|
for {
|
|
life := ci.expire.Load().(time.Time).Sub(time.Now())
|
|
if life < 0 {
|
|
s.closeUDPConn(conn)
|
|
break
|
|
} else {
|
|
time.Sleep(life)
|
|
}
|
|
}
|
|
}()
|
|
}
|
|
|
|
var err error
|
|
|
|
for {
|
|
var bs []byte
|
|
var from string
|
|
bs, from, err = ci.hyConn.ReadFrom()
|
|
if err != nil {
|
|
break
|
|
}
|
|
ci.expire.Store(time.Now().Add(s.Timeout))
|
|
udpAddr, _ := net.ResolveUDPAddr("udp", from)
|
|
_, err = conn.WriteFrom(bs, udpAddr)
|
|
if err != nil {
|
|
break
|
|
}
|
|
}
|
|
|
|
if s.ErrorFunc != nil {
|
|
s.ErrorFunc(conn.LocalAddr(), ci.target, err)
|
|
}
|
|
}
|
|
|
|
func (s *Server) Connect(conn tun2socks.UDPConn, target *net.UDPAddr) error {
|
|
action, arg := acl.ActionProxy, ""
|
|
var resErr error
|
|
if s.ACLEngine != nil {
|
|
action, arg, _, resErr = s.ACLEngine.ResolveAndMatch(target.IP.String())
|
|
}
|
|
if s.RequestFunc != nil {
|
|
s.RequestFunc(conn.LocalAddr(), target.String(), action, arg)
|
|
}
|
|
var hyConn core.UDPConn
|
|
var closeErr error
|
|
defer func() {
|
|
if s.ErrorFunc != nil && closeErr != nil {
|
|
s.ErrorFunc(conn.LocalAddr(), target.String(), closeErr)
|
|
}
|
|
}()
|
|
switch action {
|
|
case acl.ActionDirect:
|
|
if resErr != nil {
|
|
closeErr = resErr
|
|
return resErr
|
|
}
|
|
var relayConn net.Conn
|
|
relayConn, closeErr = s.Transport.LocalDial("udp", target.String())
|
|
if closeErr != nil {
|
|
return closeErr
|
|
}
|
|
hyConn = &delegatedUDPConn{
|
|
underlayConn: relayConn,
|
|
delegatedRemoteAddr: target.String(),
|
|
}
|
|
case acl.ActionProxy:
|
|
hyConn, closeErr = s.HyClient.DialUDP()
|
|
if closeErr != nil {
|
|
return closeErr
|
|
}
|
|
case acl.ActionBlock:
|
|
closeErr = errors.New("blocked in ACL")
|
|
return closeErr
|
|
case acl.ActionHijack:
|
|
hijackAddr := net.JoinHostPort(arg, strconv.Itoa(target.Port))
|
|
var relayConn net.Conn
|
|
relayConn, closeErr = s.Transport.LocalDial("udp", hijackAddr)
|
|
if closeErr != nil {
|
|
return closeErr
|
|
}
|
|
hyConn = &delegatedUDPConn{
|
|
underlayConn: relayConn,
|
|
delegatedRemoteAddr: target.String(),
|
|
}
|
|
default:
|
|
closeErr = fmt.Errorf("unknown action %d", action)
|
|
return nil
|
|
}
|
|
ci := udpConnInfo{
|
|
hyConn: hyConn,
|
|
target: net.JoinHostPort(target.IP.String(), strconv.Itoa(target.Port)),
|
|
}
|
|
ci.expire.Store(time.Now().Add(s.Timeout))
|
|
s.udpConnMapLock.Lock()
|
|
s.udpConnMap[conn] = &ci
|
|
s.udpConnMapLock.Unlock()
|
|
go s.fetchUDPInput(conn, &ci)
|
|
return nil
|
|
}
|
|
|
|
func (s *Server) ReceiveTo(conn tun2socks.UDPConn, data []byte, addr *net.UDPAddr) error {
|
|
s.udpConnMapLock.RLock()
|
|
ci, ok := s.udpConnMap[conn]
|
|
s.udpConnMapLock.RUnlock()
|
|
if !ok {
|
|
err := errors.New("previous connection closed for timeout")
|
|
s.ErrorFunc(conn.LocalAddr(), addr.String(), err)
|
|
return err
|
|
}
|
|
ci.expire.Store(time.Now().Add(s.Timeout))
|
|
_ = ci.hyConn.WriteTo(data, addr.String())
|
|
return nil
|
|
}
|
|
|
|
func (s *Server) closeUDPConn(conn tun2socks.UDPConn) {
|
|
conn.Close()
|
|
s.udpConnMapLock.Lock()
|
|
defer s.udpConnMapLock.Unlock()
|
|
if c, ok := s.udpConnMap[conn]; ok {
|
|
c.hyConn.Close()
|
|
delete(s.udpConnMap, conn)
|
|
}
|
|
}
|
|
|
|
type delegatedUDPConn struct {
|
|
underlayConn net.Conn
|
|
delegatedRemoteAddr string
|
|
}
|
|
|
|
func (c *delegatedUDPConn) ReadFrom() (bs []byte, addr string, err error) {
|
|
buf := make([]byte, udpBufferSize)
|
|
n, err := c.underlayConn.Read(buf)
|
|
if n > 0 {
|
|
bs = append(bs, buf[0:n]...)
|
|
}
|
|
if err != nil || err == io.EOF {
|
|
addr = c.delegatedRemoteAddr
|
|
}
|
|
return
|
|
}
|
|
|
|
func (c *delegatedUDPConn) WriteTo(bs []byte, addr string) error {
|
|
_, err := io.Copy(c.underlayConn, bytes.NewReader(bs))
|
|
return err
|
|
}
|
|
|
|
func (c *delegatedUDPConn) Close() error {
|
|
return c.underlayConn.Close()
|
|
}
|