github.com/sagernet/tfo-go@v0.0.0-20231209031829-7b5343ac1dc6/syscall_darwin.go (about)

     1  package tfo
     2  
     3  import (
     4  	"syscall"
     5  	"unsafe"
     6  
     7  	"golang.org/x/sys/unix"
     8  )
     9  
    10  // Do the interface allocations only once for common
    11  // Errno values.
    12  var (
    13  	errEAGAIN error = syscall.EAGAIN
    14  	errEINVAL error = syscall.EINVAL
    15  	errENOENT error = syscall.ENOENT
    16  )
    17  
    18  // errnoErr returns common boxed Errno values, to prevent
    19  // allocations at runtime.
    20  func errnoErr(e syscall.Errno) error {
    21  	switch e {
    22  	case 0:
    23  		return nil
    24  	case unix.EAGAIN:
    25  		return errEAGAIN
    26  	case unix.EINVAL:
    27  		return errEINVAL
    28  	case unix.ENOENT:
    29  		return errENOENT
    30  	}
    31  	return e
    32  }
    33  
    34  func sockaddrp(sa syscall.Sockaddr) (unsafe.Pointer, uint32, error) {
    35  	switch sa := sa.(type) {
    36  	case nil:
    37  		return nil, 0, nil
    38  	case *syscall.SockaddrInet4:
    39  		return (*sockaddrInet4)(unsafe.Pointer(sa)).sockaddr()
    40  	case *syscall.SockaddrInet6:
    41  		return (*sockaddrInet6)(unsafe.Pointer(sa)).sockaddr()
    42  	default:
    43  		return nil, 0, syscall.EAFNOSUPPORT
    44  	}
    45  }
    46  
    47  // Copied from src/syscall/syscall_unix.go
    48  type sockaddrInet4 struct {
    49  	Port int
    50  	Addr [4]byte
    51  	raw  syscall.RawSockaddrInet4
    52  }
    53  
    54  // Copied from src/syscall/syscall_unix.go
    55  type sockaddrInet6 struct {
    56  	Port   int
    57  	ZoneId uint32
    58  	Addr   [16]byte
    59  	raw    syscall.RawSockaddrInet6
    60  }
    61  
    62  func (sa *sockaddrInet4) sockaddr() (unsafe.Pointer, uint32, error) {
    63  	if sa.Port < 0 || sa.Port > 0xFFFF {
    64  		return nil, 0, syscall.EINVAL
    65  	}
    66  	sa.raw.Len = syscall.SizeofSockaddrInet4
    67  	sa.raw.Family = syscall.AF_INET
    68  	p := (*[2]byte)(unsafe.Pointer(&sa.raw.Port))
    69  	p[0] = byte(sa.Port >> 8)
    70  	p[1] = byte(sa.Port)
    71  	sa.raw.Addr = sa.Addr
    72  	return unsafe.Pointer(&sa.raw), uint32(sa.raw.Len), nil
    73  }
    74  
    75  func (sa *sockaddrInet6) sockaddr() (unsafe.Pointer, uint32, error) {
    76  	if sa.Port < 0 || sa.Port > 0xFFFF {
    77  		return nil, 0, syscall.EINVAL
    78  	}
    79  	sa.raw.Len = syscall.SizeofSockaddrInet6
    80  	sa.raw.Family = syscall.AF_INET6
    81  	p := (*[2]byte)(unsafe.Pointer(&sa.raw.Port))
    82  	p[0] = byte(sa.Port >> 8)
    83  	p[1] = byte(sa.Port)
    84  	sa.raw.Scope_id = sa.ZoneId
    85  	sa.raw.Addr = sa.Addr
    86  	return unsafe.Pointer(&sa.raw), uint32(sa.raw.Len), nil
    87  }
    88  
    89  type sa_endpoints_t struct {
    90  	sae_srcif      uint
    91  	sae_srcaddr    unsafe.Pointer
    92  	sae_srcaddrlen uint32
    93  	sae_dstaddr    unsafe.Pointer
    94  	sae_dstaddrlen uint32
    95  }
    96  
    97  const (
    98  	SAE_ASSOCID_ANY              = 0
    99  	CONNECT_RESUME_ON_READ_WRITE = 0x1
   100  	CONNECT_DATA_IDEMPOTENT      = 0x2
   101  	CONNECT_DATA_AUTHENTICATED   = 0x4
   102  )
   103  
   104  // Connectx enables TFO if a non-empty buf is passed.
   105  // If an empty buf is passed, TFO is not enabled.
   106  func Connectx(s int, srcif uint, from syscall.Sockaddr, to syscall.Sockaddr, buf []byte) (uint, error) {
   107  	from_ptr, from_n, err := sockaddrp(from)
   108  	if err != nil {
   109  		return 0, err
   110  	}
   111  
   112  	to_ptr, to_n, err := sockaddrp(to)
   113  	if err != nil {
   114  		return 0, err
   115  	}
   116  
   117  	sae := sa_endpoints_t{
   118  		sae_srcif:      srcif,
   119  		sae_srcaddr:    from_ptr,
   120  		sae_srcaddrlen: from_n,
   121  		sae_dstaddr:    to_ptr,
   122  		sae_dstaddrlen: to_n,
   123  	}
   124  
   125  	var (
   126  		flags  uint
   127  		iov    *unix.Iovec
   128  		iovcnt uint
   129  	)
   130  
   131  	if len(buf) > 0 {
   132  		flags = CONNECT_DATA_IDEMPOTENT
   133  		iov = &unix.Iovec{
   134  			Base: &buf[0],
   135  			Len:  uint64(len(buf)),
   136  		}
   137  		iovcnt = 1
   138  	}
   139  
   140  	var bytesSent uint
   141  
   142  	r1, _, e1 := unix.Syscall9(unix.SYS_CONNECTX,
   143  		uintptr(s),
   144  		uintptr(unsafe.Pointer(&sae)),
   145  		SAE_ASSOCID_ANY,
   146  		uintptr(flags),
   147  		uintptr(unsafe.Pointer(iov)),
   148  		uintptr(iovcnt),
   149  		uintptr(unsafe.Pointer(&bytesSent)),
   150  		0,
   151  		0)
   152  	ret := int(r1)
   153  	if ret == -1 {
   154  		err = errnoErr(e1)
   155  	}
   156  	return bytesSent, err
   157  }