16

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;
    }
Community
  • 1
  • 1
Daro
  • 1,990
  • 2
  • 16
  • 22

1 Answers1

10

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;
marsh-wiggle
  • 2,508
  • 3
  • 35
  • 52
Mitch
  • 21,223
  • 6
  • 63
  • 86
  • 2
    This works as described, but I've had to make one change: the struct 'DomainControllerInfo' should've been a class. The method 'Marshal.PtrToStructure' mandated it. – Herman Cordes Feb 05 '15 at 11:22
  • This is the only solution I've found that actually works without a load of monkeying around. In my case, I have a `DirectoryEntry` and from that I need to work out the netbios name. So before I use this code, I take the distinguished name, calculate a DNS name from the `DC=a,DC=b,DC=c...` bit (yielding `"a.b.c"` in this case), and then feed that to the `DsGetDcName` method. Works like a charm. – Andras Zoltan Jan 17 '23 at 02:11
  • 1
    @AndrasZoltan, if you already have the computer object's `DirectoryEntry` (`objectClass=computer`) them this is overkill. Read the `sAMAccountName` property/attribute. This solution applies when all you have is the FQDN as a string. – Mitch Jan 18 '23 at 17:36
  • Unfortunately we have customer environments where sAMAccountName isn't reliable (or used). We're trying to reverse engineer a user's old-style login name. – Andras Zoltan Feb 12 '23 at 00:34
  • @AndrasZoltan, if it works - great. `sAMAccountName` [is documented to match the NETBIOS name](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-ada3/56d7e9e4-1505-4d9a-b968-3bf0d6b92809), though. Testing shows this to be the case on both AD and Samba. The FQDN [is documented as `dNSHostName`](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-ada1/a7b140ad-06f1-420d-90b7-704f689e0032) - munging the LDAP DN is not recommended. I'm not sure what you mean by "old-style login name" - but feel free to link to a new question. – Mitch Feb 12 '23 at 17:18