Search code examples
c++cwinapiwindows-10domaincontroller

Get Domain Controller Name


Summary: I want to get the name of the Domain Controller using just WinAPI, the computer is not on a network which might make my code fail with "NERR_DCNotFound"?

I am trying to get the name of the Domain Controller that a networked computer would be part of.

The computer I am experimenting with is just a personal computer, I never setup a Domain Controller or anything which might make testing difficult? At the moment it is inside of a VirtualBox, airgapped from the internet.

When I run this from command line I get the following output, which appears to contain a Domain Name:

C:\Users\lone>gpresult /Z

Microsoft (R) Windows (R) Operating System Group Policy Result tool v2.0
© 2016 Microsoft Corporation. All rights reserved.

Created on 31/01/2018 at 12:24:12


RSOP data for DESKTOP-I53IU6C\lone on DESKTOP-I53IU6C : Logging Mode
---------------------------------------------------------------------

OS Configuration:            Standalone Workstation
OS Version:                  10.0.10586
Site Name:                   N/A
Roaming Profile:             N/A
Local Profile:               C:\Users\lone
Connected over a slow link?: No


USER SETTINGS
--------------

    Last time Group Policy was applied: 31/01/2018 at 10:30:38
    Group Policy was applied from:      N/A
    Group Policy slow link threshold:   500 kbps
    Domain Name:                        DESKTOP-I53IU6C
    Domain Type:                        <Local Computer>

    Applied Group Policy Objects
    -----------------------------
        N/A

    The following GPOs were not applied because they were filtered out
    -------------------------------------------------------------------
        Local Group Policy
            Filtering:  Not Applied (Empty)

    The user is a part of the following security groups
    ---------------------------------------------------
        None
        Everyone
        Local account and member of Administrators group
        HelpLibraryUpdaters
        BUILTIN\Administrators
        BUILTIN\Users
        NT AUTHORITY\INTERACTIVE
        CONSOLE LOGON
        NT AUTHORITY\Authenticated Users
        This Organization
        Local account
        LOCAL
        NTLM Authentication
        High Mandatory Level

    The user has the following security privileges
    ----------------------------------------------


    Resultant Set Of Policies for User
    -----------------------------------

        Software Installations
        ----------------------
            N/A

        Logon Scripts
        -------------
            N/A

        Logoff Scripts
        --------------
            N/A

        Public Key Policies
        -------------------
            N/A

        Administrative Templates
        ------------------------
            N/A

        Folder Redirection
        ------------------
            N/A

        Internet Explorer Browser User Interface
        ----------------------------------------
            N/A

        Internet Explorer Connection
        ----------------------------
            N/A

        Internet Explorer URLs
        ----------------------
            N/A

        Internet Explorer Security
        --------------------------
            N/A

        Internet Explorer Programs
        --------------------------
            N/A

I get this from the above output:

Domain Name: DESKTOP-I53IU6C

Maybe I am getting Domain Name confused with the Domain Controller Name?

I copy and pasted the code from the MSDN tutorial:

#ifndef UNICODE
#define UNICODE
#endif

#include <stdio.h>
#include <stdlib.h>  // for _wtoi function
#include <assert.h>
#include <windows.h>
#include <lm.h>

// Need to link with netapi32.lib
#pragma comment(lib, "netapi32.lib")

