mirror of https://github.com/XTLS/Xray-core
				
				
				
			
		
			
				
	
	
		
			266 lines
		
	
	
		
			7.8 KiB
		
	
	
	
		
			Go
		
	
	
			
		
		
	
	
			266 lines
		
	
	
		
			7.8 KiB
		
	
	
	
		
			Go
		
	
	
package internet
 | 
						|
 | 
						|
import (
 | 
						|
	"encoding/binary"
 | 
						|
	"net"
 | 
						|
	"os"
 | 
						|
	"syscall"
 | 
						|
	"unsafe"
 | 
						|
 | 
						|
	"github.com/xtls/xray-core/common/errors"
 | 
						|
	"golang.org/x/sys/unix"
 | 
						|
)
 | 
						|
 | 
						|
const (
 | 
						|
	sysPFINOUT     = 0x0
 | 
						|
	sysPFIN        = 0x1
 | 
						|
	sysPFOUT       = 0x2
 | 
						|
	sysPFFWD       = 0x3
 | 
						|
	sysDIOCNATLOOK = 0xc04c4417
 | 
						|
)
 | 
						|
 | 
						|
type pfiocNatlook struct {
 | 
						|
	Saddr     [16]byte /* pf_addr */
 | 
						|
	Daddr     [16]byte /* pf_addr */
 | 
						|
	Rsaddr    [16]byte /* pf_addr */
 | 
						|
	Rdaddr    [16]byte /* pf_addr */
 | 
						|
	Sport     uint16
 | 
						|
	Dport     uint16
 | 
						|
	Rsport    uint16
 | 
						|
	Rdport    uint16
 | 
						|
	Af        uint8
 | 
						|
	Proto     uint8
 | 
						|
	Direction uint8
 | 
						|
	Pad       [1]byte
 | 
						|
}
 | 
						|
 | 
						|
const (
 | 
						|
	sizeofPfiocNatlook = 0x4c
 | 
						|
	soReUsePort        = 0x00000200
 | 
						|
	soReUsePortLB      = 0x00010000
 | 
						|
)
 | 
						|
 | 
						|
