Add new Stun utility functions

This patch introduces 3 new functions on StunMessages
- Clone, copy a message
- IsStunMethod, verifies that a buffer is a StunMessage
  w/o requring a fingerprint
- EqualAttributes, compare attributes in two stun messages
  (with filter)

This methods will be used to implement GOOG_PING

BUG=webrtc:11100

Change-Id: I284726c74aa0437be0bb9fbcf943c7d64a18acec
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/160281
Commit-Queue: Jonas Oreland <jonaso@webrtc.org>
Reviewed-by: Björn Terelius <terelius@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#29950}
This commit is contained in:
Jonas Oreland
2019-11-28 17:08:07 +01:00
committed by Commit Bot
parent 2dec496f80
commit 253d50fbe6
3 changed files with 202 additions and 1 deletions

View File

@ -370,6 +370,28 @@ bool StunMessage::ValidateFingerprint(const char* data, size_t size) {
rtc::ComputeCrc32(data, size - fingerprint_attr_size));
}
bool StunMessage::IsStunMethod(rtc::ArrayView<int> methods,
const char* data,
size_t size) {
// Check the message length.
if (size % 4 != 0 || size < kStunHeaderSize)
return false;
// Skip the rest if the magic cookie isn't present.
const char* magic_cookie =
data + kStunTransactionIdOffset - kStunMagicCookieLength;
if (rtc::GetBE32(magic_cookie) != kStunMagicCookie)
return false;
int method = rtc::GetBE16(data);
for (int m : methods) {
if (m == method) {
return true;
}
}
return false;
}
bool StunMessage::AddFingerprint() {
// Add the attribute with a dummy value. Since this is a known attribute,
// it can't fail.
@ -557,6 +579,44 @@ bool StunMessage::IsValidTransactionId(const std::string& transaction_id) {
transaction_id.size() == kStunLegacyTransactionIdLength;
}
bool StunMessage::EqualAttributes(
const StunMessage* other,
std::function<bool(int type)> attribute_type_mask) const {
RTC_DCHECK(other != nullptr);
rtc::ByteBufferWriter tmp_buffer_ptr1;
rtc::ByteBufferWriter tmp_buffer_ptr2;
for (const auto& attr : attrs_) {
if (attribute_type_mask(attr->type())) {
const StunAttribute* other_attr = other->GetAttribute(attr->type());
if (other_attr == nullptr) {
return false;
}
tmp_buffer_ptr1.Clear();
tmp_buffer_ptr2.Clear();
attr->Write(&tmp_buffer_ptr1);
other_attr->Write(&tmp_buffer_ptr2);
if (tmp_buffer_ptr1.Length() != tmp_buffer_ptr2.Length()) {
return false;
}
if (memcmp(tmp_buffer_ptr1.Data(), tmp_buffer_ptr2.Data(),
tmp_buffer_ptr1.Length()) != 0) {
return false;
}
}
}
for (const auto& attr : other->attrs_) {
if (attribute_type_mask(attr->type())) {
const StunAttribute* own_attr = GetAttribute(attr->type());
if (own_attr == nullptr) {
return false;
}
// we have already compared all values...
}
}
return true;
}
// StunAttribute
StunAttribute::StunAttribute(uint16_t type, uint16_t length)
@ -1205,4 +1265,20 @@ StunMessage* IceMessage::CreateNew() const {
return new IceMessage();
}
std::unique_ptr<StunMessage> StunMessage::Clone() const {
std::unique_ptr<StunMessage> copy(CreateNew());
if (!copy) {
return nullptr;
}
rtc::ByteBufferWriter buf;
if (!Write(&buf)) {
return nullptr;
}
rtc::ByteBufferReader reader(buf);
if (!copy->Read(&reader)) {
return nullptr;
}
return copy;
}
} // namespace cricket

View File

