Coverage Report

Created: 2026-02-11 06:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/swift-nio/Sources/NIOPosix/BSDSocketAPIPosix.swift
Line
Count
Source
1
//===----------------------------------------------------------------------===//
2
//
3
// This source file is part of the SwiftNIO open source project
4
//
5
// Copyright (c) 2020-2021 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
15
#if !os(WASI)
16
17
import NIOCore
18
19
#if os(Linux) || os(Android) || os(FreeBSD) || canImport(Darwin) || os(OpenBSD)
20
21
extension Shutdown {
22
0
    internal var cValue: CInt {
23
0
        switch self {
24
0
        case .RD:
25
0
            return CInt(Posix.SHUT_RD)
26
0
        case .WR:
27
0
            return CInt(Posix.SHUT_WR)
28
0
        case .RDWR:
29
0
            return CInt(Posix.SHUT_RDWR)
30
0
        }
31
0
    }
32
}
33
34
// MARK: Implementation of _BSDSocketProtocol for POSIX systems
35
extension NIOBSDSocket {
36
    static func accept(
37
        socket s: NIOBSDSocket.Handle,
38
        address addr: UnsafeMutablePointer<sockaddr>?,
39
        address_len addrlen: UnsafeMutablePointer<socklen_t>?
40
0
    ) throws -> NIOBSDSocket.Handle? {
41
0
        try Posix.accept(descriptor: s, addr: addr, len: addrlen)
42
0
    }
43
44
    static func bind(
45
        socket s: NIOBSDSocket.Handle,
46
        address addr: UnsafePointer<sockaddr>,
47
        address_len namelen: socklen_t
48
0
    ) throws {
49
0
        try Posix.bind(descriptor: s, ptr: addr, bytes: Int(namelen))
50
0
    }
51
52
0
    static func close(socket s: NIOBSDSocket.Handle) throws {
53
0
        try Posix.close(descriptor: s)
54
0
    }
55
56
    static func connect(
57
        socket s: NIOBSDSocket.Handle,
58
        address name: UnsafePointer<sockaddr>,
59
        address_len namelen: socklen_t
60
0
    ) throws -> Bool {
61
0
        try Posix.connect(descriptor: s, addr: name, size: namelen)
62
0
    }
63
64
    static func getpeername(
65
        socket s: NIOBSDSocket.Handle,
66
        address name: UnsafeMutablePointer<sockaddr>,
67
        address_len namelen: UnsafeMutablePointer<socklen_t>
68
0
    ) throws {
69
0
        try Posix.getpeername(socket: s, address: name, addressLength: namelen)
70
0
    }
71
72
    static func getsockname(
73
        socket s: NIOBSDSocket.Handle,
74
        address name: UnsafeMutablePointer<sockaddr>,
75
        address_len namelen: UnsafeMutablePointer<socklen_t>
76
0
    ) throws {
77
0
        try Posix.getsockname(socket: s, address: name, addressLength: namelen)
78
0
    }
79
80
    static func getsockopt(
81
        socket: NIOBSDSocket.Handle,
82
        level: NIOBSDSocket.OptionLevel,
83
        option_name optname: NIOBSDSocket.Option,
84
        option_value optval: UnsafeMutableRawPointer,
85
        option_len optlen: UnsafeMutablePointer<socklen_t>
86
0
    ) throws {
87
0
        try Posix.getsockopt(
88
0
            socket: socket,
89
0
            level: level.rawValue,
90
0
            optionName: optname.rawValue,
91
0
            optionValue: optval,
92
0
            optionLen: optlen
93
0
        )
94
0
    }
95
96
0
    static func listen(socket s: NIOBSDSocket.Handle, backlog: CInt) throws {
97
0
        try Posix.listen(descriptor: s, backlog: backlog)
98
0
    }
99
100
    static func recv(
101
        socket s: NIOBSDSocket.Handle,
102
        buffer buf: UnsafeMutableRawPointer,
103
        length len: size_t
104
0
    ) throws -> IOResult<size_t> {
105
0
        try Posix.read(descriptor: s, pointer: buf, size: len)
106
0
    }
107
108
    static func recvmsg(
109
        socket: NIOBSDSocket.Handle,
110
        msgHdr: UnsafeMutablePointer<msghdr>,
111
        flags: CInt
112
    )
113
        throws -> IOResult<size_t>
114
0
    {
115
0
        try Posix.recvmsg(descriptor: socket, msgHdr: msgHdr, flags: flags)
116
0
    }
117
118
    static func sendmsg(
119
        socket: NIOBSDSocket.Handle,
120
        msgHdr: UnsafePointer<msghdr>,
121
        flags: CInt
122
    )
123
        throws -> IOResult<size_t>
124
0
    {
125
0
        try Posix.sendmsg(descriptor: socket, msgHdr: msgHdr, flags: flags)
126
0
    }
127
128
    static func send(
129
        socket s: NIOBSDSocket.Handle,
130
        buffer buf: UnsafeRawPointer,
131
        length len: size_t
132
0
    ) throws -> IOResult<size_t> {
133
0
        try Posix.write(descriptor: s, pointer: buf, size: len)
134
0
    }
135
136
    static func writev(
137
        socket s: NIOBSDSocket.Handle,
138
        iovecs: UnsafeBufferPointer<IOVector>
139
0
    ) throws -> IOResult<Int> {
140
0
        try Posix.writev(descriptor: s, iovecs: iovecs)
141
0
    }
142
143
    static func setsockopt(
144
        socket: NIOBSDSocket.Handle,
145
        level: NIOBSDSocket.OptionLevel,
146
        option_name optname: NIOBSDSocket.Option,
147
        option_value optval: UnsafeRawPointer,
148
        option_len optlen: socklen_t
149
0
    ) throws {
150
0
        try Posix.setsockopt(
151
0
            socket: socket,
152
0
            level: level.rawValue,
153
0
            optionName: optname.rawValue,
154
0
            optionValue: optval,
155
0
            optionLen: optlen
156
0
        )
157
0
    }
158
159
0
    static func shutdown(socket: NIOBSDSocket.Handle, how: Shutdown) throws {
160
0
        try Posix.shutdown(descriptor: socket, how: how)
161
0
    }
162
163
    static func socket(
164
        domain af: NIOBSDSocket.ProtocolFamily,
165
        type: NIOBSDSocket.SocketType,
166
        protocolSubtype: NIOBSDSocket.ProtocolSubtype
167
0
    ) throws -> NIOBSDSocket.Handle {
168
0
        try Posix.socket(domain: af, type: type, protocolSubtype: protocolSubtype)
169
0
    }
170
171
    static func recvmmsg(
172
        socket: NIOBSDSocket.Handle,
173
        msgvec: UnsafeMutablePointer<MMsgHdr>,
174
        vlen: CUnsignedInt,
175
        flags: CInt,
176
        timeout: UnsafeMutablePointer<timespec>?
177
0
    ) throws -> IOResult<Int> {
178
0
        try Posix.recvmmsg(
179
0
            sockfd: socket,
180
0
            msgvec: msgvec,
181
0
            vlen: vlen,
182
0
            flags: flags,
183
0
            timeout: timeout
184
0
        )
185
0
    }
186
187
    static func sendmmsg(
188
        socket: NIOBSDSocket.Handle,
189
        msgvec: UnsafeMutablePointer<MMsgHdr>,
190
        vlen: CUnsignedInt,
191
        flags: CInt
192
0
    ) throws -> IOResult<Int> {
193
0
        try Posix.sendmmsg(
194
0
            sockfd: socket,
195
0
            msgvec: msgvec,
196
0
            vlen: vlen,
197
0
            flags: flags
198
0
        )
199
0
    }
200
201
    // NOTE: this should return a `ssize_t`, however, that is not a standard
202
    // type, and defining that type is difficult.  Opt to return a `size_t`
203
    // which is the same size, but is unsigned.
204
    static func pread(
205
        socket: NIOBSDSocket.Handle,
206
        pointer: UnsafeMutableRawPointer,
207
        size: size_t,
208
        offset: off_t
209
0
    ) throws -> IOResult<size_t> {
210
0
        try Posix.pread(
211
0
            descriptor: socket,
212
0
            pointer: pointer,
213
0
            size: size,
214
0
            offset: offset
215
0
        )
216
0
    }
217
218
    // NOTE: this should return a `ssize_t`, however, that is not a standard
219
    // type, and defining that type is difficult.  Opt to return a `size_t`
220
    // which is the same size, but is unsigned.
221
    static func pwrite(
222
        socket: NIOBSDSocket.Handle,
223
        pointer: UnsafeRawPointer,
224
        size: size_t,
225
        offset: off_t
226
0
    ) throws -> IOResult<size_t> {
227
0
        try Posix.pwrite(descriptor: socket, pointer: pointer, size: size, offset: offset)
228
0
    }
229
230
    static func poll(
231
        fds: UnsafeMutablePointer<pollfd>,
232
        nfds: nfds_t,
233
        timeout: CInt
234
0
    ) throws -> CInt {
235
0
        try Posix.poll(fds: fds, nfds: nfds, timeout: timeout)
236
0
    }
237
238
    static func sendfile(
239
        socket s: NIOBSDSocket.Handle,
240
        fd: CInt,
241
        offset: off_t,
242
        len: off_t
243
0
    ) throws -> IOResult<Int> {
244
0
        try Posix.sendfile(descriptor: s, fd: fd, offset: offset, count: size_t(len))
245
0
    }
246
247
0
    static func setNonBlocking(socket: NIOBSDSocket.Handle) throws {
248
0
        try Posix.setNonBlocking(socket: socket)
249
0
    }
250
251
0
    static func cleanupUnixDomainSocket(atPath path: String) throws {
252
0
        do {
253
0
            var sb: stat = stat()
254
0
            try withUnsafeMutablePointer(to: &sb) { sbPtr in
255
0
                try Posix.stat(pathname: path, outStat: sbPtr)
256
0
            }
257
0
258
0
            // Only unlink the existing file if it is a socket
259
0
            if sb.st_mode & S_IFSOCK == S_IFSOCK {
260
0
                try Posix.unlink(pathname: path)
261
0
            } else {
262
0
                throw UnixDomainSocketPathWrongType()
263
0
            }
264
0
        } catch let err as IOError {
265
0
            // If the filepath did not exist, we consider it cleaned up
266
0
            if err.errnoCode == ENOENT {
267
0
                return
268
0
            }
269
0
            throw err
270
0
        }
271
0
    }
272
}
273
274
#if canImport(Darwin)
275
import CNIODarwin
276
private let CMSG_FIRSTHDR = CNIODarwin_CMSG_FIRSTHDR
277
private let CMSG_NXTHDR = CNIODarwin_CMSG_NXTHDR
278
private let CMSG_DATA = CNIODarwin_CMSG_DATA
279
private let CMSG_DATA_MUTABLE = CNIODarwin_CMSG_DATA_MUTABLE
280
private let CMSG_SPACE = CNIODarwin_CMSG_SPACE
281
private let CMSG_LEN = CNIODarwin_CMSG_LEN
282
#elseif os(OpenBSD)
283
import CNIOOpenBSD
284
private let CMSG_FIRSTHDR = CNIOOpenBSD_CMSG_FIRSTHDR
285
private let CMSG_NXTHDR = CNIOOpenBSD_CMSG_NXTHDR
286
private let CMSG_DATA = CNIOOpenBSD_CMSG_DATA
287
private let CMSG_DATA_MUTABLE = CNIOOpenBSD_CMSG_DATA_MUTABLE
288
private let CMSG_SPACE = CNIOOpenBSD_CMSG_SPACE
289
private let CMSG_LEN = CNIOOpenBSD_CMSG_LEN
290
#else
291
import CNIOLinux
292
private let CMSG_FIRSTHDR = CNIOLinux_CMSG_FIRSTHDR
293
private let CMSG_NXTHDR = CNIOLinux_CMSG_NXTHDR
294
private let CMSG_DATA = CNIOLinux_CMSG_DATA
295
private let CMSG_DATA_MUTABLE = CNIOLinux_CMSG_DATA_MUTABLE
296
private let CMSG_SPACE = CNIOLinux_CMSG_SPACE
297
private let CMSG_LEN = CNIOLinux_CMSG_LEN
298
#endif
299
300
// MARK: _BSDSocketControlMessageProtocol implementation
301
extension NIOBSDSocketControlMessage {
302
    static func firstHeader(
303
        inside msghdr: UnsafePointer<msghdr>
304
    )
305
        -> UnsafeMutablePointer<cmsghdr>?
306
0
    {
307
0
        CMSG_FIRSTHDR(msghdr)
308
0
    }
309
310
    static func nextHeader(
311
        inside msghdr: UnsafeMutablePointer<msghdr>,
312
        after: UnsafeMutablePointer<cmsghdr>
313
    )
314
        -> UnsafeMutablePointer<cmsghdr>?
315
0
    {
316
0
        CMSG_NXTHDR(msghdr, after)
317
0
    }
318
319
    static func data(
320
        for header: UnsafePointer<cmsghdr>
321
    )
322
        -> UnsafeRawBufferPointer?
323
0
    {
324
0
        let data = CMSG_DATA(header)
325
0
        let length =
326
0
            size_t(header.pointee.cmsg_len) - NIOBSDSocketControlMessage.length(payloadSize: 0)
327
0
        return UnsafeRawBufferPointer(start: data, count: Int(length))
328
0
    }
329
330
    static func data(
331
        for header: UnsafeMutablePointer<cmsghdr>
332
    )
333
        -> UnsafeMutableRawBufferPointer?
334
0
    {
335
0
        let data = CMSG_DATA_MUTABLE(header)
336
0
        let length =
337
0
            size_t(header.pointee.cmsg_len) - NIOBSDSocketControlMessage.length(payloadSize: 0)
338
0
        return UnsafeMutableRawBufferPointer(start: data, count: Int(length))
339
0
    }
340
341
0
    static func length(payloadSize: size_t) -> size_t {
342
0
        CMSG_LEN(payloadSize)
343
0
    }
344
345
0
    static func space(payloadSize: size_t) -> size_t {
346
0
        CMSG_SPACE(payloadSize)
347
0
    }
348
}
349
350
extension NIOBSDSocket {
351
0
    static func setUDPSegmentSize(_ segmentSize: CInt, socket: NIOBSDSocket.Handle) throws {
352
        #if os(Linux)
353
0
        var segmentSize = segmentSize
354
0
        try Self.setsockopt(
355
0
            socket: socket,
356
0
            level: .udp,
357
0
            option_name: .udp_segment,
358
0
            option_value: &segmentSize,
359
0
            option_len: socklen_t(MemoryLayout<CInt>.size)
360
0
        )
361
        #else
362
        throw ChannelError._operationUnsupported
363
        #endif
364
0
    }
365
366
0
    static func getUDPSegmentSize(socket: NIOBSDSocket.Handle) throws -> CInt {
367
        #if os(Linux)
368
0
        var segmentSize: CInt = 0
369
0
        var optionLength = socklen_t(MemoryLayout<CInt>.size)
370
0
        try withUnsafeMutablePointer(to: &segmentSize) { segmentSizeBytes in
371
0
            try Self.getsockopt(
372
0
                socket: socket,
373
0
                level: .udp,
374
0
                option_name: .udp_segment,
375
0
                option_value: segmentSizeBytes,
376
0
                option_len: &optionLength
377
0
            )
378
0
        }
379
0
        return segmentSize
380
        #else
381
        throw ChannelError._operationUnsupported
382
        #endif
383
0
    }
384
385
0
    static func setUDPReceiveOffload(_ enabled: Bool, socket: NIOBSDSocket.Handle) throws {
386
        #if os(Linux)
387
0
        var isEnabled: CInt = enabled ? 1 : 0
388
0
        try Self.setsockopt(
389
0
            socket: socket,
390
0
            level: .udp,
391
0
            option_name: .udp_gro,
392
0
            option_value: &isEnabled,
393
0
            option_len: socklen_t(MemoryLayout<CInt>.size)
394
0
        )
395
        #else
396
        throw ChannelError._operationUnsupported
397
        #endif
398
0
    }
399
400
0
    static func getUDPReceiveOffload(socket: NIOBSDSocket.Handle) throws -> Bool {
401
        #if os(Linux)
402
0
        var enabled: CInt = 0
403
0
        var optionLength = socklen_t(MemoryLayout<CInt>.size)
404
0
        try withUnsafeMutablePointer(to: &enabled) { enabledBytes in
405
0
            try Self.getsockopt(
406
0
                socket: socket,
407
0
                level: .udp,
408
0
                option_name: .udp_gro,
409
0
                option_value: enabledBytes,
410
0
                option_len: &optionLength
411
0
            )
412
0
        }
413
0
        return enabled != 0
414
        #else
415
        throw ChannelError._operationUnsupported
416
        #endif
417
0
    }
418
}
419
420
extension msghdr {
421
    var control_ptr: UnsafeMutableRawBufferPointer {
422
0
        set {
423
0
            self.msg_control = newValue.baseAddress
424
0
            self.msg_controllen = numericCast(newValue.count)
425
0
        }
426
0
        get {
427
0
            UnsafeMutableRawBufferPointer(start: self.msg_control, count: Int(self.msg_controllen))
428
0
        }
429
    }
430
}
431
#endif
432
#endif  // !os(WASI)