swarm_dial.go 3.06 KB
Newer Older
1 2 3 4 5 6
package swarm

import (
	"errors"
	"fmt"

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
7
	conn "github.com/jbenet/go-ipfs/p2p/net/conn"
8
	addrutil "github.com/jbenet/go-ipfs/p2p/net/swarm/addr"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
9
	peer "github.com/jbenet/go-ipfs/p2p/peer"
10 11 12 13 14 15 16 17 18 19 20
	lgbl "github.com/jbenet/go-ipfs/util/eventlog/loggables"

	context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
)

// 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.
21
func (s *Swarm) Dial(ctx context.Context, p peer.ID) (*Conn, error) {
22

23
	if p == s.local {
24 25 26 27 28 29 30 31 32 33 34
		return nil, errors.New("Attempted connection to self!")
	}

	// check if we already have an open connection first
	cs := s.ConnectionsToPeer(p)
	for _, c := range cs {
		if c != nil { // dump out the first one we find
			return c, nil
		}
	}

35 36 37 38
	sk := s.peers.PrivKey(s.local)
	if sk == nil {
		// may be fine for sk to be nil, just log a warning.
		log.Warning("Dial not given PrivateKey, so WILL NOT SECURE conn.")
39 40
	}

41
	remoteAddrs := s.peers.Addresses(p)
42 43
	// make sure we can use the addresses.
	remoteAddrs = addrutil.FilterAddrs(remoteAddrs)
44 45 46 47 48 49
	if len(remoteAddrs) == 0 {
		return nil, errors.New("peer has no addresses")
	}
	localAddrs := s.peers.Addresses(s.local)
	if len(localAddrs) == 0 {
		log.Debug("Dialing out with no local addresses.")
50 51
	}

52 53 54 55 56
	// open connection to peer
	d := &conn.Dialer{
		LocalPeer:  s.local,
		LocalAddrs: localAddrs,
		PrivateKey: sk,
57 58 59 60 61 62
	}

	// try to connect to one of the peer's known addresses.
	// for simplicity, we do this sequentially.
	// A future commit will do this asynchronously.
	var connC conn.Conn
63 64 65
	var err error
	for _, addr := range remoteAddrs {
		connC, err = d.Dial(ctx, addr, p)
66 67 68 69 70 71 72
		if err == nil {
			break
		}
	}
	if err != nil {
		return nil, err
	}
73 74 75
	if connC == nil {
		err = fmt.Errorf("failed to dial %s", p)
	}
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91

	// ok try to setup the new connection.
	swarmC, err := dialConnSetup(ctx, s, connC)
	if err != nil {
		log.Error("Dial newConnSetup failed. disconnecting.")
		log.Event(ctx, "dialFailureDisconnect", lgbl.NetConn(connC), lgbl.Error(err))
		swarmC.Close() // close the connection. didn't work out :(
		return nil, err
	}

	log.Event(ctx, "dial", p)
	return swarmC, nil
}

// dialConnSetup is the setup logic for a connection from the dial side. it
// needs to add the Conn to the StreamSwarm, then run newConnSetup
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
92
func dialConnSetup(ctx context.Context, s *Swarm, connC conn.Conn) (*Conn, error) {
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110

	psC, err := s.swarm.AddConn(connC)
	if err != nil {
		// connC is closed by caller if we fail.
		return nil, fmt.Errorf("failed to add conn to ps.Swarm: %s", err)
	}

	// ok try to setup the new connection. (newConnSetup will add to group)
	swarmC, err := s.newConnSetup(ctx, psC)
	if err != nil {
		log.Error("Dial newConnSetup failed. disconnecting.")
		log.Event(ctx, "dialFailureDisconnect", lgbl.NetConn(connC), lgbl.Error(err))
		swarmC.Close() // we need to call this to make sure psC is Closed.
		return nil, err
	}

	return swarmC, err
}