Merge commit from fork

* Use DNSName for input

* Use pqdn instead of fqdn.

* Update Sources/DNSServer/Records/DNSName.swift

Co-authored-by: J Logan <john_logan@apple.com>

* Simplify description

* Add test

* Make fmt

---------

Co-authored-by: jwhur <jaewon_hur@apple.com>
Co-authored-by: jwhur <57657645+JaewonHur@users.noreply.github.com>
Co-authored-by: J Logan <john_logan@apple.com>
This commit is contained in:
Mike Drob
2026-04-30 09:33:26 -05:00
committed by GitHub
co-authored by J Logan jwhur jwhur
parent fd2de35440
commit f9899013fd
10 changed files with 66 additions and 34 deletions
@@ -19,6 +19,7 @@ import ContainerAPIClient
import ContainerPersistence
import ContainerizationError
import ContainerizationExtras
import DNSServer
import Foundation
extension Application {
@@ -48,6 +49,10 @@ extension Application {
}
}
guard let domainName = try? DNSName(domainName) else {
throw ContainerizationError(.invalidArgument, message: "invalid domain name: \(domainName)")
}
let resolver: HostDNSResolver = HostDNSResolver()
do {
try resolver.createDomain(name: domainName, localhost: localhostIP)
@@ -67,7 +72,7 @@ extension Application {
throw error
}
}
print(domainName)
print(domainName.pqdn)
if localhostIP != nil {
do {
@@ -18,6 +18,7 @@ import ArgumentParser
import ContainerAPIClient
import ContainerizationError
import ContainerizationExtras
import DNSServer
import Foundation
extension Application {
@@ -37,6 +38,10 @@ extension Application {
public init() {}
public func run() async throws {
guard let domainName = try? DNSName(domainName) else {
throw ContainerizationError(.invalidArgument, message: "invalid domain name: \(domainName)")
}
let resolver = HostDNSResolver()
var localhostIP: IPAddress?
do {
@@ -52,7 +57,7 @@ extension Application {
}
guard let localhostIP else {
print(domainName)
print(domainName.pqdn)
return
}
@@ -66,7 +71,7 @@ extension Application {
} catch {
throw ContainerizationError(.invalidState, message: "failed loading pf rules")
}
print(domainName)
print(domainName.pqdn)
}
}
}
@@ -16,6 +16,7 @@
import ArgumentParser
import ContainerAPIClient
import DNSServer
import Foundation
extension Application {
@@ -42,7 +43,7 @@ extension Application {
let domains = resolver.listDomains()
try Output.render(
json: domains,
json: domains.map { $0.pqdn },
display: domains.map { PrintableDomain($0) },
format: format, quiet: quiet
)
@@ -51,9 +52,9 @@ extension Application {
}
private struct PrintableDomain: ListDisplayable {
let domain: String
let domain: DNSName
init(_ domain: String) {
init(_ domain: DNSName) {
self.domain = domain
}
@@ -62,10 +63,10 @@ private struct PrintableDomain: ListDisplayable {
}
var tableRow: [String] {
[domain]
[domain.pqdn]
}
var quietValue: String {
domain
domain.pqdn
}
}
+6 -1
View File
@@ -91,7 +91,12 @@ public struct DNSName: Sendable, Hashable, CustomStringConvertible {
/// The fully-qualified domain name with trailing dot.
public var description: String {
labels.isEmpty ? "." : labels.joined(separator: ".") + "."
labels.joined(separator: ".") + "."
}
/// The partially-qualified domain name, which is the FQDN less the trailing dot.
public var pqdn: String {
labels.joined(separator: ".")
}
/// Serialize this name into the buffer at the given offset.
@@ -16,6 +16,7 @@
import ContainerizationError
import ContainerizationExtras
import DNSServer
import Foundation
/// Functions for managing local DNS domains for containers.
@@ -33,7 +34,9 @@ public struct HostDNSResolver {
}
/// Creates a DNS resolver configuration file for domain resolved by the application.
public func createDomain(name: String, localhost: IPAddress? = nil) throws {
public func createDomain(name: DNSName, localhost: IPAddress? = nil) throws {
let name = name.pqdn
let path = self.configURL.appending(path: "\(Self.containerizationPrefix)\(name)").path
let fm: FileManager = FileManager.default
@@ -67,7 +70,9 @@ public struct HostDNSResolver {
}
/// Removes a DNS resolver configuration file for domain resolved by the application.
public func deleteDomain(name: String) throws -> IPAddress? {
public func deleteDomain(name: DNSName) throws -> IPAddress? {
let name = name.pqdn
let path = self.configURL.appending(path: "\(Self.containerizationPrefix)\(name)").path
let fm = FileManager.default
guard fm.fileExists(atPath: path) else {
@@ -90,7 +95,7 @@ public struct HostDNSResolver {
}
/// Lists application-created local DNS domains.
public func listDomains() -> [String] {
public func listDomains() -> [DNSName] {
let fm: FileManager = FileManager.default
guard
let resolverPaths = try? fm.contentsOfDirectory(
@@ -105,7 +110,7 @@ public struct HostDNSResolver {
resolverPaths
.filter { $0.lastPathComponent.starts(with: Self.containerizationPrefix) }
.compactMap { try? getDomainFromResolver(url: $0) }
.sorted()
.sorted { a, b in a.pqdn < b.pqdn }
}
/// Reinitializes the macOS DNS daemon.
@@ -128,7 +133,7 @@ public struct HostDNSResolver {
}
}
private func getDomainFromResolver(url: URL) throws -> String? {
private func getDomainFromResolver(url: URL) throws -> DNSName? {
let text = try String(contentsOf: url, encoding: .utf8)
for line in text.components(separatedBy: .newlines) {
let trimmed = line.trimmingCharacters(in: .whitespaces)
@@ -140,7 +145,7 @@ public struct HostDNSResolver {
continue
}
return String(components[1])
return try? DNSName(String(components[1]))
}
return nil
@@ -16,6 +16,7 @@
import ContainerizationError
import ContainerizationExtras
import DNSServer
import Foundation
public struct PacketFilter {
@@ -31,7 +32,7 @@ public struct PacketFilter {
self.anchorsURL = anchorsURL
}
public func createRedirectRule(from: IPAddress, to: IPAddress, domain: String) throws {
public func createRedirectRule(from: IPAddress, to: IPAddress, domain: DNSName) throws {
guard type(of: from) == type(of: to) else {
throw ContainerizationError(.invalidArgument, message: "protocol does not match: \(from) vs. \(to)")
}
@@ -45,7 +46,7 @@ public struct PacketFilter {
case .v4: inet = "inet"
case .v6: inet = "inet6"
}
let redirectRule = "rdr \(inet) from any to \(from.description) -> \(to.description) # \(domain)"
let redirectRule = "rdr \(inet) from any to \(from.description) -> \(to.description) # \(domain.pqdn)"
var content = ""
if fm.fileExists(atPath: anchorURL.path) {
@@ -62,7 +63,7 @@ public struct PacketFilter {
try lines.joined(separator: "\n").write(toFile: anchorURL.path, atomically: true, encoding: .utf8)
}
public func removeRedirectRule(from: IPAddress, to: IPAddress, domain: String) throws {
public func removeRedirectRule(from: IPAddress, to: IPAddress, domain: DNSName) throws {
guard type(of: from) == type(of: to) else {
throw ContainerizationError(.invalidArgument, message: "protocol does not match: \(from) vs. \(to)")
}
@@ -76,7 +77,7 @@ public struct PacketFilter {
case .v4: inet = "inet"
case .v6: inet = "inet6"
}
let redirectRule = "rdr \(inet) from any to \(from.description) -> \(to.description) # \(domain)"
let redirectRule = "rdr \(inet) from any to \(from.description) -> \(to.description) # \(domain.pqdn)"
guard fm.fileExists(atPath: anchorURL.path) else {
return