From 33827cb920e9309b149d76df934590886405b832 Mon Sep 17 00:00:00 2001 From: akallabeth Date: Fri, 21 Oct 2022 10:31:14 +0200 Subject: [PATCH] Updated RDP state machine * More detailed states * Better transition checks * No more recursive calling of state machine functions --- client/Sample/tf_freerdp.c | 2 +- client/X11/cli/xfreerdp.c | 2 +- include/freerdp/freerdp.h | 27 +- libfreerdp/core/activation.c | 32 +-- libfreerdp/core/connection.c | 257 ++++++++++++------- libfreerdp/core/info.c | 2 + libfreerdp/core/peer.c | 462 +++++++++++++++++++++++++++-------- libfreerdp/core/rdp.c | 292 ++++++++++++++++------ 8 files changed, 777 insertions(+), 299 deletions(-) diff --git a/client/Sample/tf_freerdp.c b/client/Sample/tf_freerdp.c index 50c6713ef..d42cebd3f 100644 --- a/client/Sample/tf_freerdp.c +++ b/client/Sample/tf_freerdp.c @@ -395,7 +395,7 @@ int main(int argc, char* argv[]) goto fail; } - if (!stream_dump_register_handlers(context, CONNECTION_STATE_MCS_CONNECT, FALSE)) + if (!stream_dump_register_handlers(context, CONNECTION_STATE_MCS_CREATE_REQUEST, FALSE)) goto fail; if (freerdp_client_start(context) != 0) diff --git a/client/X11/cli/xfreerdp.c b/client/X11/cli/xfreerdp.c index 92bbe7c55..f64f7d14a 100644 --- a/client/X11/cli/xfreerdp.c +++ b/client/X11/cli/xfreerdp.c @@ -65,7 +65,7 @@ int main(int argc, char* argv[]) goto out; } - if (!stream_dump_register_handlers(context, CONNECTION_STATE_MCS_CONNECT, FALSE)) + if (!stream_dump_register_handlers(context, CONNECTION_STATE_MCS_CREATE_REQUEST, FALSE)) goto out; if (freerdp_client_start(context) != 0) diff --git a/include/freerdp/freerdp.h b/include/freerdp/freerdp.h index 4e1f7150b..922f91346 100644 --- a/include/freerdp/freerdp.h +++ b/include/freerdp/freerdp.h @@ -80,17 +80,32 @@ extern "C" CONNECTION_STATE_INITIAL, CONNECTION_STATE_NEGO, CONNECTION_STATE_NLA, - CONNECTION_STATE_MCS_CONNECT, + CONNECTION_STATE_MCS_CREATE_REQUEST, + CONNECTION_STATE_MCS_CREATE_RESPONSE, CONNECTION_STATE_MCS_ERECT_DOMAIN, CONNECTION_STATE_MCS_ATTACH_USER, - CONNECTION_STATE_MCS_CHANNEL_JOIN, + CONNECTION_STATE_MCS_ATTACH_USER_CONFIRM, + CONNECTION_STATE_MCS_CHANNEL_JOIN_REQUEST, + CONNECTION_STATE_MCS_CHANNEL_JOIN_RESPONSE, CONNECTION_STATE_RDP_SECURITY_COMMENCEMENT, CONNECTION_STATE_SECURE_SETTINGS_EXCHANGE, - CONNECTION_STATE_CONNECT_TIME_AUTO_DETECT, + CONNECTION_STATE_CONNECT_TIME_AUTO_DETECT_REQUEST, + CONNECTION_STATE_CONNECT_TIME_AUTO_DETECT_RESPONSE, CONNECTION_STATE_LICENSING, - CONNECTION_STATE_MULTITRANSPORT_BOOTSTRAPPING, - CONNECTION_STATE_CAPABILITIES_EXCHANGE, - CONNECTION_STATE_FINALIZATION, + CONNECTION_STATE_MULTITRANSPORT_BOOTSTRAPPING_REQUEST, + CONNECTION_STATE_MULTITRANSPORT_BOOTSTRAPPING_RESPONSE, + CONNECTION_STATE_CAPABILITIES_EXCHANGE_DEMAND_ACTIVE, + CONNECTION_STATE_CAPABILITIES_EXCHANGE_MONITOR_LAYOUT, + CONNECTION_STATE_CAPABILITIES_EXCHANGE_CONFIRM_ACTIVE, + CONNECTION_STATE_FINALIZATION_SYNC, + CONNECTION_STATE_FINALIZATION_COOPERATE, + CONNECTION_STATE_FINALIZATION_REQUEST_CONTROL, + CONNECTION_STATE_FINALIZATION_PERSISTENT_KEY_LIST, + CONNECTION_STATE_FINALIZATION_FONT_LIST, + CONNECTION_STATE_FINALIZATION_CLIENT_SYNC, + CONNECTION_STATE_FINALIZATION_CLIENT_COOPERATE, + CONNECTION_STATE_FINALIZATION_CLIENT_GRANTED_CONTROL, + CONNECTION_STATE_FINALIZATION_CLIENT_FONT_MAP, CONNECTION_STATE_ACTIVE } CONNECTION_STATE; diff --git a/libfreerdp/core/activation.c b/libfreerdp/core/activation.c index d34c196e0..0947c7cd4 100644 --- a/libfreerdp/core/activation.c +++ b/libfreerdp/core/activation.c @@ -625,7 +625,7 @@ BOOL rdp_recv_deactivate_all(rdpRdp* rdp, wStream* s) } while (0); } - rdp_client_transition_to_state(rdp, CONNECTION_STATE_CAPABILITIES_EXCHANGE); + rdp_client_transition_to_state(rdp, CONNECTION_STATE_CAPABILITIES_EXCHANGE_DEMAND_ACTIVE); for (timeout = 0; timeout < freerdp_settings_get_uint32(rdp->settings, FreeRDP_TcpAckTimeout); timeout += 100) @@ -710,8 +710,6 @@ BOOL rdp_server_accept_client_control_pdu(rdpRdp* rdp, wStream* s) GrantId, ControlId); return FALSE; } - if (!rdp_send_server_control_granted_pdu(rdp)) - return FALSE; return rdp_finalize_set_flag(rdp, FINALIZE_CS_CONTROL_REQUEST_PDU); case CTRLACTION_COOPERATE: if (!rdp_finalize_is_flag_set(rdp, FINALIZE_CS_SYNCHRONIZE_PDU)) @@ -767,26 +765,21 @@ BOOL rdp_server_accept_client_font_list_pdu(rdpRdp* rdp, wStream* s) if (!rdp_recv_client_font_list_pdu(s)) return FALSE; + rdp_finalize_set_flag(rdp, FINALIZE_CS_FONT_LIST_PDU); - if (settings->SupportMonitorLayoutPdu && settings->MonitorCount && peer->AdjustMonitorsLayout && - peer->AdjustMonitorsLayout(peer)) - { - /* client supports the monitorLayout PDU, let's send him the monitors if any */ - MONITOR_DEF* monitors = NULL; + rdp_server_transition_to_state(rdp, CONNECTION_STATE_FINALIZATION_CLIENT_SYNC); + if (!rdp_send_server_synchronize_pdu(rdp)) + return FALSE; - if (!display_convert_rdp_monitor_to_monitor_def(settings->MonitorCount, - settings->MonitorDefArray, &monitors)) - return FALSE; + rdp_server_transition_to_state(rdp, CONNECTION_STATE_FINALIZATION_CLIENT_COOPERATE); + if (!rdp_send_server_control_cooperate_pdu(rdp)) + return FALSE; - if (!freerdp_display_send_monitor_layout(rdp->context, settings->MonitorCount, monitors)) - { - free(monitors); - return FALSE; - } - - free(monitors); - } + rdp_server_transition_to_state(rdp, CONNECTION_STATE_FINALIZATION_CLIENT_GRANTED_CONTROL); + if (!rdp_send_server_control_granted_pdu(rdp)) + return FALSE; + rdp_server_transition_to_state(rdp, CONNECTION_STATE_FINALIZATION_CLIENT_FONT_MAP); if (!rdp_send_server_font_map_pdu(rdp)) return FALSE; @@ -804,6 +797,7 @@ BOOL rdp_server_accept_client_persistent_key_list_pdu(rdpRdp* rdp, wStream* s) if (!rdp_recv_client_persistent_key_list_pdu(s)) return FALSE; + rdp_finalize_set_flag(rdp, FINALIZE_CS_PERSISTENT_KEY_LIST_PDU); // TODO: Actually do something with this return TRUE; } diff --git a/libfreerdp/core/connection.c b/libfreerdp/core/connection.c index 9a48d4627..2d7f33df1 100644 --- a/libfreerdp/core/connection.c +++ b/libfreerdp/core/connection.c @@ -362,25 +362,28 @@ BOOL rdp_client_connect(rdpRdp* rdp) if ((SelectedProtocol & PROTOCOL_SSL) || (SelectedProtocol == PROTOCOL_RDP)) { + wStream s = { 0 }; + if ((settings->Username != NULL) && ((freerdp_settings_get_string(settings, FreeRDP_Password) != NULL) || (settings->RedirectionPassword != NULL && settings->RedirectionPasswordLength > 0))) settings->AutoLogonEnabled = TRUE; + + if (rdp_recv_callback(rdp->transport, &s, rdp) < 0) + return FALSE; } + transport_set_blocking_mode(rdp->transport, FALSE); } + else + { + rdp_client_transition_to_state(rdp, CONNECTION_STATE_MCS_CREATE_REQUEST); + } /* everything beyond this point is event-driven and non blocking */ transport_set_recv_callbacks(rdp->transport, rdp_recv_callback, rdp); - if (rdp_get_state(rdp) != CONNECTION_STATE_NLA) - { - rdp_client_transition_to_state(rdp, CONNECTION_STATE_MCS_CONNECT); - if (!mcs_client_begin(rdp->mcs)) - return FALSE; - } - now = GetTickCount64(); dueDate = now + freerdp_settings_get_uint32(settings, FreeRDP_TcpAckTimeout); @@ -698,6 +701,8 @@ static BOOL rdp_client_establish_keys(rdpRdp* rdp) return TRUE; } + rdp_client_transition_to_state(rdp, CONNECTION_STATE_RDP_SECURITY_COMMENCEMENT); + /* encrypt client random */ free(settings->ClientRandom); settings->ClientRandomLength = CLIENT_RANDOM_LENGTH; @@ -953,8 +958,10 @@ BOOL rdp_client_connect_mcs_channel_join_confirm(rdpRdp* rdp, wStream* s) mcs->userChannelJoined = TRUE; + rdp_client_transition_to_state(rdp, CONNECTION_STATE_MCS_CHANNEL_JOIN_REQUEST); if (!mcs_send_channel_join_request(mcs, MCS_GLOBAL_CHANNEL_ID)) return FALSE; + rdp_client_transition_to_state(rdp, CONNECTION_STATE_MCS_CHANNEL_JOIN_RESPONSE); } else if (!mcs->globalChannelJoined) { @@ -965,8 +972,10 @@ BOOL rdp_client_connect_mcs_channel_join_confirm(rdpRdp* rdp, wStream* s) if (mcs->messageChannelId != 0) { + rdp_client_transition_to_state(rdp, CONNECTION_STATE_MCS_CHANNEL_JOIN_REQUEST); if (!mcs_send_channel_join_request(mcs, mcs->messageChannelId)) return FALSE; + rdp_client_transition_to_state(rdp, CONNECTION_STATE_MCS_CHANNEL_JOIN_RESPONSE); allJoined = FALSE; } @@ -975,8 +984,10 @@ BOOL rdp_client_connect_mcs_channel_join_confirm(rdpRdp* rdp, wStream* s) if (mcs->channelCount > 0) { const rdpMcsChannel* cur = &mcs->channels[0]; + rdp_client_transition_to_state(rdp, CONNECTION_STATE_MCS_CHANNEL_JOIN_REQUEST); if (!mcs_send_channel_join_request(mcs, cur->ChannelId)) return FALSE; + rdp_client_transition_to_state(rdp, CONNECTION_STATE_MCS_CHANNEL_JOIN_RESPONSE); allJoined = FALSE; } @@ -992,8 +1003,10 @@ BOOL rdp_client_connect_mcs_channel_join_confirm(rdpRdp* rdp, wStream* s) if (mcs->channelCount > 0) { const rdpMcsChannel* cur = &mcs->channels[0]; + rdp_client_transition_to_state(rdp, CONNECTION_STATE_MCS_CHANNEL_JOIN_REQUEST); if (!mcs_send_channel_join_request(mcs, cur->ChannelId)) return FALSE; + rdp_client_transition_to_state(rdp, CONNECTION_STATE_MCS_CHANNEL_JOIN_RESPONSE); allJoined = FALSE; } @@ -1016,8 +1029,10 @@ BOOL rdp_client_connect_mcs_channel_join_confirm(rdpRdp* rdp, wStream* s) if (i + 1 < mcs->channelCount) { const rdpMcsChannel* cur = &mcs->channels[i + 1]; + rdp_client_transition_to_state(rdp, CONNECTION_STATE_MCS_CHANNEL_JOIN_REQUEST); if (!mcs_send_channel_join_request(mcs, cur->ChannelId)) return FALSE; + rdp_client_transition_to_state(rdp, CONNECTION_STATE_MCS_CHANNEL_JOIN_RESPONSE); allJoined = FALSE; } @@ -1028,10 +1043,11 @@ BOOL rdp_client_connect_mcs_channel_join_confirm(rdpRdp* rdp, wStream* s) if (!rdp_client_establish_keys(rdp)) return FALSE; + rdp_client_transition_to_state(rdp, CONNECTION_STATE_SECURE_SETTINGS_EXCHANGE); if (!rdp_send_client_info(rdp)) return FALSE; - rdp_client_transition_to_state(rdp, CONNECTION_STATE_LICENSING); + rdp_client_transition_to_state(rdp, CONNECTION_STATE_CONNECT_TIME_AUTO_DETECT_REQUEST); } return TRUE; @@ -1114,7 +1130,16 @@ int rdp_client_connect_license(rdpRdp* rdp, wStream* s) WLog_ERR(TAG, "license connection sequence aborted."); return -1; case LICENSE_STATE_COMPLETED: - rdp_client_transition_to_state(rdp, CONNECTION_STATE_CAPABILITIES_EXCHANGE); + if (rdp->settings->MultitransportFlags) + { + rdp_client_transition_to_state( + rdp, CONNECTION_STATE_MULTITRANSPORT_BOOTSTRAPPING_REQUEST); + } + else + { + rdp_client_transition_to_state( + rdp, CONNECTION_STATE_CAPABILITIES_EXCHANGE_DEMAND_ACTIVE); + } return 0; default: return 0; @@ -1152,7 +1177,7 @@ int rdp_client_connect_demand_active(rdpRdp* rdp, wStream* s) return rc; } - return rdp_client_connect_confirm_active(rdp, s); + return 0; } int rdp_client_connect_finalize(rdpRdp* rdp) @@ -1163,12 +1188,15 @@ int rdp_client_connect_finalize(rdpRdp* rdp) * server-to- client PDUs; they may be sent as a single batch, provided that sequencing is * maintained. */ + rdp_client_transition_to_state(rdp, CONNECTION_STATE_FINALIZATION_SYNC); if (!rdp_send_client_synchronize_pdu(rdp)) return -1; + rdp_client_transition_to_state(rdp, CONNECTION_STATE_FINALIZATION_COOPERATE); if (!rdp_send_client_control_pdu(rdp, CTRLACTION_COOPERATE)) return -1; + rdp_client_transition_to_state(rdp, CONNECTION_STATE_FINALIZATION_REQUEST_CONTROL); if (!rdp_send_client_control_pdu(rdp, CTRLACTION_REQUEST_CONTROL)) return -1; @@ -1182,26 +1210,34 @@ int rdp_client_connect_finalize(rdpRdp* rdp) if (!rdp_finalize_is_flag_set(rdp, FINALIZE_DEACTIVATE_REACTIVATE) && rdp->settings->BitmapCachePersistEnabled) { + rdp_client_transition_to_state(rdp, CONNECTION_STATE_FINALIZATION_PERSISTENT_KEY_LIST); if (!rdp_send_client_persistent_key_list_pdu(rdp)) return -1; } + rdp_client_transition_to_state(rdp, CONNECTION_STATE_FINALIZATION_FONT_LIST); if (!rdp_send_client_font_list_pdu(rdp, FONTLIST_FIRST | FONTLIST_LAST)) return -1; + rdp_client_transition_to_state(rdp, CONNECTION_STATE_FINALIZATION_CLIENT_SYNC); return 0; } int rdp_client_transition_to_state(rdpRdp* rdp, CONNECTION_STATE state) { int status = 0; + const char* name = rdp_state_string(state); - WLog_DBG(TAG, "%s %s --> %s", __FUNCTION__, rdp_get_state_string(rdp), rdp_state_string(state)); + WLog_DBG(TAG, "%s %s --> %s", __FUNCTION__, rdp_get_state_string(rdp), name); rdp_set_state(rdp, state); switch (state) { - case CONNECTION_STATE_FINALIZATION: + case CONNECTION_STATE_FINALIZATION_SYNC: + case CONNECTION_STATE_FINALIZATION_COOPERATE: + case CONNECTION_STATE_FINALIZATION_REQUEST_CONTROL: + case CONNECTION_STATE_FINALIZATION_PERSISTENT_KEY_LIST: + case CONNECTION_STATE_FINALIZATION_FONT_LIST: update_reset_state(rdp->update); rdp_finalize_reset_flags(rdp, FALSE); break; @@ -1334,52 +1370,82 @@ BOOL rdp_server_accept_nego(rdpRdp* rdp, wStream* s) return FALSE; transport_set_blocking_mode(rdp->transport, FALSE); - rdp_server_transition_to_state(rdp, CONNECTION_STATE_NEGO); + return TRUE; } BOOL rdp_server_accept_mcs_connect_initial(rdpRdp* rdp, wStream* s) { UINT32 i; - rdpMcs* mcs = rdp->mcs; + rdpMcs* mcs; + WINPR_ASSERT(rdp); + WINPR_ASSERT(s); + + mcs = rdp->mcs; + WINPR_ASSERT(mcs); + + WINPR_ASSERT(rdp_get_state(rdp) == CONNECTION_STATE_MCS_CREATE_REQUEST); if (!mcs_recv_connect_initial(mcs, s)) return FALSE; + WINPR_ASSERT(rdp->settings); + + if (!mcs_server_apply_to_settings(mcs, rdp->settings)) + return FALSE; WLog_INFO(TAG, "Accepted client: %s", rdp->settings->ClientHostname); WLog_INFO(TAG, "Accepted channels:"); + WINPR_ASSERT(mcs->channels || (mcs->channelCount == 0)); for (i = 0; i < mcs->channelCount; i++) { + ADDIN_ARGV* arg; rdpMcsChannel* cur = &mcs->channels[i]; + const char* params[1] = { cur->Name }; WLog_INFO(TAG, " %s", cur->Name); + arg = freerdp_addin_argv_new(ARRAYSIZE(params), params); + if (!arg) + return FALSE; + + if (!freerdp_static_channel_collection_add(rdp->settings, arg)) + { + freerdp_addin_argv_free(arg); + return FALSE; + } } + rdp_server_transition_to_state(rdp, CONNECTION_STATE_MCS_CREATE_RESPONSE); if (!mcs_send_connect_response(mcs)) return FALSE; + rdp_server_transition_to_state(rdp, CONNECTION_STATE_MCS_ERECT_DOMAIN); - rdp_server_transition_to_state(rdp, CONNECTION_STATE_MCS_CONNECT); return TRUE; } BOOL rdp_server_accept_mcs_erect_domain_request(rdpRdp* rdp, wStream* s) { + WINPR_ASSERT(rdp); + WINPR_ASSERT(s); + WINPR_ASSERT(rdp_get_state(rdp) == CONNECTION_STATE_MCS_ERECT_DOMAIN); + if (!mcs_recv_erect_domain_request(rdp->mcs, s)) return FALSE; - rdp_server_transition_to_state(rdp, CONNECTION_STATE_MCS_ERECT_DOMAIN); + rdp_server_transition_to_state(rdp, CONNECTION_STATE_MCS_ATTACH_USER); return TRUE; } BOOL rdp_server_accept_mcs_attach_user_request(rdpRdp* rdp, wStream* s) { + rdp_server_transition_to_state(rdp, CONNECTION_STATE_MCS_ATTACH_USER); if (!mcs_recv_attach_user_request(rdp->mcs, s)) return FALSE; + rdp_server_transition_to_state(rdp, CONNECTION_STATE_MCS_ATTACH_USER_CONFIRM); if (!mcs_send_attach_user_confirm(rdp->mcs)) return FALSE; - rdp_server_transition_to_state(rdp, CONNECTION_STATE_MCS_ATTACH_USER); + rdp_server_transition_to_state(rdp, CONNECTION_STATE_MCS_CHANNEL_JOIN_REQUEST); return TRUE; } @@ -1388,19 +1454,28 @@ BOOL rdp_server_accept_mcs_channel_join_request(rdpRdp* rdp, wStream* s) UINT32 i; UINT16 channelId; BOOL allJoined = TRUE; - rdpMcs* mcs = rdp->mcs; + rdpMcs* mcs; - if (!mcs_recv_channel_join_request(mcs, rdp->settings, s, &channelId)) + WINPR_ASSERT(rdp); + WINPR_ASSERT(rdp->context); + + mcs = rdp->mcs; + WINPR_ASSERT(mcs); + + WINPR_ASSERT(rdp_get_state(rdp) == CONNECTION_STATE_MCS_CHANNEL_JOIN_REQUEST); + + if (!mcs_recv_channel_join_request(mcs, rdp->context->settings, s, &channelId)) return FALSE; + rdp_server_transition_to_state(rdp, CONNECTION_STATE_MCS_CHANNEL_JOIN_RESPONSE); if (!mcs_send_channel_join_confirm(mcs, channelId)) return FALSE; if (channelId == mcs->userId) mcs->userChannelJoined = TRUE; - else if (channelId == MCS_GLOBAL_CHANNEL_ID) + if (channelId == MCS_GLOBAL_CHANNEL_ID) mcs->globalChannelJoined = TRUE; - else if (channelId == mcs->messageChannelId) + if (channelId == mcs->messageChannelId) mcs->messageChannelJoined = TRUE; for (i = 0; i < mcs->channelCount; i++) @@ -1415,19 +1490,36 @@ BOOL rdp_server_accept_mcs_channel_join_request(rdpRdp* rdp, wStream* s) if ((mcs->userChannelJoined) && (mcs->globalChannelJoined) && (mcs->messageChannelId == 0 || mcs->messageChannelJoined) && allJoined) - { rdp_server_transition_to_state(rdp, CONNECTION_STATE_RDP_SECURITY_COMMENCEMENT); - } + else + rdp_server_transition_to_state(rdp, CONNECTION_STATE_MCS_CHANNEL_JOIN_REQUEST); return TRUE; } BOOL rdp_server_accept_confirm_active(rdpRdp* rdp, wStream* s, UINT16 pduLength) { - freerdp_peer* peer = rdp->context->peer; + freerdp_peer* peer; - if (rdp_get_state(rdp) != CONNECTION_STATE_CAPABILITIES_EXCHANGE) - return FALSE; + WINPR_ASSERT(rdp); + WINPR_ASSERT(rdp->context); + WINPR_ASSERT(rdp->settings); + WINPR_ASSERT(s); + + peer = rdp->context->peer; + WINPR_ASSERT(peer); + + if (rdp_get_state(rdp) != CONNECTION_STATE_CAPABILITIES_EXCHANGE_CONFIRM_ACTIVE) + { + if (freerdp_settings_get_bool(rdp->settings, FreeRDP_TransportDumpReplay)) + rdp_finalize_set_flag(rdp, FINALIZE_DEACTIVATE_REACTIVATE); + else + { + WLog_WARN(TAG, "Invalid state, got %s, expected %s", rdp_get_state_string(rdp), + rdp_state_string(CONNECTION_STATE_CAPABILITIES_EXCHANGE_CONFIRM_ACTIVE)); + return FALSE; + } + } if (!rdp_recv_confirm_active(rdp, s, pduLength)) return FALSE; @@ -1438,13 +1530,7 @@ BOOL rdp_server_accept_confirm_active(rdpRdp* rdp, wStream* s, UINT16 pduLength) if (rdp->settings->SaltedChecksum) rdp->do_secure_checksum = TRUE; - rdp_server_transition_to_state(rdp, CONNECTION_STATE_FINALIZATION); - - if (!rdp_send_server_synchronize_pdu(rdp)) - return FALSE; - - if (!rdp_send_server_control_cooperate_pdu(rdp)) - return FALSE; + rdp_server_transition_to_state(rdp, CONNECTION_STATE_FINALIZATION_SYNC); return TRUE; } @@ -1462,12 +1548,9 @@ BOOL rdp_server_reactivate(rdpRdp* rdp) if (!rdp_send_deactivate_all(rdp)) return FALSE; - rdp_server_transition_to_state(rdp, CONNECTION_STATE_CAPABILITIES_EXCHANGE); - - if (!rdp_send_demand_active(rdp)) - return FALSE; - - return TRUE; + rdp_finalize_set_flag(rdp, FINALIZE_DEACTIVATE_REACTIVATE); + return rdp_server_transition_to_state(rdp, + CONNECTION_STATE_CAPABILITIES_EXCHANGE_DEMAND_ACTIVE); } BOOL rdp_server_transition_to_state(rdpRdp* rdp, CONNECTION_STATE state) @@ -1488,47 +1571,6 @@ BOOL rdp_server_transition_to_state(rdpRdp* rdp, CONNECTION_STATE state) WLog_DBG(TAG, "%s %s --> %s", __FUNCTION__, rdp_get_state_string(rdp), rdp_state_string(state)); if (!rdp_set_state(rdp, state)) goto fail; - switch (state) - { - case CONNECTION_STATE_CAPABILITIES_EXCHANGE: - break; - - case CONNECTION_STATE_FINALIZATION: - if (!rdp_finalize_reset_flags(rdp, FALSE)) - goto fail; - break; - - case CONNECTION_STATE_ACTIVE: - update_reset_state(rdp->update); - - if (client) - { - if (!client->connected) - { - /** - * PostConnect should only be called once and should not - * be called after a reactivation sequence. - */ - IFCALLRET(client->PostConnect, client->connected, client); - - if (!client->connected) - goto fail; - } - - if (rdp_get_state(rdp) >= CONNECTION_STATE_ACTIVE) - { - IFCALLRET(client->Activate, client->activated, client); - - if (!client->activated) - goto fail; - } - } - - break; - - default: - break; - } status = TRUE; fail: @@ -1560,28 +1602,58 @@ const char* rdp_state_string(CONNECTION_STATE state) return "CONNECTION_STATE_NEGO"; case CONNECTION_STATE_NLA: return "CONNECTION_STATE_NLA"; - case CONNECTION_STATE_MCS_CONNECT: - return "CONNECTION_STATE_MCS_CONNECT"; + case CONNECTION_STATE_MCS_CREATE_REQUEST: + return "CONNECTION_STATE_MCS_CREATE_REQUEST"; + case CONNECTION_STATE_MCS_CREATE_RESPONSE: + return "CONNECTION_STATE_MCS_CREATE_RESPONSE"; case CONNECTION_STATE_MCS_ERECT_DOMAIN: return "CONNECTION_STATE_MCS_ERECT_DOMAIN"; case CONNECTION_STATE_MCS_ATTACH_USER: return "CONNECTION_STATE_MCS_ATTACH_USER"; - case CONNECTION_STATE_MCS_CHANNEL_JOIN: - return "CONNECTION_STATE_MCS_CHANNEL_JOIN"; + case CONNECTION_STATE_MCS_ATTACH_USER_CONFIRM: + return "CONNECTION_STATE_MCS_ATTACH_USER_CONFIRM"; + case CONNECTION_STATE_MCS_CHANNEL_JOIN_REQUEST: + return "CONNECTION_STATE_MCS_CHANNEL_JOIN_REQUEST"; + case CONNECTION_STATE_MCS_CHANNEL_JOIN_RESPONSE: + return "CONNECTION_STATE_MCS_CHANNEL_JOIN_RESPONSE"; case CONNECTION_STATE_RDP_SECURITY_COMMENCEMENT: return "CONNECTION_STATE_RDP_SECURITY_COMMENCEMENT"; case CONNECTION_STATE_SECURE_SETTINGS_EXCHANGE: return "CONNECTION_STATE_SECURE_SETTINGS_EXCHANGE"; - case CONNECTION_STATE_CONNECT_TIME_AUTO_DETECT: - return "CONNECTION_STATE_CONNECT_TIME_AUTO_DETECT"; + case CONNECTION_STATE_CONNECT_TIME_AUTO_DETECT_REQUEST: + return "CONNECTION_STATE_CONNECT_TIME_AUTO_DETECT_REQUEST"; + case CONNECTION_STATE_CONNECT_TIME_AUTO_DETECT_RESPONSE: + return "CONNECTION_STATE_CONNECT_TIME_AUTO_DETECT_RESPONSE"; case CONNECTION_STATE_LICENSING: return "CONNECTION_STATE_LICENSING"; - case CONNECTION_STATE_MULTITRANSPORT_BOOTSTRAPPING: - return "CONNECTION_STATE_MULTITRANSPORT_BOOTSTRAPPING"; - case CONNECTION_STATE_CAPABILITIES_EXCHANGE: - return "CONNECTION_STATE_CAPABILITIES_EXCHANGE"; - case CONNECTION_STATE_FINALIZATION: - return "CONNECTION_STATE_FINALIZATION"; + case CONNECTION_STATE_MULTITRANSPORT_BOOTSTRAPPING_REQUEST: + return "CONNECTION_STATE_MULTITRANSPORT_BOOTSTRAPPING_REQUEST"; + case CONNECTION_STATE_MULTITRANSPORT_BOOTSTRAPPING_RESPONSE: + return "CONNECTION_STATE_MULTITRANSPORT_BOOTSTRAPPING_RESPONSE"; + case CONNECTION_STATE_CAPABILITIES_EXCHANGE_DEMAND_ACTIVE: + return "CONNECTION_STATE_CAPABILITIES_EXCHANGE_DEMAND_ACTIVE"; + case CONNECTION_STATE_CAPABILITIES_EXCHANGE_MONITOR_LAYOUT: + return "CONNECTION_STATE_CAPABILITIES_EXCHANGE_MONITOR_LAYOUT"; + case CONNECTION_STATE_CAPABILITIES_EXCHANGE_CONFIRM_ACTIVE: + return "CONNECTION_STATE_CAPABILITIES_EXCHANGE_CONFIRM_ACTIVE"; + case CONNECTION_STATE_FINALIZATION_SYNC: + return "CONNECTION_STATE_FINALIZATION_SYNC"; + case CONNECTION_STATE_FINALIZATION_COOPERATE: + return "CONNECTION_STATE_FINALIZATION_COOPERATE"; + case CONNECTION_STATE_FINALIZATION_REQUEST_CONTROL: + return "CONNECTION_STATE_FINALIZATION_REQUEST_CONTROL"; + case CONNECTION_STATE_FINALIZATION_PERSISTENT_KEY_LIST: + return "CONNECTION_STATE_FINALIZATION_PERSISTENT_KEY_LIST"; + case CONNECTION_STATE_FINALIZATION_FONT_LIST: + return "CONNECTION_STATE_FINALIZATION_FONT_LIST"; + case CONNECTION_STATE_FINALIZATION_CLIENT_SYNC: + return "CONNECTION_STATE_FINALIZATION_CLIENT_SYNC"; + case CONNECTION_STATE_FINALIZATION_CLIENT_COOPERATE: + return "CONNECTION_STATE_FINALIZATION_CLIENT_COOPERATE"; + case CONNECTION_STATE_FINALIZATION_CLIENT_GRANTED_CONTROL: + return "CONNECTION_STATE_FINALIZATION_CLIENT_GRANTED_CONTROL"; + case CONNECTION_STATE_FINALIZATION_CLIENT_FONT_MAP: + return "CONNECTION_STATE_FINALIZATION_CLIENT_FONT_MAP"; case CONNECTION_STATE_ACTIVE: return "CONNECTION_STATE_ACTIVE"; default: @@ -1679,6 +1751,5 @@ int rdp_client_connect_confirm_active(rdpRdp* rdp, wStream* s) if (freerdp_shall_disconnect_context(rdp->context)) return 0; - rdp_client_transition_to_state(rdp, CONNECTION_STATE_FINALIZATION); return rdp_client_connect_finalize(rdp); } diff --git a/libfreerdp/core/info.c b/libfreerdp/core/info.c index a6d1eee84..3c7dff586 100644 --- a/libfreerdp/core/info.c +++ b/libfreerdp/core/info.c @@ -925,6 +925,8 @@ BOOL rdp_recv_client_info(rdpRdp* rdp, wStream* s) UINT16 channelId; UINT16 securityFlags = 0; + WINPR_ASSERT(rdp_get_state(rdp) == CONNECTION_STATE_SECURE_SETTINGS_EXCHANGE); + if (!rdp_read_header(rdp, s, &length, &channelId)) return FALSE; diff --git a/libfreerdp/core/peer.c b/libfreerdp/core/peer.c index ffda7c665..f1125678a 100644 --- a/libfreerdp/core/peer.c +++ b/libfreerdp/core/peer.c @@ -25,6 +25,7 @@ #include #include "info.h" +#include "display.h" #include "certificate.h" #include @@ -35,6 +36,8 @@ #define TAG FREERDP_TAG("core.peer") +static int peer_recv_pdu(freerdp_peer* client, wStream* s); + static HANDLE freerdp_peer_virtual_channel_open(freerdp_peer* client, const char* name, UINT32 flags) { @@ -437,6 +440,14 @@ static int peer_recv_tpkt_pdu(freerdp_peer* client, wStream* s) if (freerdp_shall_disconnect_context(rdp->context)) return 0; + if (rdp_get_state(rdp) <= CONNECTION_STATE_LICENSING) + { + if (!rdp_read_security_header(s, &securityFlags, NULL)) + return -1; + + return rdp_recv_message_channel_pdu(rdp, s, securityFlags); + } + if (settings->UseRdpSecurityLayer) { if (!rdp_read_security_header(s, &securityFlags, &length)) @@ -507,6 +518,124 @@ static int peer_recv_tpkt_pdu(freerdp_peer* client, wStream* s) return 0; } +static int peer_recv_handle_auto_detect(freerdp_peer* client, wStream* s) +{ + int ret = -1; + rdpRdp* rdp; + rdpSettings* settings; + + WINPR_ASSERT(client); + WINPR_ASSERT(s); + WINPR_ASSERT(client->context); + + rdp = client->context->rdp; + WINPR_ASSERT(rdp); + + settings = rdp->settings; + WINPR_ASSERT(settings); + + if (freerdp_settings_get_bool(settings, FreeRDP_NetworkAutoDetect)) + { + switch (rdp_get_state(rdp)) + { + case CONNECTION_STATE_CONNECT_TIME_AUTO_DETECT_REQUEST: + { + if (autodetect_send_connecttime_rtt_measure_request(rdp->autodetect, 0x23)) + ret = 0; + rdp_server_transition_to_state(rdp, + CONNECTION_STATE_CONNECT_TIME_AUTO_DETECT_RESPONSE); + } + break; + case CONNECTION_STATE_CONNECT_TIME_AUTO_DETECT_RESPONSE: + { + ret = peer_recv_pdu(client, s); + if (ret >= 0) + { + switch (autodetect_get_state(rdp->autodetect)) + { + case AUTODETECT_STATE_COMPLETE: + rdp_server_transition_to_state(rdp, CONNECTION_STATE_LICENSING); + ret = 1; /* Rerun in next state */ + break; + case AUTODETECT_STATE_RESPONSE: + rdp_server_transition_to_state(rdp, CONNECTION_STATE_LICENSING); + ret = 1; /* Rerun in next state */ + break; + default: + break; + } + } + } + break; + default: + WLog_ERR(TAG, "Invalid autodetect state %s", rdp_get_state_string(rdp)); + break; + } + } + else + { + rdp_server_transition_to_state(rdp, CONNECTION_STATE_LICENSING); + + ret = 1; /* Rerun in next state */ + } + + return ret; +} + +static int peer_recv_handle_licensing(freerdp_peer* client, wStream* s) +{ + int ret = -1; + rdpRdp* rdp; + rdpSettings* settings; + + WINPR_ASSERT(client); + WINPR_ASSERT(s); + WINPR_ASSERT(client->context); + + rdp = client->context->rdp; + WINPR_ASSERT(rdp); + + settings = rdp->settings; + WINPR_ASSERT(settings); + + switch (license_get_state(rdp->license)) + { + case LICENSE_STATE_INITIAL: + { + const rdpSettings* settings = rdp->settings; + const BOOL required = + freerdp_settings_get_bool(settings, FreeRDP_ServerLicenseRequired); + + if (required) + { + if (!license_server_configure(rdp->license)) + ret = -3; + else if (!license_server_send_request(rdp->license)) + ret = -2; + else + ret = 0; + } + else + { + if (license_send_valid_client_error_packet(rdp)) + ret = 1; /* Rerun in next state, might be capabilities */ + } + } + break; + case LICENSE_STATE_COMPLETED: + ret = 1; /* Licensing completed, continue in next state */ + break; + case LICENSE_STATE_ABORTED: + ret = -1; + break; + default: + ret = peer_recv_pdu(client, s); + break; + } + + return ret; +} + static int peer_recv_fastpath_pdu(freerdp_peer* client, wStream* s) { rdpRdp* rdp; @@ -548,9 +677,10 @@ static int peer_recv_fastpath_pdu(freerdp_peer* client, wStream* s) return fastpath_recv_inputs(fastpath, s); } -static int peer_recv_pdu(freerdp_peer* client, wStream* s) +int peer_recv_pdu(freerdp_peer* client, wStream* s) { int rc = tpkt_verify_header(s); + if (rc > 0) return peer_recv_tpkt_pdu(client, s); else if (rc == 0) @@ -559,6 +689,14 @@ static int peer_recv_pdu(freerdp_peer* client, wStream* s) return rc; } +static int peer_unexpected_client_message(rdpRdp* rdp, UINT32 flag) +{ + char buffer[1024] = { 0 }; + WLog_WARN(TAG, "Unexpected client message in state %s, missing flag %s", + rdp_get_state_string(rdp), rdp_finalize_flags_to_str(flag, buffer, sizeof(buffer))); + return 0; /* we ignore this as per spec input PDU are already allowed */ +} + static int peer_recv_callback_internal(rdpTransport* transport, wStream* s, void* extra) { UINT32 SelectedProtocol; @@ -581,6 +719,11 @@ static int peer_recv_callback_internal(rdpTransport* transport, wStream* s, void switch (rdp_get_state(rdp)) { case CONNECTION_STATE_INITIAL: + rdp_server_transition_to_state(rdp, CONNECTION_STATE_NEGO); + ret = 1; + break; + + case CONNECTION_STATE_NEGO: if (!rdp_server_accept_nego(rdp, s)) { WLog_ERR(TAG, "%s: %s - rdp_server_accept_nego() fail", __FUNCTION__, @@ -596,8 +739,7 @@ static int peer_recv_callback_internal(rdpTransport* transport, wStream* s, void if (SelectedProtocol & PROTOCOL_HYBRID) { SEC_WINNT_AUTH_IDENTITY* identity = nego_get_identity(rdp->nego); - sspi_CopyAuthIdentity(&client->identity, - (const SEC_WINNT_AUTH_IDENTITY_INFO*)identity); + sspi_CopyAuthIdentity(&client->identity, identity); IFCALLRET(client->Logon, client->authenticated, client, &client->identity, TRUE); nego_free_nla(rdp->nego); @@ -607,11 +749,16 @@ static int peer_recv_callback_internal(rdpTransport* transport, wStream* s, void IFCALLRET(client->Logon, client->authenticated, client, &client->identity, FALSE); } + rdp_server_transition_to_state(rdp, CONNECTION_STATE_MCS_CREATE_REQUEST); ret = 0; } break; - case CONNECTION_STATE_NEGO: + case CONNECTION_STATE_NLA: + WINPR_ASSERT(FALSE); // TODO + break; + + case CONNECTION_STATE_MCS_CREATE_REQUEST: if (!rdp_server_accept_mcs_connect_initial(rdp, s)) { WLog_ERR(TAG, @@ -624,7 +771,7 @@ static int peer_recv_callback_internal(rdpTransport* transport, wStream* s, void break; - case CONNECTION_STATE_MCS_CONNECT: + case CONNECTION_STATE_MCS_ERECT_DOMAIN: if (!rdp_server_accept_mcs_erect_domain_request(rdp, s)) { WLog_ERR(TAG, @@ -637,7 +784,7 @@ static int peer_recv_callback_internal(rdpTransport* transport, wStream* s, void break; - case CONNECTION_STATE_MCS_ERECT_DOMAIN: + case CONNECTION_STATE_MCS_ATTACH_USER: if (!rdp_server_accept_mcs_attach_user_request(rdp, s)) { WLog_ERR(TAG, @@ -650,7 +797,7 @@ static int peer_recv_callback_internal(rdpTransport* transport, wStream* s, void break; - case CONNECTION_STATE_MCS_ATTACH_USER: + case CONNECTION_STATE_MCS_CHANNEL_JOIN_REQUEST: if (!rdp_server_accept_mcs_channel_join_request(rdp, s)) { WLog_ERR(TAG, @@ -693,126 +840,242 @@ static int peer_recv_callback_internal(rdpTransport* transport, wStream* s, void __FUNCTION__, rdp_get_state_string(rdp)); } else + { + rdp_server_transition_to_state(rdp, + CONNECTION_STATE_CONNECT_TIME_AUTO_DETECT_REQUEST); + ret = 1; + } + break; + + case CONNECTION_STATE_CONNECT_TIME_AUTO_DETECT_REQUEST: + case CONNECTION_STATE_CONNECT_TIME_AUTO_DETECT_RESPONSE: + if (settings->EarlyCapabilityFlags & RNS_UD_CS_SUPPORT_NETCHAR_AUTODETECT) + { + ret = peer_recv_handle_auto_detect(client, s); + } + else { rdp_server_transition_to_state(rdp, CONNECTION_STATE_LICENSING); - ret = 2; /* Rerun, NULL stream */ + ret = 1; } break; case CONNECTION_STATE_LICENSING: - { - LicenseCallbackResult res; - - if (!client->LicenseCallback) - { - WLog_ERR(TAG, - "%s: LicenseCallback has been removed, assuming " - "licensing is ok (please fix your app)", - __FUNCTION__); - res = LICENSE_CB_COMPLETED; - } - else - res = client->LicenseCallback(client, s); - - switch (res) - { - case LICENSE_CB_INTERNAL_ERROR: - WLog_ERR(TAG, - "%s: %s - callback internal " - "error, aborting", - __FUNCTION__, rdp_get_state_string(rdp)); - break; - - case LICENSE_CB_ABORT: - break; - - case LICENSE_CB_IN_PROGRESS: - ret = 0; - break; - - case LICENSE_CB_COMPLETED: - rdp_server_transition_to_state(rdp, CONNECTION_STATE_CAPABILITIES_EXCHANGE); - ret = 2; /* Rerun, NULL stream */ - break; - - default: - WLog_ERR(TAG, - "%s: CONNECTION_STATE_LICENSING - unknown license callback " - "result %d", - __FUNCTION__, res); - ret = 0; - break; - } - + ret = peer_recv_handle_licensing(client, s); + if (ret > 0) + rdp_server_transition_to_state( + rdp, CONNECTION_STATE_MULTITRANSPORT_BOOTSTRAPPING_REQUEST); break; - } - case CONNECTION_STATE_CAPABILITIES_EXCHANGE: + case CONNECTION_STATE_MULTITRANSPORT_BOOTSTRAPPING_REQUEST: + if (settings->SupportMultitransport) { - if (client->Capabilities && !client->Capabilities(client)) - { - } - else if (!rdp_send_demand_active(rdp)) - { - WLog_ERR(TAG, - "%s: %s - " - "rdp_send_demand_active() fail", - __FUNCTION__, rdp_get_state_string(rdp)); - } + if (!multitransport_server_send_request(rdp->multitransport)) + ret = -23; else { - if (s) - { - ret = peer_recv_pdu(client, s); - if (ret < 0) - { - WLog_ERR(TAG, - "%s: %s - " - "peer_recv_pdu() fail", - __FUNCTION__, rdp_get_state_string(rdp)); - } - } - else - ret = 0; + rdp_server_transition_to_state( + rdp, CONNECTION_STATE_MULTITRANSPORT_BOOTSTRAPPING_RESPONSE); + ret = 1; } } else { - /** - * During reactivation sequence the client might sent some input or channel data - * before receiving the Deactivate All PDU. We need to process them as usual. - */ - ret = peer_recv_pdu(client, s); - if (ret < 0) - { - WLog_ERR(TAG, - "%s: %s - " - "peer_recv_pdu() fail", - __FUNCTION__, rdp_get_state_string(rdp)); - } + rdp_server_transition_to_state( + rdp, CONNECTION_STATE_CAPABILITIES_EXCHANGE_DEMAND_ACTIVE); + ret = 1; /* Rerun, initialize next state */ } - + break; + case CONNECTION_STATE_MULTITRANSPORT_BOOTSTRAPPING_RESPONSE: + ret = peer_recv_pdu(client, s); break; - case CONNECTION_STATE_FINALIZATION: - ret = peer_recv_pdu(client, s); - if (ret < 0) + case CONNECTION_STATE_CAPABILITIES_EXCHANGE_DEMAND_ACTIVE: + if (client->Capabilities && !client->Capabilities(client)) { - WLog_ERR(TAG, "%s: %s - peer_recv_pdu() fail", __FUNCTION__, + WLog_ERR(TAG, "[%s] freerdp_peer::Capabilities() callback failed", rdp_get_state_string(rdp)); } + else if (!rdp_send_demand_active(rdp)) + { + WLog_ERR(TAG, "[%s] rdp_send_demand_active() fail", rdp_get_state_string(rdp)); + } + else + { + rdp_server_transition_to_state( + rdp, CONNECTION_STATE_CAPABILITIES_EXCHANGE_MONITOR_LAYOUT); + ret = 1; + } + break; + + case CONNECTION_STATE_CAPABILITIES_EXCHANGE_MONITOR_LAYOUT: + if (settings->EarlyCapabilityFlags & RNS_UD_CS_SUPPORT_MONITOR_LAYOUT_PDU) + { + MONITOR_DEF* monitors = NULL; + + IFCALL(client->AdjustMonitorsLayout, client); + + /* client supports the monitorLayout PDU, let's send him the monitors if any */ + ret = -1; + if (freerdp_settings_get_uint32(settings, FreeRDP_MonitorCount) == 0) + { + const UINT32 w = freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth); + const UINT32 h = freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight); + const rdpMonitor primary = { .x = 0, + .y = 0, + .width = w, + .height = h, + .is_primary = TRUE, + .orig_screen = 0, + .attributes = { .physicalWidth = w, + .physicalHeight = h, + .orientation = + ORIENTATION_LANDSCAPE, + .desktopScaleFactor = 100, + .deviceScaleFactor = 100 } }; + if (!freerdp_settings_set_pointer_array(settings, FreeRDP_MonitorDefArray, 0, + &primary)) + ret = -2; + else if (!freerdp_settings_set_uint32(settings, FreeRDP_MonitorCount, 1)) + ret = -3; + } + if (ret < -1) + { + } + else if (!display_convert_rdp_monitor_to_monitor_def( + settings->MonitorCount, settings->MonitorDefArray, &monitors)) + { + } + else if (!freerdp_display_send_monitor_layout(rdp->context, settings->MonitorCount, + monitors)) + { + } + else + ret = 0; + free(monitors); + } + else + ret = 0; + rdp_server_transition_to_state(rdp, + CONNECTION_STATE_CAPABILITIES_EXCHANGE_CONFIRM_ACTIVE); + break; + + case CONNECTION_STATE_CAPABILITIES_EXCHANGE_CONFIRM_ACTIVE: + /** + * During reactivation sequence the client might sent some input or channel data + * before receiving the Deactivate All PDU. We need to process them as usual. + */ + ret = peer_recv_pdu(client, s); + break; + + case CONNECTION_STATE_FINALIZATION_SYNC: + rdp_finalize_reset_flags(rdp, FALSE); + ret = peer_recv_pdu(client, s); + if (rdp_finalize_is_flag_set(rdp, FINALIZE_CS_SYNCHRONIZE_PDU)) + { + if (!rdp_server_transition_to_state(rdp, CONNECTION_STATE_FINALIZATION_COOPERATE)) + ret = -2; + } + else + ret = peer_unexpected_client_message(rdp, FINALIZE_CS_SYNCHRONIZE_PDU); + break; + case CONNECTION_STATE_FINALIZATION_COOPERATE: + ret = peer_recv_pdu(client, s); + if (rdp_finalize_is_flag_set(rdp, FINALIZE_CS_CONTROL_COOPERATE_PDU)) + { + if (!rdp_server_transition_to_state(rdp, + CONNECTION_STATE_FINALIZATION_REQUEST_CONTROL)) + ret = -2; + } + else + ret = peer_unexpected_client_message(rdp, FINALIZE_CS_CONTROL_COOPERATE_PDU); + break; + case CONNECTION_STATE_FINALIZATION_REQUEST_CONTROL: + ret = peer_recv_pdu(client, s); + if (rdp_finalize_is_flag_set(rdp, FINALIZE_CS_CONTROL_REQUEST_PDU)) + { + if (!rdp_server_transition_to_state( + rdp, CONNECTION_STATE_FINALIZATION_PERSISTENT_KEY_LIST)) + ret = -2; + } + else + ret = peer_unexpected_client_message(rdp, FINALIZE_CS_CONTROL_REQUEST_PDU); + break; + case CONNECTION_STATE_FINALIZATION_PERSISTENT_KEY_LIST: + if (freerdp_settings_get_bool(settings, FreeRDP_BitmapCachePersistEnabled) && + !rdp_finalize_is_flag_set(rdp, FINALIZE_DEACTIVATE_REACTIVATE)) + { + ret = peer_recv_pdu(client, s); + + if (rdp_finalize_is_flag_set(rdp, FINALIZE_CS_PERSISTENT_KEY_LIST_PDU)) + { + if (!rdp_server_transition_to_state(rdp, + CONNECTION_STATE_FINALIZATION_FONT_LIST)) + ret = -2; + } + else + ret = peer_unexpected_client_message(rdp, + CONNECTION_STATE_FINALIZATION_FONT_LIST); + } + else + { + if (!rdp_server_transition_to_state(rdp, CONNECTION_STATE_FINALIZATION_FONT_LIST)) + ret = -2; + else + ret = 1; + } + break; + case CONNECTION_STATE_FINALIZATION_FONT_LIST: + ret = peer_recv_pdu(client, s); + if (rdp_finalize_is_flag_set(rdp, FINALIZE_CS_FONT_LIST_PDU)) + { + if (!rdp_server_transition_to_state(rdp, CONNECTION_STATE_ACTIVE)) + ret = -2; + update_reset_state(rdp->update); + } + else + ret = peer_unexpected_client_message(rdp, FINALIZE_CS_FONT_LIST_PDU); break; case CONNECTION_STATE_ACTIVE: - ret = peer_recv_pdu(client, s); - if (ret < 0) + if (!client->connected) { - WLog_ERR(TAG, "%s: %s - peer_recv_pdu() fail", __FUNCTION__, - rdp_get_state_string(rdp)); + /** + * PostConnect should only be called once and should not + * be called after a reactivation sequence. + */ + IFCALLRET(client->PostConnect, client->connected, client); } + if (!client->connected) + { + switch (rdp_get_state(rdp)) + { + case CONNECTION_STATE_CAPABILITIES_EXCHANGE_DEMAND_ACTIVE: + ret = 1; + break; + case CONNECTION_STATE_ACTIVE: + default: + ret = -24; + break; + } + } + else + { + if (!client->activated) + IFCALLRET(client->Activate, client->activated, client); + if (!client->activated) + ret = -23; + else + ret = peer_recv_pdu(client, s); + } break; + /* States that must not happen in server state machine */ + case CONNECTION_STATE_FINALIZATION_CLIENT_SYNC: + case CONNECTION_STATE_FINALIZATION_CLIENT_COOPERATE: + case CONNECTION_STATE_FINALIZATION_CLIENT_GRANTED_CONTROL: + case CONNECTION_STATE_FINALIZATION_CLIENT_FONT_MAP: default: WLog_ERR(TAG, "%s state %d", rdp_get_state_string(rdp), rdp_get_state(rdp)); break; @@ -831,9 +1094,6 @@ static int peer_recv_callback(rdpTransport* transport, wStream* s, void* extra) default: rc = peer_recv_callback_internal(transport, s, extra); break; - case 2: - rc = peer_recv_callback_internal(transport, NULL, extra); - break; } } while (rc > 0); diff --git a/libfreerdp/core/rdp.c b/libfreerdp/core/rdp.c index 1c82fb206..25b7f2816 100644 --- a/libfreerdp/core/rdp.c +++ b/libfreerdp/core/rdp.c @@ -1617,29 +1617,31 @@ static int rdp_recv_pdu(rdpRdp* rdp, wStream* s) return rc; } -int rdp_recv_callback(rdpTransport* transport, wStream* s, void* extra) +/* TODO: Need to properly return: + * + * -24 ... Rerun state machine, ignore wStream* s reset + * -23 ... Rerun state machine, reset wStream* s + * -1 ... Failure + * 0 ... Success + * 2 ... State ACTIVE + */ +static int rdp_recv_callback_int(rdpTransport* transport, wStream* s, void* extra) { + const UINT32 mask = FINALIZE_SC_SYNCHRONIZE_PDU | FINALIZE_SC_CONTROL_COOPERATE_PDU | + FINALIZE_SC_CONTROL_GRANTED_PDU | FINALIZE_SC_FONT_MAP_PDU; int status = 0; rdpRdp* rdp = (rdpRdp*)extra; WINPR_ASSERT(transport); WINPR_ASSERT(rdp); WINPR_ASSERT(s); - /* - * At any point in the connection sequence between when all - * MCS channels have been joined and when the RDP connection - * enters the active state, an auto-detect PDU can be received - * on the MCS message channel. - */ - if ((rdp_get_state(rdp) > CONNECTION_STATE_MCS_CHANNEL_JOIN) && - (rdp_get_state(rdp) < CONNECTION_STATE_ACTIVE)) - { - if (rdp_client_connect_auto_detect(rdp, s)) - return 0; - } switch (rdp_get_state(rdp)) { + case CONNECTION_STATE_NEGO: + rdp_client_transition_to_state(rdp, CONNECTION_STATE_MCS_CREATE_REQUEST); + status = -24; + break; case CONNECTION_STATE_NLA: if (nla_get_state(rdp->nla) < NLA_STATE_AUTH_INFO) { @@ -1647,7 +1649,7 @@ int rdp_recv_callback(rdpTransport* transport, wStream* s, void* extra) { WLog_ERR(TAG, "%s: %s - nla_recv_pdu() fail", __FUNCTION__, rdp_get_state_string(rdp)); - return -1; + status = -1; } } else if (nla_get_state(rdp->nla) == NLA_STATE_POST_NEGO) @@ -1658,92 +1660,113 @@ int rdp_recv_callback(rdpTransport* transport, wStream* s, void* extra) { WLog_ERR(TAG, "%s: %s - nego_recv() fail", __FUNCTION__, rdp_get_state_string(rdp)); - return -1; + status = -1; } - - if (!nla_set_state(rdp->nla, NLA_STATE_FINAL)) - return -1; + else if (!nla_set_state(rdp->nla, NLA_STATE_FINAL)) + status = -1; } - if (nla_get_state(rdp->nla) == NLA_STATE_AUTH_INFO) + if (status >= 0) { - transport_set_nla_mode(rdp->transport, FALSE); - - if (rdp->settings->VmConnectMode) + if (nla_get_state(rdp->nla) == NLA_STATE_AUTH_INFO) { - if (!nego_set_state(rdp->nego, NEGO_STATE_NLA)) - return -1; + transport_set_nla_mode(rdp->transport, FALSE); - if (!nego_set_requested_protocols(rdp->nego, PROTOCOL_HYBRID | PROTOCOL_SSL)) - return -1; - - nego_send_negotiation_request(rdp->nego); - - if (!nla_set_state(rdp->nla, NLA_STATE_POST_NEGO)) - return -1; - } - else - { - if (!nla_set_state(rdp->nla, NLA_STATE_FINAL)) - return -1; + if (rdp->settings->VmConnectMode) + { + if (!nego_set_state(rdp->nego, NEGO_STATE_NLA)) + status = -1; + else if (!nego_set_requested_protocols(rdp->nego, + PROTOCOL_HYBRID | PROTOCOL_SSL)) + status = -1; + else + { + if (!nego_send_negotiation_request(rdp->nego)) + status = -1; + else if (!nla_set_state(rdp->nla, NLA_STATE_POST_NEGO)) + status = -1; + } + } + else + { + if (!nla_set_state(rdp->nla, NLA_STATE_FINAL)) + status = -1; + } } } - - if (nla_get_state(rdp->nla) == NLA_STATE_FINAL) + if (status >= 0) { - nla_free(rdp->nla); - rdp->nla = NULL; - rdp_client_transition_to_state(rdp, CONNECTION_STATE_MCS_CONNECT); - if (!mcs_client_begin(rdp->mcs)) + if (nla_get_state(rdp->nla) == NLA_STATE_FINAL) { - WLog_ERR(TAG, "%s: %s - mcs_client_begin() fail", __FUNCTION__, - rdp_get_state_string(rdp)); - return -1; + rdp_client_transition_to_state(rdp, CONNECTION_STATE_MCS_CREATE_REQUEST); + status = -23; } } - break; - case CONNECTION_STATE_MCS_CONNECT: + case CONNECTION_STATE_MCS_CREATE_REQUEST: + if (!mcs_client_begin(rdp->mcs)) + { + WLog_ERR(TAG, "%s: %s - mcs_client_begin() fail", __FUNCTION__, + rdp_get_state_string(rdp)); + status = -1; + } + else + { + rdp_client_transition_to_state(rdp, CONNECTION_STATE_MCS_CREATE_RESPONSE); + } + break; + + case CONNECTION_STATE_MCS_CREATE_RESPONSE: if (!mcs_recv_connect_response(rdp->mcs, s)) { WLog_ERR(TAG, "mcs_recv_connect_response failure"); - return -1; + status = -1; } - - if (!mcs_send_erect_domain_request(rdp->mcs)) + else { - WLog_ERR(TAG, "mcs_send_erect_domain_request failure"); - return -1; + rdp_client_transition_to_state(rdp, CONNECTION_STATE_MCS_ERECT_DOMAIN); + if (!mcs_send_erect_domain_request(rdp->mcs)) + { + WLog_ERR(TAG, "mcs_send_erect_domain_request failure"); + status = -1; + } + else + { + rdp_client_transition_to_state(rdp, CONNECTION_STATE_MCS_ATTACH_USER); + if (!mcs_send_attach_user_request(rdp->mcs)) + { + WLog_ERR(TAG, "mcs_send_attach_user_request failure"); + status = -1; + } + else + rdp_client_transition_to_state(rdp, + CONNECTION_STATE_MCS_ATTACH_USER_CONFIRM); + } } - - if (!mcs_send_attach_user_request(rdp->mcs)) - { - WLog_ERR(TAG, "mcs_send_attach_user_request failure"); - return -1; - } - - rdp_client_transition_to_state(rdp, CONNECTION_STATE_MCS_ATTACH_USER); break; - case CONNECTION_STATE_MCS_ATTACH_USER: + case CONNECTION_STATE_MCS_ATTACH_USER_CONFIRM: if (!mcs_recv_attach_user_confirm(rdp->mcs, s)) { WLog_ERR(TAG, "mcs_recv_attach_user_confirm failure"); - return -1; + status = -1; } - - if (!mcs_send_channel_join_request(rdp->mcs, rdp->mcs->userId)) + else { - WLog_ERR(TAG, "mcs_send_channel_join_request failure"); - return -1; + rdp_client_transition_to_state(rdp, CONNECTION_STATE_MCS_CHANNEL_JOIN_REQUEST); + if (!mcs_send_channel_join_request(rdp->mcs, rdp->mcs->userId)) + { + WLog_ERR(TAG, "mcs_send_channel_join_request failure"); + status = -1; + } + else + rdp_client_transition_to_state(rdp, CONNECTION_STATE_MCS_CHANNEL_JOIN_RESPONSE); } - - rdp_client_transition_to_state(rdp, CONNECTION_STATE_MCS_CHANNEL_JOIN); break; - case CONNECTION_STATE_MCS_CHANNEL_JOIN: + case CONNECTION_STATE_MCS_CHANNEL_JOIN_RESPONSE: if (!rdp_client_connect_mcs_channel_join_confirm(rdp, s)) { WLog_ERR(TAG, @@ -1755,6 +1778,14 @@ int rdp_recv_callback(rdpTransport* transport, wStream* s, void* extra) break; + case CONNECTION_STATE_CONNECT_TIME_AUTO_DETECT_REQUEST: + if (!rdp_client_connect_auto_detect(rdp, s)) + { + rdp_client_transition_to_state(rdp, CONNECTION_STATE_LICENSING); + status = -23; + } + break; + case CONNECTION_STATE_LICENSING: status = rdp_client_connect_license(rdp, s); @@ -1764,7 +1795,16 @@ int rdp_recv_callback(rdpTransport* transport, wStream* s, void* extra) break; - case CONNECTION_STATE_CAPABILITIES_EXCHANGE: + case CONNECTION_STATE_MULTITRANSPORT_BOOTSTRAPPING_REQUEST: + if (!rdp_client_connect_auto_detect(rdp, s)) + { + rdp_client_transition_to_state( + rdp, CONNECTION_STATE_CAPABILITIES_EXCHANGE_DEMAND_ACTIVE); + status = -23; + } + break; + + case CONNECTION_STATE_CAPABILITIES_EXCHANGE_DEMAND_ACTIVE: status = rdp_client_connect_demand_active(rdp, s); if (status < 0) @@ -1772,23 +1812,102 @@ int rdp_recv_callback(rdpTransport* transport, wStream* s, void* extra) "%s: %s - " "rdp_client_connect_demand_active() - %i", __FUNCTION__, rdp_get_state_string(rdp), status); - + else + { + if (!rdp->settings->SupportMonitorLayoutPdu) + { + rdp_client_transition_to_state( + rdp, CONNECTION_STATE_CAPABILITIES_EXCHANGE_CONFIRM_ACTIVE); + status = -23; + } + else + { + rdp_client_transition_to_state( + rdp, CONNECTION_STATE_CAPABILITIES_EXCHANGE_MONITOR_LAYOUT); + } + } break; - case CONNECTION_STATE_FINALIZATION: + case CONNECTION_STATE_CAPABILITIES_EXCHANGE_MONITOR_LAYOUT: status = rdp_recv_pdu(rdp, s); - - if ((status >= 0) && rdp_finalize_is_flag_set(rdp, FINALIZE_SC_COMPLETE)) + if (status >= 0) { - rdp_client_transition_to_state(rdp, CONNECTION_STATE_ACTIVE); - return 2; + status = -23; + rdp_client_transition_to_state( + rdp, CONNECTION_STATE_CAPABILITIES_EXCHANGE_CONFIRM_ACTIVE); + } + break; + + case CONNECTION_STATE_CAPABILITIES_EXCHANGE_CONFIRM_ACTIVE: + status = rdp_client_connect_confirm_active(rdp, s); + break; + + case CONNECTION_STATE_FINALIZATION_CLIENT_SYNC: + { + const UINT32 flags = rdp->finalize_sc_pdus & mask; + status = rdp_recv_pdu(rdp, s); + if (status >= 0) + { + const UINT32 uflags = rdp->finalize_sc_pdus & mask; + if (flags != uflags) + rdp_client_transition_to_state(rdp, + CONNECTION_STATE_FINALIZATION_CLIENT_COOPERATE); + else + status = -1; + } + } + break; + case CONNECTION_STATE_FINALIZATION_CLIENT_COOPERATE: + { + const UINT32 flags = rdp->finalize_sc_pdus & mask; + status = rdp_recv_pdu(rdp, s); + if (status >= 0) + { + const UINT32 uflags = rdp->finalize_sc_pdus & mask; + if (flags != uflags) + rdp_client_transition_to_state( + rdp, CONNECTION_STATE_FINALIZATION_CLIENT_GRANTED_CONTROL); + else + status = -1; + } + } + break; + case CONNECTION_STATE_FINALIZATION_CLIENT_GRANTED_CONTROL: + { + const UINT32 flags = rdp->finalize_sc_pdus & mask; + status = rdp_recv_pdu(rdp, s); + if (status >= 0) + { + const UINT32 uflags = rdp->finalize_sc_pdus & mask; + if (flags != uflags) + rdp_client_transition_to_state(rdp, + CONNECTION_STATE_FINALIZATION_CLIENT_FONT_MAP); + else + status = -1; + } + } + break; + case CONNECTION_STATE_FINALIZATION_CLIENT_FONT_MAP: + { + const UINT32 flags = rdp->finalize_sc_pdus & mask; + status = rdp_recv_pdu(rdp, s); + if (status >= 0) + { + const UINT32 uflags = rdp->finalize_sc_pdus & mask; + if (flags == uflags) + WLog_WARN(TAG, "Did not receive a FINALIZE_SC_FONT_MAP_PDU"); + + { + rdp_client_transition_to_state(rdp, CONNECTION_STATE_ACTIVE); + status = 2; + } } if (status < 0) WLog_DBG(TAG, "%s: %s - rdp_recv_pdu() - %i", __FUNCTION__, rdp_get_state_string(rdp), status); - - break; + } + break; case CONNECTION_STATE_ACTIVE: status = rdp_recv_pdu(rdp, s); @@ -1806,9 +1925,26 @@ int rdp_recv_callback(rdpTransport* transport, wStream* s, void* extra) break; } + if (status < 0 && status > -23) + { + WLog_ERR(TAG, "%s: %s status %d", __FUNCTION__, rdp_get_state_string(rdp), status); + } return status; } +int rdp_recv_callback(rdpTransport* transport, wStream* s, void* extra) +{ + int rc = -1; + const size_t start = Stream_GetPosition(s); + do + { + if (rc == -23) + Stream_SetPosition(s, start); + rc = rdp_recv_callback_int(transport, s, extra); + } while (rc <= -23); + return rc; +} + BOOL rdp_send_channel_data(rdpRdp* rdp, UINT16 channelId, const BYTE* data, size_t size) { return freerdp_channel_send(rdp, channelId, data, size);