public async Task SendMetadataCore(string expectedPath) { CustomConnection connection = pair.Incoming; // 1) Send local handshake. We've already received the remote handshake as part // of the Connect method. var sendHandshake = new HandshakeMessage(rig.Manager.InfoHash, new string('g', 20), VersionInfo.ProtocolStringV100, true, true); await PeerIO.SendMessageAsync(connection, encryptor, sendHandshake); ExtendedHandshakeMessage exHand = new ExtendedHandshakeMessage(false, rig.Torrent.Metadata.Length); exHand.Supports.Add(LTMetadata.Support); await PeerIO.SendMessageAsync(connection, encryptor, exHand); // 2) Receive the metadata requests from the other peer and fulfill them byte[] buffer = rig.Torrent.Metadata; int length = (buffer.Length + 16383) / 16384; PeerMessage m; while (length > 0 && (m = await PeerIO.ReceiveMessageAsync(connection, decryptor)) != null) { LTMetadata metadata = m as LTMetadata; if (metadata != null) { if (metadata.MetadataMessageType == LTMetadata.eMessageType.Request) { metadata = new LTMetadata(LTMetadata.Support.MessageId, LTMetadata.eMessageType.Data, metadata.Piece, buffer); await PeerIO.SendMessageAsync(connection, encryptor, metadata); length--; } } } // We've sent all the pieces. Now we just wait for the torrentmanager to process them all. while (rig.Manager.Mode is MetadataMode) { System.Threading.Thread.Sleep(10); } Assert.IsTrue(File.Exists(expectedPath), "#1"); Torrent torrent = Torrent.Load(expectedPath); Assert.AreEqual(rig.Manager.InfoHash, torrent.InfoHash, "#2"); }
public async Task RequestMetadata() { await Setup(false, "path.torrent"); CustomConnection connection = pair.Incoming; // 1) Send local handshake. We've already received the remote handshake as part // of the Connect method. var sendHandshake = new HandshakeMessage(rig.Manager.Torrent.InfoHash, new string('g', 20), VersionInfo.ProtocolStringV100, true, true); await PeerIO.SendMessageAsync(connection, encryptor, sendHandshake); ExtendedHandshakeMessage exHand = new ExtendedHandshakeMessage(false, rig.TorrentDict.LengthInBytes(), 5555); exHand.Supports.Add(LTMetadata.Support); await PeerIO.SendMessageAsync(connection, encryptor, exHand); // 2) Send all our metadata requests int length = (rig.TorrentDict.LengthInBytes() + 16383) / 16384; for (int i = 0; i < length; i++) { await PeerIO.SendMessageAsync(connection, encryptor, new LTMetadata(LTMetadata.Support.MessageId, LTMetadata.eMessageType.Request, i, null)); } // 3) Receive all the metadata chunks PeerMessage m; var stream = new MemoryStream(); while (length > 0 && (m = await PeerIO.ReceiveMessageAsync(connection, decryptor)) != null) { LTMetadata metadata = m as LTMetadata; if (metadata != null) { if (metadata.MetadataMessageType == LTMetadata.eMessageType.Data) { stream.Write(metadata.MetadataPiece, 0, metadata.MetadataPiece.Length); length--; } } } // 4) Verify the hash is the same. stream.Position = 0; Assert.AreEqual(rig.Torrent.InfoHash, new InfoHash(new SHA1Managed().ComputeHash(stream)), "#1"); }
protected virtual void HandleExtendedHandshakeMessage(PeerId id, ExtendedHandshakeMessage message) { // FIXME: Use the 'version' information // FIXME: Recreate the uri? Give warning? if (message.LocalPort > 0) { id.Peer.LocalPort = message.LocalPort; } id.MaxSupportedPendingRequests = Math.Max(1, message.MaxRequests); id.ExtensionSupports = message.Supports; if (id.ExtensionSupports.Supports(PeerExchangeMessage.Support.Name)) { if (manager.HasMetadata && !manager.Torrent.IsPrivate) { id.PeerExchangeManager = new PeerExchangeManager(id); } } }
protected override void HandleExtendedHandshakeMessage(PeerId id, ExtendedHandshakeMessage message) { base.HandleExtendedHandshakeMessage(id, message); if (!id.ExtensionSupports.Supports(LTMetadata.Support.Name)) { return; } Stream = new MemoryStream(new byte[message.MetadataSize], 0, message.MetadataSize, true, true); var size = message.MetadataSize % LTMetadata.BlockSize; if (size > 0) { size = 1; } size += message.MetadataSize / LTMetadata.BlockSize; _bitField = new BitField(size); RequestNextNeededPiece(id); }
protected virtual void HandleExtendedHandshakeMessage (PeerId id, ExtendedHandshakeMessage message) { // FIXME: Use the 'version' information // FIXME: Recreate the uri? Give warning? if (message.LocalPort > 0) id.Peer.LocalPort = message.LocalPort; // If MaxRequests is zero, or negative, ignore it. if (message.MaxRequests > 0) id.MaxSupportedPendingRequests = message.MaxRequests; else Logger.Log (id.Connection, "Invalid value for libtorrent extension handshake 'MaxRequests'."); // Bugfix for MonoTorrent older than 1.0.19 if (id.ClientApp.Client == ClientApp.MonoTorrent) id.MaxSupportedPendingRequests = Math.Max (id.MaxSupportedPendingRequests, 192); id.ExtensionSupports = message.Supports; if (id.ExtensionSupports.Supports (PeerExchangeMessage.Support.Name)) { if (Manager.HasMetadata && !Manager.Torrent.IsPrivate) id.PeerExchangeManager = new PeerExchangeManager (Manager, id); } }
internal async Task SendMetadataCore(string expectedPath, PeerMessage sendAfterHandshakeMessage, bool metadataOnly = false) { CustomConnection connection = pair.Incoming; var metadataTcs = new TaskCompletionSource <byte[]> (); rig.Manager.MetadataReceived += (o, e) => metadataTcs.TrySetResult(e); // 1) Send local handshake. We've already received the remote handshake as part // of the Connect method. var sendHandshake = new HandshakeMessage(rig.Manager.InfoHash, new string ('g', 20), VersionInfo.ProtocolStringV100, true, true); await PeerIO.SendMessageAsync(connection, encryptor, sendHandshake); ExtendedHandshakeMessage exHand = new ExtendedHandshakeMessage(false, rig.Torrent.InfoMetadata.Length, 5555); exHand.Supports.Add(LTMetadata.Support); await PeerIO.SendMessageAsync(connection, encryptor, exHand); await PeerIO.SendMessageAsync(connection, encryptor, sendAfterHandshakeMessage); bool receivedHaveNone = false; // 2) Receive the metadata requests from the other peer and fulfill them byte[] buffer = rig.Torrent.InfoMetadata; var unrequestedPieces = new HashSet <int> (Enumerable.Range(0, (buffer.Length + 16383) / 16384)); PeerMessage m; while (unrequestedPieces.Count > 0 && (m = await PeerIO.ReceiveMessageAsync(connection, decryptor)) != null) { if (m is ExtendedHandshakeMessage ex) { Assert.IsNull(ex.MetadataSize); Assert.AreEqual(ClientEngine.DefaultMaxPendingRequests, ex.MaxRequests); } else if (m is HaveNoneMessage) { receivedHaveNone = true; } else if (m is LTMetadata metadata) { if (metadata.MetadataMessageType == LTMetadata.eMessageType.Request) { metadata = new LTMetadata(LTMetadata.Support.MessageId, LTMetadata.eMessageType.Data, metadata.Piece, buffer); await PeerIO.SendMessageAsync(connection, encryptor, metadata); unrequestedPieces.Remove(metadata.Piece); // Hack this in because testing is... awkward... for most of this library. // The purpose here is to ensure that duplicate pieces don't reset our data or cause the event // to be emitted multiple times. if (unrequestedPieces.Count > 0) { metadata = new LTMetadata(LTMetadata.Support.MessageId, LTMetadata.eMessageType.Data, 0, buffer); await PeerIO.SendMessageAsync(connection, encryptor, metadata); } // And let's receive many handshake messages from other peers. Ensure we process this on the correct // thread. It needs to be on the main loop as it's run in the context of the ClientEngine loop. if (rig.Manager.Mode is MetadataMode mode) { ClientEngine.MainLoop.Post(state => mode.HandleMessage(PeerId.CreateNull(12389), exHand), null); } } } } // We've sent all the pieces. Now we just wait for the torrentmanager to process them all. Torrent torrent; if (metadataOnly) { torrent = Torrent.Load(await metadataTcs.Task.WithTimeout()); } else { await rig.Manager.WaitForState(TorrentState.Downloading).WithTimeout(); Assert.IsTrue(File.Exists(expectedPath), "#1"); torrent = Torrent.Load(expectedPath); } Assert.AreEqual(rig.Manager.InfoHash, torrent.InfoHash, "#2"); Assert.AreEqual(1, torrent.AnnounceUrls.Count, "#3"); Assert.AreEqual(2, torrent.AnnounceUrls[0].Count, "#4"); Assert.IsTrue(receivedHaveNone, "#6"); if (!metadataOnly) { var peer = PeerId.CreateNull(rig.Manager.Bitfield.Length, true, false, true); Assert.DoesNotThrow(() => rig.Manager.PieceManager.AddPieceRequests(peer)); } }
internal async Task SendMetadataCore(string expectedPath, PeerMessage sendAfterHandshakeMessage) { CustomConnection connection = pair.Incoming; // 1) Send local handshake. We've already received the remote handshake as part // of the Connect method. var sendHandshake = new HandshakeMessage(rig.Manager.InfoHash, new string ('g', 20), VersionInfo.ProtocolStringV100, true, true); await PeerIO.SendMessageAsync(connection, encryptor, sendHandshake); ExtendedHandshakeMessage exHand = new ExtendedHandshakeMessage(false, rig.Torrent.InfoMetadata.Length, 5555); exHand.Supports.Add(LTMetadata.Support); await PeerIO.SendMessageAsync(connection, encryptor, exHand); await PeerIO.SendMessageAsync(connection, encryptor, sendAfterHandshakeMessage); bool receivedHaveNone = false; // 2) Receive the metadata requests from the other peer and fulfill them byte[] buffer = rig.Torrent.InfoMetadata; int length = (buffer.Length + 16383) / 16384; PeerMessage m; while (length > 0 && (m = await PeerIO.ReceiveMessageAsync(connection, decryptor)) != null) { if (m is ExtendedHandshakeMessage ex) { Assert.AreEqual(ClientEngine.DefaultMaxPendingRequests, ex.MaxRequests); } else if (m is HaveNoneMessage) { receivedHaveNone = true; } else if (m is LTMetadata metadata) { if (metadata.MetadataMessageType == LTMetadata.eMessageType.Request) { metadata = new LTMetadata(LTMetadata.Support.MessageId, LTMetadata.eMessageType.Data, metadata.Piece, buffer); await PeerIO.SendMessageAsync(connection, encryptor, metadata); length--; } } } // We've sent all the pieces. Now we just wait for the torrentmanager to process them all. await rig.Manager.WaitForState(TorrentState.Downloading).WithTimeout(); Assert.IsTrue(File.Exists(expectedPath), "#1"); Torrent torrent = Torrent.Load(expectedPath); Assert.AreEqual(rig.Manager.InfoHash, torrent.InfoHash, "#2"); Assert.AreEqual(2, rig.Manager.Torrent.AnnounceUrls.Count, "#3"); Assert.AreEqual(2, rig.Manager.Torrent.AnnounceUrls[0].Count, "#4"); Assert.AreEqual(3, rig.Manager.Torrent.AnnounceUrls[1].Count, "#5"); Assert.IsTrue(receivedHaveNone, "#6"); var peer = PeerId.CreateNull(rig.Manager.Bitfield.Length, true, false, true); Assert.DoesNotThrow(() => rig.Manager.PieceManager.AddPieceRequests(peer)); }
protected virtual void HandleExtendedHandshakeMessage(PeerId id, ExtendedHandshakeMessage message) { // FIXME: Use the 'version' information // FIXME: Recreate the uri? Give warning? if (message.LocalPort > 0) id.Peer.LocalPort = message.LocalPort; id.MaxSupportedPendingRequests = Math.Max(1, message.MaxRequests); id.ExtensionSupports = message.Supports; if (id.ExtensionSupports.Supports(PeerExchangeMessage.Support.Name)) { if (_manager.HasMetadata && !_manager.Torrent.IsPrivate) id.PeerExchangeManager = new PeerExchangeManager(id); } }