Using DsAddressToSiteNamesEx in C# sample: How to get the site and subnet names from an IP Address of a machine

The DsAddressToSiteNamesEx function obtains the site and subnet names corresponding to the addresses specified.

Sample usage: How to get the site and subnet names from an IP Address of a machine

[DllImport("Netapi32.dll", CallingConvention = CallingConvention.StdCall, EntryPoint = "DsAddressToSiteNamesEx", CharSet = CharSet.Unicode)]
        private static extern int DsAddressToSiteNamesEx(
            [In] string computerName,
            [In] Int32 EntryCount,
            [In] SOCKET_ADDRESS[] SocketAddresses,
            [Out] out IntPtr SiteNames,
            [Out] out IntPtr SubnetNames);

[DllImport("Netapi32.dll")]
private static extern int NetApiBufferFree([In] IntPtr buffer);

private const int ERROR_SUCCESS = 0;

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
internal struct SockAddr_In
{
    public Int16 sin_family;
    public Int16 sin_port;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
    public byte[] sin_addr;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
    public byte[] sin_zero;
}

[StructLayout(LayoutKind.Sequential)]
internal struct SOCKET_ADDRESS
{
    public IntPtr lpSockaddr;
    public Int32  iSockaddrLength;
}

static void Main(string[] args)
{
	string sIP = "10.251.50.101";//@ip = 10.251.50.101
	string sDC = "dc-fr-001";//the name of a domain controller
	IntPtr pSiteNames;
	IntPtr pSubnetNames;
	SOCKET_ADDRESS[] SocketAddresses = new SOCKET_ADDRESS[1];

	String[] ipArray = sIP.Split('.');

	SockAddr_In sockAddr;
	sockAddr.sin_family = 2;
	sockAddr.sin_port = 0;
	sockAddr.sin_addr = new byte[4] { byte.Parse(ipArray[0]), byte.Parse(ipArray[1]), byte.Parse(ipArray[2]), byte.Parse(ipArray[3]) };
	sockAddr.sin_zero = new byte[8] { 0, 0, 0, 0, 0, 0, 0, 0 };

	SocketAddresses[0].iSockaddrLength = Marshal.SizeOf(sockAddr);

	IntPtr pSockAddr = Marshal.AllocHGlobal(16);
	Marshal.StructureToPtr(sockAddr, pSockAddr, true);
	SocketAddresses[0].lpSockaddr = pSockAddr;

	if (DsAddressToSiteNamesEx(
			sDC,
			1,//EntryCount
			SocketAddresses,
			out pSiteNames,
			out pSubnetNames) == ERROR_SUCCESS)
	{
		string sSite = Marshal.PtrToStringAuto(Marshal.ReadIntPtr(pSiteNames, 0));
		string sSubnet = Marshal.PtrToStringAuto(Marshal.ReadIntPtr(pSubnetNames, 0));

		Console.WriteLine("Found site: " + sSite);
		Console.WriteLine("Found subnet: " + sSubnet);
		NetApiBufferFree(pSubnetNames);
		NetApiBufferFree(pSiteNames);
	}
}

Using DsGetSiteName in C# sample: how to get the name of the site where a computer resides?

The DsGetSiteName function returns the name of the site where a computer resides. The site in which the computer resides (as reported by a domain controller) is stored in the computer registry in HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\ Netlogon\Parameters.

The client IP address is used by the domain controller to retrieve the corresponding subnet that it matches in the Subnets container.
Then the name of the site is retrived from the subnet-to-site mapping.

If the client’s IP address does not match a subnet range of any of the subnets stored in Active Directory, the dclocator will randomly pick a site to use.

Sample usage to get the name of the site where a computer resides:

[DllImport("netapi32.dll", CharSet = CharSet.Auto)]
private static extern int DsGetSiteName(string ComputerName, out IntPtr SiteName);

[DllImport("Netapi32.dll", CharSet = CharSet.Unicode, SetLastError = true, CallingConvention = CallingConvention.StdCall)]
public static extern int NetApiBufferFree(IntPtr dwBuffer);

private const int ERROR_SUCCESS = 0;

static void Main(string[] args)
{
	IntPtr pSiteInfo;
	String sSiteName;

	if (DsGetSiteName(
			string.Empty,//ComputerName
			out pSiteInfo) == ERROR_SUCCESS)
	{
		sSiteName = Marshal.PtrToStringAuto(pSiteInfo);
		NetApiBufferFree(pSiteInfo);
		Console.WriteLine("Found workstaton's site Name: " + sSiteName);
	}
}

You can also use a command-line interface for the same purpose:

nltest /server:<HostName> /DsGetSite

Using DsGetDcName in C# sample: How to get a Global Catalog

The directory service functions provide a utility for locating a domain controller (DC) in a Windows domain.
The DsGetDcName function (implemented by the Netlogon service) returns the name of a domain controller in a specified domain

Sample usage:

How to get a Global Catalog

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct DomainControllerInfo
{
	public string DomainControllerName;
	public string DomainControllerAddress;
	public int DomainControllerAddressType;
	public Guid DomainGuid;
	public string DomainName;
	public string DnsForestName;
	public int Flags;
	public string DcSiteName;
	public string ClientSiteName;
}

