int32_t Openssl_Recv()

in platform/posix/transport/src/openssl_posix.c [753:862]


int32_t Openssl_Recv( NetworkContext_t * pNetworkContext,
                      void * pBuffer,
                      size_t bytesToRecv )
{
    OpensslParams_t * pOpensslParams = NULL;
    int32_t bytesReceived = 0;

    if( !isValidNetworkContext( pNetworkContext ) ||
        ( pBuffer == NULL ) ||
        ( bytesToRecv == 0 ) )
    {
        LogError( ( "Parameter check failed: invalid input, pNetworkContext is invalid or pBuffer = %p, bytesToRecv = %lu", pBuffer, bytesToRecv ) );
        bytesReceived = -1;
    }
    else
    {
        int32_t pollStatus = 1, readStatus = 1, sslError = 0;
        uint8_t shouldRead = 0U;
        struct pollfd pollFds;
        pOpensslParams = pNetworkContext->pParams;

        /* Initialize the file descriptor.
         * #POLLPRI corresponds to high-priority data while #POLLIN corresponds
         * to any other data that may be read. */
        pollFds.events = POLLIN | POLLPRI;
        pollFds.revents = 0;
        /* Set the file descriptor for poll. */
        pollFds.fd = pOpensslParams->socketDescriptor;

        /* #SSL_pending returns a value > 0 if application data
         * from the last processed TLS record remains to be read.
         * This implementation will ALWAYS block when the number of bytes
         * requested is greater than 1. Otherwise, poll the socket first
         * as blocking may negatively impact performance by waiting for the
         * entire duration of the socket timeout even when no data is available. */
        if( ( bytesToRecv > 1 ) || ( SSL_pending( pOpensslParams->pSsl ) > 0 ) )
        {
            shouldRead = 1U;
        }
        else
        {
            /* Speculative read for the start of a payload.
             * Note: This is done to avoid blocking when no
             * data is available to be read from the socket. */
            pollStatus = poll( &pollFds, 1, 0 );
        }

        if( pollStatus < 0 )
        {
            bytesReceived = -1;
        }
        else if( pollStatus == 0 )
        {
            /* No data available to be read from the socket. */
            bytesReceived = 0;
        }
        else
        {
            shouldRead = 1U;
        }

        if( shouldRead == 1U )
        {
            /* Blocking SSL read of data.
             * Note: The TLS record may only be partially received or unprocessed,
             * so it is possible that no processed application data is returned
             * even though the socket has data available to be read. */
            readStatus = ( int32_t ) SSL_read( pOpensslParams->pSsl, pBuffer,
                                               ( int32_t ) bytesToRecv );

            /* Successfully read of application data. */
            if( readStatus > 0 )
            {
                bytesReceived = readStatus;
            }
        }

        /* Handle error return status if transport read did not succeed. */
        if( readStatus <= 0 )
        {
            sslError = SSL_get_error( pOpensslParams->pSsl, readStatus );

            if( sslError == SSL_ERROR_WANT_READ )
            {
                /* The OpenSSL documentation mentions that SSL_Read can provide a
                 * return code of SSL_ERROR_WANT_READ in blocking mode, if the SSL
                 * context is not configured with with the SSL_MODE_AUTO_RETRY. This
                 * error code means that the SSL_read() operation needs to be retried
                 * to complete the read operation. Thus, setting the return value of
                 * this function as zero to represent that no data was received from
                 * the network. */
                bytesReceived = 0;
            }
            else
            {
                LogError( ( "Failed to receive data over network: SSL_read failed: "
                            "ErrorStatus=%s.",
                            ERR_reason_error_string( sslError ) ) );

                /* The transport interface requires zero return code only when the
                 * receive operation can be retried to achieve success. Thus, convert
                 * a zero error code to a negative return value as this cannot be
                 * retried. */
                bytesReceived = -1;
            }
        }
    }

    return bytesReceived;
}