Files
nccgroup-SocksOverRDP/SocksOverRDP-Plugin/SocksOverRDP-Plugin.cpp
T
2020-04-17 19:03:33 +01:00

897 lines
23 KiB
C++

/*
* MIT License
*
* Copyright(c) 2020 Balazs Bucsay
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files(the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions :
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "stdafx.h"
#include "resource.h"
#include "SocksOverRDP-Plugin.h"
#pragma comment(lib, "Ws2_32.lib")
#define SocksOverRDP_CHANNEL_NAME "SocksChannel"
#define BUF_SIZE 4096
struct arguments {
DWORD enabled;
WCHAR *ip;
WCHAR *port;
};
// main thread struct for global variables
struct threadargs {
struct arguments *running_args;
SOCKET sockserver;
BOOL run;
IWTSVirtualChannel *m_ptrChannel = NULL;
HANDLE hThread = NULL;
} ta;
struct threads
{
DWORD dwThreadId;
HANDLE hThread;
BOOL run;
SOCKET s;
struct threads *next;
};
// debug verbose flag
static BOOL bVerbose = FALSE;
static struct threads *ThreadHead = NULL;
char szOverflow[BUF_SIZE * 4];
DWORD dwOverflow = 0;
static HANDLE ghMutex, ghLLMutex;
using namespace ATL;
#define CHECK_QUIT_HR( _x_ ) if(FAILED(hr)) { return hr; }
class ATL_NO_VTABLE SocksOverRDPPlugin :
public CComObjectRootEx<CComMultiThreadModel>,
public CComCoClass<SocksOverRDPPlugin, &CLSID_CompReg>,
public IWTSPlugin,
public IWTSVirtualChannelCallback,
public IWTSListenerCallback
{
public:
CComPtr<IWTSVirtualChannel> m_ptrChannel;
DECLARE_REGISTRY_RESOURCEID(IDR_SocksOverRDPPLUGIN)
BEGIN_COM_MAP(SocksOverRDPPlugin)
COM_INTERFACE_ENTRY(IWTSPlugin)
COM_INTERFACE_ENTRY(IWTSVirtualChannelCallback)
COM_INTERFACE_ENTRY(IWTSListenerCallback)
END_COM_MAP()
DECLARE_PROTECT_FINAL_CONSTRUCT()
HRESULT FinalConstruct()
{
return S_OK;
}
void FinalRelease()
{
}
// IWTSPlugin.
//
HRESULT STDMETHODCALLTYPE
Initialize(IWTSVirtualChannelManager *pChannelMgr);
HRESULT STDMETHODCALLTYPE Connected();
HRESULT STDMETHODCALLTYPE Disconnected(DWORD dwDisconnectCode)
{
// Prevent C4100 "unreferenced parameter" warnings.
dwDisconnectCode;
return S_OK;
}
HRESULT STDMETHODCALLTYPE Terminated()
{
return S_OK;
}
VOID SetChannel(IWTSVirtualChannel *pChannel, struct threadargs *pTa);
// IWTSVirtualChannelCallbackdwDummyThreadId
//
HRESULT STDMETHODCALLTYPE OnDataReceived(ULONG cbSize, __in_bcount(cbSize) BYTE *pBuffer);
HRESULT STDMETHODCALLTYPE OnClose()
{
ta.run = FALSE;
//DebugPrint(0, L"[*] Terminating thread, closing socket and channel0");
TerminateThreads();
closesocket(gpta->sockserver);
ta.m_ptrChannel = NULL;
WSACleanup();
return S_OK;
}
HRESULT STDMETHODCALLTYPE
OnNewChannelConnection(
__in IWTSVirtualChannel *pChannel,
__in_opt BSTR data,
__out BOOL *pbAccept,
__out IWTSVirtualChannelCallback **ppCallback);
// non-inherited ones
struct threadargs *gpta;
struct arguments running_args;
static struct threads *SocksOverRDPPlugin::AddThread(DWORD dwThreadId, SOCKET s);
static VOID SocksOverRDPPlugin::DeleteThread(DWORD dwThreadId);
static VOID SocksOverRDPPlugin::TerminateThreads();
static struct threads *SocksOverRDPPlugin::LookupThread(DWORD dwThreadId);
static VOID SocksOverRDPPlugin::StopThread(DWORD dwThreadId);
static VOID SocksOverRDPPlugin::DebugPrint(HRESULT hrDbg, __in_z LPWSTR fmt, ...);
LONG SocksOverRDPPlugin::GetDWORDRegKey(HKEY hKey, WCHAR *strValueName, DWORD *nValue);
LONG SocksOverRDPPlugin::GetStringRegKey(HKEY hKey, WCHAR *strValueName, WCHAR **strValue);
BOOL SocksOverRDPPlugin::GetRegistrySettings();
static DWORD WINAPI SocksOverRDPPlugin::RelayToRDPChannel(PVOID param);
static DWORD WINAPI SocksOverRDPPlugin::ListenerThread(PVOID param);
};
OBJECT_ENTRY_AUTO(__uuidof(CompReg), SocksOverRDPPlugin)
// Add thread to linked list
struct threads *SocksOverRDPPlugin::AddThread(DWORD dwThreadId, SOCKET s)
{
struct threads *rolling;
struct threads *ThreadStruct;
DWORD dwWaitResult = WaitForSingleObject(ghLLMutex, INFINITE);
switch (dwWaitResult)
{
case WAIT_OBJECT_0:
ThreadStruct = (struct threads *)malloc(sizeof(struct threads));
ThreadStruct->dwThreadId = dwThreadId;
ThreadStruct->hThread = GetCurrentThread();
ThreadStruct->run = TRUE;
ThreadStruct->s = s;
ThreadStruct->next = NULL;
if (ThreadHead == NULL)
{
ThreadHead = ThreadStruct;
}
else
{
rolling = ThreadHead;
while (rolling->next)
{
rolling = rolling->next;
}
rolling->next = ThreadStruct;
}
if (!ReleaseMutex(ghLLMutex))
{
if (bVerbose) DebugPrint(0, L"[*] AddThread Release failed: %08X", dwThreadId);
}
return ThreadStruct;
break;
case WAIT_ABANDONED:
if (bVerbose) DebugPrint(0, L"[*] AddThread abandoned: %08X", GetCurrentThreadId());
return NULL;
}
return NULL;
}
// Remove thread from linked list
VOID SocksOverRDPPlugin::DeleteThread(DWORD dwThreadId)
{
struct threads *rolling;
struct threads *prev = NULL;
DWORD dwWaitResult = WaitForSingleObject(ghLLMutex, INFINITE);
switch (dwWaitResult)
{
case WAIT_OBJECT_0:
if (!ThreadHead)
{
if (!ReleaseMutex(ghLLMutex))
{
if (bVerbose) DebugPrint(0, L"[*] DeleteThread Release failed: %08X", GetCurrentThreadId());
}
return;
}
rolling = ThreadHead;
while (rolling->dwThreadId != dwThreadId)
{
prev = rolling;
rolling = rolling->next;
}
if (prev)
{
if (rolling->next)
{
prev->next = rolling->next;
}
else
{
prev->next = NULL;
}
}
else
{
if (ThreadHead->next)
{
ThreadHead = ThreadHead->next;
}
else
{
ThreadHead = NULL;
}
}
rolling->run = FALSE;
closesocket(rolling->s);
rolling->dwThreadId = 0xffffffff;
rolling->next = FALSE;
rolling->hThread = NULL;
rolling->s = NULL;
free(rolling);
rolling = NULL;
if (!ReleaseMutex(ghLLMutex))
{
if (bVerbose) DebugPrint(0, L"[*] DeleteThread Release failed: %08X", GetCurrentThreadId());
}
return;
break;
case WAIT_ABANDONED:
if (bVerbose) DebugPrint(0, L"[*] DeleteThread abandoned: %08X", GetCurrentThreadId());
return;
}
}
/*
Terminate all running threads that handle communication
by setting the loop condition to FALSE and closing socket.
*/
VOID SocksOverRDPPlugin::TerminateThreads()
{
struct threads *rolling;
DWORD dwWaitResult = WaitForSingleObject(ghLLMutex, INFINITE);
switch (dwWaitResult)
{
case WAIT_OBJECT_0:
rolling = ThreadHead;
if (rolling == NULL)
{
if (bVerbose) DebugPrint(0, L"[*] Nothing to terminate. Threadhead empty. %08X", GetCurrentThreadId());
}
while (rolling)
{
rolling->run = FALSE;
closesocket(rolling->s);
rolling = rolling->next;
}
if (!ReleaseMutex(ghLLMutex))
{
if (bVerbose) DebugPrint(0, L"[*] TerminateThreads Release failed: %08X", GetCurrentThreadId());
}
return;
break;
case WAIT_ABANDONED:
if (bVerbose) DebugPrint(0, L"[*] TerminateThreads abandoned: %08X", GetCurrentThreadId());
return;
}
}
// Stopping a specific thread gently by setting the loop condition to FALSE
VOID SocksOverRDPPlugin::StopThread(DWORD dwThreadId)
{
struct threads *rolling;
DWORD dwWaitResult = WaitForSingleObject(ghLLMutex, INFINITE);
switch (dwWaitResult)
{
case WAIT_OBJECT_0:
rolling = ThreadHead;
while (rolling)
{
if (rolling->dwThreadId == dwThreadId)
{
rolling->run = FALSE;
if (!ReleaseMutex(ghLLMutex))
{
if (bVerbose) DebugPrint(0, L"[*] StopThread Release failed0: %08X", GetCurrentThreadId());
}
return;
}
rolling = rolling->next;
}
if (!ReleaseMutex(ghLLMutex))
{
if (bVerbose) DebugPrint(0, L"[*] StopThread Release failed1: %08X", GetCurrentThreadId());
}
return;
break;
case WAIT_ABANDONED:
if (bVerbose) DebugPrint(0, L"[*] lookupThread abandoned: %08X", GetCurrentThreadId());
return;
}
return;
}
// Look up thread and return a pointer to it by ThreadId
struct threads *SocksOverRDPPlugin::LookupThread(DWORD dwThreadId)
{
struct threads *rolling;
DWORD dwWaitResult = WaitForSingleObject(ghLLMutex, INFINITE);
switch (dwWaitResult)
{
case WAIT_OBJECT_0:
rolling = ThreadHead;
while (rolling)
{
if (rolling->dwThreadId == dwThreadId)
{
if (!ReleaseMutex(ghLLMutex))
{
if (bVerbose) DebugPrint(0, L"[*] lookupThread Release failed0: %08X", GetCurrentThreadId());
}
return rolling;
}
rolling = rolling->next;
}
if (!ReleaseMutex(ghLLMutex))
{
if (bVerbose) DebugPrint(0, L"[*] lookupThread Release failed1: %08X", GetCurrentThreadId());
}
return NULL;
break;
case WAIT_ABANDONED:
if (bVerbose) DebugPrint(0, L"[*] lookupThread abandoned: %08X", GetCurrentThreadId());
return NULL;
}
return NULL;
}
// Debug/Verbose Print function, message shows up in the debugger.
VOID SocksOverRDPPlugin::DebugPrint(HRESULT hrDbg, __in_z LPWSTR fmt, ...)
{
HRESULT hr;
TCHAR Buffer[DEBUG_PRINT_BUFFER_SIZE];
size_t Len;
hr = StringCchPrintf(Buffer, DEBUG_PRINT_BUFFER_SIZE, TEXT("[hr=0x%8x]"), hrDbg);
assert(SUCCEEDED(hr)); // buffer is sure to be big enough
hr = StringCchLength(Buffer, DEBUG_PRINT_BUFFER_SIZE, &Len);
assert(SUCCEEDED(hr)); // StringCchPrintf is supposed to always NULL term
va_list argptr;
va_start(argptr, fmt);
hr = StringCchVPrintf(Buffer + Len, DEBUG_PRINT_BUFFER_SIZE - Len,
fmt, argptr);
// the above could fail but we don't care since we
// should get a NULL terminated partial string
// insert terminating eol (despite failure)
hr = StringCchLength(Buffer, DEBUG_PRINT_BUFFER_SIZE, &Len);
assert(SUCCEEDED(hr)); // again there should be a NULL term
if (Len < DEBUG_PRINT_BUFFER_SIZE - 1)
{
Len++;
Buffer[Len] = TEXT('\0');
}
Buffer[Len - 1] = TEXT('\n');
OutputDebugString(Buffer);
}
LONG SocksOverRDPPlugin::GetDWORDRegKey(HKEY hKey, WCHAR *strValueName, DWORD *nValue)
{
DWORD dwBufferSize(sizeof(DWORD));
DWORD nResult;
LONG nError;
if ((nError = RegQueryValueEx(hKey, strValueName, 0, NULL, (LPBYTE)&nResult, &dwBufferSize)) == ERROR_SUCCESS)
{
*nValue = nResult;
}
return nError;
}
LONG SocksOverRDPPlugin::GetStringRegKey(HKEY hKey, WCHAR *strValueName, WCHAR **strValue)
{
LPVOID szTemp = NULL;
DWORD buflen = 255;
LONG nError;
if ((szTemp = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (size_t)buflen)) == NULL)
{
if (bVerbose) DebugPrint(GetLastError(), L"[-] Error allocating heap for read buffer %ld", GetLastError());
return -1;
}
if ((nError = RegQueryValueExW(hKey, strValueName, 0, NULL, (LPBYTE)szTemp, &buflen)) != ERROR_SUCCESS)
{
HeapFree(GetProcessHeap(), HEAP_NO_SERIALIZE, szTemp);
szTemp = NULL;
}
*strValue = (WCHAR *)szTemp;
return nError;
}
BOOL SocksOverRDPPlugin::GetRegistrySettings()
{
HKEY hKey;
LONG lRes;
WCHAR *szTemp;
if ((lRes = RegOpenKeyEx(HKEY_CURRENT_USER, L"SOFTWARE\\Microsoft\\Terminal Server Client\\Default\\AddIns\\SocksOverRDP-Plugin\\", 0, KEY_READ, &hKey)) != ERROR_SUCCESS)
{
DebugPrint(lRes, L"[-] Error opening registry hive/key");
return FALSE;
}
GetDWORDRegKey(hKey, L"enabled", &running_args.enabled);
GetStringRegKey(hKey, L"ip", &szTemp);
if (szTemp != NULL)
{
if (wcslen(szTemp) < 16)
{
running_args.ip = szTemp;
}
else
{
MessageBox(NULL, L"IP too long. Please fix it under the following key:\r\nHKCU\\SOFTWARE\\Microsoft\\Terminal Server Client\\Default\\AddIns\\SocksOverRDP-Plugin\\", L"Registry value error", MB_OK);
}
}
GetStringRegKey(hKey, L"port", &szTemp);
if (szTemp != NULL)
{
if (wcslen(szTemp) < 6)
{
running_args.port = szTemp;
}
else
{
MessageBox(NULL, L"Port too long. Please fix it under the following key:\r\nHKCU\\SOFTWARE\\Microsoft\\Terminal Server Client\\Default\\AddIns\\SocksOverRDP-Plugin\\", L"Registry value error", MB_OK);
}
}
return TRUE;
}
// IWTSPlugin::Initialize implementation.
HRESULT SocksOverRDPPlugin::Initialize(__in IWTSVirtualChannelManager *pChannelMgr)
{
HRESULT hr;
CComObject<SocksOverRDPPlugin> *pListenerCallback;
CComPtr<SocksOverRDPPlugin> ptrListenerCallback;
CComPtr<IWTSListener> ptrListener;
WCHAR enabledmsg[256];
running_args.enabled = 0;
running_args.port = L"1080";
running_args.ip = L"127.0.0.1";
if (!GetRegistrySettings())
{
DebugPrint(-1, L"[-] Could not access the registry settings");
}
if (!running_args.enabled)
{
DebugPrint(0, L"[*] Plugin disabled");
return -1;
}
wnsprintf(enabledmsg, 255, L"The SocksOverRDP plugin is enabled. When the server binary gets executed, it will listen on: %s:%s", running_args.ip, running_args.port);
MessageBox(NULL, enabledmsg, L"SocksOverRDP plugin is enabled", MB_OK | MB_ICONWARNING);
// Create an instance of the CSampleListenerCallback object.
hr = CComObject<SocksOverRDPPlugin>::CreateInstance(&pListenerCallback);
CHECK_QUIT_HR("SocksOverRDPPlugin::CreateInstance");
ptrListenerCallback = pListenerCallback;
// Attach the callback to the endpoint.
hr = pChannelMgr->CreateListener(
SocksOverRDP_CHANNEL_NAME,
0,
(SocksOverRDPPlugin*)ptrListenerCallback,
&ptrListener);
CHECK_QUIT_HR("CreateListener");
return hr;
}
// MSTSC connected to server over RDP
HRESULT STDMETHODCALLTYPE SocksOverRDPPlugin::Connected()
{
return S_OK;
}
/*
This thread is started for each and every connection that is made to the socks
proxy. This part only handles the Client->RDP communication.
Whatever comes from the client will be piped into the RDP channel with prefixed
with: DWORD(threadid)+DWORD(sizeof(following_data))+BYTE(closurebit)
*/
DWORD SocksOverRDPPlugin::RelayToRDPChannel(PVOID param)
{
SOCKET c = (SOCKET)param;
DWORD dwWaitResult, dwThreadId = GetCurrentThreadId();
char buf[BUF_SIZE + sizeof(DWORD) + sizeof(DWORD) + sizeof(BYTE)];
int ret;
struct threads *pta;
pta = AddThread(dwThreadId, c);
if (!pta)
{
if (bVerbose) DebugPrint(0, L"%08X: Adding thread failure.", dwThreadId);
return -1;
}
// running till error or thread stopped
while (pta->run)
{
// reading from client socket
if ((ret = recv(c, buf + sizeof(DWORD) + sizeof(DWORD) + sizeof(BYTE), BUF_SIZE, 0)) > 0)
{
// might have been blocked for a long time and the thread should be
// stopped already
if (!pta->run) break;
// prefixing with the necessities
memcpy(buf, &dwThreadId, sizeof(DWORD));
memcpy(buf + sizeof(DWORD), &ret, sizeof(DWORD));
buf[sizeof(DWORD) + sizeof(DWORD)] = 0;
}
else
{
// SOCKET_ERROR or RST was received
if (!pta->run)
break;
if (ret && bVerbose)
DebugPrint(0, L"%08X: SOCKS thread(%d) Rsend() failed with errorelayToRDPChannel recv error: %ld %ld", dwThreadId, dwThreadId, ret, WSAGetLastError());
ret = 0;
memcpy(buf, &dwThreadId, sizeof(DWORD));
memcpy(buf + sizeof(DWORD), &ret, sizeof(DWORD));
buf[sizeof(DWORD) + sizeof(DWORD)] = 1;
pta->run = FALSE;
}
// wait on mutex, when it is acquired, block other threads to write
dwWaitResult = WaitForSingleObject(ghMutex, INFINITE);
switch (dwWaitResult)
{
case WAIT_OBJECT_0:
// write prefixed received data on channel
if (ta.m_ptrChannel != NULL)
if (ta.m_ptrChannel->Write(ret + sizeof(DWORD) + sizeof(DWORD) + sizeof(BYTE), (BYTE *)buf, NULL))
{
if (bVerbose) DebugPrint(0, L"Write to channel failed for some reason.");
}
if (!ReleaseMutex(ghMutex))
{
if (bVerbose) DebugPrint(0, L"Release failed");
}
break;
case WAIT_ABANDONED:
return FALSE;
}
}
DeleteThread(dwThreadId);
return 0;
}
DWORD SocksOverRDPPlugin::ListenerThread(PVOID param)
{
WSADATA wsaData;
ADDRINFOW *result = NULL;
ADDRINFOW hints;
SOCKET s, c;
HANDLE hDummyThread;
DWORD dwDummyThreadId;
int ret;
DebugPrint(0, L"[*] Setting up server socket");
if ((ret = WSAStartup(MAKEWORD(2, 2), &wsaData)) != 0)
{
DebugPrint(ret, L"WSAStartup() failed with error: %ld", ret);
return -1;
}
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_PASSIVE;
if ((ret = GetAddrInfoW(ta.running_args->ip, ta.running_args->port, &hints, &result)) != 0) {
DebugPrint(ret, L"[-] GetAddrInfoW() failed with error: %ld", ret);
return -1;
}
if ((s = socket(result->ai_family, result->ai_socktype, result->ai_protocol)) == INVALID_SOCKET) {
DebugPrint(WSAGetLastError(), L"[-] socket() failed with error: %ld", WSAGetLastError());
FreeAddrInfoW(result);
return -1;
}
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, "1", 1) == SOCKET_ERROR)
{
DebugPrint(WSAGetLastError(), L"[-] setsockopt() failed with error: %ld", WSAGetLastError());
}
ta.sockserver = s;
if ((ret = bind(s, result->ai_addr, (int)result->ai_addrlen)) == SOCKET_ERROR) {
DebugPrint(WSAGetLastError(), L"[-] bind() failed with error: %ld", WSAGetLastError());
FreeAddrInfoW(result);
closesocket(s);
return -1;
}
FreeAddrInfoW(result);
DebugPrint(0, L"[*] Listening on: %s:%s", ta.running_args->ip, ta.running_args->port);
if ((ret = listen(s, SOMAXCONN)) == SOCKET_ERROR) {
DebugPrint(0, L"[-] listen() failed with error: %ld", WSAGetLastError());
closesocket(s);
return -1;
}
// while till the RDP connection wasn't closed and
// create a new thread for all new connections
while (ta.run)
{
if ((c = accept(s, NULL, NULL)) == INVALID_SOCKET) {
DebugPrint(0, L"[-] accept() failed with error: %ld", WSAGetLastError());
return -1;
}
hDummyThread = CreateThread(
NULL,
0,
&SocksOverRDPPlugin::RelayToRDPChannel,
(void *)c,
0,
&dwDummyThreadId);
if (bVerbose) DebugPrint(0, L"[+] New thread created: %08X", dwDummyThreadId);
}
return 0;
}
// IWTSListenerCallback::OnNewChannelConnection implementation.
HRESULT SocksOverRDPPlugin::OnNewChannelConnection(__in IWTSVirtualChannel *pChannel,
__in_opt BSTR data, __out BOOL *pbAccept, __out IWTSVirtualChannelCallback **ppCallback)
{
HRESULT hr;
CComObject<SocksOverRDPPlugin> *pCallback;
CComPtr<SocksOverRDPPlugin> ptrCallback;
// Prevent C4100 "unreferenced parameter" warnings.
data;
*pbAccept = FALSE;
hr = CComObject<SocksOverRDPPlugin>::CreateInstance(&pCallback);
CHECK_QUIT_HR("SocksOverRDPPlugin::CreateInstance");
ptrCallback = pCallback;
ptrCallback->SetChannel(pChannel, &ta);
ta.running_args = &running_args;
ta.m_ptrChannel = pChannel;
ta.run = TRUE;
ghMutex = CreateMutex(NULL, FALSE, NULL);
ghLLMutex = CreateMutex(NULL, FALSE, NULL);
running_args.enabled = 0;
running_args.port = L"1080";
running_args.ip = L"127.0.0.1";
if (!GetRegistrySettings())
{
DebugPrint(-1, L"[-] Could not access the registry settings");
}
DebugPrint(0, L"[+] Starting Listener thread");
// Create main thread that will spawn other threads upon new connections
HANDLE hListenerThread = CreateThread(
NULL,
0,
&SocksOverRDPPlugin::ListenerThread,
NULL,
0,
NULL);
ta.hThread = hListenerThread;
*ppCallback = ptrCallback;
(*ppCallback)->AddRef();
*pbAccept = TRUE;
return hr;
}
VOID SocksOverRDPPlugin::SetChannel(IWTSVirtualChannel *pChannel, struct threadargs *pTa)
{
m_ptrChannel = pChannel;
gpta = pTa;
}
// When data is received from the RDP server on the channel, this function is
// getting called with the *data on stack
HRESULT STDMETHODCALLTYPE SocksOverRDPPlugin::OnDataReceived(ULONG cbSize, __in_bcount(cbSize) BYTE *pBuffer)
{
DWORD dwThreadId, ret, dwRecvdLen, cbFullSize;
BOOL bClose = FALSE, ofused = FALSE;
char *buf;
struct threads *pta;
buf = (char*)pBuffer;
cbFullSize = cbSize;
// if overflow was present from previous call, it was saved and should be
// prepended with the new data
// ofused if set, signals that szOverflow has the data instead of pBuffer
if (dwOverflow)
{
if (bVerbose) DebugPrint(0, L"Overflow was present from previous call. Stored bytes: %ld. Received bytes: %ld", dwOverflow, cbSize);
memcpy_s(szOverflow + dwOverflow, BUF_SIZE*4, pBuffer, cbSize);
buf = szOverflow;
cbFullSize = cbSize + dwOverflow;
dwOverflow = 0;
ofused = TRUE;
}
while (cbFullSize)
{
// parsing the header, getting threadId from server side, that matches
// the local threadID; recvlen which has the appended data length ;
// bClose bit which shows weather the connection will be/was closed
// on the server side.
memcpy(&dwThreadId, buf, sizeof(DWORD));
memcpy(&dwRecvdLen, buf + sizeof(DWORD), sizeof(DWORD));
if (buf[sizeof(DWORD) + sizeof(DWORD)] == 0x01) bClose = TRUE;
// finding thread based on the header. If no thread found, that is a
// problem, since there is no thread that has the associated info for
// this received data
pta = LookupThread(dwThreadId);
if (pta == NULL)
return -1;
if (bVerbose) DebugPrint(0, L"%08X: Read: %ld content length: %ld", dwThreadId, cbFullSize, dwRecvdLen);
if (dwRecvdLen <= cbFullSize - (sizeof(DWORD) + sizeof(DWORD) + sizeof(BYTE)))
{
if (bVerbose) DebugPrint(0, L"%08X: Last read, smaller or equal.", dwThreadId);
if (dwRecvdLen)
{
if (pta->s)
{
// send recvl bytes from the received data to the
// corresponding socket
if ((ret = send(pta->s, buf + sizeof(DWORD) + sizeof(DWORD) + sizeof(BYTE), dwRecvdLen, 0)) == SOCKET_ERROR)
{
// error happened, stopping thread gently and
// recalculating pointers to the next packet
StopThread(dwThreadId);
if (bVerbose) DebugPrint(0, L"%08X: send() failed with error %ld %ld", dwThreadId, ret, WSAGetLastError());
cbFullSize -= (dwRecvdLen + sizeof(DWORD) + sizeof(DWORD) + sizeof(BYTE));
buf += (dwRecvdLen + sizeof(DWORD) + sizeof(DWORD) + sizeof(BYTE));
}
else
{
// recalculating pointers to the next packet
cbFullSize -= (ret + sizeof(DWORD) + sizeof(DWORD) + sizeof(BYTE));
buf += (ret + sizeof(DWORD) + sizeof(DWORD) + sizeof(BYTE));
if (bVerbose) DebugPrint(0, L"%08X: Sent on socket: %ld. cbFullSize: %ld", dwThreadId, ret, cbFullSize);
}
}
}
// bClose bit was set on the server side. Closing down connection
if (bClose)
{
StopThread(dwThreadId);
if (pta->s)
shutdown(pta->s, SD_SEND);
// only RST was sent from server side
if (!dwRecvdLen)
{
cbFullSize -= sizeof(DWORD) + sizeof(DWORD) + sizeof(BYTE);
buf += sizeof(DWORD) + sizeof(DWORD) + sizeof(BYTE);
}
}
}
else
{
// data remained on the buffer, less than specified in the header
if (bVerbose) DebugPrint(0, L"!!! OVERFLOW HAPPENED %ld > %ld", dwRecvdLen, cbFullSize);
if (ofused)
{
// ofused set, memmove needed
memmove_s(szOverflow, BUF_SIZE * 4, buf, cbFullSize);
dwOverflow = cbFullSize;
}
else
{
memcpy_s(szOverflow, BUF_SIZE * 4, buf, cbFullSize);
dwOverflow = cbFullSize;
}
// not enough data to send, need to get more, stopping loop
cbFullSize = 0;
}
}
return S_OK;
}