-
Notifications
You must be signed in to change notification settings - Fork 745
Use Sendable DNS types. #1269
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use Sendable DNS types. #1269
Changes from 3 commits
4de2a15
f716800
c778d70
a1b6325
ea1e492
a108c64
5ac4c31
f13d26e
e8a75b5
04a628a
2cf5b1d
3fc5044
a9d42c5
600183b
0046f87
044ca99
5547f78
a5429fd
3e6a760
6d4ac74
c7d5d27
7cb521b
282440a
69ad281
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -14,14 +14,23 @@ | |
| // limitations under the License. | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| import DNS | ||
| import ContainerizationExtras | ||
|
|
||
| /// Handler that uses table lookup to resolve hostnames. | ||
| /// | ||
| /// All keys in `hosts4` must be canonical DNS names — fully-qualified with a | ||
| /// trailing dot (e.g. `"example.com."`). This matches the canonical form used | ||
| /// by `Question.name` when decoded from the wire. | ||
| public struct HostTableResolver: DNSHandler { | ||
| public let hosts4: [String: IPv4] | ||
| public let hosts4: [String: IPv4Address] | ||
| private let ttl: UInt32 | ||
|
|
||
| public init(hosts4: [String: IPv4], ttl: UInt32 = 300) { | ||
| /// Creates a resolver backed by a static IPv4 host table. | ||
| /// | ||
| /// - Parameter hosts4: A dictionary mapping fully-qualified domain names (with trailing dot) | ||
| /// to IPv4 addresses. Keys without a trailing dot will not match wire-decoded queries. | ||
| /// - Parameter ttl: The TTL in seconds to set on answer records. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nitpick: The TTL in seconds to set on answer records. Defaults to 300. |
||
| public init(hosts4: [String: IPv4Address], ttl: UInt32 = 300) { | ||
| self.hosts4 = hosts4 | ||
| self.ttl = ttl | ||
| } | ||
|
|
@@ -48,28 +57,11 @@ public struct HostTableResolver: DNSHandler { | |
| } | ||
| // If hostname doesn't exist, return nil which will become NXDOMAIN | ||
| return nil | ||
| case ResourceRecordType.nameServer, | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. summary: all unhandled question types in handlers should return |
||
| ResourceRecordType.alias, | ||
| ResourceRecordType.startOfAuthority, | ||
| ResourceRecordType.pointer, | ||
| ResourceRecordType.mailExchange, | ||
| ResourceRecordType.text, | ||
| ResourceRecordType.service, | ||
| ResourceRecordType.incrementalZoneTransfer, | ||
| ResourceRecordType.standardZoneTransfer, | ||
| ResourceRecordType.all: | ||
| return Message( | ||
| id: query.id, | ||
| type: .response, | ||
| returnCode: .notImplemented, | ||
| questions: query.questions, | ||
| answers: [] | ||
| ) | ||
| default: | ||
| return Message( | ||
| id: query.id, | ||
| type: .response, | ||
| returnCode: .formatError, | ||
| returnCode: .notImplemented, | ||
| questions: query.questions, | ||
| answers: [] | ||
| ) | ||
|
|
@@ -93,6 +85,6 @@ public struct HostTableResolver: DNSHandler { | |
| return nil | ||
| } | ||
|
|
||
| return HostRecord<IPv4>(name: question.name, ttl: ttl, ip: ip) | ||
| return HostRecord<IPv4Address>(name: question.name, ttl: ttl, ip: ip) | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,100 @@ | ||
| //===----------------------------------------------------------------------===// | ||
| // Copyright © 2026 Apple Inc. and the container project authors. | ||
| // | ||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||
| // you may not use this file except in compliance with the License. | ||
| // You may obtain a copy of the License at | ||
| // | ||
| // https://www.apache.org/licenses/LICENSE-2.0 | ||
| // | ||
| // Unless required by applicable law or agreed to in writing, software | ||
| // distributed under the License is distributed on an "AS IS" BASIS, | ||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| // See the License for the specific language governing permissions and | ||
| // limitations under the License. | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| import Foundation | ||
|
|
||
| // TODO: Look for a way that we can make use of the | ||
| // bit-fiddling types from ContainerizationExtras, instead | ||
| // of copying them here. | ||
|
|
||
| /// Errors that can occur during DNS message serialization/deserialization. | ||
| public enum DNSBindError: Error, CustomStringConvertible { | ||
| case marshalFailure(type: String, field: String) | ||
| case unmarshalFailure(type: String, field: String) | ||
|
|
||
| public var description: String { | ||
| switch self { | ||
| case .marshalFailure(let type, let field): | ||
| return "failed to marshal \(type).\(field)" | ||
| case .unmarshalFailure(let type, let field): | ||
| return "failed to unmarshal \(type).\(field)" | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /// Protocol for types that can be serialized to/from a byte buffer. | ||
| protocol Bindable: Sendable { | ||
| /// The fixed size of this type in bytes, if applicable. | ||
| static var size: Int { get } | ||
|
|
||
| /// Serialize this value into the buffer at the given offset. | ||
| /// - Returns: The new offset after writing. | ||
| func appendBuffer(_ buffer: inout [UInt8], offset: Int) throws -> Int | ||
|
|
||
| /// Deserialize this value from the buffer at the given offset. | ||
| /// - Returns: The new offset after reading. | ||
| mutating func bindBuffer(_ buffer: inout [UInt8], offset: Int) throws -> Int | ||
| } | ||
|
|
||
| extension [UInt8] { | ||
| /// Copy a value into the buffer at the given offset. | ||
| /// - Returns: The new offset after writing, or nil if the buffer is too small. | ||
| package mutating func copyIn<T>(as type: T.Type, value: T, offset: Int = 0) -> Int? { | ||
| let size = MemoryLayout<T>.size | ||
| guard self.count >= size + offset else { | ||
| return nil | ||
| } | ||
| return self.withUnsafeMutableBytes { | ||
| $0.baseAddress?.advanced(by: offset).assumingMemoryBound(to: T.self).pointee = value | ||
| return offset + size | ||
| } | ||
| } | ||
|
|
||
| /// Copy a value out of the buffer at the given offset. | ||
| /// - Returns: A tuple of (new offset, value), or nil if the buffer is too small. | ||
| package func copyOut<T>(as type: T.Type, offset: Int = 0) -> (Int, T)? { | ||
| let size = MemoryLayout<T>.size | ||
| guard self.count >= size + offset else { | ||
| return nil | ||
| } | ||
| return self.withUnsafeBytes { | ||
| guard let value = $0.baseAddress?.advanced(by: offset).assumingMemoryBound(to: T.self).pointee else { | ||
| return nil | ||
| } | ||
| return (offset + size, value) | ||
| } | ||
| } | ||
|
|
||
| /// Copy a byte array into the buffer at the given offset. | ||
| /// - Returns: The new offset after writing, or nil if the buffer is too small. | ||
| package mutating func copyIn(buffer: [UInt8], offset: Int = 0) -> Int? { | ||
| guard offset + buffer.count <= self.count else { | ||
| return nil | ||
| } | ||
| self[offset..<offset + buffer.count] = buffer[0..<buffer.count] | ||
| return offset + buffer.count | ||
| } | ||
|
|
||
| /// Copy bytes out of the buffer into another buffer. | ||
| /// - Returns: The new offset after reading, or nil if the buffer is too small. | ||
| package func copyOut(buffer: inout [UInt8], offset: Int = 0) -> Int? { | ||
| guard offset + buffer.count <= self.count else { | ||
| return nil | ||
| } | ||
| buffer[0..<buffer.count] = self[offset..<offset + buffer.count] | ||
| return offset + buffer.count | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