Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 74 additions & 1 deletion src/protocols/rdp/channels/rdpsnd/rdpsnd-messages.c
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ void guac_rdpsnd_formats_handler(guac_rdp_common_svc* svc,

/* Version and padding */
Stream_Write_UINT8(output_stream, 0);
Stream_Write_UINT16(output_stream, 6);
Stream_Write_UINT16(output_stream, 8); /* Version 8 is required for the server to use Wave2 (SNDC_WAVE2) */
Stream_Write_UINT8(output_stream, 0);

/* Check each server format, respond if supported and audio is enabled */
Expand Down Expand Up @@ -360,6 +360,79 @@ void guac_rdpsnd_wave_handler(guac_rdp_common_svc* svc,

}

void guac_rdpsnd_wave2_handler(guac_rdp_common_svc* svc,
wStream* input_stream, guac_rdpsnd_pdu_header* header) {

int format;
int wave_size;
int timestamp;
int block_number;
unsigned char* buffer;
wStream* output_stream;

guac_client* client = svc->client;
guac_rdpsnd* rdpsnd = (guac_rdpsnd*) svc->data;

guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
guac_audio_stream* audio = rdp_client->audio;

/* The Wave2 fixed fields occupy 12 bytes (per MS-RDPEA 2.2.3.10). */
if (Stream_GetRemainingLength(input_stream) < 12) {
guac_client_log(svc->client, GUAC_LOG_WARNING, "Audio Wave2 PDU does "
"not contain the expected number of bytes. Sound may not work "
"as expected.");
return;
}

/* Read Wave2 fixed fields */
Stream_Read_UINT16(input_stream, timestamp); /* wTimeStamp */
Stream_Read_UINT16(input_stream, format); /* wFormatNo */
Stream_Read_UINT8(input_stream, block_number); /* cBlockNo */
Stream_Seek(input_stream, 3); /* bPad */
Stream_Seek_UINT32(input_stream); /* dwAudioTimeStamp */

/*
* Unlike the legacy WaveInfo/Wave pair, the Wave2 PDU is self-contained:
* the remaining bytes are the complete audio block (with no four-byte
* split).
*/
wave_size = Stream_GetRemainingLength(input_stream);
Stream_GetPointer(input_stream, buffer);

if (audio != NULL) {

/* Reset audio stream to the format indicated by this PDU */
if (format < GUAC_RDP_MAX_FORMATS)
guac_audio_stream_reset(audio, NULL,
rdpsnd->formats[format].rate,
rdpsnd->formats[format].channels,
rdpsnd->formats[format].bps);

else
guac_client_log(svc->client, GUAC_LOG_WARNING, "RDP server "
"attempted to specify an invalid audio format. Sound may "
"not work as expected.");

/* Write audio packet */
guac_audio_stream_write_pcm(audio, buffer, wave_size);
guac_audio_stream_flush(audio);

}

/* Acknowledge via Wave Confirmation PDU (same as legacy Wave) */
output_stream = Stream_New(NULL, 8);
Stream_Write_UINT8(output_stream, SNDC_WAVECONFIRM);
Stream_Write_UINT8(output_stream, 0);
Stream_Write_UINT16(output_stream, 4);
Stream_Write_UINT16(output_stream, timestamp);
Stream_Write_UINT8(output_stream, block_number);
Stream_Write_UINT8(output_stream, 0);

/* Send Wave Confirmation PDU */
guac_rdp_common_svc_write(svc, output_stream);

}

void guac_rdpsnd_close_handler(guac_rdp_common_svc* svc,
wStream* input_stream, guac_rdpsnd_pdu_header* header) {

Expand Down
22 changes: 22 additions & 0 deletions src/protocols/rdp/channels/rdpsnd/rdpsnd-messages.h
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,28 @@ void guac_rdpsnd_wave_info_handler(guac_rdp_common_svc* svc,
void guac_rdpsnd_wave_handler(guac_rdp_common_svc* svc,
wStream* input_stream, guac_rdpsnd_pdu_header* header);

/**
* Handler for the SNDC_WAVE2 (Wave 2) PDU. Unlike the legacy SNDC_WAVE /
* SNDWAV pair, the Wave 2 PDU is self-contained: a single PDU carries both the
* wave metadata and the complete audio data (with no four-byte split). It is
* commonly used by modern Windows RDP servers. See:
*
* https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpea/25cebccb-d679-4302-8dd0-df7fb9a4f9b5
*
* @param svc
* The RDPSND channel receiving the SNDC_WAVE2 PDU.
*
* @param input_stream
* The FreeRDP input stream containing the remaining raw bytes (after the
* common header) of the SNDC_WAVE2 PDU.
*
* @param header
* The header content of the SNDC_WAVE2 PDU. All RDPSND messages contain
* the same header information.
*/
void guac_rdpsnd_wave2_handler(guac_rdp_common_svc* svc,
wStream* input_stream, guac_rdpsnd_pdu_header* header);

/**
* Handler for the SNDC_CLOSE (Close) PDU. This PDU is sent when audio
* streaming has stopped. This PDU is currently ignored by Guacamole. See:
Expand Down
5 changes: 5 additions & 0 deletions src/protocols/rdp/channels/rdpsnd/rdpsnd.c
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,11 @@ void guac_rdpsnd_process_receive(guac_rdp_common_svc* svc,
guac_rdpsnd_wave_info_handler(svc, input_stream, &header);
break;

/* Wave2 PDU */
case SNDC_WAVE2:
guac_rdpsnd_wave2_handler(svc, input_stream, &header);
break;

/* Close PDU */
case SNDC_CLOSE:
guac_rdpsnd_close_handler(svc, input_stream, &header);
Expand Down