dht.go 16.8 KB
Newer Older
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
1 2
package dht

3
import (
4
	"bytes"
5
	"fmt"
6 7
	"sync"
	"time"
8

9
	peer "github.com/jbenet/go-ipfs/peer"
Jeromy's avatar
Jeromy committed
10
	kb "github.com/jbenet/go-ipfs/routing/kbucket"
11 12
	swarm "github.com/jbenet/go-ipfs/swarm"
	u "github.com/jbenet/go-ipfs/util"
13 14

	ma "github.com/jbenet/go-multiaddr"
Jeromy's avatar
Jeromy committed
15 16 17

	ds "github.com/jbenet/datastore.go"

18
	"code.google.com/p/goprotobuf/proto"
19 20
)

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
21 22 23 24 25
// TODO. SEE https://github.com/jbenet/node-ipfs/blob/master/submodules/ipfs-dht/index.js

// IpfsDHT is an implementation of Kademlia with Coral and S/Kademlia modifications.
// It is used to implement the base IpfsRouting module.
type IpfsDHT struct {
26 27
	// Array of routing tables for differently distanced nodes
	// NOTE: (currently, only a single table is used)
28
	routingTables []*kb.RoutingTable
29

30
	network swarm.Network
31
	netChan *swarm.Chan
32

Jeromy's avatar
Jeromy committed
33 34 35 36 37
	// Local peer (yourself)
	self *peer.Peer

	// Local data
	datastore ds.Datastore
38
	dslock    sync.Mutex
39

40
	providers *ProviderManager
41

42 43
	// Signal to shutdown dht
	shutdown chan struct{}
44 45 46

	// When this peer started up
	birth time.Time
47 48 49

	//lock to make diagnostics work better
	diaglock sync.Mutex
50

51
	// listener is a server to register to listen for responses to messages
52
	listener *swarm.MessageListener
53 54
}

Jeromy's avatar
Jeromy committed
55
// NewDHT creates a new DHT object with the given peer as the 'local' host
Jeromy's avatar
Jeromy committed
56
func NewDHT(p *peer.Peer, net swarm.Network, dstore ds.Datastore) *IpfsDHT {
57
	dht := new(IpfsDHT)
Jeromy's avatar
Jeromy committed
58
	dht.network = net
59
	dht.netChan = net.GetChannel(swarm.PBWrapper_DHT_MESSAGE)
Jeromy's avatar
Jeromy committed
60
	dht.datastore = dstore
61
	dht.self = p
62
	dht.providers = NewProviderManager()
Jeromy's avatar
Jeromy committed
63
	dht.shutdown = make(chan struct{})
64

65 66 67 68
	dht.routingTables = make([]*kb.RoutingTable, 3)
	dht.routingTables[0] = kb.NewRoutingTable(20, kb.ConvertPeerID(p.ID), time.Millisecond*30)
	dht.routingTables[1] = kb.NewRoutingTable(20, kb.ConvertPeerID(p.ID), time.Millisecond*100)
	dht.routingTables[2] = kb.NewRoutingTable(20, kb.ConvertPeerID(p.ID), time.Hour)
69
	dht.listener = swarm.NewMessageListener()
70
	dht.birth = time.Now()
Jeromy's avatar
Jeromy committed
71
	return dht
72 73
}

74
// Start up background goroutines needed by the DHT
75 76 77 78
func (dht *IpfsDHT) Start() {
	go dht.handleMessages()
}

79
// Connect to a new peer at the given address, ping and add to the routing table
80
func (dht *IpfsDHT) Connect(addr *ma.Multiaddr) (*peer.Peer, error) {
81
	maddrstr, _ := addr.String()
82
	u.DOut("Connect to new peer: %s\n", maddrstr)
83
	npeer, err := dht.network.ConnectNew(addr)
84
	if err != nil {
85
		return nil, err
86 87
	}

Jeromy's avatar
Jeromy committed
88 89
	// Ping new peer to register in their routing table
	// NOTE: this should be done better...
90
	err = dht.Ping(npeer, time.Second*2)
Jeromy's avatar
Jeromy committed
91
	if err != nil {
92
		return nil, fmt.Errorf("failed to ping newly connected peer: %s\n", err)
Jeromy's avatar
Jeromy committed
93 94
	}

95 96
	dht.Update(npeer)

97
	return npeer, nil
Jeromy's avatar
Jeromy committed
98 99
}

