<NME> rdpsnd-messages.c
<BEF> /*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

#include "channels/rdpsnd/rdpsnd-messages.h"
#include "channels/rdpsnd/rdpsnd.h"
#include "rdp.h"

#include <freerdp/codec/audio.h>
#include <guacamole/audio.h>
#include <guacamole/client.h>
#include <winpr/stream.h>
#include <winpr/wtypes.h>

#include <stdlib.h>
#include <string.h>

void guac_rdpsnd_formats_handler(guac_rdp_common_svc* svc,
        wStream* input_stream, guac_rdpsnd_pdu_header* header) {

    int server_format_count;
    int server_version;
    int i;

    wStream* output_stream;
    int output_body_size;
    unsigned char* output_stream_end;

    guac_client* client = svc->client;
    guac_rdpsnd* rdpsnd = (guac_rdpsnd*) svc->data;

    guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
    guac_audio_stream* audio = rdp_client->audio;

    /* Reset own format count */
    rdpsnd->format_count = 0;

    /* 
     * Check to make sure the stream has at least 20 bytes (14 byte seek,
     * 2 x UTF16 reads, and 2 x UTF8 seeks).
     */
    if (Stream_GetRemainingLength(input_stream) < 20) {
        guac_client_log(client, GUAC_LOG_WARNING, "Server Audio Formats and "
                "Version PDU does not contain the expected number of bytes. "
                "Audio redirection may not work as expected.");
        return;
    }
    
    /* Format header */
    Stream_Seek(input_stream, 14);
    Stream_Read_UINT16(input_stream, server_format_count);
    Stream_Seek_UINT8(input_stream);
    Stream_Read_UINT16(input_stream, server_version);
    Stream_Seek_UINT8(input_stream);

    /* Initialize Client Audio Formats and Version PDU */
    output_stream = Stream_New(NULL, 24);
    Stream_Write_UINT8(output_stream,  SNDC_FORMATS);
    Stream_Write_UINT8(output_stream,  0);

    /* Fill in body size later */
    Stream_Seek_UINT16(output_stream); /* offset = 0x02 */

    /* Flags, volume, and pitch */
    Stream_Write_UINT32(output_stream, TSSNDCAPS_ALIVE);
    Stream_Write_UINT32(output_stream, 0);
    Stream_Write_UINT32(output_stream, 0);

    /* Datagram port (UDP) */
    Stream_Write_UINT16(output_stream, 0);

    /* Fill in format count later */
    Stream_Seek_UINT16(output_stream); /* offset = 0x12 */

    /* Version and padding */
    Stream_Write_UINT8(output_stream,  0);
    Stream_Write_UINT16(output_stream, 6);
    Stream_Write_UINT8(output_stream,  0);

    /* Check each server format, respond if supported and audio is enabled */
    if (audio != NULL) {
        for (i=0; i < server_format_count; i++) {

            unsigned char* format_start;

            int format_tag;
            int channels;
            int rate;
            int bps;
            int body_size;

            /* Remember position in stream */
            Stream_GetPointer(input_stream, format_start);

            /* Check to make sure Stream has at least 18 bytes. */
            if (Stream_GetRemainingLength(input_stream) < 18) {
                guac_client_log(client, GUAC_LOG_WARNING, "Server Audio "
                        "Formats and Version PDU does not contain the expected "
                        "number of bytes. Audio redirection may not work as "
                        "expected.");
                return;
            }
            
            /* Read format */
            Stream_Read_UINT16(input_stream, format_tag);
            Stream_Read_UINT16(input_stream, channels);
            Stream_Read_UINT32(input_stream, rate);
            Stream_Seek_UINT32(input_stream);
            Stream_Seek_UINT16(input_stream);
            Stream_Read_UINT16(input_stream, bps);

            /* Skip past extra data */
            Stream_Read_UINT16(input_stream, body_size);
            
            /* Check that Stream has at least body_size bytes remaining. */
            if (Stream_GetRemainingLength(input_stream) < body_size) {
                guac_client_log(client, GUAC_LOG_WARNING, "Server Audio "
                        "Formats and Version PDU does not contain the expected "
                        "number of bytes. Audio redirection may not work as "
                        "expected.");
                return;
            }
            
            Stream_Seek(input_stream, body_size);

            /* If PCM, accept */
            if (format_tag == WAVE_FORMAT_PCM) {

                /* If can fit another format, accept it */
                if (rdpsnd->format_count < GUAC_RDP_MAX_FORMATS) {

                    /* Add channel */
                    int current = rdpsnd->format_count++;
                    rdpsnd->formats[current].rate     = rate;
                    rdpsnd->formats[current].channels = channels;
                    rdpsnd->formats[current].bps      = bps;

                    /* Log format */
                    guac_client_log(client, GUAC_LOG_INFO,
                            "Accepted format: %i-bit PCM with %i channels at "
                            "%i Hz",
                            bps, channels, rate);

                    /* Ensure audio stream is configured to use accepted
                     * format */
                    guac_audio_stream_reset(audio, NULL, rate, channels, bps);

                    /* Queue format for sending as accepted */
                    Stream_EnsureRemainingCapacity(output_stream,
                            18 + body_size);
                    Stream_Write(output_stream, format_start, 18 + body_size);

                    /*
                     * BEWARE that using Stream_EnsureRemainingCapacity means
                     * that any pointers returned via Stream_GetPointer on
                     * output_stream are invalid.
                     */

                }

                /* Otherwise, log that we dropped one */
                else
                    guac_client_log(client, GUAC_LOG_INFO,
                            "Dropped valid format: %i-bit PCM with %i "
                            "channels at %i Hz",
                            bps, channels, rate);

            }

        }
    }

    /* Otherwise, ignore all supported formats as we do not intend to actually
     * receive audio */
    else
        guac_client_log(client, GUAC_LOG_DEBUG,
                "Audio explicitly disabled. Ignoring supported formats.");

    /* Calculate size of PDU */
    output_body_size = Stream_GetPosition(output_stream) - 4;
    Stream_GetPointer(output_stream, output_stream_end);

    /* Set body size */
    Stream_SetPosition(output_stream, 0x02);
    Stream_Write_UINT16(output_stream, output_body_size);

    /* Set format count */
    Stream_SetPosition(output_stream, 0x12);
    Stream_Write_UINT16(output_stream, rdpsnd->format_count);

    /* Reposition cursor at end (necessary for message send) */
    Stream_SetPointer(output_stream, output_stream_end);

    /* Send accepted formats */
    guac_rdp_common_svc_write(svc, output_stream);

    /* If version greater than 6, must send Quality Mode PDU */
    if (server_version >= 6) {

        /* Always send High Quality for now */
        output_stream = Stream_New(NULL, 8);
        Stream_Write_UINT8(output_stream, SNDC_QUALITYMODE);
        Stream_Write_UINT8(output_stream, 0);
        Stream_Write_UINT16(output_stream, 4);
        Stream_Write_UINT16(output_stream, HIGH_QUALITY);
        Stream_Write_UINT16(output_stream, 0);

        guac_rdp_common_svc_write(svc, output_stream);

    }

}

