Coverage Report

Created: 2026-03-12 06:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/swift-nio/Sources/NIOPosix/System.swift
Line
Count
Source
1
//===----------------------------------------------------------------------===//
2
//
3
// This source file is part of the SwiftNIO open source project
4
//
5
// Copyright (c) 2017-2024 Apple Inc. and the SwiftNIO project authors
6
// Licensed under Apache License v2.0
7
//
8
// See LICENSE.txt for license information
9
// See CONTRIBUTORS.txt for the list of SwiftNIO project authors
10
//
11
// SPDX-License-Identifier: Apache-2.0
12
//
13
//===----------------------------------------------------------------------===//
14
//  This file contains code that ensures errno is captured correctly when doing syscalls and no ARC traffic can happen inbetween that *could* change the errno
15
//  value before we were able to read it.
16
//  It's important that all static methods are declared with `@inline(never)` so it's not possible any ARC traffic happens while we need to read errno.
17
18
#if !os(WASI)
19
20
import NIOCore
21
22
#if canImport(Darwin)
23
@_exported import Darwin.C
24
import CNIODarwin
25
internal typealias MMsgHdr = CNIODarwin_mmsghdr
26
#elseif os(Linux) || os(FreeBSD) || os(Android)
27
#if canImport(Glibc)
28
@_exported @preconcurrency import Glibc
29
#elseif canImport(Musl)
30
@_exported @preconcurrency import Musl
31
#elseif canImport(Android)
32
@_exported @preconcurrency import Android
33
#endif
34
import CNIOLinux
35
internal typealias MMsgHdr = CNIOLinux_mmsghdr
36
internal typealias in6_pktinfo = CNIOLinux_in6_pktinfo
37
#elseif os(OpenBSD)
38
@_exported @preconcurrency import Glibc
39
import CNIOOpenBSD
40
internal typealias MMsgHdr = CNIOOpenBSD_mmsghdr
41
let INADDR_ANY = UInt32(0)
42
#elseif os(Windows)
43
@_exported import ucrt
44
45
import CNIOWindows
46
47
internal typealias MMsgHdr = CNIOWindows_mmsghdr
48
#else
49
#error("The POSIX system module was unable to identify your C library.")
50
#endif
51
52
#if os(Android)
53
let INADDR_ANY = UInt32(0)  // #define INADDR_ANY ((unsigned long int) 0x00000000)
54
let IFF_BROADCAST: CUnsignedInt = numericCast(Android.IFF_BROADCAST.rawValue)
55
let IFF_POINTOPOINT: CUnsignedInt = numericCast(Android.IFF_POINTOPOINT.rawValue)
56
let IFF_MULTICAST: CUnsignedInt = numericCast(Android.IFF_MULTICAST.rawValue)
57
internal typealias in_port_t = UInt16
58
extension ipv6_mreq {  // http://lkml.iu.edu/hypermail/linux/kernel/0106.1/0080.html
59
    init(ipv6mr_multiaddr: in6_addr, ipv6mr_interface: UInt32) {
60
        self.init(
61
            ipv6mr_multiaddr: ipv6mr_multiaddr,
62
            ipv6mr_ifindex: Int32(bitPattern: ipv6mr_interface)
63
        )
64
    }
65
}
66
#if arch(arm)
67
let S_IFSOCK = UInt32(Android.S_IFSOCK)
68
let S_IFMT = UInt32(Android.S_IFMT)
69
let S_IFREG = UInt32(Android.S_IFREG)
70
let S_IFDIR = UInt32(Android.S_IFDIR)
71
let S_IFLNK = UInt32(Android.S_IFLNK)
72
let S_IFBLK = UInt32(Android.S_IFBLK)
73
#endif
74
#endif
75
76
// Declare aliases to share more code and not need to repeat #if #else blocks
77
#if !os(Windows)
78
private let sysClose = close
79
private let sysShutdown = shutdown
80
private let sysBind = bind
81
0
private let sysFcntl: @Sendable @convention(c) (CInt, CInt, CInt) -> CInt = { fcntl($0, $1, $2) }
82
private let sysSocket = socket
83
private let sysSetsockopt = setsockopt
84
private let sysGetsockopt = getsockopt
85
private let sysListen = listen
86
private let sysAccept = accept
87
private let sysConnect = connect
88
0
private let sysOpen: @Sendable @convention(c) (UnsafePointer<CChar>, CInt) -> CInt = { open($0, $1) }
89
0
private let sysOpenWithMode: @Sendable @convention(c) (UnsafePointer<CChar>, CInt, mode_t) -> CInt = {
90
0
    open($0, $1, $2)
91
0
}
92
private let sysFtruncate = ftruncate
93
private let sysWrite = write
94
private let sysPwrite = pwrite
95
private let sysRead = read
96
private let sysPread = pread
97
private let sysLseek = lseek
98
private let sysPoll = poll
99
#else
100
private let sysWrite = _write
101
private let sysRead = _read
102
private let sysLseek = _lseek
103
private let sysFtruncate = _chsize_s
104
#endif
105
106
#if os(Android)
107
func sysRecvFrom_wrapper(
108
    sockfd: CInt,
109
    buf: UnsafeMutableRawPointer,
110
    len: CLong,
111
    flags: CInt,
112
    src_addr: UnsafeMutablePointer<sockaddr>,
113
    addrlen: UnsafeMutablePointer<socklen_t>
114
) -> CLong {
115
    // src_addr is 'UnsafeMutablePointer', but it need to be 'UnsafePointer'
116
    recvfrom(sockfd, buf, len, flags, src_addr, addrlen)
117
    // src_addr is 'UnsafeMutablePointer', but it need to be 'UnsafePointer'
118
}
119
func sysWritev_wrapper(fd: CInt, iov: UnsafePointer<iovec>?, iovcnt: CInt) -> CLong {
120
    CLong(writev(fd, iov!, iovcnt))  // cast 'Int32' to 'CLong'// cast 'Int32' to 'CLong'
121
}
122
private let sysWritev = sysWritev_wrapper
123
#elseif !os(Windows)
124
private let sysWritev: @convention(c) (Int32, UnsafePointer<iovec>?, CInt) -> CLong = writev
125
#endif
126
#if canImport(Android)
127
private let sysRecvMsg: @convention(c) (CInt, UnsafeMutablePointer<msghdr>, CInt) -> ssize_t = recvmsg
128
private let sysSendMsg: @convention(c) (CInt, UnsafePointer<msghdr>, CInt) -> ssize_t = sendmsg
129
#elseif !os(Windows)
130
private let sysRecvMsg: @convention(c) (CInt, UnsafeMutablePointer<msghdr>?, CInt) -> ssize_t = recvmsg
131
private let sysSendMsg: @convention(c) (CInt, UnsafePointer<msghdr>?, CInt) -> ssize_t = sendmsg
132
#endif
133
#if os(Windows)
134
private let sysDup: @convention(c) (CInt) -> CInt = _dup
135
#else
136
private let sysDup: @convention(c) (CInt) -> CInt = dup
137
#endif
138
#if canImport(Android)
139
private let sysGetpeername:
140
    @convention(c) (CInt, UnsafeMutablePointer<sockaddr>, UnsafeMutablePointer<socklen_t>) -> CInt = getpeername
