forked from ms/transocks
79 lines
2.0 KiB
Go
79 lines
2.0 KiB
Go
//go:build linux
|
|
// +build linux
|
|
|
|
package transocks
|
|
|
|
import (
|
|
"net"
|
|
"os"
|
|
"unsafe"
|
|
|
|
syscall "golang.org/x/sys/unix"
|
|
)
|
|
|
|
func getsockopt(s int, level int, optname int, optval unsafe.Pointer, optlen *uint32) (err error) {
|
|
_, _, e := syscall.Syscall6(
|
|
syscall.SYS_GETSOCKOPT, uintptr(s), uintptr(level), uintptr(optname),
|
|
uintptr(optval), uintptr(unsafe.Pointer(optlen)), 0)
|
|
if e != 0 {
|
|
return e
|
|
}
|
|
return
|
|
}
|
|
|
|
// GetOriginalDST retrieves the original destination address from
|
|
// NATed connection. Currently, only Linux iptables using DNAT/REDIRECT
|
|
// is supported. For other operating systems, this will just return
|
|
// conn.LocalAddr().
|
|
//
|
|
// Note that this function only works when nf_conntrack_ipv4 and/or
|
|
// nf_conntrack_ipv6 is loaded in the kernel.
|
|
func GetOriginalDST(conn *net.TCPConn) (*net.TCPAddr, error) {
|
|
f, err := conn.File()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer func() { _ = f.Close() }()
|
|
|
|
fd := int(f.Fd())
|
|
// revert to non-blocking mode.
|
|
// see http://stackoverflow.com/a/28968431/1493661
|
|
if err = syscall.SetNonblock(fd, true); err != nil {
|
|
return nil, os.NewSyscallError("setnonblock", err)
|
|
}
|
|
|
|
v6 := conn.LocalAddr().(*net.TCPAddr).IP.To4() == nil
|
|
if v6 {
|
|
var addr syscall.RawSockaddrInet6
|
|
len := uint32(unsafe.Sizeof(addr))
|
|
err = getsockopt(fd, syscall.IPPROTO_IPV6, IP6T_SO_ORIGINAL_DST,
|
|
unsafe.Pointer(&addr), &len)
|
|
if err != nil {
|
|
return nil, os.NewSyscallError("getsockopt", err)
|
|
}
|
|
ip := make([]byte, 16)
|
|
copy(ip, addr.Addr[:])
|
|
pb := *(*[2]byte)(unsafe.Pointer(&addr.Port))
|
|
return &net.TCPAddr{
|
|
IP: ip,
|
|
Port: int(pb[0])*256 + int(pb[1]),
|
|
}, nil
|
|
}
|
|
|
|
// IPv4
|
|
var addr syscall.RawSockaddrInet4
|
|
len := uint32(unsafe.Sizeof(addr))
|
|
err = getsockopt(fd, syscall.IPPROTO_IP, SO_ORIGINAL_DST,
|
|
unsafe.Pointer(&addr), &len)
|
|
if err != nil {
|
|
return nil, os.NewSyscallError("getsockopt", err)
|
|
}
|
|
ip := make([]byte, 4)
|
|
copy(ip, addr.Addr[:])
|
|
pb := *(*[2]byte)(unsafe.Pointer(&addr.Port))
|
|
return &net.TCPAddr{
|
|
IP: ip,
|
|
Port: int(pb[0])*256 + int(pb[1]),
|
|
}, nil
|
|
}
|