swarm_dial.go 15.3 KB
Newer Older
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
1 2 3
package swarm

import (
4
	"context"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
5 6 7 8 9
	"errors"
	"fmt"
	"sync"
	"time"

10 11 12 13 14
	"github.com/libp2p/go-libp2p-core/network"
	"github.com/libp2p/go-libp2p-core/peer"
	"github.com/libp2p/go-libp2p-core/transport"
	lgbl "github.com/libp2p/go-libp2p-loggables"

Steven Allen's avatar
Steven Allen committed
15
	logging "github.com/ipfs/go-log"
Jeromy's avatar
Jeromy committed
16 17
	addrutil "github.com/libp2p/go-addr-util"
	ma "github.com/multiformats/go-multiaddr"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
18 19 20 21 22 23 24 25 26 27 28 29 30 31
)

// Diagram of dial sync:
//
//   many callers of Dial()   synched w.  dials many addrs       results to callers
//  ----------------------\    dialsync    use earliest            /--------------
//  -----------------------\              |----------\           /----------------
//  ------------------------>------------<-------     >---------<-----------------
//  -----------------------|              \----x                 \----------------
//  ----------------------|                \-----x                \---------------
//                                         any may fail          if no addr at end
//                                                             retry dialAttempt x

var (
32 33
	// ErrDialBackoff is returned by the backoff code when a given peer has
	// been dialed too frequently
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
34
	ErrDialBackoff = errors.New("dial backoff")
35 36 37

	// ErrDialToSelf is returned if we attempt to dial our own peer
	ErrDialToSelf = errors.New("dial to self attempted")
Steven Allen's avatar
Steven Allen committed
38 39 40 41

	// ErrNoTransport is returned when we don't know a transport for the
	// given multiaddr.
	ErrNoTransport = errors.New("no transport for protocol")
42 43 44 45 46 47 48 49

	// ErrAllDialsFailed is returned when connecting to a peer has ultimately failed
	ErrAllDialsFailed = errors.New("all dials failed")

	// ErrNoAddresses is returned when we fail to find any addresses for a
	// peer we're trying to dial.
	ErrNoAddresses = errors.New("no addresses")

Aliabbas Merchant's avatar
Aliabbas Merchant committed
50
	// ErrNoGoodAddresses is returned when we find addresses for a peer but
51 52
	// can't use any of them.
	ErrNoGoodAddresses = errors.New("no good addresses")
53 54 55 56

	// ErrGaterDisallowedConnection is returned when the gater prevents us from
	// forming a connection with a peer.
	ErrGaterDisallowedConnection = errors.New("gater disallows connection to peer")
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
57 58
)

Steven Allen's avatar
Steven Allen committed
59
// DialAttempts governs how many times a goroutine will try to dial a given peer.
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
60 61
// Note: this is down to one, as we have _too many dials_ atm. To add back in,
// add loop back in Dial(.)
Steven Allen's avatar
Steven Allen committed
62
const DialAttempts = 1
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
63

Steven Allen's avatar
Steven Allen committed
64 65 66
// ConcurrentFdDials is the number of concurrent outbound dials over transports
// that consume file descriptors
const ConcurrentFdDials = 160
Jeromy's avatar
Jeromy committed
67

Steven Allen's avatar
Steven Allen committed
68 69 70
// DefaultPerPeerRateLimit is the number of concurrent outbound dials to make
// per peer
const DefaultPerPeerRateLimit = 8
Jeromy's avatar
Jeromy committed
71

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
// dialbackoff is a struct used to avoid over-dialing the same, dead peers.
// Whenever we totally time out on a peer (all three attempts), we add them
// to dialbackoff. Then, whenevers goroutines would _wait_ (dialsync), they
// check dialbackoff. If it's there, they don't wait and exit promptly with
// an error. (the single goroutine that is actually dialing continues to
// dial). If a dial is successful, the peer is removed from backoff.
// Example:
//
//  for {
//  	if ok, wait := dialsync.Lock(p); !ok {
//  		if backoff.Backoff(p) {
//  			return errDialFailed
//  		}
//  		<-wait
//  		continue
//  	}
//  	defer dialsync.Unlock(p)
//  	c, err := actuallyDial(p)
//  	if err != nil {
//  		dialbackoff.AddBackoff(p)
//  		continue
//  	}
//  	dialbackoff.Clear(p)
//  }
//
Jeromy's avatar
Jeromy committed
97

