dnscrypt

package module
v0.0.0-...-b5d331f Latest Latest
Warning

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

Go to latest
Published: Jun 4, 2026 License: Unlicense Imports: 29 Imported by: 0

README

DNSCrypt for Go

A modernization of github.com/ameshkov/dnscrypt, an implementation of the DNSCrypt v2 protocol.

Programming interface

See doc/api.md for detailed programming interface documentation.

Command-line interface

DNSCrypt provides CLI tool that can work as a client or server. See doc/cli.md for detailed documentation and examples.

Documentation

Overview

Package dnscrypt includes client-side and server-side implementations of the DNSCrypt v2. See https://dnscrypt.info/protocol for a detailed description of the protocol.

Index

Constants

View Source
const (

	// KeySize is the size of public and secret keys in bytes.  See
	// https://dnscrypt.info/protocol for more information.
	KeySize = 32

	// sharedKeySize is the size of the shared key used to encrypt/decrypt
	// messages.
	SharedKeySize = 32
)
View Source
const (
	// ErrTooShort is returned when the DNS query is shorter than possible.
	ErrTooShort errors.Error = "message is too short"

	// ErrQueryTooLarge is returned when the DNS query is larger than max
	// allowed size.
	ErrQueryTooLarge errors.Error = "dnscrypt query is too large"

	// ErrESVersion is returned when the cert contains unsupported es-version.
	ErrESVersion errors.Error = "unsupported es-version"

	// ErrInvalidDate is returned when the cert is not valid for the current
	// time.
	ErrInvalidDate errors.Error = "cert has invalid ts-start or ts-end"

	// ErrInvalidCertSignature is returned when the cert has invalid signature.
	ErrInvalidCertSignature errors.Error = "cert has invalid signature"

	// ErrInvalidQuery is returned when it failed to decrypt a DNSCrypt query.
	ErrInvalidQuery errors.Error = "dnscrypt query is invalid and cannot be decrypted"

	// ErrInvalidClientMagic is returned when client-magic does not match.
	ErrInvalidClientMagic errors.Error = "dnscrypt query contains invalid client magic"

	// ErrInvalidResolverMagic is returned when server-magic does not match.
	ErrInvalidResolverMagic errors.Error = "dnscrypt response contains invalid resolver magic"

	// ErrInvalidResponse is returned when it failed to decrypt a DNSCrypt
	// response.
	ErrInvalidResponse errors.Error = "dnscrypt response is invalid and cannot be decrypted"

	// ErrInvalidPadding is returned when it failed to unpad a query.
	ErrInvalidPadding errors.Error = "invalid padding"

	// ErrInvalidDNSStamp is returned when an invalid DNS stamp is provided.
	ErrInvalidDNSStamp errors.Error = "invalid stamp"

	// ErrFailedToFetchCert is returned when it failed to fetch DNSCrypt
	// certificate.
	ErrFailedToFetchCert errors.Error = "failed to fetch dnscrypt certificate"

	// ErrCertTooShort is returned when it failed to deserialize cert, too
	// short.
	ErrCertTooShort errors.Error = "cert is too short"

	// ErrCertMagic is returned when an invalid cert magic is encountered.
	ErrCertMagic errors.Error = "invalid cert magic"

	// ErrServerConfig is returned when it failed to start the DNSCrypt server
	// due to invalid configuration.
	ErrServerConfig errors.Error = "invalid server configuration"

	// ErrServerNotStarted is returned if there's nothing to shutdown.
	ErrServerNotStarted errors.Error = "server is not started"

	// ErrServerAlreadyStarted is returned if [Server.Start] is being called on
	// a server that is already started.
	ErrServerAlreadyStarted errors.Error = "server is already started"
)
View Source
const (
	// DNSCryptV2Prefix is the prefix for DNSCrypt v2 provider names.
	DNSCryptV2Prefix = "2.dnscrypt-cert."
)

Variables

This section is empty.

Functions

func HexDecodeKey

func HexDecodeKey(str string) (decoded []byte, err error)

HexDecodeKey decodes a hex-encoded string with (optional) colons to a byte array.

func HexEncodeKey

func HexEncodeKey(b []byte) (encoded string)

HexEncodeKey encodes a byte slice to a hex-encoded string.

Types

type Certificate

