Skip to content
Effect 4.0 is here. One ecosystem. Zero dependencies.

Dns

Host name resolution, DNS queries, and DNS record values.

lookup resolves a host name to the addresses used to connect to it, resolve queries DNS records of one type, and reverse looks up the names of an address. Runtime packages provide the Dns service from the host platform's resolver, whose lookup uses the operating system resolver (getaddrinfo) and therefore also reads the hosts file and other system sources, not only DNS. AddressResolver builds on lookup to resolve host:port endpoints.

33 exports Since v4.0.0 Source

Constants

Every supported record type.

Signature

declare const recordTypes: Arr.NonEmptyReadonlyArray<RecordType>

Constructors

Since v4.0.0

Creates a Dns service from platform resolver operations.

Details

Lookup and query names are parsed and normalized, and reverse lookup addresses are parsed, before platform callbacks receive them. Invalid names and addresses fail with BadName.

The constructor filters lookups by the requested address family, keeps only records of the requested type, removes duplicates, and turns empty results into NotFound failures. reverse receives the names of an address and keeps those that are valid host names, failing with InvalidResponse when there are names but none is valid. Without a reverse operation, the names are those of the PTR records at the address's reverseName, and failures of that query are reported as failures of reverse.

Only lookup is required. Platforms that can resolve addresses but cannot query records, such as browsers, omit resolve; record queries, and reverse lookups without a reverse operation, then fail with Unsupported.

Signature

declare function make(impl: {
readonly lookup: (host: DomainName, family: IpFamily | undefined) => Effect<readonly Array<IpAddress>, DnsError>;
readonly resolve?: (name: DomainName, type: RecordType) => Effect<readonly Array<DnsRecord>, DnsError>;
readonly reverse?: (address: IpAddress) => Effect<readonly Array<string>, DnsError>;
}): {
lookup<F extends IpFamily>(host: DomainNameInput, options: {
readonly family: F;
}): Effect<readonly [FamilyAddress<F>, FamilyAddress<F>], DnsError>;
lookup(host: DomainNameInput, options?: LookupOptions): Effect<readonly [IpAddress, IpAddress], DnsError>;
resolve<T extends RecordType>(name: DomainNameInput, type: T): Effect<readonly [RecordFor<T>, RecordFor<T>], DnsError>;
reverse(address: IpAddressInput): Effect<readonly [DomainName, DomainName], DnsError>;
}

Creates a checked DNS record of a record type from its fields.

Details

Every field is checked at runtime: addresses must belong to the record's address family, names must be normalized domain names, priorities, weights, and ports must be 16-bit unsigned integers, the SOA serial and timers must be 32-bit unsigned integers (the timers in whole seconds), the TLSA fields must be 8-bit unsigned integers with non-empty data, which is copied, TXT records need at least one chunk, and CAA property tags must be alphanumeric. Fields that do not belong to the record type are ignored.

Signature

declare function makeRecord<T extends RecordType>(type: T, fields: RecordFields<T>): Result<RecordFor<T>, NetAddressError>

Example Creating a service record

import { Result } from "effect"
import { Dns, Host } from "effect/net"
const record = Result.getOrThrow(Dns.makeRecord("SRV", {
target: Host.domainNameFromStringUnsafe("db.internal"),
port: 5432,
priority: 10,
weight: 5
}))
Dns.formatRecord(record) // => "SRV 10 5 5432 db.internal"

Creates a Dns service that answers from fixed data.

When to use

Use to test code that depends on Dns, or to provide fixed names on platforms without a resolver.

Details

Names without matching addresses or records fail with NotFound. CNAME records are returned by CNAME queries but are not followed.

See

Signature

