From 0c23c9684ce8a6233852c34afc815eb22229c599 Mon Sep 17 00:00:00 2001 From: Lars Gierth Date: Tue, 11 Apr 2017 10:11:41 +0200 Subject: [PATCH] Resolve /dns4, /dns6, /dnsaddr multiaddrs --- p2p/host/basic/basic_host.go | 71 +++++++++++++++++++++++++------ p2p/host/basic/basic_host_test.go | 55 ++++++++++++++++++++++++ package.json | 6 +++ 3 files changed, 120 insertions(+), 12 deletions(-) diff --git a/p2p/host/basic/basic_host.go b/p2p/host/basic/basic_host.go index a7992d0..e340088 100644 --- a/p2p/host/basic/basic_host.go +++ b/p2p/host/basic/basic_host.go @@ -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,43 @@ 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) + + 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 { diff --git a/p2p/host/basic/basic_host_test.go b/p2p/host/basic/basic_host_test.go index 89b975a..af4a830 100644 --- a/p2p/host/basic/basic_host_test.go +++ b/p2p/host/basic/basic_host_test.go @@ -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 +} diff --git a/package.json b/package.json index e8fab7d..bdb74ca 100644 --- a/package.json +++ b/package.json @@ -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", -- GitLab