Steven Allen's avatar
Steven Allen committed
98 99
// DialBackoff is a type for tracking peer dial backoffs.
//
100
// * It's safe to use its zero value.
Steven Allen's avatar
Steven Allen committed
101 102 103
// * It's thread-safe.
// * It's *not* safe to move this type after using.
type DialBackoff struct {
Will Scott's avatar
Will Scott committed
104
	entries map[peer.ID]map[string]*backoffAddr
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
105 106 107
	lock    sync.RWMutex
}

Will Scott's avatar
Will Scott committed
108
type backoffAddr struct {
Jeromy's avatar
Jeromy committed
109 110 111 112
	tries int
	until time.Time
}

Will Scott's avatar
Will Scott committed
113
func (db *DialBackoff) init(ctx context.Context) {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
114
	if db.entries == nil {
Will Scott's avatar
Will Scott committed
115
		db.entries = make(map[peer.ID]map[string]*backoffAddr)
Will Scott's avatar
Will Scott committed
116 117 118 119 120 121
	}
	go db.background(ctx)
}

func (db *DialBackoff) background(ctx context.Context) {
	ticker := time.NewTicker(BackoffMax)
Will Scott's avatar
Will Scott committed
122
	defer ticker.Stop()
Will Scott's avatar
Will Scott committed
123 124 125 126 127 128 129
	for {
		select {
		case <-ctx.Done():
			return
		case <-ticker.C:
			db.cleanup()
		}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
130 131 132 133
	}
}

// Backoff returns whether the client should backoff from dialing
Will Scott's avatar
Will Scott committed
134 135
// peer p at address addr
func (db *DialBackoff) Backoff(p peer.ID, addr ma.Multiaddr) (backoff bool) {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
136
	db.lock.Lock()
Jeromy's avatar
Jeromy committed
137 138
	defer db.lock.Unlock()

139 140
	ap, found := db.entries[p][string(addr.Bytes())]
	return found && time.Now().Before(ap.until)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
141 142
}

Steven Allen's avatar
Steven Allen committed
143 144 145 146 147 148 149 150
// BackoffBase is the base amount of time to backoff (default: 5s).
var BackoffBase = time.Second * 5

// BackoffCoef is the backoff coefficient (default: 1s).
var BackoffCoef = time.Second

// BackoffMax is the maximum backoff time (default: 5m).
var BackoffMax = time.Minute * 5
Jeromy's avatar
Jeromy committed
151

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
152 153 154
// AddBackoff lets other nodes know that we've entered backoff with
// peer p, so dialers should not wait unnecessarily. We still will
// attempt to dial with one goroutine, in case we get through.
Steven Allen's avatar
Steven Allen committed
155 156 157 158 159 160 161
//
// Backoff is not exponential, it's quadratic and computed according to the
// following formula:
//
//     BackoffBase + BakoffCoef * PriorBackoffs^2
//
// Where PriorBackoffs is the number of previous backoffs.
Will Scott's avatar
Will Scott committed
162
func (db *DialBackoff) AddBackoff(p peer.ID, addr ma.Multiaddr) {
Will Scott's avatar
Will Scott committed
163
	saddr := string(addr.Bytes())
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
164
	db.lock.Lock()
Jeromy's avatar
Jeromy committed
165 166 167
	defer db.lock.Unlock()
	bp, ok := db.entries[p]
	if !ok {
168 169
		bp = make(map[string]*backoffAddr, 1)
		db.entries[p] = bp
Will Scott's avatar
Will Scott committed
170
	}
Will Scott's avatar
Will Scott committed
171
	ba, ok := bp[saddr]
Will Scott's avatar
Will Scott committed
172
	if !ok {
Will Scott's avatar
Will Scott committed
173
		bp[saddr] = &backoffAddr{
Jeromy's avatar
Jeromy committed
174
			tries: 1,
Steven Allen's avatar
Steven Allen committed
175
			until: time.Now().Add(BackoffBase),
Jeromy's avatar
Jeromy committed
176 177 178 179
		}
		return
	}

Will Scott's avatar
Will Scott committed
180
	backoffTime := BackoffBase + BackoffCoef*time.Duration(ba.tries*ba.tries)
Steven Allen's avatar
Steven Allen committed
181 182
	if backoffTime > BackoffMax {
		backoffTime = BackoffMax
Jeromy's avatar
Jeromy committed
183
	}
Will Scott's avatar
Will Scott committed
184 185
	ba.until = time.Now().Add(backoffTime)
	ba.tries++
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
186 187 188 189
}

