apple_client_platform.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517
  1. //go:build darwin && cgo
  2. package tls
  3. /*
  4. #cgo CFLAGS: -x objective-c -fobjc-arc
  5. #cgo LDFLAGS: -framework Foundation -framework Network -framework Security
  6. #include <stdlib.h>
  7. #include "apple_client_platform_darwin.h"
  8. */
  9. import "C"
  10. import (
  11. "context"
  12. "crypto/tls"
  13. "crypto/x509"
  14. "encoding/binary"
  15. "io"
  16. "math"
  17. "net"
  18. "os"
  19. "strings"
  20. "sync"
  21. "syscall"
  22. "time"
  23. "unsafe"
  24. "github.com/sagernet/sing/common"
  25. E "github.com/sagernet/sing/common/exceptions"
  26. "golang.org/x/sys/unix"
  27. )
  28. func (c *appleClientConfig) ClientHandshake(ctx context.Context, conn net.Conn) (Conn, error) {
  29. rawSyscallConn, ok := common.Cast[syscall.Conn](conn)
  30. if !ok {
  31. return nil, E.New("apple TLS: requires fd-backed TCP connection")
  32. }
  33. syscallConn, err := rawSyscallConn.SyscallConn()
  34. if err != nil {
  35. return nil, E.Cause(err, "access raw connection")
  36. }
  37. var dupFD int
  38. controlErr := syscallConn.Control(func(fd uintptr) {
  39. dupFD, err = unix.Dup(int(fd))
  40. })
  41. if controlErr != nil {
  42. return nil, E.Cause(controlErr, "access raw connection")
  43. }
  44. if err != nil {
  45. return nil, E.Cause(err, "duplicate raw connection")
  46. }
  47. serverName := c.serverName
  48. serverNamePtr := cStringOrNil(serverName)
  49. defer cFree(serverNamePtr)
  50. alpn := strings.Join(c.nextProtos, "\n")
  51. alpnPtr := cStringOrNil(alpn)
  52. defer cFree(alpnPtr)
  53. anchorPEMPtr := cStringOrNil(c.anchorPEM)
  54. defer cFree(anchorPEMPtr)
  55. var (
  56. hasVerifyTime bool
  57. verifyTimeUnixMilli int64
  58. )
  59. if c.timeFunc != nil {
  60. hasVerifyTime = true
  61. verifyTimeUnixMilli = c.timeFunc().UnixMilli()
  62. }
  63. var errorPtr *C.char
  64. client := C.box_apple_tls_client_create(
  65. C.int(dupFD),
  66. serverNamePtr,
  67. alpnPtr,
  68. C.size_t(len(alpn)),
  69. C.uint16_t(c.minVersion),
  70. C.uint16_t(c.maxVersion),
  71. C.bool(c.insecure),
  72. anchorPEMPtr,
  73. C.size_t(len(c.anchorPEM)),
  74. C.bool(c.anchorOnly),
  75. C.bool(hasVerifyTime),
  76. C.int64_t(verifyTimeUnixMilli),
  77. &errorPtr,
  78. )
  79. if client == nil {
  80. if errorPtr != nil {
  81. defer C.free(unsafe.Pointer(errorPtr))
  82. return nil, E.New(C.GoString(errorPtr))
  83. }
  84. return nil, E.New("apple TLS: create connection")
  85. }
  86. if err = waitAppleTLSClientReady(ctx, client); err != nil {
  87. C.box_apple_tls_client_cancel(client)
  88. C.box_apple_tls_client_free(client)
  89. return nil, err
  90. }
  91. var state C.box_apple_tls_state_t
  92. stateOK := C.box_apple_tls_client_copy_state(client, &state, &errorPtr)
  93. if !bool(stateOK) {
  94. C.box_apple_tls_client_cancel(client)
  95. C.box_apple_tls_client_free(client)
  96. if errorPtr != nil {
  97. defer C.free(unsafe.Pointer(errorPtr))
  98. return nil, E.New(C.GoString(errorPtr))
  99. }
  100. return nil, E.New("apple TLS: read metadata")
  101. }
  102. defer C.box_apple_tls_state_free(&state)
  103. connectionState, rawCerts, err := parseAppleTLSState(&state)
  104. if err != nil {
  105. C.box_apple_tls_client_cancel(client)
  106. C.box_apple_tls_client_free(client)
  107. return nil, err
  108. }
  109. if len(c.certificatePublicKeySHA256) > 0 {
  110. err = VerifyPublicKeySHA256(c.certificatePublicKeySHA256, rawCerts)
  111. if err != nil {
  112. C.box_apple_tls_client_cancel(client)
  113. C.box_apple_tls_client_free(client)
  114. return nil, err
  115. }
  116. }
  117. return &appleTLSConn{
  118. rawConn: conn,
  119. client: client,
  120. state: connectionState,
  121. closed: make(chan struct{}),
  122. }, nil
  123. }
  124. const appleTLSHandshakePollInterval = 100 * time.Millisecond
  125. func waitAppleTLSClientReady(ctx context.Context, client *C.box_apple_tls_client_t) error {
  126. for {
  127. if err := ctx.Err(); err != nil {
  128. C.box_apple_tls_client_cancel(client)
  129. return err
  130. }
  131. waitTimeout := appleTLSHandshakePollInterval
  132. if deadline, loaded := ctx.Deadline(); loaded {
  133. remaining := time.Until(deadline)
  134. if remaining <= 0 {
  135. C.box_apple_tls_client_cancel(client)
  136. if err := ctx.Err(); err != nil {
  137. return err
  138. }
  139. return context.DeadlineExceeded
  140. }
  141. if remaining < waitTimeout {
  142. waitTimeout = remaining
  143. }
  144. }
  145. var errorPtr *C.char
  146. waitResult := C.box_apple_tls_client_wait_ready(client, C.int(timeoutFromDuration(waitTimeout)), &errorPtr)
  147. switch waitResult {
  148. case 1:
  149. return nil
  150. case -2:
  151. continue
  152. case 0:
  153. if errorPtr != nil {
  154. defer C.free(unsafe.Pointer(errorPtr))
  155. return E.New(C.GoString(errorPtr))
  156. }
  157. return E.New("apple TLS: handshake failed")
  158. default:
  159. return E.New("apple TLS: invalid handshake state")
  160. }
  161. }
  162. }
  163. type appleTLSConn struct {
  164. rawConn net.Conn
  165. client *C.box_apple_tls_client_t
  166. state tls.ConnectionState
  167. readAccess sync.Mutex
  168. writeAccess sync.Mutex
  169. stateAccess sync.RWMutex
  170. closeOnce sync.Once
  171. ioAccess sync.Mutex
  172. ioGroup sync.WaitGroup
  173. closed chan struct{}
  174. readEOF bool
  175. deadlineAccess sync.Mutex
  176. readDeadline time.Time
  177. writeDeadline time.Time
  178. readTimedOut bool
  179. writeTimedOut bool
  180. }
  181. func (c *appleTLSConn) Read(p []byte) (int, error) {
  182. c.readAccess.Lock()
  183. defer c.readAccess.Unlock()
  184. if c.readEOF {
  185. return 0, io.EOF
  186. }
  187. if len(p) == 0 {
  188. return 0, nil
  189. }
  190. timeoutMs, err := c.prepareReadTimeout()
  191. if err != nil {
  192. return 0, err
  193. }
  194. client, err := c.acquireClient()
  195. if err != nil {
  196. return 0, err
  197. }
  198. defer c.releaseClient()
  199. var eof C.bool
  200. var errorPtr *C.char
  201. n := C.box_apple_tls_client_read(client, unsafe.Pointer(&p[0]), C.size_t(len(p)), C.int(timeoutMs), &eof, &errorPtr)
  202. switch {
  203. case n == -2:
  204. c.markReadTimedOut()
  205. return 0, os.ErrDeadlineExceeded
  206. case n >= 0:
  207. if bool(eof) {
  208. c.readEOF = true
  209. if n == 0 {
  210. return 0, io.EOF
  211. }
  212. }
  213. return int(n), nil
  214. default:
  215. if errorPtr != nil {
  216. defer C.free(unsafe.Pointer(errorPtr))
  217. if c.isClosed() {
  218. return 0, net.ErrClosed
  219. }
  220. return 0, E.New(C.GoString(errorPtr))
  221. }
  222. return 0, net.ErrClosed
  223. }
  224. }
  225. func (c *appleTLSConn) Write(p []byte) (int, error) {
  226. c.writeAccess.Lock()
  227. defer c.writeAccess.Unlock()
  228. if len(p) == 0 {
  229. return 0, nil
  230. }
  231. timeoutMs, err := c.prepareWriteTimeout()
  232. if err != nil {
  233. return 0, err
  234. }
  235. client, err := c.acquireClient()
  236. if err != nil {
  237. return 0, err
  238. }
  239. defer c.releaseClient()
  240. var errorPtr *C.char
  241. n := C.box_apple_tls_client_write(client, unsafe.Pointer(&p[0]), C.size_t(len(p)), C.int(timeoutMs), &errorPtr)
  242. switch {
  243. case n == -2:
  244. c.markWriteTimedOut()
  245. return 0, os.ErrDeadlineExceeded
  246. case n >= 0:
  247. return int(n), nil
  248. }
  249. if errorPtr != nil {
  250. defer C.free(unsafe.Pointer(errorPtr))
  251. if c.isClosed() {
  252. return 0, net.ErrClosed
  253. }
  254. return 0, E.New(C.GoString(errorPtr))
  255. }
  256. return 0, net.ErrClosed
  257. }
  258. func (c *appleTLSConn) Close() error {
  259. var closeErr error
  260. c.closeOnce.Do(func() {
  261. close(c.closed)
  262. C.box_apple_tls_client_cancel(c.client)
  263. closeErr = c.rawConn.Close()
  264. c.ioAccess.Lock()
  265. c.ioGroup.Wait()
  266. C.box_apple_tls_client_free(c.client)
  267. c.client = nil
  268. c.ioAccess.Unlock()
  269. })
  270. return closeErr
  271. }
  272. func (c *appleTLSConn) LocalAddr() net.Addr {
  273. return c.rawConn.LocalAddr()
  274. }
  275. func (c *appleTLSConn) RemoteAddr() net.Addr {
  276. return c.rawConn.RemoteAddr()
  277. }
  278. // SetDeadline installs deadlines for subsequent Read and Write calls.
  279. //
  280. // Deadlines only apply to subsequent Read or Write calls; an in-flight call
  281. // does not observe later updates to its deadline. Callers that need to cancel
  282. // an in-flight I/O must Close the connection instead.
  283. //
  284. // Once an active Read or Write trips its deadline, the underlying
  285. // nw_connection is cancelled and the conn is no longer usable — callers must
  286. // Close after a deadline error.
  287. func (c *appleTLSConn) SetDeadline(t time.Time) error {
  288. c.deadlineAccess.Lock()
  289. c.readDeadline = t
  290. c.writeDeadline = t
  291. c.readTimedOut = false
  292. c.writeTimedOut = false
  293. c.deadlineAccess.Unlock()
  294. return nil
  295. }
  296. func (c *appleTLSConn) SetReadDeadline(t time.Time) error {
  297. c.deadlineAccess.Lock()
  298. c.readDeadline = t
  299. c.readTimedOut = false
  300. c.deadlineAccess.Unlock()
  301. return nil
  302. }
  303. func (c *appleTLSConn) SetWriteDeadline(t time.Time) error {
  304. c.deadlineAccess.Lock()
  305. c.writeDeadline = t
  306. c.writeTimedOut = false
  307. c.deadlineAccess.Unlock()
  308. return nil
  309. }
  310. func (c *appleTLSConn) prepareReadTimeout() (int, error) {
  311. c.deadlineAccess.Lock()
  312. defer c.deadlineAccess.Unlock()
  313. if c.readTimedOut {
  314. return 0, os.ErrDeadlineExceeded
  315. }
  316. timeoutMs, expired := deadlineTimeoutMs(c.readDeadline)
  317. if expired {
  318. c.readTimedOut = true
  319. return 0, os.ErrDeadlineExceeded
  320. }
  321. return timeoutMs, nil
  322. }
  323. func (c *appleTLSConn) prepareWriteTimeout() (int, error) {
  324. c.deadlineAccess.Lock()
  325. defer c.deadlineAccess.Unlock()
  326. if c.writeTimedOut {
  327. return 0, os.ErrDeadlineExceeded
  328. }
  329. timeoutMs, expired := deadlineTimeoutMs(c.writeDeadline)
  330. if expired {
  331. c.writeTimedOut = true
  332. return 0, os.ErrDeadlineExceeded
  333. }
  334. return timeoutMs, nil
  335. }
  336. func (c *appleTLSConn) markReadTimedOut() {
  337. c.deadlineAccess.Lock()
  338. c.readTimedOut = true
  339. c.deadlineAccess.Unlock()
  340. }
  341. func (c *appleTLSConn) markWriteTimedOut() {
  342. c.deadlineAccess.Lock()
  343. c.writeTimedOut = true
  344. c.deadlineAccess.Unlock()
  345. }
  346. func deadlineTimeoutMs(deadline time.Time) (int, bool) {
  347. if deadline.IsZero() {
  348. return -1, false
  349. }
  350. remaining := time.Until(deadline)
  351. if remaining <= 0 {
  352. return 0, true
  353. }
  354. return timeoutFromDuration(remaining), false
  355. }
  356. func (c *appleTLSConn) isClosed() bool {
  357. select {
  358. case <-c.closed:
  359. return true
  360. default:
  361. return false
  362. }
  363. }
  364. func (c *appleTLSConn) acquireClient() (*C.box_apple_tls_client_t, error) {
  365. c.ioAccess.Lock()
  366. defer c.ioAccess.Unlock()
  367. if c.isClosed() {
  368. return nil, net.ErrClosed
  369. }
  370. client := c.client
  371. if client == nil {
  372. return nil, net.ErrClosed
  373. }
  374. c.ioGroup.Add(1)
  375. return client, nil
  376. }
  377. func (c *appleTLSConn) releaseClient() {
  378. c.ioGroup.Done()
  379. }
  380. func (c *appleTLSConn) NetConn() net.Conn {
  381. return c.rawConn
  382. }
  383. func (c *appleTLSConn) HandshakeContext(ctx context.Context) error {
  384. return nil
  385. }
  386. func (c *appleTLSConn) ConnectionState() ConnectionState {
  387. c.stateAccess.RLock()
  388. defer c.stateAccess.RUnlock()
  389. return c.state
  390. }
  391. func parseAppleTLSState(state *C.box_apple_tls_state_t) (tls.ConnectionState, [][]byte, error) {
  392. rawCerts, peerCertificates, err := parseAppleCertChain(state.peer_cert_chain, state.peer_cert_chain_len)
  393. if err != nil {
  394. return tls.ConnectionState{}, nil, err
  395. }
  396. var negotiatedProtocol string
  397. if state.alpn != nil {
  398. negotiatedProtocol = C.GoString(state.alpn)
  399. }
  400. var serverName string
  401. if state.server_name != nil {
  402. serverName = C.GoString(state.server_name)
  403. }
  404. return tls.ConnectionState{
  405. Version: uint16(state.version),
  406. HandshakeComplete: true,
  407. CipherSuite: uint16(state.cipher_suite),
  408. NegotiatedProtocol: negotiatedProtocol,
  409. ServerName: serverName,
  410. PeerCertificates: peerCertificates,
  411. }, rawCerts, nil
  412. }
  413. func parseAppleCertChain(chain *C.uint8_t, chainLen C.size_t) ([][]byte, []*x509.Certificate, error) {
  414. if chain == nil || chainLen == 0 {
  415. return nil, nil, nil
  416. }
  417. chainBytes := C.GoBytes(unsafe.Pointer(chain), C.int(chainLen))
  418. var (
  419. rawCerts [][]byte
  420. peerCertificates []*x509.Certificate
  421. )
  422. for len(chainBytes) >= 4 {
  423. certificateLen := binary.BigEndian.Uint32(chainBytes[:4])
  424. chainBytes = chainBytes[4:]
  425. if len(chainBytes) < int(certificateLen) {
  426. return nil, nil, E.New("apple TLS: invalid certificate chain")
  427. }
  428. certificateData := append([]byte(nil), chainBytes[:certificateLen]...)
  429. certificate, err := x509.ParseCertificate(certificateData)
  430. if err != nil {
  431. return nil, nil, E.Cause(err, "parse peer certificate")
  432. }
  433. rawCerts = append(rawCerts, certificateData)
  434. peerCertificates = append(peerCertificates, certificate)
  435. chainBytes = chainBytes[certificateLen:]
  436. }
  437. if len(chainBytes) != 0 {
  438. return nil, nil, E.New("apple TLS: invalid certificate chain")
  439. }
  440. return rawCerts, peerCertificates, nil
  441. }
  442. func timeoutFromDuration(timeout time.Duration) int {
  443. if timeout <= 0 {
  444. return 0
  445. }
  446. timeoutMilliseconds := int64(timeout / time.Millisecond)
  447. if timeout%time.Millisecond != 0 {
  448. timeoutMilliseconds++
  449. }
  450. if timeoutMilliseconds > math.MaxInt32 {
  451. return math.MaxInt32
  452. }
  453. return int(timeoutMilliseconds)
  454. }
  455. func cStringOrNil(value string) *C.char {
  456. if value == "" {
  457. return nil
  458. }
  459. return C.CString(value)
  460. }
  461. func cFree(pointer *C.char) {
  462. if pointer != nil {
  463. C.free(unsafe.Pointer(pointer))
  464. }
  465. }