refactor: simplify record matching

This commit is contained in:
Fernandez Ludovic 2026-02-25 19:01:09 +01:00
commit e263ebd77c
3 changed files with 50 additions and 45 deletions

View file

@ -170,36 +170,24 @@ func (c *Challenge) selectIssuerDomainName(challIssuers []string, records []TXTR
}
func (c *Challenge) hasMatchingRecord(records []TXTRecord, issuerDomainName string, wildcard bool) bool {
for _, record := range records {
parsed, err := parseIssueValue(record.Value)
if err != nil {
continue
}
if parsed.IssuerDomainName != issuerDomainName {
continue
}
if parsed.AccountURI != c.accountURI {
continue
}
if wildcard && !strings.EqualFold(parsed.Policy, policyWildcard) {
continue
}
if c.persistUntil.IsZero() {
if !parsed.PersistUntil.IsZero() {
continue
}
} else if parsed.PersistUntil.IsZero() || !parsed.PersistUntil.Equal(c.persistUntil) {
continue
}
return true
iv := IssueValue{
IssuerDomainName: issuerDomainName,
AccountURI: c.accountURI,
PersistUntil: c.persistUntil,
}
return false
if wildcard {
iv.Policy = policyWildcard
}
return slices.ContainsFunc(records, func(record TXTRecord) bool {
parsed, err := parseIssueValue(record.Value)
if err != nil {
return false
}
return parsed.match(iv)
})
}
// GetAuthorizationDomainName returns the fully qualified DNS label

View file

@ -1,6 +1,7 @@
package dnspersist01
import (
"cmp"
"errors"
"fmt"
"strconv"
@ -23,6 +24,22 @@ type IssueValue struct {
PersistUntil time.Time
}
func (v *IssueValue) match(other IssueValue) bool {
if cmp.Or(
cmp.Compare(v.IssuerDomainName, other.IssuerDomainName),
cmp.Compare(v.AccountURI, other.AccountURI),
v.PersistUntil.Compare(other.PersistUntil),
) != 0 {
return false
}
if strings.EqualFold(other.Policy, policyWildcard) && !strings.EqualFold(v.Policy, policyWildcard) {
return false
}
return true
}
// buildIssueValue constructs an RFC 8659 issue-value for a dns-persist-01 TXT record.
// issuerDomainName and accountURI are required.
// wildcard and persistUntil are optional.
@ -54,15 +71,15 @@ func buildIssueValue(issuerDomainName, accountURI string, wildcard bool, persist
// It returns an error if any portion of the value is malformed.
//
//nolint:gocyclo // parsing and validating tagged parameters requires branching
func parseIssueValue(value string) (IssueValue, error) {
func parseIssueValue(value string) (*IssueValue, error) {
fields := strings.Split(value, ";")
issuerDomainName := trimWSP(fields[0])
if issuerDomainName == "" {
return IssueValue{}, errors.New("missing issuer-domain-name")
return nil, errors.New("missing issuer-domain-name")
}
parsed := IssueValue{
parsed := &IssueValue{
IssuerDomainName: issuerDomainName,
}
@ -72,24 +89,24 @@ func parseIssueValue(value string) (IssueValue, error) {
for _, raw := range fields[1:] {
part := trimWSP(raw)
if part == "" {
return IssueValue{}, errors.New("empty parameter or trailing semicolon provided")
return nil, errors.New("empty parameter or trailing semicolon provided")
}
// Capture each tag=value pair.
tag, val, found := strings.Cut(part, "=")
if !found {
return IssueValue{}, fmt.Errorf("malformed parameter %q should be tag=value pair", part)
return nil, fmt.Errorf("malformed parameter %q should be tag=value pair", part)
}
tag = trimWSP(tag)
if tag == "" {
return IssueValue{}, fmt.Errorf("malformed parameter %q, empty tag", part)
return nil, fmt.Errorf("malformed parameter %q, empty tag", part)
}
canonicalTag := strings.ToLower(tag)
if seenTags[canonicalTag] {
return IssueValue{}, fmt.Errorf("duplicate parameter %q", tag)
return nil, fmt.Errorf("duplicate parameter %q", tag)
}
seenTags[canonicalTag] = true
@ -102,7 +119,7 @@ func parseIssueValue(value string) (IssueValue, error) {
continue
}
return IssueValue{}, fmt.Errorf("malformed value %q for tag %q", val, tag)
return nil, fmt.Errorf("malformed value %q for tag %q", val, tag)
}
// Finally, capture expected tag values.
@ -111,7 +128,7 @@ func parseIssueValue(value string) (IssueValue, error) {
switch canonicalTag {
case paramAccountURI:
if val == "" {
return IssueValue{}, fmt.Errorf("empty value provided for mandatory %q", paramAccountURI)
return nil, fmt.Errorf("empty value provided for mandatory %q", paramAccountURI)
}
parsed.AccountURI = val
@ -130,7 +147,7 @@ func parseIssueValue(value string) (IssueValue, error) {
case paramPersistUntil:
ts, err := strconv.ParseInt(val, 10, 64)
if err != nil {
return IssueValue{}, fmt.Errorf("malformed %q: %w", paramPersistUntil, err)
return nil, fmt.Errorf("malformed %q: %w", paramPersistUntil, err)
}
parsed.PersistUntil = time.Unix(ts, 0).UTC()

View file

@ -67,13 +67,13 @@ func Test_parseIssueValue(t *testing.T) {
testCases := []struct {
desc string
value string
expected IssueValue
expected *IssueValue
expectErrContains string
}{
{
desc: "basic",
value: "authority.example; accounturi=https://authority.example/acct/123",
expected: IssueValue{
expected: &IssueValue{
IssuerDomainName: "authority.example",
AccountURI: "https://authority.example/acct/123",
},
@ -81,7 +81,7 @@ func Test_parseIssueValue(t *testing.T) {
{
desc: "wildcard policy is case-insensitive",
value: "authority.example; accounturi=https://authority.example/acct/123; policy=wIlDcArD",
expected: IssueValue{
expected: &IssueValue{
IssuerDomainName: "authority.example",
AccountURI: "https://authority.example/acct/123",
Policy: "wIlDcArD",
@ -90,7 +90,7 @@ func Test_parseIssueValue(t *testing.T) {
{
desc: "unknown param",
value: "authority.example; accounturi=https://authority.example/acct/123; extra=value",
expected: IssueValue{
expected: &IssueValue{
IssuerDomainName: "authority.example",
AccountURI: "https://authority.example/acct/123",
},
@ -98,7 +98,7 @@ func Test_parseIssueValue(t *testing.T) {
{
desc: "unknown tag with empty value",
value: "authority.example; accounturi=https://authority.example/acct/123; foo=",
expected: IssueValue{
expected: &IssueValue{
IssuerDomainName: "authority.example",
AccountURI: "https://authority.example/acct/123",
},
@ -106,7 +106,7 @@ func Test_parseIssueValue(t *testing.T) {
{
desc: "unknown tags with unusual formatting are ignored",
value: "authority.example;accounturi=https://authority.example/acct/123;bad tag=value;\nweird=\\x01337",
expected: IssueValue{
expected: &IssueValue{
IssuerDomainName: "authority.example",
AccountURI: "https://authority.example/acct/123",
},
@ -114,7 +114,7 @@ func Test_parseIssueValue(t *testing.T) {
{
desc: "all known fields with heavy whitespace",
value: " authority.example ; accounturi = https://authority.example/acct/123 ; policy = wildcard ; persistUntil = 4102444800 ",
expected: IssueValue{
expected: &IssueValue{
IssuerDomainName: "authority.example",
AccountURI: "https://authority.example/acct/123",
Policy: "wildcard",
@ -124,7 +124,7 @@ func Test_parseIssueValue(t *testing.T) {
{
desc: "policy other than wildcard is treated as absent",
value: "authority.example; accounturi=https://authority.example/acct/123; policy=notwildcard",
expected: IssueValue{
expected: &IssueValue{
IssuerDomainName: "authority.example",
AccountURI: "https://authority.example/acct/123",
},
@ -132,7 +132,7 @@ func Test_parseIssueValue(t *testing.T) {
{
desc: "missing accounturi",
value: "authority.example",
expected: IssueValue{
expected: &IssueValue{
IssuerDomainName: "authority.example",
},
},