WIP: Censorship circumvention in Egypt and UAE

* non-websocket message fetching

// FREEBIE
pull/1/head
Michael Kirk 9 years ago
parent 5ccbd4ca6d
commit f1ade83c3f

@ -0,0 +1,14 @@
// Created by Michael Kirk on 12/19/16.
// Copyright © 2016 Open Whisper Systems. All rights reserved.
#import "TSRequest.h"
NS_ASSUME_NONNULL_BEGIN
@interface OWSAcknowledgeMessageDeliveryRequest : TSRequest
- (instancetype)initWithSource:(NSString *)source timestamp:(UInt64)timestamp;
@end
NS_ASSUME_NONNULL_END

@ -0,0 +1,23 @@
// Created by Michael Kirk on 12/19/16.
// Copyright © 2016 Open Whisper Systems. All rights reserved.
#import "OWSAcknowledgeMessageDeliveryRequest.h"
NS_ASSUME_NONNULL_BEGIN
@implementation OWSAcknowledgeMessageDeliveryRequest
- (instancetype)initWithSource:(NSString *)source timestamp:(UInt64)timestamp
{
NSString *path = [NSString stringWithFormat:@"v1/messages/%@/%llu", source, timestamp];
NSURL *url = [NSURL URLWithString:path];
self = [super initWithURL:url];
self.HTTPMethod = @"DELETE";
return self;
}
@end
NS_ASSUME_NONNULL_END

@ -0,0 +1,12 @@
// Created by Michael Kirk on 12/19/16.
// Copyright © 2016 Open Whisper Systems. All rights reserved.
#import "TSRequest.h"
NS_ASSUME_NONNULL_BEGIN
@interface OWSGetMessagesRequest : TSRequest
@end
NS_ASSUME_NONNULL_END

@ -0,0 +1,18 @@
// Created by Michael Kirk on 12/19/16.
// Copyright © 2016 Open Whisper Systems. All rights reserved.
#import "OWSGetMessagesRequest.h"
NS_ASSUME_NONNULL_BEGIN
@implementation OWSGetMessagesRequest
- (instancetype)init
{
NSURL *url = [NSURL URLWithString:@"v1/messages"];
return [super initWithURL:url];
}
@end
NS_ASSUME_NONNULL_END

@ -34,6 +34,6 @@
- (void)makeRequest:(TSRequest *)request - (void)makeRequest:(TSRequest *)request
success:(void (^)(NSURLSessionDataTask *task, id responseObject))success success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure; failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure NS_SWIFT_NAME(makeRequest(_:success:failure:));
@end @end

