dns-query
Node & Browser tested, Non-JSON DNS over HTTPS (and DNS) fetching with minimal dependencies.
DNS over HTTPS (DoH) is protocol designed for performing remote Domain Name System
resolution over HTTPS. Requests are made of HTTP to increase user security and privacy.
See DNS over HTTPS for more
information.
This package provides simple function to make DoH queries both in node and the browser.
Contents
Important Note before getting started
By default dns-query
uses well-known public dns-over-https servers to execute
queries automatically compiled by the data from the DNSCrypt project.
The npm package comes with the list that was/is current on the time of the publication.
It will will try to automatically download the list from the dns-query website unless
you set the .update
property on a Session
object.
These servers come with caveats that you should be aware of:
- A server may filter, log or limit the requests it receives!
- Filtering can be useful in case you want to avoid malware/ads/adult-content.
- Logging may be required in some countries and limiting may be part of a business model.
- Furthermore the different endpoints may or may not be distributed around the globe,
making requests slower/faster depending on the client's location.
- Not all endpoints supply CORS headers which means that the list is severly reduced if you use this
library in the browser.
If you are presenting this library to an end-user, you may want to allow them to decide what endpoint
they want to use as it has privacy and usage implications!
DNS support
Node.js's dns support is limited, primary example being: resolveTxt
does not support ttl
results. For that reason, when using dns-query
in node you can also specify dns
endpoints that
should be helpful in the node environment.
JavaScript API
import { query, endpoints as defaultEndpoints } from 'dns-query'
const { cloudflare, google, opendns } = defaultEndpoints
let endpoints
endpoints = 'doh'
endpoints = 'dns'
endpoints = [cloudflare, google, opendns]
endpoints = ['cloudflare', 'google', 'opendns']
endpoints = ['https://cloudflare-dns.com/dns-query']
endpoints = [{ host: 'cloudflare-dns.com' }]
endpoints = (endpoint) => endpoint.protocol === 'https:'
endpoints = Promise.resolve('doh')
try {
const { answers } = await query({
question: {type: 'A', name: 'google.com'}
}, {
endpoints: endpoints,
retry: 3,
timeout: 4000,
signal,
})
} catch (error) {
switch (error.code) {
case 'HTTP_STATUS':
case 'RESPONSE_ERR':
case 'ABORT_ERR':
default:
}
}
Error Responses
By default query
will return the packet as received by the endpoints, even if
that package was flagged as an error. The validateResponse
helper will give a
well readable error message.
import { validateResponse, query } from 'dns-query'
const respone = validateResponse(await query())
Session API
Loading the latest list of resolvers from the servers will increase both the load
on the server hosting the list and your application's responsiveness. dns-query
comes
with support for persisting the resolvers in order to ease the load.
You can use the Session
to define the behavior for requests.
import { Session } from 'dns-query'
const session = new Session({
update: true,
updateURL: new URL(),
persist: true,
localStoragePrefix: 'dnsquery_',
maxAge: 300000,
retries: 3,
timeout: 3000,
endpoints: 'doh'
})
await session.wellknown()
await session.endpoints()
await session.query({ })
await session.lookupTxt('google.com')
By default, query
will try load the latest definitions (update: true
) but will
persist them only in memory. Subsequent requests will only update the list if
it is old.
persist: true
is useful when you restart a node process or refresh a browser.
The persisted data will then be available making subsequent requests faster still.
Persist: Node.js
In node.js this will try to persist the list of resolvers to the node_modules
directory.
Persist: Browser
In the browser it will use localStorage
to store the copy of resolvers. By default
it will use the localStoragePrefix = 'dnsquery_'
option. You will be able to find
the persisted resolvers under localStorage.getItem('dnsquery_resolvers.json')
.
query(..., {
localStoragePrefix: 'my_custom_prefix'
})
Txt Helper
The default TXT
response of dns-queries is a list of Uint8Array
's. If you have a
TXT
response you can use the combineTXT
API to combine the requests.
import { combineTXT } from 'dns-query'
const response = await query()
if (response.question.type === 'TXT') {
const txt = response.answers.map(answer => combineTXT(answer.data))
}
More convenient still is the lookupTxt
API that allows you to simple request
the TXT entries for a domain.
import { lookupTxt } from 'dns-query'
const { entries, endpoint } = await lookupTxt('google.com')
CLI
You can install dns-query
as a command-line tool using npm i dns-query -g
or by running npx dns-query
.
$ dns-query <options> <input>
dns-query - Execute a dns query over https.
USAGE:
dns-query <Options> <Input>
EXAMPLES:
$ dns-query --json -e google \
'{ "question": { "type": "A", "name": "google.com" } }'
$ dns-query --json --dns \
'{ "question": { "type": "TXT", "name": "ipfs.io" } }'
$ echo '{ "question": { "type": "A", "name": "google.com" } }' \
| dns-query --stdin --endpoint cloudflare
$ dns-query --base64 AAAAAAABAAAAAAAABGlwZnMCaW8AABAAAQ==
$ dns-query --mode=txt ipfs.io
OPTIONS:
--mode ........... Mode consume/process data.
---mode=free ... Free query input (default)
---mode=txt .... TXT data loading shortcut
--help, -h ....... Show this help
--version, -v .... Show the version
--json ........... --type=json
--base64 ......... --type=base64
--binary ......... --type=binary
--type ........... Input type. Options: json, base64, binary; Default: json
--out ............ Output type. Defaults to the input --type.
--stdin .......... Get <input> from stdin instead of cli arguments
--dns ............ Use dns endpoints
--doh ............ Use doh endpoints
--endpoint, -e ... Use a specific endpoint. Can be either the name of a known
endpoint, a json object or an url. By default uses one of the known endpoints.
If multiple are provided, one at random will be used.
--endpoints ...... Lists all known endpoints as json.
--resolvers ...... List all known resolvers as json.
--response ....... Show the http response in the result.
--retries ........ Number of retries to do in case requests fails, default: 5
--timeout ........ Timeout for the request in milliseconds, default: 30000 (5 sec)
--max-age ........ Max age of the persisted data, default: 300000 (5 min)
--no-persist ..... Dont persist the the latest resolvers
--offline ........ Do not update the resolver list
Endpoints
For an endpoint to work, it needs to satisfy this interface:
type EndpointProps = {
protocol: 'https:' | 'http:'
port?: number | string | null
host: string
ipv4?: string
ipv6?: string
cors?: boolean
path?: string
method?: 'POST' | 'GET'
} | {
protocol: 'udp4:'
ipv4: string
port?: number | string | null
pk?: string | null
} | {
protocol: 'udp6:'
ipv6: string
port?: number | string | null
pk?: string | null
}
String endpoints
Instead of passing an object you can also pass a string. If the string matches the name
of one of the endpoints, that endpoint will be used. If it doesnt match any endpoint,
then it will be parsed using the parseEndpoint
method understands an URL like structure
with additional properties defined like flags ([<name>]
).
Examples:
foo.com
→ { host: 'foo.com' }
http://bar.com:81/query [post]
→
{ host: 'bar.com', path: '/query', port: 81, method: 'post', protocol: 'http:' }
Note: If no path is given, such as foo.com
, the path will be assumed as /dns-query
, but
if a path is given such as foo.com/
it will assume that path /
!
To specify DNS endpoints you need to prefix them using udp:
(or udp4:
, udp6
)
udp://1.1.1.1
→ { host: '1.1.1.1', protocol: 'udp4' }
See Also
License
MIT