/* server is getting a feel of the round trip time */
void guac_rdpsnd_training_handler(guac_rdp_common_svc* svc,
        wStream* input_stream, guac_rdpsnd_pdu_header* header) {

    int data_size;
    wStream* output_stream;

    guac_rdpsnd* rdpsnd = (guac_rdpsnd*) svc->data;

    /* Check to make sure audio stream contains a minimum number of bytes. */
    if (Stream_GetRemainingLength(input_stream) < 4) {
        guac_client_log(svc->client, GUAC_LOG_WARNING, "Audio Training PDU "
                "does not contain the expected number of bytes. Audio "
                "redirection may not work as expected.");
        return;
    }
    
    /* Read timestamp and data size */
    Stream_Read_UINT16(input_stream, rdpsnd->server_timestamp);
    Stream_Read_UINT16(input_stream, data_size);

    /* Send training response */
    output_stream = Stream_New(NULL, 8);
    Stream_Write_UINT8(output_stream, SNDC_TRAINING);
    Stream_Write_UINT8(output_stream, 0);
    Stream_Write_UINT16(output_stream, 4);
    Stream_Write_UINT16(output_stream, rdpsnd->server_timestamp);
    Stream_Write_UINT16(output_stream, data_size);

    guac_rdp_common_svc_write(svc, output_stream);

}

