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

Added custom DNS resolver with support for DNS-over-TLS and DNS-over-HTTPS #6560

Closed
wants to merge 7 commits into from
Closed
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
12 changes: 11 additions & 1 deletion core/commands/dns.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"io"

oldcmds "github.com/ipfs/go-ipfs/commands"
ncmd "github.com/ipfs/go-ipfs/core/commands/name"
namesys "github.com/ipfs/go-ipfs/namesys"
nsopts "github.com/ipfs/interface-go-ipfs-core/options/namesys"
Expand Down Expand Up @@ -60,9 +61,18 @@ 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 {
cctx := env.(*oldcmds.Context)
cfg, err := cctx.GetConfig()
if err != nil {
return err
}

recursive, _ := req.Options[dnsRecursiveOptionName].(bool)
name := req.Arguments[0]
resolver := namesys.NewDNSResolver()
resolver, err := namesys.NewDNSResolver(cfg)
if err != nil {
return err
}

var routing []nsopts.ResolveOpt
if !recursive {
Expand Down
5 changes: 4 additions & 1 deletion core/coreapi/coreapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -215,8 +215,11 @@ 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.provider = provider.NewOfflineProvider()
subApi.namesys, err = namesys.NewNameSystem(subApi.routing, subApi.repo.Datastore(), cs, cfg)
if err != nil {
return nil, err
}

subApi.peerstore = nil
subApi.peerHost = nil
Expand Down
9 changes: 8 additions & 1 deletion core/coreapi/name.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,14 @@ 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)
cfg, err := api.repo.Config()
if err != nil {
return nil, err
}
resolver, err = namesys.NewNameSystem(api.routing, api.repo.Datastore(), 0, cfg)
if err != nil {
return nil, err
}
}

if !strings.HasPrefix(name, "/ipns/") {
Expand Down
10 changes: 9 additions & 1 deletion core/node/ipns.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,15 @@ 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
cfg, err := repo.Config()
if err != nil {
return nil, err
}
ns, err := namesys.NewNameSystem(rt, repo.Datastore(), cacheSize, cfg)
if err != nil {
return nil, err
}
return ns, nil
}
}

