Jump to content
  • 0

XenApp disconnect idle session for AGW and non AGW connections


Siva Mulpuru

Question

Hi community, is there a why to have different session idle timeout values for Gateway connections and non gateway connections for the same XenApp server? 

 

i understand "Server idle timer interval" is a citrix computer policy, wondering if AGW can override this value? 

 

Basically looking to have AGW connections  to have short idle time and internal clients to have longer idle time. 

 

Thoughts?

 

 

Link to comment

7 answers to this question

Recommended Posts

  • 0

I think I have the same question: 

 

We've got a XenApp farm and use published applications. 
Applications are delivered inhouse and also via a netscaler gateway and the storefront website. 

When working inhouse no idle timeouts should be configured because the users work with company clients that have a password protected screensaver configured by GPO. 
But when a user starts a published application from outside via the netscaler gateway (e.g. in an internet café) we'd like to have a session idle timeout. 
The storefront website timeout is working, but a once started application won't be disconnected if the user just leaves it open.
The Remote Desktop Session Idle Timeout GPO and the Citrix Policy "Server idle timer intervall" are only for computer configuration, not user based.
That means the idle timeout would also apply to inhouse users. 
Is there a way to set this kind of timeout only for the users that are connected over the netscaler gateway?

 

I already opened a case at Citrix CVAD support regarding this issue but they couldn't offer a solution.

 

Thanks and best regards, 
Stefan

Link to comment
  • 0

hi

 

In XenApp 7.x, the Session idle timer and Session idle timer interval are user settings.

These can be controlled via Citrix Policies in Studio, and applied via Access Control to specify either NetScaler-connected sessions or not.

