Skip to content

Commit 0578678

Browse files
committed
WIP: basic socket support
1 parent 2ac0cdc commit 0578678

File tree

5 files changed

+368
-0
lines changed

5 files changed

+368
-0
lines changed

Sources/System/Internals/Constants.swift

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -470,3 +470,47 @@ internal var _SEEK_HOLE: CInt { SEEK_HOLE }
470470
internal var _SEEK_DATA: CInt { SEEK_DATA }
471471
#endif
472472

473+
@_alwaysEmitIntoClient
474+
internal var _PF_LOCAL: CInt { PF_LOCAL }
475+
476+
@_alwaysEmitIntoClient
477+
internal var _PF_UNIX: CInt { PF_UNIX }
478+
479+
@_alwaysEmitIntoClient
480+
internal var _PF_INET: CInt { PF_INET }
481+
482+
@_alwaysEmitIntoClient
483+
internal var _PF_ROUTE: CInt { PF_ROUTE }
484+
485+
@_alwaysEmitIntoClient
486+
internal var _PF_KEY: CInt { PF_KEY }
487+
488+
@_alwaysEmitIntoClient
489+
internal var _PF_INET6: CInt { PF_INET6 }
490+
491+
@_alwaysEmitIntoClient
492+
internal var _PF_SYSTEM: CInt { PF_SYSTEM }
493+
494+
@_alwaysEmitIntoClient
495+
internal var _PF_NDRV: CInt { PF_NDRV }
496+
497+
@_alwaysEmitIntoClient
498+
internal var _SOCK_STREAM: CInt { SOCK_STREAM }
499+
500+
@_alwaysEmitIntoClient
501+
internal var _SOCK_DGRAM: CInt { SOCK_DGRAM }
502+
503+
@_alwaysEmitIntoClient
504+
internal var _SOCK_RAW: CInt { SOCK_RAW }
505+
506+
@_alwaysEmitIntoClient
507+
internal var _MSG_OOB: CInt { MSG_OOB }
508+
509+
@_alwaysEmitIntoClient
510+
internal var _MSG_DONTROUTE: CInt { MSG_DONTROUTE }
511+
512+
@_alwaysEmitIntoClient
513+
internal var _MSG_PEEK: CInt { MSG_PEEK }
514+
515+
@_alwaysEmitIntoClient
516+
internal var _MSG_WAITALL: CInt { MSG_WAITALL }

