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}
diff --git a/api/transport/stun.cc b/api/transport/stun.cc
index 80b7b82..4ae834c 100644
--- a/api/transport/stun.cc
+++ b/api/transport/stun.cc
@@ -370,6 +370,28 @@
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 @@
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 @@
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
diff --git a/api/transport/stun.h b/api/transport/stun.h
index 857a381..1c2cb80 100644
--- a/api/transport/stun.h
+++ b/api/transport/stun.h
@@ -202,6 +202,12 @@
// 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 @@
// 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 @@
uint16_t length_;
std::string transaction_id_;
uint32_t reduced_transaction_id_;
- std::vector<std::unique_ptr<StunAttribute>> attrs_;
uint32_t stun_magic_cookie_;
};
diff --git a/api/transport/stun_unittest.cc b/api/transport/stun_unittest.cc
index c75fb90..84a61f5 100644
--- a/api/transport/stun_unittest.cc
+++ b/api/transport/stun_unittest.cc
@@ -1751,6 +1751,109 @@
}
}
+// 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 @@
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