diff --git a/talk/app/webrtc/peerconnection.cc b/talk/app/webrtc/peerconnection.cc index 502451a17..3f674b8a7 100644 --- a/talk/app/webrtc/peerconnection.cc +++ b/talk/app/webrtc/peerconnection.cc @@ -266,13 +266,6 @@ bool ParseIceServers(const PeerConnectionInterface::IceServers& configuration, server.password, turn_transport_type, secure)); - // STUN functionality is part of TURN. - // Note: If there is only TURNS is supplied as part of configuration, - // we will have problem in fetching server reflexive candidate, as - // currently we don't have support of TCP/TLS in stunport.cc. - // In that case we should fetch server reflexive addess from - // TURN allocate response. - stun_config->push_back(StunConfiguration(address, port)); break; } case INVALID: diff --git a/talk/app/webrtc/peerconnectionfactory_unittest.cc b/talk/app/webrtc/peerconnectionfactory_unittest.cc index 4ab9e35c8..9f9d7881a 100644 --- a/talk/app/webrtc/peerconnectionfactory_unittest.cc +++ b/talk/app/webrtc/peerconnectionfactory_unittest.cc @@ -184,12 +184,6 @@ TEST_F(PeerConnectionFactoryTest, CreatePCUsingIceServers) { webrtc::PortAllocatorFactoryInterface::StunConfiguration stun1( "stun.l.google.com", 19302); stun_configs.push_back(stun1); - webrtc::PortAllocatorFactoryInterface::StunConfiguration stun2( - "test.com", 1234); - stun_configs.push_back(stun2); - webrtc::PortAllocatorFactoryInterface::StunConfiguration stun3( - "hello.com", kDefaultStunPort); - stun_configs.push_back(stun3); VerifyStunConfigurations(stun_configs); TurnConfigurations turn_configs; webrtc::PortAllocatorFactoryInterface::TurnConfiguration turn1( @@ -242,11 +236,6 @@ TEST_F(PeerConnectionFactoryTest, CreatePCUsingTurnUrlWithTransportParam) { "hello.com", kDefaultStunPort, "test", kTurnPassword, "tcp", false); turn_configs.push_back(turn); VerifyTurnConfigurations(turn_configs); - StunConfigurations stun_configs; - webrtc::PortAllocatorFactoryInterface::StunConfiguration stun( - "hello.com", kDefaultStunPort); - stun_configs.push_back(stun); - VerifyStunConfigurations(stun_configs); } TEST_F(PeerConnectionFactoryTest, CreatePCUsingSecureTurnUrl) { @@ -317,9 +306,8 @@ TEST_F(PeerConnectionFactoryTest, CreatePCUsingIPLiteralAddress) { webrtc::PortAllocatorFactoryInterface::StunConfiguration stun4( "2401:fa00:4::", 3478); stun_configs.push_back(stun4); // Default port - // Turn Address has the same host information as |stun3|. - stun_configs.push_back(stun3); VerifyStunConfigurations(stun_configs); + TurnConfigurations turn_configs; webrtc::PortAllocatorFactoryInterface::TurnConfiguration turn1( "2401:fa00:4::", 1234, "test", kTurnPassword, "udp", false); diff --git a/talk/app/webrtc/peerconnectioninterface_unittest.cc b/talk/app/webrtc/peerconnectioninterface_unittest.cc index a69bfa0da..a605b0d30 100644 --- a/talk/app/webrtc/peerconnectioninterface_unittest.cc +++ b/talk/app/webrtc/peerconnectioninterface_unittest.cc @@ -301,7 +301,7 @@ class PeerConnectionInterfaceTest : public testing::Test { EXPECT_EQ(0u, port_allocator_factory_->turn_configs().size()); CreatePeerConnection(kTurnIceServerUri, kTurnPassword, NULL); - EXPECT_EQ(1u, port_allocator_factory_->stun_configs().size()); + EXPECT_EQ(0u, port_allocator_factory_->stun_configs().size()); EXPECT_EQ(1u, port_allocator_factory_->turn_configs().size()); EXPECT_EQ(kTurnUsername, port_allocator_factory_->turn_configs()[0].username); @@ -309,8 +309,6 @@ class PeerConnectionInterfaceTest : public testing::Test { port_allocator_factory_->turn_configs()[0].password); EXPECT_EQ(kTurnHostname, port_allocator_factory_->turn_configs()[0].server.hostname()); - EXPECT_EQ(kTurnHostname, - port_allocator_factory_->stun_configs()[0].server.hostname()); } void ReleasePeerConnection() { diff --git a/talk/media/webrtc/fakewebrtcvideoengine.h b/talk/media/webrtc/fakewebrtcvideoengine.h index 5e0f0ffe1..145540636 100644 --- a/talk/media/webrtc/fakewebrtcvideoengine.h +++ b/talk/media/webrtc/fakewebrtcvideoengine.h @@ -44,7 +44,9 @@ #if !defined(USE_WEBRTC_DEV_BRANCH) namespace webrtc { -bool operator==(const webrtc::VideoCodec& c1, const webrtc::VideoCodec& c2) { +// This function is 'inline' to avoid link errors. +inline bool operator==(const webrtc::VideoCodec& c1, + const webrtc::VideoCodec& c2) { return memcmp(&c1, &c2, sizeof(c1)) == 0; } @@ -300,6 +302,7 @@ class FakeWebRtcVideoEngine send_nack_bitrate_(0), send_bandwidth_(0), receive_bandwidth_(0), + reserved_transmit_bitrate_bps_(0), suspend_below_min_bitrate_(false), overuse_observer_(NULL) { ssrcs_[0] = 0; // default ssrc. @@ -343,6 +346,7 @@ class FakeWebRtcVideoEngine unsigned int send_nack_bitrate_; unsigned int send_bandwidth_; unsigned int receive_bandwidth_; + unsigned int reserved_transmit_bitrate_bps_; bool suspend_below_min_bitrate_; webrtc::CpuOveruseObserver* overuse_observer_; #ifdef USE_WEBRTC_DEV_BRANCH @@ -635,6 +639,10 @@ class FakeWebRtcVideoEngine WEBRTC_ASSERT_CHANNEL(channel); return channels_.find(channel)->second->suspend_below_min_bitrate_; } + unsigned int GetReservedTransmitBitrate(int channel) { + WEBRTC_ASSERT_CHANNEL(channel); + return channels_.find(channel)->second->reserved_transmit_bitrate_bps_; + } WEBRTC_STUB(Release, ()); @@ -883,6 +891,10 @@ class FakeWebRtcVideoEngine // Not using WEBRTC_STUB due to bool return value virtual bool IsIPv6Enabled(int channel) { return true; } WEBRTC_STUB(SetMTU, (int, unsigned int)); +#ifdef USE_WEBRTC_DEV_BRANCH + WEBRTC_STUB(ReceivedBWEPacket, (const int, int64_t, int, + const webrtc::RTPHeader&)); +#endif // webrtc::ViERender WEBRTC_STUB(RegisterVideoRenderModule, (webrtc::VideoRender&)); @@ -1092,6 +1104,15 @@ class FakeWebRtcVideoEngine channels_[channel]->transmission_smoothing_ = enable; return 0; } +#ifdef USE_WEBRTC_DEV_BRANCH + WEBRTC_FUNC(SetReservedTransmitBitrate, (int channel, + unsigned int reserved_transmit_bitrate_bps)) { + WEBRTC_CHECK_CHANNEL(channel); + channels_[channel]->reserved_transmit_bitrate_bps_ = + reserved_transmit_bitrate_bps; + return 0; + } +#endif #ifdef USE_WEBRTC_DEV_BRANCH WEBRTC_STUB_CONST(GetRtcpPacketTypeCounters, (int, webrtc::RtcpPacketTypeCounter*, webrtc::RtcpPacketTypeCounter*)); diff --git a/talk/media/webrtc/fakewebrtcvoiceengine.h b/talk/media/webrtc/fakewebrtcvoiceengine.h index b552b49aa..bbb03917d 100644 --- a/talk/media/webrtc/fakewebrtcvoiceengine.h +++ b/talk/media/webrtc/fakewebrtcvoiceengine.h @@ -43,6 +43,10 @@ #include "webrtc/modules/audio_coding/main/interface/audio_coding_module.h" #include "webrtc/common.h" +namespace webrtc { +class ViENetwork; +} + namespace cricket { // Function returning stats will return these values @@ -106,6 +110,8 @@ class FakeWebRtcVoiceEngine dtmf_type(106), fec_type(117), nack_max_packets(0), + vie_network(NULL), + video_channel(-1), send_ssrc(0), send_audio_level_ext_(-1), send_absolute_sender_time_ext_(-1), @@ -133,6 +139,8 @@ class FakeWebRtcVoiceEngine int dtmf_type; int fec_type; int nack_max_packets; + webrtc::ViENetwork* vie_network; + int video_channel; uint32 send_ssrc; int send_audio_level_ext_; int send_absolute_sender_time_ext_; @@ -140,6 +148,7 @@ class FakeWebRtcVoiceEngine DtmfInfo dtmf_info; std::vector recv_codecs; webrtc::CodecInst send_codec; + webrtc::PacketTime last_rtp_packet_time; std::list packets; bool using_experimental_acm; }; @@ -218,6 +227,18 @@ class FakeWebRtcVoiceEngine int GetNACKMaxPackets(int channel) { return channels_[channel]->nack_max_packets; } + webrtc::ViENetwork* GetViENetwork(int channel) { + WEBRTC_ASSERT_CHANNEL(channel); + return channels_[channel]->vie_network; + } + int GetVideoChannel(int channel) { + WEBRTC_ASSERT_CHANNEL(channel); + return channels_[channel]->video_channel; + } + const webrtc::PacketTime& GetLastRtpPacketTime(int channel) { + WEBRTC_ASSERT_CHANNEL(channel); + return channels_[channel]->last_rtp_packet_time; + } bool IsUsingExperimentalAcm(int channel) { WEBRTC_ASSERT_CHANNEL(channel); return channels_[channel]->using_experimental_acm; @@ -689,6 +710,19 @@ class FakeWebRtcVoiceEngine std::string(static_cast(data), length)); return 0; } +#ifdef USE_WEBRTC_DEV_BRANCH + WEBRTC_FUNC(ReceivedRTPPacket, (int channel, const void* data, + unsigned int length, + const webrtc::PacketTime& packet_time)) { + WEBRTC_CHECK_CHANNEL(channel); + if (ReceivedRTPPacket(channel, data, length) == -1) { + return -1; + } + channels_[channel]->last_rtp_packet_time = packet_time; + return 0; + } +#endif + WEBRTC_STUB(ReceivedRTCPPacket, (int channel, const void* data, unsigned int length)); @@ -824,6 +858,16 @@ class FakeWebRtcVoiceEngine unsigned short payloadSize)); WEBRTC_STUB(GetLastRemoteTimeStamp, (int channel, uint32_t* lastRemoteTimeStamp)); +#ifdef USE_WEBRTC_DEV_BRANCH + WEBRTC_FUNC(SetVideoEngineBWETarget, (int channel, + webrtc::ViENetwork* vie_network, + int video_channel)) { + WEBRTC_CHECK_CHANNEL(channel); + channels_[channel]->vie_network = vie_network; + channels_[channel]->video_channel = video_channel; + return 0; + } +#endif // webrtc::VoEVideoSync WEBRTC_STUB(GetPlayoutBufferSize, (int& bufferMs)); diff --git a/talk/media/webrtc/webrtcvideoengine.cc b/talk/media/webrtc/webrtcvideoengine.cc index 9ed49df1a..42d416c26 100644 --- a/talk/media/webrtc/webrtcvideoengine.cc +++ b/talk/media/webrtc/webrtcvideoengine.cc @@ -238,8 +238,8 @@ struct FlushBlackFrameData : public talk_base::MessageData { class WebRtcRenderAdapter : public webrtc::ExternalRenderer { public: - explicit WebRtcRenderAdapter(VideoRenderer* renderer) - : renderer_(renderer), width_(0), height_(0) { + WebRtcRenderAdapter(VideoRenderer* renderer, int channel_id) + : renderer_(renderer), channel_id_(channel_id), width_(0), height_(0) { } virtual ~WebRtcRenderAdapter() { @@ -256,7 +256,8 @@ class WebRtcRenderAdapter : public webrtc::ExternalRenderer { if (width_ > 0 && height_ > 0 && renderer_ != NULL) { if (!renderer_->SetSize(width_, height_, 0)) { LOG(LS_ERROR) - << "WebRtcRenderAdapter SetRenderer failed to SetSize to: " + << "WebRtcRenderAdapter (channel " << channel_id_ + << ") SetRenderer failed to SetSize to: " << width_ << "x" << height_; } } @@ -268,10 +269,12 @@ class WebRtcRenderAdapter : public webrtc::ExternalRenderer { talk_base::CritScope cs(&crit_); width_ = width; height_ = height; - LOG(LS_INFO) << "WebRtcRenderAdapter frame size changed to: " + LOG(LS_INFO) << "WebRtcRenderAdapter (channel " << channel_id_ + << ") frame size changed to: " << width << "x" << height; if (renderer_ == NULL) { - LOG(LS_VERBOSE) << "WebRtcRenderAdapter the renderer has not been set. " + LOG(LS_VERBOSE) << "WebRtcRenderAdapter (channel " << channel_id_ + << ") the renderer has not been set. " << "SetSize will be called later in SetRenderer."; return 0; } @@ -313,7 +316,8 @@ class WebRtcRenderAdapter : public webrtc::ExternalRenderer { // Sanity check on decoded frame size. if (buffer_size != static_cast(VideoFrame::SizeOf(width_, height_))) { - LOG(LS_WARNING) << "WebRtcRenderAdapter received a strange frame size: " + LOG(LS_WARNING) << "WebRtcRenderAdapter (channel " << channel_id_ + << ") received a strange frame size: " << buffer_size; } @@ -351,6 +355,7 @@ class WebRtcRenderAdapter : public webrtc::ExternalRenderer { private: talk_base::CriticalSection crit_; VideoRenderer* renderer_; + int channel_id_; unsigned int width_; unsigned int height_; talk_base::RateTracker frame_rate_tracker_; @@ -539,7 +544,7 @@ class WebRtcVideoChannelRecvInfo { typedef std::map DecoderMap; // key: payload type explicit WebRtcVideoChannelRecvInfo(int channel_id) : channel_id_(channel_id), - render_adapter_(NULL), + render_adapter_(NULL, channel_id), decoder_observer_(channel_id) { } int channel_id() { return channel_id_; } @@ -3008,6 +3013,8 @@ bool WebRtcVideoMediaChannel::SetOptions(const VideoOptions &options) { adjusted_min_bitrate || start_bitrate_changed); + LOG(LS_INFO) << "Reset send codec needed is enabled? " + << reset_send_codec_needed; if (reset_send_codec_needed) { // On success, SetSendCodec() will reset send_max_bitrate_ to // expected_bitrate. @@ -3023,7 +3030,7 @@ bool WebRtcVideoMediaChannel::SetOptions(const VideoOptions &options) { if (leaky_bucket_changed) { bool enable_leaky_bucket = options_.video_leaky_bucket.GetWithDefaultIfUnset(false); - LOG(LS_INFO) << "Leaky bucket is enabled : " << enable_leaky_bucket; + LOG(LS_INFO) << "Leaky bucket is enabled? " << enable_leaky_bucket; for (SendChannelMap::iterator it = send_channels_.begin(); it != send_channels_.end(); ++it) { if (engine()->vie()->rtp()->SetTransmissionSmoothingStatus( @@ -3037,6 +3044,7 @@ bool WebRtcVideoMediaChannel::SetOptions(const VideoOptions &options) { int buffer_latency = options_.buffered_mode_latency.GetWithDefaultIfUnset( cricket::kBufferedModeDisabled); + LOG(LS_INFO) << "Buffer latency is " << buffer_latency; for (SendChannelMap::iterator it = send_channels_.begin(); it != send_channels_.end(); ++it) { if (engine()->vie()->rtp()->SetSenderBufferingMode( @@ -3057,6 +3065,8 @@ bool WebRtcVideoMediaChannel::SetOptions(const VideoOptions &options) { if (cpu_overuse_detection_changed) { bool cpu_overuse_detection = options_.cpu_overuse_detection.GetWithDefaultIfUnset(false); + LOG(LS_INFO) << "CPU overuse detection is enabled? " + << cpu_overuse_detection; for (SendChannelMap::iterator iter = send_channels_.begin(); iter != send_channels_.end(); ++iter) { WebRtcVideoChannelSendInfo* send_channel = iter->second; @@ -3067,12 +3077,14 @@ bool WebRtcVideoMediaChannel::SetOptions(const VideoOptions &options) { talk_base::DiffServCodePoint dscp = talk_base::DSCP_DEFAULT; if (options_.dscp.GetWithDefaultIfUnset(false)) dscp = kVideoDscpValue; + LOG(LS_INFO) << "DSCP is " << dscp; if (MediaChannel::SetDscp(dscp) != 0) { LOG(LS_WARNING) << "Failed to set DSCP settings for video channel"; } } if (suspend_below_min_bitrate_changed) { if (options_.suspend_below_min_bitrate.GetWithDefaultIfUnset(false)) { + LOG(LS_INFO) << "Suspend below min bitrate enabled."; for (SendChannelMap::iterator it = send_channels_.begin(); it != send_channels_.end(); ++it) { engine()->vie()->codec()->SuspendBelowMinBitrate( @@ -3084,6 +3096,7 @@ bool WebRtcVideoMediaChannel::SetOptions(const VideoOptions &options) { } #ifdef USE_WEBRTC_DEV_BRANCH if (improved_wifi_bwe_changed) { + LOG(LS_INFO) << "Improved WIFI BWE called."; webrtc::Config config; config.Set(new webrtc::AimdRemoteRateControl( options_.use_improved_wifi_bandwidth_estimator diff --git a/talk/media/webrtc/webrtcvoiceengine.cc b/talk/media/webrtc/webrtcvoiceengine.cc index 387dd9a4e..277a619c2 100644 --- a/talk/media/webrtc/webrtcvoiceengine.cc +++ b/talk/media/webrtc/webrtcvoiceengine.cc @@ -755,6 +755,7 @@ bool WebRtcVoiceEngine::ApplyOptions(const AudioOptions& options_in) { if (options.experimental_acm.Get(&enable_acm2)) { EnableExperimentalAcm(enable_acm2); } + LOG(LS_INFO) << "ACM2 enabled? " << enable_acm2; webrtc::VoEAudioProcessing* voep = voe_wrapper_->processing(); @@ -854,6 +855,7 @@ bool WebRtcVoiceEngine::ApplyOptions(const AudioOptions& options_in) { bool highpass_filter; if (options.highpass_filter.Get(&highpass_filter)) { + LOG(LS_INFO) << "High pass filter enabled? " << highpass_filter; if (voep->EnableHighPassFilter(highpass_filter) == -1) { LOG_RTCERR1(SetHighpassFilterStatus, highpass_filter); return false; @@ -862,6 +864,7 @@ bool WebRtcVoiceEngine::ApplyOptions(const AudioOptions& options_in) { bool stereo_swapping; if (options.stereo_swapping.Get(&stereo_swapping)) { + LOG(LS_INFO) << "Stereo swapping enabled? " << stereo_swapping; voep->EnableStereoChannelSwapping(stereo_swapping); if (voep->IsStereoChannelSwappingEnabled() != stereo_swapping) { LOG_RTCERR1(EnableStereoChannelSwapping, stereo_swapping); @@ -871,6 +874,7 @@ bool WebRtcVoiceEngine::ApplyOptions(const AudioOptions& options_in) { bool typing_detection; if (options.typing_detection.Get(&typing_detection)) { + LOG(LS_INFO) << "Typing detection is enabled? " << typing_detection; if (voep->SetTypingDetectionStatus(typing_detection) == -1) { // In case of error, log the info and continue LOG_RTCERR1(SetTypingDetectionStatus, typing_detection); @@ -879,6 +883,7 @@ bool WebRtcVoiceEngine::ApplyOptions(const AudioOptions& options_in) { int adjust_agc_delta; if (options.adjust_agc_delta.Get(&adjust_agc_delta)) { + LOG(LS_INFO) << "Adjust agc delta is " << adjust_agc_delta; if (!AdjustAgcLevel(adjust_agc_delta)) { return false; } @@ -886,6 +891,7 @@ bool WebRtcVoiceEngine::ApplyOptions(const AudioOptions& options_in) { bool aec_dump; if (options.aec_dump.Get(&aec_dump)) { + LOG(LS_INFO) << "Aec dump is enabled? " << aec_dump; if (aec_dump) StartAecDump(kAecDumpByAudioOptionFilename); else @@ -894,6 +900,7 @@ bool WebRtcVoiceEngine::ApplyOptions(const AudioOptions& options_in) { bool experimental_aec; if (options.experimental_aec.Get(&experimental_aec)) { + LOG(LS_INFO) << "Experimental aec is " << experimental_aec; webrtc::AudioProcessing* audioproc = voe_wrapper_->base()->audio_processing(); // We check audioproc for the benefit of tests, since FakeWebRtcVoiceEngine @@ -908,6 +915,7 @@ bool WebRtcVoiceEngine::ApplyOptions(const AudioOptions& options_in) { uint32 recording_sample_rate; if (options.recording_sample_rate.Get(&recording_sample_rate)) { + LOG(LS_INFO) << "Recording sample rate is " << recording_sample_rate; if (voe_wrapper_->hw()->SetRecordingSampleRate(recording_sample_rate)) { LOG_RTCERR1(SetRecordingSampleRate, recording_sample_rate); } @@ -915,6 +923,7 @@ bool WebRtcVoiceEngine::ApplyOptions(const AudioOptions& options_in) { uint32 playout_sample_rate; if (options.playout_sample_rate.Get(&playout_sample_rate)) { + LOG(LS_INFO) << "Playout sample rate is " << playout_sample_rate; if (voe_wrapper_->hw()->SetPlayoutSampleRate(playout_sample_rate)) { LOG_RTCERR1(SetPlayoutSampleRate, playout_sample_rate); } diff --git a/talk/p2p/base/port.cc b/talk/p2p/base/port.cc index 38157f4c4..ad692cec1 100644 --- a/talk/p2p/base/port.cc +++ b/talk/p2p/base/port.cc @@ -248,6 +248,7 @@ Connection* Port::GetConnection(const talk_base::SocketAddress& remote_addr) { void Port::AddAddress(const talk_base::SocketAddress& address, const talk_base::SocketAddress& base_address, + const talk_base::SocketAddress& related_address, const std::string& protocol, const std::string& type, uint32 type_preference, @@ -263,7 +264,7 @@ void Port::AddAddress(const talk_base::SocketAddress& address, c.set_password(password_); c.set_network_name(network_->name()); c.set_generation(generation_); - c.set_related_address(related_address_); + c.set_related_address(related_address); c.set_foundation(ComputeFoundation(type, protocol, base_address)); candidates_.push_back(c); SignalCandidateReady(this, c); diff --git a/talk/p2p/base/port.h b/talk/p2p/base/port.h index ff68dd031..6e5c383b7 100644 --- a/talk/p2p/base/port.h +++ b/talk/p2p/base/port.h @@ -172,13 +172,6 @@ class Port : public PortInterface, public talk_base::MessageHandler, send_retransmit_count_attribute_ = enable; } - const talk_base::SocketAddress& related_address() const { - return related_address_; - } - void set_related_address(const talk_base::SocketAddress& address) { - related_address_ = address; - } - // Identifies the generation that this port was created in. uint32 generation() { return generation_; } void set_generation(uint32 generation) { generation_ = generation; } @@ -315,6 +308,7 @@ class Port : public PortInterface, public talk_base::MessageHandler, // Fills in the local address of the port. void AddAddress(const talk_base::SocketAddress& address, const talk_base::SocketAddress& base_address, + const talk_base::SocketAddress& related_address, const std::string& protocol, const std::string& type, uint32 type_preference, bool final); @@ -365,7 +359,6 @@ class Port : public PortInterface, public talk_base::MessageHandler, std::string content_name_; int component_; uint32 generation_; - talk_base::SocketAddress related_address_; // In order to establish a connection to this Port (so that real data can be // sent through), the other side must send us a STUN binding request that is // authenticated with this username_fragment and password. diff --git a/talk/p2p/base/port_unittest.cc b/talk/p2p/base/port_unittest.cc index a6365d54e..61a2f4e55 100644 --- a/talk/p2p/base/port_unittest.cc +++ b/talk/p2p/base/port_unittest.cc @@ -146,19 +146,21 @@ class TestPort : public Port { virtual void PrepareAddress() { talk_base::SocketAddress addr(ip(), min_port()); - AddAddress(addr, addr, "udp", Type(), ICE_TYPE_PREFERENCE_HOST, true); + AddAddress(addr, addr, talk_base::SocketAddress(), "udp", Type(), + ICE_TYPE_PREFERENCE_HOST, true); } // Exposed for testing candidate building. void AddCandidateAddress(const talk_base::SocketAddress& addr) { - AddAddress(addr, addr, "udp", Type(), type_preference_, false); + AddAddress(addr, addr, talk_base::SocketAddress(), "udp", Type(), + type_preference_, false); } void AddCandidateAddress(const talk_base::SocketAddress& addr, const talk_base::SocketAddress& base_address, const std::string& type, int type_preference, bool final) { - AddAddress(addr, base_address, "udp", type, + AddAddress(addr, base_address, talk_base::SocketAddress(), "udp", type, type_preference, final); } @@ -2169,12 +2171,16 @@ TEST_F(PortTest, TestCandidateFoundation) { talk_base::scoped_ptr turnport(CreateTurnPort( kLocalAddr1, nat_socket_factory1(), PROTO_UDP, PROTO_UDP)); turnport->PrepareAddress(); - ASSERT_EQ_WAIT(1U, turnport->Candidates().size(), kTimeout); + ASSERT_EQ_WAIT(2U, turnport->Candidates().size(), kTimeout); + EXPECT_NE(turnport->Candidates()[0].foundation(), + turnport->Candidates()[1].foundation()); EXPECT_NE(udpport1->Candidates()[0].foundation(), - turnport->Candidates()[0].foundation()); + turnport->Candidates()[1].foundation()); EXPECT_NE(udpport2->Candidates()[0].foundation(), - turnport->Candidates()[0].foundation()); + turnport->Candidates()[1].foundation()); EXPECT_NE(stunport->Candidates()[0].foundation(), + turnport->Candidates()[1].foundation()); + EXPECT_EQ(stunport->Candidates()[0].foundation(), turnport->Candidates()[0].foundation()); } @@ -2217,11 +2223,13 @@ TEST_F(PortTest, TestCandidateRelatedAddress) { talk_base::scoped_ptr turnport(CreateTurnPort( kLocalAddr1, nat_socket_factory1(), PROTO_UDP, PROTO_UDP)); turnport->PrepareAddress(); - ASSERT_EQ_WAIT(1U, turnport->Candidates().size(), kTimeout); + ASSERT_EQ_WAIT(2U, turnport->Candidates().size(), kTimeout); EXPECT_EQ(kTurnUdpExtAddr.ipaddr(), - turnport->Candidates()[0].address().ipaddr()); - EXPECT_EQ(kNatAddr1.ipaddr(), + turnport->Candidates()[1].address().ipaddr()); + EXPECT_EQ(kLocalAddr1.ipaddr(), turnport->Candidates()[0].related_address().ipaddr()); + EXPECT_EQ(kNatAddr1.ipaddr(), + turnport->Candidates()[1].related_address().ipaddr()); } // Test priority value overflow handling when preference is set to 3. diff --git a/talk/p2p/base/relayport.cc b/talk/p2p/base/relayport.cc index af768e4f6..23571ea5f 100644 --- a/talk/p2p/base/relayport.cc +++ b/talk/p2p/base/relayport.cc @@ -240,8 +240,11 @@ void RelayPort::SetReady() { for (iter = external_addr_.begin(); iter != external_addr_.end(); ++iter) { std::string proto_name = ProtoToString(iter->proto); - AddAddress(iter->address, iter->address, proto_name, - RELAY_PORT_TYPE, ICE_TYPE_PREFERENCE_RELAY, false); + // In case of Gturn, related address is set to null socket address. + // This is due to as mapped address stun attribute is used for allocated + // address. + AddAddress(iter->address, iter->address, talk_base::SocketAddress(), + proto_name, RELAY_PORT_TYPE, ICE_TYPE_PREFERENCE_RELAY, false); } ready_ = true; SignalPortComplete(this); @@ -548,10 +551,6 @@ void RelayEntry::OnConnect(const talk_base::SocketAddress& mapped_addr, << " @ " << mapped_addr.ToSensitiveString(); connected_ = true; - // In case of Gturn related address is set to null socket address. - // This is due to mapped address stun attribute is used for allocated - // address. - port_->set_related_address(talk_base::SocketAddress()); port_->AddExternalAddress(ProtocolAddress(mapped_addr, proto)); port_->SetReady(); } diff --git a/talk/p2p/base/stunport.cc b/talk/p2p/base/stunport.cc index 95b26ac11..e9a2bb980 100644 --- a/talk/p2p/base/stunport.cc +++ b/talk/p2p/base/stunport.cc @@ -173,7 +173,7 @@ bool UDPPort::Init() { UDPPort::~UDPPort() { if (resolver_) { - resolver_->Destroy(false); + resolver_->Destroy(true); } if (!SharedSocket()) delete socket_; @@ -243,7 +243,8 @@ int UDPPort::GetError() { void UDPPort::OnLocalAddressReady(talk_base::AsyncPacketSocket* socket, const talk_base::SocketAddress& address) { - AddAddress(address, address, UDP_PROTOCOL_NAME, LOCAL_PORT_TYPE, + AddAddress(address, address, talk_base::SocketAddress(), + UDP_PROTOCOL_NAME, LOCAL_PORT_TYPE, ICE_TYPE_PREFERENCE_HOST, false); MaybePrepareStunCandidate(); } @@ -324,10 +325,9 @@ void UDPPort::OnStunBindingRequestSucceeded( if (!SharedSocket() || stun_addr != socket_->GetLocalAddress()) { // If socket is shared and |stun_addr| is equal to local socket // address then discarding the stun address. - // Setting related address before STUN candidate is added. For STUN - // related address is local socket address. - set_related_address(socket_->GetLocalAddress()); - AddAddress(stun_addr, socket_->GetLocalAddress(), UDP_PROTOCOL_NAME, + // For STUN related address is local socket address. + AddAddress(stun_addr, socket_->GetLocalAddress(), + socket_->GetLocalAddress(), UDP_PROTOCOL_NAME, STUN_PORT_TYPE, ICE_TYPE_PREFERENCE_SRFLX, false); } SetResult(true); diff --git a/talk/p2p/base/stunport_unittest.cc b/talk/p2p/base/stunport_unittest.cc index 2a98a9fdb..5850027ec 100644 --- a/talk/p2p/base/stunport_unittest.cc +++ b/talk/p2p/base/stunport_unittest.cc @@ -43,6 +43,8 @@ static const SocketAddress kBadAddr("0.0.0.1", 5000); static const SocketAddress kStunHostnameAddr("localhost", 5000); static const SocketAddress kBadHostnameAddr("not-a-real-hostname", 5000); static const int kTimeoutMs = 10000; +// stun prio = 100 << 24 | 30 (IPV4) << 8 | 256 - 0 +static const uint32 kStunCandidatePriority = 1677729535; // Tests connecting a StunPort to a fake STUN server (cricket::StunServer) // TODO: Use a VirtualSocketServer here. We have to use a @@ -178,6 +180,7 @@ TEST_F(StunPortTest, TestPrepareAddressHostname) { EXPECT_TRUE_WAIT(done(), kTimeoutMs); ASSERT_EQ(1U, port()->Candidates().size()); EXPECT_TRUE(kLocalAddr.EqualIPs(port()->Candidates()[0].address())); + EXPECT_EQ(kStunCandidatePriority, port()->Candidates()[0].priority()); } // Test that we handle hostname lookup failures properly. diff --git a/talk/p2p/base/tcpport.cc b/talk/p2p/base/tcpport.cc index d83623f7f..f74ad8b82 100644 --- a/talk/p2p/base/tcpport.cc +++ b/talk/p2p/base/tcpport.cc @@ -121,6 +121,7 @@ void TCPPort::PrepareAddress() { if (socket_->GetState() == talk_base::AsyncPacketSocket::STATE_BOUND || socket_->GetState() == talk_base::AsyncPacketSocket::STATE_CLOSED) AddAddress(socket_->GetLocalAddress(), socket_->GetLocalAddress(), + talk_base::SocketAddress(), TCP_PROTOCOL_NAME, LOCAL_PORT_TYPE, ICE_TYPE_PREFERENCE_HOST_TCP, true); } else { @@ -128,8 +129,9 @@ void TCPPort::PrepareAddress() { // Note: We still add the address, since otherwise the remote side won't // recognize our incoming TCP connections. AddAddress(talk_base::SocketAddress(ip(), 0), - talk_base::SocketAddress(ip(), 0), TCP_PROTOCOL_NAME, - LOCAL_PORT_TYPE, ICE_TYPE_PREFERENCE_HOST_TCP, true); + talk_base::SocketAddress(ip(), 0), talk_base::SocketAddress(), + TCP_PROTOCOL_NAME, LOCAL_PORT_TYPE, ICE_TYPE_PREFERENCE_HOST_TCP, + true); } } @@ -221,7 +223,7 @@ void TCPPort::OnReadyToSend(talk_base::AsyncPacketSocket* socket) { void TCPPort::OnAddressReady(talk_base::AsyncPacketSocket* socket, const talk_base::SocketAddress& address) { - AddAddress(address, address, "tcp", + AddAddress(address, address, talk_base::SocketAddress(), "tcp", LOCAL_PORT_TYPE, ICE_TYPE_PREFERENCE_HOST_TCP, true); } diff --git a/talk/p2p/base/turnport.cc b/talk/p2p/base/turnport.cc index eeaa3af60..b9eba7588 100644 --- a/talk/p2p/base/turnport.cc +++ b/talk/p2p/base/turnport.cc @@ -50,6 +50,7 @@ static const int TURN_CHANNEL_NUMBER_START = 0x4000; static const int TURN_PERMISSION_TIMEOUT = 5 * 60 * 1000; // 5 minutes static const size_t TURN_CHANNEL_HEADER_SIZE = 4U; +static const size_t MAX_CANDIDATES_PER_TURNPORT = 2; // A STUN + TURN inline bool IsTurnChannelData(uint16 msg_type) { return ((msg_type & 0xC000) == 0x4000); // MSB are 0b01 @@ -168,6 +169,27 @@ class TurnEntry : public sigslot::has_slots<> { BindState state_; }; +TurnPort::TurnPort(talk_base::Thread* thread, + talk_base::PacketSocketFactory* factory, + talk_base::Network* network, + talk_base::AsyncPacketSocket* socket, + const std::string& username, + const std::string& password, + const ProtocolAddress& server_address, + const RelayCredentials& credentials) + : Port(thread, factory, network, socket->GetLocalAddress().ipaddr(), + username, password), + server_address_(server_address), + credentials_(credentials), + socket_(socket), + resolver_(NULL), + error_(0), + request_manager_(thread), + next_channel_number_(TURN_CHANNEL_NUMBER_START), + connected_(false) { + request_manager_.SignalSendPacket.connect(this, &TurnPort::OnSendStunPacket); +} + TurnPort::TurnPort(talk_base::Thread* thread, talk_base::PacketSocketFactory* factory, talk_base::Network* network, @@ -181,6 +203,7 @@ TurnPort::TurnPort(talk_base::Thread* thread, username, password), server_address_(server_address), credentials_(credentials), + socket_(NULL), resolver_(NULL), error_(0), request_manager_(thread), @@ -197,6 +220,9 @@ TurnPort::~TurnPort() { if (resolver_) { resolver_->Destroy(false); } + if (!SharedSocket()) { + delete socket_; + } } void TurnPort::PrepareAddress() { @@ -227,19 +253,18 @@ void TurnPort::PrepareAddress() { LOG_J(LS_INFO, this) << "Trying to connect to TURN server via " << ProtoToString(server_address_.proto) << " @ " << server_address_.address.ToSensitiveString(); - if (server_address_.proto == PROTO_UDP) { - socket_.reset(socket_factory()->CreateUdpSocket( - talk_base::SocketAddress(ip(), 0), min_port(), max_port())); + if (server_address_.proto == PROTO_UDP && !SharedSocket()) { + socket_ = socket_factory()->CreateUdpSocket( + talk_base::SocketAddress(ip(), 0), min_port(), max_port()); } else if (server_address_.proto == PROTO_TCP) { int opts = talk_base::PacketSocketFactory::OPT_STUN; // If secure bit is enabled in server address, use TLS over TCP. if (server_address_.secure) { opts |= talk_base::PacketSocketFactory::OPT_TLS; } - - socket_.reset(socket_factory()->CreateClientTcpSocket( + socket_ = socket_factory()->CreateClientTcpSocket( talk_base::SocketAddress(ip(), 0), server_address_.address, - proxy(), user_agent(), opts)); + proxy(), user_agent(), opts); } if (!socket_) { @@ -253,7 +278,11 @@ void TurnPort::PrepareAddress() { socket_->SetOption(iter->first, iter->second); } - socket_->SignalReadPacket.connect(this, &TurnPort::OnReadPacket); + if (!SharedSocket()) { + // If socket is shared, AllocationSequence will receive the packet. + socket_->SignalReadPacket.connect(this, &TurnPort::OnReadPacket); + } + socket_->SignalReadyToSend.connect(this, &TurnPort::OnReadyToSend); if (server_address_.proto == PROTO_TCP) { @@ -294,12 +323,18 @@ Connection* TurnPort::CreateConnection(const Candidate& address, // Create an entry, if needed, so we can get our permissions set up correctly. CreateEntry(address.address()); - // TODO(juberti): The '0' index will need to change if we start gathering STUN - // candidates on this port. - ProxyConnection* conn = new ProxyConnection(this, 0, address); - conn->SignalDestroyed.connect(this, &TurnPort::OnConnectionDestroyed); - AddConnection(conn); - return conn; + // A TURN port will have two candiates, STUN and TURN. STUN may not + // present in all cases. If present stun candidate will be added first + // and TURN candidate later. + for (size_t index = 0; index < Candidates().size(); ++index) { + if (Candidates()[index].type() == RELAY_PORT_TYPE) { + ProxyConnection* conn = new ProxyConnection(this, index, address); + conn->SignalDestroyed.connect(this, &TurnPort::OnConnectionDestroyed); + AddConnection(conn); + return conn; + } + } + return NULL; } int TurnPort::SetOption(talk_base::Socket::Option opt, int value) { @@ -360,7 +395,7 @@ void TurnPort::OnReadPacket( talk_base::AsyncPacketSocket* socket, const char* data, size_t size, const talk_base::SocketAddress& remote_addr, const talk_base::PacketTime& packet_time) { - ASSERT(socket == socket_.get()); + ASSERT(socket == socket_); ASSERT(remote_addr == server_address_.address); // The message must be at least the size of a channel header. @@ -415,6 +450,8 @@ void TurnPort::OnResolveResult(talk_base::AsyncResolverInterface* resolver) { return; } + SignalResolvedServerAddress(this, server_address_.address, + resolver_->address()); PrepareAddress(); } @@ -428,15 +465,25 @@ void TurnPort::OnSendStunPacket(const void* data, size_t size, } void TurnPort::OnStunAddress(const talk_base::SocketAddress& address) { - // For relay, mapped address is rel-addr. - set_related_address(address); + if (server_address_.proto == PROTO_UDP && + address != socket_->GetLocalAddress()) { + AddAddress(address, + socket_->GetLocalAddress(), + socket_->GetLocalAddress(), + UDP_PROTOCOL_NAME, + STUN_PORT_TYPE, + ICE_TYPE_PREFERENCE_SRFLX, + false); + } } -void TurnPort::OnAllocateSuccess(const talk_base::SocketAddress& address) { +void TurnPort::OnAllocateSuccess(const talk_base::SocketAddress& address, + const talk_base::SocketAddress& stun_address) { connected_ = true; AddAddress(address, socket_->GetLocalAddress(), - "udp", + stun_address, + UDP_PROTOCOL_NAME, RELAY_PORT_TYPE, GetRelayPreference(server_address_.proto, server_address_.secure), true); @@ -684,7 +731,7 @@ void TurnAllocateRequest::OnResponse(StunMessage* response) { return; } - // TODO(mallinath) - Use mapped address for STUN candidate. + // Using XOR-Mapped-Address for stun. port_->OnStunAddress(mapped_attr->GetAddress()); const StunAddressAttribute* relayed_attr = @@ -703,7 +750,8 @@ void TurnAllocateRequest::OnResponse(StunMessage* response) { return; } // Notify the port the allocate succeeded, and schedule a refresh request. - port_->OnAllocateSuccess(relayed_attr->GetAddress()); + port_->OnAllocateSuccess(relayed_attr->GetAddress(), + mapped_attr->GetAddress()); port_->ScheduleRefresh(lifetime_attr->value()); } diff --git a/talk/p2p/base/turnport.h b/talk/p2p/base/turnport.h index efec18bef..4745b33ee 100644 --- a/talk/p2p/base/turnport.h +++ b/talk/p2p/base/turnport.h @@ -49,6 +49,18 @@ class TurnEntry; class TurnPort : public Port { public: + static TurnPort* Create(talk_base::Thread* thread, + talk_base::PacketSocketFactory* factory, + talk_base::Network* network, + talk_base::AsyncPacketSocket* socket, + const std::string& username, // ice username. + const std::string& password, // ice password. + const ProtocolAddress& server_address, + const RelayCredentials& credentials) { + return new TurnPort(thread, factory, network, socket, username, password, + server_address, credentials); + } + static TurnPort* Create(talk_base::Thread* thread, talk_base::PacketSocketFactory* factory, talk_base::Network* network, @@ -79,10 +91,19 @@ class TurnPort : public Port { virtual int SetOption(talk_base::Socket::Option opt, int value); virtual int GetOption(talk_base::Socket::Option opt, int* value); virtual int GetError(); - virtual void OnReadPacket( + + virtual bool HandleIncomingPacket( talk_base::AsyncPacketSocket* socket, const char* data, size_t size, const talk_base::SocketAddress& remote_addr, - const talk_base::PacketTime& packet_time); + const talk_base::PacketTime& packet_time) { + OnReadPacket(socket, data, size, remote_addr, packet_time); + return true; + } + virtual void OnReadPacket(talk_base::AsyncPacketSocket* socket, + const char* data, size_t size, + const talk_base::SocketAddress& remote_addr, + const talk_base::PacketTime& packet_time); + virtual void OnReadyToSend(talk_base::AsyncPacketSocket* socket); void OnSocketConnect(talk_base::AsyncPacketSocket* socket); @@ -92,11 +113,27 @@ class TurnPort : public Port { const std::string& hash() const { return hash_; } const std::string& nonce() const { return nonce_; } + // Signal with resolved server address. + // Parameters are port, server address and resolved server address. + // This signal will be sent only if server address is resolved successfully. + sigslot::signal3 SignalResolvedServerAddress; + // This signal is only for testing purpose. sigslot::signal3 SignalCreatePermissionResult; protected: + TurnPort(talk_base::Thread* thread, + talk_base::PacketSocketFactory* factory, + talk_base::Network* network, + talk_base::AsyncPacketSocket* socket, + const std::string& username, + const std::string& password, + const ProtocolAddress& server_address, + const RelayCredentials& credentials); + TurnPort(talk_base::Thread* thread, talk_base::PacketSocketFactory* factory, talk_base::Network* network, @@ -131,7 +168,8 @@ class TurnPort : public Port { // Stun address from allocate success response. // Currently used only for testing. void OnStunAddress(const talk_base::SocketAddress& address); - void OnAllocateSuccess(const talk_base::SocketAddress& address); + void OnAllocateSuccess(const talk_base::SocketAddress& address, + const talk_base::SocketAddress& stun_address); void OnAllocateError(); void OnAllocateRequestTimeout(); @@ -160,7 +198,7 @@ class TurnPort : public Port { ProtocolAddress server_address_; RelayCredentials credentials_; - talk_base::scoped_ptr socket_; + talk_base::AsyncPacketSocket* socket_; SocketOptionsMap socket_options_; talk_base::AsyncResolverInterface* resolver_; int error_; diff --git a/talk/p2p/base/turnport_unittest.cc b/talk/p2p/base/turnport_unittest.cc index 75ac6b5b9..79ae208f6 100644 --- a/talk/p2p/base/turnport_unittest.cc +++ b/talk/p2p/base/turnport_unittest.cc @@ -157,7 +157,13 @@ class TurnPortTest : public testing::Test, const talk_base::PacketTime& packet_time) { udp_packets_.push_back(talk_base::Buffer(data, size)); } - + void OnSocketReadPacket(talk_base::AsyncPacketSocket* socket, + const char* data, size_t size, + const talk_base::SocketAddress& remote_addr, + const talk_base::PacketTime& packet_time) { + turn_port_->HandleIncomingPacket(socket, data, size, remote_addr, + packet_time); + } talk_base::AsyncSocket* CreateServerSocket(const SocketAddress addr) { talk_base::AsyncSocket* socket = ss_->CreateAsyncSocket(SOCK_STREAM); EXPECT_GE(socket->Bind(addr), 0); @@ -185,6 +191,31 @@ class TurnPortTest : public testing::Test, // This TURN port will be the controlling. turn_port_->SetIceProtocolType(cricket::ICEPROTO_RFC5245); turn_port_->SetIceRole(cricket::ICEROLE_CONTROLLING); + ConnectSignals(); + } + + void CreateSharedTurnPort(const std::string& username, + const std::string& password, + const cricket::ProtocolAddress& server_address) { + socket_.reset(socket_factory_.CreateUdpSocket( + talk_base::SocketAddress(kLocalAddr1.ipaddr(), 0), 0, 0)); + ASSERT_TRUE(socket_ != NULL); + socket_->SignalReadPacket.connect(this, &TurnPortTest::OnSocketReadPacket); + + cricket::RelayCredentials credentials(username, password); + turn_port_.reset(cricket::TurnPort::Create( + main_, &socket_factory_, &network_, socket_.get(), kIceUfrag1, kIcePwd1, + server_address, credentials)); + // Set ICE protocol type to ICEPROTO_RFC5245, as port by default will be + // in Hybrid mode. Protocol type is necessary to send correct type STUN ping + // messages. + // This TURN port will be the controlling. + turn_port_->SetIceProtocolType(cricket::ICEPROTO_RFC5245); + turn_port_->SetIceRole(cricket::ICEROLE_CONTROLLING); + ConnectSignals(); + } + + void ConnectSignals() { turn_port_->SignalPortComplete.connect(this, &TurnPortTest::OnTurnPortComplete); turn_port_->SignalPortError.connect(this, @@ -294,6 +325,7 @@ class TurnPortTest : public testing::Test, talk_base::SocketServerScope ss_scope_; talk_base::Network network_; talk_base::BasicPacketSocketFactory socket_factory_; + talk_base::scoped_ptr socket_; cricket::TestTurnServer turn_server_; talk_base::scoped_ptr turn_port_; talk_base::scoped_ptr udp_port_; @@ -349,6 +381,12 @@ TEST_F(TurnPortTest, TestTurnConnection) { TestTurnConnection(); } +// Similar to above, except that this test will use the shared socket. +TEST_F(TurnPortTest, TestTurnConnectionUsingSharedSocket) { + CreateSharedTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr); + TestTurnConnection(); +} + // Test that we can establish a TCP connection with TURN server. TEST_F(TurnPortTest, TestTurnTcpConnection) { turn_server_.AddInternalSocket(kTurnTcpIntAddr, cricket::PROTO_TCP); diff --git a/talk/p2p/client/basicportallocator.cc b/talk/p2p/client/basicportallocator.cc index 7c285d1c3..8338abed0 100644 --- a/talk/p2p/client/basicportallocator.cc +++ b/talk/p2p/client/basicportallocator.cc @@ -148,6 +148,9 @@ class AllocationSequence : public talk_base::MessageHandler, const talk_base::PacketTime& packet_time); void OnPortDestroyed(PortInterface* port); + void OnResolvedTurnServerAddress( + TurnPort* port, const talk_base::SocketAddress& server_address, + const talk_base::SocketAddress& resolved_server_address); BasicPortAllocatorSession* session_; talk_base::Network* network_; @@ -157,8 +160,10 @@ class AllocationSequence : public talk_base::MessageHandler, uint32 flags_; ProtocolList protocols_; talk_base::scoped_ptr udp_socket_; - // Keeping a list of all UDP based ports. - std::deque ports; + // There will be only one udp port per AllocationSequence. + Port* udp_port_; + // Keeping a map for turn ports keyed with server addresses. + std::map turn_ports_; int phase_; }; @@ -693,6 +698,7 @@ AllocationSequence::AllocationSequence(BasicPortAllocatorSession* session, state_(kInit), flags_(flags), udp_socket_(), + udp_port_(NULL), phase_(0) { } @@ -855,7 +861,6 @@ void AllocationSequence::CreateUDPPorts() { } if (port) { - ports.push_back(port); // If shared socket is enabled, STUN candidate will be allocated by the // UDPPort. if (IsFlagSet(PORTALLOCATOR_ENABLE_SHARED_SOCKET) && @@ -866,7 +871,13 @@ void AllocationSequence::CreateUDPPorts() { << "AllocationSequence: No STUN server configured, skipping."; return; } - port->set_server_addr(config_->stun_address); + udp_port_ = port; + // If there is a TURN UDP server available, then we will use TURN port + // to get stun address, otherwise by UDP port. + // Shared socket mode is not used in GTURN mode. + if (config_ && !config_->SupportsProtocol(RELAY_TURN, PROTO_UDP)) { + port->set_server_addr(config_->stun_address); + } } session_->AddAllocatedPort(port, this, true); @@ -992,15 +1003,40 @@ void AllocationSequence::CreateTurnPort(const RelayServerConfig& config) { PortList::const_iterator relay_port; for (relay_port = config.ports.begin(); relay_port != config.ports.end(); ++relay_port) { - TurnPort* port = TurnPort::Create(session_->network_thread(), - session_->socket_factory(), - network_, ip_, - session_->allocator()->min_port(), - session_->allocator()->max_port(), - session_->username(), - session_->password(), - *relay_port, config.credentials); + TurnPort* port = NULL; + if (IsFlagSet(PORTALLOCATOR_ENABLE_SHARED_SOCKET)) { + port = TurnPort::Create(session_->network_thread(), + session_->socket_factory(), + network_, udp_socket_.get(), + session_->username(), session_->password(), + *relay_port, config.credentials); + } else { + port = TurnPort::Create(session_->network_thread(), + session_->socket_factory(), + network_, ip_, + session_->allocator()->min_port(), + session_->allocator()->max_port(), + session_->username(), + session_->password(), + *relay_port, config.credentials); + } + if (port) { + // If we are using shared socket for TURN and udp ports, we need to + // find a way to demux the packets to the correct port when received. + // Mapping against server_address is one way of doing this. When packet + // is received the remote_address will be checked against the map. + // If server address is not resolved, a signal will be sent from the port + // after the address is resolved. The map entry will updated with the + // resolved address when the signal is received from the port. + if (IsFlagSet(PORTALLOCATOR_ENABLE_SHARED_SOCKET)) { + // If server address is not resolved then listen for signal from port. + if ((*relay_port).address.IsUnresolved()) { + port->SignalResolvedServerAddress.connect( + this, &AllocationSequence::OnResolvedTurnServerAddress); + } + turn_ports_[(*relay_port).address] = port; + } session_->AddAllocatedPort(port, this, true); } } @@ -1011,22 +1047,46 @@ void AllocationSequence::OnReadPacket( const talk_base::SocketAddress& remote_addr, const talk_base::PacketTime& packet_time) { ASSERT(socket == udp_socket_.get()); - for (std::deque::iterator iter = ports.begin(); - iter != ports.end(); ++iter) { - // We have only one port in the queue. - // TODO(mallinath) - Add shared socket support to Relay and Turn ports. - if ((*iter)->HandleIncomingPacket( - socket, data, size, remote_addr, packet_time)) { - break; + // If the packet is received from one of the TURN server in the config, then + // pass down the packet to that port, otherwise it will be handed down to + // the local udp port. + Port* port = NULL; + std::map::iterator iter = + turn_ports_.find(remote_addr); + if (iter != turn_ports_.end()) { + port = iter->second; + } else if (udp_port_) { + port = udp_port_; + } + ASSERT(port != NULL); + port->HandleIncomingPacket(socket, data, size, remote_addr, packet_time); +} + +void AllocationSequence::OnPortDestroyed(PortInterface* port) { + if (udp_port_ == port) { + udp_port_ = NULL; + } else { + std::map::iterator iter; + for (iter = turn_ports_.begin(); iter != turn_ports_.end(); ++iter) { + if (iter->second == port) { + turn_ports_.erase(iter); + break; + } } } } -void AllocationSequence::OnPortDestroyed(PortInterface* port) { - std::deque::iterator iter = - std::find(ports.begin(), ports.end(), port); - ASSERT(iter != ports.end()); - ports.erase(iter); +void AllocationSequence::OnResolvedTurnServerAddress( + TurnPort* port, const talk_base::SocketAddress& server_address, + const talk_base::SocketAddress& resolved_server_address) { + std::map::iterator iter; + iter = turn_ports_.find(server_address); + + ASSERT(iter != turn_ports_.end()); + ASSERT(iter->second != port); + // Remove old entry and then insert using the resolved address as key. + turn_ports_.erase(iter); + turn_ports_[resolved_server_address] = port; } // PortConfiguration @@ -1044,7 +1104,7 @@ void PortConfiguration::AddRelay(const RelayServerConfig& config) { } bool PortConfiguration::SupportsProtocol( - const RelayServerConfig& relay, ProtocolType type) { + const RelayServerConfig& relay, ProtocolType type) const { PortList::const_iterator relay_port; for (relay_port = relay.ports.begin(); relay_port != relay.ports.end(); @@ -1055,4 +1115,14 @@ bool PortConfiguration::SupportsProtocol( return false; } +bool PortConfiguration::SupportsProtocol(const RelayType turn_type, + ProtocolType type) const { + for (size_t i = 0; i < relays.size(); ++i) { + if (relays[i].type == turn_type && + SupportsProtocol(relays[i], type)) + return true; + } + return false; +} + } // namespace cricket diff --git a/talk/p2p/client/basicportallocator.h b/talk/p2p/client/basicportallocator.h index c46c29aaf..b8660f0a3 100644 --- a/talk/p2p/client/basicportallocator.h +++ b/talk/p2p/client/basicportallocator.h @@ -232,8 +232,9 @@ struct PortConfiguration : public talk_base::MessageData { void AddRelay(const RelayServerConfig& config); // Determines whether the given relay server supports the given protocol. - static bool SupportsProtocol(const RelayServerConfig& relay, - ProtocolType type); + bool SupportsProtocol(const RelayServerConfig& relay, + ProtocolType type) const; + bool SupportsProtocol(const RelayType turn_type, ProtocolType type) const; }; } // namespace cricket diff --git a/talk/p2p/client/connectivitychecker_unittest.cc b/talk/p2p/client/connectivitychecker_unittest.cc index 59fdfc2f7..c62120bee 100644 --- a/talk/p2p/client/connectivitychecker_unittest.cc +++ b/talk/p2p/client/connectivitychecker_unittest.cc @@ -73,7 +73,7 @@ class FakeStunPort : public StunPort { // Just set external address and signal that we are done. virtual void PrepareAddress() { - AddAddress(kExternalAddr, kExternalAddr, "udp", + AddAddress(kExternalAddr, kExternalAddr, talk_base::SocketAddress(), "udp", STUN_PORT_TYPE, ICE_TYPE_PREFERENCE_SRFLX, true); SignalPortComplete(this); } diff --git a/talk/p2p/client/portallocator_unittest.cc b/talk/p2p/client/portallocator_unittest.cc index 0ea8fb54a..211c3545f 100644 --- a/talk/p2p/client/portallocator_unittest.cc +++ b/talk/p2p/client/portallocator_unittest.cc @@ -44,6 +44,7 @@ #include "talk/p2p/base/portallocatorsessionproxy.h" #include "talk/p2p/base/testrelayserver.h" #include "talk/p2p/base/teststunserver.h" +#include "talk/p2p/base/testturnserver.h" #include "talk/p2p/client/basicportallocator.h" #include "talk/p2p/client/httpportallocator.h" @@ -55,6 +56,7 @@ static const SocketAddress kClientIPv6Addr( "2401:fa00:4:1000:be30:5bff:fee5:c3", 0); static const SocketAddress kClientAddr2("22.22.22.22", 0); static const SocketAddress kNatAddr("77.77.77.77", talk_base::NAT_SERVER_PORT); +static const SocketAddress kRemoteClientAddr("22.22.22.22", 0); static const SocketAddress kStunAddr("99.99.99.1", cricket::STUN_SERVER_PORT); static const SocketAddress kRelayUdpIntAddr("99.99.99.2", 5000); static const SocketAddress kRelayUdpExtAddr("99.99.99.3", 5001); @@ -62,6 +64,8 @@ static const SocketAddress kRelayTcpIntAddr("99.99.99.2", 5002); static const SocketAddress kRelayTcpExtAddr("99.99.99.3", 5003); static const SocketAddress kRelaySslTcpIntAddr("99.99.99.2", 5004); static const SocketAddress kRelaySslTcpExtAddr("99.99.99.3", 5005); +static const SocketAddress kTurnUdpIntAddr("99.99.99.4", 3478); +static const SocketAddress kTurnUdpExtAddr("99.99.99.5", 0); // Minimum and maximum port for port range tests. static const int kMinPort = 10000; @@ -75,6 +79,8 @@ static const char kIcePwd0[] = "TESTICEPWD00000000000000"; static const char kContentName[] = "test content"; static const int kDefaultAllocationTimeout = 1000; +static const char kTurnUsername[] = "test"; +static const char kTurnPassword[] = "test"; namespace cricket { @@ -107,6 +113,7 @@ class PortAllocatorTest : public testing::Test, public sigslot::has_slots<> { relay_server_(Thread::Current(), kRelayUdpIntAddr, kRelayUdpExtAddr, kRelayTcpIntAddr, kRelayTcpExtAddr, kRelaySslTcpIntAddr, kRelaySslTcpExtAddr), + turn_server_(Thread::Current(), kTurnUdpIntAddr, kTurnUdpExtAddr), allocator_(new cricket::BasicPortAllocator( &network_manager_, kStunAddr, kRelayUdpIntAddr, kRelayTcpIntAddr, kRelaySslTcpIntAddr)), @@ -245,6 +252,7 @@ class PortAllocatorTest : public testing::Test, public sigslot::has_slots<> { talk_base::BasicPacketSocketFactory nat_socket_factory_; cricket::TestStunServer stun_server_; cricket::TestRelayServer relay_server_; + cricket::TestTurnServer turn_server_; talk_base::FakeNetworkManager network_manager_; talk_base::scoped_ptr allocator_; talk_base::scoped_ptr session_; @@ -653,7 +661,7 @@ TEST_F(PortAllocatorTest, TestDisableSharedUfrag) { // is allocated for udp and stun. Also verify there is only one candidate // (local) if stun candidate is same as local candidate, which will be the case // in a public network like the below test. -TEST_F(PortAllocatorTest, TestEnableSharedSocketWithoutNat) { +TEST_F(PortAllocatorTest, TestSharedSocketWithoutNat) { AddInterface(kClientAddr); allocator_->set_flags(allocator().flags() | cricket::PORTALLOCATOR_ENABLE_SHARED_UFRAG | @@ -670,7 +678,7 @@ TEST_F(PortAllocatorTest, TestEnableSharedSocketWithoutNat) { // Test that when PORTALLOCATOR_ENABLE_SHARED_SOCKET is enabled only one port // is allocated for udp and stun. In this test we should expect both stun and // local candidates as client behind a nat. -TEST_F(PortAllocatorTest, TestEnableSharedSocketWithNat) { +TEST_F(PortAllocatorTest, TestSharedSocketWithNat) { AddInterface(kClientAddr); talk_base::scoped_ptr nat_server( CreateNatServer(kNatAddr, talk_base::NAT_OPEN_CONE)); @@ -693,10 +701,51 @@ TEST_F(PortAllocatorTest, TestEnableSharedSocketWithNat) { EXPECT_EQ(3U, candidates_.size()); } +// Test that when PORTALLOCATOR_ENABLE_SHARED_SOCKET is enabled only one port +// is allocated for udp/stun/turn. In this test we should expect all local, +// stun and turn candidates. +TEST_F(PortAllocatorTest, TestSharedSocketWithNatUsingTurn) { + AddInterface(kClientAddr); + talk_base::scoped_ptr nat_server( + CreateNatServer(kNatAddr, talk_base::NAT_OPEN_CONE)); + allocator_.reset(new cricket::BasicPortAllocator( + &network_manager_, &nat_socket_factory_, kStunAddr)); + cricket::RelayServerConfig relay_server(cricket::RELAY_TURN); + cricket::RelayCredentials credentials(kTurnUsername, kTurnPassword); + relay_server.credentials = credentials; + relay_server.ports.push_back(cricket::ProtocolAddress( + kTurnUdpIntAddr, cricket::PROTO_UDP, false)); + allocator_->AddRelay(relay_server); + + allocator_->set_step_delay(cricket::kMinimumStepDelay); + allocator_->set_flags(allocator().flags() | + cricket::PORTALLOCATOR_ENABLE_SHARED_UFRAG | + cricket::PORTALLOCATOR_ENABLE_SHARED_SOCKET | + cricket::PORTALLOCATOR_DISABLE_TCP); + EXPECT_TRUE(CreateSession(cricket::ICE_CANDIDATE_COMPONENT_RTP)); + session_->StartGettingPorts(); + ASSERT_EQ_WAIT(3U, candidates_.size(), kDefaultAllocationTimeout); + ASSERT_EQ(2U, ports_.size()); + EXPECT_PRED5(CheckCandidate, candidates_[0], + cricket::ICE_CANDIDATE_COMPONENT_RTP, "local", "udp", kClientAddr); + EXPECT_PRED5(CheckCandidate, candidates_[1], + cricket::ICE_CANDIDATE_COMPONENT_RTP, "stun", "udp", + talk_base::SocketAddress(kNatAddr.ipaddr(), 0)); + EXPECT_PRED5(CheckCandidate, candidates_[2], + cricket::ICE_CANDIDATE_COMPONENT_RTP, "relay", "udp", + talk_base::SocketAddress(kTurnUdpExtAddr.ipaddr(), 0)); + EXPECT_TRUE_WAIT(candidate_allocation_done_, kDefaultAllocationTimeout); + EXPECT_EQ(3U, candidates_.size()); + // Local port will be created first and then TURN port. + // Checking TURN port has two candidates, STUN + TURN. + EXPECT_EQ(1U, ports_[0]->Candidates().size()); + EXPECT_EQ(2U, ports_[1]->Candidates().size()); +} + // This test verifies when PORTALLOCATOR_ENABLE_SHARED_SOCKET flag is enabled // and fail to generate STUN candidate, local UDP candidate is generated // properly. -TEST_F(PortAllocatorTest, TestEnableSharedSocketNoUdpAllowed) { +TEST_F(PortAllocatorTest, TestSharedSocketNoUdpAllowed) { allocator().set_flags(allocator().flags() | cricket::PORTALLOCATOR_DISABLE_RELAY | cricket::PORTALLOCATOR_DISABLE_TCP | diff --git a/talk/xmpp/chatroommoduleimpl.cc b/talk/xmpp/chatroommoduleimpl.cc index eb046d721..a12ff5e02 100644 --- a/talk/xmpp/chatroommoduleimpl.cc +++ b/talk/xmpp/chatroommoduleimpl.cc @@ -320,17 +320,13 @@ XmppChatroomModuleImpl::RequestExitChatroom() { if (!engine()) return XMPP_RETURN_BADSTATE; - // currently, can't leave a room unless you've entered - // no way to cancel a pending enter call - is that bad? - if (chatroom_state_ != XMPP_CHATROOM_STATE_IN_ROOM) - return XMPP_RETURN_BADSTATE; // $TODO - this isn't a bad state, it's a bad call, diff error code? - // exiting a chatroom is a presence request to the server XmlElement element(QN_PRESENCE); element.AddAttr(QN_TO, member_jid().Str()); element.AddAttr(QN_TYPE, "unavailable"); XmppReturnStatus status = engine()->SendStanza(&element); - if (status == XMPP_RETURN_OK) { + if (status == XMPP_RETURN_OK && + chatroom_state_ == XMPP_CHATROOM_STATE_IN_ROOM) { return ClientChangeMyPresence(XMPP_CHATROOM_STATE_REQUESTED_EXIT); } return status; @@ -513,6 +509,7 @@ XmppChatroomModuleImpl::ServerChangedOtherPresence(const XmlElement& FireMemberChanged(member); } else if (presence->available() == XMPP_PRESENCE_UNAVAILABLE) { + member->SetPresence(presence.get()); chatroom_jid_members_.erase(pos); chatroom_jid_members_version_++; FireMemberExited(member); diff --git a/talk/xmpp/pubsubtasks.cc b/talk/xmpp/pubsubtasks.cc index bbefbe55c..015708eb5 100644 --- a/talk/xmpp/pubsubtasks.cc +++ b/talk/xmpp/pubsubtasks.cc @@ -173,9 +173,16 @@ void PubSubRequestTask::HandleResult(const XmlElement* stanza) { SignalResult(this, items); } +int PubSubReceiveTask::ProcessStart() { + if (SignalUpdate.is_empty()) { + return STATE_DONE; + } + return ReceiveTask::ProcessStart(); +} + bool PubSubReceiveTask::WantsStanza(const XmlElement* stanza) { return MatchStanzaFrom(stanza, pubsubjid_) && - IsPubSubEventItemsElem(stanza, node_); + IsPubSubEventItemsElem(stanza, node_) && !SignalUpdate.is_empty(); } void PubSubReceiveTask::ReceiveStanza(const XmlElement* stanza) { diff --git a/talk/xmpp/pubsubtasks.h b/talk/xmpp/pubsubtasks.h index f0a158178..2ba618b34 100644 --- a/talk/xmpp/pubsubtasks.h +++ b/talk/xmpp/pubsubtasks.h @@ -71,6 +71,7 @@ class PubSubReceiveTask : public ReceiveTask { node_(node) { } + virtual int ProcessStart(); sigslot::signal2&> SignalUpdate; diff --git a/talk/xmpp/rostermoduleimpl.cc b/talk/xmpp/rostermoduleimpl.cc index 31b3abdf0..993cfa905 100644 --- a/talk/xmpp/rostermoduleimpl.cc +++ b/talk/xmpp/rostermoduleimpl.cc @@ -351,10 +351,6 @@ XmppPresenceImpl::set_raw_xml(const XmlElement * xml) { xml->Name() != QN_PRESENCE) return XMPP_RETURN_BADARGUMENT; - const std::string& type = xml->Attr(QN_TYPE); - if (type != STR_EMPTY && type != "unavailable") - return XMPP_RETURN_BADARGUMENT; - raw_xml_.reset(new XmlElement(*xml)); return XMPP_RETURN_OK; }