/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) |