github.com/letsencrypt/boulder@v0.20251208.0/bdns/mocks.go (about)

     1  package bdns
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"net"
     8  	"net/netip"
     9  	"os"
    10  
    11  	"github.com/miekg/dns"
    12  
    13  	blog "github.com/letsencrypt/boulder/log"
    14  )
    15  
    16  // MockClient is a mock
    17  type MockClient struct {
    18  	Log blog.Logger
    19  }
    20  
    21  // LookupTXT is a mock
    22  func (mock *MockClient) LookupTXT(_ context.Context, hostname string) ([]string, ResolverAddrs, error) {
    23  	// Use the example account-specific label prefix derived from
    24  	// "https://example.com/acme/acct/ExampleAccount"
    25  	const accountLabelPrefix = "_ujmmovf2vn55tgye._acme-challenge"
    26  
    27  	if hostname == accountLabelPrefix+".servfail.com" {
    28  		// Mirror dns-01 servfail behaviour
    29  		return nil, ResolverAddrs{"MockClient"}, fmt.Errorf("SERVFAIL")
    30  	}
    31  	if hostname == accountLabelPrefix+".good-dns01.com" {
    32  		// Mirror dns-01 good record
    33  		// base64(sha256("LoqXcYV8q5ONbJQxbmR7SCTNo3tiAXDfowyjxAjEuX0"
    34  		//               + "." + "9jg46WB3rR_AHD-EBXdN7cBkH1WOu0tA3M9fm21mqTI"))
    35  		return []string{"LPsIwTo7o8BoG0-vjCyGQGBWSVIPxI-i_X336eUOQZo"}, ResolverAddrs{"MockClient"}, nil
    36  	}
    37  	if hostname == accountLabelPrefix+".wrong-dns01.com" {
    38  		// Mirror dns-01 wrong record
    39  		return []string{"a"}, ResolverAddrs{"MockClient"}, nil
    40  	}
    41  	if hostname == accountLabelPrefix+".wrong-many-dns01.com" {
    42  		// Mirror dns-01 wrong-many record
    43  		return []string{"a", "b", "c", "d", "e"}, ResolverAddrs{"MockClient"}, nil
    44  	}
    45  	if hostname == accountLabelPrefix+".long-dns01.com" {
    46  		// Mirror dns-01 long record
    47  		return []string{"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}, ResolverAddrs{"MockClient"}, nil
    48  	}
    49  	if hostname == accountLabelPrefix+".no-authority-dns01.com" {
    50  		// Mirror dns-01 no-authority good record
    51  		// base64(sha256("LoqXcYV8q5ONbJQxbmR7SCTNo3tiAXDfowyjxAjEuX0"
    52  		//               + "." + "9jg46WB3rR_AHD-EBXdN7cBkH1WOu0tA3M9fm21mqTI"))
    53  		return []string{"LPsIwTo7o8BoG0-vjCyGQGBWSVIPxI-i_X336eUOQZo"}, ResolverAddrs{"MockClient"}, nil
    54  	}
    55  	if hostname == accountLabelPrefix+".empty-txts.com" {
    56  		// Mirror dns-01 zero TXT records
    57  		return []string{}, ResolverAddrs{"MockClient"}, nil
    58  	}
    59  
    60  	if hostname == "_acme-challenge.servfail.com" {
    61  		return nil, ResolverAddrs{"MockClient"}, fmt.Errorf("SERVFAIL")
    62  	}
    63  	if hostname == "_acme-challenge.good-dns01.com" {
    64  		// base64(sha256("LoqXcYV8q5ONbJQxbmR7SCTNo3tiAXDfowyjxAjEuX0"
    65  		//               + "." + "9jg46WB3rR_AHD-EBXdN7cBkH1WOu0tA3M9fm21mqTI"))
    66  		// expected token + test account jwk thumbprint
    67  		return []string{"LPsIwTo7o8BoG0-vjCyGQGBWSVIPxI-i_X336eUOQZo"}, ResolverAddrs{"MockClient"}, nil
    68  	}
    69  	if hostname == "_acme-challenge.wrong-dns01.com" {
    70  		return []string{"a"}, ResolverAddrs{"MockClient"}, nil
    71  	}
    72  	if hostname == "_acme-challenge.wrong-many-dns01.com" {
    73  		return []string{"a", "b", "c", "d", "e"}, ResolverAddrs{"MockClient"}, nil
    74  	}
    75  	if hostname == "_acme-challenge.long-dns01.com" {
    76  		return []string{"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}, ResolverAddrs{"MockClient"}, nil
    77  	}
    78  	if hostname == "_acme-challenge.no-authority-dns01.com" {
    79  		// base64(sha256("LoqXcYV8q5ONbJQxbmR7SCTNo3tiAXDfowyjxAjEuX0"
    80  		//               + "." + "9jg46WB3rR_AHD-EBXdN7cBkH1WOu0tA3M9fm21mqTI"))
    81  		// expected token + test account jwk thumbprint
    82  		return []string{"LPsIwTo7o8BoG0-vjCyGQGBWSVIPxI-i_X336eUOQZo"}, ResolverAddrs{"MockClient"}, nil
    83  	}
    84  	// empty-txts.com always returns zero TXT records
    85  	if hostname == "_acme-challenge.empty-txts.com" {
    86  		return []string{}, ResolverAddrs{"MockClient"}, nil
    87  	}
    88  
    89  	// Default fallback
    90  	return []string{"hostname"}, ResolverAddrs{"MockClient"}, nil
    91  }
    92  
    93  // makeTimeoutError returns a a net.OpError for which Timeout() returns true.
    94  func makeTimeoutError() *net.OpError {
    95  	return &net.OpError{
    96  		Err: os.NewSyscallError("ugh timeout", timeoutError{}),
    97  	}
    98  }
    99  
   100  type timeoutError struct{}
   101  
   102  func (t timeoutError) Error() string {
   103  	return "so sloooow"
   104  }
   105  func (t timeoutError) Timeout() bool {
   106  	return true
   107  }
   108  
   109  // LookupHost is a mock
   110  func (mock *MockClient) LookupHost(_ context.Context, hostname string) ([]netip.Addr, ResolverAddrs, error) {
   111  	if hostname == "always.invalid" ||
   112  		hostname == "invalid.invalid" {
   113  		return []netip.Addr{}, ResolverAddrs{"MockClient"}, nil
   114  	}
   115  	if hostname == "always.timeout" {
   116  		return []netip.Addr{}, ResolverAddrs{"MockClient"}, &Error{dns.TypeA, "always.timeout", makeTimeoutError(), -1, nil}
   117  	}
   118  	if hostname == "always.error" {
   119  		err := &net.OpError{
   120  			Op:  "read",
   121  			Net: "udp",
   122  			Err: errors.New("some net error"),
   123  		}
   124  		m := new(dns.Msg)
   125  		m.SetQuestion(dns.Fqdn(hostname), dns.TypeA)
   126  		m.AuthenticatedData = true
   127  		m.SetEdns0(4096, false)
   128  		return []netip.Addr{}, ResolverAddrs{"MockClient"}, &Error{dns.TypeA, hostname, err, -1, nil}
   129  	}
   130  	if hostname == "id.mismatch" {
   131  		err := dns.ErrId
   132  		m := new(dns.Msg)
   133  		m.SetQuestion(dns.Fqdn(hostname), dns.TypeA)
   134  		m.AuthenticatedData = true
   135  		m.SetEdns0(4096, false)
   136  		r := new(dns.Msg)
   137  		record := new(dns.A)
   138  		record.Hdr = dns.RR_Header{Name: dns.Fqdn(hostname), Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 0}
   139  		record.A = net.ParseIP("127.0.0.1")
   140  		r.Answer = append(r.Answer, record)
   141  		return []netip.Addr{}, ResolverAddrs{"MockClient"}, &Error{dns.TypeA, hostname, err, -1, nil}
   142  	}
   143  	// dual-homed host with an IPv6 and an IPv4 address
   144  	if hostname == "ipv4.and.ipv6.localhost" {
   145  		return []netip.Addr{
   146  			netip.MustParseAddr("::1"),
   147  			netip.MustParseAddr("127.0.0.1"),
   148  		}, ResolverAddrs{"MockClient"}, nil
   149  	}
   150  	if hostname == "ipv6.localhost" {
   151  		return []netip.Addr{
   152  			netip.MustParseAddr("::1"),
   153  		}, ResolverAddrs{"MockClient"}, nil
   154  	}
   155  	return []netip.Addr{netip.MustParseAddr("127.0.0.1")}, ResolverAddrs{"MockClient"}, nil
   156  }
   157  
   158  // LookupCAA returns mock records for use in tests.
   159  func (mock *MockClient) LookupCAA(_ context.Context, domain string) ([]*dns.CAA, string, ResolverAddrs, error) {
   160  	return nil, "", ResolverAddrs{"MockClient"}, nil
   161  }