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 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) } }