// Clear removes a backoff record. Clients should call this after a
// successful Dial.
Steven Allen's avatar
Steven Allen committed
190
func (db *DialBackoff) Clear(p peer.ID) {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
191
	db.lock.Lock()
Jeromy's avatar
Jeromy committed
192
	defer db.lock.Unlock()
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
193 194 195
	delete(db.entries, p)
}

Will Scott's avatar
Will Scott committed
196 197 198 199 200 201 202
func (db *DialBackoff) cleanup() {
	db.lock.Lock()
	defer db.lock.Unlock()
	now := time.Now()
	for p, e := range db.entries {
		good := false
		for _, backoff := range e {
203 204 205 206 207
			backoffTime := BackoffBase + BackoffCoef*time.Duration(backoff.tries*backoff.tries)
			if backoffTime > BackoffMax {
				backoffTime = BackoffMax
			}
			if now.Before(backoff.until.Add(backoffTime)) {
Will Scott's avatar
Will Scott committed
208 209 210 211 212
				good = true
				break
			}
		}
		if !good {
Will Scott's avatar
Will Scott committed
213
			delete(db.entries, p)
Will Scott's avatar
Will Scott committed
214 215 216 217
		}
	}
}

Steven Allen's avatar
Steven Allen committed
218
// DialPeer connects to a peer.
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
219 220 221 222
//
// 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,
Steven Allen's avatar
Steven Allen committed
223
// etc. to achieve connection.
224
func (s *Swarm) DialPeer(ctx context.Context, p peer.ID) (network.Conn, error) {
225 226 227 228 229
	if s.gater != nil && !s.gater.InterceptPeerDial(p) {
		log.Debugf("gater disallowed outbound connection to peer %s", p.Pretty())
		return nil, &DialError{Peer: p, Cause: ErrGaterDisallowedConnection}
	}

Steven Allen's avatar
Steven Allen committed
230 231 232 233 234 235 236 237 238
	return s.dialPeer(ctx, p)
}

// internal dial method that returns an unwrapped conn
//
// It is gated by the swarm's dial synchronization systems: dialsync and
// dialbackoff.
func (s *Swarm) dialPeer(ctx context.Context, p peer.ID) (*Conn, error) {
	log.Debugf("[%s] swarm dialing peer [%s]", s.local, p)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
239
	var logdial = lgbl.Dial("swarm", s.LocalPeer(), p, nil, nil)
240 241 242 243 244
	err := p.Validate()
	if err != nil {
		return nil, err
	}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
245 246 247 248 249
	if p == s.local {
		log.Event(ctx, "swarmDialSelf", logdial)
		return nil, ErrDialToSelf
	}

250
	defer log.EventBegin(ctx, "swarmDialAttemptSync", p).Done()
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
251 252

	// check if we already have an open connection first
Steven Allen's avatar
Steven Allen committed
253
	conn := s.bestConnToPeer(p)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
254 255 256 257
	if conn != nil {
		return conn, nil
	}

258
	// apply the DialPeer timeout
259
	ctx, cancel := context.WithTimeout(ctx, network.GetDialPeerTimeout(ctx))
260 261
	defer cancel()

262
	conn, err = s.dsync.DialLock(ctx, p)
263 264
	if err == nil {
		return conn, nil
Steven Allen's avatar
Steven Allen committed
265
	}
266

Steven Allen's avatar
Steven Allen committed
267
	log.Debugf("network for %s finished dialing %s", s.local, p)
268 269 270 271 272 273 274 275 276 277 278 279

	if ctx.Err() != nil {
		// Context error trumps any dial errors as it was likely the ultimate cause.
		return nil, ctx.Err()
	}

	if s.ctx.Err() != nil {
		// Ok, so the swarm is shutting down.
		return nil, ErrSwarmClosed
	}

	return nil, err
280
}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
281

