dial.go 5.45 KB
Newer Older
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
1
2
3
4
package conn

import (
	"fmt"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
5
	"math/rand"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
6
7
	"strings"

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
8
	addrutil "github.com/ipfs/go-libp2p/p2p/net/swarm/addr"
9
10
11
	transport "gx/ipfs/QmRHqYZs3Diy8YC3bW16zvs8VDDwS2ARKBuKwALxEMqibc/go-libp2p-transport"
	manet "gx/ipfs/QmTrxSBY8Wqd5aBB4MeizeSzS5xFbK8dQBrYaMsiGnCBhb/go-multiaddr-net"
	ci "gx/ipfs/QmUEUu1CM8bxBJxc3ZLojAi8evhTr4byQogWstABet79oY/go-libp2p-crypto"
12
	msmux "gx/ipfs/QmUeEcYJrzAEKdQXjzTxCgNZgc9sRuwharsvzzm5Gd2oGB/go-multistream"
13
14
	lgbl "gx/ipfs/QmYqiDbGUGqatbPFie11Py8cnyduoJYqcgKtjfhu1SQLh1/go-libp2p-loggables"
	peer "gx/ipfs/QmZwZjMVGss5rqYsJVGy18gNbkTJffFyq2x1uJ4e4p3ZAt/go-libp2p-peer"
Jeromy's avatar
Jeromy committed
15
16
	context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context"
	ma "gx/ipfs/QmcobAGsCjYt5DXoq9et9L8yR8er7o7Cu3DTvpaq12jYSz/go-multiaddr"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
17
18
)

Jeromy's avatar
Jeromy committed
19
20
21
22
23
24
25
type WrapFunc func(transport.Conn) transport.Conn

func NewDialer(p peer.ID, pk ci.PrivKey, wrap WrapFunc) *Dialer {
	return &Dialer{
		LocalPeer:  p,
		PrivateKey: pk,
		Wrapper:    wrap,
Jeromy's avatar
Jeromy committed
26
		fallback:   new(transport.FallbackDialer),
Jeromy's avatar
Jeromy committed
27
28
29
	}
}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
30
31
// String returns the string rep of d.
func (d *Dialer) String() string {
Jeromy's avatar
Jeromy committed
32
	return fmt.Sprintf("<Dialer %s ...>", d.LocalPeer)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
33
34
35
36
37
38
}

// 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) {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
39
	logdial := lgbl.Dial("conn", d.LocalPeer, remote, nil, raddr)
40
	logdial["encrypted"] = (d.PrivateKey != nil) // log wether this will be an encrypted dial or not.
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
41
	defer log.EventBegin(ctx, "connDial", logdial).Done()
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
42
43
44
45
46
47
48

	var connOut Conn
	var errOut error
	done := make(chan struct{})

	// do it async to ensure we respect don contexteone
	go func() {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
49
50
51
52
53
54
		defer func() {
			select {
			case done <- struct{}{}:
			case <-ctx.Done():
			}
		}()
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
55

56
57
58
59
60
61
		maconn, err := d.rawConnDial(ctx, raddr, remote)
		if err != nil {
			errOut = err
			return
		}

Jeromy's avatar
Jeromy committed
62
63
64
65
		if d.Wrapper != nil {
			maconn = d.Wrapper(maconn)
		}

66
67
68
69
70
71
		cryptoProtoChoice := SecioTag
		if !EncryptConnections {
			cryptoProtoChoice = NoEncryptionTag
		}

		err = msmux.SelectProtoOrFail(cryptoProtoChoice, maconn)
72
73
74
75
76
		if err != nil {
			errOut = err
			return
		}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
77
78
		c, err := newSingleConn(ctx, d.LocalPeer, remote, maconn)
		if err != nil {
79
			maconn.Close()
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
80
81
82
83
			errOut = err
			return
		}

84
		if d.PrivateKey == nil || EncryptConnections == false {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
85
86
87
88
			log.Warning("dialer %s dialing INSECURELY %s at %s!", d, remote, raddr)
			connOut = c
			return
		}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
89

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
90
91
92
93
94
95
96
97
98
99
100
101
		c2, err := newSecureConn(ctx, d.PrivateKey, c)
		if err != nil {
			errOut = err
			c.Close()
			return
		}

		connOut = c2
	}()

	select {
	case <-ctx.Done():
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
102
		logdial["error"] = ctx.Err()
103
		logdial["dial"] = "failure"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
104
105
106
107
108
		return nil, ctx.Err()
	case <-done:
		// whew, finished.
	}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