void guac_rdpsnd_wave_info_handler(guac_rdp_common_svc* svc,
        wStream* input_stream, guac_rdpsnd_pdu_header* header) {

    int format;

    guac_client* client = svc->client;
    guac_rdpsnd* rdpsnd = (guac_rdpsnd*) svc->data;

    guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
    guac_audio_stream* audio = rdp_client->audio;

    /* Check to make sure audio stream contains a minimum number of bytes. */
    if (Stream_GetRemainingLength(input_stream) < 12) {
        guac_client_log(svc->client, GUAC_LOG_WARNING, "Audio WaveInfo PDU "
                "does not contain the expected number of bytes. Sound may not "
                "work as expected.");
        return;
    }
    
    /* Read wave information */
    Stream_Read_UINT16(input_stream, rdpsnd->server_timestamp);
    Stream_Read_UINT16(input_stream, format);
    Stream_Read_UINT8(input_stream, rdpsnd->waveinfo_block_number);
    Stream_Seek(input_stream, 3);
    Stream_Read(input_stream, rdpsnd->initial_wave_data, 4);

    /*
     * Size of incoming wave data is equal to the body size field of this
     * header, less the size of a WaveInfo PDU (not including the header),
     * thus body_size - 12.
     */
    rdpsnd->incoming_wave_size = header->body_size - 12;

    /* Read wave in next iteration */
    rdpsnd->next_pdu_is_wave = TRUE;

    /* Reset audio stream if format has changed */
    if (audio != NULL) {
        if (format < sizeof(rdpsnd->formats)) 
            guac_audio_stream_reset(audio, NULL,
                    rdpsnd->formats[format].rate,
                    rdpsnd->formats[format].channels,
                    rdpsnd->formats[format].bps);
    
        else
            guac_client_log(svc->client, GUAC_LOG_WARNING, "RDP server "
                    "attempted to specify an invalid audio format. Sound may "
                    "not work as expected.");
    }

}

void guac_rdpsnd_wave_handler(guac_rdp_common_svc* svc,
        wStream* input_stream, guac_rdpsnd_pdu_header* header) {

    guac_client* client = svc->client;
    guac_rdpsnd* rdpsnd = (guac_rdpsnd*) svc->data;

    guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
    guac_audio_stream* audio = rdp_client->audio;
    
    /* Verify that the stream has bytes to cover the wave size plus header. */
    if (Stream_Length(input_stream) < (rdpsnd->incoming_wave_size + 4)) {
        guac_client_log(svc->client, GUAC_LOG_WARNING, "Audio Wave PDU does "
                "not contain the expected number of bytes. Sound may not work "
                "as expected.");
        return;
    }

    /* Wave Confirmation PDU */
    wStream* output_stream = Stream_New(NULL, 8);

    /* Get wave data */
    unsigned char* buffer = Stream_Buffer(input_stream);

    /* Copy over first four bytes */
    memcpy(buffer, rdpsnd->initial_wave_data, 4);

    /* Write rest of audio packet */
    if (audio != NULL) {
        guac_audio_stream_write_pcm(audio, buffer,
                rdpsnd->incoming_wave_size + 4);
        guac_audio_stream_flush(audio);
    }

    /* Write Wave Confirmation PDU */
    Stream_Write_UINT8(output_stream, SNDC_WAVECONFIRM);
    Stream_Write_UINT8(output_stream, 0);
    Stream_Write_UINT16(output_stream, 4);
    Stream_Write_UINT16(output_stream, rdpsnd->server_timestamp);
    Stream_Write_UINT8(output_stream, rdpsnd->waveinfo_block_number);
    Stream_Write_UINT8(output_stream, 0);

    /* Send Wave Confirmation PDU */
    guac_rdp_common_svc_write(svc, output_stream);

    /* We no longer expect to receive wave data */
    rdpsnd->next_pdu_is_wave = FALSE;

}

void guac_rdpsnd_close_handler(guac_rdp_common_svc* svc,
        wStream* input_stream, guac_rdpsnd_pdu_header* header) {

    /* Do nothing */

}