100 101
// Read in all messages from swarm and handle them appropriately
// NOTE: this function is just a quick sketch
102
func (dht *IpfsDHT) handleMessages() {
Jeromy's avatar
Jeromy committed
103
	u.DOut("Begin message handling routine\n")
104

105
	errs := dht.network.GetErrChan()
106 107
	for {
		select {
108
		case mes, ok := <-dht.netChan.Incoming:
109
			if !ok {
Jeromy's avatar
Jeromy committed
110
				u.DOut("handleMessages closing, bad recv on incoming\n")
111 112
				return
			}
113
			pmes := new(PBDHTMessage)
114 115
			err := proto.Unmarshal(mes.Data, pmes)
			if err != nil {
116
				u.PErr("Failed to decode protobuf message: %s\n", err)
117 118 119
				continue
			}

120
			dht.Update(mes.Peer)
121

122
			// Note: not sure if this is the correct place for this
123
			if pmes.GetResponse() {
124
				dht.listener.Respond(pmes.GetId(), mes)
125
				continue
126
			}
127 128
			//

129
			u.DOut("[peer: %s]\nGot message type: '%s' [id = %x, from = %s]\n",
Jeromy's avatar
Jeromy committed
130
				dht.self.ID.Pretty(),
131
				PBDHTMessage_MessageType_name[int32(pmes.GetType())],
132
				pmes.GetId(), mes.Peer.ID.Pretty())
133
			switch pmes.GetType() {
134
			case PBDHTMessage_GET_VALUE:
135
				go dht.handleGetValue(mes.Peer, pmes)
136
			case PBDHTMessage_PUT_VALUE:
137
				go dht.handlePutValue(mes.Peer, pmes)
138
			case PBDHTMessage_FIND_NODE:
139
				go dht.handleFindPeer(mes.Peer, pmes)
140
			case PBDHTMessage_ADD_PROVIDER:
141
				go dht.handleAddProvider(mes.Peer, pmes)
142
			case PBDHTMessage_GET_PROVIDERS:
143
				go dht.handleGetProviders(mes.Peer, pmes)
144
			case PBDHTMessage_PING:
145
				go dht.handlePing(mes.Peer, pmes)
146
			case PBDHTMessage_DIAGNOSTIC:
147
				go dht.handleDiagnostic(mes.Peer, pmes)
148 149
			default:
				u.PErr("Recieved invalid message type")
150 151
			}

152
		case err := <-errs:
153
			u.PErr("dht err: %s\n", err)
154 155
		case <-dht.shutdown:
			return
156 157 158 159
		}
	}
}

Jeromy's avatar
Jeromy committed
160
func (dht *IpfsDHT) putValueToNetwork(p *peer.Peer, key string, value []byte) error {
161
	pmes := Message{
162 163
		Type:  PBDHTMessage_PUT_VALUE,
		Key:   key,
164
		Value: value,
165
		ID:    swarm.GenerateMessageID(),
166 167 168
	}

	mes := swarm.NewMessage(p, pmes.ToProtobuf())
169
	dht.netChan.Outgoing <- mes
170 171 172
	return nil
}

