Beispiel #1
0
        public void ProcessExited(int exitCode)
        {
            Trace.TraceInformation("Received host client process exit notification, remote session {0}", _remoteSessionManager.RemoteSession.Id);

            try
            {
                // remote session is now disconnected
                _remoteSessionManager.RemoteSession.State = RemoteSessionState.Disconnected;

                // process exit code
                _remoteSessionManager.RemoteSession.ExitCode = exitCode;

                // stop monitoring the remote session owner activity, if enabled
                if (_remoteSessionManager.ClientIdleTimeout != null)
                {
                    _remoteSessionManager.ClientIdleTimeout.Cancel();
                    _remoteSessionManager.ClientIdleTimeout.Dispose();
                }

                // release the communication pipes, if any
                if (_remoteSessionManager.Pipes != null)
                {
                    _remoteSessionManager.Pipes.DeletePipes();
                }

                // if the remote session is marked for reconnection, reconnect it
                if (_remoteSessionManager.RemoteSession.Reconnect)
                {
                    _remoteSessionManager.RemoteSession.Reconnect     = false;
                    _remoteSessionManager.RemoteSession.BrowserResize = null;
                    _remoteSessionManager.HostClient.ProcessStarted   = false;
                    if (_remoteSessionManager.ClientIdleTimeout != null)
                    {
                        _remoteSessionManager.ClientIdleTimeout = new CancellationTokenSource();
                    }
                    _remoteSessionManager.RemoteSession.State = RemoteSessionState.Connecting;
                    _remoteSessionManager.SendMessage(new RemoteSessionMessage {
                        Type = MessageType.Disconnected, Prefix = "disconnected"
                    });
                }
                // otherwise, redirect to the login page (or the hosts dashboard in enterprise mode)
                else
                {
                    // CAUTION! exit code list is not exhaustive (that's why RemoteSession.ExitCode is an int)
                    RemoteSessionExitCode _exitCode;
                    if (!Enum.TryParse(_remoteSessionManager.RemoteSession.ExitCode.ToString(), out _exitCode))
                    {
                        _exitCode = RemoteSessionExitCode.Unknown;
                    }

                    // if using a connection service, send the connection state and exit code
                    if (_remoteSessionManager.RemoteSession.ConnectionService)
                    {
                        _connectionClient.SetConnectionState(
                            _remoteSessionManager.RemoteSession.Id,
                            string.IsNullOrEmpty(_remoteSessionManager.RemoteSession.VMAddress) ? _remoteSessionManager.RemoteSession.ServerAddress : _remoteSessionManager.RemoteSession.VMAddress,
                            GuidHelper.ConvertFromString(_remoteSessionManager.RemoteSession.VMGuid),
                            _remoteSessionManager.RemoteSession.State);

                        _connectionClient.SetConnectionExitCode(
                            _remoteSessionManager.RemoteSession.Id,
                            string.IsNullOrEmpty(_remoteSessionManager.RemoteSession.VMAddress) ? _remoteSessionManager.RemoteSession.ServerAddress : _remoteSessionManager.RemoteSession.VMAddress,
                            GuidHelper.ConvertFromString(_remoteSessionManager.RemoteSession.VMGuid),
                            _exitCode);
                    }

                    CleanupDisconnectedSession(_exitCode);
                }
            }
            catch (Exception exc)
            {
                Trace.TraceError("Failed to handle session disconnect, remote session {0} ({1})", _remoteSessionManager.RemoteSession.Id, exc);
                throw;
            }
        }
