swarm_dial.go 15 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")
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
53 54
)

Steven Allen's avatar
Steven Allen committed
55
// DialAttempts governs how many times a goroutine will try to dial a given peer.
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
56 57
// 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
58
const DialAttempts = 1
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
59

Steven Allen's avatar
Steven Allen committed
60 61 62
// ConcurrentFdDials is the number of concurrent outbound dials over transports
// that consume file descriptors
const ConcurrentFdDials = 160
Jeromy's avatar
Jeromy committed
63

Steven Allen's avatar
Steven Allen committed
64 65 66
// DefaultPerPeerRateLimit is the number of concurrent outbound dials to make
// per peer
const DefaultPerPeerRateLimit = 8
Jeromy's avatar
Jeromy committed
67

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
// 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
93

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

Will Scott's avatar
Will Scott committed
104 105 106
type backoffPeer map[ma.Multiaddr]*backoffAddr

type backoffAddr struct {
Jeromy's avatar
Jeromy committed
107 108 109 110
	tries int
	until time.Time
}

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

func (db *DialBackoff) background(ctx context.Context) {
	ticker := time.NewTicker(BackoffMax)
	for {
		select {
		case <-ctx.Done():
			ticker.Stop()
			return
		case <-ticker.C:
			db.cleanup()
		}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
128 129 130 131
	}
}

// Backoff returns whether the client should backoff from dialing
Will Scott's avatar
Will Scott committed
132 133
// 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
134
	db.lock.Lock()
Jeromy's avatar
Jeromy committed
135 136
	defer db.lock.Unlock()
	bp, found := db.entries[p]
Will Scott's avatar
Will Scott committed
137
	if found && bp != nil {
Will Scott's avatar
Will Scott committed
138
		ap, found := bp[addr]
Will Scott's avatar
Will Scott committed
139 140 141 142
		// TODO: cleanup out of date entries.
		if found && time.Now().Before(ap.until) {
			return true
		}
Jeromy's avatar
Jeromy committed
143 144 145
	}

	return false
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
146 147
}

Steven Allen's avatar
Steven Allen committed
148 149 150 151 152 153 154 155
// 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
156

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
157 158 159
// 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
160 161 162 163 164 165 166
//
// 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
167
func (db *DialBackoff) AddBackoff(p peer.ID, addr ma.Multiaddr) {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
168
	db.lock.Lock()
Jeromy's avatar
Jeromy committed
169 170 171
	defer db.lock.Unlock()
	bp, ok := db.entries[p]
	if !ok {
Will Scott's avatar
Will Scott committed
172 173
		db.entries[p] = backoffPeer(make(map[ma.Multiaddr]*backoffAddr))
		db.entries[p][addr] = &backoffAddr{
Will Scott's avatar
Will Scott committed
174 175 176 177 178
			tries: 1,
			until: time.Now().Add(BackoffBase),
		}
		return
	}
Will Scott's avatar
Will Scott committed
179
	ba, ok := bp[addr]
Will Scott's avatar
Will Scott committed
180
	if !ok {
Will Scott's avatar
Will Scott committed
181
		bp[addr] = &backoffAddr{
Jeromy's avatar
Jeromy committed
182
			tries: 1,
Steven Allen's avatar
Steven Allen committed
183
			until: time.Now().Add(BackoffBase),
Jeromy's avatar
Jeromy committed
184 185 186 187
		}
		return
	}

Will Scott's avatar
Will Scott committed
188
	backoffTime := BackoffBase + BackoffCoef*time.Duration(ba.tries*ba.tries)
Steven Allen's avatar
Steven Allen committed
189 190
	if backoffTime > BackoffMax {
		backoffTime = BackoffMax
Jeromy's avatar
Jeromy committed
191
	}
Will Scott's avatar
Will Scott committed
192 193
	ba.until = time.Now().Add(backoffTime)
	ba.tries++
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
194 195 196 197
}

// Clear removes a backoff record. Clients should call this after a
// successful Dial.
Steven Allen's avatar
Steven Allen committed
198
func (db *DialBackoff) Clear(p peer.ID) {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
199
	db.lock.Lock()
Jeromy's avatar
Jeromy committed
200
	defer db.lock.Unlock()
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
201 202 203
	delete(db.entries, p)
}

