Compare commits
12 Commits
master
...
signal-rel
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
71ebc08f24 | ||
|
|
52d8c91ee2 | ||
|
|
b5c9019396 | ||
|
|
b09ea163c3 | ||
|
|
401ce53eca | ||
|
|
edfb5964c5 | ||
|
|
7ee961e23a | ||
|
|
34369f1386 | ||
|
|
8414c7fefb | ||
|
|
b09ba2877c | ||
|
|
fd76ffc24f | ||
|
|
fe4c768506 |
@ -80,10 +80,12 @@ class Decompressor {
|
||||
strm.avail_in = CUnsignedInt(count)
|
||||
|
||||
repeat {
|
||||
strm.next_out = UnsafeMutablePointer<UInt8>(&buffer)
|
||||
strm.avail_out = CUnsignedInt(buffer.count)
|
||||
buffer.withUnsafeMutableBytes { (bufferPtr) in
|
||||
strm.next_out = bufferPtr.bindMemory(to: UInt8.self).baseAddress
|
||||
strm.avail_out = CUnsignedInt(bufferPtr.count)
|
||||
|
||||
res = inflate(&strm, 0)
|
||||
res = inflate(&strm, 0)
|
||||
}
|
||||
|
||||
let byteCount = buffer.count - Int(strm.avail_out)
|
||||
out.append(buffer, count: byteCount)
|
||||
@ -142,10 +144,12 @@ class Compressor {
|
||||
strm.avail_in = CUnsignedInt(data.count)
|
||||
|
||||
repeat {
|
||||
strm.next_out = UnsafeMutablePointer<UInt8>(&buffer)
|
||||
strm.avail_out = CUnsignedInt(buffer.count)
|
||||
buffer.withUnsafeMutableBytes { (bufferPtr) in
|
||||
strm.next_out = bufferPtr.bindMemory(to: UInt8.self).baseAddress
|
||||
strm.avail_out = CUnsignedInt(bufferPtr.count)
|
||||
|
||||
res = deflate(&strm, Z_SYNC_FLUSH)
|
||||
res = deflate(&strm, Z_SYNC_FLUSH)
|
||||
}
|
||||
|
||||
let byteCount = buffer.count - Int(strm.avail_out)
|
||||
compressed.append(buffer, count: byteCount)
|
||||
|
||||
@ -149,7 +149,10 @@ open class SSLSecurity : SSLTrustValidator {
|
||||
} else {
|
||||
policy = SecPolicyCreateBasicX509()
|
||||
}
|
||||
SecTrustSetPolicies(trust,policy)
|
||||
guard SecTrustSetPolicies(trust, policy) == errSecSuccess else {
|
||||
assertionFailure("unable to set trust policies")
|
||||
return false
|
||||
}
|
||||
if self.usePublicKeys {
|
||||
if let keys = self.pubKeys {
|
||||
let serverPubKeys = publicKeyChain(trust)
|
||||
@ -167,9 +170,15 @@ open class SSLSecurity : SSLTrustValidator {
|
||||
for cert in certs {
|
||||
collect.append(SecCertificateCreateWithData(nil,cert as CFData)!)
|
||||
}
|
||||
SecTrustSetAnchorCertificates(trust,collect as NSArray)
|
||||
guard SecTrustSetAnchorCertificates(trust, collect as NSArray) == errSecSuccess else {
|
||||
assertionFailure("unable to set trust anchor certificates")
|
||||
return false
|
||||
}
|
||||
var result: SecTrustResultType = .unspecified
|
||||
SecTrustEvaluate(trust,&result)
|
||||
guard SecTrustEvaluate(trust, &result) == errSecSuccess else {
|
||||
assertionFailure("unable to evaluate trust")
|
||||
return false
|
||||
}
|
||||
if result == .unspecified || result == .proceed {
|
||||
if !validateEntireChain {
|
||||
return true
|
||||
@ -213,11 +222,17 @@ open class SSLSecurity : SSLTrustValidator {
|
||||
*/
|
||||
public func extractPublicKey(_ cert: SecCertificate, policy: SecPolicy) -> SecKey? {
|
||||
var possibleTrust: SecTrust?
|
||||
SecTrustCreateWithCertificates(cert, policy, &possibleTrust)
|
||||
guard SecTrustCreateWithCertificates(cert, policy, &possibleTrust) == errSecSuccess else {
|
||||
assertionFailure("failed to create trust with certificate")
|
||||
return nil
|
||||
}
|
||||
|
||||
guard let trust = possibleTrust else { return nil }
|
||||
var result: SecTrustResultType = .unspecified
|
||||
SecTrustEvaluate(trust, &result)
|
||||
guard SecTrustEvaluate(trust, &result) == errSecSuccess else {
|
||||
assertionFailure("failed to evaluate trust")
|
||||
return nil
|
||||
}
|
||||
return SecTrustCopyPublicKey(trust)
|
||||
}
|
||||
|
||||
|
||||
@ -49,6 +49,7 @@ public enum ErrorType: Error {
|
||||
case protocolError //There was an error parsing the WebSocket frames
|
||||
case upgradeError //There was an error during the HTTP upgrade
|
||||
case closeError //There was an error during the close (socket probably has been dereferenced)
|
||||
case osError // There was an error with the underlying OS
|
||||
}
|
||||
|
||||
public struct WSError: Error {
|
||||
@ -69,6 +70,7 @@ public protocol WebSocketClient: class {
|
||||
#else
|
||||
var security: SSLTrustValidator? {get set}
|
||||
var enabledSSLCipherSuites: [SSLCipherSuite]? {get set}
|
||||
var socketSecurityLevel: StreamSocketSecurityLevel { get set }
|
||||
#endif
|
||||
var isConnected: Bool {get}
|
||||
|
||||
@ -113,6 +115,7 @@ public struct SSLSettings {
|
||||
#if os(Linux)
|
||||
#else
|
||||
public let cipherSuites: [SSLCipherSuite]?
|
||||
public var socketSecurityLevel: StreamSocketSecurityLevel
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -143,6 +146,11 @@ open class FoundationStream : NSObject, WSStream, StreamDelegate {
|
||||
|
||||
public var enableSOCKSProxy = false
|
||||
|
||||
deinit {
|
||||
inputStream?.delegate = nil
|
||||
outputStream?.delegate = nil
|
||||
}
|
||||
|
||||
public func connect(url: URL, port: Int, timeout: TimeInterval, ssl: SSLSettings, completion: @escaping ((Error?) -> Void)) {
|
||||
var readStream: Unmanaged<CFReadStream>?
|
||||
var writeStream: Unmanaged<CFWriteStream>?
|
||||
@ -166,8 +174,8 @@ open class FoundationStream : NSObject, WSStream, StreamDelegate {
|
||||
inStream.delegate = self
|
||||
outStream.delegate = self
|
||||
if ssl.useSSL {
|
||||
inStream.setProperty(StreamSocketSecurityLevel.negotiatedSSL as AnyObject, forKey: Stream.PropertyKey.socketSecurityLevelKey)
|
||||
outStream.setProperty(StreamSocketSecurityLevel.negotiatedSSL as AnyObject, forKey: Stream.PropertyKey.socketSecurityLevelKey)
|
||||
inStream.setProperty(ssl.socketSecurityLevel as AnyObject, forKey: Stream.PropertyKey.socketSecurityLevelKey)
|
||||
outStream.setProperty(ssl.socketSecurityLevel as AnyObject, forKey: Stream.PropertyKey.socketSecurityLevelKey)
|
||||
#if os(watchOS) //watchOS us unfortunately is missing the kCFStream properties to make this work
|
||||
#else
|
||||
var settings = [NSObject: NSObject]()
|
||||
@ -416,6 +424,7 @@ open class WebSocket : NSObject, StreamDelegate, WebSocketClient, WSStreamDelega
|
||||
#else
|
||||
public var security: SSLTrustValidator?
|
||||
public var enabledSSLCipherSuites: [SSLCipherSuite]?
|
||||
public var socketSecurityLevel: StreamSocketSecurityLevel = .negotiatedSSL
|
||||
#endif
|
||||
|
||||
public var isConnected: Bool {
|
||||
@ -433,7 +442,6 @@ open class WebSocket : NSObject, StreamDelegate, WebSocketClient, WSStreamDelega
|
||||
|
||||
private struct CompressionState {
|
||||
var supportsCompression = false
|
||||
var messageNeedsDecompression = false
|
||||
var serverMaxWindowBits = 15
|
||||
var clientMaxWindowBits = 15
|
||||
var clientNoContextTakeover = false
|
||||
@ -447,6 +455,10 @@ open class WebSocket : NSObject, StreamDelegate, WebSocketClient, WSStreamDelega
|
||||
private var isConnecting = false
|
||||
private let mutex = NSLock()
|
||||
private var compressionState = CompressionState()
|
||||
// `currentMessageNeedsDecompression` is not part of the `compressionState` struct
|
||||
// because currentMessageNeedsDecompression can be mutated in the read queue concurrently
|
||||
// with other `compressionState` fields being accesseed on the write queue.
|
||||
private var currentMessageNeedsDecompression = false
|
||||
private var writeQueue = OperationQueue()
|
||||
private var readStack = [WSResponse]()
|
||||
private var inputQueue = [Data]()
|
||||
@ -589,8 +601,8 @@ open class WebSocket : NSObject, StreamDelegate, WebSocketClient, WSStreamDelega
|
||||
}
|
||||
request.setValue(headerWSUpgradeValue, forHTTPHeaderField: headerWSUpgradeName)
|
||||
request.setValue(headerWSConnectionValue, forHTTPHeaderField: headerWSConnectionName)
|
||||
headerSecKey = generateWebSocketKey()
|
||||
request.setValue(headerWSVersionValue, forHTTPHeaderField: headerWSVersionName)
|
||||
headerSecKey = try! generateWebSocketKey()
|
||||
request.setValue(headerSecKey, forHTTPHeaderField: headerWSKeyName)
|
||||
|
||||
if enableCompression {
|
||||
@ -627,16 +639,16 @@ open class WebSocket : NSObject, StreamDelegate, WebSocketClient, WSStreamDelega
|
||||
/**
|
||||
Generate a WebSocket key as needed in RFC.
|
||||
*/
|
||||
private func generateWebSocketKey() -> String {
|
||||
var key = ""
|
||||
let seed = 16
|
||||
for _ in 0..<seed {
|
||||
let uni = UnicodeScalar(UInt32(97 + arc4random_uniform(25)))
|
||||
key += "\(Character(uni!))"
|
||||
private func generateWebSocketKey() throws -> String {
|
||||
let kSocketKeyByteLength = 16
|
||||
var randomData = Data(count: kSocketKeyByteLength)
|
||||
try randomData.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer<UInt8>) throws -> Void in
|
||||
guard SecRandomCopyBytes(kSecRandomDefault, kSocketKeyByteLength, bytes) == errSecSuccess else {
|
||||
throw WSError(type: .osError, message: "unable to generate random bytes", code: 0)
|
||||
}
|
||||
}
|
||||
let data = key.data(using: String.Encoding.utf8)
|
||||
let baseKey = data?.base64EncodedString(options: NSData.Base64EncodingOptions(rawValue: 0))
|
||||
return baseKey!
|
||||
|
||||
return randomData.base64EncodedString()
|
||||
}
|
||||
|
||||
/**
|
||||
@ -657,15 +669,16 @@ open class WebSocket : NSObject, StreamDelegate, WebSocketClient, WSStreamDelega
|
||||
let settings = SSLSettings(useSSL: useSSL,
|
||||
disableCertValidation: disableSSLCertValidation,
|
||||
overrideTrustHostname: overrideTrustHostname,
|
||||
desiredTrustHostname: desiredTrustHostname),
|
||||
sslClientCertificate: sslClientCertificate
|
||||
desiredTrustHostname: desiredTrustHostname,
|
||||
sslClientCertificate: sslClientCertificate)
|
||||
#else
|
||||
let settings = SSLSettings(useSSL: useSSL,
|
||||
disableCertValidation: disableSSLCertValidation,
|
||||
overrideTrustHostname: overrideTrustHostname,
|
||||
desiredTrustHostname: desiredTrustHostname,
|
||||
sslClientCertificate: sslClientCertificate,
|
||||
cipherSuites: self.enabledSSLCipherSuites)
|
||||
cipherSuites: enabledSSLCipherSuites,
|
||||
socketSecurityLevel: socketSecurityLevel)
|
||||
#endif
|
||||
certValidated = !useSSL
|
||||
let timeout = request.timeoutInterval * 1_000_000
|
||||
@ -996,9 +1009,9 @@ open class WebSocket : NSObject, StreamDelegate, WebSocketClient, WSStreamDelega
|
||||
let payloadLen = (PayloadLenMask & baseAddress[1])
|
||||
var offset = 2
|
||||
if compressionState.supportsCompression && receivedOpcode != .continueFrame {
|
||||
compressionState.messageNeedsDecompression = (RSV1Mask & baseAddress[0]) > 0
|
||||
currentMessageNeedsDecompression = (RSV1Mask & baseAddress[0]) > 0
|
||||
}
|
||||
if (isMasked > 0 || (RSVMask & baseAddress[0]) > 0) && receivedOpcode != .pong && !compressionState.messageNeedsDecompression {
|
||||
if (isMasked > 0 || (RSVMask & baseAddress[0]) > 0) && receivedOpcode != .pong && !currentMessageNeedsDecompression {
|
||||
let errCode = CloseCode.protocolError.rawValue
|
||||
doDisconnect(WSError(type: .protocolError, message: "masked and rsv data is not currently supported", code: Int(errCode)))
|
||||
writeError(errCode)
|
||||
@ -1059,7 +1072,7 @@ open class WebSocket : NSObject, StreamDelegate, WebSocketClient, WSStreamDelega
|
||||
len -= UInt64(size)
|
||||
}
|
||||
let data: Data
|
||||
if compressionState.messageNeedsDecompression, let decompressor = compressionState.decompressor {
|
||||
if currentMessageNeedsDecompression, let decompressor = compressionState.decompressor {
|
||||
do {
|
||||
data = try decompressor.decompress(bytes: baseAddress+offset, count: Int(len), finish: isFin > 0)
|
||||
if isFin > 0 && compressionState.serverNoContextTakeover {
|
||||
@ -1250,7 +1263,10 @@ open class WebSocket : NSObject, StreamDelegate, WebSocketClient, WSStreamDelega
|
||||
}
|
||||
buffer[1] |= self.MaskMask
|
||||
let maskKey = UnsafeMutablePointer<UInt8>(buffer + offset)
|
||||
_ = SecRandomCopyBytes(kSecRandomDefault, Int(MemoryLayout<UInt32>.size), maskKey)
|
||||
guard SecRandomCopyBytes(kSecRandomDefault, Int(MemoryLayout<UInt32>.size), maskKey) == errSecSuccess else {
|
||||
self.doDisconnect(WSError(type: .osError, message: "unable to generate random bytes", code: 0))
|
||||
return
|
||||
}
|
||||
offset += MemoryLayout<UInt32>.size
|
||||
|
||||
for i in 0..<dataLength {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user