declare function makeStatic(zone: StaticZone): Result<{
lookup<F extends IpFamily>(host: DomainNameInput, options: {
readonly family: F;
}): Effect<readonly [FamilyAddress<F>, FamilyAddress<F>], DnsError>;
lookup(host: DomainNameInput, options?: LookupOptions): Effect<readonly [IpAddress, IpAddress], DnsError>;
resolve<T extends RecordType>(name: DomainNameInput, type: T): Effect<readonly [RecordFor<T>, RecordFor<T>], DnsError>;
reverse(address: IpAddressInput): Effect<readonly [DomainName, DomainName], DnsError>;
}, NetAddressError>

Example Looking up a name with a static resolver

import { Effect, Result } from "effect"
import { Dns, NetAddress } from "effect/net"
const dns = Result.getOrThrow(Dns.makeStatic({
hosts: { "db.internal": ["10.0.0.5"] }
}))
const program = dns.lookup("db.internal").pipe(
Effect.map((addresses) => addresses.map(NetAddress.formatIp))
)
await Effect.runPromise(program) // => ["10.0.0.5"]

Converts a name server to the internet address to query. Strings are parsed like nameServerFromString, internet addresses and address parts with a port are converted like NetAddress.inetAddressFromInput, and other IP address inputs are converted like NetAddress.ipFromInput and use port 53.

Signature

declare function nameServerFromInput(input: string | Ipv4Address | Ipv6Address | InetAddressV4 | InetAddressV6 | readonly [number, number, number, number] | Uint8Array<ArrayBufferLike> | readonly [number, number, number, number, number, number, number, number] | Uint8Array<ArrayBufferLike> | InetAddressParts<IpAddressInput>): Result<InetAddress, NetAddressError>

Converting

Returns the reverse-lookup domain name for an IP address, in in-addr.arpa for IPv4 or ip6.arpa for IPv6.

Signature

declare function reverseName(address: IpAddress): DomainName

Example Building reverse-lookup names

import { Dns, NetAddress } from "effect/net"
Dns.reverseName(NetAddress.ipFromStringUnsafe("192.0.2.1")) // => "1.2.0.192.in-addr.arpa"

Decoding

Parses the address of a name server: an IP address, which uses port 53, or an IP address and port.

Details

IPv6 addresses with a port are written in brackets, and IPv6 addresses without one are not.

Signature

declare function nameServerFromString(input: string): Result<InetAddress, NetAddressError>

Example Parsing name server addresses

import { Result } from "effect"
import { Dns, NetAddress } from "effect/net"
const format = (input: string) => Result.map(Dns.nameServerFromString(input), NetAddress.formatInet)
format("192.0.2.53") // => Result.succeed("192.0.2.53:53")
format("2001:db8::53") // => Result.succeed("[2001:db8::53]:53")
format("[2001:db8::53]:5353") // => Result.succeed("[2001:db8::53]:5353")
Result.isFailure(format("ns.example")) // => true

Encoding

Formats a record in DNS presentation format, prefixed by its type.

Signature

declare function formatRecord(self: DnsRecord): string

Errors

Since v4.0.0

A failed lookup, query, or reverse lookup.

Signature

declare class DnsError extends YieldableError<this> & {
readonly _tag: "DnsError";
} & Readonly<{
readonly cause?: unknown;
readonly hostname: string;
readonly method: "reverse" | "lookup" | "resolve";
readonly reason: DnsErrorReason;
readonly recordType?: RecordType;
}> {
constructor(args: {
readonly cause?: unknown;
readonly hostname: string;
readonly method: "reverse" | "lookup" | "resolve";
readonly reason: DnsErrorReason;
readonly recordType?: RecordType;
});
isTemporary: boolean;
message: string;
}
Since v4.0.0

The normalized reason for a failed name resolution.

Details

  • NotFound: no records were found, either because the name does not exist or because it has no records of the requested type or address family. Like Go's DNSError.IsNotFound, the two cases are not distinguished because not every platform resolver can tell them apart.
  • Timeout, Temporary, and ServerFailure: transient failures that may succeed when retried.
  • Refused: the server refused the query or could not be reached.
  • BadName: the name was rejected as malformed.
  • InvalidResponse: the platform returned data that could not be converted into record values.
  • Unsupported: the platform does not support the operation or record type.

