Browse the docs

silk.net.resolver.

Turn a host and a port into a list of endpoints to connect to.

When to use#

Make a ResolveRequest from a Host and a Port, and call Resolver.resolve. A numeric host gives one endpoint without a lookup. A domain name goes to the Resolver provider. Use silk.net.resolver_native in a native program, or give your own provider, for example in tests.

Details#

The result keeps the order of the provider, removes exact duplicates, and keeps only the addresses of the FamilySelection. It holds at most the maxResults of the request, and never more than 64.

Errors#

  • NoAddress if no address of the selected family remains.

  • LimitExceeded if there are more distinct endpoints than maxResults. The result is never cut short.

  • Timeout if the deadline of a numeric request has passed.

  • Other ResolverError values come from the provider.

Gotchas#

For a domain name, the provider decides how to use the deadline. NativeSystemResolver cannot stop a lookup, so it returns DeadlineUnsupported for a domain request with a deadline. maxResults limits only the result, not the work of the provider.

Examples#

Resolve with a test provider#

This provider returns the same endpoints for every name. The duplicate and the IPv6 address are removed from the result.

silk
import silk.mem.allocator { Allocator, OutOfMemoryError }
import silk.core.effect { Effect }
import silk.system.monotonic_clock { MonotonicClock }
import silk.net.address {
  AddressError,
  DomainHost,
  Endpoint,
  Host,
  IpAddress,
  Ipv4Address,
  Ipv6Address,
  Port
}
import silk.core.option { Option }
import silk.core.result { Result }
import silk.net.resolver { FamilySelection, ResolveRequest, ResolvedEndpoints, Resolver, ResolverError }
import silk.system.system_clock { Instant, SystemClock }
import silk.num.u64

struct FixedResolver {}

impl FixedResolver {
  effect fn resolveDomain(
    self: &mut Self,
    request: &ResolveRequest,
    domain: &DomainHost,
  ) -> ResolvedEndpoints
    ! ResolverError | OutOfMemoryError
    ? &mut Allocator {
    drop domain
    let port = ResolveRequest.port(request)
    let mut result = match ResolvedEndpoints.make(8) {
      Result<ResolvedEndpoints, ResolverError>.Success { value } => move value
      Result<ResolvedEndpoints, ResolverError>.Failure { error } => {
        fail move error
      }
    }
    let first = IpAddress.V4 { value: Ipv4Address.fromOctets([192, 0, 2, 1]) }
    let second = IpAddress.V6 { value: Ipv6Address.fromOctets([0; 16]) }
    let added = run ResolvedEndpoints.admit(&mut result, Endpoint.make(first, port))
    let same = run ResolvedEndpoints.admit(&mut result, Endpoint.make(first, port))
    let other = run ResolvedEndpoints.admit(&mut result, Endpoint.make(second, port))
    return move result
  }
}

impl Resolver for FixedResolver {
  resolveDomain: FixedResolver.resolveDomain
}

struct FixedMonotonic {}

impl MonotonicClock for FixedMonotonic {
  effect fn now(self: &mut Self) -> Instant {
    return SystemClock.make(0, 0)
  }

  effect fn getResolution(self: &mut Self) -> u64 {
    return u64.toU64(1)
  }

  effect fn waitUntil(self: &mut Self, when: Instant) -> () {
    drop when
    return ()
  }

  effect fn waitFor(self: &mut Self, howLong: u64) -> () {
    drop howLong
    return ()
  }
}

effect fn program() -> i32
  ! ResolverError | OutOfMemoryError {
  let host = match Host.parse("example.com") {
    Result<Host, AddressError>.Success { value } => value
    Result<Host, AddressError>.Failure { error: _ } => {
      return 1
    }
  }
  let request = match ResolveRequest.make(
    host,
    Port.fromU16(443),
    FamilySelection.V4,
    4,
    Option<Instant>.none(),
  ) {
    Result<ResolveRequest, ResolverError>.Success { value } => move value
    Result<ResolveRequest, ResolverError>.Failure { error: _ } => {
      return 1
    }
  }
  let mut allocator = Allocator.systemAllocatorProvider()
  let mut resolver = FixedResolver {}
  let mut clock = FixedMonotonic {}
  let endpoints = run Resolver.resolve(&request) |> Effect.provideMut<Resolver>(&mut resolver)
    |> Effect.provideMut<MonotonicClock>(&mut clock)
    |> Effect.provideMut<Allocator>(&mut allocator)
  if ResolvedEndpoints.length(&endpoints) != 1 {
    return 1
  }
  let endpoint = ResolvedEndpoints.get(&endpoints, 0)
  let address = Endpoint.address(&endpoint)
  if !IpAddress.isV4(&address) || Port.value(&Endpoint.port(&endpoint)) != 443 {
    return 1
  }
  return 0
}

