diff --git a/channels/rdpsnd/alsa/rdpsnd_alsa.c b/channels/rdpsnd/alsa/rdpsnd_alsa.c index 91afdcdd5..72c6979d0 100644 --- a/channels/rdpsnd/alsa/rdpsnd_alsa.c +++ b/channels/rdpsnd/alsa/rdpsnd_alsa.c @@ -138,6 +138,7 @@ static void rdpsnd_alsa_set_format(rdpsndDevicePlugin* device, rdpsndFormat* for } break; + case 2: /* MS ADPCM */ case 0x11: /* IMA ADPCM */ alsa->format = SND_PCM_FORMAT_S16_LE; alsa->bytes_per_channel = 2; @@ -212,6 +213,7 @@ static boolean rdpsnd_alsa_format_supported(rdpsndDevicePlugin* device, rdpsndFo } break; + case 2: /* MS ADPCM */ case 0x11: /* IMA ADPCM */ if (format->nSamplesPerSec <= 48000 && format->wBitsPerSample == 4 && @@ -243,7 +245,14 @@ static void rdpsnd_alsa_play(rdpsndDevicePlugin* device, uint8* data, int size) if (alsa->out_handle == 0) return; - if (alsa->wformat == 0x11) + if (alsa->wformat == 2) + { + alsa->dsp_context->decode_ms_adpcm(alsa->dsp_context, + data, size, alsa->source_channels, alsa->block_size); + size = alsa->dsp_context->adpcm_size; + src = alsa->dsp_context->adpcm_buffer; + } + else if (alsa->wformat == 0x11) { alsa->dsp_context->decode_ima_adpcm(alsa->dsp_context, data, size, alsa->source_channels, alsa->block_size); diff --git a/channels/rdpsnd/pulse/rdpsnd_pulse.c b/channels/rdpsnd/pulse/rdpsnd_pulse.c index 5955db830..06f5a1716 100644 --- a/channels/rdpsnd/pulse/rdpsnd_pulse.c +++ b/channels/rdpsnd/pulse/rdpsnd_pulse.c @@ -212,6 +212,7 @@ static void rdpsnd_pulse_set_format_spec(rdpsndPulsePlugin* pulse, rdpsndFormat* sample_spec.format = PA_SAMPLE_ULAW; break; + case 2: /* MS ADPCM */ case 0x11: /* IMA ADPCM */ sample_spec.format = PA_SAMPLE_S16LE; break; @@ -364,6 +365,7 @@ static boolean rdpsnd_pulse_format_supported(rdpsndDevicePlugin* device, rdpsndF } break; + case 2: /* MS ADPCM */ case 0x11: /* IMA ADPCM */ if ((format->nSamplesPerSec <= PA_RATE_MAX) && (format->wBitsPerSample == 4) && @@ -405,7 +407,14 @@ static void rdpsnd_pulse_play(rdpsndDevicePlugin* device, uint8* data, int size) if (!pulse->stream) return; - if (pulse->format == 0x11) + if (pulse->format == 2) + { + pulse->dsp_context->decode_ms_adpcm(pulse->dsp_context, + data, size, pulse->sample_spec.channels, pulse->block_size); + size = pulse->dsp_context->adpcm_size; + src = pulse->dsp_context->adpcm_buffer; + } + else if (pulse->format == 0x11) { pulse->dsp_context->decode_ima_adpcm(pulse->dsp_context, data, size, pulse->sample_spec.channels, pulse->block_size); diff --git a/channels/rdpsnd/rdpsnd_main.c b/channels/rdpsnd/rdpsnd_main.c index f7d383ab8..ec0e5a6c3 100644 --- a/channels/rdpsnd/rdpsnd_main.c +++ b/channels/rdpsnd/rdpsnd_main.c @@ -35,27 +35,6 @@ #include "rdpsnd_main.h" -#define SNDC_CLOSE 1 -#define SNDC_WAVE 2 -#define SNDC_SETVOLUME 3 -#define SNDC_SETPITCH 4 -#define SNDC_WAVECONFIRM 5 -#define SNDC_TRAINING 6 -#define SNDC_FORMATS 7 -#define SNDC_CRYPTKEY 8 -#define SNDC_WAVEENCRYPT 9 -#define SNDC_UDPWAVE 10 -#define SNDC_UDPWAVELAST 11 -#define SNDC_QUALITYMODE 12 - -#define TSSNDCAPS_ALIVE 1 -#define TSSNDCAPS_VOLUME 2 -#define TSSNDCAPS_PITCH 4 - -#define DYNAMIC_QUALITY 0x0000 -#define MEDIUM_QUALITY 0x0001 -#define HIGH_QUALITY 0x0002 - struct rdpsnd_plugin { rdpSvcPlugin plugin; diff --git a/channels/rdpsnd/rdpsnd_main.h b/channels/rdpsnd/rdpsnd_main.h index 9c127479c..c8c402e1e 100644 --- a/channels/rdpsnd/rdpsnd_main.h +++ b/channels/rdpsnd/rdpsnd_main.h @@ -20,19 +20,9 @@ #ifndef __RDPSND_MAIN_H #define __RDPSND_MAIN_H -typedef struct rdpsnd_plugin rdpsndPlugin; +#include -typedef struct rdpsnd_format rdpsndFormat; -struct rdpsnd_format -{ - uint16 wFormatTag; - uint16 nChannels; - uint32 nSamplesPerSec; - uint16 nBlockAlign; - uint16 wBitsPerSample; - uint16 cbSize; - uint8* data; -}; +typedef struct rdpsnd_plugin rdpsndPlugin; typedef struct rdpsnd_device_plugin rdpsndDevicePlugin; diff --git a/client/X11/xf_graphics.c b/client/X11/xf_graphics.c index dfcedbca3..b25dc99d1 100644 --- a/client/X11/xf_graphics.c +++ b/client/X11/xf_graphics.c @@ -43,7 +43,7 @@ void xf_Bitmap_New(rdpContext* context, rdpBitmap* bitmap) if (bitmap->data != NULL) { data = freerdp_image_convert(bitmap->data, NULL, - bitmap->width, bitmap->height, bitmap->bpp, xfi->bpp, xfi->clrconv); + bitmap->width, bitmap->height, xfi->srcBpp, xfi->bpp, xfi->clrconv); if (bitmap->ephemeral != true) { diff --git a/client/X11/xfreerdp.1.xml b/client/X11/xfreerdp.1.xml index 1499274ee..009e41092 100644 --- a/client/X11/xfreerdp.1.xml +++ b/client/X11/xfreerdp.1.xml @@ -137,6 +137,14 @@ + + -K + + + Do not interfere with window manager bindings. Normally, xfreerdp captures all keystrokes while its window is focused. + + + -n hostname diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt index d02ac589c..fce03ec18 100644 --- a/include/CMakeLists.txt +++ b/include/CMakeLists.txt @@ -30,6 +30,7 @@ install(DIRECTORY freerdp/plugins DESTINATION include/freerdp FILES_MATCHING PAT install(DIRECTORY freerdp/locale DESTINATION include/freerdp FILES_MATCHING PATTERN "*.h") install(DIRECTORY freerdp/crypto DESTINATION include/freerdp FILES_MATCHING PATTERN "*.h") install(DIRECTORY freerdp/auth DESTINATION include/freerdp FILES_MATCHING PATTERN "*.h") +install(DIRECTORY freerdp/server DESTINATION include/freerdp FILES_MATCHING PATTERN "*.h") file(GLOB HEADERS "winpr/*.h") install_files(/include/winpr FILES ${HEADERS}) diff --git a/include/freerdp/channels/rdpsnd.h b/include/freerdp/channels/rdpsnd.h new file mode 100644 index 000000000..f54e2fb18 --- /dev/null +++ b/include/freerdp/channels/rdpsnd.h @@ -0,0 +1,59 @@ +/** + * FreeRDP: A Remote Desktop Protocol client. + * Audio Virtual Channel Types + * + * Copyright 2012 Vic Lee + * + * Licensed 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. + */ + +#ifndef __FREERDP_RDPSND_H +#define __FREERDP_RDPSND_H + +#include +#include + +typedef struct rdpsnd_format rdpsndFormat; +struct rdpsnd_format +{ + uint16 wFormatTag; + uint16 nChannels; + uint32 nSamplesPerSec; + uint16 nBlockAlign; + uint16 wBitsPerSample; + uint16 cbSize; + uint8* data; +}; + +#define SNDC_CLOSE 1 +#define SNDC_WAVE 2 +#define SNDC_SETVOLUME 3 +#define SNDC_SETPITCH 4 +#define SNDC_WAVECONFIRM 5 +#define SNDC_TRAINING 6 +#define SNDC_FORMATS 7 +#define SNDC_CRYPTKEY 8 +#define SNDC_WAVEENCRYPT 9 +#define SNDC_UDPWAVE 10 +#define SNDC_UDPWAVELAST 11 +#define SNDC_QUALITYMODE 12 + +#define TSSNDCAPS_ALIVE 1 +#define TSSNDCAPS_VOLUME 2 +#define TSSNDCAPS_PITCH 4 + +#define DYNAMIC_QUALITY 0x0000 +#define MEDIUM_QUALITY 0x0001 +#define HIGH_QUALITY 0x0002 + +#endif diff --git a/include/freerdp/listener.h b/include/freerdp/listener.h index 6b0a256f7..e007cc620 100644 --- a/include/freerdp/listener.h +++ b/include/freerdp/listener.h @@ -32,6 +32,7 @@ extern "C" { #endif typedef boolean (*psListenerOpen)(freerdp_listener* instance, const char* bind_address, uint16 port); +typedef boolean (*psListenerOpenLocal)(freerdp_listener* instance, const char* path); typedef boolean (*psListenerGetFileDescriptor)(freerdp_listener* instance, void** rfds, int* rcount); typedef boolean (*psListenerCheckFileDescriptor)(freerdp_listener* instance); typedef void (*psListenerClose)(freerdp_listener* instance); @@ -47,6 +48,7 @@ struct rdp_freerdp_listener void* param4; psListenerOpen Open; + psListenerOpenLocal OpenLocal; psListenerGetFileDescriptor GetFileDescriptor; psListenerCheckFileDescriptor CheckFileDescriptor; psListenerClose Close; diff --git a/include/freerdp/peer.h b/include/freerdp/peer.h index e0326d566..5031acc4c 100644 --- a/include/freerdp/peer.h +++ b/include/freerdp/peer.h @@ -69,6 +69,7 @@ struct rdp_freerdp_peer psPeerReceiveChannelData ReceiveChannelData; uint32 ack_frame_id; + boolean local; }; FREERDP_API void freerdp_peer_context_new(freerdp_peer* client); diff --git a/include/freerdp/server/rdpsnd.h b/include/freerdp/server/rdpsnd.h new file mode 100644 index 000000000..dcadf87f0 --- /dev/null +++ b/include/freerdp/server/rdpsnd.h @@ -0,0 +1,92 @@ +/** + * FreeRDP: A Remote Desktop Protocol client. + * Server Audio Virtual Channel + * + * Copyright 2012 Vic Lee + * + * Licensed 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. + */ + +#ifndef __SERVER_RDPSND_H +#define __SERVER_RDPSND_H + +#include +#include + +typedef struct _rdpsnd_server_context rdpsnd_server_context; + +typedef boolean (*psRdpsndServerInitialize)(rdpsnd_server_context* context); +typedef void (*psRdpsndServerSelectFormat)(rdpsnd_server_context* context, int client_format_index); +typedef boolean (*psRdpsndServerSendSamples)(rdpsnd_server_context* context, const void* buf, int nframes); +typedef boolean (*psRdpsndServerClose)(rdpsnd_server_context* context); + +typedef void (*psRdpsndServerActivated)(rdpsnd_server_context* context); + +struct _rdpsnd_server_context +{ + WTSVirtualChannelManager* vcm; + + /* Server self-defined pointer. */ + void* data; + + /* Server supported formats. Set by server. */ + const rdpsndFormat* server_formats; + int num_server_formats; + + /* Server source PCM audio format. Set by server. */ + rdpsndFormat src_format; + + /* Client supported formats. */ + rdpsndFormat* client_formats; + int num_client_formats; + int selected_client_format; + + /* Last sent audio block number. */ + int block_no; + + /*** APIs called by the server. ***/ + /** + * Initialize the channel. The caller should check the return value to see + * whether the initialization succeed. If not, the "Activated" callback + * will not be called and the server must not call any API on this context. + */ + psRdpsndServerInitialize Initialize; + /** + * Choose the audio format to be sent. The index argument is an index into + * the client_formats array and must be smaller than num_client_formats. + */ + psRdpsndServerSelectFormat SelectFormat; + /** + * Send audio samples. Actually bytes in the buffer must be: + * nframes * src_format.nBitsPerSample * src_format.nChannels / 8 + */ + psRdpsndServerSendSamples SendSamples; + /** + * Close the audio stream. + */ + psRdpsndServerClose Close; + + /*** Callbacks registered by the server. ***/ + /** + * The channel has been activated. The server maybe choose audio format and + * start audio stream from this point. Note that this callback is called + * from a different thread context so the server must be careful of thread + * synchronization. + */ + psRdpsndServerActivated Activated; +}; + +FREERDP_API rdpsnd_server_context* rdpsnd_server_context_new(WTSVirtualChannelManager* vcm); +FREERDP_API void rdpsnd_server_context_free(rdpsnd_server_context* context); + +#endif diff --git a/include/freerdp/settings.h b/include/freerdp/settings.h index 1172e7855..243773977 100644 --- a/include/freerdp/settings.h +++ b/include/freerdp/settings.h @@ -292,7 +292,8 @@ struct rdp_settings ALIGN64 char* tsg_hostname; /* 65 */ ALIGN64 char* tsg_username; /* 66 */ ALIGN64 char* tsg_password; /* 67 */ - ALIGN64 uint64 paddingC[80 - 68]; /* 68 */ + ALIGN64 boolean local; /* 68 */ + ALIGN64 uint64 paddingC[80 - 69]; /* 69 */ /* User Interface Parameters */ ALIGN64 boolean sw_gdi; /* 80 */ diff --git a/include/freerdp/utils/dsp.h b/include/freerdp/utils/dsp.h index 46ef066a4..835956ac8 100644 --- a/include/freerdp/utils/dsp.h +++ b/include/freerdp/utils/dsp.h @@ -22,12 +22,22 @@ #include -struct _ADPCM +union _ADPCM { - sint16 last_sample[2]; - sint16 last_step[2]; + struct + { + sint16 last_sample[2]; + sint16 last_step[2]; + } ima; + struct + { + uint8 predictor[2]; + sint32 delta[2]; + sint32 sample1[2]; + sint32 sample2[2]; + } ms; }; -typedef struct _ADPCM ADPCM; +typedef union _ADPCM ADPCM; typedef struct _FREERDP_DSP_CONTEXT FREERDP_DSP_CONTEXT; struct _FREERDP_DSP_CONTEXT @@ -52,6 +62,11 @@ struct _FREERDP_DSP_CONTEXT const uint8* src, int size, int channels, int block_size); void (*encode_ima_adpcm)(FREERDP_DSP_CONTEXT* context, const uint8* src, int size, int channels, int block_size); + + void (*decode_ms_adpcm)(FREERDP_DSP_CONTEXT* context, + const uint8* src, int size, int channels, int block_size); + void (*encode_ms_adpcm)(FREERDP_DSP_CONTEXT* context, + const uint8* src, int size, int channels, int block_size); }; FREERDP_API FREERDP_DSP_CONTEXT* freerdp_dsp_context_new(void); diff --git a/include/freerdp/utils/uds.h b/include/freerdp/utils/uds.h new file mode 100644 index 000000000..ea0c781b1 --- /dev/null +++ b/include/freerdp/utils/uds.h @@ -0,0 +1,28 @@ +/** + * FreeRDP: A Remote Desktop Protocol Client + * Unix Domain Socket Utils + * + * Copyright 2012 Vic Lee + * + * Licensed 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. + */ + +#ifndef FREERDP_UDS_UTILS_H +#define FREERDP_UDS_UTILS_H + +#include +#include + +FREERDP_API int freerdp_uds_connect(const char* path); + +#endif /* FREERDP_UDS_UTILS_H */ diff --git a/libfreerdp-codec/color.c b/libfreerdp-codec/color.c index fe3d63647..a660d7717 100644 --- a/libfreerdp-codec/color.c +++ b/libfreerdp-codec/color.c @@ -821,7 +821,7 @@ uint8* freerdp_icon_convert(uint8* srcData, uint8* dstData, uint8* mask, int wid for (bit = 0; bit < 8; bit++) if ((bmask & (0x80 >> bit)) == 0) - *(icon + (height - y) * width + x + bit) |= 0xFF000000; + *(icon + (height - y - 1) * width + x + bit) |= 0xFF000000; } if ((width % 8) != 0) @@ -830,7 +830,7 @@ uint8* freerdp_icon_convert(uint8* srcData, uint8* dstData, uint8* mask, int wid for (bit = 0; bit < width % 8; bit++) if ((bmask & (0x80 >> bit)) == 0) - *(icon + (height - y) * width + x + bit) |= 0xFF000000; + *(icon + (height - y - 1) * width + x + bit) |= 0xFF000000; } /* Skip padding */ @@ -839,8 +839,6 @@ uint8* freerdp_icon_convert(uint8* srcData, uint8* dstData, uint8* mask, int wid } } - free(mask); - return dstData; } diff --git a/libfreerdp-core/listener.c b/libfreerdp-core/listener.c index 2fb15a033..a39d96fec 100644 --- a/libfreerdp-core/listener.c +++ b/libfreerdp-core/listener.c @@ -26,6 +26,7 @@ #ifndef _WIN32 #include #include +#include #include #include #include @@ -121,6 +122,52 @@ static boolean freerdp_listener_open(freerdp_listener* instance, const char* bin return (listener->num_sockfds > 0 ? true : false); } +static boolean freerdp_listener_open_local(freerdp_listener* instance, const char* path) +{ +#ifndef _WIN32 + rdpListener* listener = (rdpListener*)instance->listener; + int status; + int sockfd; + struct sockaddr_un addr; + + sockfd = socket(AF_UNIX, SOCK_STREAM, 0); + if (sockfd == -1) + { + perror("socket"); + return false; + } + + fcntl(sockfd, F_SETFL, O_NONBLOCK); + + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, path, sizeof(addr.sun_path)); + unlink(path); + status = bind(sockfd, (struct sockaddr *) &addr, sizeof(addr)); + if (status != 0) + { + perror("bind"); + close(sockfd); + return false; + } + + status = listen(sockfd, 10); + if (status != 0) + { + perror("listen"); + close(sockfd); + return false; + } + + listener->sockfds[listener->num_sockfds++] = sockfd; + + printf("Listening on socket %s.\n", addr.sun_path); + + return true; +#else + return false; +#endif +} + static void freerdp_listener_close(freerdp_listener* instance) { int i; @@ -187,12 +234,18 @@ static boolean freerdp_listener_check_fds(freerdp_listener* instance) client = freerdp_peer_new(peer_sockfd); + sin_addr = NULL; if (peer_addr.ss_family == AF_INET) sin_addr = &(((struct sockaddr_in*) &peer_addr)->sin_addr); - else + else if (peer_addr.ss_family == AF_INET6) sin_addr = &(((struct sockaddr_in6*) &peer_addr)->sin6_addr); +#ifndef _WIN32 + else if (peer_addr.ss_family == AF_UNIX) + client->local = true; +#endif - inet_ntop(peer_addr.ss_family, sin_addr, client->hostname, sizeof(client->hostname)); + if (sin_addr) + inet_ntop(peer_addr.ss_family, sin_addr, client->hostname, sizeof(client->hostname)); IFCALL(instance->PeerAccepted, instance, client); } @@ -207,6 +260,7 @@ freerdp_listener* freerdp_listener_new(void) instance = xnew(freerdp_listener); instance->Open = freerdp_listener_open; + instance->OpenLocal = freerdp_listener_open_local; instance->GetFileDescriptor = freerdp_listener_get_fds; instance->CheckFileDescriptor = freerdp_listener_check_fds; instance->Close = freerdp_listener_close; diff --git a/libfreerdp-core/nego.c b/libfreerdp-core/nego.c index 7a88ca36d..34a0850c2 100644 --- a/libfreerdp-core/nego.c +++ b/libfreerdp-core/nego.c @@ -600,9 +600,12 @@ boolean nego_send_negotiation_response(rdpNego* nego) settings->tls_security = false; settings->nla_security = false; settings->rdp_security = true; - settings->encryption = true; - settings->encryption_method = ENCRYPTION_METHOD_40BIT | ENCRYPTION_METHOD_128BIT | ENCRYPTION_METHOD_FIPS; - settings->encryption_level = ENCRYPTION_LEVEL_CLIENT_COMPATIBLE; + if (!settings->local) + { + settings->encryption = true; + settings->encryption_method = ENCRYPTION_METHOD_40BIT | ENCRYPTION_METHOD_128BIT | ENCRYPTION_METHOD_FIPS; + settings->encryption_level = ENCRYPTION_LEVEL_CLIENT_COMPATIBLE; + } } else if (settings->selected_protocol == PROTOCOL_TLS) { diff --git a/libfreerdp-core/peer.c b/libfreerdp-core/peer.c index c531a23a1..4e2452746 100644 --- a/libfreerdp-core/peer.c +++ b/libfreerdp-core/peer.c @@ -26,6 +26,7 @@ static boolean freerdp_peer_initialize(freerdp_peer* client) { client->context->rdp->settings->server_mode = true; client->context->rdp->settings->frame_acknowledge = 0; + client->context->rdp->settings->local = client->local; client->context->rdp->state = CONNECTION_STATE_INITIAL; if (client->context->rdp->settings->rdp_key_file != NULL) diff --git a/libfreerdp-core/tcp.c b/libfreerdp-core/tcp.c index bfb642071..6c0390f31 100644 --- a/libfreerdp-core/tcp.c +++ b/libfreerdp-core/tcp.c @@ -46,6 +46,7 @@ #endif #include +#include #include #include #include @@ -115,30 +116,40 @@ boolean tcp_connect(rdpTcp* tcp, const char* hostname, uint16 port) uint32 option_value; socklen_t option_len; - tcp->sockfd = freerdp_tcp_connect(hostname, port); - - if (tcp->sockfd < 0) - return false; - - tcp_get_ip_address(tcp); - tcp_get_mac_address(tcp); - - option_value = 1; - option_len = sizeof(option_value); - setsockopt(tcp->sockfd, IPPROTO_TCP, TCP_NODELAY, (void*) &option_value, option_len); - - /* receive buffer must be a least 32 K */ - if (getsockopt(tcp->sockfd, SOL_SOCKET, SO_RCVBUF, (void*) &option_value, &option_len) == 0) + if (hostname[0] == '/') { - if (option_value < (1024 * 32)) - { - option_value = 1024 * 32; - option_len = sizeof(option_value); - setsockopt(tcp->sockfd, SOL_SOCKET, SO_RCVBUF, (void*) &option_value, option_len); - } - } + tcp->sockfd = freerdp_uds_connect(hostname); - tcp_set_keep_alive_mode(tcp); + if (tcp->sockfd < 0) + return false; + } + else + { + tcp->sockfd = freerdp_tcp_connect(hostname, port); + + if (tcp->sockfd < 0) + return false; + + tcp_get_ip_address(tcp); + tcp_get_mac_address(tcp); + + option_value = 1; + option_len = sizeof(option_value); + setsockopt(tcp->sockfd, IPPROTO_TCP, TCP_NODELAY, (void*) &option_value, option_len); + + /* receive buffer must be a least 32 K */ + if (getsockopt(tcp->sockfd, SOL_SOCKET, SO_RCVBUF, (void*) &option_value, &option_len) == 0) + { + if (option_value < (1024 * 32)) + { + option_value = 1024 * 32; + option_len = sizeof(option_value); + setsockopt(tcp->sockfd, SOL_SOCKET, SO_RCVBUF, (void*) &option_value, option_len); + } + } + + tcp_set_keep_alive_mode(tcp); + } return true; } diff --git a/libfreerdp-utils/CMakeLists.txt b/libfreerdp-utils/CMakeLists.txt index c69708600..f1b3e3335 100644 --- a/libfreerdp-utils/CMakeLists.txt +++ b/libfreerdp-utils/CMakeLists.txt @@ -47,6 +47,7 @@ set(FREERDP_UTILS_SRCS tcp.c thread.c time.c + uds.c unicode.c wait_obj.c) diff --git a/libfreerdp-utils/dsp.c b/libfreerdp-utils/dsp.c index 3a28b44fd..e1b7a138b 100644 --- a/libfreerdp-utils/dsp.c +++ b/libfreerdp-utils/dsp.c @@ -101,7 +101,7 @@ static uint16 dsp_decode_ima_adpcm_sample(ADPCM* adpcm, sint32 ss; sint32 d; - ss = ima_step_size_table[adpcm->last_step[channel]]; + ss = ima_step_size_table[adpcm->ima.last_step[channel]]; d = (ss >> 3); if (sample & 1) d += (ss >> 2); @@ -111,20 +111,20 @@ static uint16 dsp_decode_ima_adpcm_sample(ADPCM* adpcm, d += ss; if (sample & 8) d = -d; - d += adpcm->last_sample[channel]; + d += adpcm->ima.last_sample[channel]; if (d < -32768) d = -32768; else if (d > 32767) d = 32767; - adpcm->last_sample[channel] = (sint16) d; + adpcm->ima.last_sample[channel] = (sint16) d; - adpcm->last_step[channel] += ima_step_index_table[sample]; - if (adpcm->last_step[channel] < 0) - adpcm->last_step[channel] = 0; - else if (adpcm->last_step[channel] > 88) - adpcm->last_step[channel] = 88; + adpcm->ima.last_step[channel] += ima_step_index_table[sample]; + if (adpcm->ima.last_step[channel] < 0) + adpcm->ima.last_step[channel] = 0; + else if (adpcm->ima.last_step[channel] > 88) + adpcm->ima.last_step[channel] = 88; return (uint16) d; } @@ -150,15 +150,15 @@ static void freerdp_dsp_decode_ima_adpcm(FREERDP_DSP_CONTEXT* context, { if (size % block_size == 0) { - context->adpcm.last_sample[0] = (sint16) (((uint16)(*src)) | (((uint16)(*(src + 1))) << 8)); - context->adpcm.last_step[0] = (sint16) (*(src + 2)); + context->adpcm.ima.last_sample[0] = (sint16) (((uint16)(*src)) | (((uint16)(*(src + 1))) << 8)); + context->adpcm.ima.last_step[0] = (sint16) (*(src + 2)); src += 4; size -= 4; out_size -= 16; if (channels > 1) { - context->adpcm.last_sample[1] = (sint16) (((uint16)(*src)) | (((uint16)(*(src + 1))) << 8)); - context->adpcm.last_step[1] = (sint16) (*(src + 2)); + context->adpcm.ima.last_sample[1] = (sint16) (((uint16)(*src)) | (((uint16)(*(src + 1))) << 8)); + context->adpcm.ima.last_step[1] = (sint16) (*(src + 2)); src += 4; size -= 4; out_size -= 16; @@ -198,7 +198,7 @@ static void freerdp_dsp_decode_ima_adpcm(FREERDP_DSP_CONTEXT* context, } } - context->adpcm_size = out_size; + context->adpcm_size = dst - context->adpcm_buffer; } /** @@ -241,8 +241,8 @@ static uint8 dsp_encode_ima_adpcm_sample(ADPCM* adpcm, uint8 enc; sint32 diff; - ss = ima_step_size_table[adpcm->last_step[channel]]; - d = e = sample - adpcm->last_sample[channel]; + ss = ima_step_size_table[adpcm->ima.last_step[channel]]; + d = e = sample - adpcm->ima.last_sample[channel]; diff = ss >> 3; enc = 0; if (e < 0) @@ -273,18 +273,18 @@ static uint8 dsp_encode_ima_adpcm_sample(ADPCM* adpcm, else diff = d - e + diff; - diff += adpcm->last_sample[channel]; + diff += adpcm->ima.last_sample[channel]; if (diff < -32768) diff = -32768; else if (diff > 32767) diff = 32767; - adpcm->last_sample[channel] = (sint16) diff; + adpcm->ima.last_sample[channel] = (sint16) diff; - adpcm->last_step[channel] += ima_step_index_table[enc]; - if (adpcm->last_step[channel] < 0) - adpcm->last_step[channel] = 0; - else if (adpcm->last_step[channel] > 88) - adpcm->last_step[channel] = 88; + adpcm->ima.last_step[channel] += ima_step_index_table[enc]; + if (adpcm->ima.last_step[channel] < 0) + adpcm->ima.last_step[channel] = 0; + else if (adpcm->ima.last_step[channel] > 88) + adpcm->ima.last_step[channel] = 88; return enc; } @@ -309,15 +309,15 @@ static void freerdp_dsp_encode_ima_adpcm(FREERDP_DSP_CONTEXT* context, { if ((dst - context->adpcm_buffer) % block_size == 0) { - *dst++ = context->adpcm.last_sample[0] & 0xff; - *dst++ = (context->adpcm.last_sample[0] >> 8) & 0xff; - *dst++ = (uint8) context->adpcm.last_step[0]; + *dst++ = context->adpcm.ima.last_sample[0] & 0xff; + *dst++ = (context->adpcm.ima.last_sample[0] >> 8) & 0xff; + *dst++ = (uint8) context->adpcm.ima.last_step[0]; *dst++ = 0; if (channels > 1) { - *dst++ = context->adpcm.last_sample[1] & 0xff; - *dst++ = (context->adpcm.last_sample[1] >> 8) & 0xff; - *dst++ = (uint8) context->adpcm.last_step[1]; + *dst++ = context->adpcm.ima.last_sample[1] & 0xff; + *dst++ = (context->adpcm.ima.last_sample[1] >> 8) & 0xff; + *dst++ = (uint8) context->adpcm.ima.last_step[1]; *dst++ = 0; } } @@ -351,6 +351,241 @@ static void freerdp_dsp_encode_ima_adpcm(FREERDP_DSP_CONTEXT* context, context->adpcm_size = dst - context->adpcm_buffer; } +/** + * Microsoft ADPCM Specification: + * + * http://wiki.multimedia.cx/index.php?title=Microsoft_ADPCM + */ + +static const sint16 ms_adpcm_adaptation_table[] = +{ + 230, 230, 230, 230, 307, 409, 512, 614, + 768, 614, 512, 409, 307, 230, 230, 230 +}; + +static const sint16 ms_adpcm_coeff1_table[] = +{ + 256, 512, 0, 192, 240, 460, 392 +}; + +static const sint16 ms_adpcm_coeff2_table[] = +{ + 0, -256, 0, 64, 0, -208, -232 +}; + +static sint16 freerdp_dsp_decode_ms_adpcm_sample(ADPCM* adpcm, uint8 sample, int channel) +{ + sint8 nibble; + sint32 presample; + + nibble = (sample & 0x08 ? (sint8)sample - 16 : sample); + presample = ((adpcm->ms.sample1[channel] * ms_adpcm_coeff1_table[adpcm->ms.predictor[channel]]) + + (adpcm->ms.sample2[channel] * ms_adpcm_coeff2_table[adpcm->ms.predictor[channel]])) / 256; + presample += nibble * adpcm->ms.delta[channel]; + if (presample > 32767) + presample = 32767; + else if (presample < -32768) + presample = -32768; + adpcm->ms.sample2[channel] = adpcm->ms.sample1[channel]; + adpcm->ms.sample1[channel] = presample; + adpcm->ms.delta[channel] = adpcm->ms.delta[channel] * ms_adpcm_adaptation_table[sample] / 256; + if (adpcm->ms.delta[channel] < 16) + adpcm->ms.delta[channel] = 16; + return (sint16) presample; +} + +static void freerdp_dsp_decode_ms_adpcm(FREERDP_DSP_CONTEXT* context, + const uint8* src, int size, int channels, int block_size) +{ + uint8* dst; + uint8 sample; + uint32 out_size; + + out_size = size * 4; + if (out_size > context->adpcm_maxlength) + { + context->adpcm_maxlength = out_size + 1024; + context->adpcm_buffer = xrealloc(context->adpcm_buffer, context->adpcm_maxlength); + } + dst = context->adpcm_buffer; + while (size > 0) + { + if (size % block_size == 0) + { + if (channels > 1) + { + context->adpcm.ms.predictor[0] = *src++; + context->adpcm.ms.predictor[1] = *src++; + context->adpcm.ms.delta[0] = *((sint16*)src); + src += 2; + context->adpcm.ms.delta[1] = *((sint16*)src); + src += 2; + context->adpcm.ms.sample1[0] = *((sint16*)src); + src += 2; + context->adpcm.ms.sample1[1] = *((sint16*)src); + src += 2; + context->adpcm.ms.sample2[0] = *((sint16*)src); + src += 2; + context->adpcm.ms.sample2[1] = *((sint16*)src); + src += 2; + size -= 14; + + *((sint16*)dst) = context->adpcm.ms.sample2[0]; + dst += 2; + *((sint16*)dst) = context->adpcm.ms.sample2[1]; + dst += 2; + *((sint16*)dst) = context->adpcm.ms.sample1[0]; + dst += 2; + *((sint16*)dst) = context->adpcm.ms.sample1[1]; + dst += 2; + } + else + { + context->adpcm.ms.predictor[0] = *src++; + context->adpcm.ms.delta[0] = *((sint16*)src); + src += 2; + context->adpcm.ms.sample1[0] = *((sint16*)src); + src += 2; + context->adpcm.ms.sample2[0] = *((sint16*)src); + src += 2; + size -= 7; + + *((sint16*)dst) = context->adpcm.ms.sample2[0]; + dst += 2; + *((sint16*)dst) = context->adpcm.ms.sample1[0]; + dst += 2; + } + } + + if (channels > 1) + { + sample = *src++; + size--; + *((sint16*)dst) = freerdp_dsp_decode_ms_adpcm_sample(&context->adpcm, sample >> 4, 0); + dst += 2; + *((sint16*)dst) = freerdp_dsp_decode_ms_adpcm_sample(&context->adpcm, sample & 0x0F, 1); + dst += 2; + + sample = *src++; + size--; + *((sint16*)dst) = freerdp_dsp_decode_ms_adpcm_sample(&context->adpcm, sample >> 4, 0); + dst += 2; + *((sint16*)dst) = freerdp_dsp_decode_ms_adpcm_sample(&context->adpcm, sample & 0x0F, 1); + dst += 2; + } + else + { + sample = *src++; + size--; + *((sint16*)dst) = freerdp_dsp_decode_ms_adpcm_sample(&context->adpcm, sample >> 4, 0); + dst += 2; + *((sint16*)dst) = freerdp_dsp_decode_ms_adpcm_sample(&context->adpcm, sample & 0x0F, 0); + dst += 2; + } + } + + context->adpcm_size = dst - context->adpcm_buffer; +} + +static uint8 freerdp_dsp_encode_ms_adpcm_sample(ADPCM* adpcm, sint32 sample, int channel) +{ + sint32 presample; + sint32 errordelta; + + presample = ((adpcm->ms.sample1[channel] * ms_adpcm_coeff1_table[adpcm->ms.predictor[channel]]) + + (adpcm->ms.sample2[channel] * ms_adpcm_coeff2_table[adpcm->ms.predictor[channel]])) / 256; + errordelta = (sample - presample) / adpcm->ms.delta[channel]; + if ((sample - presample) % adpcm->ms.delta[channel] > adpcm->ms.delta[channel] / 2) + errordelta++; + if (errordelta > 7) + errordelta = 7; + else if (errordelta < -8) + errordelta = -8; + presample += adpcm->ms.delta[channel] * errordelta; + if (presample > 32767) + presample = 32767; + else if (presample < -32768) + presample = -32768; + adpcm->ms.sample2[channel] = adpcm->ms.sample1[channel]; + adpcm->ms.sample1[channel] = presample; + adpcm->ms.delta[channel] = adpcm->ms.delta[channel] * ms_adpcm_adaptation_table[(((uint8)errordelta) & 0x0F)] / 256; + if (adpcm->ms.delta[channel] < 16) + adpcm->ms.delta[channel] = 16; + return ((uint8)errordelta) & 0x0F; +} + +static void freerdp_dsp_encode_ms_adpcm(FREERDP_DSP_CONTEXT* context, + const uint8* src, int size, int channels, int block_size) +{ + uint8* dst; + sint32 sample; + uint32 out_size; + + out_size = size / 2; + if (out_size > context->adpcm_maxlength) + { + context->adpcm_maxlength = out_size + 1024; + context->adpcm_buffer = xrealloc(context->adpcm_buffer, context->adpcm_maxlength); + } + dst = context->adpcm_buffer; + + if (context->adpcm.ms.delta[0] < 16) + context->adpcm.ms.delta[0] = 16; + if (context->adpcm.ms.delta[1] < 16) + context->adpcm.ms.delta[1] = 16; + + while (size > 0) + { + if ((dst - context->adpcm_buffer) % block_size == 0) + { + if (channels > 1) + { + *dst++ = context->adpcm.ms.predictor[0]; + *dst++ = context->adpcm.ms.predictor[1]; + *dst++ = (uint8) (context->adpcm.ms.delta[0] & 0xff); + *dst++ = (uint8) ((context->adpcm.ms.delta[0] >> 8) & 0xff); + *dst++ = (uint8) (context->adpcm.ms.delta[1] & 0xff); + *dst++ = (uint8) ((context->adpcm.ms.delta[1] >> 8) & 0xff); + context->adpcm.ms.sample1[0] = *((sint16*) (src + 4)); + context->adpcm.ms.sample1[1] = *((sint16*) (src + 6)); + context->adpcm.ms.sample2[0] = *((sint16*) (src + 0)); + context->adpcm.ms.sample2[1] = *((sint16*) (src + 2)); + *((sint16*) (dst + 0)) = (sint16) context->adpcm.ms.sample1[0]; + *((sint16*) (dst + 2)) = (sint16) context->adpcm.ms.sample1[1]; + *((sint16*) (dst + 4)) = (sint16) context->adpcm.ms.sample2[0]; + *((sint16*) (dst + 6)) = (sint16) context->adpcm.ms.sample2[1]; + dst += 8; + src += 8; + size -= 8; + } + else + { + *dst++ = context->adpcm.ms.predictor[0]; + *dst++ = (uint8) (context->adpcm.ms.delta[0] & 0xff); + *dst++ = (uint8) ((context->adpcm.ms.delta[0] >> 8) & 0xff); + context->adpcm.ms.sample1[0] = *((sint16*) (src + 2)); + context->adpcm.ms.sample2[0] = *((sint16*) (src + 0)); + *((sint16*) (dst + 0)) = (sint16) context->adpcm.ms.sample1[0]; + *((sint16*) (dst + 2)) = (sint16) context->adpcm.ms.sample2[0]; + dst += 4; + src += 4; + size -= 4; + } + } + + sample = *((sint16*) src); + src += 2; + *dst = freerdp_dsp_encode_ms_adpcm_sample(&context->adpcm, sample, 0) << 4; + sample = *((sint16*) src); + src += 2; + *dst += freerdp_dsp_encode_ms_adpcm_sample(&context->adpcm, sample, channels > 1 ? 1 : 0); + dst++; + size -= 4; + } + + context->adpcm_size = dst - context->adpcm_buffer; +} + FREERDP_DSP_CONTEXT* freerdp_dsp_context_new(void) { FREERDP_DSP_CONTEXT* context; @@ -360,6 +595,8 @@ FREERDP_DSP_CONTEXT* freerdp_dsp_context_new(void) context->resample = freerdp_dsp_resample; context->decode_ima_adpcm = freerdp_dsp_decode_ima_adpcm; context->encode_ima_adpcm = freerdp_dsp_encode_ima_adpcm; + context->decode_ms_adpcm = freerdp_dsp_decode_ms_adpcm; + context->encode_ms_adpcm = freerdp_dsp_encode_ms_adpcm; return context; } diff --git a/libfreerdp-utils/list.c b/libfreerdp-utils/list.c index 5cc11be6c..936b21762 100644 --- a/libfreerdp-utils/list.c +++ b/libfreerdp-utils/list.c @@ -1,4 +1,4 @@ -/** +/* * FreeRDP: A Remote Desktop Protocol Client * Double-linked List Utils * diff --git a/libfreerdp-utils/memory.c b/libfreerdp-utils/memory.c index 525772cef..fae1cc2bc 100644 --- a/libfreerdp-utils/memory.c +++ b/libfreerdp-utils/memory.c @@ -1,4 +1,4 @@ -/** +/* * FreeRDP: A Remote Desktop Protocol Client * Memory Utils * @@ -25,7 +25,12 @@ /** * Allocate memory. - * @param size + * This function is used to secure a malloc call. + * It verifies its return value, and logs an error if the allocation failed. + * + * @param size - number of bytes to allocate. If the size is < 1, it will default to 1. + * + * @return a pointer to the allocated buffer. NULL if the allocation failed. */ void* xmalloc(size_t size) @@ -48,9 +53,13 @@ void* xmalloc(size_t size) /** * Allocate memory initialized to zero. - * @param size + * This function is used to secure a calloc call. + * It verifies its return value, and logs an error if the allocation failed. + * + * @param size - number of bytes to allocate. If the size is < 1, it will default to 1. + * + * @return a pointer to the allocated and zeroed buffer. NULL if the allocation failed. */ - void* xzalloc(size_t size) { void* mem; @@ -71,8 +80,13 @@ void* xzalloc(size_t size) /** * Reallocate memory. - * @param ptr - * @param size + * This function is used to secure a realloc call. + * It verifies its return value, and logs an error if the allocation failed. + * + * @param ptr - pointer to the buffer that needs reallocation. This can be NULL, in which case a new buffer is allocated. + * @param size - number of bytes to allocate. If the size is < 1, it will default to 1. + * + * @return a pointer to the reallocated buffer. NULL if the allocation failed (in which case the 'ptr' argument is untouched). */ void* xrealloc(void* ptr, size_t size) @@ -92,7 +106,10 @@ void* xrealloc(void* ptr, size_t size) /** * Free memory. - * @param mem + * This function is used to secure a free call. + * It verifies that the pointer is valid (non-NULL) before trying to deallocate it's buffer. + * + * @param ptr - pointer to a buffer that needs deallocation. If ptr is NULL, nothing will be done (no segfault). */ void xfree(void* ptr) @@ -103,8 +120,14 @@ void xfree(void* ptr) /** * Duplicate a string in memory. - * @param str - * @return + * This function is used to secure the strdup function. + * It will allocate a new memory buffer and copy the string content in it. + * If allocation fails, it will log an error. + * + * @param str - pointer to the character string to copy. If str is NULL, nothing is done. + * + * @return a pointer to a newly allocated character string containing the same bytes as str. + * NULL if an allocation error occurred, or if the str parameter was NULL. */ char* xstrdup(const char* str) @@ -127,9 +150,14 @@ char* xstrdup(const char* str) } /** - * Duplicate a string in memory. - * @param wstr - * @return + * Duplicate a wide string in memory. + * This function is used to secure a call to wcsdup. + * It verifies the return value, and logs a message if an allocation error occurred. + * + * @param wstr - pointer to the wide-character string to duplicate. If wstr is NULL, nothing will be done. + * + * @return a pointer to the newly allocated string, containing the same data as wstr. + * NULL if an allocation error occurred (or if wstr was NULL). */ wchar_t* xwcsdup(const wchar_t* wstr) @@ -143,6 +171,10 @@ wchar_t* xwcsdup(const wchar_t* wstr) mem = _wcsdup(wstr); #elif sun mem = wsdup(wstr); +#elif (defined(__APPLE__) && defined(__MACH__)) || defined(ANDROID) + mem = xmalloc(wcslen(wstr)); + if (mem != NULL) + wcscpy(mem, wstr); #else mem = wcsdup(wstr); #endif @@ -153,6 +185,16 @@ wchar_t* xwcsdup(const wchar_t* wstr) return mem; } +/** + * Create an uppercase version of the given string. + * This function will duplicate the string (using xstrdup()) and change its content to all uppercase. + * The original string is untouched. + * + * @param str - pointer to the character string to convert. This content is untouched by the function. + * + * @return pointer to a newly allocated character string, containing the same content as str, converted to uppercase. + * NULL if an allocation error occured. + */ char* xstrtoup(const char* str) { char* out; @@ -167,8 +209,6 @@ char* xstrtoup(const char* str) c = toupper((unsigned char)*p); *p++ = (char)c; } - return out; } - else - return NULL; + return out; } diff --git a/libfreerdp-utils/mutex.c b/libfreerdp-utils/mutex.c index a5e07ca8e..49c539076 100644 --- a/libfreerdp-utils/mutex.c +++ b/libfreerdp-utils/mutex.c @@ -1,4 +1,4 @@ -/** +/* * FreeRDP: A Remote Desktop Protocol Client * Mutex Utils * @@ -28,6 +28,22 @@ #define freerdp_mutex_t pthread_mutex_t #endif +/** + * Mutex are used to prevent concurrent accesses to specific portions of code. + * This funciton and the other freerdp_mutex_*() function are defining a portable API + * to get mutex for both Windows and Linux, where the lower level implementation is very different. + * + * This function creates a new freerdp_mutex object. + * Use freerdp_mutex_lock() to get exclusive ownership of the mutex, and lock a given (protected) portion of code. + * Use freerdp_mutex_unlock() to release your ownership on a mutex. + * Use freerdp_mutex_free() to release the resources associated with the mutex when it is no longer needed. + * + * @return a freerdp_mutex pointer. This should be considered opaque, as the implementation is platform dependent. + * NULL is returned if an allocation or initialization error occurred. + * + * @see pthread documentation for Linux implementation of mutexes + * @see MSDN http://msdn.microsoft.com/en-us/library/windows/desktop/ms682411%28v=vs.85%29.aspx for Windows implementation + */ freerdp_mutex freerdp_mutex_new(void) { #ifdef _WIN32 @@ -37,11 +53,18 @@ freerdp_mutex freerdp_mutex_new(void) #else freerdp_mutex_t* mutex; mutex = xnew(freerdp_mutex_t); - pthread_mutex_init(mutex, 0); + if (mutex) + pthread_mutex_init(mutex, 0); return mutex; #endif } +/** + * This function is used to deallocate all resources associated with a freerdp_mutex object. + * + * @param mutex [in] - Pointer to the mutex that needs to be deallocated. + * On return, this object is not valid anymore. + */ void freerdp_mutex_free(freerdp_mutex mutex) { #ifdef _WIN32 @@ -52,6 +75,15 @@ void freerdp_mutex_free(freerdp_mutex mutex) #endif } +/** + * Use this function to get exclusive ownership of the mutex. + * This should be called before entering a portion of code that needs to be protected against concurrent accesses. + * Use the freerdp_mutex_unlock() call to release ownership when you leave this protected code. + * + * @param mutex [in] - An initialized freerdp_mutex object, as returned from a call to freerdp_mutex_new(). + * This function will suspend the running thread when called, until the mutex is available. + * Only one thread at a time will be allowed to continue execution. + */ void freerdp_mutex_lock(freerdp_mutex mutex) { #ifdef _WIN32 @@ -61,6 +93,13 @@ void freerdp_mutex_lock(freerdp_mutex mutex) #endif } +/** + * Use this function to release your ownership on a mutex. + * This should be called when leaving a portion of code that needs to be protected against concurrent accesses. + * DO NOT use this call on a mutex that you do not own. See freerdp_mutex_lock() for getting ownership on a mutex. + * + * @param mutex [in] - Pointer to a mutex that was locked by a call to freerdp_mutex_lock(). + */ void freerdp_mutex_unlock(freerdp_mutex mutex) { #ifdef _WIN32 diff --git a/libfreerdp-utils/uds.c b/libfreerdp-utils/uds.c new file mode 100644 index 000000000..46c527e67 --- /dev/null +++ b/libfreerdp-utils/uds.c @@ -0,0 +1,74 @@ +/** + * FreeRDP: A Remote Desktop Protocol Client + * Unix Domain Socket Utils + * + * Copyright 2012 Vic Lee + * + * Licensed 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 + +#include +#include +#include +#include +#include +#include + +#ifndef _WIN32 + +#include +#include +#include +#include +#include +#include +#include +#include + +#endif + +int freerdp_uds_connect(const char* path) +{ +#ifndef _WIN32 + + int status; + int sockfd; + struct sockaddr_un addr; + + sockfd = socket(AF_UNIX, SOCK_STREAM, 0); + if (sockfd == -1) + { + perror("socket"); + return -1; + } + + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, path, sizeof(addr.sun_path)); + status = connect(sockfd, (struct sockaddr *) &addr, sizeof(addr)); + if (status < 0) + { + perror("connect"); + close(sockfd); + return -1; + } + + return sockfd; + +#else /* ifndef _WIN32 */ + + return -1; + +#endif +} diff --git a/server/CMakeLists.txt b/server/CMakeLists.txt index b6d659652..b208f64dd 100644 --- a/server/CMakeLists.txt +++ b/server/CMakeLists.txt @@ -29,5 +29,7 @@ if(NOT WIN32) if(WITH_X11) add_subdirectory(X11) endif() - + + # Build Server Channels library + add_subdirectory(channels) endif() diff --git a/server/channels/CMakeLists.txt b/server/channels/CMakeLists.txt new file mode 100644 index 000000000..ecd431ed8 --- /dev/null +++ b/server/channels/CMakeLists.txt @@ -0,0 +1,30 @@ +# FreeRDP: A Remote Desktop Protocol Client +# libfreerdp-server-channels cmake build script +# +# Copyright 2011 O.S. Systems Software Ltda. +# Copyright 2011 Otavio Salvador +# Copyright 2011 Marc-Andre Moreau +# +# Licensed 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. + +set(FREERDP_SERVER_CHANNELS_SRCS + rdpsnd.c +) + +add_library(freerdp-server-channels ${FREERDP_SERVER_CHANNELS_SRCS}) + +set_target_properties(freerdp-server-channels PROPERTIES VERSION ${FREERDP_VERSION_FULL} SOVERSION ${FREERDP_VERSION} PREFIX "lib") +target_link_libraries(freerdp-server-channels freerdp-channels freerdp-utils) + +install(TARGETS freerdp-server-channels DESTINATION ${CMAKE_INSTALL_LIBDIR}) + diff --git a/server/channels/rdpsnd.c b/server/channels/rdpsnd.c new file mode 100644 index 000000000..32a909487 --- /dev/null +++ b/server/channels/rdpsnd.c @@ -0,0 +1,452 @@ +/** + * FreeRDP: A Remote Desktop Protocol client. + * Server Audio Virtual Channel + * + * Copyright 2012 Vic Lee + * + * Licensed 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +typedef struct _rdpsnd_server +{ + rdpsnd_server_context context; + + void* rdpsnd_channel; + freerdp_thread* rdpsnd_channel_thread; + STREAM* rdpsnd_pdu; + + FREERDP_DSP_CONTEXT* dsp_context; + uint8* out_buffer; + int out_buffer_size; + int out_frames; + int out_pending_frames; + + uint32 src_bytes_per_sample; + uint32 src_bytes_per_frame; +} rdpsnd_server; + +#define RDPSND_PDU_INIT(_s, _msgType) \ +{ \ + stream_write_uint8(_s, _msgType); \ + stream_write_uint8(_s, 0); \ + stream_seek_uint16(_s); \ +} + +#define RDPSND_PDU_FINISH(_s) \ +{ \ + boolean _r; \ + int _pos; \ + _pos = stream_get_pos(_s); \ + stream_set_pos(_s, 2); \ + stream_write_uint16(_s, _pos - 4); \ + stream_set_pos(_s, _pos); \ + _r = WTSVirtualChannelWrite(rdpsnd->rdpsnd_channel, stream_get_head(_s), stream_get_length(_s), NULL); \ + stream_set_pos(_s, 0); \ + return _r; \ +} + +static boolean rdpsnd_server_send_formats(rdpsnd_server* rdpsnd, STREAM* s) +{ + uint16 i; + + RDPSND_PDU_INIT(s, SNDC_FORMATS); + + stream_write_uint32(s, 0); /* dwFlags */ + stream_write_uint32(s, 0); /* dwVolume */ + stream_write_uint32(s, 0); /* dwPitch */ + stream_write_uint16(s, 0); /* wDGramPort */ + stream_write_uint16(s, rdpsnd->context.num_server_formats); /* wNumberOfFormats */ + stream_write_uint8(s, rdpsnd->context.block_no); /* cLastBlockConfirmed */ + stream_write_uint16(s, 0x06); /* wVersion */ + stream_write_uint8(s, 0); /* bPad */ + + for (i = 0; i < rdpsnd->context.num_server_formats; i++) + { + stream_write_uint16(s, rdpsnd->context.server_formats[i].wFormatTag); /* wFormatTag (WAVE_FORMAT_PCM) */ + stream_write_uint16(s, rdpsnd->context.server_formats[i].nChannels); /* nChannels */ + stream_write_uint32(s, rdpsnd->context.server_formats[i].nSamplesPerSec); /* nSamplesPerSec */ + stream_write_uint32(s, rdpsnd->context.server_formats[i].nSamplesPerSec * + rdpsnd->context.server_formats[i].nChannels * + rdpsnd->context.server_formats[i].wBitsPerSample / 8); /* nAvgBytesPerSec */ + stream_write_uint16(s, rdpsnd->context.server_formats[i].nBlockAlign); /* nBlockAlign */ + stream_write_uint16(s, rdpsnd->context.server_formats[i].wBitsPerSample); /* wBitsPerSample */ + stream_write_uint16(s, rdpsnd->context.server_formats[i].cbSize); /* cbSize */ + if (rdpsnd->context.server_formats[i].cbSize > 0) + { + stream_write(s, rdpsnd->context.server_formats[i].data, rdpsnd->context.server_formats[i].cbSize); + } + } + + RDPSND_PDU_FINISH(s); +} + +static boolean rdpsnd_server_recv_formats(rdpsnd_server* rdpsnd, STREAM* s) +{ + int i; + + if (stream_get_left(s) < 20) + return false; + + stream_seek_uint32(s); /* dwFlags */ + stream_seek_uint32(s); /* dwVolume */ + stream_seek_uint32(s); /* dwPitch */ + stream_seek_uint16(s); /* wDGramPort */ + stream_read_uint16(s, rdpsnd->context.num_client_formats); /* wNumberOfFormats */ + stream_seek_uint8(s); /* cLastBlockConfirmed */ + stream_seek_uint16(s); /* wVersion */ + stream_seek_uint8(s); /* bPad */ + + if (rdpsnd->context.num_client_formats > 0) + { + rdpsnd->context.client_formats = xzalloc(rdpsnd->context.num_client_formats * sizeof(rdpsndFormat)); + for (i = 0; i < rdpsnd->context.num_client_formats; i++) + { + if (stream_get_left(s) < 18) + { + xfree(rdpsnd->context.client_formats); + rdpsnd->context.client_formats = NULL; + return false; + } + + stream_read_uint16(s, rdpsnd->context.client_formats[i].wFormatTag); + stream_read_uint16(s, rdpsnd->context.client_formats[i].nChannels); + stream_read_uint32(s, rdpsnd->context.client_formats[i].nSamplesPerSec); + stream_seek_uint32(s); /* nAvgBytesPerSec */ + stream_read_uint16(s, rdpsnd->context.client_formats[i].nBlockAlign); + stream_read_uint16(s, rdpsnd->context.client_formats[i].wBitsPerSample); + stream_read_uint16(s, rdpsnd->context.client_formats[i].cbSize); + if (rdpsnd->context.client_formats[i].cbSize > 0) + { + stream_seek(s, rdpsnd->context.client_formats[i].cbSize); + } + } + } + + return true; +} + + + +static void* rdpsnd_server_thread_func(void* arg) +{ + void* fd; + STREAM* s; + void* buffer; + uint8 msgType; + uint16 BodySize; + uint32 bytes_returned = 0; + rdpsnd_server* rdpsnd = (rdpsnd_server*) arg; + freerdp_thread* thread = rdpsnd->rdpsnd_channel_thread; + + if (WTSVirtualChannelQuery(rdpsnd->rdpsnd_channel, WTSVirtualFileHandle, &buffer, &bytes_returned) == true) + { + fd = *((void**)buffer); + WTSFreeMemory(buffer); + thread->signals[thread->num_signals++] = wait_obj_new_with_fd(fd); + } + + s = stream_new(4096); + + rdpsnd_server_send_formats(rdpsnd, s); + + while (1) + { + freerdp_thread_wait(thread); + if (freerdp_thread_is_stopped(thread)) + break; + + stream_set_pos(s, 0); + if (WTSVirtualChannelRead(rdpsnd->rdpsnd_channel, 0, stream_get_head(s), + stream_get_size(s), &bytes_returned) == false) + { + if (bytes_returned == 0) + break; + stream_check_size(s, bytes_returned); + if (WTSVirtualChannelRead(rdpsnd->rdpsnd_channel, 0, stream_get_head(s), + stream_get_size(s), &bytes_returned) == false) + break; + } + + stream_read_uint8(s, msgType); + stream_seek_uint8(s); /* bPad */ + stream_read_uint16(s, BodySize); + if (BodySize + 4 > bytes_returned) + continue; + + switch (msgType) { + case SNDC_FORMATS: + if (rdpsnd_server_recv_formats(rdpsnd, s)) + { + IFCALL(rdpsnd->context.Activated, &rdpsnd->context); + } + break; + default: + break; + } + } + + stream_free(s); + freerdp_thread_quit(thread); + + return 0; +} + +static boolean rdpsnd_server_initialize(rdpsnd_server_context* context) +{ + rdpsnd_server* rdpsnd = (rdpsnd_server*) context; + + rdpsnd->rdpsnd_channel = WTSVirtualChannelOpenEx(context->vcm, "rdpsnd", 0); + if (rdpsnd->rdpsnd_channel != NULL) + { + rdpsnd->rdpsnd_pdu = stream_new(4096); + rdpsnd->rdpsnd_channel_thread = freerdp_thread_new(); + freerdp_thread_start(rdpsnd->rdpsnd_channel_thread, rdpsnd_server_thread_func, rdpsnd); + + return true; + } + else + { + return false; + } +} + +static void rdpsnd_server_select_format(rdpsnd_server_context* context, int client_format_index) +{ + int bs; + int out_buffer_size; + rdpsndFormat *format; + rdpsnd_server* rdpsnd = (rdpsnd_server*) context; + + if (client_format_index < 0 || client_format_index >= context->num_client_formats) + { + printf("rdpsnd_server_select_format: index %d is not correct.\n", client_format_index); + return; + } + + rdpsnd->src_bytes_per_sample = context->src_format.wBitsPerSample / 8; + rdpsnd->src_bytes_per_frame = rdpsnd->src_bytes_per_sample * context->src_format.nChannels; + + context->selected_client_format = client_format_index; + format = &context->client_formats[client_format_index]; + if (format->wFormatTag == 0x11) + { + bs = (format->nBlockAlign - 4 * format->nChannels) * 4; + rdpsnd->out_frames = (format->nBlockAlign * 4 * format->nChannels * 2 / bs + 1) * bs / (format->nChannels * 2); + } + else if (format->wFormatTag == 0x02) + { + bs = (format->nBlockAlign - 7 * format->nChannels) * 2 / format->nChannels + 2; + rdpsnd->out_frames = bs * 4; + } + else + { + rdpsnd->out_frames = 0x4000 / rdpsnd->src_bytes_per_frame; + } + if (format->nSamplesPerSec != context->src_format.nSamplesPerSec) + { + rdpsnd->out_frames = (rdpsnd->out_frames * context->src_format.nSamplesPerSec + format->nSamplesPerSec - 100) / format->nSamplesPerSec; + } + rdpsnd->out_pending_frames = 0; + + out_buffer_size = rdpsnd->out_frames * rdpsnd->src_bytes_per_frame; + if (rdpsnd->out_buffer_size < out_buffer_size) + { + rdpsnd->out_buffer = xrealloc(rdpsnd->out_buffer, out_buffer_size); + rdpsnd->out_buffer_size = out_buffer_size; + } + + freerdp_dsp_context_reset_adpcm(rdpsnd->dsp_context); +} + +static boolean rdpsnd_server_send_audio_pdu(rdpsnd_server* rdpsnd) +{ + STREAM* s = rdpsnd->rdpsnd_pdu; + rdpsndFormat* format; + int tbytes_per_frame; + uint8* src; + int size; + int frames; + int fill_size; + boolean r; + + format = &rdpsnd->context.client_formats[rdpsnd->context.selected_client_format]; + tbytes_per_frame = format->nChannels * rdpsnd->src_bytes_per_sample; + + if (format->nSamplesPerSec == rdpsnd->context.src_format.nSamplesPerSec && format->nChannels == rdpsnd->context.src_format.nChannels) + { + src = rdpsnd->out_buffer; + frames = rdpsnd->out_pending_frames; + } + else + { + rdpsnd->dsp_context->resample(rdpsnd->dsp_context, rdpsnd->out_buffer, rdpsnd->src_bytes_per_sample, + rdpsnd->context.src_format.nChannels, rdpsnd->context.src_format.nSamplesPerSec, rdpsnd->out_pending_frames, + format->nChannels, format->nSamplesPerSec); + frames = rdpsnd->dsp_context->resampled_frames; + src = rdpsnd->dsp_context->resampled_buffer; + } + size = frames * tbytes_per_frame; + + if (format->wFormatTag == 0x11) + { + rdpsnd->dsp_context->encode_ima_adpcm(rdpsnd->dsp_context, + src, size, format->nChannels, format->nBlockAlign); + src = rdpsnd->dsp_context->adpcm_buffer; + size = rdpsnd->dsp_context->adpcm_size; + } + else if (format->wFormatTag == 0x02) + { + rdpsnd->dsp_context->encode_ms_adpcm(rdpsnd->dsp_context, + src, size, format->nChannels, format->nBlockAlign); + src = rdpsnd->dsp_context->adpcm_buffer; + size = rdpsnd->dsp_context->adpcm_size; + } + + rdpsnd->context.block_no = (rdpsnd->context.block_no + 1) % 256; + + /* Fill to nBlockAlign for the last audio packet */ + if ((format->wFormatTag == 0x11 || format->wFormatTag == 0x02) && + rdpsnd->out_pending_frames < rdpsnd->out_frames && (size % format->nBlockAlign) != 0) + fill_size = format->nBlockAlign - (size % format->nBlockAlign); + else + fill_size = 0; + + /* WaveInfo PDU */ + stream_set_pos(s, 0); + stream_write_uint8(s, SNDC_WAVE); /* msgType */ + stream_write_uint8(s, 0); /* bPad */ + stream_write_uint16(s, size + fill_size + 8); /* BodySize */ + + stream_write_uint16(s, 0); /* wTimeStamp */ + stream_write_uint16(s, rdpsnd->context.selected_client_format); /* wFormatNo */ + stream_write_uint8(s, rdpsnd->context.block_no); /* cBlockNo */ + stream_seek(s, 3); /* bPad */ + stream_write(s, src, 4); + + WTSVirtualChannelWrite(rdpsnd->rdpsnd_channel, stream_get_head(s), stream_get_length(s), NULL); + stream_set_pos(s, 0); + + /* Wave PDU */ + stream_check_size(s, size + fill_size); + stream_write_uint32(s, 0); /* bPad */ + stream_write(s, src + 4, size - 4); + if (fill_size > 0) + stream_write_zero(s, fill_size); + + r = WTSVirtualChannelWrite(rdpsnd->rdpsnd_channel, stream_get_head(s), stream_get_length(s), NULL); + stream_set_pos(s, 0); + + rdpsnd->out_pending_frames = 0; + + return r; +} + +static boolean rdpsnd_server_send_samples(rdpsnd_server_context* context, const void* buf, int nframes) +{ + int cframes; + int cframesize; + rdpsnd_server* rdpsnd = (rdpsnd_server*) context; + + if (rdpsnd->context.selected_client_format < 0) + return false; + + while (nframes > 0) + { + cframes = MIN(nframes, rdpsnd->out_frames - rdpsnd->out_pending_frames); + cframesize = cframes * rdpsnd->src_bytes_per_frame; + memcpy(rdpsnd->out_buffer + (rdpsnd->out_pending_frames * rdpsnd->src_bytes_per_frame), + buf, cframesize); + buf = (uint8*)buf + cframesize; + nframes -= cframes; + rdpsnd->out_pending_frames += cframes; + + if (rdpsnd->out_pending_frames >= rdpsnd->out_frames) + { + if (!rdpsnd_server_send_audio_pdu(rdpsnd)) + return false; + } + } + + return true; +} + +static boolean rdpsnd_server_close(rdpsnd_server_context* context) +{ + rdpsnd_server* rdpsnd = (rdpsnd_server*) context; + STREAM* s = rdpsnd->rdpsnd_pdu; + + if (rdpsnd->context.selected_client_format < 0) + return false; + + if (rdpsnd->out_pending_frames > 0) + { + if (!rdpsnd_server_send_audio_pdu(rdpsnd)) + return false; + } + + rdpsnd->context.selected_client_format = -1; + + RDPSND_PDU_INIT(s, SNDC_CLOSE); + RDPSND_PDU_FINISH(s); +} + +rdpsnd_server_context* rdpsnd_server_context_new(WTSVirtualChannelManager* vcm) +{ + rdpsnd_server* rdpsnd; + + rdpsnd = xnew(rdpsnd_server); + rdpsnd->context.vcm = vcm; + rdpsnd->context.selected_client_format = -1; + rdpsnd->context.Initialize = rdpsnd_server_initialize; + rdpsnd->context.SelectFormat = rdpsnd_server_select_format; + rdpsnd->context.SendSamples = rdpsnd_server_send_samples; + rdpsnd->context.Close = rdpsnd_server_close; + + rdpsnd->dsp_context = freerdp_dsp_context_new(); + + return (rdpsnd_server_context*) rdpsnd; +} + +void rdpsnd_server_context_free(rdpsnd_server_context* context) +{ + rdpsnd_server* rdpsnd = (rdpsnd_server*) context; + + if (rdpsnd->rdpsnd_channel_thread) + { + freerdp_thread_stop(rdpsnd->rdpsnd_channel_thread); + freerdp_thread_free(rdpsnd->rdpsnd_channel_thread); + } + if (rdpsnd->rdpsnd_channel) + WTSVirtualChannelClose(rdpsnd->rdpsnd_channel); + if (rdpsnd->rdpsnd_pdu) + stream_free(rdpsnd->rdpsnd_pdu); + if (rdpsnd->out_buffer) + xfree(rdpsnd->out_buffer); + if (rdpsnd->dsp_context) + freerdp_dsp_context_free(rdpsnd->dsp_context); + if (rdpsnd->context.client_formats) + xfree(rdpsnd->context.client_formats); + xfree(rdpsnd); +} diff --git a/server/test/tfreerdp.c b/server/test/tfreerdp.c index aef1206e4..b02c1ecaf 100644 --- a/server/test/tfreerdp.c +++ b/server/test/tfreerdp.c @@ -461,7 +461,7 @@ boolean tf_peer_post_connect(freerdp_peer* client) * The server may start sending graphics output and receiving keyboard/mouse input after this * callback returns. */ - printf("Client %s is activated (osMajorType %d osMinorType %d)", client->hostname, + printf("Client %s is activated (osMajorType %d osMinorType %d)", client->local ? "(local)" : client->hostname, client->settings->os_major_type, client->settings->os_minor_type); if (client->settings->autologon) { @@ -613,7 +613,7 @@ static void* test_peer_mainloop(void* arg) client->Initialize(client); context = (testPeerContext*) client->context; - printf("We've got a client %s\n", client->hostname); + printf("We've got a client %s\n", client->local ? "(local)" : client->hostname); while (1) { @@ -661,7 +661,7 @@ static void* test_peer_mainloop(void* arg) break; } - printf("Client %s disconnected.\n", client->hostname); + printf("Client %s disconnected.\n", client->local ? "(local)" : client->hostname); client->Disconnect(client); freerdp_peer_context_free(client); @@ -756,7 +756,8 @@ int main(int argc, char* argv[]) test_dump_rfx_realtime = false; /* Open the server socket and start listening. */ - if (instance->Open(instance, NULL, 3389)) + if (instance->Open(instance, NULL, 3389) && + instance->OpenLocal(instance, "/tmp/tfreerdp-server.0")) { /* Entering the server main loop. In a real server the listener can be run in its own thread. */ test_server_mainloop(instance);