type Certificate struct {
	// Serial is a 4 byte serial number in big-endian format.  If more than
	// one certificates are valid, the client must prefer the certificate with
	// a higher serial number.
	Serial uint32

	// ESVersion is the cryptographic construction to use with this
	// certificate.
	ESVersion CryptoConstruction

	// Signature is a 64-byte signature of (<resolver-pk> <client-magic>
	// <serial> <ts-start> <ts-end> <extensions>) using the Ed25519 algorithm
	// and the provider secret key.  Ed25519 must be used in this version of
	// the protocol.
	Signature [ed25519.SignatureSize]byte

	// ResolverPk is the resolver's short-term public key, which is 32 bytes
	// when using X25519.  This key is used to encrypt/decrypt DNS queries.
	ResolverPk [KeySize]byte

	// ResolverSk is the resolver's short-term private key, which is 32 bytes
	// when using X25519.  Note that it's only used in the server implementation
	// and never serialized/deserialized.  This key is used to encrypt/decrypt
	// DNS queries.
	ResolverSk [KeySize]byte

	// ClientMagic is the first 8 bytes of a client query that is to be built
	// using the information from this certificate.  It may be a truncated
	// public key.  Two valid certificates cannot share the same <client-magic>.
	ClientMagic [clientMagicSize]byte

	// NotBefore is the date the certificate is valid from, as a big-endian
	// 4-byte unsigned Unix timestamp.
	NotBefore uint32

	// NotAfter is the date the certificate is valid until (inclusive), as a
	// big-endian 4-byte unsigned Unix timestamp.
	NotAfter uint32
}

Certificate is a DNSCrypt server certificate. See ResolverConfig for more info on how to create one.

func (*Certificate) MarshalBinary

func (c *Certificate) MarshalBinary() (serialized []byte, err error)

MarshalBinary implements the encoding.BinaryMarshaler interface for *Certificate. The certificate is serialised into a byte slice using the following schema: <cert> ::= <cert-magic> <es-version> <protocol-minor-version> <signature> <resolver-pk> <client-magic> <serial> <ts-start> <ts-end>

Certificates made of these information, without extensions, are 116 bytes long. With the addition of the cert-magic, es-version and protocol-minor-version, the record is [certByteLength] bytes long. err is always nil.

func (*Certificate) Sign

func (c *Certificate) Sign(privateKey ed25519.PrivateKey)

Sign creates cert signature. privateKey must not be nil.

func (*Certificate) String

func (c *Certificate) String() (s string)

String implements the fmt.Stringer interface for *Certificate.

func (*Certificate) UnmarshalBinary

func (c *Certificate) UnmarshalBinary(b []byte) (err error)

UnmarshalBinary implements the encoding.BinaryUnmarshaler interface for *Certificate. Certificate is being deserialized using the following schema: <cert> ::= <cert-magic> <es-version> <protocol-minor-version> <signature> <resolver-pk> <client-magic> <serial> <ts-start> <ts-end>

func (*Certificate) Validate

func (c *Certificate) Validate() (err error)

Validate implements the validate.Interface for *Certificate.

func (*Certificate) VerifyDate

func (c *Certificate) VerifyDate() (ok bool)

VerifyDate checks that the cert is valid at this moment.

func (*Certificate) VerifySignature

func (c *Certificate) VerifySignature(publicKey ed25519.PublicKey) (ok bool)

VerifySignature checks if the cert is properly signed with the specified signature. publicKey must not be nil.

type Client

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

Client is a DNSCrypt resolver client.

func NewClient

func NewClient(conf *ClientConfig) (c *Client)

NewClient returns properly initialized *Client. c must be non-nil and valid.

func (*Client) DialContext

func (c *Client) DialContext(ctx context.Context, stampStr string) (info *ResolverInfo, err error)

DialContext fetches and validates DNSCrypt certificate from the given server. Data received during this call is then used for DNS requests encryption/decryption. stampStr is an sdns:// address which is parsed using go-dnsstamps package.

func (*Client) DialStampContext

func (c *Client) DialStampContext(
	ctx context.Context,
	stamp dnsstamps.ServerStamp,
) (info *ResolverInfo, err error)

DialStampContext fetches and validates DNSCrypt certificate from the given server. Data received during this call is then used for DNS requests encryption/decryption.

func (*Client) ExchangeConnContext

