Skip to content

Commit 0d90e59

Browse files
committed
Use Sendable DNS types.
- Closes #1268. - The types we were using weren't very usable with Swift 6 structured concurrency. - Use notImplemented instead of formatError for unknown record types. - Use pure actor for LocalhostDNSHandler now that we have sendable types.
1 parent 79ef846 commit 0d90e59

12 files changed

Lines changed: 51 additions & 163 deletions

Package.resolved

Lines changed: 1 addition & 19 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Package.swift

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@ let package = Package(
4747
.library(name: "TerminalProgress", targets: ["TerminalProgress"]),
4848
],
4949
dependencies: [
50-
.package(url: "https://github.com/Bouke/DNS.git", from: "1.2.0"),
5150
.package(url: "https://github.com/apple/containerization.git", exact: Version(stringLiteral: scVersion)),
5251
.package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.3.0"),
5352
.package(url: "https://github.com/apple/swift-collections.git", from: "1.2.0"),
@@ -56,7 +55,6 @@ let package = Package(
5655
.package(url: "https://github.com/apple/swift-protobuf.git", from: "1.29.0"),
5756
.package(url: "https://github.com/apple/swift-system.git", from: "1.4.0"),
5857
.package(url: "https://github.com/grpc/grpc-swift.git", from: "1.26.0"),
59-
.package(url: "https://github.com/orlandos-nl/DNSClient.git", from: "2.4.1"),
6058
.package(url: "https://github.com/swift-server/async-http-client.git", from: "1.20.1"),
6159
.package(url: "https://github.com/swiftlang/swift-docc-plugin.git", from: "1.1.0"),
6260
],
@@ -427,17 +425,15 @@ let package = Package(
427425
dependencies: [
428426
.product(name: "NIOCore", package: "swift-nio"),
429427
.product(name: "NIOPosix", package: "swift-nio"),
430-
.product(name: "DNSClient", package: "DNSClient"),
431-
.product(name: "DNS", package: "DNS"),
432428
.product(name: "Logging", package: "swift-log"),
429+
.product(name: "ContainerizationExtras", package: "containerization"),
433430
.product(name: "ContainerizationOS", package: "containerization"),
434431
]
435432
),
436433
.testTarget(
437434
name: "DNSServerTests",
438435
dependencies: [
439-
.product(name: "DNS", package: "DNS"),
440-
"DNSServer",
436+
"DNSServer"
441437
]
442438
),
443439
.testTarget(

Sources/DNSServer/Handlers/HostTableResolver.swift

Lines changed: 5 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,14 @@
1414
// limitations under the License.
1515
//===----------------------------------------------------------------------===//
1616

17-
import DNS
17+
import ContainerizationExtras
1818

1919
/// Handler that uses table lookup to resolve hostnames.
2020
public struct HostTableResolver: DNSHandler {
21-
public let hosts4: [String: IPv4]
21+
public let hosts4: [String: IPv4Address]
2222
private let ttl: UInt32
2323

24-
public init(hosts4: [String: IPv4], ttl: UInt32 = 300) {
24+
public init(hosts4: [String: IPv4Address], ttl: UInt32 = 300) {
2525
self.hosts4 = hosts4
2626
self.ttl = ttl
2727
}
@@ -48,28 +48,11 @@ public struct HostTableResolver: DNSHandler {
4848
}
4949
// If hostname doesn't exist, return nil which will become NXDOMAIN
5050
return nil
51-
case ResourceRecordType.nameServer,
52-
ResourceRecordType.alias,
53-
ResourceRecordType.startOfAuthority,
54-
ResourceRecordType.pointer,
55-
ResourceRecordType.mailExchange,
56-
ResourceRecordType.text,
57-
ResourceRecordType.service,
58-
ResourceRecordType.incrementalZoneTransfer,
59-
ResourceRecordType.standardZoneTransfer,
60-
ResourceRecordType.all:
61-
return Message(
62-
id: query.id,
63-
type: .response,
64-
returnCode: .notImplemented,
65-
questions: query.questions,
66-
answers: []
67-
)
6851
default:
6952
return Message(
7053
id: query.id,
7154
type: .response,
72-
returnCode: .formatError,
55+
returnCode: .notImplemented,
7356
questions: query.questions,
7457
answers: []
7558
)
@@ -93,6 +76,6 @@ public struct HostTableResolver: DNSHandler {
9376
return nil
9477
}
9578

96-
return HostRecord<IPv4>(name: question.name, ttl: ttl, ip: ip)
79+
return HostRecord<IPv4Address>(name: question.name, ttl: ttl, ip: ip)
9780
}
9881
}

Sources/DNSServer/Handlers/NxDomainResolver.swift

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,6 @@
1414
// limitations under the License.
1515
//===----------------------------------------------------------------------===//
1616

17-
import DNS
18-
1917
/// Handler that returns NXDOMAIN for all hostnames.
2018
public struct NxDomainResolver: DNSHandler {
2119
private let ttl: UInt32
@@ -35,29 +33,11 @@ public struct NxDomainResolver: DNSHandler {
3533
questions: query.questions,
3634
answers: []
3735
)
38-
case ResourceRecordType.nameServer,
39-
ResourceRecordType.alias,
40-
ResourceRecordType.startOfAuthority,
41-
ResourceRecordType.pointer,
42-
ResourceRecordType.mailExchange,
43-
ResourceRecordType.text,
44-
ResourceRecordType.host6,
45-
ResourceRecordType.service,
46-
ResourceRecordType.incrementalZoneTransfer,
47-
ResourceRecordType.standardZoneTransfer,
48-
ResourceRecordType.all:
49-
return Message(
50-
id: query.id,
51-
type: .response,
52-
returnCode: .notImplemented,
53-
questions: query.questions,
54-
answers: []
55-
)
5636
default:
5737
return Message(
5838
id: query.id,
5939
type: .response,
60-
returnCode: .formatError,
40+
returnCode: .notImplemented,
6141
questions: query.questions,
6242
answers: []
6343
)

Sources/DNSServer/Types.swift

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,8 @@
1414
// limitations under the License.
1515
//===----------------------------------------------------------------------===//
1616

17-
import DNS
1817
import Foundation
1918

20-
public typealias Message = DNS.Message
21-
public typealias ResourceRecord = DNS.ResourceRecord
22-
public typealias HostRecord = DNS.HostRecord
23-
public typealias IPv4 = DNS.IPv4
24-
public typealias IPv6 = DNS.IPv6
25-
public typealias ReturnCode = DNS.ReturnCode
26-
2719
public enum DNSResolverError: Swift.Error, CustomStringConvertible {
2820
case serverError(_ msg: String)
2921
case invalidHandlerSpec(_ spec: String)

Sources/Helpers/APIServer/ContainerDNSHandler.swift

Lines changed: 6 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
//===----------------------------------------------------------------------===//
1616

1717
import ContainerAPIService
18-
import DNS
18+
import ContainerizationExtras
1919
import DNSServer
2020

2121
/// Handler that uses table lookup to resolve hostnames.
@@ -50,28 +50,11 @@ struct ContainerDNSHandler: DNSHandler {
5050
)
5151
}
5252
record = result.record
53-
case ResourceRecordType.nameServer,
54-
ResourceRecordType.alias,
55-
ResourceRecordType.startOfAuthority,
56-
ResourceRecordType.pointer,
57-
ResourceRecordType.mailExchange,
58-
ResourceRecordType.text,
59-
ResourceRecordType.service,
60-
ResourceRecordType.incrementalZoneTransfer,
61-
ResourceRecordType.standardZoneTransfer,
62-
ResourceRecordType.all:
63-
return Message(
64-
id: query.id,
65-
type: .response,
66-
returnCode: .notImplemented,
67-
questions: query.questions,
68-
answers: []
69-
)
7053
default:
7154
return Message(
7255
id: query.id,
7356
type: .response,
74-
returnCode: .formatError,
57+
returnCode: .notImplemented,
7558
questions: query.questions,
7659
answers: []
7760
)
@@ -95,11 +78,11 @@ struct ContainerDNSHandler: DNSHandler {
9578
return nil
9679
}
9780
let ipv4 = ipAllocation.ipv4Address.address.description
98-
guard let ip = IPv4(ipv4) else {
81+
guard let ip = try? IPv4Address(ipv4) else {
9982
throw DNSResolverError.serverError("failed to parse IP address: \(ipv4)")
10083
}
10184

102-
return HostRecord<IPv4>(name: question.name, ttl: ttl, ip: ip)
85+
return HostRecord<IPv4Address>(name: question.name, ttl: ttl, ip: ip)
10386
}
10487

10588
private func answerHost6(question: Question) async throws -> (record: ResourceRecord?, hostnameExists: Bool) {
@@ -110,10 +93,10 @@ struct ContainerDNSHandler: DNSHandler {
11093
return (nil, true)
11194
}
11295
let ipv6 = ipv6Address.address.description
113-
guard let ip = IPv6(ipv6) else {
96+
guard let ip = try? IPv6Address(ipv6) else {
11497
throw DNSResolverError.serverError("failed to parse IPv6 address: \(ipv6)")
11598
}
11699

117-
return (HostRecord<IPv6>(name: question.name, ttl: ttl, ip: ip), true)
100+
return (HostRecord<IPv6Address>(name: question.name, ttl: ttl, ip: ip), true)
118101
}
119102
}

Sources/Helpers/APIServer/LocalhostDNSHandler.swift

Lines changed: 15 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import ContainerAPIClient
1818
import ContainerOS
1919
import ContainerPersistence
2020
import ContainerizationError
21-
import DNS
21+
import ContainerizationExtras
2222
import DNSServer
2323
import Foundation
2424
import Logging
@@ -28,42 +28,41 @@ actor LocalhostDNSHandler: DNSHandler {
2828
private let ttl: UInt32
2929
private let watcher: DirectoryWatcher
3030

31-
private let dns: Mutex<[String: IPv4]>
31+
private var dns: [String: IPv4Address]
3232

3333
public init(resolversURL: URL = HostDNSResolver.defaultConfigPath, ttl: UInt32 = 5, log: Logger) {
3434
self.ttl = ttl
3535

3636
self.watcher = DirectoryWatcher(directoryURL: resolversURL, log: log)
37-
self.dns = Mutex([:])
37+
self.dns = [:]
3838
}
3939

4040
public func monitorResolvers() async {
41-
await self.watcher.startWatching { fileURLs in
42-
var dns: [String: String] = [:]
41+
await self.watcher.startWatching { [weak self] fileURLs in
42+
var dns: [String: IPv4Address] = [:]
4343
let regex = try Regex(HostDNSResolver.localhostOptionsRegex)
4444

4545
for file in fileURLs.filter({ $0.lastPathComponent.starts(with: HostDNSResolver.containerizationPrefix) }) {
4646
let content = try String(contentsOf: file, encoding: .utf8)
4747

4848
if let match = content.firstMatch(of: regex),
49-
let ipv4 = (match[1].substring.map { String($0) })
49+
let ipv4 = (match[1].substring.map { try? IPv4Address(String($0)) })
5050
{
5151
let name = String(file.lastPathComponent.dropFirst(HostDNSResolver.containerizationPrefix.count))
5252
dns[name + "."] = ipv4
5353
}
5454
}
55-
self.dns.withLock { $0 = dns.compactMapValues { IPv4($0) } }
55+
Task { await self?.updateDNS(dns) }
5656
}
5757
}
5858

59-
nonisolated public func answer(query: Message) async throws -> Message? {
59+
public func answer(query: Message) async throws -> Message? {
6060
let question = query.questions[0]
6161
var record: ResourceRecord?
6262
switch question.type {
6363
case ResourceRecordType.host:
64-
let dns = dns.withLock { $0 }
6564
if let ip = dns[question.name] {
66-
record = HostRecord<IPv4>(name: question.name, ttl: ttl, ip: ip)
65+
record = HostRecord<IPv4Address>(name: question.name, ttl: ttl, ip: ip)
6766
}
6867
case ResourceRecordType.host6:
6968
return Message(
@@ -73,28 +72,11 @@ actor LocalhostDNSHandler: DNSHandler {
7372
questions: query.questions,
7473
answers: []
7574
)
76-
case ResourceRecordType.nameServer,
77-
ResourceRecordType.alias,
78-
ResourceRecordType.startOfAuthority,
79-
ResourceRecordType.pointer,
80-
ResourceRecordType.mailExchange,
81-
ResourceRecordType.text,
82-
ResourceRecordType.service,
83-
ResourceRecordType.incrementalZoneTransfer,
84-
ResourceRecordType.standardZoneTransfer,
85-
ResourceRecordType.all:
86-
return Message(
87-
id: query.id,
88-
type: .response,
89-
returnCode: .notImplemented,
90-
questions: query.questions,
91-
answers: []
92-
)
9375
default:
9476
return Message(
9577
id: query.id,
9678
type: .response,
97-
returnCode: .formatError,
79+
returnCode: .notImplemented,
9880
questions: query.questions,
9981
answers: []
10082
)
@@ -112,4 +94,9 @@ actor LocalhostDNSHandler: DNSHandler {
11294
answers: [record]
11395
)
11496
}
97+
98+
private func updateDNS(_ dns: [String: IPv4Address]) {
99+
self.dns = dns
100+
}
101+
115102
}

Tests/DNSServerTests/CompositeResolverTest.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
// limitations under the License.
1515
//===----------------------------------------------------------------------===//
1616

17-
import DNS
17+
import ContainerizationExtras
1818
import Testing
1919

2020
@testable import DNSServer
@@ -36,8 +36,8 @@ struct CompositeResolverTest {
3636
#expect(.noError == fooResponse?.returnCode)
3737
#expect(1 == fooResponse?.id)
3838
#expect(1 == fooResponse?.answers.count)
39-
let fooAnswer = fooResponse?.answers[0] as? HostRecord<IPv4>
40-
#expect(IPv4("1.2.3.4") == fooAnswer?.ip)
39+
let fooAnswer = fooResponse?.answers[0] as? HostRecord<IPv4Address>
40+
#expect(try IPv4Address("1.2.3.4") == fooAnswer?.ip)
4141

4242
let barQuery = Message(
4343
id: UInt16(1),
@@ -50,8 +50,8 @@ struct CompositeResolverTest {
5050
#expect(.noError == barResponse?.returnCode)
5151
#expect(1 == barResponse?.id)
5252
#expect(1 == barResponse?.answers.count)
53-
let barAnswer = barResponse?.answers[0] as? HostRecord<IPv4>
54-
#expect(IPv4("5.6.7.8") == barAnswer?.ip)
53+
let barAnswer = barResponse?.answers[0] as? HostRecord<IPv4Address>
54+
#expect(try IPv4Address("5.6.7.8") == barAnswer?.ip)
5555

5656
let otherQuery = Message(
5757
id: UInt16(1),

0 commit comments

Comments
 (0)