effect fn failed<E>(error: E) -> i32 {
  drop error
  return 1
}

pub fn main() -> i32 {
  return run Effect.catchAll(program(), failed)
}

Import as Resolver with import silk.net.resolver { Resolver }.

Public declarations: 9.

FamilySelection#

silk
pub enum FamilySelection

The address families to keep in a result.

Any#

silk
Any

Keep IPv4 and IPv6 addresses.

V4#

silk
V4

Keep only IPv4 addresses.

V6#

silk
V6

Keep only IPv6 addresses.

ResolverInputReason#

silk
pub enum ResolverInputReason

The reason that a maxResults value is not valid.

ZeroResults#

silk
ZeroResults

maxResults is 0.

TooManyResults#

silk
TooManyResults

maxResults is more than 64.

Implementation Debug for ResolverInputReason#

silk
impl Debug for ResolverInputReason

Operation debug#

silk
effect fn debug(self: &Self, formatter: &mut Formatter) -> () ! WriterError ? &mut Writer

Writes the name of the variant, such as ZeroResults.

NativeResolverOperation#

silk
pub enum NativeResolverOperation

The system call that failed in a native provider.

GetAddrInfo#

silk
GetAddrInfo

The getaddrinfo call.

Implementation Debug for NativeResolverOperation#

silk
impl Debug for NativeResolverOperation

Operation debug#

silk
effect fn debug(self: &Self, formatter: &mut Formatter) -> () ! WriterError ? &mut Writer

Writes the name of the variant, such as GetAddrInfo.

NativeResultReason#

silk
pub enum NativeResultReason

The reason that a native provider rejected a successful getaddrinfo result.

NullChain#

silk
NullChain

A successful native lookup returned no result chain.

UnknownFamily#

silk
UnknownFamily

A native result contains an unsupported address family.

NullAddress#

silk
NullAddress

A native result has no address pointer.

ShortAddress#

silk
ShortAddress

A native address is shorter than its family requires.

Implementation Debug for NativeResultReason#

silk
impl Debug for NativeResultReason

Operation debug#

silk
effect fn debug(self: &Self, formatter: &mut Formatter) -> () ! WriterError ? &mut Writer

Writes the name of the variant, such as NullChain.

ResolverError#

silk
pub union ResolverError

The error from a resolution.

InvalidInput#

silk
InvalidInput

The maxResults of the request is not valid.

Field reason#

silk
pub reason: ResolverInputReason

The rule that failed.

Field value#

silk
pub value: usize

The maxResults that you gave.

UnsupportedForm#

silk
UnsupportedForm

The provider does not support this form of host.

Timeout#

silk
Timeout

The deadline of the request has passed.

DeadlineUnsupported#

silk
DeadlineUnsupported

The provider cannot stop a lookup at a deadline, so it did not start.

NoAddress#

silk
NoAddress

No address of the selected family was found.

NameNotFound#

silk
NameNotFound

The name does not exist.

TemporaryFailure#

silk
TemporaryFailure

The lookup failed for a temporary reason, for example a DNS server timeout. Try again later.

FamilyUnsupported#

silk
FamilyUnsupported

The system does not support the selected family.

SystemResources#

silk
SystemResources

The system did not have the resources, such as memory, for the lookup.

Provider#

silk
Provider

A host resolver failed with a provider-specific code, such as a WASI network error ordinal.

Field code#

silk
pub code: i32

The code reported by the provider. Its provider module defines the domain.

LimitExceeded#

silk
LimitExceeded

The lookup found more distinct endpoints than maxResults.

Field maximum#

silk
pub maximum: usize

The maxResults of the request.

InvalidNativeResult#

silk
InvalidNativeResult

The system returned a result that is not valid.

Field reason#

silk
pub reason: NativeResultReason

The problem in the result.

Field index#

silk
pub index: usize

The position of the bad entry in the result list, from 0.

NativeFailure#

silk
NativeFailure

The system lookup failed with a code that has no other variant.

Field operation#

silk
pub operation: NativeResolverOperation

The call that failed.

Field eai#

silk
pub eai: i32

The EAI_* code from getaddrinfo.

Field errno#

silk
pub errno: Option<i32>

The errno value, when the code is EAI_SYSTEM.

Implementation Debug for ResolverError#

silk
impl Debug for ResolverError

Operation debug#

silk
effect fn debug(self: &Self, formatter: &mut Formatter) -> () ! WriterError ? &mut Writer

Writes the name of the variant and its fields, such as InvalidInput { ... }.

ResolveRequest#

silk
pub struct ResolveRequest