@ -6,22 +6,19 @@
// Copyright (c) 2013 Open Whisper Systems. All rights reserved. // Copyright (c) 2013 Open Whisper Systems. All rights reserved.
// //
#import <AFNetworking/AFNetworking.h> #import "TSNetworkManager.h"
#import "OWSHTTPSecurityPolicy.h"
#import "NSURLSessionDataTask+StatusCode.h" #import "NSURLSessionDataTask+StatusCode.h"
#import "OWSSignalService.h"
#import "TSAccountManager.h" #import "TSAccountManager.h"
#import "TSNetworkManager.h"
#import "TSStorageManager+keyingMaterial.h" #import "TSStorageManager+keyingMaterial.h"
#import "TSVerifyCodeRequest.h" #import "TSVerifyCodeRequest.h"
#import <AFNetworking/AFNetworking.h>
#define TSNetworkManagerDomain @"org.whispersystems.signal.networkManager" #define TSNetworkManagerDomain @"org.whispersystems.signal.networkManager"
@interface TSNetworkManager () @interface TSNetworkManager ()
@property AFHTTPSessionManager *operationManager; @property (nonatomic, readonly, strong) OWSSignalService *signalService;
typedef void (^failureBlock)(NSURLSessionDataTask *task, NSError *error); typedef void (^failureBlock)(NSURLSessionDataTask *task, NSError *error);
@end @end
@ -34,30 +31,22 @@ typedef void (^failureBlock)(NSURLSessionDataTask *task, NSError *error);
static TSNetworkManager *sharedMyManager = nil; static TSNetworkManager *sharedMyManager = nil;
static dispatch_once_t onceToken; static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{ dispatch_once(&onceToken, ^{
sharedMyManager = [[self alloc] initWithDefaultOperationManager]; OWSSignalService *signalService =
[[OWSSignalService alloc] initWithStorageManager:[TSStorageManager sharedManager]];
sharedMyManager = [[self alloc] initWithSignalService:signalService];
}); });
return sharedMyManager; return sharedMyManager;
} }
- (instancetype)initWithDefaultOperationManager
{
NSURLSessionConfiguration *sessionConf = NSURLSessionConfiguration.ephemeralSessionConfiguration;
NSURL *baseURL = [[NSURL alloc] initWithString:textSecureServerURL];
AFHTTPSessionManager *operationManager =
[[AFHTTPSessionManager alloc] initWithBaseURL:baseURL sessionConfiguration:sessionConf];
operationManager.securityPolicy = [OWSHTTPSecurityPolicy sharedPolicy];
return [self initWithOperationManager:operationManager];
}
- (instancetype)initWithOperationManager:(AFHTTPSessionManager *)operationManager - (instancetype)initWithSignalService:(OWSSignalService *)signalService
{ {
self = [super init]; self = [super init];
if (!self) { if (!self) {
return self; return self;
} }
_operationManager = operationManager; _signalService = signalService;
return self; return self;
} }
@ -73,48 +62,52 @@ typedef void (^failureBlock)(NSURLSessionDataTask *task, NSError *error);
void (^failure)(NSURLSessionDataTask *task, NSError *error) = void (^failure)(NSURLSessionDataTask *task, NSError *error) =
[TSNetworkManager errorPrettifyingForFailureBlock:failureBlock]; [TSNetworkManager errorPrettifyingForFailureBlock:failureBlock];
self.operationManager.requestSerializer = [AFJSONRequestSerializer serializer]; // FIXME TODO these were being rebuilt each request because we're mangling the serializer's auth headers depending
self.operationManager.responseSerializer = [AFJSONResponseSerializer serializer]; // on the request type.
// But it's kind of messy considering we want to be able to change headres.
// self.sessionManager.requestSerializer = [AFJSONRequestSerializer serializer];
// self.sessionManager.responseSerializer = [AFJSONResponseSerializer serializer];
AFHTTPSessionManager *sessionManager = self.signalService.HTTPSessionManager;
// FIXME TODO And what about baseDomain when doing s3 uploading?
if ([request isKindOfClass:[TSVerifyCodeRequest class]]) { if ([request isKindOfClass:[TSVerifyCodeRequest class]]) {
// We plant the Authorization parameter ourselves, no need to double add. // We plant the Authorization parameter ourselves, no need to double add.
[self.operationManager.requestSerializer [sessionManager.requestSerializer
setAuthorizationHeaderFieldWithUsername:((TSVerifyCodeRequest *)request).numberToValidate setAuthorizationHeaderFieldWithUsername:((TSVerifyCodeRequest *)request).numberToValidate
password:[request.parameters objectForKey:@"AuthKey"]]; password:[request.parameters objectForKey:@"AuthKey"]];
[request.parameters removeObjectForKey:@"AuthKey"]; [request.parameters removeObjectForKey:@"AuthKey"];
[self.operationManager PUT:[textSecureServerURL stringByAppendingString:request.URL.absoluteString] [sessionManager PUT:request.URL.absoluteString parameters:request.parameters success:success failure:failure];
parameters:request.parameters
success:success
failure:failure];
} else { } else {
if (![request isKindOfClass:[TSRequestVerificationCodeRequest class]]) { if (![request isKindOfClass:[TSRequestVerificationCodeRequest class]]) {
[self.operationManager.requestSerializer [sessionManager.requestSerializer
setAuthorizationHeaderFieldWithUsername:[TSAccountManager localNumber] setAuthorizationHeaderFieldWithUsername:[TSAccountManager localNumber]
password:[TSStorageManager serverAuthToken]]; password:[TSStorageManager serverAuthToken]];
} }
if ([request.HTTPMethod isEqualToString:@"GET"]) { if ([request.HTTPMethod isEqualToString:@"GET"]) {
[self.operationManager GET:[textSecureServerURL stringByAppendingString:request.URL.absoluteString] [sessionManager GET:request.URL.absoluteString
parameters:request.parameters parameters:request.parameters
progress:nil progress:nil
success:success success:success
failure:failure]; failure:failure];
} else if ([request.HTTPMethod isEqualToString:@"POST"]) { } else if ([request.HTTPMethod isEqualToString:@"POST"]) {
[self.operationManager POST:[textSecureServerURL stringByAppendingString:request.URL.absoluteString] [sessionManager POST:request.URL.absoluteString
parameters:request.parameters parameters:request.parameters
progress:nil progress:nil
success:success success:success
failure:failure]; failure:failure];
} else if ([request.HTTPMethod isEqualToString:@"PUT"]) { } else if ([request.HTTPMethod isEqualToString:@"PUT"]) {
[self.operationManager PUT:[textSecureServerURL stringByAppendingString:request.URL.absoluteString] [sessionManager PUT:request.URL.absoluteString
parameters:request.parameters parameters:request.parameters
success:success success:success
failure:failure]; failure:failure];
} else if ([request.HTTPMethod isEqualToString:@"DELETE"]) { } else if ([request.HTTPMethod isEqualToString:@"DELETE"]) {
[self.operationManager DELETE:[textSecureServerURL stringByAppendingString:request.URL.absoluteString] [sessionManager DELETE:request.URL.absoluteString
parameters:request.parameters parameters:request.parameters
success:success success:success
failure:failure]; failure:failure];
} else { } else {
DDLogError(@"Trying to perform HTTP operation with unknown verb: %@", request.HTTPMethod); DDLogError(@"Trying to perform HTTP operation with unknown verb: %@", request.HTTPMethod);
} }
@ -243,6 +236,8 @@ typedef void (^failureBlock)(NSURLSessionDataTask *task, NSError *error);
return [NSError errorWithDomain:TSNetworkManagerDomain code:code userInfo:dict]; return [NSError errorWithDomain:TSNetworkManagerDomain code:code userInfo:dict];
} }
#pragma mark - Logging
+ (NSString *)tag + (NSString *)tag
{ {
return [NSString stringWithFormat:@"[%@]", self.class]; return [NSString stringWithFormat:@"[%@]", self.class];

@ -0,0 +1,16 @@
// Created by Michael Kirk on 12/20/16.
// Copyright © 2016 Open Whisper Systems. All rights reserved.
NS_ASSUME_NONNULL_BEGIN
@class TSStorageManager;
@interface OWSCensorshipConfiguration : NSObject
- (NSString *)frontingHost;
- (NSString *)reflectorHost;
- (BOOL)isCensoredPhoneNumber:(NSString *)e164PhonNumber;
@end
NS_ASSUME_NONNULL_END

@ -0,0 +1,57 @@
// Created by Michael Kirk on 12/20/16.
// Copyright © 2016 Open Whisper Systems. All rights reserved.
#import "OWSCensorshipConfiguration.h"
#import "TSStorageManager.h"
NS_ASSUME_NONNULL_BEGIN
NSString *const OWSCensorshipConfigurationFrontingHost = @"https://google.com";
NSString *const OWSCensorshipConfigurationReflectorHost = @"signal-reflector-meek.appspot.com";
@implementation OWSCensorshipConfiguration
- (NSString *)frontingHost
{
return OWSCensorshipConfigurationFrontingHost;
}
- (NSString *)reflectorHost
{
return OWSCensorshipConfigurationReflectorHost;
}
- (NSArray<NSString *> *)censoredCountryCodes
{
// Reports of censorship in:
// Egypt
// UAE
return @[@"+20",
@"+971"];
}
- (BOOL)isCensoredPhoneNumber:(NSString *)e164PhonNumber
{
for (NSString *countryCode in self.censoredCountryCodes) {
if ([e164PhonNumber hasPrefix:countryCode]) {
return YES;
}
}
return NO;
}
#pragma mark - Logging
+ (NSString *)tag
{
return [NSString stringWithFormat:@"[%@]", self.class];
}
- (NSString *)tag
{
return self.class.tag;
}
@end
NS_ASSUME_NONNULL_END

@ -0,0 +1,18 @@
// Created by Michael Kirk on 12/20/16.
// Copyright © 2016 Open Whisper Systems. All rights reserved.
NS_ASSUME_NONNULL_BEGIN
@class TSStorageManager;
@class AFHTTPSessionManager;
@interface OWSSignalService : NSObject
- (instancetype)initWithStorageManager:(TSStorageManager *)storageManager;
@property (nonatomic, readonly) BOOL isCensored;
@property (nonatomic, readonly) AFHTTPSessionManager *HTTPSessionManager;
@end
NS_ASSUME_NONNULL_END

@ -0,0 +1,129 @@
// Created by Michael Kirk on 12/20/16.
// Copyright © 2016 Open Whisper Systems. All rights reserved.
#import "OWSSignalService.h"
#import "OWSCensorshipConfiguration.h"
#import "OWSHTTPSecurityPolicy.h"
#import "TSConstants.h"
#import "TSAccountManager.h"
#import "TSStorageManager+keyingMaterial.h"
#import <AFNetworking/AFHTTPSessionManager.h>
NS_ASSUME_NONNULL_BEGIN
@interface TSAccountManager (OWSSignalService)
@property (nullable, nonatomic, readonly) NSString *phoneNumberAwaitingVerification;
@end
@interface OWSSignalService ()
@property (nonatomic, readonly, strong) TSStorageManager *storageManager;
@property (nonatomic, readonly, strong) TSAccountManager *tsAccountManager;
@property (nonatomic, readonly, strong) OWSCensorshipConfiguration *censorshipConfiguration;
@end
@implementation OWSSignalService
- (instancetype)initWithStorageManager:(TSStorageManager *)storageManager
tsAccountManager:(TSAccountManager *)tsAccountManager
{
self = [super init];
if (!self) {
return self;
}
_storageManager = storageManager;
_tsAccountManager = tsAccountManager;
_censorshipConfiguration = [OWSCensorshipConfiguration new];
return self;
}
- (BOOL)isCensored
{
NSString *localNumber = self.storageManager.localNumber;
NSString *pendingNumber = self.tsAccountManager.phoneNumberAwaitingVerification;
if (localNumber) {
if ([self.censorshipConfiguration isCensoredPhoneNumber:localNumber]) {
DDLogInfo(@"%@ assumed censorship for localNumber: %@", self.tag, localNumber);
return YES;
} else {
DDLogInfo(@"%@ assumed no censorship for localNumber: %@", self.tag, localNumber);
return NO;
}
} else if (pendingNumber) {
if ([self.censorshipConfiguration isCensoredPhoneNumber:pendingNumber]) {
DDLogInfo(@"%@ assumed censorship for pending Number: %@", self.tag, pendingNumber);
return YES;
} else {
DDLogInfo(@"%@ assumed no censorship for pending Number: %@", self.tag, pendingNumber);
return NO;
}
} else {
DDLogError(@"no known phone number to check for censorship.");
return NO;
}
}
- (AFHTTPSessionManager *)HTTPSessionManager
{
if (self.isCensored) {
return self.reflectorHTTPSessionManager;
} else {
return self.defaultHTTPSessionManager;
}
}
- (AFHTTPSessionManager *)defaultHTTPSessionManager
{
NSURL *baseURL = [[NSURL alloc] initWithString:textSecureServerURL];
NSURLSessionConfiguration *sessionConf = NSURLSessionConfiguration.ephemeralSessionConfiguration;
AFHTTPSessionManager *sessionManager =
[[AFHTTPSessionManager alloc] initWithBaseURL:baseURL sessionConfiguration:sessionConf];
sessionManager.securityPolicy = [OWSHTTPSecurityPolicy sharedPolicy];
sessionManager.requestSerializer = [AFJSONRequestSerializer serializer];
sessionManager.responseSerializer = [AFJSONResponseSerializer serializer];
return sessionManager;
}
- (AFHTTPSessionManager *)reflectorHTTPSessionManager
{
// Target fronting domain
NSURL *baseURL = [[NSURL alloc] initWithString:self.censorshipConfiguration.frontingHost];
NSURLSessionConfiguration *sessionConf = NSURLSessionConfiguration.ephemeralSessionConfiguration;
AFHTTPSessionManager *sessionManager =
[[AFHTTPSessionManager alloc] initWithBaseURL:baseURL sessionConfiguration:sessionConf];
// FIXME TODO can we still pin SSL while fronting?
// sessionManager.securityPolicy = [OWSHTTPSecurityPolicy sharedPolicy];
sessionManager.requestSerializer = [AFJSONRequestSerializer serializer];
[sessionManager.requestSerializer setValue:self.censorshipConfiguration.reflectorHost forHTTPHeaderField:@"Host"];
sessionManager.responseSerializer = [AFJSONResponseSerializer serializer];
return sessionManager;
}
#pragma mark - Logging
+ (NSString *)tag
{
return [NSString stringWithFormat:@"[%@]", self.class];
}
- (NSString *)tag
{
return self.class.tag;
}
@end
NS_ASSUME_NONNULL_END

@ -8,15 +8,15 @@
#import "SubProtocol.pb.h" #import "SubProtocol.pb.h"
#import "Cryptography.h"
#import "OWSSignalService.h"
#import "OWSWebsocketSecurityPolicy.h"
#import "TSAccountManager.h" #import "TSAccountManager.h"
#import "TSConstants.h" #import "TSConstants.h"
#import "TSMessagesManager.h" #import "TSMessagesManager.h"
#import "TSSocketManager.h" #import "TSSocketManager.h"
#import "TSStorageManager+keyingMaterial.h" #import "TSStorageManager+keyingMaterial.h"
#import "OWSWebsocketSecurityPolicy.h"
#import "Cryptography.h"
#define kWebSocketHeartBeat 30 #define kWebSocketHeartBeat 30
#define kWebSocketReconnectTry 5 #define kWebSocketReconnectTry 5
#define kBackgroundConnectTimer 25 #define kBackgroundConnectTimer 25
@ -27,6 +27,9 @@ NSString *const SocketClosedNotification = @"SocketClosedNotification";
NSString *const SocketConnectingNotification = @"SocketConnectingNotification"; NSString *const SocketConnectingNotification = @"SocketConnectingNotification";
@interface TSSocketManager () @interface TSSocketManager ()
@property (nonatomic, readonly, strong) OWSSignalService *signalService;
@property (nonatomic, retain) NSTimer *pingTimer; @property (nonatomic, retain) NSTimer *pingTimer;
@property (nonatomic, retain) NSTimer *reconnectTimer; @property (nonatomic, retain) NSTimer *reconnectTimer;
@ -47,14 +50,19 @@ NSString *const SocketConnectingNotification = @"SocketConnectingNotification";
@implementation TSSocketManager @implementation TSSocketManager
- (instancetype)init { - (instancetype)initWithStorageManager:(TSStorageManager *)storageManager
{
self = [super init]; self = [super init];
if (self) { if (!self) {
self.websocket = nil; return self;
[self addObserver:self forKeyPath:@"status" options:0 context:kSocketStatusObservationContext];
} }
_websocket = nil;
_signalService = [[OWSSignalService alloc] initWithStorageManager:storageManager];
[self addObserver:self forKeyPath:@"status" options:0 context:kSocketStatusObservationContext];
return self; return self;
} }
@ -62,49 +70,57 @@ NSString *const SocketConnectingNotification = @"SocketConnectingNotification";
static TSSocketManager *sharedMyManager = nil; static TSSocketManager *sharedMyManager = nil;
static dispatch_once_t onceToken; static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{ dispatch_once(&onceToken, ^{
sharedMyManager = [[self alloc] init]; sharedMyManager = [[self alloc] initWithStorageManager:[TSStorageManager sharedManager]];
sharedMyManager.fetchingTaskIdentifier = UIBackgroundTaskInvalid; sharedMyManager.fetchingTaskIdentifier = UIBackgroundTaskInvalid;
sharedMyManager.didConnectBg = NO; sharedMyManager.didConnectBg = NO;
sharedMyManager.shouldDownloadMessage = NO; sharedMyManager.shouldDownloadMessage = NO;
sharedMyManager.didRetreiveMessageBg = NO; sharedMyManager.didRetreiveMessageBg = NO;
}); });
return sharedMyManager; return sharedMyManager;
} }
#pragma mark - Manage Socket #pragma mark - Manage Socket
+ (void)becomeActive { + (void)becomeActive
TSSocketManager *sharedInstance = [self sharedManager]; {
SRWebSocket *socket = [sharedInstance websocket]; [[self sharedManager] becomeActive];
}
- (void)becomeActive
{
if (self.signalService.isCensored) {
DDLogWarn(@"%@ Refusing to start websocket in `becomeActive`.", self.tag);
return;
}
SRWebSocket *socket = self.websocket;
if (socket) { if (socket) {
switch ([socket readyState]) { switch ([socket readyState]) {
case SR_OPEN: case SR_OPEN:
DDLogVerbose(@"WebSocket already open on connection request"); DDLogVerbose(@"WebSocket already open on connection request");
sharedInstance.status = kSocketStatusOpen; self.status = kSocketStatusOpen;
return; return;
case SR_CONNECTING: case SR_CONNECTING:
DDLogVerbose(@"WebSocket is already connecting"); DDLogVerbose(@"WebSocket is already connecting");
sharedInstance.status = kSocketStatusConnecting; self.status = kSocketStatusConnecting;
return; return;
default: default:
[socket close]; [socket close];
sharedInstance.status = kSocketStatusClosed; self.status = kSocketStatusClosed;
socket.delegate = nil; socket.delegate = nil;
socket = nil; socket = nil;
break; break;
} }
} }
NSString *webSocketConnect = NSString *webSocketConnect = [textSecureWebSocketAPI stringByAppendingString:[self webSocketAuthenticationString]];
[textSecureWebSocketAPI stringByAppendingString:[[self sharedManager] webSocketAuthenticationString]];
NSURL *webSocketConnectURL = [NSURL URLWithString:webSocketConnect]; NSURL *webSocketConnectURL = [NSURL URLWithString:webSocketConnect];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:webSocketConnectURL]; NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:webSocketConnectURL];
socket = [[SRWebSocket alloc] initWithURLRequest:request securityPolicy:[OWSWebsocketSecurityPolicy sharedPolicy]]; socket = [[SRWebSocket alloc] initWithURLRequest:request securityPolicy:[OWSWebsocketSecurityPolicy sharedPolicy]];
socket.delegate = [self sharedManager]; socket.delegate = self;
[[self sharedManager] setWebsocket:socket]; [self setWebsocket:socket];
[socket open]; [socket open];
} }
@ -362,4 +378,16 @@ NSString *const SocketConnectingNotification = @"SocketConnectingNotification";
[[self sharedManager] notifyStatusChange]; [[self sharedManager] notifyStatusChange];
} }
#pragma mark - Logging
+ (NSString *)tag
{
return [NSString stringWithFormat:@"[%@]", self.class];
}
- (NSString *)tag
{
return self.class.tag;
}
@end @end

Loading…
Cancel
Save