Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
21787a2
Add System.Net.Dns resolver API surface and Windows implementation
rzikm Jun 3, 2026
77dd68c
Add DnsResolver tests
rzikm Jun 3, 2026
f0ef4af
Add loopback DNS test suite and restrict DnsQueryEx custom servers to…
rzikm Jun 4, 2026
a589fad
Polish tests
rzikm Jun 11, 2026
73acd3c
Add synchronous DnsResolver methods using synchronous DnsQueryEx
rzikm Jun 11, 2026
c375219
Unify sync/async DnsResolver core methods into single bool-async methods
rzikm Jun 11, 2026
8196da6
Assert sync DnsResolver core tasks complete synchronously
rzikm Jun 11, 2026
522c396
Refactor Windows DnsResolver logic into DnsResolverPal (PAL pattern)
rzikm Jun 11, 2026
6157140
Add telemetry to DnsResolver
rzikm Jun 11, 2026
6f99d7b
Fix DnsResolver telemetry under-measuring synchronous queries
rzikm Jun 11, 2026
6068489
Address Windows DNS PR review feedback
rzikm Jun 12, 2026
42bb74e
Keep instance DnsResolver.ResolvePtr(IPAddress) overloads
rzikm Jun 16, 2026
6a9f986
More code review feedback
rzikm Jun 17, 2026
023ff8b
Reduce unsafe code in Windows DNS PAL/interop layer
rzikm Jun 17, 2026
4247861
More minor changes
rzikm Jun 17, 2026
3af512a
Address PR review feedback: thread-safety and server validation
rzikm Jun 18, 2026
14b6592
Add synchronous API coverage to DnsResolverTest via bool async theories
rzikm Jun 18, 2026
ed8096c
Address PR review feedback: port normalization, skip handling, comments
rzikm Jun 18, 2026
693bd6e
Address PR #129845 review feedback
rzikm Jun 25, 2026
982d370
Modernize DnsQueryEx interop per review feedback
rzikm Jun 26, 2026
99054e7
Address PR review feedback on DNS resolver
rzikm Jun 26, 2026
0f2ebd6
More feedback
rzikm Jun 27, 2026
ff8e4fc
Feedback
rzikm Jun 27, 2026
38dd455
regenerate ref source
rzikm Jun 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions src/libraries/Common/src/Interop/Windows/Dnsapi/Interop.DnsApi.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Runtime.InteropServices;

internal static partial class Interop
{
internal static partial class Dnsapi
{
// ---- Query types we use ----
internal const ushort DNS_TYPE_A = 0x0001;
internal const ushort DNS_TYPE_NS = 0x0002;
internal const ushort DNS_TYPE_CNAME = 0x0005;
internal const ushort DNS_TYPE_SOA = 0x0006;
internal const ushort DNS_TYPE_PTR = 0x000c;
internal const ushort DNS_TYPE_MX = 0x000f;
internal const ushort DNS_TYPE_TEXT = 0x0010;
internal const ushort DNS_TYPE_AAAA = 0x001c;
internal const ushort DNS_TYPE_SRV = 0x0021;

// ---- DnsQueryEx return codes / Win32 error codes ----
internal const int DNS_REQUEST_PENDING = 9506;
internal const int ERROR_SUCCESS = 0;
internal const int DNS_INFO_NO_RECORDS = 9501;
internal const int DNS_ERROR_RCODE_FORMAT_ERROR = 9001;
internal const int DNS_ERROR_RCODE_SERVER_FAILURE = 9002;
internal const int DNS_ERROR_RCODE_NAME_ERROR = 9003;
internal const int DNS_ERROR_RCODE_NOT_IMPLEMENTED = 9004;
internal const int DNS_ERROR_RCODE_REFUSED = 9005;

// ---- DnsQueryEx options ----
internal const ulong DNS_QUERY_STANDARD = 0x00000000;
internal const ulong DNS_QUERY_RETURN_MESSAGE = 0x00020000;

// ---- Query request versions ----
internal const uint DNS_QUERY_REQUEST_VERSION1 = 0x1;
internal const uint DNS_QUERY_REQUEST_VERSION3 = 0x3;

// ---- DNS_ADDR address family marker — addresses are stored in SOCKADDR form ----
internal const ushort AF_INET = 2;
internal const ushort AF_INET6 = 23;

// ---- DNS_CUSTOM_SERVER server types ----
internal const uint DNS_CUSTOM_SERVER_TYPE_UDP = 0x1;
internal const uint DNS_CUSTOM_SERVER_TYPE_DOH = 0x2;

// ---- DNS_CUSTOM_SERVER usage flags ----
internal const ulong DNS_CUSTOM_SERVER_UDP_FALLBACK = 0x1;

// ---- DnsFreeType for DnsFree ----
internal const int DnsFreeFlat = 0;
internal const int DnsFreeRecordList = 1;
internal const int DnsFreeParsedMessageFields = 2;

[LibraryImport(Libraries.Dnsapi, EntryPoint = "DnsQueryEx")]
internal static unsafe partial int DnsQueryEx(
DNS_QUERY_REQUEST* pQueryRequest,
DNS_QUERY_RESULT* pQueryResults,
DNS_QUERY_CANCEL* pCancelHandle);

[LibraryImport(Libraries.Dnsapi, EntryPoint = "DnsCancelQuery")]
internal static unsafe partial int DnsCancelQuery(DNS_QUERY_CANCEL* pCancelHandle);

[LibraryImport(Libraries.Dnsapi, EntryPoint = "DnsFree")]
internal static partial void DnsFree(IntPtr pData, int freeType);
}
}
199 changes: 199 additions & 0 deletions src/libraries/Common/src/Interop/Windows/Dnsapi/Interop.DnsTypes.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

