replace SocketRocket with Starscream

This commit is contained in:
Michael Kirk 2019-01-14 17:04:31 -07:00
parent 4b3c43eed6
commit 16c8a1a76e
11 changed files with 256 additions and 133 deletions

View File

@ -38,12 +38,6 @@ def shared_pods
pod 'Mantle', git: 'https://github.com/signalapp/Mantle', branch: 'signal-master' pod 'Mantle', git: 'https://github.com/signalapp/Mantle', branch: 'signal-master'
# pod 'Mantle', path: '../Mantle' # pod 'Mantle', path: '../Mantle'
# SocketRocket has some critical crash fixes on Github, but have published an official release to cocoapods in ages, so
# we were following master
# Forked and have an open PR with our changes, but they have not been merged.
# pod 'SocketRocket', :git => 'https://github.com/facebook/SocketRocket.git', inhibit_warnings: true
pod 'SocketRocket', :git => 'https://github.com/signalapp/SocketRocket.git', branch: 'mkirk/handle-sec-err', inhibit_warnings: true
# Forked for compatibily with the ShareExtension, changes have an open PR, but have not been merged. # Forked for compatibily with the ShareExtension, changes have an open PR, but have not been merged.
pod 'YapDatabase/SQLCipher', :git => 'https://github.com/signalapp/YapDatabase.git', branch: 'signal-release' pod 'YapDatabase/SQLCipher', :git => 'https://github.com/signalapp/YapDatabase.git', branch: 'signal-release'
# pod 'YapDatabase/SQLCipher', path: '../YapDatabase' # pod 'YapDatabase/SQLCipher', path: '../YapDatabase'
@ -52,6 +46,9 @@ def shared_pods
pod 'GRKOpenSSLFramework', git: 'https://github.com/signalapp/GRKOpenSSLFramework' pod 'GRKOpenSSLFramework', git: 'https://github.com/signalapp/GRKOpenSSLFramework'
#pod 'GRKOpenSSLFramework', path: '../GRKOpenSSLFramework' #pod 'GRKOpenSSLFramework', path: '../GRKOpenSSLFramework'
pod 'Starscream', git: 'git@github.com:signalapp/Starscream.git', branch: 'signal-release'
# pod 'Starscream', path: '../Starscream'
### ###
# third party pods # third party pods
#### ####

View File

@ -94,7 +94,7 @@ PODS:
- SAMKeychain - SAMKeychain
- SignalCoreKit - SignalCoreKit
- SignalMetadataKit - SignalMetadataKit
- SocketRocket - Starscream
- SwiftProtobuf - SwiftProtobuf
- YapDatabase/SQLCipher - YapDatabase/SQLCipher
- SignalServiceKit/Tests (0.9.0): - SignalServiceKit/Tests (0.9.0):
@ -110,16 +110,16 @@ PODS:
- SAMKeychain - SAMKeychain
- SignalCoreKit - SignalCoreKit
- SignalMetadataKit - SignalMetadataKit
- SocketRocket - Starscream
- SwiftProtobuf - SwiftProtobuf
- YapDatabase/SQLCipher - YapDatabase/SQLCipher
- SocketRocket (0.5.1)
- SQLCipher (4.0.1): - SQLCipher (4.0.1):
- SQLCipher/standard (= 4.0.1) - SQLCipher/standard (= 4.0.1)
- SQLCipher/common (4.0.1) - SQLCipher/common (4.0.1)
- SQLCipher/standard (4.0.1): - SQLCipher/standard (4.0.1):
- SQLCipher/common - SQLCipher/common
- SSZipArchive (2.1.4) - SSZipArchive (2.1.4)
- Starscream (3.0.6)
- SwiftProtobuf (1.2.0) - SwiftProtobuf (1.2.0)
- YapDatabase/SQLCipher (3.1.1): - YapDatabase/SQLCipher (3.1.1):
- YapDatabase/SQLCipher/Core (= 3.1.1) - YapDatabase/SQLCipher/Core (= 3.1.1)
@ -205,9 +205,9 @@ DEPENDENCIES:
- SignalMetadataKit/Tests (from `https://github.com/signalapp/SignalMetadataKit`) - SignalMetadataKit/Tests (from `https://github.com/signalapp/SignalMetadataKit`)
- SignalServiceKit (from `.`) - SignalServiceKit (from `.`)
- SignalServiceKit/Tests (from `.`) - SignalServiceKit/Tests (from `.`)
- SocketRocket (from `https://github.com/signalapp/SocketRocket.git`, branch `mkirk/handle-sec-err`)
- SQLCipher (>= 4.0.1) - SQLCipher (>= 4.0.1)
- SSZipArchive - SSZipArchive
- "Starscream (from `git@github.com:signalapp/Starscream.git`, branch `signal-release`)"
- YapDatabase/SQLCipher (from `https://github.com/signalapp/YapDatabase.git`, branch `signal-release`) - YapDatabase/SQLCipher (from `https://github.com/signalapp/YapDatabase.git`, branch `signal-release`)
- YYImage - YYImage
@ -244,9 +244,9 @@ EXTERNAL SOURCES:
:git: https://github.com/signalapp/SignalMetadataKit :git: https://github.com/signalapp/SignalMetadataKit
SignalServiceKit: SignalServiceKit:
:path: "." :path: "."
SocketRocket: Starscream:
:branch: mkirk/handle-sec-err :branch: signal-release
:git: https://github.com/signalapp/SocketRocket.git :git: "git@github.com:signalapp/Starscream.git"
YapDatabase: YapDatabase:
:branch: signal-release :branch: signal-release
:git: https://github.com/signalapp/YapDatabase.git :git: https://github.com/signalapp/YapDatabase.git
@ -273,9 +273,9 @@ CHECKOUT OPTIONS:
SignalMetadataKit: SignalMetadataKit:
:commit: 56f28fc3a6e35d548d034ef7d0009f233ca0aa62 :commit: 56f28fc3a6e35d548d034ef7d0009f233ca0aa62
:git: https://github.com/signalapp/SignalMetadataKit :git: https://github.com/signalapp/SignalMetadataKit
SocketRocket: Starscream:
:commit: 9f9563a83cd8960503074aa8de72206f83fb7a69 :commit: edfb5964c5375ca55b3b0c517e1b5b7d0ac4519a
:git: https://github.com/signalapp/SocketRocket.git :git: "git@github.com:signalapp/Starscream.git"
YapDatabase: YapDatabase:
:commit: 8e2b69110efd9499a5b553fda0165d2cea4e818d :commit: 8e2b69110efd9499a5b553fda0165d2cea4e818d
:git: https://github.com/signalapp/YapDatabase.git :git: https://github.com/signalapp/YapDatabase.git
@ -295,14 +295,14 @@ SPEC CHECKSUMS:
SAMKeychain: 483e1c9f32984d50ca961e26818a534283b4cd5c SAMKeychain: 483e1c9f32984d50ca961e26818a534283b4cd5c
SignalCoreKit: c2d8132cdedb95d35eb2f8ae7eac0957695d0a8b SignalCoreKit: c2d8132cdedb95d35eb2f8ae7eac0957695d0a8b
SignalMetadataKit: 6fa5e9a53c7f104568662521a2f3874672ff7a02 SignalMetadataKit: 6fa5e9a53c7f104568662521a2f3874672ff7a02
SignalServiceKit: 80d774c32b22567682f63c36bf9da265d82083bb SignalServiceKit: c637b66e485538dda76836a1ec560dc556035430
SocketRocket: dbb1554b8fc288ef8ef370d6285aeca7361be31e
SQLCipher: 4636a257060f6f1b4e143a143028b61a2b462d0d SQLCipher: 4636a257060f6f1b4e143a143028b61a2b462d0d
SSZipArchive: 41455d4b8d2b6ab93990820b50dc697c2554a322 SSZipArchive: 41455d4b8d2b6ab93990820b50dc697c2554a322
Starscream: ef3ece99d765eeccb67de105bfa143f929026cf5
SwiftProtobuf: 91a9856079044ef4ec762b2344c763cd9e5a73c1 SwiftProtobuf: 91a9856079044ef4ec762b2344c763cd9e5a73c1
YapDatabase: b418a4baa6906e8028748938f9159807fd039af4 YapDatabase: b418a4baa6906e8028748938f9159807fd039af4
YYImage: 1e1b62a9997399593e4b9c4ecfbbabbf1d3f3b54 YYImage: 1e1b62a9997399593e4b9c4ecfbbabbf1d3f3b54
PODFILE CHECKSUM: 7ccd091c65f353c7fcc3021a13b557a576d48509 PODFILE CHECKSUM: 7856bf087f931a4089ebd9a08ed1dfe8389b8afc
COCOAPODS: 1.5.3 COCOAPODS: 1.5.3

2
Pods

@ -1 +1 @@
Subproject commit 527dca96c23f0ac15664e9762987ff017cabdf90 Subproject commit c653092655ec0c592d7e733e1bd5eee342849ae0

View File

@ -3145,7 +3145,7 @@
"${BUILT_PRODUCTS_DIR}/SignalCoreKit/SignalCoreKit.framework", "${BUILT_PRODUCTS_DIR}/SignalCoreKit/SignalCoreKit.framework",
"${BUILT_PRODUCTS_DIR}/SignalMetadataKit/SignalMetadataKit.framework", "${BUILT_PRODUCTS_DIR}/SignalMetadataKit/SignalMetadataKit.framework",
"${BUILT_PRODUCTS_DIR}/SignalServiceKit/SignalServiceKit.framework", "${BUILT_PRODUCTS_DIR}/SignalServiceKit/SignalServiceKit.framework",
"${BUILT_PRODUCTS_DIR}/SocketRocket/SocketRocket.framework", "${BUILT_PRODUCTS_DIR}/Starscream/Starscream.framework",
"${BUILT_PRODUCTS_DIR}/SwiftProtobuf/SwiftProtobuf.framework", "${BUILT_PRODUCTS_DIR}/SwiftProtobuf/SwiftProtobuf.framework",
"${BUILT_PRODUCTS_DIR}/YYImage/YYImage.framework", "${BUILT_PRODUCTS_DIR}/YYImage/YYImage.framework",
"${BUILT_PRODUCTS_DIR}/YapDatabase/YapDatabase.framework", "${BUILT_PRODUCTS_DIR}/YapDatabase/YapDatabase.framework",
@ -3169,7 +3169,7 @@
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SignalCoreKit.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SignalCoreKit.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SignalMetadataKit.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SignalMetadataKit.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SignalServiceKit.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SignalServiceKit.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SocketRocket.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Starscream.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftProtobuf.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftProtobuf.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/YYImage.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/YYImage.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/YapDatabase.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/YapDatabase.framework",
@ -3220,7 +3220,7 @@
"${BUILT_PRODUCTS_DIR}/SignalCoreKit/SignalCoreKit.framework", "${BUILT_PRODUCTS_DIR}/SignalCoreKit/SignalCoreKit.framework",
"${BUILT_PRODUCTS_DIR}/SignalMetadataKit/SignalMetadataKit.framework", "${BUILT_PRODUCTS_DIR}/SignalMetadataKit/SignalMetadataKit.framework",
"${BUILT_PRODUCTS_DIR}/SignalServiceKit/SignalServiceKit.framework", "${BUILT_PRODUCTS_DIR}/SignalServiceKit/SignalServiceKit.framework",
"${BUILT_PRODUCTS_DIR}/SocketRocket/SocketRocket.framework", "${BUILT_PRODUCTS_DIR}/Starscream/Starscream.framework",
"${BUILT_PRODUCTS_DIR}/SwiftProtobuf/SwiftProtobuf.framework", "${BUILT_PRODUCTS_DIR}/SwiftProtobuf/SwiftProtobuf.framework",
"${BUILT_PRODUCTS_DIR}/YYImage/YYImage.framework", "${BUILT_PRODUCTS_DIR}/YYImage/YYImage.framework",
"${BUILT_PRODUCTS_DIR}/YapDatabase/YapDatabase.framework", "${BUILT_PRODUCTS_DIR}/YapDatabase/YapDatabase.framework",
@ -3243,7 +3243,7 @@
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SignalCoreKit.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SignalCoreKit.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SignalMetadataKit.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SignalMetadataKit.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SignalServiceKit.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SignalServiceKit.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SocketRocket.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Starscream.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftProtobuf.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftProtobuf.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/YYImage.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/YYImage.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/YapDatabase.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/YapDatabase.framework",

View File

@ -44,7 +44,7 @@ An Objective-C library for communicating with the Signal messaging service.
s.dependency 'AxolotlKit' s.dependency 'AxolotlKit'
s.dependency 'Mantle' s.dependency 'Mantle'
s.dependency 'YapDatabase/SQLCipher' s.dependency 'YapDatabase/SQLCipher'
s.dependency 'SocketRocket' s.dependency 'Starscream'
s.dependency 'libPhoneNumber-iOS' s.dependency 'libPhoneNumber-iOS'
s.dependency 'GRKOpenSSLFramework' s.dependency 'GRKOpenSSLFramework'
s.dependency 'SAMKeychain' s.dependency 'SAMKeychain'

View File

@ -0,0 +1,176 @@
//
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
//
import Foundation
import Starscream
@objc
public enum SSKWebSocketState: UInt {
case open, connecting, disconnected
}
@objc
public class SSKWebSocketError: NSObject, CustomNSError {
init(underlyingError: Starscream.WSError) {
self.underlyingError = underlyingError
}
// MARK: - CustomNSError
@objc
public static let errorDomain = "SignalServiceKit.SSKWebSocketError"
public var errorUserInfo: [String: Any] {
return [
type(of: self).kStatusCodeKey: underlyingError.code,
NSUnderlyingErrorKey: (underlyingError as NSError)
]
}
// MARK: -
@objc
static let kStatusCodeKey = "SSKWebSocketErrorStatusCode"
let underlyingError: Starscream.WSError
}
@objc
public protocol SSKWebSocket {
@objc
var delegate: SSKWebSocketDelegate? { get set }
@objc
var state: SSKWebSocketState { get }
@objc
func connect()
@objc
func disconnect()
@objc(writeData:error:)
func write(data: Data) throws
@objc
func writePing() throws
}
@objc
public protocol SSKWebSocketDelegate: class {
func websocketDidConnect(socket: SSKWebSocket)
func websocketDidDisconnect(socket: SSKWebSocket, error: Error?)
func websocketDidReceiveData(socket: SSKWebSocket, data: Data)
@objc optional func websocketDidReceiveMessage(socket: SSKWebSocket, text: String)
}
@objc
public class SSKWebSocketManager: NSObject {
@objc
public class func buildSocket(request: URLRequest) -> SSKWebSocket {
return SSKWebSocketImpl(request: request)
}
}
class SSKWebSocketImpl: SSKWebSocket {
private let socket: Starscream.WebSocket
init(request: URLRequest) {
let socket = WebSocket(request: request)
socket.disableSSLCertValidation = true
socket.socketSecurityLevel = StreamSocketSecurityLevel.tlSv1_2
let security = SSLSecurity(certs: [TextSecureCertificate()], usePublicKeys: false)
security.validateEntireChain = false
socket.security = security
// TODO cipher suite selection
// socket.enabledSSLCipherSuites = [TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256]
self.socket = socket
socket.delegate = self
}
// MARK: - SSKWebSocket
weak var delegate: SSKWebSocketDelegate?
var hasEverConnected = false
var state: SSKWebSocketState {
if socket.isConnected {
return .open
}
if hasEverConnected {
return .disconnected
}
return .connecting
}
func connect() {
socket.connect()
}
func disconnect() {
socket.disconnect()
}
func write(data: Data) throws {
socket.write(data: data)
}
func writePing() throws {
socket.write(ping: Data())
}
}
extension SSKWebSocketImpl: WebSocketDelegate {
func websocketDidConnect(socket: WebSocketClient) {
hasEverConnected = true
delegate?.websocketDidConnect(socket: self)
}
func websocketDidDisconnect(socket: WebSocketClient, error: Error?) {
let websocketError: Error?
switch error {
case let wsError as WSError:
websocketError = SSKWebSocketError(underlyingError: wsError)
default:
assert(error == nil, "unexpected error type: \(String(describing: error))")
websocketError = error
}
delegate?.websocketDidDisconnect(socket: self, error: websocketError)
}
func websocketDidReceiveMessage(socket: WebSocketClient, text: String) {
if let websocketDidReceiveMessage = self.delegate?.websocketDidReceiveMessage {
websocketDidReceiveMessage(self, text)
}
}
func websocketDidReceiveData(socket: WebSocketClient, data: Data) {
delegate?.websocketDidReceiveData(socket: self, data: data)
}
}
private func TextSecureCertificate() -> SSLCert {
let data = OWSHTTPSecurityPolicy.dataFromCertificateFile(forService: "textsecure")
return SSLCert(data: data)
}
private extension StreamSocketSecurityLevel {
static var tlSv1_2: StreamSocketSecurityLevel {
return StreamSocketSecurityLevel(rawValue: "kCFStreamSocketSecurityLevelTLSv1_2")
}
}