Signature

type DnsErrorReason = "NotFound" | "Timeout" | "Temporary" | "Refused" | "ServerFailure" | "BadName" | "InvalidResponse" | "Unsupported" | "Unknown"

Guards

Returns true when a value is a DNS record.

Signature

declare function isDnsRecord(u: unknown): u is DnsRecord

Returns true when a value is a supported record type.

Signature

declare function isRecordType(u: unknown): u is RecordType

Layers

Creates a layer that provides a Dns service answering from fixed data.

See

Signature

declare function layerStatic(zone: StaticZone): Layer<Dns, NetAddressError>

Models

A

interface
Since v4.0.0

An IPv4 address record.

Signature

interface A extends RecordProto<"A"> {
readonly address: Ipv4Address;
}

Aaaa

interface
Since v4.0.0

An IPv6 address record.

Signature

interface Aaaa extends RecordProto<"AAAA"> {
readonly address: Ipv6Address;
}

Caa

interface
Since v4.0.0

A certification authority authorization record.

Signature

interface Caa extends RecordProto<"CAA"> {
readonly critical: boolean;
readonly tag: string;
readonly value: string;
}

Cname

interface
Since v4.0.0

A canonical name (alias) record.

Signature

interface Cname extends RecordProto<"CNAME"> {
readonly target: DomainName;
}
Since v4.0.0

Any supported DNS record value.

Details

Records are immutable values tagged by record type, compared and hashed by their fields. They hold the record data only: owner names and TTLs are not included because platform resolvers do not report them consistently. Names returned by platform services are fully qualified and end with a dot, so passing them back to the resolver does not apply search domains.

Signature

type DnsRecord = A | Aaaa | Caa | Cname | Mx | Naptr | Ns | Ptr | Soa | Srv | Tlsa | Txt

LookupOptions

interface
Since v4.0.0

Options for address lookups. Without a family, addresses of both families are returned.

Signature

interface LookupOptions {
readonly family?: IpFamily;
}

Mx

interface
Since v4.0.0

A mail exchange record.

Signature

interface Mx extends RecordProto<"MX"> {
readonly exchange: DomainName;
readonly priority: number;
}

Naptr

interface
Since v4.0.0

A naming authority pointer record.

Signature

interface Naptr extends RecordProto<"NAPTR"> {
readonly flags: string;
readonly order: number;
readonly preference: number;
readonly regexp: string;
readonly replacement: DomainName;
readonly service: string;
}

Ns

interface
Since v4.0.0

A name server record.

Signature

interface Ns extends RecordProto<"NS"> {
readonly host: DomainName;
}

Ptr

interface
Since v4.0.0

A pointer record, used for reverse lookups and DNS service discovery.

Details

host is a fully qualified name. It is a host name for reverse lookups, but may be a DNS-SD service instance name containing spaces or other characters, such as EPSON TM-m30III._ipp._tcp.local.. Labels are UTF-8 text, with dots and backslashes inside a label escaped as \. and \\.

Signature

interface Ptr extends RecordProto<"PTR"> {
readonly host: string;
}
Since v4.0.0

The fields of a record type, without its _tag.

Signature

type RecordFields<T extends RecordType> = T extends RecordType ? Omit<RecordFor<T>, keyof RecordProto<T>> : never
Since v4.0.0

The record value for a record type.

Signature

type RecordFor<T extends RecordType> = Extract<DnsRecord, {
readonly _tag: T;
}>
Since v4.0.0

The type of a DNS record, such as "A" or "SRV".

Signature

type RecordType = "A" | "AAAA" | "CAA" | "CNAME" | "MX" | "NAPTR" | "NS" | "PTR" | "SOA" | "SRV" | "TLSA" | "TXT"

