Microsoft Azure IoTHub client MQTT transport
Dependents: STM32F746_iothub_client_sample_mqtt FXOS8700CQ_To_Azure_IoT f767zi_mqtt FXOS8700CQ_To_Azure_IoT ... more
Diff: iothubtransportmqtt.c
- Revision:
- 7:7fdd306e6224
- Parent:
- 6:16875b609849
- Child:
- 8:418a3b812584
diff -r 16875b609849 -r 7fdd306e6224 iothubtransportmqtt.c --- a/iothubtransportmqtt.c Mon Jul 18 16:44:47 2016 -0700 +++ b/iothubtransportmqtt.c Fri Jul 29 15:53:03 2016 -0700 @@ -67,11 +67,35 @@ static TICK_COUNTER_HANDLE g_msgTickCounter; +typedef enum MQTT_TRANSPORT_CREDENTIAL_TYPE_TAG +{ + CREDENTIAL_NOT_BUILD, + X509, + SAS_TOKEN_FROM_USER, + DEVICE_KEY, +} MQTT_TRANSPORT_CREDENTIAL_TYPE; + +typedef struct MQTT_TRANSPORT_CREDENTIALS_TAG +{ + MQTT_TRANSPORT_CREDENTIAL_TYPE credential_type; + union + { + // Key associated to the device to be used. + STRING_HANDLE deviceKey; + + // SAS associated to the device to be used. + STRING_HANDLE deviceSasToken; + + } CREDENTIAL_VALUE; +} MQTT_TRANSPORT_CREDENTIALS; + typedef struct MQTTTRANSPORT_HANDLE_DATA_TAG { STRING_HANDLE device_id; - STRING_HANDLE device_key; - STRING_HANDLE sasTokenSr; + STRING_HANDLE devicesPath; + + MQTT_TRANSPORT_CREDENTIALS transport_creds; + STRING_HANDLE mqttEventTopic; STRING_HANDLE mqttMessageTopic; STRING_HANDLE hostAddress; @@ -85,7 +109,6 @@ int portNum; MQTT_CLIENT_HANDLE mqttClient; uint16_t packetId; - bool sasTokenFromUser; bool isRegistered; bool connected; bool subscribed; @@ -619,10 +642,6 @@ { int result; - // Construct SAS token - size_t secSinceEpoch = (size_t)(difftime(get_time(NULL), EPOCH_TIME_T_VALUE) + 0); - size_t expiryTime = secSinceEpoch + SAS_TOKEN_DEFAULT_LIFETIME; - // Not checking the success of this variable, if fail it will fail in the SASToken creation and return false; STRING_HANDLE emptyKeyName = STRING_new(); if (emptyKeyName == NULL) @@ -632,47 +651,57 @@ else { STRING_HANDLE sasToken = NULL; - if (transportState->sasTokenFromUser) + + switch (transportState->transport_creds.credential_type) { - sasToken = STRING_clone(transportState->sasTokenSr); - } - else - { - sasToken = SASToken_Create(transportState->device_key, transportState->sasTokenSr, emptyKeyName, expiryTime); + case SAS_TOKEN_FROM_USER: + sasToken = STRING_clone(transportState->transport_creds.CREDENTIAL_VALUE.deviceSasToken); + break; + case DEVICE_KEY: + { + // Construct SAS token + size_t secSinceEpoch = (size_t)(difftime(get_time(NULL), EPOCH_TIME_T_VALUE) + 0); + size_t expiryTime = secSinceEpoch + SAS_TOKEN_DEFAULT_LIFETIME; + + sasToken = SASToken_Create(transportState->transport_creds.CREDENTIAL_VALUE.deviceKey, transportState->devicesPath, emptyKeyName, expiryTime); + break; + } + case X509: + default: + // The assumption here is that x509 is in place, if not setup + // correctly the connection will be rejected. + sasToken = NULL; + break; } - if (sasToken == NULL) + MQTT_CLIENT_OPTIONS options = { 0 }; + options.clientId = (char*)STRING_c_str(transportState->device_id); + options.willMessage = NULL; + options.username = (char*)STRING_c_str(transportState->configPassedThroughUsername); + if (sasToken != NULL) { - result = __LINE__; + options.password = (char*)STRING_c_str(sasToken); + } + options.keepAliveInterval = transportState->keepAliveValue; + options.useCleanSession = false; + options.qualityOfServiceValue = DELIVER_AT_LEAST_ONCE; + + if (GetTransportProviderIfNecessary(transportState) == 0) + { + if (mqtt_client_connect(transportState->mqttClient, transportState->xioTransport, &options) != 0) + { + LogError("failure connecting to address %s:%d.", STRING_c_str(transportState->hostAddress), transportState->portNum); + result = __LINE__; + } + else + { + (void)tickcounter_get_current_ms(g_msgTickCounter, &transportState->mqtt_connect_time); + result = 0; + } } else { - MQTT_CLIENT_OPTIONS options = { 0 }; - options.clientId = (char*)STRING_c_str(transportState->device_id); - options.willMessage = NULL; - options.username = (char*)STRING_c_str(transportState->configPassedThroughUsername); - options.password = (char*)STRING_c_str(sasToken); - options.keepAliveInterval = transportState->keepAliveValue; - options.useCleanSession = false; - options.qualityOfServiceValue = DELIVER_AT_LEAST_ONCE; - - if (GetTransportProviderIfNecessary(transportState) == 0) - { - if (mqtt_client_connect(transportState->mqttClient, transportState->xioTransport, &options) != 0) - { - LogError("failure connecting to address %s:%d.", STRING_c_str(transportState->hostAddress), transportState->portNum); - result = __LINE__; - } - else - { - (void)tickcounter_get_current_ms(g_msgTickCounter, &transportState->mqtt_connect_time); - result = 0; - } - } - else - { - result = __LINE__; - } + result = __LINE__; } STRING_delete(emptyKeyName); STRING_delete(sasToken); @@ -761,6 +790,52 @@ return result; } +static int construct_credential_information(const IOTHUB_CLIENT_CONFIG* upperConfig, PMQTTTRANSPORT_HANDLE_DATA transportState) +{ + int result; + if (upperConfig->deviceKey != NULL) + { + transportState->transport_creds.CREDENTIAL_VALUE.deviceKey = STRING_construct(upperConfig->deviceKey); + if (transportState->transport_creds.CREDENTIAL_VALUE.deviceKey == NULL) + { + LogError("Could not create device key for MQTT"); + result = __LINE__; + } + else if ( (transportState->devicesPath = ConstructSasToken(upperConfig->iotHubName, upperConfig->iotHubSuffix, upperConfig->deviceId)) == NULL) + { + STRING_delete(transportState->transport_creds.CREDENTIAL_VALUE.deviceKey); + result = __LINE__; + } + else + { + transportState->transport_creds.credential_type = DEVICE_KEY; + result = 0; + } + } + else if (upperConfig->deviceSasToken != NULL) + { + transportState->transport_creds.CREDENTIAL_VALUE.deviceSasToken = STRING_construct(upperConfig->deviceSasToken); + if (transportState->transport_creds.CREDENTIAL_VALUE.deviceSasToken == NULL) + { + result = __LINE__; + } + else + { + transportState->transport_creds.credential_type = SAS_TOKEN_FROM_USER; + transportState->devicesPath = NULL; + result = 0; + } + } + else + { + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_041: [If both deviceKey and deviceSasToken fields are NULL then IoTHubTransportMqtt_Create shall assume a x509 authentication.] */ + transportState->transport_creds.credential_type = X509; + transportState->devicesPath = NULL; + result = 0; + } + return result; +} + static PMQTTTRANSPORT_HANDLE_DATA InitializeTransportHandleData(const IOTHUB_CLIENT_CONFIG* upperConfig, PDLIST_ENTRY waitingToSend) { PMQTTTRANSPORT_HANDLE_DATA state = (PMQTTTRANSPORT_HANDLE_DATA)malloc(sizeof(MQTTTRANSPORT_HANDLE_DATA)); @@ -770,42 +845,30 @@ } else if ((state->device_id = STRING_construct(upperConfig->deviceId)) == NULL) { + LogError("failure constructing device_id."); free(state); state = NULL; } else { - state->device_key = NULL; - state->sasTokenSr = NULL; - - if ((upperConfig->deviceKey != NULL) && ((state->device_key = STRING_construct(upperConfig->deviceKey)) == NULL)) - { - LogError("Could not create device key for MQTT"); - STRING_delete(state->device_id); - free(state); - state = NULL; - } - else if ((upperConfig->deviceSasToken != NULL) && ((state->sasTokenSr = STRING_construct(upperConfig->deviceSasToken)) == NULL)) + if (construct_credential_information(upperConfig, state) != 0) { - LogError("Could not create Sas Token Sr String"); STRING_delete(state->device_id); - STRING_delete(state->device_key); - free(state); - state = NULL; - } - else if ((upperConfig->deviceSasToken == NULL) && ((state->sasTokenSr = ConstructSasToken(upperConfig->iotHubName, upperConfig->iotHubSuffix, upperConfig->deviceId)) == NULL)) - { - LogError("Could not create Sas Token Sr String."); - STRING_delete(state->device_id); - STRING_delete(state->device_key); free(state); state = NULL; } else if ((state->mqttEventTopic = ConstructEventTopic(upperConfig->deviceId)) == NULL) { LogError("Could not create mqttEventTopic for MQTT"); - STRING_delete(state->sasTokenSr); - STRING_delete(state->device_key); + STRING_delete(state->devicesPath); + if (state->transport_creds.credential_type == DEVICE_KEY) + { + STRING_delete(state->transport_creds.CREDENTIAL_VALUE.deviceKey); + } + else if (state->transport_creds.credential_type == SAS_TOKEN_FROM_USER) + { + STRING_delete(state->transport_creds.CREDENTIAL_VALUE.deviceSasToken); + } STRING_delete(state->device_id); free(state); state = NULL; @@ -813,9 +876,16 @@ else if ((state->mqttMessageTopic = ConstructMessageTopic(upperConfig->deviceId)) == NULL) { LogError("Could not create mqttMessageTopic for MQTT"); + STRING_delete(state->devicesPath); + if (state->transport_creds.credential_type == DEVICE_KEY) + { + STRING_delete(state->transport_creds.CREDENTIAL_VALUE.deviceKey); + } + else if (state->transport_creds.credential_type == SAS_TOKEN_FROM_USER) + { + STRING_delete(state->transport_creds.CREDENTIAL_VALUE.deviceSasToken); + } STRING_delete(state->mqttEventTopic); - STRING_delete(state->sasTokenSr); - STRING_delete(state->device_key); STRING_delete(state->device_id); free(state); state = NULL; @@ -825,10 +895,18 @@ state->mqttClient = mqtt_client_init(MqttRecvCallback, MqttOpCompleteCallback, state); if (state->mqttClient == NULL) { + LogError("failure initializing mqtt client."); + STRING_delete(state->devicesPath); + if (state->transport_creds.credential_type == DEVICE_KEY) + { + STRING_delete(state->transport_creds.CREDENTIAL_VALUE.deviceKey); + } + else if (state->transport_creds.credential_type == SAS_TOKEN_FROM_USER) + { + STRING_delete(state->transport_creds.CREDENTIAL_VALUE.deviceSasToken); + } STRING_delete(state->mqttEventTopic); STRING_delete(state->mqttMessageTopic); - STRING_delete(state->sasTokenSr); - STRING_delete(state->device_key); STRING_delete(state->device_id); free(state); state = NULL; @@ -836,26 +914,40 @@ else { /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_008: [The hostname shall be constructed using the iothubname and iothubSuffix.] */ - // TODO: need to strip the ssl or http or tls char tempAddress[DEFAULT_TEMP_STRING_LEN]; (void)snprintf(tempAddress, DEFAULT_TEMP_STRING_LEN, "%s.%s", upperConfig->iotHubName, upperConfig->iotHubSuffix); if ((state->hostAddress = STRING_construct(tempAddress)) == NULL) { + LogError("failure constructing host address."); + STRING_delete(state->devicesPath); + if (state->transport_creds.credential_type == DEVICE_KEY) + { + STRING_delete(state->transport_creds.CREDENTIAL_VALUE.deviceKey); + } + else if (state->transport_creds.credential_type == SAS_TOKEN_FROM_USER) + { + STRING_delete(state->transport_creds.CREDENTIAL_VALUE.deviceSasToken); + } STRING_delete(state->mqttEventTopic); STRING_delete(state->mqttMessageTopic); - STRING_delete(state->sasTokenSr); - STRING_delete(state->device_key); STRING_delete(state->device_id); free(state); state = NULL; } else if ((state->configPassedThroughUsername = buildConfigForUsername(upperConfig)) == NULL) { + STRING_delete(state->devicesPath); + if (state->transport_creds.credential_type == DEVICE_KEY) + { + STRING_delete(state->transport_creds.CREDENTIAL_VALUE.deviceKey); + } + else if (state->transport_creds.credential_type == SAS_TOKEN_FROM_USER) + { + STRING_delete(state->transport_creds.CREDENTIAL_VALUE.deviceSasToken); + } STRING_delete(state->hostAddress); STRING_delete(state->mqttEventTopic); STRING_delete(state->mqttMessageTopic); - STRING_delete(state->sasTokenSr); - STRING_delete(state->device_key); STRING_delete(state->device_id); free(state); state = NULL; @@ -864,7 +956,6 @@ { /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_010: [IoTHubTransportMqtt_Create shall allocate memory to save its internal state where all topics, hostname, device_id, device_key, sasTokenSr and client handle shall be saved.] */ DList_InitializeListHead(&(state->waitingForAck)); - state->sasTokenFromUser = (upperConfig->deviceSasToken == NULL) ? false : true; state->destroyCalled = false; state->isRegistered = false; state->subscribed = false; @@ -899,8 +990,13 @@ } /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_002: [If the parameter config's variables upperConfig or waitingToSend are NULL then IoTHubTransportMqtt_Create shall return NULL.] */ /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_003: [If the upperConfig's variables deviceId, both deviceKey and deviceSasToken, iotHubName, protocol, or iotHubSuffix are NULL then IoTHubTransportMqtt_Create shall return NULL.] */ - else if (config->upperConfig == NULL || config->upperConfig->protocol == NULL || config->upperConfig->deviceId == NULL || ((config->upperConfig->deviceKey == NULL) && (config->upperConfig->deviceSasToken == NULL)) || - config->upperConfig->iotHubName == NULL || config->upperConfig->iotHubSuffix == NULL) + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_03_003: [If both deviceKey & deviceSasToken fields are NOT NULL then IoTHubTransportMqtt_Create shall return NULL.] */ + else if (config->upperConfig == NULL || + config->upperConfig->protocol == NULL || + config->upperConfig->deviceId == NULL || + ((config->upperConfig->deviceKey != NULL) && (config->upperConfig->deviceSasToken != NULL)) || + config->upperConfig->iotHubName == NULL || + config->upperConfig->iotHubSuffix == NULL) { LogError("Invalid Argument: upperConfig structure contains an invalid parameter"); result = NULL; @@ -929,14 +1025,13 @@ LogError("Invalid Argument: deviceSasToken is empty"); result = NULL; } - /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_03_003: [If both deviceKey & deviceSasToken fields are NOT NULL then IoTHubTransportMqtt_Create shall return NULL.] */ - else if ((config->upperConfig->deviceKey != NULL) && (config->upperConfig->deviceSasToken != NULL)) + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_003: [If the upperConfig's variables deviceId, deviceKey, iotHubName, protocol, or iotHubSuffix are NULL then IoTHubTransportMqtt_Create shall return NULL.] */ + else if (strlen(config->upperConfig->iotHubName) == 0) { - LogError("Invalid Argument: cannot have both deviceKey and deviceSasToken defined"); + LogError("Invalid Argument: iotHubName is empty"); result = NULL; } - /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_003: [If the upperConfig's variables deviceId, deviceKey, iotHubName, protocol, or iotHubSuffix are NULL then IoTHubTransportMqtt_Create shall return NULL.] */ - else if (strlen(config->upperConfig->iotHubName) == 0) + else if ((g_msgTickCounter = tickcounter_create()) == NULL) { LogError("Invalid Argument: iotHubName is empty"); result = NULL; @@ -944,9 +1039,9 @@ else { result = InitializeTransportHandleData(config->upperConfig, config->waitingToSend); - if (result != NULL) + if (result == NULL) { - g_msgTickCounter = tickcounter_create(); + tickcounter_destroy(g_msgTickCounter); } } /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_009: [If any error is encountered then IoTHubTransportMqtt_Create shall return NULL.] */ @@ -992,13 +1087,25 @@ free(mqttMsgEntry); } + switch (transportState->transport_creds.credential_type) + { + case SAS_TOKEN_FROM_USER: + STRING_delete(transportState->transport_creds.CREDENTIAL_VALUE.deviceSasToken); + break; + case DEVICE_KEY: + STRING_delete(transportState->transport_creds.CREDENTIAL_VALUE.deviceKey); + STRING_delete(transportState->devicesPath); + break; + case X509: + default: + break; + } + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_014: [IoTHubTransportMqtt_Destroy shall free all the resources currently in use.] */ mqtt_client_deinit(transportState->mqttClient); STRING_delete(transportState->mqttEventTopic); STRING_delete(transportState->mqttMessageTopic); STRING_delete(transportState->device_id); - STRING_delete(transportState->device_key); - STRING_delete(transportState->sasTokenSr); STRING_delete(transportState->hostAddress); STRING_delete(transportState->configPassedThroughUsername); tickcounter_destroy(g_msgTickCounter); @@ -1234,6 +1341,18 @@ } result = IOTHUB_CLIENT_OK; } + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_039: [If the option parameter is set to "x509certificate" then the value shall be a const char of the certificate to be used for x509.] */ + else if ((strcmp("x509certificate", option) == 0) && (transportState->transport_creds.credential_type != X509)) + { + LogError("x509certificate specified, but authentication method is not x509"); + result = IOTHUB_CLIENT_INVALID_ARG; + } + /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_040: [If the option parameter is set to "x509privatekey" then the value shall be a const char of the RSA Private Key to be used for x509.] */ + else if ((strcmp("x509privatekey", option) == 0) && (transportState->transport_creds.credential_type != X509)) + { + LogError("x509privatekey specified, but authentication method is not x509"); + result = IOTHUB_CLIENT_INVALID_ARG; + } else { /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_032: [IoTHubTransportMqtt_SetOption shall pass down the option to xio_setoption if the option parameter is not a known option string for the MQTT transport.] */ @@ -1260,7 +1379,7 @@ static IOTHUB_DEVICE_HANDLE IoTHubTransportMqtt_Register(TRANSPORT_LL_HANDLE handle, const IOTHUB_DEVICE_CONFIG* device, IOTHUB_CLIENT_LL_HANDLE iotHubClientHandle, PDLIST_ENTRY waitingToSend) { - IOTHUB_DEVICE_HANDLE result; + IOTHUB_DEVICE_HANDLE result = NULL; (void)iotHubClientHandle; // Codes_SRS_IOTHUB_MQTT_TRANSPORT_17_001: [ IoTHubTransportMqtt_Register shall return NULL if the TRANSPORT_LL_HANDLE is NULL.] @@ -1280,11 +1399,6 @@ LogError("IoTHubTransportMqtt_Register: deviceId is NULL."); result = NULL; } - else if ((device->deviceKey == NULL) && (device->deviceSasToken == NULL)) - { - LogError("IoTHubTransportMqtt_Register: deviceKey and deviceSasToken are NULL."); - result = NULL; - } // Codes_SRS_IOTHUB_MQTT_TRANSPORT_03_002: [ IoTHubTransportMqtt_Register shall return NULL if both deviceKey and deviceSasToken are provided.] else if ((device->deviceKey != NULL) && (device->deviceSasToken != NULL)) { @@ -1299,7 +1413,7 @@ LogError("IoTHubTransportMqtt_Register: deviceId does not match."); result = NULL; } - else if ((device->deviceKey != NULL) && (strcmp(STRING_c_str(transportState->device_key), device->deviceKey) != 0)) + else if ( (transportState->transport_creds.credential_type == DEVICE_KEY) && (strcmp(STRING_c_str(transportState->transport_creds.CREDENTIAL_VALUE.deviceKey), device->deviceKey) != 0)) { LogError("IoTHubTransportMqtt_Register: deviceKey does not match."); result = NULL;