<MSG> GUACAMOLE-1059: Merge correct verification of sound format index.


<DFF> @@ -297,7 +297,7 @@ void guac_rdpsnd_wave_info_handler(guac_rdp_common_svc* svc,
 
     /* Reset audio stream if format has changed */
     if (audio != NULL) {
-        if (format < sizeof(rdpsnd->formats)) 
+        if (format < GUAC_RDP_MAX_FORMATS)
             guac_audio_stream_reset(audio, NULL,
                     rdpsnd->formats[format].rate,
                     rdpsnd->formats[format].channels,

{'commit': 'diff --git a/src/protocols/rdp/channels/rdpsnd/rdpsnd-messages.c b/src/protocols/rdp/channels/rdpsnd/rdpsnd-messages.c\nindex 2256fb27..1aeb8df9 100644\n--- a/src/protocols/rdp/channels/rdpsnd/rdpsnd-messages.c\n+++ b/src/protocols/rdp/channels/rdpsnd/rdpsnd-messages.c\n@@ -297,7 +297,7 @@ void guac_rdpsnd_wave_info_handler(guac_rdp_common_svc* svc,\n \n     /* Reset audio stream if format has changed */\n     if (audio != NULL) {\n-        if (format < sizeof(rdpsnd->formats)) \n+        if (format < GUAC_RDP_MAX_FORMATS)\n             guac_audio_stream_reset(audio, NULL,\n                     rdpsnd->formats[format].rate,\n                     rdpsnd->formats[format].channels,\n', 'commit_subject': 'GUACAMOLE-1059: Merge correct verification of sound format index.', 'repo_name': 'apache/guacamole-server', 'committer': {'name': 'GitHub', 'email': '1505422b2465e9a84f6fdfaa161078890c593f06@github.com', 'time_sec': '1591528822', 'tz_offset': '-240', 'date': {'seconds': '1591528822'}}, 'commit_tree': '245e1312efacd184e3d1873a23ae78efdd9d502d', 'commit_message': 'GUACAMOLE-1059: Merge correct verification of sound format index.\n\n', 'language_name': 'Perl', 'license': 'apache-2.0', 'hunks': '@@ -297,7 +297,7 @@ void guac_rdpsnd_wave_info_handler(guac_rdp_common_svc* svc,\n \n     /* Reset audio stream if format has changed */\n     if (audio != NULL) {\n-        if (format < sizeof(rdpsnd->formats)) \n+        if (format < GUAC_RDP_MAX_FORMATS)\n             guac_audio_stream_reset(audio, NULL,\n                     rdpsnd->formats[format].rate,\n                     rdpsnd->formats[format].channels,\n', 'addition_count': 1, 'deletion_count': 1, 'src_file': 'a/src/protocols/rdp/channels/rdpsnd/rdpsnd-messages.c', 'tgt_file': 'b/src/protocols/rdp/channels/rdpsnd/rdpsnd-messages.c', 'file_extension': '.c', 'before_file': '/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements.  See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership.  The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * "License"); you may not use this file except in compliance\n * with the License.  You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied.  See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\n\n#include "channels/rdpsnd/rdpsnd-messages.h"\n#include "channels/rdpsnd/rdpsnd.h"\n#include "rdp.h"\n\n#include <freerdp/codec/audio.h>\n#include <guacamole/audio.h>\n#include <guacamole/client.h>\n#include <winpr/stream.h>\n#include <winpr/wtypes.h>\n\n#include <stdlib.h>\n#include <string.h>\n\nvoid guac_rdpsnd_formats_handler(guac_rdp_common_svc* svc,\n        wStream* input_stream, guac_rdpsnd_pdu_header* header) {\n\n    int server_format_count;\n    int server_version;\n    int i;\n\n    wStream* output_stream;\n    int output_body_size;\n    unsigned char* output_stream_end;\n\n    guac_client* client = svc->client;\n    guac_rdpsnd* rdpsnd = (guac_rdpsnd*) svc->data;\n\n    guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;\n    guac_audio_stream* audio = rdp_client->audio;\n\n    /* Reset own format count */\n    rdpsnd->format_count = 0;\n\n    /* \n     * Check to make sure the stream has at least 20 bytes (14 byte seek,\n     * 2 x UTF16 reads, and 2 x UTF8 seeks).\n     */\n    if (Stream_GetRemainingLength(input_stream) < 20) {\n        guac_client_log(client, GUAC_LOG_WARNING, "Server Audio Formats and "\n                "Version PDU does not contain the expected number of bytes. "\n                "Audio redirection may not work as expected.");\n        return;\n    }\n    \n    /* Format header */\n    Stream_Seek(input_stream, 14);\n    Stream_Read_UINT16(input_stream, server_format_count);\n    Stream_Seek_UINT8(input_stream);\n    Stream_Read_UINT16(input_stream, server_version);\n    Stream_Seek_UINT8(input_stream);\n\n    /* Initialize Client Audio Formats and Version PDU */\n    output_stream = Stream_New(NULL, 24);\n    Stream_Write_UINT8(output_stream,  SNDC_FORMATS);\n    Stream_Write_UINT8(output_stream,  0);\n\n    /* Fill in body size later */\n    Stream_Seek_UINT16(output_stream); /* offset = 0x02 */\n\n    /* Flags, volume, and pitch */\n    Stream_Write_UINT32(output_stream, TSSNDCAPS_ALIVE);\n    Stream_Write_UINT32(output_stream, 0);\n    Stream_Write_UINT32(output_stream, 0);\n\n    /* Datagram port (UDP) */\n    Stream_Write_UINT16(output_stream, 0);\n\n    /* Fill in format count later */\n    Stream_Seek_UINT16(output_stream); /* offset = 0x12 */\n\n    /* Version and padding */\n    Stream_Write_UINT8(output_stream,  0);\n    Stream_Write_UINT16(output_stream, 6);\n    Stream_Write_UINT8(output_stream,  0);\n\n    /* Check each server format, respond if supported and audio is enabled */\n    if (audio != NULL) {\n        for (i=0; i < server_format_count; i++) {\n\n            unsigned char* format_start;\n\n            int format_tag;\n            int channels;\n            int rate;\n            int bps;\n            int body_size;\n\n            /* Remember position in stream */\n            Stream_GetPointer(input_stream, format_start);\n\n            /* Check to make sure Stream has at least 18 bytes. */\n            if (Stream_GetRemainingLength(input_stream) < 18) {\n                guac_client_log(client, GUAC_LOG_WARNING, "Server Audio "\n                        "Formats and Version PDU does not contain the expected "\n                        "number of bytes. Audio redirection may not work as "\n                        "expected.");\n                return;\n            }\n            \n            /* Read format */\n            Stream_Read_UINT16(input_stream, format_tag);\n            Stream_Read_UINT16(input_stream, channels);\n            Stream_Read_UINT32(input_stream, rate);\n            Stream_Seek_UINT32(input_stream);\n            Stream_Seek_UINT16(input_stream);\n            Stream_Read_UINT16(input_stream, bps);\n\n            /* Skip past extra data */\n            Stream_Read_UINT16(input_stream, body_size);\n            \n            /* Check that Stream has at least body_size bytes remaining. */\n            if (Stream_GetRemainingLength(input_stream) < body_size) {\n                guac_client_log(client, GUAC_LOG_WARNING, "Server Audio "\n                        "Formats and Version PDU does not contain the expected "\n                        "number of bytes. Audio redirection may not work as "\n                        "expected.");\n                return;\n            }\n            \n            Stream_Seek(input_stream, body_size);\n\n            /* If PCM, accept */\n            if (format_tag == WAVE_FORMAT_PCM) {\n\n                /* If can fit another format, accept it */\n                if (rdpsnd->format_count < GUAC_RDP_MAX_FORMATS) {\n\n                    /* Add channel */\n                    int current = rdpsnd->format_count++;\n                    rdpsnd->formats[current].rate     = rate;\n                    rdpsnd->formats[current].channels = channels;\n                    rdpsnd->formats[current].bps      = bps;\n\n                    /* Log format */\n                    guac_client_log(client, GUAC_LOG_INFO,\n                            "Accepted format: %i-bit PCM with %i channels at "\n                            "%i Hz",\n                            bps, channels, rate);\n\n                    /* Ensure audio stream is configured to use accepted\n                     * format */\n                    guac_audio_stream_reset(audio, NULL, rate, channels, bps);\n\n                    /* Queue format for sending as accepted */\n                    Stream_EnsureRemainingCapacity(output_stream,\n                            18 + body_size);\n                    Stream_Write(output_stream, format_start, 18 + body_size);\n\n                    /*\n                     * BEWARE that using Stream_EnsureRemainingCapacity means\n                     * that any pointers returned via Stream_GetPointer on\n                     * output_stream are invalid.\n                     */\n\n                }\n\n                /* Otherwise, log that we dropped one */\n                else\n                    guac_client_log(client, GUAC_LOG_INFO,\n                            "Dropped valid format: %i-bit PCM with %i "\n                            "channels at %i Hz",\n                            bps, channels, rate);\n\n            }\n\n        }\n    }\n\n    /* Otherwise, ignore all supported formats as we do not intend to actually\n     * receive audio */\n    else\n        guac_client_log(client, GUAC_LOG_DEBUG,\n                "Audio explicitly disabled. Ignoring supported formats.");\n\n    /* Calculate size of PDU */\n    output_body_size = Stream_GetPosition(output_stream) - 4;\n    Stream_GetPointer(output_stream, output_stream_end);\n\n    /* Set body size */\n    Stream_SetPosition(output_stream, 0x02);\n    Stream_Write_UINT16(output_stream, output_body_size);\n\n    /* Set format count */\n    Stream_SetPosition(output_stream, 0x12);\n    Stream_Write_UINT16(output_stream, rdpsnd->format_count);\n\n    /* Reposition cursor at end (necessary for message send) */\n    Stream_SetPointer(output_stream, output_stream_end);\n\n    /* Send accepted formats */\n    guac_rdp_common_svc_write(svc, output_stream);\n\n    /* If version greater than 6, must send Quality Mode PDU */\n    if (server_version >= 6) {\n\n        /* Always send High Quality for now */\n        output_stream = Stream_New(NULL, 8);\n        Stream_Write_UINT8(output_stream, SNDC_QUALITYMODE);\n        Stream_Write_UINT8(output_stream, 0);\n        Stream_Write_UINT16(output_stream, 4);\n        Stream_Write_UINT16(output_stream, HIGH_QUALITY);\n        Stream_Write_UINT16(output_stream, 0);\n\n        guac_rdp_common_svc_write(svc, output_stream);\n\n    }\n\n}\n\n/* server is getting a feel of the round trip time */\nvoid guac_rdpsnd_training_handler(guac_rdp_common_svc* svc,\n        wStream* input_stream, guac_rdpsnd_pdu_header* header) {\n\n    int data_size;\n    wStream* output_stream;\n\n    guac_rdpsnd* rdpsnd = (guac_rdpsnd*) svc->data;\n\n    /* Check to make sure audio stream contains a minimum number of bytes. */\n    if (Stream_GetRemainingLength(input_stream) < 4) {\n        guac_client_log(svc->client, GUAC_LOG_WARNING, "Audio Training PDU "\n                "does not contain the expected number of bytes. Audio "\n                "redirection may not work as expected.");\n        return;\n    }\n    \n    /* Read timestamp and data size */\n    Stream_Read_UINT16(input_stream, rdpsnd->server_timestamp);\n    Stream_Read_UINT16(input_stream, data_size);\n\n    /* Send training response */\n    output_stream = Stream_New(NULL, 8);\n    Stream_Write_UINT8(output_stream, SNDC_TRAINING);\n    Stream_Write_UINT8(output_stream, 0);\n    Stream_Write_UINT16(output_stream, 4);\n    Stream_Write_UINT16(output_stream, rdpsnd->server_timestamp);\n    Stream_Write_UINT16(output_stream, data_size);\n\n    guac_rdp_common_svc_write(svc, output_stream);\n\n}\n\nvoid guac_rdpsnd_wave_info_handler(guac_rdp_common_svc* svc,\n        wStream* input_stream, guac_rdpsnd_pdu_header* header) {\n\n    int format;\n\n    guac_client* client = svc->client;\n    guac_rdpsnd* rdpsnd = (guac_rdpsnd*) svc->data;\n\n    guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;\n    guac_audio_stream* audio = rdp_client->audio;\n\n    /* Check to make sure audio stream contains a minimum number of bytes. */\n    if (Stream_GetRemainingLength(input_stream) < 12) {\n        guac_client_log(svc->client, GUAC_LOG_WARNING, "Audio WaveInfo PDU "\n                "does not contain the expected number of bytes. Sound may not "\n                "work as expected.");\n        return;\n    }\n    \n    /* Read wave information */\n    Stream_Read_UINT16(input_stream, rdpsnd->server_timestamp);\n    Stream_Read_UINT16(input_stream, format);\n    Stream_Read_UINT8(input_stream, rdpsnd->waveinfo_block_number);\n    Stream_Seek(input_stream, 3);\n    Stream_Read(input_stream, rdpsnd->initial_wave_data, 4);\n\n    /*\n     * Size of incoming wave data is equal to the body size field of this\n     * header, less the size of a WaveInfo PDU (not including the header),\n     * thus body_size - 12.\n     */\n    rdpsnd->incoming_wave_size = header->body_size - 12;\n\n    /* Read wave in next iteration */\n    rdpsnd->next_pdu_is_wave = TRUE;\n\n    /* Reset audio stream if format has changed */\n    if (audio != NULL) {\n        if (format < sizeof(rdpsnd->formats)) \n            guac_audio_stream_reset(audio, NULL,\n                    rdpsnd->formats[format].rate,\n                    rdpsnd->formats[format].channels,\n                    rdpsnd->formats[format].bps);\n    \n        else\n            guac_client_log(svc->client, GUAC_LOG_WARNING, "RDP server "\n                    "attempted to specify an invalid audio format. Sound may "\n                    "not work as expected.");\n    }\n\n}\n\nvoid guac_rdpsnd_wave_handler(guac_rdp_common_svc* svc,\n        wStream* input_stream, guac_rdpsnd_pdu_header* header) {\n\n    guac_client* client = svc->client;\n    guac_rdpsnd* rdpsnd = (guac_rdpsnd*) svc->data;\n\n    guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;\n    guac_audio_stream* audio = rdp_client->audio;\n    \n    /* Verify that the stream has bytes to cover the wave size plus header. */\n    if (Stream_Length(input_stream) < (rdpsnd->incoming_wave_size + 4)) {\n        guac_client_log(svc->client, GUAC_LOG_WARNING, "Audio Wave PDU does "\n                "not contain the expected number of bytes. Sound may not work "\n                "as expected.");\n        return;\n    }\n\n    /* Wave Confirmation PDU */\n    wStream* output_stream = Stream_New(NULL, 8);\n\n    /* Get wave data */\n    unsigned char* buffer = Stream_Buffer(input_stream);\n\n    /* Copy over first four bytes */\n    memcpy(buffer, rdpsnd->initial_wave_data, 4);\n\n    /* Write rest of audio packet */\n    if (audio != NULL) {\n        guac_audio_stream_write_pcm(audio, buffer,\n                rdpsnd->incoming_wave_size + 4);\n        guac_audio_stream_flush(audio);\n    }\n\n    /* Write Wave Confirmation PDU */\n    Stream_Write_UINT8(output_stream, SNDC_WAVECONFIRM);\n    Stream_Write_UINT8(output_stream, 0);\n    Stream_Write_UINT16(output_stream, 4);\n    Stream_Write_UINT16(output_stream, rdpsnd->server_timestamp);\n    Stream_Write_UINT8(output_stream, rdpsnd->waveinfo_block_number);\n    Stream_Write_UINT8(output_stream, 0);\n\n    /* Send Wave Confirmation PDU */\n    guac_rdp_common_svc_write(svc, output_stream);\n\n    /* We no longer expect to receive wave data */\n    rdpsnd->next_pdu_is_wave = FALSE;\n\n}\n\nvoid guac_rdpsnd_close_handler(guac_rdp_common_svc* svc,\n        wStream* input_stream, guac_rdpsnd_pdu_header* header) {\n\n    /* Do nothing */\n\n}\n\n', 'message': 'GUACAMOLE-1059: Merge correct verification of sound format index.\n\n', 'author_name': 'GitHub', 'lang': 'c'}