public async Task AddPeers_PeerExchangeMessage_Private() { var peer = new byte[] { 192, 168, 0, 1, 100, 0 }; var dotF = new byte[] { 1 << 0 | 1 << 2 }; // 0x1 means supports encryption, 0x2 means is a seeder var id = PeerId.CreateNull(40); id.SupportsFastPeer = true; id.SupportsLTMessages = true; var torrent = TestRig.CreatePrivate(); using var engine = new ClientEngine(EngineSettingsBuilder.CreateForTests()); var manager = await engine.AddAsync(torrent, ""); manager.Mode = new DownloadMode(manager, DiskManager, ConnectionManager, Settings); var peersTask = new TaskCompletionSource <PeerExchangePeersAdded> (); manager.PeersFound += (o, e) => { if (e is PeerExchangePeersAdded args) { peersTask.TrySetResult(args); } }; var exchangeMessage = new PeerExchangeMessage(13, peer, dotF, null); manager.Mode.HandleMessage(id, exchangeMessage); var addedArgs = await peersTask.Task.WithTimeout(); Assert.AreEqual(0, addedArgs.NewPeers, "#1"); }
public async Task AddPeers_Tracker_Private() { var torrent = TestRig.CreatePrivate(); using var engine = new ClientEngine(EngineSettingsBuilder.CreateForTests()); var manager = await engine.AddAsync(torrent, ""); manager.SetTrackerManager(TrackerManager); var peersTask = new TaskCompletionSource <TrackerPeersAdded> (); manager.PeersFound += (o, e) => { if (e is TrackerPeersAdded args) { peersTask.TrySetResult(args); } }; await TrackerManager.AddTrackerAsync(new Uri ("http://test.tracker")); TrackerManager.RaiseAnnounceComplete(TrackerManager.Tiers.Single().ActiveTracker, true, new[] { new Peer("One", new Uri("ipv4://1.1.1.1:1111")), new Peer("Two", new Uri("ipv4://2.2.2.2:2222")) }); var addedArgs = await peersTask.Task.WithTimeout(); Assert.AreEqual(2, addedArgs.NewPeers, "#1"); Assert.AreEqual(2, manager.Peers.AvailablePeers.Count, "#2"); }
public void Setup() { Engine = new ClientEngine(EngineSettingsBuilder.CreateForTests()); PieceWriter = new TestWriter(); Engine.DiskManager.ChangePieceWriter(PieceWriter); Torrent = TestRig.CreateMultiFileTorrent(new[] { new TorrentFile("path", Piece.BlockSize * 1024, 0, 1024 / 8 - 1, 0, null, null, null) }, Piece.BlockSize * 8, out torrentInfo); MagnetLink = new MagnetLink(Torrent.InfoHash, "MagnetDownload"); }
public async Task Setup() { Engine = new ClientEngine(EngineSettingsBuilder.CreateForTests()); PieceWriter = new TestWriter(); await Engine.DiskManager.SetWriterAsync(PieceWriter); Torrent = TestRig.CreateMultiFileTorrent(new[] { new TorrentFile("path", Constants.BlockSize * 1024, 0, 1024 / 8 - 1, 0) }, Constants.BlockSize * 8, out torrentInfo); MagnetLink = new MagnetLink(Torrent.InfoHashes, "MagnetDownload"); }
public async Task MismatchedPeerId_PrivateTorrent() { var torrent = TestRig.CreatePrivate(); using var engine = new ClientEngine(EngineSettingsBuilder.CreateForTests()); var manager = await engine.AddAsync(torrent, ""); manager.Mode = new DownloadMode(manager, DiskManager, ConnectionManager, Settings); var peer = PeerId.CreateNull(manager.Bitfield.Length); var handshake = new HandshakeMessage(manager.InfoHash, new BEncodedString(Enumerable.Repeat('c', 20).ToArray()), VersionInfo.ProtocolStringV100, false); Assert.Throws <TorrentException> (() => manager.Mode.HandleMessage(peer, handshake)); }
public async Task ScrapeWithTruncatedInfoHash() { var link = new MagnetLink(new InfoHashes(null, new InfoHash(new byte[32]))); using var engine = new ClientEngine(EngineSettingsBuilder.CreateForTests()); var manager = await engine.AddAsync(link, ""); var args = new TrackerRequestFactory(manager).CreateScrape(); Assert.AreEqual(20, args.InfoHash.Span.Length); Assert.IsTrue(manager.InfoHashes.Contains(args.InfoHash)); Assert.IsNull(manager.InfoHashes.V1); }
public static async Task InitializeAsync(long maxDownloadSpeed) { if (torrentEngine == null) { torrentEngine = new ClientEngine(); var builder = new EngineSettingsBuilder(torrentEngine.Settings) { MaximumDownloadSpeed = (int)maxDownloadSpeed }; await torrentEngine.UpdateSettingsAsync(builder.ToSettings()); } }
public async Task EmptyPeerId_PrivateTorrent() { var torrent = TestRig.CreatePrivate(); using var engine = new ClientEngine(EngineSettingsBuilder.CreateForTests()); var manager = await engine.AddAsync(torrent, ""); manager.Mode = new DownloadMode(manager, DiskManager, ConnectionManager, Settings); var peer = PeerId.CreateNull(manager.Bitfield.Length); peer.Peer.PeerId = null; var handshake = new HandshakeMessage(manager.InfoHashes.V1OrV2, new BEncodedString(Enumerable.Repeat('c', 20).ToArray()), Constants.ProtocolStringV100, false); manager.Mode.HandleMessage(peer, handshake, default); Assert.AreEqual(handshake.PeerId, peer.PeerID); }
static async Task MainAsync(string[] args, CancellationToken token) { // Give an example of how settings can be modified for the engine. var settingBuilder = new EngineSettingsBuilder { // Allow the engine to automatically forward ports using upnp/nat-pmp (if a compatible router is available) AllowPortForwarding = true, // Automatically save a cache of the DHT table when all torrents are stopped. AutoSaveLoadDhtCache = true, // Automatically save 'FastResume' data when TorrentManager.StopAsync is invoked, automatically load it // before hash checking the torrent. Fast Resume data will be loaded as part of 'engine.AddAsync' if // torrent metadata is available. Otherwise, if a magnetlink is used to download a torrent, fast resume // data will be loaded after the metadata has been downloaded. AutoSaveLoadFastResume = true, // If a MagnetLink is used to download a torrent, the engine will try to load a copy of the metadata // it's cache directory. Otherwise the metadata will be downloaded and stored in the cache directory // so it can be reloaded later. AutoSaveLoadMagnetLinkMetadata = true, // Use a fixed port to accept incoming connections from other peers for testing purposes. Production usages should use a random port, 0, if possible. ListenEndPoint = new IPEndPoint(IPAddress.Any, 55123), // Use a fixed port for DHT communications for testing purposes. Production usages should use a random port, 0, if possible. DhtEndPoint = new IPEndPoint(IPAddress.Any, 55123), }; using var engine = new ClientEngine(settingBuilder.ToSettings()); Task task; if (args.Length == 1 && args[0] == "--vlc") { task = new VLCStream(engine).StreamAsync(InfoHash.FromHex("AEE0F0082CC2F449412C1DD8AF4C58D9AAEE4B5C"), token); } else if (args.Length == 1 && MagnetLink.TryParse(args[0], out MagnetLink link)) { task = new MagnetLinkStreaming(engine).DownloadAsync(link, token); } else { task = new StandardDownloader(engine).DownloadAsync(token); } if (engine.Settings.AllowPortForwarding) { Console.WriteLine("uPnP or NAT-PMP port mappings will be created for any ports needed by MonoTorrent"); } try { await task; } catch (OperationCanceledException) { } foreach (var manager in engine.Torrents) { var stoppingTask = manager.StopAsync(); while (manager.State != TorrentState.Stopped) { Console.WriteLine("{0} is {1}", manager.Torrent.Name, manager.State); await Task.WhenAll(stoppingTask, Task.Delay(250)); } await stoppingTask; if (engine.Settings.AutoSaveLoadFastResume) { Console.WriteLine($"FastResume data for {manager.Torrent?.Name ?? manager.InfoHash.ToHex ()} has been written to disk."); } } if (engine.Settings.AutoSaveLoadDhtCache) { Console.WriteLine($"DHT cache has been written to disk."); } if (engine.Settings.AllowPortForwarding) { Console.WriteLine("uPnP and NAT-PMP port mappings have been removed"); } }
private static async Task StartEngine() { #if DEBUG Logger.Factory = (string className) => new TextLogger(Console.Out, className); #endif int port; Torrent torrent = null; // Ask the user what port they want to use for incoming connections Console.Write($"{Environment.NewLine}Choose a listen port: "); while (!Int32.TryParse(Console.ReadLine(), out port)) { } // Create the settings which the engine will use // downloadsPath - this is the path where we will save all the files to // port - this is the port we listen for connections on EngineSettings engineSettings = new EngineSettingsBuilder { SavePath = downloadsPath, ListenPort = port, DhtPort = port, DiskCacheBytes = 5 * 1024 * 1024, }.ToSettings(); //engineSettings.GlobalMaxUploadSpeed = 30 * 1024; //engineSettings.GlobalMaxDownloadSpeed = 100 * 1024; //engineSettings.MaxReadRate = 1 * 1024 * 1024; // Create the default settings which a torrent will have. TorrentSettings torrentDefaults = new TorrentSettings(); // Create an instance of the engine. engine = new ClientEngine(engineSettings); byte[] nodes = Array.Empty <byte> (); try { if (File.Exists(dhtNodeFile)) { nodes = File.ReadAllBytes(dhtNodeFile); } } catch { Console.WriteLine("No existing dht nodes could be loaded"); } // This starts the Dht engine but does not wait for the full initialization to // complete. This is because it can take up to 2 minutes to bootstrap, depending // on how many nodes time out when they are contacted. await engine.DhtEngine.StartAsync(nodes); // If the SavePath does not exist, we want to create it. if (!Directory.Exists(engine.Settings.SavePath)) { Directory.CreateDirectory(engine.Settings.SavePath); } // If the torrentsPath does not exist, we want to create it if (!Directory.Exists(torrentsPath)) { Directory.CreateDirectory(torrentsPath); } BEncodedDictionary fastResume = new BEncodedDictionary(); try { if (File.Exists(fastResumeFile)) { fastResume = BEncodedValue.Decode <BEncodedDictionary> (File.ReadAllBytes(fastResumeFile)); } } catch { } // For each file in the torrents path that is a .torrent file, load it into the engine. foreach (string file in Directory.GetFiles(torrentsPath)) { if (file.EndsWith(".torrent", StringComparison.OrdinalIgnoreCase)) { try { // Load the .torrent from the file into a Torrent instance // You can use this to do preprocessing should you need to torrent = await Torrent.LoadAsync(file); Console.WriteLine(torrent.InfoHash.ToString()); } catch (Exception e) { Console.Write("Couldn't decode {0}: ", file); Console.WriteLine(e.Message); continue; } // When any preprocessing has been completed, you create a TorrentManager // which you then register with the engine. TorrentManager manager = new TorrentManager(torrent, downloadsPath, torrentDefaults); if (fastResume.ContainsKey(torrent.InfoHash.ToHex())) { manager.LoadFastResume(new FastResume((BEncodedDictionary)fastResume[torrent.InfoHash.ToHex()])); } await engine.Register(manager); // Store the torrent manager in our list so we can access it later torrents.Add(manager); manager.PeersFound += Manager_PeersFound; } } // If we loaded no torrents, just exist. The user can put files in the torrents directory and start // the client again if (torrents.Count == 0) { Console.WriteLine("No torrents found in the Torrents directory"); Console.WriteLine("Exiting..."); engine.Dispose(); return; } // For each torrent manager we loaded and stored in our list, hook into the events // in the torrent manager and start the engine. foreach (TorrentManager manager in torrents) { manager.PeerConnected += (o, e) => { lock (listener) listener.WriteLine($"Connection succeeded: {e.Peer.Uri}"); }; manager.ConnectionAttemptFailed += (o, e) => { lock (listener) listener.WriteLine( $"Connection failed: {e.Peer.ConnectionUri} - {e.Reason}"); }; // Every time a piece is hashed, this is fired. manager.PieceHashed += delegate(object o, PieceHashedEventArgs e) { lock (listener) listener.WriteLine($"Piece Hashed: {e.PieceIndex} - {(e.HashPassed ? "Pass" : "Fail")}"); }; // Every time the state changes (Stopped -> Seeding -> Downloading -> Hashing) this is fired manager.TorrentStateChanged += delegate(object o, TorrentStateChangedEventArgs e) { lock (listener) listener.WriteLine($"OldState: {e.OldState} NewState: {e.NewState}"); }; // Every time the tracker's state changes, this is fired manager.TrackerManager.AnnounceComplete += (sender, e) => { listener.WriteLine($"{e.Successful}: {e.Tracker}"); }; // Start the torrentmanager. The file will then hash (if required) and begin downloading/seeding await manager.StartAsync(); } // This is how to access the list of port mappings, and to see if they were // successful, pending or failed. If they failed it could be because the public port // is already in use by another computer on your network. foreach (var successfulMapping in engine.PortMappings.Created) { } foreach (var failedMapping in engine.PortMappings.Failed) { } foreach (var failedMapping in engine.PortMappings.Pending) { } // While the torrents are still running, print out some stats to the screen. // Details for all the loaded torrent managers are shown. int i = 0; bool running = true; StringBuilder sb = new StringBuilder(1024); while (running) { if ((i++) % 10 == 0) { sb.Remove(0, sb.Length); running = torrents.Exists(m => m.State != TorrentState.Stopped); AppendFormat(sb, "Total Download Rate: {0:0.00}kB/sec", engine.TotalDownloadSpeed / 1024.0); AppendFormat(sb, "Total Upload Rate: {0:0.00}kB/sec", engine.TotalUploadSpeed / 1024.0); AppendFormat(sb, "Disk Read Rate: {0:0.00} kB/s", engine.DiskManager.ReadRate / 1024.0); AppendFormat(sb, "Disk Write Rate: {0:0.00} kB/s", engine.DiskManager.WriteRate / 1024.0); AppendFormat(sb, "Cache Used: {0:0.00} kB", engine.DiskManager.CacheBytesUsed / 1024.0); AppendFormat(sb, "Cache Read: {0:0.00} kB", engine.DiskManager.TotalCacheBytesRead / 1024.0); AppendFormat(sb, "Total Read: {0:0.00} kB", engine.DiskManager.TotalBytesRead / 1024.0); AppendFormat(sb, "Total Written: {0:0.00} kB", engine.DiskManager.TotalBytesWritten / 1024.0); AppendFormat(sb, "Open Connections: {0}", engine.ConnectionManager.OpenConnections); foreach (TorrentManager manager in torrents) { AppendSeparator(sb); AppendFormat(sb, "State: {0}", manager.State); AppendFormat(sb, "Name: {0}", manager.Torrent == null ? "MetaDataMode" : manager.Torrent.Name); AppendFormat(sb, "Progress: {0:0.00}", manager.Progress); AppendFormat(sb, "Download Speed: {0:0.00} kB/s", manager.Monitor.DownloadSpeed / 1024.0); AppendFormat(sb, "Upload Speed: {0:0.00} kB/s", manager.Monitor.UploadSpeed / 1024.0); AppendFormat(sb, "Total Downloaded: {0:0.00} MB", manager.Monitor.DataBytesDownloaded / (1024.0 * 1024.0)); AppendFormat(sb, "Total Uploaded: {0:0.00} MB", manager.Monitor.DataBytesUploaded / (1024.0 * 1024.0)); AppendFormat(sb, "Tracker Status"); foreach (var tier in manager.TrackerManager.Tiers) { AppendFormat(sb, $"\t{tier.ActiveTracker} : Announce Succeeded: {tier.LastAnnounceSucceeded}. Scrape Succeeded: {tier.LastScrapeSucceeded}."); } if (manager.PieceManager != null) { AppendFormat(sb, "Current Requests: {0}", await manager.PieceManager.CurrentRequestCountAsync()); } var peers = await manager.GetPeersAsync(); AppendFormat(sb, "Outgoing:"); foreach (PeerId p in peers.Where(t => t.ConnectionDirection == Direction.Outgoing)) { AppendFormat(sb, "\t{2} - {1:0.00}/{3:0.00}kB/sec - {0} - {4} ({5})", p.Uri, p.Monitor.DownloadSpeed / 1024.0, p.AmRequestingPiecesCount, p.Monitor.UploadSpeed / 1024.0, p.EncryptionType, string.Join("|", p.SupportedEncryptionTypes.Select(t => t.ToString()).ToArray())); } AppendFormat(sb, ""); AppendFormat(sb, "Incoming:"); foreach (PeerId p in peers.Where(t => t.ConnectionDirection == Direction.Incoming)) { AppendFormat(sb, "\t{2} - {1:0.00}/{3:0.00}kB/sec - {0} - {4} ({5})", p.Uri, p.Monitor.DownloadSpeed / 1024.0, p.AmRequestingPiecesCount, p.Monitor.UploadSpeed / 1024.0, p.EncryptionType, string.Join("|", p.SupportedEncryptionTypes.Select(t => t.ToString()).ToArray())); } AppendFormat(sb, "", null); if (manager.Torrent != null) { foreach (var file in manager.Files) { AppendFormat(sb, "{1:0.00}% - {0}", file.Path, file.BitField.PercentComplete); } } } Console.Clear(); Console.WriteLine(sb.ToString()); listener.ExportTo(Console.Out); } Thread.Sleep(500); } }