/* * Copyright 2020 The WebRTC Project Authors. All rights reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ #include "p2p/base/stun_dictionary.h" #include #include "rtc_base/gunit.h" #include "rtc_base/logging.h" #include "test/gtest.h" namespace { void Sync(cricket::StunDictionaryView& dictionary, cricket::StunDictionaryWriter& writer) { int pending = writer.Pending(); auto delta = writer.CreateDelta(); if (delta == nullptr) { EXPECT_EQ(pending, 0); } else { EXPECT_NE(pending, 0); auto delta_ack = dictionary.ApplyDelta(*delta); if (!delta_ack.ok()) { RTC_LOG(LS_ERROR) << "delta_ack.error(): " << delta_ack.error().message(); } EXPECT_TRUE(delta_ack.ok()); ASSERT_NE(delta_ack.value().first.get(), nullptr); writer.ApplyDeltaAck(*delta_ack.value().first); EXPECT_FALSE(writer.Pending()); } } void XorToggle(cricket::StunByteStringAttribute& attr, size_t byte) { ASSERT_TRUE(attr.length() > byte); uint8_t val = attr.GetByte(byte); uint8_t new_val = val ^ (128 - (byte & 255)); attr.SetByte(byte, new_val); } std::unique_ptr Crop( const cricket::StunByteStringAttribute& attr, int new_length) { auto new_attr = std::make_unique(attr.type()); std::string content = std::string(attr.string_view()); content.erase(new_length); new_attr->CopyBytes(content); return new_attr; } } // namespace namespace cricket { constexpr int kKey1 = 100; TEST(StunDictionary, CreateEmptyDictionaryWriter) { StunDictionaryView dictionary; StunDictionaryWriter writer; EXPECT_TRUE(dictionary.empty()); EXPECT_TRUE(writer->empty()); EXPECT_EQ(writer.Pending(), 0); EXPECT_EQ(writer.CreateDelta().get(), nullptr); } TEST(StunDictionary, SetAndGet) { StunDictionaryWriter writer; writer.SetUInt32(kKey1)->SetValue(27); EXPECT_EQ(writer->GetUInt32(kKey1)->value(), 27u); EXPECT_EQ(writer->GetUInt64(kKey1), nullptr); EXPECT_EQ(writer->GetByteString(kKey1), nullptr); EXPECT_EQ(writer->GetAddress(kKey1), nullptr); EXPECT_EQ(writer->GetUInt16List(kKey1), nullptr); } TEST(StunDictionary, SetAndApply) { StunDictionaryWriter writer; writer.SetUInt32(kKey1)->SetValue(27); StunDictionaryView dictionary; EXPECT_TRUE(dictionary.empty()); Sync(dictionary, writer); EXPECT_EQ(dictionary.GetUInt32(kKey1)->value(), 27u); EXPECT_EQ(dictionary.bytes_stored(), 12); } TEST(StunDictionary, SetSetAndApply) { StunDictionaryWriter writer; writer.SetUInt32(kKey1)->SetValue(27); writer.SetUInt32(kKey1)->SetValue(29); StunDictionaryView dictionary; EXPECT_TRUE(dictionary.empty()); Sync(dictionary, writer); EXPECT_EQ(dictionary.GetUInt32(kKey1)->value(), 29u); EXPECT_EQ(dictionary.bytes_stored(), 12); } TEST(StunDictionary, SetAndApplyAndSetAndApply) { StunDictionaryWriter writer; writer.SetUInt32(kKey1)->SetValue(27); StunDictionaryView dictionary; EXPECT_TRUE(dictionary.empty()); Sync(dictionary, writer); EXPECT_EQ(dictionary.GetUInt32(kKey1)->value(), 27u); EXPECT_EQ(dictionary.bytes_stored(), 12); writer.SetUInt32(kKey1)->SetValue(29); Sync(dictionary, writer); EXPECT_EQ(dictionary.GetUInt32(kKey1)->value(), 29u); EXPECT_EQ(dictionary.bytes_stored(), 12); } TEST(StunDictionary, ChangeType) { StunDictionaryWriter writer; writer.SetUInt32(kKey1)->SetValue(27); EXPECT_EQ(writer->GetUInt32(kKey1)->value(), 27u); writer.SetUInt64(kKey1)->SetValue(29); EXPECT_EQ(writer->GetUInt32(kKey1), nullptr); EXPECT_EQ(writer->GetUInt64(kKey1)->value(), 29ull); } TEST(StunDictionary, ChangeTypeApply) { StunDictionaryWriter writer; writer.SetUInt32(kKey1)->SetValue(27); EXPECT_EQ(writer->GetUInt32(kKey1)->value(), 27u); StunDictionaryView dictionary; EXPECT_TRUE(dictionary.empty()); Sync(dictionary, writer); EXPECT_EQ(writer->GetUInt32(kKey1)->value(), 27u); writer.SetUInt64(kKey1)->SetValue(29); EXPECT_EQ(writer->GetUInt32(kKey1), nullptr); EXPECT_EQ(writer->GetUInt64(kKey1)->value(), 29ull); Sync(dictionary, writer); EXPECT_EQ(dictionary.GetUInt32(kKey1), nullptr); EXPECT_EQ(dictionary.GetUInt64(kKey1)->value(), 29ull); EXPECT_EQ(dictionary.bytes_stored(), 16); } TEST(StunDictionary, Pending) { StunDictionaryWriter writer; EXPECT_EQ(writer.Pending(), 0); EXPECT_FALSE(writer.Pending(kKey1)); writer.SetUInt32(kKey1)->SetValue(27); EXPECT_EQ(writer.Pending(), 1); EXPECT_TRUE(writer.Pending(kKey1)); writer.SetUInt32(kKey1)->SetValue(29); EXPECT_EQ(writer.Pending(), 1); EXPECT_TRUE(writer.Pending(kKey1)); writer.SetUInt32(kKey1 + 1)->SetValue(31); EXPECT_EQ(writer.Pending(), 2); EXPECT_TRUE(writer.Pending(kKey1)); EXPECT_TRUE(writer.Pending(kKey1 + 1)); StunDictionaryView dictionary; Sync(dictionary, writer); EXPECT_EQ(writer.Pending(), 0); EXPECT_FALSE(writer.Pending(kKey1)); writer.SetUInt32(kKey1)->SetValue(32); EXPECT_EQ(writer.Pending(), 1); EXPECT_TRUE(writer.Pending(kKey1)); } TEST(StunDictionary, Delete) { StunDictionaryWriter writer; StunDictionaryView dictionary; writer.SetUInt32(kKey1)->SetValue(27); Sync(dictionary, writer); EXPECT_EQ(dictionary.GetUInt32(kKey1)->value(), 27u); EXPECT_EQ(dictionary.bytes_stored(), 12); writer.Delete(kKey1); Sync(dictionary, writer); EXPECT_EQ(dictionary.GetUInt32(kKey1), nullptr); EXPECT_EQ(dictionary.bytes_stored(), 8); writer.Delete(kKey1); EXPECT_EQ(writer.Pending(), 0); } TEST(StunDictionary, MultiWriter) { StunDictionaryWriter writer1; StunDictionaryWriter writer2; StunDictionaryView dictionary; writer1.SetUInt32(kKey1)->SetValue(27); Sync(dictionary, writer1); EXPECT_EQ(dictionary.GetUInt32(kKey1)->value(), 27u); writer2.SetUInt32(kKey1 + 1)->SetValue(28); Sync(dictionary, writer2); EXPECT_EQ(dictionary.GetUInt32(kKey1 + 1)->value(), 28u); writer1.Delete(kKey1); Sync(dictionary, writer1); EXPECT_EQ(dictionary.GetUInt32(kKey1), nullptr); writer2.Delete(kKey1 + 1); Sync(dictionary, writer2); EXPECT_EQ(dictionary.GetUInt32(kKey1 + 1), nullptr); } TEST(StunDictionary, BytesStoredIsCountedCorrectlyAfterMultipleUpdates) { StunDictionaryWriter writer; StunDictionaryView dictionary; for (int i = 0; i < 10; i++) { writer.SetUInt32(kKey1)->SetValue(27); writer.SetUInt64(kKey1 + 1)->SetValue(28); Sync(dictionary, writer); EXPECT_EQ(dictionary.bytes_stored(), 28); EXPECT_EQ(dictionary.GetUInt32(kKey1)->value(), 27u); EXPECT_EQ(dictionary.GetUInt64(kKey1 + 1)->value(), 28ull); writer.Delete(kKey1); Sync(dictionary, writer); EXPECT_EQ(dictionary.bytes_stored(), 24); EXPECT_EQ(dictionary.GetUInt32(kKey1), nullptr); EXPECT_EQ(dictionary.GetUInt64(kKey1 + 1)->value(), 28ull); writer.Delete(kKey1 + 1); Sync(dictionary, writer); EXPECT_EQ(dictionary.bytes_stored(), 16); EXPECT_EQ(dictionary.GetUInt32(kKey1), nullptr); EXPECT_EQ(dictionary.GetUInt64(kKey1 + 1), nullptr); } } TEST(StunDictionary, MaxBytesStoredCausesErrorOnOverflow) { StunDictionaryWriter writer; StunDictionaryView dictionary; dictionary.set_max_bytes_stored(30); writer.SetUInt32(kKey1)->SetValue(27); writer.SetUInt64(kKey1 + 1)->SetValue(28); Sync(dictionary, writer); EXPECT_EQ(dictionary.bytes_stored(), 28); EXPECT_EQ(dictionary.GetUInt32(kKey1)->value(), 27u); EXPECT_EQ(dictionary.GetUInt64(kKey1 + 1)->value(), 28ull); writer.SetByteString(kKey1 + 2)->CopyBytes("k"); { auto delta = writer.CreateDelta(); auto delta_ack = dictionary.ApplyDelta(*delta); EXPECT_FALSE(delta_ack.ok()); } EXPECT_EQ(dictionary.GetUInt32(kKey1)->value(), 27u); EXPECT_EQ(dictionary.GetUInt64(kKey1 + 1)->value(), 28ull); EXPECT_EQ(dictionary.GetByteString(kKey1 + 2), nullptr); writer.Delete(kKey1 + 1); Sync(dictionary, writer); EXPECT_EQ(dictionary.GetUInt32(kKey1)->value(), 27u); EXPECT_EQ(dictionary.GetUInt64(kKey1 + 1), nullptr); EXPECT_EQ(dictionary.GetByteString(kKey1 + 2)->string_view(), "k"); } TEST(StunDictionary, DataTypes) { StunDictionaryWriter writer; StunDictionaryView dictionary; rtc::SocketAddress addr("127.0.0.1", 8080); writer.SetUInt32(kKey1)->SetValue(27); writer.SetUInt64(kKey1 + 1)->SetValue(28); writer.SetAddress(kKey1 + 2)->SetAddress(addr); writer.SetByteString(kKey1 + 3)->CopyBytes("keso"); writer.SetUInt16List(kKey1 + 4)->AddTypeAtIndex(0, 7); Sync(dictionary, writer); EXPECT_EQ(dictionary.GetUInt32(kKey1)->value(), 27u); EXPECT_EQ(dictionary.GetUInt64(kKey1 + 1)->value(), 28ull); EXPECT_EQ(dictionary.GetAddress(kKey1 + 2)->GetAddress(), addr); EXPECT_EQ(dictionary.GetByteString(kKey1 + 3)->string_view(), "keso"); EXPECT_EQ(dictionary.GetUInt16List(kKey1 + 4)->GetType(0), 7); } TEST(StunDictionary, ParseError) { StunDictionaryWriter writer; StunDictionaryView dictionary; rtc::SocketAddress addr("127.0.0.1", 8080); writer.SetUInt32(kKey1)->SetValue(27); writer.SetUInt64(kKey1 + 1)->SetValue(28); writer.SetAddress(kKey1 + 2)->SetAddress(addr); writer.SetByteString(kKey1 + 3)->CopyBytes("keso"); writer.SetUInt16List(kKey1 + 4)->AddTypeAtIndex(0, 7); auto delta = writer.CreateDelta(); // The first 10 bytes are in the header... // any modification makes parsing fail. for (int i = 0; i < 10; i++) { XorToggle(*delta, i); EXPECT_FALSE(dictionary.ApplyDelta(*delta).ok()); XorToggle(*delta, i); // toogle back } // Remove bytes from the delta. for (size_t i = 0; i < delta->length(); i++) { // The delta does not contain a footer, // so it it possible to Crop at special values (attribute boundaries) // and apply will still work. const std::vector valid_crop_length = {18, 28, 42, 56, 66, 74}; bool valid = std::find(valid_crop_length.begin(), valid_crop_length.end(), i) != valid_crop_length.end(); auto cropped_delta = Crop(*delta, i); if (valid) { EXPECT_TRUE(dictionary.ApplyDelta(*cropped_delta).ok()); } else { EXPECT_FALSE(dictionary.ApplyDelta(*cropped_delta).ok()); } } } } // namespace cricket