Microsoft DNS is able to store records in Active Directory when running on a Domain Controller. The information is stored in a Binary Large Object (BLOB) called DNSRecord. No official maps for that attribute appear to have been published. The information below is a result of reverse engineering the contents of the attribute.

Michael Smith has a very pretty PowerShell script which uses the structures below, and a few more, to convert the DnsRecord attribute into a human readable format on his blog, Michael’s meanderings….

Update 02/02/2010: In December 2009, Microsoft released a protocol specification including details of dnsRecord and dnsProperty: MS-DNSP.pdf

About the mapped structure

The map created below for DNSRecord is incomplete, the remaining values seem to defy testing. While the map below is probably accurate I reserve the right to be wrong. Despite that, the structures can be used to manually construct or decode DNSRecords via LDAP rather than using the GUI, dnscmd or WMI. Edit: The map is now complete.

About DNSRecord

The dnsRecord attribute appears on dnsNode objects. The dnsRecord attribute is multi-valued. This means that each node can contain more than one record. This is most obvious for the node representing “same as parent folder” which will hold the NS records and SOA records as a minimum.

Structures: DNSRecord

The DNSRecord attribute is composed of the fields described in the table below.

Field Name Length (Bytes) Format Description
RData Length 2 Little Endian Length of the Record Data block
Type 2 Little Endian Record type. Matches published type values on IANA
Unknown (1) 4 N/A Predictable, but unknown.
UpdatedAtSerial 4 Little Endian Changes to match the serial number in the SOA whenever the record is modified
TTL 4 Big Endian Time To Live value for the record
Unknown (2) 4 N/A Always 0
TimeStamp 4 Little Endian TimeStamp in hours from 01/01/1601 00:00:00
RData Variable Variable The record data, formatting described below

These values produce the following binary array.

Unknown 1 is a difficult value to interpret. It may contain several separate fields, however as none appear easy to decipher they were left as a single block in the map. Testing shows that “unknown 1″ has the following values:
[code lang=”plain”]
1 1 1 1 1 1
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+–+–+–+–+–+–+–+–+–+–+–+–+–+–+–+–+
| 5 | AdvRecordType |
+–+–+–+–+–+–+–+–+–+–+–+–+–+–+–+–+
| 0 | 0 |
+–+–+–+–+–+–+–+–+–+–+–+–+–+–+–+–+

Edit: The structure of Unknown 1 is as follows.
[code]
1 1 1 1 1 1
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+–+–+–+–+–+–+–+–+–+–+–+–+–+–+–+–+
| VERSION | AdvRecordType |
+–+–+–+–+–+–+–+–+–+–+–+–+–+–+–+–+
| FLAGS |
+–+–+–+–+–+–+–+–+–+–+–+–+–+–+–+–+

Modifying the first byte to any (decimal) value other than 5 will cause the record to vanish from the DNS system. It will remain in the directory, but appears to render it useless. Edit: 5 is the Version number and is a static value.

The second byte, termed AdvRecordType, appears to have a number of possible values. Experimentation shows that Root Hints have the value set to decimal 8, out-of-zone records (normally Glue for NS Records) have 128, delegations within a zone have 130 and everything else has 240. A larger data set than I have available is required to draw conclusions other than those.

Edit: The values for AdvRecordType, referred to as Rank in the documentation above are represented by this Enumeration.

[code lang=”csharp”]
public enum RankFlag : uint
{
// The record came from the cache.
CacheBit = 1,
// The record is a preconfigured root hint.
RootHint = 8,
// This value is not used.
OutsideGlue = 32,
// The record was cached from the additional section of a
// nonauthoritative response.
CacheNAAdditional = 49,
// The record was cached from the authority section of a
// nonauthoritative response.
CacheNAAuthority = 65,
// The record was cached from the additional section of an
// authoritative response.
CacheAAdditional = 81,
// The record was cached from the answer section of a
// nonauthoritative response.
CacheNAAnswer = 97,
// The record was cached from the authority section of an
// authoritative response.
CacheAAuthority = 113,
// The record is a glue record in an authoritative zone.
Glue = 128,
// The record is a delegation (type NS) record in an
// authoritative zone.
NSGlue = 130,
// The record was cached from the answer section of an
// authoritative response.
CacheAAnswer = 193,
// The record comes from an authoritative zone.
Zone = 240
}

The final two bytes appear to be set to 0 in all instances. Edit: Referred to as Flags, the value must be 0.

Edit: Unknown 2 is reserved for future use and should be set to 0 in all cases.

Structures: RDATA

Each of the structures below is a minimal representation of the record data, the structures show single-label names. In each case the “Label Length” and “Data” structures repeat where multiple labels are used, this also applies to “Responsible Person” in the SOA record.

A

The RDATA block for the A record is a static 4 byte (32 bit) field. Each byte represents an octet in the IP address.
[code lang=”plain”]
1 1 1 1 1 1
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+–+–+–+–+–+–+–+–+–+–+–+–+–+–+–+–+
| DATA |
| |
+–+–+–+–+–+–+–+–+–+–+–+–+–+–+–+–+

CNAME and NS

[code lang=”plain”]
1 1 1 1 1 1
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+–+–+–+–+–+–+–+–+–+–+–+–+–+–+–+–+
| LENGTH | NUMBER OF LABELS |
+–+–+–+–+–+–+–+–+–+–+–+–+–+–+–+–+
| LABEL LENGTH | |
|–+–+–+–+–+–+–+–+ |
/ DATA /
/ /
+–+–+–+–+–+–+–+–+–+–+–+–+–+–+–+–+

