libfreerdp-core: server-side auto-detect feature.

This commit is contained in:
Vic Lee
2014-10-28 23:29:40 +08:00
parent 0e7797ffca
commit 152d525a05
7 changed files with 288 additions and 17 deletions

View File

@@ -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 */

View 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.

View 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;

View 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;
}

View File

@@ -20,11 +20,10 @@
#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 */

View 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);

View 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);