func downloadManager(d *dht.DHT, filesToDownload <-chan string, finished chan<- bool) { currentDownloads := make(map[dht.InfoHash]chan []string) downloadEvents := make(chan downloadEvent) for { select { case newInfoHashString := <-filesToDownload: newFile, err := dht.DecodeInfoHash(newInfoHashString) if err != nil { //TODO: better error handling log.Errorf("WINSTON: DecodeInfoHash error: %v\n", err) } if _, ok := currentDownloads[newFile]; ok { log.V(3).Infof("WINSTON: File %x is already downloading, skipping...\n", newFile) continue } log.V(3).Infof("WINSTON: Accepted %x for download...\n", newFile) // Create a channel for all the found peers currentDownloads[newFile] = make(chan []string) bufferedPeerChannel := makePeerBuffer(currentDownloads[newFile]) // Ask that nice DHT fellow to find those peers :) d.PeersRequest(string(newFile), false) // Create a new gorouite that manages the download for the specific file go downloadFile(newFile, bufferedPeerChannel, downloadEvents) case newEvent := <-downloadEvents: if newEvent.eventType == eventSucessfulDownload { log.V(1).Infof("WINSTON: Download of %x completed :)\n", newEvent.infoHash) } else if newEvent.eventType == eventTimeout { log.V(1).Infof("WINSTON: Download of %x failed: time out :(\n", newEvent.infoHash) } close(currentDownloads[newEvent.infoHash]) delete(currentDownloads, newEvent.infoHash) if len(currentDownloads) == 0 { finished <- true } case newPeers, chanOk := <-d.PeersRequestResults: if !chanOk { // Something went wrong, mayday, mayday! panic("WINSTON: BORK!\n") } for ih, peers := range newPeers { // Check if download is still active if currentPeersChan, ok := currentDownloads[ih]; ok { log.V(3).Infof("WINSTON: Received %d new peers for file %x\n", len(peers), ih) currentPeersChan <- peers } else { log.V(3).Infof("WINSTON: Received %d peers for non-current file %x (probably completed or timed out)\n", len(peers), ih) } } } } }
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 main() { ipv6Address := flag.String("v6", "", "Address to bind to IPv6 interface") flag.Parse() // To see logs, use the -logtostderr flag and change the verbosity with // -v 0 (less verbose) up to -v 5 (more verbose). if len(flag.Args()) != 1 { fmt.Fprintf(os.Stderr, "Usage: %v <infohash>\n\n", os.Args[0]) fmt.Fprintf(os.Stderr, "Example infohash: %v\n", exampleIH) flag.PrintDefaults() os.Exit(1) } ih, err := dht.DecodeInfoHash(flag.Args()[0]) if err != nil { fmt.Fprintf(os.Stderr, "DecodeInfoHash error: %v\n", err) os.Exit(1) } conf4 := dht.NewConfig() conf4.UDPProto = "udp4" conf4.Port = 8445 // standard IPv4 bootstrap nodes = dht.transmissionbt.com // router.utorrent.com router.bittorrent.com conf6 := dht.NewConfig() conf6.UDPProto = "udp6" conf6.Port = 8445 conf6.Address = *ipv6Address // Starts a DHT node with the default options. It picks a random UDP port. To change this, see dht.NewConfig. d4, err := dht.New(conf4) if err != nil { fmt.Fprintf(os.Stderr, "New DHT error: %v", err) os.Exit(1) } var d6 *dht.DHT if len(*ipv6Address) > 1 { fmt.Printf("Tring to bind to IPv6=%s\n", *ipv6Address) d6, err = dht.New(conf6) if err != nil { fmt.Fprintf(os.Stderr, "New DHT error: %v", err) os.Exit(1) } if err = d6.Start(); err != nil { fmt.Fprintf(os.Stderr, "DHT start error: %v", err) os.Exit(1) } go drainresults(d6) } else { fmt.Fprintf(os.Stderr, "Not binding to IPv6 interface. If desired pass -v6=[address] for the\n") fmt.Fprintf(os.Stderr, "address you want the DHT to bind to. Privacy addresses are not recommended\n") fmt.Fprintf(os.Stderr, "Since they can expire and connections will fail\n\n") } // For debugging. go http.ListenAndServe(fmt.Sprintf(":%d", httpPortTCP), nil) if err = d4.Start(); err != nil { fmt.Fprintf(os.Stderr, "DHT start error: %v", err) os.Exit(1) } go drainresults(d4) for { d4.PeersRequest(string(ih), true) if len(*ipv6Address) > 1 { d6.PeersRequest(string(ih), true) } time.Sleep(5 * time.Second) } }