dns

package
v0.3.3 Latest Latest
Warning

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

Go to latest
Published: Aug 2, 2026 License: Apache-2.0 Imports: 9 Imported by: 0

Documentation

Overview

Package dns provides a minimal, high-performance generic matcher for DNS hostnames.

A Router maps dot-separated hostname patterns to caller-provided values, then returns the matched value and any captured parameters. Patterns are matched right-to-left using DNS label boundaries, so shared suffixes such as example.com are stored once.

Hostname matching is ASCII case-insensitive. A single trailing root dot is ignored for both insertion and matching, so example.com and example.com. are equivalent. The package does not parse host:port strings, perform IDNA conversion, or normalize Unicode; callers should provide only the hostname they want to match.

Quick Start

var router dns.Router[string]
router.Insert("example.com", "apex")
router.Insert("{tenant}.example.com", "tenant")
router.Insert("{*subdomain}.example.com", "subdomain")

value, params, ok := router.Match("api.example.com")
_ = value                 // "tenant"
_ = params.Get("tenant")  // "api"
_ = ok                    // true

Pattern Grammar

Patterns are dot-separated labels made from literal text, named parameters, and left-side catch-all parameters.

Literal labels match themselves case-insensitively. A named parameter is written as {name} and captures one non-empty label. A parameter may have literal text before or after it in the same label, such as api-{region}.example.com or {service}-prod.example.com. Each label may contain at most one parameter.

A catch-all parameter is written as {*name}. It must appear in the leftmost label of the pattern and captures the non-empty leading hostname text before the remaining suffix labels. For example, {*sub}.example.com captures "a.b" from a.b.example.com. A catch-all may have a literal prefix in its leftmost label, such as svc-{*sub}.example.com.

Literal braces are escaped by doubling them: {{ matches a literal { and }} matches a literal }. Escaped braces may also appear inside parameter names.

Matching Behavior

When more than one pattern could match, dns chooses deterministically while walking labels from right to left: literal labels beat parameter labels, parameter labels with more literal text are tried first, and catch-all patterns are considered last.

Match looks up an exact hostname. MatchInto is the same operation using a caller-provided *Params value as reusable storage. MatchSuffix and MatchSuffixInto return the best whole-label hostname suffix plus the unmatched leading prefix, which is useful for zone-style dispatch. Clone returns an independent copy of a Router's matching state.

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrInvalidHostname reports malformed dot-separated hostname structure.
	ErrInvalidHostname = errors.New("hostnames must contain non-empty labels")

	// ErrInvalidParamLabel reports a pattern label that contains more than one
	// parameter.
	ErrInvalidParamLabel = errors.New("only one parameter is allowed per hostname label")

	// ErrInvalidParam reports malformed parameter syntax or an invalid
	// parameter name.
	ErrInvalidParam = match.ErrInvalidParam

	// ErrInvalidCatchAll reports a catch-all parameter outside the leftmost
	// pattern label.
	ErrInvalidCatchAll = errors.New("catch-all parameters are only allowed at the start of a hostname pattern")
)

Functions

This section is empty.

Types

type ConflictError

type ConflictError struct {
	// Pattern is the pattern that failed to insert.
	Pattern string

	// With is the previously registered pattern that conflicts with Pattern.
	With string
}

ConflictError reports a pattern that cannot be inserted because it overlaps an already registered pattern.

func (*ConflictError) Error

func (e *ConflictError) Error() string

Error returns a human-readable description of the pattern conflict.

type Param

type Param = match.Param

Param is one captured hostname parameter.

type Params

type Params = match.Params

Params stores captured hostname parameters in pattern order.

func Merge

func Merge(a, b Params) Params

Merge returns a Params value containing a followed by b.

func NewParams

func NewParams(capacity int) Params

NewParams returns an empty Params value with room for capacity parameters.

func ParamsOf

func ParamsOf(params ...Param) Params

ParamsOf returns a Params value containing params in the same order.

type Router

type Router[T any] struct {
	// contains filtered or unexported fields
}

Router maps DNS hostname patterns to caller-provided values.

The zero value is ready to use. After patterns are registered, a Router may be used by multiple goroutines for matching. Callers that insert patterns while other goroutines use the router must synchronize access.

func (*Router[T]) Clone

func (r *Router[T]) Clone() Router[T]

Clone returns a Router containing a deep copy of r's matching state.

Future inserts into the returned Router do not mutate r. Stored values are copied by assignment.

func (*Router[T]) Insert

func (r *Router[T]) Insert(pattern string, value T)

Insert registers pattern with value.

It panics with the same errors returned by TryInsert when pattern is invalid or conflicts with an existing pattern.

func (*Router[T]) Match

func (r *Router[T]) Match(hostname string) (T, Params, bool)

Match returns the value and parameters for hostname.

Hostname matching is ASCII case-insensitive. A single trailing root dot is ignored, so example.com and example.com. are equivalent. The boolean result is false when no registered pattern matches or hostname is malformed.

func (*Router[T]) MatchInto

func (r *Router[T]) MatchInto(hostname string, params *Params) (T, bool)

MatchInto returns the value for hostname using params as parameter storage.

Params is reset before matching and must be non-nil. Use NewParams to create a reusable Params buffer large enough for the expected number of captures.

func (*Router[T]) MatchSuffix

func (r *Router[T]) MatchSuffix(hostname string) (SuffixMatch[T], bool)

MatchSuffix returns the value, parameters, and unmatched prefix for the best registered pattern that matches the right-hand suffix of hostname.

The boolean result is false when no registered pattern matches a whole-label suffix of hostname. When multiple patterns match, the pattern that consumes the most hostname labels wins.

func (*Router[T]) MatchSuffixInto

func (r *Router[T]) MatchSuffixInto(hostname string, params *Params) (SuffixMatch[T], bool)

MatchSuffixInto is like MatchSuffix, but uses params as parameter storage.

Params is reset before matching and must be non-nil. Use NewParams to create a reusable Params buffer large enough for the expected number of captures.

func (*Router[T]) TryInsert

func (r *Router[T]) TryInsert(pattern string, value T) error

TryInsert registers pattern with value.

It returns an error when pattern has invalid parameter syntax, invalid hostname-label structure, or when it would conflict with an existing pattern. Duplicate and ambiguous patterns return a *ConflictError.

type SuffixMatch

type SuffixMatch[T any] struct {
	Value  T
	Params Params
	Prefix string
}

SuffixMatch contains the result of a successful suffix match.

Prefix is the unmatched labels to the left of the matched suffix. It is empty when the match consumes the full hostname.

Jump to

Keyboard shortcuts

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