Skip to content

Commit db825ef

Browse files
committed
Use SystemPath for PacketFilter.
Replaces URL-based filesystem operations with SystemPackage.FilePath, mirroring the pattern landed in apple#1480 for HostDNSResolver: - configURL/anchorsURL → configPath/anchorsPath: FilePath - defaultConfigPath/defaultAnchorsPath now FilePath - FileManager calls switch from atPath: url.path to atPath: path.string - Tests use String(contentsOfFile:) in place of String(contentsOf: URL) Process.executableURL stays URL since Foundation.Process requires it.
1 parent 3b724ae commit db825ef

2 files changed

Lines changed: 41 additions & 38 deletions

File tree

Sources/Services/ContainerAPIService/Client/PacketFilter.swift

Lines changed: 31 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,19 @@ import ContainerizationError
1818
import ContainerizationExtras
1919
import DNSServer
2020
import Foundation
21+
import SystemPackage
2122

2223
public struct PacketFilter {
2324
public static let anchor = "com.apple.container"
24-
public static let defaultConfigPath = URL(filePath: "/etc/pf.conf")
25-
public static let defaultAnchorsPath = URL(filePath: "/etc/pf.anchors")
25+
public static let defaultConfigPath = FilePath("/etc/pf.conf")
26+
public static let defaultAnchorsPath = FilePath("/etc/pf.anchors")
2627

27-
private let configURL: URL
28-
private let anchorsURL: URL
28+
private let configPath: FilePath
29+
private let anchorsPath: FilePath
2930

30-
public init(configURL: URL = Self.defaultConfigPath, anchorsURL: URL = Self.defaultAnchorsPath) {
31-
self.configURL = configURL
32-
self.anchorsURL = anchorsURL
31+
public init(configPath: FilePath = Self.defaultConfigPath, anchorsPath: FilePath = Self.defaultAnchorsPath) {
32+
self.configPath = configPath
33+
self.anchorsPath = anchorsPath
3334
}
3435

3536
public func createRedirectRule(from: IPAddress, to: IPAddress, domain: DNSName) throws {
@@ -39,7 +40,7 @@ public struct PacketFilter {
3940

4041
let fm: FileManager = FileManager.default
4142

42-
let anchorURL = self.anchorsURL.appending(path: Self.anchor)
43+
let anchorPath = self.anchorsPath.appending(Self.anchor)
4344

4445
let inet: String
4546
switch from {
@@ -49,8 +50,8 @@ public struct PacketFilter {
4950
let redirectRule = "rdr \(inet) from any to \(from.description) -> \(to.description) # \(domain.pqdn)"
5051

5152
var content = ""
52-
if fm.fileExists(atPath: anchorURL.path) {
53-
content = try String(contentsOfFile: anchorURL.path, encoding: .utf8)
53+
if fm.fileExists(atPath: anchorPath.string) {
54+
content = try String(contentsOfFile: anchorPath.string, encoding: .utf8)
5455
} else {
5556
try addAnchorToConfig()
5657
}
@@ -60,7 +61,7 @@ public struct PacketFilter {
6061
lines.insert(redirectRule, at: lines.endIndex - 1)
6162
}
6263

63-
try lines.joined(separator: "\n").write(toFile: anchorURL.path, atomically: true, encoding: .utf8)
64+
try lines.joined(separator: "\n").write(toFile: anchorPath.string, atomically: true, encoding: .utf8)
6465
}
6566

6667
public func removeRedirectRule(from: IPAddress, to: IPAddress, domain: DNSName) throws {
@@ -70,7 +71,7 @@ public struct PacketFilter {
7071

7172
let fm: FileManager = FileManager.default
7273

73-
let anchorURL = self.anchorsURL.appending(path: Self.anchor)
74+
let anchorPath = self.anchorsPath.appending(Self.anchor)
7475

7576
let inet: String
7677
switch from {
@@ -79,40 +80,40 @@ public struct PacketFilter {
7980
}
8081
let redirectRule = "rdr \(inet) from any to \(from.description) -> \(to.description) # \(domain.pqdn)"
8182

82-
guard fm.fileExists(atPath: anchorURL.path) else {
83+
guard fm.fileExists(atPath: anchorPath.string) else {
8384
return
8485
}
8586

86-
let content = try String(contentsOfFile: anchorURL.path, encoding: .utf8)
87+
let content = try String(contentsOfFile: anchorPath.string, encoding: .utf8)
8788
let lines = content.components(separatedBy: .newlines)
8889

8990
let removedLines = lines.filter { l in
9091
l != redirectRule
9192
}
9293

9394
if removedLines == [""] {
94-
try fm.removeItem(atPath: anchorURL.path)
95+
try fm.removeItem(atPath: anchorPath.string)
9596
try removeAnchorFromConfig()
9697
} else {
97-
try removedLines.joined(separator: "\n").write(toFile: anchorURL.path, atomically: true, encoding: .utf8)
98+
try removedLines.joined(separator: "\n").write(toFile: anchorPath.string, atomically: true, encoding: .utf8)
9899
}
99100
}
100101

101102
private func addAnchorToConfig() throws {
102103
let fm: FileManager = FileManager.default
103104

104-
let anchorURL = self.anchorsURL.appending(path: Self.anchor)
105+
let anchorPath = self.anchorsPath.appending(Self.anchor)
105106

106107
/* PF requires strict ordering of anchors:
107108
scrub-anchor, nat-anchor, rdr-anchor, dummynet-anchor, anchor, load anchor
108109
*/
109110
let anchorKeywords = ["scrub-anchor", "nat-anchor", "rdr-anchor", "dummynet-anchor", "anchor", "load anchor"]
110-
let loadAnchorText = "load anchor \"\(Self.anchor)\" from \"\(anchorURL.path)\""
111+
let loadAnchorText = "load anchor \"\(Self.anchor)\" from \"\(anchorPath.string)\""
111112

112113
var content: String = ""
113114
var lines: [String] = []
114-
if fm.fileExists(atPath: self.configURL.path) {
115-
content = try String(contentsOfFile: self.configURL.path, encoding: .utf8)
115+
if fm.fileExists(atPath: self.configPath.string) {
116+
content = try String(contentsOfFile: self.configPath.string, encoding: .utf8)
116117
}
117118
lines = content.components(separatedBy: .newlines)
118119

@@ -134,28 +135,28 @@ public struct PacketFilter {
134135
}
135136

136137
do {
137-
try lines.joined(separator: "\n").write(toFile: self.configURL.path, atomically: true, encoding: .utf8)
138+
try lines.joined(separator: "\n").write(toFile: self.configPath.string, atomically: true, encoding: .utf8)
138139
} catch {
139-
throw ContainerizationError(.invalidState, message: "failed to write \"\(self.configURL.path)\"")
140+
throw ContainerizationError(.invalidState, message: "failed to write \"\(self.configPath.string)\"")
140141
}
141142
}
142143

143144
private func removeAnchorFromConfig() throws {
144145
let fm: FileManager = FileManager.default
145146

146-
guard fm.fileExists(atPath: configURL.path) else {
147+
guard fm.fileExists(atPath: configPath.string) else {
147148
return
148149
}
149150

150-
let content = try String(contentsOfFile: configURL.path, encoding: .utf8)
151+
let content = try String(contentsOfFile: configPath.string, encoding: .utf8)
151152
let lines = content.components(separatedBy: .newlines)
152153

153154
let removedLines = lines.filter { l in !l.contains(Self.anchor) }
154155

155156
do {
156-
try removedLines.joined(separator: "\n").write(toFile: configURL.path, atomically: true, encoding: .utf8)
157+
try removedLines.joined(separator: "\n").write(toFile: configPath.string, atomically: true, encoding: .utf8)
157158
} catch {
158-
throw ContainerizationError(.invalidState, message: "failed to write \"\(configURL.path)\"")
159+
throw ContainerizationError(.invalidState, message: "failed to write \"\(configPath.string)\"")
159160
}
160161
}
161162

@@ -165,7 +166,7 @@ public struct PacketFilter {
165166
let checkProcess = Foundation.Process()
166167
var checkStatus: Int32
167168
checkProcess.executableURL = URL(fileURLWithPath: "/sbin/pfctl")
168-
checkProcess.arguments = ["-n", "-f", configURL.path]
169+
checkProcess.arguments = ["-n", "-f", configPath.string]
169170
checkProcess.standardOutput = null
170171
checkProcess.standardError = null
171172

@@ -178,14 +179,14 @@ public struct PacketFilter {
178179
checkProcess.waitUntilExit()
179180
checkStatus = checkProcess.terminationStatus
180181
guard checkStatus == 0 else {
181-
throw ContainerizationError(.internalError, message: "invalid pf config \"\(configURL.path)\"")
182+
throw ContainerizationError(.internalError, message: "invalid pf config \"\(configPath.string)\"")
182183
}
183184

184185
let reloadProcess = Foundation.Process()
185186
var reloadStatus: Int32
186187

187188
reloadProcess.executableURL = URL(fileURLWithPath: "/sbin/pfctl")
188-
reloadProcess.arguments = ["-f", configURL.path]
189+
reloadProcess.arguments = ["-f", configPath.string]
189190
reloadProcess.standardOutput = null
190191
reloadProcess.standardError = null
191192

@@ -197,7 +198,7 @@ public struct PacketFilter {
197198
reloadProcess.waitUntilExit()
198199
reloadStatus = reloadProcess.terminationStatus
199200
guard reloadStatus == 0 else {
200-
throw ContainerizationError(.invalidState, message: "pfctl -f \"\(configURL.path)\" failed with status \(reloadStatus)")
201+
throw ContainerizationError(.invalidState, message: "pfctl -f \"\(configPath.string)\" failed with status \(reloadStatus)")
201202
}
202203
}
203204
}

Tests/ContainerAPIClientTests/PacketFilterTest.swift

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import ContainerizationError
1818
import ContainerizationExtras
1919
import DNSServer
2020
import Foundation
21+
import SystemPackage
2122
import Testing
2223

2324
@testable import ContainerAPIClient
@@ -32,17 +33,18 @@ struct PacketFilterTest {
3233
appropriateFor: .temporaryDirectory,
3334
create: true
3435
)
36+
let tempPath = FilePath(tempURL.path)
3537
defer { try? FileManager.default.removeItem(at: tempURL) }
36-
let configURL = tempURL.appending(path: "pf.conf")
38+
let configPath = tempPath.appending("pf.conf")
3739

38-
let pf = PacketFilter(configURL: configURL, anchorsURL: tempURL)
40+
let pf = PacketFilter(configPath: configPath, anchorsPath: tempPath)
3941
let from1 = try! IPAddress("203.0.113.113")
4042
let domain1 = try! DNSName("aaa.com")
4143
let to = try! IPAddress("127.0.0.1")
4244
try pf.createRedirectRule(from: from1, to: to, domain: domain1)
4345

44-
let anchorURL = tempURL.appending(path: "com.apple.container")
45-
var actualAnchorText = try String(contentsOf: anchorURL, encoding: .utf8)
46+
let anchorPath = tempPath.appending("com.apple.container")
47+
var actualAnchorText = try String(contentsOfFile: anchorPath.string, encoding: .utf8)
4648
var expectedAnchorTest = """
4749
rdr inet from any to \(from1) -> \(to) # \(domain1.pqdn)\n
4850
"""
@@ -53,13 +55,13 @@ struct PacketFilterTest {
5355
let domain2 = try! DNSName("bbb.com")
5456
try pf.createRedirectRule(from: from2, to: to, domain: domain2)
5557

56-
actualAnchorText = try String(contentsOf: anchorURL, encoding: .utf8)
58+
actualAnchorText = try String(contentsOfFile: anchorPath.string, encoding: .utf8)
5759
expectedAnchorTest += """
5860
rdr inet from any to \(from2) -> \(to) # \(domain2.pqdn)\n
5961
"""
6062
#expect(actualAnchorText == expectedAnchorTest)
6163

62-
let actualConfigText = try String(contentsOf: configURL, encoding: .utf8)
64+
let actualConfigText = try String(contentsOfFile: configPath.string, encoding: .utf8)
6365
let expectedConfigText = try Regex(
6466
#"""
6567
scrub-anchor "([^"]+)"
@@ -76,8 +78,8 @@ struct PacketFilterTest {
7678
try pf.removeRedirectRule(from: from1, to: to, domain: domain1)
7779
try pf.removeRedirectRule(from: from2, to: to, domain: domain2)
7880

79-
#expect(!fm.fileExists(atPath: anchorURL.path))
80-
let configText = try String(contentsOf: configURL, encoding: .utf8)
81+
#expect(!fm.fileExists(atPath: anchorPath.string))
82+
let configText = try String(contentsOfFile: configPath.string, encoding: .utf8)
8183
#expect(configText == "")
8284
}
8385

0 commit comments

Comments
 (0)