Beispiel #2
0
        /// <summary>
        /// page load (postback data is now available)
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void Page_Load(
            object sender,
            EventArgs e)
        {
            // client ip protection
            if (_clientIPTracking)
            {
                var clientIP = ClientIPHelper.ClientIPFromRequest(new HttpContextWrapper(HttpContext.Current).Request, true, new string[] { });
                if (Session[HttpSessionStateVariables.ClientIP.ToString()] == null)
                {
                    Session[HttpSessionStateVariables.ClientIP.ToString()] = clientIP;
                }
                else if (!((string)Session[HttpSessionStateVariables.ClientIP.ToString()]).Equals(clientIP))
                {
                    System.Diagnostics.Trace.TraceWarning("Failed to validate the client ip");
                    _authorizedRequest = false;
                    UpdateControls();
                    return;
                }
            }

            // session spoofing protection
            if (_httpSessionUseUri)
            {
                if (Request.Cookies[HttpRequestCookies.ClientKey.ToString()] == null)
                {
                    if (Session[HttpSessionStateVariables.ClientKey.ToString()] == null || _allowShareSessionUrl)
                    {
                        var cookie = new HttpCookie(HttpRequestCookies.ClientKey.ToString());
                        cookie.Value = Guid.NewGuid().ToString();
                        cookie.Path  = "/";
                        Response.Cookies.Add(cookie);
                    }
                    else
                    {
                        System.Diagnostics.Trace.TraceWarning("Failed to validate the client key: missing key");
                        _authorizedRequest = false;
                        UpdateControls();
                        return;
                    }
                }
                else
                {
                    var clientKey = Request.Cookies[HttpRequestCookies.ClientKey.ToString()].Value;
                    if (Session[HttpSessionStateVariables.ClientKey.ToString()] == null)
                    {
                        Session[HttpSessionStateVariables.ClientKey.ToString()] = clientKey;
                    }
                    else if (!((string)Session[HttpSessionStateVariables.ClientKey.ToString()]).Equals(clientKey) && !_allowShareSessionUrl)
                    {
                        System.Diagnostics.Trace.TraceWarning("Failed to validate the client key: key mismatch");
                        _authorizedRequest = false;
                        UpdateControls();
                        return;
                    }
                }
            }

            // retrieve the active enterprise session, if any
            if (Session[HttpSessionStateVariables.EnterpriseSession.ToString()] != null)
            {
                try
                {
                    _enterpriseSession = (EnterpriseSession)Session[HttpSessionStateVariables.EnterpriseSession.ToString()];
                }
                catch (Exception exc)
                {
                    System.Diagnostics.Trace.TraceError("Failed to retrieve the active enterprise session ({0})", exc);
                }
            }

            // retrieve the active remote session, if any
            if (Session[HttpSessionStateVariables.RemoteSession.ToString()] != null)
            {
                try
                {
                    RemoteSession = (RemoteSession)Session[HttpSessionStateVariables.RemoteSession.ToString()];

                    // if using a connection service, send the connection state
                    if (Session.SessionID.Equals(RemoteSession.OwnerSessionID) && RemoteSession.ConnectionService)
                    {
                        _connectionClient.SetConnectionState(RemoteSession.Id, string.IsNullOrEmpty(RemoteSession.VMAddress) ? RemoteSession.ServerAddress : RemoteSession.VMAddress, GuidHelper.ConvertFromString(RemoteSession.VMGuid), RemoteSession.State);
                    }

                    if (RemoteSession.State == RemoteSessionState.Disconnected)
                    {
                        // if connecting from a login page or url, show any connection failure into a dialog box
                        // otherwise, this is delegated to the connection API used and its related UI
                        if (_loginEnabled)
                        {
                            // handle connection failure
                            var script = string.Format("handleRemoteSessionExit({0});", RemoteSession.ExitCode);

                            // redirect to login page
                            if (!string.IsNullOrEmpty(_loginUrl))
                            {
                                script += string.Format("window.location.href = '{0}';", _loginUrl);
                            }

                            ClientScript.RegisterClientScriptBlock(GetType(), Guid.NewGuid().ToString(), script, true);
                        }

                        // cleanup
                        Session[HttpSessionStateVariables.RemoteSession.ToString()] = null;
                        if (Session[HttpSessionStateVariables.GuestInfo.ToString()] != null)
                        {
                            Session[HttpSessionStateVariables.GuestInfo.ToString()] = null;
                        }
                        RemoteSession = null;
                    }
                }
                catch (Exception exc)
                {
                    System.Diagnostics.Trace.TraceError("Failed to retrieve the active remote session ({0})", exc);
                }
            }
            // retrieve a shared remote session from url, if any
            else if (!string.IsNullOrEmpty(Request["gid"]))
            {
                var guestId = Guid.Empty;
                if (Guid.TryParse(Request["gid"], out guestId))
                {
                    var sharingInfo = GetSharingInfo(guestId);
                    if (sharingInfo != null)
                    {
                        Session[HttpSessionStateVariables.RemoteSession.ToString()] = sharingInfo.RemoteSession;
                        Session[HttpSessionStateVariables.GuestInfo.ToString()]     = sharingInfo.GuestInfo;

                        try
                        {
                            // remove the shared session guid from url
                            Response.Redirect("~/", true);
                        }
                        catch (ThreadAbortException)
                        {
                            // occurs because the response is ended after redirect
                        }
                    }
                }
            }

            if (_httpSessionUseUri)
            {
                // if running myrtille into an iframe, the iframe url is registered (into a cookie) after the remote session is connected
                // this is necessary to prevent a new http session from being generated for the iframe if the page is reloaded, due to the missing http session id into the iframe url (!)
                // multiple iframes (on the same page), like multiple connections/tabs, requires cookieless="UseUri" for sessionState into web.config

                // problem is, there can be many cases where the cookie is not removed after the remote session is disconnected (network issue, server down, etc?)
                // if the page is reloaded, the iframe will use it's previously registered http session... which may not exist anymore or have its active remote session disconnected
                // if that happens, unregister the iframe url (from the cookie) and reload the page; that will provide a new connection identifier to the iframe and reconnect it

                if (!string.IsNullOrEmpty(Request["fid"]) && RemoteSession == null)
                {
                    if (Request.Cookies[Request["fid"]] != null)
                    {
                        // remove the cookie for the given iframe
                        Response.Cookies[Request["fid"]].Expires = DateTime.Now.AddDays(-1);

                        // reload the page
                        ClientScript.RegisterClientScriptBlock(GetType(), Guid.NewGuid().ToString(), "parent.location.href = parent.location.href;", true);
                    }
                }
            }

            // local admin
            if (_enterpriseSession == null && RemoteSession == null && _enterpriseClient.GetMode() == EnterpriseMode.Local && !string.IsNullOrEmpty(Request["mode"]) && Request["mode"].Equals("admin"))
            {
                _localAdmin = true;
            }

            // postback events may redirect after execution; UI is updated from there
            if (!IsPostBack)
            {
                UpdateControls();
            }

            // disable the browser cache; in addition to a "noCache" dummy param, with current time, on long-polling and xhr requests
            Response.Cache.SetCacheability(HttpCacheability.NoCache);
            Response.Cache.SetNoStore();
        }
        public void ProcessExited(int exitCode)
        {
            Trace.TraceInformation("Received host client process exit notification, remote session {0}", _remoteSessionManager.RemoteSession.Id);

            try
            {
                // remote session is now disconnected
                _remoteSessionManager.RemoteSession.State = RemoteSessionState.Disconnected;

                // process exit code
                _remoteSessionManager.RemoteSession.ExitCode = exitCode;

                // stop monitoring the remote session owner activity, if enabled
                if (_remoteSessionManager.ClientIdleTimeout != null)
                {
                    _remoteSessionManager.ClientIdleTimeout.Cancel();
                    _remoteSessionManager.ClientIdleTimeout.Dispose();
                }

                // release the communication pipes, if any
                if (_remoteSessionManager.Pipes != null)
                {
                    _remoteSessionManager.Pipes.DeletePipes();
                }

                // if the remote session is marked for reconnection, reconnect it
                if (_remoteSessionManager.RemoteSession.Reconnect)
                {
                    _remoteSessionManager.RemoteSession.Reconnect     = false;
                    _remoteSessionManager.RemoteSession.BrowserResize = null;
                    _remoteSessionManager.HostClient.ProcessStarted   = false;
                    if (_remoteSessionManager.ClientIdleTimeout != null)
                    {
                        _remoteSessionManager.ClientIdleTimeout = new CancellationTokenSource();
                    }
                    _remoteSessionManager.RemoteSession.State = RemoteSessionState.Connecting;
                }

                // send a disconnect notification to the browser
                _remoteSessionManager.SendMessage(new RemoteSessionMessage {
                    Type = MessageType.Disconnected, Prefix = "disconnected"
                });

                if (_remoteSessionManager.RemoteSession.State == RemoteSessionState.Disconnected)
                {
                    // if using a connection service, send the connection state and exit code
                    if (_remoteSessionManager.RemoteSession.ConnectionService)
                    {
                        _connectionClient.SetConnectionState(
                            _remoteSessionManager.RemoteSession.Id,
                            string.IsNullOrEmpty(_remoteSessionManager.RemoteSession.VMAddress) ? _remoteSessionManager.RemoteSession.ServerAddress : _remoteSessionManager.RemoteSession.VMAddress,
                            GuidHelper.ConvertFromString(_remoteSessionManager.RemoteSession.VMGuid),
                            _remoteSessionManager.RemoteSession.State);

                        // CAUTION! exit code list is not exhaustive (that's why RemoteSession.ExitCode is an int)
                        RemoteSessionExitCode _exitCode;
                        if (!Enum.TryParse(_remoteSessionManager.RemoteSession.ExitCode.ToString(), out _exitCode))
                        {
                            _exitCode = RemoteSessionExitCode.Unknown;
                        }

                        _connectionClient.SetConnectionExitCode(
                            _remoteSessionManager.RemoteSession.Id,
                            string.IsNullOrEmpty(_remoteSessionManager.RemoteSession.VMAddress) ? _remoteSessionManager.RemoteSession.ServerAddress : _remoteSessionManager.RemoteSession.VMAddress,
                            GuidHelper.ConvertFromString(_remoteSessionManager.RemoteSession.VMGuid),
                            _exitCode);
                    }

                    // cleanup
                    try
                    {
                        _application.Lock();

                        // unregister the remote session at the application level
                        var remoteSessions = (IDictionary <Guid, RemoteSession>)_application[HttpApplicationStateVariables.RemoteSessions.ToString()];

                        if (remoteSessions.ContainsKey(_remoteSessionManager.RemoteSession.Id))
                        {
                            remoteSessions.Remove(_remoteSessionManager.RemoteSession.Id);
                        }

                        // retrieve the remote session guest(s)
                        var guests         = new List <SharingInfo>();
                        var sharedSessions = (IDictionary <Guid, SharingInfo>)_application[HttpApplicationStateVariables.SharedRemoteSessions.ToString()];
                        foreach (var sharingInfo in sharedSessions.Values)
                        {
                            if (sharingInfo.RemoteSession.Id.Equals(_remoteSessionManager.RemoteSession.Id))
                            {
                                guests.Add(sharingInfo);
                            }
                        }

                        // remove them
                        foreach (var guest in guests)
                        {
                            if (guest.GuestInfo.Active && guest.HttpSession != null)
                            {
                                if (guest.HttpSession[HttpSessionStateVariables.RemoteSession.ToString()] != null)
                                {
                                    guest.HttpSession.Remove(HttpSessionStateVariables.RemoteSession.ToString());
                                }

                                if (guest.HttpSession[HttpSessionStateVariables.GuestInfo.ToString()] != null)
                                {
                                    guest.HttpSession.Remove(HttpSessionStateVariables.GuestInfo.ToString());
                                }

                                if (guest.RemoteSession.ActiveGuests > 0)
                                {
                                    guest.RemoteSession.ActiveGuests--;
                                }
                            }
                            sharedSessions.Remove(guest.GuestInfo.Id);
                        }

                        // recycle the application pool when there is no active remote session
                        bool idleAppPoolRecycling;
                        if (!bool.TryParse(ConfigurationManager.AppSettings["IdleAppPoolRecycling"], out idleAppPoolRecycling))
                        {
                            idleAppPoolRecycling = true;
                        }

                        /*
                         *
                         * it may seem a bit extreme, but the garbage collector doesn't return the freed memory to the operating system
                         * instead, it makes it available to the memory workspace of the application pool process, which in turn uses it for faster memory allocation later
                         * while this is fine for most usage, this becomes critical when the OS is under memory pressure
                         * if that occurs, the process is meant to return its unused memory to the OS
                         * in reality, this is not always true; so the system becomes slow (hdd swap) and unstable
                         *
                         * memory usage of a process under Windows: https://dzone.com/articles/windows-process-memory-usage-demystified
                         * tool: https://technet.microsoft.com/en-us/sysinternals/vmmap.aspx
                         * garbage collector: https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/fundamentals
                         * reallocation of freed memory: https://stackoverflow.com/questions/28614210/why-doesnt-net-release-unused-memory-back-to-os-when-physical-95
                         *
                         */

                        if (idleAppPoolRecycling && remoteSessions.Count == 0)
                        {
                            // give time for the browser to receive the disconnect notification and get back to the login page
                            // the browser window/tab may also be closed, but there is no way to know it at this step
                            Thread.Sleep(2000);

                            // the gateway doesn't have enough rights to recycle the application pool, this is delegated to the myrtille services
                            _applicationPoolClient.RecycleApplicationPool(Environment.UserName);
                        }
                    }
                    catch (Exception exc)
                    {
                        Trace.TraceError("Failed to delete remote session ({0})", exc);
                    }
                    finally
                    {
                        _application.UnLock();
                    }

                    // the remote session is still bound to an http session
                    // if the related page is still active, the page will end the cleanup (unbind the remote session from the http session)
                    // otherwise (the browser window/tab was closed), the cleanup will be done automatically when the http session expires
                }
            }
            catch (Exception exc)
            {
                Trace.TraceError("Failed to cleanup disconnected session, remote session {0} ({1})", _remoteSessionManager.RemoteSession.Id, exc);
                throw;
            }
        }