[Flags]
private enum DSGETDCNAME_FLAGS : uint
{
	DS_FORCE_REDISCOVERY = 0x00000001,
	DS_DIRECTORY_SERVICE_REQUIRED = 0x00000010,
	DS_DIRECTORY_SERVICE_PREFERRED = 0x00000020,
	DS_GC_SERVER_REQUIRED = 0x00000040,
	DS_PDC_REQUIRED = 0x00000080,
	DS_BACKGROUND_ONLY = 0x00000100,
	DS_IP_REQUIRED = 0x00000200,
	DS_KDC_REQUIRED = 0x00000400,
	DS_TIMESERV_REQUIRED = 0x00000800,
	DS_WRITABLE_REQUIRED = 0x00001000,
	DS_GOOD_TIMESERV_PREFERRED = 0x00002000,
	DS_AVOID_SELF = 0x00004000,
	DS_ONLY_LDAP_NEEDED = 0x00008000,
	DS_IS_FLAT_NAME = 0x00010000,
	DS_IS_DNS_NAME = 0x00020000,
	DS_RETURN_DNS_NAME = 0x40000000,
	DS_RETURN_FLAT_NAME = 0x80000000
}

[DllImport("Netapi32.dll", CallingConvention = CallingConvention.StdCall, EntryPoint = "DsGetDcNameW", CharSet = CharSet.Unicode)]
private static extern int DsGetDcName(
	[In] string computerName,
	[In] string domainName,
	[In] IntPtr domainGuid,
	[In] string siteName,
	[In] DSGETDCNAME_FLAGS flags,
	[Out] out IntPtr domainControllerInfo);

[DllImport("Netapi32.dll")]
private static extern int NetApiBufferFree(
	[In] IntPtr buffer);

private const int ERROR_SUCCESS = 0;

static void Main(string[] args)
{
	IntPtr pDomainInfo;
	if (DsGetDcName(
			string.Empty,//ComputerName
			"contoso.com",//DomainName
			IntPtr.Zero,//DomainGuid
			string.Empty,//SiteName
			DSGETDCNAME_FLAGS.DS_DIRECTORY_SERVICE_REQUIRED |
			DSGETDCNAME_FLAGS.DS_GC_SERVER_REQUIRED |
			DSGETDCNAME_FLAGS.DS_IS_DNS_NAME |
			DSGETDCNAME_FLAGS.DS_RETURN_DNS_NAME,
			out pDomainInfo) == ERROR_SUCCESS)
	{
		DomainControllerInfo dci = new DomainControllerInfo();
		dci = (DomainControllerInfo)Marshal.PtrToStructure(pDomainInfo, typeof(DomainControllerInfo));
		NetApiBufferFree(pDomainInfo);
		pDomainInfo = IntPtr.Zero;
		Console.WriteLine("Found Global catalog DC: " + dci.DomainControllerName);
	}
}

How to check if a user is member of a specific group with C#

The following code will find out if a specific user is member of a specific group in the Active Directory.

static void Main(string[] args)
{
	string group = "LDAP://cn=sales,ou=groups,ou=west,dc=contoso,dc=com";
	string member = "LDAP://cn=Jim Smith,ou=users,ou=west,dc=contoso,dc=com";

	Console.WriteLine("Determining if " + member + " is a member of the " + group + " Group...");

	using (DirectoryEntry deMember = new DirectoryEntry(member),
						  deGroup = new DirectoryEntry(group))
	{
		bool isGroupMember = (bool)deGroup.Invoke("IsMember", new object[] { deMember.Path });
		Console.WriteLine(member + (isGroupMember ? " is" : " is not") + " a member of the " + group + " Group...");
	}
}

How Can I Get a List of All the Domains in a Forest

Here’s a sample C# code that searches for all the domain objects.
Because we want to search the global catalog we need to use the global catalog provider. Thus instead of specifying LDAP: in our binding string we specify GC.

static void Main(string[] args)
{
	DirectoryEntry rootDSE = new DirectoryEntry("LDAP://RootDSE");
	String gcPath = rootDSE.Properties["rootDomainNamingContext"].Value.ToString();
	DirectoryEntry gcRoot = new DirectoryEntry("GC://" + gcPath);

	DirectorySearcher ds = new DirectorySearcher();
	ds.SearchRoot = gcRoot;
	ds.SearchScope = SearchScope.OneLevel;
	ds.PageSize = 200;
	ds.CacheResults = false;
	ds.ClientTimeout = TimeSpan.FromSeconds(60);
	ds.Filter = "(&(objectClass=domain))";
	ds.PropertiesToLoad.Add("DC");
	ds.PropertiesToLoad.Add("distinguishedName");

	SearchResultCollection src = ds.FindAll();
	foreach (SearchResult result in src)
	{
		if (result.Properties.Contains("DC"))
		{
			Console.WriteLine((string)result.Properties["distinguishedName"][0]);
		}
	}
}

With .NET Framework 4.5 there is a very simple way to get all domains in the forest by using the class System.DirectoryServices.ActiveDirectory.Forest

static void Main(string[] args)
{
	using (Forest forest = Forest.GetCurrentForest())
	{
		Console.WriteLine("Forest:  {0}", forest.Name);
		foreach (Domain domain in forest.Domains)
		{
			Console.WriteLine(String.Format("Domain:  {0}", domain.Name));
			domain.Dispose();
		}
	}
}