15
votes

Getting the NETBIOS domain name from a fully qualified Active Directory domain name is sometimes a tedious task. I found a good answer here.

In an environment with multiple forests this approach will however not work if the PC is not in the forest you are querying. This is because LDAP://RootDSE will query information for the computer’s domain.

Some might ask: why so complicated? Just use the name before the first dot retrieved by:

ActiveDirectory.Domain.GetComputerDomain().Name;

Or just get the user's domain name:

Environment.GetEnvironmentVariable("USERDOMAIN");

or

Environment.UserDomainName;

BUT the NETBIOS domain name can be something completely different, and you or your computer might be in a different domain or forest! So this approach is usable only in a simple environment.

DJ KRAZE’s solution needs only one small modification to allow cross domain queries. This assumes a trust relationship!

private string GetNetbiosDomainName(string dnsDomainName)
{
      string netbiosDomainName = string.Empty;

      DirectoryEntry rootDSE = new DirectoryEntry(string.Format("LDAP://{0}/RootDSE",dnsDomainName));

      string configurationNamingContext = rootDSE.Properties["configurationNamingContext"][0].ToString();

      DirectoryEntry searchRoot = new DirectoryEntry("LDAP://cn=Partitions," + configurationNamingContext);

      DirectorySearcher searcher = new DirectorySearcher(searchRoot);
      searcher.SearchScope = SearchScope.OneLevel;
      searcher.PropertiesToLoad.Add("netbiosname");
      searcher.Filter = string.Format("(&(objectcategory=Crossref)(dnsRoot={0})(netBIOSName=*))", dnsDomainName);

      SearchResult result = searcher.FindOne();

      if (result != null)
      {
        netbiosDomainName = result.Properties["netbiosname"][0].ToString();
      }

      return netbiosDomainName;
    }
1
You should write this in Q&A format, or (possibly) your improved answer should go with the original question.Roger Lipscombe
My edit request was rejected because "The edit changes too much in the original post"Daro
Post it as a separate answer. Someone with more rep should be able to merge them later.Roger Lipscombe
Another good reason why the NetBIOS crap should be burried right now and forever and let DNS do that finally.Michael-O

1 Answers

9
votes

You can also use the DsGetDcName API, which will do all the monkeying around for you. It will also cache calls and not even hit the network if the domain you are querying is the local computer.

If you have additional requirements on the capabilities of the

Use:

internal static string GetNetbiosNameForDomain(string dns)
{
    IntPtr pDomainInfo;
    int result = DsGetDcName(null, dns, IntPtr.Zero, null,
        DSGETDCNAME_FLAGS.DS_IS_DNS_NAME | DSGETDCNAME_FLAGS.DS_RETURN_FLAT_NAME,
        out pDomainInfo);
    try
    {
        if (result != ERROR_SUCCESS)
            throw new Win32Exception(result);

        var dcinfo = new DomainControllerInfo();
        Marshal.PtrToStructure(pDomainInfo, dcinfo);

        return dcinfo.DomainName;
    }
    finally
    {
        if (pDomainInfo != IntPtr.Zero)
            NetApiBufferFree(pDomainInfo);
    }
}

P/Invoke:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private class 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;