dns-query
Low-level DNS requests using JavaScript in the Browser and Node.js.
import { query } from 'dns-query'
const { answers, rcode } = await query(
{ question: { type: 'TXT', name: 'google.com' } },
{ endpoints: ['1.1.1.1'] }
)
Why use it?
- You get the same API both on Node.js and the Browser (and React-Native).
- You need DNS information not exposed through high-level APIs (eg.
ttl
for TXT
entries). - You want to use DNS-over-HTTPS (DoH) servers.
What is in the package?
- DoH implementation in Node.js and the browser.
- Low level implementation of classic DNS in Node.js.
- ESM modules, CommonJS modules and typescript definitions.
- A list of well-known DoH endpoints.
- A parser for compact definitions of DoH and DNS endpoints.
- A command line tool to run DNS requests.
Contents
JavaScript API
import { query } from 'dns-query'
try {
const { answers } = await query({
question: {type: 'A', name: 'google.com'}
}, {
endpoints: ['dns.google', 'dns.switch.ch'],
retries: 3,
timeout: 4000,
signal,
})
} catch (error) {
switch (error.code) {
case 'HTTP_STATUS':
case 'RESPONSE_ERR':
case 'ABORT_ERR':
default:
}
}
import { query, lookupTxt, wellknown } from 'dns-query'
await query(, { endpoints: wellknown.endpoints() })
await lookupTxt('domain.com', { endpoints: wellknown.endpoints() })
Endpoints
You can define the endpoints in a variety of ways:
let endpoints
endpoints = ['dns.google']
endpoints = ['dns.google', 'dns.switch.ch']
endpoints = Promise.resolve(['dns.google'])
endpoints = ['https://dns.google:443/dns-query']
endpoints = ['udp://8.8.8.8']
endpoints = ['udp://[::]']
endpoints = ['http://localhost']
endpoints = ['localhost [post]']
endpoints = ['dns.google [name=google]']
endpoints = ['dns.google [ipv4=8.8.8.8]']
endpoints = [{
protocol: 'https:',
host: 'dns.google'
}]
import { toEndpoint } from 'dns-query'
endpoints = [toEndpoint('dns.google')]
Endpoint Properties
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
path?: string
method?: 'POST' | 'GET'
name?: string
} | {
protocol: 'udp4:'
ipv4: string
port?: number | string | null
pk?: string | null
name?: string
} | {
protocol: 'udp6:'
ipv6: string
port?: number | string | null
pk?: string | null
name?: string
}
Well-known Endpoints
To make your life easier, dns-query
comes with a list of well-known public dns-over-https
servers to execute queries.
import { wellknown } from 'dns-query'
let endpoints = await wellknown.endpoints()
This list is automatically compiled using the data from the DNSCrypt project!
The npm package comes with the list that was/is current on the time of it's publication.
By default await wellknown.data()
will try to automatically download the newest list from the dns-query website
and fall back to the list at publication if downloading fails.
Using the argument for wellknown.endpoints(...)
you can specify which endpoints
you want to use:
let options
options = 'doh'
options = 'dns'
options = ['@cloudflare', '@google', '@opendns']
options = ['https://cloudflare-dns.com/dns-query']
options = [{ host: 'cloudflare-dns.com' }]
options = (endpoint) => endpoint.protocol === 'https:'
options = Promise.resolve('doh')
await wellknown.endpoints(options)
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.
const dnsServers = await wellknown.endpoints('dns')
const dohServers = await wellknown.endpoints('doh')
Controlled Updates
If we loaded the resolvers/endpoints for every request, both the server load and
your application's responsiveness will suffer. Because of that the wellknown
object
will cache the known resolvers.
You can customize the behavior by creating a new Wellknown
instance with a different
configuration:
import { Wellknown } from 'dns-query'
const wk = new Wellknown({
update: true,
updateURL: new URL(),
persist: false,
localStoragePrefix: 'dnsquery_',
maxAge: 300000,
timeout: 5000
})
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
If you set persist: true
in Node.js, it will try to persist the list of resolvers relative to
the node_modules
directory.
Persist: Browser
In the browser, setting persist: true
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'
})
Note about the public servers.
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!
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())
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.
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
String endpoints
The input of endpoints are passed to the toEndpoint
helper. See Endpoints
for the supported formats.
See Also
License
MIT