github.com/yaling888/clash@v1.53.0/listener/redir/tcp_linux.go (about)

     1  package redir
     2  
     3  import (
     4  	"encoding/binary"
     5  	"errors"
     6  	"net"
     7  	"net/netip"
     8  	"unsafe"
     9  
    10  	"golang.org/x/sys/unix"
    11  
    12  	"github.com/yaling888/clash/transport/socks5"
    13  )
    14  
    15  func parserPacket(conn net.Conn) (socks5.Addr, error) {
    16  	c, ok := conn.(*net.TCPConn)
    17  	if !ok {
    18  		return nil, errors.New("only work with TCP connection")
    19  	}
    20  
    21  	rc, err := c.SyscallConn()
    22  	if err != nil {
    23  		return nil, err
    24  	}
    25  
    26  	var (
    27  		addr     netip.AddrPort
    28  		innerErr error
    29  	)
    30  
    31  	err = rc.Control(func(fd uintptr) {
    32  		if ip4 := c.LocalAddr().(*net.TCPAddr).IP.To4(); ip4 != nil {
    33  			addr, innerErr = getorigdst(fd)
    34  		} else {
    35  			addr, innerErr = getorigdst6(fd)
    36  		}
    37  	})
    38  
    39  	if innerErr != nil {
    40  		err = innerErr
    41  	}
    42  
    43  	return socks5.AddrFromStdAddrPort(addr), err
    44  }
    45  
    46  // Call getorigdst() from linux/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c
    47  func getorigdst(fd uintptr) (netip.AddrPort, error) {
    48  	addr4 := unix.RawSockaddrInet4{}
    49  	size := uint32(unsafe.Sizeof(addr4))
    50  	if err := socketcall(GETSOCKOPT, fd, unix.IPPROTO_IP, unix.SO_ORIGINAL_DST, uintptr(unsafe.Pointer(&addr4)), uintptr(unsafe.Pointer(&size)), 0); err != nil {
    51  		return netip.AddrPort{}, err
    52  	}
    53  	port := binary.BigEndian.Uint16((*(*[2]byte)(unsafe.Pointer(&addr4.Port)))[:])
    54  	return netip.AddrPortFrom(netip.AddrFrom4(addr4.Addr), port), nil
    55  }
    56  
    57  func getorigdst6(fd uintptr) (netip.AddrPort, error) {
    58  	addr6 := unix.RawSockaddrInet6{}
    59  	size := uint32(unsafe.Sizeof(addr6))
    60  	if err := socketcall(GETSOCKOPT, fd, unix.IPPROTO_IPV6, unix.SO_ORIGINAL_DST, uintptr(unsafe.Pointer(&addr6)), uintptr(unsafe.Pointer(&size)), 0); err != nil {
    61  		return netip.AddrPort{}, err
    62  	}
    63  	port := binary.BigEndian.Uint16((*(*[2]byte)(unsafe.Pointer(&addr6.Port)))[:])
    64  	return netip.AddrPortFrom(netip.AddrFrom16(addr6.Addr), port), nil
    65  }