Add new GOOG_PING and GOOG_MESSAGE_INTEGRITY_32

This patch adds
- Attribute: STUN_ATTR_GOOG_MESSAGE_INTEGRITY_32
  which is a ordinary message integrity but truncated to 32-bit
- Method: GOOG_PING,
  which will be used for webrtc:11100

Both the attribute and the method has been registered at iana,
https://www.iana.org/assignments/stun-parameters/stun-parameters.xhtml#stun-parameters-4

BUG=webrtc:11100

Change-Id: Iddd5614473fd6f18fbbe76e72d047c617df7123f
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/160180
Commit-Queue: Jonas Oreland <jonaso@webrtc.org>
Reviewed-by: Björn Terelius <terelius@webrtc.org>
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#29864}
diff --git a/api/transport/stun.cc b/api/transport/stun.cc
index 7fa3c90..80b7b82 100644
--- a/api/transport/stun.cc
+++ b/api/transport/stun.cc
@@ -132,6 +132,14 @@
   return attribute;
 }
 
+void StunMessage::ClearAttributes() {
+  for (auto it = attrs_.rbegin(); it != attrs_.rend(); ++it) {
+    (*it)->SetOwner(nullptr);
+  }
+  attrs_.clear();
+  length_ = 0;
+}
+
 const StunAddressAttribute* StunMessage::GetAddress(int type) const {
   switch (type) {
     case STUN_ATTR_MAPPED_ADDRESS: {
@@ -180,11 +188,31 @@
       GetAttribute(STUN_ATTR_UNKNOWN_ATTRIBUTES));
 }
 
-// Verifies a STUN message has a valid MESSAGE-INTEGRITY attribute, using the
-// procedure outlined in RFC 5389, section 15.4.
 bool StunMessage::ValidateMessageIntegrity(const char* data,
                                            size_t size,
                                            const std::string& password) {
+  return ValidateMessageIntegrityOfType(STUN_ATTR_MESSAGE_INTEGRITY,
+                                        kStunMessageIntegritySize, data, size,
+                                        password);
+}
+
+bool StunMessage::ValidateMessageIntegrity32(const char* data,
+                                             size_t size,
+                                             const std::string& password) {
+  return ValidateMessageIntegrityOfType(STUN_ATTR_GOOG_MESSAGE_INTEGRITY_32,
+                                        kStunMessageIntegrity32Size, data, size,
+                                        password);
+}
+
+// Verifies a STUN message has a valid MESSAGE-INTEGRITY attribute, using the
+// procedure outlined in RFC 5389, section 15.4.
+bool StunMessage::ValidateMessageIntegrityOfType(int mi_attr_type,
+                                                 size_t mi_attr_size,
+                                                 const char* data,
+                                                 size_t size,
+                                                 const std::string& password) {
+  RTC_DCHECK(mi_attr_size <= kStunMessageIntegritySize);
+
   // Verifying the size of the message.
   if ((size % 4) != 0 || size < kStunHeaderSize) {
     return false;
@@ -206,8 +234,8 @@
     attr_length = rtc::GetBE16(&data[current_pos + sizeof(attr_type)]);
 
     // If M-I, sanity check it, and break out.
-    if (attr_type == STUN_ATTR_MESSAGE_INTEGRITY) {
-      if (attr_length != kStunMessageIntegritySize ||
+    if (attr_type == mi_attr_type) {
+      if (attr_length != mi_attr_size ||
           current_pos + sizeof(attr_type) + sizeof(attr_length) + attr_length >
               size) {
         return false;
@@ -231,11 +259,11 @@
   size_t mi_pos = current_pos;
   std::unique_ptr<char[]> temp_data(new char[current_pos]);
   memcpy(temp_data.get(), data, current_pos);
-  if (size > mi_pos + kStunAttributeHeaderSize + kStunMessageIntegritySize) {
+  if (size > mi_pos + kStunAttributeHeaderSize + mi_attr_size) {
     // Stun message has other attributes after message integrity.
     // Adjust the length parameter in stun message to calculate HMAC.
     size_t extra_offset =
-        size - (mi_pos + kStunAttributeHeaderSize + kStunMessageIntegritySize);
+        size - (mi_pos + kStunAttributeHeaderSize + mi_attr_size);
     size_t new_adjusted_len = size - extra_offset - kStunHeaderSize;
 
     // Writing new length of the STUN message @ Message Length in temp buffer.
@@ -252,23 +280,41 @@
       rtc::ComputeHmac(rtc::DIGEST_SHA_1, password.c_str(), password.size(),
                        temp_data.get(), mi_pos, hmac, sizeof(hmac));
   RTC_DCHECK(ret == sizeof(hmac));
-  if (ret != sizeof(hmac))
+  if (ret != sizeof(hmac)) {
     return false;
+  }
 
   // Comparing the calculated HMAC with the one present in the message.
   return memcmp(data + current_pos + kStunAttributeHeaderSize, hmac,
-                sizeof(hmac)) == 0;
+                mi_attr_size) == 0;
 }
 
 bool StunMessage::AddMessageIntegrity(const std::string& password) {
-  return AddMessageIntegrity(password.c_str(), password.size());
+  return AddMessageIntegrityOfType(STUN_ATTR_MESSAGE_INTEGRITY,
+                                   kStunMessageIntegritySize, password.c_str(),
+                                   password.size());
 }
 
 bool StunMessage::AddMessageIntegrity(const char* key, size_t keylen) {
+  return AddMessageIntegrityOfType(STUN_ATTR_MESSAGE_INTEGRITY,
+                                   kStunMessageIntegritySize, key, keylen);
+}
+
+bool StunMessage::AddMessageIntegrity32(absl::string_view password) {
+  return AddMessageIntegrityOfType(STUN_ATTR_GOOG_MESSAGE_INTEGRITY_32,
+                                   kStunMessageIntegrity32Size, password.data(),
+                                   password.length());
+}
+
+bool StunMessage::AddMessageIntegrityOfType(int attr_type,
+                                            size_t attr_size,
+                                            const char* key,
+                                            size_t keylen) {
   // Add the attribute with a dummy value. Since this is a known attribute, it
   // can't fail.
+  RTC_DCHECK(attr_size <= kStunMessageIntegritySize);
   auto msg_integrity_attr_ptr = std::make_unique<StunByteStringAttribute>(
-      STUN_ATTR_MESSAGE_INTEGRITY, std::string(kStunMessageIntegritySize, '0'));
+      attr_type, std::string(attr_size, '0'));
   auto* msg_integrity_attr = msg_integrity_attr_ptr.get();
   AddAttribute(std::move(msg_integrity_attr_ptr));
 
@@ -290,7 +336,7 @@
   }
 
   // Insert correct HMAC into the attribute.
-  msg_integrity_attr->CopyBytes(hmac, sizeof(hmac));
+  msg_integrity_attr->CopyBytes(hmac, attr_size);
   return true;
 }
 
@@ -973,6 +1019,14 @@
   SetLength(static_cast<uint16_t>(attr_types_->size() * 2));
 }
 
+void StunUInt16ListAttribute::AddTypeAtIndex(uint16_t index, uint16_t value) {
+  if (attr_types_->size() < static_cast<size_t>(index + 1)) {
+    attr_types_->resize(index + 1);
+  }
+  (*attr_types_)[index] = value;
+  SetLength(static_cast<uint16_t>(attr_types_->size() * 2));
+}
+
 bool StunUInt16ListAttribute::Read(ByteBufferReader* buf) {
   if (length() % 2) {
     return false;
diff --git a/api/transport/stun.h b/api/transport/stun.h
index 02b352c..857a381 100644
--- a/api/transport/stun.h
+++ b/api/transport/stun.h
@@ -33,6 +33,11 @@
   STUN_BINDING_INDICATION = 0x0011,
   STUN_BINDING_RESPONSE = 0x0101,
   STUN_BINDING_ERROR_RESPONSE = 0x0111,
+
+  // Method 0x80
+  GOOG_PING_REQUEST = 0x200,
+  GOOG_PING_RESPONSE = 0x300,
+  GOOG_PING_ERROR_RESPONSE = 0x310,
 };
 
 // These are all known STUN attributes, defined in RFC 5389 and elsewhere.
@@ -119,6 +124,8 @@
 
 // STUN Message Integrity HMAC length.
 const size_t kStunMessageIntegritySize = 20;
+// Size of STUN_ATTR_MESSAGE_INTEGRITY_32
+const size_t kStunMessageIntegrity32Size = 4;
 
 class StunAddressAttribute;
 class StunAttribute;
@@ -174,16 +181,27 @@
   // Remove the last occurrence of an attribute.
   std::unique_ptr<StunAttribute> RemoveAttribute(int type);
 
+  // Remote all attributes and releases them.
+  void ClearAttributes();
+
   // Validates that a raw STUN message has a correct MESSAGE-INTEGRITY value.
   // This can't currently be done on a StunMessage, since it is affected by
   // padding data (which we discard when reading a StunMessage).
   static bool ValidateMessageIntegrity(const char* data,
                                        size_t size,
                                        const std::string& password);
+  static bool ValidateMessageIntegrity32(const char* data,
+                                         size_t size,
+                                         const std::string& password);
+
   // Adds a MESSAGE-INTEGRITY attribute that is valid for the current message.
   bool AddMessageIntegrity(const std::string& password);
   bool AddMessageIntegrity(const char* key, size_t keylen);
 
+  // Adds a STUN_ATTR_GOOG_MESSAGE_INTEGRITY_32 attribute that is valid for the
+  // current message.
+  bool AddMessageIntegrity32(absl::string_view password);
+
   // Verifies that a given buffer is STUN by checking for a correct FINGERPRINT.
   static bool ValidateFingerprint(const char* data, size_t size);
 
@@ -213,6 +231,15 @@
   StunAttribute* CreateAttribute(int type, size_t length) /* const*/;
   const StunAttribute* GetAttribute(int type) const;
   static bool IsValidTransactionId(const std::string& transaction_id);
+  bool AddMessageIntegrityOfType(int mi_attr_type,
+                                 size_t mi_attr_size,
+                                 const char* key,
+                                 size_t keylen);
+  static bool ValidateMessageIntegrityOfType(int mi_attr_type,
+                                             size_t mi_attr_size,
+                                             const char* data,
+                                             size_t size,
+                                             const std::string& password);
 
   uint16_t type_;
   uint16_t length_;
@@ -462,6 +489,7 @@
   uint16_t GetType(int index) const;
   void SetType(int index, uint16_t value);
   void AddType(uint16_t value);
+  void AddTypeAtIndex(uint16_t index, uint16_t value);
 
   bool Read(rtc::ByteBufferReader* buf) override;
   bool Write(rtc::ByteBufferWriter* buf) const override;
@@ -620,6 +648,8 @@
   STUN_ATTR_LAST_ICE_CHECK_RECEIVED = 0xC058,
   // Uint16List. Miscellaneous attributes for future extension.
   STUN_ATTR_GOOG_MISC_INFO = 0xC059,
+  // MESSAGE-INTEGRITY truncated to 32-bit.
+  STUN_ATTR_GOOG_MESSAGE_INTEGRITY_32 = 0xC060,
 };
 
 // When adding new attributes to STUN_ATTR_GOOG_MISC_INFO
diff --git a/api/transport/stun_unittest.cc b/api/transport/stun_unittest.cc
index 4baca59..c75fb90 100644
--- a/api/transport/stun_unittest.cc
+++ b/api/transport/stun_unittest.cc
@@ -332,6 +332,33 @@
   0xe5, 0x7a, 0x3b, 0xcf    //    CRC32 fingerprint
 };
 
+// 2.1.  Sample Request
+static const unsigned char kSampleRequestMI32[] = {
+  0x00, 0x01, 0x00, 0x48,   //    Request type and message length
+  0x21, 0x12, 0xa4, 0x42,   //    Magic cookie
+  0xb7, 0xe7, 0xa7, 0x01,   // }
+  0xbc, 0x34, 0xd6, 0x86,   // }  Transaction ID
+  0xfa, 0x87, 0xdf, 0xae,   // }
+  0x80, 0x22, 0x00, 0x10,   //    SOFTWARE attribute header
+  0x53, 0x54, 0x55, 0x4e,   // }
+  0x20, 0x74, 0x65, 0x73,   // }  User-agent...
+  0x74, 0x20, 0x63, 0x6c,   // }  ...name
+  0x69, 0x65, 0x6e, 0x74,   // }
+  0x00, 0x24, 0x00, 0x04,   //    PRIORITY attribute header
+  0x6e, 0x00, 0x01, 0xff,   //    ICE priority value
+  0x80, 0x29, 0x00, 0x08,   //    ICE-CONTROLLED attribute header
+  0x93, 0x2f, 0xf9, 0xb1,   // }  Pseudo-random tie breaker...
+  0x51, 0x26, 0x3b, 0x36,   // }   ...for ICE control
+  0x00, 0x06, 0x00, 0x09,   //    USERNAME attribute header
+  0x65, 0x76, 0x74, 0x6a,   // }
+  0x3a, 0x68, 0x36, 0x76,   // }  Username (9 bytes) and padding (3 bytes)
+  0x59, 0x20, 0x20, 0x20,   // }
+  0xC0, 0x60, 0x00, 0x04,   //    MESSAGE-INTEGRITY-32 attribute header
+  0x45, 0x45, 0xce, 0x7c,   // }  HMAC-SHA1 fingerprint (first 32 bit)
+  0x80, 0x28, 0x00, 0x04,   //    FINGERPRINT attribute header
+  0xe5, 0x7a, 0x3b, 0xcf    //    CRC32 fingerprint
+};
+
 // 2.2.  Sample IPv4 Response
 static const unsigned char kRfc5769SampleResponse[] = {
   0x01, 0x01, 0x00, 0x3c,  //     Response type and message length
@@ -451,6 +478,14 @@
   0x74, 0x2a, 0xf9, 0xe3   // }
 };
 
+// This truncated HMAC differs from kCalculatedHmac1
+// above since the sum is computed including header
+// and the header is different since the message is shorter
+// than when MESSAGE-INTEGRITY is used.
+static const unsigned char kCalculatedHmac1_32[] = {
+  0xda, 0x39, 0xde, 0x5d,  // }
+};
+
 // Length parameter is changed to 0x1c from 0x3c.
 // AddMessageIntegrity will add MI information and update the length param
 // accordingly.
@@ -479,6 +514,14 @@
   0x43, 0x14, 0x10, 0x28   // }
 };
 
+// This truncated HMAC differs from kCalculatedHmac2
+// above since the sum is computed including header
+// and the header is different since the message is shorter
+// than when MESSAGE-INTEGRITY is used.
+static const unsigned char kCalculatedHmac2_32[] = {
+  0xe7, 0x5c, 0xd3, 0x16,  // }
+};
+
 // clang-format on
 
 // A transaction ID without the 'magic cookie' portion
@@ -1272,6 +1315,123 @@
 }
 
 // Check our STUN message validation code against the RFC5769 test messages.
+TEST_F(StunTest, ValidateMessageIntegrity32) {
+  // Try the messages from RFC 5769.
+  EXPECT_TRUE(StunMessage::ValidateMessageIntegrity32(
+      reinterpret_cast<const char*>(kSampleRequestMI32),
+      sizeof(kSampleRequestMI32), kRfc5769SampleMsgPassword));
+  EXPECT_FALSE(StunMessage::ValidateMessageIntegrity32(
+      reinterpret_cast<const char*>(kSampleRequestMI32),
+      sizeof(kSampleRequestMI32), "InvalidPassword"));
+
+  // Try some edge cases.
+  EXPECT_FALSE(StunMessage::ValidateMessageIntegrity32(
+      reinterpret_cast<const char*>(kStunMessageWithZeroLength),
+      sizeof(kStunMessageWithZeroLength), kRfc5769SampleMsgPassword));
+  EXPECT_FALSE(StunMessage::ValidateMessageIntegrity32(
+      reinterpret_cast<const char*>(kStunMessageWithExcessLength),
+      sizeof(kStunMessageWithExcessLength), kRfc5769SampleMsgPassword));
+  EXPECT_FALSE(StunMessage::ValidateMessageIntegrity32(
+      reinterpret_cast<const char*>(kStunMessageWithSmallLength),
+      sizeof(kStunMessageWithSmallLength), kRfc5769SampleMsgPassword));
+
+  // Again, but with the lengths matching what is claimed in the headers.
+  EXPECT_FALSE(StunMessage::ValidateMessageIntegrity32(
+      reinterpret_cast<const char*>(kStunMessageWithZeroLength),
+      kStunHeaderSize + rtc::GetBE16(&kStunMessageWithZeroLength[2]),
+      kRfc5769SampleMsgPassword));
+  EXPECT_FALSE(StunMessage::ValidateMessageIntegrity32(
+      reinterpret_cast<const char*>(kStunMessageWithExcessLength),
+      kStunHeaderSize + rtc::GetBE16(&kStunMessageWithExcessLength[2]),
+      kRfc5769SampleMsgPassword));
+  EXPECT_FALSE(StunMessage::ValidateMessageIntegrity32(
+      reinterpret_cast<const char*>(kStunMessageWithSmallLength),
+      kStunHeaderSize + rtc::GetBE16(&kStunMessageWithSmallLength[2]),
+      kRfc5769SampleMsgPassword));
+
+  // Check that a too-short HMAC doesn't cause buffer overflow.
+  EXPECT_FALSE(StunMessage::ValidateMessageIntegrity32(
+      reinterpret_cast<const char*>(kStunMessageWithBadHmacAtEnd),
+      sizeof(kStunMessageWithBadHmacAtEnd), kRfc5769SampleMsgPassword));
+
+  // Test that munging a single bit anywhere in the message causes the
+  // message-integrity check to fail, unless it is after the M-I attribute.
+  char buf[sizeof(kSampleRequestMI32)];
+  memcpy(buf, kSampleRequestMI32, sizeof(kSampleRequestMI32));
+  for (size_t i = 0; i < sizeof(buf); ++i) {
+    buf[i] ^= 0x01;
+    if (i > 0)
+      buf[i - 1] ^= 0x01;
+    EXPECT_EQ(i >= sizeof(buf) - 8,
+              StunMessage::ValidateMessageIntegrity32(
+                  buf, sizeof(buf), kRfc5769SampleMsgPassword));
+  }
+}
+
+// Validate that we generate correct MESSAGE-INTEGRITY-32 attributes.
+TEST_F(StunTest, AddMessageIntegrity32) {
+  IceMessage msg;
+  rtc::ByteBufferReader buf(
+      reinterpret_cast<const char*>(kRfc5769SampleRequestWithoutMI),
+      sizeof(kRfc5769SampleRequestWithoutMI));
+  EXPECT_TRUE(msg.Read(&buf));
+  EXPECT_TRUE(msg.AddMessageIntegrity32(kRfc5769SampleMsgPassword));
+  const StunByteStringAttribute* mi_attr =
+      msg.GetByteString(STUN_ATTR_GOOG_MESSAGE_INTEGRITY_32);
+  EXPECT_EQ(4U, mi_attr->length());
+  EXPECT_EQ(0, memcmp(mi_attr->bytes(), kCalculatedHmac1_32,
+                      sizeof(kCalculatedHmac1_32)));
+
+  rtc::ByteBufferWriter buf1;
+  EXPECT_TRUE(msg.Write(&buf1));
+  EXPECT_TRUE(StunMessage::ValidateMessageIntegrity32(
+      reinterpret_cast<const char*>(buf1.Data()), buf1.Length(),
+      kRfc5769SampleMsgPassword));
+
+  IceMessage msg2;
+  rtc::ByteBufferReader buf2(
+      reinterpret_cast<const char*>(kRfc5769SampleResponseWithoutMI),
+      sizeof(kRfc5769SampleResponseWithoutMI));
+  EXPECT_TRUE(msg2.Read(&buf2));
+  EXPECT_TRUE(msg2.AddMessageIntegrity32(kRfc5769SampleMsgPassword));
+  const StunByteStringAttribute* mi_attr2 =
+      msg2.GetByteString(STUN_ATTR_GOOG_MESSAGE_INTEGRITY_32);
+  EXPECT_EQ(4U, mi_attr2->length());
+  EXPECT_EQ(0, memcmp(mi_attr2->bytes(), kCalculatedHmac2_32,
+                      sizeof(kCalculatedHmac2_32)));
+
+  rtc::ByteBufferWriter buf3;
+  EXPECT_TRUE(msg2.Write(&buf3));
+  EXPECT_TRUE(StunMessage::ValidateMessageIntegrity32(
+      reinterpret_cast<const char*>(buf3.Data()), buf3.Length(),
+      kRfc5769SampleMsgPassword));
+}
+
+// Validate that the message validates if both MESSAGE-INTEGRITY-32 and
+// MESSAGE-INTEGRITY are present in the message.
+// This is not expected to be used, but is not forbidden.
+TEST_F(StunTest, AddMessageIntegrity32AndMessageIntegrity) {
+  IceMessage msg;
+  auto attr = StunAttribute::CreateByteString(STUN_ATTR_USERNAME);
+  attr->CopyBytes("keso", sizeof("keso"));
+  msg.AddAttribute(std::move(attr));
+  msg.AddMessageIntegrity32("password1");
+  msg.AddMessageIntegrity("password2");
+
+  rtc::ByteBufferWriter buf1;
+  EXPECT_TRUE(msg.Write(&buf1));
+  EXPECT_TRUE(StunMessage::ValidateMessageIntegrity32(
+      reinterpret_cast<const char*>(buf1.Data()), buf1.Length(), "password1"));
+  EXPECT_TRUE(StunMessage::ValidateMessageIntegrity(
+      reinterpret_cast<const char*>(buf1.Data()), buf1.Length(), "password2"));
+
+  EXPECT_FALSE(StunMessage::ValidateMessageIntegrity32(
+      reinterpret_cast<const char*>(buf1.Data()), buf1.Length(), "password2"));
+  EXPECT_FALSE(StunMessage::ValidateMessageIntegrity(
+      reinterpret_cast<const char*>(buf1.Data()), buf1.Length(), "password1"));
+}
+
+// Check our STUN message validation code against the RFC5769 test messages.
 TEST_F(StunTest, ValidateFingerprint) {
   EXPECT_TRUE(StunMessage::ValidateFingerprint(
       reinterpret_cast<const char*>(kRfc5769SampleRequest),
@@ -1531,6 +1691,20 @@
   EXPECT_EQ(msg.RemoveAttribute(STUN_ATTR_USERNAME), nullptr);
 }
 
+// Test that we can remove attribute from a message.
+TEST_F(StunTest, ClearAttributes) {
+  StunMessage msg;
+
+  auto attr = StunAttribute::CreateByteString(STUN_ATTR_USERNAME);
+  attr->CopyBytes("kes", sizeof("kes"));
+  msg.AddAttribute(std::move(attr));
+  size_t len = msg.length();
+
+  msg.ClearAttributes();
+  EXPECT_EQ(msg.length(), len - /* 3 + 1 byte padding + header */ 8);
+  EXPECT_EQ(nullptr, msg.GetByteString(STUN_ATTR_USERNAME));
+}
+
 // Test CopyStunAttribute
 TEST_F(StunTest, CopyAttribute) {
   rtc::ByteBufferWriter buf;
@@ -1560,6 +1734,20 @@
       CheckStunAddressAttribute(static_cast<StunAddressAttribute*>(copy.get()),
                                 STUN_ADDRESS_IPV6, kTestMessagePort2, test_ip);
     }
+
+    {  // Test StunAddressAttribute.
+      rtc::IPAddress test_ip(kIPv6TestAddress2);
+      auto addr = StunAttribute::CreateAddress(STUN_ATTR_XOR_MAPPED_ADDRESS);
+      rtc::SocketAddress test_addr(test_ip, kTestMessagePort2);
+      addr->SetAddress(test_addr);
+      CheckStunAddressAttribute(addr.get(), STUN_ADDRESS_IPV6,
+                                kTestMessagePort2, test_ip);
+
+      auto copy = CopyStunAttribute(*addr.get(), buffer_ptr);
+      ASSERT_EQ(copy->value_type(), STUN_VALUE_ADDRESS);
+      CheckStunAddressAttribute(static_cast<StunAddressAttribute*>(copy.get()),
+                                STUN_ADDRESS_IPV6, kTestMessagePort2, test_ip);
+    }
   }
 }
 
@@ -1581,9 +1769,9 @@
   msg.SetTransactionID("ABCDEFGH");
   auto list =
       StunAttribute::CreateUInt16ListAttribute(STUN_ATTR_GOOG_MISC_INFO);
-  list->AddType(0x1U);
-  list->AddType(0x1000U);
-  list->AddType(0xAB0CU);
+  list->AddTypeAtIndex(0, 0x1U);
+  list->AddTypeAtIndex(3, 0x1000U);
+  list->AddTypeAtIndex(2, 0xAB0CU);
   msg.AddAttribute(std::move(list));
   CheckStunHeader(msg, STUN_BINDING_REQUEST, (size - 20));
 
@@ -1598,9 +1786,10 @@
   const StunUInt16ListAttribute* types =
       msg.GetUInt16List(STUN_ATTR_GOOG_MISC_INFO);
   ASSERT_TRUE(types != NULL);
-  EXPECT_EQ(3U, types->Size());
+  EXPECT_EQ(4U, types->Size());
   EXPECT_EQ(0x1U, types->GetType(0));
-  EXPECT_EQ(0x1000U, types->GetType(1));
+  EXPECT_EQ(0x0U, types->GetType(1));
+  EXPECT_EQ(0x1000U, types->GetType(3));
   EXPECT_EQ(0xAB0CU, types->GetType(2));
 }