Skip to content

Commit 7b9e200

Browse files
committed
fix: deduplicate routes when multiple domains resolve to same IP
1 parent 15c980f commit 7b9e200

6 files changed

Lines changed: 33 additions & 14 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.6.0"
6+
version "1.6.1"
77
sha256 "37b127a55aec0bdb80e824e59e840ce5b529c09086aac7fc24dc4616abb817bd"
88

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

Info.plist

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@
1717
<key>CFBundlePackageType</key>
1818
<string>APPL</string>
1919
<key>CFBundleShortVersionString</key>
20-
<string>1.6.0</string>
20+
<string>1.6.1</string>
2121
<key>CFBundleVersion</key>
22-
<string>9</string>
22+
<string>10</string>
2323
<key>LSMinimumSystemVersion</key>
2424
<string>13.0</string>
2525
<key>LSUIElement</key>

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.6.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.6.1-green" alt="Version"></a>
1515
</p>
1616

1717
## Why?

Sources/RouteManager.swift

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -934,6 +934,7 @@ final class RouteManager: ObservableObject {
934934

935935
// Collect all routes to add (for batch operation)
936936
var routesToAdd: [(destination: String, gateway: String, isNetwork: Bool, source: String)] = []
937+
var seenDestinations: Set<String> = [] // Deduplicate by destination IP
937938

938939
while index < allDomains.count {
939940
let endIndex = min(index + batchSize, allDomains.count)
@@ -964,7 +965,8 @@ final class RouteManager: ObservableObject {
964965
}
965966
dnsDiskCache[result.domain] = ips // Update persistent cache
966967

967-
for ip in ips {
968+
for ip in ips where !seenDestinations.contains(ip) {
969+
seenDestinations.insert(ip)
968970
routesToAdd.append((destination: ip, gateway: gateway, isNetwork: false, source: result.source))
969971
}
970972
} else if let cachedIPs = dnsDiskCache[result.domain], !cachedIPs.isEmpty {
@@ -973,7 +975,8 @@ final class RouteManager: ObservableObject {
973975
if let firstIP = cachedIPs.first {
974976
dnsCache[result.domain] = firstIP
975977
}
976-
for ip in cachedIPs {
978+
for ip in cachedIPs where !seenDestinations.contains(ip) {
979+
seenDestinations.insert(ip)
977980
routesToAdd.append((destination: ip, gateway: gateway, isNetwork: false, source: result.source))
978981
}
979982
} else {
@@ -991,7 +994,8 @@ final class RouteManager: ObservableObject {
991994

992995
// Collect IP ranges (these don't need DNS resolution)
993996
for service in config.services where service.enabled {
994-
for range in service.ipRanges {
997+
for range in service.ipRanges where !seenDestinations.contains(range) {
998+
seenDestinations.insert(range)
995999
routesToAdd.append((destination: range, gateway: gateway, isNetwork: true, source: service.name))
9961000
}
9971001
}
@@ -1094,12 +1098,14 @@ final class RouteManager: ObservableObject {
10941098

10951099
var newRoutes: [ActiveRoute] = []
10961100
var routesToAdd: [(destination: String, gateway: String, isNetwork: Bool, source: String)] = []
1101+
var seenDestinations: Set<String> = [] // Deduplicate by destination IP
10971102

10981103
// Build routes from DNS cache
10991104
for service in config.services where service.enabled {
11001105
for domain in service.domains {
11011106
if let cachedIPs = dnsDiskCache[domain] {
1102-
for ip in cachedIPs {
1107+
for ip in cachedIPs where !seenDestinations.contains(ip) {
1108+
seenDestinations.insert(ip)
11031109
routesToAdd.append((destination: ip, gateway: gateway, isNetwork: false, source: service.name))
11041110
}
11051111
if let firstIP = cachedIPs.first {
@@ -1108,15 +1114,17 @@ final class RouteManager: ObservableObject {
11081114
}
11091115
}
11101116
// IP ranges don't need DNS
1111-
for range in service.ipRanges {
1117+
for range in service.ipRanges where !seenDestinations.contains(range) {
1118+
seenDestinations.insert(range)
11121119
routesToAdd.append((destination: range, gateway: gateway, isNetwork: true, source: service.name))
11131120
}
11141121
}
11151122

11161123
// Custom domains
11171124
for domain in config.domains where domain.enabled {
11181125
if let cachedIPs = dnsDiskCache[domain.domain] {
1119-
for ip in cachedIPs {
1126+
for ip in cachedIPs where !seenDestinations.contains(ip) {
1127+
seenDestinations.insert(ip)
11201128
routesToAdd.append((destination: ip, gateway: gateway, isNetwork: false, source: domain.domain))
11211129
}
11221130
if let firstIP = cachedIPs.first {
@@ -1579,6 +1587,10 @@ final class RouteManager: ObservableObject {
15791587
var newRoutes: [ActiveRoute] = []
15801588
var routesToAdd: [(destination: String, gateway: String, isNetwork: Bool)] = []
15811589

1590+
// Track existing routes to avoid duplicates
1591+
let existingDestinations = Set(activeRoutes.map { $0.destination })
1592+
var seenDestinations: Set<String> = existingDestinations
1593+
15821594
// Capture DNS settings for parallel resolution
15831595
let userDNS = detectedDNSServer
15841596
let fallbackDNS = config.fallbackDNS
@@ -1608,14 +1620,16 @@ final class RouteManager: ObservableObject {
16081620
dnsCache[domain] = firstIP
16091621
}
16101622

1611-
for ip in ips {
1623+
for ip in ips where !seenDestinations.contains(ip) {
1624+
seenDestinations.insert(ip)
16121625
routesToAdd.append((destination: ip, gateway: gateway, isNetwork: false))
16131626
newRoutes.append(ActiveRoute(destination: ip, gateway: gateway, source: service.name, timestamp: Date()))
16141627
}
16151628
}
16161629

16171630
// Add IP ranges (no DNS needed)
1618-
for range in service.ipRanges {
1631+
for range in service.ipRanges where !seenDestinations.contains(range) {
1632+
seenDestinations.insert(range)
16191633
routesToAdd.append((destination: range, gateway: gateway, isNetwork: true))
16201634
newRoutes.append(ActiveRoute(destination: range, gateway: gateway, source: service.name, timestamp: Date()))
16211635
}

Sources/SettingsView.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1263,7 +1263,7 @@ struct GeneralTab: View {
12631263
HStack {
12641264
VStack(alignment: .leading, spacing: 2) {
12651265
BrandedAppName(fontSize: 13)
1266-
Text("Version 1.6.0")
1266+
Text("Version 1.6.1")
12671267
.font(.system(size: 11))
12681268
.foregroundColor(Color(hex: "6B7280"))
12691269
}
@@ -1721,7 +1721,7 @@ struct InfoTab: View {
17211721
// App name with branded colors
17221722
BrandedAppName(fontSize: 24)
17231723

1724-
Text("v1.6.0")
1724+
Text("v1.6.1")
17251725
.font(.system(size: 12, design: .monospaced))
17261726
.foregroundColor(Color(hex: "6B7280"))
17271727

docs/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ All notable changes to VPN Bypass will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [1.6.1] - 2026-01-21
9+
10+
### Fixed
11+
- **Deduplicate Routes** - Multiple domains resolving to the same IP no longer create duplicate routes
12+
813
## [1.6.0] - 2026-01-21
914

1015
### Added

0 commit comments

Comments
 (0)