libfreerdp-core: implement RPC response stub data reassembly

This commit is contained in:
Marc-André Moreau 2012-11-27 02:49:44 -05:00
parent fbacea6bad
commit 6cad536d34
6 changed files with 131 additions and 49 deletions

View File

@ -56,7 +56,7 @@ COMMAND_LINE_ARGUMENT_A args[] =
{ "u", COMMAND_LINE_VALUE_REQUIRED, "[<domain>\\]<user> or <user>[@<domain>]", NULL, NULL, -1, NULL, "Username" },
{ "p", COMMAND_LINE_VALUE_REQUIRED, "<password>", NULL, NULL, -1, NULL, "Password" },
{ "d", COMMAND_LINE_VALUE_REQUIRED, "<domain>", NULL, NULL, -1, NULL, "Domain" },
{ "g", COMMAND_LINE_VALUE_REQUIRED, "<gateway>[:port]", NULL, NULL, -1, NULL, "Gateway Hostname" },
{ "g", COMMAND_LINE_VALUE_OPTIONAL, "<gateway>[:port]", NULL, NULL, -1, NULL, "Gateway Hostname" },
{ "gu", COMMAND_LINE_VALUE_REQUIRED, "[<domain>\\]<user> or <user>[@<domain>]", NULL, NULL, -1, NULL, "Gateway username" },
{ "gp", COMMAND_LINE_VALUE_REQUIRED, "<password>", NULL, NULL, -1, NULL, "Gateway password" },
{ "gd", COMMAND_LINE_VALUE_REQUIRED, "<domain>", NULL, NULL, -1, NULL, "Gateway domain" },
@ -697,7 +697,7 @@ int freerdp_client_parse_command_line_arguments(int argc, char** argv, rdpSettin
do
{
if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
if (!(arg->Flags & COMMAND_LINE_ARGUMENT_PRESENT))
continue;
CommandLineSwitchStart(arg)
@ -820,19 +820,26 @@ int freerdp_client_parse_command_line_arguments(int argc, char** argv, rdpSettin
}
CommandLineSwitchCase(arg, "g")
{
p = strchr(arg->Value, ':');
if (p)
if (arg->Flags & COMMAND_LINE_VALUE_PRESENT)
{
length = p - arg->Value;
settings->GatewayPort = atoi(&p[1]);
settings->GatewayHostname = (char*) malloc(length + 1);
strncpy(settings->GatewayHostname, arg->Value, length);
settings->GatewayHostname[length] = '\0';
p = strchr(arg->Value, ':');
if (p)
{
length = p - arg->Value;
settings->GatewayPort = atoi(&p[1]);
settings->GatewayHostname = (char*) malloc(length + 1);
strncpy(settings->GatewayHostname, arg->Value, length);
settings->GatewayHostname[length] = '\0';
}
else
{
settings->GatewayHostname = _strdup(arg->Value);
}
}
else
{
settings->GatewayHostname = _strdup(arg->Value);
settings->GatewayHostname = _strdup(settings->ServerHostname);
}
settings->GatewayUsageMethod = TRUE;

View File

@ -555,18 +555,36 @@ BOOL rpc_send_bind_pdu(rdpRpc* rpc)
return TRUE;
}
/**
* Maximum Transmit/Receive Fragment Size Negotiation
*
* The client determines, and then sends in the bind PDU, its desired maximum size for transmitting fragments,
* and its desired maximum receive fragment size. Similarly, the server determines its desired maximum sizes
* for transmitting and receiving fragments. Transmit and receive sizes may be different to help preserve buffering.
* When the server receives the clients values, it sets its operational transmit size to the minimum of the clients
* receive size (from the bind PDU) and its own desired transmit size. Then it sets its actual receive size to the
* minimum of the clients transmit size (from the bind) and its own desired receive size. The server then returns its
* operational values in the bind_ack PDU. The client then sets its operational values from the received bind_ack PDU.
* The received transmit size becomes the clients receive size, and the received receive size becomes the clients
* transmit size. Either party may use receive buffers larger than negotiated although this will not provide any
* advantage but may not transmit larger fragments than negotiated.
*/
int rpc_recv_bind_ack_pdu(rdpRpc* rpc)
{
int status;
BYTE* auth_data;
rpcconn_hdr_t* header;
status = rpc_recv_pdu(rpc);
status = rpc_recv_pdu_fragment(rpc);
if (status > 0)
{
header = (rpcconn_hdr_t*) rpc->buffer;
rpc->max_recv_frag = header->bind_ack.max_xmit_frag;
rpc->max_xmit_frag = header->bind_ack.max_recv_frag;
rpc->ntlm->inputBuffer.cbBuffer = header->common.auth_length;
rpc->ntlm->inputBuffer.pvBuffer = malloc(header->common.auth_length);
@ -858,7 +876,7 @@ int rpc_recv_pdu_header(rdpRpc* rpc, BYTE* header)
return bytesRead;
}
int rpc_recv_pdu(rdpRpc* rpc)
int rpc_recv_pdu_fragment(rdpRpc* rpc)
{
int status;
int headerLength;
@ -901,17 +919,6 @@ int rpc_recv_pdu(rdpRpc* rpc)
bytesRead += status;
}
printf("header->common.pfc_flags: 0x%04X\n", header->common.pfc_flags);
if (header->common.pfc_flags)
{
if (!(header->common.pfc_flags & PFC_LAST_FRAG))
{
printf("Fragmented PDU\n");
rpc_pdu_header_print(header);
}
}
if (header->common.ptype == PTYPE_RTS) /* RTS PDU */
{
if (rpc->VirtualConnection->State < VIRTUAL_CONNECTION_STATE_OPENED)
@ -919,7 +926,7 @@ int rpc_recv_pdu(rdpRpc* rpc)
printf("Receiving Out-of-Sequence RTS PDU\n");
rts_recv_out_of_sequence_pdu(rpc);
return rpc_recv_pdu(rpc);
return rpc_recv_pdu_fragment(rpc);
}
else if (header->common.ptype == PTYPE_FAULT)
{
@ -930,14 +937,16 @@ int rpc_recv_pdu(rdpRpc* rpc)
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");
//printf("Sending Flow Control Ack PDU\n");
rts_send_flow_control_ack_pdu(rpc);
}
@ -950,6 +959,63 @@ int rpc_recv_pdu(rdpRpc* rpc)
return header->common.frag_length;
}
int rpc_recv_pdu(rdpRpc* rpc)
{
int status;
UINT32 StubOffset;
UINT32 StubLength;
rpcconn_hdr_t* header;
status = rpc_recv_pdu_fragment(rpc);
header = (rpcconn_hdr_t*) rpc->buffer;
if (header->common.ptype != PTYPE_RESPONSE)
{
printf("rpc_recv_pdu: unexpected ptype 0x%02X\n", header->common.ptype);
return -1;
}
if (!rpc_get_stub_data_info(rpc, rpc->buffer, &StubOffset, &StubLength))
{
printf("rpc_recv_pdu: expected stub\n");
return -1;
}
if (header->response.alloc_hint > rpc->StubSize)
{
rpc->StubSize = header->response.alloc_hint;
rpc->StubBuffer = (BYTE*) realloc(rpc->StubBuffer, rpc->StubSize);
}
CopyMemory(&rpc->StubBuffer[rpc->StubOffset], &rpc->buffer[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;
return rpc->StubLength;
}
return rpc_recv_pdu(rpc);
}
int rpc_tsg_write(rdpRpc* rpc, BYTE* data, int length, UINT16 opnum)
{
int status;
@ -1238,6 +1304,12 @@ rdpRpc* rpc_new(rdpTransport* transport)
rpc->length = 20;
rpc->buffer = (BYTE*) malloc(rpc->length);
rpc->StubOffset = 0;
rpc->StubSize = 20;
rpc->StubLength = 0;
rpc->StubFragCount = 0;
rpc->StubBuffer = (BYTE*) malloc(rpc->length);
rpc->rpc_vers = 5;
rpc->rpc_vers_minor = 0;

View File

@ -684,6 +684,12 @@ struct rdp_rpc
BYTE* buffer;
UINT32 length;
BYTE* StubBuffer;
UINT32 StubSize;
UINT32 StubLength;
UINT32 StubOffset;
UINT32 StubFragCount;
BYTE rpc_vers;
BYTE rpc_vers_minor;
BYTE packed_drep[4];
@ -722,6 +728,7 @@ int rpc_in_write(rdpRpc* rpc, BYTE* data, int length);
BOOL rpc_get_stub_data_info(rdpRpc* rpc, BYTE* header, UINT32* offset, UINT32* length);
int rpc_recv_pdu_header(rdpRpc* rpc, BYTE* header);
int rpc_recv_pdu_fragment(rdpRpc* rpc);
int rpc_recv_pdu(rdpRpc* rpc);
int rpc_tsg_write(rdpRpc* rpc, BYTE* data, int length, UINT16 opnum);

View File

@ -1328,7 +1328,7 @@ int rts_recv_pdu(rdpRpc* rpc)
int status;
rpcconn_rts_hdr_t* rts;
status = rpc_recv_pdu(rpc);
status = rpc_recv_pdu_fragment(rpc);
if (status > 0)
{

View File

@ -247,7 +247,7 @@ BOOL TsProxyCreateTunnelReadResponse(rdpTsg* tsg)
PTSG_PACKET_CAPS_RESPONSE packetCapsResponse;
PTSG_PACKET_QUARENC_RESPONSE packetQuarEncResponse;
status = rpc_recv_pdu(rpc);
status = rpc_recv_pdu_fragment(rpc);
if (status <= 0)
return FALSE;
@ -296,6 +296,9 @@ BOOL TsProxyCreateTunnelReadResponse(rdpTsg* tsg)
count = *((UINT32*) &buffer[offset]); /* ActualCount (4 bytes) */
offset += 4;
printf("CertChain (%d)\n", ((count + 1) * 2));
freerdp_hexdump(&buffer[offset], ((count + 1) * 2));
/*
* CertChainData is a wide character string, and the count is
* given in characters excluding the null terminator, therefore:
@ -397,6 +400,9 @@ BOOL TsProxyCreateTunnelReadResponse(rdpTsg* tsg)
count = *((UINT32*) &buffer[offset]); /* ActualCount (4 bytes) */
offset += 4;
printf("CertChain (%d)\n", ((count + 1) * 2));
freerdp_hexdump(&buffer[offset], ((count + 1) * 2));
/*
* CertChainData is a wide character string, and the count is
* given in characters excluding the null terminator, therefore:
@ -406,6 +412,8 @@ BOOL TsProxyCreateTunnelReadResponse(rdpTsg* tsg)
}
else
{
printf("CertChain (%d)\n", 0);
Pointer = *((UINT32*) &buffer[offset]); /* Ptr (4 bytes): 0x00020008 */
offset += 4;
}
@ -576,7 +584,7 @@ BOOL TsProxyAuthorizeTunnelReadResponse(rdpTsg* tsg)
rdpRpc* rpc = tsg->rpc;
PTSG_PACKET_RESPONSE packetResponse;
status = rpc_recv_pdu(rpc);
status = rpc_recv_pdu_fragment(rpc);
if (status <= 0)
return FALSE;
@ -808,7 +816,7 @@ BOOL TsProxyCreateChannelReadResponse(rdpTsg* tsg)
UINT32 length;
rdpRpc* rpc = tsg->rpc;
status = rpc_recv_pdu(rpc);
status = rpc_recv_pdu_fragment(rpc);
if (status < 0)
return FALSE;
@ -905,7 +913,7 @@ BOOL TsProxySetupReceivePipeReadResponse(rdpTsg* tsg)
UINT32 length;
rdpRpc* rpc = tsg->rpc;
status = rpc_recv_pdu(rpc);
status = rpc_recv_pdu_fragment(rpc);
if (status < 0)
return FALSE;
@ -1120,13 +1128,9 @@ int tsg_read(rdpTsg* tsg, BYTE* data, UINT32 length)
if (tsg->PendingPdu)
{
rpcconn_common_hdr_t* header;
header = (rpcconn_common_hdr_t*) rpc->buffer;
CopyLength = (tsg->BytesAvailable > length) ? length : tsg->BytesAvailable;
CopyMemory(data, &rpc->buffer[tsg->StubOffset + tsg->BytesRead], CopyLength);
CopyMemory(data, &rpc->StubBuffer[tsg->BytesRead], CopyLength);
tsg->BytesAvailable -= CopyLength;
tsg->BytesRead += CopyLength;
@ -1137,31 +1141,21 @@ int tsg_read(rdpTsg* tsg, BYTE* data, UINT32 length)
}
else
{
rpcconn_response_hdr_t* header;
status = rpc_recv_pdu(rpc);
header = (rpcconn_response_hdr_t*) rpc->buffer;
if (!rpc_get_stub_data_info(rpc, rpc->buffer, &tsg->StubOffset, &tsg->StubLength))
{
printf("tsg_read error: expected stub\n");
return -1;
}
if (header->alloc_hint == 4)
if (rpc->StubLength == 4)
{
DEBUG_TSG("Ignoring TsProxySetupReceivePipe Response");
return tsg_read(tsg, data, length);
}
tsg->PendingPdu = TRUE;
tsg->BytesAvailable = tsg->StubLength;
tsg->BytesAvailable = rpc->StubLength;
tsg->BytesRead = 0;
CopyLength = (tsg->BytesAvailable > length) ? length : tsg->BytesAvailable;
CopyMemory(data, &rpc->buffer[tsg->StubOffset + tsg->BytesRead], CopyLength);
CopyMemory(data, &rpc->StubBuffer[tsg->BytesRead], CopyLength);
tsg->BytesAvailable -= CopyLength;
tsg->BytesRead += CopyLength;

View File

@ -237,6 +237,8 @@ int CommandLineParseArgumentsA(int argc, LPCSTR* argv, COMMAND_LINE_ARGUMENT_A*
if (!value && (options[j].Flags & COMMAND_LINE_VALUE_REQUIRED))
return COMMAND_LINE_ERROR_MISSING_VALUE;
options[j].Flags |= COMMAND_LINE_ARGUMENT_PRESENT;
if (value)
{
options[j].Value = value;