173
func (dht *IpfsDHT) handleGetValue(p *peer.Peer, pmes *PBDHTMessage) {
174
	u.DOut("handleGetValue for key: %s\n", pmes.GetKey())
Jeromy's avatar
Jeromy committed
175
	dskey := ds.NewKey(pmes.GetKey())
176
	resp := &Message{
Jeromy's avatar
Jeromy committed
177
		Response: true,
178
		ID:       pmes.GetId(),
Jeromy's avatar
Jeromy committed
179 180 181
		Key:      pmes.GetKey(),
	}
	iVal, err := dht.datastore.Get(dskey)
Jeromy's avatar
Jeromy committed
182
	if err == nil {
Jeromy's avatar
Jeromy committed
183
		u.DOut("handleGetValue success!\n")
Jeromy's avatar
Jeromy committed
184 185
		resp.Success = true
		resp.Value = iVal.([]byte)
Jeromy's avatar
Jeromy committed
186
	} else if err == ds.ErrNotFound {
Jeromy's avatar
Jeromy committed
187
		// Check if we know any providers for the requested value
188 189
		provs := dht.providers.GetProviders(u.Key(pmes.GetKey()))
		if len(provs) > 0 {
190
			u.DOut("handleGetValue returning %d provider[s]\n", len(provs))
191
			resp.Peers = provs
Jeromy's avatar
Jeromy committed
192 193 194
			resp.Success = true
		} else {
			// No providers?
195 196
			// Find closest peer on given cluster to desired key and reply with that info

197 198 199
			level := 0
			if len(pmes.GetValue()) < 1 {
				// TODO: maybe return an error? Defaulting isnt a good idea IMO
Jeromy's avatar
Jeromy committed
200
				u.PErr("handleGetValue: no routing level specified, assuming 0\n")
201 202 203
			} else {
				level = int(pmes.GetValue()[0]) // Using value field to specify cluster level
			}
204
			u.DOut("handleGetValue searching level %d clusters\n", level)
205

206
			closer := dht.routingTables[level].NearestPeer(kb.ConvertKey(u.Key(pmes.GetKey())))
207

Jeromy's avatar
Jeromy committed
208
			if closer.ID.Equal(dht.self.ID) {
Jeromy's avatar
Jeromy committed
209
				u.DOut("Attempted to return self! this shouldnt happen...\n")
Jeromy's avatar
Jeromy committed
210 211 212
				resp.Peers = nil
				goto out
			}
213 214 215
			// If this peer is closer than the one from the table, return nil
			if kb.Closer(dht.self.ID, closer.ID, u.Key(pmes.GetKey())) {
				resp.Peers = nil
Jeromy's avatar
Jeromy committed
216
				u.DOut("handleGetValue could not find a closer node than myself.\n")
217
			} else {
218
				u.DOut("handleGetValue returning a closer peer: '%s'\n", closer.ID.Pretty())
219 220
				resp.Peers = []*peer.Peer{closer}
			}
221
		}
Jeromy's avatar
Jeromy committed
222
	} else {
223
		//temp: what other errors can a datastore return?
Jeromy's avatar
Jeromy committed
224
		panic(err)
225
	}
226

Jeromy's avatar
Jeromy committed
227
out:
228
	mes := swarm.NewMessage(p, resp.ToProtobuf())
229
	dht.netChan.Outgoing <- mes
230 231
}

Jeromy's avatar
Jeromy committed
232
// Store a value in this peer local storage
233
func (dht *IpfsDHT) handlePutValue(p *peer.Peer, pmes *PBDHTMessage) {
234 235
	dht.dslock.Lock()
	defer dht.dslock.Unlock()
Jeromy's avatar
Jeromy committed
236 237 238 239 240 241
	dskey := ds.NewKey(pmes.GetKey())
	err := dht.datastore.Put(dskey, pmes.GetValue())
	if err != nil {
		// For now, just panic, handle this better later maybe
		panic(err)
	}
242 243
}

244
func (dht *IpfsDHT) handlePing(p *peer.Peer, pmes *PBDHTMessage) {
245
	u.DOut("[%s] Responding to ping from [%s]!\n", dht.self.ID.Pretty(), p.ID.Pretty())
246
	resp := Message{
247
		Type:     pmes.GetType(),
248
		Response: true,
249
		ID:       pmes.GetId(),
250
	}
251

252
	dht.netChan.Outgoing <- swarm.NewMessage(p, resp.ToProtobuf())
253 254
}

