From 152d525a058cf6089931626f2314442e9f9615eb Mon Sep 17 00:00:00 2001 From: Vic Lee Date: Tue, 28 Oct 2014 23:29:40 +0800 Subject: [PATCH] libfreerdp-core: server-side auto-detect feature. --- include/freerdp/autodetect.h | 60 +++++++++++++ include/freerdp/freerdp.h | 9 +- include/freerdp/peer.h | 2 + libfreerdp/core/autodetect.c | 209 ++++++++++++++++++++++++++++++++++++++++++- libfreerdp/core/autodetect.h | 17 +--- libfreerdp/core/freerdp.c | 4 + libfreerdp/core/peer.c | 4 + 7 files changed, 288 insertions(+), 17 deletions(-) create mode 100644 include/freerdp/autodetect.h diff --git a/include/freerdp/autodetect.h b/include/freerdp/autodetect.h new file mode 100644 index 0000000..cea7cc8 --- /dev/null +++ b/include/freerdp/autodetect.h @@ -0,0 +1,60 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Auto-Detect PDUs + * + * Copyright 2014 Dell Software + * Copyright 2014 Vic Lee + * + * 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. + */ + +#ifndef FREERDP_AUTODETECT_H +#define FREERDP_AUTODETECT_H + +typedef struct rdp_autodetect rdpAutoDetect; + +typedef BOOL (*pRTTMeasureRequest)(rdpContext* context, UINT16 sequenceNumber); +typedef BOOL (*pRTTMeasureResponse)(rdpContext* context, UINT16 sequenceNumber); +typedef BOOL (*pBandwidthMeasureStart)(rdpContext* context, UINT16 sequenceNumber); +typedef BOOL (*pBandwidthMeasurePayload)(rdpContext* context, UINT16 payloadLength, UINT16 sequenceNumber); +typedef BOOL (*pBandwidthMeasureStop)(rdpContext* context, UINT16 payloadLength, UINT16 sequenceNumber); +typedef BOOL (*pBandwidthMeasureResults)(rdpContext* context, UINT16 sequenceNumber); +typedef BOOL (*pNetworkCharacteristicsResult)(rdpContext* context, UINT16 sequenceNumber); + +struct rdp_autodetect +{ + ALIGN64 rdpContext* context; /* 0 */ + /* RTT measurement */ + ALIGN64 UINT32 rttMeasureStartTime; /* 1 */ + /* Bandwidth measurement */ + ALIGN64 UINT32 bandwidthMeasureStartTime; /* 2 */ + ALIGN64 UINT32 bandwidthMeasureTimeDelta; /* 3 */ + ALIGN64 UINT32 bandwidthMeasureByteCount; /* 4 */ + /* Network characteristics (as reported by server) */ + ALIGN64 UINT32 netCharBandwidth; /* 5 */ + ALIGN64 UINT32 netCharBaseRTT; /* 6 */ + ALIGN64 UINT32 netCharAverageRTT; /* 7 */ + UINT64 paddingA[16 - 8]; /* 8 */ + + ALIGN64 pRTTMeasureRequest RTTMeasureRequest; /* 16 */ + ALIGN64 pRTTMeasureResponse RTTMeasureResponse; /* 17 */ + ALIGN64 pBandwidthMeasureStart BandwidthMeasureStart; /* 18 */ + ALIGN64 pBandwidthMeasurePayload BandwidthMeasurePayload; /* 19 */ + ALIGN64 pBandwidthMeasureStop BandwidthMeasureStop; /* 20 */ + ALIGN64 pBandwidthMeasureResults BandwidthMeasureResults; /* 21 */ + ALIGN64 pNetworkCharacteristicsResult NetworkCharacteristicsResult; /* 22 */ + UINT64 paddingB[32 - 23]; /* 23 */ +}; + + +#endif /* FREERDP_AUTODETECT_H */ diff --git a/include/freerdp/freerdp.h b/include/freerdp/freerdp.h index 7023e9d..fef980b 100644 --- a/include/freerdp/freerdp.h +++ b/include/freerdp/freerdp.h @@ -51,6 +51,7 @@ typedef RDP_CLIENT_ENTRY_POINTS_V1 RDP_CLIENT_ENTRY_POINTS; #include #include #include +#include #ifdef __cplusplus extern "C" { @@ -123,7 +124,8 @@ struct rdp_context ALIGN64 rdpSettings* settings; /* 40 */ ALIGN64 rdpMetrics* metrics; /* 41 */ ALIGN64 rdpCodecs* codecs; /* 42 */ - UINT64 paddingC[64 - 43]; /* 43 */ + ALIGN64 rdpAutoDetect* autodetect; /* 43 */ + UINT64 paddingC[64 - 44]; /* 44 */ UINT64 paddingD[96 - 64]; /* 64 */ UINT64 paddingE[128 - 96]; /* 96 */ @@ -161,7 +163,10 @@ struct rdp_freerdp ALIGN64 rdpSettings* settings; /**< (offset 18) Pointer to a rdpSettings structure. Will be used to maintain the required RDP settings. Will be initialized by a call to freerdp_context_new() */ - UINT64 paddingB[32 - 19]; /* 19 */ + ALIGN64 rdpAutoDetect* autodetect; /* (offset 19) + Auto-Detect handle for the connection. + Will be initialized by a call to freerdp_context_new() */ + UINT64 paddingB[32 - 20]; /* 20 */ ALIGN64 size_t ContextSize; /* (offset 32) Specifies the size of the 'context' field. freerdp_context_new() will use this size to allocate the context buffer. diff --git a/include/freerdp/peer.h b/include/freerdp/peer.h index 62d817c..4ec3835 100644 --- a/include/freerdp/peer.h +++ b/include/freerdp/peer.h @@ -25,6 +25,7 @@ #include #include #include +#include #include @@ -65,6 +66,7 @@ struct rdp_freerdp_peer rdpInput* input; rdpUpdate* update; rdpSettings* settings; + rdpAutoDetect* autodetect; void* ContextExtra; size_t ContextSize; diff --git a/libfreerdp/core/autodetect.c b/libfreerdp/core/autodetect.c index 7a8cb57..a036afd 100644 --- a/libfreerdp/core/autodetect.c +++ b/libfreerdp/core/autodetect.c @@ -41,6 +41,27 @@ typedef struct UINT16 responseType; } AUTODETECT_RSP_PDU; +static BOOL autodetect_send_rtt_measure_request(rdpContext* context, UINT16 sequenceNumber) +{ + wStream* s; + + s = rdp_message_channel_pdu_init(context->rdp); + + if (!s) + return FALSE; + + WLog_DBG(AUTODETECT_TAG, "sending RTT Measure Request PDU"); + + Stream_Write_UINT8(s, 0x06); /* headerLength (1 byte) */ + Stream_Write_UINT8(s, TYPE_ID_AUTODETECT_REQUEST); /* headerTypeId (1 byte) */ + Stream_Write_UINT16(s, sequenceNumber); /* sequenceNumber (2 bytes) */ + Stream_Write_UINT16(s, 0x0001); /* requestType (2 bytes) */ + + context->rdp->autodetect->rttMeasureStartTime = GetTickCount(); + + return rdp_send_message_channel_pdu(context->rdp, s, SEC_AUTODETECT_REQ); +} + static BOOL autodetect_send_rtt_measure_response(rdpRdp* rdp, UINT16 sequenceNumber) { wStream* s; @@ -62,6 +83,88 @@ static BOOL autodetect_send_rtt_measure_response(rdpRdp* rdp, UINT16 sequenceNum return rdp_send_message_channel_pdu(rdp, s, SEC_AUTODETECT_RSP); } +static BOOL autodetect_send_bandwidth_measure_start(rdpContext* context, UINT16 sequenceNumber) +{ + wStream* s; + + s = rdp_message_channel_pdu_init(context->rdp); + + if (!s) + return FALSE; + + WLog_DBG(AUTODETECT_TAG, "sending Bandwidth Measure Start PDU"); + + Stream_Write_UINT8(s, 0x06); /* headerLength (1 byte) */ + Stream_Write_UINT8(s, TYPE_ID_AUTODETECT_REQUEST); /* headerTypeId (1 byte) */ + Stream_Write_UINT16(s, sequenceNumber); /* sequenceNumber (2 bytes) */ + Stream_Write_UINT16(s, 0x0014); /* requestType (2 bytes) */ + + return rdp_send_message_channel_pdu(context->rdp, s, SEC_AUTODETECT_REQ); +} + +static BOOL autodetect_send_bandwidth_measure_payload(rdpContext* context, UINT16 payloadLength, UINT16 sequenceNumber) +{ + UINT32 i; + wStream* s; + + s = rdp_message_channel_pdu_init(context->rdp); + + if (!s) + return FALSE; + + WLog_DBG(AUTODETECT_TAG, "sending Bandwidth Measure Payload PDU -> payloadLength=%u"); + + /* 4-bytes aligned */ + payloadLength &= ~3; + + Stream_Write_UINT8(s, 0x08); /* headerLength (1 byte) */ + Stream_Write_UINT8(s, TYPE_ID_AUTODETECT_REQUEST); /* headerTypeId (1 byte) */ + Stream_Write_UINT16(s, sequenceNumber); /* sequenceNumber (2 bytes) */ + Stream_Write_UINT16(s, 0x0002); /* requestType (2 bytes) */ + Stream_Write_UINT16(s, payloadLength); /* payloadLength (2 bytes) */ + Stream_EnsureRemainingCapacity(s, payloadLength); + /* Random data (better measurement in case the line is compressed) */ + for (i = 0; i < payloadLength / 4; i++) + { + Stream_Write_UINT32(s, rand()); + } + + return rdp_send_message_channel_pdu(context->rdp, s, SEC_AUTODETECT_REQ); +} + +static BOOL autodetect_send_bandwidth_measure_stop(rdpContext* context, UINT16 payloadLength, UINT16 sequenceNumber) +{ + UINT32 i; + wStream* s; + + s = rdp_message_channel_pdu_init(context->rdp); + + if (!s) + return FALSE; + + WLog_DBG(AUTODETECT_TAG, "sending Bandwidth Measure Stop PDU -> payloadLength=%u"); + + /* 4-bytes aligned */ + payloadLength &= ~3; + + Stream_Write_UINT8(s, 0x08); /* headerLength (1 byte) */ + Stream_Write_UINT8(s, TYPE_ID_AUTODETECT_REQUEST); /* headerTypeId (1 byte) */ + Stream_Write_UINT16(s, sequenceNumber); /* sequenceNumber (2 bytes) */ + Stream_Write_UINT16(s, 0x002B); /* requestType (2 bytes) */ + Stream_Write_UINT16(s, payloadLength); /* payloadLength (2 bytes) */ + if (payloadLength > 0) + { + Stream_EnsureRemainingCapacity(s, payloadLength); + /* Random data (better measurement in case the line is compressed) */ + for (i = 0; i < payloadLength / 4; i++) + { + Stream_Write_UINT32(s, rand()); + } + } + + return rdp_send_message_channel_pdu(context->rdp, s, SEC_AUTODETECT_REQ); +} + static BOOL autodetect_send_bandwidth_measure_results(rdpRdp* rdp, UINT16 responseType, UINT16 sequenceNumber) { wStream* s; @@ -89,6 +192,40 @@ static BOOL autodetect_send_bandwidth_measure_results(rdpRdp* rdp, UINT16 respon return rdp_send_message_channel_pdu(rdp, s, SEC_AUTODETECT_RSP); } +static BOOL autodetect_send_netchar_result(rdpContext* context, UINT16 sequenceNumber) +{ + wStream* s; + + s = rdp_message_channel_pdu_init(context->rdp); + + if (!s) + return FALSE; + + WLog_DBG(AUTODETECT_TAG, "sending Bandwidth Network Characteristics Result PDU"); + + if (context->rdp->autodetect->netCharBandwidth > 0) + { + Stream_Write_UINT8(s, 0x12); /* headerLength (1 byte) */ + Stream_Write_UINT8(s, TYPE_ID_AUTODETECT_REQUEST); /* headerTypeId (1 byte) */ + Stream_Write_UINT16(s, sequenceNumber); /* sequenceNumber (2 bytes) */ + Stream_Write_UINT16(s, 0x08C0); /* requestType (2 bytes) */ + Stream_Write_UINT32(s, context->rdp->autodetect->netCharBaseRTT); /* baseRTT (4 bytes) */ + Stream_Write_UINT32(s, context->rdp->autodetect->netCharBandwidth); /* bandwidth (4 bytes) */ + Stream_Write_UINT32(s, context->rdp->autodetect->netCharAverageRTT); /* averageRTT (4 bytes) */ + } + else + { + Stream_Write_UINT8(s, 0x0E); /* headerLength (1 byte) */ + Stream_Write_UINT8(s, TYPE_ID_AUTODETECT_REQUEST); /* headerTypeId (1 byte) */ + Stream_Write_UINT16(s, sequenceNumber); /* sequenceNumber (2 bytes) */ + Stream_Write_UINT16(s, 0x0840); /* requestType (2 bytes) */ + Stream_Write_UINT32(s, context->rdp->autodetect->netCharBaseRTT); /* baseRTT (4 bytes) */ + Stream_Write_UINT32(s, context->rdp->autodetect->netCharAverageRTT); /* averageRTT (4 bytes) */ + } + + return rdp_send_message_channel_pdu(context->rdp, s, SEC_AUTODETECT_REQ); +} + BOOL autodetect_send_netchar_sync(rdpRdp* rdp, UINT16 sequenceNumber) { wStream* s; @@ -123,6 +260,24 @@ static BOOL autodetect_recv_rtt_measure_request(rdpRdp* rdp, wStream* s, AUTODET return autodetect_send_rtt_measure_response(rdp, autodetectReqPdu->sequenceNumber); } +static BOOL autodetect_recv_rtt_measure_response(rdpRdp* rdp, wStream* s, AUTODETECT_RSP_PDU* autodetectRspPdu) +{ + BOOL success = TRUE; + + if (autodetectRspPdu->headerLength != 0x06) + return FALSE; + + WLog_DBG(AUTODETECT_TAG, "received RTT Measure Response PDU"); + + rdp->autodetect->netCharAverageRTT = GetTickCount() - rdp->autodetect->rttMeasureStartTime; + if (rdp->autodetect->netCharBaseRTT == 0 || rdp->autodetect->netCharBaseRTT > rdp->autodetect->netCharAverageRTT) + rdp->autodetect->netCharBaseRTT = rdp->autodetect->netCharAverageRTT; + + IFCALLRET(rdp->autodetect->RTTMeasureResponse, success, rdp->context, autodetectRspPdu->sequenceNumber); + + return success; +} + static BOOL autodetect_recv_bandwidth_measure_start(rdpRdp* rdp, wStream* s, AUTODETECT_REQ_PDU* autodetectReqPdu) { if (autodetectReqPdu->headerLength != 0x06) @@ -191,8 +346,32 @@ static BOOL autodetect_recv_bandwidth_measure_stop(rdpRdp* rdp, wStream* s, AUTO return autodetect_send_bandwidth_measure_results(rdp, responseType, autodetectReqPdu->sequenceNumber); } +static BOOL autodetect_recv_bandwidth_measure_results(rdpRdp* rdp, wStream* s, AUTODETECT_RSP_PDU* autodetectRspPdu) +{ + BOOL success = TRUE; + + if (autodetectRspPdu->headerLength != 0x0E) + return FALSE; + + WLog_DBG(AUTODETECT_TAG, "received Bandwidth Measure Results PDU"); + + Stream_Read_UINT32(s, rdp->autodetect->bandwidthMeasureTimeDelta); /* timeDelta (4 bytes) */ + Stream_Read_UINT32(s, rdp->autodetect->bandwidthMeasureByteCount); /* byteCount (4 bytes) */ + + if (rdp->autodetect->bandwidthMeasureTimeDelta > 0) + rdp->autodetect->netCharBandwidth = rdp->autodetect->bandwidthMeasureByteCount * 8 / rdp->autodetect->bandwidthMeasureTimeDelta; + else + rdp->autodetect->netCharBandwidth = 0; + + IFCALLRET(rdp->autodetect->BandwidthMeasureResults, success, rdp->context, autodetectRspPdu->sequenceNumber); + + return success; +} + static BOOL autodetect_recv_netchar_result(rdpRdp* rdp, wStream* s, AUTODETECT_REQ_PDU* autodetectReqPdu) { + BOOL success = TRUE; + switch (autodetectReqPdu->requestType) { case 0x0840: @@ -222,8 +401,10 @@ static BOOL autodetect_recv_netchar_result(rdpRdp* rdp, wStream* s, AUTODETECT_R } WLog_DBG(AUTODETECT_TAG, "received Network Characteristics Result PDU -> baseRTT=%u, bandwidth=%u, averageRTT=%u", rdp->autodetect->netCharBaseRTT, rdp->autodetect->netCharBandwidth, rdp->autodetect->netCharAverageRTT); + + IFCALLRET(rdp->autodetect->NetworkCharacteristicsResult, success, rdp->context, autodetectReqPdu->sequenceNumber); - return TRUE; + return success; } int rdp_recv_autodetect_request_packet(rdpRdp* rdp, wStream* s) @@ -309,6 +490,23 @@ int rdp_recv_autodetect_response_packet(rdpRdp* rdp, wStream* s) if (autodetectRspPdu.headerTypeId != TYPE_ID_AUTODETECT_RESPONSE) return -1; + switch (autodetectRspPdu.responseType) + { + case 0x0000: + /* RTT Measure Response (RDP_RTT_RESPONSE) - MS-RDPBCGR 2.2.14.2.1 */ + success = autodetect_recv_rtt_measure_response(rdp, s, &autodetectRspPdu); + break; + + case 0x0003: + case 0x000B: + /* Bandwidth Measure Results (RDP_BW_RESULTS) - MS-RDPBCGR 2.2.14.2.2 */ + success = autodetect_recv_bandwidth_measure_results(rdp, s, &autodetectRspPdu); + break; + + default: + break; + } + return success ? 0 : -1; } @@ -328,3 +526,12 @@ void autodetect_free(rdpAutoDetect* autoDetect) { free(autoDetect); } + +void autodetect_register_server_callbacks(rdpAutoDetect* autodetect) +{ + autodetect->RTTMeasureRequest = autodetect_send_rtt_measure_request; + autodetect->BandwidthMeasureStart = autodetect_send_bandwidth_measure_start; + autodetect->BandwidthMeasurePayload = autodetect_send_bandwidth_measure_payload; + autodetect->BandwidthMeasureStop = autodetect_send_bandwidth_measure_stop; + autodetect->NetworkCharacteristicsResult = autodetect_send_netchar_result; +} diff --git a/libfreerdp/core/autodetect.h b/libfreerdp/core/autodetect.h index c4fa40e..f53115d 100644 --- a/libfreerdp/core/autodetect.h +++ b/libfreerdp/core/autodetect.h @@ -20,11 +20,10 @@ #ifndef __AUTODETECT_H #define __AUTODETECT_H -typedef struct rdp_autodetect rdpAutoDetect; - #include "rdp.h" #include +#include #include #include @@ -33,24 +32,14 @@ typedef struct rdp_autodetect rdpAutoDetect; #define TYPE_ID_AUTODETECT_REQUEST 0x00 #define TYPE_ID_AUTODETECT_RESPONSE 0x01 -struct rdp_autodetect -{ - /* Bandwidth measurement */ - UINT32 bandwidthMeasureStartTime; - UINT32 bandwidthMeasureByteCount; - - /* Network characteristics (as reported by server) */ - UINT32 netCharBandwidth; - UINT32 netCharBaseRTT; - UINT32 netCharAverageRTT; -}; - int rdp_recv_autodetect_request_packet(rdpRdp* rdp, wStream* s); int rdp_recv_autodetect_response_packet(rdpRdp* rdp, wStream* s); rdpAutoDetect* autodetect_new(void); void autodetect_free(rdpAutoDetect* autodetect); +void autodetect_register_server_callbacks(rdpAutoDetect* autodetect); + #define AUTODETECT_TAG FREERDP_TAG("core.autodetect") #endif /* __AUTODETECT_H */ diff --git a/libfreerdp/core/freerdp.c b/libfreerdp/core/freerdp.c index 395d59f..fd17baf 100644 --- a/libfreerdp/core/freerdp.c +++ b/libfreerdp/core/freerdp.c @@ -426,6 +426,7 @@ int freerdp_context_new(freerdp* instance) instance->input = rdp->input; instance->update = rdp->update; instance->settings = rdp->settings; + instance->autodetect = rdp->autodetect; context->graphics = graphics_new(context); context->rdp = rdp; @@ -433,6 +434,7 @@ int freerdp_context_new(freerdp* instance) context->input = instance->input; context->update = instance->update; context->settings = instance->settings; + context->autodetect = instance->autodetect; instance->update->context = instance->context; instance->update->pointer->context = instance->context; @@ -442,6 +444,8 @@ int freerdp_context_new(freerdp* instance) instance->input->context = context; + instance->autodetect->context = context; + update_register_client_callbacks(rdp->update); IFCALL(instance->ContextNew, instance, instance->context); diff --git a/libfreerdp/core/peer.c b/libfreerdp/core/peer.c index d2744bc..5adb4bf 100644 --- a/libfreerdp/core/peer.c +++ b/libfreerdp/core/peer.c @@ -606,17 +606,21 @@ void freerdp_peer_context_new(freerdp_peer* client) client->input = rdp->input; client->update = rdp->update; client->settings = rdp->settings; + client->autodetect = rdp->autodetect; client->context->rdp = rdp; client->context->peer = client; client->context->input = client->input; client->context->update = client->update; client->context->settings = client->settings; + client->context->autodetect = client->autodetect; client->update->context = client->context; client->input->context = client->context; + client->autodetect->context = client->context; update_register_server_callbacks(client->update); + autodetect_register_server_callbacks(client->autodetect); transport_attach(rdp->transport, client->sockfd); -- 2.7.4