summaryrefslogtreecommitdiffhomepage
path: root/ios/MullvadVPN
diff options
context:
space:
mode:
authorAndrej Mihajlov <and@mullvad.net>2019-12-04 12:13:13 +0100
committerAndrej Mihajlov <and@mullvad.net>2019-12-04 18:55:19 +0100
commitdd4ce2c1acd786bc896a32388062941d7007cd6d (patch)
tree73607dca29e2e1144ae248a13c196b715e778c34 /ios/MullvadVPN
parent0ad5a7b18853d0abfb873553082515a0c0e259ad (diff)
downloadmullvadvpn-dd4ce2c1acd786bc896a32388062941d7007cd6d.tar.xz
mullvadvpn-dd4ce2c1acd786bc896a32388062941d7007cd6d.zip
Add naive relay selector
Diffstat (limited to 'ios/MullvadVPN')
-rw-r--r--ios/MullvadVPN/MullvadEndpoint.swift19
-rw-r--r--ios/MullvadVPN/RelaySelector+RelayCache.swift21
-rw-r--r--ios/MullvadVPN/RelaySelector.swift129
3 files changed, 169 insertions, 0 deletions
diff --git a/ios/MullvadVPN/MullvadEndpoint.swift b/ios/MullvadVPN/MullvadEndpoint.swift
new file mode 100644
index 0000000000..e61fea7475
--- /dev/null
+++ b/ios/MullvadVPN/MullvadEndpoint.swift
@@ -0,0 +1,19 @@
+//
+// MullvadEndpoint.swift
+// MullvadVPN
+//
+// Created by pronebird on 12/06/2019.
+// Copyright © 2019 Amagicom AB. All rights reserved.
+//
+
+import Foundation
+import Network
+
+/// Contains server data needed to connect to a single mullvad endpoint
+struct MullvadEndpoint {
+ let ipv4Relay: NWEndpoint
+ let ipv6Relay: NWEndpoint?
+ let ipv4Gateway: IPv4Address
+ let ipv6Gateway: IPv6Address
+ let publicKey: Data
+}
diff --git a/ios/MullvadVPN/RelaySelector+RelayCache.swift b/ios/MullvadVPN/RelaySelector+RelayCache.swift
new file mode 100644
index 0000000000..b82507ba23
--- /dev/null
+++ b/ios/MullvadVPN/RelaySelector+RelayCache.swift
@@ -0,0 +1,21 @@
+//
+// RelaySelector+RelayCache.swift
+// MullvadVPN
+//
+// Created by pronebird on 07/11/2019.
+// Copyright © 2019 Amagicom AB. All rights reserved.
+//
+
+import Combine
+import Foundation
+
+extension RelaySelector {
+
+ static func loadedFromRelayCache() -> AnyPublisher<RelaySelector, RelayCacheError> {
+ return RelayCache.withDefaultLocation().publisher
+ .flatMap { $0.read() }
+ .map { RelaySelector(relayList: $0.relayList) }
+ .eraseToAnyPublisher()
+ }
+
+}
diff --git a/ios/MullvadVPN/RelaySelector.swift b/ios/MullvadVPN/RelaySelector.swift
new file mode 100644
index 0000000000..255502446c
--- /dev/null
+++ b/ios/MullvadVPN/RelaySelector.swift
@@ -0,0 +1,129 @@
+//
+// RelaySelector.swift
+// PacketTunnel
+//
+// Created by pronebird on 11/06/2019.
+// Copyright © 2019 Amagicom AB. All rights reserved.
+//
+
+import Foundation
+import Network
+
+struct RelaySelectorResult {
+ var relay: RelayList.Hostname
+ var tunnel: RelayList.WireguardTunnel
+ var endpoint: MullvadEndpoint
+}
+
+struct RelaySelector {
+
+ private let relayList: RelayList
+
+ init(relayList: RelayList) {
+ self.relayList = relayList
+ }
+
+ /// Produce a `RelayList` satisfying the given constraints
+ private func applyConstraints(_ constraints: RelayConstraints) -> RelayList {
+ let filteredCountries = relayList.countries.filter { (country) -> Bool in
+ switch constraints.location {
+ case .any:
+ return true
+ case .only(let constraint):
+ switch constraint {
+ case .country(let countryCode):
+ return countryCode == country.code
+ case .city(let countryCode, _):
+ return countryCode == country.code
+ case .hostname(let countryCode, _, _):
+ return countryCode == country.code
+ }
+ }
+ }.map { (country) -> RelayList.Country in
+ var filteredCountry = country
+ filteredCountry.cities = country.cities.filter { (city) -> Bool in
+ switch constraints.location {
+ case .any:
+ return true
+ case .only(let constraint):
+ switch constraint {
+ case .country:
+ return true
+ case .city(_, let cityCode):
+ return cityCode == city.code
+ case .hostname(_, let cityCode, _):
+ return cityCode == city.code
+ }
+ }
+ }.map { (city) -> RelayList.City in
+ var filteredCity = city
+ filteredCity.relays = city.relays.filter { (relay) -> Bool in
+ switch constraints.location {
+ case .any:
+ return true
+ case .only(let constraint):
+ switch constraint {
+ case .country, .city:
+ return true
+ case .hostname(_, _, let hostname):
+ return hostname == relay.hostname
+ }
+ }
+ }
+ .map({ (relay) -> RelayList.Hostname in
+ var filteredRelay = relay
+ filteredRelay.tunnels?.wireguard = relay.tunnels?.wireguard?
+ .filter { !$0.portRanges.isEmpty }
+
+ return filteredRelay
+ }).filter { (relay) -> Bool in
+ guard let wireguardTunnels = relay.tunnels?.wireguard else { return false }
+
+ return relay.active && !wireguardTunnels.isEmpty
+ }
+
+ return filteredCity
+ }.filter({ (city) -> Bool in
+ return !city.relays.isEmpty
+ })
+
+ return filteredCountry
+ }.filter { (country) -> Bool in
+ return !country.cities.isEmpty
+ }
+
+ return RelayList(countries: filteredCountries)
+ }
+
+ func evaluate(with constraints: RelayConstraints) -> RelaySelectorResult? {
+ let filteredRelayList = applyConstraints(constraints)
+
+ guard let randomRelay = filteredRelayList.countries.randomElement()?
+ .cities.randomElement()?
+ .relays.randomElement() else {
+ return nil
+ }
+
+ guard let randomTunnel = randomRelay.tunnels?.wireguard?.randomElement() else {
+ return nil
+ }
+
+ guard let randomPort = randomTunnel.portRanges.randomElement()?.randomElement() else {
+ return nil
+ }
+
+ let networkPort = NWEndpoint.Port(integerLiteral: randomPort)
+ let ipv4Endpoint = NWEndpoint.hostPort(host: .ipv4(randomRelay.ipv4AddrIn), port: networkPort)
+
+ let endpoint = MullvadEndpoint(
+ ipv4Relay: ipv4Endpoint,
+ ipv6Relay: nil,
+ ipv4Gateway: randomTunnel.ipv4Gateway,
+ ipv6Gateway: randomTunnel.ipv6Gateway,
+ publicKey: randomTunnel.publicKey
+ )
+
+ return RelaySelectorResult(relay: randomRelay, tunnel: randomTunnel, endpoint: endpoint)
+ }
+
+}