255
func (dht *IpfsDHT) handleFindPeer(p *peer.Peer, pmes *PBDHTMessage) {
256
	resp := Message{
257
		Type:     pmes.GetType(),
258
		ID:       pmes.GetId(),
259 260 261 262
		Response: true,
	}
	defer func() {
		mes := swarm.NewMessage(p, resp.ToProtobuf())
263
		dht.netChan.Outgoing <- mes
264 265
	}()
	level := pmes.GetValue()[0]
266
	u.DOut("handleFindPeer: searching for '%s'\n", peer.ID(pmes.GetKey()).Pretty())
267
	closest := dht.routingTables[level].NearestPeer(kb.ConvertKey(u.Key(pmes.GetKey())))
Jeromy's avatar
Jeromy committed
268
	if closest == nil {
Jeromy's avatar
Jeromy committed
269
		u.PErr("handleFindPeer: could not find anything.\n")
270
		return
Jeromy's avatar
Jeromy committed
271 272 273
	}

	if len(closest.Addresses) == 0 {
Jeromy's avatar
Jeromy committed
274
		u.PErr("handleFindPeer: no addresses for connected peer...\n")
275
		return
Jeromy's avatar
Jeromy committed
276 277
	}

278 279 280
	// If the found peer further away than this peer...
	if kb.Closer(dht.self.ID, closest.ID, u.Key(pmes.GetKey())) {
		return
Jeromy's avatar
Jeromy committed
281 282
	}

283
	u.DOut("handleFindPeer: sending back '%s'\n", closest.ID.Pretty())
284 285 286 287 288
	resp.Peers = []*peer.Peer{closest}
	resp.Success = true
}

func (dht *IpfsDHT) handleGetProviders(p *peer.Peer, pmes *PBDHTMessage) {
289
	resp := Message{
290 291
		Type:     PBDHTMessage_GET_PROVIDERS,
		Key:      pmes.GetKey(),
292
		ID:       pmes.GetId(),
293
		Response: true,
Jeromy's avatar
Jeromy committed
294 295
	}

296
	providers := dht.providers.GetProviders(u.Key(pmes.GetKey()))
297
	if providers == nil || len(providers) == 0 {
Jeromy's avatar
Jeromy committed
298 299 300 301 302
		level := 0
		if len(pmes.GetValue()) > 0 {
			level = int(pmes.GetValue()[0])
		}

303
		closer := dht.routingTables[level].NearestPeer(kb.ConvertKey(u.Key(pmes.GetKey())))
Jeromy's avatar
Jeromy committed
304 305 306 307 308
		if kb.Closer(dht.self.ID, closer.ID, u.Key(pmes.GetKey())) {
			resp.Peers = nil
		} else {
			resp.Peers = []*peer.Peer{closer}
		}
309
	} else {
310
		resp.Peers = providers
311
		resp.Success = true
312 313 314
	}

	mes := swarm.NewMessage(p, resp.ToProtobuf())
315
	dht.netChan.Outgoing <- mes
316 317
}

318 319
type providerInfo struct {
	Creation time.Time
320
	Value    *peer.Peer
321 322
}

323
func (dht *IpfsDHT) handleAddProvider(p *peer.Peer, pmes *PBDHTMessage) {
324
	key := u.Key(pmes.GetKey())
Jeromy's avatar
Jeromy committed
325
	u.DOut("[%s] Adding [%s] as a provider for '%s'\n", dht.self.ID.Pretty(), p.ID.Pretty(), peer.ID(key).Pretty())
326
	dht.providers.AddProvider(key, p)
327 328
}

329
// Halt stops all communications from this peer and shut down
330 331 332
func (dht *IpfsDHT) Halt() {
	dht.shutdown <- struct{}{}
	dht.network.Close()
333 334
	dht.providers.Halt()
	dht.listener.Halt()
335
}
336

Jeromy's avatar
Jeromy committed
337
// NOTE: not yet finished, low priority
338
func (dht *IpfsDHT) handleDiagnostic(p *peer.Peer, pmes *PBDHTMessage) {
339
	seq := dht.routingTables[0].NearestPeers(kb.ConvertPeerID(dht.self.ID), 10)
340
	listenChan := dht.listener.Listen(pmes.GetId(), len(seq), time.Second*30)
341

342
	for _, ps := range seq {
343
		mes := swarm.NewMessage(ps, pmes)
344
		dht.netChan.Outgoing <- mes
345 346 347
	}

	buf := new(bytes.Buffer)
348 349 350
	di := dht.getDiagInfo()
	buf.Write(di.Marshal())

351 352 353 354 355 356 357 358
	// NOTE: this shouldnt be a hardcoded value
	after := time.After(time.Second * 20)
	count := len(seq)
	for count > 0 {
		select {
		case <-after:
			//Timeout, return what we have
			goto out
359 360 361
		case reqResp := <-listenChan:
			pmesOut := new(PBDHTMessage)
			err := proto.Unmarshal(reqResp.Data, pmesOut)
362 363 364 365
			if err != nil {
				// It broke? eh, whatever, keep going
				continue
			}
366
			buf.Write(reqResp.Data)
367 368 369 370 371
			count--
		}
	}

out:
372
	resp := Message{
373
		Type:     PBDHTMessage_DIAGNOSTIC,
374
		ID:       pmes.GetId(),
375
		Value:    buf.Bytes(),
376 377 378 379
		Response: true,
	}

	mes := swarm.NewMessage(p, resp.ToProtobuf())
380
	dht.netChan.Outgoing <- mes
381
}
382

383 384 385
func (dht *IpfsDHT) getValueOrPeers(p *peer.Peer, key u.Key, timeout time.Duration, level int) ([]byte, []*peer.Peer, error) {
	pmes, err := dht.getValueSingle(p, key, timeout, level)
	if err != nil {
386
		return nil, nil, err
387 388 389 390 391 392 393 394 395 396 397 398 399
	}

	if pmes.GetSuccess() {
		if pmes.Value == nil { // We were given provider[s]
			val, err := dht.getFromPeerList(key, timeout, pmes.GetPeers(), level)
			if err != nil {
				return nil, nil, err
			}
			return val, nil, nil
		}

		// Success! We were given the value
		return pmes.GetValue(), nil, nil
400
	}
401

402 403 404 405 406 407 408 409
	// We were given a closer node
	var peers []*peer.Peer
	for _, pb := range pmes.GetPeers() {
		if peer.ID(pb.GetId()).Equal(dht.self.ID) {
			continue
		}
		addr, err := ma.NewMultiaddr(pb.GetAddr())
		if err != nil {
410
			u.PErr("%v\n", err.Error())
411 412
			continue
		}
413

414 415
		np, err := dht.network.GetConnection(peer.ID(pb.GetId()), addr)
		if err != nil {
416
			u.PErr("%v\n", err.Error())
417
			continue
418
		}
419 420

		peers = append(peers, np)
421
	}
422
	return nil, peers, nil
423 424
}

425 426
// getValueSingle simply performs the get value RPC with the given parameters
func (dht *IpfsDHT) getValueSingle(p *peer.Peer, key u.Key, timeout time.Duration, level int) (*PBDHTMessage, error) {
427
	pmes := Message{
428 429 430
		Type:  PBDHTMessage_GET_VALUE,
		Key:   string(key),
		Value: []byte{byte(level)},
431
		ID:    swarm.GenerateMessageID(),
Jeromy's avatar
Jeromy committed
432
	}
433
	responseChan := dht.listener.Listen(pmes.ID, 1, time.Minute)
Jeromy's avatar
Jeromy committed
434 435

	mes := swarm.NewMessage(p, pmes.ToProtobuf())
436
	t := time.Now()
437
	dht.netChan.Outgoing <- mes
Jeromy's avatar
Jeromy committed
438 439 440 441 442

	// Wait for either the response or a timeout
	timeup := time.After(timeout)
	select {
	case <-timeup:
443
		dht.listener.Unlisten(pmes.ID)
Jeromy's avatar
Jeromy committed
444
		return nil, u.ErrTimeout
445
	case resp, ok := <-responseChan:
Jeromy's avatar
Jeromy committed
446
		if !ok {
Jeromy's avatar
Jeromy committed
447
			u.PErr("response channel closed before timeout, please investigate.\n")
Jeromy's avatar
Jeromy committed
448 449
			return nil, u.ErrTimeout
		}
450 451
		roundtrip := time.Since(t)
		resp.Peer.SetLatency(roundtrip)
452 453
		pmesOut := new(PBDHTMessage)
		err := proto.Unmarshal(resp.Data, pmesOut)
Jeromy's avatar
Jeromy committed
454 455 456
		if err != nil {
			return nil, err
		}
457
		return pmesOut, nil
Jeromy's avatar
Jeromy committed
458 459 460
	}
}

461 462 463 464 465 466 467 468 469 470
// TODO: Im not certain on this implementation, we get a list of peers/providers
// from someone what do we do with it? Connect to each of them? randomly pick
// one to get the value from? Or just connect to one at a time until we get a
// successful connection and request the value from it?
func (dht *IpfsDHT) getFromPeerList(key u.Key, timeout time.Duration,
	peerlist []*PBDHTMessage_PBPeer, level int) ([]byte, error) {
	for _, pinfo := range peerlist {
		p, _ := dht.Find(peer.ID(pinfo.GetId()))
		if p == nil {
			maddr, err := ma.NewMultiaddr(pinfo.GetAddr())
Jeromy's avatar
Jeromy committed
471
			if err != nil {
472
				u.PErr("getValue error: %s\n", err)
Jeromy's avatar
Jeromy committed
473 474
				continue
			}
475

476
			p, err = dht.network.GetConnection(peer.ID(pinfo.GetId()), maddr)
Jeromy's avatar
Jeromy committed
477
			if err != nil {
478
				u.PErr("getValue error: %s\n", err)
Jeromy's avatar
Jeromy committed
479 480 481
				continue
			}
		}
482
		pmes, err := dht.getValueSingle(p, key, timeout, level)
Jeromy's avatar
Jeromy committed
483
		if err != nil {
484
			u.DErr("getFromPeers error: %s\n", err)
Jeromy's avatar
Jeromy committed
485 486
			continue
		}
487
		dht.providers.AddProvider(key, p)
Jeromy's avatar
Jeromy committed
488

489 490 491 492
		// Make sure it was a successful get
		if pmes.GetSuccess() && pmes.Value != nil {
			return pmes.GetValue(), nil
		}
Jeromy's avatar
Jeromy committed
493 494 495 496
	}
	return nil, u.ErrNotFound
}

497
func (dht *IpfsDHT) getLocal(key u.Key) ([]byte, error) {
498 499
	dht.dslock.Lock()
	defer dht.dslock.Unlock()
500
	v, err := dht.datastore.Get(ds.NewKey(string(key)))
501 502 503 504 505 506
	if err != nil {
		return nil, err
	}
	return v.([]byte), nil
}

507
func (dht *IpfsDHT) putLocal(key u.Key, value []byte) error {
508 509
	return dht.datastore.Put(ds.NewKey(string(key)), value)
}
510

511
// Update TODO(chas) Document this function
512
func (dht *IpfsDHT) Update(p *peer.Peer) {
513
	for _, route := range dht.routingTables {
514 515 516 517
		removed := route.Update(p)
		// Only drop the connection if no tables refer to this peer
		if removed != nil {
			found := false
518
			for _, r := range dht.routingTables {
519 520 521 522 523 524 525 526 527
				if r.Find(removed.ID) != nil {
					found = true
					break
				}
			}
			if !found {
				dht.network.Drop(removed)
			}
		}
528 529
	}
}
Jeromy's avatar
Jeromy committed
530

531
// Find looks for a peer with a given ID connected to this dht and returns the peer and the table it was found in.
Jeromy's avatar
Jeromy committed
532
func (dht *IpfsDHT) Find(id peer.ID) (*peer.Peer, *kb.RoutingTable) {
533
	for _, table := range dht.routingTables {
Jeromy's avatar
Jeromy committed
534 535 536 537 538 539 540
		p := table.Find(id)
		if p != nil {
			return p, table
		}
	}
	return nil, nil
}
541 542

