public void InitiateHandshake(byte[] infoHash) { logger.LogInformation("Our Peer id: " + Client.peerId.ToHuman()); var fixedHeader = new byte[20]; fixedHeader[0] = (byte)19; "BitTorrent protocol".ToASCII().CopyTo(fixedHeader, 1); var bufferBitfield = new byte[8]; bufferBitfield[5] = (byte)16; Client.extensionsEnabled = true; TcpClient connection = new TcpClient(); connection.ConnectAsync(peer.Address, peer.Port).Wait(); if (!connection.Connected) { throw new Exception("Failed to connect to Peer."); } var initialHandshake = new byte[68]; fixedHeader.CopyTo(initialHandshake, 0); bufferBitfield.CopyTo(initialHandshake, fixedHeader.Length); infoHash.CopyTo(initialHandshake, fixedHeader.Length + bufferBitfield.Length); Client.peerId.CopyTo(initialHandshake, fixedHeader.Length + bufferBitfield.Length + infoHash.Length); logger.LogInformation(LoggingEvents.HANDSHAKE_OUTGOING, "Sending our handshake to " + peer.Address + ":" + peer.Port); using (var stream = connection.GetStream()) { stream.Write(initialHandshake); logger.LogInformation(LoggingEvents.HANDSHAKE_INCOMING, "Received response from Peer."); var theirFixedHeader = stream.ReadBytes(20); if (!theirFixedHeader.SequenceEqual(fixedHeader)) { throw new Exception("Peer failed to return fixed header; aborting connection."); } var theirBuffer = stream.ReadBytes(8); if (theirBuffer[5] == 16) { theirExtensionsEnabled = true; } var theirInfoHash = stream.ReadBytes(20); logger.LogInformation(LoggingEvents.HANDSHAKE_INCOMING, "Peer's infohash is: " + theirInfoHash.ToHuman()); if (!theirInfoHash.SequenceEqual(infoHash)) { throw new Exception("Peer failed to return a matching infohash; aborting connection."); } var theirpeerId = stream.ReadBytes(20); logger.LogInformation(LoggingEvents.HANDSHAKE_INCOMING, "The Peer's ID is " + theirpeerId.ToHuman()); if (Client.extensionsEnabled && theirExtensionsEnabled) { var theirExtensionHeader = GetPeerExtensionHeader(stream); var decodedExtensionHeader = Bencoding.DecodeDict(theirExtensionHeader); var theirExtensions = decodedExtensionHeader.GetDict("m"); logger.LogInformation(LoggingEvents.EXTENSION_HEADER_IN, "Peer's extension header:" + Environment.NewLine + Bencoding.ToHuman(theirExtensionHeader)); var extensionDict = GenerateExtentionDict(); var extensionHeader = new byte[extensionDict.Length + 6]; var lengthPrefix = (extensionDict.Length + 2).EncodeBytes(); Array.Copy(lengthPrefix, extensionHeader, 4); extensionHeader[4] = 20; extensionHeader[5] = 0; extensionDict.CopyTo(extensionHeader, 6); stream.Write(extensionHeader); logger.LogInformation(LoggingEvents.EXTENSION_HEADER_OUT, "Sending our extension header: " + Environment.NewLine + Bencoding.ToHuman(extensionDict)); // Send interested message stream.Write(1.EncodeBytes()); stream.Write(new byte[1] { 2 }); logger.LogInformation(LoggingEvents.PEER_PROTOCOL_MSG, "Sent interested message."); if (theirExtensions.ContainsKey("ut_metadata")) { logger.LogInformation(LoggingEvents.METADATA_EXCHANGE, "They also support metadata exchange. Lets try that."); var theirMetadataExtensionId = (byte)theirExtensions.Get("ut_metadata"); var metadataExchange = new MetadataExchange(decodedExtensionHeader.Get("metadata_size")); try { torrent.Metadata = metadataExchange.GetMetadata(stream, connection, 2, theirMetadataExtensionId, infoHash); } catch (MetadataException e) { logger.LogWarning("Unable to get metadata from current peer: ", e); } } } } }
private void handleMessage(UDPSocket.ReceivedPacket message) { var value = Bencoding.DecodeDict(message.Data); var type = value.GetString("y"); switch (type) { case "r": { var key = new QueryKey { Token = value.GetBytes("t"), EP = message.Source }; if (pendingQueries.ContainsKey(key)) { var responseSource = pendingQueries[key]; pendingQueries.Remove(key); responseSource.TrySetResult(value); } else { logger.LogDebug("Got unexpected response message."); } break; } case "e": { var key = new QueryKey { Token = value.GetBytes("t"), EP = message.Source }; logger.LogError($"Got error mesage for {key}."); if (pendingQueries.ContainsKey(key)) { var responseSource = pendingQueries[key]; pendingQueries.Remove(key); var errors = value.GetList("e"); var code = (Int64)errors[0]; var errorMessage = ((byte[])errors[1]).FromASCII(); var exception = new Exception($"{code} {errorMessage}"); responseSource.TrySetException(new Exception[] { exception }); } else { logger.LogError($"But I don't even know {key}!"); } break; } case "q": { logger.LogDebug($"Ignored query mesage from {message.Source}."); // do nothing because we're read-only break; } default: { logger.LogDebug($"Got unknown mesage from {message.Source}."); // maybe we could send an error? break; } } }