NLTEST Flags – what does 0x20000 mean?

Requires NetTools 1.29.31 beta or later

When running NLTEST /DSGETDC command against a domain controller that is Windows 2012R2 or later, the command will display the normal flags plus an extra flag called '0x20000', but what does the 0x20000 flag mean.  First of all it's not an error code, Microsoft have added an additional feature to Windows 2012R2 and later DCs, but NLTEST hasn't been updated to display this flag correctly, even the Windows 2019 version doesn't have this flag defined.

The results deplayed by NLTEST /DSGETDC is the information returned by the DsGetDcName API, this information if defined in the DOMAIN_CONTROLLER_INFO structure.

LPSTR DomainControllerName;
LPSTR DomainControllerAddress;
ULONG DomainControllerAddressType;
GUID DomainGuid;
LPSTR DomainName;
LPSTR DnsForestName;
ULONG Flags;
LPSTR DcSiteName;
LPSTR ClientSiteName;

The Flags member has the following definitions in the dsgetdc.h file

#define DS_PDC_FLAG 0x00000001 // DC is PDC of Domain
#define DS_GC_FLAG 0x00000004 // DC is a GC of forest
#define DS_LDAP_FLAG 0x00000008 // Server supports an LDAP server
#define DS_DS_FLAG 0x00000010 // DC supports a DS and is a Domain Controller
#define DS_KDC_FLAG 0x00000020 // DC is running KDC service
#define DS_TIMESERV_FLAG 0x00000040 // DC is running time service
#define DS_CLOSEST_FLAG 0x00000080 // DC is in closest site to client
#define DS_WRITABLE_FLAG 0x00000100 // DC has a writable DS
#define DS_GOOD_TIMESERV_FLAG 0x00000200 // DC is running time service (and has clock hardware)
#define DS_NDNC_FLAG 0x00000400 // DomainName is non-domain NC serviced by the LDAP server
#define DS_SELECT_SECRET_DOMAIN_6_FLAG 0x00000800 // DC has some secrets
#define DS_FULL_SECRET_DOMAIN_6_FLAG 0x00001000 // DC has all secrets
#define DS_WS_FLAG 0x00002000 // DC is running web service
#define DS_DS_8_FLAG 0x00004000 // DC is running Win8 or later
#define DS_DS_9_FLAG 0x00008000 // DC is running Win8.1 or later
#define DS_DS_10_FLAG 0x00010000 // DC is running WinThreshold or later
#define DS_KEY_LIST_FLAG 0X00020000 // DC supports key list requests
#define DS_PING_FLAGS 0x000FFFFF // Flags returned on ping
#define DS_DNS_CONTROLLER_FLAG 0x20000000 // DomainControllerName is a DNS name
#define DS_DNS_DOMAIN_FLAG 0x40000000 // DomainName is a DNS name
#define DS_DNS_FOREST_FLAG 0x80000000 // DnsForestName is a DNS name

As you can see 0x20000 is defined in the include file as support for Key List Requests, see the Kerberos Protocol Extension [MS-KILE] section 2.2.11 for more info.  NetTools includes this decode and the result from the same server shows the option for Key List Request are supported.

HowTo: Using Search Stats OID 1.2.840.113556.1.4.970

Active Directory and LDS provide a server side control when added to query will provides statistics on the efficiency of the query that was executed, the specific control is OID 1.2.840.113556.1.4.970 - LDAP_SERVER_GET_STATS_OID and the details can be found here.

The NetTools LDAP Search option provides a simple checkbox option to enable this server side control to be added to queries.  The option is found Server Side Controls section, called Search Statistics.  When the query is run and the user has the appropriate permissions the search statistics will be returned.

When the query is executed the Statistics are displayed in the output panel after the results of the query.  Below are the statistics returned by Windows 2016 server.

Version of the operating system running on the server, will determine the statistics that will be returned.  As Windows evolved the level details returned by the server has also increased.  Windows 2000 only provided 4 different statistics, Windows 2003 increased this to 6, and for Windows 2008 this increased to 15 and it also introduced a new format which provides more details but the fields are dynamic, rather than the older static fields.

NetTools detects the Domain Controller Functional level of the server and automatically adjust the control parameters to select the highest level of detail available for the server.

The table below shows which statistics level are returned by each version of Windows

