mirror of
https://github.com/SagerNet/sing-box.git
synced 2024-12-26 14:25:39 +08:00
125 lines
3.2 KiB
Go
125 lines
3.2 KiB
Go
package redir
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"net"
|
|
"net/netip"
|
|
"os"
|
|
"strconv"
|
|
"syscall"
|
|
|
|
E "github.com/sagernet/sing/common/exceptions"
|
|
F "github.com/sagernet/sing/common/format"
|
|
M "github.com/sagernet/sing/common/metadata"
|
|
|
|
"golang.org/x/sys/unix"
|
|
)
|
|
|
|
func TProxy(fd uintptr, isIPv6 bool) error {
|
|
err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1)
|
|
if err == nil {
|
|
err = syscall.SetsockoptInt(int(fd), syscall.SOL_IP, syscall.IP_TRANSPARENT, 1)
|
|
}
|
|
if err == nil && isIPv6 {
|
|
err = syscall.SetsockoptInt(int(fd), syscall.SOL_IPV6, unix.IPV6_TRANSPARENT, 1)
|
|
}
|
|
if err == nil {
|
|
err = syscall.SetsockoptInt(int(fd), syscall.SOL_IP, syscall.IP_RECVORIGDSTADDR, 1)
|
|
}
|
|
if err == nil && isIPv6 {
|
|
err = syscall.SetsockoptInt(int(fd), syscall.SOL_IPV6, unix.IPV6_RECVORIGDSTADDR, 1)
|
|
}
|
|
return err
|
|
}
|
|
|
|
func GetOriginalDestinationFromOOB(oob []byte) (netip.AddrPort, error) {
|
|
controlMessages, err := unix.ParseSocketControlMessage(oob)
|
|
if err != nil {
|
|
return netip.AddrPort{}, err
|
|
}
|
|
for _, message := range controlMessages {
|
|
if message.Header.Level == unix.SOL_IP && message.Header.Type == unix.IP_RECVORIGDSTADDR {
|
|
return netip.AddrPortFrom(M.AddrFromIP(message.Data[4:8]), binary.BigEndian.Uint16(message.Data[2:4])), nil
|
|
} else if message.Header.Level == unix.SOL_IPV6 && message.Header.Type == unix.IPV6_RECVORIGDSTADDR {
|
|
return netip.AddrPortFrom(M.AddrFromIP(message.Data[8:24]), binary.BigEndian.Uint16(message.Data[2:4])), nil
|
|
}
|
|
}
|
|
return netip.AddrPort{}, E.New("not found")
|
|
}
|
|
|
|
func DialUDP(lAddr *net.UDPAddr, rAddr *net.UDPAddr) (*net.UDPConn, error) {
|
|
rSockAddr, err := udpAddrToSockAddr(rAddr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
lSockAddr, err := udpAddrToSockAddr(lAddr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
fd, err := syscall.Socket(udpAddrFamily(lAddr, rAddr), syscall.SOCK_DGRAM, 0)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err = syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1); err != nil {
|
|
syscall.Close(fd)
|
|
return nil, err
|
|
}
|
|
|
|
if err = syscall.SetsockoptInt(fd, syscall.SOL_IP, syscall.IP_TRANSPARENT, 1); err != nil {
|
|
syscall.Close(fd)
|
|
return nil, err
|
|
}
|
|
|
|
if err = syscall.Bind(fd, lSockAddr); err != nil {
|
|
syscall.Close(fd)
|
|
return nil, err
|
|
}
|
|
|
|
if err = syscall.Connect(fd, rSockAddr); err != nil {
|
|
syscall.Close(fd)
|
|
return nil, err
|
|
}
|
|
|
|
fdFile := os.NewFile(uintptr(fd), F.ToString("net-udp-dial-", rAddr))
|
|
defer fdFile.Close()
|
|
|
|
c, err := net.FileConn(fdFile)
|
|
if err != nil {
|
|
syscall.Close(fd)
|
|
return nil, err
|
|
}
|
|
|
|
return c.(*net.UDPConn), nil
|
|
}
|
|
|
|
func udpAddrToSockAddr(addr *net.UDPAddr) (syscall.Sockaddr, error) {
|
|
switch {
|
|
case addr.IP.To4() != nil:
|
|
ip := [4]byte{}
|
|
copy(ip[:], addr.IP.To4())
|
|
|
|
return &syscall.SockaddrInet4{Addr: ip, Port: addr.Port}, nil
|
|
|
|
default:
|
|
ip := [16]byte{}
|
|
copy(ip[:], addr.IP.To16())
|
|
|
|
zoneID, err := strconv.ParseUint(addr.Zone, 10, 32)
|
|
if err != nil {
|
|
zoneID = 0
|
|
}
|
|
|
|
return &syscall.SockaddrInet6{Addr: ip, Port: addr.Port, ZoneId: uint32(zoneID)}, nil
|
|
}
|
|
}
|
|
|
|
func udpAddrFamily(lAddr, rAddr *net.UDPAddr) int {
|
|
if (lAddr == nil || lAddr.IP.To4() != nil) && (rAddr == nil || lAddr.IP.To4() != nil) {
|
|
return syscall.AF_INET
|
|
}
|
|
return syscall.AF_INET6
|
|
}
|