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 }