Windows managed DNS writes malformed REG_MULTI_SZ and produces phantom NRPT namespaces

Open Beginner friendly
#2,619 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
82/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
cpp
Domain
networking

Research direction

Start in osdep/WinDNSHelper.cpp around line 300 and inspect how the Name REG_MULTI_SZ value is written through RegSetKeyValueA. Reproduce the managed-DNS setup on Windows and inspect the registry value and parsed NRPT policies with Get-DnsClientNrptPolicy or netsh namespace show policy. Done means the value is correctly double-terminated and only the configured namespace is reported after recreation or reboot.

Written by the indexing model from the issue text.

Description

This bug was found by Codex, but I reproduced its findings by hand to make sure it's not a bogus report. :)

Before filing a Bug Report

Using these will ensure you get quicker support, and make this space available for code-related issues. Thank you!

  • Docs Site => Troubleshooting, quickstarts, and more advanced topics.
  • Discuss Forum => Our discussion forum for users and support to mutually resolve issues & suggest ideas.
  • Reddit => Our subreddit, which we monitor regularly and is fairly active.
  • Knowledge Base => Older wiki.

If you are having a connection issue, it's much easier to diagnose through the discussion forum or the ticket system.

If you still want to file a Bug Report

Please let us know

What you expect to be happening

When managed DNS is enabled for a network, the Windows client should write a valid REG_MULTI_SZ value for the NRPT namespace.

For a single namespace such as .KDG.local, the registry data should contain the string terminator followed by the additional terminator marking the end of the multi-string:

.KDG.local<NUL><NUL>

Windows should consequently expose exactly one NRPT namespace.

What is actually happening?

ZeroTier creates this value:

HKLM\SYSTEM\CurrentControlSet\Services\Dnscache\Parameters\DnsPolicyConfig\{GUID}
    Name    REG_MULTI_SZ

The raw data contains only one terminating NUL:

Size    : 22
Hex     : 2E 00 4B 00 44 00 47 00 2E 00 6C 00 6F 00 63 00 61 00 6C 00 00 00
Unicode : .KDG.local<NUL>

A valid one-element REG_MULTI_SZ for this value requires 24 bytes:

.KDG.local<NUL><NUL>

Because the multi-string is not terminated correctly, the Windows NRPT provider reads beyond the intended value and exposes additional, apparently random namespaces. Across repeated boots we observed phantom values including:

U+0001
WSL)
g

For example, Get-DnsClientNrptPolicy returned:

Namespace  : g
NameServers: 192.168.194.3

Namespace  : .KDG.local
NameServers: 192.168.194.3

The phantom namespace changed after the ZeroTier service recreated the rule.

Rewriting Name through the Windows registry API as a proper MultiString immediately removed the phantom entries:

$key.SetValue(
    'Name',
    [string[]]@('.KDG.local'),
    [Microsoft.Win32.RegistryValueKind]::MultiString
)

Afterward, both Get-DnsClientNrptPolicy and netsh namespace show policy displayed only .KDG.local. After restarting/rebooting and allowing ZeroTier to recreate the rule, the malformed value and phantom namespace returned.

This appears to originate here:

https://github.com/zerotier/ZeroTierOne/blob/1.16.2/osdep/WinDNSHelper.cpp#L300

RegSetKeyValueA(
    dnsKey,
    NULL,
    "Name",
    REG_MULTI_SZ,
    d.data(),
    d.length()
);

The supplied byte count excludes the required REG_MULTI_SZ terminators.

Steps to reproduce the error
  1. On a Windows client, join a ZeroTier network and permit managed DNS:

    zerotier-cli set <network-id> allowDNS=1
    
  2. In ZeroTier Central, configure managed DNS with:

    Search domain: KDG.local
    DNS server:    192.168.194.3
    
  3. Confirm that the client received the configuration:

    zerotier-cli -j listnetworks
    
  4. Locate the generated registry key under:

    HKLM\SYSTEM\CurrentControlSet\Services\Dnscache\Parameters\DnsPolicyConfig
    

    ZeroTier identifies its key through the Comment value containing the network ID.

  5. Read the raw bytes and size of the Name value with RegQueryValueEx. Observe that .KDG.local occupies 22 bytes and ends with only one UTF-16 NUL.

  6. Reboot Windows or restart ZeroTier, then inspect the parsed namespaces:

    Get-DnsClientNrptPolicy
    netsh namespace show policy
    
  7. Observe one or more phantom namespaces in addition to .KDG.local.

  8. Rewrite Name as a correctly formed REG_MULTI_SZ. The phantom entries disappear immediately.

For comparison, an NRPT rule created with Microsoft’s Add-DnsClientNrptRule was correctly double-terminated.

No GPO-backed NRPT configuration was present during this reproduction:

HKLM\SOFTWARE\Policies\Microsoft\Windows NT\DNSClient\DnsPolicyConfig

was absent. This therefore appears independent of the GPO precedence behavior discussed in #1627.

Relevant console output or screenshots

ZeroTier-created value:

Type        : 7
Size        : 22
Hex         : 2E 00 4B 00 44 00 47 00 2E 00 6C 00 6F 00 63 00 61 00 6C 00 00 00
Unicode     : .KDG.local<NUL>

Rule as reported by Windows:

Name          : {GUID}
Namespace     : {.KDG.local}
NameServers   : 192.168.194.3
Comment       : <network-id>
ConfigOptions : 8

Example phantom policy output after reboot:

Namespace   : g
NameServers : 192.168.194.3

Namespace   : .KDG.local
NameServers : 192.168.194.3

Output after correcting the registry value:

Namespace   : .KDG.local
NameServers : 192.168.194.3
Operating system and ZeroTier version
Operating system: Windows 10 Pro 22H2
OS build:         19045.6456
Architecture:     x64
ZeroTier version: 1.16.2
allowDNS:         true

ZeroTier 1.16.2 was the latest stable release at the time of testing.

Dominant language
C++
Stars
17.1k
Forks
2k
PR merge metrics
No merged PRs in 30d

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from zerotier/ZeroTierOne

All issues in zerotier/ZeroTierOne

Similar issues

More C++ issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.