// Called when the DHT finds a peer func (self *DHT) ReceivePeers(r map[dht.InfoHash][]string) { for infoHash, results := range r { for _, p := range results { peerAddress := dht.DecodePeerAddress(p) logger.Debug("DHT Peer: %s", peerAddress) if self.Config.AddPeerCallback == nil { log.Panic("Must set callback for receiving DHT peers") } str, ok := self.InfoHashes[string(infoHash)] if ok != true { log.Panic("infohash not requested") } self.Config.AddPeerCallback(str, peerAddress) //_, err := peers.AddPeer(peer) //if err != nil { // logger.Info("Failed to add DHT peer: %v", err) //} } } }
// drainresults loops, printing the address of nodes it has found. func drainresults(n *dht.DHT) { fmt.Println("=========================== DHT") for r := range n.PeersRequestResults { for ih, peers := range r { for _, x := range peers { l4g.Warn("Found peer for infohash %x at %v", ih, dht.DecodePeerAddress(x)) } } } }
func checkPeers(n *dht.DHT, cm *network.ConnectionManager) { for r := range n.PeersRequestResults { for _, peers := range r { for _, x := range peers { address := dht.DecodePeerAddress(x) log.Printf("DHT Found Peer: %v\n", address) go attemptConnectionToPeer(address, cm) } } } }
func obtainPeers(d *dht.DHT, passphrase []byte, c chan Peer) { for r := range d.PeersRequestResults { for _, peers := range r { for _, x := range peers { // A DHT peer for our infohash was found. It // needs to be authenticated. checkPeer(dht.DecodePeerAddress(x), passphrase, c) } } } }
// Called when the DHT finds a peer func (self *DHT) ReceivePeers(r map[dht.InfoHash][]string, peers *pex.Pex) { for _, results := range r { for _, p := range results { peer := dht.DecodePeerAddress(p) logger.Debug("DHT Peer: %s", peer) _, err := peers.AddPeer(peer) if err != nil { logger.Info("Failed to add DHT peer: %v", err) } } } }
// drainresults loops, printing the address of nodes it has found. func drainresults(n *dht.DHT) { fmt.Println("=========================== DHT") l4g.Warn("Note that there are many bad nodes that reply to anything you ask.") l4g.Warn("Peers found:") for r := range n.PeersRequestResults { for _, peers := range r { for _, x := range peers { l4g.Warn("%v", dht.DecodePeerAddress(x)) } } } }
func downloadFile(infoHash dht.InfoHash, peerChannel <-chan string, eventsChannel chan<- downloadEvent) { //TODO: implement //TODO: get peers from buffered channel, connect to them, download torrent file //TODO: add some sure way to detect goroutine finished (defer send to channel?) peerCount := 0 tick := time.Tick(10 * time.Second) timeout := time.After(10 * time.Minute) for { select { case newPeer, chanOk := <-peerChannel: if !chanOk { log.V(2).Infof("WINSTON: Peer channel for %x was closed, probably by torrent timeout. Killing download goroutine...\n", infoHash) return } peerCount++ peerStr := dht.DecodePeerAddress(newPeer) if strings.HasSuffix(peerStr, ":1") { log.V(3).Infof("WINSTON: Skipping peer #%d for torrent %x for looking fake: %s\n", peerCount, infoHash, peerStr) continue } log.V(3).Infof("WINSTON: Peer #%d received for torrent %x: %s\n", peerCount, infoHash, peerStr) //TODO: run as paralel goroutines //TODO: taka care to have N parallel downloaders at all times, if possible //TODO: send requests for more peers periodically //TODO: gather results and stop everything once a successful result has been found torrent := peer.DownloadMetadataFromPeer(peerStr, string(infoHash)) if torrent != nil { log.V(1).Infof("WINSTON: Torrent %x really was downloaded!\n", infoHash) err := saveMetaInfo(string(infoHash), torrent) if err != nil { panic(fmt.Errorf("Could not save a simple file: %s", err)) } eventsChannel <- downloadEvent{infoHash, eventSucessfulDownload} return } log.V(1).Infof("WINSTON: Torrent %x was not downloaded, trying again...\n", infoHash) case <-tick: log.V(3).Infof("WINSTON: Tick-tack %x...\n", infoHash) case <-timeout: log.V(3).Infof("WINSTON: Torrent %x timed out...\n", infoHash) eventsChannel <- downloadEvent{infoHash, eventTimeout} return } } }
// drainresults loops, printing the address of nodes it has found. func drainresults(n *dht.DHT) { fmt.Println("=========================== DHT") for r := range n.PeersRequestResults { for ih, peers := range r { l4g.Warn("Found peer(s) for infohash %x:", ih) for _, x := range peers { l4g.Warn("==========================> %v", dht.DecodePeerAddress(x)) l4g.Warn("Note that there are many bad nodes that reply to anything you ask, so don't get too excited.") l4g.Warn("==========================") } } } }
func drain(n *dht.DHT) { count := 0 fmt.Println("=========================== DHT") fmt.Println("Note that there are many bad nodes that reply to anything you ask.") fmt.Println("Peers found:") for r := range n.PeersRequestResults { for _, peers := range r { for _, x := range peers { fmt.Printf("%d: %v\n", count, dht.DecodePeerAddress(x)) count++ } } } }
func (d *DiscoveryDHT) awaitPeers() { defer d.waitGroup.Done() ticker := time.NewTicker(time.Second) defer ticker.Stop() for d.Status() != StatusStopping { select { case r := <-d.node.PeersRequestResults: for _, peers := range r { for _, x := range peers { host := dht.DecodePeerAddress(x) d.addPeer(host) } } case <-ticker.C: } } }
func RunTorrents(flags *TorrentFlags, torrentFiles []string) (err error) { conChan, listenPort, err := ListenForPeerConnections(flags) if err != nil { log.Println("Couldn't listen for peers connection: ", err) return } quitChan := listenSigInt() createChan := make(chan string, flags.MaxActive) startChan := make(chan *TorrentSession, 1) doneChan := make(chan *TorrentSession, 1) var dhtNode dht.DHT if flags.UseDHT { dhtNode = *startDHT(flags.Port) } torrentSessions := make(map[string]*TorrentSession) go func() { for torrentFile := range createChan { ts, err := NewTorrentSession(flags, torrentFile, uint16(listenPort)) if err != nil { log.Println("Couldn't create torrent session for "+torrentFile+" .", err) doneChan <- &TorrentSession{} } else { log.Printf("Created torrent session for %s", ts.M.Info.Name) startChan <- ts } } }() torrentQueue := []string{} if len(torrentFiles) > flags.MaxActive { torrentQueue = torrentFiles[flags.MaxActive:] } for i, torrentFile := range torrentFiles { if i < flags.MaxActive { createChan <- torrentFile } else { break } } lpd := &Announcer{} if flags.UseLPD { lpd, err = NewAnnouncer(uint16(listenPort)) if err != nil { log.Println("Couldn't listen for Local Peer Discoveries: ", err) flags.UseLPD = false } } theWorldisEnding := false mainLoop: for { select { case ts := <-startChan: if !theWorldisEnding { ts.dht = &dhtNode if flags.UseLPD { lpd.Announce(ts.M.InfoHash) } torrentSessions[ts.M.InfoHash] = ts log.Printf("Starting torrent session for %s", ts.M.Info.Name) go func(t *TorrentSession) { t.DoTorrent() doneChan <- t }(ts) } case ts := <-doneChan: if ts.M != nil { delete(torrentSessions, ts.M.InfoHash) if flags.UseLPD { lpd.StopAnnouncing(ts.M.InfoHash) } } if !theWorldisEnding && len(torrentQueue) > 0 { createChan <- torrentQueue[0] torrentQueue = torrentQueue[1:] continue mainLoop } if len(torrentSessions) == 0 { break mainLoop } case <-quitChan: theWorldisEnding = true for _, ts := range torrentSessions { go ts.Quit() } case c := <-conChan: // log.Printf("New bt connection for ih %x", c.Infohash) if ts, ok := torrentSessions[c.Infohash]; ok { ts.AcceptNewPeer(c) } case dhtPeers := <-dhtNode.PeersRequestResults: for key, peers := range dhtPeers { if ts, ok := torrentSessions[string(key)]; ok { // log.Printf("Received %d DHT peers for torrent session %x\n", len(peers), []byte(key)) for _, peer := range peers { peer = dht.DecodePeerAddress(peer) ts.HintNewPeer(peer) } } else { log.Printf("Received DHT peer for an unknown torrent session %x\n", []byte(key)) } } case announce := <-lpd.Announces: hexhash, err := hex.DecodeString(announce.Infohash) if err != nil { log.Println("Err with hex-decoding:", err) } if ts, ok := torrentSessions[string(hexhash)]; ok { // log.Printf("Received LPD announce for ih %s", announce.Infohash) ts.HintNewPeer(announce.Peer) } } } if flags.UseDHT { dhtNode.Stop() } return }
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 }
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 } } }
func (cs *ControlSession) Run() { // deadlock heartbeat := make(chan struct{}, 1) quitDeadlock := make(chan struct{}) go cs.deadlockDetector(heartbeat, quitDeadlock) rechokeChan := time.Tick(10 * time.Second) verboseChan := time.Tick(10 * time.Minute) keepAliveChan := time.Tick(60 * time.Second) // Start out polling tracker every 20 seconds until we get a response. // Maybe be exponential backoff here? retrackerChan := time.Tick(20 * time.Second) trackerInfoChan := make(chan *TrackerResponse) trackerClient := NewTrackerClient("", [][]string{cs.trackers}) trackerClient.Announce(cs.makeClientStatusReport("started")) for { select { case <-retrackerChan: trackerClient.Announce(cs.makeClientStatusReport("")) case dhtInfoHashPeers := <-cs.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 cs.hintNewPeer(peer) { newPeerCount++ } } } case ti := <-trackerInfoChan: cs.logf("Got response from tracker: %#v\n", ti) newPeerCount := 0 for _, peer := range ti.Peers { if cs.hintNewPeer(peer) { newPeerCount++ } } for _, peer6 := range ti.Peers6 { if cs.hintNewPeer(peer6) { newPeerCount++ } } cs.log("Contacting", newPeerCount, "new peers") interval := ti.Interval if interval < 120 { interval = 120 } else if interval > 24*3600 { interval = 24 * 3600 } cs.log("..checking again in", interval, "seconds.") retrackerChan = time.Tick(interval * time.Second) cs.log("Contacting", newPeerCount, "new peers") case pm := <-cs.peerMessageChan: peer, message := pm.peer, pm.message peer.lastReadTime = time.Now() err2 := cs.DoMessage(peer, message) if err2 != nil { if err2 != io.EOF { cs.log("Closing peer", peer.address, "because", err2) } cs.ClosePeer(peer) } case <-rechokeChan: // TODO: recalculate who to choke / unchoke heartbeat <- struct{}{} if cs.peers.Len() < TARGET_NUM_PEERS { go cs.dht.PeersRequest(string(cs.ID.Infohash), true) } case <-verboseChan: cs.log("Peers:", cs.peers.Len()) case <-keepAliveChan: now := time.Now() for _, peer := range cs.peers.All() { if peer.lastReadTime.Second() != 0 && now.Sub(peer.lastReadTime) > 3*time.Minute { // log.Println("Closing peer", peer.address, "because timed out.") cs.ClosePeer(peer) continue } go peer.keepAlive(now) } case <-cs.quit: cs.log("Quitting torrent session") quitDeadlock <- struct{}{} return } } }