func ioctl(s uintptr, ioc int, b []byte) error {
 | 
						|
	if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, s, uintptr(ioc), uintptr(unsafe.Pointer(&b[0]))); errno != 0 {
 | 
						|
		return error(errno)
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func (nl *pfiocNatlook) rdPort() int {
 | 
						|
	return int(binary.BigEndian.Uint16((*[2]byte)(unsafe.Pointer(&nl.Rdport))[:]))
 | 
						|
}
 | 
						|
 | 
						|
func (nl *pfiocNatlook) setPort(remote, local int) {
 | 
						|
	binary.BigEndian.PutUint16((*[2]byte)(unsafe.Pointer(&nl.Sport))[:], uint16(remote))
 | 
						|
	binary.BigEndian.PutUint16((*[2]byte)(unsafe.Pointer(&nl.Dport))[:], uint16(local))
 | 
						|
}
 | 
						|
 | 
						|
// OriginalDst uses ioctl to read original destination from /dev/pf
 | 
						|
func OriginalDst(la, ra net.Addr) (net.IP, int, error) {
 | 
						|
	f, err := os.Open("/dev/pf")
 | 
						|
	if err != nil {
 | 
						|
		return net.IP{}, -1, errors.New("failed to open device /dev/pf").Base(err)
 | 
						|
	}
 | 
						|
	defer f.Close()
 | 
						|
	fd := f.Fd()
 | 
						|
	b := make([]byte, sizeofPfiocNatlook)
 | 
						|
	nl := (*pfiocNatlook)(unsafe.Pointer(&b[0]))
 | 
						|
	var raIP, laIP net.IP
 | 
						|
	var raPort, laPort int
 | 
						|
	switch la.(type) {
 | 
						|
	case *net.TCPAddr:
 | 
						|
		raIP = ra.(*net.TCPAddr).IP
 | 
						|
		laIP = la.(*net.TCPAddr).IP
 | 
						|
		raPort = ra.(*net.TCPAddr).Port
 | 
						|
		laPort = la.(*net.TCPAddr).Port
 | 
						|
		nl.Proto = syscall.IPPROTO_TCP
 | 
						|
	case *net.UDPAddr:
 | 
						|
		raIP = ra.(*net.UDPAddr).IP
 | 
						|
		laIP = la.(*net.UDPAddr).IP
 | 
						|
		raPort = ra.(*net.UDPAddr).Port
 | 
						|
		laPort = la.(*net.UDPAddr).Port
 | 
						|
		nl.Proto = syscall.IPPROTO_UDP
 | 
						|
	}
 | 
						|
	if raIP.To4() != nil {
 | 
						|
		if laIP.IsUnspecified() {
 | 
						|
			laIP = net.ParseIP("127.0.0.1")
 | 
						|
		}
 | 
						|
		copy(nl.Saddr[:net.IPv4len], raIP.To4())
 | 
						|
		copy(nl.Daddr[:net.IPv4len], laIP.To4())
 | 
						|
		nl.Af = syscall.AF_INET
 | 
						|
	}
 | 
						|
	if raIP.To16() != nil && raIP.To4() == nil {
 | 
						|
		if laIP.IsUnspecified() {
 | 
						|
			laIP = net.ParseIP("::1")
 | 
						|
		}
 | 
						|
		copy(nl.Saddr[:], raIP)
 | 
						|
		copy(nl.Daddr[:], laIP)
 | 
						|
		nl.Af = syscall.AF_INET6
 | 
						|
	}
 | 
						|
	nl.setPort(raPort, laPort)
 | 
						|
	ioc := uintptr(sysDIOCNATLOOK)
 | 
						|
	for _, dir := range []byte{sysPFOUT, sysPFIN} {
 | 
						|
		nl.Direction = dir
 | 
						|
		err = ioctl(fd, int(ioc), b)
 | 
						|
		if err == nil || err != syscall.ENOENT {
 | 
						|
			break
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if err != nil {
 | 
						|
		return net.IP{}, -1, os.NewSyscallError("ioctl", err)
 | 
						|
	}
 | 
						|
 | 
						|
	odPort := nl.rdPort()
 | 
						|
	var odIP net.IP
 | 
						|
	switch nl.Af {
 | 
						|
	case syscall.AF_INET:
 | 
						|
		odIP = make(net.IP, net.IPv4len)
 | 
						|
		copy(odIP, nl.Rdaddr[:net.IPv4len])
 | 
						|
	case syscall.AF_INET6:
 | 
						|
		odIP = make(net.IP, net.IPv6len)
 | 
						|
		copy(odIP, nl.Rdaddr[:])
 | 
						|
	}
 | 
						|
	return odIP, odPort, nil
 | 
						|
}
 | 
						|
 | 
						|
func applyOutboundSocketOptions(network string, address string, fd uintptr, config *SocketConfig) error {
 | 
						|
	if config.Mark != 0 {
 | 
						|
		if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_USER_COOKIE, int(config.Mark)); err != nil {
 | 
						|
			return errors.New("failed to set SO_USER_COOKIE").Base(err)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if isTCPSocket(network) {
 | 
						|
		tfo := config.ParseTFOValue()
 | 
						|
		if tfo > 0 {
 | 
						|
			tfo = 1
 | 
						|
		}
 | 
						|
		if tfo >= 0 {
 | 
						|
			if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, unix.TCP_FASTOPEN, tfo); err != nil {
 | 
						|
				return errors.New("failed to set TCP_FASTOPEN_CONNECT=", tfo).Base(err)
 | 
						|
			}
 | 
						|
		}
 | 
						|
		if config.TcpKeepAliveIdle > 0 || config.TcpKeepAliveInterval > 0 {
 | 
						|
			if config.TcpKeepAliveIdle > 0 {
 | 
						|
				if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, syscall.TCP_KEEPIDLE, int(config.TcpKeepAliveIdle)); err != nil {
 | 
						|
					return errors.New("failed to set TCP_KEEPIDLE", err)
 | 
						|
				}
 | 
						|
			}
 | 
						|
			if config.TcpKeepAliveInterval > 0 {
 | 
						|
				if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, syscall.TCP_KEEPINTVL, int(config.TcpKeepAliveInterval)); err != nil {
 | 
						|
					return errors.New("failed to set TCP_KEEPINTVL", err)
 | 
						|
				}
 | 
						|
			}
 | 
						|
			if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, 1); err != nil {
 | 
						|
				return errors.New("failed to set SO_KEEPALIVE", err)
 | 
						|
			}
 | 
						|
		} else if config.TcpKeepAliveInterval < 0 || config.TcpKeepAliveIdle < 0 {
 | 
						|
			if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, 0); err != nil {
 | 
						|
				return errors.New("failed to unset SO_KEEPALIVE", err)
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if config.Tproxy.IsEnabled() {
 | 
						|
		ip, _, _ := net.SplitHostPort(address)
 | 
						|
		if net.ParseIP(ip).To4() != nil {
 | 
						|
			if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IP, syscall.IP_BINDANY, 1); err != nil {
 | 
						|
				return errors.New("failed to set outbound IP_BINDANY").Base(err)
 | 
						|
			}
 | 
						|
		} else {
 | 
						|
			if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IPV6, syscall.IPV6_BINDANY, 1); err != nil {
 | 
						|
				return errors.New("failed to set outbound IPV6_BINDANY").Base(err)
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func applyInboundSocketOptions(network string, fd uintptr, config *SocketConfig) error {
 | 
						|
	if config.Mark != 0 {
 | 
						|
		if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_USER_COOKIE, int(config.Mark)); err != nil {
 | 
						|
			return errors.New("failed to set SO_USER_COOKIE").Base(err)
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if isTCPSocket(network) {
 | 
						|
		tfo := config.ParseTFOValue()
 | 
						|
		if tfo >= 0 {
 | 
						|
			if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, unix.TCP_FASTOPEN, tfo); err != nil {
 | 
						|
				return errors.New("failed to set TCP_FASTOPEN=", tfo).Base(err)
 | 
						|
			}
 | 
						|
		}
 | 
						|
		if config.TcpKeepAliveIdle > 0 || config.TcpKeepAliveInterval > 0 {
 | 
						|
			if config.TcpKeepAliveIdle > 0 {
 | 
						|
				if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, syscall.TCP_KEEPIDLE, int(config.TcpKeepAliveIdle)); err != nil {
 | 
						|
					return errors.New("failed to set TCP_KEEPIDLE", err)
 | 
						|
				}
 | 
						|
			}
 | 
						|
			if config.TcpKeepAliveInterval > 0 {
 | 
						|
				if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, syscall.TCP_KEEPINTVL, int(config.TcpKeepAliveInterval)); err != nil {
 | 
						|
					return errors.New("failed to set TCP_KEEPINTVL", err)
 | 
						|
				}
 | 
						|
			}
 | 
						|
			if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, 1); err != nil {
 | 
						|
				return errors.New("failed to set SO_KEEPALIVE", err)
 | 
						|
			}
 | 
						|
		} else if config.TcpKeepAliveInterval < 0 || config.TcpKeepAliveIdle < 0 {
 | 
						|
			if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, 0); err != nil {
 | 
						|
				return errors.New("failed to unset SO_KEEPALIVE", err)
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if config.Tproxy.IsEnabled() {
 | 
						|
		if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IPV6, syscall.IPV6_BINDANY, 1); err != nil {
 | 
						|
			if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IP, syscall.IP_BINDANY, 1); err != nil {
 | 
						|
				return errors.New("failed to set inbound IP_BINDANY").Base(err)
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func bindAddr(fd uintptr, ip []byte, port uint32) error {
 | 
						|
	setReuseAddr(fd)
 | 
						|
	setReusePort(fd)
 | 
						|
 | 
						|
	var sockaddr syscall.Sockaddr
 | 
						|
 | 
						|
	switch len(ip) {
 | 
						|
	case net.IPv4len:
 | 
						|
		a4 := &syscall.SockaddrInet4{
 | 
						|
			Port: int(port),
 | 
						|
		}
 | 
						|
		copy(a4.Addr[:], ip)
 | 
						|
		sockaddr = a4
 | 
						|
	case net.IPv6len:
 | 
						|
		a6 := &syscall.SockaddrInet6{
 | 
						|
			Port: int(port),
 | 
						|
		}
 | 
						|
		copy(a6.Addr[:], ip)
 | 
						|
		sockaddr = a6
 | 
						|
	default:
 | 
						|
		return errors.New("unexpected length of ip")
 | 
						|
	}
 | 
						|
 | 
						|
	return syscall.Bind(int(fd), sockaddr)
 | 
						|
}
 | 
						|
 | 
						|
func setReuseAddr(fd uintptr) error {
 | 
						|
	if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1); err != nil {
 | 
						|
		return errors.New("failed to set SO_REUSEADDR").Base(err).AtWarning()
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func setReusePort(fd uintptr) error {
 | 
						|
	if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, soReUsePortLB, 1); err != nil {
 | 
						|
		if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, soReUsePort, 1); err != nil {
 | 
						|
			return errors.New("failed to set SO_REUSEPORT").Base(err).AtWarning()
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 |