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);
                        }
                    }
                }
            }
        }
Example #2
0
        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;
            }
            }
        }