Soa

interface
Since v4.0.0

A start of authority record.

Details

admin is the administrator mailbox written as a domain name, such as hostmaster.example.com. A dot inside the mailbox's local part is escaped, as in john\.doe.example.com, so admin is a plain string rather than a Host.DomainName. Labels are UTF-8 text, with dots and backslashes inside a label escaped as \. and \\.

Signature

interface Soa extends RecordProto<"SOA"> {
readonly admin: string;
readonly expire: Duration;
readonly minimum: Duration;
readonly primary: DomainName;
readonly refresh: Duration;
readonly retry: Duration;
readonly serial: number;
}

Srv

interface
Since v4.0.0

A service location record.

Signature

interface Srv extends RecordProto<"SRV"> {
readonly port: number;
readonly priority: number;
readonly target: DomainName;
readonly weight: number;
}

StaticZone

interface
Since v4.0.0

Fixed names, addresses, and records for a static resolver.

Details

hosts works like a hosts file: its entries are used by lookup and reverse, but not by resolve. records holds DNS records by owner name and is used by all three operations. Names are normalized like Host.domainNameFromString, and a trailing dot is ignored. Addresses are converted like NetAddress.ipFromInput.

Signature

interface StaticZone {
readonly hosts?: ReadonlyRecord<string, readonly Array<IpAddressInput>>;
readonly records?: ReadonlyRecord<string, readonly Array<DnsRecord>>;
}

Tlsa

interface
Since v4.0.0

A TLS authentication record (RFC 6698), which pins the certificate or public key that a TLS service presents, for DNS-Based Authentication of Named Entities (DANE). Records are published at a name made of the service's port and protocol, such as _443._tcp.example.com.

Details

certUsage says how the record constrains the certificate chain (0 to 3: PKIX-TA, PKIX-EE, DANE-TA, DANE-EE), selector whether data matches the full certificate (0) or its public key (1), and matchingType whether data holds the selected content itself (0) or its SHA-256 (1) or SHA-512 (2) digest. Unassigned values are kept as they are.

Gotchas

data is a copy of the bytes the record was made from, but typed arrays cannot be frozen; treat it as read-only, because changing it changes the record's equality.

Signature

interface Tlsa extends RecordProto<"TLSA"> {
readonly certUsage: number;
readonly data: Uint8Array;
readonly matchingType: number;
readonly selector: number;
}

Txt

interface
Since v4.0.0

A text record. DNS stores text as one or more character strings, which are kept as separate chunks; protocols such as SPF read them joined, for example with record.chunks.join("").

Details

Platform services decode each character string as UTF-8, replacing bytes that are not valid UTF-8 with U+FFFD, and formatRecord writes chunks back as UTF-8 bytes.

Signature

interface Txt extends RecordProto<"TXT"> {
readonly chunks: readonly [string, string];
}

Services

Since v4.0.0

Service that resolves names to addresses, queries DNS records, and looks up the names of addresses.

Details

Every operation either returns at least one result or fails with a DnsError.

Signature

declare class Dns extends Shape<"effect/net/Dns", {
lookup<F extends IpFamily>(host: DomainNameInput, options: {
readonly family: F;
}): Effect<readonly [FamilyAddress<F>, FamilyAddress<F>], DnsError>;
lookup(host: DomainNameInput, options?: LookupOptions): Effect<readonly [IpAddress, IpAddress], DnsError>;
resolve<T extends RecordType>(name: DomainNameInput, type: T): Effect<readonly [RecordFor<T>, RecordFor<T>], DnsError>;
reverse(address: IpAddressInput): Effect<readonly [DomainName, DomainName], DnsError>;
}, this> {
constructor(_: never);
}

Unsafe

Creates a trusted DNS record of a record type from its fields, throwing on failure.

Signature

declare function makeRecordUnsafe<T extends RecordType>(type: T, fields: RecordFields<T>): RecordFor<T>