Censorship circumvention in Egypt and UAE

* domain fronting
* non-websocket message fetching

// FREEBIE
This commit is contained in:
Michael Kirk 2016-12-20 16:44:11 -06:00
parent bcd371b96c
commit ddba843d44
7 changed files with 250 additions and 11 deletions

View File

@ -4,7 +4,7 @@ source 'https://github.com/CocoaPods/Specs.git'
target 'Signal' do
pod 'SocketRocket', :git => 'https://github.com/facebook/SocketRocket.git'
pod 'AxolotlKit', git: 'https://github.com/WhisperSystems/SignalProtocolKit.git'
pod 'SignalServiceKit', git: 'https://github.com/WhisperSystems/SignalServiceKit.git', branch: 'mkirk/check-for-keychain-errors'
pod 'SignalServiceKit', git: 'https://github.com/WhisperSystems/SignalServiceKit.git', branch: 'mkirk/censorship-circumvention'
#pod 'SignalServiceKit', path: '../SignalServiceKit'
pod 'OpenSSL'
pod 'PastelogKit', '~> 1.3'

View File

@ -43,7 +43,7 @@ PODS:
- Reachability (3.2)
- SAMKeychain (1.5.2)
- SCWaveformView (1.0.0)
- SignalServiceKit (0.8.1):
- SignalServiceKit (0.9.0):
- '25519'
- AFNetworking
- AxolotlKit
@ -121,7 +121,7 @@ DEPENDENCIES:
- OpenSSL
- PastelogKit (~> 1.3)
- SCWaveformView (~> 1.0)
- SignalServiceKit (from `https://github.com/WhisperSystems/SignalServiceKit.git`, branch `mkirk/check-for-keychain-errors`)
- SignalServiceKit (from `https://github.com/WhisperSystems/SignalServiceKit.git`, branch `mkirk/censorship-circumvention`)
- SocketRocket (from `https://github.com/facebook/SocketRocket.git`)
- ZXingObjC
@ -129,7 +129,7 @@ EXTERNAL SOURCES:
AxolotlKit:
:git: https://github.com/WhisperSystems/SignalProtocolKit.git
SignalServiceKit:
:branch: mkirk/check-for-keychain-errors
:branch: mkirk/censorship-circumvention
:git: https://github.com/WhisperSystems/SignalServiceKit.git
SocketRocket:
:git: https://github.com/facebook/SocketRocket.git
@ -139,7 +139,7 @@ CHECKOUT OPTIONS:
:commit: 714f5ebe199ecc999b33c6f97a4bb57e2db90e75
:git: https://github.com/WhisperSystems/SignalProtocolKit.git
SignalServiceKit:
:commit: 5ccbd4ca6dcb97032af3641b6689866e2265510f
:commit: 78515377b14f1055c4a87548a7899650b753ce52
:git: https://github.com/WhisperSystems/SignalServiceKit.git
SocketRocket:
:commit: 41b57bb2fc292a814f758441a05243eb38457027
@ -162,7 +162,7 @@ SPEC CHECKSUMS:
Reachability: 33e18b67625424e47b6cde6d202dce689ad7af96
SAMKeychain: 1865333198217411f35327e8da61b43de79b635b
SCWaveformView: 52a96750255d817e300565a80c81fb643e233e07
SignalServiceKit: 60f92ec89fbbf3196bd786b88f065c0214db9ca8
SignalServiceKit: 59a79a51b89b963ba94db30cc99ed5212da0bb9f
SocketRocket: dbb1554b8fc288ef8ef370d6285aeca7361be31e
SQLCipher: 4c768761421736a247ed6cf412d9045615d53dff
TwistedOakCollapsingFutures: f359b90f203e9ab13dfb92c9ff41842a7fe1cd0c
@ -170,6 +170,6 @@ SPEC CHECKSUMS:
YapDatabase: b1e43555a34a5298e23a045be96817a5ef0da58f
ZXingObjC: bf15b3814f7a105b6d99f47da2333c93a063650a
PODFILE CHECKSUM: 52f34af46d045b2c4c012e12a04d06b69f0dae56
PODFILE CHECKSUM: e09325f010ba0ef1fd0bfa07f665e7be73c43ee0
COCOAPODS: 1.0.1

View File