141
private let sysGetsockname:
142
    @convention(c) (CInt, UnsafeMutablePointer<sockaddr>, UnsafeMutablePointer<socklen_t>) -> CInt = getsockname
143
#elseif !os(Windows)
144
private let sysGetpeername:
145
    @convention(c) (CInt, UnsafeMutablePointer<sockaddr>?, UnsafeMutablePointer<socklen_t>?) -> CInt = getpeername
146
private let sysGetsockname:
147
    @convention(c) (CInt, UnsafeMutablePointer<sockaddr>?, UnsafeMutablePointer<socklen_t>?) -> CInt = getsockname
148
#endif
149
150
#if os(Android)
151
private let sysIfNameToIndex: @convention(c) (UnsafePointer<CChar>) -> CUnsignedInt = if_nametoindex
152
#else
153
private let sysIfNameToIndex: @convention(c) (UnsafePointer<CChar>?) -> CUnsignedInt = if_nametoindex
154
#endif
155
#if canImport(Android)
156
private let sysSocketpair: @convention(c) (CInt, CInt, CInt, UnsafeMutablePointer<CInt>) -> CInt = socketpair
157
#elseif !os(Windows)
158
private let sysSocketpair: @convention(c) (CInt, CInt, CInt, UnsafeMutablePointer<CInt>?) -> CInt = socketpair
159
#endif
160
161
#if os(Linux) || os(Android) || canImport(Darwin) || os(OpenBSD)
162
private let sysFstat = fstat
163
private let sysStat = stat
164
private let sysLstat = lstat
165
private let sysSymlink = symlink
166
private let sysReadlink = readlink
167
private let sysUnlink = unlink
168
private let sysMkdir = mkdir
169
private let sysOpendir = opendir
170
private let sysReaddir = readdir
171
private let sysClosedir = closedir
172
private let sysRename = rename
173
private let sysRemove = remove
174
#endif
175
#if os(Linux) || os(Android)
176
private let sysSendMmsg = CNIOLinux_sendmmsg
177
private let sysRecvMmsg = CNIOLinux_recvmmsg
178
#elseif os(OpenBSD)
179
private let sysKevent = kevent
180
private let sysSendMmsg = CNIOOpenBSD_sendmmsg
181
private let sysRecvMmsg = CNIOOpenBSD_recvmmsg
182
#elseif canImport(Darwin)
183
private let sysKevent = kevent
184
private let sysMkpath = mkpath_np
185
private let sysSendMmsg = CNIODarwin_sendmmsg
186
private let sysRecvMmsg = CNIODarwin_recvmmsg
187
#endif
188
#if !os(Windows)
189
private let sysIoctl: @convention(c) (CInt, CUnsignedLong, UnsafeMutableRawPointer) -> CInt = ioctl
190
#endif  // !os(Windows)
191
192
@inlinable
193
0
func isUnacceptableErrno(_ code: CInt) -> Bool {
194
0
    // On iOS, EBADF is a possible result when a file descriptor has been reaped in the background.
195
0
    // In particular, it's possible to get EBADF from accept(), where the underlying accept() FD
196
0
    // is valid but the accepted one is not. The right solution here is to perform a check for
197
0
    // SO_ISDEFUNCT when we see this happen, but we haven't yet invested the time to do that.
198
0
    // In the meantime, we just tolerate EBADF on iOS.
199
    #if canImport(Darwin) && !os(macOS)
200
    switch code {
201
    case EFAULT:
202
        return true
203
    default:
204
        return false
205
    }
206
    #else
207
0
    switch code {
208
0
    case EFAULT, EBADF:
209
0
        return true
210
0
    default:
211
0
        return false
212
0
    }
213
    #endif
214
0
}
215
216
@inlinable
217
0
public func isUnacceptableErrnoOnClose(_ code: CInt) -> Bool {
218
0
    // We treat close() differently to all other FDs: we still want to catch EBADF here.
219
0
    switch code {
220
0
    case EFAULT, EBADF:
221
0
        return true
222
0
    default:
223
0
        return false
224
0
    }
225
0
}
226
227
@inlinable
228
0
internal func isUnacceptableErrnoForbiddingEINVAL(_ code: CInt) -> Bool {
229
0
    // We treat read() and pread() differently since we also want to catch EINVAL.
230
    #if canImport(Darwin) && !os(macOS)
231
    switch code {
232
    case EFAULT, EINVAL:
233
        return true
234
    default:
235
        return false
236
    }
237
    #else
238
0
    switch code {
239
0
    case EFAULT, EBADF, EINVAL:
240
0
        return true
241
0
    default:
242
0
        return false
243
0
    }
244
    #endif
245
0
}
246
247
#if os(Windows)
248
@inlinable
249
internal func strerror(_ errno: CInt) -> String {
250
    withUnsafeTemporaryAllocation(of: CChar.self, capacity: 95) {
251
        let result = strerror_s($0.baseAddress, $0.count, errno)
252
        guard result == 0 else { return "Unknown error: \(errno)" }
253
        return String(cString: $0.baseAddress!)
254
    }
255
}
256
#endif
257
258
@inlinable
259
0
internal func preconditionIsNotUnacceptableErrno(err: CInt, where function: String) {
260
0
    // strerror is documented to return "Unknown error: ..." for illegal value so it won't ever fail
261
    #if os(Windows)
262
    precondition(!isUnacceptableErrno(err), "unacceptable errno \(err) \(strerror(err)) in \(function))")
263
    #else
264
0
    precondition(
265
0
        !isUnacceptableErrno(err),
266
0
        "unacceptable errno \(err) \(String(cString: strerror(err)!)) in \(function))"
267
0
    )
268
    #endif
269
0
}
270
271
@inlinable
272
0
internal func preconditionIsNotUnacceptableErrnoOnClose(err: CInt, where function: String) {
273
0
    // strerror is documented to return "Unknown error: ..." for illegal value so it won't ever fail
274
    #if os(Windows)
275
    precondition(!isUnacceptableErrnoOnClose(err), "unacceptable errno \(err) \(strerror(err)) in \(function))")
276
    #else
277
0
    precondition(
278
0
        !isUnacceptableErrnoOnClose(err),
279
0
        "unacceptable errno \(err) \(String(cString: strerror(err)!)) in \(function))"
280
0
    )
281
    #endif
282
0
}
283
284
@inlinable
285
0
internal func preconditionIsNotUnacceptableErrnoForbiddingEINVAL(err: CInt, where function: String) {
286
0
    // strerror is documented to return "Unknown error: ..." for illegal value so it won't ever fail
287
    #if os(Windows)
288
    precondition(
289
        !isUnacceptableErrnoForbiddingEINVAL(err),
290
        "unacceptable errno \(err) \(strerror(err)) in \(function))"
291
    )
292
    #else
293
0
    precondition(
294
0
        !isUnacceptableErrnoForbiddingEINVAL(err),
295
0
        "unacceptable errno \(err) \(String(cString: strerror(err)!)) in \(function))"
296
0
    )
297
    #endif
298
0
}
299
300
// Sorry, we really try hard to not use underscored attributes. In this case
301
// however we seem to break the inlining threshold which makes a system call
302
// take twice the time, ie. we need this exception.
303
@inline(__always)
304
@discardableResult
305
@inlinable
306
internal func syscall<T: FixedWidthInteger>(
307
    blocking: Bool,
308
    where function: String = #function,
309
    _ body: () throws -> T
310
)
311
    throws -> IOResult<T>
