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 ( ...@@ -18,6 +18,7 @@ import (
pstore "github.com/libp2p/go-libp2p-peerstore" pstore "github.com/libp2p/go-libp2p-peerstore"
protocol "github.com/libp2p/go-libp2p-protocol" protocol "github.com/libp2p/go-libp2p-protocol"
ma "github.com/multiformats/go-multiaddr" ma "github.com/multiformats/go-multiaddr"
madns "github.com/multiformats/go-multiaddr-dns"
msmux "github.com/multiformats/go-multistream" msmux "github.com/multiformats/go-multistream"
) )
...@@ -55,12 +56,13 @@ const NATPortMap Option = iota ...@@ -55,12 +56,13 @@ const NATPortMap Option = iota
// * uses an identity service to send + receive node information // * uses an identity service to send + receive node information
// * uses a nat service to establish NAT port mappings // * uses a nat service to establish NAT port mappings
type BasicHost struct { type BasicHost struct {
network inet.Network network inet.Network
mux *msmux.MultistreamMuxer mux *msmux.MultistreamMuxer
ids *identify.IDService ids *identify.IDService
natmgr NATManager natmgr NATManager
addrs AddrsFactory addrs AddrsFactory
cmgr connmgr.ConnManager maResolver *madns.Resolver
cmgr connmgr.ConnManager
negtimeout time.Duration negtimeout time.Duration
...@@ -89,11 +91,16 @@ type HostOpts struct { ...@@ -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. // If omitted, there's no override or filtering, and the results of Addrs and AllAddrs are the same.
AddrsFactory AddrsFactory 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. // NATManager takes care of setting NAT port mappings, and discovering external addresses.
// If omitted, this will simply be disabled. // If omitted, this will simply be disabled.
NATManager NATManager NATManager NATManager
// // BandwidthReporter is used for collecting aggregate metrics of the
// bandwidth used by various protocols.
BandwidthReporter metrics.Reporter BandwidthReporter metrics.Reporter
// ConnManager is a libp2p connection manager // ConnManager is a libp2p connection manager
...@@ -113,6 +120,7 @@ func NewHost(ctx context.Context, net inet.Network, opts *HostOpts) (*BasicHost, ...@@ -113,6 +120,7 @@ func NewHost(ctx context.Context, net inet.Network, opts *HostOpts) (*BasicHost,
mux: msmux.NewMultistreamMuxer(), mux: msmux.NewMultistreamMuxer(),
negtimeout: DefaultNegotiationTimeout, negtimeout: DefaultNegotiationTimeout,
addrs: DefaultAddrsFactory, addrs: DefaultAddrsFactory,
maResolver: madns.DefaultResolver,
} }
if opts.MultistreamMuxer != nil { if opts.MultistreamMuxer != nil {
...@@ -138,6 +146,10 @@ func NewHost(ctx context.Context, net inet.Network, opts *HostOpts) (*BasicHost, ...@@ -138,6 +146,10 @@ func NewHost(ctx context.Context, net inet.Network, opts *HostOpts) (*BasicHost,
h.natmgr = opts.NATManager h.natmgr = opts.NATManager
} }
if opts.MultiaddrResolver != nil {
h.maResolver = opts.MultiaddrResolver
}
if opts.BandwidthReporter != nil { if opts.BandwidthReporter != nil {
h.bwc = opts.BandwidthReporter h.bwc = opts.BandwidthReporter
h.ids.Reporter = opts.BandwidthReporter h.ids.Reporter = opts.BandwidthReporter
...@@ -197,6 +209,8 @@ func New(net inet.Network, opts ...interface{}) *BasicHost { ...@@ -197,6 +209,8 @@ func New(net inet.Network, opts ...interface{}) *BasicHost {
hostopts.AddrsFactory = AddrsFactory(o) hostopts.AddrsFactory = AddrsFactory(o)
case connmgr.ConnManager: case connmgr.ConnManager:
hostopts.ConnManager = o 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) ( ...@@ -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 // 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 // given peer.ID. If there is not an active connection, Connect will issue a
// 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.
// h.Network.Dial, and block until a connection is open, or an error is // Connect will absorb the addresses in pi into its internal peerstore.
// returned. // It will also resolve any /dns4, /dns6, and /dnsaddr addresses.
func (h *BasicHost) Connect(ctx context.Context, pi pstore.PeerInfo) error { func (h *BasicHost) Connect(ctx context.Context, pi pstore.PeerInfo) error {
// absorb addresses into peerstore // absorb addresses into peerstore
h.Peerstore().AddAddrs(pi.ID, pi.Addrs, pstore.TempAddrTTL) h.Peerstore().AddAddrs(pi.ID, pi.Addrs, pstore.TempAddrTTL)
...@@ -420,9 +433,46 @@ func (h *BasicHost) Connect(ctx context.Context, pi pstore.PeerInfo) error { ...@@ -420,9 +433,46 @@ func (h *BasicHost) Connect(ctx context.Context, pi pstore.PeerInfo) error {
return nil 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) 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 // dialPeer opens a connection to peer, and makes sure to identify
// the connection once it has been opened. // the connection once it has been opened.
func (h *BasicHost) dialPeer(ctx context.Context, p peer.ID) error { func (h *BasicHost) dialPeer(ctx context.Context, p peer.ID) error {
......
...@@ -4,14 +4,17 @@ import ( ...@@ -4,14 +4,17 @@ import (
"bytes" "bytes"
"context" "context"
"io" "io"
"sort"
"testing" "testing"
"time" "time"
host "github.com/libp2p/go-libp2p-host" host "github.com/libp2p/go-libp2p-host"
inet "github.com/libp2p/go-libp2p-net" inet "github.com/libp2p/go-libp2p-net"
testutil "github.com/libp2p/go-libp2p-netutil" testutil "github.com/libp2p/go-libp2p-netutil"
pstore "github.com/libp2p/go-libp2p-peerstore"
protocol "github.com/libp2p/go-libp2p-protocol" protocol "github.com/libp2p/go-libp2p-protocol"
ma "github.com/multiformats/go-multiaddr" ma "github.com/multiformats/go-multiaddr"
madns "github.com/multiformats/go-multiaddr-dns"
) )
func TestHostSimple(t *testing.T) { func TestHostSimple(t *testing.T) {
...@@ -330,3 +333,55 @@ func TestProtoDowngrade(t *testing.T) { ...@@ -330,3 +333,55 @@ func TestProtoDowngrade(t *testing.T) {
s2.Close() 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 @@ ...@@ -274,6 +274,12 @@
"hash": "QmYv8PxtikjeL6AfsheUiJfoFRzKvx8hdwRf4odBWH7mQx", "hash": "QmYv8PxtikjeL6AfsheUiJfoFRzKvx8hdwRf4odBWH7mQx",
"name": "go-libp2p-circuit", "name": "go-libp2p-circuit",
"version": "1.1.3" "version": "1.1.3"
},
{
"author": "lgierth",
"hash": "QmS7xUmsTdVNU2t1bPV6o9aXuXfufAjNGYgh2bcN2z9DAs",
"name": "go-multiaddr-dns",
"version": "0.2.0"
} }
], ],
"gxVersion": "0.4.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