diff --git a/src/protocols/rdp/guac_rdpdr/rdpdr_fs_service.c b/src/protocols/rdp/guac_rdpdr/rdpdr_fs_service.c index fb82996f..85787db2 100644 --- a/src/protocols/rdp/guac_rdpdr/rdpdr_fs_service.c +++ b/src/protocols/rdp/guac_rdpdr/rdpdr_fs_service.c @@ -29,6 +29,7 @@ #include #include #include +#include #ifdef ENABLE_WINPR #include @@ -36,21 +37,6 @@ #include "compat/winpr-stream.h" #endif -static void guac_rdpdr_device_fs_announce_handler(guac_rdpdr_device* device, - wStream* output_stream, int device_id) { - - /* Filesystem header */ - guac_client_log(device->rdpdr->client, GUAC_LOG_INFO, "Sending filesystem"); - Stream_Write_UINT32(output_stream, RDPDR_DTYP_FILESYSTEM); - Stream_Write_UINT32(output_stream, device_id); - Stream_Write(output_stream, "GUAC\0\0\0\0", 8); /* DOS name */ - - /* Filesystem data */ - Stream_Write_UINT32(output_stream, GUAC_FILESYSTEM_NAME_LENGTH); - Stream_Write(output_stream, GUAC_FILESYSTEM_NAME, GUAC_FILESYSTEM_NAME_LENGTH); - -} - static void guac_rdpdr_device_fs_iorequest_handler(guac_rdpdr_device* device, wStream* input_stream, int file_id, int completion_id, int major_func, int minor_func) { @@ -128,6 +114,9 @@ static void guac_rdpdr_device_fs_iorequest_handler(guac_rdpdr_device* device, } static void guac_rdpdr_device_fs_free_handler(guac_rdpdr_device* device) { + + Stream_Free(device->device_announce, 1); + } void guac_rdpdr_register_fs(guac_rdpdrPlugin* rdpdr) { @@ -143,9 +132,21 @@ void guac_rdpdr_register_fs(guac_rdpdrPlugin* rdpdr) { device->rdpdr = rdpdr; device->device_id = id; device->device_name = "Guacamole Filesystem"; + int device_name_len = guac_utf8_strlen(device->device_name); + device->device_type = RDPDR_DTYP_FILESYSTEM; + device->dos_name = "GUACFS\0\0"; + + /* Set up the device announcement */ + device->device_announce_len = 20 + device_name_len; + device->device_announce = Stream_New(NULL, device->device_announce_len); + Stream_Write_UINT32(device->device_announce, device->device_type); + Stream_Write_UINT32(device->device_announce, device->device_id); + Stream_Write(device->device_announce, device->dos_name, 8); + Stream_Write_UINT32(device->device_announce, device_name_len); + Stream_Write(device->device_announce, device->device_name, device_name_len); + /* Set handlers */ - device->announce_handler = guac_rdpdr_device_fs_announce_handler; device->iorequest_handler = guac_rdpdr_device_fs_iorequest_handler; device->free_handler = guac_rdpdr_device_fs_free_handler; diff --git a/src/protocols/rdp/guac_rdpdr/rdpdr_messages.c b/src/protocols/rdp/guac_rdpdr/rdpdr_messages.c index fc9cd0f8..66626adb 100644 --- a/src/protocols/rdp/guac_rdpdr/rdpdr_messages.c +++ b/src/protocols/rdp/guac_rdpdr/rdpdr_messages.c @@ -22,9 +22,11 @@ #include "rdp.h" #include "rdpdr_messages.h" #include "rdpdr_service.h" +#include "unicode.h" #include #include +#include #ifdef ENABLE_WINPR #include @@ -122,20 +124,29 @@ static void guac_rdpdr_send_client_capability(guac_rdpdrPlugin* rdpdr) { static void guac_rdpdr_send_client_device_list_announce_request(guac_rdpdrPlugin* rdpdr) { - int i; - wStream* output_stream = Stream_New(NULL, 256); + /* 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); - /* List devices */ + /* Get the stream for each of the devices. */ Stream_Write_UINT32(output_stream, rdpdr->devices_registered); - for (i=0; idevices_registered; i++) { - guac_rdpdr_device* device = &(rdpdr->devices[i]); - device->announce_handler(device, output_stream, i); + for (int i=0; idevices_registered; i++) { + + Stream_Write(output_stream, + Stream_Buffer(rdpdr->devices[i].device_announce), + rdpdr->devices[i].device_announce_len); + guac_client_log(rdpdr->client, GUAC_LOG_INFO, "Registered device %i (%s)", - device->device_id, device->device_name); + rdpdr->devices[i].device_id, rdpdr->devices[i].device_name); + } svc_plugin_send((rdpSvcPlugin*) rdpdr, output_stream); @@ -273,4 +284,3 @@ void guac_rdpdr_process_prn_cache_data(guac_rdpdrPlugin* rdpdr, wStream* input_s void guac_rdpdr_process_prn_using_xps(guac_rdpdrPlugin* rdpdr, wStream* input_stream) { guac_client_log(rdpdr->client, GUAC_LOG_INFO, "Printer unexpectedly switched to XPS mode"); } - diff --git a/src/protocols/rdp/guac_rdpdr/rdpdr_messages.h b/src/protocols/rdp/guac_rdpdr/rdpdr_messages.h index 24a25938..8f276524 100644 --- a/src/protocols/rdp/guac_rdpdr/rdpdr_messages.h +++ b/src/protocols/rdp/guac_rdpdr/rdpdr_messages.h @@ -76,12 +76,6 @@ #define GUAC_PRINTER_DRIVER "M\0S\0 \0P\0u\0b\0l\0i\0s\0h\0e\0r\0 \0I\0m\0a\0g\0e\0s\0e\0t\0t\0e\0r\0\0\0" #define GUAC_PRINTER_DRIVER_LENGTH 50 -/** - * Name of the printer itself. - */ -#define GUAC_PRINTER_NAME "G\0u\0a\0c\0a\0m\0o\0l\0e\0\0\0" -#define GUAC_PRINTER_NAME_LENGTH 20 - /** * Name of the filesystem. */ diff --git a/src/protocols/rdp/guac_rdpdr/rdpdr_printer.c b/src/protocols/rdp/guac_rdpdr/rdpdr_printer.c index 40ac36f2..ace025c4 100644 --- a/src/protocols/rdp/guac_rdpdr/rdpdr_printer.c +++ b/src/protocols/rdp/guac_rdpdr/rdpdr_printer.c @@ -25,12 +25,14 @@ #include "rdp.h" #include "rdp_print_job.h" #include "rdp_status.h" +#include "unicode.h" #include #include #include #include #include +#include #include #ifdef ENABLE_WINPR @@ -131,31 +133,6 @@ void guac_rdpdr_process_print_job_close(guac_rdpdr_device* device, } -static void guac_rdpdr_device_printer_announce_handler(guac_rdpdr_device* device, - wStream* output_stream, int device_id) { - - /* Printer header */ - guac_client_log(device->rdpdr->client, GUAC_LOG_INFO, "Sending printer"); - Stream_Write_UINT32(output_stream, RDPDR_DTYP_PRINT); - Stream_Write_UINT32(output_stream, device_id); - Stream_Write(output_stream, "PRN1\0\0\0\0", 8); /* DOS name */ - - /* Printer data */ - Stream_Write_UINT32(output_stream, 24 + GUAC_PRINTER_DRIVER_LENGTH + GUAC_PRINTER_NAME_LENGTH); - Stream_Write_UINT32(output_stream, - RDPDR_PRINTER_ANNOUNCE_FLAG_DEFAULTPRINTER - | RDPDR_PRINTER_ANNOUNCE_FLAG_NETWORKPRINTER); - Stream_Write_UINT32(output_stream, 0); /* reserved - must be 0 */ - Stream_Write_UINT32(output_stream, 0); /* PnPName length (PnPName is ultimately ignored) */ - Stream_Write_UINT32(output_stream, GUAC_PRINTER_DRIVER_LENGTH); /* DriverName length */ - Stream_Write_UINT32(output_stream, GUAC_PRINTER_NAME_LENGTH); /* PrinterName length */ - Stream_Write_UINT32(output_stream, 0); /* CachedFields length */ - - Stream_Write(output_stream, GUAC_PRINTER_DRIVER, GUAC_PRINTER_DRIVER_LENGTH); - Stream_Write(output_stream, GUAC_PRINTER_NAME, GUAC_PRINTER_NAME_LENGTH); - -} - static void guac_rdpdr_device_printer_iorequest_handler(guac_rdpdr_device* device, wStream* input_stream, int file_id, int completion_id, int major_func, int minor_func) { @@ -187,10 +164,12 @@ static void guac_rdpdr_device_printer_iorequest_handler(guac_rdpdr_device* devic } static void guac_rdpdr_device_printer_free_handler(guac_rdpdr_device* device) { - /* Do nothing */ + + Stream_Free(device->device_announce, 1); + } -void guac_rdpdr_register_printer(guac_rdpdrPlugin* rdpdr) { +void guac_rdpdr_register_printer(guac_rdpdrPlugin* rdpdr, char* printer_name) { int id = rdpdr->devices_registered++; @@ -200,10 +179,42 @@ void guac_rdpdr_register_printer(guac_rdpdrPlugin* rdpdr) { /* Init device */ device->rdpdr = rdpdr; device->device_id = id; - device->device_name = "Guacamole Printer"; + device->device_name = printer_name; + int device_name_len = guac_utf8_strlen(device->device_name); + device->device_type = RDPDR_DTYP_PRINT; + device->dos_name = "PRN1\0\0\0\0"; + + /* Set up device announce stream */ + int prt_name_len = (device_name_len + 1) * 2; + device->device_announce_len = 44 + prt_name_len + + GUAC_PRINTER_DRIVER_LENGTH; + device->device_announce = Stream_New(NULL, device->device_announce_len); + + /* Write common information. */ + Stream_Write_UINT32(device->device_announce, device->device_type); + Stream_Write_UINT32(device->device_announce, device->device_id); + Stream_Write(device->device_announce, device->dos_name, 8); + + /* DeviceDataLength */ + Stream_Write_UINT32(device->device_announce, 24 + prt_name_len + GUAC_PRINTER_DRIVER_LENGTH); + + /* Begin printer-specific information */ + Stream_Write_UINT32(device->device_announce, + RDPDR_PRINTER_ANNOUNCE_FLAG_DEFAULTPRINTER + | RDPDR_PRINTER_ANNOUNCE_FLAG_NETWORKPRINTER); /* Printer flags */ + Stream_Write_UINT32(device->device_announce, 0); /* Reserved - must be 0. */ + Stream_Write_UINT32(device->device_announce, 0); /* PnPName Length - ignored. */ + Stream_Write_UINT32(device->device_announce, GUAC_PRINTER_DRIVER_LENGTH); + Stream_Write_UINT32(device->device_announce, prt_name_len); + Stream_Write_UINT32(device->device_announce, 0); /* CachedFields length. */ + + Stream_Write(device->device_announce, GUAC_PRINTER_DRIVER, GUAC_PRINTER_DRIVER_LENGTH); + guac_rdp_utf8_to_utf16((const unsigned char*) device->device_name, + device_name_len + 1, (char*) Stream_Pointer(device->device_announce), + prt_name_len); + Stream_Seek(device->device_announce, prt_name_len); /* Set handlers */ - device->announce_handler = guac_rdpdr_device_printer_announce_handler; device->iorequest_handler = guac_rdpdr_device_printer_iorequest_handler; device->free_handler = guac_rdpdr_device_printer_free_handler; diff --git a/src/protocols/rdp/guac_rdpdr/rdpdr_printer.h b/src/protocols/rdp/guac_rdpdr/rdpdr_printer.h index 6f203d6b..9dbeb42c 100644 --- a/src/protocols/rdp/guac_rdpdr/rdpdr_printer.h +++ b/src/protocols/rdp/guac_rdpdr/rdpdr_printer.h @@ -34,8 +34,15 @@ /** * Registers a new printer device within the RDPDR plugin. This must be done * before RDPDR connection finishes. + * + * @param rdpdr + * The RDP device redirection plugin where the device is registered. + * + * @param printer_name + * The name of the printer that will be registered with the RDP + * connection and passed through to the server. */ -void guac_rdpdr_register_printer(guac_rdpdrPlugin* rdpdr); +void guac_rdpdr_register_printer(guac_rdpdrPlugin* rdpdr, char* printer_name); #endif diff --git a/src/protocols/rdp/guac_rdpdr/rdpdr_service.c b/src/protocols/rdp/guac_rdpdr/rdpdr_service.c index 233b117c..611d21a5 100644 --- a/src/protocols/rdp/guac_rdpdr/rdpdr_service.c +++ b/src/protocols/rdp/guac_rdpdr/rdpdr_service.c @@ -96,7 +96,7 @@ void guac_rdpdr_process_connect(rdpSvcPlugin* plugin) { /* Register printer if enabled */ if (rdp_client->settings->printing_enabled) - guac_rdpdr_register_printer(rdpdr); + guac_rdpdr_register_printer(rdpdr, rdp_client->settings->printer_name); /* Register drive if enabled */ if (rdp_client->settings->drive_enabled) diff --git a/src/protocols/rdp/guac_rdpdr/rdpdr_service.h b/src/protocols/rdp/guac_rdpdr/rdpdr_service.h index 782d8b5d..ea5cb577 100644 --- a/src/protocols/rdp/guac_rdpdr/rdpdr_service.h +++ b/src/protocols/rdp/guac_rdpdr/rdpdr_service.h @@ -74,15 +74,30 @@ struct guac_rdpdr_device { int device_id; /** - * An arbitrary device name, used for logging purposes only. + * Device name, used for logging and for passthrough to the + * server. */ const char* device_name; /** - * Handler which will be called when the RDPDR plugin is forming the client - * device announce list. + * The type of RDPDR device that this represents. */ - guac_rdpdr_device_announce_handler* announce_handler; + uint32_t device_type; + + /** + * The DOS name of the device. Max 8 bytes, including terminator. + */ + const char *dos_name; + + /** + * The stream that stores the RDPDR device announcement for this device. + */ + wStream* device_announce; + + /** + * The length of the device_announce wStream. + */ + int device_announce_len; /** * Handler which should be called for every I/O request received. @@ -90,7 +105,7 @@ struct guac_rdpdr_device { guac_rdpdr_device_iorequest_handler* iorequest_handler; /** - * Handlel which should be called when the device is being free'd. + * Handler which should be called when the device is being freed. */ guac_rdpdr_device_free_handler* free_handler; @@ -154,7 +169,7 @@ void guac_rdpdr_process_terminate(rdpSvcPlugin* plugin); void guac_rdpdr_process_event(rdpSvcPlugin* plugin, wMessage* event); /** - * Creates a new stream which contains the ommon DR_DEVICE_IOCOMPLETION header + * Creates a new stream which contains the common DR_DEVICE_IOCOMPLETION header * used for virtually all responses. */ wStream* guac_rdpdr_new_io_completion(guac_rdpdr_device* device, diff --git a/src/protocols/rdp/rdp_settings.c b/src/protocols/rdp/rdp_settings.c index e1358303..62fa1e25 100644 --- a/src/protocols/rdp/rdp_settings.c +++ b/src/protocols/rdp/rdp_settings.c @@ -52,6 +52,7 @@ const char* GUAC_RDP_CLIENT_ARGS[] = { "color-depth", "disable-audio", "enable-printing", + "printer-name", "enable-drive", "drive-path", "create-drive-path", @@ -187,6 +188,11 @@ enum RDP_ARGS_IDX { */ IDX_ENABLE_PRINTING, + /** + * The name of the printer that will be passed through to the RDP server. + */ + IDX_PRINTER_NAME, + /** * "true" if the virtual drive should be enabled, "false" or blank * otherwise. @@ -794,6 +800,11 @@ guac_rdp_settings* guac_rdp_parse_args(guac_user* user, guac_user_parse_args_boolean(user, GUAC_RDP_CLIENT_ARGS, argv, IDX_ENABLE_PRINTING, 0); + /* Name of redirected printer */ + settings->printer_name = + guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv, + IDX_PRINTER_NAME, "Guacamole Printer"); + /* Drive enable/disable */ settings->drive_enabled = guac_user_parse_args_boolean(user, GUAC_RDP_CLIENT_ARGS, argv, @@ -990,6 +1001,7 @@ void guac_rdp_settings_free(guac_rdp_settings* settings) { free(settings->remote_app_args); free(settings->remote_app_dir); free(settings->username); + free(settings->printer_name); /* Free channel name array */ if (settings->svc_names != NULL) { diff --git a/src/protocols/rdp/rdp_settings.h b/src/protocols/rdp/rdp_settings.h index 4f3839e6..5caffe98 100644 --- a/src/protocols/rdp/rdp_settings.h +++ b/src/protocols/rdp/rdp_settings.h @@ -177,6 +177,11 @@ typedef struct guac_rdp_settings { */ int printing_enabled; + /** + * Name of the redirected printer. + */ + char* printer_name; + /** * Whether the virtual drive is enabled. */