312
0
{
313
0
    while true {
314
0
        let res = try body()
315
0
        if res == -1 {
316
            #if os(Windows)
317
            var err: CInt = 0
318
            _get_errno(&err)
319
            #else
320
0
            let err = errno
321
            #endif
322
0
            switch (err, blocking) {
323
0
            case (EINTR, _):
324
0
                continue
325
0
            case (EWOULDBLOCK, true):
326
0
                return .wouldBlock(0)
327
0
            default:
328
0
                preconditionIsNotUnacceptableErrno(err: err, where: function)
329
0
                throw IOError(errnoCode: err, reason: function)
330
0
            }
331
0
        }
332
0
        return .processed(res)
333
0
    }
334
0
}
335
336
#if canImport(Darwin)
337
@inline(__always)
338
@inlinable
339
@discardableResult
340
internal func syscall<T>(
341
    where function: String = #function,
342
    _ body: () throws -> UnsafeMutablePointer<T>?
343
)
344
    throws -> UnsafeMutablePointer<T>
345
{
346
    while true {
347
        if let res = try body() {
348
            return res
349
        } else {
350
            let err = errno
351
            switch err {
352
            case EINTR:
353
                continue
354
            default:
355
                preconditionIsNotUnacceptableErrno(err: err, where: function)
356
                throw IOError(errnoCode: err, reason: function)
357
            }
358
        }
359
    }
360
}
361
#elseif os(Linux) || os(Android) || os(OpenBSD)
362
@inline(__always)
363
@inlinable
364
@discardableResult
365
internal func syscall(
366
    where function: String = #function,
367
    _ body: () throws -> OpaquePointer?
368
)
369
    throws -> OpaquePointer
370
0
{
371
0
    while true {
372
0
        if let res = try body() {
373
0
            return res
374
0
        } else {
375
0
            let err = errno
376
0
            switch err {
377
0
            case EINTR:
378
0
                continue
379
0
            default:
380
0
                preconditionIsNotUnacceptableErrno(err: err, where: function)
381
0
                throw IOError(errnoCode: err, reason: function)
382
0
            }
383
0
        }
384
0
    }
385
0
}
386
#endif
387
388
#if !os(Windows)
389
@inline(__always)
390
@inlinable
391
@discardableResult
392
internal func syscallOptional<T>(
393
    where function: String = #function,
394
    _ body: () throws -> UnsafeMutablePointer<T>?
395
)
396
    throws -> UnsafeMutablePointer<T>?
397
0
{
398
0
    while true {
399
0
        errno = 0
400
0
        if let res = try body() {
401
0
            return res
402
0
        } else {
403
0
            let err = errno
404
0
            switch err {
405
0
            case 0:
406
0
                return nil
407
0
            case EINTR:
408
0
                continue
409
0
            default:
410
0
                preconditionIsNotUnacceptableErrno(err: err, where: function)
411
0
                throw IOError(errnoCode: err, reason: function)
412
0
            }
413
0
        }
414
0
    }
415
0
}
416
#endif
417
418
// Sorry, we really try hard to not use underscored attributes. In this case
419
// however we seem to break the inlining threshold which makes a system call
420
// take twice the time, ie. we need this exception.
421
@inline(__always)
422
@inlinable
423
@discardableResult
424
internal func syscallForbiddingEINVAL<T: FixedWidthInteger>(
425
    where function: String = #function,
426
    _ body: () throws -> T
427
)
428
    throws -> IOResult<T>
