FreeRDP/libfreerdp/core/gateway/rpc.c

777 lines
21 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* FreeRDP: A Remote Desktop Protocol Implementation
* RPC over HTTP
*
* Copyright 2012 Fujitsu Technology Solutions GmbH
* Copyright 2012 Dmitrij Jasnov <dmitrij.jasnov@ts.fujitsu.com>
* 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/tchar.h>
#include <winpr/synch.h>
#include <winpr/dsparse.h>
#include <openssl/rand.h>
#include "http.h"
#include "ntlm.h"
#include "ncacn_http.h"
#include "rpc_bind.h"
#include "rpc_fault.h"
#include "rpc_client.h"
#include "rpc.h"
/* Security Verification Trailer Signature */
rpc_sec_verification_trailer RPC_SEC_VERIFICATION_TRAILER =
{ { 0x8a, 0xe3, 0x13, 0x71, 0x02, 0xf4, 0x36, 0x71 } };
static char* PTYPE_STRINGS[] =
{
"PTYPE_REQUEST",
"PTYPE_PING",
"PTYPE_RESPONSE",
"PTYPE_FAULT",
"PTYPE_WORKING",
"PTYPE_NOCALL",
"PTYPE_REJECT",
"PTYPE_ACK",
"PTYPE_CL_CANCEL",
"PTYPE_FACK",
"PTYPE_CANCEL_ACK",
"PTYPE_BIND",
"PTYPE_BIND_ACK",
"PTYPE_BIND_NAK",
"PTYPE_ALTER_CONTEXT",
"PTYPE_ALTER_CONTEXT_RESP",
"PTYPE_RPC_AUTH_3",
"PTYPE_SHUTDOWN",
"PTYPE_CO_CANCEL",
"PTYPE_ORPHANED",
"PTYPE_RTS",
""
};
const RPC_SECURITY_PROVIDER_INFO RPC_SECURITY_PROVIDER_INFO_TABLE[] =
{
{ RPC_C_AUTHN_NONE, TRUE, -1 },
{ RPC_C_AUTHN_GSS_NEGOTIATE, TRUE, -1 },
{ RPC_C_AUTHN_WINNT, FALSE, 3 },
{ RPC_C_AUTHN_GSS_SCHANNEL, TRUE, -1 },
{ RPC_C_AUTHN_GSS_KERBEROS, TRUE, -1 },
{ RPC_C_AUTHN_DEFAULT, -1, -1 },
{ 0, -1, -1 }
};
void rpc_pdu_header_print(rpcconn_hdr_t* header)
{
printf("rpc_vers: %d\n", header->common.rpc_vers);
printf("rpc_vers_minor: %d\n", header->common.rpc_vers_minor);
if (header->common.ptype > PTYPE_RTS)
printf("ptype: %s (%d)\n", "PTYPE_UNKNOWN", header->common.ptype);
else
printf("ptype: %s (%d)\n", PTYPE_STRINGS[header->common.ptype], header->common.ptype);
printf("pfc_flags (0x%02X) = {", header->common.pfc_flags);
if (header->common.pfc_flags & PFC_FIRST_FRAG)
printf(" PFC_FIRST_FRAG");
if (header->common.pfc_flags & PFC_LAST_FRAG)
printf(" PFC_LAST_FRAG");
if (header->common.pfc_flags & PFC_PENDING_CANCEL)
printf(" PFC_PENDING_CANCEL");
if (header->common.pfc_flags & PFC_RESERVED_1)
printf(" PFC_RESERVED_1");
if (header->common.pfc_flags & PFC_CONC_MPX)
printf(" PFC_CONC_MPX");
if (header->common.pfc_flags & PFC_DID_NOT_EXECUTE)
printf(" PFC_DID_NOT_EXECUTE");
if (header->common.pfc_flags & PFC_OBJECT_UUID)
printf(" PFC_OBJECT_UUID");
printf(" }\n");
printf("packed_drep[4]: %02X %02X %02X %02X\n",
header->common.packed_drep[0], header->common.packed_drep[1],
header->common.packed_drep[2], header->common.packed_drep[3]);
printf("frag_length: %d\n", header->common.frag_length);
printf("auth_length: %d\n", header->common.auth_length);
printf("call_id: %d\n", header->common.call_id);
if (header->common.ptype == PTYPE_RESPONSE)
{
printf("alloc_hint: %d\n", header->response.alloc_hint);
printf("p_cont_id: %d\n", header->response.p_cont_id);
printf("cancel_count: %d\n", header->response.cancel_count);
printf("reserved: %d\n", header->response.reserved);
}
}
void rpc_pdu_header_init(rdpRpc* rpc, rpcconn_hdr_t* header)
{
header->common.rpc_vers = rpc->rpc_vers;
header->common.rpc_vers_minor = rpc->rpc_vers_minor;
header->common.packed_drep[0] = rpc->packed_drep[0];
header->common.packed_drep[1] = rpc->packed_drep[1];
header->common.packed_drep[2] = rpc->packed_drep[2];
header->common.packed_drep[3] = rpc->packed_drep[3];
}
UINT32 rpc_offset_align(UINT32* offset, UINT32 alignment)
{
UINT32 pad;
pad = *offset;
*offset = (*offset + alignment - 1) & ~(alignment - 1);
pad = *offset - pad;
return pad;
}
UINT32 rpc_offset_pad(UINT32* offset, UINT32 pad)
{
*offset += pad;
return pad;
}
/**
* PDU Structure with verification trailer
*
* MUST only appear in a request PDU!
* ________________________________
* | |
* | PDU Header |
* |________________________________| _______
* | | /|\
* | | |
* | Stub Data | |
* | | |
* |________________________________| |
* | | PDU Body
* | Stub Pad | |
* |________________________________| |
* | | |
* | Verification Trailer | |
* |________________________________| |
* | | |
* | Authentication Pad | |
* |________________________________| __\|/__
* | |
* | Security Trailer |
* |________________________________|
* | |
* | Authentication Token |
* |________________________________|
*
*/
BOOL rpc_get_stub_data_info(rdpRpc* rpc, BYTE* buffer, UINT32* offset, UINT32* length)
{
UINT32 alloc_hint = 0;
rpcconn_hdr_t* header;
*offset = RPC_COMMON_FIELDS_LENGTH;
header = ((rpcconn_hdr_t*) buffer);
if (header->common.ptype == PTYPE_RESPONSE)
{
*offset += 4;
rpc_offset_align(offset, 8);
alloc_hint = header->response.alloc_hint;
}
else if (header->common.ptype == PTYPE_REQUEST)
{
*offset += 4;
rpc_offset_align(offset, 8);
alloc_hint = header->request.alloc_hint;
}
else if (header->common.ptype == PTYPE_RTS)
{
*offset += 4;
}
else
{
return FALSE;
}
if (length)
{
if (header->common.ptype == PTYPE_REQUEST)
{
UINT32 sec_trailer_offset;
/**
* All PDUs that carry sec_trailer information share certain common fields:
* frag_length and auth_length. The beginning of the sec_trailer structure
* for each PDU MUST be calculated to start from offset
* (frag_length auth_length 8) from the beginning of the PDU.
*/
sec_trailer_offset = header->common.frag_length - header->common.auth_length - 8;
*length = sec_trailer_offset - *offset;
}
else
{
BYTE auth_pad_length;
auth_pad_length = *(buffer + header->common.frag_length - header->common.auth_length - 6);
*length = header->common.frag_length - (header->common.auth_length + *offset + 8 + auth_pad_length);
}
}
return TRUE;
}
int rpc_out_read(rdpRpc* rpc, BYTE* data, int length)
{
int status;
status = tls_read(rpc->TlsOut, data, length);
return status;
}
int rpc_out_write(rdpRpc* rpc, BYTE* data, int length)
{
int status;
status = tls_write_all(rpc->TlsOut, data, length);
return status;
}
int rpc_in_write(rdpRpc* rpc, BYTE* data, int length)
{
int status;
#ifdef WITH_DEBUG_TSG
rpc_pdu_header_print((rpcconn_hdr_t*) data);
printf("Sending PDU (length: %d)\n", FragBufferSize);
freerdp_hexdump(data, FragBufferSize);
#endif
status = tls_write_all(rpc->TlsIn, data, length);
return status;
}
int rpc_recv_pdu_header(rdpRpc* rpc, BYTE* header)
{
int status;
int bytesRead;
UINT32 offset;
/* Read common header fields */
bytesRead = 0;
while (bytesRead < RPC_COMMON_FIELDS_LENGTH)
{
status = rpc_out_read(rpc, &header[bytesRead], RPC_COMMON_FIELDS_LENGTH - bytesRead);
if (status < 0)
{
printf("rpc_recv_pdu_header: error reading header\n");
return status;
}
bytesRead += status;
}
rpc_get_stub_data_info(rpc, header, &offset, NULL);
while (bytesRead < offset)
{
status = rpc_out_read(rpc, &header[bytesRead], offset - bytesRead);
if (status < 0)
return status;
bytesRead += status;
}
return bytesRead;
}
int rpc_recv_pdu_fragment(rdpRpc* rpc)
{
int status;
int headerLength;
int bytesRead = 0;
rpcconn_hdr_t* header;
WaitForSingleObject(rpc->VirtualConnection->DefaultInChannel->Mutex, INFINITE);
status = rpc_recv_pdu_header(rpc, rpc->FragBuffer);
if (status < 1)
{
printf("rpc_recv_pdu_header: error reading header\n");
return status;
}
headerLength = status;
header = (rpcconn_hdr_t*) rpc->FragBuffer;
bytesRead += status;
if (header->common.frag_length > rpc->FragBufferSize)
{
rpc->FragBufferSize = header->common.frag_length;
rpc->FragBuffer = (BYTE*) realloc(rpc->FragBuffer, rpc->FragBufferSize);
header = (rpcconn_hdr_t*) rpc->FragBuffer;
}
while (bytesRead < header->common.frag_length)
{
status = rpc_out_read(rpc, &rpc->FragBuffer[bytesRead], header->common.frag_length - bytesRead);
if (status < 0)
{
printf("rpc_recv_pdu: error reading fragment\n");
return status;
}
bytesRead += status;
}
ReleaseMutex(rpc->VirtualConnection->DefaultInChannel->Mutex);
if (header->common.ptype == PTYPE_RTS) /* RTS PDU */
{
if (rpc->VirtualConnection->State < VIRTUAL_CONNECTION_STATE_OPENED)
return header->common.frag_length;
printf("Receiving Out-of-Sequence RTS PDU\n");
rts_recv_out_of_sequence_pdu(rpc, rpc->FragBuffer, header->common.frag_length);
return rpc_recv_pdu_fragment(rpc);
}
else if (header->common.ptype == PTYPE_FAULT)
{
rpc_recv_fault_pdu(header);
return -1;
}
rpc->VirtualConnection->DefaultOutChannel->BytesReceived += header->common.frag_length;
rpc->VirtualConnection->DefaultOutChannel->ReceiverAvailableWindow -= header->common.frag_length;
#if 0
printf("BytesReceived: %d ReceiverAvailableWindow: %d ReceiveWindow: %d\n",
rpc->VirtualConnection->DefaultOutChannel->BytesReceived,
rpc->VirtualConnection->DefaultOutChannel->ReceiverAvailableWindow,
rpc->ReceiveWindow);
#endif
if (rpc->VirtualConnection->DefaultOutChannel->ReceiverAvailableWindow < (rpc->ReceiveWindow / 2))
{
printf("Sending Flow Control Ack PDU\n");
rts_send_flow_control_ack_pdu(rpc);
}
#ifdef WITH_DEBUG_RPC
rpc_pdu_header_print((rpcconn_hdr_t*) header);
printf("rpc_recv_pdu_fragment: length: %d\n", header->common.frag_length);
freerdp_hexdump(rpc->FragBuffer, header->common.frag_length);
printf("\n");
#endif
return header->common.frag_length;
}
RPC_PDU* rpc_recv_pdu(rdpRpc* rpc)
{
int status;
UINT32 StubOffset;
UINT32 StubLength;
rpcconn_hdr_t* header;
status = rpc_recv_pdu_fragment(rpc);
if (rpc->State < RPC_CLIENT_STATE_CONTEXT_NEGOTIATED)
{
rpc->pdu->Flags = 0;
rpc->pdu->Buffer = rpc->FragBuffer;
rpc->pdu->Size = rpc->FragBufferSize;
rpc->pdu->Length = status;
return rpc->pdu;
}
header = (rpcconn_hdr_t*) rpc->FragBuffer;
if (header->common.ptype != PTYPE_RESPONSE)
{
printf("rpc_recv_pdu: unexpected ptype 0x%02X\n", header->common.ptype);
return NULL;
}
if (!rpc_get_stub_data_info(rpc, rpc->FragBuffer, &StubOffset, &StubLength))
{
printf("rpc_recv_pdu: expected stub\n");
return NULL;
}
if (header->response.alloc_hint > rpc->StubBufferSize)
{
rpc->StubBufferSize = header->response.alloc_hint;
rpc->StubBuffer = (BYTE*) realloc(rpc->StubBuffer, rpc->StubBufferSize);
}
CopyMemory(&rpc->StubBuffer[rpc->StubOffset], &rpc->FragBuffer[StubOffset], StubLength);
rpc->StubOffset += StubLength;
rpc->StubFragCount++;
/**
* If alloc_hint is set to a nonzero value and a request or a response is fragmented into multiple
* PDUs, implementations of these extensions SHOULD set the alloc_hint field in every PDU to be the
* combined stub data length of all remaining fragment PDUs.
*/
//printf("Receiving Fragment #%d FragStubLength: %d FragLength: %d AllocHint: %d StubOffset: %d\n",
// rpc->StubFragCount, StubLength, header->common.frag_length, header->response.alloc_hint, rpc->StubOffset);
if ((header->response.alloc_hint == StubLength))
{
//printf("Reassembled PDU (%d):\n", rpc->StubOffset);
//freerdp_hexdump(rpc->StubBuffer, rpc->StubOffset);
rpc->StubLength = rpc->StubOffset;
rpc->StubOffset = 0;
rpc->StubFragCount = 0;
rpc->pdu->Flags = RPC_PDU_FLAG_STUB;
rpc->pdu->Buffer = rpc->StubBuffer;
rpc->pdu->Size = rpc->StubBufferSize;
rpc->pdu->Length = rpc->StubLength;
return rpc->pdu;
}
return rpc_recv_pdu(rpc);
}
int rpc_write(rdpRpc* rpc, BYTE* data, int length, UINT16 opnum)
{
BYTE* buffer;
UINT32 offset;
rdpNtlm* ntlm;
UINT32 stub_data_pad;
SecBuffer Buffers[2];
SecBufferDesc Message;
SECURITY_STATUS encrypt_status;
rpcconn_request_hdr_t* request_pdu;
ntlm = rpc->ntlm;
if (ntlm->table->QueryContextAttributes(&ntlm->context, SECPKG_ATTR_SIZES, &ntlm->ContextSizes) != SEC_E_OK)
{
printf("QueryContextAttributes SECPKG_ATTR_SIZES failure\n");
return -1;
}
request_pdu = (rpcconn_request_hdr_t*) malloc(sizeof(rpcconn_request_hdr_t));
ZeroMemory(request_pdu, sizeof(rpcconn_request_hdr_t));
rpc_pdu_header_init(rpc, (rpcconn_hdr_t*) request_pdu);
request_pdu->ptype = PTYPE_REQUEST;
request_pdu->pfc_flags = PFC_FIRST_FRAG | PFC_LAST_FRAG;
request_pdu->auth_length = ntlm->ContextSizes.cbMaxSignature;
request_pdu->call_id = ++rpc->call_id;
/* opnum 8 is TsProxySetupReceivePipe, save call_id for checking pipe responses */
if (opnum == 8)
rpc->pipe_call_id = rpc->call_id;
request_pdu->alloc_hint = length;
request_pdu->p_cont_id = 0x0000;
request_pdu->opnum = opnum;
request_pdu->stub_data = data;
offset = 24;
stub_data_pad = 0;
stub_data_pad = rpc_offset_align(&offset, 8);
offset += length;
request_pdu->auth_verifier.auth_pad_length = rpc_offset_align(&offset, 4);
request_pdu->auth_verifier.auth_type = RPC_C_AUTHN_WINNT;
request_pdu->auth_verifier.auth_level = RPC_C_AUTHN_LEVEL_PKT_INTEGRITY;
request_pdu->auth_verifier.auth_reserved = 0x00;
request_pdu->auth_verifier.auth_context_id = 0x00000000;
offset += (8 + request_pdu->auth_length);
request_pdu->frag_length = offset;
buffer = (BYTE*) malloc(request_pdu->frag_length);
CopyMemory(buffer, request_pdu, 24);
offset = 24;
rpc_offset_pad(&offset, stub_data_pad);
CopyMemory(&buffer[offset], request_pdu->stub_data, length);
offset += length;
rpc_offset_pad(&offset, request_pdu->auth_verifier.auth_pad_length);
CopyMemory(&buffer[offset], &request_pdu->auth_verifier.auth_type, 8);
offset += 8;
Buffers[0].BufferType = SECBUFFER_DATA; /* auth_data */
Buffers[1].BufferType = SECBUFFER_TOKEN; /* signature */
Buffers[0].pvBuffer = buffer;
Buffers[0].cbBuffer = offset;
Buffers[1].cbBuffer = ntlm->ContextSizes.cbMaxSignature;
Buffers[1].pvBuffer = malloc(Buffers[1].cbBuffer);
ZeroMemory(Buffers[1].pvBuffer, Buffers[1].cbBuffer);
Message.cBuffers = 2;
Message.ulVersion = SECBUFFER_VERSION;
Message.pBuffers = (PSecBuffer) &Buffers;
encrypt_status = ntlm->table->EncryptMessage(&ntlm->context, 0, &Message, rpc->send_seq_num++);
if (encrypt_status != SEC_E_OK)
{
printf("EncryptMessage status: 0x%08X\n", encrypt_status);
return -1;
}
CopyMemory(&buffer[offset], Buffers[1].pvBuffer, Buffers[1].cbBuffer);
offset += Buffers[1].cbBuffer;
rpc_send_enqueue_pdu(rpc, buffer, request_pdu->frag_length);
return length;
}
int rpc_recv(rdpRpc* rpc, RPC_PDU* pdu)
{
return 0;
}
BOOL rpc_connect(rdpRpc* rpc)
{
rpc->TlsIn = rpc->transport->TlsIn;
rpc->TlsOut = rpc->transport->TlsOut;
rpc_client_start(rpc);
if (!rts_connect(rpc))
{
printf("rts_connect error!\n");
return FALSE;
}
rpc->State = RPC_CLIENT_STATE_ESTABLISHED;
if (rpc_secure_bind(rpc) != 0)
{
printf("rpc_secure_bind error!\n");
return FALSE;
}
return TRUE;
}
void rpc_client_virtual_connection_init(rdpRpc* rpc, RpcVirtualConnection* connection)
{
connection->DefaultInChannel->State = CLIENT_IN_CHANNEL_STATE_INITIAL;
connection->DefaultInChannel->BytesSent = 0;
connection->DefaultInChannel->SenderAvailableWindow = rpc->ReceiveWindow;
connection->DefaultInChannel->PingOriginator.ConnectionTimeout = 30;
connection->DefaultInChannel->PingOriginator.KeepAliveInterval = 0;
connection->DefaultInChannel->Mutex = CreateMutex(NULL, FALSE, NULL);
connection->DefaultOutChannel->State = CLIENT_OUT_CHANNEL_STATE_INITIAL;
connection->DefaultOutChannel->BytesReceived = 0;
connection->DefaultOutChannel->ReceiverAvailableWindow = rpc->ReceiveWindow;
connection->DefaultOutChannel->ReceiveWindow = rpc->ReceiveWindow;
connection->DefaultOutChannel->ReceiveWindowSize = rpc->ReceiveWindow;
connection->DefaultOutChannel->AvailableWindowAdvertised = rpc->ReceiveWindow;
connection->DefaultOutChannel->Mutex = CreateMutex(NULL, FALSE, NULL);
}
RpcVirtualConnection* rpc_client_virtual_connection_new(rdpRpc* rpc)
{
RpcVirtualConnection* connection = (RpcVirtualConnection*) malloc(sizeof(RpcVirtualConnection));
if (connection != NULL)
{
ZeroMemory(connection, sizeof(RpcVirtualConnection));
connection->State = VIRTUAL_CONNECTION_STATE_INITIAL;
connection->DefaultInChannel = (RpcInChannel*) malloc(sizeof(RpcInChannel));
connection->DefaultOutChannel = (RpcOutChannel*) malloc(sizeof(RpcOutChannel));
ZeroMemory(connection->DefaultInChannel, sizeof(RpcInChannel));
ZeroMemory(connection->DefaultOutChannel, sizeof(RpcOutChannel));
rpc_client_virtual_connection_init(rpc, connection);
}
return connection;
}
void rpc_client_virtual_connection_free(RpcVirtualConnection* virtual_connection)
{
if (virtual_connection != NULL)
{
free(virtual_connection);
}
}
/* Virtual Connection Cookie Table */
RpcVirtualConnectionCookieTable* rpc_virtual_connection_cookie_table_new(rdpRpc* rpc)
{
RpcVirtualConnectionCookieTable* table;
table = (RpcVirtualConnectionCookieTable*) malloc(sizeof(RpcVirtualConnectionCookieTable));
if (table != NULL)
{
ZeroMemory(table, sizeof(RpcVirtualConnectionCookieTable));
table->Count = 0;
table->ArraySize = 32;
table->Entries = (RpcVirtualConnectionCookieEntry*) malloc(sizeof(RpcVirtualConnectionCookieEntry) * table->ArraySize);
ZeroMemory(table->Entries, sizeof(RpcVirtualConnectionCookieEntry) * table->ArraySize);
}
return table;
}
void rpc_virtual_connection_cookie_table_free(RpcVirtualConnectionCookieTable* table)
{
if (table != NULL)
{
free(table->Entries);
free(table);
}
}
/* RPC Core Module */
rdpRpc* rpc_new(rdpTransport* transport)
{
rdpRpc* rpc = (rdpRpc*) malloc(sizeof(rdpRpc));
if (rpc != NULL)
{
ZeroMemory(rpc, sizeof(rdpRpc));
rpc->State = RPC_CLIENT_STATE_INITIAL;
rpc->transport = transport;
rpc->settings = transport->settings;
rpc->send_seq_num = 0;
rpc->ntlm = ntlm_new();
rpc->NtlmHttpIn = ntlm_http_new();
rpc->NtlmHttpOut = ntlm_http_new();
rpc_ntlm_http_init_channel(rpc, rpc->NtlmHttpIn, TSG_CHANNEL_IN);
rpc_ntlm_http_init_channel(rpc, rpc->NtlmHttpOut, TSG_CHANNEL_OUT);
rpc->FragBufferSize = 20;
rpc->FragBuffer = (BYTE*) malloc(rpc->FragBufferSize);
rpc->StubOffset = 0;
rpc->StubBufferSize = 20;
rpc->StubLength = 0;
rpc->StubFragCount = 0;
rpc->StubBuffer = (BYTE*) malloc(rpc->FragBufferSize);
rpc->rpc_vers = 5;
rpc->rpc_vers_minor = 0;
/* little-endian data representation */
rpc->packed_drep[0] = 0x10;
rpc->packed_drep[1] = 0x00;
rpc->packed_drep[2] = 0x00;
rpc->packed_drep[3] = 0x00;
rpc->max_xmit_frag = 0x0FF8;
rpc->max_recv_frag = 0x0FF8;
rpc->pdu = (RPC_PDU*) _aligned_malloc(sizeof(RPC_PDU), MEMORY_ALLOCATION_ALIGNMENT);
rpc->SendQueue = (PSLIST_HEADER) _aligned_malloc(sizeof(SLIST_HEADER), MEMORY_ALLOCATION_ALIGNMENT);
InitializeSListHead(rpc->SendQueue);
rpc->ReceiveQueue = (PSLIST_HEADER) _aligned_malloc(sizeof(SLIST_HEADER), MEMORY_ALLOCATION_ALIGNMENT);
InitializeSListHead(rpc->ReceiveQueue);
rpc->ReceiveWindow = 0x00010000;
rpc->ChannelLifetime = 0x40000000;
rpc->ChannelLifetimeSet = 0;
rpc->KeepAliveInterval = 300000;
rpc->CurrentKeepAliveInterval = rpc->KeepAliveInterval;
rpc->CurrentKeepAliveTime = 0;
rpc->VirtualConnection = rpc_client_virtual_connection_new(rpc);
rpc->VirtualConnectionCookieTable = rpc_virtual_connection_cookie_table_new(rpc);
rpc->call_id = 1;
rpc_client_new(rpc);
rpc->client->SynchronousSend = TRUE;
rpc->client->SynchronousReceive = TRUE;
}
return rpc;
}
void rpc_free(rdpRpc* rpc)
{
if (rpc != NULL)
{
RPC_PDU* PduEntry;
ntlm_http_free(rpc->NtlmHttpIn);
ntlm_http_free(rpc->NtlmHttpOut);
_aligned_free(rpc->pdu);
while ((PduEntry = (RPC_PDU*) InterlockedPopEntrySList(rpc->SendQueue)) != NULL)
_aligned_free(PduEntry);
_aligned_free(rpc->SendQueue);
while ((PduEntry = (RPC_PDU*) InterlockedPopEntrySList(rpc->ReceiveQueue)) != NULL)
_aligned_free(PduEntry);
_aligned_free(rpc->ReceiveQueue);
rpc_client_virtual_connection_free(rpc->VirtualConnection);
rpc_virtual_connection_cookie_table_free(rpc->VirtualConnectionCookieTable);
free(rpc);
}
}