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); }
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; }); }
public async void TwoUnevenPiecesInvalidFromVerified() { var expectedDigest = "0000000000000000000000000000000000000000".FromHex(); var verified = VerifiedBytes.From("hello".ToASCII(), expectedDigest, 3); await Assert.ThrowsAsync <BytesVerificationException>(async() => { await verified.Result; }); }
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()); }
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; }); }
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)); }); }
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()); }); }
// 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; } } } }