func (dht *IpfsDHT) findPeerSingle(p *peer.Peer, id peer.ID, timeout time.Duration, level int) (*PBDHTMessage, error) {
543
	pmes := Message{
544 545
		Type:  PBDHTMessage_FIND_NODE,
		Key:   string(id),
546
		ID:    swarm.GenerateMessageID(),
547 548 549 550
		Value: []byte{byte(level)},
	}

	mes := swarm.NewMessage(p, pmes.ToProtobuf())
551
	listenChan := dht.listener.Listen(pmes.ID, 1, time.Minute)
552
	t := time.Now()
553
	dht.netChan.Outgoing <- mes
554 555 556
	after := time.After(timeout)
	select {
	case <-after:
557
		dht.listener.Unlisten(pmes.ID)
558 559
		return nil, u.ErrTimeout
	case resp := <-listenChan:
560 561
		roundtrip := time.Since(t)
		resp.Peer.SetLatency(roundtrip)
562 563
		pmesOut := new(PBDHTMessage)
		err := proto.Unmarshal(resp.Data, pmesOut)
564 565 566 567
		if err != nil {
			return nil, err
		}

568
		return pmesOut, nil
569 570
	}
}
571

572 573
func (dht *IpfsDHT) printTables() {
	for _, route := range dht.routingTables {
574 575 576
		route.Print()
	}
}
Jeromy's avatar
Jeromy committed
577 578

func (dht *IpfsDHT) findProvidersSingle(p *peer.Peer, key u.Key, level int, timeout time.Duration) (*PBDHTMessage, error) {
579
	pmes := Message{
Jeromy's avatar
Jeromy committed
580 581
		Type:  PBDHTMessage_GET_PROVIDERS,
		Key:   string(key),
582
		ID:    swarm.GenerateMessageID(),
Jeromy's avatar
Jeromy committed
583 584 585 586 587
		Value: []byte{byte(level)},
	}

	mes := swarm.NewMessage(p, pmes.ToProtobuf())

588
	listenChan := dht.listener.Listen(pmes.ID, 1, time.Minute)
589
	dht.netChan.Outgoing <- mes
Jeromy's avatar
Jeromy committed
590 591 592
	after := time.After(timeout)
	select {
	case <-after:
593
		dht.listener.Unlisten(pmes.ID)
Jeromy's avatar
Jeromy committed
594 595
		return nil, u.ErrTimeout
	case resp := <-listenChan:
Jeromy's avatar
Jeromy committed
596
		u.DOut("FindProviders: got response.\n")
597 598
		pmesOut := new(PBDHTMessage)
		err := proto.Unmarshal(resp.Data, pmesOut)
Jeromy's avatar
Jeromy committed
599 600 601 602
		if err != nil {
			return nil, err
		}

603
		return pmesOut, nil
Jeromy's avatar
Jeromy committed
604 605 606
	}
}

607
// TODO: Could be done async
Jeromy's avatar
Jeromy committed
608
func (dht *IpfsDHT) addPeerList(key u.Key, peers []*PBDHTMessage_PBPeer) []*peer.Peer {
609
	var provArr []*peer.Peer
Jeromy's avatar
Jeromy committed
610 611 612 613 614 615 616 617
	for _, prov := range peers {
		// Dont add outselves to the list
		if peer.ID(prov.GetId()).Equal(dht.self.ID) {
			continue
		}
		// Dont add someone who is already on the list
		p := dht.network.Find(u.Key(prov.GetId()))
		if p == nil {
618
			u.DOut("given provider %s was not in our network already.\n", peer.ID(prov.GetId()).Pretty())
Jeromy's avatar
Jeromy committed
619 620
			var err error
			p, err = dht.peerFromInfo(prov)
Jeromy's avatar
Jeromy committed
621
			if err != nil {
622
				u.PErr("error connecting to new peer: %s\n", err)
Jeromy's avatar
Jeromy committed
623 624 625
				continue
			}
		}
626
		dht.providers.AddProvider(key, p)
627
		provArr = append(provArr, p)
Jeromy's avatar
Jeromy committed
628
	}
629
	return provArr
Jeromy's avatar
Jeromy committed
630
}
Jeromy's avatar
Jeromy committed
631 632 633 634 635 636 637 638 639

func (dht *IpfsDHT) peerFromInfo(pbp *PBDHTMessage_PBPeer) (*peer.Peer, error) {
	maddr, err := ma.NewMultiaddr(pbp.GetAddr())
	if err != nil {
		return nil, err
	}

	return dht.network.GetConnection(peer.ID(pbp.GetId()), maddr)
}