/** * FreeRDP: A Remote Desktop Protocol Implementation * Request To Send (RTS) PDUs * * Copyright 2012 Marc-Andre Moreau * * 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 "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 * */ BOOL rts_connect(rdpRpc* rpc) { int status; RTS_PDU rts_pdu; HttpResponse* http_response; if (!rpc_ntlm_http_out_connect(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_ntlm_http_in_connect(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); http_response_free(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[] = { "ReceiveWindowSize", "FlowControlAck", "ConnectionTimeout", "Cookie", "ChannelLifetime", "ClientKeepalive", "Version", "Empty", "Padding", "NegativeANCE", "ANCE", "ClientAddress", "AssociationGroupId", "Destination", "PingTrafficSentNotify" }; #endif void rts_pdu_header_read(STREAM* s, RTS_PDU_HEADER* header) { stream_read_BYTE(s, header->rpc_vers); /* rpc_vers (1 byte) */ stream_read_BYTE(s, header->rpc_vers_minor); /* rpc_vers_minor (1 byte) */ stream_read_BYTE(s, header->ptype); /* PTYPE (1 byte) */ stream_read_BYTE(s, header->pfc_flags); /* pfc_flags (1 byte) */ stream_read_BYTE(s, header->packed_drep[0]); /* packet_drep[0] (1 byte) */ stream_read_BYTE(s, header->packed_drep[1]); /* packet_drep[1] (1 byte) */ stream_read_BYTE(s, header->packed_drep[2]); /* packet_drep[2] (1 byte) */ stream_read_BYTE(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) */ stream_read_UINT16(s, header->flags); /* flags (2 bytes) */ stream_read_UINT16(s, header->numberOfCommands); /* numberOfCommands (2 bytes) */ } void rts_pdu_header_write(STREAM* s, RTS_PDU_HEADER* header) { stream_write_BYTE(s, header->rpc_vers); /* rpc_vers (1 byte) */ stream_write_BYTE(s, header->rpc_vers_minor); /* rpc_vers_minor (1 byte) */ stream_write_BYTE(s, header->ptype); /* PTYPE (1 byte) */ stream_write_BYTE(s, header->pfc_flags); /* pfc_flags (1 byte) */ stream_write_BYTE(s, header->packed_drep[0]); /* packet_drep[0] (1 byte) */ stream_write_BYTE(s, header->packed_drep[1]); /* packet_drep[1] (1 byte) */ stream_write_BYTE(s, header->packed_drep[2]); /* packet_drep[2] (1 byte) */ stream_write_BYTE(s, header->packed_drep[3]); /* packet_drep[3] (1 byte) */ stream_write_UINT16(s, header->frag_length); /* frag_length (2 bytes) */ stream_write_UINT16(s, header->auth_length); /* auth_length (2 bytes) */ stream_write_UINT32(s, header->call_id); /* call_id (4 bytes) */ stream_write_UINT16(s, header->flags); /* flags (2 bytes) */ stream_write_UINT16(s, header->numberOfCommands); /* numberOfCommands (2 bytes) */ } void rts_receive_window_size_command_read(rdpRpc* rpc, STREAM* s) { stream_seek_UINT32(s); /* ReceiveWindowSize (4 bytes) */ } void rts_receive_window_size_command_write(STREAM* s, UINT32 ReceiveWindowSize) { stream_write_UINT32(s, RTS_CMD_RECEIVE_WINDOW_SIZE); /* CommandType (4 bytes) */ stream_write_UINT32(s, ReceiveWindowSize); /* ReceiveWindowSize (4 bytes) */ } void rts_flow_control_ack_command_read(rdpRpc* rpc, STREAM* s) { /* Ack (24 bytes) */ stream_seek_UINT32(s); /* BytesReceived (4 bytes) */ stream_seek_UINT32(s); /* AvailableWindow (4 bytes) */ stream_seek(s, 16); /* ChannelCookie (16 bytes) */ } void rts_flow_control_ack_command_write(STREAM* s, UINT32 BytesReceived, UINT32 AvailableWindow, BYTE* ChannelCookie) { stream_write_UINT32(s, RTS_CMD_FLOW_CONTROL_ACK); /* CommandType (4 bytes) */ /* Ack (24 bytes) */ stream_write_UINT32(s, BytesReceived); /* BytesReceived (4 bytes) */ stream_write_UINT32(s, AvailableWindow); /* AvailableWindow (4 bytes) */ stream_write(s, ChannelCookie, 16); /* ChannelCookie (16 bytes) */ } void rts_connection_timeout_command_read(rdpRpc* rpc, STREAM* s) { stream_seek_UINT32(s); /* ConnectionTimeout (4 bytes) */ } void rts_connection_timeout_command_write(STREAM* s, UINT32 ConnectionTimeout) { stream_write_UINT32(s, RTS_CMD_CONNECTION_TIMEOUT); /* CommandType (4 bytes) */ stream_write_UINT32(s, ConnectionTimeout); /* ConnectionTimeout (4 bytes) */ } void rts_cookie_command_read(rdpRpc* rpc, STREAM* s) { stream_seek(s, 16); /* Cookie (16 bytes) */ } void rts_cookie_command_write(STREAM* s, BYTE* Cookie) { stream_write_UINT32(s, RTS_CMD_COOKIE); /* CommandType (4 bytes) */ stream_write(s, Cookie, 16); /* Cookie (16 bytes) */ } void rts_channel_lifetime_command_read(rdpRpc* rpc, STREAM* s) { stream_seek_UINT32(s); /* ChannelLifetime (4 bytes) */ } void rts_channel_lifetime_command_write(STREAM* s, UINT32 ChannelLifetime) { stream_write_UINT32(s, RTS_CMD_CHANNEL_LIFETIME); /* CommandType (4 bytes) */ stream_write_UINT32(s, ChannelLifetime); /* ChannelLifetime (4 bytes) */ } void rts_client_keepalive_command_read(rdpRpc* rpc, STREAM* s) { stream_seek_UINT32(s); /* ClientKeepalive (4 bytes) */ } void rts_client_keepalive_command_write(STREAM* s, UINT32 ClientKeepalive) { stream_write_UINT32(s, RTS_CMD_CLIENT_KEEPALIVE); /* CommandType (4 bytes) */ stream_write_UINT32(s, ClientKeepalive); /* ClientKeepalive (4 bytes) */ } void rts_version_command_read(rdpRpc* rpc, STREAM* s) { stream_seek_UINT32(s); /* Version (4 bytes) */ } void rts_version_command_write(STREAM* s) { stream_write_UINT32(s, RTS_CMD_VERSION); /* CommandType (4 bytes) */ stream_write_UINT32(s, 1); /* Version (4 bytes) */ } void rts_empty_command_read(rdpRpc* rpc, STREAM* s) { } void rts_empty_command_write(STREAM* s) { stream_write_UINT32(s, RTS_CMD_EMPTY); /* CommandType (4 bytes) */ } void rts_padding_command_read(rdpRpc* rpc, STREAM* s) { UINT32 ConformanceCount; stream_read_UINT32(s, ConformanceCount); /* ConformanceCount (4 bytes) */ stream_seek(s, ConformanceCount); /* Padding (variable) */ } void rts_padding_command_write(STREAM* s, UINT32 ConformanceCount) { stream_write_UINT32(s, ConformanceCount); /* ConformanceCount (4 bytes) */ stream_write_zero(s, ConformanceCount); /* Padding (variable) */ } void rts_negative_ance_command_read(rdpRpc* rpc, STREAM* s) { } void rts_negative_ance_command_write(STREAM* s) { stream_write_UINT32(s, RTS_CMD_NEGATIVE_ANCE); /* CommandType (4 bytes) */ } void rts_ance_command_read(rdpRpc* rpc, STREAM* s) { } void rts_ance_command_write(STREAM* s) { stream_write_UINT32(s, RTS_CMD_ANCE); /* CommandType (4 bytes) */ } void rts_client_address_command_read(rdpRpc* rpc, STREAM* s) { UINT32 AddressType; stream_read_UINT32(s, AddressType); /* AddressType (4 bytes) */ if (AddressType == 0) { stream_seek(s, 4); /* ClientAddress (4 bytes) */ } else { stream_seek(s, 16); /* ClientAddress (16 bytes) */ } stream_seek(s, 12); /* padding (12 bytes) */ } void rts_client_address_command_write(STREAM* s, UINT32 AddressType, BYTE* ClientAddress) { stream_write_UINT32(s, RTS_CMD_CLIENT_ADDRESS); /* CommandType (4 bytes) */ stream_write_UINT32(s, AddressType); /* AddressType (4 bytes) */ if (AddressType == 0) { stream_write(s, ClientAddress, 4); /* ClientAddress (4 bytes) */ } else { stream_write(s, ClientAddress, 16); /* ClientAddress (16 bytes) */ } stream_write_zero(s, 12); /* padding (12 bytes) */ } void rts_association_group_id_command_read(rdpRpc* rpc, STREAM* s) { stream_seek(s, 16); /* AssociationGroupId (16 bytes) */ } void rts_association_group_id_command_write(STREAM* s, BYTE* associationGroupId) { stream_write_UINT32(s, RTS_CMD_ASSOCIATION_GROUP_ID); /* CommandType (4 bytes) */ stream_write(s, associationGroupId, 16); /* AssociationGroupId (16 bytes) */ } void rts_destination_command_read(rdpRpc* rpc, STREAM* s) { stream_seek_UINT32(s); /* Destination (4 bytes) */ } void rts_destination_command_write(STREAM* s, UINT32 Destination) { stream_write_UINT32(s, RTS_CMD_DESTINATION); /* CommandType (4 bytes) */ stream_write_UINT32(s, Destination); /* Destination (4 bytes) */ } void rts_ping_traffic_sent_notify_command_read(rdpRpc* rpc, STREAM* s) { stream_seek_UINT32(s); /* PingTrafficSent (4 bytes) */ } void rts_ping_traffic_sent_notify_command_write(STREAM* s, UINT32 PingTrafficSent) { stream_write_UINT32(s, RTS_CMD_PING_TRAFFIC_SENT_NOTIFY); /* CommandType (4 bytes) */ stream_write_UINT32(s, PingTrafficSent); /* PingTrafficSent (4 bytes) */ } void rpc_generate_cookie(BYTE* cookie) { RAND_pseudo_bytes(cookie, 16); } BOOL rts_send_CONN_A1_pdu(rdpRpc* rpc) { STREAM* s; RTS_PDU_HEADER header; UINT32 ReceiveWindowSize; BYTE* OUTChannelCookie; BYTE* 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((BYTE*) &(rpc->VirtualConnection->Cookie)); rpc_generate_cookie((BYTE*) &(rpc->VirtualConnection->DefaultOutChannelCookie)); VirtualConnectionCookie = (BYTE*) &(rpc->VirtualConnection->Cookie); OUTChannelCookie = (BYTE*) &(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; } BOOL rts_send_CONN_B1_pdu(rdpRpc* rpc) { STREAM* s; RTS_PDU_HEADER header; BYTE* INChannelCookie; BYTE* AssociationGroupId; BYTE* 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((BYTE*) &(rpc->VirtualConnection->DefaultInChannelCookie)); rpc_generate_cookie((BYTE*) &(rpc->VirtualConnection->AssociationGroupId)); VirtualConnectionCookie = (BYTE*) &(rpc->VirtualConnection->Cookie); INChannelCookie = (BYTE*) &(rpc->VirtualConnection->DefaultInChannelCookie); AssociationGroupId = (BYTE*) &(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; } BOOL rts_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; } BOOL rts_send_flow_control_ack_pdu(rdpRpc* rpc) { STREAM* s; RTS_PDU_HEADER header; UINT32 BytesReceived; UINT32 AvailableWindow; BYTE* 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->BytesReceived; AvailableWindow = rpc->VirtualConnection->DefaultOutChannel->ReceiverAvailableWindow; ChannelCookie = (BYTE*) &(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; } BOOL 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; DEBUG_RTS("numberOfCommands:%d", rts_pdu->header.numberOfCommands); if (rts_pdu->header.flags & RTS_FLAG_PING) { rts_send_keep_alive_pdu(rpc); return 0; } s = stream_new(0); stream_attach(s, rts_pdu->content, rts_pdu->header.frag_length); for (i = 0; i < rts_pdu->header.numberOfCommands; i++) { stream_read_UINT32(s, CommandType); /* CommandType (4 bytes) */ DEBUG_RTS("CommandType: %s (0x%08X)", RTS_CMD_STRINGS[CommandType % 14], CommandType); switch (CommandType) { case RTS_CMD_RECEIVE_WINDOW_SIZE: rts_receive_window_size_command_read(rpc, s); break; case RTS_CMD_FLOW_CONTROL_ACK: rts_flow_control_ack_command_read(rpc, s); break; case RTS_CMD_CONNECTION_TIMEOUT: rts_connection_timeout_command_read(rpc, s); break; case RTS_CMD_COOKIE: rts_cookie_command_read(rpc, s); break; case RTS_CMD_CHANNEL_LIFETIME: rts_channel_lifetime_command_read(rpc, s); break; case RTS_CMD_CLIENT_KEEPALIVE: rts_client_keepalive_command_read(rpc, s); break; case RTS_CMD_VERSION: rts_version_command_read(rpc, s); break; case RTS_CMD_EMPTY: rts_empty_command_read(rpc, s); break; case RTS_CMD_PADDING: rts_padding_command_read(rpc, s); break; case RTS_CMD_NEGATIVE_ANCE: rts_negative_ance_command_read(rpc, s); break; case RTS_CMD_ANCE: rts_ance_command_read(rpc, s); break; case RTS_CMD_CLIENT_ADDRESS: rts_client_address_command_read(rpc, s); break; case RTS_CMD_ASSOCIATION_GROUP_ID: rts_association_group_id_command_read(rpc, s); break; case RTS_CMD_DESTINATION: rts_destination_command_read(rpc, s); break; case RTS_CMD_PING_TRAFFIC_SENT_NOTIFY: rts_ping_traffic_sent_notify_command_read(rpc, s); break; default: printf("Error: Unknown RTS Command Type: 0x%x\n", CommandType); stream_detach(s) ; stream_free(s) ; return -1; break; } } stream_detach(s); stream_free(s); return 0; } int rts_recv_pdu(rdpRpc* rpc, RTS_PDU* rts_pdu) { STREAM* s; int status; int length; BYTE header_buffer[20]; rdpTls* tls_out = rpc->tls_out; /* read first 20 bytes to get RTS PDU Header */ status = tls_read(tls_out, (BYTE*) &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 = (BYTE*) malloc(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; }