I'm trying to perform a local attestation between two enclaves created from two different applications.
The provided sample code for Linux here creates 3 different enclaves and then establishes secure connections between them. But those enclaves have all been created by the same application which therefore is aware of all enclave IDs.
If two different applications are creating there own enclave which should communicate with one another, how would the source enclave get to know the ID of the destination enclave? Would that ID have to be transmitted from one application to the enclave on a "general" way (IPC)?
I've tried some simple test by starting a destination enclave and printing its ID: "26ce00000002"
Then I used this ID in the local attestation example to try to connect to this running destination enclave:
uint64_t wrapper(const char *c) {
errno = 0;
uint64_t result = strtoull(c, NULL, 16);
if (errno == EINVAL) {
cout << "WRONG NUMBER" << endl;
} else if (errno == ERANGE) {
cout << "Too big\n";
}
return result;
}
uint32_t load_enclaves() {
uint32_t enclave_temp_no;
int ret, launch_token_updated;
sgx_launch_token_t launch_token;
enclave_temp_no = 0;
ret = sgx_create_enclave(ENCLAVE1_PATH, SGX_DEBUG_FLAG, &launch_token, &launch_token_updated, &e1_enclave_id, NULL);
if (ret != SGX_SUCCESS) {
return ret;
}
enclave_temp_no++;
g_enclave_id_map.insert(std::pair<sgx_enclave_id_t, uint32_t>(e1_enclave_id, enclave_temp_no));
const char *test = "26ce00000002";
e2_enclave_id = wrapper(test);
enclave_temp_no++;
g_enclave_id_map.insert(std::pair<sgx_enclave_id_t, uint32_t>(e2_enclave_id, enclave_temp_no));
return SGX_SUCCESS;
}
int main(int argc, char **argv) {
uint32_t ret_status;
sgx_status_t status;
if(load_enclaves() != SGX_SUCCESS) {
printf("\nLoad Enclave Failure");
}
printf("\nAvaliable Enclaves");
printf("\nEnclave1 - EnclaveID %lx",e1_enclave_id);
printf("\nEnclave2 - EnclaveID %lx",e2_enclave_id);
do {
//Test Create session between Enclave1(Source) and Enclave2(Destination)
status = Enclave1_test_create_session(e1_enclave_id, &ret_status, e1_enclave_id, e2_enclave_id);
if (status!=SGX_SUCCESS)
{
printf("Enclave1_test_create_session Ecall failed: Error status code is %x", status);
print_error_message(status);
break;
}
else
{
if(ret_status==0)
{
printf("\n\nSecure Channel Establishment between Source (E1) and Destination (E2) Enclaves successful !!!");
}
else
{
printf("\nSession establishment and key exchange failure between Source (E1) and Destination (E2): Error return status is %x\n", ret_status);
break;
}
}
When executing the local attestation program with the source enclave I receive a "SGX_ERROR_INVALID_ENCLAVE_ID" error? This error is not thrown by the local attestation example program but comes from somewhere in the SGX libraries and I don't know why since the destination enclave is still running, therefore the ID should exist!?
We do not need a secure connection to exchange the enclave id's. The Application can store the enclave id in a registry or on the disc along with the enclave names which can be retrieved by corresponding application to obtain the id of the required enclave. Then the application initiates a session between the source enclave and the destination enclave by doing an ECALL into the source enclave, passing in the enclave id of the destination enclave. Upon receiving the enclave id of the destination enclave, the source enclave does an OCALL into the core untrusted code which then does an ECALL into the destination enclave to exchange the messages required to establish a session using ECDH Key Exchange protocol.
Related
So I'm trying to adapt a CPP library for ESP-IDF/Arduino BLE control into ESP-IDF v5.0 but I'm getting some errors regarding Semaphore from FreeRTOS when waiting. This is most likerly due to some APIs changing between ESP-IDF 3.0 to 5.0 but since Im just learning FreeRTOS I got a bit lost with the error.
Some logs from the program before the crash:
I (2376) BLEClient: >> connect(a6:c0:82:01:17:72)
I (2386) : 119660
I (2386) BLEDevice: add conn_id: 0, GATT role: client
I (2386) FreeRTOS: Semaphore taking: name: RegEvt (0x3ffdb0d4), owner: <N/A> for connect
I (2396) FreeRTOS: Semaphore taken: name: RegEvt (0x3ffdb0d4), owner: connect
I (2406) : 119628
assert failed: xQueueGenericSend queue.c:837 (pxQueue->pcHead != ((void *)0) || pxQueue->u.xSemaphore.xMutexHolder == ((void *)0) || pxQueue->u.xSemaphore.xMutexHolder == xTaskGetCurrentTaskHandle())
The traceback:
Backtrace: 0x40081d1e:0x3ffdacb0 0x40090699:0x3ffdacd0 0x4009687d:0x3ffdacf0 0x40090e62:0x3ffdae10 0x400dc597:0x3ffdae50 0x400d94d9:0x3ffdae90 0x400da145:0x3ffdaf10 0x400e0226:0x3ffdaf60 0x400e0922:0x3ffdaf80 0x400f73fd:0x3ffdafc0 0x400f95ab:0x3ffdafe0
0x40081d1e: panic_abort at C:/Espressif/frameworks/esp-idf-v5.0/components/esp_system/panic.c:412
0x40090699: esp_system_abort at C:/Espressif/frameworks/esp-idf-v5.0/components/esp_system/esp_system.c:135
0x4009687d: __assert_func at C:/Espressif/frameworks/esp-idf-v5.0/components/newlib/assert.c:78
0x40090e62: xQueueGenericSend at C:/Espressif/frameworks/esp-idf-v5.0/components/freertos/FreeRTOS-Kernel/queue.c:828 (discriminator 2)
0x400dc597: FreeRTOS::Semaphore::give() at C:/Users/amng8/Sync/PROJECTS/PillCPP/components/BLE/FreeRTOS.cpp:107
0x400d94d9: BLEClient::gattClientEventHandler(esp_gattc_cb_event_t, unsigned char, esp_ble_gattc_cb_param_t*) at C:/Users/amng8/Sync/PROJECTS/PillCPP/components/BLE/BLEClient.cpp:233
0x400da145: BLEDevice::gattClientEventHandler(esp_gattc_cb_event_t, unsigned char, esp_ble_gattc_cb_param_t*) at C:/Users/amng8/Sync/PROJECTS/PillCPP/components/BLE/BLEDevice.cpp:163
0x400e0226: btc_gattc_cb_to_app at C:/Espressif/frameworks/esp-idf-v5.0/components/bt/host/bluedroid/btc/profile/std/gatt/btc_gattc.c:24
0x400e0922: btc_gattc_cb_handler at C:/Espressif/frameworks/esp-idf-v5.0/components/bt/host/bluedroid/btc/profile/std/gatt/btc_gattc.c:1002
0x400f73fd: btc_thread_handler at C:/Espressif/frameworks/esp-idf-v5.0/components/bt/common/btc/core/btc_task.c:207
ELF file SHA256: b8542ed11e07aba6
0x400f95ab: osi_thread_run at C:/Espressif/frameworks/esp-idf-v5.0/components/bt/common/osi/thread.c:165
The code in question is from the file BLEClient.cpp function bool BLEClient::connect(BLEAddress address, esp_ble_addr_type_t type):
/**
* #brief Connect to the partner (BLE Server).
* #param [in] address The address of the partner.
* #return True on success.
*/
bool BLEClient::connect(BLEAddress address, esp_ble_addr_type_t type) {
ESP_LOGI(LOG_TAG, ">> connect(%s)", address.toString().c_str());
// We need the connection handle that we get from registering the application. We register the app
// and then block on its completion. When the event has arrived, we will have the handle.
m_appId = BLEDevice::m_appId++;
BLEDevice::addPeerDevice(this, true, m_appId);
m_semaphoreRegEvt.take("connect");
// clearServices(); // we dont need to delete services since every client is unique?
esp_err_t errRc = ::esp_ble_gattc_app_register(m_appId);
if (errRc != ESP_OK) {
ESP_LOGE(LOG_TAG, "esp_ble_gattc_app_register: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
return false;
}
m_semaphoreRegEvt.wait("connect");
m_peerAddress = address;
// Perform the open connection request against the target BLE Server.
m_semaphoreOpenEvt.take("connect");
errRc = ::esp_ble_gattc_open(
m_gattc_if,
*getPeerAddress().getNative(), // address
type, // Note: This was added on 2018-04-03 when the latest ESP-IDF was detected to have changed the signature.
1 // direct connection <-- maybe needs to be changed in case of direct indirect connection???
);
if (errRc != ESP_OK) {
ESP_LOGE(LOG_TAG, "esp_ble_gattc_open: rc=%d %s", errRc, GeneralUtils::errorToString(errRc));
return false;
}
uint32_t rc = m_semaphoreOpenEvt.wait("connect"); // Wait for the connection to complete.
ESP_LOGD(LOG_TAG, "<< connect(), rc=%d", rc==ESP_GATT_OK);
return rc == ESP_GATT_OK;
} // connect
and what seems to be triggering the crash is the .wait("connect") command.
Once again I'm new to FreeRTOS and Semaphore and not sure what's useful or not to get help so if you need any more information please let me know.
Trying to load multiple ssl certificates in memory. I tried to code from documentation but failed as documentation seen little hard for me to understand. Seeking help from stackoverflow.
I am finding a way, that both certificates can work simultaneously at the time.
Certificates that I have to load in memory are Symantec and Zuul(.pem format).
Both symantec.pem and zuul.pem file are password protected and it contains (one certificate and one private key).
Below code loading symanctic certificate only.
Openssl version : OpenSSL 1.1.1g FIPS 21 Apr 2020
if(server_ctx==NULL)
{
/* Global system initialization*/
SSL_library_init();
SSL_load_error_strings();
}
meth=TLS_server_method();
if(status == true)
{
server_ctx=SSL_CTX_new(meth);
if(server_ctx != NULL)
{
if((strcmp(protocol_current_version,"Y") == 0) || (strcmp(protocol_current_version,"y") == 0))
{
SSL_CTX_set_min_proto_version(server_ctx, TLS1_VERSION);
}
else if((strcmp(protocol_current_version,"N") == 0) || (strcmp(protocol_current_version,"n") == 0))
{
SSL_CTX_set_min_proto_version(server_ctx, TLS1_2_VERSION);
}
}
}
/* Load our keys and certificates*/
if(!(SSL_CTX_use_certificate_file(server_ctx,keyfile,SSL_FILETYPE_PEM)))
{
status = false;
LOG(LEVEL1,"Error!!! Couldn't read certificate file: %s",ERR_error_string(ERR_get_error(),NULL));
}
if(status==true)
{SSL_CTX_set_default_passwd_cb(server_ctx,password_cb);}
if(status==true)
{
if(!(SSL_CTX_use_PrivateKey_file(server_ctx,keyfile,SSL_FILETYPE_PEM)))
{
LOG(LEVEL1,"Error!!! Couldn't read key file: %s",ERR_error_string(ERR_get_error(),NULL));
status = false;
}
}
/* Load the CAs we trust*/
if(status==true)
{
if(!(SSL_CTX_load_verify_locations(server_ctx,ca_list,0)))
{
LOG(LEVEL1,"Error!!! Couldn't read CA_LIST: %s",ERR_error_string(ERR_get_error(),NULL));
status=false;
}
}
//Force Mutual authentication if verify_ssl_peer is set
if(verify_ssl_peer)
{
SSL_CTX_set_verify(server_ctx, SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE|SSL_VERIFY_FAIL_IF_NO_PEER_CERT,NULL);
}
SSL_CTX_set_verify_depth(server_ctx,1);
//Set AUTO MODE RETRY Flag, Will not bother application till data is completely read
SSL_CTX_set_mode(server_ctx,SSL_MODE_AUTO_RETRY);
//Set the list of supported cipher suites. Typically used to force only AES128 or AES256 bit encryption:
if(status==true)
{
//Force the cipher is verify_ssl_cipher is set
if(verify_ssl_cipher)
{
if(cipher_list==NULL)
{
LOG(LEVEL1,"[%s]: Error!!! Cipher list is set to NULL, will not initialize",__FUNCTION__);
status = false;
}
else
{
if(!SSL_CTX_set_cipher_list(server_ctx,cipher_list))
{LOG(LEVEL1,"Error!!! Failed to set cipher_list for ctx: %s",ERR_error_string(ERR_get_error(),NULL));}
}
}
}
if(status==true)
{
/* Load randomness */
if(!(RAND_load_file(random_file,1024*1024)))
{
LOG(LEVEL1,"Error!!! Load randomness failed:%s",ERR_error_string(ERR_get_error(),NULL));
status=false;
}
}
if(status==true)
{
long opts = SSL_CTX_get_options(server_ctx);
opts = SSL_CTX_set_options(server_ctx,SSL_OP_ALL|opts);
LOG(LEVEL1,"SSl Server CTX Options set:%04x",opts);
}
A possible solution could be to use the Server Name Indication in the TLS Client Hello: Zuul and Symantec Clients could set the TLS SNI field differently so that your Server could elaborate it to select the proper certificate chain.
In details, your server shall first setup one SSL Context for Zuul and another one for Symantec with the right private key and certificate chain, then the servername callback shall be set in each context.
For example, to set the server name callback for your symantec context:
SSL_CTX_set_tlsext_servername_callback(symantec_context, serverNameCallbackHandler);
You can set an argument that will be handled by serverNameCallbackHandler function the for this SSL_CTX:
SSL_CTX_set_tlsext_servername_arg(symantec_context, &your_data);
Then you shall implement the callback that will be invoked durint the TLS handshake. For example:
static int serverNameCallbackHandler(SSL *ssl, int *ad, void *arg)
{
MyData *data = (MyData *) arg;
// ...
const char *servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
if (servername)
{
//Select the proper context
//...
if (match_found)
{
SSL_CTX* ctx = symantec_context;
SSL_CTX* v = SSL_set_SSL_CTX(ssl, ctx);
if (v != ctx)
{
// Internal Error on ssl context switch!
return SSL_TLSEXT_ERR_NOACK;
}
}
}
// NOTE: On Server name mismatch use the default context
return SSL_TLSEXT_ERR_OK;
}
For information about how to handle the SNI on client side, I think that the opensource code of openssl command is very helpful.
Note that you can test your server with the openssl command:
openssl s_client -servername 'your sni name' -CAfile ./RootCA.pem -connect ip:port
I'm trying to get information about services provided by bluetooth devices. Using WSALookupServiceNext I can get list of all services. However, regardless of what bluetooth device I try to discover, the resulting WSAQUERYSET structure (pqs) never contains lpServiceClassId field (while the lpszServiceInstanceName is always present). I want to know UUID of a service to determine to which class of the Table 2: Service Class Profile Identifiers it belongs.
#include<winsock2.h>
int main()
{
WSADATA data;
WSAStartup(MAKEWORD(2, 2), &data);
WSAQUERYSET qs{};
qs.dwSize = sizeof(WSAQUERYSET);
qs.dwNameSpace = NS_BTH;
qs.dwNumberOfCsAddrs = 0;
qs.lpszContext = (LPWSTR)L"12:34:56:78:99:11";
qs.lpServiceClassId = const_cast<LPGUID>(&PublicBrowseGroupServiceClass_UUID);
const DWORD flags = LUP_FLUSHCACHE | LUP_RETURN_ALL;
HANDLE hlookup = nullptr;
WSALookupServiceBegin(&qs, flags, &hlookup);
while (true) {
char buff[4096];
WSAQUERYSET* pqs = (WSAQUERYSET*)buff;
DWORD size = sizeof(buff);
memset(buff, 0, size);
const INT res = WSALookupServiceNext(hlookup, flags, &size, pqs);
if (res != 0 && GetLastError() == WSA_E_NO_MORE) {
break;
}
// it prints "service name=Advanced Audio, service uuid=0x0"
wprintf(L"service name=%s, service uuid=0x%X\n", pqs->lpszServiceInstanceName, pqs->lpServiceClassId->Data1);
}
}
Answering my own question:
In order to get service ID we have to parse SPD structures which are returned from WSALookupServiceNext function call and accessible via WSAQUERYSET.lpBlob member.
For more details see Mike Petrichenko's comment.
Also, there is a very good example how to implement it from Qt framework source code.
As for myself, I decided not to follow this path, because my goal was to write a console utility for pairing Bluetooth devices. I have found that there is a better way to do that using Windows.Devices.Enumeration API. Finally, using this API, I managed to create the BluetoothDevicePairing utility I was working on.
I tried to run this sample C++ application using pjsip library, But when I ran the application, I faced with this error:
06:56:50.480 sip_auth_client.c ...Unsupported digest algorithm "SHA-256"
06:56:50.480 pjsua_acc.c ....SIP registration error: Invalid/unsupported digest algorithm (PJSIP_EINVALIDALGORITHM) [status=171102]
But when I examined the SIP response from the server, I noticed it contained two WWW-Authenticate headers:
WWW-Authenticate: Digest realm="sip.linphone.org", nonce="ImEX4gAAAAC73QlWAAC9corBNkwAAAAA", opaque="+GNywA==", algorithm=SHA-256, qop="auth"
WWW-Authenticate: Digest realm="sip.linphone.org", nonce="ImEX4gAAAAC73QlWAAC9corBNkwAAAAA", opaque="+GNywA==", algorithm=MD5, qop="auth"
So the question is, if pjsip doesn't support sha-256 algorithm, why it doesn't use md5 alogrithm mentioned in the second header?
The sample code is:
#include <pjsua2.hpp>
#include <iostream>
using namespace pj;
// Subclass to extend the Account and get notifications etc.
class MyAccount : public Account {
public:
virtual void onRegState(OnRegStateParam &prm) {
AccountInfo ai = getInfo();
std::cout << (ai.regIsActive? "*** Register:" : "*** Unregister:")
<< " code=" << prm.code << std::endl;
}
};
int main()
{
Endpoint ep;
ep.libCreate();
// Initialize endpoint
EpConfig ep_cfg;
ep.libInit( ep_cfg );
// Create SIP transport. Error handling sample is shown
TransportConfig tcfg;
tcfg.port = 5060;
try {
ep.transportCreate(PJSIP_TRANSPORT_UDP, tcfg);
} catch (Error &err) {
std::cout << err.info() << std::endl;
return 1;
}
// Start the library (worker threads etc)
ep.libStart();
std::cout << "*** PJSUA2 STARTED ***" << std::endl;
// Configure an AccountConfig
AccountConfig acfg;
acfg.idUri = "sip:test#pjsip.org";
acfg.regConfig.registrarUri = "sip:pjsip.org";
AuthCredInfo cred("digest", "*", "test", 0, "secret");
acfg.sipConfig.authCreds.push_back( cred );
// Create the account
MyAccount *acc = new MyAccount;
acc->create(acfg);
// Here we don't have anything else to do..
pj_thread_sleep(10000);
// Delete the account. This will unregister from server
delete acc;
// This will implicitly shutdown the library
return 0;
}
I've struggled with this myself. Apparently, PJSIP only evaluates the first WWW-Authenticate header, even if the server supplies more than one.
To overcome this problem I changed the following in the source code:
In the file /pjsip/src/pjsip/sip_auth_client.c find the block of code that generates a response for the WWW-Authenticate header. It should be around line 1220.
/* Create authorization header for this challenge, and update
* authorization session.
*/
status = process_auth(tdata->pool, hchal, tdata->msg->line.req.uri,
tdata, sess, cached_auth, &hauth);
if (status != PJ_SUCCESS)
return status;
if (pj_pool_get_used_size(cached_auth->pool) >
PJSIP_AUTH_CACHED_POOL_MAX_SIZE)
{
recreate_cached_auth_pool(sess->endpt, cached_auth);
}
/* Add to the message. */
pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hauth);
/* Process next header. */
hdr = hdr->next;
and replace it with
/* Create authorization header for this challenge, and update
* authorization session.
*/
status = process_auth(tdata->pool, hchal, tdata->msg->line.req.uri,
tdata, sess, cached_auth, &hauth);
if (status != PJ_SUCCESS){
// Previously, pjsip analysed one www-auth header, and if it failed (due to unsupported sha-256 digest for example), it returned and did not consider the next www-auth header.
PJ_LOG(4,(THIS_FILE, "Invalid response, moving to next"));
//return status;
hdr = hdr->next;
}else{
if (pj_pool_get_used_size(cached_auth->pool) >
PJSIP_AUTH_CACHED_POOL_MAX_SIZE)
{
recreate_cached_auth_pool(sess->endpt, cached_auth);
}
/* Add to the message. */
pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hauth);
/* Process next header. */
hdr = hdr->next;
}
Then recompile the source (as per these instructions How to install pjsua2 packages for python?)
Please note that although this solves the problem for linphone, I did not test this for other situations (for example when the server sends multiple valid algorithms or none at all).
I'm trying to publish more than 100 messages per second to Azure Iot Hub built-in event hub. I'm using libmosquitto 1.6.8 library. I'm using the Free tier package for Azure Iot Hub, I know that there is throttle limit of 100 messages per second. But this is not related to that issue. I have not been able to publish even half of the messages to AZ Iot Hub.
Basically, I have a list of multiple values in multimap that needs to be sent. The metric list:
std::multimap< const std::string, std::tuple< const std::string, const std::string, float> > calculatedMetricList;
I'll be iterating through the multimap and constructing each value into a object payload and will be sending it through. What this means is that the mosquitto_publish method will be called multiple times.
Following is the code for publishing the messages:
void MosquittoClient::sendDataToUpstreamSystem(){
StatisticalMethod statisticalMethod;
int rc;
MosquittoClient pub_mosq(
"<IoT Hub Name>.azure-devices.net",
"<deviceID>",
"<username>",
"<Password>",
"devices/<deviceID>/messages/events/");
printf("Using MQTT to get data payload from host: %s and on port: %d.\r\n", pub_mosq.get_host(), pub_mosq.get_port());
// init the mosquitto lib
mosquitto_lib_init();
// create the mosquito object
struct mosquitto* mosq = mosquitto_new(pub_mosq.get_deviceID(), false, NULL);
// add callback functions
mosquitto_connect_callback_set(mosq, MosquittoClient::connect_callback);
mosquitto_publish_callback_set(mosq, MosquittoClient::publish_callback);
mosquitto_message_callback_set(mosq, MosquittoClient::on_message);
mosquitto_disconnect_callback_set(mosq, MosquittoClient::on_disconnect_callback);
// set mosquitto username, password and options
mosquitto_username_pw_set(mosq, pub_mosq.get_userName(), pub_mosq.get_password());
// specify the certificate to use
std::ifstream infile(pub_mosq.get_certificate());
bool certExists = infile.good();
infile.close();
if (!certExists)
{
printf("Warning: Could not find file '%s'! The mosquitto loop may fail.\r\n", pub_mosq.get_certificate());
}
printf("Using certificate: %s\r\n", pub_mosq.get_certificate());
mosquitto_tls_set(mosq, pub_mosq.get_certificate(), NULL, NULL, NULL, NULL);
// specify the mqtt version to use
int* option = new int(MQTT_PROTOCOL_V311);
rc = mosquitto_opts_set(mosq, MOSQ_OPT_PROTOCOL_VERSION, option);
if (rc != MOSQ_ERR_SUCCESS)
{
rc = pub_mosq.mosquitto_error(rc, "Error: opts_set protocol version");
}
else
{
printf("Setting up options OK\r\n");
}
// connect
printf("Connecting...\r\n");
rc = mosquitto_connect_async(mosq, pub_mosq.get_host(), pub_mosq.get_port(), 4);
if (rc != MOSQ_ERR_SUCCESS)
{
rc = pub_mosq.mosquitto_error(rc, NULL);
}
else
{
printf("Connect returned OK\r\n");
rc = mosquitto_loop_start(mosq);
if (rc != MOSQ_ERR_SUCCESS)
{
rc = pub_mosq.mosquitto_error(rc, NULL);
}
else
{
do
{
for (auto itr = Metrics::calculatedMetricList.begin(); itr != Metrics::calculatedMetricList.end(); itr++) {
int msgId = rand();
std::string test1= itr->first;
std::string test2 = std::get<0>(itr->second);
std::string test3= std::get<1>(itr->second); // metric type
float value = std::get<2>(itr->second); // value
DataPayload objectPayload(
75162345,
496523,
test3,
value,
"test",
test1,
"test",
"test",
123,
213,
23
);
objectPayload.setPayload();
std::string dataPayload = objectPayload.getPayload();
//DEBUG
std::cout << "dataPayload: " << dataPayload << std::endl;
//DEBUG
std::cout << "dataPayload Size: " << dataPayload.size() << std::endl;
// once connected, we can publish (send) a Telemetry message
printf("Publishing to topic: %s\r\n", pub_mosq.get_topic());
rc = pub_mosq.publishToTopic(mosq, &msgId, dataPayload.size(), (char *)dataPayload.c_str());
if (rc == MOSQ_ERR_SUCCESS)
{
printf("Publish returned OK\r\n");
}
else
{
rc = pub_mosq.mosquitto_error(rc, NULL);
}
}
} while (rc != MOSQ_ERR_SUCCESS);
}
}
mosquitto_loop_stop(mosq, true);
mosquitto_destroy(mosq);
mosquitto_lib_cleanup();}
Publish method:
int MosquittoClient::publishToTopic(struct mosquitto *mosq, int *msgId, int sizeOfData, char *data)
{
return mosquitto_publish(mosq, msgId, p_topic, sizeOfData, data, 1, true);
}
When running the program all the messages published return ok, according to the console. But only one or two messages are appearing on the Azure IoT Hub side.
The following image shows the monitoring of IoT Hub, at that time only one message got through.
Device Explorer Twin Monitoring
I have tried so many different solutions, but the program was unable to publish all the messages. It looks like the publish method is waiting to complete the first message but the iteration is moving onto the next message, causing it to be dropped. If that is the cause of the dropped messages, what is the best way to sequence the message sending? Otherwise, what else could be causing messages to be dropped?
Update
The problem was the program didn't waiting until the messages were successfully published to the broker (Azure IoT Hub). You will know if the message is successfully published to the broker if the publish_callback is returned.
void MosquittoClient::publish_callback(struct mosquitto* mosq, void* userdata, int mid)
{
printf("Publish OK.\r\n");
}
Solution was to sleep thread before destroy, cleanup calls and start Mosquitto loop before connection is established.
sleep(30);
mosquitto_loop_stop(mosq, true);
mosquitto_destroy(mosq);
mosquitto_lib_cleanup();
mosquitto_publish() is asynchronous: having it return MOSQ_ERR_SUCCESS simply means that the publication of the message has properly been passed to the Mosquitto thread. So at the moment you are enqueuing lots of messages, and then have your program terminate before it had a chance to actually send the packets.
You can use your MosquittoClient::publish_callback callback to check that all the messages have effectively been sent before stopping the loop and terminating your program.