From a44985c823237445e09137967bd28ec7a4064fc7 Mon Sep 17 00:00:00 2001 From: akallabeth Date: Mon, 17 Feb 2025 10:10:37 +0100 Subject: [PATCH] [core,freerdp] send MCS Disconnect Provider Ultimatum PDU On client side connection termination send a [MS-RDPBCGR] 1.3.1.4.1 User-Initiated on Client disconnect PDU --- include/freerdp/freerdp.h | 12 ++++++++++++ libfreerdp/core/freerdp.c | 24 ++++++++++++++++++++++++ libfreerdp/core/mcs.c | 6 ++++-- libfreerdp/core/mcs.h | 3 ++- libfreerdp/core/peer.c | 6 ++++-- 5 files changed, 46 insertions(+), 5 deletions(-) diff --git a/include/freerdp/freerdp.h b/include/freerdp/freerdp.h index 2d29d4ea6..4cc52314b 100644 --- a/include/freerdp/freerdp.h +++ b/include/freerdp/freerdp.h @@ -347,6 +347,8 @@ extern "C" /** * Defines the possible disconnect reasons in the MCS Disconnect Provider * Ultimatum PDU + * + * [T.125] 7 Structure of Version 2 MCSPDUs Reason ::= ENUMERATED */ enum Disconnect_Ultimatum @@ -579,6 +581,16 @@ owned by rdpRdp */ FREERDP_API BOOL freerdp_shall_disconnect_context(const rdpContext* context); FREERDP_API BOOL freerdp_disconnect(freerdp* instance); + /** @brief stringify disconnect reason of type Disconnect_Ultimatum + * + * @param reason the reason of type \b Disconnect_Ultimatum + * + * @return a string representation of \b reason or rn-unknown + * + * @since version 3.12.1 + */ + FREERDP_API const char* freerdp_disconnect_reason_string(int reason); + WINPR_DEPRECATED_VAR("use freerdp_disconnect_before_reconnect_context instead", FREERDP_API BOOL freerdp_disconnect_before_reconnect(freerdp* instance)); FREERDP_API BOOL freerdp_disconnect_before_reconnect_context(rdpContext* context); diff --git a/libfreerdp/core/freerdp.c b/libfreerdp/core/freerdp.c index 68e33f94c..30536a757 100644 --- a/libfreerdp/core/freerdp.c +++ b/libfreerdp/core/freerdp.c @@ -314,6 +314,11 @@ BOOL freerdp_abort_connect_context(rdpContext* context) freerdp_set_last_error_if_not(context, FREERDP_ERROR_CONNECT_CANCELLED); + /* Try to send a [MS-RDPBCGR] 1.3.1.4.1 User-Initiated on Client PDU, we don't care about + * success */ + if (context->rdp && context->rdp->mcs) + (void)mcs_send_disconnect_provider_ultimatum(context->rdp->mcs, + Disconnect_Ultimatum_user_requested); return utils_abort_connect(context->rdp); } @@ -1468,3 +1473,22 @@ BOOL freerdp_persist_credentials(rdpContext* context) WINPR_ASSERT(context->rdp); return utils_persist_credentials(context->rdp->originalSettings, context->rdp->settings); } + +const char* freerdp_disconnect_reason_string(int reason) +{ + switch (reason) + { + case Disconnect_Ultimatum_domain_disconnected: + return "rn-domain-disconnected"; + case Disconnect_Ultimatum_provider_initiated: + return "rn-provider-initiated"; + case Disconnect_Ultimatum_token_purged: + return "rn-token-purged"; + case Disconnect_Ultimatum_user_requested: + return "rn-user-requested"; + case Disconnect_Ultimatum_channel_purged: + return "rn-channel-purged"; + default: + return "rn-unknown"; + } +} diff --git a/libfreerdp/core/mcs.c b/libfreerdp/core/mcs.c index 0bdf8b350..a51f04732 100644 --- a/libfreerdp/core/mcs.c +++ b/libfreerdp/core/mcs.c @@ -1363,7 +1363,7 @@ BOOL mcs_recv_disconnect_provider_ultimatum(WINPR_ATTR_UNUSED rdpMcs* mcs, wStre * @param mcs mcs module */ -BOOL mcs_send_disconnect_provider_ultimatum(rdpMcs* mcs) +BOOL mcs_send_disconnect_provider_ultimatum(rdpMcs* mcs, enum Disconnect_Ultimatum reason) { wStream* s = NULL; int status = -1; @@ -1379,10 +1379,12 @@ BOOL mcs_send_disconnect_provider_ultimatum(rdpMcs* mcs) if (!mcs_write_domain_mcspdu_header(s, DomainMCSPDU_DisconnectProviderUltimatum, length, 1)) goto fail; - if (!per_write_enumerated(s, 0x80, 0)) + if (!per_write_enumerated(s, 0x80, reason)) goto fail; status = transport_write(mcs->transport, s); fail: + WLog_DBG(TAG, "sending DisconnectProviderUltimatum(%s)", + freerdp_disconnect_reason_string(reason)); Stream_Free(s, TRUE); return (status < 0) ? FALSE : TRUE; } diff --git a/libfreerdp/core/mcs.h b/libfreerdp/core/mcs.h index 54b82af28..38859bcd0 100644 --- a/libfreerdp/core/mcs.h +++ b/libfreerdp/core/mcs.h @@ -172,7 +172,8 @@ FREERDP_LOCAL BOOL mcs_send_channel_join_request(rdpMcs* mcs, UINT16 channelId); FREERDP_LOCAL BOOL mcs_recv_channel_join_confirm(rdpMcs* mcs, wStream* s, UINT16* channelId); FREERDP_LOCAL BOOL mcs_send_channel_join_confirm(rdpMcs* mcs, UINT16 channelId); FREERDP_LOCAL BOOL mcs_recv_disconnect_provider_ultimatum(rdpMcs* mcs, wStream* s, int* reason); -FREERDP_LOCAL BOOL mcs_send_disconnect_provider_ultimatum(rdpMcs* mcs); +FREERDP_LOCAL BOOL mcs_send_disconnect_provider_ultimatum(rdpMcs* mcs, + enum Disconnect_Ultimatum reason); FREERDP_LOCAL BOOL mcs_write_domain_mcspdu_header(wStream* s, DomainMCSPDU domainMCSPDU, UINT16 length, BYTE options); diff --git a/libfreerdp/core/peer.c b/libfreerdp/core/peer.c index 5be3df7ab..e8c681daf 100644 --- a/libfreerdp/core/peer.c +++ b/libfreerdp/core/peer.c @@ -394,7 +394,8 @@ static state_run_t peer_recv_data_pdu(freerdp_peer* client, wStream* s, return STATE_RUN_CONTINUE; // State changed, trigger rerun case DATA_PDU_TYPE_SHUTDOWN_REQUEST: - mcs_send_disconnect_provider_ultimatum(rdp->mcs); + mcs_send_disconnect_provider_ultimatum(rdp->mcs, + Disconnect_Ultimatum_provider_initiated); WLog_WARN(TAG, "disconnect provider ultimatum sent to peer, closing connection"); return STATE_RUN_QUIT_SESSION; @@ -1219,7 +1220,8 @@ static BOOL freerdp_peer_close(freerdp_peer* client) rdp_send_error_info(context->rdp); } - return mcs_send_disconnect_provider_ultimatum(context->rdp->mcs); + return mcs_send_disconnect_provider_ultimatum(context->rdp->mcs, + Disconnect_Ultimatum_provider_initiated); } static void freerdp_peer_disconnect(freerdp_peer* client)