libfreerdp-core: separate RTS and RPC PDU receiving

This commit is contained in:
Marc-André Moreau 2012-04-21 16:18:07 -04:00
parent 2e5215ba3a
commit 3b37d2262a
6 changed files with 505 additions and 359 deletions

View File

@ -287,6 +287,17 @@ void http_response_parse_header(HttpResponse* http_response)
}
}
void http_response_print(HttpResponse* http_response)
{
int i;
for (i = 0; i < http_response->count; i++)
{
printf("%s\n", http_response->lines[i]);
}
printf("\n");
}
HttpResponse* http_response_recv(rdpTls* tls)
{
uint8* p;

View File

@ -91,6 +91,8 @@ struct _http_response
char* Content;
};
void http_response_print(HttpResponse* http_response);
HttpResponse* http_response_recv(rdpTls* tls);
HttpResponse* http_response_new();

View File

@ -302,6 +302,21 @@ boolean rpc_in_connect_http(rdpRpc* rpc)
return true;
}
void rpc_pdu_header_read(STREAM* s, RPC_PDU_HEADER* header)
{
stream_read_uint8(s, header->rpc_vers); /* rpc_vers (1 byte) */
stream_read_uint8(s, header->rpc_vers_minor); /* rpc_vers_minor (1 byte) */
stream_read_uint8(s, header->ptype); /* PTYPE (1 byte) */
stream_read_uint8(s, header->pfc_flags); /* pfc_flags (1 byte) */
stream_read_uint8(s, header->packed_drep[0]); /* packet_drep[0] (1 byte) */
stream_read_uint8(s, header->packed_drep[1]); /* packet_drep[1] (1 byte) */
stream_read_uint8(s, header->packed_drep[2]); /* packet_drep[2] (1 byte) */
stream_read_uint8(s, header->packed_drep[3]); /* packet_drep[3] (1 byte) */
stream_read_uint16(s, header->frag_length); /* frag_length (2 bytes) */
stream_read_uint16(s, header->auth_length); /* auth_length (2 bytes) */
stream_read_uint32(s, header->call_id); /* call_id (4 bytes) */
}
int rpc_out_write(rdpRpc* rpc, uint8* data, int length)
{
int status;
@ -337,148 +352,7 @@ int rpc_in_write(rdpRpc* rpc, uint8* data, int length)
return status;
}
uint8* rpc_create_cookie()
{
uint8* ret = xmalloc(16);
RAND_pseudo_bytes(ret, 16);
return ret;
}
void rpc_generate_cookie(uint8* cookie)
{
RAND_pseudo_bytes(cookie, 16);
}
boolean rpc_send_CONN_A1_pdu(rdpRpc* rpc)
{
STREAM* s;
RTS_PDU_HEADER header;
uint32 ReceiveWindowSize;
uint8* OUTChannelCookie;
uint8* VirtualConnectionCookie;
header.rpc_vers = 5;
header.rpc_vers_minor = 0;
header.ptype = PTYPE_RTS;
header.pfc_flags = PFC_FIRST_FRAG | PFC_LAST_FRAG;
header.packed_drep[0] = 0x10;
header.packed_drep[1] = 0x00;
header.packed_drep[2] = 0x00;
header.packed_drep[3] = 0x00;
header.frag_length = 76;
header.auth_length = 0;
header.call_id = 0;
header.flags = 0;
header.numberOfCommands = 4;
DEBUG_RPC("Sending CONN_A1 RTS PDU");
s = stream_new(header.frag_length);
rpc_generate_cookie((uint8*) &(rpc->VirtualConnection->Cookie));
rpc_generate_cookie((uint8*) &(rpc->VirtualConnection->DefaultOutChannelCookie));
VirtualConnectionCookie = (uint8*) &(rpc->VirtualConnection->Cookie);
OUTChannelCookie = (uint8*) &(rpc->VirtualConnection->DefaultOutChannelCookie);
ReceiveWindowSize = rpc->VirtualConnection->DefaultOutChannel->ReceiveWindow;
rts_pdu_header_write(s, &header); /* RTS Header (20 bytes) */
rts_version_command_write(s); /* Version (8 bytes) */
rts_cookie_command_write(s, VirtualConnectionCookie); /* VirtualConnectionCookie (20 bytes) */
rts_cookie_command_write(s, OUTChannelCookie); /* OUTChannelCookie (20 bytes) */
rts_receive_window_size_command_write(s, ReceiveWindowSize); /* ReceiveWindowSize (8 bytes) */
stream_seal(s);
rpc_out_write(rpc, s->data, s->size);
stream_free(s);
return true;
}
boolean rpc_send_CONN_B1_pdu(rdpRpc* rpc)
{
STREAM* s;
RTS_PDU_HEADER header;
uint8* INChannelCookie;
uint8* AssociationGroupId;
uint8* VirtualConnectionCookie;
header.rpc_vers = 5;
header.rpc_vers_minor = 0;
header.ptype = PTYPE_RTS;
header.pfc_flags = PFC_FIRST_FRAG | PFC_LAST_FRAG;
header.packed_drep[0] = 0x10;
header.packed_drep[1] = 0x00;
header.packed_drep[2] = 0x00;
header.packed_drep[3] = 0x00;
header.frag_length = 104;
header.auth_length = 0;
header.call_id = 0;
header.flags = 0;
header.numberOfCommands = 6;
DEBUG_RPC("Sending CONN_B1 RTS PDU");
s = stream_new(header.frag_length);
rpc_generate_cookie((uint8*) &(rpc->VirtualConnection->DefaultInChannelCookie));
rpc_generate_cookie((uint8*) &(rpc->VirtualConnection->AssociationGroupId));
VirtualConnectionCookie = (uint8*) &(rpc->VirtualConnection->Cookie);
INChannelCookie = (uint8*) &(rpc->VirtualConnection->DefaultInChannelCookie);
AssociationGroupId = (uint8*) &(rpc->VirtualConnection->AssociationGroupId);
rts_pdu_header_write(s, &header); /* RTS Header (20 bytes) */
rts_version_command_write(s); /* Version (8 bytes) */
rts_cookie_command_write(s, VirtualConnectionCookie); /* VirtualConnectionCookie (20 bytes) */
rts_cookie_command_write(s, INChannelCookie); /* INChannelCookie (20 bytes) */
rts_channel_lifetime_command_write(s, 0x40000000); /* ChannelLifetime (8 bytes) */
rts_client_keepalive_command_write(s, 0x000493E0); /* ClientKeepalive (8 bytes) */
rts_association_group_id_command_write(s, AssociationGroupId); /* AssociationGroupId (20 bytes) */
stream_seal(s);
rpc_in_write(rpc, s->data, s->size);
stream_free(s);
return true;
}
boolean rpc_send_keep_alive_pdu(rdpRpc* rpc)
{
STREAM* s;
RTS_PDU_HEADER header;
header.rpc_vers = 5;
header.rpc_vers_minor = 0;
header.ptype = PTYPE_RTS;
header.pfc_flags = PFC_FIRST_FRAG | PFC_LAST_FRAG;
header.packed_drep[0] = 0x10;
header.packed_drep[1] = 0x00;
header.packed_drep[2] = 0x00;
header.packed_drep[3] = 0x00;
header.frag_length = 28;
header.auth_length = 0;
header.call_id = 0;
header.flags = 2;
header.numberOfCommands = 1;
DEBUG_RPC("Sending Keep-Alive RTS PDU");
s = stream_new(header.frag_length);
rts_pdu_header_write(s, &header); /* RTS Header (20 bytes) */
rts_client_keepalive_command_write(s, 0x00007530); /* ClientKeepalive (8 bytes) */
stream_seal(s);
rpc_in_write(rpc, s->data, s->size);
stream_free(s);
return true;
}
boolean rpc_in_send_bind(rdpRpc* rpc)
boolean rpc_send_bind_pdu(rdpRpc* rpc)
{
STREAM* pdu;
rpcconn_bind_hdr_t* bind_pdu;
@ -609,13 +483,50 @@ boolean rpc_in_send_bind(rdpRpc* rpc)
return true;
}
boolean rpc_in_send_rpc_auth_3(rdpRpc* rpc)
int rpc_recv_bind_ack_pdu(rdpRpc* rpc)
{
STREAM* s;
uint16 frag_length;
uint16 auth_length;
STREAM* ntlmssp_stream;
RPC_PDU_HEADER header;
int pdu_length = 0x8FFF; /* 32KB buffer */
uint8* pdu = xmalloc(pdu_length);
int status = rpc_out_read(rpc, pdu, pdu_length);
DEBUG_RPC("TODO: complete NTLM integration");
if (status > 0)
{
s = stream_new(0);
stream_attach(s, pdu, pdu_length);
rpc_pdu_header_read(s, &header);
frag_length = header.frag_length;
auth_length = header.auth_length;
printf("frag_length:%d auth_length:%d\n", frag_length, auth_length);
ntlmssp_stream = stream_new(0xFFFF);
stream_write(ntlmssp_stream, (pdu + (frag_length - auth_length)), auth_length);
ntlmssp_stream->p = ntlmssp_stream->data;
//ntlmssp_recv(rpc->ntlmssp, ntlmssp_stream);
stream_free(ntlmssp_stream);
}
xfree(pdu);
return status;
}
boolean rpc_send_rpc_auth_3_pdu(rdpRpc* rpc)
{
STREAM* pdu;
rpcconn_rpc_auth_3_hdr_t* rpc_auth_3_pdu;
STREAM* ntlm_stream = stream_new(0xFFFF);
DEBUG_RPC("Sending auth3 PDU");
DEBUG_RPC("Sending auth_3 PDU");
ntlm_authenticate(rpc->ntlm);
ntlm_stream->size = rpc->ntlm->outputBuffer.cbBuffer;
@ -633,6 +544,7 @@ boolean rpc_in_send_rpc_auth_3(rdpRpc* rpc)
rpc_auth_3_pdu->frag_length = 28 + ntlm_stream->size;
rpc_auth_3_pdu->auth_length = ntlm_stream->size;
rpc_auth_3_pdu->call_id = 2;
rpc_auth_3_pdu->max_xmit_frag = 0x0FF8;
rpc_auth_3_pdu->max_recv_frag = 0x0FF8;
rpc_auth_3_pdu->auth_verifier.auth_pad = NULL; /* align(4); size_is(auth_pad_length) p */
@ -663,82 +575,6 @@ boolean rpc_in_send_rpc_auth_3(rdpRpc* rpc)
return true;
}
boolean rpc_send_flow_control_ack_pdu(rdpRpc* rpc)
{
STREAM* s;
RTS_PDU_HEADER header;
uint32 BytesReceived;
uint32 AvailableWindow;
uint8* ChannelCookie;
header.rpc_vers = 5;
header.rpc_vers_minor = 0;
header.ptype = PTYPE_RTS;
header.pfc_flags = PFC_FIRST_FRAG | PFC_LAST_FRAG;
header.packed_drep[0] = 0x10;
header.packed_drep[1] = 0x00;
header.packed_drep[2] = 0x00;
header.packed_drep[3] = 0x00;
header.frag_length = 56;
header.auth_length = 0;
header.call_id = 0;
header.flags = 2;
header.numberOfCommands = 2;
DEBUG_RPC("Sending FlowControlAck RTS PDU");
BytesReceived = rpc->VirtualConnection->DefaultOutChannel->RecipientBytesReceived;
AvailableWindow = rpc->VirtualConnection->DefaultOutChannel->ReceiverAvailableWindow;
ChannelCookie = (uint8*) &(rpc->VirtualConnection->DefaultOutChannelCookie);
s = stream_new(header.frag_length);
rts_pdu_header_write(s, &header); /* RTS Header (20 bytes) */
rts_destination_command_write(s, FDOutProxy); /* Destination Command (8 bytes) */
/* FlowControlAck Command (28 bytes) */
rts_flow_control_ack_command_write(s, BytesReceived, AvailableWindow, ChannelCookie);
stream_seal(s);
rpc_in_write(rpc, s->data, s->size);
stream_free(s);
return true;
}
boolean rpc_send_ping_pdu(rdpRpc* rpc)
{
STREAM* s;
RTS_PDU_HEADER header;
header.rpc_vers = 5;
header.rpc_vers_minor = 0;
header.ptype = PTYPE_RTS;
header.pfc_flags = PFC_FIRST_FRAG | PFC_LAST_FRAG;
header.packed_drep[0] = 0x10;
header.packed_drep[1] = 0x00;
header.packed_drep[2] = 0x00;
header.packed_drep[3] = 0x00;
header.frag_length = 20;
header.auth_length = 0;
header.call_id = 0;
header.flags = 1;
header.numberOfCommands = 0;
DEBUG_RPC("Sending Ping RTS PDU");
s = stream_new(header.frag_length);
rts_pdu_header_write(s, &header); /* RTS Header (20 bytes) */
stream_seal(s);
rpc_in_write(rpc, s->data, s->size);
stream_free(s);
return true;
}
int rpc_out_read_http_header(rdpRpc* rpc)
{
int status = 0;
@ -755,27 +591,33 @@ int rpc_out_read(rdpRpc* rpc, uint8* data, int length)
STREAM* s;
int status;
uint8* pdu;
uint8 ptype;
uint16 frag_length;
int content_length;
RPC_PDU_HEADER header;
rdpTls* tls_out = rpc->tls_out;
if (rpc->VirtualConnection->DefaultOutChannel->ReceiverAvailableWindow < 0x00008FFF) /* Just a simple workaround */
rpc_send_flow_control_ack_pdu(rpc); /* Send FlowControlAck every time AW reaches the half */
rts_send_flow_control_ack_pdu(rpc); /* Send FlowControlAck every time AvailableWindow reaches the half */
pdu = xmalloc(0xFFFF);
status = tls_read(tls_out, pdu, 10);
status = tls_read(tls_out, pdu, 16); /* read first 16 bytes to get RPC PDU Header */
if (status <= 0) /* read first 10 bytes to get the frag_length value */
if (status <= 0)
{
xfree(pdu);
return status;
}
ptype = *(pdu + 2);
frag_length = *((uint16*) (pdu + 8));
s = stream_new(0);
stream_attach(s, pdu, 16);
status = tls_read(tls_out, pdu + 10, frag_length - 10);
rpc_pdu_header_read(s, &header);
stream_detach(s);
stream_free(s);
content_length = header.frag_length - 16;
status = tls_read(tls_out, pdu + 16, content_length);
if (status < 0)
{
@ -783,74 +625,35 @@ int rpc_out_read(rdpRpc* rpc, uint8* data, int length)
return status;
}
if (ptype == PTYPE_RTS) /* RTS PDU */
if (header.ptype == PTYPE_RTS) /* RTS PDU */
{
s = stream_new(0);
stream_attach(s, pdu, frag_length);
rts_pdu_recv(rpc, s);
xfree(pdu);
return 0;
printf("rpc_out_read error: Unexpected RTS PDU\n");
return -1;
}
else
{
/* RTS PDUs are not subject to flow control */
rpc->VirtualConnection->DefaultOutChannel->RecipientBytesReceived += frag_length;
rpc->VirtualConnection->DefaultOutChannel->ReceiverAvailableWindow -= frag_length;
rpc->VirtualConnection->DefaultOutChannel->RecipientBytesReceived += header.frag_length;
rpc->VirtualConnection->DefaultOutChannel->ReceiverAvailableWindow -= header.frag_length;
}
if (length < frag_length)
if (length < header.frag_length)
{
printf("rpc_out_read(): Error! Given buffer is to small. Received data fits not in.\n");
xfree(pdu);
return -1; /* TODO add buffer for storing remaining data for the next read in case destination buffer is too small */
}
memcpy(data, pdu, frag_length);
if (strncmp((char*) pdu, "HTTP", 4) == 0)
{
printf("\n%s", (char*) pdu);
printf("rpc_out_read error! receive buffer is not large enough\n");
return -1;
}
memcpy(data, pdu, header.frag_length);
#ifdef WITH_DEBUG_RPC
printf("rpc_out_read(): length: %d\n", frag_length);
freerdp_hexdump(data, frag_length);
printf("rpc_out_read(): length: %d\n", header.frag_length);
freerdp_hexdump(data, header.frag_length);
printf("\n");
#endif
xfree(pdu);
return frag_length;
}
int rpc_out_recv_bind_ack(rdpRpc* rpc)
{
uint16 frag_length;
uint16 auth_length;
STREAM* ntlmssp_stream;
int pdu_length = 0x8FFF; /* 32KB buffer */
uint8* pdu = xmalloc(pdu_length);
int status = rpc_out_read(rpc, pdu, pdu_length);
DEBUG_RPC("TODO: complete NTLM integration");
if (status > 0)
{
frag_length = *((uint16*)(pdu + 8));
auth_length = *((uint16*)(pdu + 10));
ntlmssp_stream = stream_new(0xFFFF);
stream_write(ntlmssp_stream, (pdu + (frag_length - auth_length)), auth_length);
ntlmssp_stream->p = ntlmssp_stream->data;
//ntlmssp_recv(rpc->ntlmssp, ntlmssp_stream);
stream_free(ntlmssp_stream);
}
xfree(pdu);
return status;
return header.frag_length;
}
int rpc_write(rdpRpc* rpc, uint8* data, int length, uint16 opnum)
@ -928,7 +731,7 @@ int rpc_write(rdpRpc* rpc, uint8* data, int length, uint16 opnum)
if (status < 0)
{
printf("rpc_write(): Error! rcp_in_write returned negative value.\n");
printf("rpc_write(): Error! rpc_in_write returned negative value.\n");
return status;
}
@ -1018,92 +821,32 @@ int rpc_read(rdpRpc* rpc, uint8* data, int length)
return read;
}
/**
* Connection Establishment\n
*
* Client Outbound Proxy Inbound Proxy Server\n
* | | | |\n
* |-----------------IN Channel Request--------------->| |\n
* |---OUT Channel Request-->| |<-Legacy Server Response-|\n
* | |<--------------Legacy Server Response--------------|\n
* | | | |\n
* |---------CONN_A1-------->| | |\n
* |----------------------CONN_B1--------------------->| |\n
* | |----------------------CONN_A2--------------------->|\n
* | | | |\n
* |<--OUT Channel Response--| |---------CONN_B2-------->|\n
* |<--------CONN_A3---------| | |\n
* | |<---------------------CONN_C1----------------------|\n
* | | |<--------CONN_B3---------|\n
* |<--------CONN_C2---------| | |\n
* | | | |\n
*
*/
boolean rpc_connect(rdpRpc* rpc)
{
int status;
uint8* pdu;
int pdu_length;
pdu_length = 0xFFFF;
pdu = xmalloc(pdu_length);
if (!rpc_out_connect_http(rpc))
if (!rts_connect(rpc))
{
printf("rpc_out_connect_http error!\n");
printf("rts_connect error!\n");
return false;
}
if (!rpc_send_CONN_A1_pdu(rpc))
if (!rpc_send_bind_pdu(rpc))
{
printf("rpc_send_CONN_A1_pdu error!\n");
printf("rpc_send_bind_pdu error!\n");
return false;
}
if (!rpc_in_connect_http(rpc))
if (!rpc_recv_bind_ack_pdu(rpc))
{
printf("rpc_in_connect_http error!\n");
printf("rpc_recv_bind_ack_pdu error!\n");
return false;
}
if (!rpc_send_CONN_B1_pdu(rpc))
if (!rpc_send_rpc_auth_3_pdu(rpc))
{
printf("rpc_send_CONN_B1_pdu error!\n");
printf("rpc_send_rpc_auth_3 error!\n");
return false;
}
/* Receive OUT Channel Response */
status = rpc_out_read(rpc, pdu, pdu_length);
/* Receive CONN_A3 RTS PDU */
status = rpc_out_read(rpc, pdu, pdu_length);
/* Receive CONN_C2 RTS PDU */
status = rpc_out_read(rpc, pdu, pdu_length);
/* [MS-RPCH] 3.2.1.5.3.1 Connection Establishment
* at this point VirtualChannel is created
*/
if (!rpc_in_send_bind(rpc))
{
printf("rpc_in_send_bind fault!\n");
return false;
}
if (!rpc_out_recv_bind_ack(rpc))
{
printf("rpc_out_recv_bind_ack fault!\n");
return false;
}
if (!rpc_in_send_rpc_auth_3(rpc))
{
printf("rpc_out_send_rpc_auth_3 fault!\n");
return false;
}
xfree(pdu);
return true;
}
@ -1125,6 +868,7 @@ RpcVirtualConnection* rpc_client_virtual_connection_new(rdpRpc* rpc)
if (virtual_connection != NULL)
{
virtual_connection->State = VIRTUAL_CONNECTION_STATE_INITIAL;
virtual_connection->DefaultInChannel = xnew(RpcInChannel);
virtual_connection->DefaultOutChannel = xnew(RpcOutChannel);
rpc_client_virtual_connection_init(rpc, virtual_connection);

View File

@ -41,6 +41,19 @@ typedef struct rdp_rpc_http rdpRpcHTTP;
#include <freerdp/utils/memory.h>
#include <freerdp/utils/hexdump.h>
struct _rpc_pdu_header
{
uint8 rpc_vers;
uint8 rpc_vers_minor;
uint8 ptype;
uint8 pfc_flags;
uint8 packed_drep[4];
uint16 frag_length;
uint16 auth_length;
uint32 call_id;
};
typedef struct _rpc_pdu_header RPC_PDU_HEADER;
typedef uint16 p_context_id_t;
typedef struct {
@ -588,10 +601,21 @@ typedef struct rpc_out_channel RpcOutChannel;
/* Client Virtual Connection */
enum _VIRTUAL_CONNECTION_STATE
{
VIRTUAL_CONNECTION_STATE_INITIAL,
VIRTUAL_CONNECTION_STATE_OUT_CHANNEL_WAIT,
VIRTUAL_CONNECTION_STATE_WAIT_A3W,
VIRTUAL_CONNECTION_STATE_WAIT_C2,
VIRTUAL_CONNECTION_STATE_OPENED,
VIRTUAL_CONNECTION_STATE_FINAL
};
typedef enum _VIRTUAL_CONNECTION_STATE VIRTUAL_CONNECTION_STATE;
struct rpc_virtual_connection
{
uint8 Cookie[16]; /* Virtual Connection Cookie */
uint32 State; /* Virtual Connection State */
VIRTUAL_CONNECTION_STATE State; /* Virtual Connection State */
RpcInChannel* DefaultInChannel; /* Default IN Channel */
RpcInChannel* NonDefaultInChannel; /* Non-Default IN Channel */
uint8 DefaultInChannelCookie[16]; /* Default IN Channel Cookie */
@ -642,7 +666,15 @@ void ntlm_free(rdpNtlm* ntlm);
boolean rpc_attach(rdpRpc* rpc, rdpTcp* tcp_in, rdpTcp* tcp_out, rdpTls* tls_in, rdpTls* tls_out);
boolean rpc_connect(rdpRpc* rpc);
boolean rpc_send_keep_alive_pdu(rdpRpc* rpc);
boolean rpc_out_connect_http(rdpRpc* rpc);
boolean rpc_in_connect_http(rdpRpc* rpc);
void rpc_pdu_header_read(STREAM* s, RPC_PDU_HEADER* header);
int rpc_out_write(rdpRpc* rpc, uint8* data, int length);
int rpc_in_write(rdpRpc* rpc, uint8* data, int length);
int rpc_out_read(rdpRpc* rpc, uint8* data, int length);
int rpc_write(rdpRpc* rpc, uint8* data, int length, uint16 opnum);
int rpc_read(rdpRpc* rpc, uint8* data, int length);

View File

@ -19,6 +19,81 @@
#include "rts.h"
/**
* Connection Establishment\n
*
* Client Outbound Proxy Inbound Proxy Server\n
* | | | |\n
* |-----------------IN Channel Request--------------->| |\n
* |---OUT Channel Request-->| |<-Legacy Server Response-|\n
* | |<--------------Legacy Server Response--------------|\n
* | | | |\n
* |---------CONN_A1-------->| | |\n
* |----------------------CONN_B1--------------------->| |\n
* | |----------------------CONN_A2--------------------->|\n
* | | | |\n
* |<--OUT Channel Response--| |---------CONN_B2-------->|\n
* |<--------CONN_A3---------| | |\n
* | |<---------------------CONN_C1----------------------|\n
* | | |<--------CONN_B3---------|\n
* |<--------CONN_C2---------| | |\n
* | | | |\n
*
*/
boolean rts_connect(rdpRpc* rpc)
{
int status;
RTS_PDU rts_pdu;
HttpResponse* http_response;
if (!rpc_out_connect_http(rpc))
{
printf("rpc_out_connect_http error!\n");
return false;
}
if (!rts_send_CONN_A1_pdu(rpc))
{
printf("rpc_send_CONN_A1_pdu error!\n");
return false;
}
if (!rpc_in_connect_http(rpc))
{
printf("rpc_in_connect_http error!\n");
return false;
}
if (!rts_send_CONN_B1_pdu(rpc))
{
printf("rpc_send_CONN_B1_pdu error!\n");
return false;
}
/* Receive OUT Channel Response */
http_response = http_response_recv(rpc->tls_out);
if (http_response->StatusCode != 200)
{
printf("rts_connect error!\n");
http_response_print(http_response);
return false;
}
http_response_print(http_response);
http_response_free(http_response);
/* Receive CONN_A3 RTS PDU */
status = rts_recv_pdu(rpc, &rts_pdu);
/* Receive CONN_C2 RTS PDU */
status = rts_recv_pdu(rpc, &rts_pdu);
return true;
}
#ifdef WITH_DEBUG_RTS
static const char* const RTS_CMD_STRINGS[] =
@ -272,23 +347,234 @@ void rts_ping_traffic_sent_notify_command_write(STREAM* s, uint32 PingTrafficSen
stream_write_uint32(s, PingTrafficSent); /* PingTrafficSent (4 bytes) */
}
int rts_pdu_recv(rdpRpc* rpc, STREAM* s)
void rpc_generate_cookie(uint8* cookie)
{
int i;
uint32 CommandType;
RAND_pseudo_bytes(cookie, 16);
}
boolean rts_send_CONN_A1_pdu(rdpRpc* rpc)
{
STREAM* s;
RTS_PDU_HEADER header;
uint32 ReceiveWindowSize;
uint8* OUTChannelCookie;
uint8* VirtualConnectionCookie;
header.rpc_vers = 5;
header.rpc_vers_minor = 0;
header.ptype = PTYPE_RTS;
header.pfc_flags = PFC_FIRST_FRAG | PFC_LAST_FRAG;
header.packed_drep[0] = 0x10;
header.packed_drep[1] = 0x00;
header.packed_drep[2] = 0x00;
header.packed_drep[3] = 0x00;
header.frag_length = 76;
header.auth_length = 0;
header.call_id = 0;
header.flags = 0;
header.numberOfCommands = 4;
DEBUG_RPC("Sending CONN_A1 RTS PDU");
s = stream_new(header.frag_length);
rpc_generate_cookie((uint8*) &(rpc->VirtualConnection->Cookie));
rpc_generate_cookie((uint8*) &(rpc->VirtualConnection->DefaultOutChannelCookie));
VirtualConnectionCookie = (uint8*) &(rpc->VirtualConnection->Cookie);
OUTChannelCookie = (uint8*) &(rpc->VirtualConnection->DefaultOutChannelCookie);
ReceiveWindowSize = rpc->VirtualConnection->DefaultOutChannel->ReceiveWindow;
rts_pdu_header_write(s, &header); /* RTS Header (20 bytes) */
rts_version_command_write(s); /* Version (8 bytes) */
rts_cookie_command_write(s, VirtualConnectionCookie); /* VirtualConnectionCookie (20 bytes) */
rts_cookie_command_write(s, OUTChannelCookie); /* OUTChannelCookie (20 bytes) */
rts_receive_window_size_command_write(s, ReceiveWindowSize); /* ReceiveWindowSize (8 bytes) */
stream_seal(s);
rpc_out_write(rpc, s->data, s->size);
stream_free(s);
return true;
}
boolean rts_send_CONN_B1_pdu(rdpRpc* rpc)
{
STREAM* s;
RTS_PDU_HEADER header;
uint8* INChannelCookie;
uint8* AssociationGroupId;
uint8* VirtualConnectionCookie;
header.rpc_vers = 5;
header.rpc_vers_minor = 0;
header.ptype = PTYPE_RTS;
header.pfc_flags = PFC_FIRST_FRAG | PFC_LAST_FRAG;
header.packed_drep[0] = 0x10;
header.packed_drep[1] = 0x00;
header.packed_drep[2] = 0x00;
header.packed_drep[3] = 0x00;
header.frag_length = 104;
header.auth_length = 0;
header.call_id = 0;
header.flags = 0;
header.numberOfCommands = 6;
DEBUG_RPC("Sending CONN_B1 RTS PDU");
s = stream_new(header.frag_length);
rpc_generate_cookie((uint8*) &(rpc->VirtualConnection->DefaultInChannelCookie));
rpc_generate_cookie((uint8*) &(rpc->VirtualConnection->AssociationGroupId));
VirtualConnectionCookie = (uint8*) &(rpc->VirtualConnection->Cookie);
INChannelCookie = (uint8*) &(rpc->VirtualConnection->DefaultInChannelCookie);
AssociationGroupId = (uint8*) &(rpc->VirtualConnection->AssociationGroupId);
rts_pdu_header_write(s, &header); /* RTS Header (20 bytes) */
rts_version_command_write(s); /* Version (8 bytes) */
rts_cookie_command_write(s, VirtualConnectionCookie); /* VirtualConnectionCookie (20 bytes) */
rts_cookie_command_write(s, INChannelCookie); /* INChannelCookie (20 bytes) */
rts_channel_lifetime_command_write(s, 0x40000000); /* ChannelLifetime (8 bytes) */
rts_client_keepalive_command_write(s, 0x000493E0); /* ClientKeepalive (8 bytes) */
rts_association_group_id_command_write(s, AssociationGroupId); /* AssociationGroupId (20 bytes) */
stream_seal(s);
rpc_in_write(rpc, s->data, s->size);
stream_free(s);
return true;
}
boolean rts_send_keep_alive_pdu(rdpRpc* rpc)
{
STREAM* s;
RTS_PDU_HEADER header;
rts_pdu_header_read(s, &header);
header.rpc_vers = 5;
header.rpc_vers_minor = 0;
header.ptype = PTYPE_RTS;
header.pfc_flags = PFC_FIRST_FRAG | PFC_LAST_FRAG;
header.packed_drep[0] = 0x10;
header.packed_drep[1] = 0x00;
header.packed_drep[2] = 0x00;
header.packed_drep[3] = 0x00;
header.frag_length = 28;
header.auth_length = 0;
header.call_id = 0;
header.flags = 2;
header.numberOfCommands = 1;
DEBUG_RTS("numberOfCommands:%d", header.numberOfCommands);
DEBUG_RPC("Sending Keep-Alive RTS PDU");
if (header.flags & RTS_FLAG_PING)
s = stream_new(header.frag_length);
rts_pdu_header_write(s, &header); /* RTS Header (20 bytes) */
rts_client_keepalive_command_write(s, 0x00007530); /* ClientKeepalive (8 bytes) */
stream_seal(s);
rpc_in_write(rpc, s->data, s->size);
stream_free(s);
return true;
}
boolean rts_send_flow_control_ack_pdu(rdpRpc* rpc)
{
STREAM* s;
RTS_PDU_HEADER header;
uint32 BytesReceived;
uint32 AvailableWindow;
uint8* ChannelCookie;
header.rpc_vers = 5;
header.rpc_vers_minor = 0;
header.ptype = PTYPE_RTS;
header.pfc_flags = PFC_FIRST_FRAG | PFC_LAST_FRAG;
header.packed_drep[0] = 0x10;
header.packed_drep[1] = 0x00;
header.packed_drep[2] = 0x00;
header.packed_drep[3] = 0x00;
header.frag_length = 56;
header.auth_length = 0;
header.call_id = 0;
header.flags = 2;
header.numberOfCommands = 2;
DEBUG_RPC("Sending FlowControlAck RTS PDU");
BytesReceived = rpc->VirtualConnection->DefaultOutChannel->RecipientBytesReceived;
AvailableWindow = rpc->VirtualConnection->DefaultOutChannel->ReceiverAvailableWindow;
ChannelCookie = (uint8*) &(rpc->VirtualConnection->DefaultOutChannelCookie);
s = stream_new(header.frag_length);
rts_pdu_header_write(s, &header); /* RTS Header (20 bytes) */
rts_destination_command_write(s, FDOutProxy); /* Destination Command (8 bytes) */
/* FlowControlAck Command (28 bytes) */
rts_flow_control_ack_command_write(s, BytesReceived, AvailableWindow, ChannelCookie);
stream_seal(s);
rpc_in_write(rpc, s->data, s->size);
stream_free(s);
return true;
}
boolean rts_send_ping_pdu(rdpRpc* rpc)
{
STREAM* s;
RTS_PDU_HEADER header;
header.rpc_vers = 5;
header.rpc_vers_minor = 0;
header.ptype = PTYPE_RTS;
header.pfc_flags = PFC_FIRST_FRAG | PFC_LAST_FRAG;
header.packed_drep[0] = 0x10;
header.packed_drep[1] = 0x00;
header.packed_drep[2] = 0x00;
header.packed_drep[3] = 0x00;
header.frag_length = 20;
header.auth_length = 0;
header.call_id = 0;
header.flags = 1;
header.numberOfCommands = 0;
DEBUG_RPC("Sending Ping RTS PDU");
s = stream_new(header.frag_length);
rts_pdu_header_write(s, &header); /* RTS Header (20 bytes) */
stream_seal(s);
rpc_in_write(rpc, s->data, s->size);
stream_free(s);
return true;
}
int rts_recv_pdu_commands(rdpRpc* rpc, RTS_PDU* rts_pdu)
{
int i;
STREAM* s;
uint32 CommandType;
s = stream_new(0);
stream_attach(s, rts_pdu->content, rts_pdu->header.frag_length);
DEBUG_RTS("numberOfCommands:%d", rts_pdu->header.numberOfCommands);
if (rts_pdu->header.flags & RTS_FLAG_PING)
{
rpc_send_keep_alive_pdu(rpc);
rts_send_keep_alive_pdu(rpc);
return 0;
}
for (i = 0; i < header.numberOfCommands; i++)
for (i = 0; i < rts_pdu->header.numberOfCommands; i++)
{
stream_read_uint32(s, CommandType); /* CommandType (4 bytes) */
@ -363,5 +649,61 @@ int rts_pdu_recv(rdpRpc* rpc, STREAM* s)
}
}
stream_detach(s);
stream_free(s);
return 0;
}
int rts_recv_pdu(rdpRpc* rpc, RTS_PDU* rts_pdu)
{
STREAM* s;
int status;
int length;
uint8 header_buffer[20];
rdpTls* tls_out = rpc->tls_out;
/* read first 20 bytes to get RTS PDU Header */
status = tls_read(tls_out, (uint8*) &header_buffer, 20);
if (status <= 0)
{
printf("rts_recv error\n");
return status;
}
s = stream_new(0);
stream_attach(s, header_buffer, 20);
rts_pdu_header_read(s, &(rts_pdu->header));
stream_detach(s);
stream_free(s);
length = rts_pdu->header.frag_length - 20;
rts_pdu->content = (uint8*) xmalloc(length);
status = tls_read(tls_out, rts_pdu->content, length);
if (status < 0)
{
printf("rts_recv error\n");
return status;
}
if (rts_pdu->header.ptype != PTYPE_RTS)
{
printf("rts_recv error: unexpected ptype:%d\n", rts_pdu->header.ptype);
return -1;
}
#ifdef WITH_DEBUG_RTS
printf("rts_recv(): length: %d\n", length);
freerdp_hexdump(rts_pdu->content, length);
printf("\n");
#endif
rts_recv_pdu_commands(rpc, rts_pdu);
return rts_pdu->header.frag_length;
}

View File

@ -104,6 +104,15 @@ struct _rts_pdu_header
};
typedef struct _rts_pdu_header RTS_PDU_HEADER;
struct _rts_pdu
{
RTS_PDU_HEADER header;
uint8* content;
};
typedef struct _rts_pdu RTS_PDU;
boolean rts_connect(rdpRpc* rpc);
void rts_pdu_header_read(STREAM* s, RTS_PDU_HEADER* header);
void rts_pdu_header_write(STREAM* s, RTS_PDU_HEADER* header);
@ -138,7 +147,13 @@ void rts_destination_command_write(STREAM* s, uint32 Destination);
void rts_ping_traffic_sent_notify_command_read(rdpRpc* rpc, STREAM* s);
void rts_ping_traffic_sent_notify_command_write(STREAM* s, uint32 PingTrafficSent);
int rts_pdu_recv(rdpRpc* rpc, STREAM* s);
boolean rts_send_CONN_A1_pdu(rdpRpc* rpc);
boolean rts_send_CONN_B1_pdu(rdpRpc* rpc);
boolean rts_send_keep_alive_pdu(rdpRpc* rpc);
boolean rts_send_flow_control_ack_pdu(rdpRpc* rpc);
boolean rts_send_ping_pdu(rdpRpc* rpc);
int rts_recv_pdu(rdpRpc* rpc, RTS_PDU* rts_pdu);
#ifdef WITH_DEBUG_TSG
#define WITH_DEBUG_RTS