diff --git a/include/freerdp/channels/wtsvc.h b/include/freerdp/channels/wtsvc.h new file mode 100644 index 000000000..37565076b --- /dev/null +++ b/include/freerdp/channels/wtsvc.h @@ -0,0 +1,128 @@ +/** + * FreeRDP: A Remote Desktop Protocol client. + * Server Virtual Channel Interface + * + * Copyright 2011-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. + */ + +/** + * The server-side virtual channel API follows the Microsoft Remote Desktop + * Services API functions WTSVirtualChannel* defined in: + * http://msdn.microsoft.com/en-us/library/windows/desktop/aa383464.aspx + * + * Difference between the MS API are documented in this header. All functions + * are implemented in and integrated with libfreerdp-channels. + * + * Unlike MS API, all functions except WTSVirtualChannelOpenEx in this + * implementation are thread-safe. + */ + +#ifndef __FREERDP_WTSVC_H +#define __FREERDP_WTSVC_H + +#include +#include + +typedef struct WTSVirtualChannelManager WTSVirtualChannelManager; + +#define WTS_CHANNEL_OPTION_DYNAMIC 0x00000001 + +typedef enum _WTS_VIRTUAL_CLASS +{ + WTSVirtualClientData, + WTSVirtualFileHandle +} WTS_VIRTUAL_CLASS; + +/** + * WTSVirtualChannelManager functions are FreeRDP extensions to the API. + */ +FREERDP_API WTSVirtualChannelManager* WTSCreateVirtualChannelManager(freerdp_peer* client); +FREERDP_API void WTSDestroyVirtualChannelManager(WTSVirtualChannelManager* vcm); +FREERDP_API void WTSVirtualChannelManagerGetFileDescriptor(WTSVirtualChannelManager* vcm, + void** fds, int* fds_count); +FREERDP_API boolean WTSVirtualChannelManagerCheckFileDescriptor(WTSVirtualChannelManager* vcm); + +/** + * Opens a static or dynamic virtual channel and return the handle. If the + * operation fails, a NULL handle is returned. + * + * The original MS API has 'DWORD SessionId' as the first argument, while we + * use our WTSVirtualChannelManager object instead. + * + * This functions should be called only from the main thread. + */ +FREERDP_API void* WTSVirtualChannelOpenEx( + /* __in */ WTSVirtualChannelManager* vcm, + /* __in */ const char* pVirtualName, + /* __in */ uint32 flags); + +/** + * Returns information about a specified virtual channel. + * + * Servers use this function to gain access to a virtual channel file handle + * that can be used for asynchronous I/O. + */ +FREERDP_API boolean WTSVirtualChannelQuery( + /* __in */ void* hChannelHandle, + /* __in */ WTS_VIRTUAL_CLASS WtsVirtualClass, + /* __out */ void** ppBuffer, + /* __out */ uint32* pBytesReturned); + +/** + * Frees memory allocated by WTSVirtualChannelQuery + */ +FREERDP_API void WTSFreeMemory( + /* __in */ void* pMemory); + +/** + * Reads data from the server end of a virtual channel. + * + * FreeRDP behavior: + * + * This function will always return a complete channel data packet, i.e. chunks + * are already assembled. If BufferSize argument is smaller than the packet + * size, it will set the desired size in pBytesRead and return false. The + * caller should allocate a large enough buffer and call this function again. + * Returning false with pBytesRead set to zero indicates an error has occurred. + * If no pending packet to be read, it will set pBytesRead to zero and return + * true. + * + * TimeOut is not supported, and this function will always return immediately. + * The caller should use the file handle returned by WTSVirtualChannelQuery to + * determine whether a packet has arrived. + */ +FREERDP_API boolean WTSVirtualChannelRead( + /* __in */ void* hChannelHandle, + /* __in */ uint32 TimeOut, + /* __out */ uint8* Buffer, + /* __in */ uint32 BufferSize, + /* __out */ uint32* pBytesRead); + +/** + * Writes data to the server end of a virtual channel. + */ +FREERDP_API boolean WTSVirtualChannelWrite( + /* __in */ void* hChannelHandle, + /* __in */ uint8* Buffer, + /* __in */ uint32 Length, + /* __out */ uint32* pBytesWritten); + +/** + * Closes an open virtual channel handle. + */ +FREERDP_API boolean WTSVirtualChannelClose( + /* __in */ void* hChannelHandle); + +#endif /* __FREERDP_WTSVC_H */ diff --git a/include/freerdp/peer.h b/include/freerdp/peer.h index 21d58bc45..bc8da5240 100644 --- a/include/freerdp/peer.h +++ b/include/freerdp/peer.h @@ -36,6 +36,9 @@ typedef void (*psPeerDisconnect)(freerdp_peer* client); typedef boolean (*psPeerPostConnect)(freerdp_peer* client); typedef boolean (*psPeerActivate)(freerdp_peer* client); +typedef int (*psPeerSendChannelData)(freerdp_peer* client, int channelId, uint8* data, int size); +typedef int (*psPeerReceiveChannelData)(freerdp_peer* client, int channelId, uint8* data, int size, int flags, int total_size); + struct rdp_freerdp_peer { rdpContext* context; @@ -57,6 +60,9 @@ struct rdp_freerdp_peer psPeerPostConnect PostConnect; psPeerActivate Activate; + + psPeerSendChannelData SendChannelData; + psPeerReceiveChannelData ReceiveChannelData; }; FREERDP_API void freerdp_peer_context_new(freerdp_peer* client); diff --git a/include/freerdp/utils/wait_obj.h b/include/freerdp/utils/wait_obj.h index cb75edb2c..a202a25df 100644 --- a/include/freerdp/utils/wait_obj.h +++ b/include/freerdp/utils/wait_obj.h @@ -23,6 +23,7 @@ #include FREERDP_API struct wait_obj* wait_obj_new(void); +FREERDP_API struct wait_obj* wait_obj_new_with_fd(void* fd); FREERDP_API void wait_obj_free(struct wait_obj* obj); FREERDP_API int wait_obj_is_set(struct wait_obj* obj); FREERDP_API void wait_obj_set(struct wait_obj* obj); diff --git a/libfreerdp-channels/CMakeLists.txt b/libfreerdp-channels/CMakeLists.txt index ba0108717..55a139a94 100644 --- a/libfreerdp-channels/CMakeLists.txt +++ b/libfreerdp-channels/CMakeLists.txt @@ -19,7 +19,9 @@ set(FREERDP_CHANNELS_SRCS libchannels.c - libchannels.h) + libchannels.h + wtsvc.c + wtsvc.h) add_library(freerdp-channels ${FREERDP_CHANNELS_SRCS}) diff --git a/libfreerdp-channels/wtsvc.c b/libfreerdp-channels/wtsvc.c new file mode 100644 index 000000000..03a91b4b7 --- /dev/null +++ b/libfreerdp-channels/wtsvc.c @@ -0,0 +1,372 @@ +/** + * FreeRDP: A Remote Desktop Protocol client. + * Server Virtual Channel Interface + * + * Copyright 2011-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 "config.h" +#include +#include +#include +#include +#include + +#include "wtsvc.h" + +typedef struct wts_data_item +{ + uint16 channel_id; + uint8* buffer; + uint32 length; +} wts_data_item; + +static void wts_data_item_free(wts_data_item* item) +{ + xfree(item->buffer); + xfree(item); +} + +static void WTSProcessChannelData(rdpPeerChannel* channel, int channelId, uint8* data, int size, int flags, int total_size) +{ + wts_data_item* item; + + if (flags & CHANNEL_FLAG_FIRST) + { + stream_set_pos(channel->receive_data, 0); + } + + stream_check_size(channel->receive_data, size); + stream_write(channel->receive_data, data, size); + + if (flags & CHANNEL_FLAG_LAST) + { + if (stream_get_length(channel->receive_data) != total_size) + { + printf("WTSProcessChannelData: read error\n"); + } + if (channel->channel_type == RDP_PEER_CHANNEL_TYPE_DVC) + { + /* TODO: Receive DVC channel data */ + } + else + { + item = xnew(wts_data_item); + item->length = stream_get_length(channel->receive_data); + item->buffer = xmalloc(item->length); + memcpy(item->buffer, stream_get_head(channel->receive_data), item->length); + + freerdp_mutex_lock(channel->mutex); + list_enqueue(channel->receive_queue, item); + freerdp_mutex_unlock(channel->mutex); + + wait_obj_set(channel->receive_event); + } + stream_set_pos(channel->receive_data, 0); + } +} + +static int WTSReceiveChannelData(freerdp_peer* client, int channelId, uint8* data, int size, int flags, int total_size) +{ + int i; + boolean result = false; + rdpPeerChannel* channel; + + for (i = 0; i < client->settings->num_channels; i++) + { + if (client->settings->channels[i].channel_id == channelId) + break; + } + if (i < client->settings->num_channels) + { + channel = (rdpPeerChannel*) client->settings->channels[i].handle; + if (channel != NULL) + { + WTSProcessChannelData(channel, channelId, data, size, flags, total_size); + result = true; + } + } + + return result; +} + +WTSVirtualChannelManager* WTSCreateVirtualChannelManager(freerdp_peer* client) +{ + WTSVirtualChannelManager* vcm; + + vcm = xnew(WTSVirtualChannelManager); + if (vcm != NULL) + { + vcm->client = client; + vcm->send_event = wait_obj_new(); + vcm->send_queue = list_new(); + vcm->mutex = freerdp_mutex_new(); + + client->ReceiveChannelData = WTSReceiveChannelData; + } + + return vcm; +} + +void WTSDestroyVirtualChannelManager(WTSVirtualChannelManager* vcm) +{ + wts_data_item* item; + + if (vcm != NULL) + { + if (vcm->drdynvc_channel != NULL) + { + WTSVirtualChannelClose(vcm->drdynvc_channel); + vcm->drdynvc_channel = NULL; + } + + wait_obj_free(vcm->send_event); + while ((item = (wts_data_item*) list_dequeue(vcm->send_queue)) != NULL) + { + wts_data_item_free(item); + } + list_free(vcm->send_queue); + freerdp_mutex_free(vcm->mutex); + xfree(vcm); + } +} + +void WTSVirtualChannelManagerGetFileDescriptor(WTSVirtualChannelManager* vcm, + void** fds, int* fds_count) +{ + wait_obj_get_fds(vcm->send_event, fds, fds_count); +} + +boolean WTSVirtualChannelManagerCheckFileDescriptor(WTSVirtualChannelManager* vcm) +{ + boolean result = true; + wts_data_item* item; + + wait_obj_clear(vcm->send_event); + + freerdp_mutex_lock(vcm->mutex); + while ((item = (wts_data_item*) list_dequeue(vcm->send_queue)) != NULL) + { + if (vcm->client->SendChannelData(vcm->client, item->channel_id, item->buffer, item->length) == false) + { + result = false; + } + wts_data_item_free(item); + if (result == false) + break; + } + freerdp_mutex_unlock(vcm->mutex); + + return result; +} + +void* WTSVirtualChannelOpenEx( + /* __in */ WTSVirtualChannelManager* vcm, + /* __in */ const char* pVirtualName, + /* __in */ uint32 flags) +{ + int i; + int len; + rdpPeerChannel* channel; + const char* channel_name; + freerdp_peer* client = vcm->client; + + channel_name = ((flags & WTS_CHANNEL_OPTION_DYNAMIC) != 0 ? "drdynvc" : pVirtualName); + + len = strlen(channel_name); + if (len > 8) + return NULL; + + for (i = 0; i < client->settings->num_channels; i++) + { + if (client->settings->channels[i].joined && + strncmp(client->settings->channels[i].name, channel_name, len) == 0) + { + break; + } + } + if (i >= client->settings->num_channels) + return NULL; + + channel = (rdpPeerChannel*) client->settings->channels[i].handle; + if (channel == NULL) + { + channel = xnew(rdpPeerChannel); + channel->vcm = vcm; + channel->client = client; + channel->channel_id = client->settings->channels[i].channel_id; + channel->index = i; + channel->receive_data = stream_new(client->settings->vc_chunk_size); + if ((flags & WTS_CHANNEL_OPTION_DYNAMIC) != 0) + { + channel->channel_type = RDP_PEER_CHANNEL_TYPE_DVC; + vcm->drdynvc_channel = channel; + } + else + { + channel->channel_type = RDP_PEER_CHANNEL_TYPE_SVC; + channel->receive_event = wait_obj_new(); + channel->receive_queue = list_new(); + channel->mutex = freerdp_mutex_new(); + } + + client->settings->channels[i].handle = channel; + } + if (channel->channel_type == RDP_PEER_CHANNEL_TYPE_DVC) + { + /* TODO: do DVC channel initialization here using pVirtualName */ + /* A sub-channel should be created and returned, instead of using the main drdynvc channel */ + /* Set channel->index to num_channels */ + } + + return channel; +} + +boolean WTSVirtualChannelQuery( + /* __in */ void* hChannelHandle, + /* __in */ WTS_VIRTUAL_CLASS WtsVirtualClass, + /* __out */ void** ppBuffer, + /* __out */ uint32* pBytesReturned) +{ + void* fds[10]; + int fds_count = 0; + boolean result = false; + rdpPeerChannel* channel = (rdpPeerChannel*) hChannelHandle; + + switch (WtsVirtualClass) + { + case WTSVirtualFileHandle: + wait_obj_get_fds(channel->receive_event, fds, &fds_count); + *ppBuffer = xmalloc(sizeof(void*)); + memcpy(*ppBuffer, &fds[0], sizeof(void*)); + *pBytesReturned = sizeof(void*); + result = true; + break; + + default: + break; + } + return result; +} + +void WTSFreeMemory( + /* __in */ void* pMemory) +{ + xfree(pMemory); +} + +boolean WTSVirtualChannelRead( + /* __in */ void* hChannelHandle, + /* __in */ uint32 TimeOut, + /* __out */ uint8* Buffer, + /* __in */ uint32 BufferSize, + /* __out */ uint32* pBytesRead) +{ + wts_data_item* item; + rdpPeerChannel* channel = (rdpPeerChannel*) hChannelHandle; + + item = (wts_data_item*) list_peek(channel->receive_queue); + if (item == NULL) + { + wait_obj_clear(channel->receive_event); + *pBytesRead = 0; + return true; + } + *pBytesRead = item->length; + if (item->length > BufferSize) + return false; + + /* remove the first element (same as what we just peek) */ + freerdp_mutex_lock(channel->mutex); + list_dequeue(channel->receive_queue); + if (channel->receive_queue->head == NULL) + wait_obj_clear(channel->receive_event); + freerdp_mutex_unlock(channel->mutex); + + memcpy(Buffer, item->buffer, item->length); + + return true; +} + +boolean WTSVirtualChannelWrite( + /* __in */ void* hChannelHandle, + /* __in */ uint8* Buffer, + /* __in */ uint32 Length, + /* __out */ uint32* pBytesWritten) +{ + uint32 written = 0; + wts_data_item* item; + boolean result = false; + rdpPeerChannel* channel = (rdpPeerChannel*) hChannelHandle; + WTSVirtualChannelManager* vcm = channel->vcm; + + if (channel == NULL) + return false; + + if (channel->channel_type == RDP_PEER_CHANNEL_TYPE_SVC) + { + item = xnew(wts_data_item); + item->channel_id = channel->channel_id; + item->buffer = xmalloc(Length); + item->length = Length; + memcpy(item->buffer, Buffer, Length); + + freerdp_mutex_lock(vcm->mutex); + list_enqueue(vcm->send_queue, item); + freerdp_mutex_unlock(vcm->mutex); + + wait_obj_set(vcm->send_event); + + written = Length; + result = true; + } + else + { + /* TODO: Send to DVC channel */ + } + + if (pBytesWritten != NULL) + *pBytesWritten = written; + return result; +} + +boolean WTSVirtualChannelClose( + /* __in */ void* hChannelHandle) +{ + wts_data_item* item; + rdpPeerChannel* channel = (rdpPeerChannel*) hChannelHandle; + + if (channel != NULL) + { + if (channel->index < channel->client->settings->num_channels) + channel->client->settings->channels[channel->index].handle = NULL; + stream_free(channel->receive_data); + if (channel->receive_event) + wait_obj_free(channel->receive_event); + if (channel->receive_queue) + { + while ((item = (wts_data_item*) list_dequeue(channel->receive_queue)) != NULL) + { + wts_data_item_free(item); + } + list_free(channel->receive_queue); + } + if (channel->mutex) + freerdp_mutex_free(channel->mutex); + xfree(channel); + } + + return true; +} diff --git a/libfreerdp-channels/wtsvc.h b/libfreerdp-channels/wtsvc.h new file mode 100644 index 000000000..11110d490 --- /dev/null +++ b/libfreerdp-channels/wtsvc.h @@ -0,0 +1,61 @@ +/** + * FreeRDP: A Remote Desktop Protocol client. + * Server Virtual Channel Interface + * + * Copyright 2011-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 __WTSVC_H +#define __WTSVC_H + +#include +#include +#include +#include +#include +#include + +enum +{ + RDP_PEER_CHANNEL_TYPE_SVC = 0, + RDP_PEER_CHANNEL_TYPE_DVC = 1, + RDP_PEER_CHANNEL_TYPE_DVC_SUB = 2 +}; + +typedef struct rdp_peer_channel +{ + WTSVirtualChannelManager* vcm; + freerdp_peer* client; + uint16 channel_id; + uint16 channel_type; + uint16 index; + + STREAM* receive_data; + struct wait_obj* receive_event; + LIST* receive_queue; + freerdp_mutex mutex; +} rdpPeerChannel; + +struct WTSVirtualChannelManager +{ + freerdp_peer* client; + struct wait_obj* send_event; + LIST* send_queue; + freerdp_mutex mutex; + + rdpPeerChannel* drdynvc_channel; +}; + +#endif /* __WTSVC_H */ diff --git a/libfreerdp-core/channel.c b/libfreerdp-core/channel.c index 8f137910a..e92a0a80c 100644 --- a/libfreerdp-core/channel.c +++ b/libfreerdp-core/channel.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -28,7 +29,7 @@ #include "rdp.h" #include "channel.h" -boolean freerdp_channel_send(freerdp* instance, uint16 channel_id, uint8* data, int size) +boolean freerdp_channel_send(rdpRdp* rdp, uint16 channel_id, uint8* data, int size) { STREAM* s; uint32 flags; @@ -36,11 +37,11 @@ boolean freerdp_channel_send(freerdp* instance, uint16 channel_id, uint8* data, int chunk_size; rdpChannel* channel = NULL; - for (i = 0; i < instance->settings->num_channels; i++) + for (i = 0; i < rdp->settings->num_channels; i++) { - if (instance->settings->channels[i].channel_id == channel_id) + if (rdp->settings->channels[i].channel_id == channel_id) { - channel = &instance->settings->channels[i]; + channel = &rdp->settings->channels[i]; break; } } @@ -55,11 +56,11 @@ boolean freerdp_channel_send(freerdp* instance, uint16 channel_id, uint8* data, left = size; while (left > 0) { - s = rdp_send_stream_init(instance->context->rdp); + s = rdp_send_stream_init(rdp); - if (left > (int) instance->settings->vc_chunk_size) + if (left > (int) rdp->settings->vc_chunk_size) { - chunk_size = instance->settings->vc_chunk_size; + chunk_size = rdp->settings->vc_chunk_size; } else { @@ -76,7 +77,7 @@ boolean freerdp_channel_send(freerdp* instance, uint16 channel_id, uint8* data, stream_check_size(s, chunk_size); stream_write(s, data, chunk_size); - rdp_send(instance->context->rdp, s, channel_id); + rdp_send(rdp, s, channel_id); data += chunk_size; left -= chunk_size; @@ -100,3 +101,16 @@ void freerdp_channel_process(freerdp* instance, STREAM* s, uint16 channel_id) channel_id, stream_get_tail(s), chunk_length, flags, length); } +void freerdp_channel_peer_process(freerdp_peer* client, STREAM* s, uint16 channel_id) +{ + uint32 length; + uint32 flags; + int chunk_length; + + stream_read_uint32(s, length); + stream_read_uint32(s, flags); + chunk_length = stream_get_left(s); + + IFCALL(client->ReceiveChannelData, client, + channel_id, stream_get_tail(s), chunk_length, flags, length); +} diff --git a/libfreerdp-core/channel.h b/libfreerdp-core/channel.h index afb501dee..3632247c4 100644 --- a/libfreerdp-core/channel.h +++ b/libfreerdp-core/channel.h @@ -20,7 +20,8 @@ #ifndef __CHANNEL_H #define __CHANNEL_H -boolean freerdp_channel_send(freerdp* instance, uint16 channel_id, uint8* data, int size); +boolean freerdp_channel_send(rdpRdp* rdp, uint16 channel_id, uint8* data, int size); void freerdp_channel_process(freerdp* instance, STREAM* s, uint16 channel_id); +void freerdp_channel_peer_process(freerdp_peer* client, STREAM* s, uint16 channel_id); #endif /* __CHANNEL_H */ diff --git a/libfreerdp-core/peer.c b/libfreerdp-core/peer.c index 3841f9da3..4d381dfbb 100644 --- a/libfreerdp-core/peer.c +++ b/libfreerdp-core/peer.c @@ -126,7 +126,7 @@ static boolean peer_recv_tpkt_pdu(freerdp_peer* client, STREAM* s) if (channelId != MCS_GLOBAL_CHANNEL_ID) { - /* TODO: process channel data from client */ + freerdp_channel_peer_process(client, s, channelId); } else { @@ -242,6 +242,11 @@ static void freerdp_peer_disconnect(freerdp_peer* client) transport_disconnect(client->context->rdp->transport); } +static int freerdp_peer_send_channel_data(freerdp_peer* client, int channelId, uint8* data, int size) +{ + return rdp_send_channel_data(client->context->rdp, channelId, data, size); +} + void freerdp_peer_context_new(freerdp_peer* client) { rdpRdp* rdp; @@ -288,6 +293,7 @@ freerdp_peer* freerdp_peer_new(int sockfd) client->GetFileDescriptor = freerdp_peer_get_fds; client->CheckFileDescriptor = freerdp_peer_check_fds; client->Disconnect = freerdp_peer_disconnect; + client->SendChannelData = freerdp_peer_send_channel_data; } return client; diff --git a/libfreerdp-core/rdp.c b/libfreerdp-core/rdp.c index 7ba54e3aa..3b1d7c859 100644 --- a/libfreerdp-core/rdp.c +++ b/libfreerdp-core/rdp.c @@ -775,7 +775,7 @@ static boolean rdp_recv_callback(rdpTransport* transport, STREAM* s, void* extra int rdp_send_channel_data(rdpRdp* rdp, int channel_id, uint8* data, int size) { - return freerdp_channel_send(rdp->instance, channel_id, data, size); + return freerdp_channel_send(rdp, channel_id, data, size); } /** diff --git a/libfreerdp-utils/wait_obj.c b/libfreerdp-utils/wait_obj.c index 34273380b..a2ba5b921 100644 --- a/libfreerdp-utils/wait_obj.c +++ b/libfreerdp-utils/wait_obj.c @@ -41,6 +41,7 @@ struct wait_obj #else int pipe_fd[2]; #endif + int attached; }; struct wait_obj* @@ -50,6 +51,7 @@ wait_obj_new(void) obj = xnew(struct wait_obj); + obj->attached = 0; #ifdef _WIN32 obj->event = CreateEvent(NULL, TRUE, FALSE, NULL); #else @@ -66,30 +68,50 @@ wait_obj_new(void) return obj; } +struct wait_obj* wait_obj_new_with_fd(void* fd) +{ + struct wait_obj* obj; + + obj = xnew(struct wait_obj); + + obj->attached = 1; +#ifdef _WIN32 + obj->event = fd; +#else + obj->pipe_fd[0] = (int)(long)fd; + obj->pipe_fd[1] = -1; +#endif + + return obj; +} + void wait_obj_free(struct wait_obj* obj) { if (obj) { + if (obj->attached == 0) + { #ifdef _WIN32 - if (obj->event) - { - CloseHandle(obj->event); - obj->event = NULL; - } + if (obj->event) + { + CloseHandle(obj->event); + obj->event = NULL; + } #else - if (obj->pipe_fd[0] != -1) - { - close(obj->pipe_fd[0]); - obj->pipe_fd[0] = -1; - } - if (obj->pipe_fd[1] != -1) - { - close(obj->pipe_fd[1]); - obj->pipe_fd[1] = -1; - } + if (obj->pipe_fd[0] != -1) + { + close(obj->pipe_fd[0]); + obj->pipe_fd[0] = -1; + } + if (obj->pipe_fd[1] != -1) + { + close(obj->pipe_fd[1]); + obj->pipe_fd[1] = -1; + } #endif + } xfree(obj); } diff --git a/server/test/CMakeLists.txt b/server/test/CMakeLists.txt index 842b8617c..eb930231e 100644 --- a/server/test/CMakeLists.txt +++ b/server/test/CMakeLists.txt @@ -23,3 +23,4 @@ add_executable(tfreerdp-server target_link_libraries(tfreerdp-server freerdp-core) target_link_libraries(tfreerdp-server freerdp-utils) target_link_libraries(tfreerdp-server freerdp-codec) +target_link_libraries(tfreerdp-server freerdp-channels) diff --git a/server/test/tfreerdp.c b/server/test/tfreerdp.c index ebde3fe8b..74761cf53 100644 --- a/server/test/tfreerdp.c +++ b/server/test/tfreerdp.c @@ -31,6 +31,7 @@ #include #include #include +#include static char* test_pcap_file = NULL; static boolean test_dump_rfx_realtime = true; @@ -54,6 +55,9 @@ struct test_peer_context int icon_x; int icon_y; boolean activated; + WTSVirtualChannelManager* vcm; + void* debug_channel; + freerdp_thread* debug_channel_thread; }; typedef struct test_peer_context testPeerContext; @@ -69,16 +73,28 @@ void test_peer_context_new(freerdp_peer* client, testPeerContext* context) context->icon_x = -1; context->icon_y = -1; + + context->vcm = WTSCreateVirtualChannelManager(client); } void test_peer_context_free(freerdp_peer* client, testPeerContext* context) { if (context) { + if (context->debug_channel_thread) + { + freerdp_thread_stop(context->debug_channel_thread); + freerdp_thread_free(context->debug_channel_thread); + } stream_free(context->s); xfree(context->icon_data); xfree(context->bg_data); rfx_context_free(context->rfx_context); + if (context->debug_channel) + { + WTSVirtualChannelClose(context->debug_channel); + } + WTSDestroyVirtualChannelManager(context->vcm); xfree(context); } } @@ -317,8 +333,62 @@ void tf_peer_dump_rfx(freerdp_peer* client) } } +static void* tf_debug_channel_thread_func(void* arg) +{ + void* fd; + STREAM* s; + void* buffer; + uint32 bytes_returned = 0; + testPeerContext* context = (testPeerContext*) arg; + freerdp_thread* thread = context->debug_channel_thread; + + if (WTSVirtualChannelQuery(context->debug_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); + + WTSVirtualChannelWrite(context->debug_channel, (uint8*) "test1", 5, NULL); + + while (1) + { + freerdp_thread_wait(thread); + if (freerdp_thread_is_stopped(thread)) + break; + + stream_set_pos(s, 0); + if (WTSVirtualChannelRead(context->debug_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(context->debug_channel, 0, stream_get_head(s), + stream_get_size(s), &bytes_returned) == false) + { + /* should not happen */ + break; + } + } + stream_set_pos(s, bytes_returned); + + printf("got %d bytes\n", bytes_returned); + } + + stream_free(s); + freerdp_thread_quit(thread); + + return 0; +} + boolean tf_peer_post_connect(freerdp_peer* client) { + int i; + testPeerContext* context = (testPeerContext*) client->context; + /** * This callback is called when the entire connection sequence is done, i.e. we've received the * Font List PDU from the client and sent out the Font Map PDU. @@ -342,6 +412,25 @@ boolean tf_peer_post_connect(freerdp_peer* client) /* A real server should tag the peer as activated here and start sending updates in mainloop. */ test_peer_load_icon(client); + /* Iterate all channel names requested by the client and activate those supported by the server */ + for (i = 0; i < client->settings->num_channels; i++) + { + if (client->settings->channels[i].joined) + { + if (strncmp(client->settings->channels[i].name, "rdpdbg", 6) == 0) + { + context->debug_channel = WTSVirtualChannelOpenEx(context->vcm, "rdpdbg", 0); + if (context->debug_channel != NULL) + { + printf("Open channel rdpdbg.\n"); + context->debug_channel_thread = freerdp_thread_new(); + freerdp_thread_start(context->debug_channel_thread, + tf_debug_channel_thread_func, context); + } + } + } + } + /* Return false here would stop the execution of the peer mainloop. */ return true; } @@ -394,6 +483,13 @@ void tf_peer_keyboard_event(rdpInput* input, uint16 flags, uint16 code) update->DesktopResize(update->context); context->activated = false; } + else if ((flags & 0x4000) && code == 0x2E) /* 'c' key */ + { + if (context->debug_channel) + { + WTSVirtualChannelWrite(context->debug_channel, (uint8*) "test2", 5, NULL); + } + } } void tf_peer_unicode_keyboard_event(rdpInput* input, uint16 code) @@ -421,6 +517,7 @@ static void* test_peer_mainloop(void* arg) int rcount; void* rfds[32]; fd_set rfds_set; + testPeerContext* context; freerdp_peer* client = (freerdp_peer*) arg; memset(rfds, 0, sizeof(rfds)); @@ -443,6 +540,7 @@ static void* test_peer_mainloop(void* arg) client->input->ExtendedMouseEvent = tf_peer_extended_mouse_event; client->Initialize(client); + context = (testPeerContext*) client->context; printf("We've got a client %s\n", client->hostname); @@ -455,6 +553,7 @@ static void* test_peer_mainloop(void* arg) printf("Failed to get FreeRDP file descriptor\n"); break; } + WTSVirtualChannelManagerGetFileDescriptor(context->vcm, rfds, &rcount); max_fds = 0; FD_ZERO(&rfds_set); @@ -487,6 +586,8 @@ static void* test_peer_mainloop(void* arg) if (client->CheckFileDescriptor(client) != true) break; + if (WTSVirtualChannelManagerCheckFileDescriptor(context->vcm) != true) + break; } printf("Client %s disconnected.\n", client->hostname);