Merge pull request #2408 from dvincent-devolutions/gateway

TSGateway out channel recycling
This commit is contained in:
Marc-André Moreau 2015-02-19 16:15:35 -05:00
commit ebdcf041f9
4 changed files with 189 additions and 66 deletions

View File

@ -864,6 +864,63 @@ int rpc_out_channel_connect(RpcOutChannel* outChannel, int timeout)
return 1;
}
int rpc_out_channel_replacement_connect(RpcOutChannel* outChannel, int timeout)
{
int status = 0;
HttpResponse* response = NULL;
rdpRpc* rpc = outChannel->rpc;
/* Connect OUT Channel */
if (rpc_channel_tls_connect((RpcChannel*) outChannel, timeout) < 0)
return -1;
rpc_out_channel_transition_to_state(outChannel, CLIENT_OUT_CHANNEL_STATE_CONNECTED);
if (rpc_ncacn_http_ntlm_init(rpc, (RpcChannel*) outChannel) < 0)
return FALSE;
/* Send OUT Channel Request */
if (rpc_ncacn_http_send_out_channel_request(rpc, outChannel, TRUE) < 0)
{
WLog_ERR(TAG, "rpc_ncacn_http_send_out_channel_request failure");
return FALSE;
}
rpc_out_channel_transition_to_state(outChannel, CLIENT_OUT_CHANNEL_STATE_SECURITY);
/* Receive response. */
response = http_response_recv(outChannel->tls);
if (!response)
return -1;
status = rpc_ncacn_http_recv_out_channel_response(rpc, outChannel, response);
http_response_free(response);
if ( status < 0)
{
WLog_ERR(TAG, "rpc_ncacn_http_recv_out_channel_response failure");
return -1;
}
/* Send OUT Channel Request */
if (rpc_ncacn_http_send_out_channel_request(rpc, outChannel, TRUE) < 0)
{
WLog_ERR(TAG, "rpc_ncacn_http_send_out_channel_request failure");
return -1;
}
rpc_ncacn_http_ntlm_uninit(rpc, (RpcChannel*) outChannel);
rpc_out_channel_transition_to_state(outChannel, CLIENT_OUT_CHANNEL_STATE_NEGOTIATED);
return 1;
}
BOOL rpc_connect(rdpRpc* rpc, int timeout)
{
RpcInChannel* inChannel;

View File

@ -779,7 +779,8 @@ RpcInChannel* rpc_client_in_channel_new(rdpRpc* rpc);
void rpc_in_channel_free(RpcInChannel* inChannel);
RpcOutChannel* rpc_out_channel_new(rdpRpc* rpc);
void rpc_client_out_channel_free(RpcOutChannel* outChannel);
int rpc_out_channel_replacement_connect(RpcOutChannel* outChannel, int timeout);
void rpc_out_channel_free(RpcOutChannel* outChannel);
int rpc_in_channel_transition_to_state(RpcInChannel* inChannel, CLIENT_IN_CHANNEL_STATE state);
int rpc_out_channel_transition_to_state(RpcOutChannel* outChannel, CLIENT_OUT_CHANNEL_STATE state);

View File

@ -413,7 +413,6 @@ int rpc_client_recv_fragment(rdpRpc* rpc, wStream* fragment)
if (rpc->VirtualConnection->State < VIRTUAL_CONNECTION_STATE_OPENED)
WLog_ERR(TAG, "warning: unhandled RTS PDU");
WLog_DBG(TAG, "Receiving Out-of-Sequence RTS PDU");
rts_recv_out_of_sequence_pdu(rpc, buffer, header->common.frag_length);
}
@ -452,9 +451,10 @@ int rpc_client_out_channel_recv(rdpRpc* rpc)
HttpResponse* response;
RpcInChannel* inChannel;
RpcOutChannel* outChannel;
RpcVirtualConnection* connection = rpc->VirtualConnection;
inChannel = rpc->VirtualConnection->DefaultInChannel;
outChannel = rpc->VirtualConnection->DefaultOutChannel;
inChannel = connection->DefaultInChannel;
outChannel = connection->DefaultOutChannel;
if (outChannel->State < CLIENT_OUT_CHANNEL_STATE_OPENED)
{
@ -500,7 +500,7 @@ int rpc_client_out_channel_recv(rdpRpc* rpc)
if (inChannel->State == CLIENT_IN_CHANNEL_STATE_OPENED)
{
rpc_virtual_connection_transition_to_state(rpc,
rpc->VirtualConnection, VIRTUAL_CONNECTION_STATE_OUT_CHANNEL_WAIT);
connection, VIRTUAL_CONNECTION_STATE_OUT_CHANNEL_WAIT);
}
status = 1;
@ -508,7 +508,7 @@ int rpc_client_out_channel_recv(rdpRpc* rpc)
http_response_free(response);
}
else if (rpc->VirtualConnection->State == VIRTUAL_CONNECTION_STATE_OUT_CHANNEL_WAIT)
else if (connection->State == VIRTUAL_CONNECTION_STATE_OUT_CHANNEL_WAIT)
{
/* Receive OUT channel response */
@ -604,6 +604,10 @@ int rpc_client_out_channel_recv(rdpRpc* rpc)
status = rpc_client_recv_fragment(rpc, fragment);
/* channel recycling may update channel pointers */
inChannel = connection->DefaultInChannel;
outChannel = connection->DefaultOutChannel;
if (status < 0)
return status;
@ -622,9 +626,10 @@ int rpc_client_in_channel_recv(rdpRpc* rpc)
RpcInChannel* inChannel;
RpcOutChannel* outChannel;
HANDLE InChannelEvent = NULL;
RpcVirtualConnection* connection = rpc->VirtualConnection;
inChannel = rpc->VirtualConnection->DefaultInChannel;
outChannel = rpc->VirtualConnection->DefaultOutChannel;
inChannel = connection->DefaultInChannel;
outChannel = connection->DefaultOutChannel;
BIO_get_event(inChannel->tls->bio, &InChannelEvent);
@ -673,7 +678,7 @@ int rpc_client_in_channel_recv(rdpRpc* rpc)
if (outChannel->State == CLIENT_OUT_CHANNEL_STATE_OPENED)
{
rpc_virtual_connection_transition_to_state(rpc,
rpc->VirtualConnection, VIRTUAL_CONNECTION_STATE_OUT_CHANNEL_WAIT);
connection, VIRTUAL_CONNECTION_STATE_OUT_CHANNEL_WAIT);
}
status = 1;