2000 2003/R2 2008/R2 2012/R2 2016 2019
StatsResponseValueV1 x
StatsResponseValueV2 x
StatsResponseValueV3 x x x x
StatsResponseValueV4 x x x x

The details for each set of Stats can be found below.  

While NetTools will automatically select the stats level based on the domain controller functional level, it is possible to manually specify the required stats level using the Server Side Controls dialog.  To do this, first uncheck the Search Statistics option, then click on the Controls button in the Server Side Control section and add a control as shown below, the Value to 1 for the corrsponding V1,V2, or V3 supported by the server or a Value of 5 for the V4 stats.

These are the Statistics returned by a Windows 2019 server with the Value set to 1:

Search Stats:
  Thread Count: 1
  Call Time (ms): 0
  Entries Returned: 3
  Entries Visited: 4
  Filter: ( & (objectClass=user) (name=gary*) ) 
  Index: idx_name:4:N;
  Pages Referenced: 126
  Pages Read: 0
  Pages Pre-Read: 0
  Clean Pages Modified: 0
  Dirty Pages Modified: 0
  Log Records Generated: 0
  Log Records Bytes Generated: 0

These are the Statistics returned by the same query, with the Value set to 5

Search Stats:
  Thread count: 1
  Call time (in ms): 0
  Entries Returned: 0
  Entries Visited: 0
  Used Filter: ( & (objectClass=user) (name=gary*) ) 
  Used Indexes: idx_name:4:N;
  Pages Referenced: 27
  Pages Read From Disk: 0
  Pages Pre-read From Disk: 0
  Clean Pages Modified: 0
  Dirty Pages Modified: 0
  Log Records Generated: 0
  Log Record Bytes Generated: 0
  Indices required to optimize: 
  Query optimizer state: ( & (objectClass=user:878204) (name=gary*:4) ) 
  Atq Delay: 0
  CPU Time: 0
  Search Signature: b4cce897-7577-b624-5d18-2f5a9e90754f
  Memory Usage: 26744
  JET LV Read: 0
  JET LV Created: 0
  Total call time (in ms): 0
  Total CPU time: 0
  Number of retries: 0
  Correlation ID: e2a4641a-0714-44cc-b1bf-a0b0ca8e055c
  Links Added: 0
  Links Deleted: 0

These are the various Stats data lists:

StatsResponseValueV1 ::= SEQUENCE {
  threadCountTag            INTEGER
  threadCount               INTEGER
  coreTimeTag               INTEGER
  coreTime                  INTEGER
  callTimeTag               INTEGER
  callTime                  INTEGER
  searchSubOperationsTag    INTEGER
  searchSubOperations       INTEGER
StatsResponseValueV2 ::= SEQUENCE {
  threadCountTag        INTEGER
  threadCount           INTEGER
  callTimeTag           INTEGER
  callTime              INTEGER
  entriesReturnedTag    INTEGER
  entriesReturned       INTEGER
  entriesVisitedTag     INTEGER
  entriesVisited        INTEGER
  filterTag             INTEGER
  filter                OCTET STRING
  indexTag              INTEGER
  index                 OCTET STRING

StatsResponseValueV3 ::= SEQUENCE {
  threadCountTag INTEGER
  threadCount INTEGER
  callTimeTag INTEGER
  callTime INTEGER
  entriesReturnedTag INTEGER
  entriesReturned INTEGER
  entriesVisitedTag INTEGER
  entriesVisited INTEGER
  filterTag INTEGER
  indexTag INTEGER
  pagesReferencedTag INTEGER
  pagesReferenced INTEGER
  pagesReadTag INTEGER
  pagesRead INTEGER
  pagesPrereadTag INTEGER
  pagesPreread INTEGER
  pagesDirtiedTag INTEGER
  pagesDirtied INTEGER
  pagesRedirtiedTag INTEGER
  pagesRedirtied INTEGER
  logRecordCountTag INTEGER
  logRecordCount INTEGER
  logRecordBytesTag INTEGER
  logRecordBytes INTEGER
StatsResponseValueV4 ::= SEQUENCE OF SEQUENCE {
      statisticName         OCTET STRING
      CHOICE {
         intStatistic [0]       INTEGER
         stringStatistic [1]    OCTET STRING

Process Flow for LDAP Search

This is a quick article that shows the process flow of the LDAP Search feature.  The LDAP Search function consists of a number of functions that are used to execute the query and display the results.  The main function is used to collect and validate the user inputs and connect to the server and execute the query, then a sub function is used to display the results and complete any attribute updates.  The process shown below starts when the user presses the Go button to execute the query.

How to decode LogonHours Attribute

In this post we look at the LogonHours attribute, which is used to restrict when a user is allowed to logon, and how to decode this attribute.

The LogonHours attribute has a octet data type that is used to store a 21 byte value which defines when a user is allowed to logon, outside of these hours the user will receive the following error message when they try to logon:

This may be seen as one of the following errors:

Error 1327: Account restrictions are preventing this user from signing in. For example: blank passwords aren't allowed, sign-in times are limited, or a policy restriction has been enforced

Error 1328: Your account has time restrictions that keep you from signing in right now.

The LogonHours attribute is used to define when a user is permitted to log on, it uses the 21 byte data structure to represent the day’s of the week.  It uses three bytes to represent each day of the week. The three bytes represent the hours of the day, the diagram below shows the mapping of the bytes to days and hours.

The user's permitted logon hours are displayed in the properties of the user in Active Directory User and Computers under the Account tab. 

One of the challenges with decoding the LogonHours attribute is that the data is saved based on UTC, as shown in the mapping above, however, Active Directory Users and Computers will display the details based on the local time zone of the computer running ADUC, and will adjust the times based on the time zone offset.   Below we can see that the left hand picture shows the Logon Hours on a computer with the time zone set to UTC, while the right shows the same details but the computer has a time zone set to Melbourne (UTC+10).

The time zone of the Domain Controller, which authenticates the user will be used to determine, if they can log on, or not.

This is the value of the attribute based on the permitted logon hours of Monday to Friday 6am to 7pm on a machine with time zone set to UTC, as shown in the left picture above.

DN> CN=Teena Lee,OU=Domain Users,DC=w2k12,DC=local
> logonHours: 00 00 00 C0 FF 03 C0 FF 03 C0 FF 03 C0 FF 03 C0 FF 03 00 00 00

We can see that this aligns with the mapping above, with the Sunday and Saturday bytes set to zeros. Next, this is the value set for the same time window on a machine with the time zone set to Melbourne (UTC+10)

DN> CN=Teena Lee,OU=Domain Users,DC=w2k12,DC=local
> logonHours: 00 00 F0 FF 01 F0 FF 01 F0 FF 01 F0 FF 01 F0 FF 01 00 00 00 00

The Sunday bytes now have values set, as the time was adjusted by -10 hours before it was saved. Next, this is the value set for the same time window on a machine with the time zone set to Pacific Time (UTC-10)

DN> CN=Teena Lee,OU=Domain Users,DC=w2k12,DC=local
> logonHours (BIN): 00 00 00 00 C0 FF 07 C0 FF 07 C0 FF 07 C0 FF 07 C0 FF 07 00 00 

With this one, the hours data is now written into the Saturday bytes due to the UTC-10 offset.

The LogonHours functionality is limited to a single time zone, and can potentially cause logon issues, if a user travels, or authenticates to a Domain Controller which has a different time zone set.

The AD Properties dialog in NetTools (Version 1.29.7 beta and above) has a Restrictions tab which displays the Logon Hours, by default it will use the local time zone to display this information, however, there is an option to allow you to manually adjust the time zone to see the impact the user's ability to logon.

Below is the code used to display the LogonHours in NetTools, the function is called for each square in the grid, the ACol and ARow defining the square that is being queried, the function will colour the square blue, if the LogonHour is set.  The function also automatically adjusts the LogonHours based on the local or user selected time zone.

void dgHoursDrawCell(TObject *Sender, int ACol, int ARow, TRect &Rect, TGridDrawState State)
int Index, Col,Row, Mask;
int Val, Bias;

   // use Col and Row to reflect tz offset
   Col = ACol;
   Row = ARow;

   // change start of week to Monday
   if (Row==6){
      Row = 0;
   } else {

   if (chkLocalTime->Checked){
      Bias = tz.Bias/60;  // get local time zone, tz populated when form is loaded
   } else {
      try {
          Bias = StrToInt(cmbTZOffset->Text);  // get user selection
          Bias = 0;

   Col += Bias;  // add time zone offset

   if (Col > 23) {  // rap pointer to start of next day
      Col -= 24;

   if (Col < 0) {  // rap pointer to end of the previous day
      Col += 24;

   if (Row > 6) Row = 0; // rap pointer to valid data
   if (Row < 0) Row = 6;

   if (Col >=0 && Col <=7) Index=0;  // select the correct hours offset bytes
   if (Col >=8 && Col <=15) Index=1;
   if (Col >=16 && Col <=23) Index=2;

   Index += (3 * Row);  // get correct byte
   Mask = 0x1 << (Col % 8);  // create bit mask for hour based on col number

   Val = HourBuffer[Index] & Mask;  // apply mask to check if set

   if (Val){  // Val is non zero set square to blue
       dgHours->Canvas->Brush->Color = clBlue;
   } else {
       dgHours->Canvas->Brush->Color = clWhite;

   dgHours->Canvas->FillRect(Rect);  // draw the square



Invalid characters for Office365 Sync

Office365 specifies a number of characters that can't be includes in a number of key attributes. These invalid characters vary depending on the attribute, for a full list of invalid characters in each attribute see this Microsoft article.

NetTools includes a predefined query that will show which user objects contain these invalid characters. The query is called Users: Invalid characters for O365, which is available in the LDAP Search option. These are the attributes that are included in the search

        • givenName
        • sn
        • mailNickname
        • proxyAddresses
        • UserPrincipalName 
        • mail

To run the query first select the LDAP Search Option in the left hand pane, then click on the Populate button, shown in the red square below, to connect to the AD and populate the Base DN field.


Once the Populate has finished, select the Users: Invalid characters for O365 query from the Favorites dropdown list. If required, change the BaseDN field to limit the scope of the search and then click Go.  A list of all the user objects that contain invalid characters will be displayed.

The query uses the Regex Display filter option to only display the user objects that have invalid characters.  Here are the the query properties:

[Users: Invalid characters for O365]
Attributes=userPrincipalName, proxyAddresses;SMTP, givenName, sn,displayName,mailNickname, mail
DisplayFilter=userPrincipalName regx [\"|,/:<>+=;?*'] || givenName regx [\"|,/:<>+=;?*'] || sn regx [\"|,/:<>+=;?*'] || mailNickname regx [\"|,/:<>+=;?*'] || mail regx [\"|,/:<>+=;?*'] || proxyaddresses regx [\"|,/:<>+=;?*']

For more information on the available queries see Redefined LDAP Queries  
For details on the favorites option see Favorites

Workaround for SmartScreen

When running NetTools on a Windows 10 machine, it can sometimes trigger the Microsoft Defender SmartScreen and block the execution of NetTools.  This is because NetTools is not signed and SmartScreen blocks apps that have been downloaded.  This is an example of the SmartScreen dialog that is be displayed.

To prevent SmartScreen from blocking NetTools, open the properties of NetTools.exe and check the Unblock option and click OK.

LDAP Search – Credentials

Note: The Credentials option was deprecated in version 1.28.0 and replaced with Connection Profiles

LDAP Search provides the ability to specific the credentials under which a query will be executed, it also provides the ability to select the authentication method that will be used to pass the credentials to the server.

The Credentials dialog is found when the More button is pressed.


There are nine different authentication methods available:

LDAP_AUTH_SIMPLE, this method requires the DN of the account and password, domain is not required
LDAP_AUTH_DIGEST, Digest authentication package
LDAP_AUTH_DPA, Distributed password authentication. Used by Microsoft Membership System
LDAP_AUTH_MSN, Microsoft Network Authentication Service
LDAP_AUTH_NTLM, this method uses NTLM to authenticate against the directory
LDAP_AUTH_SICILY, covers package negotiation to MSN servers
LDAP_AUTH_DIGEST, this method requires the samaccountname and password
LDAP_AUTH_NEGOTIATE, this method requires either, samaccountname or UPN and password, the domain is optional
ANONYMOUS, the username and password are not required.

See the following MS Article for more details ldap_bind_s

Warning: With the simple bind method the password is sent in clear text to the server, you should use this method in association with an SSL based connection to protect the password.

The default behavior of NetTools is use the negotiate method, when connecting to an Active Directory, you don't need to provide any credentials, the current user's context will be used based on Kerberos authentication.

A number of other options in NetTools use the credentials provided in this dialog to run the option under a different or elevated set of credentials, this is shown as Use the LDAP Search Credentials.