static void on_sasl_frame_received_callback()

in src/saslclientio.c [646:936]


static void on_sasl_frame_received_callback(void* context, AMQP_VALUE sasl_frame)
{
    SASL_CLIENT_IO_INSTANCE* sasl_client_io_instance = (SASL_CLIENT_IO_INSTANCE*)context;

    /* Codes_SRS_SASLCLIENTIO_01_067: [The SASL frame exchange shall be started as soon as the SASL header handshake is done.] */
    switch (sasl_client_io_instance->io_state)
    {
    default:
        LogError("SASL frame received while in state %d", (int)sasl_client_io_instance->io_state);
        break;

    case IO_STATE_OPEN:
    case IO_STATE_OPENING_UNDERLYING_IO:
    case IO_STATE_CLOSING:
        /* Codes_SRS_SASLCLIENTIO_01_117: [If `on_sasl_frame_received_callback` is called when the state of the IO is OPEN then the `on_io_error` callback shall be triggered.]*/
        handle_error(sasl_client_io_instance);
        break;

    case IO_STATE_SASL_HANDSHAKE:
        if (sasl_client_io_instance->sasl_header_exchange_state != SASL_HEADER_EXCHANGE_HEADER_EXCH)
        {
            /* Codes_SRS_SASLCLIENTIO_01_118: [If `on_sasl_frame_received_callback` is called in the OPENING state but the header exchange has not yet been completed, then the `on_io_error` callback shall be triggered.]*/
            handle_error(sasl_client_io_instance);
        }
        else
        {
            AMQP_VALUE descriptor = amqpvalue_get_inplace_descriptor(sasl_frame);
            if (descriptor == NULL)
            {
                /* Codes_SRS_SASLCLIENTIO_01_119: [If any error is encountered when parsing the received frame, the `on_io_open_complete` callback shall be triggered with `IO_OPEN_ERROR`.]*/
                LogError("Could not obtain SASL frame descriptor");
                handle_error(sasl_client_io_instance);
            }
            else
            {
                if (sasl_client_io_instance->is_trace_on != 0)
                {
                    log_incoming_frame(sasl_frame);
                }

                /* Codes_SRS_SASLCLIENTIO_01_032: [The peer acting as the SASL server MUST announce supported authentication mechanisms using the sasl-mechanisms frame.] */
                /* Codes_SRS_SASLCLIENTIO_01_040: [The peer playing the role of the SASL client and the peer playing the role of the SASL server MUST correspond to the TCP client and server respectively.] */
                /* Codes_SRS_SASLCLIENTIO_01_034: [<-- SASL-MECHANISMS] */
                if (is_sasl_mechanisms_type_by_descriptor(descriptor))
                {
                    switch (sasl_client_io_instance->sasl_client_negotiation_state)
                    {
                    default:
                        LogError("SASL mechanisms frame received in %" PRI_MU_ENUM " state", MU_ENUM_VALUE(SASL_CLIENT_NEGOTIATION_STATE, sasl_client_io_instance->sasl_client_negotiation_state));
                        handle_error(sasl_client_io_instance);
                        break;

                    case SASL_CLIENT_NEGOTIATION_NOT_STARTED:
                    {
                        SASL_MECHANISMS_HANDLE sasl_mechanisms_handle;

                        if (amqpvalue_get_sasl_mechanisms(sasl_frame, &sasl_mechanisms_handle) != 0)
                        {
                            /* Codes_SRS_SASLCLIENTIO_01_119: [If any error is encountered when parsing the received frame, the `on_io_open_complete` callback shall be triggered with `IO_OPEN_ERROR`.]*/
                            LogError("Could not get SASL mechanisms");
                            handle_error(sasl_client_io_instance);
                        }
                        else
                        {
                            AMQP_VALUE sasl_server_mechanisms;
                            uint32_t mechanisms_count;

                            if ((sasl_mechanisms_get_sasl_server_mechanisms(sasl_mechanisms_handle, &sasl_server_mechanisms) != 0) ||
                                (amqpvalue_get_array_item_count(sasl_server_mechanisms, &mechanisms_count) != 0) ||
                                (mechanisms_count == 0))
                            {
                                /* Codes_SRS_SASLCLIENTIO_01_042: [It is invalid for this list to be null or empty.] */
                                LogError("Invalid SASL mechanisms list");
                                handle_error(sasl_client_io_instance);
                            }
                            else
                            {
                                const char* sasl_mechanism_name = saslmechanism_get_mechanism_name(sasl_client_io_instance->sasl_mechanism);
                                if (sasl_mechanism_name == NULL)
                                {
                                    /* Codes_SRS_SASLCLIENTIO_01_119: [If any error is encountered when parsing the received frame, the `on_io_open_complete` callback shall be triggered with `IO_OPEN_ERROR`.]*/
                                    LogError("Cannot get the mechanism name");
                                    handle_error(sasl_client_io_instance);
                                }
                                else
                                {
                                    uint32_t i;

                                    for (i = 0; i < mechanisms_count; i++)
                                    {
                                        AMQP_VALUE sasl_server_mechanism;
                                        sasl_server_mechanism = amqpvalue_get_array_item(sasl_server_mechanisms, i);
                                        if (sasl_server_mechanism == NULL)
                                        {
                                            LogError("Cannot get SASL mechanisms array item for index %u", (unsigned int)i);
                                            i = mechanisms_count;
                                        }
                                        else
                                        {
                                            const char* sasl_server_mechanism_name;
                                            if (amqpvalue_get_symbol(sasl_server_mechanism, &sasl_server_mechanism_name) != 0)
                                            {
                                                LogError("Error getting server SASL mechanism from array item");
                                                i = mechanisms_count;
                                            }
                                            else
                                            {
                                                if (strcmp(sasl_mechanism_name, sasl_server_mechanism_name) == 0)
                                                {
                                                    amqpvalue_destroy(sasl_server_mechanism);
                                                    break;
                                                }
                                            }

                                            amqpvalue_destroy(sasl_server_mechanism);
                                        }
                                    }

                                    if (i == mechanisms_count)
                                    {
                                        /* Codes_SRS_SASLCLIENTIO_01_119: [If any error is encountered when parsing the received frame, the `on_io_open_complete` callback shall be triggered with `IO_OPEN_ERROR`.]*/
                                        LogError("Could not find desired SASL mechanism in the list presented by server");
                                        handle_error(sasl_client_io_instance);
                                    }
                                    else
                                    {
                                        sasl_client_io_instance->sasl_client_negotiation_state = SASL_CLIENT_NEGOTIATION_MECH_RCVD;

                                        /* Codes_SRS_SASLCLIENTIO_01_035: [SASL-INIT -->] */
                                        /* Codes_SRS_SASLCLIENTIO_01_033: [The partner MUST then choose one of the supported mechanisms and initiate a sasl exchange.] */
                                        /* Codes_SRS_SASLCLIENTIO_01_054: [Selects the sasl mechanism and provides the initial response if needed.] */
                                        if (send_sasl_init(sasl_client_io_instance, sasl_mechanism_name) != 0)
                                        {
                                            /* Codes_SRS_SASLCLIENTIO_01_119: [If any error is encountered when parsing the received frame, the `on_io_open_complete` callback shall be triggered with `IO_OPEN_ERROR`.]*/
                                            LogError("Could not send SASL init");
                                            handle_error(sasl_client_io_instance);
                                        }
                                        else
                                        {
                                            sasl_client_io_instance->sasl_client_negotiation_state = SASL_CLIENT_NEGOTIATION_INIT_SENT;
                                        }
                                    }
                                }
                            }

                            sasl_mechanisms_destroy(sasl_mechanisms_handle);
                        }

                        break;
                    }
                    }
                }
                /* Codes_SRS_SASLCLIENTIO_01_052: [Send the SASL challenge data as defined by the SASL specification.] */
                /* Codes_SRS_SASLCLIENTIO_01_036: [<-- SASL-CHALLENGE *] */
                /* Codes_SRS_SASLCLIENTIO_01_039: [the SASL challenge/response step can occur zero or more times depending on the details of the SASL mechanism chosen.] */
                else if (is_sasl_challenge_type_by_descriptor(descriptor))
                {
                    /* Codes_SRS_SASLCLIENTIO_01_032: [The peer acting as the SASL server MUST announce supported authentication mechanisms using the sasl-mechanisms frame.] */
                    if ((sasl_client_io_instance->sasl_client_negotiation_state != SASL_CLIENT_NEGOTIATION_INIT_SENT) &&
                        (sasl_client_io_instance->sasl_client_negotiation_state != SASL_CLIENT_NEGOTIATION_RESPONSE_SENT))
                    {
                        LogError("SASL challenge received in a bad state: %" PRI_MU_ENUM "", MU_ENUM_VALUE(SASL_CLIENT_NEGOTIATION_STATE, sasl_client_io_instance->sasl_client_negotiation_state));
                        handle_error(sasl_client_io_instance);
                    }
                    else
                    {
                        SASL_CHALLENGE_HANDLE sasl_challenge_handle;

                        if (amqpvalue_get_sasl_challenge(sasl_frame, &sasl_challenge_handle) != 0)
                        {
                            /* Codes_SRS_SASLCLIENTIO_01_119: [If any error is encountered when parsing the received frame, the `on_io_open_complete` callback shall be triggered with `IO_OPEN_ERROR`.]*/
                            LogError("Cannot get SASL challenge values");
                            handle_error(sasl_client_io_instance);
                        }
                        else
                        {
                            amqp_binary challenge_binary_value;

                            challenge_binary_value.bytes = NULL;
                            challenge_binary_value.length = 0;

                            /* Codes_SRS_SASLCLIENTIO_01_053: [Challenge information, a block of opaque binary data passed to the security mechanism.] */
                            if (sasl_challenge_get_challenge(sasl_challenge_handle, &challenge_binary_value) != 0)
                            {
                                /* Codes_SRS_SASLCLIENTIO_01_119: [If any error is encountered when parsing the received frame, the `on_io_open_complete` callback shall be triggered with `IO_OPEN_ERROR`.]*/
                                LogError("Cannot get SASL challenge binary value");
                                handle_error(sasl_client_io_instance);
                            }
                            else
                            {
                                SASL_MECHANISM_BYTES challenge;
                                SASL_MECHANISM_BYTES response_bytes;

                                challenge.bytes = challenge_binary_value.bytes;
                                challenge.length = challenge_binary_value.length;
                                response_bytes.bytes = NULL;
                                response_bytes.length = 0;

                                /* Codes_SRS_SASLCLIENTIO_01_057: [The contents of this data are defined by the SASL security mechanism.] */
                                /* Codes_SRS_SASLCLIENTIO_01_037: [SASL-RESPONSE -->] */
                                if (saslmechanism_challenge(sasl_client_io_instance->sasl_mechanism, &challenge, &response_bytes) != 0)
                                {
                                    /* Codes_SRS_SASLCLIENTIO_01_119: [If any error is encountered when parsing the received frame, the `on_io_open_complete` callback shall be triggered with `IO_OPEN_ERROR`.]*/
                                    LogError("SASL Challenge failed");
                                    handle_error(sasl_client_io_instance);
                                }
                                else if (send_sasl_response(sasl_client_io_instance, response_bytes) != 0)
                                {
                                    /* Codes_SRS_SASLCLIENTIO_01_119: [If any error is encountered when parsing the received frame, the `on_io_open_complete` callback shall be triggered with `IO_OPEN_ERROR`.]*/
                                    LogError("Cannot send SASL response");
                                    handle_error(sasl_client_io_instance);
                                }
                            }

                            sasl_challenge_destroy(sasl_challenge_handle);
                        }
                    }
                }
                /* Codes_SRS_SASLCLIENTIO_01_058: [This frame indicates the outcome of the SASL dialog.] */
                /* Codes_SRS_SASLCLIENTIO_01_038: [<-- SASL-OUTCOME] */
                else if (is_sasl_outcome_type_by_descriptor(descriptor))
                {
                    /* Codes_SRS_SASLCLIENTIO_01_032: [The peer acting as the SASL server MUST announce supported authentication mechanisms using the sasl-mechanisms frame.] */
                    if ((sasl_client_io_instance->sasl_client_negotiation_state != SASL_CLIENT_NEGOTIATION_INIT_SENT) &&
                        (sasl_client_io_instance->sasl_client_negotiation_state != SASL_CLIENT_NEGOTIATION_RESPONSE_SENT))
                    {
                        LogError("SASL outcome received in a bad state: %" PRI_MU_ENUM "", MU_ENUM_VALUE(SASL_CLIENT_NEGOTIATION_STATE, sasl_client_io_instance->sasl_client_negotiation_state));
                        handle_error(sasl_client_io_instance);
                    }
                    else
                    {
                        SASL_OUTCOME_HANDLE sasl_outcome;

                        sasl_client_io_instance->sasl_client_negotiation_state = SASL_CLIENT_NEGOTIATION_OUTCOME_RCVD;

                        if (amqpvalue_get_sasl_outcome(sasl_frame, &sasl_outcome) != 0)
                        {
                            LogError("Cannot get SASL outcome");
                            handle_error(sasl_client_io_instance);
                        }
                        else
                        {
                            sasl_code sasl_code;

                            /* Codes_SRS_SASLCLIENTIO_01_060: [A reply-code indicating the outcome of the SASL dialog.] */
                            if (sasl_outcome_get_code(sasl_outcome, &sasl_code) != 0)
                            {
                                LogError("Cannot get SASL outcome code");
                                handle_error(sasl_client_io_instance);
                            }
                            else
                            {
                                switch (sasl_code)
                                {
                                default:
                                case sasl_code_auth:
                                    /* Codes_SRS_SASLCLIENTIO_01_063: [1 Connection authentication failed due to an unspecified problem with the supplied credentials.] */
                                case sasl_code_sys:
                                    /* Codes_SRS_SASLCLIENTIO_01_064: [2 Connection authentication failed due to a system error.] */
                                case sasl_code_sys_perm:
                                    /* Codes_SRS_SASLCLIENTIO_01_065: [3 Connection authentication failed due to a system error that is unlikely to be corrected without intervention.] */
                                case sasl_code_sys_temp:
                                    /* Codes_SRS_SASLCLIENTIO_01_066: [4 Connection authentication failed due to a transient system error.] */
                                    LogError("SASL handshake failed with code %02X", (unsigned char)sasl_code);
                                    handle_error(sasl_client_io_instance);
                                    break;

                                case sasl_code_ok:
                                    /* Codes_SRS_SASLCLIENTIO_01_059: [Upon successful completion of the SASL dialog the security layer has been established] */
                                    /* Codes_SRS_SASLCLIENTIO_01_062: [0 Connection authentication succeeded.]  */
                                    sasl_client_io_instance->io_state = IO_STATE_OPEN;

                                    /* Codes_SRS_SASLCLIENTIO_01_072: [When the SASL handshake is complete, if the handshake is successful, the SASL client IO state shall be switched to `IO_STATE_OPEN` and the `on_io_open_complete` callback shall be called with `IO_OPEN_OK`.]*/
                                    indicate_open_complete(sasl_client_io_instance, IO_OPEN_OK);
                                    break;
                                }
                            }

                            sasl_outcome_destroy(sasl_outcome);
                        }
                    }
                }
                else
                {
                    LogError("Bad SASL frame");
                }
            }
        }
        break;
    }
}