@ -202,6 +202,12 @@ class StunMessage {
// current message.
bool AddMessageIntegrity32(absl::string_view password);
// Verify that a buffer has stun magic cookie and one of the specified
// methods. Note that it does not check for the existance of FINGERPRINT.
static bool IsStunMethod(rtc::ArrayView<int> methods,
const char* data,
size_t size);
// Verifies that a given buffer is STUN by checking for a correct FINGERPRINT.
static bool ValidateFingerprint(const char* data, size_t size);
@ -223,10 +229,20 @@ class StunMessage {
// This is used for testing.
void SetStunMagicCookie(uint32_t val);
// Contruct a copy of |this|.
std::unique_ptr<StunMessage> Clone() const;
// Check if the attributes of this StunMessage equals those of |other|
// for all attributes that |attribute_type_mask| return true
bool EqualAttributes(const StunMessage* other,
std::function<bool(int type)> attribute_type_mask) const;
protected:
// Verifies that the given attribute is allowed for this message.
virtual StunAttributeValueType GetAttributeValueType(int type) const;
std::vector<std::unique_ptr<StunAttribute>> attrs_;
private:
StunAttribute* CreateAttribute(int type, size_t length) /* const*/;
const StunAttribute* GetAttribute(int type) const;
@ -245,7 +261,6 @@ class StunMessage {
uint16_t length_;
std::string transaction_id_;
uint32_t reduced_transaction_id_;
std::vector<std::unique_ptr<StunAttribute>> attrs_;
uint32_t stun_magic_cookie_;
};

View File

@ -1751,6 +1751,109 @@ TEST_F(StunTest, CopyAttribute) {
}
}
// Test Clone
TEST_F(StunTest, Clone) {
IceMessage msg;
{
auto errorcode = StunAttribute::CreateErrorCode();
errorcode->SetCode(kTestErrorCode);
errorcode->SetReason(kTestErrorReason);
msg.AddAttribute(std::move(errorcode));
}
{
auto bytes2 = StunAttribute::CreateByteString(STUN_ATTR_USERNAME);
bytes2->CopyBytes("abcdefghijkl");
msg.AddAttribute(std::move(bytes2));
}
{
auto uval2 = StunAttribute::CreateUInt32(STUN_ATTR_RETRANSMIT_COUNT);
uval2->SetValue(11);
msg.AddAttribute(std::move(uval2));
}
{
auto addr = StunAttribute::CreateAddress(STUN_ATTR_MAPPED_ADDRESS);
addr->SetIP(rtc::IPAddress(kIPv6TestAddress1));
addr->SetPort(kTestMessagePort1);
msg.AddAttribute(std::move(addr));
}
auto copy = msg.Clone();
ASSERT_NE(nullptr, copy.get());
msg.SetTransactionID("0123456789ab");
copy->SetTransactionID("0123456789ab");
rtc::ByteBufferWriter out1;
EXPECT_TRUE(msg.Write(&out1));
rtc::ByteBufferWriter out2;
EXPECT_TRUE(copy->Write(&out2));
ASSERT_EQ(out1.Length(), out2.Length());
EXPECT_EQ(0, memcmp(out1.Data(), out2.Data(), out1.Length()));
}
// Test EqualAttributes
TEST_F(StunTest, EqualAttributes) {
IceMessage msg;
{
auto errorcode = StunAttribute::CreateErrorCode();
errorcode->SetCode(kTestErrorCode);
errorcode->SetReason(kTestErrorReason);
msg.AddAttribute(std::move(errorcode));
}
{
auto bytes2 = StunAttribute::CreateByteString(STUN_ATTR_USERNAME);
bytes2->CopyBytes("abcdefghijkl");
msg.AddAttribute(std::move(bytes2));
}
{
auto uval2 = StunAttribute::CreateUInt32(STUN_ATTR_RETRANSMIT_COUNT);
uval2->SetValue(11);
msg.AddAttribute(std::move(uval2));
}
{
auto addr = StunAttribute::CreateAddress(STUN_ATTR_MAPPED_ADDRESS);
addr->SetIP(rtc::IPAddress(kIPv6TestAddress1));
addr->SetPort(kTestMessagePort1);
msg.AddAttribute(std::move(addr));
}
auto copy = msg.Clone();
ASSERT_NE(nullptr, copy.get());
EXPECT_TRUE(copy->EqualAttributes(&msg, [](int type) { return true; }));
{
auto attr = StunAttribute::CreateByteString(STUN_ATTR_NONCE);
attr->CopyBytes("keso");
msg.AddAttribute(std::move(attr));
EXPECT_FALSE(copy->EqualAttributes(&msg, [](int type) { return true; }));
EXPECT_TRUE(copy->EqualAttributes(
&msg, [](int type) { return type != STUN_ATTR_NONCE; }));
}
{
auto attr = StunAttribute::CreateByteString(STUN_ATTR_NONCE);
attr->CopyBytes("keso");
copy->AddAttribute(std::move(attr));
EXPECT_TRUE(copy->EqualAttributes(&msg, [](int type) { return true; }));
}
{
copy->RemoveAttribute(STUN_ATTR_NONCE);
auto attr = StunAttribute::CreateByteString(STUN_ATTR_NONCE);
attr->CopyBytes("kent");
copy->AddAttribute(std::move(attr));
EXPECT_FALSE(copy->EqualAttributes(&msg, [](int type) { return true; }));
EXPECT_TRUE(copy->EqualAttributes(
&msg, [](int type) { return type != STUN_ATTR_NONCE; }));
}
{
msg.RemoveAttribute(STUN_ATTR_NONCE);
EXPECT_FALSE(copy->EqualAttributes(&msg, [](int type) { return true; }));
EXPECT_TRUE(copy->EqualAttributes(
&msg, [](int type) { return type != STUN_ATTR_NONCE; }));
}
}
TEST_F(StunTest, ReduceTransactionIdIsHostOrderIndependent) {
std::string transaction_id = "abcdefghijkl";
StunMessage message;
@ -1793,4 +1896,11 @@ TEST_F(StunTest, GoogMiscInfo) {
EXPECT_EQ(0xAB0CU, types->GetType(2));
}
TEST_F(StunTest, IsStunMethod) {
int methods[] = {STUN_BINDING_REQUEST};
EXPECT_TRUE(StunMessage::IsStunMethod(
methods, reinterpret_cast<const char*>(kRfc5769SampleRequest),
sizeof(kRfc5769SampleRequest)));
}
} // namespace cricket