Microsoft Azure IoTHub client libraries
Dependents: sht15_remote_monitoring RobotArmDemo iothub_client_sample_amqp f767zi_mqtt ... more
This library implements the Microsoft Azure IoTHub client library. The code is replicated from https://github.com/Azure/azure-iot-sdks
Diff: iothub_client_core_ll.c
- Revision:
- 91:bbf806070c5f
- Parent:
- 89:a2ed767a532e
- Child:
- 92:97148cf9aa2a
--- a/iothub_client_core_ll.c Thu Jun 28 10:07:22 2018 -0700 +++ b/iothub_client_core_ll.c Thu Jul 12 18:09:13 2018 -0700 @@ -30,6 +30,12 @@ #include "internal/iothub_client_ll_uploadtoblob.h" #endif +#ifdef USE_EDGE_MODULES +#include "azure_c_shared_utility/envvariable.h" +#include "azure_prov_client/iothub_security_factory.h" +#include "internal/iothub_client_edge.h" +#endif + #define LOG_ERROR_RESULT LogError("result = %s", ENUM_TO_STRING(IOTHUB_CLIENT_RESULT, result)); #define INDEFINITE_TIME ((time_t)(-1)) @@ -106,6 +112,9 @@ #ifndef DONT_USE_UPLOADTOBLOB IOTHUB_CLIENT_LL_UPLOADTOBLOB_HANDLE uploadToBlobHandle; #endif +#ifdef USE_EDGE_MODULES + IOTHUB_CLIENT_EDGE_HANDLE methodHandle; +#endif uint32_t data_msg_id; bool complete_twin_update_encountered; IOTHUB_AUTHORIZATION_HANDLE authorization_module; @@ -125,6 +134,119 @@ static const char PROVISIONING_TOKEN[] = "UseProvisioning"; static const char PROVISIONING_ACCEPTABLE_VALUE[] = "true"; + +#ifdef USE_EDGE_MODULES +/*The following section should be moved to iothub_module_client_ll.c during impending refactor*/ + +static const char* ENVIRONMENT_VAR_EDGEHUBCONNECTIONSTRING = "EdgeHubConnectionString"; +static const char* ENVIRONMENT_VAR_EDGEAUTHSCHEME = "IOTEDGE_AUTHSCHEME"; +static const char* ENVIRONMENT_VAR_EDGEDEVICEID = "IOTEDGE_DEVICEID"; +static const char* ENVIRONMENT_VAR_EDGEMODULEID = "IOTEDGE_MODULEID"; +static const char* ENVIRONMENT_VAR_EDGEHUBHOSTNAME = "IOTEDGE_IOTHUBHOSTNAME"; +static const char* ENVIRONMENT_VAR_EDGEGATEWAYHOST = "IOTEDGE_GATEWAYHOSTNAME"; + +static const char* SAS_TOKEN_AUTH = "sasToken"; + +typedef struct EDGE_ENVIRONMENT_VARIABLES_TAG +{ + const char* connection_string; + const char* auth_scheme; + const char* device_id; + const char* iothub_name; + const char* iothub_suffix; + const char* gatewayhostname; + const char* module_id; + char* iothub_buffer; +} EDGE_ENVIRONMENT_VARIABLES; + +static int retrieve_edge_environment_variabes(EDGE_ENVIRONMENT_VARIABLES *edge_environment_variables) +{ + int result; + const char* edgehubhostname; + char* edgehubhostname_separator; + + if ((edge_environment_variables->connection_string = environment_get_variable(ENVIRONMENT_VAR_EDGEHUBCONNECTIONSTRING)) != NULL) + { + // If a connection string is set, we use it and ignore all other environment variables. + result = 0; + } + else + { + if ((edge_environment_variables->auth_scheme = environment_get_variable(ENVIRONMENT_VAR_EDGEAUTHSCHEME)) == NULL) + { + LogError("Environment %s not set", ENVIRONMENT_VAR_EDGEAUTHSCHEME); + result = __FAILURE__; + } + else if (strcmp(edge_environment_variables->auth_scheme, SAS_TOKEN_AUTH) != 0) + { + LogError("Environment %s was set to %s, but only support for %s", ENVIRONMENT_VAR_EDGEAUTHSCHEME, edge_environment_variables->auth_scheme, SAS_TOKEN_AUTH); + result = __FAILURE__; + } + else if ((edge_environment_variables->device_id = environment_get_variable(ENVIRONMENT_VAR_EDGEDEVICEID)) == NULL) + { + LogError("Environment %s not set", ENVIRONMENT_VAR_EDGEDEVICEID); + result = __FAILURE__; + } + else if ((edgehubhostname = environment_get_variable(ENVIRONMENT_VAR_EDGEHUBHOSTNAME)) == NULL) + { + LogError("Environment %s not set", ENVIRONMENT_VAR_EDGEHUBHOSTNAME); + result = __FAILURE__; + } + else if ((edge_environment_variables->gatewayhostname = environment_get_variable(ENVIRONMENT_VAR_EDGEGATEWAYHOST)) == NULL) + { + LogError("Environment %s not set", ENVIRONMENT_VAR_EDGEGATEWAYHOST); + result = __FAILURE__; + } + else if ((edge_environment_variables->module_id = environment_get_variable(ENVIRONMENT_VAR_EDGEMODULEID)) == NULL) + { + LogError("Environment %s not set", ENVIRONMENT_VAR_EDGEMODULEID); + result = __FAILURE__; + } + // Make a copy of just ENVIRONMENT_VAR_EDGEHUBHOSTNAME. We need to make changes in place (namely inserting a '\0') + // and can't do this with system environment variable safely. + else if (mallocAndStrcpy_s(&edge_environment_variables->iothub_buffer, edgehubhostname) != 0) + { + LogError("Unable to copy buffer"); + result = __FAILURE__; + } + else if ((edgehubhostname_separator = strchr(edge_environment_variables->iothub_buffer, '.')) == NULL) + { + LogError("Environment edgehub %s invalid, requires '.' separator", edge_environment_variables->iothub_buffer); + result = __FAILURE__; + } + else if (*(edgehubhostname_separator + 1) == 0) + { + LogError("Environment edgehub %s invalid, no content after '.' separator", edge_environment_variables->iothub_buffer); + result = __FAILURE__; + } + else + { + edge_environment_variables->iothub_name = edge_environment_variables->iothub_buffer; + *edgehubhostname_separator = 0; + edge_environment_variables->iothub_suffix = edgehubhostname_separator + 1; + result = 0; + } + } + + return result; +} + +IOTHUB_CLIENT_EDGE_HANDLE IoTHubClientCore_LL_GetEdgeHandle(IOTHUB_CLIENT_CORE_LL_HANDLE iotHubClientHandle) +{ + IOTHUB_CLIENT_EDGE_HANDLE result; + if (iotHubClientHandle != NULL) + { + result = iotHubClientHandle->methodHandle; + } + else + { + result = NULL; + } + + return result; +} +#endif /* USE_EDGE_MODULES */ + static void setTransportProtocol(IOTHUB_CLIENT_CORE_LL_HANDLE_DATA* handleData, TRANSPORT_PROVIDER* protocol) { handleData->IoTHubTransport_SendMessageDisposition = protocol->IoTHubTransport_SendMessageDisposition; @@ -155,6 +277,45 @@ free(client_item); } +static int create_edge_handle(IOTHUB_CLIENT_CORE_LL_HANDLE_DATA* handle_data, const IOTHUB_CLIENT_CONFIG* config, const char* module_id) +{ + int result; + (void)config; + (void)module_id; +#ifdef USE_EDGE_MODULES + /* There is no way to currently distinguish a regular module from a edge module, so this handle is created regardless of if appropriate. + However, as a gateway hostname is required in order to create an Edge Handle, we need to at least make sure that exists + in order to prevent errors. + + The end result is that all edge modules will have an EdgeHandle, but only some non-edge modules will have it. + Regardless, non-edge modules will never be able to use the handle. + */ + if (config->protocolGatewayHostName != NULL) + { + handle_data->methodHandle = IoTHubClient_EdgeHandle_Create(config, handle_data->authorization_module, module_id); + + if (handle_data->methodHandle == NULL) + { + LogError("Unable to IoTHubModuleClient_LL_MethodHandle_Create"); + result = __FAILURE__; + } + else + { + result = 0; + } + } + else + { + result = 0; + } + +#else + (void)handle_data; + result = 0; +#endif + return result; +} + static int create_blob_upload_module(IOTHUB_CLIENT_CORE_LL_HANDLE_DATA* handle_data, const IOTHUB_CLIENT_CONFIG* config) { int result; @@ -186,6 +347,14 @@ #endif } +static void destroy_module_method_module(IOTHUB_CLIENT_CORE_LL_HANDLE_DATA* handle_data) +{ + (void)handle_data; +#ifdef USE_EDGE_MODULES + IoTHubClient_EdgeHandle_Destroy(handle_data->methodHandle); +#endif +} + /*Codes_SRS_IOTHUBCLIENT_LL_10_032: ["product_info" - takes a char string as an argument to specify the product information(e.g. `"ProductName/ProductVersion"`). ]*/ /*Codes_SRS_IOTHUBCLIENT_LL_10_034: ["product_info" - shall store the given string concatenated with the sdk information and the platform information in the form(ProductInfo DeviceSDKName / DeviceSDKVersion(OSName OSVersion; Architecture). ]*/ static STRING_HANDLE make_product_info(const char* product) @@ -293,6 +462,7 @@ /*Codes_SRS_IOTHUBCLIENT_LL_02_007: [If the underlaying layer _Create function fails them IoTHubClientCore_LL_Create shall fail and return NULL.] */ LogError("underlying transport failed"); destroy_blob_upload_module(result); + destroy_module_method_module(result); tickcounter_destroy(result->tickCounter); IoTHubClient_Auth_Destroy(result->authorization_module); STRING_delete(product_info); @@ -400,6 +570,18 @@ free(result); result = NULL; } + else if ((module_id != NULL) && create_edge_handle(result, config, module_id) != 0) + { + LogError("unable to create module method handle"); + if (!result->isSharedTransport) + { + result->IoTHubTransport_Destroy(result->transportHandle); + } + IoTHubClient_Auth_Destroy(result->authorization_module); + STRING_delete(product_info); + free(result); + result = NULL; + } else { if ((result->tickCounter = tickcounter_create()) == NULL) @@ -411,6 +593,7 @@ result->IoTHubTransport_Destroy(result->transportHandle); } destroy_blob_upload_module(result); + destroy_module_method_module(result); IoTHubClient_Auth_Destroy(result->authorization_module); STRING_delete(product_info); free(result); @@ -445,6 +628,7 @@ result->IoTHubTransport_Destroy(result->transportHandle); } destroy_blob_upload_module(result); + destroy_module_method_module(result); tickcounter_destroy(result->tickCounter); STRING_delete(product_info); free(result); @@ -470,6 +654,7 @@ result->IoTHubTransport_Destroy(result->transportHandle); } destroy_blob_upload_module(result); + destroy_module_method_module(result); tickcounter_destroy(result->tickCounter); STRING_delete(product_info); free(result); @@ -989,37 +1174,64 @@ } #ifdef USE_EDGE_MODULES -IOTHUB_CLIENT_CORE_LL_HANDLE IoTHubClientCore_LL_CreateFromEnvironment(const IOTHUB_CLIENT_CONFIG* config, const char* module_id) +IOTHUB_CLIENT_CORE_LL_HANDLE IoTHubClientCore_LL_CreateFromEnvironment(IOTHUB_CLIENT_TRANSPORT_PROVIDER protocol) { - IOTHUB_CLIENT_CORE_LL_HANDLE result; + IOTHUB_CLIENT_CORE_LL_HANDLE_DATA* result; + EDGE_ENVIRONMENT_VARIABLES edge_environment_variables; + + memset(&edge_environment_variables, 0, sizeof(edge_environment_variables)); - if (module_id == NULL) + if (retrieve_edge_environment_variabes(&edge_environment_variables) != 0) { - LogError("module_id cannot be NULL"); + LogError("retrieve_edge_environment_variabes failed"); + result = NULL; + } + // The presence of a connection string environment variable means we use it, ignoring other settings + else if (edge_environment_variables.connection_string != NULL) + { + result = IoTHubClientCore_LL_CreateFromConnectionString(edge_environment_variables.connection_string, protocol); + } + else if (iothub_security_init(IOTHUB_SECURITY_TYPE_HTTP_EDGE) != 0) + { + LogError("iothub_security_init failed"); result = NULL; } - else if ((result = IoTHubClientCore_LL_CreateImpl(config, module_id, true)) != NULL) + else { - // Because the Edge Hub almost always use self-signed certificates, we need - // to query it for the the certificate its using so we can trust it. - char* trustedCertificate = IoTHubClient_Auth_Get_TrustBundle(result->authorization_module); - IOTHUB_CLIENT_RESULT setTrustResult; + IOTHUB_CLIENT_CONFIG client_config; - if (trustedCertificate == NULL) + memset(&client_config, 0, sizeof(client_config)); + client_config.protocol = protocol; + client_config.deviceId = edge_environment_variables.device_id; + client_config.iotHubName = edge_environment_variables.iothub_name; + client_config.iotHubSuffix = edge_environment_variables.iothub_suffix; + client_config.protocolGatewayHostName = edge_environment_variables.gatewayhostname; + + if ((result = IoTHubClientCore_LL_CreateImpl(&client_config, edge_environment_variables.module_id, true)) != NULL) { - LogError("IoTHubClient_Auth_Get_TrustBundle failed"); - IoTHubClientCore_LL_Destroy(result); - result = NULL; + // Because the Edge Hub almost always use self-signed certificates, we need + // to query it for the the certificate its using so we can trust it. + char* trustedCertificate = IoTHubClient_Auth_Get_TrustBundle(result->authorization_module); + IOTHUB_CLIENT_RESULT setTrustResult; + + if (trustedCertificate == NULL) + { + LogError("IoTHubClient_Auth_Get_TrustBundle failed"); + IoTHubClientCore_LL_Destroy(result); + result = NULL; + } + else if ((setTrustResult = IoTHubClientCore_LL_SetOption(result, OPTION_TRUSTED_CERT, trustedCertificate)) != IOTHUB_CLIENT_OK) + { + LogError("IoTHubClientCore_LL_SetOption failed, err = %d", setTrustResult); + IoTHubClientCore_LL_Destroy(result); + result = NULL; + } + + free(trustedCertificate); } - else if ((setTrustResult = IoTHubClientCore_LL_SetOption(result, OPTION_TRUSTED_CERT, trustedCertificate)) != IOTHUB_CLIENT_OK) - { - LogError("IoTHubClientCore_LL_SetOption failed, err = %d", setTrustResult); - IoTHubClientCore_LL_Destroy(result); - result = NULL; - } + } - free(trustedCertificate); - } + free(edge_environment_variables.iothub_buffer); return result; } #endif @@ -1095,6 +1307,9 @@ #ifndef DONT_USE_UPLOADTOBLOB IoTHubClient_LL_UploadToBlob_Destroy(handleData->uploadToBlobHandle); #endif +#ifdef USE_EDGE_MODULES + IoTHubClient_EdgeHandle_Destroy(handleData->methodHandle); +#endif STRING_delete(handleData->product_info); free(handleData); } @@ -2595,6 +2810,31 @@ return IoTHubClientCore_LL_SetInputMessageCallbackImpl(iotHubClientHandle, inputName, eventHandlerCallback, NULL, userContextCallback, NULL, 0); } +#ifdef USE_EDGE_MODULES +/* These should be replaced during iothub_client refactor */ +IOTHUB_CLIENT_RESULT IoTHubClientCore_LL_GenericMethodInvoke(IOTHUB_CLIENT_CORE_LL_HANDLE iotHubClientHandle, const char* deviceId, const char* moduleId, const char* methodName, const char* methodPayload, unsigned int timeout, int* responseStatus, unsigned char** responsePayload, size_t* responsePayloadSize) +{ + IOTHUB_CLIENT_RESULT result; + if (iotHubClientHandle != NULL) + { + if (moduleId != NULL) + { + result = IoTHubClient_Edge_ModuleMethodInvoke(iotHubClientHandle->methodHandle, deviceId, moduleId, methodName, methodPayload, timeout, responseStatus, responsePayload, responsePayloadSize); + } + else + { + result = IoTHubClient_Edge_DeviceMethodInvoke(iotHubClientHandle->methodHandle, deviceId, methodName, methodPayload, timeout, responseStatus, responsePayload, responsePayloadSize); + } + } + else + { + result = IOTHUB_CLIENT_INVALID_ARG; + } + return result; +} +#endif + +/*end*/ #endif /* DONT_USE_UPLOADTOBLOB */