testtools/iothub_test/src/iothub_account.c (1,089 lines of code) (raw):
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#ifdef __cplusplus
#include <cstdio>
#include <cstdlib>
#include <cstddef>
#include <cstring>
#else
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#endif
#include "azure_c_shared_utility/optimize_size.h"
#include "azure_c_shared_utility/string_tokenizer.h"
#include "iothub_account.h"
#include "azure_c_shared_utility/httpapiexsas.h"
#include "azure_c_shared_utility/azure_base64.h"
#include "azure_c_shared_utility/hmacsha256.h"
#include "azure_c_shared_utility/xlogging.h"
#include "azure_c_shared_utility/sastoken.h"
#include "azure_c_shared_utility/httpapiexsas.h"
#include "azure_c_shared_utility/uniqueid.h"
#include "azure_c_shared_utility/threadapi.h"
#include "azure_c_shared_utility/random.h"
#include "iothub_service_client_auth.h"
#include "iothub_registrymanager.h"
static const char* SAS_DEVICE_PREFIX_FMT = "csdk_e2eDevice_sas_j_please_delete_%s";
static const char* X509_DEVICE_PREFIX_FMT = "csdk_e2eDevice_x509_j_please_delete_%s";
static const char* DEFAULT_CONSUMER_GROUP = "$Default";
static const int DEFAULT_PARTITION_COUNT = 16;
static const char* TEST_MODULE_NAME = "TestModule";
static const char* TEST_MANAGED_BY_1 = "TestManagedBy1";
static const char* TEST_MANAGED_BY_2 = "TestManagedBy2";
static const char* CONN_HOST_PART = "HostName=";
static const char* CONN_DEVICE_PART = ";DeviceId=";
static const char* CONN_KEY_PART = ";SharedAccessKey=";
static const char* CONN_X509_PART = ";x509=true";
static const char* CONN_MODULE_PART = ";ModuleId=";
#define DEVICE_GUID_SIZE 37
const int TEST_CREATE_MAX_RETRIES = 10;
const int TEST_DELETE_MAX_RETRIES = 10;
const int TEST_METHOD_INVOKE_MAX_RETRIES = 3;
const int TEST_SLEEP_THROTTLE_MSEC = 5 * 1000;
const int TEST_SLEEP_AFTER_CREATED_DEVICE_MSEC = 30 * 1000;
const int TEST_SLEEP_BETWEEN_CREATION_FAILURES_MSEC = 30 * 1000;
const int TEST_SLEEP_BETWEEN_DELETE_FAILURES_MSEC = 30 * 1000;
const int TEST_SLEEP_BETWEEN_METHOD_INVOKE_FAILURES_MSEC = 30 * 1000;
#define NSLOOKUP_MAX_COMMAND_SIZE 128
typedef struct IOTHUB_ACCOUNT_INFO_TAG
{
const char* connString;
const char* eventhubConnString;
char* hostname;
char* iothubName;
char* iothubSuffix;
char* sharedAccessKey;
char* sharedAccessToken;
char* keyName;
char* eventhubAccessKey;
char* x509Certificate;
char* x509PrivateKey;
char* x509Thumbprint;
size_t number_of_sas_devices;
IOTHUB_PROVISIONED_DEVICE** sasDevices;
IOTHUB_PROVISIONED_DEVICE x509Device;
IOTHUB_SERVICE_CLIENT_AUTH_HANDLE iothub_service_client_auth_handle;
IOTHUB_REGISTRYMANAGER_HANDLE iothub_registrymanager_handle;
IOTHUB_MESSAGING_HANDLE iothub_messaging_handle;
} IOTHUB_ACCOUNT_INFO;
static int generateDeviceName(const char* prefix, char** deviceName)
{
int result;
char deviceGuid[DEVICE_GUID_SIZE];
if (UniqueId_Generate(deviceGuid, DEVICE_GUID_SIZE) != UNIQUEID_OK)
{
LogError("Unable to generate unique Id.\r\n");
result = MU_FAILURE;
}
else
{
size_t len = strlen(prefix) + DEVICE_GUID_SIZE;
*deviceName = (char*)malloc(len + 1);
if (*deviceName == NULL)
{
LogError("Failure allocating device ID.\r\n");
result = MU_FAILURE;
}
else
{
if (sprintf_s(*deviceName, len + 1, prefix, deviceGuid) <= 0)
{
LogError("Failure constructing device ID.\r\n");
free(*deviceName);
result = MU_FAILURE;
}
else
{
LogInfo("Generated unique device name %s.", *deviceName);
result = 0;
}
}
}
return result;
}
static int retrieveConnStringInfo(IOTHUB_ACCOUNT_INFO* accountInfo)
{
int result;
int beginName, endName, beginIothub, endIothub, beginHost, endHost, beginKey;
size_t totalLen = strlen(accountInfo->connString);
if (sscanf(accountInfo->connString, "HostName=%n%*[^.]%n.%n%*[^;];%nSharedAccessKeyName=%n%*[^;];%nSharedAccessKey=%n", &beginHost, &endHost, &beginIothub, &endIothub, &beginName, &endName, &beginKey) != 0)
{
LogError("Failure determining the string length parameters.\r\n");
result = MU_FAILURE;
}
else if ((accountInfo->iothubName = (char*)malloc(endHost - beginHost + 1)) == NULL)
{
LogError("Failure allocating iothubName.\r\n");
result = MU_FAILURE;
}
else if ((accountInfo->hostname = (char*)malloc(endIothub - beginHost + 1)) == NULL)
{
LogError("Failure allocating hostname.\r\n");
result = MU_FAILURE;
}
else if ((accountInfo->keyName = (char*)malloc(endName - beginName + 1)) == NULL)
{
LogError("Failure allocating keyname.\r\n");
result = MU_FAILURE;
}
else if ((accountInfo->sharedAccessKey = (char*)malloc(totalLen + 1 - beginKey + 1)) == NULL)
{
LogError("Failure allocating sharedAccessKey.\r\n");
result = MU_FAILURE;
}
else if (sscanf(accountInfo->connString, "HostName=%[^.].%[^;];SharedAccessKeyName=%[^;];SharedAccessKey=%s", accountInfo->iothubName,
accountInfo->hostname + endHost - beginHost + 1,
accountInfo->keyName,
accountInfo->sharedAccessKey) != 4)
{
LogError("Failure determining the string values.\r\n");
result = MU_FAILURE;
}
else
{
(void)strcpy(accountInfo->hostname, accountInfo->iothubName);
accountInfo->hostname[endHost - beginHost] = '.';
if (mallocAndStrcpy_s(&accountInfo->iothubSuffix, accountInfo->hostname + endHost - beginHost + 1) != 0)
{
LogError("[IoTHubAccount] Failure constructing the iothubSuffix.");
result = MU_FAILURE;
}
else
{
result = 0;
}
}
if (result != 0)
{
free(accountInfo->iothubName);
accountInfo->iothubName = NULL;
free(accountInfo->hostname);
accountInfo->hostname = NULL;
free(accountInfo->keyName);
accountInfo->keyName = NULL;
free(accountInfo->sharedAccessKey);
accountInfo->sharedAccessKey = NULL;
}
return result;
}
static int createSASConnectionString(IOTHUB_ACCOUNT_INFO* accountInfo, const char* deviceId, const char* moduleId, const char* primaryAuthentication, char** connectionString) {
int result;
char* conn;
size_t sizeOfHostPart = strlen(CONN_HOST_PART);
size_t sizeOfDevicePart = strlen(CONN_DEVICE_PART);
size_t sizeOfKeyPart = strlen(CONN_KEY_PART);
size_t sizeOfHostName = strlen(accountInfo->hostname);
size_t sizeOfDeviceId = strlen(deviceId);
size_t sizeOfDeviceKey = strlen(primaryAuthentication);
size_t sizeOfModulePart = moduleId ? strlen(CONN_MODULE_PART) : 0;
size_t sizeOfModuleId = moduleId ? strlen(moduleId) : 0;
size_t connectionStringLength = sizeOfHostPart + sizeOfDevicePart + sizeOfKeyPart + sizeOfHostName + sizeOfDeviceId + sizeOfDeviceKey + sizeOfModulePart + sizeOfModuleId + 1;
conn = (char*)malloc(connectionStringLength);
if (conn == NULL)
{
LogError("Failed to allocate space for the SAS based connection string\r\n");
result = MU_FAILURE;
}
else if ((moduleId == NULL) && sprintf_s(conn, connectionStringLength, "%s%s%s%s%s%s", CONN_HOST_PART, accountInfo->hostname, CONN_DEVICE_PART, (char*)deviceId, CONN_KEY_PART, (char*)primaryAuthentication) <= 0)
{
LogError("Failed to form the connection string for SAS based connection string.\r\n");
free(conn);
result = MU_FAILURE;
}
else if ((moduleId != NULL) && sprintf_s(conn, connectionStringLength, "%s%s%s%s%s%s%s%s", CONN_HOST_PART, accountInfo->hostname, CONN_DEVICE_PART, (char*)deviceId, CONN_KEY_PART, (char*)primaryAuthentication, CONN_MODULE_PART, moduleId) <= 0)
{
LogError("Failed to form the connection string for SAS based connection string.\r\n");
free(conn);
result = MU_FAILURE;
}
else
{
*connectionString = conn;
result = 0;
}
return result;
}
static int createX509ConnectionString(IOTHUB_ACCOUNT_INFO* accountInfo, char** connectionString) {
int result;
char* conn;
size_t sizeOfHostPart = strlen(CONN_HOST_PART);
size_t sizeOfDevicePart = strlen(CONN_DEVICE_PART);
size_t sizeOfX509Part = strlen(CONN_X509_PART);
size_t sizeOfHostName = strlen(accountInfo->hostname);
size_t sizeOfDeviceId = strlen(accountInfo->x509Device.deviceId);
size_t connectionStringLength = sizeOfHostPart + sizeOfDevicePart + sizeOfX509Part + sizeOfHostName + sizeOfDeviceId + 1;
conn = (char*)malloc(connectionStringLength);
if (conn == NULL)
{
LogError("Failed to allocate space for the SAS based connection string\r\n");
result = MU_FAILURE;
}
else if (sprintf_s(conn, connectionStringLength, "%s%s%s%s%s", CONN_HOST_PART, accountInfo->hostname, CONN_DEVICE_PART, (char*)accountInfo->x509Device.deviceId, CONN_X509_PART) <= 0)
{
LogError("Failed to form the connection string for x509 based connection string.\r\n");
result = MU_FAILURE;
free(conn);
}
else
{
*connectionString = conn;
result = 0;
}
return result;
}
static void freeDeviceInfoFields(IOTHUB_DEVICE* deviceInfo)
{
free((char*)deviceInfo->deviceId);
free((char*)deviceInfo->primaryKey);
free((char*)deviceInfo->secondaryKey);
free((char*)deviceInfo->generationId);
free((char*)deviceInfo->eTag);
free((char*)deviceInfo->connectionStateUpdatedTime);
free((char*)deviceInfo->statusReason);
free((char*)deviceInfo->statusUpdatedTime);
free((char*)deviceInfo->lastActivityTime);
free((char*)deviceInfo->configuration);
free((char*)deviceInfo->deviceProperties);
free((char*)deviceInfo->serviceProperties);
memset(deviceInfo, 0, sizeof(*deviceInfo));
}
// The SLA for IoT Hub - especially around device creation - versus amount of stress that our gate runs
// put on hub means that we need to have a basic retry on creation.
static IOTHUB_REGISTRYMANAGER_RESULT createTestDeviceWithRetry(IOTHUB_REGISTRYMANAGER_HANDLE iothub_registrymanager_handle, IOTHUB_REGISTRY_DEVICE_CREATE* deviceCreateInfo, IOTHUB_DEVICE* deviceInfo)
{
IOTHUB_REGISTRYMANAGER_RESULT result = IOTHUB_REGISTRYMANAGER_ERROR;
int creationAttempts = 0;
bool doesDeviceExist = false;
ThreadAPI_Sleep(TEST_SLEEP_THROTTLE_MSEC + RANDOM_generate() % 10); // prevent Too Many Requests (429) error from service
while (true)
{
if (doesDeviceExist == false)
{
LogInfo("Invoking registry manager to create device %s", deviceCreateInfo->deviceId);
result = IoTHubRegistryManager_CreateDevice(iothub_registrymanager_handle, deviceCreateInfo, deviceInfo);
if (result == IOTHUB_REGISTRYMANAGER_OK)
{
LogInfo("Device created with status %s", MU_ENUM_TO_STRING(IOTHUB_REGISTRYMANAGER_RESULT, result));
ThreadAPI_Sleep(TEST_SLEEP_AFTER_CREATED_DEVICE_MSEC); // allow ARM cache to update
break;
}
else if (result == IOTHUB_REGISTRYMANAGER_DEVICE_EXIST)
{
doesDeviceExist = true;
}
}
if (doesDeviceExist)
{
ThreadAPI_Sleep(TEST_SLEEP_AFTER_CREATED_DEVICE_MSEC); // allow ARM cache to update
LogInfo("Invoking registry manager to get device %s", deviceCreateInfo->deviceId);
result = IoTHubRegistryManager_GetDevice(iothub_registrymanager_handle, deviceCreateInfo->deviceId, deviceInfo);
if (result == IOTHUB_REGISTRYMANAGER_OK)
{
break;
}
}
creationAttempts++;
if (creationAttempts == TEST_CREATE_MAX_RETRIES)
{
LogError("Creating device %s failed with error %s (%d). Exhausted retry attempts. Failing test", deviceCreateInfo->deviceId, MU_ENUM_TO_STRING(IOTHUB_REGISTRYMANAGER_RESULT, result), result);
break;
}
LogError("Creating device %s failed with error %s (%d). Sleeping %d milliseconds", deviceCreateInfo->deviceId, MU_ENUM_TO_STRING(IOTHUB_REGISTRYMANAGER_RESULT, result), result, TEST_SLEEP_BETWEEN_CREATION_FAILURES_MSEC);
ThreadAPI_Sleep(TEST_SLEEP_BETWEEN_CREATION_FAILURES_MSEC + RANDOM_generate() % 15); // sleep with jitter
}
return result;
}
// Provides same functionality as createTestDeviceWithRetry, except with modules instead of devices.
static IOTHUB_REGISTRYMANAGER_RESULT createTestModuleWithRetry(IOTHUB_REGISTRYMANAGER_HANDLE iothub_registrymanager_handle, IOTHUB_REGISTRY_MODULE_CREATE* moduleCreateInfo, IOTHUB_MODULE* moduleInfo)
{
IOTHUB_REGISTRYMANAGER_RESULT result = IOTHUB_REGISTRYMANAGER_ERROR;
int creationAttempts = 0;
bool doesDeviceExist = false;
ThreadAPI_Sleep(TEST_SLEEP_THROTTLE_MSEC + RANDOM_generate() % 10); // prevent Too Many Requests (429) error from service
while (true)
{
if (doesDeviceExist == false)
{
LogInfo("Invoking registry manager to create device/module %s/%s", moduleCreateInfo->deviceId, moduleCreateInfo->moduleId);
result = IoTHubRegistryManager_CreateModule(iothub_registrymanager_handle, moduleCreateInfo, moduleInfo);
if (result == IOTHUB_REGISTRYMANAGER_OK)
{
break;
}
}
if (result == IOTHUB_REGISTRYMANAGER_DEVICE_EXIST)
{
doesDeviceExist = true;
ThreadAPI_Sleep(TEST_SLEEP_AFTER_CREATED_DEVICE_MSEC); // allow ARM cache to update
LogInfo("Invoking registry manager to get device/module %s/%s", moduleCreateInfo->deviceId, moduleCreateInfo->moduleId);
result = IoTHubRegistryManager_GetModule(iothub_registrymanager_handle, moduleCreateInfo->deviceId, moduleCreateInfo->moduleId, moduleInfo);
if (result == IOTHUB_REGISTRYMANAGER_OK)
{
break;
}
}
creationAttempts++;
if (creationAttempts == TEST_CREATE_MAX_RETRIES)
{
LogError("Creating device/module %s/%s failed with error %d. Exhausted retry attempts. Failing test", moduleCreateInfo->deviceId, moduleCreateInfo->moduleId, result);
break;
}
LogError("Creating device/module %s/%s failed with error %d. Sleeping %d milliseconds", moduleCreateInfo->deviceId, moduleCreateInfo->moduleId, result, TEST_SLEEP_BETWEEN_CREATION_FAILURES_MSEC);
ThreadAPI_Sleep(TEST_SLEEP_BETWEEN_CREATION_FAILURES_MSEC + RANDOM_generate() % 15); // sleep with jitter
}
return result;
}
static int provisionDevice(IOTHUB_ACCOUNT_INFO* accountInfo, IOTHUB_ACCOUNT_AUTH_METHOD method, IOTHUB_PROVISIONED_DEVICE* deviceToProvision)
{
int result;
char* deviceId = NULL;
if (generateDeviceName(method == IOTHUB_ACCOUNT_AUTH_CONNSTRING ? (SAS_DEVICE_PREFIX_FMT) : (X509_DEVICE_PREFIX_FMT), &deviceId) != 0)
{
LogError("generateDeviceName failed\r\n");
result = MU_FAILURE;
}
else
{
IOTHUB_REGISTRYMANAGER_RESULT iothub_registrymanager_result;
IOTHUB_REGISTRY_DEVICE_CREATE deviceCreateInfo;
IOTHUB_DEVICE deviceInfo;
memset(&deviceCreateInfo, 0, sizeof(deviceCreateInfo));
memset(&deviceInfo, 0, sizeof(deviceInfo));
deviceCreateInfo.deviceId = deviceId;
if (method == IOTHUB_ACCOUNT_AUTH_CONNSTRING)
{
deviceToProvision->howToCreate = IOTHUB_ACCOUNT_AUTH_CONNSTRING;
deviceCreateInfo.primaryKey = "";
deviceCreateInfo.secondaryKey = "";
deviceCreateInfo.authMethod = IOTHUB_REGISTRYMANAGER_AUTH_SPK;
}
else
{
deviceToProvision->howToCreate = IOTHUB_ACCOUNT_AUTH_X509;
deviceCreateInfo.primaryKey = accountInfo->x509Thumbprint;
deviceCreateInfo.secondaryKey = "";
deviceCreateInfo.authMethod = IOTHUB_REGISTRYMANAGER_AUTH_X509_THUMBPRINT;
}
iothub_registrymanager_result = createTestDeviceWithRetry(accountInfo->iothub_registrymanager_handle, &deviceCreateInfo, &deviceInfo);
if (iothub_registrymanager_result != IOTHUB_REGISTRYMANAGER_OK)
{
LogError("IoTHubRegistryManager_CreateDevice failed\r\n");
free(deviceId);
result = MU_FAILURE;
}
else
{
deviceToProvision->deviceId = deviceId;
if (method == IOTHUB_ACCOUNT_AUTH_CONNSTRING)
{
if (mallocAndStrcpy_s((char**)&deviceToProvision->primaryAuthentication, (char*)deviceInfo.primaryKey) != 0)
{
LogError("mallocAndStrcpy_s failed for primaryKey\r\n");
result = MU_FAILURE;
}
else
{
if (createSASConnectionString(accountInfo, deviceToProvision->deviceId, NULL, deviceToProvision->primaryAuthentication, &deviceToProvision->connectionString) != 0)
{
result = MU_FAILURE;
}
else
{
result = 0;
}
}
}
else if (createX509ConnectionString(accountInfo, &deviceToProvision->connectionString) != 0)
{
result = MU_FAILURE;
}
else
{
deviceToProvision->certificate = accountInfo->x509Certificate;
deviceToProvision->primaryAuthentication = accountInfo->x509PrivateKey;
result = 0;
}
}
freeDeviceInfoFields(&deviceInfo);
}
return result;
}
static int provisionDevices(IOTHUB_ACCOUNT_INFO* accountInfo, IOTHUB_ACCOUNT_AUTH_METHOD method, IOTHUB_PROVISIONED_DEVICE** devicesToProvision, size_t number_of_devices)
{
int result;
size_t iterator;
result = 0;
for (iterator = 0; iterator < number_of_devices; iterator++)
{
if ((devicesToProvision[iterator] = malloc(sizeof(IOTHUB_PROVISIONED_DEVICE))) == NULL)
{
LogError("Failed creating IOTHUB_PROVISIONED_DEVICE instance for device number %lu", (unsigned long)iterator);
result = MU_FAILURE;
break;
}
else
{
memset(devicesToProvision[iterator], 0, sizeof(IOTHUB_PROVISIONED_DEVICE));
if (provisionDevice(accountInfo, method, devicesToProvision[iterator]) != 0)
{
LogError("Failed provisioning device number %lu", (unsigned long)iterator);
result = MU_FAILURE;
break;
}
}
}
return result;
}
static int updateTestModule(IOTHUB_REGISTRYMANAGER_HANDLE iothub_registrymanager_handle, IOTHUB_PROVISIONED_DEVICE* deviceToProvision)
{
int result;
IOTHUB_REGISTRY_MODULE_UPDATE moduleUpdate;
IOTHUB_MODULE moduleInfo;
// Update the auth method type from its initially set NONE to IOTHUB_REGISTRYMANAGER_AUTH_SPK.
moduleUpdate.version = IOTHUB_REGISTRY_MODULE_UPDATE_VERSION_1;
moduleUpdate.authMethod = IOTHUB_REGISTRYMANAGER_AUTH_SPK;
moduleUpdate.deviceId = deviceToProvision->deviceId;
moduleUpdate.primaryKey = "";
moduleUpdate.secondaryKey = "";
moduleUpdate.moduleId = TEST_MODULE_NAME;
moduleUpdate.status = IOTHUB_DEVICE_STATUS_ENABLED;
moduleUpdate.managedBy = TEST_MANAGED_BY_2;
memset(&moduleInfo, 0, sizeof(moduleInfo));
moduleInfo.version = IOTHUB_MODULE_VERSION_1;
IOTHUB_REGISTRYMANAGER_RESULT iothub_registrymanager_result = IoTHubRegistryManager_UpdateModule(iothub_registrymanager_handle, &moduleUpdate);
if (iothub_registrymanager_result != IOTHUB_REGISTRYMANAGER_OK)
{
LogError("IoTHubRegistryManager_UpdateModule failed, err=%d", iothub_registrymanager_result);
result = MU_FAILURE;
}
else if ((iothub_registrymanager_result = IoTHubRegistryManager_GetModule(iothub_registrymanager_handle, deviceToProvision->deviceId, deviceToProvision->moduleId, &moduleInfo)) != IOTHUB_REGISTRYMANAGER_OK)
{
LogError("IoTHubRegistryManager_UpdateModule(deviceId=%s, moduleId=%s) failed, err=%d", deviceToProvision->deviceId, deviceToProvision->moduleId, iothub_registrymanager_result);
result = MU_FAILURE;
}
else if (strcmp(moduleInfo.moduleId, TEST_MODULE_NAME) != 0)
{
LogError("ModuleName expected (%s) does not match what was returned from IoTHubRegistryManager_CreateModule (%s)", TEST_MODULE_NAME, moduleInfo.moduleId);
result = MU_FAILURE;
}
else if (strcmp(deviceToProvision->deviceId, moduleInfo.deviceId) != 0)
{
LogError("DeviceId expected (%s) does not match what was returned from IoTHubRegistryManager_CreateModule (%s)", deviceToProvision->deviceId, moduleInfo.deviceId);
result = MU_FAILURE;
}
else if (strcmp(moduleInfo.managedBy, TEST_MANAGED_BY_2) != 0)
{
LogError("IoTHubRegistryManager_UpdateModule sets managedBy=%s, expected=%s", moduleInfo.managedBy, TEST_MANAGED_BY_2);
result = MU_FAILURE;
}
else
{
result = 0;
}
IoTHubRegistryManager_FreeModuleMembers(&moduleInfo);
return result;
}
static int updateTestModuleWithRetry(IOTHUB_REGISTRYMANAGER_HANDLE iothub_registrymanager_handle, IOTHUB_PROVISIONED_DEVICE* deviceToProvision)
{
int result;
int attempts = 0;
while (true)
{
LogInfo("Attempting to update test module on %s/%s", deviceToProvision->deviceId, deviceToProvision->moduleId);
if ((result = updateTestModule(iothub_registrymanager_handle, deviceToProvision)) == 0)
{
break;
}
attempts++;
if (attempts == TEST_CREATE_MAX_RETRIES)
{
LogError("Updating device/module %s/%s failed with error %d. Exhausted retry attempts. Failing test", deviceToProvision->deviceId, deviceToProvision->moduleId, result);
break;
}
LogError("Updating device/module %s/%s failed with error %d. Sleeping %d milliseconds", deviceToProvision->deviceId, deviceToProvision->moduleId, result, TEST_SLEEP_BETWEEN_CREATION_FAILURES_MSEC);
ThreadAPI_Sleep(TEST_SLEEP_BETWEEN_CREATION_FAILURES_MSEC + RANDOM_generate() % 15);
}
return result;
}
static int provisionModule(IOTHUB_ACCOUNT_INFO* accountInfo, IOTHUB_PROVISIONED_DEVICE* deviceToProvision)
{
IOTHUB_REGISTRY_MODULE_CREATE moduleCreate;
IOTHUB_MODULE moduleInfo;
int result;
// We set the initial auth type to none, to simulate and test more closely how Edge
// modules are created. We'll upgrade this to SPK after creation.
moduleCreate.version = IOTHUB_REGISTRY_MODULE_CREATE_VERSION_1;
moduleCreate.authMethod = IOTHUB_REGISTRYMANAGER_AUTH_NONE;
moduleCreate.deviceId = deviceToProvision->deviceId;
moduleCreate.moduleId = TEST_MODULE_NAME;
moduleCreate.primaryKey = "";
moduleCreate.secondaryKey = "";
moduleCreate.managedBy = TEST_MANAGED_BY_1;
// Even though we already have a IOTHUB_SERVICE_CLIENT_AUTH_HANDLE handle (iothub_account_info->iothub_service_client_auth_handle), we get a
// new one based on the device's connection string (not the Hub) as we need to test this scenario, too.
IOTHUB_SERVICE_CLIENT_AUTH_HANDLE service_auth_from_device_connection = NULL;
IOTHUB_REGISTRYMANAGER_RESULT iothub_registrymanager_result;
IOTHUB_REGISTRYMANAGER_HANDLE iothub_registrymanager_handle = NULL;
memset(&moduleInfo, 0, sizeof(moduleInfo));
moduleInfo.version = IOTHUB_MODULE_VERSION_1;
if (mallocAndStrcpy_s(&deviceToProvision->moduleId, TEST_MODULE_NAME) != 0)
{
LogError("mallocAndStrcpy_s failed");
result = MU_FAILURE;
}
else if ((service_auth_from_device_connection = IoTHubServiceClientAuth_CreateFromConnectionString(deviceToProvision->connectionString)) == NULL)
{
LogError("IoTHubServiceClientAuth_CreateFromConnectionString(%s) failed", deviceToProvision->connectionString);
result = MU_FAILURE;
}
else if ((iothub_registrymanager_handle = IoTHubRegistryManager_Create(service_auth_from_device_connection)) == NULL)
{
LogError("IoTHubServiceClientAuth_CreateFromConnectionString(%s) failed", deviceToProvision->connectionString);
result = MU_FAILURE;
}
else if ((iothub_registrymanager_result = createTestModuleWithRetry(iothub_registrymanager_handle, &moduleCreate, &moduleInfo)) != IOTHUB_REGISTRYMANAGER_OK)
{
LogError("IoTHubRegistryManager_CreateModule failed, err=%d", iothub_registrymanager_result);
result = MU_FAILURE;
}
else if (strcmp(moduleInfo.moduleId, TEST_MODULE_NAME) != 0)
{
LogError("ModuleName expected (%s) does not match what was returned from IoTHubRegistryManager_CreateModule (%s)", TEST_MODULE_NAME, moduleInfo.moduleId);
result = MU_FAILURE;
}
else if (strcmp(deviceToProvision->deviceId, moduleInfo.deviceId) != 0)
{
LogError("DeviceId expected (%s) does not match what was returned from IoTHubRegistryManager_CreateModule (%s)", deviceToProvision->deviceId, moduleInfo.deviceId);
result = MU_FAILURE;
}
else if (strcmp(TEST_MANAGED_BY_1, moduleInfo.managedBy) != 0)
{
LogError("managedBy expected (%s) does not match what was returned from IoTHubRegistryManager_CreateModule (%s)", TEST_MANAGED_BY_1, moduleInfo.managedBy);
result = MU_FAILURE;
}
else if (updateTestModuleWithRetry(iothub_registrymanager_handle, deviceToProvision) != 0)
{
LogError("Unable to update test module");
result = MU_FAILURE;
}
else if (createSASConnectionString(accountInfo, deviceToProvision->deviceId, TEST_MODULE_NAME, deviceToProvision->primaryAuthentication, &deviceToProvision->moduleConnectionString) != 0)
{
LogError("createSASConnectionStringForModule failed");
result = MU_FAILURE;
}
else
{
LogInfo("Created device/module = %s/%s", deviceToProvision->deviceId, TEST_MODULE_NAME);
result = 0;
}
IoTHubRegistryManager_FreeModuleMembers(&moduleInfo);
if (iothub_registrymanager_handle != NULL)
{
IoTHubRegistryManager_Destroy(iothub_registrymanager_handle);
}
if (service_auth_from_device_connection != NULL)
{
IoTHubServiceClientAuth_Destroy(service_auth_from_device_connection);
}
return result;
}
static char* convert_base64_to_string(const char* base64_cert)
{
char* result;
BUFFER_HANDLE raw_cert = Azure_Base64_Decode(base64_cert);
if (raw_cert == NULL)
{
LogError("Failure decoding base64 encoded cert.\r\n");
result = NULL;
}
else
{
STRING_HANDLE cert = STRING_from_byte_array(BUFFER_u_char(raw_cert), BUFFER_length(raw_cert));
if (cert == NULL)
{
LogError("Failure creating cert from binary.\r\n");
result = NULL;
}
else
{
if (mallocAndStrcpy_s(&result, STRING_c_str(cert)) != 0)
{
LogError("Failure allocating certificate.\r\n");
result = NULL;
}
STRING_delete(cert);
}
BUFFER_delete(raw_cert);
}
return result;
}
IOTHUB_ACCOUNT_INFO_HANDLE IoTHubAccount_Init_With_Config(IOTHUB_ACCOUNT_CONFIG* config, bool testingModules)
{
IOTHUB_ACCOUNT_INFO* iothub_account_info;
int result;
if (config == NULL)
{
LogError("[IoTHubAccount] invalid configuration (NULL)");
result = MU_FAILURE;
iothub_account_info = NULL;
}
else
{
if ((iothub_account_info = calloc(1, sizeof(IOTHUB_ACCOUNT_INFO))) == NULL)
{
LogError("[IoTHubAccount] Failed allocating IOTHUB_ACCOUNT_INFO.");
result = MU_FAILURE;
}
else if ((iothub_account_info->sasDevices = (IOTHUB_PROVISIONED_DEVICE**)malloc(sizeof(IOTHUB_PROVISIONED_DEVICE*) * config->number_of_sas_devices)) == NULL)
{
LogError("[IoTHubAccount] Failed allocating array for SAS devices");
result = MU_FAILURE;
}
else
{
char* base64_cert;
char* base64_key;
char* tempThumb;
iothub_account_info->number_of_sas_devices = config->number_of_sas_devices;
memset(iothub_account_info->sasDevices, 0, sizeof(IOTHUB_PROVISIONED_DEVICE*) * iothub_account_info->number_of_sas_devices);
iothub_account_info->connString = getenv("IOTHUB_CONNECTION_STRING");
iothub_account_info->eventhubConnString = getenv("IOTHUB_EVENTHUB_CONNECTION_STRING");
base64_cert = getenv("IOTHUB_E2E_X509_CERT_BASE64");
base64_key = getenv("IOTHUB_E2E_X509_PRIVATE_KEY_BASE64");
tempThumb = getenv("IOTHUB_E2E_X509_THUMBPRINT");
if (iothub_account_info->connString == NULL)
{
LogError("Failure retrieving IoT Hub connection string from the environment.\r\n");
result = MU_FAILURE;
}
else if (iothub_account_info->eventhubConnString == NULL)
{
LogError("Failure retrieving Event Hub connection string from the environment.\r\n");
result = MU_FAILURE;
}
else if (base64_cert == NULL)
{
LogError("Failure retrieving x509 certificate from the environment.\r\n");
result = MU_FAILURE;
}
else if (base64_key == NULL)
{
LogError("Failure retrieving x509 private key from the environment.\r\n");
result = MU_FAILURE;
}
else if (tempThumb == NULL)
{
LogError("Failure retrieving x509 certificate thumbprint from the environment.\r\n");
result = MU_FAILURE;
}
else if ((iothub_account_info->x509Certificate = convert_base64_to_string(base64_cert)) == NULL)
{
LogError("Failure allocating x509 certificate from the environment.\r\n");
result = MU_FAILURE;
}
else if ((iothub_account_info->x509PrivateKey = convert_base64_to_string(base64_key)) == NULL)
{
LogError("Failure allocating x509 key from the environment.\r\n");
result = MU_FAILURE;
}
else if (mallocAndStrcpy_s(&iothub_account_info->x509Thumbprint, tempThumb) != 0)
{
LogError("Failure allocating x509 thumbprint from the environment.\r\n");
result = MU_FAILURE;
}
else if (retrieveConnStringInfo(iothub_account_info) != 0)
{
LogError("retrieveConnStringInfo failed.\r\n");
result = MU_FAILURE;
}
else if ((iothub_account_info->iothub_service_client_auth_handle = IoTHubServiceClientAuth_CreateFromConnectionString(iothub_account_info->connString)) == NULL)
{
LogError("IoTHubServiceClientAuth_CreateFromConnectionString failed.\r\n");
result = MU_FAILURE;
}
else if ((iothub_account_info->iothub_messaging_handle = IoTHubMessaging_LL_Create(iothub_account_info->iothub_service_client_auth_handle)) == NULL)
{
LogError("IoTHubMessaging_LL_Create failed\r\n");
result = MU_FAILURE;
}
else if ((iothub_account_info->iothub_registrymanager_handle = IoTHubRegistryManager_Create(iothub_account_info->iothub_service_client_auth_handle)) == NULL)
{
LogError("IoTHubRegistryManager_Create failed\r\n");
result = MU_FAILURE;
}
else if (provisionDevices(iothub_account_info, IOTHUB_ACCOUNT_AUTH_CONNSTRING, iothub_account_info->sasDevices, iothub_account_info->number_of_sas_devices) != 0)
{
LogError("Failed to create the SAS device(s)\r\n");
result = MU_FAILURE;
}
else if (provisionDevice(iothub_account_info, IOTHUB_ACCOUNT_AUTH_X509, &iothub_account_info->x509Device) != 0)
{
LogError("Failed to create the X509 device\r\n");
result = MU_FAILURE;
}
else if (testingModules && provisionModule(iothub_account_info, iothub_account_info->sasDevices[0]))
{
LogError("Failed to create the module\r\n");
result = MU_FAILURE;
}
else
{
result = 0;
}
}
}
if (result != 0)
{
IoTHubAccount_deinit(iothub_account_info);
iothub_account_info = NULL;
}
return (IOTHUB_ACCOUNT_INFO_HANDLE)iothub_account_info;
}
IOTHUB_ACCOUNT_INFO_HANDLE IoTHubAccount_Init(bool testingModules)
{
IOTHUB_ACCOUNT_CONFIG config;
config.number_of_sas_devices = 1;
return IoTHubAccount_Init_With_Config(&config, testingModules);
}
void IoTHubAccount_deinit(IOTHUB_ACCOUNT_INFO_HANDLE acctHandle)
{
if (acctHandle != NULL)
{
IOTHUB_ACCOUNT_INFO* acctInfo = (IOTHUB_ACCOUNT_INFO*)acctHandle;
size_t iterator;
IOTHUB_REGISTRYMANAGER_RESULT iothub_registrymanager_result;
for (iterator = 0; iterator < acctInfo->number_of_sas_devices; iterator++)
{
IOTHUB_PROVISIONED_DEVICE* provisioned_device = acctInfo->sasDevices[iterator];
if (provisioned_device != NULL)
{
if (provisioned_device->deviceId != NULL)
{
for (int i = 0; i < TEST_DELETE_MAX_RETRIES; i++)
{
iothub_registrymanager_result = IoTHubRegistryManager_DeleteDevice(acctInfo->iothub_registrymanager_handle, provisioned_device->deviceId);
if (iothub_registrymanager_result == IOTHUB_REGISTRYMANAGER_OK)
{
break;
}
LogError("IoTHubRegistryManager_DeleteDevice failed (%d) for SAS Based Device \"%s\"\r\n", i, provisioned_device->deviceId);
ThreadAPI_Sleep(TEST_SLEEP_BETWEEN_DELETE_FAILURES_MSEC + RANDOM_generate() % 15); // sleep with jitter
}
}
free(provisioned_device->deviceId);
free(provisioned_device->moduleId);
free(provisioned_device->primaryAuthentication);
free(provisioned_device->connectionString);
free(provisioned_device->moduleConnectionString);
free(provisioned_device);
}
}
free(acctInfo->sasDevices);
if (acctInfo->x509Device.deviceId)
{
iothub_registrymanager_result = IoTHubRegistryManager_DeleteDevice(acctInfo->iothub_registrymanager_handle, acctInfo->x509Device.deviceId);
if (iothub_registrymanager_result != IOTHUB_REGISTRYMANAGER_OK)
{
LogError("IoTHubRegistryManager_DeleteDevice failed for x509 Based Device\r\n");
}
}
IoTHubMessaging_LL_Destroy(acctInfo->iothub_messaging_handle);
IoTHubRegistryManager_Destroy(acctInfo->iothub_registrymanager_handle);
IoTHubServiceClientAuth_Destroy(acctInfo->iothub_service_client_auth_handle);
free(acctInfo->hostname);
free(acctInfo->iothubName);
free(acctInfo->iothubSuffix);
free(acctInfo->sharedAccessKey);
free(acctInfo->sharedAccessToken);
free(acctInfo->keyName);
free(acctInfo->eventhubAccessKey);
free(acctInfo->x509Device.deviceId);
free(acctInfo->x509Device.connectionString);
free(acctInfo->x509Certificate);
free(acctInfo->x509PrivateKey);
free(acctInfo->x509Thumbprint);
free(acctInfo);
}
}
const char* IoTHubAccount_GetEventHubConnectionString(IOTHUB_ACCOUNT_INFO_HANDLE acctHandle)
{
const char* result = NULL;
IOTHUB_ACCOUNT_INFO* acctInfo = (IOTHUB_ACCOUNT_INFO*)acctHandle;
if (acctInfo != NULL)
{
result = acctInfo->eventhubConnString;
}
return result;
}
const char* IoTHubAccount_GetIoTHostName(IOTHUB_ACCOUNT_INFO_HANDLE acctHandle)
{
const char* result = NULL;
IOTHUB_ACCOUNT_INFO* acctInfo = (IOTHUB_ACCOUNT_INFO*)acctHandle;
if (acctInfo != NULL)
{
result = acctInfo->hostname;
}
return result;
}
const char* IoTHubAccount_GetIoTHubName(IOTHUB_ACCOUNT_INFO_HANDLE acctHandle)
{
const char* result = NULL;
IOTHUB_ACCOUNT_INFO* acctInfo = (IOTHUB_ACCOUNT_INFO*)acctHandle;
if (acctInfo != NULL)
{
result = acctInfo->iothubName;
}
return result;
}
const char* IoTHubAccount_GetIoTHubSuffix(IOTHUB_ACCOUNT_INFO_HANDLE acctHandle)
{
const char* result = NULL;
IOTHUB_ACCOUNT_INFO* acctInfo = (IOTHUB_ACCOUNT_INFO*)acctHandle;
if (acctInfo != NULL)
{
result = acctInfo->iothubSuffix;
}
return result;
}
const char* IoTHubAccount_GetEventhubListenName(IOTHUB_ACCOUNT_INFO_HANDLE acctHandle)
{
static char listenName[64];
const char* value;
if ((value = getenv("IOTHUB_EVENTHUB_LISTEN_NAME")) == NULL)
{
value = IoTHubAccount_GetIoTHubName(acctHandle);
}
if (value != NULL &&
sprintf_s(listenName, 64, "%s", value) <= 0)
{
LogError("Failed reading IoT Hub Event Hub listen namespace (sprintf_s failed).");
}
return listenName;
}
IOTHUB_PROVISIONED_DEVICE* IoTHubAccount_GetSASDevice(IOTHUB_ACCOUNT_INFO_HANDLE acctHandle)
{
if (acctHandle != NULL)
{
return acctHandle->sasDevices[0];
}
else
{
return NULL;
}
}
IOTHUB_PROVISIONED_DEVICE** IoTHubAccount_GetSASDevices(IOTHUB_ACCOUNT_INFO_HANDLE acctHandle)
{
if (acctHandle != NULL)
{
return acctHandle->sasDevices;
}
else
{
return NULL;
}
}
IOTHUB_PROVISIONED_DEVICE* IoTHubAccount_GetX509Device(IOTHUB_ACCOUNT_INFO_HANDLE acctHandle)
{
if (acctHandle != NULL)
{
return &acctHandle->x509Device;
}
else
{
return NULL;
}
}
IOTHUB_PROVISIONED_DEVICE* IoTHubAccount_GetDevice(IOTHUB_ACCOUNT_INFO_HANDLE acctHandle, IOTHUB_ACCOUNT_AUTH_METHOD accountAuthMethod)
{
if (accountAuthMethod == IOTHUB_ACCOUNT_AUTH_X509)
{
return IoTHubAccount_GetX509Device(acctHandle);
}
else if (accountAuthMethod == IOTHUB_ACCOUNT_AUTH_CONNSTRING)
{
return IoTHubAccount_GetSASDevice(acctHandle);
}
else
{
return NULL;
}
}
const char* IoTHubAccount_GetIoTHubConnString(IOTHUB_ACCOUNT_INFO_HANDLE acctHandle)
{
if (acctHandle != NULL)
{
return ((IOTHUB_ACCOUNT_INFO*)acctHandle)->connString;
}
else
{
return NULL;
}
}
const char* IoTHubAccount_GetSharedAccessSignature(IOTHUB_ACCOUNT_INFO_HANDLE acctHandle)
{
const char* result = NULL;
IOTHUB_ACCOUNT_INFO* acctInfo = (IOTHUB_ACCOUNT_INFO*)acctHandle;
if (acctInfo != NULL)
{
if (acctInfo->sharedAccessToken != NULL)
{
// Reuse the sharedAccessToken if it's been created already
result = acctInfo->sharedAccessToken;
}
else
{
time_t currentTime = time(NULL);
size_t expiry_time = (size_t)(currentTime + 3600);
STRING_HANDLE accessKey = STRING_construct(acctInfo->sharedAccessKey);
STRING_HANDLE iotName = STRING_construct(acctInfo->hostname);
STRING_HANDLE keyName = STRING_construct(acctInfo->keyName);
if (accessKey != NULL && iotName != NULL && keyName != NULL)
{
STRING_HANDLE sasHandle = SASToken_Create(accessKey, iotName, keyName, expiry_time);
if (sasHandle == NULL)
{
result = NULL;
}
else
{
if (mallocAndStrcpy_s(&acctInfo->sharedAccessToken, STRING_c_str(sasHandle)) != 0)
{
result = NULL;
}
else
{
result = acctInfo->sharedAccessToken;
}
STRING_delete(sasHandle);
}
}
STRING_delete(accessKey);
STRING_delete(iotName);
STRING_delete(keyName);
}
}
else
{
result = NULL;
}
return result;
}
const char* IoTHubAccount_GetEventhubAccessKey(IOTHUB_ACCOUNT_INFO_HANDLE acctHandle)
{
const char* iothub_connection_string;
static char access_key[128];
if ((iothub_connection_string = IoTHubAccount_GetIoTHubConnString(acctHandle)) != NULL)
{
STRING_HANDLE iothub_connection_string_str;
if ((iothub_connection_string_str = STRING_construct(iothub_connection_string)) != NULL)
{
STRING_TOKENIZER_HANDLE tokenizer;
if ((tokenizer = STRING_TOKENIZER_create(iothub_connection_string_str)) != NULL)
{
STRING_HANDLE tokenString;
if ((tokenString = STRING_new()) != NULL)
{
STRING_HANDLE valueString;
if ((valueString = STRING_new()) != NULL)
{
while ((STRING_TOKENIZER_get_next_token(tokenizer, tokenString, "=") == 0))
{
char tokenValue[128];
strcpy(tokenValue, STRING_c_str(tokenString));
if (STRING_TOKENIZER_get_next_token(tokenizer, tokenString, ";") != 0)
{
break;
}
if (strcmp(tokenValue, "SharedAccessKey") == 0)
{
strcpy(access_key, STRING_c_str(tokenString));
break;
}
}
STRING_delete(valueString);
}
STRING_delete(tokenString);
}
STRING_TOKENIZER_destroy(tokenizer);
}
STRING_delete(iothub_connection_string_str);
}
}
return access_key;
}
const char* IoTHubAccount_GetEventhubConsumerGroup(IOTHUB_ACCOUNT_INFO_HANDLE acctHandle)
{
(void)acctHandle;
static char consumerGroup[64];
char* envVarValue = getenv("IOTHUB_EVENTHUB_CONSUMER_GROUP");
if (envVarValue != NULL)
{
strcpy(consumerGroup, envVarValue);
}
else
{
strcpy(consumerGroup, DEFAULT_CONSUMER_GROUP);
}
return consumerGroup;
}
const size_t IoTHubAccount_GetIoTHubPartitionCount(IOTHUB_ACCOUNT_INFO_HANDLE acctHandle)
{
int value;
(void)acctHandle;
char* envVarValue = getenv("IOTHUB_PARTITION_COUNT");
if (envVarValue != NULL)
{
value = atoi(envVarValue);
}
else
{
value = DEFAULT_PARTITION_COUNT;
}
return (size_t)value;
}
const IOTHUB_MESSAGING_HANDLE IoTHubAccount_GetMessagingHandle(IOTHUB_ACCOUNT_INFO_HANDLE acctHandle)
{
IOTHUB_MESSAGING_HANDLE result = NULL;
IOTHUB_ACCOUNT_INFO* acctInfo = (IOTHUB_ACCOUNT_INFO*)acctHandle;
if (acctInfo != NULL)
{
result = acctInfo->iothub_messaging_handle;
}
return result;
}
IOTHUB_GATEWAY_VERSION IoTHubAccount_GetIoTHubVersion(IOTHUB_ACCOUNT_INFO_HANDLE acctHandle)
{
IOTHUB_GATEWAY_VERSION result = IOTHUB_GATEWAY_VERSION_UNDEFINED;
const char* iotHubFqdn = IoTHubAccount_GetIoTHostName(acctHandle);
LogInfo("nslookup: check %s", iotHubFqdn);
if (iotHubFqdn != NULL)
{
const char* IoTHubGwV1Suffix = "ihsu-";
const char* IoTHubGwV2Suffix = "gateway-prod-gw-";
const char* nslookup_fmt = "nslookup %s";
char command[NSLOOKUP_MAX_COMMAND_SIZE];
char stdoutLine[128];
int commandLength = snprintf(command, NSLOOKUP_MAX_COMMAND_SIZE, nslookup_fmt, iotHubFqdn);
if (commandLength > 0 && commandLength < NSLOOKUP_MAX_COMMAND_SIZE)
{
#if defined(__APPLE__) || defined(AZIOT_LINUX)
FILE* stdOut = popen(command, "r");
#else
FILE* stdOut = _popen(command, "r");
#endif
while (fgets(stdoutLine, sizeof(stdoutLine), stdOut) != NULL)
{
LogInfo("nslookup: %s", stdoutLine);
if (strstr(stdoutLine, "Name:") == stdoutLine)
{
if (strstr(stdoutLine, IoTHubGwV1Suffix) != NULL)
{
result = IOTHUB_GATEWAY_VERSION_1;
}
else if (strstr(stdoutLine, IoTHubGwV2Suffix) != NULL)
{
result = IOTHUB_GATEWAY_VERSION_2;
}
break;
}
}
#if defined(__APPLE__) || defined(AZIOT_LINUX)
(void)pclose(stdOut);
#else
(void)_pclose(stdOut);
#endif
}
}
return result;
}