Use the dummy address 0.0.0.0:9 in the c= and the m= lines if the
default connection address is a hostname candidate.

Using a FQDN in the c= line has caused an inter-op issue with Firefox
when hostname candidates are the only candidates gathered when forming
the media sections. To address this issue, we use 0.0.0.0:9 when a
hostname candidate would be used to populate the c= and the m= lines.

The SDP grammar related to ICE candidates has been moved out of RFC8445,
and is currently defined in draft-ietf-mmusic-ice-sip-sdp. A FQDN
address must not be used in the connection address attribute per the
latest draft, if the ICE agent generates local candidates. Also, the
wildcard addresses  (0.0.0.0 or ::) with port 9 are given the exception
as the connection address that will not result in an ICE mismatch. We
thus adopt the aforementioned solution after combining these
considerations.

Bug: chromium:927309, chromium:982108
Change-Id: I3df2db0f154276da39f99650289cf81baa677e74
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/145280
Commit-Queue: Qingsi Wang <qingsi@webrtc.org>
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#28547}
diff --git a/pc/jsep_session_description_unittest.cc b/pc/jsep_session_description_unittest.cc
index 8abb500..ef86ef4 100644
--- a/pc/jsep_session_description_unittest.cc
+++ b/pc/jsep_session_description_unittest.cc
@@ -223,11 +223,14 @@
   c.set_protocol(cricket::UDP_PROTOCOL_NAME);
   c.set_address(rtc::SocketAddress("example.local", 1234));
   c.set_type(cricket::LOCAL_PORT_TYPE);
-  JsepIceCandidate hostname_candidate("audio", 0, c);
+  const size_t audio_index = 0;
+  JsepIceCandidate hostname_candidate("audio", audio_index, c);
   EXPECT_TRUE(jsep_desc_->AddCandidate(&hostname_candidate));
+
   ASSERT_NE(nullptr, jsep_desc_->description());
-  const auto& content = jsep_desc_->description()->contents()[0];
-  EXPECT_EQ("example.local:1234",
+  ASSERT_EQ(2u, jsep_desc_->description()->contents().size());
+  const auto& content = jsep_desc_->description()->contents()[audio_index];
+  EXPECT_EQ("0.0.0.0:9",
             content.media_description()->connection_address().ToString());
 }
 
@@ -242,6 +245,39 @@
   EXPECT_EQ(sdp, parsed_sdp);
 }
 
+// Test that we can serialize a JsepSessionDescription when a hostname candidate
+// is the default destination and deserialize it again. The connection address
+// in the deserialized description should be the dummy address 0.0.0.0:9.
+TEST_F(JsepSessionDescriptionTest, SerializeDeserializeWithHostnameCandidate) {
+  cricket::Candidate c;
+  c.set_component(cricket::ICE_CANDIDATE_COMPONENT_RTP);
+  c.set_protocol(cricket::UDP_PROTOCOL_NAME);
+  c.set_address(rtc::SocketAddress("example.local", 1234));
+  c.set_type(cricket::LOCAL_PORT_TYPE);
+  const size_t audio_index = 0;
+  const size_t video_index = 1;
+  JsepIceCandidate hostname_candidate_audio("audio", audio_index, c);
+  JsepIceCandidate hostname_candidate_video("video", video_index, c);
+  EXPECT_TRUE(jsep_desc_->AddCandidate(&hostname_candidate_audio));
+  EXPECT_TRUE(jsep_desc_->AddCandidate(&hostname_candidate_video));
+
+  std::string sdp = Serialize(jsep_desc_.get());
+
+  auto parsed_jsep_desc = DeSerialize(sdp);
+  EXPECT_EQ(2u, parsed_jsep_desc->number_of_mediasections());
+
+  ASSERT_NE(nullptr, parsed_jsep_desc->description());
+  ASSERT_EQ(2u, parsed_jsep_desc->description()->contents().size());
+  const auto& audio_content =
+      parsed_jsep_desc->description()->contents()[audio_index];
+  const auto& video_content =
+      parsed_jsep_desc->description()->contents()[video_index];
+  EXPECT_EQ("0.0.0.0:9",
+            audio_content.media_description()->connection_address().ToString());
+  EXPECT_EQ("0.0.0.0:9",
+            video_content.media_description()->connection_address().ToString());
+}
+
 // Tests that we can serialize and deserialize a JsepSesssionDescription
 // with candidates.
 TEST_F(JsepSessionDescriptionTest, SerializeDeserializeWithCandidates) {