cscg24-guacamole

CSCG 2024 Challenge 'Guacamole Mashup'
git clone https://git.sinitax.com/sinitax/cscg24-guacamole
Log | Files | Refs | sfeed.txt

rdpsnd-messages.c (13099B)


      1/*
      2 * Licensed to the Apache Software Foundation (ASF) under one
      3 * or more contributor license agreements.  See the NOTICE file
      4 * distributed with this work for additional information
      5 * regarding copyright ownership.  The ASF licenses this file
      6 * to you under the Apache License, Version 2.0 (the
      7 * "License"); you may not use this file except in compliance
      8 * with the License.  You may obtain a copy of the License at
      9 *
     10 *   http://www.apache.org/licenses/LICENSE-2.0
     11 *
     12 * Unless required by applicable law or agreed to in writing,
     13 * software distributed under the License is distributed on an
     14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
     15 * KIND, either express or implied.  See the License for the
     16 * specific language governing permissions and limitations
     17 * under the License.
     18 */
     19
     20#include "channels/rdpsnd/rdpsnd-messages.h"
     21#include "channels/rdpsnd/rdpsnd.h"
     22#include "rdp.h"
     23
     24#include <freerdp/codec/audio.h>
     25#include <guacamole/audio.h>
     26#include <guacamole/client.h>
     27#include <winpr/stream.h>
     28#include <winpr/wtypes.h>
     29
     30#include <stdlib.h>
     31#include <string.h>
     32
     33void guac_rdpsnd_formats_handler(guac_rdp_common_svc* svc,
     34        wStream* input_stream, guac_rdpsnd_pdu_header* header) {
     35
     36    int server_format_count;
     37    int server_version;
     38    int i;
     39
     40    wStream* output_stream;
     41    int output_body_size;
     42    unsigned char* output_stream_end;
     43
     44    guac_client* client = svc->client;
     45    guac_rdpsnd* rdpsnd = (guac_rdpsnd*) svc->data;
     46
     47    guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
     48    guac_audio_stream* audio = rdp_client->audio;
     49
     50    /* Reset own format count */
     51    rdpsnd->format_count = 0;
     52
     53    /* 
     54     * Check to make sure the stream has at least 20 bytes (14 byte seek,
     55     * 2 x UTF16 reads, and 2 x UTF8 seeks).
     56     */
     57    if (Stream_GetRemainingLength(input_stream) < 20) {
     58        guac_client_log(client, GUAC_LOG_WARNING, "Server Audio Formats and "
     59                "Version PDU does not contain the expected number of bytes. "
     60                "Audio redirection may not work as expected.");
     61        return;
     62    }
     63    
     64    /* Format header */
     65    Stream_Seek(input_stream, 14);
     66    Stream_Read_UINT16(input_stream, server_format_count);
     67    Stream_Seek_UINT8(input_stream);
     68    Stream_Read_UINT16(input_stream, server_version);
     69    Stream_Seek_UINT8(input_stream);
     70
     71    /* Initialize Client Audio Formats and Version PDU */
     72    output_stream = Stream_New(NULL, 24);
     73    Stream_Write_UINT8(output_stream,  SNDC_FORMATS);
     74    Stream_Write_UINT8(output_stream,  0);
     75
     76    /* Fill in body size later */
     77    Stream_Seek_UINT16(output_stream); /* offset = 0x02 */
     78
     79    /* Flags, volume, and pitch */
     80    Stream_Write_UINT32(output_stream, TSSNDCAPS_ALIVE);
     81    Stream_Write_UINT32(output_stream, 0);
     82    Stream_Write_UINT32(output_stream, 0);
     83
     84    /* Datagram port (UDP) */
     85    Stream_Write_UINT16(output_stream, 0);
     86
     87    /* Fill in format count later */
     88    Stream_Seek_UINT16(output_stream); /* offset = 0x12 */
     89
     90    /* Version and padding */
     91    Stream_Write_UINT8(output_stream,  0);
     92    Stream_Write_UINT16(output_stream, 6);
     93    Stream_Write_UINT8(output_stream,  0);
     94
     95    /* Check each server format, respond if supported and audio is enabled */
     96    if (audio != NULL) {
     97        for (i=0; i < server_format_count; i++) {
     98
     99            unsigned char* format_start;
    100
    101            int format_tag;
    102            int channels;
    103            int rate;
    104            int bps;
    105            int body_size;
    106
    107            /* Remember position in stream */
    108            Stream_GetPointer(input_stream, format_start);
    109
    110            /* Check to make sure Stream has at least 18 bytes. */
    111            if (Stream_GetRemainingLength(input_stream) < 18) {
    112                guac_client_log(client, GUAC_LOG_WARNING, "Server Audio "
    113                        "Formats and Version PDU does not contain the expected "
    114                        "number of bytes. Audio redirection may not work as "
    115                        "expected.");
    116                return;
    117            }
    118            
    119            /* Read format */
    120            Stream_Read_UINT16(input_stream, format_tag);
    121            Stream_Read_UINT16(input_stream, channels);
    122            Stream_Read_UINT32(input_stream, rate);
    123            Stream_Seek_UINT32(input_stream);
    124            Stream_Seek_UINT16(input_stream);
    125            Stream_Read_UINT16(input_stream, bps);
    126
    127            /* Skip past extra data */
    128            Stream_Read_UINT16(input_stream, body_size);
    129            
    130            /* Check that Stream has at least body_size bytes remaining. */
    131            if (Stream_GetRemainingLength(input_stream) < body_size) {
    132                guac_client_log(client, GUAC_LOG_WARNING, "Server Audio "
    133                        "Formats and Version PDU does not contain the expected "
    134                        "number of bytes. Audio redirection may not work as "
    135                        "expected.");
    136                return;
    137            }
    138            
    139            Stream_Seek(input_stream, body_size);
    140
    141            /* If PCM, accept */
    142            if (format_tag == WAVE_FORMAT_PCM) {
    143
    144                /* If can fit another format, accept it */
    145                if (rdpsnd->format_count < GUAC_RDP_MAX_FORMATS) {
    146
    147                    /* Add channel */
    148                    int current = rdpsnd->format_count++;
    149                    rdpsnd->formats[current].rate     = rate;
    150                    rdpsnd->formats[current].channels = channels;
    151                    rdpsnd->formats[current].bps      = bps;
    152
    153                    /* Log format */
    154                    guac_client_log(client, GUAC_LOG_INFO,
    155                            "Accepted format: %i-bit PCM with %i channels at "
    156                            "%i Hz",
    157                            bps, channels, rate);
    158
    159                    /* Ensure audio stream is configured to use accepted
    160                     * format */
    161                    guac_audio_stream_reset(audio, NULL, rate, channels, bps);
    162
    163                    /* Queue format for sending as accepted */
    164                    Stream_EnsureRemainingCapacity(output_stream,
    165                            18 + body_size);
    166                    Stream_Write(output_stream, format_start, 18 + body_size);
    167
    168                    /*
    169                     * BEWARE that using Stream_EnsureRemainingCapacity means
    170                     * that any pointers returned via Stream_GetPointer on
    171                     * output_stream are invalid.
    172                     */
    173
    174                }
    175
    176                /* Otherwise, log that we dropped one */
    177                else
    178                    guac_client_log(client, GUAC_LOG_INFO,
    179                            "Dropped valid format: %i-bit PCM with %i "
    180                            "channels at %i Hz",
    181                            bps, channels, rate);
    182
    183            }
    184
    185        }
    186    }
    187
    188    /* Otherwise, ignore all supported formats as we do not intend to actually
    189     * receive audio */
    190    else
    191        guac_client_log(client, GUAC_LOG_DEBUG,
    192                "Audio explicitly disabled. Ignoring supported formats.");
    193
    194    /* Calculate size of PDU */
    195    output_body_size = Stream_GetPosition(output_stream) - 4;
    196    Stream_GetPointer(output_stream, output_stream_end);
    197
    198    /* Set body size */
    199    Stream_SetPosition(output_stream, 0x02);
    200    Stream_Write_UINT16(output_stream, output_body_size);
    201
    202    /* Set format count */
    203    Stream_SetPosition(output_stream, 0x12);
    204    Stream_Write_UINT16(output_stream, rdpsnd->format_count);
    205
    206    /* Reposition cursor at end (necessary for message send) */
    207    Stream_SetPointer(output_stream, output_stream_end);
    208
    209    /* Send accepted formats */
    210    guac_rdp_common_svc_write(svc, output_stream);
    211
    212    /* If version greater than 6, must send Quality Mode PDU */
    213    if (server_version >= 6) {
    214
    215        /* Always send High Quality for now */
    216        output_stream = Stream_New(NULL, 8);
    217        Stream_Write_UINT8(output_stream, SNDC_QUALITYMODE);
    218        Stream_Write_UINT8(output_stream, 0);
    219        Stream_Write_UINT16(output_stream, 4);
    220        Stream_Write_UINT16(output_stream, HIGH_QUALITY);
    221        Stream_Write_UINT16(output_stream, 0);
    222
    223        guac_rdp_common_svc_write(svc, output_stream);
    224
    225    }
    226
    227}
    228
    229/* server is getting a feel of the round trip time */
    230void guac_rdpsnd_training_handler(guac_rdp_common_svc* svc,
    231        wStream* input_stream, guac_rdpsnd_pdu_header* header) {
    232
    233    int data_size;
    234    wStream* output_stream;
    235
    236    guac_rdpsnd* rdpsnd = (guac_rdpsnd*) svc->data;
    237
    238    /* Check to make sure audio stream contains a minimum number of bytes. */
    239    if (Stream_GetRemainingLength(input_stream) < 4) {
    240        guac_client_log(svc->client, GUAC_LOG_WARNING, "Audio Training PDU "
    241                "does not contain the expected number of bytes. Audio "
    242                "redirection may not work as expected.");
    243        return;
    244    }
    245    
    246    /* Read timestamp and data size */
    247    Stream_Read_UINT16(input_stream, rdpsnd->server_timestamp);
    248    Stream_Read_UINT16(input_stream, data_size);
    249
    250    /* Send training response */
    251    output_stream = Stream_New(NULL, 8);
    252    Stream_Write_UINT8(output_stream, SNDC_TRAINING);
    253    Stream_Write_UINT8(output_stream, 0);
    254    Stream_Write_UINT16(output_stream, 4);
    255    Stream_Write_UINT16(output_stream, rdpsnd->server_timestamp);
    256    Stream_Write_UINT16(output_stream, data_size);
    257
    258    guac_rdp_common_svc_write(svc, output_stream);
    259
    260}
    261
    262void guac_rdpsnd_wave_info_handler(guac_rdp_common_svc* svc,
    263        wStream* input_stream, guac_rdpsnd_pdu_header* header) {
    264
    265    int format;
    266
    267    guac_client* client = svc->client;
    268    guac_rdpsnd* rdpsnd = (guac_rdpsnd*) svc->data;
    269
    270    guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
    271    guac_audio_stream* audio = rdp_client->audio;
    272
    273    /* Check to make sure audio stream contains a minimum number of bytes. */
    274    if (Stream_GetRemainingLength(input_stream) < 12) {
    275        guac_client_log(svc->client, GUAC_LOG_WARNING, "Audio WaveInfo PDU "
    276                "does not contain the expected number of bytes. Sound may not "
    277                "work as expected.");
    278        return;
    279    }
    280    
    281    /* Read wave information */
    282    Stream_Read_UINT16(input_stream, rdpsnd->server_timestamp);
    283    Stream_Read_UINT16(input_stream, format);
    284    Stream_Read_UINT8(input_stream, rdpsnd->waveinfo_block_number);
    285    Stream_Seek(input_stream, 3);
    286    Stream_Read(input_stream, rdpsnd->initial_wave_data, 4);
    287
    288    /*
    289     * Size of incoming wave data is equal to the body size field of this
    290     * header, less the size of a WaveInfo PDU (not including the header),
    291     * thus body_size - 12.
    292     */
    293    rdpsnd->incoming_wave_size = header->body_size - 12;
    294
    295    /* Read wave in next iteration */
    296    rdpsnd->next_pdu_is_wave = TRUE;
    297
    298    /* Reset audio stream if format has changed */
    299    if (audio != NULL) {
    300        if (format < GUAC_RDP_MAX_FORMATS)
    301            guac_audio_stream_reset(audio, NULL,
    302                    rdpsnd->formats[format].rate,
    303                    rdpsnd->formats[format].channels,
    304                    rdpsnd->formats[format].bps);
    305    
    306        else
    307            guac_client_log(svc->client, GUAC_LOG_WARNING, "RDP server "
    308                    "attempted to specify an invalid audio format. Sound may "
    309                    "not work as expected.");
    310    }
    311
    312}
    313
    314void guac_rdpsnd_wave_handler(guac_rdp_common_svc* svc,
    315        wStream* input_stream, guac_rdpsnd_pdu_header* header) {
    316
    317    guac_client* client = svc->client;
    318    guac_rdpsnd* rdpsnd = (guac_rdpsnd*) svc->data;
    319
    320    guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
    321    guac_audio_stream* audio = rdp_client->audio;
    322    
    323    /* Verify that the stream has bytes to cover the wave size plus header. */
    324    if (Stream_Length(input_stream) < (rdpsnd->incoming_wave_size + 4)) {
    325        guac_client_log(svc->client, GUAC_LOG_WARNING, "Audio Wave PDU does "
    326                "not contain the expected number of bytes. Sound may not work "
    327                "as expected.");
    328        return;
    329    }
    330
    331    /* Wave Confirmation PDU */
    332    wStream* output_stream = Stream_New(NULL, 8);
    333
    334    /* Get wave data */
    335    unsigned char* buffer = Stream_Buffer(input_stream);
    336
    337    /* Copy over first four bytes */
    338    memcpy(buffer, rdpsnd->initial_wave_data, 4);
    339
    340    /* Write rest of audio packet */
    341    if (audio != NULL) {
    342        guac_audio_stream_write_pcm(audio, buffer,
    343                rdpsnd->incoming_wave_size + 4);
    344        guac_audio_stream_flush(audio);
    345    }
    346
    347    /* Write Wave Confirmation PDU */
    348    Stream_Write_UINT8(output_stream, SNDC_WAVECONFIRM);
    349    Stream_Write_UINT8(output_stream, 0);
    350    Stream_Write_UINT16(output_stream, 4);
    351    Stream_Write_UINT16(output_stream, rdpsnd->server_timestamp);
    352    Stream_Write_UINT8(output_stream, rdpsnd->waveinfo_block_number);
    353    Stream_Write_UINT8(output_stream, 0);
    354
    355    /* Send Wave Confirmation PDU */
    356    guac_rdp_common_svc_write(svc, output_stream);
    357
    358    /* We no longer expect to receive wave data */
    359    rdpsnd->next_pdu_is_wave = FALSE;
    360
    361}
    362
    363void guac_rdpsnd_close_handler(guac_rdp_common_svc* svc,
    364        wStream* input_stream, guac_rdpsnd_pdu_header* header) {
    365
    366    /* Do nothing */
    367
    368}
    369