Skip to content

Commit

Permalink
Add Resolver interface to transport
Browse files Browse the repository at this point in the history
  • Loading branch information
MarcoPolo committed Aug 25, 2022
1 parent 8df365b commit 5bce716
Show file tree
Hide file tree
Showing 9 changed files with 246 additions and 81 deletions.
16 changes: 10 additions & 6 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,9 @@ func (cfg *Config) makeSwarm() (*swarm.Swarm, error) {
if cfg.ResourceManager != nil {
opts = append(opts, swarm.WithResourceManager(cfg.ResourceManager))
}
if cfg.MultiaddrResolver != nil {
opts = append(opts, swarm.WithMultiaddrResolver(cfg.MultiaddrResolver))
}
// TODO: Make the swarm implementation configurable.
return swarm.NewSwarm(pid, cfg.Peerstore, opts...)
}
Expand Down Expand Up @@ -218,12 +221,13 @@ func (cfg *Config) NewNode() (host.Host, error) {
}

h, err := bhost.NewHost(swrm, &bhost.HostOpts{
ConnManager: cfg.ConnManager,
AddrsFactory: cfg.AddrsFactory,
NATManager: cfg.NATManager,
EnablePing: !cfg.DisablePing,
UserAgent: cfg.UserAgent,
MultiaddrResolver: cfg.MultiaddrResolver,
ConnManager: cfg.ConnManager,
AddrsFactory: cfg.AddrsFactory,
NATManager: cfg.NATManager,
EnablePing: !cfg.DisablePing,
UserAgent: cfg.UserAgent,
// TODO
// MultiaddrResolver: cfg.MultiaddrResolver,
EnableHolePunching: cfg.EnableHolePunching,
HolePunchingOptions: cfg.HolePunchingOptions,
EnableRelayService: cfg.EnableRelayService,
Expand Down
6 changes: 6 additions & 0 deletions core/transport/transport.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,12 @@ type Transport interface {
Proxy() bool
}

// Resolver can be optionally implemented by transports that want to resolve or transform the
// multiaddr.
type Resolver interface {
Resolve(ctx context.Context, maddr ma.Multiaddr) ([]ma.Multiaddr, error)
}

// Listener is an interface closely resembling the net.Listener interface. The
// only real difference is that Accept() returns Conn's of the type in this
// package, and also exposes a Multiaddr method as opposed to a regular Addr
Expand Down
77 changes: 6 additions & 71 deletions p2p/host/basic/basic_host.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,6 @@ import (
msmux "github.com/multiformats/go-multistream"
)

// The maximum number of address resolution steps we'll perform for a single
// peer (for all addresses).
const maxAddressResolution = 32

// addrChangeTickrInterval is the interval between two address change ticks.
var addrChangeTickrInterval = 5 * time.Second

Expand Down Expand Up @@ -701,77 +697,16 @@ func (h *BasicHost) Connect(ctx context.Context, pi peer.AddrInfo) error {
}
}

resolved, err := h.resolveAddrs(ctx, h.Peerstore().PeerInfo(pi.ID))
if err != nil {
return err
}
h.Peerstore().AddAddrs(pi.ID, resolved, peerstore.TempAddrTTL)
// TODO remove this
// resolved, err := h.resolveAddrs(ctx, h.Peerstore().PeerInfo(pi.ID))
// if err != nil {
// return err
// }
// h.Peerstore().AddAddrs(pi.ID, resolved, peerstore.TempAddrTTL)

return h.dialPeer(ctx, pi.ID)
}

