int IoTHubClient_Auth_Set_xio_Certificate(IOTHUB_AUTHORIZATION_HANDLE handle, XIO_HANDLE xio)
{
    int result;
    if (handle == NULL || xio == NULL)
    {
        LogError("Invalid Parameter handle: %p xio: %p", handle, xio);
        result = __FAILURE__;
    }
    else if (handle->cred_type != IOTHUB_CREDENTIAL_TYPE_X509_ECC)
    {
        LogError("Invalid credential types for this operation");
        result = __FAILURE__;
    }
    else
    {
#ifdef USE_PROV_MODULE
        CREDENTIAL_RESULT* cred_result = iothub_device_auth_generate_credentials(handle->device_auth_handle, NULL);
        if (cred_result == NULL)
        {
            LogError("Failure generating credentials");
            result = __FAILURE__;
        }
        else
        {
            if (xio_setoption(xio, OPTION_X509_ECC_CERT, cred_result->auth_cred_result.x509_result.x509_cert) != 0)
            {
                LogError("Failure setting x509 cert on xio");
                result = __FAILURE__;
            }
            else if (xio_setoption(xio, OPTION_X509_ECC_KEY, cred_result->auth_cred_result.x509_result.x509_alias_key) != 0)
            {
                LogError("Failure setting x509 key on xio");
                result = __FAILURE__;
            }
            else
            {
                result = 0;
            }
            free(cred_result);
        }
#else
        LogError("Failed HSM module is not supported");
        result = __FAILURE__;
#endif
    }
    return result;
}
static XIO_HANDLE mqtt_transport_io(const char* fqdn, const HTTP_PROXY_OPTIONS* proxy_info)
{
    XIO_HANDLE result;
    HTTP_PROXY_IO_CONFIG proxy_config;
    TLSIO_CONFIG tls_io_config;

    memset(&tls_io_config, 0, sizeof(TLSIO_CONFIG));
    tls_io_config.hostname = fqdn;
    tls_io_config.port = MQTT_PORT_NUM;
    if (proxy_info != NULL)
    {
        /* Codes_PROV_TRANSPORT_MQTT_CLIENT_07_012: [ If proxy_info is not NULL, amqp_transport_io shall construct a HTTP_PROXY_IO_CONFIG object and assign it to TLSIO_CONFIG underlying_io_parameters ] */
        proxy_config.hostname = tls_io_config.hostname;
        proxy_config.port = MQTT_PORT_NUM;
        proxy_config.proxy_hostname = proxy_info->host_address;
        proxy_config.proxy_port = proxy_info->port;
        proxy_config.username = proxy_info->username;
        proxy_config.password = proxy_info->password;

        tls_io_config.underlying_io_interface = http_proxy_io_get_interface_description();
        tls_io_config.underlying_io_parameters = &proxy_config;
    }

    const IO_INTERFACE_DESCRIPTION* tlsio_interface = platform_get_default_tlsio();
    if (tlsio_interface == NULL)
    {
        /* Codes_PROV_TRANSPORT_MQTT_CLIENT_07_013: [ If any failure is encountered amqp_transport_io shall return NULL ]*/
        LogError("platform_get_default_tlsio return NULL IO Interface");
        result = NULL;
    }
    else
    {
        result = xio_create(tlsio_interface, &tls_io_config);
        if (result == NULL)
        {
            /* Codes_PROV_TRANSPORT_MQTT_CLIENT_07_013: [ If any failure is encountered amqp_transport_io shall return NULL ]*/
            LogError("failed calling xio_create on underlying io");
            result = NULL;
        }
        else
        {
#ifdef USE_OPENSSL
            // requires tls 1.2
            int tls_version = 12;
            xio_setoption(result, OPTION_TLS_VERSION, &tls_version);
#endif
        }
    }
    /* Codes_PROV_TRANSPORT_MQTT_CLIENT_07_014: [ On success mqtt_transport_io shall return allocated XIO_HANDLE. ] */
    return result;
}
static int create_sasl_components(AMQP_CONNECTION_INSTANCE* instance)
{
    int result;
    SASL_MECHANISM_HANDLE sasl_mechanism;
    XIO_HANDLE sasl_io;

    // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_012: [`instance->sasl_mechanism` shall be created using saslmechanism_create()]
    if ((sasl_mechanism = saslmechanism_create(saslmssbcbs_get_interface(), NULL)) == NULL)
    {
        // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_013: [If saslmechanism_create() fails, amqp_connection_create() shall fail and return NULL]
        LogError("Failed creating the SASL mechanism (saslmechanism_create failed)");
        result = __FAILURE__;
    }
    else
    {
        // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_014: [A SASLCLIENTIO_CONFIG shall be set with `instance->underlying_io_transport` and `instance->sasl_mechanism`]
        SASLCLIENTIO_CONFIG sasl_client_config;
        sasl_client_config.sasl_mechanism = sasl_mechanism;
        sasl_client_config.underlying_io = instance->underlying_io_transport;
        
        // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_015: [`instance->sasl_io` shall be created using xio_create() passing saslclientio_get_interface_description() and the SASLCLIENTIO_CONFIG instance]
        if ((sasl_io = xio_create(saslclientio_get_interface_description(), &sasl_client_config)) == NULL)
        {
            // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_016: [If xio_create() fails, amqp_connection_create() shall fail and return NULL]
            LogError("Failed creating the SASL I/O (xio_create failed)");
            saslmechanism_destroy(sasl_mechanism);
            result = __FAILURE__;
        }
        // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_017: [The sasl_io "logtrace" option shall be set using xio_setoption(), passing `instance->is_trace_on`]
        else if (xio_setoption(sasl_io, SASL_IO_OPTION_LOG_TRACE, (const void*)&instance->is_trace_on) != RESULT_OK)
        {
            LogError("Failed setting the SASL I/O logging trace option (xio_setoption failed)");
            xio_destroy(sasl_io);
            saslmechanism_destroy(sasl_mechanism);
            result = __FAILURE__;
        }
        else
        {
            instance->sasl_mechanism = sasl_mechanism;
            instance->sasl_io = sasl_io;
            result = RESULT_OK;
        }
    }

    return result;
}
int tlsio_wolfssl_setoption(CONCRETE_IO_HANDLE tls_io, const char* optionName, const void* value)
{
    int result;

    if (tls_io == NULL || optionName == NULL)
    {
        result = __LINE__;
    }
    else
    {
        TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)tls_io;

        if (strcmp("TrustedCerts", optionName) == 0)
        {
            const char* cert = (const char*)value;
            if (tls_io_instance->certificate != NULL)
            {
                // Free the memory if it has been previously allocated
                free(tls_io_instance->certificate);
            }

            // Store the certificate
            size_t len = strlen(cert);
            tls_io_instance->certificate = (const char*)malloc(len+1);
            if (tls_io_instance->certificate == NULL)
            {
                result = __LINE__;
            }
            else
            {
                (void)strcpy(tls_io_instance->certificate, cert);
                result = 0;
            }
        }
        else if (tls_io_instance->socket_io == NULL)
        {
            result = __LINE__;
        }
        else
        {
            result = xio_setoption(tls_io_instance->socket_io, optionName, value);
        }
    }

    return result;
}
int amqp_connection_set_logging(AMQP_CONNECTION_HANDLE conn_handle, bool is_trace_on)
{
    int result;

    // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_052: [If `conn_handle` is NULL, amqp_connection_set_logging() shall fail and return __FAILURE__]
    if (conn_handle == NULL)
    {
        result = __FAILURE__;
        LogError("amqp_connection_set_logging failed (conn_handle is NULL)");
    }
    else
    {
        AMQP_CONNECTION_INSTANCE* instance = (AMQP_CONNECTION_INSTANCE*)conn_handle;

        // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_053: [`instance->is_trace_on` shall be set to `is_trace_on`]
        instance->is_trace_on = is_trace_on;

        // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_054: [Tracing on `instance->sasl_io` shall be set to `instance->is_trace_on` if the value has changed]
        if (instance->sasl_io != NULL &&
            xio_setoption(instance->sasl_io, SASL_IO_OPTION_LOG_TRACE, (const void*)&instance->is_trace_on) != RESULT_OK)
        {
            // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_072: [If xio_setoption() fails, amqp_connection_set_logging() shall fail and return __FAILURE__]
            result = __FAILURE__;
            LogError("amqp_connection_set_logging failed (xio_setoption() failed)");
        }
        else
        {
            // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_055: [Tracing on `instance->connection_handle` shall be set to `instance->is_trace_on` if the value has changed]
            connection_set_trace(instance->connection_handle, instance->is_trace_on);

            // Codes_SRS_IOTHUBTRANSPORT_AMQP_CONNECTION_09_056: [amqp_connection_set_logging() shall return success code 0]
            result = RESULT_OK;
        }
    }

    return result;
}
int tlsio_schannel_setoption(CONCRETE_IO_HANDLE tls_io, const char* optionName, const void* value)
{
    int result;

    if (tls_io == NULL || optionName == NULL)
    {
        result = __LINE__;
    }
    else
    {
        TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)tls_io;

        if (tls_io_instance->socket_io == NULL)
        {
            result = __LINE__;
        }
        else
        {
            result = xio_setoption(tls_io_instance->socket_io, optionName, value);
        }
    }

    return result;
}
static PROV_TRANSPORT_IO_INFO* amqp_transport_ws_io(const char* fqdn, SASL_MECHANISM_HANDLE* sasl_mechanism, const HTTP_PROXY_OPTIONS* proxy_info)
{
    PROV_TRANSPORT_IO_INFO* result;
    HTTP_PROXY_IO_CONFIG proxy_config;
    TLSIO_CONFIG tls_io_config;
    WSIO_CONFIG ws_io_config;
    const IO_INTERFACE_DESCRIPTION* ws_io_interface;
    const IO_INTERFACE_DESCRIPTION* tlsio_interface;
    if ((ws_io_interface = wsio_get_interface_description()) == NULL)
    {
        /* Codes_PROV_TRANSPORT_AMQP_WS_CLIENT_07_013: [ If any failure is encountered amqp_transport_ws_io shall return NULL ] */
        LogError("wsio_get_interface_description return NULL IO Interface");
        result = NULL;
    }
    else if ((tlsio_interface = platform_get_default_tlsio()) == NULL)
    {
        /* Codes_PROV_TRANSPORT_AMQP_WS_CLIENT_07_013: [ If any failure is encountered amqp_transport_ws_io shall return NULL ] */
        LogError("platform_get_default_tlsio return NULL IO Interface");
        result = NULL;
    }
    else
    {
        memset(&tls_io_config, 0, sizeof(TLSIO_CONFIG));

        ws_io_config.hostname = fqdn;
        ws_io_config.port = PROV_AMQP_WS_PORT_NUM;
        ws_io_config.protocol = PROV_AMQP_WS_PROTOCOL_NAME;
        ws_io_config.resource_name = PROV_AMQP_WS_RELATIVE_PATH;
        ws_io_config.underlying_io_interface = tlsio_interface;
        ws_io_config.underlying_io_parameters = &tls_io_config;

        tls_io_config.hostname = fqdn;
        tls_io_config.port = PROV_AMQP_WS_PORT_NUM;
        if (proxy_info != NULL)
        {
            /* Codes_PROV_TRANSPORT_AMQP_WS_CLIENT_07_012: [ If proxy_info is not NULL, amqp_transport_ws_io shall construct a HTTP_PROXY_IO_CONFIG object and assign it to TLSIO_CONFIG underlying_io_parameters ] */
            proxy_config.hostname = tls_io_config.hostname;
            proxy_config.port = proxy_info->port;
            proxy_config.proxy_hostname = proxy_info->host_address;
            proxy_config.proxy_port = proxy_info->port;
            proxy_config.username = proxy_info->username;
            proxy_config.password = proxy_info->password;

            tls_io_config.underlying_io_interface = http_proxy_io_get_interface_description();
            tls_io_config.underlying_io_parameters = &proxy_config;
        }
        /* Codes_PROV_TRANSPORT_AMQP_WS_CLIENT_07_014: [ On success amqp_transport_ws_io shall return an allocated PROV_TRANSPORT_IO_INFO structure. ] */
        if ((result = (PROV_TRANSPORT_IO_INFO*)malloc(sizeof(PROV_TRANSPORT_IO_INFO))) == NULL)
        {
            /* Codes_PROV_TRANSPORT_AMQP_WS_CLIENT_07_013: [ If any failure is encountered amqp_transport_ws_io shall return NULL ] */
            LogError("failure allocating prov_transport info");
            result = NULL;
        }
        else
        {
            memset(result, 0, sizeof(PROV_TRANSPORT_IO_INFO));

            /* Codes_PROV_TRANSPORT_AMQP_WS_CLIENT_07_015: [ amqp_transport_ws_io shall allocate a PROV_TRANSPORT_IO_INFO transfer_handle by calling xio_create with the ws_io_interface. ] */
            result->transport_handle = xio_create(ws_io_interface, &ws_io_config);
            if (result->transport_handle == NULL)
            {
                /* Codes_PROV_TRANSPORT_AMQP_WS_CLIENT_07_013: [ If any failure is encountered amqp_transport_ws_io shall return NULL ] */
                LogError("failed calling xio_create on underlying io");
                free(result);
                result = NULL;
            }
            else
            {
#ifdef USE_OPENSSL
                // Default to tls 1.2
                int tls_version = 12;
                xio_setoption(result->transport_handle, OPTION_TLS_VERSION, &tls_version);
#endif

                if (sasl_mechanism != NULL)
                {
                    const IO_INTERFACE_DESCRIPTION* saslio_interface;
                    SASLCLIENTIO_CONFIG sasl_io_config;
                    sasl_io_config.underlying_io = result->transport_handle;
                    sasl_io_config.sasl_mechanism = *sasl_mechanism;

                    saslio_interface = saslclientio_get_interface_description();
                    if (saslio_interface == NULL)
                    {
                        /* Codes_PROV_TRANSPORT_AMQP_WS_CLIENT_07_013: [ If any failure is encountered amqp_transport_ws_io shall return NULL ] */
                        LogError("failed calling xio_create on underlying io");
                        xio_destroy(result->transport_handle);
                        free(result);
                        result = NULL;
                    }
                    else
                    {
                        /* Codes_PROV_TRANSPORT_AMQP_WS_CLIENT_07_016: [ amqp_transport_ws_io shall allocate a PROV_TRANSPORT_IO_INFO sasl_handle by calling xio_create with the saslio_interface. ] */
                        result->sasl_handle = xio_create(saslio_interface, &sasl_io_config);
                        if (result->sasl_handle == NULL)
                        {
                            /* Codes_PROV_TRANSPORT_AMQP_WS_CLIENT_07_013: [ If any failure is encountered amqp_transport_ws_io shall return NULL ] */
                            LogError("failed calling xio_create on sasl client interface");
                            xio_destroy(result->transport_handle);
                            free(result);
                            result = NULL;
                        }
                    }
                }
            }
        }
    }
    return result;
}
static int construct_transport(PROV_TRANSPORT_MQTT_INFO* mqtt_info)
{
    int result;

    if (mqtt_info->transport_io == NULL)
    {
        HTTP_PROXY_OPTIONS* transport_proxy;
        if (mqtt_info->proxy_option.host_address != NULL)
        {
            transport_proxy = &mqtt_info->proxy_option;
        }
        else
        {
            transport_proxy = NULL;
        }

        if ((mqtt_info->transport_io = mqtt_info->transport_io_cb(mqtt_info->hostname, transport_proxy)) == NULL)
        {
            LogError("Failure calling transport_io callback");
            result = __FAILURE__;
        }
        else
        {
            if (mqtt_info->certificate != NULL && xio_setoption(mqtt_info->transport_io, OPTION_TRUSTED_CERT, mqtt_info->certificate) != 0)
            {
                LogError("Failure setting trusted certs");
                result = __FAILURE__;
                xio_destroy(mqtt_info->transport_io);
                mqtt_info->transport_io = NULL;
            }
            else if (mqtt_info->hsm_type == TRANSPORT_HSM_TYPE_X509)
            {
                if (mqtt_info->x509_cert != NULL && mqtt_info->private_key != NULL)
                {
                    if (xio_setoption(mqtt_info->transport_io, OPTION_X509_ECC_CERT, mqtt_info->x509_cert) != 0)
                    {
                        LogError("Failure setting x509 cert on xio");
                        xio_destroy(mqtt_info->transport_io);
                        mqtt_info->transport_io = NULL;
                        result = __FAILURE__;
                    }
                    else if (xio_setoption(mqtt_info->transport_io, OPTION_X509_ECC_KEY, mqtt_info->private_key) != 0)
                    {
                        LogError("Failure setting x509 key on xio");
                        xio_destroy(mqtt_info->transport_io);
                        mqtt_info->transport_io = NULL;
                        result = __FAILURE__;
                    }
                    else
                    {
                        result = 0;
                    }
                }
                else
                {
                    LogError("x509 certificate is NULL");
                    xio_destroy(mqtt_info->transport_io);
                    mqtt_info->transport_io = NULL;
                    result = __FAILURE__;
                }
            }
            else
            {
                result = 0;
            }
        }
    }
    else
    {
        result = 0;
    }
    return result;
}
int tlsio_schannel_setoption(CONCRETE_IO_HANDLE tls_io, const char* optionName, const void* value)
{
    int result;

    if (tls_io == NULL || optionName == NULL)
    {
        result = __LINE__;
    }
    else
    {
        TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)tls_io;
        /*x509certificate and x509privatekey are "referenced" by this layer*/
        if (strcmp("x509certificate", optionName) == 0)
        {
            if (tls_io_instance->x509certificate != NULL)
            {
                LogError("unable to set x509 options more than once");
                result =__LINE__;
            }
            else
            {
				tls_io_instance->x509certificate = (const char *)tlsio_schannel_CloneOption("x509certificate", value);
                if (tls_io_instance->x509privatekey != NULL)
                {
                    tls_io_instance->x509_schannel_handle = x509_schannel_create(tls_io_instance->x509certificate, tls_io_instance->x509privatekey);
                    if (tls_io_instance->x509_schannel_handle == NULL)
                    {
                        LogError("x509_schannel_create failed");
                        result = __LINE__;
                    }
                    else
                    {
                        /*all is fine, the x509 shall be used later*/
                        result = 0;
                    }
                }
                else
                {
                    result = 0; /*all is fine, maybe x509 privatekey will come and then x509 is set*/
                }
            }
        }
        else if (strcmp("x509privatekey", optionName) == 0)
        {
            if (tls_io_instance->x509privatekey != NULL)
            {
                LogError("unable to set more than once x509 options");
                result = __LINE__;
            }
            else
            {
				tls_io_instance->x509privatekey = (const char *)tlsio_schannel_CloneOption("x509privatekey", value);
                if (tls_io_instance->x509certificate!= NULL)
                {
                    tls_io_instance->x509_schannel_handle = x509_schannel_create(tls_io_instance->x509certificate, tls_io_instance->x509privatekey);
                    if (tls_io_instance->x509_schannel_handle == NULL)
                    {
                        LogError("x509_schannel_create failed");
                        result = __LINE__;
                    }
                    else
                    {
                        /*all is fine, the x509 shall be used later*/
                        result = 0;
                    }
                }
                else
                {
                    result = 0; /*all is fine, maybe x509 privatekey will come and then x509 is set*/
                }
            }
        }
        else if (tls_io_instance->socket_io == NULL)
        {
            result = __LINE__;
        }
        else
        {
            result = xio_setoption(tls_io_instance->socket_io, optionName, value);
        }
    }

    return result;
}
int tls_server_io_schannel_setoption(CONCRETE_IO_HANDLE tls_io, const char* optionName, const void* value)
{
    int result;

    if (tls_io == NULL || optionName == NULL)
    {
        LogError("invalid argument detected: CONCRETE_IO_HANDLE tls_io = %p, const char* optionName = %p", tls_io, optionName);
        result = __FAILURE__;
    }
    else
    {
        TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)tls_io;
        if (strcmp("x509certificate", optionName) == 0)
        {
            if (tls_io_instance->x509certificate != NULL)
            {
                LogError("x509certificate has already been specified");
                result = __FAILURE__;
            }
            else
            {
                tls_io_instance->x509certificate = (char *)tls_server_io_schannel_CloneOption("x509certificate", value);
                if (tls_io_instance->x509certificate == NULL)
                {
                    LogError("tls_server_io_schannel_CloneOption failed");
                    result = __FAILURE__;
                }
                else
                {
                    if (tls_io_instance->x509privatekey != NULL)
                    {
                        tls_io_instance->x509_schannel_handle = x509_schannel_create(tls_io_instance->x509certificate, tls_io_instance->x509privatekey);
                        if (tls_io_instance->x509_schannel_handle == NULL)
                        {
                            LogError("x509_schannel_create failed");
                            result = __FAILURE__;
                        }
                        else
                        {
                            /*all is fine, the x509 shall be used later*/
                            result = 0;
                        }
                    }
                    else
                    {
                        result = 0; /*all is fine, maybe x509 privatekey will come and then x509 is set*/
                    }
                }
            }
        }
        else if (strcmp("x509privatekey", optionName) == 0)
        {
            if (tls_io_instance->x509privatekey != NULL)
            {
                LogError("x509privatekey has already been specified");
                result = __FAILURE__;
            }
            else
            {
                tls_io_instance->x509privatekey = (char *)tls_server_io_schannel_CloneOption("x509privatekey", value);
                if (tls_io_instance->x509privatekey == NULL)
                {
                    LogError("tls_server_io_schannel_CloneOption failed");
                    result = __FAILURE__;
                }
                else
                {
                    if (tls_io_instance->x509certificate != NULL)
                    {
                        tls_io_instance->x509_schannel_handle = x509_schannel_create(tls_io_instance->x509certificate, tls_io_instance->x509privatekey);
                        if (tls_io_instance->x509_schannel_handle == NULL)
                        {
                            LogError("x509_schannel_create failed");
                            result = __FAILURE__;
                        }
                        else
                        {
                            /*all is fine, the x509 shall be used later*/
                            result = 0;
                        }
                    }
                    else
                    {
                        result = 0; /*all is fine, maybe x509 cert will come and then x509 is set*/
                    }
                }
            }
        }
        else if (tls_io_instance->socket_io == NULL)
        {
            LogError("tls_io_instance->socket_io is not set");
            result = __FAILURE__;
        }
        else
        {
            result = xio_setoption(tls_io_instance->socket_io, optionName, value);
        }
    }

    return result;
}