@ -25,6 +25,8 @@
452D1EE81DCA90D100A57EC4 /* MesssagesBubblesSizeCalculatorTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 452D1EE71DCA90D100A57EC4 /* MesssagesBubblesSizeCalculatorTest.swift */; };
452E3C8E1D935C77002A45B0 /* OWSConversationSettingsTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 452E3C8D1D935C77002A45B0 /* OWSConversationSettingsTableViewController.m */; };
452E3C8F1D935C77002A45B0 /* OWSConversationSettingsTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 452E3C8D1D935C77002A45B0 /* OWSConversationSettingsTableViewController.m */; };
452ECA4D1E087E7200E2F016 /* MessageFetcherJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = 452ECA4C1E087E7200E2F016 /* MessageFetcherJob.swift */; };
452ECA4E1E087E7200E2F016 /* MessageFetcherJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = 452ECA4C1E087E7200E2F016 /* MessageFetcherJob.swift */; };
4531C9C41DD8E6D800F08304 /* JSQMessagesCollectionViewCell+OWS.m in Sources */ = {isa = PBXBuildFile; fileRef = 4531C9C31DD8E6D800F08304 /* JSQMessagesCollectionViewCell+OWS.m */; };
453D28B71D32BA5F00D523F0 /* OWSDisplayedMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = 453D28B61D32BA5F00D523F0 /* OWSDisplayedMessage.m */; };
453D28BA1D332DB100D523F0 /* OWSMessagesBubblesSizeCalculator.m in Sources */ = {isa = PBXBuildFile; fileRef = 453D28B91D332DB100D523F0 /* OWSMessagesBubblesSizeCalculator.m */; };
@ -566,6 +568,7 @@
452D1EE71DCA90D100A57EC4 /* MesssagesBubblesSizeCalculatorTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MesssagesBubblesSizeCalculatorTest.swift; path = Models/MesssagesBubblesSizeCalculatorTest.swift; sourceTree = "<group>"; };
452E3C8C1D935C77002A45B0 /* OWSConversationSettingsTableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSConversationSettingsTableViewController.h; sourceTree = "<group>"; };
452E3C8D1D935C77002A45B0 /* OWSConversationSettingsTableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = OWSConversationSettingsTableViewController.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
452ECA4C1E087E7200E2F016 /* MessageFetcherJob.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MessageFetcherJob.swift; path = Jobs/MessageFetcherJob.swift; sourceTree = "<group>"; };
4531C9C21DD8E6D800F08304 /* JSQMessagesCollectionViewCell+OWS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "JSQMessagesCollectionViewCell+OWS.h"; sourceTree = "<group>"; };
4531C9C31DD8E6D800F08304 /* JSQMessagesCollectionViewCell+OWS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "JSQMessagesCollectionViewCell+OWS.m"; sourceTree = "<group>"; };
453CC0361D08E1A60040EBA3 /* sn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sn; path = translations/sn.lproj/Localizable.strings; sourceTree = "<group>"; };
@ -1295,6 +1298,7 @@
children = (
451DE9FC1DC1A28200810E42 /* SyncPushTokensJob.swift */,
45D231761DC7E8F10034FA89 /* SessionResetJob.swift */,
452ECA4C1E087E7200E2F016 /* MessageFetcherJob.swift */,
);
name = Jobs;
sourceTree = "<group>";
@ -2923,6 +2927,7 @@
45C681C61D305C9E0050903A /* OWSDisplayedMessageCollectionViewCell.m in Sources */,
E197B61E18BBEC6D00F073E5 /* AudioRouter.m in Sources */,
E197B60D18BBEC1A00F073E5 /* AudioSocket.m in Sources */,
452ECA4D1E087E7200E2F016 /* MessageFetcherJob.swift in Sources */,
A5D0699B1A50E9CB004CB540 /* ShowGroupMembersViewController.m in Sources */,
45855F371D9498A40084F340 /* OWSContactAvatarBuilder.m in Sources */,
FC31962D1A06A2190094C78E /* FingerprintViewController.m in Sources */,
@ -3062,6 +3067,7 @@
B660F7091C29988E00687D6E /* DesiredBufferDepthController.m in Sources */,
B660F70A1C29988E00687D6E /* DropoutTracker.m in Sources */,
B660F70B1C29988E00687D6E /* JitterQueue.m in Sources */,
452ECA4E1E087E7200E2F016 /* MessageFetcherJob.swift in Sources */,
B660F70C1C29988E00687D6E /* StretchFactorController.m in Sources */,
B660F70D1C29988E00687D6E /* AnonymousAudioCallbackHandler.m in Sources */,
B660F70E1C29988E00687D6E /* RemoteIOAudio.m in Sources */,

