Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support custom DoH resolvers #8068

Merged
merged 17 commits into from
May 11, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion cmd/ipfs/dnsresolve_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,3 @@ func TestApiEndpointResolveDNSNoResults(t *testing.T) {
t.Errorf("expected error not thrown; actual: %v", err)
}
}

7 changes: 6 additions & 1 deletion core/commands/dns.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,14 @@ The resolver can recursively resolve:
cmds.BoolOption(dnsRecursiveOptionName, "r", "Resolve until the result is not a DNS link.").WithDefault(true),
},
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
node, err := cmdenv.GetNode(env)
if err != nil {
return err
}

recursive, _ := req.Options[dnsRecursiveOptionName].(bool)
name := req.Arguments[0]
resolver := namesys.NewDNSResolver()
resolver := namesys.NewDNSResolver(node.DNSResolver.LookupTXT)

var routing []nsopts.ResolveOpt
if !recursive {
Expand Down
22 changes: 16 additions & 6 deletions core/commands/swarm.go
Original file line number Diff line number Diff line change
Expand Up @@ -360,14 +360,19 @@ ipfs swarm connect /ip4/104.131.131.82/tcp/4001/p2p/QmaCpDMGvV2BGHeYERUEnRQAwe3N
cmds.StringArg("address", true, true, "Address of peer to connect to.").EnableStdin(),
},
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
node, err := cmdenv.GetNode(env)
if err != nil {
return err
}

api, err := cmdenv.GetApi(env, req)
if err != nil {
return err
}

addrs := req.Arguments

pis, err := parseAddresses(req.Context, addrs)
pis, err := parseAddresses(req.Context, addrs, node.DNSResolver)
if err != nil {
return err
}
Expand Down Expand Up @@ -408,12 +413,17 @@ it will reconnect.
cmds.StringArg("address", true, true, "Address of peer to disconnect from.").EnableStdin(),
},
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
node, err := cmdenv.GetNode(env)
if err != nil {
return err
}

api, err := cmdenv.GetApi(env, req)
if err != nil {
return err
}

addrs, err := parseAddresses(req.Context, req.Arguments)
addrs, err := parseAddresses(req.Context, req.Arguments, node.DNSResolver)
if err != nil {
return err
}
Expand Down Expand Up @@ -453,9 +463,9 @@ it will reconnect.