View File

@ -15,7 +15,6 @@
#import "OWSMessageReceiver.h" #import "OWSMessageReceiver.h"
#import "OWSPrimaryStorage.h" #import "OWSPrimaryStorage.h"
#import "OWSSignalService.h" #import "OWSSignalService.h"
#import "OWSWebsocketSecurityPolicy.h"
#import "SSKEnvironment.h" #import "SSKEnvironment.h"
#import "TSAccountManager.h" #import "TSAccountManager.h"
#import "TSConstants.h" #import "TSConstants.h"
@ -24,7 +23,6 @@
#import <SignalCoreKit/Cryptography.h> #import <SignalCoreKit/Cryptography.h>
#import <SignalCoreKit/Threading.h> #import <SignalCoreKit/Threading.h>
#import <SignalServiceKit/SignalServiceKit-Swift.h> #import <SignalServiceKit/SignalServiceKit-Swift.h>
#import <SocketRocket/SRWebSocket.h>
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
@ -144,13 +142,13 @@ NSString *const kNSNotification_OWSWebSocketStateDidChange = @"kNSNotification_O
#pragma mark - #pragma mark -
// OWSWebSocket's properties should only be accessed from the main thread. // OWSWebSocket's properties should only be accessed from the main thread.
@interface OWSWebSocket () <SRWebSocketDelegate> @interface OWSWebSocket () <SSKWebSocketDelegate>
// This class has a few "tiers" of state. // This class has a few "tiers" of state.
// //
// The first tier is the actual websocket and the timers used // The first tier is the actual websocket and the timers used
// to keep it alive and connected. // to keep it alive and connected.
@property (nonatomic, nullable) SRWebSocket *websocket; @property (nonatomic, nullable) id<SSKWebSocket> websocket;
@property (nonatomic, nullable) NSTimer *heartbeatTimer; @property (nonatomic, nullable) NSTimer *heartbeatTimer;
@property (nonatomic, nullable) NSTimer *reconnectTimer; @property (nonatomic, nullable) NSTimer *reconnectTimer;
@ -257,11 +255,6 @@ NSString *const kNSNotification_OWSWebSocketStateDidChange = @"kNSNotification_O
return SSKEnvironment.shared.notificationsManager; return SSKEnvironment.shared.notificationsManager;
} }
- (OWSWebsocketSecurityPolicy *)websocketSecurityPolicy
{
return OWSWebsocketSecurityPolicy.sharedPolicy;
}
- (id<OWSUDManager>)udManager { - (id<OWSUDManager>)udManager {
return SSKEnvironment.shared.udManager; return SSKEnvironment.shared.udManager;
} }
@ -310,11 +303,11 @@ NSString *const kNSNotification_OWSWebSocketStateDidChange = @"kNSNotification_O
// Try to reuse the existing socket (if any) if it is in a valid state. // Try to reuse the existing socket (if any) if it is in a valid state.
if (self.websocket) { if (self.websocket) {
switch ([self.websocket readyState]) { switch (self.websocket.state) {
case SR_OPEN: case SSKWebSocketStateOpen:
self.state = OWSWebSocketStateOpen; self.state = OWSWebSocketStateOpen;
return; return;
case SR_CONNECTING: case SSKWebSocketStateConnecting:
OWSLogVerbose(@"WebSocket is already connecting"); OWSLogVerbose(@"WebSocket is already connecting");
self.state = OWSWebSocketStateConnecting; self.state = OWSWebSocketStateConnecting;
return; return;
@ -355,8 +348,7 @@ NSString *const kNSNotification_OWSWebSocketStateDidChange = @"kNSNotification_O
// and class state to reflect changes in app state. // and class state to reflect changes in app state.
// //
// We learn about changes to socket state through websocket // We learn about changes to socket state through websocket
// delegate methods like [webSocketDidOpen:], [didFailWithError:...] // delegate methods. These delegate methods are sometimes
// and [didCloseWithCode:...]. These delegate methods are sometimes
// invoked _after_ web socket state changes, so we sometimes learn // invoked _after_ web socket state changes, so we sometimes learn
// about changes to socket state in [ensureWebsocket]. Put another way, // about changes to socket state in [ensureWebsocket]. Put another way,
// it's not safe to assume we'll learn of changes to websocket state // it's not safe to assume we'll learn of changes to websocket state
@ -425,18 +417,17 @@ NSString *const kNSNotification_OWSWebSocketStateDidChange = @"kNSNotification_O
NSURL *webSocketConnectURL = [NSURL URLWithString:webSocketConnect]; NSURL *webSocketConnectURL = [NSURL URLWithString:webSocketConnect];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:webSocketConnectURL]; NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:webSocketConnectURL];
SRWebSocket *socket = id<SSKWebSocket> socket = [SSKWebSocketManager buildSocketWithRequest:request];
[[SRWebSocket alloc] initWithURLRequest:request securityPolicy:self.websocketSecurityPolicy];
socket.delegate = self; socket.delegate = self;
[self setWebsocket:socket]; [self setWebsocket:socket];
// [SRWebSocket open] could hypothetically call a delegate method (e.g. if // `connect` could hypothetically call a delegate method (e.g. if
// the socket failed immediately for some reason), so we update the state // the socket failed immediately for some reason), so we update the state
// _before_ calling it, not after. // _before_ calling it, not after.
_state = state; _state = state;
self.canMakeRequests = state == OWSWebSocketStateOpen; self.canMakeRequests = state == OWSWebSocketStateOpen;
[socket open]; [socket connect];
[self failAllPendingSocketMessagesIfNecessary]; [self failAllPendingSocketMessagesIfNecessary];
return; return;
} }
@ -463,7 +454,7 @@ NSString *const kNSNotification_OWSWebSocketStateDidChange = @"kNSNotification_O
OWSAssertIsOnMainThread(); OWSAssertIsOnMainThread();
self.websocket.delegate = nil; self.websocket.delegate = nil;
[self.websocket close]; [self.websocket disconnect];
self.websocket = nil; self.websocket = nil;
[self.heartbeatTimer invalidate]; [self.heartbeatTimer invalidate];
self.heartbeatTimer = nil; self.heartbeatTimer = nil;
@ -554,7 +545,7 @@ NSString *const kNSNotification_OWSWebSocketStateDidChange = @"kNSNotification_O
return; return;
} }
BOOL wasScheduled = [self.websocket sendDataNoCopy:messageData error:&error]; BOOL wasScheduled = [self.websocket writeData:messageData error:&error];
if (!wasScheduled || error) { if (!wasScheduled || error) {
OWSFailDebug(@"could not send socket request: %@", error); OWSFailDebug(@"could not send socket request: %@", error);
[socketMessage didFailBeforeSending]; [socketMessage didFailBeforeSending];
@ -676,13 +667,13 @@ NSString *const kNSNotification_OWSWebSocketStateDidChange = @"kNSNotification_O
} }
} }
#pragma mark - Delegate methods #pragma mark - SSKWebSocketDelegate
- (void)webSocketDidOpen:(SRWebSocket *)webSocket - (void)websocketDidConnectWithSocket:(id<SSKWebSocket>)websocket
{ {
OWSAssertIsOnMainThread(); OWSAssertIsOnMainThread();
OWSAssertDebug(webSocket); OWSAssertDebug(websocket);
if (webSocket != self.websocket) { if (websocket != self.websocket) {
// Ignore events from obsolete web sockets. // Ignore events from obsolete web sockets.
return; return;
} }
@ -695,19 +686,18 @@ NSString *const kNSNotification_OWSWebSocketStateDidChange = @"kNSNotification_O
[self.outageDetection reportConnectionSuccess]; [self.outageDetection reportConnectionSuccess];
} }
- (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error - (void)websocketDidDisconnectWithSocket:(id<SSKWebSocket>)websocket error:(nullable NSError *)error
{ {
OWSAssertIsOnMainThread(); OWSAssertIsOnMainThread();
OWSAssertDebug(webSocket); OWSAssertDebug(websocket);
if (webSocket != self.websocket) { if (websocket != self.websocket) {
// Ignore events from obsolete web sockets. // Ignore events from obsolete web sockets.
return; return;
} }
OWSLogError(@"Websocket did fail with error: %@", error); OWSLogError(@"Websocket did fail with error: %@", error);
if ([error.domain isEqualToString:SSKWebSocketError.errorDomain]) {
if ([error.domain isEqualToString:SRWebSocketErrorDomain] && error.code == 2132) { NSNumber *_Nullable statusCode = error.userInfo[SSKWebSocketError.kStatusCodeKey];
NSNumber *_Nullable statusCode = error.userInfo[SRHTTPResponseErrorKey];
if (statusCode.unsignedIntegerValue == 403) { if (statusCode.unsignedIntegerValue == 403) {
if (self.tsAccountManager.isRegisteredAndReady) { if (self.tsAccountManager.isRegisteredAndReady) {
[self.tsAccountManager setIsDeregistered:YES]; [self.tsAccountManager setIsDeregistered:YES];
@ -720,12 +710,12 @@ NSString *const kNSNotification_OWSWebSocketStateDidChange = @"kNSNotification_O
[self handleSocketFailure]; [self handleSocketFailure];
} }
- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(NSData *)data - (void)websocketDidReceiveDataWithSocket:(id<SSKWebSocket>)websocket data:(NSData *)data
{ {
OWSAssertIsOnMainThread(); OWSAssertIsOnMainThread();
OWSAssertDebug(webSocket); OWSAssertDebug(websocket);
if (webSocket != self.websocket) { if (websocket != self.websocket) {
// Ignore events from obsolete web sockets. // Ignore events from obsolete web sockets.
return; return;
} }
@ -749,6 +739,8 @@ NSString *const kNSNotification_OWSWebSocketStateDidChange = @"kNSNotification_O
} }
} }
#pragma mark -
- (dispatch_queue_t)serialQueue - (dispatch_queue_t)serialQueue
{ {
static dispatch_queue_t _serialQueue; static dispatch_queue_t _serialQueue;
@ -853,7 +845,7 @@ NSString *const kNSNotification_OWSWebSocketStateDidChange = @"kNSNotification_O
return; return;
} }
[self.websocket sendDataNoCopy:messageData error:&error]; [self.websocket writeData:messageData error:&error];
if (error) { if (error) {
OWSLogWarn(@"Error while trying to write on websocket %@", error); OWSLogWarn(@"Error while trying to write on websocket %@", error);
[self handleSocketFailure]; [self handleSocketFailure];
@ -887,30 +879,13 @@ NSString *const kNSNotification_OWSWebSocketStateDidChange = @"kNSNotification_O
[self.outageDetection reportConnectionFailure]; [self.outageDetection reportConnectionFailure];
} }
- (void)webSocket:(SRWebSocket *)webSocket
didCloseWithCode:(NSInteger)code
reason:(nullable NSString *)reason
wasClean:(BOOL)wasClean
{
OWSAssertIsOnMainThread();
OWSAssertDebug(webSocket);
if (webSocket != self.websocket) {
// Ignore events from obsolete web sockets.
return;
}
OWSLogWarn(@"Websocket did close with code: %ld", (long)code);
[self handleSocketFailure];
}
- (void)webSocketHeartBeat - (void)webSocketHeartBeat
{ {
OWSAssertIsOnMainThread(); OWSAssertIsOnMainThread();
if ([self shouldSocketBeOpen]) { if ([self shouldSocketBeOpen]) {
NSError *error; NSError *error;
[self.websocket sendPing:nil error:&error]; [self.websocket writePingAndReturnError:&error];
if (error) { if (error) {
OWSLogWarn(@"Error in websocket heartbeat: %@", error.localizedDescription); OWSLogWarn(@"Error in websocket heartbeat: %@", error.localizedDescription);
[self handleSocketFailure]; [self handleSocketFailure];

View File

@ -1,12 +1,17 @@
// //
// Created by Fred on 01/09/15. // Copyright (c) 2019 Open Whisper Systems. All rights reserved.
// Copyright © 2015 Open Whisper Systems. All rights reserved.
// //
#import <AFNetworking/AFSecurityPolicy.h> #import <AFNetworking/AFSecurityPolicy.h>
NS_ASSUME_NONNULL_BEGIN
extern NSData *SSKTextSecureServiceCertificateData(void);
@interface OWSHTTPSecurityPolicy : AFSecurityPolicy @interface OWSHTTPSecurityPolicy : AFSecurityPolicy
+ (instancetype)sharedPolicy; + (instancetype)sharedPolicy;
@end @end
NS_ASSUME_NONNULL_END

View File

@ -1,10 +1,12 @@
// //
// Copyright (c) 2018 Open Whisper Systems. All rights reserved. // Copyright (c) 2019 Open Whisper Systems. All rights reserved.
// //
#import "OWSHTTPSecurityPolicy.h" #import "OWSHTTPSecurityPolicy.h"
#import <AssertMacros.h> #import <AssertMacros.h>
NS_ASSUME_NONNULL_BEGIN
@implementation OWSHTTPSecurityPolicy @implementation OWSHTTPSecurityPolicy
+ (instancetype)sharedPolicy { + (instancetype)sharedPolicy {
@ -21,24 +23,15 @@
if (self) { if (self) {
self.pinnedCertificates = [NSSet setWithArray:@[ self.pinnedCertificates = [NSSet setWithArray:@[
[self certificateDataForService:@"textsecure"], [self.class certificateDataForService:@"textsecure"]
]]; ]];
} }
return self; return self;
} }
- (NSArray *)certs { + (NSData *)dataFromCertificateFileForService:(NSString *)service
return @[ (__bridge id)[self certificateForService:@"textsecure"] ]; {
}
- (NSData *)certificateDataForService:(NSString *)service {
SecCertificateRef certRef = [self certificateForService:service];
return (__bridge_transfer NSData *)SecCertificateCopyData(certRef);
}
- (SecCertificateRef)certificateForService:(NSString *)service {
NSBundle *bundle = [NSBundle bundleForClass:self.class]; NSBundle *bundle = [NSBundle bundleForClass:self.class];
NSString *path = [bundle pathForResource:service ofType:@"cer"]; NSString *path = [bundle pathForResource:service ofType:@"cer"];
@ -46,12 +39,25 @@
OWSFail(@"Missing signing certificate for service %@", service); OWSFail(@"Missing signing certificate for service %@", service);
} }
NSData *certificateData = [NSData dataWithContentsOfFile:path]; NSData *data = [NSData dataWithContentsOfFile:path];
OWSAssert(data.length > 0);
return data;
}
+ (NSData *)certificateDataForService:(NSString *)service {
SecCertificateRef certRef = [self certificateForService:service];
return (__bridge_transfer NSData *)SecCertificateCopyData(certRef);
}
+ (SecCertificateRef)certificateForService:(NSString *)service
{
NSData *certificateData = [self dataFromCertificateFileForService:service];
return SecCertificateCreateWithData(NULL, (__bridge CFDataRef)(certificateData)); return SecCertificateCreateWithData(NULL, (__bridge CFDataRef)(certificateData));
} }
- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust forDomain:(nullable NSString *)domain
- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust forDomain:(NSString *)domain { {
NSMutableArray *policies = [NSMutableArray array]; NSMutableArray *policies = [NSMutableArray array];
[policies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)domain)]; [policies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)domain)];
@ -90,3 +96,5 @@ _out:
} }
@end @end
NS_ASSUME_NONNULL_END

View File

@ -1,9 +0,0 @@
// Copyright © 2016 Open Whisper Systems. All rights reserved.
#import <SocketRocket/SRSecurityPolicy.h>
@interface OWSWebsocketSecurityPolicy : SRSecurityPolicy
+ (instancetype)sharedPolicy;
@end

View File

@ -1,29 +0,0 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
#import "OWSWebsocketSecurityPolicy.h"
#import "OWSHTTPSecurityPolicy.h"
#import <SocketRocket/SRSecurityPolicy.h>
@implementation OWSWebsocketSecurityPolicy
+ (instancetype)sharedPolicy {
static OWSWebsocketSecurityPolicy *websocketSecurityPolicy = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
// We use our own CA
websocketSecurityPolicy = [[self alloc] initWithCertificateChainValidationEnabled:NO];
#pragma clang diagnostic pop
});
return websocketSecurityPolicy;
}
- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust forDomain:(NSString *)domain {
// Delegate server trust to our existing HTTP policy.
return [[OWSHTTPSecurityPolicy sharedPolicy] evaluateServerTrust:serverTrust forDomain:domain];
}
@end