github.com/v2fly/v2ray-core/v4@v4.45.2/transport/internet/tcp/sockopt_linux.go (about)

     1  //go:build linux && !confonly
     2  // +build linux,!confonly
     3  
     4  package tcp
     5  
     6  import (
     7  	"syscall"
     8  
     9  	"github.com/v2fly/v2ray-core/v4/common/net"
    10  	"github.com/v2fly/v2ray-core/v4/transport/internet"
    11  )
    12  
    13  const SO_ORIGINAL_DST = 80 // nolint: golint,stylecheck
    14  
    15  func GetOriginalDestination(conn internet.Connection) (net.Destination, error) {
    16  	sysrawconn, f := conn.(syscall.Conn)
    17  	if !f {
    18  		return net.Destination{}, newError("unable to get syscall.Conn")
    19  	}
    20  	rawConn, err := sysrawconn.SyscallConn()
    21  	if err != nil {
    22  		return net.Destination{}, newError("failed to get sys fd").Base(err)
    23  	}
    24  	var dest net.Destination
    25  	err = rawConn.Control(func(fd uintptr) {
    26  		var remoteIP net.IP
    27  		switch addr := conn.RemoteAddr().(type) {
    28  		case *net.TCPAddr:
    29  			remoteIP = addr.IP
    30  		case *net.UDPAddr:
    31  			remoteIP = addr.IP
    32  		default:
    33  			newError("failed to call getsockopt").WriteToLog()
    34  			return
    35  		}
    36  		if remoteIP.To4() != nil {
    37  			// ipv4
    38  			addr, err := syscall.GetsockoptIPv6Mreq(int(fd), syscall.IPPROTO_IP, SO_ORIGINAL_DST)
    39  			if err != nil {
    40  				newError("failed to call getsockopt").Base(err).WriteToLog()
    41  				return
    42  			}
    43  			ip := net.IPAddress(addr.Multiaddr[4:8])
    44  			port := uint16(addr.Multiaddr[2])<<8 + uint16(addr.Multiaddr[3])
    45  			dest = net.TCPDestination(ip, net.Port(port))
    46  		} else {
    47  			// ipv6
    48  			addr, err := syscall.GetsockoptIPv6MTUInfo(int(fd), syscall.IPPROTO_IPV6, SO_ORIGINAL_DST)
    49  			if err != nil {
    50  				newError("failed to call getsockopt").Base(err).WriteToLog()
    51  				return
    52  			}
    53  			ip := net.IPAddress(addr.Addr.Addr[:])
    54  			port := net.PortFromBytes([]byte{byte(addr.Addr.Port), byte(addr.Addr.Port >> 8)})
    55  			dest = net.TCPDestination(ip, port)
    56  		}
    57  	})
    58  	if err != nil {
    59  		return net.Destination{}, newError("failed to control connection").Base(err)
    60  	}
    61  	if !dest.IsValid() {
    62  		return net.Destination{}, newError("failed to call getsockopt")
    63  	}
    64  	return dest, nil
    65  }