GUACAMOLE-249: Dynamically wrap channel entry points (FreeRDP will refuse to associate the same entry point with multiple channels).

This commit is contained in:
Michael Jumper 2019-12-22 20:14:44 -08:00
parent 664586ea54
commit 875d51c1ed
5 changed files with 236 additions and 9 deletions

View File

@ -4,5 +4,6 @@ _generated_runner.c
test_rdp test_rdp
# Autogenerated sources # Autogenerated sources
_generated_channel_entry_wrappers.c
_generated_keymaps.c _generated_keymaps.c

View File

@ -33,7 +33,8 @@ SUBDIRS = . tests
# Main RDP client library # Main RDP client library
# #
nodist_libguac_client_rdp_la_SOURCES = \ nodist_libguac_client_rdp_la_SOURCES = \
_generated_channel_entry_wrappers.c \
_generated_keymaps.c _generated_keymaps.c
libguac_client_rdp_la_SOURCES = \ libguac_client_rdp_la_SOURCES = \
@ -200,11 +201,16 @@ libguac_client_rdp_la_LIBADD += @COMMON_SSH_LTLIB@
endif endif
# #
# Autogenerate keymaps # Autogenerated keymaps and channel wrapper functions
# #
CLEANFILES = _generated_keymaps.c CLEANFILES = \
BUILT_SOURCES = _generated_keymaps.c _generated_channel_entry_wrappers.c \
_generated_keymaps.c
BUILT_SOURCES = \
_generated_channel_entry_wrappers.c \
_generated_keymaps.c
rdp_keymaps = \ rdp_keymaps = \
$(srcdir)/keymaps/base.keymap \ $(srcdir)/keymaps/base.keymap \
@ -224,9 +230,13 @@ rdp_keymaps = \
$(srcdir)/keymaps/tr_tr_qwerty.keymap $(srcdir)/keymaps/tr_tr_qwerty.keymap
_generated_keymaps.c: $(rdp_keymaps) _generated_keymaps.c: $(rdp_keymaps)
$(srcdir)/keymaps/generate.pl $(rdp_keymaps) $(AM_V_GEN) $(srcdir)/keymaps/generate.pl $(rdp_keymaps)
EXTRA_DIST = \ _generated_channel_entry_wrappers.c: $(srcdir)/channels.h $(srcdir)/generate-entry-wrappers.pl
$(rdp_keymaps) \ $(AM_V_GEN) $(srcdir)/generate-entry-wrappers.pl $(srcdir)/channels.h
EXTRA_DIST = \
$(rdp_keymaps) \
generate-entry-wrappers.pl \
keymaps/generate.pl keymaps/generate.pl

View File