What to resolve: a host, a port, the families to keep, the maximum result count, and an optional deadline. Make it with ResolveRequest.make.

ResolvedEndpoints#

silk
pub struct ResolvedEndpoints

The endpoints of a resolution, without duplicates, in the order of the provider.

Admission#

silk
pub enum Admission

The result of ResolvedEndpoints.admit.

Added#

silk
Added

The endpoint was added.

Duplicate#

silk
Duplicate

An equal endpoint is already in the list, so nothing changed.

Implementation ResolveRequest#

silk
impl ResolveRequest

Associated function make#

silk
pub fn make(
    host: Host,
    port: Port,
    family: FamilySelection,
    maxResults: usize,
    deadline: Option<Instant>,
  ) -> Result<ResolveRequest, ResolverError>

Returns a request.

maxResults is from 1 to 64. deadline is a time on the MonotonicClock.

Errors#

  • InvalidInput if maxResults is 0 or more than 64.

Method host#

silk
pub fn host(self: &Self) -> Host

Returns the host.

Method port#

silk
pub fn port(self: &Self) -> Port

Returns the port.

Method family#

silk
pub fn family(self: &Self) -> FamilySelection

Returns the families to keep.

Method maxResults#

silk
pub fn maxResults(self: &Self) -> usize

Returns the maximum number of endpoints in the result.

Method hasDeadline#

silk
pub fn hasDeadline(self: &Self) -> bool

Returns true if the request has a deadline.

Method deadline#

silk
pub fn deadline(self: &Self) -> Option<Instant>

Returns the deadline, if there is one. A provider that cannot keep it returns DeadlineUnsupported.

Implementation ResolvedEndpoints#

silk
impl ResolvedEndpoints

Associated function make#

silk
pub fn make(maximum: usize) -> Result<ResolvedEndpoints, ResolverError>

Returns an empty list that holds at most maximum endpoints, from 1 to 64.

Method length#

silk
pub fn length(self: &Self) -> usize

Returns the number of endpoints.

Method get#

silk
pub fn get(self: &Self, index: usize) -> Endpoint

Returns the endpoint at index. index must be less than ResolvedEndpoints.length.

Method asSlice#

silk
pub fn asSlice(self: &Self) -> &[Endpoint]

Returns the endpoints as a slice.

Method admit#

silk
pub effect fn admit(self: &mut Self, endpoint: Endpoint) -> Admission
  ! ResolverError | OutOfMemoryError
  ? &mut Allocator

Adds endpoint to the end of the list, if it is not in the list yet.

Errors#

  • LimitExceeded if the list is full and endpoint is new.

Resolver#

silk
pub service Resolver

The service that looks up domain names. Resolver.resolve calls it only for domain names.

When to use#

Require &mut Resolver in code that connects by name, and call Resolver.resolve. Provide silk.net.resolver_native.NativeSystemResolver in a native program, or your own provider, for example in tests.

Gotchas#

For a domain name, the provider decides how to use the deadline. NativeSystemResolver cannot stop a lookup, so it returns DeadlineUnsupported for a domain request with a deadline.

Operation resolveDomain#

silk
effect fn resolveDomain(
    request: &ResolveRequest,
    domain: &DomainHost,
  ) -> ResolvedEndpoints
  ! ResolverError | OutOfMemoryError
  ? &mut Resolver | &mut Allocator

Looks up domain and returns its endpoints, with the port of request. Resolver.resolve filters the families and removes duplicates after this call.

Implementation Resolver#

silk
impl Resolver

Associated function resolve#

silk
pub effect fn resolve(request: &ResolveRequest) -> ResolvedEndpoints
  ! ResolverError | OutOfMemoryError
  ? &mut Resolver | &mut Allocator | &mut MonotonicClock

Returns the endpoints for request.

A numeric host gives one endpoint, without the provider. A domain name calls Resolver.resolveDomain on the provider. The result keeps the order of the provider, removes exact duplicates, and keeps only the addresses of the FamilySelection. It holds at most the maxResults of the request, and never more than 64.

Errors#

  • NoAddress if no address of the selected family remains.

  • LimitExceeded if there are more distinct endpoints than maxResults. The result is never cut short.

  • Timeout if the deadline of a numeric request has passed.

  • Other ResolverError values come from the provider.

  • OutOfMemoryError if the Allocator cannot supply the result.

Gotchas#

maxResults limits only the result, not the work of the provider.

Category: Networking.

Profiles: aarch64-apple-darwin, aarch64-unknown-linux-gnu, aarch64-unknown-linux-gnu-no-libc, x86_64-unknown-linux-gnu, x86_64-unknown-linux-gnu-no-libc, wasm32-unknown-unknown, wasm32-wasip1, wasm32-wasip2.

View the source of this page