Add STUN_ATTR_GOOG_MISC_INFO

This patch adds the new STUN attribute that has been registered at iana,
https://www.iana.org/assignments/stun-parameters/stun-parameters.xhtml#stun-parameters-4

This is part of the effort to land https://webrtc-review.googlesource.com/c/src/+/85520.
I have merged that patch with upstream, and is now doing privacy review of it.

This attribute is hence not yet used.

BUG=webrtc:9446

Change-Id: Iaf177b0c28a6aa830a9422260b67436bb05ac756
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/160043
Reviewed-by: Niels Moller <nisse@webrtc.org>
Reviewed-by: Björn Terelius <terelius@webrtc.org>
Commit-Queue: Jonas Oreland <jonaso@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#29843}
diff --git a/api/transport/stun.cc b/api/transport/stun.cc
index 5e00b33..7fa3c90 100644
--- a/api/transport/stun.cc
+++ b/api/transport/stun.cc
@@ -161,6 +161,10 @@
   return static_cast<const StunByteStringAttribute*>(GetAttribute(type));
 }
 
+const StunUInt16ListAttribute* StunMessage::GetUInt16List(int type) const {
+  return static_cast<const StunUInt16ListAttribute*>(GetAttribute(type));
+}
+
 const StunErrorCodeAttribute* StunMessage::GetErrorCode() const {
   return static_cast<const StunErrorCodeAttribute*>(
       GetAttribute(STUN_ATTR_ERROR_CODE));
@@ -343,8 +347,9 @@
 }
 
 bool StunMessage::Read(ByteBufferReader* buf) {
-  if (!buf->ReadUInt16(&type_))
+  if (!buf->ReadUInt16(&type_)) {
     return false;
+  }
 
   if (type_ & 0x8000) {
     // RTP and RTCP set the MSB of first byte, since first two bits are version,
@@ -352,16 +357,19 @@
     return false;
   }
 
-  if (!buf->ReadUInt16(&length_))
+  if (!buf->ReadUInt16(&length_)) {
     return false;
+  }
 
   std::string magic_cookie;
-  if (!buf->ReadString(&magic_cookie, kStunMagicCookieLength))
+  if (!buf->ReadString(&magic_cookie, kStunMagicCookieLength)) {
     return false;
+  }
 
   std::string transaction_id;
-  if (!buf->ReadString(&transaction_id, kStunTransactionIdLength))
+  if (!buf->ReadString(&transaction_id, kStunTransactionIdLength)) {
     return false;
+  }
 
   uint32_t magic_cookie_int;
   static_assert(sizeof(magic_cookie_int) == kStunMagicCookieLength,
@@ -376,8 +384,9 @@
   transaction_id_ = transaction_id;
   reduced_transaction_id_ = ReduceTransactionId(transaction_id_);
 
-  if (length_ != buf->Length())
+  if (length_ != buf->Length()) {
     return false;
+  }
 
   attrs_.resize(0);
 
@@ -396,11 +405,13 @@
       if ((attr_length % 4) != 0) {
         attr_length += (4 - (attr_length % 4));
       }
-      if (!buf->Consume(attr_length))
+      if (!buf->Consume(attr_length)) {
         return false;
+      }
     } else {
-      if (!attr->Read(buf))
+      if (!attr->Read(buf)) {
         return false;
+      }
       attrs_.push_back(std::move(attr));
     }
   }
@@ -465,6 +476,8 @@
       return STUN_VALUE_UINT32;
     case STUN_ATTR_LAST_ICE_CHECK_RECEIVED:
       return STUN_VALUE_BYTE_STRING;
+    case STUN_ATTR_GOOG_MISC_INFO:
+      return STUN_VALUE_UINT16_LIST;
     default:
       return STUN_VALUE_UNKNOWN;
   }
@@ -573,6 +586,11 @@
 }
 
 std::unique_ptr<StunUInt16ListAttribute>
