Skip to content

Commit b45f485

Browse files
committed
chore: release v1.5.1
Performance improvements: - True parallel DNS resolution (fixed @mainactor serialization) - Batch route operations (single XPC call instead of 300+) - DNS cache for hosts file (eliminated duplicate resolution) - Increased DNS batch size from 5 to 50 Helper version bumped to 1.2.0
1 parent b6ee79c commit b45f485

9 files changed

Lines changed: 289 additions & 80 deletions

File tree

Casks/vpn-bypass.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# Or if using local tap: brew install --cask --no-quarantine ./Casks/vpn-bypass.rb
44

55
cask "vpn-bypass" do
6-
version "1.5.0"
6+
version "1.5.1"
77
sha256 "37b127a55aec0bdb80e824e59e840ce5b529c09086aac7fc24dc4616abb817bd"
88

99
url "https://github.com/GeiserX/VPNBypass/releases/download/v#{version}/VPNBypass-#{version}.dmg"

Helper/HelperTool.swift

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,74 @@ class HelperTool: NSObject, HelperProtocol {
6565
reply(result.success, result.error)
6666
}
6767

68+
// MARK: - Batch Route Management (for startup/stop performance)
69+
70+
func addRoutesBatch(routes: [[String: Any]], withReply reply: @escaping (Int, Int, String?) -> Void) {
71+
var successCount = 0
72+
var failureCount = 0
73+
var lastError: String?
74+
75+
for route in routes {
76+
guard let destination = route["destination"] as? String,
77+
let gateway = route["gateway"] as? String else {
78+
failureCount += 1
79+
continue
80+
}
81+
82+
let isNetwork = route["isNetwork"] as? Bool ?? false
83+
84+
// Validate inputs
85+
guard isValidDestination(destination), isValidIP(gateway) else {
86+
failureCount += 1
87+
continue
88+
}
89+
90+
// First try to delete existing route (ignore result)
91+
_ = executeRoute(args: ["-n", "delete", destination])
92+
93+
// Add the new route
94+
var args = ["-n", "add"]
95+
if isNetwork {
96+
args.append(contentsOf: ["-net", destination, gateway])
97+
} else {
98+
args.append(contentsOf: ["-host", destination, gateway])
99+
}
100+
101+
let result = executeRoute(args: args)
102+
if result.success {
103+
successCount += 1
104+
} else {
105+
failureCount += 1
106+
lastError = result.error
107+
}
108+
}
109+
110+
reply(successCount, failureCount, lastError)
111+
}
112+
113+
func removeRoutesBatch(destinations: [String], withReply reply: @escaping (Int, Int, String?) -> Void) {
114+
var successCount = 0
115+
var failureCount = 0
116+
var lastError: String?
117+
118+
for destination in destinations {
119+
guard isValidDestination(destination) else {
120+
failureCount += 1
121+
continue
122+
}
123+
124+
let result = executeRoute(args: ["-n", "delete", destination])
125+
if result.success {
126+
successCount += 1
127+
} else {
128+
failureCount += 1
129+
lastError = result.error
130+
}
131+
}
132+
133+
reply(successCount, failureCount, lastError)
134+
}
135+
68136
private func executeRoute(args: [String]) -> (success: Bool, error: String?) {
69137
let process = Process()
70138
process.executableURL = URL(fileURLWithPath: "/sbin/route")

Info.plist

Lines changed: 33 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -2,40 +2,38 @@
22
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
33
<plist version="1.0">
44
<dict>
5-
<key>CFBundleDevelopmentRegion</key>
6-
<string>en</string>
7-
<key>CFBundleExecutable</key>
8-
<string>VPNBypass</string>
9-
<key>CFBundleIdentifier</key>
10-
<string>com.geiserx.vpn-bypass</string>
11-
<key>CFBundleInfoDictionaryVersion</key>
12-
<string>6.0</string>
13-
<key>CFBundleIconFile</key>
14-
<string>AppIcon</string>
15-
<key>CFBundleName</key>
16-
<string>VPN Bypass</string>
17-
<key>CFBundlePackageType</key>
18-
<string>APPL</string>
19-
<key>CFBundleShortVersionString</key>
20-
<string>1.2.1</string>
21-
<key>CFBundleVersion</key>
22-
<string>3</string>
23-
<key>LSMinimumSystemVersion</key>
24-
<string>13.0</string>
25-
<key>LSUIElement</key>
26-
<true/>
27-
<key>NSHighResolutionCapable</key>
28-
<true/>
29-
<key>NSHumanReadableCopyright</key>
30-
<string>Copyright © 2026 GeiserX. All rights reserved.</string>
31-
<key>NSUserNotificationAlertStyle</key>
32-
<string>banner</string>
33-
<key>SMPrivilegedExecutables</key>
34-
<dict>
35-
<key>com.geiserx.vpnbypass.helper</key>
36-
<!-- For development, use relaxed requirement -->
37-
<!-- Production should use: identifier "com.geiserx.vpnbypass.helper" and anchor apple generic and certificate leaf[subject.CN] = "Developer ID Application: YOUR NAME" -->
38-
<string>identifier "com.geiserx.vpnbypass.helper"</string>
39-
</dict>
5+
<key>CFBundleDevelopmentRegion</key>
6+
<string>en</string>
7+
<key>CFBundleExecutable</key>
8+
<string>VPNBypass</string>
9+
<key>CFBundleIconFile</key>
10+
<string>AppIcon</string>
11+
<key>CFBundleIdentifier</key>
12+
<string>com.geiserx.vpn-bypass</string>
13+
<key>CFBundleInfoDictionaryVersion</key>
14+
<string>6.0</string>
15+
<key>CFBundleName</key>
16+
<string>VPN Bypass</string>
17+
<key>CFBundlePackageType</key>
18+
<string>APPL</string>
19+
<key>CFBundleShortVersionString</key>
20+
<string>1.5.1</string>
21+
<key>CFBundleVersion</key>
22+
<string>4</string>
23+
<key>LSMinimumSystemVersion</key>
24+
<string>13.0</string>
25+
<key>LSUIElement</key>
26+
<true/>
27+
<key>NSHighResolutionCapable</key>
28+
<true/>
29+
<key>NSHumanReadableCopyright</key>
30+
<string>Copyright © 2026 GeiserX. All rights reserved.</string>
31+
<key>NSUserNotificationAlertStyle</key>
32+
<string>banner</string>
33+
<key>SMPrivilegedExecutables</key>
34+
<dict>
35+
<key>com.geiserx.vpnbypass.helper</key>
36+
<string>identifier "com.geiserx.vpnbypass.helper"</string>
37+
</dict>
4038
</dict>
4139
</plist>

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
<p align="center">
1212
<img src="https://img.shields.io/badge/macOS-13%2B-blue" alt="macOS 13+">
1313
<img src="https://img.shields.io/badge/Swift-5.9-orange" alt="Swift 5.9">
14-
<a href="https://github.com/GeiserX/vpn-macos-bypass/releases"><img src="https://img.shields.io/badge/version-1.5.0-green" alt="Version"></a>
14+
<a href="https://github.com/GeiserX/vpn-macos-bypass/releases"><img src="https://img.shields.io/badge/version-1.5.1-green" alt="Version"></a>
1515
</p>
1616

1717
## Why?

Sources/HelperManager.swift

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,44 @@ final class HelperManager: ObservableObject {
262262
}
263263
}
264264

265+
// MARK: - Batch Route Operations (for startup/stop performance)
266+
267+
func addRoutesBatch(routes: [(destination: String, gateway: String, isNetwork: Bool)]) async -> (successCount: Int, failureCount: Int, error: String?) {
268+
guard isHelperInstalled else {
269+
return (0, routes.count, "Helper not installed")
270+
}
271+
272+
let dictRoutes = routes.map { route -> [String: Any] in
273+
return [
274+
"destination": route.destination,
275+
"gateway": route.gateway,
276+
"isNetwork": route.isNetwork
277+
]
278+
}
279+
280+
return await withCheckedContinuation { continuation in
281+
connectToHelper { helper in
282+
helper.addRoutesBatch(routes: dictRoutes) { successCount, failureCount, error in
283+
continuation.resume(returning: (successCount, failureCount, error))
284+
}
285+
}
286+
}
287+
}
288+
289+
func removeRoutesBatch(destinations: [String]) async -> (successCount: Int, failureCount: Int, error: String?) {
290+
guard isHelperInstalled else {
291+
return (0, destinations.count, "Helper not installed")
292+
}
293+
294+
return await withCheckedContinuation { continuation in
295+
connectToHelper { helper in
296+
helper.removeRoutesBatch(destinations: destinations) { successCount, failureCount, error in
297+
continuation.resume(returning: (successCount, failureCount, error))
298+
}
299+
}
300+
}
301+
}
302+
265303
// MARK: - Hosts File Operations
266304

267305
func updateHostsFile(entries: [(domain: String, ip: String)]) async -> (success: Bool, error: String?) {

Sources/HelperProtocol.swift

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,24 @@ protocol HelperProtocol {
3232
withReply reply: @escaping (Bool, String?) -> Void
3333
)
3434

35+
/// Add multiple routes in batch (faster for startup/VPN connect)
36+
/// - Parameters:
37+
/// - routes: Array of dictionaries with "destination", "gateway", and "isNetwork" keys
38+
/// - reply: Callback with success count, failure count, and optional error message
39+
func addRoutesBatch(
40+
routes: [[String: Any]],
41+
withReply reply: @escaping (Int, Int, String?) -> Void
42+
)
43+
44+
/// Remove multiple routes in batch (faster for cleanup/VPN disconnect)
45+
/// - Parameters:
46+
/// - destinations: Array of IP addresses or CIDR ranges to remove
47+
/// - reply: Callback with success count, failure count, and optional error message
48+
func removeRoutesBatch(
49+
destinations: [String],
50+
withReply reply: @escaping (Int, Int, String?) -> Void
51+
)
52+
3553
/// Update the hosts file with VPN bypass entries
3654
/// - Parameters:
3755
/// - entries: Array of dictionaries with "domain" and "ip" keys
@@ -60,7 +78,7 @@ protocol HelperProgressProtocol {
6078
// MARK: - Helper Constants
6179

6280
struct HelperConstants {
63-
static let helperVersion = "1.1.0"
81+
static let helperVersion = "1.2.0"
6482
static let bundleID = "com.geiserx.vpnbypass.helper"
6583
static let hostMarkerStart = "# VPN-BYPASS-MANAGED - START"
6684
static let hostMarkerEnd = "# VPN-BYPASS-MANAGED - END"

0 commit comments

Comments
 (0)