int wmain(int argc, wchar_t * argv[])
{

    NET_API_STATUS nStatus;

    LPCWSTR lpServer = NULL;
    LPCWSTR lpDomain = NULL;

    LPCWSTR lpDcName = NULL;

    if (argc != 3 ) {
        wprintf(L"Usage: %ws <ServerName> <DomainName>\n",
                argv[0]);
        wprintf(L"     %ws Myserver Domain\n", argv[0]);
        exit(1);
    }

    // lpServer = argv[1];
    // lpDomain = argv[2];

    wprintf(L"Calling NetGetDCName with parameters\n");
    wprintf(L"    lpServer = %ws\n", lpServer);
    wprintf(L"    lpDomain = %ws\n", lpDomain);

    //
    // Call the NetGetDCName function
    //
    nStatus = NetGetDCName(lpServer, lpDomain, (LPBYTE *) &lpDcName);
    //
    // If the call succeeds,
    //
    if (nStatus == NERR_Success) {
        wprintf(L"NetGetDCName was successful\n", nStatus);
        wprintf(L"DC Name = %ws\n", lpDcName);
        // Need to free the returned buffer
        nStatus = NetApiBufferFree( (LPVOID) lpDcName);
        if (nStatus != NERR_Success)
            wprintf(L"NetApiBufferFree failed with error: %lu (0x%lx)\n",
                nStatus, nStatus);
    } else {
        wprintf(L"NetGetDCName failed with error: %lu (0x%lx)\n", nStatus,
                nStatus);
        wprintf(L"   Error = ");
        switch (nStatus) {
        case ERROR_INVALID_PARAMETER:
            wprintf(L"ERROR_INVALID_PARAMETER\n");
            break;
        case ERROR_NO_SUCH_DOMAIN:
            wprintf(L"ERROR_NO_SUCH_DOMAIN\n");
            break;
        case ERROR_NOT_SUPPORTED:
            wprintf(L"ERROR_NOT_SUPPORTED\n");
            break;
        case ERROR_BAD_NETPATH:
            wprintf(L"ERROR_BAD_NETPATH\n");
            break;
        case ERROR_INVALID_COMPUTERNAME:
            wprintf(L"ERROR_INVALID_COMPUTERNAME\n");
            break;
        case DNS_ERROR_INVALID_NAME_CHAR:
            wprintf(L"DNS_ERROR_INVALID_NAME_CHAR\n");
            break;
        case DNS_ERROR_NON_RFC_NAME:
            wprintf(L"DNS_ERROR_NON_RFC_NAME\n");
            break;
        case ERROR_INVALID_NAME:
            wprintf(L"ERROR_INVALID_NAME\n");
            break;
        case NERR_DCNotFound:
            wprintf(L"NERR_DCNotFound\n");
            break;
        case NERR_WkstaNotStarted:
            wprintf(L"NERR_WkstaNotStarted\n");
            break;
        case RPC_S_SERVER_UNAVAILABLE:
            wprintf(L"RPC_S_SERVER_UNAVAILABLE\n");
            break;
        case RPC_E_REMOTE_DISABLED:
            wprintf(L"RPC_E_REMOTE_DISABLED\n");
            break;
        default:
            wprintf(L"Other error, see Winerror.h or lmerr.h)\n");
            break;
        }
    }

    return nStatus;
}

I only commented out the command line arguments to keep them as NULL:

    // lpServer = argv[1];
    // lpDomain = argv[2];

A pointer to a constant string that specifies the DNS or NetBIOS name of the remote server on which the function is to execute. If this parameter is NULL, the local computer is used.

A pointer to a constant string that specifies the name of the domain. The domain name must be a NetBIOS domain name (for example, microsoft). NetGetDCName does not support DNS-style names (for example, microsoft.com). If this parameter is NULL, the function returns the name of the domain controller for the primary domain.

Error I get:

NERR_DCNotFound

Why did the command line argument give me a Domain Name, but the WinAPI fails, or are they two different things with a similar name?

Thank you!


Solution

  • you can use call LsaQueryInformationPolicy first with PolicyDnsDomainInformation or PolicyPrimaryDomainInformation for get the computer primary domain.

    if the Sid member of POLICY_DNS_DOMAIN_INFO or POLICY_PRIMARY_DOMAIN_INFO (the SID of the primary domain) is 0 - this meant that system has no primary domain. in this case we need query account domain:

    On a system that is not part of a network and therefore has no primary domain, the account domain is used to house all accounts that provide access to the computer.

    for this we need call LsaQueryInformationPolicy with PolicyAccountDomainInformation and got POLICY_ACCOUNT_DOMAIN_INFO structure

    #include <Ntsecapi.h>
    
    NTSTATUS PrintDomainName()
    {
        LSA_HANDLE PolicyHandle;
    
        static LSA_OBJECT_ATTRIBUTES oa = { sizeof(oa) };
    
        NTSTATUS status = LsaOpenPolicy(0, &oa, POLICY_VIEW_LOCAL_INFORMATION, &PolicyHandle);
    
        if (LSA_SUCCESS(status))
        {
            union {
                PPOLICY_DNS_DOMAIN_INFO ppddi;
                PPOLICY_ACCOUNT_DOMAIN_INFO ppadi;
            };
    
            if (LSA_SUCCESS(status = LsaQueryInformationPolicy(PolicyHandle, PolicyDnsDomainInformation, (void**)&ppddi)))
            {
                if (ppddi->Sid)
                {
                    DbgPrint("DnsDomainName: %wZ\n", &ppddi->DnsDomainName);
                }
                else
                {
                    DbgPrint("%wZ: not domain controller !!\n", &ppddi->Name);
                    status = -1;
                }
    
                LsaFreeMemory(ppddi);
    
                if (0 > status)
                {
                    if (LSA_SUCCESS(status = LsaQueryInformationPolicy(PolicyHandle, PolicyAccountDomainInformation, (void**)&ppadi)))
                    {
                        DbgPrint("DomainName: %wZ\n", &ppadi->DomainName);
                        LsaFreeMemory(ppadi);
                    }
                }
            }
    
            LsaClose(PolicyHandle);
        }
    
        return status;
    }
    

    i got next output:

    WORKGROUP: not domain controller !!
    DomainName: DESKTOP-2N5ODLB