View File

@ -38,7 +38,7 @@
</dict>
</array>
<key>CFBundleVersion</key>
<string>2.6.9.1</string>
<string>2.6.9.4</string>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>LOGS_EMAIL</key>

View File

@ -0,0 +1,197 @@
// Created by Michael Kirk on 12/19/16.
// Copyright © 2016 Open Whisper Systems. All rights reserved.
import Foundation
import PromiseKit
@objc(OWSMessageFetcherJob)
class MessageFetcherJob: NSObject {
let TAG = "[MessageFetcherJob]"
var timer: Timer?
// MARK: injected dependencies
let networkManager: TSNetworkManager
let messagesManager: TSMessagesManager
let messageSender: MessageSender
let signalService: OWSSignalService
// var fallbackTransport = false
// ENABLED FOR DEBUG. DO NOT COMMIT!
var fallbackTransport = true
var runPromises = [Double: Promise<Void>]()
init(messagesManager: TSMessagesManager, messageSender: MessageSender, networkManager: TSNetworkManager, signalService: OWSSignalService) {
self.messagesManager = messagesManager
self.networkManager = networkManager
self.messageSender = messageSender
self.signalService = signalService
}
func runAsync() {
Logger.debug("\(TAG) \(#function)")
guard signalService.isCensored else {
Logger.debug("\(self.TAG) delegating message fetching to SocketManager since we're using normal transport.")
TSSocketManager.becomeActive(fromBackgroundExpectMessage: true)
return
}
Logger.info("\(TAG) using fallback message fetching.")
let promiseId = NSDate().timeIntervalSince1970
Logger.debug("\(self.TAG) starting promise: \(promiseId)")
let runPromise = self.fetchUndeliveredMessages().then { (envelopes: [OWSSignalServiceProtosEnvelope], more: Bool) -> () in
for envelope in envelopes {
Logger.info("\(self.TAG) received envelope.")
self.messagesManager.handleReceivedEnvelope(envelope);
self.acknowledgeDelivery(envelope: envelope)
}
if more {
Logger.info("\(self.TAG) more messages, so recursing.")
// recurse
self.runAsync()
}
}.always {
Logger.debug("\(self.TAG) cleaning up promise: \(promiseId)")
self.runPromises[promiseId] = nil
}
// maintain reference to make sure it's not de-alloced prematurely.
runPromises[promiseId] = runPromise
}
// use in DEBUG or wherever you can't receive push notifications to poll for messages.
// Do not use in production.
func startRunLoop(timeInterval: Double) {
Logger.error("\(TAG) Starting message fetch polling. This should not be used in production.");
timer = Timer.scheduledTimer(timeInterval: timeInterval, target: self, selector: #selector(runAsync), userInfo: nil, repeats: true)
}
func stopRunLoop() {
timer?.invalidate()
timer = nil
}
func parseMessagesResponse(responseObject: Any?) -> (envelopes: [OWSSignalServiceProtosEnvelope], more: Bool)? {
guard let responseObject = responseObject else {
Logger.error("\(self.TAG) response object was surpringly nil")
return nil
}
guard let responseDict = responseObject as? [String: Any] else {
Logger.error("\(self.TAG) response object was not a dictionary")
return nil
}
guard let messageDicts = responseDict["messages"] as? [[String: Any]] else {
Logger.error("\(self.TAG) messages object was not a list of dictionaries")
return nil
}
let moreMessages = { () -> Bool in
if let responseMore = responseDict["more"] as? Bool {
return responseMore
} else {
Logger.warn("\(self.TAG) more object was not a bool. Assuming no more")
return false
}
}()
let envelopes = messageDicts.map { buildEnvelope(messageDict: $0) }.filter { $0 != nil }.map { $0! }
return (
envelopes: envelopes,
more: moreMessages
)
}
func buildEnvelope(messageDict: [String: Any]) -> OWSSignalServiceProtosEnvelope? {
let builder = OWSSignalServiceProtosEnvelopeBuilder()
guard let typeInt = messageDict["type"] as? Int32 else {
Logger.error("\(TAG) message body didn't have type")
return nil
}
guard let type = OWSSignalServiceProtosEnvelopeType(rawValue:typeInt) else {
Logger.error("\(TAG) message body type was invalid")
return nil
}
builder.setType(type)
if let relay = messageDict["relay"] as? String {
builder.setRelay(relay)
}
guard let timestamp = messageDict["timestamp"] as? UInt64 else {
Logger.error("\(TAG) message body didn't have timestamp")
return nil
}
builder.setTimestamp(timestamp)
guard let source = messageDict["source"] as? String else {
Logger.error("\(TAG) message body didn't have source")
return nil
}
builder.setSource(source)
guard let sourceDevice = messageDict["sourceDevice"] as? UInt32 else {
Logger.error("\(TAG) message body didn't have sourceDevice")
return nil
}
builder.setSourceDevice(sourceDevice)
if let encodedLegacyMessage = messageDict["message"] as? String {
Logger.debug("\(TAG) message body had legacyMessage")
if let legacyMessage = Data(base64Encoded: encodedLegacyMessage) {
builder.setLegacyMessage(legacyMessage)
}
}
if let encodedContent = messageDict["content"] as? String {
Logger.debug("\(TAG) message body had content")
if let content = Data(base64Encoded: encodedContent) {
builder.setContent(content)
}
}
return builder.build()
}
func fetchUndeliveredMessages() -> Promise<(envelopes: [OWSSignalServiceProtosEnvelope], more: Bool)> {
return Promise { fulfill, reject in
let messagesRequest = OWSGetMessagesRequest()
self.networkManager.makeRequest(
messagesRequest,
success: { (task: URLSessionDataTask?, responseObject: Any?) -> () in
guard let (envelopes, more) = self.parseMessagesResponse(responseObject: responseObject) else {
Logger.error("\(self.TAG) response object had unexpected content")
return reject(OWSErrorMakeUnableToProcessServerResponseError())
}
fulfill((envelopes: envelopes, more: more))
},
failure: { (task: URLSessionDataTask?, error: Error?) in
guard let error = error else {
Logger.error("\(self.TAG) error was surpringly nil. sheesh rough day.")
return reject(OWSErrorMakeUnableToProcessServerResponseError())
}
reject(error)
})
}
}
func acknowledgeDelivery(envelope: OWSSignalServiceProtosEnvelope) {
let request = OWSAcknowledgeMessageDeliveryRequest(source: envelope.source, timestamp: envelope.timestamp)
self.networkManager.makeRequest(request,
success: { (task: URLSessionDataTask?, responseObject: Any?) -> () in
Logger.debug("\(self.TAG) acknowledged delivery for message at timestamp: \(envelope.timestamp)")
},
failure: { (task: URLSessionDataTask?, error: Error?) in
Logger.debug("\(self.TAG) acknowledging delivery for message at timestamp: \(envelope.timestamp) failed with error: \(error)")
})
}
}

View File

@ -3,7 +3,9 @@
//
#import <Foundation/Foundation.h>
#import "Cryptography.h"
#import "Environment.h"
#import "NSData+Base64.h"
#import "OWSContactAvatarBuilder.h"
#import "OWSContactsManager.h"
#import "OWSLogger.h"
@ -11,17 +13,23 @@
#import "PropertyListPreferences.h"
#import "PushManager.h"
#import "RPAccountManager.h"
#import "TSSocketManager.h"
#import "UIFont+OWS.h"
#import "UIUtil.h"
#import <SignalServiceKit/Contact.h>
#import <SignalServiceKit/NSDate+millisecondTimeStamp.h>
#import <SignalServiceKit/OWSAcknowledgeMessageDeliveryRequest.h>
#import <SignalServiceKit/OWSEndSessionMessage.h>
#import <SignalServiceKit/OWSError.h>
#import <SignalServiceKit/OWSGetMessagesRequest.h>
#import <SignalServiceKit/OWSMessageSender.h>
#import <SignalServiceKit/OWSSignalService.h>
#import <SignalServiceKit/TSAccountManager.h>
#import <SignalServiceKit/TSContactThread.h>
#import <SignalServiceKit/TSErrorMessage.h>
#import <SignalServiceKit/TSInfoMessage.h>
#import <SignalServiceKit/TSMessagesManager.h>
#import <SignalServiceKit/TSNetworkManager.h>
#import <SignalServiceKit/TSStorageManager+IdentityKeyStore.h>
#import <SignalServiceKit/TSStorageManager+SessionStore.h>
#import <SignalServiceKit/TSStorageManager+keyingMaterial.h>

View File

@ -15,9 +15,13 @@
#import "OWSContactsManager.h"
#import "PropertyListPreferences.h"
#import "RPServerRequestsManager.h"
#import "Signal-Swift.h"
#import "TSMessagesManager.h"
#import "TSAccountManager.h"
#import "TSOutgoingMessage.h"
#import "TSSocketManager.h"
#import <SignalServiceKit/OWSMessageSender.h>
#import <SignalServiceKit/OWSSignalService.h>
#define pushManagerDomain @"org.whispersystems.pushmanager"
@ -31,6 +35,7 @@
@property (nonatomic) UIBackgroundTaskIdentifier callBackgroundTask;
@property (nonatomic, readonly) OWSContactsManager *contactsManager;
@property (nonatomic, readonly) OWSMessageSender *messageSender;
@property (nonatomic, readonly) OWSMessageFetcherJob *messageFetcherJob;
@end
@ -72,6 +77,18 @@
contactsManager:contactsManager
contactsUpdater:contactsUpdater];
TSMessagesManager *messagesManager = [[TSMessagesManager alloc] initWithNetworkManager:networkManager
storageManager:storageManager
contactsManager:contactsManager
contactsUpdater:contactsUpdater
messageSender:_messageSender];
OWSSignalService *signalService = [OWSSignalService new];
_messageFetcherJob = [[OWSMessageFetcherJob alloc] initWithMessagesManager:messagesManager
messageSender:_messageSender
networkManager:networkManager
signalService:signalService];
_missingPermissionsAlertView = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"ACTION_REQUIRED_TITLE", @"")
message:NSLocalizedString(@"PUSH_SETTINGS_MESSAGE", @"")
delegate:nil
@ -86,6 +103,8 @@
#pragma mark Manage Incoming Push
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
DDLogInfo(@"received: %s", __PRETTY_FUNCTION__);
if ([self isRedPhonePush:userInfo]) {
ResponderSessionDescriptor *call;
if (![self.notificationTracker shouldProcessNotification:userInfo]) {
@ -139,9 +158,7 @@
}
}
} else {
if (![self applicationIsActive]) {
[TSSocketManager becomeActiveFromBackgroundExpectMessage:YES];
}
[self.messageFetcherJob runAsync];
}
}
@ -164,6 +181,8 @@
- (void)application:(UIApplication *)application
didReceiveRemoteNotification:(NSDictionary *)userInfo
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
DDLogInfo(@"received: %s", __PRETTY_FUNCTION__);
if ([self isRedPhonePush:userInfo]) {
[self application:application didReceiveRemoteNotification:userInfo];
}
@ -174,6 +193,8 @@
}
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification {
DDLogInfo(@"received: %s", __PRETTY_FUNCTION__);
NSString *threadId = notification.userInfo[Signal_Thread_UserInfo_Key];
if (threadId && [TSThread fetchObjectWithUniqueID:threadId]) {
[Environment messageThreadId:threadId];
@ -184,6 +205,8 @@
handleActionWithIdentifier:(NSString *)identifier
forLocalNotification:(UILocalNotification *)notification
completionHandler:(void (^)())completionHandler {
DDLogInfo(@"received: %s", __PRETTY_FUNCTION__);
[self application:application
handleActionWithIdentifier:identifier
forLocalNotification:notification
@ -197,6 +220,8 @@
withResponseInfo:(NSDictionary *)responseInfo
completionHandler:(void (^)())completionHandler
{
DDLogInfo(@"received: %s", __PRETTY_FUNCTION__);
if ([identifier isEqualToString:Signal_Message_Reply_Identifier]) {
NSString *threadId = notification.userInfo[Signal_Thread_UserInfo_Key];
@ -285,6 +310,9 @@
- (void)pushRegistry:(PKPushRegistry *)registry
didReceiveIncomingPushWithPayload:(PKPushPayload *)payload
forType:(NSString *)type {
DDLogInfo(@"received: %s", __PRETTY_FUNCTION__);
[self application:[UIApplication sharedApplication] didReceiveRemoteNotification:payload.dictionaryPayload];
}