minest

package module
v0.0.0-...-9ad9b29 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: 10 Imported by: 2

README

Golang Minimal Network Stack

GoDoc Build Status codecov

The minest Go package implements a minimal network stack with a DNS resolver that can be wired to the standard library or to bassosimone/uis, thus enabling to write integration tests using TCP in userspace.

Basic usage is like:

import (
	"context"
	"log"
	"net"
	"net/netip"

	"github.com/bassosimone/minest"
)

ctx := context.Background()

// Create and use a dialer using the standard library
dialer1 := minest.NewDialer(&net.Dialer{}, &net.Resolver{})
conn1, err := dialer1.DialContext(ctx, "tcp", "8.8.8.8:443")

// Create a DNS-over-UDP resolver using the dialer to create connections
txp := minest.NewDNSOverUDPTransport(dialer1, netip.MustParseAddrPort("8.8.4.4:53"))
reso := minest.NewResolver(txp)
addrs, err := reso.LookupA(ctx, "dns.google")

// Create a second dialer using the above resolver
dialer2 := minest.NewDialer(&net.Dialer{}, reso)
conn2, err := dialer2.DialContext(ctx, "tcp", "8.8.8.8:443")

The Resolver type depends on a DNSTransport that is not only compatible with the DNSOverUDPTransport type but also with the transports in:

Therefore, the Resolver can use DNS over UDP, TCP, TLS, QUIC, HTTPS and HTTP3.

Installation

To add this package as a dependency to your module:

go get github.com/bassosimone/minest

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 and ooni/netem.

Documentation

Overview

Package minest implements a minimal network stack.

The *Dialer and *Resolver types are like *net.Dialer and *net.Resolver but depend on interfaces. This design choice allows to use multiple network backends (including, e.g., github.com/bassosimone/uis as the backend), with the most typical backend being the standard library itself.

The *Resolver depends on NetDialer, which is an interface implemented by both *net.Dialer and *Dialer. The *Dialer depend on NetDialer and DialerResolver, which is an interface implemented by both *net.Resolver and Resolver.

A *Resolver also depends on a DNSTransport. This package includes DNSOverUDPTransport, which implements DNSTransport for DNS-over-UDP but you can also use github.com/bassosimone/dnsoverhttps and github.com/bassosimone/dnsoverstream as transports. Thus, the *Resolver can query using DNS over UDP, TCP, TLS, QUIC, HTTPS, and HTTP3.

This package focuses on measuring the internet, therefore it is optimized for simplicity and does not implement performance optimizations such as happy eyeballs inside its *Dialer.

Index

Constants

View Source
const DefaultResolverTimeout = 10 * time.Second

DefaultResolverTimeout is the default lookup timeout used by *Resolver.

Variables

This section is empty.

Functions

This section is empty.

Types

type DNSOverUDPTransport

type DNSOverUDPTransport struct {
	// Dialer is the [NetDialer] to use to create connections.
	//
	// Set by [NewDNSOverUDPTransport] to the user-provided value.
	Dialer NetDialer

	// Endpoint is the server endpoint to use to query.
	//
	// Set by [NewDNSOverUDPTransport] to the user-provided value.
	Endpoint netip.AddrPort

	// 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)
}

DNSOverUDPTransport implements DNSTransport for DNS over UDP.

Construct using NewDNSOverUDPTransport.

func NewDNSOverUDPTransport

func NewDNSOverUDPTransport(dialer NetDialer, endpoint netip.AddrPort) *DNSOverUDPTransport

NewDNSOverUDPTransport creates a new *DNSOverUDPTransport.

func (*DNSOverUDPTransport) Dial

func (dt *DNSOverUDPTransport) Dial(ctx context.Context) (net.Conn, error)

Dial creates a net.Conn with the configured endpoint.

This method enables building long-lived connections and reusing them across multiple exchanges via *DNSOverUDPTransport.ExchangeWithConn.

func (*DNSOverUDPTransport) Exchange

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

Exchange implements DNSTransport.

func (*DNSOverUDPTransport) ExchangeWithConn

func (dt *DNSOverUDPTransport) ExchangeWithConn(ctx context.Context,
	conn net.Conn, query *dnscodec.Query) (*dnscodec.Response, error)

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

This method allows reusing a long-lived connection across multiple exchanges.

func (*DNSOverUDPTransport) RecvResponse

func (dt *DNSOverUDPTransport) RecvResponse(
	ctx context.Context, conn net.Conn, queryMsg *dns.Msg) (*dnscodec.Response, error)

RecvResponse receives a *dnscodec.Response using a net.Conn.

We only honor deadlines from the context; canceling the context without a deadline does not interrupt I/O. This behavior may change in the future.

func (*DNSOverUDPTransport) SendQuery

func (dt *DNSOverUDPTransport) SendQuery(ctx context.Context, conn net.Conn, query *dnscodec.Query) (*dns.Msg, error)

SendQuery sends a *dnscodec.Query using a net.Conn.

We only honor deadlines from the context; canceling the context without a deadline does not interrupt I/O. This behavior may change in the future.

type DNSTransport

type DNSTransport interface {
	Exchange(ctx context.Context, query *dnscodec.Query) (*dnscodec.Response, error)
}

DNSTransport performs a DNS messages exchange.

type Dialer

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

Dialer allows to dial net.Conn connections pretty much like *net.Dialer except that here we use a NetDialer as the dialing backend.

Construct using NewDialer.

This *Dialer does not implement happy eyeballs and is instead very simple and focused on measuring network interference.

func NewDialer

func NewDialer(udialer NetDialer, reso DialerResolver) *Dialer

NewDialer creates a new *Dialer instance.

func (*Dialer) DialContext

func (d *Dialer) DialContext(ctx context.Context, network string, address string) (net.Conn, error)

DialContext creates a new net.Conn connection.

type DialerResolver

type DialerResolver interface {
	LookupHost(ctx context.Context, name string) ([]string, error)
}

DialerResolver is the resolver expected by *Dialer.

Both *net.Resolver and *Resolver implement this interface.

type NetDialer

type NetDialer interface {
	DialContext(ctx context.Context, network, address string) (net.Conn, error)
}

NetDialer abstracts over *net.Dialer.

type Resolver

type Resolver struct {
	// Transports are the [DNSTransport] to use.
	//
	// Set by [NewResolver] to the user-provided value.
	Transports []DNSTransport

	// Timeout is the overall lookup timeout.
	//
	// Set by [NewResolver] to [DefaultResolverTimeout].
	Timeout time.Duration
}

Resolver behaves like *net.Resolver but uses a DNSTransport.

Construct using NewResolver.

func NewResolver

func NewResolver(transport ...DNSTransport) *Resolver

NewResolver creactes a new *Resolver instance.

func (*Resolver) LookupA

func (r *Resolver) LookupA(ctx context.Context, domain string) ([]string, error)

LookupA resolves a domain to IPv4 addrs.

func (*Resolver) LookupAAAA

func (r *Resolver) LookupAAAA(ctx context.Context, domain string) ([]string, error)

LookupAAAA resolves a domain to IPv6 addrs.

func (*Resolver) LookupCNAME

func (r *Resolver) LookupCNAME(ctx context.Context, domain string) (string, error)

LookupCNAME resolves a domain to its CNAME.

func (*Resolver) LookupHost

func (r *Resolver) LookupHost(ctx context.Context, domain string) ([]string, error)

LookupHost resolves a domain to IPv4 and IPv6 addrs.

Jump to

Keyboard shortcuts

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