#pragma once /** * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0. */ #include #include #include #include #if !BYO_CRYPTO namespace Aws { namespace Iot { class MqttClient; /** * Represents a unique configuration for connecting to a single AWS IoT endpoint. You can use a single instance * of this class PER endpoint you want to connect to. This object must live through the lifetime of your * connection. */ class AWS_CRT_CPP_API MqttClientConnectionConfig final { public: static MqttClientConnectionConfig CreateInvalid(int lastError) noexcept; /** * Creates a client configuration for use with making new AWS Iot specific MQTT Connections with MTLS. * * @param endpoint endpoint to connect to * @param port port to connect to * @param socketOptions socket options to use when establishing the connection * @param tlsContext tls context that should be used for all connections sourced from this config */ MqttClientConnectionConfig( const Crt::String &endpoint, uint16_t port, const Crt::Io::SocketOptions &socketOptions, Crt::Io::TlsContext &&tlsContext); /** * Creates a client configuration for use with making new AWS Iot specific MQTT Connections with web * sockets. interceptor: a callback invoked during web socket handshake giving you the opportunity to mutate * the request for authorization/signing purposes. If not specified, it's assumed you don't need to sign the * request. proxyOptions: optional, if you want to use a proxy with websockets, specify the configuration * options here. * * If proxy options are used, the tlsContext is applied to the connection to the remote endpoint, NOT the * proxy. To make a tls connection to the proxy itself, you'll want to specify tls options in proxyOptions. * * @param endpoint endpoint to connect to * @param port port to connect to * @param socketOptions socket options to use when establishing the connection * @param tlsContext tls context that should be used for all connections sourced from this config * @param interceptor websocket upgrade handshake transformation function * @param proxyOptions proxy configuration options */ MqttClientConnectionConfig( const Crt::String &endpoint, uint16_t port, const Crt::Io::SocketOptions &socketOptions, Crt::Io::TlsContext &&tlsContext, Crt::Mqtt::OnWebSocketHandshakeIntercept &&interceptor, const Crt::Optional &proxyOptions); /** * @return true if the instance is in a valid state, false otherwise. */ explicit operator bool() const noexcept { return m_context ? true : false; } /** * @return the value of the last aws error encountered by operations on this instance. */ int LastError() const noexcept { return m_lastError; } private: MqttClientConnectionConfig(int lastError) noexcept; MqttClientConnectionConfig( const Crt::String &endpoint, uint16_t port, const Crt::Io::SocketOptions &socketOptions, Crt::Io::TlsContext &&tlsContext, const Crt::Optional &proxyOptions); Crt::String m_endpoint; uint16_t m_port; Crt::Io::TlsContext m_context; Crt::Io::SocketOptions m_socketOptions; Crt::Mqtt::OnWebSocketHandshakeIntercept m_webSocketInterceptor; Crt::String m_username; Crt::String m_password; Crt::Optional m_proxyOptions; int m_lastError; friend class MqttClient; friend class MqttClientConnectionConfigBuilder; }; using CreateSigningConfig = std::function(void)>; /** * Class encapsulating configuration for establishing an Aws IoT mqtt connection via websockets */ struct AWS_CRT_CPP_API WebsocketConfig { /** * Create a websocket configuration for use with the default credentials provider chain. Signing region * will be used for Sigv4 signature calculations. * * @param signingRegion Aws region that is being connected to. Required in order to properly sign the * handshake upgrade request * @param bootstrap client bootstrap to establish any connections needed by the default credentials * provider chain which will get built for the user */ WebsocketConfig( const Crt::String &signingRegion, Crt::Io::ClientBootstrap *bootstrap, Crt::Allocator *allocator = Crt::g_allocator) noexcept; /** * Create a websocket configuration for use with the default credentials provider chain and default * ClientBootstrap. Signing region will be used for Sigv4 signature calculations. * * For more information on the default ClientBootstrap see * Aws::Crt::ApiHandle::GetOrCreateDefaultClientBootstrap * * @param signingRegion Aws region that is being connected to. Required in order to properly sign the * handshake upgrade request */ WebsocketConfig(const Crt::String &signingRegion, Crt::Allocator *allocator = Crt::g_allocator) noexcept; /** * Create a websocket configuration for use with a custom credentials provider. Signing region will be used * for Sigv4 signature calculations. * * @param signingRegion Aws region that is being connected to. Required in order to properly sign the * handshake upgrade request * @param credentialsProvider credentials provider to source AWS credentials from */ WebsocketConfig( const Crt::String &signingRegion, const std::shared_ptr &credentialsProvider, Crt::Allocator *allocator = Crt::g_allocator) noexcept; /** * Create a websocket configuration for use with a custom credentials provider, and a custom signer. * * You'll need to provide a function for use with creating a signing Config and pass it to * createSigningConfig. * * This is useful for cases use with: * https://docs.aws.amazon.com/iot/latest/developerguide/custom-auth.html * * @param credentialsProvider * @param signer * @param createSigningConfig */ WebsocketConfig( const std::shared_ptr &credentialsProvider, const std::shared_ptr &signer, CreateSigningConfig createSigningConfig) noexcept; std::shared_ptr CredentialsProvider; std::shared_ptr Signer; CreateSigningConfig CreateSigningConfigCb; /** * @deprecated Specify ProxyOptions to use a proxy with your websocket connection. * * If MqttClientConnectionConfigBuilder::m_proxyOptions is valid, then that will be used over * this value. */ Crt::Optional ProxyOptions; Crt::String SigningRegion; Crt::String ServiceName; }; /** * Represents configuration parameters for building a MqttClientConnectionConfig object. You can use a single * instance of this class PER MqttClientConnectionConfig you want to generate. If you want to generate a config * for a different endpoint or port etc... you need a new instance of this class. */ class AWS_CRT_CPP_API MqttClientConnectionConfigBuilder final { public: MqttClientConnectionConfigBuilder(); /** * Sets the builder up for MTLS using certPath and pkeyPath. These are files on disk and must be in the PEM * format. * * @param certPath path to the X509 certificate (pem file) to use * @param pkeyPath path to the private key (pem file) to use */ MqttClientConnectionConfigBuilder( const char *certPath, const char *pkeyPath, Crt::Allocator *allocator = Crt::g_allocator) noexcept; /** * Sets the builder up for MTLS using cert and pkey. These are in-memory buffers and must be in the PEM * format. * * @param cert buffer containing the X509 certificate in a PEM format * @param pkey buffer containing the private key in a PEM format */ MqttClientConnectionConfigBuilder( const Crt::ByteCursor &cert, const Crt::ByteCursor &pkey, Crt::Allocator *allocator = Crt::g_allocator) noexcept; /** * Sets the builder up for MTLS, using a PKCS#11 library for private key operations. * * NOTE: This only works on Unix devices. */ MqttClientConnectionConfigBuilder( const Crt::Io::TlsContextPkcs11Options &pkcs11Options, Crt::Allocator *allocator = Crt::g_allocator) noexcept; /** * Sets the builder up for MTLS, using a certificate in a Windows certificate store. * * NOTE: This only works on Windows. * * @param windowsCertStorePath Path to certificate in a Windows certificate store. * The path must use backslashes and end with the certificate's thumbprint. * Example: `CurrentUser\MY\A11F8A9B5DF5B98BA3508FBCA575D09570E0D2C6` * NOTE: This only works on Windows. */ MqttClientConnectionConfigBuilder( const char *windowsCertStorePath, Crt::Allocator *allocator = Crt::g_allocator) noexcept; /** * Sets the builder up for Websocket connection. * * @param config websocket configuration information */ MqttClientConnectionConfigBuilder( const WebsocketConfig &config, Crt::Allocator *allocator = Crt::g_allocator) noexcept; /** * Sets endpoint to connect to. * * @param endpoint endpoint to connect to * * @return this builder object */ MqttClientConnectionConfigBuilder &WithEndpoint(const Crt::String &endpoint); /** * Sets endpoint to connect to. * * @param endpoint endpoint to connect to * * @return this builder object */ MqttClientConnectionConfigBuilder &WithEndpoint(Crt::String &&endpoint); /** * Overrides the default port. By default, if ALPN is supported, 443 will be used. Otherwise 8883 will be * used. If you specify 443 and ALPN is not supported, we will still attempt to connect over 443 without * ALPN. * * @param port port to connect to * * @return this builder object */ MqttClientConnectionConfigBuilder &WithPortOverride(uint16_t port) noexcept; /** * Sets the certificate authority for the endpoint you're connecting to. This is a path to a file on disk * and must be in PEM format. * * @param caPath path to the CA file in PEM format * * @return this builder object */ MqttClientConnectionConfigBuilder &WithCertificateAuthority(const char *caPath) noexcept; /** * Sets the certificate authority for the endpoint you're connecting to. This is an in-memory buffer and * must be in PEM format. * * @param cert buffer containing the CA certificate in a PEM format * * @return this builder object */ MqttClientConnectionConfigBuilder &WithCertificateAuthority(const Crt::ByteCursor &cert) noexcept; /** * TCP option: Enables TCP keep alive. Defaults to off. * * @return this builder object */ MqttClientConnectionConfigBuilder &WithTcpKeepAlive() noexcept; /** * TCP option: Sets the connect timeout. Defaults to 3 seconds. * * @param connectTimeoutMs socket connection timeout * * @return this builder object */ MqttClientConnectionConfigBuilder &WithTcpConnectTimeout(uint32_t connectTimeoutMs) noexcept; /** * TCP option: Sets time before keep alive probes are sent. Defaults to kernel defaults * * @param keepAliveTimeoutSecs time interval of no activity, in seconds, before keep alive probes * get sent * * @return this builder object */ MqttClientConnectionConfigBuilder &WithTcpKeepAliveTimeout(uint16_t keepAliveTimeoutSecs) noexcept; /** * TCP option: Sets the frequency of sending keep alive probes in seconds once the keep alive timeout * expires. Defaults to kernel defaults. * * @param keepAliveIntervalSecs the frequency of sending keep alive probes in seconds once the keep alive * timeout expires * * @return this builder object */ MqttClientConnectionConfigBuilder &WithTcpKeepAliveInterval(uint16_t keepAliveIntervalSecs) noexcept; /** * TCP option: Sets the amount of keep alive probes allowed to fail before the connection is terminated. * Defaults to kernel defaults. * * @param maxProbes the amount of keep alive probes allowed to fail before the connection is terminated * * @return this builder object */ MqttClientConnectionConfigBuilder &WithTcpKeepAliveMaxProbes(uint16_t maxProbes) noexcept; /** * Sets the minimum tls version that is acceptable for connection establishment * * @param minimumTlsVersion minimum tls version allowed in client connections * * @return this builder object */ MqttClientConnectionConfigBuilder &WithMinimumTlsVersion(aws_tls_versions minimumTlsVersion) noexcept; /** * Sets http proxy options. * * @param proxyOptions proxy configuration options for connection establishment * * @return this builder object */ MqttClientConnectionConfigBuilder &WithHttpProxyOptions( const Crt::Http::HttpClientConnectionProxyOptions &proxyOptions) noexcept; /** * Whether to send the SDK name and version number in the MQTT CONNECT packet. * Default is True. * * @param enabled true to send SDK version/name in the connect for metrics gathering purposes * * @return this builder object */ MqttClientConnectionConfigBuilder &WithMetricsCollection(bool enabled); /** * Overrides the default SDK Name to send as a metric in the MQTT CONNECT packet. * * @param sdkName string to use as the SDK name parameter in the connection string * * @return this builder object */ MqttClientConnectionConfigBuilder &WithSdkName(const Crt::String &sdkName); /** * Overrides the default SDK Version to send as a metric in the MQTT CONNECT packet. * * @param sdkVersion string to use as the SDK version parameter in the connection string * * @return this builder object */ MqttClientConnectionConfigBuilder &WithSdkVersion(const Crt::String &sdkVersion); /** * Builds a client configuration object from the set options. * * @return a new client connection config instance */ MqttClientConnectionConfig Build() noexcept; /** * @return true if the instance is in a valid state, false otherwise. */ explicit operator bool() const noexcept { return m_lastError == 0; } /** * @return the value of the last aws error encountered by operations on this instance. */ int LastError() const noexcept { return m_lastError ? m_lastError : AWS_ERROR_UNKNOWN; } private: // Common setup shared by all valid constructors MqttClientConnectionConfigBuilder(Crt::Allocator *allocator) noexcept; Crt::Allocator *m_allocator; Crt::String m_endpoint; uint16_t m_portOverride; Crt::Io::SocketOptions m_socketOptions; Crt::Io::TlsContextOptions m_contextOptions; Crt::Optional m_websocketConfig; Crt::Optional m_proxyOptions; bool m_enableMetricsCollection = true; Crt::String m_sdkName = "CPPv2"; Crt::String m_sdkVersion = AWS_CRT_CPP_VERSION; int m_lastError; }; /** * AWS IOT specific Mqtt Client. Sets defaults for using the AWS IOT service. You'll need an instance of * MqttClientConnectionConfig to use. Once NewConnection returns, you use it's return value identically * to how you would use Aws::Crt::Mqtt::MqttConnection */ class AWS_CRT_CPP_API MqttClient final { public: MqttClient(Crt::Io::ClientBootstrap &bootstrap, Crt::Allocator *allocator = Crt::g_allocator) noexcept; /** * Constructs a new Mqtt Client object using the static default ClientBootstrap. * * For more information on the default ClientBootstrap see * Aws::Crt::ApiHandle::GetOrCreateDefaultClientBootstrap */ MqttClient(Crt::Allocator *allocator = Crt::g_allocator) noexcept; /** * Creates a new mqtt connection from a connection configuration object * @param config mqtt connection configuration * @return a new mqtt connection */ std::shared_ptr NewConnection(const MqttClientConnectionConfig &config) noexcept; /** * @return the value of the last aws error encountered by operations on this instance. */ int LastError() const noexcept { return m_client.LastError(); } /** * @return true if the instance is in a valid state, false otherwise. */ explicit operator bool() const noexcept { return m_client ? true : false; } private: Crt::Mqtt::MqttClient m_client; int m_lastError; }; } // namespace Iot } // namespace Aws #endif // !BYO_CRYPTO