summaryrefslogtreecommitdiffstats
path: root/channels/rdp2tcp
diff options
context:
space:
mode:
Diffstat (limited to 'channels/rdp2tcp')
-rw-r--r--channels/rdp2tcp/CMakeLists.txt22
-rw-r--r--channels/rdp2tcp/ChannelOptions.cmake10
-rw-r--r--channels/rdp2tcp/client/CMakeLists.txt27
-rw-r--r--channels/rdp2tcp/client/rdp2tcp_main.c357
4 files changed, 416 insertions, 0 deletions
diff --git a/channels/rdp2tcp/CMakeLists.txt b/channels/rdp2tcp/CMakeLists.txt
new file mode 100644
index 0000000..31de127
--- /dev/null
+++ b/channels/rdp2tcp/CMakeLists.txt
@@ -0,0 +1,22 @@
+# FreeRDP: A Remote Desktop Protocol Implementation
+# FreeRDP cmake build script
+#
+# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+#
+# 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("rdp2tcp")
+
+if(WITH_CLIENT_CHANNELS)
+ add_channel_client(${MODULE_PREFIX} ${CHANNEL_NAME})
+endif()
diff --git a/channels/rdp2tcp/ChannelOptions.cmake b/channels/rdp2tcp/ChannelOptions.cmake
new file mode 100644
index 0000000..3cad9b1
--- /dev/null
+++ b/channels/rdp2tcp/ChannelOptions.cmake
@@ -0,0 +1,10 @@
+set(OPTION_DEFAULT OFF)
+set(OPTION_CLIENT_DEFAULT ON)
+set(OPTION_SERVER_DEFAULT OFF)
+
+define_channel_options(NAME "rdp2tcp" TYPE "static"
+ DESCRIPTION "Tunneling TCP over RDP"
+ DEFAULT ${OPTION_DEFAULT})
+
+define_channel_client_options(${OPTION_CLIENT_DEFAULT})
+define_channel_server_options(${OPTION_SERVER_DEFAULT})
diff --git a/channels/rdp2tcp/client/CMakeLists.txt b/channels/rdp2tcp/client/CMakeLists.txt
new file mode 100644
index 0000000..f7bdd20
--- /dev/null
+++ b/channels/rdp2tcp/client/CMakeLists.txt
@@ -0,0 +1,27 @@
+# FreeRDP: A Remote Desktop Protocol Implementation
+# FreeRDP cmake build script
+#
+# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+#
+# 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_client("rdp2tcp")
+
+set(${MODULE_PREFIX}_SRCS
+ rdp2tcp_main.c
+)
+
+set(${MODULE_PREFIX}_LIBS
+ freerdp
+)
+add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} TRUE "VirtualChannelEntryEx")
diff --git a/channels/rdp2tcp/client/rdp2tcp_main.c b/channels/rdp2tcp/client/rdp2tcp_main.c
new file mode 100644
index 0000000..0b2c8f1
--- /dev/null
+++ b/channels/rdp2tcp/client/rdp2tcp_main.c
@@ -0,0 +1,357 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ * rdp2tcp Virtual Channel Extension
+ *
+ * Copyright 2017 Artur Zaprzala
+ *
+ * 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 <stdio.h>
+#include <winpr/assert.h>
+
+#include <winpr/file.h>
+#include <winpr/pipe.h>
+#include <winpr/thread.h>
+
+#include <freerdp/freerdp.h>
+#include <freerdp/svc.h>
+#include <freerdp/channels/rdp2tcp.h>
+
+#include <freerdp/log.h>
+#define TAG CLIENT_TAG(RDP2TCP_DVC_CHANNEL_NAME)
+
+static int const debug = 0;
+
+typedef struct
+{
+ HANDLE hStdOutputRead;
+ HANDLE hStdInputWrite;
+ HANDLE hProcess;
+ HANDLE copyThread;
+ HANDLE writeComplete;
+ DWORD openHandle;
+ void* initHandle;
+ CHANNEL_ENTRY_POINTS_FREERDP_EX channelEntryPoints;
+ char buffer[16 * 1024];
+ char* commandline;
+} Plugin;
+
+static int init_external_addin(Plugin* plugin)
+{
+ SECURITY_ATTRIBUTES saAttr;
+ STARTUPINFOA siStartInfo; /* Using ANSI type to match CreateProcessA */
+ PROCESS_INFORMATION procInfo;
+ saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
+ saAttr.bInheritHandle = TRUE;
+ saAttr.lpSecurityDescriptor = NULL;
+ siStartInfo.cb = sizeof(STARTUPINFO);
+ siStartInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
+ siStartInfo.dwFlags = STARTF_USESTDHANDLES;
+
+ // Create pipes
+ if (!CreatePipe(&plugin->hStdOutputRead, &siStartInfo.hStdOutput, &saAttr, 0))
+ {
+ WLog_ERR(TAG, "stdout CreatePipe");
+ return -1;
+ }
+
+ if (!SetHandleInformation(plugin->hStdOutputRead, HANDLE_FLAG_INHERIT, 0))
+ {
+ WLog_ERR(TAG, "stdout SetHandleInformation");
+ return -1;
+ }
+
+ if (!CreatePipe(&siStartInfo.hStdInput, &plugin->hStdInputWrite, &saAttr, 0))
+ {
+ WLog_ERR(TAG, "stdin CreatePipe");
+ return -1;
+ }
+
+ if (!SetHandleInformation(plugin->hStdInputWrite, HANDLE_FLAG_INHERIT, 0))
+ {
+ WLog_ERR(TAG, "stdin SetHandleInformation");
+ return -1;
+ }
+
+ // Execute plugin
+ plugin->commandline = _strdup(plugin->channelEntryPoints.pExtendedData);
+ if (!CreateProcessA(NULL,
+ plugin->commandline, // command line
+ NULL, // process security attributes
+ NULL, // primary thread security attributes
+ TRUE, // handles are inherited
+ 0, // creation flags
+ NULL, // use parent's environment
+ NULL, // use parent's current directory
+ &siStartInfo, // STARTUPINFO pointer
+ &procInfo // receives PROCESS_INFORMATION
+ ))
+ {
+ WLog_ERR(TAG, "fork for addin");
+ return -1;
+ }
+
+ plugin->hProcess = procInfo.hProcess;
+ CloseHandle(procInfo.hThread);
+ CloseHandle(siStartInfo.hStdOutput);
+ CloseHandle(siStartInfo.hStdInput);
+ return 0;
+}
+
+static void dumpData(char* data, unsigned length)
+{
+ unsigned const limit = 98;
+ unsigned l = length > limit ? limit / 2 : length;
+
+ for (unsigned i = 0; i < l; ++i)
+ {
+ printf("%02hhx", data[i]);
+ }
+
+ if (length > limit)
+ {
+ printf("...");
+
+ for (unsigned i = length - l; i < length; ++i)
+ printf("%02hhx", data[i]);
+ }
+
+ puts("");
+}
+
+static DWORD WINAPI copyThread(void* data)
+{
+ DWORD status = WAIT_OBJECT_0;
+ Plugin* plugin = (Plugin*)data;
+ size_t const bufsize = 16 * 1024;
+
+ while (status == WAIT_OBJECT_0)
+ {
+
+ HANDLE handles[MAXIMUM_WAIT_OBJECTS] = { 0 };
+ DWORD dwRead = 0;
+ char* buffer = malloc(bufsize);
+
+ if (!buffer)
+ {
+ fprintf(stderr, "rdp2tcp copyThread: malloc failed\n");
+ goto fail;
+ }
+
+ // if (!ReadFile(plugin->hStdOutputRead, plugin->buffer, sizeof plugin->buffer, &dwRead,
+ // NULL))
+ if (!ReadFile(plugin->hStdOutputRead, buffer, bufsize, &dwRead, NULL))
+ {
+ free(buffer);
+ goto fail;
+ }
+
+ if (debug > 1)
+ {
+ printf(">%8u ", (unsigned)dwRead);
+ dumpData(buffer, dwRead);
+ }
+
+ if (plugin->channelEntryPoints.pVirtualChannelWriteEx(
+ plugin->initHandle, plugin->openHandle, buffer, dwRead, buffer) != CHANNEL_RC_OK)
+ {
+ free(buffer);
+ fprintf(stderr, "rdp2tcp copyThread failed %i\n", (int)dwRead);
+ goto fail;
+ }
+
+ handles[0] = plugin->writeComplete;
+ handles[1] = freerdp_abort_event(plugin->channelEntryPoints.context);
+ status = WaitForMultipleObjects(2, handles, FALSE, INFINITE);
+ if (status == WAIT_OBJECT_0)
+ ResetEvent(plugin->writeComplete);
+ }
+
+fail:
+ ExitThread(0);
+ return 0;
+}
+
+static void closeChannel(Plugin* plugin)
+{
+ if (debug)
+ puts("rdp2tcp closing channel");
+
+ WINPR_ASSERT(plugin);
+ WINPR_ASSERT(plugin->channelEntryPoints.pVirtualChannelCloseEx);
+ plugin->channelEntryPoints.pVirtualChannelCloseEx(plugin->initHandle, plugin->openHandle);
+}
+
+static void dataReceived(Plugin* plugin, void* pData, UINT32 dataLength, UINT32 totalLength,
+ UINT32 dataFlags)
+{
+ DWORD dwWritten = 0;
+
+ if (dataFlags & CHANNEL_FLAG_SUSPEND)
+ {
+ if (debug)
+ puts("rdp2tcp Channel Suspend");
+
+ return;
+ }
+
+ if (dataFlags & CHANNEL_FLAG_RESUME)
+ {
+ if (debug)
+ puts("rdp2tcp Channel Resume");
+
+ return;
+ }
+
+ if (debug > 1)
+ {
+ printf("<%c%3u/%3u ", dataFlags & CHANNEL_FLAG_FIRST ? ' ' : '+', totalLength, dataLength);
+ dumpData(pData, dataLength);
+ }
+
+ if (dataFlags & CHANNEL_FLAG_FIRST)
+ {
+ if (!WriteFile(plugin->hStdInputWrite, &totalLength, sizeof(totalLength), &dwWritten, NULL))
+ closeChannel(plugin);
+ }
+
+ if (!WriteFile(plugin->hStdInputWrite, pData, dataLength, &dwWritten, NULL))
+ closeChannel(plugin);
+}
+
+static void VCAPITYPE VirtualChannelOpenEventEx(LPVOID lpUserParam, DWORD openHandle, UINT event,
+ LPVOID pData, UINT32 dataLength, UINT32 totalLength,
+ UINT32 dataFlags)
+{
+ Plugin* plugin = (Plugin*)lpUserParam;
+
+ switch (event)
+ {
+ case CHANNEL_EVENT_DATA_RECEIVED:
+ dataReceived(plugin, pData, dataLength, totalLength, dataFlags);
+ break;
+
+ case CHANNEL_EVENT_WRITE_CANCELLED:
+ free(pData);
+ break;
+ case CHANNEL_EVENT_WRITE_COMPLETE:
+ SetEvent(plugin->writeComplete);
+ free(pData);
+ break;
+ }
+}
+
+static void channel_terminated(Plugin* plugin)
+{
+ if (debug)
+ puts("rdp2tcp terminated");
+
+ if (!plugin)
+ return;
+
+ if (plugin->copyThread)
+ TerminateThread(plugin->copyThread, 0);
+ if (plugin->writeComplete)
+ CloseHandle(plugin->writeComplete);
+
+ CloseHandle(plugin->hStdInputWrite);
+ CloseHandle(plugin->hStdOutputRead);
+ TerminateProcess(plugin->hProcess, 0);
+ CloseHandle(plugin->hProcess);
+ free(plugin->commandline);
+ free(plugin);
+}
+
+static void channel_initialized(Plugin* plugin)
+{
+ plugin->writeComplete = CreateEvent(NULL, TRUE, FALSE, NULL);
+ plugin->copyThread = CreateThread(NULL, 0, copyThread, plugin, 0, NULL);
+}
+
+static VOID VCAPITYPE VirtualChannelInitEventEx(LPVOID lpUserParam, LPVOID pInitHandle, UINT event,
+ LPVOID pData, UINT dataLength)
+{
+ Plugin* plugin = (Plugin*)lpUserParam;
+
+ switch (event)
+ {
+ case CHANNEL_EVENT_INITIALIZED:
+ channel_initialized(plugin);
+ break;
+
+ case CHANNEL_EVENT_CONNECTED:
+ if (debug)
+ puts("rdp2tcp connected");
+
+ WINPR_ASSERT(plugin);
+ WINPR_ASSERT(plugin->channelEntryPoints.pVirtualChannelOpenEx);
+ if (plugin->channelEntryPoints.pVirtualChannelOpenEx(
+ pInitHandle, &plugin->openHandle, RDP2TCP_DVC_CHANNEL_NAME,
+ VirtualChannelOpenEventEx) != CHANNEL_RC_OK)
+ return;
+
+ break;
+
+ case CHANNEL_EVENT_DISCONNECTED:
+ if (debug)
+ puts("rdp2tcp disconnected");
+
+ break;
+
+ case CHANNEL_EVENT_TERMINATED:
+ channel_terminated(plugin);
+ break;
+ }
+}
+
+#if 1
+#define VirtualChannelEntryEx rdp2tcp_VirtualChannelEntryEx
+#else
+#define VirtualChannelEntryEx FREERDP_API VirtualChannelEntryEx
+#endif
+FREERDP_ENTRY_POINT(BOOL VCAPITYPE VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS pEntryPoints,
+ PVOID pInitHandle))
+{
+ CHANNEL_ENTRY_POINTS_FREERDP_EX* pEntryPointsEx = NULL;
+ CHANNEL_DEF channelDef;
+ Plugin* plugin = (Plugin*)calloc(1, sizeof(Plugin));
+
+ if (!plugin)
+ return FALSE;
+
+ pEntryPointsEx = (CHANNEL_ENTRY_POINTS_FREERDP_EX*)pEntryPoints;
+ WINPR_ASSERT(pEntryPointsEx->cbSize >= sizeof(CHANNEL_ENTRY_POINTS_FREERDP_EX) &&
+ pEntryPointsEx->MagicNumber == FREERDP_CHANNEL_MAGIC_NUMBER);
+ plugin->initHandle = pInitHandle;
+ plugin->channelEntryPoints = *pEntryPointsEx;
+
+ if (init_external_addin(plugin) < 0)
+ {
+ free(plugin);
+ return FALSE;
+ }
+
+ strncpy(channelDef.name, RDP2TCP_DVC_CHANNEL_NAME, sizeof(channelDef.name));
+ channelDef.options =
+ CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP | CHANNEL_OPTION_COMPRESS_RDP;
+
+ if (pEntryPointsEx->pVirtualChannelInitEx(plugin, NULL, pInitHandle, &channelDef, 1,
+ VIRTUAL_CHANNEL_VERSION_WIN2000,
+ VirtualChannelInitEventEx) != CHANNEL_RC_OK)
+ return FALSE;
+
+ return TRUE;
+}
+
+// vim:ts=4