Commit 4ff80c19 authored by Lars Gierth's avatar Lars Gierth Committed by GitHub
Browse files

Merge pull request #189 from libp2p/feat/dns

Resolve /dns4, /dns6, /dnsaddr multiaddrs
parents 6d63a88a e0c5c22d
......@@ -18,6 +18,7 @@ import (
pstore "github.com/libp2p/go-libp2p-peerstore"
protocol "github.com/libp2p/go-libp2p-protocol"
ma "github.com/multiformats/go-multiaddr"
madns "github.com/multiformats/go-multiaddr-dns"
msmux "github.com/multiformats/go-multistream"
)
......@@ -55,12 +56,13 @@ const NATPortMap Option = iota
// * uses an identity service to send + receive node information
// * uses a nat service to establish NAT port mappings
type BasicHost struct {
network inet.Network
mux *msmux.MultistreamMuxer
ids *identify.IDService
natmgr NATManager
addrs AddrsFactory
cmgr connmgr.ConnManager
network inet.Network
mux *msmux.MultistreamMuxer
ids *identify.IDService
natmgr NATManager
addrs AddrsFactory
maResolver *madns.Resolver
cmgr connmgr.ConnManager
negtimeout time.Duration
......@@ -89,11 +91,16 @@ type HostOpts struct {
// If omitted, there's no override or filtering, and the results of Addrs and AllAddrs are the same.
AddrsFactory AddrsFactory
// MultiaddrResolves holds the go-multiaddr-dns.Resolver used for resolving
// /dns4, /dns6, and /dnsaddr addresses before trying to connect to a peer.
MultiaddrResolver *madns.Resolver
// NATManager takes care of setting NAT port mappings, and discovering external addresses.
// If omitted, this will simply be disabled.
NATManager NATManager
//
// BandwidthReporter is used for collecting aggregate metrics of the
// bandwidth used by various protocols.
BandwidthReporter metrics.Reporter
// ConnManager is a libp2p connection manager
......@@ -113,6 +120,7 @@ func NewHost(ctx context.Context, net inet.Network, opts *HostOpts) (*BasicHost,
mux: msmux.NewMultistreamMuxer(),
negtimeout: DefaultNegotiationTimeout,
addrs: DefaultAddrsFactory,
maResolver: madns.DefaultResolver,
}
if opts.MultistreamMuxer != nil {
......@@ -138,6 +146,10 @@ func NewHost(ctx context.Context, net inet.Network, opts *HostOpts) (*BasicHost,
h.natmgr = opts.NATManager
}
if opts.MultiaddrResolver != nil {
h.maResolver = opts.MultiaddrResolver
}
if opts.BandwidthReporter != nil {
h.bwc = opts.BandwidthReporter
h.ids.Reporter = opts.BandwidthReporter
......@@ -197,6 +209,8 @@ func New(net inet.Network, opts ...interface{}) *BasicHost {
hostopts.AddrsFactory = AddrsFactory(o)
case connmgr.ConnManager:
hostopts.ConnManager = o
case *madns.Resolver:
hostopts.MultiaddrResolver = o
}
}
......@@ -406,12 +420,11 @@ func (h *BasicHost) newStream(ctx context.Context, p peer.ID, pid protocol.ID) (
}
// Connect ensures there is a connection between this host and the peer with
// given peer.ID. Connect will absorb the addresses in pi into its internal
// peerstore. If there is not an active connection, Connect will issue a
// h.Network.Dial, and block until a connection is open, or an error is
// returned.
// given peer.ID. If there is not an active connection, Connect will issue a
// h.Network.Dial, and block until a connection is open, or an error is returned.
// Connect will absorb the addresses in pi into its internal peerstore.
// It will also resolve any /dns4, /dns6, and /dnsaddr addresses.
func (h *BasicHost) Connect(ctx context.Context, pi pstore.PeerInfo) error {
// absorb addresses into peerstore
h.Peerstore().AddAddrs(pi.ID, pi.Addrs, pstore.TempAddrTTL)
......@@ -420,9 +433,46 @@ func (h *BasicHost) Connect(ctx context.Context, pi pstore.PeerInfo) error {
return nil
}
resolved, err := h.resolveAddrs(ctx, h.Peerstore().PeerInfo(pi.ID))
if err != nil {
return err
}
h.Peerstore().AddAddrs(pi.ID, resolved, pstore.TempAddrTTL)
return h.dialPeer(ctx, pi.ID)
}
func (h *BasicHost) resolveAddrs(ctx context.Context, pi pstore.PeerInfo) ([]ma.Multiaddr, error) {
proto := ma.ProtocolWithCode(ma.P_IPFS).Name
p2paddr, err := ma.NewMultiaddr("/" + proto + "/" + pi.ID.Pretty())
if err != nil {
return nil, err
}
var addrs []ma.Multiaddr
for _, addr := range pi.Addrs {
addrs = append(addrs, addr)
if !madns.Matches(addr) {
continue
}
reqaddr := addr.Encapsulate(p2paddr)
resaddrs, err := h.maResolver.Resolve(ctx, reqaddr)
if err != nil {
log.Infof("error resolving %s: %s", reqaddr, err)
}
for _, res := range resaddrs {
pi, err := pstore.InfoFromP2pAddr(res)
if err != nil {
log.Infof("error parsing %s: %s", res, err)
}
addrs = append(addrs, pi.Addrs...)
}
}
return addrs, nil
}
// dialPeer opens a connection to peer, and makes sure to identify
// the connection once it has been opened.
func (h *BasicHost) dialPeer(ctx context.Context, p peer.ID) error {
......
......@@ -4,14 +4,17 @@ import (
"bytes"
"context"
"io"
"sort"
"testing"
"time"
host "github.com/libp2p/go-libp2p-host"
inet "github.com/libp2p/go-libp2p-net"
testutil "github.com/libp2p/go-libp2p-netutil"
pstore "github.com/libp2p/go-libp2p-peerstore"
protocol "github.com/libp2p/go-libp2p-protocol"
ma "github.com/multiformats/go-multiaddr"
madns "github.com/multiformats/go-multiaddr-dns"
)
func TestHostSimple(t *testing.T) {
......@@ -330,3 +333,55 @@ func TestProtoDowngrade(t *testing.T) {
s2.Close()
}
func TestAddrResolution(t *testing.T) {
ctx := context.Background()
p1, err := testutil.RandPeerID()
if err != nil {
t.Error(err)
}
p2, err := testutil.RandPeerID()
if err != nil {
t.Error(err)
}
addr1 := ma.StringCast("/dnsaddr/example.com")
addr2 := ma.StringCast("/ip4/192.0.2.1/tcp/123")
p2paddr1 := ma.StringCast("/dnsaddr/example.com/ipfs/" + p1.Pretty())
p2paddr2 := ma.StringCast("/ip4/192.0.2.1/tcp/123/ipfs/" + p1.Pretty())
p2paddr3 := ma.StringCast("/ip4/192.0.2.1/tcp/123/ipfs/" + p2.Pretty())
backend := &madns.MockBackend{
TXT: map[string][]string{"_dnsaddr.example.com": []string{
"dnsaddr=" + p2paddr2.String(), "dnsaddr=" + p2paddr3.String(),
}},
}
resolver := &madns.Resolver{Backend: backend}
h := New(testutil.GenSwarmNetwork(t, ctx), resolver)
defer h.Close()
pi, err := pstore.InfoFromP2pAddr(p2paddr1)
if err != nil {
t.Error(err)
}
tctx, cancel := context.WithTimeout(ctx, time.Millisecond*100)
defer cancel()
_ = h.Connect(tctx, *pi)
addrs := h.Peerstore().Addrs(pi.ID)
sort.Sort(sortedMultiaddrs(addrs))
if len(addrs) != 2 || !addrs[0].Equal(addr1) || !addrs[1].Equal(addr2) {
t.Fatalf("expected [%s %s], got %+v", addr1, addr2, addrs)
}
}
type sortedMultiaddrs []ma.Multiaddr
func (sma sortedMultiaddrs) Len() int { return len(sma) }
func (sma sortedMultiaddrs) Swap(i, j int) { sma[i], sma[j] = sma[j], sma[i] }
func (sma sortedMultiaddrs) Less(i, j int) bool {
return bytes.Compare(sma[i].Bytes(), sma[j].Bytes()) == 1
}
......@@ -274,6 +274,12 @@
"hash": "QmYv8PxtikjeL6AfsheUiJfoFRzKvx8hdwRf4odBWH7mQx",
"name": "go-libp2p-circuit",
"version": "1.1.3"
},
{
"author": "lgierth",
"hash": "QmS7xUmsTdVNU2t1bPV6o9aXuXfufAjNGYgh2bcN2z9DAs",
"name": "go-multiaddr-dns",
"version": "0.2.0"
}
],
"gxVersion": "0.4.0",
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment