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

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 */