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

     1  package redir
     2  
     3  import (
     4  	"encoding/binary"
     5  	"errors"
     6  	"net"
     7  	"net/netip"
     8  	"syscall"
     9  	"unsafe"
    10  
    11  	"golang.org/x/sys/unix"
    12  
    13  	"github.com/yaling888/clash/transport/socks5"
    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 (
    33  		addr     netip.AddrPort
    34  		innerErr error
    35  	)
    36  
    37  	err = rc.Control(func(fd uintptr) {
    38  		if ip4 := c.LocalAddr().(*net.TCPAddr).IP.To4(); ip4 != nil {
    39  			addr, innerErr = getorigdst(fd)
    40  		} else {
    41  			addr, innerErr = getorigdst6(fd)
    42  		}
    43  	})
    44  
    45  	if innerErr != nil {
    46  		err = innerErr
    47  	}
    48  
    49  	return socks5.AddrFromStdAddrPort(addr), err
    50  }
    51  
    52  // Call getorigdst() from linux/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c
    53  func getorigdst(fd uintptr) (netip.AddrPort, error) {
    54  	addr := unix.RawSockaddrInet4{}
    55  	size := uint32(unsafe.Sizeof(addr))
    56  	_, _, err := syscall.Syscall6(unix.SYS_GETSOCKOPT, fd, unix.IPPROTO_IP, SO_ORIGINAL_DST, uintptr(unsafe.Pointer(&addr)), uintptr(unsafe.Pointer(&size)), 0)
    57  	if err != 0 {
    58  		return netip.AddrPort{}, err
    59  	}
    60  	port := binary.BigEndian.Uint16((*(*[2]byte)(unsafe.Pointer(&addr.Port)))[:])
    61  	return netip.AddrPortFrom(netip.AddrFrom4(addr.Addr), port), nil
    62  }
    63  
    64  func getorigdst6(fd uintptr) (netip.AddrPort, error) {
    65  	addr := unix.RawSockaddrInet6{}
    66  	size := uint32(unsafe.Sizeof(addr))
    67  	_, _, err := syscall.Syscall6(unix.SYS_GETSOCKOPT, fd, unix.IPPROTO_IPV6, IP6T_SO_ORIGINAL_DST, uintptr(unsafe.Pointer(&addr)), uintptr(unsafe.Pointer(&size)), 0)
    68  	if err != 0 {
    69  		return netip.AddrPort{}, err
    70  	}
    71  	port := binary.BigEndian.Uint16((*(*[2]byte)(unsafe.Pointer(&addr.Port)))[:])
    72  	return netip.AddrPortFrom(netip.AddrFrom16(addr.Addr), port), nil
    73  }