@ -18,12 +18,53 @@
*/ */
#include "config.h" #include "config.h"
#include "channels.h"
#include "rdp.h" #include "rdp.h"
#include <freerdp/channels/channels.h> #include <freerdp/channels/channels.h>
#include <freerdp/freerdp.h> #include <freerdp/freerdp.h>
#include <guacamole/client.h> #include <guacamole/client.h>
int guac_rdp_wrapped_entry_ex_count = 0;
int guac_rdp_wrapped_entry_count = 0;
PVIRTUALCHANNELENTRYEX guac_rdp_wrapped_entry_ex[GUAC_RDP_MAX_CHANNELS] = { NULL };
PVIRTUALCHANNELENTRY guac_rdp_wrapped_entry[GUAC_RDP_MAX_CHANNELS] = { NULL };
PVIRTUALCHANNELENTRYEX guac_rdp_plugin_wrap_entry_ex(PVIRTUALCHANNELENTRYEX entry_ex) {
/* Do not wrap if there is insufficient space to store the wrapped
* function */
if (guac_rdp_wrapped_entry_ex_count == GUAC_RDP_MAX_CHANNELS)
return entry_ex;
/* Generate wrapped version of provided entry point */
PVIRTUALCHANNELENTRYEX wrapper = guac_rdp_entry_ex_wrappers[guac_rdp_wrapped_entry_ex_count];
guac_rdp_wrapped_entry_ex[guac_rdp_wrapped_entry_ex_count] = entry_ex;
guac_rdp_wrapped_entry_ex_count++;
return wrapper;
}
PVIRTUALCHANNELENTRY guac_rdp_plugin_wrap_entry(PVIRTUALCHANNELENTRY entry) {
/* Do not wrap if there is insufficient space to store the wrapped
* function */
if (guac_rdp_wrapped_entry_count == GUAC_RDP_MAX_CHANNELS)
return entry;
/* Generate wrapped version of provided entry point */
PVIRTUALCHANNELENTRY wrapper = guac_rdp_entry_wrappers[guac_rdp_wrapped_entry_count];
guac_rdp_wrapped_entry[guac_rdp_wrapped_entry_count] = entry;
guac_rdp_wrapped_entry_count++;
return wrapper;
}
int guac_freerdp_channels_load_plugin(rdpChannels* channels, int guac_freerdp_channels_load_plugin(rdpChannels* channels,
rdpSettings* settings, const char* name, void* data) { rdpSettings* settings, const char* name, void* data) {
@ -31,15 +72,19 @@ int guac_freerdp_channels_load_plugin(rdpChannels* channels,
PVIRTUALCHANNELENTRYEX entry_ex = (PVIRTUALCHANNELENTRYEX) (void*) freerdp_load_channel_addin_entry(name, PVIRTUALCHANNELENTRYEX entry_ex = (PVIRTUALCHANNELENTRYEX) (void*) freerdp_load_channel_addin_entry(name,
NULL, NULL, FREERDP_ADDIN_CHANNEL_STATIC | FREERDP_ADDIN_CHANNEL_ENTRYEX); NULL, NULL, FREERDP_ADDIN_CHANNEL_STATIC | FREERDP_ADDIN_CHANNEL_ENTRYEX);
if (entry_ex != NULL) if (entry_ex != NULL) {
entry_ex = guac_rdp_plugin_wrap_entry_ex(entry_ex);
return freerdp_channels_client_load_ex(channels, settings, entry_ex, data); return freerdp_channels_client_load_ex(channels, settings, entry_ex, data);
}
/* Lacking the "ex" entry point, attempt to load using the non-ex version */ /* Lacking the "ex" entry point, attempt to load using the non-ex version */
PVIRTUALCHANNELENTRY entry = freerdp_load_channel_addin_entry(name, PVIRTUALCHANNELENTRY entry = freerdp_load_channel_addin_entry(name,
NULL, NULL, FREERDP_ADDIN_CHANNEL_STATIC); NULL, NULL, FREERDP_ADDIN_CHANNEL_STATIC);
if (entry != NULL) if (entry != NULL) {
entry = guac_rdp_plugin_wrap_entry(entry);
return freerdp_channels_client_load(channels, settings, entry, data); return freerdp_channels_client_load(channels, settings, entry, data);
}
/* The plugin does not exist / cannot be loaded */ /* The plugin does not exist / cannot be loaded */
return 1; return 1;

View File

@ -25,6 +25,22 @@
#include <freerdp/channels/channels.h> #include <freerdp/channels/channels.h>
#include <freerdp/freerdp.h> #include <freerdp/freerdp.h>
/**
* The maximum number of static channels supported by Guacamole's RDP support.
* This value should be given a value which is at least the value of FreeRDP's
* CHANNEL_MAX_COUNT.
*
* NOTE: The value of this macro must be specified statically (not as a
* reference to CHANNEL_MAX_COUNT), as its value is extracted and used by the
* entry point wrapper code generator (generate-entry-wrappers.pl).
*/
#define GUAC_RDP_MAX_CHANNELS 64
/* Validate GUAC_RDP_MAX_CHANNELS is sane at compile time */
#if GUAC_RDP_MAX_CHANNELS < CHANNEL_MAX_COUNT
#error "GUAC_RDP_MAX_CHANNELS must not be less than CHANNEL_MAX_COUNT"
#endif
/** /**
* Loads the FreeRDP plugin having the given name. This function is a drop-in * Loads the FreeRDP plugin having the given name. This function is a drop-in
* replacement for freerdp_channels_load_plugin() which additionally loads * replacement for freerdp_channels_load_plugin() which additionally loads
@ -105,5 +121,83 @@ int guac_freerdp_channels_load_plugin(rdpChannels* channels,
void guac_freerdp_dynamic_channel_collection_add(rdpSettings* settings, void guac_freerdp_dynamic_channel_collection_add(rdpSettings* settings,
const char* name, ...); const char* name, ...);
/**
* The number of wrapped channel entry points currently stored within
* guac_rdp_wrapped_entry_ex.
*/
extern int guac_rdp_wrapped_entry_ex_count;
/**
* All currently wrapped entry points that use the PVIRTUALCHANNELENTRYEX
* variant.
*/
extern PVIRTUALCHANNELENTRYEX guac_rdp_wrapped_entry_ex[GUAC_RDP_MAX_CHANNELS];
/**
* Lookup table of wrapper functions for PVIRTUALCHANNELENTRYEX entry points.
* Each function within this array is generated at compile time by the entry
* point wrapper code generator (generate-entry-wrappers.pl) and automatically
* invokes the corresponding wrapped entry point stored within
* guac_rdp_wrapped_entry_ex.
*/
extern PVIRTUALCHANNELENTRYEX guac_rdp_entry_ex_wrappers[GUAC_RDP_MAX_CHANNELS];
/**
* Wraps the provided entry point function, returning a different entry point
* which simply invokes the original. As long as this function is not invoked
* more than GUAC_RDP_MAX_CHANNELS times, each returned entry point will be
* unique, even if the provided entry point is not. As FreeRDP will refuse to
* load a plugin if its entry point is already loaded, this allows a single
* FreeRDP plugin to be loaded multiple times.
*
* @param entry_ex
* The entry point function to wrap.
*
* @return
* A wrapped version of the provided entry point, or the unwrapped entry
* point if there is insufficient space remaining within
* guac_rdp_entry_ex_wrappers to wrap the entry point.
*/
PVIRTUALCHANNELENTRYEX guac_rdp_plugin_wrap_entry_ex(PVIRTUALCHANNELENTRYEX entry_ex);
/**
* The number of wrapped channel entry points currently stored within
* guac_rdp_wrapped_entry.
*/
extern int guac_rdp_wrapped_entry_count;
/**
* All currently wrapped entry points that use the PVIRTUALCHANNELENTRY
* variant.
*/
extern PVIRTUALCHANNELENTRY guac_rdp_wrapped_entry[GUAC_RDP_MAX_CHANNELS];
/**
* Lookup table of wrapper functions for PVIRTUALCHANNELENTRY entry points.
* Each function within this array is generated at compile time by the entry
* point wrapper code generator (generate-entry-wrappers.pl) and automatically
* invokes the corresponding wrapped entry point stored within
* guac_rdp_wrapped_entry.
*/
extern PVIRTUALCHANNELENTRY guac_rdp_entry_wrappers[GUAC_RDP_MAX_CHANNELS];
/**
* Wraps the provided entry point function, returning a different entry point
* which simply invokes the original. As long as this function is not invoked
* more than GUAC_RDP_MAX_CHANNELS times, each returned entry point will be
* unique, even if the provided entry point is not. As FreeRDP will refuse to
* load a plugin if its entry point is already loaded, this allows a single
* FreeRDP plugin to be loaded multiple times.
*
* @param entry
* The entry point function to wrap.
*
* @return
* A wrapped version of the provided entry point, or the unwrapped entry
* point if there is insufficient space remaining within
* guac_rdp_entry_wrappers to wrap the entry point.
*/
PVIRTUALCHANNELENTRY guac_rdp_plugin_wrap_entry(PVIRTUALCHANNELENTRY entry);
#endif #endif

View File

@ -0,0 +1,77 @@
#!/usr/bin/env perl
#
# 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.
#
#
# generate-entry-wrappers.pl
#
# Generates C source which defines wrapper functions for FreeRDP plugin entry
# points, allowing multiple instances of the same plugin to be loaded despite
# otherwise always having the same entry point.
#
# The resulting source is stored within "_generated_channel_entry_wrappers.c".
#
use strict;
##
## The maximum number of static channels supported by Guacamole's RDP support.
##
my $GUAC_RDP_MAX_CHANNELS;
# Extract value of GUAC_RDP_MAX_CHANNELS macro from provided source
while (<>) {
if ((my $value) = m/^\s*#define\s+GUAC_RDP_MAX_CHANNELS\s+(\d+)\s*$/) {
$GUAC_RDP_MAX_CHANNELS = $value;
}
}
open OUTPUT, ">", "_generated_channel_entry_wrappers.c";
# Generate required headers
print OUTPUT <<"EOF";
#include "channels.h"
#include <freerdp/channels/channels.h>
#include <freerdp/freerdp.h>
EOF
# Generate wrapper definitions for PVIRTUALCHANNELENTRYEX entry point variant
print OUTPUT <<"EOF" for (1..$GUAC_RDP_MAX_CHANNELS);
static BOOL guac_rdp_plugin_entry_ex_wrapper$_(PCHANNEL_ENTRY_POINTS_EX entry_points_ex, PVOID init_handle) {
return guac_rdp_wrapped_entry_ex[$_ - 1](entry_points_ex, init_handle);
}
EOF
# Generate wrapper definitions for PVIRTUALCHANNELENTRY entry point variant
print OUTPUT <<"EOF" for (1..$GUAC_RDP_MAX_CHANNELS);
static BOOL guac_rdp_plugin_entry_wrapper$_(PCHANNEL_ENTRY_POINTS entry_points) {
return guac_rdp_wrapped_entry[$_ - 1](entry_points);
}
EOF
# Populate lookup table of PVIRTUALCHANNELENTRYEX wrapper functions
print OUTPUT "PVIRTUALCHANNELENTRYEX guac_rdp_entry_ex_wrappers[$GUAC_RDP_MAX_CHANNELS] = {\n";
print OUTPUT " guac_rdp_plugin_entry_ex_wrapper$_,\n" for (1..$GUAC_RDP_MAX_CHANNELS);
print OUTPUT "};\n";
# Populate lookup table of PVIRTUALCHANNELENTRY wrapper functions
print OUTPUT "PVIRTUALCHANNELENTRY guac_rdp_entry_wrappers[$GUAC_RDP_MAX_CHANNELS] = {\n";
print OUTPUT " guac_rdp_plugin_entry_wrapper$_,\n" for (1..$GUAC_RDP_MAX_CHANNELS);
print OUTPUT "};\n";