+StunAttribute::CreateUInt16ListAttribute(uint16_t type) {
+  return std::make_unique<StunUInt16ListAttribute>(type, 0);
+}
+
+std::unique_ptr<StunUInt16ListAttribute>
 StunAttribute::CreateUnknownAttributes() {
   return std::make_unique<StunUInt16ListAttribute>(STUN_ATTR_UNKNOWN_ATTRIBUTES,
                                                    0);
@@ -956,8 +974,9 @@
 }
 
 bool StunUInt16ListAttribute::Read(ByteBufferReader* buf) {
-  if (length() % 2)
+  if (length() % 2) {
     return false;
+  }
 
   for (size_t i = 0; i < length() / 2; i++) {
     uint16_t attr;
diff --git a/api/transport/stun.h b/api/transport/stun.h
index e19f196..02b352c 100644
--- a/api/transport/stun.h
+++ b/api/transport/stun.h
@@ -159,6 +159,7 @@
   const StunUInt32Attribute* GetUInt32(int type) const;
   const StunUInt64Attribute* GetUInt64(int type) const;
   const StunByteStringAttribute* GetByteString(int type) const;
+  const StunUInt16ListAttribute* GetUInt16List(int type) const;
 
   // Gets these specific attribute values.
   const StunErrorCodeAttribute* GetErrorCode() const;
@@ -257,6 +258,8 @@
   static std::unique_ptr<StunUInt64Attribute> CreateUInt64(uint16_t type);
   static std::unique_ptr<StunByteStringAttribute> CreateByteString(
       uint16_t type);
+  static std::unique_ptr<StunUInt16ListAttribute> CreateUInt16ListAttribute(
+      uint16_t type);
   static std::unique_ptr<StunErrorCodeAttribute> CreateErrorCode();
   static std::unique_ptr<StunUInt16ListAttribute> CreateUnknownAttributes();
 
@@ -615,8 +618,18 @@
   STUN_ATTR_NETWORK_INFO = 0xC057,
   // Experimental: Transaction ID of the last connectivity check received.
   STUN_ATTR_LAST_ICE_CHECK_RECEIVED = 0xC058,
+  // Uint16List. Miscellaneous attributes for future extension.
+  STUN_ATTR_GOOG_MISC_INFO = 0xC059,
 };
 
+// When adding new attributes to STUN_ATTR_GOOG_MISC_INFO
+// (which is a list of uint16_t), append the indices of these attributes below
+// and do NOT change the exisiting indices. The indices of attributes must be
+// consistent with those used in ConnectionRequest::Prepare when forming a STUN
+// message for the ICE connectivity check, and they are used when parsing a
+// received STUN message.
+enum class IceGoogMiscInfoAttributeIndex {};
+
 // RFC 5245-defined errors.
 enum IceErrorCode {
   STUN_ERROR_ROLE_CONFLICT = 487,
diff --git a/api/transport/stun_unittest.cc b/api/transport/stun_unittest.cc
index 667746e..4baca59 100644
--- a/api/transport/stun_unittest.cc
+++ b/api/transport/stun_unittest.cc
@@ -1571,4 +1571,37 @@
   EXPECT_EQ(reduced_transaction_id, 1835954016u);
 }
 
+TEST_F(StunTest, GoogMiscInfo) {
+  StunMessage msg;
+  const size_t size =
+      /* msg header */ 20 +
+      /* attr header */ 4 +
+      /* 3 * 2 rounded to multiple of 4 */ 8;
+  msg.SetType(STUN_BINDING_REQUEST);
+  msg.SetTransactionID("ABCDEFGH");
+  auto list =
+      StunAttribute::CreateUInt16ListAttribute(STUN_ATTR_GOOG_MISC_INFO);
+  list->AddType(0x1U);
+  list->AddType(0x1000U);
+  list->AddType(0xAB0CU);
+  msg.AddAttribute(std::move(list));
+  CheckStunHeader(msg, STUN_BINDING_REQUEST, (size - 20));
+
+  rtc::ByteBufferWriter out;
+  EXPECT_TRUE(msg.Write(&out));
+  ASSERT_EQ(size, out.Length());
+
+  size_t read_size = ReadStunMessageTestCase(
+      &msg, reinterpret_cast<const unsigned char*>(out.Data()), out.Length());
+  ASSERT_EQ(read_size + 20, size);
+  CheckStunHeader(msg, STUN_BINDING_REQUEST, read_size);
+  const StunUInt16ListAttribute* types =
+      msg.GetUInt16List(STUN_ATTR_GOOG_MISC_INFO);
+  ASSERT_TRUE(types != NULL);
+  EXPECT_EQ(3U, types->Size());
+  EXPECT_EQ(0x1U, types->GetType(0));
+  EXPECT_EQ(0x1000U, types->GetType(1));
+  EXPECT_EQ(0xAB0CU, types->GetType(2));
+}
+
 }  // namespace cricket