429
0
{
430
0
    while true {
431
0
        let res = try body()
432
0
        if res == -1 {
433
            #if os(Windows)
434
            var err: CInt = 0
435
            _get_errno(&err)
436
            #else
437
0
            let err = errno
438
            #endif
439
0
            switch err {
440
0
            case EINTR:
441
0
                continue
442
0
            case EWOULDBLOCK:
443
0
                return .wouldBlock(0)
444
0
            default:
445
0
                preconditionIsNotUnacceptableErrnoForbiddingEINVAL(err: err, where: function)
446
0
                throw IOError(errnoCode: err, reason: function)
447
0
            }
448
0
        }
449
0
        return .processed(res)
450
0
    }
451
0
}
452
453
@usableFromInline
454
internal enum Posix: Sendable {
455
    #if canImport(Darwin)
456
    @usableFromInline
457
    static let UIO_MAXIOV: Int = 1024
458
    @usableFromInline
459
    static let SHUT_RD: CInt = CInt(Darwin.SHUT_RD)
460
    @usableFromInline
461
    static let SHUT_WR: CInt = CInt(Darwin.SHUT_WR)
462
    @usableFromInline
463
    static let SHUT_RDWR: CInt = CInt(Darwin.SHUT_RDWR)
464
    #elseif os(Linux) || os(FreeBSD) || os(Android) || os(OpenBSD)
465
    #if canImport(Glibc)
466
    @usableFromInline
467
    static let UIO_MAXIOV: Int = Int(Glibc.UIO_MAXIOV)
468
    @usableFromInline
469
    static let SHUT_RD: CInt = CInt(Glibc.SHUT_RD)
470
    @usableFromInline
471
    static let SHUT_WR: CInt = CInt(Glibc.SHUT_WR)
472
    @usableFromInline
473
    static let SHUT_RDWR: CInt = CInt(Glibc.SHUT_RDWR)
474
    #elseif canImport(Musl)
475
    @usableFromInline
476
    static let UIO_MAXIOV: Int = Int(Musl.UIO_MAXIOV)
477
    @usableFromInline
478
    static let SHUT_RD: CInt = CInt(Musl.SHUT_RD)
479
    @usableFromInline
480
    static let SHUT_WR: CInt = CInt(Musl.SHUT_WR)
481
    @usableFromInline
482
    static let SHUT_RDWR: CInt = CInt(Musl.SHUT_RDWR)
483
    #elseif canImport(Android)
484
    @usableFromInline
485
    static let UIO_MAXIOV: Int = Int(Android.UIO_MAXIOV)
486
    @usableFromInline
487
    static let SHUT_RD: CInt = CInt(Android.SHUT_RD)
488
    @usableFromInline
489
    static let SHUT_WR: CInt = CInt(Android.SHUT_WR)
490
    @usableFromInline
491
    static let SHUT_RDWR: CInt = CInt(Android.SHUT_RDWR)
492
    #endif
493
    #else
494
    @usableFromInline
495
    static var UIO_MAXIOV: Int {
496
        fatalError("unsupported OS")
497
    }
498
    @usableFromInline
499
    static var SHUT_RD: Int {
500
        fatalError("unsupported OS")
501
    }
502
    @usableFromInline
503
    static var SHUT_WR: Int {
504
        fatalError("unsupported OS")
505
    }
506
    @usableFromInline
507
    static var SHUT_RDWR: Int {
508
        fatalError("unsupported OS")
509
    }
510
    #endif
511
512
    #if canImport(Darwin)
513
    static let IPTOS_ECN_NOTECT: CInt = CNIODarwin_IPTOS_ECN_NOTECT
514
    static let IPTOS_ECN_MASK: CInt = CNIODarwin_IPTOS_ECN_MASK
515
    static let IPTOS_ECN_ECT0: CInt = CNIODarwin_IPTOS_ECN_ECT0
516
    static let IPTOS_ECN_ECT1: CInt = CNIODarwin_IPTOS_ECN_ECT1
517
    static let IPTOS_ECN_CE: CInt = CNIODarwin_IPTOS_ECN_CE
518
    #elseif os(Linux) || os(FreeBSD) || os(Android)
519
    #if os(Android)
520
    static let IPTOS_ECN_NOTECT: CInt = CInt(CNIOLinux.IPTOS_ECN_NOTECT)
521
    #else
522
    static let IPTOS_ECN_NOTECT: CInt = CInt(CNIOLinux.IPTOS_ECN_NOT_ECT)
523
    #endif
524
    static let IPTOS_ECN_MASK: CInt = CInt(CNIOLinux.IPTOS_ECN_MASK)
525
    static let IPTOS_ECN_ECT0: CInt = CInt(CNIOLinux.IPTOS_ECN_ECT0)
526
    static let IPTOS_ECN_ECT1: CInt = CInt(CNIOLinux.IPTOS_ECN_ECT1)
527
    static let IPTOS_ECN_CE: CInt = CInt(CNIOLinux.IPTOS_ECN_CE)
528
    #elseif os(OpenBSD)
529
    static let IPTOS_ECN_NOTECT: CInt = CInt(CNIOOpenBSD.IPTOS_ECN_NOTECT)
530
    static let IPTOS_ECN_MASK: CInt = CInt(CNIOOpenBSD.IPTOS_ECN_MASK)
531
    static let IPTOS_ECN_ECT0: CInt = CInt(CNIOOpenBSD.IPTOS_ECN_ECT0)
532
    static let IPTOS_ECN_ECT1: CInt = CInt(CNIOOpenBSD.IPTOS_ECN_ECT1)
533
    static let IPTOS_ECN_CE: CInt = CInt(CNIOOpenBSD.IPTOS_ECN_CE)
534
    #elseif os(Windows)
535
    static let IPTOS_ECN_NOTECT: CInt = CInt(0x00)
536
    static let IPTOS_ECN_MASK: CInt = CInt(0x03)
537
    static let IPTOS_ECN_ECT0: CInt = CInt(0x02)
538
    static let IPTOS_ECN_ECT1: CInt = CInt(0x01)
539
    static let IPTOS_ECN_CE: CInt = CInt(0x03)
540
    #endif
541
542
    #if canImport(Darwin)
543
    static let IP_RECVPKTINFO: CInt = CNIODarwin.IP_RECVPKTINFO
544
    static let IP_PKTINFO: CInt = CNIODarwin.IP_PKTINFO
545
546
    static let IPV6_RECVPKTINFO: CInt = CNIODarwin_IPV6_RECVPKTINFO
547
    static let IPV6_PKTINFO: CInt = CNIODarwin_IPV6_PKTINFO
548
    #elseif os(Linux) || os(FreeBSD) || os(Android)
549
    static let IP_RECVPKTINFO: CInt = CInt(CNIOLinux.IP_PKTINFO)
550
    static let IP_PKTINFO: CInt = CInt(CNIOLinux.IP_PKTINFO)
551
552
    static let IPV6_RECVPKTINFO: CInt = CInt(CNIOLinux.IPV6_RECVPKTINFO)
553
    static let IPV6_PKTINFO: CInt = CInt(CNIOLinux.IPV6_PKTINFO)
554
    #elseif os(OpenBSD)
555
    static let IP_PKTINFO: CInt = CInt(-1)  // Not actually present.
556
557
    static let IPV6_RECVPKTINFO: CInt = CInt(CNIOOpenBSD.IPV6_RECVPKTINFO)
558
    static let IPV6_PKTINFO: CInt = CInt(CNIOOpenBSD.IPV6_PKTINFO)
559
    #elseif os(Windows)
560
    static let IP_RECVPKTINFO: CInt = CInt(WinSDK.IP_PKTINFO)
561
    static let IP_PKTINFO: CInt = CInt(WinSDK.IP_PKTINFO)
562
563
    static let IPV6_RECVPKTINFO: CInt = CInt(WinSDK.IPV6_PKTINFO)
564
    static let IPV6_PKTINFO: CInt = CInt(WinSDK.IPV6_PKTINFO)
565
    #endif
566
567
    #if canImport(Darwin)
568
    static let SOL_UDP: CInt = CInt(IPPROTO_UDP)
569
    #elseif os(Linux) || os(FreeBSD) || os(Android) || os(OpenBSD)
570
    static let SOL_UDP: CInt = CInt(IPPROTO_UDP)
571
    #elseif os(Windows)
572
    static let SOL_UDP: CInt = CInt(IPPROTO_UDP)
573
    #endif
574
575
    #if !os(Windows)
576
    @inline(never)
577
0
    public static func shutdown(descriptor: CInt, how: Shutdown) throws {
578
0
        _ = try syscall(blocking: false) {
579
0
            sysShutdown(descriptor, how.cValue)
580
0
        }
581
0
    }
582
583
    @inline(never)
584
0
    public static func close(descriptor: CInt) throws {
585
0
        let res = sysClose(descriptor)
586
0
        if res == -1 {
587
            #if os(Windows)
588
            var err: CInt = 0
589
            _get_errno(&err)
590
            #else
591
0
            let err = errno
592
            #endif
593
0
594
0
            // There is really nothing "good" we can do when EINTR was reported on close.
595
0
            // So just ignore it and "assume" everything is fine == we closed the file descriptor.
596
0
            //
597
0
            // For more details see:
598
0
            //     - https://bugs.chromium.org/p/chromium/issues/detail?id=269623
599
0
            //     - https://lwn.net/Articles/576478/
600
0
            if err != EINTR {
601
0
                preconditionIsNotUnacceptableErrnoOnClose(err: err, where: #function)
602
0
                throw IOError(errnoCode: err, reason: "close")
603
0
            }
604
0
        }
605
0
    }
606
607
    @inline(never)
608
0
    public static func bind(descriptor: CInt, ptr: UnsafePointer<sockaddr>, bytes: Int) throws {
609
0
        _ = try syscall(blocking: false) {
610
0
            sysBind(descriptor, ptr, socklen_t(bytes))
611
0
        }
612
0
    }
613
614
    @inline(never)
615
    @discardableResult
616
    @usableFromInline
617
    // TODO: Allow varargs
618
0
    internal static func fcntl(descriptor: CInt, command: CInt, value: CInt) throws -> CInt {
619
0
        try syscall(blocking: false) {
620
0
            sysFcntl(descriptor, command, value)
621
0
        }.result
622
0
    }
623
624
    @inline(never)
625
    public static func socket(
626
        domain: NIOBSDSocket.ProtocolFamily,
627
        type: NIOBSDSocket.SocketType,
628
        protocolSubtype: NIOBSDSocket.ProtocolSubtype
629
0
    ) throws -> CInt {
630
0
        try syscall(blocking: false) {
631
0
            sysSocket(domain.rawValue, type.rawValue, protocolSubtype.rawValue)
632
0
        }.result
633
0
    }
634
635
    @inline(never)
636
    public static func setsockopt(
637
        socket: CInt,
638
        level: CInt,
639
        optionName: CInt,
640
        optionValue: UnsafeRawPointer,
641
        optionLen: socklen_t
642
0
    ) throws {
643
0
        _ = try syscall(blocking: false) {
644
0
            sysSetsockopt(socket, level, optionName, optionValue, optionLen)
645
0
        }
646
0
    }
647
648
    @inline(never)
649
    public static func getsockopt(
650
        socket: CInt,
651
        level: CInt,
652
        optionName: CInt,
653
        optionValue: UnsafeMutableRawPointer,
654
        optionLen: UnsafeMutablePointer<socklen_t>
655
0
    ) throws {
656
0
        _ = try syscall(blocking: false) {
657
0
            sysGetsockopt(socket, level, optionName, optionValue, optionLen)
658
0
        }.result
659
0
    }
660
661
    @inline(never)
662
0
    public static func listen(descriptor: CInt, backlog: CInt) throws {
663
0
        _ = try syscall(blocking: false) {
664
0
            sysListen(descriptor, backlog)
665
0
        }
666
0
    }
667
668
    @inline(never)
669
    public static func accept(
670
        descriptor: CInt,
671
        addr: UnsafeMutablePointer<sockaddr>?,
672
        len: UnsafeMutablePointer<socklen_t>?
673
0
    ) throws -> CInt? {
674
0
        let result: IOResult<CInt> = try syscall(blocking: true) {
675
0
            sysAccept(descriptor, addr, len)
676
0
        }
677
0
678
0
        if case .processed(let fd) = result {
679
0
            return fd
680
0
        } else {
681
0
            return nil
682
0
        }
683
0
    }
684
685
    @inline(never)
686
0
    public static func connect(descriptor: CInt, addr: UnsafePointer<sockaddr>, size: socklen_t) throws -> Bool {
687
0
        do {
688
0
            _ = try syscall(blocking: false) {
689
0
                sysConnect(descriptor, addr, size)
690
0
            }
691
0
            return true
692
0
        } catch let err as IOError {
693
0
            if err.errnoCode == EINPROGRESS {
694
0
                return false
695
0
            }
696
0
            throw err
697
0
        }
698
0
    }
699
700
    @inline(never)
701
0
    public static func open(file: UnsafePointer<CChar>, oFlag: CInt, mode: mode_t) throws -> CInt {
702
0
        try syscall(blocking: false) {
703
0
            sysOpenWithMode(file, oFlag, mode)
704
0
        }.result
705
0
    }
706
707
    @inline(never)
708
0
    public static func open(file: UnsafePointer<CChar>, oFlag: CInt) throws -> CInt {
709
0
        try syscall(blocking: false) {
710
0
            sysOpen(file, oFlag)
711
0
        }.result
712
0
    }
713
714
    @inline(never)
715
    public static func pwrite(
716
        descriptor: CInt,
717
        pointer: UnsafeRawPointer,
718
        size: Int,
719
        offset: off_t
720
0
    ) throws -> IOResult<Int> {
721
0
        try syscall(blocking: true) {
722
0
            sysPwrite(descriptor, pointer, size, offset)
723
0
        }
724
0
    }
725
726
    @inline(never)
727
0
    public static func writev(descriptor: CInt, iovecs: UnsafeBufferPointer<IOVector>) throws -> IOResult<Int> {
728
0
        try syscall(blocking: true) {
729
0
            sysWritev(descriptor, iovecs.baseAddress!, CInt(iovecs.count))
730
0
        }
731
0
    }
732
733
    @inline(never)
734
    public static func pread(
735
        descriptor: CInt,
736
        pointer: UnsafeMutableRawPointer,
737
        size: size_t,
738
        offset: off_t
739
0
    ) throws -> IOResult<ssize_t> {
740
0
        try syscallForbiddingEINVAL {
741
0
            sysPread(descriptor, pointer, size, offset)
742
0
        }
743
0
    }
744
745
    @inline(never)
746
    public static func recvmsg(
747
        descriptor: CInt,
748
        msgHdr: UnsafeMutablePointer<msghdr>,
749
        flags: CInt
750
0
    ) throws -> IOResult<ssize_t> {
751
0
        try syscall(blocking: true) {
752
0
            sysRecvMsg(descriptor, msgHdr, flags)
753
0
        }
754
0
    }
755
756
    @inline(never)
757
    public static func sendmsg(
758
        descriptor: CInt,
759
        msgHdr: UnsafePointer<msghdr>,
760
        flags: CInt
761
0
    ) throws -> IOResult<ssize_t> {
762
0
        try syscall(blocking: true) {
763
0
            sysSendMsg(descriptor, msgHdr, flags)
764
0
        }
765
0
    }
766
    #endif
767
768
    @inline(never)
769
    public static func read(
770
        descriptor: CInt,
771
        pointer: UnsafeMutableRawPointer,
772
        size: size_t
773
0
    ) throws -> IOResult<ssize_t> {
774
0
        try syscallForbiddingEINVAL {
775
            #if os(Windows)
776
            // Windows read, reads at most UInt32. Lets clamp size there.
777
            let size = UInt32(clamping: size)
778
            return ssize_t(sysRead(descriptor, pointer, size))
779
            #else
780
0
            sysRead(descriptor, pointer, size)
781
            #endif
782
0
        }
783
0
    }
784
785
    @inline(never)
786
0
    public static func write(descriptor: CInt, pointer: UnsafeRawPointer, size: Int) throws -> IOResult<Int> {
787
0
        try syscall(blocking: true) {
788
            #if os(Windows)
789
            let size = UInt32(clamping: size)
790
            #endif
791
0
            return numericCast(sysWrite(descriptor, pointer, size))
792
0
        }
793
0
    }
794
795
    @discardableResult
796
    @inline(never)
797
0
    public static func ftruncate(descriptor: CInt, size: off_t) throws -> CInt {
798
0
        try syscall(blocking: false) {
799
0
            sysFtruncate(descriptor, numericCast(size))
800
0
        }.result
801
0
    }
802
803
    @discardableResult
804
    @inline(never)
805
0
    public static func lseek(descriptor: CInt, offset: off_t, whence: CInt) throws -> off_t {
806
0
        try syscall(blocking: false) {
807
0
            sysLseek(descriptor, offset, whence)
808
0
        }.result
809
0
    }
810
811
    @discardableResult
812
    @inline(never)
813
0
    public static func dup(descriptor: CInt) throws -> CInt {
814
0
        try syscall(blocking: false) {
815
0
            sysDup(descriptor)
816
0
        }.result
817
0
    }
818
819
    #if !os(Windows)
820
    // It's not really posix but exists on Linux and MacOS / BSD so just put it here for now to keep it simple
821
    @inline(never)
822
0
    public static func sendfile(descriptor: CInt, fd: CInt, offset: off_t, count: size_t) throws -> IOResult<Int> {
823
0
        var written: off_t = 0
824
0
        do {
825
0
            _ = try syscall(blocking: false) { () -> ssize_t in
826
                #if canImport(Darwin)
827
                var w: off_t = off_t(count)
828
                let result: CInt = Darwin.sendfile(fd, descriptor, offset, &w, nil, 0)
829
                written = w
830
                return ssize_t(result)
831
                #elseif os(Linux) || os(FreeBSD) || os(Android)
832
0
                var off: off_t = offset
833
                #if canImport(Glibc)
834
0
                let result: ssize_t = Glibc.sendfile(descriptor, fd, &off, count)
835
                #elseif canImport(Musl)
836
                let result: ssize_t = Musl.sendfile(descriptor, fd, &off, count)
837
                #elseif canImport(Android)
838
                let result: ssize_t = Android.sendfile(descriptor, fd, &off, count)
839
                #endif
840
0
                if result >= 0 {
841
0
                    written = off_t(result)
842
0
                } else {
843
0
                    written = 0
844
0
                }
845
0
                return result
846
                #else
847
                fatalError("unsupported OS")
848
                #endif
849
0
            }
850
0
            return .processed(Int(written))
851
0
        } catch let err as IOError {
852
0
            if err.errnoCode == EAGAIN {
853
0
                return .wouldBlock(Int(written))
854
0
            }
855
0
            throw err
856
0
        }
857
0
    }
858
859
    @inline(never)
860
    public static func sendmmsg(
861
        sockfd: CInt,
862
        msgvec: UnsafeMutablePointer<MMsgHdr>,
863
        vlen: CUnsignedInt,
864
        flags: CInt
865
0
    ) throws -> IOResult<Int> {
866
0
        try syscall(blocking: true) {
867
0
            Int(sysSendMmsg(sockfd, msgvec, vlen, flags))
868
0
        }
869
0
    }
870
871
    @inline(never)
872
    public static func recvmmsg(
873
        sockfd: CInt,
874
        msgvec: UnsafeMutablePointer<MMsgHdr>,
875
        vlen: CUnsignedInt,
876
        flags: CInt,
877
        timeout: UnsafeMutablePointer<timespec>?
878
0
    ) throws -> IOResult<Int> {
879
0
        try syscall(blocking: true) {
880
0
            Int(sysRecvMmsg(sockfd, msgvec, vlen, flags, timeout))
881
0
        }
882
0
    }
883
884
    @inline(never)
885
    public static func getpeername(
886
        socket: CInt,
887
        address: UnsafeMutablePointer<sockaddr>,
888
        addressLength: UnsafeMutablePointer<socklen_t>
889
0
    ) throws {
890
0
        _ = try syscall(blocking: false) {
891
0
            sysGetpeername(socket, address, addressLength)
892
0
        }
893
0
    }
894
895
    @inline(never)
896
    public static func getsockname(
897
        socket: CInt,
898
        address: UnsafeMutablePointer<sockaddr>,
899
        addressLength: UnsafeMutablePointer<socklen_t>
900
0
    ) throws {
901
0
        _ = try syscall(blocking: false) {
902
0
            sysGetsockname(socket, address, addressLength)
903
0
        }
904
0
    }
905
    #endif
906
907
    @inline(never)
908
0
    public static func if_nametoindex(_ name: UnsafePointer<CChar>?) throws -> CUnsignedInt {
909
0
        try syscall(blocking: false) {
910
0
            sysIfNameToIndex(name!)
911
0
        }.result
912
0
    }
913
914
    #if !os(Windows)
915
    @inline(never)
916
0
    public static func poll(fds: UnsafeMutablePointer<pollfd>, nfds: nfds_t, timeout: CInt) throws -> CInt {
917
0
        try syscall(blocking: false) {
918
0
            sysPoll(fds, nfds, timeout)
919
0
        }.result
920
0
    }
921
922
    @inline(never)
923
0
    public static func fstat(descriptor: CInt, outStat: UnsafeMutablePointer<stat>) throws {
924
0
        _ = try syscall(blocking: false) {
925
0
            sysFstat(descriptor, outStat)
926
0
        }
927
0
    }
928
929
    @inline(never)
930
0
    public static func stat(pathname: String, outStat: UnsafeMutablePointer<stat>) throws {
931
0
        _ = try syscall(blocking: false) {
932
0
            sysStat(pathname, outStat)
933
0
        }
934
0
    }
935
936
    @inline(never)
937
0
    public static func lstat(pathname: String, outStat: UnsafeMutablePointer<stat>) throws {
938
0
        _ = try syscall(blocking: false) {
939
0
            sysLstat(pathname, outStat)
940
0
        }
941
0
    }
942
943
    @inline(never)
944
0
    public static func symlink(pathname: String, destination: String) throws {
945
0
        _ = try syscall(blocking: false) {
946
0
            sysSymlink(destination, pathname)
947
0
        }
948
0
    }
949
950
    @inline(never)
951
    public static func readlink(
952
        pathname: String,
953
        outPath: UnsafeMutablePointer<CChar>,
954
        outPathSize: Int
955
0
    ) throws -> CLong {
956
0
        try syscall(blocking: false) {
957
0
            sysReadlink(pathname, outPath, outPathSize)
958
0
        }.result
959
0
    }
960
961
    @inline(never)
962
0
    public static func unlink(pathname: String) throws {
963
0
        _ = try syscall(blocking: false) {
964
0
            sysUnlink(pathname)
965
0
        }
966
0
    }
967
968
    @inline(never)
969
0
    public static func mkdir(pathname: String, mode: mode_t) throws {
970
0
        _ = try syscall(blocking: false) {
971
0
            sysMkdir(pathname, mode)
972
0
        }
973
0
    }
974
975
    #if canImport(Darwin)
976
    @inline(never)
977
    public static func mkpath_np(pathname: String, mode: mode_t) throws {
978
        _ = try syscall(blocking: false) {
979
            sysMkpath(pathname, mode)
980
        }
981
    }
982
983
    @inline(never)
984
    public static func opendir(pathname: String) throws -> UnsafeMutablePointer<DIR> {
985
        try syscall {
986
            sysOpendir(pathname)
987
        }
988
    }
989
990
    @inline(never)
991
    public static func readdir(dir: UnsafeMutablePointer<DIR>) throws -> UnsafeMutablePointer<dirent>? {
992
        try syscallOptional {
993
            sysReaddir(dir)
994
        }
995
    }
996
997
    @inline(never)
998
    public static func closedir(dir: UnsafeMutablePointer<DIR>) throws {
999
        _ = try syscall(blocking: true) {
1000
            sysClosedir(dir)
1001
        }
1002
    }
1003
    #elseif os(Linux) || os(FreeBSD) || os(Android) || os(OpenBSD)
1004
    @inline(never)
1005
0
    public static func opendir(pathname: String) throws -> OpaquePointer {
1006
0
        try syscall {
1007
0
            sysOpendir(pathname)
1008
0
        }
1009
0
    }
1010
1011
    @inline(never)
1012
0
    public static func readdir(dir: OpaquePointer) throws -> UnsafeMutablePointer<dirent>? {
1013
0
        try syscallOptional {
1014
0
            sysReaddir(dir)
1015
0
        }
1016
0
    }
1017
1018
    @inline(never)
1019
0
    public static func closedir(dir: OpaquePointer) throws {
1020
0
        _ = try syscall(blocking: true) {
1021
0
            sysClosedir(dir)
1022
0
        }
1023
0
    }
1024
    #endif
1025
1026
    @inline(never)
1027
0
    public static func rename(pathname: String, newName: String) throws {
1028
0
        _ = try syscall(blocking: true) {
1029
0
            sysRename(pathname, newName)
1030
0
        }
1031
0
    }
1032
1033
    @inline(never)
1034
0
    public static func remove(pathname: String) throws {
1035
0
        _ = try syscall(blocking: true) {
1036
0
            sysRemove(pathname)
1037
0
        }
1038
0
    }
1039
1040
    @inline(never)
1041
    public static func socketpair(
1042
        domain: NIOBSDSocket.ProtocolFamily,
1043
        type: NIOBSDSocket.SocketType,
1044
        protocolSubtype: NIOBSDSocket.ProtocolSubtype,
1045
        socketVector: UnsafeMutablePointer<CInt>?
1046
0
    ) throws {
1047
0
        _ = try syscall(blocking: false) {
1048
0
            sysSocketpair(domain.rawValue, type.rawValue, protocolSubtype.rawValue, socketVector!)
1049
0
        }
1050
0
    }
1051
    #endif
1052
    #if !os(Windows)
1053
    @inline(never)
1054
0
    public static func ioctl(fd: CInt, request: CUnsignedLong, ptr: UnsafeMutableRawPointer) throws {
1055
0
        _ = try syscall(blocking: false) {
1056
0
            /// `numericCast` to support musl which accepts `CInt` (cf. `CUnsignedLong`).
1057
0
            sysIoctl(fd, numericCast(request), ptr)
1058
0
        }
1059
0
    }
1060
    #endif  // !os(Windows)
1061
}
1062
1063
/// `NIOFcntlFailedError` indicates that NIO was unable to perform an
1064
/// operation on a socket.
1065
///
1066
/// This error should never happen, unfortunately, we have seen this happen on Darwin.
1067
public struct NIOFcntlFailedError: Error {}
1068
1069
/// `NIOFailedToSetSocketNonBlockingError` indicates that NIO was unable to set a socket to non-blocking mode, either
1070
/// when connecting a socket as a client or when accepting a socket as a server.
1071
///
1072
/// This error should never happen because a socket should always be able to be set to non-blocking mode. Unfortunately,
1073
/// we have seen this happen on Darwin.
1074
@available(*, deprecated, renamed: "NIOFcntlFailedError")
1075
public struct NIOFailedToSetSocketNonBlockingError: Error {}
1076
1077
#if !os(Windows)
1078
extension Posix {
1079
0
    public static func setNonBlocking(socket: CInt) throws {
1080
0
        let flags = try Posix.fcntl(descriptor: socket, command: F_GETFL, value: 0)
1081
0
        do {
1082
0
            let ret = try Posix.fcntl(descriptor: socket, command: F_SETFL, value: flags | O_NONBLOCK)
1083
0
            assert(ret == 0, "unexpectedly, fcntl(\(socket), F_SETFL, \(flags) | O_NONBLOCK) returned \(ret)")
1084
0
        } catch let error as IOError {
1085
0
            if error.errnoCode == EINVAL {
1086
0
                // Darwin seems to sometimes do this despite the docs claiming it can't happen
1087
0
                throw NIOFcntlFailedError()
1088
0
            }
1089
0
            throw error
1090
0
        }
1091
0
    }
1092
}
1093
#endif
1094
1095
#if canImport(Darwin) || os(OpenBSD)
1096
#if canImport(Darwin)
1097
internal typealias kevent_timespec = Darwin.timespec
1098
#elseif os(OpenBSD)
1099
internal typealias kevent_timespec = CNIOOpenBSD.timespec
1100
#else
1101
#error("implementation missing")
1102
#endif
1103
1104
@usableFromInline
1105
internal enum KQueue: Sendable {
1106
1107
    // TODO: Figure out how to specify a typealias to the kevent struct without run into trouble with the swift compiler
1108
1109
    @inline(never)
1110
    public static func kqueue() throws -> CInt {
1111
        try syscall(blocking: false) {
1112
            #if canImport(Darwin)
1113
            Darwin.kqueue()
1114
            #elseif os(OpenBSD)
1115
            CNIOOpenBSD.kqueue()
1116
            #else
1117
            #error("implementation missing")
1118
            #endif
1119
        }.result
1120
    }
1121
1122
    @inline(never)
1123
    @discardableResult
1124
    public static func kevent(
1125
        kq: CInt,
1126
        changelist: UnsafePointer<kevent>?,
1127
        nchanges: CInt,
1128
        eventlist: UnsafeMutablePointer<kevent>?,
1129
        nevents: CInt,
1130
        timeout: UnsafePointer<kevent_timespec>?
1131
    ) throws -> CInt {
1132
        try syscall(blocking: false) {
1133
            sysKevent(kq, changelist, nchanges, eventlist, nevents, timeout)
1134
        }.result
1135
    }
1136
}
1137
#endif
1138
#endif  // !os(WASI)