session-ios/Session/Utilities/IP2Country.swift

71 lines
2.9 KiB
Swift
Raw Normal View History

2020-06-02 04:02:54 +02:00
final class IP2Country {
2020-08-03 01:54:05 +02:00
var countryNamesCache: [String:String] = [:]
2020-06-02 04:02:54 +02:00
2020-09-16 02:34:02 +02:00
private lazy var ipv4Table = try! CSV(name: "GeoLite2-Country-Blocks-IPv4", extension: "csv", bundle: .main, delimiter: ",", encoding: .utf8, loadColumns: true)!
private lazy var countryNamesTable = try! CSV(name: "GeoLite2-Country-Locations-English", extension: "csv", bundle: .main, delimiter: ",", encoding: .utf8, loadColumns: true)!
2020-08-03 01:54:05 +02:00
private static let workQueue = DispatchQueue(label: "IP2Country.workQueue", qos: .utility) // It's important that this is a serial queue
2020-06-02 04:02:54 +02:00
2020-06-03 05:28:09 +02:00
static var isInitialized = false
2020-06-02 04:02:54 +02:00
// MARK: Lifecycle
static let shared = IP2Country()
private init() {
2020-06-03 05:28:09 +02:00
NotificationCenter.default.addObserver(self, selector: #selector(populateCacheIfNeededAsync), name: .pathsBuilt, object: nil)
2020-06-02 04:02:54 +02:00
}
deinit {
NotificationCenter.default.removeObserver(self)
}
// MARK: Implementation
private func cacheCountry(for ip: String) -> String {
2020-06-02 04:02:54 +02:00
var truncatedIP = ip
func getCountryInternal() -> String {
2020-09-16 02:34:02 +02:00
if let country = countryNamesCache[ip] { return country }
if let ipv4TableIndex = ipv4Table.namedColumns["network"]!.firstIndex(where: { $0.starts(with: truncatedIP) }) {
let countryID = ipv4Table.namedColumns["registered_country_geoname_id"]![ipv4TableIndex]
if let countryNamesTableIndex = countryNamesTable.namedColumns["geoname_id"]!.firstIndex(of: countryID) {
let country = countryNamesTable.namedColumns["country_name"]![countryNamesTableIndex]
countryNamesCache[ip] = country
return country
}
}
if truncatedIP.contains(".") && !truncatedIP.hasSuffix(".") { // The fuzziest we want to go is xxx.x
truncatedIP.removeLast()
if truncatedIP.hasSuffix(".") { truncatedIP.removeLast() }
return getCountryInternal()
} else {
return "Unknown Country"
}
2020-06-02 04:02:54 +02:00
}
return getCountryInternal()
}
2020-06-03 05:28:09 +02:00
@objc func populateCacheIfNeededAsync() {
2020-08-03 01:54:05 +02:00
IP2Country.workQueue.async {
2020-06-03 05:28:09 +02:00
let _ = self.populateCacheIfNeeded()
}
}
func populateCacheIfNeeded() -> Bool {
if OnionRequestAPI.paths.isEmpty {
2020-11-19 05:24:09 +01:00
OnionRequestAPI.paths = Storage.shared.getOnionRequestPaths()
2020-06-02 04:02:54 +02:00
}
2020-08-20 01:22:23 +02:00
let paths = OnionRequestAPI.paths
guard !paths.isEmpty else { return false }
2020-08-20 01:22:23 +02:00
let pathToDisplay = paths.first!
2020-06-03 05:28:09 +02:00
pathToDisplay.forEach { snode in
let _ = self.cacheCountry(for: snode.ip) // Preload if needed
}
DispatchQueue.main.async {
IP2Country.isInitialized = true
NotificationCenter.default.post(name: .onionRequestPathCountriesLoaded, object: nil)
}
print("[Loki] Finished preloading onion request path countries.")
return true
2020-06-02 04:02:54 +02:00
}
}