func filterAddrs(listenAddrs []ma.Multiaddr) ([]ma.Multiaddr, error) { if len(listenAddrs) > 0 { filtered := addrutil.FilterUsableAddrs(listenAddrs) if len(filtered) < 1 { return nil, fmt.Errorf("swarm cannot use any addr in: %s", listenAddrs) } listenAddrs = filtered } return listenAddrs, nil }
func TestFilterAddrs(t *testing.T) { m := func(s string) ma.Multiaddr { maddr, err := ma.NewMultiaddr(s) if err != nil { t.Fatal(err) } return maddr } bad := []ma.Multiaddr{ m("/ip4/1.2.3.4/udp/1234"), // unreliable m("/ip4/1.2.3.4/udp/1234/sctp/1234"), // not in manet m("/ip4/1.2.3.4/udp/1234/utp"), // utp is broken m("/ip4/1.2.3.4/udp/1234/udt"), // udt is broken on arm m("/ip6/fe80::1/tcp/0"), // link local m("/ip6/fe80::100/tcp/1234"), // link local } good := []ma.Multiaddr{ m("/ip4/127.0.0.1/tcp/0"), m("/ip6/::1/tcp/0"), } goodAndBad := append(good, bad...) // test filters for _, a := range bad { if addrutil.AddrUsable(a, true) { t.Errorf("addr %s should be unusable", a) } } for _, a := range good { if !addrutil.AddrUsable(a, true) { t.Errorf("addr %s should be usable", a) } } subtestAddrsEqual(t, addrutil.FilterUsableAddrs(bad), []ma.Multiaddr{}) subtestAddrsEqual(t, addrutil.FilterUsableAddrs(good), good) subtestAddrsEqual(t, addrutil.FilterUsableAddrs(goodAndBad), good) // now test it with swarm id, err := testutil.RandPeerID() if err != nil { t.Fatal(err) } ps := peer.NewPeerstore() ctx := context.Background() if _, err := NewNetwork(ctx, bad, id, ps, metrics.NewBandwidthCounter()); err == nil { t.Fatal("should have failed to create swarm") } if _, err := NewNetwork(ctx, goodAndBad, id, ps, metrics.NewBandwidthCounter()); err != nil { t.Fatal("should have succeeded in creating swarm", err) } }
// dial is the actual swarm's dial logic, gated by Dial. func (s *Swarm) dial(ctx context.Context, p peer.ID) (*Conn, error) { var logdial = lgbl.Dial("swarm", s.LocalPeer(), p, nil, nil) if p == s.local { log.Event(ctx, "swarmDialDoDialSelf", logdial) return nil, ErrDialToSelf } defer log.EventBegin(ctx, "swarmDialDo", logdial).Done() logdial["dial"] = "failure" // start off with failure. set to "success" at the end. sk := s.peers.PrivKey(s.local) logdial["encrypted"] = (sk != nil) // log wether this will be an encrypted dial or not. if sk == nil { // fine for sk to be nil, just log. log.Debug("Dial not given PrivateKey, so WILL NOT SECURE conn.") } // get our own addrs. try dialing out from our listener addresses (reusing ports) // Note that using our peerstore's addresses here is incorrect, as that would // include observed addresses. TODO: make peerstore's address book smarter. localAddrs := s.ListenAddresses() if len(localAddrs) == 0 { log.Debug("Dialing out with no local addresses.") } // get remote peer addrs remoteAddrs := s.peers.Addrs(p) // make sure we can use the addresses. remoteAddrs = addrutil.FilterUsableAddrs(remoteAddrs) // drop out any addrs that would just dial ourselves. use ListenAddresses // as that is a more authoritative view than localAddrs. ila, _ := s.InterfaceListenAddresses() remoteAddrs = addrutil.Subtract(remoteAddrs, ila) remoteAddrs = addrutil.Subtract(remoteAddrs, s.peers.Addrs(s.local)) log.Debugf("%s swarm dialing %s -- local:%s remote:%s", s.local, p, s.ListenAddresses(), remoteAddrs) if len(remoteAddrs) == 0 { err := errors.New("peer has no addresses") logdial["error"] = err return nil, err } remoteAddrs = s.filterAddrs(remoteAddrs) if len(remoteAddrs) == 0 { err := errors.New("all adresses for peer have been filtered out") logdial["error"] = err return nil, err } // open connection to peer d := &conn.Dialer{ Dialer: manet.Dialer{ Dialer: net.Dialer{ Timeout: s.dialT, }, }, LocalPeer: s.local, LocalAddrs: localAddrs, PrivateKey: sk, Wrapper: func(c manet.Conn) manet.Conn { return mconn.WrapConn(s.bwc, c) }, } // try to get a connection to any addr connC, err := s.dialAddrs(ctx, d, p, remoteAddrs) if err != nil { logdial["error"] = err return nil, err } logdial["netconn"] = lgbl.NetConn(connC) // ok try to setup the new connection. defer log.EventBegin(ctx, "swarmDialDoSetup", logdial, lgbl.NetConn(connC)).Done() swarmC, err := dialConnSetup(ctx, s, connC) if err != nil { logdial["error"] = err connC.Close() // close the connection. didn't work out :( return nil, err } logdial["dial"] = "success" return swarmC, nil }