113 lines
3.2 KiB
Go
113 lines
3.2 KiB
Go
package dns
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net"
|
|
"strings"
|
|
"time"
|
|
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
// Verifier handles DNS TXT record verification
|
|
type Verifier struct {
|
|
resolver *net.Resolver
|
|
logger *zap.Logger
|
|
}
|
|
|
|
// ProvideVerifier creates a new DNS Verifier
|
|
func ProvideVerifier(logger *zap.Logger) *Verifier {
|
|
return &Verifier{
|
|
resolver: &net.Resolver{
|
|
PreferGo: true, // Use Go's DNS resolver
|
|
},
|
|
logger: logger.Named("dns-verifier"),
|
|
}
|
|
}
|
|
|
|
// VerifyDomainOwnership checks if a domain has the correct TXT record
|
|
// Expected format: "maplepress-verify=TOKEN"
|
|
func (v *Verifier) VerifyDomainOwnership(ctx context.Context, domain string, expectedToken string) (bool, error) {
|
|
v.logger.Info("verifying domain ownership via DNS",
|
|
zap.String("domain", domain))
|
|
|
|
// Create context with timeout (10 seconds for DNS lookup)
|
|
lookupCtx, cancel := context.WithTimeout(ctx, 10*time.Second)
|
|
defer cancel()
|
|
|
|
// Look up TXT records for the domain
|
|
txtRecords, err := v.resolver.LookupTXT(lookupCtx, domain)
|
|
if err != nil {
|
|
// Check if it's a timeout
|
|
if lookupCtx.Err() == context.DeadlineExceeded {
|
|
v.logger.Warn("DNS lookup timed out",
|
|
zap.String("domain", domain))
|
|
return false, fmt.Errorf("DNS lookup timed out after 10 seconds")
|
|
}
|
|
|
|
// Check if domain doesn't exist
|
|
if dnsErr, ok := err.(*net.DNSError); ok {
|
|
if dnsErr.IsNotFound {
|
|
v.logger.Warn("domain not found",
|
|
zap.String("domain", domain))
|
|
return false, fmt.Errorf("domain not found: %s", domain)
|
|
}
|
|
}
|
|
|
|
v.logger.Error("failed to lookup TXT records",
|
|
zap.String("domain", domain),
|
|
zap.Error(err))
|
|
return false, fmt.Errorf("failed to lookup DNS TXT records: %w", err)
|
|
}
|
|
|
|
// Expected verification record format
|
|
expectedRecord := fmt.Sprintf("maplepress-verify=%s", expectedToken)
|
|
|
|
// Check each TXT record
|
|
for _, record := range txtRecords {
|
|
v.logger.Debug("checking TXT record",
|
|
zap.String("domain", domain),
|
|
zap.String("record", record))
|
|
|
|
// Normalize whitespace and compare
|
|
normalizedRecord := strings.TrimSpace(record)
|
|
if normalizedRecord == expectedRecord {
|
|
v.logger.Info("domain ownership verified",
|
|
zap.String("domain", domain))
|
|
return true, nil
|
|
}
|
|
}
|
|
|
|
v.logger.Warn("verification record not found",
|
|
zap.String("domain", domain),
|
|
zap.String("expected", expectedRecord),
|
|
zap.Int("records_checked", len(txtRecords)))
|
|
|
|
return false, nil
|
|
}
|
|
|
|
// GetVerificationRecord returns the TXT record format for a given token
|
|
func GetVerificationRecord(token string) string {
|
|
return fmt.Sprintf("maplepress-verify=%s", token)
|
|
}
|
|
|
|
// GetVerificationInstructions returns user-friendly instructions
|
|
func GetVerificationInstructions(domain string, token string) string {
|
|
record := GetVerificationRecord(token)
|
|
return fmt.Sprintf(`To verify ownership of %s, add this DNS TXT record:
|
|
|
|
Host/Name: %s
|
|
Type: TXT
|
|
Value: %s
|
|
|
|
Instructions:
|
|
1. Log in to your domain registrar (GoDaddy, Namecheap, Cloudflare, etc.)
|
|
2. Find DNS settings or DNS management
|
|
3. Add a new TXT record with the values above
|
|
4. Wait 5-10 minutes for DNS propagation
|
|
5. Click "Verify Domain" in MaplePress
|
|
|
|
Note: DNS changes can take up to 48 hours to propagate globally, but usually complete within 10 minutes.`,
|
|
domain, domain, record)
|
|
}
|