kubo/core/node/dns.go
Marcin Rataj 7de7af0820
feat(dns): skip DNS lookups for AutoTLS hostnames (#11140)
* feat(dns): resolve libp2p.direct addresses locally without network I/O

p2p-forge hostnames encode IP addresses directly (e.g., 1-2-3-4.peerID.libp2p.direct -> 1.2.3.4),
so DNS queries are wasteful. kubo now parses these IPs in-memory.

- applies to both default libp2p.direct and custom AutoTLS.DomainSuffix
- TXT queries still delegate to network for ACME DNS-01 compatibility

- https://github.com/ipfs/kubo/pull/11140#discussion_r2683477754
  use fallback to network DNS instead of returning errors when local
  parsing fails, ensuring forward compatibility with future DNS records

- https://github.com/ipfs/kubo/pull/11140#discussion_r2683512408
  add peerID validation using peer.Decode(), matching libp2p.direct
  server behavior, with fallback on invalid peerID

- https://github.com/ipfs/kubo/pull/11140#discussion_r2683521930
  document interaction with DNS.Resolvers in config.md

- https://github.com/ipfs/kubo/pull/11140#discussion_r2683526647
  add AutoTLS.SkipDNSLookup config flag to disable local resolution
  (useful for debugging or custom DNS override scenarios)

- https://github.com/ipfs/kubo/pull/11140#discussion_r2683533462
  add E2E test verifying libp2p.direct resolves locally even when
  DNS.Resolvers points to a broken server

additional improvements:
- use madns.BasicResolver interface instead of custom basicResolver
- add compile-time interface checks for p2pForgeResolver and madns.Resolver
- refactor tests: merge IPv4/IPv6, add helpers, use config.DefaultDomainSuffix
- improve changelog to explain public good benefit (reducing DNS load)

Fixes #11136
2026-01-30 17:20:56 +01:00

57 lines
2.1 KiB
Go

package node
import (
"math"
"time"
"github.com/ipfs/boxo/gateway"
config "github.com/ipfs/kubo/config"
doh "github.com/libp2p/go-doh-resolver"
madns "github.com/multiformats/go-multiaddr-dns"
)
// Compile-time interface check: *madns.Resolver (returned by gateway.NewDNSResolver
// and madns.NewResolver) must implement madns.BasicResolver for p2pForgeResolver fallback.
var _ madns.BasicResolver = (*madns.Resolver)(nil)
func DNSResolver(cfg *config.Config) (*madns.Resolver, error) {
var dohOpts []doh.Option
if !cfg.DNS.MaxCacheTTL.IsDefault() {
dohOpts = append(dohOpts, doh.WithMaxCacheTTL(cfg.DNS.MaxCacheTTL.WithDefault(time.Duration(math.MaxUint32)*time.Second)))
}
// Replace "auto" DNS resolver placeholders with autoconf values
resolvers := cfg.DNSResolversWithAutoConf()
// Get base resolver from boxo (handles custom DoH resolvers per eTLD)
baseResolver, err := gateway.NewDNSResolver(resolvers, dohOpts...)
if err != nil {
return nil, err
}
// Check if we should skip network DNS lookups for p2p-forge domains
skipAutoTLSDNS := cfg.AutoTLS.SkipDNSLookup.WithDefault(config.DefaultAutoTLSSkipDNSLookup)
if !skipAutoTLSDNS {
// Local resolution disabled, use network DNS for everything
return baseResolver, nil
}
// Build list of p2p-forge domains to resolve locally without network I/O.
// AutoTLS hostnames encode IP addresses directly (e.g., 1-2-3-4.peerID.libp2p.direct),
// so DNS lookups are wasteful. We resolve these in-memory when possible.
forgeDomains := []string{config.DefaultDomainSuffix}
customDomain := cfg.AutoTLS.DomainSuffix.WithDefault(config.DefaultDomainSuffix)
if customDomain != config.DefaultDomainSuffix {
forgeDomains = append(forgeDomains, customDomain)
}
forgeResolver := NewP2PForgeResolver(forgeDomains, baseResolver)
// Register p2p-forge resolver for each domain, fallback to baseResolver for others
opts := []madns.Option{madns.WithDefaultResolver(baseResolver)}
for _, domain := range forgeDomains {
opts = append(opts, madns.WithDomainResolver(domain+".", forgeResolver))
}
return madns.NewResolver(opts...)
}