예제 #1
0
        public async void TwoUnevenPiecesValid()
        {
            var expectedDigest = "aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d".FromHex();
            var verified       = new VerifiedBytes(expectedDigest, 5, 3);

            // Piece indicies need to be in range.
            Assert.Throws <ArgumentOutOfRangeException>(() => {
                verified.ProvidePiece(-1, "hel".ToASCII());
            });
            Assert.Throws <ArgumentOutOfRangeException>(() => {
                verified.ProvidePiece(2, "hel".ToASCII());
            });

            // Pieces need to be of the expected size.
            Assert.Throws <ArgumentException>(() => {
                verified.ProvidePiece(0, "lo".ToASCII());
            });
            Assert.Throws <ArgumentException>(() => {
                verified.ProvidePiece(1, "hel".ToASCII());
            });

            verified.ProvidePiece(1, "lo".ToASCII());
            verified.ProvidePiece(0, "hel".ToASCII());

            // Each piece can only be provided once.
            Assert.Throws <VerifiedBytesStateException>(() => {
                verified.ProvidePiece(0, "hel".ToASCII());
            });

            var resultValue = await verified.Result;

            Assert.Equal("hello".ToASCII(), resultValue);
        }
예제 #2
0
        public async void ZeroLengthInvalid()
        {
            var expectedDigest = "0000000000000000000000000000000000000000".FromHex();
            var verified       = new VerifiedBytes(expectedDigest, 0, 1024);

            await Assert.ThrowsAsync <BytesVerificationException>(async() => {
                var resultValue = await verified.Result;
            });
        }
예제 #3
0
        public async void TwoUnevenPiecesInvalidFromVerified()
        {
            var expectedDigest = "0000000000000000000000000000000000000000".FromHex();
            var verified       = VerifiedBytes.From("hello".ToASCII(), expectedDigest, 3);

            await Assert.ThrowsAsync <BytesVerificationException>(async() => {
                await verified.Result;
            });
        }
예제 #4
0
        public async void TwoUnevenPiecesTogetherValid()
        {
            var expectedDigest = "aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d".FromHex();
            var verified       = new VerifiedBytes(expectedDigest, 5, 3);

            verified.ProvideData("hello".ToASCII());

            var resultValue = await verified.Result;

            Assert.Equal("hello".ToASCII(), resultValue);

            Assert.Equal("hel".ToASCII(), verified.GetPiece(0));
            Assert.Equal("lo".ToASCII(), verified.GetPiece(1));
            Assert.Equal("hello".ToASCII(), verified.GetData());
        }
예제 #5
0
        public async void TwoUnevenPiecesTogetherInvalid()
        {
            var expectedDigest = "aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d".FromHex();
            var verified       = new VerifiedBytes(expectedDigest, 5, 3);

            verified.ProvideData("hell!".ToASCII());

            // Data can only be provided once, even it would fix it.
            Assert.Throws <VerifiedBytesStateException>(() => {
                verified.ProvideData("hello".ToASCII());
            });

            await Assert.ThrowsAsync <BytesVerificationException>(async() => {
                var resultValue = await verified.Result;
            });
        }
예제 #6
0
        public async void ZeroLengthValid()
        {
            var expectedDigest = "da39a3ee5e6b4b0d3255bfef95601890afd80709".FromHex();
            var verified       = new VerifiedBytes(expectedDigest, 0, 1024);
            var resultValue    = await verified.Result;

            Assert.Equal(new byte[0], resultValue);
            Assert.Equal(new byte[0], verified.GetData());

            // No possible indices are valid.
            Assert.Throws <ArgumentOutOfRangeException>(() => {
                verified.ProvidePiece(0, new byte[0]);
            });
            Assert.Throws <ArgumentOutOfRangeException>(() => {
                verified.ProvidePiece(0, new byte[0]);
                Assert.Equal("".ToASCII(), verified.GetPiece(0));
            });
        }
예제 #7
0
        public async void TwoUnevenPiecesValidFromVerified()
        {
            var expectedDigest = "aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d".FromHex();
            var verified       = VerifiedBytes.From("hello".ToASCII(), expectedDigest, 3);

            Assert.Equal(expectedDigest, verified.Digest);

            var resultValue = await verified.Result;

            Assert.Equal("hello".ToASCII(), resultValue);

            // Data can only be provided once.
            Assert.Throws <VerifiedBytesStateException>(() => {
                verified.ProvideData("hello".ToASCII());
            });
            Assert.Throws <VerifiedBytesStateException>(() => {
                verified.ProvidePiece(0, "hel".ToASCII());
            });
        }
예제 #8
0
        // ourExtCode refers to the value we have associated with ut_metadata
        // theirExtCode refers to the value they have associated with ut_metadata
        public byte[] GetMetadata(NetworkStream stream, TcpClient connection, byte ourExtCode, byte theirExtCode, byte[] infohash)
        {
            if (!connection.Connected)
            {
                throw new Exception("Disconnected from peer after handshake.");
            }

            var currentPiece = 0;
            var info         = new VerifiedBytes(infohash, (Int32)metadataLength, CommonPieceSizes.BEP9_METADATA);

            using (logger.BeginScope($"Metadata request for {infohash.ToHex()} from {connection.Client.RemoteEndPoint}"))
            {
                // Request the first piece.
                var initialRequest = ConstructRequestMessage(ourExtCode, 0);
                logger.LogInformation(LoggingEvents.METADATA_REQUEST, "Sending request for first metadata piece: " + initialRequest.ToHuman());
                stream.Write(initialRequest);

                while (true)
                {
                    Int32 theirLength = 0;
                    // Read lengths until we get a non-zero (non-keepalive) length.
                    while (theirLength == 0)
                    {
                        var theirPrefix = stream.ReadBytes(4);
                        theirLength = theirPrefix.Decode32BitInteger();
                        if (theirLength == 0)
                        {
                            logger.LogInformation(LoggingEvents.PEER_PROTOCOL_MSG, "Got keepalive. Let's send our own!");
                            stream.Write(new byte[4]);
                        }
                    }

                    logger.LogInformation(LoggingEvents.PEER_PROTOCOL_MSG, $"Got message with length {theirLength}.");
                    var peerResponse   = stream.ReadBytes(theirLength);
                    var responseTypeId = peerResponse[0];
                    switch (responseTypeId)
                    {
                    case 20:
                        logger.LogInformation(LoggingEvents.EXTENSION_MESSAGE, "It's an extension message! Hurrah!");
                        var extensionId = peerResponse[1];

                        if (extensionId == theirExtCode)
                        {
                            logger.LogInformation(LoggingEvents.METADATA_RESPONSE_INCOMING, "It's a metadata exchange message!");
                            var data     = peerResponse.Slice(2);
                            var dict     = Bencoding.DecodeFirstDict(data, out Int64 dictSize);
                            var postDict = data.Slice((Int32)dictSize);     // This is the metadata itself -- a bencoded dictionary of utf8 strings

                            if (dict.GetInt("piece") != currentPiece)
                            {
                                throw new Exception($"Expected piece {currentPiece}. Instead, received {dict.GetInt("piece")}");
                            }

                            logger.LogInformation(
                                LoggingEvents.METADATA_RESPONSE_INCOMING,
                                $"Got BEP-9 {Bencoding.ToHuman(Bencoding.Encode(dict))} followed by {postDict.Length} bytes of data.\nStoring...");

                            info.ProvidePiece(currentPiece, postDict);
                            currentPiece++;

                            if (currentPiece == info.PieceCount)
                            {
                                byte[] combinedPieces;
                                try {
                                    combinedPieces = info.Result.Result;
                                } catch (BytesVerificationException ex) {
                                    logger.LogWarning(LoggingEvents.METADATA_FAILURE, "metadata verification failed!", ex);
                                    throw new MetadataException("Metadata verification failed -- try another peer.");
                                }

                                logger.LogInformation(LoggingEvents.DATA_STORAGE_ACTION, "metadata verified! saving...");
                                DataHandler.SaveMetadata(combinedPieces);
                                logger.LogInformation(LoggingEvents.DATA_STORAGE_ACTION, "metadata saved.");
                                return(combinedPieces);
                            }

                            var request = ConstructRequestMessage(ourExtCode, currentPiece);
                            logger.LogInformation(LoggingEvents.METADATA_REQUEST, "Requesting the next piece of metadata...");
                            stream.Write(request);
                        }
                        else
                        {
                            logger.LogWarning(LoggingEvents.PEER_UNEXPECTED_RESPONSE, $"It's an unexpected message type, ID {extensionId}.");
                        }

                        break;

                    case 0:
                        logger.LogInformation(LoggingEvents.PEER_PROTOCOL_MSG, "It's a choke message! :(");
                        break;

                    case 1:
                        logger.LogInformation(LoggingEvents.PEER_PROTOCOL_MSG, "It's an unchoke message! :D");
                        break;

                    case 2:
                        logger.LogInformation(LoggingEvents.PEER_PROTOCOL_MSG, "It's an interested message! <3");
                        break;

                    case 4:
                        logger.LogInformation(LoggingEvents.PEER_PROTOCOL_MSG, "It's a not interested message! </3");
                        break;

                    default:
                        logger.LogWarning(LoggingEvents.PEER_UNEXPECTED_RESPONSE, $"Unexpected message type {responseTypeId}; ignoring.");
                        break;
                    }
                }
            }
        }