Merge pull request #506 from letsencrypt/native-mx-lookup
Replace net.LookupMX use with core.LookupMX
This commit is contained in:
commit
61394e4f2d
|
|
@ -6,8 +6,11 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cactus/go-statsd-client/statsd"
|
||||
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/streadway/amqp"
|
||||
"github.com/letsencrypt/boulder/core"
|
||||
|
||||
"github.com/letsencrypt/boulder/cmd"
|
||||
blog "github.com/letsencrypt/boulder/log"
|
||||
|
|
@ -36,6 +39,9 @@ func main() {
|
|||
rai := ra.NewRegistrationAuthorityImpl()
|
||||
rai.AuthzBase = c.Common.BaseURL + wfe.AuthzPath
|
||||
rai.MaxKeySize = c.Common.MaxKeySize
|
||||
raDNSTimeout, err := time.ParseDuration(c.RA.DNSTimeout)
|
||||
cmd.FailOnError(err, "Couldn't parse RA DNS timeout")
|
||||
rai.DNSResolver = core.NewDNSResolverImpl(raDNSTimeout, []string{c.RA.DNSResolver})
|
||||
|
||||
go cmd.ProfileCmd("RA", stats)
|
||||
|
||||
|
|
|
|||
|
|
@ -92,11 +92,14 @@ func main() {
|
|||
cmd.FailOnError(err, "Couldn't parse issuer caching duration")
|
||||
|
||||
ra := ra.NewRegistrationAuthorityImpl()
|
||||
raDNSTimeout, err := time.ParseDuration(c.RA.DNSTimeout)
|
||||
cmd.FailOnError(err, "Couldn't parse RA DNS timeout")
|
||||
ra.DNSResolver = core.NewDNSResolverImpl(raDNSTimeout, []string{c.RA.DNSResolver})
|
||||
|
||||
va := va.NewValidationAuthorityImpl(c.CA.TestMode)
|
||||
dnsTimeout, err := time.ParseDuration(c.VA.DNSTimeout)
|
||||
cmd.FailOnError(err, "Couldn't parse DNS timeout")
|
||||
va.DNSResolver = core.NewDNSResolverImpl(dnsTimeout, []string{c.VA.DNSResolver})
|
||||
vaDNSTimeout, err := time.ParseDuration(c.VA.DNSTimeout)
|
||||
cmd.FailOnError(err, "Couldn't parse VA DNS timeout")
|
||||
va.DNSResolver = core.NewDNSResolverImpl(vaDNSTimeout, []string{c.VA.DNSResolver})
|
||||
va.UserAgent = c.VA.UserAgent
|
||||
|
||||
cadb, err := ca.NewCertificateAuthorityDatabaseImpl(c.CA.DBDriver, c.CA.DBConnect)
|
||||
|
|
|
|||
|
|
@ -90,6 +90,9 @@ type Config struct {
|
|||
}
|
||||
|
||||
RA struct {
|
||||
DNSResolver string
|
||||
DNSTimeout string
|
||||
|
||||
// DebugAddr is the address to run the /debug handlers on.
|
||||
DebugAddr string
|
||||
}
|
||||
|
|
|
|||
22
core/dns.go
22
core/dns.go
|
|
@ -168,3 +168,25 @@ func (dnsResolver *DNSResolverImpl) LookupCAA(hostname string) ([]*dns.CAA, time
|
|||
}
|
||||
return CAAs, rtt, nil
|
||||
}
|
||||
|
||||
// LookupMX sends a DNS query to find a MX record associated hostname and returns the
|
||||
// record target.
|
||||
func (dnsResolver *DNSResolverImpl) LookupMX(hostname string) ([]string, time.Duration, error) {
|
||||
r, rtt, err := dnsResolver.ExchangeOne(hostname, dns.TypeMX)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
if r.Rcode != dns.RcodeSuccess {
|
||||
err = fmt.Errorf("DNS failure: %d-%s for MX query", r.Rcode, dns.RcodeToString[r.Rcode])
|
||||
return nil, rtt, err
|
||||
}
|
||||
|
||||
var results []string
|
||||
for _, answer := range r.Answer {
|
||||
if mx, ok := answer.(*dns.MX); ok {
|
||||
results = append(results, mx.Mx)
|
||||
}
|
||||
}
|
||||
|
||||
return results, rtt, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -145,4 +145,5 @@ type DNSResolver interface {
|
|||
LookupHost(string) ([]net.IP, time.Duration, time.Duration, error)
|
||||
LookupCNAME(string) (string, time.Duration, error)
|
||||
LookupCAA(string) ([]*dns.CAA, time.Duration, error)
|
||||
LookupMX(string) ([]string, time.Duration, error)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -97,3 +97,14 @@ func (mock *MockDNS) LookupCAA(domain string) ([]*dns.CAA, time.Duration, error)
|
|||
}
|
||||
return results, 0, nil
|
||||
}
|
||||
|
||||
// LookupMX is a mock
|
||||
func (mock *MockDNS) LookupMX(domain string) ([]string, time.Duration, error) {
|
||||
switch domain {
|
||||
case "letsencrypt.org":
|
||||
fallthrough
|
||||
case "email.com":
|
||||
return []string{"mail.email.com"}, 0, nil
|
||||
}
|
||||
return nil, 0, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import (
|
|||
"crypto/x509"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/mail"
|
||||
"net/url"
|
||||
"regexp"
|
||||
|
|
@ -27,11 +26,12 @@ import (
|
|||
// NOTE: All of the fields in RegistrationAuthorityImpl need to be
|
||||
// populated, or there is a risk of panic.
|
||||
type RegistrationAuthorityImpl struct {
|
||||
CA core.CertificateAuthority
|
||||
VA core.ValidationAuthority
|
||||
SA core.StorageAuthority
|
||||
PA core.PolicyAuthority
|
||||
log *blog.AuditLogger
|
||||
CA core.CertificateAuthority
|
||||
VA core.ValidationAuthority
|
||||
SA core.StorageAuthority
|
||||
PA core.PolicyAuthority
|
||||
DNSResolver core.DNSResolver
|
||||
log *blog.AuditLogger
|
||||
|
||||
AuthzBase string
|
||||
MaxKeySize int
|
||||
|
|
@ -53,7 +53,7 @@ func lastPathSegment(url core.AcmeURL) string {
|
|||
return allButLastPathSegment.ReplaceAllString(url.Path, "")
|
||||
}
|
||||
|
||||
func validateEmail(address string) (err error) {
|
||||
func validateEmail(address string, resolver core.DNSResolver) (err error) {
|
||||
_, err = mail.ParseAddress(address)
|
||||
if err != nil {
|
||||
err = core.MalformedRequestError(fmt.Sprintf("%s is not a valid e-mail address", address))
|
||||
|
|
@ -61,8 +61,8 @@ func validateEmail(address string) (err error) {
|
|||
}
|
||||
splitEmail := strings.SplitN(address, "@", -1)
|
||||
domain := strings.ToLower(splitEmail[len(splitEmail)-1])
|
||||
var mx []*net.MX
|
||||
mx, err = net.LookupMX(domain)
|
||||
var mx []string
|
||||
mx, _, err = resolver.LookupMX(domain)
|
||||
if err != nil || len(mx) == 0 {
|
||||
err = core.MalformedRequestError(fmt.Sprintf("No MX record for domain %s", domain))
|
||||
return
|
||||
|
|
@ -70,13 +70,13 @@ func validateEmail(address string) (err error) {
|
|||
return
|
||||
}
|
||||
|
||||
func validateContacts(contacts []core.AcmeURL) (err error) {
|
||||
func validateContacts(contacts []core.AcmeURL, resolver core.DNSResolver) (err error) {
|
||||
for _, contact := range contacts {
|
||||
switch contact.Scheme {
|
||||
case "tel":
|
||||
continue
|
||||
case "mailto":
|
||||
err = validateEmail(contact.Opaque)
|
||||
err = validateEmail(contact.Opaque, resolver)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
@ -116,7 +116,7 @@ func (ra *RegistrationAuthorityImpl) NewRegistration(init core.Registration) (re
|
|||
}
|
||||
reg.MergeUpdate(init)
|
||||
|
||||
err = validateContacts(reg.Contact)
|
||||
err = validateContacts(reg.Contact, ra.DNSResolver)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
@ -367,7 +367,7 @@ func (ra *RegistrationAuthorityImpl) NewCertificate(req core.CertificateRequest,
|
|||
func (ra *RegistrationAuthorityImpl) UpdateRegistration(base core.Registration, update core.Registration) (reg core.Registration, err error) {
|
||||
base.MergeUpdate(update)
|
||||
|
||||
err = validateContacts(base.Contact)
|
||||
err = validateContacts(base.Contact, ra.DNSResolver)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -200,6 +200,7 @@ func initAuthorities(t *testing.T) (core.CertificateAuthority, *DummyValidationA
|
|||
ra.PA = pa
|
||||
ra.AuthzBase = "http://acme.invalid/authz/"
|
||||
ra.MaxKeySize = 4096
|
||||
ra.DNSResolver = &mocks.MockDNS{}
|
||||
|
||||
AuthzInitial.RegistrationID = Registration.ID
|
||||
|
||||
|
|
@ -230,38 +231,38 @@ func TestValidateContacts(t *testing.T) {
|
|||
invalidEmail, _ := url.Parse("mailto:admin@example.com")
|
||||
malformedEmail, _ := url.Parse("mailto:admin.com")
|
||||
|
||||
err := validateContacts([]core.AcmeURL{})
|
||||
err := validateContacts([]core.AcmeURL{}, &mocks.MockDNS{})
|
||||
test.AssertNotError(t, err, "No Contacts")
|
||||
|
||||
err = validateContacts([]core.AcmeURL{core.AcmeURL(*tel)})
|
||||
err = validateContacts([]core.AcmeURL{core.AcmeURL(*tel)}, &mocks.MockDNS{})
|
||||
test.AssertNotError(t, err, "Simple Telephone")
|
||||
|
||||
err = validateContacts([]core.AcmeURL{core.AcmeURL(*validEmail)})
|
||||
err = validateContacts([]core.AcmeURL{core.AcmeURL(*validEmail)}, &mocks.MockDNS{})
|
||||
test.AssertNotError(t, err, "Valid Email")
|
||||
|
||||
err = validateContacts([]core.AcmeURL{core.AcmeURL(*invalidEmail)})
|
||||
err = validateContacts([]core.AcmeURL{core.AcmeURL(*invalidEmail)}, &mocks.MockDNS{})
|
||||
test.AssertError(t, err, "Invalid Email")
|
||||
|
||||
err = validateContacts([]core.AcmeURL{core.AcmeURL(*malformedEmail)})
|
||||
err = validateContacts([]core.AcmeURL{core.AcmeURL(*malformedEmail)}, &mocks.MockDNS{})
|
||||
test.AssertError(t, err, "Malformed Email")
|
||||
|
||||
err = validateContacts([]core.AcmeURL{core.AcmeURL(*ansible)})
|
||||
err = validateContacts([]core.AcmeURL{core.AcmeURL(*ansible)}, &mocks.MockDNS{})
|
||||
test.AssertError(t, err, "Unknown scehme")
|
||||
}
|
||||
|
||||
func TestValidateEmail(t *testing.T) {
|
||||
err := validateEmail("an email`")
|
||||
err := validateEmail("an email`", &mocks.MockDNS{})
|
||||
test.AssertError(t, err, "Malformed")
|
||||
|
||||
err = validateEmail("a@not.a.domain")
|
||||
err = validateEmail("a@not.a.domain", &mocks.MockDNS{})
|
||||
test.AssertError(t, err, "Cannot resolve")
|
||||
t.Logf("No Resolve: %s", err)
|
||||
|
||||
err = validateEmail("a@example.com")
|
||||
err = validateEmail("a@example.com", &mocks.MockDNS{})
|
||||
test.AssertError(t, err, "No MX Record")
|
||||
t.Logf("No MX: %s", err)
|
||||
|
||||
err = validateEmail("a@email.com")
|
||||
err = validateEmail("a@email.com", &mocks.MockDNS{})
|
||||
test.AssertNotError(t, err, "Valid")
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -114,6 +114,8 @@
|
|||
},
|
||||
|
||||
"ra": {
|
||||
"dnsResolver": "8.8.8.8:53",
|
||||
"dnsTimeout": "10s",
|
||||
"debugAddr": "localhost:8002"
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -101,6 +101,8 @@
|
|||
},
|
||||
|
||||
"ra": {
|
||||
"dnsResolver": "8.8.8.8:53",
|
||||
"dnsTimeout": "10s",
|
||||
"debugAddr": "localhost:8002"
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -105,6 +105,8 @@
|
|||
},
|
||||
|
||||
"ra": {
|
||||
"dnsResolver": "127.0.0.1:8053",
|
||||
"dnsTimeout": "10s",
|
||||
"debugAddr": "localhost:8002"
|
||||
},
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue