DnsClientX is an async C# library for DNS over UDP, TCP, HTTPS (DoH), and TLS (DoT). It also has a PowerShell module that can be used to query DNS records. It provides a simple way to query DNS records using multiple DNS providers. It supports multiple DNS record types and parallel queries. It's available for .NET 8, .NET 9, .NET Standard 2.0, and .NET Framework 4.7.2.
$ dotnet add package DnsClientXDnsClientX is available as NuGet from the Nuget Gallery and as PowerShell module from PSGallery
📦 NuGet Package
💻 PowerShell Module
🛠️ Project Information
👨💻 Author & Social
DnsClientX is an async C# library for DNS over UDP, TCP, HTTPS (DoH), and TLS (DoT). It also has a PowerShell module that can be used to query DNS records. It provides a simple way to query DNS records using multiple DNS providers. It supports multiple DNS record types and parallel queries. It's available for .NET 8, .NET Standard 2.0, and .NET 4.7.2.
DnsClientX gives you two ways to work with DNS query results, depending on your needs:
Returns DNS data as raw strings - simple and straightforward:
var response = await ClientX.QueryDns("google.com", DnsRecordType.MX, DnsEndpoint.Cloudflare);
foreach (var answer in response.Answers) {
Console.WriteLine($"Raw data: {answer.Data}"); // "10 smtp.google.com"
}
Automatically parses DNS data into strongly-typed .NET objects with properties:
var response = await client.Resolve("google.com", DnsRecordType.MX, typedRecords: true);
foreach (var answer in response.TypedAnswers!) {
if (answer is MxRecord mx) {
Console.WriteLine($"Mail server: {mx.Exchange}, Priority: {mx.Preference}");
}
}
When to use each approach:
Both approaches work with all DNS record types and all query methods - simply add typedRecords: true to any query to get strongly-typed results.
It provides querying multiple DNS Providers.
| Endpoint | DoH | DoQ | DoT | UDP | TCP | DnsCrypt | ODoH |
|---|---|---|---|---|---|---|---|
| System | ✓ | ||||||
| SystemTcp | ✓ | ||||||
| Cloudflare | ✓ | ||||||
| CloudflareWireFormat | ✓ | ||||||
| CloudflareWireFormatPost | ✓ | ||||||
| CloudflareSecurity | ✓ | ||||||
| CloudflareFamily | ✓ | ||||||
| CloudflareQuic | ✓ | ||||||
| CloudflareOdoh | ❌ | ||||||
| ✓ | |||||||
| GoogleWireFormat | ✓ | ||||||
| GoogleWireFormatPost | ✓ | ||||||
| GoogleQuic | ✓ | ||||||
| AdGuard | ✓ | ||||||
| AdGuardFamily | ✓ | ||||||
| AdGuardNonFiltering | ✓ | ||||||
| Quad9 | ✓ | ||||||
| Quad9ECS | ✓ | ||||||
| Quad9Unsecure | ✓ | ||||||
| OpenDNS | ✓ | ||||||
| OpenDNSFamily | ✓ | ||||||
| DnsCryptCloudflare | ❌ | ||||||
| DnsCryptQuad9 | ❌ | ||||||
| DnsCryptRelay | ❌ | ||||||
| RootServer | ✓ | ✓ |
If you want to learn about DNS:
[!WARNING] We try to unify the responses as much as possible for common use cases by translating on the fly. This is because different providers do not store it always the same way. If you find discrepancies please open an issue or better pull request.
Cross-Platform Testing: All tests run simultaneously across Windows, Linux, and macOS to ensure compatibility.
\n line endingsWhen querying DNS records using DnsClientX, you may notice that different DNS providers can return different results for the same domain. This is normal behavior and occurs for several legitimate reasons:
Many popular websites use CDNs (like Cloudflare, Akamai, AWS CloudFront) to serve content from servers geographically closer to users. CDN-backed domains will return different IP addresses based on:
Example: A domain like www.example.com might return:
23.47.124.71, 23.47.124.85185.225.251.105, 185.225.251.40Both responses are correct - they're just optimized for different network paths.
Different DNS providers have distinct characteristics:
| Provider | Hostname | Request Format |
|---|---|---|
| Cloudflare | 1.1.1.1 / 1.0.0.1 | JSON |
8.8.8.8 / 8.8.4.4 | JSON | |
| Quad9 | dns.quad9.net | Wire |
| OpenDNS | 208.67.222.222 / 208.67.220.220 | Wire |
| AdGuard | dns.adguard.com | Wire |
| AdGuardFamily | dns-family.adguard.com | Wire |
| AdGuardNonFiltering | dns-unfiltered.adguard.com | Wire |
These differences can result in:
You should expect consistent results for:
You should expect different results for:
DNS query response times can vary significantly:
DnsClientX implements intelligent timeout and retry logic:
When testing DNS resolution:
google.com, github.com)This behavior is by design and reflects the modern, distributed nature of internet infrastructure. DnsClientX provides tools to work effectively with this reality while maintaining reliable DNS resolution.
DnsClientX provides robust system DNS resolution through DnsEndpoint.System (UDP) and DnsEndpoint.SystemTcp (TCP) endpoints. These endpoints automatically discover and use your system's configured DNS servers with intelligent cross-platform fallback behavior.
Primary Method: Network Interface Detection
NetworkInterface.GetAllNetworkInterfaces()Fallback Method: /etc/resolv.conf Parsing
/etc/resolv.conf filenameserver entriesPublic DNS Fallback: If no system DNS servers are discovered
1.1.1.18.8.8.8The system applies intelligent filtering to ensure reliable DNS servers:
169.254.x.x)fe80::)fec0:: - deprecated)%15) and adds brackets ([::1])DnsEndpoint.System)DnsEndpoint.SystemTcp)| Platform | Primary Method | Fallback Method | Final Fallback |
|---|---|---|---|
| Windows | Network Interface APIs | (Not applicable) | Public DNS |
| Linux | Network Interface APIs | /etc/resolv.conf | Public DNS |
| macOS | Network Interface APIs | /etc/resolv.conf | Public DNS |
| Docker/Container | Network Interface APIs | /etc/resolv.conf | Public DNS |
// Use system DNS with UDP (auto-fallback to TCP)
var response = await ClientX.QueryDns("google.com", DnsRecordType.A, DnsEndpoint.System);
// Use system DNS with TCP only
var response = await ClientX.QueryDns("google.com", DnsRecordType.A, DnsEndpoint.SystemTcp);
// Get system DNS servers programmatically (cached)
var systemDnsServers = SystemInformation.GetDnsFromActiveNetworkCard();
// Refresh the cache when network configuration changes
var refreshedDnsServers = SystemInformation.GetDnsFromActiveNetworkCard(refresh: true);
"No DNS servers found"
ipconfig /all on Windows, cat /etc/resolv.conf on Linux)"Slow response times"
DnsEndpoint.Cloudflare for better performance"Resolution failures in containers"
/etc/resolv.conf should be readable in Linux containersThe system DNS endpoints provide the most compatible and network-environment-aware DNS resolution, making them excellent default choices for applications that need to work across diverse network configurations.
[!IMPORTANT] This library is still in development and there are things that need to be done, tested and fixed. If you would like to help, please do so by opening an issue or a pull request. Things may and will change, as I'm not quite sure what I am doing :-)
DnsClientX provides multiple ways to query DNS records with extensive customization options.
using DnsClientX;
// Simple A record lookup using Cloudflare
var response = await ClientX.QueryDns("google.com", DnsRecordType.A, DnsEndpoint.Cloudflare);
foreach (var answer in response.Answers) {
Console.WriteLine($"{answer.Name} -> {answer.Data}");
}
// Use system-configured DNS servers
var response = await ClientX.QueryDns("google.com", DnsRecordType.A, DnsEndpoint.System);
response.Answers.DisplayToConsole();
using var client = new ClientX(DnsEndpoint.Cloudflare);
var recordTypes = new[] { DnsRecordType.A, DnsRecordType.AAAA, DnsRecordType.MX };
var responses = await client.Resolve("google.com", recordTypes);
responses.DisplayTable();
using var client = new ClientXBuilder()
.WithEndpoint(DnsEndpoint.Cloudflare)
.WithTimeout(5000)
.WithRetryCount(3)
.WithUserAgent("MyApp/1.0")
.Build();
using var client = new ClientX(DnsEndpoint.Cloudflare,
userAgent: "MyApp/1.0",
httpVersion: new Version(2, 0),
maxConnectionsPerServer: 10);
using var client = new ClientX(DnsEndpoint.Custom);
client.EndpointConfiguration.Hostname = "1.1.1.1";
client.EndpointConfiguration.RequestFormat = DnsRequestFormat.DnsOverHttpsJSON;
client.EndpointConfiguration.BaseUri = new Uri("https://1.1.1.1/dns-query");
client.EndpointConfiguration.Port = 443;
When resolving arrays of names with the helper methods, you can optionally cap in-flight requests:
using var client = new ClientX(DnsEndpoint.Cloudflare);
client.EndpointConfiguration.MaxConcurrency = Environment.ProcessorCount; // or any positive integer
// Caps parallelism for array-based helpers like Resolve/ResolveFilter
var responses = await client.Resolve(new[] { "example.com", "google.com" }, DnsRecordType.A);
By default (null), DnsClientX keeps existing behavior and does not impose an explicit concurrency cap.
DnsClientX includes a flexible multi-resolver that can query multiple endpoints using different strategies.
Usage example:
var endpoints = EndpointParser.TryParseMany(new []{
"1.1.1.1:53",
"8.8.8.8:53",
"https://dns.google/dns-query"
}, out var errors);
var options = new MultiResolverOptions {
Strategy = MultiResolverStrategy.FirstSuccess,
MaxParallelism = 4,
RespectEndpointTimeout = true
};
IDnsMultiResolver mr = new DnsMultiResolver(endpoints, options);
var response = await mr.QueryAsync("example.com", DnsRecordType.A);
Console.WriteLine($"RTT: {response.RoundTripTime.TotalMilliseconds} ms via {response.UsedTransport} @ {response.UsedEndpoint}");
// Batch API (preserves input order, isolates failures)
var results = await mr.QueryBatchAsync(new [] { "a.com", "b.com", "c.com" }, DnsRecordType.A);
Notes:
AllowTcpFallback is true, TCP is used automatically.Response.TtlMin and Response.TtlAvg expose TTL metrics derived from the answer set.MaxParallelism: Caps total in-flight queries issued by the multi-resolver.PerEndpointMaxInFlight: Caps concurrent queries per endpoint (e.g., limit to 4 per DNS server).var opts = new MultiResolverOptions {
EnableResponseCache = true,
CacheExpiration = TimeSpan.FromSeconds(30), // fallback if TTL not present
MinCacheTtl = TimeSpan.FromSeconds(1),
MaxCacheTtl = TimeSpan.FromMinutes(60)
};
Resolve-Dns -Name 'example.com' -Type A -DnsProvider Cloudflare,Google \
-ResolverStrategy FirstSuccess -ResponseCache -CacheExpirationSeconds 30 -MinCacheTtlSeconds 1 -MaxCacheTtlSeconds 3600
Balance across providers with caps:
Resolve-Dns -Name @('a.com','b.com') -Type A \
-DnsProvider System,Cloudflare,Quad9 \
-ResolverStrategy RoundRobin -MaxParallelism 32 -PerEndpointMaxInFlight 4
First success across mixed endpoints:
Resolve-Dns -Name 'example.com' -Type A \
-ResolverEndpoint '1.1.1.1:53','https://dns.google/dns-query' -ResolverStrategy FirstSuccess
Clear-DnsMultiResolverCache
Clear-DnsMultiResolverCache -ResolverDnsProvider Cloudflare,Google
Clear-DnsMultiResolverCache -ResolverEndpoint '1.1.1.1:53','https://dns.google/dns-query'
DnsClientX can automatically parse DNS records into strongly-typed objects, making it easier to work with structured data like SPF, DMARC, DKIM, and other complex record types.
using var client = new ClientX(DnsEndpoint.Cloudflare);
// Get typed records instead of raw strings
var response = await client.Resolve("google.com", DnsRecordType.MX, typedRecords: true);
// Access strongly-typed properties
foreach (var typedAnswer in response.TypedAnswers!) {
if (typedAnswer is MxRecord mx) {
Console.WriteLine($"Mail Server: {mx.Exchange}, Priority: {mx.Preference}");
}
}
Available Typed Record Classes:
| Record Type | Typed Class | Key Properties |
|---|---|---|
| A | ARecord | Address (IPAddress) |
| AAAA | AAAARecord | Address (IPAddress) |
| MX | MxRecord | Exchange, Preference |
| SRV | SrvRecord | Target, Port, Priority, Weight |
| TXT | TxtRecord, SpfRecord, DmarcRecord, DkimRecord | Parsed content |
| CAA | CaaRecord | Flags, Tag, Value |
| TLSA | TlsaRecord | CertificateUsage, Selector, MatchingType, AssociationData |
| SOA | SoaRecord | PrimaryNameServer, ResponsibleEmail, Serial, etc. |
| CNAME | CNameRecord | Target |
| NS | NsRecord | NameServer |
| PTR | PtrRecord | DomainName |
| DNSKEY | DnsKeyRecord | Flags, Protocol, Algorithm, PublicKey |
| DS | DsRecord | KeyTag, Algorithm, DigestType, Digest |
| NAPTR | NaptrRecord | Order, Preference, Flags, Services, Regexp, Replacement |
| LOC | LocRecord | Latitude, Longitude, Altitude, Size, etc. |
Specialized TXT Record Parsing:
using var client = new ClientX(DnsEndpoint.Cloudflare);
// SPF Records
var spfResponse = await client.Resolve("google.com", DnsRecordType.TXT, typedRecords: true);
foreach (var answer in spfResponse.TypedAnswers!) {
if (answer is SpfRecord spf) {
Console.WriteLine($"SPF Mechanisms: {string.Join(", ", spf.Mechanisms)}");
}
}
// DMARC Records
var dmarcResponse = await client.Resolve("_dmarc.google.com", DnsRecordType.TXT, typedRecords: true);
foreach (var answer in dmarcResponse.TypedAnswers!) {
if (answer is DmarcRecord dmarc) {
Console.WriteLine($"DMARC Policy: {dmarc.Tags["p"]}");
Console.WriteLine($"DMARC Percentage: {dmarc.Tags.GetValueOrDefault("pct", "100")}");
}
}
// DKIM Records
var dkimResponse = await client.Resolve("default._domainkey.google.com", DnsRecordType.TXT, typedRecords: true);
foreach (var answer in dmarcResponse.TypedAnswers!) {
if (answer is DkimRecord dkim) {
Console.WriteLine($"DKIM Key Type: {dkim.Tags.GetValueOrDefault("k", "rsa")}");
Console.WriteLine($"DKIM Public Key: {dkim.Tags["p"]}");
}
}
Working with Service Records:
using var client = new ClientX(DnsEndpoint.Cloudflare);
// SRV Records for service discovery
var srvResponse = await client.Resolve("_sip._tcp.example.com", DnsRecordType.SRV, typedRecords: true);
foreach (var answer in srvResponse.TypedAnswers!) {
if (answer is SrvRecord srv) {
Console.WriteLine($"Service: {srv.Target}:{srv.Port}");
Console.WriteLine($"Priority: {srv.Priority}, Weight: {srv.Weight}");
}
}
// TLSA Records for certificate validation
var tlsaResponse = await client.Resolve("_443._tcp.example.com", DnsRecordType.TLSA, typedRecords: true);
foreach (var answer in tlsaResponse.TypedAnswers!) {
if (answer is TlsaRecord tlsa) {
Console.WriteLine($"Certificate Usage: {tlsa.CertificateUsage}");
Console.WriteLine($"Selector: {tlsa.Selector}");
Console.WriteLine($"Matching Type: {tlsa.MatchingType}");
Console.WriteLine($"Association Data: {tlsa.AssociationData}");
}
}
Certificate Authority Authorization (CAA):
using var client = new ClientX(DnsEndpoint.Cloudflare);
var caaResponse = await client.Resolve("google.com", DnsRecordType.CAA, typedRecords: true);
foreach (var answer in caaResponse.TypedAnswers!) {
if (answer is CaaRecord caa) {
Console.WriteLine($"CAA Tag: {caa.Tag}");
Console.WriteLine($"CAA Value: {caa.Value}");
Console.WriteLine($"Critical: {(caa.Flags & 128) != 0}");
}
}
Location Records:
using var client = new ClientX(DnsEndpoint.Cloudflare);
var locResponse = await client.Resolve("example.com", DnsRecordType.LOC, typedRecords: true);
foreach (var answer in locResponse.TypedAnswers!) {
if (answer is LocRecord loc) {
Console.WriteLine($"Location: {loc.Latitude:F6}, {loc.Longitude:F6}");
Console.WriteLine($"Altitude: {loc.Altitude}m");
Console.WriteLine($"Size: {loc.Size}m");
}
}
Controlling TXT Record Parsing:
using var client = new ClientX(DnsEndpoint.Cloudflare);
// Force all TXT records to be returned as simple TxtRecord objects
// instead of parsing into specialized types like SpfRecord, DmarcRecord, etc.
var response = await client.Resolve("google.com", DnsRecordType.TXT,
typedRecords: true,
typedTxtAsTxt: true);
foreach (var answer in response.TypedAnswers!) {
if (answer is TxtRecord txt) {
Console.WriteLine($"TXT Data: {string.Join(" ", txt.Strings)}");
}
}
Mixed Record Type Handling:
using var client = new ClientX(DnsEndpoint.Cloudflare);
var response = await client.Resolve("google.com",
new[] { DnsRecordType.A, DnsRecordType.MX, DnsRecordType.TXT },
typedRecords: true);
foreach (var answer in response.TypedAnswers!) {
switch (answer) {
case ARecord a:
Console.WriteLine($"A Record: {a.Address}");
break;
case MxRecord mx:
Console.WriteLine($"MX Record: {mx.Exchange} (Priority: {mx.Preference})");
break;
case SpfRecord spf:
Console.WriteLine($"SPF Record: {string.Join(" ", spf.Mechanisms)}");
break;
case TxtRecord txt:
Console.WriteLine($"TXT Record: {string.Join(" ", txt.Strings)}");
break;
case UnknownRecord unknown:
Console.WriteLine($"Unknown Record: {unknown.Data}");
break;
}
}
using var client = new ClientX(DnsEndpoint.Cloudflare);
var response = await client.Resolve("google.com", DnsRecordType.A,
requestDnsSec: true,
validateDnsSec: true);
Console.WriteLine($"DNSSEC Valid: {response.IsSecure}");
using var client = new ClientX(DnsEndpoint.Cloudflare);
// Query server1.example.com, server2.example.com, server3.example.com
var responses = await client.ResolvePattern("server[1-3].example.com", DnsRecordType.A);
responses.DisplayTable();
using var client = new ClientX(DnsEndpoint.Cloudflare);
// Find TXT records containing "v=spf1"
var response = await client.ResolveFilter("google.com", DnsRecordType.TXT, "v=spf1");
response?.DisplayTable();
var endpoints = new[] {
DnsEndpoint.Cloudflare,
DnsEndpoint.Google,
DnsEndpoint.Quad9
};
var tasks = endpoints.Select(async endpoint => {
using var client = new ClientX(endpoint);
return await client.Resolve("google.com", DnsRecordType.A);
}).ToArray();
var responses = await Task.WhenAll(tasks);
foreach (var response in responses) {
response?.DisplayTable();
}
using var client = new ClientX(DnsEndpoint.Cloudflare);
var domains = new[] { "google.com", "github.com", "microsoft.com" };
var recordTypes = new[] { DnsRecordType.A, DnsRecordType.AAAA };
await foreach (var response in client.ResolveStream(domains, recordTypes)) {
Console.WriteLine($"Resolved: {response.Question.Name} ({response.Question.Type})");
response.DisplayTable();
}
// JSON format
var response = await ClientX.QueryDns("google.com", DnsRecordType.A,
DnsEndpoint.Cloudflare);
// Wire format
var response = await ClientX.QueryDns("google.com", DnsRecordType.A,
DnsEndpoint.CloudflareWireFormat);
var response = await ClientX.QueryDns("google.com", DnsRecordType.A,
DnsEndpoint.CloudflareQuic);
// System DNS with UDP (fallback to TCP)
var response = await ClientX.QueryDns("google.com", DnsRecordType.A,
DnsEndpoint.System);
// Force TCP
var response = await ClientX.QueryDns("google.com", DnsRecordType.A,
DnsEndpoint.SystemTcp);
var response = await ClientX.QueryDns("google.com", DnsRecordType.A,
DnsEndpoint.RootServer);
using var client = new ClientX(DnsEndpoint.Cloudflare);
var latency = await client.MeasureLatencyAsync();
Console.WriteLine($"Latency: {latency.TotalMilliseconds}ms");
using var client = new ClientX(DnsEndpoint.Cloudflare) {
RetryCount = 3,
Timeout = TimeSpan.FromSeconds(5)
};
var endpoints = new[] {
DnsEndpoint.Cloudflare,
DnsEndpoint.Google,
DnsEndpoint.System
};
DnsResponse? response = null;
foreach (var endpoint in endpoints) {
try {
using var client = new ClientX(endpoint);
response = await client.Resolve("google.com", DnsRecordType.A);
break; // Success, exit loop
} catch (Exception ex) {
Console.WriteLine($"Failed with {endpoint}: {ex.Message}");
// Continue to next endpoint
}
}
using var client = new ClientX(DnsEndpoint.Cloudflare);
// Discover all services in a domain
var services = await client.DiscoverServices("example.com");
foreach (var service in services) {
Console.WriteLine($"Service: {service.ServiceName} -> {service.Target}:{service.Port}");
}
// Query specific service
var srvRecords = await client.ResolveServiceAsync("ldap", "tcp", "example.com", resolveHosts: true);
foreach (var srv in srvRecords) {
Console.WriteLine($"Server: {srv.Target}:{srv.Port} (Priority: {srv.Priority})");
if (srv.Addresses != null) {
Console.WriteLine($"IPs: {string.Join(", ", srv.Addresses)}");
}
}
// Full zone transfer
using var client = new ClientX("127.0.0.1", DnsRequestFormat.DnsOverTCP) {
EndpointConfiguration = { Port = 5353 }
};
var zoneRecords = await client.ZoneTransferAsync("example.com");
foreach (var rrset in zoneRecords) {
Console.WriteLine($"Zone chunk {rrset.Index}: {string.Join(", ", rrset.Records)}");
}
// Streaming zone transfer
await foreach (var rrset in client.ZoneTransferStreamAsync("example.com")) {
Console.WriteLine($"Received: {string.Join(", ", rrset)}");
}
using var client = new ClientX("127.0.0.1", DnsRequestFormat.DnsOverTCP) {
EndpointConfiguration = { Port = 5353 }
};
// Add a record
await client.UpdateAsync("example.com", "www", DnsRecordType.A, "192.0.2.1", ttl: 300);
// Delete a record
await client.UpdateAsync("example.com", "www", DnsRecordType.A, delete: true);
using var client = new ClientX("224.0.0.251", DnsRequestFormat.Multicast) {
EndpointConfiguration = { Port = 5353 }
};
var response = await client.Resolve("printer.local", DnsRecordType.A);
using var client = new ClientX(DnsEndpoint.Cloudflare) {
Debug = true // Enables detailed logging
};
try {
using var client = new ClientX(DnsEndpoint.Cloudflare);
var response = await client.Resolve("nonexistent.domain", DnsRecordType.A);
if (response.HasError) {
Console.WriteLine($"DNS Error: {response.ErrorMessage}");
} else if (!response.Answers.Any()) {
Console.WriteLine("No records found");
} else {
response.DisplayTable();
}
} catch (TimeoutException) {
Console.WriteLine("Query timed out");
} catch (Exception ex) {
Console.WriteLine($"Unexpected error: {ex.Message}");
}
DnsClientX provides a comprehensive PowerShell module with multiple cmdlets for DNS operations.
# Install from PowerShell Gallery
Install-Module -Name DnsClientX -Scope CurrentUser
# Import the module
Import-Module DnsClientX
| Cmdlet | Alias | Description |
|---|---|---|
Resolve-Dns | Resolve-DnsQuery | Query DNS records with various providers |
Get-DnsService | Discover DNS services (DNS-SD) | |
Find-DnsService | Find specific DNS services | |
Get-DnsZone | Get-DnsZoneTransfer | Perform DNS zone transfers |
Invoke-DnsUpdate | Update DNS records (Dynamic DNS) |
# Simple A record lookup
Resolve-Dns -Name 'google.com' -Type A | Format-Table
# Query with specific DNS provider
Resolve-Dns -Name 'google.com' -Type A -DnsProvider Cloudflare | Format-Table
# Multiple domains at once
Resolve-Dns -Name 'google.com', 'github.com', 'microsoft.com' -Type A | Format-Table
# Multiple record types
Resolve-Dns -Name 'google.com' -Type A, AAAA, MX | Format-Table
# Use system-configured DNS servers
Resolve-Dns -Name 'google.com' -Type A -DnsProvider System -Verbose | Format-Table
# Query with custom DNS server
Resolve-Dns -Name 'google.com' -Type A -Server '8.8.8.8' | Format-Table
# Multiple servers with fallback
Resolve-Dns -Name 'google.com' -Type A -Server '1.1.1.1', '8.8.8.8' -Fallback | Format-Table
# Request and validate DNSSEC
Resolve-Dns -Name 'google.com' -Type A -DnsProvider Cloudflare -RequestDnsSec -ValidateDnsSec | Format-Table
# Query DNSSEC-specific records
Resolve-Dns -Name 'google.com' -Type DS, DNSKEY -DnsProvider Cloudflare | Format-Table
# Query multiple servers using pattern
Resolve-Dns -Pattern 'server[1-3].example.com' -Type A -DnsProvider Cloudflare | Format-Table
# Range patterns
Resolve-Dns -Pattern 'host[10-15].example.com' -Type A | Format-Table
# Get complete DNS response information
$Response = Resolve-Dns -Name 'google.com' -Type A -DnsProvider Cloudflare -FullResponse
# Display different parts of the response
$Response.Questions | Format-Table
$Response.Answers | Format-Table
$Response.AnswersMinimal | Format-Table
$Response.Authority | Format-Table
$Response.Additional | Format-Table
# Check response metadata
Write-Host "Response Time: $($Response.ResponseTime)ms"
Write-Host "Server: $($Response.Server)"
Write-Host "DNSSEC: $($Response.IsSecure)"
# Custom timeout (in milliseconds)
Resolve-Dns -Name 'google.com' -Type A -TimeOut 5000 | Format-Table
# With verbose output for troubleshooting
Resolve-Dns -Name 'google.com' -Type A -DnsProvider Cloudflare -Verbose | Format-Table
# Standard Cloudflare (JSON)
Resolve-Dns -Name 'google.com' -Type A -DnsProvider Cloudflare | Format-Table
# Cloudflare Wire Format
Resolve-Dns -Name 'google.com' -Type A -DnsProvider CloudflareWireFormat | Format-Table
# Cloudflare Security (blocks malware)
Resolve-Dns -Name 'google.com' -Type A -DnsProvider CloudflareSecurity | Format-Table
# Cloudflare Family (blocks malware + adult content)
Resolve-Dns -Name 'google.com' -Type A -DnsProvider CloudflareFamily | Format-Table
# Google DNS
Resolve-Dns -Name 'google.com' -Type A -DnsProvider Google | Format-Table
# Quad9 (security-focused)
Resolve-Dns -Name 'google.com' -Type A -DnsProvider Quad9 | Format-Table
# OpenDNS
Resolve-Dns -Name 'google.com' -Type A -DnsProvider OpenDNS | Format-Table
# AdGuard (ad-blocking)
Resolve-Dns -Name 'google.com' -Type A -DnsProvider AdGuard | Format-Table
# MX records for email
Resolve-Dns -Name 'google.com' -Type MX | Format-Table
# SPF records (TXT records containing v=spf1)
Resolve-Dns -Name 'google.com' -Type TXT | Where-Object { $_.Data -like "*v=spf1*" } | Format-Table
# TLSA records for certificate validation
Resolve-Dns -Name '_443._tcp.google.com' -Type TLSA | Format-Table
# CAA records for certificate authority authorization
Resolve-Dns -Name 'google.com' -Type CAA | Format-Table
# SRV records for services
Resolve-Dns -Name '_sip._tcp.example.com' -Type SRV | Format-Table
# NAPTR records for service discovery
Resolve-Dns -Name 'example.com' -Type NAPTR | Format-Table
# Discover all services in a domain
Get-DnsService -Domain 'example.com' | Format-Table
# Find specific service types
Find-DnsService -Domain 'example.com' -Service 'http' -Protocol 'tcp' | Format-Table
# Full zone transfer (AXFR)
Get-DnsZone -Zone 'example.com' -Server '127.0.0.1' -Port 5353 | Format-Table
# Zone transfer with specific server
Get-DnsZoneTransfer -Zone 'example.com' -Server 'ns1.example.com' | Format-Table
# Add a DNS record
Invoke-DnsUpdate -Zone 'example.com' -Server '127.0.0.1' -Name 'www' -Type A -Data '192.0.2.1' -Ttl 300
# Delete a DNS record
Invoke-DnsUpdate -Zone 'example.com' -Server '127.0.0.1' -Name 'www' -Type A -Delete
# Update an existing record
Invoke-DnsUpdate -Zone 'example.com' -Server '127.0.0.1' -Name 'www' -Type A -Data '192.0.2.2' -Ttl 600
# Test connectivity to DNS servers
$Servers = @('1.1.1.1', '8.8.8.8', '9.9.9.9')
foreach ($Server in $Servers) {
try {
$Result = Resolve-Dns -Name 'google.com' -Type A -Server $Server -TimeOut 2000
Write-Host "✓ $Server responded in $($Result.ResponseTime)ms" -ForegroundColor Green
} catch {
Write-Host "✗ $Server failed: $($_.Exception.Message)" -ForegroundColor Red
}
}
# Enable verbose output for debugging
Resolve-Dns -Name 'google.com' -Type A -DnsProvider Cloudflare -Verbose
# Test with fallback servers
Resolve-Dns -Name 'google.com' -Type A -Server '1.1.1.1', '8.8.8.8' -Fallback -RandomServer -Verbose
# Monitor website DNS resolution across providers
$Providers = @('Cloudflare', 'Google', 'Quad9', 'OpenDNS')
$Domain = 'example.com'
foreach ($Provider in $Providers) {
$Result = Resolve-Dns -Name $Domain -Type A -DnsProvider $Provider
Write-Host "$Provider -> $($Result.Data -join ', ')"
}
# Complete email server DNS check
$Domain = 'example.com'
Write-Host "=== Email DNS Records for $Domain ===" -ForegroundColor Cyan
# MX Records
Write-Host "`nMX Records:" -ForegroundColor Yellow
Resolve-Dns -Name $Domain -Type MX | Format-Table Name, Priority, Data
# SPF Records
Write-Host "SPF Records:" -ForegroundColor Yellow
Resolve-Dns -Name $Domain -Type TXT | Where-Object { $_.Data -like "*v=spf1*" } | Format-Table Name, Data
# DMARC Records
Write-Host "DMARC Records:" -ForegroundColor Yellow
Resolve-Dns -Name "_dmarc.$Domain" -Type TXT | Format-Table Name, Data
# DKIM Records (example)
Write-Host "DKIM Records:" -ForegroundColor Yellow
Resolve-Dns -Name "default._domainkey.$Domain" -Type TXT | Format-Table Name, Data
# DNS security assessment
$Domain = 'example.com'
Write-Host "=== DNS Security Assessment for $Domain ===" -ForegroundColor Cyan
# DNSSEC validation
$DnssecResult = Resolve-Dns -Name $Domain -Type A -DnsProvider Cloudflare -RequestDnsSec -ValidateDnsSec
Write-Host "DNSSEC Status: $(if($DnssecResult.IsSecure) { 'SECURE' } else { 'NOT SECURE' })" -ForegroundColor $(if($DnssecResult.IsSecure) { 'Green' } else { 'Red' })
# CAA Records
Write-Host "`nCAA Records:" -ForegroundColor Yellow
Resolve-Dns -Name $Domain -Type CAA | Format-Table Name, Data
# Check for common security TXT records
Write-Host "Security Policy Records:" -ForegroundColor Yellow
Resolve-Dns -Name $Domain -Type TXT | Where-Object { $_.Data -match "(v=spf1|v=DMARC1|google-site-verification)" } | Format-Table Name, Data
DnsClientX includes a command-line interface for quick DNS queries and scripting.
The CLI is distributed as DnsClientX.exe and can be built from source or downloaded from releases.
# Build from source
dotnet build DnsClientX.Cli/DnsClientX.Cli.csproj -c Release
# The executable will be available at:
# DnsClientX.Cli/bin/Release/net8.0/DnsClientX.exe (Windows)
# DnsClientX.Cli/bin/Release/net8.0/DnsClientX (Linux/macOS)
# Or build the entire solution
dotnet build DnsClientX.sln -c Release
# Simple A record query
DnsClientX.exe google.com A
# Query with specific provider
DnsClientX.exe google.com A --provider Cloudflare
# Multiple record types
DnsClientX.exe google.com A,AAAA,MX
# Custom DNS server
DnsClientX.exe google.com A --server 8.8.8.8
# JSON output format
DnsClientX.exe google.com A --format json
# Verbose output
DnsClientX.exe google.com A --verbose
# DNSSEC validation
DnsClientX.exe google.com A --dnssec --validate
# Custom timeout (milliseconds)
DnsClientX.exe google.com A --timeout 5000
# Multiple domains
DnsClientX.exe google.com,github.com A
# Pattern-based queries
DnsClientX.exe "server[1-3].example.com" A
# Zone transfer
DnsClientX.exe example.com AXFR --server 127.0.0.1 --port 5353
# Service discovery
DnsClientX.exe example.com SRV --service http --protocol tcp
# Table format (default)
DnsClientX.exe google.com A
# JSON format
DnsClientX.exe google.com A --format json
# CSV format
DnsClientX.exe google.com A --format csv
# Raw format (minimal)
DnsClientX.exe google.com A --format raw
#!/bin/bash
# Check multiple domains for A records
domains=("google.com" "github.com" "microsoft.com")
for domain in "${domains[@]}"; do
echo "Checking $domain..."
DnsClientX.exe "$domain" A --provider Cloudflare
done
# PowerShell script for DNS monitoring
$Domains = @('google.com', 'github.com', 'microsoft.com')
$Providers = @('Cloudflare', 'Google', 'Quad9')
foreach ($Domain in $Domains) {
foreach ($Provider in $Providers) {
Write-Host "Testing $Domain with $Provider" -ForegroundColor Cyan
& DnsClientX.exe $Domain A --provider $Provider --format json | ConvertFrom-Json
}
}
Please consider sharing a post about DnsClientX and the value it provides. It really does help!
This project general idea is based on DnsOverHttps by @akac which was an inspiration for DnsClientX.