@@ -2,10 +2,14 @@ package bdns
22
33import (
44 "context"
5+ "crypto/tls"
6+ "crypto/x509"
57 "errors"
68 "fmt"
9+ "io"
710 "log"
811 "net"
12+ "net/http"
913 "net/netip"
1014 "net/url"
1115 "os"
@@ -20,15 +24,37 @@ import (
2024 "github.com/miekg/dns"
2125 "github.com/prometheus/client_golang/prometheus"
2226
23- "github.com/letsencrypt/boulder/features"
2427 blog "github.com/letsencrypt/boulder/log"
2528 "github.com/letsencrypt/boulder/metrics"
2629 "github.com/letsencrypt/boulder/test"
2730)
2831
2932const dnsLoopbackAddr = "127.0.0.1:4053"
3033
31- func mockDNSQuery (w dns.ResponseWriter , r * dns.Msg ) {
34+ func mockDNSQuery (w http.ResponseWriter , httpReq * http.Request ) {
35+ if httpReq .Header .Get ("Content-Type" ) != "application/dns-message" {
36+ w .WriteHeader (http .StatusBadRequest )
37+ fmt .Fprintf (w , "client didn't send Content-Type: application/dns-message" )
38+ }
39+ if httpReq .Header .Get ("Accept" ) != "application/dns-message" {
40+ w .WriteHeader (http .StatusBadRequest )
41+ fmt .Fprintf (w , "client didn't accept Content-Type: application/dns-message" )
42+ }
43+
44+ requestBody , err := io .ReadAll (httpReq .Body )
45+ if err != nil {
46+ w .WriteHeader (http .StatusBadRequest )
47+ fmt .Fprintf (w , "reading body: %s" , err )
48+ }
49+ httpReq .Body .Close ()
50+
51+ r := new (dns.Msg )
52+ err = r .Unpack (requestBody )
53+ if err != nil {
54+ w .WriteHeader (http .StatusBadRequest )
55+ fmt .Fprintf (w , "unpacking request: %s" , err )
56+ }
57+
3258 m := new (dns.Msg )
3359 m .SetReply (r )
3460 m .Compress = false
@@ -174,45 +200,37 @@ func mockDNSQuery(w dns.ResponseWriter, r *dns.Msg) {
174200 }
175201 }
176202
177- err := w .WriteMsg (m )
203+ body , err := m .Pack ()
204+ if err != nil {
205+ fmt .Fprintf (os .Stderr , "packing reply: %s\n " , err )
206+ }
207+ w .Header ().Set ("Content-Type" , "application/dns-message" )
208+ _ , err = w .Write (body )
178209 if err != nil {
179210 panic (err ) // running tests, so panic is OK
180211 }
181212}
182213
183214func serveLoopResolver (stopChan chan bool ) {
184- dns .HandleFunc ("." , mockDNSQuery )
185- tcpServer := & dns.Server {
186- Addr : dnsLoopbackAddr ,
187- Net : "tcp" ,
188- ReadTimeout : time .Second ,
189- WriteTimeout : time .Second ,
190- }
191- udpServer := & dns.Server {
215+ m := http .NewServeMux ()
216+ m .HandleFunc ("/dns-query" , mockDNSQuery )
217+ httpServer := & http.Server {
192218 Addr : dnsLoopbackAddr ,
193- Net : "udp" ,
219+ Handler : m ,
194220 ReadTimeout : time .Second ,
195221 WriteTimeout : time .Second ,
196222 }
197223 go func () {
198- err := tcpServer .ListenAndServe ()
199- if err != nil {
200- fmt .Println (err )
201- }
202- }()
203- go func () {
204- err := udpServer .ListenAndServe ()
224+ cert := "../test/certs/ipki/localhost/cert.pem"
225+ key := "../test/certs/ipki/localhost/key.pem"
226+ err := httpServer .ListenAndServeTLS (cert , key )
205227 if err != nil {
206228 fmt .Println (err )
207229 }
208230 }()
209231 go func () {
210232 <- stopChan
211- err := tcpServer .Shutdown ()
212- if err != nil {
213- log .Fatal (err )
214- }
215- err = udpServer .Shutdown ()
233+ err := httpServer .Shutdown (context .Background ())
216234 if err != nil {
217235 log .Fatal (err )
218236 }
@@ -240,7 +258,21 @@ func pollServer() {
240258 }
241259}
242260
261+ // tlsConfig is used for the TLS config of client instances that talk to the
262+ // DoH server set up in TestMain.
263+ var tlsConfig * tls.Config
264+
243265func TestMain (m * testing.M ) {
266+ root , err := os .ReadFile ("../test/certs/ipki/minica.pem" )
267+ if err != nil {
268+ log .Fatal (err )
269+ }
270+ pool := x509 .NewCertPool ()
271+ pool .AppendCertsFromPEM (root )
272+ tlsConfig = & tls.Config {
273+ RootCAs : pool ,
274+ }
275+
244276 stop := make (chan bool , 1 )
245277 serveLoopResolver (stop )
246278 pollServer ()
@@ -253,7 +285,7 @@ func TestDNSNoServers(t *testing.T) {
253285 staticProvider , err := NewStaticProvider ([]string {})
254286 test .AssertNotError (t , err , "Got error creating StaticProvider" )
255287
256- obj := New (time .Hour , staticProvider , metrics .NoopRegisterer , clock .NewFake (), 1 , "" , blog .UseMock (), nil )
288+ obj := New (time .Hour , staticProvider , metrics .NoopRegisterer , clock .NewFake (), 1 , "" , blog .UseMock (), tlsConfig )
257289
258290 _ , resolvers , err := obj .LookupHost (context .Background (), "letsencrypt.org" )
259291 test .AssertEquals (t , len (resolvers ), 0 )
@@ -270,7 +302,7 @@ func TestDNSOneServer(t *testing.T) {
270302 staticProvider , err := NewStaticProvider ([]string {dnsLoopbackAddr })
271303 test .AssertNotError (t , err , "Got error creating StaticProvider" )
272304
273- obj := New (time .Second * 10 , staticProvider , metrics .NoopRegisterer , clock .NewFake (), 1 , "" , blog .UseMock (), nil )
305+ obj := New (time .Second * 10 , staticProvider , metrics .NoopRegisterer , clock .NewFake (), 1 , "" , blog .UseMock (), tlsConfig )
274306
275307 _ , resolvers , err := obj .LookupHost (context .Background (), "cps.letsencrypt.org" )
276308 test .AssertEquals (t , len (resolvers ), 2 )
@@ -283,7 +315,7 @@ func TestDNSDuplicateServers(t *testing.T) {
283315 staticProvider , err := NewStaticProvider ([]string {dnsLoopbackAddr , dnsLoopbackAddr })
284316 test .AssertNotError (t , err , "Got error creating StaticProvider" )
285317
286- obj := New (time .Second * 10 , staticProvider , metrics .NoopRegisterer , clock .NewFake (), 1 , "" , blog .UseMock (), nil )
318+ obj := New (time .Second * 10 , staticProvider , metrics .NoopRegisterer , clock .NewFake (), 1 , "" , blog .UseMock (), tlsConfig )
287319
288320 _ , resolvers , err := obj .LookupHost (context .Background (), "cps.letsencrypt.org" )
289321 test .AssertEquals (t , len (resolvers ), 2 )
@@ -296,7 +328,7 @@ func TestDNSServFail(t *testing.T) {
296328 staticProvider , err := NewStaticProvider ([]string {dnsLoopbackAddr })
297329 test .AssertNotError (t , err , "Got error creating StaticProvider" )
298330
299- obj := New (time .Second * 10 , staticProvider , metrics .NoopRegisterer , clock .NewFake (), 1 , "" , blog .UseMock (), nil )
331+ obj := New (time .Second * 10 , staticProvider , metrics .NoopRegisterer , clock .NewFake (), 1 , "" , blog .UseMock (), tlsConfig )
300332 bad := "servfail.com"
301333
302334 _ , _ , err = obj .LookupTXT (context .Background (), bad )
@@ -314,7 +346,7 @@ func TestDNSLookupTXT(t *testing.T) {
314346 staticProvider , err := NewStaticProvider ([]string {dnsLoopbackAddr })
315347 test .AssertNotError (t , err , "Got error creating StaticProvider" )
316348
317- obj := New (time .Second * 10 , staticProvider , metrics .NoopRegisterer , clock .NewFake (), 1 , "" , blog .UseMock (), nil )
349+ obj := New (time .Second * 10 , staticProvider , metrics .NoopRegisterer , clock .NewFake (), 1 , "" , blog .UseMock (), tlsConfig )
318350
319351 a , _ , err := obj .LookupTXT (context .Background (), "letsencrypt.org" )
320352 t .Logf ("A: %v" , a )
@@ -332,7 +364,7 @@ func TestDNSLookupHost(t *testing.T) {
332364 staticProvider , err := NewStaticProvider ([]string {dnsLoopbackAddr })
333365 test .AssertNotError (t , err , "Got error creating StaticProvider" )
334366
335- obj := New (time .Second * 10 , staticProvider , metrics .NoopRegisterer , clock .NewFake (), 1 , "" , blog .UseMock (), nil )
367+ obj := New (time .Second * 10 , staticProvider , metrics .NoopRegisterer , clock .NewFake (), 1 , "" , blog .UseMock (), tlsConfig )
336368
337369 ip , resolvers , err := obj .LookupHost (context .Background (), "servfail.com" )
338370 t .Logf ("servfail.com - IP: %s, Err: %s" , ip , err )
@@ -418,7 +450,7 @@ func TestDNSNXDOMAIN(t *testing.T) {
418450 staticProvider , err := NewStaticProvider ([]string {dnsLoopbackAddr })
419451 test .AssertNotError (t , err , "Got error creating StaticProvider" )
420452
421- obj := New (time .Second * 10 , staticProvider , metrics .NoopRegisterer , clock .NewFake (), 1 , "" , blog .UseMock (), nil )
453+ obj := New (time .Second * 10 , staticProvider , metrics .NoopRegisterer , clock .NewFake (), 1 , "" , blog .UseMock (), tlsConfig )
422454
423455 hostname := "nxdomain.letsencrypt.org"
424456 _ , _ , err = obj .LookupHost (context .Background (), hostname )
@@ -434,7 +466,7 @@ func TestDNSLookupCAA(t *testing.T) {
434466 staticProvider , err := NewStaticProvider ([]string {dnsLoopbackAddr })
435467 test .AssertNotError (t , err , "Got error creating StaticProvider" )
436468
437- obj := New (time .Second * 10 , staticProvider , metrics .NoopRegisterer , clock .NewFake (), 1 , "" , blog .UseMock (), nil )
469+ obj := New (time .Second * 10 , staticProvider , metrics .NoopRegisterer , clock .NewFake (), 1 , "" , blog .UseMock (), tlsConfig )
438470 removeIDExp := regexp .MustCompile (" id: [[:digit:]]+" )
439471
440472 caas , resp , resolvers , err := obj .LookupCAA (context .Background (), "bracewel.net" )
@@ -513,10 +545,9 @@ func (te *testExchanger) Exchange(m *dns.Msg, a string) (*dns.Msg, time.Duration
513545}
514546
515547func TestRetry (t * testing.T ) {
516- isTempErr := & net. OpError {Op : "read" , Err : tempError (true )}
517- nonTempErr := & net. OpError {Op : "read" , Err : tempError (false )}
548+ isTempErr := & url. Error {Op : "read" , Err : tempError (true )}
549+ nonTempErr := & url. Error {Op : "read" , Err : tempError (false )}
518550 servFailError := errors .New ("DNS problem: server failure at resolver looking up TXT for example.com" )
519- netError := errors .New ("DNS problem: networking error looking up TXT for example.com" )
520551 type testCase struct {
521552 name string
522553 maxTries int
@@ -567,7 +598,7 @@ func TestRetry(t *testing.T) {
567598 isTempErr ,
568599 },
569600 },
570- expected : netError ,
601+ expected : servFailError ,
571602 expectedCount : 3 ,
572603 metricsAllRetries : 1 ,
573604 },
@@ -620,7 +651,7 @@ func TestRetry(t *testing.T) {
620651 isTempErr ,
621652 },
622653 },
623- expected : netError ,
654+ expected : servFailError ,
624655 expectedCount : 3 ,
625656 metricsAllRetries : 1 ,
626657 },
@@ -634,7 +665,7 @@ func TestRetry(t *testing.T) {
634665 nonTempErr ,
635666 },
636667 },
637- expected : netError ,
668+ expected : servFailError ,
638669 expectedCount : 2 ,
639670 },
640671 }
@@ -644,7 +675,7 @@ func TestRetry(t *testing.T) {
644675 staticProvider , err := NewStaticProvider ([]string {dnsLoopbackAddr })
645676 test .AssertNotError (t , err , "Got error creating StaticProvider" )
646677
647- testClient := New (time .Second * 10 , staticProvider , metrics .NoopRegisterer , clock .NewFake (), tc .maxTries , "" , blog .UseMock (), nil )
678+ testClient := New (time .Second * 10 , staticProvider , metrics .NoopRegisterer , clock .NewFake (), tc .maxTries , "" , blog .UseMock (), tlsConfig )
648679 dr := testClient .(* impl )
649680 dr .dnsClient = tc .te
650681 _ , _ , err = dr .LookupTXT (context .Background (), "example.com" )
@@ -675,7 +706,7 @@ func TestRetry(t *testing.T) {
675706 staticProvider , err := NewStaticProvider ([]string {dnsLoopbackAddr })
676707 test .AssertNotError (t , err , "Got error creating StaticProvider" )
677708
678- testClient := New (time .Second * 10 , staticProvider , metrics .NoopRegisterer , clock .NewFake (), 3 , "" , blog .UseMock (), nil )
709+ testClient := New (time .Second * 10 , staticProvider , metrics .NoopRegisterer , clock .NewFake (), 3 , "" , blog .UseMock (), tlsConfig )
679710 dr := testClient .(* impl )
680711 dr .dnsClient = & testExchanger {errs : []error {isTempErr , isTempErr , nil }}
681712 ctx , cancel := context .WithCancel (context .Background ())
@@ -754,7 +785,7 @@ func (e *rotateFailureExchanger) Exchange(m *dns.Msg, a string) (*dns.Msg, time.
754785
755786 // If its a broken server, return a retryable error
756787 if e .brokenAddresses [a ] {
757- isTempErr := & net. OpError {Op : "read" , Err : tempError (true )}
788+ isTempErr := & url. Error {Op : "read" , Err : tempError (true )}
758789 return nil , 2 * time .Millisecond , isTempErr
759790 }
760791
@@ -776,10 +807,9 @@ func TestRotateServerOnErr(t *testing.T) {
776807 // working server
777808 staticProvider , err := NewStaticProvider (dnsServers )
778809 test .AssertNotError (t , err , "Got error creating StaticProvider" )
779- fmt .Println (staticProvider .servers )
780810
781811 maxTries := 5
782- client := New (time .Second * 10 , staticProvider , metrics .NoopRegisterer , clock .NewFake (), maxTries , "" , blog .UseMock (), nil )
812+ client := New (time .Second * 10 , staticProvider , metrics .NoopRegisterer , clock .NewFake (), maxTries , "" , blog .UseMock (), tlsConfig )
783813
784814 // Configure a mock exchanger that will always return a retryable error for
785815 // servers A and B. This will force server "[2606:4700:4700::1111]:53" to do
@@ -843,13 +873,10 @@ func (dohE *dohAlwaysRetryExchanger) Exchange(m *dns.Msg, a string) (*dns.Msg, t
843873}
844874
845875func TestDOHMetric (t * testing.T ) {
846- features .Set (features.Config {DOH : true })
847- defer features .Reset ()
848-
849876 staticProvider , err := NewStaticProvider ([]string {dnsLoopbackAddr })
850877 test .AssertNotError (t , err , "Got error creating StaticProvider" )
851878
852- testClient := New (time .Second * 11 , staticProvider , metrics .NoopRegisterer , clock .NewFake (), 0 , "" , blog .UseMock (), nil )
879+ testClient := New (time .Second * 11 , staticProvider , metrics .NoopRegisterer , clock .NewFake (), 0 , "" , blog .UseMock (), tlsConfig )
853880 resolver := testClient .(* impl )
854881 resolver .dnsClient = & dohAlwaysRetryExchanger {err : & url.Error {Op : "read" , Err : tempError (true )}}
855882
0 commit comments