diff --git a/client/common/client.c b/client/common/client.c index 0192c8f91..8c010755b 100644 --- a/client/common/client.c +++ b/client/common/client.c @@ -88,6 +88,7 @@ static void set_default_callbacks(freerdp* instance) instance->PresentGatewayMessage = client_cli_present_gateway_message; instance->LogonErrorInfo = client_cli_logon_error_info; instance->GetAccessToken = client_cli_get_access_token; + instance->RetryDialog = client_common_retry_dialog; } static BOOL freerdp_client_common_new(freerdp* instance, rdpContext* context) @@ -1189,6 +1190,48 @@ cleanup: #endif } +SSIZE_T client_common_retry_dialog(freerdp* instance, const char* what, size_t current, + void* userarg) +{ + WINPR_UNUSED(instance); + WINPR_ASSERT(instance->context); + WINPR_UNUSED(userarg); + WINPR_ASSERT(instance); + WINPR_ASSERT(what); + + if (strcmp(what, "arm-transport") != 0) + { + WLog_ERR(TAG, "Unknown module %s, aborting", what); + return -1; + } + + if (current == 0) + WLog_INFO(TAG, "[%s] Starting your VM. It may take up to 5 minutes", what); + + const rdpSettings* settings = instance->context->settings; + const BOOL enabled = freerdp_settings_get_bool(settings, FreeRDP_AutoReconnectionEnabled); + if (!enabled) + { + WLog_WARN(TAG, "Automatic reconnection disabled, terminating. Try to connect again later"); + return -1; + } + + const size_t max = freerdp_settings_get_uint32(settings, FreeRDP_AutoReconnectMaxRetries); + const size_t delay = freerdp_settings_get_uint32(settings, FreeRDP_TcpConnectTimeout); + if (current >= max) + { + WLog_ERR(TAG, + "[%s] retries exceeded. Your VM failed to start. Try again later or contact your " + "tech support for help if this keeps happening.", + what); + return -1; + } + + WLog_INFO(TAG, "[%s] retry %" PRIuz "/%" PRIuz ", delaying %" PRIuz "ms before next attempt", + what, current, max, delay); + return delay; +} + BOOL client_auto_reconnect(freerdp* instance) { return client_auto_reconnect_ex(instance, NULL); diff --git a/include/freerdp/client.h b/include/freerdp/client.h index 3b72b615b..3960f8cad 100644 --- a/include/freerdp/client.h +++ b/include/freerdp/client.h @@ -175,6 +175,9 @@ extern "C" FREERDP_API BOOL client_common_get_access_token(freerdp* instance, const char* request, char** token); + FREERDP_API SSIZE_T client_common_retry_dialog(freerdp* instance, const char* what, + size_t current, void* userarg); + FREERDP_API void freerdp_client_OnChannelConnectedEventHandler(void* context, const ChannelConnectedEventArgs* e); diff --git a/include/freerdp/freerdp.h b/include/freerdp/freerdp.h index a6d567bf0..81156cec9 100644 --- a/include/freerdp/freerdp.h +++ b/include/freerdp/freerdp.h @@ -136,6 +136,20 @@ extern "C" typedef BOOL (*pGetAccessToken)(freerdp* instance, AccessTokenType tokenType, char** token, size_t count, ...); + /** @brief Callback used to inform about a reconnection attempt + * + * @param instance The instance the information is for + * @param what A '\0' terminated string describing the module attempting to retry an operation + * @param current The current reconnection attempt, the first attempt will always have the + * value \b 0 + * @param userarg An optional custom argument + * + * @return \b -1 in case of failure (attempts exceeded, ...) or a \b delay in [ms] to wait + * before the next attempt + */ + typedef SSIZE_T (*pRetryDialog)(freerdp* instance, const char* what, size_t current, + void* userarg); + /** @brief Callback used if user interaction is required to accept * an unknown certificate. * @@ -531,7 +545,9 @@ owned by rdpRdp */ ALIGN64 pGetAccessToken GetAccessToken; /* (offset 71) Callback for obtaining an access token for \b AccessTokenType authentication */ - UINT64 paddingE[80 - 72]; /* 72 */ + ALIGN64 pRetryDialog RetryDialog; /* (offset 72) Callback for displaying a dialog in case of + something needs a retry */ + UINT64 paddingE[80 - 73]; /* 73 */ }; struct rdp_channel_handles diff --git a/libfreerdp/core/gateway/arm.c b/libfreerdp/core/gateway/arm.c index 45f2f94ea..d52de4d4e 100644 --- a/libfreerdp/core/gateway/arm.c +++ b/libfreerdp/core/gateway/arm.c @@ -56,7 +56,6 @@ #include #endif -#include #include struct rdp_arm @@ -73,9 +72,6 @@ typedef struct rdp_arm rdpArm; #define TAG FREERDP_TAG("core.gateway.arm") #ifdef WITH_AAD -static const UINT32 max_retries = 60; -static const UINT32 code_execution_interval = 10000; - static BOOL arm_tls_connect(rdpArm* arm, rdpTls* tls, int timeout) { WINPR_ASSERT(arm); @@ -839,22 +835,8 @@ static BOOL arm_handle_bad_request(rdpArm* arm, const HttpResponse* response, BO if (strcmp(gateway_code_str->valuestring, "E_PROXY_ORCHESTRATION_LB_SESSIONHOST_DEALLOCATED") == 0) { - if (arm->gateway_retry >= max_retries) - { - WLog_ERR(TAG, "We couldn’t connect because your VM failed to start. Try again later or " - "contact your tech support for help if this keeps happening."); - goto fail; - } *retry = TRUE; - - if (arm->gateway_retry == 0) - { - WLog_INFO(TAG, "Starting your VM. It may take up to 5 minutes"); - } - else - { - WLog_INFO(TAG, "Waiting for remote PC"); - } + WLog_DBG(TAG, "Starting your VM. It may take up to 5 minutes"); } else { @@ -959,13 +941,21 @@ BOOL arm_resolve_endpoint(rdpContext* context, DWORD timeout) BOOL rc = FALSE; do { + if (retry && rc) + { + freerdp* instance = context->instance; + WINPR_ASSERT(instance); + const SSIZE_T delay = IFCALLRESULT(-1, instance->RetryDialog, instance, "arm-transport", + arm->gateway_retry, arm); + arm->gateway_retry++; + if (delay > 0) + { + WLog_DBG(TAG, "Delay for %" PRIdz "ms before next attempt", delay); + Sleep(delay); + } + } rc = arm_handle_request(arm, &retry, timeout); - if (retry) - { - WLog_INFO(TAG, "Delay for %" PRIu32 "ms before next attempt"); - Sleep(code_execution_interval); - } } while (retry && rc); arm_free(arm); return rc;