mirror of
https://github.com/hanzoai/email-verifier.git
synced 2026-10-30 15:04:29 +00:00
* Initial plan * Add support for custom DNS resolver Co-authored-by: NeoCN <[email protected]> * Simplify nil-handling per review feedback Co-authored-by: NeoCN <[email protected]> * Add test covering custom resolver usage in dialSMTP Co-authored-by: NeoCN <[email protected]> * Assert custom resolver error is propagated in dialSMTP test Co-authored-by: NeoCN <[email protected]> * Accept document suggestion Co-authored-by: Copilot Autofix powered by AI <[email protected]> * fix: use require.ErrorContains for error assertion in smtp_test.go (testifylint) Co-authored-by: NeoCN <[email protected]> * fix: reorder assertions to ensure called.Load() check runs before require Co-authored-by: NeoCN <[email protected]> * test: cover Resolver(nil) restoring the default resolver, add changelog entry The nil-handling branch in Resolver() was uncovered: TestNewVerifierOK_DefaultResolver only checks the default set by NewVerifier(), and TestVerifier_Resolver only passes a non-nil resolver, so nothing exercised the reset path. Confirmed the new test is a real guard rather than a tautology: with the `if resolver == nil` branch removed, it fails with actual (*net.Resolver)(nil); with the branch restored it passes. Also adds the missing CHANGELOG entry for this feature, matching the convention in the Unreleased section. Coverage 89.4% -> 89.6%. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> * fix: read net.DefaultResolver at lookup time, not at construction NewVerifier captured net.DefaultResolver into the resolver field, which silently broke callers who redirect DNS by replacing that global -- the only way to do it before this option existed, and the exact workflow described in #186. The order matters and the failing one is the common one. Verified against main and against this branch with an external program calling CheckMX: main before fix after fix construct-then-patch honored IGNORED honored patch-then-construct honored honored honored "construct-then-patch" is the usual shape because README recommends a package level `var verifier = emailverifier.NewVerifier()`, and package variables are initialised before any init() that would install the replacement. The failure was silent: no error, the lookup just went to the system resolver. Keep the field nil unless Resolver() is called and resolve the default through a dnsResolver() accessor, mirroring what the old net.LookupMX call did (it reads net.DefaultResolver on every call). An explicit Resolver() still takes precedence over the global; confirmed by the same program. Also drops the now-redundant nil coercion in Resolver(), so nil means "use the dynamic default" rather than pinning to whatever the global happened to be -- which is what its doc comment already claimed. Coverage unchanged at 89.6%. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> * docs: explain why PreferGo is required in the resolver example The example sets PreferGo: true with no explanation, so it reads as boilerplate that a reader could reasonably delete -- which would silently break half the feature. Measured on darwin/arm64 with a custom Dial that records whether it was invoked: PreferGo=false LookupMX honors Dial=true host->IP honors Dial=false PreferGo=true LookupMX honors Dial=true host->IP honors Dial=true This matches the stdlib: Resolver.lookupMX calls goLookupMX unconditionally (net/lookup_unix.go), so MX records never take the cgo path, while lookupIP consults hostLookupOrder and can return hostLookupCgo, which ignores Dial. PreferGo makes mustUseGoResolver true, and that is the first branch in conf.lookupOrder, so it wins. Also documents that /etc/hosts is consulted before DNS even with PreferGo set, verified the same way: resolving "localhost" never reaches the custom Dial. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> * test: pin that the proxy path bypasses the custom resolver The README states that SMTP hostname resolution follows the proxy path rather than the configured resolver, but nothing enforced it: the existing TestDialSMTP_WithCustomResolver only covers proxyURI == "", and TestNewSMTPClientFailed_WithInvalidProxy passes net.DefaultResolver and only asserts the connection is refused. Adds the counterpart asserting the resolver is NOT consulted when a proxy is set, so the documented behaviour is executable. If the proxy path ever gains resolver support this test fails and prompts a docs update. Measured before writing it, via dialSMTP directly: no proxy resolverConsulted=true proxy (IP host) resolverConsulted=false proxy (hostname) resolverConsulted=false The test uses an IP for the proxy so that resolving the proxy's own name cannot influence the assertion. Verified it is a real guard: making the proxy branch call resolver.LookupHost makes it fail with "Should be false". Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> * docs: clarify that MX lookups are not routed through the proxy The preceding sentence, "when a proxy is configured, SMTP hostname resolution follows the proxy path rather than this resolver", can be read as "the resolver is irrelevant once a proxy is set". Only the MX-host-to-IP step goes through the proxy; the MX record lookup itself always uses this resolver. In smtp.go the lookup is at resolver.LookupMX before proxyURI is consulted at all, which happens later in dialSMTP. TestDialSMTP_ProxyBypassesCustomResolver pins the other half of this, that the proxy path does not consult the resolver. Worth stating explicitly because it means DNS queries still originate from the local machine when a proxy is configured. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: NeoCN <[email protected]> Co-authored-by: Jason Ma <[email protected]> Co-authored-by: Copilot Autofix powered by AI <[email protected]> Co-authored-by: Claude Opus 5 (1M context) <[email protected]>
26 lines
586 B
Go
26 lines
586 B
Go
package emailverifier
|
|
|
|
import (
|
|
"context"
|
|
"net"
|
|
)
|
|
|
|
// Mx is detail about the Mx host
|
|
type Mx struct {
|
|
HasMXRecord bool // whether has 1 or more MX record
|
|
Records []*net.MX // represent DNS MX records
|
|
}
|
|
|
|
// CheckMX will return the DNS MX records for the given domain name sorted by preference.
|
|
func (v *Verifier) CheckMX(domain string) (*Mx, error) {
|
|
domain = domainToASCII(domain)
|
|
mx, err := v.dnsResolver().LookupMX(context.Background(), domain)
|
|
if err != nil && len(mx) == 0 {
|
|
return nil, err
|
|
}
|
|
return &Mx{
|
|
HasMXRecord: len(mx) > 0,
|
|
Records: mx,
|
|
}, nil
|
|
}
|