WFAPI disconnect sessions that exceed certain idle time [override policy]

/*
Author - Siva Mulpuru [sivamulpuru.com]
Usage - WFAPI "maxIdleTimeInMin" "GatewaySNIP"
Comment - Workaround for Citrix Virtal Apps [FKA XenApp] 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);
                }
            } 
        }
    }
}

Leave a comment

Your email address will not be published.