2012-11-28 21:47:04 +04:00
|
|
|
/**
|
|
|
|
* FreeRDP: A Remote Desktop Protocol Implementation
|
|
|
|
* RPC Client
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
#include "config.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#include <winpr/crt.h>
|
|
|
|
#include <winpr/synch.h>
|
|
|
|
#include <winpr/thread.h>
|
2012-12-11 00:43:07 +04:00
|
|
|
#include <winpr/stream.h>
|
2012-11-28 21:47:04 +04:00
|
|
|
|
|
|
|
#include "rpc_client.h"
|
|
|
|
|
2012-12-11 03:56:53 +04:00
|
|
|
int rpc_client_on_read_event(rdpRpc* rpc)
|
2012-12-11 00:43:07 +04:00
|
|
|
{
|
|
|
|
int position;
|
|
|
|
int status = -1;
|
|
|
|
rpcconn_common_hdr_t* header;
|
|
|
|
|
|
|
|
position = Stream_Position(rpc->RecvFrag);
|
|
|
|
|
|
|
|
if (Stream_Position(rpc->RecvFrag) < RPC_COMMON_FIELDS_LENGTH)
|
|
|
|
{
|
|
|
|
status = rpc_out_read(rpc, Stream_Pointer(rpc->RecvFrag),
|
|
|
|
RPC_COMMON_FIELDS_LENGTH - Stream_Position(rpc->RecvFrag));
|
|
|
|
|
|
|
|
if (status < 0)
|
|
|
|
{
|
|
|
|
printf("rpc_client_frag_read: error reading header\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
Stream_Seek(rpc->RecvFrag, status);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Stream_Position(rpc->RecvFrag) >= RPC_COMMON_FIELDS_LENGTH)
|
|
|
|
{
|
|
|
|
header = (rpcconn_common_hdr_t*) Stream_Buffer(rpc->RecvFrag);
|
|
|
|
|
|
|
|
if (header->frag_length > rpc->max_recv_frag)
|
|
|
|
{
|
|
|
|
printf("rpc_client_frag_read: invalid fragment size: %d (max: %d)\n",
|
|
|
|
header->frag_length, rpc->max_recv_frag);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Stream_Position(rpc->RecvFrag) < header->frag_length)
|
|
|
|
{
|
|
|
|
status = rpc_out_read(rpc, Stream_Pointer(rpc->RecvFrag),
|
|
|
|
header->frag_length - Stream_Position(rpc->RecvFrag));
|
|
|
|
|
|
|
|
if (status < 0)
|
|
|
|
{
|
|
|
|
printf("rpc_client_frag_read: error reading fragment body\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
Stream_Seek(rpc->RecvFrag, status);
|
|
|
|
}
|
|
|
|
}
|
2012-12-11 03:56:53 +04:00
|
|
|
else
|
|
|
|
{
|
|
|
|
return status;
|
|
|
|
}
|
2012-12-11 00:43:07 +04:00
|
|
|
|
|
|
|
if (status < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
status = Stream_Position(rpc->RecvFrag) - position;
|
|
|
|
|
|
|
|
if (Stream_Position(rpc->RecvFrag) >= header->frag_length)
|
|
|
|
{
|
|
|
|
/* complete fragment received */
|
2012-12-11 03:56:53 +04:00
|
|
|
|
|
|
|
Stream_Length(rpc->RecvFrag) = Stream_Position(rpc->RecvFrag);
|
|
|
|
Stream_SetPosition(rpc->RecvFrag, 0);
|
|
|
|
|
|
|
|
Queue_Enqueue(rpc->client->FragmentQueue, rpc->RecvFrag);
|
|
|
|
rpc->RecvFrag = Stream_New(NULL, rpc->max_recv_frag);
|
2012-12-11 00:43:07 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2012-12-08 06:09:55 +04:00
|
|
|
/**
|
|
|
|
* [MS-RPCE] Client Call:
|
|
|
|
* http://msdn.microsoft.com/en-us/library/gg593159/
|
|
|
|
*/
|
|
|
|
|
|
|
|
RpcClientCall* rpc_client_call_find_by_id(rdpRpc* rpc, UINT32 CallId)
|
|
|
|
{
|
|
|
|
int index;
|
|
|
|
int count;
|
|
|
|
RpcClientCall* client_call;
|
|
|
|
|
|
|
|
ArrayList_Lock(rpc->ClientCalls);
|
|
|
|
|
|
|
|
client_call = NULL;
|
|
|
|
count = ArrayList_Count(rpc->ClientCalls);
|
|
|
|
|
|
|
|
for (index = 0; index < count; index++)
|
|
|
|
{
|
|
|
|
client_call = (RpcClientCall*) ArrayList_GetItem(rpc->ClientCalls, index);
|
|
|
|
|
|
|
|
if (client_call->CallId == CallId)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
ArrayList_Unlock(rpc->ClientCalls);
|
|
|
|
|
|
|
|
return client_call;
|
|
|
|
}
|
|
|
|
|
|
|
|
RpcClientCall* rpc_client_call_new(UINT32 CallId, UINT32 OpNum)
|
|
|
|
{
|
|
|
|
RpcClientCall* client_call;
|
|
|
|
|
|
|
|
client_call = (RpcClientCall*) malloc(sizeof(RpcClientCall));
|
|
|
|
|
|
|
|
if (client_call)
|
|
|
|
{
|
|
|
|
client_call->CallId = CallId;
|
|
|
|
client_call->OpNum = OpNum;
|
|
|
|
client_call->State = RPC_CLIENT_CALL_STATE_SEND_PDUS;
|
|
|
|
}
|
|
|
|
|
|
|
|
return client_call;
|
|
|
|
}
|
|
|
|
|
|
|
|
void rpc_client_call_free(RpcClientCall* client_call)
|
|
|
|
{
|
|
|
|
free(client_call);
|
|
|
|
}
|
|
|
|
|
2012-11-28 21:47:04 +04:00
|
|
|
int rpc_send_enqueue_pdu(rdpRpc* rpc, BYTE* buffer, UINT32 length)
|
|
|
|
{
|
2012-11-29 05:30:03 +04:00
|
|
|
RPC_PDU* pdu;
|
2012-11-28 21:47:04 +04:00
|
|
|
|
2012-12-06 01:04:01 +04:00
|
|
|
pdu = (RPC_PDU*) malloc(sizeof(RPC_PDU));
|
2012-11-29 05:30:03 +04:00
|
|
|
pdu->Buffer = buffer;
|
|
|
|
pdu->Length = length;
|
2012-11-28 21:47:04 +04:00
|
|
|
|
2012-12-06 01:04:01 +04:00
|
|
|
Queue_Enqueue(rpc->SendQueue, pdu);
|
2012-11-28 21:47:04 +04:00
|
|
|
|
2012-11-29 05:30:03 +04:00
|
|
|
if (rpc->client->SynchronousSend)
|
|
|
|
{
|
|
|
|
WaitForSingleObject(rpc->client->PduSentEvent, INFINITE);
|
|
|
|
ResetEvent(rpc->client->PduSentEvent);
|
|
|
|
}
|
|
|
|
|
2012-11-28 21:47:04 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int rpc_send_dequeue_pdu(rdpRpc* rpc)
|
|
|
|
{
|
|
|
|
int status;
|
2012-11-29 05:30:03 +04:00
|
|
|
RPC_PDU* pdu;
|
2012-12-08 06:09:55 +04:00
|
|
|
RpcClientCall* client_call;
|
|
|
|
rpcconn_common_hdr_t* header;
|
2012-11-28 21:47:04 +04:00
|
|
|
|
2012-12-06 01:04:01 +04:00
|
|
|
pdu = (RPC_PDU*) Queue_Dequeue(rpc->SendQueue);
|
2012-11-28 21:47:04 +04:00
|
|
|
|
2012-11-29 05:30:03 +04:00
|
|
|
if (!pdu)
|
2012-11-28 22:38:01 +04:00
|
|
|
return 0;
|
|
|
|
|
2012-11-29 05:30:03 +04:00
|
|
|
WaitForSingleObject(rpc->VirtualConnection->DefaultInChannel->Mutex, INFINITE);
|
|
|
|
|
|
|
|
status = rpc_in_write(rpc, pdu->Buffer, pdu->Length);
|
|
|
|
|
2012-12-08 06:09:55 +04:00
|
|
|
header = (rpcconn_common_hdr_t*) pdu->Buffer;
|
|
|
|
client_call = rpc_client_call_find_by_id(rpc, header->call_id);
|
|
|
|
client_call->State = RPC_CLIENT_CALL_STATE_DISPATCHED;
|
|
|
|
|
2012-11-29 05:30:03 +04:00
|
|
|
ReleaseMutex(rpc->VirtualConnection->DefaultInChannel->Mutex);
|
2012-11-28 21:47:04 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* This protocol specifies that only RPC PDUs are subject to the flow control abstract
|
|
|
|
* data model. RTS PDUs and the HTTP request and response headers are not subject to flow control.
|
|
|
|
* Implementations of this protocol MUST NOT include them when computing any of the variables
|
|
|
|
* specified by this abstract data model.
|
|
|
|
*/
|
|
|
|
rpc->VirtualConnection->DefaultInChannel->BytesSent += status;
|
|
|
|
rpc->VirtualConnection->DefaultInChannel->SenderAvailableWindow -= status;
|
|
|
|
|
2012-11-29 05:30:03 +04:00
|
|
|
free(pdu->Buffer);
|
2012-12-06 01:04:01 +04:00
|
|
|
free(pdu);
|
2012-11-28 21:47:04 +04:00
|
|
|
|
2012-11-29 05:30:03 +04:00
|
|
|
if (rpc->client->SynchronousSend)
|
|
|
|
SetEvent(rpc->client->PduSentEvent);
|
2012-11-28 22:38:01 +04:00
|
|
|
|
2012-11-28 21:47:04 +04:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2012-11-29 05:30:03 +04:00
|
|
|
int rpc_recv_enqueue_pdu(rdpRpc* rpc)
|
|
|
|
{
|
|
|
|
RPC_PDU* pdu;
|
|
|
|
|
2012-11-29 06:25:01 +04:00
|
|
|
pdu = rpc_recv_pdu(rpc);
|
2012-11-29 05:30:03 +04:00
|
|
|
|
2012-11-29 06:25:01 +04:00
|
|
|
if (!pdu)
|
2012-12-08 06:09:55 +04:00
|
|
|
return 0;
|
2012-11-29 05:30:03 +04:00
|
|
|
|
2012-12-06 01:04:01 +04:00
|
|
|
rpc->pdu = (RPC_PDU*) malloc(sizeof(RPC_PDU));
|
2012-11-29 05:30:03 +04:00
|
|
|
|
|
|
|
if (pdu->Flags & RPC_PDU_FLAG_STUB)
|
|
|
|
{
|
|
|
|
rpc->StubBufferSize = rpc->max_recv_frag;
|
|
|
|
rpc->StubBuffer = (BYTE*) malloc(rpc->StubBufferSize);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
rpc->FragBufferSize = rpc->max_recv_frag;
|
|
|
|
rpc->FragBuffer = (BYTE*) malloc(rpc->FragBufferSize);
|
|
|
|
}
|
|
|
|
|
2012-12-06 01:04:01 +04:00
|
|
|
Queue_Enqueue(rpc->ReceiveQueue, pdu);
|
2012-11-29 05:30:03 +04:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-11-29 07:03:18 +04:00
|
|
|
RPC_PDU* rpc_recv_dequeue_pdu(rdpRpc* rpc)
|
2012-11-29 05:30:03 +04:00
|
|
|
{
|
2012-11-29 07:03:18 +04:00
|
|
|
RPC_PDU* pdu;
|
|
|
|
DWORD dwMilliseconds;
|
|
|
|
|
|
|
|
pdu = NULL;
|
|
|
|
dwMilliseconds = rpc->client->SynchronousReceive ? INFINITE : 0;
|
|
|
|
|
2012-11-29 05:30:03 +04:00
|
|
|
if (rpc->client->SynchronousReceive)
|
2012-11-29 07:03:18 +04:00
|
|
|
rpc_recv_enqueue_pdu(rpc);
|
2012-11-29 05:30:03 +04:00
|
|
|
|
2012-12-11 03:56:53 +04:00
|
|
|
if (WaitForSingleObject(Queue_Event(rpc->ReceiveQueue), dwMilliseconds) == WAIT_OBJECT_0)
|
2012-11-29 07:03:18 +04:00
|
|
|
{
|
2012-12-06 01:04:01 +04:00
|
|
|
pdu = (RPC_PDU*) Queue_Dequeue(rpc->ReceiveQueue);
|
2012-11-29 07:03:18 +04:00
|
|
|
return pdu;
|
|
|
|
}
|
|
|
|
|
|
|
|
return pdu;
|
2012-11-29 05:30:03 +04:00
|
|
|
}
|
|
|
|
|
2012-11-28 21:47:04 +04:00
|
|
|
static void* rpc_client_thread(void* arg)
|
|
|
|
{
|
|
|
|
rdpRpc* rpc;
|
|
|
|
DWORD status;
|
2012-11-28 22:38:01 +04:00
|
|
|
DWORD nCount;
|
2012-11-29 05:30:03 +04:00
|
|
|
HANDLE events[3];
|
|
|
|
HANDLE ReadEvent;
|
2012-11-28 21:47:04 +04:00
|
|
|
|
|
|
|
rpc = (rdpRpc*) arg;
|
|
|
|
|
2012-11-29 05:30:03 +04:00
|
|
|
ReadEvent = CreateFileDescriptorEvent(NULL, TRUE, FALSE, rpc->TlsOut->sockfd);
|
|
|
|
|
2012-11-28 22:38:01 +04:00
|
|
|
nCount = 0;
|
|
|
|
events[nCount++] = rpc->client->StopEvent;
|
2012-12-11 03:56:53 +04:00
|
|
|
events[nCount++] = Queue_Event(rpc->SendQueue);
|
2012-11-29 05:30:03 +04:00
|
|
|
events[nCount++] = ReadEvent;
|
2012-11-28 21:47:04 +04:00
|
|
|
|
|
|
|
while (1)
|
|
|
|
{
|
2012-11-28 22:38:01 +04:00
|
|
|
status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
|
2012-11-28 21:47:04 +04:00
|
|
|
|
|
|
|
if (WaitForSingleObject(rpc->client->StopEvent, 0) == WAIT_OBJECT_0)
|
2012-11-29 05:30:03 +04:00
|
|
|
{
|
2012-11-28 21:47:04 +04:00
|
|
|
break;
|
2012-11-29 05:30:03 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (WaitForSingleObject(ReadEvent, 0) == WAIT_OBJECT_0)
|
|
|
|
{
|
2012-12-11 03:56:53 +04:00
|
|
|
rpc_client_on_read_event(rpc);
|
|
|
|
|
2012-11-29 07:03:18 +04:00
|
|
|
if (!rpc->client->SynchronousReceive)
|
|
|
|
rpc_recv_enqueue_pdu(rpc);
|
2012-11-29 05:30:03 +04:00
|
|
|
}
|
2012-11-28 21:47:04 +04:00
|
|
|
|
2012-12-11 03:56:53 +04:00
|
|
|
if (WaitForSingleObject(Queue_Event(rpc->SendQueue), 0) == WAIT_OBJECT_0)
|
|
|
|
{
|
|
|
|
rpc_send_dequeue_pdu(rpc);
|
|
|
|
}
|
2012-11-28 21:47:04 +04:00
|
|
|
}
|
|
|
|
|
2012-11-29 05:30:03 +04:00
|
|
|
CloseHandle(ReadEvent);
|
|
|
|
|
2012-11-28 21:47:04 +04:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2012-11-29 05:30:03 +04:00
|
|
|
int rpc_client_new(rdpRpc* rpc)
|
2012-11-28 21:47:04 +04:00
|
|
|
{
|
|
|
|
rpc->client = (RpcClient*) malloc(sizeof(RpcClient));
|
|
|
|
|
|
|
|
rpc->client->Thread = CreateThread(NULL, 0,
|
|
|
|
(LPTHREAD_START_ROUTINE) rpc_client_thread,
|
|
|
|
rpc, CREATE_SUSPENDED, NULL);
|
|
|
|
|
|
|
|
rpc->client->StopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
2012-11-28 22:38:01 +04:00
|
|
|
rpc->client->PduSentEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
2012-11-28 21:47:04 +04:00
|
|
|
|
2012-12-11 03:56:53 +04:00
|
|
|
rpc->client->FragmentQueue = Queue_New(TRUE, -1, -1);
|
|
|
|
|
2012-11-29 05:30:03 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int rpc_client_start(rdpRpc* rpc)
|
|
|
|
{
|
2012-11-28 21:47:04 +04:00
|
|
|
ResumeThread(rpc->client->Thread);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|