Sources/System/Internals/Syscalls.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,3 +115,10 @@ internal func system_dup2(_ fd: Int32, _ fd2: Int32) -> Int32 {
115115
#endif
116116
return dup2(fd, fd2)
117117
}
118+
119+
internal func system_socket(_ domain: CInt, type: CInt, protocol: CInt) -> CInt {
120+
#if ENABLE_MOCKING
121+
if mockingEnabled { return _mock(domain, type, `protocol`) }
122+
#endif
123+
return socket(domain, type, `protocol`)
124+
}
Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
/*
2+
This source file is part of the Swift System open source project
3+
4+
Copyright (c) 2021 Apple Inc. and the Swift System project authors
5+
Licensed under Apache License v2.0 with Runtime Library Exception
6+
7+
See https://swift.org/LICENSE.txt for license information
8+
*/
9+
10+
11+
// TODO: @available(...)
12+
13+
// TODO: Windows uses a SOCKET type; doesn't have file descriptor
14+
// equivalence
15+
16+
/// TODO
17+
@frozen
18+
public struct SocketDescriptor: RawRepresentable, Hashable {
19+
/// The raw C socket
20+
@_alwaysEmitIntoClient
21+
public let rawValue: CInt
22+
23+
/// Creates a strongly-typed socket from a raw C socket
24+
@_alwaysEmitIntoClient
25+
public init(rawValue: CInt) { self.rawValue = rawValue }
26+
}
27+
28+
extension SocketDescriptor {
29+
/// The file descriptor for `self`.
30+
@_alwaysEmitIntoClient
31+
public var fileDescriptor: FileDescriptor {
32+
FileDescriptor(rawValue: rawValue)
33+
}
34+
35+
/// Treat `fd` as a socket descriptor, without checking with the operating
36+
/// system that it actually refers to a socket
37+
@_alwaysEmitIntoClient
38+
public init(unchecked fd: FileDescriptor) {
39+
self.init(rawValue: fd.rawValue)
40+
}
41+
}
42+
43+
extension FileDescriptor {
44+
/// Treat `self` as a socket descriptor, without checking with the operating
45+
/// system that it actually refers to a socket
46+
@_alwaysEmitIntoClient
47+
public var uncheckedSocket: SocketDescriptor {
48+
SocketDescriptor(unchecked: self)
49+
}
50+
}
51+
52+
extension SocketDescriptor {
53+
/// Communications domain: the protocol family which should be used
54+
@frozen
55+
public struct Domain: RawRepresentable {
56+
@_alwaysEmitIntoClient
57+
public let rawValue: CInt
58+
59+
@_alwaysEmitIntoClient
60+
public init(rawValue: CInt) { self.rawValue = rawValue }
61+
62+
/// Host-internal protocols, formerly called PF_UNIX,
63+
///
64+
/// The corresponding C constant is `PF_LOCAL`
65+
@_alwaysEmitIntoClient
66+
public static var local: Domain { Domain(rawValue: _PF_LOCAL) }
67+
68+
@_alwaysEmitIntoClient
69+
@available(*, unavailable, renamed: "local")
70+
public static var unix: Domain { Domain(rawValue: _PF_UNIX) }
71+
72+
/// Internet version 4 protocols,
73+
///
74+
/// The corresponding C constant is `PF_INET`
75+
@_alwaysEmitIntoClient
76+
public static var ipv4: Domain { Domain(rawValue: _PF_INET) }
77+
78+
/// Internal Routing protocol,
79+
///
80+
/// The corresponding C constant is `PF_ROUTE`
81+
@_alwaysEmitIntoClient
82+
public static var routing: Domain { Domain(rawValue: _PF_ROUTE) }
83+
84+
/// Internal key-management function,
85+
///
86+
/// The corresponding C constant is `PF_KEY`
87+
@_alwaysEmitIntoClient
88+
public static var keyManagement: Domain { Domain(rawValue: _PF_KEY) }
89+
90+
/// Internet version 6 protocols,
91+
///
92+
/// The corresponding C constant is `PF_INET6`
93+
@_alwaysEmitIntoClient
94+
public static var ipv6: Domain { Domain(rawValue: _PF_INET6) }
95+
96+
/// System domain,
97+
///
98+
/// The corresponding C constant is `PF_SYSTEM`
99+
@_alwaysEmitIntoClient
100+
public static var system: Domain { Domain(rawValue: _PF_SYSTEM) }
101+
102+
/// Raw access to network device
103+
///
104+
/// The corresponding C constant is `PF_NDRV`
105+
@_alwaysEmitIntoClient
106+
public static var networkDevice: Domain { Domain(rawValue: _PF_NDRV) }
107+
}
108+
109+
/// TODO
110+
@frozen
111+
public struct ConnectionType: RawRepresentable {
112+
@_alwaysEmitIntoClient
113+
public let rawValue: CInt
114+
115+
@_alwaysEmitIntoClient
116+
public init(rawValue: CInt) { self.rawValue = rawValue }
117+
118+
/// Sequenced, reliable, two-way connection based byte streams.
119+
///
120+
/// The corresponding C constant is `SOCK_STREAM`
121+
@_alwaysEmitIntoClient
122+
public static var stream: ConnectionType { ConnectionType(rawValue: _SOCK_STREAM) }
123+
124+
/// Datagrams (connectionless, unreliable messages of a fixed (typically small) maximum length)
125+
///
126+
/// The corresponding C constant is `SOCK_DGRAM`
127+
@_alwaysEmitIntoClient
128+
public static var datagram: ConnectionType { ConnectionType(rawValue: _SOCK_DGRAM) }
129+
130+
/// Only available to the super user
131+
///
132+
/// The corresponding C constant is `SOCK_RAW`
133+
@_alwaysEmitIntoClient
134+
public static var raw: ConnectionType { ConnectionType(rawValue: _SOCK_RAW) }
135+
}
136+
137+
/// TODO
138+
@frozen
139+
public struct ProtocolID: RawRepresentable {
140+
@_alwaysEmitIntoClient
141+
public let rawValue: CInt
142+
143+
@_alwaysEmitIntoClient
144+
public init(rawValue: CInt) { self.rawValue = rawValue }
145+
146+
/// The default protocol for the domain and connection type combination.
147+
@_alwaysEmitIntoClient
148+
public static var `default`: ProtocolID { self.init(rawValue: 0) }
149+
}
150+
151+
// TODO: option flags (SO_DEBUG)?
152+
153+
// TODO: address families? Should we jus do INET and INET6?
154+
155+
@frozen
156+
public struct MessageFlags: OptionSet {
157+
@_alwaysEmitIntoClient
158+
public let rawValue: CInt
159+
160+
@_alwaysEmitIntoClient
161+
public init(rawValue: CInt) { self.rawValue = rawValue }
162+
163+
@_alwaysEmitIntoClient
164+
private init(_ raw: CInt) { self.init(rawValue: raw) }
165+
166+
// MSG_OOB: process out-of-band data
167+
@_alwaysEmitIntoClient
168+
public static var outOfBand: MessageFlags { MessageFlags(_MSG_OOB) }
169+
170+
// MSG_DONTROUTE: bypass routing, use direct interface
171+
@_alwaysEmitIntoClient
172+
public static var doNotRoute: MessageFlags { MessageFlags(_MSG_DONTROUTE) }
173+
174+
// MSG_PEEK: peek at incoming message
175+
@_alwaysEmitIntoClient
176+
public static var peek: MessageFlags { MessageFlags(_MSG_PEEK) }
177+
178+
// MSG_WAITALL: wait for full request or error
179+
@_alwaysEmitIntoClient
180+
public static var waitForAll: MessageFlags { MessageFlags(_MSG_WAITALL) }
181+
182+
// TODO: MSG_EOR MSG_TRUNC MSG_CTRUNC MSG_DONTWAIT MSG_EOF MSG_WAITSTREAM
183+
// TODO: MSG_FLUSH MSG_HOLD MSG_SEND MSG_HAVEMORE MSG_RCVMORE MSG_NEEDSA
184+
// TODO: MSG_NOSIGNAL
185+
}
186+
187+
}
188+
189+
extension SocketDescriptor {
190+
/*
191+
192+
int accept(int, struct sockaddr * __restrict, socklen_t * __restrict)
193+
int bind(int, const struct sockaddr *, socklen_t) __DARWIN_ALIAS(bind);
194+
int connect(int, const struct sockaddr *, socklen_t) __DARWIN_ALIAS_C(connect);
195+
int getpeername(int, struct sockaddr * __restrict, socklen_t * __restrict)
196+
int getsockname(int, struct sockaddr * __restrict, socklen_t * __restrict)
197+
int getsockopt(int, int, int, void * __restrict, socklen_t * __restrict);
198+
int listen(int, int) __DARWIN_ALIAS(listen);
199+
ssize_t recv(int, void *, size_t, int) __DARWIN_ALIAS_C(recv);
200+
ssize_t recvfrom(int, void *, size_t, int, struct sockaddr * __restrict,
201+
socklen_t * __restrict) __DARWIN_ALIAS_C(recvfrom);
202+
ssize_t recvmsg(int, struct msghdr *, int) __DARWIN_ALIAS_C(recvmsg);
203+
ssize_t send(int, const void *, size_t, int) __DARWIN_ALIAS_C(send);
204+
ssize_t sendmsg(int, const struct msghdr *, int) __DARWIN_ALIAS_C(sendmsg);
205+
ssize_t sendto(int, const void *, size_t,
206+
int, const struct sockaddr *, socklen_t) __DARWIN_ALIAS_C(sendto);
207+
int setsockopt(int, int, int, const void *, socklen_t);
208+
int shutdown(int, int);
209+
int sockatmark(int) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
210+
int socket(int, int, int);
211+
int socketpair(int, int, int, int *) __DARWIN_ALIAS(socketpair);
212+
213+
#if !defined(_POSIX_C_SOURCE)
214+
int sendfile(int, int, off_t, off_t *, struct sf_hdtr *, int);
215+
#endif /* !_POSIX_C_SOURCE */
216+
217+
#if !defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE)
218+
void pfctlinput(int, struct sockaddr *);
219+
220+
__API_AVAILABLE(macosx(10.11), ios(9.0), tvos(9.0), watchos(2.0))
221+
int connectx(int, const sa_endpoints_t *, sae_associd_t, unsigned int,
222+
const struct iovec *, unsigned int, size_t *, sae_connid_t *);
223+
224+
__API_AVAILABLE(macosx(10.11), ios(9.0), tvos(9.0), watchos(2.0))
225+
int disconnectx(int, sae_associd_t, sae_connid_t);
226+
#endif /* (!_POSIX_C_SOURCE || _DARWIN_C_SOURCE) */
227+
228+
*/
229+
}
230+
231+
// TODO: socket addresses...
232+
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
This source file is part of the Swift System open source project
3+
4+
Copyright (c) 2021 Apple Inc. and the Swift System project authors
5+
Licensed under Apache License v2.0 with Runtime Library Exception
6+
7+
See https://swift.org/LICENSE.txt for license information
8+
*/
9+
10+
extension SocketDescriptor {
11+
/// Create an endpoint for communication.
12+
///
13+
/// - Parameters:
14+
/// - domain: Select the protocol family which should be used for
15+
/// communication
16+
/// - type: Specify the semantics of communication
17+
/// - protocol: Specify a particular protocol to use with the socket.
18+
/// Normally, there is only one protocol for a particular connection
19+
/// type within a protocol family, so a default argument of `.default`
20+
/// is provided
21+
/// - retryOnInterrupt: Whether to retry the open operation
22+
/// if it throws ``Errno/interrupted``.
23+
/// The default is `true`.
24+
/// Pass `false` to try only once and throw an error upon interruption.
25+
///
26+
/// The corresponding C function is `socket`
27+
@_alwaysEmitIntoClient
28+
public static func open(
29+
_ domain: Domain,
30+
_ type: ConnectionType,
31+
_ protocol: ProtocolID = .default,
32+
retryOnInterrupt: Bool = true
33+
) throws -> SocketDescriptor {
34+
try SocketDescriptor._open(
35+
domain, type, `protocol`, retryOnInterrupt: retryOnInterrupt
36+
).get()
37+
}
38+
39+
@usableFromInline
40+
internal static func _open(
41+
_ domain: Domain,
42+
_ type: ConnectionType,
43+
_ protocol: ProtocolID = .default,
44+
retryOnInterrupt: Bool
45+
) -> Result<SocketDescriptor, Errno> {
46+
valueOrErrno(retryOnInterrupt: retryOnInterrupt) {
47+
system_socket(domain.rawValue, type: type.rawValue, protocol: `protocol`.rawValue)
48+
}.map(SocketDescriptor.init(rawValue:))
49+
}
50+
51+
52+
}

Tests/SystemTests/SocketTest.swift

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
This source file is part of the Swift System open source project
3+
4+
Copyright (c) 2021 Apple Inc. and the Swift System project authors
5+
Licensed under Apache License v2.0 with Runtime Library Exception
6+
7+
See https://swift.org/LICENSE.txt for license information
8+
*/
9+
10+
import XCTest
11+
12+
#if SYSTEM_PACKAGE
13+
import SystemPackage
14+
#else
15+
import System
16+
#endif
17+
18+
// @available(...)
19+
final class SocketTest: XCTestCase {
20+
21+
func testSyscalls() {
22+
23+
let syscallTestCases: Array<MockTestCase> = [
24+
MockTestCase(name: "socket", PF_INET6, SOCK_STREAM, 0, interruptable: true) {
25+
retryOnInterrupt in
26+
_ = try SocketDescriptor.open(.ipv6, .stream, retryOnInterrupt: retryOnInterrupt)
27+
},
28+
]
29+
30+
syscallTestCases.forEach { $0.runAllTests() }
31+
32+
}
33+
}

0 commit comments

Comments
 (0)