diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 50b8297c7..9d2d90a22 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,6 +4,7 @@ on: push: branches: - "main" + - "develop" pull_request: jobs: diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index b3fde716b..e7c70e8a2 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -4,6 +4,7 @@ on: push: branches: - "main" + - "develop" pull_request: jobs: diff --git a/.github/workflows/swiftlint.yml b/.github/workflows/swiftlint.yml index 207db9a2b..70ae2844e 100644 --- a/.github/workflows/swiftlint.yml +++ b/.github/workflows/swiftlint.yml @@ -4,6 +4,7 @@ on: push: branches: - "main" + - "develop" pull_request: paths: - '.github/workflows/swiftlint.yml' diff --git a/CHANGELOG.md b/CHANGELOG.md index c44caafc7..5c80a4198 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ All notable changes to this project will be documented in this file. +## 2.8.0 - 2023-01-30 + +[NEW] Favorite servers +[IMPROVED] Option to search and sort in the fastest server configuration list + ## 2.7.1 - 2022-12-08 [IMPROVED] Support for Split View and Slide Over mode on iPadOS diff --git a/IVPNClient/AppDelegate.swift b/IVPNClient/AppDelegate.swift index be2310cd3..8e80f6f55 100644 --- a/IVPNClient/AppDelegate.swift +++ b/IVPNClient/AppDelegate.swift @@ -111,15 +111,15 @@ class AppDelegate: UIResponder { private func showSecurityScreen() { var showWindow = false - if let _ = UIApplication.topViewController() as? AccountViewController { + if UIApplication.topViewController() as? AccountViewController != nil { showWindow = true } - if let _ = UIApplication.topViewController() as? LoginViewController { + if UIApplication.topViewController() as? LoginViewController != nil { showWindow = true } - if let _ = UIApplication.topViewController() as? CreateAccountViewController { + if UIApplication.topViewController() as? CreateAccountViewController != nil { showWindow = true } @@ -166,7 +166,7 @@ class AppDelegate: UIResponder { } }) case Config.urlTypeLogin: - if let _ = UIApplication.topViewController() as? LoginViewController { + if UIApplication.topViewController() as? LoginViewController != nil { return } @@ -177,6 +177,99 @@ class AppDelegate: UIResponder { break } } + + private func userActivityConnect() { + DispatchQueue.delay(0.75) { + if UserDefaults.shared.networkProtectionEnabled { + Application.shared.connectionManager.resetRulesAndConnectShortcut(closeApp: true, actionType: .connect) + return + } + Application.shared.connectionManager.connectShortcut(closeApp: true, actionType: .connect) + } + } + + private func userActivityDisconnect() { + DispatchQueue.delay(0.75) { + if UserDefaults.shared.networkProtectionEnabled { + Application.shared.connectionManager.resetRulesAndDisconnectShortcut(closeApp: true, actionType: .disconnect) + return + } + Application.shared.connectionManager.disconnectShortcut(closeApp: true, actionType: .disconnect) + } + } + + private func userActivityAntiTrackerEnable() { + DispatchQueue.async { + if let viewController = UIApplication.topViewController() { + if Application.shared.settings.connectionProtocol.tunnelType() == .ipsec { + viewController.showAlert(title: "IKEv2 not supported", message: "AntiTracker is supported only for OpenVPN and WireGuard protocols.") { _ in + } + return + } + + UserDefaults.shared.set(true, forKey: UserDefaults.Key.isAntiTracker) + NotificationCenter.default.post(name: Notification.Name.AntiTrackerUpdated, object: nil) + if UIApplication.topViewController() as? MainViewController != nil { + NotificationCenter.default.post(name: Notification.Name.EvaluateReconnect, object: nil) + } else { + viewController.evaluateReconnect(sender: viewController.view) + } + } + } + } + + private func userActivityAntiTrackerDisable() { + DispatchQueue.async { + if let viewController = UIApplication.topViewController() { + UserDefaults.shared.set(false, forKey: UserDefaults.Key.isAntiTracker) + NotificationCenter.default.post(name: Notification.Name.AntiTrackerUpdated, object: nil) + if UIApplication.topViewController() as? MainViewController != nil { + NotificationCenter.default.post(name: Notification.Name.EvaluateReconnect, object: nil) + } else { + viewController.evaluateReconnect(sender: viewController.view) + } + } + } + } + + private func userActivityCustomDNSEnable() { + DispatchQueue.async { + if let viewController = UIApplication.topViewController() { + if Application.shared.settings.connectionProtocol.tunnelType() == .ipsec { + viewController.showAlert(title: "IKEv2 not supported", message: "Custom DNS is supported only for OpenVPN and WireGuard protocols.") { _ in + } + return + } + + guard !UserDefaults.shared.customDNS.isEmpty else { + viewController.showAlert(title: "", message: "Please enter DNS server info") + return + } + + UserDefaults.shared.set(true, forKey: UserDefaults.Key.isCustomDNS) + NotificationCenter.default.post(name: Notification.Name.CustomDNSUpdated, object: nil) + if UIApplication.topViewController() as? MainViewController != nil { + NotificationCenter.default.post(name: Notification.Name.EvaluateReconnect, object: nil) + } else { + viewController.evaluateReconnect(sender: viewController.view) + } + } + } + } + + private func userActivityCustomDNSDisable() { + DispatchQueue.async { + if let viewController = UIApplication.topViewController() { + UserDefaults.shared.set(false, forKey: UserDefaults.Key.isCustomDNS) + NotificationCenter.default.post(name: Notification.Name.CustomDNSUpdated, object: nil) + if UIApplication.topViewController() as? MainViewController != nil { + NotificationCenter.default.post(name: Notification.Name.EvaluateReconnect, object: nil) + } else { + viewController.evaluateReconnect(sender: viewController.view) + } + } + } + } } @@ -273,86 +366,17 @@ extension AppDelegate: UIApplicationDelegate { switch userActivity.activityType { case UserActivityType.Connect: - DispatchQueue.delay(0.75) { - if UserDefaults.shared.networkProtectionEnabled { - Application.shared.connectionManager.resetRulesAndConnectShortcut(closeApp: true, actionType: .connect) - return - } - Application.shared.connectionManager.connectShortcut(closeApp: true, actionType: .connect) - } + userActivityConnect() case UserActivityType.Disconnect: - DispatchQueue.delay(0.75) { - if UserDefaults.shared.networkProtectionEnabled { - Application.shared.connectionManager.resetRulesAndDisconnectShortcut(closeApp: true, actionType: .disconnect) - return - } - Application.shared.connectionManager.disconnectShortcut(closeApp: true, actionType: .disconnect) - } + userActivityDisconnect() case UserActivityType.AntiTrackerEnable: - DispatchQueue.async { - if let viewController = UIApplication.topViewController() { - if Application.shared.settings.connectionProtocol.tunnelType() == .ipsec { - viewController.showAlert(title: "IKEv2 not supported", message: "AntiTracker is supported only for OpenVPN and WireGuard protocols.") { _ in - } - return - } - - UserDefaults.shared.set(true, forKey: UserDefaults.Key.isAntiTracker) - NotificationCenter.default.post(name: Notification.Name.AntiTrackerUpdated, object: nil) - if let _ = UIApplication.topViewController() as? MainViewController { - NotificationCenter.default.post(name: Notification.Name.EvaluateReconnect, object: nil) - } else { - viewController.evaluateReconnect(sender: viewController.view) - } - } - } + userActivityAntiTrackerEnable() case UserActivityType.AntiTrackerDisable: - DispatchQueue.async { - if let viewController = UIApplication.topViewController() { - UserDefaults.shared.set(false, forKey: UserDefaults.Key.isAntiTracker) - NotificationCenter.default.post(name: Notification.Name.AntiTrackerUpdated, object: nil) - if let _ = UIApplication.topViewController() as? MainViewController { - NotificationCenter.default.post(name: Notification.Name.EvaluateReconnect, object: nil) - } else { - viewController.evaluateReconnect(sender: viewController.view) - } - } - } + userActivityAntiTrackerDisable() case UserActivityType.CustomDNSEnable: - DispatchQueue.async { - if let viewController = UIApplication.topViewController() { - if Application.shared.settings.connectionProtocol.tunnelType() == .ipsec { - viewController.showAlert(title: "IKEv2 not supported", message: "Custom DNS is supported only for OpenVPN and WireGuard protocols.") { _ in - } - return - } - - guard !UserDefaults.shared.customDNS.isEmpty else { - viewController.showAlert(title: "", message: "Please enter DNS server info") - return - } - - UserDefaults.shared.set(true, forKey: UserDefaults.Key.isCustomDNS) - NotificationCenter.default.post(name: Notification.Name.CustomDNSUpdated, object: nil) - if let _ = UIApplication.topViewController() as? MainViewController { - NotificationCenter.default.post(name: Notification.Name.EvaluateReconnect, object: nil) - } else { - viewController.evaluateReconnect(sender: viewController.view) - } - } - } + userActivityCustomDNSEnable() case UserActivityType.CustomDNSDisable: - DispatchQueue.async { - if let viewController = UIApplication.topViewController() { - UserDefaults.shared.set(false, forKey: UserDefaults.Key.isCustomDNS) - NotificationCenter.default.post(name: Notification.Name.CustomDNSUpdated, object: nil) - if let _ = UIApplication.topViewController() as? MainViewController { - NotificationCenter.default.post(name: Notification.Name.EvaluateReconnect, object: nil) - } else { - viewController.evaluateReconnect(sender: viewController.view) - } - } - } + userActivityCustomDNSDisable() default: log(.info, message: "No such user activity") } diff --git a/IVPNClient/Assets.xcassets/favorite-server.imageset/Contents.json b/IVPNClient/Assets.xcassets/favorite-server.imageset/Contents.json new file mode 100644 index 000000000..acb6e02b2 --- /dev/null +++ b/IVPNClient/Assets.xcassets/favorite-server.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "filename" : "favourite-server-light.pdf", + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "favourite-server-dark.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/IVPNClient/Assets.xcassets/favorite-server.imageset/favourite-server-dark.pdf b/IVPNClient/Assets.xcassets/favorite-server.imageset/favourite-server-dark.pdf new file mode 100644 index 000000000..8222d66b0 Binary files /dev/null and b/IVPNClient/Assets.xcassets/favorite-server.imageset/favourite-server-dark.pdf differ diff --git a/IVPNClient/Assets.xcassets/favorite-server.imageset/favourite-server-light.pdf b/IVPNClient/Assets.xcassets/favorite-server.imageset/favourite-server-light.pdf new file mode 100644 index 000000000..e89e919a9 Binary files /dev/null and b/IVPNClient/Assets.xcassets/favorite-server.imageset/favourite-server-light.pdf differ diff --git a/IVPNClient/Assets.xcassets/icon-star-off.imageset/Contents.json b/IVPNClient/Assets.xcassets/icon-star-off.imageset/Contents.json new file mode 100644 index 000000000..ccbed1208 --- /dev/null +++ b/IVPNClient/Assets.xcassets/icon-star-off.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "star-off.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/IVPNClient/Assets.xcassets/icon-star-off.imageset/star-off.pdf b/IVPNClient/Assets.xcassets/icon-star-off.imageset/star-off.pdf new file mode 100644 index 000000000..9624c8533 Binary files /dev/null and b/IVPNClient/Assets.xcassets/icon-star-off.imageset/star-off.pdf differ diff --git a/IVPNClient/Assets.xcassets/icon-star-on.imageset/Contents.json b/IVPNClient/Assets.xcassets/icon-star-on.imageset/Contents.json new file mode 100644 index 000000000..c38ffd397 --- /dev/null +++ b/IVPNClient/Assets.xcassets/icon-star-on.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "star-on.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/IVPNClient/Assets.xcassets/icon-star-on.imageset/star-on.pdf b/IVPNClient/Assets.xcassets/icon-star-on.imageset/star-on.pdf new file mode 100644 index 000000000..bd4f1e7a7 Binary files /dev/null and b/IVPNClient/Assets.xcassets/icon-star-on.imageset/star-on.pdf differ diff --git a/IVPNClient/Config/servers.json b/IVPNClient/Config/servers.json index 6d5ade604..f8db048be 100644 --- a/IVPNClient/Config/servers.json +++ b/IVPNClient/Config/servers.json @@ -1 +1 @@ -{"wireguard":[{"gateway":"ca-qc.wg.ivpn.net","country_code":"CA","country":"Canada","city":"Montreal","latitude":45.5,"longitude":-73.5833,"isp":"M247","hosts":[{"hostname":"ca1.wg.ivpn.net","host":"37.120.130.58","public_key":"rg+GGDmjM4Vxo1hURvKmgm9yonb6qcoKbPCP/DNDBnI=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":23.32,"multihop_port":23810},{"hostname":"ca-qc1.wg.ivpn.net","host":"87.101.92.29","public_key":"98JU1mdCR8vD1aNZg017/NjBeTjuuCKUaLw0zfz/CUE=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":56.01,"multihop_port":27001}]},{"gateway":"ch.wg.ivpn.net","country_code":"CH","country":"Switzerland","city":"Zurich","latitude":47.38,"longitude":8.55,"isp":"Privatelayer","hosts":[{"hostname":"ch1.wg.ivpn.net","host":"141.255.164.66","public_key":"jVZJ61i1xxkAfriDHpwvF/GDuTvZUqhwoWSjkOJvaUA=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":11.69,"multihop_port":23610},{"hostname":"ch01.wg.ivpn.net","host":"185.212.170.141","public_key":"dU7gLfcupYd37LW0q6cxC6PHMba+eUFAUOoU/ryXZkY=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":34.61,"multihop_port":23601},{"hostname":"ch3.wg.ivpn.net","host":"141.255.166.198","public_key":"JBpgBKtqIneRuEga7mbP2PAk/e4HPRaC11H0A0+R3lA=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":17.67,"multihop_port":22901}]},{"gateway":"de.wg.ivpn.net","country_code":"DE","country":"Germany","city":"Frankfurt","latitude":50.1,"longitude":8.675,"isp":"Datapacket","hosts":[{"hostname":"de1.wg.ivpn.net","host":"185.102.219.26","public_key":"mS3/WpXjnMAMmXjSpd4nFzx9HSE3ubv2WyjpyH2REgs=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":33.38,"multihop_port":23010},{"hostname":"de01.wg.ivpn.net","host":"178.162.212.24","public_key":"Sc5AUZieg0qX8kyCy9p0OHRES4n0CHtHHM+ZPyERFTc=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":21.32,"multihop_port":23001},{"hostname":"de2.wg.ivpn.net","host":"37.58.60.151","public_key":"QhY3OtBf4FFafKtLO33e6k8JnAl8e6ktFcRUyLjCDVY=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":20.1,"multihop_port":22001}]},{"gateway":"gb.wg.ivpn.net","country_code":"GB","country":"United Kingdom","city":"London","latitude":51.5,"longitude":-0.1167,"isp":"M247","hosts":[{"hostname":"gb1.wg.ivpn.net","host":"81.92.202.114","public_key":"7+jos+Eg+hMEOQE4Std6OJ+WVnCcmbqS1/EbPwn9w3s=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":10.41,"multihop_port":20810},{"hostname":"gb01.wg.ivpn.net","host":"185.59.221.140","public_key":"yKK5x+D17Jr3Q12T/UBaDjNVmNdZBsqpvTqH6YfsGHg=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":35.8,"multihop_port":20801},{"hostname":"gb2.wg.ivpn.net","host":"185.59.221.225","public_key":"x0BTRaxsdxAd58ZyU2YMX4bmuj+Eg+8/urT2F3Vs1n8=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":5.58,"multihop_port":24201}]},{"gateway":"it.wg.ivpn.net","country_code":"IT","country":"Italy","city":"Milan","latitude":45.47,"longitude":9.205,"isp":"M247","hosts":[{"hostname":"it1.wg.ivpn.net","host":"82.102.21.90","public_key":"Aj6b81yrDk7I913R+fuSW/NAmIl87N73vHgY5/WQY0Q=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":5.83,"multihop_port":24310},{"hostname":"it01.wg.ivpn.net","host":"158.58.172.89","public_key":"QTzR5R6jeDI/cQ0CXPIqOby9GR5nn+4Bcf4bK536Vy0=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":53.23,"multihop_port":24301}]},{"gateway":"nl.wg.ivpn.net","country_code":"NL","country":"Netherlands","city":"Amsterdam","latitude":52.35,"longitude":4.9166,"isp":"Datapacket","hosts":[{"hostname":"nl1.wg.ivpn.net","host":"185.102.218.104","public_key":"AsMT2FqpkZbjzWeDch6GwufF5odl259W/hIkGytVfWo=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":20.22,"multihop_port":20301},{"hostname":"nl3.wg.ivpn.net","host":"95.211.95.9","public_key":"XDU6Syq1DY82IMatsHV0x/TAtbLiRwh/SdFCXlEn40c=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":8.14,"multihop_port":23101},{"hostname":"nl4.wg.ivpn.net","host":"95.211.95.19","public_key":"cVB66gPq5cZ9dfXY+e2pbsCyih5o1zk04l5c5VCsV1g=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":8.7,"multihop_port":23201},{"hostname":"nl5.wg.ivpn.net","host":"95.211.243.162","public_key":"NCagAawwRixI6Iw/NWiGD8lbjDNCl0aTICZKJtO/1HA=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":22.5,"multihop_port":23901},{"hostname":"nl6.wg.ivpn.net","host":"95.211.243.182","public_key":"hMWpqb3FEATHIbImPVWB/5z2nWIXghwpnJjevPY+1H0=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":56.84,"multihop_port":24101},{"hostname":"nl7.wg.ivpn.net","host":"95.211.172.105","public_key":"hQNYqtfOOAEz0IGshLx/TI9hUrfR9gIIkjVm4VsCbBM=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":13.54,"multihop_port":22501},{"hostname":"nl8.wg.ivpn.net","host":"95.211.198.167","public_key":"/nY1/OhVhdHtbnU/s31zYUuPBH0pizv4DemW5KDOUkg=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":10.4,"multihop_port":22801}]},{"gateway":"se.wg.ivpn.net","country_code":"SE","country":"Sweden","city":"Stockholm","latitude":59.3508,"longitude":18.0973,"isp":"M247","hosts":[{"hostname":"se1.wg.ivpn.net","host":"37.120.153.226","public_key":"2n0nFE1g/+vQr2AOQPm9Igyiy0zh9uTTultvOOSkMRo=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":46.74,"multihop_port":24010},{"hostname":"se01.wg.ivpn.net","host":"80.67.10.141","public_key":"u8VHnYEpoEjJWDAF9NAUkU6s810RnkMuhEfFD9U0cGo=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":5.41,"multihop_port":24001}]},{"gateway":"sg.wg.ivpn.net","country_code":"SG","country":"Singapore","city":"Singapore","latitude":1.293,"longitude":103.8558,"isp":"M247","hosts":[{"hostname":"sg1.wg.ivpn.net","host":"37.120.151.122","public_key":"hSg0At4uwuIhmTy5UT4fRbi5AN6JO2ZWTuIvqd4nHCE=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":9.75,"multihop_port":26110},{"hostname":"sg01.wg.ivpn.net","host":"185.128.24.189","public_key":"pWk0u1Xq8FHC+xpkN+C6yEKOTEanorR5zMCSfHlLzFw=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":100,"multihop_port":26101}]},{"gateway":"us-ca.wg.ivpn.net","country_code":"US","country":"United States","city":"Los Angeles, CA","latitude":34.1139,"longitude":-118.4068,"isp":"Datapacket","hosts":[{"hostname":"us-ca1.wg.ivpn.net","host":"185.180.13.41","public_key":"FGl78s9Ct6xNamQ2/CtAyXwGePrrU0kiZxfM27pm8XA=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":5.89,"multihop_port":22210},{"hostname":"us-ca01.wg.ivpn.net","host":"216.144.236.44","public_key":"B+qXdkIuETpzI0bfhGUAHN4SU91Tjs6ItdFlu93S42I=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":8.35,"multihop_port":22201},{"hostname":"us-ca2.wg.ivpn.net","host":"216.144.236.68","public_key":"qv4Tupfon5NUSwzDpM8zPizSwJZn2h+9CqrufcyDOko=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":9.24,"multihop_port":22401},{"hostname":"us-ca3.wg.ivpn.net","host":"198.54.129.100","public_key":"J5+Bx84LxNPdWEhewOvBV/fGWiDluIBlAcr1QlJZil8=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":15.52,"multihop_port":21301},{"hostname":"us-ca4.wg.ivpn.net","host":"216.144.237.83","public_key":"dYPXYr6HSRJPe3MhALwGWNtdEy1+EPE9Kqv7cTrUXk8=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":14.3,"multihop_port":21901}]},{"gateway":"us-ga.wg.ivpn.net","country_code":"US","country":"United States","city":"Atlanta, GA","latitude":33.7627,"longitude":-84.4225,"isp":"Datapacket","hosts":[{"hostname":"us-ga1.wg.ivpn.net","host":"185.93.0.212","public_key":"jD8h+pL5/d6fmYcTzl0lR8AWzQVN5XkwRFSmM/3NcDM=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":9.6,"multihop_port":24510},{"hostname":"us-ga01.wg.ivpn.net","host":"104.129.24.149","public_key":"EJFl28aYpZKfmJqb1jxxTEnGx6kaH2USVrigpHKKXhs=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":10.94,"multihop_port":24501},{"hostname":"us-ga2.wg.ivpn.net","host":"107.150.22.77","public_key":"hr2uQOEGCvGeDkoCQJ2dCI8dM8Iu5aKhb1PIvJ9q72E=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":18.3,"multihop_port":24810}]},{"gateway":"us-il.wg.ivpn.net","country_code":"US","country":"United States","city":"Chicago, IL","latitude":41.8373,"longitude":-87.6862,"isp":"Datapacket","hosts":[{"hostname":"us-il1.wg.ivpn.net","host":"89.187.181.116","public_key":"hku9gjamhoii8OvxZgx+TdUDIkOAQYFu39qbav2AyUQ=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":29.71,"multihop_port":21410},{"hostname":"us-il01.wg.ivpn.net","host":"72.11.137.158","public_key":"Uy5a8JOqneAUY1dC5s9jubLnotbyIfBsLP2nZuzRbHs=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":9.46,"multihop_port":21401},{"hostname":"us-il2.wg.ivpn.net","host":"72.11.137.148","public_key":"ANhVUMAQgStPVNRHW8mg0ZtN1YI1QHyXfNCO8+USNQQ=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":18.84,"multihop_port":24901}]},{"gateway":"us-ny.wg.ivpn.net","country_code":"US","country":"United States","city":"New York, NY","latitude":40.6943,"longitude":-73.9249,"isp":"M247","hosts":[{"hostname":"us-ny1.wg.ivpn.net","host":"91.132.137.170","public_key":"6/tjvgb7HFl7UuvBSegolxa1zKr3iSlDrlCexCmhAGE=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":16.17,"multihop_port":21210},{"hostname":"us-ny2.wg.ivpn.net","host":"212.103.48.195","public_key":"c7DwY2uT+6ulWAJ5u8qJNWHroA0qyJLcdNzf/f2kkhs=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":8.44,"multihop_port":21801},{"hostname":"us-ny3.wg.ivpn.net","host":"89.187.178.145","public_key":"m5/Ssw9SN3WuE+yD/fAsH5G8iuI8TcDGEiZZnPgiMCc=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":13.07,"multihop_port":27601}]},{"gateway":"us-tx.wg.ivpn.net","country_code":"US","country":"United States","city":"Dallas, TX","latitude":32.7936,"longitude":-96.7662,"isp":"Quadranet","hosts":[{"hostname":"us-tx1.wg.ivpn.net","host":"198.55.124.114","public_key":"JPT1veXLmasj2uQDstX24mpR7VWD+GmV8JDkidkz91Q=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":7.1,"multihop_port":21010},{"hostname":"us-tx01.wg.ivpn.net","host":"96.44.189.197","public_key":"LvWf548mFddi8PTrIGL6uD1/l85LU8z0Rc8tpvw2Vls=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":18.58,"multihop_port":21001},{"hostname":"us-tx2.wg.ivpn.net","host":"96.44.142.77","public_key":"om8hOGUcEvoOhHvJZoBHxNF4jxY/+Ml9Iy1WOSC/pFo=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":12.02,"multihop_port":25001}]},{"gateway":"at.wg.ivpn.net","country_code":"AT","country":"Austria","city":"Vienna","latitude":48.2,"longitude":16.3666,"isp":"M247","hosts":[{"hostname":"at1.wg.ivpn.net","host":"185.244.212.69","public_key":"83LUBnP97SFpnS0y1MpEAFcg8MIiQJgW1FRv/8Mc40g=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":73.22,"multihop_port":25601}]},{"gateway":"au-nsw.wg.ivpn.net","country_code":"AU","country":"Australia","city":"Sydney","latitude":-33.92,"longitude":151.1852,"isp":"M247","hosts":[{"hostname":"au-nsw1.wg.ivpn.net","host":"46.102.153.246","public_key":"KmSrG48t5xw9CJCPlYLBG3JnmiY0CnUgyRM5TUEwZhM=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":89.82,"multihop_port":26601},{"hostname":"au-nsw2.wg.ivpn.net","host":"146.70.78.75","public_key":"q+wbp7GjiTszp5G16rNpGCqxkL0qSY3CH4pcgD6UsVQ=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":29.6,"multihop_port":27901}]},{"gateway":"be.wg.ivpn.net","country_code":"BE","country":"Belgium","city":"Brussels","latitude":50.8333,"longitude":4.3333,"isp":"M247","hosts":[{"hostname":"be1.wg.ivpn.net","host":"194.187.251.13","public_key":"awriP5lpdxEMWKuG+A1DOg+vb1M5jd3WhynIMB61BhU=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":67.17,"multihop_port":25701}]},{"gateway":"bg.wg.ivpn.net","country_code":"BG","country":"Bulgaria","city":"Sofia","latitude":42.6833,"longitude":23.3167,"isp":"M247","hosts":[{"hostname":"bg1.wg.ivpn.net","host":"82.102.23.21","public_key":"WDSsdJE6wvATIWfzQwayPtE/0DaXBQgW/hPm7sQSJmU=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":85.87,"multihop_port":25901}]},{"gateway":"br.wg.ivpn.net","country_code":"BR","country":"Brazil","city":"Franca","latitude":-20.53,"longitude":-47.39,"isp":"Qnax","hosts":[{"hostname":"br1.wg.ivpn.net","host":"45.162.229.133","public_key":"eN1f15S3YzRyYCALiPGRQcjkQO9xntcdqPhJJ6TOymc=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":27.83,"multihop_port":26701}]},{"gateway":"ca.wg.ivpn.net","country_code":"CA","country":"Canada","city":"Toronto","latitude":43.7,"longitude":-79.42,"isp":"Amanah","hosts":[{"hostname":"ca01.wg.ivpn.net","host":"104.254.90.181","public_key":"mdGnCZwinuOVGg46zsWnFhhenfFId6jht9GBTKB+xUA=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":36.88,"multihop_port":23801},{"hostname":"ca2.wg.ivpn.net","host":"172.86.186.173","public_key":"5qHV10ZbFgEGnF6wg9QpKeh1l6Di2JUG/5PdNaaoNW8=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":100,"multihop_port":22101}]},{"gateway":"cz.wg.ivpn.net","country_code":"CZ","country":"Czech Republic","city":"Prague","latitude":50.0833,"longitude":14.466,"isp":"Datapacket","hosts":[{"hostname":"cz1.wg.ivpn.net","host":"185.180.14.41","public_key":"gVbEq2cGRzwCSGPqT2oRSYYN+P6IK3uvvRffErASDSk=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":25.21,"multihop_port":25201}]},{"gateway":"dk.wg.ivpn.net","country_code":"DK","country":"Denmark","city":"Copenhagen","latitude":55.6786,"longitude":12.5635,"isp":"M247","hosts":[{"hostname":"dk1.wg.ivpn.net","host":"185.245.84.229","public_key":"jTsV5gOD7lT4egDj9rhKwO2OO2X7bKs2EQPcZEnUWDE=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":100,"multihop_port":25501}]},{"gateway":"es.wg.ivpn.net","country_code":"ES","country":"Spain","city":"Madrid","latitude":40.4,"longitude":-3.6834,"isp":"Datapacket","hosts":[{"hostname":"es1.wg.ivpn.net","host":"84.17.62.98","public_key":"w7umiArTtlJ4Pk6Ii9WX5VXK5vw/Qu+Z37/icKlIYWo=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":18.51,"multihop_port":21501}]},{"gateway":"fi.wg.ivpn.net","country_code":"FI","country":"Finland","city":"Helsinki","latitude":60.1756,"longitude":24.9341,"isp":"Creanova","hosts":[{"hostname":"fi1.wg.ivpn.net","host":"194.34.134.63","public_key":"mIxEzfjZ2wV6jJVj30w38ECd2LSH4bw/HLMnM2ICHiI=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":68.58,"multihop_port":26001}]},{"gateway":"fr.wg.ivpn.net","country_code":"FR","country":"France","city":"Paris","latitude":48.8667,"longitude":2.3333,"isp":"Datapacket","hosts":[{"hostname":"fr1.wg.ivpn.net","host":"185.246.211.185","public_key":"g7BuMzj3r/noLiLR4qhQMcvU6GSIY8RGEnaYtdYsFX4=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":26.88,"multihop_port":23401}]},{"gateway":"gb-man.wg.ivpn.net","country_code":"GB","country":"United Kingdom","city":"Manchester","latitude":53.5004,"longitude":-2.248,"isp":"M247","hosts":[{"hostname":"gb-man1.wg.ivpn.net","host":"89.238.141.231","public_key":"+hf4DYilNEIjTdSOuCNcWdqVyaRoxGzXw7wvNl7f7Rg=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":14.49,"multihop_port":26901}]},{"gateway":"hk.wg.ivpn.net","country_code":"HK","country":"Hong Kong","city":"Hong Kong","latitude":22.305,"longitude":114.185,"isp":"Leaseweb","hosts":[{"hostname":"hk2.wg.ivpn.net","host":"64.120.120.239","public_key":"kyolyq4cJydI3vQB2ESTIUAy2Fq0bpOf+Qe7GIq6XEA=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":10.84,"multihop_port":27501},{"hostname":"hk3.wg.ivpn.net","host":"118.107.244.206","public_key":"qq1simsFNm2FpZM0J8u8Aa0rkk5HEasvLksPyLv+0Sk=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":9.41,"multihop_port":20800}]},{"gateway":"hu.wg.ivpn.net","country_code":"HU","country":"Hungary","city":"Budapest","latitude":47.5,"longitude":19.0833,"isp":"M247","hosts":[{"hostname":"hu1.wg.ivpn.net","host":"185.189.114.189","public_key":"G30fNdXrnlqtqqOLF23QXWzFdLIKDxLW60HoYPvqml8=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":64.06,"multihop_port":25401}]},{"gateway":"il.wg.ivpn.net","country_code":"IL","country":"Israel","city":"Holon, Tel Aviv","latitude":32.08,"longitude":34.77,"isp":"HQServ","hosts":[{"hostname":"il01.wg.ivpn.net","host":"185.191.207.197","public_key":"HR9gAjpxXU3YVt6kehBw5n8yVYVE0iIgJdc4HTqOzEE=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":9.25,"multihop_port":27301}]},{"gateway":"is.wg.ivpn.net","country_code":"IS","country":"Iceland","city":"Reykjavik","latitude":64.15,"longitude":-21.95,"isp":"Advania","hosts":[{"hostname":"is1.wg.ivpn.net","host":"82.221.107.185","public_key":"nZZT6TlQ2dXlVe3P3B5ozEScHYMWH4JY4y3to8w5dz0=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":89.72,"multihop_port":23501}]},{"gateway":"jp.wg.ivpn.net","country_code":"JP","country":"Japan","city":"Tokyo","latitude":35.685,"longitude":139.7514,"isp":"M247","hosts":[{"hostname":"jp1.wg.ivpn.net","host":"91.207.174.237","public_key":"tb9WdC3LSho3d1rI5N7kfG9e42/d+u4LPVdEYERPsSQ=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":99.52,"multihop_port":26201},{"hostname":"jp2.wg.ivpn.net","host":"185.135.77.81","public_key":"YuhEd9+a90/+uucZC+qzsyMHkfe/GiwG1dq7g2HegXQ=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":5.74,"multihop_port":20830}]},{"gateway":"lu.wg.ivpn.net","country_code":"LU","country":"Luxembourg","city":"Luxembourg","latitude":49.6117,"longitude":6.13,"isp":"Evoluso","hosts":[{"hostname":"lu1.wg.ivpn.net","host":"92.223.89.57","public_key":"hUS1OAFLGwpba8+oc5mifYtohZt/RTro5dMyYBLYHjI=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":56.06,"multihop_port":27201}]},{"gateway":"my.wg.ivpn.net","country_code":"MY","country":"Malaysia","city":"Kuala Lumpur","latitude":3.1494,"longitude":101.706,"isp":"TheGigabit","hosts":[{"hostname":"my1.wg.ivpn.net","host":"61.4.97.153","public_key":"M9SsMCpUw7ad6YbqQr8r2saBK2zAf3tBj82DzsQjgkY=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":7.18,"multihop_port":20810}]},{"gateway":"no.wg.ivpn.net","country_code":"NO","country":"Norway","city":"Oslo","latitude":59.9167,"longitude":10.75,"isp":"Servetheworld","hosts":[{"hostname":"no1.wg.ivpn.net","host":"91.189.177.156","public_key":"xFO6ksbO3Gr05rRgAW0O5Veoi4bpTgz2G9RvtBzK7Cg=","local_ip":"172.16.0.1/12","ipv6":{},"load":0,"multihop_port":25301}]},{"gateway":"pl.wg.ivpn.net","country_code":"PL","country":"Poland","city":"Warsaw","latitude":52.25,"longitude":21,"isp":"Datapacket","hosts":[{"hostname":"pl1.wg.ivpn.net","host":"185.246.208.109","public_key":"1JDmF79rWj5C+kHp71AbdHne/yGaizWCd2bLfSFvYjo=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":47.62,"multihop_port":25101}]},{"gateway":"pt.wg.ivpn.net","country_code":"PT","country":"Portugal","city":"Lisbon","latitude":38.7227,"longitude":-9.1449,"isp":"Hostwebis","hosts":[{"hostname":"pt1.wg.ivpn.net","host":"94.46.175.113","public_key":"nMnA82YVrvEK80GVoY/0Z9McWeqjcLzuMYSL+86j5nU=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":7.48,"multihop_port":27101}]},{"gateway":"ro.wg.ivpn.net","country_code":"RO","country":"Romania","city":"Bucharest","latitude":44.4334,"longitude":26.0999,"isp":"M247","hosts":[{"hostname":"ro1.wg.ivpn.net","host":"37.120.206.53","public_key":"F2uQ57hysZTlw8WYELnyCw9Lga80wNYoYwkrrxyXKmw=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":100,"multihop_port":22301}]},{"gateway":"rs.wg.ivpn.net","country_code":"RS","country":"Serbia","city":"Belgrade","latitude":44.8186,"longitude":20.468,"isp":"M247","hosts":[{"hostname":"rs1.wg.ivpn.net","host":"141.98.103.253","public_key":"xLN/lpQThQ3z3tvYf7VqdAsRL/nton1Vhv2kCZlQtWE=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":51.18,"multihop_port":26801}]},{"gateway":"sk.wg.ivpn.net","country_code":"SK","country":"Slovakia","city":"Bratislava","latitude":48.15,"longitude":17.117,"isp":"M247","hosts":[{"hostname":"sk1.wg.ivpn.net","host":"185.245.85.253","public_key":"MOBWWpEgNsKbFj4BEyWSDFLlkBs5iUFiqdSdTFTDBko=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":96.78,"multihop_port":25801}]},{"gateway":"tw.wg.ivpn.net","country_code":"TW","country":"Taiwan","city":"Taipei","latitude":25.073,"longitude":121.578,"isp":"TheGigabit","hosts":[{"hostname":"tw1.wg.ivpn.net","host":"185.189.160.59","public_key":"fMTCCbbKqPp60fkqnaQvJ9mX2r6zBlt7xhUp8sGfJQY=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":9.56,"multihop_port":20820}]},{"gateway":"ua.wg.ivpn.net","country_code":"UA","country":"Ukraine","city":"Kharkiv","latitude":50,"longitude":36.25,"isp":"Xservers","hosts":[{"hostname":"ua1.wg.ivpn.net","host":"176.103.57.129","public_key":"mIxEzfjZ2wV6jJVj30w38ECd2LSH4bw/HLMnM2ICHiI=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":6.36,"multihop_port":26301}]},{"gateway":"us-az.wg.ivpn.net","country_code":"US","country":"United States","city":"Phoenix, AZ","latitude":33.5722,"longitude":-112.0891,"isp":"M247","hosts":[{"hostname":"us-az1.wg.ivpn.net","host":"193.37.254.133","public_key":"Ts4MGazxpxL9rrYbERjgxa+kCEX85ou9gHoaJvDsRiI=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":10.46,"multihop_port":26401}]},{"gateway":"us-fl.wg.ivpn.net","country_code":"US","country":"United States","city":"Miami, FL","latitude":25.7839,"longitude":-80.2102,"isp":"Quadranet","hosts":[{"hostname":"us-fl1.wg.ivpn.net","host":"173.44.49.93","public_key":"Rkzo9WgxJBiKyEbkZvqGWtOVh9Gk9Vd7wL49SHXdHig=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":17.91,"multihop_port":24601}]},{"gateway":"us-nj.wg.ivpn.net","country_code":"US","country":"United States","city":"New Jersey, NJ","latitude":40.737764,"longitude":-74.151747,"isp":"Quadranet","hosts":[{"hostname":"us-nj3.wg.ivpn.net","host":"23.226.128.21","public_key":"AX7C1LO0ECUcHRYgX4/tIDYdR8npvfB/+pf4AfI3OHU=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":6.7,"multihop_port":21610},{"hostname":"us-nj4.wg.ivpn.net","host":"194.36.111.54","public_key":"1Te4AfL1yKo2k4jzPALnRPfKE3YSzXKo4XIRHPz5FxI=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":6.96,"multihop_port":27401}]},{"gateway":"us-nv.wg.ivpn.net","country_code":"US","country":"United States","city":"Las Vegas, NV","latitude":36.2333,"longitude":-115.2654,"isp":"M247","hosts":[{"hostname":"us-nv1.wg.ivpn.net","host":"185.242.5.37","public_key":"PRpvAZyoNWNm/KHlqafjtYoZtn1PkIPylUE4WbuYmgM=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":44.29,"multihop_port":26501}]},{"gateway":"us-ut.wg.ivpn.net","country_code":"US","country":"United States","city":"Salt Lake City, UT","latitude":40.7774,"longitude":-111.93,"isp":"100TB","hosts":[{"hostname":"us-ut1.wg.ivpn.net","host":"206.190.145.92","public_key":"KirI7bpxD186CuYiOqNHF+QUe6YmRYf6CN3pXWOJT2k=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":47.09,"multihop_port":24401}]},{"gateway":"us-va.wg.ivpn.net","country_code":"US","country":"United States","city":"Ashburn, VA","latitude":39.0437,"longitude":-77.4875,"isp":"Datapacket","hosts":[{"hostname":"us-va1.wg.ivpn.net","host":"37.19.206.106","public_key":"ZCnZK6U+cRuP/WgzIDb/P6UG2rX/KyCRd5vJ1hAbr2E=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":98.5,"multihop_port":27701}]},{"gateway":"us-wa.wg.ivpn.net","country_code":"US","country":"United States","city":"Seattle, WA","latitude":47.6211,"longitude":-122.3244,"isp":"Tzulo","hosts":[{"hostname":"us-wa2.wg.ivpn.net","host":"198.44.131.4","public_key":"VcrOOozBUCIURU0AnqMAE7AkMmC7Qrp+j/PzPbgbalU=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":23.68,"multihop_port":27801}]},{"gateway":"za.wg.ivpn.net","country_code":"ZA","country":"South Africa","city":"Johannesburg","latitude":-26.195,"longitude":28.034,"isp":"Datapacket","hosts":[{"hostname":"za1.wg.ivpn.net","host":"169.150.238.108","public_key":"tgrAA+uJZppS9esgOi0pe3rHajQQ7c/KF8WPOua6qy4=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":11.1,"multihop_port":20840}]}],"openvpn":[{"gateway":"at.gw.ivpn.net","country_code":"AT","country":"Austria","city":"Vienna","latitude":48.2,"longitude":16.3666,"isp":"M247","hosts":[{"hostname":"at1.gw.ivpn.net","host":"185.244.212.66","load":73.22,"multihop_port":25601,"obfs":{"obfs3_multihop_port":25602,"obfs4_multihop_port":25603,"obfs4_key":"75HhQC6n6ctp9Fa9wCvEnc6ip5FnEfuIGc+dVNLH4M15FjB/Ve6bI1b8lYFhk6T+4/HkDQ"}}]},{"gateway":"au-nsw.gw.ivpn.net","country_code":"AU","country":"Australia","city":"Sydney","latitude":-33.92,"longitude":151.1852,"isp":"M247","hosts":[{"hostname":"au-nsw1.gw.ivpn.net","host":"46.102.153.242","load":89.82,"multihop_port":26601,"obfs":{"obfs3_multihop_port":26602,"obfs4_multihop_port":26603,"obfs4_key":"/rjoeDjduOFq1UvT332vhS398h1RP5hC3m7sDJKNSyJ6TO8mkcxWAYILw0i+bgS/3JD5YA"}},{"hostname":"au-nsw2.gw.ivpn.net","host":"146.70.78.74","load":29.6,"multihop_port":27901,"obfs":{"obfs3_multihop_port":27902,"obfs4_multihop_port":27903,"obfs4_key":"qtdQ5krD9EQFR98xNo/v5cmGb10wqt0Om9pYMIHWQh4oz5xcAXj32rViEyN0bnkkhaZnBA"}}]},{"gateway":"be.gw.ivpn.net","country_code":"BE","country":"Belgium","city":"Brussels","latitude":50.8333,"longitude":4.3333,"isp":"M247","hosts":[{"hostname":"be1.gw.ivpn.net","host":"194.187.251.10","load":67.17,"multihop_port":25701,"obfs":{"obfs3_multihop_port":25702,"obfs4_multihop_port":25703,"obfs4_key":"cN8i60FUVy2mmGpy+tkQAz8hu/N0EGPq8cZwIotEDwdhAYdLV+ATes/AEjzdub2K68TlYg"}}]},{"gateway":"bg.gw.ivpn.net","country_code":"BG","country":"Bulgaria","city":"Sofia","latitude":42.6833,"longitude":23.3167,"isp":"M247","hosts":[{"hostname":"bg1.gw.ivpn.net","host":"82.102.23.18","load":85.87,"multihop_port":25901,"obfs":{"obfs3_multihop_port":25902,"obfs4_multihop_port":25903,"obfs4_key":"K+mCw9+zy/8pBQt6IUKRlg2eJ3DCnJ1BvIccLq/6A2D6HoZddyDnZQYb2Sb2e464dVgBWw"}}]},{"gateway":"br.gw.ivpn.net","country_code":"BR","country":"Brazil","city":"Franca","latitude":-20.53,"longitude":-47.39,"isp":"Qnax","hosts":[{"hostname":"br1.gw.ivpn.net","host":"45.162.229.130","load":27.83,"multihop_port":26701,"obfs":{"obfs3_multihop_port":26702,"obfs4_multihop_port":26703,"obfs4_key":"h4bBkocahWveuv/nWPRMYXBTw95ExTiXwmoydkNlV6hgfy8/ZjaKc34rqTuOyOH+CK7OZw"}}]},{"gateway":"ca-qc.gw.ivpn.net","country_code":"CA","country":"Canada","city":"Montreal","latitude":45.5,"longitude":-73.5833,"isp":"M247","hosts":[{"hostname":"ca-qc1.gw.ivpn.net","host":"87.101.92.26","load":56.01,"multihop_port":27001,"obfs":{"obfs3_multihop_port":27002,"obfs4_multihop_port":27003,"obfs4_key":"E97qdaar8flBavysmvdIui7WQEFKkbmNi/ITTo8bPLvv39vnaBRzFj4vjMwpzXV2jXEGRQ"}}]},{"gateway":"ca.gw.ivpn.net","country_code":"CA","country":"Canada","city":"Toronto","latitude":43.7,"longitude":-79.42,"isp":"Amanah","hosts":[{"hostname":"ca1.gw.ivpn.net","host":"104.254.90.178","load":36.88,"multihop_port":23801,"obfs":{"obfs3_multihop_port":23802,"obfs4_multihop_port":23803,"obfs4_key":"49WDCCK1QGpOTSEVflRzIIaOdCT1BLB3jr/yNUJsbaUFrq7NuoB1E3wAdArNxQq9p5otTw"}},{"hostname":"ca2.gw.ivpn.net","host":"172.86.186.170","load":100,"multihop_port":22101,"obfs":{"obfs3_multihop_port":22102,"obfs4_multihop_port":22103,"obfs4_key":"xNkQbCkA0VCe2i4WvS2CPq2eBSMydIjsHH36E+Yg0C+gOPo3SwZyN51kpB+kwsYVS32fOQ"}}]},{"gateway":"ch.gw.ivpn.net","country_code":"CH","country":"Switzerland","city":"Zurich","latitude":47.38,"longitude":8.55,"isp":"M247","hosts":[{"hostname":"ch1.gw.ivpn.net","host":"185.212.170.138","load":34.61,"multihop_port":23601,"obfs":{"obfs3_multihop_port":23602,"obfs4_multihop_port":23603,"obfs4_key":"ELnV4JNKu0vUNd3J+QDn64yfZtqM0hNN6O5n6RkDLHbeSDBZmxP1N4dlwwChV/uySX+DEQ"}},{"hostname":"ch3.gw.ivpn.net","host":"141.255.166.194","load":17.67,"multihop_port":22901,"obfs":{"obfs3_multihop_port":22902,"obfs4_multihop_port":22903,"obfs4_key":"oNaH5sHCPGGk5m3/VMOrTDL+m1qsJrze+bqDs78vhOYBpjx5Jjq5TXu1dXNfDJCKNmKnUA"}}]},{"gateway":"cz.gw.ivpn.net","country_code":"CZ","country":"Czech Republic","city":"Prague","latitude":50.0833,"longitude":14.466,"isp":"Datapacket","hosts":[{"hostname":"cz1.gw.ivpn.net","host":"195.181.160.167","load":25.21,"multihop_port":25201,"obfs":{"obfs3_multihop_port":25202,"obfs4_multihop_port":25203,"obfs4_key":"JZ3PtIyflM3VwVow2vqi08OxddOWSx9j6B6yZSGoZLs9QE0hzSAj3ZBWEsCKFeQ2RcAoCQ"}}]},{"gateway":"de.gw.ivpn.net","country_code":"DE","country":"Germany","city":"Frankfurt","latitude":50.1,"longitude":8.675,"isp":"Leaseweb","hosts":[{"hostname":"de1.gw.ivpn.net","host":"178.162.222.40","load":21.32,"multihop_port":23001,"obfs":{"obfs3_multihop_port":23002,"obfs4_multihop_port":23003,"obfs4_key":"PBhcWVcRNOCTMSWXbA2J+8eJBVzNd9H5HOr0YCF8QWwmKeSlEmqLSJQE8oDpKH5IbFH4Mw"}},{"hostname":"de2.gw.ivpn.net","host":"178.162.211.114","load":20.1,"multihop_port":22001,"obfs":{"obfs3_multihop_port":22002,"obfs4_multihop_port":22003,"obfs4_key":"dEhLA4ZsvVP8+PRvlSHKwmW8JyzR1Bwy7+BFKF7Ux4L2B5YvdqqOrv/8eHliEj2mm2Z8Iw"}}]},{"gateway":"dk.gw.ivpn.net","country_code":"DK","country":"Denmark","city":"Copenhagen","latitude":55.6786,"longitude":12.5635,"isp":"M247","hosts":[{"hostname":"dk1.gw.ivpn.net","host":"185.245.84.226","load":100,"multihop_port":25501,"obfs":{"obfs3_multihop_port":25502,"obfs4_multihop_port":25503,"obfs4_key":"ngjtv9UISX4tB5AkBnrEN2TrAnUqVwNZ688VqDlS4BDxQXJeJF3ynZtngRqeowhEahsccQ"}}]},{"gateway":"es.gw.ivpn.net","country_code":"ES","country":"Spain","city":"Madrid","latitude":40.4,"longitude":-3.6834,"isp":"Datapacket","hosts":[{"hostname":"es1.gw.ivpn.net","host":"185.93.3.193","load":18.51,"multihop_port":21501,"obfs":{"obfs3_multihop_port":21502,"obfs4_multihop_port":21503,"obfs4_key":"x4A9SBY5yzPKH1QOkEsGYcIR2JA/Pu393jv/ZSg4YO2DsVhr3TQFxcMO3QhD9iSF48smJA"}}]},{"gateway":"fi.gw.ivpn.net","country_code":"FI","country":"Finland","city":"Helsinki","latitude":60.1756,"longitude":24.9341,"isp":"Creanova","hosts":[{"hostname":"fi1.gw.ivpn.net","host":"185.112.82.12","load":68.58,"multihop_port":26001,"obfs":{"obfs3_multihop_port":26002,"obfs4_multihop_port":26003,"obfs4_key":"SvvSpGiFctjs4n2wZGnZUf9fAL8wag70SrA3FX+9f3Sq+KgBn+/8P6fE3239ezemg9svLA"}}]},{"gateway":"fr.gw.ivpn.net","country_code":"FR","country":"France","city":"Paris","latitude":48.8667,"longitude":2.3333,"isp":"Datapacket","hosts":[{"hostname":"fr1.gw.ivpn.net","host":"185.246.211.179","load":26.88,"multihop_port":23401,"obfs":{"obfs3_multihop_port":23402,"obfs4_multihop_port":23403,"obfs4_key":"CMf0pNZ46nFdG0Tpa3hE0cK9wtUAReJL7HN66G9Jq3tlrTSWrU0DLf7sCQgXV+WFoc8iaw"}}]},{"gateway":"gb-man.gw.ivpn.net","country_code":"GB","country":"United Kingdom","city":"Manchester","latitude":53.5004,"longitude":-2.248,"isp":"M247","hosts":[{"hostname":"gb-man1.gw.ivpn.net","host":"89.238.141.228","load":14.49,"multihop_port":26901,"obfs":{"obfs3_multihop_port":26902,"obfs4_multihop_port":26903,"obfs4_key":"WuP3pMrsQA+uAC72sV+Y62E1uvOWcnbTNJCmRHXqtWbbYzECF5swu62dzD/JOKoa5t0tGQ"}}]},{"gateway":"gb.gw.ivpn.net","country_code":"GB","country":"United Kingdom","city":"London","latitude":51.5,"longitude":-0.1167,"isp":"Datapacket","hosts":[{"hostname":"gb1.gw.ivpn.net","host":"185.59.221.133","load":35.8,"multihop_port":20801,"obfs":{"obfs3_multihop_port":20802,"obfs4_multihop_port":20803,"obfs4_key":"5whTESvZxQE28wKqJWZ9fQFy09d8//GNaPDeYHVR+FJZTyI+DMS6qX4Mt2FKSf/zBToaDg"}},{"hostname":"gb2.gw.ivpn.net","host":"185.59.221.88","load":5.58,"multihop_port":24201,"obfs":{"obfs3_multihop_port":24202,"obfs4_multihop_port":24203,"obfs4_key":"4hrCOgPsRfmnQ6cLvV5mtM2XZCRS/DXG/izYSg2qZC+WJ0GsnhTlKByPM2iJ555MaM8vJA"}}]},{"gateway":"hk.gw.ivpn.net","country_code":"HK","country":"Hong Kong","city":"Hong Kong","latitude":22.305,"longitude":114.185,"isp":"Leaseweb","hosts":[{"hostname":"hk2.gw.ivpn.net","host":"209.58.188.13","load":10.84,"multihop_port":27501,"obfs":{"obfs3_multihop_port":27502,"obfs4_multihop_port":27503,"obfs4_key":"GIunWjiq00Piv3Xf4VeMkmUQ8NzD8sxRkSIbA3bxrU4LhPVth+3qM2zQwI4GesQDCDY5RA"}},{"hostname":"hk3.gw.ivpn.net","host":"118.107.244.184","load":9.41,"multihop_port":20800,"obfs":{"obfs3_multihop_port":20801,"obfs4_multihop_port":20802,"obfs4_key":"wnI0gz3hLM9VhkABSncBIsGERgn16UzTxkj7dEeYYo/y2Wu0/w24rfriA5KL7ugpTyvEFg"}}]},{"gateway":"hu.gw.ivpn.net","country_code":"HU","country":"Hungary","city":"Budapest","latitude":47.5,"longitude":19.0833,"isp":"M247","hosts":[{"hostname":"hu1.gw.ivpn.net","host":"185.189.114.186","load":64.06,"multihop_port":25401,"obfs":{"obfs3_multihop_port":25402,"obfs4_multihop_port":25403,"obfs4_key":"2TwZqxAakni0S4S3ulOIqMZimqqug0KCr6pNREN6KytrtIh486nkJyiFRqaYZlx+FlxEcg"}}]},{"gateway":"il.gw.ivpn.net","country_code":"IL","country":"Israel","city":"Holon, Tel Aviv","latitude":32.08,"longitude":34.77,"isp":"HQServ","hosts":[{"hostname":"il1.gw.ivpn.net","host":"185.191.207.194","load":9.25,"multihop_port":27301,"obfs":{"obfs3_multihop_port":27302,"obfs4_multihop_port":27303,"obfs4_key":"DysuSM7UWjquj4BAVYf6mOUnRKfY1QXs17MXiV5aYapFfOkQPpx5nQPVQ2M+rLDxN9TSRg"}}]},{"gateway":"is.gw.ivpn.net","country_code":"IS","country":"Iceland","city":"Reykjavik","latitude":64.15,"longitude":-21.95,"isp":"Advania","hosts":[{"hostname":"is1.gw.ivpn.net","host":"82.221.107.178","load":89.72,"multihop_port":23501,"obfs":{"obfs3_multihop_port":23502,"obfs4_multihop_port":23503,"obfs4_key":"xx/Lor3q60/pVh4PKWi0GZaw64pPXFTkALnGlvRaBMiPRkFilr7KhYmPInDnZxzr4c4AIw"}}]},{"gateway":"it.gw.ivpn.net","country_code":"IT","country":"Italy","city":"Milan","latitude":45.47,"longitude":9.205,"isp":"SEFlow","hosts":[{"hostname":"it1.gw.ivpn.net","host":"158.58.172.73","load":53.23,"multihop_port":24301,"obfs":{"obfs3_multihop_port":24302,"obfs4_multihop_port":24303,"obfs4_key":"yJXSlTJloo6tlAxf0MI9Bu4schtcL2NlVqMPgbrhnrT/1pTZUaOUH7F7nLbVe4JSCSyeZg"}}]},{"gateway":"jp.gw.ivpn.net","country_code":"JP","country":"Japan","city":"Tokyo","latitude":35.685,"longitude":139.7514,"isp":"M247","hosts":[{"hostname":"jp1.gw.ivpn.net","host":"91.207.174.234","load":99.52,"multihop_port":26201,"obfs":{"obfs3_multihop_port":26202,"obfs4_multihop_port":26203,"obfs4_key":"CBqi0EpfoUzP/ijwYn9A9yEpItrhtX+BAKF0rvcUGv///UNzzXKNz+0RhBLdQayZx6y6TA"}},{"hostname":"jp2.gw.ivpn.net","host":"185.135.77.35","load":5.74,"multihop_port":20830,"obfs":{"obfs3_multihop_port":20831,"obfs4_multihop_port":20832,"obfs4_key":"giZJF4edg7wcjxbdgD2RjFcF9QAzExLHIJYjm2cLLtx7MrxP0p96mIFj9T8LSQotKB63fA"}}]},{"gateway":"lu.gw.ivpn.net","country_code":"LU","country":"Luxembourg","city":"Luxembourg","latitude":49.6117,"longitude":6.13,"isp":"Evoluso","hosts":[{"hostname":"lu1.gw.ivpn.net","host":"92.223.89.53","load":56.06,"multihop_port":27201,"obfs":{"obfs3_multihop_port":27202,"obfs4_multihop_port":27203,"obfs4_key":"auDmK8lVBI7fxq6UjXg7V0qoZJ3icACKm/9vruMGSr0lT6ViNsl28qMqYbjJRveHnx5eQw"}}]},{"gateway":"my.gw.ivpn.net","country_code":"MY","country":"Malaysia","city":"Kuala Lumpur","latitude":3.1494,"longitude":101.706,"isp":"TheGigabit","hosts":[{"hostname":"my1.gw.ivpn.net","host":"61.4.97.148","load":7.18,"multihop_port":20810,"obfs":{"obfs3_multihop_port":20811,"obfs4_multihop_port":20812,"obfs4_key":"k2hwCe8gDb24K5n2jNXYO5YCl5XCIEhuRwpYSsEhKWorah8OUM1C1crawbfstj+W2IQdOA"}}]},{"gateway":"nl.gw.ivpn.net","country_code":"NL","country":"Netherlands","city":"Amsterdam","latitude":52.35,"longitude":4.9166,"isp":"Leaseweb","hosts":[{"hostname":"nl3.gw.ivpn.net","host":"95.211.172.68","load":8.14,"multihop_port":23101,"obfs":{"obfs3_multihop_port":23102,"obfs4_multihop_port":23103,"obfs4_key":"eUXsScIg0K0LKVgA8yq2Mc++pfnTQ9nr3gnV8n1NIw7wRqinhO6uuXiSS5J19agQaPK1ew"}},{"hostname":"nl4.gw.ivpn.net","host":"95.211.172.95","load":8.7,"multihop_port":23201,"obfs":{"obfs3_multihop_port":23202,"obfs4_multihop_port":23203,"obfs4_key":"KEw6WJF+NDOQv7yMvq+dAAAkPbcYJ8PX6pffRd8EM3uaOy2QcpMcdHI7s700Kq/ZvV3HBQ"}},{"hostname":"nl5.gw.ivpn.net","host":"95.211.187.222","load":22.5,"multihop_port":23901,"obfs":{"obfs3_multihop_port":23902,"obfs4_multihop_port":23903,"obfs4_key":"fcWCrzzatLbk1LNKsuQZKpQrC3ZXwQ85GO5xRS467KJBRDrmvnyMb6ARbLGu+gYkTnNELQ"}},{"hostname":"nl6.gw.ivpn.net","host":"95.211.187.228","load":56.84,"multihop_port":24101,"obfs":{"obfs3_multihop_port":24102,"obfs4_multihop_port":24103,"obfs4_key":"lT3OGPQS1CwwqtalMExN7qxEoDDBLLlcoh5a6YW3DPj8esEBEG6wY5OfYonxltBzoVu4PA"}},{"hostname":"nl7.gw.ivpn.net","host":"95.211.95.22","load":13.54,"multihop_port":22501,"obfs":{"obfs3_multihop_port":22502,"obfs4_multihop_port":22503,"obfs4_key":"ffMnq7Gc/D7KWoLckJ4t8nf3zZqVdlffe2lVfUCceOyOTdRApkeJGgEai0TI1z76Yey9Lw"}},{"hostname":"nl8.gw.ivpn.net","host":"95.211.172.18","load":10.4,"multihop_port":22801,"obfs":{"obfs3_multihop_port":22802,"obfs4_multihop_port":22803,"obfs4_key":"h+u/6VkPDJXySoJ6QEM1hOjWPD58OS4AZPP9ofP/yCWGBQpPoMc78l7C74eFvqKKkFAXDw"}}]},{"gateway":"no.gw.ivpn.net","country_code":"NO","country":"Norway","city":"Oslo","latitude":59.9167,"longitude":10.75,"isp":"Servetheworld","hosts":[{"hostname":"no1.gw.ivpn.net","host":"194.242.10.150","load":12.89,"multihop_port":25301,"obfs":{"obfs3_multihop_port":25302,"obfs4_multihop_port":25303,"obfs4_key":"uhLy//KRu6DrYfgDJmwjC6Fxk5h+MRDNOwFe7qzGTjfOiHLWRSoRx6OdNvzjPPXq0ZJnZg"}}]},{"gateway":"pl.gw.ivpn.net","country_code":"PL","country":"Poland","city":"Warsaw","latitude":52.25,"longitude":21,"isp":"Datapacket","hosts":[{"hostname":"pl1.gw.ivpn.net","host":"185.246.208.86","load":47.62,"multihop_port":25101,"obfs":{"obfs3_multihop_port":25102,"obfs4_multihop_port":25103,"obfs4_key":"S9XZyigxYjF5jWcwYpMmV9HJq4Vni1yQvLKI03n0TJ7csrgsX7lPpFfECAGQruHh1wkMXg"}}]},{"gateway":"pt.gw.ivpn.net","country_code":"PT","country":"Portugal","city":"Lisbon","latitude":38.7227,"longitude":-9.1449,"isp":"Hostwebis","hosts":[{"hostname":"pt1.gw.ivpn.net","host":"94.46.175.112","load":7.48,"multihop_port":27101,"obfs":{"obfs3_multihop_port":27102,"obfs4_multihop_port":27103,"obfs4_key":"NqXqKMt8UF25hYDIwfh2K4Rr4a7F41HzZDGjX7SYwRaoOtrTL665yV6Z3h9wF+/R1YE8KQ"}}]},{"gateway":"ro.gw.ivpn.net","country_code":"RO","country":"Romania","city":"Bucharest","latitude":44.4334,"longitude":26.0999,"isp":"M247","hosts":[{"hostname":"ro1.gw.ivpn.net","host":"37.120.206.50","load":100,"multihop_port":22301,"obfs":{"obfs3_multihop_port":22302,"obfs4_multihop_port":22303,"obfs4_key":"lqfg/sP9uLakoQiA6YI5/kHQ4/FvQTp6jRgxSswjHwC8POOM23FijEWKyykngn1Eb3xfLA"}}]},{"gateway":"rs.gw.ivpn.net","country_code":"RS","country":"Serbia","city":"Belgrade","latitude":44.8186,"longitude":20.468,"isp":"M247","hosts":[{"hostname":"rs1.gw.ivpn.net","host":"141.98.103.250","load":51.18,"multihop_port":26801,"obfs":{"obfs3_multihop_port":26802,"obfs4_multihop_port":26803,"obfs4_key":"0MqgxLrLFQTlQWGAjY9es+Nv45d37/5lulWw0iEFoiUvtzOcbut8hK9AhCis17EXi+lUXw"}}]},{"gateway":"se.gw.ivpn.net","country_code":"SE","country":"Sweden","city":"Stockholm","latitude":59.3508,"longitude":18.0973,"isp":"GleSyS","hosts":[{"hostname":"se1.gw.ivpn.net","host":"80.67.10.138","load":5.41,"multihop_port":24001,"obfs":{"obfs3_multihop_port":24002,"obfs4_multihop_port":24003,"obfs4_key":"/yhTV2SsTXlwsG2mCS90ZAYIZivsSyloaFw6VDj0pnpJOuUxXcZBOgdyQ/lfWxJtvHZmPg"}}]},{"gateway":"sg.gw.ivpn.net","country_code":"SG","country":"Singapore","city":"Singapore","latitude":1.293,"longitude":103.8558,"isp":"M247","hosts":[{"hostname":"sg1.gw.ivpn.net","host":"185.128.24.186","load":100,"multihop_port":26101,"obfs":{"obfs3_multihop_port":26102,"obfs4_multihop_port":26103,"obfs4_key":"0N1ZmZlnyhS/3Y1OhvB0Bk3BGU2LFy0zyuWPYwM/P+mfX57w8zI7/YcBfIXZVgDiStt4MQ"}}]},{"gateway":"sk.gw.ivpn.net","country_code":"SK","country":"Slovakia","city":"Bratislava","latitude":48.15,"longitude":17.117,"isp":"M247","hosts":[{"hostname":"sk1.gw.ivpn.net","host":"185.245.85.250","load":96.78,"multihop_port":25801,"obfs":{"obfs3_multihop_port":25802,"obfs4_multihop_port":25803,"obfs4_key":"8sl7oPfNHdCd2xYi98xWC6mBXyPvzio34UbfUbCPEU+8wo7DVbrR9mf8goR0Ievqzax4Hw"}}]},{"gateway":"tw.gw.ivpn.net","country_code":"TW","country":"Taiwan","city":"Taipei","latitude":25.073,"longitude":121.578,"isp":"TheGigabit","hosts":[{"hostname":"tw1.gw.ivpn.net","host":"185.189.160.6","load":9.56,"multihop_port":20820,"obfs":{"obfs3_multihop_port":20821,"obfs4_multihop_port":20822,"obfs4_key":"/ilRlB3BkUzUG2yd+++6KeYEF9PEB+3T3XcQhVjfoF8wJH1kgjGXvikjS1j/1SknFpGPaA"}}]},{"gateway":"ua.gw.ivpn.net","country_code":"UA","country":"Ukraine","city":"Kharkiv","latitude":50,"longitude":36.25,"isp":"Xservers","hosts":[{"hostname":"ua1.gw.ivpn.net","host":"176.103.58.123","load":6.36,"multihop_port":26301,"obfs":{"obfs3_multihop_port":26302,"obfs4_multihop_port":26303,"obfs4_key":"RsW3q+FmLATkKnKHOheUntwvslkrXEiCBx3ajDjhyHiZMtQI+Uy7TmhFK0YaHg/qKoljYw"}}]},{"gateway":"us-az.gw.ivpn.net","country_code":"US","country":"United States","city":"Phoenix, AZ","latitude":33.5722,"longitude":-112.0891,"isp":"M247","hosts":[{"hostname":"us-az1.gw.ivpn.net","host":"193.37.254.130","load":10.46,"multihop_port":26401,"obfs":{"obfs3_multihop_port":26402,"obfs4_multihop_port":26403,"obfs4_key":"Y2klMvUi3NBIReXSALaKnNm8qI9IdWhFwgQrwl9ofUuVNuT6D93ohTTqbW//iKS5/lqndw"}}]},{"gateway":"us-ca.gw.ivpn.net","country_code":"US","country":"United States","city":"Los Angeles, CA","latitude":34.1139,"longitude":-118.4068,"isp":"Quadranet","hosts":[{"hostname":"us-ca1.gw.ivpn.net","host":"173.254.196.58","load":8.35,"multihop_port":22201,"obfs":{"obfs3_multihop_port":22202,"obfs4_multihop_port":22203,"obfs4_key":"C/Ct5AG8tkV0Yi7MnXv+bNvExY8Dgii4OeJ7DA7lIq3HptUa+WbG+IAR/UIDaMn8VeXDIg"}},{"hostname":"us-ca2.gw.ivpn.net","host":"69.12.80.146","load":9.24,"multihop_port":22401,"obfs":{"obfs3_multihop_port":22402,"obfs4_multihop_port":22403,"obfs4_key":"nvizO9jj1D3xMqCMTnP6XDIzTLy6KcYNsDSxvJjm5QwZ9Y+5gommAk9LIcCeX9o4FjlkVA"}},{"hostname":"us-ca3.gw.ivpn.net","host":"198.54.129.99","load":15.52,"multihop_port":21301,"obfs":{"obfs3_multihop_port":21302,"obfs4_multihop_port":21303,"obfs4_key":"rharPyhkUzYwmCP8ZzeaCwnJAmwOqwDTLOlJqzi/kAxMn4OVXtbzCk9Ww8XR31D2rmk3MA"}},{"hostname":"us-ca4.gw.ivpn.net","host":"173.254.204.202","load":14.3,"multihop_port":21901,"obfs":{"obfs3_multihop_port":21902,"obfs4_multihop_port":21903,"obfs4_key":"2Sdam2VI658TdQRvj5bxwKPFljrsKgwBDv/mqhXNYgDQGs+WVhAz508b2oWVgHed3Ki8dQ"}}]},{"gateway":"us-fl.gw.ivpn.net","country_code":"US","country":"United States","city":"Miami, FL","latitude":25.7839,"longitude":-80.2102,"isp":"Quadranet","hosts":[{"hostname":"us-fl1.gw.ivpn.net","host":"173.44.49.90","load":17.91,"multihop_port":24601,"obfs":{"obfs3_multihop_port":24602,"obfs4_multihop_port":24603,"obfs4_key":"vS7u4rijvWECRS/Mo2KOpl8kx0NOozp3kF/FMxGmCZILE46etitrheYI2hCpNsYUg3YTBQ"}}]},{"gateway":"us-ga.gw.ivpn.net","country_code":"US","country":"United States","city":"Atlanta, GA","latitude":33.7627,"longitude":-84.4225,"isp":"Quadranet","hosts":[{"hostname":"us-ga1.gw.ivpn.net","host":"104.129.24.146","load":10.94,"multihop_port":24501,"obfs":{"obfs3_multihop_port":24502,"obfs4_multihop_port":24503,"obfs4_key":"+No53UtdyPN4uT89vMlvRTjFnxtMKol+oOld9I9WMnlK7BU+y10oXWofzcI4eRgkm195FQ"}},{"hostname":"us-ga2.gw.ivpn.net","host":"107.150.22.74","load":18.3,"multihop_port":24810,"obfs":{"obfs3_multihop_port":24811,"obfs4_multihop_port":24812,"obfs4_key":"3viWXkxZx1KZF9uzd5tRxb5lNMMnRZ90PqwLUaZdkZuaouj+Vb20t5uk3BVz/YyZkrSoGw"}}]},{"gateway":"us-il.gw.ivpn.net","country_code":"US","country":"United States","city":"Chicago, IL","latitude":41.8373,"longitude":-87.6862,"isp":"Quadranet","hosts":[{"hostname":"us-il1.gw.ivpn.net","host":"107.150.28.82","load":9.46,"multihop_port":21401,"obfs":{"obfs3_multihop_port":21402,"obfs4_multihop_port":21403,"obfs4_key":"6PpxOt8CwINAjun8o/wsf/cAidNbJZM/Pg4im1Cx9kCBV/lau3XFq3bMwBW0SzptZ/5WdA"}},{"hostname":"us-il2.gw.ivpn.net","host":"72.11.137.146","load":18.84,"multihop_port":24901,"obfs":{"obfs3_multihop_port":24902,"obfs4_multihop_port":24903,"obfs4_key":"cfvJR/gplqAbFx8myJSW/cPC3hN1782PPma2v5YKJR5LyekWX+AHTJjzdz8xSr2mM6IGQw"}}]},{"gateway":"us-nj.gw.ivpn.net","country_code":"US","country":"United States","city":"New Jersey, NJ","latitude":40.737764,"longitude":-74.151747,"isp":"Quadranet","hosts":[{"hostname":"us-nj3.gw.ivpn.net","host":"23.226.128.18","load":6.7,"multihop_port":21610,"obfs":{"obfs3_multihop_port":21611,"obfs4_multihop_port":21612,"obfs4_key":"JtB/8Lv8MOq9+bpYC751voS2CwoEwf9ku2CziwJScn3Gc1F/BTul6ehnrGiedmpkQXAtJw"}},{"hostname":"us-nj4.gw.ivpn.net","host":"194.36.111.50","load":6.96,"multihop_port":27401,"obfs":{"obfs3_multihop_port":27402,"obfs4_multihop_port":27403,"obfs4_key":"lHDLT6cTgt0bh3ysogDdiXL4eWGRtJZ0cZGJmvpeK0YLUAsBfsOzKv46V0iv5ykwRPm6Mg"}}]},{"gateway":"us-nv.gw.ivpn.net","country_code":"US","country":"United States","city":"Las Vegas, NV","latitude":36.2333,"longitude":-115.2654,"isp":"M247","hosts":[{"hostname":"us-nv1.gw.ivpn.net","host":"185.242.5.34","load":44.29,"multihop_port":26501,"obfs":{"obfs3_multihop_port":26502,"obfs4_multihop_port":26503,"obfs4_key":"Kux7OTSrUOkklATpG67hJPz7xCWpW6eD9Qdw2GNKAmaaPA5zvPC3SXQRvyrYdyAAqMXMfg"}}]},{"gateway":"us-ny.gw.ivpn.net","country_code":"US","country":"United States","city":"New York, NY","latitude":40.6943,"longitude":-73.9249,"isp":"M247","hosts":[{"hostname":"us-ny2.gw.ivpn.net","host":"212.103.48.194","load":8.44,"multihop_port":21801,"obfs":{"obfs3_multihop_port":21802,"obfs4_multihop_port":21803,"obfs4_key":"GtK9yCXTtrLOrf0MTWdWqjzQ1vd1eKsVjiOuR5CiVaDf4dCSalyJ+kmik4nfU9Gj6UwcCg"}},{"hostname":"us-ny3.gw.ivpn.net","host":"89.187.178.144","load":13.07,"multihop_port":27601,"obfs":{"obfs3_multihop_port":27602,"obfs4_multihop_port":27603,"obfs4_key":"eL/9iKx6f5bHowNJBsT2E/Aag5w8Cvtb3DduySudJmuK2GtIOUwNzS33lFZwSUEnJIYwLg"}}]},{"gateway":"us-tx.gw.ivpn.net","country_code":"US","country":"United States","city":"Dallas, TX","latitude":32.7936,"longitude":-96.7662,"isp":"Quadranet","hosts":[{"hostname":"us-tx1.gw.ivpn.net","host":"96.44.189.194","load":18.58,"multihop_port":21001,"obfs":{"obfs3_multihop_port":21002,"obfs4_multihop_port":21003,"obfs4_key":"GBDWrGN71fGa7a7S8b2tHzjYQMhnA2vJW5yq3iID+VoSzA1xIAgHiOxISEU93v+CBTCXWg"}},{"hostname":"us-tx2.gw.ivpn.net","host":"96.44.142.74","load":12.02,"multihop_port":25001,"obfs":{"obfs3_multihop_port":25002,"obfs4_multihop_port":25003,"obfs4_key":"VYfEVELWNqOm7kyO19usXm3GSK9wawRObopQLOLDbLodKnmX/jPah+IlGkL2xYiNxEzUKw"}}]},{"gateway":"us-ut.gw.ivpn.net","country_code":"US","country":"United States","city":"Salt Lake City, UT","latitude":40.7774,"longitude":-111.93,"isp":"100TB","hosts":[{"hostname":"us-ut1.gw.ivpn.net","host":"198.105.216.28","load":47.09,"multihop_port":24401,"obfs":{"obfs3_multihop_port":24402,"obfs4_multihop_port":24403,"obfs4_key":"gb5dp9i4QnFKT0GQPMEsDlqN+JORU3lyU65s9R1064aZ1jKfWfkc+SxbOlNEh23CvIAOeg"}}]},{"gateway":"us-va.gw.ivpn.net","country_code":"US","country":"United States","city":"Ashburn, VA","latitude":39.0437,"longitude":-77.4875,"isp":"Datapacket","hosts":[{"hostname":"us-va1.gw.ivpn.net","host":"37.19.206.105","load":98.5,"multihop_port":27701,"obfs":{"obfs3_multihop_port":27702,"obfs4_multihop_port":27703,"obfs4_key":"5Lk8BSID+M9QAGSglQTrYkom+V0mkIv0HgR80+9vKCcnZOu0f+aJhfOXqKnpHarJbIEHCw"}}]},{"gateway":"us-wa.gw.ivpn.net","country_code":"US","country":"United States","city":"Seattle, WA","latitude":47.6211,"longitude":-122.3244,"isp":"Tzulo","hosts":[{"hostname":"us-wa2.gw.ivpn.net","host":"198.44.131.3","load":23.68,"multihop_port":27801,"obfs":{"obfs3_multihop_port":27802,"obfs4_multihop_port":27803,"obfs4_key":"9h0W5BTzGSDpY2tGjdvViSJfEkT/BuwHS0cnFX07cEj3RRDtmxsC0f+kapO8+Kh+fz1XMQ"}}]},{"gateway":"za.gw.ivpn.net","country_code":"ZA","country":"South Africa","city":"Johannesburg","latitude":-26.195,"longitude":28.034,"isp":"Datapacket","hosts":[{"hostname":"za1.gw.ivpn.net","host":"169.150.238.103","load":11.1,"multihop_port":20840,"obfs":{"obfs3_multihop_port":20841,"obfs4_multihop_port":20842,"obfs4_key":"vM1zdiOMhoqMKZCOi/lMj7TfWJb5/UsM25p8FE/AUezwbYWhUkhpD5RbXBOBDNhZuYzPGA"}}]}],"config":{"antitracker":{"default":{"ip":"10.0.254.2","multihop-ip":"10.0.254.102"},"hardcore":{"ip":"10.0.254.3","multihop-ip":"10.0.254.103"}},"api":{"ips":["198.50.177.220","149.56.162.156","198.50.177.222","149.56.162.159","198.50.177.223"],"ipv6s":["2607:5300:203:1735::8888","2607:5300:203:1735::8","2607:5300:203:1735:6580:7300:0:aaaa"]},"ports":{"openvpn":[{"type":"UDP","port":53},{"type":"UDP","port":80},{"type":"UDP","range":{"min":5500,"max":19999}},{"type":"UDP","range":{"min":60000,"max":65000}},{"type":"UDP","port":123},{"type":"UDP","port":2049},{"type":"UDP","range":{"min":50000,"max":59999}},{"type":"UDP","port":2050},{"type":"UDP","range":{"min":40000,"max":49999}},{"type":"UDP","port":443},{"type":"UDP","port":1194},{"type":"UDP","range":{"min":30000,"max":39999}},{"type":"TCP","port":80},{"type":"TCP","port":443},{"type":"TCP","port":1194},{"type":"TCP","port":2049},{"type":"TCP","port":2050},{"type":"TCP","port":30587},{"type":"TCP","port":41893},{"type":"TCP","port":48574},{"type":"TCP","port":58237},{"type":"TCP","range":{"min":5500,"max":19999}},{"type":"TCP","range":{"min":30000,"max":65000}}],"wireguard":[{"type":"UDP","port":53},{"type":"UDP","port":80},{"type":"UDP","port":123},{"type":"UDP","port":443},{"type":"UDP","port":500},{"type":"UDP","port":1194},{"type":"UDP","port":2049},{"type":"UDP","port":2050},{"type":"UDP","port":4500},{"type":"UDP","port":30587},{"type":"UDP","port":41893},{"type":"UDP","port":48574},{"type":"UDP","port":58237},{"type":"UDP","range":{"min":5500,"max":19999}},{"type":"UDP","range":{"min":30000,"max":65000}}],"obfs3":{"port":5145},"obfs4":{"port":5146}}}} +{"wireguard":[{"gateway":"ca-qc.wg.ivpn.net","country_code":"CA","country":"Canada","city":"Montreal","latitude":45.5,"longitude":-73.5833,"isp":"M247","hosts":[{"hostname":"ca1.wg.ivpn.net","host":"37.120.130.58","public_key":"rg+GGDmjM4Vxo1hURvKmgm9yonb6qcoKbPCP/DNDBnI=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":14.41,"multihop_port":23810},{"hostname":"ca-qc1.wg.ivpn.net","host":"87.101.92.29","public_key":"98JU1mdCR8vD1aNZg017/NjBeTjuuCKUaLw0zfz/CUE=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":19.88,"multihop_port":27001}]},{"gateway":"ch.wg.ivpn.net","country_code":"CH","country":"Switzerland","city":"Zurich","latitude":47.38,"longitude":8.55,"isp":"Privatelayer","hosts":[{"hostname":"ch1.wg.ivpn.net","host":"141.255.164.66","public_key":"jVZJ61i1xxkAfriDHpwvF/GDuTvZUqhwoWSjkOJvaUA=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":23.37,"multihop_port":23610},{"hostname":"ch01.wg.ivpn.net","host":"185.212.170.141","public_key":"dU7gLfcupYd37LW0q6cxC6PHMba+eUFAUOoU/ryXZkY=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":17.56,"multihop_port":23601},{"hostname":"ch3.wg.ivpn.net","host":"141.255.166.198","public_key":"JBpgBKtqIneRuEga7mbP2PAk/e4HPRaC11H0A0+R3lA=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":26.89,"multihop_port":22901}]},{"gateway":"de.wg.ivpn.net","country_code":"DE","country":"Germany","city":"Frankfurt","latitude":50.1,"longitude":8.675,"isp":"Datapacket","hosts":[{"hostname":"de1.wg.ivpn.net","host":"185.102.219.26","public_key":"mS3/WpXjnMAMmXjSpd4nFzx9HSE3ubv2WyjpyH2REgs=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":15.93,"multihop_port":23010},{"hostname":"de01.wg.ivpn.net","host":"178.162.212.24","public_key":"Sc5AUZieg0qX8kyCy9p0OHRES4n0CHtHHM+ZPyERFTc=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":31.78,"multihop_port":23001},{"hostname":"de2.wg.ivpn.net","host":"37.58.60.151","public_key":"QhY3OtBf4FFafKtLO33e6k8JnAl8e6ktFcRUyLjCDVY=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":22.81,"multihop_port":22001}]},{"gateway":"gb.wg.ivpn.net","country_code":"GB","country":"United Kingdom","city":"London","latitude":51.5,"longitude":-0.1167,"isp":"M247","hosts":[{"hostname":"gb1.wg.ivpn.net","host":"81.92.202.114","public_key":"7+jos+Eg+hMEOQE4Std6OJ+WVnCcmbqS1/EbPwn9w3s=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":13.88,"multihop_port":20810},{"hostname":"gb01.wg.ivpn.net","host":"185.59.221.140","public_key":"yKK5x+D17Jr3Q12T/UBaDjNVmNdZBsqpvTqH6YfsGHg=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":27.36,"multihop_port":20801},{"hostname":"gb2.wg.ivpn.net","host":"185.59.221.225","public_key":"x0BTRaxsdxAd58ZyU2YMX4bmuj+Eg+8/urT2F3Vs1n8=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":8.94,"multihop_port":24201}]},{"gateway":"it.wg.ivpn.net","country_code":"IT","country":"Italy","city":"Milan","latitude":45.47,"longitude":9.205,"isp":"M247","hosts":[{"hostname":"it1.wg.ivpn.net","host":"82.102.21.90","public_key":"Aj6b81yrDk7I913R+fuSW/NAmIl87N73vHgY5/WQY0Q=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":7.46,"multihop_port":24310},{"hostname":"it01.wg.ivpn.net","host":"158.58.172.89","public_key":"QTzR5R6jeDI/cQ0CXPIqOby9GR5nn+4Bcf4bK536Vy0=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":7.57,"multihop_port":24301}]},{"gateway":"nl.wg.ivpn.net","country_code":"NL","country":"Netherlands","city":"Amsterdam","latitude":52.35,"longitude":4.9166,"isp":"Datapacket","hosts":[{"hostname":"nl1.wg.ivpn.net","host":"185.102.218.104","public_key":"AsMT2FqpkZbjzWeDch6GwufF5odl259W/hIkGytVfWo=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":11.94,"multihop_port":20301},{"hostname":"nl3.wg.ivpn.net","host":"95.211.95.9","public_key":"XDU6Syq1DY82IMatsHV0x/TAtbLiRwh/SdFCXlEn40c=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":5.45,"multihop_port":23101},{"hostname":"nl4.wg.ivpn.net","host":"95.211.95.19","public_key":"cVB66gPq5cZ9dfXY+e2pbsCyih5o1zk04l5c5VCsV1g=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":6.73,"multihop_port":23201},{"hostname":"nl5.wg.ivpn.net","host":"95.211.243.162","public_key":"NCagAawwRixI6Iw/NWiGD8lbjDNCl0aTICZKJtO/1HA=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":35.96,"multihop_port":23901},{"hostname":"nl6.wg.ivpn.net","host":"95.211.243.182","public_key":"hMWpqb3FEATHIbImPVWB/5z2nWIXghwpnJjevPY+1H0=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":24.22,"multihop_port":24101},{"hostname":"nl7.wg.ivpn.net","host":"95.211.172.105","public_key":"hQNYqtfOOAEz0IGshLx/TI9hUrfR9gIIkjVm4VsCbBM=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":46.91,"multihop_port":22501},{"hostname":"nl8.wg.ivpn.net","host":"95.211.198.167","public_key":"/nY1/OhVhdHtbnU/s31zYUuPBH0pizv4DemW5KDOUkg=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":12.83,"multihop_port":22801}]},{"gateway":"se.wg.ivpn.net","country_code":"SE","country":"Sweden","city":"Stockholm","latitude":59.3508,"longitude":18.0973,"isp":"M247","hosts":[{"hostname":"se1.wg.ivpn.net","host":"37.120.153.226","public_key":"2n0nFE1g/+vQr2AOQPm9Igyiy0zh9uTTultvOOSkMRo=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":15.22,"multihop_port":24010},{"hostname":"se01.wg.ivpn.net","host":"80.67.10.141","public_key":"u8VHnYEpoEjJWDAF9NAUkU6s810RnkMuhEfFD9U0cGo=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":5.66,"multihop_port":24001}]},{"gateway":"sg.wg.ivpn.net","country_code":"SG","country":"Singapore","city":"Singapore","latitude":1.293,"longitude":103.8558,"isp":"M247","hosts":[{"hostname":"sg1.wg.ivpn.net","host":"37.120.151.122","public_key":"hSg0At4uwuIhmTy5UT4fRbi5AN6JO2ZWTuIvqd4nHCE=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":6.76,"multihop_port":26110},{"hostname":"sg01.wg.ivpn.net","host":"185.128.24.189","public_key":"pWk0u1Xq8FHC+xpkN+C6yEKOTEanorR5zMCSfHlLzFw=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":23.44,"multihop_port":26101}]},{"gateway":"us-ca.wg.ivpn.net","country_code":"US","country":"United States","city":"Los Angeles, CA","latitude":34.1139,"longitude":-118.4068,"isp":"Datapacket","hosts":[{"hostname":"us-ca1.wg.ivpn.net","host":"185.180.13.41","public_key":"FGl78s9Ct6xNamQ2/CtAyXwGePrrU0kiZxfM27pm8XA=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":15.76,"multihop_port":22210},{"hostname":"us-ca01.wg.ivpn.net","host":"216.144.236.44","public_key":"B+qXdkIuETpzI0bfhGUAHN4SU91Tjs6ItdFlu93S42I=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":9.34,"multihop_port":22201},{"hostname":"us-ca2.wg.ivpn.net","host":"216.144.236.68","public_key":"qv4Tupfon5NUSwzDpM8zPizSwJZn2h+9CqrufcyDOko=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":6.26,"multihop_port":22401},{"hostname":"us-ca3.wg.ivpn.net","host":"198.54.129.100","public_key":"J5+Bx84LxNPdWEhewOvBV/fGWiDluIBlAcr1QlJZil8=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":12.79,"multihop_port":21301},{"hostname":"us-ca4.wg.ivpn.net","host":"216.144.237.83","public_key":"dYPXYr6HSRJPe3MhALwGWNtdEy1+EPE9Kqv7cTrUXk8=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":8.2,"multihop_port":21901}]},{"gateway":"us-ga.wg.ivpn.net","country_code":"US","country":"United States","city":"Atlanta, GA","latitude":33.7627,"longitude":-84.4225,"isp":"Datapacket","hosts":[{"hostname":"us-ga1.wg.ivpn.net","host":"185.93.0.212","public_key":"jD8h+pL5/d6fmYcTzl0lR8AWzQVN5XkwRFSmM/3NcDM=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":10.79,"multihop_port":24510},{"hostname":"us-ga01.wg.ivpn.net","host":"104.129.24.149","public_key":"EJFl28aYpZKfmJqb1jxxTEnGx6kaH2USVrigpHKKXhs=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":5.4,"multihop_port":24501},{"hostname":"us-ga2.wg.ivpn.net","host":"107.150.22.77","public_key":"hr2uQOEGCvGeDkoCQJ2dCI8dM8Iu5aKhb1PIvJ9q72E=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":9,"multihop_port":24810}]},{"gateway":"us-il.wg.ivpn.net","country_code":"US","country":"United States","city":"Chicago, IL","latitude":41.8373,"longitude":-87.6862,"isp":"Datapacket","hosts":[{"hostname":"us-il1.wg.ivpn.net","host":"89.187.181.116","public_key":"hku9gjamhoii8OvxZgx+TdUDIkOAQYFu39qbav2AyUQ=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":20.19,"multihop_port":21410},{"hostname":"us-il01.wg.ivpn.net","host":"72.11.137.158","public_key":"Uy5a8JOqneAUY1dC5s9jubLnotbyIfBsLP2nZuzRbHs=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":13.31,"multihop_port":21401},{"hostname":"us-il2.wg.ivpn.net","host":"72.11.137.148","public_key":"ANhVUMAQgStPVNRHW8mg0ZtN1YI1QHyXfNCO8+USNQQ=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":7.21,"multihop_port":24901}]},{"gateway":"us-ny.wg.ivpn.net","country_code":"US","country":"United States","city":"New York, NY","latitude":40.6943,"longitude":-73.9249,"isp":"M247","hosts":[{"hostname":"us-ny1.wg.ivpn.net","host":"91.132.137.170","public_key":"6/tjvgb7HFl7UuvBSegolxa1zKr3iSlDrlCexCmhAGE=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":36.4,"multihop_port":21210},{"hostname":"us-ny2.wg.ivpn.net","host":"212.103.48.195","public_key":"c7DwY2uT+6ulWAJ5u8qJNWHroA0qyJLcdNzf/f2kkhs=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":8.1,"multihop_port":21801},{"hostname":"us-ny3.wg.ivpn.net","host":"89.187.178.145","public_key":"m5/Ssw9SN3WuE+yD/fAsH5G8iuI8TcDGEiZZnPgiMCc=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":5.14,"multihop_port":27601}]},{"gateway":"us-tx.wg.ivpn.net","country_code":"US","country":"United States","city":"Dallas, TX","latitude":32.7936,"longitude":-96.7662,"isp":"Quadranet","hosts":[{"hostname":"us-tx1.wg.ivpn.net","host":"198.55.124.114","public_key":"JPT1veXLmasj2uQDstX24mpR7VWD+GmV8JDkidkz91Q=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":5.41,"multihop_port":21010},{"hostname":"us-tx01.wg.ivpn.net","host":"96.44.189.197","public_key":"LvWf548mFddi8PTrIGL6uD1/l85LU8z0Rc8tpvw2Vls=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":13.06,"multihop_port":21001},{"hostname":"us-tx2.wg.ivpn.net","host":"96.44.142.77","public_key":"om8hOGUcEvoOhHvJZoBHxNF4jxY/+Ml9Iy1WOSC/pFo=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":10.52,"multihop_port":25001}]},{"gateway":"at.wg.ivpn.net","country_code":"AT","country":"Austria","city":"Vienna","latitude":48.2,"longitude":16.3666,"isp":"M247","hosts":[{"hostname":"at1.wg.ivpn.net","host":"185.244.212.69","public_key":"83LUBnP97SFpnS0y1MpEAFcg8MIiQJgW1FRv/8Mc40g=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":28.58,"multihop_port":25601}]},{"gateway":"au-nsw.wg.ivpn.net","country_code":"AU","country":"Australia","city":"Sydney","latitude":-33.92,"longitude":151.1852,"isp":"M247","hosts":[{"hostname":"au-nsw1.wg.ivpn.net","host":"46.102.153.246","public_key":"KmSrG48t5xw9CJCPlYLBG3JnmiY0CnUgyRM5TUEwZhM=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":98.95,"multihop_port":26601},{"hostname":"au-nsw2.wg.ivpn.net","host":"146.70.78.75","public_key":"q+wbp7GjiTszp5G16rNpGCqxkL0qSY3CH4pcgD6UsVQ=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":71.28,"multihop_port":27901}]},{"gateway":"be.wg.ivpn.net","country_code":"BE","country":"Belgium","city":"Brussels","latitude":50.8333,"longitude":4.3333,"isp":"M247","hosts":[{"hostname":"be1.wg.ivpn.net","host":"194.187.251.13","public_key":"awriP5lpdxEMWKuG+A1DOg+vb1M5jd3WhynIMB61BhU=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":64.31,"multihop_port":25701}]},{"gateway":"bg.wg.ivpn.net","country_code":"BG","country":"Bulgaria","city":"Sofia","latitude":42.6833,"longitude":23.3167,"isp":"M247","hosts":[{"hostname":"bg1.wg.ivpn.net","host":"82.102.23.21","public_key":"WDSsdJE6wvATIWfzQwayPtE/0DaXBQgW/hPm7sQSJmU=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":78.13,"multihop_port":25901}]},{"gateway":"br.wg.ivpn.net","country_code":"BR","country":"Brazil","city":"Franca","latitude":-20.53,"longitude":-47.39,"isp":"Qnax","hosts":[{"hostname":"br1.wg.ivpn.net","host":"45.162.229.133","public_key":"eN1f15S3YzRyYCALiPGRQcjkQO9xntcdqPhJJ6TOymc=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":11.54,"multihop_port":26701}]},{"gateway":"ca.wg.ivpn.net","country_code":"CA","country":"Canada","city":"Toronto","latitude":43.7,"longitude":-79.42,"isp":"Amanah","hosts":[{"hostname":"ca01.wg.ivpn.net","host":"104.254.90.181","public_key":"mdGnCZwinuOVGg46zsWnFhhenfFId6jht9GBTKB+xUA=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":41.78,"multihop_port":23801},{"hostname":"ca2.wg.ivpn.net","host":"172.86.186.173","public_key":"5qHV10ZbFgEGnF6wg9QpKeh1l6Di2JUG/5PdNaaoNW8=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":55.09,"multihop_port":22101}]},{"gateway":"cz.wg.ivpn.net","country_code":"CZ","country":"Czech Republic","city":"Prague","latitude":50.0833,"longitude":14.466,"isp":"Datapacket","hosts":[{"hostname":"cz1.wg.ivpn.net","host":"185.180.14.41","public_key":"gVbEq2cGRzwCSGPqT2oRSYYN+P6IK3uvvRffErASDSk=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":13.04,"multihop_port":25201}]},{"gateway":"dk.wg.ivpn.net","country_code":"DK","country":"Denmark","city":"Copenhagen","latitude":55.6786,"longitude":12.5635,"isp":"M247","hosts":[{"hostname":"dk1.wg.ivpn.net","host":"185.245.84.229","public_key":"jTsV5gOD7lT4egDj9rhKwO2OO2X7bKs2EQPcZEnUWDE=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":61.97,"multihop_port":25501}]},{"gateway":"es.wg.ivpn.net","country_code":"ES","country":"Spain","city":"Madrid","latitude":40.4,"longitude":-3.6834,"isp":"Datapacket","hosts":[{"hostname":"es1.wg.ivpn.net","host":"84.17.62.98","public_key":"w7umiArTtlJ4Pk6Ii9WX5VXK5vw/Qu+Z37/icKlIYWo=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":9.51,"multihop_port":21501}]},{"gateway":"fi.wg.ivpn.net","country_code":"FI","country":"Finland","city":"Helsinki","latitude":60.1756,"longitude":24.9341,"isp":"Creanova","hosts":[{"hostname":"fi1.wg.ivpn.net","host":"194.34.134.63","public_key":"mIxEzfjZ2wV6jJVj30w38ECd2LSH4bw/HLMnM2ICHiI=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":99.96,"multihop_port":26001}]},{"gateway":"fr.wg.ivpn.net","country_code":"FR","country":"France","city":"Paris","latitude":48.8667,"longitude":2.3333,"isp":"Datapacket","hosts":[{"hostname":"fr1.wg.ivpn.net","host":"185.246.211.185","public_key":"g7BuMzj3r/noLiLR4qhQMcvU6GSIY8RGEnaYtdYsFX4=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":16.85,"multihop_port":23401}]},{"gateway":"gb-man.wg.ivpn.net","country_code":"GB","country":"United Kingdom","city":"Manchester","latitude":53.5004,"longitude":-2.248,"isp":"M247","hosts":[{"hostname":"gb-man1.wg.ivpn.net","host":"89.238.141.231","public_key":"+hf4DYilNEIjTdSOuCNcWdqVyaRoxGzXw7wvNl7f7Rg=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":11.95,"multihop_port":26901}]},{"gateway":"hk.wg.ivpn.net","country_code":"HK","country":"Hong Kong","city":"Hong Kong","latitude":22.305,"longitude":114.185,"isp":"Leaseweb","hosts":[{"hostname":"hk2.wg.ivpn.net","host":"64.120.120.239","public_key":"kyolyq4cJydI3vQB2ESTIUAy2Fq0bpOf+Qe7GIq6XEA=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":9.47,"multihop_port":27501},{"hostname":"hk3.wg.ivpn.net","host":"118.107.244.206","public_key":"qq1simsFNm2FpZM0J8u8Aa0rkk5HEasvLksPyLv+0Sk=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":9.07,"multihop_port":20800}]},{"gateway":"hu.wg.ivpn.net","country_code":"HU","country":"Hungary","city":"Budapest","latitude":47.5,"longitude":19.0833,"isp":"M247","hosts":[{"hostname":"hu1.wg.ivpn.net","host":"185.189.114.189","public_key":"G30fNdXrnlqtqqOLF23QXWzFdLIKDxLW60HoYPvqml8=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":100,"multihop_port":25401}]},{"gateway":"il.wg.ivpn.net","country_code":"IL","country":"Israel","city":"Holon, Tel Aviv","latitude":32.08,"longitude":34.77,"isp":"HQServ","hosts":[{"hostname":"il01.wg.ivpn.net","host":"185.191.207.197","public_key":"HR9gAjpxXU3YVt6kehBw5n8yVYVE0iIgJdc4HTqOzEE=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":49.46,"multihop_port":27301}]},{"gateway":"is.wg.ivpn.net","country_code":"IS","country":"Iceland","city":"Reykjavik","latitude":64.15,"longitude":-21.95,"isp":"Advania","hosts":[{"hostname":"is1.wg.ivpn.net","host":"82.221.107.185","public_key":"nZZT6TlQ2dXlVe3P3B5ozEScHYMWH4JY4y3to8w5dz0=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":88.98,"multihop_port":23501}]},{"gateway":"jp.wg.ivpn.net","country_code":"JP","country":"Japan","city":"Tokyo","latitude":35.685,"longitude":139.7514,"isp":"M247","hosts":[{"hostname":"jp1.wg.ivpn.net","host":"91.207.174.237","public_key":"tb9WdC3LSho3d1rI5N7kfG9e42/d+u4LPVdEYERPsSQ=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":30.88,"multihop_port":26201},{"hostname":"jp2.wg.ivpn.net","host":"185.135.77.81","public_key":"YuhEd9+a90/+uucZC+qzsyMHkfe/GiwG1dq7g2HegXQ=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":5.66,"multihop_port":20830}]},{"gateway":"lu.wg.ivpn.net","country_code":"LU","country":"Luxembourg","city":"Luxembourg","latitude":49.6117,"longitude":6.13,"isp":"Evoluso","hosts":[{"hostname":"lu1.wg.ivpn.net","host":"92.223.89.57","public_key":"hUS1OAFLGwpba8+oc5mifYtohZt/RTro5dMyYBLYHjI=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":13.08,"multihop_port":27201}]},{"gateway":"my.wg.ivpn.net","country_code":"MY","country":"Malaysia","city":"Kuala Lumpur","latitude":3.1494,"longitude":101.706,"isp":"TheGigabit","hosts":[{"hostname":"my1.wg.ivpn.net","host":"61.4.97.153","public_key":"M9SsMCpUw7ad6YbqQr8r2saBK2zAf3tBj82DzsQjgkY=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":6.48,"multihop_port":20810}]},{"gateway":"no.wg.ivpn.net","country_code":"NO","country":"Norway","city":"Oslo","latitude":59.9167,"longitude":10.75,"isp":"Servetheworld","hosts":[{"hostname":"no1.wg.ivpn.net","host":"91.189.177.156","public_key":"xFO6ksbO3Gr05rRgAW0O5Veoi4bpTgz2G9RvtBzK7Cg=","local_ip":"172.16.0.1/12","ipv6":{},"load":0,"multihop_port":25301}]},{"gateway":"pl.wg.ivpn.net","country_code":"PL","country":"Poland","city":"Warsaw","latitude":52.25,"longitude":21,"isp":"Datapacket","hosts":[{"hostname":"pl1.wg.ivpn.net","host":"185.246.208.109","public_key":"1JDmF79rWj5C+kHp71AbdHne/yGaizWCd2bLfSFvYjo=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":36.56,"multihop_port":25101}]},{"gateway":"pt.wg.ivpn.net","country_code":"PT","country":"Portugal","city":"Lisbon","latitude":38.7227,"longitude":-9.1449,"isp":"Hostwebis","hosts":[{"hostname":"pt1.wg.ivpn.net","host":"94.46.175.113","public_key":"nMnA82YVrvEK80GVoY/0Z9McWeqjcLzuMYSL+86j5nU=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":5.94,"multihop_port":27101}]},{"gateway":"ro.wg.ivpn.net","country_code":"RO","country":"Romania","city":"Bucharest","latitude":44.4334,"longitude":26.0999,"isp":"M247","hosts":[{"hostname":"ro1.wg.ivpn.net","host":"37.120.206.53","public_key":"F2uQ57hysZTlw8WYELnyCw9Lga80wNYoYwkrrxyXKmw=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":43.52,"multihop_port":22301}]},{"gateway":"rs.wg.ivpn.net","country_code":"RS","country":"Serbia","city":"Belgrade","latitude":44.8186,"longitude":20.468,"isp":"M247","hosts":[{"hostname":"rs1.wg.ivpn.net","host":"141.98.103.253","public_key":"xLN/lpQThQ3z3tvYf7VqdAsRL/nton1Vhv2kCZlQtWE=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":65.32,"multihop_port":26801}]},{"gateway":"sk.wg.ivpn.net","country_code":"SK","country":"Slovakia","city":"Bratislava","latitude":48.15,"longitude":17.117,"isp":"M247","hosts":[{"hostname":"sk1.wg.ivpn.net","host":"185.245.85.253","public_key":"MOBWWpEgNsKbFj4BEyWSDFLlkBs5iUFiqdSdTFTDBko=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":100,"multihop_port":25801}]},{"gateway":"tw.wg.ivpn.net","country_code":"TW","country":"Taiwan","city":"Taipei","latitude":25.073,"longitude":121.578,"isp":"TheGigabit","hosts":[{"hostname":"tw1.wg.ivpn.net","host":"185.189.160.59","public_key":"fMTCCbbKqPp60fkqnaQvJ9mX2r6zBlt7xhUp8sGfJQY=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":8.89,"multihop_port":20820}]},{"gateway":"ua.wg.ivpn.net","country_code":"UA","country":"Ukraine","city":"Kharkiv","latitude":50,"longitude":36.25,"isp":"Xservers","hosts":[{"hostname":"ua1.wg.ivpn.net","host":"176.103.57.129","public_key":"mIxEzfjZ2wV6jJVj30w38ECd2LSH4bw/HLMnM2ICHiI=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":6.43,"multihop_port":26301}]},{"gateway":"us-az.wg.ivpn.net","country_code":"US","country":"United States","city":"Phoenix, AZ","latitude":33.5722,"longitude":-112.0891,"isp":"M247","hosts":[{"hostname":"us-az1.wg.ivpn.net","host":"193.37.254.133","public_key":"Ts4MGazxpxL9rrYbERjgxa+kCEX85ou9gHoaJvDsRiI=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":11.52,"multihop_port":26401}]},{"gateway":"us-fl.wg.ivpn.net","country_code":"US","country":"United States","city":"Miami, FL","latitude":25.7839,"longitude":-80.2102,"isp":"Quadranet","hosts":[{"hostname":"us-fl1.wg.ivpn.net","host":"173.44.49.93","public_key":"Rkzo9WgxJBiKyEbkZvqGWtOVh9Gk9Vd7wL49SHXdHig=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":8.5,"multihop_port":24601}]},{"gateway":"us-nj.wg.ivpn.net","country_code":"US","country":"United States","city":"New Jersey, NJ","latitude":40.737764,"longitude":-74.151747,"isp":"Quadranet","hosts":[{"hostname":"us-nj3.wg.ivpn.net","host":"23.226.128.21","public_key":"AX7C1LO0ECUcHRYgX4/tIDYdR8npvfB/+pf4AfI3OHU=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":17.66,"multihop_port":21610},{"hostname":"us-nj4.wg.ivpn.net","host":"194.36.111.54","public_key":"1Te4AfL1yKo2k4jzPALnRPfKE3YSzXKo4XIRHPz5FxI=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":14.38,"multihop_port":27401}]},{"gateway":"us-nv.wg.ivpn.net","country_code":"US","country":"United States","city":"Las Vegas, NV","latitude":36.2333,"longitude":-115.2654,"isp":"M247","hosts":[{"hostname":"us-nv1.wg.ivpn.net","host":"185.242.5.37","public_key":"PRpvAZyoNWNm/KHlqafjtYoZtn1PkIPylUE4WbuYmgM=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":8.82,"multihop_port":26501}]},{"gateway":"us-ut.wg.ivpn.net","country_code":"US","country":"United States","city":"Salt Lake City, UT","latitude":40.7774,"longitude":-111.93,"isp":"100TB","hosts":[{"hostname":"us-ut1.wg.ivpn.net","host":"206.190.145.92","public_key":"KirI7bpxD186CuYiOqNHF+QUe6YmRYf6CN3pXWOJT2k=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":15.91,"multihop_port":24401}]},{"gateway":"us-va.wg.ivpn.net","country_code":"US","country":"United States","city":"Ashburn, VA","latitude":39.0437,"longitude":-77.4875,"isp":"Datapacket","hosts":[{"hostname":"us-va1.wg.ivpn.net","host":"37.19.206.106","public_key":"ZCnZK6U+cRuP/WgzIDb/P6UG2rX/KyCRd5vJ1hAbr2E=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":22.97,"multihop_port":27701}]},{"gateway":"us-wa.wg.ivpn.net","country_code":"US","country":"United States","city":"Seattle, WA","latitude":47.6211,"longitude":-122.3244,"isp":"Tzulo","hosts":[{"hostname":"us-wa2.wg.ivpn.net","host":"198.44.131.4","public_key":"VcrOOozBUCIURU0AnqMAE7AkMmC7Qrp+j/PzPbgbalU=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":14.58,"multihop_port":27801}]},{"gateway":"za.wg.ivpn.net","country_code":"ZA","country":"South Africa","city":"Johannesburg","latitude":-26.195,"longitude":28.034,"isp":"Datapacket","hosts":[{"hostname":"za1.wg.ivpn.net","host":"169.150.238.108","public_key":"tgrAA+uJZppS9esgOi0pe3rHajQQ7c/KF8WPOua6qy4=","local_ip":"172.16.0.1/12","ipv6":{"local_ip":"fd00:4956:504e:ffff::/96"},"load":8.1,"multihop_port":20840}]}],"openvpn":[{"gateway":"at.gw.ivpn.net","country_code":"AT","country":"Austria","city":"Vienna","latitude":48.2,"longitude":16.3666,"isp":"M247","hosts":[{"hostname":"at1.gw.ivpn.net","host":"185.244.212.66","load":28.58,"multihop_port":25601,"obfs":{"obfs3_multihop_port":25602,"obfs4_multihop_port":25603,"obfs4_key":"75HhQC6n6ctp9Fa9wCvEnc6ip5FnEfuIGc+dVNLH4M15FjB/Ve6bI1b8lYFhk6T+4/HkDQ"}}]},{"gateway":"au-nsw.gw.ivpn.net","country_code":"AU","country":"Australia","city":"Sydney","latitude":-33.92,"longitude":151.1852,"isp":"M247","hosts":[{"hostname":"au-nsw1.gw.ivpn.net","host":"46.102.153.242","load":98.95,"multihop_port":26601,"obfs":{"obfs3_multihop_port":26602,"obfs4_multihop_port":26603,"obfs4_key":"/rjoeDjduOFq1UvT332vhS398h1RP5hC3m7sDJKNSyJ6TO8mkcxWAYILw0i+bgS/3JD5YA"}},{"hostname":"au-nsw2.gw.ivpn.net","host":"146.70.78.74","load":71.28,"multihop_port":27901,"obfs":{"obfs3_multihop_port":27902,"obfs4_multihop_port":27903,"obfs4_key":"qtdQ5krD9EQFR98xNo/v5cmGb10wqt0Om9pYMIHWQh4oz5xcAXj32rViEyN0bnkkhaZnBA"}}]},{"gateway":"be.gw.ivpn.net","country_code":"BE","country":"Belgium","city":"Brussels","latitude":50.8333,"longitude":4.3333,"isp":"M247","hosts":[{"hostname":"be1.gw.ivpn.net","host":"194.187.251.10","load":64.31,"multihop_port":25701,"obfs":{"obfs3_multihop_port":25702,"obfs4_multihop_port":25703,"obfs4_key":"cN8i60FUVy2mmGpy+tkQAz8hu/N0EGPq8cZwIotEDwdhAYdLV+ATes/AEjzdub2K68TlYg"}}]},{"gateway":"bg.gw.ivpn.net","country_code":"BG","country":"Bulgaria","city":"Sofia","latitude":42.6833,"longitude":23.3167,"isp":"M247","hosts":[{"hostname":"bg1.gw.ivpn.net","host":"82.102.23.18","load":78.13,"multihop_port":25901,"obfs":{"obfs3_multihop_port":25902,"obfs4_multihop_port":25903,"obfs4_key":"K+mCw9+zy/8pBQt6IUKRlg2eJ3DCnJ1BvIccLq/6A2D6HoZddyDnZQYb2Sb2e464dVgBWw"}}]},{"gateway":"br.gw.ivpn.net","country_code":"BR","country":"Brazil","city":"Franca","latitude":-20.53,"longitude":-47.39,"isp":"Qnax","hosts":[{"hostname":"br1.gw.ivpn.net","host":"45.162.229.130","load":11.54,"multihop_port":26701,"obfs":{"obfs3_multihop_port":26702,"obfs4_multihop_port":26703,"obfs4_key":"h4bBkocahWveuv/nWPRMYXBTw95ExTiXwmoydkNlV6hgfy8/ZjaKc34rqTuOyOH+CK7OZw"}}]},{"gateway":"ca-qc.gw.ivpn.net","country_code":"CA","country":"Canada","city":"Montreal","latitude":45.5,"longitude":-73.5833,"isp":"M247","hosts":[{"hostname":"ca-qc1.gw.ivpn.net","host":"87.101.92.26","load":19.88,"multihop_port":27001,"obfs":{"obfs3_multihop_port":27002,"obfs4_multihop_port":27003,"obfs4_key":"E97qdaar8flBavysmvdIui7WQEFKkbmNi/ITTo8bPLvv39vnaBRzFj4vjMwpzXV2jXEGRQ"}}]},{"gateway":"ca.gw.ivpn.net","country_code":"CA","country":"Canada","city":"Toronto","latitude":43.7,"longitude":-79.42,"isp":"Amanah","hosts":[{"hostname":"ca1.gw.ivpn.net","host":"104.254.90.178","load":41.78,"multihop_port":23801,"obfs":{"obfs3_multihop_port":23802,"obfs4_multihop_port":23803,"obfs4_key":"49WDCCK1QGpOTSEVflRzIIaOdCT1BLB3jr/yNUJsbaUFrq7NuoB1E3wAdArNxQq9p5otTw"}},{"hostname":"ca2.gw.ivpn.net","host":"172.86.186.170","load":55.09,"multihop_port":22101,"obfs":{"obfs3_multihop_port":22102,"obfs4_multihop_port":22103,"obfs4_key":"xNkQbCkA0VCe2i4WvS2CPq2eBSMydIjsHH36E+Yg0C+gOPo3SwZyN51kpB+kwsYVS32fOQ"}}]},{"gateway":"ch.gw.ivpn.net","country_code":"CH","country":"Switzerland","city":"Zurich","latitude":47.38,"longitude":8.55,"isp":"M247","hosts":[{"hostname":"ch1.gw.ivpn.net","host":"185.212.170.138","load":17.56,"multihop_port":23601,"obfs":{"obfs3_multihop_port":23602,"obfs4_multihop_port":23603,"obfs4_key":"ELnV4JNKu0vUNd3J+QDn64yfZtqM0hNN6O5n6RkDLHbeSDBZmxP1N4dlwwChV/uySX+DEQ"}},{"hostname":"ch3.gw.ivpn.net","host":"141.255.166.194","load":26.89,"multihop_port":22901,"obfs":{"obfs3_multihop_port":22902,"obfs4_multihop_port":22903,"obfs4_key":"oNaH5sHCPGGk5m3/VMOrTDL+m1qsJrze+bqDs78vhOYBpjx5Jjq5TXu1dXNfDJCKNmKnUA"}}]},{"gateway":"cz.gw.ivpn.net","country_code":"CZ","country":"Czech Republic","city":"Prague","latitude":50.0833,"longitude":14.466,"isp":"Datapacket","hosts":[{"hostname":"cz1.gw.ivpn.net","host":"195.181.160.167","load":13.04,"multihop_port":25201,"obfs":{"obfs3_multihop_port":25202,"obfs4_multihop_port":25203,"obfs4_key":"JZ3PtIyflM3VwVow2vqi08OxddOWSx9j6B6yZSGoZLs9QE0hzSAj3ZBWEsCKFeQ2RcAoCQ"}}]},{"gateway":"de.gw.ivpn.net","country_code":"DE","country":"Germany","city":"Frankfurt","latitude":50.1,"longitude":8.675,"isp":"Leaseweb","hosts":[{"hostname":"de1.gw.ivpn.net","host":"178.162.222.40","load":31.78,"multihop_port":23001,"obfs":{"obfs3_multihop_port":23002,"obfs4_multihop_port":23003,"obfs4_key":"PBhcWVcRNOCTMSWXbA2J+8eJBVzNd9H5HOr0YCF8QWwmKeSlEmqLSJQE8oDpKH5IbFH4Mw"}},{"hostname":"de2.gw.ivpn.net","host":"178.162.211.114","load":22.81,"multihop_port":22001,"obfs":{"obfs3_multihop_port":22002,"obfs4_multihop_port":22003,"obfs4_key":"dEhLA4ZsvVP8+PRvlSHKwmW8JyzR1Bwy7+BFKF7Ux4L2B5YvdqqOrv/8eHliEj2mm2Z8Iw"}}]},{"gateway":"dk.gw.ivpn.net","country_code":"DK","country":"Denmark","city":"Copenhagen","latitude":55.6786,"longitude":12.5635,"isp":"M247","hosts":[{"hostname":"dk1.gw.ivpn.net","host":"185.245.84.226","load":61.97,"multihop_port":25501,"obfs":{"obfs3_multihop_port":25502,"obfs4_multihop_port":25503,"obfs4_key":"ngjtv9UISX4tB5AkBnrEN2TrAnUqVwNZ688VqDlS4BDxQXJeJF3ynZtngRqeowhEahsccQ"}}]},{"gateway":"es.gw.ivpn.net","country_code":"ES","country":"Spain","city":"Madrid","latitude":40.4,"longitude":-3.6834,"isp":"Datapacket","hosts":[{"hostname":"es1.gw.ivpn.net","host":"185.93.3.193","load":9.51,"multihop_port":21501,"obfs":{"obfs3_multihop_port":21502,"obfs4_multihop_port":21503,"obfs4_key":"x4A9SBY5yzPKH1QOkEsGYcIR2JA/Pu393jv/ZSg4YO2DsVhr3TQFxcMO3QhD9iSF48smJA"}}]},{"gateway":"fi.gw.ivpn.net","country_code":"FI","country":"Finland","city":"Helsinki","latitude":60.1756,"longitude":24.9341,"isp":"Creanova","hosts":[{"hostname":"fi1.gw.ivpn.net","host":"185.112.82.12","load":99.96,"multihop_port":26001,"obfs":{"obfs3_multihop_port":26002,"obfs4_multihop_port":26003,"obfs4_key":"SvvSpGiFctjs4n2wZGnZUf9fAL8wag70SrA3FX+9f3Sq+KgBn+/8P6fE3239ezemg9svLA"}}]},{"gateway":"fr.gw.ivpn.net","country_code":"FR","country":"France","city":"Paris","latitude":48.8667,"longitude":2.3333,"isp":"Datapacket","hosts":[{"hostname":"fr1.gw.ivpn.net","host":"185.246.211.179","load":16.85,"multihop_port":23401,"obfs":{"obfs3_multihop_port":23402,"obfs4_multihop_port":23403,"obfs4_key":"CMf0pNZ46nFdG0Tpa3hE0cK9wtUAReJL7HN66G9Jq3tlrTSWrU0DLf7sCQgXV+WFoc8iaw"}}]},{"gateway":"gb-man.gw.ivpn.net","country_code":"GB","country":"United Kingdom","city":"Manchester","latitude":53.5004,"longitude":-2.248,"isp":"M247","hosts":[{"hostname":"gb-man1.gw.ivpn.net","host":"89.238.141.228","load":11.95,"multihop_port":26901,"obfs":{"obfs3_multihop_port":26902,"obfs4_multihop_port":26903,"obfs4_key":"WuP3pMrsQA+uAC72sV+Y62E1uvOWcnbTNJCmRHXqtWbbYzECF5swu62dzD/JOKoa5t0tGQ"}}]},{"gateway":"gb.gw.ivpn.net","country_code":"GB","country":"United Kingdom","city":"London","latitude":51.5,"longitude":-0.1167,"isp":"Datapacket","hosts":[{"hostname":"gb1.gw.ivpn.net","host":"185.59.221.133","load":27.36,"multihop_port":20801,"obfs":{"obfs3_multihop_port":20802,"obfs4_multihop_port":20803,"obfs4_key":"5whTESvZxQE28wKqJWZ9fQFy09d8//GNaPDeYHVR+FJZTyI+DMS6qX4Mt2FKSf/zBToaDg"}},{"hostname":"gb2.gw.ivpn.net","host":"185.59.221.88","load":8.94,"multihop_port":24201,"obfs":{"obfs3_multihop_port":24202,"obfs4_multihop_port":24203,"obfs4_key":"4hrCOgPsRfmnQ6cLvV5mtM2XZCRS/DXG/izYSg2qZC+WJ0GsnhTlKByPM2iJ555MaM8vJA"}}]},{"gateway":"hk.gw.ivpn.net","country_code":"HK","country":"Hong Kong","city":"Hong Kong","latitude":22.305,"longitude":114.185,"isp":"Leaseweb","hosts":[{"hostname":"hk2.gw.ivpn.net","host":"209.58.188.13","load":9.47,"multihop_port":27501,"obfs":{"obfs3_multihop_port":27502,"obfs4_multihop_port":27503,"obfs4_key":"GIunWjiq00Piv3Xf4VeMkmUQ8NzD8sxRkSIbA3bxrU4LhPVth+3qM2zQwI4GesQDCDY5RA"}},{"hostname":"hk3.gw.ivpn.net","host":"118.107.244.184","load":9.07,"multihop_port":20800,"obfs":{"obfs3_multihop_port":20801,"obfs4_multihop_port":20802,"obfs4_key":"wnI0gz3hLM9VhkABSncBIsGERgn16UzTxkj7dEeYYo/y2Wu0/w24rfriA5KL7ugpTyvEFg"}}]},{"gateway":"hu.gw.ivpn.net","country_code":"HU","country":"Hungary","city":"Budapest","latitude":47.5,"longitude":19.0833,"isp":"M247","hosts":[{"hostname":"hu1.gw.ivpn.net","host":"185.189.114.186","load":100,"multihop_port":25401,"obfs":{"obfs3_multihop_port":25402,"obfs4_multihop_port":25403,"obfs4_key":"2TwZqxAakni0S4S3ulOIqMZimqqug0KCr6pNREN6KytrtIh486nkJyiFRqaYZlx+FlxEcg"}}]},{"gateway":"il.gw.ivpn.net","country_code":"IL","country":"Israel","city":"Holon, Tel Aviv","latitude":32.08,"longitude":34.77,"isp":"HQServ","hosts":[{"hostname":"il1.gw.ivpn.net","host":"185.191.207.194","load":49.46,"multihop_port":27301,"obfs":{"obfs3_multihop_port":27302,"obfs4_multihop_port":27303,"obfs4_key":"DysuSM7UWjquj4BAVYf6mOUnRKfY1QXs17MXiV5aYapFfOkQPpx5nQPVQ2M+rLDxN9TSRg"}}]},{"gateway":"is.gw.ivpn.net","country_code":"IS","country":"Iceland","city":"Reykjavik","latitude":64.15,"longitude":-21.95,"isp":"Advania","hosts":[{"hostname":"is1.gw.ivpn.net","host":"82.221.107.178","load":88.98,"multihop_port":23501,"obfs":{"obfs3_multihop_port":23502,"obfs4_multihop_port":23503,"obfs4_key":"xx/Lor3q60/pVh4PKWi0GZaw64pPXFTkALnGlvRaBMiPRkFilr7KhYmPInDnZxzr4c4AIw"}}]},{"gateway":"it.gw.ivpn.net","country_code":"IT","country":"Italy","city":"Milan","latitude":45.47,"longitude":9.205,"isp":"SEFlow","hosts":[{"hostname":"it1.gw.ivpn.net","host":"158.58.172.73","load":7.57,"multihop_port":24301,"obfs":{"obfs3_multihop_port":24302,"obfs4_multihop_port":24303,"obfs4_key":"yJXSlTJloo6tlAxf0MI9Bu4schtcL2NlVqMPgbrhnrT/1pTZUaOUH7F7nLbVe4JSCSyeZg"}}]},{"gateway":"jp.gw.ivpn.net","country_code":"JP","country":"Japan","city":"Tokyo","latitude":35.685,"longitude":139.7514,"isp":"M247","hosts":[{"hostname":"jp1.gw.ivpn.net","host":"91.207.174.234","load":30.88,"multihop_port":26201,"obfs":{"obfs3_multihop_port":26202,"obfs4_multihop_port":26203,"obfs4_key":"CBqi0EpfoUzP/ijwYn9A9yEpItrhtX+BAKF0rvcUGv///UNzzXKNz+0RhBLdQayZx6y6TA"}},{"hostname":"jp2.gw.ivpn.net","host":"185.135.77.35","load":5.66,"multihop_port":20830,"obfs":{"obfs3_multihop_port":20831,"obfs4_multihop_port":20832,"obfs4_key":"giZJF4edg7wcjxbdgD2RjFcF9QAzExLHIJYjm2cLLtx7MrxP0p96mIFj9T8LSQotKB63fA"}}]},{"gateway":"lu.gw.ivpn.net","country_code":"LU","country":"Luxembourg","city":"Luxembourg","latitude":49.6117,"longitude":6.13,"isp":"Evoluso","hosts":[{"hostname":"lu1.gw.ivpn.net","host":"92.223.89.53","load":13.08,"multihop_port":27201,"obfs":{"obfs3_multihop_port":27202,"obfs4_multihop_port":27203,"obfs4_key":"auDmK8lVBI7fxq6UjXg7V0qoZJ3icACKm/9vruMGSr0lT6ViNsl28qMqYbjJRveHnx5eQw"}}]},{"gateway":"my.gw.ivpn.net","country_code":"MY","country":"Malaysia","city":"Kuala Lumpur","latitude":3.1494,"longitude":101.706,"isp":"TheGigabit","hosts":[{"hostname":"my1.gw.ivpn.net","host":"61.4.97.148","load":6.48,"multihop_port":20810,"obfs":{"obfs3_multihop_port":20811,"obfs4_multihop_port":20812,"obfs4_key":"k2hwCe8gDb24K5n2jNXYO5YCl5XCIEhuRwpYSsEhKWorah8OUM1C1crawbfstj+W2IQdOA"}}]},{"gateway":"nl.gw.ivpn.net","country_code":"NL","country":"Netherlands","city":"Amsterdam","latitude":52.35,"longitude":4.9166,"isp":"Leaseweb","hosts":[{"hostname":"nl3.gw.ivpn.net","host":"95.211.172.68","load":5.45,"multihop_port":23101,"obfs":{"obfs3_multihop_port":23102,"obfs4_multihop_port":23103,"obfs4_key":"eUXsScIg0K0LKVgA8yq2Mc++pfnTQ9nr3gnV8n1NIw7wRqinhO6uuXiSS5J19agQaPK1ew"}},{"hostname":"nl4.gw.ivpn.net","host":"95.211.172.95","load":6.73,"multihop_port":23201,"obfs":{"obfs3_multihop_port":23202,"obfs4_multihop_port":23203,"obfs4_key":"KEw6WJF+NDOQv7yMvq+dAAAkPbcYJ8PX6pffRd8EM3uaOy2QcpMcdHI7s700Kq/ZvV3HBQ"}},{"hostname":"nl5.gw.ivpn.net","host":"95.211.187.222","load":35.96,"multihop_port":23901,"obfs":{"obfs3_multihop_port":23902,"obfs4_multihop_port":23903,"obfs4_key":"fcWCrzzatLbk1LNKsuQZKpQrC3ZXwQ85GO5xRS467KJBRDrmvnyMb6ARbLGu+gYkTnNELQ"}},{"hostname":"nl6.gw.ivpn.net","host":"95.211.187.228","load":24.22,"multihop_port":24101,"obfs":{"obfs3_multihop_port":24102,"obfs4_multihop_port":24103,"obfs4_key":"lT3OGPQS1CwwqtalMExN7qxEoDDBLLlcoh5a6YW3DPj8esEBEG6wY5OfYonxltBzoVu4PA"}},{"hostname":"nl7.gw.ivpn.net","host":"95.211.95.22","load":46.91,"multihop_port":22501,"obfs":{"obfs3_multihop_port":22502,"obfs4_multihop_port":22503,"obfs4_key":"ffMnq7Gc/D7KWoLckJ4t8nf3zZqVdlffe2lVfUCceOyOTdRApkeJGgEai0TI1z76Yey9Lw"}},{"hostname":"nl8.gw.ivpn.net","host":"95.211.172.18","load":12.83,"multihop_port":22801,"obfs":{"obfs3_multihop_port":22802,"obfs4_multihop_port":22803,"obfs4_key":"h+u/6VkPDJXySoJ6QEM1hOjWPD58OS4AZPP9ofP/yCWGBQpPoMc78l7C74eFvqKKkFAXDw"}}]},{"gateway":"no.gw.ivpn.net","country_code":"NO","country":"Norway","city":"Oslo","latitude":59.9167,"longitude":10.75,"isp":"Servetheworld","hosts":[{"hostname":"no1.gw.ivpn.net","host":"194.242.10.150","load":15.17,"multihop_port":25301,"obfs":{"obfs3_multihop_port":25302,"obfs4_multihop_port":25303,"obfs4_key":"uhLy//KRu6DrYfgDJmwjC6Fxk5h+MRDNOwFe7qzGTjfOiHLWRSoRx6OdNvzjPPXq0ZJnZg"}}]},{"gateway":"pl.gw.ivpn.net","country_code":"PL","country":"Poland","city":"Warsaw","latitude":52.25,"longitude":21,"isp":"Datapacket","hosts":[{"hostname":"pl1.gw.ivpn.net","host":"185.246.208.86","load":36.56,"multihop_port":25101,"obfs":{"obfs3_multihop_port":25102,"obfs4_multihop_port":25103,"obfs4_key":"S9XZyigxYjF5jWcwYpMmV9HJq4Vni1yQvLKI03n0TJ7csrgsX7lPpFfECAGQruHh1wkMXg"}}]},{"gateway":"pt.gw.ivpn.net","country_code":"PT","country":"Portugal","city":"Lisbon","latitude":38.7227,"longitude":-9.1449,"isp":"Hostwebis","hosts":[{"hostname":"pt1.gw.ivpn.net","host":"94.46.175.112","load":5.94,"multihop_port":27101,"obfs":{"obfs3_multihop_port":27102,"obfs4_multihop_port":27103,"obfs4_key":"NqXqKMt8UF25hYDIwfh2K4Rr4a7F41HzZDGjX7SYwRaoOtrTL665yV6Z3h9wF+/R1YE8KQ"}}]},{"gateway":"ro.gw.ivpn.net","country_code":"RO","country":"Romania","city":"Bucharest","latitude":44.4334,"longitude":26.0999,"isp":"M247","hosts":[{"hostname":"ro1.gw.ivpn.net","host":"37.120.206.50","load":43.52,"multihop_port":22301,"obfs":{"obfs3_multihop_port":22302,"obfs4_multihop_port":22303,"obfs4_key":"lqfg/sP9uLakoQiA6YI5/kHQ4/FvQTp6jRgxSswjHwC8POOM23FijEWKyykngn1Eb3xfLA"}}]},{"gateway":"rs.gw.ivpn.net","country_code":"RS","country":"Serbia","city":"Belgrade","latitude":44.8186,"longitude":20.468,"isp":"M247","hosts":[{"hostname":"rs1.gw.ivpn.net","host":"141.98.103.250","load":65.32,"multihop_port":26801,"obfs":{"obfs3_multihop_port":26802,"obfs4_multihop_port":26803,"obfs4_key":"0MqgxLrLFQTlQWGAjY9es+Nv45d37/5lulWw0iEFoiUvtzOcbut8hK9AhCis17EXi+lUXw"}}]},{"gateway":"se.gw.ivpn.net","country_code":"SE","country":"Sweden","city":"Stockholm","latitude":59.3508,"longitude":18.0973,"isp":"GleSyS","hosts":[{"hostname":"se1.gw.ivpn.net","host":"80.67.10.138","load":5.66,"multihop_port":24001,"obfs":{"obfs3_multihop_port":24002,"obfs4_multihop_port":24003,"obfs4_key":"/yhTV2SsTXlwsG2mCS90ZAYIZivsSyloaFw6VDj0pnpJOuUxXcZBOgdyQ/lfWxJtvHZmPg"}}]},{"gateway":"sg.gw.ivpn.net","country_code":"SG","country":"Singapore","city":"Singapore","latitude":1.293,"longitude":103.8558,"isp":"M247","hosts":[{"hostname":"sg1.gw.ivpn.net","host":"185.128.24.186","load":23.44,"multihop_port":26101,"obfs":{"obfs3_multihop_port":26102,"obfs4_multihop_port":26103,"obfs4_key":"0N1ZmZlnyhS/3Y1OhvB0Bk3BGU2LFy0zyuWPYwM/P+mfX57w8zI7/YcBfIXZVgDiStt4MQ"}}]},{"gateway":"sk.gw.ivpn.net","country_code":"SK","country":"Slovakia","city":"Bratislava","latitude":48.15,"longitude":17.117,"isp":"M247","hosts":[{"hostname":"sk1.gw.ivpn.net","host":"185.245.85.250","load":100,"multihop_port":25801,"obfs":{"obfs3_multihop_port":25802,"obfs4_multihop_port":25803,"obfs4_key":"8sl7oPfNHdCd2xYi98xWC6mBXyPvzio34UbfUbCPEU+8wo7DVbrR9mf8goR0Ievqzax4Hw"}}]},{"gateway":"tw.gw.ivpn.net","country_code":"TW","country":"Taiwan","city":"Taipei","latitude":25.073,"longitude":121.578,"isp":"TheGigabit","hosts":[{"hostname":"tw1.gw.ivpn.net","host":"185.189.160.6","load":8.89,"multihop_port":20820,"obfs":{"obfs3_multihop_port":20821,"obfs4_multihop_port":20822,"obfs4_key":"/ilRlB3BkUzUG2yd+++6KeYEF9PEB+3T3XcQhVjfoF8wJH1kgjGXvikjS1j/1SknFpGPaA"}}]},{"gateway":"ua.gw.ivpn.net","country_code":"UA","country":"Ukraine","city":"Kharkiv","latitude":50,"longitude":36.25,"isp":"Xservers","hosts":[{"hostname":"ua1.gw.ivpn.net","host":"176.103.58.123","load":6.43,"multihop_port":26301,"obfs":{"obfs3_multihop_port":26302,"obfs4_multihop_port":26303,"obfs4_key":"RsW3q+FmLATkKnKHOheUntwvslkrXEiCBx3ajDjhyHiZMtQI+Uy7TmhFK0YaHg/qKoljYw"}}]},{"gateway":"us-az.gw.ivpn.net","country_code":"US","country":"United States","city":"Phoenix, AZ","latitude":33.5722,"longitude":-112.0891,"isp":"M247","hosts":[{"hostname":"us-az1.gw.ivpn.net","host":"193.37.254.130","load":11.52,"multihop_port":26401,"obfs":{"obfs3_multihop_port":26402,"obfs4_multihop_port":26403,"obfs4_key":"Y2klMvUi3NBIReXSALaKnNm8qI9IdWhFwgQrwl9ofUuVNuT6D93ohTTqbW//iKS5/lqndw"}}]},{"gateway":"us-ca.gw.ivpn.net","country_code":"US","country":"United States","city":"Los Angeles, CA","latitude":34.1139,"longitude":-118.4068,"isp":"Quadranet","hosts":[{"hostname":"us-ca1.gw.ivpn.net","host":"173.254.196.58","load":9.34,"multihop_port":22201,"obfs":{"obfs3_multihop_port":22202,"obfs4_multihop_port":22203,"obfs4_key":"C/Ct5AG8tkV0Yi7MnXv+bNvExY8Dgii4OeJ7DA7lIq3HptUa+WbG+IAR/UIDaMn8VeXDIg"}},{"hostname":"us-ca2.gw.ivpn.net","host":"69.12.80.146","load":6.26,"multihop_port":22401,"obfs":{"obfs3_multihop_port":22402,"obfs4_multihop_port":22403,"obfs4_key":"nvizO9jj1D3xMqCMTnP6XDIzTLy6KcYNsDSxvJjm5QwZ9Y+5gommAk9LIcCeX9o4FjlkVA"}},{"hostname":"us-ca3.gw.ivpn.net","host":"198.54.129.99","load":12.79,"multihop_port":21301,"obfs":{"obfs3_multihop_port":21302,"obfs4_multihop_port":21303,"obfs4_key":"rharPyhkUzYwmCP8ZzeaCwnJAmwOqwDTLOlJqzi/kAxMn4OVXtbzCk9Ww8XR31D2rmk3MA"}},{"hostname":"us-ca4.gw.ivpn.net","host":"173.254.204.202","load":8.2,"multihop_port":21901,"obfs":{"obfs3_multihop_port":21902,"obfs4_multihop_port":21903,"obfs4_key":"2Sdam2VI658TdQRvj5bxwKPFljrsKgwBDv/mqhXNYgDQGs+WVhAz508b2oWVgHed3Ki8dQ"}}]},{"gateway":"us-fl.gw.ivpn.net","country_code":"US","country":"United States","city":"Miami, FL","latitude":25.7839,"longitude":-80.2102,"isp":"Quadranet","hosts":[{"hostname":"us-fl1.gw.ivpn.net","host":"173.44.49.90","load":8.5,"multihop_port":24601,"obfs":{"obfs3_multihop_port":24602,"obfs4_multihop_port":24603,"obfs4_key":"vS7u4rijvWECRS/Mo2KOpl8kx0NOozp3kF/FMxGmCZILE46etitrheYI2hCpNsYUg3YTBQ"}}]},{"gateway":"us-ga.gw.ivpn.net","country_code":"US","country":"United States","city":"Atlanta, GA","latitude":33.7627,"longitude":-84.4225,"isp":"Quadranet","hosts":[{"hostname":"us-ga1.gw.ivpn.net","host":"104.129.24.146","load":5.4,"multihop_port":24501,"obfs":{"obfs3_multihop_port":24502,"obfs4_multihop_port":24503,"obfs4_key":"+No53UtdyPN4uT89vMlvRTjFnxtMKol+oOld9I9WMnlK7BU+y10oXWofzcI4eRgkm195FQ"}},{"hostname":"us-ga2.gw.ivpn.net","host":"107.150.22.74","load":9,"multihop_port":24810,"obfs":{"obfs3_multihop_port":24811,"obfs4_multihop_port":24812,"obfs4_key":"3viWXkxZx1KZF9uzd5tRxb5lNMMnRZ90PqwLUaZdkZuaouj+Vb20t5uk3BVz/YyZkrSoGw"}}]},{"gateway":"us-il.gw.ivpn.net","country_code":"US","country":"United States","city":"Chicago, IL","latitude":41.8373,"longitude":-87.6862,"isp":"Quadranet","hosts":[{"hostname":"us-il1.gw.ivpn.net","host":"107.150.28.82","load":13.31,"multihop_port":21401,"obfs":{"obfs3_multihop_port":21402,"obfs4_multihop_port":21403,"obfs4_key":"6PpxOt8CwINAjun8o/wsf/cAidNbJZM/Pg4im1Cx9kCBV/lau3XFq3bMwBW0SzptZ/5WdA"}},{"hostname":"us-il2.gw.ivpn.net","host":"72.11.137.146","load":7.21,"multihop_port":24901,"obfs":{"obfs3_multihop_port":24902,"obfs4_multihop_port":24903,"obfs4_key":"cfvJR/gplqAbFx8myJSW/cPC3hN1782PPma2v5YKJR5LyekWX+AHTJjzdz8xSr2mM6IGQw"}}]},{"gateway":"us-nj.gw.ivpn.net","country_code":"US","country":"United States","city":"New Jersey, NJ","latitude":40.737764,"longitude":-74.151747,"isp":"Quadranet","hosts":[{"hostname":"us-nj3.gw.ivpn.net","host":"23.226.128.18","load":17.66,"multihop_port":21610,"obfs":{"obfs3_multihop_port":21611,"obfs4_multihop_port":21612,"obfs4_key":"JtB/8Lv8MOq9+bpYC751voS2CwoEwf9ku2CziwJScn3Gc1F/BTul6ehnrGiedmpkQXAtJw"}},{"hostname":"us-nj4.gw.ivpn.net","host":"194.36.111.50","load":14.38,"multihop_port":27401,"obfs":{"obfs3_multihop_port":27402,"obfs4_multihop_port":27403,"obfs4_key":"lHDLT6cTgt0bh3ysogDdiXL4eWGRtJZ0cZGJmvpeK0YLUAsBfsOzKv46V0iv5ykwRPm6Mg"}}]},{"gateway":"us-nv.gw.ivpn.net","country_code":"US","country":"United States","city":"Las Vegas, NV","latitude":36.2333,"longitude":-115.2654,"isp":"M247","hosts":[{"hostname":"us-nv1.gw.ivpn.net","host":"185.242.5.34","load":8.82,"multihop_port":26501,"obfs":{"obfs3_multihop_port":26502,"obfs4_multihop_port":26503,"obfs4_key":"Kux7OTSrUOkklATpG67hJPz7xCWpW6eD9Qdw2GNKAmaaPA5zvPC3SXQRvyrYdyAAqMXMfg"}}]},{"gateway":"us-ny.gw.ivpn.net","country_code":"US","country":"United States","city":"New York, NY","latitude":40.6943,"longitude":-73.9249,"isp":"M247","hosts":[{"hostname":"us-ny2.gw.ivpn.net","host":"212.103.48.194","load":8.1,"multihop_port":21801,"obfs":{"obfs3_multihop_port":21802,"obfs4_multihop_port":21803,"obfs4_key":"GtK9yCXTtrLOrf0MTWdWqjzQ1vd1eKsVjiOuR5CiVaDf4dCSalyJ+kmik4nfU9Gj6UwcCg"}},{"hostname":"us-ny3.gw.ivpn.net","host":"89.187.178.144","load":5.14,"multihop_port":27601,"obfs":{"obfs3_multihop_port":27602,"obfs4_multihop_port":27603,"obfs4_key":"eL/9iKx6f5bHowNJBsT2E/Aag5w8Cvtb3DduySudJmuK2GtIOUwNzS33lFZwSUEnJIYwLg"}}]},{"gateway":"us-tx.gw.ivpn.net","country_code":"US","country":"United States","city":"Dallas, TX","latitude":32.7936,"longitude":-96.7662,"isp":"Quadranet","hosts":[{"hostname":"us-tx1.gw.ivpn.net","host":"96.44.189.194","load":13.06,"multihop_port":21001,"obfs":{"obfs3_multihop_port":21002,"obfs4_multihop_port":21003,"obfs4_key":"GBDWrGN71fGa7a7S8b2tHzjYQMhnA2vJW5yq3iID+VoSzA1xIAgHiOxISEU93v+CBTCXWg"}},{"hostname":"us-tx2.gw.ivpn.net","host":"96.44.142.74","load":10.52,"multihop_port":25001,"obfs":{"obfs3_multihop_port":25002,"obfs4_multihop_port":25003,"obfs4_key":"VYfEVELWNqOm7kyO19usXm3GSK9wawRObopQLOLDbLodKnmX/jPah+IlGkL2xYiNxEzUKw"}}]},{"gateway":"us-ut.gw.ivpn.net","country_code":"US","country":"United States","city":"Salt Lake City, UT","latitude":40.7774,"longitude":-111.93,"isp":"100TB","hosts":[{"hostname":"us-ut1.gw.ivpn.net","host":"198.105.216.28","load":15.91,"multihop_port":24401,"obfs":{"obfs3_multihop_port":24402,"obfs4_multihop_port":24403,"obfs4_key":"gb5dp9i4QnFKT0GQPMEsDlqN+JORU3lyU65s9R1064aZ1jKfWfkc+SxbOlNEh23CvIAOeg"}}]},{"gateway":"us-va.gw.ivpn.net","country_code":"US","country":"United States","city":"Ashburn, VA","latitude":39.0437,"longitude":-77.4875,"isp":"Datapacket","hosts":[{"hostname":"us-va1.gw.ivpn.net","host":"37.19.206.105","load":22.97,"multihop_port":27701,"obfs":{"obfs3_multihop_port":27702,"obfs4_multihop_port":27703,"obfs4_key":"5Lk8BSID+M9QAGSglQTrYkom+V0mkIv0HgR80+9vKCcnZOu0f+aJhfOXqKnpHarJbIEHCw"}}]},{"gateway":"us-wa.gw.ivpn.net","country_code":"US","country":"United States","city":"Seattle, WA","latitude":47.6211,"longitude":-122.3244,"isp":"Tzulo","hosts":[{"hostname":"us-wa2.gw.ivpn.net","host":"198.44.131.3","load":14.58,"multihop_port":27801,"obfs":{"obfs3_multihop_port":27802,"obfs4_multihop_port":27803,"obfs4_key":"9h0W5BTzGSDpY2tGjdvViSJfEkT/BuwHS0cnFX07cEj3RRDtmxsC0f+kapO8+Kh+fz1XMQ"}}]},{"gateway":"za.gw.ivpn.net","country_code":"ZA","country":"South Africa","city":"Johannesburg","latitude":-26.195,"longitude":28.034,"isp":"Datapacket","hosts":[{"hostname":"za1.gw.ivpn.net","host":"169.150.238.103","load":8.1,"multihop_port":20840,"obfs":{"obfs3_multihop_port":20841,"obfs4_multihop_port":20842,"obfs4_key":"vM1zdiOMhoqMKZCOi/lMj7TfWJb5/UsM25p8FE/AUezwbYWhUkhpD5RbXBOBDNhZuYzPGA"}}]}],"config":{"antitracker":{"default":{"ip":"10.0.254.2","multihop-ip":"10.0.254.102"},"hardcore":{"ip":"10.0.254.3","multihop-ip":"10.0.254.103"}},"api":{"ips":["198.50.177.220","149.56.162.156","198.50.177.222","149.56.162.159","198.50.177.223"],"ipv6s":["2607:5300:203:1735::8888","2607:5300:203:1735::8","2607:5300:203:1735:6580:7300:0:aaaa"]},"ports":{"openvpn":[{"type":"UDP","port":53},{"type":"UDP","port":80},{"type":"UDP","range":{"min":5500,"max":19999}},{"type":"UDP","range":{"min":60000,"max":65000}},{"type":"UDP","port":123},{"type":"UDP","port":2049},{"type":"UDP","range":{"min":50000,"max":59999}},{"type":"UDP","port":2050},{"type":"UDP","range":{"min":40000,"max":49999}},{"type":"UDP","port":443},{"type":"UDP","port":1194},{"type":"UDP","range":{"min":30000,"max":39999}},{"type":"TCP","port":80},{"type":"TCP","port":443},{"type":"TCP","port":1194},{"type":"TCP","port":2049},{"type":"TCP","port":2050},{"type":"TCP","port":30587},{"type":"TCP","port":41893},{"type":"TCP","port":48574},{"type":"TCP","port":58237},{"type":"TCP","range":{"min":5500,"max":19999}},{"type":"TCP","range":{"min":30000,"max":65000}}],"wireguard":[{"type":"UDP","port":53},{"type":"UDP","port":80},{"type":"UDP","port":123},{"type":"UDP","port":443},{"type":"UDP","port":500},{"type":"UDP","port":1194},{"type":"UDP","port":2049},{"type":"UDP","port":2050},{"type":"UDP","port":4500},{"type":"UDP","port":30587},{"type":"UDP","port":41893},{"type":"UDP","port":48574},{"type":"UDP","port":58237},{"type":"UDP","range":{"min":5500,"max":19999}},{"type":"UDP","range":{"min":30000,"max":65000}}],"obfs3":{"port":5145},"obfs4":{"port":5146}}}} diff --git a/IVPNClient/Enums/ConnectionSettings.swift b/IVPNClient/Enums/ConnectionSettings.swift index 41c8d89dc..e816e41b7 100644 --- a/IVPNClient/Enums/ConnectionSettings.swift +++ b/IVPNClient/Enums/ConnectionSettings.swift @@ -114,10 +114,8 @@ enum ConnectionSettings { for protocolObj in protocols { var containsProtocol = false - for filteredProtocol in filteredProtocols { - if filteredProtocol.tunnelType() == protocolObj.tunnelType() { - containsProtocol = true - } + for filteredProtocol in filteredProtocols where filteredProtocol.tunnelType() == protocolObj.tunnelType() { + containsProtocol = true } if !containsProtocol { @@ -131,10 +129,8 @@ enum ConnectionSettings { func supportedProtocols(protocols: [ConnectionSettings]) -> [ConnectionSettings] { var filteredProtocols = [ConnectionSettings]() - for protocolObj in protocols { - if protocolObj.tunnelType() == self.tunnelType() { - filteredProtocols.append(protocolObj) - } + for protocolObj in protocols where protocolObj.tunnelType() == self.tunnelType() { + filteredProtocols.append(protocolObj) } return filteredProtocols diff --git a/IVPNClient/Enums/ServersSort.swift b/IVPNClient/Enums/ServersSort.swift index 745298c3f..4311bf613 100644 --- a/IVPNClient/Enums/ServersSort.swift +++ b/IVPNClient/Enums/ServersSort.swift @@ -24,8 +24,15 @@ import Foundation enum ServersSort: String, CaseIterable { + case city case country case latency case proximity + + static func actions() -> [String] { + let actions = allCases.map { $0.rawValue } + return actions.map { $0.camelCaseToCapitalized() ?? "" } + } + } diff --git a/IVPNClient/Managers/ApiService+Ext.swift b/IVPNClient/Managers/ApiService+Ext.swift index e32334888..29ebe0897 100644 --- a/IVPNClient/Managers/ApiService+Ext.swift +++ b/IVPNClient/Managers/ApiService+Ext.swift @@ -32,7 +32,7 @@ extension ApiService { UIApplication.shared.isNetworkActivityIndicatorVisible = true - log(.info, message: "Load servers") + log(.info, message: "Load servers") APIClient().perform(request) { result in switch result { diff --git a/IVPNClient/Managers/StorageManager.swift b/IVPNClient/Managers/StorageManager.swift index 77c51497c..f2eb448fd 100644 --- a/IVPNClient/Managers/StorageManager.swift +++ b/IVPNClient/Managers/StorageManager.swift @@ -326,20 +326,32 @@ extension StorageManager { extension StorageManager { - static func saveServer(gateway: String, isFastestEnabled: Bool) { - if let server = fetchServer(gateway: gateway) { + static func save(server: VPNServer, isFastestEnabled: Bool) { + if let server = fetchServer(server: server) { server.isFastestEnabled = isFastestEnabled } else { let newServer = Server(context: context) - newServer.gateway = gateway.replacingOccurrences(of: ".wg.", with: ".gw.") + newServer.gateway = server.gateway.replacingOccurrences(of: ".wg.", with: ".gw.") newServer.isFastestEnabled = isFastestEnabled } saveContext() } - static func fetchServers(gateway: String = "", isFastestEnabled: Bool = false) -> [Server]? { - let request: NSFetchRequest = Server.fetchRequest(gateway: gateway, isFastestEnabled: isFastestEnabled) + static func save(server: VPNServer, isFavorite: Bool) { + if let server = fetchServer(server: server) { + server.isFavorite = isFavorite + } else { + let newServer = Server(context: context) + newServer.gateway = server.dnsName ?? server.gateway.replacingOccurrences(of: ".wg.", with: ".gw.") + newServer.isFavorite = isFavorite + } + + saveContext() + } + + static func fetchServers(gateway: String = "", isFastestEnabled: Bool = false, isFavorite: Bool = false, isHost: Bool = false) -> [Server]? { + let request: NSFetchRequest = Server.fetchRequest(gateway: gateway, isFastestEnabled: isFastestEnabled, isFavorite: isFavorite, isHost: isHost) do { let result = try context.fetch(request) @@ -353,8 +365,8 @@ extension StorageManager { return nil } - static func fetchServer(gateway: String) -> Server? { - if let servers = fetchServers(gateway: gateway) { + static func fetchServer(server: VPNServer) -> Server? { + if let servers = fetchServers(gateway: server.dnsName ?? server.gateway, isHost: server.isHost) { if let server = servers.first { return server } @@ -364,15 +376,25 @@ extension StorageManager { } static func isFastestEnabled(server vpnServer: VPNServer) -> Bool { - if let server = fetchServer(gateway: vpnServer.gateway) { + if let server = fetchServer(server: vpnServer) { return server.isFastestEnabled } return false } + static func isFavorite(server: VPNServer) -> Bool { + if let servers = fetchServers(gateway: server.dnsName ?? server.gateway, isFavorite: true, isHost: server.isHost) { + return !servers.isEmpty + } + + return false + } + static func canUpdateServer(isOn: Bool) -> Bool { - guard !isOn else { return true } + guard !isOn else { + return true + } if UserDefaults.standard.bool(forKey: UserDefaults.Key.fastestServerConfigured) { if let servers = fetchServers(isFastestEnabled: true) { diff --git a/IVPNClient/Model.xcdatamodeld/Model.xcdatamodel/contents b/IVPNClient/Model.xcdatamodeld/Model.xcdatamodel/contents index 4a78cee8f..2fc99f73c 100644 --- a/IVPNClient/Model.xcdatamodeld/Model.xcdatamodel/contents +++ b/IVPNClient/Model.xcdatamodeld/Model.xcdatamodel/contents @@ -1,5 +1,5 @@ - + @@ -14,5 +14,6 @@ + \ No newline at end of file diff --git a/IVPNClient/Models/CoreData/Server+CoreDataProperties.swift b/IVPNClient/Models/CoreData/Server+CoreDataProperties.swift index 8e323e975..23578d462 100644 --- a/IVPNClient/Models/CoreData/Server+CoreDataProperties.swift +++ b/IVPNClient/Models/CoreData/Server+CoreDataProperties.swift @@ -28,19 +28,28 @@ extension Server { @NSManaged public var gateway: String? @NSManaged public var isFastestEnabled: Bool + @NSManaged public var isFavorite: Bool - @nonobjc public class func fetchRequest(gateway: String = "", isFastestEnabled: Bool = false) -> NSFetchRequest { + @nonobjc public class func fetchRequest(gateway: String = "", isFastestEnabled: Bool = false, isFavorite: Bool = false, isHost: Bool = false) -> NSFetchRequest { let fetchRequest = NSFetchRequest(entityName: "Server") var filters = [NSPredicate]() if !gateway.isEmpty { - filters.append(NSPredicate(format: "gateway == %@", gateway.replacingOccurrences(of: ".wg.", with: ".gw."))) + if isHost { + filters.append(NSPredicate(format: "gateway == %@", gateway)) + } else { + filters.append(NSPredicate(format: "gateway == %@", gateway.replacingOccurrences(of: ".wg.", with: ".gw."))) + } } if isFastestEnabled { filters.append(NSPredicate(format: "isFastestEnabled == %@", NSNumber(value: isFastestEnabled))) } + if isFavorite { + filters.append(NSPredicate(format: "isFavorite == %@", NSNumber(value: isFavorite))) + } + fetchRequest.predicate = NSCompoundPredicate(andPredicateWithSubpredicates: filters) return fetchRequest diff --git a/IVPNClient/Models/Host.swift b/IVPNClient/Models/Host.swift index 1b62c7f87..d1a2dc0ae 100644 --- a/IVPNClient/Models/Host.swift +++ b/IVPNClient/Models/Host.swift @@ -31,6 +31,7 @@ struct Host: Codable { var host: String var hostName: String + var dnsName: String var publicKey: String var localIP: String var ipv6: IPv6? diff --git a/IVPNClient/Models/PortRange.swift b/IVPNClient/Models/PortRange.swift index 36c87f87f..97009e3fb 100644 --- a/IVPNClient/Models/PortRange.swift +++ b/IVPNClient/Models/PortRange.swift @@ -45,10 +45,8 @@ struct PortRange { // MARK: - Methods - func validate(port: Int) -> String? { - for range in ranges { - if range.contains(port) { - return nil - } + for range in ranges where range.contains(port) { + return nil } return "Enter port number in the range: \(portRangesText)" @@ -58,22 +56,17 @@ struct PortRange { var combined = [CountableClosedRange]() var accumulator = (0...0) // empty range - for interval in intervals.sorted(by: { $0.lowerBound < $1.lowerBound } ) { - + for interval in intervals.sorted(by: { $0.lowerBound < $1.lowerBound }) { if accumulator == (0...0) { accumulator = interval } if accumulator.upperBound >= interval.upperBound { // interval is already inside accumulator - } - - else if accumulator.upperBound + 1 >= interval.lowerBound { + } else if accumulator.upperBound + 1 >= interval.lowerBound { // interval hangs off the back end of accumulator accumulator = (accumulator.lowerBound...interval.upperBound) - } - - else if accumulator.upperBound <= interval.lowerBound { + } else if accumulator.upperBound <= interval.lowerBound { // interval does not overlap combined.append(accumulator) accumulator = interval diff --git a/IVPNClient/Models/Settings.swift b/IVPNClient/Models/Settings.swift index f3137bddf..ffee1f377 100644 --- a/IVPNClient/Models/Settings.swift +++ b/IVPNClient/Models/Settings.swift @@ -64,6 +64,8 @@ class Settings { } } + var serverListIsFavorite = false + // MARK: - Initialize - init(serverList: VPNServerList) { diff --git a/IVPNClient/Models/VPNServer.swift b/IVPNClient/Models/VPNServer.swift index 30c51621a..72ad2f4b2 100644 --- a/IVPNClient/Models/VPNServer.swift +++ b/IVPNClient/Models/VPNServer.swift @@ -59,20 +59,20 @@ class VPNServer { } var supportsIPv6: Bool { - for host in hosts { - if host.ipv6 == nil { - return false - } + for host in hosts where host.ipv6 == nil { + return false } return true } var enabledIPv6: Bool { - for host in hosts { - if !(host.ipv6?.localIP.isEmpty ?? true) { - return true - } + for host in hosts where !(host.ipv6?.localIP.isEmpty ?? true) { + return true + } + + if let ipv6 = ipv6 { + return !ipv6.localIP.isEmpty } return false @@ -95,11 +95,14 @@ class VPNServer { private (set) var ipAddresses: [String] private (set) var hosts: [Host] private (set) var load: Double? + private (set) var ipv6: IPv6? + var dnsName: String? // MARK: - Initialize - - init(gateway: String, countryCode: String, country: String, city: String, latitude: Double = 0, longitude: Double = 0, ipAddresses: [String] = [], hosts: [Host] = [], fastest: Bool = false, load: Double = 0) { + init(gateway: String, dnsName: String? = nil, countryCode: String, country: String, city: String, latitude: Double = 0, longitude: Double = 0, ipAddresses: [String] = [], hosts: [Host] = [], fastest: Bool = false, load: Double = 0, ipv6: IPv6? = nil) { self.gateway = gateway + self.dnsName = dnsName self.countryCode = countryCode self.country = country self.city = city @@ -109,6 +112,7 @@ class VPNServer { self.hosts = hosts self.fastest = fastest self.load = load + self.ipv6 = ipv6 } // MARK: - Methods - diff --git a/IVPNClient/Models/VPNServerList.swift b/IVPNClient/Models/VPNServerList.swift index 89b5be93a..67ccd9581 100644 --- a/IVPNClient/Models/VPNServerList.swift +++ b/IVPNClient/Models/VPNServerList.swift @@ -35,14 +35,11 @@ class VPNServerList { open private(set) var portRanges: [PortRange] var filteredFastestServers: [VPNServer] { - var serversArray = getServers() - let fastestServerConfigured = UserDefaults.standard.bool(forKey: UserDefaults.Key.fastestServerConfigured) - - if fastestServerConfigured { - serversArray = serversArray.filter { StorageManager.isFastestEnabled(server: $0) } + if UserDefaults.standard.bool(forKey: UserDefaults.Key.fastestServerConfigured) { + return getServers().filter { StorageManager.isFastestEnabled(server: $0) } } - return serversArray + return getServers() } var noPing: Bool { @@ -219,7 +216,7 @@ class VPNServerList { return servers } - func getAllHosts(_ servers: [VPNServer]? = nil) -> [VPNServer] { + func getAllHosts(_ servers: [VPNServer]? = nil, isFavorite: Bool = false) -> [VPNServer] { var allHosts: [VPNServer] = [] let allServers = servers ?? getServers() @@ -228,13 +225,25 @@ class VPNServerList { continue } + if server.hosts.count == 1 { + server.dnsName = server.hosts.first!.dnsName + } + allHosts.append(server) + if isFavorite && server.hosts.count == 1 { + continue + } + for host in server.hosts { - allHosts.append(VPNServer(gateway: host.hostName, countryCode: server.countryCode, country: "", city: server.city, load: host.load)) + allHosts.append(VPNServer(gateway: host.hostName, dnsName: host.dnsName, countryCode: server.countryCode, country: "", city: server.city, load: host.load, ipv6: host.ipv6)) } } + if isFavorite { + return allHosts.filter { StorageManager.isFavorite(server: $0) } + } + return allHosts } @@ -313,7 +322,7 @@ class VPNServerList { func saveAllServers(exceptionGateway: String) { for server in servers { let isFastestEnabled = server.gateway != exceptionGateway - StorageManager.saveServer(gateway: server.gateway, isFastestEnabled: isFastestEnabled) + StorageManager.save(server: server, isFastestEnabled: isFastestEnabled) } } @@ -367,6 +376,7 @@ class VPNServerList { var newHost = Host( host: host["host"] as? String ?? "", hostName: host["hostname"] as? String ?? "", + dnsName: host["dns_name"] as? String ?? "", publicKey: host["public_key"] as? String ?? "", localIP: host["local_ip"] as? String ?? "", multihopPort: host["multihop_port"] as? Int ?? 0, diff --git a/IVPNClient/Scenes/Base.lproj/Main.storyboard b/IVPNClient/Scenes/Base.lproj/Main.storyboard index 6c437913d..0fbb5b94a 100644 --- a/IVPNClient/Scenes/Base.lproj/Main.storyboard +++ b/IVPNClient/Scenes/Base.lproj/Main.storyboard @@ -21,7 +21,7 @@ - + - + - + @@ -3218,7 +3307,7 @@ @@ -3260,7 +3349,16 @@ - + + + + + + + + + + @@ -4153,16 +4251,15 @@ + - + - - @@ -4190,9 +4287,6 @@ - - - diff --git a/IVPNClient/Scenes/MainScreen/Map/ConnectToServerPopupView.swift b/IVPNClient/Scenes/MainScreen/Map/ConnectToServerPopupView.swift index db91ad7e2..86a1e3118 100644 --- a/IVPNClient/Scenes/MainScreen/Map/ConnectToServerPopupView.swift +++ b/IVPNClient/Scenes/MainScreen/Map/ConnectToServerPopupView.swift @@ -64,7 +64,7 @@ class ConnectToServerPopupView: UIView { return locationLabel }() - var actionButton: UIButton = { + lazy var actionButton: UIButton = { let actionButton = UIButton() actionButton.setTitle("CONNECT TO SERVER", for: .normal) actionButton.titleLabel?.font = UIFont.systemFont(ofSize: 16) @@ -75,7 +75,7 @@ class ConnectToServerPopupView: UIView { return actionButton }() - var prevButton: UIButton = { + lazy var prevButton: UIButton = { let prevButton = UIButton() prevButton.setImage(UIImage.init(named: "icon-arrow-left-gray"), for: .normal) prevButton.addTarget(self, action: #selector(prevAction), for: .touchUpInside) @@ -84,7 +84,7 @@ class ConnectToServerPopupView: UIView { return prevButton }() - var nextButton: UIButton = { + lazy var nextButton: UIButton = { let nextButton = UIButton() nextButton.setImage(UIImage.init(named: "icon-arrow-right-gray"), for: .normal) nextButton.addTarget(self, action: #selector(nextAction), for: .touchUpInside) diff --git a/IVPNClient/Scenes/MainScreen/Map/ConnectionInfoPopupView.swift b/IVPNClient/Scenes/MainScreen/Map/ConnectionInfoPopupView.swift index a5e90e340..026b26e12 100644 --- a/IVPNClient/Scenes/MainScreen/Map/ConnectionInfoPopupView.swift +++ b/IVPNClient/Scenes/MainScreen/Map/ConnectionInfoPopupView.swift @@ -72,7 +72,7 @@ class ConnectionInfoPopupView: UIView { return locationLabel }() - var actionButton: UIButton = { + lazy var actionButton: UIButton = { let actionButton = UIButton() actionButton.setImage(UIImage.init(named: "icon-info-2"), for: .normal) actionButton.accessibilityLabel = "Connection info details" diff --git a/IVPNClient/Scenes/MainScreen/View/ControlPanelView.swift b/IVPNClient/Scenes/MainScreen/View/ControlPanelView.swift index cf3e39fda..1043a4890 100644 --- a/IVPNClient/Scenes/MainScreen/View/ControlPanelView.swift +++ b/IVPNClient/Scenes/MainScreen/View/ControlPanelView.swift @@ -35,14 +35,15 @@ class ControlPanelView: UITableView { @IBOutlet weak var exitServerTableCell: UITableViewCell! @IBOutlet weak var exitServerConnectionLabel: UILabel! @IBOutlet weak var exitServerNameLabel: UILabel! + @IBOutlet weak var exitServerCountryLabel: UILabel! @IBOutlet weak var exitServerFlagImage: UIImageView! @IBOutlet weak var exitServerIPv6Label: UILabel! @IBOutlet weak var entryServerTableCell: UITableViewCell! @IBOutlet weak var entryServerConnectionLabel: UILabel! @IBOutlet weak var entryServerNameLabel: UILabel! + @IBOutlet weak var entryServerCountryLabel: UILabel! @IBOutlet weak var entryServerFlagImage: UIImageView! @IBOutlet weak var entryServerIPv6Label: UILabel! - @IBOutlet weak var fastestServerLabel: UIView! @IBOutlet weak var antiTrackerSwitch: UISwitch! @IBOutlet weak var antiTrackerLabel: UILabel! @IBOutlet weak var networkView: NetworkViewTableCell! @@ -113,7 +114,8 @@ class ControlPanelView: UITableView { updateServerName(server: Application.shared.settings.selectedServer, label: entryServerNameLabel, flag: entryServerFlagImage, ipv6Label: entryServerIPv6Label, selectedHost: Application.shared.settings.selectedHost) updateServerName(server: Application.shared.settings.selectedExitServer, label: exitServerNameLabel, flag: exitServerFlagImage, ipv6Label: exitServerIPv6Label, selectedHost: Application.shared.settings.selectedExitHost) - fastestServerLabel.isHidden = true + entryServerCountryLabel.text = Application.shared.settings.selectedServer.country + exitServerCountryLabel.text = Application.shared.settings.selectedExitServer.country } func updateAntiTracker(viewModel: VPNStatusViewModel) { diff --git a/IVPNClient/Scenes/TableCells/NetworkProtectionHeaderTableViewCell.swift b/IVPNClient/Scenes/TableCells/NetworkProtectionHeaderTableViewCell.swift index 53680cd4a..c2fabd81c 100644 --- a/IVPNClient/Scenes/TableCells/NetworkProtectionHeaderTableViewCell.swift +++ b/IVPNClient/Scenes/TableCells/NetworkProtectionHeaderTableViewCell.swift @@ -23,7 +23,7 @@ import UIKit -protocol NetworkProtectionHeaderTableViewCellDelegate: AnyObject { +protocol NetworkProtectionTableCellDelegate: AnyObject { func toggle(isOn: Bool) } @@ -31,7 +31,7 @@ class NetworkProtectionHeaderTableViewCell: UITableViewCell { @IBOutlet weak var toggleSwitch: UISwitch! - weak var delegate: NetworkProtectionHeaderTableViewCellDelegate? + weak var delegate: NetworkProtectionTableCellDelegate? @IBAction func toggle(_ sender: UISwitch) { delegate?.toggle(isOn: sender.isOn) diff --git a/IVPNClient/Scenes/TableCells/PortTableViewCell.swift b/IVPNClient/Scenes/TableCells/PortTableViewCell.swift index 8f104096e..d49f4a134 100644 --- a/IVPNClient/Scenes/TableCells/PortTableViewCell.swift +++ b/IVPNClient/Scenes/TableCells/PortTableViewCell.swift @@ -37,10 +37,8 @@ class PortTableViewCell: UITableViewCell { } private func isCustom(port: ConnectionSettings, ports: [ConnectionSettings]) -> Bool { - for portObj in ports { - if portObj.port() == port.port() { - return false - } + for portObj in ports where portObj.port() == port.port() { + return false } return true diff --git a/IVPNClient/Scenes/TableCells/ServerConfigurationCell.swift b/IVPNClient/Scenes/TableCells/ServerConfigurationCell.swift index 29aebef66..15c16074e 100644 --- a/IVPNClient/Scenes/TableCells/ServerConfigurationCell.swift +++ b/IVPNClient/Scenes/TableCells/ServerConfigurationCell.swift @@ -24,7 +24,7 @@ import UIKit protocol ServerConfigurationCellDelegate: AnyObject { - func toggle(isOn: Bool, gateway: String) + func toggle(isOn: Bool, server: VPNServer) func showValidation(error: String) } @@ -43,7 +43,7 @@ class ServerConfigurationCell: UITableViewCell { return } - delegate?.toggle(isOn: sender.isOn, gateway: viewModel.server.gateway) + delegate?.toggle(isOn: sender.isOn, server: viewModel.server) } var viewModel: VPNServerViewModel! { diff --git a/IVPNClient/Scenes/TableCells/ServerTableViewCell.swift b/IVPNClient/Scenes/TableCells/ServerTableViewCell.swift index 8240024a9..53ab051e2 100644 --- a/IVPNClient/Scenes/TableCells/ServerTableViewCell.swift +++ b/IVPNClient/Scenes/TableCells/ServerTableViewCell.swift @@ -26,19 +26,19 @@ import UIKit class ServerTableViewCell: UITableViewCell { @IBOutlet weak var flagImage: FlagImageView! - @IBOutlet weak var serverLeftConstraint: NSLayoutConstraint! @IBOutlet weak var serverName: UILabel! @IBOutlet weak var pingImage: UIImageView! @IBOutlet weak var pingTimeMs: UILabel! @IBOutlet weak var configureButton: UIButton! @IBOutlet weak var ipv6Label: UILabel! @IBOutlet weak var expandButton: UIButton! + @IBOutlet weak var favoriteButton: UIButton! var viewModel: VPNServerViewModel! { didSet { - if !isMultiHop && indexPath.row == 0 { + if !isMultiHop && indexPath.row == 0 && !isFavorite { setFastestServerCell() - } else if isMultiHop && indexPath.row == 0 || !isMultiHop && indexPath.row == 1 { + } else if (isMultiHop && indexPath.row == 0 || !isMultiHop && indexPath.row == 1) && !isFavorite { setRandomServerCell() } else if viewModel.server.isHost { setHostServerCell() @@ -83,14 +83,28 @@ class ServerTableViewCell: UITableViewCell { } var isMultiHop: Bool! + var isFavorite = false + + // MARK: - IBActions - + + @IBAction func toggleFavorite(_ sender: UIButton) { + let isFavorite = StorageManager.isFavorite(server: viewModel.server) + sender.setImage(UIImage.init(named: !isFavorite ? "icon-star-on" : "icon-star-off"), for: .normal) + StorageManager.save(server: viewModel.server, isFavorite: !isFavorite) + + if let tableView = superview as? UITableView { + tableView.reloadData() + } + } // MARK: - Methods - private func setCellAppearance() { + let isServerFavorite = StorageManager.isFavorite(server: viewModel.server) flagImage.updateUpFlagIcon() - serverName.sizeToFit() - ipv6Label.isHidden = !viewModel.showIPv6Label + ipv6Label.isHidden = !viewModel.showIPv6Label || (viewModel.server.isHost && !isFavorite) expandButton.tintColor = UIColor.init(named: Theme.ivpnGray6) + favoriteButton.setImage(UIImage.init(named: isServerFavorite ? "icon-star-on" : "icon-star-off"), for: .normal) } private func setFastestServerCell() { @@ -100,6 +114,7 @@ class ServerTableViewCell: UITableViewCell { configureButton.isHidden = false configureButton.isUserInteractionEnabled = true expandButton.isHidden = true + favoriteButton.isHidden = true } private func setRandomServerCell() { @@ -109,6 +124,7 @@ class ServerTableViewCell: UITableViewCell { configureButton.isHidden = true configureButton.isUserInteractionEnabled = true expandButton.isHidden = true + favoriteButton.isHidden = true } private func setGatewayServerCell() { @@ -118,16 +134,22 @@ class ServerTableViewCell: UITableViewCell { serverName.text = viewModel.formattedServerName(sort: sort) configureButton.isHidden = true configureButton.isUserInteractionEnabled = false - expandButton.isHidden = !UserDefaults.shared.selectHost + expandButton.isHidden = !UserDefaults.shared.selectHost || isFavorite + favoriteButton.isHidden = false } private func setHostServerCell() { serverName.text = viewModel.formattedServerName configureButton.isHidden = true configureButton.isUserInteractionEnabled = false - flagImage.image = nil - flagImage.image?.accessibilityIdentifier = "" expandButton.isHidden = true + favoriteButton.isHidden = false + flagImage.image?.accessibilityIdentifier = "" + flagImage.image = isFavorite ? viewModel.imageForCountryCode : nil + + if let gateway = Application.shared.serverList.getServer(byCity: viewModel.server.city), gateway.hosts.count <= 1 { + favoriteButton.isHidden = true + } } private func setPingTime() { diff --git a/IVPNClient/Scenes/ViewControllers/NetworkProtectionViewController.swift b/IVPNClient/Scenes/ViewControllers/NetworkProtectionViewController.swift index ff75a5dd7..33869dbf7 100644 --- a/IVPNClient/Scenes/ViewControllers/NetworkProtectionViewController.swift +++ b/IVPNClient/Scenes/ViewControllers/NetworkProtectionViewController.swift @@ -230,9 +230,9 @@ extension NetworkProtectionViewController { } -// MARK: - NetworkProtectionHeaderTableViewCellDelegate - +// MARK: - NetworkProtectionTableCellDelegate - -extension NetworkProtectionViewController: NetworkProtectionHeaderTableViewCellDelegate { +extension NetworkProtectionViewController: NetworkProtectionTableCellDelegate { func toggle(isOn: Bool) { defaults.set(isOn, forKey: UserDefaults.Key.networkProtectionEnabled) diff --git a/IVPNClient/Scenes/ViewControllers/ServerViewController.swift b/IVPNClient/Scenes/ViewControllers/ServerViewController.swift index b5e6bf1d7..f41ecd9ff 100644 --- a/IVPNClient/Scenes/ViewControllers/ServerViewController.swift +++ b/IVPNClient/Scenes/ViewControllers/ServerViewController.swift @@ -32,36 +32,21 @@ class ServerViewController: UITableViewController { // MARK: - @IBOutlets - @IBOutlet weak var searchBar: UISearchBar! + @IBOutlet weak var favoriteControl: UISegmentedControl! + @IBOutlet weak var headerView: UIView! + @IBOutlet weak var emptyView: UIView! // MARK: - Properties - var isExitServer = false + var isFavorite: Bool { + return Application.shared.settings.serverListIsFavorite + } var filteredCollection = [VPNServer]() weak var serverDelegate: ServerViewControllerDelegate? - private var collection: [VPNServer] { - var list = [VPNServer]() - - if isSearchActive { - list = filteredCollection - } else { - list = Application.shared.serverList.getAllHosts() - } - - list.insert(VPNServer(gateway: "", countryCode: "", country: "", city: "", fastest: false), at: 0) - - if !UserDefaults.shared.isMultiHop { - list.insert(VPNServer(gateway: "", countryCode: "", country: "", city: "", fastest: true), at: 0) - } - - return list - } - - private var expandedGateways: [String] = [] - - private var isSearchActive: Bool { - return !searchBar.text!.isEmpty - } + private var collection = [VPNServer]() + private var expandedGateways = [String]() // MARK: - IBActions - @@ -73,8 +58,7 @@ class ServerViewController: UITableViewController { } @IBAction func sortBy(_ sender: Any) { - let actionsRawValue = ServersSort.allCases.map { $0.rawValue } - let actions = actionsRawValue.map { $0.camelCaseToCapitalized() ?? "" } + let actions = ServersSort.actions() let selected = UserDefaults.shared.serversSort.camelCaseToCapitalized() ?? "" showActionSheet(image: nil, selected: selected, largeText: true, centered: true, title: "Sort by", actions: actions, sourceView: tableView) { [self] index in @@ -84,7 +68,7 @@ class ServerViewController: UITableViewController { UserDefaults.shared.set(sort.rawValue, forKey: UserDefaults.Key.serversSort) Application.shared.serverList.sortServers() filteredCollection = VPNServerList.sort(filteredCollection) - filteredCollection = Application.shared.serverList.getAllHosts(filteredCollection) + filteredCollection = Application.shared.serverList.getAllHosts(filteredCollection, isFavorite: isFavorite) tableView.reloadData() } } @@ -105,6 +89,11 @@ class ServerViewController: UITableViewController { tableView.reloadData() } + @IBAction func toggleFavorite(_ sender: UISegmentedControl) { + Application.shared.settings.serverListIsFavorite = sender.selectedSegmentIndex == 1 + searchTextDidChange(searchText: searchBar.text!) + } + // MARK: - View Lifecycle - override func viewDidLoad() { @@ -112,6 +101,10 @@ class ServerViewController: UITableViewController { initNavigationBar() Application.shared.serverList.sortServers() tableView.keyboardDismissMode = .onDrag + tableView.backgroundColor = UIColor.init(named: Theme.ivpnBackgroundPrimary) + favoriteControl.selectedSegmentIndex = Application.shared.settings.serverListIsFavorite ? 1 : 0 + restore() + collection = getCollection() } override func viewWillAppear(_ animated: Bool) { @@ -150,8 +143,46 @@ class ServerViewController: UITableViewController { tableView.reloadData() } + func showEmptyView() { + tableView.backgroundView = emptyView + headerView.frame = CGRect(x: 0, y: 0, width: headerView.frame.size.width, height: 41) + emptyView.frame = CGRect(x: 0, y: 0, width: emptyView.frame.size.width, height: 320) + emptyView.isHidden = false + searchBar.isHidden = true + } + + func restore() { + tableView.backgroundView = nil + headerView.frame = CGRect(x: 0, y: 0, width: headerView.frame.size.width, height: 115) + emptyView.frame = CGRect(x: 0, y: 0, width: emptyView.frame.size.width, height: 0) + emptyView.isHidden = true + searchBar.isHidden = false + } + // MARK: - Methods - + private func getCollection() -> [VPNServer] { + var list = [VPNServer]() + + if !searchBar.text!.isEmpty { + list = filteredCollection + } else { + list = Application.shared.serverList.getAllHosts(isFavorite: isFavorite) + } + + if isFavorite { + return list + } + + list.insert(VPNServer(gateway: "", countryCode: "", country: "", city: "", fastest: false), at: 0) + + if !UserDefaults.shared.isMultiHop { + list.insert(VPNServer(gateway: "", countryCode: "", country: "", city: "", fastest: true), at: 0) + } + + return list + } + private func initNavigationBar() { title = "Select Server" @@ -191,6 +222,23 @@ class ServerViewController: UITableViewController { } } + private func searchTextDidChange(searchText: String) { + guard !searchText.isEmpty else { + tableView.reloadData() + return + } + + let collection = Application.shared.serverList.getServers() + filteredCollection.removeAll(keepingCapacity: false) + filteredCollection = collection.filter { (server: VPNServer) -> Bool in + let location = "\(server.city) \(server.countryCode)".lowercased() + return location.contains(searchText.lowercased()) + } + filteredCollection = VPNServerList.sort(filteredCollection) + filteredCollection = Application.shared.serverList.getAllHosts(filteredCollection, isFavorite: isFavorite) + tableView.reloadData() + } + } // MARK: - UITableViewDataSource - @@ -198,6 +246,14 @@ class ServerViewController: UITableViewController { extension ServerViewController { override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + collection = getCollection() + + if isFavorite && collection.isEmpty && searchBar.text!.isEmpty { + showEmptyView() + } else { + restore() + } + return collection.count } @@ -205,6 +261,7 @@ extension ServerViewController { let server = collection[indexPath.row] let cell = tableView.dequeueReusableCell(withIdentifier: "ServerTableViewCell", for: indexPath) as! ServerTableViewCell cell.isMultiHop = UserDefaults.shared.isMultiHop + cell.isFavorite = isFavorite cell.indexPath = indexPath cell.viewModel = VPNServerViewModel(server: server) cell.serverToValidate = isExitServer ? Application.shared.settings.selectedServer : Application.shared.settings.selectedExitServer @@ -248,7 +305,7 @@ extension ServerViewController { } } - if (!UserDefaults.shared.isMultiHop && indexPath.row == 1) || (UserDefaults.shared.isMultiHop && indexPath.row == 0) { + if ((!UserDefaults.shared.isMultiHop && indexPath.row == 1) || (UserDefaults.shared.isMultiHop && indexPath.row == 0)) && !isFavorite { server = Application.shared.serverList.getRandomServer(isExitServer: isExitServer) server.random = true server.fastest = false @@ -272,7 +329,7 @@ extension ServerViewController { Application.shared.settings.selectedExitServer = server Application.shared.settings.selectedExitHost = selectedHost } else { - if UserDefaults.shared.isMultiHop || indexPath.row > 0 || server.random { + if UserDefaults.shared.isMultiHop || indexPath.row > 0 || server.random || isFavorite { Application.shared.settings.selectedServer = server Application.shared.settings.selectedHost = selectedHost Application.shared.settings.selectedServer.fastest = false @@ -324,7 +381,7 @@ extension ServerViewController { override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { let server = collection[indexPath.row] - if server.isHost && !expandHost(server) { + if server.isHost && !expandHost(server) && !isFavorite { return 0 } @@ -338,15 +395,7 @@ extension ServerViewController { extension ServerViewController: UISearchBarDelegate { func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) { - let collection = Application.shared.serverList.getServers() - filteredCollection.removeAll(keepingCapacity: false) - filteredCollection = collection.filter { (server: VPNServer) -> Bool in - let location = "\(server.city) \(server.countryCode)".lowercased() - return location.contains(searchBar.text!.lowercased()) - } - filteredCollection = VPNServerList.sort(filteredCollection) - filteredCollection = Application.shared.serverList.getAllHosts(filteredCollection) - tableView.reloadData() + searchTextDidChange(searchText: searchBar.text!) } func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) { diff --git a/IVPNClient/Scenes/ViewControllers/ServersConfigurationTableViewController.swift b/IVPNClient/Scenes/ViewControllers/ServersConfigurationTableViewController.swift index 42c8e8a0a..48961ac46 100644 --- a/IVPNClient/Scenes/ViewControllers/ServersConfigurationTableViewController.swift +++ b/IVPNClient/Scenes/ViewControllers/ServersConfigurationTableViewController.swift @@ -25,14 +25,44 @@ import UIKit class ServersConfigurationTableViewController: UITableViewController { + // MARK: - @IBOutlets - + + @IBOutlet weak var searchBar: UISearchBar! + // MARK: - Properties - - var collection = Application.shared.serverList.getServers() + private var filteredCollection = [VPNServer]() + + private var collection: [VPNServer] { + if !searchBar.text!.isEmpty { + return filteredCollection + } + + return Application.shared.serverList.getServers() + } + + // MARK: - IBActions - + + @IBAction func sortBy(_ sender: Any) { + let actions = ServersSort.actions() + let selected = UserDefaults.shared.serversSort.camelCaseToCapitalized() ?? "" + + showActionSheet(image: nil, selected: selected, largeText: true, centered: true, title: "Sort by", actions: actions, sourceView: tableView) { [self] index in + guard index > -1 else { return } + + let sort = ServersSort.allCases[index] + UserDefaults.shared.set(sort.rawValue, forKey: UserDefaults.Key.serversSort) + Application.shared.serverList.sortServers() + filteredCollection = VPNServerList.sort(filteredCollection) + tableView.reloadData() + } + } // MARK: - View Lifecycle - override func viewDidLoad() { super.viewDidLoad() + tableView.keyboardDismissMode = .onDrag } } @@ -82,11 +112,11 @@ extension ServersConfigurationTableViewController { extension ServersConfigurationTableViewController: ServerConfigurationCellDelegate { - func toggle(isOn: Bool, gateway: String) { + func toggle(isOn: Bool, server: VPNServer) { if UserDefaults.standard.bool(forKey: UserDefaults.Key.fastestServerConfigured) { - StorageManager.saveServer(gateway: gateway, isFastestEnabled: isOn) + StorageManager.save(server: server, isFastestEnabled: isOn) } else { - Application.shared.serverList.saveAllServers(exceptionGateway: gateway) + Application.shared.serverList.saveAllServers(exceptionGateway: server.gateway) UserDefaults.standard.set(true, forKey: UserDefaults.Key.fastestServerConfigured) } @@ -98,3 +128,31 @@ extension ServersConfigurationTableViewController: ServerConfigurationCellDelega } } + +// MARK: - UISearchBarDelegate - + +extension ServersConfigurationTableViewController: UISearchBarDelegate { + + func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) { + let collection = Application.shared.serverList.getServers() + filteredCollection.removeAll(keepingCapacity: false) + filteredCollection = collection.filter { (server: VPNServer) -> Bool in + let location = "\(server.city) \(server.countryCode)".lowercased() + return location.contains(searchBar.text!.lowercased()) + } + filteredCollection = VPNServerList.sort(filteredCollection) + tableView.reloadData() + } + + func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) { + searchBar.showsCancelButton = true + } + + func searchBarCancelButtonClicked(_ searchBar: UISearchBar) { + searchBar.showsCancelButton = false + searchBar.text = "" + searchBar.resignFirstResponder() + tableView.reloadData() + } + +} diff --git a/IVPNClient/Scenes/ViewControllers/SettingsViewController.swift b/IVPNClient/Scenes/ViewControllers/SettingsViewController.swift index daf011d26..5e347fd5b 100644 --- a/IVPNClient/Scenes/ViewControllers/SettingsViewController.swift +++ b/IVPNClient/Scenes/ViewControllers/SettingsViewController.swift @@ -362,7 +362,7 @@ class SettingsViewController: UITableViewController { var presentMailComposer = true // App logs - var appLog: String? = nil + var appLog: String? if let file = NSData(contentsOfFile: appLogPath) { appLog = String(data: file as Data, encoding: .utf8) ?? "" } @@ -375,7 +375,7 @@ class SettingsViewController: UITableViewController { } // WireGuard tunnel logs - var wireguardLog: String? = nil + var wireguardLog: String? if let file = NSData(contentsOfFile: wireguardLogPath) { wireguardLog = String(data: file as Data, encoding: .utf8) ?? "" } diff --git a/IVPNClient/Utilities/Extensions/String+Ext.swift b/IVPNClient/Utilities/Extensions/String+Ext.swift index 08f5c3403..aebff3e8f 100644 --- a/IVPNClient/Utilities/Extensions/String+Ext.swift +++ b/IVPNClient/Utilities/Extensions/String+Ext.swift @@ -92,11 +92,9 @@ extension String { var array = self.split { $0.isNewline } array = array.reversed() - for line in array { - if line.contains(from) { - let string = line.substring(from: from, to: to) - return string?.trimmingCharacters(in: .whitespacesAndNewlines) ?? "" - } + for line in array where line.contains(from) { + let string = line.substring(from: from, to: to) + return string?.trimmingCharacters(in: .whitespacesAndNewlines) ?? "" } return "" diff --git a/IVPNClient/Utilities/Extensions/UIDevice+Ext.swift b/IVPNClient/Utilities/Extensions/UIDevice+Ext.swift index a2b8d87f4..6bb431bd0 100644 --- a/IVPNClient/Utilities/Extensions/UIDevice+Ext.swift +++ b/IVPNClient/Utilities/Extensions/UIDevice+Ext.swift @@ -58,46 +58,46 @@ extension UIDevice { // swiftlint:disable cyclomatic_complexity func mapToDevice(identifier: String) -> String { switch identifier { - case "iPod5,1": return "iPod Touch 5" - case "iPod7,1": return "iPod Touch 6" - case "iPhone3,1", "iPhone3,2", "iPhone3,3": return "iPhone 4" - case "iPhone4,1": return "iPhone 4s" - case "iPhone5,1", "iPhone5,2": return "iPhone 5" - case "iPhone5,3", "iPhone5,4": return "iPhone 5c" - case "iPhone6,1", "iPhone6,2": return "iPhone 5s" - case "iPhone7,2": return "iPhone 6" - case "iPhone7,1": return "iPhone 6 Plus" - case "iPhone8,1": return "iPhone 6s" - case "iPhone8,2": return "iPhone 6s Plus" - case "iPhone9,1", "iPhone9,3": return "iPhone 7" - case "iPhone9,2", "iPhone9,4": return "iPhone 7 Plus" - case "iPhone8,4": return "iPhone SE" - case "iPhone10,1", "iPhone10,4": return "iPhone 8" - case "iPhone10,2", "iPhone10,5": return "iPhone 8 Plus" - case "iPhone10,3", "iPhone10,6": return "iPhone X" - case "iPhone11,2": return "iPhone XS" - case "iPhone11,4", "iPhone11,6": return "iPhone XS Max" - case "iPhone11,8": return "iPhone XR" - case "iPad2,1", "iPad2,2", "iPad2,3", "iPad2,4":return "iPad 2" - case "iPad3,1", "iPad3,2", "iPad3,3": return "iPad 3" - case "iPad3,4", "iPad3,5", "iPad3,6": return "iPad 4" - case "iPad4,1", "iPad4,2", "iPad4,3": return "iPad Air" - case "iPad5,3", "iPad5,4": return "iPad Air 2" - case "iPad6,11", "iPad6,12": return "iPad 5" - case "iPad7,5", "iPad7,6": return "iPad 6" - case "iPad2,5", "iPad2,6", "iPad2,7": return "iPad Mini" - case "iPad4,4", "iPad4,5", "iPad4,6": return "iPad Mini 2" - case "iPad4,7", "iPad4,8", "iPad4,9": return "iPad Mini 3" - case "iPad5,1", "iPad5,2": return "iPad Mini 4" - case "iPad6,3", "iPad6,4": return "iPad Pro 9.7 Inch" - case "iPad6,7", "iPad6,8": return "iPad Pro 12.9 Inch" - case "iPad7,1", "iPad7,2": return "iPad Pro 12.9 Inch 2. Generation" - case "iPad7,3", "iPad7,4": return "iPad Pro 10.5 Inch" - case "AppleTV5,3": return "Apple TV" - case "AppleTV6,2": return "Apple TV 4K" - case "AudioAccessory1,1": return "HomePod" - case "i386", "x86_64": return "Simulator \(mapToDevice(identifier: ProcessInfo().environment["SIMULATOR_MODEL_IDENTIFIER"] ?? "iOS"))" - default: return identifier + case "iPod5,1": return "iPod Touch 5" + case "iPod7,1": return "iPod Touch 6" + case "iPhone3,1", "iPhone3,2", "iPhone3,3": return "iPhone 4" + case "iPhone4,1": return "iPhone 4s" + case "iPhone5,1", "iPhone5,2": return "iPhone 5" + case "iPhone5,3", "iPhone5,4": return "iPhone 5c" + case "iPhone6,1", "iPhone6,2": return "iPhone 5s" + case "iPhone7,2": return "iPhone 6" + case "iPhone7,1": return "iPhone 6 Plus" + case "iPhone8,1": return "iPhone 6s" + case "iPhone8,2": return "iPhone 6s Plus" + case "iPhone9,1", "iPhone9,3": return "iPhone 7" + case "iPhone9,2", "iPhone9,4": return "iPhone 7 Plus" + case "iPhone8,4": return "iPhone SE" + case "iPhone10,1", "iPhone10,4": return "iPhone 8" + case "iPhone10,2", "iPhone10,5": return "iPhone 8 Plus" + case "iPhone10,3", "iPhone10,6": return "iPhone X" + case "iPhone11,2": return "iPhone XS" + case "iPhone11,4", "iPhone11,6": return "iPhone XS Max" + case "iPhone11,8": return "iPhone XR" + case "iPad2,1", "iPad2,2", "iPad2,3", "iPad2,4": return "iPad 2" + case "iPad3,1", "iPad3,2", "iPad3,3": return "iPad 3" + case "iPad3,4", "iPad3,5", "iPad3,6": return "iPad 4" + case "iPad4,1", "iPad4,2", "iPad4,3": return "iPad Air" + case "iPad5,3", "iPad5,4": return "iPad Air 2" + case "iPad6,11", "iPad6,12": return "iPad 5" + case "iPad7,5", "iPad7,6": return "iPad 6" + case "iPad2,5", "iPad2,6", "iPad2,7": return "iPad Mini" + case "iPad4,4", "iPad4,5", "iPad4,6": return "iPad Mini 2" + case "iPad4,7", "iPad4,8", "iPad4,9": return "iPad Mini 3" + case "iPad5,1", "iPad5,2": return "iPad Mini 4" + case "iPad6,3", "iPad6,4": return "iPad Pro 9.7 Inch" + case "iPad6,7", "iPad6,8": return "iPad Pro 12.9 Inch" + case "iPad7,1", "iPad7,2": return "iPad Pro 12.9 Inch 2. Generation" + case "iPad7,3", "iPad7,4": return "iPad Pro 10.5 Inch" + case "AppleTV5,3": return "Apple TV" + case "AppleTV6,2": return "Apple TV 4K" + case "AudioAccessory1,1": return "HomePod" + case "i386", "x86_64": return "Simulator \(mapToDevice(identifier: ProcessInfo().environment["SIMULATOR_MODEL_IDENTIFIER"] ?? "iOS"))" + default: return identifier } } // swiftlint:enable cyclomatic_complexity diff --git a/wireguard-tunnel-provider/PacketTunnelProvider.swift b/wireguard-tunnel-provider/PacketTunnelProvider.swift index c61039d14..3bdf0d12f 100644 --- a/wireguard-tunnel-provider/PacketTunnelProvider.swift +++ b/wireguard-tunnel-provider/PacketTunnelProvider.swift @@ -107,7 +107,6 @@ class PacketTunnelProvider: NEPacketTunnelProvider { wg_log(.info, message: "Starting tunnel") wg_log(.info, message: "Public key: \(KeyChain.wgPublicKey ?? "")") - wg_log(.info, message: "Addresses: \(addresses)") setTunnelNetworkSettings(tunnelSettings) { error in if error != nil {