Haruue Icymoon f3f604b59a
refactor(tun): switch to xjasonlyu/tun2socks
fix: #306, #394, #185, #161

break changes:

we won't create or maintain the interface now, so it is user's duty to
create the tun interface, for example, with command like
  ip tuntap add dev hytun mode tun
(or maybe we can use songgao/water to keep this behavior unchanged).

correspondingly, the "address", "gateway", "mask", "dns", "persist"
options in the "tun" config have been removed.

in addition, please note xjasonlyu/tun2socks is licensed under GPLv3 and
hysteria is licensed under MIT, I don't ensure is it legal to use it as
a go mod, but there are too many requests related on it so whatever...
2022-08-10 02:43:07 +08:00

111 lines
2.1 KiB
Go

package tun
import (
"fmt"
"github.com/tobyxdd/hysteria/pkg/core"
"github.com/xjasonlyu/tun2socks/v2/core/adapter"
"net"
"strconv"
"time"
)
const udpBufferSize = 65535
func (s *Server) HandleUDP(conn adapter.UDPConn) {
go s.handleUDPConn(conn)
}
func (s *Server) handleUDPConn(conn adapter.UDPConn) {
defer conn.Close()
id := conn.ID()
remoteAddr := net.UDPAddr{
IP: net.IP(id.LocalAddress),
Port: int(id.LocalPort),
}
localAddr := net.UDPAddr{
IP: net.IP(id.RemoteAddress),
Port: int(id.RemotePort),
}
if s.RequestFunc != nil {
s.RequestFunc(&localAddr, remoteAddr.String())
}
var err error
defer func() {
if s.ErrorFunc != nil && err != nil {
s.ErrorFunc(&localAddr, remoteAddr.String(), err)
}
}()
rc, err := s.HyClient.DialUDP()
if err != nil {
return
}
defer rc.Close()
err = s.relayUDP(conn, rc, &remoteAddr, s.Timeout)
}
func (s *Server) relayUDP(lc adapter.UDPConn, rc core.UDPConn, to *net.UDPAddr, timeout time.Duration) (err error) {
errChan := make(chan error, 2)
// local => remote
go func() {
buf := make([]byte, udpBufferSize)
for {
if timeout != 0 {
_ = lc.SetDeadline(time.Now().Add(timeout))
n, err := lc.Read(buf)
if n > 0 {
err = rc.WriteTo(buf[:n], to.String())
if err != nil {
errChan <- err
return
}
}
if err != nil {
errChan <- err
return
}
}
}
}()
// remote => local
go func() {
for {
pkt, addr, err := rc.ReadFrom()
if err != nil {
errChan <- err
return
}
if pkt != nil {
host, portStr, err := net.SplitHostPort(addr)
if err != nil {
errChan <- err
return
}
port, err := strconv.Atoi(portStr)
if err != nil {
errChan <- fmt.Errorf("cannot parse as port: %s", portStr)
return
}
// adapter.UDPConn doesn't support WriteFrom() yet,
// so we check the src address and behavior like a symmetric NAT
if !to.IP.Equal(net.ParseIP(host)) || to.Port != port {
// drop the packet silently
continue
}
_, err = lc.Write(pkt)
if err != nil {
errChan <- err
return
}
}
}
}()
return <-errChan
}