11package certmagic
22
33import (
4+ "context"
45 "errors"
56 "fmt"
67 "net"
@@ -18,21 +19,24 @@ import (
1819//
1920// It has been modified.
2021
21- // findZoneByFQDN determines the zone apex for the given fqdn by recursing
22- // up the domain labels until the nameserver returns a SOA record in the
23- // answer section. The logger must be non-nil.
24- func findZoneByFQDN (logger * zap.Logger , fqdn string , nameservers []string ) (string , error ) {
22+ // FindZoneByFQDN determines the zone apex for the given fully-qualified
23+ // domain name (FQDN) by recursing up the domain labels until the nameserver
24+ // returns a SOA record in the answer section. The logger must be non-nil.
25+ //
26+ // EXPERIMENTAL: This API was previously unexported, and may be changed or
27+ // unexported again in the future. Do not rely on it at this time.
28+ func FindZoneByFQDN (ctx context.Context , logger * zap.Logger , fqdn string , nameservers []string ) (string , error ) {
2529 if ! strings .HasSuffix (fqdn , "." ) {
2630 fqdn += "."
2731 }
28- soa , err := lookupSoaByFqdn (logger , fqdn , nameservers )
32+ soa , err := lookupSoaByFqdn (ctx , logger , fqdn , nameservers )
2933 if err != nil {
3034 return "" , err
3135 }
3236 return soa .zone , nil
3337}
3438
35- func lookupSoaByFqdn (logger * zap.Logger , fqdn string , nameservers []string ) (* soaCacheEntry , error ) {
39+ func lookupSoaByFqdn (ctx context. Context , logger * zap.Logger , fqdn string , nameservers []string ) (* soaCacheEntry , error ) {
3640 logger = logger .Named ("soa_lookup" )
3741
3842 if ! strings .HasSuffix (fqdn , "." ) {
@@ -42,13 +46,17 @@ func lookupSoaByFqdn(logger *zap.Logger, fqdn string, nameservers []string) (*so
4246 fqdnSOACacheMu .Lock ()
4347 defer fqdnSOACacheMu .Unlock ()
4448
49+ if err := ctx .Err (); err != nil {
50+ return nil , err
51+ }
52+
4553 // prefer cached version if fresh
4654 if ent := fqdnSOACache [fqdn ]; ent != nil && ! ent .isExpired () {
4755 logger .Debug ("using cached SOA result" , zap .String ("entry" , ent .zone ))
4856 return ent , nil
4957 }
5058
51- ent , err := fetchSoaByFqdn (logger , fqdn , nameservers )
59+ ent , err := fetchSoaByFqdn (ctx , logger , fqdn , nameservers )
5260 if err != nil {
5361 return nil , err
5462 }
@@ -66,15 +74,19 @@ func lookupSoaByFqdn(logger *zap.Logger, fqdn string, nameservers []string) (*so
6674 return ent , nil
6775}
6876
69- func fetchSoaByFqdn (logger * zap.Logger , fqdn string , nameservers []string ) (* soaCacheEntry , error ) {
77+ func fetchSoaByFqdn (ctx context. Context , logger * zap.Logger , fqdn string , nameservers []string ) (* soaCacheEntry , error ) {
7078 var err error
7179 var in * dns.Msg
7280
7381 labelIndexes := dns .Split (fqdn )
7482 for _ , index := range labelIndexes {
83+ if err := ctx .Err (); err != nil {
84+ return nil , err
85+ }
86+
7587 domain := fqdn [index :]
7688
77- in , err = dnsQuery (domain , dns .TypeSOA , nameservers , true )
89+ in , err = dnsQuery (ctx , domain , dns .TypeSOA , nameservers , true )
7890 if err != nil {
7991 continue
8092 }
@@ -122,12 +134,12 @@ func dnsMsgContainsCNAME(msg *dns.Msg) bool {
122134 return false
123135}
124136
125- func dnsQuery (fqdn string , rtype uint16 , nameservers []string , recursive bool ) (* dns.Msg , error ) {
137+ func dnsQuery (ctx context. Context , fqdn string , rtype uint16 , nameservers []string , recursive bool ) (* dns.Msg , error ) {
126138 m := createDNSMsg (fqdn , rtype , recursive )
127139 var in * dns.Msg
128140 var err error
129141 for _ , ns := range nameservers {
130- in , err = sendDNSQuery (m , ns )
142+ in , err = sendDNSQuery (ctx , m , ns )
131143 if err == nil && len (in .Answer ) > 0 {
132144 break
133145 }
@@ -147,16 +159,16 @@ func createDNSMsg(fqdn string, rtype uint16, recursive bool) *dns.Msg {
147159 return m
148160}
149161
150- func sendDNSQuery (m * dns.Msg , ns string ) (* dns.Msg , error ) {
162+ func sendDNSQuery (ctx context. Context , m * dns.Msg , ns string ) (* dns.Msg , error ) {
151163 udp := & dns.Client {Net : "udp" , Timeout : dnsTimeout }
152- in , _ , err := udp .Exchange ( m , ns )
164+ in , _ , err := udp .ExchangeContext ( ctx , m , ns )
153165 // two kinds of errors we can handle by retrying with TCP:
154166 // truncation and timeout; see https://github.com/caddyserver/caddy/issues/3639
155167 truncated := in != nil && in .Truncated
156168 timeoutErr := err != nil && strings .Contains (err .Error (), "timeout" )
157169 if truncated || timeoutErr {
158170 tcp := & dns.Client {Net : "tcp" , Timeout : dnsTimeout }
159- in , _ , err = tcp .Exchange ( m , ns )
171+ in , _ , err = tcp .ExchangeContext ( ctx , m , ns )
160172 }
161173 return in , err
162174}
@@ -205,7 +217,8 @@ func systemOrDefaultNameservers(path string, defaults []string) []string {
205217 return config .Servers
206218}
207219
208- // populateNameserverPorts ensures that all nameservers have a port number.
220+ // populateNameserverPorts ensures that all nameservers have a port number
221+ // If not, the the default DNS server port of 53 will be appended.
209222func populateNameserverPorts (servers []string ) {
210223 for i := range servers {
211224 _ , port , _ := net .SplitHostPort (servers [i ])
@@ -216,7 +229,7 @@ func populateNameserverPorts(servers []string) {
216229}
217230
218231// checkDNSPropagation checks if the expected record has been propagated to all authoritative nameservers.
219- func checkDNSPropagation (logger * zap.Logger , fqdn string , recType uint16 , expectedValue string , checkAuthoritativeServers bool , resolvers []string ) (bool , error ) {
232+ func checkDNSPropagation (ctx context. Context , logger * zap.Logger , fqdn string , recType uint16 , expectedValue string , checkAuthoritativeServers bool , resolvers []string ) (bool , error ) {
220233 logger = logger .Named ("propagation" )
221234
222235 if ! strings .HasSuffix (fqdn , "." ) {
@@ -227,7 +240,7 @@ func checkDNSPropagation(logger *zap.Logger, fqdn string, recType uint16, expect
227240 // dereference (follow) a CNAME record if we are targeting a CNAME record
228241 // itself
229242 if recType != dns .TypeCNAME {
230- r , err := dnsQuery (fqdn , recType , resolvers , true )
243+ r , err := dnsQuery (ctx , fqdn , recType , resolvers , true )
231244 if err != nil {
232245 return false , fmt .Errorf ("CNAME dns query: %v" , err )
233246 }
@@ -237,7 +250,7 @@ func checkDNSPropagation(logger *zap.Logger, fqdn string, recType uint16, expect
237250 }
238251
239252 if checkAuthoritativeServers {
240- authoritativeServers , err := lookupNameservers (logger , fqdn , resolvers )
253+ authoritativeServers , err := lookupNameservers (ctx , logger , fqdn , resolvers )
241254 if err != nil {
242255 return false , fmt .Errorf ("looking up authoritative nameservers: %v" , err )
243256 }
@@ -246,13 +259,13 @@ func checkDNSPropagation(logger *zap.Logger, fqdn string, recType uint16, expect
246259 }
247260 logger .Debug ("checking authoritative nameservers" , zap .Strings ("resolvers" , resolvers ))
248261
249- return checkAuthoritativeNss (fqdn , recType , expectedValue , resolvers )
262+ return checkAuthoritativeNss (ctx , fqdn , recType , expectedValue , resolvers )
250263}
251264
252265// checkAuthoritativeNss queries each of the given nameservers for the expected record.
253- func checkAuthoritativeNss (fqdn string , recType uint16 , expectedValue string , nameservers []string ) (bool , error ) {
266+ func checkAuthoritativeNss (ctx context. Context , fqdn string , recType uint16 , expectedValue string , nameservers []string ) (bool , error ) {
254267 for _ , ns := range nameservers {
255- r , err := dnsQuery (fqdn , recType , []string {ns }, true )
268+ r , err := dnsQuery (ctx , fqdn , recType , []string {ns }, true )
256269 if err != nil {
257270 return false , fmt .Errorf ("querying authoritative nameservers: %v" , err )
258271 }
@@ -293,15 +306,15 @@ func checkAuthoritativeNss(fqdn string, recType uint16, expectedValue string, na
293306}
294307
295308// lookupNameservers returns the authoritative nameservers for the given fqdn.
296- func lookupNameservers (logger * zap.Logger , fqdn string , resolvers []string ) ([]string , error ) {
309+ func lookupNameservers (ctx context. Context , logger * zap.Logger , fqdn string , resolvers []string ) ([]string , error ) {
297310 var authoritativeNss []string
298311
299- zone , err := findZoneByFQDN ( logger , fqdn , resolvers )
312+ zone , err := FindZoneByFQDN ( ctx , logger , fqdn , resolvers )
300313 if err != nil {
301314 return nil , fmt .Errorf ("could not determine the zone for '%s': %w" , fqdn , err )
302315 }
303316
304- r , err := dnsQuery (zone , dns .TypeNS , resolvers , true )
317+ r , err := dnsQuery (ctx , zone , dns .TypeNS , resolvers , true )
305318 if err != nil {
306319 return nil , fmt .Errorf ("querying NS resolver for zone '%s' recursively: %v" , zone , err )
307320 }
@@ -330,11 +343,14 @@ func updateDomainWithCName(r *dns.Msg, fqdn string) string {
330343 return fqdn
331344}
332345
333- // recursiveNameservers are used to pre-check DNS propagation. It
346+ // RecursiveNameservers are used to pre-check DNS propagation. It
334347// picks user-configured nameservers (custom) OR the defaults
335348// obtained from resolv.conf and defaultNameservers if none is
336349// configured and ensures that all server addresses have a port value.
337- func recursiveNameservers (custom []string ) []string {
350+ //
351+ // EXPERIMENTAL: This API was previously unexported, and may be
352+ // be unexported again in the future. Do not rely on it at this time.
353+ func RecursiveNameservers (custom []string ) []string {
338354 var servers []string
339355 if len (custom ) == 0 {
340356 servers = systemOrDefaultNameservers (defaultResolvConf , defaultNameservers )
0 commit comments