You will need to have Universal licenses to allow this, and make sure you have smart access enabled on the NetScaler (i.e. ensure that 'ICA Only' isn't ticked on the VIP).

 

create two new Citrix Policies, containing different idle timer settings

Assign different access policies for each - one that applies if they match a NetScaler access policy, and one that doesn't

 

Regards

 

Ken Z

User Session idle timer.png

Assign Policy.png

Link to comment
  • 0

Unfortunately these policies only apply to Desktop OS and not to Server OS, thus they are not working with XenApp. 

I already tried that by myself several times and it has also been confirmed by Citrix support yesterday, who tried it in their lab.

The policy setting "Server idle timer intervall" is working (also confirmed in a lab by Citrix yesterday), but that one can only be applied to servers and you can't separate between inhouse connections and gateway connections. 

Link to comment
  • 0

How about setting a GPO with the following

 

User configuration > Policies > Administrative Templates > Windows Components > Remote Desktop Services > Remote Desktop Session Host > Session Time Limits

          Set time limit for active but idle Remote Desktop Services sessions

          End session when time limits are reached

 

Then try and get a WMI Filter that can determine the source address of the connection (NetScaler SNIP)? I know that WMI Filters are normally based on the target computer, but maybe someone knows a way to reverse that? Who's an expert in writing WMI filters? 

 

Regards

 

Ken Z

Link to comment
  • 0

Thanks, Ken.

I thought about that, too, and I guess that would work if we could figure out via WMI if a user is connected through Netscaler. 

Unfortunately the client IP address isn't the appropriate filter.

Would be great if someone had an idea how to determine in a WMI filter if a user is connected through Netscaler!

 

Cheers, 

Stefan

 

btw: The same GPO setting exists under "computer configuration" and that one has precedence over the "user configuration", so if anyone else wants to try it ensure that it's not configured there, too.

 

Link to comment
  • 0

Stefan and Ken, thanks for posting your thoughts. 

 

I read through WFAPI SDK and could come up with a workaround using C# utility that would run on a schedule basis. Code below if interested.

 

Session idle time logic based on LastInputTime of WF_SESSION_TIME struct. Compiled for 64 bit CPU.

 

/*
Author - Siva Mulpuru [www.sivamulpuru.com]
Usage - WFAPI "maxIdleTimeInMin" "GatewaySNIP"
Comment - Workaround for Citrix Virtal Apps [FKA XemApp] to disconnect AGW connections when exceed passed value. Designed to run as scheduled job. 
          Allows non AGW connections to use serverIdleTimeout policy while providing override for AGW connections.
 
 */

using System;
using System.Runtime.InteropServices;

namespace WFAPI
{
    class Program
    {
        [DllImport("wfapi.dll", CharSet = CharSet.Unicode, SetLastError = true)]
        public static extern bool WFQuerySessionInformation(System.IntPtr hServer, int sessionId, WF_INFO_CLASS WFInfoClass, out System.IntPtr ppBuffer, out uint pBytesReturned);

        [DllImport("wfapi.dll", ExactSpelling = true, SetLastError = false)]
        public static extern void WFFreeMemory(IntPtr memory);

        [DllImport("wfapi.dll", ExactSpelling = true, SetLastError = false)]
        public static extern bool WFDisconnectSession(System.IntPtr hServer, int sessionId, bool bWait);

        [DllImport("wfapi.dll", CharSet = CharSet.Unicode, SetLastError = true)]
        public static extern bool WFEnumerateSessions(IntPtr hServer, 
            [MarshalAs(UnmanagedType.U4)] Int32 Reserved,  
            [MarshalAs(UnmanagedType.U4)] Int32 Version,  
            ref IntPtr pSessionInfo,  
            [MarshalAs(UnmanagedType.U4)] 
            ref Int32 pCount);   

        public enum WF_INFO_CLASS    
        {
            WFVersion,             
            WFInitialProgram,
            WFWorkingDirectory,
            WFOEMId,
            WFSessionId,
            WFUserName,
            WFWinStationName,
            WFDomainName,
            WFConnectState,
            WFClientBuildNumber,
            WFClientName,
            WFClientDirectory,
            WFClientProductId,
            WFClientHardwareId,
            WFClientAddress,
            WFClientDisplay,
            WFClientCache,
            WFClientDrives,
            WFICABufferLength,
            WFLicenseEnabler,
            RESERVED2,
            WFApplicationName,
            WFVersionEx,
            WFClientInfo,
            WFUserInfo,
            WFAppInfo,
            WFClientLatency,
            WFSessionTime,
            WFLicensingModel
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct WF_SESSION_TIME
        {
            public long ConnectTime;
            public long DisconnectTime;
            public long LastInputTime;
            public long LogonTime;
            public long CurrentTime;
        }

        public enum  WF_CONNECTSTATE_CLASS
        {
            WFActive,              // User logged on to WinStation
            WFConnected,           // WinStation connected to client
            WFConnectQuery,        // In the process of connecting to client
            WFShadow,              // Shadowing another WinStation
            WFDisconnected,        // WinStation logged on without client
            WFIdle,                // Waiting for client to connect
            WFListen,              // WinStation is listening for connection
            WFReset,               // WinStation is being reset
            WFDown,                // WinStation is down due to error
            WFInit                 // WinStation in initialization
        }
        
        [StructLayout(LayoutKind.Sequential)]
        public struct WF_CLIENT_ADDRESS
        {
            public int AddressFamily;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
            public byte[] Address;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct WF_SESSION_INFO
        {
            public Int32 SessionID;
            [MarshalAs(UnmanagedType.LPStr)]
            public String pWinStationName;
            public WF_CONNECTSTATE_CLASS State;
        }
        static DateTime getSessionLastInteractionTime(int sessionid)
        {
            IntPtr buffer = IntPtr.Zero;
            DateTime lastInteractionTime = DateTime.Now;
            uint bytesReturned = 0;
            try
            {
                if (WFQuerySessionInformation(IntPtr.Zero, sessionid, WF_INFO_CLASS.WFSessionTime, out buffer, out bytesReturned))
                {
                    WF_SESSION_TIME WFSessionTime = (WF_SESSION_TIME)Marshal.PtrToStructure(buffer, typeof(WF_SESSION_TIME));
                    lastInteractionTime = DateTime.FromFileTime(WFSessionTime.LastInputTime);
                }
            }
            finally
            {
                WFFreeMemory(buffer);                
            }
            return lastInteractionTime;
        }
        
        static string getClientIP(int sessionid)
        {
            IntPtr buffer = IntPtr.Zero;
            uint bytesReturned;
            string clientIPAddress = "0.0.0.0";
            try
            {
                if (WFQuerySessionInformation(IntPtr.Zero, sessionid, WF_INFO_CLASS.WFClientAddress, out buffer, out bytesReturned))
                {

                    WF_CLIENT_ADDRESS si = (WF_CLIENT_ADDRESS)Marshal.PtrToStructure((System.IntPtr)buffer, typeof(WF_CLIENT_ADDRESS));
                    clientIPAddress = si.Address[2] + "." + si.Address[3] + "." + si.Address[4] + "." + si.Address[5];
                }
            }
            finally
            {
                WFFreeMemory(buffer);
            }
            return clientIPAddress;
        }
        
        static void Main(string[] args)
        {
            int maxIdleTimeInMin = Convert.ToInt32(args[0]);
            string gatewaySNIP = args[1];
            //Console.WriteLine("{0} {1}", maxIdleTimeInMin, gatewaySNIP);
            IntPtr pSessionInfo = IntPtr.Zero;
            int Count = 0;
            if (WFEnumerateSessions(IntPtr.Zero,0,1,ref pSessionInfo,ref Count))
            {
                try
                {
                    Int32 dataSize = Marshal.SizeOf(typeof(WF_SESSION_INFO));
                    Int64 current = (int)pSessionInfo;
                    for (int i = 0; i < Count; i++)
                    {
                        WF_SESSION_INFO si = (WF_SESSION_INFO)Marshal.PtrToStructure((System.IntPtr)current, typeof(WF_SESSION_INFO));
                        current += dataSize;                        
                        if (si.State == WF_CONNECTSTATE_CLASS.WFActive)
                        {
                            //Console.WriteLine("{0} {1} {2}", si.SessionID, getSessionLastInteractionTime(si.SessionID), getClientIP(si.SessionID));                            
                            if(gatewaySNIP.CompareTo(getClientIP(si.SessionID)) == 0 && (DateTime.Now - getSessionLastInteractionTime(si.SessionID)).TotalMinutes > maxIdleTimeInMin)
                            {
                               bool result = WFDisconnectSession(IntPtr.Zero, si.SessionID, false);
                               Console.WriteLine("{0} {1} is {2}", "Disconnect result for SessionID", si.SessionID, result);
                            }
                        }
                    }
                }
                finally
                {
                    WFFreeMemory(pSessionInfo);
                }
            } 
        }
    }
}

 

 

Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...