cscg24-guacamole

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

guac-common-svc.c (12282B)


      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 "config.h"
     21#include "channels/common-svc.h"
     22
     23#include <freerdp/svc.h>
     24#include <guacamole/client.h>
     25#include <guacamole/mem.h>
     26#include <winpr/stream.h>
     27#include <winpr/wtsapi.h>
     28#include <winpr/wtypes.h>
     29
     30#include <stdlib.h>
     31
     32/**
     33 * Event handler for events which deal with data transmitted over an open SVC,
     34 * including receipt of inbound data and completion of outbound writes.
     35 *
     36 * The FreeRDP requirements for this function follow those of the
     37 * VirtualChannelOpenEventEx callback defined within Microsoft's RDP API:
     38 *
     39 * https://docs.microsoft.com/en-us/previous-versions/windows/embedded/aa514754%28v%3dmsdn.10%29
     40 *
     41 * @param user_param
     42 *     The pointer to arbitrary data originally passed via the first parameter
     43 *     of the pVirtualChannelInitEx() function call when the associated channel
     44 *     was initialized. The pVirtualChannelInitEx() function is exposed within
     45 *     the channel entry points structure.
     46 *
     47 * @param open_handle
     48 *     The handle which identifies the channel itself, typically referred to
     49 *     within the FreeRDP source as OpenHandle.
     50 *
     51 * @param event
     52 *     An integer representing the event that should be handled. This will be
     53 *     either CHANNEL_EVENT_DATA_RECEIVED, CHANNEL_EVENT_WRITE_CANCELLED, or
     54 *     CHANNEL_EVENT_WRITE_COMPLETE.
     55 *
     56 * @param data
     57 *     The data received, for CHANNEL_EVENT_DATA_RECEIVED events, and the value
     58 *     passed as user data to pVirtualChannelWriteEx() for
     59 *     CHANNEL_EVENT_WRITE_* events (note that user data for
     60 *     pVirtualChannelWriteEx() as implemented by FreeRDP MUST either be NULL
     61 *     or a wStream containing the data written).
     62 *
     63 * @param data_length
     64 *     The number of bytes of event-specific data.
     65 *
     66 * @param total_length
     67 *     The total number of bytes expected to be received from the RDP server
     68 *     due to this single write (from the server's perspective). Each write may
     69 *     actually be split into multiple chunks, thus resulting in multiple
     70 *     receive events for the same logical block of data. The relationship
     71 *     between chunks is indicated with the CHANNEL_FLAG_FIRST and
     72 *     CHANNEL_FLAG_LAST flags.
     73 *
     74 * @param data_flags
     75 *     The result of a bitwise OR of the CHANNEL_FLAG_* flags which apply to
     76 *     the data received. This value is relevant only to
     77 *     CHANNEL_EVENT_DATA_RECEIVED events. Valid flags are CHANNEL_FLAG_FIRST,
     78 *     CHANNEL_FLAG_LAST, and CHANNEL_FLAG_ONLY. The flag CHANNEL_FLAG_MIDDLE
     79 *     is not itself a flag, but the absence of both CHANNEL_FLAG_FIRST and
     80 *     CHANNEL_FLAG_LAST.
     81 */
     82static VOID guac_rdp_common_svc_handle_open_event(LPVOID user_param,
     83        DWORD open_handle, UINT event, LPVOID data, UINT32 data_length,
     84        UINT32 total_length, UINT32 data_flags) {
     85
     86#ifndef FREERDP_SVC_CORE_FREES_WSTREAM
     87    /* Free stream data after send is complete */
     88    if ((event == CHANNEL_EVENT_WRITE_CANCELLED
     89            || event == CHANNEL_EVENT_WRITE_COMPLETE) && data != NULL) {
     90        Stream_Free((wStream*) data, TRUE);
     91        return;
     92    }
     93#endif
     94
     95    /* Ignore all events except for received data */
     96    if (event != CHANNEL_EVENT_DATA_RECEIVED)
     97        return;
     98
     99    guac_rdp_common_svc* svc = (guac_rdp_common_svc*) user_param;
    100
    101    /* Validate relevant handle matches that of SVC */
    102    if (open_handle != svc->_open_handle) {
    103        guac_client_log(svc->client, GUAC_LOG_WARNING, "%i bytes of data "
    104                "received from within the remote desktop session for SVC "
    105                "\"%s\" are being dropped because the relevant open handle "
    106                "(0x%X) does not match the open handle of the SVC (0x%X).",
    107                data_length, svc->name, open_handle, svc->_open_handle);
    108        return;
    109    }
    110
    111    /* If receiving first chunk, allocate sufficient space for all remaining
    112     * chunks */
    113    if (data_flags & CHANNEL_FLAG_FIRST) {
    114
    115        /* Limit maximum received size */
    116        if (total_length > GUAC_SVC_MAX_ASSEMBLED_LENGTH) {
    117            guac_client_log(svc->client, GUAC_LOG_WARNING, "RDP server has "
    118                    "requested to send a sequence of %i bytes, but this "
    119                    "exceeds the maximum buffer space of %i bytes. Received "
    120                    "data may be truncated.", total_length,
    121                    GUAC_SVC_MAX_ASSEMBLED_LENGTH);
    122            total_length = GUAC_SVC_MAX_ASSEMBLED_LENGTH;
    123        }
    124
    125        svc->_input_stream = Stream_New(NULL, total_length);
    126    }
    127
    128    /* leave if we don't have a stream. */
    129    if (svc->_input_stream == NULL)
    130        return;
    131    
    132    /* Add chunk to buffer only if sufficient space remains */
    133    if (Stream_EnsureRemainingCapacity(svc->_input_stream, data_length))
    134        Stream_Write(svc->_input_stream, data, data_length);
    135    else
    136        guac_client_log(svc->client, GUAC_LOG_WARNING, "%i bytes of data "
    137                "received from within the remote desktop session for SVC "
    138                "\"%s\" are being dropped because the maximum available "
    139                "space for received data has been exceeded.", data_length,
    140                svc->name);
    141
    142    /* Fire event once last chunk has been received */
    143    if (data_flags & CHANNEL_FLAG_LAST) {
    144
    145        Stream_SealLength(svc->_input_stream);
    146        Stream_SetPosition(svc->_input_stream, 0);
    147
    148        /* Handle channel-specific data receipt tasks, if any */
    149        if (svc->_receive_handler)
    150            svc->_receive_handler(svc, svc->_input_stream);
    151
    152        Stream_Free(svc->_input_stream, TRUE);
    153        svc->_input_stream = NULL;
    154
    155    }
    156
    157}
    158
    159/**
    160 * Processes a CHANNEL_EVENT_CONNECTED event, completing the
    161 * connection/initialization process of the channel.
    162 *
    163 * @param rdpsnd
    164 *     The guac_rdp_common_svc structure representing the channel.
    165 */
    166static void guac_rdp_common_svc_process_connect(guac_rdp_common_svc* svc) {
    167
    168    /* Open FreeRDP side of connected channel */
    169    UINT32 open_status =
    170        svc->_entry_points.pVirtualChannelOpenEx(svc->_init_handle,
    171                &svc->_open_handle, svc->_channel_def.name,
    172                guac_rdp_common_svc_handle_open_event);
    173
    174    /* Warn if the channel cannot be opened after all */
    175    if (open_status != CHANNEL_RC_OK) {
    176        guac_client_log(svc->client, GUAC_LOG_WARNING, "SVC \"%s\" could not "
    177                "be opened: %s (error %i)", svc->name,
    178                WTSErrorToString(open_status), open_status);
    179        return;
    180    }
    181
    182    /* Handle channel-specific connect tasks, if any */
    183    if (svc->_connect_handler)
    184        svc->_connect_handler(svc);
    185
    186    /* Channel is now ready */
    187    guac_client_log(svc->client, GUAC_LOG_DEBUG, "SVC \"%s\" connected.",
    188            svc->name);
    189
    190}
    191
    192/**
    193 * Processes a CHANNEL_EVENT_TERMINATED event, freeing all resources associated
    194 * with the channel.
    195 *
    196 * @param svc
    197 *     The guac_rdp_common_svc structure representing the channel.
    198 */
    199static void guac_rdp_common_svc_process_terminate(guac_rdp_common_svc* svc) {
    200
    201    /* Handle channel-specific termination tasks, if any */
    202    if (svc->_terminate_handler)
    203        svc->_terminate_handler(svc);
    204
    205    guac_client_log(svc->client, GUAC_LOG_DEBUG, "SVC \"%s\" disconnected.",
    206            svc->name);
    207    guac_mem_free(svc);
    208
    209}
    210
    211/**
    212 * Event handler for events which deal with the overall lifecycle of an SVC.
    213 * This specific implementation of the event handler currently handles only
    214 * CHANNEL_EVENT_CONNECTED and CHANNEL_EVENT_TERMINATED events, delegating
    215 * actual handling of those events to guac_rdp_common_svc_process_connect() and
    216 * guac_rdp_common_svc_process_terminate() respectively.
    217 *
    218 * The FreeRDP requirements for this function follow those of the
    219 * VirtualChannelInitEventEx callback defined within Microsoft's RDP API:
    220 *
    221 * https://docs.microsoft.com/en-us/previous-versions/windows/embedded/aa514727%28v%3dmsdn.10%29
    222 *
    223 * @param user_param
    224 *     The pointer to arbitrary data originally passed via the first parameter
    225 *     of the pVirtualChannelInitEx() function call when the associated channel
    226 *     was initialized. The pVirtualChannelInitEx() function is exposed within
    227 *     the channel entry points structure.
    228 *
    229 * @param init_handle
    230 *     The handle which identifies the client connection, typically referred to
    231 *     within the FreeRDP source as pInitHandle.
    232 *
    233 * @param event
    234 *     An integer representing the event that should be handled. This will be
    235 *     either CHANNEL_EVENT_CONNECTED, CHANNEL_EVENT_DISCONNECTED,
    236 *     CHANNEL_EVENT_INITIALIZED, CHANNEL_EVENT_TERMINATED, or
    237 *     CHANNEL_EVENT_V1_CONNECTED.
    238 *
    239 * @param data
    240 *     NULL in all cases except the CHANNEL_EVENT_CONNECTED event, in which
    241 *     case this is a null-terminated string containing the name of the server.
    242 *
    243 * @param data_length
    244 *     The number of bytes of data, if any.
    245 */
    246static VOID guac_rdp_common_svc_handle_init_event(LPVOID user_param,
    247        LPVOID init_handle, UINT event, LPVOID data, UINT data_length) {
    248
    249    guac_rdp_common_svc* svc = (guac_rdp_common_svc*) user_param;
    250
    251    /* Validate relevant handle matches that of SVC */
    252    if (init_handle != svc->_init_handle) {
    253        guac_client_log(svc->client, GUAC_LOG_WARNING, "An init event (#%i) "
    254                "for SVC \"%s\" has been dropped because the relevant init "
    255                "handle (0x%X) does not match the init handle of the SVC "
    256                "(0x%X).", event, svc->name, init_handle, svc->_init_handle);
    257        return;
    258    }
    259
    260    switch (event) {
    261
    262        /* The remote desktop side of the SVC has been connected */
    263        case CHANNEL_EVENT_CONNECTED:
    264            guac_rdp_common_svc_process_connect(svc);
    265            break;
    266
    267        /* The channel has disconnected and now must be cleaned up */
    268        case CHANNEL_EVENT_TERMINATED:
    269            guac_rdp_common_svc_process_terminate(svc);
    270            break;
    271
    272    }
    273
    274}
    275
    276/**
    277 * Entry point for FreeRDP plugins. This function is automatically invoked when
    278 * the plugin is loaded.
    279 *
    280 * @param entry_points
    281 *     Functions and data specific to the FreeRDP side of the virtual channel
    282 *     and plugin. This structure must be copied within implementation-specific
    283 *     storage such that the functions it references can be invoked when
    284 *     needed.
    285 *
    286 * @param init_handle
    287 *     The handle which identifies the client connection, typically referred to
    288 *     within the FreeRDP source as pInitHandle. This handle is also provided
    289 *     to the channel init event handler. The handle must eventually be used
    290 *     within the channel open event handler to obtain a handle to the channel
    291 *     itself.
    292 *
    293 * @return
    294 *     TRUE if the plugin has initialized successfully, FALSE otherwise.
    295 */
    296BOOL VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS_EX entry_points,
    297        PVOID init_handle) {
    298
    299    CHANNEL_ENTRY_POINTS_FREERDP_EX* entry_points_ex =
    300        (CHANNEL_ENTRY_POINTS_FREERDP_EX*) entry_points;
    301
    302    /* Get structure representing the Guacamole side of the SVC from plugin
    303     * parameters */
    304    guac_rdp_common_svc* svc = (guac_rdp_common_svc*) entry_points_ex->pExtendedData;
    305
    306    /* Copy FreeRDP data into SVC structure for future reference */
    307    svc->_entry_points = *entry_points_ex;
    308    svc->_init_handle = init_handle;
    309
    310    /* Complete initialization */
    311    if (svc->_entry_points.pVirtualChannelInitEx(svc, NULL, init_handle,
    312                &svc->_channel_def, 1, VIRTUAL_CHANNEL_VERSION_WIN2000,
    313                guac_rdp_common_svc_handle_init_event) != CHANNEL_RC_OK) {
    314        return FALSE;
    315    }
    316
    317    return TRUE;
    318
    319}
    320