-
Notifications
You must be signed in to change notification settings - Fork 5.5k
Implement DnsResolver for Windows #129845
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
rzikm
wants to merge
19
commits into
dotnet:main
Choose a base branch
from
rzikm:dns-resolver
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
19 commits
Select commit
Hold shift + click to select a range
21787a2
Add System.Net.Dns resolver API surface and Windows implementation
rzikm 77dd68c
Add DnsResolver tests
rzikm f0ef4af
Add loopback DNS test suite and restrict DnsQueryEx custom servers to…
rzikm a589fad
Polish tests
rzikm 73acd3c
Add synchronous DnsResolver methods using synchronous DnsQueryEx
rzikm c375219
Unify sync/async DnsResolver core methods into single bool-async methods
rzikm 8196da6
Assert sync DnsResolver core tasks complete synchronously
rzikm 522c396
Refactor Windows DnsResolver logic into DnsResolverPal (PAL pattern)
rzikm 6157140
Add telemetry to DnsResolver
rzikm 6f99d7b
Fix DnsResolver telemetry under-measuring synchronous queries
rzikm 6068489
Address Windows DNS PR review feedback
rzikm 42bb74e
Keep instance DnsResolver.ResolvePtr(IPAddress) overloads
rzikm 6a9f986
More code review feedback
rzikm 023ff8b
Reduce unsafe code in Windows DNS PAL/interop layer
rzikm 4247861
More minor changes
rzikm 3af512a
Address PR review feedback: thread-safety and server validation
rzikm 14b6592
Add synchronous API coverage to DnsResolverTest via bool async theories
rzikm ed8096c
Address PR review feedback: port normalization, skip handling, comments
rzikm 693bd6e
Address PR #129845 review feedback
rzikm File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
70 changes: 70 additions & 0 deletions
70
src/libraries/Common/src/Interop/Windows/Dnsapi/Interop.DnsApi.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| // 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; | ||
|
|
||
| internal delegate void DnsQueryCompletionRoutine(IntPtr pQueryContext, IntPtr pQueryResults); | ||
|
|
||
| [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); | ||
| } | ||
| } |
204 changes: 204 additions & 0 deletions
204
src/libraries/Common/src/Interop/Windows/Dnsapi/Interop.DnsTypes.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,204 @@ | ||||||
| // 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 | ||||||
| { | ||||||
| // DNS_QUERY_REQUEST (v1) — Win8 / Server 2012+ | ||||||
| [StructLayout(LayoutKind.Sequential)] | ||||||
| internal unsafe struct DNS_QUERY_REQUEST | ||||||
| { | ||||||
| public uint Version; | ||||||
| public IntPtr QueryName; // PCWSTR | ||||||
| public ushort QueryType; | ||||||
| public ulong QueryOptions; | ||||||
| public DNS_ADDR_ARRAY* pDnsServerList; | ||||||
| public uint InterfaceIndex; | ||||||
| public IntPtr pQueryCompletionCallback; // PDNS_QUERY_COMPLETION_ROUTINE | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| public IntPtr pQueryContext; | ||||||
| } | ||||||
|
|
||||||
| // DNS_QUERY_REQUEST3 — Win11 Build 22000+ | ||||||
| [StructLayout(LayoutKind.Sequential)] | ||||||
| internal unsafe struct DNS_QUERY_REQUEST3 | ||||||
| { | ||||||
| public uint Version; | ||||||
| public IntPtr QueryName; | ||||||
| public ushort QueryType; | ||||||
| public ulong QueryOptions; | ||||||
| public DNS_ADDR_ARRAY* pDnsServerList; | ||||||
| public uint InterfaceIndex; | ||||||
| public IntPtr 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 IntPtr 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 | ||||||
| // Unsafe.SizeOf<DNS_RECORD_HEADER>() rather than a hard-coded offset. | ||||||
| [StructLayout(LayoutKind.Sequential)] | ||||||
| internal struct DNS_RECORD_HEADER | ||||||
| { | ||||||
| public IntPtr pNext; // DNS_RECORD* | ||||||
| public IntPtr 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 Ip6AddressBytes Ip6Address; | ||||||
| } | ||||||
|
|
||||||
| [System.Runtime.CompilerServices.InlineArray(16)] | ||||||
| internal struct Ip6AddressBytes | ||||||
| { | ||||||
| private byte _element0; | ||||||
| } | ||||||
|
|
||||||
| [StructLayout(LayoutKind.Sequential)] | ||||||
| internal struct DNS_PTR_DATA | ||||||
| { | ||||||
| public IntPtr 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 struct DNS_CNAME_DATA | ||||||
| { | ||||||
| public IntPtr pNameHost; | ||||||
| } | ||||||
|
|
||||||
| internal struct DNS_NS_DATA | ||||||
| { | ||||||
| public IntPtr pNameHost; | ||||||
| } | ||||||
| #pragma warning restore CS0649 | ||||||
|
|
||||||
| [StructLayout(LayoutKind.Sequential)] | ||||||
| internal struct DNS_MX_DATA | ||||||
| { | ||||||
| public IntPtr pNameExchange; // PCWSTR | ||||||
| public ushort wPreference; | ||||||
| public ushort Pad; | ||||||
| } | ||||||
|
|
||||||
| [StructLayout(LayoutKind.Sequential)] | ||||||
| internal struct DNS_SRV_DATA | ||||||
| { | ||||||
| public IntPtr 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 struct DNS_SOA_DATA | ||||||
| { | ||||||
| public IntPtr pNamePrimaryServer; // PCWSTR | ||||||
| public IntPtr pNameAdministrator; // PCWSTR | ||||||
| public uint dwSerialNo; | ||||||
| public uint dwRefresh; | ||||||
| public uint dwRetry; | ||||||
| public uint dwExpire; | ||||||
| public uint dwDefaultTtl; | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And other
PCWSTRfields.