MX

[code lang=”plain”]
1 1 1 1 1 1
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+–+–+–+–+–+–+–+–+–+–+–+–+–+–+–+–+
| PRIORITY |
+–+–+–+–+–+–+–+–+–+–+–+–+–+–+–+–+
| LENGTH | NUMBER OF LABELS |
+–+–+–+–+–+–+–+–+–+–+–+–+–+–+–+–+
| LABEL LENGTH | |
|–+–+–+–+–+–+–+–+ |
/ DATA /
/ /
+–+–+–+–+–+–+–+–+–+–+–+–+–+–+–+–+

SOA

[code lang=”plain”]
1 1 1 1 1 1
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+–+–+–+–+–+–+–+–+–+–+–+–+–+–+–+–+
| SERIAL |
| |
+–+–+–+–+–+–+–+–+–+–+–+–+–+–+–+–+
| REFRESH |
| |
+–+–+–+–+–+–+–+–+–+–+–+–+–+–+–+–+
| RETRY |
| |
+–+–+–+–+–+–+–+–+–+–+–+–+–+–+–+–+
| EXPIRE |
| |
+–+–+–+–+–+–+–+–+–+–+–+–+–+–+–+–+
| MINIMUM TTL |
| |
+–+–+–+–+–+–+–+–+–+–+–+–+–+–+–+–+
| LENGTH | NUMBER OF LABELS |
+–+–+–+–+–+–+–+–+–+–+–+–+–+–+–+–+
| LABEL LENGTH | |
+–+–+–+–+–+–+–+–+ |
/ DATA /
/ /
+–+–+–+–+–+–+–+–+–+–+–+–+–+–+–+–+
| LENGTH | NUMBER OF LABELS |
+–+–+–+–+–+–+–+–+–+–+–+–+–+–+–+–+
| LABEL LENGTH | |
|–+–+–+–+–+–+–+–+ |
/ RESPONSIBLE PERSON /
/ /
+–+–+–+–+–+–+–+–+–+–+–+–+–+–+–+–+

SRV

[code lang=”plain”]
1 1 1 1 1 1
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+–+–+–+–+–+–+–+–+–+–+–+–+–+–+–+–+
| PRIORITY |
+–+–+–+–+–+–+–+–+–+–+–+–+–+–+–+–+
| WEIGHT |
+–+–+–+–+–+–+–+–+–+–+–+–+–+–+–+–+
| PORT |
+–+–+–+–+–+–+–+–+–+–+–+–+–+–+–+–+
| LENGTH | NUMBER OF LABELS |
+–+–+–+–+–+–+–+–+–+–+–+–+–+–+–+–+
| LABEL LENGTH | |
|–+–+–+–+–+–+–+–+ |
/ DATA /
/ /
+–+–+–+–+–+–+–+–+–+–+–+–+–+–+–+–+

TXT

[code lang=”plain”]
1 1 1 1 1 1
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+–+–+–+–+–+–+–+–+–+–+–+–+–+–+–+–+
| LENGTH | |
|–+–+–+–+–+–+–+–+ |
/ DATA /
/ /
+–+–+–+–+–+–+–+–+–+–+–+–+–+–+–+–+

3 Comments

  1. I was doing the same thing late summer. I was writing a comprehensive DNS Health check web portal and needed to get to the Dynamic vs. Static for records of AD-Integrated DNS zones. I was doing this from Linux machine using ldapsearch with -t flag to capture the dnsnode BLOBs. I used Linux od (octal dump) to index into all the various integer data types, which made my effort trivial. The only non-trivial part is the revese-engineering process. Why doesn’t Microsoft just publish this? Anyway, when I first started out, I captured field names from LDP.exe queries, assuming that whoever wrote it understands the real BLOB structure. Maybe these field names will help or confuse matters more? I believe your unknown_1 is actully 3 fields, the last of which is a little-endian storing bit-level flags of various meanings. Since it was reverse-engineered with minimal data, I also do not guarantee any of this to be correct! đŸ™‚

    Reverse Engineer of dnsRecord attribute BLOB – a work in progress
    [code]
    Bytes Field Data Type Meaning
    —– —– ——— ——-
    1-2 wDataLength 16-bit int declares RR variable length
    3-4 wType 16-bit int DNS RR Type (Standard)
    5 Version 8-bit int ?, values seen = 5
    6 Rank 8-bit int ?, values seen = 240
    7-8 wFlags 16 bits presumably various bit flags?
    9-12 dwSerial 32-bit int ? you show this as zone serial number upon update
    13-16 dwTtlSeconds 32-bit int
    17-20 dwTimeout 32-bit int ? Zone-level TTL?
    21-24 dwStartRefreshHr 32-bit int record timestamp 0 for STATIC!
    25 RRval_len 8-bit uint byte length of RR string value
    26 wNumLabels 8-bit uint Number of DNS RR labels
    27 label_len1 8-bit uint size of first label
    28 – X label_chars_lens 8-bit uints rest of labelsizes and labels

    Reply

  2. Thanks for this, saved me a lot of time trying to figure out where the TimeStamp is stored. Why do you say the MS documentation is not accurate though? Which particular part of it is not accurate?

    Reply

    • A few of the fields it described were incorrect (in that they didn’t stand up to testing), I forget exactly which. I’ll take that bit out since the MS document is likely to have been updated since I wrote my article.

      Chris

      Reply

Leave a Reply

Your email address will not be published. Required fields are marked *