109
110
111
112
113
114
115
116
	if errOut != nil {
		logdial["error"] = errOut
		logdial["dial"] = "failure"
		return nil, errOut
	}

	logdial["dial"] = "success"
	return connOut, nil
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
117
118
}

Jeromy's avatar
Jeromy committed
119
120
func (d *Dialer) AddDialer(pd transport.Dialer) {
	d.Dialers = append(d.Dialers, pd)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
121
122
}

Jeromy's avatar
Jeromy committed
123
124
125
126
127
128
// returns dialer that can dial the given address
func (d *Dialer) subDialerForAddr(raddr ma.Multiaddr) transport.Dialer {
	for _, pd := range d.Dialers {
		if pd.Matches(raddr) {
			return pd
		}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
129
130
	}

Jeromy's avatar
Jeromy committed
131
132
133
134
	if d.fallback.Matches(raddr) {
		return d.fallback
	}

Jeromy's avatar
Jeromy committed
135
	return nil
136
137
}

Jeromy's avatar
Jeromy committed
138
139
140
141
142
// rawConnDial dials the underlying net.Conn + manet.Conns
func (d *Dialer) rawConnDial(ctx context.Context, raddr ma.Multiaddr, remote peer.ID) (transport.Conn, error) {
	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)
143
144
	}

Jeromy's avatar
Jeromy committed
145
146
147
	sd := d.subDialerForAddr(raddr)
	if sd == nil {
		return nil, fmt.Errorf("no dialer for %s", raddr)
148
149
	}

Jeromy's avatar
Jeromy committed
150
	return sd.Dial(raddr)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
151
152
153
154
155
156
157
}

func pickLocalAddr(laddrs []ma.Multiaddr, raddr ma.Multiaddr) (laddr ma.Multiaddr) {
	if len(laddrs) < 1 {
		return nil
	}

158
	// make sure that we ONLY use local addrs that match the remote addr.
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
159
160
161
162
163
	laddrs = manet.AddrMatch(raddr, laddrs)
	if len(laddrs) < 1 {
		return nil
	}

164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
	// make sure that we ONLY use local addrs that CAN dial the remote addr.
	// filter out all the local addrs that aren't capable
	raddrIPLayer := ma.Split(raddr)[0]
	raddrIsLoopback := manet.IsIPLoopback(raddrIPLayer)
	raddrIsLinkLocal := manet.IsIP6LinkLocal(raddrIPLayer)
	laddrs = addrutil.FilterAddrs(laddrs, func(a ma.Multiaddr) bool {
		laddrIPLayer := ma.Split(a)[0]
		laddrIsLoopback := manet.IsIPLoopback(laddrIPLayer)
		laddrIsLinkLocal := manet.IsIP6LinkLocal(laddrIPLayer)
		if laddrIsLoopback { // our loopback addrs can only dial loopbacks.
			return raddrIsLoopback
		}
		if laddrIsLinkLocal {
			return raddrIsLinkLocal // out linklocal addrs can only dial link locals.
		}
		return true
	})

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
182
183
184
185
186
187
	// TODO pick with a good heuristic
	// we use a random one for now to prevent bad addresses from making nodes unreachable
	// with a random selection, multiple tries may work.
	return laddrs[rand.Intn(len(laddrs))]
}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
188
189
190
191
192
193
194
195
196
197
// MultiaddrProtocolsMatch returns whether two multiaddrs match in protocol stacks.
func MultiaddrProtocolsMatch(a, b ma.Multiaddr) bool {
	ap := a.Protocols()
	bp := b.Protocols()

	if len(ap) != len(bp) {
		return false
	}

	for i, api := range ap {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
198
		if api.Code != bp[i].Code {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
			return false
		}
	}

	return true
}

// MultiaddrNetMatch returns the first Multiaddr found to match  network.
func MultiaddrNetMatch(tgt ma.Multiaddr, srcs []ma.Multiaddr) ma.Multiaddr {
	for _, a := range srcs {
		if MultiaddrProtocolsMatch(tgt, a) {
			return a
		}
	}
	return nil
}