Compare commits
	
		
			18 Commits
		
	
	
		
			v7.5.4
			...
			feature/ma
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 84361c16a9 | ||
|  | d72e5e70f6 | ||
|  | e2c5f751bd | ||
|  | 351b86e266 | ||
|  | d0cbff4f4e | ||
|  | cbfc9b9f94 | ||
|  | ca816d801f | ||
|  | 2f354d31eb | ||
|  | 2c6c1edd37 | ||
|  | 9799e7e84b | ||
|  | 81be970679 | ||
|  | 52221906f6 | ||
|  | 3e786fe23a | ||
|  | de24aac7d5 | ||
|  | cd4b0ccf6f | ||
|  | 4e1888ac19 | ||
|  | 237ede56aa | ||
|  | ba3b1c1a0f | 
							
								
								
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1,3 +1,7 @@ | ||||
| build | ||||
| *.pyc | ||||
| venv | ||||
| ixsnake/ixsnake/.certs/ | ||||
| site/ | ||||
| ws/.certs/ | ||||
| ws/.srl | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| FROM alpine:edge as build | ||||
| FROM alpine:3.11 as build | ||||
|  | ||||
| RUN apk add --no-cache gcc g++ musl-dev linux-headers cmake openssl-dev  | ||||
| RUN apk add --no-cache make | ||||
| @@ -16,7 +16,7 @@ WORKDIR /opt | ||||
| USER app | ||||
| RUN [ "make", "ws_install" ] | ||||
|  | ||||
| FROM alpine:edge as runtime | ||||
| FROM alpine:3.11 as runtime | ||||
|  | ||||
| RUN apk add --no-cache libstdc++ | ||||
| RUN apk add --no-cache strace | ||||
|   | ||||
| @@ -1,5 +1,44 @@ | ||||
| # Changelog | ||||
| All notable changes to this project will be documented in this file. | ||||
| All changes to this project will be documented in this file. | ||||
|  | ||||
| ## [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 | ||||
|  | ||||
| ## [7.6.2] - 2019-12-20 | ||||
|  | ||||
| (mbedtls) correct support for using own certificate and private key | ||||
|  | ||||
| ## [7.6.1] - 2019-12-20 | ||||
|  | ||||
| (ws commands) in websocket proxy, disable automatic reconnections + in Dockerfile, use alpine 3.11 | ||||
|  | ||||
| ## [7.6.0] - 2019-12-19 | ||||
|  | ||||
| (cobra) Add TLS options to all cobra commands and classes. Add example to the doc. | ||||
|  | ||||
| ## [7.5.8] - 2019-12-18 | ||||
|  | ||||
| (cobra-to-sentry) capture application version from device field | ||||
|  | ||||
| ## [7.5.7] - 2019-12-18 | ||||
|  | ||||
| (tls) Experimental TLS server support with mbedtls (windows) + process cert tlsoption (client + server) | ||||
|  | ||||
| ## [7.5.6] - 2019-12-18 | ||||
|  | ||||
| (tls servers) Make it clear that apple ssl and mbedtls backends do not support SSL in server mode | ||||
|  | ||||
| ## [7.5.5] - 2019-12-17 | ||||
|  | ||||
| (tls options client) TLSOptions struct _validated member should be initialized to false | ||||
|  | ||||
| ## [7.5.4] - 2019-12-16 | ||||
|  | ||||
|   | ||||
							
								
								
									
										125
									
								
								docs/ws.md
									
									
									
									
									
								
							
							
						
						
									
										125
									
								
								docs/ws.md
									
									
									
									
									
								
							| @@ -243,6 +243,127 @@ Options: | ||||