282 283 284
// doDial is an ugly shim method to retain all the logging and backoff logic
// of the old dialsync code
func (s *Swarm) doDial(ctx context.Context, p peer.ID) (*Conn, error) {
Steven Allen's avatar
Steven Allen committed
285 286 287 288 289 290 291 292
	// Short circuit.
	// By the time we take the dial lock, we may already *have* a connection
	// to the peer.
	c := s.bestConnToPeer(p)
	if c != nil {
		return c, nil
	}

Steven Allen's avatar
Steven Allen committed
293 294
	logdial := lgbl.Dial("swarm", s.LocalPeer(), p, nil, nil)

295 296 297
	// 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()
Steven Allen's avatar
Steven Allen committed
298 299

	conn, err := s.dial(ctx, p)
300
	if err != nil {
Steven Allen's avatar
Steven Allen committed
301 302 303 304 305 306 307 308 309
		conn = s.bestConnToPeer(p)
		if conn != nil {
			// Hm? What error?
			// Could have canceled the dial because we received a
			// connection or some other random reason.
			// Just ignore the error and return the connection.
			log.Debugf("ignoring dial error because we have a connection: %s", err)
			return conn, nil
		}
310

Steven Allen's avatar
Steven Allen committed
311
		// ok, we failed.
312
		return nil, err
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
313
	}
314
	return conn, nil
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
315 316
}

Steven Allen's avatar
Steven Allen committed
317 318 319 320 321
func (s *Swarm) canDial(addr ma.Multiaddr) bool {
	t := s.TransportForDialing(addr)
	return t != nil && t.CanDial(addr)
}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
322 323 324 325 326 327 328 329 330 331 332
// 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)
Matt Joiner's avatar
Matt Joiner committed
333
	logdial["encrypted"] = sk != nil // log whether this will be an encrypted dial or not.
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
334 335 336 337 338
	if sk == nil {
		// fine for sk to be nil, just log.
		log.Debug("Dial not given PrivateKey, so WILL NOT SECURE conn.")
	}

Jeromy's avatar
Jeromy committed
339 340
	//////
	/*
341
		This slice-to-chan code is temporary, the peerstore can currently provide
Jeromy's avatar
Jeromy committed
342 343 344 345 346
		a channel as an interface for receiving addresses, but more thought
		needs to be put into the execution. For now, this allows us to use
		the improved rate limiter, while maintaining the outward behaviour
		that we previously had (halting a dial when we run out of addrs)
	*/
Matt Joiner's avatar
Matt Joiner committed
347 348
	peerAddrs := s.peers.Addrs(p)
	if len(peerAddrs) == 0 {
349
		return nil, &DialError{Peer: p, Cause: ErrNoAddresses}
Matt Joiner's avatar
Matt Joiner committed
350
	}
351
	goodAddrs := s.filterKnownUndialables(p, peerAddrs)
Matt Joiner's avatar
Matt Joiner committed
352
	if len(goodAddrs) == 0 {
353
		return nil, &DialError{Peer: p, Cause: ErrNoGoodAddresses}
Matt Joiner's avatar
Matt Joiner committed
354
	}
355
	goodAddrsChan := make(chan ma.Multiaddr, len(goodAddrs))
Will Scott's avatar
Will Scott committed
356
	nonBackoff := false
357
	for _, a := range goodAddrs {
Will Scott's avatar
Will Scott committed
358 359 360 361 362
		// skip addresses in back-off
		if !s.backf.Backoff(p, a) {
			nonBackoff = true
			goodAddrsChan <- a
		}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
363
	}
364
	close(goodAddrsChan)
Will Scott's avatar
Will Scott committed
365 366 367
	if !nonBackoff {
		return nil, ErrDialBackoff
	}
Jeromy's avatar
Jeromy committed
368
	/////////
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
369 370

	// try to get a connection to any addr
371 372 373
	connC, dialErr := s.dialAddrs(ctx, p, goodAddrsChan)
	if dialErr != nil {
		logdial["error"] = dialErr.Cause.Error()
374 375 376 377
		switch dialErr.Cause {
		case context.Canceled, context.DeadlineExceeded:
			// Always prefer the context errors as we rely on being
			// able to check them.
378 379 380
			//
			// Removing this will BREAK backoff (causing us to
			// backoff when canceling dials).
381
			return nil, dialErr.Cause
382 383
		}
		return nil, dialErr
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
384
	}
Steven Allen's avatar
Steven Allen committed
385 386 387 388
	logdial["conn"] = logging.Metadata{
		"localAddr":  connC.LocalMultiaddr(),
		"remoteAddr": connC.RemoteMultiaddr(),
	}
389
	swarmC, err := s.addConn(connC, network.DirOutbound)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
390
	if err != nil {
Jeromy's avatar
Jeromy committed
391
		logdial["error"] = err.Error()
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
392
		connC.Close() // close the connection. didn't work out :(
393
		return nil, &DialError{Peer: p, Cause: err}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
394 395 396 397 398 399
	}

	logdial["dial"] = "success"
	return swarmC, nil
}

400 401 402 403 404
// filterKnownUndialables takes a list of multiaddrs, and removes those
// that we definitely don't want to dial: addresses configured to be blocked,
// IPv6 link-local addresses, addresses without a dial-capable transport,
// and addresses that we know to be our own.
// This is an optimization to avoid wasting time on dials that we know are going to fail.
405
func (s *Swarm) filterKnownUndialables(p peer.ID, addrs []ma.Multiaddr) []ma.Multiaddr {
406 407 408 409 410 411 412 413 414 415 416 417 418 419 420
	lisAddrs, _ := s.InterfaceListenAddresses()
	var ourAddrs []ma.Multiaddr
	for _, addr := range lisAddrs {
		protos := addr.Protocols()
		// we're only sure about filtering out /ip4 and /ip6 addresses, so far
		if len(protos) == 2 && (protos[0].Code == ma.P_IP4 || protos[0].Code == ma.P_IP6) {
			ourAddrs = append(ourAddrs, addr)
		}
	}

	return addrutil.FilterAddrs(addrs,
		addrutil.SubtractFilter(ourAddrs...),
		s.canDial,
		// TODO: Consider allowing link-local addresses
		addrutil.AddrOverNonLocalIP,
421 422 423
		func(addr ma.Multiaddr) bool {
			return s.gater == nil || s.gater.InterceptAddrDial(p, addr)
		},
424 425 426
	)
}

427
func (s *Swarm) dialAddrs(ctx context.Context, p peer.ID, remoteAddrs <-chan ma.Multiaddr) (transport.CapableConn, *DialError) {
Jeromy's avatar
Jeromy committed
428
	log.Debugf("%s swarm dialing %s", s.local, p)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
429 430 431 432

	ctx, cancel := context.WithCancel(ctx)
	defer cancel() // cancel work when we exit func

Jeromy's avatar
Jeromy committed
433 434
	// use a single response type instead of errs and conns, reduces complexity *a ton*
	respch := make(chan dialResult)
435
	err := &DialError{Peer: p}
Jeromy's avatar
Jeromy committed
436

437 438
	defer s.limiter.clearAllPeerDials(p)

Jeromy's avatar
Jeromy committed
439
	var active int
440
dialLoop:
441 442 443 444
	for remoteAddrs != nil || active > 0 {
		// Check for context cancellations and/or responses first.
		select {
		case <-ctx.Done():
445
			break dialLoop
446 447 448 449
		case resp := <-respch:
			active--
			if resp.Err != nil {
				// Errors are normal, lots of dials will fail
Will Scott's avatar
Will Scott committed
450 451 452 453
				if resp.Err != context.Canceled {
					s.backf.AddBackoff(p, resp.Addr)
				}

tg's avatar
tg committed
454
				log.Infof("got error on dial: %s", resp.Err)
455
				err.recordErr(resp.Addr, resp.Err)
456 457 458 459 460 461 462 463 464 465
			} else if resp.Conn != nil {
				return resp.Conn, nil
			}

			// We got a result, try again from the top.
			continue
		default:
		}

		// Now, attempt to dial.
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
466
		select {
Jeromy's avatar
Jeromy committed
467 468 469 470
		case addr, ok := <-remoteAddrs:
			if !ok {
				remoteAddrs = nil
				continue
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
471 472
			}

Jeromy's avatar
Jeromy committed
473 474 475
			s.limitedDial(ctx, p, addr, respch)
			active++
		case <-ctx.Done():
476
			break dialLoop
Jeromy's avatar
Jeromy committed
477 478 479 480
		case resp := <-respch:
			active--
			if resp.Err != nil {
				// Errors are normal, lots of dials will fail
Will Scott's avatar
Will Scott committed
481 482 483 484
				if resp.Err != context.Canceled {
					s.backf.AddBackoff(p, resp.Addr)
				}

tg's avatar
tg committed
485
				log.Infof("got error on dial: %s", resp.Err)
486
				err.recordErr(resp.Addr, resp.Err)
Jeromy's avatar
Jeromy committed
487 488
			} else if resp.Conn != nil {
				return resp.Conn, nil
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
489 490 491
			}
		}
	}
tg's avatar
tg committed
492

493 494 495
	if ctxErr := ctx.Err(); ctxErr != nil {
		err.Cause = ctxErr
	} else if len(err.DialErrors) == 0 {
496
		err.Cause = network.ErrNoRemoteAddrs
497 498
	} else {
		err.Cause = ErrAllDialsFailed
tg's avatar
tg committed
499
	}
500
	return nil, err
Jeromy's avatar
Jeromy committed
501 502
}

Jeromy's avatar
Jeromy committed
503 504 505
// limitedDial will start a dial to the given peer when
// it is able, respecting the various different types of rate
// limiting that occur without using extra goroutines per addr
Jeromy's avatar
Jeromy committed
506 507 508 509 510 511 512
func (s *Swarm) limitedDial(ctx context.Context, p peer.ID, a ma.Multiaddr, resp chan dialResult) {
	s.limiter.AddDialJob(&dialJob{
		addr: a,
		peer: p,
		resp: resp,
		ctx:  ctx,
	})
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
513 514
}

515
func (s *Swarm) dialAddr(ctx context.Context, p peer.ID, addr ma.Multiaddr) (transport.CapableConn, error) {
Steven Allen's avatar
Steven Allen committed
516 517 518 519
	// Just to double check. Costs nothing.
	if s.local == p {
		return nil, ErrDialToSelf
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
520 521
	log.Debugf("%s swarm dialing %s %s", s.local, p, addr)

522 523
	tpt := s.TransportForDialing(addr)
	if tpt == nil {
Steven Allen's avatar
Steven Allen committed
524
		return nil, ErrNoTransport
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
525 526
	}

527
	connC, err := tpt.Dial(ctx, addr, p)
Steven Allen's avatar
Steven Allen committed
528
	if err != nil {
529
		return nil, err
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
530 531
	}

Steven Allen's avatar
Steven Allen committed
532 533
	// Trust the transport? Yeah... right.
	if connC.RemotePeer() != p {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
534
		connC.Close()
535
		err = fmt.Errorf("BUG in transport %T: tried to dial %s, dialed %s", p, connC.RemotePeer(), tpt)
Steven Allen's avatar
Steven Allen committed
536 537
		log.Error(err)
		return nil, err
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
538 539 540 541 542
	}

	// success! we got one!
	return connC, nil
}