multiresolver

package module
v0.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 25, 2025 License: MIT Imports: 8 Imported by: 0

README

multiresolver

Race multiple hostname resolvers and return the first successful answer (or collect all).

  • First-success: stop at the earliest valid result.
  • Gather-all: wait for all candidates and return every success.
  • Batteries: system DNS, custom DNS server, optional mDNS.

Quick start

package main

import (
    "context"
    "fmt"
    "net/netip"
    "time"

    "github.com/paullesiak/multiresolver"
)

func main() {
    r := multiresolver.New(
        multiresolver.System("system"),
        multiresolver.DNSServer("quad9", netip.MustParseAddrPort("9.9.9.9:53")),
        multiresolver.MDNS("mdns"),
    )

    ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
    defer cancel()

    // First successful answer (single host)
    res, err := r.Resolve(ctx, "example.com")
    if err != nil {
        panic(err)
    }
    fmt.Printf("winner: %s addrs: %v\n", res.Source, res.Addrs)

    // Or, gather all successes (single host)
    all, err := r.ResolveAll(ctx, "example.com")
    if err != nil {
        panic(err)
    }
    fmt.Printf("all: %+v\n", all)
}
Multiple hostnames

Provide multiple equivalent hostnames and race across all resolvers and hosts:

res, err := r.ResolveAny(ctx, []string{"svc-a.local", "svc-b.local", "svc-c.local"})

Documentation

Index

Constants

This section is empty.

Variables

View Source
var ErrNoCandidates = errors.New("multiresolver: no candidates available")

ErrNoCandidates indicates that no resolver candidates were provided or none produced an address.

Functions

This section is empty.

Types

type Candidate

type Candidate struct {
	Name   string
	Lookup LookupFunc
}

Candidate ties an identifier to a lookup function so that successes and failures can be attributed to the source that produced them.

func DNSServer

func DNSServer(name string, server netip.AddrPort) Candidate

DNSServer returns a candidate using a dedicated *net.Resolver that directs queries to the supplied DNS server address. Connections use Go's pure resolver stack to avoid libc lookups.

func LookupFromResolver

func LookupFromResolver(name string, resolver *net.Resolver) Candidate

LookupFromResolver wraps a *net.Resolver so it can participate as a candidate with the provided name. The resolver is queried via LookupNetIP to avoid string parsing conversions.

func MDNS

func MDNS(name string) Candidate

MDNS creates a candidate that performs multicast DNS lookups using the default implementation.

func MDNSWithQuery

func MDNSWithQuery(name string, query MDNSQuery) Candidate

MDNSWithQuery creates an mDNS candidate that uses the provided query function. Supplying nil falls back to the default mDNS stack that ships with this package.

func System

func System(name string) Candidate

System returns a candidate backed by the process-wide default DNS resolver.

type LookupFunc

type LookupFunc func(ctx context.Context, host string) ([]netip.Addr, error)

LookupFunc performs a host lookup for the provided name and returns zero or more network addresses.

type MDNSQuery

type MDNSQuery func(ctx context.Context, host string) (netip.Addr, error)

MDNSQuery resolves a host via mDNS and returns a single address.

type Observer

type Observer interface {
	Start(name string)
	Success(name string, addrs []netip.Addr)
	Error(name string, err error)
}

Observer receives lifecycle notifications for each candidate execution. Implementations can expose metrics or logging without being hard-wired into the resolver.

type Resolver

type Resolver struct {
	// contains filtered or unexported fields
}

Resolver coordinates multiple lookup candidates and races them to find an answer.

func New

func New(candidates ...Candidate) *Resolver

New constructs a Resolver from the provided candidates, discarding any entries lacking a lookup function. It never mutates the original slice.

func (*Resolver) Resolve

func (r *Resolver) Resolve(ctx context.Context, host string) (Result, error)

Resolve races every candidate until one returns at least one address. The first successful result is returned immediately and the remaining lookups are cancelled. The error joins include ErrNoCandidates plus the underlying failures so that callers can inspect the causes.

func (*Resolver) ResolveAll

func (r *Resolver) ResolveAll(ctx context.Context, host string) ([]Result, error)

ResolveAll waits for every candidate to finish and returns each successful result. The order of the slice reflects the time at which the answers arrived. When all candidates fail, the joined error includes ErrNoCandidates and the individual failures.

func (*Resolver) ResolveAny

func (r *Resolver) ResolveAny(ctx context.Context, hosts []string) (Result, error)

ResolveAny races all candidates across all provided hosts and returns the first successful result. When hosts or candidates are empty, or when all lookups fail, an error joined with ErrNoCandidates is returned.

func (*Resolver) WithObserver

func (r *Resolver) WithObserver(observer Observer) *Resolver

WithObserver registers an observer that receives callbacks during resolution. The resolver is returned so the helper can be chained from the call site.

type Result

type Result struct {
	// Host is the hostname that produced this result.
	Host string
	// Source is the candidate (resolver name) that resolved Host.
	Source string
	// Addrs are the resolved addresses for Host.
	Addrs []netip.Addr
}

Result captures the candidate that succeeded and the addresses it resolved.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL