//go:build standalone package checker import ( "context" "encoding/json" "net/http" "net/http/httptest" "net/url" "strings" "testing" "github.com/miekg/dns" ) // stubTLSA returns a synthetic TLSA RR with the given fields, avoiding the // textual-parse boilerplate of dns.NewRR. func stubTLSA(owner string, usage, selector, matching uint8, cert string) *dns.TLSA { return &dns.TLSA{ Hdr: dns.RR_Header{Name: dns.Fqdn(owner), Rrtype: dns.TypeTLSA, Class: dns.ClassINET, Ttl: 3600}, Usage: usage, Selector: selector, MatchingType: matching, Certificate: cert, } } func withStubLookup(t *testing.T, records []*dns.TLSA, err error) { t.Helper() withStubLookupValidated(t, records, true, err) } func withStubLookupValidated(t *testing.T, records []*dns.TLSA, validated bool, err error) { t.Helper() prev := tlsaLookup tlsaLookup = func(_ context.Context, _ string) ([]*dns.TLSA, bool, error) { return records, validated, err } t.Cleanup(func() { tlsaLookup = prev }) } func postForm(values url.Values) *http.Request { req := httptest.NewRequest("POST", "/check", strings.NewReader(values.Encode())) req.Header.Set("Content-Type", "application/x-www-form-urlencoded") req.ParseForm() return req } func TestParseForm_PopulatesServiceFromDNS(t *testing.T) { withStubLookup(t, []*dns.TLSA{ stubTLSA("_443._tcp.example.com", 3, 1, 1, "DEADBEEF"), stubTLSA("_443._tcp.example.com", 2, 0, 1, "cafebabe"), }, nil) p := &daneProvider{} opts, err := p.ParseForm(postForm(url.Values{ "domain_name": {"example.com"}, "port": {"443"}, "proto": {"tcp"}, })) if err != nil { t.Fatalf("ParseForm: %v", err) } svc, ok := opts[OptionService].(serviceMessage) if !ok { t.Fatalf("service option has wrong type: %#v", opts[OptionService]) } if svc.Type != serviceType { t.Errorf("service type = %q, want %q", svc.Type, serviceType) } if svc.Domain != "example.com" { t.Errorf("service domain = %q, want example.com", svc.Domain) } var body struct { TLSA []struct { Hdr struct { Name string } Usage uint8 Selector uint8 MatchingType uint8 Certificate string } `json:"tlsa"` } if err := json.Unmarshal(svc.Service, &body); err != nil { t.Fatalf("decode service body: %v", err) } if len(body.TLSA) != 2 { t.Fatalf("got %d TLSA entries, want 2", len(body.TLSA)) } if body.TLSA[0].Certificate != "deadbeef" { t.Errorf("expected lowercased cert, got %q", body.TLSA[0].Certificate) } if body.TLSA[0].Hdr.Name != "_443._tcp.example.com" { t.Errorf("owner = %q, want _443._tcp.example.com", body.TLSA[0].Hdr.Name) } } func TestParseForm_NoRecordsIsError(t *testing.T) { withStubLookup(t, nil, nil) p := &daneProvider{} _, err := p.ParseForm(postForm(url.Values{ "domain_name": {"example.com"}, "port": {"443"}, "proto": {"tcp"}, })) if err == nil { t.Fatal("expected error when no TLSA records found, got nil") } if !strings.Contains(err.Error(), "no TLSA records") { t.Errorf("unexpected error %v", err) } } func TestParseForm_StartTLSOverride(t *testing.T) { withStubLookup(t, []*dns.TLSA{stubTLSA("_25._tcp.mail.example.com", 3, 1, 1, "aa")}, nil) p := &daneProvider{} opts, err := p.ParseForm(postForm(url.Values{ "domain_name": {"mail.example.com"}, "port": {"25"}, "proto": {"tcp"}, "starttls": {"smtp"}, })) if err != nil { t.Fatalf("ParseForm: %v", err) } override, ok := opts[OptionSTARTTLS].(map[string]string) if !ok { t.Fatalf("starttls option type = %T", opts[OptionSTARTTLS]) } if override["25/tcp"] != "smtp" { t.Errorf("override[25/tcp] = %q, want smtp", override["25/tcp"]) } } func TestParseForm_InvalidPort(t *testing.T) { p := &daneProvider{} _, err := p.ParseForm(postForm(url.Values{ "domain_name": {"example.com"}, "port": {"0"}, "proto": {"tcp"}, })) if err == nil { t.Fatal("expected error for port 0") } }