Skip to content

Commit 3d532f3

Browse files
committed
Add nice wrappers for sockaddr and sockaddr_in (#1)
Add Swifty wrappers for socket and IP addresses
1 parent aae1ff5 commit 3d532f3

File tree

13 files changed

+802
-15
lines changed

13 files changed

+802
-15
lines changed

.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
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 String {
11+
internal init(
12+
_unsafeUninitializedCapacity capacity: Int,
13+
initializingUTF8With body: (UnsafeMutableBufferPointer<UInt8>) throws -> Int
14+
) rethrows {
15+
if #available(macOS 11, iOS 14.0, watchOS 7.0, tvOS 14.0, *) {
16+
self = try String(
17+
unsafeUninitializedCapacity: capacity,
18+
initializingUTF8With: body)
19+
return
20+
}
21+
22+
let array = try Array<UInt8>(
23+
unsafeUninitializedCapacity: capacity
24+
) { buffer, count in
25+
count = try body(buffer)
26+
}
27+
self = String(decoding: array, as: UTF8.self)
28+
}
29+
}

Sources/System/Internals/CInterop.swift

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
This source file is part of the Swift System open source project
33

4-
Copyright (c) 2020 Apple Inc. and the Swift System project authors
4+
Copyright (c) 2020 - 2021 Apple Inc. and the Swift System project authors
55
Licensed under Apache License v2.0 with Runtime Library Exception
66

