diff --git a/channels/echo/CMakeLists.txt b/channels/echo/CMakeLists.txt index 3044e5ee5..bfa8297c9 100644 --- a/channels/echo/CMakeLists.txt +++ b/channels/echo/CMakeLists.txt @@ -21,3 +21,6 @@ if(WITH_CLIENT_CHANNELS) add_channel_client(${MODULE_PREFIX} ${CHANNEL_NAME}) endif() +if(WITH_SERVER_CHANNELS) + add_channel_server(${MODULE_PREFIX} ${CHANNEL_NAME}) +endif() diff --git a/channels/echo/ChannelOptions.cmake b/channels/echo/ChannelOptions.cmake index 47a7d075e..eb4950a15 100644 --- a/channels/echo/ChannelOptions.cmake +++ b/channels/echo/ChannelOptions.cmake @@ -1,7 +1,7 @@ set(OPTION_DEFAULT OFF) set(OPTION_CLIENT_DEFAULT ON) -set(OPTION_SERVER_DEFAULT OFF) +set(OPTION_SERVER_DEFAULT ON) define_channel_options(NAME "echo" TYPE "dynamic" DESCRIPTION "Echo Virtual Channel Extension" diff --git a/channels/echo/server/CMakeLists.txt b/channels/echo/server/CMakeLists.txt new file mode 100644 index 000000000..47fb633e6 --- /dev/null +++ b/channels/echo/server/CMakeLists.txt @@ -0,0 +1,36 @@ +# FreeRDP: A Remote Desktop Protocol Implementation +# FreeRDP cmake build script +# +# Copyright 2012 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. + +define_channel_server("echo") + +set(${MODULE_PREFIX}_SRCS + echo_main.c) + +add_channel_server_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE "DVCPluginEntry") + +set_target_properties(${MODULE_NAME} PROPERTIES PREFIX "") + +set_complex_link_libraries(VARIABLE ${MODULE_PREFIX}_LIBS + MONOLITHIC ${MONOLITHIC_BUILD} + MODULE freerdp + MODULES freerdp-utils freerdp-core) + +target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS}) + +install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets) + +set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Server") diff --git a/channels/echo/server/echo_main.c b/channels/echo/server/echo_main.c new file mode 100644 index 000000000..37d42cb61 --- /dev/null +++ b/channels/echo/server/echo_main.c @@ -0,0 +1,233 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Echo Virtual Channel Extension + * + * Copyright 2014 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include +#include +#include +#include + +#include + +typedef struct _echo_server +{ + echo_server_context context; + + BOOL opened; + + HANDLE stopEvent; + + HANDLE thread; + void* echo_channel; + + DWORD SessionId; + +} echo_server; + +static BOOL echo_server_open_channel(echo_server* echo) +{ + DWORD Error; + HANDLE hEvent; + DWORD StartTick; + DWORD BytesReturned = 0; + PULONG pSessionId = NULL; + + if (WTSQuerySessionInformationA(echo->context.vcm, WTS_CURRENT_SESSION, + WTSSessionId, (LPSTR*) &pSessionId, &BytesReturned) == FALSE) + { + return FALSE; + } + echo->SessionId = (DWORD) *pSessionId; + WTSFreeMemory(pSessionId); + + hEvent = WTSVirtualChannelManagerGetEventHandle(echo->context.vcm); + StartTick = GetTickCount(); + + while (echo->echo_channel == NULL) + { + WaitForSingleObject(hEvent, 1000); + + echo->echo_channel = WTSVirtualChannelOpenEx(echo->SessionId, + "ECHO", WTS_CHANNEL_OPTION_DYNAMIC); + + if (echo->echo_channel) + break; + + Error = GetLastError(); + if (Error == ERROR_NOT_FOUND) + break; + + if (GetTickCount() - StartTick > 5000) + break; + } + + return echo->echo_channel ? TRUE : FALSE; +} + +static void* echo_server_thread_func(void* arg) +{ + wStream* s; + void* buffer; + DWORD nCount; + HANDLE events[8]; + BOOL ready = FALSE; + HANDLE ChannelEvent; + DWORD BytesReturned = 0; + echo_server* echo = (echo_server*) arg; + + if (echo_server_open_channel(echo) == FALSE) + { + IFCALL(echo->context.OpenResult, &echo->context, ECHO_SERVER_OPEN_RESULT_NOTSUPPORTED); + return NULL; + } + + buffer = NULL; + BytesReturned = 0; + ChannelEvent = NULL; + + if (WTSVirtualChannelQuery(echo->echo_channel, WTSVirtualEventHandle, &buffer, &BytesReturned) == TRUE) + { + if (BytesReturned == sizeof(HANDLE)) + CopyMemory(&ChannelEvent, buffer, sizeof(HANDLE)); + + WTSFreeMemory(buffer); + } + + nCount = 0; + events[nCount++] = echo->stopEvent; + events[nCount++] = ChannelEvent; + + /* Wait for the client to confirm that the Graphics Pipeline dynamic channel is ready */ + + while (1) + { + if (WaitForMultipleObjects(nCount, events, FALSE, 100) == WAIT_OBJECT_0) + { + IFCALL(echo->context.OpenResult, &echo->context, ECHO_SERVER_OPEN_RESULT_CLOSED); + break; + } + + if (WTSVirtualChannelQuery(echo->echo_channel, WTSVirtualChannelReady, &buffer, &BytesReturned) == FALSE) + { + IFCALL(echo->context.OpenResult, &echo->context, ECHO_SERVER_OPEN_RESULT_ERROR); + break; + } + + ready = *((BOOL*) buffer); + + WTSFreeMemory(buffer); + + if (ready) + { + IFCALL(echo->context.OpenResult, &echo->context, ECHO_SERVER_OPEN_RESULT_OK); + break; + } + } + + s = Stream_New(NULL, 4096); + + while (ready) + { + if (WaitForMultipleObjects(nCount, events, FALSE, INFINITE) == WAIT_OBJECT_0) + break; + + Stream_SetPosition(s, 0); + + WTSVirtualChannelRead(echo->echo_channel, 0, NULL, 0, &BytesReturned); + if (BytesReturned < 1) + continue; + Stream_EnsureRemainingCapacity(s, BytesReturned); + if (WTSVirtualChannelRead(echo->echo_channel, 0, (PCHAR) Stream_Buffer(s), + (ULONG) Stream_Capacity(s), &BytesReturned) == FALSE) + { + break; + } + + IFCALL(echo->context.Response, &echo->context, (PCHAR) Stream_Buffer(s), BytesReturned); + } + + Stream_Free(s, TRUE); + WTSVirtualChannelClose(echo->echo_channel); + echo->echo_channel = NULL; + + return NULL; +} + +static void echo_server_open(echo_server_context* context) +{ + echo_server* echo = (echo_server*) context; + + if (echo->thread == NULL) + { + echo->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + echo->thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) echo_server_thread_func, (void*) echo, 0, NULL); + } +} + +static void echo_server_close(echo_server_context* context) +{ + echo_server* echo = (echo_server*) context; + + if (echo->thread) + { + SetEvent(echo->stopEvent); + WaitForSingleObject(echo->thread, INFINITE); + CloseHandle(echo->thread); + CloseHandle(echo->stopEvent); + echo->thread = NULL; + echo->stopEvent = NULL; + } +} + +static BOOL echo_server_request(echo_server_context* context, const BYTE* buffer, UINT32 length) +{ + echo_server* echo = (echo_server*) context; + + return WTSVirtualChannelWrite(echo->echo_channel, (PCHAR) buffer, length, NULL); +} + +echo_server_context* echo_server_context_new(HANDLE vcm) +{ + echo_server* echo; + + echo = (echo_server*) calloc(1, sizeof(echo_server)); + + echo->context.vcm = vcm; + echo->context.Open = echo_server_open; + echo->context.Close = echo_server_close; + echo->context.Request = echo_server_request; + + return (echo_server_context*) echo; +} + +void echo_server_context_free(echo_server_context* context) +{ + echo_server* echo = (echo_server*) context; + + echo_server_close(context); + + free(echo); +} diff --git a/channels/server/channels.c b/channels/server/channels.c index 1f470e6e9..cac4243bd 100644 --- a/channels/server/channels.c +++ b/channels/server/channels.c @@ -43,6 +43,7 @@ #include #include #include +#include #include #include @@ -57,6 +58,9 @@ void freerdp_channels_dummy() cliprdr_server_context_new(NULL); cliprdr_server_context_free(NULL); + echo_server_context_new(NULL); + echo_server_context_free(NULL); + rdpdr_server_context_new(NULL); rdpdr_server_context_free(NULL); diff --git a/include/freerdp/server/echo.h b/include/freerdp/server/echo.h new file mode 100644 index 000000000..e2a4c4260 --- /dev/null +++ b/include/freerdp/server/echo.h @@ -0,0 +1,85 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Echo Virtual Channel Extension + * + * Copyright 2014 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_CHANNEL_ECHO_SERVER_H +#define FREERDP_CHANNEL_ECHO_SERVER_H + +#include + +typedef enum ECHO_SERVER_OPEN_RESULT +{ + ECHO_SERVER_OPEN_RESULT_OK = 0, + ECHO_SERVER_OPEN_RESULT_CLOSED = 1, + ECHO_SERVER_OPEN_RESULT_NOTSUPPORTED = 2, + ECHO_SERVER_OPEN_RESULT_ERROR = 3 +} ECHO_SERVER_OPEN_RESULT; + +typedef struct _echo_server_context echo_server_context; + +typedef void (*psEchoServerOpen)(echo_server_context* context); +typedef void (*psEchoServerClose)(echo_server_context* context); +typedef BOOL (*psEchoServerRequest)(echo_server_context* context, const BYTE* buffer, UINT32 length); + +typedef void (*psEchoServerOpenResult)(echo_server_context* context, ECHO_SERVER_OPEN_RESULT result); +typedef void (*psEchoServerResponse)(echo_server_context* context, const BYTE* buffer, UINT32 length); + +struct _echo_server_context +{ + HANDLE vcm; + + /* Server self-defined pointer. */ + void* data; + + /*** APIs called by the server. ***/ + /** + * Open the echo channel. + */ + psEchoServerOpen Open; + /** + * Close the echo channel. + */ + psEchoServerClose Close; + /** + * Send echo request PDU. + */ + psEchoServerRequest Request; + + /*** Callbacks registered by the server. ***/ + /** + * Indicate whether the channel is opened successfully. + */ + psEchoServerOpenResult OpenResult; + /** + * Receive echo response PDU. + */ + psEchoServerResponse Response; +}; + +#ifdef __cplusplus +extern "C" { +#endif + +FREERDP_API echo_server_context* echo_server_context_new(HANDLE vcm); +FREERDP_API void echo_server_context_free(echo_server_context* context); + +#ifdef __cplusplus +} +#endif + +#endif /* FREERDP_CHANNEL_ECHO_SERVER_H */