libfreerdp-core: server-side auto-detect feature.
authorVic Lee <llyzs.vic@gmail.com>
Tue, 28 Oct 2014 15:29:40 +0000 (23:29 +0800)
committerVic Lee <llyzs.vic@gmail.com>
Tue, 28 Oct 2014 16:49:49 +0000 (00:49 +0800)
include/freerdp/autodetect.h [new file with mode: 0644]
include/freerdp/freerdp.h
include/freerdp/peer.h
libfreerdp/core/autodetect.c
libfreerdp/core/autodetect.h
libfreerdp/core/freerdp.c
libfreerdp/core/peer.c

diff --git a/include/freerdp/autodetect.h b/include/freerdp/autodetect.h
new file mode 100644 (file)
index 0000000..cea7cc8
--- /dev/null
@@ -0,0 +1,60 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ * Auto-Detect PDUs
+ *
+ * Copyright 2014 Dell Software <Mike.McDonald@software.dell.com>
+ * 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 */
index 7023e9d..fef980b 100644 (file)
@@ -51,6 +51,7 @@ typedef RDP_CLIENT_ENTRY_POINTS_V1 RDP_CLIENT_ENTRY_POINTS;
 #include <freerdp/input.h>
 #include <freerdp/update.h>
 #include <freerdp/message.h>
+#include <freerdp/autodetect.h>
 
 #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.
index 62d817c..4ec3835 100644 (file)
@@ -25,6 +25,7 @@
 #include <freerdp/settings.h>
 #include <freerdp/input.h>
 #include <freerdp/update.h>
+#include <freerdp/autodetect.h>
 
 #include <winpr/sspi.h>
 
@@ -65,6 +66,7 @@ struct rdp_freerdp_peer
        rdpInput* input;
        rdpUpdate* update;
        rdpSettings* settings;
+       rdpAutoDetect* autodetect;
 
        void* ContextExtra;
        size_t ContextSize;
index 7a8cb57..a036afd 100644 (file)
@@ -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;
+}
index c4fa40e..f53115d 100644 (file)
 #ifndef __AUTODETECT_H
 #define __AUTODETECT_H
 
-typedef struct rdp_autodetect rdpAutoDetect;
-
 #include "rdp.h"
 
 #include <freerdp/freerdp.h>
+#include <freerdp/autodetect.h>
 #include <freerdp/log.h>
 
 #include <winpr/stream.h>
@@ -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 */
index 395d59f..fd17baf 100644 (file)
@@ -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);
index d2744bc..5adb4bf 100644 (file)
@@ -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);