diff --git a/libfreerdp/core/mcs.c b/libfreerdp/core/mcs.c index 43a7a480c..ba34f98b1 100644 --- a/libfreerdp/core/mcs.c +++ b/libfreerdp/core/mcs.c @@ -818,6 +818,54 @@ BOOL mcs_send_channel_join_confirm(rdpMcs* mcs, UINT16 channel_id) return (status < 0) ? FALSE : TRUE; } +/** + * Receive MCS Disconnect Provider Ultimatum PDU.\n + * @param mcs mcs module + */ + +BOOL mcs_recv_disconnect_provider_ultimatum(rdpMcs* mcs, wStream* s, int* reason) +{ + BYTE b1, b2; + + /* + * http://msdn.microsoft.com/en-us/library/cc240872.aspx: + * + * PER encoded (ALIGNED variant of BASIC-PER) PDU contents: + * 21 80 + * + * 0x21: + * 0 - --\ + * 0 - | + * 1 - | CHOICE: From DomainMCSPDU select disconnectProviderUltimatum (8) + * 0 - | of type DisconnectProviderUltimatum + * 0 - | + * 0 - --/ + * 0 - --\ + * 1 - | + * | DisconnectProviderUltimatum::reason = rn-user-requested (3) + * 0x80: | + * 1 - --/ + * 0 - padding + * 0 - padding + * 0 - padding + * 0 - padding + * 0 - padding + * 0 - padding + * 0 - padding + */ + + if (Stream_GetRemainingLength(s) < 1) + return FALSE; + + Stream_Rewind_UINT8(s); + Stream_Read_UINT8(s, b1); + Stream_Read_UINT8(s, b2); + + *reason = ((b1 & 0x01) << 1) | (b2 >> 7); + + return TRUE; +} + /** * Send MCS Disconnect Provider Ultimatum PDU.\n * @param mcs mcs module diff --git a/libfreerdp/core/mcs.h b/libfreerdp/core/mcs.h index 1006231a7..9901f5d95 100644 --- a/libfreerdp/core/mcs.h +++ b/libfreerdp/core/mcs.h @@ -20,6 +20,8 @@ #ifndef __MCS_H #define __MCS_H +typedef struct rdp_mcs rdpMcs; + #include "transport.h" #include @@ -52,6 +54,15 @@ enum MCS_Result MCS_Result_enum_length = 16 }; +enum MCS_Reason +{ + MCS_Reason_domain_disconnected = 0, + MCS_Reason_provider_initiated = 1, + MCS_Reason_token_purged = 2, + MCS_Reason_user_requested = 3, + MCS_Reason_channel_purged = 4 +}; + enum DomainMCSPDU { DomainMCSPDU_PlumbDomainIndication = 0, @@ -115,7 +126,7 @@ typedef struct struct rdp_mcs { UINT16 user_id; - struct rdp_transport* transport; + rdpTransport* transport; DomainParameters domainParameters; DomainParameters targetParameters; DomainParameters minimumParameters; @@ -124,7 +135,6 @@ struct rdp_mcs BOOL user_channel_joined; BOOL global_channel_joined; }; -typedef struct rdp_mcs rdpMcs; #define MCS_SEND_DATA_HEADER_MAX_LENGTH 8 @@ -148,6 +158,7 @@ BOOL mcs_recv_channel_join_request(rdpMcs* mcs, wStream* s, UINT16* channel_id); BOOL mcs_send_channel_join_request(rdpMcs* mcs, UINT16 channel_id); BOOL mcs_recv_channel_join_confirm(rdpMcs* mcs, wStream* s, UINT16* channel_id); BOOL mcs_send_channel_join_confirm(rdpMcs* mcs, UINT16 channel_id); +BOOL mcs_recv_disconnect_provider_ultimatum(rdpMcs* mcs, wStream* s, int* reason); BOOL mcs_send_disconnect_provider_ultimatum(rdpMcs* mcs); BOOL mcs_read_domain_mcspdu_header(wStream* s, enum DomainMCSPDU* domainMCSPDU, UINT16* length); void mcs_write_domain_mcspdu_header(wStream* s, enum DomainMCSPDU domainMCSPDU, UINT16 length, BYTE options); diff --git a/libfreerdp/core/rdp.c b/libfreerdp/core/rdp.c index 69e964e18..589f1dda2 100644 --- a/libfreerdp/core/rdp.c +++ b/libfreerdp/core/rdp.c @@ -229,6 +229,25 @@ wStream* rdp_data_pdu_init(rdpRdp* rdp) return s; } +BOOL rdp_set_error_info(rdpRdp* rdp, UINT32 errorInfo) +{ + rdp->errorInfo = errorInfo; + + if (rdp->errorInfo != ERRINFO_SUCCESS) + { + ErrorInfoEventArgs e; + rdpContext* context = rdp->instance->context; + + rdp_print_errinfo(rdp->errorInfo); + + EventArgsInit(&e, "freerdp"); + e.code = rdp->errorInfo; + PubSub_OnErrorInfo(context->pubSub, context, &e); + } + + return TRUE; +} + /** * Read an RDP packet header.\n * @param rdp rdp module @@ -256,12 +275,25 @@ BOOL rdp_read_header(rdpRdp* rdp, wStream* s, UINT16* length, UINT16* channelId) if (MCSPDU == DomainMCSPDU_DisconnectProviderUltimatum) { - BYTE reason; + int reason = 0; TerminateEventArgs e; rdpContext* context = rdp->instance->context; - (void) per_read_enumerated(s, &reason, 0); - DEBUG_RDP("DisconnectProviderUltimatum from server, reason code 0x%02x\n", reason); + if (!mcs_recv_disconnect_provider_ultimatum(rdp->mcs, s, &reason)) + return FALSE; + + if (rdp->errorInfo == ERRINFO_SUCCESS) + { + /** + * Some servers like Windows Server 2008 R2 do not send the error info pdu + * when the user logs off like they should. Map DisconnectProviderUltimatum + * to a ERRINFO_LOGOFF_BY_USER when the errinfo code is ERRINFO_SUCCESS. + */ + + rdp_set_error_info(rdp, ERRINFO_LOGOFF_BY_USER); + } + + fprintf(stderr, "DisconnectProviderUltimatum: reason: %d\n", reason); rdp->disconnect = TRUE; @@ -503,22 +535,14 @@ BOOL rdp_send_data_pdu(rdpRdp* rdp, wStream* s, BYTE type, UINT16 channel_id) BOOL rdp_recv_set_error_info_data_pdu(rdpRdp* rdp, wStream* s) { + UINT32 errorInfo; + if (Stream_GetRemainingLength(s) < 4) return FALSE; - Stream_Read_UINT32(s, rdp->errorInfo); /* errorInfo (4 bytes) */ + Stream_Read_UINT32(s, errorInfo); /* errorInfo (4 bytes) */ - if (rdp->errorInfo != ERRINFO_SUCCESS) - { - ErrorInfoEventArgs e; - rdpContext* context = rdp->instance->context; - - rdp_print_errinfo(rdp->errorInfo); - - EventArgsInit(&e, "freerdp"); - e.code = rdp->errorInfo; - PubSub_OnErrorInfo(context->pubSub, context, &e); - } + rdp_set_error_info(rdp, errorInfo); return TRUE; }