dnsserver

package
v0.3.7 Latest Latest
Warning

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

Go to latest
Published: Nov 13, 2026 License: Apache-2.0 Imports: 16 Imported by: 0

Documentation

Overview

Package dnsserver is an in-process DNS server for tests: an authoritative server with a small zone store, RFC 2136 UPDATE with TSIG, and a log of every message it received. It is imported by tests only; it listens on 127.0.0.1 with random ports, needs no privileges and runs the same on Linux, macOS and Windows.

The Store is what the tests of the agent and of the ACME server share: the agent writes TXT records into it through RFC 2136 (or through the Cloudflare stub of this package), and the ACME server reads them through this server as its resolver.

Index

Constants

View Source
const ChallengeLabel = "_acme-challenge."

ChallengeLabel is the first label of every dns-01 record name.

Variables

This section is empty.

Functions

This section is empty.

Types

type CloudflareOptions

type CloudflareOptions struct {
	// Token is the API token that may edit DNS records.
	Token string
	// ZoneToken, if set, is the only token that may list zones (the split
	// Zone:Read and DNS:Edit setup); otherwise Token does both.
	ZoneToken string
}

CloudflareOptions configure the Cloudflare stub.

type CloudflareRequest

type CloudflareRequest struct {
	Method, Path, Query string
	// Authorization is the header as sent, "Bearer <token>".
	Authorization string
}

CloudflareRequest is one request the stub saw.

type CloudflareStub

type CloudflareStub struct {
	// URL is the API base, https://127.0.0.1:port/client/v4.
	URL string
	// Roots trusts the stub's certificate.
	Roots *x509.CertPool
	// contains filtered or unexported fields
}

CloudflareStub is an httptest TLS server that speaks the part of the Cloudflare v4 API that the agent uses (list zones, create and delete a DNS record) and writes into a Store, so the ACME server's resolver sees what the agent "published".

func StartCloudflare

func StartCloudflare(tb testing.TB, store *Store, opts CloudflareOptions) *CloudflareStub

StartCloudflare starts the stub until the test ends.

func (*CloudflareStub) ErrorText

func (c *CloudflareStub) ErrorText(s string)

ErrorText sets the message of every error answer, to test that a secret in an API answer does not reach a log.

func (*CloudflareStub) FailDeletes

func (c *CloudflareStub) FailDeletes(status int)

FailDeletes makes every DELETE answer with this HTTP status (0 = normal).

func (*CloudflareStub) LoseCreateAnswers

func (c *CloudflareStub) LoseCreateAnswers(body string)

LoseCreateAnswers makes the stub create a record as usual but answer the request with body (HTTP 502, not JSON), as a proxy that loses the real answer would: the record exists, and the client never learns its ID.

func (*CloudflareStub) NotJSON

func (c *CloudflareStub) NotJSON(body string)

NotJSON makes every answer this body with status 200 (a captive portal or a broken proxy). "" = normal.

func (*CloudflareStub) Requests

func (c *CloudflareStub) Requests() []CloudflareRequest

Requests returns the log.

type Key

type Key struct {
	// Name is the key name, e.g. "acme-agent".
	Name string
	// Secret is the base64 secret, as in BIND's key statement.
	Secret string
	// Algorithm is "hmac-sha256" (default), "hmac-sha384" or "hmac-sha512".
	Algorithm string
	// Allow limits the names this key may change (BIND's update-policy). Nil
	// allows every name in the zone. A refused name makes the whole UPDATE
	// REFUSED.
	Allow func(name string) bool
}

Key is a TSIG key the server accepts for UPDATE.

type Message

type Message struct {
	Network string // "udp" or "tcp"
	Opcode  string // "QUERY" or "UPDATE"
	Name    string // the question name, or the zone of an UPDATE
	Type    string // the question type
	Rcode   string // the answer's response code
	Signed  bool   // the request carried a TSIG record
	TSIGOK  bool   // ... and it verified
	Updates int    // records in the update section
}

Message is one request the server received.

type Record

type Record struct{ Name, Value string }

Record is one TXT value at one name.

type Server

type Server struct {
	// Store holds the zones; tests read and write it directly.
	Store *Store
	// contains filtered or unexported fields
}

Server is the running test server.

func Start

func Start(tb testing.TB, store *Store, keys ...Key) *Server

Start runs a server on 127.0.0.1 (the same random port for UDP and TCP) until the test ends. Without keys, UPDATE needs no signature.

func (*Server) Addr

func (s *Server) Addr() string

Addr is host:port of the server (UDP and TCP).

func (*Server) ForgeResponseMAC

func (s *Server) ForgeResponseMAC(on bool)

ForgeResponseMAC makes every signed answer carry a wrong MAC.

func (*Server) HideTXT

func (s *Server) HideTXT(f func(name, value string) bool)

HideTXT makes TXT answers skip the records the function selects, while an UPDATE still stores them: the record "never appears" for a checker but does exist, so a test can still see whether it was cleaned up. Nil shows everything again.

func (*Server) Host

func (s *Server) Host() string

Host is the IP address of the server.

func (*Server) Messages

func (s *Server) Messages() []Message

Messages returns a copy of the log.

func (*Server) Queries

func (s *Server) Queries(name, qtype string) []Message

Queries returns the QUERY messages for a name and type ("" = any).

func (*Server) Updates

func (s *Server) Updates() int

Updates counts the UPDATE messages that were applied.

type Store

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

Store holds the zones and records of the test server. It is safe for concurrent use. Names are compared in canonical form (lower case, fully qualified), so callers may pass "Host.Example.lan" or "host.example.lan.".

func NewStore

func NewStore(zones ...string) *Store

NewStore returns a store that is authoritative for the given zones.

func (*Store) AddTXT

func (s *Store) AddTXT(name, value string)

AddTXT adds a TXT value; an identical value is kept once (RFC 2136 §2.5.1).

func (*Store) AddZone

func (s *Store) AddZone(zone string)

AddZone makes the store authoritative for a zone.

func (*Store) CNAME

func (s *Store) CNAME(name string) (string, bool)

CNAME returns the target of an alias.

func (*Store) ChallengeRecords

func (s *Store) ChallengeRecords() []Record

ChallengeRecords returns every TXT record whose name starts with _acme-challenge., sorted. A test that ends with an empty result proves the agent cleaned up.

func (*Store) DeleteTXT

func (s *Store) DeleteTXT(name, value string) bool

DeleteTXT removes one value (RFC 2136 §2.5.4) and reports whether it existed.

func (*Store) DeleteTXTSet

func (s *Store) DeleteTXTSet(name string)

DeleteTXTSet removes all TXT values of a name (RFC 2136 §2.5.2).

func (*Store) HasName

func (s *Store) HasName(name string) bool

HasName reports whether any record lives at name.

func (*Store) Serial

func (s *Store) Serial() uint32

Serial is the current SOA serial.

func (*Store) SetCNAME

func (s *Store) SetCNAME(name, target string)

SetCNAME makes name an alias of target (CNAME delegation of _acme-challenge). An empty target removes the alias.

func (*Store) TXT

func (s *Store) TXT(name string) []string

TXT returns the values of a name.

func (*Store) ZoneOf

func (s *Store) ZoneOf(name string) (string, bool)

ZoneOf returns the longest zone that contains name.

func (*Store) Zones

func (s *Store) Zones() []string

Zones returns the zones the store is authoritative for.

Jump to

Keyboard shortcuts

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