UA3F/src/internal/server/base/tcp_linux.go
2025-12-09 16:24:55 +08:00

62 lines
1.5 KiB
Go

//go:build linux
package base
import (
"errors"
"fmt"
"net"
"syscall"
"golang.org/x/sys/unix"
)
const SO_MARK = 0xc9
// Connect dials the target address with SO_MARK set and returns the connection.
func Connect(addr string, mark int) (target net.Conn, err error) {
dialer := net.Dialer{
Control: func(network, address string, c syscall.RawConn) error {
return c.Control(func(fd uintptr) {
unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_MARK, mark)
})
},
}
conn, err := dialer.Dial("tcp", addr)
if err != nil {
return nil, fmt.Errorf("Connect dialer.Dial SO_MARK(%d): %v", mark, err)
}
return conn, nil
}
// GetOriginalDstAddr retrieves the original destination address of the redirected connection.
func GetOriginalDstAddr(conn net.Conn) (addr string, err error) {
fd, err := GetConnFD(conn)
if err != nil {
return "", fmt.Errorf("GetConnFD: %v", err)
}
raw, err := unix.GetsockoptIPv6Mreq(fd, unix.SOL_IP, unix.SO_ORIGINAL_DST)
if err != nil {
return "", fmt.Errorf("unix.GetsockoptIPv6Mreq: %v", err)
}
ip := net.IPv4(raw.Multiaddr[4], raw.Multiaddr[5], raw.Multiaddr[6], raw.Multiaddr[7])
port := uint16(raw.Multiaddr[2])<<8 + uint16(raw.Multiaddr[3])
return fmt.Sprintf("%s:%d", ip.String(), port), nil
}
func GetConnFD(conn net.Conn) (fd int, err error) {
tcpConn, ok := conn.(*net.TCPConn)
if !ok {
return 0, errors.New("GetConnFD connection is not *net.TCPConn")
}
file, err := tcpConn.File()
if err != nil {
return 0, fmt.Errorf("tcpConn.File: %v", err)
}
defer file.Close()
return int(file.Fd()), nil
}