🏡 index : quine-global/quintodrome.git

package tests

import (
	"context"
	"encoding/binary"
	"io"
	"net"
	"net/netip"

	"golang.org/x/net/dns/dnsmessage"
)

// StubResolver returns a net.Resolver that answers A queries from records (absolute name to IPv4,
// e.g. "localhost." to "127.0.0.1") instead of the system DNS. Other names do not resolve.
func StubResolver(records map[string]string) *net.Resolver {
	return &net.Resolver{
		PreferGo: true,
		Dial: func(context.Context, string, string) (net.Conn, error) {
			client, server := net.Pipe()
			go serveStubDNS(server, records)
			return client, nil
		},
	}
}

// net.Pipe is not a PacketConn, so the resolver sends one query framed with a 2-byte length prefix.
func serveStubDNS(conn net.Conn, records map[string]string) {
	defer conn.Close()
	var size [2]byte
	if _, err := io.ReadFull(conn, size[:]); err != nil {
		return
	}
	buf := make([]byte, binary.BigEndian.Uint16(size[:]))
	if _, err := io.ReadFull(conn, buf); err != nil {
		return
	}
	var msg dnsmessage.Message
	if err := msg.Unpack(buf); err != nil || len(msg.Questions) != 1 {
		return
	}
	msg.Response = true
	q := msg.Questions[0]
	if ip, err := netip.ParseAddr(records[q.Name.String()]); err == nil && q.Type == dnsmessage.TypeA {
		msg.Answers = []dnsmessage.Resource{{
			Header: dnsmessage.ResourceHeader{Name: q.Name, Type: q.Type, Class: q.Class},
			Body:   &dnsmessage.AResource{A: ip.As4()},
		}}
	}
	resp, err := msg.Pack()
	if err != nil {
		return
	}
	_, _ = conn.Write(append(binary.BigEndian.AppendUint16(nil, uint16(len(resp))), resp...))
}