259 lines
7.9 KiB
Go
259 lines
7.9 KiB
Go
// Copyright 2015 ISRG. All rights reserved
|
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
package core
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
"os"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/letsencrypt/boulder/test"
|
|
|
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/miekg/dns"
|
|
)
|
|
|
|
const dnsLoopbackAddr = "127.0.0.1:4053"
|
|
|
|
func mockDNSQuery(w dns.ResponseWriter, r *dns.Msg) {
|
|
defer w.Close()
|
|
m := new(dns.Msg)
|
|
m.SetReply(r)
|
|
m.Compress = false
|
|
|
|
appendAnswer := func(rr dns.RR) {
|
|
m.Answer = append(m.Answer, rr)
|
|
}
|
|
for _, q := range r.Question {
|
|
q.Name = strings.ToLower(q.Name)
|
|
if q.Name == "servfail.com." {
|
|
m.Rcode = dns.RcodeServerFailure
|
|
break
|
|
}
|
|
switch q.Qtype {
|
|
case dns.TypeSOA:
|
|
record := new(dns.SOA)
|
|
record.Hdr = dns.RR_Header{Name: "letsencrypt.org.", Rrtype: dns.TypeSOA, Class: dns.ClassINET, Ttl: 0}
|
|
record.Ns = "ns.letsencrypt.org."
|
|
record.Mbox = "master.letsencrypt.org."
|
|
record.Serial = 1
|
|
record.Refresh = 1
|
|
record.Retry = 1
|
|
record.Expire = 1
|
|
record.Minttl = 1
|
|
appendAnswer(record)
|
|
case dns.TypeAAAA:
|
|
if q.Name == "v6.letsencrypt.org." {
|
|
record := new(dns.AAAA)
|
|
record.Hdr = dns.RR_Header{Name: "v6.letsencrypt.org.", Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: 0}
|
|
record.AAAA = net.ParseIP("::1")
|
|
appendAnswer(record)
|
|
}
|
|
case dns.TypeA:
|
|
if q.Name == "cps.letsencrypt.org." {
|
|
record := new(dns.A)
|
|
record.Hdr = dns.RR_Header{Name: "cps.letsencrypt.org.", Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 0}
|
|
record.A = net.ParseIP("127.0.0.1")
|
|
appendAnswer(record)
|
|
}
|
|
case dns.TypeCNAME:
|
|
if q.Name == "cname.letsencrypt.org." {
|
|
record := new(dns.CNAME)
|
|
record.Hdr = dns.RR_Header{Name: "cname.letsencrypt.org.", Rrtype: dns.TypeCNAME, Class: dns.ClassINET, Ttl: 30}
|
|
record.Target = "cps.letsencrypt.org."
|
|
appendAnswer(record)
|
|
}
|
|
if q.Name == "cname.example.com." {
|
|
record := new(dns.CNAME)
|
|
record.Hdr = dns.RR_Header{Name: "cname.example.com.", Rrtype: dns.TypeCNAME, Class: dns.ClassINET, Ttl: 30}
|
|
record.Target = "CAA.example.com."
|
|
appendAnswer(record)
|
|
}
|
|
case dns.TypeDNAME:
|
|
if q.Name == "dname.letsencrypt.org." {
|
|
record := new(dns.DNAME)
|
|
record.Hdr = dns.RR_Header{Name: "dname.letsencrypt.org.", Rrtype: dns.TypeDNAME, Class: dns.ClassINET, Ttl: 30}
|
|
record.Target = "cps.letsencrypt.org."
|
|
appendAnswer(record)
|
|
}
|
|
case dns.TypeCAA:
|
|
if q.Name == "bracewel.net." || q.Name == "caa.example.com." {
|
|
record := new(dns.CAA)
|
|
record.Hdr = dns.RR_Header{Name: q.Name, Rrtype: dns.TypeCAA, Class: dns.ClassINET, Ttl: 0}
|
|
record.Tag = "issue"
|
|
record.Value = "letsencrypt.org"
|
|
record.Flag = 1
|
|
appendAnswer(record)
|
|
}
|
|
if q.Name == "cname.example.com." {
|
|
record := new(dns.CAA)
|
|
record.Hdr = dns.RR_Header{Name: "caa.example.com.", Rrtype: dns.TypeCAA, Class: dns.ClassINET, Ttl: 0}
|
|
record.Tag = "issue"
|
|
record.Value = "letsencrypt.org"
|
|
record.Flag = 1
|
|
appendAnswer(record)
|
|
}
|
|
case dns.TypeTXT:
|
|
if q.Name == "split-txt.letsencrypt.org." {
|
|
record := new(dns.TXT)
|
|
record.Hdr = dns.RR_Header{Name: "split-txt.letsencrypt.org.", Rrtype: dns.TypeTXT, Class: dns.ClassINET, Ttl: 0}
|
|
record.Txt = []string{"a", "b", "c"}
|
|
appendAnswer(record)
|
|
}
|
|
}
|
|
}
|
|
|
|
w.WriteMsg(m)
|
|
return
|
|
}
|
|
|
|
func serveLoopResolver(stopChan chan bool) chan bool {
|
|
dns.HandleFunc(".", mockDNSQuery)
|
|
server := &dns.Server{Addr: dnsLoopbackAddr, Net: "udp", ReadTimeout: time.Millisecond, WriteTimeout: time.Millisecond}
|
|
waitChan := make(chan bool, 1)
|
|
go func() {
|
|
waitChan <- true
|
|
err := server.ListenAndServe()
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
return
|
|
}
|
|
}()
|
|
go func() {
|
|
<-stopChan
|
|
err := server.Shutdown()
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
}
|
|
}()
|
|
return waitChan
|
|
}
|
|
|
|
func TestMain(m *testing.M) {
|
|
stop := make(chan bool, 1)
|
|
wait := serveLoopResolver(stop)
|
|
<-wait
|
|
ret := m.Run()
|
|
stop <- true
|
|
os.Exit(ret)
|
|
}
|
|
|
|
func TestDNSNoServers(t *testing.T) {
|
|
obj := NewTestDNSResolverImpl(time.Hour, []string{})
|
|
|
|
_, _, err := obj.ExchangeOne("letsencrypt.org", dns.TypeA)
|
|
|
|
test.AssertError(t, err, "No servers")
|
|
}
|
|
|
|
func TestDNSOneServer(t *testing.T) {
|
|
obj := NewTestDNSResolverImpl(time.Second*10, []string{dnsLoopbackAddr})
|
|
|
|
_, _, err := obj.ExchangeOne("letsencrypt.org", dns.TypeSOA)
|
|
|
|
test.AssertNotError(t, err, "No message")
|
|
}
|
|
|
|
func TestDNSDuplicateServers(t *testing.T) {
|
|
obj := NewTestDNSResolverImpl(time.Second*10, []string{dnsLoopbackAddr, dnsLoopbackAddr})
|
|
|
|
_, _, err := obj.ExchangeOne("letsencrypt.org", dns.TypeSOA)
|
|
|
|
test.AssertNotError(t, err, "No message")
|
|
}
|
|
|
|
func TestDNSLookupsNoServer(t *testing.T) {
|
|
obj := NewTestDNSResolverImpl(time.Second*10, []string{})
|
|
|
|
_, _, err := obj.LookupTXT("letsencrypt.org")
|
|
test.AssertError(t, err, "No servers")
|
|
|
|
_, _, err = obj.LookupHost("letsencrypt.org")
|
|
test.AssertError(t, err, "No servers")
|
|
|
|
_, _, err = obj.LookupCAA("letsencrypt.org")
|
|
test.AssertError(t, err, "No servers")
|
|
}
|
|
|
|
func TestDNSServFail(t *testing.T) {
|
|
obj := NewTestDNSResolverImpl(time.Second*10, []string{dnsLoopbackAddr})
|
|
bad := "servfail.com"
|
|
|
|
_, _, err := obj.LookupTXT(bad)
|
|
test.AssertError(t, err, "LookupTXT didn't return an error")
|
|
|
|
_, _, err = obj.LookupHost(bad)
|
|
test.AssertError(t, err, "LookupHost didn't return an error")
|
|
|
|
// CAA lookup ignores validation failures from the resolver for now
|
|
// and returns an empty list of CAA records.
|
|
emptyCaa, _, err := obj.LookupCAA(bad)
|
|
test.Assert(t, len(emptyCaa) == 0, "Query returned non-empty list of CAA records")
|
|
test.AssertNotError(t, err, "LookupCAA returned an error")
|
|
}
|
|
|
|
func TestDNSLookupTXT(t *testing.T) {
|
|
obj := NewTestDNSResolverImpl(time.Second*10, []string{dnsLoopbackAddr})
|
|
|
|
a, rtt, err := obj.LookupTXT("letsencrypt.org")
|
|
t.Logf("A: %v RTT %s", a, rtt)
|
|
test.AssertNotError(t, err, "No message")
|
|
|
|
a, rtt, err = obj.LookupTXT("split-txt.letsencrypt.org")
|
|
t.Logf("A: %v RTT %s", a, rtt)
|
|
test.AssertNotError(t, err, "No message")
|
|
test.AssertEquals(t, len(a), 1)
|
|
test.AssertEquals(t, a[0], "abc")
|
|
}
|
|
|
|
func TestDNSLookupHost(t *testing.T) {
|
|
obj := NewTestDNSResolverImpl(time.Second*10, []string{dnsLoopbackAddr})
|
|
|
|
ip, _, err := obj.LookupHost("servfail.com")
|
|
t.Logf("servfail.com - IP: %s, Err: %s", ip, err)
|
|
test.AssertError(t, err, "Server failure")
|
|
test.Assert(t, len(ip) == 0, "Should not have IPs")
|
|
|
|
ip, _, err = obj.LookupHost("nonexistent.letsencrypt.org")
|
|
t.Logf("nonexistent.letsencrypt.org - IP: %s, Err: %s", ip, err)
|
|
test.AssertNotError(t, err, "Not an error to not exist")
|
|
test.Assert(t, len(ip) == 0, "Should not have IPs")
|
|
|
|
// Single IPv4 address
|
|
ip, _, err = obj.LookupHost("cps.letsencrypt.org")
|
|
t.Logf("cps.letsencrypt.org - IP: %s, Err: %s", ip, err)
|
|
test.AssertNotError(t, err, "Not an error to exist")
|
|
test.Assert(t, len(ip) == 1, "Should have IP")
|
|
ip, _, err = obj.LookupHost("cps.letsencrypt.org")
|
|
t.Logf("cps.letsencrypt.org - IP: %s, Err: %s", ip, err)
|
|
test.AssertNotError(t, err, "Not an error to exist")
|
|
test.Assert(t, len(ip) == 1, "Should have IP")
|
|
|
|
// No IPv6
|
|
ip, _, err = obj.LookupHost("v6.letsencrypt.org")
|
|
t.Logf("v6.letsencrypt.org - IP: %s, Err: %s", ip, err)
|
|
test.AssertNotError(t, err, "Not an error to exist")
|
|
test.Assert(t, len(ip) == 0, "Should not have IPs")
|
|
}
|
|
|
|
func TestDNSLookupCAA(t *testing.T) {
|
|
obj := NewTestDNSResolverImpl(time.Second*10, []string{dnsLoopbackAddr})
|
|
|
|
caas, _, err := obj.LookupCAA("bracewel.net")
|
|
test.AssertNotError(t, err, "CAA lookup failed")
|
|
test.Assert(t, len(caas) > 0, "Should have CAA records")
|
|
|
|
caas, _, err = obj.LookupCAA("nonexistent.letsencrypt.org")
|
|
test.AssertNotError(t, err, "CAA lookup failed")
|
|
test.Assert(t, len(caas) == 0, "Shouldn't have CAA records")
|
|
|
|
caas, _, err = obj.LookupCAA("cname.example.com")
|
|
test.AssertNotError(t, err, "CAA lookup failed")
|
|
test.Assert(t, len(caas) > 0, "Should follow CNAME to find CAA")
|
|
}
|