// parseAddresses is a function that takes in a slice of string peer addresses
// (multiaddr + peerid) and returns a slice of properly constructed peers
func parseAddresses(ctx context.Context, addrs []string) ([]peer.AddrInfo, error) {
func parseAddresses(ctx context.Context, addrs []string, rslv *madns.Resolver) ([]peer.AddrInfo, error) {
// resolve addresses
maddrs, err := resolveAddresses(ctx, addrs)
maddrs, err := resolveAddresses(ctx, addrs, rslv)
if err != nil {
return nil, err
}
Expand All @@ -464,7 +474,7 @@ func parseAddresses(ctx context.Context, addrs []string) ([]peer.AddrInfo, error
}

// resolveAddresses resolves addresses parallelly
func resolveAddresses(ctx context.Context, addrs []string) ([]ma.Multiaddr, error) {
func resolveAddresses(ctx context.Context, addrs []string, rslv *madns.Resolver) ([]ma.Multiaddr, error) {
ctx, cancel := context.WithTimeout(ctx, dnsResolveTimeout)
defer cancel()

Expand All @@ -488,7 +498,7 @@ func resolveAddresses(ctx context.Context, addrs []string) ([]ma.Multiaddr, erro
wg.Add(1)
go func(maddr ma.Multiaddr) {
defer wg.Done()
raddrs, err := madns.Resolve(ctx, maddr)
raddrs, err := rslv.Resolve(ctx, maddr)
if err != nil {
resolveErrC <- err
return
Expand Down
2 changes: 2 additions & 0 deletions core/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import (
"github.com/libp2p/go-libp2p/p2p/discovery"
p2pbhost "github.com/libp2p/go-libp2p/p2p/host/basic"
ma "github.com/multiformats/go-multiaddr"
madns "github.com/multiformats/go-multiaddr-dns"

"github.com/ipfs/go-ipfs/core/bootstrap"
"github.com/ipfs/go-ipfs/core/node"
Expand Down Expand Up @@ -88,6 +89,7 @@ type IpfsNode struct {
Filters *ma.Filters `optional:"true"`
Bootstrapper io.Closer `optional:"true"` // the periodic bootstrapper
Routing routing.Routing `optional:"true"` // the routing system. recommend ipfs-dht
DNSResolver *madns.Resolver // the DNS resolver
Exchange exchange.Interface // the block exchange + strategy (bitswap)
Namesys namesys.NameSystem // the name system, resolves paths to hashes
Provider provider.System // the value provider system
Expand Down
17 changes: 14 additions & 3 deletions core/coreapi/coreapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import (
routing "github.com/libp2p/go-libp2p-core/routing"
pubsub "github.com/libp2p/go-libp2p-pubsub"
record "github.com/libp2p/go-libp2p-record"
madns "github.com/multiformats/go-multiaddr-dns"

"github.com/ipfs/go-ipfs/core"
"github.com/ipfs/go-ipfs/core/node"
Expand All @@ -62,8 +63,9 @@ type CoreAPI struct {
recordValidator record.Validator
exchange exchange.Interface

namesys namesys.NameSystem
routing routing.Routing
namesys namesys.NameSystem
routing routing.Routing
dnsResolver *madns.Resolver

provider provider.System

Expand Down Expand Up @@ -174,6 +176,7 @@ func (api *CoreAPI) WithOptions(opts ...options.ApiOption) (coreiface.CoreAPI, e
recordValidator: n.RecordValidator,
exchange: n.Exchange,
routing: n.Routing,
dnsResolver: n.DNSResolver,

provider: n.Provider,

Expand Down Expand Up @@ -212,7 +215,15 @@ func (api *CoreAPI) WithOptions(opts ...options.ApiOption) (coreiface.CoreAPI, e
}

subApi.routing = offlineroute.NewOfflineRouter(subApi.repo.Datastore(), subApi.recordValidator)
subApi.namesys = namesys.NewNameSystem(subApi.routing, subApi.repo.Datastore(), cs)

subApi.namesys, err = namesys.NewNameSystem(subApi.routing,
namesys.WithDatastore(subApi.repo.Datastore()),
namesys.WithDNSResolver(subApi.dnsResolver),
namesys.WithCache(cs))
if err != nil {
return nil, fmt.Errorf("error constructing namesys: %w", err)
}

subApi.provider = provider.NewOfflineProvider()

subApi.peerstore = nil
Expand Down
8 changes: 6 additions & 2 deletions core/coreapi/name.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,13 @@ func (api *NameAPI) Search(ctx context.Context, name string, opts ...caopts.Name
}

var resolver namesys.Resolver = api.namesys

if !options.Cache {
resolver = namesys.NewNameSystem(api.routing, api.repo.Datastore(), 0)
resolver, err = namesys.NewNameSystem(api.routing,
namesys.WithDatastore(api.repo.Datastore()),
namesys.WithDNSResolver(api.dnsResolver))
if err != nil {
return nil, err
}
}

if !strings.HasPrefix(name, "/ipns/") {
Expand Down
81 changes: 81 additions & 0 deletions core/node/dns.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package node

import (
"fmt"
"strings"

config "github.com/ipfs/go-ipfs-config"
doh "github.com/libp2p/go-doh-resolver"
madns "github.com/multiformats/go-multiaddr-dns"

"github.com/miekg/dns"
)

var defaultResolvers = map[string]string{
"eth.": "https://resolver.cloudflare-eth.com/dns-query",
"crypto.": "https://resolver.cloudflare-eth.com/dns-query",
}

func newResolver(url string) (madns.BasicResolver, error) {
if !strings.HasPrefix(url, "https://") {
return nil, fmt.Errorf("invalid resolver url: %s", url)
}

return doh.NewResolver(url), nil
}

func DNSResolver(cfg *config.Config) (*madns.Resolver, error) {
vyzo marked this conversation as resolved.
Show resolved Hide resolved
var opts []madns.Option
var err error

domains := make(map[string]struct{}) // to track overriden default resolvers
rslvrs := make(map[string]madns.BasicResolver) // to reuse resolvers for the same URL

for domain, url := range cfg.DNS.Resolvers {
if domain != "." && !dns.IsFqdn(domain) {
return nil, fmt.Errorf("invalid domain %s; must be FQDN", domain)
}

domains[domain] = struct{}{}
if url == "" {
// allow overriding of implicit defaults with the default resolver
continue
}

rslv, ok := rslvrs[url]
if !ok {
rslv, err = newResolver(url)
if err != nil {
return nil, fmt.Errorf("bad resolver for %s: %w", domain, err)
}
rslvrs[url] = rslv
}

if domain != "." {
opts = append(opts, madns.WithDomainResolver(domain, rslv))
} else {
opts = append(opts, madns.WithDefaultResolver(rslv))
}
}

// fill in defaults if not overriden by the user
for domain, url := range defaultResolvers {
_, ok := domains[domain]
if ok {
continue
}

rslv, ok := rslvrs[url]
if !ok {
rslv, err = newResolver(url)
if err != nil {
return nil, fmt.Errorf("bad resolver for %s: %w", domain, err)
}
rslvrs[url] = rslv
}

opts = append(opts, madns.WithDomainResolver(domain, rslv))
}

return madns.NewResolver(opts...)
}
3 changes: 3 additions & 0 deletions core/node/groups.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ var BaseLibP2P = fx.Options(
fx.Provide(libp2p.PNet),
fx.Provide(libp2p.ConnectionManager),
fx.Provide(libp2p.Host),
fx.Provide(libp2p.MultiaddrResolver),

fx.Provide(libp2p.DiscoveryHandler),

Expand Down Expand Up @@ -264,6 +265,7 @@ func Online(bcfg *BuildCfg, cfg *config.Config) fx.Option {
return fx.Options(
fx.Provide(OnlineExchange(shouldBitswapProvide)),
maybeProvide(Graphsync, cfg.Experimental.GraphsyncEnabled),
fx.Provide(DNSResolver),
fx.Provide(Namesys(ipnsCacheSize)),
fx.Provide(Peering),
PeerWith(cfg.Peering.Peers...),
Expand All @@ -281,6 +283,7 @@ func Online(bcfg *BuildCfg, cfg *config.Config) fx.Option {
func Offline(cfg *config.Config) fx.Option {
return fx.Options(
fx.Provide(offline.Exchange),
fx.Provide(DNSResolver),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This preserves existing behavior. @Stebalien Is this the correct behavior, or would we want some nilDNSResolver here?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we should use the nilDNSResolver. I'd consider this a bug fix.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But we can also punt to a second PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's pretty easy to do, so I can do it here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

undone, as it is a behaviour change that leads to a regression; we can easily consider it in a follow up pr.

fx.Provide(Namesys(0)),
fx.Provide(offroute.NewOfflineRouter),
OfflineProviders(cfg.Experimental.StrategicProviding, cfg.Reprovider.Strategy, cfg.Reprovider.Interval),
Expand Down
16 changes: 13 additions & 3 deletions core/node/ipns.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/libp2p/go-libp2p-core/peerstore"
"github.com/libp2p/go-libp2p-core/routing"
"github.com/libp2p/go-libp2p-record"
madns "github.com/multiformats/go-multiaddr-dns"

"github.com/ipfs/go-ipfs/repo"
"github.com/ipfs/go-namesys"
Expand All @@ -27,9 +28,18 @@ func RecordValidator(ps peerstore.Peerstore) record.Validator {
}

// Namesys creates new name system
func Namesys(cacheSize int) func(rt routing.Routing, repo repo.Repo) (namesys.NameSystem, error) {
return func(rt routing.Routing, repo repo.Repo) (namesys.NameSystem, error) {
return namesys.NewNameSystem(rt, repo.Datastore(), cacheSize), nil
func Namesys(cacheSize int) func(rt routing.Routing, rslv *madns.Resolver, repo repo.Repo) (namesys.NameSystem, error) {
return func(rt routing.Routing, rslv *madns.Resolver, repo repo.Repo) (namesys.NameSystem, error) {
opts := []namesys.Option{
namesys.WithDatastore(repo.Datastore()),
namesys.WithDNSResolver(rslv),
}

if cacheSize > 0 {
opts = append(opts, namesys.WithCache(cacheSize))
}

return namesys.NewNameSystem(rt, opts...)
}
}

Expand Down
11 changes: 11 additions & 0 deletions core/node/libp2p/dns.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package libp2p

import (
"github.com/libp2p/go-libp2p"
madns "github.com/multiformats/go-multiaddr-dns"
)

func MultiaddrResolver(rslv *madns.Resolver) (opts Libp2pOpts, err error) {
opts.Opts = append(opts.Opts, libp2p.MultiaddrResolver(rslv))
return opts, nil
}
Loading