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 }