rdpsnd-messages.c (13099B)
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/rdpsnd/rdpsnd-messages.h" 21#include "channels/rdpsnd/rdpsnd.h" 22#include "rdp.h" 23 24#include <freerdp/codec/audio.h> 25#include <guacamole/audio.h> 26#include <guacamole/client.h> 27#include <winpr/stream.h> 28#include <winpr/wtypes.h> 29 30#include <stdlib.h> 31#include <string.h> 32 33void guac_rdpsnd_formats_handler(guac_rdp_common_svc* svc, 34 wStream* input_stream, guac_rdpsnd_pdu_header* header) { 35 36 int server_format_count; 37 int server_version; 38 int i; 39 40 wStream* output_stream; 41 int output_body_size; 42 unsigned char* output_stream_end; 43 44 guac_client* client = svc->client; 45 guac_rdpsnd* rdpsnd = (guac_rdpsnd*) svc->data; 46 47 guac_rdp_client* rdp_client = (guac_rdp_client*) client->data; 48 guac_audio_stream* audio = rdp_client->audio; 49 50 /* Reset own format count */ 51 rdpsnd->format_count = 0; 52 53 /* 54 * Check to make sure the stream has at least 20 bytes (14 byte seek, 55 * 2 x UTF16 reads, and 2 x UTF8 seeks). 56 */ 57 if (Stream_GetRemainingLength(input_stream) < 20) { 58 guac_client_log(client, GUAC_LOG_WARNING, "Server Audio Formats and " 59 "Version PDU does not contain the expected number of bytes. " 60 "Audio redirection may not work as expected."); 61 return; 62 } 63 64 /* Format header */ 65 Stream_Seek(input_stream, 14); 66 Stream_Read_UINT16(input_stream, server_format_count); 67 Stream_Seek_UINT8(input_stream); 68 Stream_Read_UINT16(input_stream, server_version); 69 Stream_Seek_UINT8(input_stream); 70 71 /* Initialize Client Audio Formats and Version PDU */ 72 output_stream = Stream_New(NULL, 24); 73 Stream_Write_UINT8(output_stream, SNDC_FORMATS); 74 Stream_Write_UINT8(output_stream, 0); 75 76 /* Fill in body size later */ 77 Stream_Seek_UINT16(output_stream); /* offset = 0x02 */ 78 79 /* Flags, volume, and pitch */ 80 Stream_Write_UINT32(output_stream, TSSNDCAPS_ALIVE); 81 Stream_Write_UINT32(output_stream, 0); 82 Stream_Write_UINT32(output_stream, 0); 83 84 /* Datagram port (UDP) */ 85 Stream_Write_UINT16(output_stream, 0); 86 87 /* Fill in format count later */ 88 Stream_Seek_UINT16(output_stream); /* offset = 0x12 */ 89 90 /* Version and padding */ 91 Stream_Write_UINT8(output_stream, 0); 92 Stream_Write_UINT16(output_stream, 6); 93 Stream_Write_UINT8(output_stream, 0); 94 95 /* Check each server format, respond if supported and audio is enabled */ 96 if (audio != NULL) { 97 for (i=0; i < server_format_count; i++) { 98 99 unsigned char* format_start; 100 101 int format_tag; 102 int channels; 103 int rate; 104 int bps; 105 int body_size; 106 107 /* Remember position in stream */ 108 Stream_GetPointer(input_stream, format_start); 109 110 /* Check to make sure Stream has at least 18 bytes. */ 111 if (Stream_GetRemainingLength(input_stream) < 18) { 112 guac_client_log(client, GUAC_LOG_WARNING, "Server Audio " 113 "Formats and Version PDU does not contain the expected " 114 "number of bytes. Audio redirection may not work as " 115 "expected."); 116 return; 117 } 118 119 /* Read format */ 120 Stream_Read_UINT16(input_stream, format_tag); 121 Stream_Read_UINT16(input_stream, channels); 122 Stream_Read_UINT32(input_stream, rate); 123 Stream_Seek_UINT32(input_stream); 124 Stream_Seek_UINT16(input_stream); 125 Stream_Read_UINT16(input_stream, bps); 126 127 /* Skip past extra data */ 128 Stream_Read_UINT16(input_stream, body_size); 129 130 /* Check that Stream has at least body_size bytes remaining. */ 131 if (Stream_GetRemainingLength(input_stream) < body_size) { 132 guac_client_log(client, GUAC_LOG_WARNING, "Server Audio " 133 "Formats and Version PDU does not contain the expected " 134 "number of bytes. Audio redirection may not work as " 135 "expected."); 136 return; 137 } 138 139 Stream_Seek(input_stream, body_size); 140 141 /* If PCM, accept */ 142 if (format_tag == WAVE_FORMAT_PCM) { 143 144 /* If can fit another format, accept it */ 145 if (rdpsnd->format_count < GUAC_RDP_MAX_FORMATS) { 146 147 /* Add channel */ 148 int current = rdpsnd->format_count++; 149 rdpsnd->formats[current].rate = rate; 150 rdpsnd->formats[current].channels = channels; 151 rdpsnd->formats[current].bps = bps; 152 153 /* Log format */ 154 guac_client_log(client, GUAC_LOG_INFO, 155 "Accepted format: %i-bit PCM with %i channels at " 156 "%i Hz", 157 bps, channels, rate); 158 159 /* Ensure audio stream is configured to use accepted 160 * format */ 161 guac_audio_stream_reset(audio, NULL, rate, channels, bps); 162 163 /* Queue format for sending as accepted */ 164 Stream_EnsureRemainingCapacity(output_stream, 165 18 + body_size); 166 Stream_Write(output_stream, format_start, 18 + body_size); 167 168 /* 169 * BEWARE that using Stream_EnsureRemainingCapacity means 170 * that any pointers returned via Stream_GetPointer on 171 * output_stream are invalid. 172 */ 173 174 } 175 176 /* Otherwise, log that we dropped one */ 177 else 178 guac_client_log(client, GUAC_LOG_INFO, 179 "Dropped valid format: %i-bit PCM with %i " 180 "channels at %i Hz", 181 bps, channels, rate); 182 183 } 184 185 } 186 } 187 188 /* Otherwise, ignore all supported formats as we do not intend to actually 189 * receive audio */ 190 else 191 guac_client_log(client, GUAC_LOG_DEBUG, 192 "Audio explicitly disabled. Ignoring supported formats."); 193 194 /* Calculate size of PDU */ 195 output_body_size = Stream_GetPosition(output_stream) - 4; 196 Stream_GetPointer(output_stream, output_stream_end); 197 198 /* Set body size */ 199 Stream_SetPosition(output_stream, 0x02); 200 Stream_Write_UINT16(output_stream, output_body_size); 201 202 /* Set format count */ 203 Stream_SetPosition(output_stream, 0x12); 204 Stream_Write_UINT16(output_stream, rdpsnd->format_count); 205 206 /* Reposition cursor at end (necessary for message send) */ 207 Stream_SetPointer(output_stream, output_stream_end); 208 209 /* Send accepted formats */ 210 guac_rdp_common_svc_write(svc, output_stream); 211 212 /* If version greater than 6, must send Quality Mode PDU */ 213 if (server_version >= 6) { 214 215 /* Always send High Quality for now */ 216 output_stream = Stream_New(NULL, 8); 217 Stream_Write_UINT8(output_stream, SNDC_QUALITYMODE); 218 Stream_Write_UINT8(output_stream, 0); 219 Stream_Write_UINT16(output_stream, 4); 220 Stream_Write_UINT16(output_stream, HIGH_QUALITY); 221 Stream_Write_UINT16(output_stream, 0); 222 223 guac_rdp_common_svc_write(svc, output_stream); 224 225 } 226 227} 228 229/* server is getting a feel of the round trip time */ 230void guac_rdpsnd_training_handler(guac_rdp_common_svc* svc, 231 wStream* input_stream, guac_rdpsnd_pdu_header* header) { 232 233 int data_size; 234 wStream* output_stream; 235 236 guac_rdpsnd* rdpsnd = (guac_rdpsnd*) svc->data; 237 238 /* Check to make sure audio stream contains a minimum number of bytes. */ 239 if (Stream_GetRemainingLength(input_stream) < 4) { 240 guac_client_log(svc->client, GUAC_LOG_WARNING, "Audio Training PDU " 241 "does not contain the expected number of bytes. Audio " 242 "redirection may not work as expected."); 243 return; 244 } 245 246 /* Read timestamp and data size */ 247 Stream_Read_UINT16(input_stream, rdpsnd->server_timestamp); 248 Stream_Read_UINT16(input_stream, data_size); 249 250 /* Send training response */ 251 output_stream = Stream_New(NULL, 8); 252 Stream_Write_UINT8(output_stream, SNDC_TRAINING); 253 Stream_Write_UINT8(output_stream, 0); 254 Stream_Write_UINT16(output_stream, 4); 255 Stream_Write_UINT16(output_stream, rdpsnd->server_timestamp); 256 Stream_Write_UINT16(output_stream, data_size); 257 258 guac_rdp_common_svc_write(svc, output_stream); 259 260} 261 262void guac_rdpsnd_wave_info_handler(guac_rdp_common_svc* svc, 263 wStream* input_stream, guac_rdpsnd_pdu_header* header) { 264 265 int format; 266 267 guac_client* client = svc->client; 268 guac_rdpsnd* rdpsnd = (guac_rdpsnd*) svc->data; 269 270 guac_rdp_client* rdp_client = (guac_rdp_client*) client->data; 271 guac_audio_stream* audio = rdp_client->audio; 272 273 /* Check to make sure audio stream contains a minimum number of bytes. */ 274 if (Stream_GetRemainingLength(input_stream) < 12) { 275 guac_client_log(svc->client, GUAC_LOG_WARNING, "Audio WaveInfo PDU " 276 "does not contain the expected number of bytes. Sound may not " 277 "work as expected."); 278 return; 279 } 280 281 /* Read wave information */ 282 Stream_Read_UINT16(input_stream, rdpsnd->server_timestamp); 283 Stream_Read_UINT16(input_stream, format); 284 Stream_Read_UINT8(input_stream, rdpsnd->waveinfo_block_number); 285 Stream_Seek(input_stream, 3); 286 Stream_Read(input_stream, rdpsnd->initial_wave_data, 4); 287 288 /* 289 * Size of incoming wave data is equal to the body size field of this 290 * header, less the size of a WaveInfo PDU (not including the header), 291 * thus body_size - 12. 292 */ 293 rdpsnd->incoming_wave_size = header->body_size - 12; 294 295 /* Read wave in next iteration */ 296 rdpsnd->next_pdu_is_wave = TRUE; 297 298 /* Reset audio stream if format has changed */ 299 if (audio != NULL) { 300 if (format < GUAC_RDP_MAX_FORMATS) 301 guac_audio_stream_reset(audio, NULL, 302 rdpsnd->formats[format].rate, 303 rdpsnd->formats[format].channels, 304 rdpsnd->formats[format].bps); 305 306 else 307 guac_client_log(svc->client, GUAC_LOG_WARNING, "RDP server " 308 "attempted to specify an invalid audio format. Sound may " 309 "not work as expected."); 310 } 311 312} 313 314void guac_rdpsnd_wave_handler(guac_rdp_common_svc* svc, 315 wStream* input_stream, guac_rdpsnd_pdu_header* header) { 316 317 guac_client* client = svc->client; 318 guac_rdpsnd* rdpsnd = (guac_rdpsnd*) svc->data; 319 320 guac_rdp_client* rdp_client = (guac_rdp_client*) client->data; 321 guac_audio_stream* audio = rdp_client->audio; 322 323 /* Verify that the stream has bytes to cover the wave size plus header. */ 324 if (Stream_Length(input_stream) < (rdpsnd->incoming_wave_size + 4)) { 325 guac_client_log(svc->client, GUAC_LOG_WARNING, "Audio Wave PDU does " 326 "not contain the expected number of bytes. Sound may not work " 327 "as expected."); 328 return; 329 } 330 331 /* Wave Confirmation PDU */ 332 wStream* output_stream = Stream_New(NULL, 8); 333 334 /* Get wave data */ 335 unsigned char* buffer = Stream_Buffer(input_stream); 336 337 /* Copy over first four bytes */ 338 memcpy(buffer, rdpsnd->initial_wave_data, 4); 339 340 /* Write rest of audio packet */ 341 if (audio != NULL) { 342 guac_audio_stream_write_pcm(audio, buffer, 343 rdpsnd->incoming_wave_size + 4); 344 guac_audio_stream_flush(audio); 345 } 346 347 /* Write Wave Confirmation PDU */ 348 Stream_Write_UINT8(output_stream, SNDC_WAVECONFIRM); 349 Stream_Write_UINT8(output_stream, 0); 350 Stream_Write_UINT16(output_stream, 4); 351 Stream_Write_UINT16(output_stream, rdpsnd->server_timestamp); 352 Stream_Write_UINT8(output_stream, rdpsnd->waveinfo_block_number); 353 Stream_Write_UINT8(output_stream, 0); 354 355 /* Send Wave Confirmation PDU */ 356 guac_rdp_common_svc_write(svc, output_stream); 357 358 /* We no longer expect to receive wave data */ 359 rdpsnd->next_pdu_is_wave = FALSE; 360 361} 362 363void guac_rdpsnd_close_handler(guac_rdp_common_svc* svc, 364 wStream* input_stream, guac_rdpsnd_pdu_header* header) { 365 366 /* Do nothing */ 367 368} 369