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
		
	
| 
											5 years ago
										 | // | ||
|  | //  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 { | ||
| 
											5 years ago
										 |             AssertIsOnMainThread() | ||
|  |             ensureCheckTimer() | ||
| 
											5 years ago
										 |         } | ||
|  |     } | ||
|  | 
 | ||
|  |     // 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 | ||
|  |         } | ||
|  |     } | ||
|  | } |