|   --transfer-timeout INT      Transfer timeout | ||||
| ``` | ||||
|  | ||||
| ## Cobra Client | ||||
| ## Cobra client and server | ||||
|  | ||||
| [cobra](https://github.com/machinezone/cobra) is a real time messenging server. ws has a sub-command to interact with cobra. | ||||
| [cobra](https://github.com/machinezone/cobra) is a real time messenging server. ws has several sub-command to interact with cobra. There is also a minimal cobra compatible server named snake available. | ||||
|  | ||||
| Below are examples on running a snake server and clients with TLS enabled (the server only works with the OpenSSL and the Mbed TLS backend for now). | ||||
|  | ||||
| First, generate certificates. | ||||
|  | ||||
| ``` | ||||
| $ cd /path/to/IXWebSocket | ||||
| $ cd ixsnake/ixsnake | ||||
| $ bash ../../ws/generate_certs.sh | ||||
| Generating RSA private key, 2048 bit long modulus | ||||
| .....+++ | ||||
| .................+++ | ||||
| e is 65537 (0x10001) | ||||
| generated ./.certs/trusted-ca-key.pem | ||||
| generated ./.certs/trusted-ca-crt.pem | ||||
| Generating RSA private key, 2048 bit long modulus | ||||
| ..+++ | ||||
| .......................................+++ | ||||
| e is 65537 (0x10001) | ||||
| generated ./.certs/trusted-server-key.pem | ||||
| Signature ok | ||||
| subject=/O=machinezone/O=IXWebSocket/CN=trusted-server | ||||
| Getting CA Private Key | ||||
| generated ./.certs/trusted-server-crt.pem | ||||
| Generating RSA private key, 2048 bit long modulus | ||||
| ...................................+++ | ||||
| ..................................................+++ | ||||
| e is 65537 (0x10001) | ||||
| generated ./.certs/trusted-client-key.pem | ||||
| Signature ok | ||||
| subject=/O=machinezone/O=IXWebSocket/CN=trusted-client | ||||
| Getting CA Private Key | ||||
| generated ./.certs/trusted-client-crt.pem | ||||
| Generating RSA private key, 2048 bit long modulus | ||||
| ..............+++ | ||||
| .......................................+++ | ||||
| e is 65537 (0x10001) | ||||
| generated ./.certs/untrusted-ca-key.pem | ||||
| generated ./.certs/untrusted-ca-crt.pem | ||||
| Generating RSA private key, 2048 bit long modulus | ||||
| ..........+++ | ||||
| ................................................+++ | ||||
| e is 65537 (0x10001) | ||||
| generated ./.certs/untrusted-client-key.pem | ||||
| Signature ok | ||||
| subject=/O=machinezone/O=IXWebSocket/CN=untrusted-client | ||||
| Getting CA Private Key | ||||
| generated ./.certs/untrusted-client-crt.pem | ||||
| Generating RSA private key, 2048 bit long modulus | ||||
| .....................................................................................+++ | ||||
| ...........+++ | ||||
| e is 65537 (0x10001) | ||||
| generated ./.certs/selfsigned-client-key.pem | ||||
| Signature ok | ||||
| subject=/O=machinezone/O=IXWebSocket/CN=selfsigned-client | ||||
| Getting Private key | ||||
| generated ./.certs/selfsigned-client-crt.pem | ||||
| ``` | ||||
|  | ||||
| Now run the snake server. | ||||
|  | ||||
| ``` | ||||
| $ export certs=.certs | ||||
| $ ws snake --tls --port 8765 --cert-file ${certs}/trusted-server-crt.pem --key-file ${certs}/trusted-server-key.pem --ca-file ${certs}/trusted-ca-crt.pem | ||||
| { | ||||
|   "apps": { | ||||
|     "FC2F10139A2BAc53BB72D9db967b024f": { | ||||
|       "roles": { | ||||
|         "_sub": { | ||||
|           "secret": "66B1dA3ED5fA074EB5AE84Dd8CE3b5ba" | ||||
|         }, | ||||
|         "_pub": { | ||||
|           "secret": "1c04DB8fFe76A4EeFE3E318C72d771db" | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| redis host: 127.0.0.1 | ||||
| redis password: | ||||
| redis port: 6379 | ||||
| ``` | ||||
|  | ||||
| As a new connection comes in, such output should be printed | ||||
|  | ||||
| ``` | ||||
| [2019-12-19 20:27:19.724] [info] New connection | ||||
| id: 0 | ||||
| Uri: /v2?appkey=_health | ||||
| Headers: | ||||
| Connection: Upgrade | ||||
| Host: 127.0.0.1:8765 | ||||
| Sec-WebSocket-Extensions: permessage-deflate; server_max_window_bits=15; client_max_window_bits=15 | ||||
| Sec-WebSocket-Key: d747B0fE61Db73f7Eh47c0== | ||||
| Sec-WebSocket-Protocol: json | ||||
| Sec-WebSocket-Version: 13 | ||||
| Upgrade: websocket | ||||
| User-Agent: ixwebsocket/7.5.8 macos ssl/OpenSSL OpenSSL 1.0.2q  20 Nov 2018 zlib 1.2.11 | ||||
| ``` | ||||
|  | ||||
| To connect and publish a message, do: | ||||
|  | ||||
| ``` | ||||
| $ export certs=.certs | ||||
| $ cd /path/to/ws/folder | ||||
| $ ls cobraMetricsSample.json | ||||
| cobraMetricsSample.json | ||||
| $ ws cobra_publish --endpoint wss://127.0.0.1:8765 --appkey FC2F10139A2BAc53BB72D9db967b024f --rolename _pub --rolesecret 1c04DB8fFe76A4EeFE3E318C72d771db --channel foo --cert-file ${certs}/trusted-client-crt.pem --key-file ${certs}/trusted-client-key.pem --ca-file ${certs}/trusted-ca-crt.pem cobraMetricsSample.json | ||||
| [2019-12-19 20:46:42.656] [info] Publisher connected | ||||
| [2019-12-19 20:46:42.657] [info] Connection: Upgrade | ||||
| [2019-12-19 20:46:42.657] [info] Sec-WebSocket-Accept: rs99IFThoBrhSg+k8G4ixH9yaq4= | ||||
| [2019-12-19 20:46:42.657] [info] Sec-WebSocket-Extensions: permessage-deflate; server_max_window_bits=15; client_max_window_bits=15 | ||||
| [2019-12-19 20:46:42.657] [info] Server: ixwebsocket/7.5.8 macos ssl/OpenSSL OpenSSL 1.0.2q  20 Nov 2018 zlib 1.2.11 | ||||
| [2019-12-19 20:46:42.657] [info] Upgrade: websocket | ||||
| [2019-12-19 20:46:42.658] [info] Publisher authenticated | ||||
| [2019-12-19 20:46:42.658] [info] Published msg 3 | ||||
| [2019-12-19 20:46:42.659] [info] Published message id 3 acked | ||||
| ``` | ||||
|  | ||||
| To use OpenSSL on macOS, compile with `make ws_openssl`. First you will have to install OpenSSL libraries, which can be done with Homebrew. Use `make ws_mbedtls` accordingly to use MbedTLS. | ||||
|   | ||||
| @@ -7,6 +7,7 @@ | ||||
| #include "IXCobraConnection.h" | ||||
| #include <ixcrypto/IXHMac.h> | ||||
| #include <ixwebsocket/IXWebSocket.h> | ||||
| #include <ixwebsocket/IXSocketTLSOptions.h> | ||||
|  | ||||
| #include <algorithm> | ||||
| #include <stdexcept> | ||||
| @@ -244,7 +245,8 @@ namespace ix | ||||
|                                     const std::string& endpoint, | ||||
|                                     const std::string& rolename, | ||||
|                                     const std::string& rolesecret, | ||||
|                                     const WebSocketPerMessageDeflateOptions& webSocketPerMessageDeflateOptions) | ||||
|                                     const WebSocketPerMessageDeflateOptions& webSocketPerMessageDeflateOptions, | ||||
|                                     const SocketTLSOptions& socketTLSOptions) | ||||
|     { | ||||
|         _roleName = rolename; | ||||
|         _roleSecret = rolesecret; | ||||
| @@ -257,6 +259,7 @@ namespace ix | ||||
|         std::string url = ss.str(); | ||||
|         _webSocket->setUrl(url); | ||||
|         _webSocket->setPerMessageDeflateOptions(webSocketPerMessageDeflateOptions); | ||||
|         _webSocket->setTLSOptions(socketTLSOptions); | ||||
|     } | ||||
|  | ||||
|     // | ||||
|   | ||||
| @@ -20,6 +20,7 @@ | ||||
| namespace ix | ||||
| { | ||||
|     class WebSocket; | ||||
|     struct SocketTLSOptions; | ||||
|  | ||||
|     enum CobraConnectionEventType | ||||
|     { | ||||
| @@ -62,7 +63,8 @@ namespace ix | ||||
|                        const std::string& endpoint, | ||||
|                        const std::string& rolename, | ||||
|                        const std::string& rolesecret, | ||||
|                        const WebSocketPerMessageDeflateOptions& webSocketPerMessageDeflateOptions); | ||||
|                        const WebSocketPerMessageDeflateOptions& webSocketPerMessageDeflateOptions, | ||||
|                        const SocketTLSOptions& socketTLSOptions); | ||||
|  | ||||
|         /// Set the traffic tracker callback | ||||
|         static void setTrafficTrackerCallback(const TrafficTrackerCallback& callback); | ||||
|   | ||||
| @@ -5,6 +5,7 @@ | ||||
|  */ | ||||
|  | ||||
| #include "IXCobraMetricsPublisher.h" | ||||
| #include <ixwebsocket/IXSocketTLSOptions.h> | ||||
|  | ||||
| #include <algorithm> | ||||
| #include <stdexcept> | ||||
| @@ -31,14 +32,15 @@ namespace ix | ||||
|                                           const std::string& channel, | ||||
|                                           const std::string& rolename, | ||||
|                                           const std::string& rolesecret, | ||||
|                                           bool enablePerMessageDeflate) | ||||
|                                           bool enablePerMessageDeflate, | ||||
|                                           const SocketTLSOptions& socketTLSOptions) | ||||
|     { | ||||
|         // Configure the satori connection and start its publish background thread | ||||
|         _cobra_metrics_theaded_publisher.start(); | ||||
|  | ||||
|         _cobra_metrics_theaded_publisher.configure(appkey, endpoint, channel, | ||||
|                                                    rolename, rolesecret, | ||||
|                                                    enablePerMessageDeflate); | ||||
|                                                    enablePerMessageDeflate, socketTLSOptions); | ||||
|     } | ||||
|  | ||||
|     Json::Value& CobraMetricsPublisher::getGenericAttributes() | ||||
|   | ||||
| @@ -15,6 +15,8 @@ | ||||
|  | ||||
| namespace ix | ||||
| { | ||||
|     struct SocketTLSOptions; | ||||
|  | ||||
|     class CobraMetricsPublisher | ||||
|     { | ||||
|     public: | ||||
| @@ -43,7 +45,8 @@ namespace ix | ||||
|                        const std::string& channel, | ||||
|                        const std::string& rolename, | ||||
|                        const std::string& rolesecret, | ||||
|                        bool enablePerMessageDeflate); | ||||
|                        bool enablePerMessageDeflate, | ||||
|                        const SocketTLSOptions& socketTLSOptions); | ||||
|  | ||||
|         /// Setter for the list of blacklisted metrics ids. | ||||
|         /// That list is sorted internally for fast lookups | ||||
|   | ||||
| @@ -6,6 +6,7 @@ | ||||
|  | ||||
| #include "IXCobraMetricsThreadedPublisher.h" | ||||
| #include <ixwebsocket/IXSetThreadName.h> | ||||
| #include <ixwebsocket/IXSocketTLSOptions.h> | ||||
| #include <ixcore/utils/IXCoreLogger.h> | ||||
|  | ||||
| #include <algorithm> | ||||
| @@ -92,14 +93,17 @@ namespace ix | ||||
|                                                   const std::string& channel, | ||||
|                                                   const std::string& rolename, | ||||
|                                                   const std::string& rolesecret, | ||||
|                                                   bool enablePerMessageDeflate) | ||||
|                                                   bool enablePerMessageDeflate, | ||||
|                                                   const SocketTLSOptions& socketTLSOptions) | ||||
|     { | ||||
|         _channel = channel; | ||||
|  | ||||
|         ix::IXCoreLogger::Log(socketTLSOptions.getDescription().c_str()); | ||||
|  | ||||
|         ix::WebSocketPerMessageDeflateOptions webSocketPerMessageDeflateOptions(enablePerMessageDeflate); | ||||
|         _cobra_connection.configure(appkey, endpoint, | ||||
|                                     rolename, rolesecret, | ||||
|                                     webSocketPerMessageDeflateOptions); | ||||
|                                     webSocketPerMessageDeflateOptions, socketTLSOptions); | ||||
|     } | ||||
|  | ||||
|     void CobraMetricsThreadedPublisher::pushMessage(MessageKind messageKind) | ||||
|   | ||||
| @@ -18,6 +18,8 @@ | ||||
|  | ||||
| namespace ix | ||||
| { | ||||
|     struct SocketTLSOptions; | ||||
|  | ||||
|     class CobraMetricsThreadedPublisher | ||||
|     { | ||||
|     public: | ||||
| @@ -30,7 +32,8 @@ namespace ix | ||||
|                        const std::string& channel, | ||||
|                        const std::string& rolename, | ||||
|                        const std::string& rolesecret, | ||||
|                        bool enablePerMessageDeflate); | ||||
|                        bool enablePerMessageDeflate, | ||||
|                        const SocketTLSOptions& socketTLSOptions); | ||||
|  | ||||
|         /// Start the worker thread, used for background publishing | ||||
|         void start(); | ||||
|   | ||||
| @@ -182,7 +182,6 @@ namespace ix | ||||
|  | ||||
|         Json::Value extra; | ||||
|         extra["cobra_event"] = msg; | ||||
|         extra["cobra_event"] = msg; | ||||
|  | ||||
|         // Builtin tags | ||||
|         Json::Value gameTag; | ||||
| @@ -200,6 +199,11 @@ namespace ix | ||||
|         environmentTag.append(msg["device"]["environment"]); | ||||
|         tags.append(environmentTag); | ||||
|  | ||||
|         Json::Value clientVersionTag; | ||||
|         clientVersionTag.append("client_version"); | ||||
|         clientVersionTag.append(msg["device"]["app_version"]); | ||||
|         tags.append(clientVersionTag); | ||||
|  | ||||
|         payload["tags"] = tags; | ||||
|  | ||||
|         return _jsonWriter.write(payload); | ||||
|   | ||||
| @@ -9,6 +9,7 @@ | ||||
| #include <nlohmann/json.hpp> | ||||
| #include <string> | ||||
| #include <vector> | ||||
| #include <ixwebsocket/IXSocketTLSOptions.h> | ||||
|  | ||||
| namespace snake | ||||
| { | ||||
| @@ -26,6 +27,9 @@ namespace snake | ||||
|         // AppKeys | ||||
|         nlohmann::json apps; | ||||
|  | ||||
|         // TLS options | ||||
|         ix::SocketTLSOptions socketTLSOptions; | ||||
|  | ||||
|         // Misc | ||||
|         bool verbose; | ||||
|     }; | ||||
|   | ||||
| @@ -20,7 +20,7 @@ namespace snake | ||||
|         : _appConfig(appConfig) | ||||
|         , _server(appConfig.port, appConfig.hostname) | ||||
|     { | ||||
|         ; | ||||
|         _server.setTLSOptions(appConfig.socketTLSOptions); | ||||
|     } | ||||
|  | ||||
|     // | ||||
|   | ||||
| @@ -117,8 +117,7 @@ namespace | ||||
|             { | ||||
|                 char localBuffer[128]; | ||||
|                 Boolean success; | ||||
|                 success = | ||||
|                     CFStringGetCString(message, localBuffer, 128, kCFStringEncodingUTF8); | ||||
|                 success = CFStringGetCString(message, localBuffer, 128, kCFStringEncodingUTF8); | ||||
|                 if (success) | ||||
|                 { | ||||
|                     errMsg = localBuffer; | ||||
| @@ -131,6 +130,104 @@ namespace | ||||
|         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 | ||||
| @@ -148,6 +245,69 @@ namespace ix | ||||
|         SocketAppleSSL::close(); | ||||
|     } | ||||
|  | ||||
|     bool SocketAppleSSL::accept(std::string& errMsg) | ||||
|     { | ||||
|         errMsg = "TLS not supported yet in server mode with apple ssl backend"; | ||||
|         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, | ||||
| @@ -168,6 +328,8 @@ namespace ix | ||||
|             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); | ||||
|   | ||||
| @@ -21,6 +21,8 @@ namespace ix | ||||
|         SocketAppleSSL(const SocketTLSOptions& tlsOptions, int fd = -1); | ||||
|         ~SocketAppleSSL(); | ||||
|  | ||||
|         virtual bool accept(std::string& errMsg) final; | ||||
|  | ||||
|         virtual bool connect(const std::string& host, | ||||
|                              int port, | ||||
|                              std::string& errMsg, | ||||
| @@ -32,6 +34,8 @@ namespace ix | ||||
|         virtual ssize_t recv(void* buffer, size_t length) final; | ||||
|  | ||||
|     private: | ||||
|         bool handleTLSOptions(std::string& errMsg); | ||||
|  | ||||
|         SSLContextRef _sslContext; | ||||
|         mutable std::mutex _mutex; // AppleSSL routines are not thread-safe | ||||
|  | ||||
|   | ||||
| @@ -38,9 +38,10 @@ namespace ix | ||||
|         mbedtls_ctr_drbg_init(&_ctr_drbg); | ||||
|         mbedtls_entropy_init(&_entropy); | ||||
|         mbedtls_x509_crt_init(&_cacert); | ||||
|         mbedtls_x509_crt_init(&_cert); | ||||
|     } | ||||
|  | ||||
|     bool SocketMbedTLS::init(const std::string& host, std::string& errMsg) | ||||
|     bool SocketMbedTLS::init(const std::string& host, bool isClient, std::string& errMsg) | ||||
|     { | ||||
|         initMBedTLS(); | ||||
|         std::lock_guard<std::mutex> lock(_mutex); | ||||
| @@ -58,7 +59,7 @@ namespace ix | ||||
|         } | ||||
|  | ||||
|         if (mbedtls_ssl_config_defaults(&_conf, | ||||
|                                         MBEDTLS_SSL_IS_CLIENT, | ||||
|                                         (isClient) ? MBEDTLS_SSL_IS_CLIENT : MBEDTLS_SSL_IS_SERVER, | ||||
|                                         MBEDTLS_SSL_TRANSPORT_STREAM, | ||||
|                                         MBEDTLS_SSL_PRESET_DEFAULT) != 0) | ||||
|         { | ||||
| @@ -68,13 +69,27 @@ namespace ix | ||||
|  | ||||
|         mbedtls_ssl_conf_rng(&_conf, mbedtls_ctr_drbg_random, &_ctr_drbg); | ||||
|  | ||||
|         if (_tlsOptions.hasCertAndKey()) | ||||
|         { | ||||
|             if (mbedtls_x509_crt_parse_file(&_cert, _tlsOptions.certFile.c_str()) < 0) | ||||
|             { | ||||
|                 errMsg = "Cannot parse cert file '" + _tlsOptions.certFile + "'"; | ||||
|                 return false; | ||||
|             } | ||||
|             if (mbedtls_pk_parse_keyfile(&_pkey, _tlsOptions.keyFile.c_str(), "") < 0) | ||||
|             { | ||||
|                 errMsg = "Cannot parse key file '" + _tlsOptions.keyFile + "'"; | ||||
|                 return false; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (_tlsOptions.isPeerVerifyDisabled()) | ||||
|         { | ||||
|             mbedtls_ssl_conf_authmode(&_conf, MBEDTLS_SSL_VERIFY_NONE); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             mbedtls_ssl_conf_ca_chain(&_conf, &_cacert, NULL); | ||||
|             mbedtls_ssl_conf_authmode(&_conf, MBEDTLS_SSL_VERIFY_REQUIRED); | ||||
|  | ||||
|             // FIXME: should we call mbedtls_ssl_conf_verify ? | ||||
|  | ||||
| @@ -87,7 +102,13 @@ namespace ix | ||||
|                 errMsg = "Cannot parse CA file '" + _tlsOptions.caFile + "'"; | ||||
|                 return false; | ||||
|             } | ||||
|             mbedtls_ssl_conf_authmode(&_conf, MBEDTLS_SSL_VERIFY_REQUIRED); | ||||
|  | ||||
|             mbedtls_ssl_conf_ca_chain(&_conf, &_cacert, NULL); | ||||
|  | ||||
|             if (_tlsOptions.hasCertAndKey()) | ||||
|             { | ||||
|                 mbedtls_ssl_conf_own_cert(&_conf, &_cert, &_pkey); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (mbedtls_ssl_setup(&_ssl, &_conf) != 0) | ||||
| @@ -96,7 +117,7 @@ namespace ix | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         if (mbedtls_ssl_set_hostname(&_ssl, host.c_str()) != 0) | ||||
|         if (!host.empty() && mbedtls_ssl_set_hostname(&_ssl, host.c_str()) != 0) | ||||
|         { | ||||
|             errMsg = "SNI setup failed"; | ||||
|             return false; | ||||
| @@ -105,6 +126,50 @@ namespace ix | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     bool SocketMbedTLS::accept(std::string& errMsg) | ||||
|     { | ||||
|         bool isClient = false; | ||||
|         bool initialized = init(std::string(), isClient, errMsg); | ||||
|         if (!initialized) | ||||
|         { | ||||
|             close(); | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         mbedtls_ssl_set_bio(&_ssl, &_sockfd, mbedtls_net_send, mbedtls_net_recv, NULL); | ||||
|  | ||||
|         int res; | ||||
|         do | ||||
|         { | ||||
|             std::lock_guard<std::mutex> lock(_mutex); | ||||
|             res = mbedtls_ssl_handshake(&_ssl); | ||||
|         } while (res == MBEDTLS_ERR_SSL_WANT_READ || res == MBEDTLS_ERR_SSL_WANT_WRITE); | ||||
|  | ||||
|         if (res != 0) | ||||
|         { | ||||
|             char buf[256]; | ||||
|             mbedtls_strerror(res, buf, sizeof(buf)); | ||||
|  | ||||
|             errMsg = "error in handshake : "; | ||||
|             errMsg += buf; | ||||
|  | ||||
|             if (res == MBEDTLS_ERR_X509_CERT_VERIFY_FAILED) | ||||
|             { | ||||
|                 char verifyBuf[512]; | ||||
|                 uint32_t flags = mbedtls_ssl_get_verify_result(&_ssl); | ||||
|  | ||||
|                 mbedtls_x509_crt_verify_info(verifyBuf, sizeof(verifyBuf), "  ! ", flags); | ||||
|                 errMsg += " : "; | ||||
|                 errMsg += verifyBuf; | ||||
|             } | ||||
|  | ||||
|             close(); | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     bool SocketMbedTLS::connect(const std::string& host, | ||||
|                                 int port, | ||||
|                                 std::string& errMsg, | ||||
| @@ -116,7 +181,8 @@ namespace ix | ||||
|             if (_sockfd == -1) return false; | ||||
|         } | ||||
|  | ||||
|         bool initialized = init(host, errMsg); | ||||
|         bool isClient = true; | ||||
|         bool initialized = init(host, isClient, errMsg); | ||||
|         if (!initialized) | ||||
|         { | ||||
|             close(); | ||||
| @@ -156,6 +222,7 @@ namespace ix | ||||
|         mbedtls_ctr_drbg_free(&_ctr_drbg); | ||||
|         mbedtls_entropy_free(&_entropy); | ||||
|         mbedtls_x509_crt_free(&_cacert); | ||||
|         mbedtls_x509_crt_free(&_cert); | ||||
|  | ||||
|         Socket::close(); | ||||
|     } | ||||
|   | ||||
| @@ -26,6 +26,8 @@ namespace ix | ||||
|         SocketMbedTLS(const SocketTLSOptions& tlsOptions, int fd = -1); | ||||
|         ~SocketMbedTLS(); | ||||
|  | ||||
|         virtual bool accept(std::string& errMsg) final; | ||||
|  | ||||
|         virtual bool connect(const std::string& host, | ||||
|                              int port, | ||||
|                              std::string& errMsg, | ||||
| @@ -42,11 +44,13 @@ namespace ix | ||||
|         mbedtls_entropy_context _entropy; | ||||
|         mbedtls_ctr_drbg_context _ctr_drbg; | ||||
|         mbedtls_x509_crt _cacert; | ||||
|         mbedtls_x509_crt _cert; | ||||
|         mbedtls_pk_context _pkey; | ||||
|  | ||||
|         std::mutex _mutex; | ||||
|         SocketTLSOptions _tlsOptions; | ||||
|  | ||||
|         bool init(const std::string& host, std::string& errMsg); | ||||
|         bool init(const std::string& host, bool isClient, std::string& errMsg); | ||||
|         void initMBedTLS(); | ||||
|     }; | ||||
|  | ||||
|   | ||||
| @@ -8,6 +8,7 @@ | ||||
|  | ||||
| #include <assert.h> | ||||
| #include <fstream> | ||||
| #include <sstream> | ||||
|  | ||||
| namespace ix | ||||
| { | ||||
| @@ -71,4 +72,16 @@ namespace ix | ||||
|     { | ||||
|         return _errMsg; | ||||
|     } | ||||
|  | ||||
|     std::string SocketTLSOptions::getDescription() const | ||||
|     { | ||||
|         std::stringstream ss; | ||||
|         ss << "TLS Options:" << std::endl; | ||||
|         ss << "  certFile = " << certFile << std::endl; | ||||
|         ss << "  keyFile  = " << keyFile << std::endl; | ||||
|         ss << "  caFile   = " << caFile << std::endl; | ||||
|         ss << "  ciphers  = " << ciphers << std::endl; | ||||
|         ss << "  ciphers  = " << ciphers << std::endl; | ||||
|         return ss.str(); | ||||
|     } | ||||
| } // namespace ix | ||||
|   | ||||
| @@ -43,8 +43,10 @@ namespace ix | ||||
|  | ||||
|         const std::string& getErrorMsg() const; | ||||
|  | ||||
|         std::string getDescription() const; | ||||
|  | ||||
|     private: | ||||
|         mutable std::string _errMsg; | ||||
|         mutable bool _validated; | ||||
|         mutable bool _validated = false; | ||||
|     }; | ||||
| } // namespace ix | ||||
|   | ||||
| @@ -144,7 +144,9 @@ namespace ix | ||||
|  | ||||
|         if (!UrlParser::parse(url, protocol, host, path, query, port)) | ||||
|         { | ||||
|             return WebSocketInitResult(false, 0, std::string("Could not parse URL ") + url); | ||||
|             std::stringstream ss; | ||||
|             ss << "Could not parse url: '" << url << "'"; | ||||
|             return WebSocketInitResult(false, 0, ss.str()); | ||||
|         } | ||||
|  | ||||
|         std::string errorMsg; | ||||
|   | ||||
| @@ -6,4 +6,4 @@ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #define IX_WEBSOCKET_VERSION "7.5.4" | ||||
| #define IX_WEBSOCKET_VERSION "7.6.4" | ||||
|   | ||||
| @@ -34,10 +34,10 @@ namespace | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     class SatoriChat | ||||
|     class CobraChat | ||||
|     { | ||||
|     public: | ||||
|         SatoriChat(const std::string& user, | ||||
|         CobraChat(const std::string& user, | ||||
|                    const std::string& session, | ||||
|                    const std::string& endpoint); | ||||
|  | ||||
| @@ -72,9 +72,9 @@ namespace | ||||
|         std::mutex _logMutex; | ||||
|     }; | ||||
|  | ||||
|     SatoriChat::SatoriChat(const std::string& user, | ||||
|                            const std::string& session, | ||||
|                            const std::string& endpoint) | ||||
|     CobraChat::CobraChat(const std::string& user, | ||||
|                          const std::string& session, | ||||
|                          const std::string& endpoint) | ||||
|         : _user(user) | ||||
|         , _session(session) | ||||
|         , _endpoint(endpoint) | ||||
| @@ -83,34 +83,34 @@ namespace | ||||
|     { | ||||
|     } | ||||
|  | ||||
|     void SatoriChat::start() | ||||
|     void CobraChat::start() | ||||
|     { | ||||
|         _thread = std::thread(&SatoriChat::run, this); | ||||
|         _thread = std::thread(&CobraChat::run, this); | ||||
|     } | ||||
|  | ||||
|     void SatoriChat::stop() | ||||
|     void CobraChat::stop() | ||||
|     { | ||||
|         _stop = true; | ||||
|         _thread.join(); | ||||
|     } | ||||
|  | ||||
|     bool SatoriChat::isReady() const | ||||
|     bool CobraChat::isReady() const | ||||
|     { | ||||
|         return _connectedAndSubscribed; | ||||
|     } | ||||
|  | ||||
|     size_t SatoriChat::getReceivedMessagesCount() const | ||||
|     size_t CobraChat::getReceivedMessagesCount() const | ||||
|     { | ||||
|         return _receivedQueue.size(); | ||||
|     } | ||||
|  | ||||
|     bool SatoriChat::hasPendingMessages() const | ||||
|     bool CobraChat::hasPendingMessages() const | ||||
|     { | ||||
|         std::unique_lock<std::mutex> lock(_queue_mutex); | ||||
|         return !_publish_queue.empty(); | ||||
|     } | ||||
|  | ||||
|     Json::Value SatoriChat::popMessage() | ||||
|     Json::Value CobraChat::popMessage() | ||||
|     { | ||||
|         std::unique_lock<std::mutex> lock(_queue_mutex); | ||||
|         auto msg = _publish_queue.front(); | ||||
| @@ -121,7 +121,7 @@ namespace | ||||
|     // | ||||
|     // Callback to handle received messages, that are printed on the console | ||||
|     // | ||||
|     void SatoriChat::subscribe(const std::string& channel) | ||||
|     void CobraChat::subscribe(const std::string& channel) | ||||
|     { | ||||
|         std::string filter; | ||||
|         _conn.subscribe(channel, filter, [this](const Json::Value& msg) { | ||||
| @@ -151,7 +151,7 @@ namespace | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     void SatoriChat::sendMessage(const std::string& text) | ||||
|     void CobraChat::sendMessage(const std::string& text) | ||||
|     { | ||||
|         Json::Value msg; | ||||
|         msg["user"] = _user; | ||||
| @@ -166,16 +166,21 @@ namespace | ||||
|     // Do satori communication on a background thread, where we can have | ||||
|     // something like an event loop that publish, poll and receive data | ||||
|     // | ||||
|     void SatoriChat::run() | ||||
|     void CobraChat::run() | ||||
|     { | ||||
|         // "chat" conf | ||||
|         std::string appkey("FC2F10139A2BAc53BB72D9db967b024f"); | ||||
|         std::string channel = _session; | ||||
|         std::string role = "_sub"; | ||||
|         std::string secret = "66B1dA3ED5fA074EB5AE84Dd8CE3b5ba"; | ||||
|         SocketTLSOptions socketTLSOptions; | ||||
|  | ||||
|         _conn.configure( | ||||
|             appkey, _endpoint, role, secret, ix::WebSocketPerMessageDeflateOptions(true)); | ||||
|         _conn.configure(appkey, | ||||
|                         _endpoint, | ||||
|                         role, | ||||
|                         secret, | ||||
|                         ix::WebSocketPerMessageDeflateOptions(true), | ||||
|                         socketTLSOptions); | ||||
|         _conn.connect(); | ||||
|  | ||||
|         _conn.setEventCallback([this, channel](ix::CobraConnectionEventType eventType, | ||||
| @@ -280,8 +285,8 @@ TEST_CASE("Cobra_chat", "[cobra_chat]") | ||||
|         ss << "ws://localhost:" << port; | ||||
|         std::string endpoint = ss.str(); | ||||
|  | ||||
|         SatoriChat chatA("jean", session, endpoint); | ||||
|         SatoriChat chatB("paul", session, endpoint); | ||||
|         CobraChat chatA("jean", session, endpoint); | ||||
|         CobraChat chatB("paul", session, endpoint); | ||||
|  | ||||
|         chatA.start(); | ||||
|         chatB.start(); | ||||
|   | ||||
| @@ -62,11 +62,14 @@ namespace | ||||
|         gMessageCount = 0; | ||||
|  | ||||
|         ix::CobraConnection conn; | ||||
|         SocketTLSOptions socketTLSOptions; | ||||
|  | ||||
|         conn.configure(APPKEY, | ||||
|                        endpoint, | ||||
|                        SUBSCRIBER_ROLE, | ||||
|                        SUBSCRIBER_SECRET, | ||||
|                        ix::WebSocketPerMessageDeflateOptions(true)); | ||||
|                        ix::WebSocketPerMessageDeflateOptions(true), | ||||
|                        socketTLSOptions); | ||||
|         conn.connect(); | ||||
|  | ||||
|         conn.setEventCallback([&conn](ix::CobraConnectionEventType eventType, | ||||
| @@ -202,9 +205,15 @@ TEST_CASE("Cobra_Metrics_Publisher", "[cobra]") | ||||
|  | ||||
|     ix::CobraMetricsPublisher cobraMetricsPublisher; | ||||
|  | ||||
|     SocketTLSOptions socketTLSOptions; | ||||
|     bool perMessageDeflate = true; | ||||
|     cobraMetricsPublisher.configure( | ||||
|         APPKEY, endpoint, CHANNEL, PUBLISHER_ROLE, PUBLISHER_SECRET, perMessageDeflate); | ||||
|     cobraMetricsPublisher.configure(APPKEY, | ||||
|                                     endpoint, | ||||
|                                     CHANNEL, | ||||
|                                     PUBLISHER_ROLE, | ||||
|                                     PUBLISHER_SECRET, | ||||
|                                     perMessageDeflate, | ||||
|                                     socketTLSOptions); | ||||
|     cobraMetricsPublisher.setSession(uuid4()); | ||||
|     cobraMetricsPublisher.enable(true); // disabled by default, needs to be enabled to be active | ||||
|  | ||||
|   | ||||
| @@ -53,7 +53,7 @@ done | ||||
| # Start a receiver | ||||
| mkdir -p /tmp/ws_test/receive | ||||
| cd /tmp/ws_test/receive | ||||
| ws receive "${protocol}127.0.0.1:8090" ${delay} --pidfile /tmp/ws_test/pidfile.receive ${server_tls} & | ||||
| ws receive "${protocol}127.0.0.1:8090" ${delay} --pidfile /tmp/ws_test/pidfile.receive ${client_tls} & | ||||
|  | ||||
| mkdir -p /tmp/ws_test/send | ||||
| cd /tmp/ws_test/send | ||||
|   | ||||
							
								
								
									
										62
									
								
								ws/ws.cpp
									
									
									
									
									
								
							
							
						
						
									
										62
									
								
								ws/ws.cpp
									
									
									
									
									
								
							| @@ -218,6 +218,7 @@ int main(int argc, char** argv) | ||||
|     cobraSubscribeApp->add_option("--pidfile", pidfile, "Pid file"); | ||||
|     cobraSubscribeApp->add_option("--filter", filter, "Stream SQL Filter"); | ||||
|     cobraSubscribeApp->add_flag("-q", quiet, "Quiet / only display stats"); | ||||
|     addTLSOptions(cobraSubscribeApp); | ||||
|  | ||||
|     CLI::App* cobraPublish = app.add_subcommand("cobra_publish", "Cobra publisher"); | ||||
|     cobraPublish->add_option("--appkey", appkey, "Appkey")->required(); | ||||
| @@ -229,6 +230,7 @@ int main(int argc, char** argv) | ||||
|     cobraPublish->add_option("path", path, "Path to the file to send") | ||||
|         ->required() | ||||
|         ->check(CLI::ExistingPath); | ||||
|     addTLSOptions(cobraPublish); | ||||
|  | ||||
|     CLI::App* cobraMetricsPublish = | ||||
|         app.add_subcommand("cobra_metrics_publish", "Cobra metrics publisher"); | ||||
| @@ -242,6 +244,7 @@ int main(int argc, char** argv) | ||||
|         ->required() | ||||
|         ->check(CLI::ExistingPath); | ||||
|     cobraMetricsPublish->add_flag("--stress", stress, "Stress mode"); | ||||
|     addTLSOptions(cobraMetricsPublish); | ||||
|  | ||||
|     CLI::App* cobra2statsd = app.add_subcommand("cobra_to_statsd", "Cobra metrics to statsd"); | ||||
|     cobra2statsd->add_option("--appkey", appkey, "Appkey"); | ||||
| @@ -256,6 +259,7 @@ int main(int argc, char** argv) | ||||
|     cobra2statsd->add_flag("-v", verbose, "Verbose"); | ||||
|     cobra2statsd->add_option("--pidfile", pidfile, "Pid file"); | ||||
|     cobra2statsd->add_option("--filter", filter, "Stream SQL Filter"); | ||||
|     addTLSOptions(cobra2statsd); | ||||
|  | ||||
|     CLI::App* cobra2sentry = app.add_subcommand("cobra_to_sentry", "Cobra metrics to sentry"); | ||||
|     cobra2sentry->add_option("--appkey", appkey, "Appkey")->required(); | ||||
| @@ -269,6 +273,7 @@ int main(int argc, char** argv) | ||||
|     cobra2sentry->add_flag("-s", strict, "Strict mode. Error out when sending to sentry fails"); | ||||
|     cobra2sentry->add_option("--pidfile", pidfile, "Pid file"); | ||||
|     cobra2sentry->add_option("--filter", filter, "Stream SQL Filter"); | ||||
|     addTLSOptions(cobra2sentry); | ||||
|  | ||||
|     CLI::App* cobra2redisApp = | ||||
|         app.add_subcommand("cobra_metrics_to_redis", "Cobra metrics to redis"); | ||||
| @@ -282,17 +287,19 @@ int main(int argc, char** argv) | ||||
|     cobra2redisApp->add_option("--hostname", hostname, "Redis hostname"); | ||||
|     cobra2redisApp->add_option("--port", redisPort, "Redis port"); | ||||
|     cobra2redisApp->add_flag("-q", quiet, "Quiet / only display stats"); | ||||
|     addTLSOptions(cobra2redisApp); | ||||
|  | ||||
|     CLI::App* runApp = app.add_subcommand("snake", "Snake server"); | ||||
|     runApp->add_option("--port", port, "Connection url"); | ||||
|     runApp->add_option("--host", hostname, "Hostname"); | ||||
|     runApp->add_option("--pidfile", pidfile, "Pid file"); | ||||
|     runApp->add_option("--redis_hosts", redisHosts, "Redis hosts"); | ||||
|     runApp->add_option("--redis_port", redisPort, "Redis hosts"); | ||||
|     runApp->add_option("--redis_password", redisPassword, "Redis password"); | ||||
|     runApp->add_option("--apps_config_path", appsConfigPath, "Path to auth data") | ||||
|     CLI::App* snakeApp = app.add_subcommand("snake", "Snake server"); | ||||
|     snakeApp->add_option("--port", port, "Connection url"); | ||||
|     snakeApp->add_option("--host", hostname, "Hostname"); | ||||
|     snakeApp->add_option("--pidfile", pidfile, "Pid file"); | ||||
|     snakeApp->add_option("--redis_hosts", redisHosts, "Redis hosts"); | ||||
|     snakeApp->add_option("--redis_port", redisPort, "Redis hosts"); | ||||
|     snakeApp->add_option("--redis_password", redisPassword, "Redis password"); | ||||
|     snakeApp->add_option("--apps_config_path", appsConfigPath, "Path to auth data") | ||||
|         ->check(CLI::ExistingPath); | ||||
|     runApp->add_flag("-v", verbose, "Verbose"); | ||||
|     snakeApp->add_flag("-v", verbose, "Verbose"); | ||||
|     addTLSOptions(snakeApp); | ||||
|  | ||||
|     CLI::App* httpServerApp = app.add_subcommand("httpd", "HTTP server"); | ||||
|     httpServerApp->add_option("--port", port, "Port"); | ||||
| @@ -314,6 +321,7 @@ int main(int argc, char** argv) | ||||
|     proxyServerApp->add_option("--host", hostname, "Hostname"); | ||||
|     proxyServerApp->add_option("--remote_host", remoteHost, "Remote Hostname"); | ||||
|     proxyServerApp->add_flag("-v", verbose, "Verbose"); | ||||
|     addTLSOptions(proxyServerApp); | ||||
|  | ||||
|     CLI::App* minidumpApp = app.add_subcommand("upload_minidump", "Upload a minidump to sentry"); | ||||
|     minidumpApp->add_option("--minidump", minidump, "Minidump path")->check(CLI::ExistingPath); | ||||
| @@ -408,16 +416,17 @@ int main(int argc, char** argv) | ||||
|     else if (app.got_subcommand("cobra_subscribe")) | ||||
|     { | ||||
|         ret = ix::ws_cobra_subscribe_main( | ||||
|             appkey, endpoint, rolename, rolesecret, channel, filter, quiet); | ||||
|             appkey, endpoint, rolename, rolesecret, channel, filter, quiet, tlsOptions); | ||||
|     } | ||||
|     else if (app.got_subcommand("cobra_publish")) | ||||
|     { | ||||
|         ret = ix::ws_cobra_publish_main(appkey, endpoint, rolename, rolesecret, channel, path); | ||||
|         ret = ix::ws_cobra_publish_main( | ||||
|             appkey, endpoint, rolename, rolesecret, channel, path, tlsOptions); | ||||
|     } | ||||
|     else if (app.got_subcommand("cobra_metrics_publish")) | ||||
|     { | ||||
|         ret = ix::ws_cobra_metrics_publish_main( | ||||
|             appkey, endpoint, rolename, rolesecret, channel, path, stress); | ||||
|             appkey, endpoint, rolename, rolesecret, channel, path, stress, tlsOptions); | ||||
|     } | ||||
|     else if (app.got_subcommand("cobra_to_statsd")) | ||||
|     { | ||||
| @@ -431,22 +440,39 @@ int main(int argc, char** argv) | ||||
|                                           statsdPort, | ||||
|                                           prefix, | ||||
|                                           fields, | ||||
|                                           verbose); | ||||
|                                           verbose, | ||||
|                                           tlsOptions); | ||||
|     } | ||||
|     else if (app.got_subcommand("cobra_to_sentry")) | ||||
|     { | ||||
|         ret = ix::ws_cobra_to_sentry_main( | ||||
|             appkey, endpoint, rolename, rolesecret, channel, filter, dsn, verbose, strict, jobs); | ||||
|         ret = ix::ws_cobra_to_sentry_main(appkey, | ||||
|                                           endpoint, | ||||
|                                           rolename, | ||||
|                                           rolesecret, | ||||
|                                           channel, | ||||
|                                           filter, | ||||
|                                           dsn, | ||||
|                                           verbose, | ||||
|                                           strict, | ||||
|                                           jobs, | ||||
|                                           tlsOptions); | ||||
|     } | ||||
|     else if (app.got_subcommand("cobra_metrics_to_redis")) | ||||
|     { | ||||
|         ret = ix::ws_cobra_metrics_to_redis( | ||||
|             appkey, endpoint, rolename, rolesecret, channel, filter, hostname, redisPort); | ||||
|         ret = ix::ws_cobra_metrics_to_redis(appkey, | ||||
|                                             endpoint, | ||||
|                                             rolename, | ||||
|                                             rolesecret, | ||||
|                                             channel, | ||||
|                                             filter, | ||||
|                                             hostname, | ||||
|                                             redisPort, | ||||
|                                             tlsOptions); | ||||
|     } | ||||
|     else if (app.got_subcommand("snake")) | ||||
|     { | ||||
|         ret = ix::ws_snake_main( | ||||
|             port, hostname, redisHosts, redisPort, redisPassword, verbose, appsConfigPath); | ||||
|             port, hostname, redisHosts, redisPort, redisPassword, verbose, appsConfigPath, tlsOptions); | ||||
|     } | ||||
|     else if (app.got_subcommand("httpd")) | ||||
|     { | ||||
|   | ||||
							
								
								
									
										21
									
								
								ws/ws.h
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								ws/ws.h
									
									
									
									
									
								
							| @@ -76,14 +76,16 @@ namespace ix | ||||
|                                 const std::string& rolesecret, | ||||
|                                 const std::string& channel, | ||||
|                                 const std::string& filter, | ||||
|                                 bool quiet); | ||||
|                                 bool quiet, | ||||
|                                 const ix::SocketTLSOptions& tlsOptions); | ||||
|  | ||||
|     int ws_cobra_publish_main(const std::string& appkey, | ||||
|                               const std::string& endpoint, | ||||
|                               const std::string& rolename, | ||||
|                               const std::string& rolesecret, | ||||
|                               const std::string& channel, | ||||
|                               const std::string& path); | ||||
|                               const std::string& path, | ||||
|                               const ix::SocketTLSOptions& tlsOptions); | ||||
|  | ||||
|     int ws_cobra_metrics_publish_main(const std::string& appkey, | ||||
|                                       const std::string& endpoint, | ||||
| @@ -91,7 +93,8 @@ namespace ix | ||||
|                                       const std::string& rolesecret, | ||||
|                                       const std::string& channel, | ||||
|                                       const std::string& path, | ||||
|                                       bool stress); | ||||
|                                       bool stress, | ||||
|                                       const ix::SocketTLSOptions& tlsOptions); | ||||
|  | ||||
|     int ws_cobra_to_statsd_main(const std::string& appkey, | ||||
|                                 const std::string& endpoint, | ||||
| @@ -103,7 +106,8 @@ namespace ix | ||||
|                                 int port, | ||||
|                                 const std::string& prefix, | ||||
|                                 const std::string& fields, | ||||
|                                 bool verbose); | ||||
|                                 bool verbose, | ||||
|                                 const ix::SocketTLSOptions& tlsOptions); | ||||
|  | ||||
|     int ws_cobra_to_sentry_main(const std::string& appkey, | ||||
|                                 const std::string& endpoint, | ||||
| @@ -114,7 +118,8 @@ namespace ix | ||||
|                                 const std::string& dsn, | ||||
|                                 bool verbose, | ||||
|                                 bool strict, | ||||
|                                 int jobs); | ||||
|                                 int jobs, | ||||
|                                 const ix::SocketTLSOptions& tlsOptions); | ||||
|  | ||||
|     int ws_cobra_metrics_to_redis(const std::string& appkey, | ||||
|                                   const std::string& endpoint, | ||||
| @@ -123,7 +128,8 @@ namespace ix | ||||
|                                   const std::string& channel, | ||||
|                                   const std::string& filter, | ||||
|                                   const std::string& host, | ||||
|                                   int port); | ||||
|                                   int port, | ||||
|                                   const ix::SocketTLSOptions& tlsOptions); | ||||
|  | ||||
|     int ws_snake_main(int port, | ||||
|                       const std::string& hostname, | ||||
| @@ -131,7 +137,8 @@ namespace ix | ||||
|                       int redisPort, | ||||
|                       const std::string& redisPassword, | ||||
|                       bool verbose, | ||||
|                       const std::string& appsConfigPath); | ||||
|                       const std::string& appsConfigPath, | ||||
|                       const ix::SocketTLSOptions& tlsOptions); | ||||
|  | ||||
|     int ws_httpd_main(int port, | ||||
|                       const std::string& hostname, | ||||
|   | ||||
| @@ -22,7 +22,8 @@ namespace ix | ||||
|                                       const std::string& rolesecret, | ||||
|                                       const std::string& channel, | ||||
|                                       const std::string& path, | ||||
|                                       bool stress) | ||||
|                                       bool stress, | ||||
|                                       const ix::SocketTLSOptions& tlsOptions) | ||||
|     { | ||||
|         std::atomic<int> sentMessages(0); | ||||
|         std::atomic<int> ackedMessages(0); | ||||
| @@ -37,7 +38,7 @@ namespace ix | ||||
|  | ||||
|         bool enablePerMessageDeflate = true; | ||||
|         cobraMetricsPublisher.configure( | ||||
|             appkey, endpoint, channel, rolename, rolesecret, enablePerMessageDeflate); | ||||
|             appkey, endpoint, channel, rolename, rolesecret, enablePerMessageDeflate, tlsOptions); | ||||
|  | ||||
|         while (!cobraMetricsPublisher.isAuthenticated()) | ||||
|             ; | ||||
|   | ||||
| @@ -25,11 +25,16 @@ namespace ix | ||||
|                                   const std::string& channel, | ||||
|                                   const std::string& filter, | ||||
|                                   const std::string& host, | ||||
|                                   int port) | ||||
|                                   int port, | ||||
|                                   const ix::SocketTLSOptions& tlsOptions) | ||||
|     { | ||||
|         ix::CobraConnection conn; | ||||
|         conn.configure( | ||||
|             appkey, endpoint, rolename, rolesecret, ix::WebSocketPerMessageDeflateOptions(true)); | ||||
|         conn.configure(appkey, | ||||
|                        endpoint, | ||||
|                        rolename, | ||||
|                        rolesecret, | ||||
|                        ix::WebSocketPerMessageDeflateOptions(true), | ||||
|                        tlsOptions); | ||||
|         conn.connect(); | ||||
|  | ||||
|         // Display incoming messages | ||||
|   | ||||
| @@ -22,7 +22,8 @@ namespace ix | ||||
|                               const std::string& rolename, | ||||
|                               const std::string& rolesecret, | ||||
|                               const std::string& channel, | ||||
|                               const std::string& path) | ||||
|                               const std::string& path, | ||||
|                               const ix::SocketTLSOptions& tlsOptions) | ||||
|     { | ||||
|         std::ifstream f(path); | ||||
|         std::string str((std::istreambuf_iterator<char>(f)), std::istreambuf_iterator<char>()); | ||||
| @@ -36,9 +37,12 @@ namespace ix | ||||
|         } | ||||
|  | ||||
|         ix::CobraConnection conn; | ||||
|         conn.configure( | ||||
|             appkey, endpoint, rolename, rolesecret, ix::WebSocketPerMessageDeflateOptions(true)); | ||||
|         conn.connect(); | ||||
|         conn.configure(appkey, | ||||
|                        endpoint, | ||||
|                        rolename, | ||||
|                        rolesecret, | ||||
|                        ix::WebSocketPerMessageDeflateOptions(true), | ||||
|                        tlsOptions); | ||||
|  | ||||
|         // Display incoming messages | ||||
|         std::atomic<bool> authenticated(false); | ||||
| @@ -89,6 +93,8 @@ namespace ix | ||||
|             } | ||||
|         }); | ||||
|  | ||||
|         conn.connect(); | ||||
|  | ||||
|         while (!authenticated) | ||||
|             ; | ||||
|         while (!messageAcked) | ||||
|   | ||||
| @@ -20,11 +20,16 @@ namespace ix | ||||
|                                 const std::string& rolesecret, | ||||
|                                 const std::string& channel, | ||||
|                                 const std::string& filter, | ||||
|                                 bool quiet) | ||||
|                                 bool quiet, | ||||
|                                 const ix::SocketTLSOptions& tlsOptions) | ||||
|     { | ||||
|         ix::CobraConnection conn; | ||||
|         conn.configure( | ||||
|             appkey, endpoint, rolename, rolesecret, ix::WebSocketPerMessageDeflateOptions(true)); | ||||
|         conn.configure(appkey, | ||||
|                        endpoint, | ||||
|                        rolename, | ||||
|                        rolesecret, | ||||
|                        ix::WebSocketPerMessageDeflateOptions(true), | ||||
|                        tlsOptions); | ||||
|         conn.connect(); | ||||
|  | ||||
|         Json::FastWriter jsonWriter; | ||||
|   | ||||
| @@ -28,11 +28,16 @@ namespace ix | ||||
|                                 const std::string& dsn, | ||||
|                                 bool verbose, | ||||
|                                 bool strict, | ||||
|                                 int jobs) | ||||
|                                 int jobs, | ||||
|                                 const ix::SocketTLSOptions& tlsOptions) | ||||
|     { | ||||
|         ix::CobraConnection conn; | ||||
|         conn.configure( | ||||
|             appkey, endpoint, rolename, rolesecret, ix::WebSocketPerMessageDeflateOptions(true)); | ||||
|         conn.configure(appkey, | ||||
|                        endpoint, | ||||
|                        rolename, | ||||
|                        rolesecret, | ||||
|                        ix::WebSocketPerMessageDeflateOptions(true), | ||||
|                        tlsOptions); | ||||
|         conn.connect(); | ||||
|  | ||||
|         Json::FastWriter jsonWriter; | ||||
| @@ -132,11 +137,13 @@ namespace ix | ||||
|                         { | ||||
|                             seconds = 30; | ||||
|                             spdlog::warn("Error parsing Retry-After header. " | ||||
|                                          "Using {} for the sleep duration", seconds); | ||||
|                                          "Using {} for the sleep duration", | ||||
|                                          seconds); | ||||
|                         } | ||||
|  | ||||
|                         spdlog::warn("Error 429 - Too Many Requests. ws will sleep " | ||||
|                                      "and retry after {} seconds", retryAfter); | ||||
|                                      "and retry after {} seconds", | ||||
|                                      retryAfter); | ||||
|  | ||||
|                         throttled = true; | ||||
|                         auto duration = std::chrono::seconds(seconds); | ||||
|   | ||||
| @@ -66,11 +66,16 @@ namespace ix | ||||
|                                 int port, | ||||
|                                 const std::string& prefix, | ||||
|                                 const std::string& fields, | ||||
|                                 bool verbose) | ||||
|                                 bool verbose, | ||||
|                                 const ix::SocketTLSOptions& tlsOptions) | ||||
|     { | ||||
|         ix::CobraConnection conn; | ||||
|         conn.configure( | ||||
|             appkey, endpoint, rolename, rolesecret, ix::WebSocketPerMessageDeflateOptions(true)); | ||||
|         conn.configure(appkey, | ||||
|                        endpoint, | ||||
|                        rolename, | ||||
|                        rolesecret, | ||||
|                        ix::WebSocketPerMessageDeflateOptions(true), | ||||
|                        tlsOptions); | ||||
|         conn.connect(); | ||||
|  | ||||
|         auto tokens = parseFields(fields); | ||||
|   | ||||
| @@ -61,7 +61,7 @@ namespace ix | ||||
|  | ||||
|             // Server connection | ||||
|             state->webSocket().setOnMessageCallback([webSocket, state, verbose]( | ||||
|                                                         const WebSocketMessagePtr& msg) { | ||||
|                                                      const WebSocketMessagePtr& msg) { | ||||
|                 if (msg->type == ix::WebSocketMessageType::Open) | ||||
|                 { | ||||
|                     std::cerr << "New connection" << std::endl; | ||||
| @@ -120,6 +120,7 @@ namespace ix | ||||
|                     std::string url(remoteUrl); | ||||
|                     url += msg->openInfo.uri; | ||||
|                     state->webSocket().setUrl(url); | ||||
|                     state->webSocket().disableAutomaticReconnection(); | ||||
|                     state->webSocket().start(); | ||||
|  | ||||
|                     // we should sleep here for a bit until we've established the | ||||
|   | ||||
| @@ -43,7 +43,8 @@ namespace ix | ||||
|                       int redisPort, | ||||
|                       const std::string& redisPassword, | ||||
|                       bool verbose, | ||||
|                       const std::string& appsConfigPath) | ||||
|                       const std::string& appsConfigPath, | ||||
|                       const SocketTLSOptions& socketTLSOptions) | ||||
|     { | ||||
|         snake::AppConfig appConfig; | ||||
|         appConfig.port = port; | ||||
| @@ -51,6 +52,7 @@ namespace ix | ||||
|         appConfig.verbose = verbose; | ||||
|         appConfig.redisPort = redisPort; | ||||
|         appConfig.redisPassword = redisPassword; | ||||
|         appConfig.socketTLSOptions = socketTLSOptions; | ||||
|  | ||||
|         // Parse config file | ||||
|         auto str = readAsString(appsConfigPath); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user