dnsoverhttps

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

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

Go to latest
Published: Mar 2, 2026 License: GPL-3.0 Imports: 6 Imported by: 0

README

Golang DNS-over-HTTPS transport

GoDoc Build Status codecov

The dnsoverhttps Go package implements a DNS-over-HTTPS transport with a small API suited for measurements and testing.

Basic usage is like:

import (
	"context"
	"log"
	"net/http"

	"github.com/bassosimone/dnscodec"
	"github.com/bassosimone/dnsoverhttps"
	"github.com/miekg/dns"
)

// 1. create the DNS-over-HTTPS transport
client := &http.Client{}
dt := dnsoverhttps.NewTransport(client, "https://dns.google/dns-query")

// 2. create and send a query
query := dnscodec.NewQuery("dns.google", dns.TypeA)
resp, err := dt.Exchange(context.Background(), query)
if err != nil {
	log.Fatal(err)
}

Features

  • DNS-over-HTTPS: Implements POST-based DNS-over-HTTPS.

  • Small API: One transport with a single Exchange method.

  • Deterministic queries: Mutates queries for transport needs while keeping the caller's query intact.

Installation

To add this package as a dependency to your module:

go get github.com/bassosimone/dnsoverhttps

Development

To run the tests:

go test -v .

To measure test coverage:

go test -v -cover .

License

SPDX-License-Identifier: GPL-3.0-or-later

History

Adapted from rbmk-project/rbmk.

Documentation

Overview

Package dnsoverhttps implements a DNS-over-HTTPS transport.

The API is intentionally small and designed for measurement use cases.

Example (WithLocalServer)
package main

import (
	"context"
	"crypto/tls"
	"fmt"
	"net"
	"net/http"
	"net/netip"
	"slices"

	"github.com/bassosimone/dnscodec"
	"github.com/bassosimone/dnsoverhttps"
	"github.com/bassosimone/dnstest"
	"github.com/bassosimone/pkitest"
	"github.com/bassosimone/runtimex"
	"github.com/miekg/dns"
)

func main() {
	// 1. Create PKI for testing
	//
	// See https://github.com/bassosimone/pkitest
	pki := pkitest.MustNewPKI("testdata")
	certConfig := &pkitest.SelfSignedCertConfig{
		CommonName:   "example.com",
		DNSNames:     []string{"example.com"},
		IPAddrs:      []net.IP{net.IPv4(127, 0, 0, 1)},
		Organization: []string{"Example"},
	}
	cert := pki.MustNewCert(certConfig)
	clientConfig := &tls.Config{RootCAs: pki.CertPool()}

	// 2. Create DNS server for testing
	//
	// See https://github.com/bassosimone/dnstest
	dnsConfig := dnstest.NewHandlerConfig()
	dnsConfig.AddNetipAddr("dns.google", netip.MustParseAddr("8.8.4.4"))
	dnsConfig.AddNetipAddr("dns.google", netip.MustParseAddr("8.8.8.8"))
	dnsHandler := dnstest.NewHandler(dnsConfig)
	srv := dnstest.MustNewHTTPSServer(&net.ListenConfig{}, "127.0.0.1:0", cert, dnsHandler)
	defer srv.Close()

	// 3. Create the DNS transport
	httpClient := &http.Client{Transport: &http.Transport{TLSClientConfig: clientConfig}}
	dt := dnsoverhttps.NewTransport(httpClient, srv.URL())

	// 4. Create the query
	query := dnscodec.NewQuery("dns.google", dns.TypeA)

	// 5. Exchange with the server
	ctx := context.Background()
	resp := runtimex.PanicOnError1(dt.Exchange(ctx, query))

	// 6. Obtain the A records from the response
	addrs := runtimex.PanicOnError1(resp.RecordsA())

	// 7. Sort and print the addresses
	slices.Sort(addrs)
	fmt.Printf("%+v\n", addrs)

}
Output:

[8.8.4.4 8.8.8.8]

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func NewRequest

func NewRequest(ctx context.Context, query *dnscodec.Query, URL string) (*http.Request, *dns.Msg, error)

NewRequest serializes a DNS query message into an HTTP request.

Returns the HTTP request ready for the round trip and the *dns.Msg query, which is required later on to properly validate the DNS response.

func NewRequestWithHook

func NewRequestWithHook(ctx context.Context,
	query *dnscodec.Query, URL string, observeHook func([]byte)) (*http.Request, *dns.Msg, error)

NewRequestWithHook is like NewRequest but calls observeHook with a copy of the raw DNS query after serialization. If observeHook is nil, it is not called.

func ReadResponse

func ReadResponse(ctx context.Context, resp *http.Response, query *dns.Msg) (*dnscodec.Response, error)

ReadResponse reads and validates a DNS response as the response for the given query.

Because this function reads the whole response body, it closes it when done.

The context is used to interrupt reading the round trip or reading the response body.

func ReadResponseWithHook

func ReadResponseWithHook(ctx context.Context,
	httpResp *http.Response, queryMsg *dns.Msg, observeHook func([]byte)) (*dnscodec.Response, error)

ReadResponseWithHook is like ReadResponse but calls observeHook with a copy of the raw DNS response after reading. If observeHook is nil, it is not called.

Types

type Client

type Client interface {
	Do(req *http.Request) (*http.Response, error)
}

Client abstracts over *http.Client.

type Transport

type Transport struct {
	// Client is the [Client] to use to exchange a query for a response.
	//
	// Set by [NewTransport] to the user-provided value.
	Client Client

	// URL is the server URL to use to exchange a query for a response.
	//
	// Set by [NewTransport] to the user-provided value.
	URL string

	// ObserveRawQuery is an optional hook called with a copy of the raw DNS query.
	ObserveRawQuery func([]byte)

	// ObserveRawResponse is an optional hook called with a copy of the raw DNS response.
	ObserveRawResponse func([]byte)
}

Transport is a DNS-over-HTTPS transport.

Construct using NewTransport.

func NewTransport

func NewTransport(client Client, URL string) *Transport

NewTransport creates a new *Transport.

func (*Transport) Exchange

func (dt *Transport) Exchange(ctx context.Context, query *dnscodec.Query) (*dnscodec.Response, error)

Exchange sends a *dnscodec.Query and receives a *dnscodec.Response.

Jump to

Keyboard shortcuts

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