View File

@ -443,7 +443,7 @@ int rts_send_CONN_A1_pdu(rdpRpc* rpc)
header.Flags = RTS_FLAG_NONE;
header.NumberOfCommands = 4;
WLog_DBG(TAG, "Sending CONN_A1 RTS PDU");
WLog_DBG(TAG, "Sending CONN/A1 RTS PDU");
VirtualConnectionCookie = (BYTE*) &(connection->Cookie);
OUTChannelCookie = (BYTE*) &(outChannel->Cookie);
@ -473,7 +473,7 @@ int rts_recv_CONN_A3_pdu(rdpRpc* rpc, BYTE* buffer, UINT32 length)
rts_connection_timeout_command_read(rpc, &buffer[24], length - 24, &ConnectionTimeout);
WLog_DBG(TAG, "ConnectionTimeout: %d", ConnectionTimeout);
WLog_DBG(TAG, "Receiving CONN/A3 RTS PDU: ConnectionTimeout: %d", ConnectionTimeout);
rpc->VirtualConnection->DefaultInChannel->PingOriginator.ConnectionTimeout = ConnectionTimeout;
@ -499,7 +499,7 @@ int rts_send_CONN_B1_pdu(rdpRpc* rpc)
header.Flags = RTS_FLAG_NONE;
header.NumberOfCommands = 6;
WLog_DBG(TAG, "Sending CONN_B1 RTS PDU");
WLog_DBG(TAG, "Sending CONN/B1 RTS PDU");
VirtualConnectionCookie = (BYTE*) &(connection->Cookie);
INChannelCookie = (BYTE*) &(inChannel->Cookie);
@ -540,8 +540,8 @@ int rts_recv_CONN_C2_pdu(rdpRpc* rpc, BYTE* buffer, UINT32 length)
offset += rts_receive_window_size_command_read(rpc, &buffer[offset], length - offset, &ReceiveWindowSize) + 4;
offset += rts_connection_timeout_command_read(rpc, &buffer[offset], length - offset, &ConnectionTimeout) + 4;
WLog_DBG(TAG, "ConnectionTimeout: %d", ConnectionTimeout);
WLog_DBG(TAG, "ReceiveWindowSize: %d", ReceiveWindowSize);
WLog_DBG(TAG, "Receiving CONN/C2 RTS PDU: ConnectionTimeout: %d ReceiveWindowSize: %d",
ConnectionTimeout, ReceiveWindowSize);
rpc->VirtualConnection->DefaultInChannel->PingOriginator.ConnectionTimeout = ConnectionTimeout;
rpc->VirtualConnection->DefaultInChannel->PeerReceiveWindow = ReceiveWindowSize;
@ -640,11 +640,8 @@ int rts_recv_flow_control_ack_pdu(rdpRpc* rpc, BYTE* buffer, UINT32 length)
offset += rts_flow_control_ack_command_read(rpc, &buffer[offset], length - offset,
&BytesReceived, &AvailableWindow, (BYTE*) &ChannelCookie) + 4;
#if 0
WLog_ERR(TAG, "BytesReceived: %d AvailableWindow: %d",
BytesReceived, AvailableWindow);
WLog_ERR(TAG, "ChannelCookie: " RPC_UUID_FORMAT_STRING "", RPC_UUID_FORMAT_ARGUMENTS(ChannelCookie));
#endif
WLog_ERR(TAG, "Receiving FlowControlAck RTS PDU: BytesReceived: %d AvailableWindow: %d",
BytesReceived, AvailableWindow);
rpc->VirtualConnection->DefaultInChannel->SenderAvailableWindow =
AvailableWindow - (rpc->VirtualConnection->DefaultInChannel->BytesSent - BytesReceived);
@ -681,11 +678,8 @@ int rts_recv_flow_control_ack_with_destination_pdu(rdpRpc* rpc, BYTE* buffer, UI
offset += rts_flow_control_ack_command_read(rpc, &buffer[offset], length - offset,
&BytesReceived, &AvailableWindow, (BYTE*) &ChannelCookie) + 4;
#if 0
WLog_ERR(TAG, "Destination: %d BytesReceived: %d AvailableWindow: %d",
Destination, BytesReceived, AvailableWindow);
WLog_ERR(TAG, "ChannelCookie: " RPC_UUID_FORMAT_STRING "", RPC_UUID_FORMAT_ARGUMENTS(ChannelCookie));
#endif
WLog_ERR(TAG, "Receiving FlowControlAckWithDestination RTS PDU: BytesReceived: %d AvailableWindow: %d",
BytesReceived, AvailableWindow);
rpc->VirtualConnection->DefaultInChannel->SenderAvailableWindow =
AvailableWindow - (rpc->VirtualConnection->DefaultInChannel->BytesSent - BytesReceived);
@ -805,7 +799,7 @@ int rts_send_OUT_R2_A7_pdu(rdpRpc* rpc)
BYTE* buffer;
rpcconn_rts_hdr_t header;
BYTE* SuccessorChannelCookie;
RpcOutChannel* outChannel = rpc->VirtualConnection->DefaultOutChannel;
RpcInChannel* inChannel = rpc->VirtualConnection->DefaultInChannel;
RpcOutChannel* nextOutChannel = rpc->VirtualConnection->NonDefaultOutChannel;
rts_pdu_header_init(&header);
@ -813,7 +807,7 @@ int rts_send_OUT_R2_A7_pdu(rdpRpc* rpc)
header.Flags = RTS_FLAG_OUT_CHANNEL;
header.NumberOfCommands = 3;
WLog_DBG(TAG, "Sending OUT R2/A7 RTS PDU");
WLog_DBG(TAG, "Sending OUT_R2/A7 RTS PDU");
SuccessorChannelCookie = (BYTE*) &(nextOutChannel->Cookie);
@ -827,7 +821,7 @@ int rts_send_OUT_R2_A7_pdu(rdpRpc* rpc)
rts_cookie_command_write(&buffer[28], SuccessorChannelCookie); /* SuccessorChannelCookie (20 bytes) */
rts_version_command_write(&buffer[48]); /* Version (8 bytes) */
status = rpc_out_channel_write(outChannel, buffer, header.frag_length);
status = rpc_in_channel_write(inChannel, buffer, header.frag_length);
free(buffer);
@ -839,14 +833,14 @@ int rts_send_OUT_R2_C1_pdu(rdpRpc* rpc)
int status;
BYTE* buffer;
rpcconn_rts_hdr_t header;
RpcOutChannel* outChannel = rpc->VirtualConnection->DefaultOutChannel;
RpcOutChannel* nextOutChannel = rpc->VirtualConnection->NonDefaultOutChannel;
rts_pdu_header_init(&header);
header.frag_length = 24;
header.Flags = RTS_FLAG_PING;
header.NumberOfCommands = 1;
WLog_DBG(TAG, "Sending OUT R2/C1 RTS PDU");
WLog_DBG(TAG, "Sending OUT_R2/C1 RTS PDU");
buffer = (BYTE*) malloc(header.frag_length);
@ -856,7 +850,7 @@ int rts_send_OUT_R2_C1_pdu(rdpRpc* rpc)
CopyMemory(buffer, ((BYTE*) &header), 20); /* RTS Header (20 bytes) */
rts_empty_command_write(&buffer[20]); /* Empty command (4 bytes) */
status = rpc_out_channel_write(outChannel, buffer, header.frag_length);
status = rpc_out_channel_write(nextOutChannel, buffer, header.frag_length);
free(buffer);
@ -881,7 +875,7 @@ int rts_send_OUT_R1_A3_pdu(rdpRpc* rpc)
header.Flags = RTS_FLAG_RECYCLE_CHANNEL;
header.NumberOfCommands = 5;
WLog_DBG(TAG, "Sending OUT R1/A3 RTS PDU");
WLog_DBG(TAG, "Sending OUT_R1/A3 RTS PDU");
VirtualConnectionCookie = (BYTE*) &(connection->Cookie);
PredecessorChannelCookie = (BYTE*) &(outChannel->Cookie);
@ -900,7 +894,7 @@ int rts_send_OUT_R1_A3_pdu(rdpRpc* rpc)
rts_cookie_command_write(&buffer[68], SuccessorChannelCookie); /* SuccessorChannelCookie (20 bytes) */
rts_receive_window_size_command_write(&buffer[88], ReceiveWindowSize); /* ReceiveWindowSize (8 bytes) */
status = rpc_out_channel_write(outChannel, buffer, header.frag_length);
status = rpc_out_channel_write(nextOutChannel, buffer, header.frag_length);
free(buffer);
@ -909,48 +903,94 @@ int rts_send_OUT_R1_A3_pdu(rdpRpc* rpc)
int rts_recv_OUT_R1_A2_pdu(rdpRpc* rpc, BYTE* buffer, UINT32 length)
{
int status;
UINT32 offset;
UINT32 Destination = 0;
RpcVirtualConnection* connection = rpc->VirtualConnection;
WLog_DBG(TAG, "Receiving OUT R1/A2 RTS PDU");
offset = 24;
offset += rts_destination_command_read(rpc, &buffer[offset], length - offset, &Destination) + 4;
WLog_DBG(TAG, "Destination: %d", Destination);
WLog_ERR(TAG, "TS Gateway channel recycling is incomplete");
connection->NonDefaultOutChannel = rpc_out_channel_new(rpc);
if (!connection->NonDefaultOutChannel)
return -1;
status = rpc_out_channel_replacement_connect(connection->NonDefaultOutChannel, 5000);
if (status < 0)
{
WLog_ERR(TAG, "rpc_out_channel_replacement_connect failure");
return -1;
}
status = rts_send_OUT_R1_A3_pdu(rpc);
if (status < 0)
{
WLog_ERR(TAG, "rts_send_OUT_R1/A3_pdu failure");
return -1;
}
rpc_out_channel_transition_to_state(connection->NonDefaultOutChannel, CLIENT_OUT_CHANNEL_STATE_OPENED_A6W);
rpc_out_channel_transition_to_state(connection->DefaultOutChannel, CLIENT_OUT_CHANNEL_STATE_OPENED_A6W);
return 1;
}
int rts_recv_OUT_R2_A6_pdu(rdpRpc* rpc, BYTE* buffer, UINT32 length)
{
int status;
UINT32 offset;
UINT32 Destination = 0;
RpcVirtualConnection* connection = rpc->VirtualConnection;
offset = 24;
offset += rts_destination_command_read(rpc, &buffer[offset], length - offset, &Destination) + 4;
offset += rts_empty_command_read(rpc, &buffer[offset], length - offset) + 4;
WLog_DBG(TAG, "Destination: %d", Destination);
WLog_DBG(TAG, "Receiving OUT R2/A6 RTS PDU");
status = rts_send_OUT_R2_C1_pdu(rpc);
if (status < 0)
{
WLog_ERR(TAG, "rts_send_OUT_R2_C1_pdu failure");
return -1;
}
status = rts_send_OUT_R2_A7_pdu(rpc);
if (status < 0)
{
WLog_ERR(TAG, "rts_send_OUT_R2_A7_pdu failure");
return -1;
}
rpc_out_channel_transition_to_state(connection->NonDefaultOutChannel, CLIENT_OUT_CHANNEL_STATE_OPENED_B3W);
rpc_out_channel_transition_to_state(connection->DefaultOutChannel, CLIENT_OUT_CHANNEL_STATE_OPENED_B3W);
return 1;
}
int rts_recv_OUT_R2_B3_pdu(rdpRpc* rpc, BYTE* buffer, UINT32 length)
{
UINT32 offset;
int status;
HttpResponse* response;
RpcVirtualConnection* connection = rpc->VirtualConnection;
offset = 24;
offset += rts_ance_command_read(rpc, &buffer[offset], length - offset) + 4;
WLog_DBG(TAG, "Receiving OUT R2/B3 RTS PDU");
rpc_out_channel_free(connection->DefaultOutChannel);
connection->DefaultOutChannel = connection->NonDefaultOutChannel;
connection->NonDefaultOutChannel = NULL;
rpc_out_channel_transition_to_state(connection->DefaultOutChannel, CLIENT_OUT_CHANNEL_STATE_OPENED);
response = http_response_recv(connection->DefaultOutChannel->tls);
if (!response)
return -1;
status = rpc_ncacn_http_recv_out_channel_response(rpc, connection->DefaultOutChannel, response);
http_response_free(response);
return 1;
}
@ -961,34 +1001,54 @@ int rts_recv_out_of_sequence_pdu(rdpRpc* rpc, BYTE* buffer, UINT32 length)
UINT32 SignatureId;
rpcconn_rts_hdr_t* rts;
RtsPduSignature signature;
RpcVirtualConnection* connection = rpc->VirtualConnection;
rts = (rpcconn_rts_hdr_t*) buffer;
rts_extract_pdu_signature(rpc, &signature, rts);
SignatureId = rts_identify_pdu_signature(rpc, &signature, NULL);
switch (SignatureId)
if (rts_match_pdu_signature(rpc, &RTS_PDU_FLOW_CONTROL_ACK_SIGNATURE, rts))
{
case RTS_PDU_FLOW_CONTROL_ACK:
status = rts_recv_flow_control_ack_pdu(rpc, buffer, length);
break;
status = rts_recv_flow_control_ack_pdu(rpc, buffer, length);
}
else if (rts_match_pdu_signature(rpc, &RTS_PDU_FLOW_CONTROL_ACK_WITH_DESTINATION_SIGNATURE, rts))
{
status = rts_recv_flow_control_ack_with_destination_pdu(rpc, buffer, length);
}
else if (rts_match_pdu_signature(rpc, &RTS_PDU_PING_SIGNATURE, rts))
{
status = rts_send_ping_pdu(rpc);
}
else
{
if (connection->DefaultOutChannel->State == CLIENT_OUT_CHANNEL_STATE_OPENED)
{
if (rts_match_pdu_signature(rpc, &RTS_PDU_OUT_R1_A2_SIGNATURE, rts))
{
status = rts_recv_OUT_R1_A2_pdu(rpc, buffer, length);
}
}
else if (connection->DefaultOutChannel->State == CLIENT_OUT_CHANNEL_STATE_OPENED_A6W)
{
if (rts_match_pdu_signature(rpc, &RTS_PDU_OUT_R2_A6_SIGNATURE, rts))
{
status = rts_recv_OUT_R2_A6_pdu(rpc, buffer, length);
}
}
else if (connection->DefaultOutChannel->State == CLIENT_OUT_CHANNEL_STATE_OPENED_B3W)
{
if (rts_match_pdu_signature(rpc, &RTS_PDU_OUT_R2_B3_SIGNATURE, rts))
{
status = rts_recv_OUT_R2_B3_pdu(rpc, buffer, length);
}
}
}
case RTS_PDU_FLOW_CONTROL_ACK_WITH_DESTINATION:
status = rts_recv_flow_control_ack_with_destination_pdu(rpc, buffer, length);
break;
case RTS_PDU_PING:
status = rts_send_ping_pdu(rpc);
break;
case RTS_PDU_OUT_R1_A2:
status = rts_recv_OUT_R1_A2_pdu(rpc, buffer, length);
break;
default:
WLog_ERR(TAG, "unimplemented signature id: 0x%08X", SignatureId);
rts_print_pdu_signature(rpc, &signature);
break;
if (status < 0)
{
WLog_ERR(TAG, "error parsing RTS PDU with signature id: 0x%08X", SignatureId);
rts_print_pdu_signature(rpc, &signature);
}
return status;