mike-jumper commented on a change in pull request #243: GUACAMOLE-249: Migrate to FreeRDP 2.x URL: https://github.com/apache/guacamole-server/pull/243#discussion_r364954598
########## File path: src/protocols/rdp/channels/rdpdr/rdpdr-messages.c ########## @@ -0,0 +1,349 @@ +/* + * 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/rdpdr/rdpdr-messages.h" +#include "channels/rdpdr/rdpdr.h" +#include "rdp.h" +#include "settings.h" + +#include <freerdp/channels/rdpdr.h> +#include <guacamole/client.h> +#include <winpr/stream.h> + +#include <stdlib.h> +#include <string.h> + +/** + * Sends a Client Announce Reply message. The Client Announce Reply message is + * required to be sent in response to the Server Announce Request message. See: + * + * https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpefs/d6fe6d1b-c145-4a6f-99aa-4fe3cdcea398 + * + * @param svc + * The guac_rdp_common_svc representing the static virtual channel being + * used for RDPDR. + * + * @param major + * The major version of the RDPDR protocol in use. This value must always + * be 1. + * + * @param minor + * The minor version of the RDPDR protocol in use. This value must be + * either 2, 5, 10, 12, or 13. + * + * @param client_id + * The client ID received in the Server Announce Request, or a randomly + * generated ID. + */ +static void guac_rdpdr_send_client_announce_reply(guac_rdp_common_svc* svc, + unsigned int major, unsigned int minor, unsigned int client_id) { + + wStream* output_stream = Stream_New(NULL, 12); + + /* Write header */ + Stream_Write_UINT16(output_stream, RDPDR_CTYP_CORE); + Stream_Write_UINT16(output_stream, PAKID_CORE_CLIENTID_CONFIRM); + + /* Write content */ + Stream_Write_UINT16(output_stream, major); + Stream_Write_UINT16(output_stream, minor); + Stream_Write_UINT32(output_stream, client_id); + + guac_rdp_common_svc_write(svc, output_stream); + +} + +/** + * Sends a Client Name Request message. The Client Name Request message is used + * by the client to announce its own name. See: + * + * https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpefs/902497f1-3b1c-4aee-95f8-1668f9b7b7d2 + * + * @param svc + * The guac_rdp_common_svc representing the static virtual channel being + * used for RDPDR. + * + * @param name + * The name that should be used for the client. + */ +static void guac_rdpdr_send_client_name_request(guac_rdp_common_svc* svc, + const char* name) { + + int name_bytes = strlen(name) + 1; + wStream* output_stream = Stream_New(NULL, 16 + name_bytes); + + /* Write header */ + Stream_Write_UINT16(output_stream, RDPDR_CTYP_CORE); + Stream_Write_UINT16(output_stream, PAKID_CORE_CLIENT_NAME); + + /* Write content */ + Stream_Write_UINT32(output_stream, 0); /* ASCII */ + Stream_Write_UINT32(output_stream, 0); /* 0 required by RDPDR spec */ + Stream_Write_UINT32(output_stream, name_bytes); + Stream_Write(output_stream, name, name_bytes); + + guac_rdp_common_svc_write(svc, output_stream); + +} + +/** + * Sends a Client Core Capability Response message. The Client Core Capability + * Response message is used to announce the client's capabilities, in response + * to receiving the server's capabilities via a Server Core Capability Request. + * See: + * + * https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpefs/f513bf87-cca0-488a-ac5c-18cf18f4a7e1 + * + * @param svc + * The guac_rdp_common_svc representing the static virtual channel being + * used for RDPDR. + */ +static void guac_rdpdr_send_client_capability(guac_rdp_common_svc* svc) { + + wStream* output_stream = Stream_New(NULL, 256); + guac_client_log(svc->client, GUAC_LOG_DEBUG, "Sending capabilities..."); + + /* Write header */ + Stream_Write_UINT16(output_stream, RDPDR_CTYP_CORE); + Stream_Write_UINT16(output_stream, PAKID_CORE_CLIENT_CAPABILITY); + + /* Capability count + padding */ + Stream_Write_UINT16(output_stream, 3); + Stream_Write_UINT16(output_stream, 0); /* Padding */ + + /* General capability header */ + Stream_Write_UINT16(output_stream, CAP_GENERAL_TYPE); + Stream_Write_UINT16(output_stream, 44); + Stream_Write_UINT32(output_stream, GENERAL_CAPABILITY_VERSION_02); + + /* General capability data */ + Stream_Write_UINT32(output_stream, GUAC_OS_TYPE); /* osType - required to be ignored */ + Stream_Write_UINT32(output_stream, 0); /* osVersion */ + Stream_Write_UINT16(output_stream, 1); /* protocolMajor - must be set to 1 */ + Stream_Write_UINT16(output_stream, RDPDR_MINOR_RDP_VERSION_5_2); /* protocolMinor */ + Stream_Write_UINT32(output_stream, 0xFFFF); /* ioCode1 */ + Stream_Write_UINT32(output_stream, 0); /* ioCode2 */ + Stream_Write_UINT32(output_stream, + RDPDR_DEVICE_REMOVE_PDUS + | RDPDR_CLIENT_DISPLAY_NAME_PDU + | RDPDR_USER_LOGGEDON_PDU); /* extendedPDU */ + Stream_Write_UINT32(output_stream, 0); /* extraFlags1 */ + Stream_Write_UINT32(output_stream, 0); /* extraFlags2 */ + Stream_Write_UINT32(output_stream, 0); /* SpecialTypeDeviceCap */ + + /* Printer support header */ + Stream_Write_UINT16(output_stream, CAP_PRINTER_TYPE); + Stream_Write_UINT16(output_stream, 8); + Stream_Write_UINT32(output_stream, PRINT_CAPABILITY_VERSION_01); + + /* Drive support header */ + Stream_Write_UINT16(output_stream, CAP_DRIVE_TYPE); + Stream_Write_UINT16(output_stream, 8); + Stream_Write_UINT32(output_stream, DRIVE_CAPABILITY_VERSION_02); + + guac_rdp_common_svc_write(svc, output_stream); + guac_client_log(svc->client, GUAC_LOG_DEBUG, "Capabilities sent."); + +} + +/** + * Sends a Client Device List Announce Request message. The Client Device List + * Announce Request message is used by the client to enumerate all devices + * which should be made available within the RDP session via RDPDR. See: + * + * https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpefs/10ef9ada-cba2-4384-ab60-7b6290ed4a9a + * + * @param svc + * The guac_rdp_common_svc representing the static virtual channel being + * used for RDPDR. + */ +static void guac_rdpdr_send_client_device_list_announce_request(guac_rdp_common_svc* svc) { + + guac_rdpdr* rdpdr = (guac_rdpdr*) svc->data; + + /* Calculate number of bytes needed for the stream */ + int streamBytes = 16; + for (int i=0; i < rdpdr->devices_registered; i++) + streamBytes += rdpdr->devices[i].device_announce_len; + + /* Allocate the stream */ + wStream* output_stream = Stream_New(NULL, streamBytes); + + /* Write header */ + Stream_Write_UINT16(output_stream, RDPDR_CTYP_CORE); + Stream_Write_UINT16(output_stream, PAKID_CORE_DEVICELIST_ANNOUNCE); + + /* Get the stream for each of the devices. */ + Stream_Write_UINT32(output_stream, rdpdr->devices_registered); + for (int i=0; i<rdpdr->devices_registered; i++) { + + Stream_Write(output_stream, + Stream_Buffer(rdpdr->devices[i].device_announce), + rdpdr->devices[i].device_announce_len); + + guac_client_log(svc->client, GUAC_LOG_DEBUG, "Registered device %i (%s)", + rdpdr->devices[i].device_id, rdpdr->devices[i].device_name); + + } + + guac_rdp_common_svc_write(svc, output_stream); + guac_client_log(svc->client, GUAC_LOG_DEBUG, "All supported devices sent."); + +} + +void guac_rdpdr_process_server_announce(guac_rdp_common_svc* svc, + wStream* input_stream) { + + unsigned int major, minor, client_id; + + Stream_Read_UINT16(input_stream, major); + Stream_Read_UINT16(input_stream, minor); + Stream_Read_UINT32(input_stream, client_id); + + /* Must choose own client ID if minor not >= 12 */ + if (minor < 12) + client_id = random() & 0xFFFF; + + guac_client_log(svc->client, GUAC_LOG_INFO, "Connected to RDPDR %u.%u as client 0x%04x", major, minor, client_id); + + /* Respond to announce */ + guac_rdpdr_send_client_announce_reply(svc, major, minor, client_id); + + /* Name request */ + guac_rdpdr_send_client_name_request(svc, ((guac_rdp_client*) svc->client->data)->settings->client_name); + +} + +void guac_rdpdr_process_clientid_confirm(guac_rdp_common_svc* svc, + wStream* input_stream) { + guac_client_log(svc->client, GUAC_LOG_DEBUG, "Client ID confirmed"); +} + +void guac_rdpdr_process_device_reply(guac_rdp_common_svc* svc, + wStream* input_stream) { + + guac_rdpdr* rdpdr = (guac_rdpdr*) svc->data; + + unsigned int device_id, ntstatus; + int severity, c, n, facility, code; Review comment: Weirdly enough, they are standard names. The standard is not set by FreeRDP but by Microsoft. Each of these variables refers to a portion of a 32-bit "NTSTATUS" code: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/87fba13e-bf06-450e-83b1-9241dc81e781 As I'm calling the "sev" field "severity" here, it might make sense to change `c` to `customer`, however I'm not sure anyone knows what `n` is: > **N (1 bit)**: Reserved. MUST be set to 0 so that it is possible to map an NTSTATUS value to an equivalent HRESULT value, as specified in section 2.1, by setting this bit. ---------------------------------------------------------------- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. For queries about this service, please contact Infrastructure at: [email protected] With regards, Apache Git Services
