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 }