mirror of https://github.com/oxen-io/session-ios
				
				
				
			
			You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			128 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			Swift
		
	
			
		
		
	
	
			128 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			Swift
		
	
//
 | 
						|
//  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
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 |