Updated RDP state machine

* More detailed states
* Better transition checks
* No more recursive calling of state machine functions
This commit is contained in:
akallabeth 2022-10-21 10:31:14 +02:00 committed by Martin Fleisz
parent 06c2ab76e0
commit 33827cb920
8 changed files with 777 additions and 299 deletions

View File

@ -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)

View File

@ -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)

View File

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

View File

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

View File

@ -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);
}

View File

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

View File

@ -25,6 +25,7 @@
#include <winpr/winsock.h>
#include "info.h"
#include "display.h"
#include "certificate.h"
#include <freerdp/log.h>
@ -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);

View File

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