github.com/chwjbn/xclash@v0.2.0/listener/redir/tcp_linux.go (about)

     1  package redir
     2  
     3  import (
     4  	"errors"
     5  	"net"
     6  	"syscall"
     7  	"unsafe"
     8  
     9  	"github.com/chwjbn/xclash/transport/socks5"
    10  )
    11  
    12  const (
    13  	SO_ORIGINAL_DST      = 80 // from linux/include/uapi/linux/netfilter_ipv4.h
    14  	IP6T_SO_ORIGINAL_DST = 80 // from linux/include/uapi/linux/netfilter_ipv6/ip6_tables.h
    15  )
    16  
    17  func parserPacket(conn net.Conn) (socks5.Addr, error) {
    18  	c, ok := conn.(*net.TCPConn)
    19  	if !ok {
    20  		return nil, errors.New("only work with TCP connection")
    21  	}
    22  
    23  	rc, err := c.SyscallConn()
    24  	if err != nil {
    25  		return nil, err
    26  	}
    27  
    28  	var addr socks5.Addr
    29  
    30  	rc.Control(func(fd uintptr) {
    31  		addr, err = getorigdst(fd)
    32  	})
    33  
    34  	return addr, err
    35  }
    36  
    37  // Call getorigdst() from linux/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c
    38  func getorigdst(fd uintptr) (socks5.Addr, error) {
    39  	raw := syscall.RawSockaddrInet4{}
    40  	siz := unsafe.Sizeof(raw)
    41  	if err := socketcall(GETSOCKOPT, fd, syscall.IPPROTO_IP, SO_ORIGINAL_DST, uintptr(unsafe.Pointer(&raw)), uintptr(unsafe.Pointer(&siz)), 0); err != nil {
    42  		return nil, err
    43  	}
    44  
    45  	addr := make([]byte, 1+net.IPv4len+2)
    46  	addr[0] = socks5.AtypIPv4
    47  	copy(addr[1:1+net.IPv4len], raw.Addr[:])
    48  	port := (*[2]byte)(unsafe.Pointer(&raw.Port)) // big-endian
    49  	addr[1+net.IPv4len], addr[1+net.IPv4len+1] = port[0], port[1]
    50  	return addr, nil
    51  }