Skip to content

Commit 4f8949d

Browse files
committed
Add system client as option for transport type
1 parent f87e6e4 commit 4f8949d

File tree

6 files changed

+141
-0
lines changed

6 files changed

+141
-0
lines changed
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// DNSKit
2+
// Copyright (C) Ian Spence and other DNSKit Contributors
3+
//
4+
// This program is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Lesser General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// This program is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Lesser General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Lesser General Public License
15+
// along with this program. If not, see <https://www.gnu.org/licenses/>.
16+
17+
import Foundation
18+
import Network
19+
20+
internal final class SystemClient: IClient {
21+
private let dispatchQueue: DispatchQueue
22+
23+
init(address: String, transportOptions: TransportOptions) throws {
24+
self.dispatchQueue = DispatchQueue(label: "io.ecn.dnskit.systemclient", qos: .userInitiated)
25+
}
26+
27+
func send(message: Message, complete: @escaping @Sendable (Result<Response, DNSKitError>) -> Void) {
28+
self.dispatchQueue.async {
29+
do {
30+
let timer = Timer.start()
31+
let reply = try SystemResolver.query(question: message.questions[0], dnssecOk: message.dnssecOK)
32+
complete(.success(.init(message: reply, serverAddress: nil, elapsed: timer.stop())))
33+
} catch {
34+
if let error = error as? DNSKitError {
35+
complete(.failure(error))
36+
} else {
37+
complete(.failure(.internalError("\(error)")))
38+
}
39+
}
40+
}
41+
}
42+
43+
func authenticate(message: Message, complete: @escaping @Sendable (Result<DNSSECResult, any Error>) -> Void) {
44+
self.dispatchQueue.async {
45+
do {
46+
let result = try SystemResolver.authenticate(message: message)
47+
complete(.success(result))
48+
} catch {
49+
complete(.failure(error))
50+
}
51+
}
52+
}
53+
}

Sources/DNSKit/Docs.docc/index.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ DNSKit provides a wide array of support across the DNS ecosystem, including:
66

77
- Native support for most common DNS record types.
88
- Support for DNS over HTTPS, DNS over TLS, DNS over Quic, and traditional DNS using TCP or UDP.
9+
- Bridge for using the system resolver in Swift (libresolv bridge)
910
- Full DNSSEC signature validation & chain trust establishment.
1011
- WHOIS client for domain information.
1112

@@ -22,3 +23,7 @@ Use the ``WHOISClient/lookup(_:)`` class to perform WHOIS lookups
2223

2324
Re-using the same query you used to perform your query, call ``Query/authenticate(message:)``. Be sure that ``QueryOptions/dnssecRequested``
2425
was set to true in your original query.
26+
27+
### System Resolver
28+
29+
To utilize the System's resolver, use the ``SystemResolver/query(question:dnssecOk:)`` method.

Sources/DNSKit/Query.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,8 @@ public final class Query: Sendable {
148148
} else {
149149
fatalError("Attempted to use quic on unsupported target")
150150
}
151+
case .System:
152+
clients.append(try SystemClient(address: serverAddress, transportOptions: transportOptions))
151153
}
152154
}
153155

@@ -264,6 +266,8 @@ public final class Query: Sendable {
264266
} else {
265267
fatalError("Attempted to use quic on unsupported target")
266268
}
269+
case .System:
270+
_ = try SystemClient(address: serverAddress, transportOptions: TransportOptions())
267271
}
268272
}
269273
return nil

Sources/DNSKit/Types.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,11 @@ public enum TransportType: String, Codable, CaseIterable, Sendable {
9595
///
9696
/// Expects the server address to be an IPv4 or IPv6 address with optional port, defaulting to 853.
9797
case QUIC = "quic"
98+
/// Special transport type to indicate to use the system's DNS resolver.
99+
///
100+
/// When using this option in ``Query/init(transportType:transportOptions:serverAddresses:recordType:name:queryOptions:)``
101+
/// the `serverAddresses` and `transportOptions` values are ingored.
102+
case System = "system"
98103

99104
public func string() -> String {
100105
return String(describing: self)

Tests/DNSKitTests/ClientTests.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ final class ClientTests {
5252
} else {
5353
fatalError("Attempted to use Quic client on unsupported platform")
5454
}
55+
case .System:
56+
self.client = try SystemClient(address: serverAddress, transportOptions: transportOptions)
5557
}
5658
}
5759

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// DNSKit
2+
// Copyright (C) Ian Spence and other DNSKit Contributors
3+
//
4+
// This program is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Lesser General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// This program is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Lesser General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Lesser General Public License
15+
// along with this program. If not, see <https://www.gnu.org/licenses/>.
16+
17+
import XCTest
18+
@testable import DNSKit
19+
20+
final class SystemClientTests: XCTestCase, IClientTests {
21+
func testQuery() async throws {
22+
try await ClientTests(transportType: .System, serverAddress: "").testQuery()
23+
}
24+
25+
func testPTRQuery() async throws {
26+
try await ClientTests(transportType: .System, serverAddress: "").testPTRQuery()
27+
}
28+
29+
func testQueryNXDOMAIN() async throws {
30+
try await ClientTests(transportType: .System, serverAddress: "").testQueryNXDOMAIN()
31+
}
32+
33+
func testAuthenticateMessageA() async throws {
34+
try await ClientTests(transportType: .System, serverAddress: "").testAuthenticateMessageA()
35+
}
36+
37+
func testAuthenticateMessageSOA() async throws {
38+
try await ClientTests(transportType: .System, serverAddress: "").testAuthenticateMessageSOA()
39+
}
40+
41+
func testAuthenticateRoot() async throws {
42+
try await ClientTests(transportType: .System, serverAddress: "").testAuthenticateRoot()
43+
}
44+
45+
func testAuthenticateTLD() async throws {
46+
try await ClientTests(transportType: .System, serverAddress: "").testAuthenticateTLD()
47+
}
48+
49+
func testAuthenticateCNAME() async throws {
50+
try await ClientTests(transportType: .System, serverAddress: "").testAuthenticateCNAME()
51+
}
52+
53+
func testLocalControl() async throws {
54+
// Test does not apply
55+
}
56+
57+
func testLocalRandomData() async throws {
58+
// Test does not apply
59+
}
60+
61+
func testLocalLengthOver() async throws {
62+
// Test does not apply
63+
}
64+
65+
func testLocalLengthUnder() async throws {
66+
// Test does not apply
67+
}
68+
69+
func testLocalAQueryInvalidAddress() async throws {
70+
// Test does not apply
71+
}
72+
}

0 commit comments

Comments
 (0)