Will Scott's avatar
Will Scott committed
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225
func (db *DialBackoff) cleanup() {
	db.lock.Lock()
	defer db.lock.Unlock()
	now := time.Now()
	deletePeers := []peer.ID{}
	for p, e := range db.entries {
		good := false
		for _, backoff := range e {
			if now.Before(backoff.until) {
				good = true
				break
			}
		}
		if !good {
			deletePeers = append(deletePeers, p)
		}
	}
	for _, p := range deletePeers {
		delete(db.entries, p)
	}
}

Steven Allen's avatar
Steven Allen committed
226
// DialPeer connects to a peer.
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
227 228 229 230
//
// 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
231
// etc. to achieve connection.
232
func (s *Swarm) DialPeer(ctx context.Context, p peer.ID) (network.Conn, error) {
Steven Allen's avatar
Steven Allen committed
233 234 235 236 237 238 239 240 241
	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
242
	var logdial = lgbl.Dial("swarm", s.LocalPeer(), p, nil, nil)
243 244 245 246 247
	err := p.Validate()
	if err != nil {
		return nil, err
	}

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

253
	defer log.EventBegin(ctx, "swarmDialAttemptSync", p).Done()
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
254 255

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

261
	// apply the DialPeer timeout
262
	ctx, cancel := context.WithTimeout(ctx, network.GetDialPeerTimeout(ctx))
263 264
	defer cancel()

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

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

	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
283
}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
284

285 286 287
// 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
288 289 290 291 292 293 294 295
	// 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
296 297
	logdial := lgbl.Dial("swarm", s.LocalPeer(), p, nil, nil)

298 299 300
	// 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
301 302

	conn, err := s.dial(ctx, p)
303
	if err != nil {
Steven Allen's avatar
Steven Allen committed
304 305 306 307 308 309 310 311 312
		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
		}
313

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

Steven Allen's avatar
Steven Allen committed
320 321 322 323 324
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
325 326 327 328 329 330 331 332 333 334 335
// 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
336
	logdial["encrypted"] = sk != nil // log whether this will be an encrypted dial or not.
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
337 338 339 340 341
	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
342 343
	//////
	/*
344
		This slice-to-chan code is temporary, the peerstore can currently provide
Jeromy's avatar
Jeromy committed
345 346 347 348 349
		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
350 351
	peerAddrs := s.peers.Addrs(p)
	if len(peerAddrs) == 0 {
352
		return nil, &DialError{Peer: p, Cause: ErrNoAddresses}
Matt Joiner's avatar
Matt Joiner committed
353 354
	}
	goodAddrs := s.filterKnownUndialables(peerAddrs)
Matt Joiner's avatar
Matt Joiner committed
355
	if len(goodAddrs) == 0 {
356
		return nil, &DialError{Peer: p, Cause: ErrNoGoodAddresses}
Matt Joiner's avatar
Matt Joiner committed
357
	}
358
	goodAddrsChan := make(chan ma.Multiaddr, len(goodAddrs))
Will Scott's avatar
Will Scott committed
359
	nonBackoff := false
360
	for _, a := range goodAddrs {
Will Scott's avatar
Will Scott committed
361 362 363 364 365
		// skip addresses in back-off
		if !s.backf.Backoff(p, a) {
			nonBackoff = true
			goodAddrsChan <- a
		}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
366
	}
367
	close(goodAddrsChan)
Will Scott's avatar
Will Scott committed
368 369 370
	if !nonBackoff {
		return nil, ErrDialBackoff
	}
Jeromy's avatar
Jeromy committed
371
	/////////
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
372 373

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

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

403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427
// 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.
func (s *Swarm) filterKnownUndialables(addrs []ma.Multiaddr) []ma.Multiaddr {
	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,
		addrutil.FilterNeg(s.Filters.AddrBlocked),
	)
}

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

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

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

438 439
	defer s.limiter.clearAllPeerDials(p)

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

tg's avatar
tg committed
455
				log.Infof("got error on dial: %s", resp.Err)
456
				err.recordErr(resp.Addr, resp.Err)
457 458 459 460 461 462 463 464 465 466
			} 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
467
		select {
Jeromy's avatar
Jeromy committed
468 469 470 471
		case addr, ok := <-remoteAddrs:
			if !ok {
				remoteAddrs = nil
				continue
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
472 473
			}

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

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

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

Jeromy's avatar
Jeromy committed
504 505 506
// 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
507 508 509 510 511 512 513
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
514 515
}

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

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

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

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

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