Ejemplo n.º 1
0
// The 'nodes' response is a string with fixed length contacts concatenated arbitrarily.
func parseNodesString(nodes string, proto string) (parsed map[string]string) {
	var nodeContactLen int
	if proto == "udp4" {
		nodeContactLen = v4nodeContactLen
	} else if proto == "udp6" {
		nodeContactLen = v6nodeContactLen
	} else {
		return
	}
	parsed = make(map[string]string)
	if len(nodes)%nodeContactLen > 0 {
		log.V(3).Infof("DHT: len(NodeString) = %d, INVALID LENGTH, should be a multiple of %d", len(nodes), nodeContactLen)
		log.V(5).Infof("%T %#v\n", nodes, nodes)
		return
	} else {
		log.V(5).Infof("DHT: len(NodeString) = %d, had %d nodes, nodeContactLen=%d\n", len(nodes), len(nodes)/nodeContactLen, nodeContactLen)
	}
	for i := 0; i < len(nodes); i += nodeContactLen {
		id := nodes[i : i+nodeIdLen]
		address := nettools.BinaryToDottedPort(nodes[i+nodeIdLen : i+nodeContactLen])
		parsed[id] = address
	}
	return

}
Ejemplo n.º 2
0
func stringToPeers(in string) (peers []string) {
	peers = make([]string, 0)
	for i := 0; i < len(in); i += PEER_LEN {
		peer := nettools.BinaryToDottedPort(in[i : i+PEER_LEN])
		peers = append(peers, peer)
	}

	return
}
Ejemplo n.º 3
0
func (r *routingTable) kill(n *remoteNode, p *peerStore) {
	delete(r.addresses, n.address.String())
	r.nTree.cut(InfoHash(n.id), 0)
	totalKilledNodes.Add(1)

	if r.boundaryNode != nil && n.id == r.boundaryNode.id {
		r.resetNeighborhoodBoundary()
	}
	p.killContact(nettools.BinaryToDottedPort(n.addressBinaryFormat))
}
Ejemplo n.º 4
0
func getTrackerInfo(url string) (tr *TrackerResponse, err error) {
	r, err := proxyHttpGet(url)
	if err != nil {
		return
	}
	defer r.Body.Close()
	if r.StatusCode >= 400 {
		data, _ := ioutil.ReadAll(r.Body)
		reason := "Bad Request " + string(data)
		log.Println(reason)
		err = errors.New(reason)
		return
	}

	var tr2 TrackerResponse
	err = bencode.NewDecoder(r.Body).Decode(&tr2)
	r.Body.Close()
	if err != nil {
		return
	}

	// Decode peers
	if len(tr2.PeersRaw) > 0 {
		const peerLen = 6
		nPeers := len(tr2.PeersRaw) / peerLen
		// log.Println("Tracker gave us", nPeers, "peers")
		tr2.Peers = make([]string, nPeers)
		for i := 0; i < nPeers; i++ {
			peer := nettools.BinaryToDottedPort(tr2.PeersRaw[i*peerLen : (i+1)*peerLen])
			tr2.Peers[i/peerLen] = peer
		}
	}

	// Decode peers6

	if len(tr2.Peers6Raw) > 0 {
		const peerLen = 18
		nPeers := len(tr2.Peers6Raw) / peerLen
		// log.Println("Tracker gave us", nPeers, "IPv6 peers")
		tr2.Peers6 = make([]string, nPeers)
		for i := 0; i < nPeers; i++ {
			peerEntry := tr2.Peers6Raw[i*peerLen : (i+1)*peerLen]
			host := net.IP(peerEntry[0:16])
			port := int((uint(peerEntry[16]) << 8) | uint(peerEntry[17]))
			peer := net.JoinHostPort(host.String(), strconv.Itoa(port))
			tr2.Peers6[i] = peer
		}
	}

	tr = &tr2
	return
}
Ejemplo n.º 5
0
func (r *routingTable) addNewNeighbor(n *remoteNode, displaceBoundary bool, proto string) {
	if err := r.insert(n, proto); err != nil {
		log.V(3).Infof("addNewNeighbor error: %v", err)
		return
	}
	if displaceBoundary && r.boundaryNode != nil {
		// This will also take care of setting a new boundary.
		r.kill(r.boundaryNode)
	} else {
		r.resetNeighborhoodBoundary()
	}
	log.V(4).Infof("New neighbor added %s with proximity %d", nettools.BinaryToDottedPort(n.addressBinaryFormat), r.proximity)
}
Ejemplo n.º 6
0
// The 'nodes' response is a string with fixed length contacts concatenated arbitrarily.
func parseNodesString(nodes string) (parsed map[string]string) {
	parsed = make(map[string]string)
	if len(nodes)%nodeContactLen > 0 {
		l4g.Info("DHT: Invalid length of nodes.")
		l4g.Info("DHT: Should be a multiple of %d, got %d", nodeContactLen, len(nodes))
		return
	}
	for i := 0; i < len(nodes); i += nodeContactLen {
		id := nodes[i : i+nodeIdLen]
		address := nettools.BinaryToDottedPort(nodes[i+nodeIdLen : i+nodeContactLen])
		parsed[id] = address
	}
	return

}
Ejemplo n.º 7
0
// DecodePeerAddress transforms the binary-encoded host:port address into a
// human-readable format. So, "abcdef" becomes 97.98.99.100:25958.
func DecodePeerAddress(x string) string {
	return nettools.BinaryToDottedPort(x)
}
Ejemplo n.º 8
0
func (t *TorrentSession) DoTorrent() (err error) {
	t.lastHeartBeat = time.Now()
	go t.deadlockDetector()
	log.Println("Fetching torrent.")
	rechokeChan := time.Tick(1 * time.Second)
	// Start out polling tracker every 20 seconds untill we get a response.
	// Maybe be exponential backoff here?
	retrackerChan := time.Tick(20 * time.Second)
	keepAliveChan := time.Tick(60 * time.Second)
	t.trackerInfoChan = make(chan *TrackerResponse)

	conChan := make(chan net.Conn)
	if useProxy() {
		// Only listen for peer connections if not using a proxy.
		t.listenForPeerConnections(conChan)
	}

	if t.m.Info.Private != 1 && useDHT {
		t.dht.PeersRequest(t.m.InfoHash, true)
	}

	t.fetchTrackerInfo("started")

	for {
		select {
		case _ = <-retrackerChan:
			if !trackerLessMode {
				t.fetchTrackerInfo("")
			}
		case dhtInfoHashPeers := <-t.dht.PeersRequestResults:
			newPeerCount := 0
			// key = infoHash. The torrent client currently only
			// supports one download at a time, so let's assume
			// it's the case.
			for _, peers := range dhtInfoHashPeers {
				for _, peer := range peers {
					peer = dht.DecodePeerAddress(peer)
					if _, ok := t.peers[peer]; !ok {
						newPeerCount++
						go connectToPeer(peer, conChan)
					}
				}
			}
			// log.Println("Contacting", newPeerCount, "new peers (thanks DHT!)")
		case ti := <-t.trackerInfoChan:
			t.ti = ti
			log.Println("Torrent has", t.ti.Complete, "seeders and", t.ti.Incomplete, "leachers.")
			if !trackerLessMode {
				peers := t.ti.Peers
				log.Println("Tracker gave us", len(peers)/6, "peers")
				newPeerCount := 0
				for i := 0; i < len(peers); i += 6 {
					peer := nettools.BinaryToDottedPort(peers[i : i+6])
					if _, ok := t.peers[peer]; !ok {
						newPeerCount++
						go connectToPeer(peer, conChan)
					}
				}
				log.Println("Contacting", newPeerCount, "new peers")
				interval := t.ti.Interval
				if interval < 120 {
					interval = 120
				} else if interval > 24*3600 {
					interval = 24 * 3600
				}
				log.Println("..checking again in", interval, "seconds.")
				retrackerChan = time.Tick(interval * time.Second)
				log.Println("Contacting", newPeerCount, "new peers")
			}
			interval := t.ti.Interval
			if interval < 120 {
				interval = 120
			} else if interval > 24*3600 {
				interval = 24 * 3600
			}
			log.Println("..checking again in", interval.String())
			retrackerChan = time.Tick(interval * time.Second)

		case pm := <-t.peerMessageChan:
			peer, message := pm.peer, pm.message
			peer.lastReadTime = time.Now()
			err2 := t.DoMessage(peer, message)
			if err2 != nil {
				if err2 != io.EOF {
					log.Println("Closing peer", peer.address, "because", err2)
				}
				t.ClosePeer(peer)
			}
		case conn := <-conChan:
			t.AddPeer(conn)
		case _ = <-rechokeChan:
			// TODO: recalculate who to choke / unchoke
			t.lastHeartBeat = time.Now()
			ratio := float64(0.0)
			if t.si.Downloaded > 0 {
				ratio = float64(t.si.Uploaded) / float64(t.si.Downloaded)
			}
			log.Println("Peers:", len(t.peers), "downloaded:", t.si.Downloaded,
				"uploaded:", t.si.Uploaded, "ratio", ratio)
			log.Println("good, total", t.goodPieces, t.totalPieces)
			if len(t.peers) < TARGET_NUM_PEERS && t.goodPieces < t.totalPieces {
				if t.m.Info.Private != 1 && useDHT {
					go t.dht.PeersRequest(t.m.InfoHash, true)
				}
				if !trackerLessMode {
					if t.ti == nil || t.ti.Complete > 100 {
						t.fetchTrackerInfo("")
					}
				}
			}
		case _ = <-keepAliveChan:
			now := time.Now()
			for _, peer := range t.peers {
				if peer.lastReadTime.Second() != 0 && now.Sub(peer.lastReadTime) > 3*time.Minute {
					// log.Println("Closing peer", peer.address, "because timed out.")
					t.ClosePeer(peer)
					continue
				}
				err2 := t.doCheckRequests(peer)
				if err2 != nil {
					if err2 != io.EOF {
						log.Println("Closing peer", peer.address, "because", err2)
					}
					t.ClosePeer(peer)
					continue
				}
				peer.keepAlive(now)
			}
		}
	}
	return
}
Ejemplo n.º 9
0
// Requires Internet access and can be flaky if the server or the internet is
// slow.
func TestDHTLarge(t *testing.T) {
	if testing.Short() {
		t.Skip("TestDHTLarge requires internet access and can be flaky. Skipping in short mode.")
	}
	defer stats(t)
	c := NewConfig()
	c.SaveRoutingTable = false
	node, err := New(c)
	if err != nil {
		t.Fatalf("dht New: %v", err)
	}
	if err = node.Start(); err != nil {
		t.Fatalf("node.Run: %v", err)
	}
	realDHTNodes := []string{
		"1.a.magnets.im",
		"router.utorrent.com",
	}
	for _, addr := range realDHTNodes {
		ip, err := net.LookupHost(addr)
		if err != nil {
			t.Error(err)
			continue
		}
		node.AddNode(ip[0] + ":6881")
	}

	// Test that we can reach at least one node.
	success := false
	var (
		reachable int
		v         expvar.Var
	)
	for i := 0; i < 10; i++ {
		v = expvar.Get("totalNodesReached")
		reachable, err = strconv.Atoi(v.String())
		if err != nil {
			t.Errorf("totalNodesReached conversion to int failed: %v", err)
			continue
		}
		if reachable > 0 {
			t.Logf("Contacted %d DHT nodes.", reachable)
			success = true
			break
		}
		time.Sleep(time.Second)
	}
	if !success {
		t.Fatal("No external DHT node could be contacted.")
	}

	// Test that we can find peers for a known torrent in a timely fashion.
	//
	// Torrent from: http://www.clearbits.net/torrents/244-time-management-for-anarchists-1
	infoHash := InfoHash("\xb4\x62\xc0\xa8\xbc\xef\x1c\xe5\xbb\x56\xb9\xfd\xb8\xcf\x37\xff\xd0\x2f\x5f\x59")
	go node.PeersRequest(string(infoHash), true)
	var infoHashPeers map[InfoHash][]string
	select {
	case infoHashPeers = <-node.PeersRequestResults:
		t.Logf("Found %d peers.", len(infoHashPeers[infoHash]))
	case <-time.Tick(10 * time.Second):
		t.Fatal("Could not find new peers: timed out")
	}
	for ih, peers := range infoHashPeers {
		if infoHash != ih {
			t.Fatal("Unexpected infohash returned")
		}
		if len(peers) == 0 {
			t.Fatal("Could not find new torrent peers.")
		}
		for _, peer := range peers {
			t.Logf("peer found: %v", nettools.BinaryToDottedPort(peer))
		}
	}
}
Ejemplo n.º 10
0
// Process another node's response to a find_node query.
func (d *DHT) processFindNodeResults(node *remoteNode, resp responseType) {
	var nodelist string
	totalRecvFindNodeReply.Add(1)

	query, _ := node.pendingQueries[resp.T]
	if d.config.UDPProto == "udp4" {
		nodelist = resp.R.Nodes
	} else if d.config.UDPProto == "udp6" {
		nodelist = resp.R.Nodes6
	}
	log.V(5).Infof("processFindNodeResults find_node = %s len(nodelist)=%d", nettools.BinaryToDottedPort(node.addressBinaryFormat), len(nodelist))

	if nodelist != "" {
		for id, address := range parseNodesString(nodelist, d.config.UDPProto) {
			_, addr, existed, err := d.routingTable.hostPortToNode(address, d.config.UDPProto)
			if err != nil {
				log.V(3).Infof("DHT error parsing node from find_find response: %v", err)
				continue
			}
			if id == d.nodeId {
				log.V(5).Infof("DHT got reference of self for find_node, id %x", id)
				continue
			}
			if addr == node.address.String() {
				// SelfPromotions are more common for find_node. They are
				// happening even for router.bittorrent.com
				totalSelfPromotions.Add(1)
				continue
			}
			if existed {
				if log.V(4) {
					x := hashDistance(query.ih, InfoHash(node.id))
					log.Infof("DHT: processFindNodeResults DUPE node reference, query %x: %x@%v from %x@%v. Distance: %x.",
						query.ih, id, address, node.id, node.address, x)
				}
				totalFindNodeDupes.Add(1)
			} else {
				if log.V(4) {
					x := hashDistance(query.ih, InfoHash(node.id))
					log.Infof("DHT: Got new node reference, query %x: %x@%v from %x@%v. Distance: %x.",
						query.ih, id, address, node.id, node.address, x)
				}
				// Includes the node in the routing table and ignores errors.
				//
				// Only continue the search if we really have to.
				r, err := d.routingTable.getOrCreateNode(id, addr, d.config.UDPProto)
				if err != nil {
					log.Warningf("processFindNodeResults calling getOrCreateNode: %v. Id=%x, Address=%q", err, id, addr)
					continue
				}
				if d.needMoreNodes() {
					select {
					case d.nodesRequest <- ihReq{query.ih, false}:
					default:
						// Too many find_node commands queued up. Dropping
						// this. The node has already been added to the
						// routing table so we're not losing any
						// information.
					}
				}
				d.getMorePeers(r)
			}
		}
	}
}
Ejemplo n.º 11
0
func (ts *TorrentSession) DoTorrent() {
	ts.heartbeat = make(chan bool, 1)
	if ts.flags.UseDeadlockDetector {
		go ts.deadlockDetector()
	}

	if ts.flags.Cacher != nil && ts.fileStore != nil {
		cache := ts.flags.Cacher.NewCache(ts.M.InfoHash, ts.totalPieces, int(ts.M.Info.PieceLength), ts.totalSize)
		ts.fileStore.SetCache(cache)
	}

	heartbeatDuration := 1 * time.Second
	heartbeatChan := time.Tick(heartbeatDuration)

	keepAliveChan := time.Tick(60 * time.Second)
	var retrackerChan <-chan time.Time
	ts.hintNewPeerChan = make(chan string)
	ts.addPeerChan = make(chan *BtConn)
	if !ts.trackerLessMode {
		// Start out polling tracker every 20 seconds until we get a response.
		// Maybe be exponential backoff here?
		retrackerChan = time.Tick(20 * time.Second)
		ts.trackerInfoChan = make(chan *TrackerResponse)
		ts.trackerReportChan = make(chan ClientStatusReport)
		startTrackerClient(ts.flags.Dial, ts.M.Announce, ts.M.AnnounceList, ts.trackerInfoChan, ts.trackerReportChan)
	}

	if ts.Session.UseDHT {
		ts.dht.PeersRequest(ts.M.InfoHash, true)
	}

	if !ts.trackerLessMode && ts.Session.HaveTorrent {
		ts.fetchTrackerInfo("started")
	}

	defer ts.Shutdown()

	lastDownloaded := ts.Session.Downloaded

	for {
		if !ts.execOnSeedingDone && ts.goodPieces == ts.totalPieces {
			ts.execOnSeeding()
			ts.execOnSeedingDone = true
		}
		select {
		case <-ts.chokePolicyHeartbeat:
			ts.chokePeers()
		case hintNewPeer := <-ts.hintNewPeerChan:
			ts.tryNewPeer(hintNewPeer)
		case btconn := <-ts.addPeerChan:
			ts.addPeerImp(btconn)
		case <-retrackerChan:
			if !ts.trackerLessMode {
				ts.fetchTrackerInfo("")
			}
		case ti := <-ts.trackerInfoChan:
			ts.ti = ti
			log.Println("[", ts.M.Info.Name, "] Torrent has", ts.ti.Complete, "seeders and", ts.ti.Incomplete, "leachers")
			if !ts.trackerLessMode {
				newPeerCount := 0
				{
					peers := ts.ti.Peers
					if len(peers) > 0 {
						const peerLen = 6
						log.Println("[", ts.M.Info.Name, "] Tracker gave us", len(peers)/peerLen, "peers")
						for i := 0; i < len(peers); i += peerLen {
							peer := nettools.BinaryToDottedPort(peers[i : i+peerLen])
							if ts.tryNewPeer(peer) {
								newPeerCount++
							}
						}
					}
				}
				{
					peers6 := ts.ti.Peers6
					if len(peers6) > 0 {
						const peerLen = 18
						log.Println("[", ts.M.Info.Name, "] Tracker gave us", len(peers6)/peerLen, "IPv6 peers")
						for i := 0; i < len(peers6); i += peerLen {
							peerEntry := peers6[i : i+peerLen]
							host := net.IP(peerEntry[0:16])
							port := int((uint(peerEntry[16]) << 8) | uint(peerEntry[17]))
							peer := net.JoinHostPort(host.String(), strconv.Itoa(port))
							if ts.tryNewPeer(peer) {
								newPeerCount++
							}
						}
					}
				}
				log.Println("[", ts.M.Info.Name, "] Contacting", newPeerCount, "new peers")
			}

			interval := ts.ti.Interval
			minInterval := uint(120)
			maxInterval := uint(24 * 3600)
			if interval < minInterval {
				interval = minInterval
			} else if interval > maxInterval {
				interval = maxInterval
			}
			log.Println("[", ts.M.Info.Name, "] ..checking again in", interval, "seconds")
			retrackerChan = time.Tick(time.Duration(interval) * time.Second)

		case pm := <-ts.peerMessageChan:
			peer, message := pm.peer, pm.message
			peer.lastReadTime = time.Now()
			err2 := ts.DoMessage(peer, message)
			if err2 != nil {
				if err2 != io.EOF {
					log.Println("[", ts.M.Info.Name, "] Closing peer", peer.address, "because", err2)
				}
				ts.ClosePeer(peer)
			}
		case <-heartbeatChan:
			if ts.flags.UseDeadlockDetector {
				ts.heartbeat <- true
			}
			ratio := float64(0.0)
			if ts.Session.Downloaded > 0 {
				ratio = float64(ts.Session.Uploaded) / float64(ts.Session.Downloaded)
			}
			speed := humanSize(float64(ts.Session.Downloaded-lastDownloaded) / heartbeatDuration.Seconds())
			lastDownloaded = ts.Session.Downloaded
			log.Printf("[ %s ] Peers: %d downloaded: %d (%s/s) uploaded: %d ratio: %f pieces: %d/%d\n",
				ts.M.Info.Name,
				len(ts.peers),
				ts.Session.Downloaded,
				speed,
				ts.Session.Uploaded,
				ratio,
				ts.goodPieces,
				ts.totalPieces)
			if ts.totalPieces != 0 && ts.goodPieces == ts.totalPieces && ratio >= ts.flags.SeedRatio {
				log.Println("[", ts.M.Info.Name, "] Achieved target seed ratio", ts.flags.SeedRatio)
				return
			}
			if len(ts.peers) < TARGET_NUM_PEERS && (ts.totalPieces == 0 || ts.goodPieces < ts.totalPieces) {
				if ts.Session.UseDHT {
					go ts.dht.PeersRequest(ts.M.InfoHash, true)
				}
				if !ts.trackerLessMode {
					if ts.ti == nil || ts.ti.Complete > 100 {
						ts.fetchTrackerInfo("")
					}
				}
			}
		case <-keepAliveChan:
			now := time.Now()
			for _, peer := range ts.peers {
				if peer.lastReadTime.Second() != 0 && now.Sub(peer.lastReadTime) > 3*time.Minute {
					// log.Println("[", ts.M.Info.Name, "] Closing peer", peer.address, "because timed out")
					ts.ClosePeer(peer)
					continue
				}
				err2 := ts.doCheckRequests(peer)
				if err2 != nil {
					if err2 != io.EOF {
						log.Println("[", ts.M.Info.Name, "] Closing peer", peer.address, "because", err2)
					}
					ts.ClosePeer(peer)
					continue
				}
				peer.keepAlive(now)
			}

		case <-ts.quit:
			log.Println("[", ts.M.Info.Name, "] Quitting torrent session")
			return
		}
	}
}
Ejemplo n.º 12
0
func (t *TorrentSession) DoTorrent() (err error) {
	t.lastHeartBeat = time.Now()

	log.Println("Fetching torrent.")
	rechokeChan := time.Tick(time.Duration(cfg.rechokeTick) * time.Second)
	// Start out polling tracker every 20 seconds untill we get a response.
	// Maybe be exponential backoff here?
	retrackerChan := time.Tick(20 * time.Second)
	keepAliveChan := time.Tick(60 * time.Second)
	t.trackerInfoChan = make(chan *TrackerResponse)

	conChan := make(chan net.Conn)
	if !useProxy() {
		// Only listen for peer connections if not using a proxy.
		t.listenForPeerConnections(conChan)
	}

	DHTPeersRequestResults := make(chan map[string][]string)
	if t.m.Info.Private != 1 && cfg.useDHT {
		DHTPeersRequestResults = t.dht.PeersRequestResults
		t.dht.PeersRequest(t.m.InfoHash, true)
	}

	if !t.isSeeding() {
		t.fetchTrackerInfo("completed")
	}else{
		t.fetchTrackerInfo("started")
	}

	for {
		select {
		case _ = <-retrackerChan:
			if !cfg.trackerLessMode {
				t.fetchTrackerInfo("")
			}
		case dhtInfoHashPeers := <-DHTPeersRequestResults:
			newPeerCount := 0
			// key = infoHash. The torrent client currently only
			// supports one download at a time, so let's assume
			// it's the case.
			for _, peers := range dhtInfoHashPeers {
				for _, peer := range peers {
					peer = nettools.BinaryToDottedPort(peer)
					if !t.PeerExist(peer) {
						newPeerCount++
						go connectToPeer(peer, conChan)
					}
				}
			}
			// log.Println("Contacting", newPeerCount, "new peers (thanks DHT!)")
		case ti := <-t.trackerInfoChan:
			if t.isSeeding() {
				break
			}

			t.ti = ti
			//log.Println("Torrent has", t.ti.Complete, "seeders and", t.ti.Incomplete, "leachers.")
			if !cfg.trackerLessMode {
				peers := t.ti.Peers
				//log.Println("Tracker gave us", len(peers)/6, "peers")
				newPeerCount := 0
				for i := 0; i < len(peers); i += 6 {
					peer := nettools.BinaryToDottedPort(peers[i : i+6])
					//log.Printf("trackerInfoChan %v\n", peer)
					selfIp, err := GetLocalIP(); 
					if err != nil{
						log.Println(err)
						panic("")
					}
					
					if !t.PeerExist(peer) && !strings.Contains(peer, selfIp) && t.PeerCount() < cfg.TARGET_NUM_PEERS{
						newPeerCount++
						go connectToPeer(peer, conChan)
					}
				}
				//log.Println("Contacting", newPeerCount, "new peers")
				interval := t.ti.Interval
				
				//log.Println("..checking again in", interval, "seconds.")
				retrackerChan = time.Tick(interval * time.Second)
				//log.Println("Contacting", newPeerCount, "new peers")
			}
			interval := t.ti.Interval
			//log.Println("..checking again in", interval.String())
			retrackerChan = time.Tick(interval * time.Second)

		case pm := <-t.peerMessageChan:
			peer, message := pm.peer, pm.message
			peer.lastReadTime = time.Now()
			err2 := t.DoMessage(peer, message)
			if err2 != nil {
				if err2 != io.EOF {
					log.Println("Closing peer", peer.address, "because", err2)
				}
				t.ClosePeer(peer)
			}
		case conn := <-conChan:
			t.AddPeer(conn)
		case ioResult := <-t.ioResponceChan:
			if _, ok := ioResult.(*WriteContext); ok {
				
			}else if context, ok := ioResult.(*ReadContext); ok {
				value := &CacheValue{}
				value.buf = append(value.buf, context.msgBuf[9:]...)

				t.cache.Set(context.globalOffset, value)
				//make sure the peer is still alive
				for _, v := range t.peers{
					if v == context.peer{
						context.peer.sendMessage(context.msgBuf)
						t.si.Uploaded += int64(context.length)
					}
				}
			}else{
				panic("Unknown message")
			}	
		case _ = <-rechokeChan:
			/*
			t.lastHeartBeat = time.Now()
			ratio := float64(0.0)
			if t.si.Downloaded > 0 {
				ratio = float64(t.si.Uploaded) / float64(t.si.Downloaded)
			}
			log.Println("Peers:", t.PeerCount(), "downloaded:", t.si.Downloaded, "uploaded:", t.si.Uploaded, "ratio", ratio)
			log.Println("good, total", t.goodPieces, t.totalPieces)
			*/
			if t.PeerCount() < cfg.TARGET_NUM_PEERS && t.goodPieces < t.totalPieces {
				if t.m.Info.Private != 1 && cfg.useDHT {
					go t.dht.PeersRequest(t.m.InfoHash, true)
				}
				if !cfg.trackerLessMode {
					if t.ti == nil || t.ti.Complete > 100 {
						t.fetchTrackerInfo("")
					}
				}
			}

			t.SchedulerChokeUnchoke()

		case _ = <-keepAliveChan:
			now := time.Now()
			for _, peer := range t.peers {
				if peer.lastReadTime.Second() != 0 && now.Sub(peer.lastReadTime) > 3*time.Minute {
					log.Println("Closing peer", peer.address, "because timed out.")
					t.ClosePeer(peer)
					continue
				}
				err2 := t.doCheckRequests(peer)
				if err2 != nil {
					if err2 != io.EOF {
						log.Println("Closing peer", peer.address, "because", err2)
					}
					t.ClosePeer(peer)
					continue
				}
				peer.keepAlive(now)
			}
		}
	}
	return
}
Ejemplo n.º 13
0
func (t *TorrentSession) DoTorrent() {
	t.heartbeat = make(chan bool, 1)
	if t.flags.UseDeadlockDetector {
		go t.deadlockDetector()
	}
	log.Println("Fetching torrent.")
	heartbeatChan := time.Tick(1 * time.Second)
	keepAliveChan := time.Tick(60 * time.Second)
	var retrackerChan <-chan time.Time
	t.hintNewPeerChan = make(chan string)
	t.addPeerChan = make(chan *btConn)
	if !t.trackerLessMode {
		// Start out polling tracker every 20 seconds until we get a response.
		// Maybe be exponential backoff here?
		retrackerChan = time.Tick(20 * time.Second)
		t.trackerInfoChan = make(chan *TrackerResponse)
		t.trackerReportChan = make(chan ClientStatusReport)
		startTrackerClient(t.flags.Dial, t.M.Announce, t.M.AnnounceList, t.trackerInfoChan, t.trackerReportChan)
	}

	if t.si.UseDHT {
		t.dht.PeersRequest(t.M.InfoHash, true)
	}

	if !t.trackerLessMode && t.si.HaveTorrent {
		t.fetchTrackerInfo("started")
	}

	defer t.Shutdown()

	for {
		var peersRequestResults chan map[dht.InfoHash][]string
		peersRequestResults = nil
		if t.dht != nil {
			peersRequestResults = t.dht.PeersRequestResults
		}
		select {
		case <-t.chokePolicyHeartbeat:
			t.chokePeers()
		case hintNewPeer := <-t.hintNewPeerChan:
			t.hintNewPeerImp(hintNewPeer)
		case btconn := <-t.addPeerChan:
			t.addPeerImp(btconn)
		case <-retrackerChan:
			if !t.trackerLessMode {
				t.fetchTrackerInfo("")
			}
		case dhtInfoHashPeers := <-peersRequestResults:
			newPeerCount := 0
			// key = infoHash. The torrent client currently only
			// supports one download at a time, so let's assume
			// it's the case.
			for _, peers := range dhtInfoHashPeers {
				for _, peer := range peers {
					peer = dht.DecodePeerAddress(peer)
					if t.mightAcceptPeer(peer) {
						newPeerCount++
						if t.si.HaveTorrent {
							go t.connectToPeer(peer)
						}
					}
				}
			}
			// log.Println("Contacting", newPeerCount, "new peers (thanks DHT!)")
		case ti := <-t.trackerInfoChan:
			t.ti = ti
			log.Println("Torrent has", t.ti.Complete, "seeders and", t.ti.Incomplete, "leachers.")
			if !t.trackerLessMode {
				newPeerCount := 0
				{
					peers := t.ti.Peers
					if len(peers) > 0 {
						const peerLen = 6
						log.Println("Tracker gave us", len(peers)/peerLen, "peers")
						for i := 0; i < len(peers); i += peerLen {
							peer := nettools.BinaryToDottedPort(peers[i : i+peerLen])
							if t.mightAcceptPeer(peer) {
								newPeerCount++
								go t.connectToPeer(peer)
							}
						}
					}
				}
				{
					peers6 := t.ti.Peers6
					if len(peers6) > 0 {
						const peerLen = 18
						log.Println("Tracker gave us", len(peers6)/peerLen, "IPv6 peers")
						for i := 0; i < len(peers6); i += peerLen {
							peerEntry := peers6[i : i+peerLen]
							host := net.IP(peerEntry[0:16])
							port := int((uint(peerEntry[16]) << 8) | uint(peerEntry[17]))
							peer := net.JoinHostPort(host.String(), strconv.Itoa(port))
							if t.mightAcceptPeer(peer) {
								newPeerCount++
								go t.connectToPeer(peer)
							}
						}
					}
				}
				log.Println("Contacting", newPeerCount, "new peers")
			}

			interval := t.ti.Interval
			minInterval := uint(120)
			maxInterval := uint(24 * 3600)
			if interval < minInterval {
				interval = minInterval
			} else if interval > maxInterval {
				interval = maxInterval
			}
			log.Println("..checking again in", interval, "seconds.")
			retrackerChan = time.Tick(time.Duration(interval) * time.Second)

		case pm := <-t.peerMessageChan:
			peer, message := pm.peer, pm.message
			peer.lastReadTime = time.Now()
			err2 := t.DoMessage(peer, message)
			if err2 != nil {
				if err2 != io.EOF {
					log.Println("Closing peer", peer.address, "because", err2)
				}
				t.ClosePeer(peer)
			}
		case <-heartbeatChan:
			if t.flags.UseDeadlockDetector {
				t.heartbeat <- true
			}
			ratio := float64(0.0)
			if t.si.Downloaded > 0 {
				ratio = float64(t.si.Uploaded) / float64(t.si.Downloaded)
			}
			log.Println("Peers:", len(t.peers), "downloaded:", t.si.Downloaded,
				"uploaded:", t.si.Uploaded, "ratio", ratio)
			log.Println("good, total", t.goodPieces, t.totalPieces)
			if t.goodPieces == t.totalPieces && ratio >= t.flags.SeedRatio {
				log.Println("Achieved target seed ratio", t.flags.SeedRatio)
				return
			}
			if len(t.peers) < TARGET_NUM_PEERS && (t.totalPieces == 0 || t.goodPieces < t.totalPieces) {
				if t.si.UseDHT {
					go t.dht.PeersRequest(t.M.InfoHash, true)
				}
				if !t.trackerLessMode {
					if t.ti == nil || t.ti.Complete > 100 {
						t.fetchTrackerInfo("")
					}
				}
			}
		case <-keepAliveChan:
			now := time.Now()
			for _, peer := range t.peers {
				if peer.lastReadTime.Second() != 0 && now.Sub(peer.lastReadTime) > 3*time.Minute {
					// log.Println("Closing peer", peer.address, "because timed out.")
					t.ClosePeer(peer)
					continue
				}
				err2 := t.doCheckRequests(peer)
				if err2 != nil {
					if err2 != io.EOF {
						log.Println("Closing peer", peer.address, "because", err2)
					}
					t.ClosePeer(peer)
					continue
				}
				peer.keepAlive(now)
			}

		case <-t.quit:
			log.Println("Quitting torrent session")
			return
		}
	}
}
Ejemplo n.º 14
0
// Requires Internet access and can be flaky if the server or the internet is
// slow.
func TestDHTLarge(t *testing.T) {
	if testing.Short() {
		t.Skip("TestDHTLarge requires internet access and can be flaky. Skipping in short mode.")
	}
	defer stats(t)
	c := NewConfig()
	c.Port = 6060 // ...
	//c.SaveRoutingTable = false
	node, err := New(c)
	if err != nil {
		t.Fatalf("dht New: %v", err)
	}
	go node.Run()
	realDHTNodes := []string{
		"1.a.magnets.im",
		"router.utorrent.com",
	}
	for _, addr := range realDHTNodes {
		ip, err := net.LookupHost(addr)
		if err != nil {
			t.Error(err)
			continue
		}
		node.AddNode(ip[0]+":6881", "")
	}

	node.AddNode("117.78.1.52:6891", "")
	node.AddNode("88.183.138.12:52804", "")

	// Test that we can reach at least one node.
	success := false
	var (
		reachable int
		v         expvar.Var
	)
	for i := 0; i < 10; i++ {
		v = expvar.Get("totalNodesReached")
		reachable, err = strconv.Atoi(v.String())
		if err != nil {
			t.Errorf("totalNodesReached conversion to int failed: %v", err)
			continue
		}
		if reachable > 0 {
			t.Logf("Contacted %d DHT nodes.", reachable)
			success = true
			break
		}
		time.Sleep(time.Second)
	}
	if !success {
		t.Fatal("No external DHT node could be contacted.")
	}

	// Test that we can find peers for a known torrent in a timely fashion.
	//
	// Torrent from: http://www.clearbits.net/torrents/244-time-management-for-anarchists-1
	//infoHash := InfoHash("\xb4\x62\xc0\xa8\xbc\xef\x1c\xe5\xbb\x56\xb9\xfd\xb8\xcf\x37\xff\xd0\x2f\x5f\x59")
	infoHash := InfoHash("\xc0\x66\x42\x34\xb4\x4f\x25\xde\x6c\xc7\xb5\x36\xa7\x98\xc6\x5f\x85\x80\x79\xcb")
	//c0 66 42 34 b4 4f 25 de 6c c7 b5 36 a7 98 c6 5f 85 80 79 cb
	go node.PeersRequest(infoHash, true)
	timeout := make(chan bool, 1)
	go func() {
		time.Sleep(30 * time.Second)
		timeout <- true
	}()
	var infoHashPeers map[InfoHash][]string
	select {
	case infoHashPeers = <-node.PeersRequestResults:
		t.Logf("Found %d peers.", len(infoHashPeers[infoHash]))
	case <-timeout:
		t.Fatal("Could not find new peers: timed out")
	}
	for ih, peers := range infoHashPeers {
		if infoHash != ih {
			t.Fatal("Unexpected infohash returned")
		}
		if len(peers) == 0 {
			t.Fatal("Could not find new torrent peers.")
		}
		for _, peer := range peers {
			t.Logf("peer found: %v", nettools.BinaryToDottedPort(peer))
		}
	}
}
Ejemplo n.º 15
0
// Requires Internet access and can be flaky if the server or the internet is
// slow.
func TestDHTLarge(t *testing.T) {
	node := startDHTNode(t)
	realDHTNodes := []string{
		"1.a.magnets.im",
	}
	for _, addr := range realDHTNodes {
		ip, err := net.LookupHost(addr)
		if err != nil {
			t.Error(err)
			continue
		}
		node.AddNode(ip[0] + ":6881")
	}

	// Test that we can reach at least one node.
	success := false
	var (
		reachable int
		v         expvar.Var
		err       error
	)
	for i := 0; i < 10; i++ {
		v = expvar.Get("totalReachableNodes")
		reachable, err = strconv.Atoi(v.String())
		if err != nil {
			t.Errorf("totalReachableNodes conversion to int failed: %v", err)
			continue
		}
		if reachable > 0 {
			t.Logf("Contacted %d DHT nodes.", reachable)
			success = true
			break
		}
		time.Sleep(time.Second)
	}
	if !success {
		t.Fatal("No external DHT node could be contacted.")
	}

	// Test that we can find peers for a known torrent in a timely fashion.
	//
	// Torrent from: http://www.clearbits.net/torrents/244-time-management-for-anarchists-1
	infoHash := InfoHash("\xb4\x62\xc0\xa8\xbc\xef\x1c\xe5\xbb\x56\xb9\xfd\xb8\xcf\x37\xff\xd0\x2f\x5f\x59")
	go node.PeersRequest(string(infoHash), true)
	timeout := make(chan bool, 1)
	go func() {
		time.Sleep(10 * time.Second)
		timeout <- true
	}()
	var infoHashPeers map[InfoHash][]string
	select {
	case infoHashPeers = <-node.PeersRequestResults:
		t.Logf("Found %d peers.", len(infoHashPeers[infoHash]))
	case <-timeout:
		t.Fatal("Could not find new peers: timed out")
	}
	for ih, peers := range infoHashPeers {
		if infoHash != ih {
			t.Fatal("Unexpected infohash returned")
		}
		if len(peers) == 0 {
			t.Fatal("Could not find new torrent peers.")
		}
		for _, peer := range peers {
			t.Logf("peer found: %v", nettools.BinaryToDottedPort(peer))
		}
	}
	t.Logf("=== Stats ===")
	t.Logf("totalReachableNodes: %v", totalReachableNodes)
	t.Logf("totalDupes: %v", totalDupes)
	t.Logf("totalPeers: %v", totalPeers)
	t.Logf("totalSentGetPeers: %v", totalSentGetPeers)
}