diff --git a/pc/jsep_session_description.cc b/pc/jsep_session_description.cc index 9499e9a253..88db17414f 100644 --- a/pc/jsep_session_description.cc +++ b/pc/jsep_session_description.cc @@ -56,6 +56,7 @@ void UpdateConnectionAddress( cricket::MediaContentDescription* media_desc) { int port = kDummyPort; std::string ip = kDummyAddress; + std::string hostname; int current_preference = kPreferenceUnknown; int current_family = AF_UNSPEC; for (size_t i = 0; i < candidate_collection.count(); ++i) { @@ -81,12 +82,16 @@ void UpdateConnectionAddress( } current_preference = preference; current_family = family; - port = jsep_candidate->candidate().address().port(); - ip = jsep_candidate->candidate().address().ipaddr().ToString(); + const rtc::SocketAddress& candidate_addr = + jsep_candidate->candidate().address(); + port = candidate_addr.port(); + ip = candidate_addr.ipaddr().ToString(); + hostname = candidate_addr.hostname(); + } + rtc::SocketAddress connection_addr(ip, port); + if (rtc::IPIsUnspec(connection_addr.ipaddr()) && !hostname.empty()) { + connection_addr = rtc::SocketAddress(hostname, port); } - rtc::SocketAddress connection_addr; - connection_addr.SetIP(ip); - connection_addr.SetPort(port); media_desc->set_connection_address(connection_addr); } diff --git a/pc/jsep_session_description_unittest.cc b/pc/jsep_session_description_unittest.cc index 4dc0788fb7..6ac1a0066a 100644 --- a/pc/jsep_session_description_unittest.cc +++ b/pc/jsep_session_description_unittest.cc @@ -212,6 +212,22 @@ TEST_F(JsepSessionDescriptionTest, AddCandidateDuplicates) { EXPECT_EQ(1u, jsep_desc_->candidates(0)->count()); } +// Test that the connection address is set to a hostname address after adding a +// hostname candidate. +TEST_F(JsepSessionDescriptionTest, AddHostnameCandidate) { + 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); + JsepIceCandidate hostname_candidate("audio", 0, 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", + content.media_description()->connection_address().ToString()); +} + // Test that we can serialize a JsepSessionDescription and deserialize it again. TEST_F(JsepSessionDescriptionTest, SerializeDeserialize) { std::string sdp = Serialize(jsep_desc_.get()); diff --git a/pc/webrtc_sdp.cc b/pc/webrtc_sdp.cc index 736122b7f2..f7a26af32a 100644 --- a/pc/webrtc_sdp.cc +++ b/pc/webrtc_sdp.cc @@ -1385,9 +1385,15 @@ void BuildMediaDescription(const ContentInfo* content_info, } else if (media_desc->connection_address().family() == AF_INET) { os << " " << kConnectionIpv4Addrtype << " " << media_desc->connection_address().ipaddr().ToString(); - } else { + } else if (media_desc->connection_address().family() == AF_INET6) { os << " " << kConnectionIpv6Addrtype << " " << media_desc->connection_address().ipaddr().ToString(); + } else if (!media_desc->connection_address().hostname().empty()) { + // For hostname candidates, we use c=IN IP4 . + os << " " << kConnectionIpv4Addrtype << " " + << media_desc->connection_address().hostname(); + } else { + os << " " << kConnectionIpv4Addrtype << " " << kDummyAddress; } AddLine(os.str(), message); diff --git a/pc/webrtc_sdp_unittest.cc b/pc/webrtc_sdp_unittest.cc index c04a1d9990..b232caf824 100644 --- a/pc/webrtc_sdp_unittest.cc +++ b/pc/webrtc_sdp_unittest.cc @@ -3991,7 +3991,28 @@ TEST_F(WebRtcSdpTest, ParseConnectionDataIPv6) { content2.media_description()->connection_address().ToString()); } -// Test that the invalid or unsupprted connection data cannot be parsed. +// Test that a c= line that contains a hostname connection address can be +// parsed. +TEST_F(WebRtcSdpTest, ParseConnectionDataWithHostnameConnectionAddress) { + JsepSessionDescription jsep_desc(kDummyType); + std::string sdp = kSdpString; + EXPECT_TRUE(SdpDeserialize(sdp, &jsep_desc)); + + sdp = kSdpString; + Replace("c=IN IP4 0.0.0.0\r\n", "c=IN IP4 example.local\r\n", &sdp); + Replace("c=IN IP4 0.0.0.0\r\n", "c=IN IP4 example.local\r\n", &sdp); + ASSERT_TRUE(SdpDeserialize(sdp, &jsep_desc)); + + ASSERT_NE(nullptr, jsep_desc.description()); + const auto& content1 = jsep_desc.description()->contents()[0]; + EXPECT_EQ("example.local:9", + content1.media_description()->connection_address().ToString()); + const auto& content2 = jsep_desc.description()->contents()[1]; + EXPECT_EQ("example.local:9", + content2.media_description()->connection_address().ToString()); +} + +// Test that the invalid or unsupported connection data cannot be parsed. TEST_F(WebRtcSdpTest, ParseConnectionDataFailure) { JsepSessionDescription jsep_desc(kDummyType); std::string sdp = kSdpString; @@ -4038,6 +4059,31 @@ TEST_F(WebRtcSdpTest, SerializeAndDeserializeWithConnectionAddress) { video_desc->connection_address().ToString()); } +// Test that a media description that contains a hostname connection address can +// be correctly serialized. +TEST_F(WebRtcSdpTest, SerializeAndDeserializeWithHostnameConnectionAddress) { + JsepSessionDescription expected_jsep(kDummyType); + cricket::Candidate c; + const rtc::SocketAddress hostname_addr("example.local", 1234); + audio_desc_->set_connection_address(hostname_addr); + video_desc_->set_connection_address(hostname_addr); + ASSERT_TRUE( + expected_jsep.Initialize(desc_.Copy(), kSessionId, kSessionVersion)); + // Serialization. + std::string message = webrtc::SdpSerialize(expected_jsep); + // Deserialization. + JsepSessionDescription jdesc(kDummyType); + ASSERT_TRUE(SdpDeserialize(message, &jdesc)); + auto audio_desc = jdesc.description() + ->GetContentByName(kAudioContentName) + ->media_description(); + auto video_desc = jdesc.description() + ->GetContentByName(kVideoContentName) + ->media_description(); + EXPECT_EQ(hostname_addr, audio_desc->connection_address()); + EXPECT_EQ(hostname_addr, video_desc->connection_address()); +} + // RFC4566 says "If a session has no meaningful name, the value "s= " SHOULD be // used (i.e., a single space as the session name)." So we should accept that. TEST_F(WebRtcSdpTest, DeserializeEmptySessionName) {