clash/listener/redir/tcp_linux.go

65 lines
1.8 KiB
Go
Raw Normal View History

2018-08-11 20:00:34 +00:00
package redir
import (
2022-12-22 04:00:56 +00:00
"encoding/binary"
2018-08-11 20:00:34 +00:00
"errors"
"net"
2022-12-22 04:00:56 +00:00
"net/netip"
2018-08-11 20:00:34 +00:00
"syscall"
"unsafe"
2021-05-13 14:39:33 +00:00
"github.com/Dreamacro/clash/transport/socks5"
2022-12-22 04:00:56 +00:00
"golang.org/x/sys/unix"
2018-08-11 20:00:34 +00:00
)
const (
SO_ORIGINAL_DST = 80 // from linux/include/uapi/linux/netfilter_ipv4.h
IP6T_SO_ORIGINAL_DST = 80 // from linux/include/uapi/linux/netfilter_ipv6/ip6_tables.h
)
2019-04-25 05:48:47 +00:00
func parserPacket(conn net.Conn) (socks5.Addr, error) {
2018-08-11 20:00:34 +00:00
c, ok := conn.(*net.TCPConn)
if !ok {
return nil, errors.New("only work with TCP connection")
}
rc, err := c.SyscallConn()
if err != nil {
return nil, err
}
2022-12-22 04:00:56 +00:00
var addr netip.AddrPort
2018-08-11 20:00:34 +00:00
rc.Control(func(fd uintptr) {
2022-12-22 11:25:30 +00:00
if ip4 := c.LocalAddr().(*net.TCPAddr).IP.To4(); ip4 != nil {
addr, err = getorigdst(fd)
} else {
addr, err = getorigdst6(fd)
}
2018-08-11 20:00:34 +00:00
})
2022-12-22 04:00:56 +00:00
return socks5.AddrFromStdAddrPort(addr), err
2018-08-11 20:00:34 +00:00
}
// Call getorigdst() from linux/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c
2022-12-22 04:00:56 +00:00
func getorigdst(fd uintptr) (netip.AddrPort, error) {
addr := unix.RawSockaddrInet4{}
size := uint32(unsafe.Sizeof(addr))
if err := socketcall(GETSOCKOPT, fd, syscall.IPPROTO_IP, SO_ORIGINAL_DST, uintptr(unsafe.Pointer(&addr)), uintptr(unsafe.Pointer(&size)), 0); err != nil {
return netip.AddrPort{}, err
2018-08-11 20:00:34 +00:00
}
2022-12-22 04:00:56 +00:00
port := binary.BigEndian.Uint16((*(*[2]byte)(unsafe.Pointer(&addr.Port)))[:])
return netip.AddrPortFrom(netip.AddrFrom4(addr.Addr), port), nil
2018-08-11 20:00:34 +00:00
}
2022-12-22 11:25:30 +00:00
func getorigdst6(fd uintptr) (netip.AddrPort, error) {
addr := unix.RawSockaddrInet6{}
size := uint32(unsafe.Sizeof(addr))
if err := socketcall(GETSOCKOPT, fd, syscall.IPPROTO_IPV6, IP6T_SO_ORIGINAL_DST, uintptr(unsafe.Pointer(&addr)), uintptr(unsafe.Pointer(&size)), 0); err != nil {
return netip.AddrPort{}, err
}
port := binary.BigEndian.Uint16((*(*[2]byte)(unsafe.Pointer(&addr.Port)))[:])
return netip.AddrPortFrom(netip.AddrFrom16(addr.Addr), port), nil
}