github.com/igoogolx/clash@v1.19.8/listener/redir/tcp_linux.go (about)

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