/** * FreeRDP: A Remote Desktop Protocol Implementation * Auto-Detect PDUs * * Copyright 2014 Dell Software * * 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. */ #include #include #include #include "autodetect.h" #define TYPE_ID_AUTODETECT_REQUEST 0x00 #define TYPE_ID_AUTODETECT_RESPONSE 0x01 #define RDP_RTT_REQUEST_TYPE_CONTINUOUS 0x0001 #define RDP_RTT_REQUEST_TYPE_CONNECTTIME 0x1001 #define RDP_RTT_RESPONSE_TYPE 0x0000 #define RDP_BW_START_REQUEST_TYPE_CONTINUOUS 0x0014 #define RDP_BW_START_REQUEST_TYPE_TUNNEL 0x0114 #define RDP_BW_START_REQUEST_TYPE_CONNECTTIME 0x1014 #define RDP_BW_PAYLOAD_REQUEST_TYPE 0x0002 #define RDP_BW_STOP_REQUEST_TYPE_CONNECTTIME 0x002B #define RDP_BW_STOP_REQUEST_TYPE_CONTINUOUS 0x0429 #define RDP_BW_STOP_REQUEST_TYPE_TUNNEL 0x0629 #define RDP_NETCHAR_SYNC_RESPONSE_TYPE 0x0018 #define RDP_NETCHAR_RESULTS_0x0840 0x0840U #define RDP_NETCHAR_RESULTS_0x0880 0x0880U #define RDP_NETCHAR_RESULTS_0x08C0 0x08C0U typedef struct { UINT8 headerLength; UINT8 headerTypeId; UINT16 sequenceNumber; UINT16 requestType; } AUTODETECT_REQ_PDU; typedef struct { UINT8 headerLength; UINT8 headerTypeId; UINT16 sequenceNumber; UINT16 responseType; } AUTODETECT_RSP_PDU; static const char* autodetect_header_type_string(UINT8 headerType, char* buffer, size_t size) { const char* str = NULL; switch (headerType) { case TYPE_ID_AUTODETECT_REQUEST: str = "TYPE_ID_AUTODETECT_REQUEST"; break; case TYPE_ID_AUTODETECT_RESPONSE: str = "TYPE_ID_AUTODETECT_RESPONSE"; break; default: str = "TYPE_ID_AUTODETECT_UNKNOWN"; break; } (void)_snprintf(buffer, size, "%s [0x%08" PRIx8 "]", str, headerType); return buffer; } static const char* autodetect_request_type_to_string(UINT32 requestType) { switch (requestType) { case RDP_RTT_RESPONSE_TYPE: return "RDP_RTT_RESPONSE_TYPE"; case RDP_BW_RESULTS_RESPONSE_TYPE_CONNECTTIME: return "RDP_BW_RESULTS_RESPONSE_TYPE_CONNECTTIME"; case RDP_BW_RESULTS_RESPONSE_TYPE_CONTINUOUS: return "RDP_BW_RESULTS_RESPONSE_TYPE_CONTINUOUS"; case RDP_RTT_REQUEST_TYPE_CONTINUOUS: return "RDP_RTT_REQUEST_TYPE_CONTINUOUS"; case RDP_RTT_REQUEST_TYPE_CONNECTTIME: return "RDP_RTT_REQUEST_TYPE_CONNECTTIME"; case RDP_BW_START_REQUEST_TYPE_CONTINUOUS: return "RDP_BW_START_REQUEST_TYPE_CONTINUOUS"; case RDP_BW_START_REQUEST_TYPE_TUNNEL: return "RDP_BW_START_REQUEST_TYPE_TUNNEL"; case RDP_BW_START_REQUEST_TYPE_CONNECTTIME: return "RDP_BW_START_REQUEST_TYPE_CONNECTTIME"; case RDP_BW_PAYLOAD_REQUEST_TYPE: return "RDP_BW_PAYLOAD_REQUEST_TYPE"; case RDP_BW_STOP_REQUEST_TYPE_CONNECTTIME: return "RDP_BW_STOP_REQUEST_TYPE_CONNECTTIME"; case RDP_BW_STOP_REQUEST_TYPE_CONTINUOUS: return "RDP_BW_STOP_REQUEST_TYPE_CONTINUOUS"; case RDP_BW_STOP_REQUEST_TYPE_TUNNEL: return "RDP_BW_STOP_REQUEST_TYPE_TUNNEL"; case RDP_NETCHAR_RESULTS_0x0840: return "RDP_NETCHAR_RESULTS_0x0840"; case RDP_NETCHAR_RESULTS_0x0880: return "RDP_NETCHAR_RESULTS_0x0880"; case RDP_NETCHAR_RESULTS_0x08C0: return "RDP_NETCHAR_RESULTS_0x08C0"; default: return "UNKNOWN"; } } static const char* autodetect_request_type_to_string_buffer(UINT32 requestType, char* buffer, size_t size) { const char* str = autodetect_request_type_to_string(requestType); (void)_snprintf(buffer, size, "%s [0x%08" PRIx32 "]", str, requestType); return buffer; } static BOOL autodetect_send_rtt_measure_request(rdpAutoDetect* autodetect, RDP_TRANSPORT_TYPE transport, UINT16 sequenceNumber) { UINT16 requestType = 0; wStream* s = NULL; WINPR_ASSERT(autodetect); WINPR_ASSERT(autodetect->context); s = rdp_message_channel_pdu_init(autodetect->context->rdp); if (!s) return FALSE; if (freerdp_get_state(autodetect->context) < CONNECTION_STATE_ACTIVE) requestType = RDP_RTT_REQUEST_TYPE_CONNECTTIME; else requestType = RDP_RTT_REQUEST_TYPE_CONTINUOUS; WLog_Print(autodetect->log, WLOG_TRACE, "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, requestType); /* requestType (2 bytes) */ autodetect->rttMeasureStartTime = GetTickCount64(); return rdp_send_message_channel_pdu(autodetect->context->rdp, s, SEC_AUTODETECT_REQ); } static BOOL autodetect_send_rtt_measure_response(rdpAutoDetect* autodetect, UINT16 sequenceNumber) { wStream* s = NULL; WINPR_ASSERT(autodetect); WINPR_ASSERT(autodetect->context); /* Send the response PDU to the server */ s = rdp_message_channel_pdu_init(autodetect->context->rdp); if (!s) return FALSE; WLog_Print(autodetect->log, WLOG_TRACE, "sending RTT Measure Response PDU (seqNumber=0x%" PRIx16 ")", sequenceNumber); Stream_Write_UINT8(s, 0x06); /* headerLength (1 byte) */ Stream_Write_UINT8(s, TYPE_ID_AUTODETECT_RESPONSE); /* headerTypeId (1 byte) */ Stream_Write_UINT16(s, sequenceNumber); /* sequenceNumber (2 bytes) */ Stream_Write_UINT16(s, RDP_RTT_RESPONSE_TYPE); /* responseType (1 byte) */ return rdp_send_message_channel_pdu(autodetect->context->rdp, s, SEC_AUTODETECT_RSP); } static BOOL autodetect_send_bandwidth_measure_start(rdpAutoDetect* autodetect, RDP_TRANSPORT_TYPE transport, UINT16 sequenceNumber) { UINT16 requestType = 0; wStream* s = NULL; WINPR_ASSERT(autodetect); WINPR_ASSERT(autodetect->context); s = rdp_message_channel_pdu_init(autodetect->context->rdp); if (!s) return FALSE; if (freerdp_get_state(autodetect->context) < CONNECTION_STATE_ACTIVE) requestType = RDP_BW_START_REQUEST_TYPE_CONNECTTIME; else requestType = RDP_BW_START_REQUEST_TYPE_CONTINUOUS; WLog_Print(autodetect->log, WLOG_TRACE, "sending Bandwidth Measure Start PDU(seqNumber=%" PRIu16 ")", sequenceNumber); 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, requestType); /* requestType (2 bytes) */ return rdp_send_message_channel_pdu(autodetect->context->rdp, s, SEC_AUTODETECT_REQ); } static BOOL autodetect_send_bandwidth_measure_payload(rdpAutoDetect* autodetect, RDP_TRANSPORT_TYPE transport, UINT16 sequenceNumber, UINT16 payloadLength) { wStream* s = NULL; WINPR_ASSERT(autodetect); WINPR_ASSERT(autodetect->context); WINPR_ASSERT(freerdp_get_state(autodetect->context) < CONNECTION_STATE_ACTIVE); s = rdp_message_channel_pdu_init(autodetect->context->rdp); if (!s) return FALSE; WLog_Print(autodetect->log, WLOG_TRACE, "sending Bandwidth Measure Payload PDU -> payloadLength=%" PRIu16 "", payloadLength); /* 4-bytes aligned */ payloadLength &= ~3; if (!Stream_EnsureRemainingCapacity(s, 8 + payloadLength)) { WLog_Print(autodetect->log, WLOG_ERROR, "Failed to ensure %" PRIuz " bytes in stream", 8ull + payloadLength); Stream_Release(s); return FALSE; } 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, RDP_BW_PAYLOAD_REQUEST_TYPE); /* requestType (2 bytes) */ Stream_Write_UINT16(s, payloadLength); /* payloadLength (2 bytes) */ /* Random data (better measurement in case the line is compressed) */ winpr_RAND(Stream_Pointer(s), payloadLength); Stream_Seek(s, payloadLength); return rdp_send_message_channel_pdu(autodetect->context->rdp, s, SEC_AUTODETECT_REQ); } static BOOL autodetect_send_bandwidth_measure_stop(rdpAutoDetect* autodetect, RDP_TRANSPORT_TYPE transport, UINT16 sequenceNumber, UINT16 payloadLength) { UINT16 requestType = 0; wStream* s = NULL; WINPR_ASSERT(autodetect); WINPR_ASSERT(autodetect->context); s = rdp_message_channel_pdu_init(autodetect->context->rdp); if (!s) return FALSE; if (freerdp_get_state(autodetect->context) < CONNECTION_STATE_ACTIVE) requestType = RDP_BW_STOP_REQUEST_TYPE_CONNECTTIME; else requestType = RDP_BW_STOP_REQUEST_TYPE_CONTINUOUS; if (requestType == RDP_BW_STOP_REQUEST_TYPE_CONTINUOUS) payloadLength = 0; WLog_Print(autodetect->log, WLOG_TRACE, "sending Bandwidth Measure Stop PDU -> payloadLength=%" PRIu16 "", payloadLength); /* 4-bytes aligned */ payloadLength &= ~3; Stream_Write_UINT8(s, requestType == RDP_BW_STOP_REQUEST_TYPE_CONNECTTIME ? 0x08 : 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, requestType); /* requestType (2 bytes) */ if (requestType == RDP_BW_STOP_REQUEST_TYPE_CONNECTTIME) { Stream_Write_UINT16(s, payloadLength); /* payloadLength (2 bytes) */ if (payloadLength > 0) { if (!Stream_EnsureRemainingCapacity(s, payloadLength)) { WLog_Print(autodetect->log, WLOG_ERROR, "Failed to ensure %" PRIuz " bytes in stream", payloadLength); Stream_Release(s); return FALSE; } /* Random data (better measurement in case the line is compressed) */ winpr_RAND(Stream_Pointer(s), payloadLength); Stream_Seek(s, payloadLength); } } return rdp_send_message_channel_pdu(autodetect->context->rdp, s, SEC_AUTODETECT_REQ); } static BOOL autodetect_send_bandwidth_measure_results(rdpAutoDetect* autodetect, RDP_TRANSPORT_TYPE transport, UINT16 responseType, UINT16 sequenceNumber) { BOOL success = TRUE; wStream* s = NULL; UINT64 timeDelta = GetTickCount64(); WINPR_ASSERT(autodetect); WINPR_ASSERT(autodetect->context); /* Compute the total time */ if (autodetect->bandwidthMeasureStartTime > timeDelta) { WLog_Print(autodetect->log, WLOG_WARN, "Invalid bandwidthMeasureStartTime %" PRIu64 " > current %" PRIu64 ", trimming to 0", autodetect->bandwidthMeasureStartTime, timeDelta); timeDelta = 0; } else timeDelta -= autodetect->bandwidthMeasureStartTime; /* Send the result PDU to the server */ s = rdp_message_channel_pdu_init(autodetect->context->rdp); if (!s) return FALSE; WLog_Print(autodetect->log, WLOG_TRACE, "sending Bandwidth Measure Results PDU -> timeDelta=%" PRIu64 ", byteCount=%" PRIu32 "", timeDelta, autodetect->bandwidthMeasureByteCount); Stream_Write_UINT8(s, 0x0E); /* headerLength (1 byte) */ Stream_Write_UINT8(s, TYPE_ID_AUTODETECT_RESPONSE); /* headerTypeId (1 byte) */ Stream_Write_UINT16(s, sequenceNumber); /* sequenceNumber (2 bytes) */ Stream_Write_UINT16(s, responseType); /* responseType (1 byte) */ Stream_Write_UINT32(s, (UINT32)MIN(timeDelta, UINT32_MAX)); /* timeDelta (4 bytes) */ Stream_Write_UINT32(s, autodetect->bandwidthMeasureByteCount); /* byteCount (4 bytes) */ IFCALLRET(autodetect->ClientBandwidthMeasureResult, success, autodetect, transport, responseType, sequenceNumber, (UINT32)MIN(timeDelta, UINT32_MAX), autodetect->bandwidthMeasureByteCount); if (!success) { WLog_Print(autodetect->log, WLOG_ERROR, "ClientBandwidthMeasureResult failed"); return FALSE; } return rdp_send_message_channel_pdu(autodetect->context->rdp, s, SEC_AUTODETECT_RSP); } static BOOL autodetect_send_netchar_result(rdpAutoDetect* autodetect, RDP_TRANSPORT_TYPE transport, UINT16 sequenceNumber, const rdpNetworkCharacteristicsResult* result) { wStream* s = NULL; WINPR_ASSERT(autodetect); WINPR_ASSERT(autodetect->context); s = rdp_message_channel_pdu_init(autodetect->context->rdp); if (!s) return FALSE; WLog_Print(autodetect->log, WLOG_TRACE, "sending Network Characteristics Result PDU"); switch (result->type) { case RDP_NETCHAR_RESULT_TYPE_BASE_RTT_AVG_RTT: 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, result->type); /* requestType (2 bytes) */ Stream_Write_UINT32(s, result->baseRTT); /* baseRTT (4 bytes) */ Stream_Write_UINT32(s, result->averageRTT); /* averageRTT (4 bytes) */ break; case RDP_NETCHAR_RESULT_TYPE_BW_AVG_RTT: 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, result->type); /* requestType (2 bytes) */ Stream_Write_UINT32(s, result->bandwidth); /* bandwidth (4 bytes) */ Stream_Write_UINT32(s, result->averageRTT); /* averageRTT (4 bytes) */ break; case RDP_NETCHAR_RESULT_TYPE_BASE_RTT_BW_AVG_RTT: 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, result->type); /* requestType (2 bytes) */ Stream_Write_UINT32(s, result->baseRTT); /* baseRTT (4 bytes) */ Stream_Write_UINT32(s, result->bandwidth); /* bandwidth (4 bytes) */ Stream_Write_UINT32(s, result->averageRTT); /* averageRTT (4 bytes) */ break; default: WINPR_ASSERT(FALSE); break; } return rdp_send_message_channel_pdu(autodetect->context->rdp, s, SEC_AUTODETECT_REQ); } static FREERDP_AUTODETECT_STATE autodetect_on_connect_time_auto_detect_begin_default(rdpAutoDetect* autodetect) { WINPR_ASSERT(autodetect); WINPR_ASSERT(autodetect->RTTMeasureRequest); if (!autodetect->RTTMeasureRequest(autodetect, RDP_TRANSPORT_TCP, 0x23)) return FREERDP_AUTODETECT_STATE_FAIL; return FREERDP_AUTODETECT_STATE_REQUEST; } static FREERDP_AUTODETECT_STATE autodetect_on_connect_time_auto_detect_progress_default(rdpAutoDetect* autodetect) { WINPR_ASSERT(autodetect); if (autodetect->state == FREERDP_AUTODETECT_STATE_RESPONSE || autodetect->state == FREERDP_AUTODETECT_STATE_COMPLETE) return FREERDP_AUTODETECT_STATE_COMPLETE; return autodetect->state; } static BOOL autodetect_send_netchar_sync(rdpAutoDetect* autodetect, RDP_TRANSPORT_TYPE transport, UINT16 sequenceNumber) { wStream* s = NULL; WINPR_ASSERT(autodetect); WINPR_ASSERT(autodetect->context); WINPR_ASSERT(autodetect->context->rdp); /* Send the response PDU to the server */ s = rdp_message_channel_pdu_init(autodetect->context->rdp); if (!s) return FALSE; WLog_Print(autodetect->log, WLOG_TRACE, "sending Network Characteristics Sync PDU -> bandwidth=%" PRIu32 ", rtt=%" PRIu32 "", autodetect->netCharBandwidth, autodetect->netCharAverageRTT); Stream_Write_UINT8(s, 0x0E); /* headerLength (1 byte) */ Stream_Write_UINT8(s, TYPE_ID_AUTODETECT_RESPONSE); /* headerTypeId (1 byte) */ Stream_Write_UINT16(s, sequenceNumber); /* sequenceNumber (2 bytes) */ Stream_Write_UINT16(s, RDP_NETCHAR_SYNC_RESPONSE_TYPE); /* responseType (1 byte) */ Stream_Write_UINT32(s, autodetect->netCharBandwidth); /* bandwidth (4 bytes) */ Stream_Write_UINT32(s, autodetect->netCharAverageRTT); /* rtt (4 bytes) */ return rdp_send_message_channel_pdu(autodetect->context->rdp, s, SEC_AUTODETECT_RSP); } static BOOL autodetect_recv_rtt_measure_request(rdpAutoDetect* autodetect, RDP_TRANSPORT_TYPE transport, wStream* s, const AUTODETECT_REQ_PDU* autodetectReqPdu) { WINPR_ASSERT(autodetect); WINPR_ASSERT(s); WINPR_ASSERT(autodetectReqPdu); if (autodetectReqPdu->headerLength != 0x06) { WLog_Print(autodetect->log, WLOG_ERROR, "autodetectReqPdu->headerLength != 0x06 [0x%02" PRIx8 "]", autodetectReqPdu->headerLength); return FALSE; } WLog_Print(autodetect->log, WLOG_TRACE, "received RTT Measure Request PDU"); /* Send a response to the server */ return autodetect_send_rtt_measure_response(autodetect, autodetectReqPdu->sequenceNumber); } static BOOL autodetect_recv_rtt_measure_response(rdpAutoDetect* autodetect, RDP_TRANSPORT_TYPE transport, wStream* s, const AUTODETECT_RSP_PDU* autodetectRspPdu) { BOOL success = TRUE; WINPR_ASSERT(autodetect); WINPR_ASSERT(autodetectRspPdu); if (autodetectRspPdu->headerLength != 0x06) { WLog_Print(autodetect->log, WLOG_ERROR, "autodetectRspPdu->headerLength != 0x06 [0x%02" PRIx8 "]", autodetectRspPdu->headerLength); return FALSE; } WLog_Print(autodetect->log, WLOG_TRACE, "received RTT Measure Response PDU"); autodetect->netCharAverageRTT = (UINT32)MIN(GetTickCount64() - autodetect->rttMeasureStartTime, UINT32_MAX); if (autodetect->netCharBaseRTT == 0 || autodetect->netCharBaseRTT > autodetect->netCharAverageRTT) autodetect->netCharBaseRTT = autodetect->netCharAverageRTT; IFCALLRET(autodetect->RTTMeasureResponse, success, autodetect, transport, autodetectRspPdu->sequenceNumber); if (!success) WLog_Print(autodetect->log, WLOG_WARN, "RTTMeasureResponse failed"); return success; } static BOOL autodetect_recv_bandwidth_measure_start(rdpAutoDetect* autodetect, RDP_TRANSPORT_TYPE transport, wStream* s, const AUTODETECT_REQ_PDU* autodetectReqPdu) { WINPR_ASSERT(autodetect); WINPR_ASSERT(s); WINPR_ASSERT(autodetectReqPdu); if (autodetectReqPdu->headerLength != 0x06) { WLog_Print(autodetect->log, WLOG_ERROR, "autodetectReqPdu->headerLength != 0x06 [0x%02" PRIx8 "]", autodetectReqPdu->headerLength); return FALSE; } WLog_Print(autodetect->log, WLOG_TRACE, "received Bandwidth Measure Start PDU - time=%" PRIu64 "", GetTickCount64()); /* Initialize bandwidth measurement parameters */ autodetect->bandwidthMeasureStartTime = GetTickCount64(); autodetect->bandwidthMeasureByteCount = 0; /* Continuous Auto-Detection: mark the start of the measurement */ if (autodetectReqPdu->requestType == RDP_BW_START_REQUEST_TYPE_CONTINUOUS) { autodetect->bandwidthMeasureStarted = TRUE; } return TRUE; } static BOOL autodetect_recv_bandwidth_measure_payload(rdpAutoDetect* autodetect, RDP_TRANSPORT_TYPE transport, wStream* s, const AUTODETECT_REQ_PDU* autodetectReqPdu) { UINT16 payloadLength = 0; WINPR_ASSERT(autodetect); WINPR_ASSERT(s); WINPR_ASSERT(autodetectReqPdu); if (autodetectReqPdu->headerLength != 0x08) { WLog_Print(autodetect->log, WLOG_ERROR, "autodetectReqPdu->headerLength != 0x08 [0x%02" PRIx8 "]", autodetectReqPdu->headerLength); return FALSE; } if (!Stream_CheckAndLogRequiredLengthWLog(autodetect->log, s, 2)) return FALSE; Stream_Read_UINT16(s, payloadLength); /* payloadLength (2 bytes) */ if (!Stream_CheckAndLogRequiredLengthWLog(autodetect->log, s, payloadLength)) return FALSE; Stream_Seek(s, payloadLength); WLog_Print(autodetect->log, WLOG_DEBUG, "received Bandwidth Measure Payload PDU -> payloadLength=%" PRIu16 "", payloadLength); /* Add the payload length to the bandwidth measurement parameters */ autodetect->bandwidthMeasureByteCount += payloadLength; return TRUE; } static BOOL autodetect_recv_bandwidth_measure_stop(rdpAutoDetect* autodetect, RDP_TRANSPORT_TYPE transport, wStream* s, const AUTODETECT_REQ_PDU* autodetectReqPdu) { UINT16 payloadLength = 0; UINT16 responseType = 0; WINPR_ASSERT(autodetect); WINPR_ASSERT(s); WINPR_ASSERT(autodetectReqPdu); if (autodetectReqPdu->requestType == RDP_BW_STOP_REQUEST_TYPE_CONNECTTIME) { if (autodetectReqPdu->headerLength != 0x08) { WLog_Print(autodetect->log, WLOG_ERROR, "autodetectReqPdu->headerLength != 0x08 [0x%02" PRIx8 "]", autodetectReqPdu->headerLength); return FALSE; } if (!Stream_CheckAndLogRequiredLengthWLog(autodetect->log, s, 2)) return FALSE; Stream_Read_UINT16(s, payloadLength); /* payloadLength (2 bytes) */ } else { if (autodetectReqPdu->headerLength != 0x06) { WLog_Print(autodetect->log, WLOG_ERROR, "autodetectReqPdu->headerLength != 0x06 [0x%02" PRIx8 "]", autodetectReqPdu->headerLength); return FALSE; } payloadLength = 0; } if (!Stream_CheckAndLogRequiredLengthWLog(autodetect->log, s, payloadLength)) return FALSE; Stream_Seek(s, payloadLength); WLog_Print(autodetect->log, WLOG_TRACE, "received Bandwidth Measure Stop PDU -> payloadLength=%" PRIu16 "", payloadLength); /* Add the payload length to the bandwidth measurement parameters */ autodetect->bandwidthMeasureByteCount += payloadLength; /* Continuous Auto-Detection: mark the stop of the measurement */ if (autodetectReqPdu->requestType == RDP_BW_STOP_REQUEST_TYPE_CONTINUOUS) { autodetect->bandwidthMeasureStarted = FALSE; } /* Send a response the server */ responseType = autodetectReqPdu->requestType == RDP_BW_STOP_REQUEST_TYPE_CONNECTTIME ? RDP_BW_RESULTS_RESPONSE_TYPE_CONNECTTIME : RDP_BW_RESULTS_RESPONSE_TYPE_CONTINUOUS; return autodetect_send_bandwidth_measure_results(autodetect, transport, responseType, autodetectReqPdu->sequenceNumber); } static BOOL autodetect_recv_bandwidth_measure_results(rdpAutoDetect* autodetect, RDP_TRANSPORT_TYPE transport, wStream* s, const AUTODETECT_RSP_PDU* autodetectRspPdu) { UINT32 timeDelta = 0; UINT32 byteCount = 0; BOOL success = TRUE; WINPR_ASSERT(autodetect); WINPR_ASSERT(s); WINPR_ASSERT(autodetectRspPdu); if (autodetectRspPdu->headerLength != 0x0E) { WLog_Print(autodetect->log, WLOG_ERROR, "autodetectRspPdu->headerLength != 0x0E [0x%02" PRIx8 "]", autodetectRspPdu->headerLength); return FALSE; } WLog_Print(autodetect->log, WLOG_TRACE, "received Bandwidth Measure Results PDU"); if (!Stream_CheckAndLogRequiredLengthWLog(autodetect->log, s, 8)) return FALSE; Stream_Read_UINT32(s, timeDelta); /* timeDelta (4 bytes) */ Stream_Read_UINT32(s, byteCount); /* byteCount (4 bytes) */ IFCALLRET(autodetect->BandwidthMeasureResults, success, autodetect, transport, autodetectRspPdu->sequenceNumber, autodetectRspPdu->responseType, timeDelta, byteCount); if (!success) WLog_Print(autodetect->log, WLOG_WARN, "BandwidthMeasureResults failed"); return success; } static BOOL autodetect_recv_netchar_sync(rdpAutoDetect* autodetect, RDP_TRANSPORT_TYPE transport, wStream* s, const AUTODETECT_RSP_PDU* autodetectRspPdu) { UINT32 bandwidth = 0; UINT32 rtt = 0; BOOL success = TRUE; WINPR_ASSERT(autodetect); WINPR_ASSERT(s); WINPR_ASSERT(autodetectRspPdu); if (autodetectRspPdu->headerLength != 0x0E) { WLog_Print(autodetect->log, WLOG_ERROR, "autodetectRspPdu->headerLength != 0x0E [0x%02" PRIx8 "]", autodetectRspPdu->headerLength); return FALSE; } if (!Stream_CheckAndLogRequiredLengthWLog(autodetect->log, s, 8)) return FALSE; /* bandwidth and averageRTT fields are present (baseRTT field is not) */ Stream_Read_UINT32(s, bandwidth); /* bandwidth (4 bytes) */ Stream_Read_UINT32(s, rtt); /* rtt (4 bytes) */ WLog_Print(autodetect->log, WLOG_TRACE, "received Network Characteristics Sync PDU -> bandwidth=%" PRIu32 ", rtt=%" PRIu32 "", bandwidth, rtt); IFCALLRET(autodetect->NetworkCharacteristicsSync, success, autodetect, transport, autodetectRspPdu->sequenceNumber, bandwidth, rtt); if (!success) WLog_Print(autodetect->log, WLOG_WARN, "NetworkCharacteristicsSync failed"); return success; } static BOOL autodetect_recv_netchar_request(rdpAutoDetect* autodetect, RDP_TRANSPORT_TYPE transport, wStream* s, const AUTODETECT_REQ_PDU* autodetectReqPdu) { rdpNetworkCharacteristicsResult result = { 0 }; BOOL success = TRUE; WINPR_ASSERT(autodetect); WINPR_ASSERT(s); WINPR_ASSERT(autodetectReqPdu); switch (autodetectReqPdu->requestType) { case RDP_NETCHAR_RESULTS_0x0840: /* baseRTT and averageRTT fields are present (bandwidth field is not) */ if (autodetectReqPdu->headerLength != 0x0E) { WLog_Print(autodetect->log, WLOG_ERROR, "autodetectReqPdu->headerLength != 0x0E [0x%02" PRIx8 "]", autodetectReqPdu->headerLength); return FALSE; } if (!Stream_CheckAndLogRequiredLengthWLog(autodetect->log, s, 8)) return FALSE; result.type = RDP_NETCHAR_RESULT_TYPE_BASE_RTT_AVG_RTT; Stream_Read_UINT32(s, result.baseRTT); /* baseRTT (4 bytes) */ Stream_Read_UINT32(s, result.averageRTT); /* averageRTT (4 bytes) */ break; case RDP_NETCHAR_RESULTS_0x0880: /* bandwidth and averageRTT fields are present (baseRTT field is not) */ if (autodetectReqPdu->headerLength != 0x0E) { WLog_Print(autodetect->log, WLOG_ERROR, "autodetectReqPdu->headerLength != 0x0E [0x%02" PRIx8 "]", autodetectReqPdu->headerLength); return FALSE; } if (!Stream_CheckAndLogRequiredLengthWLog(autodetect->log, s, 8)) return FALSE; result.type = RDP_NETCHAR_RESULT_TYPE_BW_AVG_RTT; Stream_Read_UINT32(s, result.bandwidth); /* bandwidth (4 bytes) */ Stream_Read_UINT32(s, result.averageRTT); /* averageRTT (4 bytes) */ break; case RDP_NETCHAR_RESULTS_0x08C0: /* baseRTT, bandwidth, and averageRTT fields are present */ if (autodetectReqPdu->headerLength != 0x12) { WLog_Print(autodetect->log, WLOG_ERROR, "autodetectReqPdu->headerLength != 0x012 [0x%02" PRIx8 "]", autodetectReqPdu->headerLength); return FALSE; } if (!Stream_CheckAndLogRequiredLengthWLog(autodetect->log, s, 12)) return FALSE; result.type = RDP_NETCHAR_RESULT_TYPE_BASE_RTT_BW_AVG_RTT; Stream_Read_UINT32(s, result.baseRTT); /* baseRTT (4 bytes) */ Stream_Read_UINT32(s, result.bandwidth); /* bandwidth (4 bytes) */ Stream_Read_UINT32(s, result.averageRTT); /* averageRTT (4 bytes) */ break; default: WINPR_ASSERT(FALSE); break; } WLog_Print(autodetect->log, WLOG_TRACE, "received Network Characteristics Result PDU -> baseRTT=%" PRIu32 ", bandwidth=%" PRIu32 ", averageRTT=%" PRIu32 "", result.baseRTT, result.bandwidth, result.averageRTT); IFCALLRET(autodetect->NetworkCharacteristicsResult, success, autodetect, transport, autodetectReqPdu->sequenceNumber, &result); if (!success) WLog_Print(autodetect->log, WLOG_WARN, "NetworkCharacteristicsResult failed"); return success; } state_run_t autodetect_recv_request_packet(rdpAutoDetect* autodetect, RDP_TRANSPORT_TYPE transport, wStream* s) { AUTODETECT_REQ_PDU autodetectReqPdu = { 0 }; const rdpSettings* settings = NULL; BOOL success = FALSE; WINPR_ASSERT(autodetect); WINPR_ASSERT(autodetect->context); settings = autodetect->context->settings; WINPR_ASSERT(settings); if (!Stream_CheckAndLogRequiredLengthWLog(autodetect->log, s, 6)) return STATE_RUN_FAILED; Stream_Read_UINT8(s, autodetectReqPdu.headerLength); /* headerLength (1 byte) */ Stream_Read_UINT8(s, autodetectReqPdu.headerTypeId); /* headerTypeId (1 byte) */ Stream_Read_UINT16(s, autodetectReqPdu.sequenceNumber); /* sequenceNumber (2 bytes) */ Stream_Read_UINT16(s, autodetectReqPdu.requestType); /* requestType (2 bytes) */ if (WLog_IsLevelActive(autodetect->log, WLOG_TRACE)) { char rbuffer[128] = { 0 }; const char* requestTypeStr = autodetect_request_type_to_string_buffer( autodetectReqPdu.requestType, rbuffer, sizeof(rbuffer)); char hbuffer[128] = { 0 }; const char* headerStr = autodetect_header_type_string(autodetectReqPdu.headerTypeId, hbuffer, sizeof(hbuffer)); WLog_Print(autodetect->log, WLOG_TRACE, "rdp_recv_autodetect_request_packet: headerLength=%" PRIu8 ", headerTypeId=%s, sequenceNumber=%" PRIu16 ", requestType=%s", autodetectReqPdu.headerLength, headerStr, autodetectReqPdu.sequenceNumber, requestTypeStr); } if (!freerdp_settings_get_bool(settings, FreeRDP_NetworkAutoDetect)) { char rbuffer[128] = { 0 }; const char* requestTypeStr = autodetect_request_type_to_string_buffer( autodetectReqPdu.requestType, rbuffer, sizeof(rbuffer)); WLog_Print(autodetect->log, WLOG_WARN, "Received a [MS-RDPBCGR] 2.2.14.1.1 RTT Measure Request [%s] " "message but support was not enabled", requestTypeStr); goto fail; } if (autodetectReqPdu.headerTypeId != TYPE_ID_AUTODETECT_REQUEST) { char rbuffer[128] = { 0 }; const char* requestTypeStr = autodetect_request_type_to_string_buffer( autodetectReqPdu.requestType, rbuffer, sizeof(rbuffer)); char hbuffer[128] = { 0 }; const char* headerStr = autodetect_header_type_string(autodetectReqPdu.headerTypeId, hbuffer, sizeof(hbuffer)); WLog_Print(autodetect->log, WLOG_ERROR, "Received a [MS-RDPBCGR] 2.2.14.1.1 RTT Measure Request [%s] " "message with invalid headerTypeId=%s", requestTypeStr, headerStr); goto fail; } IFCALL(autodetect->RequestReceived, autodetect, transport, autodetectReqPdu.requestType, autodetectReqPdu.sequenceNumber); switch (autodetectReqPdu.requestType) { case RDP_RTT_REQUEST_TYPE_CONTINUOUS: case RDP_RTT_REQUEST_TYPE_CONNECTTIME: /* RTT Measure Request (RDP_RTT_REQUEST) - MS-RDPBCGR 2.2.14.1.1 */ success = autodetect_recv_rtt_measure_request(autodetect, transport, s, &autodetectReqPdu); break; case RDP_BW_START_REQUEST_TYPE_CONTINUOUS: case RDP_BW_START_REQUEST_TYPE_TUNNEL: case RDP_BW_START_REQUEST_TYPE_CONNECTTIME: /* Bandwidth Measure Start (RDP_BW_START) - MS-RDPBCGR 2.2.14.1.2 */ success = autodetect_recv_bandwidth_measure_start(autodetect, transport, s, &autodetectReqPdu); break; case RDP_BW_PAYLOAD_REQUEST_TYPE: /* Bandwidth Measure Payload (RDP_BW_PAYLOAD) - MS-RDPBCGR 2.2.14.1.3 */ success = autodetect_recv_bandwidth_measure_payload(autodetect, transport, s, &autodetectReqPdu); break; case RDP_BW_STOP_REQUEST_TYPE_CONNECTTIME: case RDP_BW_STOP_REQUEST_TYPE_CONTINUOUS: case RDP_BW_STOP_REQUEST_TYPE_TUNNEL: /* Bandwidth Measure Stop (RDP_BW_STOP) - MS-RDPBCGR 2.2.14.1.4 */ success = autodetect_recv_bandwidth_measure_stop(autodetect, transport, s, &autodetectReqPdu); break; case RDP_NETCHAR_RESULTS_0x0840: case RDP_NETCHAR_RESULTS_0x0880: case RDP_NETCHAR_RESULTS_0x08C0: /* Network Characteristics Result (RDP_NETCHAR_RESULT) - MS-RDPBCGR 2.2.14.1.5 */ success = autodetect_recv_netchar_request(autodetect, transport, s, &autodetectReqPdu); break; default: WLog_Print(autodetect->log, WLOG_ERROR, "Unknown requestType=0x%04" PRIx16, autodetectReqPdu.requestType); break; } fail: if (success) autodetect->state = FREERDP_AUTODETECT_STATE_REQUEST; else autodetect->state = FREERDP_AUTODETECT_STATE_FAIL; return success ? STATE_RUN_SUCCESS : STATE_RUN_FAILED; } state_run_t autodetect_recv_response_packet(rdpAutoDetect* autodetect, RDP_TRANSPORT_TYPE transport, wStream* s) { AUTODETECT_RSP_PDU autodetectRspPdu = { 0 }; const rdpSettings* settings = NULL; BOOL success = FALSE; WINPR_ASSERT(autodetect); WINPR_ASSERT(autodetect->context); WINPR_ASSERT(s); settings = autodetect->context->settings; WINPR_ASSERT(settings); if (!Stream_CheckAndLogRequiredLengthWLog(autodetect->log, s, 6)) goto fail; Stream_Read_UINT8(s, autodetectRspPdu.headerLength); /* headerLength (1 byte) */ Stream_Read_UINT8(s, autodetectRspPdu.headerTypeId); /* headerTypeId (1 byte) */ Stream_Read_UINT16(s, autodetectRspPdu.sequenceNumber); /* sequenceNumber (2 bytes) */ Stream_Read_UINT16(s, autodetectRspPdu.responseType); /* responseType (2 bytes) */ if (WLog_IsLevelActive(autodetect->log, WLOG_TRACE)) { char rbuffer[128] = { 0 }; const char* requestStr = autodetect_request_type_to_string_buffer( autodetectRspPdu.responseType, rbuffer, sizeof(rbuffer)); char hbuffer[128] = { 0 }; const char* headerStr = autodetect_header_type_string(autodetectRspPdu.headerTypeId, hbuffer, sizeof(hbuffer)); WLog_Print(autodetect->log, WLOG_TRACE, "rdp_recv_autodetect_response_packet: headerLength=%" PRIu8 ", headerTypeId=%s" ", sequenceNumber=%" PRIu16 ", requestType=%s", autodetectRspPdu.headerLength, headerStr, autodetectRspPdu.sequenceNumber, requestStr); } if (!freerdp_settings_get_bool(settings, FreeRDP_NetworkAutoDetect)) { char rbuffer[128] = { 0 }; const char* requestStr = autodetect_request_type_to_string_buffer( autodetectRspPdu.responseType, rbuffer, sizeof(rbuffer)); WLog_Print(autodetect->log, WLOG_WARN, "Received a [MS-RDPBCGR] 2.2.14.2.1 RTT Measure Response [%s] " "message but support was not enabled", requestStr); return STATE_RUN_FAILED; } if (autodetectRspPdu.headerTypeId != TYPE_ID_AUTODETECT_RESPONSE) { char rbuffer[128] = { 0 }; const char* requestStr = autodetect_request_type_to_string_buffer( autodetectRspPdu.responseType, rbuffer, sizeof(rbuffer)); char hbuffer[128] = { 0 }; const char* headerStr = autodetect_header_type_string(autodetectRspPdu.headerTypeId, hbuffer, sizeof(hbuffer)); WLog_Print(autodetect->log, WLOG_ERROR, "Received a [MS-RDPBCGR] 2.2.14.2.1 RTT Measure Response [%s] " "message with invalid headerTypeId=%s", requestStr, headerStr); goto fail; } IFCALL(autodetect->ResponseReceived, autodetect, transport, autodetectRspPdu.responseType, autodetectRspPdu.sequenceNumber); switch (autodetectRspPdu.responseType) { case RDP_RTT_RESPONSE_TYPE: /* RTT Measure Response (RDP_RTT_RESPONSE) - MS-RDPBCGR 2.2.14.2.1 */ success = autodetect_recv_rtt_measure_response(autodetect, transport, s, &autodetectRspPdu); break; case RDP_BW_RESULTS_RESPONSE_TYPE_CONNECTTIME: case RDP_BW_RESULTS_RESPONSE_TYPE_CONTINUOUS: /* Bandwidth Measure Results (RDP_BW_RESULTS) - MS-RDPBCGR 2.2.14.2.2 */ success = autodetect_recv_bandwidth_measure_results(autodetect, transport, s, &autodetectRspPdu); break; case RDP_NETCHAR_SYNC_RESPONSE_TYPE: /* Network Characteristics Sync (RDP_NETCHAR_SYNC) - MS-RDPBCGR 2.2.14.2.3 */ success = autodetect_recv_netchar_sync(autodetect, transport, s, &autodetectRspPdu); break; default: WLog_Print(autodetect->log, WLOG_ERROR, "Unknown responseType=0x%04" PRIx16, autodetectRspPdu.responseType); break; } fail: if (success) { if (autodetectRspPdu.responseType == RDP_BW_RESULTS_RESPONSE_TYPE_CONNECTTIME) autodetect->state = FREERDP_AUTODETECT_STATE_COMPLETE; else autodetect->state = FREERDP_AUTODETECT_STATE_RESPONSE; } else autodetect->state = FREERDP_AUTODETECT_STATE_FAIL; return success ? STATE_RUN_SUCCESS : STATE_RUN_FAILED; } void autodetect_on_connect_time_auto_detect_begin(rdpAutoDetect* autodetect) { WINPR_ASSERT(autodetect); WINPR_ASSERT(autodetect->OnConnectTimeAutoDetectBegin); autodetect->state = autodetect->OnConnectTimeAutoDetectBegin(autodetect); } void autodetect_on_connect_time_auto_detect_progress(rdpAutoDetect* autodetect) { WINPR_ASSERT(autodetect); WINPR_ASSERT(autodetect->OnConnectTimeAutoDetectProgress); autodetect->state = autodetect->OnConnectTimeAutoDetectProgress(autodetect); } rdpAutoDetect* autodetect_new(rdpContext* context) { rdpAutoDetect* autoDetect = (rdpAutoDetect*)calloc(1, sizeof(rdpAutoDetect)); if (!autoDetect) return NULL; autoDetect->context = context; autoDetect->log = WLog_Get(AUTODETECT_TAG); return autoDetect; } void autodetect_free(rdpAutoDetect* autoDetect) { free(autoDetect); } void autodetect_register_server_callbacks(rdpAutoDetect* autodetect) { WINPR_ASSERT(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; /* * Default handlers for Connect-Time Auto-Detection * (MAY be overridden by the API user) */ autodetect->OnConnectTimeAutoDetectBegin = autodetect_on_connect_time_auto_detect_begin_default; autodetect->OnConnectTimeAutoDetectProgress = autodetect_on_connect_time_auto_detect_progress_default; } FREERDP_AUTODETECT_STATE autodetect_get_state(rdpAutoDetect* autodetect) { WINPR_ASSERT(autodetect); return autodetect->state; } rdpAutoDetect* autodetect_get(rdpContext* context) { WINPR_ASSERT(context); WINPR_ASSERT(context->rdp); return context->rdp->autodetect; }