Compare commits
	
		
			1 Commits
		
	
	
		
			v7.6.5
			...
			feature/ma
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 84361c16a9 | 
| @@ -1,16 +1,13 @@ | ||||
| # Changelog | ||||
| 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 | ||||
|  | ||||
| (client) error handling, quote url in error case when failing to parse one | ||||
| (ws) ws_cobra_publish: register callbacks before connecting | ||||
| (doc) mention mbedtls in supported ssl server backend | ||||
|  | ||||
|  | ||||
| ## [7.6.3] - 2019-12-20 | ||||
|  | ||||
| (tls) add a simple description of the TLS configuration routine for debugging | ||||
|   | ||||
| @@ -24,7 +24,6 @@ namespace ix | ||||
|     PublishTrackerCallback CobraConnection::_publishTrackerCallback = nullptr; | ||||
|     constexpr size_t CobraConnection::kQueueMaxSize; | ||||
|     constexpr CobraConnection::MsgId CobraConnection::kInvalidMsgId; | ||||
|     constexpr int CobraConnection::kPingIntervalSecs; | ||||
|  | ||||
|     CobraConnection::CobraConnection() : | ||||
|         _webSocket(new WebSocket()), | ||||
| @@ -229,10 +228,6 @@ namespace ix | ||||
|                     ss << "HTTP Status: "      << msg->errorInfo.http_status << std::endl; | ||||
|                     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->setPerMessageDeflateOptions(webSocketPerMessageDeflateOptions); | ||||
|         _webSocket->setTLSOptions(socketTLSOptions); | ||||
|         _webSocket->setPingInterval(kPingIntervalSecs); | ||||
|     } | ||||
|  | ||||
|     // | ||||
|   | ||||
| @@ -30,8 +30,7 @@ namespace ix | ||||
|         CobraConnection_EventType_Closed = 3, | ||||
|         CobraConnection_EventType_Subscribed = 4, | ||||
|         CobraConnection_EventType_UnSubscribed = 5, | ||||
|         CobraConnection_EventType_Published = 6, | ||||
|         CobraConnection_EventType_Pong = 7 | ||||
|         CobraConnection_EventType_Published = 6 | ||||
|     }; | ||||
|  | ||||
|     enum CobraConnectionPublishMode | ||||
| @@ -216,9 +215,6 @@ namespace ix | ||||
|  | ||||
|         // Each pdu sent should have an incremental unique 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 | ||||
|   | ||||
| @@ -65,10 +65,6 @@ namespace ix | ||||
|                 { | ||||
|                     ss << "Published message " << msgId << " acked"; | ||||
|                 } | ||||
|                 else if (eventType == ix::CobraConnection_EventType_Pong) | ||||
|                 { | ||||
|                     ss << "Received websocket pong"; | ||||
|                 } | ||||
|  | ||||
|                 ix::IXCoreLogger::Log(ss.str().c_str()); | ||||
|         }); | ||||
|   | ||||
| @@ -24,47 +24,9 @@ | ||||
|  | ||||
| #include <Security/SecureTransport.h> | ||||
|  | ||||
| namespace ix | ||||
| namespace | ||||
| { | ||||
|     SocketAppleSSL::SocketAppleSSL(const SocketTLSOptions& tlsOptions, int fd) | ||||
|         : 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) | ||||
|     OSStatus read_from_socket(SSLConnectionRef connection, void* data, size_t* len) | ||||
|     { | ||||
|         int fd = (int) (long) connection; | ||||
|         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; | ||||
|         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) | ||||
|     { | ||||
| @@ -150,6 +251,63 @@ namespace ix | ||||
|         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 | ||||
|     bool SocketAppleSSL::connect(const std::string& host, | ||||
|                                  int port, | ||||
| @@ -165,11 +323,13 @@ namespace ix | ||||
|  | ||||
|             _sslContext = SSLCreateContext(kCFAllocatorDefault, kSSLClientSide, kSSLStreamType); | ||||
|  | ||||
|             SSLSetIOFuncs(_sslContext, SocketAppleSSL::readFromSocket, SocketAppleSSL::writeToSocket); | ||||
|             SSLSetIOFuncs(_sslContext, read_from_socket, write_to_socket); | ||||
|             SSLSetConnection(_sslContext, (SSLConnectionRef)(long) _sockfd); | ||||
|             SSLSetProtocolVersionMin(_sslContext, kTLSProtocol12); | ||||
|             SSLSetPeerDomainName(_sslContext, host.c_str(), host.size()); | ||||
|  | ||||
|             if (!handleTLSOptions(errMsg)) return false; // FIXME not calling close() | ||||
|  | ||||
|             if (_tlsOptions.isPeerVerifyDisabled()) | ||||
|             { | ||||
|                 Boolean option(1); | ||||
|   | ||||
| @@ -34,9 +34,7 @@ namespace ix | ||||
|         virtual ssize_t recv(void* buffer, size_t length) final; | ||||
|  | ||||
|     private: | ||||
|         static std::string getSSLErrorDescription(OSStatus status); | ||||
|         static OSStatus writeToSocket(SSLConnectionRef connection, const void* data, size_t* len); | ||||
|         static OSStatus readFromSocket(SSLConnectionRef connection, void* data, size_t* len); | ||||
|         bool handleTLSOptions(std::string& errMsg); | ||||
|  | ||||
|         SSLContextRef _sslContext; | ||||
|         mutable std::mutex _mutex; // AppleSSL routines are not thread-safe | ||||
|   | ||||
| @@ -6,4 +6,4 @@ | ||||
|  | ||||
| #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); | ||||
|                 messageAcked = true; | ||||
|             } | ||||
|             else if (eventType == ix::CobraConnection_EventType_Pong) | ||||
|             { | ||||
|                 spdlog::info("Received websocket pong"); | ||||
|             } | ||||
|         }); | ||||
|  | ||||
|         conn.connect(); | ||||
|   | ||||
| @@ -100,10 +100,6 @@ namespace ix | ||||
|                 { | ||||
|                     spdlog::error("Published message hacked: {}", msgId); | ||||
|                 } | ||||
|                 else if (eventType == ix::CobraConnection_EventType_Pong) | ||||
|                 { | ||||
|                     spdlog::info("Received websocket pong"); | ||||
|                 } | ||||
|             }); | ||||
|  | ||||
|         while (true) | ||||
|   | ||||
| @@ -245,10 +245,6 @@ namespace ix | ||||
|             { | ||||
|                 spdlog::error("Published message hacked: {}", msgId); | ||||
|             } | ||||
|             else if (eventType == ix::CobraConnection_EventType_Pong) | ||||
|             { | ||||
|                 spdlog::info("Received websocket pong"); | ||||
|             } | ||||
|         }); | ||||
|  | ||||
|         while (true) | ||||
|   | ||||
| @@ -160,10 +160,6 @@ namespace ix | ||||
|             { | ||||
|                 spdlog::error("Published message hacked: {}", msgId); | ||||
|             } | ||||
|             else if (eventType == ix::CobraConnection_EventType_Pong) | ||||
|             { | ||||
|                 spdlog::info("Received websocket pong"); | ||||
|             } | ||||
|         }); | ||||
|  | ||||
|         while (true) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user