/*
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);
}
}
}
}
}