// rawConnDial dials the underlying net.Conn + manet.Conns func (d *Dialer) rawConnDial(ctx context.Context, raddr ma.Multiaddr, remote peer.ID) (manet.Conn, error) { // before doing anything, check we're going to be able to dial. // we may not support the given address. if _, _, err := manet.DialArgs(raddr); err != nil { return nil, err } if strings.HasPrefix(raddr.String(), "/ip4/0.0.0.0") { log.Event(ctx, "connDialZeroAddr", lgbl.Dial("conn", d.LocalPeer, remote, nil, raddr)) return nil, fmt.Errorf("Attempted to connect to zero address: %s", raddr) } // get local addr to use. laddr := pickLocalAddr(d.LocalAddrs, raddr) logdial := lgbl.Dial("conn", d.LocalPeer, remote, laddr, raddr) defer log.EventBegin(ctx, "connDialRawConn", logdial).Done() // make a copy of the manet.Dialer, we may need to change its timeout. madialer := d.Dialer if laddr != nil && reuseportIsAvailable() { // we're perhaps going to dial twice. half the timeout, so we can afford to. // otherwise our context would expire right after the first dial. madialer.Dialer.Timeout = (madialer.Dialer.Timeout / 2) // dial using reuseport.Dialer, because we're probably reusing addrs. // this is optimistic, as the reuseDial may fail to bind the port. rpev := log.EventBegin(ctx, "connDialReusePort", logdial) if nconn, retry, reuseErr := reuseDial(madialer.Dialer, laddr, raddr); reuseErr == nil { // if it worked, wrap the raw net.Conn with our manet.Conn logdial["reuseport"] = "success" rpev.Done() return manet.WrapNetConn(nconn) } else if !retry { // reuseDial is sure this is a legitimate dial failure, not a reuseport failure. logdial["reuseport"] = "failure" logdial["error"] = reuseErr rpev.Done() return nil, reuseErr } else { // this is a failure to reuse port. log it. logdial["reuseport"] = "retry" logdial["error"] = reuseErr rpev.Done() } } defer log.EventBegin(ctx, "connDialManet", logdial).Done() return madialer.Dial(raddr) }
// Dial connects to a peer. // // The idea is that the client of Swarm does not need to know what network // the connection will happen over. Swarm can use whichever it choses. // This allows us to use various transport protocols, do NAT traversal/relay, // etc. to achive connection. 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, "swarmDialSelf", logdial) return nil, ErrDialToSelf } return s.gatedDialAttempt(ctx, p) }
// gatedDialAttempt is an attempt to dial a node. It is gated by the swarm's // dial synchronization systems: dialsync and dialbackoff. func (s *Swarm) gatedDialAttempt(ctx context.Context, p peer.ID) (*Conn, error) { var logdial = lgbl.Dial("swarm", s.LocalPeer(), p, nil, nil) defer log.EventBegin(ctx, "swarmDialAttemptSync", logdial).Done() // check if we already have an open connection first conn := s.bestConnectionToPeer(p) if conn != nil { return conn, nil } // check if there's an ongoing dial to this peer if ok, wait := s.dsync.Lock(p); ok { // ok, we have been charged to dial! let's do it. // if it succeeds, dial will add the conn to the swarm itself. defer log.EventBegin(ctx, "swarmDialAttemptStart", logdial).Done() ctxT, cancel := context.WithTimeout(ctx, s.dialT) conn, err := s.dial(ctxT, p) cancel() s.dsync.Unlock(p) log.Debugf("dial end %s", conn) if err != nil { log.Event(ctx, "swarmDialBackoffAdd", logdial) s.backf.AddBackoff(p) // let others know to backoff // ok, we failed. try again. (if loop is done, our error is output) return nil, fmt.Errorf("dial attempt failed: %s", err) } log.Event(ctx, "swarmDialBackoffClear", logdial) s.backf.Clear(p) // okay, no longer need to backoff return conn, nil } else { // we did not dial. we must wait for someone else to dial. // check whether we should backoff first... if s.backf.Backoff(p) { log.Event(ctx, "swarmDialBackoff", logdial) return nil, ErrDialBackoff } defer log.EventBegin(ctx, "swarmDialWait", logdial).Done() select { case <-wait: // wait for that other dial to finish. // see if it worked, OR we got an incoming dial in the meantime... conn := s.bestConnectionToPeer(p) if conn != nil { return conn, nil } return nil, ErrDialFailed case <-ctx.Done(): // or we may have to bail... return nil, ctx.Err() } } }
// newConn constructs a new connection func newSingleConn(ctx context.Context, local, remote peer.ID, maconn manet.Conn) (Conn, error) { ml := lgbl.Dial("conn", local, remote, maconn.LocalMultiaddr(), maconn.RemoteMultiaddr()) conn := &singleConn{ local: local, remote: remote, maconn: maconn, msgrw: msgio.NewReadWriter(maconn), event: log.EventBegin(ctx, "connLifetime", ml), } log.Debugf("newSingleConn %p: %v to %v", conn, local, remote) return conn, nil }
// 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 }
// Dial connects to a peer over a particular address // Ensures raddr is part of peer.Addresses() // Example: d.DialAddr(ctx, peer.Addresses()[0], peer) func (d *Dialer) Dial(ctx context.Context, raddr ma.Multiaddr, remote peer.ID) (Conn, error) { logdial := lgbl.Dial("conn", d.LocalPeer, remote, nil, raddr) logdial["encrypted"] = (d.PrivateKey != nil) // log wether this will be an encrypted dial or not. defer log.EventBegin(ctx, "connDial", logdial).Done() var connOut Conn var errOut error done := make(chan struct{}) // do it async to ensure we respect don contexteone go func() { defer func() { select { case done <- struct{}{}: case <-ctx.Done(): } }() maconn, err := d.rawConnDial(ctx, raddr, remote) if err != nil { errOut = err return } if d.Wrapper != nil { maconn = d.Wrapper(maconn) } c, err := newSingleConn(ctx, d.LocalPeer, remote, maconn) if err != nil { maconn.Close() errOut = err return } if d.PrivateKey == nil || EncryptConnections == false { log.Warning("dialer %s dialing INSECURELY %s at %s!", d, remote, raddr) connOut = c return } c2, err := newSecureConn(ctx, d.PrivateKey, c) if err != nil { errOut = err c.Close() return } connOut = c2 }() select { case <-ctx.Done(): logdial["error"] = ctx.Err() logdial["dial"] = "failure" return nil, ctx.Err() case <-done: // whew, finished. } if errOut != nil { logdial["error"] = errOut logdial["dial"] = "failure" return nil, errOut } logdial["dial"] = "success" return connOut, nil }