func (c *Client) ExchangeConnContext(
	ctx context.Context,
	conn net.Conn,
	m *dns.Msg,
	info *ResolverInfo,
) (resp *dns.Msg, err error)

ExchangeConnContext performs a synchronous DNS query to the specified DNSCrypt server and returns a DNS response. DNSCrypt server information needs to be fetched and validated prior to this call using the Client.DialStampContext method. conn, m, and info must not be nil.

func (*Client) ExchangeContext

func (c *Client) ExchangeContext(
	ctx context.Context,
	m *dns.Msg,
	info *ResolverInfo,
) (resp *dns.Msg, err error)

ExchangeContext performs a synchronous DNS query to the specified DNSCrypt server and returns a DNS response. This method creates a new network connection for every call so avoid using it for TCP. DNSCrypt cert needs to be fetched and validated prior to this call using the Client.DialStampContext method. m and info must not be nil.

type ClientConfig

type ClientConfig struct {
	// Logger is a logger instance for Client.  If not set, slog.Default()
	// will be used.
	Logger *slog.Logger

	// Proto is the base network protocol.
	Proto Proto

	// UDPSize is the maximum size of a DNS response (or query) this client
	// can send or receive.  If not set, we use [dns.MinMsgSize] by default.
	UDPSize int
}

ClientConfig is the configuration structure for Client.

type CryptoConstruction

type CryptoConstruction uint16

CryptoConstruction represents the encryption algorithm (either XSalsa20Poly1305 or XChacha20Poly1305).

const (
	// UndefinedConstruction is the default value for empty CertInfo only.
	UndefinedConstruction CryptoConstruction = iota

	// XSalsa20Poly1305 represents XSalsa20Poly1305 encryption.
	XSalsa20Poly1305 CryptoConstruction = 0x0001

	// XChacha20Poly1305 represents XChacha20Poly1305 encryption.
	XChacha20Poly1305 CryptoConstruction = 0x0002
)

func (CryptoConstruction) String

func (c CryptoConstruction) String() (construction string)

String implements the fmt.Stringer interface for CryptoConstruction.

type Handler

type Handler interface {
	// ServeDNS handles DNS requests.  rw and r must not be nil.
	ServeDNS(ctx context.Context, rw ResponseWriter, r *dns.Msg) (err error)
}

Handler describes DNS handlers.

type Proto

type Proto string

Proto represents the base network protocol.

const (
	// ProtoTCP represents the TCP protocol.
	ProtoTCP Proto = "tcp"

	// ProtoUDP represents the UDP protocol.
	ProtoUDP Proto = "udp"
)

func ProtoFromString

func ProtoFromString(s string) (p Proto, err error)

ProtoFromString converts s into a Proto and makes sure it is valid. This should be preferred to a simple type conversion.

func (Proto) Validate

func (p Proto) Validate() (err error)

Validate implements the validate.Interface for Proto.

type ResolverConfig

type ResolverConfig struct {
	// ProviderName is the DNSCrypt provider name.
	ProviderName string `yaml:"provider_name"`

	// PublicKey is the DNSCrypt resolver public key.
	PublicKey string `yaml:"public_key"`

	// PrivateKey is the DNSCrypt resolver private key.  The main and only
	// purpose of this key is to sign the certificate.
	PrivateKey string `yaml:"private_key"`

	// ResolverSk is a hex-encoded short-term private key.  This key is used to
	// encrypt/decrypt DNS queries.  If not set, we'll generate a new random
	// ResolverSk and ResolverPk.
	ResolverSk string `yaml:"resolver_secret"`

	// ResolverPk is a hex-encoded short-term public key corresponding to
	// ResolverSk.  This key is used to encrypt/decrypt DNS queries.
	ResolverPk string `yaml:"resolver_public"`

	// ESVersion is the crypto to use in this resolver.
	ESVersion CryptoConstruction `yaml:"es_version"`

	// CertificateTTL is the time-to-live value for the certificate that is
	// generated using this ResolverConfig.  If not set, we'll use 1 year by
	// default.
	CertificateTTL time.Duration `yaml:"certificate_ttl"`
}

ResolverConfig is the DNSCrypt resolver configuration.

func GenerateResolverConfig

func GenerateResolverConfig(
	providerName string,
	privateKey ed25519.PrivateKey,
	ttl time.Duration,
) (rc ResolverConfig, err error)

