int rpc_recv_pdu_header(rdpRpc* rpc, BYTE* header) { int status; int bytesRead; UINT32 offset; /* Read common header fields */ bytesRead = 0; while (bytesRead < RPC_COMMON_FIELDS_LENGTH) { status = rpc_out_read(rpc, &header[bytesRead], RPC_COMMON_FIELDS_LENGTH - bytesRead); if (status < 0) { printf("rpc_recv_pdu_header: error reading header\n"); return status; } bytesRead += status; } rpc_get_stub_data_info(rpc, header, &offset, NULL); while (bytesRead < offset) { status = rpc_out_read(rpc, &header[bytesRead], offset - bytesRead); if (status < 0) return status; bytesRead += status; } return bytesRead; }
int rpc_client_on_fragment_received_event(rdpRpc* rpc) { BYTE* buffer; UINT32 StubOffset; UINT32 StubLength; wStream* fragment; rpcconn_hdr_t* header; freerdp* instance; instance = (freerdp*) rpc->transport->settings->instance; if (!rpc->client->pdu) rpc->client->pdu = rpc_client_receive_pool_take(rpc); fragment = Queue_Dequeue(rpc->client->FragmentQueue); buffer = (BYTE*) Stream_Buffer(fragment); header = (rpcconn_hdr_t*) Stream_Buffer(fragment); if (rpc->State < RPC_CLIENT_STATE_CONTEXT_NEGOTIATED) { rpc->client->pdu->Flags = 0; rpc->client->pdu->CallId = header->common.call_id; Stream_EnsureCapacity(rpc->client->pdu->s, Stream_Length(fragment)); Stream_Write(rpc->client->pdu->s, buffer, Stream_Length(fragment)); Stream_Length(rpc->client->pdu->s) = Stream_GetPosition(rpc->client->pdu->s); rpc_client_fragment_pool_return(rpc, fragment); Queue_Enqueue(rpc->client->ReceiveQueue, rpc->client->pdu); SetEvent(rpc->transport->ReceiveEvent); rpc->client->pdu = NULL; return 0; } if (header->common.ptype == PTYPE_RTS) { if (rpc->VirtualConnection->State >= VIRTUAL_CONNECTION_STATE_OPENED) { //fprintf(stderr, "Receiving Out-of-Sequence RTS PDU\n"); rts_recv_out_of_sequence_pdu(rpc, buffer, header->common.frag_length); rpc_client_fragment_pool_return(rpc, fragment); } else { fprintf(stderr, "warning: unhandled RTS PDU\n"); } return 0; } else if (header->common.ptype == PTYPE_FAULT) { rpc_recv_fault_pdu(header); return -1; } if (header->common.ptype != PTYPE_RESPONSE) { fprintf(stderr, "Unexpected RPC PDU type: %d\n", header->common.ptype); return -1; } rpc->VirtualConnection->DefaultOutChannel->BytesReceived += header->common.frag_length; rpc->VirtualConnection->DefaultOutChannel->ReceiverAvailableWindow -= header->common.frag_length; if (!rpc_get_stub_data_info(rpc, buffer, &StubOffset, &StubLength)) { fprintf(stderr, "rpc_recv_pdu_fragment: expected stub\n"); return -1; } if (StubLength == 4) { //fprintf(stderr, "Ignoring TsProxySendToServer Response\n"); printf("Got stub length 4 with flags %d and callid %d\n", header->common.pfc_flags, header->common.call_id); /* received a disconnect request from the server? */ if ((header->common.call_id == rpc->PipeCallId) && (header->common.pfc_flags & PFC_LAST_FRAG)) { TerminateEventArgs e; instance->context->rdp->disconnect = TRUE; rpc->transport->tsg->state = TSG_STATE_TUNNEL_CLOSE_PENDING; EventArgsInit(&e, "freerdp"); e.code = 0; PubSub_OnTerminate(instance->context->pubSub, instance->context, &e); } rpc_client_fragment_pool_return(rpc, fragment); return 0; } Stream_EnsureCapacity(rpc->client->pdu->s, header->response.alloc_hint); buffer = (BYTE*) Stream_Buffer(fragment); header = (rpcconn_hdr_t*) Stream_Buffer(fragment); if (rpc->StubFragCount == 0) rpc->StubCallId = header->common.call_id; if (rpc->StubCallId != header->common.call_id) { fprintf(stderr, "invalid call_id: actual: %d, expected: %d, frag_count: %d\n", rpc->StubCallId, header->common.call_id, rpc->StubFragCount); } Stream_Write(rpc->client->pdu->s, &buffer[StubOffset], StubLength); rpc->StubFragCount++; rpc_client_fragment_pool_return(rpc, fragment); if (rpc->VirtualConnection->DefaultOutChannel->ReceiverAvailableWindow < (rpc->ReceiveWindow / 2)) { //fprintf(stderr, "Sending Flow Control Ack PDU\n"); rts_send_flow_control_ack_pdu(rpc); } /** * If alloc_hint is set to a nonzero value and a request or a response is fragmented into multiple * PDUs, implementations of these extensions SHOULD set the alloc_hint field in every PDU to be the * combined stub data length of all remaining fragment PDUs. */ if (header->response.alloc_hint == StubLength) { rpc->client->pdu->Flags = RPC_PDU_FLAG_STUB; rpc->client->pdu->CallId = rpc->StubCallId; Stream_Length(rpc->client->pdu->s) = Stream_GetPosition(rpc->client->pdu->s); rpc->StubFragCount = 0; rpc->StubCallId = 0; Queue_Enqueue(rpc->client->ReceiveQueue, rpc->client->pdu); rpc->client->pdu = NULL; return 0; } return 0; }
RPC_PDU* rpc_recv_pdu(rdpRpc* rpc) { int status; UINT32 StubOffset; UINT32 StubLength; rpcconn_hdr_t* header; status = rpc_recv_pdu_fragment(rpc); if (rpc->State < RPC_CLIENT_STATE_CONTEXT_NEGOTIATED) { rpc->pdu->Flags = 0; rpc->pdu->Buffer = rpc->FragBuffer; rpc->pdu->Size = rpc->FragBufferSize; rpc->pdu->Length = status; return rpc->pdu; } header = (rpcconn_hdr_t*) rpc->FragBuffer; if (header->common.ptype != PTYPE_RESPONSE) { printf("rpc_recv_pdu: unexpected ptype 0x%02X\n", header->common.ptype); return NULL; } if (!rpc_get_stub_data_info(rpc, rpc->FragBuffer, &StubOffset, &StubLength)) { printf("rpc_recv_pdu: expected stub\n"); return NULL; } if (header->response.alloc_hint > rpc->StubBufferSize) { rpc->StubBufferSize = header->response.alloc_hint; rpc->StubBuffer = (BYTE*) realloc(rpc->StubBuffer, rpc->StubBufferSize); } CopyMemory(&rpc->StubBuffer[rpc->StubOffset], &rpc->FragBuffer[StubOffset], StubLength); rpc->StubOffset += StubLength; rpc->StubFragCount++; /** * If alloc_hint is set to a nonzero value and a request or a response is fragmented into multiple * PDUs, implementations of these extensions SHOULD set the alloc_hint field in every PDU to be the * combined stub data length of all remaining fragment PDUs. */ //printf("Receiving Fragment #%d FragStubLength: %d FragLength: %d AllocHint: %d StubOffset: %d\n", // rpc->StubFragCount, StubLength, header->common.frag_length, header->response.alloc_hint, rpc->StubOffset); if ((header->response.alloc_hint == StubLength)) { //printf("Reassembled PDU (%d):\n", rpc->StubOffset); //freerdp_hexdump(rpc->StubBuffer, rpc->StubOffset); rpc->StubLength = rpc->StubOffset; rpc->StubOffset = 0; rpc->StubFragCount = 0; rpc->pdu->Flags = RPC_PDU_FLAG_STUB; rpc->pdu->Buffer = rpc->StubBuffer; rpc->pdu->Size = rpc->StubBufferSize; rpc->pdu->Length = rpc->StubLength; return rpc->pdu; } return rpc_recv_pdu(rpc); }
int rpc_client_on_fragment_received_event(rdpRpc* rpc) { BYTE* buffer; UINT32 StubOffset; UINT32 StubLength; wStream* fragment; rpcconn_hdr_t* header; if (!rpc->client->pdu) rpc->client->pdu = rpc_client_receive_pool_take(rpc); fragment = Queue_Dequeue(rpc->client->FragmentQueue); buffer = (BYTE*) Stream_Buffer(fragment); header = (rpcconn_hdr_t*) Stream_Buffer(fragment); if (rpc->State < RPC_CLIENT_STATE_CONTEXT_NEGOTIATED) { rpc->client->pdu->Flags = 0; rpc->client->pdu->CallId = header->common.call_id; Stream_EnsureCapacity(rpc->client->pdu->s, Stream_Length(fragment)); Stream_Write(rpc->client->pdu->s, buffer, Stream_Length(fragment)); Stream_Length(rpc->client->pdu->s) = Stream_Position(rpc->client->pdu->s); rpc_client_fragment_pool_return(rpc, fragment); Queue_Enqueue(rpc->client->ReceiveQueue, rpc->client->pdu); SetEvent(rpc->transport->ReceiveEvent); rpc->client->pdu = NULL; return 0; } if (header->common.ptype == PTYPE_RTS) { if (rpc->VirtualConnection->State >= VIRTUAL_CONNECTION_STATE_OPENED) { //printf("Receiving Out-of-Sequence RTS PDU\n"); rts_recv_out_of_sequence_pdu(rpc, buffer, header->common.frag_length); rpc_client_fragment_pool_return(rpc, fragment); } else { printf("warning: unhandled RTS PDU\n"); } return 0; } else if (header->common.ptype == PTYPE_FAULT) { rpc_recv_fault_pdu(header); return -1; } if (header->common.ptype != PTYPE_RESPONSE) { printf("Unexpected RPC PDU type: %d\n", header->common.ptype); return -1; } rpc->VirtualConnection->DefaultOutChannel->BytesReceived += header->common.frag_length; rpc->VirtualConnection->DefaultOutChannel->ReceiverAvailableWindow -= header->common.frag_length; if (!rpc_get_stub_data_info(rpc, buffer, &StubOffset, &StubLength)) { printf("rpc_recv_pdu_fragment: expected stub\n"); return -1; } if (StubLength == 4) { //printf("Ignoring TsProxySendToServer Response\n"); rpc_client_fragment_pool_return(rpc, fragment); return 0; } Stream_EnsureCapacity(rpc->client->pdu->s, header->response.alloc_hint); buffer = (BYTE*) Stream_Buffer(fragment); header = (rpcconn_hdr_t*) Stream_Buffer(fragment); if (rpc->StubFragCount == 0) rpc->StubCallId = header->common.call_id; if (rpc->StubCallId != header->common.call_id) { printf("invalid call_id: actual: %d, expected: %d, frag_count: %d\n", rpc->StubCallId, header->common.call_id, rpc->StubFragCount); } Stream_Write(rpc->client->pdu->s, &buffer[StubOffset], StubLength); rpc->StubFragCount++; rpc_client_fragment_pool_return(rpc, fragment); if (rpc->VirtualConnection->DefaultOutChannel->ReceiverAvailableWindow < (rpc->ReceiveWindow / 2)) { //printf("Sending Flow Control Ack PDU\n"); rts_send_flow_control_ack_pdu(rpc); } /** * If alloc_hint is set to a nonzero value and a request or a response is fragmented into multiple * PDUs, implementations of these extensions SHOULD set the alloc_hint field in every PDU to be the * combined stub data length of all remaining fragment PDUs. */ if (header->response.alloc_hint == StubLength) { rpc->client->pdu->Flags = RPC_PDU_FLAG_STUB; rpc->client->pdu->CallId = rpc->StubCallId; Stream_Length(rpc->client->pdu->s) = Stream_Position(rpc->client->pdu->s); rpc->StubFragCount = 0; rpc->StubCallId = 0; Queue_Enqueue(rpc->client->ReceiveQueue, rpc->client->pdu); rpc->client->pdu = NULL; return 0; } return 0; }