rail.c (10012B)
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/rail.h" 21#include "plugins/channels.h" 22#include "rdp.h" 23#include "settings.h" 24 25#include <freerdp/client/rail.h> 26#include <freerdp/event.h> 27#include <freerdp/freerdp.h> 28#include <freerdp/rail.h> 29#include <guacamole/client.h> 30#include <winpr/wtypes.h> 31#include <winpr/wtsapi.h> 32 33#include <stddef.h> 34#include <string.h> 35 36#ifdef FREERDP_RAIL_CALLBACKS_REQUIRE_CONST 37/** 38 * FreeRDP 2.0.0-rc4 and newer requires the final argument for all RAIL 39 * callbacks to be const. 40 */ 41#define RAIL_CONST const 42#else 43/** 44 * FreeRDP 2.0.0-rc3 and older requires the final argument for all RAIL 45 * callbacks to NOT be const. 46 */ 47#define RAIL_CONST 48#endif 49 50/** 51 * Completes initialization of the RemoteApp session, responding to the server 52 * handshake, sending client status and system parameters, and executing the 53 * desired RemoteApp command. This is accomplished using the Handshake PDU, 54 * Client Information PDU, one or more Client System Parameters Update PDUs, 55 * and the Client Execute PDU respectively. These PDUs MUST be sent for the 56 * desired RemoteApp to run, and MUST NOT be sent until after a Handshake or 57 * HandshakeEx PDU has been received. See: 58 * 59 * https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdperp/cec4eb83-b304-43c9-8378-b5b8f5e7082a (Handshake PDU) 60 * https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdperp/743e782d-f59b-40b5-a0f3-adc74e68a2ff (Client Information PDU) 61 * https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdperp/60344497-883f-4711-8b9a-828d1c580195 (System Parameters Update PDU) 62 * https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdperp/98a6e3c3-c2a9-42cc-ad91-0d9a6c211138 (Client Execute PDU) 63 * 64 * @param rail 65 * The RailClientContext structure used by FreeRDP to handle the RAIL 66 * channel for the current RDP session. 67 * 68 * @return 69 * CHANNEL_RC_OK (zero) if the PDUs were sent successfully, an error code 70 * (non-zero) otherwise. 71 */ 72static UINT guac_rdp_rail_complete_handshake(RailClientContext* rail) { 73 74 UINT status; 75 76 guac_client* client = (guac_client*) rail->custom; 77 guac_rdp_client* rdp_client = (guac_rdp_client*) client->data; 78 79 RAIL_HANDSHAKE_ORDER handshake = { 80 81 /* Build number 7600 (0x1DB0) apparently represents Windows 7 and 82 * compatibility with RDP 7.0. As of this writing, this is the same 83 * build number sent for RAIL connections by xfreerdp. */ 84 .buildNumber = 7600 85 86 }; 87 88 /* Send client handshake response */ 89 pthread_mutex_lock(&(rdp_client->message_lock)); 90 status = rail->ClientHandshake(rail, &handshake); 91 pthread_mutex_unlock(&(rdp_client->message_lock)); 92 93 if (status != CHANNEL_RC_OK) 94 return status; 95 96 RAIL_CLIENT_STATUS_ORDER client_status = { 97 .flags = 0x00 98 }; 99 100 /* Send client status */ 101 pthread_mutex_lock(&(rdp_client->message_lock)); 102 status = rail->ClientInformation(rail, &client_status); 103 pthread_mutex_unlock(&(rdp_client->message_lock)); 104 105 if (status != CHANNEL_RC_OK) 106 return status; 107 108 RAIL_SYSPARAM_ORDER sysparam = { 109 110 .dragFullWindows = FALSE, 111 112 .highContrast = { 113 .flags = 114 HCF_AVAILABLE 115 | HCF_CONFIRMHOTKEY 116 | HCF_HOTKEYACTIVE 117 | HCF_HOTKEYAVAILABLE 118 | HCF_HOTKEYSOUND 119 | HCF_INDICATOR, 120 .colorScheme = { 121 .string = NULL, 122 .length = 0 123 } 124 }, 125 126 .keyboardCues = FALSE, 127 .keyboardPref = FALSE, 128 .mouseButtonSwap = FALSE, 129 130 .workArea = { 131 .left = 0, 132 .top = 0, 133 .right = rdp_client->settings->width, 134 .bottom = rdp_client->settings->height 135 }, 136 137 .params = 138 SPI_MASK_SET_DRAG_FULL_WINDOWS 139 | SPI_MASK_SET_HIGH_CONTRAST 140 | SPI_MASK_SET_KEYBOARD_CUES 141 | SPI_MASK_SET_KEYBOARD_PREF 142 | SPI_MASK_SET_MOUSE_BUTTON_SWAP 143 | SPI_MASK_SET_WORK_AREA 144 145 }; 146 147 /* Send client system parameters */ 148 pthread_mutex_lock(&(rdp_client->message_lock)); 149 status = rail->ClientSystemParam(rail, &sysparam); 150 pthread_mutex_unlock(&(rdp_client->message_lock)); 151 152 if (status != CHANNEL_RC_OK) 153 return status; 154 155 RAIL_EXEC_ORDER exec = { 156 .flags = RAIL_EXEC_FLAG_EXPAND_ARGUMENTS, 157 .RemoteApplicationProgram = rdp_client->settings->remote_app, 158 .RemoteApplicationWorkingDir = rdp_client->settings->remote_app_dir, 159 .RemoteApplicationArguments = rdp_client->settings->remote_app_args, 160 }; 161 162 /* Execute desired RemoteApp command */ 163 pthread_mutex_lock(&(rdp_client->message_lock)); 164 status = rail->ClientExecute(rail, &exec); 165 pthread_mutex_unlock(&(rdp_client->message_lock)); 166 167 return status; 168 169} 170 171/** 172 * Callback which is invoked when a Handshake PDU is received from the RDP 173 * server. No communication for RemoteApp may occur until the Handshake PDU 174 * (or, alternatively, the HandshakeEx PDU) is received. See: 175 * 176 * https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdperp/cec4eb83-b304-43c9-8378-b5b8f5e7082a 177 * 178 * @param rail 179 * The RailClientContext structure used by FreeRDP to handle the RAIL 180 * channel for the current RDP session. 181 * 182 * @param handshake 183 * The RAIL_HANDSHAKE_ORDER structure representing the Handshake PDU that 184 * was received. 185 * 186 * @return 187 * CHANNEL_RC_OK (zero) if the PDU was handled successfully, an error code 188 * (non-zero) otherwise. 189 */ 190static UINT guac_rdp_rail_handshake(RailClientContext* rail, 191 RAIL_CONST RAIL_HANDSHAKE_ORDER* handshake) { 192 return guac_rdp_rail_complete_handshake(rail); 193} 194 195/** 196 * Callback which is invoked when a HandshakeEx PDU is received from the RDP 197 * server. No communication for RemoteApp may occur until the HandshakeEx PDU 198 * (or, alternatively, the Handshake PDU) is received. See: 199 * 200 * https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdperp/5cec5414-27de-442e-8d4a-c8f8b41f3899 201 * 202 * @param rail 203 * The RailClientContext structure used by FreeRDP to handle the RAIL 204 * channel for the current RDP session. 205 * 206 * @param handshake_ex 207 * The RAIL_HANDSHAKE_EX_ORDER structure representing the HandshakeEx PDU 208 * that was received. 209 * 210 * @return 211 * CHANNEL_RC_OK (zero) if the PDU was handled successfully, an error code 212 * (non-zero) otherwise. 213 */ 214static UINT guac_rdp_rail_handshake_ex(RailClientContext* rail, 215 RAIL_CONST RAIL_HANDSHAKE_EX_ORDER* handshake_ex) { 216 return guac_rdp_rail_complete_handshake(rail); 217} 218 219/** 220 * Callback which associates handlers specific to Guacamole with the 221 * RailClientContext instance allocated by FreeRDP to deal with received 222 * RAIL (RemoteApp) messages. 223 * 224 * This function is called whenever a channel connects via the PubSub event 225 * system within FreeRDP, but only has any effect if the connected channel is 226 * the RAIL channel. This specific callback is registered with the PubSub 227 * system of the relevant rdpContext when guac_rdp_rail_load_plugin() is 228 * called. 229 * 230 * @param context 231 * The rdpContext associated with the active RDP session. 232 * 233 * @param e 234 * Event-specific arguments, mainly the name of the channel, and a 235 * reference to the associated plugin loaded for that channel by FreeRDP. 236 */ 237static void guac_rdp_rail_channel_connected(rdpContext* context, 238 ChannelConnectedEventArgs* e) { 239 240 guac_client* client = ((rdp_freerdp_context*) context)->client; 241 242 /* Ignore connection event if it's not for the RAIL channel */ 243 if (strcmp(e->name, RAIL_SVC_CHANNEL_NAME) != 0) 244 return; 245 246 /* The structure pointed to by pInterface is guaranteed to be a 247 * RailClientContext if the channel is RAIL */ 248 RailClientContext* rail = (RailClientContext*) e->pInterface; 249 250 /* Init FreeRDP RAIL context, ensuring the guac_client can be accessed from 251 * within any RAIL-specific callbacks */ 252 rail->custom = client; 253 rail->ServerHandshake = guac_rdp_rail_handshake; 254 rail->ServerHandshakeEx = guac_rdp_rail_handshake_ex; 255 256 guac_client_log(client, GUAC_LOG_DEBUG, "RAIL (RemoteApp) channel " 257 "connected."); 258 259} 260 261void guac_rdp_rail_load_plugin(rdpContext* context) { 262 263 guac_client* client = ((rdp_freerdp_context*) context)->client; 264 265 /* Attempt to load FreeRDP support for the RAIL channel */ 266 if (guac_freerdp_channels_load_plugin(context, "rail", context->settings)) { 267 guac_client_log(client, GUAC_LOG_WARNING, 268 "Support for the RAIL channel (RemoteApp) could not be " 269 "loaded. This support normally takes the form of a plugin " 270 "which is built into FreeRDP. Lacking this support, " 271 "RemoteApp will not work."); 272 return; 273 } 274 275 /* Complete RDP side of initialization when channel is connected */ 276 PubSub_SubscribeChannelConnected(context->pubSub, 277 (pChannelConnectedEventHandler) guac_rdp_rail_channel_connected); 278 279 guac_client_log(client, GUAC_LOG_DEBUG, "Support for RAIL (RemoteApp) " 280 "registered. Awaiting channel connection."); 281 282} 283