Compare commits
1 Commits
v7.6.5
...
feature/ma
Author | SHA1 | Date | |
---|---|---|---|
84361c16a9 |
@ -1,16 +1,13 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
All changes to this project will be documented in this file.
|
All changes to this project will be documented in this file.
|
||||||
|
|
||||||
## [7.6.5] - 2019-12-24
|
|
||||||
|
|
||||||
(cobra client) send a websocket ping every 30s to keep the connection opened
|
|
||||||
|
|
||||||
## [7.6.4] - 2019-12-22
|
## [7.6.4] - 2019-12-22
|
||||||
|
|
||||||
(client) error handling, quote url in error case when failing to parse one
|
(client) error handling, quote url in error case when failing to parse one
|
||||||
(ws) ws_cobra_publish: register callbacks before connecting
|
(ws) ws_cobra_publish: register callbacks before connecting
|
||||||
(doc) mention mbedtls in supported ssl server backend
|
(doc) mention mbedtls in supported ssl server backend
|
||||||
|
|
||||||
|
|
||||||
## [7.6.3] - 2019-12-20
|
## [7.6.3] - 2019-12-20
|
||||||
|
|
||||||
(tls) add a simple description of the TLS configuration routine for debugging
|
(tls) add a simple description of the TLS configuration routine for debugging
|
||||||
|
@ -24,7 +24,6 @@ namespace ix
|
|||||||
PublishTrackerCallback CobraConnection::_publishTrackerCallback = nullptr;
|
PublishTrackerCallback CobraConnection::_publishTrackerCallback = nullptr;
|
||||||
constexpr size_t CobraConnection::kQueueMaxSize;
|
constexpr size_t CobraConnection::kQueueMaxSize;
|
||||||
constexpr CobraConnection::MsgId CobraConnection::kInvalidMsgId;
|
constexpr CobraConnection::MsgId CobraConnection::kInvalidMsgId;
|
||||||
constexpr int CobraConnection::kPingIntervalSecs;
|
|
||||||
|
|
||||||
CobraConnection::CobraConnection() :
|
CobraConnection::CobraConnection() :
|
||||||
_webSocket(new WebSocket()),
|
_webSocket(new WebSocket()),
|
||||||
@ -229,10 +228,6 @@ namespace ix
|
|||||||
ss << "HTTP Status: " << msg->errorInfo.http_status << std::endl;
|
ss << "HTTP Status: " << msg->errorInfo.http_status << std::endl;
|
||||||
invokeErrorCallback(ss.str(), std::string());
|
invokeErrorCallback(ss.str(), std::string());
|
||||||
}
|
}
|
||||||
else if (msg->type == ix::WebSocketMessageType::Pong)
|
|
||||||
{
|
|
||||||
invokeEventCallback(ix::CobraConnection_EventType_Pong);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -265,7 +260,6 @@ namespace ix
|
|||||||
_webSocket->setUrl(url);
|
_webSocket->setUrl(url);
|
||||||
_webSocket->setPerMessageDeflateOptions(webSocketPerMessageDeflateOptions);
|
_webSocket->setPerMessageDeflateOptions(webSocketPerMessageDeflateOptions);
|
||||||
_webSocket->setTLSOptions(socketTLSOptions);
|
_webSocket->setTLSOptions(socketTLSOptions);
|
||||||
_webSocket->setPingInterval(kPingIntervalSecs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -30,8 +30,7 @@ namespace ix
|
|||||||
CobraConnection_EventType_Closed = 3,
|
CobraConnection_EventType_Closed = 3,
|
||||||
CobraConnection_EventType_Subscribed = 4,
|
CobraConnection_EventType_Subscribed = 4,
|
||||||
CobraConnection_EventType_UnSubscribed = 5,
|
CobraConnection_EventType_UnSubscribed = 5,
|
||||||
CobraConnection_EventType_Published = 6,
|
CobraConnection_EventType_Published = 6
|
||||||
CobraConnection_EventType_Pong = 7
|
|
||||||
};
|
};
|
||||||
|
|
||||||
enum CobraConnectionPublishMode
|
enum CobraConnectionPublishMode
|
||||||
@ -216,9 +215,6 @@ namespace ix
|
|||||||
|
|
||||||
// Each pdu sent should have an incremental unique id
|
// Each pdu sent should have an incremental unique id
|
||||||
std::atomic<uint64_t> _id;
|
std::atomic<uint64_t> _id;
|
||||||
|
|
||||||
// Frequency at which we send a websocket ping to the backing cobra connection
|
|
||||||
static constexpr int kPingIntervalSecs = 30;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ix
|
} // namespace ix
|
||||||
|
@ -65,10 +65,6 @@ namespace ix
|
|||||||
{
|
{
|
||||||
ss << "Published message " << msgId << " acked";
|
ss << "Published message " << msgId << " acked";
|
||||||
}
|
}
|
||||||
else if (eventType == ix::CobraConnection_EventType_Pong)
|
|
||||||
{
|
|
||||||
ss << "Received websocket pong";
|
|
||||||
}
|
|
||||||
|
|
||||||
ix::IXCoreLogger::Log(ss.str().c_str());
|
ix::IXCoreLogger::Log(ss.str().c_str());
|
||||||
});
|
});
|
||||||
|
@ -24,47 +24,9 @@
|
|||||||
|
|
||||||
#include <Security/SecureTransport.h>
|
#include <Security/SecureTransport.h>
|
||||||
|
|
||||||
namespace ix
|
namespace
|
||||||
{
|
{
|
||||||
SocketAppleSSL::SocketAppleSSL(const SocketTLSOptions& tlsOptions, int fd)
|
OSStatus read_from_socket(SSLConnectionRef connection, void* data, size_t* len)
|
||||||
: Socket(fd)
|
|
||||||
, _sslContext(nullptr)
|
|
||||||
, _tlsOptions(tlsOptions)
|
|
||||||
{
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
SocketAppleSSL::~SocketAppleSSL()
|
|
||||||
{
|
|
||||||
SocketAppleSSL::close();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string SocketAppleSSL::getSSLErrorDescription(OSStatus status)
|
|
||||||
{
|
|
||||||
std::string errMsg("Unknown SSL error.");
|
|
||||||
|
|
||||||
CFErrorRef error = CFErrorCreate(kCFAllocatorDefault, kCFErrorDomainOSStatus, status, NULL);
|
|
||||||
if (error)
|
|
||||||
{
|
|
||||||
CFStringRef message = CFErrorCopyDescription(error);
|
|
||||||
if (message)
|
|
||||||
{
|
|
||||||
char localBuffer[128];
|
|
||||||
Boolean success;
|
|
||||||
success = CFStringGetCString(message, localBuffer, 128, kCFStringEncodingUTF8);
|
|
||||||
if (success)
|
|
||||||
{
|
|
||||||
errMsg = localBuffer;
|
|
||||||
}
|
|
||||||
CFRelease(message);
|
|
||||||
}
|
|
||||||
CFRelease(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
return errMsg;
|
|
||||||
}
|
|
||||||
|
|
||||||
OSStatus SocketAppleSSL::readFromSocket(SSLConnectionRef connection, void* data, size_t* len)
|
|
||||||
{
|
{
|
||||||
int fd = (int) (long) connection;
|
int fd = (int) (long) connection;
|
||||||
if (fd < 0) return errSSLInternal;
|
if (fd < 0) return errSSLInternal;
|
||||||
@ -105,7 +67,7 @@ namespace ix
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
OSStatus SocketAppleSSL::writeToSocket(SSLConnectionRef connection, const void* data, size_t* len)
|
OSStatus write_to_socket(SSLConnectionRef connection, const void* data, size_t* len)
|
||||||
{
|
{
|
||||||
int fd = (int) (long) connection;
|
int fd = (int) (long) connection;
|
||||||
if (fd < 0) return errSSLInternal;
|
if (fd < 0) return errSSLInternal;
|
||||||
@ -143,6 +105,145 @@ namespace ix
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string getSSLErrorDescription(OSStatus status)
|
||||||
|
{
|
||||||
|
std::string errMsg("Unknown SSL error.");
|
||||||
|
|
||||||
|
CFErrorRef error = CFErrorCreate(kCFAllocatorDefault, kCFErrorDomainOSStatus, status, NULL);
|
||||||
|
if (error)
|
||||||
|
{
|
||||||
|
CFStringRef message = CFErrorCopyDescription(error);
|
||||||
|
if (message)
|
||||||
|
{
|
||||||
|
char localBuffer[128];
|
||||||
|
Boolean success;
|
||||||
|
success = CFStringGetCString(message, localBuffer, 128, kCFStringEncodingUTF8);
|
||||||
|
if (success)
|
||||||
|
{
|
||||||
|
errMsg = localBuffer;
|
||||||
|
}
|
||||||
|
CFRelease(message);
|
||||||
|
}
|
||||||
|
CFRelease(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return errMsg;
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef CURL_BUILD_IOS
|
||||||
|
OSStatus CopyIdentityFromPKCS12File(
|
||||||
|
const char *cPath,
|
||||||
|
const char *cPassword,
|
||||||
|
SecIdentityRef *out_cert_and_key)
|
||||||
|
{
|
||||||
|
OSStatus status = errSecItemNotFound;
|
||||||
|
CFURLRef pkcs_url = CFURLCreateFromFileSystemRepresentation(
|
||||||
|
NULL, (const UInt8 *)cPath, strlen(cPath), false);
|
||||||
|
CFStringRef password = cPassword ? CFStringCreateWithCString(NULL,
|
||||||
|
cPassword, kCFStringEncodingUTF8) : NULL;
|
||||||
|
CFDataRef pkcs_data = NULL;
|
||||||
|
|
||||||
|
/* We can import P12 files on iOS or OS X 10.7 or later: */
|
||||||
|
/* These constants are documented as having first appeared in 10.6 but they
|
||||||
|
raise linker errors when used on that cat for some reason. */
|
||||||
|
if (CFURLCreateDataAndPropertiesFromResource(
|
||||||
|
NULL, pkcs_url, &pkcs_data, NULL, NULL, &status)) {
|
||||||
|
CFArrayRef items = NULL;
|
||||||
|
|
||||||
|
/* On iOS SecPKCS12Import will never add the client certificate to the
|
||||||
|
* Keychain.
|
||||||
|
*
|
||||||
|
* It gives us back a SecIdentityRef that we can use directly. */
|
||||||
|
#if CURL_BUILD_IOS
|
||||||
|
const void *cKeys[] = {kSecImportExportPassphrase};
|
||||||
|
const void *cValues[] = {password};
|
||||||
|
CFDictionaryRef options = CFDictionaryCreate(NULL, cKeys, cValues,
|
||||||
|
password ? 1L : 0L, NULL, NULL);
|
||||||
|
|
||||||
|
if (options != NULL) {
|
||||||
|
status = SecPKCS12Import(pkcs_data, options, &items);
|
||||||
|
CFRelease(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* On macOS SecPKCS12Import will always add the client certificate to
|
||||||
|
* the Keychain.
|
||||||
|
*
|
||||||
|
* As this doesn't match iOS, and apps may not want to see their client
|
||||||
|
* certificate saved in the the user's keychain, we use SecItemImport
|
||||||
|
* with a NULL keychain to avoid importing it.
|
||||||
|
*
|
||||||
|
* This returns a SecCertificateRef from which we can construct a
|
||||||
|
* SecIdentityRef.
|
||||||
|
*/
|
||||||
|
#else
|
||||||
|
SecItemImportExportKeyParameters keyParams;
|
||||||
|
SecExternalFormat inputFormat = kSecFormatPKCS12;
|
||||||
|
SecExternalItemType inputType = kSecItemTypeCertificate;
|
||||||
|
|
||||||
|
memset(&keyParams, 0x00, sizeof(keyParams));
|
||||||
|
keyParams.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
|
||||||
|
keyParams.passphrase = password;
|
||||||
|
|
||||||
|
status = SecItemImport(pkcs_data, NULL, &inputFormat, &inputType,
|
||||||
|
0, &keyParams, NULL, &items);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Extract the SecIdentityRef */
|
||||||
|
if (status == errSecSuccess && items && CFArrayGetCount(items))
|
||||||
|
{
|
||||||
|
CFIndex i, count;
|
||||||
|
count = CFArrayGetCount(items);
|
||||||
|
|
||||||
|
for (i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
CFTypeRef item = (CFTypeRef) CFArrayGetValueAtIndex(items, i);
|
||||||
|
CFTypeID itemID = CFGetTypeID(item);
|
||||||
|
|
||||||
|
if (itemID == CFDictionaryGetTypeID())
|
||||||
|
{
|
||||||
|
CFTypeRef identity = (CFTypeRef) CFDictionaryGetValue(
|
||||||
|
(CFDictionaryRef) item,
|
||||||
|
kSecImportItemIdentity);
|
||||||
|
CFRetain(identity);
|
||||||
|
*out_cert_and_key = (SecIdentityRef) identity;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (itemID == SecCertificateGetTypeID())
|
||||||
|
{
|
||||||
|
status = SecIdentityCreateWithCertificate(NULL,
|
||||||
|
(SecCertificateRef) item,
|
||||||
|
out_cert_and_key);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (items) CFRelease(items);
|
||||||
|
CFRelease(pkcs_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (password) CFRelease(password);
|
||||||
|
CFRelease(pkcs_url);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} // anonymous namespace
|
||||||
|
|
||||||
|
namespace ix
|
||||||
|
{
|
||||||
|
SocketAppleSSL::SocketAppleSSL(const SocketTLSOptions& tlsOptions, int fd)
|
||||||
|
: Socket(fd)
|
||||||
|
, _sslContext(nullptr)
|
||||||
|
, _tlsOptions(tlsOptions)
|
||||||
|
{
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
SocketAppleSSL::~SocketAppleSSL()
|
||||||
|
{
|
||||||
|
SocketAppleSSL::close();
|
||||||
|
}
|
||||||
|
|
||||||
bool SocketAppleSSL::accept(std::string& errMsg)
|
bool SocketAppleSSL::accept(std::string& errMsg)
|
||||||
{
|
{
|
||||||
@ -150,6 +251,63 @@ namespace ix
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SocketAppleSSL::handleTLSOptions(std::string& errMsg)
|
||||||
|
{
|
||||||
|
SecIdentityRef cert_and_key = NULL;
|
||||||
|
|
||||||
|
const char* ssl_cert = _tlsOptions.certFile.c_str();
|
||||||
|
|
||||||
|
OSStatus err = CopyIdentityFromPKCS12File(ssl_cert, "foobar", &cert_and_key);
|
||||||
|
|
||||||
|
if (err == noErr && cert_and_key)
|
||||||
|
{
|
||||||
|
SecCertificateRef cert = NULL;
|
||||||
|
CFTypeRef certs_c[1];
|
||||||
|
CFArrayRef certs;
|
||||||
|
|
||||||
|
err = SecIdentityCopyCertificate(cert_and_key, &cert);
|
||||||
|
|
||||||
|
certs_c[0] = cert_and_key;
|
||||||
|
certs = CFArrayCreate(NULL, (const void **)certs_c, 1L,
|
||||||
|
&kCFTypeArrayCallBacks);
|
||||||
|
err = SSLSetCertificate(_sslContext, certs);
|
||||||
|
if (err != noErr)
|
||||||
|
{
|
||||||
|
errMsg = "SSLSetCertificate failed";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch(err) {
|
||||||
|
case errSecAuthFailed: case -25264: /* errSecPkcs12VerifyFailure */
|
||||||
|
errMsg = "SSL: Incorrect password for the certificate \"%s\" "
|
||||||
|
"and its private key."; // , ssl_cert);
|
||||||
|
break;
|
||||||
|
case -26275: /* errSecDecode */ case -25257: /* errSecUnknownFormat */
|
||||||
|
errMsg = "SSL: Couldn't make sense of the data in the "
|
||||||
|
"certificate \"%s\" and its private key.";
|
||||||
|
; // ssl_cert);
|
||||||
|
break;
|
||||||
|
case -25260: /* errSecPassphraseRequired */
|
||||||
|
errMsg = "SSL The certificate \"%s\" requires a password.";
|
||||||
|
// ssl_cert);
|
||||||
|
break;
|
||||||
|
case errSecItemNotFound:
|
||||||
|
errMsg = "SSL: Can't find the certificate \"%s\" and its private "
|
||||||
|
"key in the Keychain."; // , ssl_cert);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
errMsg = "SSL: Can't load the certificate \"%s\" and its private "
|
||||||
|
"key: OSStatus %d" ; // , ssl_cert, err);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// No wait support
|
// No wait support
|
||||||
bool SocketAppleSSL::connect(const std::string& host,
|
bool SocketAppleSSL::connect(const std::string& host,
|
||||||
int port,
|
int port,
|
||||||
@ -165,11 +323,13 @@ namespace ix
|
|||||||
|
|
||||||
_sslContext = SSLCreateContext(kCFAllocatorDefault, kSSLClientSide, kSSLStreamType);
|
_sslContext = SSLCreateContext(kCFAllocatorDefault, kSSLClientSide, kSSLStreamType);
|
||||||
|
|
||||||
SSLSetIOFuncs(_sslContext, SocketAppleSSL::readFromSocket, SocketAppleSSL::writeToSocket);
|
SSLSetIOFuncs(_sslContext, read_from_socket, write_to_socket);
|
||||||
SSLSetConnection(_sslContext, (SSLConnectionRef)(long) _sockfd);
|
SSLSetConnection(_sslContext, (SSLConnectionRef)(long) _sockfd);
|
||||||
SSLSetProtocolVersionMin(_sslContext, kTLSProtocol12);
|
SSLSetProtocolVersionMin(_sslContext, kTLSProtocol12);
|
||||||
SSLSetPeerDomainName(_sslContext, host.c_str(), host.size());
|
SSLSetPeerDomainName(_sslContext, host.c_str(), host.size());
|
||||||
|
|
||||||
|
if (!handleTLSOptions(errMsg)) return false; // FIXME not calling close()
|
||||||
|
|
||||||
if (_tlsOptions.isPeerVerifyDisabled())
|
if (_tlsOptions.isPeerVerifyDisabled())
|
||||||
{
|
{
|
||||||
Boolean option(1);
|
Boolean option(1);
|
||||||
|
@ -34,9 +34,7 @@ namespace ix
|
|||||||
virtual ssize_t recv(void* buffer, size_t length) final;
|
virtual ssize_t recv(void* buffer, size_t length) final;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static std::string getSSLErrorDescription(OSStatus status);
|
bool handleTLSOptions(std::string& errMsg);
|
||||||
static OSStatus writeToSocket(SSLConnectionRef connection, const void* data, size_t* len);
|
|
||||||
static OSStatus readFromSocket(SSLConnectionRef connection, void* data, size_t* len);
|
|
||||||
|
|
||||||
SSLContextRef _sslContext;
|
SSLContextRef _sslContext;
|
||||||
mutable std::mutex _mutex; // AppleSSL routines are not thread-safe
|
mutable std::mutex _mutex; // AppleSSL routines are not thread-safe
|
||||||
|
@ -6,4 +6,4 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#define IX_WEBSOCKET_VERSION "7.6.5"
|
#define IX_WEBSOCKET_VERSION "7.6.4"
|
||||||
|
@ -91,10 +91,6 @@ namespace ix
|
|||||||
spdlog::info("Published message id {} acked", msgId);
|
spdlog::info("Published message id {} acked", msgId);
|
||||||
messageAcked = true;
|
messageAcked = true;
|
||||||
}
|
}
|
||||||
else if (eventType == ix::CobraConnection_EventType_Pong)
|
|
||||||
{
|
|
||||||
spdlog::info("Received websocket pong");
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
conn.connect();
|
conn.connect();
|
||||||
|
@ -100,10 +100,6 @@ namespace ix
|
|||||||
{
|
{
|
||||||
spdlog::error("Published message hacked: {}", msgId);
|
spdlog::error("Published message hacked: {}", msgId);
|
||||||
}
|
}
|
||||||
else if (eventType == ix::CobraConnection_EventType_Pong)
|
|
||||||
{
|
|
||||||
spdlog::info("Received websocket pong");
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
|
@ -245,10 +245,6 @@ namespace ix
|
|||||||
{
|
{
|
||||||
spdlog::error("Published message hacked: {}", msgId);
|
spdlog::error("Published message hacked: {}", msgId);
|
||||||
}
|
}
|
||||||
else if (eventType == ix::CobraConnection_EventType_Pong)
|
|
||||||
{
|
|
||||||
spdlog::info("Received websocket pong");
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
|
@ -160,10 +160,6 @@ namespace ix
|
|||||||
{
|
{
|
||||||
spdlog::error("Published message hacked: {}", msgId);
|
spdlog::error("Published message hacked: {}", msgId);
|
||||||
}
|
}
|
||||||
else if (eventType == ix::CobraConnection_EventType_Pong)
|
|
||||||
{
|
|
||||||
spdlog::info("Received websocket pong");
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
|
Reference in New Issue
Block a user