| OLD | NEW |
| 1 /* | 1 /* |
| 2 * libjingle | 2 * libjingle |
| 3 * Copyright 2004 Google Inc. | 3 * Copyright 2004 Google Inc. |
| 4 * | 4 * |
| 5 * Redistribution and use in source and binary forms, with or without | 5 * Redistribution and use in source and binary forms, with or without |
| 6 * modification, are permitted provided that the following conditions are met: | 6 * modification, are permitted provided that the following conditions are met: |
| 7 * | 7 * |
| 8 * 1. Redistributions of source code must retain the above copyright notice, | 8 * 1. Redistributions of source code must retain the above copyright notice, |
| 9 * this list of conditions and the following disclaimer. | 9 * this list of conditions and the following disclaimer. |
| 10 * 2. Redistributions in binary form must reproduce the above copyright notice, | 10 * 2. Redistributions in binary form must reproduce the above copyright notice, |
| (...skipping 231 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 242 } | 242 } |
| 243 | 243 |
| 244 bool BaseChannel::SetTransport_w(const std::string& transport_name) { | 244 bool BaseChannel::SetTransport_w(const std::string& transport_name) { |
| 245 ASSERT(worker_thread_ == rtc::Thread::Current()); | 245 ASSERT(worker_thread_ == rtc::Thread::Current()); |
| 246 | 246 |
| 247 if (transport_name == transport_name_) { | 247 if (transport_name == transport_name_) { |
| 248 // Nothing to do if transport name isn't changing | 248 // Nothing to do if transport name isn't changing |
| 249 return true; | 249 return true; |
| 250 } | 250 } |
| 251 | 251 |
| 252 // When using DTLS-SRTP, we must reset the SrtpFilter every time the transport |
| 253 // changes and wait until the DTLS handshake is complete to set the newly |
| 254 // negotiated parameters. |
| 255 if (ShouldSetupDtlsSrtp()) { |
| 256 srtp_filter_.ResetParams(); |
| 257 } |
| 258 |
| 252 set_transport_channel(transport_controller_->CreateTransportChannel_w( | 259 set_transport_channel(transport_controller_->CreateTransportChannel_w( |
| 253 transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP)); | 260 transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP)); |
| 254 if (!transport_channel()) { | 261 if (!transport_channel()) { |
| 255 return false; | 262 return false; |
| 256 } | 263 } |
| 257 if (rtcp_transport_enabled()) { | 264 if (rtcp_transport_enabled()) { |
| 258 LOG(LS_INFO) << "Create RTCP TransportChannel for " << content_name() | 265 LOG(LS_INFO) << "Create RTCP TransportChannel for " << content_name() |
| 259 << " on " << transport_name << " transport "; | 266 << " on " << transport_name << " transport "; |
| 260 set_rtcp_transport_channel(transport_controller_->CreateTransportChannel_w( | 267 set_rtcp_transport_channel(transport_controller_->CreateTransportChannel_w( |
| 261 transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTCP)); | 268 transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTCP)); |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 311 | 318 |
| 312 if (old_tc) { | 319 if (old_tc) { |
| 313 DisconnectFromTransportChannel(old_tc); | 320 DisconnectFromTransportChannel(old_tc); |
| 314 transport_controller_->DestroyTransportChannel_w( | 321 transport_controller_->DestroyTransportChannel_w( |
| 315 transport_name_, cricket::ICE_CANDIDATE_COMPONENT_RTCP); | 322 transport_name_, cricket::ICE_CANDIDATE_COMPONENT_RTCP); |
| 316 } | 323 } |
| 317 | 324 |
| 318 rtcp_transport_channel_ = new_tc; | 325 rtcp_transport_channel_ = new_tc; |
| 319 | 326 |
| 320 if (new_tc) { | 327 if (new_tc) { |
| 328 RTC_CHECK(!(ShouldSetupDtlsSrtp() && srtp_filter_.IsActive())) |
| 329 << "Setting RTCP for DTLS/SRTP after SrtpFilter is active " |
| 330 << "should never happen."; |
| 321 ConnectToTransportChannel(new_tc); | 331 ConnectToTransportChannel(new_tc); |
| 322 for (const auto& pair : rtcp_socket_options_) { | 332 for (const auto& pair : rtcp_socket_options_) { |
| 323 new_tc->SetOption(pair.first, pair.second); | 333 new_tc->SetOption(pair.first, pair.second); |
| 324 } | 334 } |
| 325 } | 335 } |
| 326 | 336 |
| 327 // Update aggregate writable/ready-to-send state between RTP and RTCP upon | 337 // Update aggregate writable/ready-to-send state between RTP and RTCP upon |
| 328 // setting new channel | 338 // setting new channel |
| 329 UpdateWritableState_w(); | 339 UpdateWritableState_w(); |
| 330 SetReadyToSend(true, new_tc && new_tc->writable()); | 340 SetReadyToSend(true, new_tc && new_tc->writable()); |
| 331 } | 341 } |
| 332 | 342 |
| 333 void BaseChannel::ConnectToTransportChannel(TransportChannel* tc) { | 343 void BaseChannel::ConnectToTransportChannel(TransportChannel* tc) { |
| 334 ASSERT(worker_thread_ == rtc::Thread::Current()); | 344 ASSERT(worker_thread_ == rtc::Thread::Current()); |
| 335 | 345 |
| 336 tc->SignalWritableState.connect(this, &BaseChannel::OnWritableState); | 346 tc->SignalWritableState.connect(this, &BaseChannel::OnWritableState); |
| 337 tc->SignalReadPacket.connect(this, &BaseChannel::OnChannelRead); | 347 tc->SignalReadPacket.connect(this, &BaseChannel::OnChannelRead); |
| 338 tc->SignalReadyToSend.connect(this, &BaseChannel::OnReadyToSend); | 348 tc->SignalReadyToSend.connect(this, &BaseChannel::OnReadyToSend); |
| 349 tc->SignalDtlsState.connect(this, &BaseChannel::OnDtlsState); |
| 339 } | 350 } |
| 340 | 351 |
| 341 void BaseChannel::DisconnectFromTransportChannel(TransportChannel* tc) { | 352 void BaseChannel::DisconnectFromTransportChannel(TransportChannel* tc) { |
| 342 ASSERT(worker_thread_ == rtc::Thread::Current()); | 353 ASSERT(worker_thread_ == rtc::Thread::Current()); |
| 343 | 354 |
| 344 tc->SignalWritableState.disconnect(this); | 355 tc->SignalWritableState.disconnect(this); |
| 345 tc->SignalReadPacket.disconnect(this); | 356 tc->SignalReadPacket.disconnect(this); |
| 346 tc->SignalReadyToSend.disconnect(this); | 357 tc->SignalReadyToSend.disconnect(this); |
| 358 tc->SignalDtlsState.disconnect(this); |
| 347 } | 359 } |
| 348 | 360 |
| 349 bool BaseChannel::Enable(bool enable) { | 361 bool BaseChannel::Enable(bool enable) { |
| 350 worker_thread_->Invoke<void>(Bind( | 362 worker_thread_->Invoke<void>(Bind( |
| 351 enable ? &BaseChannel::EnableMedia_w : &BaseChannel::DisableMedia_w, | 363 enable ? &BaseChannel::EnableMedia_w : &BaseChannel::DisableMedia_w, |
| 352 this)); | 364 this)); |
| 353 return true; | 365 return true; |
| 354 } | 366 } |
| 355 | 367 |
| 356 bool BaseChannel::AddRecvStream(const StreamParams& sp) { | 368 bool BaseChannel::AddRecvStream(const StreamParams& sp) { |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 409 } | 421 } |
| 410 | 422 |
| 411 bool BaseChannel::IsReadyToReceive() const { | 423 bool BaseChannel::IsReadyToReceive() const { |
| 412 // Receive data if we are enabled and have local content, | 424 // Receive data if we are enabled and have local content, |
| 413 return enabled() && IsReceiveContentDirection(local_content_direction_); | 425 return enabled() && IsReceiveContentDirection(local_content_direction_); |
| 414 } | 426 } |
| 415 | 427 |
| 416 bool BaseChannel::IsReadyToSend() const { | 428 bool BaseChannel::IsReadyToSend() const { |
| 417 // Send outgoing data if we are enabled, have local and remote content, | 429 // Send outgoing data if we are enabled, have local and remote content, |
| 418 // and we have had some form of connectivity. | 430 // and we have had some form of connectivity. |
| 419 return enabled() && | 431 return enabled() && IsReceiveContentDirection(remote_content_direction_) && |
| 420 IsReceiveContentDirection(remote_content_direction_) && | |
| 421 IsSendContentDirection(local_content_direction_) && | 432 IsSendContentDirection(local_content_direction_) && |
| 422 was_ever_writable(); | 433 was_ever_writable() && |
| 434 (srtp_filter_.IsActive() || !ShouldSetupDtlsSrtp()); |
| 423 } | 435 } |
| 424 | 436 |
| 425 bool BaseChannel::SendPacket(rtc::Buffer* packet, | 437 bool BaseChannel::SendPacket(rtc::Buffer* packet, |
| 426 const rtc::PacketOptions& options) { | 438 const rtc::PacketOptions& options) { |
| 427 return SendPacket(false, packet, options); | 439 return SendPacket(false, packet, options); |
| 428 } | 440 } |
| 429 | 441 |
| 430 bool BaseChannel::SendRtcp(rtc::Buffer* packet, | 442 bool BaseChannel::SendRtcp(rtc::Buffer* packet, |
| 431 const rtc::PacketOptions& options) { | 443 const rtc::PacketOptions& options) { |
| 432 return SendPacket(true, packet, options); | 444 return SendPacket(true, packet, options); |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 467 bool rtcp = PacketIsRtcp(channel, data, len); | 479 bool rtcp = PacketIsRtcp(channel, data, len); |
| 468 rtc::Buffer packet(data, len); | 480 rtc::Buffer packet(data, len); |
| 469 HandlePacket(rtcp, &packet, packet_time); | 481 HandlePacket(rtcp, &packet, packet_time); |
| 470 } | 482 } |
| 471 | 483 |
| 472 void BaseChannel::OnReadyToSend(TransportChannel* channel) { | 484 void BaseChannel::OnReadyToSend(TransportChannel* channel) { |
| 473 ASSERT(channel == transport_channel_ || channel == rtcp_transport_channel_); | 485 ASSERT(channel == transport_channel_ || channel == rtcp_transport_channel_); |
| 474 SetReadyToSend(channel == rtcp_transport_channel_, true); | 486 SetReadyToSend(channel == rtcp_transport_channel_, true); |
| 475 } | 487 } |
| 476 | 488 |
| 489 void BaseChannel::OnDtlsState(TransportChannel* channel, |
| 490 DtlsTransportState state) { |
| 491 if (!ShouldSetupDtlsSrtp()) { |
| 492 return; |
| 493 } |
| 494 |
| 495 // Reset the srtp filter if it's not the CONNECTED state. For the CONNECTED |
| 496 // state, setting up DTLS-SRTP context is deferred to ChannelWritable_w to |
| 497 // cover other scenarios like the whole channel is writable (not just this |
| 498 // TransportChannel) or when TransportChannel is attached after DTLS is |
| 499 // negotiated. |
| 500 if (state != DTLS_TRANSPORT_CONNECTED) { |
| 501 srtp_filter_.ResetParams(); |
| 502 } |
| 503 } |
| 504 |
| 477 void BaseChannel::SetReadyToSend(bool rtcp, bool ready) { | 505 void BaseChannel::SetReadyToSend(bool rtcp, bool ready) { |
| 478 if (rtcp) { | 506 if (rtcp) { |
| 479 rtcp_ready_to_send_ = ready; | 507 rtcp_ready_to_send_ = ready; |
| 480 } else { | 508 } else { |
| 481 rtp_ready_to_send_ = ready; | 509 rtp_ready_to_send_ = ready; |
| 482 } | 510 } |
| 483 | 511 |
| 484 if (rtp_ready_to_send_ && | 512 if (rtp_ready_to_send_ && |
| 485 // In the case of rtcp mux |rtcp_transport_channel_| will be null. | 513 // In the case of rtcp mux |rtcp_transport_channel_| will be null. |
| 486 (rtcp_ready_to_send_ || !rtcp_transport_channel_)) { | 514 (rtcp_ready_to_send_ || !rtcp_transport_channel_)) { |
| (...skipping 267 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 754 if (transport_channel_ && transport_channel_->writable() && | 782 if (transport_channel_ && transport_channel_->writable() && |
| 755 (!rtcp_transport_channel_ || rtcp_transport_channel_->writable())) { | 783 (!rtcp_transport_channel_ || rtcp_transport_channel_->writable())) { |
| 756 ChannelWritable_w(); | 784 ChannelWritable_w(); |
| 757 } else { | 785 } else { |
| 758 ChannelNotWritable_w(); | 786 ChannelNotWritable_w(); |
| 759 } | 787 } |
| 760 } | 788 } |
| 761 | 789 |
| 762 void BaseChannel::ChannelWritable_w() { | 790 void BaseChannel::ChannelWritable_w() { |
| 763 ASSERT(worker_thread_ == rtc::Thread::Current()); | 791 ASSERT(worker_thread_ == rtc::Thread::Current()); |
| 764 if (writable_) | 792 if (writable_) { |
| 765 return; | 793 return; |
| 794 } |
| 766 | 795 |
| 767 LOG(LS_INFO) << "Channel writable (" << content_name_ << ")" | 796 LOG(LS_INFO) << "Channel writable (" << content_name_ << ")" |
| 768 << (was_ever_writable_ ? "" : " for the first time"); | 797 << (was_ever_writable_ ? "" : " for the first time"); |
| 769 | 798 |
| 770 std::vector<ConnectionInfo> infos; | 799 std::vector<ConnectionInfo> infos; |
| 771 transport_channel_->GetStats(&infos); | 800 transport_channel_->GetStats(&infos); |
| 772 for (std::vector<ConnectionInfo>::const_iterator it = infos.begin(); | 801 for (std::vector<ConnectionInfo>::const_iterator it = infos.begin(); |
| 773 it != infos.end(); ++it) { | 802 it != infos.end(); ++it) { |
| 774 if (it->best_connection) { | 803 if (it->best_connection) { |
| 775 LOG(LS_INFO) << "Using " << it->local_candidate.ToSensitiveString() | 804 LOG(LS_INFO) << "Using " << it->local_candidate.ToSensitiveString() |
| 776 << "->" << it->remote_candidate.ToSensitiveString(); | 805 << "->" << it->remote_candidate.ToSensitiveString(); |
| 777 break; | 806 break; |
| 778 } | 807 } |
| 779 } | 808 } |
| 780 | 809 |
| 781 // If we're doing DTLS-SRTP, now is the time. | |
| 782 if (!was_ever_writable_ && ShouldSetupDtlsSrtp()) { | |
| 783 if (!SetupDtlsSrtp(false)) { | |
| 784 SignalDtlsSetupFailure_w(false); | |
| 785 return; | |
| 786 } | |
| 787 | |
| 788 if (rtcp_transport_channel_) { | |
| 789 if (!SetupDtlsSrtp(true)) { | |
| 790 SignalDtlsSetupFailure_w(true); | |
| 791 return; | |
| 792 } | |
| 793 } | |
| 794 } | |
| 795 | |
| 796 was_ever_writable_ = true; | 810 was_ever_writable_ = true; |
| 811 MaybeSetupDtlsSrtp_w(); |
| 797 writable_ = true; | 812 writable_ = true; |
| 798 ChangeState(); | 813 ChangeState(); |
| 799 } | 814 } |
| 800 | 815 |
| 801 void BaseChannel::SignalDtlsSetupFailure_w(bool rtcp) { | 816 void BaseChannel::SignalDtlsSetupFailure_w(bool rtcp) { |
| 802 ASSERT(worker_thread() == rtc::Thread::Current()); | 817 ASSERT(worker_thread() == rtc::Thread::Current()); |
| 803 signaling_thread()->Invoke<void>(Bind( | 818 signaling_thread()->Invoke<void>(Bind( |
| 804 &BaseChannel::SignalDtlsSetupFailure_s, this, rtcp)); | 819 &BaseChannel::SignalDtlsSetupFailure_s, this, rtcp)); |
| 805 } | 820 } |
| 806 | 821 |
| 807 void BaseChannel::SignalDtlsSetupFailure_s(bool rtcp) { | 822 void BaseChannel::SignalDtlsSetupFailure_s(bool rtcp) { |
| 808 ASSERT(signaling_thread() == rtc::Thread::Current()); | 823 ASSERT(signaling_thread() == rtc::Thread::Current()); |
| 809 SignalDtlsSetupFailure(this, rtcp); | 824 SignalDtlsSetupFailure(this, rtcp); |
| 810 } | 825 } |
| 811 | 826 |
| 812 bool BaseChannel::SetDtlsSrtpCryptoSuites(TransportChannel* tc, bool rtcp) { | 827 bool BaseChannel::SetDtlsSrtpCryptoSuites(TransportChannel* tc, bool rtcp) { |
| 813 std::vector<int> crypto_suites; | 828 std::vector<int> crypto_suites; |
| 814 // We always use the default SRTP crypto suites for RTCP, but we may use | 829 // We always use the default SRTP crypto suites for RTCP, but we may use |
| 815 // different crypto suites for RTP depending on the media type. | 830 // different crypto suites for RTP depending on the media type. |
| 816 if (!rtcp) { | 831 if (!rtcp) { |
| 817 GetSrtpCryptoSuites(&crypto_suites); | 832 GetSrtpCryptoSuites(&crypto_suites); |
| 818 } else { | 833 } else { |
| 819 GetDefaultSrtpCryptoSuites(&crypto_suites); | 834 GetDefaultSrtpCryptoSuites(&crypto_suites); |
| 820 } | 835 } |
| 821 return tc->SetSrtpCryptoSuites(crypto_suites); | 836 return tc->SetSrtpCryptoSuites(crypto_suites); |
| 822 } | 837 } |
| 823 | 838 |
| 824 bool BaseChannel::ShouldSetupDtlsSrtp() const { | 839 bool BaseChannel::ShouldSetupDtlsSrtp() const { |
| 825 return true; | 840 // Since DTLS is applied to all channels, checking RTP should be enough. |
| 841 return transport_channel_ && transport_channel_->IsDtlsActive(); |
| 826 } | 842 } |
| 827 | 843 |
| 828 // This function returns true if either DTLS-SRTP is not in use | 844 // This function returns true if either DTLS-SRTP is not in use |
| 829 // *or* DTLS-SRTP is successfully set up. | 845 // *or* DTLS-SRTP is successfully set up. |
| 830 bool BaseChannel::SetupDtlsSrtp(bool rtcp_channel) { | 846 bool BaseChannel::SetupDtlsSrtp(bool rtcp_channel) { |
| 831 bool ret = false; | 847 bool ret = false; |
| 832 | 848 |
| 833 TransportChannel* channel = | 849 TransportChannel* channel = |
| 834 rtcp_channel ? rtcp_transport_channel_ : transport_channel_; | 850 rtcp_channel ? rtcp_transport_channel_ : transport_channel_; |
| 835 | 851 |
| 836 // No DTLS | 852 RTC_DCHECK(channel->IsDtlsActive()); |
| 837 if (!channel->IsDtlsActive()) | |
| 838 return true; | |
| 839 | 853 |
| 840 int selected_crypto_suite; | 854 int selected_crypto_suite; |
| 841 | 855 |
| 842 if (!channel->GetSrtpCryptoSuite(&selected_crypto_suite)) { | 856 if (!channel->GetSrtpCryptoSuite(&selected_crypto_suite)) { |
| 843 LOG(LS_ERROR) << "No DTLS-SRTP selected crypto suite"; | 857 LOG(LS_ERROR) << "No DTLS-SRTP selected crypto suite"; |
| 844 return false; | 858 return false; |
| 845 } | 859 } |
| 846 | 860 |
| 847 LOG(LS_INFO) << "Installing keys from DTLS-SRTP on " | 861 LOG(LS_INFO) << "Installing keys from DTLS-SRTP on " |
| 848 << content_name() << " " | 862 << content_name() << " " |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 908 } | 922 } |
| 909 | 923 |
| 910 if (!ret) | 924 if (!ret) |
| 911 LOG(LS_WARNING) << "DTLS-SRTP key installation failed"; | 925 LOG(LS_WARNING) << "DTLS-SRTP key installation failed"; |
| 912 else | 926 else |
| 913 dtls_keyed_ = true; | 927 dtls_keyed_ = true; |
| 914 | 928 |
| 915 return ret; | 929 return ret; |
| 916 } | 930 } |
| 917 | 931 |
| 932 void BaseChannel::MaybeSetupDtlsSrtp_w() { |
| 933 if (srtp_filter_.IsActive()) { |
| 934 return; |
| 935 } |
| 936 |
| 937 if (!ShouldSetupDtlsSrtp()) { |
| 938 return; |
| 939 } |
| 940 |
| 941 if (!SetupDtlsSrtp(false)) { |
| 942 SignalDtlsSetupFailure_w(false); |
| 943 return; |
| 944 } |
| 945 |
| 946 if (rtcp_transport_channel_) { |
| 947 if (!SetupDtlsSrtp(true)) { |
| 948 SignalDtlsSetupFailure_w(true); |
| 949 return; |
| 950 } |
| 951 } |
| 952 } |
| 953 |
| 918 void BaseChannel::ChannelNotWritable_w() { | 954 void BaseChannel::ChannelNotWritable_w() { |
| 919 ASSERT(worker_thread_ == rtc::Thread::Current()); | 955 ASSERT(worker_thread_ == rtc::Thread::Current()); |
| 920 if (!writable_) | 956 if (!writable_) |
| 921 return; | 957 return; |
| 922 | 958 |
| 923 LOG(LS_INFO) << "Channel not writable (" << content_name_ << ")"; | 959 LOG(LS_INFO) << "Channel not writable (" << content_name_ << ")"; |
| 924 writable_ = false; | 960 writable_ = false; |
| 925 ChangeState(); | 961 ChangeState(); |
| 926 } | 962 } |
| 927 | 963 |
| (...skipping 1328 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2256 // that the transport channel is ready. | 2292 // that the transport channel is ready. |
| 2257 signaling_thread()->Post(this, MSG_READYTOSENDDATA, | 2293 signaling_thread()->Post(this, MSG_READYTOSENDDATA, |
| 2258 new DataChannelReadyToSendMessageData(writable)); | 2294 new DataChannelReadyToSendMessageData(writable)); |
| 2259 } | 2295 } |
| 2260 | 2296 |
| 2261 void DataChannel::GetSrtpCryptoSuites(std::vector<int>* crypto_suites) const { | 2297 void DataChannel::GetSrtpCryptoSuites(std::vector<int>* crypto_suites) const { |
| 2262 GetSupportedDataCryptoSuites(crypto_suites); | 2298 GetSupportedDataCryptoSuites(crypto_suites); |
| 2263 } | 2299 } |
| 2264 | 2300 |
| 2265 bool DataChannel::ShouldSetupDtlsSrtp() const { | 2301 bool DataChannel::ShouldSetupDtlsSrtp() const { |
| 2266 return (data_channel_type_ == DCT_RTP); | 2302 return (data_channel_type_ == DCT_RTP) && BaseChannel::ShouldSetupDtlsSrtp(); |
| 2267 } | 2303 } |
| 2268 | 2304 |
| 2269 void DataChannel::OnStreamClosedRemotely(uint32_t sid) { | 2305 void DataChannel::OnStreamClosedRemotely(uint32_t sid) { |
| 2270 rtc::TypedMessageData<uint32_t>* message = | 2306 rtc::TypedMessageData<uint32_t>* message = |
| 2271 new rtc::TypedMessageData<uint32_t>(sid); | 2307 new rtc::TypedMessageData<uint32_t>(sid); |
| 2272 signaling_thread()->Post(this, MSG_STREAMCLOSEDREMOTELY, message); | 2308 signaling_thread()->Post(this, MSG_STREAMCLOSEDREMOTELY, message); |
| 2273 } | 2309 } |
| 2274 | 2310 |
| 2275 } // namespace cricket | 2311 } // namespace cricket |
| OLD | NEW |