dns_client 1.3.1
dns_client: ^1.3.1 copied to clipboard
Dart DNS-over-HTTPS (DoH) client supporting Google, Cloudflare, AdGuard, and Quad9 providers.
dns_client #
Dart implementation of DNS-over-HTTPS (DoH).
Features #
- Multiple DNS Providers - Google, Cloudflare, AdGuard, NextDNS, Quad9, OpenDNS, or custom DoH endpoints
- Wire Format Support - RFC 1035/8484 compliant DNS wire format with HTTP/2 for Quad9
- 36 DNS Record Types - A, AAAA, MX, TXT, SRV, CAA, HTTPS, SVCB, DNSKEY, DS, and more
- Privacy Protection - Hide client IP from authoritative nameservers
- Error Handling - Detailed exceptions for DNS and HTTP failures
- Dart 3 Support - Requires Dart SDK 3.7.0+
Installation #
Add to your pubspec.yaml:
dependencies:
dns_client: ^1.0.0
Then run:
dart pub get
Quick Start #
import 'package:dns_client/dns_client.dart';
void main() async {
final dns = DnsOverHttps.google();
final addresses = await dns.lookup('google.com');
for (final address in addresses) {
print(address.address);
}
dns.close();
}
Usage Examples #
Basic DNS Lookup #
// Using Google DNS
final dns = DnsOverHttps.google();
final addresses = await dns.lookup('example.com');
dns.close();
// Using Cloudflare DNS
final dns = DnsOverHttps.cloudflare();
final addresses = await dns.lookup('example.com');
dns.close();
// Using AdGuard DNS (blocks ads and trackers)
final dns = DnsOverHttps.adguard();
final addresses = await dns.lookup('example.com');
dns.close();
// Using AdGuard DNS (no filtering)
final dns = DnsOverHttps.adguardNonFiltering();
final addresses = await dns.lookup('example.com');
dns.close();
// Using AdGuard DNS (family protection)
final dns = DnsOverHttps.adguardFamily();
final addresses = await dns.lookup('example.com');
dns.close();
// Using Quad9 DNS (malware blocking + DNSSEC)
final dns = DnsOverHttpsWire.quad9();
final addresses = await dns.lookup('example.com');
dns.close();
// Using Quad9 DNS with ECS (geolocation hints)
final dns = DnsOverHttpsWire.quad9Ecs();
final addresses = await dns.lookup('example.com');
dns.close();
// Using Quad9 DNS (no filtering)
final dns = DnsOverHttpsWire.quad9Unsecured();
final addresses = await dns.lookup('example.com');
dns.close();
// Using NextDNS
final dns = DnsOverHttps.nextdns();
final addresses = await dns.lookup('example.com');
dns.close();
// Using NextDNS with custom configuration
final dns = DnsOverHttps.nextdns(configId: 'abc123');
final addresses = await dns.lookup('example.com');
dns.close();
// Using OpenDNS
final dns = DnsOverHttpsWire.opendns();
final addresses = await dns.lookup('example.com');
dns.close();
// Using OpenDNS FamilyShield (blocks adult content)
final dns = DnsOverHttpsWire.opendnsFamilyShield();
final addresses = await dns.lookup('example.com');
dns.close();
Query Specific Record Types #
final dns = DnsOverHttps.google();
// MX records (mail servers)
final mxRecords = await dns.lookupDataByRRType('example.com', RRType.MX);
print('Mail servers: $mxRecords');
// TXT records (SPF, DKIM, domain verification)
final txtRecords = await dns.lookupDataByRRType('example.com', RRType.TXT);
print('TXT records: $txtRecords');
// SRV records (service discovery)
final srvRecords = await dns.lookupDataByRRType(
'_jmap._tcp.fastmail.com',
RRType.SRV,
);
print('SRV records: $srvRecords');
// CAA records (certificate authority authorization)
final caaRecords = await dns.lookupDataByRRType('example.com', RRType.CAA);
print('CAA records: $caaRecords');
// HTTPS records (service binding)
final httpsRecords = await dns.lookupDataByRRType('example.com', RRType.HTTPS);
print('HTTPS records: $httpsRecords');
dns.close();
Custom DNS Provider #
final dns = DnsOverHttps(
'https://dns.quad9.net:5053/dns-query',
timeout: Duration(seconds: 10),
);
final addresses = await dns.lookup('example.com');
dns.close();
Privacy Settings #
Hide your IP address from authoritative nameservers:
final dns = DnsOverHttps(
'https://dns.google/resolve',
maximalPrivacy: true, // Sets edns_client_subnet=0.0.0.0/0
);
final addresses = await dns.lookup('example.com');
dns.close();
Timeout Configuration #
final dns = DnsOverHttps.google(
timeout: Duration(seconds: 10), // Default is 5 seconds
);
final addresses = await dns.lookup('example.com');
dns.close();
Error Handling #
final dns = DnsOverHttps.google();
try {
final records = await dns.lookupDataByRRType(
'nonexistent.invalid',
RRType.A,
);
} on DnsLookupException catch (e) {
// DNS-level error (NXDOMAIN, SERVFAIL, etc.)
print('DNS error: ${e.message}');
print('Hostname: ${e.hostname}');
print('Status: ${e.status}'); // 3 = NXDOMAIN, 2 = SERVFAIL
} on DnsHttpException catch (e) {
// HTTP-level error
print('HTTP error: ${e.statusCode}');
print('Message: ${e.message}');
} finally {
dns.close();
}
Full Response Inspection #
Access the complete DNS response for detailed information:
final dns = DnsOverHttps.google();
final record = await dns.lookupHttpsByRRType('example.com', RRType.MX);
if (record.isSuccess) {
print('Status: ${record.status}'); // 0 = NOERROR
for (final answer in record.answer ?? []) {
print('Name: ${answer.name}');
print('Type: ${answer.type}');
print('TTL: ${answer.TTL} seconds');
print('Data: ${answer.data}');
}
} else if (record.isNxDomain) {
print('Domain does not exist');
} else if (record.isServerFailure) {
print('Server failure');
}
dns.close();
API Reference #
DnsOverHttps #
Constructors:
| Constructor | Description |
|---|---|
DnsOverHttps(url, {timeout, maximalPrivacy}) |
Custom DoH endpoint |
DnsOverHttps.google({timeout}) |
Google DNS (dns.google) |
DnsOverHttps.cloudflare({timeout}) |
Cloudflare DNS (1.1.1.1) |
DnsOverHttps.adguard({timeout}) |
AdGuard DNS (blocks ads/trackers) |
DnsOverHttps.adguardNonFiltering({timeout}) |
AdGuard DNS (no filtering) |
DnsOverHttps.adguardFamily({timeout}) |
AdGuard DNS (family protection) |
DnsOverHttps.nextdns({configId, timeout}) |
NextDNS (optional custom config) |
DnsOverHttps.nextdnsAnycast({configId, timeout}) |
NextDNS Anycast endpoint |
Methods:
| Method | Returns | Description |
|---|---|---|
lookup(hostname) |
Future<List<InternetAddress>> |
Resolve hostname to IP addresses |
lookupDataByRRType(hostname, rrType) |
Future<List<String>> |
Query specific record type |
lookupHttpsByRRType(hostname, rrType) |
Future<DnsRecord> |
Get full DNS response |
close({force}) |
void |
Shutdown HTTP client |
DnsOverHttpsWire #
Wire format DoH client using HTTP/2 (RFC 1035/8484). Required for Quad9 and OpenDNS.
Constructors:
| Constructor | Description |
|---|---|
DnsOverHttpsWire(url, {timeout}) |
Custom wire format DoH endpoint |
DnsOverHttpsWire.quad9({timeout}) |
Quad9 DNS (malware blocking + DNSSEC) |
DnsOverHttpsWire.quad9Ecs({timeout}) |
Quad9 DNS with EDNS Client Subnet |
DnsOverHttpsWire.quad9Unsecured({timeout}) |
Quad9 DNS (no filtering) |
DnsOverHttpsWire.opendns({timeout}) |
OpenDNS (enterprise-grade) |
DnsOverHttpsWire.opendnsFamilyShield({timeout}) |
OpenDNS FamilyShield (blocks adult) |
Methods:
| Method | Returns | Description |
|---|---|---|
lookup(hostname) |
Future<List<InternetAddress>> |
Resolve hostname to IP addresses |
lookupDataByRRType(hostname, rrType) |
Future<List<String>> |
Query specific record type |
lookupWire(hostname, rrType) |
Future<DnsRecord> |
Get full DNS response |
close({force}) |
Future<void> |
Shutdown HTTP/2 client |
Supported Record Types (RRType) #
| Type | Constant | Value | Description |
|---|---|---|---|
| A | RRType.A |
1 | IPv4 address |
| NS | RRType.NS |
2 | Name server |
| CNAME | RRType.CNAME |
5 | Canonical name (alias) |
| SOA | RRType.SOA |
6 | Start of authority |
| PTR | RRType.PTR |
12 | Reverse DNS pointer |
| HINFO | RRType.HINFO |
13 | Host information |
| MX | RRType.MX |
15 | Mail exchanger |
| TXT | RRType.TXT |
16 | Text record |
| RP | RRType.RP |
17 | Responsible person |
| AFSDB | RRType.AFSDB |
18 | AFS database |
| KEY | RRType.KEY |
25 | Security key |
| AAAA | RRType.AAAA |
28 | IPv6 address |
| LOC | RRType.LOC |
29 | Geographic location |
| SRV | RRType.SRV |
33 | Service location |
| NAPTR | RRType.NAPTR |
35 | Naming authority pointer |
| KX | RRType.KX |
36 | Key exchanger |
| CERT | RRType.CERT |
37 | Certificate |
| APL | RRType.APL |
42 | Address prefix list |
| DS | RRType.DS |
43 | Delegation signer (DNSSEC) |
| IPSECKEY | RRType.IPSECKEY |
45 | IPsec key |
| NSEC | RRType.NSEC |
47 | Next secure (DNSSEC) |
| DNSKEY | RRType.DNSKEY |
48 | DNS key (DNSSEC) |
| DHCID | RRType.DHCID |
49 | DHCP identifier |
| NSEC3 | RRType.NSEC3 |
50 | Hashed denial (DNSSEC) |
| NSEC3PARAM | RRType.NSEC3PARAM |
51 | NSEC3 parameters |
| SMIMEA | RRType.SMIMEA |
53 | S/MIME certificate |
| HIP | RRType.HIP |
55 | Host identity protocol |
| CDS | RRType.CDS |
59 | Child DS (DNSSEC) |
| SVCB | RRType.SVCB |
64 | Service binding |
| HTTPS | RRType.HTTPS |
65 | HTTPS binding |
| EUI48 | RRType.EUI48 |
108 | MAC address (48-bit) |
| EUI64 | RRType.EUI64 |
109 | MAC address (64-bit) |
| URI | RRType.URI |
256 | URI mapping |
| CAA | RRType.CAA |
257 | CA authorization |
| TA | RRType.TA |
32768 | Trust anchor |
| DLV | RRType.DLV |
32769 | DNSSEC lookaside |
Custom record types:
// TLSA record (type 52)
final tlsaType = RRType('TLSA', 52);
final tlsaRecords = await dns.lookupDataByRRType('example.com', tlsaType);
Response Classes #
DnsRecord:
| Property | Type | Description |
|---|---|---|
status |
int |
DNS response code |
answer |
List<Answer>? |
DNS answers |
isSuccess |
bool |
True if status == 0 |
isFailure |
bool |
True if status != 0 |
isNxDomain |
bool |
True if status == 3 |
isServerFailure |
bool |
True if status == 2 |
Answer:
| Property | Type | Description |
|---|---|---|
name |
String |
Domain name |
type |
int |
Record type code |
TTL |
int |
Time to live (seconds) |
data |
String |
Record data |
Exceptions #
DnsLookupException - Thrown when DNS query fails:
hostname- The queried hostnamestatus- DNS status code (2=SERVFAIL, 3=NXDOMAIN, etc.)message- Error description
DnsHttpException - Thrown when HTTP request fails:
statusCode- HTTP status codemessage- Error description
DnsWireFormatException - Thrown when wire format parsing fails:
message- Error descriptionoffset- Byte offset where error occurred
Requirements #
- Dart SDK:
>=3.7.0 <4.0.0
License #
See LICENSE file.
Contributing #
Please file feature requests and bugs at the issue tracker.