GenerateResolverConfig generates resolver configuration for a given provider name. providerName is mandatory. If needed, DNSCryptV2Prefix prefix is added to it. privateKey is optional. If not set, it will be generated automatically.

func (*ResolverConfig) CreateStamp

func (rc *ResolverConfig) CreateStamp(addr string) (stamp dnsstamps.ServerStamp, err error)

CreateStamp generates a DNS stamp for this resolver.

func (*ResolverConfig) NewCert

func (rc *ResolverConfig) NewCert() (cert *Certificate, err error)

NewCert generates a signed Certificate to be used by Server.

func (*ResolverConfig) Validate

func (rc *ResolverConfig) Validate() (err error)

Validate implements the validate.Interface for *ResolverConfig.

type ResolverInfo

type ResolverInfo struct {
	// ResolverCert contains certificate info (obtained with the first
	// unencrypted DNS request).
	ResolverCert *Certificate

	// ServerAddress is the server IP address.
	ServerAddress string

	// ProviderName is the provider name.
	ProviderName string

	// ServerPublicKey is the resolver public key (this key is used to
	// validate cert signature).
	ServerPublicKey ed25519.PublicKey

	// SharedKey is the shared key that is to be used to encrypt/decrypt
	// messages.
	SharedKey [KeySize]byte

	// SecretKey is the client short-term secret key.
	SecretKey [KeySize]byte

	// PublicKey is the client short-term public key.
	PublicKey [KeySize]byte
}

ResolverInfo contains DNSCrypt resolver information necessary for decryption/encryption.

type ResponseWriter

type ResponseWriter interface {
	// LocalAddr returns local socket address.
	LocalAddr() (addr net.Addr)

	// RemoteAddr returns remote client socket address.
	RemoteAddr() (addr net.Addr)

	// WriteMsg writes response message to the client.  m must not be nil.
	WriteMsg(ctx context.Context, m *dns.Msg) (err error)
}

ResponseWriter describes response writer for various protocols.

type Server

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

Server is a DNSCrypt server implementation.

func NewServer

func NewServer(conf *ServerConfig) (s *Server, err error)

NewServer returns properly initialized *Server. conf must be non-nil and valid.

func (*Server) LocalAddr

func (s *Server) LocalAddr() (addr net.Addr)

LocalAddr returns the local network address for the given protocol, if known.

func (*Server) Shutdown

func (s *Server) Shutdown(ctx context.Context) (err error)

Shutdown implements the service.Interface for *Server. It waits until all connections are processed and only after that it leaves the method. If context deadline is specified, it will exit earlier.

func (*Server) Start

func (s *Server) Start(ctx context.Context) (err error)

Start implements the service.Interface for *Server. It does not block calling goroutine.

type ServerConfig

type ServerConfig struct {
	// Handler to invoke.  If nil, the [DefaultHandler] is used.
	Handler Handler

	// ResolverCert contains resolver certificate.  It must not be nil.
	ResolverCert *Certificate

	// Logger is a logger instance for Server.  If not set, slog.Default() will
	// be used.
	Logger *slog.Logger

	// ProviderName is a DNSCrypt provider name.
	ProviderName string

	// Addr is the address for server to listen.  It must not be empty.
	Addr netip.AddrPort

	// Proto defines protocol for serving.  It must be one of the following:
	//
	//	- [ProtoTCP]
	//	- [ProtoUDP]
	Proto Proto

	// UDPSize is the default buffer size to use to read incoming UDP messages.
	// If not set it defaults to [defaultUDPSize].
	UDPSize uint
}

ServerConfig is the configuration structure for Server.

func (*ServerConfig) Validate

func (c *ServerConfig) Validate() (err error)

Validate implements the validate.Interface for *ServerConfig.

Directories

Path Synopsis
cmd
dnscrypt command
internal
cmd
Package cmd is the DNSCrypt entry point.
Package cmd is the DNSCrypt entry point.
forward
Package forward contains all the necessary utilities for DNS request forwarding.
Package forward contains all the necessary utilities for DNS request forwarding.
version
Package version contains the version information.
Package version contains the version information.
xsecretbox
Package xsecretbox implements encryption/decryption of a message using specified keys.
Package xsecretbox implements encryption/decryption of a message using specified keys.

Jump to

Keyboard shortcuts

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