internal static partial class Interop
{
internal static partial class Dnsapi
{
// DNS_QUERY_REQUEST (v1) — Win8 / Server 2012+
[StructLayout(LayoutKind.Sequential)]
internal unsafe struct DNS_QUERY_REQUEST
{
public uint Version;
public char* QueryName; // PCWSTR
public ushort QueryType;
public ulong QueryOptions;
public DNS_ADDR_ARRAY* pDnsServerList;
public uint InterfaceIndex;
public delegate* unmanaged[Stdcall]<nint, nint, void> pQueryCompletionCallback; // PDNS_QUERY_COMPLETION_ROUTINE
public IntPtr pQueryContext;
}

// DNS_QUERY_REQUEST3 — Win11 Build 22000+
[StructLayout(LayoutKind.Sequential)]
internal unsafe struct DNS_QUERY_REQUEST3

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This (and DNS_QUERY_REQUEST_VERSION3, and similar) seem to be unused

{
public uint Version;
public char* QueryName;
public ushort QueryType;
public ulong QueryOptions;
public DNS_ADDR_ARRAY* pDnsServerList;
public uint InterfaceIndex;
public delegate* unmanaged[Stdcall]<nint, nint, void> pQueryCompletionCallback;
public IntPtr pQueryContext;
public int IsNetworkQueryRequired; // BOOL
public uint RequiredNetworkIndex;
public uint cCustomServers;
public DNS_CUSTOM_SERVER* pCustomServers;
}

[StructLayout(LayoutKind.Sequential)]
internal unsafe struct DNS_QUERY_RESULT
{
public uint Version;
public int QueryStatus;
public ulong QueryOptions;
public IntPtr pQueryRecords; // DNS_RECORD*
public IntPtr Reserved;
}

[StructLayout(LayoutKind.Sequential)]
internal unsafe struct DNS_QUERY_CANCEL
{
public fixed byte Reserved[32];
}

[StructLayout(LayoutKind.Sequential)]
internal unsafe struct DNS_ADDR
{
// SOCKET_ADDRESS-like: 32 bytes of SOCKADDR_STORAGE-ish + extras.
// DnsApi documents this struct as 64 bytes total with the first 32
// being the SOCKADDR (IPv4/IPv6 SOCKADDR fits within).
public fixed byte MaxSa[32];
public uint DnsAddrUserDword0;
public uint DnsAddrUserDword1;
public uint DnsAddrUserDword2;
public uint DnsAddrUserDword3;
public uint DnsAddrUserDword4;
public uint DnsAddrUserDword5;
public uint DnsAddrUserDword6;
public uint DnsAddrUserDword7;
}

[StructLayout(LayoutKind.Sequential)]
internal unsafe struct DNS_ADDR_ARRAY
{
public uint MaxCount;
public uint AddrCount;
public uint Tag;
public ushort Family;
public ushort WordReserved;
public uint Flags;
public uint MatchFlag;
public uint Reserved1;
public uint Reserved2;
// followed by AddrCount entries of DNS_ADDR
// (we allocate the trailing array contiguously)
}

[StructLayout(LayoutKind.Sequential)]
internal unsafe struct DNS_CUSTOM_SERVER
{
public uint dwServerType; // DNS_CUSTOM_SERVER_TYPE_*
public ulong ullFlags;
public char* pwszTemplate; // PCWSTR (DoH only)
public fixed byte ServerAddr[32]; // SOCKADDR
}

// ---- DNS_RECORD (variable layout: header + Data union) ----
// We declare the fixed header layout and read the data area as a byte blob,
// re-interpreting per record type. The Data union follows the header; because the
// header contains two pointers, its size (and therefore the Data offset) depends on
// the pointer width - 24 bytes on 32-bit and 32 bytes on 64-bit. Callers must use
// sizeof(DNS_RECORD_HEADER) rather than a hard-coded offset.
[StructLayout(LayoutKind.Sequential)]
internal unsafe struct DNS_RECORD_HEADER
{
public IntPtr pNext; // DNS_RECORD*
public char* pName; // PCWSTR
public ushort wType;
public ushort wDataLength; // not always reliable; use type to interpret
public uint Flags; // contains Section in the low bits
public uint dwTtl;
public uint dwReserved;
// followed by Data union
}

// ---- Section field within DNS_RECORD.Flags ----
// The Section is the lowest 2 bits of the DW_FLAGS field.
internal const uint DNSREC_SECTION_MASK = 0x3;
internal const uint DNSREC_QUESTION = 0;
internal const uint DNSREC_ANSWER = 1;
internal const uint DNSREC_AUTHORITY = 2;
internal const uint DNSREC_ADDITIONAL = 3;

// ---- Data unions ----
[StructLayout(LayoutKind.Sequential)]
internal struct DNS_A_DATA
{
public uint IpAddress; // network byte order
}

[StructLayout(LayoutKind.Sequential)]
internal struct DNS_AAAA_DATA
{
public InlineArray16<byte> Ip6Address;
}
Comment thread
rzikm marked this conversation as resolved.

[StructLayout(LayoutKind.Sequential)]
internal unsafe struct DNS_PTR_DATA
{
public char* pNameHost; // PCWSTR
}

// Same shape as DNS_PTR_DATA — Windows uses DNS_PTR_DATA for NS/CNAME too,
// but typed aliases keep call sites self-documenting.
#pragma warning disable CS0649 // fields populated via native marshalling
internal unsafe struct DNS_CNAME_DATA
{
public char* pNameHost;
}

internal unsafe struct DNS_NS_DATA
{
public char* pNameHost;
}
#pragma warning restore CS0649

[StructLayout(LayoutKind.Sequential)]
internal unsafe struct DNS_MX_DATA
{
public char* pNameExchange; // PCWSTR
public ushort wPreference;
public ushort Pad;
}

[StructLayout(LayoutKind.Sequential)]
internal unsafe struct DNS_SRV_DATA
{
public char* pNameTarget; // PCWSTR
public ushort wPriority;
public ushort wWeight;
public ushort wPort;
public ushort Pad;
}

[StructLayout(LayoutKind.Sequential)]
internal unsafe struct DNS_TXT_DATA
{
public uint dwStringCount;
// followed by dwStringCount entries of PCWSTR
}

[StructLayout(LayoutKind.Sequential)]
internal unsafe struct DNS_SOA_DATA
{
public char* pNamePrimaryServer; // PCWSTR
public char* pNameAdministrator; // PCWSTR
public uint dwSerialNo;
public uint dwRefresh;
public uint dwRetry;
public uint dwExpire;
public uint dwDefaultTtl;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ internal static partial class Libraries
internal const string Credui = "credui.dll";
internal const string Crypt32 = "crypt32.dll";
internal const string CryptUI = "cryptui.dll";
internal const string Dnsapi = "dnsapi.dll";
internal const string Dsrole = "dsrole.dll";
internal const string Gdi32 = "gdi32.dll";
internal const string HttpApi = "httpapi.dll";
Expand Down
Loading
Loading