dd6d829550
This big patch allows to have non-blocking writes. To achieve this, it slightly changes the way transport is handled. The misc transport layers are handled with OpenSSL BIOs. In the chain we insert a bufferedBIO that will bufferize write calls that couldn't be honored. For an access with Tls security the BIO chain would look like this: FreeRdp Code ===> SSL bio ===> buffered BIO ===> socket BIO The buffered BIO will store bytes that couldn't be send because of blocking write calls. This patch also rework TSG so that it would look like this in the case of SSL security with TSG: (TSG in) > SSL BIO => buffered BIO ==> socket BIO / FreeRdp => SSL BIO => TSG BIO \ > SSL BIO => buffered BIO ==> socket BIO (TSG out) So from the FreeRDP point of view sending something is only BIO_writing on the frontBio (last BIO on the left).
1065 lines
30 KiB
C
1065 lines
30 KiB
C
/**
|
|
* FreeRDP: A Remote Desktop Protocol Implementation
|
|
* Request To Send (RTS) PDUs
|
|
*
|
|
* 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 <winpr/crt.h>
|
|
#include <winpr/winhttp.h>
|
|
|
|
#include "ncacn_http.h"
|
|
#include "rpc_client.h"
|
|
|
|
#include "rts.h"
|
|
|
|
/**
|
|
* [MS-RPCH]: Remote Procedure Call over HTTP Protocol Specification:
|
|
* http://msdn.microsoft.com/en-us/library/cc243950/
|
|
*/
|
|
|
|
/**
|
|
* 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
|
|
*
|
|
*/
|
|
|
|
BOOL rts_connect(rdpRpc* rpc)
|
|
{
|
|
RPC_PDU* pdu;
|
|
rpcconn_rts_hdr_t* rts;
|
|
HttpResponse* http_response;
|
|
|
|
/**
|
|
* Connection Opening
|
|
*
|
|
* When opening a virtual connection to the server, an implementation of this protocol MUST perform
|
|
* the following sequence of steps:
|
|
*
|
|
* 1. Send an IN channel request as specified in section 2.1.2.1.1, containing the connection timeout,
|
|
* ResourceType UUID, and Session UUID values, if any, supplied by the higher-layer protocol or application.
|
|
*
|
|
* 2. Send an OUT channel request as specified in section 2.1.2.1.2.
|
|
*
|
|
* 3. Send a CONN/A1 RTS PDU as specified in section 2.2.4.2
|
|
*
|
|
* 4. Send a CONN/B1 RTS PDU as specified in section 2.2.4.5
|
|
*
|
|
* 5. Wait for the connection establishment protocol sequence as specified in 3.2.1.5.3.1 to complete
|
|
*
|
|
* An implementation MAY execute steps 1 and 2 in parallel. An implementation SHOULD execute steps
|
|
* 3 and 4 in parallel. An implementation MUST execute step 3 after completion of step 1 and execute
|
|
* step 4 after completion of step 2.
|
|
*
|
|
*/
|
|
|
|
rpc->VirtualConnection->State = VIRTUAL_CONNECTION_STATE_INITIAL;
|
|
DEBUG_RTS("VIRTUAL_CONNECTION_STATE_INITIAL");
|
|
|
|
rpc->client->SynchronousSend = TRUE;
|
|
rpc->client->SynchronousReceive = TRUE;
|
|
|
|
if (!rpc_ntlm_http_out_connect(rpc))
|
|
{
|
|
fprintf(stderr, "%s: rpc_out_connect_http error!\n", __FUNCTION__);
|
|
return FALSE;
|
|
}
|
|
|
|
if (rts_send_CONN_A1_pdu(rpc) != 0)
|
|
{
|
|
fprintf(stderr, "%s: rpc_send_CONN_A1_pdu error!\n", __FUNCTION__);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!rpc_ntlm_http_in_connect(rpc))
|
|
{
|
|
fprintf(stderr, "%s: rpc_in_connect_http error!\n", __FUNCTION__);
|
|
return FALSE;
|
|
}
|
|
|
|
if (rts_send_CONN_B1_pdu(rpc) < 0)
|
|
{
|
|
fprintf(stderr, "%s: rpc_send_CONN_B1_pdu error!\n", __FUNCTION__);
|
|
return FALSE;
|
|
}
|
|
|
|
rpc->VirtualConnection->State = VIRTUAL_CONNECTION_STATE_OUT_CHANNEL_WAIT;
|
|
DEBUG_RTS("VIRTUAL_CONNECTION_STATE_OUT_CHANNEL_WAIT");
|
|
|
|
/**
|
|
* Receive OUT Channel Response
|
|
*
|
|
* A client implementation MUST NOT accept the OUT channel HTTP response in any state other than
|
|
* Out Channel Wait. If received in any other state, this HTTP response is a protocol error. Therefore,
|
|
* the client MUST consider the virtual connection opening a failure and indicate this to higher layers
|
|
* in an implementation-specific way. The Microsoft Windows® implementation returns
|
|
* RPC_S_PROTOCOL_ERROR, as specified in [MS-ERREF], to higher-layer protocols.
|
|
*
|
|
* If this HTTP response is received in Out Channel Wait state, the client MUST process the fields of
|
|
* this response as defined in this section.
|
|
*
|
|
* First, the client MUST determine whether the response indicates a success or a failure. If the status
|
|
* code is set to 200, the client MUST interpret this as a success, and it MUST do the following:
|
|
*
|
|
* 1. Ignore the values of all other header fields.
|
|
*
|
|
* 2. Transition to Wait_A3W state.
|
|
*
|
|
* 3. Wait for network events.
|
|
*
|
|
* 4. Skip the rest of the processing in this section.
|
|
*
|
|
* If the status code is not set to 200, the client MUST interpret this as a failure and follow the same
|
|
* processing rules as specified in section 3.2.2.5.6.
|
|
*
|
|
*/
|
|
|
|
http_response = http_response_recv(rpc->TlsOut);
|
|
if (!http_response)
|
|
{
|
|
fprintf(stderr, "%s: unable to retrieve OUT Channel Response!\n", __FUNCTION__);
|
|
return FALSE;
|
|
}
|
|
|
|
if (http_response->StatusCode != HTTP_STATUS_OK)
|
|
{
|
|
fprintf(stderr, "%s: error! Status Code: %d\n", __FUNCTION__, http_response->StatusCode);
|
|
http_response_print(http_response);
|
|
http_response_free(http_response);
|
|
|
|
if (http_response->StatusCode == HTTP_STATUS_DENIED)
|
|
{
|
|
if (!connectErrorCode)
|
|
{
|
|
connectErrorCode = AUTHENTICATIONERROR;
|
|
}
|
|
|
|
if (!freerdp_get_last_error(((freerdp*)(rpc->settings->instance))->context))
|
|
{
|
|
freerdp_set_last_error(((freerdp*)(rpc->settings->instance))->context, FREERDP_ERROR_AUTHENTICATION_FAILED);
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
if (http_response->bodyLen)
|
|
{
|
|
/* inject bytes we have read in the body as a received packet for the RPC client */
|
|
rpc->client->RecvFrag = rpc_client_fragment_pool_take(rpc);
|
|
Stream_EnsureCapacity(rpc->client->RecvFrag, http_response->bodyLen);
|
|
CopyMemory(rpc->client->RecvFrag, http_response->BodyContent, http_response->bodyLen);
|
|
}
|
|
|
|
//http_response_print(http_response);
|
|
http_response_free(http_response);
|
|
|
|
rpc->VirtualConnection->State = VIRTUAL_CONNECTION_STATE_WAIT_A3W;
|
|
DEBUG_RTS("VIRTUAL_CONNECTION_STATE_WAIT_A3W");
|
|
|
|
/**
|
|
* Receive CONN_A3 RTS PDU
|
|
*
|
|
* A client implementation MUST NOT accept the CONN/A3 RTS PDU in any state other than
|
|
* Wait_A3W. If received in any other state, this PDU is a protocol error and the client
|
|
* MUST consider the virtual connection opening a failure and indicate this to higher
|
|
* layers in an implementation-specific way.
|
|
*
|
|
* Set the ConnectionTimeout in the Ping Originator of the Client's IN Channel to the
|
|
* ConnectionTimeout in the CONN/A3 PDU.
|
|
*
|
|
* If this RTS PDU is received in Wait_A3W state, the client MUST transition the state
|
|
* machine to Wait_C2 state and wait for network events.
|
|
*
|
|
*/
|
|
|
|
rpc_client_start(rpc);
|
|
|
|
pdu = rpc_recv_dequeue_pdu(rpc);
|
|
if (!pdu)
|
|
return FALSE;
|
|
|
|
rts = (rpcconn_rts_hdr_t*) Stream_Buffer(pdu->s);
|
|
|
|
if (!rts_match_pdu_signature(rpc, &RTS_PDU_CONN_A3_SIGNATURE, rts))
|
|
{
|
|
fprintf(stderr, "%s: unexpected RTS PDU: Expected CONN/A3\n", __FUNCTION__);
|
|
return FALSE;
|
|
}
|
|
|
|
rts_recv_CONN_A3_pdu(rpc, Stream_Buffer(pdu->s), Stream_Length(pdu->s));
|
|
|
|
rpc_client_receive_pool_return(rpc, pdu);
|
|
|
|
rpc->VirtualConnection->State = VIRTUAL_CONNECTION_STATE_WAIT_C2;
|
|
DEBUG_RTS("VIRTUAL_CONNECTION_STATE_WAIT_C2");
|
|
|
|
/**
|
|
* Receive CONN_C2 RTS PDU
|
|
*
|
|
* A client implementation MUST NOT accept the CONN/C2 RTS PDU in any state other than Wait_C2.
|
|
* If received in any other state, this PDU is a protocol error and the client MUST consider the virtual
|
|
* connection opening a failure and indicate this to higher layers in an implementation-specific way.
|
|
*
|
|
* If this RTS PDU is received in Wait_C2 state, the client implementation MUST do the following:
|
|
*
|
|
* 1. Transition the state machine to opened state.
|
|
*
|
|
* 2. Set the connection time-out protocol variable to the value of the ConnectionTimeout field from
|
|
* the CONN/C2 RTS PDU.
|
|
*
|
|
* 3. Set the PeerReceiveWindow value in the SendingChannel of the Client IN Channel to the
|
|
* ReceiveWindowSize value in the CONN/C2 PDU.
|
|
*
|
|
* 4. Indicate to higher-layer protocols that the virtual connection opening is a success.
|
|
*
|
|
*/
|
|
|
|
pdu = rpc_recv_dequeue_pdu(rpc);
|
|
if (!pdu)
|
|
return FALSE;
|
|
|
|
rts = (rpcconn_rts_hdr_t*) Stream_Buffer(pdu->s);
|
|
|
|
if (!rts_match_pdu_signature(rpc, &RTS_PDU_CONN_C2_SIGNATURE, rts))
|
|
{
|
|
fprintf(stderr, "%s: unexpected RTS PDU: Expected CONN/C2\n", __FUNCTION__);
|
|
return FALSE;
|
|
}
|
|
|
|
rts_recv_CONN_C2_pdu(rpc, Stream_Buffer(pdu->s), Stream_Length(pdu->s));
|
|
|
|
rpc_client_receive_pool_return(rpc, pdu);
|
|
|
|
rpc->VirtualConnection->State = VIRTUAL_CONNECTION_STATE_OPENED;
|
|
DEBUG_RTS("VIRTUAL_CONNECTION_STATE_OPENED");
|
|
|
|
rpc->client->SynchronousSend = TRUE;
|
|
rpc->client->SynchronousReceive = TRUE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
#ifdef WITH_DEBUG_RTS
|
|
|
|
static const char* const RTS_CMD_STRINGS[] =
|
|
{
|
|
"ReceiveWindowSize",
|
|
"FlowControlAck",
|
|
"ConnectionTimeout",
|
|
"Cookie",
|
|
"ChannelLifetime",
|
|
"ClientKeepalive",
|
|
"Version",
|
|
"Empty",
|
|
"Padding",
|
|
"NegativeANCE",
|
|
"ANCE",
|
|
"ClientAddress",
|
|
"AssociationGroupId",
|
|
"Destination",
|
|
"PingTrafficSentNotify"
|
|
};
|
|
|
|
#endif
|
|
|
|
/**
|
|
* RTS PDU Header
|
|
*
|
|
* The RTS PDU Header has the same layout as the common header of the connection-oriented RPC
|
|
* PDU as specified in [C706] section 12.6.1, with a few additional requirements around the contents
|
|
* of the header fields. The additional requirements are as follows:
|
|
*
|
|
* All fields MUST use little-endian byte order.
|
|
*
|
|
* Fragmentation MUST NOT occur for an RTS PDU.
|
|
*
|
|
* PFC_FIRST_FRAG and PFC_LAST_FRAG MUST be present in all RTS PDUs, and all other PFC flags
|
|
* MUST NOT be present.
|
|
*
|
|
* The rpc_vers and rpc_vers_minor fields MUST contain version information as described in
|
|
* [MS-RPCE] section 1.7.
|
|
*
|
|
* PTYPE MUST be set to a value of 20 (0x14). This field differentiates RTS packets from other RPC packets.
|
|
*
|
|
* The packed_drep MUST indicate little-endian integer and floating-pointer byte order, IEEE float-point
|
|
* format representation, and ASCII character format as specified in [C706] section 12.6.
|
|
*
|
|
* The auth_length MUST be set to 0.
|
|
*
|
|
* The frag_length field MUST reflect the size of the header plus the size of all commands, including
|
|
* the variable portion of variable-sized commands.
|
|
*
|
|
* The call_id MUST be set to 0 by senders and MUST be 0 on receipt.
|
|
*
|
|
*/
|
|
|
|
void rts_pdu_header_init(rpcconn_rts_hdr_t* header)
|
|
{
|
|
ZeroMemory(header, sizeof(*header));
|
|
header->rpc_vers = 5;
|
|
header->rpc_vers_minor = 0;
|
|
header->ptype = PTYPE_RTS;
|
|
header->packed_drep[0] = 0x10;
|
|
header->packed_drep[1] = 0x00;
|
|
header->packed_drep[2] = 0x00;
|
|
header->packed_drep[3] = 0x00;
|
|
header->pfc_flags = PFC_FIRST_FRAG | PFC_LAST_FRAG;
|
|
header->auth_length = 0;
|
|
header->call_id = 0;
|
|
}
|
|
|
|
int rts_receive_window_size_command_read(rdpRpc* rpc, BYTE* buffer, UINT32 length, UINT32* ReceiveWindowSize)
|
|
{
|
|
if (ReceiveWindowSize)
|
|
*ReceiveWindowSize = *((UINT32*) &buffer[0]); /* ReceiveWindowSize (4 bytes) */
|
|
|
|
return 4;
|
|
}
|
|
|
|
int rts_receive_window_size_command_write(BYTE* buffer, UINT32 ReceiveWindowSize)
|
|
{
|
|
if (buffer)
|
|
{
|
|
*((UINT32*) &buffer[0]) = RTS_CMD_RECEIVE_WINDOW_SIZE; /* CommandType (4 bytes) */
|
|
*((UINT32*) &buffer[4]) = ReceiveWindowSize; /* ReceiveWindowSize (4 bytes) */
|
|
}
|
|
|
|
return 8;
|
|
}
|
|
|
|
int rts_flow_control_ack_command_read(rdpRpc* rpc, BYTE* buffer, UINT32 length,
|
|
UINT32* BytesReceived, UINT32* AvailableWindow, BYTE* ChannelCookie)
|
|
{
|
|
/* Ack (24 bytes) */
|
|
|
|
if (BytesReceived)
|
|
*BytesReceived = *((UINT32*) &buffer[0]); /* BytesReceived (4 bytes) */
|
|
|
|
if (AvailableWindow)
|
|
*AvailableWindow = *((UINT32*) &buffer[4]); /* AvailableWindow (4 bytes) */
|
|
|
|
if (ChannelCookie)
|
|
CopyMemory(ChannelCookie, &buffer[8], 16); /* ChannelCookie (16 bytes) */
|
|
|
|
return 24;
|
|
}
|
|
|
|
int rts_flow_control_ack_command_write(BYTE* buffer, UINT32 BytesReceived, UINT32 AvailableWindow, BYTE* ChannelCookie)
|
|
{
|
|
if (buffer)
|
|
{
|
|
*((UINT32*) &buffer[0]) = RTS_CMD_FLOW_CONTROL_ACK; /* CommandType (4 bytes) */
|
|
|
|
/* Ack (24 bytes) */
|
|
*((UINT32*) &buffer[4]) = BytesReceived; /* BytesReceived (4 bytes) */
|
|
*((UINT32*) &buffer[8]) = AvailableWindow; /* AvailableWindow (4 bytes) */
|
|
CopyMemory(&buffer[12], ChannelCookie, 16); /* ChannelCookie (16 bytes) */
|
|
}
|
|
|
|
return 28;
|
|
}
|
|
|
|
int rts_connection_timeout_command_read(rdpRpc* rpc, BYTE* buffer, UINT32 length, UINT32* ConnectionTimeout)
|
|
{
|
|
if (ConnectionTimeout)
|
|
*ConnectionTimeout = *((UINT32*) &buffer[0]); /* ConnectionTimeout (4 bytes) */
|
|
|
|
return 4;
|
|
}
|
|
|
|
int rts_connection_timeout_command_write(BYTE* buffer, UINT32 ConnectionTimeout)
|
|
{
|
|
if (buffer)
|
|
{
|
|
*((UINT32*) &buffer[0]) = RTS_CMD_CONNECTION_TIMEOUT; /* CommandType (4 bytes) */
|
|
*((UINT32*) &buffer[4]) = ConnectionTimeout; /* ConnectionTimeout (4 bytes) */
|
|
}
|
|
|
|
return 8;
|
|
}
|
|
|
|
int rts_cookie_command_read(rdpRpc* rpc, BYTE* buffer, UINT32 length)
|
|
{
|
|
/* Cookie (16 bytes) */
|
|
|
|
return 16;
|
|
}
|
|
|
|
int rts_cookie_command_write(BYTE* buffer, BYTE* Cookie)
|
|
{
|
|
if (buffer)
|
|
{
|
|
*((UINT32*) &buffer[0]) = RTS_CMD_COOKIE; /* CommandType (4 bytes) */
|
|
CopyMemory(&buffer[4], Cookie, 16); /* Cookie (16 bytes) */
|
|
}
|
|
|
|
return 20;
|
|
}
|
|
|
|
int rts_channel_lifetime_command_read(rdpRpc* rpc, BYTE* buffer, UINT32 length)
|
|
{
|
|
/* ChannelLifetime (4 bytes) */
|
|
|
|
return 4;
|
|
}
|
|
|
|
int rts_channel_lifetime_command_write(BYTE* buffer, UINT32 ChannelLifetime)
|
|
{
|
|
if (buffer)
|
|
{
|
|
*((UINT32*) &buffer[0]) = RTS_CMD_CHANNEL_LIFETIME; /* CommandType (4 bytes) */
|
|
*((UINT32*) &buffer[4]) = ChannelLifetime; /* ChannelLifetime (4 bytes) */
|
|
}
|
|
|
|
return 8;
|
|
}
|
|
|
|
int rts_client_keepalive_command_read(rdpRpc* rpc, BYTE* buffer, UINT32 length)
|
|
{
|
|
/* ClientKeepalive (4 bytes) */
|
|
|
|
return 4;
|
|
}
|
|
|
|
int rts_client_keepalive_command_write(BYTE* buffer, UINT32 ClientKeepalive)
|
|
{
|
|
/**
|
|
* An unsigned integer that specifies the keep-alive interval, in milliseconds,
|
|
* that this connection is configured to use. This value MUST be 0 or in the inclusive
|
|
* range of 60,000 through 4,294,967,295. If it is 0, it MUST be interpreted as 300,000.
|
|
*/
|
|
|
|
if (buffer)
|
|
{
|
|
*((UINT32*) &buffer[0]) = RTS_CMD_CLIENT_KEEPALIVE; /* CommandType (4 bytes) */
|
|
*((UINT32*) &buffer[4]) = ClientKeepalive; /* ClientKeepalive (4 bytes) */
|
|
}
|
|
|
|
return 8;
|
|
}
|
|
|
|
int rts_version_command_read(rdpRpc* rpc, BYTE* buffer, UINT32 length)
|
|
{
|
|
/* Version (4 bytes) */
|
|
|
|
return 4;
|
|
}
|
|
|
|
int rts_version_command_write(BYTE* buffer)
|
|
{
|
|
if (buffer)
|
|
{
|
|
*((UINT32*) &buffer[0]) = RTS_CMD_VERSION; /* CommandType (4 bytes) */
|
|
*((UINT32*) &buffer[4]) = 1; /* Version (4 bytes) */
|
|
}
|
|
|
|
return 8;
|
|
}
|
|
|
|
int rts_empty_command_read(rdpRpc* rpc, BYTE* buffer, UINT32 length)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int rts_empty_command_write(BYTE* buffer)
|
|
{
|
|
if (buffer)
|
|
{
|
|
*((UINT32*) &buffer[0]) = RTS_CMD_EMPTY; /* CommandType (4 bytes) */
|
|
}
|
|
|
|
return 4;
|
|
}
|
|
|
|
int rts_padding_command_read(rdpRpc* rpc, BYTE* buffer, UINT32 length)
|
|
{
|
|
UINT32 ConformanceCount;
|
|
|
|
ConformanceCount = *((UINT32*) &buffer[0]); /* ConformanceCount (4 bytes) */
|
|
/* Padding (variable) */
|
|
|
|
return ConformanceCount + 4;
|
|
}
|
|
|
|
int rts_padding_command_write(BYTE* buffer, UINT32 ConformanceCount)
|
|
{
|
|
if (buffer)
|
|
{
|
|
*((UINT32*) &buffer[0]) = RTS_CMD_PADDING; /* CommandType (4 bytes) */
|
|
*((UINT32*) &buffer[4]) = ConformanceCount; /* ConformanceCount (4 bytes) */
|
|
ZeroMemory(&buffer[8], ConformanceCount); /* Padding (variable) */
|
|
}
|
|
|
|
return 8 + ConformanceCount;
|
|
}
|
|
|
|
int rts_negative_ance_command_read(rdpRpc* rpc, BYTE* buffer, UINT32 length)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int rts_negative_ance_command_write(BYTE* buffer)
|
|
{
|
|
if (buffer)
|
|
{
|
|
*((UINT32*) &buffer[0]) = RTS_CMD_NEGATIVE_ANCE; /* CommandType (4 bytes) */
|
|
}
|
|
|
|
return 4;
|
|
}
|
|
|
|
int rts_ance_command_read(rdpRpc* rpc, BYTE* buffer, UINT32 length)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int rts_ance_command_write(BYTE* buffer)
|
|
{
|
|
if (buffer)
|
|
{
|
|
*((UINT32*) &buffer[0]) = RTS_CMD_ANCE; /* CommandType (4 bytes) */
|
|
}
|
|
|
|
return 4;
|
|
}
|
|
|
|
int rts_client_address_command_read(rdpRpc* rpc, BYTE* buffer, UINT32 length)
|
|
{
|
|
UINT32 AddressType;
|
|
|
|
AddressType = *((UINT32*) &buffer[0]); /* AddressType (4 bytes) */
|
|
|
|
if (AddressType == 0)
|
|
{
|
|
/* ClientAddress (4 bytes) */
|
|
/* padding (12 bytes) */
|
|
|
|
return 4 + 4 + 12;
|
|
}
|
|
else
|
|
{
|
|
/* ClientAddress (16 bytes) */
|
|
/* padding (12 bytes) */
|
|
|
|
return 4 + 16 + 12;
|
|
}
|
|
}
|
|
|
|
int rts_client_address_command_write(BYTE* buffer, UINT32 AddressType, BYTE* ClientAddress)
|
|
{
|
|
if (buffer)
|
|
{
|
|
*((UINT32*) &buffer[0]) = RTS_CMD_CLIENT_ADDRESS; /* CommandType (4 bytes) */
|
|
*((UINT32*) &buffer[4]) = AddressType; /* AddressType (4 bytes) */
|
|
}
|
|
|
|
if (AddressType == 0)
|
|
{
|
|
if (buffer)
|
|
{
|
|
CopyMemory(&buffer[8], ClientAddress, 4); /* ClientAddress (4 bytes) */
|
|
ZeroMemory(&buffer[12], 12); /* padding (12 bytes) */
|
|
}
|
|
|
|
return 24;
|
|
}
|
|
else
|
|
{
|
|
if (buffer)
|
|
{
|
|
CopyMemory(&buffer[8], ClientAddress, 16); /* ClientAddress (16 bytes) */
|
|
ZeroMemory(&buffer[24], 12); /* padding (12 bytes) */
|
|
}
|
|
|
|
return 36;
|
|
}
|
|
}
|
|
|
|
int rts_association_group_id_command_read(rdpRpc* rpc, BYTE* buffer, UINT32 length)
|
|
{
|
|
/* AssociationGroupId (16 bytes) */
|
|
|
|
return 16;
|
|
}
|
|
|
|
int rts_association_group_id_command_write(BYTE* buffer, BYTE* AssociationGroupId)
|
|
{
|
|
if (buffer)
|
|
{
|
|
*((UINT32*) &buffer[0]) = RTS_CMD_ASSOCIATION_GROUP_ID; /* CommandType (4 bytes) */
|
|
CopyMemory(&buffer[4], AssociationGroupId, 16); /* AssociationGroupId (16 bytes) */
|
|
}
|
|
|
|
return 20;
|
|
}
|
|
|
|
int rts_destination_command_read(rdpRpc* rpc, BYTE* buffer, UINT32 length, UINT32* Destination)
|
|
{
|
|
if (Destination)
|
|
*Destination = *((UINT32*) &buffer[0]); /* Destination (4 bytes) */
|
|
|
|
return 4;
|
|
}
|
|
|
|
int rts_destination_command_write(BYTE* buffer, UINT32 Destination)
|
|
{
|
|
if (buffer)
|
|
{
|
|
*((UINT32*) &buffer[0]) = RTS_CMD_DESTINATION; /* CommandType (4 bytes) */
|
|
*((UINT32*) &buffer[4]) = Destination; /* Destination (4 bytes) */
|
|
}
|
|
|
|
return 8;
|
|
}
|
|
|
|
int rts_ping_traffic_sent_notify_command_read(rdpRpc* rpc, BYTE* buffer, UINT32 length)
|
|
{
|
|
/* PingTrafficSent (4 bytes) */
|
|
|
|
return 4;
|
|
}
|
|
|
|
int rts_ping_traffic_sent_notify_command_write(BYTE* buffer, UINT32 PingTrafficSent)
|
|
{
|
|
if (buffer)
|
|
{
|
|
*((UINT32*) &buffer[0]) = RTS_CMD_PING_TRAFFIC_SENT_NOTIFY; /* CommandType (4 bytes) */
|
|
*((UINT32*) &buffer[4]) = PingTrafficSent; /* PingTrafficSent (4 bytes) */
|
|
}
|
|
|
|
return 8;
|
|
}
|
|
|
|
void rts_generate_cookie(BYTE* cookie)
|
|
{
|
|
RAND_pseudo_bytes(cookie, 16);
|
|
}
|
|
|
|
/* CONN/A Sequence */
|
|
|
|
int rts_send_CONN_A1_pdu(rdpRpc* rpc)
|
|
{
|
|
BYTE* buffer;
|
|
rpcconn_rts_hdr_t header;
|
|
UINT32 ReceiveWindowSize;
|
|
BYTE* OUTChannelCookie;
|
|
BYTE* VirtualConnectionCookie;
|
|
|
|
rts_pdu_header_init(&header);
|
|
header.frag_length = 76;
|
|
header.Flags = RTS_FLAG_NONE;
|
|
header.NumberOfCommands = 4;
|
|
|
|
DEBUG_RPC("Sending CONN_A1 RTS PDU");
|
|
|
|
rts_generate_cookie((BYTE*) &(rpc->VirtualConnection->Cookie));
|
|
rts_generate_cookie((BYTE*) &(rpc->VirtualConnection->DefaultOutChannelCookie));
|
|
|
|
VirtualConnectionCookie = (BYTE*) &(rpc->VirtualConnection->Cookie);
|
|
OUTChannelCookie = (BYTE*) &(rpc->VirtualConnection->DefaultOutChannelCookie);
|
|
ReceiveWindowSize = rpc->VirtualConnection->DefaultOutChannel->ReceiveWindow;
|
|
|
|
buffer = (BYTE*) malloc(header.frag_length);
|
|
if (!buffer)
|
|
return -1;
|
|
|
|
CopyMemory(buffer, ((BYTE*) &header), 20); /* RTS Header (20 bytes) */
|
|
rts_version_command_write(&buffer[20]); /* Version (8 bytes) */
|
|
rts_cookie_command_write(&buffer[28], VirtualConnectionCookie); /* VirtualConnectionCookie (20 bytes) */
|
|
rts_cookie_command_write(&buffer[48], OUTChannelCookie); /* OUTChannelCookie (20 bytes) */
|
|
rts_receive_window_size_command_write(&buffer[68], ReceiveWindowSize); /* ReceiveWindowSize (8 bytes) */
|
|
|
|
rpc_out_write(rpc, buffer, header.frag_length);
|
|
|
|
free(buffer);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int rts_recv_CONN_A3_pdu(rdpRpc* rpc, BYTE* buffer, UINT32 length)
|
|
{
|
|
UINT32 ConnectionTimeout;
|
|
|
|
rts_connection_timeout_command_read(rpc, &buffer[24], length - 24, &ConnectionTimeout);
|
|
|
|
DEBUG_RTS("ConnectionTimeout: %d", ConnectionTimeout);
|
|
|
|
rpc->VirtualConnection->DefaultInChannel->PingOriginator.ConnectionTimeout = ConnectionTimeout;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* CONN/B Sequence */
|
|
|
|
int rts_send_CONN_B1_pdu(rdpRpc* rpc)
|
|
{
|
|
BYTE* buffer;
|
|
UINT32 length;
|
|
rpcconn_rts_hdr_t header;
|
|
BYTE* INChannelCookie;
|
|
BYTE* AssociationGroupId;
|
|
BYTE* VirtualConnectionCookie;
|
|
int status;
|
|
|
|
rts_pdu_header_init(&header);
|
|
header.frag_length = 104;
|
|
header.Flags = RTS_FLAG_NONE;
|
|
header.NumberOfCommands = 6;
|
|
|
|
DEBUG_RPC("Sending CONN_B1 RTS PDU");
|
|
|
|
rts_generate_cookie((BYTE*) &(rpc->VirtualConnection->DefaultInChannelCookie));
|
|
rts_generate_cookie((BYTE*) &(rpc->VirtualConnection->AssociationGroupId));
|
|
|
|
VirtualConnectionCookie = (BYTE*) &(rpc->VirtualConnection->Cookie);
|
|
INChannelCookie = (BYTE*) &(rpc->VirtualConnection->DefaultInChannelCookie);
|
|
AssociationGroupId = (BYTE*) &(rpc->VirtualConnection->AssociationGroupId);
|
|
|
|
buffer = (BYTE*) malloc(header.frag_length);
|
|
if (!buffer)
|
|
return -1;
|
|
|
|
CopyMemory(buffer, ((BYTE*) &header), 20); /* RTS Header (20 bytes) */
|
|
rts_version_command_write(&buffer[20]); /* Version (8 bytes) */
|
|
rts_cookie_command_write(&buffer[28], VirtualConnectionCookie); /* VirtualConnectionCookie (20 bytes) */
|
|
rts_cookie_command_write(&buffer[48], INChannelCookie); /* INChannelCookie (20 bytes) */
|
|
rts_channel_lifetime_command_write(&buffer[68], rpc->ChannelLifetime); /* ChannelLifetime (8 bytes) */
|
|
rts_client_keepalive_command_write(&buffer[76], rpc->KeepAliveInterval); /* ClientKeepalive (8 bytes) */
|
|
rts_association_group_id_command_write(&buffer[84], AssociationGroupId); /* AssociationGroupId (20 bytes) */
|
|
|
|
length = header.frag_length;
|
|
|
|
status = rpc_in_write(rpc, buffer, length);
|
|
|
|
free(buffer);
|
|
|
|
return status;
|
|
}
|
|
|
|
/* CONN/C Sequence */
|
|
|
|
int rts_recv_CONN_C2_pdu(rdpRpc* rpc, BYTE* buffer, UINT32 length)
|
|
{
|
|
UINT32 offset;
|
|
UINT32 ReceiveWindowSize;
|
|
UINT32 ConnectionTimeout;
|
|
|
|
offset = 24;
|
|
offset += rts_version_command_read(rpc, &buffer[offset], length - offset) + 4;
|
|
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;
|
|
|
|
DEBUG_RTS("ConnectionTimeout: %d", ConnectionTimeout);
|
|
DEBUG_RTS("ReceiveWindowSize: %d", ReceiveWindowSize);
|
|
|
|
/* TODO: verify if this is the correct protocol variable */
|
|
rpc->VirtualConnection->DefaultInChannel->PingOriginator.ConnectionTimeout = ConnectionTimeout;
|
|
|
|
rpc->VirtualConnection->DefaultInChannel->PeerReceiveWindow = ReceiveWindowSize;
|
|
|
|
rpc->VirtualConnection->DefaultInChannel->State = CLIENT_IN_CHANNEL_STATE_OPENED;
|
|
rpc->VirtualConnection->DefaultOutChannel->State = CLIENT_OUT_CHANNEL_STATE_OPENED;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Out-of-Sequence PDUs */
|
|
|
|
int rts_send_keep_alive_pdu(rdpRpc* rpc)
|
|
{
|
|
BYTE* buffer;
|
|
UINT32 length;
|
|
rpcconn_rts_hdr_t header;
|
|
|
|
rts_pdu_header_init(&header);
|
|
header.frag_length = 28;
|
|
header.Flags = RTS_FLAG_OTHER_CMD;
|
|
header.NumberOfCommands = 1;
|
|
|
|
DEBUG_RPC("Sending Keep-Alive RTS PDU");
|
|
|
|
buffer = (BYTE*) malloc(header.frag_length);
|
|
if (!buffer)
|
|
return -1;
|
|
CopyMemory(buffer, ((BYTE*) &header), 20); /* RTS Header (20 bytes) */
|
|
rts_client_keepalive_command_write(&buffer[20], rpc->CurrentKeepAliveInterval); /* ClientKeepAlive (8 bytes) */
|
|
|
|
length = header.frag_length;
|
|
|
|
if (rpc_in_write(rpc, buffer, length) < 0)
|
|
return -1;
|
|
free(buffer);
|
|
|
|
return length;
|
|
}
|
|
|
|
int rts_send_flow_control_ack_pdu(rdpRpc* rpc)
|
|
{
|
|
BYTE* buffer;
|
|
UINT32 length;
|
|
rpcconn_rts_hdr_t header;
|
|
UINT32 BytesReceived;
|
|
UINT32 AvailableWindow;
|
|
BYTE* ChannelCookie;
|
|
|
|
rts_pdu_header_init(&header);
|
|
header.frag_length = 56;
|
|
header.Flags = RTS_FLAG_OTHER_CMD;
|
|
header.NumberOfCommands = 2;
|
|
|
|
DEBUG_RPC("Sending FlowControlAck RTS PDU");
|
|
|
|
BytesReceived = rpc->VirtualConnection->DefaultOutChannel->BytesReceived;
|
|
AvailableWindow = rpc->VirtualConnection->DefaultOutChannel->AvailableWindowAdvertised;
|
|
ChannelCookie = (BYTE*) &(rpc->VirtualConnection->DefaultOutChannelCookie);
|
|
|
|
rpc->VirtualConnection->DefaultOutChannel->ReceiverAvailableWindow =
|
|
rpc->VirtualConnection->DefaultOutChannel->AvailableWindowAdvertised;
|
|
|
|
buffer = (BYTE*) malloc(header.frag_length);
|
|
if (!buffer)
|
|
return -1;
|
|
|
|
CopyMemory(buffer, ((BYTE*) &header), 20); /* RTS Header (20 bytes) */
|
|
rts_destination_command_write(&buffer[20], FDOutProxy); /* Destination Command (8 bytes) */
|
|
|
|
/* FlowControlAck Command (28 bytes) */
|
|
rts_flow_control_ack_command_write(&buffer[28], BytesReceived, AvailableWindow, ChannelCookie);
|
|
|
|
length = header.frag_length;
|
|
|
|
if (rpc_in_write(rpc, buffer, length) < 0)
|
|
return -1;
|
|
free(buffer);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int rts_recv_flow_control_ack_pdu(rdpRpc* rpc, BYTE* buffer, UINT32 length)
|
|
{
|
|
UINT32 offset;
|
|
UINT32 BytesReceived;
|
|
UINT32 AvailableWindow;
|
|
BYTE ChannelCookie[16];
|
|
|
|
offset = 24;
|
|
offset += rts_flow_control_ack_command_read(rpc, &buffer[offset], length - offset,
|
|
&BytesReceived, &AvailableWindow, (BYTE*) &ChannelCookie) + 4;
|
|
|
|
#if 0
|
|
fprintf(stderr, "BytesReceived: %d AvailableWindow: %d\n",
|
|
BytesReceived, AvailableWindow);
|
|
fprintf(stderr, "ChannelCookie: " RPC_UUID_FORMAT_STRING "\n", RPC_UUID_FORMAT_ARGUMENTS(ChannelCookie));
|
|
#endif
|
|
|
|
rpc->VirtualConnection->DefaultInChannel->SenderAvailableWindow =
|
|
AvailableWindow - (rpc->VirtualConnection->DefaultInChannel->BytesSent - BytesReceived);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int rts_recv_flow_control_ack_with_destination_pdu(rdpRpc* rpc, BYTE* buffer, UINT32 length)
|
|
{
|
|
UINT32 offset;
|
|
UINT32 Destination;
|
|
UINT32 BytesReceived;
|
|
UINT32 AvailableWindow;
|
|
BYTE ChannelCookie[16];
|
|
|
|
/**
|
|
* When the sender receives a FlowControlAck RTS PDU, it MUST use the following formula to
|
|
* recalculate its Sender AvailableWindow variable:
|
|
*
|
|
* Sender AvailableWindow = Receiver AvailableWindow_from_ack - (BytesSent - BytesReceived_from_ack)
|
|
*
|
|
* Where:
|
|
*
|
|
* Receiver AvailableWindow_from_ack is the Available Window field in the Flow Control
|
|
* Acknowledgement Structure (section 2.2.3.4) in the PDU received.
|
|
*
|
|
* BytesReceived_from_ack is the Bytes Received field in the Flow Control Acknowledgement structure
|
|
* in the PDU received.
|
|
*
|
|
*/
|
|
|
|
offset = 24;
|
|
offset += rts_destination_command_read(rpc, &buffer[offset], length - offset, &Destination) + 4;
|
|
offset += rts_flow_control_ack_command_read(rpc, &buffer[offset], length - offset,
|
|
&BytesReceived, &AvailableWindow, (BYTE*) &ChannelCookie) + 4;
|
|
|
|
#if 0
|
|
fprintf(stderr, "Destination: %d BytesReceived: %d AvailableWindow: %d\n",
|
|
Destination, BytesReceived, AvailableWindow);
|
|
fprintf(stderr, "ChannelCookie: " RPC_UUID_FORMAT_STRING "\n", RPC_UUID_FORMAT_ARGUMENTS(ChannelCookie));
|
|
#endif
|
|
|
|
rpc->VirtualConnection->DefaultInChannel->SenderAvailableWindow =
|
|
AvailableWindow - (rpc->VirtualConnection->DefaultInChannel->BytesSent - BytesReceived);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int rts_send_ping_pdu(rdpRpc* rpc)
|
|
{
|
|
BYTE* buffer;
|
|
UINT32 length;
|
|
rpcconn_rts_hdr_t header;
|
|
|
|
rts_pdu_header_init(&header);
|
|
header.frag_length = 20;
|
|
header.Flags = RTS_FLAG_PING;
|
|
header.NumberOfCommands = 0;
|
|
|
|
DEBUG_RPC("Sending Ping RTS PDU");
|
|
|
|
buffer = (BYTE*) malloc(header.frag_length);
|
|
if (!buffer)
|
|
return -1;
|
|
|
|
CopyMemory(buffer, ((BYTE*) &header), 20); /* RTS Header (20 bytes) */
|
|
|
|
length = header.frag_length;
|
|
|
|
if (rpc_in_write(rpc, buffer, length) < 0)
|
|
return -1;
|
|
free(buffer);
|
|
|
|
return length;
|
|
}
|
|
|
|
int rts_command_length(rdpRpc* rpc, UINT32 CommandType, BYTE* buffer, UINT32 length)
|
|
{
|
|
int CommandLength = 0;
|
|
|
|
switch (CommandType)
|
|
{
|
|
case RTS_CMD_RECEIVE_WINDOW_SIZE:
|
|
CommandLength = RTS_CMD_RECEIVE_WINDOW_SIZE_LENGTH;
|
|
break;
|
|
|
|
case RTS_CMD_FLOW_CONTROL_ACK:
|
|
CommandLength = RTS_CMD_FLOW_CONTROL_ACK_LENGTH;
|
|
break;
|
|
|
|
case RTS_CMD_CONNECTION_TIMEOUT:
|
|
CommandLength = RTS_CMD_CONNECTION_TIMEOUT_LENGTH;
|
|
break;
|
|
|
|
case RTS_CMD_COOKIE:
|
|
CommandLength = RTS_CMD_COOKIE_LENGTH;
|
|
break;
|
|
|
|
case RTS_CMD_CHANNEL_LIFETIME:
|
|
CommandLength = RTS_CMD_CHANNEL_LIFETIME_LENGTH;
|
|
break;
|
|
|
|
case RTS_CMD_CLIENT_KEEPALIVE:
|
|
CommandLength = RTS_CMD_CLIENT_KEEPALIVE_LENGTH;
|
|
break;
|
|
|
|
case RTS_CMD_VERSION:
|
|
CommandLength = RTS_CMD_VERSION_LENGTH;
|
|
break;
|
|
|
|
case RTS_CMD_EMPTY:
|
|
CommandLength = RTS_CMD_EMPTY_LENGTH;
|
|
break;
|
|
|
|
case RTS_CMD_PADDING: /* variable-size */
|
|
CommandLength = rts_padding_command_read(rpc, buffer, length);
|
|
break;
|
|
|
|
case RTS_CMD_NEGATIVE_ANCE:
|
|
CommandLength = RTS_CMD_NEGATIVE_ANCE_LENGTH;
|
|
break;
|
|
|
|
case RTS_CMD_ANCE:
|
|
CommandLength = RTS_CMD_ANCE_LENGTH;
|
|
break;
|
|
|
|
case RTS_CMD_CLIENT_ADDRESS: /* variable-size */
|
|
CommandLength = rts_client_address_command_read(rpc, buffer, length);
|
|
break;
|
|
|
|
case RTS_CMD_ASSOCIATION_GROUP_ID:
|
|
CommandLength = RTS_CMD_ASSOCIATION_GROUP_ID_LENGTH;
|
|
break;
|
|
|
|
case RTS_CMD_DESTINATION:
|
|
CommandLength = RTS_CMD_DESTINATION_LENGTH;
|
|
break;
|
|
|
|
case RTS_CMD_PING_TRAFFIC_SENT_NOTIFY:
|
|
CommandLength = RTS_CMD_PING_TRAFFIC_SENT_NOTIFY_LENGTH;
|
|
break;
|
|
|
|
default:
|
|
fprintf(stderr, "Error: Unknown RTS Command Type: 0x%x\n", CommandType);
|
|
return -1;
|
|
break;
|
|
}
|
|
|
|
return CommandLength;
|
|
}
|
|
|
|
int rts_recv_out_of_sequence_pdu(rdpRpc* rpc, BYTE* buffer, UINT32 length)
|
|
{
|
|
UINT32 SignatureId;
|
|
rpcconn_rts_hdr_t* rts;
|
|
RtsPduSignature signature;
|
|
|
|
rts = (rpcconn_rts_hdr_t*) buffer;
|
|
|
|
rts_extract_pdu_signature(rpc, &signature, rts);
|
|
SignatureId = rts_identify_pdu_signature(rpc, &signature, NULL);
|
|
|
|
switch (SignatureId)
|
|
{
|
|
case RTS_PDU_FLOW_CONTROL_ACK:
|
|
return rts_recv_flow_control_ack_pdu(rpc, buffer, length);
|
|
case RTS_PDU_FLOW_CONTROL_ACK_WITH_DESTINATION:
|
|
return rts_recv_flow_control_ack_with_destination_pdu(rpc, buffer, length);
|
|
case RTS_PDU_PING:
|
|
return rts_send_ping_pdu(rpc);
|
|
default:
|
|
fprintf(stderr, "%s: unimplemented signature id: 0x%08X\n", __FUNCTION__, SignatureId);
|
|
rts_print_pdu_signature(rpc, &signature);
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|