Expand Down
4 changes: 4 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ require (
github.com/libp2p/go-maddr-filter v0.0.5
github.com/mattn/go-runewidth v0.0.4 // indirect
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
github.com/miekg/dns v1.1.12
github.com/mitchellh/go-homedir v1.1.0
github.com/mr-tron/base58 v1.1.2
github.com/multiformats/go-multiaddr v0.0.4
Expand All @@ -95,6 +96,7 @@ require (
github.com/multiformats/go-multibase v0.0.1
github.com/multiformats/go-multihash v0.0.5
github.com/opentracing/opentracing-go v1.1.0
github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/pkg/errors v0.8.1
github.com/prometheus/client_golang v0.9.3
github.com/prometheus/procfs v0.0.0-20190519111021-9935e8e0588d // indirect
Expand All @@ -109,7 +111,9 @@ require (
go.uber.org/goleak v0.10.0 // indirect
go.uber.org/multierr v1.1.0 // indirect
go4.org v0.0.0-20190313082347-94abd6928b1d // indirect
golang.org/x/net v0.0.0-20190620200207-3b0461eec859
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb
google.golang.org/appengine v1.4.0 // indirect
gopkg.in/cheggaaa/pb.v1 v1.0.28
gotest.tools/gotestsum v0.3.4
)
Expand Down
89 changes: 85 additions & 4 deletions namesys/dns.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,107 @@ package namesys
import (
"context"
"errors"
"fmt"
"net"
"strconv"
"strings"

config "github.com/ipfs/go-ipfs-config"
path "github.com/ipfs/go-path"
opts "github.com/ipfs/interface-go-ipfs-core/options/namesys"
isd "github.com/jbenet/go-is-domain"
ma "github.com/multiformats/go-multiaddr"
)

// LookupTXTFunc is the interface for the lookupTXT property of DNSResolver
type LookupTXTFunc func(name string) (txt []string, err error)

// DNSResolver implements a Resolver on DNS domains
type DNSResolver struct {
lookupTXT LookupTXTFunc
// TODO: maybe some sort of caching?
// cache would need a timeout
}

// NewDNSResolver constructs a name resolver using DNS TXT records.
func NewDNSResolver() *DNSResolver {
return &DNSResolver{lookupTXT: net.LookupTXT}
func NewDNSResolver(cfg *config.Config) (*DNSResolver, error) {
// Check if we're using a custom DNS server
if len(cfg.DNS.Resolver) > 0 {

Choose a reason for hiding this comment

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

Please add system environmental variables.

// Custom DNS resolver
dns := &customDNS{}

// Parse the multi-address
resolverMaddr, err := ma.NewMultiaddr(cfg.DNS.Resolver)
if err != nil {
return nil, fmt.Errorf("Invalid DNS Resolver address: %q (err: %s)", cfg.DNS.Resolver, err)
}
proto := resolverMaddr.Protocols()

// Need to have at least 1 component
if len(proto) < 1 {
return nil, fmt.Errorf("Invalid DNS Resolver address: %q (Not enough components)", cfg.DNS.Resolver)
}

// First component should be the IP or DNS
if proto[0].Code == ma.P_IP4 || proto[0].Code == ma.P_IP6 {
val, err := resolverMaddr.ValueForProtocol(proto[0].Code)
if err != nil {
return nil, fmt.Errorf("Invalid DNS Resolver address: %q (Invalid value for protocol 0)", cfg.DNS.Resolver)
}
dns.Address = val
} else {
return nil, fmt.Errorf("Invalid DNS Resolver address: %q (Invalid type for protocol 0)", cfg.DNS.Resolver)
}

// Second component is optional and it's the port
// If it's UDP, we stop here, as we're using normal DNS
// If it's TCP, we will need more info
if len(proto) > 1 && proto[1].Code == ma.P_UDP {
val, err := resolverMaddr.ValueForProtocol(proto[1].Code)
if err != nil {
return nil, fmt.Errorf("Invalid DNS Resolver address: %q (Invalid value for protocol 1)", cfg.DNS.Resolver)
}
dns.Protocol = "udp"
if len(val) > 0 && val != "0" {
n, err := strconv.ParseUint(val, 10, 32)
if err != nil {
return nil, err
}
dns.Port = uint(n)
}
} else if len(proto) > 2 && proto[1].Code == ma.P_TCP {
// Require at least 3 components here because we need to know if it's using tls or https
val, err := resolverMaddr.ValueForProtocol(proto[1].Code)
if err != nil {
return nil, fmt.Errorf("Invalid DNS Resolver address: %q (Invalid value for protocol 1)", cfg.DNS.Resolver)
}
if len(val) > 0 && val != "0" {
n, err := strconv.ParseUint(val, 10, 32)
if err != nil {
return nil, err
}
dns.Port = uint(n)
}

// Need the protocol: "tls" for DNS-over-TLS or "https" for DNS-over-HTTPS
if proto[2].Code == ma.P_HTTPS {
// TODO: Get the host, not yet supported
dns.Protocol = "dns-over-https"

// This is pending https://github.com/multiformats/multicodec/pull/145 and the addition of the TLS protocol insiode https://github.com/multiformats/go-multiaddr/
/*} else if proto[2].Code == ma.P_TLS {
dns.Protocol = "dns-over-tls" */
} else {
return nil, fmt.Errorf("Invalid DNS Resolver address: %q (Invalid format)", cfg.DNS.Resolver)
}
} else if len(proto) > 1 {
return nil, fmt.Errorf("Invalid DNS Resolver address: %q (Invalid format)", cfg.DNS.Resolver)
}

// Return the resolver
return &DNSResolver{lookupTXT: dns.LookupTXT}, nil
}

// Use the system's built-in resolver
return &DNSResolver{lookupTXT: net.LookupTXT}, nil
}

// Resolve implements Resolver.
Expand Down
Loading