func (h *BasicHost) resolveAddrs(ctx context.Context, pi peer.AddrInfo) ([]ma.Multiaddr, error) {
proto := ma.ProtocolWithCode(ma.P_P2P).Name
p2paddr, err := ma.NewMultiaddr("/" + proto + "/" + pi.ID.Pretty())
if err != nil {
return nil, err
}

resolveSteps := 0

// Recursively resolve all addrs.
//
// While the toResolve list is non-empty:
// * Pop an address off.
// * If the address is fully resolved, add it to the resolved list.
// * Otherwise, resolve it and add the results to the "to resolve" list.
toResolve := append(([]ma.Multiaddr)(nil), pi.Addrs...)
resolved := make([]ma.Multiaddr, 0, len(pi.Addrs))
for len(toResolve) > 0 {
// pop the last addr off.
addr := toResolve[len(toResolve)-1]
toResolve = toResolve[:len(toResolve)-1]

// if it's resolved, add it to the resolved list.
if !madns.Matches(addr) {
resolved = append(resolved, addr)
continue
}

resolveSteps++

// We've resolved too many addresses. We can keep all the fully
// resolved addresses but we'll need to skip the rest.
if resolveSteps >= maxAddressResolution {
log.Warnf(
"peer %s asked us to resolve too many addresses: %s/%s",
pi.ID,
resolveSteps,
maxAddressResolution,
)
continue
}

// otherwise, resolve it
reqaddr := addr.Encapsulate(p2paddr)
resaddrs, err := h.maResolver.Resolve(ctx, reqaddr)
if err != nil {
log.Infof("error resolving %s: %s", reqaddr, err)
}

// add the results to the toResolve list.
for _, res := range resaddrs {
pi, err := peer.AddrInfoFromP2pAddr(res)
if err != nil {
log.Infof("error parsing %s: %s", res, err)
}
toResolve = append(toResolve, pi.Addrs...)
}
}

return resolved, 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 {
Expand Down
40 changes: 40 additions & 0 deletions p2p/net/swarm/dial_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@ import (
"github.com/libp2p/go-libp2p/core/peer"
"github.com/libp2p/go-libp2p/core/peerstore"
testutil "github.com/libp2p/go-libp2p/core/test"
"github.com/libp2p/go-libp2p/p2p/net/swarm"
. "github.com/libp2p/go-libp2p/p2p/net/swarm"
swarmt "github.com/libp2p/go-libp2p/p2p/net/swarm/testing"

"github.com/libp2p/go-libp2p-testing/ci"

ma "github.com/multiformats/go-multiaddr"
madns "github.com/multiformats/go-multiaddr-dns"
manet "github.com/multiformats/go-multiaddr/net"
"github.com/stretchr/testify/require"
)
Expand Down Expand Up @@ -45,6 +47,44 @@ func TestBasicDialPeer(t *testing.T) {
s.Close()
}

func TestBasicDialPeerWithResolver(t *testing.T) {
t.Parallel()

mockResolver := madns.MockResolver{IP: make(map[string][]net.IPAddr)}
ipaddr, err := net.ResolveIPAddr("ip4", "127.0.0.1")
require.NoError(t, err)
mockResolver.IP["example.com"] = []net.IPAddr{*ipaddr}
resolver, err := madns.NewResolver(madns.WithDomainResolver("example.com", &mockResolver))
require.NoError(t, err)

swarms := makeSwarms(t, 2, swarmt.WithSwarmOpts([]swarm.Option{swarm.WithMultiaddrResolver(resolver)}))
defer closeSwarms(swarms)
s1 := swarms[0]
s2 := swarms[1]

// Change the multiaddr from /ip4/127.0.0.1/... to /dns4/example.com/... so
// that the resovler has to resolve this
var s2Addrs []ma.Multiaddr
for _, a := range s2.ListenAddresses() {
_, rest := ma.SplitFunc(a, func(c ma.Component) bool {
return c.Protocol().Code == ma.P_TCP || c.Protocol().Code == ma.P_UDP
},
)
if rest != nil {
s2Addrs = append(s2Addrs, ma.StringCast("/dns4/example.com").Encapsulate(rest))
}
}

s1.Peerstore().AddAddrs(s2.LocalPeer(), s2Addrs, peerstore.PermanentAddrTTL)

c, err := s1.DialPeer(context.Background(), s2.LocalPeer())
require.NoError(t, err)

s, err := c.NewStream(context.Background())
require.NoError(t, err)
s.Close()
}

func TestDialWithNoListeners(t *testing.T) {
t.Parallel()

Expand Down
12 changes: 12 additions & 0 deletions p2p/net/swarm/swarm.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (

logging "github.com/ipfs/go-log/v2"
ma "github.com/multiformats/go-multiaddr"
madns "github.com/multiformats/go-multiaddr-dns"
)

const (
Expand Down Expand Up @@ -54,6 +55,14 @@ func WithConnectionGater(gater connmgr.ConnectionGater) Option {
}
}

// WithMultiaddrResolver sets a custom multiaddr.Resolver
func WithMultiaddrResolver(maResolver *madns.Resolver) Option {
return func(s *Swarm) error {
s.maResolver = maResolver
return nil
}
}

// WithMetrics sets a metrics reporter
func WithMetrics(reporter metrics.Reporter) Option {
return func(s *Swarm) error {
Expand Down Expand Up @@ -127,6 +136,8 @@ type Swarm struct {
m map[int]transport.Transport
}

maResolver *madns.Resolver

// stream handlers
streamh atomic.Value

Expand All @@ -153,6 +164,7 @@ func NewSwarm(local peer.ID, peers peerstore.Peerstore, opts ...Option) (*Swarm,
ctxCancel: cancel,
dialTimeout: defaultDialTimeout,
dialTimeoutLocal: defaultDialTimeoutLocal,
maResolver: madns.DefaultResolver,
}

s.conns.m = make(map[peer.ID][]*Conn)
Expand Down
99 changes: 97 additions & 2 deletions p2p/net/swarm/swarm_dial.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,14 @@ import (
"github.com/libp2p/go-libp2p/core/transport"

ma "github.com/multiformats/go-multiaddr"
madns "github.com/multiformats/go-multiaddr-dns"
manet "github.com/multiformats/go-multiaddr/net"
)

// The maximum number of address resolution steps we'll perform for a single
// peer (for all addresses).
const maxAddressResolution = 32

// Diagram of dial sync:
//
// many callers of Dial() synched w. dials many addrs results to callers
Expand Down Expand Up @@ -292,7 +297,32 @@ func (s *Swarm) addrsForDial(ctx context.Context, p peer.ID) ([]ma.Multiaddr, er
return nil, ErrNoAddresses
}

goodAddrs := s.filterKnownUndialables(p, peerAddrs)
peerAddrsAfterTransportResolved := make([]ma.Multiaddr, 0, len(peerAddrs))
for _, a := range peerAddrs {
tpt := s.TransportForDialing(a)
resolver, ok := tpt.(transport.Resolver)
if ok {
resolvedAddrs, err := resolver.Resolve(ctx, a)
if err != nil {
log.Warnf("Failed to resolve multiaddr %s by transport %v: %v", a, tpt, err)
continue
}
peerAddrsAfterTransportResolved = append(peerAddrsAfterTransportResolved, resolvedAddrs...)
} else {
peerAddrsAfterTransportResolved = append(peerAddrsAfterTransportResolved, a)
}
}

// Resolve dns or dnsaddrs
resolved, err := s.resolveAddrs(ctx, peer.AddrInfo{
ID: p,
Addrs: peerAddrsAfterTransportResolved,
})
if err != nil {
return nil, err
}

goodAddrs := s.filterKnownUndialables(p, resolved)
if forceDirect, _ := network.GetForceDirectDial(ctx); forceDirect {
goodAddrs = ma.FilterAddrs(goodAddrs, s.nonProxyAddr)
}
Expand All @@ -301,7 +331,72 @@ func (s *Swarm) addrsForDial(ctx context.Context, p peer.ID) ([]ma.Multiaddr, er
return nil, ErrNoGoodAddresses
}

return goodAddrs, nil
// Do we store the addrs?
// h.Peerstore().AddAddrs(pi.ID, goodAddrs, peerstore.TempAddrTTL)

return resolved, nil
}

func (s *Swarm) resolveAddrs(ctx context.Context, pi peer.AddrInfo) ([]ma.Multiaddr, error) {
proto := ma.ProtocolWithCode(ma.P_P2P).Name
p2paddr, err := ma.NewMultiaddr("/" + proto + "/" + pi.ID.Pretty())
if err != nil {
return nil, err
}

resolveSteps := 0

// Recursively resolve all addrs.
//
// While the toResolve list is non-empty:
// * Pop an address off.
// * If the address is fully resolved, add it to the resolved list.
// * Otherwise, resolve it and add the results to the "to resolve" list.
toResolve := append(([]ma.Multiaddr)(nil), pi.Addrs...)
resolved := make([]ma.Multiaddr, 0, len(pi.Addrs))
for len(toResolve) > 0 {
// pop the last addr off.
addr := toResolve[len(toResolve)-1]
toResolve = toResolve[:len(toResolve)-1]

// if it's resolved, add it to the resolved list.
if !madns.Matches(addr) {
resolved = append(resolved, addr)
continue
}

resolveSteps++

// We've resolved too many addresses. We can keep all the fully
// resolved addresses but we'll need to skip the rest.
if resolveSteps >= maxAddressResolution {
log.Warnf(
"peer %s asked us to resolve too many addresses: %s/%s",
pi.ID,
resolveSteps,
maxAddressResolution,
)
continue
}

// otherwise, resolve it
reqaddr := addr.Encapsulate(p2paddr)
resaddrs, err := s.maResolver.Resolve(ctx, reqaddr)
if err != nil {
log.Infof("error resolving %s: %s", reqaddr, err)
}

// add the results to the toResolve list.
for _, res := range resaddrs {
pi, err := peer.AddrInfoFromP2pAddr(res)
if err != nil {
log.Infof("error parsing %s: %s", res, err)
}
toResolve = append(toResolve, pi.Addrs...)
}
}

return resolved, nil
}

func (s *Swarm) dialNextAddr(ctx context.Context, p peer.ID, addr ma.Multiaddr, resch chan dialResult) error {
Expand Down
Loading

0 comments on commit 5bce716

Please sign in to comment.