In my program sometimes I am finding that inside MQTT_ProcessLoop I am getting blocked. This is a very difficult block to catch as it only happens maybe 1% of the time. I believe the block is happening here
MQTT_ProcessLoop > receiveSingleIteration > MQTT_GetIncomingPacketTypeAndLength > readFunc
This makes me believe that this is happening inside of mbedtls_ssl_read. Upon inspection, it seems like mbedtls_ssl_read is a blocking function by default. I am trying to add a timeout to mbedtls_ssl_read, but it is causing my handshaking to fail. Here’s some snippets from my attempt.
I’m wondering if I am calling the callback function in the incorrect location of TLS_setup?
/**
* \brief Callback type: set a pair of timers/delays to watch
*
* \param ctx Context pointer
* \param int_ms Intermediate delay in milliseconds
* \param fin_ms Final delay in milliseconds
* 0 cancels the current timer.
*
* \note This callback must at least store the necessary information
* for the associated \c mbedtls_ssl_get_timer_t callback to
* return correct information.
*
* \note If using a event-driven style of programming, an event must
* be generated when the final delay is passed. The event must
* cause a call to \c mbedtls_ssl_handshake() with the proper
* SSL context to be scheduled. Care must be taken to ensure
* that at most one such call happens at a time.
*
* \note Only one timer at a time must be running. Calling this
* function while a timer is running must cancel it. Cancelled
* timers must not generate any event.
*/
void TLS_set_timer( void * ctx, uint32_t int_ms, uint32_t fin_ms )
{
U8 timerStatus = true;
timerSSL_t * timer = ctx;
U32 currentTime = TLS_GetTimeMsCallback();
if( !timer )
{
// LOG_error(("Timer not found"));
timerStatus = false;
}
if( timerStatus )
{
if( int_ms == 0 || fin_ms == 0 )
{
// LOG_debug(("Timer canceled"));
timer->isRunning = false;
}
else
{
// LOG_debug(("Timer set"));
timer->start_ms = currentTime;
timer->int_ms = currentTime + int_ms;
timer->fin_ms = currentTime + fin_ms;
timer->isRunning = true;
}
}
}
/**
* \brief Callback type: get status of timers/delays
*
* \param ctx Context pointer
*
* \return This callback must return:
* -1 if cancelled (fin_ms == 0),
* 0 if none of the delays have passed,
* 1 if only the intermediate delay has passed,
* 2 if the final delay has passed.
*/
int TLS_get_timer( void * ctx )
{
int timerStatus = 0;
timerSSL_t * timer = ctx;
U32 currentTime = TLS_GetTimeMsCallback();
if( !timer )
{
// LOG_error(("Timer not found"));
timerStatus = -1;
}
else if( timer->fin_ms == 0 )
{
timerStatus = -1;
}
else
{
if( timer->int_ms >= currentTime )
{
timerStatus++;
}
if( timer->fin_ms >= currentTime )
{
timerStatus++;
}
}
// LOG_error(("Timer status: %d", timerStatus));
return timerStatus;
}
/**
* @brief Setup SSL connection with server
*
* @param pNetworkContext
* @param pSslContext Secured connection context
* @param pNetworkCredentials Contains the credentials necessary for tls connection setup
* @return int32_t Error code
*/
static int32_t SSL_setup( NetworkContext_t * pNetworkContext, SSLContext_t * pSslContext,
NetworkCredentials_t *pNetworkCredentials )
{
Status.ErrorsIOT |= IOT_SSL_SETUP;
int32_t mbedtlsError = TLS_TRANSPORT_SUCCESS;
LOG_debug( ( "Initializing SSL" ) );
mbedtls_ssl_config_init( &( pSslContext->config ) );
mbedtls_x509_crt_init( &( pSslContext->rootCa ) ); // Root Server Cert
mbedtls_ssl_init( &( pSslContext->context ) );
mbedtls_ctr_drbg_init( &ctr_drbg ); // Random Number Generation
mbedtls_entropy_init( &entropy ); // Random Number Generation
// mbedtls_ssl_conf_dbg(&(pSslContext->config), debugWrapper, NULL);
mbedtls_ssl_set_timer_cb(pSslContext, &timerSSL, TLS_set_timer, TLS_get_timer);
mbedtlsError = xInitializePkcs11Session( &( pSslContext->xP11Session ) ); // Holds callback functions
if( mbedtlsError != TLS_TRANSPORT_SUCCESS )
{
return TLS_TRANSPORT_INTERNAL_ERROR;
}
mbedtlsError = C_GetFunctionList( &( pSslContext->pxP11FunctionList ) ); // Holds callback functions
if( mbedtlsError != TLS_TRANSPORT_SUCCESS )
{
return TLS_TRANSPORT_INTERNAL_ERROR;
}
mbedtlsError = mbedtls_ctr_drbg_seed( &ctr_drbg, mbedtls_entropy_func, &entropy, NULL, 0 ); // Seed for RNG
if( mbedtlsError != TLS_TRANSPORT_SUCCESS )
{
return TLS_TRANSPORT_INTERNAL_ERROR;
}
mbedtlsError = mbedtls_ssl_config_defaults( &(pSslContext->config), MBEDTLS_SSL_IS_CLIENT,
MBEDTLS_SSL_TRANSPORT_STREAM,
MBEDTLS_SSL_PRESET_DEFAULT );
if( mbedtlsError != TLS_TRANSPORT_SUCCESS )
{
return TLS_TRANSPORT_INSUFFICIENT_MEMORY;
}
pSslContext->certProfile = mbedtls_x509_crt_profile_default; // Root server certificate
mbedtls_ssl_conf_authmode(&(pSslContext->config), MBEDTLS_SSL_VERIFY_REQUIRED);
LOG_debug(("Setting TRNG function"));
mbedtls_ssl_conf_rng(&(pSslContext->config), callbackRandomBytes, pSslContext);
LOG_debug(("Checking root server certificate"));
mbedtls_ssl_conf_cert_profile(&(pSslContext->config), &(pSslContext->certProfile));
/* Parse the server root CA certificate into the SSL context. */
mbedtlsError = mbedtls_x509_crt_parse(&(pSslContext->rootCa), pNetworkCredentials->pRootCa,
pNetworkCredentials->rootCaSize);
mbedtls_ssl_conf_ca_chain(&(pSslContext->config), &(pSslContext->rootCa), NULL);
if( mbedtlsError != TLS_TRANSPORT_SUCCESS )
{
return TLS_TRANSPORT_INVALID_CREDENTIALS;
}
/* Enable SNI if requested. */
if( pNetworkCredentials->disableSni == pdFALSE )
{
LOG_debug( ( "Checking hostname" ) );
mbedtlsError = mbedtls_ssl_set_hostname(&(pSslContext->context), pNetworkContext->hostname);
if( mbedtlsError != TLS_TRANSPORT_SUCCESS )
{
return TLS_TRANSPORT_INTERNAL_ERROR;
}
}
/* Set Maximum Fragment Length if enabled. */
#ifdef MBEDTLS_SSL_MAX_FRAGMENT_LENGTH
/* Enable the max fragment extension. 4096 bytes is currently the largest fragment size permitted.
* See RFC 8449 https://tools.ietf.org/html/rfc8449 for more information.
*
* Smaller values can be found in "mbedtls/include/ssl.h".
*/
mbedtlsError = mbedtls_ssl_conf_max_frag_len(&(pTlsTransportParams->sslContext.config),
MBEDTLS_SSL_MAX_FRAG_LEN_4096 );
if( mbedtlsError != TLS_TRANSPORT_SUCCESS )
{
return TLS_TRANSPORT_INTERNAL_ERROR;
}
#endif /* ifdef MBEDTLS_SSL_MAX_FRAGMENT_LENGTH */
Status.ErrorsIOT &= ~IOT_SSL_SETUP;
return TLS_TRANSPORT_SUCCESS;
}
/**
* @brief Performs the SSL handshake with the server
*
* @param pSslContext Secured connection context
* @param pTlsTransportParams Definition of the network context for the transport interface
* implementation that uses mbedTLS and FreeRTOS+TLS sockets
* @return int32_t
*/
static int32_t SSL_handshake( SSLContext_t * pSslContext,
TlsTransportParams_t * pTlsTransportParams )
{
Status.ErrorsIOT |= IOT_SSL_HANDSHAKE;
int32_t mbedtlsError = TLS_TRANSPORT_SUCCESS;
LOG_debug( ( "Attempting TLS handshake" ) );
mbedtlsError = mbedtls_ssl_setup(&(pSslContext->context), &(pSslContext->config));
if( mbedtlsError != TLS_TRANSPORT_SUCCESS )
{
return mbedtlsError;
}
/* These two macros MBEDTLS_SSL_SEND and MBEDTLS_SSL_RECV need to be
* defined in mbedtls_config.h according to which implementation you use.
*/
mbedtls_ssl_set_bio(&( pSslContext->context ), (void *) pTlsTransportParams->tcpSocket,
mbedtls_platform_send, mbedtls_platform_recv, NULL);
do
{
LOG_debug( ( "Shaking hands" ) );
mbedtlsError = mbedtls_ssl_handshake( &( pSslContext->context ) );
} while( ( mbedtlsError == MBEDTLS_ERR_SSL_WANT_READ ) ||
( mbedtlsError == MBEDTLS_ERR_SSL_WANT_WRITE ) );
if( mbedtlsError != TLS_TRANSPORT_SUCCESS )
{
return TLS_TRANSPORT_HANDSHAKE_FAILED;
}
Status.ErrorsIOT &= ~IOT_SSL_HANDSHAKE;
return TLS_TRANSPORT_SUCCESS;
}