mirror of https://github.com/oxen-io/session-ios
mirror of https://github.com/oxen-io/session-ios
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
127 lines
4.0 KiB
127 lines
4.0 KiB
// |
|
// Copyright (c) 2018 Open Whisper Systems. All rights reserved. |
|
// |
|
|
|
import Foundation |
|
import os |
|
|
|
@objc |
|
public class OutageDetection: NSObject { |
|
@objc(sharedManager) |
|
public static let shared = OutageDetection() |
|
|
|
@objc public static let outageStateDidChange = Notification.Name("OutageStateDidChange") |
|
|
|
// These properties should only be accessed on the main thread. |
|
@objc |
|
public var hasOutage = false { |
|
didSet { |
|
AssertIsOnMainThread() |
|
|
|
if hasOutage != oldValue { |
|
Logger.info("hasOutage: \(hasOutage).") |
|
|
|
NotificationCenter.default.postNotificationNameAsync(OutageDetection.outageStateDidChange, object: nil) |
|
} |
|
} |
|
} |
|
private var shouldCheckForOutage = false { |
|
didSet { |
|
AssertIsOnMainThread() |
|
ensureCheckTimer() |
|
} |
|
} |
|
|
|
// We only show the outage warning when we're certain there's an outage. |
|
// DNS lookup failures, etc. are not considered an outage. |
|
private func checkForOutageSync() -> Bool { |
|
let host = CFHostCreateWithName(nil, "uptime.signal.org" as CFString).takeRetainedValue() |
|
CFHostStartInfoResolution(host, .addresses, nil) |
|
var success: DarwinBoolean = false |
|
guard let addresses = CFHostGetAddressing(host, &success)?.takeUnretainedValue() as NSArray? else { |
|
Logger.error("CFHostGetAddressing failed: no addresses.") |
|
return false |
|
} |
|
guard success.boolValue else { |
|
Logger.error("CFHostGetAddressing failed.") |
|
return false |
|
} |
|
var isOutageDetected = false |
|
for case let address as NSData in addresses { |
|
var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST)) |
|
if getnameinfo(address.bytes.assumingMemoryBound(to: sockaddr.self), socklen_t(address.length), |
|
&hostname, socklen_t(hostname.count), nil, 0, NI_NUMERICHOST) == 0 { |
|
let addressString = String(cString: hostname) |
|
let kHealthyAddress = "127.0.0.1" |
|
let kOutageAddress = "127.0.0.2" |
|
if addressString == kHealthyAddress { |
|
// Do nothing. |
|
} else if addressString == kOutageAddress { |
|
isOutageDetected = true |
|
} else { |
|
owsFailDebug("unexpected address: \(addressString)") |
|
} |
|
} |
|
} |
|
return isOutageDetected |
|
} |
|
|
|
private func checkForOutageAsync() { |
|
Logger.info("") |
|
|
|
DispatchQueue.global().async { |
|
let isOutageDetected = self.checkForOutageSync() |
|
DispatchQueue.main.async { |
|
self.hasOutage = isOutageDetected |
|
} |
|
} |
|
} |
|
|
|
private var checkTimer: Timer? |
|
private func ensureCheckTimer() { |
|
// Only monitor for outages in the main app. |
|
guard CurrentAppContext().isMainApp else { |
|
return |
|
} |
|
|
|
if shouldCheckForOutage { |
|
if checkTimer != nil { |
|
// Already has timer. |
|
return |
|
} |
|
|
|
// The TTL of the DNS record is 60 seconds. |
|
checkTimer = WeakTimer.scheduledTimer(timeInterval: 60, target: self, userInfo: nil, repeats: true) { [weak self] _ in |
|
AssertIsOnMainThread() |
|
|
|
guard CurrentAppContext().isMainAppAndActive else { |
|
return |
|
} |
|
|
|
guard let strongSelf = self else { |
|
return |
|
} |
|
|
|
strongSelf.checkForOutageAsync() |
|
} |
|
} else { |
|
checkTimer?.invalidate() |
|
checkTimer = nil |
|
} |
|
} |
|
|
|
@objc |
|
public func reportConnectionSuccess() { |
|
DispatchMainThreadSafe { |
|
self.shouldCheckForOutage = false |
|
self.hasOutage = false |
|
} |
|
} |
|
|
|
@objc |
|
public func reportConnectionFailure() { |
|
DispatchMainThreadSafe { |
|
self.shouldCheckForOutage = true |
|
} |
|
} |
|
}
|
|
|