77
See https://swift.org/LICENSE.txt for license information
@@ -29,12 +29,6 @@ import ucrt
2929
/// A namespace for C and platform types
3030
// @available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *)
3131
public enum CInterop {
32-
#if os(Windows)
33-
public typealias Mode = CInt
34-
#else
35-
public typealias Mode = mode_t
36-
#endif
37-
3832
/// The C `char` type
3933
public typealias Char = CChar
4034

@@ -63,4 +57,21 @@ public enum CInterop {
6357
/// on API.
6458
public typealias PlatformUnicodeEncoding = UTF8
6559
#endif
60+
61+
public typealias Mode = mode_t
62+
63+
public typealias SockAddr = sockaddr
64+
public typealias SockLen = socklen_t
65+
public typealias SAFamily = sa_family_t
66+
67+
public typealias SockAddrIn = sockaddr_in
68+
public typealias InAddr = in_addr
69+
public typealias InAddrT = in_addr_t
70+
71+
public typealias In6Addr = in6_addr
72+
73+
public typealias InPort = in_port_t
74+
75+
public typealias SockAddrIn6 = sockaddr_in6
76+
public typealias SockAddrUn = sockaddr_un
6677
}

Sources/System/Internals/Constants.swift

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
This source file is part of the Swift System open source project
33

4-
Copyright (c) 2020 Apple Inc. and the Swift System project authors
4+
Copyright (c) 2020 - 2021 Apple Inc. and the Swift System project authors
55
Licensed under Apache License v2.0 with Runtime Library Exception
66

77
See https://swift.org/LICENSE.txt for license information
@@ -782,5 +782,8 @@ internal var _IPPROTO_TCP: CInt { IPPROTO_TCP }
782782
@_alwaysEmitIntoClient
783783
internal var _SOL_SOCKET: CInt { SOL_SOCKET }
784784

785+
@_alwaysEmitIntoClient
786+
internal var _INET_ADDRSTRLEN: CInt { INET_ADDRSTRLEN }
785787

786-
788+
@_alwaysEmitIntoClient
789+
internal var _INET6_ADDRSTRLEN: CInt { INET6_ADDRSTRLEN }

Sources/System/Internals/Exports.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
This source file is part of the Swift System open source project
33

4-
Copyright (c) 2020 Apple Inc. and the Swift System project authors
4+
Copyright (c) 2020 - 2021 Apple Inc. and the Swift System project authors
55
Licensed under Apache License v2.0 with Runtime Library Exception
66

77
See https://swift.org/LICENSE.txt for license information
@@ -25,7 +25,6 @@ import ucrt
2525
#endif
2626

2727
internal typealias _COffT = off_t
28-
internal typealias _CSockLenT = socklen_t
2928

3029
// MARK: syscalls and variables
3130

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
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 FixedWidthInteger {
11+
internal var _networkOrder: Self {
12+
bigEndian
13+
}
14+
15+
internal init(_networkOrder value: Self) {
16+
self.init(bigEndian: value)
17+
}
18+
}

Sources/System/Internals/Syscalls.swift

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
This source file is part of the Swift System open source project
33

4-
Copyright (c) 2020 Apple Inc. and the Swift System project authors
4+
Copyright (c) 2020 - 2021 Apple Inc. and the Swift System project authors
55
Licensed under Apache License v2.0 with Runtime Library Exception
66

77
See https://swift.org/LICENSE.txt for license information
@@ -181,3 +181,28 @@ internal func system_setsockopt(
181181
return setsockopt(socket, level, option, value, length)
182182
}
183183

184+
internal func system_inet_ntop(
185+
_ af: CInt,
186+
_ src: UnsafeRawPointer,
187+
_ dst: UnsafeMutablePointer<CChar>,
188+
_ size: CInterop.SockLen
189+
) -> CInt { // Note: returns 0 on success, -1 on failure, unlike the original
190+
#if ENABLE_MOCKING
191+
if mockingEnabled { return _mock(af, src, dst, size) }
192+
#endif
193+
let res = inet_ntop(af, src, dst, size)
194+
if Int(bitPattern: res) == 0 { return -1 }
195+
assert(Int(bitPattern: res) == Int(bitPattern: dst))
196+
return 0
197+
}
198+
199+
internal func system_inet_pton(
200+
_ af: CInt,
201+
_ src: UnsafePointer<CChar>,
202+
_ dst: UnsafeMutableRawPointer
203+
) -> CInt {
204+
#if ENABLE_MOCKING
205+
if mockingEnabled { return _mock(af, src, dst) }
206+
#endif
207+
return inet_pton(af, src, dst)
208+
}
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
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 SocketAddress {
11+
@frozen
12+
public struct IPv4: RawRepresentable {
13+
public var rawValue: CInterop.SockAddrIn
14+
15+
public init(rawValue: CInterop.SockAddrIn) {
16+
self.rawValue = rawValue
17+
self.rawValue.sin_family = CInterop.SAFamily(Family.ipv4.rawValue)
18+
}
19+
20+
public init?(_ address: SocketAddress) {
21+
guard address.family == .ipv4 else { return nil }
22+
let value: CInterop.SockAddrIn? = address.withUnsafeBytes { buffer in
23+
guard buffer.count >= MemoryLayout<CInterop.SockAddrIn>.size else {
24+
return nil
25+
}
26+
return buffer.baseAddress!.load(as: CInterop.SockAddrIn.self)
27+
}
28+
guard let value = value else { return nil }
29+
self.rawValue = value
30+
}
31+
}
32+
}
33+
34+
extension SocketAddress {
35+
public init(_ ipv4: IPv4) {
36+
self = Swift.withUnsafeBytes(of: ipv4.rawValue) { buffer in
37+
SocketAddress(buffer)
38+
}
39+
}
40+
}
41+
42+
extension SocketAddress.IPv4 {
43+
public init(address: Address, port: Port) {
44+
rawValue = CInterop.SockAddrIn()
45+
rawValue.sin_family = CInterop.SAFamily(SocketDescriptor.Domain.ipv4.rawValue);
46+
rawValue.sin_port = port.rawValue._networkOrder
47+
rawValue.sin_addr = CInterop.InAddr(s_addr: address.rawValue._networkOrder)
48+
}
49+
50+
public init?(address: String, port: Port) {
51+
guard let address = Address(address) else { return nil }
52+
self.init(address: address, port: port)
53+
}
54+
}
55+
56+
extension SocketAddress.IPv4: Hashable {
57+
public static func ==(left: Self, right: Self) -> Bool {
58+
left.address == right.address && left.port == right.port
59+
}
60+
61+
public func hash(into hasher: inout Hasher) {
62+
hasher.combine(address)
63+
hasher.combine(port)
64+
}
65+
}
66+
67+
extension SocketAddress.IPv4: CustomStringConvertible {
68+
public var description: String {
69+
"\(address):\(port)"
70+
}
71+
}
72+
73+
extension SocketAddress.IPv4 {
74+
@frozen
75+
public struct Port: RawRepresentable, ExpressibleByIntegerLiteral, Hashable {
76+
/// The port number, in host byte order.
77+
public var rawValue: CInterop.InPort
78+
79+
public init(_ value: CInterop.InPort) {
80+
self.rawValue = value
81+
}
82+
83+
public init(rawValue: CInterop.InPort) {
84+
self.init(rawValue)
85+
}
86+
87+
public init(integerLiteral value: CInterop.InPort) {
88+
self.init(value)
89+
}
90+
}
91+
92+
public var port: Port {
93+
get { Port(CInterop.InPort(_networkOrder: rawValue.sin_port)) }
94+
set { rawValue.sin_port = newValue.rawValue._networkOrder }
95+
}
96+
}
97+
98+
extension SocketAddress.IPv4.Port: CustomStringConvertible {
99+
public var description: String {
100+
rawValue.description
101+
}
102+
}
103+
104+
extension SocketAddress.IPv4 {
105+
@frozen
106+
public struct Address: RawRepresentable, Hashable {
107+
/// The raw internet address value, in host byte order.
108+
public var rawValue: CInterop.InAddrT
109+
110+
public init(rawValue: CInterop.InAddrT) {
111+
self.rawValue = rawValue
112+
}
113+
}
114+
115+
public var address: Address {
116+
get {
117+
let value = CInterop.InAddrT(_networkOrder: rawValue.sin_addr.s_addr)
118+
return Address(rawValue: value)
119+
}
120+
set {
121+
rawValue.sin_addr.s_addr = newValue.rawValue._networkOrder
122+
}
123+
}
124+
}
125+
126+
extension SocketAddress.IPv4.Address: CustomStringConvertible {
127+
public var description: String {
128+
_inet_ntop()
129+
}
130+
131+
internal func _inet_ntop() -> String {
132+
let addr = CInterop.InAddr(s_addr: rawValue._networkOrder)
133+
return withUnsafeBytes(of: addr) { src in
134+
String(_unsafeUninitializedCapacity: Int(_INET_ADDRSTRLEN)) { dst in
135+
dst.baseAddress!.withMemoryRebound(
136+
to: CChar.self,
137+
capacity: Int(_INET_ADDRSTRLEN)
138+
) { dst in
139+
let res = system_inet_ntop(
140+
_PF_INET,
141+
src.baseAddress!,
142+
dst,
143+
CInterop.SockLen(_INET_ADDRSTRLEN))
144+
if res == -1 {
145+
let errno = Errno.current
146+
fatalError("Failed to convert IPv4 address to string: \(errno)")
147+
}
148+
let length = system_strlen(dst)
149+
assert(length <= _INET_ADDRSTRLEN)
150+
return length
151+
}
152+
}
153+
}
154+
}
155+
}
156+
157+
extension SocketAddress.IPv4.Address: LosslessStringConvertible {
158+
public init?(_ description: String) {
159+
guard let value = Self._inet_pton(description) else { return nil }
160+
self = value
161+
}
162+
163+
internal static func _inet_pton(_ string: String) -> Self? {
164+
string.withCString { ptr in
165+
var addr = CInterop.InAddr()
166+
let res = system_inet_pton(_PF_INET, ptr, &addr)
167+
guard res == 1 else { return nil }
168+
return Self(rawValue: CInterop.InAddrT(_networkOrder: addr.s_addr))
169+
}
170+
}
171+
}

0 commit comments

Comments
 (0)